diff --git a/Assignment-05/questions.md b/Assignment-05/questions.md index b4789bf92878bf1116e24c4c9ea28110d6d5f4e7..67a25161bfa9ce32f95b75ff5ed41e50c84771e5 100644 --- a/Assignment-05/questions.md +++ b/Assignment-05/questions.md @@ -1,15 +1,15 @@ 1. Your shell forks multiple child processes when executing piped commands. How does your implementation ensure that all child processes complete before the shell continues accepting user input? What would happen if you forgot to call waitpid() on all child processes? -_answer here_ +_The shell makes sure that it's all complete by calling waitpid() for each child process. It could cause a incorrect output, mess with the pointers, cause a zombie process to occur, and messy things to happen._ 2. The dup2() function is used to redirect input and output file descriptors. Explain why it is necessary to close unused pipe ends after calling dup2(). What could go wrong if you leave pipes open? -_answer here_ +_Closing pipes is very necessary, it will cause memory leaks, and make the pipes open causing it to read forever, and also make sure that the pipline close correctly._ -3. Your shell recognizes built-in commands (cd, exit, dragon). Unlike external commands, built-in commands do not require execvp(). Why is cd implemented as a built-in rather than an external command? What challenges would arise if cd were implemented as an external process? +3. Your shell recognizes built-in commands (cd, exit, dragon). Unlike external commands, built-in commands do not require execvp(). Why is cd implemented as a _built-in rather than an external command? What challenges would arise if cd were implemented as an external process?_ -_answer here_ +I think cd makes it so that the shell directory itself is changed and not the original directory, it wouldn't change our shell, and would be inefficient. 4. Currently, your shell supports a fixed number of piped commands (CMD_MAX). How would you modify your implementation to allow an arbitrary number of piped commands while still handling memory allocation efficiently? What trade-offs would you need to consider? -_answer here_ +_So we would use dynamic memory instead and create more loops. I think the memory would be really confusing, and could cause lots of performance errors with all of the memory management we would be doing. Lots of error handling to deal with memory allocations, and failed mallocs._ diff --git a/Assignment-05/starter/dshlib.c b/Assignment-05/starter/dshlib.c index f02f23a881d0cbe9b6db77b1f185e20149292d50..a21bbe118fc2e9a07dbce69cd35702cf72f86fe0 100644 --- a/Assignment-05/starter/dshlib.c +++ b/Assignment-05/starter/dshlib.c @@ -53,6 +53,7 @@ int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff) { return OK; } + int execute_pipeline(command_list_t *cmd_list) { int num_commands = cmd_list->num; int pipefd[2]; @@ -73,7 +74,7 @@ int execute_pipeline(command_list_t *cmd_list) { return ERR_MEMORY; } - if (pids[i] == 0) { + if (pids[i] == 0) { if (prev_pipe_read != -1) { dup2(prev_pipe_read, STDIN_FILENO); close(prev_pipe_read); @@ -85,10 +86,21 @@ int execute_pipeline(command_list_t *cmd_list) { close(pipefd[0]); } + if (prev_pipe_read != -1) { + close(prev_pipe_read); + } + cmd_buff_t *cmd = &cmd_list->commands[i]; + + if (cmd->argv[0] == NULL) { + fprintf(stderr, "execvp error: command is NULL\n"); + exit(ERR_EXEC_CMD); + } + execvp(cmd->argv[0], cmd->argv); + perror("execvp"); - exit(EXIT_FAILURE); + exit(ERR_EXEC_CMD); } else { if (prev_pipe_read != -1) { close(prev_pipe_read); @@ -104,13 +116,12 @@ int execute_pipeline(command_list_t *cmd_list) { if (prev_pipe_read != -1) { char buffer[1024]; ssize_t bytes_read; - while ((bytes_read = read(prev_pipe_read, buffer, sizeof(buffer) - 1))) { - if (bytes_read < 0) { - perror("read"); - break; - } + while ((bytes_read = read(prev_pipe_read, buffer, sizeof(buffer) - 1)) > 0) { buffer[bytes_read] = '\0'; - printf("%s", buffer); + printf("%s", buffer); + } + if (bytes_read < 0) { + perror("read"); } close(prev_pipe_read); } @@ -123,6 +134,9 @@ int execute_pipeline(command_list_t *cmd_list) { } + + + int parse_pipeline(char *cmd_line, command_list_t *clist) { if (cmd_line == NULL || clist == NULL) { return ERR_CMD_OR_ARGS_TOO_BIG; @@ -174,73 +188,91 @@ int parse_pipeline(char *cmd_line, command_list_t *clist) { return OK; } -int execute_pipeline(command_list_t *cmd_list) { - int num_commands = cmd_list->num; - int pipefd[2]; - int prev_pipe_read = -1; - pid_t pids[num_commands]; +int exec_local_cmd_loop() { + char cmd_buff[SH_CMD_MAX]; + int rc = OK; - for (int i = 0; i < num_commands; i++) { - if (i < num_commands - 1) { - if (pipe(pipefd) == -1) { - perror("pipe"); - return ERR_MEMORY; - } + while (1) { + printf("%s", SH_PROMPT); + + if (fgets(cmd_buff, SH_CMD_MAX, stdin) == NULL) { + printf("\n"); + break; } - pids[i] = fork(); - if (pids[i] == -1) { - perror("fork"); - return ERR_MEMORY; + cmd_buff[strcspn(cmd_buff, "\n")] = '\0'; + + if (strlen(cmd_buff) == 0) { + printf("%s\n", CMD_WARN_NO_CMD); + rc = WARN_NO_CMDS; + continue; } - if (pids[i] == 0) { - if (prev_pipe_read != -1) { - dup2(prev_pipe_read, STDIN_FILENO); - close(prev_pipe_read); + if (strstr(cmd_buff, "|") != NULL) { + command_list_t cmd_list; + if (parse_pipeline(cmd_buff, &cmd_list) != OK) { + fprintf(stderr, "%s\n", CMD_ERR_PIPE_LIMIT); + rc = ERR_TOO_MANY_COMMANDS; + continue; } - if (i < num_commands - 1) { - dup2(pipefd[1], STDOUT_FILENO); - close(pipefd[1]); - close(pipefd[0]); + if (execute_pipeline(&cmd_list) != OK) { + fprintf(stderr, "%s\n", CMD_ERR_PIPE_LIMIT); + rc = ERR_MEMORY; } - cmd_buff_t *cmd = &cmd_list->commands[i]; - execvp(cmd->argv[0], cmd->argv); - perror("execvp"); - exit(EXIT_FAILURE); - } else { - if (prev_pipe_read != -1) { - close(prev_pipe_read); + for (int i = 0; i < cmd_list.num; i++) { + free(cmd_list.commands[i]._cmd_buffer); + } + } else { + cmd_buff_t cmd; + if (build_cmd_buff(cmd_buff, &cmd) != OK) { + fprintf(stderr, "%s\n", CMD_ERR_PIPE_LIMIT); + rc = ERR_MEMORY; + continue; } - if (i < num_commands - 1) { - prev_pipe_read = pipefd[0]; - close(pipefd[1]); + if (strcmp(cmd.argv[0], EXIT_CMD) == 0) { + free(cmd._cmd_buffer); + break; } - } - } - if (prev_pipe_read != -1) { - char buffer[1024]; - ssize_t bytes_read; - while ((bytes_read = read(prev_pipe_read, buffer, sizeof(buffer) - 1)) { - if (bytes_read < 0) { - perror("read"); - break; + if (strcmp(cmd.argv[0], "cd") == 0) { + if (cmd.argc == 1) { + chdir(getenv("HOME")); + } else if (cmd.argc == 2) { + if (chdir(cmd.argv[1]) != 0) { + perror("cd"); + } + } else { + fprintf(stderr, "%s\n", CMD_ERR_PIPE_LIMIT); + } + free(cmd._cmd_buffer); + continue; } - buffer[bytes_read] = '\0'; - printf("%s", buffer); - } - close(prev_pipe_read); - } - - for (int i = 0; i < num_commands; i++) { - waitpid(pids[i], NULL, 0); + pid_t pid = fork(); + if (pid < 0) { + perror("fork failed"); + rc = ERR_MEMORY; + } else if (pid == 0) { + execvp(cmd.argv[0], cmd.argv); + perror("execvp failed"); + exit(EXIT_FAILURE); + } else { + int status; + wait(&status); + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + fprintf(stderr, "Command failed with exit code %d\n", WEXITSTATUS(status)); + } + } + } + + free(cmd._cmd_buffer); + } } - return OK; + return rc; } -