modified: makefile
[GalaxyCodeBases.git] / tools / lh3misc / sys / runit / runit.c
blob3876eeb0704d62e6322109fb7d30b5549476481a
1 /* Author: attractivechaos */
3 /* last modified: 2008-0306 */
5 #include <sys/time.h>
6 #include <sys/times.h>
7 #include <sys/wait.h>
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <signal.h>
13 #include <assert.h>
14 #include <fcntl.h>
15 #include <pthread.h>
16 #include "runlib.h"
18 typedef struct
20 double cpu_limit;
21 double mem_limit;
22 int check_interval, interval;
23 int timeout;
24 size_t memoryout;
25 FILE *log_fp;
26 char *prefix;
27 int out, err;
28 int fd_out[2], fd_err[2];
29 } RunStruct;
31 typedef struct
33 unsigned error;
34 double utime, stime, rtime;
35 size_t max_rss, max_vsize;
36 int retval;
37 double avg_rss, avg_vsize;
38 } RunResult;
40 /* declarations to provide consistent linkage */
41 extern char *optarg;
42 extern int optind;
43 extern int opterr;
45 int getopt_standard(int nargc, char * const *nargv, const char *ostr);
47 RunStruct *run_new_RunStruct()
49 RunStruct *rs = (RunStruct*)malloc(sizeof(RunStruct));
50 rs->interval = 10000;
51 rs->check_interval = 1;
52 rs->log_fp = stderr;
53 rs->out = STDOUT_FILENO;
54 rs->err = STDERR_FILENO;
55 rs->timeout = 0;
56 rs->memoryout = 0;
57 rs->mem_limit = 0.05;
58 rs->cpu_limit = 1.0;
59 rs->prefix = 0;
60 return rs;
62 void run_upd_RunStruct(RunStruct *rs)
64 rs->log_fp = stderr;
65 if (rs->prefix) {
66 char *prefix = rs->prefix;
67 char *tmp = (char*)malloc(strlen(prefix) + 5);
68 pipe(rs->fd_out); pipe(rs->fd_err);
69 strcpy(tmp, prefix); strcat(tmp, ".log");
70 rs->log_fp = fopen(tmp, "w");
71 strcpy(tmp, prefix); strcat(tmp, ".out");
72 rs->out = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0644);
73 strcpy(tmp, prefix); strcat(tmp, ".err");
74 rs->err = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0644);
75 free(tmp);
78 void run_free_RunStruct(RunStruct *rs)
80 /* rs->out, rs->err, rs->fd_out[2], rs->fd_err[2] will be closed elsewhere */
81 if (rs->log_fp != stderr) fclose(rs->log_fp);
82 free(rs->prefix);
83 free(rs);
85 int run_fill_RunStruct(int argc, char *argv[], RunStruct *rs)
87 int c;
88 while ((c = getopt_standard(argc, argv, "c:m:p:T:M:")) >= 0) {
89 switch (c) {
90 case 'c': rs->cpu_limit = atof(optarg); break;
91 case 'm': rs->mem_limit = atof(optarg); break;
92 case 'T': rs->timeout = atoi(optarg); break;
93 case 'M': rs->memoryout = atoi(optarg); break;
94 case 'p': rs->prefix = strdup(optarg); break;
97 return optind;
100 static void *write_out(void *arg)
102 RunStruct *rs = (RunStruct*)arg;
103 char *buf[1024];
104 int len;
105 close(rs->fd_out[1]);
106 while ((len = read(rs->fd_out[0], buf, 10)) != 0)
107 write(rs->out, buf, len);
108 close(rs->fd_out[0]);
109 close(rs->out);
110 return 0;
112 static void *write_err(void *arg)
114 RunStruct *rs = (RunStruct*)arg;
115 char *buf[1024];
116 int len;
117 close(rs->fd_err[1]);
118 while ((len = read(rs->fd_err[0], buf, 10)) != 0)
119 write(rs->err, buf, len);
120 close(rs->fd_err[0]);
121 close(rs->err);
122 return 0;
125 RunResult *runit(int argc, char *argv[], RunStruct *rs)
127 pid_t pid;
128 if ((pid = fork()) != 0) {
129 struct tms time_used;
130 int status, ret, check_time;
131 int is_killed;
132 double sum_rss, sum_vsize;
133 double start_time;
134 double last_time;
135 RunSysStatic rss;
136 RunSysDyn rsd;
137 RunProcDyn rpd;
138 RunResult *rr;
139 pthread_t outid, errid;
141 run_get_static_sys_info(&rss);
142 run_get_dynamic_sys_info(&rsd);
143 start_time = rsd.wall_clock;
144 rr = (RunResult*)malloc(sizeof(RunResult));
145 rr->max_rss = rr->max_vsize = 0;
146 rr->error = 0;
147 check_time = 1;
148 sum_rss = sum_vsize = 0.0;
149 last_time = 0.0;
150 is_killed = 1;
151 if (rs->prefix) {
152 pthread_create(&outid, 0, write_out, rs);
153 pthread_create(&errid, 0, write_err, rs);
155 while ((ret = waitpid(pid, &status, WNOHANG)) != pid) {
156 run_get_dynamic_sys_info(&rsd);
157 run_get_dynamic_proc_info(pid, &rpd);
158 if (rpd.rss > rr->max_rss) rr->max_rss = rpd.rss;
159 if (rpd.vsize > rr->max_vsize) rr->max_vsize = rpd.vsize;
160 sum_rss += rpd.rss * (rpd.utime + rpd.stime - last_time);
161 sum_vsize += rpd.vsize * (rpd.utime + rpd.stime - last_time);
162 last_time = rpd.utime + rpd.stime;
163 if (rs->memoryout && rpd.rss > rs->memoryout) {
164 kill(pid, SIGKILL);
165 fprintf(rs->log_fp, "++ memory out!\n");
166 goto retstat;
168 if (rs->timeout && rpd.utime + rpd.stime > rs->timeout) {
169 kill(pid, SIGKILL);
170 fprintf(rs->log_fp, "++ time out!\n");
171 goto retstat;
173 if ((double)rsd.mem_available/rss.mem_total < rs->mem_limit
174 && (double)rpd.rss/rss.mem_total > rs->mem_limit)
176 kill(pid, SIGKILL);
177 fprintf(rs->log_fp, "++ memory insufficient: %lu/%lu vs %lu\n", rsd.mem_free, rsd.mem_available, rss.mem_total);
178 goto retstat;
180 if ((int)((rpd.utime + rpd.stime) / rs->check_interval + 0.5) == check_time) {
181 if ((rpd.utime + rpd.stime)/(rsd.wall_clock - start_time) > rs->cpu_limit) {
182 double stop_time = rpd.utime + rpd.stime - (rsd.wall_clock - start_time) * rs->cpu_limit;
183 if (stop_time > 0.0) {
184 kill(pid, SIGSTOP);
185 if (stop_time >= 1.0) {
186 sleep((int)stop_time);
187 stop_time -= (int)stop_time;
189 usleep((int)(stop_time*1e6));
190 kill(pid, SIGCONT);
193 ++check_time;
195 usleep(rs->interval);
197 if (ret < 0) rr->error |= 1;
198 is_killed = 0;
199 retstat:
200 if (is_killed) rr->error |= 2;
201 times(&time_used);
202 run_get_dynamic_sys_info(&rsd);
203 rr->retval = WEXITSTATUS(status);
204 rr->rtime = rsd.wall_clock - start_time;
205 rr->utime = (is_killed)? rpd.utime : time_used.tms_cutime / 100.0;
206 rr->stime = (is_killed)? rpd.stime : time_used.tms_cstime / 100.0;
207 rr->avg_rss = sum_rss / (rpd.utime + rpd.stime);
208 rr->avg_vsize = sum_vsize / (rpd.utime + rpd.stime);
209 if (rs->prefix) {
210 pthread_join(outid, 0);
211 pthread_join(errid, 0);
213 return rr;
214 } else {
215 char **true_argv = (char**)malloc(sizeof(char*) * (argc + 1));
216 int i;
217 for (i = 0; i < argc; ++i) true_argv[i] = argv[i];
218 true_argv[i] = 0;
219 usleep(1000);
220 if (rs->prefix) {
221 dup2(rs->fd_out[1], STDOUT_FILENO);
222 close(rs->fd_out[0]); close(rs->fd_out[1]);
223 dup2(rs->fd_err[1], STDERR_FILENO);
224 close(rs->fd_err[0]); close(rs->fd_err[1]);
226 if (execvp(true_argv[0], true_argv) == -1) {
227 fprintf(stderr, "** fail to lauch the program '%s'!\n", true_argv[0]);
228 free(true_argv);
229 exit(-1);
232 return 0;
235 static void show_sys_info(FILE *fpout)
237 RunSysStatic rss;
238 RunSysDyn rsd;
239 run_get_static_sys_info(&rss);
240 run_get_dynamic_sys_info(&rsd);
241 fprintf(fpout, "\n");
242 fprintf(fpout, "-- totalmem %16.3f kB\n", rss.mem_total / 1024.0);
243 fprintf(fpout, "-- available %16.3f kB\n", rsd.mem_available / 1024.0);
244 fprintf(fpout, "-- free %16.3f kB\n", rsd.mem_free / 1024.0);
245 /* fprintf(fpout, "-- page size %16.3f kB\n", rss.page_size / 1024.0); */
246 /* fprintf(fpout, "-- virtual %16.3f kB\n", rss.swap_total / 1024.0); */
249 static int usage()
251 show_sys_info(stderr);
252 fprintf(stderr, "\n");
253 fprintf(stderr, "Program: runit (a program launcher)\n");
254 fprintf(stderr, "Usage: runit [run_options] command [command_options]\n\n");
255 fprintf(stderr, "Options: -c FLOAT average CPU usage [1.0]\n");
256 fprintf(stderr, " -m FLOAT lowest system memory [0.05]\n");
257 fprintf(stderr, " -T INT timeout in second [0]\n");
258 fprintf(stderr, " -M INT memoryout in kB [0]\n");
259 fprintf(stderr, " -p STR log output and send to the background [null]\n\n");
260 return 1;
263 int main_core(int argc, char *argv[], int offset, RunStruct *rs)
265 int error, i;
266 RunResult *rr;
267 run_upd_RunStruct(rs);
268 rr = runit(argc-offset, argv+offset, rs);
269 if (rr) {
270 fprintf(rs->log_fp, "-- CMD:");
271 for (i = 0; i != argc; ++i)
272 fprintf(rs->log_fp, " %s", argv[i]);
273 fprintf(rs->log_fp, "\n");
274 show_sys_info(rs->log_fp);
275 fprintf(rs->log_fp, "\n");
276 fprintf(rs->log_fp, "-- retval %16d\n", rr->retval);
277 fprintf(rs->log_fp, "-- real %16.3f sec\n", rr->rtime);
278 fprintf(rs->log_fp, "-- user %16.3f sec\n", rr->utime);
279 fprintf(rs->log_fp, "-- sys %16.3f sec\n", rr->stime);
280 fprintf(rs->log_fp, "-- maxrss %16.3f kB\n", rr->max_rss / 1024.0);
281 fprintf(rs->log_fp, "-- avgrss %16.3f kB\n", rr->avg_rss / 1024.0);
282 fprintf(rs->log_fp, "-- maxvsize %16.3f kB\n", rr->max_vsize / 1024.0);
283 fprintf(rs->log_fp, "-- avgvsize %16.3f kB\n", rr->avg_vsize / 1024.0);
284 fprintf(rs->log_fp, "\n");
286 error = rr->error;
287 free(rr);
288 run_free_RunStruct(rs);
289 return error;
292 int main(int argc, char *argv[])
294 int offset;
295 RunStruct *rs;
296 if (argc == 1) return usage();
297 rs = run_new_RunStruct();
298 offset = run_fill_RunStruct(argc, argv, rs);
299 if (rs->prefix) {
300 if (fork() == 0) {
301 if (setsid() == -1) perror("Could mot become a session leader");
302 return main_core(argc, argv, offset, rs);
304 return 0;
306 return main_core(argc, argv, offset, rs);
309 /*********************** standard getopt() ***********************************/
313 * getopt.c --
315 * Standard UNIX getopt function. Code is from BSD.
317 * Copyright (c) 1987-2002 The Regents of the University of California.
318 * All rights reserved.
320 * Redistribution and use in source and binary forms, with or without
321 * modification, are permitted provided that the following conditions are met:
323 * A. Redistributions of source code must retain the above copyright notice,
324 * this list of conditions and the following disclaimer.
325 * B. Redistributions in binary form must reproduce the above copyright notice,
326 * this list of conditions and the following disclaimer in the documentation
327 * and/or other materials provided with the distribution.
328 * C. Neither the names of the copyright holders nor the names of its
329 * contributors may be used to endorse or promote products derived from this
330 * software without specific prior written permission.
332 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
333 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
334 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
335 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
336 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
337 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
338 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
339 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
340 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
341 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
342 * POSSIBILITY OF SUCH DAMAGE.
345 /* #if !defined(lint)
346 * static char sccsid[] = "@(#)getopt.c 8.2 (Berkeley) 4/2/94";
347 * #endif
350 int opterr = 1, /* if error message should be printed */
351 optind = 1, /* index into parent argv vector */
352 optopt, /* character checked for validity */
353 optreset; /* reset getopt */
354 char *optarg; /* argument associated with option */
356 #define BADCH (int)'?'
357 #define BADARG (int)':'
358 #define EMSG ""
361 * getopt --
362 * Parse argc/argv argument vector.
364 int getopt_standard(int nargc, char * const *nargv, const char *ostr)
366 static char *place = EMSG; /* option letter processing */
367 char *oli; /* option letter list index */
369 if (optreset || !*place) { /* update scanning pointer */
370 optreset = 0;
371 if (optind >= nargc || *(place = nargv[optind]) != '-') {
372 place = EMSG;
373 return (EOF);
375 if (place[1] && *++place == '-') { /* found "--" */
376 ++optind;
377 place = EMSG;
378 return (EOF);
380 } /* option letter okay? */
381 if ((optopt = (int)*place++) == (int)':' ||
382 !(oli = strchr(ostr, optopt))) {
384 * if the user didn't specify '-' as an option,
385 * assume it means EOF.
387 if (optopt == (int)'-')
388 return (EOF);
389 if (!*place)
390 ++optind;
391 if (opterr && *ostr != ':')
392 (void)fprintf(stderr,
393 "%s: illegal option -- %c\n", __FILE__, optopt);
394 return (BADCH);
396 if (*++oli != ':') { /* don't need argument */
397 optarg = NULL;
398 if (!*place)
399 ++optind;
401 else { /* need an argument */
402 if (*place) /* no white space */
403 optarg = place;
404 else if (nargc <= ++optind) { /* no arg */
405 place = EMSG;
406 if (*ostr == ':')
407 return (BADARG);
408 if (opterr)
409 (void)fprintf(stderr,
410 "%s: option requires an argument -- %c\n",
411 __FILE__, optopt);
412 return (BADCH);
414 else /* white space */
415 optarg = nargv[optind];
416 place = EMSG;
417 ++optind;
419 return (optopt); /* dump back option letter */