Home | History | Annotate | Line # | Download | only in rfcomm_sppd
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