source: tools/start-stop-daemon_nondeb/start-stop-daemon_nondeb.c@ 3

Last change on this file since 3 was 2, checked in by alloc, 11 years ago

Refs #2

File size: 33.8 KB
Line 
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 as well.
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/*
24 * MODIFIED TO NOT DEPEND ON EXTERNAL HEADERS!
25 * By Christian Illy <christian@illy.bz>
26 * Used dpkg source package from https://packages.debian.org/stable/dpkg
27 * Version 1.16.14
28 */
29
30// from config.h, generated by configure:
31#define VERSION "1.16.14_01"
32
33//*****************************************
34// Stuff from dpkg/macros.h
35#define DPKG_ATTR_NORET
36#define DPKG_ATTR_PRINTF(n)
37/**
38 * @def array_count
39 *
40 * Returns the amount of items in an array.
41 */
42#ifndef array_count
43#define array_count(a) (sizeof(a) / sizeof((a)[0]))
44#endif
45// End of stuff from dpkg/macros.h
46//*****************************************
47
48
49#define OSLinux
50
51#define MIN_POLL_INTERVAL 20000 /* µs */
52
53#include <sys/syscall.h>
54
55#include <sys/types.h>
56#include <sys/time.h>
57#include <sys/stat.h>
58#include <sys/ioctl.h>
59#include <sys/termios.h>
60
61#include <assert.h>
62#include <errno.h>
63#include <limits.h>
64#include <fcntl.h>
65#include <dirent.h>
66#include <ctype.h>
67#include <string.h>
68#include <pwd.h>
69#include <grp.h>
70#include <signal.h>
71#include <unistd.h>
72#include <stddef.h>
73#include <stdbool.h>
74#include <stdarg.h>
75#include <stdlib.h>
76#include <stdio.h>
77#include <getopt.h>
78
79#include <error.h>
80
81#ifdef _POSIX_PRIORITY_SCHEDULING
82#include <sched.h>
83#else
84#define SCHED_OTHER -1
85#define SCHED_FIFO -1
86#define SCHED_RR -1
87#endif
88
89/* This comes from TASK_COMM_LEN defined in Linux' include/linux/sched.h. */
90#define PROCESS_NAME_SIZE 15
91
92#if defined(SYS_ioprio_set)
93#define HAVE_IOPRIO_SET
94#endif
95
96enum {
97 IOPRIO_WHO_PROCESS = 1,
98 IOPRIO_WHO_PGRP,
99 IOPRIO_WHO_USER,
100};
101
102enum {
103 IOPRIO_CLASS_NONE,
104 IOPRIO_CLASS_RT,
105 IOPRIO_CLASS_BE,
106 IOPRIO_CLASS_IDLE,
107};
108
109enum action_code {
110 action_none,
111 action_start,
112 action_stop,
113 action_status,
114};
115
116static enum action_code action;
117static int testmode = 0;
118static int quietmode = 0;
119static int exitnodo = 1;
120static int background = 0;
121static int close_io = 1;
122static int mpidfile = 0;
123static int signal_nr = SIGTERM;
124static int user_id = -1;
125static int runas_uid = -1;
126static int runas_gid = -1;
127static const char *userspec = NULL;
128static char *changeuser = NULL;
129static const char *changegroup = NULL;
130static char *changeroot = NULL;
131static const char *changedir = "/";
132static const char *cmdname = NULL;
133static char *execname = NULL;
134static char *startas = NULL;
135static const char *pidfile = NULL;
136static char what_stop[1024];
137static const char *progname = "";
138static int nicelevel = 0;
139static int umask_value = -1;
140
141#define IOPRIO_CLASS_SHIFT 13
142#define IOPRIO_PRIO_VALUE(class, prio) (((class) << IOPRIO_CLASS_SHIFT) | (prio))
143#define IO_SCHED_PRIO_MIN 0
144#define IO_SCHED_PRIO_MAX 7
145
146static struct stat exec_stat;
147
148/* LSB Init Script process status exit codes. */
149enum status_code {
150 status_ok = 0,
151 status_dead_pidfile = 1,
152 status_dead_lockfile = 2,
153 status_dead = 3,
154 status_unknown = 4,
155};
156
157struct pid_list {
158 struct pid_list *next;
159 pid_t pid;
160};
161
162static struct pid_list *found = NULL;
163static struct pid_list *killed = NULL;
164
165/* Resource scheduling policy. */
166struct res_schedule {
167 const char *policy_name;
168 int policy;
169 int priority;
170};
171
172struct schedule_item {
173 enum {
174 sched_timeout,
175 sched_signal,
176 sched_goto,
177 /* Only seen within parse_schedule and callees. */
178 sched_forever,
179 } type;
180 /* Seconds, signal no., or index into array. */
181 int value;
182};
183
184static struct res_schedule *proc_sched = NULL;
185static struct res_schedule *io_sched = NULL;
186
187static int schedule_length;
188static struct schedule_item *schedule = NULL;
189
190
191static void DPKG_ATTR_PRINTF(1)
192warning(const char *format, ...)
193{
194 va_list arglist;
195
196 fprintf(stderr, "%s: warning: ", progname);
197 va_start(arglist, format);
198 vfprintf(stderr, format, arglist);
199 va_end(arglist);
200}
201
202static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1)
203fatal(const char *format, ...)
204{
205 va_list arglist;
206 int errno_fatal = errno;
207
208 fprintf(stderr, "%s: ", progname);
209 va_start(arglist, format);
210 vfprintf(stderr, format, arglist);
211 va_end(arglist);
212 if (errno_fatal)
213 fprintf(stderr, " (%s)\n", strerror(errno_fatal));
214 else
215 fprintf(stderr, "\n");
216
217 if (action == action_status)
218 exit(status_unknown);
219 else
220 exit(2);
221}
222
223static void *
224xmalloc(int size)
225{
226 void *ptr;
227
228 ptr = malloc(size);
229 if (ptr)
230 return ptr;
231 fatal("malloc(%d) failed", size);
232}
233
234static char *
235xstrdup(const char *str)
236{
237 char *new_str;
238
239 new_str = strdup(str);
240 if (new_str)
241 return new_str;
242 fatal("strdup(%s) failed", str);
243}
244
245static void
246xgettimeofday(struct timeval *tv)
247{
248 if (gettimeofday(tv, NULL) != 0)
249 fatal("gettimeofday failed");
250}
251
252static void
253tmul(struct timeval *a, int b)
254{
255 a->tv_sec *= b;
256 a->tv_usec *= b;
257 a->tv_sec = a->tv_sec + a->tv_usec / 1000000;
258 a->tv_usec %= 1000000;
259}
260
261static char *
262newpath(const char *dirname, const char *filename)
263{
264 char *path;
265 size_t path_len;
266
267 path_len = strlen(dirname) + 1 + strlen(filename) + 1;
268 path = xmalloc(path_len);
269 snprintf(path, path_len, "%s/%s", dirname, filename);
270
271 return path;
272}
273
274static long
275get_open_fd_max(void)
276{
277 return getdtablesize();
278}
279
280static void
281daemonize(void)
282{
283 pid_t pid;
284
285 if (quietmode < 0)
286 printf("Detaching to start %s...", startas);
287
288 pid = fork();
289 if (pid < 0)
290 fatal("unable to do first fork");
291 else if (pid) /* Parent. */
292 _exit(0);
293
294 /* Create a new session. */
295 setsid();
296
297 pid = fork();
298 if (pid < 0)
299 fatal("unable to do second fork");
300 else if (pid) /* Parent. */
301 _exit(0);
302
303 if (quietmode < 0)
304 printf("done.\n");
305}
306
307static void
308write_pidfile(const char *filename, pid_t pid)
309{
310 FILE *fp;
311 int fd;
312
313 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW, 0666);
314 if (fd < 0)
315 fp = NULL;
316 else
317 fp = fdopen(fd, "w");
318
319 if (fp == NULL)
320 fatal("unable to open pidfile '%s' for writing", filename);
321
322 fprintf(fp, "%d\n", pid);
323
324 if (fclose(fp))
325 fatal("unable to close pidfile '%s'", filename);
326}
327
328static void
329pid_list_push(struct pid_list **list, pid_t pid)
330{
331 struct pid_list *p;
332
333 p = xmalloc(sizeof(*p));
334 p->next = *list;
335 p->pid = pid;
336 *list = p;
337}
338
339static void
340pid_list_free(struct pid_list **list)
341{
342 struct pid_list *here, *next;
343
344 for (here = *list; here != NULL; here = next) {
345 next = here->next;
346 free(here);
347 }
348
349 *list = NULL;
350}
351
352static void
353usage(void)
354{
355 printf(
356"Usage: start-stop-daemon [<option> ...] <command>\n"
357"\n"
358"Commands:\n"
359" -S|--start -- <argument> ... start a program and pass <arguments> to it\n"
360" -K|--stop stop a program\n"
361" -T|--status get the program status\n"
362" -H|--help print help information\n"
363" -V|--version print version\n"
364"\n"
365"Matching options (at least one is required):\n"
366" -p|--pidfile <pid-file> pid file to check\n"
367" -x|--exec <executable> program to start/check if it is running\n"
368" -n|--name <process-name> process name to check\n"
369" -u|--user <username|uid> process owner to check\n"
370"\n"
371"Options:\n"
372" -g|--group <group|gid> run process as this group\n"
373" -c|--chuid <name|uid[:group|gid]>\n"
374" change to this user/group before starting\n"
375" process\n"
376" -s|--signal <signal> signal to send (default TERM)\n"
377" -a|--startas <pathname> program to start (default is <executable>)\n"
378" -r|--chroot <directory> chroot to <directory> before starting\n"
379" -d|--chdir <directory> change to <directory> (default is /)\n"
380" -N|--nicelevel <incr> add incr to the process' nice level\n"
381" -P|--procsched <policy[:prio]>\n"
382" use <policy> with <prio> for the kernel\n"
383" process scheduler (default prio is 0)\n"
384" -I|--iosched <class[:prio]> use <class> with <prio> to set the IO\n"
385" scheduler (default prio is 4)\n"
386" -k|--umask <mask> change the umask to <mask> before starting\n"
387" -b|--background force the process to detach\n"
388" -C|--no-close do not close any file descriptor\n"
389" -m|--make-pidfile create the pidfile before starting\n"
390" -R|--retry <schedule> check whether processes die, and retry\n"
391" -t|--test test mode, don't do anything\n"
392" -o|--oknodo exit status 0 (not 1) if nothing done\n"
393" -q|--quiet be more quiet\n"
394" -v|--verbose be more verbose\n"
395"\n"
396"Retry <schedule> is <item>|/<item>/... where <item> is one of\n"
397" -<signal-num>|[-]<signal-name> send that signal\n"
398" <timeout> wait that many seconds\n"
399" forever repeat remainder forever\n"
400"or <schedule> may be just <timeout>, meaning <signal>/<timeout>/KILL/<timeout>\n"
401"\n"
402"The process scheduler <policy> can be one of:\n"
403" other, fifo or rr\n"
404"\n"
405"The IO scheduler <class> can be one of:\n"
406" real-time, best-effort or idle\n"
407"\n"
408"Exit status:\n"
409" 0 = done\n"
410" 1 = nothing done (=> 0 if --oknodo)\n"
411" 2 = with --retry, processes would not die\n"
412" 3 = trouble\n"
413"Exit status with --status:\n"
414" 0 = program is running\n"
415" 1 = program is not running and the pid file exists\n"
416" 3 = program is not running\n"
417" 4 = unable to determine status\n");
418}
419
420static void
421do_version(void)
422{
423 printf("start-stop-daemon %s for non-Debian\n\n", VERSION);
424
425 printf("Written by Marek Michalkiewicz, public domain.\n");
426}
427
428static void DPKG_ATTR_NORET
429badusage(const char *msg)
430{
431 if (msg)
432 fprintf(stderr, "%s: %s\n", progname, msg);
433 fprintf(stderr, "Try '%s --help' for more information.\n", progname);
434
435 if (action == action_status)
436 exit(status_unknown);
437 else
438 exit(3);
439}
440
441struct sigpair {
442 const char *name;
443 int signal;
444};
445
446static const struct sigpair siglist[] = {
447 { "ABRT", SIGABRT },
448 { "ALRM", SIGALRM },
449 { "FPE", SIGFPE },
450 { "HUP", SIGHUP },
451 { "ILL", SIGILL },
452 { "INT", SIGINT },
453 { "KILL", SIGKILL },
454 { "PIPE", SIGPIPE },
455 { "QUIT", SIGQUIT },
456 { "SEGV", SIGSEGV },
457 { "TERM", SIGTERM },
458 { "USR1", SIGUSR1 },
459 { "USR2", SIGUSR2 },
460 { "CHLD", SIGCHLD },
461 { "CONT", SIGCONT },
462 { "STOP", SIGSTOP },
463 { "TSTP", SIGTSTP },
464 { "TTIN", SIGTTIN },
465 { "TTOU", SIGTTOU }
466};
467
468static int
469parse_unsigned(const char *string, int base, int *value_r)
470{
471 long value;
472 char *endptr;
473
474 if (!string[0])
475 return -1;
476
477 errno = 0;
478 value = strtol(string, &endptr, base);
479 if (string == endptr || *endptr != '\0' || errno != 0)
480 return -1;
481 if (value < 0 || value > INT_MAX)
482 return -1;
483
484 *value_r = value;
485 return 0;
486}
487
488static int
489parse_signal(const char *sig_str, int *sig_num)
490{
491 unsigned int i;
492
493 if (parse_unsigned(sig_str, 10, sig_num) == 0)
494 return 0;
495
496 for (i = 0; i < array_count(siglist); i++) {
497 if (strcmp(sig_str, siglist[i].name) == 0) {
498 *sig_num = siglist[i].signal;
499 return 0;
500 }
501 }
502 return -1;
503}
504
505static int
506parse_umask(const char *string, int *value_r)
507{
508 return parse_unsigned(string, 0, value_r);
509}
510
511static void
512validate_proc_schedule(void)
513{
514#ifdef _POSIX_PRIORITY_SCHEDULING
515 int prio_min, prio_max;
516
517 prio_min = sched_get_priority_min(proc_sched->policy);
518 prio_max = sched_get_priority_max(proc_sched->policy);
519
520 if (proc_sched->priority < prio_min)
521 badusage("process scheduler priority less than min");
522 if (proc_sched->priority > prio_max)
523 badusage("process scheduler priority greater than max");
524#endif
525}
526
527static void
528parse_proc_schedule(const char *string)
529{
530 char *policy_str, *prio_str;
531 int prio = 0;
532
533 policy_str = xstrdup(string);
534 policy_str = strtok(policy_str, ":");
535 prio_str = strtok(NULL, ":");
536
537 if (prio_str && parse_unsigned(prio_str, 10, &prio) != 0)
538 fatal("invalid process scheduler priority");
539
540 proc_sched = xmalloc(sizeof(*proc_sched));
541 proc_sched->policy_name = policy_str;
542
543 if (strcmp(policy_str, "other") == 0) {
544 proc_sched->policy = SCHED_OTHER;
545 proc_sched->priority = 0;
546 } else if (strcmp(policy_str, "fifo") == 0) {
547 proc_sched->policy = SCHED_FIFO;
548 proc_sched->priority = prio;
549 } else if (strcmp(policy_str, "rr") == 0) {
550 proc_sched->policy = SCHED_RR;
551 proc_sched->priority = prio;
552 } else
553 badusage("invalid process scheduler policy");
554
555 validate_proc_schedule();
556}
557
558static void
559parse_io_schedule(const char *string)
560{
561 char *class_str, *prio_str;
562 int prio = 4;
563
564 class_str = xstrdup(string);
565 class_str = strtok(class_str, ":");
566 prio_str = strtok(NULL, ":");
567
568 if (prio_str && parse_unsigned(prio_str, 10, &prio) != 0)
569 fatal("invalid IO scheduler priority");
570
571 io_sched = xmalloc(sizeof(*io_sched));
572 io_sched->policy_name = class_str;
573
574 if (strcmp(class_str, "real-time") == 0) {
575 io_sched->policy = IOPRIO_CLASS_RT;
576 io_sched->priority = prio;
577 } else if (strcmp(class_str, "best-effort") == 0) {
578 io_sched->policy = IOPRIO_CLASS_BE;
579 io_sched->priority = prio;
580 } else if (strcmp(class_str, "idle") == 0) {
581 io_sched->policy = IOPRIO_CLASS_IDLE;
582 io_sched->priority = 7;
583 } else
584 badusage("invalid IO scheduler policy");
585
586 if (io_sched->priority < IO_SCHED_PRIO_MIN)
587 badusage("IO scheduler priority less than min");
588 if (io_sched->priority > IO_SCHED_PRIO_MAX)
589 badusage("IO scheduler priority greater than max");
590}
591
592static void
593set_proc_schedule(struct res_schedule *sched)
594{
595#ifdef _POSIX_PRIORITY_SCHEDULING
596 struct sched_param param;
597
598 param.sched_priority = sched->priority;
599
600 if (sched_setscheduler(getpid(), sched->policy, &param) == -1)
601 fatal("unable to set process scheduler");
602#endif
603}
604
605#ifdef HAVE_IOPRIO_SET
606static inline int
607ioprio_set(int which, int who, int ioprio)
608{
609 return syscall(SYS_ioprio_set, which, who, ioprio);
610}
611#endif
612
613static void
614set_io_schedule(struct res_schedule *sched)
615{
616#ifdef HAVE_IOPRIO_SET
617 int io_sched_mask;
618
619 io_sched_mask = IOPRIO_PRIO_VALUE(sched->policy, sched->priority);
620 if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), io_sched_mask) == -1)
621 warning("unable to alter IO priority to mask %i (%s)\n",
622 io_sched_mask, strerror(errno));
623#endif
624}
625
626static void
627parse_schedule_item(const char *string, struct schedule_item *item)
628{
629 const char *after_hyph;
630
631 if (strcmp(string, "forever") == 0) {
632 item->type = sched_forever;
633 } else if (isdigit(string[0])) {
634 item->type = sched_timeout;
635 if (parse_unsigned(string, 10, &item->value) != 0)
636 badusage("invalid timeout value in schedule");
637 } else if ((after_hyph = string + (string[0] == '-')) &&
638 parse_signal(after_hyph, &item->value) == 0) {
639 item->type = sched_signal;
640 } else {
641 badusage("invalid schedule item (must be [-]<signal-name>, "
642 "-<signal-number>, <timeout> or 'forever'");
643 }
644}
645
646static void
647parse_schedule(const char *schedule_str)
648{
649 char item_buf[20];
650 const char *slash;
651 int count, repeatat;
652 size_t str_len;
653
654 count = 0;
655 for (slash = schedule_str; *slash; slash++)
656 if (*slash == '/')
657 count++;
658
659 schedule_length = (count == 0) ? 4 : count + 1;
660 schedule = xmalloc(sizeof(*schedule) * schedule_length);
661
662 if (count == 0) {
663 schedule[0].type = sched_signal;
664 schedule[0].value = signal_nr;
665 parse_schedule_item(schedule_str, &schedule[1]);
666 if (schedule[1].type != sched_timeout) {
667 badusage ("--retry takes timeout, or schedule list"
668 " of at least two items");
669 }
670 schedule[2].type = sched_signal;
671 schedule[2].value = SIGKILL;
672 schedule[3] = schedule[1];
673 } else {
674 count = 0;
675 repeatat = -1;
676 while (schedule_str != NULL) {
677 slash = strchr(schedule_str, '/');
678 str_len = slash ? (size_t)(slash - schedule_str) : strlen(schedule_str);
679 if (str_len >= sizeof(item_buf))
680 badusage("invalid schedule item: far too long"
681 " (you must delimit items with slashes)");
682 memcpy(item_buf, schedule_str, str_len);
683 item_buf[str_len] = '\0';
684 schedule_str = slash ? slash + 1 : NULL;
685
686 parse_schedule_item(item_buf, &schedule[count]);
687 if (schedule[count].type == sched_forever) {
688 if (repeatat >= 0)
689 badusage("invalid schedule: 'forever'"
690 " appears more than once");
691 repeatat = count;
692 continue;
693 }
694 count++;
695 }
696 if (repeatat == count)
697 badusage("invalid schedule: 'forever' appears last, "
698 "nothing to repeat");
699 if (repeatat >= 0) {
700 schedule[count].type = sched_goto;
701 schedule[count].value = repeatat;
702 count++;
703 }
704 assert(count == schedule_length);
705 }
706}
707
708static void
709set_action(enum action_code new_action)
710{
711 if (action == new_action)
712 return;
713
714 if (action != action_none)
715 badusage("only one command can be specified");
716
717 action = new_action;
718}
719
720static void
721parse_options(int argc, char * const *argv)
722{
723 static struct option longopts[] = {
724 { "help", 0, NULL, 'H'},
725 { "stop", 0, NULL, 'K'},
726 { "start", 0, NULL, 'S'},
727 { "status", 0, NULL, 'T'},
728 { "version", 0, NULL, 'V'},
729 { "startas", 1, NULL, 'a'},
730 { "name", 1, NULL, 'n'},
731 { "oknodo", 0, NULL, 'o'},
732 { "pidfile", 1, NULL, 'p'},
733 { "quiet", 0, NULL, 'q'},
734 { "signal", 1, NULL, 's'},
735 { "test", 0, NULL, 't'},
736 { "user", 1, NULL, 'u'},
737 { "group", 1, NULL, 'g'},
738 { "chroot", 1, NULL, 'r'},
739 { "verbose", 0, NULL, 'v'},
740 { "exec", 1, NULL, 'x'},
741 { "chuid", 1, NULL, 'c'},
742 { "nicelevel", 1, NULL, 'N'},
743 { "procsched", 1, NULL, 'P'},
744 { "iosched", 1, NULL, 'I'},
745 { "umask", 1, NULL, 'k'},
746 { "background", 0, NULL, 'b'},
747 { "no-close", 0, NULL, 'C'},
748 { "make-pidfile", 0, NULL, 'm'},
749 { "retry", 1, NULL, 'R'},
750 { "chdir", 1, NULL, 'd'},
751 { NULL, 0, NULL, 0 }
752 };
753 const char *umask_str = NULL;
754 const char *signal_str = NULL;
755 const char *schedule_str = NULL;
756 const char *proc_schedule_str = NULL;
757 const char *io_schedule_str = NULL;
758 int c;
759
760 for (;;) {
761 c = getopt_long(argc, argv,
762 "HKSVTa:n:op:qr:s:tu:vx:c:N:P:I:k:bCmR:g:d:",
763 longopts, NULL);
764 if (c == -1)
765 break;
766 switch (c) {
767 case 'H': /* --help */
768 usage();
769 exit(0);
770 case 'K': /* --stop */
771 set_action(action_stop);
772 break;
773 case 'S': /* --start */
774 set_action(action_start);
775 break;
776 case 'T': /* --status */
777 set_action(action_status);
778 break;
779 case 'V': /* --version */
780 do_version();
781 exit(0);
782 case 'a': /* --startas <pathname> */
783 startas = optarg;
784 break;
785 case 'n': /* --name <process-name> */
786 cmdname = optarg;
787 break;
788 case 'o': /* --oknodo */
789 exitnodo = 0;
790 break;
791 case 'p': /* --pidfile <pid-file> */
792 pidfile = optarg;
793 break;
794 case 'q': /* --quiet */
795 quietmode = 1;
796 break;
797 case 's': /* --signal <signal> */
798 signal_str = optarg;
799 break;
800 case 't': /* --test */
801 testmode = 1;
802 break;
803 case 'u': /* --user <username>|<uid> */
804 userspec = optarg;
805 break;
806 case 'v': /* --verbose */
807 quietmode = -1;
808 break;
809 case 'x': /* --exec <executable> */
810 execname = optarg;
811 break;
812 case 'c': /* --chuid <username>|<uid> */
813 /* We copy the string just in case we need the
814 * argument later. */
815 changeuser = xstrdup(optarg);
816 changeuser = strtok(changeuser, ":");
817 changegroup = strtok(NULL, ":");
818 break;
819 case 'g': /* --group <group>|<gid> */
820 changegroup = optarg;
821 break;
822 case 'r': /* --chroot /new/root */
823 changeroot = optarg;
824 break;
825 case 'N': /* --nice */
826 nicelevel = atoi(optarg);
827 break;
828 case 'P': /* --procsched */
829 proc_schedule_str = optarg;
830 break;
831 case 'I': /* --iosched */
832 io_schedule_str = optarg;
833 break;
834 case 'k': /* --umask <mask> */
835 umask_str = optarg;
836 break;
837 case 'b': /* --background */
838 background = 1;
839 break;
840 case 'C': /* --no-close */
841 close_io = 0;
842 break;
843 case 'm': /* --make-pidfile */
844 mpidfile = 1;
845 break;
846 case 'R': /* --retry <schedule>|<timeout> */
847 schedule_str = optarg;
848 break;
849 case 'd': /* --chdir /new/dir */
850 changedir = optarg;
851 break;
852 default:
853 /* Message printed by getopt. */
854 badusage(NULL);
855 }
856 }
857
858 if (signal_str != NULL) {
859 if (parse_signal(signal_str, &signal_nr) != 0)
860 badusage("signal value must be numeric or name"
861 " of signal (KILL, INT, ...)");
862 }
863
864 if (schedule_str != NULL) {
865 parse_schedule(schedule_str);
866 }
867
868 if (proc_schedule_str != NULL)
869 parse_proc_schedule(proc_schedule_str);
870
871 if (io_schedule_str != NULL)
872 parse_io_schedule(io_schedule_str);
873
874 if (umask_str != NULL) {
875 if (parse_umask(umask_str, &umask_value) != 0)
876 badusage("umask value must be a positive number");
877 }
878
879 if (action == action_none)
880 badusage("need one of --start or --stop or --status");
881
882 if (!execname && !pidfile && !userspec && !cmdname)
883 badusage("need at least one of --exec, --pidfile, --user or --name");
884
885 if (cmdname && strlen(cmdname) > PROCESS_NAME_SIZE)
886 warning("this system is not able to track process names\n"
887 "longer than %d characters, please use --exec "
888 "instead of --name.\n", PROCESS_NAME_SIZE);
889
890 if (!startas)
891 startas = execname;
892
893 if (action == action_start && !startas)
894 badusage("--start needs --exec or --startas");
895
896 if (mpidfile && pidfile == NULL)
897 badusage("--make-pidfile requires --pidfile");
898
899 if (background && action != action_start)
900 badusage("--background is only relevant with --start");
901
902 if (!close_io && !background)
903 badusage("--no-close is only relevant with --background");
904}
905
906static bool
907pid_is_exec(pid_t pid, const struct stat *esb)
908{
909 char lname[32];
910 char lcontents[_POSIX_PATH_MAX];
911 const char deleted[] = " (deleted)";
912 int nread;
913 struct stat sb;
914
915 sprintf(lname, "/proc/%d/exe", pid);
916 nread = readlink(lname, lcontents, sizeof(lcontents));
917 if (nread == -1)
918 return false;
919
920 lcontents[nread] = '\0';
921 if (strcmp(lcontents + nread - strlen(deleted), deleted) == 0)
922 lcontents[nread - strlen(deleted)] = '\0';
923
924 if (stat(lcontents, &sb) != 0)
925 return false;
926
927 return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino);
928}
929
930static bool
931pid_is_user(pid_t pid, uid_t uid)
932{
933 struct stat sb;
934 char buf[32];
935
936 sprintf(buf, "/proc/%d", pid);
937 if (stat(buf, &sb) != 0)
938 return false;
939 return (sb.st_uid == uid);
940}
941
942static bool
943pid_is_cmd(pid_t pid, const char *name)
944{
945 char buf[32];
946 FILE *f;
947 int c;
948
949 sprintf(buf, "/proc/%d/stat", pid);
950 f = fopen(buf, "r");
951 if (!f)
952 return false;
953 while ((c = getc(f)) != EOF && c != '(')
954 ;
955 if (c != '(') {
956 fclose(f);
957 return false;
958 }
959 /* This hopefully handles command names containing ‘)’. */
960 while ((c = getc(f)) != EOF && c == *name)
961 name++;
962 fclose(f);
963 return (c == ')' && *name == '\0');
964}
965
966static bool
967pid_is_running(pid_t pid)
968{
969 if (kill(pid, 0) == 0 || errno == EPERM)
970 return true;
971 else if (errno == ESRCH)
972 return false;
973 else
974 fatal("error checking pid %u status", pid);
975}
976
977static enum status_code
978pid_check(pid_t pid)
979{
980 if (execname && !pid_is_exec(pid, &exec_stat))
981 return status_dead;
982 if (userspec && !pid_is_user(pid, user_id))
983 return status_dead;
984 if (cmdname && !pid_is_cmd(pid, cmdname))
985 return status_dead;
986 if (action != action_stop && !pid_is_running(pid))
987 return status_dead;
988
989 pid_list_push(&found, pid);
990
991 return status_ok;
992}
993
994static enum status_code
995do_pidfile(const char *name)
996{
997 FILE *f;
998 static pid_t pid = 0;
999
1000 if (pid)
1001 return pid_check(pid);
1002
1003 f = fopen(name, "r");
1004 if (f) {
1005 enum status_code pid_status;
1006
1007 if (fscanf(f, "%d", &pid) == 1)
1008 pid_status = pid_check(pid);
1009 else
1010 pid_status = status_unknown;
1011 fclose(f);
1012
1013 if (pid_status == status_dead)
1014 return status_dead_pidfile;
1015 else
1016 return pid_status;
1017 } else if (errno == ENOENT)
1018 return status_dead;
1019 else
1020 fatal("unable to open pidfile %s", name);
1021}
1022
1023static enum status_code
1024do_procinit(void)
1025{
1026 DIR *procdir;
1027 struct dirent *entry;
1028 int foundany;
1029 pid_t pid;
1030 enum status_code prog_status = status_dead;
1031
1032 procdir = opendir("/proc");
1033 if (!procdir)
1034 fatal("unable to opendir /proc");
1035
1036 foundany = 0;
1037 while ((entry = readdir(procdir)) != NULL) {
1038 enum status_code pid_status;
1039
1040 if (sscanf(entry->d_name, "%d", &pid) != 1)
1041 continue;
1042 foundany++;
1043
1044 pid_status = pid_check(pid);
1045 if (pid_status < prog_status)
1046 prog_status = pid_status;
1047 }
1048 closedir(procdir);
1049 if (!foundany)
1050 fatal("nothing in /proc - not mounted?");
1051
1052 return prog_status;
1053}
1054
1055static enum status_code
1056do_findprocs(void)
1057{
1058 pid_list_free(&found);
1059
1060 if (pidfile)
1061 return do_pidfile(pidfile);
1062 else
1063 return do_procinit();
1064}
1065
1066static void
1067do_stop(int sig_num, int *n_killed, int *n_notkilled)
1068{
1069 struct pid_list *p;
1070
1071 do_findprocs();
1072
1073 *n_killed = 0;
1074 *n_notkilled = 0;
1075
1076 if (!found)
1077 return;
1078
1079 pid_list_free(&killed);
1080
1081 for (p = found; p; p = p->next) {
1082 if (testmode) {
1083 if (quietmode <= 0)
1084 printf("Would send signal %d to %d.\n",
1085 sig_num, p->pid);
1086 (*n_killed)++;
1087 } else if (kill(p->pid, sig_num) == 0) {
1088 pid_list_push(&killed, p->pid);
1089 (*n_killed)++;
1090 } else {
1091 if (sig_num)
1092 warning("failed to kill %d: %s\n",
1093 p->pid, strerror(errno));
1094 (*n_notkilled)++;
1095 }
1096 }
1097}
1098
1099static void
1100do_stop_summary(int retry_nr)
1101{
1102 struct pid_list *p;
1103
1104 if (quietmode >= 0 || !killed)
1105 return;
1106
1107 printf("Stopped %s (pid", what_stop);
1108 for (p = killed; p; p = p->next)
1109 printf(" %d", p->pid);
1110 putchar(')');
1111 if (retry_nr > 0)
1112 printf(", retry #%d", retry_nr);
1113 printf(".\n");
1114}
1115
1116static void
1117set_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/*
1124 * We want to keep polling for the processes, to see if they've exited, or
1125 * until the timeout expires.
1126 *
1127 * This is a somewhat complicated algorithm to try to ensure that we notice
1128 * reasonably quickly when all the processes have exited, but don't spend
1129 * too much CPU time polling. In particular, on a fast machine with
1130 * quick-exiting daemons we don't want to delay system shutdown too much,
1131 * whereas on a slow one, or where processes are taking some time to exit,
1132 * we want to increase the polling interval.
1133 *
1134 * The algorithm is as follows: we measure the elapsed time it takes to do
1135 * one poll(), and wait a multiple of this time for the next poll. However,
1136 * if that would put us past the end of the timeout period we wait only as
1137 * long as the timeout period, but in any case we always wait at least
1138 * MIN_POLL_INTERVAL (20ms). The multiple (‘ratio’) starts out as 2, and
1139 * increases by 1 for each poll to a maximum of 10; so we use up to between
1140 * 30% and 10% of the machine's resources (assuming a few reasonable things
1141 * about system performance).
1142 */
1143static bool
1144do_stop_timeout(int timeout, int *n_killed, int *n_notkilled)
1145{
1146 struct timeval stopat, before, after, interval, maxinterval;
1147 int r, ratio;
1148
1149 xgettimeofday(&stopat);
1150 stopat.tv_sec += timeout;
1151 ratio = 1;
1152 for (;;) {
1153 xgettimeofday(&before);
1154 if (timercmp(&before, &stopat, >))
1155 return false;
1156
1157 do_stop(0, n_killed, n_notkilled);
1158 if (!*n_killed)
1159 return true;
1160
1161 xgettimeofday(&after);
1162
1163 if (!timercmp(&after, &stopat, <))
1164 return false;
1165
1166 if (ratio < 10)
1167 ratio++;
1168
1169 timersub(&stopat, &after, &maxinterval);
1170 timersub(&after, &before, &interval);
1171 tmul(&interval, ratio);
1172
1173 if (interval.tv_sec < 0 || interval.tv_usec < 0)
1174 interval.tv_sec = interval.tv_usec = 0;
1175
1176 if (timercmp(&interval, &maxinterval, >))
1177 interval = maxinterval;
1178
1179 if (interval.tv_sec == 0 &&
1180 interval.tv_usec <= MIN_POLL_INTERVAL)
1181 interval.tv_usec = MIN_POLL_INTERVAL;
1182
1183 r = select(0, NULL, NULL, NULL, &interval);
1184 if (r < 0 && errno != EINTR)
1185 fatal("select() failed for pause");
1186 }
1187}
1188
1189static int
1190finish_stop_schedule(bool anykilled)
1191{
1192 if (anykilled)
1193 return 0;
1194
1195 if (quietmode <= 0)
1196 printf("No %s found running; none killed.\n", what_stop);
1197
1198 return exitnodo;
1199}
1200
1201static int
1202run_stop_schedule(void)
1203{
1204 int position, n_killed, n_notkilled, value, retry_nr;
1205 bool anykilled;
1206
1207 if (testmode) {
1208 if (schedule != NULL) {
1209 if (quietmode <= 0)
1210 printf("Ignoring --retry in test mode\n");
1211 schedule = NULL;
1212 }
1213 }
1214
1215 if (cmdname)
1216 set_what_stop(cmdname);
1217 else if (execname)
1218 set_what_stop(execname);
1219 else if (pidfile)
1220 sprintf(what_stop, "process in pidfile '%.200s'", pidfile);
1221 else if (userspec)
1222 sprintf(what_stop, "process(es) owned by '%.200s'", userspec);
1223 else
1224 fatal("internal error, no match option, please report");
1225
1226 anykilled = false;
1227 retry_nr = 0;
1228
1229 if (schedule == NULL) {
1230 do_stop(signal_nr, &n_killed, &n_notkilled);
1231 do_stop_summary(0);
1232 if (n_notkilled > 0 && quietmode <= 0)
1233 printf("%d pids were not killed\n", n_notkilled);
1234 if (n_killed)
1235 anykilled = true;
1236 return finish_stop_schedule(anykilled);
1237 }
1238
1239 for (position = 0; position < schedule_length; position++) {
1240 reposition:
1241 value = schedule[position].value;
1242 n_notkilled = 0;
1243
1244 switch (schedule[position].type) {
1245 case sched_goto:
1246 position = value;
1247 goto reposition;
1248 case sched_signal:
1249 do_stop(value, &n_killed, &n_notkilled);
1250 do_stop_summary(retry_nr++);
1251 if (!n_killed)
1252 return finish_stop_schedule(anykilled);
1253 else
1254 anykilled = true;
1255 continue;
1256 case sched_timeout:
1257 if (do_stop_timeout(value, &n_killed, &n_notkilled))
1258 return finish_stop_schedule(anykilled);
1259 else
1260 continue;
1261 default:
1262 assert(!"schedule[].type value must be valid");
1263 }
1264 }
1265
1266 if (quietmode <= 0)
1267 printf("Program %s, %d process(es), refused to die.\n",
1268 what_stop, n_killed);
1269
1270 return 2;
1271}
1272
1273int
1274main(int argc, char **argv)
1275{
1276 enum status_code prog_status;
1277 int devnull_fd = -1;
1278 gid_t rgid;
1279 uid_t ruid;
1280 progname = argv[0];
1281
1282 parse_options(argc, argv);
1283 argc -= optind;
1284 argv += optind;
1285
1286 if (execname) {
1287 char *fullexecname;
1288
1289 /* If it's a relative path, normalize it. */
1290 if (execname[0] != '/')
1291 execname = newpath(changedir, execname);
1292
1293 if (changeroot)
1294 fullexecname = newpath(changeroot, execname);
1295 else
1296 fullexecname = execname;
1297
1298 if (stat(fullexecname, &exec_stat))
1299 fatal("unable to stat %s", fullexecname);
1300
1301 if (fullexecname != execname)
1302 free(fullexecname);
1303 }
1304
1305 if (userspec && sscanf(userspec, "%d", &user_id) != 1) {
1306 struct passwd *pw;
1307
1308 pw = getpwnam(userspec);
1309 if (!pw)
1310 fatal("user '%s' not found", userspec);
1311
1312 user_id = pw->pw_uid;
1313 }
1314
1315 if (changegroup && sscanf(changegroup, "%d", &runas_gid) != 1) {
1316 struct group *gr = getgrnam(changegroup);
1317 if (!gr)
1318 fatal("group '%s' not found", changegroup);
1319 changegroup = gr->gr_name;
1320 runas_gid = gr->gr_gid;
1321 }
1322 if (changeuser) {
1323 struct passwd *pw;
1324 struct stat st;
1325
1326 if (sscanf(changeuser, "%d", &runas_uid) == 1)
1327 pw = getpwuid(runas_uid);
1328 else
1329 pw = getpwnam(changeuser);
1330 if (!pw)
1331 fatal("user '%s' not found", changeuser);
1332 changeuser = pw->pw_name;
1333 runas_uid = pw->pw_uid;
1334 if (changegroup == NULL) {
1335 /* Pass the default group of this user. */
1336 changegroup = ""; /* Just empty. */
1337 runas_gid = pw->pw_gid;
1338 }
1339 if (stat(pw->pw_dir, &st) == 0)
1340 setenv("HOME", pw->pw_dir, 1);
1341 }
1342
1343 if (action == action_stop) {
1344 int i = run_stop_schedule();
1345 exit(i);
1346 }
1347
1348 prog_status = do_findprocs();
1349
1350 if (action == action_status)
1351 exit(prog_status);
1352
1353 if (found) {
1354 if (quietmode <= 0)
1355 printf("%s already running.\n", execname ? execname : "process");
1356 exit(exitnodo);
1357 }
1358 if (testmode && quietmode <= 0) {
1359 printf("Would start %s ", startas);
1360 while (argc-- > 0)
1361 printf("%s ", *argv++);
1362 if (changeuser != NULL) {
1363 printf(" (as user %s[%d]", changeuser, runas_uid);
1364 if (changegroup != NULL)
1365 printf(", and group %s[%d])", changegroup, runas_gid);
1366 else
1367 printf(")");
1368 }
1369 if (changeroot != NULL)
1370 printf(" in directory %s", changeroot);
1371 if (nicelevel)
1372 printf(", and add %i to the priority", nicelevel);
1373 if (proc_sched)
1374 printf(", with scheduling policy %s with priority %i",
1375 proc_sched->policy_name, proc_sched->priority);
1376 if (io_sched)
1377 printf(", with IO scheduling class %s with priority %i",
1378 io_sched->policy_name, io_sched->priority);
1379 printf(".\n");
1380 }
1381 if (testmode)
1382 exit(0);
1383 if (quietmode < 0)
1384 printf("Starting %s...\n", startas);
1385 *--argv = startas;
1386 if (background)
1387 /* Ok, we need to detach this process. */
1388 daemonize();
1389 if (background && close_io) {
1390 devnull_fd = open("/dev/null", O_RDWR);
1391 if (devnull_fd < 0)
1392 fatal("unable to open '%s'", "/dev/null");
1393 }
1394 if (nicelevel) {
1395 errno = 0;
1396 if ((nice(nicelevel) == -1) && (errno != 0))
1397 fatal("unable to alter nice level by %i", nicelevel);
1398 }
1399 if (proc_sched)
1400 set_proc_schedule(proc_sched);
1401 if (io_sched)
1402 set_io_schedule(io_sched);
1403 if (umask_value >= 0)
1404 umask(umask_value);
1405 if (mpidfile && pidfile != NULL)
1406 /* User wants _us_ to make the pidfile. */
1407 write_pidfile(pidfile, getpid());
1408 if (changeroot != NULL) {
1409 if (chdir(changeroot) < 0)
1410 fatal("unable to chdir() to %s", changeroot);
1411 if (chroot(changeroot) < 0)
1412 fatal("unable to chroot() to %s", changeroot);
1413 }
1414 if (chdir(changedir) < 0)
1415 fatal("unable to chdir() to %s", changedir);
1416
1417 rgid = getgid();
1418 ruid = getuid();
1419 if (changegroup != NULL) {
1420 if (rgid != (gid_t)runas_gid)
1421 if (setgid(runas_gid))
1422 fatal("unable to set gid to %d", runas_gid);
1423 }
1424 if (changeuser != NULL) {
1425 /* We assume that if our real user and group are the same as
1426 * the ones we should switch to, the supplementary groups
1427 * will be already in place. */
1428 if (rgid != (gid_t)runas_gid || ruid != (uid_t)runas_uid)
1429 if (initgroups(changeuser, runas_gid))
1430 fatal("unable to set initgroups() with gid %d",
1431 runas_gid);
1432
1433 if (ruid != (uid_t)runas_uid)
1434 if (setuid(runas_uid))
1435 fatal("unable to set uid to %s", changeuser);
1436 }
1437
1438 /* Set a default umask for dumb programs. */
1439 if (background && umask_value < 0)
1440 umask(022);
1441
1442 if (background && close_io) {
1443 int i;
1444
1445 dup2(devnull_fd, 0); /* stdin */
1446 dup2(devnull_fd, 1); /* stdout */
1447 dup2(devnull_fd, 2); /* stderr */
1448
1449 /* Now close all extra fds. */
1450 for (i = get_open_fd_max() - 1; i >= 3; --i)
1451 close(i);
1452 }
1453 execv(startas, argv);
1454 fatal("unable to start %s", startas);
1455}
Note: See TracBrowser for help on using the repository browser.