diff options
Diffstat (limited to 'contrib/package/freifunk-watchdog/src/watchdog.c')
-rw-r--r-- | contrib/package/freifunk-watchdog/src/watchdog.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/contrib/package/freifunk-watchdog/src/watchdog.c b/contrib/package/freifunk-watchdog/src/watchdog.c new file mode 100644 index 0000000000..aea0516811 --- /dev/null +++ b/contrib/package/freifunk-watchdog/src/watchdog.c @@ -0,0 +1,347 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org> + */ + +#include "watchdog.h" + +/* Get BSSID of given interface */ +static int iw_get_bssid(int iwfd, const char *ifname, char *bssid) +{ + struct iwreq iwrq; + + if( iw_ioctl(iwfd, ifname, SIOCGIWAP, &iwrq) >= 0 ) + { + unsigned char *addr = (unsigned char *)iwrq.u.ap_addr.sa_data; + + sprintf(bssid, "%02X:%02X:%02X:%02X:%02X:%02X", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + return 0; + } + + return -1; +} + +/* Get channel of given interface */ +static int iw_get_channel(int iwfd, const char *ifname, int *channel) +{ + int i; + char buffer[sizeof(struct iw_range)]; + double cur_freq, cmp_freq; + struct iwreq iwrq; + struct iw_range *range; + + memset(buffer, 0, sizeof(buffer)); + + iwrq.u.data.pointer = (char *)buffer; + iwrq.u.data.length = sizeof(buffer); + iwrq.u.data.flags = 0; + + if( iw_ioctl(iwfd, ifname, SIOCGIWRANGE, &iwrq) < 0) + { + *channel = -1; + return -1; + } + + range = (struct iw_range *)buffer; + + if( iw_ioctl(iwfd, ifname, SIOCGIWFREQ, &iwrq) >= 0 ) + { + cur_freq = ((double)iwrq.u.freq.m) * pow(10, iwrq.u.freq.e); + if( cur_freq < 1000.00 ) + { + *channel = (int)cur_freq; + return 0; + } + + for(i = 0; i < range->num_frequency; i++) + { + cmp_freq = ((double)range->freq[i].m) * pow(10, range->freq[i].e); + if( cmp_freq == cur_freq ) + { + *channel = (int)range->freq[i].i; + return 0; + } + } + } + + *channel = -1; + return -1; +} + +/* Get the (first) pid of given process name */ +static int find_process(const char *name) +{ + int pid = -1; + int file; + char buffer[128]; + char cmpname[128]; + DIR *dir; + struct dirent *entry; + + if( (dir = opendir("/proc")) != NULL ) + { + snprintf(cmpname, sizeof(cmpname), "Name:\t%s\n", name); + + while( (entry = readdir(dir)) != NULL ) + { + if( !strcmp(entry->d_name, "..") || !isdigit(*entry->d_name) ) + continue; + + sprintf(buffer, "/proc/%s/status", entry->d_name); + if( (file = open(buffer, O_RDONLY)) > -1 ) + { + read(file, buffer, sizeof(buffer)); + close(file); + + if( strstr(buffer, cmpname) == buffer ) + { + pid = atoi(entry->d_name); + break; + } + } + } + + closedir(dir); + return pid; + } + + syslog(LOG_CRIT, "Unable to open /proc: %s", + strerror(errno)); + + return -1; +} + +/* Check if given uci file was updated */ +static int check_uci_update(const char *config, time_t *mtime) +{ + struct stat s; + char path[128]; + + snprintf(path, sizeof(path), "/etc/config/%s", config); + if( stat(path, &s) > -1 ) + { + if( (*mtime == 0) || (s.st_mtime > *mtime) ) + { + *mtime = s.st_mtime; + return 1; + } + else + { + snprintf(path, sizeof(path), "/var/state/%s", config); + if( stat(path, &s) > -1 ) + { + if( (*mtime == 0) || (s.st_mtime > *mtime) ) + { + *mtime = s.st_mtime; + return 1; + } + } + + return 0; + } + } + + return -1; +} + +/* Add tuple */ +static void load_wifi_uci_add_iface(const char *section, struct uci_itr_ctx *itr) +{ + wifi_tuple_t *t; + const char *ucitmp; + int val = 0; + + if( (t = (wifi_tuple_t *)malloc(sizeof(wifi_tuple_t))) != NULL ) + { + ucitmp = ucix_get_option(itr->ctx, "wireless", section, "ifname"); + if(ucitmp) + { + strncpy(t->ifname, ucitmp, sizeof(t->ifname)); + val++; + } + + ucitmp = ucix_get_option(itr->ctx, "wireless", section, "bssid"); + if(ucitmp) + { + strncpy(t->bssid, ucitmp, sizeof(t->bssid)); + val++; + } + + ucitmp = ucix_get_option(itr->ctx, "wireless", section, "device"); + if(ucitmp) + { + ucitmp = ucix_get_option(itr->ctx, "wireless", ucitmp, "channel"); + if(ucitmp) + { + t->channel = atoi(ucitmp); + val++; + } + } + + if( val == 3 ) + { + syslog(LOG_INFO, "Monitoring %s: bssid=%s channel=%d", + t->ifname, t->bssid, t->channel); + + t->next = itr->list; + itr->list = t; + } + else + { + free(t); + } + } +} + +/* Load config */ +static wifi_tuple_t * load_wifi_uci(wifi_tuple_t *ifs, time_t *modtime) +{ + struct uci_context *ctx; + struct uci_itr_ctx itr; + wifi_tuple_t *cur, *next; + + if( check_uci_update("wireless", modtime) ) + { + syslog(LOG_INFO, "Config changed, reloading"); + + if( (ctx = ucix_init("wireless")) != NULL ) + { + if( ifs != NULL ) + { + for(cur = ifs; cur; cur = next) + { + next = cur->next; + free(cur); + } + } + + itr.list = NULL; + itr.ctx = ctx; + + ucix_for_each_section_type(ctx, "wireless", "wifi-iface", + (void *)load_wifi_uci_add_iface, &itr); + + return itr.list; + } + } + + return ifs; +} + +/* Daemon implementation */ +static int do_daemon(void) +{ + int iwfd; + int channel; + char bssid[18]; + + wifi_tuple_t *ifs = NULL, *curif; + time_t modtime = 0; + + int restart_wifi = 0; + int restart_cron = 0; + + openlog(SYSLOG_IDENT, 0, LOG_DAEMON); + //daemon(1, 1); + + if( (iwfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ) + { + perror("Can not open wireless control socket"); + return 1; + } + + while( 1 ) + { + if( (ifs = load_wifi_uci(ifs, &modtime)) == NULL ) + { + printf("Can not load wireless uci. File corrupt?\n"); + return 1; + } + + /* Check crond */ + if( find_process("crond") < 0 ) + { + syslog(LOG_WARNING, "The crond process died, restarting"); + restart_cron++; + } + + /* Check wireless interfaces */ + for( curif = ifs; curif; curif = curif->next ) + { + /* Get current channel and bssid */ + if( (iw_get_bssid(iwfd, curif->ifname, bssid) == 0) && + (iw_get_channel(iwfd, curif->ifname, &channel) == 0) ) + { + /* Check BSSID */ + if( strcasecmp(bssid, curif->bssid) != 0 ) + { + syslog(LOG_WARNING, "BSSID mismatch on %s: current=%s wanted=%s", + curif->ifname, bssid, curif->bssid); + + restart_wifi++; + } + + /* Check channel */ + if( channel != curif->channel ) + { + syslog(LOG_WARNING, "Channel mismatch on %s: current=%d wanted=%d", + curif->ifname, channel, curif->channel); + + restart_wifi++; + } + } + else + { + syslog(LOG_WARNING, "Requested interface %s not present", curif->ifname); + } + } + + + /* Wifi restart required? */ + if( restart_wifi > 0 ) + { + restart_wifi = 0; + EXEC(WIFI_ACTION); + } + + /* Cron restart required? */ + if( restart_cron > 0 ) + { + restart_cron = 0; + EXEC(CRON_ACTION); + } + + sleep(INTERVAL); + } + + closelog(); + return 0; +} + + +int main(int argc, char *argv[]) +{ + /* Check if watchdog is running ... */ + if( (argc > 1) && (strcmp(argv[1], "running") == 0) ) + { + return (find_process(BINARY) >= 0); + } + + /* Start daemon */ + return do_daemon(); +} |