openwrt 网页sysupgrade刷固件流程

2024-06-16 14:18

本文主要是介绍openwrt 网页sysupgrade刷固件流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

       一次偶然的机会,阅读了openwrt网页升级的实现细节,以实际操作流程,结合网络资料,整理了这篇流程。

      本文按照网页升级固件时涉及到的各个模块的先后顺序进行介绍, openwrt 固件的升级功能流程,从页面传入升级文件到升级文件检测,再到调用升级脚本进行升级,升级完成后,进行系统重启。 在实际阅读系统脚本时,会发现网上的资料可能和手上的代码有所区别,这是版本不同一起的,但是以老王的经验来看,核心流程还是一致的。

. web server

1MVC

     OpenWRT中采用LuCI作为它的Web interface界面框架,采用Lua语言.LUCI采用MVCM-V-C ——Model--View--Controller,模式-视图-控制器,这是一种范型,对应:

feeds\luci\modules\admin-full\luasrc下的controller  model  view三个文件夹

 

2、模块入口在controller/admin/下,在index()函数中,使用entry函数来完成每个模块函数的注册

entry(path, target, title=nil, order=nil)

target主要分为三类:call(调用函数),template(调用htm,view下)和cbi(定义各种控件,在model下,属于核心模块)

cbi.lua文件中封装了所有的控件元素,例如复选框,下拉列表等

也就是说,我们在pc端输入192.168.29.1出现的网页,事实上是由lua脚本生成的,这里我们注重介绍 /usr/lib/lua/luci/controller/admin/system.lua, 升级的核心操作都位于这个文件中。

. system.lua

 

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函数,获取boardnameramips_board_name函数从文件 /tmp/sysinfo/board_name 中获取board name,若没有则为unknown,并返回给调用者,我用的板是ralink-socplatform_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=4count=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最后的处理就是调用升级脚本:

fork_exec("killall dropbear uhttpd;  sleep 1; /sbin/sysupgrade %s %q %s %s" %{ keep, image_tmp, size, checksum})

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

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

 

. sysupgrade脚本

 

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

-i       开启交互模式

-d      重启前延迟,延迟秒数是传进来的

-v      会打印sysupgrade脚本中的一些信息,脚本中默认打印

-q      -v相反

-n      升级后不保存配置,默认保存配置

-c      保存所有的改动配置文件到/etc/

-b      sysupgrade.conf中指定的文件,创建.tar.gz格式备份文件

-r      用上步创建的.tar.gz文件,恢复配置

-l       列出将会备份的文件列表

-f       .tar.gz恢复配置

-F      即使升级文件检测失败,也要升级,这个参数是危险的,慎用

-T      验证升级文件和.tar.gz配置文件,但不升级

-h      打印帮助信息

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

 

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

 

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

 

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

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

-v时的升级过程:

root@OpenWrt:/# sysupgrade -i -v/tmp/firmware.img 

Keep config files over reflash (Y/n): y

Edit config file list (y/N): n

Saving config files...

etc/config/dhcp

etc/config/dropbear

etc/config/firewall

etc/config/fstab

etc/config/luci

etc/config/network

etc/config/system

etc/config/ucitrack

etc/config/uhttpd

etc/config/wireless

etc/dropbear/dropbear_dss_host_key

etc/dropbear/dropbear_rsa_host_key

etc/group

etc/hosts

etc/inittab

etc/passwd

etc/profile

etc/rc.local

etc/shells

etc/sysctl.conf

Sending TERM to remaining processes ...dnsmasq ubusd btnd logd netifd uhttpd ntpd

Sending KILL to remaining processes ... 

Switching to ramdisk...

Performing system upgrade...

Unlocking firmware ...

 

Writing from <stdin> to firmware...     

Appending jffs2 data from/tmp/sysupgrade.tgz to firmware...

Writing from <stdin> to firmware...     

Upgrade completed

Reboot (Y/n):

 

 

不带-v时的升级过程:

root@OpenWrt:/# sysupgrade -i/tmp/firmware.img 

Keep config files over reflash (Y/n): y

Edit config file list (y/N): n

Saving config files...

Sending TERM to remaining processes ...dnsmasq ubusd btnd logd netifd uhttpd ntpd

