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 WvSubProc::WvSubProc()
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
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_RSS, &rlim);
00057 }
00058
00059
00060 execvp(cmd, (char * const *)argv);
00061
00062
00063
00064
00065 _exit(242);
00066 }
00067 else if (pid > 0)
00068 running = true;
00069 else if (pid < 0)
00070 return pid;
00071
00072 return 0;
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
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
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
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
00151 retval = _startv(last_cmd, argv);
00152
00153
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
00170
00171
00172
00173
00174 setpgid(0,0);
00175
00176
00177 WvStringList::Iter i(env);
00178 for (i.rewind(); i.next(); )
00179 putenv(i().edit());
00180 }
00181 else if (pid > 0)
00182 {
00183
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
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
00223
00224 assert(pid != 1);
00225 if (::kill(-pid, sig) < 0 && errno == ESRCH)
00226 kill_primary(sig);
00227 }
00228
00229
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);
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
00287
00288
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
00301
00302
00303
00304 dead_pid = waitpid(pid, &status, (msec_delay >= 0) ? WNOHANG : 0);
00305
00306
00307
00308
00309 if (dead_pid == pid
00310 || (dead_pid < 0 && (errno == ECHILD || errno == ESRCH)))
00311 {
00312
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
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
00335
00336
00337 waitpid(subpid, NULL, WNOHANG);
00338
00339 if (::kill(-subpid, 0) && errno == ESRCH)
00340 i.xunlink();
00341 }
00342
00343
00344
00345
00346 if (!wait_children || old_pids.isempty())
00347 xrunning = false;
00348 }
00349
00350
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 }