From 5c7d82b79cca2ee51c732994b40ca62ab340b50e Mon Sep 17 00:00:00 2001 From: TapTap Date: Thu, 3 Sep 2026 16:18:46 +0200 Subject: [PATCH 1/2] feat: add rsync debug flags --- src/client/client_cli.c | 105 +++++++++++++++++++++++++++++++++++++++ src/client/usage.c | 8 +++ src/client/usage.h | 1 + src/server/server.c | 1 + src/shared/chunk.c | 4 +- src/shared/compression.c | 10 ++-- src/shared/log.c | 30 +++++++++++ src/shared/log.h | 34 +++++++++++++ src/shared/protocol.c | 24 ++++----- tests/test_client_cli.c | 30 +++++++++++ 10 files changed, 228 insertions(+), 19 deletions(-) diff --git a/src/client/client_cli.c b/src/client/client_cli.c index 191cc07..8d822a9 100644 --- a/src/client/client_cli.c +++ b/src/client/client_cli.c @@ -88,6 +88,99 @@ static int set_nonneg_int_option(int* dest, const char* value, const char* optio static int read_patterns_from_file(const char* filepath, char*** patterns, int* count); +static int parse_debug_flags(const char* value, Config* config) { + if (!value || value[0] == '\0' || value[0] == ',' || value[strlen(value) - 1] == ',' || + strstr(value, ",,")) { + log_message(LOG_LEVEL_ERROR, "--debug requires at least one flag"); + return -1; + } + + char* flags = str_dup(value); + if (!flags) { + log_message(LOG_LEVEL_ERROR, "memory allocation failed for --debug"); + return -1; + } + uint32_t parsed = (uint32_t)config->debug_level; + char* saveptr = NULL; + for (char* token = strtok_r(flags, ",", &saveptr); token != NULL; + token = strtok_r(NULL, ",", &saveptr)) { + uint32_t flag = 0; + if (strcmp(token, "help") == 0) { + print_debug_usage(); + free(flags); + return 1; + } else if (strcmp(token, "all") == 0) { + parsed = LOG_DEBUG_ALL; + continue; + } else if (strcmp(token, "none") == 0) { + parsed = 0; + continue; + } else if (strcmp(token, "io") == 0) { + flag = LOG_DEBUG_IO; + } else if (strcmp(token, "proto") == 0) { + flag = LOG_DEBUG_PROTO; + } else if (strcmp(token, "pack") == 0) { + flag = LOG_DEBUG_PACK; + } else if (strcmp(token, "util") == 0) { + flag = LOG_DEBUG_UTIL; + } else if (strcmp(token, "acl") == 0) { + flag = LOG_DEBUG_ACL; + } else if (strcmp(token, "backup") == 0) { + flag = LOG_DEBUG_BACKUP; + } else if (strcmp(token, "bind") == 0) { + flag = LOG_DEBUG_BIND; + } else if (strcmp(token, "chksum") == 0) { + flag = LOG_DEBUG_CHKSUM; + } else if (strcmp(token, "connect") == 0) { + flag = LOG_DEBUG_CONNECT; + } else if (strcmp(token, "cmd") == 0) { + flag = LOG_DEBUG_CMD; + } else if (strcmp(token, "del") == 0) { + flag = LOG_DEBUG_DEL; + } else if (strcmp(token, "digest") == 0) { + flag = LOG_DEBUG_DIGEST; + } else if (strcmp(token, "dflt") == 0) { + flag = LOG_DEBUG_DFLT; + } else if (strcmp(token, "flist") == 0) { + flag = LOG_DEBUG_FLIST; + } else if (strcmp(token, "fuzzer") == 0) { + flag = LOG_DEBUG_FUZZER; + } else if (strcmp(token, "genr") == 0) { + flag = LOG_DEBUG_GENR; + } else if (strcmp(token, "hash") == 0) { + flag = LOG_DEBUG_HASH; + } else if (strcmp(token, "hlink") == 0) { + flag = LOG_DEBUG_HLINK; + } else if (strcmp(token, "iconv") == 0) { + flag = LOG_DEBUG_ICONV; + } else if (strcmp(token, "nstr") == 0) { + flag = LOG_DEBUG_NSTR; + } else if (strcmp(token, "own") == 0) { + flag = LOG_DEBUG_OWN; + } else if (strcmp(token, "proc") == 0) { + flag = LOG_DEBUG_PROC; + } else if (strcmp(token, "recv") == 0) { + flag = LOG_DEBUG_RECV; + } else if (strcmp(token, "send") == 0) { + flag = LOG_DEBUG_SEND; + } else if (strcmp(token, "time") == 0) { + flag = LOG_DEBUG_TIME; + } else if (strcmp(token, "tls") == 0) { + flag = LOG_DEBUG_TLS; + } else { + log_message(LOG_LEVEL_ERROR, "unknown --debug flag: %s", token); + free(flags); + return -1; + } + parsed |= flag; + } + free(flags); + config->debug_level = (int)parsed; + set_log_debug_flags(parsed); + set_log_level(LOG_LEVEL_DEBUG); + return 0; +} + /* Parse a string as an unsigned long long. Returns 0 on success, -1 on error. */ static int parse_ull_arg(const char* val, unsigned long long* out, const char* optname) { char* end; @@ -350,7 +443,19 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args, 0) return -1; } else if (opt_is(argv[i], "-v", "--verbose")) { + config->debug_level = LOG_DEBUG_ALL; set_log_level(LOG_LEVEL_DEBUG); + set_log_debug_flags(LOG_DEBUG_ALL); + } else if (strncmp(argv[i], "--debug=", 8) == 0) { + int debug_ret = parse_debug_flags(argv[i] + 8, config); + if (debug_ret != 0) + return debug_ret; + } else if (opt_is(argv[i], "--debug", NULL)) { + if (i + 1 >= argc) + return parse_debug_flags(NULL, config); + int debug_ret = parse_debug_flags(argv[++i], config); + if (debug_ret != 0) + return debug_ret; } else if (opt_is(argv[i], "-T", NULL) && i + 1 < argc) { if (set_positive_int_option(&config->timeout, argv[++i], "-T") != 0) return -1; diff --git a/src/client/usage.c b/src/client/usage.c index 2f800af..d239165 100644 --- a/src/client/usage.c +++ b/src/client/usage.c @@ -37,6 +37,7 @@ void print_usage(void) { 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(" --debug=FLAGS Fine-grained debug logging (use --debug=help for flags)\n"); printf(" -M, --preserve Preserve file metadata\n"); printf(" --chunk-size Chunk size in bytes (default: %d)\n", DEFAULT_CHUNK_SIZE); printf(" --source-dir Source directory\n"); @@ -72,3 +73,10 @@ void print_usage(void) { printf(" --help Show this help\n"); printf(" -V, --version Show version\n"); } + +void print_debug_usage(void) { + printf("Debug flags: ACL,BACKUP,BIND,CHKSUM,CONNECT,CMD,DEL,DIGEST,DFLT,FLIST,\n"); + printf(" FUZZER,GENR,HASH,HLINK,ICONV,IO,NSTR,OWN,PACK,PROC,PROTO,\n"); + printf(" RECV,SEND,TIME,TLS,UTIL,ALL,NONE\n"); + printf("Flags may be comma-separated, for example: --debug=io,proto\n"); +} diff --git a/src/client/usage.h b/src/client/usage.h index ef480c5..ca8d65b 100644 --- a/src/client/usage.h +++ b/src/client/usage.h @@ -2,5 +2,6 @@ #define USAGE_H void print_usage(void); +void print_debug_usage(void); #endif diff --git a/src/server/server.c b/src/server/server.c index af5cd43..2fefb03 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -380,6 +380,7 @@ int main(int argc, char* argv[]) { stdio_mode = true; } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) { set_log_level(LOG_LEVEL_DEBUG); + set_log_debug_flags(LOG_DEBUG_ALL); } else if (strcmp(argv[i], "--tls") == 0) { use_tls = true; } else if (strcmp(argv[i], "--cert") == 0 && i + 1 < argc) { diff --git a/src/shared/chunk.c b/src/shared/chunk.c index 3af1c63..fb6fd72 100644 --- a/src/shared/chunk.c +++ b/src/shared/chunk.c @@ -290,7 +290,7 @@ Chunk* chunk_deserialize(Data* data, bool use_metadata) { } Data* chunk_compress(Chunk* chunk, int compression_level, bool use_metadata) { - log_message(LOG_LEVEL_DEBUG, "Starting to compress chunk"); + log_debug_message(LOG_DEBUG_PACK, "Starting to compress chunk"); Data* serialized = chunk_serialize(chunk, use_metadata); if (serialized == NULL) return NULL; @@ -298,7 +298,7 @@ Data* chunk_compress(Chunk* chunk, int compression_level, bool use_metadata) { data_destroy(serialized); if (compressed == NULL) return NULL; - log_message(LOG_LEVEL_DEBUG, "Chunk successfully compressed"); + log_debug_message(LOG_DEBUG_PACK, "Chunk successfully compressed"); return compressed; } diff --git a/src/shared/compression.c b/src/shared/compression.c index 1a42ba5..30033e3 100644 --- a/src/shared/compression.c +++ b/src/shared/compression.c @@ -28,7 +28,7 @@ bool compression_should_skip(const char* path) { } Data* data_compress(Data* data_to_compress, int compression_level) { - log_message(LOG_LEVEL_DEBUG, "Starting to compress data"); + log_debug_message(LOG_DEBUG_UTIL, "Starting to compress data"); size_t dst_size = ZSTD_compressBound(data_to_compress->size); Data* compressed_data = data_create_empty(dst_size); if (compressed_data == NULL) @@ -66,8 +66,8 @@ Data* data_compress(Data* data_to_compress, int compression_level) { compressed_data->size = output.pos; ZSTD_freeCCtx(cctx); - log_message(LOG_LEVEL_DEBUG, "Data succesfully compressed from %zu to %zu", - data_to_compress->size, compressed_data->size); + log_debug_message(LOG_DEBUG_UTIL, "Data succesfully compressed from %zu to %zu", + data_to_compress->size, compressed_data->size); return compressed_data; } @@ -75,7 +75,7 @@ Data* data_decompress_limited(Data* compressed_data, size_t maximum_size) { if (!compressed_data || (!compressed_data->data && compressed_data->size != 0) || maximum_size == 0) return NULL; - log_message(LOG_LEVEL_DEBUG, "Start to decompress data"); + log_debug_message(LOG_DEBUG_UTIL, "Start to decompress data"); unsigned long long dst_size = ZSTD_getFrameContentSize(compressed_data->data, compressed_data->size); if (ZSTD_isError(dst_size)) { @@ -155,7 +155,7 @@ Data* data_decompress_limited(Data* compressed_data, size_t maximum_size) { uncompressed_data->size = output.pos; ZSTD_freeDCtx(dctx); - log_message(LOG_LEVEL_DEBUG, "Decompressed data successfully"); + log_debug_message(LOG_DEBUG_UTIL, "Decompressed data successfully"); return uncompressed_data; } diff --git a/src/shared/log.c b/src/shared/log.c index 29bbe0d..31eb5f7 100644 --- a/src/shared/log.c +++ b/src/shared/log.c @@ -7,12 +7,21 @@ static const char* log_level_strings[] = {"DEBUG", "INFO", "WARN", "ERROR"}; static LogLevel current_log_level = LOG_LEVEL_WARNING; +static uint32_t current_debug_flags = 0; static FILE* log_fp = NULL; void set_log_level(LogLevel level) { current_log_level = level; } +void set_log_debug_flags(uint32_t flags) { + current_debug_flags = flags; +} + +uint32_t get_log_debug_flags(void) { + return current_debug_flags; +} + void log_set_file(FILE* fp) { log_fp = fp; } @@ -53,6 +62,27 @@ void log_message(LogLevel log_level, const char* format, ...) { } } +void log_debug_message(LogDebugFlag flag, const char* format, ...) { + if (current_log_level > LOG_LEVEL_DEBUG || !(current_debug_flags & flag)) + return; + + time_t now = time(NULL); + struct tm t; + if (!localtime_r(&now, &t)) + return; + + va_list args; + va_start(args, format); + write_message(stdout, LOG_LEVEL_DEBUG, t, format, args); + va_end(args); + + if (log_fp) { + va_start(args, format); + write_message(log_fp, LOG_LEVEL_DEBUG, t, format, args); + va_end(args); + } +} + void log_perror(const char* context) { log_message(LOG_LEVEL_ERROR, "%s: %s", context, strerror(errno)); } diff --git a/src/shared/log.h b/src/shared/log.h index acea629..e66dfc6 100644 --- a/src/shared/log.h +++ b/src/shared/log.h @@ -2,12 +2,46 @@ #define LOG_H #include +#include typedef enum { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_ERROR } LogLevel; +typedef enum { + LOG_DEBUG_IO = 1u << 0, + LOG_DEBUG_PROTO = 1u << 1, + LOG_DEBUG_PACK = 1u << 2, + LOG_DEBUG_UTIL = 1u << 3, + LOG_DEBUG_ACL = 1u << 4, + LOG_DEBUG_BACKUP = 1u << 5, + LOG_DEBUG_BIND = 1u << 6, + LOG_DEBUG_CHKSUM = 1u << 7, + LOG_DEBUG_CONNECT = 1u << 8, + LOG_DEBUG_CMD = 1u << 9, + LOG_DEBUG_DEL = 1u << 10, + LOG_DEBUG_DIGEST = 1u << 11, + LOG_DEBUG_DFLT = 1u << 12, + LOG_DEBUG_FLIST = 1u << 13, + LOG_DEBUG_FUZZER = 1u << 14, + LOG_DEBUG_GENR = 1u << 15, + LOG_DEBUG_HASH = 1u << 16, + LOG_DEBUG_HLINK = 1u << 17, + LOG_DEBUG_ICONV = 1u << 18, + LOG_DEBUG_NSTR = 1u << 19, + LOG_DEBUG_OWN = 1u << 20, + LOG_DEBUG_PROC = 1u << 21, + LOG_DEBUG_RECV = 1u << 22, + LOG_DEBUG_SEND = 1u << 23, + LOG_DEBUG_TIME = 1u << 24, + LOG_DEBUG_TLS = 1u << 25, + LOG_DEBUG_ALL = (1u << 26) - 1, +} LogDebugFlag; + void log_message(LogLevel log_level, const char* message, ...); void log_perror(const char* context); void set_log_level(LogLevel level); +void set_log_debug_flags(uint32_t flags); +uint32_t get_log_debug_flags(void); +void log_debug_message(LogDebugFlag flag, const char* message, ...); void log_set_file(FILE* fp); #endif diff --git a/src/shared/protocol.c b/src/shared/protocol.c index d3e4aa3..638deb4 100644 --- a/src/shared/protocol.c +++ b/src/shared/protocol.c @@ -184,7 +184,7 @@ static int deadline_remaining_ms(const struct timespec* deadline) { bool protocol_send_n_data(ProtocolSession* session, const void* data, size_t data_size) { if (!data && data_size != 0) return false; - log_message(LOG_LEVEL_DEBUG, " Sending n Data: %zu", data_size); + log_debug_message(LOG_DEBUG_IO, " Sending n Data: %zu", data_size); if (!session) return false; int fd = session->write_fd; @@ -228,12 +228,12 @@ bool protocol_send_n_data(ProtocolSession* session, const void* data, size_t dat if (session->ssl) wait_events = POLLOUT; } - log_message(LOG_LEVEL_DEBUG, " Send n Data: %zu", total_bytes_send); + log_debug_message(LOG_DEBUG_IO, " Send n Data: %zu", total_bytes_send); return true; } bool protocol_receive_n_data(ProtocolSession* session, void* data, size_t data_size) { - log_message(LOG_LEVEL_DEBUG, " Receiving n Data: %zu", data_size); + log_debug_message(LOG_DEBUG_IO, " Receiving n Data: %zu", data_size); if (!session) return false; int fd = session->read_fd; @@ -287,7 +287,7 @@ bool protocol_receive_n_data(ProtocolSession* session, void* data, size_t data_s if (session->ssl) wait_events = POLLIN; } - log_message(LOG_LEVEL_DEBUG, " Received n Data: %zu", total_bytes_received); + log_debug_message(LOG_DEBUG_IO, " Received n Data: %zu", total_bytes_received); return true; } @@ -328,7 +328,7 @@ bool protocol_send_str(ProtocolSession* session, const char* data) { return false; if (!protocol_send_n_data(session, data, size)) return false; - log_message(LOG_LEVEL_DEBUG, "Send String: %s", data); + log_debug_message(LOG_DEBUG_PROTO, "Send String: %s", data); return true; } @@ -356,7 +356,7 @@ char* protocol_receive_str(ProtocolSession* session) { } data[size] = '\0'; session->total_allocated_bytes += size + 1; - log_message(LOG_LEVEL_DEBUG, "Received String: %s", data); + log_debug_message(LOG_DEBUG_PROTO, "Received String: %s", data); return data; } @@ -370,7 +370,7 @@ bool protocol_send_data(ProtocolSession* session, const Data* data) { return false; if (!protocol_send_n_data(session, data->data, data_size)) return false; - log_message(LOG_LEVEL_DEBUG, "Send %lld data", data_size); + log_debug_message(LOG_DEBUG_PROTO, "Send %lld data", data_size); return true; } @@ -400,7 +400,7 @@ Data* protocol_receive_data_limited(ProtocolSession* session, unsigned long long return NULL; } session->total_allocated_bytes += allocation_size; - log_message(LOG_LEVEL_DEBUG, "Received %lld data", size); + log_debug_message(LOG_DEBUG_PROTO, "Received %lld data", size); Data* result = data_create(data, (size_t)size); if (!result) { session->total_allocated_bytes -= allocation_size; @@ -417,28 +417,28 @@ Data* protocol_receive_data(ProtocolSession* session) { bool protocol_send_int(ProtocolSession* session, int data) { if (!protocol_send_n_data(session, &data, sizeof(int))) return false; - log_message(LOG_LEVEL_DEBUG, "Send Int: %d", data); + log_debug_message(LOG_DEBUG_PROTO, "Send Int: %d", data); return true; } bool protocol_receive_int(ProtocolSession* session, int* data) { if (!protocol_receive_n_data(session, data, sizeof(int))) return false; - log_message(LOG_LEVEL_DEBUG, "Received Int: %d", *data); + log_debug_message(LOG_DEBUG_PROTO, "Received Int: %d", *data); return true; } bool protocol_send_status(ProtocolSession* session, Status status) { if (!protocol_send_n_data(session, &status, sizeof(Status))) return false; - log_message(LOG_LEVEL_DEBUG, "Send Status: %s", status_to_string(status)); + log_debug_message(LOG_DEBUG_PROTO, "Send Status: %s", status_to_string(status)); return true; } bool protocol_receive_status(ProtocolSession* session, Status* status) { if (!protocol_receive_n_data(session, status, sizeof(Status))) return false; - log_message(LOG_LEVEL_DEBUG, "Received Status: %s", status_to_string(*status)); + log_debug_message(LOG_DEBUG_PROTO, "Received Status: %s", status_to_string(*status)); return true; } diff --git a/tests/test_client_cli.c b/tests/test_client_cli.c index 656fd40..ffea726 100644 --- a/tests/test_client_cli.c +++ b/tests/test_client_cli.c @@ -1,6 +1,7 @@ #include "test_client_cli.h" #include "client_validation.h" #include "config.h" +#include "log.h" #include "test_utils.h" #include "utils.h" #include @@ -247,6 +248,33 @@ static void test_parse_args_valid_compression_level() { config_delete(cfg); } +static void test_parse_args_debug_flags() { + Config* cfg = config_create(); + char* argv[] = {"fastsync", "--debug=io,proto", "/src", "/dst"}; + int positional_args[2]; + int positional_count = 0; + + EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), 0); + EXPECT_EQ_INT(cfg->debug_level, LOG_DEBUG_IO | LOG_DEBUG_PROTO); + EXPECT_EQ_INT(get_log_debug_flags(), LOG_DEBUG_IO | LOG_DEBUG_PROTO); + config_delete(cfg); +} + +static void test_parse_args_debug_flags_validation() { + static const char* const values[] = {"", "io,", ",io", "io,,proto", "unknown"}; + for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) { + Config* cfg = config_create(); + char option[64]; + snprintf(option, sizeof(option), "--debug=%s", values[i]); + char* argv[] = {"fastsync", option, "/src", "/dst"}; + int positional_args[2]; + int positional_count = 0; + + EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), -1); + config_delete(cfg); + } +} + /* Test parse_args unknown option returns error */ static void test_parse_args_unknown_option() { Config* cfg = config_create(); @@ -357,6 +385,8 @@ void test_client_cli() { test_parse_args_invalid_server_port(); test_parse_args_invalid_compression_level(); test_parse_args_valid_compression_level(); + test_parse_args_debug_flags(); + test_parse_args_debug_flags_validation(); test_parse_args_unknown_option(); test_parse_args_rejects_unimplemented_options(); test_parse_args_archive(); From caabd2e314e929aa3f41173156e64ab771b2d97f Mon Sep 17 00:00:00 2001 From: TapTap Date: Thu, 3 Sep 2026 21:28:21 +0200 Subject: [PATCH 2/2] fix: reject unsupported rsync debug categories --- RSYNC_COMPAT.md | 2 +- src/client/client_cli.c | 46 +---------------------------------------- src/client/usage.c | 5 ++--- src/shared/log.h | 24 +-------------------- tests/test_client_cli.c | 20 +++++++++++++----- 5 files changed, 20 insertions(+), 77 deletions(-) diff --git a/RSYNC_COMPAT.md b/RSYNC_COMPAT.md index 4b236ec..b2ede27 100644 --- a/RSYNC_COMPAT.md +++ b/RSYNC_COMPAT.md @@ -24,7 +24,7 @@ This document maps rsync's full feature set to FastSync's current implementation | `--help` | Show help | ✅ Implemented | Prints usage and exits; `-h` is not accepted | | `-V`, `--version` | Print version | ✅ Implemented | | | `--info=FLAGS` | Fine-grained info verbosity | ❌ Not Implemented | Removed because it had no effect | -| `--debug=FLAGS` | Fine-grained debug verbosity | ❌ Not Implemented | Removed because it had no effect | +| `--debug=FLAGS` | Fine-grained debug verbosity | ⚠️ Partial | `io`, `proto`, `pack`, and `util` are supported; other rsync categories are rejected | | `--stderr=MODE` | Change stderr output mode | ❌ Not Implemented | | | `--no-motd` | Suppress daemon MOTD | ❌ Not Implemented | | | `--exclude=PATTERN` | Exclude files matching pattern | ✅ Implemented | Glob matching in scanner | diff --git a/src/client/client_cli.c b/src/client/client_cli.c index 8d822a9..ff9dd26 100644 --- a/src/client/client_cli.c +++ b/src/client/client_cli.c @@ -123,52 +123,8 @@ static int parse_debug_flags(const char* value, Config* config) { flag = LOG_DEBUG_PACK; } else if (strcmp(token, "util") == 0) { flag = LOG_DEBUG_UTIL; - } else if (strcmp(token, "acl") == 0) { - flag = LOG_DEBUG_ACL; - } else if (strcmp(token, "backup") == 0) { - flag = LOG_DEBUG_BACKUP; - } else if (strcmp(token, "bind") == 0) { - flag = LOG_DEBUG_BIND; - } else if (strcmp(token, "chksum") == 0) { - flag = LOG_DEBUG_CHKSUM; - } else if (strcmp(token, "connect") == 0) { - flag = LOG_DEBUG_CONNECT; - } else if (strcmp(token, "cmd") == 0) { - flag = LOG_DEBUG_CMD; - } else if (strcmp(token, "del") == 0) { - flag = LOG_DEBUG_DEL; - } else if (strcmp(token, "digest") == 0) { - flag = LOG_DEBUG_DIGEST; - } else if (strcmp(token, "dflt") == 0) { - flag = LOG_DEBUG_DFLT; - } else if (strcmp(token, "flist") == 0) { - flag = LOG_DEBUG_FLIST; - } else if (strcmp(token, "fuzzer") == 0) { - flag = LOG_DEBUG_FUZZER; - } else if (strcmp(token, "genr") == 0) { - flag = LOG_DEBUG_GENR; - } else if (strcmp(token, "hash") == 0) { - flag = LOG_DEBUG_HASH; - } else if (strcmp(token, "hlink") == 0) { - flag = LOG_DEBUG_HLINK; - } else if (strcmp(token, "iconv") == 0) { - flag = LOG_DEBUG_ICONV; - } else if (strcmp(token, "nstr") == 0) { - flag = LOG_DEBUG_NSTR; - } else if (strcmp(token, "own") == 0) { - flag = LOG_DEBUG_OWN; - } else if (strcmp(token, "proc") == 0) { - flag = LOG_DEBUG_PROC; - } else if (strcmp(token, "recv") == 0) { - flag = LOG_DEBUG_RECV; - } else if (strcmp(token, "send") == 0) { - flag = LOG_DEBUG_SEND; - } else if (strcmp(token, "time") == 0) { - flag = LOG_DEBUG_TIME; - } else if (strcmp(token, "tls") == 0) { - flag = LOG_DEBUG_TLS; } else { - log_message(LOG_LEVEL_ERROR, "unknown --debug flag: %s", token); + log_message(LOG_LEVEL_ERROR, "unsupported --debug flag: %s", token); free(flags); return -1; } diff --git a/src/client/usage.c b/src/client/usage.c index d239165..5572de9 100644 --- a/src/client/usage.c +++ b/src/client/usage.c @@ -75,8 +75,7 @@ void print_usage(void) { } void print_debug_usage(void) { - printf("Debug flags: ACL,BACKUP,BIND,CHKSUM,CONNECT,CMD,DEL,DIGEST,DFLT,FLIST,\n"); - printf(" FUZZER,GENR,HASH,HLINK,ICONV,IO,NSTR,OWN,PACK,PROC,PROTO,\n"); - printf(" RECV,SEND,TIME,TLS,UTIL,ALL,NONE\n"); + printf("Supported debug flags: IO,PROTO,PACK,UTIL,ALL,NONE\n"); printf("Flags may be comma-separated, for example: --debug=io,proto\n"); + printf("Other rsync debug flags are unsupported and rejected.\n"); } diff --git a/src/shared/log.h b/src/shared/log.h index e66dfc6..da28b18 100644 --- a/src/shared/log.h +++ b/src/shared/log.h @@ -11,29 +11,7 @@ typedef enum { LOG_DEBUG_PROTO = 1u << 1, LOG_DEBUG_PACK = 1u << 2, LOG_DEBUG_UTIL = 1u << 3, - LOG_DEBUG_ACL = 1u << 4, - LOG_DEBUG_BACKUP = 1u << 5, - LOG_DEBUG_BIND = 1u << 6, - LOG_DEBUG_CHKSUM = 1u << 7, - LOG_DEBUG_CONNECT = 1u << 8, - LOG_DEBUG_CMD = 1u << 9, - LOG_DEBUG_DEL = 1u << 10, - LOG_DEBUG_DIGEST = 1u << 11, - LOG_DEBUG_DFLT = 1u << 12, - LOG_DEBUG_FLIST = 1u << 13, - LOG_DEBUG_FUZZER = 1u << 14, - LOG_DEBUG_GENR = 1u << 15, - LOG_DEBUG_HASH = 1u << 16, - LOG_DEBUG_HLINK = 1u << 17, - LOG_DEBUG_ICONV = 1u << 18, - LOG_DEBUG_NSTR = 1u << 19, - LOG_DEBUG_OWN = 1u << 20, - LOG_DEBUG_PROC = 1u << 21, - LOG_DEBUG_RECV = 1u << 22, - LOG_DEBUG_SEND = 1u << 23, - LOG_DEBUG_TIME = 1u << 24, - LOG_DEBUG_TLS = 1u << 25, - LOG_DEBUG_ALL = (1u << 26) - 1, + LOG_DEBUG_ALL = (1u << 4) - 1, } LogDebugFlag; void log_message(LogLevel log_level, const char* message, ...); diff --git a/tests/test_client_cli.c b/tests/test_client_cli.c index ffea726..5fa79ba 100644 --- a/tests/test_client_cli.c +++ b/tests/test_client_cli.c @@ -250,18 +250,28 @@ static void test_parse_args_valid_compression_level() { static void test_parse_args_debug_flags() { Config* cfg = config_create(); - char* argv[] = {"fastsync", "--debug=io,proto", "/src", "/dst"}; + char* argv[] = {"fastsync", "--debug=io,proto,pack,util", "/src", "/dst"}; int positional_args[2]; int positional_count = 0; EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), 0); - EXPECT_EQ_INT(cfg->debug_level, LOG_DEBUG_IO | LOG_DEBUG_PROTO); - EXPECT_EQ_INT(get_log_debug_flags(), LOG_DEBUG_IO | LOG_DEBUG_PROTO); + EXPECT_EQ_INT(cfg->debug_level, LOG_DEBUG_ALL); + EXPECT_EQ_INT(get_log_debug_flags(), LOG_DEBUG_ALL); + config_delete(cfg); +} + +static void test_parse_args_debug_help() { + Config* cfg = config_create(); + char* argv[] = {"fastsync", "--debug=help"}; + int positional_args[2]; + int positional_count = 0; + + EXPECT_EQ_INT(parse_args(cfg, 2, argv, positional_args, &positional_count), 1); config_delete(cfg); } static void test_parse_args_debug_flags_validation() { - static const char* const values[] = {"", "io,", ",io", "io,,proto", "unknown"}; + static const char* const values[] = {"", "io,", ",io", "io,,proto", "acl", "tls", "unknown"}; for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) { Config* cfg = config_create(); char option[64]; @@ -306,7 +316,6 @@ static void test_parse_args_rejects_unimplemented_options() { "--itemize-changes", "--out-format", "--info", - "--debug", "--list-only", "-h", "--human-readable", @@ -386,6 +395,7 @@ void test_client_cli() { test_parse_args_invalid_compression_level(); test_parse_args_valid_compression_level(); test_parse_args_debug_flags(); + test_parse_args_debug_help(); test_parse_args_debug_flags_validation(); test_parse_args_unknown_option(); test_parse_args_rejects_unimplemented_options();