rfcomm_sppd.c revision 114878
1114878Sjulian/* 2114878Sjulian * rfcomm_sppd.c 3114878Sjulian * 4114878Sjulian * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com> 5114878Sjulian * All rights reserved. 6114878Sjulian * 7114878Sjulian * Redistribution and use in source and binary forms, with or without 8114878Sjulian * modification, are permitted provided that the following conditions 9114878Sjulian * are met: 10114878Sjulian * 1. Redistributions of source code must retain the above copyright 11114878Sjulian * notice, this list of conditions and the following disclaimer. 12114878Sjulian * 2. Redistributions in binary form must reproduce the above copyright 13114878Sjulian * notice, this list of conditions and the following disclaimer in the 14114878Sjulian * documentation and/or other materials provided with the distribution. 15114878Sjulian * 16114878Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17114878Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18114878Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19114878Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20114878Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21114878Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22114878Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23114878Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24114878Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25114878Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26114878Sjulian * SUCH DAMAGE. 27114878Sjulian * 28114878Sjulian * $Id: rfcomm_sppd.c,v 1.2 2003/04/27 19:22:30 max Exp $ 29114878Sjulian * $FreeBSD: head/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c 114878 2003-05-10 21:44:42Z julian $ 30114878Sjulian */ 31114878Sjulian 32114878Sjulian#include <sys/types.h> 33114878Sjulian#include <sys/socket.h> 34114878Sjulian#include <sys/stat.h> 35114878Sjulian#include <bitstring.h> 36114878Sjulian#include <err.h> 37114878Sjulian#include <errno.h> 38114878Sjulian#include <fcntl.h> 39114878Sjulian#include <grp.h> 40114878Sjulian#include <limits.h> 41114878Sjulian#include <ng_hci.h> 42114878Sjulian#include <ng_l2cap.h> 43114878Sjulian#include <ng_btsocket.h> 44114878Sjulian#include <signal.h> 45114878Sjulian#include <stdarg.h> 46114878Sjulian#include <stdio.h> 47114878Sjulian#include <stdlib.h> 48114878Sjulian#include <string.h> 49114878Sjulian#include <syslog.h> 50114878Sjulian#include <termios.h> 51114878Sjulian#include <unistd.h> 52114878Sjulian 53114878Sjulian#define SPPD_IDENT "rfcomm_sppd" 54114878Sjulian#define SPPD_BUFFER_SIZE 1024 55114878Sjulian#define max(a, b) (((a) > (b))? (a) : (b)) 56114878Sjulian 57114878Sjulianstatic int sppd_ttys_open (char const *tty, int *amaster, int *aslave); 58114878Sjulianstatic int sppd_read (int fd, char *buffer, int size); 59114878Sjulianstatic int sppd_write (int fd, char *buffer, int size); 60114878Sjulianstatic void sppd_sighandler (int s); 61114878Sjulianstatic void usage (void); 62114878Sjulian 63114878Sjulianstatic int done; /* are we done? */ 64114878Sjulian 65114878Sjulian/* Main */ 66114878Sjulianint 67114878Sjulianmain(int argc, char *argv[]) 68114878Sjulian{ 69114878Sjulian struct sigaction sa; 70114878Sjulian struct sockaddr_rfcomm ra; 71114878Sjulian bdaddr_t addr; 72114878Sjulian int n, background, channel, s, amaster, aslave; 73114878Sjulian fd_set rfd; 74114878Sjulian char *tty = NULL, buf[SPPD_BUFFER_SIZE]; 75114878Sjulian 76114878Sjulian memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)); 77114878Sjulian background = channel = 0; 78114878Sjulian 79114878Sjulian /* Parse command line options */ 80114878Sjulian while ((n = getopt(argc, argv, "a:bc:t:h")) != -1) { 81114878Sjulian switch (n) { 82114878Sjulian case 'a': { /* BDADDR */ 83114878Sjulian int a0, a1, a2, a3, a4, a5; 84114878Sjulian 85114878Sjulian if (sscanf(optarg, "%x:%x:%x:%x:%x:%x", 86114878Sjulian &a5, &a4, &a3, &a2, &a1, &a0) != 6) 87114878Sjulian usage(); 88114878Sjulian /* NOT REACHED */ 89114878Sjulian 90114878Sjulian addr.b[0] = a0 & 0xff; 91114878Sjulian addr.b[1] = a1 & 0xff; 92114878Sjulian addr.b[2] = a2 & 0xff; 93114878Sjulian addr.b[3] = a3 & 0xff; 94114878Sjulian addr.b[4] = a4 & 0xff; 95114878Sjulian addr.b[5] = a5 & 0xff; 96114878Sjulian } break; 97114878Sjulian 98114878Sjulian case 'c': /* RFCOMM channel */ 99114878Sjulian channel = atoi(optarg); 100114878Sjulian break; 101114878Sjulian 102114878Sjulian case 'b': /* Run in background */ 103114878Sjulian background = 1; 104114878Sjulian break; 105114878Sjulian 106114878Sjulian case 't': /* Slave TTY name */ 107114878Sjulian tty = optarg; 108114878Sjulian break; 109114878Sjulian 110114878Sjulian case 'h': 111114878Sjulian default: 112114878Sjulian usage(); 113114878Sjulian /* NOT REACHED */ 114114878Sjulian } 115114878Sjulian } 116114878Sjulian 117114878Sjulian /* Check if we have everything we need */ 118114878Sjulian if (channel == 0 || tty == NULL || 119114878Sjulian memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0) 120114878Sjulian usage(); 121114878Sjulian /* NOT REACHED */ 122114878Sjulian 123114878Sjulian /* Set signal handlers */ 124114878Sjulian memset(&sa, 0, sizeof(sa)); 125114878Sjulian sa.sa_handler = sppd_sighandler; 126114878Sjulian 127114878Sjulian if (sigaction(SIGTERM, &sa, NULL) < 0) 128114878Sjulian err(1, "Could not sigaction(SIGTERM)"); 129114878Sjulian 130114878Sjulian if (sigaction(SIGHUP, &sa, NULL) < 0) 131114878Sjulian err(1, "Could not sigaction(SIGHUP)"); 132114878Sjulian 133114878Sjulian if (sigaction(SIGINT, &sa, NULL) < 0) 134114878Sjulian err(1, "Could not sigaction(SIGINT)"); 135114878Sjulian 136114878Sjulian sa.sa_handler = SIG_IGN; 137114878Sjulian sa.sa_flags = SA_NOCLDWAIT; 138114878Sjulian 139114878Sjulian if (sigaction(SIGCHLD, &sa, NULL) < 0) 140114878Sjulian err(1, "Could not sigaction(SIGCHLD)"); 141114878Sjulian 142114878Sjulian /* Open TTYs */ 143114878Sjulian if (sppd_ttys_open(tty, &amaster, &aslave) < 0) 144114878Sjulian exit(1); 145114878Sjulian 146114878Sjulian /* Open RFCOMM connection */ 147114878Sjulian memset(&ra, 0, sizeof(ra)); 148114878Sjulian ra.rfcomm_len = sizeof(ra); 149114878Sjulian ra.rfcomm_family = AF_BLUETOOTH; 150114878Sjulian 151114878Sjulian s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); 152114878Sjulian if (s < 0) 153114878Sjulian err(1, "Could not create socket"); 154114878Sjulian 155114878Sjulian if (bind(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) 156114878Sjulian err(1, "Could not bind socket"); 157114878Sjulian 158114878Sjulian memcpy(&ra.rfcomm_bdaddr, &addr, sizeof(ra.rfcomm_bdaddr)); 159114878Sjulian ra.rfcomm_channel = channel; 160114878Sjulian 161114878Sjulian if (connect(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) 162114878Sjulian err(1, "Could not connect socket"); 163114878Sjulian 164114878Sjulian /* Became daemon if required */ 165114878Sjulian if (background) { 166114878Sjulian switch (fork()) { 167114878Sjulian case -1: 168114878Sjulian err(1, "Could not fork()"); 169114878Sjulian /* NOT REACHED */ 170114878Sjulian 171114878Sjulian case 0: 172114878Sjulian exit(0); 173114878Sjulian /* NOT REACHED */ 174114878Sjulian 175114878Sjulian default: 176114878Sjulian if (daemon(0, 0) < 0) 177114878Sjulian err(1, "Could not daemon()"); 178114878Sjulian break; 179114878Sjulian } 180114878Sjulian } 181114878Sjulian 182114878Sjulian openlog(SPPD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON); 183114878Sjulian syslog(LOG_INFO, "Starting on %s...", tty); 184114878Sjulian 185114878Sjulian for (done = 0; !done; ) { 186114878Sjulian FD_ZERO(&rfd); 187114878Sjulian FD_SET(amaster, &rfd); 188114878Sjulian FD_SET(s, &rfd); 189114878Sjulian 190114878Sjulian n = select(max(amaster, s) + 1, &rfd, NULL, NULL, NULL); 191114878Sjulian if (n < 0) { 192114878Sjulian if (errno == EINTR) 193114878Sjulian continue; 194114878Sjulian 195114878Sjulian syslog(LOG_ERR, "Could not select(). %s", 196114878Sjulian strerror(errno)); 197114878Sjulian exit(1); 198114878Sjulian } 199114878Sjulian 200114878Sjulian if (n == 0) 201114878Sjulian continue; 202114878Sjulian 203114878Sjulian if (FD_ISSET(amaster, &rfd)) { 204114878Sjulian n = sppd_read(amaster, buf, sizeof(buf)); 205114878Sjulian if (n < 0) { 206114878Sjulian syslog(LOG_ERR, "Could not read master pty, " \ 207114878Sjulian "fd=%d. %s", amaster, strerror(errno)); 208114878Sjulian exit(1); 209114878Sjulian } 210114878Sjulian 211114878Sjulian if (n == 0) 212114878Sjulian break; /* XXX */ 213114878Sjulian 214114878Sjulian if (sppd_write(s, buf, n) < 0) { 215114878Sjulian syslog(LOG_ERR, "Could not write to socket, " \ 216114878Sjulian "fd=%d, size=%d. %s", 217114878Sjulian s, n, strerror(errno)); 218114878Sjulian exit(1); 219114878Sjulian } 220114878Sjulian } 221114878Sjulian 222114878Sjulian if (FD_ISSET(s, &rfd)) { 223114878Sjulian n = sppd_read(s, buf, sizeof(buf)); 224114878Sjulian if (n < 0) { 225114878Sjulian syslog(LOG_ERR, "Could not read socket, " \ 226114878Sjulian "fd=%d. %s", s, strerror(errno)); 227114878Sjulian exit(1); 228114878Sjulian } 229114878Sjulian 230114878Sjulian if (n == 0) 231114878Sjulian break; 232114878Sjulian 233114878Sjulian if (sppd_write(amaster, buf, n) < 0) { 234114878Sjulian syslog(LOG_ERR, "Could not write to master " \ 235114878Sjulian "pty, fd=%d, size=%d. %s", 236114878Sjulian amaster, n, strerror(errno)); 237114878Sjulian exit(1); 238114878Sjulian } 239114878Sjulian } 240114878Sjulian } 241114878Sjulian 242114878Sjulian syslog(LOG_INFO, "Completed on %s", tty); 243114878Sjulian closelog(); 244114878Sjulian 245114878Sjulian close(s); 246114878Sjulian close(aslave); 247114878Sjulian close(amaster); 248114878Sjulian 249114878Sjulian return (0); 250114878Sjulian} 251114878Sjulian 252114878Sjulian/* Open TTYs */ 253114878Sjulianstatic int 254114878Sjuliansppd_ttys_open(char const *tty, int *amaster, int *aslave) 255114878Sjulian{ 256114878Sjulian char pty[PATH_MAX]; 257114878Sjulian struct group *gr = NULL; 258114878Sjulian gid_t ttygid; 259114878Sjulian struct termios tio; 260114878Sjulian 261114878Sjulian /* 262114878Sjulian * Master PTY 263114878Sjulian */ 264114878Sjulian 265114878Sjulian strlcpy(pty, tty, sizeof(pty)); 266114878Sjulian pty[5] = 'p'; 267114878Sjulian 268114878Sjulian if (strcmp(pty, tty) == 0) { 269114878Sjulian syslog(LOG_ERR, "Master and slave tty are the same (%s)", tty); 270114878Sjulian return (-1); 271114878Sjulian } 272114878Sjulian 273114878Sjulian if ((*amaster = open(pty, O_RDWR, 0)) < 0) { 274114878Sjulian syslog(LOG_ERR, "Could not open(%s). %s", pty, strerror(errno)); 275114878Sjulian return (-1); 276114878Sjulian } 277114878Sjulian 278114878Sjulian /* 279114878Sjulian * Slave TTY 280114878Sjulian */ 281114878Sjulian 282114878Sjulian if ((gr = getgrnam("tty")) != NULL) 283114878Sjulian ttygid = gr->gr_gid; 284114878Sjulian else 285114878Sjulian ttygid = -1; 286114878Sjulian 287114878Sjulian (void) chown(tty, getuid(), ttygid); 288114878Sjulian (void) chmod(tty, S_IRUSR|S_IWUSR|S_IWGRP); 289114878Sjulian (void) revoke(tty); 290114878Sjulian 291114878Sjulian if ((*aslave = open(tty, O_RDWR, 0)) < 0) { 292114878Sjulian syslog(LOG_ERR, "Could not open(%s). %s", tty, strerror(errno)); 293114878Sjulian close(*amaster); 294114878Sjulian return (-1); 295114878Sjulian } 296114878Sjulian 297114878Sjulian /* 298114878Sjulian * Make slave TTY raw 299114878Sjulian */ 300114878Sjulian 301114878Sjulian cfmakeraw(&tio); 302114878Sjulian 303114878Sjulian if (tcsetattr(*aslave, TCSANOW, &tio) < 0) { 304114878Sjulian syslog(LOG_ERR, "Could not tcsetattr(). %s", strerror(errno)); 305114878Sjulian close(*aslave); 306114878Sjulian close(*amaster); 307114878Sjulian return (-1); 308114878Sjulian } 309114878Sjulian 310114878Sjulian return (0); 311114878Sjulian} /* sppd_ttys_open */ 312114878Sjulian 313114878Sjulian/* Read data */ 314114878Sjulianstatic int 315114878Sjuliansppd_read(int fd, char *buffer, int size) 316114878Sjulian{ 317114878Sjulian int n; 318114878Sjulian 319114878Sjulianagain: 320114878Sjulian n = read(fd, buffer, size); 321114878Sjulian if (n < 0) { 322114878Sjulian if (errno == EINTR) 323114878Sjulian goto again; 324114878Sjulian 325114878Sjulian return (-1); 326114878Sjulian } 327114878Sjulian 328114878Sjulian return (n); 329114878Sjulian} /* sppd_read */ 330114878Sjulian 331114878Sjulian/* Write data */ 332114878Sjulianstatic int 333114878Sjuliansppd_write(int fd, char *buffer, int size) 334114878Sjulian{ 335114878Sjulian int n, wrote; 336114878Sjulian 337114878Sjulian for (wrote = 0; size > 0; ) { 338114878Sjulian n = write(fd, buffer, size); 339114878Sjulian switch (n) { 340114878Sjulian case -1: 341114878Sjulian if (errno != EINTR) 342114878Sjulian return (-1); 343114878Sjulian break; 344114878Sjulian 345114878Sjulian case 0: 346114878Sjulian /* XXX can happen? */ 347114878Sjulian break; 348114878Sjulian 349114878Sjulian default: 350114878Sjulian wrote += n; 351114878Sjulian buffer += n; 352114878Sjulian size -= n; 353114878Sjulian break; 354114878Sjulian } 355114878Sjulian } 356114878Sjulian 357114878Sjulian return (wrote); 358114878Sjulian} /* sppd_write */ 359114878Sjulian 360114878Sjulian/* Signal handler */ 361114878Sjulianstatic void 362114878Sjuliansppd_sighandler(int s) 363114878Sjulian{ 364114878Sjulian syslog(LOG_INFO, "Signal %d received. Total %d signals received\n", 365114878Sjulian s, ++ done); 366114878Sjulian} /* sppd_sighandler */ 367114878Sjulian 368114878Sjulian/* Display usage and exit */ 369114878Sjulianstatic void 370114878Sjulianusage(void) 371114878Sjulian{ 372114878Sjulian fprintf(stdout, 373114878Sjulian"Usage: %s options\n" \ 374114878Sjulian"Where options are:\n" \ 375114878Sjulian"\t-a bdaddr BDADDR to connect to (required)\n" \ 376114878Sjulian"\t-b Run in background\n" \ 377114878Sjulian"\t-c channel RFCOMM channel to connect to (required)\n" \ 378114878Sjulian"\t-t tty TTY name\n" \ 379114878Sjulian"\t-h Display this message\n", SPPD_IDENT); 380114878Sjulian 381114878Sjulian exit(255); 382114878Sjulian} /* usage */ 383114878Sjulian 384