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

Write your own shell, called myShell. This will work like the Bash shell you are

ID: 3692550 • Letter: W

Question

Write your own shell, called myShell. This will work like the Bash shell you are used to using, prompting for a command line and running commands, but it will not have many of the special features of the Bash shell.

Here are the exec() and fork():

http://linux.die.net/man/2/fork

Overview In this assignment you will write your own shell, called myShell. This will work like the Bash shell you are used to using, prompting for a command line and running commands, but it will not have many of the special features of the Bash shell. * Your shell will support the following two built in commands: o exit Command Line * The general syntax of a command line is command [argl arg2 . ..1 where items in square brackets are optional Command lines may be 100 characters, with at most 20 arguments. » Use ": " as a prompt for each command line. Command Executiorn » For executing commands use: o forkO to start new processes o dup20 to duplicate file descriptors o exec???0 (there are several versions of exec) to execute new programs o waitpid) You will need to use waitpid to check for completed background processes. Read about the WNOHANG option of waitpid. It might be useful! » Your shell should use the PATH variable to look for commands, and it should allow shell scripts to be executed. The right version of the exec function will do this for you automatically. » If a command fails because the shell could not find the command to run, then the shell will print an error message. · Your shell will wait for the completion of commands before prompting for a new command » A blank line (one without any commands) should do nothing; ·Built-in command specifications o exit exit The exit command exits the shell. It takes no arguments. If arguments are supplied, then they are ignored cd [dir] Purpose: The cd command changes directories. when called with no arguments, it changes to the directory specified in the HOME environment variable You can get your process' value of HOME with getenv("HOME"). You can change directories with chdir0 It can also take one argument, the path of the directory to change to. If it is passed more than one argument, the extra arguments are ignored. For example: cd dir1 dir2 Will have the same effect as: cd dir1

Explanation / Answer

myShell.h

#include <stdio.h>

#include <errno.h>

#include <ctype.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <signal.h>

#include <sys/wait.h>

#include <fcntl.h>

#include <sys/stat.h>

#include <string.h>

#define MAX_LENGTH 100

#define MAX_TOKENS 20

/*Function Prototypes*/

/* parseCommandLine()

* This function gets a line as input from the user using getline() and parses

* the line into tokens. The parsed tokens will be put into 2D char array

* "tokens" which is sent as the only argument to the function.

* Check "parseCommandLine.c" for more information

*/

void* parseCommandLine(char **tokens);

int do_command(char **arg,int in, int out, int pp);

int do_background(char **arg, int in, int out, int bg);

/*DO NOT CHANGE ANYTHING ABOVE THIS LINE*/

myShell.c

#include "myShell.h"

void *parseCommandLine ( char *tokens[]);

main(){

char *buff[20];

char *inputString;

int status,input_idx,output_idx,piping_idx,back_idx,count;

int dead;

   sigset_t newSet, origSet;

   sigset_t set;

   sigfillset( &newSet );

   struct sigaction original_act, new_act;

   new_act.sa_handler = SIG_IGN;

   new_act.sa_flags = 0;

   new_act.sa_mask = set;

//Loop forever

while(1) {

       //Print out the prompt and get the input

       sleep (1);  

       while ((dead=waitpid(-1,&status,WNOHANG)) > 0){

           if (WIFEXITED(status)){

               printf("Child %d exited due to %d ",dead,status);

           }

           else if (WIFSIGNALED(status)){

               printf("Child %d terminated due to %d ",dead,status);

           }

       }

       fprintf (stdout, "myShell> ");

       sigaction(SIGINT, &new_act, NULL);

       inputString = parseCommandLine(buff);

       count = 0;

   input_idx = 0;

   output_idx = 0;

   piping_idx = 0;

   back_idx = 0;

       if(buff[0] == NULL) {

           continue;

       }

       else if (strcmp (buff[0],"#") == 0){

           continue;

       }

       else if(strcmp(buff[0], "exit") == 0) {

return 0;

}

       else if(strcmp(buff[0], "cd") == 0){

           if(buff[1] == NULL){

               chdir(getenv("HOME"));  

           }

           else{

   chdir(buff[1]);

           }

       }

       else if ( buff [0] != NULL ){

          while ( buff[count] != NULL ){

       if ( strcmp(buff[count], "<") == 0 ){

       input_idx = count;

       buff[count] = NULL;

       }  

       else if (strcmp(buff[count], ">") == 0 ){

       output_idx = count ;

       buff[count] = NULL;

       }      

       else if (strcmp(buff[count], "|") == 0 ){

       piping_idx = count;

       buff[count] = NULL;

       }  

       else if (strcmp(buff[count], "&") == 0 ){

       back_idx = count;

       buff[count] = NULL;

       }

       count ++;

       }

           if (back_idx == 0 ){

               do_command (buff,input_idx,output_idx,piping_idx);

           }

           else if (back_idx != 0 ){

               do_background (buff, input_idx, output_idx, back_idx);

           }

       }

       free(inputString);

   }

}

