source: MondoRescue/branches/3.3/mindi-busybox/networking/ifplugd.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 19.9 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * ifplugd for busybox, based on ifplugd 0.28 (written by Lennart Poettering).
4 *
5 * Copyright (C) 2009 Maksym Kryzhanovskyy <xmaks@email.cz>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10//usage:#define ifplugd_trivial_usage
11//usage: "[OPTIONS]"
12//usage:#define ifplugd_full_usage "\n\n"
13//usage: "Network interface plug detection daemon\n"
14//usage: "\n -n Don't daemonize"
15//usage: "\n -s Don't log to syslog"
16//usage: "\n -i IFACE Interface"
17//usage: "\n -f/-F Treat link detection error as link down/link up"
18//usage: "\n (otherwise exit on error)"
19//usage: "\n -a Don't up interface at each link probe"
20//usage: "\n -M Monitor creation/destruction of interface"
21//usage: "\n (otherwise it must exist)"
22//usage: "\n -r PROG Script to run"
23//usage: "\n -x ARG Extra argument for script"
24//usage: "\n -I Don't exit on nonzero exit code from script"
25//usage: "\n -p Don't run \"up\" script on startup"
26//usage: "\n -q Don't run \"down\" script on exit"
27//usage: "\n -l Always run script on startup"
28//usage: "\n -t SECS Poll time in seconds"
29//usage: "\n -u SECS Delay before running script after link up"
30//usage: "\n -d SECS Delay after link down"
31//usage: "\n -m MODE API mode (mii, priv, ethtool, wlan, iff, auto)"
32//usage: "\n -k Kill running daemon"
33
34#include "libbb.h"
35
36#include "fix_u32.h"
37#include <linux/if.h>
38#include <linux/mii.h>
39#include <linux/ethtool.h>
40#ifdef HAVE_NET_ETHERNET_H
41/* musl breakage:
42 * In file included from /usr/include/net/ethernet.h:10,
43 * from networking/ifplugd.c:41:
44 * /usr/include/netinet/if_ether.h:96: error: redefinition of 'struct ethhdr'
45 *
46 * Build succeeds without it on musl. Commented it out.
47 * If on your system you need it, consider removing <linux/ethtool.h>
48 * and copy-pasting its definitions here (<linux/ethtool.h> is what pulls in
49 * conflicting definition of struct ethhdr on musl).
50 */
51/* # include <net/ethernet.h> */
52#endif
53#include <linux/netlink.h>
54#include <linux/rtnetlink.h>
55#include <linux/sockios.h>
56#include <syslog.h>
57
58#define __user
59#include <linux/wireless.h>
60
61#ifndef ETH_ALEN
62# define ETH_ALEN 6
63#endif
64
65/*
66From initial port to busybox, removed most of the redundancy by
67converting implementation of a polymorphic interface to the strict
68functional style. The main role is run a script when link state
69changed, other activities like audio signal or detailed reports
70are on the script itself.
71
72One questionable point of the design is netlink usage:
73
74We have 1 second timeout by default to poll the link status,
75it is short enough so that there are no real benefits in
76using netlink to get "instantaneous" interface creation/deletion
77notifications. We can check for interface existence by just
78doing some fast ioctl using its name.
79
80Netlink code then can be just dropped (1k or more?)
81*/
82
83
84#define IFPLUGD_ENV_PREVIOUS "IFPLUGD_PREVIOUS"
85#define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT"
86
87enum {
88 FLAG_NO_AUTO = 1 << 0, // -a, Do not enable interface automatically
89 FLAG_NO_DAEMON = 1 << 1, // -n, Do not daemonize
90 FLAG_NO_SYSLOG = 1 << 2, // -s, Do not use syslog, use stderr instead
91 FLAG_IGNORE_FAIL = 1 << 3, // -f, Ignore detection failure, retry instead (failure is treated as DOWN)
92 FLAG_IGNORE_FAIL_POSITIVE = 1 << 4, // -F, Ignore detection failure, retry instead (failure is treated as UP)
93 FLAG_IFACE = 1 << 5, // -i, Specify ethernet interface
94 FLAG_RUN = 1 << 6, // -r, Specify program to execute
95 FLAG_IGNORE_RETVAL = 1 << 7, // -I, Don't exit on nonzero return value of program executed
96 FLAG_POLL_TIME = 1 << 8, // -t, Specify poll time in seconds
97 FLAG_DELAY_UP = 1 << 9, // -u, Specify delay for configuring interface
98 FLAG_DELAY_DOWN = 1 << 10, // -d, Specify delay for deconfiguring interface
99 FLAG_API_MODE = 1 << 11, // -m, Force API mode (mii, priv, ethtool, wlan, auto)
100 FLAG_NO_STARTUP = 1 << 12, // -p, Don't run script on daemon startup
101 FLAG_NO_SHUTDOWN = 1 << 13, // -q, Don't run script on daemon quit
102 FLAG_INITIAL_DOWN = 1 << 14, // -l, Run "down" script on startup if no cable is detected
103 FLAG_EXTRA_ARG = 1 << 15, // -x, Specify an extra argument for action script
104 FLAG_MONITOR = 1 << 16, // -M, Use interface monitoring
105#if ENABLE_FEATURE_PIDFILE
106 FLAG_KILL = 1 << 17, // -k, Kill a running daemon
107#endif
108};
109#if ENABLE_FEATURE_PIDFILE
110# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:Mk"
111#else
112# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:M"
113#endif
114
115enum { // interface status
116 IFSTATUS_ERR = -1,
117 IFSTATUS_DOWN = 0,
118 IFSTATUS_UP = 1,
119};
120
121enum { // constant fds
122 ioctl_fd = 3,
123 netlink_fd = 4,
124};
125
126struct globals {
127 smallint iface_last_status;
128 smallint iface_prev_status;
129 smallint iface_exists;
130 smallint api_method_num;
131
132 /* Used in getopt32, must have sizeof == sizeof(int) */
133 unsigned poll_time;
134 unsigned delay_up;
135 unsigned delay_down;
136
137 const char *iface;
138 const char *api_mode;
139 const char *script_name;
140 const char *extra_arg;
141};
142#define G (*ptr_to_globals)
143#define INIT_G() do { \
144 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
145 G.iface_last_status = -1; \
146 G.iface_exists = 1; \
147 G.poll_time = 1; \
148 G.delay_down = 5; \
149 G.iface = "eth0"; \
150 G.api_mode = "a"; \
151 G.script_name = "/etc/ifplugd/ifplugd.action"; \
152} while (0)
153
154
155/* Utility routines */
156
157static void set_ifreq_to_ifname(struct ifreq *ifreq)
158{
159 memset(ifreq, 0, sizeof(struct ifreq));
160 strncpy_IFNAMSIZ(ifreq->ifr_name, G.iface);
161}
162
163static int network_ioctl(int request, void* data, const char *errmsg)
164{
165 int r = ioctl(ioctl_fd, request, data);
166 if (r < 0 && errmsg)
167 bb_perror_msg("%s failed", errmsg);
168 return r;
169}
170
171/* Link detection routines and table */
172
173static smallint detect_link_mii(void)
174{
175 /* char buffer instead of bona-fide struct avoids aliasing warning */
176 char buf[sizeof(struct ifreq)];
177 struct ifreq *const ifreq = (void *)buf;
178
179 struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
180
181 set_ifreq_to_ifname(ifreq);
182
183 if (network_ioctl(SIOCGMIIPHY, ifreq, "SIOCGMIIPHY") < 0) {
184 return IFSTATUS_ERR;
185 }
186
187 mii->reg_num = 1;
188
189 if (network_ioctl(SIOCGMIIREG, ifreq, "SIOCGMIIREG") < 0) {
190 return IFSTATUS_ERR;
191 }
192
193 return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
194}
195
196static smallint detect_link_priv(void)
197{
198 /* char buffer instead of bona-fide struct avoids aliasing warning */
199 char buf[sizeof(struct ifreq)];
200 struct ifreq *const ifreq = (void *)buf;
201
202 struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
203
204 set_ifreq_to_ifname(ifreq);
205
206 if (network_ioctl(SIOCDEVPRIVATE, ifreq, "SIOCDEVPRIVATE") < 0) {
207 return IFSTATUS_ERR;
208 }
209
210 mii->reg_num = 1;
211
212 if (network_ioctl(SIOCDEVPRIVATE+1, ifreq, "SIOCDEVPRIVATE+1") < 0) {
213 return IFSTATUS_ERR;
214 }
215
216 return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
217}
218
219static smallint detect_link_ethtool(void)
220{
221 struct ifreq ifreq;
222 struct ethtool_value edata;
223
224 set_ifreq_to_ifname(&ifreq);
225
226 edata.cmd = ETHTOOL_GLINK;
227 ifreq.ifr_data = (void*) &edata;
228
229 if (network_ioctl(SIOCETHTOOL, &ifreq, "ETHTOOL_GLINK") < 0) {
230 return IFSTATUS_ERR;
231 }
232
233 return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN;
234}
235
236static smallint detect_link_iff(void)
237{
238 struct ifreq ifreq;
239
240 set_ifreq_to_ifname(&ifreq);
241
242 if (network_ioctl(SIOCGIFFLAGS, &ifreq, "SIOCGIFFLAGS") < 0) {
243 return IFSTATUS_ERR;
244 }
245
246 /* If IFF_UP is not set (interface is down), IFF_RUNNING is never set
247 * regardless of link status. Simply continue to report last status -
248 * no point in reporting spurious link downs if interface is disabled
249 * by admin. When/if it will be brought up,
250 * we'll report real link status.
251 */
252 if (!(ifreq.ifr_flags & IFF_UP) && G.iface_last_status != IFSTATUS_ERR)
253 return G.iface_last_status;
254
255 return (ifreq.ifr_flags & IFF_RUNNING) ? IFSTATUS_UP : IFSTATUS_DOWN;
256}
257
258static smallint detect_link_wlan(void)
259{
260 int i;
261 struct iwreq iwrequest;
262 uint8_t mac[ETH_ALEN];
263
264 memset(&iwrequest, 0, sizeof(iwrequest));
265 strncpy_IFNAMSIZ(iwrequest.ifr_ifrn.ifrn_name, G.iface);
266
267 if (network_ioctl(SIOCGIWAP, &iwrequest, "SIOCGIWAP") < 0) {
268 return IFSTATUS_ERR;
269 }
270
271 memcpy(mac, &iwrequest.u.ap_addr.sa_data, ETH_ALEN);
272
273 if (mac[0] == 0xFF || mac[0] == 0x44 || mac[0] == 0x00) {
274 for (i = 1; i < ETH_ALEN; ++i) {
275 if (mac[i] != mac[0])
276 return IFSTATUS_UP;
277 }
278 return IFSTATUS_DOWN;
279 }
280
281 return IFSTATUS_UP;
282}
283
284enum { // api mode
285 API_ETHTOOL, // 'e'
286 API_MII, // 'm'
287 API_PRIVATE, // 'p'
288 API_WLAN, // 'w'
289 API_IFF, // 'i'
290 API_AUTO, // 'a'
291};
292
293static const char api_modes[] ALIGN1 = "empwia";
294
295static const struct {
296 const char *name;
297 smallint (*func)(void);
298} method_table[] = {
299 { "SIOCETHTOOL" , &detect_link_ethtool },
300 { "SIOCGMIIPHY" , &detect_link_mii },
301 { "SIOCDEVPRIVATE" , &detect_link_priv },
302 { "wireless extension", &detect_link_wlan },
303 { "IFF_RUNNING" , &detect_link_iff },
304};
305
306static const char *strstatus(int status)
307{
308 if (status == IFSTATUS_ERR)
309 return "error";
310 return "down\0up" + (status * 5);
311}
312
313static int run_script(const char *action)
314{
315 char *env_PREVIOUS, *env_CURRENT;
316 char *argv[5];
317 int r;
318
319 bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action);
320
321 argv[0] = (char*) G.script_name;
322 argv[1] = (char*) G.iface;
323 argv[2] = (char*) action;
324 argv[3] = (char*) G.extra_arg;
325 argv[4] = NULL;
326
327 env_PREVIOUS = xasprintf("%s=%s", IFPLUGD_ENV_PREVIOUS, strstatus(G.iface_prev_status));
328 putenv(env_PREVIOUS);
329 env_CURRENT = xasprintf("%s=%s", IFPLUGD_ENV_CURRENT, strstatus(G.iface_last_status));
330 putenv(env_CURRENT);
331
332 /* r < 0 - can't exec, 0 <= r < 0x180 - exited, >=0x180 - killed by sig (r-0x180) */
333 r = spawn_and_wait(argv);
334
335 unsetenv(IFPLUGD_ENV_PREVIOUS);
336 unsetenv(IFPLUGD_ENV_CURRENT);
337 free(env_PREVIOUS);
338 free(env_CURRENT);
339
340 bb_error_msg("exit code: %d", r & 0xff);
341 return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r;
342}
343
344static void up_iface(void)
345{
346 struct ifreq ifrequest;
347
348 if (!G.iface_exists)
349 return;
350
351 set_ifreq_to_ifname(&ifrequest);
352 if (network_ioctl(SIOCGIFFLAGS, &ifrequest, "getting interface flags") < 0) {
353 G.iface_exists = 0;
354 return;
355 }
356
357 if (!(ifrequest.ifr_flags & IFF_UP)) {
358 ifrequest.ifr_flags |= IFF_UP;
359 /* Let user know we mess up with interface */
360 bb_error_msg("upping interface");
361 if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0)
362 xfunc_die();
363 }
364
365#if 0 /* why do we mess with IP addr? It's not our business */
366 if (network_ioctl(SIOCGIFADDR, &ifrequest, "can't get interface address") < 0) {
367 } else if (ifrequest.ifr_addr.sa_family != AF_INET) {
368 bb_perror_msg("the interface is not IP-based");
369 } else {
370 ((struct sockaddr_in*)(&ifrequest.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
371 network_ioctl(SIOCSIFADDR, &ifrequest, "can't set interface address");
372 }
373 network_ioctl(SIOCGIFFLAGS, &ifrequest, "can't get interface flags");
374#endif
375}
376
377static void maybe_up_new_iface(void)
378{
379 if (!(option_mask32 & FLAG_NO_AUTO))
380 up_iface();
381
382#if 0 /* bloat */
383 struct ifreq ifrequest;
384 struct ethtool_drvinfo driver_info;
385
386 set_ifreq_to_ifname(&ifrequest);
387 driver_info.cmd = ETHTOOL_GDRVINFO;
388 ifrequest.ifr_data = &driver_info;
389 if (network_ioctl(SIOCETHTOOL, &ifrequest, NULL) == 0) {
390 char buf[sizeof("/xx:xx:xx:xx:xx:xx")];
391
392 /* Get MAC */
393 buf[0] = '\0';
394 set_ifreq_to_ifname(&ifrequest);
395 if (network_ioctl(SIOCGIFHWADDR, &ifrequest, NULL) == 0) {
396 sprintf(buf, "/%02X:%02X:%02X:%02X:%02X:%02X",
397 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[0]),
398 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[1]),
399 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[2]),
400 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[3]),
401 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[4]),
402 (uint8_t)(ifrequest.ifr_hwaddr.sa_data[5]));
403 }
404
405 bb_error_msg("using interface %s%s with driver<%s> (version: %s)",
406 G.iface, buf, driver_info.driver, driver_info.version);
407 }
408#endif
409 if (G.api_mode[0] == 'a')
410 G.api_method_num = API_AUTO;
411}
412
413static smallint detect_link(void)
414{
415 smallint status;
416
417 if (!G.iface_exists)
418 return (option_mask32 & FLAG_MONITOR) ? IFSTATUS_DOWN : IFSTATUS_ERR;
419
420 /* Some drivers can't detect link status when the interface is down.
421 * I imagine detect_link_iff() is the most vulnerable.
422 * That's why -a "noauto" in an option, not a hardwired behavior.
423 */
424 if (!(option_mask32 & FLAG_NO_AUTO))
425 up_iface();
426
427 if (G.api_method_num == API_AUTO) {
428 int i;
429 smallint sv_logmode;
430
431 sv_logmode = logmode;
432 for (i = 0; i < ARRAY_SIZE(method_table); i++) {
433 logmode = LOGMODE_NONE;
434 status = method_table[i].func();
435 logmode = sv_logmode;
436 if (status != IFSTATUS_ERR) {
437 G.api_method_num = i;
438 bb_error_msg("using %s detection mode", method_table[i].name);
439 break;
440 }
441 }
442 } else {
443 status = method_table[G.api_method_num].func();
444 }
445
446 if (status == IFSTATUS_ERR) {
447 if (option_mask32 & FLAG_IGNORE_FAIL)
448 status = IFSTATUS_DOWN;
449 else if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE)
450 status = IFSTATUS_UP;
451 else if (G.api_mode[0] == 'a')
452 bb_error_msg("can't detect link status");
453 }
454
455 if (status != G.iface_last_status) {
456 G.iface_prev_status = G.iface_last_status;
457 G.iface_last_status = status;
458 }
459
460 return status;
461}
462
463static NOINLINE int check_existence_through_netlink(void)
464{
465 int iface_len;
466 /* Buffer was 1K, but on linux-3.9.9 it was reported to be too small.
467 * netlink.h: "limit to 8K to avoid MSG_TRUNC when PAGE_SIZE is very large".
468 * Note: on error returns (-1) we exit, no need to free replybuf.
469 */
470 enum { BUF_SIZE = 8 * 1024 };
471 char *replybuf = xmalloc(BUF_SIZE);
472
473 iface_len = strlen(G.iface);
474 while (1) {
475 struct nlmsghdr *mhdr;
476 ssize_t bytes;
477
478 bytes = recv(netlink_fd, replybuf, BUF_SIZE, MSG_DONTWAIT);
479 if (bytes < 0) {
480 if (errno == EAGAIN)
481 goto ret;
482 if (errno == EINTR)
483 continue;
484 bb_perror_msg("netlink: recv");
485 return -1;
486 }
487
488 mhdr = (struct nlmsghdr*)replybuf;
489 while (bytes > 0) {
490 if (!NLMSG_OK(mhdr, bytes)) {
491 bb_error_msg("netlink packet too small or truncated");
492 return -1;
493 }
494
495 if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) {
496 struct rtattr *attr;
497 int attr_len;
498
499 if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
500 bb_error_msg("netlink packet too small or truncated");
501 return -1;
502 }
503
504 attr = IFLA_RTA(NLMSG_DATA(mhdr));
505 attr_len = IFLA_PAYLOAD(mhdr);
506
507 while (RTA_OK(attr, attr_len)) {
508 if (attr->rta_type == IFLA_IFNAME) {
509 int len = RTA_PAYLOAD(attr);
510 if (len > IFNAMSIZ)
511 len = IFNAMSIZ;
512 if (iface_len <= len
513 && strncmp(G.iface, RTA_DATA(attr), len) == 0
514 ) {
515 G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK);
516 }
517 }
518 attr = RTA_NEXT(attr, attr_len);
519 }
520 }
521
522 mhdr = NLMSG_NEXT(mhdr, bytes);
523 }
524 }
525
526 ret:
527 free(replybuf);
528 return G.iface_exists;
529}
530
531#if ENABLE_FEATURE_PIDFILE
532static NOINLINE pid_t read_pid(const char *filename)
533{
534 int len;
535 char buf[128];
536
537 len = open_read_close(filename, buf, 127);
538 if (len > 0) {
539 buf[len] = '\0';
540 /* returns ULONG_MAX on error => -1 */
541 return bb_strtoul(buf, NULL, 10);
542 }
543 return 0;
544}
545#endif
546
547int ifplugd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
548int ifplugd_main(int argc UNUSED_PARAM, char **argv)
549{
550 int iface_status;
551 int delay_time;
552 const char *iface_status_str;
553 struct pollfd netlink_pollfd[1];
554 unsigned opts;
555 const char *api_mode_found;
556#if ENABLE_FEATURE_PIDFILE
557 char *pidfile_name;
558 pid_t pid_from_pidfile;
559#endif
560
561 INIT_G();
562
563 opt_complementary = "t+:u+:d+";
564 opts = getopt32(argv, OPTION_STR,
565 &G.iface, &G.script_name, &G.poll_time, &G.delay_up,
566 &G.delay_down, &G.api_mode, &G.extra_arg);
567 G.poll_time *= 1000;
568
569 applet_name = xasprintf("ifplugd(%s)", G.iface);
570
571#if ENABLE_FEATURE_PIDFILE
572 pidfile_name = xasprintf(CONFIG_PID_FILE_PATH "/ifplugd.%s.pid", G.iface);
573 pid_from_pidfile = read_pid(pidfile_name);
574
575 if (opts & FLAG_KILL) {
576 if (pid_from_pidfile > 0)
577 /* Upstream tool use SIGINT for -k */
578 kill(pid_from_pidfile, SIGINT);
579 return EXIT_SUCCESS;
580 }
581
582 if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
583 bb_error_msg_and_die("daemon already running");
584#endif
585
586 api_mode_found = strchr(api_modes, G.api_mode[0]);
587 if (!api_mode_found)
588 bb_error_msg_and_die("unknown API mode '%s'", G.api_mode);
589 G.api_method_num = api_mode_found - api_modes;
590
591 if (!(opts & FLAG_NO_DAEMON))
592 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
593
594 xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), ioctl_fd);
595 if (opts & FLAG_MONITOR) {
596 struct sockaddr_nl addr;
597 int fd = xsocket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
598
599 memset(&addr, 0, sizeof(addr));
600 addr.nl_family = AF_NETLINK;
601 addr.nl_groups = RTMGRP_LINK;
602 addr.nl_pid = getpid();
603
604 xbind(fd, (struct sockaddr*)&addr, sizeof(addr));
605 xmove_fd(fd, netlink_fd);
606 }
607
608 write_pidfile(pidfile_name);
609
610 /* this can't be moved before socket creation */
611 if (!(opts & FLAG_NO_SYSLOG)) {
612 openlog(applet_name, 0, LOG_DAEMON);
613 logmode |= LOGMODE_SYSLOG;
614 }
615
616 bb_signals(0
617 | (1 << SIGINT )
618 | (1 << SIGTERM)
619 | (1 << SIGQUIT)
620 | (1 << SIGHUP ) /* why we ignore it? */
621 /* | (1 << SIGCHLD) - run_script does not use it anymore */
622 , record_signo);
623
624 bb_error_msg("started: %s", bb_banner);
625
626 if (opts & FLAG_MONITOR) {
627 struct ifreq ifrequest;
628 set_ifreq_to_ifname(&ifrequest);
629 G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0);
630 }
631
632 if (G.iface_exists)
633 maybe_up_new_iface();
634
635 iface_status = detect_link();
636 if (iface_status == IFSTATUS_ERR)
637 goto exiting;
638 iface_status_str = strstatus(iface_status);
639
640 if (opts & FLAG_MONITOR) {
641 bb_error_msg("interface %s",
642 G.iface_exists ? "exists"
643 : "doesn't exist, waiting");
644 }
645 /* else we assume it always exists, but don't mislead user
646 * by potentially lying that it really exists */
647
648 if (G.iface_exists) {
649 bb_error_msg("link is %s", iface_status_str);
650 }
651
652 if ((!(opts & FLAG_NO_STARTUP)
653 && iface_status == IFSTATUS_UP
654 )
655 || (opts & FLAG_INITIAL_DOWN)
656 ) {
657 if (run_script(iface_status_str) != 0)
658 goto exiting;
659 }
660
661 /* Main loop */
662 netlink_pollfd[0].fd = netlink_fd;
663 netlink_pollfd[0].events = POLLIN;
664 delay_time = 0;
665 while (1) {
666 int iface_status_old;
667
668 switch (bb_got_signal) {
669 case SIGINT:
670 case SIGTERM:
671 bb_got_signal = 0;
672 goto cleanup;
673 case SIGQUIT:
674 bb_got_signal = 0;
675 goto exiting;
676 default:
677 bb_got_signal = 0;
678 break;
679 }
680
681 if (poll(netlink_pollfd,
682 (opts & FLAG_MONITOR) ? 1 : 0,
683 G.poll_time
684 ) < 0
685 ) {
686 if (errno == EINTR)
687 continue;
688 bb_perror_msg("poll");
689 goto exiting;
690 }
691
692 if ((opts & FLAG_MONITOR)
693 && (netlink_pollfd[0].revents & POLLIN)
694 ) {
695 int iface_exists_old;
696
697 iface_exists_old = G.iface_exists;
698 G.iface_exists = check_existence_through_netlink();
699 if (G.iface_exists < 0) /* error */
700 goto exiting;
701 if (iface_exists_old != G.iface_exists) {
702 bb_error_msg("interface %sappeared",
703 G.iface_exists ? "" : "dis");
704 if (G.iface_exists)
705 maybe_up_new_iface();
706 }
707 }
708
709 /* note: if !G.iface_exists, returns DOWN */
710 iface_status_old = iface_status;
711 iface_status = detect_link();
712 if (iface_status == IFSTATUS_ERR) {
713 if (!(opts & FLAG_MONITOR))
714 goto exiting;
715 iface_status = IFSTATUS_DOWN;
716 }
717 iface_status_str = strstatus(iface_status);
718
719 if (iface_status_old != iface_status) {
720 bb_error_msg("link is %s", iface_status_str);
721
722 if (delay_time) {
723 /* link restored its old status before
724 * we ran script. don't run the script: */
725 delay_time = 0;
726 } else {
727 delay_time = monotonic_sec();
728 if (iface_status == IFSTATUS_UP)
729 delay_time += G.delay_up;
730 if (iface_status == IFSTATUS_DOWN)
731 delay_time += G.delay_down;
732#if 0 /* if you are back in 1970... */
733 if (delay_time == 0) {
734 sleep(1);
735 delay_time = 1;
736 }
737#endif
738 }
739 }
740
741 if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
742 if (run_script(iface_status_str) != 0)
743 goto exiting;
744 delay_time = 0;
745 }
746 } /* while (1) */
747
748 cleanup:
749 if (!(opts & FLAG_NO_SHUTDOWN)
750 && (iface_status == IFSTATUS_UP
751 || (iface_status == IFSTATUS_DOWN && delay_time)
752 )
753 ) {
754 setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
755 setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
756 run_script("down\0up"); /* reusing string */
757 }
758
759 exiting:
760 remove_pidfile(pidfile_name);
761 bb_error_msg_and_die("exiting");
762}
Note: See TracBrowser for help on using the repository browser.