CSC 1600: Operating Systems Concepts Spring 2004 Programming Assignment: The Shell Due: Day before Spring Break. The shell is the term often used for the command line interpreter of an operating system (particularly for UNIX systems). That is, the shell is the program that reads and parses the user’s input command line and then starts up the processes needed to carry out these command(s). This programming assignment requires you to write a stripped-down shell for a UNIX-like OS. The programming assignment itself is rather short (excluding the parser -- the code I will give you -- it can be done in less than 2 pages of code, including comments!). However, in order to write a shell you will need to understand the UNIX mechanisms for process creation/termination, interprocess communication, and basic file management, as well as the C system calls which are used to implement these operating system constructs. Such a short program can be deceptively difficult, particularly because you will be dealing with the creation and synchronization of multiple concurrent processes. I would recommend that you think carefully and make sure you understand the above mechanisms before attempting to write any code. 1. BEFORE YOU BEGIN THIS ASSIGNMENT, make sure you understand the basic functionality of the standard UNIX shell, in particular the following features: A) redirection of a command’s input/output from or to a file. That is, the “>“ and “ file.lst will run the ls command and put the output of the command (a listing of all the files in the directory) into the file file.lst. B) the use of pipes to connect the output of one command to the input of the following command on the command line. That is, the use of the “|” (vertical bar) in the UNIX shell. For example, the command ls | sort will run the ls command and use the output of the ls command as the input to the sort command. C) the execution of commands “in the background.” That is, the use of the “ & ” at the end of a command line in the UNIX shell. When a command line is terminated with a “&”, the shell will begin execution of the command(s) in the command line, immediately re-display the prompt (i.e., “csh>“), and then accept and execute additional commands without waiting for the commands in the first command line to terminate. These commands in the first command line are said to be running “in the background.” Before attempting the assignment, you should try these features out within UNIX for yourselves, since you’ll have to try them out in your own shell later on. 2. You will need to use the following system calls in your shell program: fork(), wait(), exit(), close(), open(), pipe(), and dup(), which we will go over in class; you will also use the execvp() system call. If you need to look up information on these calls, you should log into the UNIX machine you are using for this assignment and type man (short for “manual”), followed by the name of the system call you want more information about.

For example, man pipe will display the documentation for the pipe() system call. Note that when a process executes the execvp(string,args) system call, the image of the calling process is overlayed with the image of the executable file named in string. That is, the image of the calling process becomes the image file named in string. For example, if string is “ls”, a process executing the execvp() will execute the ls command (and then terminate). Note that there is no “return” from the execvp() system call to the calling program, unless the execvp() fails. 3. I have available for your use a command line parser for your shell. If you are working on this assignment within the CSC sun cluster (e.g. monet.vill.edu), you should put the statement #include “/mnt/csc8410/parse.c” as the first line in your shell program. This include statement will include the parser code into your C program as well as the various definition files needed for the parser and for the above system calls. The parser itself will parse a command line containing commands (separated by pipes), redirection of I/O, and the optional background flag “&”. It will also perform limited checking for the correct use of “>“, “=i */

char *argv[10];

}

int background;

/* if this is set to YES after parsecmd() is executed, means */ /* that command line should be executed in background */

When your shell does an execvp() system call to execute one of the commands in the command line, the command name and its arguments can be taken directly from the commands[] array (defined for you in parse.c). For example, the system call: execvp(commands[i].cmdname, &commands[i].argv[0]);

will cause the calling process’ image to be overlayed with the executable image for the ith command in the command line. The return value from parsecmd() is the number of commands contained in the command line just parsed. A negative return value signifies that an error was encountered in parsing the command line. Your shell should NOT attempt to execute any of the commands in a command line when a parse error occurs! 4. Using the above system calls and parsecmd() you are to write a program for a shell which will: • •







execute up to 5 commands on a command line (separated by pipes). The commands may have arguments and flags as well. handle the arbitrary (but syntactically correct) use of redirection of input and/or output for a single command or for each of the two commands at the start and end of a "pipe chain." implement the use of background commands (i.e., the termination of a command line by “&”), in which case your shell should initiate the command(s) in the command line, but NOT wait for the command(s) to finish before giving its next prompt and possibly executing the next command. Your shell must accept the non-Unix command “goodbye.” This command is used to tell your shell to quit. There will be several differences between your shell and a full-blown Unix or Linux shell. In your report, explain why the “cd” command does not work for your shell, but does work for Linux/Unix.

5. Here are a few examples of command line strings that your shell should be able to handle. Assume your shell prompt is myshell> myshell> date myshell> cc prog1.c myshell> date > file.txt myshell> ls -l > file.lst myshell> sort < file.lst | wc myshell> ls -l | sort -r myshell> sort file.lst | wc myshell> man “grep” | grep “the” > results.txt

6. A few final notes on implementing your shell: • PLEASE, OH PLEASE FOR THE LOVE OF ALL THAT IS HOLY AND GOOD, FOLLOW THIS HINT: Start out simple, and incrementally add to the functionality of your shell. I will not require you to hand in pieces of code as you go along, so it is up to YOU to pace yourself. I recommend designing the code in the following steps: • FIRST: write a program that does nothing but print out a prompt, read in a command line, and then prints back out to you the contents of the linked list returned by the parser. This is to verify you understand how to access the data. Have the program spit out error messages for poorly formatted command lines. • SECOND: update the program so it "exec's" a single command (no pipes or redirects), without spawning any separate child processes. • THIRD: update the program so it spawns a child process for a single command (no pipes or redirects). Make the shell wait until the child process dies before printing out a new prompt. • FOURTH: update the program so it handles ">" and "