openwrt luci固件升级流程

文档说明:

本文档简述了openwrt sdk的升级功能流程,从页面传入升级文件到升级文件检测,再到调用升级脚本进行升级,升级完成后,进行系统重启。最后简述了如何添加升级文件标识,标识包括自己定义的字段,以及升级软件的md5值。达到防止用户用其他openwrt sdk编译出来的固件刷新我们的系统,以及防止升级软件被破坏。

 

文档需要用到的文件及在sdk中的路径(某些文件路径可能随项目的不同而改动):

 

文档正文:

1.从页面接受传过来的升级文件

作为整个流程的开始,功能实现在文件system.lua中。

这是一个lua文件,很容易在function index函数中找到系统升级功能的入口函数:action_flashops。在这个函数中首先通过fp = io.open(image_tmp, “w”)打开升级临时文件:/tmp/firmware.img,打开后通过fp:write(chunk)写入页面传进来的升级文件。到这里,接受升级文件完成 。

 

2.检测升级文件的合法性

实现同样在文件system.lua中。

在函数image_supported()中进行检测,这个函数通过image magic number来检测升级文件是否合法。函数image_supported()会调用platform.sh脚本中的platform_check_image函数,

platform_check_image函数调用ramips.sh脚本中的ramips_board_name函数,获取board name,ramips_board_name函数从文件 /tmp/sysinfo/board_name 中获取board name,若没有则为unknown,并返回给调用者,我用的板是ralink-soc。platform_check_image函数继续调用common.sh脚本中的get_magic_long函数,用以获取升级文件magic,就是升级文件前4位。get_magic_long函数调用common.sh脚本中的get_image函数用以获取文件/tmp/firmware.img内容,其实就是cat /tmp/firmware.img. 获取到的内容,通过dd bs=4 count=1,来获取前4位,最后通过hexdump -v -n 4 -e ‘1/1 “%02x”‘处理以十六进制编码返回调用者。获取到的升级文件magic,在platform_check_image函数中与27051956做对比,这个值是在固件编译的时候已经定好了的。如果相等,就是合法的升级文件,继续升级动作;不相等则为非法升级文件,做一些后续处理并终止升级动作。到这里检测升级文件合法性完成。

 

3.检测升级文件不合法后的处理

实现同样在文件system.lua中。

检测到不合法后,通过nixio.fs.unlink(image_tmp)来删除临时文件/tmp/firmware.img,并通过image_invalid = true,设置检测失败,用以通知页面显示提示信息。终止升级。处理完成。

 

4.检测升级文件合法后的处理

实现同样在文件system.lua中。

检测到升级文件合法后,会获取一些升级文件相关的信息,用以在页面显示:调用image_checksum(),获取checksum;调用storage_size(),获取可用空间大小;调用nixio.fs.stat(image_tmp).size,获取升级文件大小;以及页面传过来的是否保存配置的值;其中,image_checksum()函数用的是md5sum命令,storage_size()函数是在系统文件/proc/mtd中找到firmware分区大小。

接下来如果用户选择进行升级文件,则会现在页面上打印一些提示信息,用于提示用户:正在升级,不要断开电源等等。

文件system.lua最后的处理就是调用升级脚本:

 

这条语句,先清除dropbear 和uhttpd进程,再等待1秒,最后调用升级脚本sysupgrade,传过去的参数就是keep:是否要保存配置;image_tmp:升级文件/tmp/firmware.img。

OK,到这里system.lua文件中关于升级前的准备工作都完成了,视线请转到升级脚本sysupgrade上。

 

5.运行升级脚本

实现在文件sysupgrade中。

脚本开始,像所有的主体处理程序一样,会对传进来的参数进行处理。下面对这些参数的介绍:

 

这些参数的使用在脚本中都有介绍,不再多讲。

接下来:[ -z “$ARGV” -a -z “$NEED_IMAGE” -o $HELP -gt 0 ],意思是:如果没有升级文件参数,且没有命令行参数-b(create-backup),-r(restore-backup),或者带有-h(help)参数,则打印帮助信息。这个条件为真的话,会在终端打印帮助信息,退出脚本。

 

接下来:[ -n “$ARGV” -a -n “$NEED_IMAGE” ],意思是:不要指定-b或-r(创建配置、恢复配置)的同时,指定升级文件。为真的话,打印提示信息,退出脚本。

 

接下来:[ “$CONF_BACKUP” = “-” ] && export VERBOSE=0,意思是:选择备份配置但传进来的文件为“-”时,不打印备份文件时的过程。

下面展示一下-v选项的作用:

带-v时的升级过程:

 

不带-v时的升级过程:

 

继续分析:

 

