Make WvStreams compile with gcc 4.4.
[wvstreams.git] / utils / wvsubproc.cc
bloba7cdb00688d2e58eab4096bf8c94bf59fc818ca1
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * A class for reliably starting/stopping subprocesses. See
6 * wvsubproc.h.
7 */
8 #include "wvsubproc.h"
9 #include "wvtimeutils.h"
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <sys/types.h>
13 #include <sys/wait.h>
14 #include <sys/time.h>
15 #include <stdarg.h>
16 #include <errno.h>
17 #include <assert.h>
19 #include "wvfork.h"
21 void WvSubProc::init()
23 pid = -1;
24 memlimit = -1;
25 running = false;
26 estatus = 0;
30 WvSubProc::~WvSubProc()
32 // we need to kill the process here, or else we could leave
33 // zombies lying around...
34 stop(100);
38 int WvSubProc::_startv(const char cmd[], const char * const *argv)
40 int waitfd = -1;
42 pid = fork(&waitfd);
43 //fprintf(stderr, "pid for '%s' is %d\n", cmd, pid);
45 if (!pid) // child process
47 // unblock the parent.
48 close(waitfd);
50 #ifdef RLIMIT_AS
51 // Set memory limit, if applicable
52 if (memlimit > 0)
54 struct rlimit rlim;
55 memset(&rlim, 0, sizeof(rlim));
56 rlim.rlim_cur = memlimit * 1024 * 1024;
57 rlim.rlim_max = memlimit * 1024 * 1024;
58 setrlimit(RLIMIT_AS, &rlim);
60 #endif
62 // run the subprocess.
63 execvp(cmd, (char * const *)argv);
65 // if we get this far, just make sure we exit, not return.
66 // The code 242 should be somewhat recognizable by the calling
67 // process so we know something is up.
68 _exit(242);
70 else if (pid > 0) // parent process
71 running = true;
72 else if (pid < 0)
73 return pid;
75 return 0; // ok
79 void WvSubProc::prepare(const char cmd[], ...)
81 va_list ap;
82 va_start(ap, cmd);
83 preparev(cmd, ap);
84 va_end(ap);
88 void WvSubProc::preparev(const char cmd[], va_list ap)
90 const char *argptr;
92 // remember the command so start_again() will work
93 last_cmd = cmd;
94 last_args.zap();
95 while ((argptr = va_arg(ap, const char *)) != NULL)
96 last_args.append(new WvString(argptr), true);
100 void WvSubProc::preparev(const char cmd[], const char * const *argv)
102 const char * const *argptr;
104 // remember the command so start_again() will work
105 last_cmd = cmd;
106 last_args.zap();
107 for (argptr = argv; argptr && *argptr; argptr++)
108 last_args.append(new WvString(*argptr), true);
111 void WvSubProc::preparev(const char cmd[], WvStringList &args)
113 last_cmd = cmd;
114 last_args.zap();
116 WvStringList::Iter i(args);
117 for (i.rewind(); i.next(); )
118 last_args.append(new WvString(*i), true);
121 int WvSubProc::start(const char cmd[], ...)
123 va_list ap;
124 va_start(ap, cmd);
125 preparev(cmd, ap);
126 va_end(ap);
128 return start_again();
132 int WvSubProc::startv(const char cmd[], const char * const *argv)
134 preparev(cmd, argv);
135 return start_again();
139 int WvSubProc::start_again()
141 int retval;
142 const char **argptr;
144 assert(!!last_cmd);
146 // create a new argv array from our stored values
147 const char **argv = new const char*[last_args.count() + 1];
148 WvStringList::Iter i(last_args);
149 for (argptr = argv, i.rewind(); i.next(); argptr++)
150 *argptr = *i;
151 *argptr = NULL;
153 // run the program
154 retval = _startv(last_cmd, argv);
156 // clean up
157 deletev argv;
159 return retval;
163 int WvSubProc::fork(int *waitfd)
165 static WvString ldpreload, ldlibrary;
167 running = false;
168 estatus = 0;
170 pid = wvfork_start(waitfd);
172 if (!pid)
174 // child process
176 // set the process group of this process, so "negative" kill
177 // will kill everything in the whole session, not just the
178 // main process.
179 setpgid(0,0);
181 // set up any extra environment variables
182 WvStringList::Iter i(env);
183 for (i.rewind(); i.next(); )
185 WvStringList words;
186 words.splitstrict(*i, "=");
187 WvString name = words.popstr();
188 WvString value = words.join("=");
189 if (name == "LD_LIBRARY_PATH" && getenv("LD_LIBRARY_PATH"))
191 if (!!value)
193 // don't override - merge!
194 ldlibrary = WvString("%s=%s:%s", name,
195 value, getenv("LD_LIBRARY_PATH"));
196 putenv(ldlibrary.edit());
199 else if (name == "LD_PRELOAD" && getenv("LD_PRELOAD"))
201 if (!!value)
203 // don't override - merge!
204 ldpreload = WvString("%s=%s:%s", name,
205 value, getenv("LD_PRELOAD"));
206 putenv(ldpreload.edit());
209 else if (!value)
211 // no equals or setting to empty string?
212 // then we must want to unset it!
213 // This is evil, but this is the most simple
214 unsetenv(name);
216 else
217 putenv(i->edit());
220 else if (pid > 0)
222 // parent process
223 running = true;
225 else if (pid < 0)
226 return -errno;
228 return pid;
232 pid_t WvSubProc::pidfile_pid()
234 if (!!pidfile)
236 // unfortunately, we don't have WvFile in basic wvutils...
237 char buf[1024];
238 pid_t p = -1;
239 FILE *file = fopen(pidfile, "r");
241 memset(buf, 0, sizeof(buf));
242 if (file && fread(buf, 1, sizeof(buf), file) > 0)
243 p = atoi(buf);
244 if (file)
245 fclose(file);
246 if (p <= 0)
247 p = -1;
248 return p;
251 return -1;
255 void WvSubProc::kill(int sig)
257 assert(!running || pid > 0 || !old_pids.isempty());
259 if (pid > 0)
261 // if the process group has disappeared, kill the main process
262 // instead.
263 assert(pid != 1); // make sure we don't kill -1
264 if (::kill(-pid, sig) < 0 && errno == ESRCH)
265 kill_primary(sig);
268 // kill leftover subprocesses too.
269 pid_tList::Iter i(old_pids);
270 for (i.rewind(); i.next(); )
272 pid_t subpid = *i;
273 assert(subpid != 1 && subpid != -1); // make sure we don't kill -1
274 if (::kill(-subpid, sig) < 0 && errno == ESRCH)
275 ::kill(subpid, sig);
280 void WvSubProc::kill_primary(int sig)
282 assert(!running || pid > 0 || !old_pids.isempty());
284 if (running && pid > 0)
285 ::kill(pid, sig);
289 void WvSubProc::stop(time_t msec_delay, bool kill_children)
291 wait(0);
293 if (running)
295 if (kill_children)
296 kill(SIGTERM);
297 else
298 kill_primary(SIGTERM);
300 wait(msec_delay, kill_children);
303 if (running)
305 if (kill_children)
306 kill(SIGKILL);
307 else
308 kill_primary(SIGKILL);
310 wait(-1, kill_children);
315 void WvSubProc::wait(time_t msec_delay, bool wait_children)
317 bool xrunning;
318 int status;
319 pid_t dead_pid;
320 struct timeval tv1, tv2;
321 struct timezone tz;
323 assert(!running || pid > 0 || !old_pids.isempty());
325 // running might be false if the parent process is dead and you called
326 // wait(x, false) before. However, if we're now doing wait(x, true),
327 // we want to keep going until the children are dead too.
328 xrunning = (running || (wait_children && !old_pids.isempty()));
330 if (!xrunning) return;
332 gettimeofday(&tv1, &tz);
333 tv2 = tv1;
337 if (pid > 0)
339 // waiting on a process group is unfortunately useless
340 // since you can only get notifications for your direct
341 // descendants. We have to "kill" with a zero signal instead
342 // to try to detect whether they've died or not.
343 dead_pid = waitpid(pid, &status, (msec_delay >= 0) ? WNOHANG : 0);
345 //fprintf(stderr, "%ld: dead_pid=%d; pid=%d\n",
346 // msecdiff(tv2, tv1), dead_pid, pid);
348 if (dead_pid == pid
349 || (dead_pid < 0 && (errno == ECHILD || errno == ESRCH)))
351 // the main process is dead - save its status.
352 estatus = status;
353 old_pids.append(new pid_t(pid), true);
355 pid_t p2 = pidfile_pid();
356 if (pid != p2)
357 pid = p2;
358 else
359 pid = -1;
361 else if (dead_pid < 0)
362 perror("WvSubProc::waitpid");
365 // no need to do this next part if the primary subproc isn't dead yet
366 if (pid < 0)
368 pid_tList::Iter i(old_pids);
369 for (i.rewind(); i.next(); )
371 pid_t subpid = *i;
373 // if the subproc is our direct descendant, we'll be able
374 // to kill it forever if it's a zombie. Sigh. waitpid()
375 // on it just in case.
376 waitpid(subpid, NULL, WNOHANG);
378 if (::kill(-subpid, 0) && errno == ESRCH)
379 i.xunlink();
382 // if the primary is dead _and_ we either don't care about
383 // children or all our children are dead, then the subproc
384 // isn't actually running.
385 if (!wait_children || old_pids.isempty())
386 xrunning = false;
389 // wait a while, so we're not spinning _too_ fast in a loop
390 if (xrunning && msec_delay != 0)
391 usleep(50*1000);
393 gettimeofday(&tv2, &tz);
395 } while (xrunning && msec_delay
396 && (msec_delay < 0 || msecdiff(tv2, tv1) < msec_delay));
398 if (!xrunning)
399 running = false;