Homework 4: Simple xv6 shell

This assignment will make you familiar with how Unix systems use the system call interface. Specifically, you will build a simple shell for the xv6 system. This assignment requires xv6, so you have to follow the setup instructions here Xv6 Setup Instructions. Now you're ready to read the Chapter 0 of the xv6 book.

Building a shell

First of all, xv6 already comes with a shell, you should take a look at the source code here: sh.c

Your job is to write a similar, but simpler shell. It should be able to run commands with arguments, handle input and output redirection, and set up two-element pipelines. Your shell should act like the xv6 shell sh for these examples as well as similar commands:

echo hello there
echo something > file.txt
ls | grep READ
grep lion < data.txt | wc > count
echo echo hello | nsh

You should put your shell source in nsh.c inside the xv6 root folder, and modify the Makefile to compile it (take a look at how other programs are compiled for xv6, e.g., wc).

We'll test your nsh.c with our copies of other xv6 files; so while you can modify other files, your shell should not depend on those modifications. Your shell should use @ (at sign) as a prompt rather than $, to avoid confusion with the real shell. An xv6 session with your shell might look like this:

xv6 kernel is booting
$ nsh
@ grep Ken < README
xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix
@ 

One restriction for this homework: you cannot use a memory allocator provided by xv6 (malloc()). Instead you should use only local (stack-allocated) variables and global variables. It's OK to impose reasonable fixed limits on things like the maximum length of a command name, the maximum number of arguments, or the maximum length of any single argument.

We supply you with an xv6 test program testsh, source in testsh.c. You're done with the lab if your shell passes all the tests, like this:

$ testsh nsh
simple echo: PASS
simple grep: PASS
two commands: PASS
output redirection: PASS
input redirection: PASS
both redirections: PASS
simple pipe: PASS
pipe and redirects: PASS
lots of commands: PASS
passed all tests

Hints

You don't need to implement features that aren't required by the tests; for example, you don't have to implement parentheses or quoting.

The Kernighan and Ritchie C book is full of useful bits of code; feel free to incorporate any of it in your solution (with comments stating where you got the code). For example, you may find useful the gettoken() function from the parser in Section 5.12.

nsh is much simpler than xv6's sh in terms of features, so you are likely best off writing nsh from scratch. But, you can look at the xv6 shell source (in sh.c) for ideas, and borrow code, as long as you include comments saying where you got the code.

Xv6 supplies you with a small library of C functions in ulib.c; feel free to use them. As mentioned above, however, you are not allowed to use malloc().

Remember to close un-needed file descriptors, both to avoid running out, and because a process reading a pipe won't see end-of-file until all the write descriptors for that pipe are closed.

Every system call in your code should check whether the call returned an error.

testsh redirects your shell's standard output, which means you won't see it. Your shell should print error and debug messages to file descriptor 2, the standard error, using printf(2,...).

If testsh complains, it may help to modify testsh to print the command it wants your shell to run, and to modify your shell to print the command-line(s) it receives and the commands it executes.

Another helpful trick is to modify one() in testsh.c to print the output it read from your shell, and the output it expected.

Submission

Please submit code to Gradescope Gradescope. Your submission should consist only of Makefile and nsh.c. All other files would be ignored. If you have extra credit parts then place them in the folders extra1, extra2, extra3 respectively. In other words, the structure of the submission should be:
/
  - Makefile
  - nsh.c
  - /extra1                         --optional
    - Makefile
    - nsh.c
  - /extra2                         --optional
    - Makefile
    - nsh.c
  - /extra3                         --optional
    - Makefile
    - nsh.c

Challenge exercises (extra 21%, 7% each)

You can add any feature of your choice to your shell. But, you may want to consider the following as a start:

  • Implement lists of commands, separated by ";"
  • Implement sub shells by implementing "(" and ")"
  • Implement running commands in the background by supporting "&" and "wait"
Updated: November, 2019