From 3e81b0123b4bbfedbdc1135a6a4305c347f91a3a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 9 Aug 2003 16:41:59 +0000 Subject: Initial commit git-svn-id: file:///home/lennart/svn/public/aeswepd/trunk@3 022f378f-78c4-0310-b860-d162c87e6274 --- src/waproamd.c | 667 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 667 insertions(+) create mode 100644 src/waproamd.c (limited to 'src/waproamd.c') diff --git a/src/waproamd.c b/src/waproamd.c new file mode 100644 index 0000000..c2af3af --- /dev/null +++ b/src/waproamd.c @@ -0,0 +1,667 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "iwapi.h" +#include "interface.h" +#include "exec.h" +#include "nlapi.h" +#include "ifmonitor.h" +#include "assocwatch.h" + +#ifndef SYSCONFDIR +#define SYSCONFDIR "/etc/waproamd" +#endif + +#ifndef SCRIPTDIR +#define SCRIPTDIR SYSCONFDIR"/scripts" +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +char *interface_name = NULL; +int interface_index = -1; + +int disabled = 0, + associated = 0; + +struct hw_addr associated_ap; +struct hw_addr current_ap; + +int use_assocwatch = 1, + use_ifmonitor = 0, + daemonize = 1, + wait_on_fork = 0, + use_syslog = 1; + +int poll_interval = 5, + scan_interval = 10; + +char log_ident[32], pid_ident[32]; + +int issue_scan(struct interface *i) { + + //fprintf(stderr, "Scanning...\n"); + + if (iw_set_mode(i, IW_MODE_INFRA) < 0) + return -1; + + if (iw_set_ap(i, &null_ap) < 0) + return -1; + + if (iw_set_essid(i, NULL) < 0) + return -1; + + if (iw_scan(i) < 0) + return -1; + + return 0; +} + +void get_script_path(char *path, int l, struct hw_addr *ap) { + assert(path && l); + + if (!ap) { + snprintf(path, l, "%s/default", SCRIPTDIR); + return; + } + + snprintf(path, l, "%s/%02x:%02x:%02x:%02x:%02x:%02x", + SCRIPTDIR, + ap->addr[0], ap->addr[1], ap->addr[2], + ap->addr[3], ap->addr[4], ap->addr[5]); +} + +struct ap_info selected_ap; +int selected_ap_has_script; +int selected_ap_valid; + +static int scan_result_cb(struct ap_info*ap) { + int b; + char path[PATH_MAX]; + + assert(ap); + get_script_path(path, sizeof(path), &ap->ap); + b = access(path, X_OK) == 0; + + if (selected_ap_valid) { + + if (!b || selected_ap_has_script) + return 0; + } + + memcpy(&selected_ap, ap, sizeof(struct ap_info)); + selected_ap_valid = 1; + selected_ap_has_script = 1; + return 0; +} + +int read_scan(struct interface *i, struct ap_info **ap) { + int r; + selected_ap_valid = 0; + selected_ap_has_script = 0; + + if ((r = iw_scan_result(i, scan_result_cb)) < 0) + return -1; + + if (r == 1) + return 1; + + *ap = selected_ap_valid ? &selected_ap : NULL; + + return 0; +} + +int run_script(struct hw_addr *ap, const char *arg) { + char path[PATH_MAX]; + + get_script_path(path, sizeof(path), ap); + + if (access(path, X_OK) < 0) + get_script_path(path, sizeof(path), NULL); + + return log_exec(SCRIPTDIR, path, arg); +}; + + +int set_current_ap(struct hw_addr *a) { + char t[32]; + + if (!a) + a = &null_ap; + + if (!hw_addr_equal(a, ¤t_ap)) { + + if (!hw_addr_equal(¤t_ap, &null_ap)) { + if (run_script(¤t_ap, "stop") < 0) + return -1; + } + + memcpy(¤t_ap, a, sizeof(struct hw_addr)); + + snprint_hw_addr(t, sizeof(t), ¤t_ap); + setenv("AP", t, 1); + setenv("IFACE", interface_name, 1); + + if (!hw_addr_equal(¤t_ap, &null_ap)) { + fprintf(stderr, "Selected AP "); + print_hw_addr(stderr, ¤t_ap); + fprintf(stderr, "\n"); + + if (run_script(¤t_ap, "start") < 0) + return -1; + } + } + + + return 0; +} + +int ifmonitor_cb(int b, int index, unsigned short type, const char *name) { + if (!name) + return 0; + + if (!strcmp(name, interface_name)) { + interface_index = index; + disabled = !b; + } + return 0; +} + +int assocwatch_cb(int index, struct hw_addr *a) { + char name[IFNAMSIZ+1]; + + if (get_ifname(index, name, sizeof(name)) < 0) + return -1; + + if (!strcmp(name, interface_name)) { + interface_index = index; + disabled = 0; + if ((associated = !!a)) + memcpy(&associated_ap, a, sizeof(struct hw_addr)); + } + + return 0; +} + +int go(struct interface *i) { + time_t next_scan; + int scanning = 0; + int send_retval = 1; + int r = -1, sigfd; + fd_set fds; + + daemon_log(LOG_INFO, "waproamd "VERSION" initializing%s%s.", use_ifmonitor ? ", using NETLINK device monitoring" : "", use_assocwatch ? ", using wireless event notifications" : ""); + + if (daemon_pid_file_create() < 0) { + daemon_log(LOG_ERR, "Could not create PID file %s.", daemon_pid_file_proc()); + goto finish; + } + + if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, -1) < 0) { + daemon_log(LOG_ERR, "Could not register signal handler: %s", strerror(errno)); + goto finish; + } + + if (nlapi_open(RTMGRP_LINK) < 0) + goto finish; + + if (use_ifmonitor) { + int b; + + if ((b = is_iface_available(interface_name)) < 0) + goto finish; + + disabled = !b; + } else + disabled = 0; + + memset(¤t_ap, 0, sizeof(current_ap)); + + if ((associated = interface_is_assoc(i, &associated_ap)) < 0) { + if (!use_ifmonitor) + goto finish; + + associated = 0; + } + + daemon_log(LOG_INFO, "Currently %sassociated, interface %s.", associated ? "" : "not ", disabled ? "disabled" : "enabled"); + + if (use_assocwatch) + if (assocwatch_init(assocwatch_cb) < 0) + goto finish; + + if (use_ifmonitor) + if (ifmonitor_init(ifmonitor_cb) < 0) + goto finish; + + daemon_log(LOG_INFO, "Initialization complete."); + + set_current_ap(&associated_ap); + next_scan = associated || disabled ? (time_t) -1 : 0; + + if (daemonize && wait_on_fork) { + daemon_retval_send(0); + send_retval = 0; + } + + FD_ZERO(&fds); + FD_SET(sigfd = daemon_signal_fd(), &fds); + FD_SET(nlapi_fd, &fds); + + for (;;) { + fd_set qfds; + struct timeval tv, *ptv; + time_t now = time(NULL); + int a, d; + + if (next_scan != (time_t) -1 && next_scan <= now) { + if (issue_scan(i) < 0) { + if (!use_ifmonitor) + goto finish; + } else + scanning = 1; + + next_scan = (time_t) -1; + } + + ptv = NULL; + + if (scanning) { + tv.tv_sec = 0; + tv.tv_usec = 100000; + ptv = &tv; + } else if (!use_assocwatch) { + tv.tv_sec = poll_interval; + tv.tv_usec = 0; + ptv = &tv; + } + + if (next_scan != (time_t) -1) { + struct timeval tv2; + now = time(NULL); + tv2.tv_sec = next_scan > now ? next_scan - now : 0; + tv2.tv_usec = 0; + + if (!ptv || tv2.tv_sec < tv.tv_sec) { + tv.tv_sec = tv2.tv_sec; + tv.tv_usec = tv2.tv_usec; + ptv = &tv; + } + } + + qfds = fds; + + if (select(FD_SETSIZE, &qfds, NULL, NULL, ptv) < 0) { + if (errno == EINTR) + continue; + + fprintf(stderr, "select() failed: %s\n", strerror(errno)); + goto finish; + } + + a = associated; + d = disabled; + + if (FD_ISSET(nlapi_fd, &qfds)) + if (nlapi_work(0) < 0) + goto finish; + + if (!use_assocwatch) { + if ((associated = interface_is_assoc(i, &associated_ap)) < 0) { + if (!use_ifmonitor) + goto finish; + + associated = 0; + } + } + + /* Changed: enabled -> disabled */ + if (!d && disabled) { + fprintf(stderr, "Interface disabled\n"); + + if (associated) + if (set_current_ap(&null_ap) < 0) + goto finish; + + associated = 0; + } + + /* Changed: disabled -> enabled */ + if (d && !disabled) { + fprintf(stderr, "Interface enabled\n"); + associated = 0; + } + + if (!disabled) { + /* Changed: associated -> not associated */ + if (a && !associated) + fprintf(stderr, "No longer associated.\n"); + + /* Changed: not associated -> associated */ + if (!a && associated) { + if (set_current_ap(&associated_ap) < 0) + goto finish; + + fprintf(stderr, "Associated.\n"); + next_scan = (time_t) -1; + } + + if (scanning) { + int r; + struct ap_info *ai = NULL; + + if ((r = read_scan(i, &ai)) < 0) { + if (!use_ifmonitor) + goto finish; + + scanning = 0; + + } else if (!r) { + + scanning = 0; + + if (!associated) { + + if (set_current_ap(ai ? &ai->ap : NULL) < 0) + goto finish; + } + } + } + } + + if (FD_ISSET(sigfd, &qfds)) { + int sig; + + if ((sig = daemon_signal_next()) < 0) { + daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno)); + goto finish; + } + + + switch (sig) { + + case SIGINT: + case SIGTERM: + case SIGQUIT: + r = 0; + goto finish; + + case SIGHUP: + next_scan = 0; + break; + + default: + daemon_log(LOG_INFO, "Ignoring unknown signal %s", strsignal(sig)); + break; + + } + } + + if (next_scan == (time_t) -1 && !scanning) + if (!disabled && !associated) + next_scan = time(NULL) + scan_interval; + + if (disabled || associated) { + scanning = 0; + next_scan = (time_t) -1; + } + } + + r = 0; + +finish: + + if (send_retval && daemonize && wait_on_fork) + daemon_retval_send(1); + + nlapi_close(); + + daemon_pid_file_remove(); + daemon_signal_done(); + + daemon_log(LOG_INFO, "Exiting."); + + return r; +} + +void usage(char *p) { + if (strrchr(p, '/')) + p = strchr(p, '/')+1; + + printf("%s -- Wireless Access Point Roaming Daemon for 802.11b\n\n" + "Usage: %s [options]\n\n" + "Options:\n" + " -n --no-daemon Do not daemonize (for debugging) (%s)\n" + " -s --no-syslog Do not use syslog, use stderr instead (for debugging) (%s)\n" + " -i --iface=IFACE Specify network interface (%s)\n" + " -w --wait-on-fork Wait until daemon fork finished (%s)\n" + " -M --monitor Use interface monitoring (%s)\n" + " -e --no-event Don't use wireless event API (%s)\n" + " -t --scan-interval Specify scan interval (%i)\n" + " -p --poll-interval Specify association poll interval, unless using -e (%i)\n" + " -h --help Show this help\n" + " -k --kill Kill a running daemon\n" + " -c --check-running Check if a daemon is currently running\n" + " -v --version Show version\n", + p, p, + !daemonize ? "on" : "off", + !use_syslog ? "on" : "off", + interface_name, + wait_on_fork ? "on" : "off", + use_ifmonitor ? "on" : "off", + use_assocwatch ? "off" : "on", + scan_interval, + poll_interval); +} + +void parse_args(int argc, char *argv[]) { + static struct option long_options[] = { + {"no-daemon", no_argument, 0, 'n'}, + {"no-syslog", no_argument, 0, 's'}, + {"iface", required_argument, 0, 'i'}, + {"wait-on-fork", no_argument, 0, 'w'}, + {"monitor", no_argument, 0, 'M'}, + {"no-event", no_argument, 0, 'e'}, + {"scan-interval", required_argument, 0, 't'}, + {"poll-interval", required_argument, 0, 'p'}, + {"help", no_argument, 0, 'h'}, + {"kill", no_argument, 0, 'k'}, + {"check-running", no_argument, 0, 'c'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; + int option_index = 0; + int _help = 0, _kill = 0, _check = 0, _version = 0; + + for (;;) { + int c; + + if ((c = getopt_long(argc, argv, "nsi:whkcvMet:p:", long_options, &option_index)) < 0) + break; + + switch (c) { + case 'n' : + daemonize = !daemonize; + break; + + case 's' : + use_syslog = !use_syslog; + break; + + case 'i' : + if (interface_name) + free(interface_name); + interface_name = strdup(optarg); + break; + + case 'w': + wait_on_fork = !wait_on_fork; + break; + + case 'M': + use_ifmonitor = !use_ifmonitor; + break; + + case 'e': + use_assocwatch = !use_assocwatch; + break; + + case 't': + if ((scan_interval = atoi(optarg)) < 0) { + daemon_log(LOG_ERR, "Scan interval must be a positive, nonzero integer."); + exit(1); + } + + case 'p': + if ((poll_interval = atoi(optarg)) < 0) { + daemon_log(LOG_ERR, "Poll interval must be a positive, nonzero integer."); + exit(1); + } + + case 'h': + _help = 1; + break; + + case 'k': + _kill = 1; + break; + + case 'c': + _check = 1; + break; + + case 'v': + _version = 1; + break; + + default: + fprintf(stderr, "Unknown parameter.\n"); + exit(1); + } + } + + if (!interface_name) + interface_name = strdup("wlan0"); + + snprintf(pid_ident, sizeof(pid_ident), "waproamd.%s", interface_name); + daemon_pid_file_ident = pid_ident; + snprintf(log_ident, sizeof(log_ident), "waproamd(%s)", interface_name); + daemon_log_ident = log_ident; + + + if (_help) { + usage(argv[0]); + exit(0); + } + + if (_kill) { + if (daemon_pid_file_kill(SIGINT) < 0) { + daemon_log(LOG_ERR, "Failed to kill daemon. (%s)", strerror(errno)); + exit(6); + } + + exit(0); + } + + if (_version) { + printf("waproamd "VERSION"\n"); + exit(0); + } + + if (_check) { + pid_t pid = daemon_pid_file_is_running(); + + if (pid == (pid_t) -1) + printf("waproamd not running.\n"); + else + printf("waproamd process for device %s running as pid %u.\n", interface_name, pid); + + exit(pid == 0 ? 255 : 0); + } + + if (!use_syslog) + daemon_log_use = DAEMON_LOG_STDERR; + +} + +int main(int argc, char *argv[]) { + struct interface *i = NULL; + int r = 1; + pid_t pid; + + daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]); + + parse_args(argc, argv); + + if (geteuid() != 0) { + daemon_log(LOG_ERR, "Sorry, you need to be root to run this binary."); + goto finish; + } + + if ((pid = daemon_pid_file_is_running()) >= 0) { + daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid); + goto finish; + + } + + if (daemonize) { + pid_t pid; + + if (wait_on_fork) + if (daemon_retval_init() < 0) { + daemon_log(LOG_ERR, "Sorry, could not create pipe: %s", strerror(errno)); + goto finish; + } + + if ((pid = daemon_fork()) < 0) + goto finish; + + if (pid) { + int c = 0; + + // Parent process + + if (wait_on_fork) + if ((c = daemon_retval_wait(60)) < 0) { + daemon_log(LOG_WARNING, "Killing background process."); + kill(pid, SIGTERM); + } + + r = c; + goto finish; + } + } + + if (!(i = interface_open(interface_name)) < 0) + goto finish; + + if (go(i) < 0) + goto finish; + + r = 0; + +finish: + + if (i) + interface_close(i); + + if (interface_name) + free(interface_name); + + return r; +} -- cgit