base-files: always use staged sysupgrade
authorMatthias Schiffer <mschiffer@universe-factory.net>
Fri, 21 Apr 2017 22:54:50 +0000 (00:54 +0200)
committerMatthias Schiffer <mschiffer@universe-factory.net>
Mon, 29 May 2017 21:50:32 +0000 (23:50 +0200)
Support for the -d and -p options is dropped; it may be added again at some
point by adding these flags to the ubus sysupgrade call.

A downside of this is that we get a lot less information about the progress
of the upgrade: as soon as the actual upgrade starts, all shell sessions
are killed to allow unmounting the root filesystem.

Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
package/base-files/files/lib/upgrade/common.sh
package/base-files/files/lib/upgrade/nand.sh
package/base-files/files/lib/upgrade/stage2 [new file with mode: 0755]
package/base-files/files/sbin/sysupgrade

index e3519ca..17248c2 100644 (file)
@@ -56,7 +56,6 @@ run_ramfs() { # <command> [...]
                /bin/rm /usr/bin/basename /bin/kill /bin/chmod /usr/bin/find \
                /bin/mknod
 
-       install_bin /bin/uclient-fetch /bin/wget
        install_bin /sbin/mtd
        install_bin /sbin/mount_root
        install_bin /sbin/snapshot
@@ -96,51 +95,37 @@ run_ramfs() { # <command> [...]
        exec /bin/busybox ash -c "$*"
 }
 
-kill_remaining() { # [ <signal> ]
+kill_remaining() { # [ <signal> [ <loop> ] ]
        local sig="${1:-TERM}"
+       local loop="${2:-0}"
+       local run=true
+       local stat
+
        echo -n "Sending $sig to remaining processes ... "
 
-       local my_pid=$$
-       local my_ppid=$(cut -d' ' -f4  /proc/$my_pid/stat)
-       local my_ppisupgraded=
-       grep -q upgraded /proc/$my_ppid/cmdline >/dev/null && {
-               local my_ppisupgraded=1
-       }
-       
-       local stat
-       for stat in /proc/[0-9]*/stat; do
-               [ -f "$stat" ] || continue
-
-               local pid name state ppid rest
-               read pid name state ppid rest < $stat
-               name="${name#(}"; name="${name%)}"
-
-               local cmdline
-               read cmdline < /proc/$pid/cmdline
-
-               # Skip kernel threads
-               [ -n "$cmdline" ] || continue
-
-               if [ $$ -eq 1 ] || [ $my_ppid -eq 1 ] && [ -n "$my_ppisupgraded" ]; then
-                       # Running as init process, kill everything except me
-                       if [ $pid -ne $$ ] && [ $pid -ne $my_ppid ]; then
-                               echo -n "$name "
-                               kill -$sig $pid 2>/dev/null
-                       fi
-               else 
-                       case "$name" in
-                               # Skip essential services
-                               *procd*|*ash*|*init*|*watchdog*|*ssh*|*dropbear*|*telnet*|*login*|*hostapd*|*wpa_supplicant*|*nas*|*relayd*) : ;;
-
-                               # Killable process
-                               *)
-                                       if [ $pid -ne $$ ] && [ $ppid -ne $$ ]; then
-                                               echo -n "$name "
-                                               kill -$sig $pid 2>/dev/null
-                                       fi
-                               ;;
-                       esac
-               fi
+       while $run; do
+               run=false
+               for stat in /proc/[0-9]*/stat; do
+                       [ -f "$stat" ] || continue
+
+                       local pid name state ppid rest
+                       read pid name state ppid rest < $stat
+                       name="${name#(}"; name="${name%)}"
+
+                       # Skip PID1, ourself and our children
+                       [ $pid -ne 1 -a $pid -ne $$ -a $ppid -ne $$ ] || continue
+
+                       local cmdline
+                       read cmdline < /proc/$pid/cmdline
+
+                       # Skip kernel threads
+                       [ -n "$cmdline" ] || continue
+
+                       echo -n "$name "
+                       kill -$sig $pid 2>/dev/null
+
+                       [ $loop -eq 1 ] && run=true
+               done
        done
        echo ""
 }
@@ -175,28 +160,31 @@ v() {
        [ "$VERBOSE" -ge 1 ] && echo "$@"
 }
 
