Lab 4: Redirecting I/O

Overview

The purpose of this lab is to develop a working knowledge of UNIX system-level I/O and IPC via unnamed pipes. You will do this by extending the capabilities of the tiny shell you developed in the last lab.

Preliminaries

In this lab you'll be working in the labs/4_iolab directory.

As before, don't forget to commit your previous work and pull the latest changes from the central repository before starting!

You will be building on top of the tsh.c file you completed for the previous lab, so the first thing you'll need to do is copy that file over the tsh.c file present in the 4_iolab directory (which is just an empty placeholder). The following command will accomplish this:

cp ../3_shlab/tsh.c .

If your previous shell implementation was buggy or incomplete, however, you might want to start with a fresh version of the original tsh.c file given to you for the shell lab. You can do that by checking out the appropriate version from Git and copying it over — ask a TA or myself if you need help doing this.

Note that we will not, except where necessary, be checking for functionality that should've been completed for the previous shell lab.

Extending the Shell

You will be adding input and output redirection with the ">", and "<" shell operators, and basic interprocess communication via unnamed pipes with the "|" operator.

To accomplish this, your implementation must be modified to:

To make your life easier (and reduce the amount of code you have to write), there are a number of simplifying assumptions you can make:

Testing

As before, we provide trace files to test your I/O redirection and pipe implementations. Use make test{01-04} to run the trace files on your shell. A reference shell is not provided for this lab, but you can still use make rtest{01-04} to run the trace files on the system shell to see the desired output.

Grading

You can receive a maximum of 30 points on this lab:

Besides basic functionality, we will be checking for the following:

Hints

This section gives you a few hints to get started with the lab. Skip it if you're feeling confident!

Opening and Creating Files

It's also important that you specify the correct flags to the "open" system call (which you'll need to open files for I/O redirection). When opening a file for input, you'll probably want to use the following:

open(filename, O_RDONLY);

but for output redirection, where you'll likely have to create a file and delete its existing contents, use:

open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);

Where the second argument specifies that the file should be opened for writing (O_WRONLY), that it should be created if it doesn't already exist (O_CREAT), and that it should be truncated to size 0 before writing (O_TRUNC). The last argument specifies that if the file is being newly created it should have its read (S_IRUSR) and write (S_IWUSR) permissions set for the user, so that you can examine the file after it's been created.

Look up the "open" manpage for more details.

Remember to close the files when you're done! (In all necessary processes.)

Parsing multiple commands

When dealing with two commands that are separated by the pipe (|) character, you're going to have to do more than just call parseline once, as you did before.

The strchr function can be used to determine if a string contains a given character, and the strsep function can be used to "split" the string (with a "\0") on a specified token.

The following snippet of code demonstrates their combined use for parsing two separate commands joined with a pipe:

char *argv[MAXARGS];
char *p = cmdline;

if (strchr(cmdline, '|')) {    
    strsep(&p, "|"); // The '>' character in cmdline is replaced with
                     // a '\0', and p points to subsequent character.

    // Now use parseline to parse the two halves of the command line

    parseline(cmdline, argv);
    printf("%s\n", argv[0]);
    // fork and exec the first process

    parseline(p, argv);
    printf("%s\n", argv[0]);
    // fork and exec the second process
}

Note that you shouldn't need to call parseline a second time to handle input and output redirection (the second half of cmdline will simply consist of a file name, which you can manually remove whitespace from).