From e90b48c458d3ebc6d08e0ac36f7a09ac21fd2eee Mon Sep 17 00:00:00 2001 From: Andy Spencer Date: Thu, 15 Oct 2009 03:27:37 +0000 Subject: [PATCH] Lots of work on mkinit Interprocess communication, mkinit now reads from a fifo, which is written to by initctld and (by default) tty0. Initctld reads the old /dev/initctl fifo which is used by shutdown. Adding a better concept of runlevels and -stop scripts for most services. --- init.mk | 117 +++++++++++++++++++------------- mkfile | 13 +++- src/initctld.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++ src/mkinit | 134 ++++++++++++++++++++++--------------- src/service | 2 +- 5 files changed, 342 insertions(+), 101 deletions(-) create mode 100644 src/initctld.c diff --git a/init.mk b/init.mk index eeeda21..aebee30 100644 --- a/init.mk +++ b/init.mk @@ -2,26 +2,36 @@ # 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: / @@ -33,7 +43,7 @@ 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/* @@ -50,8 +60,10 @@ halt:QVE: stop-utmp stop-hwclock stop-alsa $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 @@ -59,42 +71,44 @@ start-mounts:QVPservice -u: boot 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 @@ -102,81 +116,94 @@ start-utmp:QVPservice -u: start-fsclean 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 diff --git a/mkfile b/mkfile index e762874..8a60b60 100644 --- a/mkfile +++ b/mkfile @@ -1,14 +1,19 @@ # 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 @@ -18,3 +23,7 @@ uninstall:VE: rm /sbin/mkinit rmdir /lib/mkinit/state/ rmdir /lib/mkinit/ + +<../mkcommon + +# vim: ft=mk diff --git a/src/initctld.c b/src/initctld.c new file mode 100644 index 0000000..892a25e --- /dev/null +++ b/src/initctld.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 \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; +} diff --git a/src/mkinit b/src/mkinit index 581e662..0542431 100755 --- a/src/mkinit +++ b/src/mkinit @@ -1,8 +1,15 @@ -#!/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 - < " 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 diff --git a/src/service b/src/service index 034df51..d4b7a7d 100755 --- a/src/service +++ b/src/service @@ -20,7 +20,7 @@ exit STATE=/lib/mkinit/state action="$1" -service="${2/*-}" +service="${2/-*}" [ "$action" -a "$service" ] || usage -- 2.43.2