WvStreams
|
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 WvStringList words; 00186 words.splitstrict(*i, "="); 00187 WvString name = words.popstr(); 00188 WvString value = words.join("="); 00189 if (name == "LD_LIBRARY_PATH" && getenv("LD_LIBRARY_PATH")) 00190 { 00191 if (!!value) 00192 { 00193 // don't override - merge! 00194 ldlibrary = WvString("%s=%s:%s", name, 00195 value, getenv("LD_LIBRARY_PATH")); 00196 putenv(ldlibrary.edit()); 00197 } 00198 } 00199 else if (name == "LD_PRELOAD" && getenv("LD_PRELOAD")) 00200 { 00201 if (!!value) 00202 { 00203 // don't override - merge! 00204 ldpreload = WvString("%s=%s:%s", name, 00205 value, getenv("LD_PRELOAD")); 00206 putenv(ldpreload.edit()); 00207 } 00208 } 00209 else if (!value) 00210 { 00211 // no equals or setting to empty string? 00212 // then we must want to unset it! 00213 // This is evil, but this is the most simple 00214 unsetenv(name); 00215 } 00216 else 00217 putenv(i->edit()); 00218 } 00219 } 00220 else if (pid > 0) 00221 { 00222 // parent process 00223 running = true; 00224 } 00225 else if (pid < 0) 00226 return -errno; 00227 00228 return pid; 00229 } 00230 00231 00232 pid_t WvSubProc::pidfile_pid() 00233 { 00234 if (!!pidfile) 00235 { 00236 // unfortunately, we don't have WvFile in basic wvutils... 00237 char buf[1024]; 00238 pid_t p = -1; 00239 FILE *file = fopen(pidfile, "r"); 00240 00241 memset(buf, 0, sizeof(buf)); 00242 if (file && fread(buf, 1, sizeof(buf), file) > 0) 00243 p = atoi(buf); 00244 if (file) 00245 fclose(file); 00246 if (p <= 0) 00247 p = -1; 00248 return p; 00249 } 00250 00251 return -1; 00252 } 00253 00254 00255 void WvSubProc::kill(int sig) 00256 { 00257 assert(!running || pid > 0 || !old_pids.isempty()); 00258 00259 if (pid > 0) 00260 { 00261 // if the process group has disappeared, kill the main process 00262 // instead. 00263 assert(pid != 1); // make sure we don't kill -1 00264 if (::kill(-pid, sig) < 0 && errno == ESRCH) 00265 kill_primary(sig); 00266 } 00267 00268 // kill leftover subprocesses too. 00269 pid_tList::Iter i(old_pids); 00270 for (i.rewind(); i.next(); ) 00271 { 00272 pid_t subpid = *i; 00273 assert(subpid != 1 && subpid != -1); // make sure we don't kill -1 00274 if (::kill(-subpid, sig) < 0 && errno == ESRCH) 00275 ::kill(subpid, sig); 00276 } 00277 } 00278 00279 00280 void WvSubProc::kill_primary(int sig) 00281 { 00282 assert(!running || pid > 0 || !old_pids.isempty()); 00283 00284 if (running && pid > 0) 00285 ::kill(pid, sig); 00286 } 00287 00288 00289 void WvSubProc::stop(time_t msec_delay, bool kill_children) 00290 { 00291 wait(0); 00292 00293 if (running) 00294 { 00295 if (kill_children) 00296 kill(SIGTERM); 00297 else 00298 kill_primary(SIGTERM); 00299 00300 wait(msec_delay, kill_children); 00301 } 00302 00303 if (running) 00304 { 00305 if (kill_children) 00306 kill(SIGKILL); 00307 else 00308 kill_primary(SIGKILL); 00309 00310 wait(-1, kill_children); 00311 } 00312 } 00313 00314 00315 void WvSubProc::wait(time_t msec_delay, bool wait_children) 00316 { 00317 bool xrunning; 00318 int status; 00319 pid_t dead_pid; 00320 struct timeval tv1, tv2; 00321 struct timezone tz; 00322 00323 assert(!running || pid > 0 || !old_pids.isempty()); 00324 00325 // running might be false if the parent process is dead and you called 00326 // wait(x, false) before. However, if we're now doing wait(x, true), 00327 // we want to keep going until the children are dead too. 00328 xrunning = (running || (wait_children && !old_pids.isempty())); 00329 00330 if (!xrunning) return; 00331 00332 gettimeofday(&tv1, &tz); 00333 tv2 = tv1; 00334 00335 do 00336 { 00337 if (pid > 0) 00338 { 00339 // waiting on a process group is unfortunately useless 00340 // since you can only get notifications for your direct 00341 // descendants. We have to "kill" with a zero signal instead 00342 // to try to detect whether they've died or not. 00343 dead_pid = waitpid(pid, &status, (msec_delay >= 0) ? WNOHANG : 0); 00344 00345 //fprintf(stderr, "%ld: dead_pid=%d; pid=%d\n", 00346 // msecdiff(tv2, tv1), dead_pid, pid); 00347 00348 if (dead_pid == pid 00349 || (dead_pid < 0 && (errno == ECHILD || errno == ESRCH))) 00350 { 00351 // the main process is dead - save its status. 00352 estatus = status; 00353 old_pids.append(new pid_t(pid), true); 00354 00355 pid_t p2 = pidfile_pid(); 00356 if (pid != p2) 00357 pid = p2; 00358 else 00359 pid = -1; 00360 } 00361 else if (dead_pid < 0) 00362 perror("WvSubProc::waitpid"); 00363 } 00364 00365 // no need to do this next part if the primary subproc isn't dead yet 00366 if (pid < 0) 00367 { 00368 pid_tList::Iter i(old_pids); 00369 for (i.rewind(); i.next(); ) 00370 { 00371 pid_t subpid = *i; 00372 00373 // if the subproc is our direct descendant, we'll be able 00374 // to kill it forever if it's a zombie. Sigh. waitpid() 00375 // on it just in case. 00376 waitpid(subpid, NULL, WNOHANG); 00377 00378 if (::kill(-subpid, 0) && errno == ESRCH) 00379 i.xunlink(); 00380 } 00381 00382 // if the primary is dead _and_ we either don't care about 00383 // children or all our children are dead, then the subproc 00384 // isn't actually running. 00385 if (!wait_children || old_pids.isempty()) 00386 xrunning = false; 00387 } 00388 00389 // wait a while, so we're not spinning _too_ fast in a loop 00390 if (xrunning && msec_delay != 0) 00391 usleep(50*1000); 00392 00393 gettimeofday(&tv2, &tz); 00394 00395 } while (xrunning && msec_delay 00396 && (msec_delay < 0 || msecdiff(tv2, tv1) < msec_delay)); 00397 00398 if (!xrunning) 00399 running = false; 00400 }