Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Design a C program to serve as a shell interface that accepts user commands and

ID: 3673559 • Letter: D

Question

Design a C program to serve as a shell interface that accepts user commands and then executes each command in a separate process. A shell interface gives the user a prompt, after which the next command is entered. The format of the prompt is: FirstName-L3SID>

Assuming your first name is demo and L3SID is 123, and the user’s next command is cat prog.c, so you would enter: demo-123> cat prog.c

The shell process first prints the prompt, reads what the user enters on the command line (in the above case, cat prog.c), and then creates a separate child process that performs the command. Unless otherwise specified, the shell (parent) process waits for the child to exit before printing the next prompt and continuing. However, UNIX shells typically also allow the child process to run in the background, or concurrently. To accomplish this, we add an ampersand (&) at the end of the command. Thus, if we rewrite the above command as: demo-123> cat prog.c &, the shell (parent) and child processes will run concurrently. In both cases the separate child process is created using the fork() system call, and the user’s command is executed using one of the system calls in the exec() family. For any command running in the background, the shell (parent) process does not wait for the completion of the child process; the shell prints the prompt and reads the next command immediately.

A C program that provides the general operations of a command-line shell is as follows.

            #include <stdio.h>

#include <unistd.h>

            #include <time.h>

            #define MAXLINE 80 /* The maximum length command */

            int main(void)

            {

                        char *args[MAXLINE/2 + 1];           /* command line with max 40 arguments */

                        int should_run = 1;               /* flag to determine when to exit program */

                        printf("CS149 Shell from FirstName LastName ");       /* replace w/ name */

                        while (should_run) {

                                    printf("FirstName-L3SID>");           /* prompt- replace FirstName and L3SID */

                                  fflush(stdout);

                                    /* After reading user input, the steps are:

                                    * (1) fork a child process using fork()

                                    * (2) the child process will invoke execvp()

                                    * (3) if command included &, parent will NOT invoke wait()

                                      */

                                    }

                                    return 0;

}

The main() function presents the prompt and outlines the steps to be taken after input from the user has been read. The main() function continually loops as long as should_run equals 1; when the user enters exit at the prompt, your shell will set should_run to 0 and terminate.

You should modify the main() function so that a child process is forked and executes the command specified by the user. This will require parsing what the user has entered into separate tokens and storing the tokens in an array of character strings args. For example, if the user enters the command

ps –af at the prompt, the values stored in the args array are:

     args[0] = "ps"

     args[1] = "-af"

     args[2] = NULL

This args array will be passed to the execvp() function, which has the following prototype:

            execvp(char *command, char *params[]);

Here, command represents the command to be performed and params stores the parameters to this command. The execvp() function should be invoked as execvp(args[0], args). Be sure to check whether the user included an & to determine whether or not the shell (parent) process is to wait for thechild to exit.

When fork() or execvp() failed, the shell should print out error messages, output the prompt and accept the next command from user.

The shell does not need to support any additional functions such as pipe, and I/O redirection.

Compile your program with “gcc –o shell shell.c”. You can execute the program with “./shell”.

Explanation / Answer

Hi below i have explained a example program in C to serve as a shell interface that accepts user commands and then executes each command in a separate process. A shell interface gives the user a prompt, after which the next command is entered.

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include <string.h>

#include <sys/wait.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <signal.h>

#include <limits.h>

#include <errno.h>

#define CMD_HISTORY_SIZE 10

static char * cmd_history[CMD_HISTORY_SIZE];

static int cmd_history_count = 0;

static void exec_cmd(const char * line)

