blob: ac859af75d64e0420f7a0d6c2407553880580953 [file] [log] [blame]
Jack Rosenthal9541b8c2019-07-26 10:45:55 -06001#!/bin/bash
Ken Mixter689b9ee2010-01-07 18:23:52 -08002# Copyright (c) 2009 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# Library for setting up remote access and running remote commands.
7
Sean O'Connora6db82e2010-01-27 12:11:08 -08008DEFAULT_PRIVATE_KEY="${GCLIENT_ROOT}/src/scripts/mod_for_test_scripts/\
9ssh_keys/testing_rsa"
Ken Mixter689b9ee2010-01-07 18:23:52 -080010
11DEFINE_string remote "" "remote hostname/IP of running Chromium OS instance"
12DEFINE_string private_key "$DEFAULT_PRIVATE_KEY" \
13 "Private key of root account on remote host"
Douglas Andersonedc2e002020-05-14 10:48:26 -070014DEFINE_integer ssh_port 0 \
Zelidrag Hornung61d97682010-06-15 11:55:21 -070015 "SSH port of the remote machine running Chromium OS instance"
Gilad Arnold2ff2f112012-08-28 10:13:05 -070016DEFINE_integer ssh_connect_timeout 30 \
17 "SSH connect timeout in seconds"
18DEFINE_integer ssh_connection_attempts 4 \
19 "SSH connection attempts"
Douglas Andersonaaab1a32016-11-11 13:48:55 -080020DEFINE_boolean ssh_allow_agent ${FLAGS_FALSE} "Don't block out SSH_AUTH_SOCK"
Ken Mixter689b9ee2010-01-07 18:23:52 -080021
Marc Herbert5d519fa2015-06-12 15:15:44 -070022# Returns true if $1 has at least two colons.
23has_two_colons_or_more() {
24 # IPv6 addresses have at least two colons while IPv4 addresses and
25 # hostnames have none.
26 [[ "$1" == *:*:* ]]
27}
28
29# Prints $1 enclosed with brackets if it looks like an IPv6 address
30# and unchanged otherwise.
31brackets_enclosed_if_ipv6() {
32 local rem="$1"
33 if has_two_colons_or_more "${rem}"; then
34 rem="[${rem}]"
35 fi
36 echo "${rem}"
37}
38
Gilad Arnold2ff2f112012-08-28 10:13:05 -070039ssh_connect_settings() {
Douglas Andersonedc2e002020-05-14 10:48:26 -070040 local for_tool="$1"
41
Gilad Arnold2ff2f112012-08-28 10:13:05 -070042 if [[ -n "$SSH_CONNECT_SETTINGS" ]]; then
43 # If connection settings were fixed in an environment variable, just return
44 # those values.
45 echo -n "$SSH_CONNECT_SETTINGS"
46 else
47 # Otherwise, return the default (or user overridden) settings.
48 local settings=(
49 "Protocol=2"
50 "ConnectTimeout=${FLAGS_ssh_connect_timeout}"
51 "ConnectionAttempts=${FLAGS_ssh_connection_attempts}"
52 "ServerAliveInterval=10"
53 "ServerAliveCountMax=3"
54 "StrictHostKeyChecking=no"
Dmitry Torokhovec132152016-05-13 11:22:59 -070055 "IdentitiesOnly=yes"
56 "IdentityFile=${TMP_PRIVATE_KEY}"
57 "UserKnownHostsFile=${TMP_KNOWN_HOSTS}"
Douglas Andersond8807652016-11-11 14:01:18 -080058 "ControlPath=${TMP_CONTROL_FILE}"
59 "ControlMaster=auto"
60 "ControlPersist=45"
Gilad Arnold2ff2f112012-08-28 10:13:05 -070061 )
62 printf -- '-o %s ' "${settings[@]}"
Douglas Andersonedc2e002020-05-14 10:48:26 -070063
64 if [[ "${FLAGS_ssh_port}" -ne 0 ]]; then
65 if [[ "${for_tool}" == "scp" ]]; then
66 printf -- ' -P %d ' "${FLAGS_ssh_port}"
67 else
68 printf -- ' -p %d ' "${FLAGS_ssh_port}"
69 fi
70 fi
Gilad Arnold2ff2f112012-08-28 10:13:05 -070071 fi
72}
David Jamesf5850902011-09-30 10:51:48 -070073
Chris Sosaef964302010-04-27 13:21:08 -070074# Copies $1 to $2 on remote host
Mike Frysinger6b1abb22012-05-11 13:44:06 -040075remote_cp_to() {
Benson Leung57bf27f2018-01-14 19:16:58 -080076 local scp_rem
77 scp_rem="$(brackets_enclosed_if_ipv6 "${FLAGS_remote}")"
Douglas Andersonedc2e002020-05-14 10:48:26 -070078 REMOTE_OUT=$(scp $(ssh_connect_settings scp) \
Benson Leung57bf27f2018-01-14 19:16:58 -080079 "$1" "root@${scp_rem}:$2")
Chris Sosaef964302010-04-27 13:21:08 -070080 return ${PIPESTATUS[0]}
81}
82
Doug Anderson4e678382012-12-07 12:38:54 -080083# Raw rsync access to the remote
84# Use like: remote_rsync_raw -a /path/from/ root@${FLAGS_remote}:/path/to/
85remote_rsync_raw() {
Jack Rosenthal9541b8c2019-07-26 10:45:55 -060086 local reason=0
Douglas Andersonedc2e002020-05-14 10:48:26 -070087 rsync -e "ssh $(ssh_connect_settings ssh)" "$@" || reason=$?
Jack Rosenthal9541b8c2019-07-26 10:45:55 -060088 case ${reason} in
89 11 )
90 # no space left on device, call handle_no_space if implemented
91 if command -v handle_no_space >/dev/null; then
92 handle_no_space
93 fi
94 ;;
95 * )
96 ;;
97 esac
98 return ${reason}
Doug Anderson4e678382012-12-07 12:38:54 -080099}
100
Ken Mixtercc4f1dd2010-08-31 12:07:11 -0700101# Copies a list of remote files specified in file $1 to local location
102# $2. Directory paths in $1 are collapsed into $2.
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400103remote_rsync_from() {
Marc Herbert5d519fa2015-06-12 15:15:44 -0700104 local rsync_rem
105 rsync_rem="$(brackets_enclosed_if_ipv6 "${FLAGS_remote}")"
106 remote_rsync_raw --no-R --files-from="$1" \
107 root@"${rsync_rem}:/" "$2"
Doug Anderson4e678382012-12-07 12:38:54 -0800108}
109
110# Send a directory from $1 to $2 on remote host
111#
112# Tries to use rsync -a but will fall back to tar if the remote doesn't
113# have rsync.
114#
115# Use like: remote_send_to /build/board/lib/modules/ /lib/modules/
116remote_send_to() {
Marc Herbert5d519fa2015-06-12 15:15:44 -0700117 local rsync_rem
Doug Anderson4e678382012-12-07 12:38:54 -0800118 if [ ! -d "$1" ]; then
119 die "$1 must be a directory"
120 fi
121
122 if remote_sh rsync --version >/dev/null 2>&1; then
Marc Herbert5d519fa2015-06-12 15:15:44 -0700123 rsync_rem="$(brackets_enclosed_if_ipv6 "${FLAGS_remote}")"
124 remote_rsync_raw -a "$1/" root@"${rsync_rem}:$2/"
Doug Anderson4e678382012-12-07 12:38:54 -0800125 else
126 tar -C "$1" -cz . | remote_sh tar -C "$2" -xz
127 fi
Ken Mixtercc4f1dd2010-08-31 12:07:11 -0700128}
129
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400130_remote_sh() {
Douglas Andersonedc2e002020-05-14 10:48:26 -0700131 REMOTE_OUT=$(ssh $(ssh_connect_settings ssh) \
David Jamesf5850902011-09-30 10:51:48 -0700132 root@$FLAGS_remote "$@")
Ken Mixter689b9ee2010-01-07 18:23:52 -0800133 return ${PIPESTATUS[0]}
134}
135
Chris Sosafaeee5f2011-09-26 16:08:14 -0700136# Wrapper for ssh that runs the commmand given by the args on the remote host
Chris Sosa539b3412012-02-27 14:46:10 -0800137# If an ssh error occurs, re-runs the ssh command.
Ian Coolidgec3d5d912017-03-07 14:21:28 -0800138# Output is stored in REMOTE_OUT.
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400139remote_sh() {
Chris Sosafaeee5f2011-09-26 16:08:14 -0700140 local ssh_status=0
Chris Sosa539b3412012-02-27 14:46:10 -0800141 _remote_sh "$@" || ssh_status=$?
Chris Sosafaeee5f2011-09-26 16:08:14 -0700142 # 255 indicates an ssh error.
143 if [ ${ssh_status} -eq 255 ]; then
Chris Sosa539b3412012-02-27 14:46:10 -0800144 _remote_sh "$@"
Chris Sosafaeee5f2011-09-26 16:08:14 -0700145 else
146 return ${ssh_status}
147 fi
148}
149
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400150remote_sh_raw() {
Douglas Andersonedc2e002020-05-14 10:48:26 -0700151 ssh $(ssh_connect_settings ssh) \
David Jamesf5850902011-09-30 10:51:48 -0700152 $EXTRA_REMOTE_SH_ARGS root@$FLAGS_remote "$@"
Andrew de los Reyese08639b2011-09-21 15:44:05 -0700153 return $?
154}
155
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400156remote_sh_allow_changed_host_key() {
Ken Mixter689b9ee2010-01-07 18:23:52 -0800157 rm -f $TMP_KNOWN_HOSTS
158 remote_sh "$@"
159}
160
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400161set_up_remote_access() {
Ken Mixter689b9ee2010-01-07 18:23:52 -0800162 cp $FLAGS_private_key $TMP_PRIVATE_KEY
163 chmod 0400 $TMP_PRIVATE_KEY
Ken Mixter689b9ee2010-01-07 18:23:52 -0800164
165 # Verify the client is reachable before continuing
Gaurav Shahaf7d5d12011-09-21 16:42:16 -0700166 local output
167 local status=0
Frank Henigmand6b6cf62012-11-02 13:47:16 -0400168 if output=$(remote_sh -n "true" 2>&1); then
Gaurav Shahaf7d5d12011-09-21 16:42:16 -0700169 :
170 else
171 status=$?
172 echo "Could not initiate first contact with remote host"
173 echo "$output"
174 fi
175 return $status
Ken Mixter689b9ee2010-01-07 18:23:52 -0800176}
177
Ken Mixtercc4f1dd2010-08-31 12:07:11 -0700178# Ask the target what board it is
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400179learn_board() {
Ken Mixtercc4f1dd2010-08-31 12:07:11 -0700180 [ -n "${FLAGS_board}" ] && return
Frank Henigmand6b6cf62012-11-02 13:47:16 -0400181 remote_sh -n grep CHROMEOS_RELEASE_BOARD /etc/lsb-release
Ken Mixtercc4f1dd2010-08-31 12:07:11 -0700182 FLAGS_board=$(echo "${REMOTE_OUT}" | cut -d '=' -f 2)
183 if [ -z "${FLAGS_board}" ]; then
184 error "Board required"
185 exit 1
186 fi
187 info "Target reports board is ${FLAGS_board}"
188}
189
Ian Coolidgec3d5d912017-03-07 14:21:28 -0800190# Discover partition numbers from the target.
191learn_partition_layout() {
192 source <(remote_sh_raw cat /usr/sbin/write_gpt.sh)
193 load_base_vars
194}
195
Chris Wolfed91df7a2012-02-29 16:55:48 -0500196# Checks whether a remote device has rebooted successfully.
197#
198# This uses a rapidly-retried SSH connection, which will wait for at most
199# about ten seconds. If the network returns an error (e.g. host unreachable)
200# the actual delay may be shorter.
201#
202# Return values:
203# 0: The device has rebooted successfully
204# 1: The device has not yet rebooted
205# 255: Unable to communicate with the device
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400206_check_if_rebooted() {
Chris Wolfed91df7a2012-02-29 16:55:48 -0500207 (
208 # In my tests SSH seems to be waiting rather longer than would be expected
209 # from these parameters. These values produce a ~10 second wait.
210 # (in a subshell to avoid clobbering the global settings)
211 SSH_CONNECT_SETTINGS="$(sed \
212 -e 's/\(ConnectTimeout\)=[0-9]*/\1=2/' \
213 -e 's/\(ConnectionAttempts\)=[0-9]*/\1=2/' \
Douglas Andersonedc2e002020-05-14 10:48:26 -0700214 <<<"$(ssh_connect_settings ssh)")"
Chris Wolfed91df7a2012-02-29 16:55:48 -0500215 remote_sh_allow_changed_host_key -q -- '[ ! -e /tmp/awaiting_reboot ]'
216 )
Chris Sosa24da49e2011-02-01 17:06:12 -0800217}
Mandeep Singh Bainesa63cd2d2010-12-02 11:58:26 -0800218
Chris Wolfed91df7a2012-02-29 16:55:48 -0500219# Triggers a reboot on a remote device and waits for it to complete.
220#
221# This function will not return until the SSH server on the remote device
222# is available after the reboot.
223#
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400224remote_reboot() {
Chris Wolfed91df7a2012-02-29 16:55:48 -0500225 info "Rebooting ${FLAGS_remote}..."
Andrey Ulanovc68f4882017-01-30 17:41:42 -0800226 # 'reboot' is ran in background to make sure the command completes before
227 # sshd is terminated.
228 remote_sh_raw "touch /tmp/awaiting_reboot; reboot &"
Chris Wolfed91df7a2012-02-29 16:55:48 -0500229 local start_time=${SECONDS}
230
231 # Wait for five seconds before we start polling
232 sleep 5
233
234 # Add a hard timeout of 5 minutes before giving up.
235 local timeout=300
236 local timeout_expiry=$(( start_time + timeout ))
237 while [ ${SECONDS} -lt ${timeout_expiry} ]; do
238 # Used to throttle the loop -- see step_remaining_time at the bottom.
239 local step_start_time=${SECONDS}
240
241 local status=0
242 _check_if_rebooted || status=$?
243
244 local elapsed=$(( SECONDS - start_time ))
245 case ${status} in
246 0) printf ' %4ds: reboot complete\n' ${elapsed} >&2 ; return 0 ;;
247 1) printf ' %4ds: device has not yet shut down\n' ${elapsed} >&2 ;;
248 255) printf ' %4ds: can not connect to device\n' ${elapsed} >&2 ;;
249 *) die " internal error" ;;
250 esac
251
252 # To keep the loop from spinning too fast, delay until it has taken at
253 # least five seconds. When we are actively trying SSH connections this
254 # should never happen.
255 local step_remaining_time=$(( step_start_time + 5 - SECONDS ))
256 if [ ${step_remaining_time} -gt 0 ]; then
257 sleep ${step_remaining_time}
258 fi
Mandeep Singh Bainesa63cd2d2010-12-02 11:58:26 -0800259 done
Brian Norris3123fa12017-09-28 10:26:28 -0700260 die_notrace "Reboot has not completed after ${timeout} seconds; giving up."
Mandeep Singh Bainesa63cd2d2010-12-02 11:58:26 -0800261}
262
Mandeep Singh Bainesaef91ad2011-01-14 14:17:25 -0800263# Called by clients before exiting.
264# Part of the remote_access.sh interface but now empty.
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400265cleanup_remote_access() {
Mandeep Singh Bainesaef91ad2011-01-14 14:17:25 -0800266 true
Sean O'Connor9969ce92010-02-01 17:10:03 -0800267}
268
Mike Frysinger6b1abb22012-05-11 13:44:06 -0400269remote_access_init() {
Ken Mixter689b9ee2010-01-07 18:23:52 -0800270 TMP_PRIVATE_KEY=$TMP/private_key
271 TMP_KNOWN_HOSTS=$TMP/known_hosts
Brian Norrisb9bd9ab2020-07-21 11:34:47 -0700272 TMP_CONTROL_FILE="${TMP}/ssh_control-%C"
Douglas Andersond8807652016-11-11 14:01:18 -0800273
Ken Mixter689b9ee2010-01-07 18:23:52 -0800274 if [ -z "$FLAGS_remote" ]; then
275 echo "Please specify --remote=<IP-or-hostname> of the Chromium OS instance"
276 exit 1
277 fi
Douglas Andersonaaab1a32016-11-11 13:48:55 -0800278
279 # Having SSH_AUTH_SOCK set makes our ssh connections super slow so unset
280 # if it's not really needed.
281 if [[ ${FLAGS_ssh_allow_agent} -eq ${FLAGS_FALSE} ]]; then
282 unset SSH_AUTH_SOCK
283 fi
284
Ken Mixter689b9ee2010-01-07 18:23:52 -0800285 set_up_remote_access
286}