diff options
Diffstat (limited to 'src/nm-logging.c')
| -rw-r--r-- | src/nm-logging.c | 1036 |
1 files changed, 0 insertions, 1036 deletions
diff --git a/src/nm-logging.c b/src/nm-logging.c deleted file mode 100644 index 11affbfb..00000000 --- a/src/nm-logging.c +++ /dev/null @@ -1,1036 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2006 - 2012 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include "nm-default.h" - -#include "nm-logging.h" - -#include <dlfcn.h> -#include <syslog.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include <strings.h> - -#if SYSTEMD_JOURNAL - #define SD_JOURNAL_SUPPRESS_LOCATION - #include <systemd/sd-journal.h> -#endif - -#include "nm-glib-aux/nm-logging-base.h" -#include "nm-glib-aux/nm-time-utils.h" -#include "nm-errors.h" - -/*****************************************************************************/ - -/* Notes about thread-safety: - * - * NetworkManager generally is single-threaded and uses a (GLib) mainloop. - * However, nm-logging is in parts thread-safe. That means: - * - * - functions that configure logging (nm_logging_init(), nm_logging_setup()) and - * most other functions MUST be called only from the main-thread. These functions - * are expected to be called infrequently, so they may or may not use a mutex - * (but the overhead is negligible here). - * - * - functions that do the actual logging logging (nm_log(), nm_logging_enabled()) are - * thread-safe and may be used from multiple threads. - * - When called from the not-main-thread, @mt_require_locking must be set to %TRUE. - * In this case, a Mutex will be used for accessing the global state. - * - When called from the main-thread, they may optionally pass @mt_require_locking %FALSE. - * This avoids extra locking and is in particular interesting for nm_logging_enabled(), - * which is expected to be called frequently and from the main-thread. - * - * Note that the logging macros honor %NM_THREAD_SAFE_ON_MAIN_THREAD define, to automatically - * set @mt_require_locking. That means, by default %NM_THREAD_SAFE_ON_MAIN_THREAD is "1", - * and code that only runs on the main-thread (which is the majority), can get away - * without locking. - */ - -/*****************************************************************************/ - -G_STATIC_ASSERT(LOG_EMERG == 0); -G_STATIC_ASSERT(LOG_ALERT == 1); -G_STATIC_ASSERT(LOG_CRIT == 2); -G_STATIC_ASSERT(LOG_ERR == 3); -G_STATIC_ASSERT(LOG_WARNING == 4); -G_STATIC_ASSERT(LOG_NOTICE == 5); -G_STATIC_ASSERT(LOG_INFO == 6); -G_STATIC_ASSERT(LOG_DEBUG == 7); - -/* We have more then 32 logging domains. Assert that it compiles to a 64 bit sized enum */ -G_STATIC_ASSERT(sizeof(NMLogDomain) >= sizeof(guint64)); - -/* Combined domains */ -#define LOGD_ALL_STRING "ALL" -#define LOGD_DEFAULT_STRING "DEFAULT" -#define LOGD_DHCP_STRING "DHCP" -#define LOGD_IP_STRING "IP" - -/*****************************************************************************/ - -typedef enum { - LOG_BACKEND_GLIB, - LOG_BACKEND_SYSLOG, - LOG_BACKEND_JOURNAL, -} LogBackend; - -typedef struct { - NMLogDomain num; - const char *name; -} LogDesc; - -typedef struct { - char *logging_domains_to_string; -} GlobalMain; - -typedef struct { - NMLogLevel log_level; - bool uses_syslog : 1; - bool init_pre_done : 1; - bool init_done : 1; - bool debug_stderr : 1; - const char *prefix; - const char *syslog_identifier; - - /* before we setup syslog (during start), the backend defaults to GLIB, meaning: - * we use g_log() for all logging. At that point, the application is not yet supposed - * to do any logging and doing so indicates a bug. - * - * Afterwards, the backend is either SYSLOG or JOURNAL. From that point, also - * g_log() is redirected to this backend via a logging handler. */ - LogBackend log_backend; -} Global; - -/*****************************************************************************/ - -G_LOCK_DEFINE_STATIC(log); - -/* This data must only be accessed from the main-thread (and as - * such does not need any lock). */ -static GlobalMain gl_main = {}; - -static union { - /* a union with an immutable and a mutable alias for the Global. - * Since nm-logging must be thread-safe, we must take care at which - * places we only read value ("imm") and where we modify them ("mut"). */ - Global mut; - const Global imm; -} gl = { - .imm = - { - /* nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL); */ - .log_level = LOGL_INFO, - .log_backend = LOG_BACKEND_GLIB, - .syslog_identifier = "SYSLOG_IDENTIFIER=" G_LOG_DOMAIN, - .prefix = "", - }, -}; - -NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL] = { - /* nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL); - * - * Note: LOGD_VPN_PLUGIN is special and must be disabled for - * DEBUG and TRACE levels. */ - [LOGL_INFO] = LOGD_DEFAULT, - [LOGL_WARN] = LOGD_DEFAULT, - [LOGL_ERR] = LOGD_DEFAULT, -}; - -/*****************************************************************************/ - -static const LogDesc domain_desc[] = { - {LOGD_PLATFORM, "PLATFORM"}, - {LOGD_RFKILL, "RFKILL"}, - {LOGD_ETHER, "ETHER"}, - {LOGD_WIFI, "WIFI"}, - {LOGD_BT, "BT"}, - {LOGD_MB, "MB"}, - {LOGD_DHCP4, "DHCP4"}, - {LOGD_DHCP6, "DHCP6"}, - {LOGD_PPP, "PPP"}, - {LOGD_WIFI_SCAN, "WIFI_SCAN"}, - {LOGD_IP4, "IP4"}, - {LOGD_IP6, "IP6"}, - {LOGD_AUTOIP4, "AUTOIP4"}, - {LOGD_DNS, "DNS"}, - {LOGD_VPN, "VPN"}, - {LOGD_SHARING, "SHARING"}, - {LOGD_SUPPLICANT, "SUPPLICANT"}, - {LOGD_AGENTS, "AGENTS"}, - {LOGD_SETTINGS, "SETTINGS"}, - {LOGD_SUSPEND, "SUSPEND"}, - {LOGD_CORE, "CORE"}, - {LOGD_DEVICE, "DEVICE"}, - {LOGD_OLPC, "OLPC"}, - {LOGD_INFINIBAND, "INFINIBAND"}, - {LOGD_FIREWALL, "FIREWALL"}, - {LOGD_ADSL, "ADSL"}, - {LOGD_BOND, "BOND"}, - {LOGD_VLAN, "VLAN"}, - {LOGD_BRIDGE, "BRIDGE"}, - {LOGD_DBUS_PROPS, "DBUS_PROPS"}, - {LOGD_TEAM, "TEAM"}, - {LOGD_CONCHECK, "CONCHECK"}, - {LOGD_DCB, "DCB"}, - {LOGD_DISPATCH, "DISPATCH"}, - {LOGD_AUDIT, "AUDIT"}, - {LOGD_SYSTEMD, "SYSTEMD"}, - {LOGD_VPN_PLUGIN, "VPN_PLUGIN"}, - {LOGD_PROXY, "PROXY"}, - {0}, -}; - -/*****************************************************************************/ - -static char *_domains_to_string(gboolean include_level_override, - NMLogLevel log_level, - const NMLogDomain log_state[static _LOGL_N_REAL]); - -/*****************************************************************************/ - -static gboolean -_syslog_identifier_valid_domain(const char *domain) -{ - char c; - - if (!domain || !domain[0]) - return FALSE; - - /* we pass the syslog identifier as format string. No funny stuff. */ - - for (; (c = domain[0]); domain++) { - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') - || NM_IN_SET(c, '-', '_')) - continue; - return FALSE; - } - return TRUE; -} - -static gboolean -_syslog_identifier_assert(const char *syslog_identifier) -{ - g_assert(syslog_identifier); - g_assert(g_str_has_prefix(syslog_identifier, "SYSLOG_IDENTIFIER=")); - g_assert(_syslog_identifier_valid_domain(&syslog_identifier[NM_STRLEN("SYSLOG_IDENTIFIER=")])); - return TRUE; -} - -static const char * -syslog_identifier_domain(const char *syslog_identifier) -{ - nm_assert(_syslog_identifier_assert(syslog_identifier)); - return &syslog_identifier[NM_STRLEN("SYSLOG_IDENTIFIER=")]; -} - -#if SYSTEMD_JOURNAL -static const char * -syslog_identifier_full(const char *syslog_identifier) -{ - nm_assert(_syslog_identifier_assert(syslog_identifier)); - return &syslog_identifier[0]; -} -#endif - -/*****************************************************************************/ - -static gboolean -match_log_level(const char *level, NMLogLevel *out_level, GError **error) -{ - if (_nm_log_parse_level(level, out_level)) - return TRUE; - - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL, - _("Unknown log level '%s'"), - level); - return FALSE; -} - -gboolean -nm_logging_setup(const char *level, const char *domains, char **bad_domains, GError **error) -{ - GString * unrecognized = NULL; - NMLogDomain cur_log_state[_LOGL_N_REAL]; - NMLogDomain new_log_state[_LOGL_N_REAL]; - NMLogLevel cur_log_level; - NMLogLevel new_log_level; - gs_free const char **domains_v = NULL; - gsize i_d; - int i; - gboolean had_platform_debug; - gs_free char * domains_free = NULL; - - NM_ASSERT_ON_MAIN_THREAD(); - - g_return_val_if_fail(!bad_domains || !*bad_domains, FALSE); - g_return_val_if_fail(!error || !*error, FALSE); - - cur_log_level = gl.imm.log_level; - memcpy(cur_log_state, _nm_logging_enabled_state, sizeof(cur_log_state)); - - new_log_level = cur_log_level; - - if (!domains || !*domains) { - domains_free = _domains_to_string(FALSE, cur_log_level, cur_log_state); - domains = domains_free; - } - - for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) - new_log_state[i] = 0; - - if (level && *level) { - if (!match_log_level(level, &new_log_level, error)) - return FALSE; - if (new_log_level == _LOGL_KEEP) { - new_log_level = cur_log_level; - for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) - new_log_state[i] = cur_log_state[i]; - } - } - - domains_v = nm_utils_strsplit_set(domains, ", "); - for (i_d = 0; domains_v && domains_v[i_d]; i_d++) { - const char * s = domains_v[i_d]; - const char * p; - const LogDesc *diter; - NMLogLevel domain_log_level; - NMLogDomain bits; - - /* LOGD_VPN_PLUGIN is protected, that is, when setting ALL or DEFAULT, - * it does not enable the verbose levels DEBUG and TRACE, because that - * may expose sensitive data. */ - NMLogDomain protect = LOGD_NONE; - - p = strchr(s, ':'); - if (p) { - *((char *) p) = '\0'; - if (!match_log_level(p + 1, &domain_log_level, error)) - return FALSE; - } else - domain_log_level = new_log_level; - - bits = 0; - - if (domains_free) { - /* The caller didn't provide any domains to set (`nmcli general logging level DEBUG`). - * We reset all domains that were previously set, but we still want to protect - * VPN_PLUGIN domain. */ - protect = LOGD_VPN_PLUGIN; - } - - /* Check for combined domains */ - if (!g_ascii_strcasecmp(s, LOGD_ALL_STRING)) { - bits = LOGD_ALL; - protect = LOGD_VPN_PLUGIN; - } else if (!g_ascii_strcasecmp(s, LOGD_DEFAULT_STRING)) { - bits = LOGD_DEFAULT; - protect = LOGD_VPN_PLUGIN; - } else if (!g_ascii_strcasecmp(s, LOGD_DHCP_STRING)) - bits = LOGD_DHCP; - else if (!g_ascii_strcasecmp(s, LOGD_IP_STRING)) - bits = LOGD_IP; - - /* Check for compatibility domains */ - else if (!g_ascii_strcasecmp(s, "HW")) - bits = LOGD_PLATFORM; - else if (!g_ascii_strcasecmp(s, "WIMAX")) - continue; - - else { - for (diter = &domain_desc[0]; diter->name; diter++) { - if (!g_ascii_strcasecmp(diter->name, s)) { - bits = diter->num; - break; - } - } - - if (!bits) { - if (!bad_domains) { - g_set_error(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN, - _("Unknown log domain '%s'"), - s); - return FALSE; - } - - if (unrecognized) - g_string_append(unrecognized, ", "); - else - unrecognized = g_string_new(NULL); - g_string_append(unrecognized, s); - continue; - } - } - - if (domain_log_level == _LOGL_KEEP) { - for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) - new_log_state[i] = (new_log_state[i] & ~bits) | (cur_log_state[i] & bits); - } else { - for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) { - if (i < domain_log_level) - new_log_state[i] &= ~bits; - else { - new_log_state[i] |= bits; - if ((protect & bits) && i < LOGL_INFO) - new_log_state[i] &= ~protect; - } - } - } - } - - nm_clear_g_free(&gl_main.logging_domains_to_string); - - had_platform_debug = _nm_logging_enabled_lockfree(LOGL_DEBUG, LOGD_PLATFORM); - - G_LOCK(log); - - gl.mut.log_level = new_log_level; - for (i = 0; i < G_N_ELEMENTS(new_log_state); i++) - _nm_logging_enabled_state[i] = new_log_state[i]; - - G_UNLOCK(log); - - if (had_platform_debug && !_nm_logging_enabled_lockfree(LOGL_DEBUG, LOGD_PLATFORM)) { - /* when debug logging is enabled, platform will cache all access to - * sysctl. When the user disables debug-logging, we want to clear that - * cache right away. */ - _nm_logging_clear_platform_logging_cache(); - } - - if (unrecognized) - *bad_domains = g_string_free(unrecognized, FALSE); - - return TRUE; -} - -const char * -nm_logging_level_to_string(void) -{ - NM_ASSERT_ON_MAIN_THREAD(); - - return level_desc[gl.imm.log_level].name; -} - -const char * -nm_logging_all_levels_to_string(void) -{ - static GString *str; - - if (G_UNLIKELY(!str)) { - int i; - - str = g_string_new(NULL); - for (i = 0; i < G_N_ELEMENTS(level_desc); i++) { - if (str->len) - g_string_append_c(str, ','); - g_string_append(str, level_desc[i].name); - } - } - - return str->str; -} - -const char * -nm_logging_domains_to_string(void) -{ - NM_ASSERT_ON_MAIN_THREAD(); - - if (G_UNLIKELY(!gl_main.logging_domains_to_string)) { - gl_main.logging_domains_to_string = - _domains_to_string(TRUE, gl.imm.log_level, _nm_logging_enabled_state); - } - - return gl_main.logging_domains_to_string; -} - -static char * -_domains_to_string(gboolean include_level_override, - NMLogLevel log_level, - const NMLogDomain log_state[static _LOGL_N_REAL]) -{ - const LogDesc *diter; - GString * str; - int i; - - /* We don't just return g_strdup() the logging domains that were set during - * nm_logging_setup(), because we want to expand "DEFAULT" and "ALL". - */ - - str = g_string_sized_new(75); - for (diter = &domain_desc[0]; diter->name; diter++) { - /* If it's set for any lower level, it will also be set for LOGL_ERR */ - if (!(diter->num & log_state[LOGL_ERR])) - continue; - - if (str->len) - g_string_append_c(str, ','); - g_string_append(str, diter->name); - - if (!include_level_override) - continue; - - /* Check if it's logging at a lower level than the default. */ - for (i = 0; i < log_level; i++) { - if (diter->num & log_state[i]) { - g_string_append_printf(str, ":%s", level_desc[i].name); - break; - } - } - /* Check if it's logging at a higher level than the default. */ - if (!(diter->num & log_state[log_level])) { - for (i = log_level + 1; i < _LOGL_N_REAL; i++) { - if (diter->num & log_state[i]) { - g_string_append_printf(str, ":%s", level_desc[i].name); - break; - } - } - } - } - return g_string_free(str, FALSE); -} - -static char _all_logging_domains_to_str[273]; - -const char * -nm_logging_all_domains_to_string(void) -{ - static const char *volatile str = NULL; - const char *s; - -again: - s = g_atomic_pointer_get(&str); - if (G_UNLIKELY(!s)) { - static gsize once = 0; - const LogDesc *diter; - gsize buf_l; - char * buf_p; - - if (!g_once_init_enter(&once)) - goto again; - - buf_p = _all_logging_domains_to_str; - buf_l = sizeof(_all_logging_domains_to_str); - - nm_utils_strbuf_append_str(&buf_p, &buf_l, LOGD_DEFAULT_STRING); - for (diter = &domain_desc[0]; diter->name; diter++) { - nm_utils_strbuf_append_c(&buf_p, &buf_l, ','); - nm_utils_strbuf_append_str(&buf_p, &buf_l, diter->name); - if (diter->num == LOGD_DHCP6) - nm_utils_strbuf_append_str(&buf_p, &buf_l, "," LOGD_DHCP_STRING); - else if (diter->num == LOGD_IP6) - nm_utils_strbuf_append_str(&buf_p, &buf_l, "," LOGD_IP_STRING); - } - nm_utils_strbuf_append_str(&buf_p, &buf_l, LOGD_ALL_STRING); - - /* Did you modify the logging domains (or their names)? Adjust the size of - * _all_logging_domains_to_str buffer above to have the exact size. */ - nm_assert(strlen(_all_logging_domains_to_str) == sizeof(_all_logging_domains_to_str) - 1); - nm_assert(buf_l == 1); - - s = _all_logging_domains_to_str; - g_atomic_pointer_set(&str, s); - g_once_init_leave(&once, 1); - } - - return s; -} - -/** - * nm_logging_get_level: - * @domain: find the lowest enabled logging level for the - * given domain. If this is a set of multiple - * domains, the most verbose level will be returned. - * - * Returns: the lowest (most verbose) logging level for the - * give @domain, or %_LOGL_OFF if it is disabled. - **/ -NMLogLevel -nm_logging_get_level(NMLogDomain domain) -{ - NMLogLevel sl = _LOGL_OFF; - - G_STATIC_ASSERT(LOGL_TRACE == 0); - while (sl > LOGL_TRACE && _nm_logging_enabled_lockfree(sl - 1, domain)) - sl--; - return sl; -} - -gboolean -_nm_logging_enabled_locking(NMLogLevel level, NMLogDomain domain) -{ - gboolean v; - - G_LOCK(log); - v = _nm_logging_enabled_lockfree(level, domain); - G_UNLOCK(log); - return v; -} - -gboolean -_nm_log_enabled_impl(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain) -{ - return nm_logging_enabled_mt(mt_require_locking, level, domain); -} - -#if SYSTEMD_JOURNAL -static void -_iovec_set(struct iovec *iov, const void *str, gsize len) -{ - iov->iov_base = (void *) str; - iov->iov_len = len; -} - -static void -_iovec_set_string(struct iovec *iov, const char *str) -{ - _iovec_set(iov, str, strlen(str)); -} - - #define _iovec_set_string_literal(iov, str) _iovec_set((iov), "" str "", NM_STRLEN(str)) - -_nm_printf(3, 4) static void _iovec_set_format(struct iovec *iov, - char ** iov_free, - const char * format, - ...) -{ - va_list ap; - char * str; - - va_start(ap, format); - str = g_strdup_vprintf(format, ap); - va_end(ap); - - _iovec_set_string(iov, str); - *iov_free = str; -} - - #define _iovec_set_format_a(iov, reserve_extra, format, ...) \ - G_STMT_START \ - { \ - const gsize _size = (reserve_extra) + (NM_STRLEN(format) + 3); \ - char *const _buf = g_alloca(_size); \ - int _len; \ - \ - G_STATIC_ASSERT_EXPR((reserve_extra) + (NM_STRLEN(format) + 3) <= 96); \ - \ - _len = g_snprintf(_buf, _size, "" format "", ##__VA_ARGS__); \ - \ - nm_assert(_len >= 0); \ - nm_assert(_len < _size); \ - nm_assert(_len == strlen(_buf)); \ - \ - _iovec_set((iov), _buf, _len); \ - } \ - G_STMT_END - - #define _iovec_set_format_str_a(iov, max_str_len, format, str_arg) \ - G_STMT_START \ - { \ - const char *_str_arg = (str_arg); \ - \ - nm_assert(_str_arg &&strlen(_str_arg) < (max_str_len)); \ - _iovec_set_format_a((iov), (max_str_len), format, str_arg); \ - } \ - G_STMT_END - -#endif - -void -_nm_log_impl(const char *file, - guint line, - const char *func, - gboolean mt_require_locking, - NMLogLevel level, - NMLogDomain domain, - int error, - const char *ifname, - const char *conn_uuid, - const char *fmt, - ...) -{ - va_list args; - char * msg; - GTimeVal tv; - int errsv; - const NMLogDomain *cur_log_state; - NMLogDomain cur_log_state_copy[_LOGL_N_REAL]; - Global g_copy; - const Global * g; - - if (G_UNLIKELY(mt_require_locking)) { - G_LOCK(log); - /* we evaluate logging-enabled under lock. There is still a race that - * we might log the message below *after* logging was disabled. That means, - * when disabling logging, we might still log messages. */ - if (!_nm_logging_enabled_lockfree(level, domain)) { - G_UNLOCK(log); - return; - } - g_copy = gl.imm; - memcpy(cur_log_state_copy, _nm_logging_enabled_state, sizeof(cur_log_state_copy)); - G_UNLOCK(log); - g = &g_copy; - cur_log_state = cur_log_state_copy; - } else { - NM_ASSERT_ON_MAIN_THREAD(); - if (!_nm_logging_enabled_lockfree(level, domain)) - return; - g = &gl.imm; - cur_log_state = _nm_logging_enabled_state; - } - - (void) cur_log_state; - - errsv = errno; - - /* Make sure that %m maps to the specified error */ - if (error != 0) { - if (error < 0) - error = -error; - errno = error; - } - - va_start(args, fmt); - msg = g_strdup_vprintf(fmt, args); - va_end(args); - -#define MESSAGE_FMT "%s%-7s [%ld.%04ld] %s" -#define MESSAGE_ARG(prefix, tv, msg) \ - prefix, level_desc[level].level_str, (tv).tv_sec, ((tv).tv_usec / 100), (msg) - - g_get_current_time(&tv); - - if (g->debug_stderr) - g_printerr(MESSAGE_FMT "\n", MESSAGE_ARG(g->prefix, tv, msg)); - - switch (g->log_backend) { -#if SYSTEMD_JOURNAL - case LOG_BACKEND_JOURNAL: - { - gint64 now, boottime; - struct iovec iov_data[15]; - struct iovec * iov = iov_data; - char * iov_free_data[5]; - char ** iov_free = iov_free_data; - const LogDesc *diter; - NMLogDomain dom_all; - char s_log_domains_buf[NM_STRLEN("NM_LOG_DOMAINS=") + sizeof(_all_logging_domains_to_str)]; - char *s_log_domains; - gsize l_log_domains; - - now = nm_utils_get_monotonic_timestamp_nsec(); - boottime = nm_utils_monotonic_timestamp_as_boottime(now, 1); - - _iovec_set_format_a(iov++, 30, "PRIORITY=%d", level_desc[level].syslog_level); - _iovec_set_format(iov++, - iov_free++, - "MESSAGE=" MESSAGE_FMT, - MESSAGE_ARG(g->prefix, tv, msg)); - _iovec_set_string(iov++, syslog_identifier_full(g->syslog_identifier)); - _iovec_set_format_a(iov++, 30, "SYSLOG_PID=%ld", (long) getpid()); - - dom_all = domain; - s_log_domains = s_log_domains_buf; - l_log_domains = sizeof(s_log_domains_buf); - - nm_utils_strbuf_append_str(&s_log_domains, &l_log_domains, "NM_LOG_DOMAINS="); - for (diter = &domain_desc[0]; dom_all != 0 && diter->name; diter++) { - if (!NM_FLAGS_ANY(dom_all, diter->num)) - continue; - if (dom_all != domain) - nm_utils_strbuf_append_c(&s_log_domains, &l_log_domains, ','); - nm_utils_strbuf_append_str(&s_log_domains, &l_log_domains, diter->name); - dom_all &= ~diter->num; - } - nm_assert(l_log_domains > 0); - _iovec_set(iov++, s_log_domains_buf, s_log_domains - s_log_domains_buf); - - G_STATIC_ASSERT_EXPR(LOG_FAC(LOG_DAEMON) == 3); - _iovec_set_string_literal(iov++, "SYSLOG_FACILITY=3"); - _iovec_set_format_str_a(iov++, 15, "NM_LOG_LEVEL=%s", level_desc[level].name); - if (func) - _iovec_set_format(iov++, iov_free++, "CODE_FUNC=%s", func); - _iovec_set_format(iov++, iov_free++, "CODE_FILE=%s", file ?: ""); - _iovec_set_format_a(iov++, 20, "CODE_LINE=%u", line); - _iovec_set_format_a(iov++, - 60, - "TIMESTAMP_MONOTONIC=%lld.%06lld", - (long long) (now / NM_UTILS_NSEC_PER_SEC), - (long long) ((now % NM_UTILS_NSEC_PER_SEC) / 1000)); - _iovec_set_format_a(iov++, - 60, - "TIMESTAMP_BOOTTIME=%lld.%06lld", - (long long) (boottime / NM_UTILS_NSEC_PER_SEC), - (long long) ((boottime % NM_UTILS_NSEC_PER_SEC) / 1000)); - if (error != 0) - _iovec_set_format_a(iov++, 30, "ERRNO=%d", error); - if (ifname) - _iovec_set_format(iov++, iov_free++, "NM_DEVICE=%s", ifname); - if (conn_uuid) - _iovec_set_format(iov++, iov_free++, "NM_CONNECTION=%s", conn_uuid); - - nm_assert(iov <= &iov_data[G_N_ELEMENTS(iov_data)]); - nm_assert(iov_free <= &iov_free_data[G_N_ELEMENTS(iov_free_data)]); - - sd_journal_sendv(iov_data, iov - iov_data); - - for (; --iov_free >= iov_free_data;) - g_free(*iov_free); - } break; -#endif - case LOG_BACKEND_SYSLOG: - syslog(level_desc[level].syslog_level, MESSAGE_FMT, MESSAGE_ARG(g->prefix, tv, msg)); - break; - default: - g_log(syslog_identifier_domain(g->syslog_identifier), - level_desc[level].g_log_level, - MESSAGE_FMT, - MESSAGE_ARG(g->prefix, tv, msg)); - break; - } - - g_free(msg); - - errno = errsv; -} - -/*****************************************************************************/ - -void -_nm_utils_monotonic_timestamp_initialized(const struct timespec *tp, - gint64 offset_sec, - gboolean is_boottime) -{ - NM_ASSERT_ON_MAIN_THREAD(); - - if (_nm_logging_enabled_lockfree(LOGL_DEBUG, LOGD_CORE)) { - time_t now = time(NULL); - struct tm tm; - char s[255]; - - strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime_r(&now, &tm)); - nm_log_dbg(LOGD_CORE, - "monotonic timestamp started counting 1.%09ld seconds ago with " - "an offset of %lld.0 seconds to %s (local time is %s)", - tp->tv_nsec, - (long long) -offset_sec, - is_boottime ? "CLOCK_BOOTTIME" : "CLOCK_MONOTONIC", - s); - } -} - -/*****************************************************************************/ - -static void -nm_log_handler(const char *log_domain, GLogLevelFlags level, const char *message, gpointer ignored) -{ - int syslog_priority; - - switch (level & G_LOG_LEVEL_MASK) { - case G_LOG_LEVEL_ERROR: - syslog_priority = LOG_CRIT; - break; - case G_LOG_LEVEL_CRITICAL: - syslog_priority = LOG_ERR; - break; - case G_LOG_LEVEL_WARNING: - syslog_priority = LOG_WARNING; - break; - case G_LOG_LEVEL_MESSAGE: - syslog_priority = LOG_NOTICE; - break; - case G_LOG_LEVEL_DEBUG: - syslog_priority = LOG_DEBUG; - break; - case G_LOG_LEVEL_INFO: - default: - syslog_priority = LOG_INFO; - break; - } - - /* we don't need any locking here. The glib log handler gets only registered - * once during nm_logging_init() and the global data is not modified afterwards. */ - nm_assert(gl.imm.init_done); - - if (gl.imm.debug_stderr) - g_printerr("%s%s\n", gl.imm.prefix, message ?: ""); - - switch (gl.imm.log_backend) { -#if SYSTEMD_JOURNAL - case LOG_BACKEND_JOURNAL: - { - gint64 now, boottime; - - now = nm_utils_get_monotonic_timestamp_nsec(); - boottime = nm_utils_monotonic_timestamp_as_boottime(now, 1); - - sd_journal_send("PRIORITY=%d", - syslog_priority, - "MESSAGE=%s%s", - gl.imm.prefix, - message ?: "", - syslog_identifier_full(gl.imm.syslog_identifier), - "SYSLOG_PID=%ld", - (long) getpid(), - "SYSLOG_FACILITY=3", - "GLIB_DOMAIN=%s", - log_domain ?: "", - "GLIB_LEVEL=%d", - (int) (level & G_LOG_LEVEL_MASK), - "TIMESTAMP_MONOTONIC=%lld.%06lld", - (long long) (now / NM_UTILS_NSEC_PER_SEC), - (long long) ((now % NM_UTILS_NSEC_PER_SEC) / 1000), - "TIMESTAMP_BOOTTIME=%lld.%06lld", - (long long) (boottime / NM_UTILS_NSEC_PER_SEC), - (long long) ((boottime % NM_UTILS_NSEC_PER_SEC) / 1000), - NULL); - } break; -#endif - default: - syslog(syslog_priority, "%s%s", gl.imm.prefix, message ?: ""); - break; - } -} - -gboolean -nm_logging_syslog_enabled(void) -{ - NM_ASSERT_ON_MAIN_THREAD(); - - return gl.imm.uses_syslog; -} - -void -nm_logging_init_pre(const char *syslog_identifier, char *prefix_take) -{ - /* this function may be called zero or one times, and only - * - on the main thread - * - not after nm_logging_init(). */ - - NM_ASSERT_ON_MAIN_THREAD(); - - if (gl.imm.init_pre_done) - g_return_if_reached(); - - if (gl.imm.init_done) - g_return_if_reached(); - - if (!_syslog_identifier_valid_domain(syslog_identifier)) - g_return_if_reached(); - - if (!prefix_take || !prefix_take[0]) - g_return_if_reached(); - - G_LOCK(log); - - gl.mut.init_pre_done = TRUE; - - gl.mut.syslog_identifier = g_strdup_printf("SYSLOG_IDENTIFIER=%s", syslog_identifier); - nm_assert(_syslog_identifier_assert(gl.imm.syslog_identifier)); - - /* we pass the allocated string on and never free it. */ - gl.mut.prefix = prefix_take; - - G_UNLOCK(log); -} - -void -nm_logging_init(const char *logging_backend, gboolean debug) -{ - gboolean fetch_monotonic_timestamp = FALSE; - gboolean obsolete_debug_backend = FALSE; - LogBackend x_log_backend; - - /* this function may be called zero or one times, and only on the - * main thread. */ - - NM_ASSERT_ON_MAIN_THREAD(); - - nm_assert(NM_IN_STRSET("" NM_CONFIG_DEFAULT_LOGGING_BACKEND, - NM_LOG_CONFIG_BACKEND_JOURNAL, - NM_LOG_CONFIG_BACKEND_SYSLOG)); - - if (gl.imm.init_done) - g_return_if_reached(); - - if (!logging_backend) - logging_backend = "" NM_CONFIG_DEFAULT_LOGGING_BACKEND; - - if (nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_DEBUG)) { - /* "debug" was wrongly documented as a valid logging backend. It makes no sense however, - * because printing to stderr only makes sense when not demonizing. Whether to daemonize - * is only controlled via command line arguments (--no-daemon, --debug) and not via the - * logging backend from configuration. - * - * Fall back to the default. */ - logging_backend = "" NM_CONFIG_DEFAULT_LOGGING_BACKEND; - obsolete_debug_backend = TRUE; - } - - G_LOCK(log); - -#if SYSTEMD_JOURNAL - if (!nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_SYSLOG)) { - x_log_backend = LOG_BACKEND_JOURNAL; - - /* We only log the monotonic-timestamp with structured logging (journal). - * Only in this case, fetch the timestamp. */ - fetch_monotonic_timestamp = TRUE; - } else -#endif - { - x_log_backend = LOG_BACKEND_SYSLOG; - openlog(syslog_identifier_domain(gl.imm.syslog_identifier), LOG_PID, LOG_DAEMON); - } - - gl.mut.init_done = TRUE; - gl.mut.log_backend = x_log_backend; - gl.mut.uses_syslog = TRUE; - gl.mut.debug_stderr = debug; - - g_log_set_handler(syslog_identifier_domain(gl.imm.syslog_identifier), - G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, - nm_log_handler, - NULL); - - G_UNLOCK(log); - - if (fetch_monotonic_timestamp) { - /* ensure we read a monotonic timestamp. Reading the timestamp the first - * time causes a logging message. We don't want to do that during _nm_log_impl. */ - nm_utils_get_monotonic_timestamp_nsec(); - } - - if (obsolete_debug_backend) - nm_log_dbg(LOGD_CORE, - "config: ignore deprecated logging backend 'debug', fallback to '%s'", - logging_backend); - - if (nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_SYSLOG)) { - /* good */ - } else if (nm_streq(logging_backend, NM_LOG_CONFIG_BACKEND_JOURNAL)) { -#if !SYSTEMD_JOURNAL - nm_log_warn(LOGD_CORE, - "config: logging backend 'journal' is not available, fallback to 'syslog'"); -#endif - } else { - nm_log_warn(LOGD_CORE, - "config: invalid logging backend '%s', fallback to '%s'", - logging_backend, -#if SYSTEMD_JOURNAL - NM_LOG_CONFIG_BACKEND_JOURNAL -#else - NM_LOG_CONFIG_BACKEND_SYSLOG -#endif - ); - } -} |