OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [dejagnu/] [lib/] [remote.exp] - Blame information for rev 1767

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
# Copyright (C) 92, 93, 94, 95, 96, 97, 98, 1999 Free Software Foundation, Inc.
2
 
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
 
17
# Please email any bugs, comments, and/or additions to this file to:
18
# bug-dejagnu@prep.ai.mit.edu
19
 
20
# This file was written by Rob Savoye. (rob@cygnus.com)
21
 
22
# load various protocol support modules
23
 
24
load_lib "mondfe.exp"
25
load_lib "xsh.exp"
26
load_lib "telnet.exp"
27
load_lib "rlogin.exp"
28
load_lib "kermit.exp"
29
load_lib "tip.exp"
30
load_lib "rsh.exp"
31
load_lib "ftp.exp"
32
 
33
#
34
# Open a connection to a remote host or target. This requires the target_info
35
# array be filled in with the proper info to work.
36
#
37
# type is either "build", "host", "target", or the name of a board loaded
38
# into the board_info array. The default is target if no name is supplied.
39
# It returns the spawn id of the process that is the connection.
40
#
41
 
42
proc remote_open { args } {
43
    global reboot
44
 
45
    if { [llength $args] == 0 } {
46
        set type "target"
47
    } else {
48
        set type $args
49
    }
50
 
51
    # Shudder...
52
    if { $reboot && $type == "target" } {
53
        reboot_target;
54
    }
55
 
56
    return [call_remote "" open $type];
57
}
58
 
59
proc remote_raw_open { args } {
60
    return [eval call_remote raw open $args];
61
}
62
 
63
# Run the specified COMMANDLINE on the local machine, redirecting input
64
# to file INP (if non-empty), redirecting output to file OUTP (if non-empty),
65
# and waiting TIMEOUT seconds for the command to complete before killing
66
# it. A two-member list is returned; the first member is the exit status
67
# of the command, the second is any output produced from the command
68
# (if output is redirected, this may or may not be empty). If output is
69
# redirected, both stdout and stderr will appear in the specified file.
70
#
71
# Caveats: A pipeline is used if input or output is redirected. There
72
# will be problems with killing the program if a pipeline is used. Either
73
# the "tee" command or the "cat" command is used in the pipeline if input
74
# or output is redirected. If the program needs to be killed, /bin/sh and
75
# the kill command will be invoked.
76
#
77
proc local_exec { commandline inp outp timeout } {
78
    # TCL's exec is a pile of crap. It does two very inappropriate things;
79
    # firstly, it has no business returning an error if the program being
80
    # executed happens to write to stderr. Secondly, it appends its own
81
    # error messages to the output of the command if the process exits with
82
    # non-zero status.
83
    #
84
    # So, ok, we do this funny stuff with using spawn sometimes and
85
    # open others because of spawn's inability to invoke commands with
86
    # redirected I/O. We also hope that nobody passes in a command that's
87
    # a pipeline, because spawn can't handle it.
88
    #
89
    # We want to use spawn in most cases, because tcl's pipe mechanism
90
    # doesn't assign process groups correctly and we can't reliably kill
91
    # programs that bear children. We can't use tcl's exec because it has
92
    # no way to timeout programs that hang. *sigh*
93
    #
94
    if { "$inp" == "" && "$outp" == "" } {
95
        set id -1;
96
        set result [catch "eval spawn \{${commandline}\}" pid];
97
        if { $result == 0 } {
98
            set result2 0;
99
        } else {
100
            set pid 0;
101
            set result2 5;
102
        }
103
    } else {
104
        # Can you say "uuuuuugly"? I knew you could!
105
        # All in the name of non-infinite hangs.
106
        if { $inp != "" } {
107
            set inp "< $inp";
108
            set mode "r";
109
        } else {
110
            set mode "w";
111
        }
112
 
113
        set use_tee 0;
114
        # We add |& cat so that TCL exec doesn't freak out if the
115
        # program writes to stderr.
116
        if { $outp == "" } {
117
            set outp "|& cat"
118
        } else {
119
            set outpf "$outp";
120
            set outp "> $outp"
121
            if { $inp != "" } {
122
                set use_tee 1;
123
            }
124
        }
125
        # Why do we use tee? Because open can't redirect both input and output.
126
        if { $use_tee } {
127
            set result [catch {open "| ${commandline} $inp |& tee $outpf" RDONLY} id] ;
128
        } else {
129
            set result [catch {open "| ${commandline} $inp $outp" $mode} id] ;
130
        }
131
 
132
        if { $result != 0 } {
133
            global errorInfo
134
            return [list -1 "open of $commandline $inp $outp failed: $errorInfo"];
135
        }
136
        set pid [pid $id];
137
        set result [catch "spawn -leaveopen $id" result2];
138
    }
139
    # Prepend "-" to each pid, to generate the "process group IDs" needed by
140
    # kill.
141
    set pgid "-[join $pid { -}]";
142
    verbose "pid is $pid $pgid";
143
    if { $result != 0 || $result2 != 0 } {
144
        # This shouldn't happen.
145
        global errorInfo;
146
        if [info exists errorInfo] {
147
            set foo $errorInfo;
148
        } else {
149
            set foo "";
150
        }
151
        verbose "spawn -open $id failed, $result $result2, $foo";
152
        catch "close $id";
153
        return [list -1 "spawn failed"];
154
    }
155
 
156
    set got_eof 0;
157
    set output "";
158
 
159
    # Wait for either $timeout seconds to elapse, or for the program to
160
    # exit.
161
    expect {
162
        -i $spawn_id -timeout $timeout -re ".+" {
163
            append output $expect_out(buffer);
164
            if { [string length $output] < 512000 } {
165
                exp_continue -continue_timer;
166
            }
167
        }
168
        timeout {
169
            warning "program timed out.";
170
        }
171
        eof {
172
            set got_eof 1;
173
        }
174
    }
175
 
176
    # Uuuuuuugh. Now I'm getting really sick.
177
    # If we didn't get an EOF, we have to kill the poor defenseless program.
178
    # However, TCL has no kill primitive, so we have to execute an external
179
    # command in order to execute the execution. (English. Gotta love it.)
180
    if { ! $got_eof } {
181
        verbose "killing $pid $pgid";
182
        exec sh -c "exec > /dev/null 2>&1 && (kill -2 $pgid || kill -2 $pid) && sleep 5 && (kill -15 $pgid || kill $pid) && sleep 5 && (kill -9 $pgid || kill -9 $pid)" &;
183
    }
184
    # This will hang if the kill doesn't work. Nothin' to do, and it's not ok.
185
    catch "close -i $spawn_id";
186
    set r2 [catch "wait -i $spawn_id" wres];
187
    if { $id > 0 } {
188
        set r2 [catch "close $id" res];
189
    } else {
190
        verbose "waitres is $wres" 2;
191
        if { $r2 == 0 } {
192
            set r2 [lindex $wres 3];
193
            if { [llength $wres] > 4 } {
194
                if { [lindex $wres 4] == "CHILDKILLED" } {
195
                    set r2 1;
196
                }
197
            }
198
            if { $r2 != 0 } {
199
                set res "$wres";
200
            } else {
201
                set res "";
202
            }
203
        } else {
204
            set res "wait failed";
205
        }
206
    }
207
    if { $r2 != 0 || $res != "" || ! $got_eof } {
208
        verbose "close result is $res";
209
        set status 1;
210
    } else {
211
        set status 0;
212
    }
213
    verbose "output is $output";
214
    if { $outp == "" } {
215
        return [list $status $output];
216
    } else {
217
        return [list $status ""];
218
    }
219
}
220
 
