static void
handle_hotplug_msg(char *data, int size)
{
const char *subsystem = NULL, *interface = NULL;
char *cur, *end, *sep;
struct device *dev;
int skip;
bool add;
if (!strncmp(data, "add@", 4))
add = true;
else if (!strncmp(data, "remove@", 7))
add = false;
else
return;
skip = strlen(data) + 1;
end = data + size;
for (cur = data + skip; cur < end; cur += skip) {
skip = strlen(cur) + 1;
sep = strchr(cur, '=');
if (!sep)
continue;
*sep = 0;
if (!strcmp(cur, "INTERFACE"))
interface = sep + 1;
else if (!strcmp(cur, "SUBSYSTEM")) {
subsystem = sep + 1;
if (strcmp(subsystem, "net") != 0)
return;
}
if (subsystem && interface)
goto found;
}
return;
found:
dev = device_get(interface, false);
if (!dev)
return;
if (dev->type != &simple_device_type)
return;
if (add && system_if_force_external(dev->ifname))
return;
device_set_present(dev, add);
}
static void
handle_hotplug_event(struct uloop_fd *u, unsigned int events)
{
struct event_socket *ev = container_of(u, struct event_socket, uloop);
struct sockaddr_nl nla;
unsigned char *buf = NULL;
int size;
while ((size = nl_recv(ev->sock, &nla, &buf, NULL)) > 0) {
if (nla.nl_pid == 0)
handle_hotplug_msg((char *) buf, size);
free(buf);
}
}
static int cb_rtnl_event(struct nl_msg *msg, void *arg)
{
struct nlmsghdr *nh = nlmsg_hdr(msg);
struct nlattr *nla[__IFLA_MAX];
int link_state = 0;
char buf[10];
if (nh->nlmsg_type != RTM_NEWLINK)
goto out;
nlmsg_parse(nh, sizeof(struct ifinfomsg), nla, __IFLA_MAX - 1, NULL);
if (!nla[IFLA_IFNAME])
goto out;
struct device *dev = device_get(nla_data(nla[IFLA_IFNAME]), false);
if (!dev || dev->type->keep_link_status)
goto out;
if (!system_get_dev_sysctl("/sys/class/net/%s/carrier", dev->ifname, buf, sizeof(buf)))
link_state = strtoul(buf, NULL, 0);
device_set_link(dev, link_state ? true : false);
out:
return 0;
}
int system_init(void)
{
static struct event_socket rtnl_event;
static struct event_socket hotplug_event;
sock_ioctl = socket(AF_LOCAL, SOCK_DGRAM, 0);
system_fd_set_cloexec(sock_ioctl);
// Prepare socket for routing / address control
sock_rtnl = create_socket(NETLINK_ROUTE, 0);
if (!sock_rtnl)
return -1;
if (!create_event_socket(&rtnl_event, NETLINK_ROUTE, cb_rtnl_event))
return -1;
if (!create_raw_event_socket(&hotplug_event, NETLINK_KOBJECT_UEVENT, 1,
handle_hotplug_event, 0))
return -1;
// Receive network link events form kernel
nl_socket_add_membership(rtnl_event.sock, RTNLGRP_LINK);
return 0;
}