/** * Debug configuration implementation * Runtime debug configuration system for flexible debug output control */ #include "debug_config.h" #include #include #include #include #include #include #include #include /* Rate limiting */ static size_t output_count = 0; static time_t last_output_time = 0; /* ANSI color codes */ static const char* color_red = "\033[31m"; static const char* color_yellow = "\033[33m"; static const char* color_green = "\033[32m"; static const char* color_blue = "\033[34m"; static const char* color_magenta = "\033[35m"; static const char* color_cyan = "\033[36m"; static const char* color_reset = "\033[0m"; /* Get category by name */ static debug_category_t get_category_by_name(const char* name) { struct { const char* n; debug_category_t c; } map[] = { {"none", DEBUG_CATEGORY_NONE}, {"uasync", DEBUG_CATEGORY_UASYNC}, {"ll_queue", DEBUG_CATEGORY_LL_QUEUE}, {"connection", DEBUG_CATEGORY_CONNECTION}, {"etcp", DEBUG_CATEGORY_ETCP}, {"crypto", DEBUG_CATEGORY_CRYPTO}, {"memory", DEBUG_CATEGORY_MEMORY}, {"timing", DEBUG_CATEGORY_TIMING}, {"config", DEBUG_CATEGORY_CONFIG}, {"tun", DEBUG_CATEGORY_TUN}, {"routing", DEBUG_CATEGORY_ROUTING}, {"timers", DEBUG_CATEGORY_TIMERS}, {"all", DEBUG_CATEGORY_ALL}, {NULL, 0} }; for (int i = 0; map[i].n; i++) { if (strcasecmp(map[i].n, name) == 0) { return map[i].c; } } return DEBUG_CATEGORY_NONE; } /* Get level by name */ static debug_level_t get_level_by_name(const char* name) { struct { const char* n; debug_level_t l; } map[] = { {"none", DEBUG_LEVEL_NONE}, {"error", DEBUG_LEVEL_ERROR}, {"warn", DEBUG_LEVEL_WARN}, {"info", DEBUG_LEVEL_INFO}, {"debug", DEBUG_LEVEL_DEBUG}, {"trace", DEBUG_LEVEL_TRACE}, {NULL, 0} }; for (int i = 0; map[i].n; i++) { if (strcasecmp(map[i].n, name) == 0) { return map[i].l; } } return DEBUG_LEVEL_NONE; } /* Global debug configuration */ debug_config_t g_debug_config; FILE* debug_output_file = NULL; /* Forward declaration for global_config structure */ struct global_config { char log_file[256]; char debug_level[32]; uint32_t debug_categories; int enable_timestamp; int enable_function_names; int enable_file_lines; int enable_colors; }; /* Apply debug settings from configuration if not overridden by CLI */ void debug_apply_config(const struct global_config *config, int cli_override_level, int cli_override_categories) { if (!config) return; // Apply log file if specified and not overridden if (config->log_file[0] != '\0') { debug_set_output_file(config->log_file); DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Set log file to: %s", config->log_file); } // Apply debug level from config if not overridden by CLI if (!cli_override_level && config->debug_level[0] != '\0') { debug_level_t level = DEBUG_LEVEL_INFO; // default if (strcasecmp(config->debug_level, "error") == 0) level = DEBUG_LEVEL_ERROR; else if (strcasecmp(config->debug_level, "warn") == 0) level = DEBUG_LEVEL_WARN; else if (strcasecmp(config->debug_level, "info") == 0) level = DEBUG_LEVEL_INFO; else if (strcasecmp(config->debug_level, "debug") == 0) level = DEBUG_LEVEL_DEBUG; else if (strcasecmp(config->debug_level, "trace") == 0) level = DEBUG_LEVEL_TRACE; debug_set_level(level); DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Set debug level from config: %s", config->debug_level); } // Apply debug categories from config if not overridden by CLI if (!cli_override_categories && config->debug_categories != 0) { debug_set_categories(config->debug_categories); DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Set debug categories from config: 0x%X", config->debug_categories); } // Apply output options from config debug_enable_timestamp(config->enable_timestamp); debug_enable_function_name(config->enable_function_names); debug_enable_file_line(config->enable_file_lines); debug_enable_color(config->enable_colors); DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Applied debug configuration from file"); } /* Initialize debug system with default settings */ void debug_config_init(void) { g_debug_config.default_level = DEBUG_LEVEL_ERROR; g_debug_config.categories = DEBUG_CATEGORY_ALL; memset(g_debug_config.category_levels, 0, sizeof(g_debug_config.category_levels)); g_debug_config.timestamp_enabled = 1; g_debug_config.function_name_enabled = 1; g_debug_config.file_line_enabled = 1; g_debug_config.color_enabled = 1; g_debug_config.max_output_per_second = 0; g_debug_config.output_file = NULL; if (debug_output_file && debug_output_file != stdout && debug_output_file != stderr) { fclose(debug_output_file); } debug_output_file = stdout; output_count = 0; last_output_time = 0; } /* Set debug level */ void debug_set_level(debug_level_t level) { g_debug_config.default_level = level; } /* Enable/disable specific categories */ void debug_enable_category(debug_category_t category) { g_debug_config.categories |= category; } void debug_disable_category(debug_category_t category) { g_debug_config.categories &= ~category; } void debug_set_categories(uint32_t categories) { g_debug_config.categories = categories; } /* Configure output options */ void debug_enable_timestamp(int enable) { g_debug_config.timestamp_enabled = enable; } void debug_enable_function_name(int enable) { g_debug_config.function_name_enabled = enable; } void debug_enable_file_line(int enable) { g_debug_config.file_line_enabled = enable; } void debug_enable_color(int enable) { g_debug_config.color_enabled = enable; } void debug_set_rate_limit(size_t max_per_second) { g_debug_config.max_output_per_second = max_per_second; } void debug_set_output_file(const char* file_path) { if (file_path == NULL) { if (debug_output_file && debug_output_file != stdout && debug_output_file != stderr) { fclose(debug_output_file); } debug_output_file = stdout; g_debug_config.output_file = NULL; } else { FILE* new_file = fopen(file_path, "a"); if (new_file) { if (debug_output_file && debug_output_file != stdout && debug_output_file != stderr) { fclose(debug_output_file); } debug_output_file = new_file; g_debug_config.output_file = file_path; } } } /* Check if debug output should be shown for given level and category */ int debug_should_output(debug_level_t level, debug_category_t category) { /* Check if category is enabled */ if (!(g_debug_config.categories & category)) { return 0; } /* Check if level is sufficient */ debug_level_t eff_level = debug_get_effective_level(category); if (level > eff_level) { return 0; } /* Rate limiting check */ if (g_debug_config.max_output_per_second > 0) { time_t current_time = time(NULL); if (current_time != last_output_time) { output_count = 0; last_output_time = current_time; } if (output_count >= g_debug_config.max_output_per_second) { return 0; } output_count++; } return 1; } /* Get current debug level for a category */ debug_level_t debug_get_effective_level(debug_category_t category) { if (category == DEBUG_CATEGORY_ALL || category == DEBUG_CATEGORY_NONE) { return g_debug_config.default_level; } /* Assume single category bit */ uint32_t cat = category; if (__builtin_popcount(cat) != 1) { return DEBUG_LEVEL_NONE; /* Not supported for multiple */ } int index = __builtin_ctz(cat); if (index >= NUM_DEBUG_CATEGORIES) { return DEBUG_LEVEL_NONE; } debug_level_t lev = g_debug_config.category_levels[index]; return (lev == DEBUG_LEVEL_NONE) ? g_debug_config.default_level : lev; } /* Get color for debug level */ static const char* get_level_color(debug_level_t level) { if (!g_debug_config.color_enabled) { return ""; } switch (level) { case DEBUG_LEVEL_ERROR: return color_red; case DEBUG_LEVEL_WARN: return color_yellow; case DEBUG_LEVEL_INFO: return color_green; case DEBUG_LEVEL_DEBUG: return color_blue; case DEBUG_LEVEL_TRACE: return color_magenta; default: return ""; } } /* Get level name */ static const char* get_level_name(debug_level_t level) { switch (level) { case DEBUG_LEVEL_ERROR: return "ERROR"; case DEBUG_LEVEL_WARN: return "WARN"; case DEBUG_LEVEL_INFO: return "INFO"; case DEBUG_LEVEL_DEBUG: return "DEBUG"; case DEBUG_LEVEL_TRACE: return "TRACE"; default: return "UNKNOWN"; } } /* Format and output debug message */ void debug_output(debug_level_t level, debug_category_t category, const char* function, const char* file, int line, const char* format, ...) { #define BUFFER_SIZE 4096 char buffer[BUFFER_SIZE]; int offset = 0; size_t remaining = BUFFER_SIZE; va_list args; va_start(args, format); FILE* output = debug_output_file ? debug_output_file : stdout; /* Add timestamp if enabled */ if (g_debug_config.timestamp_enabled) { time_t now = time(NULL); struct tm* tm_info = localtime(&now); char time_str[32]; strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info); offset += snprintf(buffer + offset, remaining, "[%s] ", time_str); remaining = BUFFER_SIZE - offset; } /* Add level and color */ const char* color = get_level_color(level); const char* level_name = get_level_name(level); if (g_debug_config.color_enabled) { offset += snprintf(buffer + offset, remaining, "%s[%s]%s ", color, level_name, color_reset); } else { offset += snprintf(buffer + offset, remaining, "[%s] ", level_name); } remaining = BUFFER_SIZE - offset; /* Add category */ offset += snprintf(buffer + offset, remaining, "[%d] ", category); remaining = BUFFER_SIZE - offset; /* Add function name if enabled */ if (g_debug_config.function_name_enabled && function) { offset += snprintf(buffer + offset, remaining, "%s() ", function); remaining = BUFFER_SIZE - offset; } /* Add file:line if enabled */ if (g_debug_config.file_line_enabled && file) { offset += snprintf(buffer + offset, remaining, "(%s:%d) ", file, line); remaining = BUFFER_SIZE - offset; } /* Add the actual message */ offset += vsnprintf(buffer + offset, remaining, format, args); remaining = BUFFER_SIZE - offset; /* Add newline */ if (remaining > 1) { buffer[offset] = '\n'; buffer[offset + 1] = '\0'; } else { buffer[BUFFER_SIZE - 2] = '\n'; buffer[BUFFER_SIZE - 1] = '\0'; } va_end(args); /* Output the entire line at once */ fprintf(output, "%s", buffer); fflush(output); #undef BUFFER_SIZE } /* Parse debug configuration from string */ int debug_parse_config(const char* config_string) { if (!config_string || !*config_string) { return 0; } char* str = strdup(config_string); if (!str) { return -1; } char* token = strtok(str, ","); while (token) { /* Trim leading spaces */ while (isspace(*token)) { token++; } char* colon = strchr(token, ':'); if (!colon) { free(str); return -1; } *colon = '\0'; char* cat_name = token; char* level_name = colon + 1; /* Trim leading spaces in level_name */ while (isspace(*level_name)) { level_name++; } /* Find category */ debug_category_t cat = get_category_by_name(cat_name); if (cat == DEBUG_CATEGORY_NONE) { free(str); return -1; } /* Find level */ debug_level_t lev = get_level_by_name(level_name); if (lev == DEBUG_LEVEL_NONE) { free(str); return -1; } if (cat == DEBUG_CATEGORY_ALL) { g_debug_config.default_level = lev; } else { g_debug_config.categories |= cat; int index = __builtin_ctz(cat); if (index < NUM_DEBUG_CATEGORIES) { g_debug_config.category_levels[index] = lev; } } token = strtok(NULL, ","); } free(str); return 0; } /* Apply debug settings from configuration string values */ void debug_apply_config_values(const char *log_file, const char *debug_level_str, uint32_t debug_categories, int enable_timestamp, int enable_function_names, int enable_file_lines, int enable_colors, int cli_override_level, int cli_override_categories) { // This function would be implemented to apply individual config values // For now, it's a placeholder that could be expanded based on specific needs }