diff options
Diffstat (limited to 'pand/main.c')
-rw-r--r-- | pand/main.c | 798 |
1 files changed, 0 insertions, 798 deletions
diff --git a/pand/main.c b/pand/main.c deleted file mode 100644 index ce3b8ebb..00000000 --- a/pand/main.c +++ /dev/null @@ -1,798 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> - * Copyright (C) 2002-2008 Marcel Holtmann <marcel@holtmann.org> - * - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#define _GNU_SOURCE -#include <stdio.h> -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <signal.h> -#include <getopt.h> -#include <sys/poll.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/socket.h> - -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/l2cap.h> -#include <bluetooth/bnep.h> - -#include "pand.h" - -#ifdef NEED_PPOLL -#include "ppoll.h" -#endif - -static uint16_t role = BNEP_SVC_PANU; /* Local role (ie service) */ -static uint16_t service = BNEP_SVC_NAP; /* Remote service */ - -static int detach = 1; -static int persist; -static int use_sdp = 1; -static int use_cache; -static int link_mode = 0; -static int cleanup; -static int search_duration = 10; - -static struct { - int valid; - char dst[40]; - bdaddr_t bdaddr; -} cache; - -static char netdev[16] = "bnep%d"; -static char *pidfile = NULL; -static char *devupcmd = NULL; -static char *devdowncmd = NULL; - -static bdaddr_t src_addr = *BDADDR_ANY; -static int src_dev = -1; - -static volatile int terminate; - -static void do_kill(char *dst); - -enum { - NONE, - SHOW, - LISTEN, - CONNECT, - KILL -} modes; - -struct script_arg { - char dev[20]; - char dst[20]; - int sk; - int nsk; -}; - -static void run_script(char *script, char *dev, char *dst, int sk, int nsk) -{ - char *argv[4]; - struct sigaction sa; - - if (!script) - return; - - if (access(script, R_OK | X_OK)) - return; - - if (fork()) - return; - - if (sk >= 0) - close(sk); - - if (nsk >= 0) - close(nsk); - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); - - argv[0] = script; - argv[1] = dev; - argv[2] = dst; - argv[3] = NULL; - - execv(script, argv); - - exit(1); -} - -/* Wait for disconnect or error condition on the socket */ -static int w4_hup(int sk, struct script_arg *down_cmd) -{ - struct pollfd pf; - sigset_t sigs; - int n; - - sigfillset(&sigs); - sigdelset(&sigs, SIGCHLD); - sigdelset(&sigs, SIGPIPE); - sigdelset(&sigs, SIGTERM); - sigdelset(&sigs, SIGINT); - sigdelset(&sigs, SIGHUP); - - while (!terminate) { - pf.fd = sk; - pf.events = POLLERR | POLLHUP; - - n = ppoll(&pf, 1, NULL, &sigs); - - if (n < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - - syslog(LOG_ERR, "Poll failed. %s(%d)", - strerror(errno), errno); - - return 1; - } - - if (n) { - int err = 0; - socklen_t olen = sizeof(err); - - getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &olen); - - syslog(LOG_INFO, "%s disconnected%s%s", netdev, - err ? " : " : "", err ? strerror(err) : ""); - - if (down_cmd) - run_script(devdowncmd, - down_cmd->dev, down_cmd->dst, - down_cmd->sk, down_cmd->nsk); - - close(sk); - - return 0; - } - } - - return 0; -} - -static int do_listen(void) -{ - struct l2cap_options l2o; - struct sockaddr_l2 l2a; - socklen_t olen; - int sk, lm; - - if (use_sdp) - bnep_sdp_register(&src_addr, role); - - /* Create L2CAP socket and bind it to PSM BNEP */ - sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); - if (sk < 0) { - syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)", - strerror(errno), errno); - return -1; - } - - memset(&l2a, 0, sizeof(l2a)); - l2a.l2_family = AF_BLUETOOTH; - bacpy(&l2a.l2_bdaddr, &src_addr); - l2a.l2_psm = htobs(BNEP_PSM); - - if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a))) { - syslog(LOG_ERR, "Bind failed. %s(%d)", - strerror(errno), errno); - return -1; - } - - /* Setup L2CAP options according to BNEP spec */ - memset(&l2o, 0, sizeof(l2o)); - olen = sizeof(l2o); - if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen) < 0) { - syslog(LOG_ERR, "Failed to get L2CAP options. %s(%d)", - strerror(errno), errno); - return -1; - } - - l2o.imtu = l2o.omtu = BNEP_MTU; - if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) { - syslog(LOG_ERR, "Failed to set L2CAP options. %s(%d)", - strerror(errno), errno); - return -1; - } - - /* Set link mode */ - lm = link_mode; - if (lm && setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) { - syslog(LOG_ERR, "Failed to set link mode. %s(%d)", - strerror(errno), errno); - return -1; - } - - listen(sk, 10); - - while (!terminate) { - socklen_t alen = sizeof(l2a); - char devname[16]; - int nsk; - - nsk = accept(sk, (struct sockaddr *) &l2a, &alen); - if (nsk < 0) { - syslog(LOG_ERR, "Accept failed. %s(%d)", - strerror(errno), errno); - continue; - } - - switch (fork()) { - case 0: - break; - case -1: - syslog(LOG_ERR, "Fork failed. %s(%d)", - strerror(errno), errno); - default: - close(nsk); - continue; - } - - strncpy(devname, netdev, 16); - devname[15] = '\0'; - - if (!bnep_accept_connection(nsk, role, devname)) { - char str[40]; - struct script_arg down_cmd; - - ba2str(&l2a.l2_bdaddr, str); - - syslog(LOG_INFO, "New connection from %s at %s", - str, devname); - - run_script(devupcmd, devname, str, sk, nsk); - - memset(&down_cmd, 0, sizeof(struct script_arg)); - strncpy(down_cmd.dev, devname, strlen(devname) + 1); - strncpy(down_cmd.dst, str, strlen(str) + 1); - down_cmd.sk = sk; - down_cmd.nsk = nsk; - w4_hup(nsk, &down_cmd); - } else { - syslog(LOG_ERR, "Connection failed. %s(%d)", - strerror(errno), errno); - } - - close(nsk); - exit(0); - } - - if (use_sdp) - bnep_sdp_unregister(); - - return 0; -} - -/* Connect and initiate BNEP session - * Returns: - * -1 - critical error (exit persist mode) - * 1 - non critical error - * 0 - success - */ -static int create_connection(char *dst, bdaddr_t *bdaddr) -{ - struct l2cap_options l2o; - struct sockaddr_l2 l2a; - socklen_t olen; - int sk, r = 0; - struct script_arg down_cmd; - - syslog(LOG_INFO, "Connecting to %s", dst); - - sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); - if (sk < 0) { - syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)", - strerror(errno), errno); - return -1; - } - - /* Setup L2CAP options according to BNEP spec */ - memset(&l2o, 0, sizeof(l2o)); - olen = sizeof(l2o); - getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen); - l2o.imtu = l2o.omtu = BNEP_MTU; - setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)); - - memset(&l2a, 0, sizeof(l2a)); - l2a.l2_family = AF_BLUETOOTH; - bacpy(&l2a.l2_bdaddr, &src_addr); - - if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a))) - syslog(LOG_ERR, "Bind failed. %s(%d)", - strerror(errno), errno); - - memset(&l2a, 0, sizeof(l2a)); - l2a.l2_family = AF_BLUETOOTH; - bacpy(&l2a.l2_bdaddr, bdaddr); - l2a.l2_psm = htobs(BNEP_PSM); - - if (!connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) && - !bnep_create_connection(sk, role, service, netdev)) { - - syslog(LOG_INFO, "%s connected", netdev); - - run_script(devupcmd, netdev, dst, sk, -1); - - if (persist || devdowncmd) { - memset(&down_cmd, 0, sizeof(struct script_arg)); - strncpy(down_cmd.dev, netdev, strlen(netdev) + 1); - strncpy(down_cmd.dst, dst, strlen(dst) + 1); - down_cmd.sk = sk; - down_cmd.nsk = -1; - w4_hup(sk, &down_cmd); - - if (terminate && cleanup) { - syslog(LOG_INFO, "Disconnecting from %s.", dst); - do_kill(dst); - } - } - - r = 0; - } else { - syslog(LOG_ERR, "Connect to %s failed. %s(%d)", - dst, strerror(errno), errno); - r = 1; - } - - close(sk); - - if (use_cache) { - if (!r) { - /* Succesesful connection, validate cache */ - strcpy(cache.dst, dst); - bacpy(&cache.bdaddr, bdaddr); - cache.valid = use_cache; - } else - cache.valid--; - } - - return r; -} - -/* Search and connect - * Returns: - * -1 - critical error (exit persist mode) - * 1 - non critical error - * 0 - success - */ -static int do_connect(void) -{ - inquiry_info *ii; - int reconnect = 0; - int i, n, r = 0; - - do { - if (reconnect) - sleep(persist); - reconnect = 1; - - if (cache.valid > 0) { - /* Use cached bdaddr */ - r = create_connection(cache.dst, &cache.bdaddr); - if (r < 0) { - terminate = 1; - break; - } - continue; - } - - syslog(LOG_INFO, "Inquiring"); - - /* FIXME: Should we use non general LAP here ? */ - - ii = NULL; - n = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0); - if (n < 0) { - syslog(LOG_ERR, "Inquiry failed. %s(%d)", - strerror(errno), errno); - continue; - } - - for (i = 0; i < n; i++) { - char dst[40]; - ba2str(&ii[i].bdaddr, dst); - - if (use_sdp) { - syslog(LOG_INFO, "Searching for %s on %s", - bnep_svc2str(service), dst); - - if (bnep_sdp_search(&src_addr, &ii[i].bdaddr, service) <= 0) - continue; - } - - r = create_connection(dst, &ii[i].bdaddr); - if (r < 0) { - terminate = 1; - break; - } - } - bt_free(ii); - } while (!terminate && persist); - - return r; -} - -static void do_show(void) -{ - bnep_show_connections(); -} - -static void do_kill(char *dst) -{ - if (dst) - bnep_kill_connection((void *) strtoba(dst)); - else - bnep_kill_all_connections(); -} - -static void sig_hup(int sig) -{ - return; -} - -static void sig_term(int sig) -{ - terminate = 1; -} - -static int write_pidfile(void) -{ - int fd; - FILE *f; - pid_t pid; - - do { - fd = open(pidfile, O_WRONLY|O_TRUNC|O_CREAT|O_EXCL, 0644); - if (fd == -1) { - /* Try to open the file for read. */ - fd = open(pidfile, O_RDONLY); - if (fd < 0) { - syslog(LOG_ERR, "Could not read old pidfile: %s(%d)", - strerror(errno), errno); - return -1; - } - - /* We're already running; send a SIGHUP (we presume that they - * are calling ifup for a reason, so they probably want to - * rescan) and then exit cleanly and let things go on in the - * background. Muck with the filename so that we don't go - * deleting the pid file for the already-running instance. - */ - f = fdopen(fd, "r"); - if (!f) { - syslog(LOG_ERR, "Could not fdopen old pidfile: %s(%d)", - strerror(errno), errno); - close(fd); - return -1; - } - - pid = 0; - if (fscanf(f, "%d", &pid) != 1) - pid = 0; - fclose(f); - - if (pid) { - /* Try to kill it. */ - if (kill(pid, SIGHUP) == -1) { - /* No such pid; remove the bogus pid file. */ - syslog(LOG_INFO, "Removing stale pidfile"); - unlink(pidfile); - fd = -1; - } else { - /* Got it. Don't mess with the pid file on - * our way out. */ - syslog(LOG_INFO, "Signalling existing process %d and exiting\n", pid); - pidfile = NULL; - return -1; - } - } - } - } while(fd == -1); - - f = fdopen(fd, "w"); - if (!f) { - syslog(LOG_ERR, "Could not fdopen new pidfile: %s(%d)", - strerror(errno), errno); - close(fd); - unlink(pidfile); - return -1; - } - - fprintf(f, "%d\n", getpid()); - fclose(f); - - return 0; -} - -static struct option main_lopts[] = { - { "help", 0, 0, 'h' }, - { "listen", 0, 0, 's' }, - { "connect", 1, 0, 'c' }, - { "search", 2, 0, 'Q' }, - { "kill", 1, 0, 'k' }, - { "killall", 0, 0, 'K' }, - { "role", 1, 0, 'r' }, - { "service", 1, 0, 'd' }, - { "ethernet", 1, 0, 'e' }, - { "device", 1, 0, 'i' }, - { "nosdp", 0, 0, 'D' }, - { "list", 0, 0, 'l' }, - { "show", 0, 0, 'l' }, - { "nodetach", 0, 0, 'n' }, - { "persist", 2, 0, 'p' }, - { "auth", 0, 0, 'A' }, - { "encrypt", 0, 0, 'E' }, - { "secure", 0, 0, 'S' }, - { "master", 0, 0, 'M' }, - { "cache", 0, 0, 'C' }, - { "pidfile", 1, 0, 'P' }, - { "devup", 1, 0, 'u' }, - { "devdown", 1, 0, 'o' }, - { "autozap", 0, 0, 'z' }, - { 0, 0, 0, 0 } -}; - -static char main_sopts[] = "hsc:k:Kr:d:e:i:lnp::DQ::AESMC::P:u:o:z"; - -static char main_help[] = - "Bluetooth PAN daemon version " VERSION " \n" - "Usage:\n" - "\tpand <options>\n" - "Options:\n" - "\t--show --list -l Show active PAN connections\n" - "\t--listen -s Listen for PAN connections\n" - "\t--connect -c <bdaddr> Create PAN connection\n" - "\t--autozap -z Disconnect automatically on exit\n" - "\t--search -Q[duration] Search and connect\n" - "\t--kill -k <bdaddr> Kill PAN connection\n" - "\t--killall -K Kill all PAN connections\n" - "\t--role -r <role> Local PAN role (PANU, NAP, GN)\n" - "\t--service -d <role> Remote PAN service (PANU, NAP, GN)\n" - "\t--ethernet -e <name> Network interface name\n" - "\t--device -i <bdaddr> Source bdaddr\n" - "\t--nosdp -D Disable SDP\n" - "\t--auth -A Enable authentication\n" - "\t--encrypt -E Enable encryption\n" - "\t--secure -S Secure connection\n" - "\t--master -M Become the master of a piconet\n" - "\t--nodetach -n Do not become a daemon\n" - "\t--persist -p[interval] Persist mode\n" - "\t--cache -C[valid] Cache addresses\n" - "\t--pidfile -P <pidfile> Create PID file\n" - "\t--devup -u <script> Script to run when interface comes up\n" - "\t--devdown -o <script> Script to run when interface comes down\n"; - -int main(int argc, char *argv[]) -{ - char *dst = NULL, *src = NULL; - struct sigaction sa; - int mode = NONE; - int opt; - - while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) { - switch(opt) { - case 'l': - mode = SHOW; - detach = 0; - break; - - case 's': - mode = LISTEN; - break; - - case 'c': - mode = CONNECT; - dst = strdup(optarg); - break; - - case 'Q': - mode = CONNECT; - if (optarg) - search_duration = atoi(optarg); - break; - - case 'k': - mode = KILL; - detach = 0; - dst = strdup(optarg); - break; - - case 'K': - mode = KILL; - detach = 0; - break; - - case 'i': - src = strdup(optarg); - break; - - case 'r': - bnep_str2svc(optarg, &role); - break; - - case 'd': - bnep_str2svc(optarg, &service); - break; - - case 'D': - use_sdp = 0; - break; - - case 'A': - link_mode |= L2CAP_LM_AUTH; - break; - - case 'E': - link_mode |= L2CAP_LM_ENCRYPT; - break; - - case 'S': - link_mode |= L2CAP_LM_SECURE; - break; - - case 'M': - link_mode |= L2CAP_LM_MASTER; - break; - - case 'e': - strncpy(netdev, optarg, 16); - netdev[15] = '\0'; - break; - - case 'n': - detach = 0; - break; - - case 'p': - if (optarg) - persist = atoi(optarg); - else - persist = 5; - break; - - case 'C': - if (optarg) - use_cache = atoi(optarg); - else - use_cache = 2; - break; - - case 'P': - pidfile = strdup(optarg); - break; - - case 'u': - devupcmd = strdup(optarg); - break; - - case 'o': - devdowncmd = strdup(optarg); - break; - - case 'z': - cleanup = 1; - break; - - case 'h': - default: - printf(main_help); - exit(0); - } - } - - argc -= optind; - argv += optind; - optind = 0; - - if (bnep_init()) - return -1; - - /* Check non daemon modes first */ - switch (mode) { - case SHOW: - do_show(); - return 0; - - case KILL: - do_kill(dst); - return 0; - - case NONE: - printf(main_help); - return 0; - } - - /* Initialize signals */ - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_NOCLDSTOP; - sa.sa_handler = SIG_IGN; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); - - sa.sa_handler = sig_hup; - sigaction(SIGHUP, &sa, NULL); - - sa.sa_handler = sig_term; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - - if (detach && daemon(0, 0)) { - perror("Can't start daemon"); - exit(1); - } - - openlog("pand", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); - syslog(LOG_INFO, "Bluetooth PAN daemon version %s", VERSION); - - if (src) { - src_dev = hci_devid(src); - if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) { - syslog(LOG_ERR, "Invalid source. %s(%d)", - strerror(errno), errno); - return -1; - } - } - - if (pidfile && write_pidfile()) - return -1; - - if (dst) { - /* Disable cache invalidation */ - use_cache = 0; - - strncpy(cache.dst, dst, sizeof(cache.dst) - 1); - str2ba(dst, &cache.bdaddr); - cache.valid = 1; - free(dst); - } - - switch (mode) { - case CONNECT: - do_connect(); - break; - - case LISTEN: - do_listen(); - break; - } - - if (pidfile) - unlink(pidfile); - - return 0; -} |