minit, a Minimal init system.
No binary (build failed)
—
(log yok)
| @@ -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 | |
| @@ -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 \ | |
| @@ -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" | |
| @@ -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 | |
| @@ -0,0 +1,612 @@ | |
| + | /* |
| + | * minit version 0.9.1 by Felix von Leitner |
| + | * ported to busybox by Glenn McGrath <[email protected]> |
| + | * |
| + | * 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; |
| + | } |
| + | } |
| + | } |
| @@ -0,0 +1,300 @@ | |
| + | /* |
| + | * minit version 0.9.1 by Felix von Leitner |
| + | * ported to busybox by Glenn McGrath <[email protected]> |
| + | * |
| + | * 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. |
| + | */ |
| @@ -0,0 +1,78 @@ | |
| + | /* |
| + | * minit version 0.9.1 by Felix von Leitner |
| + | * ported to busybox by Glenn McGrath <[email protected]> |
| + | * |
| + | * 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); |
| + | } |