Skip to content
Snippets Groups Projects
Select Git revision
  • master
1 result

dsh

Blame
  • dshlib.c 11.75 KiB
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdbool.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    
    #include "dshlib.h"
    
    /*
     * 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()
     */
    
    void trim_spaces(char *str) {
    	while (*str && isspace((unsigned char)*str)) {
    		str++;
    	}
    
    	size_t len = strlen(str);
    	while (len > 0 && isspace((unsigned char)str[len - 1])) {
    		str[len - 1] = '\0';
    		len--;
    	}
    }
    
    Built_In_Cmds match_command(const char *input) {
    	if(strcmp(input, EXIT_CMD) == 0) {
    		return BI_CMD_EXIT;
    	}
    	else if(strcmp(input, "dragon") == 0) {
    		return BI_CMD_DRAGON;
    	}
    	else if(strcmp(input, "cd") == 0) {
    		return BI_CMD_CD;
    	}
    	else {
    		return BI_NOT_BI;
    	}
    }
    
    Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd) {
    	if(cmd->argc == 0) {
    		return BI_NOT_BI;
    	}
    
    	if(strcmp(cmd->argv[0], "cd") == 0) {
    		if(cmd->argc == 2) {
    			if(chdir(cmd->argv[1]) == -1) {
    				perror("cd failed");
    			}
    		}
    
    		return BI_CMD_CD;
    	}
    
    	return BI_NOT_BI;
    }
    
    int build_cmd_list(char *cmd_line, command_list_t *clist) {
    	//printf("Before trimming the cmd_line in build_cmd_list: '%s'\n", cmd_line);
    	trim_spaces(cmd_line);	
    	//printf("Trimmed cmd_line: '%s'\n", cmd_line);
    
    	char *cmd_tok_save = NULL;
    	char *cmd_tok = strtok_r(cmd_line, PIPE_STRING, &cmd_tok_save);
    
    	int index = 0;
    
    	while(cmd_tok != NULL) {
    		trim_spaces(cmd_tok);
    
    		//printf("Command %d: '%s'\n", index, cmd_tok);
    
    		if(index >= CMD_MAX) {
    			return ERR_TOO_MANY_COMMANDS;
    		}
    
    		cmd_buff_t *cmd = &clist->commands[index];
    		cmd->argc = 0;
    		cmd->_cmd_buffer = cmd_tok;
    		
    		//printf("Command %d (before args parsing): '%s'\n", index, cmd_tok);
    
    		char *arg_tok_save = NULL;
    		char *arg_tok = strtok_r(cmd_tok, " ", &arg_tok_save);
    
    		while(arg_tok != NULL && cmd->argc < CMD_ARGV_MAX) {
    			trim_spaces(arg_tok);
    
    			//printf("Command %d: argv[%d]: '%s'\n", index, cmd->argc, arg_tok);
    
    			cmd->argv[cmd->argc] = arg_tok;
    			cmd->argc++;
    
    			arg_tok = strtok_r(NULL, " ", &arg_tok_save);
    		}
    
    		cmd->argv[cmd->argc] = NULL;
    
    		//printf("Command %d finished parsing. argc = %d\n", index, cmd->argc);
    
    		index++;
    		cmd_tok = strtok_r(NULL, PIPE_STRING, &cmd_tok_save);
    	}
    
    	clist->num = index;
    	//printf("Total number of commands: %d\n", clist->num);
    	return OK;
    }
    
    /*
    int exec_cmd(cmd_buff_t *cmd, int cmd_index, command_list_t *clist) {
    	int pipe_fds[2];
    	pid_t pid;
    
    	int is_last_cmd = (cmd_index == clist->num - 1);
    
    	printf("Command %d: is_last_cmd = %d\n", cmd_index, is_last_cmd);
    
    	if(!is_last_cmd) {
    		if(pipe(pipe_fds) == -1) {
    			perror("Pipe failed");
    			return -1;
    		}
    
    		printf("Command %d: Pipe created with fd[0] = %d, fd[1] = %d\n", cmd_index, pipe_fds[0], pipe_fds[1]);
    	}
    
    	pid = fork();
    
    	if(pid == -1) {
    		perror("Fork failed");
    		return -1;
    	}
    
    	if(pid == 0) {
    		printf("Command %d: Before redirecting, STDOUT_FILENO = %d\n", cmd_index, STDOUT_FILENO);
    		
    		// Debug: Print the arguments for execvp to check if the command is correct
            	printf("Command %d: execvp arguments:\n", cmd_index);
            	for (int i = 0; cmd->argv[i] != NULL; i++) {
                		printf("argv[%d]: '%s'\n", i, cmd->argv[i]);
            	}
    
    		// Ensure stdin redirection for the second command
                    if (cmd_index > 0) {
                            if (dup2(clist->pipes[cmd_index - 1][0], STDIN_FILENO) == -1) {
                                    perror("Failed to redirect stdin");
                                    exit(1);
                            }
                            printf("Command %d: stdin redirected from pipe[%d][0] to STDIN_FILENO\n", cmd_index, cmd_index - 1);
    			close(clist->pipes[cmd_index - 1][0]);
    			printf("Command %d: Closed pipe[%d][0] in child process\n", cmd_index, cmd_index - 1);
                    }
    
    		// Redirect stdout if it's not the last command
        		if (!is_last_cmd) {
            		if (dup2(pipe_fds[1], STDOUT_FILENO) == -1) {
                			perror("Failed to redirect stdout");
                			exit(1);
            		}
            		printf("Command %d: stdout redirected to pipe[%d][1] from STDOUT_FILENO\n", cmd_index, cmd_index);
    			//close(pipe_fds[1]); // Close the write end of the pipe
                		//printf("Command %d: Closed pipe[%d][1] in child process\n", cmd_index, cmd_index);
        		}
    
    		for(int i = 0; i < clist->num - 1; i++) {
                		if (i != cmd_index - 1) { // Don't close the pipe we're reading from
                    		close(clist->pipes[i][0]);
                    		close(clist->pipes[i][1]);
                    		printf("Command %d: Closed clist->pipes[%d][0] and clist->pipes[%d][1] in child process\n", cmd_index, i, i);   
                		}
            	}
    
    		close(pipe_fds[1]);
    
    		if(execvp(cmd->argv[0], cmd->argv) == -1) {
    			perror("Execvp failed");
    			exit(1);
    		}
    	}
    	else {
    		if(!is_last_cmd) {
    			close(pipe_fds[1]);
    			printf("Command %d: Parent closing write end pipe[%d][1]\n", cmd_index, cmd_index);
    			//close(pipe_fds[0]);
    			//printf("Command %d: Parent closing read end pipe[%d][0]\n", cmd_index, cmd_index);
    		}
    
    		if(cmd_index > 0) {
    			close(clist->pipes[cmd_index - 1][0]);
    			printf("Command %d: Parent closing pipe[%d][0]\n", cmd_index, cmd_index - 1);
    		}
    
    		int status;
    		waitpid(pid, &status, 0);
    		printf("Command %d: Child process finished with status %d\n", cmd_index, status);
    	}
    	return OK;
    }
    
    */
    
    int exec_cmd(cmd_buff_t *cmd, int cmd_index, command_list_t *clist) {
        int pipe_fds[2];
        pid_t pid;
    
        int is_last_cmd = (cmd_index == clist->num - 1);
    
        printf("Command %d: is_last_cmd = %d\n", cmd_index, is_last_cmd);
    
        // Create pipe for non-last commands
        if (!is_last_cmd) {
            if (pipe(pipe_fds) == -1) {
                perror("Pipe failed");
                return -1;
            }
            printf("Command %d: Pipe created with fd[0] = %d, fd[1] = %d\n", cmd_index, pipe_fds[0], pipe_fds[1]);
        }
    
        pid = fork();
    
        if (pid == -1) {
            perror("Fork failed");
            return -1;
        }
    
        if (pid == 0) { // Child process
            printf("Command %d: Before redirecting, STDOUT_FILENO = %d\n", cmd_index, STDOUT_FILENO);
    
    	printf("Debug: Command %d, before redirecting, pipe_fds[1] = %d\n", cmd_index, pipe_fds[1]);
    
            // Debug: Print the arguments for execvp to check if the command is correct
            printf("Command %d: execvp arguments:\n", cmd_index);
            for (int i = 0; cmd->argv[i] != NULL; i++) {
                printf("argv[%d]: '%s'\n", i, cmd->argv[i]);
            }
    
            // Redirect input from previous command, if it's not the first command
            if (cmd_index > 0) {
                if (dup2(clist->pipes[cmd_index - 1][0], STDIN_FILENO) == -1) {
                    perror("Failed to redirect stdin");
                    exit(1);
                }
                printf("Command %d: stdin redirected from pipe[%d][0] to STDIN_FILENO\n", cmd_index, cmd_index - 1);
                close(clist->pipes[cmd_index - 1][0]); // Close the read end of the previous pipe
                printf("Command %d: Closed pipe[%d][0] in child process\n", cmd_index, cmd_index - 1);
            }
    
            // Redirect stdout to the pipe if this is not the last command
            if (!is_last_cmd) {
                if (dup2(pipe_fds[1], STDOUT_FILENO) == -1) {
                    perror("Failed to redirect stdout");
                    exit(1);
                }
                printf("Command %d: stdout redirected to pipe[%d][1] from STDOUT_FILENO\n", cmd_index, cmd_index);
                close(pipe_fds[1]); // Close the write end of the pipe after redirection
                printf("Command %d: Closed pipe[%d][1] in child process\n", cmd_index, cmd_index);
            }
    
            // Close all unnecessary file descriptors in child process
            for (int i = 0; i < clist->num - 1; i++) {
                if (i != cmd_index - 1) { // Don't close the pipe we're reading from
                    close(clist->pipes[i][0]);
                    close(clist->pipes[i][1]);
                    printf("Command %d: Closed clist->pipes[%d][0] and clist->pipes[%d][1] in child process\n", cmd_index, i, i);
                }
            }
    
            // Execute the command
            if (execvp(cmd->argv[0], cmd->argv) == -1) {
                perror("Execvp failed");
    	    printf("Debug: Execvp failed for command %d. Pipe fds: pipe_fds[0] = %d, pipe_fds[1] = %d\n", cmd_index, pipe_fds[0], pipe_fds[1]);
                exit(1);
            }
        } else { // Parent process
            // Close the write end of the pipe in the parent if it's not the last command
            if (!is_last_cmd) {
                close(pipe_fds[1]); // Parent closes the write end of the pipe
                printf("Command %d: Parent closing write end pipe[%d][1]\n", cmd_index, cmd_index);
            }
    
            // Close the read end of the previous pipe in the parent process
            if (cmd_index > 0) {
                close(clist->pipes[cmd_index - 1][0]); // Parent closes the previous read end
                printf("Command %d: Parent closing pipe[%d][0]\n", cmd_index, cmd_index - 1);
            }
    
            // Wait for the child process to finish
            int status;
            waitpid(pid, &status, 0);
            printf("Command %d: Child process finished with status %d\n", cmd_index, status);
        }
        return OK;
    }
    
    
    int exec_local_cmd_loop() {
    
    	char *cmd_buff = malloc(SH_CMD_MAX * sizeof(char));
    	if (cmd_buff == NULL) {
    		fprintf(stderr, "Memory allocation failed\n");
    		return ERR_MEMORY;
    	}
    
    	int rc = 0;
    
    	command_list_t clist;
    
    
    	while(1) {
    		printf("%s", SH_PROMPT);
    		if(fgets(cmd_buff, ARG_MAX, stdin) == NULL) {
    			printf("\n");
    			break;
    		}
    
    		//printf("Raw input: '%s'\n", cmd_buff);
    
    		// remove the trailing \n from cmd_buff
    		cmd_buff[strcspn(cmd_buff, "\n")] = '\0';
    
    		// IMPLEMENT THE REST OF THE REQUIREMENTS
    
    		trim_spaces(cmd_buff);
    		//printf("Trimmed input in exec_local_cmd_loop: '%s'\n", cmd_buff);
    
    		if(strlen(cmd_buff) == 0) {
    			printf(CMD_WARN_NO_CMD);
    			continue;
    		}
    
    		if(strcmp(cmd_buff, EXIT_CMD) == 0) {
    			free(cmd_buff);
    			exit(OK);
    		}
    
    		rc = build_cmd_list(cmd_buff, &clist);
    		//printf("RC value: %d\n", rc);
    
    		switch(rc) {
    			case OK:
    				for(int i = 0; i < clist.num; i++) {
    					if(exec_built_in_cmd(&clist.commands[i]) == BI_NOT_BI) {
    						exec_cmd(&clist.commands[i], i, &clist);
    					}
    				}
    				break;
    			case WARN_NO_CMDS:
    				printf(CMD_WARN_NO_CMD);
    				break;
    			case ERR_TOO_MANY_COMMANDS:
    				printf(CMD_ERR_PIPE_LIMIT, CMD_MAX);
    				break;
    		}
    	}
    
    	free(cmd_buff);
    	return OK;
    }