This page last updated Thu Sep 27 14:10:56 PDT 2007
The program you will write has these requirements. It will be started with the command jshell [ prompt ]. If the optional prompt is not specified, the prompt will be "jshell: ". For example, invoking jshell with no argument, produces the following prompt
./jshell jshell:but invoking it with the argument cs170: produces
./jshell cs170: cs170:You will be provided a parser (in a separate file) that will read in one line of commands that will consist of executables and arguments and the tokens <, >, >>, &, and |. The parser will interpret these using c shell semantics and produces a list of programs to be executed and files for input and output. You will then create all of these processes with the proper redirection of input and output. The shell should exit when the user types CTRL-D or exit.
When this program is finished, you should be able to run a session that looks like this:
calvin:labs/cs170/jshell> jshell
jshell: pwd
/cs/student/msa/labs/cs170/jshell/
jshell: cat > f1
this is a test
once I had a girl on rocky top
jshell: cat f1 | sort
once I had a girl on rocky top
this is a test
jshell: sort < f1 | head -1 | cat -n
1 once I had a girl on rocky top
jshell: exit
calvin:labs/cs170/jshell>
A working for executable for Linux can be found at /cs/class/cs170/bin/jshell
CMD *commands; int error_code; error_code = parse(&commands);
The command struct is pretty straight forward. It looks like this:
typedef struct command_struct
{
struct command_struct * prevCmd;
struct command_struct * nextCmd;
char * file;
char ** argv;
char * in_redir;
char * out_redir;
int appending; /* flag, non-zero = append, zero = truncate */
int background; /* whether the line terminates with an & */
} CMD;
Each pipe-separated (|) executable command will have a structure like this that describes it. The file field is the executable to be run, and the argv field contains the NULL terminated argv that should be passed to the program. If there is an input or output redirect, the file name will contained in in_redir or out_redir respectively. The appending flag tells you if output should append to or truncate the output file, and the background flag tells whether the commands should be run in the background. The prevCmd and nextCmd fields point to the previous and next commands in the pipeline. Thus, the output of a command that is not the last command should go to nextCmd, and the input from any command but the first should come from prevCmd.
The other function in the file, int cmd_free(CMD *), is used to free the memory used by the command list. Use it to free every list generated by the parse() function. It would be wise to figure out the parser first before you work on the system calls. Any further question on the parser should be directed to the source code in parse.c
Input and output redirection, either to files or pipes, is done using the dup2() command. This command allows you redirect a file descriptor to point to another file descriptor. For this lab you will create a file descriptor using the open() or pipe() system call and then set stdin (0) or stdout (1) to point to the new descriptors. Then, when you call execvp(), the new process will read or write from the file or pipe thinking it is the standard input or output. Look at the headsort.c program for an example.
The shell must wait until all of the previously started programs complete unless the user runs them as background processes (with &). This is done with the wait() system call. Also, the shell will need deal with processes that become zombies. Zombie processes are processes that have exited, but the OS keeps them around until the parent (the shell) checks their exit status. You should use wait4() or waitpid() with the non-blocking option set to clean up any zombie processes before you start new processes.
All of these functions are system calls, which means they need to be tested to see if they fail when you use them. The man pages will tell you what the call should return on success (man 2 function). If there is an error you need to report it with the perror() function.