| #!/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 |