P0 - Build a Shell 🐚

The objective of this assignment is to build a UNIX style shell.

Outcomes:

What to submit:

Procedure:

  1. This assignment will guide you through the process of building a shell by iteratively improving your code and adding more features, starting with a very simple program

    1. At the end you should have a shell that mostly works
  2. You must make at least one commit per level completed, and label clearly at which commit you think you completed a particular level

  3. You must implement all ten levels listed below

    1. If you want to go further for an extra challenge, see the list of ideas at the bottom

Levels:

  1. The shell prints a prompt, which consists of the absolute path the shell is currently running in (see man 3 getcwd) followed by a $ and a space (e.g. /your/current/directory$). The shell then prints a new line and exits without any user interaction.

  2. The shell reads lines of user input, but doesn't do anything with them. It just prints a new prompt before each line. This loops until the shell gets EOF from user input (ctrl+d), at which point it exits with code 0.

  3. If the user types anything, the shell prints "Unrecognized command" (but does not exit the loop). However, if the user just hits enter without typing anything, no error message is printed.

  4. The shell splits the line of input into pieces delimited by whitespace characters (see man 3 isspace). Instead of just printing "Unrecognized command," the shell shall include the name of the program in the error message (e.g. if the user types cat shell.c, the shell prints "Unrecognized command: cat"). If the user enters only whitespace, no error message is printed.

  5. The shell supports a few builtin commands.

    exit takes no arguments and closes the shell (return value of 0). If arguments are provided, it prints an error message and does not exit the shell.

    cd takes exactly one argument and changes the working directory of the shell process to the provided path (see man 2 chdir). If an incorrect number of arguments are provided, it prints usage info. If chdir fails, an error message with a description of the errno (see man 3 warn) is printed.

    exec takes at least one argument and replaces the shell with an instance of the executable file whose path is provided as the first argument (see man 2 execve). It provides all its arguments as the argv for the called command, i.e. argv[0] is the first argument to exec, argv[1] is the second argument (if it exists), etc. If execve fails, an error message with a description of the errno is printed, and the shell continues running.

  6. The shell supports running executable files as commands within child processes. If the first piece of the input looks like a relative or absolute path (contains a /), a child process is created (see man 2 fork), and the command specified by the first argument is executed within the child using the provided arguments (see man 2 execve), similar to the exec builtin.

    If executing the command fails, the child process prints an error message including a description of the errno (don't forget to exit the child process). The shell waits for the child to finish running before printing the next prompt (see man 2 waitpid).

  7. In the case that the user types something that isn't a path or a builtin, before printing the unrecognized command error, the shell checks whether a file with that name exists in each of the directories listed in the PATH environment variable in order (see man 3 getenv and man 2 stat).

    If a file with that name is found, the search can stop and that file is executed with arguments in a child process. Only if no file is found in any of the directories should the unrecognized command error be printed.

    You must do the path searching manually, and cannot rely on a member of the exec family that does path searching automatically, such as execvp.

  8. Before processing the entered commands, the shell performs home directory substitution on the pieces (command name or arguments) that start with a ~. It should first determine a username string by taking a substring of the piece after the ~ until the end of the string or the first /, whichever comes first.

  9. As the shell is processing the commands and arguments, if it finds a < or >, it skips any whitespace characters and attempts to treat the next piece of the input as a filename for redirection. If there is no filename before the end of the string, an error message is printed.

    A command can have more than one redirection. If there are multiple of the same redirections, then the right-most one takes precedence. A redirection causes the shell to open the corresponding file and replace one (or both) of the child process' IO streams with the file descriptor of the open file (see man 2 dup).

  10. The shell supports the | (pipe) operator to chain multiple commands and their inputs and outputs. Each command separated by a | is spawned as its own child process. The shell creates a unidirectional pipe (see man 2 pipe) for each |, redirects the stdout from the left command to the writing end of the pipe, and redirects the stdin of the right command to the reading end of the pipe.

    Any file redirections specified by the user take precedence over the implied redirections from the |.

COMP.5170 ADDITIONAL REQUIREMENTS

For students in the graduate section of this course, you are expected to implement in patches 12-16+ at least five additional features from the list below. You may complete more than five for extra credit, and undergraduates may recieve extra credit for completing any number of them. In general, each feature should be contained in its own patch, and the patch should clearly document which feature is added.

Policies & Procedures


  1. This is because the glibc implementation of getpwnam will open a shared library with dlopen to try and find the specified user by other means if its search of /etc/passwd is fruitless. This is a known issue, and the leak can be safely ignored--but if it really bothers you, feel free to use musl libc instead. 


msg = (silence)
whoami = None
singularity v0.6-4-gbe148cb https://github.com/underground-software/singularity