Home | History | Annotate | Download | only in PrivilegedHelperTools
      1 #!/bin/sh
      2 
      3 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      4 # Use of this source code is governed by a BSD-style license that can be
      5 # found in the LICENSE file.
      6 
      7 NAME=org.chromium.chromoting
      8 HOST_BUNDLE_NAME=@@HOST_BUNDLE_NAME@@
      9 PREFPANE_BUNDLE_NAME=@@PREFPANE_BUNDLE_NAME@@
     10 CONFIG_DIR=/Library/PrivilegedHelperTools
     11 ENABLED_FILE=$CONFIG_DIR/$NAME.me2me_enabled
     12 CONFIG_FILE=$CONFIG_DIR/$NAME.json
     13 HOST_EXE=$CONFIG_DIR/$HOST_BUNDLE_NAME/Contents/MacOS/remoting_me2me_host
     14 PLIST_FILE=$CONFIG_DIR/$HOST_BUNDLE_NAME/Contents/Info.plist
     15 PREF_PANE_BUNDLE=/Library/PreferencePanes/$PREFPANE_BUNDLE_NAME
     16 
     17 # The exit code returned by 'wait' when a process is terminated by SIGTERM.
     18 SIGTERM_EXIT_CODE=143
     19 
     20 # Range of exit codes returned by the host to indicate that a permanent error
     21 # has occurred and that the host should not be restarted. Please, keep these
     22 # constants in sync with remoting/host/host_exit_codes.h.
     23 MIN_PERMANENT_ERROR_EXIT_CODE=100
     24 MAX_PERMANENT_ERROR_EXIT_CODE=105
     25 
     26 # Constants controlling the host process relaunch throttling.
     27 MINIMUM_RELAUNCH_INTERVAL=60
     28 MAXIMUM_HOST_FAILURES=10
     29 
     30 # Exit code 126 is defined by Posix to mean "Command found, but not
     31 # executable", and is returned if the process cannot be launched due to
     32 # parental control.
     33 PERMISSION_DENIED_PARENTAL_CONTROL=126
     34 
     35 HOST_PID=0
     36 SIGNAL_WAS_TRAPPED=0
     37 
     38 # This script works as a proxy between launchd and the host. Signals of
     39 # interest to the host must be forwarded.
     40 SIGNAL_LIST="SIGHUP SIGINT SIGQUIT SIGILL SIGTRAP SIGABRT SIGEMT \
     41       SIGFPE SIGKILL SIGBUS SIGSEGV SIGSYS SIGPIPE SIGALRM SIGTERM SIGURG \
     42       SIGSTOP SIGTSTP SIGCONT SIGCHLD SIGTTIN SIGTTOU SIGIO SIGXCPU SIGXFSZ \
     43       SIGVTALRM SIGPROF SIGWINCH SIGINFO SIGUSR1 SIGUSR2"
     44 
     45 handle_signal() {
     46   SIGNAL_WAS_TRAPPED=1
     47 }
     48 
     49 run_host() {
     50   local host_failure_count=0
     51   local host_start_time=0
     52 
     53   while true; do
     54     if [[ ! -f "$ENABLED_FILE" ]]; then
     55       echo "Daemon is disabled."
     56       exit 0
     57     fi
     58 
     59     # If this is not the first time the host has run, make sure we don't
     60     # relaunch it too soon.
     61     if [[ "$host_start_time" -gt 0 ]]; then
     62       local host_lifetime=$(($(date +%s) - $host_start_time))
     63       echo "Host ran for ${host_lifetime}s"
     64       if [[ "$host_lifetime" -lt "$MINIMUM_RELAUNCH_INTERVAL" ]]; then
     65         # If the host didn't run for very long, assume it crashed. Relaunch only
     66         # after a suitable delay and increase the failure count.
     67         host_failure_count=$(($host_failure_count + 1))
     68         echo "Host failure count $host_failure_count/$MAXIMUM_HOST_FAILURES"
     69         if [[ "$host_failure_count" -ge "$MAXIMUM_HOST_FAILURES" ]]; then
     70           echo "Too many host failures. Giving up."
     71           exit 1
     72         fi
     73         local relaunch_in=$(($MINIMUM_RELAUNCH_INTERVAL - $host_lifetime))
     74         echo "Relaunching in ${relaunch_in}s"
     75         sleep "$relaunch_in"
     76       else
     77         # If the host ran for long enough, reset the crash counter.
     78         host_failure_count=0
     79       fi
     80     fi
     81 
     82     # Execute the host asynchronously and forward signals to it.
     83     trap "handle_signal" $SIGNAL_LIST
     84     host_start_time=$(date +%s)
     85     "$HOST_EXE" --host-config="$CONFIG_FILE" &
     86     HOST_PID="$!"
     87 
     88     # Wait for the host to return and process its exit code.
     89     while true; do
     90       wait "$HOST_PID"
     91       EXIT_CODE="$?"
     92       if [[ $SIGNAL_WAS_TRAPPED -eq 1 ]]; then
     93         # 'wait' returned as the result of a trapped signal and the exit code is
     94         # the signal that was trapped + 128. Forward the signal to the host.
     95         SIGNAL_WAS_TRAPPED=0
     96         local SIGNAL=$(($EXIT_CODE - 128))
     97         echo "Forwarding signal $SIGNAL to host"
     98         kill -$SIGNAL "$HOST_PID"
     99       elif [[ "$EXIT_CODE" -eq "0" ||
    100               "$EXIT_CODE" -eq "$SIGTERM_EXIT_CODE" ||
    101               "$EXIT_CODE" -eq "$PERMISSION_DENIED_PARENTAL_CONTROL" ||
    102               ("$EXIT_CODE" -ge "$MIN_PERMANENT_ERROR_EXIT_CODE" && \
    103               "$EXIT_CODE" -le "$MAX_PERMANENT_ERROR_EXIT_CODE") ]]; then
    104         echo "Host returned permanent exit code $EXIT_CODE at ""$(date)"""
    105         if [[ "$EXIT_CODE" -eq 101 ]]; then
    106           # Exit code 101 is "hostID deleted", which indicates that the host
    107           # was taken off-line remotely. To prevent the host being restarted
    108           # when the login context changes, try to delete the "enabled" file.
    109           # Since this requires root privileges, this is only possible when
    110           # this script is launched in the "login" context. In the "aqua"
    111           # context, just exit and try again next time.
    112           echo "Host id deleted - disabling"
    113           rm -f "$ENABLED_FILE" 2>/dev/null
    114         fi
    115         exit "$EXIT_CODE"
    116       else
    117         # Ignore non-permanent error-code and launch host again. Stop handling
    118         # signals temporarily in case the script has to sleep to throttle host
    119         # relaunches. While throttling, there is no host process to which to
    120         # forward the signal, so the default behaviour should be restored.
    121         echo "Host returned non-permanent exit code $EXIT_CODE at ""$(date)"""
    122         trap - $SIGNAL_LIST
    123         HOST_PID=0
    124         break
    125       fi
    126     done
    127   done
    128 }
    129 
    130 if [[ "$1" = "--disable" ]]; then
    131   # This script is executed from base::mac::ExecuteWithPrivilegesAndWait(),
    132   # which requires the child process to write its PID to stdout before
    133   # anythine else. See base/mac/authorization_util.h for details.
    134   echo $$
    135   rm -f "$ENABLED_FILE"
    136 elif [[ "$1" = "--enable" ]]; then
    137   echo $$
    138   # Ensure the config file is private whilst being written.
    139   rm -f "$CONFIG_FILE"
    140   umask 0077
    141   cat > "$CONFIG_FILE"
    142   # Ensure the config is readable by the user registering the host.
    143   chmod +a "$USER:allow:read" "$CONFIG_FILE"
    144   touch "$ENABLED_FILE"
    145 elif [[ "$1" = "--save-config" ]]; then
    146   echo $$
    147   cat > "$CONFIG_FILE"
    148 elif [[ "$1" = "--host-version" ]]; then
    149   /usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$PLIST_FILE"
    150 elif [[ "$1" = "--relaunch-prefpane" ]]; then
    151   # Wait for the parent (System Preferences applet) to die, by reading from
    152   # stdin until the pipe is broken.
    153   cat 2>/dev/null || true
    154   open "$PREF_PANE_BUNDLE"
    155 elif [[ "$1" = "--run-from-launchd" ]]; then
    156   echo Host started for user $USER at $"$(date)"
    157   run_host
    158 else
    159   echo $$
    160   exit 1
    161 fi
    162