/home/vlg/develop/libASSA/libassa/assa/Fork.cpp

Go to the documentation of this file.
00001 // -*- c++ -*-
00002 //------------------------------------------------------------------------------
00003 //                              Fork.cpp
00004 //------------------------------------------------------------------------------
00005 //  Copyright (C) 1997-2003,2005  Vladislav Grinchenko
00006 //
00007 //  This library is free software; you can redistribute it and/or
00008 //  modify it under the terms of the GNU Library General Public
00009 //  License as published by the Free Software Foundation; either
00010 //  version 2 of the License, or (at your option) any later version.
00011 //------------------------------------------------------------------------
00012 
00013 #include <iostream>
00014 #include <fcntl.h>
00015 
00016 #if !defined(WIN32)
00017 #    include <syslog.h>
00018 #endif
00019 
00020 #include "assa/Fork.h"
00021 #include "assa/CmdLineOpts.h"
00022 #include "assa/SigAction.h"
00023 #include "assa/EventHandler.h"
00024 #include "assa/SigHandler.h"
00025 
00026 using namespace ASSA;
00027 
00028 //------------------------------------------------------------------------------
00029 /* 
00030  * Note the use of _exit () instead of exit () in the child's portion.
00031  * For some GUI toolkits, calling exit() causes problems.
00032  *
00033  * From Gtk+ (www.gtk.org) FAQ:
00034  *
00035  *  "When GDK opens an X display, it creates a socket file descriptor. 
00036  *   When you use the exit() function, you implicitly close all the 
00037  *   open file descriptors, and the underlying X library really
00038  *   doesn't like this. The right function to use here is _exit()."
00039  *
00040  * From UNIX exit(2) man page:
00041  *
00042  *  "The function _exit terminates the calling process "immediately". 
00043  *   Any open file descriptors belonging to the process are closed; 
00044  *   any children of the process are inherited by process 1, init, and 
00045  *   the process's parent is sent a SIGCHLD signal."
00046  * 
00047  * _exit() doesn't not call standard I/O cleanup routines.
00048  *
00049  * From S.A. Rago "Unix System V Network Programming", sec. 2.4, p.74
00050  *
00051  *  "When a child terminates, the operationg system sends the SIGCHLD
00052  *   signal to the parent process. The default disposition for SIGCHLD
00053  *   is to ignore the signal, but a process can catch the signal and
00054  *   obtain the status of its children."
00055  *
00056  * We also preserve the SIGCHLD action mask here and catch it to get
00057  * the child's completion status. SIGCHLD signal mask is initially set to 
00058  * SIG_IGN by GenServer, but can be reset later on by a third-party library 
00059  * linked into the application code.
00060  */
00061 int
00062 Fork::
00063 fork_exec (const string& cmd_, 
00064            const string& args_, 
00065            Fork::wait4status_t wait_for_completion_, 
00066            bool ignore_output_)
00067 {
00068     trace_with_mask("Fork[static]::fork_exec",FORK);
00069 
00070     DL((FORK,"exec \"%s %s\")\n", cmd_.c_str (), args_.c_str ()));
00071     if (cmd_.size () == 0) { 
00072         return -1;
00073     }
00074 
00075 #if defined(WIN32)
00076 
00077     return -1;    // NOT IMPLEMENTED YET
00078 
00079 #else
00080 
00081     Fork f (Fork::LEAVE_ALONE, wait_for_completion_);
00082 
00083     if (f.isChild ()) {
00084         string arg_list (cmd_);
00085         arg_list += " " + args_;
00086         int argc = 0;
00087         char** argv = 0;
00088         CmdLineOpts::str_to_argv (arg_list, argc, argv);
00089 
00093         if (ignore_output_) {
00094             for (int i = 0; i < 1024; i++) {
00095                 (void) close(i);
00096             }
00097             pid_t nullfd = open("/dev/null", O_WRONLY | O_CREAT, 0666);
00098             if (nullfd == -1) {
00099                 syslog (LOG_ERR,"failed to open \"/dev/null\"");
00100                 _exit (-1);
00101             }
00102         
00103             (void) dup2  (nullfd, 1);
00104             (void) dup2  (nullfd, 2);
00105             (void) close (nullfd);
00106         }
00107 
00108         execvp (cmd_.c_str (), argv);     
00109 
00110         EL((ASSAERR,"fork_exec (\"%s\") failed\n", cmd_.c_str ()));
00111         _exit (-1);
00112     }
00113 
00114     if (! wait_for_completion_) {
00115         return f.getChildPID ();
00116     }
00117 
00118     return f.get_exit_status ();
00119     
00120 #endif // defined(WIN32)
00121 }
00122 
00123 
00124 #if !defined(WIN32)
00125 //------------------------------------------------------------------------------
00126 // Static declarations
00127 //
00128 ASSA_DECL_SINGLETON(ForkList);
00129 
00130 //------------------------------------------------------------------------------
00131 // Member functions
00132 //
00133 int
00134 ChildStatusHandler::
00135 handle_signal (int signum_)
00136 {
00137     trace_with_mask("ChildStatusHandler::handle_signal", FORK);
00138     DL((FORK, "Caught signal # %d\n", signum_));
00139 
00140     if (signum_ == SIGCHLD) {
00141         int status;
00142         m_caught = true;
00143         pid_t ret = ::wait (&status);
00144         DL((FORK,"wait() = %d (PID)\n", ret));
00145 
00146         if (ret > 0 && (WIFEXITED (status))) {
00147             m_exit_status = WEXITSTATUS (status);
00148         }
00149         else {
00150             m_exit_status = ret;
00151         }
00152     }
00153 
00154     DL((FORK,"child exit_status = %d\n", m_exit_status));
00155     return 0;
00156 }
00157 
00158 //------------------------------------------------------------------------------
00159 Fork::
00160 Fork (Fork::state_t state_, Fork::wait4status_t catch_status_)
00161 {
00162     trace_with_mask("Fork::Fork",FORK);
00163 
00164     if (catch_status_ == COLLECT_STATUS) {
00165         m_local_sh.install (SIGCHLD, &m_chstath, 0, 0, &m_old_disp);
00166     }
00167 
00168     if ((m_pid = fork()) < 0) {
00169         EL((ASSAERR,"failed to fork() - out of swap space?\n"));
00170         exit (1);                        // die right here
00171     }
00172     
00173     if (m_pid) {                         // The Parent
00174         if (state_ != LEAVE_ALONE) {
00175             ForkList::get_instance()->
00176                 m_list.push_back (new fnode_t (m_pid, state_));
00177         }
00178         if (catch_status_ == COLLECT_STATUS) {
00179             if (! m_chstath.caught ()) {
00180                 pause ();
00181             }
00182             m_local_sh.remove (SIGCHLD, &m_chstath, &m_old_disp, 0);
00183         }
00184     }
00185 }
00186 
00187 
00188 ForkList::
00189 ~ForkList()
00190 {
00191     trace_with_mask("ForkList::~ForkList",FORK);
00192 
00193     list<fnode_t* >::iterator i;
00194     pid_t pid;
00195 
00196     // Go through the list and send SIGTERM to those children
00197     // whose flags were set at fork time.
00198 
00199     for (i = m_list.begin(); i != m_list.end(); i++) {
00200         if ((*i)->needKill()) {
00201             ::kill((*i)->getPID(), SIGTERM);
00202         }
00203     }
00204     // Wait for all children to exit.
00205 
00206     while ( ! m_list.empty() ) { // wait for child to exit
00207         pid = ::wait(NULL);
00208         if ( pid < 0 ) { // error on wait
00209             EL((ASSAERR,"Error on wait()\n"));
00210             exit (EXIT_FAILURE);
00211         }
00212         // Search for child through the list by its pid.
00213         // If found, remove it from list and release memory.
00214         
00215         list<fnode_t* >::iterator j;
00216         
00217         for (j = m_list.begin(); j != m_list.end(); j++) {
00218             if ((*j)->getPID() == pid) {
00219                 fnode_t* ep = *j;
00220                 m_list.erase(j);
00221                 delete ep;
00222                 break;
00223             }
00224         }
00225     }
00226 }
00227 
00228 #endif // !defined(WIN32)

Generated on Sun Aug 13 15:08:00 2006 for libassa by  doxygen 1.4.6