1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
6 *
7 *
8 *  This program is free software; you can redistribute it and/or modify
9 *  it under the terms of the GNU General Public License as published by
10 *  the Free Software Foundation; either version 2 of the License, or
11 *  (at your option) any later version.
12 *
13 *  This program is distributed in the hope that it will be useful,
14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *  GNU General Public License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with this program; if not, write to the Free Software
20 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <stdio.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <unistd.h>
32#include <stdlib.h>
33#include <signal.h>
34#include <syslog.h>
35#include <getopt.h>
36#include <sys/poll.h>
37#include <sys/ioctl.h>
38#include <sys/socket.h>
39
40#include <bluetooth/bluetooth.h>
41#include <bluetooth/rfcomm.h>
42
43/* IO cancelation */
44static volatile sig_atomic_t __io_canceled;
45
46static inline void io_init(void)
47{
48	__io_canceled = 0;
49}
50
51static inline void io_cancel(void)
52{
53	__io_canceled = 1;
54}
55
56/* Signal functions */
57static void sig_hup(int sig)
58{
59	return;
60}
61
62static void sig_term(int sig)
63{
64	syslog(LOG_INFO, "Closing RFCOMM channel");
65	io_cancel();
66}
67
68/* Read exactly len bytes (Signal safe)*/
69static inline int read_n(int fd, char *buf, int len)
70{
71	register int t = 0, w;
72
73	while (!__io_canceled && len > 0) {
74		if ((w = read(fd, buf, len)) < 0) {
75			if (errno == EINTR || errno == EAGAIN)
76				continue;
77			return -1;
78		}
79		if (!w)
80			return 0;
81		len -= w;
82		buf += w;
83		t += w;
84	}
85
86	return t;
87}
88
89/* Write exactly len bytes (Signal safe)*/
90static inline int write_n(int fd, char *buf, int len)
91{
92	register int t = 0, w;
93
94	while (!__io_canceled && len > 0) {
95		if ((w = write(fd, buf, len)) < 0) {
96			if (errno == EINTR || errno == EAGAIN)
97				continue;
98			return -1;
99		}
100		if (!w)
101			return 0;
102		len -= w;
103		buf += w;
104		t += w;
105	}
106
107	return t;
108}
109
110/* Create the RFCOMM connection */
111static int create_connection(bdaddr_t *bdaddr, uint8_t channel)
112{
113	struct sockaddr_rc remote_addr, local_addr;
114	int fd, err;
115
116	if ((fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0)
117		return fd;
118
119	memset(&local_addr, 0, sizeof(local_addr));
120	local_addr.rc_family = AF_BLUETOOTH;
121	bacpy(&local_addr.rc_bdaddr, BDADDR_ANY);
122	if ((err = bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr))) < 0) {
123		close(fd);
124		return err;
125	}
126
127	memset(&remote_addr, 0, sizeof(remote_addr));
128	remote_addr.rc_family = AF_BLUETOOTH;
129	bacpy(&remote_addr.rc_bdaddr, bdaddr);
130	remote_addr.rc_channel = channel;
131	if ((err = connect(fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr))) < 0) {
132		close(fd);
133		return err;
134	}
135
136	syslog(LOG_INFO, "RFCOMM channel %d connected", channel);
137
138	return fd;
139}
140
141/* Process the data from socket and pseudo tty */
142static int process_data(int fd)
143{
144	struct pollfd p[2];
145	char buf[1024];
146	int err, r;
147
148	p[0].fd = 0;
149	p[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
150
151	p[1].fd = fd;
152	p[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
153
154	err = 0;
155
156	while (!__io_canceled) {
157		p[0].revents = 0;
158		p[1].revents = 0;
159
160		err = poll(p, 2, -1);
161		if (err < 0)
162			break;
163
164		err = 0;
165
166		if (p[0].revents) {
167			if (p[0].revents & (POLLERR | POLLHUP | POLLNVAL))
168			  break;
169			r = read(0, buf, sizeof(buf));
170			if (r < 0) {
171				if (errno != EINTR && errno != EAGAIN) {
172					err = r;
173					break;
174				}
175			}
176
177			err = write_n(fd, buf, r);
178			if (err < 0)
179				break;
180		}
181
182		if (p[1].revents) {
183			if (p[1].revents & (POLLERR | POLLHUP | POLLNVAL))
184				break;
185			r = read(fd, buf, sizeof(buf));
186			if (r < 0) {
187				if (errno != EINTR && errno != EAGAIN) {
188					err = r;
189					break;
190				}
191			}
192
193			err = write_n(1, buf, r);
194			if (err < 0)
195				break;
196		}
197	}
198
199	return err;
200}
201
202static void usage(void)
203{
204	printf("Usage:\tppporc <bdaddr> [channel]\n");
205}
206
207int main(int argc, char** argv)
208{
209	struct sigaction sa;
210	int fd, err, opt;
211
212	bdaddr_t bdaddr;
213	uint8_t channel;
214
215	/* Parse command line options */
216	while ((opt = getopt(argc, argv, "h")) != EOF) {
217		switch(opt) {
218		case 'h':
219			usage();
220			exit(0);
221		}
222	}
223
224	argc -= optind;
225	argv += optind;
226
227	switch (argc) {
228	case 1:
229		str2ba(argv[0], &bdaddr);
230		channel = 1;
231		break;
232	case 2:
233		str2ba(argv[0], &bdaddr);
234		channel = atoi(argv[1]);
235		break;
236	default:
237		usage();
238		exit(0);
239	}
240
241	/* Initialize syslog */
242	openlog("ppporc", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
243	syslog(LOG_INFO, "PPP over RFCOMM");
244
245	/* Initialize signals */
246	memset(&sa, 0, sizeof(sa));
247	sa.sa_flags   = SA_NOCLDSTOP;
248	sa.sa_handler = SIG_IGN;
249	sigaction(SIGCHLD, &sa, NULL);
250	sigaction(SIGPIPE, &sa, NULL);
251
252	sa.sa_handler = sig_term;
253	sigaction(SIGTERM, &sa, NULL);
254	sigaction(SIGINT,  &sa, NULL);
255
256	sa.sa_handler = sig_hup;
257	sigaction(SIGHUP, &sa, NULL);
258
259	syslog(LOG_INFO, "Connecting to %s", argv[0]);
260
261	if ((fd = create_connection(&bdaddr, channel)) < 0) {
262		syslog(LOG_ERR, "Can't connect to remote device (%s)", strerror(errno));
263		return fd;
264	}
265
266	err = process_data(fd);
267
268	close(fd);
269
270	return err;
271}
272