Shell Program

Goal

My program serves as a command line interface for users to interact with the operating system, similar in functionality to Linux OS. Here are the functionalities that it accomplishes:

  1. Reads commands from the user
  2. Parses the commands
  3. Executes commands and creates new processes

It is possible to execute single commands or to chain multiple commands together. Input to files and from files can be redirected as output to other files or to the command line. Processes can be run in the foreground or in the background so that the user can input other commands during execution. Finally, the program handles process controls like defunct (zombie) process cleanup and manages memory so that there are no leaks. It will also inform the user of improper command inputs and how to adjust commands for execution.

How it's Accomplished

Parse and Validate: The prompt "mysh: " appears and awaits user input. The parse() function takes in the entered command line and divides it into tokens. These tokens are words and operators that are separated by a space. The tokens are put into an array as strings that can be processed sequentially for parsing logic within the syntax_error() function. Some basic checks that the syntax_error() function performs are ensuring that file names exist for redirection and that background execution is initiated with the '&' operator at the end of the command line.

Building the Command: The create_set() function turns the tokenized array into a CommandSet object. The CommandSet object holds the necessary information to execute each command: command, argument, and redirection information. Commands and their arguments are separated by the '|' (pipe) operator. create_set() also performs additional syntax checks that apply to longer commands such as verifying that only the beginning contains input redirection and only the ending contains output redirection. Other checks include double output redirection and prohibiting append with output redirection.

Process Handling and Execution: The main() function performs process execution and handling in either the foreground or background. The execution phase follows the same pattern for both: iterate through the commands within the CommandSet, create pipes as necessary for the output of the current command to be directed as input for the next, fork a child process, and setup the file descriptors in the child. File descriptors are setup for the current command using dup2() which delineates the input and/or output information. The first command sets output, middle commands set input and output, and the last command sets input. Once all of the command information is set for execution, execvp() is called for the command and its arguments to replace the child process with the updated program state.

Defunct Process Cleanup: For background processes, the parent process will close its copy of file descriptors and continue to the next command without calling wait(). This is what enables processes to run in the background. When executing foreground processes, the parent calls wait() to cleanup the child when finished. However, if there is a completed background process ID in the queue ahead of the foreground process, it will cleanup those until the foreground process is reached.

Logic

data
train
scores