Question: This project consists of modifying a C program which serves a shell interface that accepts user commands and then executes each command in a separate
This project consists of modifying a C program which serves a shell interface that accepts user commands and then executes each command in a separate process. A shell interface provides the user a prompt after which the next command is entered. The example below illustrates the prompt sh> and the users next command: cat prog.c. This command displays the file prog.c on the terminal using the Unix cat command. sh> cat prog.c One technique for implementing a shell interface is to have the parent process first read what the user enters on the command line (i.e. cat prog.c), and then create a separate child process that performs the command. Unless otherwise specified, the parent process waits for the child to exit before continuing. However, Unix shells typically also allow the child process to run in the background or concurrentlyas well by specifying the ampersand (&) at the end of the command. By rewriting the above command as sh> cat prog.c & the parent and child processes now run concurrently. The project is organized into three parts: (1) Design a Unix Shell; (2) Implement a Unix Shell (without a history feature); (3) Implement a Unix Shell with a history feature. 1 (10 pts) Design a Unix Shell The Unix Shell basically is a loop that repeatedly do the following things: presents a prompt, say sh> and waits for the user to enter a command line; parses the command line into an array, say args. For example, if the user enters ls -l at the sh-> prompt, args[0] becomes equals to the string ls, and args[1] is set to the string to -l; create a new process space by the system call fork(); load the user command (an executable code) by the system call exec(); One trick in the execution of a user command in a Unix Shell is the distinction of foreground and background of the command by checking whether there is an & system at the end of the command line. If yes, it is supposed to be a background execution, otherwise, a foreground execution. For a foreground command, the shell should wait the completion of the user command before it presents a new prompt; for a background commnad, the shell does not wait. Draw a flowchart to design a unix shell. The design should be clear enough to begin coding, i.e., the implementation of the shell. 1 2 (70 pts) Implement a Unix Shell without a history feature Based on the above design, you now need to implement a simple Unix Shell that can execute command lines input by a user. It is not required to support a history feature (which will be implemented in the third part). A significant portion of time will be spent on command line parsing. As mentioned in the lecture, Unix uses the system call exec() to load a user code (executable) to the space of the calling process. excevp() is a version of exec() that has the following interface: execvp(char *command, char *params[]); where command represents the command to be performed and params stores the parameters to this command. A typical example of calling excevp() is execvp(args[0], args); be sure to check the value of background to determine if the parent process is to wait for the child to exit or not. 3 (20 pts) Implement a Unix Shell with a history feature The next task is to modify above program so that it provides a history feature that allows the user access up to the 10 most recently entered commands. These commands will be numbered starting at 1 and will continue to grow larger even past 10, e.g., if the user has entered 35 commands, the 10 most recent commands should be numbered 26 to 35. This history feature will be implementing using a few different techniques. First, the user will be able to list these commands when he/she presses , which is a SIGINT signal. Unix systems use signals to notify a process that a particular event has occured. Signals may be either synchronous or asychronous, depending upon the source and the reason for the event being signaled. Once a signal has been generated by the occurrence of a certain event (e.g., division by zero, illegal memory access, user entering , etc.), the signal is delievered to a process where it must be handled. A process receiving a signal may handle it by one of the following techniques: Ignoring the signal using the default handler, or providing a separate signal-handling function. Signals may be handled by first setting certain fields in the C structure struct sigaction and then passing this structure to the sigaction() function. For example, the signal SIGINT represents the signal for terminating a program whith control sequence . The default signal handler for SIGINT is to terminate the program. Alternatively, a program may choose to setup its own signal-handling function by setting the sa handler field in struct sigaction to the name of the function which will handle the signal and then invoking the sigaction() function, passing it (1) the signal we are setting up a handler for, and (2) a pointer to struct sigaction. The following is an example code on unix signal handling: #include #include 2 #include #define BUFFER_SIZE 50 char buffer[BUFFER_SIZE]; /* the signal handling function */ void handle_SIGINT() { write(STDOUT_FILENO, buffer, strlen(buffer)); exit(0); } int main(int argc, char *argv[]) { /* set up the signal handler */ struct sigaction handler; handler.sa_handler = handle_SIGINT; sigaction(SIGINT, &handler, NULL); /* generate the output message */ strcpy(buffer, "Caught Control C "); /* loop until we receive */ while (1); return 0; } For this project, if the user enters , the signal handler will output a list of the most recent 10 commands. With this list, the user can run any of the previous 10 command by entering r x where x is the first letter of that command. If more than one command starts with x, execute the most recent one. Aslo the user should be able to run the most recent command again by just entering r. You can assume that only whitespace will separate the r and the first letter and that the letter will be followed by n n. Again r alone will be immedidately followed by the n n character if it is wished to execute the most recent command. Any command that is executed in this fashion should be echoed on the users screen and the command is also placed in the history buffer as the next command. (r x does not go into the history list; the actual command that it speficied, though, does.) If the user attempts to use this history facility to run a command and the command is detected to be erroneous, and error message should be given to the user and the command not entered into the history 3 list, and the excevp() function should not be called.
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
