00001
00002
00003
00004
00005
00006
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
00033
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
00044
00045 if (!pid)
00046 {
00047
00048 close(waitfd);
00049
00050 #ifdef RLIMIT_AS
00051
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
00063 execvp(cmd, (char * const *)argv);
00064
00065
00066
00067
00068 _exit(242);
00069 }
00070 else if (pid > 0)
00071 running = true;
00072 else if (pid < 0)
00073 return pid;
00074
00075 return 0;
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
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
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
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
00154 retval = _startv(last_cmd, argv);
00155
00156
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
00175
00176
00177
00178
00179 setpgid(0,0);
00180
00181
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
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
00204 ldpreload = WvString("%s=%s:%s", name,
00205 value, getenv("LD_PRELOAD"));
00206 putenv(ldpreload.edit());
00207 }
00208 }
00209 else if (!value)
00210 {
00211
00212
00213
00214 unsetenv(name);
00215 }
00216 else
00217 putenv(i->edit());
00218 }
00219 }
00220 else if (pid > 0)
00221 {
00222
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
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
00262
00263 assert(pid != 1);
00264 if (::kill(-pid, sig) < 0 && errno == ESRCH)
00265 kill_primary(sig);
00266 }
00267
00268
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);
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
00326
00327
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
00340
00341
00342
00343 dead_pid = waitpid(pid, &status, (msec_delay >= 0) ? WNOHANG : 0);
00344
00345
00346
00347
00348 if (dead_pid == pid
00349 || (dead_pid < 0 && (errno == ECHILD || errno == ESRCH)))
00350 {
00351
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
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
00374
00375
00376 waitpid(subpid, NULL, WNOHANG);
00377
00378 if (::kill(-subpid, 0) && errno == ESRCH)
00379 i.xunlink();
00380 }
00381
00382
00383
00384
00385 if (!wait_children || old_pids.isempty())
00386 xrunning = false;
00387 }
00388
00389
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 }