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 # Version = @@VERSION@@ 8 9 NAME=org.chromium.chromoting 10 CONFIG_DIR=/Library/PrivilegedHelperTools 11 HOST_EXE=$CONFIG_DIR/$NAME.me2me_host.app/Contents/MacOS/remoting_me2me_host 12 PLIST_FILE=$CONFIG_DIR/$NAME.me2me_host.app/Contents/Info.plist 13 ENABLED_FILE=$CONFIG_DIR/$NAME.me2me_enabled 14 CONFIG_FILE=$CONFIG_DIR/$NAME.json 15 PREF_PANE_BUNDLE=/Library/PreferencePanes/$NAME.prefPane 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