Description
PART I The Shell Skeleton:
In the two parts of the second assignment, you will eventually design and implement a simple shell command interpreter, called mybash, which is a subset of a typical UNIX shell. The basic function of a shell is to accept lines of text as input and execute programs in response.
The command input must be in the following format (explained in more detail below): cmd (arg)* (<infile)? (| cmd (arg)* )* (>(>)? outfile)? (&)? The shell will eventually have these basic features1 : • Execution of simple commands: a simple command consists of a command name followed by an optional sequence of arguments separated by spaces or tabs, each of which is a single word. A command may have up to 10 arguments.
While a shell can have built-in commands (see below), it must be able to execute simple commands existing as independent files in the file system. • I/O Redirection: A simple commands standard input may be redirected to come from a file by preceding the file name with “<”. Similarly, the standard output may be redirected with “>”, which truncates the output file. If the output redirection symbol is “>>”, the output is appended to the file.
An output file is created if it doesn’t exist. • A pipeline consists of a sequence of one or more simple commands separated with bars “|”. Each except the last simple command in a pipeline has its standard output connected, via a pipe, to the standard input of its right neighbor. • A pipeline (and a simple command) is terminated with a newline (“\n”, [RETURN]), or an ampersand “&”.
In the first case the shell waits for the 1 These are the basic features that you will need to implement in assignment 2. Some additional features will be added in assignment 3. rightmost simple command to terminate before continuing. In the ampersand case, the shell does not wait. • Built-in commands: Built-in commands are assignment, set, cd, exit, jobs, kill, fg, bg, pwd2 . Here are some examples of valid command lines: ls ls –l > file1 cat < infile | grep 14877 | wc cat < infile | grep file1.txt > outfile & prog1 arg1 arg2 arg3 arg4 < file2.txt | prog2 arg1 | prog3 > outfile I will provide you with three headstart files: • parser.c: include functions to parse command lines entered by the user, • a skeleton mybash.c file, and • a makefile.
The headstart files will be provided to you as an archive file (“headstart1.tar”). In order to unpack the archive files, copy the headstart1.tar file to your project subdirectory and enter the following command: bash-3.00$ tar –xvf headstart1.tar The parser.c file contains functions to scan and parse user input. The function int ParseCommandLine(char line[], struct CommandData *data) takes a command line as an argument and populates a CommandData structure. The function returns 1 if it was able to successfully parse the line, and zero if there was some kind of error in the line.
The structure of CommandData is as follows: struct Command { char *command; /* simple command name */ char *args[11]; /* Array of up to 11 arguments */ int numargs; /* number of arguments */ }; struct CommandData { struct Command TheCommands[20]; /* the commands to be executed. TheCommands[0] is the first command to be executed. Its output is piped to TheCommands[1], etc. */ int numcommands; /* the number of commands in the above array */ char *infile; /* the file for input redirection, NULL if none */ char *outfile; /* the file for output redirection, NULL if none */ int background; /* 0 if process is to run in foreground, 1 if in background */ }; 2 Some of these built-in commands will be implemented in assignment 3.
In this first part you won’t need to implement the actual functionality of the shell. Starting with the headstart files, you will complete a skeleton of the shell. This shell skeleton will execute the following loop: 1. Print a shell prompt that displays the current working directory (eg, “/home/drews/>”) 2. Read the next command line (terminates with ‘\n’) 3. Parse the command line using the ParseCommandLine function which will populate the CommandData structure. 4. Print information about each command line: For each simple command print the command name and all the arguments. Print the filenames for input redirection or output redirection, NULL if none.
Indicate whether the command line is executed with background option turned on or off. Indicate whether the command is a builtin command or not. 5. Repeat, until the user enters the exit command Requirements: 1. Read the following article by D. Ritchie and K. Thompson: D. Ritchie and K. Thompson, “The UNIX Time-Sharing System,” Communications of ACM 7, 7, July 1974.
http://citeseer.ist.psu.edu/ritchie74unix.html 2. Implement the Shell Skeleton as described above.
Organization:
You may work individually or in groups with up to one other person (two total). Your program must be written in either C or C++ and compile and run correctly on the prime machines in the lab. Submission: You must submit your program in a similar way to the first assignment: (use the command “/home/drews/bin/442submit 4”).
The project number for the electronic submission is 4. Include the names of all the group members in all the source code files! Grading: The maximum number of points for this part of the assignment is 10. Hints and Help: • A Unix Tutorial for Beginners: http://www.ee.surrey.ac.uk/Teaching/Unix/ • Useful system calls and library functions (see man pages for more info): getcwd(), strncmp(), gets(), strdup() Example: /home/drews/> ls Number of simple commands : 1 command1 : ls Input file : (Null) Output file : (Null) Background option : OFF Built-in command : NO /home/drews/> ls –l > file1 Number of simple commands : 1 command1 : ls arg[0] : -l Input file : (Null) Output file : (file1)
Background option : OFF Built-in command : NO /home/drews/> cat < infile | grep 14877 | wc Number of simple commands : 3 command1 : ls arg[0] : -l command2 : grep arg[0] : 14877 command3 : wc Input file : infile Output file : (Null) Background option : OFF Built-in command : NO /home/drews/> cat < infile | grep file1.txt > outfile & Number of simple commands : 2 command1 : cat command2 : grep arg[0] : file1.txt Input file : infile Output file : outfile Background option : ON Built-in command : NO /home/drews/> exit command1 : exit Input file : (Null) Output file : (Null) Background option : OFF Built-in command : YES
PART II: Basic Shell Functionality:
This is the second part of your shell project. You will start with the with PART I and complete a simple, but useful shell. Input to bash consists of lines using the same grammar as the first part of the assignment, with additions as listed below. • In this part of the project you will implement the execution of simple commands.
A simple command will be given either as an absolute path name, a relative path name, or just a command name, as in: /bin/ls ./a.out ls A simple command given as absolute or relative path name will be executed without further processing. If the command name is not an absolute or relative path, bash must search the directories in the PATH environment variable to find a directory containing the command and run the appropriate command, if found.
For example, if the PATH environment variable contains: /bin:/usr/bin:/etc then bash should interpret the command “ls” as referring to the program “/bin/ls” and the command “mount” as referring to the program “/etc/mount”. Note that the system call access() may be helpful for determining if an executable file exists with a given name. • Your Bash shell will redirect the input and output of the command to the files specified on the command line, if included. By default, of course, bash should leave the stdin, stdout, and stderr attached to the terminal.
Error checking and reporting is particularly important here. • Your Bash shell will allow simple pipelines consisting of only 2 commands. For example, the following command is a simple pipeline: ls –l | grep txt Built-in Commands: • In this assignment your Bash will support the following built-in commands: Builtin Command Behavior cd Change directory to the value of the HOME variable cd Change the current working directory to the value of the variable directory pwd Print the current working directory exit Exit shell set Print the Unix environment of your shell process DEBUG=yes Enable debugging DEBU=no Disable debugging The built-in functions are not executed by forking and executing an executable.
Instead, the shell process executes them itself. All other command must be executed in a child process.
Debugging:
Shell-level debugging is enabled when the value of the shell variable DEBUG is “yes”, and disabled when it is “no”. If DEBUG is set to yes, you will print the debugging information from part I. Example: /home/drews/> DEBUG=yes Debugging is on /home/drews/> ls –l > file1 DEBUG OUTPUT: ————————– DEBUG: Number of simple commands: 1 DEBUG: command1 : ls DEBUG: arg[0] : -l DEBUG: Input file : (Null) DEBUG: Output file : (file1) DEBUG: Background option : OFF DEBUG: Built-in command : NO ————————– /home/drews/> DEBUG=no Debugging is off /home/drews/>
Outline of a Simple Shell with Builtin Commands:
• The following (pseudo) C-code skeleton illustrates the basic shell operation: Grading: 1. [10 points] Implementation of simple commands 2. [10 points] Implementation of simple pipelines 3. [5 points] Implementation of input redirection 4. [5 points] Implementation of output redirection with/without output append. 5. [10 points] Other built-in commands (cd, cd pathname, pwd, exit) Organization: • You may work individually or in groups with up to one other person (two total). Your program must be written in either C or C++ and compile and run correctly on the prime machines in the lab. Submission: •
You must submit your program by way of electronic submission as in assignment 1. The project number for the electronic submission is 5. Include the names of all the group members in all the source code files! Hints and Help: • The man pages are your friends! Manpages of particular interest would be: int main(void){ /* Variable declarations and definitions */ while(1) { DisplayPrompt();
ParseCommandLine(inputBuffer, CommandStructure); if( BuiltInTest(CommandStructure) == 1 ) ExecBuiltInCommand(CommandStructure); /* execute built-in command */ else{ /* The steps for executing the command are: */ (1) Redirect Input/Output for the command, if necessary (2) Create a new process which is a copy of the calling process (-> fork() system call) (3) Child process will run a new program (-> one of the exec() system calls) (4) if( BackgroundOption(CommandStructure) == 0 ) the parent shell will wait(), otherwise it will continue the while loop } /* end else */ } /* end while */ } /* end
main */ fork(2) exit(2) exec(2), execl(2), execv(2), execle(2), execve(2), execlp(2), execvp(2) wait(2), waitpid(2) dup2(3C) signal(3C), sigaction(2), sigprocmask(2) getcwd(3C), chdir(2)
For example, to pull up the manpage for dup2 on the Solaris machines, you will have to type bash-3.00$ man -s 3C dup2 • You may find it convenient to implement the features in the following order: 1. Run commands with absolute path names 2. Use PATH to find commands 4. Implement other built-in commands 5. Implement I/O redirection 6. Implement simple pipelined commands