Blame src/nm-logging.c

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