2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
16 * Copyright (C) 2009 Jo-Philipp Wich <jow@openwrt.org>
21 /* Global watchdog fd, required by signal handler */
24 /* Handle finished childs */
25 static void sigchld_handler(int sig
)
29 while( (pid
= waitpid(-1, NULL
, WNOHANG
)) > 0 )
30 syslog(LOG_INFO
, "Child returned (pid %d)", pid
);
33 /* Watchdog shutdown helper */
34 static void shutdown_watchdog(int sig
)
36 static const char wshutdown
= WATCH_SHUTDOWN
;
40 syslog(LOG_INFO
, "Stopping watchdog timer");
41 write(wdfd
, &wshutdown
, 1);
49 /* Get BSSID of given interface */
50 static int iw_get_bssid(int iwfd
, const char *ifname
, char *bssid
)
54 if( iw_ioctl(iwfd
, ifname
, SIOCGIWAP
, &iwrq
) >= 0 )
56 unsigned char *addr
= (unsigned char *)iwrq
.u
.ap_addr
.sa_data
;
58 sprintf(bssid
, "%02X:%02X:%02X:%02X:%02X:%02X",
59 addr
[0], addr
[1], addr
[2], addr
[3], addr
[4], addr
[5]);
67 /* Get channel of given interface */
68 static int iw_get_channel(int iwfd
, const char *ifname
, int *channel
)
71 char buffer
[sizeof(struct iw_range
)];
72 double cur_freq
, cmp_freq
;
74 struct iw_range
*range
;
76 memset(buffer
, 0, sizeof(buffer
));
78 iwrq
.u
.data
.pointer
= (char *)buffer
;
79 iwrq
.u
.data
.length
= sizeof(buffer
);
80 iwrq
.u
.data
.flags
= 0;
82 if( iw_ioctl(iwfd
, ifname
, SIOCGIWRANGE
, &iwrq
) < 0)
88 range
= (struct iw_range
*)buffer
;
90 if( iw_ioctl(iwfd
, ifname
, SIOCGIWFREQ
, &iwrq
) >= 0 )
92 cur_freq
= ((double)iwrq
.u
.freq
.m
) * pow(10, iwrq
.u
.freq
.e
);
93 if( cur_freq
< 1000.00 )
95 *channel
= (int)cur_freq
;
99 for(i
= 0; i
< range
->num_frequency
; i
++)
101 cmp_freq
= ((double)range
->freq
[i
].m
) * pow(10, range
->freq
[i
].e
);
102 if( cmp_freq
== cur_freq
)
104 *channel
= (int)range
->freq
[i
].i
;
114 /* Get the (first) pid of given process name */
115 static int find_process(const char *name
)
122 struct dirent
*entry
;
124 if( (dir
= opendir("/proc")) != NULL
)
126 snprintf(cmpname
, sizeof(cmpname
), "Name:\t%s\n", name
);
128 while( (entry
= readdir(dir
)) != NULL
)
130 if( !strcmp(entry
->d_name
, "..") || !isdigit(*entry
->d_name
) )
133 sprintf(buffer
, "/proc/%s/status", entry
->d_name
);
134 if( (file
= open(buffer
, O_RDONLY
)) > -1 )
136 read(file
, buffer
, sizeof(buffer
));
139 if( strstr(buffer
, cmpname
) == buffer
)
141 pid
= atoi(entry
->d_name
);
143 /* Skip myself ... */
144 if( pid
== getpid() )
156 syslog(LOG_CRIT
, "Unable to open /proc: %s",
162 /* Get the 5 minute load average */
163 static double find_loadavg(void)
169 if( (fd
= open("/proc/loadavg", O_RDONLY
)) > -1 )
171 if( read(fd
, buffer
, sizeof(buffer
)) == sizeof(buffer
) )
172 load
= atof(&buffer
[5]);
180 /* Check if given uci file was updated */
181 static int check_uci_update(const char *config
, time_t *mtime
)
186 snprintf(path
, sizeof(path
), "/var/state/%s", config
);
187 if( stat(path
, &s
) > -1 )
189 if( (*mtime
== 0) || (s
.st_mtime
> *mtime
) )
200 static void load_wifi_uci_add_iface(const char *section
, struct uci_wifi_iface_itr_ctx
*itr
)
206 ucitmp
= ucix_get_option(itr
->ctx
, "wireless", section
, "mode");
207 if( ucitmp
&& !strncmp(ucitmp
, "adhoc", 5) )
209 if( (t
= (wifi_tuple_t
*)malloc(sizeof(wifi_tuple_t
))) != NULL
)
211 ucitmp
= ucix_get_option(itr
->ctx
, "wireless", section
, "ifname");
214 strncpy(t
->ifname
, ucitmp
, sizeof(t
->ifname
));
218 ucitmp
= ucix_get_option(itr
->ctx
, "wireless", section
, "bssid");
221 strncpy(t
->bssid
, ucitmp
, sizeof(t
->bssid
));
225 ucitmp
= ucix_get_option(itr
->ctx
, "wireless", section
, "device");
228 ucitmp
= ucix_get_option(itr
->ctx
, "wireless", ucitmp
, "channel");
231 t
->channel
= atoi(ucitmp
);
238 syslog(LOG_INFO
, "Monitoring %s: bssid=%s channel=%d",
239 t
->ifname
, t
->bssid
, t
->channel
);
253 static wifi_tuple_t
* load_wifi_uci(wifi_tuple_t
*ifs
, time_t *modtime
)
255 struct uci_context
*ctx
;
256 struct uci_wifi_iface_itr_ctx itr
;
257 wifi_tuple_t
*cur
, *next
;
259 if( check_uci_update("wireless", modtime
) )
261 syslog(LOG_INFO
, "Wireless config changed, reloading");
263 if( (ctx
= ucix_init("wireless")) != NULL
)
267 for(cur
= ifs
; cur
; cur
= next
)
277 ucix_for_each_section_type(ctx
, "wireless", "wifi-iface",
278 (void *)load_wifi_uci_add_iface
, &itr
);
288 static void load_watchdog_uci_add_process(const char *section
, struct uci_process_itr_ctx
*itr
)
294 if( (t
= (process_tuple_t
*)malloc(sizeof(process_tuple_t
))) != NULL
)
298 ucitmp
= ucix_get_option(itr
->ctx
, "freifunk-watchdog", section
, "process");
301 strncpy(t
->process
, ucitmp
, sizeof(t
->process
));
305 ucitmp
= ucix_get_option(itr
->ctx
, "freifunk-watchdog", section
, "initscript");
308 strncpy(t
->initscript
, ucitmp
, sizeof(t
->initscript
));
314 syslog(LOG_INFO
, "Monitoring %s: initscript=%s",
315 t
->process
, t
->initscript
);
328 static process_tuple_t
* load_watchdog_uci(process_tuple_t
*procs
)
330 struct uci_context
*ctx
;
331 struct uci_process_itr_ctx itr
;
332 process_tuple_t
*cur
, *next
;
334 syslog(LOG_INFO
, "Loading watchdog config");
336 if( (ctx
= ucix_init("freifunk-watchdog")) != NULL
)
340 for(cur
= procs
; cur
; cur
= next
)
350 ucix_for_each_section_type(ctx
, "freifunk-watchdog", "process",
351 (void *)load_watchdog_uci_add_process
, &itr
);
359 /* Daemon implementation */
360 static int do_daemon(void)
362 static int wdtrigger
= 1;
363 static int wdtimeout
= BASE_INTERVAL
* 2;
364 static const char wdkeepalive
= WATCH_KEEPALIVE
;
371 wifi_tuple_t
*ifs
= NULL
, *curr_if
;
372 process_tuple_t
*procs
= NULL
, *curr_proc
;
373 time_t wireless_modtime
= 0;
376 int restart_wifi
= 0;
377 int loadavg_panic
= 0;
379 openlog(SYSLOG_IDENT
, 0, LOG_DAEMON
);
380 memset(&sa
, 0, sizeof(sa
));
382 if( (iwfd
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1 )
384 syslog(LOG_ERR
, "Can not open wireless control socket: %s",
390 if( (wdfd
= open(WATCH_DEVICE
, O_WRONLY
)) > -1 )
392 syslog(LOG_INFO
, "Opened %s - polling every %i seconds",
393 WATCH_DEVICE
, BASE_INTERVAL
);
395 /* Install signal handler to halt watchdog on shutdown */
396 sa
.sa_handler
= shutdown_watchdog
;
397 sa
.sa_flags
= SA_NOCLDWAIT
| SA_RESTART
;
398 sigaction(SIGHUP
, &sa
, NULL
);
399 sigaction(SIGINT
, &sa
, NULL
);
400 sigaction(SIGPIPE
, &sa
, NULL
);
401 sigaction(SIGTERM
, &sa
, NULL
);
402 sigaction(SIGUSR1
, &sa
, NULL
);
403 sigaction(SIGUSR2
, &sa
, NULL
);
405 /* Set watchdog timeout to twice the interval */
406 ioctl(wdfd
, WDIOC_SETTIMEOUT
, &wdtimeout
);
409 /* Install signal handler to reap childs */
410 sa
.sa_handler
= sigchld_handler
;
412 sigaction(SIGCHLD
, &sa
, NULL
);
414 /* Load watchdog configuration only once */
415 procs
= load_watchdog_uci(procs
);
419 /* Check/increment action interval */
420 if( ++action_intv
>= ACTION_INTERVAL
)
422 /* Reset action interval */
425 /* Check average load */
426 if( find_loadavg() >= LOAD_TRESHOLD
)
431 /* Check wireless interfaces */
432 ifs
= load_wifi_uci(ifs
, &wireless_modtime
);
433 for( curr_if
= ifs
; curr_if
; curr_if
= curr_if
->next
)
435 /* Get current channel and bssid */
436 if( (iw_get_bssid(iwfd
, curr_if
->ifname
, bssid
) == 0) &&
437 (iw_get_channel(iwfd
, curr_if
->ifname
, &channel
) == 0) )
440 if( strcasecmp(bssid
, curr_if
->bssid
) != 0 )
442 syslog(LOG_WARNING
, "BSSID mismatch on %s: current=%s wanted=%s",
443 curr_if
->ifname
, bssid
, curr_if
->bssid
);
449 else if( channel
!= curr_if
->channel
)
451 syslog(LOG_WARNING
, "Channel mismatch on %s: current=%d wanted=%d",
452 curr_if
->ifname
, channel
, curr_if
->channel
);
459 syslog(LOG_WARNING
, "Requested interface %s not present", curr_if
->ifname
);
463 /* Check processes */
464 for( curr_proc
= procs
; curr_proc
; curr_proc
= curr_proc
->next
)
466 if( find_process(curr_proc
->process
) < 0 )
467 curr_proc
->restart
++;
469 curr_proc
->restart
= 0;
471 /* Process restart required? */
472 if( curr_proc
->restart
>= HYSTERESIS
)
474 curr_proc
->restart
= 0;
475 syslog(LOG_WARNING
, "The %s process died, restarting", curr_proc
->process
);
481 /* Wifi restart required? */
482 if( restart_wifi
>= HYSTERESIS
)
485 syslog(LOG_WARNING
, "Channel or BSSID mismatch on wireless interface, restarting");
489 /* Is there a load problem? */
490 if( loadavg_panic
>= HYSTERESIS
)
492 syslog(LOG_EMERG
, "Critical system load level, triggering reset!");
494 /* Try watchdog, fall back to reboot */
496 ioctl(wdfd
, WDIOC_SETTIMEOUT
, &wdtrigger
);
503 /* Reset watchdog timer */
505 write(wdfd
, &wdkeepalive
, 1);
507 sleep(BASE_INTERVAL
);
510 shutdown_watchdog(0);
517 int main(int argc
, char *argv
[])
519 /* Check if watchdog is running ... */
520 if( (argc
> 1) && (strcmp(argv
[1], "running") == 0) )
522 return (find_process(BINARY
) == -1);