diff options
Diffstat (limited to 'init')
-rw-r--r-- | init/start_stop_daemon.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/init/start_stop_daemon.c b/init/start_stop_daemon.c new file mode 100644 index 0000000..0152283 --- /dev/null +++ b/init/start_stop_daemon.c @@ -0,0 +1,271 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini start-stop-daemon implementation(s) for busybox + * + * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>, + * public domain. + * Adapted for busybox David Kimdon <dwhedon@gordian.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <signal.h> +#include <errno.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <pwd.h> + +#include "busybox.h" + +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 *progname = ""; + +struct pid_list { + struct pid_list *next; + int pid; +}; + +static struct pid_list *found = NULL; +static struct pid_list *killed = NULL; + +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 +parse_options(int argc, char * const *argv) +{ + + int c; + + for (;;) { + c = getopt (argc, argv, "a:n:s:u:x:KS"); + if (c == EOF) + break; + switch (c) { + case 'K': + stop = 1; + break; + case 'S': + start = 1; + break; + case 'a': + startas = optarg; + break; + case 'n': + cmdname = optarg; + break; + case 's': + if (sscanf(optarg, "%d", &signal_nr) != 1) + error_msg_and_die ("-s takes a numeric argument"); + break; + case 'u': + userspec = optarg; + break; + case 'x': + execname = optarg; + break; + default: + show_usage(); + exit(1); + } + } + + if (start == stop) + error_msg_and_die ("need one of -S or -K"); + + if (!execname && !userspec) + error_msg_and_die ("need at least one of -x or -u"); + + if (!startas) + startas = execname; + + if (start && !startas) + error_msg_and_die ("-S needs -x or -a"); +} + + +static int +pid_is_exec(int pid, const char *exec) +{ + char buf[PATH_MAX]; + FILE *fp; + + sprintf(buf, "/proc/%d/cmdline", pid); + fp = fopen(buf, "r"); + if (fp && fgets (buf, sizeof (buf), fp) ) { + if (strncmp (buf, exec, strlen(exec)) == 0) + return 1; + } + return 0; +} + + +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, execname)) { + return; + } + if (userspec && !pid_is_user(pid, user_id)) { + return; + } + if (cmdname && !pid_is_cmd(pid, cmdname)) { + return; + } + push(&found, pid); +} + + + +static void +do_procfs(void) +{ + DIR *procdir; + struct dirent *entry; + int foundany, pid; + + procdir = opendir("/proc"); + if (!procdir) + perror_msg_and_die ("opendir /proc"); + + foundany = 0; + while ((entry = readdir(procdir)) != NULL) { + if (sscanf(entry->d_name, "%d", &pid) != 1) + continue; + foundany++; + check(pid); + } + closedir(procdir); + if (!foundany) + error_msg_and_die ("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 (userspec) + sprintf(what, "process(es) owned by `%s'", userspec); + else + error_msg_and_die ("internal error, please report"); + + if (!found) { + printf("no %s found; none killed.\n", what); + exit(0); + } + for (p = found; p; p = p->next) { + 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 (killed) { + printf("stopped %s (pid", what); + for (p = killed; p; p = p->next) + printf(" %d", p->pid); + printf(").\n"); + } +} + + +int +start_stop_daemon_main(int argc, char **argv) +{ + progname = argv[0]; + + parse_options(argc, argv); + argc -= optind; + argv += optind; + + if (userspec && sscanf(userspec, "%d", &user_id) != 1) { + struct passwd *pw; + + pw = getpwnam(userspec); + if (!pw) + error_msg_and_die ("user `%s' not found\n", userspec); + + user_id = pw->pw_uid; + } + + do_procfs(); + + if (stop) { + do_stop(); + exit(0); + } + + if (found) { + printf("%s already running.\n", execname); + printf("%d\n",found->pid); + exit(0); + } + *--argv = startas; + execv(startas, argv); + perror_msg_and_die ("unable to start %s", startas); +} + |