diff --git a/Assignment-06/starter/dshlib.c b/Assignment-06/starter/dshlib.c index b9115371cb17fa65d1c206f492ab4f5068dbea58..1216a77b0674d138679bafc129f89651e522c0b6 100644 --- a/Assignment-06/starter/dshlib.c +++ b/Assignment-06/starter/dshlib.c @@ -6,10 +6,11 @@ #include <unistd.h> #include <fcntl.h> #include <sys/wait.h> +#include <errno.h> #include "dshlib.h" - +// Function to build a command buffer from a command line int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff) { if (cmd_line == NULL || cmd_buff == NULL) { return ERR_CMD_OR_ARGS_TOO_BIG; @@ -25,11 +26,11 @@ int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff) { char *ptr = original_line; char *arg_start = NULL; - bool in_quotes = false;//For quotes + bool in_quotes = false; // For quotes char quote_char = '\0'; while (*ptr != '\0') { - if ((isspace((unsigned char)*ptr) && !in_quotes)) { + if ((isspace((unsigned char)*ptr) && !in_quotes) { if (arg_start != NULL) { *ptr = '\0'; cmd_buff->argv[cmd_buff->argc++] = arg_start; @@ -59,18 +60,11 @@ int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff) { cmd_buff->argv[cmd_buff->argc] = NULL; - // Debugging output to verify parsing - // printf("Parsed command:\n"); - // for (int i = 0; i < cmd_buff->argc; i++) { - // printf(" argv[%d]: %s\n", i, cmd_buff->argv[i]); - // } - - - return OK; } -char *trim_whitespace(char *str) { //Had to make a new function for readability +// Function to trim whitespace from a string +char *trim_whitespace(char *str) { char *end; while (isspace((unsigned char)*str)) str++; @@ -82,15 +76,13 @@ char *trim_whitespace(char *str) { //Had to make a new function for readability end = str + strlen(str) - 1; while (end > str && isspace((unsigned char)*end)) end--; - // new null terminator + // New null terminator *(end + 1) = '\0'; return str; } - - - +// Function to parse a pipeline of commands int parse_pipeline(const char *cmd_line, command_list_t *clist) { if (cmd_line == NULL || clist == NULL) { return ERR_CMD_OR_ARGS_TOO_BIG; @@ -100,7 +92,8 @@ int parse_pipeline(const char *cmd_line, command_list_t *clist) { if (line_copy == NULL) { return ERR_MEMORY; } - //Parsing using pipe. + + // Parsing using pipe clist->num = 0; char *saveptr; char *command = strtok_r(line_copy, "|", &saveptr); @@ -126,22 +119,11 @@ int parse_pipeline(const char *cmd_line, command_list_t *clist) { return OK; } - - +// Function to execute a pipeline of commands int execute_pipeline(command_list_t *clist) { int num_commands = clist->num; int pipefd[2 * (num_commands - 1)]; pid_t pids[num_commands]; - - // for (int i = 0; i < clist->num; i++) { - // printf("Command %d:\n", i); - // for (int j = 0; clist->commands[i].argv[j] != NULL; j++) { - // printf(" argv[%d]: %s\n", j, clist->commands[i].argv[j]); - // } - // } - - - for (int i = 0; i < num_commands - 1; i++) { if (pipe(pipefd + 2 * i) == -1) { @@ -159,7 +141,7 @@ int execute_pipeline(command_list_t *clist) { } if (pids[i] == 0) { // Child process - // if not first moves input + // Redirect input if not the first command if (i > 0) { if (dup2(pipefd[2 * (i - 1)], STDIN_FILENO) == -1) { perror("dup2 stdin"); @@ -175,7 +157,7 @@ int execute_pipeline(command_list_t *clist) { } } - // Close all pipe file descriptors. Very important stuff + // Close all pipe file descriptors for (int j = 0; j < 2 * (num_commands - 1); j++) { close(pipefd[j]); } @@ -187,12 +169,12 @@ int execute_pipeline(command_list_t *clist) { } } - // Closes all pipe + // Close all pipe file descriptors in the parent for (int i = 0; i < 2 * (num_commands - 1); i++) { close(pipefd[i]); } - // waitpid so everyhting is smooth + // Wait for all child processes to finish for (int i = 0; i < num_commands; i++) { int status; waitpid(pids[i], &status, 0); @@ -206,137 +188,90 @@ int execute_pipeline(command_list_t *clist) { return OK; } -static void start_server() { - int listen_socket; - int ret; - -} - -/**** - **** FOR REMOTE SHELL USE YOUR SOLUTION FROM SHELL PART 3 HERE - **** THE MAIN FUNCTION CALLS THIS ONE AS ITS ENTRY POINT TO - **** EXECUTE THE SHELL LOCALLY - **** - */ - -/* - * Implement your exec_local_cmd_loop function by building a loop that prompts the - * user for input. Use the SH_PROMPT constant from dshlib.h and then - * use fgets to accept user input. - * - * while(1){ - * printf("%s", SH_PROMPT); - * if (fgets(cmd_buff, ARG_MAX, stdin) == NULL){ - * printf("\n"); - * break; - * } - * //remove the trailing \n from cmd_buff - * cmd_buff[strcspn(cmd_buff,"\n")] = '\0'; - * - * //IMPLEMENT THE REST OF THE REQUIREMENTS - * } - * - * Also, use the constants in the dshlib.h in this code. - * SH_CMD_MAX maximum buffer size for user input - * EXIT_CMD constant that terminates the dsh program - * SH_PROMPT the shell prompt - * OK the command was parsed properly - * WARN_NO_CMDS the user command was empty - * ERR_TOO_MANY_COMMANDS too many pipes used - * ERR_MEMORY dynamic memory management failure - * - * errors returned - * OK No error - * ERR_MEMORY Dynamic memory management failure - * WARN_NO_CMDS No commands parsed - * ERR_TOO_MANY_COMMANDS too many pipes used - * - * console messages - * CMD_WARN_NO_CMD print on WARN_NO_CMDS - * CMD_ERR_PIPE_LIMIT print on ERR_TOO_MANY_COMMANDS - * CMD_ERR_EXECUTE print on execution failure of external command - * - * Standard Library Functions You Might Want To Consider Using (assignment 1+) - * malloc(), free(), strlen(), fgets(), strcspn(), printf() - * - * Standard Library Functions You Might Want To Consider Using (assignment 2+) - * fork(), execvp(), exit(), chdir() - */ - - int exec_remote_cmd_loop(char *address, int port) { - char cmd_buff[RDSH_COMM_BUFF_SZ]; - char rsp_buff[RDSH_COMM_BUFF_SZ]; - int cli_socket; - ssize_t recv_bytes; - int is_eof; - - // Connect to the server - cli_socket = start_client(address, port); - if (cli_socket < 0) { - perror("start client"); - return client_cleanup(cli_socket, NULL, NULL, ERR_RDSH_CLIENT); - } +// Main loop for executing local commands +int exec_local_cmd_loop() { + char cmd_buff[SH_CMD_MAX]; + int rc = OK; while (1) { - printf("rsh> "); - if (!fgets(cmd_buff, RDSH_COMM_BUFF_SZ, stdin)) { - break; // Exit on input error or EOF + printf("%s", SH_PROMPT); + + if (fgets(cmd_buff, SH_CMD_MAX, stdin) == NULL) { + printf("\n"); + break; } - // Remove the newline character from the command - size_t cmd_len = strlen(cmd_buff); - if (cmd_len > 0 && cmd_buff[cmd_len - 1] == '\n') { - cmd_buff[cmd_len - 1] = '\0'; + cmd_buff[strcspn(cmd_buff, "\n")] = '\0'; + + if (strlen(cmd_buff) == 0) { + printf("%s\n", CMD_WARN_NO_CMD); + rc = WARN_NO_CMDS; + continue; } - // Handle local commands (e.g., cd, exit) - if (strncmp(cmd_buff, "cd", 2) == 0) { - // Handle cd locally + 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; + } + + rc = execute_pipeline(&cmd_list); + + // Free memory for each command's buffer + 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 (cmd.argc == 1) { - chdir(getenv("HOME")); - } else if (cmd.argc == 2) { - if (chdir(cmd.argv[1]) != 0) { - perror("cd"); + + if (strcmp(cmd.argv[0], EXIT_CMD) == 0) { + free(cmd._cmd_buffer); + 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); } - } else { - fprintf(stderr, "%s\n", CMD_ERR_PIPE_LIMIT); + free(cmd._cmd_buffer); + continue; } - free(cmd._cmd_buffer); - continue; - } else if (strncmp(cmd_buff, "exit", 4) == 0) { - break; // Exit the remote shell - } - // Send the command to the server - ssize_t sent_bytes = send(cli_socket, cmd_buff, strlen(cmd_buff), 0); - if (sent_bytes < 0) { - perror("send"); - return client_cleanup(cli_socket, NULL, NULL, ERR_RDSH_COMMUNICATION); - } + 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); - // Receive the server's response - is_eof = 0; - while ((recv_bytes = recv(cli_socket, rsp_buff, RDSH_COMM_BUFF_SZ, 0)) > 0) { - printf("%.*s", (int)recv_bytes, rsp_buff); - if (rsp_buff[recv_bytes - 1] == RDSH_EOF_CHAR) { - is_eof = 1; - break; + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + fprintf(stderr, "Command failed with exit code %d\n", WEXITSTATUS(status)); + } + } } - } - if (recv_bytes < 0) { - perror("recv"); - return client_cleanup(cli_socket, NULL, NULL, ERR_RDSH_COMMUNICATION); - } else if (recv_bytes == 0) { - printf("Server closed the connection\n"); - break; + free(cmd._cmd_buffer); } } - return client_cleanup(cli_socket, NULL, NULL, OK); -} + return rc; +} \ No newline at end of file