int do_command(char **args, int in, int out, int pp){

int statusL,statusR,count,i;

int input,output,piping,back;

   pid_t childL,childR;

   sigset_t newSet, origSet;

   sigset_t set;

   sigfillset( &newSet );

    struct sigaction original_act, new_act;

    new_act.sa_handler = SIG_DFL;

    new_act.sa_flags = 0;

   new_act.sa_mask = set;

   input = in;

   output = out;

   piping = pp;

   back = 0;

int pipefd[2],fd_input,fd_output;

if ( input !=0 && output != 0 && piping != 0 ){ // command < file | command > file

       pipe(pipefd);

       childL = fork();

       if ( childL < 0 ){

           perror ("fork error");

       }

       else if ( childL == 0 ){ // Left part

           sigaction(SIGINT, &new_act, &original_act);

           fd_input = open(args[input+1],O_RDONLY);

           dup2 (fd_input,0); // Open file, do command and output to the pipefd[1]

           close(pipefd[0]);

           dup2 (pipefd[1],1);

           close(pipefd[1]);

           execvp(args[0],args);

           close(fd_input);

       }

       else {

           childR = fork();

           if (childR == -1 ){

               perror ("fork failed!");

           }

           else if ( childR == 0){ // Righ part

               sigaction(SIGINT, &new_act, &original_act);

               fd_output = open(args[output+1],O_CREAT|O_WRONLY,0600);

               if ( fd_output == -1 ){

                   perror (" Open out redirection file error ");

               }

               close (pipefd[1]); //Open output file, do command and output to the fd_output

               sleep (1);

               dup2 (pipefd[0],0);

               close (pipefd[0]);

               dup2 (fd_output,1);

               execvp(args[piping+1],args+piping+1);

               close(fd_output);

           }

           close(pipefd[0]);

           close(pipefd[1]);

           waitpid(childL,&statusL,0); // Parent wait for the children

       }

   }

if ( input == 0 && output != 0 && piping != 0 ){           /*command | command > file*/

       pipe(pipefd);

       childL = fork ();

       if ( childL == -1){

           perror ("fork failed!");

       }

       else if ( childL == 0 ){                   /*Left part*/

           sigaction(SIGINT, &new_act, &original_act);

           close(pipefd[0]);

           dup2 (pipefd[1],1);                   /*Do the command and output to pipefd[1]*/

           close(pipefd[1]);

           execvp(args[0],args);

       }

       else{

           childR = fork();

           if (childR == -1){

               perror ("fork failed!");

           }

           else if (childR == 0 ){                   /*Right part*/

               sigaction(SIGINT, &new_act, &original_act);

               fd_output = open (args[output+1],O_WRONLY|O_CREAT,0600);

               dup2 (fd_output,1);

               close(pipefd[1]);               /*Open the output file, do command and out redirection to the fd_output*/

               sleep(1);

               dup2 (pipefd[0],0);

               close(pipefd[0]);

               execvp(args[piping+1],args+piping+1);

           }

           close (pipefd[0]);

           close (pipefd[1]);

           waitpid(childL,&statusL,0);               /*Prents wait for children*/

       }

   }

if (input != 0 && output == 0 && piping !=0 ){               /*command < file | command*/

       pipe(pipefd);

       childL = fork();

       if (childL == -1){

           perror ("fork failed!");

       }

       else if (childL == 0){                       /*Left part*/

           sigaction(SIGINT, &new_act, &original_act);

           fd_input = open (args[input+1],O_RDONLY);

           dup2 (fd_input,0);

           close(pipefd[0]);                   /*Open input file, do command and output to the pipefd[1]*/

           dup2 (pipefd[1],1);

           execvp(args[0],args);

           close(fd_input);

       }

       else {

           childR = fork();

           if (childR == -1 ){

               perror ("fork failed! ");

           }  

           else if (childR == 0){                   /*Right part*/

               sigaction(SIGINT, &new_act, &original_act);

               close(pipefd[1]);

               dup2 (pipefd[0],0);

               close(pipefd[0]);

               execvp(args[piping+1],args+piping+1);       /*Do the command and outout to the screen*/  

           }

           close(pipefd[0]);

           close(pipefd[1]);

           waitpid(childL,&statusL,0);

       }

   }

if (input == 0 && output == 0 && piping != 0 && back == 0){               /*command | command*/

       pipe(pipefd);

       childL = fork ();

       if (childL == -1 ){

           perror ("fork failed! ");

       }

       else if (childL == 0){                       /*Left part*/

           sigaction(SIGINT, &new_act, &original_act);

           close(pipefd[0]);

           dup2 (pipefd[1],1);

           close(pipefd[1]);                   /*Do the command and output to the pipefd[1]*/

           execvp(args[0],args);

       }

       else{

           childR = fork();

           if (childR == -1){

               perror ("fork failed! ");

           }

           else if (childR == 0){                   /*Right part*/

               sigaction(SIGINT, &new_act, &original_act);

               close(pipefd[1]);

               dup2(pipefd[0],0);               /*Do the command and ouput to the screen*/

               close(pipefd[0]);

               execvp(args[piping+1],args+piping+1);

           }

           close(pipefd[0]);

           close(pipefd[1]);

           waitpid(childL,&statusL,0);           /*Parent wait for children*/

       }

   }

if ( back == 0 && piping == 0 ){

       if (input == 0 && output == 0){                   /*Just only have command*/

           childL = fork();                  

           if (childL == -1){

               printf ("fork failed! ");

           }

           else if (childL == 0 ){                   /*Do the command and output to the screen*/

               sigaction(SIGINT, &new_act, &original_act);

               execvp (args[0],args);

               exit(-1);

           }

           else{

               waitpid(childL,&statusL,0);       /*Parent wait for child*/

           }

       }

       else if (input != 0 && output == 0){               /*command < file*/

           childL = fork();

           if (childL == -1 ){

               perror ("fork failed! ");  

           }

           else if (childL == 0){                   /*Open the input file, do the command and output to the screen*/

               sigaction(SIGINT, &new_act, &original_act);

               fd_input = open (args[input+1],O_RDONLY);

               dup2(fd_input,0);

               execvp(args[0],args);

           }

           else {

waitpid(childL,&statusL,0); /*Parent wait for child*/

           }

       }

       else if (input == 0 && output != 0){               /*command > file*/

           childL = fork();

if (childL == -1 ){

perror ("fork failed! ");

}

else if (childL == 0){ /*Open the output file, do the command and output to the fd_output*/

sigaction(SIGINT, &new_act, &original_act);

               fd_output = open (args[output+1],O_WRONLY|O_CREAT,0600);

dup2(fd_output,1);

execvp(args[0],args);

}

else {

waitpid(childL,&statusL,0); /*Parent wait for child*/

}

       }

       else{                               /*command < file > file or command >file < file*/

           if ( input < output ){                   /*command < file > file*/      

               childL = fork();

   if (childL == -1 ){

   perror ("fork failed! ");

   }

   else if (childL == 0){ /*Open the input and ouput file, do the command and output to the fd_output*/

   sigaction(SIGINT, &new_act, &original_act);

                   fd_input = open (args[input+1],O_RDONLY);

                   fd_output = open (args[output+1],O_WRONLY|O_CREAT,0600);

   dup2(fd_input,0);

                   dup2(fd_output,1);

   execvp(args[0],args);

   }

   else {

   waitpid(childL,&statusL,0); /*Parent wait for child*/

   }                                              

           }

           else if ( input > output ){                   /*command > file1 < file2*/

childL = fork();

if (childL == -1 ){

perror ("fork failed! ");

}

else if (childL == 0){ /*Open the input and ouput file, do the command and output to the fd_output*/

sigaction(SIGINT, &new_act, &original_act);

                   fd_input = open (args[input+1],O_RDONLY);

fd_output = open (args[output+1],O_WRONLY|O_CREAT,0600);

dup2(fd_input,0);

dup2(fd_output,1);

execvp(args[0],args);

}

else {

waitpid(childL,&statusL,0); /*Parent wait for child*/

}

           }                                      

       }

   }

}

