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

Generated on Mon Dec 19 15:59:00 2005 for libassa by  doxygen 1.4.5