diff options
Diffstat (limited to '03_exercise/srv/shell.c')
-rw-r--r-- | 03_exercise/srv/shell.c | 307 |
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; +} |