hotplug脚本调用
hotplug调用流程
最终调用hotplug脚本在netifd interface-event.c:
char *eventnames[] = {"ifdown", "ifup", "ifupdate"};
setenv("ACTION", eventnames[event], 1);
setenv("INTERFACE", ifname, 1);
if (device)
setenv("DEVICE", device, 1);
argv[0] = hotplug_cmd_path;
argv[1] = "iface";
argv[2] = NULL;
execvp(argv[0], argv);
exit(127);
netifd’s cycle appears to be:
- Start some proto-specific mechanism for acquiring interface configuration. For DHCP this is driven by proto-shell.c, which spawns /lib/netifd/proto/dhcp.sh, which is a thin wrapper around udhcpc. udhcpc is long-lived and set up to call back into /lib/netifd/dhcp.script for dhcp events:
833 root 1488 S udhcpc -p /var/run/udhcpc-eth1.pid -s /lib/netifd/dhcp.script -f -t 0 -i eth1 -C
dhcp.script packages variables from udhcpc into json and end up doing ‘ubus call network.interface notify_proto ’. This in turn is handled by netifd in proto-shell.c, in proto_shell_notify. Action is 0, which leads into proto_shell_update_link.
proto_shell_update_link delivers an event to the interface through state->proto.proto_event (interface_proto_cb), which calls interface_event, which leads to the snippet above, calling into /sbin/hotplug-call with ACTION=ifupdate.
代码实现
内核
代码路径:linux/drivers/net/raeth/raether.c
内核,网口down/up 在raether.c中监测寄存器来判断网口的插拔,当出现WAN口插拔事件之后,向udhcpc发送信号release/renew
#if defined(CONFIG_RALINK_MT7621) || defined(CONFIG_ARCH_MT7623)
fp = filp_open("/var/run/udhcpc-eth1.pid", O_RDONLY, 0);
#else
fp = filp_open("/var/run/udhcpc-eth0.2.pid", O_RDONLY, 0);
#endif
if (IS_ERR(fp))
return;
if (fp->f_op && fp->f_op->read) {
if (fp->f_op->read(fp, pid, 8, &fp->f_pos) > 0) {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
p = pid_task(find_get_pid(simple_strtoul(pid, NULL, 10)), PIDTYPE_PID);
#else
p = find_task_by_pid(simple_strtoul(pid, NULL, 10));
#endif
if (NULL != p) {
send_sig(SIGUSR2, p, 0);
send_sig(SIGUSR1, p, 0);
}
}
udhcp
代码路径:package/utils/busybox/src/networking/udhcp/dhcp.c
udhcp接受到SIGUSR2和SIGUSR1之后的处理
case SIGUSR2:
perform_release(server_addr, requested_ip);
timeout = INT_MAX;
continue;
在perform_release函数中调用了脚本dhcp.script
udhcp_run_script(NULL, "deconfig");
netifd
代码路径:/package/network/config/netifd
被udcp调用的dhcp.script脚本中有:
deconfig_interface() {
proto_init_update "*" 0
proto_send_update "$INTERFACE"
}
case "$1" in
deconfig)
deconfig_interface
;;
renew|bound)
setup_interface
;;
esac
其中proto_init_update proto_send_update在netifd-proto.sh
代码路径:/package/network/config/netifd/src/scripts/netifd-proto.sh
proto_send_update() {
local interface="$1"
proto_close_nested
json_add_boolean keep "$PROTO_KEEP"
_proto_push_array "ipaddr" "$PROTO_IPADDR" _proto_push_ipv4_addr
_proto_push_array "ip6addr" "$PROTO_IP6ADDR" _proto_push_ipv6_addr
_proto_push_array "routes" "$PROTO_ROUTE" _proto_push_route
_proto_push_array "routes6" "$PROTO_ROUTE6" _proto_push_route
_proto_push_array "ip6prefix" "$PROTO_PREFIX6" _proto_push_string
_proto_push_array "dns" "$PROTO_DNS" _proto_push_string
_proto_push_array "dns_search" "$PROTO_DNS_SEARCH" _proto_push_string
_proto_notify "$interface"
}
_proto_notify() {
local interface="$1"
local options="$2"
json_add_string "interface" "$interface"
ubus $options call network.interface notify_proto "$(json_dump)"
}
在ubus.c中注册了notify_proto
{ .name = "notify_proto", .handler = netifd_iface_notify_proto },
在src/proto-shell.c
proto_shell_notify函数action=0
switch(blobmsg_get_u32(tb[NOTIFY_ACTION])) {
case 0:
return proto_shell_update_link(state, attr, tb);
if (!keep)
state->proto.proto_event(&state->proto, IFPEV_UP);
interface.c中interface_set_proto_state函数
state->proto_event = interface_proto_cb;
state->iface = iface;
在回调函数interface_proto_cb函数中
switch (ev) {
case IFPEV_UP:
if (iface->state != IFS_SETUP) {
interface_event(iface, IFEV_UPDATE);
return;
}
if (!iface->l3_dev.dev)
interface_set_l3_dev(iface, iface->main_dev.dev);
interface_ip_set_enabled(&iface->config_ip, true);
system_flush_routes();
iface->state = IFS_UP;
iface->start_time = system_get_rtime();
interface_event(iface, IFEV_UP);
netifd_log_message(L_NOTICE, "Interface '%s' is now up\n", iface->name);
break;
调用interface_even函数
struct interface_user *dep, *tmp;
struct device *adev = NULL;
list_for_each_entry_safe(dep, tmp, &iface->users, list)
dep->cb(dep, iface, ev);
list_for_each_entry_safe(dep, tmp, &iface_all_users, list)
dep->cb(dep, iface, ev);
interface_even.c/run_cmd函数
static const char * const eventnames[] = {"ifdown", "ifup", "ifupdate"};
setenv("ACTION", eventnames[event], 1);
setenv("INTERFACE", ifname, 1);
if (device)
setenv("DEVICE", device, 1);
....
argv[0] = hotplug_cmd_path;
argv[1] = "iface";
argv[2] = NULL;
execvp(argv[0], argv);
exit(127);
其中的变量定义如下:
char *hotplug_cmd_path = DEFAULT_HOTPLUG_PATH;
#define DEFAULT_HOTPLUG_PATH "/sbin/hotplug-call"
hotplug-call脚本
代码路径:/package/base-files/files/sbin/hotplug-call
[ \! -z "$1" -a -d /etc/hotplug.d/$1 ] && {
for script in $(ls /etc/hotplug.d/$1/* 2>&-); do (
[ -f $script ] && . $script
); done
}
将/etc/hotplug.d/iface下的脚本都执行一遍