rfcomm_sppd.c revision 1.1 1 /* $NetBSD: rfcomm_sppd.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $ */
2
3 /*-
4 * Copyright (c) 2006 Itronix Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of Itronix Inc. may not be used to endorse
16 * or promote products derived from this software without specific
17 * prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 /*
32 * rfcomm_sppd.c
33 *
34 * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>
35 * All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 *
58 * $Id: rfcomm_sppd.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $
59 * $FreeBSD: src/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c,v 1.8 2005/12/07 19:41:58 emax Exp $
60 */
61
62 #include <sys/cdefs.h>
63 __COPYRIGHT("@(#) Copyright (c) 2006 Itronix, Inc.\n"
64 "@(#) Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>\n"
65 "All rights reserved.\n");
66 __RCSID("$NetBSD: rfcomm_sppd.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $");
67
68 #include <bluetooth.h>
69 #include <ctype.h>
70 #include <err.h>
71 #include <errno.h>
72 #include <fcntl.h>
73 #include <grp.h>
74 #include <limits.h>
75 #include <paths.h>
76 #include <sdp.h>
77 #include <signal.h>
78 #include <stdarg.h>
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <string.h>
82 #include <syslog.h>
83 #include <termios.h>
84 #include <unistd.h>
85
86 #include "rfcomm_sdp.h"
87
88 #define SPPD_IDENT "rfcomm_sppd"
89 #define SPPD_BUFFER_SIZE 1024
90 #define max(a, b) (((a) > (b))? (a) : (b))
91
92 static int sppd_ttys_open (char const *tty, int *amaster, int *aslave);
93 static int sppd_read (int fd, char *buffer, size_t size);
94 static int sppd_write (int fd, char *buffer, size_t size);
95 static void sppd_sighandler (int s);
96 static void usage (void);
97
98 static int done; /* are we done? */
99
100 /* Main */
101 int
102 main(int argc, char *argv[])
103 {
104 struct sigaction sa;
105 struct sockaddr_bt ra;
106 bdaddr_t laddr, raddr;
107 uint8_t channel;
108 int n, background, service,
109 s, amaster, aslave, fd;
110 fd_set rfd;
111 char *tty = NULL, *ep = NULL, buf[SPPD_BUFFER_SIZE];
112
113 bdaddr_copy(&laddr, BDADDR_ANY);
114 bdaddr_copy(&raddr, BDADDR_ANY);
115 background = channel = 0;
116 service = SDP_SERVICE_CLASS_SERIAL_PORT;
117
118 /* Parse command line options */
119 while ((n = getopt(argc, argv, "a:bc:d:t:h")) != -1) {
120 switch (n) {
121 case 'a': /* BDADDR */
122 if (!bt_aton(optarg, &raddr)) {
123 struct hostent *he = NULL;
124
125 if ((he = bt_gethostbyname(optarg)) == NULL)
126 errx(EXIT_FAILURE,
127 "%s: %s", optarg, hstrerror(h_errno));
128
129 bdaddr_copy(&raddr, (bdaddr_t *)he->h_addr);
130 }
131 break;
132
133 case 'c': /* RFCOMM channel */
134 channel = strtoul(optarg, &ep, 10);
135 if (*ep != '\0') {
136 channel = 0;
137 switch (tolower((int)optarg[0])) {
138 case 'd': /* DialUp Networking */
139 service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
140 break;
141
142 case 'f': /* Fax */
143 service = SDP_SERVICE_CLASS_FAX;
144 break;
145
146 case 'l': /* LAN */
147 service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
148 break;
149
150 case 's': /* Serial Port */
151 service = SDP_SERVICE_CLASS_SERIAL_PORT;
152 break;
153
154 default:
155 errx(EXIT_FAILURE, "Unknown service name: %s",
156 optarg);
157 /* NOT REACHED */
158 }
159 }
160 break;
161
162 case 'b': /* Run in background */
163 background = 1;
164 break;
165
166 case 'd': /* device */
167 if (!bt_devaddr(optarg, &laddr))
168 err(EXIT_FAILURE, "%s", optarg);
169 break;
170
171 case 't': /* Slave TTY name */
172 if (optarg[0] != '/')
173 asprintf(&tty, "%s%s", _PATH_DEV, optarg);
174 else
175 tty = optarg;
176 break;
177
178 case 'h':
179 default:
180 usage();
181 /* NOT REACHED */
182 }
183 }
184
185 /* Check if we have everything we need */
186 if (bdaddr_any(&raddr))
187 usage();
188 /* NOT REACHED */
189
190 /* Set signal handlers */
191 memset(&sa, 0, sizeof(sa));
192 sa.sa_handler = sppd_sighandler;
193
194 if (sigaction(SIGTERM, &sa, NULL) < 0)
195 err(EXIT_FAILURE, "Could not sigaction(SIGTERM)");
196
197 if (sigaction(SIGHUP, &sa, NULL) < 0)
198 err(EXIT_FAILURE, "Could not sigaction(SIGHUP)");
199
200 if (sigaction(SIGINT, &sa, NULL) < 0)
201 err(EXIT_FAILURE, "Could not sigaction(SIGINT)");
202
203 sa.sa_handler = SIG_IGN;
204 sa.sa_flags = SA_NOCLDWAIT;
205
206 if (sigaction(SIGCHLD, &sa, NULL) < 0)
207 err(EXIT_FAILURE, "Could not sigaction(SIGCHLD)");
208
209 /* Check channel, if was not set then obtain it via SDP */
210 if (channel == 0 && service != 0)
211 if (rfcomm_channel_lookup(&laddr, &raddr,
212 service, &channel, &n) != 0)
213 errx(EXIT_FAILURE,
214 "Could not obtain RFCOMM channel: %s",
215 strerror(n));
216
217 if (channel < 1 || channel > 30)
218 errx(EXIT_FAILURE,
219 "Invalid RFCOMM channel number %d", channel);
220
221 /* Open TTYs */
222 if (tty == NULL) {
223 if (background)
224 usage();
225
226 aslave = 0;
227 amaster = STDIN_FILENO;
228 fd = STDOUT_FILENO;
229 } else {
230 if (sppd_ttys_open(tty, &amaster, &aslave) < 0)
231 exit(EXIT_FAILURE);
232
233 fd = amaster;
234 }
235
236 /* Open RFCOMM connection */
237 memset(&ra, 0, sizeof(ra));
238 ra.bt_len = sizeof(ra);
239 ra.bt_family = AF_BLUETOOTH;
240
241 s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
242 if (s < 0)
243 err(EXIT_FAILURE, "Could not create socket");
244
245 bdaddr_copy(&ra.bt_bdaddr, &laddr);
246 if (bind(s, (struct sockaddr *) &ra, sizeof(ra)) < 0)
247 err(EXIT_FAILURE, "Could not bind socket");
248
249 ra.bt_channel = channel;
250 bdaddr_copy(&ra.bt_bdaddr, &raddr);
251
252 if (connect(s, (struct sockaddr *) &ra, sizeof(ra)) < 0)
253 err(EXIT_FAILURE, "Could not connect socket");
254
255 /* Became daemon if required */
256 if (background) {
257 switch (fork()) {
258 case -1:
259 err(EXIT_FAILURE, "Could not fork()");
260 /* NOT REACHED */
261
262 case 0:
263 exit(EXIT_SUCCESS);
264 /* NOT REACHED */
265
266 default:
267 if (daemon(0, 0) < 0)
268 err(EXIT_FAILURE, "Could not daemon()");
269 break;
270 }
271 }
272
273 openlog(SPPD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON);
274 syslog(LOG_INFO, "Starting on %s...",
275 (tty != NULL) ? tty : "stdin/stdout");
276
277 for (done = 0; !done; ) {
278 FD_ZERO(&rfd);
279 FD_SET(amaster, &rfd);
280 FD_SET(s, &rfd);
281
282 n = select(max(amaster, s) + 1, &rfd, NULL, NULL, NULL);
283 if (n < 0) {
284 if (errno == EINTR)
285 continue;
286
287 syslog(LOG_ERR, "Could not select(). %s",
288 strerror(errno));
289 exit(EXIT_FAILURE);
290 }
291
292 if (n == 0)
293 continue;
294
295 if (FD_ISSET(amaster, &rfd)) {
296 n = sppd_read(amaster, buf, sizeof(buf));
297 if (n < 0) {
298 syslog(LOG_ERR, "Could not read master pty, "
299 "fd=%d. %s", amaster, strerror(errno));
300 exit(EXIT_FAILURE);
301 }
302
303 if (n == 0)
304 break; /* XXX */
305
306 if (sppd_write(s, buf, (size_t)n) < 0) {
307 syslog(LOG_ERR, "Could not write to socket, "
308 "fd=%d, size=%d. %s",
309 s, n, strerror(errno));
310 exit(EXIT_FAILURE);
311 }
312 }
313
314 if (FD_ISSET(s, &rfd)) {
315 n = sppd_read(s, buf, sizeof(buf));
316 if (n < 0) {
317 syslog(LOG_ERR, "Could not read socket, " \
318 "fd=%d. %s", s, strerror(errno));
319 exit(EXIT_FAILURE);
320 }
321
322 if (n == 0)
323 break;
324
325 if (sppd_write(fd, buf, (size_t)n) < 0) {
326 syslog(LOG_ERR, "Could not write to master " \
327 "pty, fd=%d, size=%d. %s",
328 fd, n, strerror(errno));
329 exit(EXIT_FAILURE);
330 }
331 }
332 }
333
334 syslog(LOG_INFO, "Completed on %s", (tty != NULL)? tty : "stdin/stdout");
335 closelog();
336
337 close(s);
338
339 if (tty != NULL) {
340 close(aslave);
341 close(amaster);
342 }
343
344 return (0);
345 }
346
347 /* Open TTYs */
348 static int
349 sppd_ttys_open(char const *tty, int *amaster, int *aslave)
350 {
351 char pty[PATH_MAX], *slash;
352 struct group *gr = NULL;
353 gid_t ttygid;
354 struct termios tio;
355
356 /*
357 * Construct master PTY name. The slave tty name must be less then
358 * PATH_MAX characters in length, must contain '/' character and
359 * must not end with '/'.
360 */
361
362 if (strlen(tty) >= sizeof(pty)) {
363 syslog(LOG_ERR, "Slave tty name is too long");
364 return (-1);
365 }
366
367 strlcpy(pty, tty, sizeof(pty));
368 slash = strrchr(pty, '/');
369 if (slash == NULL || slash[1] == '\0') {
370 syslog(LOG_ERR, "Invalid slave tty name (%s)", tty);
371 return (-1);
372 }
373
374 slash[1] = 'p';
375
376 if (strcmp(pty, tty) == 0) {
377 syslog(LOG_ERR, "Master and slave tty are the same (%s)", tty);
378 return (-1);
379 }
380
381 if ((*amaster = open(pty, O_RDWR, 0)) < 0) {
382 syslog(LOG_ERR, "Could not open(%s). %s", pty, strerror(errno));
383 return (-1);
384 }
385
386 /*
387 * Slave TTY
388 */
389
390 if ((gr = getgrnam("tty")) != NULL)
391 ttygid = gr->gr_gid;
392 else
393 ttygid = (gid_t)-1;
394
395 (void) chown(tty, getuid(), ttygid);
396 (void) chmod(tty, S_IRUSR|S_IWUSR|S_IWGRP);
397 (void) revoke(tty);
398
399 if ((*aslave = open(tty, O_RDWR, 0)) < 0) {
400 syslog(LOG_ERR, "Could not open(%s). %s", tty, strerror(errno));
401 close(*amaster);
402 return (-1);
403 }
404
405 /*
406 * Make slave TTY raw
407 */
408
409 cfmakeraw(&tio);
410
411 if (tcsetattr(*aslave, TCSANOW, &tio) < 0) {
412 syslog(LOG_ERR, "Could not tcsetattr(). %s", strerror(errno));
413 close(*aslave);
414 close(*amaster);
415 return (-1);
416 }
417
418 return (0);
419 } /* sppd_ttys_open */
420
421 /* Read data */
422 static int
423 sppd_read(int fd, char *buffer, size_t size)
424 {
425 int n;
426
427 again:
428 n = read(fd, buffer, size);
429 if (n < 0) {
430 if (errno == EINTR)
431 goto again;
432
433 return (-1);
434 }
435
436 return (n);
437 } /* sppd_read */
438
439 /* Write data */
440 static int
441 sppd_write(int fd, char *buffer, size_t size)
442 {
443 int n, wrote;
444
445 for (wrote = 0; size > 0; ) {
446 n = write(fd, buffer, size);
447 switch (n) {
448 case -1:
449 if (errno != EINTR)
450 return (-1);
451 break;
452
453 case 0:
454 /* XXX can happen? */
455 break;
456
457 default:
458 wrote += n;
459 buffer += n;
460 size -= n;
461 break;
462 }
463 }
464
465 return (wrote);
466 } /* sppd_write */
467
468 /* Signal handler */
469 static void
470 sppd_sighandler(int s)
471 {
472 syslog(LOG_INFO, "Signal %d received. Total %d signals received\n",
473 s, ++ done);
474 } /* sppd_sighandler */
475
476 /* Display usage and exit */
477 static void
478 usage(void)
479 {
480 fprintf(stdout,
481 "Usage: %s options\n" \
482 "Where options are:\n" \
483 "\t-a address Address to connect to (required)\n" \
484 "\t-b Run in background\n" \
485 "\t-c channel RFCOMM channel to connect to\n" \
486 "\t-d device Device to connect from\n" \
487 "\t-t tty TTY name (required in background mode)\n" \
488 "\t-h Display this message\n", SPPD_IDENT);
489
490 exit(EXIT_FAILURE);
491 } /* usage */
492