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

0:扯淡

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

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


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

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


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

3:脚本分析

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

  1. 上面就是通过openwrt系统提供的sysupgrade命令来对系统进行更新的。  
  2. root@OpenWrt:~# which sysupgrade  
  3. /sbin/sysupgrade  
  4. 看看脚本中的主要内容,  
  5. include /lib/upgrade  
  6.   
  7. do_save_conffiles() {  
  8.     local conf_tar="${1:-$CONF_TAR}"  
  9.   
  10.     [ -z "$(rootfs_type)" ] && {  
  11.         echo "Cannot save config while running from ramdisk."  
  12.         ask_bool 0 "Abort" && exit  
  13.         return 0  
  14.     }  
  15.     run_hooks "$CONFFILES" $sysupgrade_init_conffiles  
  16.     ask_bool 0 "Edit config file list" && vi "$CONFFILES"  
  17.   
  18.     v "Saving config files..."  
  19.     [ "$VERBOSE" -gt 1 ] && TAR_V="v" || TAR_V=""  
  20.     tar c${TAR_V}zf "$conf_tar" -T "$CONFFILES" 2>/dev/null  
  21. }  
  22. 其中run_hooks函数的定义如下,其主要是执行钩子函数,即,第一个参数为:函数参数,第二个参数之后为:调用函数。  
  23.   
  24. run_hooks() {  
  25.     local arg="$1"; shift  
  26.     for func in "$@"; do  
  27.         eval "$func $arg"  
  28.     done  
  29. }  
  30.     run_hooks "$CONFFILES" $sysupgrade_init_conffiles 的作用就是将需要保存的文件名字保存到"$CONFFILES"文件中,保存那些文件了  
  31.     定义在add_uci_conffiles()和add_overlayfiles()函数中。  
  32. add_uci_conffiles() {  
  33.     local file="$1"  
  34.     ( find $(sed -ne '/^[[:space:]]*$/d; /^#/d; p' \  
  35.         /etc/sysupgrade.conf /lib/upgrade/keep.d/* 2>/dev/null) \  
  36.         -type f 2>/dev/null;  
  37.       opkg list-changed-conffiles ) | sort -u > "$file"  
  38.     return 0  
  39. }  
  40.   
  41. add_overlayfiles() {  
  42.     local file="$1"  
  43.     find /overlay/etc/ -type f | sed \  
  44.         -e 's,^/overlay/,/,' \  
  45.         -e '\,/META_[a-zA-Z0-9]*$,d' \  
  46.         -e '\,/functions.sh$,d' \  
  47.         -e '\,/[^/]*-opkg$,d' \  
  48.     > "$file"  
  49.     return 0  
  50. }  
  51. 默认保存的文件内容如下:如果需要对自定  
  52. etc/wifidog.conf  
  53. etc/sysctl.conf  
  54. etc/squid/squid.conf  
  55. etc/shells  
  56. etc/rc.local  
  57. etc/profile  
  58. etc/passwd  
  59. etc/inittab  
  60. etc/hosts  
  61. etc/group  
  62. etc/dropbear/dropbear_rsa_host_key  
  63. etc/dropbear/dropbear_dss_host_key  
  64. etc/crontabs/root  
  65. etc/config/wifidog  
  66. etc/config/uhttpd  
  67. etc/config/system  
  68. etc/config/redirect  
  69. etc/config/network  
  70. etc/config/ip  
  71. etc/config/firewall  
  72. etc/config/dropbear  
  73. etc/config/dhcp  
  74. 函数ask_bool()实现是否与命令行进行交互式的处理。  
  75.     v "Saving config files..."  
  76.     [ "$VERBOSE" -gt 1 ] && TAR_V="v" || TAR_V=""  
  77.     tar c${TAR_V}zf "$conf_tar" -T "$CONFFILES" 2>/dev/null  
  78. 实现对上面的数据文件进行压缩处理,其文件名称为:/tmp/sysupgrade.tgz  
  79.   
  80. if [ -n "$CONF_IMAGE" ]; then  
  81.         case "$(get_magic_word $CONF_IMAGE cat)" in  
  82.                 # .gz files  
  83.                 1f8b) ;;  
  84.                 *)  
  85.                         echo "Invalid config file. Please use only .tar.gz files"  
  86.                         exit 1  
  87.                 ;;  
  88.         esac  
  89.         get_image "$CONF_IMAGE" "cat" > "$CONF_TAR"  
  90.         export SAVE_CONFIG=1  
  91. elif ask_bool $SAVE_CONFIG "Keep config files over reflash"; then  
  92.         do_save_conffiles  
  93.         export SAVE_CONFIG=1  
  94. else  
  95.         export SAVE_CONFIG=0  
  96. fi  
  97. 上面的条件判断执行的是elif,即默认是保存更改过的配置文件。 export SAVE_CONFIG=1  
  98.   
  99. 其中语句  
  100. kill_remaining TERM  
  101. sleep 3  
  102. kill_remaining KILL  
  103. 实现对进程的term和kill操作  
  104. kill_remaining() { # [ <signal> ]  
  105.     local sig="${1:-TERM}"  
  106.     echo -n "Sending $sig to remaining processes ... "  
  107.   
  108.     local stat  
  109.     for stat in /proc/[0-9]*/stat; do  
  110.         [ -f "$stat" ] || continue  
  111.   
  112.         local pid name state ppid rest  
  113.         read pid name state ppid rest < $stat  
  114.         name="${name#(}"; name="${name%)}"  
  115.   
  116.         local cmdline  
  117.         read cmdline < /proc/$pid/cmdline  
  118.   
  119.         # Skip kernel threads   
  120.         [ -n "$cmdline" ] || continue  
  121.   
  122.         case "$name" in  
  123.             # Skip essential services  
  124.             *ash*|*init*|*watchdog*|*ssh*|*dropbear*|*telnet*|*login*|*hostapd*|*wpa_supplicant*) : ;;  
  125.   
  126.             # Killable process  
  127.             *)  
  128.                 if [ $pid -ne $$ ] && [ $ppid -ne $$ ]; then  
  129.                     echo -n "$name "  
  130.                     kill -$sig $pid 2>/dev/null  
  131.                 fi  
  132.             ;;  
  133.         esac  
  134.     done  
  135.     echo ""  
  136. }  
  137.   
  138. 在升级操作之前有一个run_ramfs(),将一个最小能运行的系统mount到内存中去。为后的操作提供运行环境。  
  139. run_ramfs() { # <command> [...]  
  140.     install_bin /bin/busybox /bin/ash /bin/sh /bin/mount /bin/umount        \  
  141.         /sbin/pivot_root /usr/bin/wget /sbin/reboot /bin/sync /bin/dd   \  
  142.         /bin/grep /bin/cp /bin/mv /bin/tar /usr/bin/md5sum "/usr/bin/[" \  
  143.         /bin/vi /bin/ls /bin/cat /usr/bin/awk /usr/bin/hexdump          \  
  144.         /bin/sleep /bin/zcat /usr/bin/bzcat /usr/bin/printf /usr/bin/wc  
  145.   
  146.     install_bin /sbin/mtd  
  147.     for file in $RAMFS_COPY_BIN; do  
  148.         install_bin $file  
  149.     done  
  150.     install_file /etc/resolv.conf /lib/functions.sh /lib/functions.sh /lib/upgrade/*.sh $RAMFS_COPY_DATA  
  151.   
  152.     pivot $RAM_ROOT /mnt || {  
  153.         echo "Failed to switch over to ramfs. Please reboot."  
  154.         exit 1  
  155.     }  
  156.   
  157.     mount -o remount,ro /mnt  
  158.     umount -l /mnt  
  159.   
  160.     grep /overlay /proc/mounts > /dev/null && {  
  161.         mount -o remount,ro /overlay  
  162.         umount -l /overlay  
  163.     }  
  164.   
  165.     # spawn a new shell from ramdisk to reduce the probability of cache issues  
  166.     exec /bin/busybox ash -c "$*"  
  167. }  
  168. 在整整操作之前先看看 mtd,sysupgrade 更新过程实际使用的就是mtd命令  
  169. root@OpenWrt:/overlay/etc#mtd   
  170. Usage: mtd [<options> ...] <command> [<arguments> ...] <device>[:<device>...]  
  171.   
  172. The device is in the format of mtdX (eg: mtd4) or its label.  
  173. mtd recognizes these commands:  
  174.         unlock                  unlock the device  
  175.         refresh                 refresh mtd partition  
  176.         erase                   erase all data on device  
  177.         write <imagefile>|-     write <imagefile> (use - for stdin) to device  
  178.         jffs2write <file>       append <file> to the jffs2 partition on the device  
  179. Following options are available:  
  180.         -q                      quiet mode (once: no [w] on writing,  
  181.                                            twice: no status messages)  
  182.         -n                      write without first erasing the blocks  
  183.         -r                      reboot after successful command  
  184.         -f                      force write without trx checks  
  185.         -e <device>             erase <device> before executing the command  
  186.         -d <name>               directory for jffs2write, defaults to "tmp"  
  187.         -j <name>               integrate <file> into jffs2 data when writing an image  
  188.         -p                      write beginning at partition offset  
  189.   
  190. Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards  
  191.          mtd -r write linux.trx linux  



  1. do_upgrade() {  
  2.     v "Performing system upgrade..."  
  3.     if type 'platform_do_upgrade' >/dev/null 2>/dev/null; then  
  4.         platform_do_upgrade "$ARGV"  
  5.     else  
  6.         default_do_upgrade "$ARGV"  
  7.     fi  
  8.     [ "$SAVE_CONFIG" -eq 1 -a -n "$USE_REFRESH" ] && {  
  9.         v "Refreshing partitions"  
  10.         if type 'platform_refresh_partitions' >/dev/null 2>/dev/null; then  
  11.             platform_refresh_partitions  
  12.         else  
  13.             refresh_mtd_partitions  
  14.         fi  
  15.         if type 'platform_copy_config' >/dev/null 2>/dev/null; then  
  16.             platform_copy_config  
  17.         else  
  18.             jffs2_copy_config  
  19.         fi  
  20.     }  
  21.     v "Upgrade completed"  
  22.     [ -n "$DELAY" ] && sleep "$DELAY"  
  23.     ask_bool 1 "Reboot" && {  
  24.         v "Rebooting system..."  
  25.         reboot -f  
  26.         sleep 5  
  27.         echo b 2>/dev/null >/proc/sysrq-trigger  
  28.     }  
  29. }  
  30.   
  31. default_do_upgrade() {  
  32.     sync  
  33.     if [ "$SAVE_CONFIG" -eq 1 -a -z "$USE_REFRESH" ]; then  
  34.         get_image "$1" | mtd -j "$CONF_TAR" write - "${PART_NAME:-image}"  
  35.     else  
  36.         get_image "$1" | mtd write - "${PART_NAME:-image}"  
  37.     fi  
  38. }  
  39.   
  40. platform_do_upgrade() {  
  41.     local rootfs="$(x86_get_rootfs)"  
  42.     local rootfsdev="${rootfs##*:}"  
  43.   
  44.     sync  
  45.     [ -b ${rootfsdev%[0-9]} ] && get_image "$@" | dd of=${rootfsdev%[0-9]} bs=4096 conv=fsync  
  46.     sleep 1  
  47. }  
  48.   
  49.   
  50. x86_get_rootfs() {  
  51.     local rootfsdev  
  52.     local rootfstype  
  53.       
  54.     rootfstype="$(awk 'BEGIN { RS=" "; FS="="; } ($1 == "rootfstype") { print $2 }' < /proc/cmdline)"  
  55.     case "$rootfstype" in  
  56.         squashfs|jffs2)  
  57.             rootfsdev="$(awk 'BEGIN { RS=" "; FS="="; } ($1 == "block2mtd.block2mtd") { print substr($2,1,index($2, ",")-1) }' < /proc/cmdline)";;  
  58.         ext4)  
  59.             rootfsdev="$(awk 'BEGIN { RS=" "; FS="="; } ($1 == "root") { print $2 }' < /proc/cmdline)";;  
  60.     esac  
  61.           
  62.     echo "$rootfstype:$rootfsdev"  
  63. }  
  64.   
  65. jffs2_copy_config() {  
  66.     if grep rootfs_data /proc/mtd >/dev/null; then  
  67.         # squashfs+jffs2  
  68.         mtd -e rootfs_data jffs2write "$CONF_TAR" rootfs_data  
  69.     else  
  70.         # jffs2  
  71.         mtd jffs2write "$CONF_TAR" rootfs  
  72.     fi  
  73. }  
  74. refresh_mtd_partitions() {  
  75.     mtd refresh rootfs  
  76. }  

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

没有更多推荐了,返回首页