2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
5 * A class for reliably starting/stopping subprocesses. See
9 #include "wvtimeutils.h"
12 #include <sys/types.h>
21 void WvSubProc::init()
30 WvSubProc::~WvSubProc()
32 // we need to kill the process here, or else we could leave
33 // zombies lying around...
38 int WvSubProc::_startv(const char cmd
[], const char * const *argv
)
43 //fprintf(stderr, "pid for '%s' is %d\n", cmd, pid);
45 if (!pid
) // child process
47 // unblock the parent.
51 // Set memory limit, if applicable
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
);
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.
70 else if (pid
> 0) // parent process
79 void WvSubProc::prepare(const char cmd
[], ...)
88 void WvSubProc::preparev(const char cmd
[], va_list ap
)
92 // remember the command so start_again() will work
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
107 for (argptr
= argv
; argptr
&& *argptr
; argptr
++)
108 last_args
.append(new WvString(*argptr
), true);
111 void WvSubProc::preparev(const char cmd
[], WvStringList
&args
)
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
[], ...)
128 return start_again();
132 int WvSubProc::startv(const char cmd
[], const char * const *argv
)
135 return start_again();
139 int WvSubProc::start_again()
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
++)
154 retval
= _startv(last_cmd
, argv
);
163 int WvSubProc::fork(int *waitfd
)
165 static WvString ldpreload
, ldlibrary
;
170 pid
= wvfork_start(waitfd
);
176 // set the process group of this process, so "negative" kill
177 // will kill everything in the whole session, not just the
181 // set up any extra environment variables
182 WvStringList::Iter
i(env
);
183 for (i
.rewind(); i
.next(); )
186 words
.splitstrict(*i
, "=");
187 WvString name
= words
.popstr();
188 WvString value
= words
.join("=");
189 if (name
== "LD_LIBRARY_PATH" && getenv("LD_LIBRARY_PATH"))
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"))
203 // don't override - merge!
204 ldpreload
= WvString("%s=%s:%s", name
,
205 value
, getenv("LD_PRELOAD"));
206 putenv(ldpreload
.edit());
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
232 pid_t
WvSubProc::pidfile_pid()
236 // unfortunately, we don't have WvFile in basic wvutils...
239 FILE *file
= fopen(pidfile
, "r");
241 memset(buf
, 0, sizeof(buf
));
242 if (file
&& fread(buf
, 1, sizeof(buf
), file
) > 0)
255 void WvSubProc::kill(int sig
)
257 assert(!running
|| pid
> 0 || !old_pids
.isempty());
261 // if the process group has disappeared, kill the main process
263 assert(pid
!= 1); // make sure we don't kill -1
264 if (::kill(-pid
, sig
) < 0 && errno
== ESRCH
)
268 // kill leftover subprocesses too.
269 pid_tList::Iter
i(old_pids
);
270 for (i
.rewind(); i
.next(); )
273 assert(subpid
!= 1 && subpid
!= -1); // make sure we don't kill -1
274 if (::kill(-subpid
, sig
) < 0 && errno
== ESRCH
)
280 void WvSubProc::kill_primary(int sig
)
282 assert(!running
|| pid
> 0 || !old_pids
.isempty());
284 if (running
&& pid
> 0)
289 void WvSubProc::stop(time_t msec_delay
, bool kill_children
)
298 kill_primary(SIGTERM
);
300 wait(msec_delay
, kill_children
);
308 kill_primary(SIGKILL
);
310 wait(-1, kill_children
);
315 void WvSubProc::wait(time_t msec_delay
, bool wait_children
)
320 struct timeval tv1
, tv2
;
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
);
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);
349 || (dead_pid
< 0 && (errno
== ECHILD
|| errno
== ESRCH
)))
351 // the main process is dead - save its status.
353 old_pids
.append(new pid_t(pid
), true);
355 pid_t p2
= pidfile_pid();
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
368 pid_tList::Iter
i(old_pids
);
369 for (i
.rewind(); i
.next(); )
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
)
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())
389 // wait a while, so we're not spinning _too_ fast in a loop
390 if (xrunning
&& msec_delay
!= 0)
393 gettimeofday(&tv2
, &tz
);
395 } while (xrunning
&& msec_delay
396 && (msec_delay
< 0 || msecdiff(tv2
, tv1
) < msec_delay
));