CS 31 Lab 8: The Shell

Due Tuesday, April 24, before midnight


Here are the partnerships and here's a link to GitHub.

Contents


Goals


Overview

A shell is a program that you use to run other programs. We've become accustomed to using the bash shell in our terminal windows, but there are many other shell programs, each of which tries to make it easy for its users to run programs. bash may now seem to you like the one true way of doing things, but if you stop to think about it I bet you will quickly think of ways you could improve bash. For this lab, we will build our own shell programs. Your shell probably won't be an improvement over bash (at least not yet) but it will be a full-featured programming environment that's actually useable in practice.

The main job of your shell program is to do the following:

  1. Print a prompt (like the $ in bash).
  2. Wait for the user to enter a command.
  3. Run the command.
  4. Repeat.

Most commands correspond to a program that the user wants to run. The first part of such a command is the name of the binary executable file for the program. Some executables (like ls, vim, or make) can be referenced without needing to specify the directory where they live. With others (like the programs we've written this semester) we must specify the path to the executable, either relative to the current directory (./myprogram) or in absolute terms (/home/mauskop/cs31/myprogram). Running programs is easier than you might think. We just need to fork a new process and call execvp with the name of or path to the executable. If there are command line arguments we also must pass these to execvp.

There are a small number of commands that don't correspond to programs, but instead are implemented in the shell itself. Your shell should have four of these built-in commands:

  1. exit to terminate the shell program.
  2. cd to change the current directory.
  3. history to see a history of recent commands, labeled with consecutive numeric ids.
  4. !n to run a command from the history. The n should be replaced with the numeric id of the command you want to run.

In addition, your shell should allow its users to chose whether a program will run as a foreground process or background process. Running in the foreground is the default behavior. It means that the user can't invoke any additional commands until the process has exited. Running in the background means that the user can continue entering commands while the background process runs. We'll use the same convention as bash, and indicate a background process by ending the command line with &.


Details

Your starting repository contains the following C files:

Read over all these files with your partner, but only modify the code in cs31shell.c and circularqueue.c. You should also modify circularqueue.h by adding comments.

Built-in commands should be impemented right in cs31shell.c. If the user enters a command that isn't a built-in, assume the command is the name of an executable. Fork a child process and use execvp to run the executable in this child. If it's running in the foreground, the parent process (i.e. the shell) should wait for the child to exit before proceeding. If the user indicates that the executable should run in the background, the parent can proceed immediately, but when the child exits, it must be reaped in a signal handler. You should handle execvp errors, which usually indicate that the user mistyped the name of the executable. See the lecture slides for more on fork, execvp, signal, and waitpid.

Here is an example run of a working shell program. Consult this and bash itself to get a sense of how your shell should behave in various circumstances.


Requirements


Tips


Extra Challenge

Implement the parsecommand library yourself in a file called parsecommand.c. Modify the Makefile so it compiles the shell using your source code instead of the publicly available parsecommand.o file. You can implement this library however you like as long as your shell program is still robust.

Our implementation uses a single statically-allocated global character array (of size MAX_COMMAND_SIZE) to store a copy of the command line. It selectively replaces whitespace characters with null terminating characters (i.e. '\0') within this character array. This allows us to store multiple strings in a single array.

If you do the extra challenge please indicate this in the questionnaire.