Loading...
Searching...
No Matches
Fork.cpp
Go to the documentation of this file.
// -*- 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 <iostream>
#include <fcntl.h>
#if !defined(WIN32)
# include <syslog.h>
#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);
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 exit_action_, 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 (exit_action_ != LEAVE_ALONE) {
ForkList::get_instance()->m_list.push_back (new fnode_t (m_pid, exit_action_));
}
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<fnode_t* >::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<fnode_t* >::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)
CmdLineOpts.h
Class to handle processing command-line options.
EventHandler.h
An abstract interface for handling I/O events, timers, and such.
Fork.h
A simple wrapper around fork() library function call.
EL
#define EL(X)
A macro for writing error message to the Logger.
Definition Logger.h:285
DL
#define DL(X)
A macro for writing debug message to the Logger.
Definition Logger.h:273
trace_with_mask
#define trace_with_mask(s, m)
trace_with_mask() is used to trace function call chain in C++ program.
Definition Logger.h:437
SigAction.h
SigAction is a C++ wrapper around sigaction structure.
SigHandler.h
Class SigHandler is a UNIX signal handlers manager/dispatcher class.
ASSA_DECL_SINGLETON
#define ASSA_DECL_SINGLETON(K)
ASSA_DECL_SINGLETON macro inserts static member declarations mandated by the Singleton class.
Definition Singleton.h:82
ASSA::ChildStatusHandler::m_caught
bool m_caught
Definition Fork.h:77
ASSA::ChildStatusHandler::handle_signal
int handle_signal(int signum_)
Signal handler callback.
Definition Fork.cpp:135
ASSA::ChildStatusHandler::m_exit_status
int m_exit_status
Definition Fork.h:76
ASSA::CmdLineOpts::str_to_argv
static void str_to_argv(const string &src_, int &argc_, char **&argv_)
Static function.
Definition CmdLineOpts.cpp:661
ASSA::ForkList
ForkList is a singleton class that keeps a list of all forked children.
Definition Fork.h:232
ASSA::ForkList::m_list
list< fnode_t * > m_list
List of children's data structures.
Definition Fork.h:241
ASSA::ForkList::~ForkList
~ForkList()
Destructor. Wipe out childer based on their state.
Definition Fork.cpp:188
ASSA::Fork::m_pid
pid_t m_pid
Child pid.
Definition Fork.h:178
ASSA::Fork::state_t
state_t
Child completion states.
Definition Fork.h:91
ASSA::Fork::LEAVE_ALONE
@ LEAVE_ALONE
Ignore all running children on exit.
Definition Fork.h:94
ASSA::Fork::isChild
bool isChild() const
Test whether we are in child section of the code.
Definition Fork.h:136
ASSA::Fork::wait4status_t
wait4status_t
Definition Fork.h:99
ASSA::Fork::COLLECT_STATUS
@ COLLECT_STATUS
Wait for child to complete and collect its exit status.
Definition Fork.h:101
ASSA::Fork::m_old_disp
SigAction m_old_disp
Old signal disposition.
Definition Fork.h:187
ASSA::Fork::m_local_sh
SigHandler m_local_sh
Local signal handler.
Definition Fork.h:181
ASSA::Fork::fork_exec
static int fork_exec(const string &cmd_, const string &args_, wait4status_t wait_for_completion_, bool ignore_output_=false)
Execute an external command.
Definition Fork.cpp:63
ASSA::Fork::Fork
Fork(state_t exit_action_=WAIT_ON_EXIT, wait4status_t catch_status_=COLLECT_STATUS)
Fork the current process in two immediately.
Definition Fork.cpp:160
ASSA::Fork::get_exit_status
int get_exit_status() const
Retrieve exit status of a child process if the constructor's parameter catch_status_ was set to TRUE.
Definition Fork.h:151
ASSA::Fork::getChildPID
pid_t getChildPID() const
Retrieve child process id.
Definition Fork.h:142
ASSA::Fork::m_chstath
ChildStatusHandler m_chstath
Handler to catch Child's status.
Definition Fork.h:184
ASSA::Singleton< ForkList >::get_instance
static ForkList * get_instance()
Definition Singleton.h:47
ASSA::fnode_t
forknode_t class.
Definition Fork.h:195
ASSA
Definition Acceptor.h:40
ASSA::FORK
@ FORK
Class Fork messages.
Definition LogMask.h:47
ASSA::ASSAERR
@ ASSAERR
ASSA and system errors.
Definition LogMask.h:34