rfcommd

RFCOMM daemon to run filters on clients.
git clone git://arztpraxis-lohmann.de/rfcommd
Log | Files | Refs

commit 70f725160626e24b13cdd304ed86b1184783fc97
Author: Christoph Lohmann <20h@r-36.net>
Date:   Fri, 25 Mar 2022 18:10:39 +0100

Initial commit of rfcommd.

Diffstat:
Makefile | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
arg.h | 46++++++++++++++++++++++++++++++++++++++++++++++
etc/conf.d/rfcommd.gentoo | 6++++++
etc/init.d/rfcommd.gentoo | 29+++++++++++++++++++++++++++++
filters/euroklavfilter | 40++++++++++++++++++++++++++++++++++++++++
filters/spirofilter | 39+++++++++++++++++++++++++++++++++++++++
rfcommd.c | 595+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 808 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,53 @@ +# rfcomm-script - a rfcomm service script +# See LICENSE file for copyright and license details. +.POSIX: + +NAME = rfcommd +VERSION = 0.2 + +PREFIX = /usr/local +BINDIR = ${PREFIX}/sbin +MANDIR = ${PREFIX}/share/man/man1 + +RFCOMMD_CFLAGS = -D_GNU_SOURCE -Wall -I. -I/usr/include ${CFLAGS} +RFCOMMD_LDFLAGS = -L/usr/lib -L. -lbluetooth ${LDFLAGS} + +SRC = rfcommd.c +OBJ = ${SRC:.c=.o} + +all: ${NAME} + +.c.o: + ${CC} ${RFCOMMD_CFLAGS} -c $< + +${OBJ}: + +${NAME}: ${OBJ} + ${CC} -o $@ ${OBJ} ${RFCOMMD_LDFLAGS} + +clean: + rm -f ${NAME} ${OBJ} ${NAME}-${VERSION}.tar.gz + +install: all + mkdir -p "${DESTDIR}${BINDIR}" + cp -f ${NAME} "${DESTDIR}${BINDIR}" + chmod 755 "${DESTDIR}${BINDIR}/${NAME}" + #mkdir -p "${DESTDIR}${MANDIR}" + #cp -f ${NAME}.1 "${DESTDIR}${MANDIR}" + #chmod 644 "${DESTDIR}${MANDIR}/${MANDIR}.1" + +uninstall: + rm -f "${DESTDIR}${BINDIR}/${NAME}" + #rm -f "${DESTDIR}${MANDIR}/${NAME}.1" + +dist: clean + mkdir -p ${NAME}-${VERSION} + cp -R *.c *.h Makefile \ + ${NAME}-${VERSION} + tar -cf ${NAME}-${VERSION}.tar ${NAME}-${VERSION} + gzip ${NAME}-${VERSION}.tar + mv ${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION}.tgz + rm -rf "${NAME}-${VERSION}" + +.PHONY: all clean + diff --git a/arg.h b/arg.h @@ -0,0 +1,46 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN(ARGV0) for (ARGV0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/etc/conf.d/rfcommd.gentoo b/etc/conf.d/rfcommd.gentoo @@ -0,0 +1,6 @@ +# +# Parameters to be passed to rfcommd +# + +RFCOMMD_ARGS=(-d -f 10:00:E8:27:E3:B3 "sudo -u david /usr/local/bin/spirofilter {}" -f 00:1B:35:14:21:2C "sudo -u david /usr/local/bin/euroklavfilter {}" -f 00:1B:35:17:4B:84 "sudo -u david /usr/local/bin/scpecgfilter {}") + diff --git a/etc/init.d/rfcommd.gentoo b/etc/init.d/rfcommd.gentoo @@ -0,0 +1,29 @@ +#!/sbin/openrc-run +# Copyright 1999-2016 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +depend() { + need bluetooth +} + +start() { + ebegin "Starting rfcommd" + #[ -n "$RFCOMMD_ARGS" ] && RFCOMMD_ARGS="-- $RFCOMMD_ARGS" + start-stop-daemon -Sbimp /var/run/rfcommd.pid \ + -1 /var/log/rfcommd-stdout.log \ + -2 /var/log/rfcommd-stderr.log \ + -x /usr/sbin/rfcommd -- "${RFCOMMD_ARGS[@]}" + #start-stop-daemon -Sbimp /var/run/rfcommd.pid \ + # -1 /tmp/rfcommd-stdout.log \ + # -2 /tmp/rfcommd-stderr.log \ + # -x /usr/sbin/rfcommd ${RFCOMMD_ARGS} + eend $? "Failed to start rfcommd" +} + +stop() { + ebegin "Stopping rfcommd" + start-stop-daemon -Kip /var/run/rfcommd.pid + eend $? "Failed to stop rfcommd" +} + diff --git a/filters/euroklavfilter b/filters/euroklavfilter @@ -0,0 +1,40 @@ +#!/bin/sh + +euroklavdb="/home/david/share/euroklav" +dbpath="${euroklavdb}" + +if [ $# -gt 0 ]; +then + infile="$1" + case "${infile}" in + /dev/rfcomm*) + stty -F "${infile}" raw -echo -echoe -echok + ;; + esac +else + infile="/dev/stdin" +fi + +cd "${dbpath}" +datetime="$(date +%Y-%m-%d-%H-%M-%S)" +outfile="euroklav-${datetime}.txt" +cat "${infile}" \ +| while read -r line; +do + case "${line}" in + $'\003'*) + datetime="$(date +%Y-%m-%d-%H-%M-%S)" + outfile="euroklav-${datetime}.txt" + ;; + *" 4.07 4.08"*) + printf "%s\n" "${line}" >> ${outfile} + cat ${outfile} \ + | tr -d '\002\003' \ + | tr '\370' ' ' \ + | tr '\346' ' ' \ + | lpr + ;; + esac + printf "%s\n" "${line}" >> ${outfile} +done + diff --git a/filters/spirofilter b/filters/spirofilter @@ -0,0 +1,39 @@ +#!/bin/sh + +tmpfile="$(mktemp)" +spirometrydb="/home/david/share/spirometry" + +datetime="$(date +%Y-%m-%d-%H-%M-%S)" +if [ $# -gt 0 ]; +then + infile="$1" + case "${infile}" in + /dev/rfcomm*) + stty -F "${infile}" raw -echo -echoe -echok + ;; + *) + datetime="$(echo $infile \ + | cut -d- -f 2- \ + | cut -d'.' -f 1)" + ;; + esac +else + infile="/dev/stdin" +fi + +cat "${infile}" > "${tmpfile}" +dbpath="${spirometrydb}" +cd "${dbpath}" +outfile="spirometry-$(cat "${tmpfile}" \ + | tr -d '\r' \ + | iconv -f iso8859-1 -t utf-8 \ + | grep -a "^NAME" \ + | sed 's,NAME \([^ ]*\)[ ]*\([^ ]*\)[ ]*#ID \([0-9]*\)[ ]*,\1-\2-\3,' \ + | sed 's, ,_,g; s,/,_,g;')-${datetime}.pcl" +mv "${tmpfile}" "${outfile}" 2>/dev/null +chown david:david "${outfile}" 2>/dev/null + +cat "${outfile}" \ + | sed '/Daten von spirolab III Ver/d' \ + | lpr + diff --git a/rfcommd.c b/rfcommd.c @@ -0,0 +1,595 @@ +/* + * RFCOMM DAEMON + * Copied from rfcomm.c in bluez. + * SDP code from pybluez. + * + * Copy me if you can. + * by 20h + */ + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <signal.h> +#include <libgen.h> +#include <termios.h> +#include <stdint.h> +#include <poll.h> +#include <stdarg.h> +#include <sys/poll.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/rfcomm.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> + +#include "arg.h" + +volatile sig_atomic_t __io_canceled = 0; + +int dodebug = 0; + +void +debug(char *fmt, ...) +{ + va_list fmtargs; + + if (!dodebug) + return; + + va_start(fmtargs, fmt); + vfprintf(stderr, fmt, fmtargs); + va_end(fmtargs); +} + +void +sig_hup(int sig) +{ + return; +} + +void +sig_term(int sig) +{ + __io_canceled = 1; +} + +void +setup_signals(void) +{ + struct sigaction sa; + + 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); +} + +int +_adv_available(struct hci_dev_info *di) +{ + uint32_t *flags = &di->flags; + int dd; + + if (hci_test_bit(HCI_RAW, &flags) && !bacmp(&di->bdaddr, BDADDR_ANY)) { + dd = hci_open_dev(di->dev_id); + + if (dd < 0) + return -1; + hci_read_bd_addr(dd, &di->bdaddr, 1000); + hci_close_dev(dd); + } + + return (hci_test_bit(HCI_UP, flags) && + hci_test_bit(HCI_RUNNING, flags) && + hci_test_bit(HCI_PSCAN, flags) && + hci_test_bit(HCI_ISCAN, flags)) != 0 ? 0 : -1; +} + +int +_any_adv_available(void) +{ + struct hci_dev_list_req *dl = NULL; + struct hci_dev_req *dr = NULL; + struct hci_dev_info di = {0, }; + int result = -1; + int ctl = -1; + int i; + + if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) + return -1; + + if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + + sizeof(uint16_t)))) { + goto CLEAN_UP_RETURN; + } + dl->dev_num = HCI_MAX_DEV; + dr = dl->dev_req; + + if (ioctl(ctl, HCIGETDEVLIST, (void *)dl) < 0) + goto CLEAN_UP_RETURN; + + for (i = 0; i < dl->dev_num; i++) { + di.dev_id = (dr+i)->dev_id; + if (ioctl(ctl, HCIGETDEVINFO, (void *)&di) < 0) + continue; + + if (_adv_available(&di) == 0) { + result = 0; + goto CLEAN_UP_RETURN; + } + } + +CLEAN_UP_RETURN: + close(ctl); + free(dl); + + return result; +} + +int +adv_available(int sock) +{ + bdaddr_t ba = {{0, }}; + struct sockaddr addr = {0, }; + int dev_id = -1; + socklen_t alen = sizeof(addr); + struct sockaddr_rc const *addr_rc = (struct sockaddr_rc const *)&addr; + struct hci_dev_info di; + + if (getsockname(sock, &addr, &alen) < 0) + return -1; + + ba = addr_rc->rc_bdaddr; + + if (bacmp(&ba, BDADDR_ANY) == 0) { + dev_id = -1; + } else { + dev_id = hci_get_route(&ba); + } + + if (dev_id == -1) { + return _any_adv_available(); + } else { + if (hci_devinfo(dev_id, &di)) + return -1; + return _adv_available(&di); + } +} + +int +str2uuid(char *uuidstr, uuid_t *uuid) +{ + uint32_t uuid_int[4]; + int i; + char *endptr, buf[9] = { 0 }; + + if (strlen(uuidstr) == 36) { + if (uuidstr[8] != '-' && uuidstr[13] != '-' && + uuidstr[18] != '-' && uuidstr[23] != '-') { + return 1; + } + + strncpy(buf, uuidstr, 8); + uuid_int[0] = htonl(strtoul(buf, &endptr, 16)); + if (endptr != buf+8) + return 1; + + strncpy(buf, uuidstr+9, 4); + strncpy(buf+4, uuidstr+14, 4); + uuid_int[1] = htonl(strtoul(buf, &endptr, 16)); + if (endptr != buf+8) + return 1; + + strncpy(buf, uuidstr+19, 4); + strncpy(buf+4, uuidstr+24, 4); + uuid_int[2] = htonl(strtoul(buf, &endptr, 16)); + if (endptr != buf+8) + return 1; + + strncpy(buf, uuidstr+28, 4); + uuid_int[3] = htonl(strtoul(buf, &endptr, 16)); + if (endptr != buf+8) + return 1; + + if (uuid != NULL) + sdp_uuid128_create(uuid, uuid_int); + } else if (strlen(uuidstr) == 8) { + uuid_int[0] = strtoul(uuidstr, &endptr, 16); + if (endptr != uuidstr+8) + return 1; + if (uuid != NULL) + sdp_uuid32_create(uuid, uuid_int[0]); + } else if (strlen(uuidstr) == 4) { + i = strtol(uuidstr, &endptr, 16); + if (endptr != uuidstr+4) + return 1; + if (uuid != NULL) + sdp_uuid16_create(uuid, i); + } else { + return 1; + } + + return 0; +} + +int +sdp_advertise_service(int sock, char *svcname, + char *svcid, int svc_class, int profiles, + char *svcprovider, char *svcdescription) +{ + char addrbuf[256]; + int res, err = 0; + struct sockaddr *sockaddr; + uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_class_uuid, + svc_uuid; + sdp_profile_desc_t *profile_desc; + sdp_list_t *l2cap_list = NULL, *rfcomm_list = NULL, *root_list = NULL, + *proto_list = NULL, *profile_list = NULL, + *svc_class_list = NULL, *access_proto_list = NULL; + sdp_data_t *channel = 0; + sdp_record_t record; + sdp_session_t *session = 0; + uint8_t rfcomm_channel; + socklen_t addrlen = sizeof(struct sockaddr_rc); + + str2uuid(svcid, &svc_uuid); + sdp_uuid16_create(&svc_class_uuid, svc_class); + + memset(addrbuf, 0, sizeof(addrbuf)); + + if (adv_available(sock) < 0) + return -1; + + res = getsockname(sock, (struct sockaddr *)addrbuf, &addrlen); + if (res < 0) + return -1; + sockaddr = (struct sockaddr *)addrbuf; + + memset(&record, 0, sizeof(record)); + memset(&record.handle, 0xff, sizeof(record.handle)); + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root_list = sdp_list_append(0, &root_uuid); + sdp_set_browse_groups(&record, root_list); + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + l2cap_list = sdp_list_append(0, &l2cap_uuid); + proto_list = sdp_list_append(0, l2cap_list); + rfcomm_channel = ((struct sockaddr_rc *)sockaddr)->rc_channel; + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel); + rfcomm_list = sdp_list_append(0, &rfcomm_uuid); + sdp_list_append(rfcomm_list, channel); + sdp_list_append(proto_list, rfcomm_list); + access_proto_list = sdp_list_append(0, proto_list); + sdp_set_access_protos(&record, access_proto_list); + svc_class_list = sdp_list_append(svc_class_list, &svc_class_uuid); + sdp_set_service_classes(&record, svc_class_list); + + profile_desc = (sdp_profile_desc_t *)malloc(sizeof(sdp_profile_desc_t)); + if (profile_desc == NULL) + return -1; + sdp_uuid16_create(&profile_desc->uuid, profiles); + profile_list = sdp_list_append(profile_list, profile_desc); + sdp_set_profile_descs(&record, profile_list); + + sdp_set_info_attr(&record, svcname, svcprovider, svcdescription); + sdp_set_service_id(&record, svc_uuid); + + session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0); + if (!session) + return -1; + err = sdp_record_register(session, &record, 0); + + if (channel) + sdp_data_free(channel); + sdp_list_free(l2cap_list, 0); + sdp_list_free(rfcomm_list, 0); + sdp_list_free(root_list, 0); + sdp_list_free(access_proto_list, 0); + sdp_list_free(svc_class_list, 0); + sdp_list_free(profile_list, free); + + if (err) + return -1; + + return 0; +} + +void +usage(char *argv0) +{ + fprintf(stderr, "%s [-dhrAESM] [-i hciX|bdaddr] [-L linger] [-c channel] [-f filter cmd] [cmd]\n", + basename(argv0)); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int rfcomm_raw_tty = 0, auth = 0, encryption = 0, + secure = 0, master = 0, linger = 0, sk, nsk, fd, lm , try = 30, + ctl, rc_channel = 1, filteri, dev; + char *argv0, *optarg, dst[18], devname[MAXPATHLEN], *replace, + *cmd, *oldcmd, *defaultcmd = NULL, *runcmd; + struct sockaddr_rc laddr, raddr; + struct rfcomm_dev_req req; + struct termios ti; + socklen_t alen; + bdaddr_t bdaddr; + struct linger l; + + char **cmds = NULL; + char **filteraddrs = NULL; + bdaddr_t **filterbds = NULL; + int filtern = 0; + + bacpy(&bdaddr, BDADDR_ANY); + + ARGBEGIN(argv0) { + case 'c': + rc_channel = atoi(EARGF(usage(argv0))); + break; + case 'd': + dodebug = 1; + break; + case 'i': + optarg = EARGF(usage(argv0)); + if (strncmp(optarg, "hci", 3) == 0) { + hci_devba(atoi(optarg + 3), &bdaddr); + } else { + str2ba(optarg, &bdaddr); + } + break; + case 'f': + ++filtern; + filteraddrs = realloc(filteraddrs, + filtern * sizeof(*filteraddrs)); + if (filteraddrs == NULL) + exit(1); + + cmds = realloc(cmds, filtern * sizeof(*cmds)); + if (cmds == NULL) + exit(1); + + filterbds = realloc(filterbds, filtern * sizeof(*filterbds)); + if (filterbds == NULL) + exit(1); + + filteraddrs[filtern-1] = EARGF(usage(argv0)); + argv++, argc--; + if (argc <= 0) + usage(argv0); + cmds[filtern-1] = argv[0]; + + filterbds[filtern-1] = malloc(sizeof(*filterbds)); + if (filterbds[filtern-1] == NULL) + exit(1); + str2ba(filteraddrs[filtern-1], filterbds[filtern-1]); + break; + case 'r': + rfcomm_raw_tty = 1; + break; + case 'A': + auth = 1; + break; + case 'E': + encryption = 1; + break; + case 'S': + secure = 1; + break; + case 'M': + master = 1; + break; + case 'L': + linger = atoi(EARGF(usage(argv0))); + break; + case 'h': + default: + usage(argv0); + } ARGEND; + + if (argc > 0) + defaultcmd = argv[0]; + if (defaultcmd == NULL && filtern < 0) + usage(argv[0]); + + for (filteri = 0; filteri < filtern; filteri++) { + ba2str(filterbds[filteri], dst); + debug("filter: %s (%s) -> %s\n", + filteraddrs[filteri], dst, cmds[filteri]); + } + debug("defaultcmd: %s\n", defaultcmd); + + setup_signals(); + + ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM); + if (ctl < 0) { + perror("Can't open RFCOMM control socket"); + return 1; + } + + laddr.rc_family = AF_BLUETOOTH; + bacpy(&laddr.rc_bdaddr, &bdaddr); + laddr.rc_channel = rc_channel; + + sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sk < 0) { + perror("Can't create RFCOMM socket"); + return 1; + } + + lm = 0; + if (master) + lm |= RFCOMM_LM_MASTER; + if (auth) + lm |= RFCOMM_LM_AUTH; + if (encryption) + lm |= RFCOMM_LM_ENCRYPT; + if (secure) + lm |= RFCOMM_LM_SECURE; + + if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { + perror("Can't set RFCOMM link mode"); + close(sk); + return 1; + } + + if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) { + perror("Can't bind RFCOMM socket"); + close(sk); + return 1; + } + + debug("Waiting for connection on channel %d\n", laddr.rc_channel); + + listen(sk, 10); + + sdp_advertise_service(sk, + "SPP Printer", + "00001101-0000-1000-8000-00805F9B34FB", + SERIAL_PORT_SVCLASS_ID, + SERIAL_PORT_PROFILE_ID, + "SPP Printer Emulation", + "rfcommd"); + + while (!__io_canceled) { + alen = sizeof(raddr); + nsk = accept(sk, (struct sockaddr *)&raddr, &alen); + + if (fork() != 0) + continue; + + ba2str(&raddr.rc_bdaddr, dst); + debug("Accept from %s\n", dst); + + for (filteri = 0; filteri < filtern; filteri++) { + if (!bacmp(filterbds[filteri], &raddr.rc_bdaddr)) { + runcmd = cmds[filteri]; + debug("filter found: %s -> %s\n", + filteraddrs[filteri], + runcmd); + break; + } + } + if (filteri >= filtern) { + if (defaultcmd != NULL) { + debug("running defaultcmd = %s\n", + defaultcmd); + runcmd = defaultcmd; + } else { + close(nsk); + continue; + } + } + + alen = sizeof(laddr); + if (getsockname(nsk, (struct sockaddr *)&laddr, &alen) < 0) { + perror("Can't get RFCOMM socket name"); + close(nsk); + continue; + } + + if (linger) { + l.l_onoff = 1; + l.l_linger = linger; + + if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) { + perror("Can't set linger option"); + close(nsk); + continue; + } + } + + memset(&req, 0, sizeof(req)); + req.dev_id = -1; + req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP); + + bacpy(&req.src, &laddr.rc_bdaddr); + bacpy(&req.dst, &raddr.rc_bdaddr); + req.channel = raddr.rc_channel; + + dev = ioctl(nsk, RFCOMMCREATEDEV, &req); + if (dev < 0) { + perror("Can't create RFCOMM TTY"); + close(sk); + continue; + } + + snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev); + while ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) { + if (errno == EACCES) { + perror("Can't open RFCOMM device"); + goto release; + } + + snprintf(devname, MAXPATHLEN - 1, "/dev/bluetooth/rfcomm/%d", dev); + if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) { + if (try--) { + snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev); + usleep(100 * 1000); + continue; + } + perror("Can't open RFCOMM device"); + goto release; + } + } + + if (rfcomm_raw_tty) { + tcflush(fd, TCIOFLUSH); + + cfmakeraw(&ti); + tcsetattr(fd, TCSANOW, &ti); + } + + ba2str(&req.dst, dst); + debug("Connection from %s to %s\n", dst, devname); + + /* Replace all occurences of '{}' with the rfcomm device path. */ + asprintf(&oldcmd, "%s", runcmd); + while ((replace = strstr(oldcmd, "{}"))) { + replace[0] = '%'; + replace[1] = 's'; + asprintf(&cmd, oldcmd, devname); + free(oldcmd); + oldcmd = cmd; + } + + debug("Executing %s\n", cmd); + + system(cmd); + free(cmd); + + close(fd); + close(nsk); +release: + memset(&req, 0, sizeof(req)); + req.dev_id = dev; + req.flags = (1 << RFCOMM_HANGUP_NOW); + ioctl(ctl, RFCOMMRELEASEDEV, &req); + } + + close(sk); + + return 0; +} +