about summary refs log tree commit diff
path: root/src/libnm-client-impl/nm-libnm-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-client-impl/nm-libnm-utils.c')
-rw-r--r--src/libnm-client-impl/nm-libnm-utils.c163
1 files changed, 132 insertions, 31 deletions
diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c
index 6a7c155e..398c6ebc 100644
--- a/src/libnm-client-impl/nm-libnm-utils.c
+++ b/src/libnm-client-impl/nm-libnm-utils.c
@@ -16,7 +16,13 @@
 
 /*****************************************************************************/
 
-volatile int _nml_dbus_log_level = 0;
+#define LOG_FILE_FD_UNSET   -3
+#define LOG_FILE_FD_NONE    -2
+#define LOG_FILE_FD_DEFUNCT -1
+
+volatile int _nml_dbus_log_level   = 0;
+const char  *_nml_dbus_log_file    = NULL;
+int          _nml_dbus_log_file_fd = LOG_FILE_FD_UNSET;
 
 int
 _nml_dbus_log_level_init(void)
@@ -40,6 +46,97 @@ _nml_dbus_log_level_init(void)
     return l;
 }
 
+static const char *
+_nml_dbus_log_file_init(void)
+{
+    const char *s;
+
+    s = g_getenv("LIBNM_CLIENT_DEBUG_FILE");
+    if (nm_str_not_empty(s)) {
+        if (strstr(s, "%p")) {
+            gs_strfreev char **tokens = NULL;
+            char               pid_str[100];
+
+            tokens = g_strsplit(s, "%p", -1);
+            nm_sprintf_buf(pid_str, "%lld", (long long) getpid());
+            s = nm_str_realloc(g_strjoinv(pid_str, tokens));
+        } else
+            s = g_strdup(s);
+    } else
+        s = "";
+
+    if (!g_atomic_pointer_compare_and_exchange(&_nml_dbus_log_file, NULL, s)) {
+        if (s[0] != '\0')
+            g_free((gpointer) s);
+        s = g_atomic_pointer_get(&_nml_dbus_log_file);
+    }
+
+    return s;
+}
+
+#define nml_dbus_log_file()                             \
+    ({                                                  \
+        const char *_l;                                 \
+                                                        \
+        _l = g_atomic_pointer_get(&_nml_dbus_log_file); \
+        if (G_UNLIKELY(!_l))                            \
+            _l = _nml_dbus_log_file_init();             \
+                                                        \
+        _l[0] ? _l : NULL;                              \
+    })
+
+static int
+_nml_dbus_log_file_fd_init(void)
+{
+    static GMutex mutex;
+    NM_G_MUTEX_LOCKED(&mutex);
+    int fd;
+
+    fd = g_atomic_int_get(&_nml_dbus_log_file_fd);
+    if (fd == LOG_FILE_FD_UNSET) {
+        const char *name;
+
+        name = nml_dbus_log_file();
+        if (!name)
+            fd = LOG_FILE_FD_NONE;
+        else {
+            fd = open(name, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, 0600);
+            if (fd < 0)
+                fd = LOG_FILE_FD_DEFUNCT;
+        }
+        g_atomic_int_set(&_nml_dbus_log_file_fd, fd);
+    }
+
+    return fd;
+}
+
+#define nml_dbus_log_file_fd()                                                          \
+    ({                                                                                  \
+        int _fd2;                                                                       \
+                                                                                        \
+        _fd2 = g_atomic_int_get(&_nml_dbus_log_file_fd);                                \
+        if (G_UNLIKELY(_fd2 == LOG_FILE_FD_UNSET))                                      \
+            _fd2 = _nml_dbus_log_file_fd_init();                                        \
+                                                                                        \
+        nm_assert(NM_IN_SET(_fd2, LOG_FILE_FD_NONE, LOG_FILE_FD_DEFUNCT) || _fd2 >= 0); \
+        _fd2;                                                                           \
+    })
+
+#define _log_printf(use_stdout, ...)            \
+    G_STMT_START                                \
+    {                                           \
+        const int _fd = nml_dbus_log_file_fd(); \
+                                                \
+        if (_fd != LOG_FILE_FD_NONE) {          \
+            if (_fd >= 0)                       \
+                dprintf(_fd, __VA_ARGS__);      \
+        } else if (use_stdout)                  \
+            g_print(__VA_ARGS__);               \
+        else                                    \
+            g_printerr(__VA_ARGS__);            \
+    }                                           \
+    G_STMT_END
+
 void
 _nml_dbus_log(NMLDBusLogLevel level, gboolean use_stdout, const char *fmt, ...)
 {
@@ -94,21 +191,13 @@ _nml_dbus_log(NMLDBusLogLevel level, gboolean use_stdout, const char *fmt, ...)
 
     pid = getpid();
 
-    if (use_stdout) {
-        g_print("libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n",
+    _log_printf(use_stdout,
+                "libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n",
                 (long long) pid,
                 prefix,
                 ts / NM_UTILS_NSEC_PER_SEC,
                 (ts / (NM_UTILS_NSEC_PER_SEC / 100000)) % 100000,
                 msg);
-    } else {
-        g_printerr("libnm-dbus[%lld]: %s[%" G_GINT64_FORMAT ".%05" G_GINT64_FORMAT "] %s\n",
-                   (long long) pid,
-                   prefix,
-                   ts / NM_UTILS_NSEC_PER_SEC,
-                   (ts / (NM_UTILS_NSEC_PER_SEC / 100000)) % 100000,
-                   msg);
-    }
 }
 
 /*****************************************************************************/
@@ -693,6 +782,7 @@ const NMLDBusMetaIface *const _nml_dbus_meta_ifaces[] = {
     &_nml_dbus_meta_iface_nm_device_generic,
     &_nml_dbus_meta_iface_nm_device_iptunnel,
     &_nml_dbus_meta_iface_nm_device_infiniband,
+    &_nml_dbus_meta_iface_nm_device_loopback,
     &_nml_dbus_meta_iface_nm_device_lowpan,
     &_nml_dbus_meta_iface_nm_device_macsec,
     &_nml_dbus_meta_iface_nm_device_macvlan,
@@ -752,11 +842,11 @@ nml_dbus_meta_iface_get(const char *dbus_iface_name)
 
     if (NM_STR_HAS_PREFIX(dbus_iface_name, COMMON_PREFIX)) {
         /* optimize, that in fact all our interfaces have the same prefix. */
-        idx = nm_utils_ptrarray_find_binary_search((gconstpointer *) _nml_dbus_meta_ifaces,
-                                                   G_N_ELEMENTS(_nml_dbus_meta_ifaces),
-                                                   &dbus_iface_name[NM_STRLEN(COMMON_PREFIX)],
-                                                   _strcmp_common_prefix,
-                                                   NULL);
+        idx = nm_ptrarray_find_bsearch((gconstpointer *) _nml_dbus_meta_ifaces,
+                                       G_N_ELEMENTS(_nml_dbus_meta_ifaces),
+                                       &dbus_iface_name[NM_STRLEN(COMMON_PREFIX)],
+                                       _strcmp_common_prefix,
+                                       NULL);
     } else
         return NULL;
 
@@ -775,12 +865,12 @@ nml_dbus_meta_property_get(const NMLDBusMetaIface *meta_iface,
     nm_assert(meta_iface);
     nm_assert(dbus_property_name);
 
-    idx = nm_utils_array_find_binary_search(meta_iface->dbus_properties,
-                                            sizeof(meta_iface->dbus_properties[0]),
-                                            meta_iface->n_dbus_properties,
-                                            &dbus_property_name,
-                                            nm_strcmp_p_with_data,
-                                            NULL);
+    idx = nm_array_find_bsearch(meta_iface->dbus_properties,
+                                meta_iface->n_dbus_properties,
+                                sizeof(meta_iface->dbus_properties[0]),
+                                &dbus_property_name,
+                                nm_strcmp_p_with_data,
+                                NULL);
     if (idx < 0) {
         NM_SET_OUT(out_idx, meta_iface->n_dbus_properties);
         return NULL;
@@ -883,8 +973,10 @@ nm_utils_g_param_spec_is_default(const GParamSpec *pspec)
 /**
  * nm_utils_print:
  * @output_mode: if 1 it uses g_print(). If 2, it uses g_printerr().
- *   If 0, it uses either g_print() or g_printerr(), depending
- *   on LIBNM_CLIENT_DEBUG (and the "stdout" flag).
+ *   If 0, it uses the same output as internal libnm debug logging
+ *   does. That is, depending on LIBNM_CLIENT_DEBUG's "stdout" flag
+ *   it uses g_print() or g_printerr() and if LIBNM_CLIENT_DEBUG_FILE is
+ *   set, it writes the output to file instead
  * @msg: the message to print. The function does not append
  *   a trailing newline.
  *
@@ -895,6 +987,11 @@ nm_utils_g_param_spec_is_default(const GParamSpec *pspec)
  * with these functions (it implements additional buffering). By
  * using nm_utils_print(), the same logging mechanisms can be used.
  *
+ * Also, libnm honors LIBNM_CLIENT_DEBUG_FILE environment. If this
+ * is set to a filename pattern (accepting "%p" for the process ID),
+ * then the debug log is written to that file instead. With @output_mode
+ * zero, the same location will be written. Since: 1.44.
+ *
  * Since: 1.30
  */
 void
@@ -904,17 +1001,21 @@ nm_utils_print(int output_mode, const char *msg)
 
     g_return_if_fail(msg);
 
-    if (output_mode == 0) {
+    switch (output_mode) {
+    case 0:
         nml_dbus_log_enabled_full(NML_DBUS_LOG_LEVEL_ANY, &use_stdout);
-        output_mode = use_stdout ? 1 : 2;
-    }
-
-    if (output_mode == 1)
+        _log_printf(use_stdout, "%s", msg);
+        break;
+    case 1:
         g_print("%s", msg);
-    else if (output_mode == 2)
+        break;
+    case 2:
         g_printerr("%s", msg);
-    else
+        break;
+    default:
         g_return_if_reached();
+        break;
+    }
 }
 
 /*****************************************************************************/