#include #include #include #include #include #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; }