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.c307
1 files changed, 307 insertions, 0 deletions
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;
+}