Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

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 wvsubproc.h.
00006  */
00007 #include "wvsubproc.h"
00008 #include <stdio.h>
00009 #include <unistd.h>
00010 #include <sys/types.h>
00011 #include <sys/wait.h>
00012 #include <sys/time.h>
00013 #include <stdarg.h>
00014 #include <errno.h>
00015 #include <assert.h>
00016 
00017 
00018 WvSubProc::WvSubProc()
00019 {
00020     pid = -1;
00021     running = false;
00022     estatus = 0;
00023 }
00024 
00025 
00026 WvSubProc::~WvSubProc()
00027 {
00028     // we need to kill the process here, or else we could leave
00029     // Zombies lying around...
00030     stop(100);
00031 }
00032 
00033 
00034 int WvSubProc::start(const char cmd[], ...)
00035 {
00036     va_list ap;
00037     int nargs, count, retval;
00038     char *cptr;
00039     char **argv;
00040     
00041     assert(!running);
00042     
00043     // count the number of arguments
00044     va_start(ap, cmd);
00045     for (nargs = 0; (cptr = va_arg(ap, char *)) != NULL; nargs++)
00046         ;
00047     va_end(ap);
00048     
00049     // allocate enough space for all the args, plus NULL.
00050     argv = new char* [nargs+1];
00051     
00052     // copy the arguments into the array
00053     va_start(ap, cmd);
00054     for (count = 0; count < nargs; count++)
00055         argv[count] = va_arg(ap, char *);
00056     argv[nargs] = NULL;
00057     
00058     retval = startv(cmd, argv);
00059     
00060     delete[] argv;
00061     
00062     return retval;
00063 }
00064 
00065 
00066 int WvSubProc::startv(const char cmd[], const char * const *argv)
00067 {
00068     running = false;
00069     estatus = 0;
00070     
00071     pid = fork();   // don't need wvfork(), since we call exec().
00072     //fprintf(stderr, "pid for '%s' is %d\n", cmd, pid);
00073     
00074     if (!pid)
00075     {
00076         // child process
00077          
00078         // set the process group of this process, so "negative" kill will kill
00079         // everything in the whole session, not just the main process.
00080         setpgid(0,0);
00081         
00082         // set up any extra environment variables
00083         WvStringList::Iter i(env);
00084         for (i.rewind(); i.next(); )
00085             putenv(i().edit());
00086         
00087         // run the subprocess.
00088         execvp(cmd, (char * const *)argv);
00089         
00090         // if we get this far, just make sure we exit, not return.  The code 242
00091         // should be somewhat recognizable by the calling process so we know
00092         // something is up.
00093         _exit(242);
00094     }
00095     else if (pid > 0)
00096     {
00097         // parent process
00098         running = true;
00099     }
00100     else if (pid < 0)
00101         return -errno;
00102     
00103     return 0; // ok
00104 }
00105 
00106 
00107 void WvSubProc::kill(int sig)
00108 {
00109     assert(!running || pid > 1);
00110     
00111     if (running)
00112     {
00113         // if the process group has disappeared, kill the main process instead
00114         if (::kill(-pid, sig) < 0 && errno == ESRCH)
00115             kill_primary(sig);
00116     }
00117 }
00118 
00119 
00120 void WvSubProc::kill_primary(int sig)
00121 {
00122     assert(!running || pid > 1);
00123     
00124     if (running)
00125         ::kill(pid, sig);
00126 }
00127 
00128 
00129 void WvSubProc::stop(time_t msec_delay)
00130 {
00131     if (!running) return;
00132     
00133     wait(0);
00134     
00135     if (running)
00136     {
00137         kill(SIGTERM);
00138         wait(msec_delay);
00139     }
00140     
00141     if (running)
00142     {
00143         kill(SIGKILL);
00144         wait(-1);
00145     }
00146 }
00147 
00148 
00149 static long msecdiff(struct timeval &a, struct timeval &b)
00150 {
00151     long secdiff, usecdiff;
00152     
00153     secdiff = a.tv_sec - b.tv_sec;
00154     usecdiff = a.tv_usec - b.tv_usec;
00155     
00156     return secdiff*1000 + usecdiff/1000;
00157 }
00158 
00159 
00160 void WvSubProc::wait(time_t msec_delay)
00161 {
00162     int status;
00163     pid_t dead_pid;
00164     struct timeval tv1, tv2;
00165     struct timezone tz;
00166     
00167     assert(!running || pid > 1);
00168     
00169     if (!running) return;
00170     
00171     gettimeofday(&tv1, &tz);
00172     tv2 = tv1;
00173     
00174     do
00175     {
00176         // note: there's a small chance that the child process
00177         // hasn't run setpgrp() yet, so no processes will be available
00178         // for "-pid".  Wait on pid if it fails.
00179         // 
00180         // also note: waiting on a process group is actually useless since you
00181         // can only get notifications for your direct descendants.  We have
00182         // to "kill" with a zero signal instead to try to detect whether they've
00183         // died or not.
00184         dead_pid = waitpid(-pid, &status, 
00185                            (msec_delay >= 0) ? WNOHANG : 0);
00186         if (dead_pid < 0 && errno == ECHILD)
00187             dead_pid = waitpid(pid, &status, 
00188                                (msec_delay >= 0) ? WNOHANG : 0);
00189         
00190         //fprintf(stderr, "%ld: dead_pid=%d; pid=%d\n",
00191         //      msecdiff(tv2, tv1), dead_pid, pid);
00192         if (dead_pid < 0)
00193         {
00194             // all relevant children are dead!
00195             if (errno == ECHILD)
00196             {
00197                 if (::kill(-pid, 0) && errno == ESRCH)
00198                 {
00199                     running = false;
00200                     pid = -1;
00201                 }
00202             }
00203             else
00204                 perror("WvSubProc::wait");
00205         }
00206         else if (dead_pid == pid)
00207         {
00208             // it's the main process - save its status.
00209             estatus = status;
00210         }
00211         
00212         if (running && msec_delay != 0)
00213         {
00214             // wait a while, so we're not spinning _too_ fast in a loop
00215             usleep(50*1000);
00216         }
00217         
00218         gettimeofday(&tv2, &tz);
00219         
00220     } while (running && msec_delay 
00221              && (msec_delay < 0 || msecdiff(tv2, tv1) < msec_delay));
00222 }

Generated on Fri Apr 5 15:16:53 2002 for WvStreams by doxygen1.2.15