221
#
222
# Execute the supplied program on HOSTNAME. There are four optional arguments;
223
# the first is a set of arguments to pass to PROGRAM, the second is an
224
# input file to feed to stdin of PROGRAM, the third is the name of an
225
# output file where the output from PROGRAM should be written, and
226
# the fourth is a timeout value (we give up after the specified # of seconds
227
# has elapsed).
228
#
229
# A two-element list is returned. The first value is the exit status of the
230
# program (-1 if the exec failed). The second is any output produced by
231
# the program (which may or may not be empty if output from the program was
232
# redirected).
233
#
234
proc remote_exec { hostname program args } {
235
    if { [llength $args] > 0 } {
236
        set pargs [lindex $args 0];
237
    } else {
238
        set pargs ""
239
    }
240
 
241
    if { [llength $args] > 1 } {
242
        set inp "[lindex $args 1]";
243
    } else {
244
        set inp ""
245
    }
246
 
247
    if { [llength $args] > 2 } {
248
        set outp "[lindex $args 2]";
249
    } else {
250
        set outp ""
251
    }
252
 
253
    # 300 is probably a lame default.
254
    if { [llength $args] > 3 } {
255
        set timeout "[lindex $args 3]";
256
    } else {
257
        set timeout 300
258
    }
259
 
260
    verbose -log "Executing on $hostname: $program $pargs $inp $outp (timeout = $timeout)" 2;
261
 
262
    # Run it locally if appropriate.
263
    if { ![is_remote $hostname] } {
264
        return [local_exec "$program $pargs" $inp $outp $timeout];
265
    } else {
266
        return [call_remote "" exec $hostname $program $pargs $inp $outp];
267
    }
268
}
269
 
