/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2002-2009 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 <getopt.h> #include <sys/poll.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <bluetooth/bluetooth.h> #include <bluetooth/rfcomm.h> /* 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; }