Magellan Linux

Contents of /tags/mkinitrd-6_3_3/busybox/networking/ifplugd.c

Parent Directory Parent Directory | Revision Log Revision Log


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