Implementing your own system function

Let us delve into the details of loading and running executable files.

Standard system function

The standard C language in Unix provides a function system with the prototype
    int system(const char * command);
This function executes the command specified as argument by invoking the Unix shell. The shell executes the command in the background, and passes the result to the calling program (-1 in case of an error, the value returned by the main function of program specified as command otherwise). To be able to use the system function, you need a working shell in the first place. Our ultimate goal is to write a shell from scratch, we'll have to assume that the system function is unavailable to us.

Your own mysystem function

Let us write our own mysystem function with the prototype:

   void mysystem(const char * command);
The mysystem function should execute the command supplied as an argument. If the command fails, the function should print an error message. Standard C offers a system function called execvp that searches for a supplied command in all directories listed in the PATH environment variable, and if found, it executes the command:
    execvp(char * cmd, char * cmdargv[]);
Here cmd is the command to be executed, and cmdargv is an array of strings representing the arguments for cmd. By convention, the first string in cmdargv is the name of the file to be executed. The function execvp returns to the calling program only if there is an error, such as not being able to find the executable. After a successful execvp, there is no return to the calling program.

Implementation guidelines for mysystem(char * command):

  1. Tokenize command using strtok with delimiter string " \n" (which includes the space character, the newline character, and the null character by default). You will need to save each command token into an array of strings, say cmdargv. For example, if command is "gcc mysh.c -o sh", then cmdargv should look as in the diagram below.

  2. The name of the program to execute is the first token in the command. You saved it in cmdargv[0]. To execute this command, spawn a child process using fork. The child process will be an exact copy of the calling process. The child process may now invoke execvp to replace its own image with the image of the command to be executed:
        execvp(cmdargv[0], cmdargv);
    
    If the executable cmdargv[0] exists, then execvp succeeds and your child process ends (because the child code, which included the loop, gets overwritten by the code of the executable cmdargv[0]). If the executable cmdargv[0] does not exist, then the child continues with the instruction following the execvp call.

  3. The parent waits for the child to finish execution, then returns (so the function mysystem ends).

Submission Instructions

This work will be completed in class. There are no handins for this activity.