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 Tue Oct 5 01:09:21 2004 for WvStreams by doxygen 1.3.7