diff options
author | Stefan Zabka <zabkaste@hu-berlin.de> | 2020-06-10 17:11:44 +0200 |
---|---|---|
committer | Stefan Zabka <zabkaste@hu-berlin.de> | 2020-06-10 17:11:44 +0200 |
commit | 5598f8300b9d3e7b49a4d3e65d1d837616b8beb5 (patch) | |
tree | 84fe96600f41f7d9a7b806edf8f27a3fbb82da4e /03_exercise/srv | |
parent | 47aaae2c42d554963fb811b68fdf28c9743598e8 (diff) | |
parent | d80bb89ceaee252cff304c3f7dcb160d3bee8fde (diff) | |
download | betriebssysteme-5598f8300b9d3e7b49a4d3e65d1d837616b8beb5.tar.gz betriebssysteme-5598f8300b9d3e7b49a4d3e65d1d837616b8beb5.zip |
Merge branch 'master' into threadpool
Diffstat (limited to '03_exercise/srv')
-rw-r--r-- | 03_exercise/srv/Makefile | 23 | ||||
-rw-r--r-- | 03_exercise/srv/array.c | 59 | ||||
-rw-r--r-- | 03_exercise/srv/array.h | 142 | ||||
-rw-r--r-- | 03_exercise/srv/builtins.c | 120 | ||||
-rw-r--r-- | 03_exercise/srv/builtins.h | 7 | ||||
-rw-r--r-- | 03_exercise/srv/process.c | 169 | ||||
-rw-r--r-- | 03_exercise/srv/process.h | 30 | ||||
-rw-r--r-- | 03_exercise/srv/prompt_utils.c | 90 | ||||
-rw-r--r-- | 03_exercise/srv/prompt_utils.h | 10 | ||||
-rw-r--r-- | 03_exercise/srv/server.c | 89 | ||||
-rw-r--r-- | 03_exercise/srv/shell.c | 307 | ||||
-rw-r--r-- | 03_exercise/srv/shell.h | 6 |
12 files changed, 1052 insertions, 0 deletions
diff --git a/03_exercise/srv/Makefile b/03_exercise/srv/Makefile new file mode 100644 index 0000000..4fede74 --- /dev/null +++ b/03_exercise/srv/Makefile @@ -0,0 +1,23 @@ +#!/usr/bin/make +.SUFFIXES: +.PHONY: all run clean +TAR = server +SRC = $(wildcard *.c) +OBJ = $(SRC:%.c=%.o) + +DEP = $(OBJ:%.o=%.d) +-include $(DEP) + +%.o: %.c + $(CC) $(CFLAGS) $< -o $@ + +$(TAR): $(OBJ) + $(CC) $(LFLAGS) $^ -o $@ + +all: $(TAR) + +run: all + ./$(TAR) + +clean: + $(RM) $(RMFILES) $(OBJ) $(TAR) $(DEP) diff --git a/03_exercise/srv/array.c b/03_exercise/srv/array.c new file mode 100644 index 0000000..4f08366 --- /dev/null +++ b/03_exercise/srv/array.c @@ -0,0 +1,59 @@ +/***************************************************************************//** + * @file array.c + * @author Dorian Weber + * @brief Implementation des generalisierten Arrays. + ******************************************************************************/ + +#include "array.h" +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +/* ********************************************************* public functions */ + +/* (die runden Klammern um einige Funktionsnamen sind notwendig, da Makros + * gleichen Namens existieren und der Präprozessor diese expandieren würde) */ + +void* (arrayInit)(size_t capacity, size_t size) { + ArrayHdr *hdr = malloc(sizeof(*hdr) + size*capacity); + + if (hdr == NULL) + return NULL; + + hdr->len = 0; + hdr->cap = capacity; + + return hdr + 1; +} + +void arrayRelease(void* self) { + free(((ArrayHdr*) self) - 1); +} + +void* (arrayPush)(void* self, size_t size) { + ArrayHdr *hdr = ((ArrayHdr*) self) - 1; + + if (hdr->len == hdr->cap) { + hdr->cap *= 2; + hdr = realloc(hdr, sizeof(*hdr) + size*hdr->cap); + + if (hdr == NULL) { + fputs("program ran out of heap memory\n", stderr); + exit(-1); + } + } + + ++hdr->len; + return hdr + 1; +} + +void (arrayPop)(void* self) { + ArrayHdr *hdr = ((ArrayHdr*) self) - 1; + assert(hdr->len > 0); + --hdr->len; +} + +/* Symbol für die Inline-Funktionen erzeugen und aus diesem Modul exportieren */ +extern void arrayClear(void* self); +extern int arrayIsEmpty(const void* self); +extern size_t arrayLen(const void* self); diff --git a/03_exercise/srv/array.h b/03_exercise/srv/array.h new file mode 100644 index 0000000..78d21c9 --- /dev/null +++ b/03_exercise/srv/array.h @@ -0,0 +1,142 @@ +/***************************************************************************//** + * @file array.h + * @author Dorian Weber + * @brief Generischer Array-Typ. + * + * @details + * Diese Datei enthält die Schnittstelle eines generischen Array-Typs. Um den + * Element-Typ des Arrays festzulegen, deklariert man eine Variable als Zeiger + * auf den Element-Typ. Diese kann danach initialisiert und so benutzt werden, + * als wäre sie ein Zeiger auf ein Array variabler Länge. + * + * Hier ist ein Nutzungsbeispiel: + * @code + * int *array; + * + * arrayInit(array); + * arrayPush(array) = 1; + * arrayPush(array) = 2; + * arrayPush(array) = 3; + * + * while (!arrayIsEmpty(array)) + * printf("%i\n", arrayPop(array)); + * + * arrayRelease(array); + * @endcode + * + * Viele der genutzten Funktionen sind in Form von Makros implementiert, die die + * Variable, die den Zeiger auf das Array hält, aktualisieren, obwohl es so + * aussieht, als würde sich dieser Wert niemals ändern. Das macht es riskant + * mehr als einen Zeiger auf das Array zu halten, da jede Vergrößerung des + * Arrays alle Zeiger auf und in das Array potentiell invalidiert. Obwohl diese + * Fehlerquelle subtil und äußerst schwer diagnostizierbar ist - es ist eine + * extrem einfache Abstraktion - führt diese Datenstruktur meiner Meinung nach + * trotzdem zu einem massiven Produktivitätsgewinn; man sollte sich nur in etwa + * im Klaren über die unterliegende Implementation sein, um genau die Untermenge + * von Programmiertechniken auszuwählen, die korrekt funktioniert. + ******************************************************************************/ + +#ifndef ARRAY_H_INCLUDED +#define ARRAY_H_INCLUDED + +/* *** includes ************************************************************* */ + +#include <stddef.h> + +/* *** structures *********************************************************** */ + +/**@brief Arrayheader. + * + * Diese Struktur wird jedem Array im Speicher vorangestellt und beinhaltet + * Informationen über Kapazität und aktuelle Auslastung des Arrays. Die + * Arrayelemente schließen sich dieser Struktur unmittelbar an, so dass der + * Nutzer von dieser versteckten Information nichts bemerkt. + */ +typedef struct ArrayHdr +{ + size_t len; /**<@brief Anzahl der Array-Elemente. */ + size_t cap; /**<@brief Kapazität des reservierten Speichers. */ +} ArrayHdr; + +/* *** interface ************************************************************ */ + +/**@internal + * @brief Initialisiert und gibt einen Zeiger auf den Start des Arrays zurück. + * @param capacity initiale Kapazität + * @param size Größe der Arrayelemente + * @return ein Zeiger auf den Start des Arrays, falls erfolgreich,\n + * \c NULL im Falle eines Speicherfehlers + */ +extern void* arrayInit(size_t capacity, size_t size); + +/**@brief Initialisiert ein neues Array. + * @param self das Array + * @return 0, falls keine Fehler bei der Initialisierung aufgetreten sind,\n + * -1 ansonsten + */ +#define arrayInit(self) \ + ((self = arrayInit(8, sizeof((self)[0]))) == NULL ? -1 : 0) + +/**@brief Gibt das Array und alle assoziierten Strukturen frei. + * @param self das Array + */ +extern void arrayRelease(void* self); + +/**@internal + * @brief Reserviert Platz für einen neuen Wert im Array. + * @param self das Array + * @param size Größe der Arrayelemente + * @return der neue Zeiger auf den Start des Arrays + */ +extern void* arrayPush(void* self, size_t size); + +/**@brief Legt einen Wert auf das Array. + * @param self das Array + */ +#define arrayPush(self) \ + (self = arrayPush(self, sizeof((self)[0])), (self)+arrayLen(self)-1)[0] + +/**@brief Entfernt das oberste Element des Arrays. + * @param self das Array + */ +extern void arrayPop(void* self); + +/**@brief Entfernt und liefert das oberste Element des Arrays. + * @param self das Array + * @return das oberste Element von \p self + */ +#define arrayPop(self) \ + (arrayPop(self), (self)+arrayLen(self))[0] + +/**@brief Gibt das oberste Element des Arrays zurück. + * @param self das Array + * @return das oberste Element von \p self + */ +#define arrayTop(self) \ + (self)[arrayLen(self) - 1] + +/**@brief Setzt die Länge des Arrays auf 0 zurück. +* @param self das Array +*/ +inline void arrayClear(void* self) { + ((ArrayHdr*) self)[-1].len = 0; +} + +/**@brief Gibt zurück, ob das Array leer ist. + * @param self das Array + * @return 0, falls nicht leer\n + 1, falls leer + */ +inline int arrayIsEmpty(const void* self) { + return ((ArrayHdr*) self)[-1].len == 0; +} + +/**@brief Gibt die Anzahl der Array-Elemente zurück. + * @param self das Array + * @return Anzahl der Elemente des Arrays + */ +inline size_t arrayLen(const void* self) { + return ((ArrayHdr*) self)[-1].len; +} + +#endif /* ARRAY_H_INCLUDED */ diff --git a/03_exercise/srv/builtins.c b/03_exercise/srv/builtins.c new file mode 100644 index 0000000..f5497a3 --- /dev/null +++ b/03_exercise/srv/builtins.c @@ -0,0 +1,120 @@ +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <wait.h> +#include <stdlib.h> + + +#include "array.h" +#include "builtins.h" + +void handle(int sig) { + /* Do nothing. */ +} + +typedef struct { + pid_t pid; + bool done; +} hack_struct; + +void builtin_wait(process p, bool bind) { + struct sigaction handler; + handler.sa_handler = handle; + sigemptyset(&handler.sa_mask); + handler.sa_flags = 0; + + hack_struct *pcs; + arrayInit(pcs); + + size_t i = 1; + bool error = false; + while (!error && p.argv[i] != NULL) { + char *end; + hack_struct pc; + pc.pid = strtol(p.argv[i], &end, 10); + pc.done = false; + if (*end != '\0') { + fprintf(stderr, "Not a valid pid: %s\n", p.argv[i]); + error = true; + break; + } + arrayPush(pcs) = pc; + ++i; + } + + if (error || arrayLen(pcs) == 0) { + printf("plz giv pid\n"); + arrayRelease(pcs); + return; + } + + int options = WUNTRACED; + if (!bind) { + options |= WNOHANG; + } + + int done_count = 0; + while (done_count != arrayLen(pcs)) { + for (int j = 0; j < arrayLen(pcs); ++j) { + int status; + hack_struct *current_pc = &j[pcs]; + if (current_pc->done) { + continue; + } + + if (bind) { + printf("Resuming %ld...\n", (long) current_pc->pid); + }/* else { + printf("Waiting for %ld...\n", (long) current_pc->pid); + }*/ + // install signal handler without SA_RESTART, so waitpid gets interrupted + sigaction(SIGINT, &handler, NULL); + pid_t res; + if ((res = waitpid(current_pc->pid, &status, options)) < 0) { + if (EINTR == errno) { + // cancelled by ctrl-c + if (bind) { + current_pc->done = true; + ++done_count; + kill(current_pc->pid, SIGKILL); + waitpid(current_pc->pid, &status, options); + printf("Killed [%ld]\n", (long) current_pc->pid); + } else { + kill(current_pc->pid, SIGCONT); + } + } else { + perror("Could not wait for process"); + ++done_count; + current_pc->done = true; + } + } else if (res == 0) { + // wait terminated because nohang + } else { + current_pc->done = true; + ++done_count; + if (bind) { + printf("[%i] ", WEXITSTATUS(status)); + } else { + printf("[%i] TERMINATED\n", current_pc->pid); + if (WIFEXITED(status)) { + printf("[%i] exited normally with status: %i\n", current_pc->pid, + WEXITSTATUS(status)); + } else if (WIFSTOPPED(status)) { + printf("[%i] was stopped by signal: %s\n", current_pc->pid, + strsignal(WSTOPSIG(status))); + } else if (WIFSIGNALED(status)) { + printf("[%i] was terminated by signal: %s\n", current_pc->pid, + strsignal(WTERMSIG(status))); + } else { + printf("[%i] exited (not sure why), exit status: %i\n", current_pc->pid, + WEXITSTATUS(status)); + } + } + } + signal(SIGINT, SIG_IGN); + } + } + + arrayRelease(pcs); +} diff --git a/03_exercise/srv/builtins.h b/03_exercise/srv/builtins.h new file mode 100644 index 0000000..43571f7 --- /dev/null +++ b/03_exercise/srv/builtins.h @@ -0,0 +1,7 @@ +#ifndef BUILTINS_H +#define BUILTINS_H + +#include "process.h" +void builtin_wait(process p, bool bind); + +#endif
\ No newline at end of file diff --git a/03_exercise/srv/process.c b/03_exercise/srv/process.c new file mode 100644 index 0000000..fe66a66 --- /dev/null +++ b/03_exercise/srv/process.c @@ -0,0 +1,169 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wait.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; + switch (c = line[i]) { + default: + arrayPush(part) = c; + break; + case ' ': + case '\t': + case '\0': + if (arrayLen(part) == 0) + continue; + + arrayPush(part) = '\0'; + arrayPush(local_parts) = part; + arrayInit(part); + } + } + + if (arrayLen(part) == 0) { + arrayRelease(part); + } else { + arrayPush(part) = '\0'; + 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 = {.argv = NULL, .argc = 0, .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.pid = fork()) == 0) { + if (p.in_fd != 0) { + dup2(p.in_fd, 0); + close(p.in_fd); + } + if (p.in_fd == 0 && !p.blocking) { + close(0); + } + + if (p.out_fd != 0) { + dup2(p.out_fd, 1); + close(p.out_fd); + } + + execvp(p.argv[0], p.argv); + fprintf(stderr, "command not found: \"%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, p.blocking ? 0 : WNOHANG) < 0) { + fprintf(stderr, "wait error'ed out\n"); + } + + return WEXITSTATUS(status); +} + +void free_processes(process **pr) { + process *processes = *pr; + while (!arrayIsEmpty(processes)) { + process p = arrayPop(processes); + while (!arrayIsEmpty(p.argv)) { + char *tmp = arrayTop(p.argv); + if (tmp) { + arrayRelease(tmp); + } + arrayPop(p.argv) = NULL; + } + arrayRelease(p.argv); + } + arrayRelease(processes); + *pr = NULL; +}
\ No newline at end of file diff --git a/03_exercise/srv/process.h b/03_exercise/srv/process.h new file mode 100644 index 0000000..6e9208c --- /dev/null +++ b/03_exercise/srv/process.h @@ -0,0 +1,30 @@ +#ifndef SHELL_PROCESS_H +#define SHELL_PROCESS_H + +#include <stddef.h> +#include <stdbool.h> + +typedef struct { + char **argv; + size_t argc; + int in_fd; + int out_fd; + int pid; + bool blocking; +} process; + +/* + * Parses the given line and creates an array of processes at *processes + * Expects tail -F file | grep panic & to mean that both processes should + * run in the background + */ +int parse_line(char const *line, process **processes); + +/* + * returns the return code of the executed program + */ +int exec_command(process p); + +void free_processes(process **processes); + +#endif // SHELL_PROCESS_H diff --git a/03_exercise/srv/prompt_utils.c b/03_exercise/srv/prompt_utils.c new file mode 100644 index 0000000..751a81b --- /dev/null +++ b/03_exercise/srv/prompt_utils.c @@ -0,0 +1,90 @@ +#include <stdlib.h> +#include <string.h> + +#include "array.h" +#include "prompt_utils.h" + +char const *relative_path(char const *const from_dir, char const *const to_dir) { + // easiest cases first + { + if (strcmp(from_dir, to_dir) == 0) { + char *return_value = malloc(2); + strcpy(return_value, "."); + return return_value; + } + } + { + if (strcmp("/", from_dir) == 0) { + char *return_value = malloc(strlen(to_dir) + 2); + return_value[0] = '.'; + memcpy(return_value + 1, to_dir, strlen(to_dir) + 1); + return return_value; + } + } + + // splitting path into pieces so we can do strcmp on each of them + size_t const *const from_dir_indices = get_separator_indices(from_dir, '/'); + size_t const *const to_dir_indices = get_separator_indices(to_dir, '/'); + + size_t from_dir_len = arrayLen(from_dir_indices); + size_t to_dir_len = arrayLen(to_dir_indices); + + // finding the longest common substring + size_t array_len = from_dir_len < to_dir_len ? from_dir_len : to_dir_len; + size_t i = 0; + size_t common_position = 0; + for (; i < array_len - 1; ++i) { + if (from_dir_indices[i + 1] != to_dir_indices[i + 1]) { + break; + } + size_t index = from_dir_indices[i]; + size_t count = from_dir_indices[i + 1] - from_dir_indices[i]; + if (strncmp(from_dir + index, to_dir + index, count) != 0) { + break; + } + common_position = from_dir_indices[i + 1]; + } + + size_t levels_up = from_dir_len - i - 1; + char *return_value; + if (levels_up == 0) { + // equal dirs for whole length of path and not equal => subdir + size_t length = strlen(to_dir + common_position) + strlen("./") + 1; + return_value = malloc(length * sizeof(char)); + strcpy(return_value, "."); + strcat(return_value, to_dir + common_position); + } else { + char const *const go_up = "/.."; + size_t length = strlen(to_dir + common_position) + strlen("..") + + strlen(go_up) * (levels_up - 1) + 1; + return_value = malloc(length * sizeof(char)); + strcpy(return_value, ".."); + for (size_t j = 0; j < levels_up - 1; ++j) { + strcat(return_value, go_up); + } + if (strcmp("/", to_dir) != 0) { + strcat(return_value, to_dir + common_position); + } + } + + arrayRelease((void *)from_dir_indices); + arrayRelease((void *)to_dir_indices); + + return return_value; +} + +size_t *get_separator_indices(char const *const text, char seperator) { + size_t *indices; + arrayInit(indices); + char const *current = text; + if (strchr(current, seperator) == NULL) { + arrayRelease(indices); + return NULL; + } + while ((current = strchr(current, seperator)) != NULL) { + arrayPush(indices) = (size_t) (current - text); + ++current; + } + arrayPush(indices) = strlen(text); + return indices; +} diff --git a/03_exercise/srv/prompt_utils.h b/03_exercise/srv/prompt_utils.h new file mode 100644 index 0000000..36d065d --- /dev/null +++ b/03_exercise/srv/prompt_utils.h @@ -0,0 +1,10 @@ +#ifndef PROMPT_UTILS_H_INCLUDED +#define PROMPT_UTILS_H_INCLUDED + +// returns the relative path to get from the `from_dir` to the `to_dir` +char const *relative_path(char const *from_dir, char const *to_dir); + +// returns the position of each occurrence of the separator +size_t *get_separator_indices(char const *text, char separator); + +#endif // PROMPT_UTILS_H_INCLUDED diff --git a/03_exercise/srv/server.c b/03_exercise/srv/server.c new file mode 100644 index 0000000..67fe21c --- /dev/null +++ b/03_exercise/srv/server.c @@ -0,0 +1,89 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "shell.h" + +/* + * READ ME + * What works: + * - remote shell, weitesgehend die funktionalität der vorhergehenden aufgabe (damit auch pipes und background, + * auch wenn wir das nicht ausführlich getestet haben) + * - get und put für dateien + * - mehrere clients an einem server + * Known bugs: + * - das erkennen, wann eine neue prompt geprintet werden muss ist aktuell sehr hacky client-seitig implementiert, + * scheitert teilweise - die eingabe geht trzd ganz normal, es wird nur keine prompt angezeigt + */ + +#define PORT 9000 + +#define BUF_SIZE 256 + +static inline void die(const char *msg) { + perror(msg); + exit(-1); +} + +int main() { + setvbuf(stderr, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + + struct sockaddr_in srv_addr, cli_addr; + int sockopt = 1; + socklen_t sad_sz = sizeof(struct sockaddr_in); + int sfd; + + srv_addr.sin_family = AF_INET; + srv_addr.sin_port = htons(PORT); + srv_addr.sin_addr.s_addr = INADDR_ANY; + + if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + die("Could not open socket"); + + setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &sockopt, sizeof(sockopt)); + + if (bind(sfd, (struct sockaddr *) &srv_addr, sad_sz) < 0) + die("Could not bind socket"); + + if (listen(sfd, 1) < 0) + die("Could not listen on socket"); + + while (1) { + int cfd, pid; + cfd = accept(sfd, (struct sockaddr *) &cli_addr, &sad_sz); + if (cfd < 0) { + perror("Could not accept incoming connection"); + break; + } + + // fork new process + pid = fork(); + if (pid < 0) { + perror("ERROR in new process creation"); + } + + if (pid == 0) { + // child process + close(sfd); + + printf("srv: connected: %s\n", inet_ntoa(cli_addr.sin_addr)); + int status = shell(cfd); + fprintf(stdout, "srv: client disconnected: %s (%d)\n", inet_ntoa(cli_addr.sin_addr), status); + fflush(stdout); + close(cfd); + } else { + // parent process + close(cfd); + } + } + + close(sfd); + + return 0; +} diff --git a/03_exercise/srv/shell.c b/03_exercise/srv/shell.c new file mode 100644 index 0000000..b956d3c --- /dev/null +++ b/03_exercise/srv/shell.c @@ -0,0 +1,307 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdbool.h> +#include <wait.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <netinet/tcp.h> + +#include "array.h" +#include "process.h" +#include "builtins.h" + +char *get_current_dir_name(void); + +#define BUF_SIZE 2048 + +process *processes; + +int write_all(int target_sock, char *buffer, size_t size) { + int nwrite, sent = 0; + + while (sent < size) { + if ((nwrite = write(target_sock, buffer + sent, size - sent)) < 0) { + perror("Write"); + printf("\n"); + return -1; + } + + sent += nwrite; + } + + return sent; +} + +void receive_file(int recv_sock, char *path, int size) { + char buffer[BUF_SIZE]; + int file_fd; + int received; + + if ((file_fd = open(path, (O_WRONLY | O_CREAT | O_TRUNC), 0644)) == -1) { + perror("Open"); + printf("\n"); + } + + if (size == 0) { + // just touch + close(file_fd); + return; + } + + received = 0; + + /* Read all data */ + while (received < size) { + int nread; + if ((nread = read(recv_sock, buffer, BUF_SIZE)) > 0) { + if (write_all(file_fd, buffer, nread) != nread) { + break; + } + received += nread; + } + } + + if (received == size) { + printf("successfully wrote to remote file \"%s/%s\"\n", get_current_dir_name(), path); + printf("\n"); + } else { + printf("error writing remote file \"%s/%s\"\n", get_current_dir_name(), path); + printf("\n"); + } + + close(file_fd); +} + +void send_file(int client_fd, char *path) { + printf("Trying to get \"%s\" from remote...\n", path); + char buffer[BUF_SIZE]; + memset(buffer, 0, BUF_SIZE); + int tmp_fd, file_fd; + + if ((tmp_fd = open(path, O_RDONLY)) == -1) { + perror("Open"); + printf("\n"); + return; + } + FILE *file = fdopen(tmp_fd, "r"); + fseek(file, 0L, SEEK_END); + long int sz = ftell(file); + fseek(file, 0L, SEEK_SET); + fclose(file); + close(tmp_fd); + if ((file_fd = open(path, O_RDONLY)) == -1) { + perror("Open"); + printf("\n"); + return; + } + + int flag = 1; + // force flush + flag = 1; + setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + flag = 0; + setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + + int length = snprintf(NULL, 0, "%ld", sz); + sprintf(buffer, "<<!%ld!", sz); + sprintf(&(buffer[3 + length + 1]), "%s!\n", path); + + usleep(150); + if (write(client_fd, buffer, strlen(buffer)) != strlen(buffer)) { + perror("write header"); + return; + } + usleep(150); + + flag = 1; + setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + usleep(150); + flag = 0; + setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + + usleep(150); + + memset(buffer, 0, BUF_SIZE); + + errno = 0; + size_t count; + while ((count = read(file_fd, buffer, sizeof(buffer))) > 0) { + /*printf("Sent %i bytes\n", */write_all(client_fd, buffer, count)/*)*/; + } + + if (errno) { + perror("wad"); + printf("\n"); + } + + close(file_fd); + + //printf("done.\n"); +} + +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); + } + } + } +} + +bool check_put(char *buffer, int sock) { + // "<<!bytecount!name" + char cmd[4]; + + memcpy(cmd, buffer, 3); + cmd[3] = 0; + + if (strcmp(cmd, "<<!") == 0) { + char rest[BUF_SIZE - 3]; + size_t file_size; + int offset = 3; + int i = offset; + for (; i < BUF_SIZE; ++i) { + if (buffer[i] == '!') + break; + rest[i - offset] = buffer[i]; + } + rest[i - offset] = 0; + file_size = atoi(rest); + + offset = ++i; + for (; i < BUF_SIZE; ++i) { + if (buffer[i] == '!') + break; + rest[i - offset] = buffer[i]; + } + rest[i - offset] = 0; + char path[strlen(rest) + 1]; + memcpy(path, rest, strlen(rest)); + path[strlen(rest)] = 0; + + receive_file(sock, path, file_size); + + return true; + } + return false; +} + +int shell(int in_fd) { + int stdoutCopy = dup(STDOUT_FILENO); + int stderrCopy = dup(STDERR_FILENO); + + dup2(in_fd, STDOUT_FILENO); + dup2(in_fd, STDERR_FILENO); + + 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" + ); + printf("\n$> "); + + char const *const original_wd = get_current_dir_name(); + //char const *prompt = relative_path(original_wd, original_wd); + + bool done = false; + while (!done) { + char line[BUF_SIZE]; + __ssize_t length; + + fflush(stdout); + if ((length = read(in_fd, line, BUF_SIZE)) < 0) { + fprintf(stderr, "Failed to read from STDIN"); + fflush(stderr); + exit(-1); + } + + if (strspn(line, " \n\t") == strlen(line)) { + // skip empty lines - empty being just spaces or tabs + continue; + } + line[length - 1] = '\0'; // cut the line feed + + if (strlen(line) == 0) { + continue; + } + + if (check_put(line, in_fd)) + 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"); + printf("\n"); + } + 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>"); + fprintf(stderr, "\n"); + fflush(stderr); + ret = -1; + } + + if (ret) + printf("[%i] ", ret); + else { + printf("%s", get_current_dir_name()); + printf("\n"); + } + + } else if (strcmp(processes[0].argv[0], "exit") == 0) { + done = true; + } else if (strcmp(processes[0].argv[0], "get") == 0) { + if (arrayLen(processes[0].argv) != 3) { + printf("please provide a file name to get!"); + printf("\n"); + } else { + send_file(in_fd, processes[0].argv[1]); + } + } 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);*/ + } + printf("\n"); + } + + free_processes(&processes); + } + + // reset output + dup2(STDOUT_FILENO, stdoutCopy); + dup2(STDERR_FILENO, stderrCopy); + + free((void *) original_wd); + + return 0; +} diff --git a/03_exercise/srv/shell.h b/03_exercise/srv/shell.h new file mode 100644 index 0000000..3c79844 --- /dev/null +++ b/03_exercise/srv/shell.h @@ -0,0 +1,6 @@ +#ifndef BETRIEBSYSTEME_SHELL_H +#define BETRIEBSYSTEME_SHELL_H + +int shell(int in_fd); + +#endif //BETRIEBSYSTEME_SHELL_H |