/* * 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 "config.h" #if defined(linux) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) # define OSLinux #elif defined(__GNU__) # define OSHURD #elif defined(__sparc__) # 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 /*us*/ #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 HAVE_SYS_CDEFS_H #include #endif #ifdef HAVE_STDDEF_H #include #endif #ifdef HAVE_ERROR_H # include #endif #if HAVE_C_ATTRIBUTE # define CONSTANT __attribute__((constant)) # define PRINTFFORMAT(si, tc) __attribute__((format(printf,si,tc))) # define NONRETURNING __attribute__((noreturn)) # define UNUSED __attribute__((unused)) # define NONRETURNPRINTFFORMAT(si, tc) __attribute__((format(printf,si,tc),noreturn)) #else # define CONSTANT # define PRINTFFORMAT(si, tc) # define NONRETURNING # define UNUSED # define NONRETURNPRINTFFORMAT(si, tc) #endif 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 = 15; 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; 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; struct schedule_item { enum { sched_timeout, sched_signal, sched_goto, sched_forever } type; int value; /* seconds, signal no., or index into array */ /* sched_forever is only seen within parse_schedule and callees */ }; 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 #ifdef __GNUC__ static void fatal(const char *format, ...) NONRETURNPRINTFFORMAT(1, 2); static void badusage(const char *msg) NONRETURNING; #else static void fatal(const char *format, ...); static void badusage(const char *msg); #endif /* This next part serves only to construct the TVCALC macro, which * is used for doing arithmetic on struct timeval's. It works like this: * TVCALC(result, expression); * where result is a struct timeval (and must be an lvalue) and * expression is the single expression for both components. In this * expression you can use the special values TVELEM, which when fed a * const struct timeval* gives you the relevant component, and * TVADJUST. TVADJUST is necessary when subtracting timevals, to make * it easier to renormalise. Whenver you subtract timeval elements, * you must make sure that TVADJUST is added to the result of the * subtraction (before any resulting multiplication or what have you). * TVELEM must be linear in TVADJUST. */ typedef long tvselector(const struct timeval*); static long tvselector_sec(const struct timeval *tv) { return tv->tv_sec; } static long tvselector_usec(const struct timeval *tv) { return tv->tv_usec; } #define TVCALC_ELEM(result, expr, sec, adj) \ { \ const long TVADJUST = adj; \ long (*const TVELEM)(const struct timeval*) = tvselector_##sec; \ (result).tv_##sec = (expr); \ } #define TVCALC(result,expr) \ do { \ TVCALC_ELEM(result, expr, sec, (-1)); \ TVCALC_ELEM(result, expr, usec, (+1000000)); \ (result).tv_sec += (result).tv_usec / 1000000; \ (result).tv_usec %= 1000000; \ } while(0) 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 void xgettimeofday(struct timeval *tv) { if (gettimeofday(tv,0) != 0) fatal("gettimeofday failed: %s", strerror(errno)); } 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( "start-stop-daemon %s for Debian - small and fast C version written by\n" "Marek Michalkiewicz , public domain.\n" "\n" "Usage: start-stop-daemon [