device: renew dhcp leases on awake for software devices
[NetworkManager.git] / src / nm-logging.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Copyright (C) 2006 - 2012 Red Hat, Inc.
19  * Copyright (C) 2006 - 2008 Novell, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include <dlfcn.h>
25 #include <syslog.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <sys/wait.h>
31 #include <sys/stat.h>
32 #include <strings.h>
33 #include <string.h>
34
35 #if defined (NO_SYSTEMD_JOURNAL) && defined (SYSTEMD_JOURNAL)
36 #undef SYSTEMD_JOURNAL
37 #define SYSTEMD_JOURNAL 0
38 #endif
39
40 #if SYSTEMD_JOURNAL
41 #define SD_JOURNAL_SUPPRESS_LOCATION
42 #include <systemd/sd-journal.h>
43 #endif
44
45 #include "nm-errors.h"
46 #include "nm-core-utils.h"
47
48 typedef enum {
49         LOG_FORMAT_FLAG_NONE                                = 0,
50         LOG_FORMAT_FLAG_TIMESTAMP_DEBUG                     = (1LL << 0),
51         LOG_FORMAT_FLAG_TIMESTAMP_INFO                      = (1LL << 1),
52         LOG_FORMAT_FLAG_TIMESTAMP_ERROR                     = (1LL << 2),
53         LOG_FORMAT_FLAG_LOCATION_DEBUG                      = (1LL << 3),
54         LOG_FORMAT_FLAG_LOCATION_INFO                       = (1LL << 4),
55         LOG_FORMAT_FLAG_LOCATION_ERROR                      = (1LL << 5),
56         LOG_FORMAT_FLAG_ALIGN_LOCATION                      = (1LL << 6),
57
58         _LOG_FORMAT_FLAG_TIMESTAMP                          = LOG_FORMAT_FLAG_TIMESTAMP_DEBUG |
59                                                               LOG_FORMAT_FLAG_TIMESTAMP_INFO |
60                                                               LOG_FORMAT_FLAG_TIMESTAMP_ERROR,
61         _LOG_FORMAT_FLAG_LOCATION                           = LOG_FORMAT_FLAG_LOCATION_DEBUG |
62                                                               LOG_FORMAT_FLAG_LOCATION_INFO |
63                                                               LOG_FORMAT_FLAG_LOCATION_ERROR,
64
65         _LOG_FORMAT_FLAG_LEVEL_DEBUG                        = LOG_FORMAT_FLAG_TIMESTAMP_DEBUG |
66                                                               LOG_FORMAT_FLAG_LOCATION_DEBUG,
67         _LOG_FORMAT_FLAG_LEVEL_INFO                         = LOG_FORMAT_FLAG_TIMESTAMP_INFO |
68                                                               LOG_FORMAT_FLAG_LOCATION_INFO,
69         _LOG_FORMAT_FLAG_LEVEL_ERROR                        = LOG_FORMAT_FLAG_TIMESTAMP_ERROR |
70                                                               LOG_FORMAT_FLAG_LOCATION_ERROR,
71
72         _LOG_FORMAT_FLAG_SYSLOG                             = _LOG_FORMAT_FLAG_TIMESTAMP |
73                                                               LOG_FORMAT_FLAG_LOCATION_DEBUG |
74                                                               LOG_FORMAT_FLAG_LOCATION_ERROR |
75                                                               LOG_FORMAT_FLAG_ALIGN_LOCATION,
76
77         _LOG_FORMAT_FLAG_DEFAULT                            = _LOG_FORMAT_FLAG_TIMESTAMP,
78 } LogFormatFlags;
79
80 void (*_nm_logging_clear_platform_logging_cache) (void);
81
82 static void
83 nm_log_handler (const gchar *log_domain,
84                 GLogLevelFlags level,
85                 const gchar *message,
86                 gpointer ignored);
87
88 typedef struct {
89         NMLogDomain num;
90         const char *name;
91 } LogDesc;
92
93 typedef struct {
94         const char *name;
95         const char *level_str;
96         int syslog_level;
97         GLogLevelFlags g_log_level;
98         LogFormatFlags log_format_level;
99 } LogLevelDesc;
100
101 static struct {
102         NMLogLevel log_level;
103         NMLogDomain logging[_LOGL_N_REAL];
104         gboolean logging_set_up;
105         LogFormatFlags log_format_flags;
106         enum {
107                 LOG_BACKEND_GLIB,
108                 LOG_BACKEND_SYSLOG,
109                 LOG_BACKEND_JOURNAL,
110         } log_backend;
111         char *logging_domains_to_string;
112         const LogLevelDesc level_desc[_LOGL_N];
113
114 #define _DOMAIN_DESC_LEN 37
115         /* Would be nice to use C99 flexible array member here,
116          * but that feature doesn't seem well supported. */
117         const LogDesc domain_desc[_DOMAIN_DESC_LEN];
118 } global = {
119         .log_level = LOGL_INFO,
120         .log_backend = LOG_BACKEND_GLIB,
121         .log_format_flags = _LOG_FORMAT_FLAG_DEFAULT,
122         .level_desc = {
123                 [LOGL_TRACE] = { "TRACE", "<trace>", LOG_DEBUG,   G_LOG_LEVEL_DEBUG,   _LOG_FORMAT_FLAG_LEVEL_DEBUG },
124                 [LOGL_DEBUG] = { "DEBUG", "<debug>", LOG_INFO,    G_LOG_LEVEL_DEBUG,   _LOG_FORMAT_FLAG_LEVEL_DEBUG },
125                 [LOGL_INFO]  = { "INFO",  "<info>",  LOG_INFO,    G_LOG_LEVEL_INFO,    _LOG_FORMAT_FLAG_LEVEL_INFO },
126                 [LOGL_WARN]  = { "WARN",  "<warn>",  LOG_WARNING, G_LOG_LEVEL_MESSAGE, _LOG_FORMAT_FLAG_LEVEL_INFO },
127                 [LOGL_ERR]   = { "ERR",   "<error>", LOG_ERR,     G_LOG_LEVEL_MESSAGE, _LOG_FORMAT_FLAG_LEVEL_ERROR },
128                 [_LOGL_OFF]  = { "OFF",   NULL,      0,           0,                   0 },
129                 [_LOGL_KEEP] = { "KEEP",  NULL,      0,           0,                   0 },
130         },
131         .domain_desc = {
132                 { LOGD_PLATFORM,  "PLATFORM" },
133                 { LOGD_RFKILL,    "RFKILL" },
134                 { LOGD_ETHER,     "ETHER" },
135                 { LOGD_WIFI,      "WIFI" },
136                 { LOGD_BT,        "BT" },
137                 { LOGD_MB,        "MB" },
138                 { LOGD_DHCP4,     "DHCP4" },
139                 { LOGD_DHCP6,     "DHCP6" },
140                 { LOGD_PPP,       "PPP" },
141                 { LOGD_WIFI_SCAN, "WIFI_SCAN" },
142                 { LOGD_IP4,       "IP4" },
143                 { LOGD_IP6,       "IP6" },
144                 { LOGD_AUTOIP4,   "AUTOIP4" },
145                 { LOGD_DNS,       "DNS" },
146                 { LOGD_VPN,       "VPN" },
147                 { LOGD_SHARING,   "SHARING" },
148                 { LOGD_SUPPLICANT,"SUPPLICANT" },
149                 { LOGD_AGENTS,    "AGENTS" },
150                 { LOGD_SETTINGS,  "SETTINGS" },
151                 { LOGD_SUSPEND,   "SUSPEND" },
152                 { LOGD_CORE,      "CORE" },
153                 { LOGD_DEVICE,    "DEVICE" },
154                 { LOGD_OLPC,      "OLPC" },
155                 { LOGD_INFINIBAND,"INFINIBAND" },
156                 { LOGD_FIREWALL,  "FIREWALL" },
157                 { LOGD_ADSL,      "ADSL" },
158                 { LOGD_BOND,      "BOND" },
159                 { LOGD_VLAN,      "VLAN" },
160                 { LOGD_BRIDGE,    "BRIDGE" },
161                 { LOGD_DBUS_PROPS,"DBUS_PROPS" },
162                 { LOGD_TEAM,      "TEAM" },
163                 { LOGD_CONCHECK,  "CONCHECK" },
164                 { LOGD_DCB,       "DCB" },
165                 { LOGD_DISPATCH,  "DISPATCH" },
166                 { LOGD_AUDIT,     "AUDIT" },
167                 { LOGD_SYSTEMD,   "SYSTEMD" },
168                 { 0, NULL }
169                 /* keep _DOMAIN_DESC_LEN in sync */
170         },
171 };
172
173 /* We have more then 32 logging domains. Assert that it compiles to a 64 bit sized enum */
174 G_STATIC_ASSERT (sizeof (NMLogDomain) >= sizeof (guint64));
175
176 /* Combined domains */
177 #define LOGD_ALL_STRING     "ALL"
178 #define LOGD_DEFAULT_STRING "DEFAULT"
179 #define LOGD_DHCP_STRING    "DHCP"
180 #define LOGD_IP_STRING      "IP"
181
182 /************************************************************************/
183
184 static char *_domains_to_string (gboolean include_level_override);
185
186 /************************************************************************/
187
188 static void
189 _ensure_initialized (void)
190 {
191         if (G_UNLIKELY (!global.logging_set_up)) {
192                 int errsv = errno;
193
194                 nm_logging_setup ("INFO", LOGD_DEFAULT_STRING, NULL, NULL);
195
196                 /* must ensure that errno is not modified. */
197                 errno = errsv;
198         }
199 }
200
201 static gboolean
202 match_log_level (const char  *level,
203                  NMLogLevel  *out_level,
204                  GError     **error)
205 {
206         int i;
207
208         for (i = 0; i < G_N_ELEMENTS (global.level_desc); i++) {
209                 if (!g_ascii_strcasecmp (global.level_desc[i].name, level)) {
210                         *out_level = i;
211                         return TRUE;
212                 }
213         }
214
215         g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL,
216                      _("Unknown log level '%s'"), level);
217         return FALSE;
218 }
219
220 gboolean
221 nm_logging_setup (const char  *level,
222                   const char  *domains,
223                   char       **bad_domains,
224                   GError     **error)
225 {
226         GString *unrecognized = NULL;
227         NMLogDomain new_logging[G_N_ELEMENTS (global.logging)];
228         NMLogLevel new_log_level = global.log_level;
229         char **tmp, **iter;
230         int i;
231         gboolean had_platform_debug;
232         gs_free char *domains_free = NULL;
233
234         g_return_val_if_fail (!bad_domains || !*bad_domains, FALSE);
235         g_return_val_if_fail (!error || !*error, FALSE);
236
237         /* domains */
238         if (!domains || !*domains) {
239                 domains = global.logging_set_up
240                           ? (domains_free = _domains_to_string (FALSE))
241                           : LOGD_DEFAULT_STRING;
242         }
243
244         global.logging_set_up = TRUE;
245
246         for (i = 0; i < G_N_ELEMENTS (new_logging); i++)
247                 new_logging[i] = 0;
248
249         /* levels */
250         if (level && *level) {
251                 if (!match_log_level (level, &new_log_level, error))
252                         return FALSE;
253                 if (new_log_level == _LOGL_KEEP) {
254                         new_log_level = global.log_level;
255                         for (i = 0; i < G_N_ELEMENTS (new_logging); i++)
256                                 new_logging[i] = global.logging[i];
257                 }
258         }
259
260         tmp = g_strsplit_set (domains, ", ", 0);
261         for (iter = tmp; iter && *iter; iter++) {
262                 const LogDesc *diter;
263                 NMLogLevel domain_log_level;
264                 NMLogDomain bits;
265                 char *p;
266
267                 if (!strlen (*iter))
268                         continue;
269
270                 p = strchr (*iter, ':');
271                 if (p) {
272                         *p = '\0';
273                         if (!match_log_level (p + 1, &domain_log_level, error)) {
274                                 g_strfreev (tmp);
275                                 return FALSE;
276                         }
277                 } else
278                         domain_log_level = new_log_level;
279
280                 bits = 0;
281
282                 /* Check for combined domains */
283                 if (!g_ascii_strcasecmp (*iter, LOGD_ALL_STRING))
284                         bits = LOGD_ALL;
285                 else if (!g_ascii_strcasecmp (*iter, LOGD_DEFAULT_STRING))
286                         bits = LOGD_DEFAULT;
287                 else if (!g_ascii_strcasecmp (*iter, LOGD_DHCP_STRING))
288                         bits = LOGD_DHCP;
289                 else if (!g_ascii_strcasecmp (*iter, LOGD_IP_STRING))
290                         bits = LOGD_IP;
291
292                 /* Check for compatibility domains */
293                 else if (!g_ascii_strcasecmp (*iter, "HW"))
294                         bits = LOGD_PLATFORM;
295                 else if (!g_ascii_strcasecmp (*iter, "WIMAX"))
296                         continue;
297
298                 else {
299                         for (diter = &global.domain_desc[0]; diter->name; diter++) {
300                                 if (!g_ascii_strcasecmp (diter->name, *iter)) {
301                                         bits = diter->num;
302                                         break;
303                                 }
304                         }
305
306                         if (!bits) {
307                                 if (!bad_domains) {
308                                         g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN,
309                                                      _("Unknown log domain '%s'"), *iter);
310                                         return FALSE;
311                                 }
312
313                                 if (unrecognized)
314                                         g_string_append (unrecognized, ", ");
315                                 else
316                                         unrecognized = g_string_new (NULL);
317                                 g_string_append (unrecognized, *iter);
318                                 continue;
319                         }
320                 }
321
322                 if (domain_log_level == _LOGL_KEEP) {
323                         for (i = 0; i < G_N_ELEMENTS (new_logging); i++)
324                                 new_logging[i] = (new_logging[i] & ~bits) | (global.logging[i] & bits);
325                 } else {
326                         for (i = 0; i < G_N_ELEMENTS (new_logging); i++) {
327                                 if (i < domain_log_level)
328                                         new_logging[i] &= ~bits;
329                                 else
330                                         new_logging[i] |= bits;
331                         }
332                 }
333         }
334         g_strfreev (tmp);
335
336         g_clear_pointer (&global.logging_domains_to_string, g_free);
337
338         had_platform_debug = nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM);
339
340         global.log_level = new_log_level;
341         for (i = 0; i < G_N_ELEMENTS (new_logging); i++)
342                 global.logging[i] = new_logging[i];
343
344         if (   had_platform_debug
345             && _nm_logging_clear_platform_logging_cache
346             && !nm_logging_enabled (LOGL_DEBUG, LOGD_PLATFORM)) {
347                 /* when debug logging is enabled, platform will cache all access to
348                  * sysctl. When the user disables debug-logging, we want to clear that
349                  * cache right away. */
350                 _nm_logging_clear_platform_logging_cache ();
351         }
352
353         if (unrecognized)
354                 *bad_domains = g_string_free (unrecognized, FALSE);
355
356         return TRUE;
357 }
358
359 const char *
360 nm_logging_level_to_string (void)
361 {
362         return global.level_desc[global.log_level].name;
363 }
364
365 const char *
366 nm_logging_all_levels_to_string (void)
367 {
368         static GString *str;
369
370         if (G_UNLIKELY (!str)) {
371                 int i;
372
373                 str = g_string_new (NULL);
374                 for (i = 0; i < G_N_ELEMENTS (global.level_desc); i++) {
375                         if (str->len)
376                                 g_string_append_c (str, ',');
377                         g_string_append (str, global.level_desc[i].name);
378                 }
379         }
380
381         return str->str;
382 }
383
384 const char *
385 nm_logging_domains_to_string (void)
386 {
387         _ensure_initialized ();
388
389         if (G_UNLIKELY (!global.logging_domains_to_string))
390                 global.logging_domains_to_string = _domains_to_string (TRUE);
391
392         return global.logging_domains_to_string;
393 }
394
395 static char *
396 _domains_to_string (gboolean include_level_override)
397 {
398         const LogDesc *diter;
399         GString *str;
400         int i;
401
402         /* We don't just return g_strdup (global.log_domains) because we want to expand
403          * "DEFAULT" and "ALL".
404          */
405
406         str = g_string_sized_new (75);
407         for (diter = &global.domain_desc[0]; diter->name; diter++) {
408                 /* If it's set for any lower level, it will also be set for LOGL_ERR */
409                 if (!(diter->num & global.logging[LOGL_ERR]))
410                         continue;
411
412                 if (str->len)
413                         g_string_append_c (str, ',');
414                 g_string_append (str, diter->name);
415
416                 if (!include_level_override)
417                         continue;
418
419                 /* Check if it's logging at a lower level than the default. */
420                 for (i = 0; i < global.log_level; i++) {
421                         if (diter->num & global.logging[i]) {
422                                 g_string_append_printf (str, ":%s", global.level_desc[i].name);
423                                 break;
424                         }
425                 }
426                 /* Check if it's logging at a higher level than the default. */
427                 if (!(diter->num & global.logging[global.log_level])) {
428                         for (i = global.log_level + 1; i < G_N_ELEMENTS (global.logging); i++) {
429                                 if (diter->num & global.logging[i]) {
430                                         g_string_append_printf (str, ":%s", global.level_desc[i].name);
431                                         break;
432                                 }
433                         }
434                 }
435         }
436         return g_string_free (str, FALSE);
437 }
438
439 const char *
440 nm_logging_all_domains_to_string (void)
441 {
442         static GString *str;
443
444         if (G_UNLIKELY (!str)) {
445                 const LogDesc *diter;
446
447                 str = g_string_new (LOGD_DEFAULT_STRING);
448                 for (diter = &global.domain_desc[0]; diter->name; diter++) {
449                         g_string_append_c (str, ',');
450                         g_string_append (str, diter->name);
451                         if (diter->num == LOGD_DHCP6)
452                                 g_string_append (str, "," LOGD_DHCP_STRING);
453                         else if (diter->num == LOGD_IP6)
454                                 g_string_append (str, "," LOGD_IP_STRING);
455                 }
456                 g_string_append (str, "," LOGD_ALL_STRING);
457         }
458
459         return str->str;
460 }
461
462 gboolean
463 nm_logging_enabled (NMLogLevel level, NMLogDomain domain)
464 {
465         if ((guint) level >= G_N_ELEMENTS (global.logging))
466                 g_return_val_if_reached (FALSE);
467
468         /* This function is guaranteed not to modify errno. */
469         _ensure_initialized ();
470
471         return !!(global.logging[level] & domain);
472 }
473
474 #if SYSTEMD_JOURNAL
475 __attribute__((__format__ (__printf__, 4, 5)))
476 static void
477 _iovec_set_format (struct iovec *iov, gboolean *iov_free, int i, const char *format, ...)
478 {
479         va_list ap;
480         char *str;
481
482         va_start (ap, format);
483         str = g_strdup_vprintf (format, ap);
484         va_end (ap);
485
486         iov[i].iov_base = str;
487         iov[i].iov_len = strlen (str);
488         iov_free[i] = TRUE;
489 }
490
491 static void
492 _iovec_set_string (struct iovec *iov, gboolean *iov_free, int i, const char *str, gsize len)
493 {
494         iov[i].iov_base = (char *) str;
495         iov[i].iov_len = len;
496         iov_free[i] = FALSE;
497 }
498 #define _iovec_set_literal_string(iov, iov_free, i, str) _iovec_set_string ((iov), (iov_free), (i), (""str""), NM_STRLEN (str))
499 #endif
500
501 void
502 _nm_log_impl (const char *file,
503               guint line,
504               const char *func,
505               NMLogLevel level,
506               NMLogDomain domain,
507               int error,
508               const char *fmt,
509               ...)
510 {
511         va_list args;
512         char *msg;
513         char *fullmsg;
514         char s_buf_timestamp[64];
515         char s_buf_location[1024];
516         GTimeVal tv;
517
518         if ((guint) level >= G_N_ELEMENTS (global.logging))
519                 g_return_if_reached ();
520
521         _ensure_initialized ();
522
523         if (!(global.logging[level] & domain))
524                 return;
525
526         /* Make sure that %m maps to the specified error */
527         if (error != 0) {
528                 if (error < 0)
529                         error = -error;
530                 errno = error;
531         }
532
533         va_start (args, fmt);
534         msg = g_strdup_vprintf (fmt, args);
535         va_end (args);
536
537         if (NM_FLAGS_ANY (global.log_format_flags, global.level_desc[level].log_format_level & _LOG_FORMAT_FLAG_TIMESTAMP)) {
538                 g_get_current_time (&tv);
539                 nm_sprintf_buf (s_buf_timestamp, " [%ld.%04ld]", tv.tv_sec, (tv.tv_usec + 50) / 100);
540         } else
541                 s_buf_timestamp[0] = '\0';
542
543         s_buf_location[0] = '\0';
544         if (NM_FLAGS_ANY (global.log_format_flags, global.level_desc[level].log_format_level & _LOG_FORMAT_FLAG_LOCATION)) {
545 #define MAX_LEN_FILE 37
546 #define MAX_LEN_FUNC 26
547                 gsize l = sizeof (s_buf_location);
548                 char *p = s_buf_location, *p_buf;
549                 gsize len;
550                 char s_buf[MAX (MAX_LEN_FILE, MAX_LEN_FUNC) + 30];
551
552                 if (file) {
553                         if (NM_FLAGS_HAS (global.log_format_flags, LOG_FORMAT_FLAG_ALIGN_LOCATION)) {
554                                 /* left-align the "[file:line]" string, but truncate from left to MAX_LEN_FILE chars. */
555                                 len = strlen (file);
556                                 nm_sprintf_buf (s_buf, "[%s:%u]",
557                                                 len > MAX_LEN_FILE ? &file[len - MAX_LEN_FILE] : file,
558                                                 line);
559                                 len = strlen (s_buf);
560                                 if (len > MAX_LEN_FILE) {
561                                         p_buf = &s_buf[len - MAX_LEN_FILE];
562                                         p_buf[0] = '[';
563                                 } else
564                                         p_buf = s_buf;
565                                 nm_utils_strbuf_append (&p, &l, " %-"G_STRINGIFY (MAX_LEN_FILE)"s", p_buf);
566                         } else
567                                 nm_utils_strbuf_append (&p, &l, " [%s:%u]", file, line);
568                 }
569                 if (func) {
570                         if (NM_FLAGS_HAS (global.log_format_flags, LOG_FORMAT_FLAG_ALIGN_LOCATION)) {
571                                 /* left-align the "func():" string, but truncate from left to MAX_LEN_FUNC chars. */
572                                 len = strlen (func);
573                                 nm_sprintf_buf (s_buf, "%s():",
574                                                 len > MAX_LEN_FUNC ? &func[len - MAX_LEN_FUNC] : func);
575                                 len = strlen (s_buf);
576                                 nm_utils_strbuf_append (&p, &l, " %-"G_STRINGIFY (MAX_LEN_FUNC)"s",
577                                                         len > MAX_LEN_FUNC ? &s_buf[len - MAX_LEN_FUNC] : s_buf);
578                         } else
579                                 nm_utils_strbuf_append (&p, &l, " %s():", func);
580                 }
581         }
582
583         switch (global.log_backend) {
584 #if SYSTEMD_JOURNAL
585         case LOG_BACKEND_JOURNAL:
586                 {
587                         gint64 now, boottime;
588 #define _NUM_MAX_FIELDS_SYSLOG_FACILITY 10
589 #define _NUM_FIELDS (10 + _NUM_MAX_FIELDS_SYSLOG_FACILITY)
590                         int i_field = 0;
591                         struct iovec iov[_NUM_FIELDS];
592                         gboolean iov_free[_NUM_FIELDS];
593
594                         now = nm_utils_get_monotonic_timestamp_ns ();
595                         boottime = nm_utils_monotonic_timestamp_as_boottime (now, 1);
596
597                         _iovec_set_format (iov, iov_free, i_field++, "PRIORITY=%d", global.level_desc[level].syslog_level);
598                         _iovec_set_format (iov, iov_free, i_field++, "MESSAGE="
599                                            "%-7s%s%s %s",
600                                            global.level_desc[level].level_str,
601                                            s_buf_timestamp,
602                                            s_buf_location,
603                                            msg);
604                         _iovec_set_literal_string (iov, iov_free, i_field++, "SYSLOG_IDENTIFIER=" G_LOG_DOMAIN);
605                         _iovec_set_format (iov, iov_free, i_field++, "SYSLOG_PID=%ld", (long) getpid ());
606                         {
607                                 const LogDesc *diter;
608                                 int i_domain = _NUM_MAX_FIELDS_SYSLOG_FACILITY;
609                                 const char *s_domain_1 = NULL;
610                                 GString *s_domain_all = NULL;
611                                 NMLogDomain dom_all = domain;
612                                 NMLogDomain dom = dom_all & global.logging[level];
613
614                                 for (diter = &global.domain_desc[0]; diter->name; diter++) {
615                                         if (!NM_FLAGS_HAS (dom_all, diter->num))
616                                                 continue;
617
618                                         /* construct a list of all domains (not only the enabled ones).
619                                          * Note that in by far most cases, there is only one domain present.
620                                          * Hence, save the construction of the GString. */
621                                         dom_all &= ~diter->num;
622                                         if (!s_domain_1)
623                                                 s_domain_1 = diter->name;
624                                         else {
625                                                 if (!s_domain_all)
626                                                         s_domain_all = g_string_new (s_domain_1);
627                                                 g_string_append_c (s_domain_all, ',');
628                                                 g_string_append (s_domain_all, diter->name);
629                                         }
630
631                                         if (NM_FLAGS_HAS (dom, diter->num)) {
632                                                 if (i_domain > 0) {
633                                                         /* SYSLOG_FACILITY is specified multiple times for each domain that is actually enabled. */
634                                                         _iovec_set_format (iov, iov_free, i_field++, "SYSLOG_FACILITY=%s", diter->name);
635                                                         i_domain--;
636                                                 }
637                                                 dom &= ~diter->num;
638                                         }
639                                         if (!dom && !dom_all)
640                                                 break;
641                                 }
642                                 if (s_domain_all) {
643                                         _iovec_set_format (iov, iov_free, i_field++, "NM_LOG_DOMAINS=%s", s_domain_all->str);
644                                         g_string_free (s_domain_all, TRUE);
645                                 } else
646                                         _iovec_set_format (iov, iov_free, i_field++, "NM_LOG_DOMAINS=%s", s_domain_1);
647                         }
648                         _iovec_set_format (iov, iov_free, i_field++, "NM_LOG_LEVEL=%s", global.level_desc[level].name);
649                         _iovec_set_format (iov, iov_free, i_field++, "CODE_FUNC=%s", func ?: "");
650                         _iovec_set_format (iov, iov_free, i_field++, "CODE_FILE=%s", file ?: "");
651                         _iovec_set_format (iov, iov_free, i_field++, "CODE_LINE=%u", line);
652                         _iovec_set_format (iov, iov_free, i_field++, "TIMESTAMP_MONOTONIC=%lld.%06lld", (long long) (now / NM_UTILS_NS_PER_SECOND), (long long) ((now % NM_UTILS_NS_PER_SECOND) / 1000));
653                         _iovec_set_format (iov, iov_free, i_field++, "TIMESTAMP_BOOTTIME=%lld.%06lld", (long long) (boottime / NM_UTILS_NS_PER_SECOND), (long long) ((boottime % NM_UTILS_NS_PER_SECOND) / 1000));
654                         if (error != 0)
655                                 _iovec_set_format (iov, iov_free, i_field++, "ERRNO=%d", error);
656
657                         nm_assert (i_field <= G_N_ELEMENTS (iov));
658
659                         sd_journal_sendv (iov, i_field);
660
661                         for (; i_field > 0; ) {
662                                 i_field--;
663                                 if (iov_free[i_field])
664                                         g_free (iov[i_field].iov_base);
665                         }
666                 }
667                 break;
668 #endif
669         default:
670                 fullmsg = g_strdup_printf ("%-7s%s%s %s",
671                                            global.level_desc[level].level_str,
672                                            s_buf_timestamp,
673                                            s_buf_location,
674                                            msg);
675
676                 if (global.log_backend == LOG_BACKEND_SYSLOG)
677                         syslog (global.level_desc[level].syslog_level, "%s", fullmsg);
678                 else
679                         g_log (G_LOG_DOMAIN, global.level_desc[level].g_log_level, "%s", fullmsg);
680                 g_free (fullmsg);
681                 break;
682         }
683
684         g_free (msg);
685 }
686
687 /************************************************************************/
688
689 static void
690 nm_log_handler (const gchar *log_domain,
691                 GLogLevelFlags level,
692                 const gchar *message,
693                 gpointer ignored)
694 {
695         int syslog_priority;
696
697         switch (level & G_LOG_LEVEL_MASK) {
698         case G_LOG_LEVEL_ERROR:
699                 syslog_priority = LOG_CRIT;
700                 break;
701         case G_LOG_LEVEL_CRITICAL:
702                 syslog_priority = LOG_ERR;
703                 break;
704         case G_LOG_LEVEL_WARNING:
705                 syslog_priority = LOG_WARNING;
706                 break;
707         case G_LOG_LEVEL_MESSAGE:
708                 syslog_priority = LOG_NOTICE;
709                 break;
710         case G_LOG_LEVEL_DEBUG:
711                 syslog_priority = LOG_DEBUG;
712                 break;
713         case G_LOG_LEVEL_INFO:
714         default:
715                 syslog_priority = LOG_INFO;
716                 break;
717         }
718
719         switch (global.log_backend) {
720 #if SYSTEMD_JOURNAL
721         case LOG_BACKEND_JOURNAL:
722                 {
723                         gint64 now, boottime;
724
725                         now = nm_utils_get_monotonic_timestamp_ns ();
726                         boottime = nm_utils_monotonic_timestamp_as_boottime (now, 1);
727
728                         sd_journal_send ("PRIORITY=%d", syslog_priority,
729                                          "MESSAGE=%s", message ?: "",
730                                          "SYSLOG_IDENTIFIER=%s", G_LOG_DOMAIN,
731                                          "SYSLOG_PID=%ld", (long) getpid (),
732                                          "SYSLOG_FACILITY=GLIB",
733                                          "GLIB_DOMAIN=%s", log_domain ?: "",
734                                          "GLIB_LEVEL=%d", (int) (level & G_LOG_LEVEL_MASK),
735                                          "TIMESTAMP_MONOTONIC=%lld.%06lld", (long long) (now / NM_UTILS_NS_PER_SECOND), (long long) ((now % NM_UTILS_NS_PER_SECOND) / 1000),
736                                          "TIMESTAMP_BOOTTIME=%lld.%06lld", (long long) (boottime / NM_UTILS_NS_PER_SECOND), (long long) ((boottime % NM_UTILS_NS_PER_SECOND) / 1000),
737                                          NULL);
738                 }
739                 break;
740 #endif
741         default:
742                 syslog (syslog_priority, "%s", message ?: "");
743                 break;
744         }
745 }
746
747 void
748 nm_logging_syslog_openlog (const char *logging_backend)
749 {
750         LogFormatFlags log_format_flags;
751
752         if (global.log_backend != LOG_BACKEND_GLIB)
753                 g_return_if_reached ();
754
755         if (!logging_backend)
756                 logging_backend = ""NM_CONFIG_LOGGING_BACKEND_DEFAULT;
757
758         log_format_flags = _LOG_FORMAT_FLAG_DEFAULT;
759
760         if (strcmp (logging_backend, "debug") == 0) {
761                 global.log_backend = LOG_BACKEND_SYSLOG;
762                 openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR | LOG_PID, LOG_USER);
763 #if SYSTEMD_JOURNAL
764         } else if (strcmp (logging_backend, "syslog") != 0) {
765                 global.log_backend = LOG_BACKEND_JOURNAL;
766
767                 /* ensure we read a monotonic timestamp. Reading the timestamp the first
768                  * time causes a logging message. We don't want to do that during _nm_log_impl. */
769                 nm_utils_get_monotonic_timestamp_ns ();
770 #endif
771         } else {
772                 global.log_backend = LOG_BACKEND_SYSLOG;
773                 openlog (G_LOG_DOMAIN, LOG_PID, LOG_DAEMON);
774         }
775
776         global.log_format_flags = log_format_flags;
777
778         g_log_set_handler (G_LOG_DOMAIN,
779                            G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
780                            nm_log_handler,
781                            NULL);
782 }
783