root/luci/branches/luci-0.8/contrib/package/freifunk-watchdog/src/watchdog.c @ 4511

Revision 4511, 9.2 KB (checked in by jow, 4 years ago)

luci-0.8: merge r4510

Line 
1/*
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.
6 *
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.
11 *
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.
15 *
16 *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
17 */
18
19#include "watchdog.h"
20
21/* Global watchdog fd, required by signal handler */
22int wdfd = -1;
23
24/* Watchdog shutdown helper */
25static void shutdown_watchdog(int sig)
26{
27    static int wdelay = 3600;
28    static const char wshutdown = WATCH_SHUTDOWN;
29
30    if( wdfd > -1 )
31    {
32        syslog(LOG_INFO, "Stopping watchdog timer");
33        write(wdfd, &wshutdown, 1);
34
35        /* Older Kamikaze versions are compiled with
36         * CONFIG_WATCHDOG_NOWAYOUT=y, this can be
37         * harmful if we're in the middle of an upgrade.
38         * Increase the watchdog timeout to 3600 seconds
39         * here to avoid unplanned reboots. */
40        ioctl(wdfd, WDIOC_SETTIMEOUT, &wdelay);
41
42        close(wdfd);
43        wdfd = -1;
44    }
45
46    exit(0);
47}
48
49/* Get BSSID of given interface */
50static int iw_get_bssid(int iwfd, const char *ifname, char *bssid)
51{
52    struct iwreq iwrq;
53
54    if( iw_ioctl(iwfd, ifname, SIOCGIWAP, &iwrq) >= 0 )
55    {
56        unsigned char *addr = (unsigned char *)iwrq.u.ap_addr.sa_data;
57
58        sprintf(bssid, "%02X:%02X:%02X:%02X:%02X:%02X",
59            addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
60
61        return 0;
62    }
63
64    return -1;
65}
66
67/* Get channel of given interface */
68static int iw_get_channel(int iwfd, const char *ifname, int *channel)
69{
70    int i;
71    char buffer[sizeof(struct iw_range)];
72    double cur_freq, cmp_freq;
73    struct iwreq iwrq;
74    struct iw_range *range;
75
76    memset(buffer, 0, sizeof(buffer));
77
78    iwrq.u.data.pointer = (char *)buffer;
79    iwrq.u.data.length = sizeof(buffer);
80    iwrq.u.data.flags = 0;
81
82    if( iw_ioctl(iwfd, ifname, SIOCGIWRANGE, &iwrq) < 0)
83    {
84        *channel = -1;
85        return -1;
86    }
87
88    range = (struct iw_range *)buffer;
89
90    if( iw_ioctl(iwfd, ifname, SIOCGIWFREQ, &iwrq) >= 0 )
91    {
92        cur_freq = ((double)iwrq.u.freq.m) * pow(10, iwrq.u.freq.e);
93        if( cur_freq < 1000.00 )
94        {
95            *channel = (int)cur_freq;
96            return 0;
97        }
98
99        for(i = 0; i < range->num_frequency; i++)
100        {
101            cmp_freq = ((double)range->freq[i].m) * pow(10, range->freq[i].e);
102            if( cmp_freq == cur_freq )
103            {
104                *channel = (int)range->freq[i].i;
105                return 0;
106            }
107        }
108    }
109
110    *channel = -1;
111    return -1;
112}
113
114/* Get the (first) pid of given process name */
115static int find_process(const char *name)
116{
117    int pid = -1;
118    int file;
119    char buffer[128];
120    char cmpname[128];
121    DIR *dir;
122    struct dirent *entry;
123
124    if( (dir = opendir("/proc")) != NULL )
125    {
126        snprintf(cmpname, sizeof(cmpname), "Name:\t%s\n", name);
127
128        while( (entry = readdir(dir)) != NULL )
129        {
130            if( !strcmp(entry->d_name, "..") || !isdigit(*entry->d_name) )
131                continue;
132
133            sprintf(buffer, "/proc/%s/status", entry->d_name);
134            if( (file = open(buffer, O_RDONLY)) > -1 )
135            {
136                read(file, buffer, sizeof(buffer));
137                close(file);
138
139                if( strstr(buffer, cmpname) == buffer )
140                {
141                    pid = atoi(entry->d_name);
142
143                    /* Skip myself ... */
144                    if( pid == getpid() )
145                        pid = -1;
146                    else
147                        break;
148                }
149            }
150        }
151
152        closedir(dir);
153        return pid;
154    }
155
156    syslog(LOG_CRIT, "Unable to open /proc: %s",
157        strerror(errno));
158
159    return -1;
160}
161
162/* Get the 5 minute load average */
163static double find_loadavg(void)
164{
165    int fd;
166    char buffer[10];
167    double load = 0.00;
168
169    if( (fd = open("/proc/loadavg", O_RDONLY)) > -1 )
170    {
171        if( read(fd, buffer, sizeof(buffer)) == sizeof(buffer) )
172            load = atof(&buffer[5]);
173
174        close(fd);
175    }
176
177    return load;
178}
179
180/* Check if given uci file was updated */
181static int check_uci_update(const char *config, time_t *mtime)
182{
183    struct stat s;
184    char path[128];
185
186    snprintf(path, sizeof(path), "/var/state/%s", config);
187    if( stat(path, &s) > -1 )
188    {
189        if( (*mtime == 0) || (s.st_mtime > *mtime) )
190        {
191            *mtime = s.st_mtime;
192            return 1;
193        }
194
195        return 0;
196    }
197
198    return -1;
199}
200
201/* Add tuple */
202static void load_wifi_uci_add_iface(const char *section, struct uci_itr_ctx *itr)
203{
204    wifi_tuple_t *t;
205    const char *ucitmp;
206    int val = 0;
207
208    if( (t = (wifi_tuple_t *)malloc(sizeof(wifi_tuple_t))) != NULL )
209    {
210        ucitmp = ucix_get_option(itr->ctx, "wireless", section, "ifname");
211        if(ucitmp)
212        {
213            strncpy(t->ifname, ucitmp, sizeof(t->ifname));
214            val++;
215        }
216
217        ucitmp = ucix_get_option(itr->ctx, "wireless", section, "bssid");
218        if(ucitmp)
219        {
220            strncpy(t->bssid, ucitmp, sizeof(t->bssid));
221            val++;
222        }
223
224        ucitmp = ucix_get_option(itr->ctx, "wireless", section, "device");
225        if(ucitmp)
226        {
227            ucitmp = ucix_get_option(itr->ctx, "wireless", ucitmp, "channel");
228            if(ucitmp)
229            {
230                t->channel = atoi(ucitmp);
231                val++;
232            }
233        }
234
235        if( val == 3 )
236        {
237            syslog(LOG_INFO, "Monitoring %s: bssid=%s channel=%d",
238                t->ifname, t->bssid, t->channel);
239
240            t->next = itr->list;
241            itr->list = t;
242        }
243        else
244        {
245            free(t);
246        }
247    }
248}
249
250/* Load config */
251static wifi_tuple_t * load_wifi_uci(wifi_tuple_t *ifs, time_t *modtime)
252{
253    struct uci_context *ctx;
254    struct uci_itr_ctx itr;
255    wifi_tuple_t *cur, *next;
256
257    if( check_uci_update("wireless", modtime) )
258    {
259        syslog(LOG_INFO, "Config changed, reloading");
260
261        if( (ctx = ucix_init("wireless")) != NULL )
262        {
263            if( ifs != NULL )
264            {
265                for(cur = ifs; cur; cur = next)
266                {
267                    next = cur->next;
268                    free(cur);
269                }
270            }
271
272            itr.list = NULL;
273            itr.ctx = ctx;
274
275            ucix_for_each_section_type(ctx, "wireless", "wifi-iface",
276                (void *)load_wifi_uci_add_iface, &itr);
277
278            return itr.list;
279        }
280    }
281
282    return ifs;
283}
284
285/* Daemon implementation */
286static int do_daemon(void)
287{
288    static int wdtrigger = 1;
289    static int wdtimeout = INTERVAL * 2;
290    static const char wdkeepalive = WATCH_KEEPALIVE;
291
292    int iwfd;
293    int channel;
294    char bssid[18];
295    struct sigaction sa;
296
297    wifi_tuple_t *ifs = NULL, *curif;
298    time_t modtime = 0;
299
300    int restart_wifi = 0;
301    int restart_cron = 0;
302    int restart_sshd = 0;
303    int loadavg_panic = 0;
304
305    openlog(SYSLOG_IDENT, 0, LOG_DAEMON);
306    //daemon(1, 1);
307
308    if( (iwfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 )
309    {
310        syslog(LOG_ERR, "Can not open wireless control socket: %s",
311            strerror(errno));
312
313        return 1;
314    }
315
316    if( (wdfd = open(WATCH_DEVICE, O_WRONLY)) > -1 )
317    {
318        syslog(LOG_INFO, "Opened %s - polling every %i seconds",
319            WATCH_DEVICE, INTERVAL);
320
321        /* Install signal handler to halt watchdog on shutdown */
322        sa.sa_handler = shutdown_watchdog;
323        sa.sa_flags = SA_NOCLDWAIT | SA_RESTART;
324        sigaction(SIGHUP,  &sa, NULL);
325        sigaction(SIGINT,  &sa, NULL);
326        sigaction(SIGPIPE, &sa, NULL);
327        sigaction(SIGTERM, &sa, NULL);
328        sigaction(SIGUSR1, &sa, NULL);
329        sigaction(SIGUSR2, &sa, NULL);
330
331        /* Set watchdog timeout to twice the interval */
332        ioctl(wdfd, WDIOC_SETTIMEOUT, &wdtimeout);
333    }
334
335    while( 1 )
336    {
337        /* Check average load */
338        if( find_loadavg() >= LOAD_TRESHOLD )
339            loadavg_panic++;
340        else
341            loadavg_panic = 0;
342
343        /* Check crond */
344        if( find_process("crond") < 0 )
345            restart_cron++;
346
347        /* Check SSHd */
348        if( find_process("dropbear") < 0 )
349            restart_sshd++;
350
351        /* Check wireless interfaces */
352        ifs = load_wifi_uci(ifs, &modtime);
353        for( curif = ifs; curif; curif = curif->next )
354        {
355            /* Get current channel and bssid */
356            if( (iw_get_bssid(iwfd, curif->ifname, bssid) == 0) &&
357                (iw_get_channel(iwfd, curif->ifname, &channel) == 0) )
358            {
359                /* Check BSSID */
360                if( strcasecmp(bssid, curif->bssid) != 0 )
361                {
362                    syslog(LOG_WARNING, "BSSID mismatch on %s: current=%s wanted=%s",
363                        curif->ifname, bssid, curif->bssid);
364
365                    restart_wifi++;
366                }
367
368                /* Check channel */
369                else if( channel != curif->channel )
370                {
371                    syslog(LOG_WARNING, "Channel mismatch on %s: current=%d wanted=%d",
372                        curif->ifname, channel, curif->channel);
373
374                    restart_wifi++;
375                }
376            }
377            else
378            {
379                syslog(LOG_WARNING, "Requested interface %s not present", curif->ifname);
380            }
381        }
382
383
384        /* Wifi restart required? */
385        if( restart_wifi >= HYSTERESIS )
386        {
387            restart_wifi = 0;
388            syslog(LOG_WARNING, "Channel or BSSID mismatch on wireless interface, restarting");
389            EXEC(WIFI_ACTION);
390        }
391
392        /* Cron restart required? */
393        if( restart_cron >= HYSTERESIS )
394        {
395            restart_cron = 0;
396            syslog(LOG_WARNING, "The cron process died, restarting");
397            EXEC(CRON_ACTION);
398        }
399
400        /* SSHd restart required? */
401        if( restart_sshd >= HYSTERESIS )
402        {
403            restart_sshd = 0;
404            syslog(LOG_WARNING, "The ssh process died, restarting");
405            EXEC(SSHD_ACTION);
406        }
407
408        /* Is there a load problem? */
409        if( loadavg_panic >= HYSTERESIS )
410        {
411            syslog(LOG_EMERG, "Critical system load level, triggering reset!");
412
413            /* Try watchdog, fall back to reboot */
414            if( wdfd > -1 )
415                ioctl(wdfd, WDIOC_SETTIMEOUT, &wdtrigger);
416            else
417                EXEC(LOAD_ACTION);
418        }
419
420        /* Reset watchdog timer */
421        if( wdfd > -1 )
422            write(wdfd, &wdkeepalive, 1);
423
424        sleep(INTERVAL);
425    }
426
427    shutdown_watchdog(0);
428    closelog();
429
430    return 0;
431}
432
433
434int main(int argc, char *argv[])
435{
436    /* Check if watchdog is running ... */
437    if( (argc > 1) && (strcmp(argv[1], "running") == 0) )
438    {
439        return (find_process(BINARY) == -1);
440    }
441
442    /* Start daemon */
443    return do_daemon();
444}
Note: See TracBrowser for help on using the browser.