BigShell Specification

In this assignment, you will explore many important process concepts by implementing a POSIX-like shell program. Your program will:

  1. Parse command-line input into commands to be executed

  2. Execute a variety of external commands (programs) as separate processes

  3. Implement a variety of shell built-in commands within the shell itself

  4. Perform a variety of i/o redirection on behalf of commands to be executed

  5. Assign, evaluate, and export to the environment, shell variables

  6. Implement signal handling appropriate for a shell and executed commands

  7. Manage processes and pipelines of processes using job control concepts

Learning Outcomes

  • Describe the Unix process API (Module 3, MLO 2)

  • Write programs using the Unix process API (Module 3, MLO 3)

  • Explain the concept of signals and their uses (Module 3, MLO 2)

  • Write programs using the Unix API for signal handling (Module 3, MLO 3)

  • Explain I/O redirection and write programs that can employ I/O redirection (Module 3, MLO 4)

Overview

You will write a program, named BigShell, that implements a large subset of the POSIX Shell’s functionality.

The high-level functionalities you will be tasked with implementing are:

  • The following built-in shell utilities:

    • cd – Change the working directory

    • exit – Cause the shell to exit

    • unset – unset values of variables

  • Signal handling behavior of the shell, and commands

  • Command execution

    • Expansion of:

      • Command words

      • Assignment values

      • Redirection filename operands

    • I/O Redirection of the following operators:

      • >

      • <

      • <>

      • >>

      • >|

      • >&

      • <&

    • Variable assignment

    • Simple commands:

      • Foreground commands – ;

      • Background commands – &

    • Shell pipelines – |

    • Job control

Reference Documents

The overall assignment is loosely based on the shell command language specification in POSIX.1-2008, with several features removed or simplified.

Builtins

  • cd(1)

    • chdir(3)

  • exit(1)

    • exit(3)

    • strtol(3)

  • unset(1)

    • getenv(3)/setenv(3)

Signal Handling

  • sigaction(3)

  • kill(2)

  • signal.h(0)

  • signal(7)

I/O Redirection

  • dup(2)

  • fcntl(3)

  • open(2)

Foreground/Background Commands

  • wait(2)

Pipelines

  • pipe(2)

Job Control

  • isatty(3)

  • setpgid(3)/setpgrp(3)

  • getpgid(3)/getpgrp(3)

  • tcsetpgrp(3)/tcgetpgrp(3)

General Purpose

  • ctype.h(0)

  • errno.h(0)

Command Language

Discussion

This program is intended to be a fun project for you to learn fundamental process concepts while also building a featureful program to show off as your portfolio project. Writing a shell is very challenging–the language is difficult to parse, the specification leaves out many important details, and is often ambiguously worded, and the size of a shell’s code-base is very large. Accomplishing this on your own is a huge feat!

Neither the provided skeleton code, nor reference implementation, are 100% bug free–in fact, I have found several bugs while writing this documentation after having finished the published implementation. Similarly, your program is not expected to handle every edge case or work perfectly in every situation. There may be aspects of the specification which are ambiguous, and edge cases which produce unexpected results. You are always encouraged to use your best judgement in approaching ambiguities, and if in doubt, ask staff for guidance.

The specification is intended merely as a guideline. Unless explicitly tested by the grading rubric, any other aspects of the program, specified or not, are up to you. The TODOs listed in the skeleton code should serve as a road-map for completion of the assignment. Each TODO should explain in detail what you need to implement in order to get to the same functionality as the reference implementation.

The suggested order of implementation is:

  • Built-in Commands

  • Non-built-in commands

  • Foreground/Background process waiting

  • Redirection

  • Pipelines

  • Variable Assignment

  • Signal Handling

  • Job Control Functionality

Constraints

  1. Your program must compile to the release build target with the provided makefile.

Testing Your Program

A reference implementation is provided with the assignment skeleton code. It has been tested to run in the GitHub Codespaces environment and scores full points on the grading script. It is likely to also run on any recent Linux distribution.

Rubric

The example expected test output listed below should serve as a guide. You are encouraged to change variable names, try more complex constructions, and different commands. A rubric item may not be tested exactly as shown, but gives a general idea of what to expect–we won’t try and surprise you or throw you any curve-balls.

Built-in Commands [20%]

  1. Does the exit built-in work appropriately? [5%]

    bigshell$ exit 123
    bash$ echo $?
    123
    
    bigshell$ bash -c 'exit 123'   # Set the $? variable to 123
    bigshell$ exit
    bash$ echo $?
    123
    
  2. Does the cd built-in work appropriately? [5%]

    $ echo $HOME
    /home/bennybeaver
    $ cd
    $ pwd
    /home/bennybeaver
    $ cd /bin
    $ pwd
    /bin
    
  3. If the HOME variable is modified, does the cd utility respond appropriately? [5%]

    $ cd
    $ pwd
    /home/donnyduck
    $ HOME=/home/bennybeaver   # Supposing that /home/bennybeaver is a directory that exists
    $ cd
    $ pwd
    /home/bennybeaver
    
  4. Does the unset built-in work appropriately? [5%]

    $ echo $HOME
    /home/bennybeaver
    $ unset HOME
    $ echo $HOME
    
    $ X=12
    $ echo $X
    12
    $ unset X
    $ echo $X
    
    $
    

