/* * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 2021 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include "wslog.h" #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #define PREFIX_BUFSIZE 128 #define ENV_VAR_LEVEL "WIRESHARK_LOG_LEVEL" #define ENV_VAR_DOMAINS "WIRESHARK_LOG_DOMAINS" #define DEFAULT_LOG_LEVEL LOG_LEVEL_MESSAGE static enum ws_log_level current_log_level = LOG_LEVEL_NONE; static const char *registered_appname = NULL; static GPtrArray *domain_filter = NULL; static ws_log_writer_cb *registered_log_writer = NULL; static void *registered_log_writer_data = NULL; static ws_log_writer_free_data_cb *registered_log_writer_data_free = NULL; static FILE *custom_log = NULL; static void ws_log_cleanup(void); void ws_log_fprint(FILE *fp, const char *format, va_list ap, const char *prefix) { fputs(prefix, fp); vfprintf(fp, format, ap); fputc('\n', fp); fflush(fp); } const char *ws_log_level_to_string(enum ws_log_level level) { switch (level) { case LOG_LEVEL_NONE: return "(none)"; case LOG_LEVEL_ERROR: return "ERROR"; case LOG_LEVEL_CRITICAL: return "CRITICAL"; case LOG_LEVEL_WARNING: return "WARNING"; case LOG_LEVEL_MESSAGE: return "MESSAGE"; case LOG_LEVEL_INFO: return "INFO"; case LOG_LEVEL_DEBUG: return "DEBUG"; default: return "(BOGUS LOG LEVEL)"; } } gboolean ws_log_level_is_active(enum ws_log_level level) { return level <= current_log_level; } gboolean ws_log_domain_is_active(const char *domain) { if (domain_filter == NULL) return TRUE; for (guint i = 0; i < domain_filter->len; i++) { if (g_ascii_strcasecmp(domain_filter->pdata[i], domain) == 0) { return TRUE; } } return FALSE; } static gboolean log_drop_message(const char *domain, enum ws_log_level level) { if (level <= LOG_LEVEL_CRITICAL) return FALSE; return !ws_log_level_is_active(level) || !ws_log_domain_is_active(domain); } enum ws_log_level ws_log_get_level(void) { return current_log_level; } enum ws_log_level ws_log_set_level(enum ws_log_level log_level) { ws_assert(log_level > LOG_LEVEL_NONE && log_level < _LOG_LEVEL_LAST); current_log_level = log_level; return current_log_level; } enum ws_log_level ws_log_set_level_str(const char *str_level) { enum ws_log_level level; if (!str_level) return LOG_LEVEL_NONE; if (g_ascii_strcasecmp(str_level, "debug") == 0) level = LOG_LEVEL_DEBUG; else if (g_ascii_strcasecmp(str_level, "info") == 0) level = LOG_LEVEL_INFO; else if (g_ascii_strcasecmp(str_level, "message") == 0) level = LOG_LEVEL_MESSAGE; else if (g_ascii_strcasecmp(str_level, "warning") == 0) level = LOG_LEVEL_WARNING; else if (g_ascii_strcasecmp(str_level, "critical") == 0) level = LOG_LEVEL_CRITICAL; else if (g_ascii_strcasecmp(str_level, "error") == 0) level = LOG_LEVEL_ERROR; else return LOG_LEVEL_NONE; current_log_level = level; return current_log_level; } static const char *opt_level = "--log-level"; static const char *opt_domains = "--log-domains"; static const char *opt_file = "--log-file"; int ws_log_parse_args(int *argc_ptr, char *argv[], void (*print_err)(const char *, ...)) { char **ptr = argv; int count = *argc_ptr; int ret = 0; size_t optlen; const char *option, *value; int prune_extra; while (*ptr != NULL) { if (g_str_has_prefix(*ptr, opt_level)) { option = opt_level; optlen = strlen(opt_level); } else if (g_str_has_prefix(*ptr, opt_domains)) { option = opt_domains; optlen = strlen(opt_domains); } else if (g_str_has_prefix(*ptr, opt_file)) { option = opt_file; optlen = strlen(opt_file); } else { ptr += 1; count -= 1; continue; } value = *ptr + optlen; /* Two possibilities: * --