libassa  3.5.1
Fork.cpp
Go to the documentation of this file.
1 // -*- c++ -*-
2 //------------------------------------------------------------------------------
3 // Fork.cpp
4 //------------------------------------------------------------------------------
5 // Copyright (C) 1997-2003,2005 Vladislav Grinchenko
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Library General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
11 //------------------------------------------------------------------------
12 
13 #include <iostream>
14 #include <fcntl.h>
15 
16 #if !defined(WIN32)
17 # include <syslog.h>
18 #endif
19 
20 #include "assa/Fork.h"
21 #include "assa/CmdLineOpts.h"
22 #include "assa/SigAction.h"
23 #include "assa/EventHandler.h"
24 #include "assa/SigHandler.h"
25 
26 using namespace ASSA;
27 
28 //------------------------------------------------------------------------------
29 /*
30  * Note the use of _exit () instead of exit () in the child's portion.
31  * For some GUI toolkits, calling exit() causes problems.
32  *
33  * From Gtk+ (www.gtk.org) FAQ:
34  *
35  * "When GDK opens an X display, it creates a socket file descriptor.
36  * When you use the exit() function, you implicitly close all the
37  * open file descriptors, and the underlying X library really
38  * doesn't like this. The right function to use here is _exit()."
39  *
40  * From UNIX exit(2) man page:
41  *
42  * "The function _exit terminates the calling process "immediately".
43  * Any open file descriptors belonging to the process are closed;
44  * any children of the process are inherited by process 1, init, and
45  * the process's parent is sent a SIGCHLD signal."
46  *
47  * _exit() doesn't not call standard I/O cleanup routines.
48  *
49  * From S.A. Rago "Unix System V Network Programming", sec. 2.4, p.74
50  *
51  * "When a child terminates, the operationg system sends the SIGCHLD
52  * signal to the parent process. The default disposition for SIGCHLD
53  * is to ignore the signal, but a process can catch the signal and
54  * obtain the status of its children."
55  *
56  * We also preserve the SIGCHLD action mask here and catch it to get
57  * the child's completion status. SIGCHLD signal mask is initially set to
58  * SIG_IGN by GenServer, but can be reset later on by a third-party library
59  * linked into the application code.
60  */
61 int
63 fork_exec (const string& cmd_,
64  const string& args_,
65  Fork::wait4status_t wait_for_completion_,
66  bool ignore_output_)
67 {
68  trace_with_mask("Fork[static]::fork_exec",FORK);
69 
70  DL((FORK,"exec \"%s %s\")\n", cmd_.c_str (), args_.c_str ()));
71  if (cmd_.size () == 0) {
72  return -1;
73  }
74 
75 #if defined(WIN32)
76 
77  return -1; // NOT IMPLEMENTED YET
78 
79 #else
80 
81  Fork f (Fork::LEAVE_ALONE, wait_for_completion_);
82 
83  if (f.isChild ()) {
84  string arg_list (cmd_);
85  arg_list += " " + args_;
86  int argc = 0;
87  char** argv = 0;
88  CmdLineOpts::str_to_argv (arg_list, argc, argv);
89 
93  if (ignore_output_) {
94  for (int i = 0; i < 1024; i++) {
95  (void) close(i);
96  }
97  pid_t nullfd = open("/dev/null", O_WRONLY | O_CREAT, 0666);
98  if (nullfd == -1) {
99  syslog (LOG_ERR,"failed to open \"/dev/null\"");
100  _exit (-1);
101  }
102 
103  (void) dup2 (nullfd, 1);
104  (void) dup2 (nullfd, 2);
105  (void) close (nullfd);
106  }
107 
108  execvp (cmd_.c_str (), argv);
109 
110  EL((ASSAERR,"fork_exec (\"%s\") failed\n", cmd_.c_str ()));
111  _exit (-1);
112  }
113 
114  if (! wait_for_completion_) {
115  return f.getChildPID ();
116  }
117 
118  return f.get_exit_status ();
119 
120 #endif // defined(WIN32)
121 }
122 
123 
124 #if !defined(WIN32)
125 //------------------------------------------------------------------------------
126 // Static declarations
127 //
129 
130 //------------------------------------------------------------------------------
131 // Member functions
132 //
133 int
135 handle_signal (int signum_)
136 {
137  trace_with_mask("ChildStatusHandler::handle_signal", FORK);
138  DL((FORK, "Caught signal # %d\n", signum_));
139 
140  if (signum_ == SIGCHLD) {
141  int status;
142  m_caught = true;
143  pid_t ret = ::wait (&status);
144  DL((FORK,"wait() = %d (PID)\n", ret));
145 
146  if (ret > 0 && (WIFEXITED (status))) {
147  m_exit_status = WEXITSTATUS (status);
148  }
149  else {
150  m_exit_status = ret;
151  }
152  }
153 
154  DL((FORK,"child exit_status = %d\n", m_exit_status));
155  return 0;
156 }
157 
158 //------------------------------------------------------------------------------
160 Fork (Fork::state_t exit_action_, Fork::wait4status_t catch_status_)
161 {
162  trace_with_mask("Fork::Fork",FORK);
163 
164  if (catch_status_ == COLLECT_STATUS) {
165  m_local_sh.install (SIGCHLD, &m_chstath, 0, 0, &m_old_disp);
166  }
167 
168  if ((m_pid = fork()) < 0) {
169  EL((ASSAERR,"failed to fork() - out of swap space?\n"));
170  exit (1); // die right here
171  }
172 
173  if (m_pid) { // The Parent
174  if (exit_action_ != LEAVE_ALONE) {
175  ForkList::get_instance()->m_list.push_back (new fnode_t (m_pid, exit_action_));
176  }
177  if (catch_status_ == COLLECT_STATUS) {
178  if (! m_chstath.caught ()) {
179  pause ();
180  }
181  m_local_sh.remove (SIGCHLD, &m_chstath, &m_old_disp, 0);
182  }
183  }
184 }
185 
186 
188 ~ForkList()
189 {
190  trace_with_mask("ForkList::~ForkList",FORK);
191 
192  list<fnode_t* >::iterator i;
193  pid_t pid;
194 
195  // Go through the list and send SIGTERM to those children
196  // whose flags were set at fork time.
197 
198  for (i = m_list.begin(); i != m_list.end(); i++) {
199  if ((*i)->needKill()) {
200  ::kill((*i)->getPID(), SIGTERM);
201  }
202  }
203  // Wait for all children to exit.
204 
205  while ( ! m_list.empty() ) { // wait for child to exit
206  pid = ::wait(NULL);
207  if ( pid < 0 ) { // error on wait
208  EL((ASSAERR,"Error on wait()\n"));
209  exit (EXIT_FAILURE);
210  }
211  // Search for child through the list by its pid.
212  // If found, remove it from list and release memory.
213 
214  list<fnode_t* >::iterator j;
215 
216  for (j = m_list.begin(); j != m_list.end(); j++) {
217  if ((*j)->getPID() == pid) {
218  fnode_t* ep = *j;
219  m_list.erase(j);
220  delete ep;
221  break;
222  }
223  }
224  }
225 }
226 
227 #endif // !defined(WIN32)
Class to handle processing command-line options.
An abstract interface for handling I/O events, timers, and such.
ASSA_DECL_SINGLETON(ForkList)
A simple wrapper around fork() library function call.
#define EL(X)
A macro for writing error message to the Logger.
Definition: Logger.h:285
#define DL(X)
A macro for writing debug message to the Logger.
Definition: Logger.h:273
#define trace_with_mask(s, m)
trace_with_mask() is used to trace function call chain in C++ program.
Definition: Logger.h:437
SigAction is a C++ wrapper around sigaction structure.
Class SigHandler is a UNIX signal handlers manager/dispatcher class.
int handle_signal(int signum_)
Signal handler callback.
Definition: Fork.cpp:135
bool caught() const
Definition: Fork.h:73
static void str_to_argv(const string &src_, int &argc_, char **&argv_)
Static function.
ForkList is a singleton class that keeps a list of all forked children.
Definition: Fork.h:232
list< fnode_t * > m_list
List of children's data structures.
Definition: Fork.h:241
~ForkList()
Destructor. Wipe out childer based on their state.
Definition: Fork.cpp:188
Fork class is a simple wrapper around C library function fork().
Definition: Fork.h:86
pid_t m_pid
Child pid.
Definition: Fork.h:178
state_t
Child completion states.
Definition: Fork.h:91
@ LEAVE_ALONE
Ignore all running children on exit.
Definition: Fork.h:94
bool isChild() const
Test whether we are in child section of the code.
Definition: Fork.h:136
wait4status_t
Definition: Fork.h:99
@ COLLECT_STATUS
Wait for child to complete and collect its exit status.
Definition: Fork.h:101
SigAction m_old_disp
Old signal disposition.
Definition: Fork.h:187
SigHandler m_local_sh
Local signal handler.
Definition: Fork.h:181
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
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
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
pid_t getChildPID() const
Retrieve child process id.
Definition: Fork.h:142
ChildStatusHandler m_chstath
Handler to catch Child's status.
Definition: Fork.h:184
virtual int remove(int signum_, EventHandler *eh_=0, SigAction *new_disp_=0, SigAction *old_disp_=0)
Remove EventHandler associated with signum_.
Definition: SigHandler.cpp:107
virtual int install(int signum_, EventHandler *new_hand_, SigAction *new_disp_=0, EventHandler **old_hand_=0, SigAction *old_disp_=0)
Add new signal handler and new disposition for the signal.
Definition: SigHandler.cpp:70
static ForkList * get_instance()
Return an instance of templated class T.
Definition: Singleton.h:47
forknode_t class.
Definition: Fork.h:195
Definition: Acceptor.h:40
@ FORK
Class Fork messages
Definition: LogMask.h:47
@ ASSAERR
ASSA and system errors
Definition: LogMask.h:34