Coverage Report

Created: 2026-07-25 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dbus-broker/src/util/log.c
Line
Count
Source
1
/* SPDX-License-Identifier: GPL-3.0-or-later */
2
/* SPDX-FileCopyrightText: D-Bus Broker Developers */
3
4
/*
5
 * Log Context
6
 *
7
 * The log context provides an infrastructure to send log-messages to the
8
 * system log daemon. Both structured and unstructured logging is supported.
9
 * Depending on which logging daemon is used, a compatible mode is selected.
10
 *
11
 * Right now, these modes are supported:
12
 *
13
 *     * stderr: Stream-based, unstructured logging to the inherited stderr
14
 *               channel.
15
 *               This is the default.
16
 *
17
 *     * journal: Datagram-based, structured logging to the journal. Other
18
 *                journal-API-compatible daemons can be used as well. No
19
 *                journal-specific API besides the datagram socket is used.
20
 *
21
 * The logging API provides a staging buffer to assemble structured log
22
 * messages. Once a message is complete, it must be committed, which will send
23
 * it out and prepare the context for the next message.
24
 *
25
 * The log_*append*() APIs append log fields to the staging buffer. They can
26
 * be used consecutively. Convenience wrappers are provided to add the most
27
 * common structured fields. The staging buffer should only be used for
28
 * structured logging. The main log-message is not part of it.
29
 *
30
 * Once a log message is completed, the log_*commit*() APIs commit the log
31
 * message. At the time of commit, the caller must provide the actual main log
32
 * message, which appears in the logs. Multiple convenience wrappers are
33
 * provided to allow formatted messages.
34
 * Note that depending on the backend, the structured fields might be
35
 * discarded. Not all logging systems support them.
36
 *
37
 * By default, logging is synchronous and blocking. That is, no data is
38
 * buffered in the log-context once a message is committed. All data is written
39
 * into the socket in a blocking manner. The data will be buffered in the
40
 * kernel socket queues, of course.
41
 * This mode is not always desirable. In particular, we allow logging on behalf
42
 * of peers in dbus-broker. This means, whenever a peer screws up, we often
43
 * have more information than the peer itself, so we want to log data on behalf
44
 * of them. This, however, makes us susceptible to DoS attacks. Hence, we
45
 * support a lossy mode. If a log context is set to lossy mode, messages will
46
 * be submitted to the socket in non-blocking mode. Once the kernel buffers run
47
 * full, we submit a single, one-time, synchronous warning to the log, and from
48
 * then on will discard log messages, if the kernel buffers are full.
49
 *
50
 * Lastly, please note that each log context must not be used from multiple
51
 * threads. Use separate contexts for each thread. Also be aware that stream
52
 * sockets do not have atomic writes, so you might need separate sockets.
53
 *
54
 * In case of structured logging, we support:
55
 *
56
 *     * MESSAGE: The string-formatted log message.
57
 *
58
 *     * CODE_FILE: Source-code file where the logging call originated. Usually
59
 *                  corresponds to the expanded __FILE__ constant.
60
 *
61
 *     * CODE_LINE: Source-code line number where the logging call originated.
62
 *                  Usually corresponds to the expanded __LINE__ constant.
63
 *
64
 *     * CODE_FUNC: Source-code function where the logging call originated.
65
 *                  Usually correspoends to the expanded __func__ constant.
66
 *
67
 *     * PRIORITY: Syslog-compatible log priority formatted as integer. See
68
 *                 syslog.h for details on LOG_EMERG..LOG_DEBUG.
69
 *
70
 *     * SYSLOG_FACILITY: Syslog facility formatted as integer.
71
 *
72
 *     * SYSLOG_IDENTIFIER: Name of the program where the log message
73
 *                          originated.
74
 *
75
 *     * ERRNO: Errno-style error code that caused the log-message (or 0 if
76
 *              none).
77
 *
78
 *     * DBUS_BROKER_LOG_DROPPED: Number of total log messages that were
79
 *                                dropped so far due to excessive logging.
80
 */
