1 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: f; c-basic-offset: 4 -*- */ |
---|
2 | /* $Id: ifplugd.c,v 1.1.1.1 2004-04-09 20:09:13 amb Exp $ */ |
---|
3 | |
---|
4 | /* |
---|
5 | * This file is part of ifplugd. |
---|
6 | * |
---|
7 | * ifplugd is free software; you can redistribute it and/or modify it |
---|
8 | * under the terms of the GNU General Public License as published by |
---|
9 | * the Free Software Foundation; either version 2 of the License, or |
---|
10 | * (at your option) any later version. |
---|
11 | * |
---|
12 | * ifplugd is distributed in the hope that it will be useful, but |
---|
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
15 | * General Public License for more details. |
---|
16 | * |
---|
17 | * You should have received a copy of the GNU General Public License |
---|
18 | * along with ifplugd; if not, write to the Free Software Foundation, |
---|
19 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
---|
20 | */ |
---|
21 | |
---|
22 | #define _GNU_SOURCE |
---|
23 | |
---|
24 | #include <stdlib.h> |
---|
25 | #include <stdio.h> |
---|
26 | #include <string.h> |
---|
27 | #include <signal.h> |
---|
28 | #include <unistd.h> |
---|
29 | #include <sys/wait.h> |
---|
30 | #include <errno.h> |
---|
31 | #include <sys/stat.h> |
---|
32 | #include <getopt.h> |
---|
33 | #include <stdarg.h> |
---|
34 | #include <syslog.h> |
---|
35 | #include <fcntl.h> |
---|
36 | #include <linux/kd.h> |
---|
37 | #include <sys/ioctl.h> |
---|
38 | #include <limits.h> |
---|
39 | #include <net/if.h> |
---|
40 | #include <linux/sockios.h> |
---|
41 | #include <sys/types.h> |
---|
42 | #include <ctype.h> |
---|
43 | #include <sys/time.h> |
---|
44 | #include <time.h> |
---|
45 | |
---|
46 | #include <libdaemon/dlog.h> |
---|
47 | #include <libdaemon/dpid.h> |
---|
48 | #include <libdaemon/dsignal.h> |
---|
49 | #include <libdaemon/dfork.h> |
---|
50 | |
---|
51 | #include "ethtool-local.h" |
---|
52 | #include "interface.h" |
---|
53 | #include "nlapi.h" |
---|
54 | #include "ifmonitor.h" |
---|
55 | #include "svn-revision.h" |
---|
56 | |
---|
57 | #ifdef HAVE_CONFIG_H |
---|
58 | #include <config.h> |
---|
59 | #endif |
---|
60 | |
---|
61 | #define VARRUN "/var/run" |
---|
62 | #define IFPLUGD_ENV_PREVIOUS "IFPLUGD_PREVIOUS" |
---|
63 | #define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT" |
---|
64 | |
---|
65 | int interface_auto_up = 1, |
---|
66 | interface_do_message = 1; |
---|
67 | |
---|
68 | char *interface = NULL; |
---|
69 | char *run = SYSCONFDIR"/ifplugd/ifplugd.action"; |
---|
70 | char *extra_arg = NULL; |
---|
71 | |
---|
72 | int polltime = 1, |
---|
73 | delay_up = 0, |
---|
74 | delay_down = 5; |
---|
75 | |
---|
76 | int daemonize = 1, |
---|
77 | use_beep = 1, |
---|
78 | no_shutdown_script = 0, |
---|
79 | wait_on_fork = 0, |
---|
80 | wait_on_kill = 0, |
---|
81 | use_syslog = 1, |
---|
82 | ignore_retval = 0, |
---|
83 | initial_down = 0, |
---|
84 | use_ifmonitor = 0; |
---|
85 | |
---|
86 | int disabled = 0; |
---|
87 | |
---|
88 | interface_status_t failure_status = IFSTATUS_ERR; |
---|
89 | |
---|
90 | enum { API_AUTO, API_ETHTOOL, API_MII, API_PRIVATE, API_WLAN } api_mode = API_AUTO; |
---|
91 | |
---|
92 | interface_status_t (*detect_beat_func)(int, char*); |
---|
93 | interface_status_t (*cached_detect_beat_func)(int, char*) = NULL; |
---|
94 | |
---|
95 | // 0: high, 1: low, 2: very low |
---|
96 | void beep(int b) { |
---|
97 | int fd = -1, argp; |
---|
98 | |
---|
99 | if (!use_beep) |
---|
100 | return; |
---|
101 | |
---|
102 | if ((fd = open("/dev/tty1", O_WRONLY|O_NOCTTY)) < 0) { |
---|
103 | use_beep = 0; |
---|
104 | daemon_log(LOG_WARNING, "Could not open /dev/tty, cannot beep."); |
---|
105 | goto finish; |
---|
106 | } |
---|
107 | |
---|
108 | switch (b) { |
---|
109 | case 0: argp = (100<<16) + 0x637; break; |
---|
110 | case 1: argp = (100<<16) + 0x937; break; |
---|
111 | default: argp = (100<<16) + 0x1237; break; |
---|
112 | } |
---|
113 | |
---|
114 | if (ioctl(fd, KDMKTONE, argp) != 0) { |
---|
115 | use_beep = 0; |
---|
116 | daemon_log(LOG_WARNING, "Beep failure, disabled."); |
---|
117 | goto finish; |
---|
118 | } |
---|
119 | |
---|
120 | usleep((argp >> 16)*1000); |
---|
121 | |
---|
122 | finish: |
---|
123 | |
---|
124 | if (fd >= 0) |
---|
125 | close(fd); |
---|
126 | |
---|
127 | return; |
---|
128 | } |
---|
129 | |
---|
130 | const char *pid_file_proc() { |
---|
131 | static char fn[PATH_MAX]; |
---|
132 | snprintf(fn, sizeof(fn), "%s/ifplugd.%s.pid", VARRUN, interface); |
---|
133 | return fn; |
---|
134 | } |
---|
135 | |
---|
136 | int action(interface_status_t status) { |
---|
137 | pid_t pid; |
---|
138 | int _pipe[2]; |
---|
139 | unsigned n = 0; |
---|
140 | static char buf[256]; |
---|
141 | char *arg = (status == IFSTATUS_UP ? "up" : "down"); |
---|
142 | int sigfd, r; |
---|
143 | fd_set rfds; |
---|
144 | |
---|
145 | daemon_log(LOG_INFO, "Executing '%s %s %s'.", run, interface, arg); |
---|
146 | |
---|
147 | if (pipe(_pipe) < 0) { |
---|
148 | daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno)); |
---|
149 | return -1; |
---|
150 | } |
---|
151 | |
---|
152 | if ((pid = fork()) < 0) { |
---|
153 | daemon_log(LOG_ERR, "fork() failed: %s", strerror(errno)); |
---|
154 | return -1; |
---|
155 | |
---|
156 | } else if (pid == 0) { |
---|
157 | dup2(_pipe[1], 1); |
---|
158 | dup2(_pipe[1], 2); |
---|
159 | |
---|
160 | if (_pipe[0] > 2) |
---|
161 | close(_pipe[0]); |
---|
162 | |
---|
163 | if (_pipe[1] > 2) |
---|
164 | close(_pipe[1]); |
---|
165 | |
---|
166 | umask(0022); /* Set up a sane umask */ |
---|
167 | |
---|
168 | execl(run, run, interface, arg, extra_arg, 0); |
---|
169 | |
---|
170 | _exit(EXIT_FAILURE); |
---|
171 | } |
---|
172 | |
---|
173 | close(_pipe[1]); |
---|
174 | |
---|
175 | FD_ZERO(&rfds); |
---|
176 | FD_SET(_pipe[0], &rfds); |
---|
177 | FD_SET(sigfd = daemon_signal_fd(), &rfds); |
---|
178 | |
---|
179 | n = 0; |
---|
180 | |
---|
181 | for (;;) { |
---|
182 | fd_set wrfds = rfds; |
---|
183 | |
---|
184 | if (select(FD_SETSIZE, &wrfds, NULL, NULL, NULL) < 0) { |
---|
185 | |
---|
186 | if (errno == EINTR) |
---|
187 | continue; |
---|
188 | |
---|
189 | break; |
---|
190 | } |
---|
191 | |
---|
192 | |
---|
193 | if (FD_ISSET(_pipe[0], &wrfds)) { |
---|
194 | char c; |
---|
195 | |
---|
196 | if (read(_pipe[0], &c, 1) != 1) |
---|
197 | break; |
---|
198 | |
---|
199 | buf[n] = c; |
---|
200 | |
---|
201 | if (c == '\n' || n >= sizeof(buf) - 2) { |
---|
202 | if (c != '\n') n++; |
---|
203 | buf[n] = 0; |
---|
204 | |
---|
205 | if (buf[0]) |
---|
206 | daemon_log(LOG_WARNING, "client: %s", buf); |
---|
207 | |
---|
208 | n = 0; |
---|
209 | } else |
---|
210 | n++; |
---|
211 | } |
---|
212 | |
---|
213 | if (FD_ISSET(sigfd, &wrfds)) { |
---|
214 | int sig; |
---|
215 | |
---|
216 | if ((sig = daemon_signal_next()) < 0) { |
---|
217 | daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno)); |
---|
218 | break; |
---|
219 | } |
---|
220 | |
---|
221 | if (sig != SIGCHLD) { |
---|
222 | daemon_log(LOG_WARNING, "Killing child."); |
---|
223 | kill(pid, SIGTERM); |
---|
224 | } |
---|
225 | } |
---|
226 | |
---|
227 | } |
---|
228 | |
---|
229 | if (n > 0) { |
---|
230 | buf[n] = 0; |
---|
231 | daemon_log(LOG_WARNING, "client: %s", buf); |
---|
232 | } |
---|
233 | |
---|
234 | waitpid(pid, &r, 0); |
---|
235 | |
---|
236 | close(_pipe[0]); |
---|
237 | |
---|
238 | if (!WIFEXITED(r) || WEXITSTATUS(r) != 0) { |
---|
239 | if (status == IFSTATUS_UP) |
---|
240 | beep(2); |
---|
241 | daemon_log(LOG_ERR, "Program execution failed, return value is %i.", WEXITSTATUS(r)); |
---|
242 | |
---|
243 | return ignore_retval ? 0 : -1; |
---|
244 | } else { |
---|
245 | if (status == IFSTATUS_UP) |
---|
246 | beep(0); |
---|
247 | |
---|
248 | daemon_log(LOG_INFO, "Program executed successfully."); |
---|
249 | return 0; |
---|
250 | } |
---|
251 | } |
---|
252 | |
---|
253 | interface_status_t detect_beat_auto(int fd, char *iface) { |
---|
254 | interface_status_t status; |
---|
255 | |
---|
256 | if (cached_detect_beat_func && (status = cached_detect_beat_func(fd, iface)) != IFSTATUS_ERR) |
---|
257 | return status; |
---|
258 | |
---|
259 | if ((status = interface_detect_beat_mii(fd, iface)) != IFSTATUS_ERR) { |
---|
260 | cached_detect_beat_func = interface_detect_beat_mii; |
---|
261 | daemon_log(LOG_INFO, "Using detection mode: SIOCGMIIPHY"); |
---|
262 | return status; |
---|
263 | } |
---|
264 | |
---|
265 | if ((status = interface_detect_beat_ethtool(fd, iface)) != IFSTATUS_ERR) { |
---|
266 | cached_detect_beat_func = interface_detect_beat_ethtool; |
---|
267 | daemon_log(LOG_INFO, "Using detection mode: SIOCETHTOOL"); |
---|
268 | return status; |
---|
269 | } |
---|
270 | |
---|
271 | if ((status = interface_detect_beat_wlan(fd, iface)) != IFSTATUS_ERR) { |
---|
272 | cached_detect_beat_func = interface_detect_beat_wlan; |
---|
273 | daemon_log(LOG_INFO, "Using detection mode: wireless extension"); |
---|
274 | return status; |
---|
275 | } |
---|
276 | |
---|
277 | if ((status = interface_detect_beat_priv(fd, iface)) != IFSTATUS_ERR) { |
---|
278 | cached_detect_beat_func = interface_detect_beat_priv; |
---|
279 | daemon_log(LOG_INFO, "Using detection mode: SIOCDEVPRIVATE"); |
---|
280 | return status; |
---|
281 | } |
---|
282 | |
---|
283 | return IFSTATUS_ERR; |
---|
284 | } |
---|
285 | |
---|
286 | char *strstatus(interface_status_t s) { |
---|
287 | switch(s) { |
---|
288 | case IFSTATUS_UP: return "up"; |
---|
289 | case IFSTATUS_DOWN: return "down"; |
---|
290 | case IFSTATUS_ERR: return "error"; |
---|
291 | default: return "disabled"; |
---|
292 | } |
---|
293 | } |
---|
294 | |
---|
295 | interface_status_t detect_beat(int fd, char*iface) { |
---|
296 | interface_status_t status; |
---|
297 | static interface_status_t last_status = (interface_status_t) -1; |
---|
298 | |
---|
299 | if (disabled) |
---|
300 | return IFSTATUS_DOWN; |
---|
301 | |
---|
302 | if ((status = detect_beat_func(fd, iface)) == IFSTATUS_ERR) |
---|
303 | status = failure_status; |
---|
304 | |
---|
305 | if (status == IFSTATUS_ERR && detect_beat_func == detect_beat_auto) |
---|
306 | daemon_log(LOG_INFO, "Failed to detect plug status of %s", interface); |
---|
307 | |
---|
308 | if (status != last_status) { |
---|
309 | setenv(IFPLUGD_ENV_PREVIOUS, strstatus(last_status), 1); |
---|
310 | setenv(IFPLUGD_ENV_CURRENT, strstatus(status), 1); |
---|
311 | last_status = status; |
---|
312 | } |
---|
313 | |
---|
314 | return status; |
---|
315 | } |
---|
316 | |
---|
317 | int welcome_iface(int fd, char *iface) { |
---|
318 | struct ifreq ifr; |
---|
319 | struct ethtool_drvinfo drvinfo; |
---|
320 | char txt[256]; |
---|
321 | |
---|
322 | if (interface_auto_up) |
---|
323 | interface_up(fd, iface); |
---|
324 | |
---|
325 | memset(&ifr, 0, sizeof(ifr)); |
---|
326 | strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1); |
---|
327 | |
---|
328 | if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) |
---|
329 | snprintf(txt, sizeof(txt)-1, "Using interface %s", iface); |
---|
330 | else |
---|
331 | snprintf(txt, sizeof(txt)-1, "Using interface %s/%02X:%02X:%02X:%02X:%02X:%02X", iface, ifr.ifr_hwaddr.sa_data[0] & 0xFF, ifr.ifr_hwaddr.sa_data[1] & 0xFF, ifr.ifr_hwaddr.sa_data[2] & 0xFF, ifr.ifr_hwaddr.sa_data[3] & 0xFF, ifr.ifr_hwaddr.sa_data[4] & 0xFF, ifr.ifr_hwaddr.sa_data[5] & 0xFF); |
---|
332 | |
---|
333 | memset(&ifr, 0, sizeof(ifr)); |
---|
334 | strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1); |
---|
335 | |
---|
336 | drvinfo.cmd = ETHTOOL_GDRVINFO; |
---|
337 | ifr.ifr_data = (caddr_t) &drvinfo; |
---|
338 | |
---|
339 | if (ioctl(fd, SIOCETHTOOL, &ifr) != -1) |
---|
340 | daemon_log(LOG_INFO, "%s with driver <%s> (version: %s)", txt, drvinfo.driver, drvinfo.version); |
---|
341 | else |
---|
342 | daemon_log(LOG_INFO, "%s", txt); |
---|
343 | |
---|
344 | cached_detect_beat_func = NULL; |
---|
345 | |
---|
346 | return 0; |
---|
347 | } |
---|
348 | |
---|
349 | int ifmonitor_cb(int b, int index, unsigned short type, const char *name) { |
---|
350 | if (!name) |
---|
351 | return 0; |
---|
352 | |
---|
353 | if (!strcmp(name, interface)) |
---|
354 | disabled = !b; |
---|
355 | |
---|
356 | return 0; |
---|
357 | } |
---|
358 | |
---|
359 | int is_iface_available(int s, char *p) { |
---|
360 | struct ifreq req; |
---|
361 | int r; |
---|
362 | |
---|
363 | memset(&req, 0, sizeof(req)); |
---|
364 | strncpy(req.ifr_name, p, IFNAMSIZ); |
---|
365 | |
---|
366 | if ((r = ioctl(s, SIOCGIFINDEX, &req)) < 0 && errno != ENODEV) { |
---|
367 | daemon_log(LOG_ERR, "SIOCGIFINDEX failed: %s\n", strerror(errno)); |
---|
368 | return -1; |
---|
369 | } |
---|
370 | return r >= 0 && req.ifr_ifindex >= 0; |
---|
371 | } |
---|
372 | |
---|
373 | void work(void) { |
---|
374 | interface_status_t status; |
---|
375 | int fd = -1; |
---|
376 | fd_set fds; |
---|
377 | int sigfd; |
---|
378 | time_t t = 0; |
---|
379 | int send_retval = 1; |
---|
380 | int paused = 0; |
---|
381 | static char log_ident[256]; |
---|
382 | |
---|
383 | snprintf(log_ident, sizeof(log_ident), "ifplugd(%s)", interface); |
---|
384 | |
---|
385 | daemon_log_ident = log_ident; |
---|
386 | |
---|
387 | daemon_log(LOG_INFO, "ifplugd "VERSION" initializing%s.", use_ifmonitor ? ", using NETLINK device monitoring" : ""); |
---|
388 | |
---|
389 | if (daemon_pid_file_create() < 0) { |
---|
390 | daemon_log(LOG_ERR, "Could not create PID file %s.", daemon_pid_file_proc()); |
---|
391 | goto finish; |
---|
392 | } |
---|
393 | |
---|
394 | if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, SIGCHLD, SIGUSR1, SIGUSR2, -1) < 0) { |
---|
395 | daemon_log(LOG_ERR, "Could not register signal handler: %s", strerror(errno)); |
---|
396 | goto finish; |
---|
397 | } |
---|
398 | |
---|
399 | switch (api_mode) { |
---|
400 | case API_ETHTOOL: detect_beat_func = interface_detect_beat_ethtool; break; |
---|
401 | case API_MII: detect_beat_func = interface_detect_beat_mii; break; |
---|
402 | case API_PRIVATE: detect_beat_func = interface_detect_beat_priv; break; |
---|
403 | case API_WLAN: detect_beat_func = interface_detect_beat_wlan;break; |
---|
404 | |
---|
405 | default: |
---|
406 | detect_beat_func = detect_beat_auto; |
---|
407 | interface_do_message = 0; |
---|
408 | break; |
---|
409 | } |
---|
410 | |
---|
411 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { |
---|
412 | daemon_log(LOG_ERR, "socket(): %s", strerror(errno)); |
---|
413 | goto finish; |
---|
414 | } |
---|
415 | |
---|
416 | if (use_ifmonitor) { |
---|
417 | int b; |
---|
418 | if ((b = is_iface_available(fd, interface)) < 0) { |
---|
419 | daemon_log(LOG_ERR, "Failed to check interface availabilty!"); |
---|
420 | goto finish; |
---|
421 | } |
---|
422 | |
---|
423 | disabled = !b; |
---|
424 | |
---|
425 | if (nlapi_open(RTMGRP_LINK) < 0) |
---|
426 | goto finish; |
---|
427 | |
---|
428 | if (ifmonitor_init(ifmonitor_cb) < 0) |
---|
429 | goto finish; |
---|
430 | } else |
---|
431 | disabled = 0; |
---|
432 | |
---|
433 | if (!disabled) { |
---|
434 | if (welcome_iface(fd, interface) < 0) |
---|
435 | goto finish; |
---|
436 | } |
---|
437 | |
---|
438 | if ((status = detect_beat(fd, interface)) == IFSTATUS_ERR) |
---|
439 | goto finish; |
---|
440 | |
---|
441 | daemon_log(LOG_INFO, "Initialization complete, link beat %sdetected%s.", status == IFSTATUS_UP ? "" : "not ", use_ifmonitor ? (disabled ? ", interface disabled" : ", interface enabled") : ""); |
---|
442 | beep(status == IFSTATUS_UP ? 0 : 1); |
---|
443 | |
---|
444 | if (status == IFSTATUS_UP || initial_down) |
---|
445 | if (action(status) < 0) |
---|
446 | goto finish; |
---|
447 | |
---|
448 | if (send_retval && daemonize && wait_on_fork) { |
---|
449 | char c = status == IFSTATUS_UP ? 2 : (status == IFSTATUS_DOWN ? 3 : 1); |
---|
450 | daemon_retval_send(c); |
---|
451 | send_retval = 0; |
---|
452 | } |
---|
453 | |
---|
454 | FD_ZERO(&fds); |
---|
455 | FD_SET(sigfd = daemon_signal_fd(), &fds); |
---|
456 | |
---|
457 | if (use_ifmonitor) |
---|
458 | FD_SET(nlapi_fd, &fds); |
---|
459 | |
---|
460 | for (;;) { |
---|
461 | interface_status_t s; |
---|
462 | fd_set qfds = fds; |
---|
463 | int d; |
---|
464 | struct timeval tv = { polltime, 0 }; |
---|
465 | |
---|
466 | if (select(FD_SETSIZE, &qfds, NULL, NULL, &tv) < 0) { |
---|
467 | if (errno == EINTR) |
---|
468 | continue; |
---|
469 | |
---|
470 | daemon_log(LOG_ERR, "select(): %s", strerror(errno)); |
---|
471 | goto finish; |
---|
472 | } |
---|
473 | |
---|
474 | //daemon_log(LOG_INFO, "select()"); |
---|
475 | |
---|
476 | d = disabled; |
---|
477 | s = status; |
---|
478 | |
---|
479 | if (use_ifmonitor) { |
---|
480 | |
---|
481 | if (FD_ISSET(nlapi_fd, &qfds)) { |
---|
482 | if (nlapi_work(0) < 0) |
---|
483 | goto finish; |
---|
484 | } |
---|
485 | |
---|
486 | if (d && !disabled) { |
---|
487 | daemon_log(LOG_INFO, "Interface enabled"); |
---|
488 | welcome_iface(fd, interface); |
---|
489 | status = IFSTATUS_DOWN; |
---|
490 | } |
---|
491 | |
---|
492 | if (!d && disabled) { |
---|
493 | daemon_log(LOG_INFO, "Interface disabled"); |
---|
494 | status = IFSTATUS_DOWN; |
---|
495 | } |
---|
496 | } |
---|
497 | |
---|
498 | |
---|
499 | if (!paused && !disabled) { |
---|
500 | //daemon_log(LOG_INFO, "detect"); |
---|
501 | if ((status = detect_beat(fd, interface)) == IFSTATUS_ERR) { |
---|
502 | if (!use_ifmonitor) |
---|
503 | goto finish; |
---|
504 | |
---|
505 | status = IFSTATUS_DOWN; |
---|
506 | } |
---|
507 | } |
---|
508 | |
---|
509 | if (status != s) { |
---|
510 | daemon_log(LOG_INFO, "Link beat %s.", status == IFSTATUS_DOWN ? "lost" : "detected"); |
---|
511 | beep(status == IFSTATUS_UP ? 0 : 1); |
---|
512 | |
---|
513 | if (t) |
---|
514 | t = 0; |
---|
515 | else { |
---|
516 | t = time(NULL); |
---|
517 | |
---|
518 | if (status == IFSTATUS_UP) |
---|
519 | t += delay_up; |
---|
520 | |
---|
521 | if (status == IFSTATUS_DOWN) |
---|
522 | t += delay_down; |
---|
523 | } |
---|
524 | } |
---|
525 | |
---|
526 | if (FD_ISSET(sigfd, &qfds)) { |
---|
527 | int sig; |
---|
528 | |
---|
529 | if ((sig = daemon_signal_next()) < 0) { |
---|
530 | daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno)); |
---|
531 | goto finish; |
---|
532 | } |
---|
533 | |
---|
534 | switch (sig) { |
---|
535 | |
---|
536 | case SIGINT: |
---|
537 | case SIGTERM: |
---|
538 | goto cleanup; |
---|
539 | |
---|
540 | case SIGQUIT: |
---|
541 | goto finish; |
---|
542 | |
---|
543 | case SIGCHLD: |
---|
544 | break; |
---|
545 | |
---|
546 | case SIGHUP: |
---|
547 | daemon_log(LOG_INFO, "SIGHUP: %s, link detected on %s: %s", paused ? "Suspended" : "Running", interface, status == IFSTATUS_DOWN ? "no" : "yes"); |
---|
548 | |
---|
549 | if (use_ifmonitor) |
---|
550 | daemon_log(LOG_INFO, "SIGHUP: Interface %s", disabled ? "disabled" : "enabled"); |
---|
551 | break; |
---|
552 | |
---|
553 | case SIGUSR1: |
---|
554 | daemon_log(LOG_INFO, "SIGUSR1: Daemon suspended (#%i)", ++paused); |
---|
555 | break; |
---|
556 | |
---|
557 | case SIGUSR2: |
---|
558 | if (paused > 0) { |
---|
559 | daemon_log(LOG_INFO, "SIGUSR2: Daemon resumed (#%i)", paused); |
---|
560 | paused --; |
---|
561 | } |
---|
562 | |
---|
563 | break; |
---|
564 | |
---|
565 | default: |
---|
566 | daemon_log(LOG_INFO, "Ignoring unknown signal %s", strsignal(sig)); |
---|
567 | break; |
---|
568 | } |
---|
569 | } |
---|
570 | |
---|
571 | if (t && t < time(NULL)) { |
---|
572 | t = 0; |
---|
573 | |
---|
574 | if (action(status) < 0) |
---|
575 | goto finish; |
---|
576 | } |
---|
577 | } |
---|
578 | |
---|
579 | cleanup: |
---|
580 | if (!no_shutdown_script && (status == IFSTATUS_UP || (status == IFSTATUS_DOWN && t))) { |
---|
581 | setenv(IFPLUGD_ENV_PREVIOUS, strstatus(status), 1); |
---|
582 | setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1); |
---|
583 | action(IFSTATUS_DOWN); |
---|
584 | beep(1); |
---|
585 | } |
---|
586 | |
---|
587 | finish: |
---|
588 | |
---|
589 | if (fd >= 0) |
---|
590 | close(fd); |
---|
591 | |
---|
592 | if (use_ifmonitor) |
---|
593 | nlapi_close(); |
---|
594 | |
---|
595 | if (send_retval && daemonize && wait_on_fork) |
---|
596 | daemon_retval_send(255); |
---|
597 | |
---|
598 | daemon_pid_file_remove(); |
---|
599 | daemon_signal_done(); |
---|
600 | |
---|
601 | daemon_log(LOG_INFO, "Exiting."); |
---|
602 | } |
---|
603 | |
---|
604 | void usage(char *p) { |
---|
605 | char *m; |
---|
606 | |
---|
607 | switch (api_mode) { |
---|
608 | case API_ETHTOOL: m = "ethtool"; break; |
---|
609 | case API_MII: m = "mii"; break; |
---|
610 | case API_PRIVATE: m = "priv"; break; |
---|
611 | case API_WLAN: m = "wlan"; break; |
---|
612 | default: m = "auto"; |
---|
613 | } |
---|
614 | |
---|
615 | if (strrchr(p, '/')) |
---|
616 | p = strchr(p, '/')+1; |
---|
617 | |
---|
618 | printf("%s -- Network Interface Plug Detection Daemon\n\n" |
---|
619 | "Usage: %s [options]\n\n" |
---|
620 | "Options:\n" |
---|
621 | " -a --no-auto Do not enable interface automatically (%s)\n" |
---|
622 | " -n --no-daemon Do not daemonize (for debugging) (%s)\n" |
---|
623 | " -s --no-syslog Do not use syslog, use stderr instead (for debugging) (%s)\n" |
---|
624 | " -b --no-beep Do not beep (%s)\n" |
---|
625 | " -f --ignore-fail Ignore detection failure, retry instead (failure is treated as DOWN) (%s)\n" |
---|
626 | " -F --ignore-fail-positive Ignore detection failure, retry instead (failure is treated as UP) (%s)\n" |
---|
627 | " -i --iface=IFACE Specify ethernet interface (%s)\n" |
---|
628 | " -r --run=EXEC Specify program to execute (%s)\n" |
---|
629 | " -I --ignore-retval Don't exit on nonzero return value of program executed (%s)\n" |
---|
630 | " -t --poll-time=SECS Specify poll time in seconds (%i)\n" |
---|
631 | " -u --delay-up=SECS Specify delay for configuring interface (%i)\n" |
---|
632 | " -d --delay-down=SECS Specify delay for deconfiguring interface (%i)\n" |
---|
633 | " -m --api-mode=MODE Force API mode (mii, priv, ethtool, wlan, auto) (%s)\n" |
---|
634 | " -q --no-shutdown Don't run script on daemon quit (%s)\n" |
---|
635 | " -l --initial-down Run \"down\" script on startup if now cable is detected (%s)\n" |
---|
636 | " -w --wait-on-fork Wait until daemon fork finished (%s)\n" |
---|
637 | " -W --wait-on-kill When run with -k, wait until the daemon died (%s)\n" |
---|
638 | " -x --extra-arg Specify an extra argument for action script\n" |
---|
639 | " -M --monitor Use interface monitoring (%s)\n" |
---|
640 | " -h --help Show this help\n" |
---|
641 | " -k --kill Kill a running daemon\n" |
---|
642 | " -c --check-running Check if a daemon is currently running\n" |
---|
643 | " -v --version Show version\n" |
---|
644 | " -S --suspend Suspend running daemon\n" |
---|
645 | " -R --resume Resume running daemon\n" |
---|
646 | " -z --info Write status of running daemon to syslog\n", |
---|
647 | p, p, |
---|
648 | !interface_auto_up ? "on" : "off", |
---|
649 | !daemonize ? "on" : "off", |
---|
650 | !use_syslog ? "on" : "off", |
---|
651 | !use_beep ? "on" : "off", |
---|
652 | failure_status == IFSTATUS_DOWN ? "on" : "off", |
---|
653 | failure_status == IFSTATUS_UP ? "on" : "off", |
---|
654 | interface, |
---|
655 | run, |
---|
656 | ignore_retval ? "on" : "off", |
---|
657 | polltime, |
---|
658 | delay_up, |
---|
659 | delay_down, |
---|
660 | m, |
---|
661 | no_shutdown_script ? "on" : "off", |
---|
662 | initial_down ? "on" : "off", |
---|
663 | wait_on_fork ? "on" : "off", |
---|
664 | wait_on_kill ? "on" : "off", |
---|
665 | use_ifmonitor ? "on" : "off"); |
---|
666 | } |
---|
667 | |
---|
668 | void parse_args(int argc, char *argv[]) { |
---|
669 | static struct option long_options[] = { |
---|
670 | {"no-auto", no_argument, 0, 'a'}, |
---|
671 | {"no-daemon", no_argument, 0, 'n'}, |
---|
672 | {"no-syslog", no_argument, 0, 's'}, |
---|
673 | {"no-beep", no_argument, 0, 'b'}, |
---|
674 | {"ignore-fail", no_argument, 0, 'f'}, |
---|
675 | {"ignore-fail-positive", no_argument, 0, 'F'}, |
---|
676 | {"ignore-retval", no_argument, 0, 'I'}, |
---|
677 | {"iface", required_argument, 0, 'i'}, |
---|
678 | {"run", required_argument, 0, 'r'}, |
---|
679 | {"poll-time", required_argument, 0, 't'}, |
---|
680 | {"delay-up", required_argument, 0, 'u'}, |
---|
681 | {"delay-down", required_argument, 0, 'd'}, |
---|
682 | {"api-mode", required_argument, 0, 'm'}, |
---|
683 | {"wait-on-fork", no_argument, 0, 'w'}, |
---|
684 | {"wait-on-kill", no_argument, 0, 'W'}, |
---|
685 | {"no-shutdown", no_argument, 0, 'q'}, |
---|
686 | {"help", no_argument, 0, 'h'}, |
---|
687 | {"kill", no_argument, 0, 'k'}, |
---|
688 | {"check-running", no_argument, 0, 'c'}, |
---|
689 | {"version", no_argument, 0, 'v'}, |
---|
690 | {"extra-arg", required_argument, 0, 'x'}, |
---|
691 | {"suspend", no_argument, 0, 'S'}, |
---|
692 | {"resume", no_argument, 0, 'R'}, |
---|
693 | {"info", no_argument, 0, 'z'}, |
---|
694 | {"inital-down", no_argument, 0, 'l'}, |
---|
695 | {"monitor", no_argument, 0, 'M'}, |
---|
696 | {0, 0, 0, 0} |
---|
697 | }; |
---|
698 | int option_index = 0; |
---|
699 | int help = 0, _kill = 0, _check = 0, _version = 0, _suspend = 0, _resume = 0, _info = 0; |
---|
700 | |
---|
701 | for (;;) { |
---|
702 | int c; |
---|
703 | |
---|
704 | if ((c = getopt_long(argc, argv, "asni:r:t:u:d:hkbfFvm:qwx:cISRzlMW", long_options, &option_index)) < 0) |
---|
705 | break; |
---|
706 | |
---|
707 | switch (c) { |
---|
708 | case 'a' : |
---|
709 | interface_auto_up = !interface_auto_up; |
---|
710 | break; |
---|
711 | case 's' : |
---|
712 | use_syslog = !use_syslog; |
---|
713 | break; |
---|
714 | case 'n' : |
---|
715 | daemonize = !daemonize; |
---|
716 | break; |
---|
717 | case 'i' : |
---|
718 | if (interface) |
---|
719 | free(interface); |
---|
720 | interface = strdup(optarg); |
---|
721 | break; |
---|
722 | case 'r': |
---|
723 | run = strdup(optarg); |
---|
724 | break; |
---|
725 | case 'I': |
---|
726 | ignore_retval = !ignore_retval; |
---|
727 | break; |
---|
728 | case 't': |
---|
729 | polltime = atoi(optarg); |
---|
730 | if (polltime < 0) polltime = 0; |
---|
731 | break; |
---|
732 | case 'u': |
---|
733 | delay_up = atoi(optarg); |
---|
734 | break; |
---|
735 | case 'd': |
---|
736 | delay_down = atoi(optarg); |
---|
737 | break; |
---|
738 | case 'h': |
---|
739 | help = 1; |
---|
740 | break; |
---|
741 | case 'k': |
---|
742 | _kill = 1; |
---|
743 | break; |
---|
744 | case 'c': |
---|
745 | _check = 1; |
---|
746 | break; |
---|
747 | case 'v': |
---|
748 | _version = 1; |
---|
749 | break; |
---|
750 | case 'b': |
---|
751 | use_beep = !use_beep; |
---|
752 | break; |
---|
753 | case 'f': |
---|
754 | failure_status = IFSTATUS_DOWN; |
---|
755 | break; |
---|
756 | case 'F': |
---|
757 | failure_status = IFSTATUS_UP; |
---|
758 | break; |
---|
759 | case 'm': |
---|
760 | switch (tolower(optarg[0])) { |
---|
761 | case 'e': api_mode = API_ETHTOOL; break; |
---|
762 | case 'm': api_mode = API_MII; break; |
---|
763 | case 'p': api_mode = API_PRIVATE; break; |
---|
764 | case 'w': api_mode = API_WLAN; break; |
---|
765 | case 'a': api_mode = API_AUTO; break; |
---|
766 | default: |
---|
767 | daemon_log(LOG_ERR, "Unknown API mode: %s", optarg); |
---|
768 | exit(2); |
---|
769 | } |
---|
770 | break; |
---|
771 | case 'q': |
---|
772 | no_shutdown_script = !no_shutdown_script; |
---|
773 | break; |
---|
774 | case 'l': |
---|
775 | initial_down = !initial_down; |
---|
776 | break; |
---|
777 | case 'w': |
---|
778 | wait_on_fork = !wait_on_fork; |
---|
779 | break; |
---|
780 | case 'W': |
---|
781 | wait_on_kill = !wait_on_kill; |
---|
782 | break; |
---|
783 | case 'x': |
---|
784 | extra_arg = strdup(optarg); |
---|
785 | break; |
---|
786 | case 'S': |
---|
787 | _suspend = 1; |
---|
788 | break; |
---|
789 | case 'R': |
---|
790 | _resume = 1; |
---|
791 | break; |
---|
792 | case 'z': |
---|
793 | _info = 1; |
---|
794 | break; |
---|
795 | case 'M': |
---|
796 | use_ifmonitor = !use_ifmonitor; |
---|
797 | break; |
---|
798 | default: |
---|
799 | daemon_log(LOG_ERR, "Unknown parameter."); |
---|
800 | exit(1); |
---|
801 | } |
---|
802 | } |
---|
803 | |
---|
804 | |
---|
805 | if (!interface) |
---|
806 | interface = strdup("eth0"); |
---|
807 | if (!use_syslog) |
---|
808 | daemon_log_use = DAEMON_LOG_STDERR; |
---|
809 | |
---|
810 | |
---|
811 | if (help) { |
---|
812 | usage(argv[0]); |
---|
813 | exit(0); |
---|
814 | } |
---|
815 | |
---|
816 | if (_kill || _resume || _suspend || _info) { |
---|
817 | int rv; |
---|
818 | |
---|
819 | if (_kill && wait_on_kill) |
---|
820 | rv = daemon_pid_file_kill_wait(SIGINT, 5); |
---|
821 | else |
---|
822 | rv = daemon_pid_file_kill(_kill ? SIGINT : (_resume ? SIGUSR2 : (_info ? SIGHUP : SIGUSR1))); |
---|
823 | |
---|
824 | if (rv < 0) { |
---|
825 | daemon_log(LOG_ERR, "Failed to kill daemon. (%s)", strerror(errno)); |
---|
826 | exit(6); |
---|
827 | } |
---|
828 | |
---|
829 | exit(0); |
---|
830 | } |
---|
831 | |
---|
832 | if (_version) { |
---|
833 | |
---|
834 | #ifdef SVN_REVISION |
---|
835 | printf("ifplugd "VERSION" (SVN: "SVN_REVISION")\n"); |
---|
836 | #else |
---|
837 | printf("ifplugd "VERSION"\n"); |
---|
838 | #endif |
---|
839 | |
---|
840 | exit(0); |
---|
841 | } |
---|
842 | |
---|
843 | if (_check) { |
---|
844 | pid_t pid = daemon_pid_file_is_running(); |
---|
845 | |
---|
846 | if (pid == (pid_t) -1 || pid == 0) { |
---|
847 | printf("ifplugd not running.\n"); |
---|
848 | exit(255); |
---|
849 | } else { |
---|
850 | printf("ifplugd process for device %s running as pid %u.\n", interface, pid); |
---|
851 | exit(0); |
---|
852 | } |
---|
853 | } |
---|
854 | |
---|
855 | } |
---|
856 | |
---|
857 | static volatile int alarmed = 0; |
---|
858 | |
---|
859 | void sigalrm() { |
---|
860 | alarmed = 1; |
---|
861 | } |
---|
862 | |
---|
863 | int main(int argc, char* argv[]) { |
---|
864 | |
---|
865 | daemon_pid_file_proc = pid_file_proc; |
---|
866 | |
---|
867 | if ((daemon_log_ident = strrchr(argv[0], '/'))) |
---|
868 | daemon_log_ident++; |
---|
869 | else |
---|
870 | daemon_log_ident = argv[0]; |
---|
871 | |
---|
872 | parse_args(argc, argv); |
---|
873 | |
---|
874 | if (geteuid() != 0) { |
---|
875 | daemon_log(LOG_ERR, "Sorry, you need to be root to run this binary."); |
---|
876 | return 2; |
---|
877 | } |
---|
878 | |
---|
879 | if (daemon_pid_file_is_running() >= 0) { |
---|
880 | daemon_log(LOG_ERR, "Sorry, there is already an instance of ifplugd for %s running.", interface); |
---|
881 | return 4; |
---|
882 | } |
---|
883 | |
---|
884 | if (daemonize) { |
---|
885 | pid_t pid; |
---|
886 | |
---|
887 | if (wait_on_fork) |
---|
888 | if (daemon_retval_init() < 0) { |
---|
889 | daemon_log(LOG_ERR, "Sorry, could not create pipe: %s", strerror(errno)); |
---|
890 | return 4; |
---|
891 | } |
---|
892 | |
---|
893 | if ((pid = daemon_fork()) < 0) |
---|
894 | return 3; |
---|
895 | |
---|
896 | if (pid) { |
---|
897 | int c = 0; |
---|
898 | |
---|
899 | // Parent process |
---|
900 | |
---|
901 | if (wait_on_fork) |
---|
902 | if ((c = daemon_retval_wait(60)) < 0) { |
---|
903 | daemon_log(LOG_WARNING, "Killing background process."); |
---|
904 | kill(pid, SIGTERM); |
---|
905 | } |
---|
906 | |
---|
907 | if (c > 3) |
---|
908 | daemon_log(LOG_ERR, "Daemon failed with error condition #%i. See syslog for details", c); |
---|
909 | |
---|
910 | return c; |
---|
911 | } |
---|
912 | } |
---|
913 | |
---|
914 | // Let's go |
---|
915 | work(); |
---|
916 | return 0; |
---|
917 | } |
---|