int do_background (char **args, int in, int out, int bg){

int statusL,statusR,count,i;

int input,output,piping,back;

pid_t childL,childR;

input = in;

output = out;

int pipefd[2],fd_input,fd_output;

if (input == 0 && output == 0){                   /*Just only have command*/

       childL = fork();                  

       if (childL == -1){

           printf ("fork failed! ");

       }

       else if (childL == 0 ){           /*Do the command and output to /dev/null*/

           printf("Child %d is running in the background ",getpid());

           fd_input = open("/dev/null",O_RDONLY);

           dup2 (fd_input,0);

           execvp (args[0],args);

           close(fd_input);

       }

   }

else if (input != 0 && output == 0){               /*command < file*/

       childL = fork();

       if (childL == -1 ){

           perror ("fork failed! ");  

       }

       else if (childL == 0){       /*Open the input file, do the command and output to the screen*/

           printf("Child %d is running in the background ",getpid());

           fd_input = open (args[input+1],O_RDONLY);

           dup2(fd_input,0);

           execvp(args[0],args);

           close(fd_input);

       }

   }      

else if (input == 0 && output != 0){               /*command > file*/

       childL = fork();

if (childL == -1 ){

   perror ("fork failed! ");

}  

else if (childL == 0){ /*Open the output file, do the command and output to the fd_output*/

           printf("Child %d is running in the background ",getpid());

           fd_input = open ("/dev/null",O_RDONLY);

   fd_output = open (args[output+1],O_WRONLY|O_CREAT,0600);

           dup2(fd_input,0);

   dup2(fd_output,1);

   execvp(args[0],args);

           close(fd_input);

           close(fd_output);

}

   }

else{                   /*command < file > file or command >file < file*/

       childL = fork();

if (childL == -1 ){

   perror ("fork failed! ");

}

else if (childL == 0){

           printf("Child %d is running in the background ",getpid());

   fd_input = open (args[input+1],O_RDONLY);

           fd_output = open (args[output+1],O_WRONLY|O_CREAT,0600);

dup2(fd_input,0);

          dup2(fd_output,1);

execvp(args[0],args);

}  

   }

}

