Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

Introduction The purpose of this programming assignment is to give you some experience with processes and input/output at the system-call level in UNIX/Linux. Make certain

Introduction The purpose of this programming assignment is to give you some experience with processes and input/output at the system-call level in UNIX/Linux. Make certain you read and understand all of the material in the assignment. A significant part of the code for this assignment is already provided, and you may use it if you wish; details appear later in the assignment. The program you write will be a simple command line interpreter, or shell. It will not have nearly as much functionality as a normal shell. Instead, it will deal only with running processes and handling redirection of the standard input and output to those processes. Specifically, your program will repeatedly do the following things: 1. Read a command line from the standard input. At the end of file, just quit. Also ignore lines that contain only blanks and tab characters. 2. Identify the words1 on the command line. 3. Verify that the first word is either a path to an executable command, or that it is the name of an executable file in one of the directories identified by the PATH environment variable; if it is neither of these, print an error message and repeat from step 1. 4. Create a child process to execute the command (using the fork system call). Verify that fork did not return -1 (which would indicate an error). 5a. In the child process just created: a. Redirect the standard input and the standard output, if necessary (as directed by the appropriate command line options). b. Prepare the argument structure necessary for the execve system call. c. Invoke execve to begin execution of the child process; if execve returns, print an appropriate error message and exit (using the exit system call). 5b. In the parent process, execute the wait system call to wait for the child process to terminate. Details Lets consider more detail about each of these actions. In step 1 you must use the read system call, not any other input mechanism provided by the C library. To avoid extra complexity, assume you are reading commands from a disk file, and read a single byte at 1 For our purposes, a word is just a sequence of printable non-blank characters; we assume at least one space or tab character will separate each word from the next. Computer Science 4500/8506 Programming Assignment 1 Fall 2017 a time. The file descriptor to use is 0, which corresponds to the standard input. When read returns 0 you have encountered the end of file. When the single character read is an end of line character (' '), then you have encountered the end of a line. You should echo the input you read; when youre actually reading from the keyboard youll get an extra copy of the input, but when reading from a disk file, youll get a display of the command youre about to process. You may assume that no input line contains more than 100 characters (including the end of line character). Step 2 is a simple parsing operation. Assume each word is separated from another word by one or more blanks or tab characters (that is, whitespace). We will not worry about quoting arguments with single- or double-quotes or backslashes (as more complete shells will do). Additionally, you may assume that the characters used to specify redirection of input and output (i.e. '>' and '<') will be separated from other words by whitespace. You may assume there will be no more than 16 words in any input line, including the redirection characters. You may use C library functions like strtok to assist in this step. You may also assume no word will be longer than 64 characters, including a null terminator. At this point you have a sequence of words corresponding to a command name, its arguments, and possibly some input/output redirection information. In step 3 we will validate the name of the command to be executed (which is in the first command line word). If this word includes a '/', then it is a path to a command. Otherwise it is assumed to be the name of a command (file) that appears in one of the directories specified in the PATH environment variable. This is just a colon-separated list of the names of directories in which the shell is to search for an executable file. You can see what this contains by typing the command echo $PATH. A possible result of this might be /bin:/usr/bin:/sbin:/usr/local/bin:/usr/games So if I should enter a command line doit to it (which is not an explicit path, as it does not include '/'), I expect the shell to search first in /bin for doit, then in /usr/bin, and so forth, finally searching in /usr/games. If the file doit is not found in any of those directories, an error should be reported (something like doit not found), and the shell should repeat from step 1. To determine if the file exists and is executable you should use the access system call. This call has two arguments. The first is the path to a file, and the second is the access mode to the file that is to be checked. In our case, we specifically want to test for executability, so the second argument would be X_OK (which is defined by including the header file unistd.h). If access returns 0, then the file exists and is executable (at least as far as the files permissions indicate). Otherwise, the file does not exist, or is not executable. (Notice that the first argument to access must be the path to the file, not just the file name. As a result, youll need to concatenate a directory name from the PATH environment variable, a slash, and the file name to yield a suitable string for the first argument to access.) Step 4 is simple: just use the fork system call to create a process, being sure to save the value it returns. As is always the case, check to see if the returned value is 1. If it is, some error occurred (like you got stuck in a loop calling fork!), and you should abandon further execution (by printing an appropriate error message and calling exit.) Recall that the fork system call returns 0 in the child process just created, and non-zero in the parent. Step 5a is thus executed only when fork returns 0. The first task, step 5a.a, is to handle redirection of the standard input and output, if requested. The syntax for redirecting the standard input is a include a consecutive pair of words '<' and a filename in the command line; redirecting the standard output is Computer Science 4500/8506 Programming Assignment 1 Fall 2017 similar but requires that there be the words '>' and a filename in the command line. So the standard input to the command doit could be redirected to the file some.input with this command: doit < some.input Standard shells will also allow < some.input doit but we will assume that the first word in the command line always identifies the command. So the '<' will always appear somewhere after the first word (if it appears at all). A similar command could be used to redirect the standard output to the file some.output: doit > some.output Recall that fork provides a child process with the same set of open files present in its parent process. Without redirection, the child will then access the same standard input and output files used by the parent, specifically through file descriptors 0 and 1, respectively. To redirect the standard input to file some.input we use four steps: Open the file named some.input for reading (use the open system call). If this fails, then either the file doesnt exist, or were not allowed to read it. In any event, we cannot continue, so print an appropriate error message and exit the child process. If we assume the file descriptor successfully returned by open in the preceding step is in variable fd, then we must arrange for the child process to have that file descriptor in place of file descriptor 0. Two steps achieve this: close file descriptor 0, and dup file descriptor fd. Since the dup system call makes a copy of the specified file descriptor (fd) on the lowest currently unused file descriptor (which is obviously 0 at this point, since we just closed 0), we now have file descriptor 0 associated with the redirected input file. The last step is to close file descriptor fd, since it is no longer needed (file descriptor 0 will remain associated with the file). Similar steps can be used to redirect output to a file, using file descriptor 1 instead of file descriptor 0. Of course, the file will need to be created if it does not already exist, and errors could be reported (for example, the file already exists but you are not allowed to write to it). In step 5a.b the child process prepares the arguments for the execve system call. The arguments are passed to execve through an array of pointers to the argument strings, with a null pointer provided as the last entry. It is conventionally the case that the first argument (in the array entry with subscript 0) is a pointer to the string representing the name of the program being executed. The first argument to execve is the path to the file to be executed, which was determined in step 3. The second argument is a pointer to the array of argument pointers. The third and last argument to execve is a pointer to an array of strings representing the environment for the program about to be executed. This should usually be the value of the external variable environ. A very simple program illustrates these concepts. It execves the program /bin/echo to display Hello, world! and is available on loki in the file /home/stanw/csci4500/echo_me.c; this directory also contains other sample programs.): Computer Science 4500/8506 Programming Assignment 1 Fall 2017 Step 5a.c is very simple. Remember that the execve system call will not normally return; if it does, it indicates that the system detected an error that prevented its successful execution, so you should print an error message and exit. Step 5b is executed only by the parent process after receiving a non-negative, non-zero value from fork. Here the parent executes the wait system call to delay further execution until the child process terminates. Like most system calls, wait can return -1 to indicate an error. In our program, the only error that might be expected is failure of the execve system call in step 5a.c. So regardless of the value returned by wait, the process (that is, the original process) will continue executing. Heres a simple example that shows how a program might use the wait system call. In this example, the value of status returned by wait is used to indicate how the child process terminated. Your program for this assignment need not diagnose the reason for the child process termination. #include #include #include extern char **environ; int main(int argc, char *argv[]) { pid_t pid; int status, which; char msg[100]; pid = fork(); if (pid == -1) { write(1,"fork failed. ",12); exit(1); } if (pid == 0) { /* child process */ int x, y, z; y = 12; z = 0; x = y / z; /* note this will fail! */ exit(1); } if (pid != 0) { /* parent */ which = wait(&status); /* which usually has a pid */ if (which == -1) { write(1,"wait failed. ",12); exit(1); } if (status & 0xff) { /* abnormal termination */ sprintf(msg,"process %d terminated abnormally for reason %d ", which, status & 0xff); } else { /* normal termination */ sprintf(msg,"process %d terminated normally with status %d ", which, (status >> 8) & 0xff); } write(1,msg,strlen(msg)); } } Computer Science 4500/8506 Programming Assignment 1 Fall 2017 Notes and Restrictions You may use sprintf (as illustrated in the last example) to prepare messages to be displayed, but no standard C library functions that result in input or output must be used in your solution. Instead, all input and output must be accomplished using the read and write system calls (with open used to achieve redirection of standard input and output). You may also use the string functions for things like parsing the command line and computing the length of strings (for the write system call). Likewise, use no other standard C library functions related to creating processes. Your solution must be your own work only; do not work with others in developing your solution (except, perhaps for using the code provided on loki in /home/stanw/csci4500. Evaluation Your program will be evaluated by testing with a variety of commands. To receive 100 percent, your program must process the entire set of commands correctly. Failure of your program to compile on loki will result in a grade of no more than 50 percent. Failure to correctly handle a command line with I/O redirection will result in a grade of no more than 75 percent. Failure to submit the program by the due date may result in a 5 point grade reduction for each day the program is late. The instructors solution to this problem is available in binary form (that is, as an executable) in the file /home/stanw/csci4500/prog1. Use it to determine acceptable results of processing a set of commands. A relatively simple set of commands, but very representative, can be found in prog1.input; the expected output can be obtained by using the commands in this file as input to the instructors solution. Note that the instructors solution diagnoses most possible errors, but you may assume there will be no errors in the data used to evaluate your solution (other than attempting to execute a command that doesnt exist). As noted earlier, there are a number of pieces of sample code provided in the /home/stanw/csci4500 directory on loki; the file named MANIFEST is a summary of these files. In particular, the file named prog1_temp.c is a complete, but simple, shell that will successfully process command lines containing single commands. You may use any or all of this code in your solution. Requirements You must write (and test) a C program that functions as a simple shell as just described. Your solution should be a single file of C source code named prog1.c and a makefile (named makefile or Makefile). The solution must be submitted by Thursday, October 19, 2017 by 11:59 PM. To submit your solution, create a directory named csci4500-173-prog1 at the top level of your home directory on loki and place the two required files in it. That is, if your username is brenfro, then you must create a directory named /home/brenfro/csci4500-173-prog1 and place the files prog1.c and makefile in that directory. There should be no other files in that directory, and the files you place there should not be changed or removed until you have received a grade report for the assignment. Also make certain your solution successfully builds (using your makefile) and runs on loki; this is the system where programming assignment solutions will be evaluated.

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

Big Data Systems A 360-degree Approach

Authors: Jawwad ShamsiMuhammad Khojaye

1st Edition

0429531575, 9780429531576

More Books

Students also viewed these Databases questions

Question

2 How unemployment and inflation are measured.

Answered: 1 week ago

Question

305 mg of C6H12O6 in 55.2 mL of solution whats the molarity

Answered: 1 week ago

Question

What is the most important part of any HCM Project Map and why?

Answered: 1 week ago