diff --git a/src/ndhc.c b/src/ndhc.c index 9145d01..fcbc778 100644 --- a/src/ndhc.c +++ b/src/ndhc.c @@ -282,7 +282,7 @@ static void do_ndhc_work(void) { struct epoll_event events[NDHC_NUM_EP_FDS]; long long nowts; - int timeout = -1; + int timeout; cs.epollFd = epoll_create1(0); if (cs.epollFd < 0) @@ -299,11 +299,9 @@ static void do_ndhc_work(void) epoll_add(cs.epollFd, sockdStream[0]); if (client_config.enable_rfkill && cs.rfkillFd != -1) epoll_add(cs.epollFd, cs.rfkillFd); - if (!cs.rfkill_set) { - start_dhcp_listen(&cs); - nowts = curms(); - goto jumpstart; - } + start_dhcp_listen(&cs); + nowts = curms(); + goto jumpstart; for (;;) { int r = epoll_wait(cs.epollFd, events, NDHC_NUM_EP_FDS, timeout); @@ -482,6 +480,41 @@ void background(void) write_pid(pidfile); } +static void wait_for_rfkill() +{ + cs.rfkill_set = 1; + struct epoll_event events[2]; + int rfkfd = rfkill_open(&client_config.enable_rfkill); + if (rfkfd < 0) + suicide("can't wait for rfkill to end if /dev/rfkill can't be opened"); + int epfd = epoll_create1(0); + if (epfd < 0) + suicide("epoll_create1 failed"); + epoll_add(epfd, rfkfd); + for (;;) { + int r = epoll_wait(epfd, events, 2, -1); + if (r < 0) { + if (errno == EINTR) + continue; + else + suicide("epoll_wait failed"); + } + for (int i = 0; i < r; ++i) { + int fd = events[i].data.fd; + if (fd == rfkfd) { + if (events[i].events & EPOLLIN) { + if (!rfkill_wait_for_end(&cs, rfkfd)) + goto rfkill_gone; + } + } else + suicide("epoll_wait: unknown fd"); + } + } +rfkill_gone: + close(epfd); + close(rfkfd); +} + int main(int argc, char *argv[]) { parse_cmdline(argc, argv); @@ -502,11 +535,7 @@ int main(int argc, char *argv[]) switch (perform_ifup()) { case 1: cs.ifsPrevState = IFS_UP; case 0: break; - case -3: - cs.rfkill_set = 1; - cs.rfkill_at_init = 1; - cs.ifsPrevState = IFS_DOWN; - break; + case -3: wait_for_rfkill(); break; default: suicide("failed to set the interface to up state"); } diff --git a/src/ndhc.h b/src/ndhc.h index 152362e..bb20992 100644 --- a/src/ndhc.h +++ b/src/ndhc.h @@ -47,7 +47,7 @@ struct client_state_t { struct nk_random_state_u32 rnd32_state; uint8_t routerArp[6], serverArp[6]; uint8_t using_dhcp_bpf, init, got_router_arp, got_server_arp; - uint8_t rfkill_set, rfkill_at_init; + uint8_t rfkill_set; }; struct client_config_t { diff --git a/src/rfkill.c b/src/rfkill.c index 1e2f155..d4a3b0d 100644 --- a/src/rfkill.c +++ b/src/rfkill.c @@ -52,56 +52,89 @@ int rfkill_open(char enable_rfkill[static 1]) return r; } -void handle_rfkill_notice(struct client_state_t cs[static 1], uint32_t rfkidx) +static int rfkill_check(struct client_state_t cs[static 1], uint32_t rfkidx, + int (*rfenable)(struct client_state_t[static 1]), + int (*rfdisable)(struct client_state_t[static 1])) { struct rfkill_event event; ssize_t len = safe_read(cs->rfkillFd, (char *)&event, sizeof event); if (len < 0) { log_error("rfkill: safe_read failed: %s", strerror(errno)); - return; + return -1; } if (len != RFKILL_EVENT_SIZE_V1) { log_error("rfkill: event has unexpected size: %d", len); - return; + return -1; } log_line("rfkill: idx[%u] type[%u] op[%u] soft[%u] hard[%u]", event.idx, event.type, event.op, event.soft, event.hard); if (event.idx != rfkidx) - return; + return 0; if (event.op != RFKILL_OP_CHANGE && event.op != RFKILL_OP_CHANGE_ALL) - return; + return 0; if (event.soft || event.hard) { - cs->rfkill_set = 1; - if (cs->ifsPrevState == IFS_UP) { - log_line("rfkill: radio now blocked; bringing interface down"); - cs->ifsPrevState = IFS_DOWN; - ifnocarrier_action(cs); - } else - log_line("rfkill: radio now blocked, but interface isn't up"); + return rfenable(cs); } else { - cs->rfkill_set = 0; - if (cs->ifsPrevState == IFS_DOWN) { - log_line("rfkill: radio now unblocked; bringing interface up"); - if (cs->rfkill_at_init) { - cs->rfkill_at_init = 0; - switch (perform_ifup()) { - case 1: case 0: break; - case -3: - cs->rfkill_set = 1; - cs->rfkill_at_init = 1; - log_line("rfkill: radio immediately blocked again; spurious?"); - return; - default: suicide("failed to set the interface to up state"); - } - } - cs->ifsPrevState = IFS_UP; - ifup_action(cs); - } else { - if (cs->ifsPrevState == IFS_SHUT) - log_line("rfkill: radio now unblocked, but interface was shut down by user"); - else - log_line("rfkill: radio now unblocked, but interface is removed"); - } + return rfdisable(cs); } } +static int handle_rfkill_notice_enable(struct client_state_t cs[static 1]) +{ + cs->rfkill_set = 1; + if (cs->ifsPrevState == IFS_UP) { + log_line("rfkill: radio now blocked; bringing interface down"); + cs->ifsPrevState = IFS_DOWN; + ifnocarrier_action(cs); + } else + log_line("rfkill: radio now blocked, but interface isn't up"); + return 0; +} + +static int handle_rfkill_notice_disable(struct client_state_t cs[static 1]) +{ + cs->rfkill_set = 0; + if (cs->ifsPrevState == IFS_DOWN) { + log_line("rfkill: radio now unblocked; bringing interface up"); + cs->ifsPrevState = IFS_UP; + ifup_action(cs); + } else { + if (cs->ifsPrevState == IFS_SHUT) + log_line("rfkill: radio now unblocked, but interface was shut down by user"); + else + log_line("rfkill: radio now unblocked, but interface is removed"); + } + return 0; +} + +static int rfkill_wait_for_end_enable(struct client_state_t cs[static 1]) +{ + (void)cs; + return -1; +} + +static int rfkill_wait_for_end_disable(struct client_state_t cs[static 1]) +{ + switch (perform_ifup()) { + case 1: case 0: + cs->rfkill_set = 0; + return 0; + case -3: + log_line("rfkill: radio immediately blocked again; spurious?"); + return -1; + default: suicide("failed to set the interface to up state"); + } +} + +int handle_rfkill_notice(struct client_state_t cs[static 1], uint32_t rfkidx) +{ + return rfkill_check(cs, rfkidx, handle_rfkill_notice_enable, + handle_rfkill_notice_disable); +} + +int rfkill_wait_for_end(struct client_state_t cs[static 1], uint32_t rfkidx) +{ + return rfkill_check(cs, rfkidx, rfkill_wait_for_end_enable, + rfkill_wait_for_end_disable); +} + diff --git a/src/rfkill.h b/src/rfkill.h index ac2ae18..96ab123 100644 --- a/src/rfkill.h +++ b/src/rfkill.h @@ -29,7 +29,8 @@ */ int rfkill_open(char enable_rfkill[static 1]); -void handle_rfkill_notice(struct client_state_t cs[static 1], uint32_t rfkidx); +int handle_rfkill_notice(struct client_state_t cs[static 1], uint32_t rfkidx); +int rfkill_wait_for_end(struct client_state_t cs[static 1], uint32_t rfkidx); #endif