Parameters and Variables [24%]

  1. If a variable is set with no command words, does it persist as an internal (not exported) shell variable? [3%]

    $ unset X       # Ensure X isn't set yet
    $ X=123
    $ echo $X       # Show that X is expanded
    123
    $ printenv X    # Show that X is not exported
    $
    
  2. If a variable is exported with the export utility, does a child process that is executed have that variable set in its environment? [3%]

    $ unset X
    $ X=123
    $ export X
    $ printenv X
    123
    
  3. If a variable is set as part of a command, does the child process that is executed have that variable set in its environment (and only its environment)? [3%]

    $ printenv X        # Show that X is unset in the shell
    $ X=123 printenv X  # Show that X is added to the environment of the printenv command
    123
    $ printenv X        # Show that X remains unset in the shell
    $
    
  4. If a foreground command is executed, does the $? special parameter get updated appropriately? [3%]

    $ true
    $ echo $?
    0
    $ false
    $ echo $?
    1
    $ sh -c 'exit 123'
    $ echo $?
    123
    
  5. If a background command is executed, does the $! special parameter get updated appropriately? [3%]

    $ sleep 10 &
    $ echo $!
    29483
    $ pgrep sleep
    29483
    
  6. If a foreground command is executed, does it not modify $!? [3%]

    $ sleep 10 &
    $ pgrep sleep
    29483
    $ echo "hello world"
    hello world
    $ echo $!
    29483
    
  7. If the PATH variable is changed, does it affect command lookup? [3%]

    $ echo test
    test
    $ PATH=
    $ echo test
    bigshell: No such file or directory
    
  8. Does the cd built-in update the PWD shell variable? [3%]

    $ cd
    $ printenv PWD
    /home/bennybeaver
    $ cd /bin
    $ printenv PWD
    /bin
    

Word Expansions [6%]

  1. Are command words expanded properly? [2%]

    $ X=echo
    $ $X test
    test
    $ Y=hello
    $ Z=world
    $ $X $Y $Z
    hello world
    
    $ X=testfile
    $ DIR=~/$X
    $ echo $DIR
    /home/bennybeaver/testfile
    
  2. Are assignment values expanded properly? [2%]

    $ X=1234
    $ Y=~/$X
    $ echo $Y
    /home/bennybeaver/1234
    
  3. Are redirection filenames expanded properly? [2%]

    $ X=outfile
    $ echo "hello world" >| ~/$X
    $ cat /home/bennybeaver/outfile
    hello world
    

Redirection [18%]

Note

Each of the tests shown in this section assume that the file(s) being redirected do not initially exist.

  1. > operator creates a new file, and doesn’t overwrite existing file? [3%]

    $ echo test > testfile
    $ cat testfile
    test
    $ echo test2 > testfile
    [[some error message]]
    $ cat testfile
    test
    
  2. >| operator creates a new file, and does overwrite existing file? [3%]

    $ echo test >| testfile
    $ cat testfile
    test
    $ echo test2 >| testfile
    $ cat testfile
    test2
    
  3. < operator works appropriately? [3%]

    $ sh -c 'echo test > testfile'
    $ < testfile cat
    test
    
  4. <> operator works appropriately? [3%]

    $ sh -c 'echo test > testfile'
    $ <>testfile sh -c 'cat; echo test2 >&0'
    test
    $ cat testfile
    test
    test2
    
  5. >> operator works appropriately? [3%]

    $ sh -c 'echo hello > testfile'
    $ cat testfile
    hello
    $ >> testfile  echo world
    $ cat testfile
    hello
    world
    
  6. >& operator works appropriately? [4%]

    $ 5>&1   sh -c 'echo ERROR! >&5'
    ERROR!
    
  7. Multiple redirections work appropriately? [4%]

    $ >testfile 5>&1 6>&2  sh -c 'echo HELLO >&5; echo WORLD >&6'
    WORLD
    $ cat testfile
    HELLO
    

Pipelines [6%]

  1. Pipelines work appropriately? [3%]

    $ echo hello world! | sed 's/hello/goodbye/' | cat -v
    goodbye world!
    
  2. Pipelines work with redirects? [3%]

    $ 5>&1  sh -c 'echo hello world! >&5' | sed 's/hello/goodbye/' | cat -v
    goodbye world!
    

Synchronous Commands [15%]

  1. Synchronous commands run in foreground (BigShell waits on them)? [5%]

    $ sleep 100
                   # ...waiting...
    
  2. Sending stop signal to synchronous commands causes them to be stopped and placed in the background? [5%]

    $ sh -c 'sleep 5; killall -SIGSTOP sleep;' &
    [0] 29347
    $ sleep 100
                   # ... 5 seconds later ...
    [1] Stopped
    [0] Done
    $
    
  3. Sending kill signal to synchronous commands causes them to exit, and $? is updated appropriately? [5%]

    $ sh -c 'sleep 5; killall -SIGKILL sleep;' &
    [0] 23959
    $ sleep 100
                   # ... 5 seconds later ...
    [0] Done
    $ echo $?
    137
                   # 137 = 128 + 9 (SIGKILL is signal # 9)
    

Signals [6%]

  1. BigShell ignores the SIGTSTP, SIGINT, and SIGTTOU signals? [3%]

    $ kill -s SIGTSTP $$
    $ kill -s SIGINT $$
    $ kill -s SIGTTOU $$
    $
    
  2. Child processes don’t ignore these signals? [3%]

    $ sleep 100
    ^C$           # ctrl-C -- SIGINT
    $
    

Job Control

Entirely optional–an extra exercise if you want to push your understanding, it won’t be tested in the grading script.