CSCI 340: Operating Systems
Fall 2007
Program 3: Command Shell
Code due: Midnight Thursday November 8
(see below)
Grading Weight: 4 (programs will have a weight between 1 - 5)

NOTE: if you turn the assignment in by November 1 (the original deadline) you get 15% extra credit.


Overview:

Write a command shell like the Bash shell.  Your program will display a prompt and execute commands the user types.

It must implement pipelines (the | operator in the Bash shell).  For example, your program will display a prompt ("$ ") and the user can enter a command of this form:


$ ls -l |  sort -k 4

In this example, your shell will create two processes, one to execute ls and one to execute sort.  The output of ls will be piped to the input of sort.

The shell must also handle file redirections (the < and > operators in the Bash shell).  For example,

$ sort < input_filename | head -5  > output_filename

You may work with one other student in the class.  However, you must work together (sit right next to each other) at least 90% of the time.  To do otherwise would be considered cheating.

Requirements:

The shell must continue to accept command lines until the user enters exit.  At this point the shell must terminate.

The shell must fork a new process for each command.  It must not fork any more processes than are needed.  The shell must call wait() on all the processes it creates (you can run into problems if you fail to do this).

The shell must be able to handle up to 100 commands piped together on a single line:

$ cmd1 | cmd2 | ... | cmd100

Each command must be able to accept up to 100 arguments:

$ cmd1 arg1 arg2 ... arg100 | cmd2 arg1 arg2 ... arg100 | ... | cmd100 arg1 arg2 ... arg100

Files can only be redirected into the first command.  Only output from the last command can be redirected to a file.

If no file is redirected into the first command, it should read from standard input.  If the output of the last command is not redirected to a file, it should be written to standard output.

Your shell must handle commands with full pathnames (e.g. /bin/ls) and commands without a full pathname (e.g. ls).  For the commands without a path look in every directory in the PATH environment variable until you find a match (just the bash shell). You must do this yourself, you may not call execvp() which does this for you.

Command names and arguments must be separated by a space on the command line.  However, the special characters (<, >, |) do not have to be separated by spaces.  The following two inputs should work the same:

$ sort<input_filename|head -5>output_filename
$ sort < input_filename | head -5  > output_filename

You do not have to recover from user errors.  If the user enters a command line that contains an error (such as a command that does not exist, or a file that cannot be opened) print an error message and exit your program.

You may implement this using either C or C++.  Use the .c extension for C files and .cpp for C++ files.
 The shell must compile using gcc if you use C or g++ if you use C++.

You must provide a Makefile that creates an executable named shell.  That is, when I type make in the directory that contains your files, an executable named shell will be created.

You must write this program from scratch.  Unlike the chat program, you may not borrow any code.  Using anyone else's code (including copying the code out of a book) will be considered cheating.  If you are working with someone and he or she cheats, you will also be charged with cheating.


Hints:

Check the value returned from ALL system calls.

The file pipes.cpp contains examples of most of the system functions you will have to call.  You will also need to call fopen() and fileno() (to implement redirection).

The system call getenv("PATH") will return a string containing the value of the PATH environment variable.  This string is a colon separated list of directories.  You can use the strsep() function to break it into separate tokens.  See strsep.c for an example of how to use it.


Be careful with fork().  If you have a bug in your code and fork() a large number of process, you will crash the machine.  If you are working on tiglon or jaguar everyone will be mad at you.  The following code illustrates a common mistake:

child_pid = fork();
if (child_pid == 0)
{
    cout << "hello from child" << endl;
    // should call exit() here
}
child2_pid = fork();
if (child2_pid == 0)
{
    cout << "hello from child 2" << endl;
    // should call exit() here
}

In the above code, both the parent and the first child call the fork() for child2.  Make sure that all children call exit() when they are done executing their code.

Since the special characters (<,>, |) do not have to have spaces before or after them, you need to preprocess the input line to add these spaces.  Once you add the spaces you can use strsep() to break it into tokens (see 
strsep.c).

The Unix command ps -lf -u username (where username is your username) will tell you all the process you have running.  You can kill one (or more) of these processes using the command kill -9 pid (where pid is one or more of the numbers in the PID column of the output of ps).

If you are desperate (processes forking faster then you can stop them), do the following (username is your username):

$ pkill -24 -u username
log in again because the above will have stopped the shell process you were using
$ pkill -9 -u username
log in again because the above will have killed the shell process you were using

Most students find this assignment very difficult.  It is surprisingly difficult to get everything straight, and it is a very very hard program to debug, when it does not work there are very few clues as to what is going wrong.

You must make sure you close all the pipes after the calls to dup2().  In other words, if you have 3 commands and create two pipes, all three processes have to close both sides of both pipes, and the parent must close both sides of both pipes (that is 12 calls to close).  If you forget a single call to close it can cause your program to hang.


Sample Test Cases:

Unix Filters

All the commands I use to test your program will work like this:

while (read(stdin, buf) == true)
{
process buf
write(stdout, buf)
}

-or-

while (read(stdin, buf) == true)
    ;
process all the input
write all the output to stdout


Many Unix commands use one of these two forms.  Examples of the first are: grep sed awk.  Examples of the second are: sort wc

Since all the commands will work the same, you don't need to think about what command I will use to test your program.

I will test the following aspects of your program:

connecting several commands together:    ls | sort

handling arguments:  /bin/ls -l | sort -r -k 7

file redirection:  ls | sort -r -k 7 | cat > foo

Knowing your program is correct:

You will know your program is correct if it has the same behavior as the bash shell, that is, type the same command to bash and your program.  If you see the exact same results, your program is correct.



How to turn in code:
Turn in all the files necessary to make your program work including a Makefile.  My solution contains the files:

shell.cpp
Makefile

Copy your files to the turn in directory for this assignment.

See How to turn in assignments for details on turning in assignments.

If you are working with someone, only turn in one copy of the assignment.  It is very helpful (but not mandatory) if people working together always turn their assignments in under the same name.  Make sure you list both names at the top of all files.




Late assignments:

You will lose 10% if your program is 1-24 hours late.
You will lose 20% if your program is 24-48 hours late.
Programs will not be accepted 48 hours past the deadline.

e-mail late assignments to me (avoid .zip files because they are usually removed by virus scanners).

It may take me longer to grade late assignments (if I have already graded the other assignments, grading late assignments gets a low priority).

I use the time I receive your e-mail, not the time you send your e-mail as the turn in time (sometimes e-mail from an ISP takes a day or two to be delivered).  If you want to be absolutely sure your assignment gets to me immediately, e-mail it from your csuchico or ecst account.