aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn L McGrath <bug1@ihug.co.nz>2003-05-11 14:52:39 +0000
committerGlenn L McGrath <bug1@ihug.co.nz>2003-05-11 14:52:39 +0000
commit1e11c34be4decfef8fbda8a8e01cd60def8232e5 (patch)
tree6e93956fef2bcd4c1db18031dc081dcaf689f2d1
parent8c6887c855460ee9e688e2a51e29f99faa2a2d8c (diff)
downloadbusybox-1e11c34be4decfef8fbda8a8e01cd60def8232e5.tar.gz
minit, a Minimal init system.
-rw-r--r--include/applets.h9
-rw-r--r--include/usage.h29
-rw-r--r--init/Config.in20
-rw-r--r--init/Makefile.in3
-rw-r--r--init/minit.c612
-rw-r--r--init/msvc.c300
-rw-r--r--init/pidfilehack.c78
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);
+}