commit 70f725160626e24b13cdd304ed86b1184783fc97
Author: Christoph Lohmann <20h@r-36.net>
Date: Fri, 25 Mar 2022 18:10:39 +0100
Initial commit of rfcommd.
Diffstat:
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;
+}
+