Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

wvsubproc.cc

Go to the documentation of this file.
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 WvSubProc::WvSubProc()
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         // Set memory limit, if applicable
00051         if (memlimit > 0)
00052         {
00053             struct rlimit rlim;
00054             rlim.rlim_cur = memlimit * 1024 * 1024;
00055             rlim.rlim_max = memlimit * 1024 * 1024;
00056             setrlimit(RLIMIT_AS, &rlim);
00057         }
00058 
00059         // run the subprocess.
00060         execvp(cmd, (char * const *)argv);
00061         
00062         // if we get this far, just make sure we exit, not return.
00063         // The code 242 should be somewhat recognizable by the calling
00064         // process so we know something is up.
00065         _exit(242);
00066     }
00067     else if (pid > 0) // parent process
00068         running = true;
00069     else if (pid < 0)
00070         return pid;
00071     
00072     return 0; // ok
00073 }
00074 
00075 
00076 void WvSubProc::prepare(const char cmd[], ...)
00077 {
00078     va_list ap;
00079     va_start(ap, cmd);
00080     preparev(cmd, ap);
00081     va_end(ap);
00082 }
00083 
00084 
00085 void WvSubProc::preparev(const char cmd[], va_list ap)
00086 {
00087     const char *argptr;
00088     
00089     // remember the command so start_again() will work
00090     last_cmd = cmd;
00091     last_args.zap();
00092     while ((argptr = va_arg(ap, const char *)) != NULL)
00093         last_args.append(new WvString(argptr), true);
00094 }
00095 
00096 
00097 void WvSubProc::preparev(const char cmd[], const char * const *argv)
00098 {
00099     const char * const *argptr;
00100     
00101     // remember the command so start_again() will work
00102     last_cmd = cmd;
00103     last_args.zap();
00104     for (argptr = argv; argptr && *argptr; argptr++)
00105         last_args.append(new WvString(*argptr), true);
00106 }
00107 
00108 void WvSubProc::preparev(const char cmd[], WvStringList &args)
00109 {
00110     last_cmd = cmd;
00111     last_args.zap();
00112 
00113     WvStringList::Iter i(args);
00114     for (i.rewind(); i.next(); )
00115         last_args.append(new WvString(*i), true);
00116 }
00117 
00118 int WvSubProc::start(const char cmd[], ...)
00119 {
00120     va_list ap;
00121     va_start(ap, cmd);
00122     preparev(cmd, ap);
00123     va_end(ap);
00124     
00125     return start_again();
00126 }
00127 
00128 
00129 int WvSubProc::startv(const char cmd[], const char * const *argv)
00130 {
00131     preparev(cmd, argv);
00132     return start_again();
00133 }
00134 
00135 
00136 int WvSubProc::start_again()
00137 {
00138     int retval;
00139     const char **argptr;
00140     
00141     assert(!!last_cmd);
00142     
00143     // create a new argv array from our stored values
00144     const char **argv = new const char*[last_args.count() + 1];
00145     WvStringList::Iter i(last_args);
00146     for (argptr = argv, i.rewind(); i.next(); argptr++)
00147         *argptr = *i;
00148     *argptr = NULL;
00149     
00150     // run the program
00151     retval = _startv(last_cmd, argv);
00152     
00153     // clean up
00154     delete[] argv;
00155     
00156     return retval;
00157 }
00158 
00159 
00160 int WvSubProc::fork(int *waitfd)
00161 {
00162     running = false;
00163     estatus = 0;
00164 
00165     pid = wvfork_start(waitfd);
00166 
00167     if (!pid)
00168     {
00169         // child process
00170          
00171         // set the process group of this process, so "negative" kill
00172         // will kill everything in the whole session, not just the
00173         // main process.
00174         setpgid(0,0);
00175 
00176         // set up any extra environment variables
00177         WvStringList::Iter i(env);
00178         for (i.rewind(); i.next(); )
00179             putenv(i().edit());
00180     }
00181     else if (pid > 0)
00182     {
00183         // parent process
00184         running = true;
00185     }
00186     else if (pid < 0)
00187         return -errno;
00188     
00189     return pid;
00190 }
00191 
00192 
00193 pid_t WvSubProc::pidfile_pid()
00194 {
00195     if (!!pidfile)
00196     {
00197         // unfortunately, we don't have WvFile in basic wvutils...
00198         char buf[1024];
00199         pid_t p = -1;
00200         FILE *file = fopen(pidfile, "r");
00201         
00202         memset(buf, 0, sizeof(buf));
00203         if (file && fread(buf, 1, sizeof(buf), file) > 0)
00204             p = atoi(buf);
00205         if (file)
00206             fclose(file);
00207         if (p <= 0)
00208             p = -1;
00209         return p;
00210     }
00211     
00212     return -1;
00213 }
00214 
00215 
00216 void WvSubProc::kill(int sig)
00217 {
00218     assert(!running || pid > 0 || !old_pids.isempty());
00219     
00220     if (pid > 0)
00221     {
00222         // if the process group has disappeared, kill the main process
00223         // instead.
00224         assert(pid != 1);  // make sure we don't kill -1
00225         if (::kill(-pid, sig) < 0 && errno == ESRCH)
00226             kill_primary(sig);
00227     }
00228     
00229     // kill leftover subprocesses too.
00230     pid_tList::Iter i(old_pids);
00231     for (i.rewind(); i.next(); )
00232     {
00233         pid_t subpid = *i;
00234         assert(subpid != 1 && subpid != -1); // make sure we don't kill -1
00235         if (::kill(-subpid, sig) < 0 && errno == ESRCH)
00236             ::kill(subpid, sig);
00237     }
00238 }
00239 
00240 
00241 void WvSubProc::kill_primary(int sig)
00242 {
00243     assert(!running || pid > 0 || !old_pids.isempty());
00244     
00245     if (running && pid > 0)
00246         ::kill(pid, sig);
00247 }
00248 
00249 
00250 void WvSubProc::stop(time_t msec_delay, bool kill_children)
00251 {
00252     wait(0);
00253     
00254     if (running)
00255     {
00256         if (kill_children)
00257             kill(SIGTERM);
00258         else
00259             kill_primary(SIGTERM);
00260 
00261         wait(msec_delay, kill_children);
00262     }
00263     
00264     if (running)
00265     {
00266         if (kill_children)
00267             kill(SIGKILL);
00268         else
00269             kill_primary(SIGKILL);
00270 
00271         wait(-1, kill_children);
00272     }
00273 }
00274 
00275 
00276 void WvSubProc::wait(time_t msec_delay, bool wait_children)
00277 {
00278     bool xrunning;
00279     int status;
00280     pid_t dead_pid;
00281     struct timeval tv1, tv2;
00282     struct timezone tz;
00283     
00284     assert(!running || pid > 0 || !old_pids.isempty());
00285 
00286     // running might be false if the parent process is dead and you called
00287     // wait(x, false) before.  However, if we're now doing wait(x, true),
00288     // we want to keep going until the children are dead too.
00289     xrunning = (running || (wait_children && !old_pids.isempty()));
00290     
00291     if (!xrunning) return;
00292     
00293     gettimeofday(&tv1, &tz);
00294     tv2 = tv1;
00295     
00296     do
00297     {
00298         if (pid > 0)
00299         {
00300             // waiting on a process group is unfortunately useless
00301             // since you can only get notifications for your direct
00302             // descendants.  We have to "kill" with a zero signal instead
00303             // to try to detect whether they've died or not.
00304             dead_pid = waitpid(pid, &status, (msec_delay >= 0) ? WNOHANG : 0);
00305         
00306             //fprintf(stderr, "%ld: dead_pid=%d; pid=%d\n",
00307             //  msecdiff(tv2, tv1), dead_pid, pid);
00308             
00309             if (dead_pid == pid 
00310                 || (dead_pid < 0 && (errno == ECHILD || errno == ESRCH)))
00311             {
00312                 // the main process is dead - save its status.
00313                 estatus = status;
00314                 old_pids.append(new pid_t(pid), true);
00315                 
00316                 pid_t p2 = pidfile_pid();
00317                 if (pid != p2)
00318                     pid = p2;
00319                 else
00320                     pid = -1;
00321             }
00322             else if (dead_pid < 0)
00323                 perror("WvSubProc::waitpid");
00324         }
00325         
00326         // no need to do this next part if the primary subproc isn't dead yet
00327         if (pid < 0)
00328         {
00329             pid_tList::Iter i(old_pids);
00330             for (i.rewind(); i.next(); )
00331             {
00332                 pid_t subpid = *i;
00333                 
00334                 // if the subproc is our direct descendant, we'll be able
00335                 // to kill it forever if it's a zombie.  Sigh.  waitpid()
00336                 // on it just in case.
00337                 waitpid(subpid, NULL, WNOHANG);
00338                 
00339                 if (::kill(-subpid, 0) && errno == ESRCH)
00340                     i.xunlink();
00341             }
00342             
00343             // if the primary is dead _and_ we either don't care about
00344             // children or all our children are dead, then the subproc
00345             // isn't actually running.
00346             if (!wait_children || old_pids.isempty())
00347                 xrunning = false;
00348         }
00349 
00350         // wait a while, so we're not spinning _too_ fast in a loop
00351         if (xrunning && msec_delay != 0)
00352             usleep(50*1000);
00353         
00354         gettimeofday(&tv2, &tz);
00355         
00356     } while (xrunning && msec_delay
00357              && (msec_delay < 0 || msecdiff(tv2, tv1) < msec_delay));
00358 
00359     if (!xrunning)
00360         running = false;
00361 }

Generated on Sat Mar 13 14:55:58 2004 for WvStreams by doxygen 1.3.6-20040222