Initial commit of rfcommd. - rfcommd - RFCOMM daemon to run filters on clients.
 (HTM) git clone git://bitreich.org/rfcommd/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/rfcommd/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 70f725160626e24b13cdd304ed86b1184783fc97
 (HTM) Author: Christoph Lohmann <20h@r-36.net>
       Date:   Fri, 25 Mar 2022 18:10:39 +0100
       
       Initial commit of rfcommd.
       
       Diffstat:
         A Makefile                            |      53 ++++++++++++++++++++++++++++++
         A arg.h                               |      46 +++++++++++++++++++++++++++++++
         A etc/conf.d/rfcommd.gentoo           |       6 ++++++
         A etc/init.d/rfcommd.gentoo           |      29 +++++++++++++++++++++++++++++
         A filters/euroklavfilter              |      40 +++++++++++++++++++++++++++++++
         A filters/spirofilter                 |      39 +++++++++++++++++++++++++++++++
         A rfcommd.c                           |     595 +++++++++++++++++++++++++++++++
       
       7 files changed, 808 insertions(+), 0 deletions(-)
       ---
 (DIR) 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
       +
 (DIR) 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
 (DIR) 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 {}")
       +
 (DIR) 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"
       +}
       +
 (DIR) 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
       +
 (DIR) 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
       +
 (DIR) 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;
       +}
       +