+json_string() {
+       local v="$1"
+       v="${v//\\/\\\\}"
+       v="${v//\"/\\\"}"
+       echo "\"$v\""
+}
+
 rootfs_type() {
        /bin/mount | awk '($3 ~ /^\/$/) && ($5 !~ /rootfs/) { print $5 }'
 }
 
 get_image() { # <source> [ <command> ]
        local from="$1"
-       local conc="$2"
-       local cmd
-
-       case "$from" in
-               http://*|ftp://*) cmd="wget -O- -q";;
-               *) cmd="cat";;
-       esac
-       if [ -z "$conc" ]; then
-               local magic="$(eval $cmd \"$from\" 2>/dev/null | dd bs=2 count=1 2>/dev/null | hexdump -n 2 -e '1/1 "%02x"')"
+       local cat="$2"
+
+       if [ -z "$cat" ]; then
+               local magic="$(dd if="$from" bs=2 count=1 2>/dev/null | hexdump -n 2 -e '1/1 "%02x"')"
                case "$magic" in
-                       1f8b) conc="zcat";;
-                       425a) conc="bzcat";;
+                       1f8b) cat="zcat";;
+                       425a) cat="bzcat";;
+                       *) cat="cat";;
                esac
        fi
 
-       eval "$cmd \"$from\" 2>/dev/null ${conc:+| $conc}"
+       $cat "$from" 2>/dev/null
 }
 
 get_magic_word() {
@@ -320,12 +308,14 @@ default_do_upgrade() {
        fi
 }
 
-do_upgrade() {
+do_upgrade_stage2() {
        v "Performing system upgrade..."
-       if type 'platform_do_upgrade' >/dev/null 2>/dev/null; then
-               platform_do_upgrade "$ARGV"
+       if [ -n "$do_upgrade" ]; then
+               $do_upgrade "$IMAGE"
+       elif type 'platform_do_upgrade' >/dev/null 2>/dev/null; then
+               platform_do_upgrade "$IMAGE"
        else
-               default_do_upgrade "$ARGV"
+               default_do_upgrade "$IMAGE"
        fi
 
        if [ "$SAVE_CONFIG" -eq 1 ] && type 'platform_copy_config' >/dev/null 2>/dev/null; then
@@ -333,12 +323,11 @@ do_upgrade() {
        fi
 
        v "Upgrade completed"
-       [ -n "$DELAY" ] && sleep "$DELAY"
-       ask_bool 1 "Reboot" && {
-               v "Rebooting system..."
-               umount -a
-               reboot -f
-               sleep 5
-               echo b 2>/dev/null >/proc/sysrq-trigger
-       }
+       sleep 1
+
+       v "Rebooting system..."
+       umount -a
+       reboot -f
+       sleep 5
+       echo b 2>/dev/null >/proc/sysrq-trigger
 }
index 9c831df..894964e 100644 (file)
@@ -283,7 +283,16 @@ nand_upgrade_tar() {
 }
 
 # Recognize type of passed file and start the upgrade process
-nand_do_upgrade_stage2() {
+nand_do_upgrade() {
+       if [ -n "$IS_PRE_UPGRADE" ]; then
+               # Previously, nand_do_upgrade was called from the platform_pre_upgrade
+               # hook; this piece of code handles scripts that haven't been
+               # updated. All scripts should gradually move to call nand_do_upgrade
+               # from platform_do_upgrade instead.
+               export do_upgrade=nand_do_upgrade
+               return
+       fi
+
        local file_type=$(identify $1)
 
        if type 'platform_nand_pre_upgrade' >/dev/null 2>/dev/null; then
@@ -299,45 +308,6 @@ nand_do_upgrade_stage2() {
        esac
 }
 
-nand_upgrade_stage2() {
-       [ $1 = "nand" ] && {
-               [ -f "$2" ] && {
-                       touch /tmp/sysupgrade
-
-                       killall -9 telnetd
-                       killall -9 dropbear
-                       killall -9 ash
-
-                       kill_remaining TERM
-                       sleep 3
-                       kill_remaining KILL
-
-                       sleep 1
-
-                       if [ -n "$(rootfs_type)" ]; then
-                               v "Switching to ramdisk..."
-                               run_ramfs ". /lib/functions.sh; include /lib/upgrade; nand_do_upgrade_stage2 $2"
-                       else
-                               nand_do_upgrade_stage2 $2
-                       fi
-                       return 0
-               }
-               echo "Nand upgrade failed"
-               exit 1
-       }
-}
-
-nand_upgrade_stage1() {
-       [ -f /tmp/sysupgrade-nand-path ] && {
-               path="$(cat /tmp/sysupgrade-nand-path)"
-               [ "$SAVE_CONFIG" != 1 -a -f "$CONF_TAR" ] &&
-                       rm $CONF_TAR
-
-               ubus call system nandupgrade "{\"prefix\": \"$RAM_ROOT\", \"path\": \"$path\" }"
-               exit 0
-       }
-}
-
 # Check if passed file is a valid one for NAND sysupgrade. Currently it accepts
 # 3 types of files:
 # 1) UBI - should contain an ubinized image, header is checked for the proper
@@ -364,13 +334,3 @@ nand_do_platform_check() {
 
        return 0
 }
-
-# Start NAND upgrade process
-#
-# $(1): file to be used for upgrade
-nand_do_upgrade() {
-       echo -n $1 > /tmp/sysupgrade-nand-path
-       install_bin /sbin/upgraded
-       ln -s "$RAM_ROOT"/sbin/upgraded /tmp/upgraded
-       nand_upgrade_stage1
-}
diff --git a/package/base-files/files/lib/upgrade/stage2 b/package/base-files/files/lib/upgrade/stage2
new file mode 100755 (executable)
index 0000000..4e2aa3a
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+. /lib/functions.sh
+. /lib/functions/system.sh
+
+export IMAGE="$1"
+COMMAND="$2"
+
+export ARGV="$IMAGE"
+export ARGC=1
+
+export SAVE_CONFIG=1
+export SAVE_PARTITIONS=1
+
+export INTERACTIVE=0
+export VERBOSE=1
+export CONFFILES=/tmp/sysupgrade.conffiles
+export CONF_TAR=/tmp/sysupgrade.tgz
+
+
+[ -f "$CONF_TAR" ] || export SAVE_CONFIG=0
+[ -f /tmp/sysupgrade.always.overwrite.bootdisk.partmap ] && export SAVE_PARTITIONS=0
+
+include /lib/upgrade
+
+
+killall -9 telnetd
+killall -9 dropbear
+killall -9 ash
+
+kill_remaining TERM
+sleep 3
+kill_remaining KILL 1
+
+sleep 1
+
+
+if [ -n "$IMAGE" ] && type 'platform_pre_upgrade' >/dev/null 2>/dev/null; then
+       IS_PRE_UPGRADE=1 platform_pre_upgrade "$IMAGE"
+
+       # Needs to be unset again because of busybox weirdness ...
+       IS_PRE_UPGRADE=
+fi
+
+if [ -n "$(rootfs_type)" ]; then
+       echo "Switching to ramdisk..."
+       run_ramfs "$COMMAND"
+else
+       exec /bin/busybox ash -c "$COMMAND"
+fi
index c095ca8..2d67371 100755 (executable)
@@ -1,4 +1,7 @@
 #!/bin/sh
+
+[ "$1" = "nand" ] && exec /lib/upgrade/stage2 "$2" "$3"
+
 . /lib/functions.sh
 . /lib/functions/system.sh
 
@@ -11,7 +14,6 @@ export VERBOSE=1
 export SAVE_CONFIG=1
 export SAVE_OVERLAY=0
 export SAVE_PARTITIONS=1
-export DELAY=
 export CONF_IMAGE=
 export CONF_BACKUP_LIST=0
 export CONF_BACKUP=
@@ -25,7 +27,6 @@ export TEST=0
 while [ -n "$1" ]; do
        case "$1" in
                -i) export INTERACTIVE=1;;
-               -d) export DELAY="$2"; shift;;
                -v) export VERBOSE="$(($VERBOSE + 1))";;
                -q) export VERBOSE="$(($VERBOSE - 1))";;
                -n) export SAVE_CONFIG=0;;
