summaryrefslogtreecommitdiffstats
path: root/shell/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/process.c')
-rw-r--r--shell/process.c173
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