diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2008-08-04 20:58:34 +0200 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2008-08-04 20:58:34 +0200 |
commit | 40372f9dc3ab115870a10343124216a041e55d17 (patch) | |
tree | ef5bfa80e3f560cf6856fd7a0d5c27c75c2cefe0 /compat/dun.c | |
parent | 295eb0c879802eacff46cf3c962646363d81f6d3 (diff) |
Move hidd pand and dund into compat directory
Diffstat (limited to 'compat/dun.c')
-rw-r--r-- | compat/dun.c | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/compat/dun.c b/compat/dun.c new file mode 100644 index 00000000..de437cf4 --- /dev/null +++ b/compat/dun.c @@ -0,0 +1,331 @@ +/* + * + * 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 + +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <syslog.h> +#include <dirent.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> + +#include "dund.h" +#include "lib.h" + +#define PROC_BASE "/proc" + +static int for_each_port(int (*func)(struct rfcomm_dev_info *, unsigned long), unsigned long arg) +{ + struct rfcomm_dev_list_req *dl; + struct rfcomm_dev_info *di; + long r = 0; + int sk, i; + + sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM); + if (sk < 0 ) { + perror("Can't open RFCOMM control socket"); + exit(1); + } + + dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di)); + if (!dl) { + perror("Can't allocate request memory"); + close(sk); + exit(1); + } + + dl->dev_num = RFCOMM_MAX_DEV; + di = dl->dev_info; + + if (ioctl(sk, RFCOMMGETDEVLIST, (void *) dl) < 0) { + perror("Can't get device list"); + exit(1); + } + + for (i = 0; i < dl->dev_num; i++) { + r = func(di + i, arg); + if (r) break; + } + + close(sk); + return r; +} + +static int uses_rfcomm(char *path, char *dev) +{ + struct dirent *de; + DIR *dir; + + dir = opendir(path); + if (!dir) + return 0; + + if (chdir(path) < 0) + return 0; + + while ((de = readdir(dir)) != NULL) { + char link[PATH_MAX + 1]; + int len = readlink(de->d_name, link, sizeof(link)); + if (len > 0) { + link[len] = 0; + if (strstr(link, dev)) + return 1; + } + } + + closedir(dir); + + return 0; +} + +static int find_pppd(int id, pid_t *pid) +{ + struct dirent *de; + char path[PATH_MAX + 1]; + char dev[10]; + int empty = 1; + DIR *dir; + + dir = opendir(PROC_BASE); + if (!dir) { + perror(PROC_BASE); + return -1; + } + + sprintf(dev, "rfcomm%d", id); + + *pid = 0; + while ((de = readdir(dir)) != NULL) { + empty = 0; + if (isdigit(de->d_name[0])) { + sprintf(path, "%s/%s/fd", PROC_BASE, de->d_name); + if (uses_rfcomm(path, dev)) { + *pid = atoi(de->d_name); + break; + } + } + } + closedir(dir); + + if (empty) + fprintf(stderr, "%s is empty (not mounted ?)\n", PROC_BASE); + + return *pid != 0; +} + +static int dun_exec(char *tty, char *prog, char **args) +{ + int pid = fork(); + int fd; + + switch (pid) { + case -1: + return -1; + + case 0: + break; + + default: + return pid; + } + + setsid(); + + /* Close all FDs */ + for (fd = 3; fd < 20; fd++) + close(fd); + + execvp(prog, args); + + syslog(LOG_ERR, "Error while executing %s", prog); + + exit(1); +} + +static int dun_create_tty(int sk, char *tty, int size) +{ + struct sockaddr_rc sa; + struct stat st; + socklen_t alen; + int id, try = 30; + + struct rfcomm_dev_req req = { + flags: (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP), + dev_id: -1 + }; + + alen = sizeof(sa); + if (getpeername(sk, (struct sockaddr *) &sa, &alen) < 0) + return -1; + bacpy(&req.dst, &sa.rc_bdaddr); + + alen = sizeof(sa); + if (getsockname(sk, (struct sockaddr *) &sa, &alen) < 0) + return -1; + bacpy(&req.src, &sa.rc_bdaddr); + req.channel = sa.rc_channel; + + id = ioctl(sk, RFCOMMCREATEDEV, &req); + if (id < 0) + return id; + + snprintf(tty, size, "/dev/rfcomm%d", id); + while (stat(tty, &st) < 0) { + snprintf(tty, size, "/dev/bluetooth/rfcomm/%d", id); + if (stat(tty, &st) < 0) { + snprintf(tty, size, "/dev/rfcomm%d", id); + if (try--) { + usleep(100 * 1000); + continue; + } + + memset(&req, 0, sizeof(req)); + req.dev_id = id; + ioctl(sk, RFCOMMRELEASEDEV, &req); + + return -1; + } + } + + return id; +} + +int dun_init(void) +{ + return 0; +} + +int dun_cleanup(void) +{ + return 0; +} + +static int show_conn(struct rfcomm_dev_info *di, unsigned long arg) +{ + pid_t pid; + + if (di->state == BT_CONNECTED && + (di->flags & (1<<RFCOMM_REUSE_DLC)) && + (di->flags & (1<<RFCOMM_TTY_ATTACHED)) && + (di->flags & (1<<RFCOMM_RELEASE_ONHUP))) { + + if (find_pppd(di->id, &pid)) { + char dst[18]; + ba2str(&di->dst, dst); + + printf("rfcomm%d: %s channel %d pppd pid %d\n", + di->id, dst, di->channel, pid); + } + } + return 0; +} + +static int kill_conn(struct rfcomm_dev_info *di, unsigned long arg) +{ + bdaddr_t *dst = (bdaddr_t *) arg; + pid_t pid; + + if (di->state == BT_CONNECTED && + (di->flags & (1<<RFCOMM_REUSE_DLC)) && + (di->flags & (1<<RFCOMM_TTY_ATTACHED)) && + (di->flags & (1<<RFCOMM_RELEASE_ONHUP))) { + + if (dst && bacmp(&di->dst, dst)) + return 0; + + if (find_pppd(di->id, &pid)) { + if (kill(pid, SIGINT) < 0) + perror("Kill"); + + if (!dst) + return 0; + return 1; + } + } + return 0; +} + +int dun_show_connections(void) +{ + for_each_port(show_conn, 0); + return 0; +} + +int dun_kill_connection(uint8_t *dst) +{ + for_each_port(kill_conn, (unsigned long) dst); + return 0; +} + +int dun_kill_all_connections(void) +{ + for_each_port(kill_conn, 0); + return 0; +} + +int dun_open_connection(int sk, char *pppd, char **args, int wait) +{ + char tty[100]; + int pid; + + if (dun_create_tty(sk, tty, sizeof(tty) - 1) < 0) { + syslog(LOG_ERR, "RFCOMM TTY creation failed. %s(%d)", strerror(errno), errno); + return -1; + } + + args[0] = "pppd"; + args[1] = tty; + args[2] = "nodetach"; + + pid = dun_exec(tty, pppd, args); + if (pid < 0) { + syslog(LOG_ERR, "Exec failed. %s(%d)", strerror(errno), errno); + return -1; + } + + if (wait) { + int status; + waitpid(pid, &status, 0); + /* FIXME: Check for waitpid errors */ + } + + return 0; +} |