Select Git revision
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;
}