Here is an example of how your shell should work: (italics indicate typed commands)
% ./myshell shell> echo hello world! hello world! shell> emacs test.txt & process 3456 started shell> mozilla & process 3457 started shell> cp test.txt newfile.txt shell> cd /tmp current directory is /tmp shell> wait process 3457 ended process 3456 ended shell> exit %
This project isn't as complicated as it sounds. The operating system will take care of most of the hard parts, you just have to understand and use the underlying facilities. You don't have to implement all of the complicated features found in real shells, such as pipes, redirection, and so forth. However, your program does need to be robust: when errors of various kinds are found, a sensible error message should be printed, and the shell should continue on normally.
ls, who, date, cat, cp, emacs, echo, grep.
If the command is typed normally, your shell should create a new process by calling fork() and then run the named program with its arguments by calling execvp(). The parent process should then wait for the newly-created process by calling waitpid(). If the command ends with the character & (ampersand), then the shell should not call waitpid() but simply allow the child process to run in the background while the user is allowed to execute more commands. In addition to external commands, your shell should also check for the following commands and handle them internally:
wait - This internal command should cause the shell
to wait for all previously-started background processes to complete.
cd - This internal command should use the system call
chdir() to change the working directory of the shell
to the directory named by the first argument.
exit - This internal command should cause the shell
to exit after all background processes have completed.
In addition to running interactively, your shell should also be able to run using a file as input. For example, if I place commands into the file foo:
echo hello world!Then I should be able to run the shell, using that file as an input to the shell with the < (less-than) redirection character:
% ./myshell < foo shell> hello world!Note that your shell does not need to open a new file to handle this case; simply use fgets() as if you were reading from the console. The trick is that you must be careful to check for an end-of-file (EOF) condition on the input. EOF is indicated when fgets() returning null. If your shell receives an EOF, then it has come to the end of the input file and should exit cleanly.
You must be very careful to check for errors in every aspect of the shell. Note that most Unix system calls use a return value of -1 to indicate failure. If a system call fails, the global variable errno is set to an integer error code, and strerror(errno) points to a string describing that error code. If an error occurs, you should be sure to detect it and print out a reasonable error message.
For example, if the user attempts to run a program that does not exist, your shell should print an error similar to the following:
shell> badprog badprog: No such file or directory.There are many other places that errors could occur in the shell. In systems programming, almost any function can return an error. You must code carefully, checking for errors at every stage. When something goes wrong, your shell should produce a suitable message and continue on if possible. We will hold you to this by testing your shell with all manner of bad inputs.
Here are some hints to get you started.
For reading input and printing output, you can use:
Note that, by custom, a Unix call is appended with a number indicating what section of the online manual it is contained in. Manual section 1 contains shell commands, section 2 contains system calls, section 3 contains library functions, and there are a few more sections. For example, fork(2) means that fork() is a system call described in section 2 of the manual. You can read the manual page for any command or function by running the command:
% man [section] <name>The section number is optional. If left out, man will search section 1, then section 2, and so forth. This can cause problems if you are looking for a system call with the same name as a shell command. For example:
% man printfwill yield printf(1), a shell command, which is not what you want. However:
% man 3 printfwill show you the printf(3) family of standard library calls, which is what you really want to see. Usually, the man pages you want are found in sections 2 and 3.
Your entire shell program should be contained in a single file myshell.c. To hand in this file, simply copy the source file and nothing else into /afs/nd.edu/coursesp.05/cse/cse341.01/dropbox/YOURNAME/p1. We will compile and test your code on the Fitzpatrick cluster, so be sure to build and test your code on those machines.
The grade on this project will take into account the correct functioning of your shell (50%), the ability to handle errors cleanly (30%), and good coding style (20%). Be sure to test your shell carefully using a variety of inputs.