ifplugd.c 19 KB

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