wvsubproc.cc

00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * A class for reliably starting/stopping subprocesses.  See
00006  * wvsubproc.h.
00007  */
00008 #include "wvsubproc.h"
00009 #include "wvtimeutils.h"
00010 #include <stdio.h>
00011 #include <unistd.h>
00012 #include <sys/types.h>
00013 #include <sys/wait.h>
00014 #include <sys/time.h>
00015 #include <stdarg.h>
00016 #include <errno.h>
00017 #include <assert.h>
00018 
00019 #include "wvfork.h"
00020 
00021 void WvSubProc::init()
00022 {
00023     pid = -1;
00024     memlimit = -1;
00025     running = false;
00026     estatus = 0;
00027 }
00028 
00029 
00030 WvSubProc::~WvSubProc()
00031 {
00032     // we need to kill the process here, or else we could leave
00033     // zombies lying around...
00034     stop(100);
00035 }
00036 
00037 
00038 int WvSubProc::_startv(const char cmd[], const char * const *argv)
00039 {
00040     int waitfd = -1;
00041     
00042     pid = fork(&waitfd);
00043     //fprintf(stderr, "pid for '%s' is %d\n", cmd, pid);
00044     
00045     if (!pid) // child process
00046     {
00047         // unblock the parent.
00048         close(waitfd);
00049         
00050 #ifdef RLIMIT_AS
00051         // Set memory limit, if applicable
00052         if (memlimit > 0)
00053         {
00054             struct rlimit rlim;
00055             memset(&rlim, 0, sizeof(rlim));
00056             rlim.rlim_cur = memlimit * 1024 * 1024;
00057             rlim.rlim_max = memlimit * 1024 * 1024;
00058             setrlimit(RLIMIT_AS, &rlim);
00059         }
00060 #endif
00061 
00062         // run the subprocess.
00063         execvp(cmd, (char * const *)argv);
00064         
00065         // if we get this far, just make sure we exit, not return.
00066         // The code 242 should be somewhat recognizable by the calling
00067         // process so we know something is up.
00068         _exit(242);
00069     }
00070     else if (pid > 0) // parent process
00071         running = true;
00072     else if (pid < 0)
00073         return pid;
00074     
00075     return 0; // ok
00076 }
00077 
00078 
00079 void WvSubProc::prepare(const char cmd[], ...)
00080 {
00081     va_list ap;
00082     va_start(ap, cmd);
00083     preparev(cmd, ap);
00084     va_end(ap);
00085 }
00086 
00087 
00088 void WvSubProc::preparev(const char cmd[], va_list ap)
00089 {
00090     const char *argptr;
00091     
00092     // remember the command so start_again() will work
00093     last_cmd = cmd;
00094     last_args.zap();
00095     while ((argptr = va_arg(ap, const char *)) != NULL)
00096         last_args.append(new WvString(argptr), true);
00097 }
00098 
00099 
00100 void WvSubProc::preparev(const char cmd[], const char * const *argv)
00101 {
00102     const char * const *argptr;
00103     
00104     // remember the command so start_again() will work
00105     last_cmd = cmd;
00106     last_args.zap();
00107     for (argptr = argv; argptr && *argptr; argptr++)
00108         last_args.append(new WvString(*argptr), true);
00109 }
00110 
00111 void WvSubProc::preparev(const char cmd[], WvStringList &args)
00112 {
00113     last_cmd = cmd;
00114     last_args.zap();
00115 
00116     WvStringList::Iter i(args);
00117     for (i.rewind(); i.next(); )
00118         last_args.append(new WvString(*i), true);
00119 }
00120 
00121 int WvSubProc::start(const char cmd[], ...)
00122 {
00123     va_list ap;
00124     va_start(ap, cmd);
00125     preparev(cmd, ap);
00126     va_end(ap);
00127     
00128     return start_again();
00129 }
00130 
00131 
00132 int WvSubProc::startv(const char cmd[], const char * const *argv)
00133 {
00134     preparev(cmd, argv);
00135     return start_again();
00136 }
00137 
00138 
00139 int WvSubProc::start_again()
00140 {
00141     int retval;
00142     const char **argptr;
00143     
00144     assert(!!last_cmd);
00145     
00146     // create a new argv array from our stored values
00147     const char **argv = new const char*[last_args.count() + 1];
00148     WvStringList::Iter i(last_args);
00149     for (argptr = argv, i.rewind(); i.next(); argptr++)
00150         *argptr = *i;
00151     *argptr = NULL;
00152     
00153     // run the program
00154     retval = _startv(last_cmd, argv);
00155     
00156     // clean up
00157     deletev argv;
00158     
00159     return retval;
00160 }
00161 
00162 
00163 int WvSubProc::fork(int *waitfd)
00164 {
00165     static WvString ldpreload, ldlibrary;
00166     
00167     running = false;
00168     estatus = 0;
00169 
00170     pid = wvfork_start(waitfd);
00171 
00172     if (!pid)
00173     {
00174         // child process
00175          
00176         // set the process group of this process, so "negative" kill
00177         // will kill everything in the whole session, not just the
00178         // main process.
00179         setpgid(0,0);
00180 
00181         // set up any extra environment variables
00182         WvStringList::Iter i(env);
00183         for (i.rewind(); i.next(); )
00184         {
00185             if (!strncmp(*i, "LD_LIBRARY_PATH=", 16)
00186                 && getenv("LD_LIBRARY_PATH"))
00187             {
00188                 // don't override - merge!
00189                 ldlibrary = WvString("%s:%s", *i,
00190                                      getenv("LD_LIBRARY_PATH") + 16);
00191                 putenv(ldlibrary.edit());
00192             }
00193             else if (!strncmp(*i, "LD_PRELOAD=", 11)
00194                   && getenv("LD_PRELOAD"))
00195             {
00196                 // don't override - merge!
00197                 ldpreload = WvString("%s:%s", *i,
00198                                      getenv("LD_PRELOAD") + 11);
00199                 putenv(ldpreload.edit());
00200             }
00201             else if (!strstr(*i, "="))
00202             {
00203                 // no equals? then we must want to unset it!
00204                 // This is evil, but this is the most simple
00205                 unsetenv(*i);
00206             } 
00207             else 
00208                 putenv(i->edit());
00209         }
00210     }
00211     else if (pid > 0)
00212     {
00213         // parent process
00214         running = true;
00215     }
00216     else if (pid < 0)
00217         return -errno;
00218     
00219     return pid;
00220 }
00221 
00222 
00223 pid_t WvSubProc::pidfile_pid()
00224 {
00225     if (!!pidfile)
00226     {
00227         // unfortunately, we don't have WvFile in basic wvutils...
00228         char buf[1024];
00229         pid_t p = -1;
00230         FILE *file = fopen(pidfile, "r");
00231         
00232         memset(buf, 0, sizeof(buf));
00233         if (file && fread(buf, 1, sizeof(buf), file) > 0)
00234             p = atoi(buf);
00235         if (file)
00236             fclose(file);
00237         if (p <= 0)
00238             p = -1;
00239         return p;
00240     }
00241     
00242     return -1;
00243 }
00244 
00245 
00246 void WvSubProc::kill(int sig)
00247 {
00248     assert(!running || pid > 0 || !old_pids.isempty());
00249     
00250     if (pid > 0)
00251     {
00252         // if the process group has disappeared, kill the main process
00253         // instead.
00254         assert(pid != 1);  // make sure we don't kill -1
00255         if (::kill(-pid, sig) < 0 && errno == ESRCH)
00256             kill_primary(sig);
00257     }
00258     
00259     // kill leftover subprocesses too.
00260     pid_tList::Iter i(old_pids);
00261     for (i.rewind(); i.next(); )
00262     {
00263         pid_t subpid = *i;
00264         assert(subpid != 1 && subpid != -1); // make sure we don't kill -1
00265         if (::kill(-subpid, sig) < 0 && errno == ESRCH)
00266 	    ::kill(subpid, sig);
00267     }
00268 }
00269 
00270 
00271 void WvSubProc::kill_primary(int sig)
00272 {
00273     assert(!running || pid > 0 || !old_pids.isempty());
00274     
00275     if (running && pid > 0)
00276 	::kill(pid, sig);
00277 }
00278 
00279 
00280 void WvSubProc::stop(time_t msec_delay, bool kill_children)
00281 {
00282     wait(0);
00283     
00284     if (running)
00285     {
00286         if (kill_children)
00287             kill(SIGTERM);
00288         else
00289             kill_primary(SIGTERM);
00290 
00291         wait(msec_delay, kill_children);
00292     }
00293     
00294     if (running)
00295     {
00296         if (kill_children)
00297             kill(SIGKILL);
00298         else
00299             kill_primary(SIGKILL);
00300 
00301         wait(-1, kill_children);
00302     }
00303 }
00304 
00305 
00306 void WvSubProc::wait(time_t msec_delay, bool wait_children)
00307 {
00308     bool xrunning;
00309     int status;
00310     pid_t dead_pid;
00311     struct timeval tv1, tv2;
00312     struct timezone tz;
00313     
00314     assert(!running || pid > 0 || !old_pids.isempty());
00315 
00316     // running might be false if the parent process is dead and you called
00317     // wait(x, false) before.  However, if we're now doing wait(x, true),
00318     // we want to keep going until the children are dead too.
00319     xrunning = (running || (wait_children && !old_pids.isempty()));
00320     
00321     if (!xrunning) return;
00322     
00323     gettimeofday(&tv1, &tz);
00324     tv2 = tv1;
00325     
00326     do
00327     {
00328         if (pid > 0)
00329         {
00330             // waiting on a process group is unfortunately useless
00331             // since you can only get notifications for your direct
00332             // descendants.  We have to "kill" with a zero signal instead
00333             // to try to detect whether they've died or not.
00334             dead_pid = waitpid(pid, &status, (msec_delay >= 0) ? WNOHANG : 0);
00335         
00336             //fprintf(stderr, "%ld: dead_pid=%d; pid=%d\n",
00337             //  msecdiff(tv2, tv1), dead_pid, pid);
00338             
00339             if (dead_pid == pid 
00340                 || (dead_pid < 0 && (errno == ECHILD || errno == ESRCH)))
00341             {
00342                 // the main process is dead - save its status.
00343                 estatus = status;
00344                 old_pids.append(new pid_t(pid), true);
00345                 
00346                 pid_t p2 = pidfile_pid();
00347                 if (pid != p2)
00348                     pid = p2;
00349                 else
00350                     pid = -1;
00351             }
00352             else if (dead_pid < 0)
00353                 perror("WvSubProc::waitpid");
00354         }
00355         
00356         // no need to do this next part if the primary subproc isn't dead yet
00357         if (pid < 0)
00358         {
00359             pid_tList::Iter i(old_pids);
00360             for (i.rewind(); i.next(); )
00361             {
00362                 pid_t subpid = *i;
00363                 
00364                 // if the subproc is our direct descendant, we'll be able
00365                 // to kill it forever if it's a zombie.  Sigh.  waitpid()
00366                 // on it just in case.
00367                 waitpid(subpid, NULL, WNOHANG);
00368                 
00369                 if (::kill(-subpid, 0) && errno == ESRCH)
00370                     i.xunlink();
00371             }
00372             
00373             // if the primary is dead _and_ we either don't care about
00374             // children or all our children are dead, then the subproc
00375             // isn't actually running.
00376             if (!wait_children || old_pids.isempty())
00377                 xrunning = false;
00378         }
00379 
00380         // wait a while, so we're not spinning _too_ fast in a loop
00381         if (xrunning && msec_delay != 0)
00382             usleep(50*1000);
00383         
00384         gettimeofday(&tv2, &tz);
00385         
00386     } while (xrunning && msec_delay
00387              && (msec_delay < 0 || msecdiff(tv2, tv1) < msec_delay));
00388 
00389     if (!xrunning)
00390         running = false;
00391 }

Generated on Thu May 25 21:51:04 2006 for WvStreams by  doxygen 1.4.6