In this project, we’ll implement a small shell(smash) that supports a build-in commands as well as a support for external commands. However, we also added 2 custom signal handlers for both ctrl+c and ctrl+z.
please follow this link to see the full code of the project: https://github.com/ThunderWiring/Smash
Code Design and Data Structure
In order to make it easy for implementing the processes handling, we assumed that there at most 10 processes run in the background. Thus it was a good idea to use a static array as the primary data structure.
We divided the project to 4 parts:
Smash: Driver of the whole project, it includes the main() function as well. Reads the text commands from the user and calls the right functions to implement them
Commands: API that provides the right tools for executing the build-in commands which been decided earlier for the smash to support, in addition to executing any external commands that bash supports in general.
Signals: API that contains the custom signal handlers for both ctrl+c and ctrl+z.
JobsArray: ADT that contains the structs which describe a single process.
As stated above, we used an array in order to manage the 10 different process that runs in the background. An array was choose, since we already assumed that there are at most 10 processes which can run in the background. In addition, we don’t need to allocate a memory for the array in the heap, but it’s recommended to use a static one, because we don’t need to clone it.
Those are variables which are located in commands.h and recognized by all the other sources in the programs. They serve the purpose of providing all the info regarding the process running in the foreground.
char* processName: Name of the proccess
int ID: The serial number of the process, ranging from 1 to 10
int pID: PID number of the process
int suspended: Has 2 values: 0 for activated, 1 for suspended.
- Bool done: Has 2 values: true: if the process done running. False: if the process still in the background. Note that the purpose of this field is to indicate whether if we can overwrite the process which had done = true when inserting a new process.
- time_t creationTime: When the process had been created since the program starts running.
API of the data structure:
The JobsArray supports the following functions:
- void init(Job* jobs): Initialize the 10 cells of the array to a ‘done’ processes so that we can overwrite their instances.
- void printJobs(Job* jobs): prints the jobs which run in the background in a certain format which oncludes the ID, PID, time it’s been waiting.
- void cleanDoneJobs(Job* jobs): removes the jobs that finished theie execution from the array.
- int getSize(Job* jobs): number of the background processes.
- void insertJob: Adding a job to the array by overwriting a cell of a done running process.
Auxiliary methods in commands.c
- static void aux_run_fg: Runs a process from the background jobs array on the foreground. This function receives the index of the job’s cell in the array.
- static int aux_get_last_bg_process: Gets the index of the process’s cell which was entered recently to the background jobs array. This function receives the index of the job’s cell in the array.
- static int aux_get_last_suspended: Gets the index of the process’s cell which was suspended recently in the background jobs array.
- static void aux_resume_suspended: Resumes the process in the given cell index in the jobs array. This function receives the index of the job’s cell in the array.
Note: The above functions were made static because they are not part of the API, so we capsulated them from external use by declaring them as ‘static’.
As a part of the program, we provided two custom functions which handle the reception of the following signals: ctrl+c and ctrl+z. Those handlers(their declaration and implementation) sit in the signals API. In addition, we provided also another method called ‘setSignal’ which recognize the reception of those two supported signals and sets the handlers for them to the custom methods which we’ve written using the Linux function ‘sigaction’
How does the program works? Basically, it is a regular program that reads an input(mainly string) from the user, then interpret it and provide the expected output. We compile the code using the ‘GCC’ compiler which comes with Linux, then run the exactable code in order to be able to type regular bash code. The program reads the bash commands from the user by using the C – library function ‘fgets()’ in ‘while(1)’ loop, then removes all the preset delimiters and store it in a buffer to process by a function of the commands API, which we described earlier.