tutorial slide
[tsh-lab.git] / sdriver.pl
blob852bf3d5728178b47de6c564631933047c7eeb98
1 #!/usr/bin/perl
2 #!/usr/local/bin/perl
3 use Getopt::Std;
4 use FileHandle;
5 use IPC::Open2;
7 #######################################################################
8 # sdriver.pl - Shell driver
10 # Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved.
11 # May not be used, modified, or copied without permission.
13 # The driver runs a student's shell program as a child, sends
14 # commands and signals to the child as directed by a trace file,
15 # and captures and displays the output produced by the child.
17 # Tracefile format:
19 # The tracefile consists of text lines that are either blank lines,
20 # comment lines, driver commands, or shell commands. Blank lines are
21 # ignored. Comment lines begin with "#" and are echo'd without change
22 # to stdout. Driver commands are intepreted by the driver and are not
23 # passed to the child shell. All other lines are shell commands and
24 # are passed without modification to the shell, which reads them on
25 # stdin. Output produced by the child on stdout/stderr is read by
26 # the parent and printed on its stdout.
28 # Driver commands:
29 # TSTP Send a SIGTSTP signal to the child
30 # INT Send a SIGINT signal to the child
31 # QUIT Send a SIGQUIT signal to the child
32 # KILL Send a SIGKILL signal to the child
33 # CLOSE Close Writer (sends EOF signal to child)
34 # WAIT Wait() for child to terminate
35 # SLEEP <n> Sleep for <n> seconds
37 ######################################################################
40 # usage - print help message and terminate
42 sub usage
44 printf STDERR "$_[0]\n";
45 printf STDERR "Usage: $0 [-hv] -t <trace> -s <shellprog> -a <args>\n";
46 printf STDERR "Options:\n";
47 printf STDERR " -h Print this message\n";
48 printf STDERR " -v Be more verbose\n";
49 printf STDERR " -t <trace> Trace file\n";
50 printf STDERR " -s <shell> Shell program to test\n";
51 printf STDERR " -a <args> Shell arguments\n";
52 printf STDERR " -g Generate output for autograder\n";
53 die "\n" ;
56 # Parse the command line arguments
57 getopts('hgvt:s:a:');
58 if ($opt_h) {
59 usage();
61 if (!$opt_t) {
62 usage("Missing required -t argument");
64 if (!$opt_s) {
65 usage("Missing required -s argument");
67 $verbose = $opt_v;
68 $infile = $opt_t;
69 $shellprog = $opt_s;
70 $shellargs = $opt_a;
71 $grade = $opt_g;
73 # Make sure the input script exists and is readable
74 -e $infile
75 or die "$0: ERROR: $infile not found\n";
76 -r $infile
77 or die "$0: ERROR: $infile is not readable\n";
79 # Make sure the shell program exists and is executable
80 -e $shellprog
81 or die "$0: ERROR: $shellprog not found\n";
82 -x $shellprog
83 or die "$0: ERROR: $shellprog is not executable\n";
86 # Open the input script
87 open INFILE, $infile
88 or die "$0: ERROR: Couldn't open input file $infile: $!\n";
91 # Fork a child, run the shell in it, and connect the parent
92 # and child with a pair of unidirectional pipes:
93 # parent:Writer -> child:stdin
94 # child:stdout -> parent:Reader
96 $pid = open2(\*Reader, \*Writer, "$shellprog $shellargs");
97 Writer->autoflush();
99 # The autograder will want to know the child shell's pid
100 if ($grade) {
101 print ("pid=$pid\n");
105 # Parent reads a trace file, sends commands to the child shell.
107 while (<INFILE>) {
108 $line = $_;
109 chomp($line);
111 # Comment line
112 if ($line =~ /^#/) {
113 print "$line\n";
116 # Blank line
117 elsif ($line =~ /^\s*$/) {
118 if ($verbose) {
119 print "$0: Ignoring blank line\n";
123 # Send SIGTSTP (ctrl-z)
124 elsif ($line =~ /TSTP/) {
125 if ($verbose) {
126 print "$0: Sending SIGTSTP signal to process $pid\n";
128 kill 'TSTP', $pid;
131 # Send SIGINT (ctrl-c)
132 elsif ($line =~ /INT/) {
133 if ($verbose) {
134 print "$0: Sending SIGINT signal to process $pid\n";
136 kill 'INT', $pid;
139 # Send SIGQUIT (whenever we need graceful termination)
140 elsif ($line =~ /QUIT/) {
141 if ($verbose) {
142 print "$0: Sending SIGQUIT signal to process $pid\n";
144 kill 'QUIT', $pid;
147 # Send SIGKILL
148 elsif ($line =~ /KILL/) {
149 if ($verbose) {
150 print "$0: Sending SIGKILL signal to process $pid\n";
152 kill 'KILL', $pid;
155 # Close pipe (sends EOF notification to child)
156 elsif ($line =~ /CLOSE/) {
157 if ($verbose) {
158 print "$0: Closing output end of pipe to child $pid\n";
160 close Writer;
163 # Wait for child to terminate
164 elsif ($line =~ /WAIT/) {
165 if ($verbose) {
166 print "$0: Waiting for child $pid\n";
168 wait;
169 if ($verbose) {
170 print "$0: Child $pid reaped\n";
174 # Sleep
175 elsif ($line =~ /SLEEP (\d+)/) {
176 if ($verbose) {
177 print "$0: Sleeping $1 secs\n";
179 sleep $1;
182 # Unknown input
183 else {
184 if ($verbose) {
185 print "$0: Sending :$line: to child $pid\n";
187 print Writer "$line\n";
192 # Parent echoes the output produced by the child.
194 close Writer;
195 if ($verbose) {
196 print "$0: Reading data from child $pid\n";
198 while ($line = <Reader>) {
199 print $line;
201 close Reader;
203 # Finally, parent reaps child
204 wait;
206 if ($verbose) {
207 print "$0: Shell terminated\n";
210 exit;