summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2016-05-02 00:07:51 +0200
committerDirk Engling <erdgeist@erdgeist.org>2016-05-02 00:07:51 +0200
commitb8ca6f7735b9735c2ce2b28c337eae3054146feb (patch)
tree0ede64a0cd3cd89d47f8b024bc45b0925a8ab0f0
Move over here from https://reviews.freebsd.org/D3778HEADmaster
-rw-r--r--bluetooth-config257
1 files changed, 257 insertions, 0 deletions
diff --git a/bluetooth-config b/bluetooth-config
new file mode 100644
index 0000000..daaf074
--- /dev/null
+++ b/bluetooth-config
@@ -0,0 +1,257 @@
1#!/bin/sh
2
3# Define our bail out shortcut
4exerr () { echo -e "Error: $*" >&2 ; exit 1; }
5
6# Assuming we are called to do the pair-new-device subcommand first
7
8main() {
9unset node device bdaddresses retry
10
11[ $( id -u ) -eq 0 ] || exerr "$0 must modify files that belong to root. Re-run as root."
12
13# Get command line options
14while getopts :a:n: arg; do
15 case ${arg} in
16 n) node="$OPTARG";;
17 a) device="$OPTARG";;
18 ?) exerr "Syntax: $0 [-n node] [-a address] cmd";;
19 esac
20done
21
22known_nodes=$(/usr/sbin/hccontrol read_node_list 2>/dev/null | \
23 /usr/bin/tail -n +2 | /usr/bin/cut -d ' ' -f 1)
24
25# Check if netgraph knows about any HCI nodes
26if ! [ "${known_nodes}" ]; then
27 ng_nodes=$(/usr/sbin/ngctl list 2>/dev/null | \
28 /usr/bin/grep -o "Name: .* Type: ubt" | /usr/bin/cut -d ' ' -f 2)
29
30 [ "${ng_nodes}" ] || exerr "No Bluetooth host controllers found."
31
32 unset found
33 for n in ${ng_nodes}; do
34 if [ "${n}" = "${node%hci}" ]; then
35 # If we found the node but its stack is not set up, do it now
36 /usr/sbin/service bluetooth start ${node%hci} || exit 1
37 found="YES"
38 fi
39 done
40
41 # If we have Bluetooth controller nodes without a set up stack,
42 # ask the user if we shall start it up
43 if ! [ "${found}" ]; then
44 printf "No usable Bluetooth host controllers were found.\n"
45 printf "These host controllers exist in the system:\n %s" " ${ng_nodes}"
46 read -p "Choose a host controller to set up: [${ng_nodes%% *}]" node
47 : ${node:="${ng_nodes%% *}"}
48 /usr/sbin/service bluetooth start ${node} || exit 1
49 fi
50
51 # Re-read known nodes
52 known_nodes=$(/usr/sbin/hccontrol read_node_list 2>/dev/null | \
53 /usr/bin/tail -n +2 | /usr/bin/cut -d ' ' -f 1)
54 # check if we succeeded in bringing it up
55 [ "${known_nodes}" ] || exerr "Failed to set up Bluetooth stack"
56fi
57
58# If a node was requested on command line, check if it is there
59if [ "${node}" ]; then
60 unset found
61 for n in ${known_nodes}; do
62 [ "${n}" = "${node}" ] && found="YES"
63 [ "${n}" = "${node}hci" ] && node="${node}hci" && found="YES"
64 done
65 [ "${found}" ] || exerr "Node ${node} not found"
66fi
67
68[ "${node}" ] && node="-n ${node}"
69
70while ! [ "${bdaddresses}" ]; do
71 retry=X${retry}
72 printf "Scanning for new Bluetooth devices (Attempt %d of 5) ... " ${#retry}
73 bdaddresses=$( /usr/sbin/hccontrol -N ${node} inquiry 2>/dev/null | \
74 /usr/bin/grep -o "BD_ADDR: .*" | /usr/bin/cut -d ' ' -f 2 )
75
76 # Count entries and, if a device was requested on command line,
77 # try to find it
78 unset found count
79 for bdaddress in ${bdaddresses}; do
80 count=X${count}
81 if [ "${bdaddress}" = "${device}" ]; then
82 found=YES
83 bdaddresses="${device}"
84 count=X
85 break
86 fi
87 done
88
89 # If device was requested on command line but is not found,
90 # or no devices found at all, rescan until retry is exhausted
91 if ! [ "${found}" -o "${count}" -a -z "${device}" ]; then
92 printf "failed.\n"
93 if [ "${#retry}" -eq 5 ]; then
94 [ "${device}" ] && exerr "Device ${device} not found"
95 exerr "No new Bluetooth devices found"
96 fi
97 unset bdaddresses
98 sleep 2
99 continue
100 fi
101
102 printf "done.\nFound %d new Bluetooth devic%.*s (scanning for names):\n" ${#count} ${#count} es
103
104 # Looping again for the faster feedback
105 unset count
106 for bdaddress in ${bdaddresses}; do
107 count=X${count}
108 bdname=$( /usr/bin/bthost -b "${bdaddress}" 2>/dev/null )
109 friendlyname=$( /usr/sbin/hccontrol Remote_Name_Request ${bdaddress} 2> /dev/null | \
110 /usr/bin/grep -o "Name: .*" | /usr/bin/cut -d ' ' -f 2- )
111
112 # sdpcontrol should be able to pull vendor and product id via sdp
113 printf "[%2d] %s\t\"%s\" (%s)\n" ${#count} "${bdaddress}" "${friendlyname}" "${bdname}"
114
115 eval bdaddress_${#count}=\${bdaddress}
116 eval bdname_${#count}=\${bdname}
117 eval friendlyname_${#count}=\${friendlyname}
118 done
119
120 # If a device was pre-selected, do not query the user
121 [ "${device}" ] && topair=1 || unset topair
122
123 # Even if only one device was found, user may chose 0 to rescan
124 while ! [ "${topair}" ]; do
125 read -p "Select device to pair with [1-${#count}, 0 to rescan]: " topair
126 if ! [ "${topair}" -ge 0 -a "${topair}" -le "${#count}" ] 2>/dev/null ; then
127 printf "Value out of range: %s.\n" {topair}
128 unset topair
129 fi
130 done
131
132 [ "${topair}" -eq "0" ] && unset bdaddresses retry
133done
134
135eval bdaddress=\${bdaddress_${topair}}
136eval bdname=\${bdname_${topair}}
137eval friendlyname=\${friendlyname_${topair}}
138
139# Do we need to add an entry to /etc/bluetooth/hosts?
140if ! [ "${bdname}" ]; then
141 printf "\nAdding device ${bdaddress} to /etc/bluetooth/hosts.\n"
142
143 while ! [ "${bdname}" ]; do
144 read -p "Enter friendly name. [${friendlyname}]: " REPLY
145 : ${REPLY:="${friendlyname}"}
146
147 if [ "${REPLY}" ]; then
148 # Remove white space and non-friendly characters
149 bdname=$( printf "%s" "${REPLY}" | tr -c '[:alnum:]-,.' _ )
150 [ "${REPLY}" != "${bdname}" ] && printf "Notice: Using sanitized name \"%s\" in /etc/bluetooth/hosts.\n" "${bdname}"
151 fi
152 done
153
154 printf "%s\t%s\n" "${bdaddress}" "${bdname}" >> /etc/bluetooth/hosts
155fi
156
157# If scanning for the name did not succeed, resort to bdname
158: ${friendlyname:="${bdname}"}
159
160# Now over to hcsecd
161
162# Since hcsecd does not allow querying for known devices, we need to
163# check for bdaddr entries manually.
164#
165# Also we cannot really modify the PIN in an existing entry. So we
166# need to prompt the user to manually do it and restart this script.
167if ! /usr/sbin/service hcsecd enabled; then
168 printf "\nWarning: hcsecd is not enabled.\nThis daemon manages pairing requests.\n"
169 read -p "Enable hcsecd? [yes]: " REPLY
170 case "${REPLY}" in no|n|NO|N|No|nO) ;; *) /usr/sbin/sysrc hcsecd_enable="YES";; esac
171fi
172secd_config=$( /usr/sbin/sysrc -n hcsecd_config )
173secd_entries=$( /usr/bin/grep -Eo "bdaddr[[:space:]]+(${bdaddress}|${bdname})" ${secd_config} | awk '{ print $2; }' )
174
175if [ "${secd_entries}" ]; then
176 printf "\nWarning: An entry for device %s is already present in %s.\n" ${secd_entries} ${secd_config}
177 printf "To modify pairing information, edit this file and run\n service hcsecd restart\n"
178 read -p "Continue? [yes]: " REPLY
179 case "${REPLY}" in no|n|NO|N|No|nO) exit;; esac
180else
181 printf "\nWriting pairing information description block to %s.\n" ${secd_config}
182 printf "(To get PIN, put device in pairing mode first.)\n"
183 read -p "Enter PIN [nopin]: " pin
184 [ "${pin}" ] && pin=\""${pin}"\" || pin="nopin"
185
186 # Write out new hcsecd config block
187 printf "\ndevice {\n\tbdaddr\t%s;\n\tname\t\"%s\";\n\tkey\tnokey\;\n\tpin\t%s\;\n}\n" \
188 "${bdaddress}" "${friendlyname}" "${pin}" >> ${secd_config}
189
190 # ... and make daemon reload config, TODO: hcsecd should provide a reload hook
191 /usr/sbin/service hcsecd restart
192
193 # TODO: We should check if hcsecd succeeded pairing and revert to an old version
194 # of hcsecd.conf so we can undo adding the block above and retry with a new PIN
195 # also, if there's a way to force devices to re-pair, try this
196fi
197
198# Now check for specific services to be provided by the device
199# First up: HID
200
201if /usr/sbin/sdpcontrol -a "${bdaddress}" search HID | \
202 /usr/bin/grep -q "^Record Handle: "; then
203
204 printf "\nThis device provides human interface device services.\n"
205 read -p "Set it up? [yes]: " REPLY
206 case "${REPLY}" in no|n|NO|N|No|nO) ;;
207 *)
208 if ! /usr/sbin/service bthidd enabled; then
209 printf "\nWarning: bthidd is not enabled."
210 printf "\nThis daemon manages Bluetooth HID devices.\n"
211 read -p "Enable bthidd? [yes]: " REPLY
212 case "${REPLY}" in no|n|NO|N|No|nO) ;; *) /usr/sbin/sysrc bthidd_enable="YES";; esac
213 fi
214
215 # Check if bthidd already knows about this device
216 bthidd_known=$( /usr/sbin/bthidcontrol -a "${bdaddress}" known | \
217 /usr/bin/grep "${bdaddress}" )
218 if [ "${bthidd_known}" ]; then
219 printf "Notice: Device %s already known to bthidd.\n" "${bdaddress}"
220 else
221 bthidd_config=$( /usr/sbin/sysrc -n bthidd_config )
222 printf "Writing HID descriptor block to %s ... " "${bthidd_config}"
223 /usr/sbin/bthidcontrol -a "${bdaddress}" query >> "${bthidd_config}"
224
225 # Re-read config to see if we succeeded adding the device
226 bthidd_known=$( /usr/sbin/bthidcontrol -a "${bdaddress}" known | \
227 grep "${bdaddress}" )
228 if ! [ "${bthidd_known}" ]; then
229 printf "failed.\n"
230 else
231 printf "success.\nTo re-read its config, bthidd must be restarted.\n"
232 printf "Warning: If a Bluetooth keyboard is being used, the connection might be lost.\n"
233 printf "It can be manually restarted later with\n service bthidd restart\n"
234 read -p "Restart bthidd now? [yes]: " REPLY
235 case "${REPLY}" in no|n|NO|N|No|nO) ;; *) /usr/sbin/service bthidd restart;; esac
236 fi
237 fi
238 ;;
239 esac
240fi
241
242}
243
244# After function definitions, main() can use them
245main "$@"
246
247exit
248
249# TODO
250# * If device is a keyboard, offer a text entry test field and if it does
251# not succeed, leave some clues for debugging (i.e. if the node responds
252# to pings, maybe switch keyboard on/off, etc)
253# * Same if device is a mouse, i.e. hexdump /dev/sysmouse.
254# * If device offers DUN profiles, ask the user if an entry in
255# /etc/ppp/ppp.conf should be created
256# * If OPUSH or SPP is offered, refer to the respective man pages to give
257# some clues how to continue