diff options
Diffstat (limited to '03_exercise/cli')
-rw-r--r-- | 03_exercise/cli/Makefile | 23 | ||||
-rw-r--r-- | 03_exercise/cli/client.c | 317 |
2 files changed, 340 insertions, 0 deletions
diff --git a/03_exercise/cli/Makefile b/03_exercise/cli/Makefile new file mode 100644 index 0000000..c09533d --- /dev/null +++ b/03_exercise/cli/Makefile @@ -0,0 +1,23 @@ +#!/usr/bin/make +.SUFFIXES: +.PHONY: all run clean +TAR = client +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/cli/client.c b/03_exercise/cli/client.c new file mode 100644 index 0000000..6447034 --- /dev/null +++ b/03_exercise/cli/client.c @@ -0,0 +1,317 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <signal.h> +#include <stdbool.h> + +#define PORT 9000 +#define HOST "127.0.0.1" + +#define BUF_SIZE 2048 + +#define BREAK 1 +#define CONTINUE 2 + +char *get_current_dir_name(void); + +int server_sock; +pid_t pid; + +/* Signal Handler for SIGINT */ +void sigintHandler(int sig_num); + +static inline void die(char const *msg); + +int write_all(int target_sock, char *buffer, size_t size); + +void send_file(char *path); + +int parse(char *buffer, ssize_t length); + +void read_stdin(); + +_Noreturn void read_server_sock(); + +int main() { + setvbuf(stdout, NULL, _IONBF, 0); + + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(PORT), + .sin_addr.s_addr = inet_addr(HOST) + }; + + if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + die("Could not open socket"); + + if (connect(server_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) + die("Could not connect to socket"); + + signal(SIGINT, &sigintHandler); + + pid = fork(); + + if (pid == 0) { + read_stdin(); + } else { + read_server_sock(); + } + + close(server_sock); + kill(pid, SIGKILL); + + return 0; +} + +void receive_file(int recv_sock, char *path, size_t size, char *start_buf, size_t start_buf_len) { + char buffer[BUF_SIZE]; + int file_fd; + int received; + + if ((file_fd = open(path, (O_WRONLY | O_CREAT | O_TRUNC), 0644)) == -1) { perror("Open"); } + + if (size == 0) { + // just touch + close(file_fd); + return; + } + + received = 0; + + if (start_buf) { + // there was part of the file already in the "packet" which was supposed to contain only the header + // put that into the file too + if (write_all(file_fd, start_buf, start_buf_len) != start_buf_len) { + perror("write start buf"); + printf("\n"); + return; + } + received += start_buf_len; + } + + /* 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 got remote file \"%s/%s\"", get_current_dir_name(), path); + printf("\n"); + } else { + printf("error getting remote file \"%s/%s\"", get_current_dir_name(), path); + printf("\n"); + } + + close(file_fd); +} + +bool check_get(char *buffer) { + // "<<!bytecount!name" + char cmd[4]; + + memcpy(cmd, buffer, 3); + cmd[3] = 0; + + if (strcmp(cmd, "<<!") == 0) { + //printf("[c]: \"%s\"\n", buffer); + int offset = 3; + int i = offset; + char rest[BUF_SIZE - 3]; + size_t file_size; + 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(server_sock, path, file_size, &(buffer[i+2]), strlen(&(buffer[i+2]))); + + return true; + } + return false; +} + +void read_server_sock() { + char server_buffer[BUF_SIZE]; + + while (1) { + ssize_t length; + if ((length = read(server_sock, server_buffer, BUF_SIZE)) > 0) { + server_buffer[length] = '\0'; + + // get issued, as it seems + if (check_get(server_buffer)) { + printf("$> "); + fflush(stdout); + continue; + } + + printf("%s", server_buffer); + if (length == 1 && server_buffer[length - 1] == '\n') { + printf("$> "); + fflush(stdout); + } + } + } +} + +void read_stdin() { + char stdin_buffer[BUF_SIZE]; + + bool done = false; + while (!done) { + ssize_t length; + memset(stdin_buffer, 0, BUF_SIZE); + if ((length = read(STDIN_FILENO, stdin_buffer, BUF_SIZE)) > 0) { + switch (parse(stdin_buffer, length)) { + case CONTINUE: + continue; + case BREAK: + done = true; + break; + default: + break; + } + } + } +} + +void sigintHandler(int sig_num) { + errno = 0; + if (fcntl(server_sock, F_GETFD) != -1 || errno != EBADF) { + sig_num = write(server_sock, "exit\n", 6); + (void) sig_num; + close(server_sock); + } + errno = 0; + die("Terminating client\n"); +} + +static inline void die(char const *msg) { + perror(msg); + kill(pid, SIGKILL); + exit(-1); +} + +int write_all(int target_sock, char *buffer, size_t size) { + int sent = 0; + + while (sent < size) { + int nwrite; + if ((nwrite = write(target_sock, buffer + sent, size - sent)) < 0) { + die("Write"); + } + + sent += nwrite; + } + + return sent; +} + +void send_file(char *path) { + printf("Trying to send \"%s\" to 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"); + 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"); + return; + } + + int length = snprintf(NULL, 0, "%ld", sz); + sprintf(buffer, "<<!%ld!", sz); + sprintf(&(buffer[3 + length + 1]), "%s!\n", path); + + //printf("[c] \"%s\"\n", buffer); + + if (write(server_sock, buffer, strlen(buffer)) != strlen(buffer)) { + perror("write header"); + return; + } + 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(server_sock, buffer, count)); + } + + if (errno) + perror("wad"); + + close(file_fd); + + printf("done.\n"); +} + +int parse(char *buffer, ssize_t length) { + char cmd[4]; + char path[BUF_SIZE - 3]; + memcpy(cmd, buffer, 3); + cmd[3] = 0; + memcpy(path, buffer + 4, BUF_SIZE - 4); + path[strlen(path) - 1] = 0; + if (strcmp(cmd, "put") == 0) { + if (strlen(path) == 0) { + fprintf(stderr, "path missing\n"); + } else { + send_file(path); + } + return CONTINUE; + } + + buffer[length] = '\0'; + + if (strspn(buffer, " \n\t") == strlen(buffer)) { + // skip empty lines - empty being just spaces or tabs + printf("$> "); + return CONTINUE; + } + + if (write(server_sock, buffer, strlen(buffer)) < 0) + die("Could not send message"); + + // TODO: exit should only close the connection (cleanly), NOT terminate the server + if (strcmp(buffer, "exit\n") == 0) { + return BREAK; + } + + return 0; +}
\ No newline at end of file |