/[gentoo-src]/rc-scripts/src/start-stop-daemon.c
Gentoo

Contents of /rc-scripts/src/start-stop-daemon.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (show annotations) (download) (as text)
Fri May 13 05:24:43 2005 UTC (14 years, 10 months ago) by vapier
Branch: MAIN
CVS Tags: HEAD
Changes since 1.4: +1 -1 lines
File MIME type: text/x-csrc
add update from upstream ssd to detect kfbsd #80021

1 /*
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

  ViewVC Help
Powered by ViewVC 1.1.20