Skip to content
Snippets Groups Projects
Commit b8df7ebf authored by Ansh's avatar Ansh
Browse files

Assignment 5 done

parent 23ae7975
No related branches found
No related tags found
No related merge requests found
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_
_My implementation makes sure this doesn't happen by calling waitpid() on each child process after it has been forked. If I had forgot to do this, this may have led to zombie processes and resource leaks from the child because it won't terminate properly._
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_
_It is necessary to close unused pipes to prevent resource leakage because if you don't, it will burn resources for no reason._
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_
_If we were to treat cd as an external command, it would cd the child process instead of the parent, which means we basically wouldn't be cd'ing anything at all since when the child process terminates, the parent shell is still in the original directory._
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_
_I could modify my implementation by using dynamic lists instead of a fixed number. However, if we were to do that, we would potentially be needing a lot of memory if the person typing in the shell decides to put in a large number of commands constantly. This would also lead to some complexity within the code because you now would need to deal with your shell having to go through several more commands at once than what you might have built the shell for._
File mode changed from 100755 to 100644
......@@ -12,3 +12,77 @@ EOF
# Assertions
[ "$status" -eq 0 ]
}
@test "Exit Command works" {
run ./dsh <<EOF
exit
EOF
# Assertions
[ "$status" -eq 0 ]
}
@test "Multiple commands via pipes are run properly" {
run ./dsh <<EOF
ls | ls
exit
EOF
# Assertions
[ "$status" -eq 0 ]
}
@test "No command is given" {
run ./dsh <<EOF
exit
EOF
# Strip all whitespace (spaces, tabs, newlines) from the output
stripped_output=$(echo "$output" | tr -d '[:space:]')
# Expected output with all whitespace removed for easier matching
expected_output="dsh3>warning:nocommandsprovideddsh3>"
echo "Captured stdout:"
echo "Output: $stripped_output"
echo "Expected: $expected_output"
echo "Exit Status: $status"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
@test "Check if built in cd command works correctly" {
run ./dsh <<EOF
cd ..
pwd
EOF
# Assertions
[ "$status" -eq 0 ]
}
@test "Check error handling for too many pipes" {
run ./dsh <<EOF
ls | grep dshlib.c | grep armed | grep and | grep dangerous | grep again | grep again | grep again | grep again
exit
EOF
# Strip all whitespace (spaces, tabs, newlines) from the output
stripped_output=$(echo "$output" | tr -d '[:space:]')
# Expected output with all whitespace removed for easier matching
expected_output="dsh3>error:pipinglimitedto8commandsdsh3>"
echo "Captured stdout:"
echo "Output: $stripped_output"
echo "Expected: $expected_output"
echo "Exit Status: $status"
# Check exact match
[ "$stripped_output" = "$expected_output" ]
# Assertions
[ "$status" -eq 0 ]
}
No preview for this file type
......@@ -150,193 +150,60 @@ int build_cmd_list(char *cmd_line, command_list_t *clist) {
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]);
int exec_cmd(cmd_buff_t *cmd, int cmd_index, command_list_t *clist, int pipefd_in) {
int pipefd[2];
if(pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork();
pid_t pid = fork();
if(pid == -1) {
perror("Fork failed");
return -1;
perror("fork");
exit(EXIT_FAILURE);
}
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(pipefd[0]);
}
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 < clist->num - 1) {
close(pipefd[0]);
}
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);
dup2(pipefd_in, STDIN_FILENO);
close(pipefd_in);
}
int status;
waitpid(pid, &status, 0);
printf("Command %d: Child process finished with status %d\n", cmd_index, status);
if(cmd_index < clist->num - 1) {
close(STDOUT_FILENO);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
}
return OK;
}
*/
int exec_cmd(cmd_buff_t *cmd, int cmd_index, command_list_t *clist) {
int pipe_fds[2];
pid_t pid;
execvp(cmd->argv[0], cmd->argv);
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);
}
perror("execvp");
exit(EXIT_FAILURE);
}
else {
close(pipefd[1]);
// 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);
if(cmd_index < clist->num - 1) {
int next_pipefd = pipefd[0];
waitpid(pid, NULL, 0);
return next_pipefd;
}
// 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);
else {
waitpid(pid, NULL, 0);
close(pipefd[0]);
return -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));
......@@ -382,9 +249,10 @@ int exec_local_cmd_loop() {
switch(rc) {
case OK:
int pipefd_in = -1;
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);
pipefd_in = exec_cmd(&clist.commands[i], i, &clist, pipefd_in);
}
}
break;
......
......@@ -80,7 +80,7 @@ Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd);
//main execution context
int exec_local_cmd_loop();
int exec_cmd(cmd_buff_t *cmd, int cmd_index, command_list_t *clist);
int exec_cmd(cmd_buff_t *cmd, int cmd_index, command_list_t *clist, int pipefd_in);
int execute_pipeline(command_list_t *clist);
......
Output of first command:
whoami
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment