添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

OpenWrt 按键处理逻辑采用 hotplug 事件方式进行管理,reset按键,用来进行重启或者恢复出厂操作。热插拔事件流程:内核具有检测 键盘守护程序,gpio_button_hotplug 模块,源码位于 openwrt/package/kernel/gpio-button-hotplug 文件夹下,系统启动时加载键盘检测模块,就驻留到内核。
在这里插入图片描述
内核gpio_button_hotplug 模块检测到键盘后,通过netlink-kobject方式发送到用户空间,用户空间 ubusd 系统获得到事件后,执行键盘响应的回调函数,响应键盘。
本文直接分析用户空间程序处理逻辑,不分析内核键盘模块程序、请读者自行参考netlink框架和gpio_button_hotplug.c文件分析。

RESET按键的操作对应为:
单击 - 重启设备
长按 5秒以上 – 恢复出厂设置
当然,这些操作可以通过配置etc/rc.button/reset脚本处理方法,重新定义按键功能。为什么修改rese脚本就能够重新定义呢?
接下来逐步分析,揭晓 openWRT的按键管理逻辑。

hotplug 事件注册

在 《详解 OpenWRT系统框架基础软件模块之 procd》篇,分析过procd线程启动时,调用 openWRT的state_enter() 状态机函数,此函数中,在系统 early_init 阶段,调用 hotplug() 函数注册RPC服务响应程序。源码如下:

