CSC 357 Programming Assignment 6
vssh -- A Very Simple Shell
In this assignment you are implementing a very simple shell program named "vssh". The vssh shell provides the following functionality:
Like other UNIX shells, vssh is an interactive program that allows the user to execute other programs. When vssh is executed with no argument, it outputs the prompt "%- " and awaits user commands. Details of the commands follow.
Vssh can accept a single command-line argument for executing a file in batch mode. This form of execution is described further below.
Vssh can itself be invoked with redirected input and/or output, as in
vssh < infile > outfilewhere it accepts input commands from infile and sends its standard output to outfile. When vssh accepts redirected input, it processes the commands in the given file, and then exits without prompting for any interactive commands. When vsh has its output redirected, it enters a command loop, but does not print a prompt. Hence, vssh prints a prompt only if both stdin and stdout are a terminal device.
Vssh executes programs in the same way that other UNIX shells do. The program name can be an absolute path, relative path, or single program name. If the latter, vssh searches the PATH environment variable for the program. Note that vssh does not recognize the tilde character as anything special in a path, i.e., '~' at the beginning of a path is NOT recognized as the user's home directory, just as regular character in the path name.
The program must exist and have its execute permission set for the current user. If the program does not exist on the current user's path, vssh outputs the error message
prog: Command not foundwhere "prog" is the name of the program. If the program exists but is not executable, vssh outputs the error
prog: Permission denied.If any other error occurs when the program is invoked with exec, vssh outputs what comes from perror for the failed call to exec.
As with other shells, vssh executes binary executables as such, or non-binary executables using the interpreter specified in the first line "#!" declaration, or using vssh if no "#!" appears in the first line of the program.
Vssh accepts zero or one command-line argument. Without an argument, vssh enters a command-input loop, with the prompt character "%- ". With a command-line argument, vssh interprets the argument as a file containing vssh commands. After interpreting the file, vssh exits, without entering its command loop.
If the command-line argument does not exist as a file, vssh outputs the error
arg: Command not found.where "arg" is the command-line argument. Vssh ignores any arguments beyond the first, without printing any error message regarding the excess arguments.
Vssh performs input/output redirection in the same manner as other shells. If a redirected input file does not exist, vssh outputs the error
file: No such file or directory.where "file" is the name of the redirected file. If a redirected output file exists, it is overwritten by the redirected output. If a redirected output file does not exist, it is created and written. If a redirected output file is not writable, vssh outputs the error:
file: Permission denied.
Vssh handles pipes in the same manner as other shells. If a program in a pipeline does not exist or is not executable, vssh outputs the appropriate error message, and the entire pipeline is terminated immediately.
Normally, vssh waits for an executing program to terminate before outputting its next prompt. If a vssh command line ends in '&', vssh executes the command in the background, and outputs its next prompt without waiting for the program to finish.
The command preceding the '&' can have input/output redirection, but cannot be a pipeline. If it is a pipeline, vssh outputs the error:
Pipelines cannot be backgrounded.and does not execute anything.
Syntactically, the '&' must be preceded by whitespace, otherwise it is considered to be part of the command name. If any non-whitespace characters follow the '&', vssh outputs the error
Junk after '&'.and does not execute anything.
Backgrounded jobs that remain executing concurrently with vssh can be listed with the jobs command described below.
When vssh is waiting for a program to complete execution, the program can be interrupted (and thereby terminated) by typing the Control-C character on the terminal. When the program is terminated, vssh prints its next prompt.
An executing program can be suspended by typing the Control-Z character. A suspended program can be resumed with the fg or % commands described below. Suspended programs can be listed using the jobs command, described below.
The history command described below lists previously-executed vssh commands. Any one of the listed commands can be re-executed using the command "! job", where job is the number of the command listed in the history.
When vssh re-executes a command, it first echos the command, then performs it, then outputs the next prompt.
If the job number does not appear in the history list, or is not a number, vssh outputs the error:
job: Event not found.where job is the value following the !.
Syntactically, the job number can follow the ! with zero or more intervening whitespace characters.
The built-in vssh commands are not executable programs, but specific commands executed internally by vssh. The reason these commands cannot be executed as external programs is because they require some form of special processing. For example, cd cannot be executed as a program, because it would change the working directory of the child process, but not vssh itself.
None of the built-in commands can involve redirection, be part of a pipeline, or be backgrounded. That is, a built-in command must be the first and only command on the line. If any user inputs follows a syntactically legal built-in command, vssh outputs the error
Junk after built-in command.and does not execute the command. If a built-in command appears anywhere in the middle of a pipeline, or as the argument of a redirection, vssh assumes the command is the name of an executable program or file, as appropriate. E.g., the vssh command
ls cd > bgruns the ls program on a file or directory named "cd", sending the output to a file named "bg".
Details of each built-in command follow.
The cd command changes the current working directory of vssh. All subsequent commands are relative to the changed-to path.
If there are no suspended jobs, the exit command echos the "exit" command name then exits vssh . If there are suspended jobs, vssh outputs the message:
There are suspended jobs.without exiting. If the user executes immediately executes another exit command, without any other intervening commands, then vssh terminates all suspended jobs and exits.
The jobs command lists all backgrounded and suspended jobs, in the format:
[1] command1where the commandi are the commands that invoked the backgrounded or suspended programs.
[2] command2
...
[n] commandn
The order of the jobs list is based on the chronological order of its "job time". For a backgrounded job initially invoked with '&', its job time is its invocation time. For a job initially invoked without '&', but subsequently suspended with control-Z, its job time is the time the control-Z was issued.
The next section on the kill commands describes the effect of killing on the order of the jobs list.
The bg command backgrounds the most recently suspended command. fg foregrounds the most recently backgrounded command. Foreground means having the command executing such that vssh is waiting for it, as if the command had been started without being backgrounded.
The %n command foregrounds the backgrounded job n. If n is not one of the job numbers listed by jobs or is not a number, vssh outputs the error
n: No such job.
The kill command takes a job number in the form "%n", or a plain integer n. In the "%n" case, vssh kills the specified job, or outputs the "No such job." message if n is not one of the job numbers listed by jobs or is not an integer. When the argument to kill is a plain integer, vssh invokes "/bin/kill n" to kill the job. Any error subsequent messages are up bin/kill.
When a job is killed, there is a "hole" in the jobs list, if the killed job is
not the last in the list. E.g., suppose the jobs list looks like this:
and then a "kill %3" command is issued. The next jobs command output looks like this:[1] progA [2] progB [3] protC [4] progD
If a new job is invoked in the background or suspended, any holes in the jobs list are filled. E.g., if the user invokes progE in the immediately preceding jobs state, the next jobs list looks like this:[1] progA [2] progB [4] progD
[1] progA [2] progB [3] progE [4] progD
The history command outputs a list of up to 100 most recently executed commands, in the format:
i commandiwhere i = 1 if n <= 100, or i = n - 100 otherwise.
i+1 commandi+1
...
n history
The command !k re-executes the kth command from the history. If k is not listed in the history or is not a number, vssh outputs the error
k: Event not found.
There will be an executable version of vssh on hornet in
~gfisher/classes/357/programs/6/testing/vsshRun this program to see how it performs the commands specified above.
(The program will be available by Friday, 26 May.)
The work you've done in the most recent program and lab has involved the use of fork and exec that you'll be using in the implementation of vssh. The examples discussed in the week 8 and 9 lecture notes show the use of other system calls you'll be using for vssh.
The following C library functions may be particularly useful in your
implementation. You can read about these in Stevens and the man pages.
Function Description fork and exec you've been working with these recently sigaction you've worked with this also; note that you may want to use the extended form of signal handler that takes a sininfo parameter, for dealing with programs that send SIGTSTP to vssh kill send a signal to a process waitpid you've worked with this in recent assignments and labs; note utility of WNOHANG option for use in job control pipe you've worked with this in the most recent lab isatty indicate if a file descriptor is associated with a terminal
You can use the parser as-is, for performing the syntax analysis of vssh command lines, including the handling of syntax error messages.357/programs/6/parser
The parser has a number of .c and .h files, plus a Makefile.
A listing of the key .h file is attached to the end of this writeup.
The Makefile compiles the .o files that you can include with your
implementation of vssh. It also builds a testing program named
parse-cl that parses a command line and dumps the data structure it
builds.
Here are some additional points to consider for your vssh
implementation:
Submit a set of .c and .h files plus a Makefile that
compiles your program into an executable named vssh.
The testing plan file in the 357 program/6 directory has the precise
point breakdown.
NO collaboration is allowed on this assignment. Everyone must do their own
individual work.
Submit your deliverables using the handin program on hornet. For Section 1 of 357, the command is
For Section 2, it'shandin gfisher prog6-s1 ... Makefile
where "..." are your program files.handin gfisher prog6-s2 ... Makefile
Note that handin does not support the hand-in of directories. If your
program is structured to use the supplied parser files from a parser
subdirectory, your Makefile must create this subdirectory and move the parser
files into it before compiling. Given this, it's easier just to work with the
parser files in the same directory as your own files.
Follows on the next page (in the paper version of this writeup).
1 /** 2 * This file defines a pipeline data structure that represents a shell command 3 * line. A pipeline is a list of command-line stages, defined by the clstage 4 * structure. A stage contains the name of a command, its redirected input, 5 * redirected output, argc, and argv. 6 * 7 * To see what a pipeline looks like for a shell command, run the parse-cl 8 * program. It will dump the pipeline structure for each command line input 9 * after the "what? " prompt. 10 */ 11 12 #ifndef PIPELINE 13 #define PIPELINE 14 15 #include <stdio.h> 16 #include <sys/types.h> 17 #include "stringlist.h" 18 19 typedef struct clstage *clstage; 20 21 struct clstage { 22 char *inname; /* input filename (or NULL for stdin) */ 23 char *outname; /* output filename (NULL for stdout) */ 24 int argc; /* argc and argv for the child */ 25 char **argv; /* Array for argv */ 26 27 clstage next; /* link pointer for listing in the parser */ 28 }; 29 30 typedef struct pipeline { 31 char *cline; /* the original command line */ 32 int length; /* length of the pipeline */ 33 struct clstage *stage; /* descriptors for the stages */ 34 } *pipeline; 35 36 37 /* prototypes for pipeline.c */ 38 extern void print_pipeline(FILE *where, pipeline cl); 39 extern void free_pipeline(pipeline cl); 40 extern pipeline parse_pipeline(char *line); 41 extern clstage make_stage(slist l); 42 extern void free_stage(clstage s); 43 extern void free_stagelist(clstage s); 44 extern clstage append_stage(clstage s, clstage t); 45 extern pipeline make_pipeline(clstage stages); 46 extern int check_pipeline(pipeline pl, int lineno); 47 48 #endif