81
82
#include <c-stdaux.h>
83
#include <linux/sockios.h>
84
#include <stdlib.h>
85
#include <sys/ioctl.h>
86
#include <sys/mman.h>
87
#include <sys/socket.h>
88
#include <sys/syslog.h>
89
#include "util/error.h"
90
#include "util/log.h"
91
#include "util/misc.h"
92
93
/* lets retrict log records to 2MiB */
94
0
#define LOG_SIZE_MAX (2ULL * 1024ULL * 1024ULL)
95
96
/* warning that is sent on first dropped log message */
97
0
#define LOG_WARNING_DROPPED "<3>Log messages dropped\n"
98
99
/**
100
 * log_init() - initialize log context
101
 * @log:                log context to initialize
102
 *
103
 * This initializes the log-context @log with no output configured. Hence, all
104
 * log messages will be silently dropped.
105
 */
106
0
void log_init(Log *log) {
107
0
        *log = (Log)LOG_NULL;
108
0
        log->mode = LOG_MODE_NONE;
109
0
        log->consumed = false;
110
0
        log->map_size = LOG_SIZE_MAX;
111
        /* NOTE: Other log_init_*() variants override these. */
112
0
}
113
114
/**
115
 * log_init_stderr() - initialize log context
116
 * @log:                log context to initialize
117
 * @stderr_fd:          stderr stream FD to use
118
 *
119
 * This initializes the log-context @log and prepares it for output to stderr
120
 * given its file-descriptor as @stderr_fd. Note that the file-descriptor is
121
 * not consumed, but ownership is retained by the caller.
122
 */
123
0
void log_init_stderr(Log *log, int stderr_fd) {
124
0
        c_assert(stderr_fd >= 0);
125
126
0
        log_init(log);
127
0
        log->log_fd = stderr_fd;
128
0
        log->mode = LOG_MODE_STDERR;
129
0
        log->consumed = false;
130
0
}
131
132
/**
133
 * log_init_journal() - initialize log context
134
 * @log:                log context to initialize
135
 * @journal_fd:         Datagram-FD to the journal
136
 *
137
 * This initializes the log-context @log with the journal datagram
138
 * socket @journal_fd to be used for log-messages.
139
 */
140
0
void log_init_journal(Log *log, int journal_fd) {
141
0
        c_assert(journal_fd >= 0);
142
143
0
        log_init(log);
144
0
        log->log_fd = journal_fd;
145
0
        log->mode = LOG_MODE_JOURNAL;
146
0
        log->consumed = false;
147
0
}
148
149
/**
150
 * log_init_journal_consume() - initialize log context
151
 * @log:                log context to initialize
152
 * @journal_fd:         Datagram-FD to the journal
153
 *
154
 * This initializes the log-context @log and consumes the journal datagram
155
 * socket @journal_fd to be used for log-messages.
156
 */
157
0
void log_init_journal_consume(Log *log, int journal_fd) {
158
0
        c_assert(journal_fd >= 0);
159
160
0
        log_init(log);
161
0
        log->log_fd = journal_fd;
162
0
        log->mode = LOG_MODE_JOURNAL;
163
0
        log->consumed = true;
164
0
}
165
166
/**
167
 * log_deinit() - deinitialize log context
168
 * @log:                log to operate on
169
 *
170
 * This deinitializes the log context @log and releases all resources. The
171
 * context is reset to LOG_NULL afterwards.
172
 *
173
 * Calling this on LOG_NULL is a no-op.
174
 */
175
0
void log_deinit(Log *log) {
176
0
        if (log->map != MAP_FAILED)
177
0
                munmap(log->map, log->map_size);
178
0
        c_close(log->mem_fd);
179
0
        if (log->consumed)
180
0
                c_close(log->log_fd);
181
0
        *log = (Log)LOG_NULL;
182
0
}
183
184
/**
185
 * log_get_fd() - get logging fd
186
 * @log:                log context to operate on
187
 *
188
 * This returns the file-descriptor used to submit log messages to. This is
189
 * usually the same FD that was passed to the constructor. Note that the FD is
190
 * still owned by the log context, so it must be treated as read-only.
191
 *
192
 * Return: Log file-descriptor in use, or -1 if none.
193
 */
194
0
int log_get_fd(Log *log) {
195
0
        return log->log_fd;
196
0
}
197
198
/**
199
 * log_set_lossy() - set lossy mode
200
 * @log:                log context to operate on
201
 * @lossy:              lossy mode to set
202
 *
203
 * This changes the lossy-mode of the log context @log. If @lossy is false, all
204
 * log messages will be sent in blocking mode to the logging daemon, and
205
 * transmission will be reliable.
206
 *
207
 * If @lossy is true, logging will be lossy. This means, any message submitted
208
 * to the log context might be dropped, with the advantage of logging being
209
 * safe even in quotad contexts.
210
 *
211
 * Default is non-lossy mode.
212
 */
