Index: stable/5/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.1 =================================================================== --- stable/5/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.1 (revision 152964) +++ stable/5/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.1 (revision 152965) @@ -1,127 +1,134 @@ .\" Copyright (c) 2001-2003 Maksim Yevmenkin .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $Id: rfcomm_sppd.1,v 1.3 2003/09/07 18:15:55 max Exp $ .\" $FreeBSD$ .\" .Dd April 26, 2003 .Dt RFCOMM_SPPD 1 .Os .Sh NAME .Nm rfcomm_sppd .Nd RFCOMM Serial Port Profile daemon .Sh SYNOPSIS .Nm .Op Fl bh .Fl a Ar address .Fl c Ar channel .Op Fl t Ar tty .Sh DESCRIPTION The .Nm utility is a Serial Port Profile daemon. It opens RFCOMM connection to the specified .Ar address server and .Ar channel . Once connection is established, the .Nm utility provides access to the server's remote serial port via stdin/stdout or via .Xr pty 4 interface if .Fl t option was specified. .Pp The .Nm utility opens both master and slave pseudo terminals. This is done to ensure that RFCOMM connection stays open until .Nm is terminated. The data received from the master pseudo terminal are sent over the RFCOMM connection. The data received from the RFCOMM connection are written into master pseudo terminal. The application in its turn opens the slave pseudo terminal and operates on it just like it would operate over the standard serial port. .Pp The options are as follows: .Bl -tag -width indent .It Fl a Ar address This required option specifies the address of the remote RFCOMM server. The address can be specified as BD_ADDR or name. If name was specified then the .Nm utility will attempt to resolve the name via .Xr bt_gethostbyname 3 . .It Fl b Detach from the controlling terminal, i.e., run in background. .It Fl c Ar channel This option specifies RFCOMM channel to connect to. -The channel must provide Serial Port service. +RFCOMM channel could either be a number between 1 and 30 or a service name. +Supported service names are: +.Cm DUN +(for DialUp Networking service), +.Cm FAX +(for Fax service) and +.Cm SP +(for Serial Port service). If channel was not specified then .Nm -utility will try to obtain RFCOMM channel via Service Discovery Protocol from -the server. +utility will try to obtain RFCOMM channel for Serial Port service via Service +Discovery Protocol from the server. .It Fl h Display usage message and exit. .It Fl t Ar tty Slave pseudo tty name. If not set stdin/stdout will be used. This option is required if .Fl b option was specified. .El .Sh EXAMPLES .Dl "rfcomm_sppd -a 00:01:02:03:04:05 -c 1 -t /dev/ttyp1" .Pp Will start the .Nm utility and open RFCOMM connection to the server at .Li 00:01:02:03:04:05 and channel .Li 1 . Once the connection has been established, .Pa /dev/ttyp1 can be used to talk to the remote serial port on the server. .Sh FILES .Bl -tag -width ".Pa /dev/tty[p-sP-S][0-9a-v]" -compact .It Pa /dev/pty[p-sP-S][0-9a-v] master pseudo terminals .It Pa /dev/tty[p-sP-S][0-9a-v] slave pseudo terminals .El .Sh DIAGNOSTICS .Ex -std .Sh BUGS Please report if found. .Sh SEE ALSO .Xr bluetooth 3 , .Xr ng_btsocket 4 , .Xr pty 4 , .Xr rfcomm_pppd 8 .Sh AUTHORS .An Maksim Yevmenkin Aq m_evmenkin@yahoo.com Index: stable/5/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c =================================================================== --- stable/5/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c (revision 152964) +++ stable/5/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c (revision 152965) @@ -1,416 +1,440 @@ /* * rfcomm_sppd.c * * Copyright (c) 2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: rfcomm_sppd.c,v 1.4 2003/09/07 18:15:55 max Exp $ * $FreeBSD$ */ #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SPPD_IDENT "rfcomm_sppd" #define SPPD_BUFFER_SIZE 1024 #define max(a, b) (((a) > (b))? (a) : (b)) int rfcomm_channel_lookup (bdaddr_t const *local, bdaddr_t const *remote, int service, int *channel, int *error); static int sppd_ttys_open (char const *tty, int *amaster, int *aslave); static int sppd_read (int fd, char *buffer, int size); static int sppd_write (int fd, char *buffer, int size); static void sppd_sighandler (int s); static void usage (void); static int done; /* are we done? */ /* Main */ int main(int argc, char *argv[]) { struct sigaction sa; struct sockaddr_rfcomm ra; bdaddr_t addr; - int n, background, channel, s, amaster, aslave, fd; + int n, background, channel, service, + s, amaster, aslave, fd; fd_set rfd; - char *tty = NULL, buf[SPPD_BUFFER_SIZE]; + char *tty = NULL, *ep = NULL, buf[SPPD_BUFFER_SIZE]; memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)); background = channel = 0; + service = SDP_SERVICE_CLASS_SERIAL_PORT; /* Parse command line options */ while ((n = getopt(argc, argv, "a:bc:t:h")) != -1) { switch (n) { case 'a': /* BDADDR */ if (!bt_aton(optarg, &addr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&addr, he->h_addr, sizeof(addr)); } break; case 'c': /* RFCOMM channel */ - channel = atoi(optarg); + channel = strtoul(optarg, &ep, 10); + if (*ep != '\0') { + channel = 0; + switch (tolower(optarg[0])) { + case 'd': /* DialUp Networking */ + service = SDP_SERVICE_CLASS_DIALUP_NETWORKING; + break; + + case 'f': /* Fax */ + service = SDP_SERVICE_CLASS_FAX; + break; + + case 's': /* Serial Port */ + service = SDP_SERVICE_CLASS_SERIAL_PORT; + break; + + default: + errx(1, "Unknown service name: %s", + optarg); + /* NOT REACHED */ + } + } break; case 'b': /* Run in background */ background = 1; break; case 't': /* Slave TTY name */ if (optarg[0] != '/') asprintf(&tty, "%s%s", _PATH_DEV, optarg); else tty = optarg; break; case 'h': default: usage(); /* NOT REACHED */ } } /* Check if we have everything we need */ if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0) usage(); /* NOT REACHED */ /* Set signal handlers */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sppd_sighandler; if (sigaction(SIGTERM, &sa, NULL) < 0) err(1, "Could not sigaction(SIGTERM)"); if (sigaction(SIGHUP, &sa, NULL) < 0) err(1, "Could not sigaction(SIGHUP)"); if (sigaction(SIGINT, &sa, NULL) < 0) err(1, "Could not sigaction(SIGINT)"); sa.sa_handler = SIG_IGN; sa.sa_flags = SA_NOCLDWAIT; if (sigaction(SIGCHLD, &sa, NULL) < 0) err(1, "Could not sigaction(SIGCHLD)"); /* Check channel, if was not set then obtain it via SDP */ - if (channel == 0) + if (channel == 0 && service != 0) if (rfcomm_channel_lookup(NULL, &addr, - SDP_SERVICE_CLASS_SERIAL_PORT, &channel, &n) != 0) + service, &channel, &n) != 0) errc(1, n, "Could not obtain RFCOMM channel"); if (channel <= 0 || channel > 30) errx(1, "Invalid RFCOMM channel number %d", channel); /* Open TTYs */ if (tty == NULL) { if (background) usage(); amaster = STDIN_FILENO; fd = STDOUT_FILENO; } else { if (sppd_ttys_open(tty, &amaster, &aslave) < 0) exit(1); fd = amaster; } /* Open RFCOMM connection */ memset(&ra, 0, sizeof(ra)); ra.rfcomm_len = sizeof(ra); ra.rfcomm_family = AF_BLUETOOTH; s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); if (s < 0) err(1, "Could not create socket"); if (bind(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) err(1, "Could not bind socket"); memcpy(&ra.rfcomm_bdaddr, &addr, sizeof(ra.rfcomm_bdaddr)); ra.rfcomm_channel = channel; if (connect(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) err(1, "Could not connect socket"); /* Became daemon if required */ if (background) { switch (fork()) { case -1: err(1, "Could not fork()"); /* NOT REACHED */ case 0: exit(0); /* NOT REACHED */ default: if (daemon(0, 0) < 0) err(1, "Could not daemon()"); break; } } openlog(SPPD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "Starting on %s...", (tty != NULL)? tty : "stdin/stdout"); for (done = 0; !done; ) { FD_ZERO(&rfd); FD_SET(amaster, &rfd); FD_SET(s, &rfd); n = select(max(amaster, s) + 1, &rfd, NULL, NULL, NULL); if (n < 0) { if (errno == EINTR) continue; syslog(LOG_ERR, "Could not select(). %s", strerror(errno)); exit(1); } if (n == 0) continue; if (FD_ISSET(amaster, &rfd)) { n = sppd_read(amaster, buf, sizeof(buf)); if (n < 0) { syslog(LOG_ERR, "Could not read master pty, " \ "fd=%d. %s", amaster, strerror(errno)); exit(1); } if (n == 0) break; /* XXX */ if (sppd_write(s, buf, n) < 0) { syslog(LOG_ERR, "Could not write to socket, " \ "fd=%d, size=%d. %s", s, n, strerror(errno)); exit(1); } } if (FD_ISSET(s, &rfd)) { n = sppd_read(s, buf, sizeof(buf)); if (n < 0) { syslog(LOG_ERR, "Could not read socket, " \ "fd=%d. %s", s, strerror(errno)); exit(1); } if (n == 0) break; if (sppd_write(fd, buf, n) < 0) { syslog(LOG_ERR, "Could not write to master " \ "pty, fd=%d, size=%d. %s", fd, n, strerror(errno)); exit(1); } } } syslog(LOG_INFO, "Completed on %s", (tty != NULL)? tty : "stdin/stdout"); closelog(); close(s); if (tty != NULL) { close(aslave); close(amaster); } return (0); } /* Open TTYs */ static int sppd_ttys_open(char const *tty, int *amaster, int *aslave) { char pty[PATH_MAX], *slash; struct group *gr = NULL; gid_t ttygid; struct termios tio; /* * Construct master PTY name. The slave tty name must be less then * PATH_MAX characters in length, must contain '/' character and * must not end with '/'. */ if (strlen(tty) >= sizeof(pty)) { syslog(LOG_ERR, "Slave tty name is too long"); return (-1); } strlcpy(pty, tty, sizeof(pty)); slash = strrchr(pty, '/'); if (slash == NULL || slash[1] == '\0') { syslog(LOG_ERR, "Invalid slave tty name (%s)", tty); return (-1); } slash[1] = 'p'; if (strcmp(pty, tty) == 0) { syslog(LOG_ERR, "Master and slave tty are the same (%s)", tty); return (-1); } if ((*amaster = open(pty, O_RDWR, 0)) < 0) { syslog(LOG_ERR, "Could not open(%s). %s", pty, strerror(errno)); return (-1); } /* * Slave TTY */ if ((gr = getgrnam("tty")) != NULL) ttygid = gr->gr_gid; else ttygid = -1; (void) chown(tty, getuid(), ttygid); (void) chmod(tty, S_IRUSR|S_IWUSR|S_IWGRP); (void) revoke(tty); if ((*aslave = open(tty, O_RDWR, 0)) < 0) { syslog(LOG_ERR, "Could not open(%s). %s", tty, strerror(errno)); close(*amaster); return (-1); } /* * Make slave TTY raw */ cfmakeraw(&tio); if (tcsetattr(*aslave, TCSANOW, &tio) < 0) { syslog(LOG_ERR, "Could not tcsetattr(). %s", strerror(errno)); close(*aslave); close(*amaster); return (-1); } return (0); } /* sppd_ttys_open */ /* Read data */ static int sppd_read(int fd, char *buffer, int size) { int n; again: n = read(fd, buffer, size); if (n < 0) { if (errno == EINTR) goto again; return (-1); } return (n); } /* sppd_read */ /* Write data */ static int sppd_write(int fd, char *buffer, int size) { int n, wrote; for (wrote = 0; size > 0; ) { n = write(fd, buffer, size); switch (n) { case -1: if (errno != EINTR) return (-1); break; case 0: /* XXX can happen? */ break; default: wrote += n; buffer += n; size -= n; break; } } return (wrote); } /* sppd_write */ /* Signal handler */ static void sppd_sighandler(int s) { syslog(LOG_INFO, "Signal %d received. Total %d signals received\n", s, ++ done); } /* sppd_sighandler */ /* Display usage and exit */ static void usage(void) { fprintf(stdout, "Usage: %s options\n" \ "Where options are:\n" \ "\t-a address Address to connect to (required)\n" \ "\t-b Run in background\n" \ "\t-c channel RFCOMM channel to connect to\n" \ "\t-t tty TTY name (required in background mode)\n" \ "\t-h Display this message\n", SPPD_IDENT); exit(255); } /* usage */ Index: stable/6/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.1 =================================================================== --- stable/6/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.1 (revision 152964) +++ stable/6/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.1 (revision 152965) @@ -1,127 +1,134 @@ .\" Copyright (c) 2001-2003 Maksim Yevmenkin .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $Id: rfcomm_sppd.1,v 1.3 2003/09/07 18:15:55 max Exp $ .\" $FreeBSD$ .\" .Dd April 26, 2003 .Dt RFCOMM_SPPD 1 .Os .Sh NAME .Nm rfcomm_sppd .Nd RFCOMM Serial Port Profile daemon .Sh SYNOPSIS .Nm .Op Fl bh .Fl a Ar address .Fl c Ar channel .Op Fl t Ar tty .Sh DESCRIPTION The .Nm utility is a Serial Port Profile daemon. It opens RFCOMM connection to the specified .Ar address server and .Ar channel . Once connection is established, the .Nm utility provides access to the server's remote serial port via stdin/stdout or via .Xr pty 4 interface if .Fl t option was specified. .Pp The .Nm utility opens both master and slave pseudo terminals. This is done to ensure that RFCOMM connection stays open until .Nm is terminated. The data received from the master pseudo terminal are sent over the RFCOMM connection. The data received from the RFCOMM connection are written into master pseudo terminal. The application in its turn opens the slave pseudo terminal and operates on it just like it would operate over the standard serial port. .Pp The options are as follows: .Bl -tag -width indent .It Fl a Ar address This required option specifies the address of the remote RFCOMM server. The address can be specified as BD_ADDR or name. If name was specified then the .Nm utility will attempt to resolve the name via .Xr bt_gethostbyname 3 . .It Fl b Detach from the controlling terminal, i.e., run in background. .It Fl c Ar channel This option specifies RFCOMM channel to connect to. -The channel must provide Serial Port service. +RFCOMM channel could either be a number between 1 and 30 or a service name. +Supported service names are: +.Cm DUN +(for DialUp Networking service), +.Cm FAX +(for Fax service) and +.Cm SP +(for Serial Port service). If channel was not specified then .Nm -utility will try to obtain RFCOMM channel via Service Discovery Protocol from -the server. +utility will try to obtain RFCOMM channel for Serial Port service via Service +Discovery Protocol from the server. .It Fl h Display usage message and exit. .It Fl t Ar tty Slave pseudo tty name. If not set stdin/stdout will be used. This option is required if .Fl b option was specified. .El .Sh FILES .Bl -tag -width ".Pa /dev/tty[p-sP-S][0-9a-v]" -compact .It Pa /dev/pty[p-sP-S][0-9a-v] master pseudo terminals .It Pa /dev/tty[p-sP-S][0-9a-v] slave pseudo terminals .El .Sh EXIT STATUS .Ex -std .Sh EXAMPLES .Dl "rfcomm_sppd -a 00:01:02:03:04:05 -c 1 -t /dev/ttyp1" .Pp Will start the .Nm utility and open RFCOMM connection to the server at .Li 00:01:02:03:04:05 and channel .Li 1 . Once the connection has been established, .Pa /dev/ttyp1 can be used to talk to the remote serial port on the server. .Sh SEE ALSO .Xr bluetooth 3 , .Xr ng_btsocket 4 , .Xr pty 4 , .Xr rfcomm_pppd 8 .Sh AUTHORS .An Maksim Yevmenkin Aq m_evmenkin@yahoo.com .Sh BUGS Please report if found. Index: stable/6/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c =================================================================== --- stable/6/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c (revision 152964) +++ stable/6/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sppd.c (revision 152965) @@ -1,416 +1,440 @@ /* * rfcomm_sppd.c * * Copyright (c) 2003 Maksim Yevmenkin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: rfcomm_sppd.c,v 1.4 2003/09/07 18:15:55 max Exp $ * $FreeBSD$ */ #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SPPD_IDENT "rfcomm_sppd" #define SPPD_BUFFER_SIZE 1024 #define max(a, b) (((a) > (b))? (a) : (b)) int rfcomm_channel_lookup (bdaddr_t const *local, bdaddr_t const *remote, int service, int *channel, int *error); static int sppd_ttys_open (char const *tty, int *amaster, int *aslave); static int sppd_read (int fd, char *buffer, int size); static int sppd_write (int fd, char *buffer, int size); static void sppd_sighandler (int s); static void usage (void); static int done; /* are we done? */ /* Main */ int main(int argc, char *argv[]) { struct sigaction sa; struct sockaddr_rfcomm ra; bdaddr_t addr; - int n, background, channel, s, amaster, aslave, fd; + int n, background, channel, service, + s, amaster, aslave, fd; fd_set rfd; - char *tty = NULL, buf[SPPD_BUFFER_SIZE]; + char *tty = NULL, *ep = NULL, buf[SPPD_BUFFER_SIZE]; memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)); background = channel = 0; + service = SDP_SERVICE_CLASS_SERIAL_PORT; /* Parse command line options */ while ((n = getopt(argc, argv, "a:bc:t:h")) != -1) { switch (n) { case 'a': /* BDADDR */ if (!bt_aton(optarg, &addr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&addr, he->h_addr, sizeof(addr)); } break; case 'c': /* RFCOMM channel */ - channel = atoi(optarg); + channel = strtoul(optarg, &ep, 10); + if (*ep != '\0') { + channel = 0; + switch (tolower(optarg[0])) { + case 'd': /* DialUp Networking */ + service = SDP_SERVICE_CLASS_DIALUP_NETWORKING; + break; + + case 'f': /* Fax */ + service = SDP_SERVICE_CLASS_FAX; + break; + + case 's': /* Serial Port */ + service = SDP_SERVICE_CLASS_SERIAL_PORT; + break; + + default: + errx(1, "Unknown service name: %s", + optarg); + /* NOT REACHED */ + } + } break; case 'b': /* Run in background */ background = 1; break; case 't': /* Slave TTY name */ if (optarg[0] != '/') asprintf(&tty, "%s%s", _PATH_DEV, optarg); else tty = optarg; break; case 'h': default: usage(); /* NOT REACHED */ } } /* Check if we have everything we need */ if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0) usage(); /* NOT REACHED */ /* Set signal handlers */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sppd_sighandler; if (sigaction(SIGTERM, &sa, NULL) < 0) err(1, "Could not sigaction(SIGTERM)"); if (sigaction(SIGHUP, &sa, NULL) < 0) err(1, "Could not sigaction(SIGHUP)"); if (sigaction(SIGINT, &sa, NULL) < 0) err(1, "Could not sigaction(SIGINT)"); sa.sa_handler = SIG_IGN; sa.sa_flags = SA_NOCLDWAIT; if (sigaction(SIGCHLD, &sa, NULL) < 0) err(1, "Could not sigaction(SIGCHLD)"); /* Check channel, if was not set then obtain it via SDP */ - if (channel == 0) + if (channel == 0 && service != 0) if (rfcomm_channel_lookup(NULL, &addr, - SDP_SERVICE_CLASS_SERIAL_PORT, &channel, &n) != 0) + service, &channel, &n) != 0) errc(1, n, "Could not obtain RFCOMM channel"); if (channel <= 0 || channel > 30) errx(1, "Invalid RFCOMM channel number %d", channel); /* Open TTYs */ if (tty == NULL) { if (background) usage(); amaster = STDIN_FILENO; fd = STDOUT_FILENO; } else { if (sppd_ttys_open(tty, &amaster, &aslave) < 0) exit(1); fd = amaster; } /* Open RFCOMM connection */ memset(&ra, 0, sizeof(ra)); ra.rfcomm_len = sizeof(ra); ra.rfcomm_family = AF_BLUETOOTH; s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); if (s < 0) err(1, "Could not create socket"); if (bind(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) err(1, "Could not bind socket"); memcpy(&ra.rfcomm_bdaddr, &addr, sizeof(ra.rfcomm_bdaddr)); ra.rfcomm_channel = channel; if (connect(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) err(1, "Could not connect socket"); /* Became daemon if required */ if (background) { switch (fork()) { case -1: err(1, "Could not fork()"); /* NOT REACHED */ case 0: exit(0); /* NOT REACHED */ default: if (daemon(0, 0) < 0) err(1, "Could not daemon()"); break; } } openlog(SPPD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "Starting on %s...", (tty != NULL)? tty : "stdin/stdout"); for (done = 0; !done; ) { FD_ZERO(&rfd); FD_SET(amaster, &rfd); FD_SET(s, &rfd); n = select(max(amaster, s) + 1, &rfd, NULL, NULL, NULL); if (n < 0) { if (errno == EINTR) continue; syslog(LOG_ERR, "Could not select(). %s", strerror(errno)); exit(1); } if (n == 0) continue; if (FD_ISSET(amaster, &rfd)) { n = sppd_read(amaster, buf, sizeof(buf)); if (n < 0) { syslog(LOG_ERR, "Could not read master pty, " \ "fd=%d. %s", amaster, strerror(errno)); exit(1); } if (n == 0) break; /* XXX */ if (sppd_write(s, buf, n) < 0) { syslog(LOG_ERR, "Could not write to socket, " \ "fd=%d, size=%d. %s", s, n, strerror(errno)); exit(1); } } if (FD_ISSET(s, &rfd)) { n = sppd_read(s, buf, sizeof(buf)); if (n < 0) { syslog(LOG_ERR, "Could not read socket, " \ "fd=%d. %s", s, strerror(errno)); exit(1); } if (n == 0) break; if (sppd_write(fd, buf, n) < 0) { syslog(LOG_ERR, "Could not write to master " \ "pty, fd=%d, size=%d. %s", fd, n, strerror(errno)); exit(1); } } } syslog(LOG_INFO, "Completed on %s", (tty != NULL)? tty : "stdin/stdout"); closelog(); close(s); if (tty != NULL) { close(aslave); close(amaster); } return (0); } /* Open TTYs */ static int sppd_ttys_open(char const *tty, int *amaster, int *aslave) { char pty[PATH_MAX], *slash; struct group *gr = NULL; gid_t ttygid; struct termios tio; /* * Construct master PTY name. The slave tty name must be less then * PATH_MAX characters in length, must contain '/' character and * must not end with '/'. */ if (strlen(tty) >= sizeof(pty)) { syslog(LOG_ERR, "Slave tty name is too long"); return (-1); } strlcpy(pty, tty, sizeof(pty)); slash = strrchr(pty, '/'); if (slash == NULL || slash[1] == '\0') { syslog(LOG_ERR, "Invalid slave tty name (%s)", tty); return (-1); } slash[1] = 'p'; if (strcmp(pty, tty) == 0) { syslog(LOG_ERR, "Master and slave tty are the same (%s)", tty); return (-1); } if ((*amaster = open(pty, O_RDWR, 0)) < 0) { syslog(LOG_ERR, "Could not open(%s). %s", pty, strerror(errno)); return (-1); } /* * Slave TTY */ if ((gr = getgrnam("tty")) != NULL) ttygid = gr->gr_gid; else ttygid = -1; (void) chown(tty, getuid(), ttygid); (void) chmod(tty, S_IRUSR|S_IWUSR|S_IWGRP); (void) revoke(tty); if ((*aslave = open(tty, O_RDWR, 0)) < 0) { syslog(LOG_ERR, "Could not open(%s). %s", tty, strerror(errno)); close(*amaster); return (-1); } /* * Make slave TTY raw */ cfmakeraw(&tio); if (tcsetattr(*aslave, TCSANOW, &tio) < 0) { syslog(LOG_ERR, "Could not tcsetattr(). %s", strerror(errno)); close(*aslave); close(*amaster); return (-1); } return (0); } /* sppd_ttys_open */ /* Read data */ static int sppd_read(int fd, char *buffer, int size) { int n; again: n = read(fd, buffer, size); if (n < 0) { if (errno == EINTR) goto again; return (-1); } return (n); } /* sppd_read */ /* Write data */ static int sppd_write(int fd, char *buffer, int size) { int n, wrote; for (wrote = 0; size > 0; ) { n = write(fd, buffer, size); switch (n) { case -1: if (errno != EINTR) return (-1); break; case 0: /* XXX can happen? */ break; default: wrote += n; buffer += n; size -= n; break; } } return (wrote); } /* sppd_write */ /* Signal handler */ static void sppd_sighandler(int s) { syslog(LOG_INFO, "Signal %d received. Total %d signals received\n", s, ++ done); } /* sppd_sighandler */ /* Display usage and exit */ static void usage(void) { fprintf(stdout, "Usage: %s options\n" \ "Where options are:\n" \ "\t-a address Address to connect to (required)\n" \ "\t-b Run in background\n" \ "\t-c channel RFCOMM channel to connect to\n" \ "\t-t tty TTY name (required in background mode)\n" \ "\t-h Display this message\n", SPPD_IDENT); exit(255); } /* usage */