summaryrefslogtreecommitdiffstats
path: root/03_exercise/cli/client.c
diff options
context:
space:
mode:
Diffstat (limited to '03_exercise/cli/client.c')
-rw-r--r--03_exercise/cli/client.c317
1 files changed, 317 insertions, 0 deletions
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