| #!/bin/dash |
| # Copyright (c) 2012 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. |
| # |
| # NOTE: This script works in dash, but is not as featureful. Specifically, |
| # dash omits readline support (history & command line editing). So we try |
| # to run through bash if it exists, otherwise we stick to dash. All other |
| # code should be coded to the POSIX standard and avoid bashisms. |
| # |
| # Please test that any changes continue to work in dash by running |
| # '/build/$BOARD/bin/dash crosh --dash' before checking them in. |
| |
| # Disable path expansion at the command line. None of our builtins need or want |
| # it, and it's safer/saner to disallow it in the first place. |
| set -f |
| |
| # Don't allow SIGHUP to terminate crosh. This guarantees that even if the user |
| # closes the crosh window, we make our way back up to the main loop, which gives |
| # cleanup code in command handlers a chance to run. |
| trap '' HUP |
| |
| # Do not let CTRL+C kill crosh itself. This does let the user kill commands |
| # that are run by crosh (like `ping`). |
| trap : INT |
| |
| # If it exists, use $DATA_DIR to define the $HOME location, as a first step |
| # to entirely removing the use of $HOME. (Though HOME should be used as |
| # fall-back when DATA_DIR is unset.) |
| # TODO(keescook): remove $HOME entirely crbug.com/333031 |
| if [ "${DATA_DIR+set}" = "set" ]; then |
| export HOME="${DATA_DIR}/user" |
| fi |
| |
| IS_BASH=0 |
| try_bash() { |
| # If dash was explicitly requested, then nothing to do. |
| case " $* " in |
| *" --dash "*) return 0;; |
| esac |
| |
| # If we're already bash, then nothing to do. |
| if type "history" 2>/dev/null | grep -q "shell builtin"; then |
| IS_BASH=1 |
| return 0 |
| fi |
| |
| # Still here? Relaunch in bash. |
| exec /bin/bash $0 "$@" |
| } |
| try_bash "$@" |
| |
| INTRO_TEXT="Welcome to crosh, the Chrome OS developer shell. |
| |
| If you got here by mistake, don't panic! Just close this tab and carry on. |
| |
| Type 'help' for a list of commands. |
| |
| If you want to customize the look/behavior, you can use the options page. |
| Load it by using the Ctrl+Shift+P keyboard shortcut. |
| " |
| |
| # Gets set to "1" when in dev mode. |
| CROSH_DEVMODE= |
| # Gets set to "1" when running on removable media (e.g. USB stick). |
| CROSH_REMOVABLE= |
| CROSH_MODPATH="/usr/share/crosh" |
| CHROMEOS_INSTALL=/usr/sbin/chromeos-install |
| |
| check_digits() { |
| expr "$1" : '^[[:digit:]]*$' > /dev/null |
| } |
| |
| # Load all modules found in the specified subdir. |
| load_modules() { |
| local subdir="$1" |
| local dir="${CROSH_MODPATH}/${subdir}" |
| local mod |
| |
| # Turn on path expansion long enough to find local modules. |
| set +f |
| for mod in "${dir}"/[0-9][0-9]-*.sh; do |
| # Then turn path expansion back off. |
| set -f |
| if [ -e "${mod}" ]; then |
| echo "Loading extra module: ${mod}" |
| . "${mod}" || : |
| fi |
| done |
| } |
| |
| load_extra_crosh() { |
| # First load the common modules (board/project/etc... specific). |
| load_modules "extra.d" |
| |
| # Load the removable modules, if the rootfs is on removable |
| # media. e.g. It's a USB stick. |
| if [ -z "${CROSH_REMOVABLE}" ]; then |
| local src |
| if [ -e /usr/share/misc/chromeos-common.sh ]; then |
| . "/usr/share/misc/chromeos-common.sh" || exit 1 |
| |
| src=$(get_block_dev_from_partition_dev "$(rootdev -s)") |
| if [ "$(cat /sys/block/${src#/dev/}/removable 2>/dev/null)" = "1" ]; then |
| CROSH_REMOVABLE="1" |
| fi |
| fi |
| fi |
| if [ "${CROSH_REMOVABLE}" = "1" ]; then |
| load_modules "removable.d" |
| fi |
| |
| # Load the dev-mode modules, if in dev mode, or if forced. |
| # This comes last so it can override any release modules. |
| if [ -z "${CROSH_DEVMODE}" ]; then |
| if type crossystem >/dev/null 2>&1; then |
| crossystem "cros_debug?1" |
| CROSH_DEVMODE=$((!$?)) |
| else |
| echo "Could not locate 'crossystem'; assuming devmode is off." |
| fi |
| fi |
| if [ "${CROSH_DEVMODE}" = "1" ]; then |
| load_modules "dev.d" |
| fi |
| |
| } |
| |
| shell_read() { |
| local prompt="$1" |
| shift |
| |
| if [ "$IS_BASH" -eq "1" ]; then |
| # In bash, -e gives readline support. |
| read -p "$prompt" -e $@ |
| else |
| read -p "$prompt" $@ |
| fi |
| } |
| |
| shell_history() { |
| if [ "$IS_BASH" -eq "1" ]; then |
| # In bash, the history builtin can be used to manage readline history |
| history "$@" |
| fi |
| } |
| |
| shell_history_init() { |
| # Do not set any HISTXXX vars until after security check below. |
| local histfile="${HOME}/.crosh_history" |
| |
| # Limit the history to the last 100 entries to keep the file from growing |
| # out of control (unlikely, but let's be safe). |
| local histsize="100" |
| |
| # For security sake, let's clean up the file before we let the shell get a |
| # chance to read it. Clean out any non-printable ASCII chars (sorry UTF8 |
| # users) as it's the easiest way to be sure. We limit to 4k to be sane. |
| # We need a tempfile on the same device to avoid races w/multiple crosh |
| # tabs opening at the same time. |
| local tmpfile |
| if ! tmpfile="$(mktemp "${histfile}.XXXXXX")"; then |
| echo "warning: could not clean up history; ignoring it" |
| return |
| fi |
| # Ignore cat errors in case it doesn't exist yet. |
| cat "${histfile}" 2>/dev/null | |
| tr -dc '[:print:]\t\n' | |
| tail -n "${histsize}" | |
| tail -c 4096 > "${tmpfile}" |
| if ! mv "${tmpfile}" "${histfile}"; then |
| echo "warning: could not clean up history; ignoring it" |
| rm -f "${tmpfile}" |
| return |
| fi |
| |
| # Set this before any other history settings as some of them implicitly |
| # operate on this file. |
| HISTFILE="${histfile}" |
| |
| # Now we can limit the size of the history file. |
| HISTSIZE=${histsize} |
| HISTFILESIZE=${histsize} |
| |
| # Do not add entries that begin with a space, and dedupe sequential commands. |
| HISTCONTROL="ignoreboth" |
| |
| # Initialize pseudo completion support. Do it before we load the user's |
| # history so that new entries don't come after old ones. |
| if [ ${IS_BASH} -eq 1 ]; then |
| local f |
| for f in $(declare -F | sed -n '/-f cmd_/s:.*cmd_::p'); do |
| # Do not add duplicates to avoid ballooning history. |
| grep -qs "^${f} *$" "${HISTFILE}" || shell_history -s "${f}" |
| done |
| fi |
| |
| # Now load the user's history. |
| shell_history -r "${HISTFILE}" |
| } |
| |
| # Prints the value corresponding to the passed-in field in the output from |
| # dump_power_status. |
| get_power_status_field() { |
| local field="$1" |
| dump_power_status | awk -v field="${field}" '$1 == field { print $2 }' |
| } |
| |
| USAGE_help='[command]' |
| HELP_help=' |
| Display general help, or details for a specific command. |
| ' |
| cmd_help() ( |
| local cmd |
| |
| case $# in |
| 0) |
| local cmds="exit help help_advanced ping top" |
| for cmd in ${cmds}; do |
| cmd_help "${cmd}" |
| done |
| ;; |
| 1) |
| cmd="$1" |
| if check_command "${cmd}"; then |
| local usage="USAGE_${cmd}" help="HELP_${cmd}" |
| printf "${cmd} " |
| eval printf %s "\"\$${usage}\"" |
| eval echo "\" \$${help}\"" |
| else |
| echo "help: unknown command '${cmd}'" |
| return 1 |
| fi |
| ;; |
| *) |
| help "too many arguments" |
| return 1 |
| ;; |
| esac |
| ) |
| |
| # Useful alias for commands to call us. |
| help() { |
| if [ $# -ne 0 ]; then |
| printf 'ERROR: %s\n\n' "$*" |
| fi |
| |
| # This is set by `dispatch` when a valid command has been found. |
| if [ -n "${CURRENT_COMMAND}" ]; then |
| cmd_help "${CURRENT_COMMAND}" |
| fi |
| } |
| |
| USAGE_help_advanced='' |
| HELP_help_advanced=' |
| Display the help for more advanced commands, mainly used for debugging. |
| ' |
| cmd_help_advanced() ( |
| local cmd commands |
| |
| if [ $# -ne 0 ]; then |
| help "too many arguments" |
| return 1 |
| fi |
| |
| commands=$(set | grep -o '^HELP_[^=]*' | \ |
| sed -e 's:^HELP_::' -e 's:=$::' | sort) |
| for cmd in ${commands}; do |
| cmd_help "${cmd}" |
| done |
| ) |
| |
| # We move the trailing brace to the next line so that we avoid the style |
| # checker from rejecting the use of braces. We cannot use subshells here |
| # as we want the set the exit variable in crosh itself. |
| # http://crbug.com/318368 |
| USAGE_exit='' |
| HELP_exit=' |
| Exit crosh. |
| ' |
| cmd_exit() |
| { |
| exit='y' |
| } |
| |
| USAGE_set_time='[<time string>]' |
| HELP_set_time=' |
| Sets the system time if the the system has been unable to get it from the |
| network. The <time string> uses the format of the GNU coreutils date command. |
| ' |
| cmd_set_time() ( |
| local spec="$*" |
| if [ -z "${spec}" ]; then |
| echo "A date/time specification is required." |
| echo "E.g., set_time 10 February 2012 11:21am" |
| echo "(Remember to set your timezone in Settings first.)" |
| return |
| fi |
| local sec status |
| sec=$(date +%s --date="${spec}" 2>&1) |
| status=$? |
| if [ ${status} -ne 0 -o -z "${sec}" ]; then |
| echo "Unable to understand the specified time:" |
| echo "${sec}" |
| return |
| fi |
| local reply |
| reply=$(dbus-send --system --type=method_call --print-reply \ |
| --dest=org.torproject.tlsdate /org/torproject/tlsdate \ |
| org.torproject.tlsdate.SetTime "int64:$((sec))" 2>/dev/null) |
| status=$? |
| if [ ${status} -ne 0 ]; then |
| echo "Time not set. Unable to communicate with the time service." |
| return |
| fi |
| # Reply format: <dbus response header>\n uint32 <code>\n |
| local code |
| code=$(echo "${reply}" | sed -n -e '$s/.*uint32 \([0-9]\).*/\1/p') |
| case "${code}" in |
| 0) |
| echo "Time has been set." |
| ;; |
| 1) |
| echo "Requested time was invalid (too large or too small): ${sec}" |
| ;; |
| 2) |
| echo "Time not set. Network time cannot be overriden." |
| ;; |
| 3) |
| echo "Time not set. There was a communication error." |
| ;; |
| *) |
| echo "An unexpected response was received: ${code}" |
| echo "Details: ${reply}" |
| esac |
| ) |
| |
| # Set the vars to pass the unittests ... |
| USAGE_ssh='' |
| HELP_ssh='' |
| # ... then unset them to hide the command from "help" output. |
| unset USAGE_ssh HELP_ssh |
| cmd_ssh() ( |
| cat <<EOF |
| The 'ssh' command has been removed. Please install the official SSH extension: |
| https://chrome.google.com/webstore/detail/pnhechapfaindjhompbnflcldabbghjo |
| EOF |
| ) |
| |
| USAGE_swap='[enable <size (MB)> | disable | status]' |
| HELP_swap=' |
| Manage the system swap file. |
| ' |
| cmd_swap() ( |
| local swap_enable_file="/home/chronos/.swap_enabled" |
| |
| case "$1" in |
| "enable") |
| if [ -z "$2" ]; then |
| # remove file in case root owns it |
| rm -f $swap_enable_file |
| touch $swap_enable_file |
| elif ! printf '%s' "$2" | grep -q "^[0-9]*$"; then |
| help "$2 is not a valid integer literal" |
| return 1 |
| elif [ "$2" -ne 500 -a \ |
| "$2" -ne 1000 -a \ |
| "$2" -ne 2000 -a \ |
| "$2" -ne 3000 -a \ |
| "$2" -ne 4000 -a \ |
| "$2" -ne 4500 -a \ |
| "$2" -ne 6000 ]; then |
| echo "Valid choices: 500, 1000, 2000, 3000, 4000, 4500 or 6000." |
| help "invalid size $2." |
| return 1 |
| else |
| # remove file in case root owns it |
| rm -f $swap_enable_file |
| echo "$2" > $swap_enable_file |
| echo "You have selected a size of $2 MB for compressed swap." |
| echo "Your choice may affect system performance in various ways." |
| echo "Presumably you know what you are doing." |
| fi |
| echo "Swap will be ON at next reboot." |
| ;; |
| "disable") |
| # remove file in case root owns it |
| rm -f $swap_enable_file |
| echo 0 > $swap_enable_file |
| echo "You are turning compressed swap off." |
| echo "This may impact system performance in various ways." |
| echo "Presumably you know what you are doing." |
| echo "Swap will be OFF at next reboot." |
| ;; |
| "status"|"") |
| /sbin/swapon -s |
| ;; |
| *) |
| help "unknown option: $1" |
| return 1 |
| ;; |
| esac |
| ) |
| |
| USAGE_time_info='' |
| HELP_time_info=' |
| Returns the current synchronization state for the time service. |
| ' |
| cmd_time_info() ( |
| echo "Last time synchronization information:" |
| dbus-send --system --type=method_call --print-reply \ |
| --dest=org.torproject.tlsdate /org/torproject/tlsdate \ |
| org.torproject.tlsdate.LastSyncInfo 2>/dev/null | |
| sed -n \ |
| -e 's/boolean/network-synchronized:/p' \ |
| -e 's/string/last-source:/p' \ |
| -e 's/int64/last-synced-time:/p' |
| ) |
| |
| USAGE_bt_console='[<agent capability>]' |
| HELP_bt_console=' |
| Enters a Bluetooth debugging console. Optional argument specifies the |
| capability of a pairing agent the console will provide; see the Bluetooth |
| Core specification for valid options. |
| ' |
| cmd_bt_console() ( |
| if [ -x /usr/bin/bluetoothctl ]; then |
| /usr/bin/bluetoothctl "${1:+--agent=$1}" |
| else |
| echo "Bluetooth console not available" |
| fi |
| ) |
| |
| USAGE_ff_debug='[<tag_expr>] [--list_valid_tags] [--reset]' |
| HELP_ff_debug=' |
| Add and remove flimflam debugging tags. "ff_debug help" for more details. |
| ' |
| cmd_ff_debug() ( |
| if [ "$1" = "help" ]; then |
| set -- --help |
| fi |
| /usr/bin/ff_debug "$@" |
| ) |
| |
| USAGE_wpa_debug='[<debug_level>] [--list_valid_level] [--reset]' |
| HELP_wpa_debug=' |
| Set wpa_supplicant debugging level. "wpa_debug help" for more details. |
| ' |
| cmd_wpa_debug() ( |
| if [ "$1" = "help" ]; then |
| set -- --help |
| fi |
| /usr/bin/wpa_debug "$@" |
| ) |
| |
| USAGE_set_arpgw='<true | false>' |
| HELP_set_arpgw=' |
| Turn on extra network state checking to make sure the default gateway |
| is reachable. |
| ' |
| cmd_set_arpgw() ( |
| case "$1" in |
| "true"|"false") |
| /usr/bin/set_arpgw "$1" |
| ;; |
| "") |
| /usr/bin/set_arpgw |
| ;; |
| *) |
| help "unknown option: $1" |
| return 1 |
| ;; |
| esac |
| ) |
| |
| USAGE_set_wake_on_lan='<true | false>' |
| HELP_set_wake_on_lan=' |
| Enable or disable Wake on LAN for Ethernet devices. This command takes |
| effect after re-connecting to Ethernet and is not persistent across system |
| restarts. |
| ' |
| cmd_set_wake_on_lan() ( |
| case "$1" in |
| "true"|"false") |
| /usr/bin/set_wake_on_lan "$1" |
| ;; |
| "") |
| /usr/bin/set_wake_on_lan |
| ;; |
| *) |
| help "unknown option: $1" |
| return 1 |
| ;; |
| esac |
| ) |
| |
| USAGE_network_logging='<wifi | cellular | ethernet>' |
| HELP_network_logging=' |
| A function that enables a predefined set of tags useful for |
| debugging the specified device. |
| ' |
| cmd_network_logging() ( |
| if [ -n "$1" ]; then |
| case "$1" in |
| "wifi") |
| echo; /usr/bin/ff_debug service+wifi+inet+device+manager |
| echo; /usr/bin/wpa_debug msgdump |
| echo; /usr/bin/modem set-logging info |
| ;; |
| "cellular") |
| echo; /usr/bin/ff_debug service+cellular+modem+device+manager |
| echo; /usr/bin/wpa_debug info |
| echo; /usr/bin/modem set-logging debug |
| ;; |
| "ethernet") |
| echo; /usr/bin/ff_debug service+ethernet+device+manager |
| echo; /usr/bin/wpa_debug info |
| echo; /usr/bin/modem set-logging info |
| ;; |
| *) |
| help "unknown option: $1" |
| ;; |
| esac |
| else |
| help "missing command" |
| fi |
| ) |
| |
| USAGE_network_diag="[--date] [--flimflam] [--link] [--show-macs] [--wifi] \ |
| [--wifi-mon] <host>" |
| HELP_network_diag=' |
| A function that performs a suite of network diagnostics. Saves a copy |
| of the output to your Downloads directory. |
| ' |
| cmd_network_diag() ( |
| /usr/bin/network_diag "$@" |
| ) |
| |
| debugd() { |
| # Default timeout 30 seconds. |
| local timeout="--reply-timeout=30000" |
| case $1 in |
| --reply-timeout=*) timeout=$1; shift;; |
| esac |
| local method="$1"; shift |
| dbus-send ${timeout} --system --print-reply --fixed --dest=org.chromium.debugd \ |
| /org/chromium/debugd "org.chromium.debugd.$method" "$@" |
| } |
| |
| # Run a debugd command for a long time and poll its output. |
| # This expects Start & Stop methods. |
| debugd_poll() ( |
| local methodbase="$1"; shift |
| |
| local pid fifo |
| |
| # Make sure we clean up the background process and temp files when the |
| # user kills us with CTRL+C. |
| cleanup() { |
| # Don't let the user kill us while cleaning up. |
| trap : INT |
| |
| if [ -n "${pid}" ]; then |
| if ! debugd "${methodbase}Stop" "string:${pid}"; then |
| echo "warning: could not stop ${methodbase}" |
| fi |
| pid='' |
| fi |
| |
| if [ -n "${fifo}" ]; then |
| dir=$(dirname "${fifo}") |
| rm -rf "${dir}" |
| fifo='' |
| fi |
| } |
| trap cleanup INT |
| |
| if ! fifo="$(mk_fifo)"; then |
| # The mk_fifo command already showed a warning. |
| return 1 |
| fi |
| debugd "${methodbase}Start" "$@" 2>&1 >"${fifo}" & |
| |
| read pid < "${fifo}" |
| cat "${fifo}" |
| cleanup |
| ) |
| |
| USAGE_ping="[-4] [-6] [-c count] [-i interval] [-n] [-s packetsize] \ |
| [-W waittime] <destination>" |
| HELP_ping=' |
| Send ICMP ECHO_REQUEST packets to a network host. If <destination> is "gw" |
| then the next hop gateway for the default route is used. |
| Default is to use IPv4 [-4] rather than IPv6 [-6] addresses. |
| ' |
| cmd_ping() ( |
| local option="dict:string:variant:" |
| local dest |
| |
| while [ $# -gt 0 ]; do |
| # Do just enough parsing to filter/map options; we |
| # depend on ping to handle final validation. |
| case "$1" in |
| -4) option="${option}v6,boolean:false," ;; |
| -6) option="${option}v6,boolean:true," ;; |
| -i) shift; option="${option}interval,int32:$1," ;; |
| -c) shift; option="${option}count,int32:$1," ;; |
| -W) shift; option="${option}waittime,int32:$1," ;; |
| -s) shift; option="${option}packetsize,int32:$1," ;; |
| -n) option="${option}numeric,boolean:true," ;; |
| -b) option="${option}broadcast,boolean:true," ;; |
| -*) |
| help "unknown option: $1" |
| return 1 |
| ;; |
| *) |
| if [ "${dest+set}" = "set" ]; then |
| help "too many destinations specified" |
| return 1 |
| fi |
| dest="$1" |
| ;; |
| esac |
| |
| shift |
| done |
| |
| if [ "${dest+set}" != "set" ]; then |
| help "missing parameter: destination" |
| return 1 |
| fi |
| |
| if [ "${dest}" = "gw" ]; then |
| # Convenient shorthand for the next-hop gateway attached |
| # to the default route; this means if you have a host named |
| # "gw" then you'll need to specify a FQDN or IP address. |
| dest=$(ip route show | awk '$1 == "default" { print $3 }') |
| if [ -z "${dest}" ]; then |
| echo "Cannot determine primary gateway; routing table is:" |
| cmd_route |
| return 1 |
| fi |
| fi |
| |
| # Remove trailing comma in the options list if it exists. |
| debugd_poll Ping "fd:1" "string:${dest}" "${option%,}" |
| ) |
| |
| USAGE_chaps_debug='[start|stop|<log_level>]' |
| HELP_chaps_debug=' |
| Sets the chapsd logging level. No arguments will start verbose logging. |
| ' |
| cmd_chaps_debug() ( |
| local level=${1:--2} |
| if [ "$1" = "stop" ]; then |
| level=0 |
| fi |
| if [ "$1" = "start" ]; then |
| level=-2 |
| fi |
| rm -f /home/chronos/.chaps_debug_1 /home/chronos/.chaps_debug_2 |
| if [ "${level}" = "-1" ]; then |
| touch /home/chronos/.chaps_debug_1 |
| elif [ "${level}" = "-2" ]; then |
| touch /home/chronos/.chaps_debug_2 |
| fi |
| /usr/bin/chaps_client --set_log_level=${level} 2> /dev/null |
| if [ $? -eq 0 ]; then |
| echo "Logging level set to ${level}." |
| else |
| echo "Failed to set logging level." |
| fi |
| ) |
| |
| USAGE_route='[-4] [-6]' |
| HELP_route=' |
| Display the routing tables. |
| Default is to show IPv4 [-4] rather than IPv6 [-6] routes. |
| ' |
| cmd_route() ( |
| local option="dict:string:variant:" |
| |
| while [ $# -gt 0 ]; do |
| case $1 in |
| -4) option="${option}v6,boolean:false," ;; |
| -6) option="${option}v6,boolean:true," ;; |
| *) |
| help "unknown option: $1" |
| return 1 |
| esac |
| shift |
| done |
| |
| debugd GetRoutes "${option%,}" |
| ) |
| |
| USAGE_tracepath='[-4] [-6] [-n] <destination>[/port]' |
| HELP_tracepath=' |
| Trace the path/route to a network host. |
| Default is to trace IPv4 [-4] rather than IPv6 [-6] targets. |
| ' |
| cmd_tracepath() ( |
| local option="dict:string:variant:" |
| local dest |
| |
| while [ $# -gt 0 ]; do |
| # Do just enough parsing to filter/map options; we |
| # depend on tracepath to handle final validation. |
| case "$1" in |
| -4) option="${option}v6,boolean:false," ;; |
| -6) option="${option}v6,boolean:true," ;; |
| -n) option="${option}numeric,boolean:true," ;; |
| -*) |
| help "unknown option: $1" |
| return 1 |
| ;; |
| *) |
| if [ "${dest+set}" = "set" ]; then |
| help "too many destinations specified" |
| return 1 |
| fi |
| dest="$1" |
| ;; |
| esac |
| |
| shift |
| done |
| |
| if [ "${dest+set}" != "set" ]; then |
| help "missing parameter: destination" |
| return 1 |
| fi |
| |
| # Remove trailing comma in the options list if it exists. |
| debugd_poll TracePath "fd:1" "string:${dest}" "${option%,}" |
| ) |
| |
| USAGE_top='' |
| HELP_top=' |
| Run top. |
| ' |
| cmd_top() ( |
| # -s is "secure" mode, which disables kill, renice, and change display/sleep |
| # interval. |
| top -s |
| ) |
| |
| USAGE_modem='<command> [args...]' |
| HELP_modem=' |
| Interact with the 3G modem. Run "modem help" for detailed help. |
| ' |
| cmd_modem() ( |
| /usr/bin/modem "$@" |
| ) |
| |
| USAGE_modem_set_carrier='carrier-name' |
| HELP_modem_set_carrier=' |
| Configures the modem for the specified carrier. |
| ' |
| cmd_modem_set_carrier() ( |
| /usr/bin/modem set-carrier "$@" |
| ) |
| |
| USAGE_set_apn='[-c] [-n <network-id>] [-u <username>] [-p <password>] <apn>' |
| HELP_set_apn=' |
| Set the APN to use when connecting to the network specified by <network-id>. |
| If <network-id> is not specified, use the network-id of the currently |
| registered network. |
| |
| The -c option clears the APN to be used, so that the default APN will be used |
| instead. |
| ' |
| cmd_set_apn() ( |
| /usr/bin/set_apn "$@" |
| ) |
| |
| USAGE_set_cellular_ppp='[-c] [-u <username>] [-p <password>]' |
| HELP_set_cellular_ppp=' |
| Set the PPP username and/or password for an existing cellular connection. |
| If neither -u nor -p is provided, show the existing PPP username for |
| the cellular connection. |
| |
| The -c option clears any existing PPP username and PPP password for an |
| existing cellular connection. |
| ' |
| cmd_set_cellular_ppp() ( |
| /usr/bin/set_cellular_ppp "$@" |
| ) |
| |
| USAGE_connectivity='' |
| HELP_connectivity=' |
| Shows connectivity status. "connectivity help" for more details |
| ' |
| cmd_connectivity() ( |
| /usr/bin/connectivity "$@" |
| ) |
| |
| USAGE_autest='[-scheduled]' |
| HELP_autest=' |
| Trigger an auto-update. To fake a scheduled update check use -scheduled. |
| ' |
| cmd_autest() ( |
| local omaha_url="autest" |
| |
| if [ "$1" = "-scheduled" ]; then |
| # pretend that this is a scheduled check as opposed to an user-initiated |
| # check for testing features that get enabled only on scheduled checks. |
| omaha_url="autest-scheduled" |
| fi |
| |
| echo "Calling update_engine_client with omaha_url = $omaha_url" |
| /usr/bin/update_engine_client --omaha_url=$omaha_url |
| ) |
| |
| USAGE_p2p_update='[enable|disable] [--num-connections] [--show-peers]' |
| HELP_p2p_update=' |
| Enables or disables the peer-to-peer (P2P) sharing of updates over the local |
| network. This will both attempt to get updates from other peers in the |
| network and share the downloaded updates with them. Run this command without |
| arguments to see the current state. Additional switches will display number |
| of connections and P2P peers. |
| ' |
| cmd_p2p_update() ( |
| |
| if [ "$1" = "" ]; then |
| /usr/bin/update_engine_client -show_p2p_update |
| fi |
| |
| while [ $# -gt 0 ]; do |
| case "$1" in |
| "enable") |
| /usr/bin/update_engine_client -p2p_update=yes -show_p2p_update |
| ;; |
| |
| "disable") |
| /usr/bin/update_engine_client -p2p_update=no -show_p2p_update |
| ;; |
| |
| "--num-connections") |
| if p2p_check_enabled; then |
| echo "Number of active p2p connections:" |
| /usr/sbin/p2p-client --num-connections |
| fi |
| ;; |
| |
| "--show-peers") |
| if p2p_check_enabled; then |
| echo "Current p2p peers:" |
| /usr/sbin/p2p-client --list-all |
| fi |
| ;; |
| |
| *) |
| help "unknown option: $1" |
| return 1 |
| ;; |
| esac |
| |
| shift |
| done |
| ) |
| |
| p2p_check_enabled() { |
| if ! /usr/bin/update_engine_client -show_p2p_update 2>&1 \ |
| | grep -q "ENABLED"; then |
| echo "Run \"p2p_update enable\" to enable peer-to-peer before" \ |
| "using this command." |
| return 1 |
| fi |
| } |
| |
| USAGE_rollback='' |
| HELP_rollback=' |
| Attempt to rollback to the previous update cached on your system. Only |
| available on non-stable channels and non-enterprise enrolled devices. Please |
| note that this will powerwash your device. |
| ' |
| cmd_rollback() ( |
| if /usr/bin/update_engine_client --rollback; then |
| echo "Rollback attempt succeeded -- after a couple minutes you will" \ |
| "get an update available and you should reboot to complete rollback." |
| else |
| echo "Rollback attempt failed. Check chrome://system for more information." |
| fi |
| ) |
| |
| USAGE_update_over_cellular='[enable|disable]' |
| HELP_update_over_cellular=' |
| Enables or disables the auto updates over cellular networks. Run without |
| arguments to see the current state. |
| ' |
| cmd_update_over_cellular() ( |
| case "$1" in |
| "enable") |
| param="-update_over_cellular=yes" |
| echo "When available, auto-updates download in the background any time " \ |
| "the computer is powered on. Note: this may incur additional " \ |
| "cellular charges, including roaming and/or data charges, as per " \ |
| "your carrier arrangement." |
| ;; |
| |
| "disable") |
| param="-update_over_cellular=no" |
| ;; |
| |
| "") |
| param="" |
| ;; |
| |
| *) |
| help "unknown option: $1" |
| return 1 |
| ;; |
| esac |
| /usr/bin/update_engine_client $param -show_update_over_cellular |
| ) |
| |
| USAGE_upload_crashes='' |
| HELP_upload_crashes=' |
| Uploads available crash reports to the crash server. |
| ' |
| cmd_upload_crashes() ( |
| debugd UploadCrashes |
| echo "Check chrome://crashes for status updates" |
| ) |
| |
| USAGE_upload_devcoredumps='[enable|disable]' |
| HELP_upload_devcoredumps=' |
| Enable or disable the upload of devcoredump reports. |
| ' |
| cmd_upload_devcoredumps() ( |
| case "$1" in |
| "enable") |
| debugd EnableDevCoredumpUpload |
| ;; |
| |
| "disable") |
| debugd DisableDevCoredumpUpload |
| ;; |
| |
| *) |
| help "unknown option: $1" |
| return 1 |
| ;; |
| esac |
| ) |
| |
| USAGE_try_touch_experiment='<experiment hex>' |
| HELP_try_touch_experiment=' |
| Manage touchpad experiments. |
| ' |
| cmd_try_touch_experiment() ( |
| /usr/sbin/try_touch_experiment "$@" |
| ) |
| |
| USAGE_inputcontrol='' |
| HELP_inputcontrol=' |
| Manually adjust advanced touchpad and mouse settings. Run |
| "inputcontrol help" for full list of options. |
| ' |
| cmd_inputcontrol() ( |
| if [ "$1" = "help" ]; then |
| set -- -h |
| fi |
| /opt/google/input/inputcontrol "$@" |
| ) |
| |
| USAGE_rlz='< status | enable | disable >' |
| HELP_rlz=" |
| Enable or disable RLZ. See this site for details: |
| http://dev.chromium.org/developers/design-documents/extensions/\ |
| proposed-changes/apis-under-development/rlz-api |
| " |
| cmd_rlz() ( |
| local flag_file="$HOME/.rlz_disabled" |
| local enabled=1 |
| local changed=0 |
| if [ -r "${flag_file}" ]; then |
| enabled=0 |
| fi |
| case "$1" in |
| "status") |
| if [ $enabled -eq 1 ]; then |
| echo "Currently enabled" |
| else |
| echo "Currently disabled" |
| fi |
| return |
| ;; |
| |
| "enable") |
| if [ $enabled -eq 0 ]; then |
| changed=1 |
| fi |
| rm -f "${flag_file}" |
| ;; |
| |
| "disable") |
| if [ $enabled -eq 1 ]; then |
| changed=1 |
| fi |
| touch "${flag_file}" |
| ;; |
| |
| *) |
| help "unknown option: $1" |
| return 1 |
| ;; |
| esac |
| if [ $changed -eq 1 ]; then |
| echo "You must reboot for this to take effect." |
| else |
| echo "No change." |
| fi |
| ) |
| |
| USAGE_syslog='<message>' |
| HELP_syslog=' |
| Logs a message to syslog (the system log daemon). |
| ' |
| cmd_syslog() ( |
| logger -t crosh -- "$*" |
| ) |
| |
| mk_fifo() { |
| local dir fifo |
| |
| # We want C-c to terminate the running test so that the UI stays the same. |
| # Therefore, create a fifo to direct the output of the test to, and have a |
| # subshell read from the fifo and emit to stdout. When the subshell ends (at a |
| # C-c), we stop the test and clean up the fifo. |
| # no way to mktemp a fifo, so make a dir to hold it instead |
| dir=$(mktemp -d "/tmp/crosh-test-XXXXXXXXXX") |
| if [ $? -ne 0 ]; then |
| echo "Can't create temporary directory" |
| return 1 |
| fi |
| fifo="${dir}/fifo" |
| if ! mkfifo "${fifo}"; then |
| echo "Can't create fifo at ${fifo}" |
| return 1 |
| fi |
| |
| echo "${fifo}" |
| } |
| |
| USAGE_storage_test_1='' |
| HELP_storage_test_1=' |
| Performs a short offline SMART test. |
| ' |
| cmd_storage_test_1() ( |
| option="$1" |
| |
| debugd Smartctl "string:abort_test" >/dev/null |
| |
| test=$(debugd Smartctl "string:short_test") |
| if [ "$option" != "-v" ]; then |
| echo "$test" | sed -n '1p;2p' |
| echo "" |
| echo "$test" | grep "Please wait" |
| else |
| echo "$test" |
| fi |
| |
| echo "" |
| |
| while debugd Smartctl "string:capabilities" | |
| grep -q "of test remaining"; do |
| true |
| done |
| |
| result=$(debugd Smartctl "string:selftest") |
| if [ "$option" != "-v" ]; then |
| echo "$result" | grep -e "Num" -e "# 1" |
| else |
| echo "$result" |
| fi |
| |
| debugd Smartctl "string:abort_test" >/dev/null |
| ) |
| |
| USAGE_storage_test_2='' |
| HELP_storage_test_2=' |
| Performs an extensive readability test. |
| ' |
| cmd_storage_test_2() ( |
| debugd_poll Badblocks "fd:1" |
| ) |
| |
| # Set the vars to pass the unittests ... |
| USAGE_storage_status='' |
| HELP_storage_status='' |
| # ... then unset them to hide the command from "help" output. |
| unset USAGE_storage_status HELP_storage_status |
| cmd_storage_status() ( |
| echo "Removed. See storage_info section in chrome://system" |
| ) |
| |
| USAGE_memory_test='' |
| HELP_memory_test=' |
| Performs extensive memory testing on the available free memory. |
| ' |
| cmd_memory_test() ( |
| # Getting total free memory in KB. |
| mem=$(cat /proc/meminfo | grep MemFree | tr -s " " | cut -d" " -f 2) |
| |
| # Converting to MB. |
| mem=$(($mem / 1024)) |
| |
| # Giving OS 200MB free memory before hogging the rest of it. |
| mem=$(($mem - 200)) |
| |
| debugd_poll Memtester "fd:1" "uint32:${mem}" |
| ) |
| |
| USAGE_battery_firmware='<info|check|update>' |
| HELP_battery_firmware=' |
| info : Query battery info. |
| check : Check whether the AC adapter is connected. |
| Also check whether the battery firmware is the latest. |
| update : Trigger battery firmware update. |
| ' |
| cmd_battery_firmware() ( |
| option="$1" |
| case "${option}" in |
| info|check) |
| debugd --reply-timeout=$(( 10 * 60 * 1000 )) BatteryFirmware "string:${option}" |
| echo "" |
| ;; |
| update) |
| # Increased the reply-timeout to 10 min for Battery Firmware update process. |
| # Battery Firmware Update process time includes the following: |
| # 1 setup delay time before entery battery firmware update mode. |
| # 2 battery flash erase time. |
| # 3 data transfer time from AP to EC, and then to Batttery. |
| # 4 battery flash program/write time. |
| # 5 re-try time on errors. |
| # Note: take ~2 min on a success battery firmware update case. |
| echo "" |
| echo "================================================================================" |
| echo " Battery firmware update is in progress." |
| echo "================================================================================" |
| echo "Please DO NOT remove the power adapter cable, otherwise the update will fail." |
| echo "" |
| echo "To recover from a failed battery firmware update," |
| echo " please plug in the power adapter cable, reboot and run this command again." |
| echo "" |
| echo "================================================================================" |
| echo "" |
| debugd --reply-timeout=$(( 10 * 60 * 1000 )) BatteryFirmware "string:${option}" |
| echo "" |
| ;; |
| *) |
| help "Unknown option: ${option}" |
| ;; |
| esac |
| ) |
| |
| USAGE_battery_test='[<test length>]' |
| HELP_battery_test=' |
| Tests battery discharge rate for given number of seconds. Without an argument, |
| defaults to 300 seconds. |
| ' |
| cmd_battery_test() ( |
| local test_length="$1" |
| if [ -z "$test_length" ]; then |
| echo "No test length specified. Defaulting to 300 seconds." |
| test_length=300 |
| fi |
| |
| if ! check_digits "${test_length}"; then |
| echo "Invalid test length." |
| return 1 |
| fi |
| |
| if [ "$(get_power_status_field 'battery_present')" != '1' ]; then |
| echo "No battery found." |
| return 1 |
| fi |
| |
| if [ "$(get_power_status_field 'battery_discharging')" = '1' ]; then |
| local bat_status='discharging' |
| local bat_discharging=1 |
| else |
| local bat_status='charging or full' |
| local bat_discharging=0 |
| fi |
| |
| local bat_pct="$(get_power_status_field 'battery_percent')" |
| local bat_full="$(get_power_status_field 'battery_charge_full')" |
| local bat_full_design="$(get_power_status_field 'battery_charge_full_design')" |
| local bat_health="$(echo "${bat_full}" "${bat_full_design}" | \ |
| awk '{ printf "%.2f", 100.0 * $1 / $2 }')" |
| |
| echo "Battery is ${bat_status} (${bat_pct}% left)" |
| echo "Battery health: ${bat_health}%" |
| |
| if [ "${bat_discharging}" != '1' ]; then |
| echo "Please make sure the power supply is unplugged and retry the test." |
| return 1 |
| fi |
| |
| echo "Please wait..." |
| sleep "${test_length}" |
| |
| local bat_after="$(get_power_status_field 'battery_percent')" |
| local bat_diff="$(echo "${bat_pct}" "${bat_after}" | \ |
| awk '{ printf "%.2f", $1 - $2 }')" |
| echo "Battery discharged ${bat_diff}% in ${test_length} second(s)." |
| ) |
| |
| USAGE_dump_emk='' |
| HELP_dump_emk=' |
| Show the EMK (Enterprise Machine Key). |
| ' |
| cmd_dump_emk() ( |
| /usr/sbin/cryptohome --action=tpm_attestation_key_status \ |
| --name=attest-ent-machine |
| ) |
| |
| USAGE_enroll_status='[--mode] [--domain] [--user]' |
| HELP_enroll_status=' |
| Displays device enrollment information. |
| ' |
| cmd_enroll_status() ( |
| if [ "$1" = "" ]; then |
| if check_enterprise_mode; then |
| echo "Enrollment mode:" |
| /usr/sbin/cryptohome --action=install_attributes_get \ |
| --name=enterprise.mode |
| else |
| echo "This device is not enterprise enrolled." |
| fi |
| fi |
| |
| while [ $# -gt 0 ]; do |
| case "$1" in |
| "--mode") |
| if check_enterprise_mode; then |
| echo "Enrollment mode:" |
| /usr/sbin/cryptohome --action=install_attributes_get \ |
| --name=enterprise.mode |
| else |
| echo "This device is not enterprise enrolled." |
| fi |
| ;; |
| |
| "--domain") |
| if check_enterprise_mode; then |
| echo "Enrollment domain:" |
| /usr/sbin/cryptohome --action=install_attributes_get \ |
| --name=enterprise.domain |
| else |
| echo "Enterprise enrollment domain not found." |
| fi |
| ;; |
| |
| "--user") |
| if check_enterprise_mode; then |
| echo "Enrollment user:" |
| /usr/sbin/cryptohome --action=install_attributes_get \ |
| --name=enterprise.user |
| else |
| echo "Enterprise enrollment user not found." |
| fi |
| ;; |
| |
| *) |
| help "unknown option: $1" |
| return 1 |
| ;; |
| esac |
| |
| shift |
| done |
| ) |
| |
| check_enterprise_mode() { |
| if ! /usr/sbin/cryptohome --action=install_attributes_get \ |
| --name=enterprise.mode 2>&1 | grep -q "enterprise"; then |
| return 1 |
| fi |
| } |
| |
| USAGE_tpm_status='' |
| HELP_tpm_status=' |
| Prints TPM (Trusted Platform Module) status information. |
| ' |
| cmd_tpm_status() ( |
| /usr/sbin/cryptohome --action=tpm_more_status |
| ) |
| |
| USAGE_dmesg='[-d|-k|-r|-t|-u|-x]' |
| HELP_dmesg=' |
| Display kernel log buffer |
| ' |
| cmd_dmesg() ( |
| # We whitelist only a few options. |
| local opt |
| for opt in "$@"; do |
| if ! printf '%s' "${opt}" | grep -sqE "^-[dkrtux]+$"; then |
| help "unknown option: $*" |
| return 1 |
| fi |
| done |
| dmesg "$@" |
| ) |
| |
| USAGE_free='[options]' |
| HELP_free=' |
| Display free/used memory info |
| ' |
| cmd_free() ( |
| # All options are harmless, so pass them through. |
| free "$@" |
| ) |
| |
| USAGE_meminfo='' |
| HELP_meminfo=' |
| Display detailed memory statistics |
| ' |
| cmd_meminfo() ( |
| if [ $# -eq 0 ]; then |
| cat /proc/meminfo |
| else |
| help "unknown option: $*" |
| return 1 |
| fi |
| ) |
| |
| USAGE_uptime='' |
| HELP_uptime=' |
| Display uptime/load info |
| ' |
| cmd_uptime() ( |
| if [ $# -eq 0 ]; then |
| uptime |
| else |
| help "unknown option: $*" |
| return 1 |
| fi |
| ) |
| |
| USAGE_uname='[-a|-s|-n|-r|-v|-m|-p|-i|-o]' |
| HELP_uname=' |
| Display system info |
| ' |
| cmd_uname() ( |
| # We whitelist only a few options. |
| local opt |
| for opt in "$@"; do |
| if ! printf '%s' "${opt}" | grep -sqE "^-[asnrvmpio]+$"; then |
| help "unknown option: $*" |
| return 1 |
| fi |
| done |
| uname "$@" |
| ) |
| |
| substr() { |
| local str="$1" |
| local start="$2" |
| local end="$3" |
| |
| if [ "$IS_BASH" = "1" ]; then |
| # NB: use printf to avoid echo interpreting -n |
| if [ -z "$end" ]; then |
| printf '%s\n' ${str:$start} |
| else |
| printf '%s\n' ${str:$start:$end} |
| fi |
| return |
| fi |
| |
| start=$(expr "$start" + 1) |
| |
| if [ ! -z "$end" ]; then |
| end=$(expr "$end" - 1) |
| fi |
| |
| printf '%s' "${str}" | cut -c${start}-${end} |
| } |
| |
| # Return true if the first arg is a valid command. |
| check_command() { |
| local command="$1" |
| |
| type "cmd_${command}" 2>/dev/null | head -1 | grep -q "function" |
| } |
| |
| dispatch() { |
| local line="$1" |
| local command="" |
| local p params="" |
| |
| local space_pos=$(expr index "$line" ' ') |
| |
| if [ $space_pos = 0 ]; then |
| command=$line |
| else |
| command=$(substr "$line" "0" "$space_pos") |
| command=${command% *} |
| params=$(substr "$line" "$space_pos") |
| fi |
| |
| if ! check_command "${command}"; then |
| help "unknown command: ${command}" |
| else |
| # See if --help was requested; if so, handle it directly so each command |
| # doesn't have to deal with it directly. |
| local p |
| for p in ${params}; do |
| if [ "${p}" = "-h" -o "${p}" = "--help" ]; then |
| cmd_help "${command}" |
| return 0 |
| fi |
| done |
| |
| # Only add valid commands to the history. |
| shell_history -s "$LINE_" |
| |
| # Set CURRENT_COMMAND for the `help` helper. |
| CURRENT_COMMAND="${command}" "cmd_${command}" ${params} |
| fi |
| } |
| |
| repl() { |
| echo "${INTRO_TEXT}" |
| if [ "$IS_BASH" != "1" ]; then |
| echo "Sorry, line editing and command history disabled due to" \ |
| "shell limitations." |
| fi |
| |
| # This will be set by the 'exit' command to tell us to quit. |
| local exit |
| |
| # Create a colorized prompt to make it easier to see commands start/finish. |
| local prompt |
| prompt="$(printf '%bcrosh>%b ' '\001\033[1;33m\002' '\001\033[0m\002')" |
| |
| while [ -z "${exit}" ]; do |
| if shell_read "${prompt}" LINE_; then |
| if [ ! -z "$LINE_" ]; then |
| dispatch "$LINE_" |
| fi |
| else |
| echo |
| return 1 |
| fi |
| done |
| } |
| |
| usage() { |
| if [ $# -ne 0 ]; then |
| # Send stdout below to stderr instead. |
| exec 1>&2 |
| fi |
| cat <<EOF |
| Usage: crosh [options] |
| |
| Options: |
| --dev Force dev mode. |
| --removable Force removable (USB) mode. |
| --usb Same as above. |
| --help, -h Show this help string. |
| EOF |
| if [ $# -ne 0 ]; then |
| echo "ERROR: $*" |
| exit 1 |
| else |
| exit 0 |
| fi |
| } |
| |
| main() { |
| # If we aren't installed, use local files for testing. |
| if [ ! -d "${CROSH_MODPATH}" ]; then |
| CROSH_MODPATH=$(dirname "$0") |
| echo "Loading from local paths instead: ${CROSH_MODPATH}" |
| fi |
| |
| while [ $# -gt 0 ]; do |
| case $1 in |
| --dev) |
| CROSH_DEVMODE="1" |
| ;; |
| --removable|--usb) |
| CROSH_REMOVABLE="1" |
| ;; |
| --help|-h) |
| usage |
| ;; |
| *) |
| usage "Unknown option: $1" |
| ;; |
| esac |
| shift |
| done |
| |
| load_extra_crosh |
| |
| INPUTRC="${CROSH_MODPATH}/inputrc.crosh" |
| |
| shell_history_init |
| |
| repl |
| |
| # Save the history after the clean exit. |
| shell_history -w "${HISTFILE}" |
| } |
| main "$@" |