/* * 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 #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 */ #ifdef HAVE_SYS_CDEFS_H #include #endif #ifdef HAVE_SYS_SYSCALL_H #include #endif #if defined(OSHurd) #include #include #endif #if defined(OSOpenBSD) || defined(OSFreeBSD) || defined(OSNetBSD) #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 #ifdef HAVE_STDDEF_H #include #endif #include #include #include #include #include #ifdef HAVE_ERROR_H #include #endif #ifdef _POSIX_PRIORITY_SCHEDULING #include #else #define SCHED_OTHER -1 #define SCHED_FIFO -1 #define SCHED_RR -1 #endif #if defined(OSLinux) /* This comes from TASK_COMM_LEN defined in Linux's include/linux/sched.h. */ #define PROCESS_NAME_SIZE 15 #elif defined(OSsunos) #define PROCESS_NAME_SIZE 15 #elif defined(OSDarwin) #define PROCESS_NAME_SIZE 16 #elif defined(OSNetBSD) #define PROCESS_NAME_SIZE 16 #elif defined(OSOpenBSD) #define PROCESS_NAME_SIZE 16 #elif defined(OSFreeBSD) #define PROCESS_NAME_SIZE 19 #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, }; enum action_code { action_none, action_start, action_stop, action_status, }; static enum action_code action; static int testmode = 0; static int quietmode = 0; static int exitnodo = 1; 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 /* LSB Init Script process status exit codes. */ enum status_code { status_ok = 0, status_dead_pidfile = 1, status_dead_lockfile = 2, status_dead = 3, status_unknown = 4, }; 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, /* Only seen within parse_schedule and callees. */ sched_forever, } type; /* Seconds, signal no., or index into array. */ int value; }; 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 DPKG_ATTR_PRINTF(1) warning(const char *format, ...) { va_list arglist; fprintf(stderr, "%s: warning: ", progname); va_start(arglist, format); vfprintf(stderr, format, arglist); va_end(arglist); } static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1) fatal(const char *format, ...) { va_list arglist; int errno_fatal = errno; fprintf(stderr, "%s: ", progname); va_start(arglist, format); vfprintf(stderr, format, arglist); va_end(arglist); if (errno_fatal) fprintf(stderr, " (%s)\n", strerror(errno_fatal)); else fprintf(stderr, "\n"); if (action == action_status) exit(status_unknown); else 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"); } 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 } #ifndef HAVE_SETSID static void detach_controlling_tty(void) { #ifdef HAVE_TIOCNOTTY int tty_fd; tty_fd = open("/dev/tty", O_RDWR); /* The current process does not have a controlling tty. */ if (tty_fd < 0) return; if (ioctl(tty_fd, TIOCNOTTY, 0) != 0) fatal("unable to detach controlling tty"); close(tty_fd); #endif } #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"); else if (pid) /* Parent. */ _exit(0); /* Create a new session. */ #ifdef HAVE_SETSID setsid(); #else setpgid(0, 0); detach_controlling_tty(); #endif pid = fork(); if (pid < 0) fatal("unable to do second fork"); else if (pid) /* Parent. */ _exit(0); if (quietmode < 0) printf("done.\n"); } static void pid_list_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 pid_list_free(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 usage(void) { printf( "Usage: start-stop-daemon [