· 6 years ago · Apr 24, 2019, 09:18 PM
1#!/bin/dash
2# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5#
6# NOTE: This script works in dash, but is not as featureful. Specifically,
7# dash omits readline support (history & command line editing). So we try
8# to run through bash if it exists, otherwise we stick to dash. All other
9# code should be coded to the POSIX standard and avoid bashisms.
10#
11# Please test that any changes continue to work in dash by running
12# '/build/$BOARD/bin/dash crosh --dash' before checking them in.
13# Disable path expansion at the command line. None of our builtins need or want
14# it, and it's safer/saner to disallow it in the first place.
15set -f
16# Don't allow SIGHUP to terminate crosh. This guarantees that even if the user
17# closes the crosh window, we make our way back up to the main loop, which gives
18# cleanup code in command handlers a chance to run.
19trap '' HUP
20# Do not let CTRL+C or CTRL+\ kill crosh itself. This does let the user kill
21# commands that are run by crosh (like `ping`).
22trap : INT QUIT
23# If it exists, use $DATA_DIR to define the $HOME location, as a first step
24# to entirely removing the use of $HOME. (Though HOME should be used as
25# fall-back when DATA_DIR is unset.)
26# TODO(keescook): remove $HOME entirely crbug.com/333031
27if [ "${DATA_DIR+set}" = "set" ]; then
28 export HOME="${DATA_DIR}/user"
29fi
30IS_BASH=0
31try_bash() {
32 # If dash was explicitly requested, then nothing to do.
33 case " $* " in
34 *" --dash "*) return 0;;
35 esac
36 # If we're already bash, then nothing to do.
37 if type "history" 2>/dev/null | grep -q "shell builtin"; then
38 IS_BASH=1
39 return 0
40 fi
41 # Still here? Relaunch in bash.
42 exec /bin/bash "$0" "$@"
43}
44try_bash "$@"
45INTRO_TEXT="Welcome to crosh, the Chrome OS developer shell.
46If you got here by mistake, don't panic! Just close this tab and carry on.
47Type 'help' for a list of commands.
48If you want to customize the look/behavior, you can use the options page.
49Load it by using the Ctrl+Shift+P keyboard shortcut.
50"
51# Gets set to "1" when in dev mode.
52CROSH_DEVMODE=
53# Gets set to "1" when running on removable media (e.g. USB stick).
54CROSH_REMOVABLE=
55CROSH_MODPATH="/usr/share/crosh"
56check_digits() {
57 expr "$1" : '^[[:digit:]]*$' > /dev/null
58}
59# Load all modules found in the specified subdir.
60load_modules() {
61 local subdir="$1"
62 local dir="${CROSH_MODPATH}/${subdir}"
63 local mod
64 # Turn on path expansion long enough to find local modules.
65 set +f
66 for mod in "${dir}"/[0-9][0-9]-*.sh; do
67 # Then turn path expansion back off.
68 set -f
69 if [ -e "${mod}" ]; then
70 echo "Loading extra module: ${mod}"
71 . "${mod}" || :
72 fi
73 done
74}
75load_extra_crosh() {
76 # First load the common modules (board/project/etc... specific).
77 load_modules "extra.d"
78 # Load the removable modules, if the rootfs is on removable
79 # media. e.g. It's a USB stick.
80 if [ -z "${CROSH_REMOVABLE}" ]; then
81 local src
82 if [ -e /usr/share/misc/chromeos-common.sh ]; then
83 . "/usr/share/misc/chromeos-common.sh" || exit 1
84 src=$(get_block_dev_from_partition_dev "$(rootdev -s)")
85 local removable="$(cat "/sys/block/${src#/dev/}/removable" 2>/dev/null)"
86 if [ "${removable}" = "1" ]; then
87 CROSH_REMOVABLE="1"
88 fi
89 fi
90 fi
91 if [ "${CROSH_REMOVABLE}" = "1" ]; then
92 load_modules "removable.d"
93 fi
94 # Load the dev-mode modules, if in dev mode, or if forced.
95 # This comes last so it can override any release modules.
96 if [ -z "${CROSH_DEVMODE}" ]; then
97 if type crossystem >/dev/null 2>&1; then
98 crossystem "cros_debug?1"
99 CROSH_DEVMODE=$((!$?))
100 else
101 echo "Could not locate 'crossystem'; assuming devmode is off."
102 fi
103 fi
104 if [ "${CROSH_DEVMODE}" = "1" ]; then
105 load_modules "dev.d"
106 fi
107}
108shell_read() {
109 local prompt="$1"
110 shift
111 if [ "$IS_BASH" -eq "1" ]; then
112 # In bash, -e gives readline support.
113 read -p "$prompt" -e "$@"
114 else
115 read -p "$prompt" "$@"
116 fi
117}
118shell_history() {
119 if [ "$IS_BASH" -eq "1" ]; then
120 # In bash, the history builtin can be used to manage readline history
121 history "$@"
122 fi
123}
124shell_history_init() {
125 # Do not set any HISTXXX vars until after security check below.
126 local histfile="${HOME}/.crosh_history"
127 # Limit the history to the last 100 entries to keep the file from growing
128 # out of control (unlikely, but let's be safe).
129 local histsize="100"
130 # For security sake, let's clean up the file before we let the shell get a
131 # chance to read it. Clean out any non-printable ASCII chars (sorry UTF8
132 # users) as it's the easiest way to be sure. We limit to 4k to be sane.
133 # We need a tempfile on the same device to avoid races w/multiple crosh
134 # tabs opening at the same time.
135 local tmpfile
136 if ! tmpfile="$(mktemp "${histfile}.XXXXXX")"; then
137 echo "warning: could not clean up history; ignoring it"
138 return
139 fi
140 # Ignore cat errors in case it doesn't exist yet.
141 cat "${histfile}" 2>/dev/null |
142 tr -dc '[:print:]\t\n' |
143 tail -n "${histsize}" |
144 tail -c 4096 > "${tmpfile}"
145 if ! mv "${tmpfile}" "${histfile}"; then
146 echo "warning: could not clean up history; ignoring it"
147 rm -f "${tmpfile}"
148 return
149 fi
150 # Set this before any other history settings as some of them implicitly
151 # operate on this file.
152 HISTFILE="${histfile}"
153 # Now we can limit the size of the history file.
154 HISTSIZE=${histsize}
155 HISTFILESIZE=${histsize}
156 # Do not add entries that begin with a space, and dedupe sequential commands.
157 HISTCONTROL="ignoreboth"
158 # Initialize pseudo completion support. Do it before we load the user's
159 # history so that new entries don't come after old ones.
160 if [ ${IS_BASH} -eq 1 ]; then
161 local f
162 for f in $(registered_visible_commands); do
163 # Do not add duplicates to avoid ballooning history.
164 grep -qs "^${f} *$" "${HISTFILE}" || shell_history -s "${f}"
165 done
166 fi
167 # Now load the user's history.
168 shell_history -r "${HISTFILE}"
169}
170# Returns status 0 if the argument is a valid positive integer
171# (i.e. it's not the null string, and contains only digits).
172is_numeric() {
173 ! echo "$1" | grep -qE '[^0-9]|^$'
174}
175# Prints the value corresponding to the passed-in field in the output from
176# dump_power_status.
177get_power_status_field() {
178 local field="$1"
179 dump_power_status | awk -v field="${field}" '$1 == field { print $2 }'
180}
181# Returns value of variable with given name.
182expand_var() {
183 local var="$1"
184 eval echo "\"\$${var}\""
185}
186# Determine whether the variable is set in the environment.
187var_is_set() {
188 local var="$1"
189 [ "$(expand_var "{${var}+set}")" = "set" ]
190}
191USAGE_help='[command]'
192HELP_help='
193 Display general help, or details for a specific command.
194'
195cmd_help() (
196 local cmd
197 case $# in
198 0)
199 local cmds="exit help help_advanced ping top"
200 for cmd in ${cmds}; do
201 cmd_help "${cmd}"
202 done
203 ;;
204 1)
205 # The ordering and relationship of variables here is subtle.
206 # Consult README.md for more details.
207 cmd="$1"
208 if ! registered_crosh_command "${cmd}"; then
209 echo "help: unknown command '${cmd}'"
210 return 1
211 elif ! check_command_available "${cmd}"; then
212 echo "help: command '${cmd}' is not available"
213 return 1
214 else
215 # If the command has a custom help func, call it.
216 if registered_crosh_command_help "${cmd}"; then
217 # Make sure the output is at least somewhat close to our standardized
218 # form below. We always want the program name first and a blank line
219 # at the end. This way `help_advanced` isn't completely ugly.
220 echo "${cmd}"
221 # The sed statement trims the first & last lines if they're blank.
222 "help_${cmd}" | sed -e '1{/^$/d}' -e '${/^$/d}'
223 echo
224 elif var_is_set "USAGE_${cmd}"; then
225 # Only show this function if the usage strings are actually set.
226 local usage="$(expand_var "USAGE_${cmd}")"
227 local help="$(expand_var "HELP_${cmd}")"
228 printf '%s %s %s\n\n' "${cmd}" "${usage}" "${help}"
229 fi
230 fi
231 ;;
232 *)
233 help "too many arguments"
234 return 1
235 ;;
236 esac
237)
238# Useful alias for commands to call us.
239help() {
240 if [ $# -ne 0 ]; then
241 printf 'ERROR: %s\n\n' "$*"
242 fi
243 # This is set by `dispatch` when a valid command has been found.
244 if [ -n "${CURRENT_COMMAND}" ]; then
245 cmd_help "${CURRENT_COMMAND}"
246 fi
247}
248USAGE_help_advanced=''
249HELP_help_advanced='
250 Display the help for more advanced commands, mainly used for debugging.
251'
252cmd_help_advanced() (
253 local cmd
254 if [ $# -ne 0 ]; then
255 help "too many arguments"
256 return 1
257 fi
258 for cmd in $(registered_visible_commands); do
259 if check_command_available "${cmd}"; then
260 cmd_help "${cmd}"
261 fi
262 done
263)
264# We move the trailing brace to the next line so that we avoid the style
265# checker from rejecting the use of braces. We cannot use subshells here
266# as we want the set the exit variable in crosh itself.
267# http://crbug.com/318368
268USAGE_exit=''
269HELP_exit='
270 Exit crosh.
271'
272cmd_exit()
273{
274 exit='y'
275}
276USAGE_set_time='[<time string>]'
277HELP_set_time='
278 Sets the system time if the the system has been unable to get it from the
279 network. The <time string> uses the format of the GNU coreutils date command.
280'
281cmd_set_time() (
282 local spec="$*"
283 if [ -z "${spec}" ]; then
284 echo "A date/time specification is required."
285 echo "E.g., set_time 10 February 2012 11:21am"
286 echo "(Remember to set your timezone in Settings first.)"
287 return
288 fi
289 local sec status
290 sec=$(date +%s --date="${spec}" 2>&1)
291 status=$?
292 if [ ${status} -ne 0 -o -z "${sec}" ]; then
293 echo "Unable to understand the specified time:"
294 echo "${sec}"
295 return
296 fi
297 local reply
298 reply=$(dbus-send --system --type=method_call --print-reply \
299 --dest=org.torproject.tlsdate /org/torproject/tlsdate \
300 org.torproject.tlsdate.SetTime "int64:$((sec))" 2>/dev/null)
301 status=$?
302 if [ ${status} -ne 0 ]; then
303 echo "Time not set. Unable to communicate with the time service."
304 return
305 fi
306 # Reply format: <dbus response header>\n uint32 <code>\n
307 local code
308 code=$(echo "${reply}" | sed -n -E '$s/.*uint32 ([0-9]).*/\1/p')
309 case "${code}" in
310 0)
311 echo "Time has been set."
312 ;;
313 1)
314 echo "Requested time was invalid (too large or too small): ${sec}"
315 ;;
316 2)
317 echo "Time not set. Network time cannot be overriden."
318 ;;
319 3)
320 echo "Time not set. There was a communication error."
321 ;;
322 *)
323 echo "An unexpected response was received: ${code}"
324 echo "Details: ${reply}"
325 esac
326)
327# Check if a particular Chrome feature is enabled.
328# Use the DBus method name as the parameter.
329is_chrome_feature_enabled() {
330 local method="$1"
331 local reply status
332 reply=$(dbus-send --system --type=method_call --print-reply \
333 --dest=org.chromium.ChromeFeaturesService \
334 /org/chromium/ChromeFeaturesService \
335 "org.chromium.ChromeFeaturesServiceInterface.${method}" \
336 "string:${CROS_USER_ID_HASH}" 2>/dev/null)
337 status=$?
338 [ ${status} -eq 0 -a "${reply##* }" = "true" ]
339}
340imageloader() {
341 # Default timeout 30 seconds.
342 local timeout="--reply-timeout=30000"
343 case $1 in
344 --reply-timeout=*) timeout=$1; shift;;
345 esac
346 local method="$1"; shift
347 dbus-send ${timeout} --system --type=method_call \
348 --fixed --print-reply --dest=org.chromium.ComponentUpdaterService \
349 /org/chromium/ComponentUpdaterService \
350 "org.chromium.ComponentUpdaterService.${method}" "$@" \
351 2>&1 >/dev/null
352}
353HELP_vmc=''
354EXEC_vmc='/usr/bin/vmc'
355cmd_vmc() (
356 if ! is_chrome_feature_enabled "IsCrostiniEnabled"; then
357 echo "This command is not available."
358 return 1
359 fi
360 vmc "$@"
361)
362help_vmc() (
363 vmc --help
364)
365USAGE_vsh='<vm_name> [<container_name>]'
366HELP_vsh='
367 Connect to a shell inside the VM <vm_name>, or to a shell inside the container
368 <container_name> within the VM <vm_name>.
369'
370EXEC_vsh='/usr/bin/vsh'
371cmd_vsh() (
372 if ! is_chrome_feature_enabled "IsCrostiniEnabled"; then
373 echo "This command is not available."
374 return 1
375 fi
376 if [ $# -ne 1 -a $# -ne 2 ]; then
377 help "Missing vm_name"
378 return 1
379 fi
380 local vm_name="$1"; shift
381 if [ $# -eq 1 ]; then
382 local container_name="$1"; shift
383 vsh --vm_name="${vm_name}" --owner_id="${CROS_USER_ID_HASH}" \
384 --target_container="${container_name}"
385 else
386 vsh --vm_name="${vm_name}" --owner_id="${CROS_USER_ID_HASH}" -- \
387 LXD_DIR=/mnt/stateful/lxd \
388 LXD_CONF=/mnt/stateful/lxd_conf
389 fi
390)
391# Set the vars to pass the unittests ...
392USAGE_ssh=''
393HELP_ssh=''
394# ... then unset them to hide the command from "help" output.
395unset USAGE_ssh HELP_ssh
396cmd_ssh() (
397 cat <<EOF
398The 'ssh' command has been removed. Please install the official SSH extension:
399https://chrome.google.com/webstore/detail/pnhechapfaindjhompbnflcldabbghjo
400EOF
401)
402USAGE_swap='[ enable <size (MB)> | disable | start | stop | status
403 | set_margin <discard threshold (MB)> | set_extra_free <amount (MB)>
404 | set_min_filelist <amount (MB)> ]'
405HELP_swap='
406 Change kernel memory manager parameters
407 (FOR EXPERIMENTS ONLY --- USE AT OWN RISK)
408 "swap status" (also "swap" with no arguments) shows the values of various
409 memory manager parameters and related statistics.
410 The enable/disable options enable or disable compressed swap (zram)
411 persistently across reboots, and take effect at the next boot. The enable
412 option takes the size of the swap area (in megabytes before compression).
413 If the size is omitted, the factory default is chosen.
414 The start/stop options turn swap on/off immediately, but leave the settings
415 alone, so that the original behavior is restored at the next boot.
416 WARNING: if swap is in use, turning it off can cause the system to
417 temporarily hang while the kernel frees up memory. This can take
418 a long time to finish.
419 The set_margin, set_min_filelist, and set_extra_free options change
420 kernel parameters with similar names. The change is immediate and
421 persistent across reboots. Using the string "default" as the value
422 restores the factory default behavior.
423'
424cmd_swap() (
425 local cmd="${1:-}"
426 shift
427 # Check the usage first.
428 case "${cmd}" in
429 enable)
430 if [ $# -gt 1 ]; then
431 help "${cmd} takes only one optional argument"
432 return 1
433 fi
434 ;;
435 disable|start|stop|status|"")
436 if [ $# -ne 0 ]; then
437 help "${cmd} takes no arguments"
438 return 1
439 fi
440 ;;
441 set_margin|set_extra_free|set_min_filelist)
442 if [ $# -ne 1 ]; then
443 help "${cmd} takes one argument"
444 return 1
445 fi
446 ;;
447 *)
448 help "unknown option: ${cmd}"
449 return 1
450 ;;
451 esac
452 # Then actually process the request.
453 case "${cmd}" in
454 "enable")
455 local size="${1:-default}"
456 if [ "${size}" = "default" ]; then
457 size=-1
458 elif ! is_numeric "${size}"; then
459 help "'${size}' is not a valid number"
460 return 1
461 fi
462 debugd SwapEnable "int32:${size}" "boolean:false"
463 ;;
464 "disable")
465 debugd SwapDisable "boolean:false"
466 ;;
467 "start")
468 debugd SwapStartStop "boolean:true"
469 ;;
470 "stop")
471 debugd SwapStartStop "boolean:false"
472 ;;
473 "set_margin"|"set_extra_free"|"set_min_filelist")
474 local amount="$1"
475 if [ "${amount}" = "default" ]; then
476 # Special value requesting use of the factory default value.
477 amount=-1
478 elif ! is_numeric "${amount}"; then
479 help "${cmd} takes a positive integer argument"
480 return 1
481 fi
482 debugd SwapSetParameter "string:${cmd#set_}" "int32:${amount}"
483 ;;
484 "status"|"")
485 debugd SwapStatus
486 ;;
487 esac
488)
489USAGE_time_info=''
490HELP_time_info='
491 Returns the current synchronization state for the time service.
492'
493cmd_time_info() (
494 echo "Last time synchronization information:"
495 dbus-send --system --type=method_call --print-reply \
496 --dest=org.torproject.tlsdate /org/torproject/tlsdate \
497 org.torproject.tlsdate.LastSyncInfo 2>/dev/null |
498 sed -n \
499 -e 's/boolean/network-synchronized:/p' \
500 -e 's/string/last-source:/p' \
501 -e 's/int64/last-synced-time:/p'
502)
503USAGE_bt_console='[<agent capability>]'
504HELP_bt_console='
505 Enters a Bluetooth debugging console. Optional argument specifies the
506 capability of a pairing agent the console will provide; see the Bluetooth
507 Core specification for valid options.
508'
509EXEC_bt_console='/usr/bin/bluetoothctl'
510cmd_bt_console() (
511 "${EXEC_bt_console}" "${1:+--agent=$1}"
512)
513USAGE_newblue='<enable | disable>'
514HELP_newblue='Set the preference of enabling/disabling the use of Newblue
515Bluetooth stack. In order for the preference to take effect, please login as
516the owner account and go to chrome://flags to enable the "Newblue" flag and
517reboot the device.
518'
519cmd_newblue() (
520 if [ $# -ne 1 ]; then
521 help "unexpected number of argument"
522 return 1
523 fi
524 local enabled
525 case "$1" in
526 enable)
527 enabled="true"
528 ;;
529 disable)
530 enabled="false"
531 ;;
532 *)
533 help "unknown option $1"
534 return 1
535 ;;
536 esac
537 dbus-send --system --type=method_call --dest=org.bluez /org/bluez \
538 org.chromium.BluetoothExperimental.SetNewblueEnabled "boolean:${enabled}"
539 if [ $? -ne 0 ]; then
540 echo "ERROR: setting newblue status failed"
541 return 1
542 fi
543)
544# Set the help string so crosh can discover us automatically.
545HELP_ff_debug=''
546EXEC_ff_debug='/usr/bin/ff_debug'
547cmd_ff_debug() (
548 debugd_shill ff_debug "$@"
549)
550help_ff_debug() (
551 cmd_ff_debug --help
552)
553# Set the help string so crosh can discover us automatically.
554HELP_wpa_debug=''
555EXEC_wpa_debug='/usr/bin/wpa_debug'
556cmd_wpa_debug() (
557 debugd_shill wpa_debug "$@"
558)
559help_wpa_debug() (
560 cmd_wpa_debug --help
561)
562USAGE_authpolicy_debug='<level>'
563HELP_authpolicy_debug='
564 Set authpolicy daemon debugging level.
565 <level> can be 0 (quiet), 1 (taciturn), 2 (chatty), or 3 (verbose).
566'
567cmd_authpolicy_debug() (
568 if [ $# -ne 1 ]; then
569 help "exactly one argument (level) accepted"
570 return 1
571 fi
572 local level="$1"
573 if ! check_digits "${level}"; then
574 help "level must be a number"
575 return 1
576 fi
577 local reply status
578 reply=$(dbus-send --system --type=method_call --print-reply \
579 --dest=org.chromium.AuthPolicy /org/chromium/AuthPolicy \
580 "org.chromium.AuthPolicy.SetDefaultLogLevel" "int32:$1" 2>&1)
581 status=$?
582 if [ ${status} -ne 0 ]; then
583 echo "ERROR: ${reply}"
584 return 1
585 fi
586 # Reply format: <dbus response header>\n string "<error_message>"\n
587 local error_message
588 error_message=$(echo "${reply}" | sed -n -E '$s/.*string "(.*)"/\1/p')
589 if [ "${error_message}" = "" ]; then
590 echo "Successfully set debugging level to ${level}."
591 if [ ${level} -gt 0 ]; then
592 echo "Debug logs turned on for 30 minutes. Be sure to turn them off again"
593 echo "when you are done, e.g. by rebooting the device or setting the"
594 echo "level to 0. Authpolicy debugging info will be written into"
595 echo "/var/log/authpolicy.log, and will be attached to the next Feedback"
596 echo "report you send to Google. You may review these logs before sending"
597 echo "them."
598 else
599 echo "Debug logs turned off."
600 fi
601 else
602 help "${error_message}"
603 fi
604)
605USAGE_set_arpgw='<true | false>'
606HELP_set_arpgw='
607 Turn on extra network state checking to make sure the default gateway
608 is reachable.
609'
610cmd_set_arpgw() (
611 debugd_shill set_arpgw "$@"
612)
613USAGE_set_wake_on_lan='<true | false>'
614HELP_set_wake_on_lan='
615 Enable or disable Wake on LAN for Ethernet devices. This command takes
616 effect after re-connecting to Ethernet and is not persistent across system
617 restarts.
618'
619cmd_set_wake_on_lan() (
620 debugd_shill set_wake_on_lan "$@"
621)
622USAGE_wifi_power_save='< status | enable | disable >'
623HELP_wifi_power_save='
624 Enable or disable WiFi power save mode. This command is not persistent across
625 system restarts.
626'
627cmd_wifi_power_save() (
628 case "$1" in
629 "status"|"")
630 debugd GetWifiPowerSave
631 ;;
632 "enable")
633 debugd SetWifiPowerSave "boolean:true"
634 ;;
635 "disable")
636 debugd SetWifiPowerSave "boolean:false"
637 ;;
638 *)
639 help "unknown option: $1"
640 return 1
641 ;;
642 esac
643)
644# Set the vars to pass the unittests ...
645USAGE_network_logging=''
646HELP_network_logging=''
647# ... then unset them to hide the command from "help" output.
648unset USAGE_network_logging HELP_network_logging
649cmd_network_logging() (
650 cat <<EOF
651This command has been removed. Please use the Chrome page instead, and select
652your interface there:
653chrome://net-internals/#chromeos
654EOF
655)
656# Set the help string so crosh can discover us automatically.
657HELP_network_diag=''
658EXEC_network_diag='/usr/bin/network_diag'
659cmd_network_diag() (
660 case " $* " in
661 *" --no-log "*)
662 debugd_shill network_diag "$@"
663 ;;
664 *)
665 # Create a mostly unique filename. We won't care about clobbering existing
666 # files here as this is a user generated request, and we scope the filename
667 # to this specific use case.
668 local downloads_dir="/home/user/${CROS_USER_ID_HASH}/Downloads"
669 local timestamp="$(date +'%Y-%m-%d.%H-%M-%S')"
670 local logname="network_diagnostics_${timestamp}.txt"
671 local logpath="${downloads_dir}/${logname}"
672 rm -f "${logpath}"
673 echo "Saving output to Downloads under: ${logname}"
674 debugd_shill network_diag "$@" | tee "${logpath}"
675 ;;
676 esac
677)
678help_network_diag() (
679 debugd_shill network_diag --help
680)
681u2f_warning='
682 ### IMPORTANT: The U2F feature is experimental and not suitable for
683 ### general production use in its current form. The current
684 ### implementation is still in flux and some features (including
685 ### security-relevant ones) are still missing. You are welcome to
686 ### play with this, but use at your own risk. You have been warned.
687'
688USAGE_u2f_flags='<u2f | g2f>[, user keys, verbose]'
689HELP_u2f_flags="
690${u2f_warning}
691 Set flags to override the second-factor authentication daemon configuration.
692 u2f: Always enable the standard U2F mode even if not set in device policy.
693 g2f: Always enable the U2F mode plus some additional extensions.
694 user_keys: Enable user-specific keys.
695 verbose: Increase the daemon logging verbosity in /var/log/messages.
696"
697cmd_u2f_flags() (
698 echo "${u2f_warning}"
699 debugd SetU2fFlags "string:$*"
700)
701debugd() {
702 # Default timeout 30 seconds.
703 local timeout="--reply-timeout=30000"
704 case $1 in
705 --reply-timeout=*) timeout=$1; shift;;
706 esac
707 local method="$1"; shift
708 dbus-send ${timeout} --system --print-reply --fixed --dest=org.chromium.debugd \
709 /org/chromium/debugd "org.chromium.debugd.$method" "$@"
710}
711# Run a debugd command for a long time and poll its output.
712# This expects Start & Stop methods.
713debugd_poll() (
714 local methodbase="$1"; shift
715 local pid fifo
716 # Make sure we clean up the background process and temp files when the
717 # user kills us with CTRL+C or CTRL+\.
718 cleanup() {
719 # Don't let the user kill us while cleaning up.
720 trap : INT QUIT
721 if [ -n "${pid}" ]; then
722 if ! debugd "${methodbase}Stop" "string:${pid}"; then
723 echo "warning: could not stop ${methodbase}"
724 fi
725 pid=''
726 fi
727 if [ -n "${fifo}" ]; then
728 dir=$(dirname "${fifo}")
729 rm -rf "${dir}"
730 fifo=''
731 fi
732 }
733 trap cleanup INT QUIT
734 if ! fifo="$(mk_fifo)"; then
735 # The mk_fifo command already showed a warning.
736 return 1
737 fi
738 debugd "${methodbase}Start" "$@" 2>&1 >"${fifo}" &
739 read pid < "${fifo}"
740 # Background cat and block with `wait` to give debugd a chance to flush
741 # output on trapped signal.
742 cat "${fifo}" &
743 wait $!
744 cleanup
745)
746# Run a shill script via debugd.
747debugd_shill() {
748 local script="$1"; shift
749 local args="$(printf '%s,' "$@")"
750 debugd_poll RunShillScript "fd:1" "string:${script}" "array:string:${args%,}"
751}
752USAGE_ping="[-4] [-6] [-c count] [-i interval] [-n] [-s packetsize] \
753[-W waittime] <destination>"
754HELP_ping='
755 Send ICMP ECHO_REQUEST packets to a network host. If <destination> is "gw"
756 then the next hop gateway for the default route is used.
757 Default is to use IPv4 [-4] rather than IPv6 [-6] addresses.
758'
759cmd_ping() (
760 local option="dict:string:variant:"
761 local dest
762 while [ $# -gt 0 ]; do
763 # Do just enough parsing to filter/map options; we
764 # depend on ping to handle final validation.
765 case "$1" in
766 -4) option="${option}v6,boolean:false," ;;
767 -6) option="${option}v6,boolean:true," ;;
768 -i) shift; option="${option}interval,int32:$1," ;;
769 -c) shift; option="${option}count,int32:$1," ;;
770 -W) shift; option="${option}waittime,int32:$1," ;;
771 -s) shift; option="${option}packetsize,int32:$1," ;;
772 -n) option="${option}numeric,boolean:true," ;;
773 -b) option="${option}broadcast,boolean:true," ;;
774 -*)
775 help "unknown option: $1"
776 return 1
777 ;;
778 *)
779 if [ "${dest+set}" = "set" ]; then
780 help "too many destinations specified"
781 return 1
782 fi
783 dest="$1"
784 ;;
785 esac
786 shift
787 done
788 if [ "${dest+set}" != "set" ]; then
789 help "missing parameter: destination"
790 return 1
791 fi
792 if [ "${dest}" = "gw" ]; then
793 # Convenient shorthand for the next-hop gateway attached
794 # to the default route; this means if you have a host named
795 # "gw" then you'll need to specify a FQDN or IP address.
796 dest=$(ip route show | awk '$1 == "default" { print $3 }')
797 if [ -z "${dest}" ]; then
798 echo "Cannot determine primary gateway; routing table is:"
799 cmd_route
800 return 1
801 fi
802 fi
803 # Remove trailing comma in the options list if it exists.
804 debugd_poll Ping "fd:1" "string:${dest}" "${option%,}"
805)
806USAGE_chaps_debug='[start|stop|<log_level>]'
807HELP_chaps_debug='
808 Sets the chapsd logging level. No arguments will start verbose logging.
809'
810cmd_chaps_debug() (
811 local level=${1:--2}
812 if [ "$1" = "stop" ]; then
813 level=0
814 fi
815 if [ "$1" = "start" ]; then
816 level=-2
817 fi
818 /usr/bin/chaps_client --set_log_level=${level} 2> /dev/null
819 if [ $? -eq 0 ]; then
820 echo "Logging level set to ${level}."
821 else
822 echo "Failed to set logging level."
823 fi
824)
825USAGE_route='[-4] [-6]'
826HELP_route='
827 Display the routing tables.
828 Default is to show IPv4 [-4] rather than IPv6 [-6] routes.
829'
830cmd_route() (
831 local option="dict:string:variant:"
832 while [ $# -gt 0 ]; do
833 case $1 in
834 -4) option="${option}v6,boolean:false," ;;
835 -6) option="${option}v6,boolean:true," ;;
836 *)
837 help "unknown option: $1"
838 return 1
839 esac
840 shift
841 done
842 debugd GetRoutes "${option%,}"
843)
844USAGE_tracepath='[-4] [-6] [-n] <destination>[/port]'
845HELP_tracepath='
846 Trace the path/route to a network host.
847 Default is to trace IPv4 [-4] rather than IPv6 [-6] targets.
848'
849cmd_tracepath() (
850 local option="dict:string:variant:"
851 local dest
852 while [ $# -gt 0 ]; do
853 # Do just enough parsing to filter/map options; we
854 # depend on tracepath to handle final validation.
855 case "$1" in
856 -4) option="${option}v6,boolean:false," ;;
857 -6) option="${option}v6,boolean:true," ;;
858 -n) option="${option}numeric,boolean:true," ;;
859 -*)
860 help "unknown option: $1"
861 return 1
862 ;;
863 *)
864 if [ "${dest+set}" = "set" ]; then
865 help "too many destinations specified"
866 return 1
867 fi
868 dest="$1"
869 ;;
870 esac
871 shift
872 done
873 if [ "${dest+set}" != "set" ]; then
874 help "missing parameter: destination"
875 return 1
876 fi
877 # Remove trailing comma in the options list if it exists.
878 debugd_poll TracePath "fd:1" "string:${dest}" "${option%,}"
879)
880USAGE_top=''
881HELP_top='
882 Run top.
883'
884cmd_top() (
885 # -s is "secure" mode, which disables kill, renice, and change display/sleep
886 # interval. Set HOME to /mnt/empty to make sure we don't parse any files in
887 # the stateful partition. https://crbug.com/677934
888 HOME="/mnt/empty" top -s
889)
890USAGE_modem='<command> [args...]'
891HELP_modem='
892 Interact with the 3G modem. Run "modem help" for detailed help.
893'
894cmd_modem() (
895 debugd_shill modem "$@"
896)
897USAGE_modem_set_carrier='carrier-name'
898HELP_modem_set_carrier='
899 Configures the modem for the specified carrier.
900'
901cmd_modem_set_carrier() (
902 debugd_shill modem set-carrier "$@"
903)
904USAGE_set_apn='[-c] [-n <network-id>] [-u <username>] [-p <password>] <apn>'
905HELP_set_apn='
906 Set the APN to use when connecting to the network specified by <network-id>.
907 If <network-id> is not specified, use the network-id of the currently
908 registered network.
909 The -c option clears the APN to be used, so that the default APN will be used
910 instead.
911'
912cmd_set_apn() (
913 debugd_shill set_apn "$@"
914)
915USAGE_set_cellular_ppp='[-c] [-u <username>] [-p <password>]'
916HELP_set_cellular_ppp='
917 Set the PPP username and/or password for an existing cellular connection.
918 If neither -u nor -p is provided, show the existing PPP username for
919 the cellular connection.
920 The -c option clears any existing PPP username and PPP password for an
921 existing cellular connection.
922'
923cmd_set_cellular_ppp() (
924 /usr/bin/set_cellular_ppp "$@"
925)
926USAGE_connectivity=''
927HELP_connectivity='
928 Shows connectivity status. "connectivity help" for more details
929'
930cmd_connectivity() (
931 /usr/bin/connectivity "$@"
932)
933USAGE_autest='[-scheduled]'
934HELP_autest='
935 Trigger an auto-update. To fake a scheduled update check use -scheduled.
936'
937cmd_autest() (
938 local omaha_url="autest"
939 if [ "$1" = "-scheduled" ]; then
940 # pretend that this is a scheduled check as opposed to an user-initiated
941 # check for testing features that get enabled only on scheduled checks.
942 omaha_url="autest-scheduled"
943 fi
944 echo "Calling update_engine_client with omaha_url = $omaha_url"
945 /usr/bin/update_engine_client --omaha_url=$omaha_url
946)
947USAGE_p2p_update='[enable|disable] [--num-connections] [--show-peers]'
948HELP_p2p_update='
949 Enables or disables the peer-to-peer (P2P) sharing of updates over the local
950 network. This will both attempt to get updates from other peers in the
951 network and share the downloaded updates with them. Run this command without
952 arguments to see the current state. Additional switches will display number
953 of connections and P2P peers.
954'
955cmd_p2p_update() (
956 if [ "$1" = "" ]; then
957 /usr/bin/update_engine_client -show_p2p_update
958 fi
959 while [ $# -gt 0 ]; do
960 case "$1" in
961 "enable")
962 /usr/bin/update_engine_client -p2p_update=yes -show_p2p_update
963 ;;
964 "disable")
965 /usr/bin/update_engine_client -p2p_update=no -show_p2p_update
966 ;;
967 "--num-connections")
968 if p2p_check_enabled; then
969 echo "Number of active p2p connections:"
970 /usr/sbin/p2p-client --num-connections
971 fi
972 ;;
973 "--show-peers")
974 if p2p_check_enabled; then
975 echo "Current p2p peers:"
976 /usr/sbin/p2p-client --list-all
977 fi
978 ;;
979 *)
980 help "unknown option: $1"
981 return 1
982 ;;
983 esac
984 shift
985 done
986)
987p2p_check_enabled() {
988 if ! /usr/bin/update_engine_client -show_p2p_update 2>&1 \
989 | grep -q "ENABLED"; then
990 echo "Run \"p2p_update enable\" to enable peer-to-peer before" \
991 "using this command."
992 return 1
993 fi
994}
995USAGE_rollback=''
996HELP_rollback='
997 Attempt to rollback to the previous update cached on your system. Only
998 available on non-stable channels and non-enterprise enrolled devices. Please
999 note that this will powerwash your device.
1000'
1001cmd_rollback() (
1002 if /usr/bin/update_engine_client --rollback; then
1003 echo "Rollback attempt succeeded -- after a couple minutes you will" \
1004 "get an update available and you should reboot to complete rollback."
1005 else
1006 echo "Rollback attempt failed. Check chrome://system for more information."
1007 fi
1008)
1009USAGE_update_over_cellular='[enable|disable]'
1010HELP_update_over_cellular='
1011 Enables or disables the auto updates over cellular networks. Run without
1012 arguments to see the current state.
1013'
1014cmd_update_over_cellular() (
1015 case "$1" in
1016 "enable")
1017 param="-update_over_cellular=yes"
1018 echo "When available, auto-updates download in the background any time " \
1019 "the computer is powered on. Note: this may incur additional " \
1020 "cellular charges, including roaming and/or data charges, as per " \
1021 "your carrier arrangement."
1022 ;;
1023 "disable")
1024 param="-update_over_cellular=no"
1025 ;;
1026 "")
1027 param=""
1028 ;;
1029 *)
1030 help "unknown option: $1"
1031 return 1
1032 ;;
1033 esac
1034 /usr/bin/update_engine_client $param -show_update_over_cellular
1035)
1036USAGE_upload_crashes=''
1037HELP_upload_crashes='
1038 Uploads available crash reports to the crash server.
1039'
1040cmd_upload_crashes() (
1041 debugd UploadCrashes
1042 echo "Check chrome://crashes for status updates"
1043)
1044USAGE_upload_devcoredumps='[enable|disable]'
1045HELP_upload_devcoredumps='
1046 Enable or disable the upload of devcoredump reports.
1047'
1048cmd_upload_devcoredumps() (
1049 case "$1" in
1050 "enable")
1051 debugd EnableDevCoredumpUpload
1052 ;;
1053 "disable")
1054 debugd DisableDevCoredumpUpload
1055 ;;
1056 *)
1057 help "unknown option: $1"
1058 return 1
1059 ;;
1060 esac
1061)
1062USAGE_rlz='< status | enable | disable >'
1063HELP_rlz="
1064 Enable or disable RLZ. See this site for details:
1065 http://dev.chromium.org/developers/design-documents/extensions/\
1066proposed-changes/apis-under-development/rlz-api
1067"
1068cmd_rlz() (
1069 local flag_file="$HOME/.rlz_disabled"
1070 local enabled=1
1071 local changed=0
1072 if [ -r "${flag_file}" ]; then
1073 enabled=0
1074 fi
1075 case "$1" in
1076 "status")
1077 if [ $enabled -eq 1 ]; then
1078 echo "Currently enabled"
1079 else
1080 echo "Currently disabled"
1081 fi
1082 return
1083 ;;
1084 "enable")
1085 if [ $enabled -eq 0 ]; then
1086 changed=1
1087 fi
1088 rm -f "${flag_file}"
1089 ;;
1090 "disable")
1091 if [ $enabled -eq 1 ]; then
1092 changed=1
1093 fi
1094 touch "${flag_file}"
1095 ;;
1096 *)
1097 help "unknown option: $1"
1098 return 1
1099 ;;
1100 esac
1101 if [ $changed -eq 1 ]; then
1102 echo "You must reboot for this to take effect."
1103 else
1104 echo "No change."
1105 fi
1106)
1107USAGE_syslog='<message>'
1108HELP_syslog='
1109 Logs a message to syslog (the system log daemon).
1110'
1111cmd_syslog() (
1112 logger -t crosh -- "$*"
1113)
1114mk_fifo() {
1115 local dir fifo
1116 # We want C-c to terminate the running test so that the UI stays the same.
1117 # Therefore, create a fifo to direct the output of the test to, and have a
1118 # subshell read from the fifo and emit to stdout. When the subshell ends (at a
1119 # C-c), we stop the test and clean up the fifo.
1120 # no way to mktemp a fifo, so make a dir to hold it instead
1121 dir=$(mktemp -d "/tmp/crosh-test-XXXXXXXXXX")
1122 if [ $? -ne 0 ]; then
1123 echo "Can't create temporary directory"
1124 return 1
1125 fi
1126 fifo="${dir}/fifo"
1127 if ! mkfifo "${fifo}"; then
1128 echo "Can't create fifo at ${fifo}"
1129 return 1
1130 fi
1131 echo "${fifo}"
1132}
1133USAGE_storage_test_1=''
1134HELP_storage_test_1='
1135 Performs a short offline SMART test.
1136'
1137cmd_storage_test_1() (
1138 option="$1"
1139 debugd Smartctl "string:abort_test" >/dev/null
1140 test=$(debugd Smartctl "string:short_test")
1141 if [ "$option" != "-v" ]; then
1142 echo "$test" | sed -n '1p;2p'
1143 echo ""
1144 echo "$test" | grep "Please wait"
1145 else
1146 echo "$test"
1147 fi
1148 echo ""
1149 while debugd Smartctl "string:capabilities" |
1150 grep -q "of test remaining"; do
1151 true
1152 done
1153 result=$(debugd Smartctl "string:selftest")
1154 if [ "$option" != "-v" ]; then
1155 echo "$result" | grep -e "Num" -e "# 1"
1156 else
1157 echo "$result"
1158 fi
1159 debugd Smartctl "string:abort_test" >/dev/null
1160)
1161USAGE_storage_test_2=''
1162HELP_storage_test_2='
1163 Performs an extensive readability test.
1164'
1165cmd_storage_test_2() (
1166 debugd_poll Badblocks "fd:1"
1167)
1168USAGE_memory_test=''
1169HELP_memory_test='
1170 Performs extensive memory testing on the available free memory.
1171'
1172cmd_memory_test() (
1173 # Getting total free memory in KB.
1174 mem=$(cat /proc/meminfo | grep MemFree | tr -s " " | cut -d" " -f 2)
1175 # Converting to MiB.
1176 mem=$(($mem / 1024))
1177 # Giving OS 200MB free memory before hogging the rest of it.
1178 mem=$(($mem - 200))
1179 debugd_poll Memtester "fd:1" "uint32:${mem}"
1180)
1181USAGE_battery_firmware='<info|check|update>'
1182HELP_battery_firmware='
1183 info : Query battery info.
1184 check : Check whether the AC adapter is connected.
1185 Also check whether the battery firmware is the latest.
1186 update : Trigger battery firmware update.
1187'
1188cmd_battery_firmware() (
1189 option="$1"
1190 case "${option}" in
1191 info|check)
1192 debugd --reply-timeout=$(( 10 * 60 * 1000 )) BatteryFirmware "string:${option}"
1193 echo ""
1194 ;;
1195 update)
1196 # Increased the reply-timeout to 10 min for Battery Firmware update process.
1197 # Battery Firmware Update process time includes the following:
1198 # 1 setup delay time before entery battery firmware update mode.
1199 # 2 battery flash erase time.
1200 # 3 data transfer time from AP to EC, and then to Batttery.
1201 # 4 battery flash program/write time.
1202 # 5 re-try time on errors.
1203 # Note: take ~2 min on a success battery firmware update case.
1204 echo ""
1205 echo "================================================================================"
1206 echo " Battery firmware update is in progress."
1207 echo "================================================================================"
1208 echo "Please DO NOT remove the power adapter cable, otherwise the update will fail."
1209 echo ""
1210 echo "To recover from a failed battery firmware update,"
1211 echo " please plug in the power adapter cable, reboot and run this command again."
1212 echo ""
1213 echo "================================================================================"
1214 echo ""
1215 debugd --reply-timeout=$(( 10 * 60 * 1000 )) BatteryFirmware "string:${option}"
1216 echo ""
1217 ;;
1218 *)
1219 help "Unknown option: ${option}"
1220 ;;
1221 esac
1222)
1223USAGE_battery_test='[<test length>]'
1224HELP_battery_test='
1225 Tests battery discharge rate for given number of seconds. Without an argument,
1226 defaults to 300 seconds.
1227'
1228cmd_battery_test() (
1229 local test_length="$1"
1230 if [ -z "$test_length" ]; then
1231 echo "No test length specified. Defaulting to 300 seconds."
1232 test_length=300
1233 fi
1234 if ! check_digits "${test_length}"; then
1235 echo "Invalid test length."
1236 return 1
1237 fi
1238 if [ "$(get_power_status_field 'battery_present')" != '1' ]; then
1239 echo "No battery found."
1240 return 1
1241 fi
1242 if [ "$(get_power_status_field 'battery_discharging')" = '1' ]; then
1243 local bat_status='discharging'
1244 local bat_discharging=1
1245 else
1246 local bat_status='charging or full'
1247 local bat_discharging=0
1248 fi
1249 local bat_pct="$(get_power_status_field 'battery_percent')"
1250 local bat_full="$(get_power_status_field 'battery_charge_full')"
1251 local bat_full_design="$(get_power_status_field 'battery_charge_full_design')"
1252 local bat_health="$(echo "${bat_full}" "${bat_full_design}" | \
1253 awk '{ printf "%.2f", 100.0 * $1 / $2 }')"
1254 echo "Battery is ${bat_status} (${bat_pct}% left)"
1255 echo "Battery health: ${bat_health}%"
1256 if [ "${bat_discharging}" != '1' ]; then
1257 echo "Please make sure the power supply is unplugged and retry the test."
1258 return 1
1259 fi
1260 echo "Please wait..."
1261 sleep "${test_length}"
1262 local bat_after="$(get_power_status_field 'battery_percent')"
1263 local bat_diff="$(echo "${bat_pct}" "${bat_after}" | \
1264 awk '{ printf "%.2f", $1 - $2 }')"
1265 echo "Battery discharged ${bat_diff}% in ${test_length} second(s)."
1266)
1267USAGE_dump_emk=''
1268HELP_dump_emk='
1269 Show the EMK (Enterprise Machine Key).
1270'
1271cmd_dump_emk() (
1272 /usr/sbin/cryptohome --action=tpm_attestation_key_status \
1273 --name=attest-ent-machine
1274)
1275USAGE_enroll_status='[--mode] [--domain] [--realm] [--user]'
1276HELP_enroll_status='
1277 Displays device enrollment information.
1278'
1279cmd_enroll_status() (
1280 if [ "$1" = "" ]; then
1281 if check_enterprise_mode; then
1282 echo "Enrollment mode:"
1283 /usr/sbin/cryptohome --action=install_attributes_get \
1284 --name=enterprise.mode
1285 else
1286 echo "This device is not enterprise enrolled."
1287 fi
1288 fi
1289 while [ $# -gt 0 ]; do
1290 case "$1" in
1291 "--mode")
1292 if check_enterprise_mode; then
1293 echo "Enrollment mode:"
1294 /usr/sbin/cryptohome --action=install_attributes_get \
1295 --name=enterprise.mode
1296 else
1297 echo "This device is not enterprise enrolled."
1298 fi
1299 ;;
1300 "--domain")
1301 if check_enterprise_mode; then
1302 echo "Enrollment domain:"
1303 /usr/sbin/cryptohome --action=install_attributes_get \
1304 --name=enterprise.domain
1305 else
1306 echo "Enterprise enrollment domain not found."
1307 fi
1308 ;;
1309 "--realm")
1310 if check_enterprise_mode; then
1311 echo "Enrollment realm:"
1312 /usr/sbin/cryptohome --action=install_attributes_get \
1313 --name=enterprise.realm
1314 else
1315 echo "Enterprise enrollment realm not found."
1316 fi
1317 ;;
1318 "--user")
1319 if check_enterprise_mode; then
1320 echo "Enrollment user:"
1321 /usr/sbin/cryptohome --action=install_attributes_get \
1322 --name=enterprise.user
1323 else
1324 echo "Enterprise enrollment user not found."
1325 fi
1326 ;;
1327 *)
1328 help "unknown option: $1"
1329 return 1
1330 ;;
1331 esac
1332 shift
1333 done
1334)
1335check_enterprise_mode() {
1336 if ! /usr/sbin/cryptohome --action=install_attributes_get \
1337 --name=enterprise.mode 2>&1 | grep -q "enterprise"; then
1338 return 1
1339 fi
1340}
1341USAGE_tpm_status=''
1342HELP_tpm_status='
1343 Prints TPM (Trusted Platform Module) status information.
1344'
1345cmd_tpm_status() (
1346 /usr/sbin/cryptohome --action=tpm_more_status
1347)
1348USAGE_cryptohome_status=''
1349HELP_cryptohome_status='
1350 Get human-readable status information from cryptohomed.
1351'
1352cmd_cryptohome_status() (
1353 if [ $# -eq 0 ]; then
1354 /usr/sbin/cryptohome --action=status
1355 else
1356 help "too many arguments"
1357 return 1
1358 fi
1359)
1360USAGE_dmesg='[-d|-k|-r|-t|-u|-w|-x]'
1361HELP_dmesg='
1362 Display kernel log buffer
1363'
1364cmd_dmesg() (
1365 # We whitelist only a few options.
1366 local opt
1367 for opt in "$@"; do
1368 if ! printf '%s' "${opt}" | grep -sqE '^-[dkrtuwx]+$'; then
1369 help "unknown option: $*"
1370 return 1
1371 fi
1372 done
1373 dmesg "$@"
1374)
1375USAGE_free='[options]'
1376HELP_free='
1377 Display free/used memory info
1378'
1379cmd_free() (
1380 # All options are harmless, so pass them through.
1381 free "$@"
1382)
1383USAGE_meminfo=''
1384HELP_meminfo='
1385 Display detailed memory statistics
1386'
1387cmd_meminfo() (
1388 if [ $# -eq 0 ]; then
1389 cat /proc/meminfo
1390 else
1391 help "unknown option: $*"
1392 return 1
1393 fi
1394)
1395USAGE_uptime=''
1396HELP_uptime='
1397 Display uptime/load info
1398'
1399cmd_uptime() (
1400 if [ $# -eq 0 ]; then
1401 uptime
1402 else
1403 help "unknown option: $*"
1404 return 1
1405 fi
1406)
1407USAGE_uname='[-a|-s|-n|-r|-v|-m|-p|-i|-o]'
1408HELP_uname='
1409 Display system info
1410'
1411cmd_uname() (
1412 # We whitelist only a few options.
1413 local opt
1414 for opt in "$@"; do
1415 if ! printf '%s' "${opt}" | grep -sqE '^-[asnrvmpio]+$'; then
1416 help "unknown option: $*"
1417 return 1
1418 fi
1419 done
1420 uname "$@"
1421)
1422USAGE_vmstat='[-a|-d|-f|-m|-n|-s|-w] [delay [count]]'
1423HELP_vmstat='
1424 Report virtual memory statistics
1425'
1426cmd_vmstat() (
1427 # We whitelist only a few options.
1428 local opt
1429 for opt in "$@"; do
1430 if ! printf '%s' "${opt}" | grep -sqE '^(-[adfmnsw]+|[0-9]+)$'; then
1431 help "unknown option: ${opt}"
1432 return 1
1433 fi
1434 done
1435 vmstat "$@"
1436)
1437EXEC_ccd_pass='/usr/sbin/gsctool'
1438HELP_ccd_pass="
1439 When prompted, set or clear CCD password (use the word 'clear' to clear
1440 the password).
1441"
1442USAGE_ccd_pass=''
1443cmd_ccd_pass() (
1444 "${EXEC_ccd_pass}" -t -P
1445)
1446EXEC_verify_ro='/usr/share/cros/cr50-verify-ro.sh'
1447HELP_verify_ro="
1448 Verify AP and EC RO firmware on a Chrome OS device connected over SuzyQ
1449 cable, if supported by the device.
1450"
1451USAGE_verify_ro=''
1452cmd_verify_ro() (
1453 local cr50_image="/opt/google/cr50/firmware/cr50.bin.prod"
1454 local ro_db="/opt/google/cr50/ro_db"
1455 if [ $# -ne 0 ]; then
1456 help "too many arguments"
1457 return 1
1458 fi
1459 if [ ! -f "${cr50_image}" -o ! -d "${ro_db}" ]; then
1460 echo "This device can not be used for RO verification"
1461 return 1
1462 fi
1463 debugd_poll UpdateAndVerifyFWOnUsb \
1464 "fd:1" "string:${cr50_image}" "string:${ro_db}"
1465)
1466USAGE_c='<container name> [args]'
1467HELP_c='
1468 Run a program inside a container
1469'
1470EXEC_c='/usr/bin/run_oci'
1471cmd_c() (
1472 cleanup() {
1473 trap : INT QUIT
1474 debugd ContainerStopped
1475 }
1476 trap cleanup INT QUIT
1477 local container="$1"
1478 if [ -z "${container}" ]; then
1479 echo "No container specified"
1480 return 1
1481 fi
1482 shift
1483 local mount_dir=$(/usr/bin/mount_extension_image --name="${container}")
1484 if [ -z "${mount_dir}" ]; then
1485 echo "Could not find container"
1486 return 1
1487 fi
1488 debugd ContainerStarted
1489 HOME=/home/user /usr/bin/run_oci \
1490 --cgroup_parent=chronos_containers \
1491 --bind_mount="/home/user/${CROS_USER_ID_HASH}/Downloads:/home/user" \
1492 --bind_mount=/run/djfs:/dev \
1493 run -c "${mount_dir}" "${container}" -- "$@"
1494 cleanup
1495)
1496USAGE_evtest='evtest'
1497EXEC_evtest='/usr/bin/evtest'
1498HELP_evtest='
1499 Run evtest in safe mode.
1500'
1501cmd_evtest() (
1502 if [ $# -ne 0 ]; then
1503 help "too many arguments"
1504 return 1
1505 else
1506 # --safe is "safe" mode, which will only print limited info.
1507 # We don't allow any other arguments now. Any attempt to enable more
1508 # features should go through security review first.
1509 "${EXEC_evtest}" --safe
1510 fi
1511)
1512USAGE_alt_os='[-s SIZE_GB] enable | disable'
1513HELP_alt_os='Manage Alt OS mode'
1514EXEC_alt_os='/usr/bin/campfire-setup'
1515cmd_alt_os() (
1516 local opt opt_size
1517 while getopts "s:" opt; do
1518 case "${opt}" in
1519 s)
1520 opt_size="${OPTARG}"
1521 if ! is_numeric "${opt_size}"; then
1522 help "'${opt_size}' is not a valid number"
1523 fi
1524 ;;
1525 *)
1526 help "unknown option: '${opt}'"
1527 return 1
1528 esac
1529 done
1530 shift $((OPTIND - 1))
1531 local action="$1"
1532 case "${action}" in
1533 enable)
1534 size="${opt_size:-40}"
1535 if ! debugd CampfireEnableAltOS "int32:${size}"; then
1536 echo "Failed to enable Alt OS."
1537 return 1
1538 fi
1539 ;;
1540 disable)
1541 if ! debugd CampfireDisableAltOS; then
1542 echo "Failed to disable Alt OS."
1543 return 1
1544 fi
1545 ;;
1546 *)
1547 help "unknown action: '${action}'"
1548 return 1
1549 esac
1550)
1551substr() {
1552 local str="$1"
1553 local start="$2"
1554 local end="$3"
1555 start=$(expr "$start" + 1)
1556 if [ ! -z "$end" ]; then
1557 end=$(expr "$end" - 1)
1558 fi
1559 printf '%s' "${str}" | cut -c${start}-${end}
1560}
1561# Return true if the arg is a shell function.
1562is_shell_function() {
1563 local func="$1"
1564 type "${func}" 2>/dev/null | head -1 | grep -q 'function'
1565}
1566# Return all registered crosh commands intended for users.
1567# This will not include "hidden" commands that lack help as those are not yet
1568# meant for wider consumption, or they've been deprecated.
1569registered_visible_commands() {
1570 # Remember: Use a form that works under POSIX shell. Since that does not
1571 # offer any way of doing introspection on registered functions, we have to
1572 # stick to probing the environment.
1573 set | grep -o '^HELP_[^=]*' | sed -e 's:^HELP_::' -e 's:=$::' | sort
1574}
1575# Return true if the first arg is a valid crosh command.
1576registered_crosh_command() {
1577 local command="$1"
1578 is_shell_function "cmd_${command}"
1579}
1580# Return true if the specified command has a custom help function.
1581registered_crosh_command_help() {
1582 local command="$1"
1583 is_shell_function "help_${command}"
1584}
1585# Return true if the first arg is a command available on
1586# the system. We assume that all commands that do not
1587# define EXEC_<command> variable are always available. For
1588# commands that do define EXEC_<command> variable, we
1589# ensure that it contains name of a valid shell command.
1590check_command_available() {
1591 local exec="$(expand_var "EXEC_$1")"
1592 [ -z "${exec}" ] || command -v "${exec}" >/dev/null
1593}
1594dispatch() {
1595 local line="$1"
1596 local command=""
1597 local params=""
1598 local p
1599 local space_pos="$(expr index "${line}" ' ')"
1600 if [ "${space_pos}" = "0" ]; then
1601 command=$line
1602 else
1603 command=$(substr "$line" "0" "$space_pos")
1604 command=${command% *}
1605 params=$(substr "$line" "$space_pos")
1606 fi
1607 if ! registered_crosh_command "${command}"; then
1608 help "unknown command: ${command}"
1609 elif ! check_command_available "${command}"; then
1610 help "command '${command}' is not available"
1611 else
1612 # Only add valid commands to the history.
1613 shell_history -s "$LINE_"
1614 # See if --help was requested; if so, handle it directly so each command
1615 # doesn't have to deal with it directly.
1616 for p in ${params}; do
1617 if [ "${p}" = "-h" -o "${p}" = "--help" ]; then
1618 cmd_help "${command}"
1619 return 0
1620 fi
1621 done
1622 # Set CURRENT_COMMAND for the `help` helper.
1623 CURRENT_COMMAND="${command}" "cmd_${command}" ${params}
1624 fi
1625}
1626repl() {
1627 echo "${INTRO_TEXT}"
1628 if [ "$IS_BASH" != "1" ]; then
1629 echo "Sorry, line editing and command history disabled due to" \
1630 "shell limitations."
1631 fi
1632 # This will be set by the 'exit' command to tell us to quit.
1633 local exit
1634 # Create a colorized prompt to make it easier to see commands start/finish.
1635 local prompt
1636 prompt="$(printf '%bcrosh>%b ' '\001\033[1;33m\002' '\001\033[0m\002')"
1637 while [ -z "${exit}" ]; do
1638 if shell_read "${prompt}" LINE_; then
1639 if [ ! -z "$LINE_" ]; then
1640 dispatch "$LINE_"
1641 fi
1642 else
1643 echo
1644 return 1
1645 fi
1646 done
1647}
1648usage() {
1649 if [ $# -ne 0 ]; then
1650 # Send stdout below to stderr instead.
1651 exec 1>&2
1652 fi
1653 cat <<EOF
1654Usage: crosh [options]
1655Options:
1656 --dash Force running through dash.
1657 --dev Force dev mode.
1658 --removable Force removable (USB) mode.
1659 --usb Same as above.
1660 --help, -h Show this help string.
1661EOF
1662 if [ $# -ne 0 ]; then
1663 echo "ERROR: $*"
1664 exit 1
1665 else
1666 exit 0
1667 fi
1668}
1669main() {
1670 # If we aren't installed, use local files for testing.
1671 if [ ! -d "${CROSH_MODPATH}" ]; then
1672 CROSH_MODPATH=$(dirname "$0")
1673 echo "Loading from local paths instead: ${CROSH_MODPATH}"
1674 fi
1675 while [ $# -gt 0 ]; do
1676 case $1 in
1677 --dash)
1678 # This was handled above in try_bash.
1679 ;;
1680 --dev)
1681 CROSH_DEVMODE="1"
1682 ;;
1683 --removable|--usb)
1684 CROSH_REMOVABLE="1"
1685 ;;
1686 --help|-h)
1687 usage
1688 ;;
1689 *)
1690 usage "Unknown option: $1"
1691 ;;
1692 esac
1693 shift
1694 done
1695 load_extra_crosh
1696 INPUTRC="${CROSH_MODPATH}/inputrc.crosh"
1697 shell_history_init
1698 repl
1699 # Save the history after the clean exit.
1700 shell_history -w "${HISTFILE}"
1701}
1702main "$@"