blob: 308c6d0b12e51cc20a6e9f00c9c5f4057d719c1a [file] [log] [blame]
#!/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.
TRY_BASH=1
if echo "$@" | grep -q -- "--dash"; then
TRY_BASH=0
fi
# NOTE: This script *almost* works in dash. Unfortunately, dash is missing
# the 'history' builtin and readline support is missing from the 'read'
# builtin.
#
# Please test that any changes continue to work in dash by running
# '/build/board-name/bin/dash crosh --dash' before checking them in.
if type "history" 2>/dev/null | grep -q "shell builtin"; then
IS_BASH=1
else
IS_BASH=0
fi
if [ "$TRY_BASH" = "1" -a "$IS_BASH" != "1" -a -x "/bin/bash" ]; then
# Relaunch in bash if we can.
exec /bin/bash $0 "$@"
fi
#
# Please keep the help text in alphabetical order!
#
HELP='
exit
Exit crosh.
help
Display this help.
help_advanced
Display the help for more advanced commands, mainly used for debugging.
ping [-c count] [-i interval] [-n] [-s packetsize] [-W waittime] <destination>
Send ICMP ECHO_REQUEST packets to a network host. If <destination> is "gw"
then the next hop gateway for the default route is used.
ssh [optional args...]
Starts the ssh subsystem if invoked without any arguments.
"ssh <user> <host>", "ssh <user> <host> <port>", "ssh <user>@<host>",
or "ssh <user>@<host> <port>" connect without entering the subsystem.
ssh_forget_host
Remove a host from the list of known ssh hosts. This command displays
a menu of known hosts and prompts for the host to forget.
top
Run top.
'
HELP_ADVANCED='
connectivity
Shows connectivity status. "connectivity help" for more details
experimental_storage < status | enable | disable >
Enable or disable experimental storage features.
ff_debug [<tag_expr>] [--help] [--list_valid_tags] [--reset]
Add and remove flimflam debugging tags.
modem <command> [args...]
Interact with the 3G modem. Run "modem help" for detailed help.
modem_set_carrier carrier-name
Configures the modem for the specified carrier.
network_logging <wifi | cellular | ethernet>
A function that enables a predefined set of tags useful for
debugging the specified device.
network_diag [--date] [--flimflam] [--link] [--show-macs] [--wifi] [--help]
[--wifi-monitor] <host>
A function that performs a suite of network diagnostics. Saves a copy
of the output to your download directory.
rlz < status | enable | disable >
Enable or disable RLZ.
route [-n] [-6]
Display the routing tables.
set_apn [-n <network-id>] [-u <username>] [-p <password>] <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.
set_apn -c
Clear the APN to be used, so that the default APN will be used instead.
set_arpgw <true | false>
Turn on extra network state checking to make sure the default gateway
is reachable.
tpcontrol {status|taptoclick [on|off]|sensitivity [1-5]|set <property> <value>}
tpcontrol {syntp [on|off]}
Manually adjust advanced touchpad settings.
tracepath [-n] <destination>[/port]
Trace the path/route to a network host.
wpa_debug [<debug_level>] [--help] [--list_valid_level] [--reset]
Set wpa_supplicant debugging level.
syslog <message>
Logs a message to syslog.
chaps_debug [start|stop|<log_level>]
Sets the chapsd logging level. No arguments will start verbose logging.
'
CHROMEOS_INSTALL=/usr/sbin/chromeos-install
. "/usr/sbin/chromeos-common.sh" || exit 1
SRC=$(get_block_dev_from_partition_dev $(rootdev -s))
if [ "$(cat /sys/block/${SRC#/dev/}/removable)" == "1" ]; then
REMOVABLE=1
fi
if [ "$(echo "$@" | grep -- "--usb")" -o "$REMOVABLE" = "1" ]; then
. "$(dirname "$0")/crosh-usb"
fi
# Force dev behavior on dev images.
/usr/bin/crossystem "cros_debug?1"
DEVMODE=$((!$?))
if [ "$(echo "$@" | grep -- "--dev")" -o "$DEVMODE" = "1" ]; then
. "$(dirname "$0")/crosh-dev"
fi
if [ -e "$(dirname $0)/crosh-workarounds" ]; then
. "$(dirname "$0")/crosh-workarounds"
fi
ctrl_c() {
return
}
trap ctrl_c INT
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
}
cmd_help() {
echo "$HELP"
}
cmd_help_advanced() {
echo "$HELP_ADVANCED"
}
cmd_exit() {
exit
}
check_dforward() {
# Matches a port number between 8000 and 8999.
expr "$1" : '^8[0-9][0-9][0-9]$' > /dev/null
}
check_forward() {
# Matches three things, separated by ':':
# A port number, between 8000 and 8999;
# A hostname
# A port number, unrestricted
expr "$1" : '^8[0-9][0-9][0-9]:[[:alnum:]][-[:alnum:].]*:[1-9][0-9]*$' \
> /dev/null
}
check_serveraliveinterval() {
# Matches a number of seconds.
expr "$1" : '^[0-9][0-9]*$' > /dev/null;
}
check_keyfile() {
# Allow files in /home/chronos/user, /media. Note that we *do* allow .., so
# this isn't actually a security barrier, just a molly-guard to keep users
# from putting keys in insecure storage.
if [ ! -f "$1" -a ! -f "$HOME/Downloads/$1" ]; then
return 1;
fi
if [ ! -r "$1" -a ! -r "$HOME/Downloads/$1" ]; then
return 1;
fi
(expr "$1" : "^$HOME" > /dev/null) || \
(expr "$1" : '^/media/' > /dev/null) || \
(expr "$1" : '^[^/]*$' > /dev/null)
}
check_ssh_host() {
check_hostname "$1" || check_ipv6 "$1";
}
cmd_ssh() {
local user=""
local host=""
local port="22"
local idfile=""
local dforwards=""
local forwards=""
local nocmd=""
local serveraliveinterval=""
local line
local cmd
local params
local exit
local id=$(uuidgen)
local defopts="-e none -F /etc/ssh/ssh_config"
if [ ! -z "$1" ]; then
# Sets the username, host, and optionally port directly. We accept:
# 'user host', 'user host port', 'user@host', or 'user@host port'.
local at_pos=$(expr index "$1" '@')
if [ $at_pos = 0 ]; then
user="$1"
host="$2"
if [ ! -z "$3" ]; then
port="$3"
fi
else
user=$(substr "$1" "0" $(expr "$at_pos" - 1))
host=$(substr "$1" "$at_pos")
if [ ! -z "$2" ]; then
port="$2"
fi
fi
fi
if [ -z "$user" -o -z "$host" ]; then
while [ 1 ]; do
if ! shell_read "ssh> " line; then
echo;
exit=1;
break
fi
local space_pos=$(expr index "$line" ' ')
if [ $space_pos = 0 ]; then
cmd="$line"
params=""
else
cmd=$(substr "$line" "0" "$space_pos")
params=$(substr "$line" "$space_pos")
fi
case "$cmd" in
key)
if check_keyfile "$params"; then
mkdir -p -m 600 "$HOME/.ssh";
trap "rm -f $HOME/.ssh/key-$id" EXIT;
(cd "$HOME/Downloads" ; cp -- "$params" "$HOME/.ssh/key-$id")
chmod 600 "$HOME/.ssh/key-$id"
idfile="-i $HOME/.ssh/key-$id"
else
echo "File '$params' is not a valid key file. Key files must reside"
echo "under /media or $HOME. Key files in the Downloads directory may"
echo "be specified with an unqualified name."
fi
;;
dynamic-forward)
if ! check_dforward "$params"; then
echo "Invalid forward '$params'."
echo "Note that the local port number must be in 8000-8999."
else
dforwards="$dforwards -D$params"
fi
;;
forward)
if ! check_forward "$params"; then
echo "Invalid forward '$params'"
echo "Note that the local port number must be in 8000-8999."
else
forwards="$forwards -L$params"
fi
;;
nocmd)
nocmd="-N"
;;
server-alive-interval)
if ! check_serveraliveinterval "$params"; then
echo "Invalid ServerAliveInterval '$params'."
else
serveraliveinterval="-oServerAliveInterval=$params"
fi
;;
port)
if ! check_digits "$params"; then
echo "Invalid port '$params'"
else
port="$params"
fi
;;
user)
user="$params"
;;
host)
host="$params"
;;
exit)
exit=1
break
;;
connect)
break
;;
*)
echo "connect - connect"
echo "dynamic-forward port - dynamic socks proxy (-D)"
echo "forward port:host:port - static port forward (-L)"
echo "help - this"
echo "host <hostname> - remote hostname"
echo "key <file> - sets private key to use (-i)"
echo "nocmd - don't execute command (-N)"
echo "port <num> - port on remote host (-p)"
echo "server-alive-interval <num> - set ServerAliveInterval option"
echo "exit - exit ssh subsystem"
echo "user <username> - username on remote host"
echo "Note that this program can only bind local ports in the range"
echo "8000-8999, inclusive."
;;
esac
done
fi
if [ -z "$exit" ]; then
if [ -z "$user" ]; then
echo "No username given."
elif [ -z "$host" ]; then
echo "No host given."
elif ! check_username "$user"; then
echo "Invalid username '$user'"
elif ! check_ssh_host "$host"; then
echo "Invalid hostname '$host'"
else
ssh $defopts $idfile $dforwards $forwards $nocmd $serveraliveinterval \
-p "$port" -l "$user" "$host"
fi
fi
if [ -n "$idfile" ]; then
rm "$HOME/.ssh/key-$id";
trap - EXIT;
fi
}
cmd_ssh_forget_host() {
local known_hosts="$(readlink -f $HOME/.ssh/known_hosts)"
# Test that the known_hosts is a regular file and is not 0 length.
if [ ! -f "$known_hosts" -o ! -s "$known_hosts" ]; then
echo "No known hosts."
return
fi
local count="$(cat "$known_hosts" | wc -l)"
# Print an indexed list of the known hosts.
echo "Known hosts:"
awk '{ print " " NR ") " $1; }' "$known_hosts"
local hostno
while true; do
LINE_=""
shell_read "Please select a host to forget [1-$count]: " LINE_
if [ -z "$LINE_" ]; then
echo "Aborting."
return
fi
# Extract the numbers from the user input.
hostno="$(echo $LINE_ | sed 's/[^[:digit:]]*//g')"
# If they typed a number...
if [ -n "$hostno" ]; then
# And it was in the proper range...
if [ $hostno -gt 0 -a $hostno -le $count ]; then
# Then we can stop asking for input.
break
fi
fi
echo "Invalid selection. Please enter a number between 1 and $count."
done
local trimmed="$(awk -v I="$hostno" 'NR != I { print }' "$known_hosts")"
echo "$trimmed" > "$known_hosts"
chmod 644 "$known_hosts"
}
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 ! echo "$2" | grep -q "^[0-9]*$"; then
echo "$2 is not a valid integer literal"
cmd_swap usage
return 1
elif [ "$2" -ne 500 -a \
"$2" -ne 1000 -a \
"$2" -ne 2000 -a \
"$2" -ne 3000 -a \
"$2" -ne 4500 -a \
"$2" -ne 6000 ]; then
echo "invalid size $2. Valid choices: 500, 1000, 2000, 3000, 4500 or 6000"
cmd_swap usage
return 1
else
# remove file in case root owns it
rm -f $swap_enable_file
echo "$2" >$swap_enable_file
fi
echo "Swap will be ON at next reboot."
echo "You are turning on compressed swap, which is an experimental feature"
echo "of Chrome OS. System instability may result. To turn it off, use"
echo "the command \"swap disable\"."
;;
"disable")
rm -f $swap_enable_file
echo "Swap will be OFF at next reboot."
;;
"status")
/sbin/swapon -s
;;
*)
echo "Usage: swap < status | enable [<optional swap size (MB)>] | disable >"
return 1
;;
esac
}
cmd_experimental_storage() {
local enable=""
if [ -z "$1" ]; then
echo "Usage: experimental_storage < status | enable | disable >"
return 1
fi
case "$1" in
"status")
;;
"enable")
enable="true"
;;
"disable")
enable="false"
;;
*)
echo "Invalid parameter: $1"
return 1
;;
esac
if [ -z "$enable" ]; then
enable=$( dbus-send --system --type=method_call --print-reply \
--dest=org.chromium.CrosDisks /org/chromium/CrosDisks \
org.freedesktop.DBus.Properties.Get string:org.chromium.CrosDisks \
string:ExperimentalFeaturesEnabled 2>/dev/null )
if [ $? -eq 0 ]; then
if echo "$enable" | grep -iqs "true"; then
echo "Experimental storage features are currently enabled."
else
echo "Experimental storage features are currently disabled."
fi
else
echo "Could not determine the status of experimental storage features."
fi
else
if dbus-send --system --type=method_call --print-reply \
--dest=org.chromium.CrosDisks /org/chromium/CrosDisks \
org.freedesktop.DBus.Properties.Set \
string:org.chromium.CrosDisks string:ExperimentalFeaturesEnabled \
variant:boolean:${enable} >/dev/null 2>&1; then
echo "Experimental storage features have been ${1}d."
else
echo "Could not $1 experimental storage features."
fi
fi
}
cmd_ff_debug(){
/usr/bin/ff_debug "$@"
}
cmd_wpa_debug(){
/usr/bin/wpa_debug "$@"
}
cmd_set_arpgw(){
/usr/bin/set_arpgw "$@"
}
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")
echo "Usage: network_logging <wifi | cellular | ethernet>"
;;
*)
echo "Invalid parameter $1"
;;
esac
else
echo "Missing parameter wifi | cellular | ethernet"
fi
}
cmd_network_diag(){
/usr/bin/network_diagnostics "$@"
}
debugd() {
method="$1"; shift
dbus-send --system --print-reply --fixed --dest=org.chromium.debugd \
/org/chromium/debugd "org.chromium.debugd.$method" "$@"
}
cmd_ping() {
local option="dict:string:variant:"
# NB: use printf to avoid echo interpreting -n
while [ "$(printf '%s' "$1" | cut -c1)" = "-" ]; do
# Do just enough parsing to filter/map options; we
# depend on ping to handle final validation
if [ "$1" = "-i" ]; then
shift; option="${option}interval,int32:$1,"
elif [ "$1" = "-c" ]; then
shift; option="${option}count,int32:$1,"
elif [ "$1" = "-W" ]; then
shift; option="${option}waittime,int32:$1,"
elif [ "$1" = "-s" ]; then
shift; option="${option}packetsize,int32:$1,"
elif [ "$1" = "-n" ]; then
option="${option}numeric,boolean:true"
else
echo "Unknown option: $1"
return 1
fi
shift
done
if [ -z "$1" ]; then
echo "Missing parameter: destination"
return 1
fi
local dest="$1"
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=$(/sbin/route -n | awk '$1 == "0.0.0.0" { print $2; }')
if [ -z "$dest" ]; then
echo "Cannot determine primary gateway; routing table is:"
cmd_route -n
return 1
fi
fi
option=$(echo "$option" | sed 's/,$//')
# We want C-c to terminate the running ping so that the UI stays the same.
# Therefore, create a fifo to direct the output of the ping to, and have a
# subshell read from the fifo and emit to stdout. When the subshell ends (at a
# C-c), we stop the ping and clean up the fifo.
# no way to mktemp a fifo, so make a dir to hold it instead
dir=$(mktemp -d "/tmp/crosh-ping-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
debugd PingStart "fd:1" "string:$dest" "$option" 2>&1 > "$fifo" &
read pid < "$fifo"
echo "pid: $pid"
(while read line; do echo "$line"; done) < "$fifo"
debugd PingStop "string:$pid"
if [ $? -ne 0 ]; then
echo "Can't stop ping"
return 1
fi
rm -rf "$dir"
}
# TODO(dkrahn): Remove once this is the default. crosbug.com/22543
cmd_chaps() {
local flag_file="/home/chronos/.disable_chaps"
local enabled=1
local changed=0
if [ -r ${flag_file} ]; then
enabled=0
fi
if [ -z "$1" ]; then
if [ ${enabled} -eq 1 ]; then
echo "Currently enabled"
else
echo "Currently disabled"
fi
return
fi
if [ "$1" = "on" ]; then
if [ $enabled -eq 0 ]; then
changed=1;
fi
rm -f "${flag_file}"
else
if [ $enabled -eq 1 ]; then
changed=1
fi
touch "${flag_file}"
fi
if [ $changed -eq 1 ]; then
echo "You must reboot for this to take effect."
else
echo "No change."
fi
}
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*
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
}
cmd_route() {
local option="dict:string:variant:"
# NB: use printf to avoid echo interpreting -n
while [ "$(printf '%s' "$1" | cut -c1)" = "-" ]; do
if [ "$1" = "-n" ]; then
option="${option}numeric,boolean:true,"
elif [ "$1" = "-6" ]; then
option="${option}v6,boolean:true,"
else
echo "Unknown option: $1"
return 1
fi
shift
done
option=$(echo "$option" | sed 's/,$//')
debugd GetRoutes "$option"
}
cmd_tracepath() {
local option="dict:string:variant:"
# NB: use printf to avoid echo interpreting -n
while [ "$(printf '%s' "$1" | cut -c1)" = "-" ]; do
if [ "$1" = "-n" ]; then
option="${option}numeric,boolean:true,"
else
echo "Unknown option: $1"
return 1
fi
shift
done
option=$(echo "$option" | sed 's/,$//')
if [ -z "$1" ]; then
echo "Missing parameter: destination"
return 1
fi
# We want C-c to terminate the running tracepath so that the UI stays the same.
# Therefore, create a fifo to direct the output of the tracepath to, and have a
# subshell read from the fifo and emit to stdout. When the subshell ends (at a
# C-c), we stop the tracepath and clean up the fifo.
# TODO(ellyjones): factor this (and the similar code in cmd_ping) out somehow
# no way to mktemp a fifo, so make a dir to hold it instead
dir=$(mktemp -d "/tmp/crosh-tracepath-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
debugd TracePathStart "fd:1" "string:$1" "$option" 2>&1 > "$fifo" &
read pid < "$fifo"
echo "pid: $pid"
(while read line; do echo "$line"; done) < "$fifo"
debugd TracePathStop "string:$pid"
if [ $? -ne 0 ]; then
echo "Can't stop tracepath"
return 1
fi
rm -rf "$dir"
}
cmd_top() {
# -s is "secure" mode, which disables kill, renice, and change display/sleep
# interval.
top -s
}
cmd_modem() {
/usr/bin/modem "$@"
}
cmd_modem_set_carrier() {
/usr/bin/modem set-carrier "$@"
}
cmd_set_apn() {
/usr/bin/set_apn "$@"
}
cmd_connectivity() {
/usr/bin/connectivity "$@"
}
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
}
cmd_tpcontrol() {
/opt/google/touchpad/tpcontrol "$@"
}
cmd_try_touch_experiment() {
# Test out a new set of Touch parameters briefly, then revert them.
# This takes in a single string of hex digits ie ([a-fA-F0-9]*) that
# correspond to an ascii string of the form
# PARAMETER1 NAME:PARAMETER1 VALUE,PARAMETER2 NAME:PARAMETER2 VALUE,...
# The input is encoded as hex to a) eliminate escaping issues for spaces
# and b) obfuscate which properties are being changed from the user so they
# don't see what they're changing, and have it influence their decision.
# First, check and make sure only hex digit characters have been passed in
if [ -n "`echo \"$@\" | sed 's/[0-9a-fA-F]//g'`" ]; then
return 1
fi
# Converting the input from hex to ascii
treatment="`echo \"$@\" | sed 's/\(..\)/\\\\u\1/g'`"
treatment=`printf "${treatment}\n"`
if [ -n "`echo \"${treatment}\" | sed 's/[0-9a-zA-Z :,\.\-]//g'`" ]; then
return 1;
fi
# Get the current system setup and old values so we can restore them
device=`/opt/google/touchpad/tpcontrol listdev`
old_values="`xinput list-props ${device}`"
# Splitting the treatment into property name/value pairs
treatment_commands=""
reset_commands=""
IFS=,
for pair in ${treatment}; do
name="`echo "${pair}" | cut -d ":" -f1`"
new_value="`echo "${pair}" | cut -d ":" -f2`"
property_entry=`echo "${old_values}" | grep "${name}" | tr -d "\t"`
property_number=`echo "${property_entry}" | sed "s/^.*(\([0-9]*\)).*$/\1/"`
old_value=`echo "${property_entry}" | sed "s/^[^:]*:\(.*\)$/\1/"`
change_cmd="${property_number}:${new_value}"
reset_cmd="${property_number}:${old_value}"
treatment_commands="${change_cmd},${treatment_commands}"
reset_commands="${reset_cmd},${reset_commands}"
done
echo "Applying Changes now..."
change_properties "${treatment_commands}", "${device}"
echo "Press Ctrl+C to return to your original settings."
cat
echo "Reverting Changes now..."
change_properties "${reset_commands}", "${device}"
# Checking to make sure everything is back to normal
restored_values="`xinput list-props ${device}`"
if [ "${old_values}" != "${restored_values}" ]; then
echo "ERROR: Some properties may not have been reset correctly!"
echo "If your touchpad is misbehaving, you can restore it by logging out"
echo "and back in again."
fi
}
change_properties() {
# change format: PROPERTY_NUMBER:PROPERTY_VALUE,...
cleaned="`echo "$1" | sed "s/,*$//g"`"
IFS=,
for cmd in ${cleaned}; do
property_number="`echo "${cmd}" | cut -d ":" -f1`"
property_value="`echo "${cmd}" | cut -d ":" -f2`"
xinput set-prop "$2" "${property_number}" "${property_value}"
done
}
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}"
;;
*)
echo "Usage: rlz < status | enable | disable >"
return 1
;;
esac
if [ $changed -eq 1 ]; then
echo "You must reboot for this to take effect."
else
echo "No change."
fi
}
cmd_syslog() {
logger -t crosh -- "$*"
}
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
echo "$str" | cut -c${start}-${end}
}
dispatch() {
local line="$1"
local command=""
local params=""
local space_pos=$(expr index "$line" ' ')
if [ $space_pos = 0 ]; then
command=$line
else
command=$(substr "$line" "0" "$space_pos")
params=$(substr "$line" "$space_pos")
fi
if ! type "cmd_$command" 2>/dev/null | head -1 | grep -q "function"; then
echo "Unknown command: '$command'"
else
command="cmd_$command"
$command $params
fi
}
# Checks that a given string looks like a hostname or IPv4 address (starts
# with an alphanumeric and contains only alphanumeric, '.', or '-'
# characters).
check_hostname() {
expr "$1" : '^[[:alnum:]][-[:alnum:].]*$' > /dev/null
}
# Checks that a given string could plausibly be an IPv6 address
# (hexadecimal, ':', and '.' characters only, followed by an optional zone
# index consisting of a '%' and a device name).
check_ipv6() {
echo "$1" | /bin/grep -E -q '^[0-9a-fA-F:.]+(%[a-z0-9]+)?$'
}
# Checks that a given string starts with an alphanumeric, and contains only
# alphanumeric and zero or more of "_:.~%$^\-"
check_username() {
expr "$1" : '^[[:alnum:]][[:alnum:]_:.~%$^\-]*$' > /dev/null
}
check_digits() {
expr "$1" : '^[[:digit:]]*$' > /dev/null
}
# 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
repl() {
echo "Welcome to crosh, type 'help' for a list of commands."
if [ "$IS_BASH" != "1" ]; then
echo "Sorry, line editing and command history disabled due to" \
"shell limitations."
fi
while [ 1 ]; do
if shell_read "crosh> " LINE_; then
if [ ! -z "$LINE_" ]; then
shell_history -s "$LINE_"
dispatch "$LINE_"
fi
else
echo
return 1
fi
done
}
INPUTRC="$(dirname "$0")/inputrc.crosh"
HISTFILE="$HOME/.crosh_history"
shell_history -r $HISTFILE
# Initialize pseudo completion support.
if [ "$IS_BASH" = "1" ]; then
bind 'TAB:dynamic-complete-history' 2>/dev/null
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
repl
shell_history -w $HISTFILE