@@ -50,10 +51,9 @@ done
 export CONFFILES=/tmp/sysupgrade.conffiles
 export CONF_TAR=/tmp/sysupgrade.tgz
 
-export ARGV="$*"
-export ARGC="$#"
+IMAGE="$1"
 
-[ -z "$ARGV" -a -z "$NEED_IMAGE" -o $HELP -gt 0 ] && {
+[ -z "$IMAGE" -a -z "$NEED_IMAGE" -o $HELP -gt 0 ] && {
        cat <<EOF
 Usage: $0 [<upgrade-option>...] <image file or URL>
        $0 [-q] [-i] <backup-command> <file>
@@ -90,7 +90,7 @@ EOF
        exit 1
 }
 
-[ -n "$ARGV" -a -n "$NEED_IMAGE" ] && {
+[ -n "$IMAGE" -a -n "$NEED_IMAGE" ] && {
        cat <<-EOF
                -b|--create-backup and -r|--restore-backup do not perform a firmware upgrade.
                Do not specify both -b|-r and a firmware image.
@@ -136,14 +136,13 @@ sysupgrade_pre_upgrade="fwtool_pre_upgrade"
 
 include /lib/upgrade
 
-[ "$1" = "nand" ] && nand_upgrade_stage2 $@
-
 do_save_conffiles() {
        local conf_tar="${1:-$CONF_TAR}"
 
        [ -z "$(rootfs_type)" ] && {
                echo "Cannot save config while running from ramdisk."
                ask_bool 0 "Abort" && exit
+               rm -f "$conf_tar"
                return 0
        }
        run_hooks "$CONFFILES" $sysupgrade_init_conffiles
@@ -184,8 +183,33 @@ type platform_check_image >/dev/null 2>/dev/null || {
        exit 1
 }
 
+case "$IMAGE" in
+       http://*)
+               wget -O/tmp/sysupgrade.img "$IMAGE"
+               IMAGE=/tmp/sysupgrade.img
+               ;;
+esac
+
+IMAGE="$(readlink -f "$IMAGE")"
+
+case "$IMAGE" in
+       '')
+               echo "Image file not found."
+               exit 1
+               ;;
+       /tmp/*) ;;
+       *)
+               v "Image not in /tmp, copying..."
+               cp -f "$IMAGE" /tmp/sysupgrade.img
+               IMAGE=/tmp/sysupgrade.img
+               ;;
+esac
+
+export ARGV="$IMAGE"
+export ARGC=1
+
 for check in $sysupgrade_image_check; do
-       ( eval "$check \"\$ARGV\"" ) || {
+       ( $check "$IMAGE" ) || {
                if [ $FORCE -eq 1 ]; then
                        echo "Image check '$check' failed but --force given - will update anyway!"
                        break
@@ -211,6 +235,7 @@ elif ask_bool $SAVE_CONFIG "Keep config files over reflash"; then
        [ $TEST -eq 1 ] || do_save_conffiles
        export SAVE_CONFIG=1
 else
+       [ $TEST -eq 1 ] || rm -f "$CONF_TAR"
        export SAVE_CONFIG=0
 fi
 
@@ -218,28 +243,18 @@ if [ $TEST -eq 1 ]; then
        exit 0
 fi
 
-run_hooks "" $sysupgrade_pre_upgrade
-
-# Some platforms/devices may want different sysupgrade process, e.g. without
-# killing processes yet or calling ubus system upgrade method.
-# This is needed e.g. on NAND devices where we just want to trigger stage1 at
-# this point.
-if type 'platform_pre_upgrade' >/dev/null 2>/dev/null; then
-       platform_pre_upgrade "$ARGV"
+if [ $SAVE_PARTITIONS -eq 0 ]; then
+       touch /tmp/sysupgrade.always.overwrite.bootdisk.partmap
+else
+       rm -f /tmp/sysupgrade.always.overwrite.bootdisk.partmap
 fi
 
-ubus call system upgrade
-touch /tmp/sysupgrade
-
-if [ ! -f /tmp/failsafe ] ; then
-       kill_remaining TERM
-       sleep 3
-       kill_remaining KILL
-fi
+run_hooks "" $sysupgrade_pre_upgrade
 
-if [ -n "$(rootfs_type)" ]; then
-       v "Switching to ramdisk..."
-       run_ramfs '. /lib/functions.sh; include /lib/upgrade; do_upgrade'
-else
-       do_upgrade
-fi
+install_bin /sbin/upgraded
+v "Commencing upgrade. All shell sessions will be closed now."
+ubus call system sysupgrade "{
+       \"prefix\": \"$RAM_ROOT\",
+       \"path\": $(json_string "$IMAGE"),
+       \"command\": \". /lib/functions.sh; include /lib/upgrade; do_upgrade_stage2\"
+}"