static void state_enter(void)
	char ubus_cmd[] = "/sbin/ubusd";
	switch (state) {
	case STATE_EARLY:
		LOG("- early -\n");
		watchdog_init(0);
		hotplug("/etc/hotplug.json");   //热插拔函数初始化,hotplug.json文件内容非常关键
		procd_coldplug();				//冷插拔函数初始化
		break;
	case STATE_UBUS:
		// try to reopen incase the wdt was not available before coldplug
		watchdog_init(0);
		set_stdio("console");
		LOG("- ubus -\n");
		procd_connect_ubus();
		service_start_early("ubus", ubus_cmd);
		break;
	case STATE_INIT:
		LOG("- init -\n");
		procd_inittab();
		procd_inittab_run("respawn");
		procd_inittab_run("askconsole");
		procd_inittab_run("askfirst");
		procd_inittab_run("sysinit");
		// switch to syslog log channel
		ulog_open(ULOG_SYSLOG, LOG_DAEMON, "procd");
		break;
	case STATE_RUNNING:
		LOG("- init complete -\n");
		procd_inittab_run("respawnlate");
		procd_inittab_run("askconsolelate");
		break;
..... 省略部分源码
	default:
		ERROR("Unhandled state %d\n", state);
		return;

文件 /etc/hotplug.json 内容

[ "case", "ACTION", { "add": [ [ "if", [ "and", [ "has", "MAJOR" ], [ "has", "MINOR" ] [ "if", [ "eq", "DEVNAME", [ "null", "full", "ptmx", "zero", "tty", "net", "random", "urandom" ] [ "makedev", "/dev/%DEVNAME%", "0666" ], [ "return" ] [ "if", [ "regex", "DEVNAME", "^snd" ], [ "makedev", "/dev/%DEVNAME%", "0660", "audio" ] [ "if", [ "regex", "DEVNAME", "^tty" ], [ "makedev", "/dev/%DEVNAME%", "0660", "dialout" ] [ "if", [ "has", "DEVNAME" ], [ "makedev", "/dev/%DEVNAME%", "0600" ] [ "if", [ "has", "FIRMWARE" ], [ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ], [ "load-firmware", "/lib/firmware" ], [ "return" ] "remove" : [ [ "if", [ "and", [ "has", "DEVNAME" ], [ "has", "MAJOR" ], [ "has", "MINOR" ] [ "rm", "/dev/%DEVNAME%" ] } ], [ "if", [ "and", //键盘事件 [ "has", "BUTTON" ], [ "eq", "SUBSYSTEM", "button" ] [ "button", "/etc/rc.button/%BUTTON%" ] // 键盘事件执行的脚本位置 [ "if", [ "and", // usb串口事件 [ "eq", "SUBSYSTEM", "usb-serial" ], [ "regex", "DEVNAME", [ "^ttyUSB", "^ttyACM" ] [ "exec", "/sbin/hotplug-call", "tty" ], [ "if", [ "isdir", "/etc/hotplug.d/%SUBSYSTEM%" ], [ "exec", "/sbin/hotplug-call", "%SUBSYSTEM%" ]

由此文件看到 [ “button”, “/etc/rc.button/%BUTTON%” ] 内容,也就是说键盘热插拔事件发送到用户空间后,会触发程序执行此文件夹下的脚本。

OpenWRT 系统中的按键处理方法如下:

  • 在 etc/rc.button/ 文件夹下有热拔插事件响应脚本
  • 按键热拔插事件、触发etc/rc.button/ 对应的按键响应脚本
    系统按键响应逻辑是,执行响应脚本文件,用户可以直接修改脚本文件,来修订键盘处理响应逻辑。

查看 rc.button/

root@LEDE:/# ls /etc/rc.button/
failsafe  power     reset     rfkill

响应脚本内容,分别如下:

/# cat etc/rc.button/power       // 电源off执行内容
#!/bin/sh
[ "${ACTION}" = "released" ] || exit 0
exec /sbin/poweroff
return 0
/# cat etc/rc.button/failsafe 	// 进入安全模式
#!/bin/sh
[ "${TYPE}" = "switch" ] || echo ${BUTTON} > /tmp/failsafe_button
return 0
 

重点看 复位按键 的脚本

root@LEDE:/# cat etc/rc.button/reset //复位按键处理
#!/bin/sh
. /lib/functions.sh
OVERLAY="$( grep ' /overlay ' /proc/mounts )"
case "$ACTION" in
pressed)								// 内核模块gpio_button_hotplug 发送事件类型pressed
	[ -z "$OVERLAY" ] && return 0
	return 5
timeout)
	. /etc/diag.sh
	set_state failsafe
released)								// 内核模块gpio_button_hotplug回发送事件类型released,
										// 并携带按键时间SEEN变量值,因此脚本判定此变量的时间值
	if [ "$SEEN" -lt 1 ]						// 小于 1s , 执行reboot指令
		echo "REBOOT" > /dev/console
		reboot								
	elif [ "$SEEN" -gt 5 -a -n "$OVERLAY" ]		// 按键超出 5s , 就执行 jffs2reset -y && reboot
		echo "FACTORY RESET" > /dev/console
		jffs2reset -y && reboot &			
return 0

至此,我们揭开reset按键功能、openWRT系统是处理的全过程。接下来重点分析恢复出厂功能是如何实现的,
下面内容的重点是文件系统知识,特别是 overlay 文件系统相关知识。关于 overlay 文件系统原理,推荐阅读 《
深入理解 overlayfs (一、原理)》。

reset 处理逻辑分析

reset 按键超出 5s , 就执行 jffs2reset -y && reboot & 命令,我们进一步分析 jffs2reset 命令。

  • 执行 jffs2reset -y 结果如下
root@ixeRouter:~# jffs2reset -y                                                                                                                              
/dev/mtdblock6 is mounted as /overlay, only erasing files
root@ixeRouter:~# cat /proc/mtd                                                                                                                              
dev:    size   erasesize  name
mtd0: 00030000 00010000 "bootloader"
mtd1: 00010000 00010000 "config"
mtd2: 00010000 00010000 "factory"
mtd3: 01fb0000 00010000 "firmware"
mtd4: 0020670f 00010000 "kernel"
mtd5: 01da98f1 00010000 "rootfs"
mtd6: 00640000 00010000 "rootfs_data"

得知,mtd6中的内容为 rootfs_data, 也就是路由器设备配置相关内容被擦除,
系统启动后、恢复到出厂镜像配置状态。

jffs2reset 源码分析

查看 fstools 文件夹下的 CMakeList.txt文件内容,摘录部分内容如下:

ADD_EXECUTABLE(mount_root mount_root.c) TARGET_LINK_LIBRARIES(mount_root fstools) INSTALL(TARGETS mount_root RUNTIME DESTINATION sbin)
find_library(json NAMES json-c json)
ADD_EXECUTABLE(blockd blockd.c) TARGET_LINK_LIBRARIES(blockd fstools ubus blobmsg_json ${json}) INSTALL(TARGETS blockd RUNTIME DESTINATION sbin)
ADD_EXECUTABLE(block block.c probe.c probe-libblkid.c) IF(DEFINED CMAKE_UBIFS_EXTROOT) ADD_DEFINITIONS(-DUBIFS_EXTROOT)
	TARGET_LINK_LIBRARIES(block blkid-tiny dl uci ubox ubus blobmsg_json ubi-utils ${json}) ELSE(DEFINED CMAKE_UBIFS_EXTROOT) TARGET_LINK_LIBRARIES(block blkid-tiny dl uci ubox ubus blobmsg_json ${json}) ENDIF(DEFINED CMAKE_UBIFS_EXTROOT) INSTALL(TARGETS block RUNTIME DESTINATION sbin)
# jffs2reset 命令内容对应的源码文件
ADD_EXECUTABLE(jffs2reset jffs2reset.c) TARGET_LINK_LIBRARIES(jffs2reset fstools) INSTALL(TARGETS jffs2reset RUNTIME DESTINATION sbin)
ADD_EXECUTABLE(snapshot_tool snapshot.c) TARGET_LINK_LIBRARIES(snapshot_tool fstools) INSTALL(TARGETS snapshot_tool RUNTIME DESTINATION sbin)
ADD_EXECUTABLE(ubi ubi.c) TARGET_LINK_LIBRARIES(ubi ubi-utils ubox) INSTALL(TARGETS ubi RUNTIME DESTINATION sbin) 

jffs2reset.c 源码文件内容

int main(int argc, char **argv) struct volume *v; int ch, yes = 0, reset = 0; while ((ch = getopt(argc, argv, "yr")) != -1) { switch(ch) { case 'y': yes = 1; break; case 'r': reset = 1; break; if (!yes && ask_user()) return -1; * TODO: Currently this only checks if kernel supports OverlayFS. We * should check if there is a mount point using it with rootfs_data * as upperdir. if (find_filesystem("overlay")) { ULOG_ERR("overlayfs not supported by kernel\n"); return -1; v = volume_find("rootfs_data"); // 查找分区 rootfs_data if (!v) { ULOG_ERR("MTD partition 'rootfs_data' not found\n"); return -1; volume_init(v); if (!strcmp(*argv, "jffs2mark")) return jffs2_mark(v); return jffs2_reset(v, reset); // 执行 jffs2_reset

jffs2_reset 函数内容

static int jffs2_reset(struct volume *v, int reset) char *mp; mp = find_mount_point(v->blk, 1); if (mp) { ULOG_INFO("%s is mounted as %s, only erasing files\n", v->blk, mp); fs_state_set("/overlay", FS_STATE_PENDING); overlay_delete(mp, false); //delete overlay分区内容 mount(mp, "/", NULL, MS_REMOUNT, 0); } else { ULOG_INFO("%s is not mounted\n", v->blk); return jffs2_mark(v); if (reset) { sync(); sleep(2); reboot(RB_AUTOBOOT); while (1) return 0;
  • 路由器系统文件构成
root@ixeRouter:~# cat /proc/filesystems                                                                                                                      
nodev   sysfs
nodev   rootfs
nodev   ramfs
nodev   bdev
nodev   proc
nodev   tmpfs
nodev   debugfs
nodev   tracefs
nodev   sockfs
nodev   bpf
nodev   pipefs
// 路由器文件系统构成
nodev   devpts
        squashfs	//文件系统
nodev   jffs2
nodev   overlay		//文件系统
nodev   ubifs
                    OpenWRT 系统中的按键处理方法如下:在 etc/rc.button/ 文件夹下有热拔插事件响应脚本按键热拔插事件、触发etc/rc.button/ 对应的按键响应脚本系统按键响应逻辑是,执行响应脚本文件,用户可以直接修改脚本文件,来修订键盘处理响应逻辑。查看 rc.button/内容如下root@LEDE:/# ls /etc/rc.button/failsafe  power     reset     rfkill响应脚本内容,分别如下:电源按键/# cat etc/
 openwrt-19.07.0 ... 19.07.2 :
 openwrt-18.06 :
 安装后,您将在LuCI页面的“ Network菜单列表中看到一个新的“ Traffic status菜单项。
原则上,从, 支持lua版本
dev:    size   erasesize  name
mtd0: 00030000 00010000 "u-boot"
mtd1: 00010000 00010000 "u-boot-env"
mtd2: 00010000 00010000 "factory"
mtd3: 00fb0000 00010000 "firmware"
mtd4: 00185df7 00010000 "kernel"
mtd5: 00e2a209 00010000 "ro
				
错误提示为:/dev/loop0 写保护,将以只读方式挂载 mount: 文件系统类型错误、选项错误、/dev/loop0 上有坏超级块 重新创建img: 运行bximage,选1. Create new floppy or hard disk image。输入fd,默认1.44M Creating floppy image 'a.img' with 2880 sectors The fol...
OpenWrt/LEDE 上可用的 Kcptun 客户端/服务端 到 页面下载最新版的kcptun-client 或 kcptun-server(注:请根据你的路由器架构下载对应版本) 将文件上传到你的路由器上,进行安装 opkg install kcptun-client_*.ipk opkg install kcptun-server_*.ipk 安装完毕,你可以在 /usr/bin 目录下找到对应的二进制文件。 root@OpenWrt:~# kcptun-client -v kcptun version 20190109-2_OpenWrt root@OpenWrt:~# kcptun-server -v kcptun version 20190109-2_OpenWrt
luci-theme-infinityfreedom InfinityFreedom是LuCI的干净HTML5主题。 它基于luci主题材料。 该主题是专门为HomeLede(基于OpenWrt)固件设计的,也可以用于其他版本的OpenWrt。 它目前与Luci18兼容,并且计划在此版本稳定后开发其他版本的Luci。 有关HomeLede固件的信息,请参见: : 将InfinityFreedom添加到您自己的LEDE / OpenWRT构建中 编辑feeds.conf.default并添加以下内容: # luci-theme-infinityfreedom src-git infinityfreedom https://github.com/xiaoqingfengATGH/luci-theme-infinityfreedom.git
tobiaswaldvogel / openwrt-addpack maxlicheng / luci-app-unblockmusic giaulo / luci-app-filebrowser sensec / ddns-scripts_aliyun 弗雷芬克胶 Kiougar / luci-wrtbwmon anshi233 / Openwrt-BBR zhaojh329 / oui sypopo / luci-theme-argon-mc OnionIoT / OpenWRT软件包 mchome / openwrt-vlmcsd mchome / luci-app-vlmcsd 玫瑰色/露西主题玫瑰色 openwrt-develop / luci-theme-atmaterial aa65535 / openwr uci -q show system.@restorefactory[0] || { uci add system restorefactory uci set system.@restorefactory[0].button=reset uci set system.@restorefactory[0].action=pressed uci set system.@restorefactory[0].timeout.