diff options
Diffstat (limited to 'tools/ppporc.c')
-rw-r--r-- | tools/ppporc.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/tools/ppporc.c b/tools/ppporc.c new file mode 100644 index 00000000..2d8b034d --- /dev/null +++ b/tools/ppporc.c @@ -0,0 +1,273 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2002-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <syslog.h> +#include <sys/poll.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> + +extern int optind, opterr, optopt; +extern char *optarg; + +/* IO cancelation */ +static volatile sig_atomic_t __io_canceled; + +static inline void io_init(void) +{ + __io_canceled = 0; +} + +static inline void io_cancel(void) +{ + __io_canceled = 1; +} + +/* Signal functions */ +static void sig_hup(int sig) +{ + return; +} + +static void sig_term(int sig) +{ + syslog(LOG_INFO, "Closing RFCOMM channel"); + io_cancel(); +} + +/* Read exactly len bytes (Signal safe)*/ +static inline int read_n(int fd, char *buf, int len) +{ + register int t = 0, w; + + while (!__io_canceled && len > 0) { + if ((w = read(fd, buf, len)) < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + if (!w) + return 0; + len -= w; + buf += w; + t += w; + } + + return t; +} + +/* Write exactly len bytes (Signal safe)*/ +static inline int write_n(int fd, char *buf, int len) +{ + register int t = 0, w; + + while (!__io_canceled && len > 0) { + if ((w = write(fd, buf, len)) < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + if (!w) + return 0; + len -= w; + buf += w; + t += w; + } + + return t; +} + +/* Create the RFCOMM connection */ +static int create_connection(bdaddr_t *bdaddr, uint8_t channel) +{ + struct sockaddr_rc remote_addr, local_addr; + int fd, err; + + if ((fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) + return fd; + + memset(&local_addr, 0, sizeof(local_addr)); + local_addr.rc_family = AF_BLUETOOTH; + bacpy(&local_addr.rc_bdaddr, BDADDR_ANY); + if ((err = bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr))) < 0) { + close(fd); + return err; + } + + memset(&remote_addr, 0, sizeof(remote_addr)); + remote_addr.rc_family = AF_BLUETOOTH; + bacpy(&remote_addr.rc_bdaddr, bdaddr); + remote_addr.rc_channel = channel; + if ((err = connect(fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr))) < 0) { + close(fd); + return err; + } + + syslog(LOG_INFO, "RFCOMM channel %d connected", channel); + + return fd; +} + +/* Process the data from socket and pseudo tty */ +static int process_data(int fd) +{ + struct pollfd p[2]; + char buf[1024]; + int err, r; + + p[0].fd = 0; + p[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; + + p[1].fd = fd; + p[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; + + err = 0; + + while (!__io_canceled) { + p[0].revents = 0; + p[1].revents = 0; + + err = poll(p, 2, -1); + if (err < 0) + break; + + err = 0; + + if (p[0].revents) { + if (p[0].revents & (POLLERR | POLLHUP | POLLNVAL)) + break; + r = read(0, buf, sizeof(buf)); + if (r < 0) { + if (errno != EINTR && errno != EAGAIN) { + err = r; + break; + } + } + + err = write_n(fd, buf, r); + if (err < 0) + break; + } + + if (p[1].revents) { + if (p[1].revents & (POLLERR | POLLHUP | POLLNVAL)) + break; + r = read(fd, buf, sizeof(buf)); + if (r < 0) { + if (errno != EINTR && errno != EAGAIN) { + err = r; + break; + } + } + + err = write_n(1, buf, r); + if (err < 0) + break; + } + } + + return err; +} + +static void usage(void) +{ + printf("Usage:\tppporc <bdaddr> [channel]\n"); +} + +int main(int argc, char** argv) +{ + struct sigaction sa; + int fd, err, opt; + + bdaddr_t bdaddr; + uint8_t channel; + + /* Parse command line options */ + while ((opt = getopt(argc, argv, "h")) != EOF) { + switch(opt) { + case 'h': + usage(); + exit(0); + } + } + + argc -= optind; + argv += optind; + + switch (argc) { + case 1: + str2ba(argv[0], &bdaddr); + channel = 1; + break; + case 2: + str2ba(argv[0], &bdaddr); + channel = atoi(argv[1]); + break; + default: + usage(); + exit(0); + } + + /* Initialize syslog */ + openlog("ppporc", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); + syslog(LOG_INFO, "PPP over RFCOMM"); + + /* Initialize signals */ + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + + sa.sa_handler = sig_term; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + sa.sa_handler = sig_hup; + sigaction(SIGHUP, &sa, NULL); + + syslog(LOG_INFO, "Connecting to %s", argv[0]); + + if ((fd = create_connection(&bdaddr, channel)) < 0) { + syslog(LOG_ERR, "Can't connect to remote device (%s)", strerror(errno)); + return fd; + } + + err = process_data(fd); + + close(fd); + + return err; +} |