# See COPYING for terms
# Config
-PATH=/lib/mkinit/bin:/bin:/sbin:/usr/bin:/usr/sbin
MKSHELL=/usr/lib/plan9/bin/rc
NPROC=10
# Example
-#start-test:VQPservice -u: start-foo
+#test-start:VQPservice -u: foo-start
# echo starting test
# service -U $target
#
-#stop-test:VQPservice -d: /
+#test-stop:VQPservice -d: /
# echo stopping test
# service -D $target
# Runlevels
-# Make getty wait (for bootchart)
+user = alsa keymap polipo spam
+system = at cron hddtemp hostname hwclock i8k sshd swap syslog
+bare = cpufreq fsclean getty localhost modules mounts uevents utmp
+
default:V: user
-user:V: system `{echo start-^(alsa keymap polipo spam)}
-system:V: bare `{echo start-^(at cron hddtemp hostname hwclock i8k sshd swap syslog)}
-bare:V: `{echo start-^(cpufreq fsclean getty localhost modules mounts uevents)}
+user:V: `{echo $user^-start $system^-start $bare^-start}
+system:V: `{echo $user^-stop $system^-start $bare^-start}
+bare:V: `{echo $user^-stop $system^-stop $bare^-start}
+single:V: `{echo $user^-stop $system^-stop $bare^-stop }
+
+poweroff:V: halt
+ $P poweroff -ndf
+reboot:V: halt
+ $P reboot -ndf
+kexec:V: halt
+ $P reboot -ndfk
# Initial setup/shutdown for mkinit
boot:QVEPservice -u: /
service -U $target
# Kill all process, then remount and sync
-halt:QVE: stop-utmp stop-hwclock stop-alsa
+halt:QVE: utmp-stop hwclock-stop alsa-stop
echo Stopping init
rm -f /lib/mkinit/state/*
$P mount -o remount,ro /
$P sync
+# Bare
+# ----
# Proc, mtab, udev, fstab
-start-mounts:QVPservice -u: boot
+mounts-start:QVPservice -u: boot
echo Starting mounts
$P cat /proc/mounts > /etc/mtab
$P udevd --daemon
service -U $target
# Load kernel modules
-start-modules:QVEPservice -u: boot
+modules-start:QVEPservice -u: boot
echo Starting modules
$P modprobe uvesafb
service -U $target
# Trigger udev uevents
-start-uevents:QVEPservice -u: start-mounts
+uevents-start:QVEPservice -u: mounts-start
echo Starting uevents
$P udevadm trigger
$P udevadm settle '--timeout=10'
service -U $target
# Clean out /tmp and /var/run directories
-start-fsclean:QVPservice -u: boot
+fsclean-start:QVPservice -u: boot
echo Starting fsclean
$P rm -rf /tmp/*
$P rm -rf /var/run/*
service -U $target
# Spawn gettys for tty[456]
-start-getty:QVPservice -u: start-hostname start-utmp
+getty-start:QVPservice -u: hostname-start utmp-start
echo Starting getty
$P respawn /sbin/agetty 38400 tty4 linux &
$P respawn /sbin/agetty 38400 tty5 linux &
$P respawn /sbin/agetty 38400 tty6 linux &
service -U $target
+getty-stop_cmd=pkill agetty
# Spawn qingys for tty[23]
-start-qingy:QVPservice -u: start-hostname start-utmp start-modules start-uevents
+qingy-start:QVPservice -u: hostname-start utmp-start modules-start uevents-start
echo Starting qingy
$P respawn /sbin/qingy tty2 &
$P respawn /sbin/qingy tty3 &
service -U $target
+getty-stop_cmd=pkill qingy
# Login records
-start-utmp:QVPservice -u: start-fsclean
+utmp-start:QVPservice -u: fsclean-start
echo Starting utmp
for (i in /var/run/utmp /var/log/wtmp) {
echo -n > $i
chmod 0664 $i
}
service -U $target
-utmp_stop_cmd=halt -w
+utmp-stop_cmd=halt -w
# CPU freq
-start-cpufreq:QVPservice -u: start-uevents
+cpufreq-start:QVPservice -u: uevents-start
echo Starting cpufreq
cpufreq-set -g ondemand
service -U $target
# Keymap (us-cc = us with ctrl-capslock switched)
-keymap_start_cmd=loadkeys -u us-cc
+keymap-start_cmd=loadkeys -u us-cc
# Localhost
-localhost_start_cmd=ifconfig lo 127.0.0.1
-localhost_stop_cmd=ifconfig lo down
+localhost-start_cmd=ifconfig lo 127.0.0.1
+localhost-stop_cmd=ifconfig lo down
# Set hostname
-hostname_start_cmd=hostname b
+hostname-start_cmd=hostname b
# Kernel parameters
-sysctl_start_cmd=sysctl -p
+sysctl-start_cmd=sysctl -p
# Console
# -------
-at_start_cmd=atd
-cron_start_cmd=cron
-hwclock_start_cmd=hwclock --hctosys --utc
-hwclock_stop_cmd=hwclock --systohc --utc
-swap_start_cmd=swapon -a
-swap_stop_cmd=swapoff -a
-start-syslog:QVPservice -u: start-mounts
+at-start_cmd=atd
+at-stop_cmd=pkill atd
+
+cron-start_cmd=cron
+cron-stop_cmd=pkill cron
+
+hwclock-start_cmd=hwclock --hctosys --utc
+hwclock-stop_cmd=hwclock --systohc --utc
+
+swap-start_cmd=swapon -a
+swap-stop_cmd=swapoff -a
+
+syslog-start:QVPservice -u: mounts-start
echo Starting syslog;
$P syslog-ng
service -U $target
-start-hddtemp:QVPservice -u: start-localhost
+syslog-stop_cmd=pkill syslog
+
+hddtemp-start:QVPservice -u: localhost-start
echo Starting hddtemp
$P hddtemp -d -l 127.0.0.1 /dev/sda
service -U $target
-hddtemp_stop_cmd=pkill hddtemp
+hddtemp-stop_cmd=pkill hddtemp
# Desktop
# -------
-alsa_start_cmd=alsactl restore
-alsa_stop_cmd=alsactl store
-sshd_start_cmd=/usr/sbin/sshd
-start-spam:QVPservice -u: start-localhost
+alsa-start_cmd=alsactl restore
+alsa-stop_cmd=alsactl store
+
+sshd-start_cmd=/usr/sbin/sshd
+sshd-stop_cmd=pkill sshd
+
+spam-start:QVPservice -u: localhost-start
echo Starting spam
$P spamd -d
service -U $target
-start-polipo:QVPservice -u: start-localhost
+spam-stop_cmd=pkill spamd
+
+polipo-start:QVPservice -u: localhost-start
echo Starting poliop
$P polipo
service -U $target
-polipo_stop_cmd=pkill polipo
+polipo-stop_cmd=pkill polipo
# Library
# -------
-start-%:QVPservice -u: boot
- if (~ $#($stem^_start_cmd) 0)
+%-start:QVPservice -u: boot
+ if (~ $#($stem^-start_cmd) 0)
exit 0
echo Starting $stem
- $P $($stem^_start_cmd)
+ $P $($stem^-start_cmd)
service -U $target
-stop-%:QVPservice -d: /
- if (~ $#($stem^_stop_cmd) 0)
+%-stop:QVPservice -d: /
+ if (~ $#($stem^-stop_cmd) 0)
exit 0
echo Stopping $stem
- $P $($stem^_stop_cmd)
+ $P $($stem^-stop_cmd)
service -D $target
-zap-%:QVPservice -d: /
+%-zap:QVPservice -d: /
service -D $target
-status-%:QV:
+%-status:QV:
service -q $target
# Copyright (C) 2009 Andy Spencer
# See COPYING for terms
-install:V:
+PROGS=src/initctld
+CLEAN=src/*.o
+
+default:V: all
+
+install:V: all
install -d \
$DESTDIR/etc \
$DESTDIR/sbin \
$DESTDIR/lib/mkinit/bin \
$DESTDIR/lib/mkinit/state
install -t $DESTDIR/lib/mkinit/bin \
- ./src/{mkinit,service,respawn}
+ ./src/{mkinit,service,respawn,initctld}
install -t $DESTDIR/etc ./init.mk
ln -sf $DESTDIR/lib/mkinit/bin/mkinit $DESTDIR/sbin
rm /sbin/mkinit
rmdir /lib/mkinit/state/
rmdir /lib/mkinit/
+
+<../mkcommon
+
+# vim: ft=mk
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define INIT_MAGIC 0x03091969
+#define INIT_CMD_START 0
+#define INIT_CMD_RUNLVL 1
+#define INIT_CMD_POWERFAIL 2
+#define INIT_CMD_POWERFAILNOW 3
+#define INIT_CMD_POWEROK 4
+#define INIT_CMD_BSD 5
+#define INIT_CMD_SETENV 6
+#define INIT_CMD_UNSETENV 7
+#define INIT_CMD_CHANGECONS 12345
+#define INITRQ_HLEN 64
+
+/**
+ * tellinit
+ * 0 shutdown
+ * 1 single
+ * 2 nonetwork
+ * 3 default
+ * 4 default
+ * 5 gui
+ * 6 reboot
+ * abc ignored (inittab)
+ * sS single user
+ * qQ reload
+ * uU reload
+ *
+ * shutdown
+ * -r, reboot runlevel 6 5, setenv INIT_HALT
+ * -h, default halt runlevel 0 5, setenv INIT_HALT
+ * -P, poweroff runlevel S 5, setenv INIT_HALT=POWERDOWN
+ * -H, just halt runlevel S 5, setenv INIT_HALT=HALT
+ * -f, skip fsck runlevel S 5, setenv INIT_HALT
+ * -F, force fskc runlevel S 5, setenv INIT_HALT
+ */
+
+/**
+ * This is what BSD 4.4 uses when talking to init.
+ * Linux doesn't use this right now.
+ */
+struct init_request_bsd {
+ char gen_id[8]; /* Beats me.. telnetd uses "fe" */
+ char tty_id[16]; /* Tty name minus /dev/tty */
+ char host[INITRQ_HLEN]; /* Hostname */
+ char term_type[16]; /* Terminal type */
+ int signal; /* Signal to send */
+ int pid; /* Process to send to */
+ char exec_name[128]; /* Program to execute */
+ char reserved[128]; /* For future expansion. */
+};
+
+/**
+ * Because of legacy interfaces, "runlevel" and "sleeptime" aren't in a
+ * seperate struct in the union.
+ *
+ * The weird sizes are because init expects the whole struct to be 384 bytes.
+ */
+struct init_request {
+ int magic; /* Magic number */
+ int cmd; /* What kind of request */
+ int runlevel; /* Runlevel to change to */
+ int sleeptime; /* Time between TERM and KILL */
+ union {
+ struct init_request_bsd bsd;
+ char data[368];
+ } i;
+};
+
+const char *telinit_map[0x100] = {
+ /* not sure if these are mapped correctly */
+ ['0'] "runlevel poweroff",
+ ['1'] "runlevel single",
+ ['2'] "runlevel bare",
+ ['3'] "runlevel system",
+ ['5'] "runlevel user",
+ ['6'] "runlevel reboot",
+ ['s'] "runlevel single",
+ ['S'] "runlevel single",
+ ['q'] "reload",
+ ['Q'] "reload",
+ ['u'] "reload",
+ ['U'] "reload",
+};
+
+int open_initctl(char *init_fifo)
+{
+ /* First, try to create /dev/initctl if not present. */
+ struct stat st, st2;
+ if (stat(init_fifo, &st2) < 0 && errno == ENOENT)
+ mkfifo(init_fifo, 0600);
+
+ /* Now finally try to open /dev/initctl */
+ int pipe_fd = open(init_fifo, O_RDWR);
+ if (pipe_fd < 0)
+ err(errno, "error opening %s", init_fifo);
+
+ /* Make sure it's a fifo */
+ fstat(pipe_fd, &st);
+ if (!S_ISFIFO(st.st_mode)) {
+ errno = EINVAL;
+ err(errno, "%s is not a fifo", init_fifo);
+ }
+
+ return pipe_fd;
+}
+
+void process_request(struct init_request *request)
+{
+ if (request->sleeptime)
+ printf("eval SLEEP_TIME=%d\n", request->sleeptime);
+
+ switch (request->cmd) {
+ case INIT_CMD_RUNLVL:
+ if (telinit_map[request->runlevel])
+ printf("%s\n", telinit_map[request->runlevel]);
+ break;
+ case INIT_CMD_POWERFAIL:
+ printf("powerfail\n");
+ break;
+ case INIT_CMD_POWERFAILNOW:
+ printf("powerfailnow\n");
+ break;
+ case INIT_CMD_POWEROK:
+ printf("powerok\n");
+ break;
+ case INIT_CMD_SETENV:
+ printf("eval export %.*s\n",
+ sizeof(request->i.data),
+ request->i.data);
+ break;
+ case INIT_CMD_CHANGECONS:
+ printf("changeconsole %.*s\n",
+ sizeof(request->i.bsd.reserved),
+ request->i.bsd.reserved);
+ break;
+ default:
+ fprintf(stderr, "got unimplemented initrequest");
+ break;
+ }
+ fflush(stdout);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ printf("usage: %s <initctl>\n", argv[0]);
+ return -EINVAL;
+ }
+ char *init_fifo = argv[1];
+ int pipe_fd = open_initctl(init_fifo);
+
+ /* Main loop, process data and reopen pipe when necessasairy */
+ while (1) {
+ /* Read the data, return on EINTR. */
+ struct init_request request;
+ int n = read(pipe_fd, &request, sizeof(request));
+ if (n == 0)
+ pipe_fd = open_initctl(init_fifo);
+ else if (n <= 0)
+ warn("error reading request");
+ else if (n != sizeof(request))
+ warn("short read");
+ else if (request.magic != INIT_MAGIC)
+ warn("got bogus request");
+ else
+ process_request(&request);
+ }
+
+ return 0;
+}
-#!/bin/sh
+#!/bin/bash
# Copyright (C) 2009 Andy Spencer
# See ../COPYING for terms
+# GLobals
+CMD=/lib/mkinit/cmd
+INITCTL=/dev/initctl
+PATH=/lib/mkinit/bin:/bin:/sbin:/usr/bin:/usr/sbin
+export PATH
+
+# Functions
function usage {
cat - <<EOF
usage: $0 [options] [command]
-i -k "$@"
}
-# Handle arguments
-TEMP=`getopt -n "$0" \
- --options th \
- --longoptions test,help \
- -- "$@"`
-[ $? != 0 ] && usage
-eval set -- "$TEMP"
-while true; do
- [ "$TEST" ] && echo "\$1=$1"
- case "$1" in
- -h|--help ) usage ;;
- -t|--test ) TEST=true; shift ;;
- -- ) shift; cmd="$1";
- shift; args="$@";
- break ;;
- * ) ;;
- esac
-done
-if [ "$TEST" ]; then
- echo "Options"
- echo " test=$TEST"
- echo " cmd=$cmd"
- echo " args=$args"
-fi
-
-# Main loop
-while true; do
+function process {
+ cmd="$1"
+ shift
+ args="$@"
case "$cmd" in
boot )
echo
echo "mkinit -- booting"
- if runamk -a "$args" && ! [ "$TEST" ]; then
- # booted successuflly, redirect input
- echo "skipping redirect"
- #pipe=/lib/mkinit/cmd
- #[ -p pipe ] || mkfifo $pipe
- #exec 0< $pipe
- fi
- ;;
- halt|reboot|poweroff )
- echo "mkinit -- ${cmd}ing"
- runamk "halt"
- if [ "$args" -o "$cmd" = reboot -o "$cmd" = poweroff ]; then
- # mk handles syncing and logging message
- ${TEST:+echo} $cmd -ndf $args
- fi
+ runamk -a "$args"
;;
restart )
if [ "$args" ]; then
echo "mkinit -- restarting $args"
- runamk "stop-$args"
- runamk "start-$args"
+ runamk "$args-stop"
+ runamk "$args-start"
fi
;;
start|stop|zap|status )
if [ "$args" ]; then
echo "mkinit -- ${cmd}ing $args"
- runamk "${cmd}-$args"
+ runamk "$args-$cmd"
fi
;;
- mk )
+ mk|runlevel )
if [ "$args" ]; then
- echo "mkinit -- running mk cmd [$args]"
+ [ "$cmd" = mk ] &&
+ echo "mkinit -- running mk cmd [$args]"
+ [ "$cmd" = runlevel ] &&
+ echo "mkinit -- entering runlevel $args"
runamk "$args"
fi
;;
reload )
- exec /sbin/mkinit ${TEST:+"-t"}
+ echo "mkinit -- ${cmd}ing"
+ exec $0 -r ${TEST:+"-t"}
;;
eval )
- $args
+ eval $args
;;
?* )
- echo "unknown command [$cmd $args]"
+ echo "mkinit -- unknown command [$cmd $args]"
;;
esac
- read -e -p "mkinit> " cmd args
- history -s $cmd $args
+}
+
+# Handle arguments
+TEMP=`getopt -n "$0" \
+ --options hrt \
+ --longoptions help,reload,test \
+ -- "$@"`
+[ $? != 0 ] && usage
+eval set -- "$TEMP"
+while true; do
+ [ "$TEST" ] && echo "\$1=$1"
+ case "$1" in
+ -h|--help ) usage ;;
+ -r|--reload ) RELOAD=true; shift ;;
+ -t|--test ) TEST=true; shift ;;
+ -- ) shift; cmd="$1";
+ shift; args="$@";
+ break ;;
+ * ) ;;
+ esac
+done
+
+# Initial boot-up
+process $cmd $args
+
+if [ "$TEST" ]; then
+ CMD=/tmp/pipe
+ echo "Options"
+ echo " test=$TEST"
+ echo " reload=$RELOAD"
+ echo " cmd=$cmd"
+ echo " args=$args"
+fi
+
+if [ ! "$RELOAD" ]; then
+ # Fork /dev/initctl listener
+ initctld $INITCTL |
+ while read line; do
+ echo $line > $CMD
+ done &
+
+ # Fork console listener
+ while read -e -p "mkinit> " line; do
+ echo $line > $CMD
+ history -s $line
+ done <&0 &
+fi
+
+# Kill listeners on exit
+trap "pkill -HUP -P $$ initctld" EXIT
+
+# Main loop
+while true; do
+while line=$(line); do
+ process $line
+done < $CMD
done
STATE=/lib/mkinit/state
action="$1"
-service="${2/*-}"
+service="${2/-*}"
[ "$action" -a "$service" ] || usage