Initial commit
commit
9f8a7b6c89
|
@ -0,0 +1,88 @@
|
||||||
|
# Makefile for the CS:APP Shell Lab
|
||||||
|
|
||||||
|
TEAM = NOBODY
|
||||||
|
VERSION = 1
|
||||||
|
DRIVER = ./sdriver.pl
|
||||||
|
TSH = ./tsh
|
||||||
|
TSHREF = ./tshref
|
||||||
|
TSHARGS = "-p"
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -O2
|
||||||
|
FILES = $(TSH) ./myspin ./mysplit ./mystop ./myint
|
||||||
|
|
||||||
|
all: $(FILES)
|
||||||
|
|
||||||
|
##################
|
||||||
|
# Regression tests
|
||||||
|
##################
|
||||||
|
|
||||||
|
# Run tests using the student's shell program
|
||||||
|
test01:
|
||||||
|
$(DRIVER) -t trace01.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test02:
|
||||||
|
$(DRIVER) -t trace02.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test03:
|
||||||
|
$(DRIVER) -t trace03.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test04:
|
||||||
|
$(DRIVER) -t trace04.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test05:
|
||||||
|
$(DRIVER) -t trace05.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test06:
|
||||||
|
$(DRIVER) -t trace06.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test07:
|
||||||
|
$(DRIVER) -t trace07.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test08:
|
||||||
|
$(DRIVER) -t trace08.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test09:
|
||||||
|
$(DRIVER) -t trace09.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test10:
|
||||||
|
$(DRIVER) -t trace10.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test11:
|
||||||
|
$(DRIVER) -t trace11.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test12:
|
||||||
|
$(DRIVER) -t trace12.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test13:
|
||||||
|
$(DRIVER) -t trace13.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test14:
|
||||||
|
$(DRIVER) -t trace14.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
test15:
|
||||||
|
$(DRIVER) -t trace15.txt -s $(TSH) -a $(TSHARGS)
|
||||||
|
|
||||||
|
# Run the tests using the reference shell program
|
||||||
|
rtest01:
|
||||||
|
$(DRIVER) -t trace01.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest02:
|
||||||
|
$(DRIVER) -t trace02.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest03:
|
||||||
|
$(DRIVER) -t trace03.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest04:
|
||||||
|
$(DRIVER) -t trace04.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest05:
|
||||||
|
$(DRIVER) -t trace05.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest06:
|
||||||
|
$(DRIVER) -t trace06.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest07:
|
||||||
|
$(DRIVER) -t trace07.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest08:
|
||||||
|
$(DRIVER) -t trace08.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest09:
|
||||||
|
$(DRIVER) -t trace09.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest10:
|
||||||
|
$(DRIVER) -t trace10.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest11:
|
||||||
|
$(DRIVER) -t trace11.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest12:
|
||||||
|
$(DRIVER) -t trace12.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest13:
|
||||||
|
$(DRIVER) -t trace13.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest14:
|
||||||
|
$(DRIVER) -t trace14.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
rtest15:
|
||||||
|
$(DRIVER) -t trace15.txt -s $(TSHREF) -a $(TSHARGS)
|
||||||
|
|
||||||
|
|
||||||
|
# clean up
|
||||||
|
clean:
|
||||||
|
rm -f $(FILES) *.o *~
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
################
|
||||||
|
CS:APP Shell Lab
|
||||||
|
################
|
||||||
|
|
||||||
|
Files:
|
||||||
|
|
||||||
|
Makefile # Compiles your shell program and runs the tests
|
||||||
|
README # This file
|
||||||
|
tsh.c # The shell program that you will write and hand in
|
||||||
|
tshref # The reference shell binary.
|
||||||
|
|
||||||
|
# The remaining files are used to test your shell
|
||||||
|
sdriver.pl # The trace-driven shell driver
|
||||||
|
trace*.txt # The 15 trace files that control the shell driver
|
||||||
|
tshref.out # Example output of the reference shell on all 15 traces
|
||||||
|
|
||||||
|
# Little C programs that are called by the trace files
|
||||||
|
myspin.c # Takes argument <n> and spins for <n> seconds
|
||||||
|
mysplit.c # Forks a child that spins for <n> seconds
|
||||||
|
mystop.c # Spins for <n> seconds and sends SIGTSTP to itself
|
||||||
|
myint.c # Spins for <n> seconds and sends SIGINT to itself
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* myint.c - Another handy routine for testing your tiny shell
|
||||||
|
*
|
||||||
|
* usage: myint <n>
|
||||||
|
* Sleeps for <n> seconds and sends SIGINT to itself.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i, secs;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <n>\n", argv[0]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
secs = atoi(argv[1]);
|
||||||
|
|
||||||
|
for (i=0; i < secs; i++)
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
pid = getpid();
|
||||||
|
|
||||||
|
if (kill(pid, SIGINT) < 0)
|
||||||
|
fprintf(stderr, "kill (int) error");
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* myspin.c - A handy program for testing your tiny shell
|
||||||
|
*
|
||||||
|
* usage: myspin <n>
|
||||||
|
* Sleeps for <n> seconds in 1-second chunks.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i, secs;
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <n>\n", argv[0]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
secs = atoi(argv[1]);
|
||||||
|
for (i=0; i < secs; i++)
|
||||||
|
sleep(1);
|
||||||
|
exit(0);
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* mysplit.c - Another handy routine for testing your tiny shell
|
||||||
|
*
|
||||||
|
* usage: mysplit <n>
|
||||||
|
* Fork a child that spins for <n> seconds in 1-second chunks.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i, secs;
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <n>\n", argv[0]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
secs = atoi(argv[1]);
|
||||||
|
|
||||||
|
|
||||||
|
if (fork() == 0) { /* child */
|
||||||
|
for (i=0; i < secs; i++)
|
||||||
|
sleep(1);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parent waits for child to terminate */
|
||||||
|
wait(NULL);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* mystop.c - Another handy routine for testing your tiny shell
|
||||||
|
*
|
||||||
|
* usage: mystop <n>
|
||||||
|
* Sleeps for <n> seconds and sends SIGTSTP to itself.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i, secs;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <n>\n", argv[0]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
secs = atoi(argv[1]);
|
||||||
|
|
||||||
|
for (i=0; i < secs; i++)
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
pid = getpid();
|
||||||
|
|
||||||
|
if (kill(-pid, SIGTSTP) < 0)
|
||||||
|
fprintf(stderr, "kill (tstp) error");
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,210 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
#!/usr/local/bin/perl
|
||||||
|
use Getopt::Std;
|
||||||
|
use FileHandle;
|
||||||
|
use IPC::Open2;
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# sdriver.pl - Shell driver
|
||||||
|
#
|
||||||
|
# Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved.
|
||||||
|
# May not be used, modified, or copied without permission.
|
||||||
|
#
|
||||||
|
# The driver runs a student's shell program as a child, sends
|
||||||
|
# commands and signals to the child as directed by a trace file,
|
||||||
|
# and captures and displays the output produced by the child.
|
||||||
|
#
|
||||||
|
# Tracefile format:
|
||||||
|
#
|
||||||
|
# The tracefile consists of text lines that are either blank lines,
|
||||||
|
# comment lines, driver commands, or shell commands. Blank lines are
|
||||||
|
# ignored. Comment lines begin with "#" and are echo'd without change
|
||||||
|
# to stdout. Driver commands are intepreted by the driver and are not
|
||||||
|
# passed to the child shell. All other lines are shell commands and
|
||||||
|
# are passed without modification to the shell, which reads them on
|
||||||
|
# stdin. Output produced by the child on stdout/stderr is read by
|
||||||
|
# the parent and printed on its stdout.
|
||||||
|
#
|
||||||
|
# Driver commands:
|
||||||
|
# TSTP Send a SIGTSTP signal to the child
|
||||||
|
# INT Send a SIGINT signal to the child
|
||||||
|
# QUIT Send a SIGQUIT signal to the child
|
||||||
|
# KILL Send a SIGKILL signal to the child
|
||||||
|
# CLOSE Close Writer (sends EOF signal to child)
|
||||||
|
# WAIT Wait() for child to terminate
|
||||||
|
# SLEEP <n> Sleep for <n> seconds
|
||||||
|
#
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
#
|
||||||
|
# usage - print help message and terminate
|
||||||
|
#
|
||||||
|
sub usage
|
||||||
|
{
|
||||||
|
printf STDERR "$_[0]\n";
|
||||||
|
printf STDERR "Usage: $0 [-hv] -t <trace> -s <shellprog> -a <args>\n";
|
||||||
|
printf STDERR "Options:\n";
|
||||||
|
printf STDERR " -h Print this message\n";
|
||||||
|
printf STDERR " -v Be more verbose\n";
|
||||||
|
printf STDERR " -t <trace> Trace file\n";
|
||||||
|
printf STDERR " -s <shell> Shell program to test\n";
|
||||||
|
printf STDERR " -a <args> Shell arguments\n";
|
||||||
|
printf STDERR " -g Generate output for autograder\n";
|
||||||
|
die "\n" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse the command line arguments
|
||||||
|
getopts('hgvt:s:a:');
|
||||||
|
if ($opt_h) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
if (!$opt_t) {
|
||||||
|
usage("Missing required -t argument");
|
||||||
|
}
|
||||||
|
if (!$opt_s) {
|
||||||
|
usage("Missing required -s argument");
|
||||||
|
}
|
||||||
|
$verbose = $opt_v;
|
||||||
|
$infile = $opt_t;
|
||||||
|
$shellprog = $opt_s;
|
||||||
|
$shellargs = $opt_a;
|
||||||
|
$grade = $opt_g;
|
||||||
|
|
||||||
|
# Make sure the input script exists and is readable
|
||||||
|
-e $infile
|
||||||
|
or die "$0: ERROR: $infile not found\n";
|
||||||
|
-r $infile
|
||||||
|
or die "$0: ERROR: $infile is not readable\n";
|
||||||
|
|
||||||
|
# Make sure the shell program exists and is executable
|
||||||
|
-e $shellprog
|
||||||
|
or die "$0: ERROR: $shellprog not found\n";
|
||||||
|
-x $shellprog
|
||||||
|
or die "$0: ERROR: $shellprog is not executable\n";
|
||||||
|
|
||||||
|
|
||||||
|
# Open the input script
|
||||||
|
open INFILE, $infile
|
||||||
|
or die "$0: ERROR: Couldn't open input file $infile: $!\n";
|
||||||
|
|
||||||
|
#
|
||||||
|
# Fork a child, run the shell in it, and connect the parent
|
||||||
|
# and child with a pair of unidirectional pipes:
|
||||||
|
# parent:Writer -> child:stdin
|
||||||
|
# child:stdout -> parent:Reader
|
||||||
|
#
|
||||||
|
$pid = open2(\*Reader, \*Writer, "$shellprog $shellargs");
|
||||||
|
Writer->autoflush();
|
||||||
|
|
||||||
|
# The autograder will want to know the child shell's pid
|
||||||
|
if ($grade) {
|
||||||
|
print ("pid=$pid\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Parent reads a trace file, sends commands to the child shell.
|
||||||
|
#
|
||||||
|
while (<INFILE>) {
|
||||||
|
$line = $_;
|
||||||
|
chomp($line);
|
||||||
|
|
||||||
|
# Comment line
|
||||||
|
if ($line =~ /^#/) {
|
||||||
|
print "$line\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Blank line
|
||||||
|
elsif ($line =~ /^\s*$/) {
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Ignoring blank line\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Send SIGTSTP (ctrl-z)
|
||||||
|
elsif ($line =~ /TSTP/) {
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Sending SIGTSTP signal to process $pid\n";
|
||||||
|
}
|
||||||
|
kill 'TSTP', $pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Send SIGINT (ctrl-c)
|
||||||
|
elsif ($line =~ /INT/) {
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Sending SIGINT signal to process $pid\n";
|
||||||
|
}
|
||||||
|
kill 'INT', $pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Send SIGQUIT (whenever we need graceful termination)
|
||||||
|
elsif ($line =~ /QUIT/) {
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Sending SIGQUIT signal to process $pid\n";
|
||||||
|
}
|
||||||
|
kill 'QUIT', $pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Send SIGKILL
|
||||||
|
elsif ($line =~ /KILL/) {
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Sending SIGKILL signal to process $pid\n";
|
||||||
|
}
|
||||||
|
kill 'KILL', $pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Close pipe (sends EOF notification to child)
|
||||||
|
elsif ($line =~ /CLOSE/) {
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Closing output end of pipe to child $pid\n";
|
||||||
|
}
|
||||||
|
close Writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Wait for child to terminate
|
||||||
|
elsif ($line =~ /WAIT/) {
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Waiting for child $pid\n";
|
||||||
|
}
|
||||||
|
wait;
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Child $pid reaped\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sleep
|
||||||
|
elsif ($line =~ /SLEEP (\d+)/) {
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Sleeping $1 secs\n";
|
||||||
|
}
|
||||||
|
sleep $1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Unknown input
|
||||||
|
else {
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Sending :$line: to child $pid\n";
|
||||||
|
}
|
||||||
|
print Writer "$line\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Parent echoes the output produced by the child.
|
||||||
|
#
|
||||||
|
close Writer;
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Reading data from child $pid\n";
|
||||||
|
}
|
||||||
|
while ($line = <Reader>) {
|
||||||
|
print $line;
|
||||||
|
}
|
||||||
|
close Reader;
|
||||||
|
|
||||||
|
# Finally, parent reaps child
|
||||||
|
wait;
|
||||||
|
|
||||||
|
if ($verbose) {
|
||||||
|
print "$0: Shell terminated\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
exit;
|
|
@ -0,0 +1,5 @@
|
||||||
|
#
|
||||||
|
# trace01.txt - Properly terminate on EOF.
|
||||||
|
#
|
||||||
|
CLOSE
|
||||||
|
WAIT
|
|
@ -0,0 +1,5 @@
|
||||||
|
#
|
||||||
|
# trace02.txt - Process builtin quit command.
|
||||||
|
#
|
||||||
|
quit
|
||||||
|
WAIT
|
|
@ -0,0 +1,5 @@
|
||||||
|
#
|
||||||
|
# trace03.txt - Run a foreground job.
|
||||||
|
#
|
||||||
|
/bin/echo tsh> quit
|
||||||
|
quit
|
|
@ -0,0 +1,5 @@
|
||||||
|
#
|
||||||
|
# trace04.txt - Run a background job.
|
||||||
|
#
|
||||||
|
/bin/echo -e tsh> ./myspin 1 \046
|
||||||
|
./myspin 1 &
|
|
@ -0,0 +1,11 @@
|
||||||
|
#
|
||||||
|
# trace05.txt - Process jobs builtin command.
|
||||||
|
#
|
||||||
|
/bin/echo -e tsh> ./myspin 2 \046
|
||||||
|
./myspin 2 &
|
||||||
|
|
||||||
|
/bin/echo -e tsh> ./myspin 3 \046
|
||||||
|
./myspin 3 &
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
|
@ -0,0 +1,8 @@
|
||||||
|
#
|
||||||
|
# trace06.txt - Forward SIGINT to foreground job.
|
||||||
|
#
|
||||||
|
/bin/echo -e tsh> ./myspin 4
|
||||||
|
./myspin 4
|
||||||
|
|
||||||
|
SLEEP 2
|
||||||
|
INT
|
|
@ -0,0 +1,14 @@
|
||||||
|
#
|
||||||
|
# trace07.txt - Forward SIGINT only to foreground job.
|
||||||
|
#
|
||||||
|
/bin/echo -e tsh> ./myspin 4 \046
|
||||||
|
./myspin 4 &
|
||||||
|
|
||||||
|
/bin/echo -e tsh> ./myspin 5
|
||||||
|
./myspin 5
|
||||||
|
|
||||||
|
SLEEP 2
|
||||||
|
INT
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
|
@ -0,0 +1,14 @@
|
||||||
|
#
|
||||||
|
# trace08.txt - Forward SIGTSTP only to foreground job.
|
||||||
|
#
|
||||||
|
/bin/echo -e tsh> ./myspin 4 \046
|
||||||
|
./myspin 4 &
|
||||||
|
|
||||||
|
/bin/echo -e tsh> ./myspin 5
|
||||||
|
./myspin 5
|
||||||
|
|
||||||
|
SLEEP 2
|
||||||
|
TSTP
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
|
@ -0,0 +1,20 @@
|
||||||
|
#
|
||||||
|
# trace09.txt - Process bg builtin command
|
||||||
|
#
|
||||||
|
/bin/echo -e tsh> ./myspin 4 \046
|
||||||
|
./myspin 4 &
|
||||||
|
|
||||||
|
/bin/echo -e tsh> ./myspin 5
|
||||||
|
./myspin 5
|
||||||
|
|
||||||
|
SLEEP 2
|
||||||
|
TSTP
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
||||||
|
|
||||||
|
/bin/echo tsh> bg %2
|
||||||
|
bg %2
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
|
@ -0,0 +1,22 @@
|
||||||
|
#
|
||||||
|
# trace10.txt - Process fg builtin command.
|
||||||
|
#
|
||||||
|
/bin/echo -e tsh> ./myspin 4 \046
|
||||||
|
./myspin 4 &
|
||||||
|
|
||||||
|
SLEEP 1
|
||||||
|
/bin/echo tsh> fg %1
|
||||||
|
fg %1
|
||||||
|
|
||||||
|
SLEEP 1
|
||||||
|
TSTP
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
||||||
|
|
||||||
|
/bin/echo tsh> fg %1
|
||||||
|
fg %1
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#
|
||||||
|
# trace11.txt - Forward SIGINT to every process in foreground process group
|
||||||
|
#
|
||||||
|
/bin/echo -e tsh> ./mysplit 4
|
||||||
|
./mysplit 4
|
||||||
|
|
||||||
|
SLEEP 2
|
||||||
|
INT
|
||||||
|
|
||||||
|
/bin/echo tsh> /bin/ps a
|
||||||
|
/bin/ps a
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#
|
||||||
|
# trace12.txt - Forward SIGTSTP to every process in foreground process group
|
||||||
|
#
|
||||||
|
/bin/echo -e tsh> ./mysplit 4
|
||||||
|
./mysplit 4
|
||||||
|
|
||||||
|
SLEEP 2
|
||||||
|
TSTP
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
||||||
|
|
||||||
|
/bin/echo tsh> /bin/ps a
|
||||||
|
/bin/ps a
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#
|
||||||
|
# trace13.txt - Restart every stopped process in process group
|
||||||
|
#
|
||||||
|
/bin/echo -e tsh> ./mysplit 4
|
||||||
|
./mysplit 4
|
||||||
|
|
||||||
|
SLEEP 2
|
||||||
|
TSTP
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
||||||
|
|
||||||
|
/bin/echo tsh> /bin/ps a
|
||||||
|
/bin/ps a
|
||||||
|
|
||||||
|
/bin/echo tsh> fg %1
|
||||||
|
fg %1
|
||||||
|
|
||||||
|
/bin/echo tsh> /bin/ps a
|
||||||
|
/bin/ps a
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
#
|
||||||
|
# trace14.txt - Simple error handling
|
||||||
|
#
|
||||||
|
/bin/echo tsh> ./bogus
|
||||||
|
./bogus
|
||||||
|
|
||||||
|
/bin/echo -e tsh> ./myspin 4 \046
|
||||||
|
./myspin 4 &
|
||||||
|
|
||||||
|
/bin/echo tsh> fg
|
||||||
|
fg
|
||||||
|
|
||||||
|
/bin/echo tsh> bg
|
||||||
|
bg
|
||||||
|
|
||||||
|
/bin/echo tsh> fg a
|
||||||
|
fg a
|
||||||
|
|
||||||
|
/bin/echo tsh> bg a
|
||||||
|
bg a
|
||||||
|
|
||||||
|
/bin/echo tsh> fg 9999999
|
||||||
|
fg 9999999
|
||||||
|
|
||||||
|
/bin/echo tsh> bg 9999999
|
||||||
|
bg 9999999
|
||||||
|
|
||||||
|
/bin/echo tsh> fg %2
|
||||||
|
fg %2
|
||||||
|
|
||||||
|
/bin/echo tsh> fg %1
|
||||||
|
fg %1
|
||||||
|
|
||||||
|
SLEEP 2
|
||||||
|
TSTP
|
||||||
|
|
||||||
|
/bin/echo tsh> bg %2
|
||||||
|
bg %2
|
||||||
|
|
||||||
|
/bin/echo tsh> bg %1
|
||||||
|
bg %1
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
#
|
||||||
|
# trace15.txt - Putting it all together
|
||||||
|
#
|
||||||
|
|
||||||
|
/bin/echo tsh> ./bogus
|
||||||
|
./bogus
|
||||||
|
|
||||||
|
/bin/echo tsh> ./myspin 10
|
||||||
|
./myspin 10
|
||||||
|
|
||||||
|
SLEEP 2
|
||||||
|
INT
|
||||||
|
|
||||||
|
/bin/echo -e tsh> ./myspin 3 \046
|
||||||
|
./myspin 3 &
|
||||||
|
|
||||||
|
/bin/echo -e tsh> ./myspin 4 \046
|
||||||
|
./myspin 4 &
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
||||||
|
|
||||||
|
/bin/echo tsh> fg %1
|
||||||
|
fg %1
|
||||||
|
|
||||||
|
SLEEP 2
|
||||||
|
TSTP
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
||||||
|
|
||||||
|
/bin/echo tsh> bg %3
|
||||||
|
bg %3
|
||||||
|
|
||||||
|
/bin/echo tsh> bg %1
|
||||||
|
bg %1
|
||||||
|
|
||||||
|
/bin/echo tsh> jobs
|
||||||
|
jobs
|
||||||
|
|
||||||
|
/bin/echo tsh> fg %1
|
||||||
|
fg %1
|
||||||
|
|
||||||
|
/bin/echo tsh> quit
|
||||||
|
quit
|
||||||
|
|
|
@ -0,0 +1,509 @@
|
||||||
|
/*
|
||||||
|
* tsh - A tiny shell program with job control
|
||||||
|
*
|
||||||
|
* Schlittenhardt, Anna und Tran,Tuan-Dat
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
/* Misc manifest constants */
|
||||||
|
#define MAXLINE 1024 /* max line size */
|
||||||
|
#define MAXARGS 128 /* max args on a command line */
|
||||||
|
#define MAXJOBS 16 /* max jobs at any point in time */
|
||||||
|
#define MAXJID 1<<16 /* max job ID */
|
||||||
|
|
||||||
|
/* Job states */
|
||||||
|
#define UNDEF 0 /* undefined */
|
||||||
|
#define FG 1 /* running in foreground */
|
||||||
|
#define BG 2 /* running in background */
|
||||||
|
#define ST 3 /* stopped */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Jobs states: FG (foreground), BG (background), ST (stopped)
|
||||||
|
* Job state transitions and enabling actions:
|
||||||
|
* FG -> ST : ctrl-z
|
||||||
|
* ST -> FG : fg command
|
||||||
|
* ST -> BG : bg command
|
||||||
|
* BG -> FG : fg command
|
||||||
|
* At most 1 job can be in the FG state.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Global variables */
|
||||||
|
extern char **environ; /* defined in libc */
|
||||||
|
char prompt[] = "tsh> "; /* command line prompt (DO NOT CHANGE) */
|
||||||
|
int verbose = 0; /* if true, print additional output */
|
||||||
|
int nextjid = 1; /* next job ID to allocate */
|
||||||
|
char sbuf[MAXLINE]; /* for composing sprintf messages */
|
||||||
|
|
||||||
|
struct job_t { /* The job struct */
|
||||||
|
pid_t pid; /* job PID */
|
||||||
|
int jid; /* job ID [1, 2, ...] */
|
||||||
|
int state; /* UNDEF, BG, FG, or ST */
|
||||||
|
char cmdline[MAXLINE]; /* command line */
|
||||||
|
};
|
||||||
|
struct job_t jobs[MAXJOBS]; /* The job list */
|
||||||
|
/* End global variables */
|
||||||
|
|
||||||
|
|
||||||
|
/* Function prototypes */
|
||||||
|
|
||||||
|
/* Here are the functions that you will implement */
|
||||||
|
void eval(char *cmdline);
|
||||||
|
int builtin_cmd(char **argv);
|
||||||
|
void do_bgfg(char **argv);
|
||||||
|
void waitfg(pid_t pid);
|
||||||
|
|
||||||
|
void sigchld_handler(int sig);
|
||||||
|
void sigtstp_handler(int sig);
|
||||||
|
void sigint_handler(int sig);
|
||||||
|
|
||||||
|
/* Here are helper routines that we've provided for you */
|
||||||
|
int parseline(const char *cmdline, char **argv);
|
||||||
|
void sigquit_handler(int sig);
|
||||||
|
|
||||||
|
void clearjob(struct job_t *job);
|
||||||
|
void initjobs(struct job_t *jobs);
|
||||||
|
int maxjid(struct job_t *jobs);
|
||||||
|
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline);
|
||||||
|
int deletejob(struct job_t *jobs, pid_t pid);
|
||||||
|
pid_t fgpid(struct job_t *jobs);
|
||||||
|
struct job_t *getjobpid(struct job_t *jobs, pid_t pid);
|
||||||
|
struct job_t *getjobjid(struct job_t *jobs, int jid);
|
||||||
|
int pid2jid(pid_t pid);
|
||||||
|
void listjobs(struct job_t *jobs);
|
||||||
|
|
||||||
|
void usage(void);
|
||||||
|
void unix_error(char *msg);
|
||||||
|
void app_error(char *msg);
|
||||||
|
typedef void handler_t(int);
|
||||||
|
handler_t *Signal(int signum, handler_t *handler);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* main - The shell's main routine
|
||||||
|
*/
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
char cmdline[MAXLINE];
|
||||||
|
int emit_prompt = 1; /* emit prompt (default) */
|
||||||
|
|
||||||
|
/* Redirect stderr to stdout (so that driver will get all output
|
||||||
|
* on the pipe connected to stdout) */
|
||||||
|
dup2(1, 2);
|
||||||
|
|
||||||
|
/* Parse the command line */
|
||||||
|
while ((c = getopt(argc, argv, "hvp")) != EOF) {
|
||||||
|
switch (c) {
|
||||||
|
case 'h': /* print help message */
|
||||||
|
usage();
|
||||||
|
break;
|
||||||
|
case 'v': /* emit additional diagnostic info */
|
||||||
|
verbose = 1;
|
||||||
|
break;
|
||||||
|
case 'p': /* don't print a prompt */
|
||||||
|
emit_prompt = 0; /* handy for automatic testing */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Install the signal handlers */
|
||||||
|
|
||||||
|
/* These are the ones you will need to implement */
|
||||||
|
Signal(SIGINT, sigint_handler); /* ctrl-c */
|
||||||
|
Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */
|
||||||
|
Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */
|
||||||
|
|
||||||
|
/* This one provides a clean way to kill the shell */
|
||||||
|
Signal(SIGQUIT, sigquit_handler);
|
||||||
|
|
||||||
|
/* Initialize the job list */
|
||||||
|
initjobs(jobs);
|
||||||
|
|
||||||
|
/* Execute the shell's read/eval loop */
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
/* Read command line */
|
||||||
|
if (emit_prompt) {
|
||||||
|
printf("%s", prompt);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin))
|
||||||
|
app_error("fgets error");
|
||||||
|
if (feof(stdin)) { /* End of file (ctrl-d) */
|
||||||
|
fflush(stdout);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Evaluate the command line */
|
||||||
|
eval(cmdline);
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0); /* control never reaches here */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* eval - Evaluate the command line that the user has just typed in
|
||||||
|
*
|
||||||
|
* If the user has requested a built-in command (quit, jobs, bg or fg)
|
||||||
|
* then execute it immediately. Otherwise, fork a child process and
|
||||||
|
* run the job in the context of the child. If the job is running in
|
||||||
|
* the foreground, wait for it to terminate and then return. Note:
|
||||||
|
* each child process must have a unique process group ID so that our
|
||||||
|
* background children don't receive SIGINT (SIGTSTP) from the kernel
|
||||||
|
* when we type ctrl-c (ctrl-z) at the keyboard.
|
||||||
|
*/
|
||||||
|
void eval(char *cmdline)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parseline - Parse the command line and build the argv array.
|
||||||
|
*
|
||||||
|
* Characters enclosed in single quotes are treated as a single
|
||||||
|
* argument. Return true if the user has requested a BG job, false if
|
||||||
|
* the user has requested a FG job.
|
||||||
|
*/
|
||||||
|
int parseline(const char *cmdline, char **argv)
|
||||||
|
{
|
||||||
|
static char array[MAXLINE]; /* holds local copy of command line */
|
||||||
|
char *buf = array; /* ptr that traverses command line */
|
||||||
|
char *delim; /* points to first space delimiter */
|
||||||
|
int argc; /* number of args */
|
||||||
|
int bg; /* background job? */
|
||||||
|
|
||||||
|
strcpy(buf, cmdline);
|
||||||
|
buf[strlen(buf)-1] = ' '; /* replace trailing '\n' with space */
|
||||||
|
while (*buf && (*buf == ' ')) /* ignore leading spaces */
|
||||||
|
buf++;
|
||||||
|
|
||||||
|
/* Build the argv list */
|
||||||
|
argc = 0;
|
||||||
|
if (*buf == '\'') {
|
||||||
|
buf++;
|
||||||
|
delim = strchr(buf, '\'');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delim = strchr(buf, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
while (delim) {
|
||||||
|
argv[argc++] = buf;
|
||||||
|
*delim = '\0';
|
||||||
|
buf = delim + 1;
|
||||||
|
while (*buf && (*buf == ' ')) /* ignore spaces */
|
||||||
|
buf++;
|
||||||
|
|
||||||
|
if (*buf == '\'') {
|
||||||
|
buf++;
|
||||||
|
delim = strchr(buf, '\'');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delim = strchr(buf, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argv[argc] = NULL;
|
||||||
|
|
||||||
|
if (argc == 0) /* ignore blank line */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* should the job run in the background? */
|
||||||
|
if ((bg = (*argv[argc-1] == '&')) != 0) {
|
||||||
|
argv[--argc] = NULL;
|
||||||
|
}
|
||||||
|
return bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* builtin_cmd - If the user has typed a built-in command then execute
|
||||||
|
* it immediately.
|
||||||
|
*/
|
||||||
|
int builtin_cmd(char **argv)
|
||||||
|
{
|
||||||
|
return 0; /* not a builtin command */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* do_bgfg - Execute the builtin bg and fg commands
|
||||||
|
*/
|
||||||
|
void do_bgfg(char **argv)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* waitfg - Block until process pid is no longer the foreground process
|
||||||
|
*/
|
||||||
|
void waitfg(pid_t pid)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************
|
||||||
|
* Signal handlers
|
||||||
|
*****************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
|
||||||
|
* a child job terminates (becomes a zombie), or stops because it
|
||||||
|
* received a SIGSTOP or SIGTSTP signal. The handler reaps all
|
||||||
|
* available zombie children, but doesn't wait for any other
|
||||||
|
* currently running children to terminate.
|
||||||
|
*/
|
||||||
|
void sigchld_handler(int sig)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sigint_handler - The kernel sends a SIGINT to the shell whenver the
|
||||||
|
* user types ctrl-c at the keyboard. Catch it and send it along
|
||||||
|
* to the foreground job.
|
||||||
|
*/
|
||||||
|
void sigint_handler(int sig)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
|
||||||
|
* the user types ctrl-z at the keyboard. Catch it and suspend the
|
||||||
|
* foreground job by sending it a SIGTSTP.
|
||||||
|
*/
|
||||||
|
void sigtstp_handler(int sig)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* End signal handlers
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/***********************************************
|
||||||
|
* Helper routines that manipulate the job list
|
||||||
|
**********************************************/
|
||||||
|
|
||||||
|
/* clearjob - Clear the entries in a job struct */
|
||||||
|
void clearjob(struct job_t *job) {
|
||||||
|
job->pid = 0;
|
||||||
|
job->jid = 0;
|
||||||
|
job->state = UNDEF;
|
||||||
|
job->cmdline[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initjobs - Initialize the job list */
|
||||||
|
void initjobs(struct job_t *jobs) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXJOBS; i++)
|
||||||
|
clearjob(&jobs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* maxjid - Returns largest allocated job ID */
|
||||||
|
int maxjid(struct job_t *jobs)
|
||||||
|
{
|
||||||
|
int i, max=0;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXJOBS; i++)
|
||||||
|
if (jobs[i].jid > max)
|
||||||
|
max = jobs[i].jid;
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* addjob - Add a job to the job list */
|
||||||
|
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (pid < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXJOBS; i++) {
|
||||||
|
if (jobs[i].pid == 0) {
|
||||||
|
jobs[i].pid = pid;
|
||||||
|
jobs[i].state = state;
|
||||||
|
jobs[i].jid = nextjid++;
|
||||||
|
if (nextjid > MAXJOBS)
|
||||||
|
nextjid = 1;
|
||||||
|
strcpy(jobs[i].cmdline, cmdline);
|
||||||
|
if(verbose){
|
||||||
|
printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Tried to create too many jobs\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deletejob - Delete a job whose PID=pid from the job list */
|
||||||
|
int deletejob(struct job_t *jobs, pid_t pid)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (pid < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXJOBS; i++) {
|
||||||
|
if (jobs[i].pid == pid) {
|
||||||
|
clearjob(&jobs[i]);
|
||||||
|
nextjid = maxjid(jobs)+1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fgpid - Return PID of current foreground job, 0 if no such job */
|
||||||
|
pid_t fgpid(struct job_t *jobs) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXJOBS; i++)
|
||||||
|
if (jobs[i].state == FG)
|
||||||
|
return jobs[i].pid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* getjobpid - Find a job (by PID) on the job list */
|
||||||
|
struct job_t *getjobpid(struct job_t *jobs, pid_t pid) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (pid < 1)
|
||||||
|
return NULL;
|
||||||
|
for (i = 0; i < MAXJOBS; i++)
|
||||||
|
if (jobs[i].pid == pid)
|
||||||
|
return &jobs[i];
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* getjobjid - Find a job (by JID) on the job list */
|
||||||
|
struct job_t *getjobjid(struct job_t *jobs, int jid)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (jid < 1)
|
||||||
|
return NULL;
|
||||||
|
for (i = 0; i < MAXJOBS; i++)
|
||||||
|
if (jobs[i].jid == jid)
|
||||||
|
return &jobs[i];
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pid2jid - Map process ID to job ID */
|
||||||
|
int pid2jid(pid_t pid)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (pid < 1)
|
||||||
|
return 0;
|
||||||
|
for (i = 0; i < MAXJOBS; i++)
|
||||||
|
if (jobs[i].pid == pid) {
|
||||||
|
return jobs[i].jid;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* listjobs - Print the job list */
|
||||||
|
void listjobs(struct job_t *jobs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXJOBS; i++) {
|
||||||
|
if (jobs[i].pid != 0) {
|
||||||
|
printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
|
||||||
|
switch (jobs[i].state) {
|
||||||
|
case BG:
|
||||||
|
printf("Running ");
|
||||||
|
break;
|
||||||
|
case FG:
|
||||||
|
printf("Foreground ");
|
||||||
|
break;
|
||||||
|
case ST:
|
||||||
|
printf("Stopped ");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("listjobs: Internal error: job[%d].state=%d ",
|
||||||
|
i, jobs[i].state);
|
||||||
|
}
|
||||||
|
printf("%s", jobs[i].cmdline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/******************************
|
||||||
|
* end job list helper routines
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
|
||||||
|
/***********************
|
||||||
|
* Other helper routines
|
||||||
|
***********************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* usage - print a help message
|
||||||
|
*/
|
||||||
|
void usage(void)
|
||||||
|
{
|
||||||
|
printf("Usage: shell [-hvp]\n");
|
||||||
|
printf(" -h print this message\n");
|
||||||
|
printf(" -v print additional diagnostic information\n");
|
||||||
|
printf(" -p do not emit a command prompt\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* unix_error - unix-style error routine
|
||||||
|
*/
|
||||||
|
void unix_error(char *msg)
|
||||||
|
{
|
||||||
|
fprintf(stdout, "%s: %s\n", msg, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* app_error - application-style error routine
|
||||||
|
*/
|
||||||
|
void app_error(char *msg)
|
||||||
|
{
|
||||||
|
fprintf(stdout, "%s\n", msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Signal - wrapper for the sigaction function
|
||||||
|
*/
|
||||||
|
handler_t *Signal(int signum, handler_t *handler)
|
||||||
|
{
|
||||||
|
struct sigaction action, old_action;
|
||||||
|
|
||||||
|
action.sa_handler = handler;
|
||||||
|
sigemptyset(&action.sa_mask); /* block sigs of type being handled */
|
||||||
|
action.sa_flags = SA_RESTART; /* restart syscalls if possible */
|
||||||
|
|
||||||
|
if (sigaction(signum, &action, &old_action) < 0)
|
||||||
|
unix_error("Signal error");
|
||||||
|
return (old_action.sa_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sigquit_handler - The driver program can gracefully terminate the
|
||||||
|
* child shell by sending it a SIGQUIT signal.
|
||||||
|
*/
|
||||||
|
void sigquit_handler(int sig)
|
||||||
|
{
|
||||||
|
printf("Terminating after receipt of SIGQUIT signal\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,220 @@
|
||||||
|
make[1]: Entering directory `/afs/cs.cmu.edu/project/ics/im/labs/shlab/src'
|
||||||
|
./sdriver.pl -t trace01.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace01.txt - Properly terminate on EOF.
|
||||||
|
#
|
||||||
|
./sdriver.pl -t trace02.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace02.txt - Process builtin quit command.
|
||||||
|
#
|
||||||
|
./sdriver.pl -t trace03.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace03.txt - Run a foreground job.
|
||||||
|
#
|
||||||
|
tsh> quit
|
||||||
|
./sdriver.pl -t trace04.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace04.txt - Run a background job.
|
||||||
|
#
|
||||||
|
tsh> ./myspin 1 &
|
||||||
|
[1] (26252) ./myspin 1 &
|
||||||
|
./sdriver.pl -t trace05.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace05.txt - Process jobs builtin command.
|
||||||
|
#
|
||||||
|
tsh> ./myspin 2 &
|
||||||
|
[1] (26256) ./myspin 2 &
|
||||||
|
tsh> ./myspin 3 &
|
||||||
|
[2] (26258) ./myspin 3 &
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26256) Running ./myspin 2 &
|
||||||
|
[2] (26258) Running ./myspin 3 &
|
||||||
|
./sdriver.pl -t trace06.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace06.txt - Forward SIGINT to foreground job.
|
||||||
|
#
|
||||||
|
tsh> ./myspin 4
|
||||||
|
Job [1] (26263) terminated by signal 2
|
||||||
|
./sdriver.pl -t trace07.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace07.txt - Forward SIGINT only to foreground job.
|
||||||
|
#
|
||||||
|
tsh> ./myspin 4 &
|
||||||
|
[1] (26267) ./myspin 4 &
|
||||||
|
tsh> ./myspin 5
|
||||||
|
Job [2] (26269) terminated by signal 2
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26267) Running ./myspin 4 &
|
||||||
|
./sdriver.pl -t trace08.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace08.txt - Forward SIGTSTP only to foreground job.
|
||||||
|
#
|
||||||
|
tsh> ./myspin 4 &
|
||||||
|
[1] (26274) ./myspin 4 &
|
||||||
|
tsh> ./myspin 5
|
||||||
|
Job [2] (26276) stopped by signal 20
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26274) Running ./myspin 4 &
|
||||||
|
[2] (26276) Stopped ./myspin 5
|
||||||
|
./sdriver.pl -t trace09.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace09.txt - Process bg builtin command
|
||||||
|
#
|
||||||
|
tsh> ./myspin 4 &
|
||||||
|
[1] (26281) ./myspin 4 &
|
||||||
|
tsh> ./myspin 5
|
||||||
|
Job [2] (26283) stopped by signal 20
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26281) Running ./myspin 4 &
|
||||||
|
[2] (26283) Stopped ./myspin 5
|
||||||
|
tsh> bg %2
|
||||||
|
[2] (26283) ./myspin 5
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26281) Running ./myspin 4 &
|
||||||
|
[2] (26283) Running ./myspin 5
|
||||||
|
./sdriver.pl -t trace10.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace10.txt - Process fg builtin command.
|
||||||
|
#
|
||||||
|
tsh> ./myspin 4 &
|
||||||
|
[1] (26290) ./myspin 4 &
|
||||||
|
tsh> fg %1
|
||||||
|
Job [1] (26290) stopped by signal 20
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26290) Stopped ./myspin 4 &
|
||||||
|
tsh> fg %1
|
||||||
|
tsh> jobs
|
||||||
|
./sdriver.pl -t trace11.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace11.txt - Forward SIGINT to every process in foreground process group
|
||||||
|
#
|
||||||
|
tsh> ./mysplit 4
|
||||||
|
Job [1] (26298) terminated by signal 2
|
||||||
|
tsh> /bin/ps a
|
||||||
|
PID TTY STAT TIME COMMAND
|
||||||
|
25181 pts/3 S 0:00 -usr/local/bin/tcsh -i
|
||||||
|
26239 pts/3 S 0:00 make tshrefout
|
||||||
|
26240 pts/3 S 0:00 /bin/sh -c make tests > tshref.out 2>&1
|
||||||
|
26241 pts/3 S 0:00 make tests
|
||||||
|
26295 pts/3 S 0:00 perl ./sdriver.pl -t trace11.txt -s ./tsh -a -p
|
||||||
|
26296 pts/3 S 0:00 ./tsh -p
|
||||||
|
26301 pts/3 R 0:00 /bin/ps a
|
||||||
|
./sdriver.pl -t trace12.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace12.txt - Forward SIGTSTP to every process in foreground process group
|
||||||
|
#
|
||||||
|
tsh> ./mysplit 4
|
||||||
|
Job [1] (26305) stopped by signal 20
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26305) Stopped ./mysplit 4
|
||||||
|
tsh> /bin/ps a
|
||||||
|
PID TTY STAT TIME COMMAND
|
||||||
|
25181 pts/3 S 0:00 -usr/local/bin/tcsh -i
|
||||||
|
26239 pts/3 S 0:00 make tshrefout
|
||||||
|
26240 pts/3 S 0:00 /bin/sh -c make tests > tshref.out 2>&1
|
||||||
|
26241 pts/3 S 0:00 make tests
|
||||||
|
26302 pts/3 S 0:00 perl ./sdriver.pl -t trace12.txt -s ./tsh -a -p
|
||||||
|
26303 pts/3 S 0:00 ./tsh -p
|
||||||
|
26305 pts/3 T 0:00 ./mysplit 4
|
||||||
|
26306 pts/3 T 0:00 ./mysplit 4
|
||||||
|
26309 pts/3 R 0:00 /bin/ps a
|
||||||
|
./sdriver.pl -t trace13.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace13.txt - Restart every stopped process in process group
|
||||||
|
#
|
||||||
|
tsh> ./mysplit 4
|
||||||
|
Job [1] (26313) stopped by signal 20
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26313) Stopped ./mysplit 4
|
||||||
|
tsh> /bin/ps a
|
||||||
|
PID TTY STAT TIME COMMAND
|
||||||
|
25181 pts/3 S 0:00 -usr/local/bin/tcsh -i
|
||||||
|
26239 pts/3 S 0:00 make tshrefout
|
||||||
|
26240 pts/3 S 0:00 /bin/sh -c make tests > tshref.out 2>&1
|
||||||
|
26241 pts/3 S 0:00 make tests
|
||||||
|
26310 pts/3 S 0:00 perl ./sdriver.pl -t trace13.txt -s ./tsh -a -p
|
||||||
|
26311 pts/3 S 0:00 ./tsh -p
|
||||||
|
26313 pts/3 T 0:00 ./mysplit 4
|
||||||
|
26314 pts/3 T 0:00 ./mysplit 4
|
||||||
|
26317 pts/3 R 0:00 /bin/ps a
|
||||||
|
tsh> fg %1
|
||||||
|
tsh> /bin/ps a
|
||||||
|
PID TTY STAT TIME COMMAND
|
||||||
|
25181 pts/3 S 0:00 -usr/local/bin/tcsh -i
|
||||||
|
26239 pts/3 S 0:00 make tshrefout
|
||||||
|
26240 pts/3 S 0:00 /bin/sh -c make tests > tshref.out 2>&1
|
||||||
|
26241 pts/3 S 0:00 make tests
|
||||||
|
26310 pts/3 S 0:00 perl ./sdriver.pl -t trace13.txt -s ./tsh -a -p
|
||||||
|
26311 pts/3 S 0:00 ./tsh -p
|
||||||
|
26320 pts/3 R 0:00 /bin/ps a
|
||||||
|
./sdriver.pl -t trace14.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace14.txt - Simple error handling
|
||||||
|
#
|
||||||
|
tsh> ./bogus
|
||||||
|
./bogus: Command not found
|
||||||
|
tsh> ./myspin 4 &
|
||||||
|
[1] (26326) ./myspin 4 &
|
||||||
|
tsh> fg
|
||||||
|
fg command requires PID or %jobid argument
|
||||||
|
tsh> bg
|
||||||
|
bg command requires PID or %jobid argument
|
||||||
|
tsh> fg a
|
||||||
|
fg: argument must be a PID or %jobid
|
||||||
|
tsh> bg a
|
||||||
|
bg: argument must be a PID or %jobid
|
||||||
|
tsh> fg 9999999
|
||||||
|
(9999999): No such process
|
||||||
|
tsh> bg 9999999
|
||||||
|
(9999999): No such process
|
||||||
|
tsh> fg %2
|
||||||
|
%2: No such job
|
||||||
|
tsh> fg %1
|
||||||
|
Job [1] (26326) stopped by signal 20
|
||||||
|
tsh> bg %2
|
||||||
|
%2: No such job
|
||||||
|
tsh> bg %1
|
||||||
|
[1] (26326) ./myspin 4 &
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26326) Running ./myspin 4 &
|
||||||
|
./sdriver.pl -t trace15.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace15.txt - Putting it all together
|
||||||
|
#
|
||||||
|
tsh> ./bogus
|
||||||
|
./bogus: Command not found
|
||||||
|
tsh> ./myspin 10
|
||||||
|
Job [1] (26343) terminated by signal 2
|
||||||
|
tsh> ./myspin 3 &
|
||||||
|
[1] (26345) ./myspin 3 &
|
||||||
|
tsh> ./myspin 4 &
|
||||||
|
[2] (26347) ./myspin 4 &
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26345) Running ./myspin 3 &
|
||||||
|
[2] (26347) Running ./myspin 4 &
|
||||||
|
tsh> fg %1
|
||||||
|
Job [1] (26345) stopped by signal 20
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26345) Stopped ./myspin 3 &
|
||||||
|
[2] (26347) Running ./myspin 4 &
|
||||||
|
tsh> bg %3
|
||||||
|
%3: No such job
|
||||||
|
tsh> bg %1
|
||||||
|
[1] (26345) ./myspin 3 &
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26345) Running ./myspin 3 &
|
||||||
|
[2] (26347) Running ./myspin 4 &
|
||||||
|
tsh> fg %1
|
||||||
|
tsh> quit
|
||||||
|
./sdriver.pl -t trace16.txt -s ./tsh -a "-p"
|
||||||
|
#
|
||||||
|
# trace16.txt - Tests whether the shell can handle SIGTSTP and SIGINT
|
||||||
|
# signals that come from other processes instead of the terminal.
|
||||||
|
#
|
||||||
|
tsh> ./mystop 2
|
||||||
|
Job [1] (26359) stopped by signal 20
|
||||||
|
tsh> jobs
|
||||||
|
[1] (26359) Stopped ./mystop 2
|
||||||
|
tsh> ./myint 2
|
||||||
|
Job [2] (26362) terminated by signal 2
|
||||||
|
make[1]: Leaving directory `/afs/cs.cmu.edu/project/ics/im/labs/shlab/src'
|
Loading…
Reference in New Issue