// -*- c++ -*- //------------------------------------------------------------------------------ // Fork.cpp //------------------------------------------------------------------------------ // Copyright (C) 1997-2003,2005 Vladislav Grinchenko // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. //------------------------------------------------------------------------ #include #include #if !defined(WIN32) # include #endif #include "assa/Fork.h" #include "assa/CmdLineOpts.h" #include "assa/SigAction.h" #include "assa/EventHandler.h" #include "assa/SigHandler.h" using namespace ASSA; //------------------------------------------------------------------------------ /* * Note the use of _exit () instead of exit () in the child's portion. * For some GUI toolkits, calling exit() causes problems. * * From Gtk+ (www.gtk.org) FAQ: * * "When GDK opens an X display, it creates a socket file descriptor. * When you use the exit() function, you implicitly close all the * open file descriptors, and the underlying X library really * doesn't like this. The right function to use here is _exit()." * * From UNIX exit(2) man page: * * "The function _exit terminates the calling process "immediately". * Any open file descriptors belonging to the process are closed; * any children of the process are inherited by process 1, init, and * the process's parent is sent a SIGCHLD signal." * * _exit() doesn't not call standard I/O cleanup routines. * * From S.A. Rago "Unix System V Network Programming", sec. 2.4, p.74 * * "When a child terminates, the operationg system sends the SIGCHLD * signal to the parent process. The default disposition for SIGCHLD * is to ignore the signal, but a process can catch the signal and * obtain the status of its children." * * We also preserve the SIGCHLD action mask here and catch it to get * the child's completion status. SIGCHLD signal mask is initially set to * SIG_IGN by GenServer, but can be reset later on by a third-party library * linked into the application code. */ int Fork:: fork_exec (const string& cmd_, const string& args_, Fork::wait4status_t wait_for_completion_, bool ignore_output_) { trace_with_mask("Fork[static]::fork_exec",FORK); DL((FORK,"exec \"%s %s\")\n", cmd_.c_str (), args_.c_str ())); if (cmd_.size () == 0) { return -1; } #if defined(WIN32) return -1; // NOT IMPLEMENTED YET #else Fork f (Fork::LEAVE_ALONE, wait_for_completion_); if (f.isChild ()) { string arg_list (cmd_); arg_list += " " + args_; int argc = 0; char** argv = 0; CmdLineOpts::str_to_argv (arg_list, argc, argv); /** Close all file descriptors and reduped stdout/stderr to /dev/null */ if (ignore_output_) { for (int i = 0; i < 1024; i++) { (void) close(i); } pid_t nullfd = open("/dev/null", O_WRONLY | O_CREAT, 0666); if (nullfd == -1) { syslog (LOG_ERR,"failed to open \"/dev/null\""); _exit (-1); } (void) dup2 (nullfd, 1); (void) dup2 (nullfd, 2); (void) close (nullfd); } execvp (cmd_.c_str (), argv); EL((ASSAERR,"fork_exec (\"%s\") failed\n", cmd_.c_str ())); _exit (-1); } if (! wait_for_completion_) { return f.getChildPID (); } return f.get_exit_status (); #endif // defined(WIN32) } #if !defined(WIN32) //------------------------------------------------------------------------------ // Static declarations // ASSA_DECL_SINGLETON(ForkList); //------------------------------------------------------------------------------ // Member functions // int ChildStatusHandler:: handle_signal (int signum_) { trace_with_mask("ChildStatusHandler::handle_signal", FORK); DL((FORK, "Caught signal # %d\n", signum_)); if (signum_ == SIGCHLD) { int status; m_caught = true; pid_t ret = ::wait (&status); DL((FORK,"wait() = %d (PID)\n", ret)); if (ret > 0 && (WIFEXITED (status))) { m_exit_status = WEXITSTATUS (status); } else { m_exit_status = ret; } } DL((FORK,"child exit_status = %d\n", m_exit_status)); return 0; } //------------------------------------------------------------------------------ Fork:: Fork (Fork::state_t state_, Fork::wait4status_t catch_status_) { trace_with_mask("Fork::Fork",FORK); if (catch_status_ == COLLECT_STATUS) { m_local_sh.install (SIGCHLD, &m_chstath, 0, 0, &m_old_disp); } if ((m_pid = fork()) < 0) { EL((ASSAERR,"failed to fork() - out of swap space?\n")); exit (1); // die right here } if (m_pid) { // The Parent if (state_ != LEAVE_ALONE) { ForkList::get_instance()-> m_list.push_back (new fnode_t (m_pid, state_)); } if (catch_status_ == COLLECT_STATUS) { if (! m_chstath.caught ()) { pause (); } m_local_sh.remove (SIGCHLD, &m_chstath, &m_old_disp, 0); } } } ForkList:: ~ForkList() { trace_with_mask("ForkList::~ForkList",FORK); list::iterator i; pid_t pid; // Go through the list and send SIGTERM to those children // whose flags were set at fork time. for (i = m_list.begin(); i != m_list.end(); i++) { if ((*i)->needKill()) { ::kill((*i)->getPID(), SIGTERM); } } // Wait for all children to exit. while ( ! m_list.empty() ) { // wait for child to exit pid = ::wait(NULL); if ( pid < 0 ) { // error on wait EL((ASSAERR,"Error on wait()\n")); exit (EXIT_FAILURE); } // Search for child through the list by its pid. // If found, remove it from list and release memory. list::iterator j; for (j = m_list.begin(); j != m_list.end(); j++) { if ((*j)->getPID() == pid) { fnode_t* ep = *j; m_list.erase(j); delete ep; break; } } } } #endif // !defined(WIN32)