Logo Search packages:      
Sourcecode: waproamd version File versions  Download package

waproamd.c

/* $Id: waproamd.c 70 2004-02-08 20:18:25Z lennart $ */

/*
 * This file is part of waproamd.
 *
 * waproamd 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.
 *
 * waproamd 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 waproamd; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 */

#define _GNU_SOURCE

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <assert.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/stat.h>
#include <limits.h>

#include <libdaemon/dpid.h>
#include <libdaemon/dlog.h>
#include <libdaemon/dfork.h>
#include <libdaemon/dsignal.h>
#include <libdaemon/dexec.h>

#include "iwapi.h"
#include "interface.h"
#include "nlapi.h"
#include "ifmonitor.h"
#include "assocwatch.h"
#include "waproamd.h"
#include "iwhostroam.h"

char *interface_name = NULL;
int interface_index = -1;

int disabled = 0,
    associated = 0,
    do_status_check = 0;

struct ap_info associated_ap;

struct ap_info current_ap;
int current_ap_valid = 0;

int use_assocwatch = 1,
    use_ifmonitor = 0,
    daemonize = 1,
    wait_on_fork = 0,
    wait_on_kill = 0,
    use_syslog = 1,
    use_userspace_roaming = 1;

int poll_interval = 5,
    scan_interval = 30;

char log_ident[32], pid_ident[32];

int get_script_path(char *path, int l, struct ap_info *ai) {
    assert(path && l);
    struct stat st;

    if (!ai) {
        snprintf(path, l, "%s/default", SCRIPTDIR);
        return 0;
    }
    
    snprintf(path, l, "%s/%02x:%02x:%02x:%02x:%02x:%02x",
             SCRIPTDIR,
             ai->ap.addr[0], ai->ap.addr[1], ai->ap.addr[2],
             ai->ap.addr[3], ai->ap.addr[4], ai->ap.addr[5]);

    if (!stat(path, &st))
        return st.st_mode & S_IXUSR ? 0 : 1;
    
    snprintf(path, l, "%s/%02X:%02X:%02X:%02X:%02X:%02X",
             SCRIPTDIR,
             ai->ap.addr[0], ai->ap.addr[1], ai->ap.addr[2],
             ai->ap.addr[3], ai->ap.addr[4], ai->ap.addr[5]);
    
    if (!stat(path, &st))
        return st.st_mode & S_IXUSR ? 0 : 1;

    if (ai->essid[0]) {
        snprintf(path, l, "%s/essid:%s",
                 SCRIPTDIR,
                 escape_essid(ai->essid));
        
        if (!stat(path, &st))
            return st.st_mode & S_IXUSR ? 0 : 1;
    }

    return -1;
}

int total_aps_found_n, aps_found_n;

struct ap_info scan_ap;
int scan_ap_has_script;

static int scan_result_cb(struct ap_info*ai) {
    int f;
    char path[PATH_MAX], t[32];
    
    assert(ai);

    if (total_aps_found_n == 0)
        daemon_log(LOG_INFO, "Scan results:");

    total_aps_found_n++;
    
    snprint_hw_addr(t, sizeof(t), &ai->ap);
    f = get_script_path(path, sizeof(path), ai);
    daemon_log(LOG_INFO, "%i. Found AP %s, ESSID '%s', script: %s", total_aps_found_n, t, escape_essid(ai->essid), f == 0 ? "yes" : (f < 0 ? "no" : "off"));

    if (f <= 0) {
        aps_found_n++;
        
        /* Select this AP, if no better was found before */
        if ((aps_found_n <= 1 || (!scan_ap_has_script && f == 0))) {
            memcpy(&scan_ap, ai, sizeof(struct ap_info));
            scan_ap_has_script = f == 0;
        }
    }
    
    return 0;
}

int read_scan(struct interface *i, struct ap_info **ap) {
    int r;
    aps_found_n = total_aps_found_n = 0;
    scan_ap_has_script = 0;
    memset(&scan_ap, 0, sizeof(scan_ap));
    
    if ((r = iw_scan_result(i, scan_result_cb)) < 0)
        return -1;

    if (r == 1)
        return 1;

    daemon_log(LOG_INFO, "Scan completed with %u suitable networks. (total: %u)", aps_found_n, total_aps_found_n);

    *ap = aps_found_n > 0 ? &scan_ap : NULL;

    return 0;
}
        
int run_script(struct ap_info *ai, const char *arg) {
    char t[32];
    char path[PATH_MAX];
    int ret = 0;
    
    if (get_script_path(path, sizeof(path), ai) < 0)
        get_script_path(path, sizeof(path), NULL);

    setenv("IFACE", interface_name, 1);
    snprint_hw_addr(t, sizeof(t), &ai->ap);
    setenv("AP", t, 1);
    setenv("ESSID", ai->essid, 1);
    setenv("ESSID_ESCAPED", escape_essid(ai->essid), 1);

    daemon_log(LOG_INFO, "Running script '%s %s'", path, arg);
    
    if (daemon_exec(SCRIPTDIR, &ret, path, path, arg, (char*) 0) < 0) 
        daemon_log(LOG_WARNING, "Script execution failed abnormously.");
    else if (ret != 0)
        daemon_log(LOG_WARNING, "Script returned %i as return value.", ret);
    else
        daemon_log(LOG_INFO, "Script successfully executed.");

    return 0;
        
};

