blob: 3723f9bf601f14f1d61534a93889cd6c2a54f2fc [file] [log] [blame]
#!/bin/sh
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Define some script constants.
scriptName="${0##*/}"
BRLTTY_USER="brltty"
BRLTTY_GROUP="brltty"
BRLTTY_SUBDIR="brltty"
BRLTTY_VARDIR="/var/lib/${BRLTTY_SUBDIR}"
BRLTTY_RUNDIR="/run/${BRLTTY_SUBDIR}"
BRLTTY_CFGFILE="/etc/brltty.conf"
BRLTTY_DEVFILE="${BRLTTY_RUNDIR}/device"
BRLTTY_DRVFILE="${BRLTTY_RUNDIR}/driver"
BRLTTY_ENVFILE="${BRLTTY_RUNDIR}/env"
BRLTTY_LOGFILE="${BRLTTY_RUNDIR}/log"
BRLTTY_PIDFILE="${BRLTTY_RUNDIR}/pid"
BRLAPI_SUBDIR="BrlAPI"
BRLAPI_SOCDIR="${BRLTTY_RUNDIR}/${BRLAPI_SUBDIR}"
# The number (0) must be kept in sync with the Host api parameter in
# brltty.conf.
BRLAPI_SOCFILE="${BRLAPI_SOCDIR}/0"
showUsageSummary() {
cat <<END-OF-USAGE-SUMMARY
Usage: ${scriptName} [-option ...] [action [argument ...]]
These options may be specified:
-h show this usage summary and then exit
-q be quiet - don't show informative output
-v be verbose - show host commands, etc
These actions may be specified:
device
driver
pid
prepare
run [brltty-argument ...]
stop
test
wait
END-OF-USAGE-SUMMARY
}
putLine() {
local line="${1}"
"${quietRequested}" || echo "${line}"
}
putFile() {
local file="${1}"
[ -f "${file}" ] || return 8
"${quietRequested}" && return 0
cat "${file}"
}
writeMessage() {
local message="${1}"
echo >&2 "${scriptName}: ${message}"
}
syntaxError() {
local message="${1}"
writeMessage "${message}"
exit 2
}
# Set the option defaults.
helpRequested=false
quietRequested=false
verboseRequested=false
# Process the options.
while getopts ":hqv" option
do
case "${option}"
in
h) helpRequested=true;;
q) quietRequested=true;;
v) verboseRequested=true;;
:) syntaxError "missing operand: -${OPTARG}";;
\?) syntaxError "unknown option: -${OPTARG}";;
*) syntaxError "unimplemented option: -${option}";;
esac
done
# Remove the options from the command line.
shift $((OPTIND - 1))
# If help was requested then just show the usage summary.
"${helpRequested}" && {
showUsageSummary
exit 0
}
# Set the default action (and its arguments).
[ "${#}" -eq 0 ] && set -- run
log() {
local message="${*}"
[ -t 2 ] && writeMessage "${message}"
logger -t brltty_udev "${Message}"
}
executeHostCommand() {
"${verboseRequested}" && writeMessage "host command: ${*}"
"${@}"
local exitStatus="${?}"
"${verboseRequested}" && {
[ "${exitStatus}" -eq 0 ] || writeMessage "exit status: ${exitStatus}"
}
return "${exitStatus}"
}
# Ensure that the needed directories exist and have the right ownership and permissions.
prepareNeededDirectories() {
# NOTE: Chrome is observing ${BRLAPI_SOCDIR} for changes.
# It is important to only remove world-readability *after* setting the
# group ownership so that the browser doesn't lose read access.
# This is a race that would cause chrome to stop observing the directory
# and therefore not get notified when a braille display is connected.
set -- "${BRLTTY_VARDIR}" "${BRLTTY_RUNDIR}" "${BRLAPI_SOCDIR}"
executeHostCommand mkdir -p -m 0755 "${@}"
executeHostCommand chown -R "${BRLTTY_USER}:${BRLTTY_GROUP}" "${@}"
executeHostCommand chmod 750 "${@}"
}
# Send a signal to the process that owns the BrlAPI server socket.
sendSignal() {
local signal="${1}"
# This must be the last command so that we return its exit status.
[ -e "${BRLAPI_SOCFILE}" ] && {
executeHostCommand fuser -s -k -"${signal}" "${BRLAPI_SOCFILE}"
}
}
isProcessRunning() {
# This must be the last command so that we return its exit status.
sendSignal 0
}
# Stop any brltty processes that might be holding on to the
# BrlAPI server socket. We try hard, because failure to free up the socket
# will lock the braille user out from the machine!
stopProcess() {
sendSignal TERM || return 0
log "waiting for brltty to terminate"
local delay=0.1 # in seconds
local limit=5
local try=0
while [ $((try += 1)) -le "${limit}" ]
do
executeHostCommand sleep "${delay}"
sendSignal TERM || return 0
done
log "forcing brltty to terminate"
sendSignal KILL
}
noMoreParameters() {
[ "${#}" -eq 0 ] || syntaxError "too many parameters"
}
action_device() {
noMoreParameters "${@}"
putFile "${BRLTTY_DEVFILE}"
exit "${?}"
}
action_driver() {
noMoreParameters "${@}"
putFile "${BRLTTY_DRVFILE}"
exit "${?}"
}
action_pid() {
noMoreParameters "${@}"
putFile "${BRLTTY_PIDFILE}"
exit "${?}"
}
action_prepare() {
noMoreParameters "${@}"
prepareNeededDirectories
exit "${?}"
}
# Run brltty within a minijail.
action_run() {
stopProcess
prepareNeededDirectories
# Files created by the daemon shouldn't be accessible by users not in the
# brltty group.
executeHostCommand umask 0007
# If the Bluetooth address is defined then use that device.
[ -n "${ADDRESS}" ] && {
executeHostCommand export BRLTTY_BRAILLE_DEVICE="bluetooth:${ADDRESS}"
}
echo "${BRLTTY_BRAILLE_DEVICE}" >"${BRLTTY_DEVFILE}"
echo "${BRLTTY_BRAILLE_DRIVER}" >"${BRLTTY_DRVFILE}"
log "starting brltty -" \
"device:${BRLTTY_BRAILLE_DEVICE:-unknown}" \
"driver:${BRLTTY_BRAILLE_DRIVER:-unknown}"
executeHostCommand export BRLTTY_CONFIGURATION_FILE="${BRLTTY_CFGFILE}"
executeHostCommand export BRLTTY_UPDATABLE_DIRECTORY="${BRLTTY_VARDIR}"
executeHostCommand export BRLTTY_WRITABLE_DIRECTORY="${BRLTTY_RUNDIR}"
executeHostCommand export BRLTTY_PID_FILE="${BRLTTY_PIDFILE}"
executeHostCommand export BRLTTY_LOG_FILE="${BRLTTY_LOGFILE}"
# The provided arguments for brltty are in ${@}.
# Add ours before them in case they contain syntax errors.
set -- -E "${@}" # Allow options to be specified via environment variables.
set -- -n "${@}" # Keep brltty in the foreground.
set -- -q "${@}" # Suppress the start message.
# Run brltty within a restricted environment.
env >"${BRLTTY_ENVFILE}"
executeHostCommand exec \
/sbin/minijail0 -u "${BRLTTY_USER}" -g "${BRLTTY_GROUP}" -G -- \
/bin/brltty "${@}"
# We shouldn't get here - but we might because exec can fail.
exit "${?}"
}
action_stop() {
noMoreParameters "${@}"
stopProcess
exit "${?}"
}
action_test() {
noMoreParameters "${@}"
isProcessRunning
local exitStatus="${?}"
[ "${exitStatus}" -eq 0 ] && putLine "running" || putLine "stopped"
exit "${exitStatus}"
}
action_wait() {
noMoreParameters "${@}"
local delay=0.5
local limit=10
local try=0
while isProcessRunning
do
[ $((try += 1)) -gt "${limit}" ] && {
log "brltty has not terminated"
exit 9
}
executeHostCommand sleep "${delay}"
done
exit 0
}
[ "${#}" -eq 0 ] && syntaxError "missing action"
action="${1}"
shift
case "${action}"
in
device | driver | pid | prepare | run | stop | test | wait)
"action_${action}" "${@}"
;;
*) syntaxError "unknown action: ${action}";;
esac
# Just in case we get here.
exit 0