Merge pull request 'feat: add SSH transport, rsync-style CLI, and --stdio server mode' (#16) from transport-abstraction into main
Reviewed-on: #16
This commit is contained in:
@@ -20,5 +20,6 @@ pkgs.mkShell {
|
|||||||
shellHook = ''
|
shellHook = ''
|
||||||
export NIX_ENFORCE_PURITY=0
|
export NIX_ENFORCE_PURITY=0
|
||||||
cmake -B build
|
cmake -B build
|
||||||
|
export PATH="$PWD/build:$PATH"
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|||||||
+132
-34
@@ -16,6 +16,9 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
|
||||||
|
static char *server_host = "127.0.0.1";
|
||||||
|
static int server_port = 8080;
|
||||||
|
|
||||||
int send_chunk(Client *client, Chunk *chunk, Config *config) {
|
int send_chunk(Client *client, Chunk *chunk, Config *config) {
|
||||||
if (config->use_chunk_serialization) {
|
if (config->use_chunk_serialization) {
|
||||||
send_status(client->file_descriptor, STATUS_CHUNK);
|
send_status(client->file_descriptor, STATUS_CHUNK);
|
||||||
@@ -99,12 +102,17 @@ int load_files_multithreaded(void *pipeline_context) {
|
|||||||
|
|
||||||
int send_chunks_multithreaded(void *pipeline_context) {
|
int send_chunks_multithreaded(void *pipeline_context) {
|
||||||
PipelineContextSender *context = (PipelineContextSender *)pipeline_context;
|
PipelineContextSender *context = (PipelineContextSender *)pipeline_context;
|
||||||
Client *client = client_create();
|
Client *client;
|
||||||
const char *env_ip = getenv("FASTSYNC_SERVER_IP");
|
if (context->config->transport == TRANSPORT_SSH) {
|
||||||
const char *ip = env_ip ? env_ip : "127.0.0.1";
|
if (context->config->use_sendfile) {
|
||||||
const char *env_port = getenv("FASTSYNC_SERVER_PORT");
|
fprintf(stderr, "Error: -f/--sendfile is not supported with SSH transport\n");
|
||||||
int port = env_port ? atoi(env_port) : 8080;
|
return 1;
|
||||||
client_connect(client, (char *)ip, port);
|
}
|
||||||
|
client = client_connect_ssh(context->config->ssh_destination);
|
||||||
|
} else {
|
||||||
|
client = client_create();
|
||||||
|
client_connect(client, server_host, server_port);
|
||||||
|
}
|
||||||
config_send(client->file_descriptor, context->config);
|
config_send(client->file_descriptor, context->config);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -114,9 +122,10 @@ int send_chunks_multithreaded(void *pipeline_context) {
|
|||||||
&context->condition_not_full_loader, &context->loader_done);
|
&context->condition_not_full_loader, &context->loader_done);
|
||||||
if (current_chunk == NULL) {
|
if (current_chunk == NULL) {
|
||||||
send_status(client->file_descriptor, STATUS_FINISHED);
|
send_status(client->file_descriptor, STATUS_FINISHED);
|
||||||
|
int ok = receive_status(client->file_descriptor) == STATUS_OK;
|
||||||
client_disconnect(client);
|
client_disconnect(client);
|
||||||
client_delete(client);
|
client_delete(client);
|
||||||
return thrd_success;
|
return ok ? thrd_success : 1;
|
||||||
}
|
}
|
||||||
if (send_chunk(client, current_chunk, context->config) != 0) {
|
if (send_chunk(client, current_chunk, context->config) != 0) {
|
||||||
perror("Something unexpected happend while sending the chunk");
|
perror("Something unexpected happend while sending the chunk");
|
||||||
@@ -127,12 +136,17 @@ int send_chunks_multithreaded(void *pipeline_context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int send_files(Config *config) {
|
int send_files(Config *config) {
|
||||||
Client *client = client_create();
|
Client *client;
|
||||||
const char *env_ip = getenv("FASTSYNC_SERVER_IP");
|
if (config->transport == TRANSPORT_SSH) {
|
||||||
const char *ip = env_ip ? env_ip : "127.0.0.1";
|
if (config->use_sendfile) {
|
||||||
const char *env_port = getenv("FASTSYNC_SERVER_PORT");
|
fprintf(stderr, "Error: -f/--sendfile is not supported with SSH transport\n");
|
||||||
int port = env_port ? atoi(env_port) : 8080;
|
return 1;
|
||||||
client_connect(client, (char *)ip, port);
|
}
|
||||||
|
client = client_connect_ssh(config->ssh_destination);
|
||||||
|
} else {
|
||||||
|
client = client_create();
|
||||||
|
client_connect(client, server_host, server_port);
|
||||||
|
}
|
||||||
config_send(client->file_descriptor, config);
|
config_send(client->file_descriptor, config);
|
||||||
DirectoryScanner *scanner = directory_scanner_create(config->send_directory, config->use_metadata);
|
DirectoryScanner *scanner = directory_scanner_create(config->send_directory, config->use_metadata);
|
||||||
Chunk *current_chunk;
|
Chunk *current_chunk;
|
||||||
@@ -145,12 +159,11 @@ int send_files(Config *config) {
|
|||||||
chunk_destroy(current_chunk);
|
chunk_destroy(current_chunk);
|
||||||
}
|
}
|
||||||
send_status(client->file_descriptor, STATUS_FINISHED);
|
send_status(client->file_descriptor, STATUS_FINISHED);
|
||||||
if (receive_status(client->file_descriptor) != STATUS_OK)
|
int ok = receive_status(client->file_descriptor) == STATUS_OK;
|
||||||
return -1;
|
|
||||||
directory_scanner_destroy(scanner);
|
directory_scanner_destroy(scanner);
|
||||||
client_disconnect(client);
|
client_disconnect(client);
|
||||||
client_delete(client);
|
client_delete(client);
|
||||||
return 0;
|
return ok ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int send_files_multithreaded(Config *config) {
|
int send_files_multithreaded(Config *config) {
|
||||||
@@ -176,37 +189,65 @@ int send_files_multithreaded(Config *config) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_arg(char *argument_given, char *argument_to_set, bool *result,
|
static int is_remote_dest(const char *s) {
|
||||||
char *message) {
|
const char *colon = strchr(s, ':');
|
||||||
if (strcmp(argument_given, argument_to_set) == 0) {
|
if (!colon) return 0;
|
||||||
*result = true;
|
if (colon == s) return 0;
|
||||||
log_message(LOG_LEVEL_INFO, message);
|
for (const char *p = s; p < colon; p++) {
|
||||||
|
if (*p == '/') return 0;
|
||||||
}
|
}
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_usage(void) {
|
||||||
|
printf("Usage:\n");
|
||||||
|
printf(" fastsync [options] <source> <destination>\n");
|
||||||
|
printf(" fastsync [options] --source-dir <src> --dest-dir <dst>\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("Destination formats:\n");
|
||||||
|
printf(" user@host:/path SSH transport (rsync-style)\n");
|
||||||
|
printf(" host:/path SSH transport (current user)\n");
|
||||||
|
printf(" /local/path TCP transport (requires server on localhost:8080)\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("Options:\n");
|
||||||
|
printf(" -c [level] Enable compression (level 1-22, default 5)\n");
|
||||||
|
printf(" -m Enable multithreading\n");
|
||||||
|
printf(" -s Enable chunk serialization\n");
|
||||||
|
printf(" -f Enable sendfile (TCP only, not with -c or -s)\n");
|
||||||
|
printf(" -v, --verbose Enable debug logging\n");
|
||||||
|
printf(" -M, --preserve Preserve file metadata\n");
|
||||||
|
printf(" --source-dir <path> Source directory\n");
|
||||||
|
printf(" --dest-dir <path> Destination directory\n");
|
||||||
|
printf(" --save-to-disk Write received files to disk\n");
|
||||||
|
printf(" --server-host <ip> Server IP address (default: 127.0.0.1)\n");
|
||||||
|
printf(" --server-port <n> Server port (default: 8080)\n");
|
||||||
|
printf(" --help Show this help\n");
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
const char *env_source = getenv("FASTSYNC_SOURCE_DIR");
|
const char *env_source = getenv("FASTSYNC_SOURCE_DIR");
|
||||||
const char *env_dest = getenv("FASTSYNC_DEST_DIR");
|
const char *env_dest = getenv("FASTSYNC_DEST_DIR");
|
||||||
const char *env_save = getenv("FASTSYNC_SAVE_TO_DISK");
|
const char *env_save = getenv("FASTSYNC_SAVE_TO_DISK");
|
||||||
|
|
||||||
char *source_dir =
|
|
||||||
env_source ? str_dup((char *)env_source)
|
|
||||||
: str_dup("/home/taptap/Nextcloud/Uni/moodle/B. Schnor: "
|
|
||||||
"Konzepte Paralleler Programmierung, SoSe 2026");
|
|
||||||
char *dest_dir =
|
|
||||||
env_dest ? str_dup((char *)env_dest) : str_dup("./data_copied");
|
|
||||||
bool save_to_disk = false;
|
bool save_to_disk = false;
|
||||||
if (env_save &&
|
if (env_save &&
|
||||||
(strcmp(env_save, "true") == 0 || strcmp(env_save, "1") == 0)) {
|
(strcmp(env_save, "true") == 0 || strcmp(env_save, "1") == 0)) {
|
||||||
save_to_disk = true;
|
save_to_disk = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Config *config = config_create(str_dup("1.0.0"), source_dir, dest_dir,
|
Config *config = config_create(str_dup("1.0.0"), NULL, NULL,
|
||||||
save_to_disk, false, false, false, false, 5, 20, false);
|
save_to_disk, false, false, false, false, 5, 20, false);
|
||||||
|
|
||||||
|
int positional_args[2];
|
||||||
|
int positional_count = 0;
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (strcmp(argv[i], "-c") == 0) {
|
if (strcmp(argv[i], "--help") == 0) {
|
||||||
|
print_usage();
|
||||||
|
return 0;
|
||||||
|
} else if (strcmp(argv[i], "-c") == 0) {
|
||||||
config->use_compression = true;
|
config->use_compression = true;
|
||||||
log_message(LOG_LEVEL_INFO, "Enabled Compression");
|
log_message(LOG_LEVEL_INFO, "Enabled Compression");
|
||||||
|
|
||||||
if (i + 1 < argc) {
|
if (i + 1 < argc) {
|
||||||
char *end_ptr;
|
char *end_ptr;
|
||||||
int level = strtol(argv[i + 1], &end_ptr, 10);
|
int level = strtol(argv[i + 1], &end_ptr, 10);
|
||||||
@@ -214,6 +255,7 @@ int main(int argc, char *argv[]) {
|
|||||||
config->compression_level = level;
|
config->compression_level = level;
|
||||||
log_message(LOG_LEVEL_INFO, "Set Compression level to %d",
|
log_message(LOG_LEVEL_INFO, "Set Compression level to %d",
|
||||||
config->compression_level);
|
config->compression_level);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (strcmp(argv[i], "--source-dir") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "--source-dir") == 0 && i + 1 < argc) {
|
||||||
@@ -230,19 +272,75 @@ int main(int argc, char *argv[]) {
|
|||||||
} else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--sendfile") == 0) {
|
} else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--sendfile") == 0) {
|
||||||
config->use_sendfile = true;
|
config->use_sendfile = true;
|
||||||
log_message(LOG_LEVEL_INFO, "Enabled sendfile");
|
log_message(LOG_LEVEL_INFO, "Enabled sendfile");
|
||||||
|
} else if (strcmp(argv[i], "-m") == 0) {
|
||||||
|
config->use_multithreading = true;
|
||||||
|
log_message(LOG_LEVEL_INFO, "Enabled Multithreading");
|
||||||
|
} else if (strcmp(argv[i], "-s") == 0) {
|
||||||
|
config->use_chunk_serialization = true;
|
||||||
|
log_message(LOG_LEVEL_INFO, "Enabled Chunk Serialization");
|
||||||
|
} else if (strcmp(argv[i], "--server-host") == 0 && i + 1 < argc) {
|
||||||
|
free(server_host);
|
||||||
|
server_host = str_dup(argv[++i]);
|
||||||
|
} else if (strcmp(argv[i], "--server-port") == 0 && i + 1 < argc) {
|
||||||
|
server_port = atoi(argv[++i]);
|
||||||
|
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
|
||||||
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
|
} else if (argv[i][0] == '-') {
|
||||||
|
fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
handle_arg(argv[i], "-m", &config->use_multithreading,
|
if (positional_count < 2)
|
||||||
"Enabled Multithreading");
|
positional_args[positional_count++] = i;
|
||||||
handle_arg(argv[i], "-s", &config->use_chunk_serialization,
|
else {
|
||||||
"Enabled Chunk Serialization");
|
fprintf(stderr, "Unexpected argument: %s\n", argv[i]);
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (positional_count == 2) {
|
||||||
|
free(config->send_directory);
|
||||||
|
free(config->receive_root_directory);
|
||||||
|
config->send_directory = str_dup(argv[positional_args[0]]);
|
||||||
|
config->receive_root_directory = str_dup(argv[positional_args[1]]);
|
||||||
|
config->save_to_disk = true;
|
||||||
|
|
||||||
|
if (is_remote_dest(config->receive_root_directory)) {
|
||||||
|
config->transport = TRANSPORT_SSH;
|
||||||
|
config->ssh_destination = str_dup(config->receive_root_directory);
|
||||||
|
char *colon = strchr(config->receive_root_directory, ':');
|
||||||
|
char *path = str_dup(colon + 1);
|
||||||
|
free(config->receive_root_directory);
|
||||||
|
config->receive_root_directory = path;
|
||||||
|
}
|
||||||
|
} else if (positional_count == 1) {
|
||||||
|
fprintf(stderr, "Error: missing destination argument\n");
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
if (!config->send_directory && env_source)
|
||||||
|
config->send_directory = str_dup((char *)env_source);
|
||||||
|
if (!config->receive_root_directory && env_dest)
|
||||||
|
config->receive_root_directory = str_dup((char *)env_dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config->send_directory || !config->receive_root_directory) {
|
||||||
|
fprintf(stderr, "Error: source and destination directories are required\n");
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
if (config->use_sendfile && (config->use_chunk_serialization || config->use_compression)) {
|
if (config->use_sendfile && (config->use_chunk_serialization || config->use_compression)) {
|
||||||
fprintf(stderr, "Error: -f/--sendfile cannot be combined with -c (compression) or -s (chunk serialization)\n");
|
fprintf(stderr, "Error: -f/--sendfile cannot be combined with -c (compression) or -s (chunk serialization)\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config->transport == TRANSPORT_SSH && config->use_sendfile) {
|
||||||
|
fprintf(stderr, "Error: -f/--sendfile is not supported with SSH transport\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (config->use_multithreading)
|
if (config->use_multithreading)
|
||||||
return send_files_multithreaded(config);
|
return send_files_multithreaded(config);
|
||||||
return send_files(config);
|
return send_files(config);
|
||||||
|
|||||||
+12
-1
@@ -10,6 +10,7 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <threads.h>
|
#include <threads.h>
|
||||||
|
|
||||||
File *file_receive(Config *config, int file_descriptor) {
|
File *file_receive(Config *config, int file_descriptor) {
|
||||||
@@ -161,12 +162,22 @@ void handler(int file_descriptor) {
|
|||||||
thrd_join(receiver, NULL);
|
thrd_join(receiver, NULL);
|
||||||
thrd_join(writer, NULL);
|
thrd_join(writer, NULL);
|
||||||
pipeline_context_receiver_destroy(context);
|
pipeline_context_receiver_destroy(context);
|
||||||
|
send_status(file_descriptor, STATUS_OK);
|
||||||
} else
|
} else
|
||||||
receive_files(config, file_descriptor);
|
receive_files(config, file_descriptor);
|
||||||
close(file_descriptor);
|
close(file_descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main(int argc, char *argv[]) {
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--stdio") == 0) {
|
||||||
|
io_set_fds(STDIN_FILENO, STDOUT_FILENO);
|
||||||
|
handler(STDIN_FILENO);
|
||||||
|
return 0;
|
||||||
|
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
|
||||||
|
set_log_level(LOG_LEVEL_DEBUG);
|
||||||
|
}
|
||||||
|
}
|
||||||
Server *server = server_create(8080);
|
Server *server = server_create(8080);
|
||||||
server_listen(server, handler);
|
server_listen(server, handler);
|
||||||
server_delete(server);
|
server_delete(server);
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ Config *config_create(char *version, char *send_directory,
|
|||||||
config->compression_level = compression_level;
|
config->compression_level = compression_level;
|
||||||
config->num_connections = num_connections;
|
config->num_connections = num_connections;
|
||||||
config->use_sendfile = use_sendfile;
|
config->use_sendfile = use_sendfile;
|
||||||
|
config->transport = TRANSPORT_TCP;
|
||||||
|
config->ssh_destination = NULL;
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,6 +31,7 @@ void config_delete(Config *config) {
|
|||||||
free(config->version);
|
free(config->version);
|
||||||
free(config->send_directory);
|
free(config->send_directory);
|
||||||
free(config->receive_root_directory);
|
free(config->receive_root_directory);
|
||||||
|
free(config->ssh_destination);
|
||||||
free(config);
|
free(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +66,8 @@ Config *config_receive(int file_descriptor) {
|
|||||||
config->compression_level = receive_int(file_descriptor);
|
config->compression_level = receive_int(file_descriptor);
|
||||||
config->num_connections = receive_int(file_descriptor);
|
config->num_connections = receive_int(file_descriptor);
|
||||||
config->use_sendfile = receive_int(file_descriptor);
|
config->use_sendfile = receive_int(file_descriptor);
|
||||||
|
config->transport = TRANSPORT_TCP;
|
||||||
|
config->ssh_destination = NULL;
|
||||||
send_status(file_descriptor, STATUS_OK);
|
send_status(file_descriptor, STATUS_OK);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TRANSPORT_TCP,
|
||||||
|
TRANSPORT_SSH
|
||||||
|
} TransportType;
|
||||||
|
|
||||||
typedef struct Config {
|
typedef struct Config {
|
||||||
char *version;
|
char *version;
|
||||||
char *send_directory;
|
char *send_directory;
|
||||||
@@ -16,6 +21,8 @@ typedef struct Config {
|
|||||||
bool use_metadata;
|
bool use_metadata;
|
||||||
int compression_level;
|
int compression_level;
|
||||||
int num_connections;
|
int num_connections;
|
||||||
|
TransportType transport;
|
||||||
|
char *ssh_destination;
|
||||||
} Config;
|
} Config;
|
||||||
|
|
||||||
Config *config_create(char *version, char *send_directory,
|
Config *config_create(char *version, char *send_directory,
|
||||||
|
|||||||
+8
-5
@@ -4,7 +4,11 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
static const char *log_level_strings[] = {"DEBUG", "INFO", "WARN", "ERROR"};
|
static const char *log_level_strings[] = {"DEBUG", "INFO", "WARN", "ERROR"};
|
||||||
static LogLevel current_log_level = LOG_LEVEL_DEBUG;
|
static LogLevel current_log_level = LOG_LEVEL_WARNING;
|
||||||
|
|
||||||
|
void set_log_level(LogLevel level) {
|
||||||
|
current_log_level = level;
|
||||||
|
}
|
||||||
|
|
||||||
void log_message(LogLevel log_level, char *format, ...) {
|
void log_message(LogLevel log_level, char *format, ...) {
|
||||||
if (log_level < current_log_level)
|
if (log_level < current_log_level)
|
||||||
@@ -12,14 +16,13 @@ void log_message(LogLevel log_level, char *format, ...) {
|
|||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
struct tm *t = localtime(&now);
|
struct tm *t = localtime(&now);
|
||||||
|
|
||||||
// Print timestamp and log level to the file
|
fprintf(stderr, "%04d-%02d-%02d %02d:%02d:%02d [%s]: ", t->tm_year + 1900,
|
||||||
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,
|
t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
|
||||||
log_level_strings[log_level]);
|
log_level_strings[log_level]);
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
vprintf(format, args);
|
vfprintf(stderr, format, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
printf("\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ typedef enum {
|
|||||||
} LogLevel;
|
} LogLevel;
|
||||||
|
|
||||||
void log_message(LogLevel log_level, char *message, ...);
|
void log_message(LogLevel log_level, char *message, ...);
|
||||||
|
void set_log_level(LogLevel level);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+138
-8
@@ -1,12 +1,134 @@
|
|||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static __thread int io_read_fd = -1;
|
||||||
|
static __thread int io_write_fd = -1;
|
||||||
|
|
||||||
|
void io_set_fds(int read_fd, int write_fd) {
|
||||||
|
io_read_fd = read_fd;
|
||||||
|
io_write_fd = write_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int io_fd(int dir_fd, int file_descriptor) {
|
||||||
|
return (dir_fd != -1) ? dir_fd : file_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char user[256];
|
||||||
|
char host[256];
|
||||||
|
char remote_path[4096];
|
||||||
|
} RemoteDest;
|
||||||
|
|
||||||
|
static int parse_remote_dest(const char *dest, RemoteDest *r) {
|
||||||
|
const char *colon = strchr(dest, ':');
|
||||||
|
if (!colon) return -1;
|
||||||
|
|
||||||
|
size_t remote_path_len = strlen(colon + 1);
|
||||||
|
if (remote_path_len >= sizeof(r->remote_path)) return -1;
|
||||||
|
memcpy(r->remote_path, colon + 1, remote_path_len + 1);
|
||||||
|
|
||||||
|
const char *at = memchr(dest, '@', colon - dest);
|
||||||
|
if (at) {
|
||||||
|
size_t user_len = at - dest;
|
||||||
|
if (user_len >= sizeof(r->user)) return -1;
|
||||||
|
memcpy(r->user, dest, user_len);
|
||||||
|
r->user[user_len] = '\0';
|
||||||
|
|
||||||
|
size_t host_len = colon - at - 1;
|
||||||
|
if (host_len >= sizeof(r->host)) return -1;
|
||||||
|
memcpy(r->host, at + 1, host_len);
|
||||||
|
r->host[host_len] = '\0';
|
||||||
|
} else {
|
||||||
|
r->user[0] = '\0';
|
||||||
|
size_t host_len = colon - dest;
|
||||||
|
if (host_len >= sizeof(r->host)) return -1;
|
||||||
|
memcpy(r->host, dest, host_len);
|
||||||
|
r->host[host_len] = '\0';
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Client *client_connect_ssh(char *destination) {
|
||||||
|
RemoteDest r;
|
||||||
|
if (parse_remote_dest(destination, &r) != 0) {
|
||||||
|
fprintf(stderr, "Invalid remote destination: %s\n", destination);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sv[2];
|
||||||
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
|
||||||
|
perror("socketpair failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int exec_pipe[2];
|
||||||
|
if (pipe(exec_pipe) < 0) {
|
||||||
|
perror("pipe failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("fork failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
close(sv[0]);
|
||||||
|
close(exec_pipe[0]);
|
||||||
|
fcntl(exec_pipe[1], F_SETFD, FD_CLOEXEC);
|
||||||
|
|
||||||
|
if (sv[1] != STDIN_FILENO)
|
||||||
|
dup2(sv[1], STDIN_FILENO);
|
||||||
|
if (sv[1] != STDOUT_FILENO)
|
||||||
|
dup2(sv[1], STDOUT_FILENO);
|
||||||
|
if (sv[1] > 1) close(sv[1]);
|
||||||
|
|
||||||
|
char ssh_user[512];
|
||||||
|
if (r.user[0] != '\0')
|
||||||
|
snprintf(ssh_user, sizeof(ssh_user), "%s@%s", r.user, r.host);
|
||||||
|
else
|
||||||
|
snprintf(ssh_user, sizeof(ssh_user), "%s", r.host);
|
||||||
|
|
||||||
|
execlp("ssh", "ssh", "-o", "Compression=no", "-o",
|
||||||
|
"ControlMaster=no", ssh_user, "fastsync-server", "--stdio",
|
||||||
|
(char *)NULL);
|
||||||
|
perror("exec of ssh failed");
|
||||||
|
(void)write(exec_pipe[1], "x", 1);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sv[1]);
|
||||||
|
close(exec_pipe[1]);
|
||||||
|
|
||||||
|
char exec_status;
|
||||||
|
ssize_t n = read(exec_pipe[0], &exec_status, 1);
|
||||||
|
close(exec_pipe[0]);
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
close(sv[0]);
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
fprintf(stderr, "Error: could not launch 'fastsync-server --stdio' on remote\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Client *client = malloc(sizeof(Client));
|
||||||
|
client->file_descriptor = sv[0];
|
||||||
|
client->address.sin_family = AF_UNIX;
|
||||||
|
client->address_length = 0;
|
||||||
|
client->ssh_child_pid = pid;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
Server *server_create(int port) {
|
Server *server_create(int port) {
|
||||||
Server *server = (Server *)malloc(sizeof(Server));
|
Server *server = (Server *)malloc(sizeof(Server));
|
||||||
if (server == NULL) {
|
if (server == NULL) {
|
||||||
@@ -82,6 +204,7 @@ Client *client_create() {
|
|||||||
client->file_descriptor = file_descriptor;
|
client->file_descriptor = file_descriptor;
|
||||||
client->address.sin_family = AF_INET;
|
client->address.sin_family = AF_INET;
|
||||||
client->address_length = sizeof(client->address);
|
client->address_length = sizeof(client->address);
|
||||||
|
client->ssh_child_pid = -1;
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +223,14 @@ void client_connect(Client *client, char *host, int port) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void client_disconnect(Client *client) { close(client->file_descriptor); }
|
void client_disconnect(Client *client) {
|
||||||
|
close(client->file_descriptor);
|
||||||
|
if (client->ssh_child_pid > 0) {
|
||||||
|
int status;
|
||||||
|
waitpid(client->ssh_child_pid, &status, 0);
|
||||||
|
client->ssh_child_pid = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void client_delete(Client *client) {
|
void client_delete(Client *client) {
|
||||||
if (client == NULL)
|
if (client == NULL)
|
||||||
@@ -110,12 +240,11 @@ void client_delete(Client *client) {
|
|||||||
|
|
||||||
void send_n_data(int file_descriptor, void *data, size_t data_size) {
|
void send_n_data(int file_descriptor, void *data, size_t data_size) {
|
||||||
log_message(LOG_LEVEL_DEBUG, " Sending n Data: %zu", data_size);
|
log_message(LOG_LEVEL_DEBUG, " Sending n Data: %zu", data_size);
|
||||||
|
int fd = io_fd(io_write_fd, file_descriptor);
|
||||||
ssize_t total_bytes_send = 0;
|
ssize_t total_bytes_send = 0;
|
||||||
while (total_bytes_send < data_size) {
|
while (total_bytes_send < data_size) {
|
||||||
printf("Trying: %zu\n", data_size - total_bytes_send);
|
ssize_t bytes_send = write(fd, (char *)data + total_bytes_send,
|
||||||
ssize_t bytes_send = send(file_descriptor, (char *)data + total_bytes_send,
|
data_size - total_bytes_send);
|
||||||
data_size - total_bytes_send, 0);
|
|
||||||
printf("Bytes send: %zd\n", bytes_send);
|
|
||||||
if (bytes_send <= 0) {
|
if (bytes_send <= 0) {
|
||||||
perror("Could not send data!");
|
perror("Could not send data!");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@@ -127,11 +256,12 @@ void send_n_data(int file_descriptor, void *data, size_t data_size) {
|
|||||||
|
|
||||||
void receive_n_data(int file_descriptor, void *data, size_t data_size) {
|
void receive_n_data(int file_descriptor, void *data, size_t data_size) {
|
||||||
log_message(LOG_LEVEL_DEBUG, " Receiving n Data: %zu", data_size);
|
log_message(LOG_LEVEL_DEBUG, " Receiving n Data: %zu", data_size);
|
||||||
|
int fd = io_fd(io_read_fd, file_descriptor);
|
||||||
size_t total_bytes_received = 0;
|
size_t total_bytes_received = 0;
|
||||||
while (total_bytes_received < data_size) {
|
while (total_bytes_received < data_size) {
|
||||||
long long bytes_received =
|
ssize_t bytes_received =
|
||||||
recv(file_descriptor, data + total_bytes_received,
|
read(fd, (char *)data + total_bytes_received,
|
||||||
data_size - total_bytes_received, 0);
|
data_size - total_bytes_received);
|
||||||
if (bytes_received == -1 || bytes_received == 0) {
|
if (bytes_received == -1 || bytes_received == 0) {
|
||||||
perror("Could not receive bytes!");
|
perror("Could not receive bytes!");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
|||||||
@@ -21,13 +21,16 @@ typedef struct Client {
|
|||||||
struct sockaddr_in address;
|
struct sockaddr_in address;
|
||||||
unsigned int address_length;
|
unsigned int address_length;
|
||||||
int file_descriptor;
|
int file_descriptor;
|
||||||
|
pid_t ssh_child_pid;
|
||||||
} Client;
|
} Client;
|
||||||
|
|
||||||
Client *client_create();
|
Client *client_create();
|
||||||
void client_disconnect(Client *client);
|
void client_disconnect(Client *client);
|
||||||
void client_delete(Client *client);
|
void client_delete(Client *client);
|
||||||
void client_connect(Client *client, char *host, int port);
|
void client_connect(Client *client, char *host, int port);
|
||||||
|
Client *client_connect_ssh(char *destination);
|
||||||
|
|
||||||
|
void io_set_fds(int read_fd, int write_fd);
|
||||||
void send_n_data(int file_descriptor, void *data, size_t data_size);
|
void send_n_data(int file_descriptor, void *data, size_t data_size);
|
||||||
void receive_n_data(int file_descriptor, void *data, size_t data_size);
|
void receive_n_data(int file_descriptor, void *data, size_t data_size);
|
||||||
void send_str(int file_descriptor, char *data);
|
void send_str(int file_descriptor, char *data);
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ BASE_CLIENT_FLAGS = ["--save-to-disk"]
|
|||||||
|
|
||||||
TEST_CASES = [
|
TEST_CASES = [
|
||||||
{"name": "Standard", "flags": []},
|
{"name": "Standard", "flags": []},
|
||||||
|
{"name": "Posix Args (no flags)", "flags": [], "posix": True},
|
||||||
{"name": "Standard (no metadata)", "flags": [], "use_metadata": False},
|
{"name": "Standard (no metadata)", "flags": [], "use_metadata": False},
|
||||||
{"name": "Multithreading (-m)", "flags": ["-m"]},
|
{"name": "Multithreading (-m)", "flags": ["-m"]},
|
||||||
{"name": "Compression (-c)", "flags": ["-c"]},
|
{"name": "Compression (-c)", "flags": ["-c"]},
|
||||||
@@ -66,6 +67,17 @@ TEST_CASES = [
|
|||||||
{"name": "Sendfile + Multithreading (-f -m)", "flags": ["-f", "-m"]},
|
{"name": "Sendfile + Multithreading (-f -m)", "flags": ["-f", "-m"]},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
SSH_CASES = [
|
||||||
|
{"name": "SSH (localhost)", "flags": []},
|
||||||
|
{"name": "SSH Multithreading (-m)", "flags": ["-m"]},
|
||||||
|
{"name": "SSH Compression (-c)", "flags": ["-c"]},
|
||||||
|
{"name": "SSH Chunk Serialization (-s)", "flags": ["-s"]},
|
||||||
|
{"name": "SSH Compression + Chunk Serialization (-c -s)", "flags": ["-c", "-s"]},
|
||||||
|
{"name": "SSH Multithreading + Compression (-m -c)", "flags": ["-m", "-c"]},
|
||||||
|
{"name": "SSH Multithreading + Chunk Serialization (-m -s)", "flags": ["-m", "-s"]},
|
||||||
|
{"name": "SSH Multithreading + Compression + Chunk Serialization (-m -c -s)", "flags": ["-m", "-c", "-s"]},
|
||||||
|
]
|
||||||
|
|
||||||
RSYNC_CASES = [
|
RSYNC_CASES = [
|
||||||
{"name": "rsync (archive)", "args": ["-aH"]},
|
{"name": "rsync (archive)", "args": ["-aH"]},
|
||||||
{"name": "rsync (archive + compress)", "args": ["-aHz"]},
|
{"name": "rsync (archive + compress)", "args": ["-aHz"]},
|
||||||
@@ -183,9 +195,12 @@ def start_rsync_daemon(source_dir):
|
|||||||
return port, conf, daemon
|
return port, conf, daemon
|
||||||
|
|
||||||
|
|
||||||
def run_single_test(cmd, name, source_dir, dest_dir, *, source_prefix=None):
|
def run_single_test(cmd, name, source_dir, dest_dir, *, source_prefix=None, no_server=False):
|
||||||
if os.path.exists(dest_dir):
|
if os.path.exists(dest_dir):
|
||||||
shutil.rmtree(dest_dir)
|
shutil.rmtree(dest_dir)
|
||||||
|
if no_server:
|
||||||
|
server = None
|
||||||
|
else:
|
||||||
server = subprocess.Popen(SERVER_CMD, stdout=subprocess.DEVNULL, stderr=None)
|
server = subprocess.Popen(SERVER_CMD, stdout=subprocess.DEVNULL, stderr=None)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
try:
|
try:
|
||||||
@@ -193,6 +208,7 @@ def run_single_test(cmd, name, source_dir, dest_dir, *, source_prefix=None):
|
|||||||
result = subprocess.run(cmd, text=True, capture_output=True)
|
result = subprocess.run(cmd, text=True, capture_output=True)
|
||||||
duration = time.monotonic() - start
|
duration = time.monotonic() - start
|
||||||
finally:
|
finally:
|
||||||
|
if server:
|
||||||
wait_proc(server)
|
wait_proc(server)
|
||||||
|
|
||||||
mismatches, missing = [], []
|
mismatches, missing = [], []
|
||||||
@@ -246,6 +262,9 @@ def run_profile(profile_name, source_dir, dest_dir):
|
|||||||
results = []
|
results = []
|
||||||
for case in TEST_CASES:
|
for case in TEST_CASES:
|
||||||
flags = BASE_CLIENT_FLAGS + (["-M"] if case.get("use_metadata", True) else []) + case["flags"]
|
flags = BASE_CLIENT_FLAGS + (["-M"] if case.get("use_metadata", True) else []) + case["flags"]
|
||||||
|
if case.get("posix"):
|
||||||
|
cmd = client_prefix + BASE_CLIENT_CMD + [source_dir, dest_dir] + flags
|
||||||
|
else:
|
||||||
cmd = client_prefix + BASE_CLIENT_CMD + ["--source-dir", source_dir, "--dest-dir", dest_dir] + flags
|
cmd = client_prefix + BASE_CLIENT_CMD + ["--source-dir", source_dir, "--dest-dir", dest_dir] + flags
|
||||||
print(f"\n --- {case['name']} ---\n Running: {' '.join(cmd)}")
|
print(f"\n --- {case['name']} ---\n Running: {' '.join(cmd)}")
|
||||||
try:
|
try:
|
||||||
@@ -255,6 +274,19 @@ def run_profile(profile_name, source_dir, dest_dir):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
results.append({"name": case["name"], "suite": profile_name, "status": "Error", "time": "N/A", "error": str(e)})
|
results.append({"name": case["name"], "suite": profile_name, "status": "Error", "time": "N/A", "error": str(e)})
|
||||||
|
|
||||||
|
if SSH_AVAILABLE:
|
||||||
|
for case in SSH_CASES:
|
||||||
|
flags = BASE_CLIENT_FLAGS + (["-M"] if case.get("use_metadata", True) else []) + case["flags"]
|
||||||
|
ssh_dest = f"localhost:{dest_dir}_ssh"
|
||||||
|
cmd = BASE_CLIENT_CMD + [source_dir, ssh_dest] + flags
|
||||||
|
print(f"\n --- {case['name']} ---\n Running: {' '.join(cmd)}")
|
||||||
|
try:
|
||||||
|
r = run_single_test(cmd, case["name"], source_dir, f"{dest_dir}_ssh", no_server=True)
|
||||||
|
r["suite"] = profile_name
|
||||||
|
results.append(r)
|
||||||
|
except Exception as e:
|
||||||
|
results.append({"name": case["name"], "suite": profile_name, "status": "Error", "time": "N/A", "error": str(e)})
|
||||||
|
|
||||||
port, conf, daemon = start_rsync_daemon(source_dir)
|
port, conf, daemon = start_rsync_daemon(source_dir)
|
||||||
try:
|
try:
|
||||||
for case in RSYNC_CASES:
|
for case in RSYNC_CASES:
|
||||||
@@ -344,7 +376,100 @@ def format_throughput(bps):
|
|||||||
return f"{bps:.0f} B/s"
|
return f"{bps:.0f} B/s"
|
||||||
|
|
||||||
|
|
||||||
|
SSH_AVAILABLE = False
|
||||||
|
|
||||||
|
def check_ssh_localhost():
|
||||||
|
global SSH_AVAILABLE
|
||||||
|
build_dir = os.path.abspath("build")
|
||||||
|
server_path = os.path.join(build_dir, "server")
|
||||||
|
|
||||||
|
r = subprocess.run(["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=5",
|
||||||
|
"localhost", "which", "fastsync-server"],
|
||||||
|
capture_output=True, timeout=10)
|
||||||
|
if r.returncode == 0:
|
||||||
|
SSH_AVAILABLE = True
|
||||||
|
return
|
||||||
|
|
||||||
|
SSH_AVAILABLE = False
|
||||||
|
# Try each PATH dir: create symlink, then verify with which
|
||||||
|
r = subprocess.run(
|
||||||
|
["ssh", "-o", "BatchMode=yes", "localhost",
|
||||||
|
'echo "$PATH"'],
|
||||||
|
capture_output=True, timeout=10, text=True)
|
||||||
|
if r.returncode != 0:
|
||||||
|
return
|
||||||
|
for d in r.stdout.strip().split(":"):
|
||||||
|
d = d.strip()
|
||||||
|
if not d:
|
||||||
|
continue
|
||||||
|
if "wrappers" in d:
|
||||||
|
continue
|
||||||
|
test = subprocess.run(
|
||||||
|
["ssh", "-o", "BatchMode=yes", "localhost",
|
||||||
|
f'test -w "{d}" && ln -sf {server_path} "{d}/fastsync-server" && which fastsync-server'],
|
||||||
|
capture_output=True, timeout=10)
|
||||||
|
if test.returncode == 0:
|
||||||
|
SSH_AVAILABLE = True
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def preflight_checks():
|
||||||
|
global SSH_AVAILABLE
|
||||||
|
errors = []
|
||||||
|
print("Pre-flight checks:")
|
||||||
|
print(" [1] --help flag...", end=" ")
|
||||||
|
r = subprocess.run(BASE_CLIENT_CMD + ["--help"], capture_output=True, text=True)
|
||||||
|
if r.returncode == 0 and "Usage:" in r.stdout and "SSH transport" in r.stdout:
|
||||||
|
print("OK")
|
||||||
|
else:
|
||||||
|
print("FAIL")
|
||||||
|
errors.append("--help failed")
|
||||||
|
|
||||||
|
print(" [2] Remote SSH dest detection...", end=" ")
|
||||||
|
r = subprocess.run(BASE_CLIENT_CMD + ["/x", "somehost:/y"], capture_output=True, text=True, timeout=5)
|
||||||
|
if r.returncode != 0 and ("ssh" in r.stderr or "Could not receive" in r.stderr or "could not launch" in r.stderr or "Error" in r.stderr):
|
||||||
|
print("OK (detected as SSH)")
|
||||||
|
else:
|
||||||
|
print("FAIL (not detected as SSH dest)")
|
||||||
|
errors.append("SSH detection failed")
|
||||||
|
|
||||||
|
print(" [3] Server --stdio flag...", end=" ")
|
||||||
|
try:
|
||||||
|
r = subprocess.run(["./build/server", "--stdio"], capture_output=True, text=True, timeout=3)
|
||||||
|
if r.returncode != 0 and ("receiving" in r.stderr or "receiving" in r.stdout or "Receiving" in r.stderr):
|
||||||
|
print("OK (started in stdio mode)")
|
||||||
|
else:
|
||||||
|
print("WARN (stdio exited: rc=%d)" % r.returncode)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
print("OK (waiting for stdin)")
|
||||||
|
|
||||||
|
print(" [4] Posix arg syntax (no server, expect failure)...", end=" ")
|
||||||
|
r = subprocess.run(BASE_CLIENT_CMD + ["/tmp/x", "/tmp/y"], capture_output=True, text=True, timeout=5)
|
||||||
|
if r.returncode != 0 and "connect" in r.stderr:
|
||||||
|
print("OK (TCP fallback)")
|
||||||
|
else:
|
||||||
|
print("FAIL")
|
||||||
|
errors.append("Posix arg syntax failed")
|
||||||
|
|
||||||
|
check_ssh_localhost()
|
||||||
|
print(" [5] SSH to localhost...", end=" ")
|
||||||
|
if SSH_AVAILABLE:
|
||||||
|
print("OK")
|
||||||
|
else:
|
||||||
|
print("SKIP (install fastsync-server in PATH on remote)")
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
print(f"\n {len(errors)} pre-flight check(s) failed: {', '.join(errors)}")
|
||||||
|
sys.exit(1)
|
||||||
|
print(" All pre-flight checks passed.\n")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
os.system("cmake -B build -S . > /dev/null 2>&1")
|
||||||
|
if os.system("cd build && make -j$(nproc) 2>&1 | tail -3") != 0:
|
||||||
|
print("Build failed")
|
||||||
|
sys.exit(1)
|
||||||
|
preflight_checks()
|
||||||
parser = argparse.ArgumentParser(description="FastSync integration test / benchmark")
|
parser = argparse.ArgumentParser(description="FastSync integration test / benchmark")
|
||||||
parser.add_argument("--source-dir", default=DEFAULT_SOURCE_DIR)
|
parser.add_argument("--source-dir", default=DEFAULT_SOURCE_DIR)
|
||||||
parser.add_argument("--dest-dir", default=DEFAULT_DEST_DIR)
|
parser.add_argument("--dest-dir", default=DEFAULT_DEST_DIR)
|
||||||
@@ -353,11 +478,6 @@ def main():
|
|||||||
parser.add_argument("--wan", action="store_true")
|
parser.add_argument("--wan", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
os.system("cmake -B build -S . > /dev/null 2>&1")
|
|
||||||
if os.system("cd build && make -j$(nproc) 2>&1 | tail -3") != 0:
|
|
||||||
print("Build failed")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
total_bytes = generate_test_files(args.source_dir)
|
total_bytes = generate_test_files(args.source_dir)
|
||||||
if os.path.exists(args.dest_dir):
|
if os.path.exists(args.dest_dir):
|
||||||
shutil.rmtree(args.dest_dir)
|
shutil.rmtree(args.dest_dir)
|
||||||
|
|||||||
@@ -18,6 +18,20 @@ static void test_config_lifecycle() {
|
|||||||
EXPECT_FALSE(cfg->use_chunk_serialization);
|
EXPECT_FALSE(cfg->use_chunk_serialization);
|
||||||
EXPECT_FALSE(cfg->use_compression);
|
EXPECT_FALSE(cfg->use_compression);
|
||||||
EXPECT_EQ_INT(cfg->num_connections, 4);
|
EXPECT_EQ_INT(cfg->num_connections, 4);
|
||||||
|
EXPECT_EQ_INT(cfg->transport, TRANSPORT_TCP);
|
||||||
|
EXPECT_NULL(cfg->ssh_destination);
|
||||||
|
config_delete(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_config_ssh_dest() {
|
||||||
|
Config *cfg = config_create(str_dup("1.0"), str_dup("/src"), str_dup("user@host:/dst"),
|
||||||
|
true, false, false, false, false, 1, 4, false);
|
||||||
|
cfg->transport = TRANSPORT_SSH;
|
||||||
|
cfg->ssh_destination = str_dup("user@host:/dst");
|
||||||
|
EXPECT_NOT_NULL(cfg);
|
||||||
|
EXPECT_EQ_INT(cfg->transport, TRANSPORT_SSH);
|
||||||
|
EXPECT_EQ_STR(cfg->ssh_destination, "user@host:/dst");
|
||||||
|
EXPECT_EQ_STR(cfg->receive_root_directory, "user@host:/dst");
|
||||||
config_delete(cfg);
|
config_delete(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +69,7 @@ static void test_pipeline_receiver_lifecycle() {
|
|||||||
|
|
||||||
void test_config() {
|
void test_config() {
|
||||||
test_config_lifecycle();
|
test_config_lifecycle();
|
||||||
|
test_config_ssh_dest();
|
||||||
test_pipeline_sender_lifecycle();
|
test_pipeline_sender_lifecycle();
|
||||||
test_pipeline_receiver_lifecycle();
|
test_pipeline_receiver_lifecycle();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user