int set_current_ap(struct interface *i, struct ap_info *a) {

    if ((a && current_ap_valid && !iw_ap_info_equal(a, &current_ap)) || (!!a != !!current_ap_valid)) {

        if (current_ap_valid) {
            if (run_script(&current_ap, "stop") < 0)
                return -1;
        }

        if ((current_ap_valid = !!a)) {
            char t[20];

            memcpy(&current_ap, a, sizeof(struct ap_info));
            snprint_hw_addr(t, sizeof(t), &current_ap.ap);
            daemon_log(LOG_INFO, "Selected new AP %s with ESSID '%s'", t, escape_essid(a->essid));
            
            if (run_script(&current_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))
        return 0;

    return 0;
}

int go(struct interface *i) {
    time_t next_scan;
    int scanning = 0;
    int send_retval = 1;
    int r = -1, sigfd;
    int paused = 0;
    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, SIGCHLD, SIGUSR1, SIGUSR2, -1) < 0) {
        daemon_log(LOG_ERR, "Could not register signal handler: %s", strerror(errno));
        goto finish;
    }

    if (nlapi_open(RTMGRP_LINK) < 0)
        goto finish;

    interface_up(i);
    
    /* Check whether interface is available */
    if (use_ifmonitor) {
        int b;
        
        if ((b = is_iface_available(interface_name)) < 0)
            goto finish;

        disabled = !b;
    } else
        disabled = 0;

    /* Try to enable userspace roaming in the driver */
    if (!disabled && use_userspace_roaming) {

        if (iw_set_hostroam(i, 2) < 0)
            daemon_log(LOG_WARNING, "Enabling user space roaming failed, doing without.");
    }

    memset(&current_ap, 0, sizeof(current_ap));
    current_ap_valid = 0;

    /* Check association status */
    if (!disabled) {
        if ((associated = iw_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(i, associated ? &associated_ap : NULL);
    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;
        time_t now = time(NULL);
        int a, d;

        if (next_scan != (time_t) -1 && next_scan <= now) {
            if (iw_scan(i) < 0) {
                if (!use_ifmonitor)
                    goto finish;
            } else 
                scanning = 1;
            
            next_scan = (time_t) -1;
        }

        if (do_status_check) {
            tv.tv_sec = 0;
            tv.tv_usec = 0;
        } if (scanning) {
            tv.tv_sec = 0;
            tv.tv_usec = 100000;
        } else {
            tv.tv_sec = poll_interval;
            tv.tv_usec = 0;
        }
        
        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 (tv2.tv_sec < tv.tv_sec)
                tv = tv2;
        }

        qfds = fds;

        if (select(FD_SETSIZE, &qfds, NULL, NULL, &tv) < 0) {
            if (errno == EINTR)
                continue;

            daemon_log(LOG_ERR, "select() failed: %s", strerror(errno));
            goto finish;
        }

        a = associated;
        d = disabled;
        
        if (FD_ISSET(nlapi_fd, &qfds))
            if (nlapi_work(0) < 0)
                goto finish;

        if (!disabled && !paused && !scanning) {

            if ((associated = iw_assoc(i, &associated_ap)) < 0) {
                if (!use_ifmonitor)
                    goto finish;

                associated = 0;
            }

            do_status_check = 0;
        }

        if (paused) {

            /* If paused ignore new data */
            associated = a;
            disabled = d;
            
        } else {
            
            /* Changed: enabled -> disabled */
            if (!d && disabled) {
                daemon_log(LOG_INFO, "Interface disabled.");
                
                if (associated)
                    if (set_current_ap(i, NULL) < 0)
                        goto finish;
                
                associated = scanning = 0;
            }
            
            /* Changed: disabled -> enabled */
            if (d && !disabled) {
                daemon_log(LOG_INFO, "Interface enabled.");
                associated = scanning = 0;

                interface_up(i);
                
                if (use_userspace_roaming) {
                    if (iw_set_hostroam(i, 2) < 0)
                        daemon_log(LOG_WARNING, "Enabling user space roaming failed, doing without.");
                }
            }

            if (!disabled) {
                /* Changed: associated -> not associated */
                if (a && !associated) {
                    daemon_log(LOG_INFO, "No longer associated.");
                    scanning = 0;
                }
                
                /* Changed: not associated -> associated */
                if (!a && associated) {
                    if (set_current_ap(i, &associated_ap) < 0)
                        goto finish;
                    
                    daemon_log(LOG_INFO, "Associated.");
                    scanning = 0;
                }                
        
                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 (set_current_ap(i, ai) < 0)
                            if (!use_ifmonitor)
                                goto finish;
                        
                        if (ai && iw_tune(i, ai) < 0)
                            if (!use_ifmonitor)
                                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:
                    if (!disabled && !associated && !paused) {
                        next_scan = 0;
                        daemon_log(LOG_INFO, "SIGHUP: Immediate scan requested");
                    } else
                        daemon_log(LOG_INFO, "SIGHUP: Immediate scan request ignored");
                    
                    break;

                case SIGCHLD:
                        break;

                case SIGUSR1:
                    daemon_log(LOG_INFO, "SIGUSR1: Daemon suspended (#%i)", ++paused);
                    break;

                case SIGUSR2:
                    if (paused > 0) {
                        daemon_log(LOG_INFO, "SIGUSR2: Daemon resumed (#%i)", paused--);
                        if (!paused)
                            do_status_check = 1;
                    }

                    break;
                    
                default:
                    daemon_log(LOG_INFO, "Ignoring unknown signal %s", strsignal(sig));
                    break;

            }
        }
        
        if (disabled || associated || paused) {
            scanning = 0;
            next_scan = (time_t) -1;
        } else if (next_scan == (time_t) -1 && !scanning)
            next_scan = time(NULL) + scan_interval;
    }

    r = 0;
    
finish:

    daemon_log(LOG_INFO, "Shutdown");
    
    set_current_ap(i, NULL);

    if (use_userspace_roaming)
        iw_set_hostroam(i, 0);
    
    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 WLAN IEEE 802.11\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"
           "   -W --wait-on-kill         When run with -k, wait until the daemon died (%s)\n"
           "   -M --monitor              Use interface monitoring (%s)\n"
           "   -e --no-event             Don't use wireless event API (%s)\n"
           "   -U --no-userspace-roaming Don't enable user space roaming via private ioctl (%s)\n"
           "   -t --scan-interval=SECS   Specify scan interval (%i)\n"
           "   -p --poll-interval=SECS   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"
           "   -S --suspend              Suspend running daemon\n"
           "   -R --resume               Resume running daemon\n"
           "   -r --issue-scan           Tell running daemon to issue a new scan immediately\n",
           p, p,
           !daemonize ? "on" : "off",
           !use_syslog ? "on" : "off",
           interface_name,
           wait_on_fork ? "on" : "off",
           wait_on_kill ? "on" : "off",
           use_ifmonitor ? "on" : "off",
           use_assocwatch ? "off" : "on",
           use_userspace_roaming ? "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'},
        {"wait-on-kill",         no_argument, 0, 'W'},
        {"monitor",              no_argument, 0, 'M'},
        {"no-event",             no_argument, 0, 'e'},
        {"no-userspace-roaming", no_argument, 0, 'U'},
        {"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'},
        {"suspend",              no_argument, 0, 'S'},
        {"resume",               no_argument, 0, 'R'},
        {"issue-scan",           no_argument, 0, 'r'},
        {0, 0, 0, 0}
    };
    int option_index = 0;
    int _help = 0, _kill = 0, _check = 0, _version = 0, _suspend = 0, _resume = 0, _issuescan = 0;
    
    for (;;) {
        int c;
        
        if ((c = getopt_long(argc, argv, "nsi:whkcvMet:p:SRrWrU", 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 'W':
                wait_on_kill = !wait_on_kill;
                break;
            case 'S':
                _suspend = 1;
                break;
                
            case 'R':
                _resume = 1;
                break;
                
            case 'r':
                _issuescan = 1;
                break;
                
            case 'M':
                use_ifmonitor = !use_ifmonitor;
                break;

            case 'e':
                use_assocwatch = !use_assocwatch;
                break;

            case 'U':
                use_userspace_roaming = !use_userspace_roaming;
                break;
                
            case 't':
                if ((scan_interval = atoi(optarg)) < 0) {
                    daemon_log(LOG_ERR, "Scan interval must be a positive, nonzero integer.");
                    exit(1);
                }
                break;
                
            case 'p':
                if ((poll_interval = atoi(optarg)) < 0) {
                    daemon_log(LOG_ERR, "Poll interval must be a positive, nonzero integer.");
                    exit(1);
                }
                break;
                
            case 'h':
                _help = 1;
                break;

            case 'k':
                _kill = 1;
                break;

            case 'c':
                _check = 1;
                break;

            case 'v':
                _version = 1;
                break;
                
            default:
                daemon_log(LOG_ERR, "Unknown parameter.");
                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 || _resume || _suspend || _issuescan) {
        int rv;

        if (_kill && wait_on_kill)
            rv = daemon_pid_file_kill_wait(SIGINT, 5);
        else
            rv = daemon_pid_file_kill(_kill ? SIGINT : (_resume ? SIGUSR2 : (_issuescan ? SIGHUP : SIGUSR1)));
        
        if (rv < 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 || pid == 0) {
            printf("waproamd not running.\n");
            exit(255);
        } else {
            printf("waproamd process for device %s running as pid %u.\n", interface_name, pid);
            exit(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;
}

Generated by  Doxygen 1.6.0   Back to index