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