213
0
void log_set_lossy(Log *log, bool lossy) {
214
0
        log->lossy = lossy;
215
0
}
216
217
0
static bool log_alloc(Log *log) {
218
0
        _c_cleanup_(c_closep) int mem_fd = -1;
219
0
        void *p;
220
0
        int r;
221
222
0
        if (log->error || log->truncated)
223
0
                return false;
224
0
        if (log->map != MAP_FAILED)
225
0
                return true;
226
227
0
        c_assert(!log->offset);
228
229
        /*
230
         * systemd-journald used to verify seals explicitly, and any new seal
231
         * showing up was refused. Hence, we cannot set MFD_NOEXEC but have to
232
         * use MFD_EXEC.
233
         */
234
0
        mem_fd = misc_memfd(
235
0
                "dbus-broker-log",
236
0
                MISC_MFD_CLOEXEC | MISC_MFD_ALLOW_SEALING | MISC_MFD_EXEC,
237
0
                0
238
0
        );
239
0
        if (mem_fd < 0) {
240
                /*
241
                 * In case of EINVAL, memfd_create() is not available. We then
242
                 * use normal anonymous memory as backing.
243
                 */
244
0
                if (mem_fd != -EINVAL) {
245
0
                        log->error = error_fold(mem_fd);
246
0
                        return false;
247
0
                }
248
0
                mem_fd = -1;
249
250
0
                p = mmap(NULL, log->map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
251
0
                if (p == MAP_FAILED) {
252
0
                        log->error = error_origin(-errno);
253
0
                        return false;
254
0
                }
255
0
        } else {
256
0
                r = ftruncate(mem_fd, log->map_size);
257
0
                if (r < 0) {
258
0
                        log->error = error_origin(-errno);
259
0
                        return false;
260
0
                }
261
262
0
                p = mmap(NULL, log->map_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0);
263
0
                if (p == MAP_FAILED) {
264
0
                        log->error = error_origin(-errno);
265
0
                        return false;
266
0
                }
267
0
        }
268
269
0
        log->mem_fd = mem_fd;
270
0
        log->map = p;
271
0
        mem_fd = -1;
272
0
        return true;
273
0
}
274
275
0
static int log_fd_send(int destination_fd, int payload_fd) {
276
0
        union {
277
0
                char buffer[CMSG_SPACE(sizeof(int))];
278
0
                struct cmsghdr cmsg;
279
0
        } control;
280
0
        struct msghdr msg;
281
0
        ssize_t l;
282
283
0
        control.cmsg.cmsg_len = CMSG_LEN(sizeof(int));
284
0
        control.cmsg.cmsg_level = SOL_SOCKET;
285
0
        control.cmsg.cmsg_type = SCM_RIGHTS;
286
0
        c_memcpy(CMSG_DATA(&control.cmsg), &payload_fd, sizeof(int));
287
288
0
        msg = (struct msghdr){
289
0
                .msg_control = &control.cmsg,
290
0
                .msg_controllen = control.cmsg.cmsg_len,
291
0
        };
292
293
0
        l = sendmsg(destination_fd, &msg, MSG_NOSIGNAL);
294
0
        if (l)
295
0
                return (l < 0) ? error_origin(-errno) : error_origin(-ENOTRECOVERABLE);
296
297
0
        return 0;
298
0
}
299
300
0
static int log_loop_send(int destination_fd, const void *blob, size_t n_blob) {
301
0
        ssize_t l;
302
303
0
        while (n_blob > 0) {
304
0
                l = send(destination_fd, blob, n_blob, MSG_NOSIGNAL);
305
0
                if (l < 0)
306
0
                        return error_origin(-errno);
307
0
                else if (l == 0 || l > (ssize_t)n_blob)
308
0
                        return error_origin(-ENOTRECOVERABLE);
309
310
0
                blob += l;
311
0
                n_blob -= l;
312
0
        }
313
314
0
        return 0;
315
0
}
316
317
0
static int log_stream_send(Log *log) {
318
0
        bool log_warn = false;
319
0
        const void *blob = log->map;
320
0
        size_t n_blob = log->offset;
321
0
        ssize_t l;
322
0
        int r, v;
323
324
        /*
325
         * Stream sockets are a bit nasty since they lack atomic writes. Hence,
326
         * whenever we end up with a short-write in lossy mode, we need to
327
         * finish the current message in synchronous mode, and then log a
328
         * warning. Otherwise, we end up with a garbled output.
329
         *
330
         * Now, we remember whenever we dropped message. From then on, we only
331
         * ever write messages if the output buffer of our socket is empty.
332
         * Preferably, we would check that the message fits into the kernel
333
         * buffer, but there is no way to do that. Hence, we simply require the
334
         * buffer to be empty.
335
         * This guarantees the operation will be non-blocking, as long as
336
         * suitably sized log messages are used.
337
         */
338
339
0
        if (log->truncated) {
340
0
                log_warn = true;
341
0
                goto out;
342
0
        }
343
344
0
        if (log->lossy) {
345
0
                if (log->n_dropped) {
346
0
                        r = ioctl(log->log_fd, SIOCOUTQ, &v);
347
0
                        if (r < 0) {
348
0
                                return error_origin(-errno);
349
0
                        } else if (v) {
350
0
                                ++log->n_dropped;
351
0
                                return 0;
352
0
                        }
353
0
                }
354
355
0
                l = send(log->log_fd, blob, n_blob, MSG_NOSIGNAL | MSG_DONTWAIT);
356
0
                if (l >= (ssize_t)n_blob) {
357
0
                        return 0;
358
0
                } else if (l < 0) {
359
0
                        if (errno != EAGAIN)
360
0
                                return error_origin(-errno);
361
362
0
                        l = 0;
363
0
                }
364
365
0
                blob += l;
366
0
                n_blob -= l;
367
368
0
                log_warn = true;
369
0
        }
370
371
0
        r = log_loop_send(log->log_fd, blob, n_blob);
372
0
        if (r)
373
0
                return error_trace(r);
374
375
0
out:
376
0
        if (log_warn && !log->n_dropped++) {
377
0
                r = log_loop_send(log->log_fd,
378
0
                                  LOG_WARNING_DROPPED,
379
0
                                  strlen(LOG_WARNING_DROPPED));
380
0
                if (r)
381
0
                        return error_trace(r);
382
0
        }
383
384
0
        return 0;
385
0
}
386
387
0
static int log_journal_send(Log *log) {
388
0
        _c_cleanup_(c_closep) int mfd = -1;
389
0
        ssize_t l;
390
0
        int r;
391
392
0
        if (log->truncated)
393
0
                goto out_drop;
394
395
0
        l = send(log->log_fd,
396
0
                 log->map,
397
0
                 log->offset,
398
0
                 log->lossy ?
399
0
                         MSG_NOSIGNAL | MSG_DONTWAIT :
400
0
                         MSG_NOSIGNAL);
401
0
        if (l == (ssize_t)log->offset) {
402
0
                return 0;
403
0
        } else if (l >= 0) {
404
                /*
405
                 * Partially sent? This should not happen. We require datagram
406
                 * sockets that send atomically and never truncate unasked.
407
                 * Tell our caller about this, so they can deal with it.
408
                 */
409
0
                return LOG_E_TRUNCATED;
410
0
        } else if (errno == EAGAIN) {
411
0
                goto out_drop;
412
0
        } else if (errno != EMSGSIZE) {
413
0
                return error_origin(-errno);
414
0
        }
415
416
        /*
417
         * We could not send the message as a single datagram. Instead, we now
418
         * seal the memfd and send it as payload of an empty datagram. The
419
         * journal can deal with this and treats the memfd content as log
420
         * message.
421
         *
422
         * Note that this means there is an inflight memfd pending on the
423
         * journal that is accounted on us as long as the journal did not read
424
         * it, yet. Hence, clients should never be able to trigger such log
425
         * messages, otherwise they could exploit this and make us exceed our
426
         * inflight FD limit.
427
         *
428
         * We could send a pipe-fd as barrier and block on it. Hence, we would
429
         * be able to know at which point our messages are no longer inflight.
430
         * However, this would be a quite expensive code-path, making this
431
         * entire path completely useless.
432
         *
433
         * Long story short: Do not write excessive log messages, unless
434
         *                   debugging is enabled in some way.
435
         */
436
437
0
        if (log->lossy || log->mem_fd < 0)
438
0
                goto out_drop;
439
440
0
        mfd = log->mem_fd;
441
0
        log->mem_fd = -1;
442
0
        munmap(log->map, log->map_size);
443
0
        log->map = MAP_FAILED;
444
445
0
        r = ftruncate(mfd, log->offset);
446
0
        if (r < 0)
447
0
                return error_origin(-errno);
448
449
0
        r = misc_memfd_add_seals(
450
0
                mfd,
451
0
                MISC_F_SEAL_SEAL | MISC_F_SEAL_SHRINK |
452
0
                MISC_F_SEAL_GROW | MISC_F_SEAL_WRITE
453
0
        );
454
0
        if (r < 0)
455
0
                return error_origin(-errno);
456
457
0
        r = log_fd_send(log->log_fd, mfd);
458
0
        if (r < 0)
459
0
                return error_trace(r);
460
461
0
        return 0;
462
463
0
out_drop:
464
0
        if (!log->n_dropped++) {
465
0
                l = send(log->log_fd,
466
0
                         LOG_WARNING_DROPPED,
467
0
                         strlen(LOG_WARNING_DROPPED),
468
0
                         MSG_NOSIGNAL);
469
0
                if (l < 0)
470
0
                        return error_origin(-errno);
471
0
        }
472
473
0
        return 0;
474
0
}
475
476
0
static int log_commit_stderr(Log *log, const char *format, va_list args) {
477
0
        int r;
478
479
        /*
480
         * Lets format the log-message in our staging buffer and print it out.
481
         * Note that we don't support structured logging. Hence, we simply
482
         * ignore anything that is in the staging buffer and instead just print
483
         * our message.
484
         * Sadly, `%r' is still no standardized format specifier, so we need
485
         * the separate calls into log_append() here.
486
         */
487
488
0
        log->offset = 0;
489
490
0
        if (format && *format) {
491
0
                log_appendf(log, "<%d>", log->level ?: LOG_INFO);
492
0
                log_vappendf(log, format, args);
493
0
                log_appends(log, "\n");
494
0
        }
495
496
0
        if (log->error)
497
0
                r = error_trace(log->error);
498
0
        else if (log->offset)
499
0
                r = log_stream_send(log);
500
0
        else
501
0
                r = 0;
502
503
0
        return error_trace(r);
504
0
}
505
506
0
static int log_commit_journal(Log *log, const char *format, va_list args) {
507
0
        int r;
508
509
        /*
510
         * Lets append `MESSAGE=%s\n' to the staging buffer and then submit the
511
         * entire blob to the journal. We support passing it as sealed memfd in
512
         * case it exceeds the datagram maximum.
513
         * Sadly, `%r' is still no standardized format specifier, so we need
514
         * the separate calls into log_append() here.
515
         */
516
517
0
        if (format && *format) {
518
0
                log_appends(log, "MESSAGE=");
519
0
                log_vappendf(log, format, args);
520
0
                log_appends(log, "\n");
521
0
        }
522
523
0
        if (log->error)
524
0
                r = error_trace(log->error);
525
0
        else if (log->offset)
526
0
                r = log_journal_send(log);
527
0
        else
528
0
                r = 0;
529
530
0
        return error_trace(r);
531
0
}
532
533
/**
534
 * log_vcommitf() - commit log message
535
 * @log:                log to operate on
536
 * @format:             log message format string
537
 * @args:               arguments for format string
538
 *
539
 * This formats a log message and commits it. @format is used as format string
540
 * for the log message, with @args filled in at the respective places.
541
 *
542
 * Any previously appended log fields are amended to the log message and
543
 * submitted with it. In case the log output does not support structured
544
 * logging, they will be lost, and only the message itself is submitted.
545
 *
546
 * If the pending log message is poisoned, or if it could not be submitted,
547
 * an error will be returned. After this call returns (regardless whether it
548
 * failed or not), the log context is ready to take the next log message.
549
 *
550
 * Return: 0 on success, LOG_E_TRUNCATED if the message was truncated by the
551
 *         log channel, LOG_E_OVERSIZED if the message exceeded the maximum
552
 *         size, and negative error code on failure.
553
 */
554
0
int log_vcommitf(Log *log, const char *format, va_list args) {
555
0
        int r;
556
557
0
        switch (log->mode) {
558
0
        case LOG_MODE_NONE:
559
0
                r = error_trace(log->error);
560
0
                break;
561
0
        case LOG_MODE_STDERR:
562
0
                r = log_commit_stderr(log, format, args);
563
0
                break;
564
0
        case LOG_MODE_JOURNAL:
565
0
                r = log_commit_journal(log, format, args);
566
0
                break;
567
0
        default:
568
0
                r = error_origin(-ENOTRECOVERABLE);
569
0
                break;
570
0
        }
571
572
0
        log->truncated = 0;
573
0
        log->error = 0;
574
0
        log->level = 0;
575
0
        log->offset = 0;
576
577
0
        return r;
578
0
}
579
580
/**
581
 * log_append() - append structured fields
582
 * @log:                log context to operate on
583
 * @data:               data to append
584
 * @n_data:             length of data to append
585
 *
586
 * This appends the given data to the structured log staging buffer. Note that
587
 * you can encode binary data, as long as you follow the protocol of journald.
588
 * But you're recommended to just insert newline separated ascii key-value
589
 * pairs.
590
 */
591
0
void log_append(Log *log, const void *data, size_t n_data) {
592
0
        if (!n_data || !log_alloc(log))
593
0
                return;
594
595
0
        if (log->map_size - log->offset < n_data) {
596
0
                log->truncated = true;
597
0
                return;
598
0
        }
599
600
0
        c_memcpy(log->map + log->offset, data, n_data);
601
0
        log->offset += n_data;
602
0
}
603
604
/**
605
 * log_vappendf() - append structured fields
606
 * @log:                log context to operate on
607
 * @format:             formatted data to append
608
 * @args:               arguments to fill in via format string
609
 *
610
 * This is similar to log_append(), but uses printf-style formatting rather
611
 * than copying a blob verbatim.
612
 */
613
0
void log_vappendf(Log *log, const char *format, va_list args) {
614
0
        int r;
615
616
0
        if (!log_alloc(log))
617
0
                return;
618
619
0
        r = vsnprintf(log->map + log->offset,
620
0
                      log->map_size - log->offset,
621
0
                      format,
622
0
                      args);
623
0
        if (r < 0 || r >= (ssize_t)(log->map_size - log->offset)) {
624
0
                log->truncated = true;
625
0
                return;
626
0
        }
627
628
0
        log->offset += r;
629
0
}
630
631
/**
632
 * log_append_common() - append common log entries
633
 * @log:                log to operate on
634
 * @level:              syslog level indicator
635
 * @error:              errno-style error code
636
 * @id:                 log message ID, or NULL
637
 * @prov:               log provenance
638
 *
639
 * This appends known, common fields to the current log message. This should be
640
 * called for every log message.
641
 */
642
void log_append_common(Log *log,
643
                       int level,
644
                       int error,
645
                       const char *id,
646
0
                       LogProvenance prov) {
647
        /*
648
         * Use LOG_DAEMON if the log-facility is 0. Most people don't specify
649
         * any facility, so lets just apply a default. Note that 0 actually
650
         * means 'kernel' as facility, but we just treat it as unspecified
651
         * here, since no-one should specify 'kernel' from user-space.
652
         */
653
0
        level = LOG_MAKEPRI(LOG_FAC(level) ?: LOG_DAEMON,
654
0
                            LOG_PRI(level) ?: LOG_INFO);
655
656
        /*
657
         * Always remember the level of the current log message. In case the
658
         * journal is not used, we can still prefix the syslog / stderr
659
         * messages with it.
660
         */
661
0
        log->level = level;
662
663
        /*
664
         * If we have a journal-entry (or successfully allocated one), simply
665
         * append the known, common fields.
666
         */
667
0
        if (log_alloc(log)) {
668
0
                log_appendf(log,
669
0
                            "PRIORITY=%i\n"
670
0
                            "SYSLOG_FACILITY=%i\n"
671
0
                            "SYSLOG_IDENTIFIER=%s\n"
672
0
                            "ERRNO=%i\n"
673
0
                            "CODE_FILE=%s\n"
674
0
                            "CODE_LINE=%i\n"
675
0
                            "CODE_FUNC=%s\n"
676
0
                            "DBUS_BROKER_LOG_DROPPED=%"PRIu64"\n",
677
0
                            LOG_PRI(level),
678
0
                            LOG_FAC(level),
679
0
                            program_invocation_short_name,
680
0
                            error,
681
0
                            prov.file ?: "<unknown>",
682
0
                            prov.line,
683
0
                            prov.func ?: "<unknown>",
684
0
                            log->n_dropped);
685
0
                if (id)
686
0
                        log_appendf(log, "MESSAGE_ID=%s\n", id);
687
0
        }
688
0
}