/* * A rewrite of the original Debian's start-stop-daemon Perl script * in C (faster - it is executed many times during system startup). * * Written by Marek Michalkiewicz , * public domain. Based conceptually on start-stop-daemon.pl, by Ian * Jackson . May be used and distributed * freely for any purpose. Changes by Christian Schwarz * , to make output conform to the Debian * Console Message Standard, also placed in public domain. Minor * changes by Klee Dienes , also placed in the Public * Domain. * * Changes by Ben Collins , added --chuid, --background * and --make-pidfile options, placed in public domain aswell. * * Port to OpenBSD by Sontri Tomo Huynh * and Andreas Schuldei * * Changes by Ian Jackson: added --retry (and associated rearrangements). */ #include #include #if defined(linux) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) # define OSLinux #elif defined(__GNU__) # define OSHURD #elif defined(__sun) # define OSsunos #elif defined(OPENBSD) || defined(__OpenBSD__) # define OSOpenBSD #elif defined(hpux) # define OShpux #elif defined(__FreeBSD__) # define OSFreeBSD #elif defined(__NetBSD__) # define OSNetBSD #else # error Unknown architecture - cannot build start-stop-daemon #endif #define MIN_POLL_INTERVAL 20000 /* µs */ #if defined(OSHURD) #include #include #endif #if defined(OSOpenBSD) || defined(OSFreeBSD) || defined(OSNetBSD) #include #include #include #include #include #include #endif #ifdef HAVE_KVM_H #include #include #include #endif #if defined(OShpux) #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _POSIX_PRIORITY_SCHEDULING #include #else #define SCHED_OTHER -1 #define SCHED_FIFO -1 #define SCHED_RR -1 #endif #ifdef HAVE_SYS_CDEFS_H #include #endif #ifdef HAVE_STDDEF_H #include #endif #ifdef HAVE_ERROR_H #include #endif #ifdef HAVE_SYS_SYSCALL_H #include #endif #if defined(SYS_ioprio_set) && defined(linux) #define HAVE_IOPRIO_SET #endif enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP, IOPRIO_WHO_USER, }; enum { IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE, }; static int testmode = 0; static int quietmode = 0; static int exitnodo = 1; static int start = 0; static int stop = 0; static int background = 0; static int mpidfile = 0; static int signal_nr = SIGTERM; static int user_id = -1; static int runas_uid = -1; static int runas_gid = -1; static const char *userspec = NULL; static char *changeuser = NULL; static const char *changegroup = NULL; static char *changeroot = NULL; static const char *changedir = "/"; static const char *cmdname = NULL; static char *execname = NULL; static char *startas = NULL; static const char *pidfile = NULL; static char what_stop[1024]; static const char *progname = ""; static int nicelevel = 0; static int umask_value = -1; #define IOPRIO_CLASS_SHIFT 13 #define IOPRIO_PRIO_VALUE(class, prio) (((class) << IOPRIO_CLASS_SHIFT) | (prio)) #define IO_SCHED_PRIO_MIN 0 #define IO_SCHED_PRIO_MAX 7 static struct stat exec_stat; #if defined(OSHURD) static struct proc_stat_list *procset = NULL; #endif struct pid_list { struct pid_list *next; pid_t pid; }; static struct pid_list *found = NULL; static struct pid_list *killed = NULL; /* Resource scheduling policy. */ struct res_schedule { const char *policy_name; int policy; int priority; }; struct schedule_item { enum { sched_timeout, sched_signal, sched_goto, sched_forever /* Only seen within parse_schedule and callees */ } type; int value; /* Seconds, signal no., or index into array */ }; static struct res_schedule *proc_sched = NULL; static struct res_schedule *io_sched = NULL; static int schedule_length; static struct schedule_item *schedule = NULL; static void *xmalloc(int size); static void push(struct pid_list **list, pid_t pid); static void do_help(void); static void parse_options(int argc, char * const *argv); static int pid_is_user(pid_t pid, uid_t uid); static int pid_is_cmd(pid_t pid, const char *name); static void check(pid_t pid); static void do_pidfile(const char *name); static void do_stop(int signal_nr, int quietmode, int *n_killed, int *n_notkilled, int retry_nr); #if defined(OSLinux) || defined(OShpux) static int pid_is_exec(pid_t pid, const struct stat *esb); #endif static void fatal(const char *format, ...) NONRETURNING PRINTFFORMAT(1, 2); static void badusage(const char *msg) NONRETURNING; static void fatal(const char *format, ...) { va_list arglist; fprintf(stderr, "%s: ", progname); va_start(arglist, format); vfprintf(stderr, format, arglist); va_end(arglist); fprintf(stderr, " (%s)\n", strerror (errno)); exit(2); } static void * xmalloc(int size) { void *ptr; ptr = malloc(size); if (ptr) return ptr; fatal("malloc(%d) failed", size); } static char * xstrdup(const char *str) { char *new_str; new_str = strdup(str); if (new_str) return new_str; fatal("strdup(%s) failed", str); } static void xgettimeofday(struct timeval *tv) { if (gettimeofday(tv, NULL) != 0) fatal("gettimeofday failed: %s", strerror(errno)); } static void tmul(struct timeval *a, int b) { a->tv_sec *= b; a->tv_usec *= b; a->tv_sec = a->tv_sec + a->tv_usec / 1000000; a->tv_usec %= 1000000; } static long get_open_fd_max(void) { #ifdef HAVE_GETDTABLESIZE return getdtablesize(); #else return sysconf(_SC_OPEN_MAX); #endif } static void daemonize(void) { pid_t pid; if (quietmode < 0) printf("Detaching to start %s...", startas); pid = fork(); if (pid < 0) fatal("Unable to do first fork.\n"); else if (pid) /* Parent */ _exit(0); /* Create a new session */ #ifdef HAVE_SETSID setsid(); #else setpgid(0, 0); #endif pid = fork(); if (pid < 0) fatal("Unable to do second fork.\n"); else if (pid) /* Parent */ _exit(0); if (quietmode < 0) printf("done.\n"); } static void push(struct pid_list **list, pid_t pid) { struct pid_list *p; p = xmalloc(sizeof(*p)); p->next = *list; p->pid = pid; *list = p; } static void clear(struct pid_list **list) { struct pid_list *here, *next; for (here = *list; here != NULL; here = next) { next = here->next; free(here); } *list = NULL; } static void do_help(void) { printf( "Usage: start-stop-daemon [