Many programs need the assistance of the operating system in order to produce a desired output. The operating system handles many tasks, such as file/Directory manipulation and I/O, and process and thread scheduling and execution. Functions that allow you to communicate with the operating system are called "system calls", or "syscalls" for short. As you will eventually learn in 15-213, there are many programs written that take advantage of syscalls.
In this assignment, you will write your own UNIX shell. Many shells that are offered have a lot of fancy features added to increase usability such as tab completion, history and piping, but they all have the same basic functionality of being able to execute programs in the background or foreground. In later classes such as 15-213 and 15-310 you might be required to implement some of these more complex features, but for now we only want you to understand how to make and manage new processes in UNIX. So your shell only need to be able to handle 2 basic type of commands:
yourshell> <COMMAND> <ARGUMENTS>
yourshell> <COMMAND> <ARGUMENTS> &
In addition to those commands, your shell will accept 2 shell specific commands related to the shell, itself:
If your shell is supplied with just "<COMMAND> <ARGUMENTS>" then it should execute the command and wait for it to finish before continuing. However, if your shell is supplied with "<COMMAND> <ARGUMENTS> &" then it should fork a new process and, without waiting for the new process to terminate, be ready to accept a new command. The operating system will handle all of the process scheduling so there's nothing to worry about there. Finally, the shell needs to recognize the "quit" and "jobs" commands. Quit terminates the shell. Jobs prints out all current active processes and their respective PID. This means that you will need to implement some sort of data structure to hold all active processes and when processes exit remove them correctly. Note that you do not want this to affect your shell's ability to execute processes in the background. You may want to read the man page for "waitpid" and pay close attention to the WNOHANG flag. When calling exec, you need to provide it with the absolute system path to the command you wish to execute. Therefore, if you try calling "ls", it will not work. Instead, you'll need to give the full path, "/bin/ls". Real shells have an understanding of where commands live, known as the "search path". But, this isn't necessary for the 15-123 shell -- instead, we'll always feed our shelsl the full path, so they don't need to know it.
The first step you will need to do is to write a parser/grammar checker. A parser simply takes a command line as one long string and splits it up into tokens. This can be done easily using "strok" and/or sscanf. A grammar checker just makes sure that the command you supplied conforms to what the input should look like. For our simplified shell, we have very few grammatical rules. You need only check that "&" comes after a command and it's arguments, and not before. If your shell is given the "quit" command, anything after this command would not make sense, so your shell should give an error message and carry on. Ditto for "jobs".
After you have tokenized a command line and made sure that it's in the correct format, you need to execute the command. If you are given the quit command, you simply exit the program. The "jobs" command should produce a jobs listing. Any other command should spawn a child process and exec it into the requested command with the requested arguments. If the & is not given, the shell should wait for the running child process to finish before continuing.
You can name your shell whatever you like. You can hardcode the name in your shell, or use "argv" Either way, actually print out your shell's name with a ">" after it. Your parse error messages do not have to be very elaborative, see the example below.
yourshell> /bin/ls -laF
yourshell> /somepath/SortHugeDatabase &
yourshell> & /bin/ls
yourshell: Parse error! Invalid command line.