Quash Shell  0.1
A simple yet powerfull shell program
Quash Shell Documentation

EECS 678 - Project 1 - Quash Shell

Introduction

In this project, you will complete the Quite a Shell (quash) program using the UNIX system calls. You may work in groups of 2. The purpose of this project is as follows:

A skeleton has been provided, but it lacks most of the core functionality one would expect from a shell program. Quash should behave similar to csh, bash or other popular shell programs.

Installation

To build Quash use:

make

To generate this documentation in HTML and LaTeX use:

make doc

To clean quash use:

make clean

Usage

To run Quash use:

./quash

or

make test

Features

The main file you will modify is src/execute.c. You may not use or modify files in the src/parsing directory, with the notable exception of the destroy_parser() function in src/parsing/parse_interface.c if necessary. Your output should match our example result files exactly.

The following features should be implemented in Quash:

1 [QUASH]$ program1 &
2 Background job started: [1] 2342 program1 &
3 [QUASH]$ ls
4 Documents Downloads
5 Completed: [1] 2342 program1 &
1 [QUASH]$ echo Hello Quash! > a.txt # Write "Hello Quash!\n" to a file
2 [QUASH]$ cat a.txt
3 Hello Quash!
4 [QUASH]$ echo Hey Quash! > a.txt # Truncate the previous contents of a.txt and write "Hey Quash!\n" to the file
5 [QUASH]$ cat a.txt # Print file contents. If we didn't actually truncate we would see "Hey Quash!h!\n" as the output of this command.
6 Hey Quash!
7 [QUASH]$ cat < a.txt # Make cat read from a.txt via standard in
8 Hey Quash!
9 [QUASH]$ cat < a.txt > b.txt # Multiple redirect. Read from a.txt and write to b.txt.
10 [QUASH]$ cat b.txt
11 Hey Quash!
12 [QUASH]$ cat a.txt >> b.txt # Append output of a.txt to b.txt
13 [QUASH]$ cat b.txt
14 Hey Quash!
15 Hey Quash!
16 [QUASH]$
1 [QUASH]$ cat src/quash.c | grep running
2 // Check if loop is running
3 bool is_running() {
4  return state.running;
5  state.running = false;
6  while (is_running()) {
7 [QUASH]$ cat src/quash.c | grep running | grep return
8  return state.running;

Built-in Functions

All built-in commands should be implemented in quash itself. They cannot be external programs of any kind. Quash should support the following built-in functions:

1 [QUASH]$ echo Hello world! 'How are you today?'
2 Hello world! How are you today?
3 [QUASH]$ echo $HOME/Development
4 /home/jrobinson/Development
5 [QUASH]$
1 [QUASH]$ export PATH=/usr/bin:/bin # Set the PATH environment variable
2 [QUASH]$ echo $PATH # Print the current value of PATH
3 /usr/bin:/bin
4 [QUASH]$ echo $HOME
5 /home/jrobinson
6 [QUASH]$ export PATH=$HOME # Set the PATH environment variable to the value of HOME
7 [QUASH]$ echo $PATH # Print the current value of PATH
8 /home/jrobinson
9 [QUASH]$
1 [QUASH]$ echo $PWD
2 /home/jrobinson
3 [QUASH]$ cd .. # Go up one directory
4 [QUASH]$ echo $PWD
5 /home
6 [QUASH]$ cd $HOME # Go to path in the HOME environment variable
7 /home/jrobinson
8 [QUASH]$
1 [QUASH]$ pwd # Print the working directory
2 /home/jrobinson
3 [QUASH]$ echo $PWD # Print the PWD environment variable
4 /home/jrobinson
5 [QUASH]$ export PWD=/usr # Change the PWD environment variable
6 [QUASH]$ pwd
7 /home/jrobinson
8 [QUASH]$ echo $PWD
9 /usr
10 [QUASH]$
1 [BASH]$ ./quash
2 Welcome...
3 [QUASH]$ exit
4 [BASH]$ ./quash
5 Welcome...
6 [QUASH]$ quit
7 [BASH]$
1 [QUASH]$ find -type f | grep '*.c' > out.txt &
2 Background job started: [1] 2342 find / -type f | grep '*.c' > out.txt &
3 [QUASH]$ sleep 15 &
4 Background job started: [2] 2343 sleep 15 &
5 [QUASH]$ jobs # List currently running background jobs
6 [1] 2342 find / -type f | grep '*.c' > out.txt &
7 [2] 2343 sleep 15 &
8 [QUASH]$

Useful Functions in the Quash Skeleton

The following are some funtions outside of src/execute.c that you may want to use in your implementation:

Useful System Calls and Library Functions

The following is a list and brief description of some system calls and library functions you may want to use and their respective man page entries. Note that this list may not be exhaustive, but be sure what ever library functions you use will run on the lab machines:

You may NOT use the system(3) function anywhere in your project

Project Hints and Comments

In Quash, a job is defined as a single command or a list of commands separated by pipes. For example the following are each one job:

cat file.txt # A job with a single process running under it

find | grep *.qsh # A job with two processes running under it

A job may contain more than one process and should have a unique id for the current list of jobs in Quash, a knowledge of all of the pids for processes that run under it, and an expanded string depicting what was typed in on the command line to create that job. When passing the pid to the various print job functions you just need to give one pid associated with the job. The job id should also be assigned in a similar manner as bash assigns them. That is the job id of a new background job is one greater than the maximum job id in the background job list. Experiment with background jobs in Bash for more details on the id assignment.

The structure of the functions in src/execute.c will be explained in the following paragraphs.

The entry level function for execution in quash is run_script(). This function is responsible for calling create_process() on an array of CommandHolders. After all of the processes have been created for a job, run_script() should either wait on all processes inside a foreground job to complete or add it to the background job list without waiting if it is a background job.

The create_process() function is intended to be the place where you fork processes, handle pipe creation, and file redirection. You should not call execvp(3) from this function. Instead you should call derivatives of the example_run_command() function. Also you can determine whether you should use the boolean variables at the top of this function to determine if pipes and redirects should be setup. It may be necessary to keep a global execution state structure so that different calls to create process can view important information created in previous invocations of create_process() (i.e. the file descriptors for open pipes of previous processes).

When implementing the run_<command type> functions in src/execute.c, the command structures usually hold everything needed to pass to the corresponding function calls. For example, the GenericCommand structure contains an args field fully formatted and ready to pass into the argv argument of the execvp(3) library function. The one exception to this rule is run_cd(). In the CDCommand structure, the field dir is the path that the user typed. This needs to be expanded to an absolute path with realpath(3) before you use it.

You should not have to search for environment variables ($) in your functions. The parser uses your implementation of lookup_env() function to expand the environment variables for you.

Testing

There is an automated testing script written for this project in run_tests.bash. Using the "make test" target in the make file will run the ./run-tests.bash command present in the Makefile. If you want more control you may run this script directly from the command line with "./run_tests.bash [-cdstuv]". The various options are listed below or with "./run_tests.bash -h".

Grading Policy

Partial credit will be given for incomplete programs. However, a program that cannot compile will get 0 points. The feature tests are placed into multiple tiers of completeness. The output to standard out from your code must match our output exactly, except for whitespace, for the next tier of grading to be accessible. This is due to reliance of previous tiers in subsequent tier tests. If we cannot run your code in one tier then it becomes far more difficult test later tiers. The point breakdown for features is below:

Description Score
  • Tier 0
    • Quash compiles
10%
  • Tier 1
    • Single commands without arguments (ls)
    • Simple built-in commands
      • pwd
      • echo with a single argument
30%
  • Tier 2
    • Single commands with arguments (ls -a /)
    • Built-in commands
      • echo with multiple arguments
      • cd
      • export
    • Environment Variables
      • echo with environment variables (echo $HOME)
      • Execution with environment variables (du -H $PWD/..)
30%
  • Tier 3
    • Built-in commands
      • jobs
      • kill
    • Piping output between one command and another (find -type f | grep '*.c')
    • Redirect standard input to any command from file (cat < a.txt)
    • Redirect standard output from a command to a file (cat b.txt > a.txt)
    • Background processes
      • Job completion notification
30%
  • Tier 4 (extra credit)
    • Pipes and redirects can be mixed (cat < a.txt | grep -o World | cat > b.txt)
    • Pipes and redirects work with built-in commands
    • Append redirection (cat a.txt | grep -o World >> b.txt)
10%
  • Valgrind Memory Errors
    • While not ideal, you will not lose any points for "still reachable" blocks
    • Unacceptable Memory Leaks
      • Definately lost
      • Indirectly lost
      • Possibly lost
    • Unacceptable Access Violations
      • Invalid Read
      • Invalid Write
      • Invalid free
      • Use of uninitialized values
-5% from tier grade down to 0% for each tier with violations

Submission

Each group should submit the project to your TA via Blackboard. Create a zip file of your code using "make submit". You should also check that the zipped project still builds and runs correctly after building the submit target with "make unsubmit". Your TA will be using this command to extract and build your project so make sure it works correctly. If you modify either of these targets, please ensure all file extensions are renamed to ".txt" in the submission and extract correctly with the unsubmit target.

Miscellaneous

Start early! You need to use C language to implement this project.