From 1e11c34be4decfef8fbda8a8e01cd60def8232e5 Mon Sep 17 00:00:00 2001
From: Glenn L McGrath <bug1@ihug.co.nz>
Date: Sun, 11 May 2003 14:52:39 +0000
Subject: [PATCH] minit, a Minimal init system.

---
 include/applets.h  |   9 +
 include/usage.h    |  29 +++
 init/Config.in     |  20 ++
 init/Makefile.in   |   3 +
 init/minit.c       | 612 +++++++++++++++++++++++++++++++++++++++++++++
 init/msvc.c        | 300 ++++++++++++++++++++++
 init/pidfilehack.c |  78 ++++++
 7 files changed, 1051 insertions(+)
 create mode 100644 init/minit.c
 create mode 100644 init/msvc.c
 create mode 100644 init/pidfilehack.c

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);
+}