Magellan Linux

Annotation of /tags/init-0_5_2/src/start-stop-daemon.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 851 - (hide annotations) (download)
Mon May 4 19:58:56 2009 UTC (15 years ago) by niro
File MIME type: text/plain
File size: 32243 byte(s)
tagged 'init-0_5_2'
1 niro 111 /*
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     * Updated by Aron Griffis <agriffis@gentoo.org>:
28     * Fetched updates from Debian's dpkg-1.10.20, including fix for
29     * Gentoo bug 22686 (start-stop-daemon in baselayout doesn't allow
30     * altered nicelevel).
31     */
32    
33     #define VERSION "1.10.20"
34     #include <stddef.h>
35    
36     #define NONRETURNPRINTFFORMAT(x, y) \
37     __attribute__((noreturn, format(printf, x, y)))
38     #define NONRETURNING \
39     __attribute__((noreturn))
40    
41     #if defined(linux) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__))
42     # define OSLinux
43     #elif defined(__GNU__)
44     # define OSHURD
45     #elif defined(__sparc__)
46     # define OSsunos
47     #elif defined(OPENBSD) || defined(__OpenBSD__)
48     # define OSOpenBSD
49     #elif defined(hpux)
50     # define OShpux
51     #elif defined(__FreeBSD__)
52     # define OSFreeBSD
53     #elif defined(__NetBSD__)
54     # define OSNetBSD
55     #else
56     # error Unknown architecture - cannot build start-stop-daemon
57     #endif
58    
59     #define MIN_POLL_INTERVAL 20000 /*us*/
60    
61     #if defined(OSHURD)
62     # include <hurd.h>
63     # include <ps.h>
64     #endif
65    
66     #if defined(OSOpenBSD) || defined(OSFreeBSD) || defined(OSNetBSD)
67     #include <sys/param.h>
68     #include <sys/user.h>
69     #include <sys/proc.h>
70     #include <sys/stat.h>
71     #include <sys/sysctl.h>
72     #include <sys/types.h>
73    
74     #include <err.h>
75     #include <kvm.h>
76     #include <limits.h>
77     #endif
78    
79     #if defined(OShpux)
80     #include <sys/param.h>
81     #include <sys/pstat.h>
82     #endif
83    
84     #include <errno.h>
85     #include <stdio.h>
86     #include <stdlib.h>
87     #include <string.h>
88     #include <stdarg.h>
89     #include <signal.h>
90     #include <sys/stat.h>
91     #include <dirent.h>
92     #include <sys/time.h>
93     #include <unistd.h>
94     #include <getopt.h>
95     #include <pwd.h>
96     #include <grp.h>
97     #include <sys/ioctl.h>
98     #include <sys/types.h>
99     #include <sys/termios.h>
100     #include <fcntl.h>
101     #include <limits.h>
102     #include <assert.h>
103     #include <ctype.h>
104    
105     #include "headers.h"
106    
107     #ifdef HURD_IHASH_H
108     # include <hurd/ihash.h>
109     #endif
110    
111     static int testmode = 0;
112     static int quietmode = 0;
113     static int exitnodo = 1;
114     static int start = 0;
115     static int stop = 0;
116     static int background = 0;
117     static int mpidfile = 0;
118     static int signal_nr = 15;
119     static const char *signal_str = NULL;
120     static int user_id = -1;
121     static int runas_uid = -1;
122     static int runas_gid = -1;
123     static const char *userspec = NULL;
124     static char *changeuser = NULL;
125     static const char *changegroup = NULL;
126     static char *changeroot = NULL;
127     static const char *changedir = NULL;
128     static const char *cmdname = NULL;
129     static char *execname = NULL;
130     static char *startas = NULL;
131     static const char *pidfile = NULL;
132     static char what_stop[1024];
133     static const char *schedule_str = NULL;
134     static const char *progname = "";
135     static int nicelevel = 0;
136    
137     static struct stat exec_stat;
138     #if defined(OSHURD)
139     static struct proc_stat_list *procset;
140     #endif
141    
142    
143     struct pid_list {
144     struct pid_list *next;
145     pid_t pid;
146     };
147    
148     static struct pid_list *found = NULL;
149     static struct pid_list *killed = NULL;
150    
151     struct schedule_item {
152     enum { sched_timeout, sched_signal, sched_goto, sched_forever } type;
153     int value; /* seconds, signal no., or index into array */
154     /* sched_forever is only seen within parse_schedule and callees */
155     };
156    
157     static int schedule_length;
158     static struct schedule_item *schedule = NULL;
159    
160     static void *xmalloc(int size);
161     static void push(struct pid_list **list, pid_t pid);
162     static void do_help(void);
163     static void parse_options(int argc, char * const *argv);
164     static int pid_is_user(pid_t pid, uid_t uid);
165     static int pid_is_cmd(pid_t pid, const char *name);
166     static void check(pid_t pid);
167     static void do_pidfile(const char *name);
168     static void do_stop(int signal_nr, int quietmode,
169     int *n_killed, int *n_notkilled, int retry_nr);
170     #if defined(OSLinux) || defined(OShpux)
171     static int pid_is_exec(pid_t pid, const struct stat *esb);
172     #endif
173    
174     #ifdef __GNUC__
175     static void fatal(const char *format, ...)
176     NONRETURNPRINTFFORMAT(1, 2);
177     static void badusage(const char *msg)
178     NONRETURNING;
179     #else
180     static void fatal(const char *format, ...);
181     static void badusage(const char *msg);
182     #endif
183    
184     /* This next part serves only to construct the TVCALC macro, which
185     * is used for doing arithmetic on struct timeval's. It works like this:
186     * TVCALC(result, expression);
187     * where result is a struct timeval (and must be an lvalue) and
188     * expression is the single expression for both components. In this
189     * expression you can use the special values TVELEM, which when fed a
190     * const struct timeval* gives you the relevant component, and
191     * TVADJUST. TVADJUST is necessary when subtracting timevals, to make
192     * it easier to renormalise. Whenver you subtract timeval elements,
193     * you must make sure that TVADJUST is added to the result of the
194     * subtraction (before any resulting multiplication or what have you).
195     * TVELEM must be linear in TVADJUST.
196     */
197     typedef long tvselector(const struct timeval*);
198     static long tvselector_sec(const struct timeval *tv) { return tv->tv_sec; }
199     static long tvselector_usec(const struct timeval *tv) { return tv->tv_usec; }
200     #define TVCALC_ELEM(result, expr, sec, adj) \
201     { \
202     const long TVADJUST = adj; \
203     long (*const TVELEM)(const struct timeval*) = tvselector_##sec; \
204     (result).tv_##sec = (expr); \
205     }
206     #define TVCALC(result,expr) \
207     do { \
208     TVCALC_ELEM(result, expr, sec, (-1)); \
209     TVCALC_ELEM(result, expr, usec, (+1000000)); \
210     (result).tv_sec += (result).tv_usec / 1000000; \
211     (result).tv_usec %= 1000000; \
212     } while(0)
213    
214    
215     static void
216     fatal(const char *format, ...)
217     {
218     va_list arglist;
219    
220     fprintf(stderr, "%s: ", progname);
221     va_start(arglist, format);
222     vfprintf(stderr, format, arglist);
223     va_end(arglist);
224     putc('\n', stderr);
225     exit(2);
226     }
227    
228    
229     static void *
230     xmalloc(int size)
231     {
232     void *ptr;
233    
234     ptr = malloc(size);
235     if (ptr)
236     return ptr;
237     fatal("malloc(%d) failed", size);
238     }
239    
240    
241     static void
242     xgettimeofday(struct timeval *tv)
243     {
244     if (gettimeofday(tv,0) != 0)
245     fatal("gettimeofday failed: %s", strerror(errno));
246     }
247    
248    
249     static void
250     push(struct pid_list **list, pid_t pid)
251     {
252     struct pid_list *p;
253    
254     p = xmalloc(sizeof(*p));
255     p->next = *list;
256     p->pid = pid;
257     *list = p;
258     }
259    
260     static void
261     clear(struct pid_list **list)
262     {
263     struct pid_list *here, *next;
264    
265     for (here = *list; here != NULL; here = next) {
266     next = here->next;
267     free(here);
268     }
269    
270     *list = NULL;
271     }
272    
273     static void
274     do_help(void)
275     {
276     printf(
277     "start-stop-daemon " VERSION " for Debian - small and fast C version written by\n"
278     "Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>, public domain.\n"
279     "\n"
280     "Usage:\n"
281     " start-stop-daemon -S|--start options ... -- arguments ...\n"
282     " start-stop-daemon -K|--stop options ...\n"
283     " start-stop-daemon -H|--help\n"
284     " start-stop-daemon -V|--version\n"
285     "\n"
286     "Options (at least one of --exec|--pidfile|--user is required):\n"
287     " -x|--exec <executable> program to start/check if it is running\n"
288     " -p|--pidfile <pid-file> pid file to check\n"
289     " -c|--chuid <name|uid[:group|gid]>\n"
290     " change to this user/group before starting process\n"
291     " -u|--user <username>|<uid> stop processes owned by this user\n"
292     " -g|--group <group|gid> run process as this group\n"
293     " -n|--name <process-name> stop processes with this name\n"
294     " -s|--signal <signal> signal to send (default TERM)\n"
295     " -a|--startas <pathname> program to start (default is <executable>)\n"
296     " -C|--chdir <directory> Change to <directory>(default is /)\n"
297     " -N|--nicelevel <incr> add incr to the process's nice level\n"
298     " -b|--background force the process to detach\n"
299     " -m|--make-pidfile create the pidfile before starting\n"
300     " -R|--retry <schedule> check whether processes die, and retry\n"
301     " -t|--test test mode, don't do anything\n"
302     " -o|--oknodo exit status 0 (not 1) if nothing done\n"
303     " -q|--quiet be more quiet\n"
304     " -v|--verbose be more verbose\n"
305     "Retry <schedule> is <item>|/<item>/... where <item> is one of\n"
306     " -<signal-num>|[-]<signal-name> send that signal\n"
307     " <timeout> wait that many seconds\n"
308     " forever repeat remainder forever\n"
309     "or <schedule> may be just <timeout>, meaning <signal>/<timeout>/KILL/<timeout>\n"
310     "\n"
311     "Exit status: 0 = done 1 = nothing done (=> 0 if --oknodo)\n"
312     " 3 = trouble 2 = with --retry, processes wouldn't die\n");
313     }
314    
315    
316     static void
317     badusage(const char *msg)
318     {
319     if (msg)
320     fprintf(stderr, "%s: %s\n", progname, msg);
321     fprintf(stderr, "Try `%s --help' for more information.\n", progname);
322     exit(3);
323     }
324    
325     struct sigpair {
326     const char *name;
327     int signal;
328     };
329    
330     const struct sigpair siglist[] = {
331     { "ABRT", SIGABRT },
332     { "ALRM", SIGALRM },
333     { "FPE", SIGFPE },
334     { "HUP", SIGHUP },
335     { "ILL", SIGILL },
336     { "INT", SIGINT },
337     { "KILL", SIGKILL },
338     { "PIPE", SIGPIPE },
339     { "QUIT", SIGQUIT },
340     { "SEGV", SIGSEGV },
341     { "TERM", SIGTERM },
342     { "USR1", SIGUSR1 },
343     { "USR2", SIGUSR2 },
344     { "CHLD", SIGCHLD },
345     { "CONT", SIGCONT },
346     { "STOP", SIGSTOP },
347     { "TSTP", SIGTSTP },
348     { "TTIN", SIGTTIN },
349     { "TTOU", SIGTTOU }
350     };
351    
352     static int parse_integer(const char *string, int *value_r) {
353     unsigned long ul;
354     char *ep;
355    
356     if (!string[0])
357     return -1;
358    
359     ul= strtoul(string,&ep,10);
360     if (ul > INT_MAX || *ep != '\0')
361     return -1;
362    
363     *value_r= ul;
364     return 0;
365     }
366    
367     static int parse_signal(const char *signal_str, int *signal_nr)
368     {
369     unsigned int i;
370    
371     if (parse_integer(signal_str, signal_nr) == 0)
372     return 0;
373    
374     for (i = 0; i < sizeof (siglist) / sizeof (siglist[0]); i++) {
375     if (strcmp (signal_str, siglist[i].name) == 0) {
376     *signal_nr = siglist[i].signal;
377     return 0;
378     }
379     }
380     return -1;
381     }
382    
383     static void
384     parse_schedule_item(const char *string, struct schedule_item *item) {
385     const char *after_hyph;
386    
387     if (!strcmp(string,"forever")) {
388     item->type = sched_forever;
389     } else if (isdigit(string[0])) {
390     item->type = sched_timeout;
391     if (parse_integer(string, &item->value) != 0)
392     badusage("invalid timeout value in schedule");
393     } else if ((after_hyph = string + (string[0] == '-')) &&
394     parse_signal(after_hyph, &item->value) == 0) {
395     item->type = sched_signal;
396     } else {
397     badusage("invalid schedule item (must be [-]<signal-name>, "
398     "-<signal-number>, <timeout> or `forever'");
399     }
400     }
401    
402     static void
403     parse_schedule(const char *schedule_str) {
404     char item_buf[20];
405     const char *slash;
406     int count, repeatat;
407     ptrdiff_t str_len;
408    
409     count = 0;
410     for (slash = schedule_str; *slash; slash++)
411     if (*slash == '/')
412     count++;
413    
414     schedule_length = (count == 0) ? 4 : count+1;
415     schedule = xmalloc(sizeof(*schedule) * schedule_length);
416    
417     if (count == 0) {
418     schedule[0].type = sched_signal;
419     schedule[0].value = signal_nr;
420     parse_schedule_item(schedule_str, &schedule[1]);
421     if (schedule[1].type != sched_timeout) {
422     badusage ("--retry takes timeout, or schedule list"
423     " of at least two items");
424     }
425     schedule[2].type = sched_signal;
426     schedule[2].value = SIGKILL;
427     schedule[3]= schedule[1];
428     } else {
429     count = 0;
430     repeatat = -1;
431     while (schedule_str != NULL) {
432     slash = strchr(schedule_str,'/');
433     str_len = slash ? slash - schedule_str : strlen(schedule_str);
434     if (str_len >= (ptrdiff_t)sizeof(item_buf))
435     badusage("invalid schedule item: far too long"
436     " (you must delimit items with slashes)");
437     memcpy(item_buf, schedule_str, str_len);
438     item_buf[str_len] = 0;
439     schedule_str = slash ? slash+1 : NULL;
440    
441     parse_schedule_item(item_buf, &schedule[count]);
442     if (schedule[count].type == sched_forever) {
443     if (repeatat >= 0)
444     badusage("invalid schedule: `forever'"
445     " appears more than once");
446     repeatat = count;
447     continue;
448     }
449     count++;
450     }
451     if (repeatat >= 0) {
452     schedule[count].type = sched_goto;
453     schedule[count].value = repeatat;
454     count++;
455     }
456     assert(count == schedule_length);
457     }
458     }
459    
460     static void
461     parse_options(int argc, char * const *argv)
462     {
463     static struct option longopts[] = {
464     { "help", 0, NULL, 'H'},
465     { "stop", 0, NULL, 'K'},
466     { "start", 0, NULL, 'S'},
467     { "version", 0, NULL, 'V'},
468     { "startas", 1, NULL, 'a'},
469     { "name", 1, NULL, 'n'},
470     { "oknodo", 0, NULL, 'o'},
471     { "pidfile", 1, NULL, 'p'},
472     { "quiet", 0, NULL, 'q'},
473     { "signal", 1, NULL, 's'},
474     { "test", 0, NULL, 't'},
475     { "user", 1, NULL, 'u'},
476     { "group", 1, NULL, 'g'},
477     { "chroot", 1, NULL, 'r'},
478     { "verbose", 0, NULL, 'v'},
479     { "exec", 1, NULL, 'x'},
480     { "chuid", 1, NULL, 'c'},
481     { "nicelevel", 1, NULL, 'N'},
482     { "background", 0, NULL, 'b'},
483     { "make-pidfile", 0, NULL, 'm'},
484     { "retry", 1, NULL, 'R'},
485     { "chdir", 1, NULL, 'd'},
486     { NULL, 0, NULL, 0}
487     };
488     int c;
489    
490     for (;;) {
491     c = getopt_long(argc, argv, "HKSV:a:n:op:qr:s:tu:vx:c:N:bmR:g:d:",
492     longopts, (int *) 0);
493     if (c == -1)
494     break;
495     switch (c) {
496     case 'H': /* --help */
497     do_help();
498     exit(0);
499     case 'K': /* --stop */
500     stop = 1;
501     break;
502     case 'S': /* --start */
503     start = 1;
504     break;
505     case 'V': /* --version */
506     printf("start-stop-daemon " VERSION "\n");
507     exit(0);
508     case 'a': /* --startas <pathname> */
509     startas = optarg;
510     break;
511     case 'n': /* --name <process-name> */
512     cmdname = optarg;
513     break;
514     case 'o': /* --oknodo */
515     exitnodo = 0;
516     break;
517     case 'p': /* --pidfile <pid-file> */
518     pidfile = optarg;
519     break;
520     case 'q': /* --quiet */
521     quietmode = 1;
522     break;
523     case 's': /* --signal <signal> */
524     signal_str = optarg;
525     break;
526     case 't': /* --test */
527     testmode = 1;
528     break;
529     case 'u': /* --user <username>|<uid> */
530     userspec = optarg;
531     break;
532     case 'v': /* --verbose */
533     quietmode = -1;
534     break;
535     case 'x': /* --exec <executable> */
536     execname = optarg;
537     break;
538     case 'c': /* --chuid <username>|<uid> */
539     /* we copy the string just in case we need the
540     * argument later. */
541     changeuser = strdup(optarg);
542     changeuser = strtok(changeuser, ":");
543     changegroup = strtok(NULL, ":");
544     break;
545     case 'g': /* --group <group>|<gid> */
546     changegroup = optarg;
547     break;
548     case 'r': /* --chroot /new/root */
549     changeroot = optarg;
550     break;
551     case 'N': /* --nice */
552     nicelevel = atoi(optarg);
553     break;
554     case 'b': /* --background */
555     background = 1;
556     break;
557     case 'm': /* --make-pidfile */
558     mpidfile = 1;
559     break;
560     case 'R': /* --retry <schedule>|<timeout> */
561     schedule_str = optarg;
562     break;
563     case 'd': /* --chdir /new/dir */
564     changedir = optarg;
565     break;
566     default:
567     badusage(NULL); /* message printed by getopt */
568     }
569     }
570    
571     if (signal_str != NULL) {
572     if (parse_signal (signal_str, &signal_nr) != 0)
573     badusage("signal value must be numeric or name"
574     " of signal (KILL, INT, ...)");
575     }
576    
577     if (schedule_str != NULL) {
578     parse_schedule(schedule_str);
579     }
580    
581     if (start == stop)
582     badusage("need one of --start or --stop");
583    
584     if (!execname && !pidfile && !userspec && !cmdname)
585     badusage("need at least one of --exec, --pidfile, --user or --name");
586    
587     if (!startas)
588     startas = execname;
589    
590     if (start && !startas)
591     badusage("--start needs --exec or --startas");
592    
593     if (mpidfile && pidfile == NULL)
594     badusage("--make-pidfile is only relevant with --pidfile");
595    
596     if (background && !start)
597     badusage("--background is only relevant with --start");
598    
599     }
600    
601     #if defined(OSLinux)
602     static int
603     pid_is_exec(pid_t pid, const struct stat *esb)
604     {
605     struct stat sb;
606     char buf[32];
607    
608     sprintf(buf, "/proc/%d/exe", pid);
609     if (stat(buf, &sb) != 0)
610     return 0;
611     return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino);
612     }
613    
614    
615     static int
616     pid_is_user(pid_t pid, uid_t uid)
617     {
618     struct stat sb;
619     char buf[32];
620    
621     sprintf(buf, "/proc/%d", pid);
622     if (stat(buf, &sb) != 0)
623     return 0;
624     return (sb.st_uid == uid);
625     }
626    
627    
628     static int
629     pid_is_cmd(pid_t pid, const char *name)
630     {
631     char buf[32];
632     FILE *f;
633     int c;
634    
635     sprintf(buf, "/proc/%d/stat", pid);
636     f = fopen(buf, "r");
637     if (!f)
638     return 0;
639     while ((c = getc(f)) != EOF && c != '(')
640     ;
641     if (c != '(') {
642     fclose(f);
643     return 0;
644     }
645     /* this hopefully handles command names containing ')' */
646     while ((c = getc(f)) != EOF && c == *name)
647     name++;
648     fclose(f);
649     return (c == ')' && *name == '\0');
650     }
651     #endif /* OSLinux */
652    
653    
654     #if defined(OSHURD)
655     static int
656     pid_is_user(pid_t pid, uid_t uid)
657     {
658     struct stat sb;
659     char buf[32];
660     struct proc_stat *pstat;
661    
662     sprintf(buf, "/proc/%d", pid);
663     if (stat(buf, &sb) != 0)
664     return 0;
665     return (sb.st_uid == uid);
666     pstat = proc_stat_list_pid_proc_stat (procset, pid);
667     if (pstat == NULL)
668     fatal ("Error getting process information: NULL proc_stat struct");
669     proc_stat_set_flags (pstat, PSTAT_PID | PSTAT_OWNER_UID);
670     return (pstat->owner_uid == uid);
671     }
672    
673     static int
674     pid_is_cmd(pid_t pid, const char *name)
675     {
676     struct proc_stat *pstat;
677     pstat = proc_stat_list_pid_proc_stat (procset, pid);
678     if (pstat == NULL)
679     fatal ("Error getting process information: NULL proc_stat struct");
680     proc_stat_set_flags (pstat, PSTAT_PID | PSTAT_ARGS);
681     return (!strcmp (name, pstat->args));
682     }
683     #endif /* OSHURD */
684    
685    
686     static int
687     pid_is_running(pid_t pid)
688     {
689     struct stat sb;
690     char buf[32];
691    
692     sprintf(buf, "/proc/%d", pid);
693     if (stat(buf, &sb) != 0) {
694     if (errno!=ENOENT)
695     fatal("Error stating %s: %s", buf, strerror(errno));
696     return 0;
697     }
698    
699     return 1;
700     }
701    
702     static void
703     check(pid_t pid)
704     {
705     #if defined(OSLinux) || defined(OShpux)
706     if (execname && !pid_is_exec(pid, &exec_stat))
707     #elif defined(OSHURD) || defined(OSFreeBSD) || defined(OSNetBSD)
708     /* I will try this to see if it works */
709     if (execname && !pid_is_cmd(pid, execname))
710     #endif
711     return;
712     if (userspec && !pid_is_user(pid, user_id))
713     return;
714     if (cmdname && !pid_is_cmd(pid, cmdname))
715     return;
716     if (start && !pid_is_running(pid))
717     return;
718     push(&found, pid);
719     }
720    
721     static void
722     do_pidfile(const char *name)
723     {
724     FILE *f;
725     pid_t pid;
726    
727     f = fopen(name, "r");
728     if (f) {
729     if (fscanf(f, "%d", &pid) == 1)
730     check(pid);
731     fclose(f);
732     } else if (errno != ENOENT)
733     fatal("open pidfile %s: %s", name, strerror(errno));
734    
735     }
736    
737     /* WTA: this needs to be an autoconf check for /proc/pid existance.
738     */
739    
740     #if defined(OSLinux) || defined (OSsunos) || defined(OSfreebsd)
741     static void
742     do_procinit(void)
743     {
744     DIR *procdir;
745     struct dirent *entry;
746     int foundany;
747     pid_t pid;
748    
749     procdir = opendir("/proc");
750     if (!procdir)
751     fatal("opendir /proc: %s", strerror(errno));
752    
753     foundany = 0;
754     while ((entry = readdir(procdir)) != NULL) {
755     if (sscanf(entry->d_name, "%d", &pid) != 1)
756     continue;
757     foundany++;
758     check(pid);
759     }
760     closedir(procdir);
761     if (!foundany)
762     fatal("nothing in /proc - not mounted?");
763     }
764     #endif /* OSLinux */
765    
766    
767     #if defined(OSHURD)
768     error_t
769     check_all(void *ptr)
770     {
771     struct proc_stat *pstat = ptr;
772    
773     check(pstat->pid);
774     return 0;
775     }
776    
777     static void
778     do_procinit(void)
779     {
780     struct ps_context *context;
781     error_t err;
782    
783     err = ps_context_create(getproc(), &context);
784     if (err)
785     error(1, err, "ps_context_create");
786    
787     err = proc_stat_list_create(context, &procset);
788     if (err)
789     error(1, err, "proc_stat_list_create");
790    
791     err = proc_stat_list_add_all(procset, 0, 0);
792     if (err)
793     error(1, err, "proc_stat_list_add_all");
794    
795     /* Check all pids */
796     ihash_iterate(context->procs, check_all);
797     }
798     #endif /* OSHURD */
799    
800    
801     #if defined(OSOpenBSD) || defined(OSFreeBSD) || defined(OSNetBSD)
802     static int
803     pid_is_cmd(pid_t pid, const char *name)
804     {
805     kvm_t *kd;
806     int nentries, argv_len=0;
807     struct kinfo_proc *kp;
808     char errbuf[_POSIX2_LINE_MAX], buf[_POSIX2_LINE_MAX];
809     char **pid_argv_p;
810     char *start_argv_0_p, *end_argv_0_p;
811    
812    
813     kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
814     if (kd == 0)
815     errx(1, "%s", errbuf);
816     if ((kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &nentries)) == 0)
817     errx(1, "%s", kvm_geterr(kd));
818     if ((pid_argv_p = kvm_getargv(kd, kp, argv_len)) == 0)
819     errx(1, "%s", kvm_geterr(kd));
820    
821     start_argv_0_p = *pid_argv_p;
822     /* find and compare string */
823    
824     /* find end of argv[0] then copy and cut of str there. */
825     if ((end_argv_0_p = strchr(*pid_argv_p, ' ')) == 0 )
826     /* There seems to be no space, so we have the command
827     * allready in its desired form. */
828     start_argv_0_p = *pid_argv_p;
829     else {
830     /* Tests indicate that this never happens, since
831     * kvm_getargv itselfe cuts of tailing stuff. This is
832     * not what the manpage says, however. */
833     strncpy(buf, *pid_argv_p, (end_argv_0_p - start_argv_0_p));
834     buf[(end_argv_0_p - start_argv_0_p) + 1] = '\0';
835     start_argv_0_p = buf;
836     }
837    
838     if (strlen(name) != strlen(start_argv_0_p))
839     return 0;
840     return (strcmp(name, start_argv_0_p) == 0) ? 1 : 0;
841     }
842    
843     static int
844     pid_is_user(pid_t pid, uid_t uid)
845     {
846     kvm_t *kd;
847     int nentries; /* Value not used */
848     uid_t proc_uid;
849     struct kinfo_proc *kp;
850     char errbuf[_POSIX2_LINE_MAX];
851    
852    
853     kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
854     if (kd == 0)
855     errx(1, "%s", errbuf);
856     if ((kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &nentries)) == 0)
857     errx(1, "%s", kvm_geterr(kd));
858     if (kp->ki_ruid )
859     kvm_read(kd, (u_long)&(kp->ki_ruid),
860     &proc_uid, sizeof(uid_t));
861     else
862     return 0;
863     return (proc_uid == (uid_t)uid);
864     }
865    
866     static int
867     pid_is_exec(pid_t pid, const char *name)
868     {
869     kvm_t *kd;
870     int nentries;
871     struct kinfo_proc *kp;
872     char errbuf[_POSIX2_LINE_MAX], *pidexec;
873    
874     kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
875     if (kd == 0)
876     errx(1, "%s", errbuf);
877     if ((kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &nentries)) == 0)
878     errx(1, "%s", kvm_geterr(kd));
879     pidexec = kp->ki_comm;
880     if (strlen(name) != strlen(pidexec))
881     return 0;
882     return (strcmp(name, pidexec) == 0) ? 1 : 0;
883     }
884    
885    
886     static void
887     do_procinit(void)
888     {
889     /* Nothing to do */
890     }
891    
892     #endif /* OSOpenBSD */
893    
894    
895     #if defined(OShpux)
896     static int
897     pid_is_user(pid_t pid, uid_t uid)
898     {
899     struct pst_status pst;
900    
901     if (pstat_getproc(&pst, sizeof(pst), (size_t) 0, (int) pid) < 0)
902     return 0;
903     return ((uid_t) pst.pst_uid == uid);
904     }
905    
906     static int
907     pid_is_cmd(pid_t pid, const char *name)
908     {
909     struct pst_status pst;
910    
911     if (pstat_getproc(&pst, sizeof(pst), (size_t) 0, (int) pid) < 0)
912     return 0;
913     return (strcmp(pst.pst_ucomm, name) == 0);
914     }
915    
916     static int
917     pid_is_exec(pid_t pid, const struct stat *esb)
918     {
919     struct pst_status pst;
920    
921     if (pstat_getproc(&pst, sizeof(pst), (size_t) 0, (int) pid) < 0)
922     return 0;
923     return ((dev_t) pst.pst_text.psf_fsid.psfs_id == esb->st_dev
924     && (ino_t) pst.pst_text.psf_fileid == esb->st_ino);
925     }
926    
927     static void
928     do_procinit(void)
929     {
930     struct pst_status pst[10];
931     int i, count;
932     int idx = 0;
933    
934     while ((count = pstat_getproc(pst, sizeof(pst[0]), 10, idx)) > 0) {
935     for (i = 0; i < count; i++)
936     check(pst[i].pst_pid);
937     idx = pst[count - 1].pst_idx + 1;
938     }
939     }
940     #endif /* OShpux */
941    
942    
943     static void
944     do_findprocs(void)
945     {
946     clear(&found);
947    
948     if (pidfile)
949     do_pidfile(pidfile);
950     else
951     do_procinit();
952     }
953    
954     /* return 1 on failure */
955     static void
956     do_stop(int signal_nr, int quietmode, int *n_killed, int *n_notkilled, int retry_nr)
957     {
958     struct pid_list *p;
959    
960     do_findprocs();
961    
962     *n_killed = 0;
963     *n_notkilled = 0;
964    
965     if (!found)
966     return;
967    
968     clear(&killed);
969    
970     for (p = found; p; p = p->next) {
971     if (testmode) {
972     printf("Would send signal %d to %d.\n",
973     signal_nr, p->pid);
974     (*n_killed)++;
975     } else if (kill(p->pid, signal_nr) == 0) {
976     push(&killed, p->pid);
977     (*n_killed)++;
978     } else {
979     printf("%s: warning: failed to kill %d: %s\n",
980     progname, p->pid, strerror(errno));
981     (*n_notkilled)++;
982     }
983     }
984     if (quietmode < 0 && killed) {
985     printf("Stopped %s (pid", what_stop);
986     for (p = killed; p; p = p->next)
987     printf(" %d", p->pid);
988     putchar(')');
989     if (retry_nr > 0)
990     printf(", retry #%d", retry_nr);
991     printf(".\n");
992     }
993     }
994    
995    
996     static void
997     set_what_stop(const char *str)
998     {
999     strncpy(what_stop, str, sizeof(what_stop));
1000     what_stop[sizeof(what_stop)-1] = '\0';
1001     }
1002    
1003     static int
1004     run_stop_schedule(void)
1005     {
1006     int r, position, n_killed, n_notkilled, value, ratio, anykilled, retry_nr;
1007     struct timeval stopat, before, after, interval, maxinterval;
1008    
1009     if (testmode) {
1010     if (schedule != NULL) {
1011     printf("Ignoring --retry in test mode\n");
1012     schedule = NULL;
1013     }
1014     }
1015    
1016     if (cmdname)
1017     set_what_stop(cmdname);
1018     else if (execname)
1019     set_what_stop(execname);
1020     else if (pidfile)
1021     sprintf(what_stop, "process in pidfile `%.200s'", pidfile);
1022     else if (userspec)
1023     sprintf(what_stop, "process(es) owned by `%.200s'", userspec);
1024     else
1025     fatal("internal error, please report");
1026    
1027     anykilled = 0;
1028     retry_nr = 0;
1029    
1030     if (schedule == NULL) {
1031     do_stop(signal_nr, quietmode, &n_killed, &n_notkilled, 0);
1032     if (n_notkilled > 0 && quietmode <= 0)
1033     printf("%d pids were not killed\n", n_notkilled);
1034     if (n_killed)
1035     anykilled = 1;
1036     goto x_finished;
1037     }
1038    
1039     for (position = 0; position < schedule_length; ) {
1040     value= schedule[position].value;
1041     n_notkilled = 0;
1042    
1043     switch (schedule[position].type) {
1044    
1045     case sched_goto:
1046     position = value;
1047     continue;
1048    
1049     case sched_signal:
1050     do_stop(value, quietmode, &n_killed, &n_notkilled, retry_nr++);
1051     if (!n_killed)
1052     goto x_finished;
1053     else
1054     anykilled = 1;
1055     goto next_item;
1056    
1057     case sched_timeout:
1058     /* We want to keep polling for the processes, to see if they've exited,
1059     * or until the timeout expires.
1060     *
1061     * This is a somewhat complicated algorithm to try to ensure that we
1062     * notice reasonably quickly when all the processes have exited, but
1063     * don't spend too much CPU time polling. In particular, on a fast
1064     * machine with quick-exiting daemons we don't want to delay system
1065     * shutdown too much, whereas on a slow one, or where processes are
1066     * taking some time to exit, we want to increase the polling
1067     * interval.
1068     *
1069     * The algorithm is as follows: we measure the elapsed time it takes
1070     * to do one poll(), and wait a multiple of this time for the next
1071     * poll. However, if that would put us past the end of the timeout
1072     * period we wait only as long as the timeout period, but in any case
1073     * we always wait at least MIN_POLL_INTERVAL (20ms). The multiple
1074     * (`ratio') starts out as 2, and increases by 1 for each poll to a
1075     * maximum of 10; so we use up to between 30% and 10% of the
1076     * machine's resources (assuming a few reasonable things about system
1077     * performance).
1078     */
1079     xgettimeofday(&stopat);
1080     stopat.tv_sec += value;
1081     ratio = 1;
1082     for (;;) {
1083     xgettimeofday(&before);
1084     if (timercmp(&before,&stopat,>))
1085     goto next_item;
1086    
1087     do_stop(0, 1, &n_killed, &n_notkilled, 0);
1088     if (!n_killed)
1089     goto x_finished;
1090    
1091     xgettimeofday(&after);
1092    
1093     if (!timercmp(&after,&stopat,<))
1094     goto next_item;
1095    
1096     if (ratio < 10)
1097     ratio++;
1098    
1099     TVCALC(interval, ratio * (TVELEM(&after) - TVELEM(&before) + TVADJUST));
1100     TVCALC(maxinterval, TVELEM(&stopat) - TVELEM(&after) + TVADJUST);
1101    
1102     if (timercmp(&interval,&maxinterval,>))
1103     interval = maxinterval;
1104    
1105     if (interval.tv_sec == 0 &&
1106     interval.tv_usec <= MIN_POLL_INTERVAL)
1107     interval.tv_usec = MIN_POLL_INTERVAL;
1108    
1109     r = select(0,0,0,0,&interval);
1110     if (r < 0 && errno != EINTR)
1111     fatal("select() failed for pause: %s",
1112     strerror(errno));
1113     }
1114    
1115     default:
1116     assert(!"schedule[].type value must be valid");
1117    
1118     }
1119    
1120     next_item:
1121     position++;
1122     }
1123    
1124     if (quietmode <= 0)
1125     printf("Program %s, %d process(es), refused to die.\n",
1126     what_stop, n_killed);
1127    
1128     return 2;
1129    
1130     x_finished:
1131     if (!anykilled) {
1132     if (quietmode <= 0)
1133     printf("No %s found running; none killed.\n", what_stop);
1134     return exitnodo;
1135     } else {
1136     return 0;
1137     }
1138     }
1139    
1140    
1141     int main(int argc, char **argv) NONRETURNING;
1142     int
1143     main(int argc, char **argv)
1144     {
1145     int devnull_fd = -1;
1146     #ifdef HAVE_TIOCNOTTY
1147     int tty_fd = -1;
1148     #endif
1149     progname = argv[0];
1150    
1151     parse_options(argc, argv);
1152     argc -= optind;
1153     argv += optind;
1154    
1155     if (execname && stat(execname, &exec_stat))
1156     fatal("stat %s: %s", execname, strerror(errno));
1157    
1158     if (userspec && sscanf(userspec, "%d", &user_id) != 1) {
1159     struct passwd *pw;
1160    
1161     pw = getpwnam(userspec);
1162     if (!pw)
1163     fatal("user `%s' not found\n", userspec);
1164    
1165     user_id = pw->pw_uid;
1166     }
1167    
1168     if (changegroup && sscanf(changegroup, "%d", &runas_gid) != 1) {
1169     struct group *gr = getgrnam(changegroup);
1170     if (!gr)
1171     fatal("group `%s' not found\n", changegroup);
1172     runas_gid = gr->gr_gid;
1173     }
1174     if (changeuser && sscanf(changeuser, "%d", &runas_uid) != 1) {
1175     struct passwd *pw = getpwnam(changeuser);
1176     if (!pw)
1177     fatal("user `%s' not found\n", changeuser);
1178     runas_uid = pw->pw_uid;
1179     if (changegroup == NULL) { /* pass the default group of this user */
1180     changegroup = ""; /* just empty */
1181     runas_gid = pw->pw_gid;
1182     }
1183     }
1184    
1185     if (stop) {
1186     int i = run_stop_schedule();
1187     exit(i);
1188     }
1189    
1190     do_findprocs();
1191    
1192     if (found) {
1193     if (quietmode <= 0)
1194     printf("%s already running.\n", execname ? execname : "process");
1195     exit(exitnodo);
1196     }
1197     if (testmode) {
1198     printf("Would start %s ", startas);
1199     while (argc-- > 0)
1200     printf("%s ", *argv++);
1201     if (changeuser != NULL) {
1202     printf(" (as user %s[%d]", changeuser, runas_uid);
1203     if (changegroup != NULL)
1204     printf(", and group %s[%d])", changegroup, runas_gid);
1205     else
1206     printf(")");
1207     }
1208     if (changeroot != NULL)
1209     printf(" in directory %s", changeroot);
1210     if (nicelevel)
1211     printf(", and add %i to the priority", nicelevel);
1212     printf(".\n");
1213     exit(0);
1214     }
1215     if (quietmode < 0)
1216     printf("Starting %s...\n", startas);
1217     *--argv = startas;
1218     if (background) { /* ok, we need to detach this process */
1219     int i;
1220     if (quietmode < 0)
1221     printf("Detatching to start %s...", startas);
1222     i = fork();
1223     if (i<0) {
1224     fatal("Unable to fork.\n");
1225     }
1226     if (i) { /* parent */
1227     if (quietmode < 0)
1228     printf("done.\n");
1229     exit(0);
1230     }
1231     /* child continues here */
1232    
1233     #ifdef HAVE_TIOCNOTTY
1234     tty_fd=open("/dev/tty", O_RDWR);
1235     #endif
1236     devnull_fd=open("/dev/null", O_RDWR);
1237     }
1238     if (nicelevel) {
1239     errno=0;
1240     if ((nice(nicelevel)==-1) && (errno!=0))
1241     fatal("Unable to alter nice level by %i: %s", nicelevel,
1242     strerror(errno));
1243     }
1244     if (mpidfile && pidfile != NULL) { /* user wants _us_ to make the pidfile :) */
1245     FILE *pidf = fopen(pidfile, "w");
1246     pid_t pidt = getpid();
1247     if (pidf == NULL)
1248     fatal("Unable to open pidfile `%s' for writing: %s", pidfile,
1249     strerror(errno));
1250     fprintf(pidf, "%d\n", pidt);
1251     fclose(pidf);
1252     }
1253     if (changeroot != NULL) {
1254     if (chdir(changeroot) < 0)
1255     fatal("Unable to chdir() to %s", changeroot);
1256     if (chroot(changeroot) < 0)
1257     fatal("Unable to chroot() to %s", changeroot);
1258     }
1259     if (changedir != NULL && chdir(changedir) < 0)
1260     fatal("Unable to chdir() to %s", changedir);
1261     if (changeuser != NULL) {
1262     if (setgid(runas_gid))
1263     fatal("Unable to set gid to %d", runas_gid);
1264     if (initgroups(changeuser, runas_gid))
1265     fatal("Unable to set initgroups() with gid %d", runas_gid);
1266     if (setuid(runas_uid))
1267     fatal("Unable to set uid to %s", changeuser);
1268     }
1269     if (background) { /* continue background setup */
1270     int i;
1271     #ifdef HAVE_TIOCNOTTY
1272     /* change tty */
1273     ioctl(tty_fd, TIOCNOTTY, 0);
1274     close(tty_fd);
1275     #endif
1276     umask(022); /* set a default for dumb programs */
1277     dup2(devnull_fd,0); /* stdin */
1278     dup2(devnull_fd,1); /* stdout */
1279     dup2(devnull_fd,2); /* stderr */
1280     #if defined(OShpux)
1281     /* now close all extra fds */
1282     for (i=sysconf(_SC_OPEN_MAX)-1; i>=3; --i) close(i);
1283     #else
1284     /* now close all extra fds */
1285     for (i=getdtablesize()-1; i>=3; --i) close(i);
1286     #endif
1287    
1288     /* create a new session */
1289     #ifdef HAVE_SETSID
1290     setsid();
1291     #else
1292     setpgid(0,0);
1293     #endif
1294     }
1295     execv(startas, argv);
1296     fatal("Unable to start %s: %s", startas, strerror(errno));
1297     }
1298