/[gentoo-alt]/trunk/baselayout-prefix/src/start-stop-daemon.c
Gentoo

Contents of /trunk/baselayout-prefix/src/start-stop-daemon.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1687 - (hide annotations) (download) (as text)
Thu Mar 31 14:12:21 2011 UTC (3 years, 5 months ago) by grobian
File MIME type: text/x-csrc
File size: 35016 byte(s)
applied baselayout-1.12.5-termios_h.patch

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

  ViewVC Help
Powered by ViewVC 1.1.20