refactor: quality cleanup — table-driven CLI, file.c split, scanner helpers, unified error reporting #216
+39
-42
@@ -537,6 +537,17 @@ static int load_files_multithreaded(void* pipeline_context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Print a one-line transfer progress report to stderr. `suffix` ends the
|
||||||
|
line (e.g. "Done.\n") or is "" for in-place refresh. Shared by the
|
||||||
|
single-threaded loop and the multithreaded progress thread. */
|
||||||
|
static void print_transfer_progress(unsigned long long total_bytes, time_t start,
|
||||||
|
const char* suffix) {
|
||||||
|
double elapsed = difftime(time(NULL), start);
|
||||||
|
double rate = elapsed > 0.0 ? total_bytes / (1048576.0 * elapsed) : 0.0;
|
||||||
|
fprintf(stderr, "\rSent %.1f MB (%.1f MB/s) %s", total_bytes / 1048576.0, rate, suffix);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
/* Progress-reporting thread for multithreaded send. Runs in parallel with
|
/* Progress-reporting thread for multithreaded send. Runs in parallel with
|
||||||
the scanner/loader/sender threads and prints periodic progress to stderr. */
|
the scanner/loader/sender threads and prints periodic progress to stderr. */
|
||||||
static int progress_thread_fn(void* arg) {
|
static int progress_thread_fn(void* arg) {
|
||||||
@@ -551,20 +562,14 @@ static int progress_thread_fn(void* arg) {
|
|||||||
mtx_unlock(&context->mutex_progress);
|
mtx_unlock(&context->mutex_progress);
|
||||||
|
|
||||||
if (done) {
|
if (done) {
|
||||||
time_t now = time(NULL);
|
print_transfer_progress(total, start, "Done.\n");
|
||||||
double elapsed = difftime(now, start);
|
|
||||||
double rate = elapsed > 0.0 ? total / (1048576.0 * elapsed) : 0.0;
|
|
||||||
fprintf(stderr, "\rSent %.1f MB (%.1f MB/s) Done.\n", total / 1048576.0, rate);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
if (now - last_progress >= 1) {
|
if (now - last_progress >= 1) {
|
||||||
last_progress = now;
|
last_progress = now;
|
||||||
double elapsed = difftime(now, start);
|
print_transfer_progress(total, start, "");
|
||||||
double rate = elapsed > 0.0 ? total / (1048576.0 * elapsed) : 0.0;
|
|
||||||
fprintf(stderr, "\rSent %.1f MB (%.1f MB/s) ", total / 1048576.0, rate);
|
|
||||||
fflush(stderr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timespec ts = {0, 100 * 1000000L}; /* 100 ms */
|
struct timespec ts = {0, 100 * 1000000L}; /* 100 ms */
|
||||||
@@ -587,29 +592,21 @@ int send_files(Config* config) {
|
|||||||
protocol_session_init(&session, client->file_descriptor, client->file_descriptor);
|
protocol_session_init(&session, client->file_descriptor, client->file_descriptor);
|
||||||
protocol_session_set_ssl(&session, (SSL*)client->ssl);
|
protocol_session_set_ssl(&session, (SSL*)client->ssl);
|
||||||
protocol_session_bind(&session);
|
protocol_session_bind(&session);
|
||||||
if (!config_send(client->file_descriptor, config)) {
|
int ret = 1;
|
||||||
disconnect_transfer_client(client);
|
DirectoryScanner* scanner = NULL;
|
||||||
protocol_session_unbind();
|
ArrayList* manifest = NULL;
|
||||||
return 1;
|
if (!config_send(client->file_descriptor, config))
|
||||||
}
|
goto send_fail;
|
||||||
ScannerOptions scanner_options = scanner_options_from_config(config, 0);
|
ScannerOptions scanner_options = scanner_options_from_config(config, 0);
|
||||||
DirectoryScanner* scanner =
|
scanner = directory_scanner_create_with_options(config->send_directory, &scanner_options);
|
||||||
directory_scanner_create_with_options(config->send_directory, &scanner_options);
|
manifest = create_transfer_manifest(config);
|
||||||
|
if (!scanner || (config->use_delete && !manifest))
|
||||||
|
goto send_fail;
|
||||||
Chunk* current_chunk;
|
Chunk* current_chunk;
|
||||||
unsigned long long total_bytes = 0;
|
unsigned long long total_bytes = 0;
|
||||||
int total_files = 0;
|
int total_files = 0;
|
||||||
time_t last_progress = 0;
|
time_t last_progress = 0;
|
||||||
time_t start = time(NULL);
|
time_t start = time(NULL);
|
||||||
ArrayList* manifest = create_transfer_manifest(config);
|
|
||||||
if (!scanner || (config->use_delete && !manifest)) {
|
|
||||||
if (scanner)
|
|
||||||
directory_scanner_destroy(scanner);
|
|
||||||
if (manifest)
|
|
||||||
array_list_delete(manifest);
|
|
||||||
disconnect_transfer_client(client);
|
|
||||||
protocol_session_unbind();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
while ((current_chunk = directory_scanner_next(scanner)) != NULL) {
|
while ((current_chunk = directory_scanner_next(scanner)) != NULL) {
|
||||||
unsigned long long chunk_bytes = 0;
|
unsigned long long chunk_bytes = 0;
|
||||||
for (int i = 0; i < current_chunk->element_count; i++) {
|
for (int i = 0; i < current_chunk->element_count; i++) {
|
||||||
@@ -621,16 +618,21 @@ int send_files(Config* config) {
|
|||||||
goto send_fail;
|
goto send_fail;
|
||||||
}
|
}
|
||||||
if (!config->use_sendfile) {
|
if (!config->use_sendfile) {
|
||||||
|
bool load_ok = true;
|
||||||
for (int i = 0; i < current_chunk->element_count; i++) {
|
for (int i = 0; i < current_chunk->element_count; i++) {
|
||||||
File* f = current_chunk->items[i];
|
File* f = current_chunk->items[i];
|
||||||
if (f->data->size > STREAM_THRESHOLD)
|
if (f->data->size > STREAM_THRESHOLD)
|
||||||
continue;
|
continue;
|
||||||
if (!file_load_data(f)) {
|
if (!file_load_data(f)) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to load file data");
|
log_message(LOG_LEVEL_ERROR, "Failed to load file data");
|
||||||
chunk_destroy(current_chunk);
|
load_ok = false;
|
||||||
goto send_fail;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!load_ok) {
|
||||||
|
chunk_destroy(current_chunk);
|
||||||
|
goto send_fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (send_chunk(client, current_chunk, config) != 0) {
|
if (send_chunk(client, current_chunk, config) != 0) {
|
||||||
log_message(LOG_LEVEL_ERROR, "Failed to send chunk");
|
log_message(LOG_LEVEL_ERROR, "Failed to send chunk");
|
||||||
@@ -645,10 +647,7 @@ int send_files(Config* config) {
|
|||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
if (now - last_progress >= 1) {
|
if (now - last_progress >= 1) {
|
||||||
last_progress = now;
|
last_progress = now;
|
||||||
double elapsed = difftime(now, start);
|
print_transfer_progress(total_bytes, start, "");
|
||||||
double rate = elapsed > 0 ? total_bytes / (1048576.0 * elapsed) : 0;
|
|
||||||
fprintf(stderr, "\rSent %.1f MB (%.1f MB/s) ", total_bytes / 1048576.0, rate);
|
|
||||||
fflush(stderr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chunk_destroy(current_chunk);
|
chunk_destroy(current_chunk);
|
||||||
@@ -665,28 +664,26 @@ int send_files(Config* config) {
|
|||||||
manifest = NULL;
|
manifest = NULL;
|
||||||
}
|
}
|
||||||
bool ok = finalize_transfer(client);
|
bool ok = finalize_transfer(client);
|
||||||
double elapsed_total = difftime(time(NULL), start);
|
if (config->show_progress)
|
||||||
if (config->show_progress) {
|
print_transfer_progress(total_bytes, start, "Done.\n");
|
||||||
double rate = elapsed_total > 0 ? total_bytes / (1048576.0 * elapsed_total) : 0;
|
|
||||||
fprintf(stderr, "\rSent %.1f MB (%.1f MB/s) Done.\n", total_bytes / 1048576.0, rate);
|
|
||||||
}
|
|
||||||
if (config->stats) {
|
if (config->stats) {
|
||||||
|
double elapsed_total = difftime(time(NULL), start);
|
||||||
double rate = elapsed_total > 0 ? total_bytes / (1048576.0 * elapsed_total) : 0;
|
double rate = elapsed_total > 0 ? total_bytes / (1048576.0 * elapsed_total) : 0;
|
||||||
fprintf(stderr, "Stats: %d files, %.1f MB, %.1f MB/s\n", total_files, total_bytes / 1048576.0,
|
fprintf(stderr, "Stats: %d files, %.1f MB, %.1f MB/s\n", total_files, total_bytes / 1048576.0,
|
||||||
rate);
|
rate);
|
||||||
}
|
}
|
||||||
directory_scanner_destroy(scanner);
|
ret = ok ? 0 : 1;
|
||||||
disconnect_transfer_client(client);
|
|
||||||
protocol_session_unbind();
|
|
||||||
return ok ? 0 : 1;
|
|
||||||
|
|
||||||
send_fail:
|
send_fail:
|
||||||
|
/* Single cleanup path for all exits. The manifest is intentionally deleted
|
||||||
|
here even on success without --delete, fixing a pre-existing leak. */
|
||||||
if (manifest)
|
if (manifest)
|
||||||
array_list_delete(manifest);
|
array_list_delete(manifest);
|
||||||
directory_scanner_destroy(scanner);
|
if (scanner)
|
||||||
|
directory_scanner_destroy(scanner);
|
||||||
disconnect_transfer_client(client);
|
disconnect_transfer_client(client);
|
||||||
protocol_session_unbind();
|
protocol_session_unbind();
|
||||||
return 1;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int send_files_multithreaded(Config* config) {
|
int send_files_multithreaded(Config* config) {
|
||||||
|
|||||||
Reference in New Issue
Block a user