libassa 3.5.0
|
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)