rfcomm_sppd.c revision 114878
1238106Sdes/* 2238106Sdes * rfcomm_sppd.c 3238106Sdes * 4238106Sdes * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com> 5238106Sdes * All rights reserved. 6238106Sdes * 7238106Sdes * Redistribution and use in source and binary forms, with or without 8238106Sdes * modification, are permitted provided that the following conditions 9238106Sdes * are met: 10238106Sdes * 1. Redistributions of source code must retain the above copyright 11238106Sdes * notice, this list of conditions and the following disclaimer. 12238106Sdes * 2. Redistributions in binary form must reproduce the above copyright 13238106Sdes * notice, this list of conditions and the following disclaimer in the 14238106Sdes * documentation and/or other materials provided with the distribution. 15238106Sdes * 16238106Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17238106Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18238106Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19238106Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20238106Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21238106Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22238106Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23238106Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24238106Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25238106Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26238106Sdes * SUCH DAMAGE. 27238106Sdes * 28238106Sdes * $Id: rfcomm_sppd.c,v 1.2 2003/04/27 19:22:30 max Exp $ 29238106Sdes * $FreeBSD: head/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c 114878 2003-05-10 21:44:42Z julian $ 30238106Sdes */ 31238106Sdes 32238106Sdes#include <sys/types.h> 33238106Sdes#include <sys/socket.h> 34238106Sdes#include <sys/stat.h> 35238106Sdes#include <bitstring.h> 36238106Sdes#include <err.h> 37238106Sdes#include <errno.h> 38238106Sdes#include <fcntl.h> 39238106Sdes#include <grp.h> 40238106Sdes#include <limits.h> 41238106Sdes#include <ng_hci.h> 42238106Sdes#include <ng_l2cap.h> 43238106Sdes#include <ng_btsocket.h> 44238106Sdes#include <signal.h> 45238106Sdes#include <stdarg.h> 46238106Sdes#include <stdio.h> 47238106Sdes#include <stdlib.h> 48238106Sdes#include <string.h> 49238106Sdes#include <syslog.h> 50238106Sdes#include <termios.h> 51238106Sdes#include <unistd.h> 52238106Sdes 53238106Sdes#define SPPD_IDENT "rfcomm_sppd" 54238106Sdes#define SPPD_BUFFER_SIZE 1024 55238106Sdes#define max(a, b) (((a) > (b))? (a) : (b)) 56238106Sdes 57238106Sdesstatic int sppd_ttys_open (char const *tty, int *amaster, int *aslave); 58238106Sdesstatic int sppd_read (int fd, char *buffer, int size); 59238106Sdesstatic int sppd_write (int fd, char *buffer, int size); 60238106Sdesstatic void sppd_sighandler (int s); 61238106Sdesstatic void usage (void); 62238106Sdes 63238106Sdesstatic int done; /* are we done? */ 64238106Sdes 65238106Sdes/* Main */ 66238106Sdesint 67238106Sdesmain(int argc, char *argv[]) 68238106Sdes{ 69238106Sdes struct sigaction sa; 70238106Sdes struct sockaddr_rfcomm ra; 71238106Sdes bdaddr_t addr; 72238106Sdes int n, background, channel, s, amaster, aslave; 73238106Sdes fd_set rfd; 74238106Sdes char *tty = NULL, buf[SPPD_BUFFER_SIZE]; 75238106Sdes 76238106Sdes memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)); 77238106Sdes background = channel = 0; 78238106Sdes 79238106Sdes /* Parse command line options */ 80238106Sdes while ((n = getopt(argc, argv, "a:bc:t:h")) != -1) { 81238106Sdes switch (n) { 82238106Sdes case 'a': { /* BDADDR */ 83238106Sdes int a0, a1, a2, a3, a4, a5; 84238106Sdes 85238106Sdes if (sscanf(optarg, "%x:%x:%x:%x:%x:%x", 86238106Sdes &a5, &a4, &a3, &a2, &a1, &a0) != 6) 87238106Sdes usage(); 88238106Sdes /* NOT REACHED */ 89238106Sdes 90238106Sdes addr.b[0] = a0 & 0xff; 91238106Sdes addr.b[1] = a1 & 0xff; 92238106Sdes addr.b[2] = a2 & 0xff; 93238106Sdes addr.b[3] = a3 & 0xff; 94238106Sdes addr.b[4] = a4 & 0xff; 95238106Sdes addr.b[5] = a5 & 0xff; 96238106Sdes } break; 97238106Sdes 98238106Sdes case 'c': /* RFCOMM channel */ 99238106Sdes channel = atoi(optarg); 100238106Sdes break; 101238106Sdes 102238106Sdes case 'b': /* Run in background */ 103238106Sdes background = 1; 104238106Sdes break; 105238106Sdes 106238106Sdes case 't': /* Slave TTY name */ 107238106Sdes tty = optarg; 108238106Sdes break; 109238106Sdes 110238106Sdes case 'h': 111238106Sdes default: 112238106Sdes usage(); 113238106Sdes /* NOT REACHED */ 114238106Sdes } 115238106Sdes } 116238106Sdes 117238106Sdes /* Check if we have everything we need */ 118238106Sdes if (channel == 0 || tty == NULL || 119238106Sdes memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0) 120238106Sdes usage(); 121238106Sdes /* NOT REACHED */ 122238106Sdes 123238106Sdes /* Set signal handlers */ 124238106Sdes memset(&sa, 0, sizeof(sa)); 125238106Sdes sa.sa_handler = sppd_sighandler; 126238106Sdes 127238106Sdes if (sigaction(SIGTERM, &sa, NULL) < 0) 128238106Sdes err(1, "Could not sigaction(SIGTERM)"); 129238106Sdes 130238106Sdes if (sigaction(SIGHUP, &sa, NULL) < 0) 131238106Sdes err(1, "Could not sigaction(SIGHUP)"); 132238106Sdes 133238106Sdes if (sigaction(SIGINT, &sa, NULL) < 0) 134238106Sdes err(1, "Could not sigaction(SIGINT)"); 135238106Sdes 136238106Sdes sa.sa_handler = SIG_IGN; 137238106Sdes sa.sa_flags = SA_NOCLDWAIT; 138238106Sdes 139238106Sdes if (sigaction(SIGCHLD, &sa, NULL) < 0) 140238106Sdes err(1, "Could not sigaction(SIGCHLD)"); 141238106Sdes 142238106Sdes /* Open TTYs */ 143238106Sdes if (sppd_ttys_open(tty, &amaster, &aslave) < 0) 144238106Sdes exit(1); 145238106Sdes 146238106Sdes /* Open RFCOMM connection */ 147238106Sdes memset(&ra, 0, sizeof(ra)); 148238106Sdes ra.rfcomm_len = sizeof(ra); 149238106Sdes ra.rfcomm_family = AF_BLUETOOTH; 150238106Sdes 151238106Sdes s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); 152238106Sdes if (s < 0) 153238106Sdes err(1, "Could not create socket"); 154238106Sdes 155238106Sdes if (bind(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) 156238106Sdes err(1, "Could not bind socket"); 157238106Sdes 158238106Sdes memcpy(&ra.rfcomm_bdaddr, &addr, sizeof(ra.rfcomm_bdaddr)); 159238106Sdes ra.rfcomm_channel = channel; 160238106Sdes 161238106Sdes if (connect(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) 162238106Sdes err(1, "Could not connect socket"); 163238106Sdes 164238106Sdes /* Became daemon if required */ 165238106Sdes if (background) { 166238106Sdes switch (fork()) { 167238106Sdes case -1: 168238106Sdes err(1, "Could not fork()"); 169238106Sdes /* NOT REACHED */ 170238106Sdes 171238106Sdes case 0: 172238106Sdes exit(0); 173238106Sdes /* NOT REACHED */ 174238106Sdes 175238106Sdes default: 176238106Sdes if (daemon(0, 0) < 0) 177238106Sdes err(1, "Could not daemon()"); 178238106Sdes break; 179238106Sdes } 180238106Sdes } 181238106Sdes 182238106Sdes openlog(SPPD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON); 183238106Sdes syslog(LOG_INFO, "Starting on %s...", tty); 184238106Sdes 185238106Sdes for (done = 0; !done; ) { 186238106Sdes FD_ZERO(&rfd); 187238106Sdes FD_SET(amaster, &rfd); 188238106Sdes FD_SET(s, &rfd); 189238106Sdes 190238106Sdes n = select(max(amaster, s) + 1, &rfd, NULL, NULL, NULL); 191238106Sdes if (n < 0) { 192238106Sdes if (errno == EINTR) 193238106Sdes continue; 194238106Sdes 195238106Sdes syslog(LOG_ERR, "Could not select(). %s", 196238106Sdes strerror(errno)); 197238106Sdes exit(1); 198238106Sdes } 199238106Sdes 200238106Sdes if (n == 0) 201238106Sdes continue; 202238106Sdes 203238106Sdes if (FD_ISSET(amaster, &rfd)) { 204238106Sdes n = sppd_read(amaster, buf, sizeof(buf)); 205238106Sdes if (n < 0) { 206238106Sdes syslog(LOG_ERR, "Could not read master pty, " \ 207238106Sdes "fd=%d. %s", amaster, strerror(errno)); 208238106Sdes exit(1); 209238106Sdes } 210238106Sdes 211238106Sdes if (n == 0) 212238106Sdes break; /* XXX */ 213238106Sdes 214238106Sdes if (sppd_write(s, buf, n) < 0) { 215238106Sdes syslog(LOG_ERR, "Could not write to socket, " \ 216238106Sdes "fd=%d, size=%d. %s", 217238106Sdes s, n, strerror(errno)); 218238106Sdes exit(1); 219238106Sdes } 220238106Sdes } 221238106Sdes 222238106Sdes if (FD_ISSET(s, &rfd)) { 223238106Sdes n = sppd_read(s, buf, sizeof(buf)); 224238106Sdes if (n < 0) { 225238106Sdes syslog(LOG_ERR, "Could not read socket, " \ 226238106Sdes "fd=%d. %s", s, strerror(errno)); 227238106Sdes exit(1); 228238106Sdes } 229238106Sdes 230238106Sdes if (n == 0) 231238106Sdes break; 232238106Sdes 233238106Sdes if (sppd_write(amaster, buf, n) < 0) { 234238106Sdes syslog(LOG_ERR, "Could not write to master " \ 235238106Sdes "pty, fd=%d, size=%d. %s", 236238106Sdes amaster, n, strerror(errno)); 237238106Sdes exit(1); 238238106Sdes } 239238106Sdes } 240238106Sdes } 241238106Sdes 242238106Sdes syslog(LOG_INFO, "Completed on %s", tty); 243238106Sdes closelog(); 244238106Sdes 245238106Sdes close(s); 246238106Sdes close(aslave); 247238106Sdes close(amaster); 248238106Sdes 249238106Sdes return (0); 250238106Sdes} 251238106Sdes 252238106Sdes/* Open TTYs */ 253238106Sdesstatic int 254238106Sdessppd_ttys_open(char const *tty, int *amaster, int *aslave) 255238106Sdes{ 256238106Sdes char pty[PATH_MAX]; 257238106Sdes struct group *gr = NULL; 258238106Sdes gid_t ttygid; 259238106Sdes struct termios tio; 260238106Sdes 261238106Sdes /* 262238106Sdes * Master PTY 263238106Sdes */ 264238106Sdes 265238106Sdes strlcpy(pty, tty, sizeof(pty)); 266238106Sdes pty[5] = 'p'; 267238106Sdes 268238106Sdes if (strcmp(pty, tty) == 0) { 269238106Sdes syslog(LOG_ERR, "Master and slave tty are the same (%s)", tty); 270238106Sdes return (-1); 271238106Sdes } 272238106Sdes 273238106Sdes if ((*amaster = open(pty, O_RDWR, 0)) < 0) { 274238106Sdes syslog(LOG_ERR, "Could not open(%s). %s", pty, strerror(errno)); 275238106Sdes return (-1); 276238106Sdes } 277238106Sdes 278238106Sdes /* 279238106Sdes * Slave TTY 280238106Sdes */ 281238106Sdes 282238106Sdes if ((gr = getgrnam("tty")) != NULL) 283238106Sdes ttygid = gr->gr_gid; 284238106Sdes else 285238106Sdes ttygid = -1; 286238106Sdes 287238106Sdes (void) chown(tty, getuid(), ttygid); 288238106Sdes (void) chmod(tty, S_IRUSR|S_IWUSR|S_IWGRP); 289238106Sdes (void) revoke(tty); 290238106Sdes 291238106Sdes if ((*aslave = open(tty, O_RDWR, 0)) < 0) { 292238106Sdes syslog(LOG_ERR, "Could not open(%s). %s", tty, strerror(errno)); 293238106Sdes close(*amaster); 294238106Sdes return (-1); 295238106Sdes } 296238106Sdes 297238106Sdes /* 298238106Sdes * Make slave TTY raw 299238106Sdes */ 300238106Sdes 301238106Sdes cfmakeraw(&tio); 302238106Sdes 303238106Sdes if (tcsetattr(*aslave, TCSANOW, &tio) < 0) { 304238106Sdes syslog(LOG_ERR, "Could not tcsetattr(). %s", strerror(errno)); 305238106Sdes close(*aslave); 306238106Sdes close(*amaster); 307238106Sdes return (-1); 308238106Sdes } 309238106Sdes 310238106Sdes return (0); 311238106Sdes} /* sppd_ttys_open */ 312238106Sdes 313238106Sdes/* Read data */ 314238106Sdesstatic int 315238106Sdessppd_read(int fd, char *buffer, int size) 316238106Sdes{ 317238106Sdes int n; 318238106Sdes 319238106Sdesagain: 320238106Sdes n = read(fd, buffer, size); 321238106Sdes if (n < 0) { 322238106Sdes if (errno == EINTR) 323238106Sdes goto again; 324238106Sdes 325238106Sdes return (-1); 326238106Sdes } 327238106Sdes 328238106Sdes return (n); 329238106Sdes} /* sppd_read */ 330238106Sdes 331238106Sdes/* Write data */ 332238106Sdesstatic int 333238106Sdessppd_write(int fd, char *buffer, int size) 334238106Sdes{ 335238106Sdes int n, wrote; 336238106Sdes 337238106Sdes for (wrote = 0; size > 0; ) { 338238106Sdes n = write(fd, buffer, size); 339238106Sdes switch (n) { 340238106Sdes case -1: 341238106Sdes if (errno != EINTR) 342238106Sdes return (-1); 343238106Sdes break; 344238106Sdes 345238106Sdes case 0: 346238106Sdes /* XXX can happen? */ 347238106Sdes break; 348238106Sdes 349238106Sdes default: 350238106Sdes wrote += n; 351238106Sdes buffer += n; 352238106Sdes size -= n; 353238106Sdes break; 354238106Sdes } 355238106Sdes } 356238106Sdes 357238106Sdes return (wrote); 358238106Sdes} /* sppd_write */ 359238106Sdes 360238106Sdes/* Signal handler */ 361238106Sdesstatic void 362238106Sdessppd_sighandler(int s) 363238106Sdes{ 364238106Sdes syslog(LOG_INFO, "Signal %d received. Total %d signals received\n", 365238106Sdes s, ++ done); 366238106Sdes} /* sppd_sighandler */ 367238106Sdes 368238106Sdes/* Display usage and exit */ 369238106Sdesstatic void 370238106Sdesusage(void) 371238106Sdes{ 372238106Sdes fprintf(stdout, 373238106Sdes"Usage: %s options\n" \ 374238106Sdes"Where options are:\n" \ 375238106Sdes"\t-a bdaddr BDADDR to connect to (required)\n" \ 376238106Sdes"\t-b Run in background\n" \ 377238106Sdes"\t-c channel RFCOMM channel to connect to (required)\n" \ 378238106Sdes"\t-t tty TTY name\n" \ 379238106Sdes"\t-h Display this message\n", SPPD_IDENT); 380238106Sdes 381238106Sdes exit(255); 382238106Sdes} /* usage */ 383238106Sdes 384238106Sdes