parseCommandLine.c

#include "myShell.h"

/*Function Definition*/

void* parseCommandLine(char *tokens[])

{

int bytes_read, i=0, token=0;

   size_t nbytes = MAX_LENGTH + 2;/*accounts for end of string '' char*/

char *my_string;

/*malloc enough space for a MAX_LENGTH character line*/

   my_string = (char *)malloc(nbytes + 1);

if(my_string == NULL) /*malloc fails*/

       return NULL;

/*Get the input line from user*/

/*getline() also reads the newline ' ' character*/

   bytes_read = getline (&my_string, &nbytes, stdin);

if(bytes_read == -1) /*If getline() failed*/

   {

       free(my_string); /* free up the space -- no memory leaks!*/

       fprintf(stderr, "getline() failed ");

       return NULL;

   }

if(bytes_read > MAX_LENGTH+1) /*If the input is too long*/

   {

       free(my_string); /* free up the space -- no memory leaks!*/

       fprintf(stderr, "Too many characters in command ");

       return NULL;

   }

do

   {

       /*Truncate space and tab characters*/

       while(my_string[i]==' ' || my_string[i]==' ')

       {

           my_string[i]='';

           i++;

       }

      

       /*If ' ' is reached (end of input)*/

       if(my_string[i]==' ')

       {

           if(token > MAX_TOKENS) /*Too many number of tokens*/

           {

               free(my_string); /* free up the space*/

               fprintf(stderr, "Too many arguments ");

               return NULL;

           }

      

           /*replace ' ' with '' (end of string character*/

           my_string[i]='';

       /*end token string with NULL so that execvp will work*/

           tokens[token]=NULL;

           /* return the pointer to the malloced memory so that

           * call can free it*/

           return my_string;

       }

  

       /*char not a newline. It must be the start of new token.*/

       tokens[token]=&my_string[i];

       token++;

       i++;

       /*get the rest of the characters in this token*/

       /*isgraph() determines if a character is printable and a

       * non-space*/

       while(isgraph(my_string[i]) && my_string[i] != ' ')

       {

           i++;

       }

   }while(1);

}