summaryrefslogtreecommitdiffstats
path: root/03_exercise/srv/shell.c
diff options
context:
space:
mode:
Diffstat (limited to '03_exercise/srv/shell.c')
-rw-r--r--03_exercise/srv/shell.c121
1 files changed, 121 insertions, 0 deletions
diff --git a/03_exercise/srv/shell.c b/03_exercise/srv/shell.c
new file mode 100644
index 0000000..a32fc5f
--- /dev/null
+++ b/03_exercise/srv/shell.c
@@ -0,0 +1,121 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <wait.h>
+#include <errno.h>
+
+#include "array.h"
+#include "process.h"
+#include "prompt_utils.h"
+#include "builtins.h"
+
+#define BUF_SIZE 256
+
+process *processes;
+
+const char * const get_current_dir_name();
+
+void signal_handler(int signal) {
+ if (signal == SIGINT) {
+ for (size_t i = 0; i < arrayLen(processes); ++i) {
+ pid_t pid = processes[i].pid;
+ if (pid != 0) {
+ kill(pid, SIGINT);
+ }
+ }
+ }
+}
+
+int shell(int in_fd) {
+ setvbuf(stderr, NULL, _IONBF, 0);
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ dup2(in_fd, 1);
+ dup2(in_fd, 2);
+
+ // I don't think the shell should exit on SIG_TERM
+ if (signal(SIGINT, SIG_IGN) == SIG_ERR) {
+ perror("Couldn't ignore sigterm");
+ exit(errno);
+ }
+
+ printf("Welcome! Available built-ins are:\n"
+ "cd: `cd <path>` - if no path is given, return to the current dir\n"
+ "wait: `wait pid1 ... pidN` - wait on the processes and report their exit conditions\n"
+ "fg: `fg pid` - pulls a process from the background back in the foreground\n"
+ "\n"
+ "You can put processes in the background using `&`\n"
+ "And with `|` you can pipe the output from one process to the input of another\n"
+ );
+
+ char const *const original_wd = get_current_dir_name();
+
+ bool done = false;
+ while (!done) {
+ char line[BUF_SIZE];
+
+ for (int i = 0; i < BUF_SIZE; ++i) {
+ read(in_fd, line + i, 1);
+ if (line[i] == '\n') {
+ line[i] = 0;
+ break;
+ }
+ }
+
+ if (strspn(line, " \n\t") == strlen(line)) {
+ // skip empty lines - empty being just spaces or tabs
+ continue;
+ }
+
+ processes = NULL;
+ parse_line(line, &processes);
+
+ if (strcmp(processes[0].argv[0], "cd") == 0) {
+ if (arrayLen(processes) != 1) {
+ perror("Can't chain cd with other processes");
+ }
+ int ret;
+ switch (arrayLen(processes[0].argv)) {
+ case 3:
+ ret = chdir(processes[0].argv[1]);
+ break;
+ case 2:
+ ret = chdir(original_wd);
+ break;
+ default:
+ fprintf(stderr, "usage: cd <path>\n");
+ fflush(stderr);
+ ret = -1;
+ }
+
+ if (ret)
+ printf("[%i] ", ret);
+
+ } else if (strcmp(processes[0].argv[0], "exit") == 0) {
+ done = true;
+ } else if (strcmp(processes[0].argv[0], "wait") == 0) {
+ builtin_wait(processes[0], false);
+ } else if (strcmp(processes[0].argv[0], "fg") == 0) {
+ // same behaviour as wait, just bind to shell again (i.e. terminate process on ctrl-c)
+ builtin_wait(processes[0], true);
+ } else {
+ if (arrayLen(processes) != 0 && processes[arrayLen(processes) - 1].blocking) {
+ signal(SIGINT, signal_handler);
+ }
+ for (size_t i = 0; i < arrayLen(processes); ++i) {
+ int ret = exec_command(processes[i]);
+ if (ret)
+ printf("[%i] ", ret);
+ }
+ signal(SIGINT, SIG_IGN);
+ }
+
+ free_processes(&processes);
+ }
+
+ free((void *) original_wd);
+
+ return 0;
+}