diff options
Diffstat (limited to 'dund/dun.c')
| -rw-r--r-- | dund/dun.c | 308 | 
1 files changed, 308 insertions, 0 deletions
| diff --git a/dund/dun.c b/dund/dun.c new file mode 100644 index 00000000..8ef72ce6 --- /dev/null +++ b/dund/dun.c @@ -0,0 +1,308 @@ +/* +  dund - Bluetooth LAN/DUN daemon for BlueZ +  Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> +	 +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License, version 2, as +  published by the Free Software Foundation. + +  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA +*/ + +/* + * $Id$ + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <syslog.h> +#include <dirent.h> +#include <ctype.h> + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <sys/wait.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; +	chdir(path); + +	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); +	exit(1); +} + +static int dun_create_tty(int sk, char *tty, int size) +{ +	struct sockaddr_rc sa; +	struct stat st; +	int id, alen; + +	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); +	if (stat(tty, &st) < 0) { +		snprintf(tty, size, "/dev/bluetooth/rfcomm/%d", id); +		if (stat(tty, &st) < 0) { +			snprintf(tty, size, "/dev/rfcomm%d", id); +			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; +} | 
