Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

#include #include #include #include #include #include #include #include smp1_tests.h /* DEFINE SECTION */ #define SHELL_BUFFER_SIZE 256 /* Size of the Shell input buffer */ #define

#include #include #include #include #include #include #include

#include "smp1_tests.h"

/* DEFINE SECTION */ #define SHELL_BUFFER_SIZE 256 /* Size of the Shell input buffer */ #define SHELL_MAX_ARGS 8 /* Maximum number of arguments parsed */

/* VARIABLE SECTION */ enum { STATE_SPACE, STATE_NON_SPACE }; /* Parser states */

int imthechild(const char *path_to_exec, char *const args[]) { // TO-DO P5.1 return execv(path_to_exec, args) ? -1 : 0; }

void imtheparent(pid_t child_pid, int run_in_background) { int child_return_val, child_error_code;

/* fork returned a positive pid so we are the parent */ fprintf(stderr, " Parent says 'child process has been forked with pid=%d' ", child_pid); if (run_in_background) { fprintf(stderr, " Parent says 'run_in_background=1 ... so we're not waiting for the child' "); return; } // TO-DO P5.4 wait(&child_return_val); /* Use the WEXITSTATUS to extract the status code from the return value */ child_error_code = WEXITSTATUS(child_return_val); fprintf(stderr, " Parent says 'wait() returned so the child with pid=%d is finished.' ", child_pid); if (child_error_code != 0) { /* Error: Child process failed. Most likely a failed exec */ fprintf(stderr, " Parent says 'Child process %d failed with code %d' ", child_pid, child_error_code); } }

/* MAIN PROCEDURE SECTION */ int main(int argc, char **argv) { pid_t shell_pid, pid_from_fork; int n_read, i, exec_argc, parser_state, run_in_background; /* buffer: The Shell's input buffer. */ char buffer[SHELL_BUFFER_SIZE]; /* exec_argv: Arguments passed to exec call including NULL terminator. */ char *exec_argv[SHELL_MAX_ARGS + 1]; // TO-DO new variables for P5.2, P5.3, P5.6

/* Entrypoint for the testrunner program */ if (argc > 1 && !strcmp(argv[1], "-test")) { return run_smp1_tests(argc - 1, argv + 1); }

/* Allow the Shell prompt to display the pid of this process */ shell_pid = getpid();

while (1) { /* The Shell runs in an infinite loop, processing input. */

// TO-DO P5.2 fprintf(stdout, "Shell(pid=%d)> ", shell_pid); fflush(stdout);

/* Read a line of input. */ if (fgets(buffer, SHELL_BUFFER_SIZE, stdin) == NULL) return EXIT_SUCCESS; n_read = strlen(buffer); run_in_background = n_read > 2 && buffer[n_read - 2] == '&'; buffer[n_read - run_in_background - 1] = ' ';

// TO-DO P5.3

/* Parse the arguments: the first argument is the file or command * * we want to run. */

parser_state = STATE_SPACE; for (exec_argc = 0, i = 0; (buffer[i] != ' ') && (exec_argc < SHELL_MAX_ARGS); i++) {

if (!isspace(buffer[i])) { if (parser_state == STATE_SPACE) exec_argv[exec_argc++] = &buffer[i]; parser_state = STATE_NON_SPACE; } else { buffer[i] = '\0'; parser_state = STATE_SPACE; } }

/* run_in_background is 1 if the input line's last character * * is an ampersand (indicating background execution). */

buffer[i] = '\0'; /* Terminate input, overwriting the '&' if it exists */

/* If no command was given (empty line) the Shell just prints the prompt again */ if (!exec_argc) continue; /* Terminate the list of exec parameters with NULL */ exec_argv[exec_argc] = NULL;

/* If Shell runs 'exit' it exits the program. */ if (!strcmp(exec_argv[0], "exit")) { printf("Exiting process %d ", shell_pid); return EXIT_SUCCESS; /* End Shell program */

} else if (!strcmp(exec_argv[0], "cd") && exec_argc > 1) { /* Running 'cd' changes the Shell's working directory. */ /* Alternative: try chdir inside a forked child: if(fork() == 0) { */ if (chdir(exec_argv[1])) /* Error: change directory failed */ fprintf(stderr, "cd: failed to chdir %s ", exec_argv[1]); /* End alternative: exit(EXIT_SUCCESS);} */

} else { /* Execute Commands */ /* Try replacing 'fork()' with '0'. What happens? */ pid_from_fork = fork();

if (pid_from_fork < 0) { /* Error: fork() failed. Unlikely, but possible (e.g. OS * * kernel runs out of memory or process descriptors). */ fprintf(stderr, "fork failed "); continue; } if (pid_from_fork == 0) { // TO-DO P5.6

return imthechild(exec_argv[0], &exec_argv[0]); /* Exit from main. */ } else { imtheparent(pid_from_fork, run_in_background); /* Parent will continue around the loop. */ } } /* end if */ } /* end while loop */

return EXIT_SUCCESS; } /* end main() */

1. Modify this project so that you can use 'ls' instead of '/bin/ls' (i.e. the shell searches the path for the command to execute.) Consider the following 3 cases: * The command includes an absolute path, such as /bin/ls * The command starts with a dot, such as ./shell * The command is somewhere in the ENVPATH, such as ls

Test: ./shell -test path

2. Modify this project so that the command prompt includes a counter that increments for each command executed (starting with 1). Your program should use the following prompt format: "Shell(pid=%1)%2> " %1=process pid %2=counter (You will need to change this into a correct printf format) Do not increment the counter if no command is supplied to execute.

Test: ./shell -test counter

3. Modify this project so that '!NN' re-executes the n'th command entered.nYou can assume that NN will only be tested with values 1 through 9, no more than 9 values will be entered. If it goes over 9, loop back to 1 (use a circular queue structure)

Shell(...)1> ls Shell(...)2> !1 # re-executes ls Shell(...)3> !2 # re-executes ls Shell(...)4> !4 # prints "Not valid" to stderr Shell(...)4> !5 # prints "Not valid" to stderr

Test: ./shell -test rerun

4. Modify the project so that it uses waitpid instead of wait. The parent process must wait for its direct child to finish executing before showing the next shell prompt.

5. Create a new builtin command 'sub' that forks the program to create a new subshell. The parent shell should run the imtheparent() function just as if we were running an external command (like 'ls').

./shell Shell(.n1..)1> newsub Shell(.n2..)1> exit # Exits sub shell Shell(.n1..)1> exit # Exits back to 'real' shell

6. Create a new global variable to prevent a subshell from invoking a subshell invoking a subshell (i.e., more than 3 levels deep):

./shell Shell(.n1..)1> newsub Shell(.n2..)1> newsub Shell(.n3..)1> newsub # prints "Too deep!" to stderr

Test: ./shell -test sub

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

Structured Search For Big Data From Keywords To Key-objects

Authors: Mikhail Gilula

1st Edition

012804652X, 9780128046524

Students also viewed these Databases questions

Question

Draw and explain the operation of LVDT for pressure measurement

Answered: 1 week ago