Project II: Processes and the Command Line Shell

In this project you will write your own command line shell while practicing your knowledge of processes. The goals of this project are:
  • To learn the relationship between the kernel, the shell, and user-level programs.
  • To learn how to employ the Unix process management system calls.
  • To gain more experience in rigorous error handling.

Essential Requirements


You will write a program called myshell which is capable of executing, managing, and monitoring user level programs. This program will be similar in purpose and design to everyday shells like bash or tcsh, although the syntax will be slightly different. myshell will be invoked without any arguments, and will support several different commands.

Your program should print out a prompt like myshell> when it is ready to accept input. It must read a line of input, accepting several possible commands. The start command will start another program with command line arguments, print out the process number of the running program, and then accept another line of input (i.e., the program will run concurrently to the shell!). For example:

myshell> start emacs myfile.c
myshell: process 346 started
myshell> 
The wait command takes no arguments and causes the shell to wait until a process exits. When this happens, indicate whether the exit was normal or abnormal, along with the exit code or signal number and name, respectively. If there are no processes to wait for, print an appropriate message and then accept another line of input. For example:
myshell> wait
myshell: process 346 exited normally with status 0

myshell> wait
myshell: process 347 exited abnormally with signal 11: Segmentation fault.

myshell> wait
myshell: no processes left
The run command combines the behavior of start and wait. run should start a program, possibly with command line arguments, wait for that particular process to finish, and print the exit status. For example:
myshell> run date
Mon Jan 19 11:51:57 EST 2009
myshell: process 348 exited normally with status 0
The kill, stop, and continue commands take a process ID as an argument, and send the SIGKILL, SIGSTOP, and SIGCONT signals, respectively, to the indicated processes. Note that a process that is killed still requires a wait in order to collect the exit status. For example:
myshell> kill 349
myshell: process 349 killed

myshell> wait
myshell: process 349 exited abnormally with signal 9: Killed.

myshell> stop 350
myshell: process 350 stopped.

myshell> continue 350
myshell: process 350 continued
After each command completes, your program must continue to print a prompt and accept another line of input. The shell should exit with status zero if the command is quit or exit or the input reaches end-of-file. If the user types a blank line, simply print another prompt and accept a new line of input. If the user types any other command, the shell should print a reasonable error message:
myshell> bargle ls -la
myshell: unknown command: bargle

Your shell must accept inputs lines of up to 4096 characters, and must handle up to 100 distinct words on a line.

Technical Hints


You will need to read the manual pages for the following system and library calls at a minimum:
fork, execvp, wait, waitpid, kill, exit, printf, fgets, strtok, 
strcmp, strsignal, atoi
Use fgets to read one line of text after printing the prompt. Note that if you printf a prompt that has no newline on the end, it will not immediately display. Call fflush(stdout) to force the output.

Breaking the input line into separate words is a little tricky, but is only a few lines of code once you get it right. Call strtok(line," \t\n") once to obtain the first word, and then strtok(0," \t\n") repeatedly to get the rest, until it returns null. Declare an array of pointers char *words[100], then, for each word found by strtok, save a pointer to the word in words[i]. Keep track of the number of words as nwords, then set words[nwords] = 0; when you have found the last one.

Once you have broken the input line into words, you can check words[0] for the command name, use strcmp to check for string equality and atoi to convert a string to an integer.

Use fork and execvp as discussed in class to implement start. Use wait to implement wait, waitpid to implement run, and kill to implement kill, stop, and continue. Look up man 7 signal to get a list of signals and descriptions.

Make sure to stop if fgets returns null, indicating end-of-file. This allows you to run myshell and read commands from a file. For example, if you create myscript with the following contents:

start ls
wait
start date
wait
Then, you can run the shell on that input like this:
./myshell < myscript

Testing


Make sure to test your program on a wide variety of conditions. Try running multiple programs simultaneously. Create some simple programs that crash or exit with values other than zero, to make sure that wait and run report the correct exit status. Try running interactive program like emacs, and use stop, continue, and kill on it to see what happens.

Make sure to carefully handle all possible error conditions. Every system call can fail in a number of ways. You must cleanly handle all possible errors with a reasonable error message, as discussed in Project I. It is up to you to read the man pages carefully and learn what errors are possible.

Grading


Your grade will be based on:
  • Correct functioning of the shell according to the specification. (50%)
  • Correct handling of all possible error conditions. (40%)
  • Good coding style, including clear formatting, sensible variable names, and useful comments. (10%)

Turning In


Turn in a source file named myshell.c and a makefile to your dropbox directory, which is:
/afs/nd.edu/coursesp.16/cse/cse30341.01/dropbox/YOURAFSNAME/project2
This assignment is due at 5PM on Thursday, February 18th. Late assignments will not be accepted.

Your program will be compiled and graded on the Linux machines in the Fitzpatrick computer lab. Therefore, you should do your development work either sitting in that lab, or using ssh to connect to the machines remotely. The TA will hold office hours in the lab, and will be happy to help you with those machines.

If you insist on doing the homework on your personal computer or laptop, then you are on your own. Please do not ask the TA to fiddle around with your personal computers. Leave extra time to ensure that your program works correctly when transferred to the Fitzpatrick machines.

Extra Credit


For up to ten percent extra credit, write a second version of myshell that also implements redirection of standard input and output files. That is, if the user enters:
start sort <infile >outfile
then sort will use infile as its standard input file and outfile as its standard input file. Hint: after fork, but before exec, open the input and output files, and use dup2 to move the opened file descriptors to positions zero and one.

Turn in this version as myshell_extracredit.c.