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; +} | 