270
proc standard_exec { hostname args } {
271
    return [eval rsh_exec \"$hostname\" $args];
272
}
273
 
274
#
275
# Close the remote connection.
276
#       arg - This is the name of the machine whose connection we're closing,
277
#             or target, host or build.
278
#
279
 
280
proc remote_close { host } {
281
    while { 1 } {
282
        set result [call_remote "" close "$host"];
283
        if { [remote_pop_conn $host] != "pass" } {
284
            break;
285
        }
286
    }
287
    return $result;
288
}
289
 
290
proc remote_raw_close { host } {
291
    return [call_remote raw close "$host"];
292
}
293
 
294
proc standard_close { host } {
295
    global board_info
296
 
297
    if [board_info ${host} exists fileid] {
298
        set shell_id [board_info ${host} fileid];
299
        set pid -1;
300
 
301
        verbose "Closing the remote shell $shell_id" 2
302
        if [board_info ${host} exists fileid_origid] {
303
            set oid [board_info ${host} fileid_origid];
304
            set pid [pid $oid];
305
            unset board_info(${host},fileid_origid);
306
        } else {
307
            set result [catch "exp_pid -i $shell_id" pid];
308
            if { $result != 0 || $pid <= 0 } {
309
                set result [catch "pid $shell_id" pid];
310
                if { $result != 0 } {
311
                    set pid -1;
312
                }
313
            }
314
        }
315
        if { $pid > 0 } {
316
            verbose "doing kill, pid is $pid";
317
            # This is very, very nasty. Then again, if after did something
318
            # reasonable...
319
            set pgid "-[join $pid { -}]";
320
            exec sh -c "exec > /dev/null 2>&1 && (kill -2 $pgid || kill -2 $pid) && sleep 5 && (kill $pgid || kill $pid) && sleep 5 && (kill -9 $pgid || kill -9 $pid)" &;
321
        }
322
        verbose "pid is $pid";
323
        catch "close -i $shell_id";
324
        if [info exists oid] {
325
            catch "close $oid";
326
        }
327
        catch "wait -i $shell_id";
328
        unset board_info(${host},fileid);
329
        verbose "Shell closed.";
330
    }
331
    return 0;
332
}
333
 
334
#
335
# Set the connection into "binary" mode, a.k.a. no processing of input
336
# characters.
337
#
338
proc remote_binary { host } {
339
    return [call_remote "" binary "$host"];
340
}
341
 
342
proc remote_raw_binary { host } {
343
    return [call_remote raw binary "$host"];
344
}
345
 
346
 
347
 
348
proc remote_reboot { host } {
349
    clone_output "\nRebooting ${host}\n";
350
    # FIXME: don't close the host connection, or all the remote
351
    # procedures will fail.
352
    # remote_close $host;
353
    set status [call_remote "" reboot "$host"];
354
    if [board_info $host exists name] {
355
        set host [board_info $host name];
356
    }
357
    if { [info proc ${host}_init] != "" } {
358
        ${host}_init $host;
359
    }
360
    return $status;
361
}
362
 
363
proc standard_reboot { host } {
364
    return "";
365
}
366
#
367
# Download file FILE to DEST. If the optional DESTFILE is specified,
368
# that file will be used on the destination board. It returns either
369
# "" (indicating that the download failed), or the name of the file on
370
# the destination machine.
371
#
372
 
373
proc remote_download { dest file args } {
374
    if { [llength $args] > 0 } {
375
        set destfile [lindex $args 0];
376
    } else {
377
        set destfile [file tail $file];
378
    }
379
 
380
    if { ![is_remote $dest] } {
381
        if { $destfile == "" || $destfile == $file } {
382
            return $file;
383
        } else {
384
            set result [catch "exec cp -p $file $destfile" output];
385
            if [regexp "same file|are identical" $output] {
386
                set result 0
387
                set output ""
388
            } else {
389
                # try to make sure we can read it
390
                # and write it (in case we copy onto it again)
391
                catch {exec chmod u+rw $destfile}
392
            }
393
            if { $result != 0 || $output != "" } {
394
                perror "remote_download to $dest of $file to $destfile: $output"
395
                return "";
396
            } else {
397
                return $destfile;
398
            }
399
        }
400
    }
401
 
402
    return [call_remote "" download $dest $file $destfile];
403
}
404
 
405
#
406
# The default download procedure. Uses rcp to download to $dest.
407
#
408
 
409
proc standard_download {dest file destfile} {
410
    return [rsh_download $dest $file $destfile];
411
}
412
 
413
proc remote_upload {dest srcfile args} {
414
    if { [llength $args] > 0 } {
415
        set destfile [lindex $args 0];
416
    } else {
417
        set destfile [file tail $srcfile];
418
    }
419
 
420
    if { ![is_remote $dest] } {
421
        if { $destfile == "" || $srcfile == $destfile } {
422
            return $srcfile;
423
        }
424
        set result [catch "exec cp -p $srcfile $destfile" output];
425
        return $destfile;
426
    }
427
 
428
    return [call_remote "" upload $dest $srcfile $destfile];
429
}
430
 
431
proc standard_upload { dest srcfile destfile } {
432
    return [rsh_upload $dest $srcfile $destfile];
433
}
434
 
435
#
436
# A standard procedure to call the appropriate function. It first looks
437
# for a board-specific version, then a version specific to the protocol,
438
# and then finally it will call standard_$proc.
439
#
440
 
441
proc call_remote { type proc dest args } {
442
    if [board_info $dest exists name] {
443
        set dest [board_info $dest name];
444
    }
445
 
446
    if { $dest != "host" && $dest != "build" && $dest != "target" } {
447
        if { ![board_info $dest exists name] } {
448
            global board;
449
 
450
            if [info exists board] {
451
                blooie
452
            }
453
            load_board_description $dest;
454
        }
455
    }
456
 
457
    set high_prot ""
458
    if { $type != "raw" } {
459
        if [board_info $dest exists protocol] {
460
            set high_prot "${dest} [board_info $dest protocol]";
461
        } else {
462
            set high_prot "${dest} [board_info $dest generic_name]";
463
        }
464
    }
465
 
466
    verbose "call_remote $type $proc $dest $args " 3
467
    # Close has to be handled specially.
468
    if { $proc == "close" || $proc == "open" } {
469
        foreach try "$high_prot [board_info $dest connect] telnet standard" {
470
            if { $try != "" } {
471
                if { [info proc "${try}_${proc}"] != "" } {
472
                    verbose "call_remote calling ${try}_${proc}" 3
473
                    set result [eval ${try}_${proc} \"$dest\" $args];
474
                    break;
475
                }
476
            }
477
        }
478
        set ft "[board_info $dest file_transfer]"
479
        if { [info proc "${ft}_${proc}"] != "" } {
480
            verbose "calling ${ft}_${proc} $dest $args" 3
481
            set result2 [eval ${ft}_${proc} \"$dest\" $args];
482
        }
483
        if ![info exists result] {
484
            if [info exists result2] {
485
                set result $result2;
486
            } else {
487
                set result "";
488
            }
489
        }
490
        return $result;
491
    }
492
    foreach try "${high_prot} [board_info $dest file_transfer] [board_info $dest connect] telnet standard" {
493
        verbose "looking for ${try}_${proc}" 4
494
        if { $try != "" } {
495
            if { [info proc "${try}_${proc}"] != "" } {
496
                verbose "call_remote calling ${try}_${proc}" 3
497
                return [eval ${try}_${proc} \"$dest\" $args];
498
            }
499
        }
500
    }
501
    if { $proc == "close" } {
502
        return ""
503
    }
504
    error "No procedure for '$proc' in call_remote"
505
    return -1;
506
}
507
 
508
#
509
# Send FILE through the existing session established to DEST.
510
#
511
proc remote_transmit { dest file } {
512
    return [call_remote "" transmit "$dest" "$file"];
513
}
514
 
515
proc remote_raw_transmit { dest file } {
516
    return [call_remote raw transmit "$dest" "$file"];
517
}
518
 
519
#
520
# The default transmit procedure if no other exists. This feeds the
521
# supplied file directly into the connection.
522
#
523
proc standard_transmit {dest file} {
524
    if [board_info ${dest} exists name] {
525
        set dest [board_info ${dest} name];
526
    }
527
    if [board_info ${dest} exists baud] {
528
        set baud [board_info ${dest} baud];
529
    } else {
530
        set baud 9600;
531
    }
532
    set shell_id [board_info ${dest} fileid];
533
 
534
    set lines 0
535
    set chars 0;
536
    set fd [open $file r]
537
    while { [gets $fd cur_line] >= 0 } {
538
        set errmess ""
539
        catch "send -i $shell_id \"$cur_line\r\"" errmess
540
        if [string match "write\(spawn_id=\[0-9\]+\):" $errmess] {
541
            perror "sent \"$cur_line\" got expect error \"$errmess\""
542
            catch "close $fd"
543
            return -1
544
        }
545
        set chars [expr $chars + ([string length $cur_line] * 10)]
546
        if { $chars > $baud } {
547
            sleep 1;
548
            set chars 0
549
        }
550
        verbose "." 3
551
        verbose "Sent $cur_line" 4
552
        incr lines
553
    }
554
    verbose "$lines lines transmitted" 2
555
    close $fd
556
    return 0
557
}
558
 
559
proc remote_send { dest string } {
560
    return [call_remote "" send "$dest" "$string"];
561
}
562
 
563
proc remote_raw_send { dest string } {
564
    return [call_remote raw send "$dest" "$string"];
565
}
566
 
567
proc standard_send { dest string } {
568
    if ![board_info $dest exists fileid] {
569
        perror "no fileid for $dest"
570
        return "no fileid for $dest";
571
    } else {
572
        set shell_id [board_info $dest fileid]
573
        verbose "shell_id in standard_send is $shell_id" 3
574
        verbose "send -i [board_info $dest fileid] -- {$string}" 3
575
        if [catch "send -i [board_info $dest fileid] -- {$string}" errorInfo] {
576
            return "$errorInfo";
577
        } else {
578
            return "";
579
        }
580
    }
581
}
582
 
583
proc file_on_host { op file args } {
584
    return [eval remote_file host \"$op\" '\$file\" $args];
585
}
586
 
587
proc file_on_build { op file args } {
588
    return [eval remote_file build \"$op\" \"$file\" $args];
589
}
590
 
591
proc remote_file { dest args } {
592
    return [eval call_remote \"\" file \"$dest\" $args];
593
}
594
 
595
proc remote_raw_file { dest args } {
596
    return [eval call_remote raw file \"$dest\" $args];
597
}
598
 
599
#
600
# Perform the specified file op on a remote Unix board.
601
#
602
 
603
proc standard_file { dest op args } {
604
    set file [lindex $args 0];
605
    verbose "dest in standard_file is $dest";
606
    if { ![is_remote $dest] } {
607
        switch $op {
608
            cmp {
609
                set otherfile [lindex $args 1];
610
                if { [file exists $file] && [file exists $otherfile]
611
                     && [file size $file] == [file size $otherfile] } {
612
                    set r [remote_exec build cmp "$file $otherfile"];
613
                    if { [lindex $r 0] == 0 } {
614
                        return 0;
615
                    }
616
                }
617
                return 1;
618
            }
619
            tail {
620
                return [file tail $file];
621
            }
622
            dirname {
623
                if { [file pathtype $file] == "relative" } {
624
                    set file [remote_file $dest absolute $file];
625
                }
626
                set result [file dirname $file];
627
                if { $result == "" } {
628
                    return "/";
629
                }
630
                return $result;
631
            }
632
            join {
633
                return [file join [lindex $args 0] [lindex $args 1]];
634
            }
635
            absolute {
636
                return [unix_clean_filename $dest $file];
637
            }
638
            exists {
639
                return [file exists $file];
640
            }
641
            delete {
642
                foreach x $args {
643
                    if { [file exists $x] && [file isfile $x] } {
644
                        exec rm -f $x;
645
                    }
646
                }
647
                return;
648
            }
649
        }
650
    }
651
    switch $op {
652
        exists {
653
            # mmmm, quotes.
654
            set status [remote_exec $dest "sh -c 'exit `\[ -f $file \]`'"];
655
            return [lindex $status 0];
656
        }
657
        delete {
658
            set file ""
659
            # Allow multiple files to be deleted at once.
660
            foreach x $args {
661
                append file " $x";
662
            }
663
            verbose "remote_file deleting $file"
664
            set status [remote_exec $dest "rm -f $file"];
665
            return [lindex $status 0];
666
        }
667
    }
668
}
669
 
670
#
671
# Return an absolute version of the filename in $file, with . and ..
672
# removed.
673
#
674
proc unix_clean_filename { dest file } {
675
    if { [file pathtype $file] == "relative" } {
676
        set file [remote_file $dest join [pwd] $file];
677
    }
678
    set result "";
679
    foreach x [split $file "/"] {
680
        if { $x == "." || $x == "" } {
681
            continue;
682
        }
683
        if { $x == ".." } {
684
            set rlen [expr [llength $result] - 2];
685
            if { $rlen >= 0 } {
686
                set result [lrange $result 0 $rlen];
687
            } else {
688
                set result ""
689
            }
690
            continue;
691
        }
692
        lappend result $x;
693
    }
694
    return "/[join $result /]"
695
}
696
 
697
#
698
# Start COMMANDLINE running on DEST. By default it is not possible to
699
# redirect I/O. If the optional keyword "readonly" is specified, input
700
# to the command may be redirected. If the optional keyword
701
# "writeonly" is specified, output from the command may be redirected.
702
#
703
# If the command is successfully started, a positive "spawn id" is returned.
704
# If the spawn fails, a negative value will be returned.
705
#
706
# Once the command is spawned, you can interact with it via the remote_expect
707
# and remote_wait functions.
708
#
709
proc remote_spawn { dest commandline args } {
710
    global board_info
711
 
712
    if ![is_remote $dest] {
713
        if [info exists board_info($dest,fileid)] {
714
            unset board_info($dest,fileid);
715
        }
716
        verbose "remote_spawn is local" 3;
717
        if [board_info $dest exists name] {
718
            set dest [board_info $dest name];
719
        }
720
 
721
        verbose "spawning command $commandline"
722
 
723
        if { [llength $args] > 0 } {
724
            if { [lindex $args 0] == "readonly" } {
725
                set result [catch { open "| ${commandline} |& cat" "r" } id];
726
                if { $result != 0 } {
727
                    return -1;
728
                }
729
            } else {
730
                set result [catch {open "| ${commandline}" "w"} id] ;
731
                if { $result != 0 } {
732
                    return -1;
733
                }
734
            }
735
            set result [catch "spawn -leaveopen $id" result2];
736
            if { $result == 0 && $result2 == 0} {
737
                verbose "setting board_info($dest,fileid) to $spawn_id" 3
738
                set board_info($dest,fileid) $spawn_id;
739
                set board_info($dest,fileid_origid) $id;
740
                return $spawn_id;
741
            } else {
742
                # This shouldn't happen.
743
                global errorInfo;
744
                if [info exists errorInfo] {
745
                    set foo $errorInfo;
746
                } else {
747
                    set foo "";
748
                }
749
                verbose "spawn -open $id failed, $result $result2, $foo";
750
                catch "close $id";
751
                return -1;
752
            }
753
        } else {
754
            set result [catch "spawn $commandline" pid];
755
            if { $result == 0 } {
756
                verbose "setting board_info($dest,fileid) to $spawn_id" 3
757
                set board_info($dest,fileid) $spawn_id;
758
                return $spawn_id;
759
            } else {
760
                verbose -log "spawn of $commandline failed";
761
                return -1;
762
            }
763
        }
764
    }
765
 
766
    # Seems to me there should be a cleaner way to do this.
767
    if { "$args" == "" } {
768
        return [call_remote "" spawn "$dest" "$commandline"];
769
    } else {
770
        return [call_remote "" spawn "$dest" "$commandline" $args];
771
    }
772
}
773
 
774
proc remote_raw_spawn { dest commandline } {
775
    return [call_remote raw spawn "$dest" "$commandline"];
776
}
777
 
778
#
779
# The default spawn procedure. Uses rsh to connect to $dest.
780
#
781
proc standard_spawn { dest commandline } {
782
    global board_info
783
 
784
    if [board_info $dest exists hostname] {
785
        set remote [board_info $dest hostname];
786
    } else {
787
        set remote $dest;
788
    }
789
    spawn rsh $remote $commandline;
790
    set board_info($dest,fileid) $spawn_id;
791
    return $spawn_id;
792
}
793
 
794
#
795
# Run PROG on DEST, with optional arguments, input and output files.
796
# It returns a list of two items. The first is ether "pass" if the program
797
# loaded, ran and exited with a zero exit status, or "fail" otherwise.
798
# The second argument is any output produced by the program while it was
799
# running.
800
#
801
proc remote_load { dest prog args } {
802
    global tool
803
 
804
    set dname [board_info $dest name];
805
    set cache "[getenv REMOTELOAD_CACHE]/$tool/$dname/[file tail $prog]";
806
    set empty [is_remote $dest];
807
    if { [board_info $dest exists is_simulator] || [getenv REMOTELOAD_CACHE] == "" } {
808
        set empty 0;
809
    } else {
810
        for { set x 0; } {$x < [llength $args] } {incr x} {
811
            if { [lindex $args $x] != "" } {
812
                set empty 0;
813
                break;
814
            }
815
        }
816
    }
817
    if $empty {
818
        global sum_program;
819
 
820
        if [info exists sum_program] {
821
            if ![target_info exists objcopy] {
822
                set_currtarget_info objcopy [find_binutils_prog objcopy];
823
            }
824
            if [is_remote host] {
825
                set dprog [remote_download host $prog "a.out"];
826
            } else {
827
                set dprog $prog;
828
            }
829
            set status [remote_exec host "[target_info objcopy]" "-O srec $dprog ${dprog}.sum"];
830
            if [is_remote host] {
831
                remote_file upload ${dprog}.sum ${prog}.sum;
832
            }
833
            if { [lindex $status 0] == 0 } {
834
                set sumout [remote_exec build "$sum_program" "${prog}.sum"];
835
                set sum [lindex $sumout 1];
836
                regsub "\[\r\n \t\]+$" "$sum" "" sum;
837
            } else {
838
                set sumout [remote_exec build "$sum_program" "${prog}"];
839
                set sum [lindex $sumout 1];
840
                regsub "\[\r\n \t\]+$" "$sum" "" sum;
841
            }
842
            remote_file build delete ${prog}.sum;
843
        }
844
        if [file exists $cache] {
845
            set same 0;
846
            if [info exists sum_program] {
847
                set id [open $cache "r"];
848
                set oldsum [read $id];
849
                close $id;
850
                if { $oldsum == $sum } {
851
                    set same 1;
852
                }
853
            } else {
854
                if { [remote_file build cmp $prog $cache] == 0 } {
855
                    set same 1;
856
                }
857
            }
858
            if { $same } {
859
                set fd [open "${cache}.res" "r"];
860
                gets $fd l1;
861
                set result [list $l1 [read $fd]];
862
                close $fd;
863
            }
864
        }
865
    }
866
    if ![info exists result] {
867
        set result [eval call_remote \"\" load \"$dname\" \"$prog\" $args];
868
        # Not quite happy about the "pass" condition, but it makes sense if
869
        # you think about it for a while-- *why* did the test not pass?
870
        if { $empty && [lindex $result 0] == "pass" } {
871
            if { [getenv LOAD_REMOTECACHE] != "" } {
872
                set dir "[getenv REMOTELOAD_CACHE]/$tool/$dname"
873
                if ![file exists $dir] {
874
                    file mkdir $dir
875
                }
876
                if [file exists $dir] {
877
                    if [info exists sum_program] {
878
                        set id [open $cache "w"];
879
                        puts -nonewline $id "$sum";
880
                        close $id;
881
                    } else {
882
                        remote_exec build cp "$prog $cache";
883
                    }
884
                    set id [open "${cache}.res" "w"];
885
                    puts $id [lindex $result 0];
886
                    puts -nonewline $id [lindex $result 1];
887
                    close $id;
888
                }
889
            }
890
        }
891
    }
892
    return $result;
893
}
894
 
895
proc remote_raw_load { dest prog args } {
896
    return [eval call_remote raw load \"$dest\" \"$prog\" $args ];
897
}
898
 
899
#
900
# The default load procedure if no other exists for $dest. It uses
901
# remote_download and remote_exec to load and execute the program.
902
#
903
 
904
proc standard_load { dest prog args } {
905
    if { [llength $args] > 0 } {
906
        set pargs [lindex $args 0];
907
    } else {
908
        set pargs ""
909
    }
910
 
911
    if { [llength $args] > 1 } {
912
        set inp "[lindex $args 1]";
913
    } else {
914
        set inp ""
915
    }
916
 
917
    if ![file exists $prog] then {
918
        # We call both here because this should never happen.
919
        perror "$prog does not exist in standard_load."
920
        verbose -log "$prog does not exist." 3
921
        return "untested"
922
    }
923
 
924
    if [is_remote $dest] {
925
        set remotefile "/tmp/[file tail $prog].[pid]"
926
        set remotefile [remote_download $dest $prog $remotefile];
927
        if { $remotefile == "" } {
928
            verbose -log "Download of $prog to [board_info $dest name] failed." 3
929
            return "unresolved"
930
        }
931
        if [board_info $dest exists remote_link] {
932
            if [[board_info $dest remote_link] $remotefile] {
933
                verbose -log "Couldn't do remote link"
934
                remote_file target delete $remotefile
935
                return "unresolved"
936
            }
937
        }
938
        set status [remote_exec $dest $remotefile $pargs $inp];
939
        remote_file $dest delete $remotefile;
940
    } else {
941
        set status [remote_exec $dest $prog $pargs $inp];
942
    }
943
    if { [lindex $status 0] < 0 } {
944
        verbose -log "Couldn't execute $prog, [lindex $status 1]" 3
945
        return "unresolved"
946
    }
947
    set output [lindex $status 1]
948
    set status [lindex $status 0]
949
 
950
    verbose -log "Executed $prog, status $status" 2
951
    if ![string match "" $output] {
952
        verbose -log -- "$output" 2
953
    }
954
    if { $status == 0 } {
955
        return [list "pass" $output];
956
    } else {
957
        return [list "fail" $output];
958
    }
959
}
960
 
961
#
962
# Loads PROG into DEST.
963
#
964
proc remote_ld { dest prog } {
965
    return [eval call_remote \"\" ld \"$dest\" \"$prog\"];
966
}
967
 
968
proc remote_raw_ld { dest prog } {
969
    return [eval call_remote raw ld \"$dest\" \"$prog\"];
970
}
971
 
972
# Wait up to TIMEOUT seconds for the last spawned command on DEST to
973
# complete. A list of two values is returned; the first is the exit
974
# status (-1 if the program timed out), and the second is any output
975
# produced by the command.
976
 
977
proc remote_wait { dest timeout } {
978
    return [eval call_remote \"\" wait \"$dest\" $timeout];
979
}
980
 
981
proc remote_raw_wait { dest timeout } {
982
    return [eval call_remote raw wait \"$dest\" $timeout];
983
}
984
 
985
# The standard wait procedure, used for commands spawned on the local
986
# machine.
987
proc standard_wait { dest timeout } {
988
    set output "";
989
    set status -1;
990
 
991
    if [info exists exp_close_result] {
992
        unset exp_close_result;
993
    }
994
    remote_expect $dest $timeout {
995
        -re ".+" {
996
            append output $expect_out(buffer);
997
            if { [string length $output] > 512000 } {
998
                remote_close $dest;
999
                set status 1;
1000
            } else {
1001
                exp_continue -continue_timer;
1002
            }
1003
        }
1004
        timeout {
1005
            warning "program timed out.";
1006
        }
1007
        eof {
1008
            if [board_info $dest exists fileid_origid] {
1009
                global board_info;
1010
 
1011
                set id [board_info $dest fileid];
1012
                set oid [board_info $dest fileid_origid];
1013
                verbose "$id $oid"
1014
                unset board_info($dest,fileid);
1015
                unset board_info($dest,fileid_origid);
1016
                catch "close -i $id";
1017
                # I don't believe this. You HAVE to do a wait, even tho
1018
                # it won't work! stupid ()*$%*)(% expect...
1019
                catch "wait -i $id";
1020
                set r2 [catch "close $oid" res];
1021
                if { $r2 != 0 } {
1022
                    verbose "close result is $res";
1023
                    set status 1;
1024
                } else {
1025
                    set status 0;
1026
                }
1027
            } else {
1028
                set s [wait -i [board_info $dest fileid]];
1029
                if { [lindex $s 0] != 0 && [lindex $s 2] == 0 } {
1030
                    set status [lindex $s 3];
1031
                    if { [llength $s] > 4 } {
1032
                        if { [lindex $s 4] == "CHILDKILLED" } {
1033
                            set status 1;
1034
                        }
1035
                    }
1036
                }
1037
            }
1038
        }
1039
    }
1040
 
1041
    remote_close $dest;
1042
    return [list $status $output];
1043
}
1044
 
1045
# This checks the value cotained in the variable named "variable" in
1046
# the calling procedure for output from the status wrapper and returns
1047
# a non-negative value if it exists; otherwise, it returns -1. The
1048
# output from the wrapper is removed from the variable.
1049
 
1050
proc check_for_board_status  { variable } {
1051
    upvar $variable output;
1052
 
1053
    if [regexp "(^|\[\r\n\])\\*\\*\\* EXIT code" $output] {
1054
        regsub "^.*\\*\\*\\* EXIT code " $output "" result;
1055
        regsub "\[\r\n\].*$" $result "" result;
1056
        regsub -all "(^|\[\r\n\])\\*\\*\\* EXIT code \[^\r\n\]*(\[\r\n\]\[\r\n\]?|$)" $output "" output;
1057
        regsub "^\[^0-9\]*" $result "" result
1058
        regsub "\[^0-9\]*$" $result "" result
1059
        verbose "got board status $result" 3
1060
        verbose "output is $output" 3
1061
        if { $result == "" } {
1062
            return -1;
1063
        } else {
1064
            return [expr $result];
1065
        }
1066
    } else {
1067
        return -1;
1068
    }
1069
}
1070
 
1071
#
1072
# remote_expect works basically the same as standard expect, but it
1073
# also takes care of getting the file descriptor from the specified
1074
# host and also calling the timeout/eof/default section if there is an
1075
# error on the expect call.
1076
#
1077
 
1078
proc remote_expect { board timeout args } {
1079
    global errorInfo errorCode;
1080
    global remote_suppress_flag;
1081
 
1082
    set spawn_id [board_info $board fileid];
1083
 
1084
    if { [llength $args] == 1 } {
1085
        set args "[lindex $args 0]";
1086
    }
1087
 
1088
    set res {}
1089
    set got_re 0;
1090
    set need_append 1;
1091
 
1092
    set orig "$args";
1093
 
1094
    set error_sect "";
1095
    set save_next 0;
1096
 
1097
    if { $spawn_id == "" } {
1098
        # This should be an invalid spawn id.
1099
        set spawn_id 1000;
1100
    }
1101
 
1102
    for { set i 0; } { $i < [llength $args] } { incr i ; }  {
1103
        if { $need_append } {
1104
            append res "\n-i $spawn_id ";
1105
            set need_append 0;
1106
        }
1107
 
1108
        set x "[lrange $args $i $i]";
1109
        regsub "^\n*\[  \]*" "$x" "" x;
1110
 
1111
        if { $x == "-i" || $x == "-timeout" || $x == "-ex" } {
1112
            append res "$x ";
1113
            set next [expr ${i}+1];
1114
            append res "[lrange $args $next $next]";
1115
            incr i;
1116
            continue;
1117
        }
1118
        if { $x == "-n" || $x == "-notransfer" || $x == "-nocase" || $x == "-indices" } {
1119
            append res "${x} ";
1120
            continue;
1121
        }
1122
        if { $x == "-re" } {
1123
            append res "${x} ";
1124
            set next [expr ${i}+1];
1125
            set y [lrange $args $next $next];
1126
            append res "${y} ";
1127
            set got_re 1;
1128
            incr i;
1129
            continue;
1130
        }
1131
        if { $got_re } {
1132
            set need_append 0;
1133
            append res "$x ";
1134
            set got_re 0;
1135
            if { $save_next } {
1136
                set save_next 0;
1137
                set error_sect [lindex $args $i];
1138
            }
1139
        } else {
1140
            if { ${x} == "eof" } {
1141
                set save_next 1;
1142
            } elseif { ${x} == "default" || ${x} == "timeout" } {
1143
                if { $error_sect == "" } {
1144
                    set save_next 1;
1145
                }
1146
            }
1147
            append res "${x} ";
1148
            set got_re 1;
1149
        }
1150
    }
1151
 
1152
    if [info exists remote_suppress_flag] {
1153
        if { $remote_suppress_flag } {
1154
            set code 1;
1155
        }
1156
    }
1157
    if ![info exists code] {
1158
        set res "\n-timeout $timeout $res";
1159
        set body "expect \{\n-i $spawn_id -timeout $timeout $orig\}";
1160
        set code [catch {uplevel $body} string];
1161
    }
1162
 
1163
    if {$code == 1} {
1164
        if { $error_sect != "" } {
1165
            set code [catch {uplevel $error_sect} string];
1166
        } else {
1167
            warning "remote_expect statement without a default case?!";
1168
            return;
1169
        }
1170
    }
1171
 
1172
    if {$code == 1} {
1173
        return -code error -errorinfo $errorInfo -errorcode $errorCode $string
1174
    } elseif {$code == 2} {
1175
        return -code return $string
1176
    } elseif {$code == 3} {
1177
        return
1178
    } elseif {$code > 4} {
1179
        return -code $code $string
1180
    }
1181
}
1182
 
1183
# Push the current connection to HOST onto a stack.
1184
proc remote_push_conn { host } {
1185
    global board_info;
1186
 
1187
    set name [board_info $host name];
1188
 
1189
    if { $name == "" } {
1190
        return "fail";
1191
    }
1192
 
1193
    if ![board_info $host exists fileid] {
1194
        return "fail";
1195
    }
1196
 
1197
    set fileid [board_info $host fileid];
1198
    set conninfo [board_info $host conninfo];
1199
    if ![info exists board_info($name,fileid_stack)] {
1200
        set board_info($name,fileid_stack) {}
1201
    }
1202
    set board_info($name,fileid_stack) [list $fileid $conninfo $board_info($name,fileid_stack)];
1203
    unset board_info($name,fileid);
1204
    if [info exists board_info($name,conninfo)] {
1205
        unset board_info($name,conninfo);
1206
    }
1207
    return "pass";
1208
}
1209
 
1210
# Pop a previously-pushed connection from a stack. You should have closed the
1211
# current connection before doing this.
1212
proc remote_pop_conn { host } {
1213
    global board_info;
1214
 
1215
    set name [board_info $host name];
1216
 
1217
    if { $name == "" } {
1218
        return "fail";
1219
    }
1220
    if ![info exists board_info($name,fileid_stack)] {
1221
        return "fail";
1222
    }
1223
    set stack $board_info($name,fileid_stack);
1224
    if { [llength $stack] < 3 } {
1225
        return "fail";
1226
    }
1227
    set board_info($name,fileid) [lindex $stack 0];
1228
    set board_info($name,conninfo) [lindex $stack 1];
1229
    set board_info($name,fileid_stack) [lindex $stack 2];
1230
    return "pass";
1231
}
1232
 
1233
#
1234
# Swap the current connection with the topmost one on the stack.
1235
#
1236
proc remote_swap_conn { host } {
1237
    global board_info;
1238
    set name [board_info $host name];
1239
 
1240
    if ![info exists board_info($name,fileid)] {
1241
        return "fail";
1242
    }
1243
 
1244
    set fileid $board_info($name,fileid);
1245
    if [info exists board_info($name,conninfo)] {
1246
        set conninfo $board_info($name,conninfo);
1247
    } else {
1248
        set conninfo {}
1249
    }
1250
    if { [remote_pop_conn $host] != "pass" } {
1251
        set board_info($name,fileid) $fileid;
1252
        set board_info($name,conninfo) $conninfo;
1253
        return "fail";
1254
    }
1255
    set newfileid $board_info($name,fileid);
1256
    set newconninfo $board_info($name,conninfo);
1257
    set board_info($name,fileid) $fileid;
1258
    set board_info($name,conninfo) $conninfo;
1259
    remote_push_conn $host;
1260
    set board_info($name,fileid) $newfileid;
1261
    set board_info($name,conninfo) $newconninfo;
1262
    return "pass";
1263
}
1264
 
1265
set sum_program "testcsum";

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.