Sending KILL to remaining processes ... 

Switching to ramdisk...

Performing system upgrade...

Unlocking firmware ...

 

Writing from <stdin> to firmware...     

Appending jffs2 data from/tmp/sysupgrade.tgz to firmware...

Writing from <stdin> to firmware...     

Upgrade completed

Reboot (Y/n):

 

继续分析:

if [ $CONF_BACKUP_LIST -eq 1 ]; then

         add_uci_conffiles"$CONFFILES"

         cat"$CONFFILES"

         rm-f "$CONFFILES"

         exit0

fi

如果需要列出配置文件列表,就调用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."

                   exit1

         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 magicnumber来判断升级文件是否合法

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

                            echo"Image check '$check' failed but --force given - will update anyway!"

                            break

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

                            echo"Image check '$check' failed."

                            exit1

                   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"

                            exit1

                   ;;

         esac

         get_image"$CONF_IMAGE" "cat" > "$CONF_TAR"

         exportSAVE_CONFIG=1

elif ask_bool $SAVE_CONFIG "Keepconfig files over reflash"; then ###默认升级保存配置

         [$TEST -eq 1 ] || do_save_conffiles

         exportSAVE_CONFIG=1

else

         exportSAVE_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  

         exit0

fi

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

 

接下来:

run_hooks ""$sysupgrade_pre_upgrade

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

run_hooks() {

         localarg="$1"; shift

         forfunc in "$@"; do

                   eval"$func $arg"

         done

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

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

#append sysupgrade_pre_upgrade  disable_watchdog

 

disable_watchdog() { 

         killallwatchdog

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

                   echo'Could not disable watchdog'

                   return1

         }

}

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

 

接下来:

ubus call system upgrade

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

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

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

 

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

 

 

do_upgrade $1 $2 $3  最后,会调用do_upgrade 这三个参数是由system.lua传进来的,分别是

/tmp/firmware.img 4718596 74833bd4ece2239813a232f80d6ee855     $1代表固件文件, $2代表size $3代表md5

 

也就是说,sysupgrade完成了大量的检测,备份等操作,最后调用do_upgrade来执行固件升级, do_upgrade位于/lib/upgrade/common.sh

 

. common.sh

common.sh中组要是处理do_upgrade

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

     v "Readback firmware and Check"

     mtd check $PART_NAME "$2" "$3"

     if [ $? -eq 1 ]; then

         v "Firmware Check Failed"

         exit 1

     else

         v "Upgrade completed"

         mtd create $PART_NAME success

         if [ $? -eq 1 ]; then

              v "Create Misc Info Failed"

              exit 1

         else

              reboot

         fi

     fi

}

其中 $PART_NAME firmwareB (或frimwareA, 在我们的系统中,存在frimwareA & frimwareB两个固件存储位置, 这个在刷固件时,会获取上次刷写固件的位置,然后本次刷写到另一个位置)

$2代表固件size

$3代表md5

[形如 mtd check firmwareB 4718596 c09ad918ccfb8aa88ea1e2601fc1768e ]

 

get_current_partiton() {

     mtd=`cat /proc/cmdline | awk  '{print $2}' | awk -F= '{print $2}' | awk -F/ '{print $3}'`

     if [ "$mtd" == "mtdblock7" ] ; then

     #Current mtd is firmwareB, the target is firmwareA

         PART_NAME=firmwareA// 

     else

         PART_NAME=firmwareB

     fi

     echo $PART_NAME

}

//从我们测试现象来下,frimwareB代表//dev/mtdblock7, frimwareA代表mtdblock5

 

default_do_upgrade() {

     get_current_partiton

     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}"

         if [ $? -eq 1 ]; then

              v "Write Flash Failed"

              exit 1

         fi

     fi

}

参考链接:

https://blog.csdn.net/kinbo88/article/details/21454517

https://blog.csdn.net/u013485792/article/details/50505706

这篇关于openwrt 网页sysupgrade刷固件流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1066666

相关文章

问题-windows-VPN不正确关闭导致网页打不开

