diff options
-rw-r--r-- | term-utils/agetty.c | 125 |
1 files changed, 107 insertions, 18 deletions
diff --git a/term-utils/agetty.c b/term-utils/agetty.c index 122959fd3..bf5a8d38e 100644 --- a/term-utils/agetty.c +++ b/term-utils/agetty.c @@ -129,9 +129,12 @@ */ #ifdef AGETTY_RELOAD # include <sys/inotify.h> +# include <linux/netlink.h> +# include <linux/rtnetlink.h> # define AGETTY_RELOAD_FILENAME "/run/agetty.reload" /* trigger file */ # define AGETTY_RELOAD_FDNONE -2 /* uninitialized fd */ static int inotify_fd = AGETTY_RELOAD_FDNONE; +static int netlink_fd = AGETTY_RELOAD_FDNONE; #endif /* @@ -1516,6 +1519,80 @@ done: } #ifdef AGETTY_RELOAD +static void open_netlink(void) +{ + struct sockaddr_nl addr = { 0, }; + int sock; + + if (netlink_fd != AGETTY_RELOAD_FDNONE) + return; + + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock >= 0) { + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) + close(sock); + else + netlink_fd = sock; + } +} + +static int process_netlink_msg(int *changed) +{ + char buf[4096]; + struct sockaddr_nl snl; + struct nlmsghdr *h; + int rc; + + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf) + }; + struct msghdr msg = { + .msg_name = &snl, + .msg_namelen = sizeof(snl), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0 + }; + + rc = recvmsg(netlink_fd, &msg, MSG_DONTWAIT); + if (rc < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) + return 0; + + /* Failure, just stop listening for changes */ + close(netlink_fd); + netlink_fd = AGETTY_RELOAD_FDNONE; + return 0; + } + + for (h = (struct nlmsghdr *)buf; NLMSG_OK(h, (unsigned int)rc); h = NLMSG_NEXT(h, rc)) { + if (h->nlmsg_type == NLMSG_DONE || + h->nlmsg_type == NLMSG_ERROR) { + close(netlink_fd); + netlink_fd = AGETTY_RELOAD_FDNONE; + return 0; + } + + *changed = 1; + break; + } + + return 1; +} + +static int process_netlink(void) +{ + int changed = 0; + while (process_netlink_msg(&changed)); + return changed; +} + static int wait_for_term_input(int fd) { char buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; @@ -1556,33 +1633,41 @@ static int wait_for_term_input(int fd) AGETTY_RELOAD_FILENAME); } - FD_ZERO(&rfds); - FD_SET(fd, &rfds); + while (1) { + FD_ZERO(&rfds); + FD_SET(fd, &rfds); - if (inotify_fd >= 0) - FD_SET(inotify_fd, &rfds); + if (inotify_fd >= 0) + FD_SET(inotify_fd, &rfds); + if (netlink_fd >= 0) + FD_SET(netlink_fd, &rfds); - /* If waiting fails, just fall through, presumably reading input will fail */ - if (select(max(fd, inotify_fd) + 1, &rfds, NULL, NULL, NULL) < 0) - return 1; + /* If waiting fails, just fall through, presumably reading input will fail */ + if (select(max(fd, inotify_fd) + 1, &rfds, NULL, NULL, NULL) < 0) + return 1; - if (FD_ISSET(fd, &rfds)) { - count = read(fd, buffer, sizeof (buffer)); + if (FD_ISSET(fd, &rfds)) { + count = read(fd, buffer, sizeof (buffer)); - tcsetattr(fd, TCSANOW, &orig); + tcsetattr(fd, TCSANOW, &orig); - /* Reinject the bytes we read back into the buffer, usually just one byte */ - for (i = 0; i < count; i++) - ioctl(fd, TIOCSTI, buffer + i); + /* Reinject the bytes we read back into the buffer, usually just one byte */ + for (i = 0; i < count; i++) + ioctl(fd, TIOCSTI, buffer + i); - /* Have terminal input */ - return 1; + /* Have terminal input */ + return 1; - } else { - tcsetattr(fd, TCSANOW, &orig); + } else if (netlink_fd >= 0 && FD_ISSET(netlink_fd, &rfds)) { + if (!process_netlink()) + continue; /* Just drain the inotify buffer */ - while (read(inotify_fd, buffer, sizeof (buffer)) > 0); + } else if (inotify_fd >= 0 && FD_ISSET(inotify_fd, &rfds)) { + while (read(inotify_fd, buffer, sizeof (buffer)) > 0); + } + + tcsetattr(fd, TCSANOW, &orig); /* Need to reprompt */ return 0; @@ -2371,6 +2456,10 @@ static void output_special_char(unsigned char c, struct options *op, struct ifaddrs *addrs = NULL; char iface[128]; +#ifdef AGETTY_RELOAD + open_netlink(); +#endif + if (getifaddrs(&addrs)) break; |