Introduction:

This lab assignment is a further step based on the UNIX Shell interface you built in lab 1.

Screenshot of the code of lab one :

/*
* Command to compile the program is gcc shell.c .
By default it will create a output file
for example a.out
and command to run this file is ./a.out
*
* To test this shell.c I have used the provided instructions.
* To compile : gcc -g shell.c -o mysh
* to run this file: ./mysh
*/
#include < stdio.h >
#include < stdlib.h >
#include < unistd.h >
#include < sys/types.h >
#include < sys/wait.h >
#define MAX_LINE 80
void setup(char inputBuffer[], char *args[],int *background)
{
int length, /* # of characters in the command line */
i, /* loop index for accessing inputBuffer array */
start, /* index where beginning of next command parameter is */
ct; /* index of where to place the next parameter into args[] */
ct = 0;
/* read what the user enters on the command line */
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
start = -1;
if (length == 0)
exit(0); /* ^d was entered, end of user command stream */
if (length < 0){
perror("error reading the command");
exit(-1); /* terminate with error code of -1 */
}
/* examine every character in the inputBuffer */
for (i = 0; i < length; i++) {
switch (inputBuffer[i]){
case ' ':
case '\t' : /* argument separators */
if(start != -1){
args[ct] = &inputBuffer[start]; /* set up pointer */
ct++;
}
inputBuffer[i] = '\0'; /* add a null char; make a C string */
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL; /* no more arguments to this command */
break;
case '&':
*background = 1;
inputBuffer[i] = '\0';
break;
default : /* some other character */
if (start == -1)
start = i;
}
}
args[ct] = NULL; /* just in case the input line was > 80 */
}
int main(void)
{
char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
int background; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE/2+1];/* command line (of 80) has max of 40 arguments */
while (1){ /* Program terminates normally inside setup */
background = 0;
printf("COMMAND- >\n");
setup(inputBuffer, args, &background);
/* the steps are:
(1) fork a child process using fork()
(2) the child process will invoke execvp()
(3) if background == 0, the parent will wait,
otherwise returns to the setup() function. */
/*process identification data type pid_t */
/*calling the fork system call and capture the return value in result*/
pid_t result = fork();
/*fork system call will divide the execution into child and parent processes.*/
if(result < 0){ /*if fork result is less than zero it's error. Execution is failed.*/
perror("Error in Execution"); /*show an error message*/
}
else if(result == 0 ){ /*if return value from fork is 0 then its inside the child process
so invoke execvp() with appropriate parameters*/
printf("this is inside child process");
execvp(args[0],args);
exit(0);
}
/*the current process that is creating the child process is the parent process*/
else{ /*if its greater than zero its the parent process*/
if(background == 0) /*check for the background */
{
wait(NULL); /*parent processes will wait until child process is done
executing */
printf("parent process ends after child process is done");
}
/*else{
setup(inputBuffer, args, &background);
}*/
}
/* get next command */
}

This time, you need to add history feature into your UNIX Shell. The history feature allows users to access up to ten most recently entered commands. These commands will be numbered starting at 1 and will continue to grow larger even past 10, e.g. if the user has entered 35 commands, the ten most recent commands should be numbered 26 to 35.

A user will be able to list ten most recently entered commands when he/she presses < Control >< C >, which sends the SIGINT signal to the shell process via OS kernels. UNIX systems use signals to notify a process that a particular event has occurred. Signals may be either synchronous or asynchronous, depending on the source and the reason for the event being signaled. Once a signal has been generated by the occurrence of a certain event (e.g., division by zero, illegal memory access, user entering < Control >< C >, etc.), the signal is delivered to a process where it must be handled. After receiving a signal, a process may handle it by one of the following techniques:

  • Ignoring the signal
  • Using the default signal handler, or
  • Providing a separate signal-handling function.

Signals may be handled by first setting certain fields in the C structure struct sigaction and then passing this structure to the sigaction() function. Signals are defined in the include file /usr/include/sys/signal.h. For example, SIGINT represents the signal for terminating a program with the control sequence < Control >< C >. The default signal handler for SIGINT is to terminate the program.

Alternatively, a program may choose to set up its own signal-handling function by setting the sa_handler field in struct sigaction to the name of the function which will handle the signal and then invoking the sigaction() function, passing it (1) the signal we are setting up a handler for, and (2) a pointer to struct sigaction.

In Figure 3 we show a C program that uses the function handle_SIGINT() for handling the SIGINT signal. This function prints out the message "Caught Control C" and then invokes the exit() function to terminate the program.

#include < stdio.h >
#include < signal.h >
#include < unistd.h >
#define BUFFER_SIZE 50
static char buffer[BUFFER_SIZE];
/* the signal handler function */
void handle_SIGINT() {
write(STDOUT_FILENO,buffer,strlen(buffer));
exit(0); // This is only for illustration purposes. You may
want to // delete the exit(0) statement for this lab
assignment.
}
int main(int argc, char *argv[])
{
/* set up the signal handler */
struct sigaction handler;
handler.sa_handler =
handle_SIGINT;
sigaction(SIGINT,
&handler, NULL);
strcpy(buffer,"Caught < ctrl >< c >n");
/* wait for < control > < C > */
while (1)
;
return 0;
}

This program will run in the while (1) loop until the user enters the sequence < Control >< C >. When this occurs, the signal-handling function handle_SIGINT() is invoked.

The signal-handling function should be declared above main() and because control can be transferred to this function at any point, no parameters may be passed to it. Therefore, any data that it must access in your program must be declared globally, i.e. at the top of the source file before your function declarations.

If the user enters < Control >< C >, the signal handler will output a list of the most recent 10 commands, followed by the command prompt. With this list, the user can run any of the previous 10 commands by entering r x where 'x' is the first letter of that command. If more than one command starts with x, execute the most recent one. If no command starts with x, print out an error message and continue accepting user inputs. Also, the user should be able to run the most recent command again by just entering r. You can assume that only one space will separate the r and the first letter and that the letter will be followed by \n'. Again, r alone will be immediately followed by the \n character if it is wished to execute the most recent command.

Any command that is executed in this fashion should be echoed on the user's screen and the command is also placed in the history buffer as the next command. (r x does not go into the history list; the actual command that it specifies, though, does.)

Academic Honesty!
It is not our intention to break the school's academic policy. Posted solutions are meant to be used as a reference and should not be submitted as is. We are not held liable for any misuse of the solutions. Please see the frequently asked questions page for further questions and inquiries.
Kindly complete the form. Please provide a valid email address and we will get back to you within 24 hours. Payment is through PayPal, Buy me a Coffee or Cryptocurrency. We are a nonprofit organization however we need funds to keep this organization operating and to be able to complete our research and development projects.