diff options
Diffstat (limited to 'shell/process.c')
-rw-r--r-- | shell/process.c | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/shell/process.c b/shell/process.c new file mode 100644 index 0000000..093b712 --- /dev/null +++ b/shell/process.c @@ -0,0 +1,173 @@ +#include <wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "array.h" +#include "process.h" + +// given a substring of a line tries to parse out as much information as possible +int parse_command(char const *line, char const *end, Process *p) { + char *part; + char **local_parts; + if (arrayInit(part) != 0 || arrayInit(local_parts) != 0) { + fprintf(stderr, "Failed to prepare new part / parts array whilst parsing line"); + return -1; + } + + for (size_t i = 0; line + i < end; ++i) { + char c = line[i]; + if (c == ' ' || c == '\0') { + if (arrayLen(part) == 0) + continue; + + arrayPush(part) = '\0'; + arrayPush(local_parts) = part; + + if (c == '\0') { + arrayPush(local_parts) = NULL; + break; + } else { + arrayInit(part); + } + } else { + arrayPush(part) = c; + } + } + + if (arrayLen(part) == 0) { + arrayRelease(part); + } else { + arrayPush(local_parts) = part; + } + + arrayPush(local_parts) = NULL; + p->argc = arrayLen(local_parts) - 1; + p->argv = local_parts; + + return 0; +} + +int parse_line(char const *const line, Process **processes) { + // splits the line at | and then parses the commands + int ret_code; + + if (arrayInit(*processes) != 0) { + perror("Failed to initialize processes array"); + return -1; + } + + bool done = false; + char const *cursor = line; + while (!done) { + Process p = {.in_fd = 0, .out_fd = 0, .pid = 0, .blocking = true}; + char const *end = strchr(cursor, '|'); + + if (end == NULL) { + done = true; + end = line + strlen(line); + } + + if ((ret_code = parse_command(cursor, end, &p)) != 0) { + return ret_code; + } + + arrayPush(*processes) = p; + cursor = end + 1; + } + + size_t p_len = arrayLen(*processes); + Process *last = *processes + (p_len - 1); + + // linking up all processes as we currently only have pipes for multiple commands + for (size_t i = 0; i < p_len - 1; ++i) { + int fds[2]; + if (pipe(fds) != 0) { + perror("Failed to create pipe"); + return -1; + } + (*processes)[i].out_fd = fds[1]; + (*processes)[i + 1].in_fd = fds[0]; + } + + // setting all processes to non blocking when + if (strcmp(last->argv[last->argc - 1], "&") == 0) { + arrayPop(last->argv) = NULL; + last->argc = last->argc - 1; + last->argv[last->argc] = NULL; + + for (size_t i = 0; i < p_len; ++i) { + (*processes)[i].blocking = false; + } + } + return ret_code; +} + +int exec_command(Process p) { + int status; + + if (!p.blocking) { + // if the process is to be put in the background let a fork handle it + if (fork() != 0) + return 0; + } + + if ((p.pid = fork()) == 0) { + if (p.in_fd != 0) { + dup2(p.in_fd, 0); + close(p.in_fd); + } + if (p.out_fd != 0) { + dup2(p.out_fd, 1); + close(p.out_fd); + } + execvp(p.argv[0], p.argv); + fprintf(stderr, "could not execute \"%s\"\n", p.argv[0]); + fflush(stderr); + exit(-1); + } + + if (p.pid < 0) { + fprintf(stderr, "no fork\n"); + exit(-2); + } + + if (p.in_fd != 0) { + close(p.in_fd); + } + + if (p.out_fd != 0) { + close(p.out_fd); + } + + if (!p.blocking) + printf("[%i]\n", p.pid); + + if (waitpid(p.pid, &status, 0) < 0) { + fprintf(stderr, "wait error'ed out\n"); + } + + if (!p.blocking) { + printf("[%i] TERMINATED\n", p.pid); + printf("[%i] EXIT STATUS: %i\n", p.pid, status); // TODO: exit status is wrong + exit(0); + } + + return WEXITSTATUS(status); +} + +void free_processes(Process **pr) { + Process *processes = *pr; + while (!arrayIsEmpty(processes)) { + Process p = arrayPop(processes); + while (!arrayIsEmpty(p.argv)) { + char *tmp = arrayPop(p.argv); + if (tmp) + arrayRelease(tmp); + } + arrayRelease(p.argv); + } + arrayRelease(processes); + *pr = NULL; +}
\ No newline at end of file |