Implementing your own system function

In this programming assignment, you will delve into the details of loading and running executable files. You will be using this piece in a future assignment to extend the features of your smart shell.

You may work alone or with one other classmate, and each person is expected to contribute equally to the effort. Note: if needed, a complete working version of the first part of the smart shell will be provided as a starting point.

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. Given that 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

The main goal of this assignment is to write your own mysystem function with the prototype:

   void mysystem(const char * command);
The mysystem 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 execv that executes the command specified as the first argument:
    execv(char * fullcmd, char * cmdargv[]);
Here fullcmd is the full path for the file (command) to be executed, and cmdargv is an array of strings representing the arguments for fullcmd. By convention, the first string in cmdargv is the name of the file to be executed. The function execv returns to the calling program only if there is an error, such as not being able to find the executable. After a successful execv, there is no return to the calling program.

In building the full command for execv, you will make use of the global variable searchpath built by the function SaveSearchPaths from your previous assignment.

Implementation guidelines for mysystem(char * command):

  1. Tokenize command using the delimiter string " \n" (which includes the null character and the space character and the newline character). 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]. The next step is to find this executable in one of the directories listed in the PATH environment variable. For each such directory, you will need to build the full path for the executable by appending the executable name to the directory name. For example, if the first directory listed in PATH is "/bin", then we try the full path "/bin/gcc". If this command fails, then we continue looking in the second directory, and so on. We will build our full path in a dynamically allocated buffer
        char * fullcmd;
    
    To implement this step, spawn a child process using fork. The child process will be an exact copy of the calling process. The child process scans searchpaths and uses each entry i as follows:
  3. The parent waits for the child to finish execution, then returns (so the function mysystem ends).

Note: You may not use calls to execvp or exceve in this assignment.

Project Specifications

In your csc1600/smartshell create two C source files called smartshell2.c and mysystem.c, and a header file mysystem.h:

Compile your project using the command

    gcc smartshell2.c mysystem.c stringarray.c -o smartshell2
The three source files will be linked into one executable file called smartshell2.

Submission Instructions

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

Have fun!