1612 lines
47 KiB
Plaintext
1612 lines
47 KiB
Plaintext
--- src/client/scanner.h ---
|
|
|
|
#ifndef SCANNER_H
|
|
#define SCANNER_H
|
|
|
|
#include "chunk.h"
|
|
#include "queue.h"
|
|
typedef struct {
|
|
Queue *directories;
|
|
} DirectoryScanner;
|
|
|
|
DirectoryScanner *directory_scanner_create(char *root_directory);
|
|
Chunk *directory_scanner_next(DirectoryScanner *scanner);
|
|
void directory_scanner_destroy(DirectoryScanner *scanner);
|
|
|
|
#endif
|
|
--- src/shared/queue.h ---
|
|
|
|
#ifndef QUEUE_H
|
|
#define QUEUE_H
|
|
|
|
#include <stdbool.h>
|
|
#include <threads.h>
|
|
|
|
typedef struct Queue {
|
|
void **items;
|
|
int front;
|
|
int rear;
|
|
int size;
|
|
int capacity;
|
|
void (*item_destroyer)(void *item);
|
|
} Queue;
|
|
|
|
Queue *queue_create(int capacity, void (*destroyer)(void *item));
|
|
void queue_destroy(Queue *queue);
|
|
bool queue_is_empty(Queue *queue);
|
|
bool queue_is_full(Queue *queue);
|
|
void queue_double_capacity(Queue *queue);
|
|
void queue_enqueue(Queue *queue, void *item);
|
|
void queue_enqueue_multithreaded(Queue *queue, void *item, mtx_t *mutex,
|
|
cnd_t *condition_not_empty,
|
|
cnd_t *condition_not_full);
|
|
void *queue_dequeue(Queue *queue);
|
|
void *queue_dequeue_multithreaded(Queue *queue, mtx_t *mutex,
|
|
cnd_t *condition_not_empty,
|
|
cnd_t *condition_not_full,
|
|
bool *other_thread_done);
|
|
|
|
#endif
|
|
--- src/shared/config.h ---
|
|
|
|
#ifndef CONFIG_H
|
|
#define CONFIG_H
|
|
|
|
#include <stdbool.h>
|
|
|
|
typedef struct Config {
|
|
char *version;
|
|
char *send_directory;
|
|
char *receive_root_directory;
|
|
bool save_to_disk;
|
|
bool use_multithreading;
|
|
bool use_chunk_serialization;
|
|
bool use_compression;
|
|
bool use_multiprocessing;
|
|
int num_connections;
|
|
} Config;
|
|
|
|
Config *config_create(char *version, char *send_directory,
|
|
char *receive_directory, bool save_to_disk,
|
|
bool use_multithreading, bool use_chunk_serialization,
|
|
bool use_compression, bool use_multiprocessing,
|
|
int num_connections);
|
|
void config_delete(Config *config);
|
|
void config_send(int file_descriptor, Config *config);
|
|
Config *config_receive(int file_descriptor);
|
|
|
|
#endif
|
|
--- src/shared/array_list.h ---
|
|
|
|
#ifndef ARRAY_LIST_H
|
|
#define ARRAY_LIST_H
|
|
|
|
#define INITIAL_ARRAY_SIZE 100
|
|
|
|
typedef struct ArrayList {
|
|
void **items;
|
|
int size;
|
|
int capacity;
|
|
void (*item_destroyer)(void *item);
|
|
} ArrayList;
|
|
|
|
ArrayList *array_list_create(void (*item_destroyer)(void *item));
|
|
void array_list_delete(ArrayList *array_list);
|
|
void array_list_clear(ArrayList *array_list);
|
|
void array_list_extend(ArrayList *array_list);
|
|
void array_list_add(ArrayList *array_list, void *item);
|
|
void **array_list_to_array(ArrayList *array_list);
|
|
|
|
#endif
|
|
--- src/shared/log.h ---
|
|
|
|
#ifndef LOG_H
|
|
#define LOG_H
|
|
|
|
typedef enum {
|
|
LOG_LEVEL_DEBUG,
|
|
LOG_LEVEL_INFO,
|
|
LOG_LEVEL_WARNING,
|
|
LOG_LEVEL_ERROR
|
|
} LogLevel;
|
|
|
|
void log_message(LogLevel log_level, char *message, ...);
|
|
|
|
#endif
|
|
--- src/shared/chunk.h ---
|
|
|
|
#ifndef CHUNK_H
|
|
#define CHUNK_H
|
|
|
|
#include "socket.h"
|
|
#include <sys/stat.h>
|
|
|
|
#define DESIRED_CHUNK_SIZE 10 * 1024 * 1024
|
|
#define FILE_PATH_SEPERATOR "#&&SEPP&&#"
|
|
#define FILE_PATH_DATA_SEPERATOR "#&&SEPD&&#"
|
|
|
|
typedef struct {
|
|
char *path;
|
|
struct stat stats;
|
|
char *data;
|
|
} File;
|
|
|
|
typedef struct {
|
|
char *path;
|
|
DataFragment *data_fragment;
|
|
} FileReceive;
|
|
|
|
typedef struct {
|
|
File **items;
|
|
int element_count;
|
|
} Chunk;
|
|
|
|
typedef struct {
|
|
void *data;
|
|
unsigned long long data_size;
|
|
} Data;
|
|
|
|
File *file_create(const char *path, struct stat *stats);
|
|
void file_destroy(void *item);
|
|
void file_load_data(File *file);
|
|
void file_print(void *item);
|
|
void file_content_to_buffer(File *file, char *buffer);
|
|
|
|
FileReceive *file_receive_create(char *path, DataFragment *data_fragment);
|
|
void file_receive_destroy(void *file_receive);
|
|
|
|
Chunk *chunk_create(File **items, int element_count);
|
|
void chunk_destroy(void *chunk);
|
|
void chunk_print(void *chunk);
|
|
Data *chunk_format(Chunk *chunk);
|
|
Data *chunk_compress(Chunk *chunk);
|
|
|
|
Data *chunk_data_create(void *data, unsigned long long data_size);
|
|
void chunk_data_delete(void *chunk);
|
|
void chunk_data_to_disk(Data *chunk, char *root_directory);
|
|
#endif
|
|
--- src/shared/utils.h ---
|
|
|
|
#ifndef UTILS_H
|
|
#define UTILS_H
|
|
|
|
void mkdir_r(char *path);
|
|
char *str_dup(char *string);
|
|
void to_disk(char *path, void *data, unsigned long long data_size);
|
|
char *path_cat(char *path1, char *path2);
|
|
|
|
#endif
|
|
--- src/shared/multiprocessing.h ---
|
|
|
|
#ifndef MULTIPROCESSING_H
|
|
#define MULTIPROCESSING_H
|
|
|
|
#include <threads.h>
|
|
|
|
#include "config.h"
|
|
#include "queue.h"
|
|
|
|
typedef struct {
|
|
Config *config;
|
|
Queue *queue_scanner;
|
|
mtx_t mutex_scanner;
|
|
cnd_t condition_not_full_scanner;
|
|
cnd_t condition_not_empty_scanner;
|
|
bool scanner_done;
|
|
Queue *queue_loader;
|
|
mtx_t mutex_loader;
|
|
cnd_t condition_not_full_loader;
|
|
cnd_t condition_not_empty_loader;
|
|
bool loader_done;
|
|
} PipelineContextSender;
|
|
|
|
typedef struct PipelineContextReceiver {
|
|
Queue *queue;
|
|
Config *config;
|
|
int file_descriptor;
|
|
mtx_t mutex;
|
|
cnd_t condition_not_full;
|
|
cnd_t condition_not_empty;
|
|
bool receiver_done;
|
|
} PipelineContextReceiver;
|
|
|
|
PipelineContextSender *pipeline_context_sender_create(Config *config,
|
|
Queue *queue_scanner,
|
|
Queue *queue_loader);
|
|
void pipeline_context_sender_destroy(PipelineContextSender *context);
|
|
PipelineContextReceiver *pipeline_context_receiver_create(Config *config,
|
|
Queue *queue_receiver,
|
|
int file_descriptor);
|
|
void pipeline_context_receiver_destroy(PipelineContextReceiver *context);
|
|
#endif
|
|
--- src/shared/socket.h ---
|
|
|
|
#ifndef SOCKET_H
|
|
#define SOCKET_H
|
|
|
|
#include "array_list.h"
|
|
#include <netinet/in.h>
|
|
|
|
typedef unsigned long long NET_SIZE;
|
|
typedef int Status;
|
|
enum NET_STATUS { OK, ERROR, FINISHED, NEXT };
|
|
|
|
typedef struct Server {
|
|
struct sockaddr_in address;
|
|
unsigned int address_length;
|
|
int file_descriptor;
|
|
} Server;
|
|
|
|
Server *server_create(int port);
|
|
void server_listen(Server *server, void (*handler)(int file_descriptor));
|
|
void server_delete(Server *server);
|
|
|
|
typedef struct Client {
|
|
struct sockaddr_in address;
|
|
unsigned int address_length;
|
|
int file_descriptor;
|
|
} Client;
|
|
|
|
typedef struct DataFragment {
|
|
void *data;
|
|
unsigned long long size;
|
|
} DataFragment;
|
|
|
|
Client *client_create();
|
|
void client_disconnect(Client *client);
|
|
void client_delete(Client *client);
|
|
void client_connect(Client *client, char *host, int port);
|
|
|
|
DataFragment *data_fragment_create(void *data, unsigned long long size);
|
|
void data_fragment_delete(void *data_fragment);
|
|
|
|
void send_n_data(int file_descriptor, void *data, NET_SIZE data_size);
|
|
void receive_n_data(int file_descriptor, void *data, NET_SIZE data_size);
|
|
void send_str(int file_descriptor, char *data);
|
|
char *receive_str(int file_descriptor);
|
|
void send_data(int file_descriptor, void *data, unsigned long long data_size);
|
|
DataFragment *receive_data(int file_descriptor);
|
|
void send_int(int file_descriptor, int data);
|
|
int receive_int(int file_descriptor);
|
|
void send_status(int file_descriptor, Status status);
|
|
Status receive_status(int file_descriptor);
|
|
|
|
#endif
|
|
--- src/client/client.c ---
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <threads.h>
|
|
#include <unistd.h>
|
|
|
|
#include "chunk.h"
|
|
#include "config.h"
|
|
#include "log.h"
|
|
#include "multiprocessing.h"
|
|
#include "queue.h"
|
|
#include "scanner.h"
|
|
#include "socket.h"
|
|
#include "utils.h"
|
|
#include <dirent.h>
|
|
|
|
int send_chunk(Client *client, Chunk *chunk, bool use_compression) {
|
|
if (use_compression) {
|
|
Data *data = chunk_compress(chunk);
|
|
send_data(client->file_descriptor, data->data, data->data_size);
|
|
} else {
|
|
for (int i = 0; i < chunk->element_count; i++) {
|
|
send_status(client->file_descriptor, NEXT);
|
|
File *file = chunk->items[i];
|
|
send_str(client->file_descriptor, file->path);
|
|
send_data(client->file_descriptor, file->data, file->stats.st_size);
|
|
}
|
|
send_status(client->file_descriptor, FINISHED);
|
|
if (receive_status(client->file_descriptor) != OK)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int scan_directory_multithreaded(void *pipeline_context) {
|
|
PipelineContextSender *context = (PipelineContextSender *)pipeline_context;
|
|
mtx_lock(&context->mutex_scanner);
|
|
DirectoryScanner *scanner =
|
|
directory_scanner_create(context->config->send_directory);
|
|
mtx_unlock(&context->mutex_scanner);
|
|
|
|
Chunk *current_chunk;
|
|
while ((current_chunk = directory_scanner_next(scanner)) != NULL)
|
|
queue_enqueue_multithreaded(context->queue_scanner, current_chunk,
|
|
&context->mutex_scanner,
|
|
&context->condition_not_empty_scanner,
|
|
&context->condition_not_full_scanner);
|
|
mtx_lock(&context->mutex_scanner);
|
|
context->scanner_done = true;
|
|
cnd_signal(&context->condition_not_empty_scanner);
|
|
mtx_unlock(&context->mutex_scanner);
|
|
|
|
directory_scanner_destroy(scanner);
|
|
return thrd_success;
|
|
}
|
|
|
|
int load_files_multithreaded(void *pipeline_context) {
|
|
PipelineContextSender *context = (PipelineContextSender *)pipeline_context;
|
|
while (true) {
|
|
Chunk *chunk = queue_dequeue_multithreaded(
|
|
context->queue_scanner, &context->mutex_scanner,
|
|
&context->condition_not_empty_scanner,
|
|
&context->condition_not_full_scanner, &context->scanner_done);
|
|
if (chunk == NULL) {
|
|
mtx_lock(&context->mutex_loader);
|
|
context->loader_done = true;
|
|
cnd_signal(&context->condition_not_empty_loader);
|
|
mtx_unlock(&context->mutex_loader);
|
|
return thrd_success;
|
|
}
|
|
for (int i = 0; i < chunk->element_count; i++)
|
|
file_load_data(chunk->items[i]);
|
|
queue_enqueue_multithreaded(context->queue_loader, chunk,
|
|
&context->mutex_loader,
|
|
&context->condition_not_empty_loader,
|
|
&context->condition_not_full_loader);
|
|
}
|
|
}
|
|
|
|
int send_chunks_multithreaded(void *pipeline_context) {
|
|
PipelineContextSender *context = (PipelineContextSender *)pipeline_context;
|
|
bool use_compression = context->config->use_compression;
|
|
Client *client = client_create();
|
|
client_connect(client, "127.0.0.1", 8080);
|
|
config_send(client->file_descriptor, context->config);
|
|
|
|
while (true) {
|
|
Chunk *current_chunk = queue_dequeue_multithreaded(
|
|
context->queue_loader, &context->mutex_loader,
|
|
&context->condition_not_empty_loader,
|
|
&context->condition_not_full_loader, &context->loader_done);
|
|
if (current_chunk == NULL) {
|
|
client_disconnect(client);
|
|
client_delete(client);
|
|
return thrd_success;
|
|
}
|
|
send_chunk(client, current_chunk, use_compression);
|
|
chunk_destroy(current_chunk);
|
|
}
|
|
}
|
|
|
|
int send_files(Config *config) {
|
|
Client *client = client_create();
|
|
client_connect(client, "127.0.0.1", 8080);
|
|
config_send(client->file_descriptor, config);
|
|
DirectoryScanner *scanner = directory_scanner_create(config->send_directory);
|
|
Chunk *current_chunk;
|
|
while ((current_chunk = directory_scanner_next(scanner)) != NULL) {
|
|
chunk_print(current_chunk);
|
|
for (int i = 0; i < current_chunk->element_count; i++)
|
|
file_load_data(current_chunk->items[i]);
|
|
send_chunk(client, current_chunk, config->use_compression);
|
|
chunk_destroy(current_chunk);
|
|
}
|
|
printf("FINISHED");
|
|
directory_scanner_destroy(scanner);
|
|
client_disconnect(client);
|
|
client_delete(client);
|
|
return 0;
|
|
}
|
|
|
|
void handle_arg(char *argument_given, char *argument_to_set, bool *result,
|
|
char *message) {
|
|
if (strcmp(argument_given, argument_to_set) == 0) {
|
|
*result = true;
|
|
log_message(LOG_LEVEL_INFO, message);
|
|
}
|
|
}
|
|
|
|
int send_files_multithreaded(Config *config) {
|
|
PipelineContextSender *context =
|
|
pipeline_context_sender_create(config, queue_create(100, chunk_destroy),
|
|
queue_create(100, chunk_destroy));
|
|
|
|
thrd_t scanner, loader, sender;
|
|
if (thrd_create(&scanner, scan_directory_multithreaded, context) !=
|
|
thrd_success ||
|
|
thrd_create(&loader, load_files_multithreaded, context) != thrd_success ||
|
|
thrd_create(&sender, send_chunks_multithreaded, context) !=
|
|
thrd_success) {
|
|
perror("Error creating threads.\n");
|
|
return 1;
|
|
}
|
|
|
|
thrd_join(scanner, NULL);
|
|
thrd_join(loader, NULL);
|
|
thrd_join(sender, NULL);
|
|
|
|
pipeline_context_sender_destroy(context);
|
|
return 0;
|
|
}
|
|
|
|
int send_files_multiprocessed(Config *config) {
|
|
int cores = sysconf(_SC_NPROCESSORS_ONLN);
|
|
int pid = fork();
|
|
if (pid == -1) {
|
|
perror("Error Forking!");
|
|
return 1;
|
|
} else if (pid == 0) {
|
|
}
|
|
for (int i = 0; i < cores; i++) {
|
|
int pid = fork();
|
|
if (pid == -1) {
|
|
perror("Error forking!");
|
|
return 1;
|
|
} else if (pid == 0) {
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
Config *config = config_create(
|
|
str_dup("1.0.0"), str_dup("/home/taptap/Nextcloud/Uni/moodle/MINT-Raum"),
|
|
str_dup("./data_copied"), false, false, false, false, false, 1);
|
|
for (int i = 1; i < argc; i++) {
|
|
handle_arg(argv[i], "-m", &config->use_multithreading,
|
|
"Enabled Multithreading");
|
|
handle_arg(argv[i], "-s", &config->use_chunk_serialization,
|
|
"Enabled Chunk Serialization");
|
|
handle_arg(argv[i], "-c", &config->use_compression, "Enabled Compression");
|
|
handle_arg(argv[i], "-p", &config->use_multiprocessing,
|
|
"Enabled Multiprocessing");
|
|
}
|
|
|
|
if (config->use_multithreading)
|
|
return send_files_multithreaded(config);
|
|
else if (config->use_multiprocessing)
|
|
return send_files_multiprocessed(config);
|
|
return send_files(config);
|
|
}
|
|
--- src/client/scanner.c ---
|
|
|
|
#include "scanner.h"
|
|
#include "array_list.h"
|
|
#include "chunk.h"
|
|
#include "queue.h"
|
|
#include "utils.h"
|
|
#include <dirent.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
DirectoryScanner *directory_scanner_create(char *root_directory) {
|
|
DirectoryScanner *scanner = malloc(sizeof(DirectoryScanner));
|
|
scanner->directories = queue_create(100, free);
|
|
queue_enqueue(scanner->directories, str_dup(root_directory));
|
|
return scanner;
|
|
}
|
|
|
|
void directory_scanner_destroy(DirectoryScanner *scanner) {
|
|
if (scanner == NULL)
|
|
return;
|
|
queue_destroy(scanner->directories);
|
|
free(scanner);
|
|
}
|
|
|
|
Chunk *chunk_data_to_chunk(ArrayList *chunk_data) {
|
|
void **chunk_items = array_list_to_array(chunk_data);
|
|
Chunk *chunk = chunk_create((File **)chunk_items, chunk_data->size);
|
|
free(chunk_items);
|
|
chunk_data->item_destroyer = NULL;
|
|
array_list_delete(chunk_data);
|
|
return chunk;
|
|
}
|
|
|
|
Chunk *directory_scanner_next(DirectoryScanner *scanner) {
|
|
ArrayList *chunk_data = array_list_create(file_destroy);
|
|
unsigned long long chunk_data_size = 0;
|
|
|
|
while (!queue_is_empty(scanner->directories)) {
|
|
char *path = (char *)queue_dequeue(scanner->directories);
|
|
DIR *dir;
|
|
struct dirent *entry;
|
|
dir = opendir(path);
|
|
if (dir == NULL) {
|
|
perror("Could not open directory!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
printf("%s", path);
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
|
continue;
|
|
}
|
|
char *cur_path = path_cat(path, entry->d_name);
|
|
struct stat stats;
|
|
stat(cur_path, &stats);
|
|
if (!S_ISREG(stats.st_mode))
|
|
queue_enqueue(scanner->directories, (void *)cur_path);
|
|
else {
|
|
File *file = file_create(cur_path, &stats);
|
|
array_list_add(chunk_data, file);
|
|
chunk_data_size += file->stats.st_size;
|
|
if (chunk_data_size > DESIRED_CHUNK_SIZE)
|
|
return chunk_data_to_chunk(chunk_data);
|
|
free(cur_path);
|
|
}
|
|
}
|
|
closedir(dir);
|
|
free(path);
|
|
}
|
|
if (chunk_data->size > 0)
|
|
return chunk_data_to_chunk(chunk_data);
|
|
return NULL;
|
|
}
|
|
--- src/shared/multiprocessing.c ---
|
|
|
|
#include "multiprocessing.h"
|
|
#include "config.h"
|
|
#include "queue.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <threads.h>
|
|
|
|
PipelineContextSender *pipeline_context_sender_create(Config *config,
|
|
Queue *queue_scanner,
|
|
Queue *queue_loader) {
|
|
PipelineContextSender *context = malloc(sizeof(PipelineContextSender));
|
|
context->config = config;
|
|
context->queue_scanner = queue_scanner;
|
|
context->queue_loader = queue_loader;
|
|
context->scanner_done = false;
|
|
context->loader_done = false;
|
|
if (mtx_init(&context->mutex_scanner, mtx_plain) != thrd_success ||
|
|
cnd_init(&context->condition_not_full_scanner) != thrd_success ||
|
|
cnd_init(&context->condition_not_empty_scanner) != thrd_success ||
|
|
mtx_init(&context->mutex_loader, mtx_plain) != thrd_success ||
|
|
cnd_init(&context->condition_not_full_loader) != thrd_success ||
|
|
cnd_init(&context->condition_not_empty_loader) != thrd_success) {
|
|
perror("Error initializing synchronization objects!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
return context;
|
|
}
|
|
|
|
void pipeline_context_sender_destroy(PipelineContextSender *context) {
|
|
config_delete(context->config);
|
|
queue_destroy(context->queue_scanner);
|
|
queue_destroy(context->queue_loader);
|
|
mtx_destroy(&context->mutex_scanner);
|
|
cnd_destroy(&context->condition_not_full_scanner);
|
|
cnd_destroy(&context->condition_not_empty_scanner);
|
|
mtx_destroy(&context->mutex_loader);
|
|
cnd_destroy(&context->condition_not_full_loader);
|
|
cnd_destroy(&context->condition_not_empty_loader);
|
|
free(context);
|
|
}
|
|
|
|
PipelineContextReceiver *pipeline_context_receiver_create(Config *config,
|
|
Queue *queue,
|
|
int file_descriptor) {
|
|
PipelineContextReceiver *context = malloc(sizeof(PipelineContextReceiver));
|
|
context->config = config;
|
|
context->queue = queue;
|
|
context->file_descriptor = file_descriptor;
|
|
context->receiver_done = false;
|
|
if (mtx_init(&context->mutex, mtx_plain) != thrd_success ||
|
|
cnd_init(&context->condition_not_full) != thrd_success ||
|
|
cnd_init(&context->condition_not_empty) != thrd_success) {
|
|
perror("Error initializing synchronization objects!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
return context;
|
|
}
|
|
|
|
void pipeline_context_receiver_destroy(PipelineContextReceiver *context) {
|
|
config_delete(context->config);
|
|
queue_destroy(context->queue);
|
|
mtx_destroy(&context->mutex);
|
|
cnd_destroy(&context->condition_not_full);
|
|
cnd_destroy(&context->condition_not_empty);
|
|
free(context);
|
|
}
|
|
--- src/shared/array_list.c ---
|
|
|
|
#include "array_list.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
ArrayList *array_list_create(void (*item_destroyer)(void *item)) {
|
|
ArrayList *list = (ArrayList *)malloc(sizeof(ArrayList));
|
|
if (list == NULL) {
|
|
perror("FATAL ERROR: Could not allocate memory for array list struct");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
list->items = malloc(INITIAL_ARRAY_SIZE * sizeof(void *));
|
|
if (list->items == NULL) {
|
|
perror("FATAL ERROR: Could not allocate memory for list items");
|
|
free(list);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
list->size = 0;
|
|
list->capacity = INITIAL_ARRAY_SIZE;
|
|
list->item_destroyer = item_destroyer;
|
|
return list;
|
|
}
|
|
|
|
void array_list_delete(ArrayList *array_list) {
|
|
if (array_list == NULL)
|
|
return;
|
|
if (array_list->item_destroyer != NULL) {
|
|
for (int i = 0; i < array_list->size; i++) {
|
|
array_list->item_destroyer(array_list->items[i]);
|
|
array_list->items[i] = NULL;
|
|
}
|
|
}
|
|
free(array_list->items);
|
|
free(array_list);
|
|
}
|
|
|
|
void array_list_clear(ArrayList *array_list) {
|
|
if (array_list == NULL)
|
|
return;
|
|
for (int i = 0; i < array_list->size; i++)
|
|
array_list->items[i] = NULL;
|
|
array_list->size = 0;
|
|
}
|
|
|
|
void array_list_extend(ArrayList *array_list) {
|
|
if (array_list == NULL)
|
|
return;
|
|
int new_capacity = array_list->capacity * 2;
|
|
if (new_capacity == 0)
|
|
new_capacity = INITIAL_ARRAY_SIZE;
|
|
array_list->items = realloc(array_list->items, new_capacity * sizeof(void *));
|
|
if (array_list->items == NULL) {
|
|
perror("FATAL ERROR: Could not reallocate memory for array list struct");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
array_list->capacity = new_capacity;
|
|
}
|
|
|
|
void array_list_add(ArrayList *array_list, void *item) {
|
|
if (array_list == NULL) {
|
|
return;
|
|
}
|
|
if (array_list->capacity == array_list->size) {
|
|
array_list_extend(array_list);
|
|
}
|
|
array_list->items[array_list->size] = item;
|
|
array_list->size += 1;
|
|
}
|
|
|
|
void **array_list_to_array(ArrayList *array_list) {
|
|
if (array_list == NULL) {
|
|
return NULL;
|
|
}
|
|
void **array = malloc(array_list->size * sizeof(void *));
|
|
if (array == NULL) {
|
|
perror("Could not malloc space for array from array list!");
|
|
return NULL;
|
|
}
|
|
memcpy(array, array_list->items, array_list->size * sizeof(void *));
|
|
return array;
|
|
}
|
|
--- src/shared/utils.c ---
|
|
|
|
#include "utils.h"
|
|
#include "libgen.h"
|
|
#include "sys/stat.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
void mkdir_r(char *path) {
|
|
char *path_duplicate = malloc(strlen(path) + 1);
|
|
strcpy(path_duplicate, path);
|
|
char *path_current = (char *)malloc((strlen(path) + 2) * sizeof(char));
|
|
char *path_current_position = path_current;
|
|
const char *delimiter = "/";
|
|
char *part = strtok(path_duplicate, delimiter);
|
|
// struct stat st;
|
|
while (part != NULL) {
|
|
strcpy(path_current_position, part);
|
|
path_current_position += strlen(part) * sizeof(char);
|
|
strcpy(path_current_position, "/");
|
|
path_current_position += sizeof(char);
|
|
struct stat st;
|
|
if (stat(path_current, &st) != 0) {
|
|
if (mkdir(path_current, 0755) != 0) {
|
|
perror("Could not create directory");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
part = strtok(NULL, delimiter);
|
|
}
|
|
free(path_duplicate);
|
|
free(path_current);
|
|
}
|
|
|
|
char *str_dup(char *string) {
|
|
if (string == NULL)
|
|
return NULL;
|
|
char *new_string = (char *)malloc(strlen(string) + 1);
|
|
strcpy(new_string, string);
|
|
return new_string;
|
|
}
|
|
|
|
void to_disk(char *path, void *data, unsigned long long data_size) {
|
|
char *directory = str_dup(path);
|
|
directory = dirname(directory);
|
|
mkdir_r(directory);
|
|
FILE *file_pointer = fopen(path, "wb");
|
|
if (file_pointer == NULL) {
|
|
perror("Could not open File");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
fwrite(data, 1, data_size, file_pointer);
|
|
fclose(file_pointer);
|
|
free(directory);
|
|
}
|
|
|
|
char *path_cat(char *path1, char *path2) {
|
|
if (path1 == NULL || *path1 == '\0')
|
|
return str_dup(path2);
|
|
if (path2 == NULL || *path2 == '\0')
|
|
return str_dup(path1);
|
|
int path1_len = strlen(path1);
|
|
int path2_len = strlen(path2);
|
|
char *path2_pointer = path2;
|
|
if (path1[path1_len - 1] == '/')
|
|
path1_len -= 1;
|
|
if (path2[0] == '/') {
|
|
path2_pointer += 1;
|
|
path2_len -= 1;
|
|
}
|
|
char *new_path = malloc(path1_len + path2_len + 2);
|
|
memcpy(new_path, path1, path1_len);
|
|
new_path[path1_len] = '/';
|
|
memcpy(new_path + path1_len + 1, path2_pointer, path2_len);
|
|
new_path[path1_len + path2_len + 1] = '\0';
|
|
return new_path;
|
|
}
|
|
--- src/shared/queue.c ---
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <threads.h>
|
|
|
|
#include "queue.h"
|
|
|
|
Queue *queue_create(int capacity, void (*destroyer)(void *item)) {
|
|
Queue *queue = (Queue *)malloc(sizeof(Queue));
|
|
if (queue == NULL) {
|
|
perror("FATAL ERROR: Could not allocate memory for queue structure");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
queue->items = malloc(capacity * sizeof(void *));
|
|
if (queue->items == NULL) {
|
|
perror("FATAL ERROR: Could not allocate memory for queue items");
|
|
free(queue);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (int i = 0; i < capacity; ++i) {
|
|
queue->items[i] = NULL;
|
|
}
|
|
|
|
queue->capacity = capacity;
|
|
queue->front = 0;
|
|
queue->rear = 0;
|
|
queue->size = 0;
|
|
queue->item_destroyer = destroyer;
|
|
|
|
return queue;
|
|
}
|
|
|
|
void queue_destroy(Queue *queue) {
|
|
if (queue == NULL)
|
|
return;
|
|
|
|
for (int i = 0; i < queue->size; ++i) {
|
|
int index = (queue->front + i) % queue->capacity;
|
|
queue->item_destroyer(queue->items[index]);
|
|
}
|
|
free(queue->items);
|
|
free(queue);
|
|
}
|
|
|
|
bool queue_is_empty(Queue *queue) {
|
|
if (queue == NULL)
|
|
return true;
|
|
return queue->size == 0;
|
|
}
|
|
|
|
bool queue_is_full(Queue *queue) {
|
|
if (queue == NULL)
|
|
return false;
|
|
return queue->size == queue->capacity;
|
|
}
|
|
|
|
void queue_double_capacity(Queue *queue) {
|
|
if (queue == NULL)
|
|
return;
|
|
unsigned int new_capacity = queue->capacity * 2;
|
|
if (new_capacity <= 1)
|
|
new_capacity = 100;
|
|
void **new_items = malloc(new_capacity * sizeof(void *));
|
|
if (new_items == NULL) {
|
|
perror("FATAL ERROR: Could not allocate memory for doubling capacity of "
|
|
"queue.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
for (int i = 0; i < queue->size; i++)
|
|
new_items[i] = queue->items[(i + queue->front) % queue->capacity];
|
|
free(queue->items);
|
|
queue->items = new_items;
|
|
queue->front = 0;
|
|
queue->rear = queue->size;
|
|
queue->capacity = new_capacity;
|
|
}
|
|
|
|
void queue_enqueue(Queue *queue, void *item) {
|
|
if (queue == NULL || item == NULL) {
|
|
perror("ERROR: Cannot enqueue with a null queue or item.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (queue_is_full(queue))
|
|
queue_double_capacity(queue);
|
|
queue->items[queue->rear] = item;
|
|
queue->rear = (queue->rear + 1) % queue->capacity;
|
|
queue->size++;
|
|
}
|
|
|
|
void queue_enqueue_multithreaded(Queue *queue, void *item, mtx_t *mutex,
|
|
cnd_t *condition_not_empty,
|
|
cnd_t *condition_not_full) {
|
|
mtx_lock(mutex);
|
|
while (queue_is_full(queue))
|
|
cnd_wait(condition_not_full, mutex);
|
|
queue_enqueue(queue, item);
|
|
cnd_signal(condition_not_empty);
|
|
mtx_unlock(mutex);
|
|
}
|
|
|
|
void *queue_dequeue(Queue *queue) {
|
|
if (queue == NULL || queue_is_empty(queue)) {
|
|
perror("ERROR: Could not dequeue from null or empty queue.");
|
|
return NULL;
|
|
}
|
|
|
|
void *item = queue->items[queue->front];
|
|
queue->items[queue->front] = NULL;
|
|
queue->front = (queue->front + 1) % queue->capacity;
|
|
queue->size--;
|
|
return item;
|
|
}
|
|
|
|
void *queue_dequeue_multithreaded(Queue *queue, mtx_t *mutex,
|
|
cnd_t *condition_not_empty,
|
|
cnd_t *condition_not_full,
|
|
bool *other_thread_done) {
|
|
mtx_lock(mutex);
|
|
while (queue_is_empty(queue) && !*other_thread_done)
|
|
cnd_wait(condition_not_empty, mutex);
|
|
if (queue_is_empty(queue) && *other_thread_done) {
|
|
mtx_unlock(mutex);
|
|
return NULL;
|
|
}
|
|
void *item = queue_dequeue(queue);
|
|
cnd_signal(condition_not_full);
|
|
mtx_unlock(mutex);
|
|
return item;
|
|
}
|
|
--- src/shared/chunk.c ---
|
|
|
|
#include <dirent.h>
|
|
#include <libgen.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <zstd.h>
|
|
|
|
#include "chunk.h"
|
|
#include "log.h"
|
|
#include "socket.h"
|
|
|
|
File *file_create(const char *path, struct stat *stats) {
|
|
File *file = (File *)malloc(sizeof(File));
|
|
if (file == NULL) {
|
|
perror("FATAL ERROR: Could not allocate memory for file struct");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
file->stats = *stats;
|
|
|
|
int path_len = strlen(path);
|
|
file->path = (char *)malloc(path_len + 1);
|
|
if (file->path == NULL) {
|
|
perror("FATAL ERROR: Could not allocate memory for path file string");
|
|
free(file);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
strcpy(file->path, path);
|
|
file->data = NULL;
|
|
return file;
|
|
}
|
|
|
|
void file_destroy(void *item) {
|
|
if (item == NULL)
|
|
return;
|
|
File *file = (File *)item;
|
|
free(file->data);
|
|
file->data = NULL;
|
|
free(file->path);
|
|
file->path = NULL;
|
|
free(file);
|
|
}
|
|
|
|
void file_load_data(File *file) {
|
|
if (file == NULL)
|
|
return;
|
|
file->data = malloc(file->stats.st_size);
|
|
if (file->data == NULL) {
|
|
perror("Could not allocate memeor y for file data!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
file_content_to_buffer(file, file->data);
|
|
}
|
|
|
|
void file_print(void *item) {
|
|
if (item == NULL)
|
|
return;
|
|
printf("%s\n", ((File *)item)->path);
|
|
}
|
|
|
|
void file_content_to_buffer(File *file, char *buffer) {
|
|
if (buffer == NULL) {
|
|
perror("Buffer is to write file content to is NULL!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
FILE *file_pointer = fopen(file->path, "rb");
|
|
if (file_pointer == NULL) {
|
|
perror("Could not open the file!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
size_t bytes_read = fread(buffer, 1, file->stats.st_size, file_pointer);
|
|
if (bytes_read != (size_t)file->stats.st_size) {
|
|
perror("Read to many or to less bytes from File!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
fclose(file_pointer);
|
|
}
|
|
|
|
FileReceive *file_receive_create(char *path, DataFragment *data_fragment) {
|
|
FileReceive *file = malloc(sizeof(FileReceive));
|
|
file->path = path;
|
|
file->data_fragment = data_fragment;
|
|
return file;
|
|
}
|
|
|
|
void file_receive_destroy(void *file_receive) {
|
|
if (file_receive == NULL)
|
|
return;
|
|
FileReceive *file = (FileReceive *)file_receive;
|
|
data_fragment_delete(file->data_fragment);
|
|
free(file->path);
|
|
free(file);
|
|
}
|
|
|
|
Chunk *chunk_create(File **items, int element_count) {
|
|
Chunk *chunk = (Chunk *)malloc(sizeof(Chunk));
|
|
if (chunk == NULL) {
|
|
perror("FATAL ERROR: Could not allocate memory for chunk structure");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
chunk->items = (File **)malloc(element_count * sizeof(File *));
|
|
if (chunk->items == NULL) {
|
|
perror("FATAL ERROR: Could not allocate memory for items of chunk "
|
|
"structure");
|
|
free(chunk);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (int i = 0; i < element_count; i++) {
|
|
chunk->items[i] = items[i];
|
|
}
|
|
chunk->element_count = element_count;
|
|
return chunk;
|
|
}
|
|
|
|
void chunk_destroy(void *item) {
|
|
if (item == NULL) {
|
|
return;
|
|
}
|
|
Chunk *chunk = (Chunk *)item;
|
|
for (int i = 0; i < chunk->element_count; ++i) {
|
|
if (chunk->items[i] != NULL) {
|
|
file_destroy(chunk->items[i]);
|
|
}
|
|
}
|
|
free(chunk->items);
|
|
free(chunk);
|
|
}
|
|
|
|
void chunk_print(void *item) {
|
|
if (item == NULL)
|
|
return;
|
|
Chunk *chunk = (Chunk *)item;
|
|
for (int i = 0; i < chunk->element_count; ++i)
|
|
if (chunk->items[i] != NULL)
|
|
file_print(chunk->items[i]);
|
|
}
|
|
|
|
Data *chunk_format(Chunk *chunk) {
|
|
unsigned long long buffer_size = 0;
|
|
for (int i = 0; i < chunk->element_count; ++i) {
|
|
buffer_size += sizeof(int);
|
|
buffer_size += strlen(chunk->items[i]->path);
|
|
buffer_size += sizeof(unsigned long long);
|
|
buffer_size += chunk->items[i]->stats.st_size;
|
|
}
|
|
|
|
char *data = malloc(buffer_size);
|
|
if (data == NULL) {
|
|
perror("Could not allocate data for ChunkFormated!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
char *current_data_pointer = data;
|
|
for (int i = 0; i < chunk->element_count; ++i) {
|
|
File *file = chunk->items[i];
|
|
// add path len
|
|
int path_length = (int)strlen(file->path);
|
|
memcpy(current_data_pointer, &path_length, sizeof(int));
|
|
current_data_pointer += sizeof(int);
|
|
// add path
|
|
memcpy(current_data_pointer, file->path, path_length);
|
|
current_data_pointer += path_length;
|
|
// add file data len
|
|
unsigned long long file_length = file->stats.st_size;
|
|
memcpy(current_data_pointer, &file_length, sizeof(unsigned long long));
|
|
current_data_pointer += sizeof(unsigned long long);
|
|
// add file data
|
|
file_content_to_buffer(file, current_data_pointer);
|
|
current_data_pointer += file_length;
|
|
}
|
|
if (current_data_pointer - data != (long)(long)buffer_size) {
|
|
perror("Buffer of Chunk wasn't filled enough!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
return chunk_data_create(data, buffer_size);
|
|
}
|
|
|
|
Data *chunk_compress(Chunk *chunk) {
|
|
unsigned long long data_size = 0;
|
|
for (int i = 0; i < chunk->element_count; i++) {
|
|
data_size += sizeof(unsigned long long);
|
|
data_size += strlen(chunk->items[i]->path);
|
|
data_size += sizeof(unsigned long long);
|
|
data_size += chunk->items[i]->stats.st_size;
|
|
}
|
|
char *data = malloc(data_size);
|
|
char *data_pointer = data;
|
|
for (int i = 0; i < chunk->element_count; i++) {
|
|
// path length
|
|
unsigned long long path_len = strlen(chunk->items[i]->path);
|
|
memcpy(data_pointer, &path_len, sizeof(unsigned long long));
|
|
data_pointer += sizeof(unsigned long long);
|
|
memcpy(data_pointer, chunk->items[i]->path, path_len);
|
|
data_pointer += path_len;
|
|
// file data
|
|
unsigned long long data_size = chunk->items[i]->stats.st_size;
|
|
memcpy(data_pointer, &data_size, sizeof(unsigned long long));
|
|
data_pointer += data_size;
|
|
memcpy(data_pointer, chunk->items[i]->data, data_size);
|
|
data_pointer += data_size;
|
|
}
|
|
size_t compressed_data_size = ZSTD_compressBound(data_size);
|
|
void *compressed_data = malloc(compressed_data_size);
|
|
if (compressed_data == NULL) {
|
|
log_message(ERROR, "Could not allocate memory for compressed Chunk");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
// TODO
|
|
}
|
|
|
|
Data *chunk_data_create(void *data, unsigned long long data_size) {
|
|
Data *chunk_formated = malloc(sizeof(Data));
|
|
if (chunk_formated == NULL) {
|
|
perror("Could not allocate memory for ChunkFormated");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
chunk_formated->data = data;
|
|
chunk_formated->data_size = data_size;
|
|
return chunk_formated;
|
|
}
|
|
|
|
void chunk_data_delete(void *chunk) {
|
|
Data *chunk_data = (Data *)chunk;
|
|
free(chunk_data->data);
|
|
free(chunk_data);
|
|
}
|
|
|
|
// void chunk_data_to_disk(ChunkData *chunk_formated, char *root_directory) {
|
|
// char *current_data_pointer = chunk_formated->data;
|
|
// while (current_data_pointer - (char *)chunk_formated->data <
|
|
// chunk_formated->data_size) {
|
|
// // get path length
|
|
// int path_length = 0;
|
|
// memcpy(&path_length, (int *)current_data_pointer, sizeof(int));
|
|
// current_data_pointer += sizeof(int);
|
|
// // get path
|
|
// int path_dir_size =
|
|
// (strlen(root_directory) + path_length + 1) * sizeof(char);
|
|
// char *path = (char *)malloc(path_dir_size);
|
|
// if (path == NULL) {
|
|
// perror("Could not allocate memory for path!");
|
|
// exit(EXIT_FAILURE);
|
|
// }
|
|
// snprintf(path, path_dir_size, "%s%.*s", root_directory, path_length,
|
|
// current_data_pointer);
|
|
// current_data_pointer += sizeof(char) * path_length;
|
|
// // get data length
|
|
// unsigned long long data_size = 0;
|
|
// memcpy(&data_size, (unsigned long long *)current_data_pointer,
|
|
// sizeof(unsigned long long));
|
|
// current_data_pointer += sizeof(unsigned long long);
|
|
// // create File Receive
|
|
// FileReceive *file =
|
|
// file_receive_create(path, data_size, current_data_pointer);
|
|
// file_receive_print(file);
|
|
// file_receive_to_disk(file);
|
|
// current_data_pointer += sizeof(char) * data_size;
|
|
// }
|
|
// }
|
|
--- src/shared/socket.c ---
|
|
|
|
#include "socket.h"
|
|
#include "log.h"
|
|
#include <arpa/inet.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
Server *server_create(int port) {
|
|
Server *server = (Server *)malloc(sizeof(Server));
|
|
if (server == NULL) {
|
|
perror("Could not allocate space for Server");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (file_descriptor < 0) {
|
|
perror("Could not create Socket!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
server->file_descriptor = file_descriptor;
|
|
int opt = 1;
|
|
if (setsockopt(server->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &opt,
|
|
sizeof(opt))) {
|
|
perror("Error setting a socket option!");
|
|
close(server->file_descriptor);
|
|
free(server);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
server->address.sin_family = AF_INET;
|
|
server->address.sin_addr.s_addr = INADDR_ANY;
|
|
server->address.sin_port = htons(port);
|
|
server->address_length = sizeof(server->address);
|
|
|
|
if (bind(server->file_descriptor, (struct sockaddr *)&server->address,
|
|
server->address_length) < 0) {
|
|
perror("Could not bind server");
|
|
close(server->file_descriptor);
|
|
free(server);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return server;
|
|
}
|
|
|
|
void server_delete(Server *server) {
|
|
free(server);
|
|
server = NULL;
|
|
};
|
|
|
|
void server_listen(Server *server, void (*handler)(int file_descriptor)) {
|
|
log_message(LOG_LEVEL_INFO, "Start Listening on Port: %d",
|
|
server->address.sin_port);
|
|
if (listen(server->file_descriptor, 3) < 0) {
|
|
perror("Could not listen on port!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int file_descriptor =
|
|
accept(server->file_descriptor, (struct sockaddr *)&server->address,
|
|
&server->address_length);
|
|
if (server->file_descriptor < 0) {
|
|
perror("Could not accept the connection");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
log_message(LOG_LEVEL_INFO, "Received Connection");
|
|
handler(file_descriptor);
|
|
close(server->file_descriptor);
|
|
close(file_descriptor);
|
|
}
|
|
|
|
Client *client_create() {
|
|
int file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (file_descriptor < 0) {
|
|
perror("Could not create Socket!");
|
|
exit(EXIT_FAILURE);
|
|
};
|
|
|
|
Client *client = (Client *)malloc(sizeof(Client));
|
|
client->file_descriptor = file_descriptor;
|
|
client->address.sin_family = AF_INET;
|
|
client->address_length = sizeof(client->address);
|
|
return client;
|
|
}
|
|
|
|
void client_connect(Client *client, char *host, int port) {
|
|
client->address.sin_port = htons(port);
|
|
|
|
if (inet_pton(AF_INET, host, &client->address.sin_addr) <= 0) {
|
|
perror("Could not convert host address!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (connect(client->file_descriptor, (struct sockaddr *)&client->address,
|
|
client->address_length) < 0) {
|
|
perror("Could not connect to Server!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
void client_disconnect(Client *client) { close(client->file_descriptor); }
|
|
|
|
void client_delete(Client *client) {
|
|
if (client == NULL)
|
|
return;
|
|
free(client);
|
|
}
|
|
|
|
DataFragment *data_fragment_create(void *data, unsigned long long size) {
|
|
DataFragment *data_fragment = malloc(sizeof(DataFragment));
|
|
data_fragment->data = data;
|
|
data_fragment->size = size;
|
|
return data_fragment;
|
|
}
|
|
|
|
void data_fragment_delete(void *data_fragment) {
|
|
if (data_fragment == NULL)
|
|
return;
|
|
DataFragment *fragment = (DataFragment *)data_fragment;
|
|
free(fragment->data);
|
|
free(fragment);
|
|
}
|
|
|
|
void send_n_data(int file_descriptor, void *data, NET_SIZE data_size) {
|
|
log_message(LOG_LEVEL_DEBUG, " Sending n Data: %d", data_size);
|
|
NET_SIZE total_bytes_send = 0;
|
|
while (total_bytes_send < data_size) {
|
|
long long bytes_send =
|
|
send(file_descriptor, (char *)data + total_bytes_send,
|
|
data_size - total_bytes_send, 0);
|
|
if (bytes_send == 0) {
|
|
perror("Could not send data!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
total_bytes_send += bytes_send;
|
|
}
|
|
log_message(LOG_LEVEL_DEBUG, " Send n Data: %d", total_bytes_send);
|
|
}
|
|
|
|
void receive_n_data(int file_descriptor, void *data, NET_SIZE data_size) {
|
|
log_message(LOG_LEVEL_DEBUG, " Receiving n Data: %d", data_size);
|
|
NET_SIZE total_bytes_received = 0;
|
|
while (total_bytes_received < data_size) {
|
|
long long bytes_received =
|
|
recv(file_descriptor, data + total_bytes_received,
|
|
data_size - total_bytes_received, 0);
|
|
if (bytes_received == -1 || bytes_received == 0) {
|
|
perror("Could not receive bytes!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
total_bytes_received += bytes_received;
|
|
}
|
|
log_message(LOG_LEVEL_DEBUG, " Received n Data: %d", total_bytes_received);
|
|
}
|
|
|
|
void send_str(int file_descriptor, char *data) {
|
|
NET_SIZE size = strlen(data);
|
|
send_n_data(file_descriptor, &size, sizeof(NET_SIZE));
|
|
send_n_data(file_descriptor, data, size);
|
|
log_message(LOG_LEVEL_DEBUG, "Send String: %s", data);
|
|
}
|
|
|
|
char *receive_str(int file_descriptor) {
|
|
NET_SIZE size;
|
|
receive_n_data(file_descriptor, &size, sizeof(NET_SIZE));
|
|
char *data = (char *)malloc(size + 1);
|
|
receive_n_data(file_descriptor, data, size);
|
|
data[size] = '\0';
|
|
log_message(LOG_LEVEL_DEBUG, "Received String: %s", data);
|
|
return data;
|
|
}
|
|
|
|
void send_data(int file_descriptor, void *data, unsigned long long data_size) {
|
|
send_n_data(file_descriptor, &data_size, sizeof(unsigned long long));
|
|
send_n_data(file_descriptor, data, data_size);
|
|
log_message(LOG_LEVEL_DEBUG, "Send %lld data", data_size);
|
|
}
|
|
|
|
DataFragment *receive_data(int file_descriptor) {
|
|
unsigned long long size = 0;
|
|
receive_n_data(file_descriptor, &size, sizeof(unsigned long long));
|
|
void *data = malloc(size);
|
|
receive_n_data(file_descriptor, data, size);
|
|
log_message(LOG_LEVEL_DEBUG, "Received %lld data", size);
|
|
return data_fragment_create(data, size);
|
|
}
|
|
|
|
void send_int(int file_descriptor, int data) {
|
|
send_n_data(file_descriptor, &data, sizeof(int));
|
|
log_message(LOG_LEVEL_DEBUG, "Send Int: %d", data);
|
|
}
|
|
|
|
int receive_int(int file_descriptor) {
|
|
int data;
|
|
receive_n_data(file_descriptor, &data, sizeof(int));
|
|
log_message(LOG_LEVEL_DEBUG, "Received Int: %d", data);
|
|
return data;
|
|
}
|
|
|
|
void send_status(int file_descriptor, Status status) {
|
|
send_n_data(file_descriptor, &status, sizeof(Status));
|
|
log_message(LOG_LEVEL_DEBUG, "Send Status: %d", status);
|
|
}
|
|
|
|
Status receive_status(int file_descriptor) {
|
|
Status data;
|
|
receive_n_data(file_descriptor, &data, sizeof(Status));
|
|
log_message(LOG_LEVEL_DEBUG, "Received Status: %d", data);
|
|
return data;
|
|
}
|
|
--- src/shared/log.c ---
|
|
|
|
#include "log.h"
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
|
|
static const char *log_level_strings[] = {"DEBUG", "INFO", "WARN", "ERROR"};
|
|
static LogLevel current_log_level = LOG_LEVEL_DEBUG;
|
|
|
|
void log_message(LogLevel log_level, char *format, ...) {
|
|
if (log_level < current_log_level)
|
|
return;
|
|
time_t now = time(NULL);
|
|
struct tm *t = localtime(&now);
|
|
|
|
// Print timestamp and log level to the file
|
|
printf("%04d-%02d-%02d %02d:%02d:%02d [%s]: ", t->tm_year + 1900,
|
|
t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
|
|
log_level_strings[log_level]);
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
vprintf(format, args);
|
|
va_end(args);
|
|
printf("\n");
|
|
}
|
|
--- src/shared/config.c ---
|
|
|
|
#include "config.h"
|
|
#include "socket.h"
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
Config *config_create(char *version, char *send_directory,
|
|
char *receive_directory, bool save_to_disk,
|
|
bool use_multithreading, bool use_chunk_serialization,
|
|
bool use_compression, bool use_multiprocessing,
|
|
int num_connections) {
|
|
|
|
Config *config = malloc(sizeof(Config));
|
|
config->version = version;
|
|
config->send_directory = send_directory;
|
|
config->receive_root_directory = receive_directory;
|
|
config->save_to_disk = save_to_disk;
|
|
config->use_multithreading = use_multithreading;
|
|
config->use_chunk_serialization = use_chunk_serialization;
|
|
config->use_compression = use_compression;
|
|
config->use_multiprocessing = use_multiprocessing;
|
|
config->num_connections = num_connections;
|
|
return config;
|
|
}
|
|
|
|
void config_delete(Config *config) {
|
|
free(config->version);
|
|
free(config->send_directory);
|
|
free(config->receive_root_directory);
|
|
free(config);
|
|
}
|
|
|
|
void config_send(int file_descriptor, Config *config) {
|
|
send_str(file_descriptor, config->version);
|
|
send_str(file_descriptor, config->send_directory);
|
|
send_str(file_descriptor, config->receive_root_directory);
|
|
send_int(file_descriptor, config->save_to_disk);
|
|
send_int(file_descriptor, config->use_multithreading);
|
|
send_int(file_descriptor, config->use_chunk_serialization);
|
|
send_int(file_descriptor, config->use_compression);
|
|
send_int(file_descriptor, config->use_multiprocessing);
|
|
send_int(file_descriptor, config->num_connections);
|
|
if (receive_status(file_descriptor) != OK) {
|
|
perror("Error transmitting config!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
Config *config_receive(int file_descriptor) {
|
|
Config *config = (Config *)malloc(sizeof(Config));
|
|
config->version = receive_str(file_descriptor);
|
|
config->send_directory = receive_str(file_descriptor);
|
|
config->receive_root_directory = receive_str(file_descriptor);
|
|
config->save_to_disk = receive_int(file_descriptor);
|
|
config->use_multithreading = receive_int(file_descriptor);
|
|
config->use_chunk_serialization = receive_int(file_descriptor);
|
|
config->use_compression = receive_int(file_descriptor);
|
|
config->use_multiprocessing = receive_int(file_descriptor);
|
|
config->num_connections = receive_int(file_descriptor);
|
|
send_status(file_descriptor, OK);
|
|
return config;
|
|
}
|
|
--- src/server/server.c ---
|
|
|
|
#include "chunk.h"
|
|
#include "config.h"
|
|
#include "multiprocessing.h"
|
|
#include "queue.h"
|
|
#include "socket.h"
|
|
#include "unistd.h"
|
|
#include "utils.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <threads.h>
|
|
|
|
FileReceive *receive_file_receive(int file_descriptor) {
|
|
char *path = (char *)receive_str(file_descriptor);
|
|
printf("%s\n", path);
|
|
DataFragment *file_data_fragment = receive_data(file_descriptor);
|
|
FileReceive *file = file_receive_create(path, file_data_fragment);
|
|
return file;
|
|
}
|
|
|
|
int receive_thread(void *pipeline_context) {
|
|
PipelineContextReceiver *context =
|
|
(PipelineContextReceiver *)pipeline_context;
|
|
mtx_lock(&context->mutex);
|
|
int file_descriptor = context->file_descriptor;
|
|
mtx_unlock(&context->mutex);
|
|
|
|
while (receive_status(file_descriptor) == NEXT) {
|
|
FileReceive *file = receive_file_receive(file_descriptor);
|
|
queue_enqueue_multithreaded(context->queue, file, &context->mutex,
|
|
&context->condition_not_empty,
|
|
&context->condition_not_full);
|
|
}
|
|
mtx_lock(&context->mutex);
|
|
context->receiver_done = true;
|
|
cnd_signal(&context->condition_not_empty);
|
|
mtx_unlock(&context->mutex);
|
|
return thrd_success;
|
|
}
|
|
|
|
int write_thread(void *pipeline_context) {
|
|
PipelineContextReceiver *context =
|
|
(PipelineContextReceiver *)pipeline_context;
|
|
mtx_lock(&context->mutex);
|
|
bool save_to_disk = context->config->save_to_disk;
|
|
char *root_directory = str_dup(context->config->receive_root_directory);
|
|
mtx_unlock(&context->mutex);
|
|
|
|
while (true) {
|
|
FileReceive *file = queue_dequeue_multithreaded(
|
|
context->queue, &context->mutex, &context->condition_not_empty,
|
|
&context->condition_not_full, &context->receiver_done);
|
|
if (file == NULL) {
|
|
free(root_directory);
|
|
return thrd_success;
|
|
}
|
|
if (save_to_disk)
|
|
to_disk(path_cat(root_directory, file->path), file->data_fragment->data,
|
|
file->data_fragment->size);
|
|
}
|
|
}
|
|
|
|
int receive_files(Config *config, int file_descriptor) {
|
|
Status status = receive_status(file_descriptor);
|
|
while (status == NEXT) {
|
|
FileReceive *file = receive_file_receive(file_descriptor);
|
|
if (config->save_to_disk)
|
|
to_disk(path_cat(config->receive_root_directory, file->path),
|
|
file->data_fragment->data, file->data_fragment->size);
|
|
file_receive_destroy(file);
|
|
status = receive_status(file_descriptor);
|
|
}
|
|
if (status != FINISHED) {
|
|
send_status(file_descriptor, ERROR);
|
|
return -1;
|
|
}
|
|
send_status(file_descriptor, OK);
|
|
return 0;
|
|
}
|
|
|
|
void handler(int file_descriptor) {
|
|
Config *config = config_receive(file_descriptor);
|
|
if (config->use_multithreading) {
|
|
PipelineContextReceiver *context = pipeline_context_receiver_create(
|
|
config, queue_create(100, file_receive_destroy), file_descriptor);
|
|
thrd_t receiver, writer;
|
|
if (thrd_create(&receiver, receive_thread, context) != thrd_success ||
|
|
thrd_create(&writer, write_thread, context) != thrd_success) {
|
|
perror("Error creating Threads!");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
thrd_join(receiver, NULL);
|
|
thrd_join(writer, NULL);
|
|
pipeline_context_receiver_destroy(context);
|
|
} else
|
|
receive_files(config, file_descriptor);
|
|
close(file_descriptor);
|
|
}
|
|
|
|
int main() {
|
|
Server *server = server_create(8080);
|
|
server_listen(server, handler);
|
|
server_delete(server);
|
|
return 0;
|
|
}
|
|
--- Makefile ---
|
|
|
|
CC = gcc
|
|
CFLAGS = -Wall -g -pthread -std=c11 -Isrc/shared -Isrc/server -Isrc/client
|
|
LIBS = -lzstd
|
|
|
|
OBJ_DIR = obj
|
|
SRC_DIR = src
|
|
SHARED_SRCS = $(wildcard src/shared/*.c)
|
|
SHARED_OBJS = $(patsubst $(SRC_DIR)/shared/%.c, $(OBJ_DIR)/shared/%.o, $(SHARED_SRCS))
|
|
|
|
SERVER = server
|
|
SERVER_SRCS = $(wildcard src/server/*.c)
|
|
SERVER_OBJS = $(patsubst $(SRC_DIR)/server/%.c, $(OBJ_DIR)/server/%.o, $(SERVER_SRCS))
|
|
|
|
CLIENT = client
|
|
CLIENT_SRCS = $(wildcard src/client/*.c)
|
|
CLIENT_OBJS = $(patsubst $(SRC_DIR)/client/%.c, $(OBJ_DIR)/client/%.o, $(CLIENT_SRCS))
|
|
|
|
all: $(SERVER) $(CLIENT)
|
|
|
|
$(SERVER): $(SHARED_OBJS) $(SERVER_OBJS)
|
|
$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
|
|
$(CLIENT): $(SHARED_OBJS) $(CLIENT_OBJS)
|
|
$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
|
|
|
|
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
|
|
@mkdir -p $(dir $@)
|
|
$(CC) $(CFLAGS) -c $< -o $@
|
|
|
|
.PHONY: all clean
|
|
|
|
clean:
|
|
rm -r $(OBJ_DIR) $(SERVER) $(CLIENT)
|