This new version of bluetooth-config.sh tries to follow the style of other

shell scripts in base, removes the bogus "copyleft", adds the BeerWare license
header and uses rc.subr(8) new 'enable' keyword for adding entries in
rc.conf(5).

Submitted by:	erdgeist <erdgeist@erdgeist.org>
Approved by:	bapt
MFC after:	2 weeks
This commit is contained in:
Lars Engels 2019-01-14 08:23:47 +00:00
parent 28b740da38
commit e1bd727904
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=343020

View File

@ -1,8 +1,11 @@
#!/bin/sh
#-
# Copyleft 2019 Dirk Engling
#
# This script is released under the beerware license.
# ----------------------------------------------------------------------------
# "THE BEER-WARE LICENSE" (Revision 42):
# <erdgeist@erdgeist.org> wrote this file. As long as you retain this notice you
# can do whatever you want with this stuff. If we meet some day, and you think
# this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp
# ----------------------------------------------------------------------------
#
# $FreeBSD$
#
@ -11,8 +14,6 @@
exerr () { echo -e "Error: $*" >&2 ; exit 1; }
print_syntax () { echo -e "Syntax: $0 scan [-d device] [-n node]"; exit 1; }
# Assuming we are called to do the pair-new-device subcommand first
main() {
unset node device started bdaddresses retry
@ -22,132 +23,140 @@ shift
# Get command line options
while getopts :d:n: arg; do
case ${arg} in
d) device="$OPTARG";;
n) node="$OPTARG";;
?) print_syntax;;
esac
case ${arg} in
d) device="$OPTARG";;
n) node="$OPTARG";;
?) print_syntax;;
esac
done
# No use running without super user rights
[ $( id -u ) -eq 0 ] || exerr "$0 must modify files that belong to root. Re-run as root."
if [ $( id -u ) -ne 0 ]; then
exerr "$0 must modify files that belong to root. Re-run as root."
fi
known_nodes=$(/usr/sbin/hccontrol read_node_list 2>/dev/null | \
/usr/bin/tail -n +2 | /usr/bin/cut -d ' ' -f 1)
known_nodes=$( /usr/sbin/hccontrol read_node_list 2>/dev/null |\
/usr/bin/tail -n +2 | /usr/bin/cut -d ' ' -f 1 )
# Check if netgraph knows about any HCI nodes
if ! [ "${known_nodes}" ]; then
ng_nodes=$(/usr/sbin/ngctl list 2>/dev/null | \
/usr/bin/grep -o "Name: .* Type: ubt" | /usr/bin/cut -d ' ' -f 2)
ng_nodes=$( /usr/sbin/ngctl list 2>/dev/null | \
/usr/bin/grep -o "Name: .* Type: ubt" |/usr/bin/cut -d' ' -f2 )
[ "${ng_nodes}" ] || exerr "No Bluetooth host controllers found."
[ "${ng_nodes}" ] || exerr "No Bluetooth host controllers found."
unset found
for n in ${ng_nodes}; do
if [ "${n}" = "${node%hci}" ]; then
# If we found the node but its stack is not set up, do it now
/usr/sbin/service bluetooth start ${node%hci} || exit 1
found="YES"
fi
done
unset found
for n in ${ng_nodes}; do
if [ "${n}" = "${node%hci}" ]; then
# Found the node but its stack is not set up? Do it now.
/usr/sbin/service bluetooth start ${node%hci} || exit 1
found="YES"
fi
done
# If we have Bluetooth controller nodes without a set up stack,
# ask the user if we shall start it up
if ! [ "${found}" ]; then
printf "No usable Bluetooth host controllers were found.\n"
printf "These host controllers exist in the system:\n %s" " ${ng_nodes}"
read -p "Choose a host controller to set up: [${ng_nodes%% *}]" node
: ${node:="${ng_nodes%% *}"}
/usr/sbin/service bluetooth start ${node} || exit 1
fi
# If we have Bluetooth controller nodes without a set up stack,
# ask the user if we shall start it up
if ! [ "${found}" ]; then
printf "No usable Bluetooth host controllers were found.\n"
printf "These host controllers exist in the system:\n"
printf " %s\n" "${ng_nodes}"
prompt="Choose a host controller to set up: [${ng_nodes%% *}]"
read -p "${prompt}" node
: ${node:="${ng_nodes%% *}"}
/usr/sbin/service bluetooth start ${node} || exit 1
fi
# Re-read known nodes
known_nodes=$(/usr/sbin/hccontrol read_node_list 2>/dev/null | \
/usr/bin/tail -n +2 | /usr/bin/cut -d ' ' -f 1)
# check if we succeeded in bringing it up
[ "${known_nodes}" ] || exerr "Failed to set up Bluetooth stack"
# Re-read known nodes
known_nodes=$(/usr/sbin/hccontrol read_node_list 2>/dev/null |
/usr/bin/tail -n +2 | /usr/bin/cut -d ' ' -f 1 )
# check if we succeeded in bringing it up
[ "${known_nodes}" ] || exerr "Failed to set up Bluetooth stack"
fi
# if a node was requested on command line, check if it is there
if [ "${node}" ]; then
unset found
for n in ${known_nodes}; do
[ "${n}" = "${node}" ] && found="YES"
[ "${n}" = "${node}hci" ] && node="${node}hci" && found="YES"
done
[ "${found}" ] || exerr "Node ${node} not found"
unset found
for n in ${known_nodes}; do
[ "${n}" = "${node}" ] && found="YES"
[ "${n}" = "${node}hci" ] && node="${node}hci" && found="YES"
done
[ "${found}" ] || exerr "Node ${node} not found"
fi
[ "${node}" ] && node="-n ${node}"
while ! [ "${bdaddresses}" ]; do
retry=X${retry}
printf "Scanning for new Bluetooth devices (Attempt %d of 5) ... " ${#retry}
bdaddresses=$( /usr/sbin/hccontrol -N ${node} inquiry 2>/dev/null | \
/usr/bin/grep -o "BD_ADDR: .*" | /usr/bin/cut -d ' ' -f 2 )
retry=X${retry}
printf "Scanning for new Bluetooth devices (Attempt %d of 5) ... " \
${#retry}
bdaddresses=$( /usr/sbin/hccontrol -N ${node} inquiry 2>/dev/null |
/usr/bin/grep -o "BD_ADDR: .*" | /usr/bin/cut -d ' ' -f 2 )
# Count entries and, if a device was requested on command line,
# try to find it
unset found count
for bdaddress in ${bdaddresses}; do
count=X${count}
if [ "${bdaddress}" = "${device}" ]; then
found=YES
bdaddresses="${device}"
count=X
break
fi
done
# Count entries and, if a device was requested on command line,
# try to find it
unset found count
for bdaddress in ${bdaddresses}; do
count=X${count}
if [ "${bdaddress}" = "${device}" ]; then
found=YES
bdaddresses="${device}"
count=X
break
fi
done
# If device was requested on command line but is not found,
# or no devices found at all, rescan until retry is exhausted
if ! [ "${found}" -o "${count}" -a -z "${device}" ]; then
printf "failed.\n"
if [ "${#retry}" -eq 5 ]; then
[ "${device}" ] && exerr "Device ${device} not found"
exerr "No new Bluetooth devices found"
fi
unset bdaddresses
sleep 2
continue
fi
# If device was requested on command line but is not found,
# or no devices found at all, rescan until retry is exhausted
if ! [ "${found}" -o "${count}" -a -z "${device}" ]; then
printf "failed.\n"
if [ "${#retry}" -eq 5 ]; then
[ "${device}" ] && exerr "Device ${device} not found"
exerr "No new Bluetooth devices found"
fi
unset bdaddresses
sleep 2
continue
fi
[ ${#count} -gt 1 ] && plural=s || plural=''
printf "done.\nFound %d new bluetooth device%s (scanning for names):\n" ${#count} ${plural}
[ ${#count} -gt 1 ] && plural=s || plural=''
printf "done.\nFound %d new bluetooth device%s " ${#count} ${plural}
printf "(now scanning for names):\n"
# Looping again for the faster feedback
unset count
for bdaddress in ${bdaddresses}; do
count=X${count}
bdname=$( /usr/bin/bthost -b "${bdaddress}" 2>/dev/null )
friendlyname=$( /usr/sbin/hccontrol Remote_Name_Request ${bdaddress} 2> /dev/null | \
/usr/bin/grep -o "Name: .*" | /usr/bin/cut -d ' ' -f 2- )
# Looping again for the faster feedback
unset count
for bdaddress in ${bdaddresses}; do
count=X${count}
bdname=$( /usr/bin/bthost -b "${bdaddress}" 2>/dev/null )
friendlyname=$( /usr/sbin/hccontrol Remote_Name_Request \
${bdaddress} 2> /dev/null |
/usr/bin/grep -o "Name: .*" |/usr/bin/cut -d ' ' -f 2- )
# sdpcontrol should be able to pull vendor and product id via sdp
printf "[%2d] %s\t\"%s\" (%s)\n" ${#count} "${bdaddress}" "${friendlyname}" "${bdname}"
# sdpcontrol should be able to pull vendor + product id via sdp
printf "[%2d] %s\t\"%s\" (%s)\n" ${#count} "${bdaddress}" \
"${friendlyname}" "${bdname}"
eval bdaddress_${#count}=\${bdaddress}
eval bdname_${#count}=\${bdname}
eval friendlyname_${#count}=\${friendlyname}
done
eval bdaddress_${#count}=\${bdaddress}
eval bdname_${#count}=\${bdname}
eval friendlyname_${#count}=\${friendlyname}
done
# If a device was pre-selected, do not query the user
[ "${device}" ] && topair=1 || unset topair
# If a device was pre-selected, do not query the user
[ "${device}" ] && topair=1 || unset topair
# Even if only one device was found, user may chose 0 to rescan
while ! [ "${topair}" ]; do
if [ ${#count} -eq 1 ]; then
read -p "Select device to pair with [1, or 0 to rescan]: " topair
else
read -p "Select device to pair with [1-${#count}, or 0 to rescan]: " topair
fi
if ! [ "${topair}" -ge 0 -a "${topair}" -le "${#count}" ] 2>/dev/null ; then
printf "Value out of range: %s.\n" {topair}
unset topair
fi
done
# Even if only one device was found, user may chose 0 to rescan
while ! [ "${topair}" ]; do
prompt="Select device to pair with [1"
[ ${#count} -gt 1 ] && prompt="${prompt}-${#count}"
read -p "${prompt}, or 0 to rescan]: " topair
if ! [ "${topair}" -ge 0 -a "${topair}" -le "${#count}" ] \
2>/dev/null ; then
printf "Value out of range: %s.\n" {topair}
unset topair
fi
done
[ "${topair}" -eq "0" ] && unset bdaddresses retry
[ "${topair}" -eq "0" ] && unset bdaddresses retry
done
eval bdaddress=\${bdaddress_${topair}}
@ -156,20 +165,24 @@ eval friendlyname=\${friendlyname_${topair}}
# Do we need to add an entry to /etc/bluetooth/hosts?
if ! [ "${bdname}" ]; then
printf "\nAdding device ${bdaddress} to /etc/bluetooth/hosts.\n"
printf "\nAdding device ${bdaddress} to /etc/bluetooth/hosts.\n"
while ! [ "${bdname}" ]; do
read -p "Enter friendly name. [${friendlyname}]: " REPLY
: ${REPLY:="${friendlyname}"}
while ! [ "${bdname}" ]; do
read -p "Enter friendly name. [${friendlyname}]: " _r
: ${_r:="${friendlyname}"}
if [ "${REPLY}" ]; then
# Remove white space and non-friendly characters
bdname=$( printf "%s" "${REPLY}" | tr -c '[:alnum:]-,.' _ )
[ "${REPLY}" != "${bdname}" ] && printf "Notice: Using sanitized name \"%s\" in /etc/bluetooth/hosts.\n" "${bdname}"
fi
done
if [ "${_r}" ]; then
# Remove white space and non-friendly characters
bdname=$( printf "%s" "${_r}" | tr -c '[:alnum:]-,.' _ )
if [ "${_r}" != "${bdname}" ]; then
printf "Notice: Using sanitized name"
printf "\"%s\" in /etc/bluetooth/hosts.\n" \
"${bdname}"
fi
fi
done
printf "%s\t%s\n" "${bdaddress}" "${bdname}" >> /etc/bluetooth/hosts
printf "%s\t%s\n" "${bdaddress}" "${bdname}" >> /etc/bluetooth/hosts
fi
# If scanning for the name did not succeed, resort to bdname
@ -183,86 +196,109 @@ fi
# Also we cannot really modify the PIN in an existing entry. So we
# need to prompt the user to manually do it and restart this script
if ! /usr/sbin/service hcsecd enabled; then
printf "\nWarning: hcsecd is not enabled.\nThis daemon manages pairing requests.\n"
read -p "Enable hcsecd? [yes]: " REPLY
case "${REPLY}" in no|n|NO|N|No|nO) ;; *) /usr/sbin/sysrc hcsecd_enable="YES";; esac
printf "\nWarning: hcsecd is not enabled.\n"
printf "This daemon manages pairing requests.\n"
read -p "Enable hcsecd? [yes]: " _r
case "${_r}" in
no|n|NO|N|No|nO) ;;
*) /usr/sbin/service hcsecd enable;;
esac
fi
secd_config=$( /usr/sbin/sysrc -n hcsecd_config )
secd_entries=$( /usr/bin/grep -Eo "bdaddr[[:space:]]+(${bdaddress}|${bdname})" ${secd_config} | awk '{ print $2; }' )
secd_entries=$( /usr/bin/grep -Eo "bdaddr[[:space:]]+(${bdaddress}|${bdname})" \
${secd_config} | awk '{ print $2; }' )
if [ "${secd_entries}" ]; then
printf "\nWarning: An entry for device %s is already present in %s.\n" ${secd_entries} ${secd_config}
printf "To modify pairing information, edit this file and run\n service hcsecd restart\n"
read -p "Continue? [yes]: " REPLY
case "${REPLY}" in no|n|NO|N|No|nO) exit;; esac
printf "\nWarning: An entry for device %s is already present in %s.\n" \
${secd_entries} ${secd_config}
printf "To modify pairing information, edit this file and run\n"
printf " service hcsecd restart\n"
read -p "Continue? [yes]: " _r
case "${_r}" in no|n|NO|N|No|nO) exit;; esac
else
printf "\nWriting pairing information description block to %s.\n" ${secd_config}
printf "(To get PIN, put device in pairing mode first.)\n"
read -p "Enter PIN [nopin]: " pin
[ "${pin}" ] && pin=\""${pin}"\" || pin="nopin"
printf "\nWriting pairing information description block to %s.\n" \
${secd_config}
printf "(To get PIN, put device in pairing mode first.)\n"
read -p "Enter PIN [nopin]: " pin
[ "${pin}" ] && pin=\""${pin}"\" || pin="nopin"
# Write out new hcsecd config block
printf "\ndevice {\n\tbdaddr\t%s;\n\tname\t\"%s\";\n\tkey\tnokey\;\n\tpin\t%s\;\n}\n" \
"${bdaddress}" "${friendlyname}" "${pin}" >> ${secd_config}
# Write out new hcsecd config block
printf "\ndevice {\n\tbdaddr\t%s;\n\tname\t\"%s\";\n\tkey\tnokey\;\n\tpin\t%s\;\n}\n" \
"${bdaddress}" "${friendlyname}" "${pin}" >> ${secd_config}
# ... and make daemon reload config, TODO: hcsecd should provide a reload hook
/usr/sbin/service hcsecd restart
# ... and make daemon reload config
# TODO: hcsecd should provide a reload hook
/usr/sbin/service hcsecd onerestart
# TODO: we should check if hcsecd succeeded pairing and revert to an old version
# of hcsecd.conf so we can undo adding the block above and retry with a new PIN
# also, if there's a way to force devices to re-pair, try this
# TODO: we should check if hcsecd succeeded pairing and revert to an
# old version of hcsecd.conf so we can undo adding the block above and
# retry with a new PIN
# also, if there's a way to force devices to re-pair, try this
fi
# now check for specific services to be provided by the device
# first up: HID
if /usr/sbin/sdpcontrol -a "${bdaddress}" search HID | \
/usr/bin/grep -q "^Record Handle: "; then
/usr/sbin/sdpcontrol -a "${bdaddress}" search HID | \
/usr/bin/grep -q "^Record Handle: " || exit 0
printf "\nThis device provides human interface device services.\n"
read -p "Set it up? [yes]: " REPLY
case "${REPLY}" in no|n|NO|N|No|nO) ;;
*)
if ! /usr/sbin/service bthidd enabled; then
printf "\nWarning: bthidd is not enabled."
printf "\nThis daemon manages Bluetooth HID devices.\n"
read -p "Enable bthidd? [yes]: " REPLY
case "${REPLY}" in no|n|NO|N|No|nO) ;; *) /usr/sbin/sysrc bthidd_enable="YES";; esac
fi
printf "\nThis device provides human interface device services.\n"
read -p "Set it up? [yes]: " _r
case "${_r}" in
no|n|NO|N|No|nO) exit 0;;
*);;
esac
# Check if bthidd already knows about this device
bthidd_known=$( /usr/sbin/bthidcontrol -a "${bdaddress}" known | \
/usr/bin/grep "${bdaddress}" )
if [ "${bthidd_known}" ]; then
printf "Notice: Device %s already known to bthidd.\n" "${bdaddress}"
else
bthidd_config=$( /usr/sbin/sysrc -n bthidd_config )
printf "Writing HID descriptor block to %s ... " "${bthidd_config}"
/usr/sbin/bthidcontrol -a "${bdaddress}" query >> "${bthidd_config}"
# Here we have found an HID and were asked to set it up
# NOTE: look out for the two exit 0 above if you extend this script
# Re-read config to see if we succeeded adding the device
bthidd_known=$( /usr/sbin/bthidcontrol -a "${bdaddress}" known | \
grep "${bdaddress}" )
if ! [ "${bthidd_known}" ]; then
printf "failed.\n"
else
printf "success.\nTo re-read its config, bthidd must be restarted.\n"
printf "Warning: If a Bluetooth keyboard is being used, the connection might be lost.\n"
printf "It can be manually restarted later with\n service bthidd restart\n"
read -p "Restart bthidd now? [yes]: " REPLY
case "${REPLY}" in no|n|NO|N|No|nO) ;; *) /usr/sbin/service bthidd restart;; esac
fi
fi
;;
esac
if ! /usr/sbin/service bthidd enabled; then
printf "\nWarning: bthidd is not enabled."
printf "\nThis daemon manages Bluetooth HID devices.\n"
read -p "Enable bthidd? [yes]: " _r
case "${_r}" in
no|n|NO|N|No|nO) ;;
*) /usr/sbin/service bthidd enable;;
esac
fi
# Check if bthidd already knows about this device
bthidd_known=$( /usr/sbin/bthidcontrol -a "${bdaddress}" known | \
/usr/bin/grep "${bdaddress}" )
if [ "${bthidd_known}" ]; then
printf "Notice: Device %s already known to bthidd.\n" "${bdaddress}"
return 0
fi
bthidd_config=$( /usr/sbin/sysrc -n bthidd_config )
printf "Writing HID descriptor block to %s ... " "${bthidd_config}"
/usr/sbin/bthidcontrol -a "${bdaddress}" query >> "${bthidd_config}"
# Re-read config to see if we succeeded adding the device
bthidd_known=$( /usr/sbin/bthidcontrol -a "${bdaddress}" known | \
grep "${bdaddress}" )
if ! [ "${bthidd_known}" ]; then
printf "failed.\n"
else
printf "success.\nTo re-read its config, bthidd must be restarted.\n"
printf "Warning: If a Bluetooth keyboard is being used, the connection"
printf "might be lost.\n"
printf "It can be manually restarted later with\n"
printf " service bthidd restart\n"
read -p "Restart bthidd now? [yes]: " _r
case "${_r}" in
no|n|NO|N|No|nO) ;;
*) /usr/sbin/service bthidd onerestart;;
esac
fi
}
# After function definitions, main() can use them
main "$@"
exit
exit 0
# TODO
# * If device is a keyboard, offer a text entry test field and if it does