/* * 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. */ #include #include #include #include #include #include #include #include #include #include #include #define VERSION "version 0.3, 1996-06-05" static int testmode = 0; static int quietmode = 0; static int exitnodo = 1; static int start = 0; static int stop = 0; static int signal_nr = 15; static int user_id = -1; static const char *userspec = NULL; static const char *cmdname = NULL; static char *execname = NULL; static char *startas = NULL; static const char *pidfile = NULL; static const char *progname = ""; static struct stat exec_stat; struct pid_list { struct pid_list *next; int pid; }; static struct pid_list *found = NULL; static struct pid_list *killed = NULL; static void *xmalloc(int size); static void push(struct pid_list **list, int pid); static void do_help(void); static void parse_options(int argc, char * const *argv); static int pid_is_exec(int pid, const struct stat *esb); static int pid_is_user(int pid, int uid); static int pid_is_cmd(int pid, const char *name); static void check(int pid); static void do_pidfile(const char *name); static void do_procfs(void); static void do_stop(void); #ifdef __GNUC__ static void fatal(const char *format, ...) __attribute__((noreturn, format(printf, 1, 2))); static void badusage(const char *msg) __attribute__((noreturn)); #else static void fatal(const char *format, ...); static void badusage(const char *msg); #endif static void fatal(const char *format, ...) { va_list arglist; fprintf(stderr, "%s: ", progname); va_start(arglist, format); vfprintf(stderr, format, arglist); va_end(arglist); putc('\n', stderr); exit(2); } static void * xmalloc(int size) { void *ptr; ptr = malloc(size); if (ptr) return ptr; fatal("malloc(%d) failed", size); } static void push(struct pid_list **list, int pid) { struct pid_list *p; p = xmalloc(sizeof(*p)); p->next = *list; p->pid = pid; *list = p; } static void do_help(void) { printf("start-stop-daemon for Debian Linux - small and fast C version written by\n" "Marek Michalkiewicz , public domain. %s\n" "\n" "Usage:\n" " start-stop-daemon -S|--start options ... -- arguments ...\n" " start-stop-daemon -K|--stop options ...\n" " start-stop-daemon -H|--help\n" " start-stop-daemon -V|--version\n" "\n" "Options (at least one of --exec|--pidfile|--user is required):\n" " -x|--exec program to start/check if it is running\n" " -p|--pidfile pid file to check\n" " -u|--user | stop this user's processes\n" " -n|--name stop processes with this name\n" " -s|--signal signal to send (default 15)\n" " -a|--startas program to start (default )\n" " -t|--test test mode, don't do anything\n" " -o|--oknodo exit status 0 (not 1) if nothing done\n" " -q|--quiet | -v, --verbose\n" "\n" "Exit status: 0 = done 1 = nothing done (=> 0 if --oknodo) 2 = trouble\n", VERSION); } static void badusage(const char *msg) { if (msg && *msg) fprintf(stderr, "%s: %s\n", progname, msg); fprintf(stderr, "Try `%s --help' for more information.\n", progname); exit(2); } static void parse_options(int argc, char * const *argv) { static struct option longopts[] = { { "help", 0, NULL, 'H'}, { "stop", 0, NULL, 'K'}, { "start", 0, NULL, 'S'}, { "version", 0, NULL, 'V'}, { "startas", 1, NULL, 'a'}, { "name", 1, NULL, 'n'}, { "oknodo", 0, NULL, 'o'}, { "pidfile", 1, NULL, 'p'}, { "quiet", 0, NULL, 'q'}, { "signal", 1, NULL, 's'}, { "test", 0, NULL, 't'}, { "user", 1, NULL, 'u'}, { "verbose", 0, NULL, 'v'}, { "exec", 1, NULL, 'x'}, { NULL, 0, NULL, 0} }; int c; for (;;) { c = getopt_long(argc, argv, "HKSVa:n:op:qs:tu:vx:", longopts, (int *) 0); if (c == -1) break; switch (c) { case 'H': /* --help */ do_help(); exit(0); case 'K': /* --stop */ stop = 1; break; case 'S': /* --start */ start = 1; break; case 'V': /* --version */ printf("start-stop-daemon " VERSION "\n"); exit(0); case 'a': /* --startas */ startas = optarg; break; case 'n': /* --name */ cmdname = optarg; break; case 'o': /* --oknodo */ exitnodo = 0; break; case 'p': /* --pidfile */ pidfile = optarg; break; case 'q': /* --quiet */ quietmode = 1; break; case 's': /* --signal */ if (sscanf(optarg, "%d", &signal_nr) != 1) badusage("--signal takes a numeric argument"); break; case 't': /* --test */ testmode = 1; break; case 'u': /* --user | */ userspec = optarg; break; case 'v': /* --verbose */ quietmode = -1; break; case 'x': /* --exec */ execname = optarg; break; default: badusage(""); /* message printed by getopt */ } } if (start == stop) badusage("need one of --start or --stop"); if (!execname && !pidfile && !userspec) badusage("need at least one of --exec, --pidfile or --user"); if (!startas) startas = execname; if (start && !startas) badusage("--start needs --exec or --startas"); } static int pid_is_exec(int pid, const struct stat *esb) { struct stat sb; char buf[32]; sprintf(buf, "/proc/%d/exe", pid); if (stat(buf, &sb) != 0) return 0; return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); } static int pid_is_user(int pid, int uid) { struct stat sb; char buf[32]; sprintf(buf, "/proc/%d", pid); if (stat(buf, &sb) != 0) return 0; return (sb.st_uid == uid); } static int pid_is_cmd(int pid, const char *name) { char buf[32]; FILE *f; int c; sprintf(buf, "/proc/%d/stat", pid); f = fopen(buf, "r"); if (!f) return 0; while ((c = getc(f)) != EOF && c != '(') ; if (c != '(') { fclose(f); return 0; } /* this hopefully handles command names containing ')' */ while ((c = getc(f)) != EOF && c == *name) name++; fclose(f); return (c == ')' && *name == '\0'); } static void check(int pid) { if (execname && !pid_is_exec(pid, &exec_stat)) return; if (userspec && !pid_is_user(pid, user_id)) return; if (cmdname && !pid_is_cmd(pid, cmdname)) return; push(&found, pid); } static void do_pidfile(const char *name) { FILE *f; int pid; f = fopen(name, "r"); if (f) { if (fscanf(f, "%d", &pid) == 1) check(pid); fclose(f); } } static void do_procfs(void) { DIR *procdir; struct dirent *entry; int foundany, pid; procdir = opendir("/proc"); if (!procdir) fatal("opendir /proc: %s", strerror(errno)); foundany = 0; while ((entry = readdir(procdir)) != NULL) { if (sscanf(entry->d_name, "%d", &pid) != 1) continue; foundany++; check(pid); } closedir(procdir); if (!foundany) fatal("nothing in /proc - not mounted?"); } static void do_stop(void) { char what[1024]; struct pid_list *p; if (cmdname) strcpy(what, cmdname); else if (execname) strcpy(what, execname); else if (pidfile) sprintf(what, "process in pidfile `%s'", pidfile); else if (userspec) sprintf(what, "process(es) owned by `%s'", userspec); else fatal("internal error, please report"); if (!found) { if (quietmode <= 0) printf("no %s found; none killed.\n", what); exit(exitnodo); } for (p = found; p; p = p->next) { if (testmode) printf("would send signal %d to %d.\n", signal_nr, p->pid); else if (kill(p->pid, signal_nr) == 0) push(&killed, p->pid); else printf("%s: warning: failed to kill %d: %s\n", progname, p->pid, strerror(errno)); } if (quietmode < 0 && killed) { printf("stopped %s (pid", what); for (p = killed; p; p = p->next) printf(" %d", p->pid); printf(").\n"); } } int main(int argc, char **argv) { progname = argv[0]; parse_options(argc, argv); argc -= optind; argv += optind; if (execname && stat(execname, &exec_stat)) fatal("stat %s: %s", execname, strerror(errno)); if (userspec && sscanf(userspec, "%d", &user_id) != 1) { struct passwd *pw; pw = getpwnam(userspec); if (!pw) fatal("user `%s' not found\n", userspec); user_id = pw->pw_uid; } if (pidfile) do_pidfile(pidfile); else do_procfs(); if (stop) { do_stop(); exit(0); } if (found) { if (quietmode <= 0) printf("%s already running.\n", execname); exit(exitnodo); } if (testmode) { printf("would start %s ", startas); while (argc-- > 0) printf("%s ", *argv++); printf(".\n"); exit(0); } if (quietmode < 0) printf("starting %s ...\n", startas); *--argv = startas; execv(startas, argv); fatal("unable to start %s: %s", startas, strerror(errno)); }