1#include <err.h>2#include <errno.h>3#include <limits.h>4#include <stdbool.h>5#include <stdio.h>6#include <stdlib.h>7#include <string.h>8#include <unistd.h>9#include <semaphore.h>10#include <pthread.h>1112#include <sys/ioctl.h>13#include <sys/types.h>1415#include <net/if.h>16#include <libmnl/libmnl.h>17#include <linux/if.h>18#include <linux/if_link.h>19#include <linux/rtnetlink.h>2021/* Timeout after 30s by default, set to zero to wait indefinitely. */22#define DEFAULT_TIMEOUT 302324struct context {25 struct mnl_socket *nl;26 unsigned int if_idx;27 sem_t *sema;28};2930static bool31get_timeout(unsigned int *r)32{33 unsigned long delay;34 const char *timeout;3536 if (!(timeout = getenv("IF_WAITIF_TIMEOUT"))) {37 *r = DEFAULT_TIMEOUT; // no timeout configured38 return true;39 }4041 errno = 0;42 delay = strtoul(timeout, NULL, 10);43 if (!delay && errno) {44 return false;45 } else if (delay > UINT_MAX) {46 errno = EOVERFLOW;47 return false;48 }4950 *r = (unsigned int)delay;51 return true;52}5354static int55data_cb(const struct nlmsghdr *nlh, void *arg)56{57 struct ifinfomsg *ifm;58 struct context *ctx;5960 ctx = (struct context *)arg;61 ifm = mnl_nlmsg_get_payload(nlh);6263 if ((unsigned)ifm->ifi_index == ctx->if_idx && ifm->ifi_flags & IFF_RUNNING)64 return MNL_CB_STOP;6566 return MNL_CB_OK;67}6869static void *70netlink_loop(void *arg)71{72 ssize_t ret;73 struct context *ctx;74 char buf[MNL_SOCKET_BUFFER_SIZE];7576 ctx = (struct context*)arg;77 ret = mnl_socket_recvfrom(ctx->nl, buf, sizeof(buf));78 while (ret > 0) {79 ret = mnl_cb_run(buf, (size_t)ret, 0, 0, data_cb, arg);80 if (ret <= 0)81 break;82 ret = mnl_socket_recvfrom(ctx->nl, buf, sizeof(buf));83 }84 if (ret == -1) {85 warn("netlink_loop failed");86 return NULL;87 }8889 sem_post(ctx->sema);90 return NULL;91}9293static int94iface_is_up(struct mnl_socket *nl, const char *iface)95{96 int fd;97 size_t namelen;98 struct ifreq req;99100 namelen = strlen(iface);101 if (namelen >= IFNAMSIZ) {102 errno = ENAMETOOLONG;103 return -1;104 }105 memcpy(req.ifr_name, iface, namelen+1);106107 fd = mnl_socket_get_fd(nl);108 if (ioctl(fd, SIOCGIFFLAGS, &req) < 0)109 return -1;110 return req.ifr_flags & IFF_RUNNING;111}112113static bool114run_nl_thread(pthread_t *thread, sem_t *sema)115{116 static struct context ctx;117 const char *iface;118 int iface_state_up;119120 if (!(iface = getenv("IFACE")) || !(ctx.if_idx = if_nametoindex(iface))) {121 errno = EINVAL;122 return false;123 }124125 ctx.nl = mnl_socket_open(NETLINK_ROUTE);126 if (ctx.nl == NULL)127 return false;128 if (mnl_socket_bind(ctx.nl, RTMGRP_LINK, MNL_SOCKET_AUTOPID) == -1)129 return false;130131 iface_state_up = iface_is_up(ctx.nl, iface);132 if (iface_state_up == -1)133 return false;134135 // Check if the link was up prior to socket creation.136 if (iface_state_up) {137 mnl_socket_close(ctx.nl);138 sem_post(sema);139 } else {140 ctx.sema = sema;141 if ((errno = pthread_create(thread, NULL, netlink_loop, &ctx)))142 return false;143 }144145 return true;146}147148static bool149wait_for_iface(sem_t *sema, unsigned int timeout)150{151 struct timespec ts;152153 // No timeout → block indefinitely154 if (!timeout) {155 if (sem_wait(sema) == -1)156 return false;157 return true;158 }159160 if (clock_gettime(CLOCK_REALTIME, &ts))161 return false;162 ts.tv_sec += timeout;163 if (sem_timedwait(sema, &ts) == -1)164 return false; // detect timeout via errno165166 return true;167}168169int170main(void)171{172 sem_t sema;173 pthread_t thread;174 const char *phase;175 unsigned int timeout;176177 // XXX: The executor doesn't require root privileges but is178 // started as root by ifupdown-ng. We could drop privileges here.179180 // Executor only runs in "up" phase.181 if (!(phase = getenv("PHASE")))182 errx(EXIT_FAILURE, "Couldn't determine current phase");183 if (strcmp(phase, "up"))184 return EXIT_SUCCESS;185186 if (!get_timeout(&timeout))187 err(EXIT_FAILURE, "get_timeout failed");188189 if (getenv("VERBOSE")) {190 fprintf(stderr, "waitif: Waiting ");191 if (timeout)192 fprintf(stderr, "up to %u seconds", timeout);193 else194 fprintf(stderr, "indefinitely");195 fprintf(stderr, " for interface to come up\n");196 }197198 sem_init(&sema, 0, 0);199 if (!run_nl_thread(&thread, &sema))200 err(EXIT_FAILURE, "run_nl_thread failed");201 if (!wait_for_iface(&sema, timeout))202 err(EXIT_FAILURE, "wait_for_iface failed");203204 return EXIT_SUCCESS;205}