15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/bin/sh
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NAME=org.chromium.chromoting
8e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen MurdochHOST_BUNDLE_NAME=@@HOST_BUNDLE_NAME@@
9e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen MurdochPREFPANE_BUNDLE_NAME=@@PREFPANE_BUNDLE_NAME@@
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CONFIG_DIR=/Library/PrivilegedHelperTools
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ENABLED_FILE=$CONFIG_DIR/$NAME.me2me_enabled
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CONFIG_FILE=$CONFIG_DIR/$NAME.json
13effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochHOST_EXE=$CONFIG_DIR/$HOST_BUNDLE_NAME/Contents/MacOS/remoting_me2me_host
14effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochPLIST_FILE=$CONFIG_DIR/$HOST_BUNDLE_NAME/Contents/Info.plist
15effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochPREF_PANE_BUNDLE=/Library/PreferencePanes/$PREFPANE_BUNDLE_NAME
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The exit code returned by 'wait' when a process is terminated by SIGTERM.
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SIGTERM_EXIT_CODE=143
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Range of exit codes returned by the host to indicate that a permanent error
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# has occurred and that the host should not be restarted. Please, keep these
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# constants in sync with remoting/host/host_exit_codes.h.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MIN_PERMANENT_ERROR_EXIT_CODE=100
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MAX_PERMANENT_ERROR_EXIT_CODE=105
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Constants controlling the host process relaunch throttling.
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)MINIMUM_RELAUNCH_INTERVAL=60
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)MAXIMUM_HOST_FAILURES=10
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Exit code 126 is defined by Posix to mean "Command found, but not
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# executable", and is returned if the process cannot be launched due to
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# parental control.
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)PERMISSION_DENIED_PARENTAL_CONTROL=126
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HOST_PID=0
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SIGNAL_WAS_TRAPPED=0
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# This script works as a proxy between launchd and the host. Signals of
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# interest to the host must be forwarded.
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SIGNAL_LIST="SIGHUP SIGINT SIGQUIT SIGILL SIGTRAP SIGABRT SIGEMT \
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SIGFPE SIGKILL SIGBUS SIGSEGV SIGSYS SIGPIPE SIGALRM SIGTERM SIGURG \
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SIGSTOP SIGTSTP SIGCONT SIGCHLD SIGTTIN SIGTTOU SIGIO SIGXCPU SIGXFSZ \
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      SIGVTALRM SIGPROF SIGWINCH SIGINFO SIGUSR1 SIGUSR2"
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)handle_signal() {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SIGNAL_WAS_TRAPPED=1
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)run_host() {
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  local host_failure_count=0
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  local host_start_time=0
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while true; do
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ ! -f "$ENABLED_FILE" ]]; then
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      echo "Daemon is disabled."
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 0
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # If this is not the first time the host has run, make sure we don't
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # relaunch it too soon.
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if [[ "$host_start_time" -gt 0 ]]; then
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      local host_lifetime=$(($(date +%s) - $host_start_time))
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      echo "Host ran for ${host_lifetime}s"
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if [[ "$host_lifetime" -lt "$MINIMUM_RELAUNCH_INTERVAL" ]]; then
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # If the host didn't run for very long, assume it crashed. Relaunch only
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # after a suitable delay and increase the failure count.
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        host_failure_count=$(($host_failure_count + 1))
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        echo "Host failure count $host_failure_count/$MAXIMUM_HOST_FAILURES"
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if [[ "$host_failure_count" -ge "$MAXIMUM_HOST_FAILURES" ]]; then
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          echo "Too many host failures. Giving up."
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          exit 1
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        fi
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        local relaunch_in=$(($MINIMUM_RELAUNCH_INTERVAL - $host_lifetime))
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        echo "Relaunching in ${relaunch_in}s"
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        sleep "$relaunch_in"
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      else
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # If the host ran for long enough, reset the crash counter.
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        host_failure_count=0
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fi
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fi
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Execute the host asynchronously and forward signals to it.
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    trap "handle_signal" $SIGNAL_LIST
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    host_start_time=$(date +%s)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "$HOST_EXE" --host-config="$CONFIG_FILE" &
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HOST_PID="$!"
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Wait for the host to return and process its exit code.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while true; do
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      wait "$HOST_PID"
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EXIT_CODE="$?"
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if [[ $SIGNAL_WAS_TRAPPED -eq 1 ]]; then
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # 'wait' returned as the result of a trapped signal and the exit code is
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # the signal that was trapped + 128. Forward the signal to the host.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SIGNAL_WAS_TRAPPED=0
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        local SIGNAL=$(($EXIT_CODE - 128))
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        echo "Forwarding signal $SIGNAL to host"
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kill -$SIGNAL "$HOST_PID"
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      elif [[ "$EXIT_CODE" -eq "0" ||
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "$EXIT_CODE" -eq "$SIGTERM_EXIT_CODE" ||
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              "$EXIT_CODE" -eq "$PERMISSION_DENIED_PARENTAL_CONTROL" ||
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              ("$EXIT_CODE" -ge "$MIN_PERMANENT_ERROR_EXIT_CODE" && \
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              "$EXIT_CODE" -le "$MAX_PERMANENT_ERROR_EXIT_CODE") ]]; then
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        echo "Host returned permanent exit code $EXIT_CODE at ""$(date)"""
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if [[ "$EXIT_CODE" -eq 101 ]]; then
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # Exit code 101 is "hostID deleted", which indicates that the host
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # was taken off-line remotely. To prevent the host being restarted
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # when the login context changes, try to delete the "enabled" file.
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # Since this requires root privileges, this is only possible when
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # this script is launched in the "login" context. In the "aqua"
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          # context, just exit and try again next time.
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          echo "Host id deleted - disabling"
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          rm -f "$ENABLED_FILE" 2>/dev/null
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        fi
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        exit "$EXIT_CODE"
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # Ignore non-permanent error-code and launch host again. Stop handling
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # signals temporarily in case the script has to sleep to throttle host
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # relaunches. While throttling, there is no host process to which to
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        # forward the signal, so the default behaviour should be restored.
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        echo "Host returned non-permanent exit code $EXIT_CODE at ""$(date)"""
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        trap - $SIGNAL_LIST
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        HOST_PID=0
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fi
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    done
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  done
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if [[ "$1" = "--disable" ]]; then
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This script is executed from base::mac::ExecuteWithPrivilegesAndWait(),
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # which requires the child process to write its PID to stdout before
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # anythine else. See base/mac/authorization_util.h for details.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo $$
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rm -f "$ENABLED_FILE"
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)elif [[ "$1" = "--enable" ]]; then
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo $$
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Ensure the config file is private whilst being written.
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rm -f "$CONFIG_FILE"
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  umask 0077
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cat > "$CONFIG_FILE"
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Ensure the config is readable by the user registering the host.
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chmod +a "$USER:allow:read" "$CONFIG_FILE"
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  touch "$ENABLED_FILE"
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)elif [[ "$1" = "--save-config" ]]; then
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo $$
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cat > "$CONFIG_FILE"
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)elif [[ "$1" = "--host-version" ]]; then
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$PLIST_FILE"
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)elif [[ "$1" = "--relaunch-prefpane" ]]; then
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Wait for the parent (System Preferences applet) to die, by reading from
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # stdin until the pipe is broken.
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cat 2>/dev/null || true
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open "$PREF_PANE_BUNDLE"
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)elif [[ "$1" = "--run-from-launchd" ]]; then
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  echo Host started for user $USER at $"$(date)"
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  run_host
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)else
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo $$
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit 1
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)fi
162