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 <xm@subsignal.org>
21 /* Get BSSID of given interface */
22 static int iw_get_bssid(int iwfd, const char *ifname, char *bssid)
26 if( iw_ioctl(iwfd, ifname, SIOCGIWAP, &iwrq) >= 0 )
28 unsigned char *addr = (unsigned char *)iwrq.u.ap_addr.sa_data;
30 sprintf(bssid, "%02X:%02X:%02X:%02X:%02X:%02X",
31 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
39 /* Get channel of given interface */
40 static int iw_get_channel(int iwfd, const char *ifname, int *channel)
43 char buffer[sizeof(struct iw_range)];
44 double cur_freq, cmp_freq;
46 struct iw_range *range;
48 memset(buffer, 0, sizeof(buffer));
50 iwrq.u.data.pointer = (char *)buffer;
51 iwrq.u.data.length = sizeof(buffer);
52 iwrq.u.data.flags = 0;
54 if( iw_ioctl(iwfd, ifname, SIOCGIWRANGE, &iwrq) < 0)
60 range = (struct iw_range *)buffer;
62 if( iw_ioctl(iwfd, ifname, SIOCGIWFREQ, &iwrq) >= 0 )
64 cur_freq = ((double)iwrq.u.freq.m) * pow(10, iwrq.u.freq.e);
65 if( cur_freq < 1000.00 )
67 *channel = (int)cur_freq;
71 for(i = 0; i < range->num_frequency; i++)
73 cmp_freq = ((double)range->freq[i].m) * pow(10, range->freq[i].e);
74 if( cmp_freq == cur_freq )
76 *channel = (int)range->freq[i].i;
86 /* Get the (first) pid of given process name */
87 static int find_process(const char *name)
96 if( (dir = opendir("/proc")) != NULL )
98 snprintf(cmpname, sizeof(cmpname), "Name:\t%s\n", name);
100 while( (entry = readdir(dir)) != NULL )
102 if( !strcmp(entry->d_name, "..") || !isdigit(*entry->d_name) )
105 sprintf(buffer, "/proc/%s/status", entry->d_name);
106 if( (file = open(buffer, O_RDONLY)) > -1 )
108 read(file, buffer, sizeof(buffer));
111 if( strstr(buffer, cmpname) == buffer )
113 pid = atoi(entry->d_name);
115 /* Skip myself ... */
116 if( pid == getpid() )
128 syslog(LOG_CRIT, "Unable to open /proc: %s",
134 /* Get the 5 minute load average */
135 static double find_loadavg(void)
141 if( (fd = open("/proc/loadavg", O_RDONLY)) > -1 )
143 if( read(fd, buffer, sizeof(buffer)) == sizeof(buffer) )
144 load = atof(&buffer[5]);
152 /* Check if given uci file was updated */
153 static int check_uci_update(const char *config, time_t *mtime)
158 snprintf(path, sizeof(path), "/var/state/%s", config);
159 if( stat(path, &s) > -1 )
161 if( (*mtime == 0) || (s.st_mtime > *mtime) )
174 static void load_wifi_uci_add_iface(const char *section, struct uci_itr_ctx *itr)
180 if( (t = (wifi_tuple_t *)malloc(sizeof(wifi_tuple_t))) != NULL )
182 ucitmp = ucix_get_option(itr->ctx, "wireless", section, "ifname");
185 strncpy(t->ifname, ucitmp, sizeof(t->ifname));
189 ucitmp = ucix_get_option(itr->ctx, "wireless", section, "bssid");
192 strncpy(t->bssid, ucitmp, sizeof(t->bssid));
196 ucitmp = ucix_get_option(itr->ctx, "wireless", section, "device");
199 ucitmp = ucix_get_option(itr->ctx, "wireless", ucitmp, "channel");
202 t->channel = atoi(ucitmp);
209 syslog(LOG_INFO, "Monitoring %s: bssid=%s channel=%d",
210 t->ifname, t->bssid, t->channel);
223 static wifi_tuple_t * load_wifi_uci(wifi_tuple_t *ifs, time_t *modtime)
225 struct uci_context *ctx;
226 struct uci_itr_ctx itr;
227 wifi_tuple_t *cur, *next;
229 if( check_uci_update("wireless", modtime) )
231 syslog(LOG_INFO, "Config changed, reloading");
233 if( (ctx = ucix_init("wireless")) != NULL )
237 for(cur = ifs; cur; cur = next)
247 ucix_for_each_section_type(ctx, "wireless", "wifi-iface",
248 (void *)load_wifi_uci_add_iface, &itr);
257 /* Daemon implementation */
258 static int do_daemon(void)
266 wifi_tuple_t *ifs = NULL, *curif;
269 int restart_wifi = 0;
270 int restart_cron = 0;
271 int restart_sshd = 0;
272 int loadavg_panic = 0;
274 openlog(SYSLOG_IDENT, 0, LOG_DAEMON);
277 if( (iwfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 )
279 syslog(LOG_ERR, "Can not open wireless control socket: %s",
285 if( (wdfd = open(WATCH_DEVICE, O_WRONLY)) > -1 )
287 syslog(LOG_INFO, "Opened %s - polling each %i seconds",
288 WATCH_DEVICE, INTERVAL);
293 /* Check average load */
294 if( find_loadavg() >= LOAD_TRESHOLD )
300 if( find_process("crond") < 0 )
304 if( find_process("dropbear") < 0 )
307 /* Check wireless interfaces */
308 ifs = load_wifi_uci(ifs, &modtime);
309 for( curif = ifs; curif; curif = curif->next )
311 /* Get current channel and bssid */
312 if( (iw_get_bssid(iwfd, curif->ifname, bssid) == 0) &&
313 (iw_get_channel(iwfd, curif->ifname, &channel) == 0) )
316 if( strcasecmp(bssid, curif->bssid) != 0 )
318 syslog(LOG_WARNING, "BSSID mismatch on %s: current=%s wanted=%s",
319 curif->ifname, bssid, curif->bssid);
325 else if( channel != curif->channel )
327 syslog(LOG_WARNING, "Channel mismatch on %s: current=%d wanted=%d",
328 curif->ifname, channel, curif->channel);
335 syslog(LOG_WARNING, "Requested interface %s not present", curif->ifname);
340 /* Wifi restart required? */
341 if( restart_wifi >= HYSTERESIS )
344 syslog(LOG_WARNING, "Channel or BSSID mismatch on wireless interface, restarting");
348 /* Cron restart required? */
349 if( restart_cron >= HYSTERESIS )
352 syslog(LOG_WARNING, "The cron process died, restarting");
356 /* SSHd restart required? */
357 if( restart_sshd >= HYSTERESIS )
360 syslog(LOG_WARNING, "The ssh process died, restarting");
364 /* Is there a load problem? */
365 if( loadavg_panic >= HYSTERESIS )
367 syslog(LOG_EMERG, "Critical system load level, triggering reset!");
369 /* Try watchdog, fall back to reboot */
371 ioctl(wdfd, WDIOC_SETTIMEOUT, &wdtrigger);
376 /* Reset watchdog timer */
378 write(wdfd, '\0', 1);
385 syslog(LOG_INFO, "Stopping watchdog timer");
386 write(wdfd, WATCH_SHUTDOWN, 1);
395 int main(int argc, char *argv[])
397 /* Check if watchdog is running ... */
398 if( (argc > 1) && (strcmp(argv[1], "running") == 0) )
400 return (find_process(BINARY) == -1);