Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/networking/ifplugd.c

Parent Directory Parent Directory | Revision Log Revision Log


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