1 # Copyright
(C
) 1992-2019, 2020 Free Software Foundation
, Inc.
3 # This file is part of DejaGnu.
5 # DejaGnu is free software
; you can redistribute it and
/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation
; either version
3 of the License
, or
8 #
(at your option
) any later version.
10 # DejaGnu is distributed in the hope that it will be useful
, but
11 # WITHOUT
ANY WARRANTY
; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License
for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with DejaGnu
; if not
, write to the Free Software Foundation
,
17 # Inc.
, 51 Franklin Street
- Fifth Floor
, Boston
, MA
02110-1301, USA.
19 # This file was written by Rob Savoye
<rob@welcomehome.org
>.
21 #
Load various protocol support modules.
31 # Open a connection to a remote host or target. This requires the target_info
32 # array be filled in with the proper
info to work.
34 # type is either
"build", "host", "target", or the name of a board loaded
35 # into the board_info array. The default is target
if no
name is supplied.
36 # It returns the spawn id of the process that is the connection.
38 proc remote_open
{ args } {
41 if { [llength $
args] == 0 } {
48 if { $reboot
&& $type eq
"target" } {
52 return [call_remote
"" open $type]
55 proc remote_raw_open
{ args } {
56 return [eval call_remote raw open $
args]
59 # Close a spawn ID
, and wait
for the process to die.
If PID is not
60 #
-1, then the process is killed
if it doesn
't exit gracefully.
62 proc close_wait_program { program_id pid {wres_varname ""} } {
63 if {$wres_varname ne "" } {
64 upvar 1 $wres_varname wres
70 # Tcl has no kill primitive, so we have to execute an external
71 # command in order to kill the process.
72 verbose "doing kill, pid is $pid"
73 # Send SIGINT to give the program a better chance to interrupt
74 # whatever it might be doing and react to stdin closing.
75 # eg, in case of GDB, this should get it back to the prompt.
76 # Do so separately for each PID in the list to avoid differences
77 # in return value behavior for kill between shells
79 # Prepend "-" to generate the "process group ID" needed by
81 exec sh -c "exec > /dev/null 2>&1 && (kill -2 -$spid || kill -2 $spid)"
84 # If the program doesn't exit gracefully when stdin closes
,
85 # we
'll need to kill it. But only do this after 'wait
'ing a
86 # bit, to avoid killing the wrong process in case of a
87 # PID-reuse race. The extra sleep at the end is there to give
88 # time to kill $exec_pid without having _that_ be subject to a
91 set sh_cmd "exec > /dev/null 2>&1"
92 append sh_cmd " && sleep $secs && ("
94 append sh_cmd "(kill -15 -$spid || kill -15 $spid);"
96 append sh_cmd ") && sleep $secs && ("
98 append sh_cmd "(kill -9 -$spid || kill -9 $spid);"
100 append sh_cmd ") && sleep $secs"
101 set exec_pid [exec sh -c $sh_cmd &]
103 verbose "pid is $pid"
105 # This closes the program's stdin. This should cause well behaved
106 # interactive programs to exit. This will hang
if the kill
107 # doesn
't work. Nothin' to
do, and it
's not OK.
108 catch "close -i $program_id"
111 set res [catch "wait -i $program_id" wres]
112 if { $exec_pid != -1 && [llength $pid] == 1 } {
113 # We reaped the process, so cancel the pending force-kills, as
114 # otherwise if the PID is reused for some other unrelated
115 # process, we'd kill the wrong process.
117 #
Do this
if the PID list only has a single entry however
, as
118 # otherwise `wait
' will have returned right away regardless of
119 # whether any process of the pipeline has exited.
121 # Use `catch' in case the force
-kills have completed
, so as not
122 # to cause TCL to choke
if `kill
' returns a failure.
123 catch {exec sh -c "kill -9 $exec_pid" >& /dev/null}
129 # Run the specified COMMANDLINE on the local machine, redirecting
130 # input from file INP (if non-empty), redirecting output to file OUTP
131 # (if non-empty), and waiting TIMEOUT seconds for the command to
132 # complete before killing it. A list of two elements is returned: the
133 # first member is the exit status of the command, the second is any
134 # output produced from the command (if output is redirected, this may
135 # or may not be empty). If output is redirected, both stdout and
136 # stderr will appear in the specified file.
138 # Caveats: A pipeline is used if input or output is redirected. There
139 # will be problems with killing the program if a pipeline is used. Either
140 # the "tee" command or the "cat" command is used in the pipeline if input
141 # or output is redirected. If the program needs to be killed, /bin/sh and
142 # the kill command will be invoked.
144 proc local_exec { commandline inp outp timeout } {
145 # Tcl's exec is a pile of crap. It does two very inappropriate things.
146 # Firstly
, it has no business returning an error
if the
program being
147 # executed happens to write to stderr. Secondly
, it appends its own
148 # error messages to the output of the command
if the process exits with
151 # So
, ok
, we
do this funny stuff with using spawn sometimes and
152 # open others because of spawn
's inability to invoke commands with
153 # redirected I/O. We also hope that nobody passes in a command that's
154 # a pipeline
, because spawn can
't handle it.
156 # We want to use spawn in most cases, because Tcl's pipe mechanism
157 # doesn
't assign process groups correctly and we can't reliably kill
158 # programs that bear children. We can
't use Tcl's exec because it has
159 # no way to timeout programs that hang.
161 # The expect command will close the connection when it sees
162 # EOF. Closing the connection may send SIGHUP to the child and
163 # cause it to exit before it can exit normally. The child should
166 if { $inp eq
"" && $outp eq "" } {
168 set result
[catch
"eval spawn -ignore SIGHUP \{${commandline}\}" pid]
169 if { $result
== 0 } {
176 # Use a command pipeline with open.
185 # We add |
& cat so that Tcl exec doesn
't freak out if the
186 # program writes to stderr.
196 # Why do we use tee? Because open can't redirect both input and output.
198 set result
[catch
{open
"| $commandline $inp |& tee $outpf" RDONLY} id]
200 set result
[catch
{open
"| $commandline $inp $outp" $mode} id]
203 if { $result
!= 0 } {
204 return [list
-1 "open of $commandline $inp $outp failed: $errorInfo"]
207 set result
[catch
"spawn -ignore SIGHUP -leaveopen $id" result2]
209 # Prepend
"-" to each pid, to generate the "process group IDs" needed by
211 set pgid
"-[join $pid { -}]"
212 verbose
"pid is $pid $pgid"
213 if { $result
!= 0 || $result2
!= 0 } {
214 # This shouldn
't happen.
215 if {[info exists errorInfo]} {
220 verbose "spawn -open $id failed, $result $result2, $foo"
222 return [list -1 "spawn failed"]
228 # Wait for either $timeout seconds to elapse, or for the program to
231 -i $spawn_id -timeout $timeout -re ".+" {
232 append output $expect_out(buffer)
233 exp_continue -continue_timer
236 warning "program timed out"
243 # If we didn't
get EOF
, we have to kill the poor defenseless
program.
247 set r2
[close_wait_program $spawn_id $pid wres
]
250 #
If timed
-out
, don
't wait for all the processes associated
251 # with the pipeline to terminate as a stuck one would cause
253 catch {fconfigure $id -blocking false}
255 set r2 [catch "close $id" res]
257 verbose "waitres is $wres" 2
259 set r2 [lindex $wres 3]
260 if { [llength $wres] > 4 } {
261 if { [lindex $wres 4] eq "CHILDKILLED" } {
271 set res "wait failed"
274 if { $r2 != 0 || $res ne "" || ! $got_eof } {
275 verbose "close result is $res"
280 verbose "output is $output status $status"
281 if { $outp eq "" || $outp eq "|& cat" } {
282 return [list $status $output]
284 return [list $status ""]
289 # Execute the supplied program on HOSTNAME. There are four optional arguments
290 # the first is a set of arguments to pass to PROGRAM, the second is an
291 # input file to feed to stdin of PROGRAM, the third is the name of an
292 # output file where the output from PROGRAM should be written, and
293 # the fourth is a timeout value (we give up after the specified # of seconds
296 # A two-element list is returned. The first value is the exit status of the
297 # program (-1 if the exec failed). The second is any output produced by
298 # the program (which may or may not be empty if output from the program was
301 proc remote_exec { hostname program args } {
302 if { [llength $args] > 0 } {
303 set pargs [lindex $args 0]
308 if { [llength $args] > 1 } {
309 set inp "[lindex $args 1]"
314 if { [llength $args] > 2 } {
315 set outp "[lindex $args 2]"
320 # call_remote below gets its timeout from global variable, so set
323 set old_timeout $timeout
324 # 300 is probably a lame default.
325 if { [llength $args] > 3 } {
326 set timeout "[lindex $args 3]"
331 verbose -log "Executing on $hostname: $program $pargs $inp $outp (timeout = $timeout)" 2
333 # Run it locally if appropriate.
334 if { ![isremote $hostname] } {
335 set result [local_exec "$program $pargs" $inp $outp $timeout]
337 if { [board_info $hostname exists remotedir] } {
338 set remotedir [board_info $hostname remotedir]
339 # This is a bit too clever. Join cd $remotedir and
340 # $program on the command line with ';' and not '&&'. When
341 # called, $program may be mkdir to initially create the
342 # remote directory, in which case cd would fail.
343 set program "test -d $remotedir && cd $remotedir; $program"
345 set result [call_remote "" exec $hostname $program $pargs $inp $outp]
349 set timeout $old_timeout
353 proc standard_exec { hostname args } {
354 return [eval rsh_exec \"$hostname\" $args]
357 # Close the remote connection.
358 # arg - This is the name of the machine whose connection we're closing
,
359 # or target
, host or build.
361 proc remote_close
{ host
} {
363 set result
[call_remote
"" close $host]
364 if { [remote_pop_conn $host
] ne
"pass" } {
371 proc remote_raw_close
{ host
} {
372 return [call_remote raw close $host
]
375 proc standard_close
{ host
} {
378 if {[board_info $host
exists fileid
]} {
379 set shell_id
[board_info $host fileid
]
382 verbose
"Closing the remote shell $shell_id" 2
383 if {[board_info $host
exists fileid_origid
]} {
384 set oid
[board_info $host fileid_origid
]
386 unset board_info
($host
,fileid_origid
)
388 set result
[catch
"exp_pid -i $shell_id" pid]
389 if { $result
!= 0 || $pid
<= 0 } {
390 set result
[catch
"pid $shell_id" pid]
391 if { $result
!= 0 } {
397 close_wait_program $shell_id $pid
399 if {[info exists oid
]} {
401 # Don
't wait for all the processes associated with the
402 # pipeline to terminate as a stuck one would cause us
404 catch {fconfigure $oid -blocking false}
409 unset board_info($host,fileid)
410 verbose "Shell closed."
415 # Set the connection into "binary" mode, a.k.a. no processing of input
418 proc remote_binary { host } {
419 return [call_remote "" binary $host]
422 proc remote_raw_binary { host } {
423 return [call_remote raw binary $host]
427 # Return value of this function depends on actual implementation of reboot that
428 # will be used, in practice it is expected that remote_reboot returns 1 on
429 # success and 0 on failure.
431 proc remote_reboot { host } {
432 clone_output "\nRebooting $host\n"
433 # FIXME: don't close the host connection
, or all the remote
434 # procedures will fail.
436 set status [call_remote
"" reboot $host]
437 if {[board_info $host
exists name]} {
438 set host
[board_info $host
name]
440 if { [info procs $
{host
}_init
] ne
"" } {
446 # It looks like that this proc is never called
, instead $
{board
}_reboot defined
447 # in base
-config.exp will be used because it has higher priority and
448 # base
-config.exp is always imported by runtest.
450 proc standard_reboot
{ host
} {
454 # Download file FILE to DEST.
If the optional DESTFILE is specified
,
455 # that file will be used
on the destination board. It returns either
456 #
"" (indicating that the download failed), or the name of the file on
457 # the destination machine.
460 proc remote_download
{ dest file
args } {
461 if { [llength $
args] > 0 } {
462 set destfile
[lindex $
args 0]
464 set destfile
[file tail $file
]
467 if { ![isremote $dest
] } {
468 if { $destfile eq
"" || $destfile == $file } {
471 verbose
-log "Downloading on $dest to $destfile: $file" 2
472 set result
[catch
"exec cp -p $file $destfile" output]
473 if {[regexp
"same file|are identical" $output]} {
477 # try to make sure we can read it
478 # and write it
(in case we copy onto it again
)
479 catch
{exec chmod u
+rw $destfile
}
481 if { $result
!= 0 || $output ne
"" } {
482 perror
"remote_download to $dest of $file to $destfile: $output"
489 if { [board_info $dest
exists remotedir
] } {
490 set remotedir
[board_info $dest remotedir
]
491 set status [remote_exec $dest
mkdir "-p $remotedir"]
492 if { [lindex $
status 0] != 0 } {
493 perror
"Couldn't create remote directory $remotedir on $dest"
496 set destfile $remotedir
/$destfile
499 return [call_remote
"" download $dest $file $destfile]
502 # The default download procedure. Uses rcp to download to $dest.
504 proc standard_download
{dest file destfile
} {
505 set orig_destfile $destfile
507 if {[board_info $dest
exists nfsdir
]} {
508 set destdir
[board_info $dest nfsdir
]
509 if {[board_info $dest
exists nfsroot_server
]} {
510 set dest
[board_info $dest nfsroot_server
]
514 set destfile $destdir
/$destfile
518 set result
[rsh_download $dest $file $destfile
]
519 if { $result eq $destfile
} {
520 return $orig_destfile
526 set result
[catch
"exec cp -p $file $destfile" output]
527 if {[regexp
"same file|are identical" $output]} {
531 # try to make sure we can read it
532 # and write it
(in case we copy onto it again
)
533 catch
{exec chmod u
+rw $destfile
}
535 if { $result
!= 0 || $output ne
"" } {
536 perror
"remote_download to $dest of $file to $destfile: $output"
539 return $orig_destfile
543 proc remote_upload
{dest srcfile
args} {
544 if { [llength $
args] > 0 } {
545 set destfile
[lindex $
args 0]
547 set destfile
[file tail $srcfile
]
550 if { ![isremote $dest
] } {
551 if { $destfile eq
"" || $srcfile eq $destfile } {
554 set result
[catch
"exec cp -p $srcfile $destfile" output]
558 return [call_remote
"" upload $dest $srcfile $destfile]
561 proc standard_upload
{ dest srcfile destfile
} {
562 set orig_srcfile $srcfile
564 if {[board_info $dest
exists nfsdir
]} {
565 set destdir
[board_info $dest nfsdir
]
566 if {[board_info $dest
exists nfsroot_server
]} {
567 set dest
[board_info $dest nfsroot_server
]
571 set srcfile $destdir
/$srcfile
575 return [rsh_upload $dest $srcfile $destfile
]
578 set result
[catch
"exec cp -p $srcfile $destfile" output]
579 if {[regexp
"same file|are identical" $output]} {
583 # try to make sure we can read it
584 # and write it
(in case we copy onto it again
)
585 catch
{exec chmod u
+rw $destfile
}
587 if { $result
!= 0 || $output ne
"" } {
588 perror
"remote_upload to $dest of $srcfile to $destfile: $output"
595 # A standard procedure to
call the appropriate function. It first looks
596 #
for a board
-specific version
, then a version specific to the protocol
,
597 # and
then finally it will
call standard_$proc.
599 proc call_remote
{ type proc dest
args } {
600 if {[board_info $dest
exists name]} {
601 set dest
[board_info $dest
name]
604 if { $proc eq
"reboot" } {
605 regsub
{/.
*} $dest
"" dest
606 verbose
"Changed dest to $dest"
609 if { $dest ne
"host" && $dest ne "build" && $dest ne "target" } {
610 if { ![board_info $dest
exists name] } {
613 if {[info exists board
]} {
616 load_board_description $dest
617 if { $proc eq
"reboot" } {
618 regsub
{/.
*} $dest
"" dest
619 verbose
"Changed dest to $dest"
625 if { $type ne
"raw" } {
626 if {[board_info $dest
exists protocol
]} {
627 set high_prot
"$dest [board_info $dest protocol]"
629 set high_prot
"$dest [board_info $dest generic_name]"
633 verbose
"call_remote $type $proc $dest $args " 3
634 # Close has to be handled specially.
635 if { $proc eq
"close" || $proc eq "open" } {
636 foreach try
"$high_prot [board_info $dest connect] telnet standard" {
638 if { [info procs
"${try}_${proc}"] ne "" } {
639 verbose
"call_remote calling ${try}_${proc}" 3
640 set result
[eval $
{try
}_$
{proc
} \"$dest
\" $
args]
645 set ft
"[board_info $dest file_transfer]"
646 if { [info procs
"${ft}_${proc}"] ne "" } {
647 verbose
"calling ${ft}_${proc} $dest $args" 3
648 set result2
[eval $
{ft
}_$
{proc
} \"$dest
\" $
args]
650 if {![info exists result
]} {
651 if {[info exists result2
]} {
659 foreach try
"$high_prot [board_info $dest file_transfer] [board_info $dest connect] telnet standard" {
660 verbose
"looking for ${try}_${proc}" 4
662 if { [info procs
"${try}_${proc}"] ne "" } {
663 verbose
"call_remote calling ${try}_${proc}" 3
664 return [eval $
{try
}_$
{proc
} \"$dest
\" $
args]
668 if { $proc eq
"close" } {
671 error
"No procedure for '$proc' in call_remote"
674 # Send FILE through the existing session established to DEST.
676 proc remote_transmit
{ dest file
} {
677 return [call_remote
"" transmit $dest $file]
680 proc remote_raw_transmit
{ dest file
} {
681 return [call_remote raw transmit $dest $file
]
684 # The default transmit procedure
if no other
exists. This feeds the
685 # supplied file directly into the connection.
687 proc standard_transmit
{dest file
} {
688 if {[board_info $dest
exists name]} {
689 set dest
[board_info $dest
name]
691 if {[board_info $dest
exists baud
]} {
692 set baud
[board_info $dest baud
]
696 set shell_id
[board_info $dest fileid
]
700 set fd
[open $file r
]
701 while { [gets $fd cur_line
] >= 0 } {
703 catch
"send -i $shell_id \"$cur_line\r\"" errmess
704 if {[string match
"write\(spawn_id=\[0-9\]+\):" $errmess]} {
705 perror
"sent \"$cur_line\" got expect error \"$errmess\""
709 set chars
[expr
{$chars
+ ([string length $cur_line
] * 10)}]
710 if { $chars
> $baud
} {
715 verbose
"Sent $cur_line" 4
718 verbose
"$lines lines transmitted" 2
723 proc remote_send
{ dest string
} {
724 return [call_remote
"" send $dest $string]
727 proc remote_raw_send
{ dest string
} {
728 return [call_remote raw send $dest $string
]
731 proc standard_send
{ dest string
} {
732 if {![board_info $dest
exists fileid
]} {
733 perror
"no fileid for $dest"
734 return "no fileid for $dest"
736 set shell_id
[board_info $dest fileid
]
737 verbose
"shell_id in standard_send is $shell_id" 3
738 verbose
"send -i [board_info $dest fileid] -- $string" 3
739 if {[catch
"send -i [board_info $dest fileid] -- \$string" errorInfo]} {
747 proc file_on_host
{ op file
args } {
748 return [eval remote_file host
\"$op
\" \"$file
\" $
args]
751 proc file_on_build
{ op file
args } {
752 return [eval remote_file build
\"$op
\" \"$file
\" $
args]
755 proc remote_file
{ dest
args } {
756 return [eval call_remote
\"\" file
\"$dest
\" $
args]
759 proc remote_raw_file
{ dest
args } {
760 return [eval call_remote raw file
\"$dest
\" $
args]
763 # Perform the specified file op
on a remote Unix board.
765 proc standard_file
{ dest op
args } {
766 set file
[lindex $
args 0]
767 verbose
"dest in proc standard_file is $dest" 3
768 if { ![isremote $dest
] } {
771 set otherfile
[lindex $
args 1]
772 if { [file
exists $file
] && [file
exists $otherfile
]
773 && [file size $file
] == [file size $otherfile
] } {
774 set r
[remote_exec build cmp
"$file $otherfile"]
775 if { [lindex $r
0] == 0 } {
782 return [file tail $file
]
785 if { [file pathtype $file
] eq
"relative" } {
786 set file
[remote_file $dest absolute $file
]
788 set result
[file dirname $file
]
789 if { $result eq
"" } {
795 return [file join
[lindex $
args 0] [lindex $
args 1]]
798 return [unix_clean_filename $dest $file
]
801 return [file
exists $file
]
805 if { [file
exists $x
] && [file isfile $x
] } {
806 file
delete -force
-- $x
815 set status [remote_exec $dest
"test -f $file"]
816 return [expr
{[lindex $
status 0] == 0}]
820 # Allow multiple files to be deleted at once.
824 verbose
"remote_file deleting $file"
825 set status [remote_exec $dest
"rm -f $file"]
826 return [lindex $
status 0]
832 #
Return an absolute version of the filename in $file
, with . and ..
835 proc unix_clean_filename
{ dest file
} {
836 if { [file pathtype $file
] eq
"relative" } {
837 set file
[remote_file $dest join
[pwd
] $file
]
840 foreach x
[split $file
"/"] {
841 if { $x eq
"." || $x eq "" } {
845 set rlen
[expr
{[llength $result
] - 2}]
847 set result
[lrange $result
0 $rlen
]
855 return "/[join $result /]"
859 # Start COMMANDLINE running
on DEST. By default it is not possible to
860 # redirect I
/O.
If the optional keyword
"readonly" is specified, input
861 # to the command may be redirected.
If the optional keyword
862 #
"writeonly" is specified, output from the command may be redirected.
864 #
If the command is successfully started
, a positive
"spawn id" is returned.
865 #
If the spawn fails
, a negative value will be returned.
867 # Once the command is spawned
, you can interact with it via the remote_expect
868 # and remote_wait functions.
870 proc remote_spawn
{ dest commandline
args } {
873 if {![isremote $dest
]} {
874 if {[info exists board_info
($dest
,fileid
)]} {
875 unset board_info
($dest
,fileid
)
877 verbose
"remote_spawn is local" 3
878 if {[board_info $dest
exists name]} {
879 set dest
[board_info $dest
name]
882 verbose
"spawning command $commandline"
884 if { [llength $
args] > 0 } {
885 if { [lindex $
args 0] eq
"readonly" } {
886 set result
[catch
{ open
"| $commandline |& cat" "r" } id]
887 if { $result
!= 0 } {
891 set result
[catch
{open
"| $commandline" "w"} id]
892 if { $result
!= 0 } {
896 set result
[catch
"spawn -leaveopen $id" result2]
897 if { $result
== 0 && $result2
== 0} {
898 verbose
"setting board_info($dest,fileid) to $spawn_id" 3
899 set board_info
($dest
,fileid
) $spawn_id
900 set board_info
($dest
,fileid_origid
) $id
903 # This shouldn
't happen.
905 if {[info exists errorInfo]} {
910 verbose "spawn -open $id failed, $result $result2, $foo"
915 set result [catch "spawn $commandline" pid]
916 if { $result == 0 } {
917 verbose "setting board_info($dest,fileid) to $spawn_id" 3
918 set board_info($dest,fileid) $spawn_id
921 verbose -log "spawn of $commandline failed"
927 # Seems to me there should be a cleaner way to do this.
929 return [call_remote "" spawn $dest $commandline]
931 return [call_remote "" spawn $dest $commandline $args]
935 proc remote_raw_spawn { dest commandline } {
936 return [call_remote raw spawn $dest $commandline]
939 # The default spawn procedure. Uses rsh to connect to $dest.
941 proc standard_spawn { dest commandline } {
944 if {![board_info $dest exists rsh_prog]} {
945 if { [which remsh] != 0 } {
951 set RSH [board_info $dest rsh_prog]
954 if {[board_info $dest exists hostname]} {
955 set remote [board_info $dest hostname]
960 if {![board_info $dest exists username]} {
961 spawn $RSH $remote $commandline
963 spawn $RSH -l [board_info $dest username] $remote $commandline
966 set board_info($dest,fileid) $spawn_id
970 # Run PROG on DEST, with optional arguments, input and output files.
971 # It returns a list of two items. The first is ether "pass" if the
972 # program loaded, ran and exited with a zero exit status, or "fail"
973 # otherwise. The second argument is any output produced by the
974 # program while it was running.
976 proc remote_load { dest prog args } {
979 set dname [board_info $dest name]
980 set cache "[getenv REMOTELOAD_CACHE]/$tool/$dname/[file tail $prog]"
981 set empty [isremote $dest]
982 if { [board_info $dest exists is_simulator] || [getenv REMOTELOAD_CACHE] eq "" } {
985 for { set x 0 } {$x < [llength $args] } {incr x} {
986 if { [lindex $args $x] ne "" } {
995 if {[info exists sum_program]} {
996 if {![target_info exists objcopy]} {
997 set_currtarget_info objcopy [find_binutils_prog objcopy]
999 if {[isremote host]} {
1000 set dprog [remote_download host $prog "a.out"]
1004 set status [remote_exec host "[target_info objcopy]" "-O srec $dprog $dprog.sum"]
1005 if {[isremote host]} {
1006 remote_file upload $dprog.sum $prog.sum
1008 if { [lindex $status 0] == 0 } {
1009 set sumout [remote_exec build $sum_program $prog.sum]
1010 set sum [lindex $sumout 1]
1011 regsub "\[\r\n \t\]+$" $sum "" sum
1013 set sumout [remote_exec build $sum_program $prog]
1014 set sum [lindex $sumout 1]
1015 regsub "\[\r\n \t\]+$" $sum "" sum
1017 remote_file build delete $prog.sum
1019 if {[file exists $cache]} {
1021 if {[info exists sum_program]} {
1022 set id [open $cache "r"]
1023 set oldsum [read $id]
1025 if { $oldsum == $sum } {
1029 if { [remote_file build cmp $prog $cache] == 0 } {
1034 set fd [open $cache.res "r"]
1036 set result [list $l1 [read $fd]]
1041 if {![info exists result]} {
1042 set result [eval call_remote \"\" load \"$dname\" \"$prog\" $args]
1043 # Not quite happy about the "pass" condition, but it makes sense if
1044 # you think about it for a while-- *why* did the test not pass?
1045 if { $empty && [lindex $result 0] eq "pass" } {
1046 if { [getenv LOAD_REMOTECACHE] ne "" } {
1047 set dir "[getenv REMOTELOAD_CACHE]/$tool/$dname"
1048 if {![file exists $dir]} {
1051 if {[file exists $dir]} {
1052 if {[info exists sum_program]} {
1053 set id [open $cache "w"]
1054 puts -nonewline $id $sum
1057 remote_exec build cp "$prog $cache"
1059 set id [open $cache.res "w"]
1060 puts $id [lindex $result 0]
1061 puts -nonewline $id [lindex $result 1]
1070 proc remote_raw_load { dest prog args } {
1071 return [eval call_remote raw load \"$dest\" \"$prog\" $args ]
1074 # The default load procedure if no other exists for $dest. It uses
1075 # remote_download and remote_exec to load and execute the program.
1077 proc standard_load { dest prog args } {
1080 if { [llength $args] > 0 } {
1081 set pargs [lindex $args 0]
1086 if { [llength $args] > 1 } {
1087 set inp "[lindex $args 1]"
1092 if {![file exists $prog]} then {
1093 # We call both here because this should never happen.
1094 perror "$prog does not exist in standard_load."
1095 verbose -log "$prog does not exist." 3
1099 if {[isremote $dest]} {
1100 if {![board_info $dest exists remotedir]} {
1101 set board_info($dest,remotedir) "/tmp/runtest.[pid]"
1103 set remotefile [file tail $prog]
1104 set remotefile [remote_download $dest $prog $remotefile]
1105 if { $remotefile eq "" } {
1106 verbose -log "Download of $prog to [board_info $dest name] failed." 3
1109 if {[board_info $dest exists remote_link]} {
1110 if {[[board_info $dest remote_link] $remotefile]} {
1111 verbose -log "Couldn't
do remote link
"
1112 remote_file target
delete $remotefile
1116 set status [remote_exec $dest $remotefile $pargs $inp
]
1117 remote_file $dest
delete $remotefile
1119 set status [remote_exec $dest $prog $pargs $inp
]
1121 if { [lindex $
status 0] < 0 } {
1122 verbose
-log "Couldn't execute $prog, [lindex $status 1]" 3
1125 set output
[lindex $
status 1]
1126 set status [lindex $
status 0]
1128 verbose
-log "Executed $prog, status $status" 2
1129 if { $output ne
"" } {
1130 verbose
-log -- $output
2
1132 if { $
status == 0 } {
1133 return [list
"pass" $output]
1135 return [list
"fail" $output]
1139 # Loads PROG into DEST.
1141 proc remote_ld
{ dest prog
} {
1142 return [eval call_remote
\"\" ld \"$dest
\" \"$prog
\"]
1145 proc remote_raw_ld
{ dest prog
} {
1146 return [eval call_remote raw
ld \"$dest
\" \"$prog
\"]
1149 # Wait up to TIMEOUT
seconds for the last spawned command
on DEST to
1150 # complete. A list of two
values is returned
; the first is the exit
1151 #
status (-1 if the
program timed out
), and the second is
any output
1152 # produced by the command.
1154 proc remote_wait
{ dest timeout
} {
1155 return [eval call_remote
\"\" wait
\"$dest
\" $timeout
]
1158 proc remote_raw_wait
{ dest timeout
} {
1159 return [eval call_remote raw wait
\"$dest
\" $timeout
]
1162 # The standard wait procedure
, used
for commands spawned
on the local
1165 proc standard_wait
{ dest timeout
} {
1169 if {[info exists exp_close_result
]} {
1170 unset exp_close_result
1172 remote_expect $dest $timeout
{
1174 append output $expect_out
(buffer
)
1175 exp_continue
-continue_timer
1178 warning
"program timed out."
1181 # There may be trailing characters in the buffer.
1183 append output $expect_out
(buffer
)
1184 if {[board_info $dest
exists fileid_origid
]} {
1187 set id
[board_info $dest fileid
]
1188 set oid
[board_info $dest fileid_origid
]
1190 unset board_info
($dest
,fileid
)
1191 unset board_info
($dest
,fileid_origid
)
1192 catch
"close -i $id"
1193 # I don
't believe this. You HAVE to do a wait, even tho
1194 # it won't work
! stupid
()*$
%*)(% expect...
1196 set r2
[catch
"close $oid" res]
1198 verbose
"close result is $res"
1204 set s
[wait
-i
[board_info $dest fileid
]]
1205 if { [lindex $s
0] != 0 && [lindex $s
2] == 0 } {
1206 set status [lindex $s
3]
1207 if { [llength $s
] > 4 } {
1208 if { [lindex $s
4] eq
"CHILDKILLED" } {
1218 return [list $
status $output
]
1221 # This checks the value contained in the
variable named
"variable" in
1222 # the calling procedure
for output from the
status wrapper and returns
1223 # a non
-negative value
if it
exists; otherwise
, it returns
-1. The
1224 # output from the wrapper is removed from the
variable.
1226 proc check_for_board_status
{ variable } {
1227 upvar $
variable output
1229 #
If all programs of this board have a wrapper that always outputs a
1230 #
status message
, then the absence of it means that the
program
1231 # crashed
, regardless of
status found elsewhere
(e.g. simulator exit
1233 if { [target_info needs_status_wrapper
] ne
"" } then {
1234 set nomatch_return
2
1236 set nomatch_return
-1
1239 if {[regexp
"(^|\[\r\n\])\\*\\*\\* EXIT code" $output]} {
1240 regsub
"^.*\\*\\*\\* EXIT code " $output "" result
1241 regsub
"\[\r\n\].*$" $result "" result
1242 regsub
-all
"(^|\[\r\n\]|\r\n)\\*\\*\\* EXIT code \[^\r\n\]*(\[\r\n\]\[\r\n\]?|$)" $output "" output
1243 regsub
"^\[^0-9\]*" $result "" result
1244 regsub
"\[^0-9\]*$" $result "" result
1245 verbose
"got board status $result" 3
1246 verbose
"output is $output" 3
1247 if { $result eq
"" } {
1248 return $nomatch_return
1250 return [expr
{$result
}]
1253 return $nomatch_return
1257 # remote_expect works basically the same as standard expect
, but it
1258 # also takes care of getting the file descriptor from the specified
1259 # host and also calling the timeout
/eof
/default section
if there is an
1260 # error
on the expect
call.
1262 proc remote_expect
{ board timeout
args } {
1263 global errorInfo errorCode
1264 global remote_suppress_flag
1266 set spawn_id
[board_info $board fileid
]
1268 if { [llength $
args] == 1 } {
1269 set args "[lindex $args 0]"
1281 if { $spawn_id eq
"" } {
1282 # This should be an invalid spawn id.
1286 for { set i
0 } { $i
< [llength $
args] } { incr i
} {
1287 if { $need_append
} {
1288 append res
"\n-i $spawn_id "
1292 set x
"[lrange $args $i $i]"
1293 regsub
"^\n*\[ \t\]*" $x "" x
1295 if { $x eq
"-i" || $x eq "-timeout" || $x eq "-ex" } {
1297 set next
[expr
{$i
+ 1}]
1298 append res
"[lrange $args $next $next]"
1302 if { $x eq
"-n" || $x eq "-notransfer" || $x eq "-nocase" || $x eq "-indices" } {
1306 if { $x eq
"-re" } {
1308 set next
[expr
{$i
+ 1}]
1309 set y
[lrange $
args $next $next
]
1321 set error_sect
[lindex $
args $i
]
1324 if { $x eq
"eof" } {
1326 } elseif
{ $x eq
"default" || $x eq "timeout" } {
1327 if { $error_sect eq
"" } {
1336 if {[info exists remote_suppress_flag
]} {
1337 if { $remote_suppress_flag
} {
1341 if {![info exists code
]} {
1342 set res
"\n-timeout $timeout $res"
1343 set body
"expect \{\n-i $spawn_id -timeout $timeout $orig\}"
1344 set code
[catch
{uplevel $body
} string
]
1348 if {[info exists string
]} {
1349 perror
"$errorInfo $errorCode $string"
1352 if { $error_sect ne
"" } {
1353 set code
[catch
{uplevel $error_sect
} string
]
1355 warning
"remote_expect statement without a default case"
1361 return -code error
-errorinfo $errorInfo
-errorcode $errorCode $string
1363 return -code $code $string
1367 #
Push the current connection to HOST onto a stack.
1369 proc remote_push_conn
{ host
} {
1372 set name [board_info $host
name]
1374 if { $
name eq
"" } {
1378 if {![board_info $host
exists fileid
]} {
1382 set fileid
[board_info $host fileid
]
1383 set conninfo
[board_info $host conninfo
]
1384 if {![info exists board_info
($
name,fileid_stack
)]} {
1385 set board_info
($
name,fileid_stack
) {}
1387 set board_info
($
name,fileid_stack
) [list $fileid $conninfo $board_info
($
name,fileid_stack
)]
1388 unset board_info
($
name,fileid
)
1389 if {[info exists board_info
($
name,conninfo
)]} {
1390 unset board_info
($
name,conninfo
)
1395 #
Pop a previously
-pushed connection from a stack. You should have closed the
1396 # current connection before doing this.
1398 proc remote_pop_conn
{ host
} {
1401 set name [board_info $host
name]
1403 if { $
name eq
"" } {
1406 if {![info exists board_info
($
name,fileid_stack
)]} {
1409 set stack $board_info
($
name,fileid_stack
)
1410 if { [llength $stack
] < 3 } {
1413 set board_info
($
name,fileid
) [lindex $stack
0]
1414 set board_info
($
name,conninfo
) [lindex $stack
1]
1415 set board_info
($
name,fileid_stack
) [lindex $stack
2]
1419 # Swap the current connection with the topmost one
on the stack.
1421 proc remote_swap_conn
{ host
} {
1423 set name [board_info $host
name]
1425 if {![info exists board_info
($
name,fileid
)]} {
1429 set fileid $board_info
($
name,fileid
)
1430 if {[info exists board_info
($
name,conninfo
)]} {
1431 set conninfo $board_info
($
name,conninfo
)
1435 if { [remote_pop_conn $host
] ne
"pass" } {
1436 set board_info
($
name,fileid
) $fileid
1437 set board_info
($
name,conninfo
) $conninfo
1440 set newfileid $board_info
($
name,fileid
)
1441 set newconninfo $board_info
($
name,conninfo
)
1442 set board_info
($
name,fileid
) $fileid
1443 set board_info
($
name,conninfo
) $conninfo
1444 remote_push_conn $host
1445 set board_info
($
name,fileid
) $newfileid
1446 set board_info
($
name,conninfo
) $newconninfo
1450 set sum_program
"testcsum"