From: Matthias Schiffer Date: Sat, 22 Apr 2017 00:54:50 +0200 Subject: base-files: always use staged sysupgrade 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 diff --git a/package/base-files/files/lib/upgrade/common.sh b/package/base-files/files/lib/upgrade/common.sh index 8c1b3b180e1ecfdd2c106dd3b8853cd487ff89b4..ef9aac709fc1619ba15eeb0aeb4a9a6a372369b2 100644 --- a/package/base-files/files/lib/upgrade/common.sh +++ b/package/base-files/files/lib/upgrade/common.sh @@ -56,7 +56,6 @@ run_ramfs() { # [...] /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() { # [...] exec /bin/busybox ash -c "$*" } -kill_remaining() { # [ ] +kill_remaining() { # [ [ ] ] 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() { # [ ] 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() { @@ -331,12 +319,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 @@ -344,12 +334,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 } diff --git a/package/base-files/files/lib/upgrade/nand.sh b/package/base-files/files/lib/upgrade/nand.sh index 6bd2005344c081df20e5a330a69e49e37225c39f..1e69c8f9657b39adf2a2c33bd9bac9303bcbc3d7 100644 --- a/package/base-files/files/lib/upgrade/nand.sh +++ b/package/base-files/files/lib/upgrade/nand.sh @@ -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 index 0000000000000000000000000000000000000000..4e2aa3a23c3bab07a795762a30a4d4f701081934 --- /dev/null +++ b/package/base-files/files/lib/upgrade/stage2 @@ -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 diff --git a/package/base-files/files/sbin/sysupgrade b/package/base-files/files/sbin/sysupgrade index 71c7faa6dbc485ff33e776ff087e1aca58cfb0b0..fa08f4194daa0ee74758b9f49a23fe8dcb4b933d 100755 --- a/package/base-files/files/sbin/sysupgrade +++ b/package/base-files/files/sbin/sysupgrade @@ -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 <...] $0 [-q] [-i] @@ -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. @@ -155,14 +155,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 @@ -203,8 +202,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 @@ -230,6 +254,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 @@ -237,28 +262,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\" +}"