openwrt系统 sysupgrade 命令执行过程分析

对这个openwrt的细节方面了解的还比较欠缺,故从实际中的经常用的功能说起,研究研究,可以了解更多的细节。

在openwrt的页面中已经涉及到下面的内容如:
1.jpg

其中在更新系统时候有进行配置保存及恢复的功能。

1:sysupgrade是的交互式使用命令如下:
2.jpg

经过上面的系统更新之后,会保存上面显示的配置文件中的内容,故更新系统不会影响配置的丢失。

2:脚本分析

脚本有点多,其中分析主要的脚本就可以了,其他的加一些打印信息可以帮助分析执行流程。

上面就是通过openwrt系统提供的sysupgrade命令来对系统进行更新的。  
root@OpenWrt:~# which sysupgrade  
/sbin/sysupgrade  
看看脚本中的主要内容,  
include /lib/upgrade  

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  
        return 0  
    }  
    run_hooks "$CONFFILES" $sysupgrade_init_conffiles  
    ask_bool 0 "Edit config file list" && vi "$CONFFILES"  

    v "Saving config files..."  
    [ "$VERBOSE" -gt 1 ] && TAR_V="v" || TAR_V=""  
    tar c${TAR_V}zf "$conf_tar" -T "$CONFFILES" 2>/dev/null  
}  
其中run_hooks函数的定义如下,其主要是执行钩子函数,即,第一个参数为:函数参数,第二个参数之后为:调用函数。  

run_hooks() {  
    local arg="$1"; shift  
    for func in "$@"; do  
        eval "$func $arg"  
    done  
}  
    run_hooks "$CONFFILES" $sysupgrade_init_conffiles 的作用就是将需要保存的文件名字保存到"$CONFFILES"文件中,保存那些文件了  
    定义在add_uci_conffiles()和add_overlayfiles()函数中。  