如果需要列出配置文件列表,就 调用add_uci_conffiles函数生成列表,并打印到终端。函数add_uci_conffiles(),找出需要保存的配置文件。通过在文件/etc/sysupgrade.conf中 ,/lib/upgrade/keep.d/*目录下,以及命令opkg list-changed-conffiles的输出中,找出配置 文件,其中opkg list-changed-conffiles 列出用户修改的配置文件。

 

接下来:

if [ -n “$CONF_BACKUP” ]; then

do_save_conffiles “$CONF_BACKUP”

exit $?

fi

如果需要创建配置备份文件,则调用函数do_save_conffiles,生成配置文件。函数do_save_conffiles(),打包上一部列出的 配置文件 。

 

接下来:

if [ -n “$CONF_RESTORE” ]; then ###需要恢复配置

if [ “$CONF_RESTORE” != “-” ] && [ ! -f “$CONF_RESTORE” ]; then ###判断所需要的配置文件是否存在

echo “Backup archive ‘$CONF_RESTORE’ not found.”

exit 1

fi

 

[ “$VERBOSE” -gt 1 ] && TAR_V=”v” || TAR_V=””

tar -C / -x${TAR_V}zf “$CONF_RESTORE”

exit $?

fi

经过一些判断,解压配置文件 包。

接下来:

type platform_check_image,检测platform_check_image命令是否存在,为了 下步做准备。找不到的话,脚本 退出,升级终止。

 

接下来:

for check in $sysupgrade_image_check;

do( eval “$check \”\$ARGV\”” ) || {  ###通过board name 和image magic number来判断升级文件是否合法

if [ $FORCE -eq 1 ]; then  ####检测失败了,但是因为设置了-F选项,强制升级,停止检测

echo “Image check ‘$check’ failed but –force given – will update anyway!”

break

else   ###检测失败,且不要求强制升级,脚本退出,停止升级

echo “Image check ‘$check’ failed.”

exit 1

fi

}

done

做升级文件的检测,$sysupgrade_image_check就是platform_check_image,这个 检测在升级开始的 时候,已经做过了 ,这里又做了一遍。如果检测失败了,但是设置了-F选项,强制升级,如果没设置,就脚本退出,停止升级。

 

接下来:

if [ -n “$CONF_IMAGE” ]; then  ####需要从文件中恢复配置

case “$(get_magic_word $CONF_IMAGE cat)” in   ####获取文件内容,并拷贝2个字节,且转换为16进制输出

# .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  ###默认升级保存配置

[ $TEST -eq 1 ] || do_save_conffiles

export SAVE_CONFIG=1

else

export SAVE_CONFIG=0

fi

这段的 意思是,如果需要从文件中恢复配置,就开始处理。这个文件是从命令行参数中传进来的。get_magic_word函数在文件common.sh中,这个函数与前面所讲的get_magic_long函数实现基本一样,所 不同的是get_magic_word函数利用dd bs=2 count=1,获取头2个字节,而不是4个。

接下来“

if [ $TEST -eq 1 ]; then

exit 0

fi

这段的意思是:如果设置了-T选项,因为只是做了升级文件和.tar.gz配置文件的检测,不需要升级,脚本退出,停止升级。

 

接下来:

run_hooks “” $sysupgrade_pre_upgrade

这句的意思是:运行函数sysupgrade_pre_upgrade。先介绍下run_hooks函数,定义在文件common.sh中。

run_hooks() {

local arg=”$1″; shift

for func in “$@”; do

eval “$func $arg”

done

}

run_hooks函数是钩子函数,其中传过来的第一个参数是函数运行的参数,其余参数为要运行的函数。

再介绍sysupgrade_pre_upgrade函数。定义在platform.sh文件中。

#append sysupgrade_pre_upgrade  disable_watchdog

 

disable_watchdog() {

killall watchdog

( ps | grep -v ‘grep’ | grep ‘/dev/watchdog’ ) && {

echo ‘Could not disable watchdog’

return 1

}

}

其实就是在升级前,去清除watchdog进程。不过这个函数被注释掉了,所以不用管。

 

接下来:

ubus call system upgrade

openwrt ubus: 为了在OpenWrt中提供守护进程和应用程序间的通讯,开发了ubus项目工程。它包含了守护进程、库以及一些额外的帮助程序。

核心部分是ubusd守护进程,它提供了其他守护进程将自己注册以及发送消息的接口。因为这个,接口通过使用Unix socket来实现,并使用TLV(type-length-value)消息。

用法:Usage: ubus [<options>] <command> [arguments…]

 

这句的意思是:调用注册到ubus进程的system路径下的update方法,update方法设置了upgrade_running变量值为1,使得在ubus上注册的服务退出时无需等待。

 

接下来:

kill_remaining TERM

sleep 3

kill_remaining KILL

kill_remaining函数,这个函数定义在common.sh文件中,就是发送信号给一些应用层进程。

用以升级前清除进程。

 

接下来:

run_ramfs ‘. /lib/functions.sh; include /lib/upgrade; do_upgrade’

是sysupgrade脚本的最后一句。也是开始升级的地方。

run_ramfs函数定义在common.sh文件中,先是安装一些命令到/tmp/root下,再在/tmp/root下构建一个临时文件系统。最后调用exec /bin/busybox ash -c “$*”,来调用do_upgrade函数。do_upgrade函数调用platform_do_upgrade函数,platform_do_upgrade函数调用default_do_upgrade函数。

 

default_do_upgrade() {

sync

if [ “$SAVE_CONFIG” -eq 1 ]; then

get_image “$1” | mtd $MTD_CONFIG_ARGS -j “$CONF_TAR” write – “${PART_NAME:-image}”

else

get_image “$1” | mtd write – “${PART_NAME:-image}”

fi

}

介绍一下这个函数,sync命令的作用是,将有关文件系统的存储器常驻信息送入物理介质内,以确保文件系统的完整性。然后就是根据是否需要保存配置去分别调用mtd命令,去完成升级。

 

接下来:

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

升级完了,需要重启了。整个升级流程到这里,就完成了。

6.添加升级文件标识以及升级文件md5值

功能很简单,就是在升级文件前添加一个自定义的字段,以及升级软件的md5值。用以阻止刷机,以及升级文件被破坏。字段以及md5值添加在编译时进行的。字段、md5值取出,是在升级文件检测时进行的。

添加字段:

在Makefile(路径前面已经列出)文件中,最后生成升级文件的地方添加。

 

endef行前有+号的地方就是添加的代码。添加了”100msh“字段在升级文件前。

 

字段取出:

 

行前有+号的地方,就是添加的代码。就是取出第一行,其他不变。

另外default_do_upgrade()函数中的get_image “$1″,修改成cat /tmp/myupfile,因为在调用default_do_upgrade()函数时,get_image函数中有些命令已经不能再用。

发表评论

电子邮件地址不会被公开。 必填项已用*标注