examples/shutdown-1.0: an example of reboot which does not signal init
No binary (build failed)
—
(log yok)
| @@ -0,0 +1,30 @@ | |
| + | # Replaces traditional overdesigned shutdown mechanism. |
| + | # |
| + | # No communication with init is necessary: |
| + | # just ask all processes to exit. |
| + | # Then unmount everything. Then reboot or power off. |
| + | |
| + | # Install /sbin/ symlinks named halt, reboot, poweroff |
| + | # (and also possibly shutdown) to e.g. |
| + | # /app/shutdown-1.0/script/shutdown: |
| + | # |
| + | ln -s /app/shutdown-1.0/script/shutdown /sbin/halt |
| + | ln -s /app/shutdown-1.0/script/shutdown /sbin/reboot |
| + | ln -s /app/shutdown-1.0/script/shutdown /sbin/poweroff |
| + | # |
| + | # shutdown spawns do_shutdown in new session, redirected to /dev/null, |
| + | # tells user that shutdown is in progress, and sleeps |
| + | # (for cosmetic reasons: do not confuse user by letting him |
| + | # type more commands in this terminal). |
| + | # |
| + | # do_shutdown tries to switch to a VT console. |
| + | # Then, (only if -r) it spawns a hardshutdown child, to reboot |
| + | # unconditionally in 30 seconds if something later goes seriously bad. |
| + | # Then it runs stop_tasks, writing to /var/log/reboot/YYYYMMDDhhmmss.log, |
| + | # then it runs stop_storage. |
| + | # Then it commands kernel to halt/reboot/poweroff, if requested. |
| + | # Then it sleeps forever. |
| + | # |
| + | # Build the hardshutdown binary: |
| + | # |
| + | cd script && ./hardshutdown.make.sh |
| @@ -0,0 +1,54 @@ | |
| + | #!/bin/sh |
| + | # We are called with stdin/out/err = /dev/null |
| + | |
| + | resetgracetime=60 |
| + | |
| + | logfile="/var/log/reboot/`date '+%Y%m%d%H%M%S'`.log" |
| + | mkdir -p /var/log/reboot |
| + | |
| + | PATH=/sbin:/bin |
| + | |
| + | say() { |
| + | printf "\r%s\n\r" "$*" |
| + | } |
| + | |
| + | # Since there is a potential for various fuckups during umount, |
| + | # we start delayed hard reboot here which will forcibly |
| + | # reboot hung box in a remote datacenter a thousand miles away ;) |
| + | if test "$1" = "-r"; then |
| + | ./hardshutdown -r "$resetgracetime" & |
| + | fi |
| + | |
| + | # Now, (try to) switch away from X and open a console. I've seen reboots |
| + | # hung on open("/dev/console"), therefore we do it _after_ hardshutdown |
| + | exec >/dev/console 2>&1 |
| + | |
| + | if test "$1" = "-r"; then |
| + | say "* `date '+%H:%M:%S'` Scheduled hard reboot in $resetgracetime seconds" |
| + | fi |
| + | |
| + | say "* `date '+%H:%M:%S'` Stopping tasks (see /var/log/reboot/* files)" |
| + | # log reboot event to file. %Y%m%d%H%M%S: YYYYMMDDHHMMSS |
| + | ./stop_tasks >"$logfile" 2>&1 |
| + | |
| + | # Dying X tends to leave us at semi-random vt. Try to fix that, |
| + | # but if it doesn't work, proceed anyway. |
| + | exec >/dev/null 2>&1 |
| + | chvt 1 & sleep 1 |
| + | exec >/dev/console 2>&1 |
| + | |
| + | command -v ctrlaltdel >/dev/null && { |
| + | say "* `date '+%H:%M:%S'` Setting Ctrl-Alt-Del to 'hard'" |
| + | ctrlaltdel hard |
| + | } |
| + | |
| + | say "* `date '+%H:%M:%S'` Stopping storage devices" |
| + | # we can't log this: we are about to unmount everything! |
| + | ./stop_storage "$@" |
| + | |
| + | # If we have cmdline params, start hardshutdown with them |
| + | test "$*" && ./hardshutdown "$@" |
| + | |
| + | # Just sleep endlessly... |
| + | say "* `date '+%H:%M:%S'` You may now power off or press Ctrl-Alt-Del to reboot" |
| + | while true; do sleep 32000; done |
| @@ -0,0 +1,168 @@ | |
| + | /* Including <unistd.h> makes sure that on a glibc system |
| + | * <features.h> is included, which again defines __GLIBC__ |
| + | */ |
| + | |
| + | #include <unistd.h> |
| + | #include <stdio.h> /* puts */ |
| + | #include <time.h> /* nanosleep */ |
| + | #include <errno.h> |
| + | #include <stdlib.h> |
| + | #include <string.h> |
| + | |
| + | |
| + | /* |
| + | * Magic values required to use _reboot() system call. |
| + | */ |
| + | #define LINUX_REBOOT_MAGIC1 0xfee1dead |
| + | #define LINUX_REBOOT_MAGIC2 672274793 |
| + | #define LINUX_REBOOT_MAGIC2A 85072278 |
| + | #define LINUX_REBOOT_MAGIC2B 369367448 |
| + | /* |
| + | * Commands accepted by the _reboot() system call. |
| + | * |
| + | * RESTART Restart system using default command and mode. |
| + | * HALT Stop OS and give system control to ROM monitor, if any. |
| + | * CAD_ON Ctrl-Alt-Del sequence causes RESTART command. |
| + | * CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task. |
| + | * POWER_OFF Stop OS and remove all power from system, if possible. |
| + | * RESTART2 Restart system using given command string. |
| + | */ |
| + | #define LINUX_REBOOT_CMD_RESTART 0x01234567 |
| + | #define LINUX_REBOOT_CMD_HALT 0xCDEF0123 |
| + | #define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF |
| + | #define LINUX_REBOOT_CMD_CAD_OFF 0x00000000 |
| + | #define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC |
| + | #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4 |
| + | |
| + | |
| + | #define USE_LIBC |
| + | |
| + | #ifdef USE_LIBC |
| + | |
| + | /* libc version */ |
| + | #if defined __GLIBC__ && __GLIBC__ >= 2 |
| + | # include <sys/reboot.h> |
| + | # define REBOOT(cmd) reboot(cmd) |
| + | #else |
| + | extern int reboot(int, int, int); |
| + | # define REBOOT(cmd) reboot(LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,(cmd)) |
| + | #endif |
| + | |
| + | static int my_reboot(int cmd) |
| + | { |
| + | return REBOOT(cmd); |
| + | } |
| + | |
| + | #else /* no USE_LIBC */ |
| + | |
| + | /* direct syscall version */ |
| + | #include <linux/unistd.h> |
| + | |
| + | #ifdef _syscall3 |
| + | _syscall3(int, reboot, int, magic, int, magic_too, int, cmd); |
| + | #else |
| + | /* Let us hope we have a 3-argument reboot here */ |
| + | extern int reboot(int, int, int); |
| + | #endif |
| + | |
| + | static int my_reboot(int cmd) |
| + | { |
| + | return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd); |
| + | } |
| + | |
| + | #endif |
| + | |
| + | |
| + | static void do_reboot(void) |
| + | { |
| + | my_reboot(LINUX_REBOOT_CMD_RESTART); |
| + | } |
| + | static void do_poweroff(void) |
| + | { |
| + | my_reboot(LINUX_REBOOT_CMD_POWER_OFF); |
| + | } |
| + | static void do_halt(void) |
| + | { |
| + | my_reboot(LINUX_REBOOT_CMD_HALT); |
| + | } |
| + | |
| + | static void usage(void) |
| + | { |
| + | puts( |
| + | "Usage: hardshutdown -h|-r|-p [NN]\n" |
| + | " NN - seconds to sleep before requested action" |
| + | ); |
| + | exit(1); |
| + | } |
| + | |
| + | enum action_t { |
| + | SHUTDOWN, // do nothing |
| + | HALT, |
| + | POWEROFF, |
| + | REBOOT |
| + | }; |
| + | |
| + | int main(int argc, char *argv[]) |
| + | { |
| + | struct timespec t = {0,0}; |
| + | enum action_t action = SHUTDOWN; |
| + | int c, i; |
| + | char *prog, *ptr; |
| + | |
| + | //if (*argv[0] == '-') argv[0]++; /* allow shutdown as login shell */ |
| + | prog = argv[0]; |
| + | ptr = strrchr(prog,'/'); |
| + | if (ptr) |
| + | prog = ptr+1; |
| + | |
| + | for (c=1; c < argc; c++) { |
| + | if (argv[c][0] >= '0' && argv[c][0] <= '9') { |
| + | t.tv_sec = strtol(argv[c], NULL, 10); |
| + | continue; |
| + | } |
| + | if (argv[c][0] != '-') { |
| + | usage(); |
| + | return 1; |
| + | } |
| + | for (i=1; argv[c][i]; i++) { |
| + | switch (argv[c][i]) { |
| + | case 'h': |
| + | action = HALT; |
| + | break; |
| + | case 'p': |
| + | action = POWEROFF; |
| + | break; |
| + | case 'r': |
| + | action = REBOOT; |
| + | break; |
| + | default: |
| + | usage(); |
| + | return 1; |
| + | } |
| + | } |
| + | } |
| + | |
| + | if (action==SHUTDOWN) { |
| + | usage(); |
| + | return 1; |
| + | } |
| + | |
| + | chdir("/"); |
| + | while (nanosleep(&t,&t)<0) |
| + | if (errno!=EINTR) break; |
| + | |
| + | switch (action) { |
| + | case HALT: |
| + | do_halt(); |
| + | break; |
| + | case POWEROFF: |
| + | do_poweroff(); |
| + | break; |
| + | case REBOOT: |
| + | do_reboot(); |
| + | break; |
| + | default: /* SHUTDOWN */ |
| + | break; |
| + | } |
| + | return 1; |
| + | } |
| @@ -0,0 +1,8 @@ | |
| + | #!/bin/sh |
| + | |
| + | gcc -Wall -Os -o hardshutdown hardshutdown.c |
| + | strip hardshutdown |
| + | |
| + | #or: |
| + | #diet gcc -Wall -o hardshutdown hardshutdown.c |
| + | #elftrunc hardshutdown |
| @@ -0,0 +1,64 @@ | |
| + | #!/bin/sh |
| + | |
| + | PATH=/sbin:/usr/sbin:/bin:/usr/bin |
| + | |
| + | # Usually, /sbin/ has symlinks named halt, reboot, poweroff |
| + | # (and also possibly shutdown) to e.g. |
| + | # /app/shutdown-1.0/script/shutdown (this file). |
| + | cd /app/shutdown-1.0/script || exit 1 |
| + | test -x ./do_shutdown || exit 1 |
| + | test -x ./hardshutdown || exit 1 |
| + | |
| + | # "reboot -f" -> "shutdown -f -r" -> "hardshutdown -r" -> immediate reboot |
| + | # "reboot" -> "shutdown -r" -> "do_shutdown -r" |
| + | # ^^^^^^^^^^^^^^^^^^ similarly for halt, poweroff. |
| + | # "shutdown" -> "do_shutdown" (everything killed/unmounted, but kernel not asked to do any poweroff etc) |
| + | force="" |
| + | test x"$1" = x"-f" && { |
| + | force="-f" |
| + | shift |
| + | } |
| + | test ! "$*" && test x"${0##*/}" = x"halt" && exec "$0" $force -h |
| + | test ! "$*" && test x"${0##*/}" = x"reboot" && exec "$0" $force -r |
| + | test ! "$*" && test x"${0##*/}" = x"poweroff" && exec "$0" $force -p |
| + | # We have something else than allowed parameters? |
| + | test x"$*" = x"" || test x"$*" = x"-h" || test x"$*" = x"-r" || test x"$*" = x"-p" || { |
| + | echo "Syntax: $0 [-f] [-h/-r/-p]" |
| + | exit 1 |
| + | } |
| + | |
| + | # Emergency shutdown? |
| + | test "$force" && { |
| + | exec ./hardshutdown "$@" |
| + | exit 1 |
| + | } |
| + | |
| + | # Normal shutdown |
| + | |
| + | # We must have these executables on root fs |
| + | # (mount/umount aren't checked, all systems are ok versus that): |
| + | test -x /bin/killall5 -o -x /sbin/killall5 || exit 1 |
| + | test -x /bin/ps -o -x /sbin/ps || exit 1 |
| + | test -x /bin/date -o -x /sbin/date || exit 1 |
| + | test -x /bin/xargs -o -x /sbin/xargs || exit 1 |
| + | test -x /bin/wc -o -x /sbin/wc || exit 1 |
| + | test -x /bin/cat -o -x /sbin/cat || exit 1 |
| + | test -x /bin/sort -o -x /sbin/sort || exit 1 |
| + | |
| + | i="`ulimit -n`" |
| + | echo -n "Closing file descriptors $i-3... " |
| + | while test "$i" -ge 3; do |
| + | eval "exec $i>&-" |
| + | i=$((i-1)) |
| + | done |
| + | |
| + | echo "Shutting down. Please stand by..." |
| + | |
| + | # setsid & /dev/null: |
| + | # make it a process leader & detach it from current tty. |
| + | # Why /dev/null and not /dev/console? |
| + | # I have seen a system which locked up while opening /dev/console |
| + | # due to the bug (?) in keyboard driver. |
| + | setsid env - PATH="$PATH" ./do_shutdown "$@" </dev/null >/dev/null 2>&1 & |
| + | |
| + | while true; do read junk; done |
| @@ -0,0 +1,81 @@ | |
| + | #!/bin/sh |
| + | # Do unmount/remount-ro. Wait. |
| + | # KILL everybody. Wait. |
| + | # Repeat. |
| + | |
| + | umountcnt=2 |
| + | writeout=0 # increase if your kernel doesn ot guarantee writes to complete |
| + | |
| + | # No /usr - we are expecting all binaries to be accessible |
| + | # from root fs alone |
| + | PATH=/sbin:/bin |
| + | |
| + | say() { |
| + | printf "\r%s\n\r" "$*" |
| + | } |
| + | |
| + | showps() { |
| + | # sleep 1 ensures that xargs will have time to start up |
| + | # this makes pslist less prone to random jitter |
| + | pslist=`{ sleep 1; ps -A -o comm=; } | sort | xargs` |
| + | pscnt=$(( `say "$pslist" | wc -w` + 0 )) |
| + | if test x"$VERBOSE" = x; then |
| + | say "* `date '+%H:%M:%S'` $pscnt processes" |
| + | else |
| + | say "* `date '+%H:%M:%S'` Processes ($pscnt): $pslist" |
| + | fi |
| + | } |
| + | |
| + | say "<*> `date '+%Y-%m-%d %H:%M:%S'` Executing '$0 $*'" |
| + | |
| + | showps |
| + | |
| + | i="$umountcnt" |
| + | while test "$i" -gt 0; do |
| + | say "* `date '+%H:%M:%S'` Unmounting filesystems" |
| + | umount -a -n -r -f |
| + | # In case we unmounted proc... |
| + | test -e /proc/version || mount -t proc none /proc |
| + | # Remounting / RO isn't necessary when /etc/mtab is linked to /proc/mounts: |
| + | # already done. But let's be more paranoid here... |
| + | say "* `date '+%H:%M:%S'` Remounting root filesystem read-only" |
| + | mount -n -o remount,ro / |
| + | say "* `date '+%H:%M:%S'` Freeing loop devices" |
| + | for a in /dev/loop*; do |
| + | test -b "$a" && losetup -d "$a" |
| + | done |
| + | say "* `date '+%H:%M:%S'` Syncing" |
| + | sync |
| + | say "* `date '+%H:%M:%S'` Executing: killall5 -KILL" |
| + | killall5 -9 |
| + | showps |
| + | i=$((i-1)) |
| + | done |
| + | |
| + | say "* `date '+%H:%M:%S'` Filesystem status (/proc/mounts)" |
| + | cat /proc/mounts \ |
| + | | { |
| + | bad=false |
| + | while read dev mntpoint fstype opt n1 n2; do |
| + | case "$fstype" in |
| + | ( proc | sysfs | usbfs | devpts | rpc_pipefs | binfmt_misc | autofs | rootfs | tmpfs | ramfs ) |
| + | say "$dev $mntpoint $fstype $opt $n1 $n2" |
| + | continue |
| + | ;; |
| + | esac |
| + | if test "${opt:0:2}" = "rw"; then |
| + | say "$dev $mntpoint $fstype $opt $n1 $n2 - RW!" |
| + | bad=true |
| + | else |
| + | say "$dev $mntpoint $fstype $opt $n1 $n2" |
| + | fi |
| + | done |
| + | if $bad; then |
| + | say "ERROR: we have filesystems mounted RW! Press <Enter> (^J)..." |
| + | read junk </dev/console |
| + | #sh </dev/console >&0 2>&0 # debug |
| + | fi |
| + | } |
| + | |
| + | # Disk cache writeout |
| + | sleep "$writeout" |
| @@ -0,0 +1,70 @@ | |
| + | #!/bin/sh |
| + | # We are trying to be nice. |
| + | # TERM everybody. Give them some time to die. |
| + | # KILL might make some filesystems non-unmountable, |
| + | # so we'll do it in stop_storage instead. |
| + | |
| + | killcnt=30 |
| + | |
| + | PATH=/sbin:/usr/sbin:/bin:/usr/bin |
| + | |
| + | echo "<*> `date '+%Y-%m-%d %H:%M:%S'` Executing '$0 $*'" |
| + | |
| + | showps() { |
| + | # sleep 1 ensures that xargs will have time to start up. |
| + | # This makes pslist less prone to random jitter. |
| + | pslist=`{ sleep 1; ps -A -o comm=; } | sort | xargs` |
| + | pscnt=$(( `echo "$pslist" | wc -w` + 0 )) |
| + | if test x"$VERBOSE" = x; then |
| + | echo "* `date '+%H:%M:%S'` $pscnt processes" |
| + | else |
| + | echo "* `date '+%H:%M:%S'` Processes ($pscnt): $pslist" |
| + | fi |
| + | } |
| + | |
| + | # Sync. |
| + | # Rationale: sometimes buggy root processes can |
| + | # hang the system when killed (X for example may have problems |
| + | # with restoring text mode on a poorly supported hardware). |
| + | # These are bugs and must be fixed, but until then users will lose |
| + | # dirty data on shutdown! Let's make that less likely. |
| + | sync & |
| + | |
| + | # Send SIGTERMs. If list of processes changes, proceed slower. |
| + | # If it has stabilised (all who wanted to, exited), proceed faster. |
| + | showps |
| + | i="$killcnt" |
| + | while test "$i" -gt 0; do |
| + | echo "* `date '+%H:%M:%S'` Sending CONT, TERM" #, HUP" |
| + | # I've seen "killall5 2.86" which doesn't grok signal names! |
| + | killall5 -18 |
| + | killall5 -15 |
| + | #killall5 -1 # HUP: because interactive bash does not die on TERM... |
| + | # but init will reread /etc/inittab on HUP and my /etc is on non root fs! |
| + | # -> umounts will complain. |
| + | oldpslist="$pslist" |
| + | showps |
| + | if test x"$pslist" = x"$oldpslist"; then |
| + | i=$((i-8)) |
| + | fi |
| + | i=$((i-2)) |
| + | done |
| + | |
| + | echo "* `date '+%H:%M:%S'` Turning off swap" |
| + | swapoff -a |
| + | cat /proc/swaps | grep -v ^Filename | cut -d ' ' -f1 \ |
| + | | while read -r line; do |
| + | test "$line" && { |
| + | echo swapoff "$line" |
| + | swapoff "$line" |
| + | } |
| + | done |
| + | |
| + | echo "* /proc/swaps:" |
| + | cat /proc/swaps |
| + | echo "* /proc/mounts:" |
| + | cat /proc/mounts |
| + | echo "* ps -A e:" |
| + | ps -A e |
| + | echo "* top -bn1:" |
| + | top -bn1 |