add_uci_conffiles() {  
    local file="$1"  
    ( find $(sed -ne '/^[[:space:]]*$/d; /^#/d; p' \  
        /etc/sysupgrade.conf /lib/upgrade/keep.d/* 2>/dev/null) \  
        -type f 2>/dev/null;  
      opkg list-changed-conffiles ) | sort -u > "$file"  
    return 0  
}  

add_overlayfiles() {  
    local file="$1"  
    find /overlay/etc/ -type f | sed \  
        -e 's,^/overlay/,/,' \  
        -e '\,/META_[a-zA-Z0-9]*$,d' \  
        -e '\,/functions.sh$,d' \  
        -e '\,/[^/]*-opkg$,d' \  
    > "$file"  
    return 0  
}  
默认保存的文件内容如下:如果需要对自定  
etc/wifidog.conf  
etc/sysctl.conf  
etc/squid/squid.conf  
etc/shells  
etc/rc.local  
etc/profile  
etc/passwd  
etc/inittab  
etc/hosts  
etc/group  
etc/dropbear/dropbear_rsa_host_key  
etc/dropbear/dropbear_dss_host_key  
etc/crontabs/root  
etc/config/wifidog  
etc/config/uhttpd  
etc/config/system  
etc/config/redirect  
etc/config/network  
etc/config/ip  
etc/config/firewall  
etc/config/dropbear  
etc/config/dhcp  
函数ask_bool()实现是否与命令行进行交互式的处理。  
    v "Saving config files..."  
    [ "$VERBOSE" -gt 1 ] && TAR_V="v" || TAR_V=""  
    tar c${TAR_V}zf "$conf_tar" -T "$CONFFILES" 2>/dev/null  
实现对上面的数据文件进行压缩处理,其文件名称为:/tmp/sysupgrade.tgz  

if [ -n "$CONF_IMAGE" ]; then  
        case "$(get_magic_word $CONF_IMAGE cat)" in  
                # .gz files  
                1f8b) ;;  
                *)  
                        echo "Invalid config file. Please use only .tar.gz files"  
                        exit 1  
                ;;  
        esac  
        get_image "$CONF_IMAGE" "cat" > "$CONF_TAR"  
        export SAVE_CONFIG=1  
elif ask_bool $SAVE_CONFIG "Keep config files over reflash"; then  
        do_save_conffiles  
        export SAVE_CONFIG=1  
else  
        export SAVE_CONFIG=0  
fi  
上面的条件判断执行的是elif,即默认是保存更改过的配置文件。 export SAVE_CONFIG=1  

其中语句  
kill_remaining TERM  
sleep 3  
kill_remaining KILL  
实现对进程的term和kill操作  
kill_remaining() { # [ <signal> ]  
    local sig="${1:-TERM}"  
    echo -n "Sending $sig to remaining processes ... "  

    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  

        case "$name" in  
            # Skip essential services  
            *ash*|*init*|*watchdog*|*ssh*|*dropbear*|*telnet*|*login*|*hostapd*|*wpa_supplicant*) : ;;  

            # Killable process  
            *)  
                if [ $pid -ne $$ ] && [ $ppid -ne $$ ]; then  
                    echo -n "$name "  
                    kill -$sig $pid 2>/dev/null  
                fi  
            ;;  
        esac  
    done  
    echo ""  
}  

在升级操作之前有一个run_ramfs(),将一个最小能运行的系统mount到内存中去。为后的操作提供运行环境。  
run_ramfs() { # <command> [...]  
    install_bin /bin/busybox /bin/ash /bin/sh /bin/mount /bin/umount        \  
        /sbin/pivot_root /usr/bin/wget /sbin/reboot /bin/sync /bin/dd   \  
        /bin/grep /bin/cp /bin/mv /bin/tar /usr/bin/md5sum "/usr/bin/[" \  
        /bin/vi /bin/ls /bin/cat /usr/bin/awk /usr/bin/hexdump          \  
        /bin/sleep /bin/zcat /usr/bin/bzcat /usr/bin/printf /usr/bin/wc  

    install_bin /sbin/mtd  
    for file in $RAMFS_COPY_BIN; do  
        install_bin $file  
    done  
    install_file /etc/resolv.conf /lib/functions.sh /lib/functions.sh /lib/upgrade/*.sh $RAMFS_COPY_DATA  

    pivot $RAM_ROOT /mnt || {  
        echo "Failed to switch over to ramfs. Please reboot."  
        exit 1  
    }  

    mount -o remount,ro /mnt  
    umount -l /mnt  

    grep /overlay /proc/mounts > /dev/null && {  
        mount -o remount,ro /overlay  
        umount -l /overlay  
    }  

    # spawn a new shell from ramdisk to reduce the probability of cache issues  
    exec /bin/busybox ash -c "$*"  
}  
在整整操作之前先看看 mtd,sysupgrade 更新过程实际使用的就是mtd命令  
root@OpenWrt:/overlay/etc#mtd   
Usage: mtd [<options> ...] <command> [<arguments> ...] <device>[:<device>...]  

The device is in the format of mtdX (eg: mtd4) or its label.  
mtd recognizes these commands:  
        unlock                  unlock the device  
        refresh                 refresh mtd partition  
        erase                   erase all data on device  
        write <imagefile>|-     write <imagefile> (use - for stdin) to device  
        jffs2write <file>       append <file> to the jffs2 partition on the device  
Following options are available:  
        -q                      quiet mode (once: no [w] on writing,  
                                           twice: no status messages)  
        -n                      write without first erasing the blocks  
        -r                      reboot after successful command  
        -f                      force write without trx checks  
        -e <device>             erase <device> before executing the command  
        -d <name>               directory for jffs2write, defaults to "tmp"  
        -j <name>               integrate <file> into jffs2 data when writing an image  
        -p                      write beginning at partition offset  

Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards  
         mtd -r write linux.trx linux  


do_upgrade() {  
    v "Performing system upgrade..."  
    if type 'platform_do_upgrade' >/dev/null 2>/dev/null; then  
        platform_do_upgrade "$ARGV"  
    else  
        default_do_upgrade "$ARGV"  
    fi  
    [ "$SAVE_CONFIG" -eq 1 -a -n "$USE_REFRESH" ] && {  
        v "Refreshing partitions"  
        if type 'platform_refresh_partitions' >/dev/null 2>/dev/null; then  
            platform_refresh_partitions  
        else  
            refresh_mtd_partitions  
        fi  
        if type 'platform_copy_config' >/dev/null 2>/dev/null; then  
            platform_copy_config  
        else  
            jffs2_copy_config  
        fi  
    }  
    v "Upgrade completed"  
    [ -n "$DELAY" ] && sleep "$DELAY"  
    ask_bool 1 "Reboot" && {  
        v "Rebooting system..."  
        reboot -f  
        sleep 5  
        echo b 2>/dev/null >/proc/sysrq-trigger  
    }  
}  

default_do_upgrade() {  
    sync  
    if [ "$SAVE_CONFIG" -eq 1 -a -z "$USE_REFRESH" ]; then  
        get_image "$1" | mtd -j "$CONF_TAR" write - "${PART_NAME:-image}"  
    else  
        get_image "$1" | mtd write - "${PART_NAME:-image}"  
    fi  
}  

platform_do_upgrade() {  
    local rootfs="$(x86_get_rootfs)"  
    local rootfsdev="${rootfs##*:}"  

    sync  
    [ -b ${rootfsdev%[0-9]} ] && get_image "$@" | dd of=${rootfsdev%[0-9]} bs=4096 conv=fsync  
    sleep 1  
}  


x86_get_rootfs() {  
    local rootfsdev  
    local rootfstype  

    rootfstype="$(awk 'BEGIN { RS=" "; FS="="; } ($1 == "rootfstype") { print $2 }' < /proc/cmdline)"  
    case "$rootfstype" in  
        squashfs|jffs2)  
            rootfsdev="$(awk 'BEGIN { RS=" "; FS="="; } ($1 == "block2mtd.block2mtd") { print substr($2,1,index($2, ",")-1) }' < /proc/cmdline)";;  
        ext4)  
            rootfsdev="$(awk 'BEGIN { RS=" "; FS="="; } ($1 == "root") { print $2 }' < /proc/cmdline)";;  
    esac  

    echo "$rootfstype:$rootfsdev"  
}  

jffs2_copy_config() {  
    if grep rootfs_data /proc/mtd >/dev/null; then  
        # squashfs+jffs2  
        mtd -e rootfs_data jffs2write "$CONF_TAR" rootfs_data  
    else  
        # jffs2  
        mtd jffs2write "$CONF_TAR" rootfs  
    fi  
}  
refresh_mtd_partitions() {  
    mtd refresh rootfs  
}  

其中需要注意的是不同的平台如,Atheros和x86的各个平台的执行过程有所不同,最终一点是需要将$CONF_TAR保存到系统的rootfs_data或者rootfs_data分区数据中去。

转自:http://blog.csdn.net/stone8761/article/details/39692769

本文章由 http://www.wifidog.pro/2015/01/30/openwrt-sysupgrade.html 整理编辑,转载请注明出处

标签: wifidog认证 wifidog安装 wifidog原理 wifidog分析 wifidog配置 wifidog流程 wifidog服务器 wifidog-ddwrt wifidog openwrt