为什么会发生这类事情呢? 主要原因是关机之前vpn没有关掉导致的。 至于为什么没关掉vpn会导致网页打不开,我猜测是因为vpn建立的链接没被更改。 正确关掉vpn的时候,会把ip链接断掉,如果你不正确关掉,ip链接没有断掉,此时你vpn又是没启动的,没有域名解析,所以就打不开网站。 你可以在打不开网页的时候,把vpn打开,你会发现网络又可以登录了。 方法一 注意:方法一虽然方便,但是可能会有

工作流Activiti初体验—流程撤回【二】

已经玩工作流了,打算还是研究一下撤回的功能。但是流程图里面并不带撤回的组件,所以需要自己动态改造一下,还是延续上一个流程继续试验撤回功能。《工作流Activiti初体验【一】》 完整流程图 我们研究一下分发任务撤回到发起任务,其他环节的撤回类似 撤回的原理大概如下: 将分发任务后面的方向清空,把发起任务拼接到原来的判断网关,然后结束分发任务,这样流程就到发起任务了 此时的流程如上图,

ROS话题通信流程自定义数据格式

ROS话题通信流程自定义数据格式 需求流程实现步骤定义msg文件编辑配置文件编译 在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如:

完整的申请邓白氏编码的流程(手把手教你申请邓白氏编码

完整的申请邓白氏编码的流程(手把手教你申请邓白氏编码)  标签: 编码邓白氏编码申请流程苹果开发者账号申请 2016-07-08 16:13  2274人阅读  评论(2)  收藏  举报   分类: 技术  苹果开发  邓白氏编码申请 版权声明:本文为博主原创文章,未经博主允许不得转载。     申请公司的苹果开发者账号和企业级的苹

网页脚本输入这么简单

如何在网页中进行脚本操作呢? 研究了一下,很简单,用google浏览器的Console直接操作javaScript。思路: Created with Raphaël 2.1.0 开始 输入(如何输入) 点击(如何点击) 结束 下面是,通过脚本刷直播屏的实现,直接在Console输入即可 var words=new Arra

办理河南建筑工程乙级设计资质的流程与要点

办理河南建筑工程乙级设计资质的流程与要点 办理河南建筑工程乙级设计资质的流程与要点主要包括以下几个方面: 流程: 工商注册与资质规划:确保企业具有独立法人资格,完成工商注册,并明确乙级设计资质的具体要求,包括注册资本、人员配置、技术条件等。 专业技术人员配置: 雇佣或签约符合资质要求的专业技术人员,包括但不限于:一级注册结构工程师2名、一级注册建筑师2名、注册暖通工程师1名、注册供配电工

黑龙江等保测评的具体流程是怎样的

黑龙江等保测评的具体流程 黑龙江等保测评是根据《中华人民共和国网络安全法》及相关法律法规,对信息系统安全保护能力进行评估和验证的过程。以下是黑龙江等保测评的具体流程: 系统定级:根据业务、资产、安全技术、安全管理等方面的情况,对企业的安全防护水平进行评估,编制定级报告,为客户提供技术支持,协助客户编制定级报告,并组织相关专家对定级报告进行评估。 系统备案:持定级报告及登记表到当地的公安网监

Eclipse使用git最基本流程

Eclipse使用git最基本流程,eclipsegit流程 git有诸多好处,网上都说的很清楚了,在这里我不再赘述。对于我来说,私下里想做一些项目,而又不能很好的保存自己的代码和进行版本控制,这时候,就用到了git。下面,就以我个人为例讲讲git从0开始如何安装使用。 Step1 准备工作 msysgit,下载地址为http://msysgit.github.io/

Android Framework学习(四)之Launcher启动流程解析

在之前的博客中,我们学习了init进程、Zygote进程和SyetemServer进程的启动过程,我们知道SystemServer进程主要用于启动系统的各种服务,二者其中就包含了负责启动Launcher的服务,LauncherAppService,本篇博客我们将一起学习Launcher相关的知识。 Launcher概述 Launcher程序就是我们平时看到的桌面程序,它其实也是一个Androi

前端微信网页授权+WebSocket

这篇教程只介绍前端需要做的工作; 注意: 1,开发者必须关注公众号,且是公众号下的开发者; 2,域名地址必须是公众号绑定的域名地址; 微信授权需要调试工具:微信web开发者工具; 参考微信文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 微信授权,前端与服务端的交互只涉及appId,code,和re