/[baselayout]/trunk/sbin/start-stop-daemon.c
Gentoo

Contents of /trunk/sbin/start-stop-daemon.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 310 - (hide annotations) (download) (as text)
Sun Jan 26 08:27:42 2003 UTC (16 years, 4 months ago) by azarah
File MIME type: text/x-csrc
File size: 24483 byte(s)
fix nice in start-stop-daemon.c

1 woodchip 77 /*
2     * A rewrite of the original Debian's start-stop-daemon Perl script
3     * in C (faster - it is executed many times during system startup).
4     *
5     * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
6     * public domain. Based conceptually on start-stop-daemon.pl, by Ian
7     * Jackson <ijackson@gnu.ai.mit.edu>. May be used and distributed
8     * freely for any purpose. Changes by Christian Schwarz
9     * <schwarz@monet.m.isar.de>, to make output conform to the Debian
10     * Console Message Standard, also placed in public domain. Minor
11     * changes by Klee Dienes <klee@debian.org>, also placed in the Public
12     * Domain.
13     *
14     * Changes by Ben Collins <bcollins@debian.org>, added --chuid, --background
15     * and --make-pidfile options, placed in public domain aswell.
16     *
17     * Port to OpenBSD by Sontri Tomo Huynh <huynh.29@osu.edu>
18     * and Andreas Schuldei <andreas@schuldei.org>
19     *
20     * Changes by Ian Jackson: added --retry (and associated rearrangements).
21     *
22     * Modified for Gentoo rc-scripts by Donny Davies <woodchip@gentoo.org>:
23     * I removed the BSD/Hurd/OtherOS stuff, added #include <stddef.h>
24     * and stuck in a #define VERSION "1.9.18". Now it compiles without
25     * the whole automake/config.h dance.
26     */
27    
28     #include <stddef.h>
29     #define VERSION "1.9.18"
30    
31     #define MIN_POLL_INTERVAL 20000 /*us*/
32    
33     #include <errno.h>
34     #include <stdio.h>
35     #include <stdlib.h>
36     #include <string.h>
37     #include <stdarg.h>
38     #include <signal.h>
39     #include <sys/stat.h>
40     #include <dirent.h>
41     #include <sys/time.h>
42     #include <unistd.h>
43     #include <getopt.h>
44     #include <pwd.h>
45     #include <grp.h>
46     #include <sys/ioctl.h>
47     #include <sys/types.h>
48     #include <sys/termios.h>
49     #include <fcntl.h>
50     #include <limits.h>
51     #include <assert.h>
52     #include <ctype.h>
53     #include <error.h>
54    
55     static int testmode = 0;
56     static int quietmode = 0;
57     static int exitnodo = 1;
58     static int start = 0;
59     static int stop = 0;
60     static int background = 0;
61     static int mpidfile = 0;
62     static int signal_nr = 15;
63     static const char *signal_str = NULL;
64     static int user_id = -1;
65     static int runas_uid = -1;
66     static int runas_gid = -1;
67     static const char *userspec = NULL;
68     static char *changeuser = NULL;
69     static const char *changegroup = NULL;
70     static char *changeroot = NULL;
71     static const char *cmdname = NULL;
72     static char *execname = NULL;
73     static char *startas = NULL;
74     static const char *pidfile = NULL;
75     static char what_stop[1024];
76     static const char *schedule_str = NULL;
77     static const char *progname = "";
78     static int nicelevel = 0;
79    
80     static struct stat exec_stat;
81    
82     struct pid_list {
83     struct pid_list *next;
84     pid_t pid;
85     };
86    
87     static struct pid_list *found = NULL;
88     static struct pid_list *killed = NULL;
89    
90     struct schedule_item {
91     enum { sched_timeout, sched_signal, sched_goto, sched_forever } type;
92     int value; /* seconds, signal no., or index into array */
93     /* sched_forever is only seen within parse_schedule and callees */
94     };
95    
96     static int schedule_length;
97     static struct schedule_item *schedule = NULL;
98    
99     static void *xmalloc(int size);
100     static void push(struct pid_list **list, pid_t pid);
101     static void do_help(void);
102     static void parse_options(int argc, char * const *argv);
103     static int pid_is_user(pid_t pid, uid_t uid);
104     static int pid_is_cmd(pid_t pid, const char *name);
105     static void check(pid_t pid);
106     static void do_pidfile(const char *name);
107     static void do_stop(int signal_nr, int quietmode,
108     int *n_killed, int *n_notkilled, int retry_nr);
109     static int pid_is_exec(pid_t pid, const struct stat *esb);
110    
111     #ifdef __GNUC__
112     static void fatal(const char *format, ...)
113     __attribute__((noreturn, format(printf, 1, 2)));
114     static void badusage(const char *msg)
115     __attribute__((noreturn));
116     #else
117     static void fatal(const char *format, ...);
118     static void badusage(const char *msg);
119     #endif
120    
121     /* This next part serves only to construct the TVCALC macro, which
122     * is used for doing arithmetic on struct timeval's. It works like this:
123     * TVCALC(result, expression);
124     * where result is a struct timeval (and must be an lvalue) and
125     * expression is the single expression for both components. In this
126     * expression you can use the special values TVELEM, which when fed a
127     * const struct timeval* gives you the relevant component, and
128     * TVADJUST. TVADJUST is necessary when subtracting timevals, to make
129     * it easier to renormalise. Whenver you subtract timeval elements,
130     * you must make sure that TVADJUST is added to the result of the
131     * subtraction (before any resulting multiplication or what have you).
132     * TVELEM must be linear in TVADJUST.
133     */
134     typedef long tvselector(const struct timeval*);
135     static long tvselector_sec(const struct timeval *tv) { return tv->tv_sec; }
136     static long tvselector_usec(const struct timeval *tv) { return tv->tv_usec; }
137     #define TVCALC_ELEM(result, expr, sec, adj) \
138     { \
139     const long TVADJUST = adj; \
140     long (*const TVELEM)(const struct timeval*) = tvselector_##sec; \
141     (result).tv_##sec = (expr); \
142     }
143     #define TVCALC(result,expr) \
144     do { \
145     TVCALC_ELEM(result, expr, sec, (-1)); \
146     TVCALC_ELEM(result, expr, usec, (+1000000)); \
147     (result).tv_sec += (result).tv_usec / 1000000; \
148     (result).tv_usec %= 1000000; \
149     } while(0)
150    
151    
152     static void
153     fatal(const char *format, ...)
154     {
155     va_list arglist;
156    
157     fprintf(stderr, "%s: ", progname);
158     va_start(arglist, format);
159     vfprintf(stderr, format, arglist);
160     va_end(arglist);
161     putc('\n', stderr);
162     exit(2);
163     }
164    
165    
166     static void *
167     xmalloc(int size)
168     {
169     void *ptr;
170    
171     ptr = malloc(size);
172     if (ptr)
173     return ptr;
174     fatal("malloc(%d) failed", size);
175     }
176    
177    
178     static void
179     xgettimeofday(struct timeval *tv)
180     {
181     if (gettimeofday(tv,0) != 0)
182     fatal("gettimeofday failed: %s", strerror(errno));
183     }
184    
185    
186     static void
187     push(struct pid_list **list, pid_t pid)
188     {
189     struct pid_list *p;
190    
191     p = xmalloc(sizeof(*p));
192     p->next = *list;
193     p->pid = pid;
194     *list = p;
195     }
196    
197     static void
198     clear(struct pid_list **list)
199     {
200     struct pid_list *here, *next;
201    
202     for (here = *list; here != NULL; here = next) {
203     next = here->next;
204     free(here);
205     }
206    
207     *list = NULL;
208     }
209    
210     static void
211     do_help(void)
212     {
213     printf(
214     "start-stop-daemon " VERSION " for Debian - small and fast C version written by\n"
215     "Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>, public domain.\n"
216     "\n"
217     "Usage:\n"
218     " start-stop-daemon -S|--start options ... -- arguments ...\n"
219     " start-stop-daemon -K|--stop options ...\n"
220     " start-stop-daemon -H|--help\n"
221     " start-stop-daemon -V|--version\n"
222     "\n"
223     "Options (at least one of --exec|--pidfile|--user is required):\n"
224     " -x|--exec <executable> program to start/check if it is running\n"
225     " -p|--pidfile <pid-file> pid file to check\n"
226     " -c|--chuid <name|uid[:group|gid]>\n"
227     " change to this user/group before starting process\n"
228     " -u|--user <username>|<uid> stop processes owned by this user\n"
229     " -n|--name <process-name> stop processes with this name\n"
230     " -s|--signal <signal> signal to send (default TERM)\n"
231     " -a|--startas <pathname> program to start (default is <executable>)\n"
232     " -N|--nicelevel <incr> add incr to the process's nice level\n"
233     " -b|--background force the process to detach\n"
234     " -m|--make-pidfile create the pidfile before starting\n"
235     " -R|--retry <schedule> check whether processes die, and retry\n"
236     " -t|--test test mode, don't do anything\n"
237     " -o|--oknodo exit status 0 (not 1) if nothing done\n"
238     " -q|--quiet be more quiet\n"
239     " -v|--verbose be more verbose\n"
240     "Retry <schedule> is <item>|/<item>/... where <item> is one of\n"
241     " -<signal-num>|[-]<signal-name> send that signal\n"
242     " <timeout> wait that many seconds\n"
243     " forever repeat remainder forever\n"
244     "or <schedule> may be just <timeout>, meaning <signal>/<timeout>/KILL/<timeout>\n"
245     "\n"
246     "Exit status: 0 = done 1 = nothing done (=> 0 if --oknodo)\n"
247     " 3 = trouble 2 = with --retry, processes wouldn't die\n");
248     }
249    
250    
251     static void
252     badusage(const char *msg)
253     {
254     if (msg)
255     fprintf(stderr, "%s: %s\n", progname, msg);
256     fprintf(stderr, "Try `%s --help' for more information.\n", progname);
257     exit(3);
258     }
259    
260     struct sigpair {
261     const char *name;
262     int signal;
263     };
264    
265     const struct sigpair siglist[] = {
266     { "ABRT", SIGABRT },
267     { "ALRM", SIGALRM },
268     { "FPE", SIGFPE },
269     { "HUP", SIGHUP },
270     { "ILL", SIGILL },
271     { "INT", SIGINT },
272     { "KILL", SIGKILL },
273     { "PIPE", SIGPIPE },
274     { "QUIT", SIGQUIT },
275     { "SEGV", SIGSEGV },
276     { "TERM", SIGTERM },
277     { "USR1", SIGUSR1 },
278     { "USR2", SIGUSR2 },
279     { "CHLD", SIGCHLD },
280     { "CONT", SIGCONT },
281     { "STOP", SIGSTOP },
282     { "TSTP", SIGTSTP },
283     { "TTIN", SIGTTIN },
284     { "TTOU", SIGTTOU }
285     };
286    
287     static int parse_integer (const char *string, int *value_r) {
288     unsigned long ul;
289     char *ep;
290    
291     if (!string[0])
292     return -1;
293    
294     ul= strtoul(string,&ep,10);
295     if (ul > INT_MAX || *ep != '\0')
296     return -1;
297    
298     *value_r= ul;
299     return 0;
300     }
301    
302     static int parse_signal (const char *signal_str, int *signal_nr)
303     {
304     unsigned int i;
305    
306     if (parse_integer(signal_str, signal_nr) == 0)
307     return 0;
308    
309     for (i = 0; i < sizeof (siglist) / sizeof (siglist[0]); i++) {
310     if (strcmp (signal_str, siglist[i].name) == 0) {
311     *signal_nr = siglist[i].signal;
312     return 0;
313     }
314     }
315     return -1;
316     }
317    
318     static void
319     parse_schedule_item(const char *string, struct schedule_item *item) {
320     const char *after_hyph;
321    
322     if (!strcmp(string,"forever")) {
323     item->type = sched_forever;
324     } else if (isdigit(string[0])) {
325     item->type = sched_timeout;
326     if (parse_integer(string, &item->value) != 0)
327     badusage("invalid timeout value in schedule");
328     } else if ((after_hyph = string + (string[0] == '-')) &&
329     parse_signal(after_hyph, &item->value) == 0) {
330     item->type = sched_signal;
331     } else {
332     badusage("invalid schedule item (must be [-]<signal-name>, "
333     "-<signal-number>, <timeout> or `forever'");
334     }
335     }
336    
337     static void
338     parse_schedule(const char *schedule_str) {
339     char item_buf[20];
340     const char *slash;
341     int count, repeatat;
342     ptrdiff_t str_len;
343    
344     count = 0;
345     for (slash = schedule_str; *slash; slash++)
346     if (*slash == '/')
347     count++;
348    
349     schedule_length = (count == 0) ? 4 : count+1;
350     schedule = xmalloc(sizeof(*schedule) * schedule_length);
351    
352     if (count == 0) {
353     schedule[0].type = sched_signal;
354     schedule[0].value = signal_nr;
355     parse_schedule_item(schedule_str, &schedule[1]);
356     if (schedule[1].type != sched_timeout) {
357     badusage ("--retry takes timeout, or schedule list"
358     " of at least two items");
359     }
360     schedule[2].type = sched_signal;
361     schedule[2].value = SIGKILL;
362     schedule[3]= schedule[1];
363     } else {
364     count = 0;
365     repeatat = -1;
366     while (schedule_str != NULL) {
367     slash = strchr(schedule_str,'/');
368     str_len = slash ? slash - schedule_str : strlen(schedule_str);
369     if (str_len >= (ptrdiff_t)sizeof(item_buf))
370     badusage("invalid schedule item: far too long"
371     " (you must delimit items with slashes)");
372     memcpy(item_buf, schedule_str, str_len);
373     item_buf[str_len] = 0;
374     schedule_str = slash ? slash+1 : NULL;
375    
376     parse_schedule_item(item_buf, &schedule[count]);
377     if (schedule[count].type == sched_forever) {
378     if (repeatat >= 0)
379     badusage("invalid schedule: `forever'"
380     " appears more than once");
381     repeatat = count;
382     continue;
383     }
384     count++;
385     }
386     if (repeatat >= 0) {
387     schedule[count].type = sched_goto;
388     schedule[count].value = repeatat;
389     count++;
390     }
391     assert(count == schedule_length);
392     }
393     }
394    
395     static void
396     parse_options(int argc, char * const *argv)
397     {
398     static struct option longopts[] = {
399     { "help", 0, NULL, 'H'},
400     { "stop", 0, NULL, 'K'},
401     { "start", 0, NULL, 'S'},
402     { "version", 0, NULL, 'V'},
403     { "startas", 1, NULL, 'a'},
404     { "name", 1, NULL, 'n'},
405     { "oknodo", 0, NULL, 'o'},
406     { "pidfile", 1, NULL, 'p'},
407     { "quiet", 0, NULL, 'q'},
408     { "signal", 1, NULL, 's'},
409     { "test", 0, NULL, 't'},
410     { "user", 1, NULL, 'u'},
411     { "chroot", 1, NULL, 'r'},
412     { "verbose", 0, NULL, 'v'},
413     { "exec", 1, NULL, 'x'},
414     { "chuid", 1, NULL, 'c'},
415     { "nicelevel", 1, NULL, 'N'},
416     { "background", 0, NULL, 'b'},
417     { "make-pidfile", 0, NULL, 'm'},
418     { "retry", 1, NULL, 'R'},
419     { NULL, 0, NULL, 0}
420     };
421     int c;
422    
423     for (;;) {
424     c = getopt_long(argc, argv, "HKSVa:n:op:qr:s:tu:vx:c:N:bmR:",
425     longopts, (int *) 0);
426     if (c == -1)
427     break;
428     switch (c) {
429     case 'H': /* --help */
430     do_help();
431     exit(0);
432     case 'K': /* --stop */
433     stop = 1;
434     break;
435     case 'S': /* --start */
436     start = 1;
437     break;
438     case 'V': /* --version */
439     printf("start-stop-daemon " VERSION "\n");
440     exit(0);
441     case 'a': /* --startas <pathname> */
442     startas = optarg;
443     break;
444     case 'n': /* --name <process-name> */
445     cmdname = optarg;
446     break;
447     case 'o': /* --oknodo */
448     exitnodo = 0;
449     break;
450     case 'p': /* --pidfile <pid-file> */
451     pidfile = optarg;
452     break;
453     case 'q': /* --quiet */
454     quietmode = 1;
455     break;
456     case 's': /* --signal <signal> */
457     signal_str = optarg;
458     break;
459     case 't': /* --test */
460     testmode = 1;
461     break;
462     case 'u': /* --user <username>|<uid> */
463     userspec = optarg;
464     break;
465     case 'v': /* --verbose */
466     quietmode = -1;
467     break;
468     case 'x': /* --exec <executable> */
469     execname = optarg;
470     break;
471     case 'c': /* --chuid <username>|<uid> */
472     /* we copy the string just in case we need the
473     * argument later. */
474     changeuser = strdup(optarg);
475     changeuser = strtok(changeuser, ":");
476     changegroup = strtok(NULL, ":");
477     break;
478     case 'r': /* --chroot /new/root */
479     changeroot = optarg;
480     break;
481     case 'N': /* --nice */
482     nicelevel = atoi(optarg);
483     break;
484     case 'b': /* --background */
485     background = 1;
486     break;
487     case 'm': /* --make-pidfile */
488     mpidfile = 1;
489     break;
490     case 'R': /* --retry <schedule>|<timeout> */
491     schedule_str = optarg;
492     break;
493     default:
494     badusage(NULL); /* message printed by getopt */
495     }
496     }
497    
498     if (signal_str != NULL) {
499     if (parse_signal (signal_str, &signal_nr) != 0)
500     badusage("signal value must be numeric or name"
501     " of signal (KILL, INTR, ...)");
502     }
503    
504     if (schedule_str != NULL) {
505     parse_schedule(schedule_str);
506     }
507    
508     if (start == stop)
509     badusage("need one of --start or --stop");
510    
511     if (!execname && !pidfile && !userspec && !cmdname)
512     badusage("need at least one of --exec, --pidfile, --user or --name");
513    
514     if (!startas)
515     startas = execname;
516    
517     if (start && !startas)
518     badusage("--start needs --exec or --startas");
519    
520     if (mpidfile && pidfile == NULL)
521     badusage("--make-pidfile is only relevant with --pidfile");
522    
523     if (background && !start)
524     badusage("--background is only relevant with --start");
525    
526     }
527    
528     static int
529     pid_is_exec(pid_t pid, const struct stat *esb)
530     {
531     struct stat sb;
532     char buf[32];
533    
534     sprintf(buf, "/proc/%d/exe", pid);
535     if (stat(buf, &sb) != 0)
536     return 0;
537     return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino);
538     }
539    
540    
541     static int
542     pid_is_user(pid_t pid, uid_t uid)
543     {
544     struct stat sb;
545     char buf[32];
546    
547     sprintf(buf, "/proc/%d", pid);
548     if (stat(buf, &sb) != 0)
549     return 0;
550     return (sb.st_uid == uid);
551     }
552    
553    
554     static int
555     pid_is_cmd(pid_t pid, const char *name)
556     {
557     char buf[32];
558     FILE *f;
559     int c;
560    
561     sprintf(buf, "/proc/%d/stat", pid);
562     f = fopen(buf, "r");
563     if (!f)
564     return 0;
565     while ((c = getc(f)) != EOF && c != '(')
566     ;
567     if (c != '(') {
568     fclose(f);
569     return 0;
570     }
571     /* this hopefully handles command names containing ')' */
572     while ((c = getc(f)) != EOF && c == *name)
573     name++;
574     fclose(f);
575     return (c == ')' && *name == '\0');
576     }
577    
578    
579     static void
580     check(pid_t pid)
581     {
582     if (execname && !pid_is_exec(pid, &exec_stat))
583     return;
584     if (userspec && !pid_is_user(pid, user_id))
585     return;
586     if (cmdname && !pid_is_cmd(pid, cmdname))
587     return;
588     push(&found, pid);
589     }
590    
591     static void
592     do_pidfile(const char *name)
593     {
594     FILE *f;
595     pid_t pid;
596    
597     f = fopen(name, "r");
598     if (f) {
599     if (fscanf(f, "%d", &pid) == 1)
600     check(pid);
601     fclose(f);
602     } else if (errno != ENOENT)
603     fatal("open pidfile %s: %s", name, strerror(errno));
604    
605     }
606    
607     /* WTA: this needs to be an autoconf check for /proc/pid existance.
608     */
609     static void
610     do_procinit(void)
611     {
612     DIR *procdir;
613     struct dirent *entry;
614     int foundany;
615     pid_t pid;
616    
617     procdir = opendir("/proc");
618     if (!procdir)
619     fatal("opendir /proc: %s", strerror(errno));
620    
621     foundany = 0;
622     while ((entry = readdir(procdir)) != NULL) {
623     if (sscanf(entry->d_name, "%d", &pid) != 1)
624     continue;
625     foundany++;
626     check(pid);
627     }
628     closedir(procdir);
629     if (!foundany)
630     fatal("nothing in /proc - not mounted?");
631     }
632    
633     static void
634     do_findprocs(void)
635     {
636     clear(&found);
637    
638     if (pidfile)
639     do_pidfile(pidfile);
640     else
641     do_procinit();
642     }
643    
644     /* return 1 on failure */
645     static void
646     do_stop(int signal_nr, int quietmode, int *n_killed, int *n_notkilled, int retry_nr)
647     {
648     struct pid_list *p;
649    
650     do_findprocs();
651    
652     *n_killed = 0;
653     *n_notkilled = 0;
654    
655     if (!found)
656     return;
657    
658     clear(&killed);
659    
660     for (p = found; p; p = p->next) {
661     if (testmode)
662     printf("Would send signal %d to %d.\n",
663     signal_nr, p->pid);
664     else if (kill(p->pid, signal_nr) == 0) {
665     push(&killed, p->pid);
666     (*n_killed)++;
667     } else {
668     printf("%s: warning: failed to kill %d: %s\n",
669     progname, p->pid, strerror(errno));
670     (*n_notkilled)++;
671     }
672     }
673     if (quietmode < 0 && killed) {
674     printf("Stopped %s (pid", what_stop);
675     for (p = killed; p; p = p->next)
676     printf(" %d", p->pid);
677     putchar(')');
678     if (retry_nr > 0)
679     printf(", retry #%d", retry_nr);
680     printf(".\n");
681     }
682     }
683    
684    
685     static void
686     set_what_stop(const char *str)
687     {
688     strncpy(what_stop, str, sizeof(what_stop));
689     what_stop[sizeof(what_stop)-1] = '\0';
690     }
691    
692     static int
693     run_stop_schedule(void)
694     {
695     int r, position, n_killed, n_notkilled, value, ratio, anykilled, retry_nr;
696     struct timeval stopat, before, after, interval, maxinterval;
697    
698     if (testmode) {
699     if (schedule != NULL) {
700     printf("Ignoring --retry in test mode\n");
701     schedule = NULL;
702     }
703     }
704    
705     if (cmdname)
706     set_what_stop(cmdname);
707     else if (execname)
708     set_what_stop(execname);
709     else if (pidfile)
710     sprintf(what_stop, "process in pidfile `%.200s'", pidfile);
711     else if (userspec)
712     sprintf(what_stop, "process(es) owned by `%.200s'", userspec);
713     else
714     fatal("internal error, please report");
715    
716     anykilled = 0;
717     retry_nr = 0;
718    
719     if (schedule == NULL) {
720     do_stop(signal_nr, quietmode, &n_killed, &n_notkilled, 0);
721     if (n_notkilled > 0 && quietmode <= 0)
722     printf("%d pids were not killed\n", n_notkilled);
723     if (n_killed)
724     anykilled = 1;
725     goto x_finished;
726     }
727    
728     for (position = 0; position < schedule_length; ) {
729     value= schedule[position].value;
730     n_notkilled = 0;
731    
732     switch (schedule[position].type) {
733    
734     case sched_goto:
735     position = value;
736     continue;
737    
738     case sched_signal:
739     do_stop(value, quietmode, &n_killed, &n_notkilled, retry_nr++);
740     if (!n_killed)
741     goto x_finished;
742     else
743     anykilled = 1;
744     goto next_item;
745    
746     case sched_timeout:
747     /* We want to keep polling for the processes, to see if they've exited,
748     * or until the timeout expires.
749     *
750     * This is a somewhat complicated algorithm to try to ensure that we
751     * notice reasonably quickly when all the processes have exited, but
752     * don't spend too much CPU time polling. In particular, on a fast
753     * machine with quick-exiting daemons we don't want to delay system
754     * shutdown too much, whereas on a slow one, or where processes are
755     * taking some time to exit, we want to increase the polling
756     * interval.
757     *
758     * The algorithm is as follows: we measure the elapsed time it takes
759     * to do one poll(), and wait a multiple of this time for the next
760     * poll. However, if that would put us past the end of the timeout
761     * period we wait only as long as the timeout period, but in any case
762     * we always wait at least MIN_POLL_INTERVAL (20ms). The multiple
763     * (`ratio') starts out as 2, and increases by 1 for each poll to a
764     * maximum of 10; so we use up to between 30% and 10% of the
765     * machine's resources (assuming a few reasonable things about system
766     * performance).
767     */
768     xgettimeofday(&stopat);
769     stopat.tv_sec += value;
770     ratio = 1;
771     for (;;) {
772     xgettimeofday(&before);
773     if (timercmp(&before,&stopat,>))
774     goto next_item;
775    
776     do_stop(0, 1, &n_killed, &n_notkilled, 0);
777     if (!n_killed)
778     goto x_finished;
779    
780     xgettimeofday(&after);
781    
782     if (!timercmp(&after,&stopat,<))
783     goto next_item;
784    
785     if (ratio < 10)
786     ratio++;
787    
788     TVCALC(interval, ratio * (TVELEM(&after) - TVELEM(&before) + TVADJUST));
789     TVCALC(maxinterval, TVELEM(&stopat) - TVELEM(&after) + TVADJUST);
790    
791     if (timercmp(&interval,&maxinterval,>))
792     interval = maxinterval;
793    
794     if (interval.tv_sec == 0 &&
795     interval.tv_usec <= MIN_POLL_INTERVAL)
796     interval.tv_usec = MIN_POLL_INTERVAL;
797    
798     r = select(0,0,0,0,&interval);
799     if (r < 0 && errno != EINTR)
800     fatal("select() failed for pause: %s",
801     strerror(errno));
802     }
803    
804     default:
805     assert(!"schedule[].type value must be valid");
806    
807     }
808    
809     next_item:
810     position++;
811     }
812    
813     if (quietmode <= 0)
814     printf("Program %s, %d process(es), refused to die.\n",
815     what_stop, n_killed);
816    
817     return 2;
818    
819     x_finished:
820     if (!anykilled) {
821     if (quietmode <= 0)
822     printf("No %s found running; none killed.\n", what_stop);
823     return exitnodo;
824     } else {
825     return 0;
826     }
827     }
828    
829     /*
830     int main(int argc, char **argv) NONRETURNING;
831     */
832    
833     int
834     main(int argc, char **argv)
835     {
836     progname = argv[0];
837    
838     parse_options(argc, argv);
839     argc -= optind;
840     argv += optind;
841    
842     if (execname && stat(execname, &exec_stat))
843     fatal("stat %s: %s", execname, strerror(errno));
844    
845     if (userspec && sscanf(userspec, "%d", &user_id) != 1) {
846     struct passwd *pw;
847    
848     pw = getpwnam(userspec);
849     if (!pw)
850     fatal("user `%s' not found\n", userspec);
851    
852     user_id = pw->pw_uid;
853     }
854    
855     if (changegroup && sscanf(changegroup, "%d", &runas_gid) != 1) {
856     struct group *gr = getgrnam(changegroup);
857     if (!gr)
858     fatal("group `%s' not found\n", changegroup);
859     runas_gid = gr->gr_gid;
860     }
861     if (changeuser && sscanf(changeuser, "%d", &runas_uid) != 1) {
862     struct passwd *pw = getpwnam(changeuser);
863     if (!pw)
864     fatal("user `%s' not found\n", changeuser);
865     runas_uid = pw->pw_uid;
866     if (changegroup == NULL) { /* pass the default group of this user */
867     changegroup = ""; /* just empty */
868     runas_gid = pw->pw_gid;
869     }
870     }
871    
872     if (stop) {
873     int i = run_stop_schedule();
874     exit(i);
875     }
876    
877     do_findprocs();
878    
879     if (found) {
880     if (quietmode <= 0)
881     printf("%s already running.\n", execname);
882     exit(exitnodo);
883     }
884     if (testmode) {
885     printf("Would start %s ", startas);
886     while (argc-- > 0)
887     printf("%s ", *argv++);
888     if (changeuser != NULL) {
889     printf(" (as user %s[%d]", changeuser, runas_uid);
890     if (changegroup != NULL)
891     printf(", and group %s[%d])", changegroup, runas_gid);
892     else
893     printf(")");
894     }
895     if (changeroot != NULL)
896     printf(" in directory %s", changeroot);
897     if (nicelevel)
898     printf(", and add %i to the priority", nicelevel);
899     printf(".\n");
900     exit(0);
901     }
902     if (quietmode < 0)
903     printf("Starting %s...\n", startas);
904     *--argv = startas;
905     if (changeroot != NULL) {
906     if (chdir(changeroot) < 0)
907     fatal("Unable to chdir() to %s", changeroot);
908     if (chroot(changeroot) < 0)
909     fatal("Unable to chroot() to %s", changeroot);
910     }
911     if (changeuser != NULL) {
912     if (setgid(runas_gid))
913     fatal("Unable to set gid to %d", runas_gid);
914     if (initgroups(changeuser, runas_gid))
915     fatal("Unable to set initgroups() with gid %d", runas_gid);
916     if (setuid(runas_uid))
917     fatal("Unable to set uid to %s", changeuser);
918     }
919    
920     if (background) { /* ok, we need to detach this process */
921     int i, fd;
922     if (quietmode < 0)
923     printf("Detatching to start %s...", startas);
924     i = fork();
925     if (i<0) {
926     fatal("Unable to fork.\n");
927     }
928     if (i) { /* parent */
929     if (quietmode < 0)
930     printf("done.\n");
931     exit(0);
932     }
933     /* child continues here */
934     /* now close all extra fds */
935     for (i=getdtablesize()-1; i>=0; --i) close(i);
936     /* change tty */
937     fd = open("/dev/tty", O_RDWR);
938     ioctl(fd, TIOCNOTTY, 0);
939     close(fd);
940     chdir("/");
941     umask(022); /* set a default for dumb programs */
942     setpgid(0,0); /* set the process group */
943     fd=open("/dev/null", O_RDWR); /* stdin */
944     dup(fd); /* stdout */
945     dup(fd); /* stderr */
946     }
947     if (nicelevel) {
948 azarah 310 if (nice(nicelevel)==-1 && errno)
949 woodchip 77 fatal("Unable to alter nice level by %i: %s", nicelevel,
950     strerror(errno));
951     }
952     if (mpidfile && pidfile != NULL) { /* user wants _us_ to make the pidfile :) */
953     FILE *pidf = fopen(pidfile, "w");
954     pid_t pidt = getpid();
955     if (pidf == NULL)
956     fatal("Unable to open pidfile `%s' for writing: %s", pidfile,
957     strerror(errno));
958     fprintf(pidf, "%d\n", pidt);
959     fclose(pidf);
960     }
961     execv(startas, argv);
962     fatal("Unable to start %s: %s", startas, strerror(errno));
963     }
964    

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.20