diff options
author | Glenn L McGrath <bug1@ihug.co.nz> | 2003-05-11 14:52:39 +0000 |
---|---|---|
committer | Glenn L McGrath <bug1@ihug.co.nz> | 2003-05-11 14:52:39 +0000 |
commit | 1e11c34be4decfef8fbda8a8e01cd60def8232e5 (patch) | |
tree | 6e93956fef2bcd4c1db18031dc081dcaf689f2d1 | |
parent | 8c6887c855460ee9e688e2a51e29f99faa2a2d8c (diff) | |
download | busybox-1e11c34be4decfef8fbda8a8e01cd60def8232e5.tar.gz |
minit, a Minimal init system.
-rw-r--r-- | include/applets.h | 9 | ||||
-rw-r--r-- | include/usage.h | 29 | ||||
-rw-r--r-- | init/Config.in | 20 | ||||
-rw-r--r-- | init/Makefile.in | 3 | ||||
-rw-r--r-- | init/minit.c | 612 | ||||
-rw-r--r-- | init/msvc.c | 300 | ||||
-rw-r--r-- | init/pidfilehack.c | 78 |
7 files changed, 1051 insertions, 0 deletions
diff --git a/include/applets.h b/include/applets.h index 668c84914..6697be5ee 100644 --- a/include/applets.h +++ b/include/applets.h @@ -358,6 +358,9 @@ #ifdef CONFIG_MESG APPLET(mesg, mesg_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) #endif +#ifdef CONFIG_MINIT + APPLET(minit, minit_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif #ifdef CONFIG_MKDIR APPLET(mkdir, mkdir_main, _BB_DIR_BIN, _BB_SUID_NEVER) #endif @@ -388,6 +391,9 @@ #ifdef CONFIG_MSH APPLET_NOUSAGE("msh", msh_main, _BB_DIR_BIN, _BB_SUID_NEVER) #endif +#ifdef CONFIG_MSVC + APPLET(msvc, msvc_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif #ifdef CONFIG_MT APPLET(mt, mt_main, _BB_DIR_BIN, _BB_SUID_NEVER) #endif @@ -415,6 +421,9 @@ #ifdef CONFIG_PASSWD APPLET(passwd, passwd_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS) #endif +#ifdef CONFIG_PIDFILEHACK + APPLET(pidfilehack, pidfilehack_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif #ifdef CONFIG_PIDOF APPLET(pidof, pidof_main, _BB_DIR_BIN, _BB_SUID_NEVER) #endif diff --git a/include/usage.h b/include/usage.h index e98e8b97c..fde6bbe76 100644 --- a/include/usage.h +++ b/include/usage.h @@ -1430,6 +1430,11 @@ "\ty\tAllow write access to your terminal.\n" \ "\tn\tDisallow write access to your terminal.\n" +#define minit_trivial_usage \ + "[-spPrRC]" +#define minit_full_usage \ + "[-spPrRC]" + #define mkdir_trivial_usage \ "[OPTION] DIRECTORY..." #define mkdir_full_usage \ @@ -1559,6 +1564,25 @@ "$ mount /dev/fd0 /mnt -t msdos -o ro\n" \ "$ mount /tmp/diskimage /opt -t ext2 -o loop\n" +#define msvc_trivial_usage \ + "-[udorspchaitkx] service" +#define msvc_full_usafe \ + "[option] service\n" \ + "Where option is one of\n" \ + "\t-u\tUp. If the service is not running, start it. If the service stops, restart it.\n" \ + "\t-d\tDown. If the service is running, stop it, do not restart it.\n" \ + "\t-o\tOnce. If the service is not running, start it. Do not restart it if it stops.\n" \ + "\t-r\tTell supervise that the service is normally running; this affects status messages.\n" \ + "\t-s\tTell supervise that the service is normally stopped; this affects status messages.\n" \ + "\t-p\tPause. Send the service a STOP signal.\n" \ + "\t-c\tContinue. Send the service a CONT signal.\n" \ + "\t-h\tHangup. Send the service a HUP signal.\n" \ + "\t-a\tAlarm. Send the service an ALRM signal.\n" \ + "\t-i\tInterrupt. Send the service an INT signal.\n" \ + "\t-t\tTerminate. Send the service a TERM signal.\n" \ + "\t-k\tKill. Send the service a KILL signal.\n" \ + "\t-x\tExit. supervise will quit as soon as the service is down.\n" + #define mt_trivial_usage \ "[-f device] opcode value" #define mt_full_usage \ @@ -1666,6 +1690,11 @@ "\t-l\tLocks (disables) the specified user account.\n" \ "\t-u\tUnlocks (re-enables) the specified user account." +#define pidfilehack_trivial_usage \ + "[daemon.pid] [daemon]" +#define pidfilehack_full_usage \ + "service /var/run/daemon.pid /usr/sbin/daemon args...\n" + #define pidof_trivial_usage \ "process-name [process-name ...]" #define pidof_full_usage \ diff --git a/init/Config.in b/init/Config.in index 90173c552..a478e07c8 100644 --- a/init/Config.in +++ b/init/Config.in @@ -61,6 +61,26 @@ config CONFIG_REBOOT help Please submit a patch to add help text for this item. +config CONFIG_MINIT + bool "minit" + default n + help + Minimal init, based on minit v0.9.1 + +config CONFIG_PIDFILEHACK + bool "pidfilehack" + default y + depends on CONFIG_MINIT + help + pidfilehack is used by minit to run servers. + +config CONFIG_MSVC + bool "msvc" + default y + depends on CONFIG_MINIT + help + msvc is used to start and stop processes controlled by minit + # Should start-stop-daemon be moved under debianutils? config CONFIG_START_STOP_DAEMON bool "start-stop-daemon" diff --git a/init/Makefile.in b/init/Makefile.in index a43c4a7f4..9e2f4bcf7 100644 --- a/init/Makefile.in +++ b/init/Makefile.in @@ -26,6 +26,9 @@ INIT-y:= INIT-$(CONFIG_HALT) += halt.o INIT-$(CONFIG_INIT) += init.o INIT-$(CONFIG_MESG) += mesg.o +INIT-$(CONFIG_MINIT) += minit.o +INIT-$(CONFIG_MSVC) += msvc.o +INIT-$(CONFIG_PIDFILEHACK) += pidfilehack.o INIT-$(CONFIG_POWEROFF) += poweroff.o INIT-$(CONFIG_REBOOT) += reboot.o INIT-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o diff --git a/init/minit.c b/init/minit.c new file mode 100644 index 000000000..6645dc6ce --- /dev/null +++ b/init/minit.c @@ -0,0 +1,612 @@ +/* + * minit version 0.9.1 by Felix von Leitner + * ported to busybox by Glenn McGrath <bug1@optushome.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <time.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <sys/fcntl.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/reboot.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <sys/wait.h> +#include <linux/kd.h> + +#include "busybox.h" + +#define MINITROOT "/etc/minit" + +static int i_am_init; +static int infd, outfd; + +extern char **environ; + +static struct process { + char *name; + pid_t pid; + char respawn; + char circular; + time_t startedat; + int __stdin, __stdout; + int logservice; +} *root; + +static int maxprocess = -1; + +static int processalloc = 0; + +static unsigned int fmt_ulong(char *dest, unsigned long i) +{ + register unsigned long len, tmp, len2; + + /* first count the number of bytes needed */ + for (len = 1, tmp = i; tmp > 9; ++len) + tmp /= 10; + if (dest) + for (tmp = i, dest += len, len2 = len + 1; --len2; tmp /= 10) + *--dest = (tmp % 10) + '0'; + return len; +} + +/* split buf into n strings that are separated by c. return n as *len. + * Allocate plus more slots and leave the first ofs of them alone. */ +static char **split(char *buf, int c, int *len, int plus, int ofs) +{ + int n = 1; + char **v = 0; + char **w; + + /* step 1: count tokens */ + char *s; + + for (s = buf; *s; s++) + if (*s == c) + n++; + /* step 2: allocate space for pointers */ + v = (char **) malloc((n + plus) * sizeof(char *)); + if (!v) + return 0; + w = v + ofs; + *w++ = buf; + for (s = buf;; s++) { + while (*s && *s != c) + s++; + if (*s == 0) + break; + if (*s == c) { + *s = 0; + *w++ = s + 1; + } + } + *len = w - v; + return v; +} + +static int openreadclose(char *fn, char **buf, unsigned long *len) +{ + int fd = open(fn, O_RDONLY); + + if (fd < 0) + return -1; + if (!*buf) { + *len = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + *buf = (char *) malloc(*len + 1); + if (!*buf) { + close(fd); + return -1; + } + } + *len = read(fd, *buf, *len); + if (*len != (unsigned long) -1) + (*buf)[*len] = 0; + return close(fd); +} + +/* return index of service in process data structure or -1 if not found */ +static int findservice(char *service) +{ + int i; + + for (i = 0; i <= maxprocess; i++) { + if (!strcmp(root[i].name, service)) + return i; + } + return -1; +} + +/* look up process index in data structure by PID */ +static int findbypid(pid_t pid) +{ + int i; + + for (i = 0; i <= maxprocess; i++) { + if (root[i].pid == pid) + return i; + } + return -1; +} + +/* clear circular dependency detection flags */ +static void circsweep(void) +{ + int i; + + for (i = 0; i <= maxprocess; i++) + root[i].circular = 0; +} + +/* add process to data structure, return index or -1 */ +static int addprocess(struct process *p) +{ + if (maxprocess + 1 >= processalloc) { + struct process *fump; + + processalloc += 8; + if ((fump = + (struct process *) xrealloc(root, + processalloc * + sizeof(struct process))) == 0) + return -1; + root = fump; + } + memmove(&root[++maxprocess], p, sizeof(struct process)); + return maxprocess; +} + +/* load a service into the process data structure and return index or -1 + * if failed */ +static int loadservice(char *service) +{ + struct process tmp; + int fd; + + if (*service == 0) + return -1; + fd = findservice(service); + if (fd >= 0) + return fd; + if (chdir(MINITROOT) || chdir(service)) + return -1; + if (!(tmp.name = strdup(service))) + return -1; + tmp.pid = 0; + fd = open("respawn", O_RDONLY); + if (fd >= 0) { + tmp.respawn = 1; + close(fd); + } else + tmp.respawn = 0; + tmp.startedat = 0; + tmp.circular = 0; + tmp.__stdin = 0; + tmp.__stdout = 1; + { + char *logservice = alloca(strlen(service) + 5); + + strcpy(logservice, service); + strcat(logservice, "/log"); + tmp.logservice = loadservice(logservice); + if (tmp.logservice >= 0) { + int pipefd[2]; + + if (pipe(pipefd)) + return -1; + root[tmp.logservice].__stdin = pipefd[0]; + tmp.__stdout = pipefd[1]; + } + } + return (addprocess(&tmp)); +} + +/* usage: isup(findservice("sshd")). + * returns nonzero if process is up */ +static int isup(int service) +{ + if (service < 0) + return 0; + return (root[service].pid != 0); +} + +static void opendevconsole(void) +{ + int fd; + + if ((fd = open("/dev/console", O_RDWR | O_NOCTTY)) >= 0) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) + close(fd); + } +} + +/* called from inside the service directory, return the PID or 0 on error */ +static pid_t forkandexec(int pause_flag, int service) +{ + char **argv = 0; + int count = 0; + pid_t p; + int fd; + unsigned long len; + char *s = 0; + int argc; + char *argv0 = 0; + + again: + switch (p = fork()) { + case (pid_t) - 1: + if (count > 3) + return 0; + sleep(++count * 2); + goto again; + case 0: + /* child */ + + if (i_am_init) { + ioctl(0, TIOCNOTTY, 0); + setsid(); + opendevconsole(); + tcsetpgrp(0, getpgrp()); + } + close(infd); + close(outfd); + if (pause_flag) { + struct timespec req; + + req.tv_sec = 0; + req.tv_nsec = 500000000; + nanosleep(&req, 0); + } + if (!openreadclose("params", &s, &len)) { + argv = split(s, '\n', &argc, 2, 1); + if (argv[argc - 1]) + argv[argc - 1] = 0; + else + argv[argc] = 0; + } else { + argv = (char **) xmalloc(2 * sizeof(char *)); + argv[1] = 0; + } + argv0 = (char *) xmalloc(PATH_MAX + 1); + if (!argv || !argv0) + goto abort; + if (readlink("run", argv0, PATH_MAX) < 0) { + if (errno != EINVAL) + goto abort; /* not a symbolic link */ + argv0 = strdup("./run"); + } + argv[0] = strrchr(argv0, '/'); + if (argv[0]) + argv[0]++; + else + argv[0] = argv0; + if (root[service].__stdin != 0) + dup2(root[service].__stdin, 0); + if (root[service].__stdout != 1) { + dup2(root[service].__stdout, 1); + dup2(root[service].__stdout, 2); + } + { + int i; + + for (i = 3; i < 1024; ++i) + close(i); + } + execve(argv0, argv, environ); + _exit(0); + abort: + free(argv0); + free(argv); + _exit(0); + default: + fd = open("sync", O_RDONLY); + if (fd >= 0) { + pid_t p2; + + close(fd); + p2 = waitpid(p, 0, 0); + return 1; + } + return p; + } +} + +/* start a service, return nonzero on error */ +static int startnodep(int service, int pause_flag) +{ + /* step 1: see if the process is already up */ + if (isup(service)) + return 0; + + /* step 2: fork and exec service, put PID in data structure */ + if (chdir(MINITROOT) || chdir(root[service].name)) + return -1; + root[service].startedat = time(0); + root[service].pid = forkandexec(pause_flag, service); + return root[service].pid; +} + +static int startservice(int service, int pause_flag) +{ + int dir = -1; + unsigned long len; + char *s = 0; + pid_t pid; + + if (service < 0) + return 0; + if (root[service].circular) + return 0; + root[service].circular = 1; + if (root[service].logservice >= 0) + startservice(root[service].logservice, pause_flag); + if (chdir(MINITROOT) || chdir(root[service].name)) + return -1; + if ((dir = open(".", O_RDONLY)) >= 0) { + if (!openreadclose("depends", &s, &len)) { + char **deps; + int depc, i; + + deps = split(s, '\n', &depc, 0, 0); + for (i = 0; i < depc; i++) { + int service_index; + + if (deps[i][0] == '#') + continue; + service_index = loadservice(deps[i]); + if (service_index >= 0 && root[service_index].pid != 1) + startservice(service_index, 0); + } + fchdir(dir); + } + pid = startnodep(service, pause_flag); + close(dir); + dir = -1; + return pid; + } + return 0; +} + +static void sulogin(void) +{ + /* exiting on an initialization failure is not a good idea for init */ + char *argv[] = { "sulogin", 0 }; + execve("/sbin/sulogin", argv, environ); + exit(1); +} + +static void handlekilled(pid_t killed) +{ + int i; + + if (killed == (pid_t) - 1) { + write(2, "all services exited.\n", 21); + exit(0); + } + if (killed == 0) + return; + i = findbypid(killed); + if (i >= 0) { + root[i].pid = 0; + if (root[i].respawn) { + circsweep(); + startservice(i, time(0) - root[i].startedat < 1); + } else { + root[i].startedat = time(0); + root[i].pid = 1; + } + } +} + +static void childhandler(void) +{ + int status; + pid_t killed; + + do { + killed = waitpid(-1, &status, WNOHANG); + handlekilled(killed); + } while (killed && killed != (pid_t) - 1); +} + +static volatile int dowinch = 0; +static volatile int doint = 0; + +static void sigchild(int whatever) +{ +} +static void sigwinch(int sig) +{ + dowinch = 1; +} +static void sigint(int sig) +{ + doint = 1; +} + +extern int minit_main(int argc, char *argv[]) +{ + /* Schritt 1: argv[1] als Service nehmen und starten */ + struct pollfd pfd; + time_t last = time(0); + int nfds = 1; + int count = 0; + int i; + + infd = open("/etc/minit/in", O_RDWR); + outfd = open("/etc/minit/out", O_RDWR | O_NONBLOCK); + if (getpid() == 1) { + int fd; + + i_am_init = 1; + reboot(0); + if ((fd = open("/dev/console", O_RDWR | O_NOCTTY))) { + ioctl(fd, KDSIGACCEPT, SIGWINCH); + close(fd); + } else + ioctl(0, KDSIGACCEPT, SIGWINCH); + } +/* signal(SIGPWR,sighandler); don't know what to do about it */ +/* signal(SIGHUP,sighandler); ??? */ + { + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = 0; + sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; + sa.sa_handler = sigchild; + sigaction(SIGCHLD, &sa, 0); + sa.sa_handler = sigint; + sigaction(SIGINT, &sa, 0); /* ctrl-alt-del */ + sa.sa_handler = sigwinch; + sigaction(SIGWINCH, &sa, 0); /* keyboard request */ + } + if (infd < 0 || outfd < 0) { + puts("minit: could not open /etc/minit/in or /etc/minit/out\n"); + sulogin(); + nfds = 0; + } else + pfd.fd = infd; + pfd.events = POLLIN; + + for (i = 1; i < argc; i++) { + circsweep(); + if (startservice(loadservice(argv[i]), 0)) + count++; + } + circsweep(); + if (!count) + startservice(loadservice("default"), 0); + for (;;) { + char buf[1501]; + time_t now; + + if (doint) { + doint = 0; + startservice(loadservice("ctrlaltdel"), 0); + } + if (dowinch) { + dowinch = 0; + startservice(loadservice("kbreq"), 0); + } + childhandler(); + now = time(0); + if (now < last || now - last > 30) { + /* The system clock was reset. Compensate. */ + long diff = last - now; + int j; + + for (j = 0; j <= maxprocess; ++j) { + root[j].startedat -= diff; + } + } + last = now; + switch (poll(&pfd, nfds, 5000)) { + case -1: + if (errno == EINTR) { + childhandler(); + break; + } + opendevconsole(); + puts("poll failed!\n"); + sulogin(); + /* what should we do if poll fails?! */ + break; + case 1: + i = read(infd, buf, 1500); + if (i > 1) { + pid_t pid; + int idx = 0; + int tmp; + + buf[i] = 0; + + if (buf[0] != 's' && ((idx = findservice(buf + 1)) < 0)) + error: + write(outfd, "0", 1); + else { + switch (buf[0]) { + case 'p': + write(outfd, buf, fmt_ulong(buf, root[idx].pid)); + break; + case 'r': + root[idx].respawn = 0; + goto ok; + case 'R': + root[idx].respawn = 1; + goto ok; + case 'C': + if (kill(root[idx].pid, 0)) { /* check if still active */ + handlekilled(root[idx].pid); /* no!?! remove form active list */ + goto error; + } + goto ok; + break; + case 'P': + { + unsigned char *x = buf + strlen(buf) + 1; + unsigned char c; + + tmp = 0; + while ((c = *x++ - '0') < 10) + tmp = tmp * 10 + c; + } + if (tmp > 0) { + if (kill(tmp, 0)) + goto error; + pid = tmp; + } + root[idx].pid = tmp; + goto ok; + case 's': + idx = loadservice(buf + 1); + if (idx < 0) + goto error; + if (root[idx].pid < 2) { + root[idx].pid = 0; + circsweep(); + idx = startservice(idx, 0); + if (idx == 0) { + write(outfd, "0", 1); + break; + } + } + ok: + write(outfd, "1", 1); + break; + case 'u': + write(outfd, buf, + fmt_ulong(buf, time(0) - root[idx].startedat)); + } + } + } + break; + default: + break; + } + } +} diff --git a/init/msvc.c b/init/msvc.c new file mode 100644 index 000000000..d72ddce1e --- /dev/null +++ b/init/msvc.c @@ -0,0 +1,300 @@ +/* + * minit version 0.9.1 by Felix von Leitner + * ported to busybox by Glenn McGrath <bug1@optushome.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <sys/fcntl.h> +#include <sys/file.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "busybox.h" + +static int infd, outfd; + +static char buf[1500]; + +static unsigned int fmt_ulong(char *dest, unsigned long i) +{ + register unsigned long len, tmp, len2; + + /* first count the number of bytes needed */ + for (len = 1, tmp = i; tmp > 9; ++len) + tmp /= 10; + if (dest) + for (tmp = i, dest += len, len2 = len + 1; --len2; tmp /= 10) + *--dest = (tmp % 10) + '0'; + return len; +} + +static void addservice(char *service) +{ + char *service_ptr; + + if (strncmp(service, "/etc/minit/", 11) == 0) { + service += 11; + } + + while ((service_ptr = last_char_is(service, '/')) != NULL) { + *service_ptr = 0; + } + strncpy(buf + 1, service, 1400); + buf[1400] = 0; +} + +static int addreadwrite(char *service) +{ + addservice(service); + write(infd, buf, strlen(buf)); + return read(outfd, buf, 1500); +} + +/* return PID, 0 if error */ +static pid_t __readpid(char *service) +{ + int len; + + buf[0] = 'p'; + len = addreadwrite(service); + if (len < 0) + return 0; + buf[len] = 0; + return atoi(buf); +} + +/* return nonzero if error */ +static int respawn(char *service, int yesno) +{ + int len; + + buf[0] = yesno ? 'R' : 'r'; + len = addreadwrite(service); + return (len != 1 || buf[0] == '0'); +} + +/* return nonzero if error */ +static int startservice(char *service) +{ + int len; + + buf[0] = 's'; + len = addreadwrite(service); + return (len != 1 || buf[0] == '0'); +} + +extern int msvc_main(int argc, char **argv) +{ + if (argc < 2) { + bb_show_usage(); + } + infd = bb_xopen("/etc/minit/in", O_WRONLY); + outfd = bb_xopen("/etc/minit/out", O_RDONLY); + + while (lockf(infd, F_LOCK, 1)) { + bb_perror_msg("could not aquire lock!\n"); + sleep(1); + } + + if (argc == 2) { + pid_t pid = __readpid(argv[1]); + + if (buf[0] != '0') { + int len; + int up_time; + + printf("%s: ", argv[1]); + if (pid == 0) + printf("down "); + else if (pid == 1) + printf("finished "); + else { + printf("up (pid %d) ", pid); + } + + buf[0] = 'u'; + len = addreadwrite(argv[1]); + if (len < 0) { + up_time = 0; + } else { + buf[len] = 0; + up_time = atoi(buf); + } + printf("%d seconds\n", up_time); + + if (pid == 0) + return 2; + else if (pid == 1) + return 3; + else + return 0; + } else { + bb_error_msg_and_die("no such service"); + } + } else { + int i; + int ret = 0; + int sig = 0; + pid_t pid; + + if (argv[1][0] == '-') { + switch (argv[1][1]) { + case 'g': + for (i = 2; i < argc; ++i) { + pid = __readpid(argv[i]); + if (pid < 2) { + if (pid == 1) { + bb_error_msg("%s, service termination", argv[i]); + } else { + bb_error_msg("%s, no such service", argv[i]); + } + ret = 1; + } + printf("%d\n", pid); + } + break; + case 'p': + sig = SIGSTOP; + goto dokill; + break; + case 'c': + sig = SIGCONT; + goto dokill; + break; + case 'h': + sig = SIGHUP; + goto dokill; + break; + case 'a': + sig = SIGALRM; + goto dokill; + break; + case 'i': + sig = SIGINT; + goto dokill; + break; + case 't': + sig = SIGTERM; + goto dokill; + break; + case 'k': + sig = SIGKILL; + goto dokill; + break; + case 'o': + for (i = 2; i < argc; ++i) + if (startservice(argv[i]) || respawn(argv[i], 0)) { + bb_error_msg("Couldnt not start %s\n", argv[i]); + ret = 1; + } + break; + case 'd': + for (i = 2; i < argc; ++i) { + pid = __readpid(argv[i]); + if (pid == 0) { + bb_error_msg("%s, no such service\n", argv[i]); + ret = 1; + } else if (pid == 1) + continue; + if (respawn(argv[i], 0) || kill(pid, SIGTERM) + || kill(pid, SIGCONT)); + } + break; + case 'u': + for (i = 2; i < argc; ++i) { + if (startservice(argv[i]) || respawn(argv[i], 1)) { + bb_error_msg("Couldnt not start %s\n", argv[i]); + ret = 1; + } + break; + } + case 'C': + for (i = 2; i < argc; ++i) { + int len; + + buf[0] = 'C'; + len = addreadwrite(argv[i]); + if (len != 1 || buf[0] == '0') { + bb_error_msg("%s has terminated or was killed\n", + argv[i]); + ret = 1; + } + } + break; + case 'P': + pid = atoi(argv[1] + 2); + if (pid > 1) { + char *tmp; + int len; + + buf[0] = 'P'; + addservice(argv[2]); + tmp = buf + strlen(buf) + 1; + tmp[fmt_ulong(tmp, pid)] = 0; + write(infd, buf, strlen(buf) + strlen(tmp) + 2); + len = read(outfd, buf, 1500); + if (len != 1 || buf[0] == '0') { + bb_error_msg_and_die("Couldnt not set pid of service %s\n", argv[2]); + } + } + break; + default: + bb_show_usage(); + } + } else { + bb_show_usage(); + } + return ret; +dokill: + for (i = 2; i < argc; i++) { + pid = __readpid(argv[i]); + if (!pid) { + bb_error_msg("%s no such service\n", argv[i]); + ret = 1; + } + if (kill(pid, sig)) { + bb_error_msg("%s, could not send signal %d to PID %d\n", + argv[i], sig, pid); + ret = 1; + } + } + return ret; + } +} + +/* + -u Up. If the service is not running, start it. If the service stops, + restart it. + -d Down. If the service is running, send it a TERM signal and then a CONT + signal. After it stops, do not restart it. + -o Once. If the service is not running, start it. Do not restart it if it + stops. + -r Tell supervise that the service is normally running; this affects status + messages. + -s Tell supervise that the service is normally stopped; this affects status + messages. + -p Pause. Send the service a STOP signal. + -c Continue. Send the service a CONT signal. + -h Hangup. Send the service a HUP signal. + -a Alarm. Send the service an ALRM signal. + -i Interrupt. Send the service an INT signal. + -t Terminate. Send the service a TERM signal. + -k Kill. Send the service a KILL signal. + -x Exit. supervise will quit as soon as the service is down. +*/ diff --git a/init/pidfilehack.c b/init/pidfilehack.c new file mode 100644 index 000000000..a2d905b55 --- /dev/null +++ b/init/pidfilehack.c @@ -0,0 +1,78 @@ +/* + * minit version 0.9.1 by Felix von Leitner + * ported to busybox by Glenn McGrath <bug1@optushome.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/fcntl.h> + +#include "busybox.h" +/* purpose: argv[1] is the full path to a PID file, + * argv+2 is the daemon to run. + * the daemon is expected to fork in the background and write its PID in + * the pid file. + */ + +extern int pidfilehack_main(int argc, char **argv) +{ + int count = 0; + + if (argc < 3) { + bb_show_usage(); + + } + if (unlink(argv[2]) && (errno != ENOENT)) { + bb_perror_msg("could not remove pid file"); + } + switch (fork()) { + case -1: + bb_perror_msg("could not fork"); + return 2; + case 0: /* child */ + execv(argv[3], argv + 3); + bb_perror_msg("execvp failed"); + return 3; + } + do { + int fd = open(argv[2], O_RDONLY); + + if (fd >= 0) { + static char buf[100] = "-P"; + int len = read(fd, buf + 2, 100); + + close(fd); + if (len > 0) { + char *_argv[] = { "msvc", 0, 0, 0 }; + if (buf[len + 1] == '\n') { + buf[len + 1] = 0; + } else { + buf[len + 2] = 0; + } + _argv[1] = buf; + _argv[2] = argv[1]; + execvp(_argv[0], _argv); + bb_perror_msg("execvp failed"); + return 0; + } + } + sleep(1); + } while (++count < 30); + + return(0); +} |