{

char * CMD = strdup(line);

char *params[10];

int argc = 0;

   params[argc++] = strtok(CMD, " ");

while(params[argc-1] != NULL){

       params[argc++] = strtok(NULL, " ");

   }

   argc--;

int background = 0;

if(strcmp(params[argc-1], "&") == 0){

       background = 1;

       params[--argc] = NULL;

   }

int fd[2] = {-1, -1};

while(argc >= 3){

      

       if(strcmp(params[argc-2], ">") == 0){   // output

           fd[1] = open(params[argc-1], O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP|S_IWGRP);

           if(fd[1] == -1){

               perror("open");

               free(CMD);

               return;

           }

           params[argc-2] = NULL;

           argc -= 2;

       }else if(strcmp(params[argc-2], "<") == 0){ // input

           fd[0] = open(params[argc-1], O_RDONLY);

           if(fd[0] == -1){

               perror("open");

               free(CMD);

               return;

           }

           params[argc-2] = NULL;

           argc -= 2;

       }else{

           break;

       }

   }

int status;

   pid_t pid = fork();

switch(pid){

       case -1:

           perror("fork");

           break;

       case 0:   // child

           if(fd[0] != -1){

               if(dup2(fd[0], STDIN_FILENO) != STDIN_FILENO){

                   perror("dup2");

                   exit(1);

               }

           }

           if(fd[1] != -1){

               if(dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO){

                   perror("dup2");

                   exit(1);

               }

           }

           execvp(params[0], params);

           perror("execvp");

           exit(0);

       default: // parent

           close(fd[0]);close(fd[1]);

           if(!background)

               waitpid(pid, &status, 0);

           break;

   }

   free(CMD);

}

static void add_to_history(const char * cmd){

if(cmd_history_count == (CMD_HISTORY_SIZE-1)){

       int i;

       free(cmd_history[0]);

      

      

       for(i=1; i < cmd_history_count; i++)

           cmd_history[i-1] = cmd_history[i];

       cmd_history_count--;

   }

   cmd_history[cmd_history_count++] = strdup(cmd);

}

static void run_from_history(const char * cmd){

int index = 0;

if(cmd_history_count == 0){

       printf("No commands in history ");

       return ;

   }

if(cmd[1] == '!')

       index = cmd_history_count-1;

else{

       index = atoi(&cmd[1]) - 1;

       if((index < 0) || (index > cmd_history_count)){

           fprintf(stderr, "No such command in history. ");

           return;

       }

   }

   printf("%s ", cmd);

   exec_cmd(cmd_history[index]);

}

static void list_history(){

int i;

for(i=cmd_history_count-1; i >=0 ; i--){

       printf("%i %s ", i+1, cmd_history[i]);

   }

}

static void signal_handler(const int rc){

switch(rc){

       case SIGTERM:

       case SIGINT:

           break;

      

       case SIGCHLD:

          

          

           while (waitpid(-1, NULL, WNOHANG) > 0);

           break;

   }

}

// main

int main(int argc, char *argv[]){

struct sigaction act, act_old;

   act.sa_handler = signal_handler;

   act.sa_flags = 0;

   sigemptyset(&act.sa_mask);

if(   (sigaction(SIGINT, &act, &act_old) == -1) ||

       (sigaction(SIGCHLD, &act, &act_old) == -1)){ // Ctrl^C

       perror("signal");

       return 1;

   }

   size_t line_size = 100;

char * line = (char*) malloc(sizeof(char)*line_size);

if(line == NULL){

       perror("malloc");

       return 1;  

   }

int inter = 0;

while(1){

       if(!inter)

           printf("mysh > ");

       if(getline(&line, &line_size, stdin) == -1){

           if(errno == EINTR){

               clearerr(stdin);

               inter = 1;

               continue;

           }

           perror("getline");

           break;

       }

       inter = 0;

       int line_len = strlen(line);

       if(line_len == 1){

           continue;

       }

       line[line_len-1] = '';

      

       if(strcmp(line, "exit") == 0){

           break;

       }else if(strcmp(line, "history") == 0){

           list_history();

       }else if(line[0] == '!'){

           run_from_history(line);

       }else{

           add_to_history(line);

           exec_cmd(line);

       }

   }

   free(line);

return 0;

}