Blame lib/remote.exp

Packit 62fe53
# Copyright (C) 1992-2016 Free Software Foundation, Inc.
Packit 62fe53
#
Packit 62fe53
# This file is part of DejaGnu.
Packit 62fe53
#
Packit 62fe53
# DejaGnu is free software; you can redistribute it and/or modify it
Packit 62fe53
# under the terms of the GNU General Public License as published by
Packit 62fe53
# the Free Software Foundation; either version 3 of the License, or
Packit 62fe53
# (at your option) any later version.
Packit 62fe53
#
Packit 62fe53
# DejaGnu is distributed in the hope that it will be useful, but
Packit 62fe53
# WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 62fe53
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 62fe53
# General Public License for more details.
Packit 62fe53
#
Packit 62fe53
# You should have received a copy of the GNU General Public License
Packit 62fe53
# along with DejaGnu; if not, write to the Free Software Foundation,
Packit 62fe53
# Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
Packit 62fe53
Packit 62fe53
# This file was written by Rob Savoye <rob@welcomehome.org>.
Packit 62fe53
Packit 62fe53
# Load various protocol support modules.
Packit 62fe53
Packit 62fe53
load_lib "telnet.exp"
Packit 62fe53
load_lib "rlogin.exp"
Packit 62fe53
load_lib "kermit.exp"
Packit 62fe53
load_lib "tip.exp"
Packit 62fe53
load_lib "rsh.exp"
Packit 62fe53
load_lib "ssh.exp"
Packit 62fe53
load_lib "ftp.exp"
Packit 62fe53
Packit 62fe53
# Open a connection to a remote host or target. This requires the target_info
Packit 62fe53
# array be filled in with the proper info to work.
Packit 62fe53
#
Packit 62fe53
# type is either "build", "host", "target", or the name of a board loaded
Packit 62fe53
# into the board_info array. The default is target if no name is supplied.
Packit 62fe53
# It returns the spawn id of the process that is the connection.
Packit 62fe53
#
Packit 62fe53
proc remote_open { args } {
Packit 62fe53
    global reboot
Packit 62fe53
Packit 62fe53
    if { [llength $args] == 0 } {
Packit 62fe53
	set type "target"
Packit 62fe53
    } else {
Packit 62fe53
	set type $args
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    # Shudder...
Packit 62fe53
    if { $reboot && $type == "target" } {
Packit 62fe53
	reboot_target
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    return [call_remote "" open $type]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_raw_open { args } {
Packit 62fe53
    return [eval call_remote raw open $args]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Close a spawn ID, and wait for the process to die.  If PID is not
Packit 62fe53
# -1, then the process is killed if it doesn't exit gracefully.
Packit 62fe53
#
Packit 62fe53
proc close_wait_program { program_id pid {wres_varname ""} } {
Packit 62fe53
    if {$wres_varname != "" } {
Packit 62fe53
	upvar 1 $wres_varname wres
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    set exec_pid -1
Packit 62fe53
Packit 62fe53
    if { $pid > 0 } {
Packit 62fe53
	# Tcl has no kill primitive, so we have to execute an external
Packit 62fe53
	# command in order to kill the process.
Packit 62fe53
	verbose "doing kill, pid is $pid"
Packit 62fe53
	# Prepend "-" to generate the "process group ID" needed by
Packit 62fe53
	# kill.
Packit 62fe53
	set pgid "-$pid"
Packit 62fe53
	# Send SIGINT to give the program a better chance to interrupt
Packit 62fe53
	# whatever it might be doing and react to stdin closing.
Packit 62fe53
	# eg, in case of GDB, this should get it back to the prompt.
Packit 62fe53
	exec sh -c "exec > /dev/null 2>&1 && (kill -2 $pgid || kill -2 $pid)"
Packit 62fe53
Packit 62fe53
	# If the program doesn't exit gracefully when stdin closes,
Packit 62fe53
	# we'll need to kill it.  But only do this after 'wait'ing a
Packit 62fe53
	# bit, to avoid killing the wrong process in case of a
Packit 62fe53
	# PID-reuse race.  The extra sleep at the end is there to give
Packit 62fe53
	# time to kill $exec_pid without having _that_ be subject to a
Packit 62fe53
	# PID reuse race.
Packit 62fe53
	set secs 5
Packit 62fe53
	set sh_cmd "exec > /dev/null 2>&1"
Packit 62fe53
	append sh_cmd " && sleep $secs && (kill -15 $pgid || kill -15 $pid)"
Packit 62fe53
	append sh_cmd " && sleep $secs && (kill -9 $pgid || kill -9 $pid)"
Packit 62fe53
	append sh_cmd " && sleep $secs"
Packit 62fe53
	set exec_pid [exec sh -c "$sh_cmd" &]
Packit 62fe53
    }
Packit 62fe53
    verbose "pid is $pid"
Packit 62fe53
Packit 62fe53
    # This closes the program's stdin.  This should cause well behaved
Packit 62fe53
    # interactive programs to exit.  This will hang if the kill
Packit 62fe53
    # doesn't work.  Nothin' to do, and it's not OK.
Packit 62fe53
    catch "close -i $program_id"
Packit 62fe53
Packit 62fe53
    # Reap it.
Packit 62fe53
    set res [catch "wait -i $program_id" wres]
Packit 62fe53
    if {$exec_pid != -1} {
Packit 62fe53
	# We reaped the process, so cancel the pending force-kills, as
Packit 62fe53
	# otherwise if the PID is reused for some other unrelated
Packit 62fe53
	# process, we'd kill the wrong process.
Packit 62fe53
	exec sh -c "exec > /dev/null 2>&1 && kill -9 $exec_pid"
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    return $res
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Run the specified COMMANDLINE on the local machine, redirecting
Packit 62fe53
# input from file INP (if non-empty), redirecting output to file OUTP
Packit 62fe53
# (if non-empty), and waiting TIMEOUT seconds for the command to
Packit 62fe53
# complete before killing it. A list of two elements is returned: the
Packit 62fe53
# first member is the exit status of the command, the second is any
Packit 62fe53
# output produced from the command (if output is redirected, this may
Packit 62fe53
# or may not be empty). If output is redirected, both stdout and
Packit 62fe53
# stderr will appear in the specified file.
Packit 62fe53
#
Packit 62fe53
# Caveats: A pipeline is used if input or output is redirected. There
Packit 62fe53
# will be problems with killing the program if a pipeline is used. Either
Packit 62fe53
# the "tee" command or the "cat" command is used in the pipeline if input
Packit 62fe53
# or output is redirected. If the program needs to be killed, /bin/sh and
Packit 62fe53
# the kill command will be invoked.
Packit 62fe53
#
Packit 62fe53
proc local_exec { commandline inp outp timeout } {
Packit 62fe53
    # Tcl's exec is a pile of crap. It does two very inappropriate things.
Packit 62fe53
    # Firstly, it has no business returning an error if the program being
Packit 62fe53
    # executed happens to write to stderr. Secondly, it appends its own
Packit 62fe53
    # error messages to the output of the command if the process exits with
Packit 62fe53
    # non-zero status.
Packit 62fe53
    #
Packit 62fe53
    # So, ok, we do this funny stuff with using spawn sometimes and
Packit 62fe53
    # open others because of spawn's inability to invoke commands with
Packit 62fe53
    # redirected I/O. We also hope that nobody passes in a command that's
Packit 62fe53
    # a pipeline, because spawn can't handle it.
Packit 62fe53
    #
Packit 62fe53
    # We want to use spawn in most cases, because Tcl's pipe mechanism
Packit 62fe53
    # doesn't assign process groups correctly and we can't reliably kill
Packit 62fe53
    # programs that bear children. We can't use Tcl's exec because it has
Packit 62fe53
    # no way to timeout programs that hang.
Packit 62fe53
    #
Packit 62fe53
    # The expect command will close the connection when it sees
Packit 62fe53
    # EOF. Closing the connection may send SIGHUP to the child and
Packit 62fe53
    # cause it to exit before it can exit normally.  The child should
Packit 62fe53
    # ignore SIGHUP.
Packit 62fe53
    global errorInfo
Packit 62fe53
    if { "$inp" == "" && "$outp" == "" } {
Packit 62fe53
	set id -1
Packit 62fe53
	set result [catch "eval spawn -ignore SIGHUP \{${commandline}\}" pid]
Packit 62fe53
	if { $result == 0 } {
Packit 62fe53
	    set result2 0
Packit 62fe53
	} else {
Packit 62fe53
	    set pid 0
Packit 62fe53
	    set result2 5
Packit 62fe53
	}
Packit 62fe53
    } else {
Packit 62fe53
	# Use a command pipeline with open.
Packit 62fe53
	if { $inp != "" } {
Packit 62fe53
	    set inp "< $inp"
Packit 62fe53
	    set mode "r"
Packit 62fe53
	} else {
Packit 62fe53
	    set mode "w"
Packit 62fe53
	}
Packit 62fe53
Packit 62fe53
	set use_tee 0
Packit 62fe53
	# We add |& cat so that Tcl exec doesn't freak out if the
Packit 62fe53
	# program writes to stderr.
Packit 62fe53
	if { $outp == "" } {
Packit 62fe53
	    set outp "|& cat"
Packit 62fe53
	} else {
Packit 62fe53
	    set outpf "$outp"
Packit 62fe53
	    set outp "> $outp"
Packit 62fe53
	    if { $inp != "" } {
Packit 62fe53
		set use_tee 1
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
	# Why do we use tee? Because open can't redirect both input and output.
Packit 62fe53
	if { $use_tee } {
Packit 62fe53
	    set result [catch {open "| ${commandline} $inp |& tee $outpf" RDONLY} id]
Packit 62fe53
	} else {
Packit 62fe53
	    set result [catch {open "| ${commandline} $inp $outp" $mode} id]
Packit 62fe53
	}
Packit 62fe53
Packit 62fe53
	if { $result != 0 } {
Packit 62fe53
	    return [list -1 "open of $commandline $inp $outp failed: $errorInfo"]
Packit 62fe53
	}
Packit 62fe53
	set pid [pid $id]
Packit 62fe53
	set result [catch "spawn -ignore SIGHUP -leaveopen $id" result2]
Packit 62fe53
    }
Packit 62fe53
    # Prepend "-" to each pid, to generate the "process group IDs" needed by
Packit 62fe53
    # kill.
Packit 62fe53
    set pgid "-[join $pid { -}]"
Packit 62fe53
    verbose "pid is $pid $pgid"
Packit 62fe53
    if { $result != 0 || $result2 != 0 } {
Packit 62fe53
	# This shouldn't happen.
Packit 62fe53
	if {[info exists errorInfo]} {
Packit 62fe53
	    set foo $errorInfo
Packit 62fe53
	} else {
Packit 62fe53
	    set foo ""
Packit 62fe53
	}
Packit 62fe53
	verbose "spawn -open $id failed, $result $result2, $foo"
Packit 62fe53
	catch "close $id"
Packit 62fe53
	return [list -1 "spawn failed"]
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    set got_eof 0
Packit 62fe53
    set output ""
Packit 62fe53
Packit 62fe53
    # Wait for either $timeout seconds to elapse, or for the program to
Packit 62fe53
    # exit.
Packit 62fe53
    expect {
Packit 62fe53
	-i $spawn_id -timeout $timeout -re ".+" {
Packit 62fe53
	    append output $expect_out(buffer)
Packit 62fe53
	    exp_continue -continue_timer
Packit 62fe53
	}
Packit 62fe53
	timeout {
Packit 62fe53
	    warning "program timed out"
Packit 62fe53
	}
Packit 62fe53
	eof {
Packit 62fe53
	    set got_eof 1
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    # If we didn't get EOF, we have to kill the poor defenseless program.
Packit 62fe53
    if { $got_eof } {
Packit 62fe53
	set pid -1
Packit 62fe53
    }
Packit 62fe53
    set r2 [close_wait_program $spawn_id $pid wres]
Packit 62fe53
    if { $id > 0 } {
Packit 62fe53
	set r2 [catch "close $id" res]
Packit 62fe53
    } else {
Packit 62fe53
	verbose "waitres is $wres" 2
Packit 62fe53
	if { $r2 == 0 } {
Packit 62fe53
	    set r2 [lindex $wres 3]
Packit 62fe53
	    if { [llength $wres] > 4 } {
Packit 62fe53
		if { [lindex $wres 4] == "CHILDKILLED" } {
Packit 62fe53
		    set r2 1
Packit 62fe53
		}
Packit 62fe53
	    }
Packit 62fe53
	    if { $r2 != 0 } {
Packit 62fe53
		set res "$wres"
Packit 62fe53
	    } else {
Packit 62fe53
		set res ""
Packit 62fe53
	    }
Packit 62fe53
	} else {
Packit 62fe53
	    set res "wait failed"
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
    if { $r2 != 0 || $res != "" || ! $got_eof } {
Packit 62fe53
	verbose "close result is $res"
Packit 62fe53
	set status 1
Packit 62fe53
    } else {
Packit 62fe53
	set status 0
Packit 62fe53
    }
Packit 62fe53
    verbose "output is $output status $status"
Packit 62fe53
    if { $outp == "" || $outp == "|& cat" } {
Packit 62fe53
	return [list $status $output]
Packit 62fe53
    } else {
Packit 62fe53
	return [list $status ""]
Packit 62fe53
    }
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
#
Packit 62fe53
# Execute the supplied program on HOSTNAME. There are four optional arguments
Packit 62fe53
# the first is a set of arguments to pass to PROGRAM, the second is an
Packit 62fe53
# input file to feed to stdin of PROGRAM, the third is the name of an
Packit 62fe53
# output file where the output from PROGRAM should be written, and
Packit 62fe53
# the fourth is a timeout value (we give up after the specified # of seconds
Packit 62fe53
# has elapsed).
Packit 62fe53
#
Packit 62fe53
# A two-element list is returned. The first value is the exit status of the
Packit 62fe53
# program (-1 if the exec failed). The second is any output produced by
Packit 62fe53
# the program (which may or may not be empty if output from the program was
Packit 62fe53
# redirected).
Packit 62fe53
#
Packit 62fe53
proc remote_exec { hostname program args } {
Packit 62fe53
    if { [llength $args] > 0 } {
Packit 62fe53
	set pargs [lindex $args 0]
Packit 62fe53
    } else {
Packit 62fe53
	set pargs ""
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if { [llength $args] > 1 } {
Packit 62fe53
	set inp "[lindex $args 1]"
Packit 62fe53
    } else {
Packit 62fe53
	set inp ""
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if { [llength $args] > 2 } {
Packit 62fe53
	set outp "[lindex $args 2]"
Packit 62fe53
    } else {
Packit 62fe53
	set outp ""
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    # call_remote below gets its timeout from global variable, so set
Packit 62fe53
    # it here.
Packit 62fe53
    global timeout
Packit 62fe53
    set old_timeout $timeout
Packit 62fe53
    # 300 is probably a lame default.
Packit 62fe53
    if { [llength $args] > 3 } {
Packit 62fe53
	set timeout "[lindex $args 3]"
Packit 62fe53
    } else {
Packit 62fe53
	set timeout 300
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    verbose -log "Executing on $hostname: $program $pargs $inp $outp (timeout = $timeout)" 2
Packit 62fe53
Packit 62fe53
    # Run it locally if appropriate.
Packit 62fe53
    if { ![is_remote $hostname] } {
Packit 62fe53
	set result [local_exec "$program $pargs" $inp $outp $timeout]
Packit 62fe53
    } else {
Packit 62fe53
        if { [board_info $hostname exists remotedir] } {
Packit 62fe53
            set remotedir [board_info $hostname remotedir]
Packit 62fe53
	    # This is a bit too clever. Join cd $remotedir and
Packit 62fe53
	    # $program on the command line with ';' and not '&&'. When
Packit 62fe53
	    # called, $program may be mkdir to initially create the
Packit 62fe53
	    # remote directory, in which case cd would fail.
Packit 62fe53
            set program "test -d $remotedir && cd $remotedir; $program"
Packit 62fe53
        }
Packit 62fe53
	set result [call_remote "" exec $hostname $program $pargs $inp $outp]
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    # Restore timeout.
Packit 62fe53
    set timeout $old_timeout
Packit 62fe53
    return $result
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc standard_exec { hostname args } {
Packit 62fe53
    return [eval rsh_exec \"$hostname\" $args]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Close the remote connection.
Packit 62fe53
#	arg - This is the name of the machine whose connection we're closing,
Packit 62fe53
#	      or target, host or build.
Packit 62fe53
#
Packit 62fe53
proc remote_close { host } {
Packit 62fe53
    while { 1 } {
Packit 62fe53
	set result [call_remote "" close "$host"]
Packit 62fe53
	if { [remote_pop_conn $host] != "pass" } {
Packit 62fe53
	    break
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
    return $result
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_raw_close { host } {
Packit 62fe53
    return [call_remote raw close "$host"]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc standard_close { host } {
Packit 62fe53
    global board_info
Packit 62fe53
Packit 62fe53
    if {[board_info ${host} exists fileid]} {
Packit 62fe53
	set shell_id [board_info ${host} fileid]
Packit 62fe53
	set pid -1
Packit 62fe53
Packit 62fe53
	verbose "Closing the remote shell $shell_id" 2
Packit 62fe53
	if {[board_info ${host} exists fileid_origid]} {
Packit 62fe53
	    set oid [board_info ${host} fileid_origid]
Packit 62fe53
	    set pid [pid $oid]
Packit 62fe53
	    unset board_info(${host},fileid_origid)
Packit 62fe53
	} else {
Packit 62fe53
	    set result [catch "exp_pid -i $shell_id" pid]
Packit 62fe53
	    if { $result != 0 || $pid <= 0 } {
Packit 62fe53
		set result [catch "pid $shell_id" pid]
Packit 62fe53
		if { $result != 0 } {
Packit 62fe53
		    set pid -1
Packit 62fe53
		}
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
Packit 62fe53
	close_wait_program $shell_id $pid
Packit 62fe53
Packit 62fe53
	if {[info exists oid]} {
Packit 62fe53
	    catch "close $oid"
Packit 62fe53
	}
Packit 62fe53
Packit 62fe53
	unset board_info(${host},fileid)
Packit 62fe53
	verbose "Shell closed."
Packit 62fe53
    }
Packit 62fe53
    return 0
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Set the connection into "binary" mode, a.k.a. no processing of input
Packit 62fe53
# characters.
Packit 62fe53
#
Packit 62fe53
proc remote_binary { host } {
Packit 62fe53
    return [call_remote "" binary "$host"]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_raw_binary { host } {
Packit 62fe53
    return [call_remote raw binary "$host"]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53

Packit 62fe53
# Return value of this function depends on actual implementation of reboot that
Packit 62fe53
# will be used, in practice it is expected that remote_reboot returns 1 on
Packit 62fe53
# success and 0 on failure.
Packit 62fe53
#
Packit 62fe53
proc remote_reboot { host } {
Packit 62fe53
    clone_output "\nRebooting ${host}\n"
Packit 62fe53
    # FIXME: don't close the host connection, or all the remote
Packit 62fe53
    # procedures will fail.
Packit 62fe53
    # remote_close $host
Packit 62fe53
    set status [call_remote "" reboot "$host"]
Packit 62fe53
    if {[board_info $host exists name]} {
Packit 62fe53
	set host [board_info $host name]
Packit 62fe53
    }
Packit 62fe53
    if { [info procs ${host}_init] != "" } {
Packit 62fe53
	${host}_init $host
Packit 62fe53
    }
Packit 62fe53
    return $status
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# It looks like that this proc is never called, instead ${board}_reboot defined
Packit 62fe53
# in base-config.exp will be used because it has higher priority and
Packit 62fe53
# base-config.exp is always imported by runtest.
Packit 62fe53
#
Packit 62fe53
proc standard_reboot { host } {
Packit 62fe53
    return 1
Packit 62fe53
}
Packit 62fe53
#
Packit 62fe53
# Download file FILE to DEST. If the optional DESTFILE is specified,
Packit 62fe53
# that file will be used on the destination board. It returns either
Packit 62fe53
# "" (indicating that the download failed), or the name of the file on
Packit 62fe53
# the destination machine.
Packit 62fe53
#
Packit 62fe53
Packit 62fe53
proc remote_download { dest file args } {
Packit 62fe53
    if { [llength $args] > 0 } {
Packit 62fe53
	set destfile [lindex $args 0]
Packit 62fe53
    } else {
Packit 62fe53
	set destfile [file tail $file]
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if { ![is_remote $dest] } {
Packit 62fe53
	if { $destfile == "" || $destfile == $file } {
Packit 62fe53
	    return $file
Packit 62fe53
	} else {
Packit 62fe53
	    verbose -log "Downloading on $dest to $destfile: $file" 2
Packit 62fe53
	    set result [catch "exec cp -p $file $destfile" output]
Packit 62fe53
	    if {[regexp "same file|are identical" $output]} {
Packit 62fe53
		set result 0
Packit 62fe53
		set output ""
Packit 62fe53
	    } else {
Packit 62fe53
		# try to make sure we can read it
Packit 62fe53
		# and write it (in case we copy onto it again)
Packit 62fe53
		catch {exec chmod u+rw $destfile}
Packit 62fe53
	    }
Packit 62fe53
	    if { $result != 0 || $output != "" } {
Packit 62fe53
		perror "remote_download to $dest of $file to $destfile: $output"
Packit 62fe53
		return ""
Packit 62fe53
	    } else {
Packit 62fe53
		return $destfile
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
    if { [board_info $dest exists remotedir] } {
Packit 62fe53
        set remotedir [board_info $dest remotedir]
Packit 62fe53
        set status [remote_exec $dest mkdir "-p $remotedir"]
Packit 62fe53
        if { [lindex $status 0] != 0 } {
Packit 62fe53
            perror "Couldn't create remote directory $remotedir on $dest"
Packit 62fe53
	    return ""
Packit 62fe53
        }
Packit 62fe53
        set destfile "$remotedir/$destfile"
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    return [call_remote "" download $dest $file $destfile]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# The default download procedure. Uses rcp to download to $dest.
Packit 62fe53
#
Packit 62fe53
proc standard_download {dest file destfile} {
Packit 62fe53
    set orig_destfile $destfile
Packit 62fe53
Packit 62fe53
    if {[board_info $dest exists nfsdir]} {
Packit 62fe53
	set destdir [board_info $dest nfsdir]
Packit 62fe53
	if {[board_info $dest exists nfsroot_server]} {
Packit 62fe53
	    set dest [board_info $dest nfsroot_server]
Packit 62fe53
	} else {
Packit 62fe53
	    set dest ""
Packit 62fe53
	}
Packit 62fe53
	set destfile "$destdir/$destfile"
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if { "$dest" != "" } {
Packit 62fe53
	set result [rsh_download $dest $file $destfile]
Packit 62fe53
	if { $result == $destfile } {
Packit 62fe53
	    return $orig_destfile
Packit 62fe53
	} else {
Packit 62fe53
	    return $result
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    set result [catch "exec cp -p $file $destfile" output]
Packit 62fe53
    if {[regexp "same file|are identical" $output]} {
Packit 62fe53
	set result 0
Packit 62fe53
	set output ""
Packit 62fe53
    } else {
Packit 62fe53
	# try to make sure we can read it
Packit 62fe53
	# and write it (in case we copy onto it again)
Packit 62fe53
	catch {exec chmod u+rw $destfile}
Packit 62fe53
    }
Packit 62fe53
    if { $result != 0 || $output != "" } {
Packit 62fe53
	perror "remote_download to $dest of $file to $destfile: $output"
Packit 62fe53
	return ""
Packit 62fe53
    } else {
Packit 62fe53
	return $orig_destfile
Packit 62fe53
    }
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_upload {dest srcfile args} {
Packit 62fe53
    if { [llength $args] > 0 } {
Packit 62fe53
	set destfile [lindex $args 0]
Packit 62fe53
    } else {
Packit 62fe53
	set destfile [file tail $srcfile]
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if { ![is_remote $dest] } {
Packit 62fe53
	if { $destfile == "" || $srcfile == $destfile } {
Packit 62fe53
	    return $srcfile
Packit 62fe53
	}
Packit 62fe53
	set result [catch "exec cp -p $srcfile $destfile" output]
Packit 62fe53
	return $destfile
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    return [call_remote "" upload $dest $srcfile $destfile]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc standard_upload { dest srcfile destfile } {
Packit 62fe53
    set orig_srcfile $srcfile
Packit 62fe53
Packit 62fe53
    if {[board_info $dest exists nfsdir]} {
Packit 62fe53
	set destdir [board_info $dest nfsdir]
Packit 62fe53
	if {[board_info $dest exists nfsroot_server]} {
Packit 62fe53
	    set dest [board_info $dest nfsroot_server]
Packit 62fe53
	} else {
Packit 62fe53
	    set dest ""
Packit 62fe53
	}
Packit 62fe53
	set srcfile "$destdir/$srcfile"
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if { "$dest" != "" } {
Packit 62fe53
	return [rsh_upload $dest $srcfile $destfile]
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    set result [catch "exec cp -p $srcfile $destfile" output]
Packit 62fe53
    if {[regexp "same file|are identical" $output]} {
Packit 62fe53
	set result 0
Packit 62fe53
	set output ""
Packit 62fe53
    } else {
Packit 62fe53
	# try to make sure we can read it
Packit 62fe53
	# and write it (in case we copy onto it again)
Packit 62fe53
	catch {exec chmod u+rw $destfile}
Packit 62fe53
    }
Packit 62fe53
    if { $result != 0 || $output != "" } {
Packit 62fe53
	perror "remote_upload to $dest of $srcfile to $destfile: $output"
Packit 62fe53
	return ""
Packit 62fe53
    } else {
Packit 62fe53
	return $destfile
Packit 62fe53
    }
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# A standard procedure to call the appropriate function. It first looks
Packit 62fe53
# for a board-specific version, then a version specific to the protocol,
Packit 62fe53
# and then finally it will call standard_$proc.
Packit 62fe53
#
Packit 62fe53
proc call_remote { type proc dest args } {
Packit 62fe53
    if {[board_info $dest exists name]} {
Packit 62fe53
	set dest [board_info $dest name]
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if { $proc == "reboot" } {
Packit 62fe53
	regsub {/.*} "$dest" "" dest
Packit 62fe53
	verbose "Changed dest to $dest"
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if { $dest != "host" && $dest != "build" && $dest != "target" } {
Packit 62fe53
	if { ![board_info $dest exists name] } {
Packit 62fe53
	    global board
Packit 62fe53
Packit 62fe53
	    if {[info exists board]} {
Packit 62fe53
		error "board exists"
Packit 62fe53
	    }
Packit 62fe53
	    load_board_description $dest
Packit 62fe53
	    if { $proc == "reboot" } {
Packit 62fe53
		regsub {/.*} "$dest" "" dest
Packit 62fe53
		verbose "Changed dest to $dest"
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    set high_prot ""
Packit 62fe53
    if { $type != "raw" } {
Packit 62fe53
	if {[board_info $dest exists protocol]} {
Packit 62fe53
	    set high_prot "${dest} [board_info $dest protocol]"
Packit 62fe53
	} else {
Packit 62fe53
	    set high_prot "${dest} [board_info $dest generic_name]"
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    verbose "call_remote $type $proc $dest $args " 3
Packit 62fe53
    # Close has to be handled specially.
Packit 62fe53
    if { $proc == "close" || $proc == "open" } {
Packit 62fe53
	foreach try "$high_prot [board_info $dest connect] telnet standard" {
Packit 62fe53
	    if { $try != "" } {
Packit 62fe53
		if { [info procs "${try}_${proc}"] != "" } {
Packit 62fe53
		    verbose "call_remote calling ${try}_${proc}" 3
Packit 62fe53
		    set result [eval ${try}_${proc} \"$dest\" $args]
Packit 62fe53
		    break
Packit 62fe53
		}
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
	set ft "[board_info $dest file_transfer]"
Packit 62fe53
	if { [info procs "${ft}_${proc}"] != "" } {
Packit 62fe53
	    verbose "calling ${ft}_${proc} $dest $args" 3
Packit 62fe53
	    set result2 [eval ${ft}_${proc} \"$dest\" $args]
Packit 62fe53
	}
Packit 62fe53
	if {![info exists result]} {
Packit 62fe53
	    if {[info exists result2]} {
Packit 62fe53
		set result $result2
Packit 62fe53
	    } else {
Packit 62fe53
		set result ""
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
	return $result
Packit 62fe53
    }
Packit 62fe53
    foreach try "${high_prot} [board_info $dest file_transfer] [board_info $dest connect] telnet standard" {
Packit 62fe53
	verbose "looking for ${try}_${proc}" 4
Packit 62fe53
	if { $try != "" } {
Packit 62fe53
	    if { [info procs "${try}_${proc}"] != "" } {
Packit 62fe53
		verbose "call_remote calling ${try}_${proc}" 3
Packit 62fe53
		return [eval ${try}_${proc} \"$dest\" $args]
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
    if { $proc == "close" } {
Packit 62fe53
	return ""
Packit 62fe53
    }
Packit 62fe53
    error "No procedure for '$proc' in call_remote"
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Send FILE through the existing session established to DEST.
Packit 62fe53
#
Packit 62fe53
proc remote_transmit { dest file } {
Packit 62fe53
    return [call_remote "" transmit "$dest" "$file"]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_raw_transmit { dest file } {
Packit 62fe53
    return [call_remote raw transmit "$dest" "$file"]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# The default transmit procedure if no other exists. This feeds the
Packit 62fe53
# supplied file directly into the connection.
Packit 62fe53
#
Packit 62fe53
proc standard_transmit {dest file} {
Packit 62fe53
    if {[board_info ${dest} exists name]} {
Packit 62fe53
	set dest [board_info ${dest} name]
Packit 62fe53
    }
Packit 62fe53
    if {[board_info ${dest} exists baud]} {
Packit 62fe53
	set baud [board_info ${dest} baud]
Packit 62fe53
    } else {
Packit 62fe53
	set baud 9600
Packit 62fe53
    }
Packit 62fe53
    set shell_id [board_info ${dest} fileid]
Packit 62fe53
Packit 62fe53
    set lines 0
Packit 62fe53
    set chars 0
Packit 62fe53
    set fd [open $file r]
Packit 62fe53
    while { [gets $fd cur_line] >= 0 } {
Packit 62fe53
	set errmess ""
Packit 62fe53
	catch "send -i $shell_id \"$cur_line\r\"" errmess
Packit 62fe53
	if {[string match "write\(spawn_id=\[0-9\]+\):" $errmess]} {
Packit 62fe53
	    perror "sent \"$cur_line\" got expect error \"$errmess\""
Packit 62fe53
	    catch "close $fd"
Packit 62fe53
	    return -1
Packit 62fe53
	}
Packit 62fe53
	set chars [expr {$chars + ([string length $cur_line] * 10)}]
Packit 62fe53
	if { $chars > $baud } {
Packit 62fe53
	    sleep 1
Packit 62fe53
	    set chars 0
Packit 62fe53
	}
Packit 62fe53
	verbose "." 3
Packit 62fe53
	verbose "Sent $cur_line" 4
Packit 62fe53
	incr lines
Packit 62fe53
    }
Packit 62fe53
    verbose "$lines lines transmitted" 2
Packit 62fe53
    close $fd
Packit 62fe53
    return 0
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_send { dest string } {
Packit 62fe53
    return [call_remote "" send "$dest" "$string"]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_raw_send { dest string } {
Packit 62fe53
    return [call_remote raw send "$dest" "$string"]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc standard_send { dest string } {
Packit 62fe53
    if {![board_info $dest exists fileid]} {
Packit 62fe53
	perror "no fileid for $dest"
Packit 62fe53
	return "no fileid for $dest"
Packit 62fe53
    } else {
Packit 62fe53
	set shell_id [board_info $dest fileid]
Packit 62fe53
	verbose "shell_id in standard_send is $shell_id" 3
Packit 62fe53
	verbose "send -i [board_info $dest fileid] -- $string" 3
Packit 62fe53
	if {[catch "send -i [board_info $dest fileid] -- \$string" errorInfo]} {
Packit 62fe53
	    return "$errorInfo"
Packit 62fe53
	} else {
Packit 62fe53
	    return ""
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc file_on_host { op file args } {
Packit 62fe53
    return [eval remote_file host \"$op\" \"$file\" $args]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc file_on_build { op file args } {
Packit 62fe53
    return [eval remote_file build \"$op\" \"$file\" $args]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_file { dest args } {
Packit 62fe53
    return [eval call_remote \"\" file \"$dest\" $args]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_raw_file { dest args } {
Packit 62fe53
    return [eval call_remote raw file \"$dest\" $args]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Perform the specified file op on a remote Unix board.
Packit 62fe53
#
Packit 62fe53
proc standard_file { dest op args } {
Packit 62fe53
    set file [lindex $args 0]
Packit 62fe53
    verbose "dest in proc standard_file is $dest" 3
Packit 62fe53
    if { ![is_remote $dest] } {
Packit 62fe53
	switch -- $op {
Packit 62fe53
	    cmp {
Packit 62fe53
		set otherfile [lindex $args 1]
Packit 62fe53
		if { [file exists $file] && [file exists $otherfile]
Packit 62fe53
		     && [file size $file] == [file size $otherfile] } {
Packit 62fe53
		    set r [remote_exec build cmp "$file $otherfile"]
Packit 62fe53
		    if { [lindex $r 0] == 0 } {
Packit 62fe53
			return 0
Packit 62fe53
		    }
Packit 62fe53
		}
Packit 62fe53
		return 1
Packit 62fe53
	    }
Packit 62fe53
	    tail {
Packit 62fe53
		return [file tail $file]
Packit 62fe53
	    }
Packit 62fe53
	    dirname {
Packit 62fe53
		if { [file pathtype $file] == "relative" } {
Packit 62fe53
		    set file [remote_file $dest absolute $file]
Packit 62fe53
		}
Packit 62fe53
		set result [file dirname $file]
Packit 62fe53
		if { $result == "" } {
Packit 62fe53
		    return "/"
Packit 62fe53
		}
Packit 62fe53
		return $result
Packit 62fe53
	    }
Packit 62fe53
	    join {
Packit 62fe53
		return [file join [lindex $args 0] [lindex $args 1]]
Packit 62fe53
	    }
Packit 62fe53
	    absolute {
Packit 62fe53
		return [unix_clean_filename $dest $file]
Packit 62fe53
	    }
Packit 62fe53
	    exists {
Packit 62fe53
		return [file exists $file]
Packit 62fe53
	    }
Packit 62fe53
	    delete {
Packit 62fe53
		foreach x $args {
Packit 62fe53
		    if { [file exists $x] && [file isfile $x] } {
Packit 62fe53
			file delete -force -- $x
Packit 62fe53
		    }
Packit 62fe53
		}
Packit 62fe53
		return {}
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
    } else {
Packit 62fe53
	switch -- $op {
Packit 62fe53
	    exists {
Packit 62fe53
		set status [remote_exec $dest "test -f $file"]
Packit 62fe53
		return [expr {[lindex $status 0] == 0}]
Packit 62fe53
	    }
Packit 62fe53
	    delete {
Packit 62fe53
		set file ""
Packit 62fe53
		# Allow multiple files to be deleted at once.
Packit 62fe53
		foreach x $args {
Packit 62fe53
		    append file " $x"
Packit 62fe53
		}
Packit 62fe53
		verbose "remote_file deleting $file"
Packit 62fe53
		set status [remote_exec $dest "rm -f $file"]
Packit 62fe53
		return [lindex $status 0]
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Return an absolute version of the filename in $file, with . and ..
Packit 62fe53
# removed.
Packit 62fe53
#
Packit 62fe53
proc unix_clean_filename { dest file } {
Packit 62fe53
    if { [file pathtype $file] == "relative" } {
Packit 62fe53
	set file [remote_file $dest join [pwd] $file]
Packit 62fe53
    }
Packit 62fe53
    set result ""
Packit 62fe53
    foreach x [split $file "/"] {
Packit 62fe53
	if { $x == "." || $x == "" } {
Packit 62fe53
	    continue
Packit 62fe53
	}
Packit 62fe53
	if { $x == ".." } {
Packit 62fe53
	    set rlen [expr {[llength $result] - 2}]
Packit 62fe53
	    if { $rlen >= 0 } {
Packit 62fe53
		set result [lrange $result 0 $rlen]
Packit 62fe53
	    } else {
Packit 62fe53
		set result ""
Packit 62fe53
	    }
Packit 62fe53
	    continue
Packit 62fe53
	}
Packit 62fe53
	lappend result $x
Packit 62fe53
    }
Packit 62fe53
    return "/[join $result /]"
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
#
Packit 62fe53
# Start COMMANDLINE running on DEST. By default it is not possible to
Packit 62fe53
# redirect I/O. If the optional keyword "readonly" is specified, input
Packit 62fe53
# to the command may be redirected. If the optional keyword
Packit 62fe53
# "writeonly" is specified, output from the command may be redirected.
Packit 62fe53
#
Packit 62fe53
# If the command is successfully started, a positive "spawn id" is returned.
Packit 62fe53
# If the spawn fails, a negative value will be returned.
Packit 62fe53
#
Packit 62fe53
# Once the command is spawned, you can interact with it via the remote_expect
Packit 62fe53
# and remote_wait functions.
Packit 62fe53
#
Packit 62fe53
proc remote_spawn { dest commandline args } {
Packit 62fe53
    global board_info
Packit 62fe53
Packit 62fe53
    if {![is_remote $dest]} {
Packit 62fe53
	if {[info exists board_info($dest,fileid)]} {
Packit 62fe53
	    unset board_info($dest,fileid)
Packit 62fe53
	}
Packit 62fe53
	verbose "remote_spawn is local" 3
Packit 62fe53
	if {[board_info $dest exists name]} {
Packit 62fe53
	    set dest [board_info $dest name]
Packit 62fe53
	}
Packit 62fe53
Packit 62fe53
	verbose "spawning command $commandline"
Packit 62fe53
Packit 62fe53
	if { [llength $args] > 0 } {
Packit 62fe53
	    if { [lindex $args 0] == "readonly" } {
Packit 62fe53
		set result [catch { open "| ${commandline} |& cat" "r" } id]
Packit 62fe53
		if { $result != 0 } {
Packit 62fe53
		    return -1
Packit 62fe53
		}
Packit 62fe53
	    } else {
Packit 62fe53
		set result [catch {open "| ${commandline}" "w"} id]
Packit 62fe53
		if { $result != 0 } {
Packit 62fe53
		    return -1
Packit 62fe53
		}
Packit 62fe53
	    }
Packit 62fe53
	    set result [catch "spawn -leaveopen $id" result2]
Packit 62fe53
	    if { $result == 0 && $result2 == 0} {
Packit 62fe53
		verbose "setting board_info($dest,fileid) to $spawn_id" 3
Packit 62fe53
		set board_info($dest,fileid) $spawn_id
Packit 62fe53
		set board_info($dest,fileid_origid) $id
Packit 62fe53
		return $spawn_id
Packit 62fe53
	    } else {
Packit 62fe53
		# This shouldn't happen.
Packit 62fe53
		global errorInfo
Packit 62fe53
		if {[info exists errorInfo]} {
Packit 62fe53
		    set foo $errorInfo
Packit 62fe53
		} else {
Packit 62fe53
		    set foo ""
Packit 62fe53
		}
Packit 62fe53
		verbose "spawn -open $id failed, $result $result2, $foo"
Packit 62fe53
		catch "close $id"
Packit 62fe53
		return -1
Packit 62fe53
	    }
Packit 62fe53
	} else {
Packit 62fe53
	    set result [catch "spawn $commandline" pid]
Packit 62fe53
	    if { $result == 0 } {
Packit 62fe53
		verbose "setting board_info($dest,fileid) to $spawn_id" 3
Packit 62fe53
		set board_info($dest,fileid) $spawn_id
Packit 62fe53
		return $spawn_id
Packit 62fe53
	    } else {
Packit 62fe53
		verbose -log "spawn of $commandline failed"
Packit 62fe53
		return -1
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    # Seems to me there should be a cleaner way to do this.
Packit 62fe53
    if { "$args" == "" } {
Packit 62fe53
	return [call_remote "" spawn "$dest" "$commandline"]
Packit 62fe53
    } else {
Packit 62fe53
	return [call_remote "" spawn "$dest" "$commandline" $args]
Packit 62fe53
    }
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_raw_spawn { dest commandline } {
Packit 62fe53
    return [call_remote raw spawn "$dest" "$commandline"]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# The default spawn procedure. Uses rsh to connect to $dest.
Packit 62fe53
#
Packit 62fe53
proc standard_spawn { dest commandline } {
Packit 62fe53
    global board_info
Packit 62fe53
Packit 62fe53
    if {![board_info $dest exists rsh_prog]} {
Packit 62fe53
	if { [which remsh] != 0 } {
Packit 62fe53
	    set RSH remsh
Packit 62fe53
	} else {
Packit 62fe53
	    set RSH rsh
Packit 62fe53
	}
Packit 62fe53
    } else {
Packit 62fe53
	set RSH [board_info $dest rsh_prog]
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if {[board_info $dest exists hostname]} {
Packit 62fe53
	set remote [board_info $dest hostname]
Packit 62fe53
    } else {
Packit 62fe53
	set remote $dest
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if {![board_info $dest exists username]} {
Packit 62fe53
	spawn $RSH $remote $commandline
Packit 62fe53
    } else {
Packit 62fe53
	spawn $RSH -l [board_info $dest username] $remote $commandline
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    set board_info($dest,fileid) $spawn_id
Packit 62fe53
    return $spawn_id
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Run PROG on DEST, with optional arguments, input and output files.
Packit 62fe53
# It returns a list of two items. The first is ether "pass" if the
Packit 62fe53
# program loaded, ran and exited with a zero exit status, or "fail"
Packit 62fe53
# otherwise.  The second argument is any output produced by the
Packit 62fe53
# program while it was running.
Packit 62fe53
#
Packit 62fe53
proc remote_load { dest prog args } {
Packit 62fe53
    global tool
Packit 62fe53
Packit 62fe53
    set dname [board_info $dest name]
Packit 62fe53
    set cache "[getenv REMOTELOAD_CACHE]/$tool/$dname/[file tail $prog]"
Packit 62fe53
    set empty [is_remote $dest]
Packit 62fe53
    if { [board_info $dest exists is_simulator] || [getenv REMOTELOAD_CACHE] == "" } {
Packit 62fe53
	set empty 0
Packit 62fe53
    } else {
Packit 62fe53
	for { set x 0 } {$x < [llength $args] } {incr x} {
Packit 62fe53
	    if { [lindex $args $x] != "" } {
Packit 62fe53
		set empty 0
Packit 62fe53
		break
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
    if {$empty} {
Packit 62fe53
	global sum_program
Packit 62fe53
Packit 62fe53
	if {[info exists sum_program]} {
Packit 62fe53
	    if {![target_info exists objcopy]} {
Packit 62fe53
		set_currtarget_info objcopy [find_binutils_prog objcopy]
Packit 62fe53
	    }
Packit 62fe53
	    if {[is_remote host]} {
Packit 62fe53
		set dprog [remote_download host $prog "a.out"]
Packit 62fe53
	    } else {
Packit 62fe53
		set dprog $prog
Packit 62fe53
	    }
Packit 62fe53
	    set status [remote_exec host "[target_info objcopy]" "-O srec $dprog ${dprog}.sum"]
Packit 62fe53
	    if {[is_remote host]} {
Packit 62fe53
		remote_file upload ${dprog}.sum ${prog}.sum
Packit 62fe53
	    }
Packit 62fe53
	    if { [lindex $status 0] == 0 } {
Packit 62fe53
		set sumout [remote_exec build "$sum_program" "${prog}.sum"]
Packit 62fe53
		set sum [lindex $sumout 1]
Packit 62fe53
		regsub "\[\r\n \t\]+$" "$sum" "" sum
Packit 62fe53
	    } else {
Packit 62fe53
		set sumout [remote_exec build "$sum_program" "${prog}"]
Packit 62fe53
		set sum [lindex $sumout 1]
Packit 62fe53
		regsub "\[\r\n \t\]+$" "$sum" "" sum
Packit 62fe53
	    }
Packit 62fe53
	    remote_file build delete ${prog}.sum
Packit 62fe53
	}
Packit 62fe53
	if {[file exists $cache]} {
Packit 62fe53
	    set same 0
Packit 62fe53
	    if {[info exists sum_program]} {
Packit 62fe53
		set id [open $cache "r"]
Packit 62fe53
		set oldsum [read $id]
Packit 62fe53
		close $id
Packit 62fe53
		if { $oldsum == $sum } {
Packit 62fe53
		    set same 1
Packit 62fe53
		}
Packit 62fe53
	    } else {
Packit 62fe53
		if { [remote_file build cmp $prog $cache] == 0 } {
Packit 62fe53
		    set same 1
Packit 62fe53
		}
Packit 62fe53
	    }
Packit 62fe53
	    if { $same } {
Packit 62fe53
		set fd [open "${cache}.res" "r"]
Packit 62fe53
		gets $fd l1
Packit 62fe53
		set result [list $l1 [read $fd]]
Packit 62fe53
		close $fd
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
    if {![info exists result]} {
Packit 62fe53
	set result [eval call_remote \"\" load \"$dname\" \"$prog\" $args]
Packit 62fe53
	# Not quite happy about the "pass" condition, but it makes sense if
Packit 62fe53
	# you think about it for a while-- *why* did the test not pass?
Packit 62fe53
	if { $empty && [lindex $result 0] == "pass" } {
Packit 62fe53
	    if { [getenv LOAD_REMOTECACHE] != "" } {
Packit 62fe53
		set dir "[getenv REMOTELOAD_CACHE]/$tool/$dname"
Packit 62fe53
		if {![file exists $dir]} {
Packit 62fe53
		    file mkdir $dir
Packit 62fe53
		}
Packit 62fe53
		if {[file exists $dir]} {
Packit 62fe53
		    if {[info exists sum_program]} {
Packit 62fe53
			set id [open $cache "w"]
Packit 62fe53
			puts -nonewline $id "$sum"
Packit 62fe53
			close $id
Packit 62fe53
		    } else {
Packit 62fe53
			remote_exec build cp "$prog $cache"
Packit 62fe53
		    }
Packit 62fe53
		    set id [open "${cache}.res" "w"]
Packit 62fe53
		    puts $id [lindex $result 0]
Packit 62fe53
		    puts -nonewline $id [lindex $result 1]
Packit 62fe53
		    close $id
Packit 62fe53
		}
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
    return $result
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_raw_load { dest prog args } {
Packit 62fe53
    return [eval call_remote raw load \"$dest\" \"$prog\" $args ]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# The default load procedure if no other exists for $dest. It uses
Packit 62fe53
# remote_download and remote_exec to load and execute the program.
Packit 62fe53
#
Packit 62fe53
proc standard_load { dest prog args } {
Packit 62fe53
    global board_info
Packit 62fe53
Packit 62fe53
    if { [llength $args] > 0 } {
Packit 62fe53
	set pargs [lindex $args 0]
Packit 62fe53
    } else {
Packit 62fe53
	set pargs ""
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if { [llength $args] > 1 } {
Packit 62fe53
	set inp "[lindex $args 1]"
Packit 62fe53
    } else {
Packit 62fe53
	set inp ""
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if {![file exists $prog]} then {
Packit 62fe53
	# We call both here because this should never happen.
Packit 62fe53
	perror "$prog does not exist in standard_load."
Packit 62fe53
	verbose -log "$prog does not exist." 3
Packit 62fe53
	return "untested"
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if {[is_remote $dest]} {
Packit 62fe53
        if {![board_info $dest exists remotedir]} {
Packit 62fe53
            set board_info($dest,remotedir) "/tmp/runtest.[pid]"
Packit 62fe53
        }
Packit 62fe53
	set remotefile [file tail $prog]
Packit 62fe53
	set remotefile [remote_download $dest $prog $remotefile]
Packit 62fe53
	if { $remotefile == "" } {
Packit 62fe53
	    verbose -log "Download of $prog to [board_info $dest name] failed." 3
Packit 62fe53
	    return "unresolved"
Packit 62fe53
	}
Packit 62fe53
	if {[board_info $dest exists remote_link]} {
Packit 62fe53
	    if {[[board_info $dest remote_link] $remotefile]} {
Packit 62fe53
		verbose -log "Couldn't do remote link"
Packit 62fe53
		remote_file target delete $remotefile
Packit 62fe53
		return "unresolved"
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
	set status [remote_exec $dest $remotefile $pargs $inp]
Packit 62fe53
	remote_file $dest delete $remotefile
Packit 62fe53
    } else {
Packit 62fe53
	set status [remote_exec $dest $prog $pargs $inp]
Packit 62fe53
    }
Packit 62fe53
    if { [lindex $status 0] < 0 } {
Packit 62fe53
	verbose -log "Couldn't execute $prog, [lindex $status 1]" 3
Packit 62fe53
	return "unresolved"
Packit 62fe53
    }
Packit 62fe53
    set output [lindex $status 1]
Packit 62fe53
    set status [lindex $status 0]
Packit 62fe53
Packit 62fe53
    verbose -log "Executed $prog, status $status" 2
Packit 62fe53
    if {![string match "" $output]} {
Packit 62fe53
	verbose -log -- "$output" 2
Packit 62fe53
    }
Packit 62fe53
    if { $status == 0 } {
Packit 62fe53
	return [list "pass" $output]
Packit 62fe53
    } else {
Packit 62fe53
	return [list "fail" $output]
Packit 62fe53
    }
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Loads PROG into DEST.
Packit 62fe53
#
Packit 62fe53
proc remote_ld { dest prog } {
Packit 62fe53
    return [eval call_remote \"\" ld \"$dest\" \"$prog\"]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_raw_ld { dest prog } {
Packit 62fe53
    return [eval call_remote raw ld \"$dest\" \"$prog\"]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Wait up to TIMEOUT seconds for the last spawned command on DEST to
Packit 62fe53
# complete. A list of two values is returned; the first is the exit
Packit 62fe53
# status (-1 if the program timed out), and the second is any output
Packit 62fe53
# produced by the command.
Packit 62fe53
#
Packit 62fe53
proc remote_wait { dest timeout } {
Packit 62fe53
    return [eval call_remote \"\" wait \"$dest\" $timeout]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
proc remote_raw_wait { dest timeout } {
Packit 62fe53
    return [eval call_remote raw wait \"$dest\" $timeout]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# The standard wait procedure, used for commands spawned on the local
Packit 62fe53
# machine.
Packit 62fe53
#
Packit 62fe53
proc standard_wait { dest timeout } {
Packit 62fe53
    set output ""
Packit 62fe53
    set status -1
Packit 62fe53
Packit 62fe53
    if {[info exists exp_close_result]} {
Packit 62fe53
	unset exp_close_result
Packit 62fe53
    }
Packit 62fe53
    remote_expect $dest $timeout {
Packit 62fe53
	-re ".+" {
Packit 62fe53
	    append output $expect_out(buffer)
Packit 62fe53
	    exp_continue -continue_timer
Packit 62fe53
	}
Packit 62fe53
	timeout {
Packit 62fe53
	    warning "program timed out."
Packit 62fe53
	}
Packit 62fe53
	eof {
Packit 62fe53
	    # There may be trailing characters in the buffer.
Packit 62fe53
	    # Append them, too.
Packit 62fe53
	    append output $expect_out(buffer)
Packit 62fe53
	    if {[board_info $dest exists fileid_origid]} {
Packit 62fe53
		global board_info
Packit 62fe53
Packit 62fe53
		set id [board_info $dest fileid]
Packit 62fe53
		set oid [board_info $dest fileid_origid]
Packit 62fe53
		verbose "$id $oid"
Packit 62fe53
		unset board_info($dest,fileid)
Packit 62fe53
		unset board_info($dest,fileid_origid)
Packit 62fe53
		catch "close -i $id"
Packit 62fe53
		# I don't believe this. You HAVE to do a wait, even tho
Packit 62fe53
		# it won't work! stupid ()*$%*)(% expect...
Packit 62fe53
		catch "wait -i $id"
Packit 62fe53
		set r2 [catch "close $oid" res]
Packit 62fe53
		if { $r2 != 0 } {
Packit 62fe53
		    verbose "close result is $res"
Packit 62fe53
		    set status 1
Packit 62fe53
		} else {
Packit 62fe53
		    set status 0
Packit 62fe53
		}
Packit 62fe53
	    } else {
Packit 62fe53
		set s [wait -i [board_info $dest fileid]]
Packit 62fe53
		if { [lindex $s 0] != 0 && [lindex $s 2] == 0 } {
Packit 62fe53
		    set status [lindex $s 3]
Packit 62fe53
		    if { [llength $s] > 4 } {
Packit 62fe53
			if { [lindex $s 4] == "CHILDKILLED" } {
Packit 62fe53
			    set status 1
Packit 62fe53
			}
Packit 62fe53
		    }
Packit 62fe53
		}
Packit 62fe53
	    }
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    remote_close $dest
Packit 62fe53
    return [list $status $output]
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# This checks the value contained in the variable named "variable" in
Packit 62fe53
# the calling procedure for output from the status wrapper and returns
Packit 62fe53
# a non-negative value if it exists; otherwise, it returns -1. The
Packit 62fe53
# output from the wrapper is removed from the variable.
Packit 62fe53
#
Packit 62fe53
proc check_for_board_status  { variable } {
Packit 62fe53
    upvar $variable output
Packit 62fe53
Packit 62fe53
    # If all programs of this board have a wrapper that always outputs a
Packit 62fe53
    # status message, then the absence of it means that the program
Packit 62fe53
    # crashed, regardless of status found elsewhere (e.g. simulator exit
Packit 62fe53
    # code).
Packit 62fe53
    if { [target_info needs_status_wrapper] != "" } then {
Packit 62fe53
	set nomatch_return 2
Packit 62fe53
    } else {
Packit 62fe53
	set nomatch_return -1
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if {[regexp "(^|\[\r\n\])\\*\\*\\* EXIT code" $output]} {
Packit 62fe53
	regsub "^.*\\*\\*\\* EXIT code " $output "" result
Packit 62fe53
	regsub "\[\r\n\].*$" $result "" result
Packit 62fe53
	regsub -all "(^|\[\r\n\]|\r\n)\\*\\*\\* EXIT code \[^\r\n\]*(\[\r\n\]\[\r\n\]?|$)" $output "" output
Packit 62fe53
	regsub "^\[^0-9\]*" $result "" result
Packit 62fe53
	regsub "\[^0-9\]*$" $result "" result
Packit 62fe53
	verbose "got board status $result" 3
Packit 62fe53
	verbose "output is $output" 3
Packit 62fe53
	if { $result == "" } {
Packit 62fe53
	    return $nomatch_return
Packit 62fe53
	} else {
Packit 62fe53
	    return [expr {$result}]
Packit 62fe53
	}
Packit 62fe53
    } else {
Packit 62fe53
	return $nomatch_return
Packit 62fe53
    }
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# remote_expect works basically the same as standard expect, but it
Packit 62fe53
# also takes care of getting the file descriptor from the specified
Packit 62fe53
# host and also calling the timeout/eof/default section if there is an
Packit 62fe53
# error on the expect call.
Packit 62fe53
#
Packit 62fe53
proc remote_expect { board timeout args } {
Packit 62fe53
    global errorInfo errorCode
Packit 62fe53
    global remote_suppress_flag
Packit 62fe53
Packit 62fe53
    set spawn_id [board_info $board fileid]
Packit 62fe53
Packit 62fe53
    if { [llength $args] == 1 } {
Packit 62fe53
	set args "[lindex $args 0]"
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    set res {}
Packit 62fe53
    set got_re 0
Packit 62fe53
    set need_append 1
Packit 62fe53
Packit 62fe53
    set orig "$args"
Packit 62fe53
Packit 62fe53
    set error_sect ""
Packit 62fe53
    set save_next 0
Packit 62fe53
Packit 62fe53
    if { $spawn_id == "" } {
Packit 62fe53
	# This should be an invalid spawn id.
Packit 62fe53
	set spawn_id 1000
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    for { set i 0 } { $i < [llength $args] } { incr i }  {
Packit 62fe53
	if { $need_append } {
Packit 62fe53
	    append res "\n-i $spawn_id "
Packit 62fe53
	    set need_append 0
Packit 62fe53
	}
Packit 62fe53
Packit 62fe53
	set x "[lrange $args $i $i]"
Packit 62fe53
	regsub "^\n*\[ 	\]*" "$x" "" x
Packit 62fe53
Packit 62fe53
	if { $x == "-i" || $x == "-timeout" || $x == "-ex" } {
Packit 62fe53
	    append res "$x "
Packit 62fe53
	    set next [expr {$i + 1}]
Packit 62fe53
	    append res "[lrange $args $next $next]"
Packit 62fe53
	    incr i
Packit 62fe53
	    continue
Packit 62fe53
	}
Packit 62fe53
	if { $x == "-n" || $x == "-notransfer" || $x == "-nocase" || $x == "-indices" } {
Packit 62fe53
	    append res "${x} "
Packit 62fe53
	    continue
Packit 62fe53
	}
Packit 62fe53
	if { $x == "-re" } {
Packit 62fe53
	    append res "${x} "
Packit 62fe53
	    set next [expr {$i + 1}]
Packit 62fe53
	    set y [lrange $args $next $next]
Packit 62fe53
	    append res "${y} "
Packit 62fe53
	    set got_re 1
Packit 62fe53
	    incr i
Packit 62fe53
	    continue
Packit 62fe53
	}
Packit 62fe53
	if { $got_re } {
Packit 62fe53
	    set need_append 0
Packit 62fe53
	    append res "$x "
Packit 62fe53
	    set got_re 0
Packit 62fe53
	    if { $save_next } {
Packit 62fe53
		set save_next 0
Packit 62fe53
		set error_sect [lindex $args $i]
Packit 62fe53
	    }
Packit 62fe53
	} else {
Packit 62fe53
	    if { ${x} == "eof" } {
Packit 62fe53
		set save_next 1
Packit 62fe53
	    } elseif { ${x} == "default" || ${x} == "timeout" } {
Packit 62fe53
		if { $error_sect == "" } {
Packit 62fe53
		    set save_next 1
Packit 62fe53
		}
Packit 62fe53
	    }
Packit 62fe53
	    append res "${x} "
Packit 62fe53
	    set got_re 1
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if {[info exists remote_suppress_flag]} {
Packit 62fe53
	if { $remote_suppress_flag } {
Packit 62fe53
	    set code 1
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
    if {![info exists code]} {
Packit 62fe53
	set res "\n-timeout $timeout $res"
Packit 62fe53
	set body "expect \{\n-i $spawn_id -timeout $timeout $orig\}"
Packit 62fe53
	set code [catch {uplevel $body} string]
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if {$code == 1} {
Packit 62fe53
	if {[info exists string]} {
Packit 62fe53
	    perror "$errorInfo $errorCode $string"
Packit 62fe53
	}
Packit 62fe53
Packit 62fe53
	if { $error_sect != "" } {
Packit 62fe53
	    set code [catch {uplevel $error_sect} string]
Packit 62fe53
	} else {
Packit 62fe53
	    warning "remote_expect statement without a default case"
Packit 62fe53
	    return
Packit 62fe53
	}
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if {$code == 1} {
Packit 62fe53
	return -code error -errorinfo $errorInfo -errorcode $errorCode $string
Packit 62fe53
    } else {
Packit 62fe53
	return -code $code $string
Packit 62fe53
    }
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Push the current connection to HOST onto a stack.
Packit 62fe53
#
Packit 62fe53
proc remote_push_conn { host } {
Packit 62fe53
    global board_info
Packit 62fe53
Packit 62fe53
    set name [board_info $host name]
Packit 62fe53
Packit 62fe53
    if { $name == "" } {
Packit 62fe53
	return "fail"
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    if {![board_info $host exists fileid]} {
Packit 62fe53
	return "fail"
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    set fileid [board_info $host fileid]
Packit 62fe53
    set conninfo [board_info $host conninfo]
Packit 62fe53
    if {![info exists board_info($name,fileid_stack)]} {
Packit 62fe53
	set board_info($name,fileid_stack) {}
Packit 62fe53
    }
Packit 62fe53
    set board_info($name,fileid_stack) [list $fileid $conninfo $board_info($name,fileid_stack)]
Packit 62fe53
    unset board_info($name,fileid)
Packit 62fe53
    if {[info exists board_info($name,conninfo)]} {
Packit 62fe53
	unset board_info($name,conninfo)
Packit 62fe53
    }
Packit 62fe53
    return "pass"
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Pop a previously-pushed connection from a stack. You should have closed the
Packit 62fe53
# current connection before doing this.
Packit 62fe53
#
Packit 62fe53
proc remote_pop_conn { host } {
Packit 62fe53
    global board_info
Packit 62fe53
Packit 62fe53
    set name [board_info $host name]
Packit 62fe53
Packit 62fe53
    if { $name == "" } {
Packit 62fe53
	return "fail"
Packit 62fe53
    }
Packit 62fe53
    if {![info exists board_info($name,fileid_stack)]} {
Packit 62fe53
	return "fail"
Packit 62fe53
    }
Packit 62fe53
    set stack $board_info($name,fileid_stack)
Packit 62fe53
    if { [llength $stack] < 3 } {
Packit 62fe53
	return "fail"
Packit 62fe53
    }
Packit 62fe53
    set board_info($name,fileid) [lindex $stack 0]
Packit 62fe53
    set board_info($name,conninfo) [lindex $stack 1]
Packit 62fe53
    set board_info($name,fileid_stack) [lindex $stack 2]
Packit 62fe53
    return "pass"
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
# Swap the current connection with the topmost one on the stack.
Packit 62fe53
#
Packit 62fe53
proc remote_swap_conn { host } {
Packit 62fe53
    global board_info
Packit 62fe53
    set name [board_info $host name]
Packit 62fe53
Packit 62fe53
    if {![info exists board_info($name,fileid)]} {
Packit 62fe53
	return "fail"
Packit 62fe53
    }
Packit 62fe53
Packit 62fe53
    set fileid $board_info($name,fileid)
Packit 62fe53
    if {[info exists board_info($name,conninfo)]} {
Packit 62fe53
	set conninfo $board_info($name,conninfo)
Packit 62fe53
    } else {
Packit 62fe53
	set conninfo {}
Packit 62fe53
    }
Packit 62fe53
    if { [remote_pop_conn $host] != "pass" } {
Packit 62fe53
	set board_info($name,fileid) $fileid
Packit 62fe53
	set board_info($name,conninfo) $conninfo
Packit 62fe53
	return "fail"
Packit 62fe53
    }
Packit 62fe53
    set newfileid $board_info($name,fileid)
Packit 62fe53
    set newconninfo $board_info($name,conninfo)
Packit 62fe53
    set board_info($name,fileid) $fileid
Packit 62fe53
    set board_info($name,conninfo) $conninfo
Packit 62fe53
    remote_push_conn $host
Packit 62fe53
    set board_info($name,fileid) $newfileid
Packit 62fe53
    set board_info($name,conninfo) $newconninfo
Packit 62fe53
    return "pass"
Packit 62fe53
}
Packit 62fe53
Packit 62fe53
set sum_program "testcsum"