39 --> 详解 OpenWRT 软件组件之 hotplug、coldplug 模块

OpenWrt 内部消息总线 ubus 是支撑 该系统核心,系统软件模块之间关系图如下:
在这里插入图片描述
此图仅是把部分软件模块标识出来,其中还有很多软件模块如:uhttpd、rpcd、mwan3、hotplug、coldplug等等模块,都是依托openWRT的系统ubus总线来构建。此框图非常重要、能够快速建立系统软件组件之间关系。

热插拔与冷插拔入口

调用 openWRT的state_enter() 状态机函数,此函数中,在系统early阶段初始 hotplug()、procd_coldplug()函数。注册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;

	case STATE_SHUTDOWN:
		/* Redirect output to the console for the users' benefit */
		set_console();
		LOG("- shutdown -\n");
		procd_inittab_run("shutdown");
		sync();
		break;

	case STATE_HALT:
		// To prevent killed processes from interrupting the sleep
		signal(SIGCHLD, SIG_IGN);
		LOG("- SIGTERM processes -\n");
		kill(-1, SIGTERM);
		sync();
		sleep(1);
		LOG("- SIGKILL processes -\n");
		kill(-1, SIGKILL);
		sync();
		sleep(1);
#ifndef DISABLE_INIT
		perform_halt();
#else
		exit(EXIT_SUCCESS);
#endif
		break;

	default:
		ERROR("Unhandled state %d\n", state);
		return;
	};
}

一、热插拔源码走读

void hotplug(char *rules)
{
	struct sockaddr_nl nls = {};
	int nlbufsize = 512 * 1024;

	rule_file = strdup(rules);
	nls.nl_family = AF_NETLINK;
	nls.nl_pid = getpid();
	nls.nl_groups = -1;
	// 建立 NETLINK_KOBJECT_UEVENT 连接
	if ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) {
		ERROR("Failed to open hotplug socket: %m\n");
		exit(1);
	}
	if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) {
		ERROR("Failed to bind hotplug socket: %m\n");
		exit(1);
	}
	// 建立 监听
	if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize)))
		ERROR("Failed to resize receive buffer: %m\n");

	json_script_init(&jctx);
	queue_proc.cb = queue_proc_cb;					//设置回调函数
	uloop_fd_add(&hotplug_fd, ULOOP_READ);			//把 hotplug_fd 添加到 ubus 链表中
}

文件 /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%" ]
		]
	]
]

热插拔回调函数

static void hotplug_handler(struct uloop_fd *u, unsigned int ev)
{
	int i = 0;
	static char buf[4096];
	int len = recv(u->fd, buf, sizeof(buf) - 1, MSG_DONTWAIT);
	void *index;
	if (len < 1)
		return;

	buf[len] = '\0';

	blob_buf_init(&b, 0);
	index = blobmsg_open_table(&b, NULL);
	while (i < len) {
		int l = strlen(buf + i) + 1;
		char *e = strstr(&buf[i], "=");

		if (e) {
			*e = '\0';
			blobmsg_add_string(&b, &buf[i], &e[1]);
		}
		i += l;
	}
	blobmsg_close_table(&b, index);
	hotplug_handler_debug(b.head);
	json_script_run(&jctx, rule_file, blob_data(b.head));		//执行 /etc/hotplug.json 执行 json 脚本程序。
}

热插拔回调函数中,调用json_script_run()函数,此函数调用如下函数

static void __json_script_run(struct json_call *call, struct json_script_file *file,
			      struct blob_attr *context)
{
	struct json_script_ctx *ctx = call->ctx;

	if (file->seq == call->seq) {
		if (context)
			ctx->handle_error(ctx, "Recursive include", context);

		return;
	}

	file->seq = call->seq;
	while (file) {									// 遍历 json 文件中所有的脚本文件
		json_process_cmd(call, file->data);			// 执行脚本中命令
		file = file->next;
	}
}

调用如下函数,此函数

static int __json_process_cmd(struct json_call *call, struct blob_attr *cur)
{
	struct json_script_ctx *ctx = call->ctx;
	const char *name;
	bool found;
	int ret;

	if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
	    blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
		ctx->handle_error(ctx, "Unexpected element type", cur);
		return -1;
	}

	ret = __json_process_type(call, cur, cmd, ARRAY_SIZE(cmd), &found);
	if (found)
		return ret;

	name = blobmsg_data(blobmsg_data(cur));
	ret = cmd_process_strings(call, cur);
	if (ret)
		return ret;

	ctx->handle_command(ctx, name, blob_data(ctx->buf.head), call->vars);

	return 0;
}

二、冷插拔源码走读


void procd_coldplug(void)
{
	char *argv[] = { "udevtrigger", NULL };
	unsigned int oldumask = umask(0);

	if (!is_container()) {
		umount2("/dev/pts", MNT_DETACH);
		umount2("/dev/", MNT_DETACH);
		mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755,size=512K");
		mkdir("/dev/pts", 0755);
		mount("devpts", "/dev/pts", "devpts", MS_NOEXEC | MS_NOSUID, 0);
	}

	ignore(symlink("/tmp/shm", "/dev/shm"));
	umask(oldumask);
	udevtrigger.cb = udevtrigger_complete;
	udevtrigger.pid = fork();
	if (!udevtrigger.pid) {
		execvp(argv[0], argv);
		ERROR("Failed to start coldplug: %m\n");
		exit(EXIT_FAILURE);
	}

	if (udevtrigger.pid <= 0) {
		ERROR("Failed to start new coldplug instance: %m\n");
		return;
	}

	uloop_process_add(&udevtrigger);

	DEBUG(4, "Launched coldplug instance, pid=%d\n", (int) udevtrigger.pid);
}

此部分代码需要进一步解读,可参考《详解 OpenWRT RESET按键、键盘响应逻辑》

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值