A simple command shell

Upload via Moodle as: shell.tar or shell208.c

As usual, you are welcome to work with a partner if you wish.

Goals

Rubric

Maximum score: 25 points (+ 3.0001 possible extra credit) === Basics === 1 - author name(s) in each of your source files 1 - support "help", printing a short summary of your shell's features 6 - simplest mode: print prompt, get one-word command from user, execute command (or print an error message), repeat until killed 4 - support single commands with command-line arguments 3 - code quality === Advanced === 4 - support "command1 | command2" (piping command1's stdout to command2's stdin) 4 - support "command > file" (redirecting command's stdout to a file) 1 - support "command < file" (redirecting command's stdin from a file) 1 - support "command &" (running command in the background) === Extra credit === 1 - maintain a command history and let the user cycle through previous commands using up-arrow and down-arrow 1 - support arbitrary sequences of pipes ("command1 | command2 |...") up to 16 commands and 15 pipes 1 - respond to signal SIGINT by terminating running child processes and a one-line message per process saying it has been terminated (see below for clarification) 0 - other features? not necessary, but go wild if you wish (and include them in your "help" statement) 0.0001 - come up with a clever-yet-friendly name for your shell

Background

When you interact with a Unix (or other similar) command prompt, you are interacting with a program known as a command shell. This might be bash in a WSL or Linux terminal, bash in the terminal in VS Code, zsh in a macOS Terminal, PowerShell in Windows, MS-DOS in a Windows cmd window, etc.

Though command shells are typically sophisticated programs with many features, and they often implement their own full-fledged programming languages (e.g., "bash scripting"), their flow of control is, at heart, conceptually very simple:

  1. print a prompt for the user
  2. read the user's command
  3. execute the command
  4. wait for the command to finish
  5. go to step 1

Your assignment

You will write a simple command shell, named shell208, that performs the most common operations that shells perform. Specifically, you will want your shell to be able to handle the styles of operation listed in the rubric above.

For a score of 23/25 (an excellent score), your shell should be able to handle commands involving single-word commands, commands with command-line arguments, pairs of command connected by a | ("pipe"), and redirection of standard output to a file. For example:

shell208$ help shell208$ ls shell208$ ls -l mysubdirectory shell208$ ps auxww | grep jondich shell208$ ls -l > listing.txt

For an additional challenge and up to 2 extra points, your shell can support redirecting stdin from a using < and letting commands execute in the background using &.

This scoring system is designed specifically to weight the most straightforward features most highly, since one of the key goals of the assignment is to make clear to you that process-manipulation programs can be quite simply structured.

I'm also offering a few extra credit points. These will be applied to your point total at the very end of the term, after I have assigned course grades. That is, there's no chance of your extra credit points affecting the grade boundaries for the class as a whole, but extra credit points offer a small chance of improving your course grade. But really, all this talk of grades is kind of beside the point—the "extra credit" features are mainly for fun, and to give you a chance to try to implement something more difficult if you have the time and inclination.

With that excessively wordy preamble, the extra credit features are: using up/down arrow keys to cycle through recent commands and re-execute them; implementing multi-command pipelines ("command1 | command2 | command3 |..."); and responding to a SIGINT signal by terminating the currently running child processes.

Submitting your work

Submit your program via Moodle in one of two forms.

Help

I have posted a collection of sample programs that demonstrate all of the essential techniques you will need to complete this assignment. You'll mostly be repurposing the sample programs and making sure the logic fits together.

I have posted:

A note about the Ctrl-C feature

Dealing with Ctrl-C in a shell is tricky. If you are running a process A in the foreground (i.e. without &), then the Ctrl-C will cause a SIGINT signal to be sent to process A. Unless A has registered a SIGINT handler, the signal will terminate A (and if A's parent process has called "wait", then wait will return in the parent, etc.).

But what if you are running a pipeline (e.g. "A | B")? Do both A and B receive a SIGINT signal? Do they both terminate? How might you find out? (You could, for example, write a couple simple programs of your own, include in them SIGINT handlers that print a message? That way you could experiment with the conditions under which the child and parent processes receive a SIGINT signal.)

An additional tricky part of this feature is that there is no standard way to iterate over the child processes of a Unix process. So even if you figured out a way to forward a SIGINT signal received by a child process to the parent process, the parent wouldn't be able to just walk through its children terminating each one in turn.

On the other hand, there are ways to examine the termination status of a child process when wait or waitpid returns information about a child to the parent. You can try to search for information on the internet for this.

So, I have moved this feature from the Advanced section of the rubric to the Extra Credit section, and I have left its specification relatively vague. If you want to explore your options for handling Ctrl-C, go ahead and implement something interesting. If you want to explain your choices, you can do so either in a readme file or in the comments in your source code.

Have fun!

This program is pretty cool once you get it working.