1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
6 *  Copyright (C) 2002-2009  Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
9 *  This program is free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <stdio.h>
30#include <errno.h>
31#include <ctype.h>
32#include <fcntl.h>
33#include <unistd.h>
34#include <stdlib.h>
35#include <syslog.h>
36#include <dirent.h>
37
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <sys/wait.h>
41#include <sys/param.h>
42#include <sys/ioctl.h>
43#include <sys/socket.h>
44
45#include <netinet/in.h>
46
47#include <bluetooth/bluetooth.h>
48#include <bluetooth/rfcomm.h>
49
50#include "dund.h"
51#include "lib.h"
52
53#define PROC_BASE  "/proc"
54
55static int for_each_port(int (*func)(struct rfcomm_dev_info *, unsigned long), unsigned long arg)
56{
57	struct rfcomm_dev_list_req *dl;
58	struct rfcomm_dev_info *di;
59	long r = 0;
60	int  sk, i;
61
62	sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
63	if (sk < 0 ) {
64		perror("Can't open RFCOMM control socket");
65		exit(1);
66	}
67
68	dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
69	if (!dl) {
70		perror("Can't allocate request memory");
71		close(sk);
72		exit(1);
73	}
74
75	dl->dev_num = RFCOMM_MAX_DEV;
76	di = dl->dev_info;
77
78	if (ioctl(sk, RFCOMMGETDEVLIST, (void *) dl) < 0) {
79		perror("Can't get device list");
80		exit(1);
81	}
82
83	for (i = 0; i < dl->dev_num; i++) {
84		r = func(di + i, arg);
85		if (r) break;
86	}
87
88	close(sk);
89	return r;
90}
91
92static int uses_rfcomm(char *path, char *dev)
93{
94	struct dirent *de;
95	DIR   *dir;
96
97	dir = opendir(path);
98	if (!dir)
99		return 0;
100
101	if (chdir(path) < 0)
102		return 0;
103
104	while ((de = readdir(dir)) != NULL) {
105		char link[PATH_MAX + 1];
106		int  len = readlink(de->d_name, link, sizeof(link));
107		if (len > 0) {
108			link[len] = 0;
109			if (strstr(link, dev))
110				return 1;
111		}
112	}
113
114	closedir(dir);
115
116	return 0;
117}
118
119static int find_pppd(int id, pid_t *pid)
120{
121	struct dirent *de;
122	char  path[PATH_MAX + 1];
123	char  dev[10];
124	int   empty = 1;
125	DIR   *dir;
126
127	dir = opendir(PROC_BASE);
128	if (!dir) {
129		perror(PROC_BASE);
130		return -1;
131	}
132
133	sprintf(dev, "rfcomm%d", id);
134
135	*pid = 0;
136	while ((de = readdir(dir)) != NULL) {
137		empty = 0;
138		if (isdigit(de->d_name[0])) {
139			sprintf(path, "%s/%s/fd", PROC_BASE, de->d_name);
140			if (uses_rfcomm(path, dev)) {
141				*pid = atoi(de->d_name);
142				break;
143			}
144		}
145	}
146	closedir(dir);
147
148	if (empty)
149		fprintf(stderr, "%s is empty (not mounted ?)\n", PROC_BASE);
150
151	return *pid != 0;
152}
153
154static int dun_exec(char *tty, char *prog, char **args)
155{
156	int pid = fork();
157	int fd;
158
159	switch (pid) {
160	case -1:
161		return -1;
162
163	case 0:
164		break;
165
166	default:
167		return pid;
168	}
169
170	setsid();
171
172	/* Close all FDs */
173	for (fd = 3; fd < 20; fd++)
174		close(fd);
175
176	execvp(prog, args);
177
178	syslog(LOG_ERR, "Error while executing %s", prog);
179
180	exit(1);
181}
182
183static int dun_create_tty(int sk, char *tty, int size)
184{
185	struct sockaddr_rc sa;
186	struct stat st;
187	socklen_t alen;
188	int id, try = 30;
189
190	struct rfcomm_dev_req req = {
191		flags:   (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
192		dev_id:  -1
193	};
194
195	alen = sizeof(sa);
196	if (getpeername(sk, (struct sockaddr *) &sa, &alen) < 0)
197		return -1;
198	bacpy(&req.dst, &sa.rc_bdaddr);
199
200	alen = sizeof(sa);
201	if (getsockname(sk, (struct sockaddr *) &sa, &alen) < 0)
202		return -1;
203	bacpy(&req.src, &sa.rc_bdaddr);
204	req.channel = sa.rc_channel;
205
206	id = ioctl(sk, RFCOMMCREATEDEV, &req);
207	if (id < 0)
208		return id;
209
210	snprintf(tty, size, "/dev/rfcomm%d", id);
211	while (stat(tty, &st) < 0) {
212		snprintf(tty, size, "/dev/bluetooth/rfcomm/%d", id);
213		if (stat(tty, &st) < 0) {
214			snprintf(tty, size, "/dev/rfcomm%d", id);
215			if (try--) {
216				usleep(100 * 1000);
217				continue;
218			}
219
220			memset(&req, 0, sizeof(req));
221			req.dev_id = id;
222			ioctl(sk, RFCOMMRELEASEDEV, &req);
223
224			return -1;
225		}
226	}
227
228	return id;
229}
230
231int dun_init(void)
232{
233	return 0;
234}
235
236int dun_cleanup(void)
237{
238	return 0;
239}
240
241static int show_conn(struct rfcomm_dev_info *di, unsigned long arg)
242{
243	pid_t pid;
244
245	if (di->state == BT_CONNECTED &&
246		(di->flags & (1<<RFCOMM_REUSE_DLC)) &&
247		(di->flags & (1<<RFCOMM_TTY_ATTACHED)) &&
248		(di->flags & (1<<RFCOMM_RELEASE_ONHUP))) {
249
250		if (find_pppd(di->id, &pid)) {
251			char dst[18];
252			ba2str(&di->dst, dst);
253
254			printf("rfcomm%d: %s channel %d pppd pid %d\n",
255					di->id, dst, di->channel, pid);
256		}
257	}
258	return 0;
259}
260
261static int kill_conn(struct rfcomm_dev_info *di, unsigned long arg)
262{
263	bdaddr_t *dst = (bdaddr_t *) arg;
264	pid_t pid;
265
266	if (di->state == BT_CONNECTED &&
267		(di->flags & (1<<RFCOMM_REUSE_DLC)) &&
268		(di->flags & (1<<RFCOMM_TTY_ATTACHED)) &&
269		(di->flags & (1<<RFCOMM_RELEASE_ONHUP))) {
270
271		if (dst && bacmp(&di->dst, dst))
272			return 0;
273
274		if (find_pppd(di->id, &pid)) {
275			if (kill(pid, SIGINT) < 0)
276				perror("Kill");
277
278			if (!dst)
279				return 0;
280			return 1;
281		}
282	}
283	return 0;
284}
285
286int dun_show_connections(void)
287{
288	for_each_port(show_conn, 0);
289	return 0;
290}
291
292int dun_kill_connection(uint8_t *dst)
293{
294	for_each_port(kill_conn, (unsigned long) dst);
295	return 0;
296}
297
298int dun_kill_all_connections(void)
299{
300	for_each_port(kill_conn, 0);
301	return 0;
302}
303
304int dun_open_connection(int sk, char *pppd, char **args, int wait)
305{
306	char tty[100];
307	int  pid;
308
309	if (dun_create_tty(sk, tty, sizeof(tty) - 1) < 0) {
310		syslog(LOG_ERR, "RFCOMM TTY creation failed. %s(%d)", strerror(errno), errno);
311		return -1;
312	}
313
314	args[0] = "pppd";
315	args[1] = tty;
316	args[2] = "nodetach";
317
318	pid = dun_exec(tty, pppd, args);
319	if (pid < 0) {
320		syslog(LOG_ERR, "Exec failed. %s(%d)", strerror(errno), errno);
321		return -1;
322	}
323
324	if (wait) {
325		int status;
326		waitpid(pid, &status, 0);
327		/* FIXME: Check for waitpid errors */
328	}
329
330	return 0;
331}
332