• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

5 of 6 new or added lines in 1 file covered. (83.33%)

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.06
/src/basic/log.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <sys/signalfd.h>
5
#include <sys/stat.h>
6
#include <sys/uio.h>
7
#include <threads.h>
8
#include <unistd.h>
9

10
#include "sd-messages.h"
11

12
#include "alloc-util.h"
13
#include "ansi-color.h"
14
#include "argv-util.h"
15
#include "env-util.h"
16
#include "errno-util.h"
17
#include "extract-word.h"
18
#include "fd-util.h"
19
#include "format-util.h"
20
#include "iovec-util.h"
21
#include "list.h"
22
#include "log.h"
23
#include "log-context.h"
24
#include "parse-util.h"
25
#include "proc-cmdline.h"
26
#include "process-util.h"
27
#include "ratelimit.h"
28
#include "signal-util.h"
29
#include "socket-util.h"
30
#include "stdio-util.h"
31
#include "string-table.h"
32
#include "string-util.h"
33
#include "strv.h"
34
#include "syslog-util.h"
35
#include "terminal-util.h"
36
#include "time-util.h"
37
#include "utf8.h"
38

39
#define SNDBUF_SIZE (8*1024*1024)
40
#define IOVEC_MAX 256U
41

42
static log_syntax_callback_t log_syntax_callback = NULL;
43
static void *log_syntax_callback_userdata = NULL;
44

45
static LogTarget log_target = LOG_TARGET_CONSOLE;
46
static int log_max_level = LOG_INFO;
47
static int log_target_max_level[_LOG_TARGET_SINGLE_MAX] = {
48
        [LOG_TARGET_CONSOLE] = INT_MAX,
49
        [LOG_TARGET_KMSG]    = INT_MAX,
50
        [LOG_TARGET_SYSLOG]  = INT_MAX,
51
        [LOG_TARGET_JOURNAL] = INT_MAX,
52
};
53
static int log_facility = LOG_DAEMON;
54
static bool ratelimit_kmsg = true;
55

56
static int console_fd = STDERR_FILENO;
57
static int console_fd_is_tty = -1; /* tri-state: -1 means don't know */
58
static int syslog_fd = -EBADF;
59
static int kmsg_fd = -EBADF;
60
static int journal_fd = -EBADF;
61

62
static bool syslog_is_stream = false;
63

64
static int show_color = -1; /* tristate */
65
static bool show_location = false;
66
static bool show_time = false;
67
static bool show_tid = false;
68

69
static bool upgrade_syslog_to_journal = false;
70
static bool always_reopen_console = false;
71
static bool open_when_needed = false;
72
static bool prohibit_ipc = false;
73

74
static thread_local const char *log_prefix = NULL;
75

76
#if LOG_MESSAGE_VERIFICATION || defined(__COVERITY__)
77
bool _log_message_dummy = false; /* Always false */
78
#endif
79

80
/* An assert to use in logging functions that does not call recursively
81
 * into our logging functions (since that might lead to a loop). */
82
#define assert_raw(expr)                                                \
83
        do {                                                            \
84
                if (_unlikely_(!(expr))) {                              \
85
                        fputs(#expr "\n", stderr);                      \
86
                        abort();                                        \
87
                }                                                       \
88
        } while (false)
89

90
static void log_close_console(void) {
348,074✔
91
        /* See comment in log_close_journal() */
92
        (void) safe_close_above_stdio(TAKE_FD(console_fd));
348,074✔
93
        console_fd_is_tty = -1;
348,074✔
94
}
348,074✔
95

96
static int log_open_console(void) {
90,596✔
97

98
        if (!always_reopen_console) {
90,596✔
99
                console_fd = STDERR_FILENO;
70,577✔
100
                console_fd_is_tty = -1;
70,577✔
101
                return 0;
70,577✔
102
        }
103

104
        if (console_fd < 3) {
20,019✔
105
                int fd;
17,759✔
106

107
                fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
17,759✔
108
                if (fd < 0)
17,759✔
109
                        return fd;
110

111
                console_fd = fd_move_above_stdio(fd);
14,962✔
112
                console_fd_is_tty = true;
14,962✔
113
        }
114

115
        return 0;
116
}
117

118
static void log_close_kmsg(void) {
181,540✔
119
        /* See comment in log_close_journal() */
120
        (void) safe_close(TAKE_FD(kmsg_fd));
181,540✔
121
}
181,540✔
122

123
static int log_open_kmsg(void) {
17,257✔
124

125
        if (kmsg_fd >= 0)
17,257✔
126
                return 0;
127

128
        kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
15,626✔
129
        if (kmsg_fd < 0)
15,626✔
130
                return -errno;
7,638✔
131

132
        kmsg_fd = fd_move_above_stdio(kmsg_fd);
7,988✔
133
        return 0;
7,988✔
134
}
135

136
static void log_close_syslog(void) {
438,666✔
137
        /* See comment in log_close_journal() */
138
        (void) safe_close(TAKE_FD(syslog_fd));
438,666✔
139
}
438,666✔
140

141
static int create_log_socket(int type) {
156,906✔
142
        struct timeval tv;
156,906✔
143
        int fd;
156,906✔
144

145
        fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0);
156,906✔
146
        if (fd < 0)
156,906✔
UNCOV
147
                return -errno;
×
148

149
        fd = fd_move_above_stdio(fd);
156,906✔
150
        (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
156,906✔
151

152
        /* We need a blocking fd here since we'd otherwise lose messages way too early. However, let's not hang forever
153
         * in the unlikely case of a deadlock. */
154
        if (getpid_cached() == 1)
156,906✔
155
                timeval_store(&tv, 10 * USEC_PER_MSEC);
335✔
156
        else
157
                timeval_store(&tv, 10 * USEC_PER_SEC);
156,571✔
158
        (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
156,906✔
159

160
        return fd;
156,906✔
161
}
162

163
static int log_open_syslog(void) {
2✔
164
        int r;
2✔
165

166
        if (syslog_fd >= 0)
2✔
167
                return 0;
168

169
        syslog_fd = create_log_socket(SOCK_DGRAM);
2✔
170
        if (syslog_fd < 0) {
2✔
UNCOV
171
                r = syslog_fd;
×
UNCOV
172
                goto fail;
×
173
        }
174

175
        r = connect_unix_path(syslog_fd, AT_FDCWD, "/dev/log");
2✔
176
        if (r < 0) {
2✔
UNCOV
177
                safe_close(syslog_fd);
×
178

179
                /* Some legacy syslog systems still use stream sockets. They really shouldn't. But what can
180
                 * we do... */
181
                syslog_fd = create_log_socket(SOCK_STREAM);
×
UNCOV
182
                if (syslog_fd < 0) {
×
UNCOV
183
                        r = syslog_fd;
×
UNCOV
184
                        goto fail;
×
185
                }
186

UNCOV
187
                r = connect_unix_path(syslog_fd, AT_FDCWD, "/dev/log");
×
UNCOV
188
                if (r < 0)
×
UNCOV
189
                        goto fail;
×
190

191
                syslog_is_stream = true;
×
192
        } else
193
                syslog_is_stream = false;
2✔
194

195
        return 0;
196

197
fail:
×
198
        log_close_syslog();
×
UNCOV
199
        return r;
×
200
}
201

202
static void log_close_journal(void) {
281,684✔
203
        /* If the journal FD is bad, safe_close will fail, and will try to log, which will fail, so we'll
204
         * try to close the journal FD, which is bad, so safe_close will fail... Whether we can close it
205
         * or not, invalidate it immediately so that we don't get in a recursive loop until we run out of
206
         * stack. */
207
        (void) safe_close(TAKE_FD(journal_fd));
281,684✔
208
}
281,684✔
209

210
static int log_open_journal(void) {
164,656✔
211
        int r;
164,656✔
212

213
        if (journal_fd >= 0)
164,656✔
214
                return 0;
215

216
        journal_fd = create_log_socket(SOCK_DGRAM);
156,904✔
217
        if (journal_fd < 0) {
156,904✔
UNCOV
218
                r = journal_fd;
×
UNCOV
219
                goto fail;
×
220
        }
221

222
        r = connect_unix_path(journal_fd, AT_FDCWD, "/run/systemd/journal/socket");
156,904✔
223
        if (r < 0)
156,904✔
224
                goto fail;
53✔
225

226
        return 0;
227

228
fail:
53✔
229
        log_close_journal();
53✔
230
        return r;
53✔
231
}
232

233
bool stderr_is_journal(void) {
249,385✔
234
        _cleanup_free_ char *w = NULL;
249,385✔
235
        const char *e;
249,385✔
236
        uint64_t dev, ino;
249,385✔
237
        struct stat st;
249,385✔
238

239
        e = getenv("JOURNAL_STREAM");
249,385✔
240
        if (!e)
249,385✔
241
                return false;
242

243
        if (extract_first_word(&e, &w, ":", EXTRACT_DONT_COALESCE_SEPARATORS) <= 0)
16,970✔
244
                return false;
245
        if (!e)
16,970✔
246
                return false;
247

248
        if (safe_atou64(w, &dev) < 0)
16,970✔
249
                return false;
250
        if (safe_atou64(e, &ino) < 0)
16,970✔
251
                return false;
252

253
        if (fstat(STDERR_FILENO, &st) < 0)
16,970✔
254
                return false;
255

256
        return st.st_dev == dev && st.st_ino == ino;
17,765✔
257
}
258

259
int log_open(void) {
257,128✔
260
        int r;
257,128✔
261

262
        /* Do not call from library code. */
263

264
        /* This function is often called in preparation for logging. Let's make sure we don't clobber errno,
265
         * so that a call to a logging function immediately following a log_open() call can still easily
266
         * reference an error that happened immediately before the log_open() call. */
267
        PROTECT_ERRNO;
257,128✔
268

269
        /* If we don't use the console, we close it here to not get killed by SAK. If we don't use syslog, we
270
         * close it here too, so that we are not confused by somebody deleting the socket in the fs, and to
271
         * make sure we don't use it if prohibit_ipc is set. If we don't use /dev/kmsg we still keep it open,
272
         * because there is no reason to close it. */
273

274
        if (log_target == LOG_TARGET_NULL) {
257,128✔
275
                log_close_journal();
1✔
276
                log_close_syslog();
1✔
277
                log_close_console();
1✔
278
                return 0;
279
        }
280

281
        if (getpid_cached() == 1 ||
503,011✔
282
            stderr_is_journal() ||
245,884✔
283
            IN_SET(log_target,
233,002✔
284
                   LOG_TARGET_KMSG,
285
                   LOG_TARGET_JOURNAL,
286
                   LOG_TARGET_JOURNAL_OR_KMSG,
287
                   LOG_TARGET_SYSLOG,
288
                   LOG_TARGET_SYSLOG_OR_KMSG)) {
289

290
                if (!prohibit_ipc) {
175,824✔
291
                        if (IN_SET(log_target,
171,835✔
292
                                   LOG_TARGET_AUTO,
293
                                   LOG_TARGET_JOURNAL_OR_KMSG,
294
                                   LOG_TARGET_JOURNAL)) {
295

296
                                r = log_open_journal();
157,080✔
297
                                if (r >= 0) {
157,080✔
298
                                        log_close_syslog();
157,040✔
299
                                        log_close_console();
157,040✔
300
                                        return r;
301
                                }
302
                        }
303

304
                        if (IN_SET(log_target,
14,795✔
305
                                   LOG_TARGET_SYSLOG_OR_KMSG,
306
                                   LOG_TARGET_SYSLOG)) {
307

308
                                r = log_open_syslog();
2✔
309
                                if (r >= 0) {
2✔
310
                                        log_close_journal();
2✔
311
                                        log_close_console();
2✔
312
                                        return r;
313
                                }
314
                        }
315
                }
316

317
                if (IN_SET(log_target, LOG_TARGET_AUTO,
18,782✔
318
                                       LOG_TARGET_JOURNAL_OR_KMSG,
319
                                       LOG_TARGET_SYSLOG_OR_KMSG,
320
                                       LOG_TARGET_KMSG)) {
321
                        r = log_open_kmsg();
17,128✔
322
                        if (r >= 0) {
17,128✔
323
                                log_close_journal();
9,490✔
324
                                log_close_syslog();
9,490✔
325
                                log_close_console();
9,490✔
326
                                return r;
327
                        }
328
                }
329
        }
330

331
        log_close_journal();
90,595✔
332
        log_close_syslog();
90,595✔
333

334
        return log_open_console();
90,595✔
335
}
336

337
void log_set_target(LogTarget target) {
107,675✔
338
        assert(target >= 0);
107,675✔
339
        assert(target < _LOG_TARGET_MAX);
107,675✔
340

341
        if (upgrade_syslog_to_journal) {
107,675✔
342
                if (target == LOG_TARGET_SYSLOG)
310✔
343
                        target = LOG_TARGET_JOURNAL;
344
                else if (target == LOG_TARGET_SYSLOG_OR_KMSG)
310✔
UNCOV
345
                        target = LOG_TARGET_JOURNAL_OR_KMSG;
×
346
        }
347

348
        log_target = target;
107,675✔
349
}
107,675✔
350

351
void log_set_target_and_open(LogTarget target) {
284✔
352
        log_set_target(target);
284✔
353
        log_open();
284✔
354
}
284✔
355

356
void log_close(void) {
181,540✔
357
        /* Do not call from library code. */
358

359
        log_close_journal();
181,540✔
360
        log_close_syslog();
181,540✔
361
        log_close_kmsg();
181,540✔
362
        log_close_console();
181,540✔
363
}
181,540✔
364

365
void log_forget_fds(void) {
11,568✔
366
        /* Do not call from library code. */
367

368
        console_fd = kmsg_fd = syslog_fd = journal_fd = -EBADF;
11,568✔
369
        console_fd_is_tty = -1;
11,568✔
370
}
11,568✔
371

372
int log_set_max_level(int level) {
700,976✔
373
        assert(level == LOG_NULL || log_level_is_valid(level));
700,976✔
374

375
        int old = log_max_level;
700,976✔
376
        log_max_level = level;
700,976✔
377

378
        /* Also propagate max log level to libc's syslog(), just in case some other component loaded into our
379
         * process logs directly via syslog(). You might wonder why we maintain our own log level variable if
380
         * libc has the same functionality. This has multiple reasons, first and foremost that we want to
381
         * apply this to all our log targets, not just syslog and console. Moreover, we cannot query the
382
         * current log mask from glibc without changing it, but that's useful for testing the current log
383
         * level before even entering the log functions like we do in our macros. */
384
        setlogmask(LOG_UPTO(level));
700,976✔
385

386
        /* Ensure that our own LOG_NULL define maps sanely to the log mask */
387
        assert_cc(LOG_UPTO(LOG_NULL) == 0);
700,976✔
388

389
        return old;
700,976✔
390
}
391

392
void log_set_facility(int facility) {
244✔
393
        log_facility = facility;
244✔
394
}
244✔
395

396
static bool check_console_fd_is_tty(void) {
161,950✔
397
        if (console_fd < 0)
161,950✔
398
                return false;
399

400
        if (console_fd_is_tty < 0)
161,950✔
401
                console_fd_is_tty = isatty_safe(console_fd);
11,310✔
402

403
        return console_fd_is_tty;
161,950✔
404
}
405

406
static int write_to_console(
247,028✔
407
                int level,
408
                int error,
409
                const char *file,
410
                int line,
411
                const char *func,
412
                const char *buffer) {
413

414
        static int dumb = -1;
247,028✔
415

416
        char location[256],
247,028✔
417
             header_time[FORMAT_TIMESTAMP_MAX],
418
             prefix[1 + DECIMAL_STR_MAX(int) + 2],
419
             tid_string[3 + DECIMAL_STR_MAX(pid_t) + 1];
420
        struct iovec iovec[11];
247,028✔
421
        const char *on = NULL, *off = NULL;
247,028✔
422
        size_t n = 0;
247,028✔
423

424
        if (console_fd < 0)
247,028✔
425
                return 0;
247,028✔
426

427
        if (dumb < 0)
238,859✔
428
                dumb = getenv_terminal_is_dumb();
12,249✔
429

430
        if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_CONSOLE])
238,859✔
431
                return 0;
432

433
        if (log_target == LOG_TARGET_CONSOLE_PREFIXED) {
161,950✔
434
                xsprintf(prefix, "<%i>", level);
437✔
435
                iovec[n++] = IOVEC_MAKE_STRING(prefix);
437✔
436
        }
437

438
        if (show_time &&
161,950✔
UNCOV
439
            format_timestamp(header_time, sizeof(header_time), now(CLOCK_REALTIME))) {
×
UNCOV
440
                iovec[n++] = IOVEC_MAKE_STRING(header_time);
×
UNCOV
441
                iovec[n++] = IOVEC_MAKE_STRING(" ");
×
442
        }
443

444
        if (show_tid) {
161,950✔
UNCOV
445
                xsprintf(tid_string, "(" PID_FMT ") ", gettid());
×
UNCOV
446
                iovec[n++] = IOVEC_MAKE_STRING(tid_string);
×
447
        }
448

449
        if (log_get_show_color())
161,950✔
450
                get_log_colors(LOG_PRI(level), &on, &off, NULL);
148,879✔
451

452
        if (show_location) {
161,950✔
UNCOV
453
                const char *lon = "", *loff = "";
×
454
                if (log_get_show_color()) {
×
455
                        lon = ansi_highlight_yellow4();
×
UNCOV
456
                        loff = ansi_normal();
×
457
                }
458

UNCOV
459
                (void) snprintf(location, sizeof location, "%s%s:%i%s: ", lon, file, line, loff);
×
UNCOV
460
                iovec[n++] = IOVEC_MAKE_STRING(location);
×
461
        }
462

463
        if (on)
161,950✔
464
                iovec[n++] = IOVEC_MAKE_STRING(on);
139,070✔
465
        if (log_prefix) {
161,950✔
466
                iovec[n++] = IOVEC_MAKE_STRING(log_prefix);
7,025✔
467
                iovec[n++] = IOVEC_MAKE_STRING(": ");
7,025✔
468
        }
469
        iovec[n++] = IOVEC_MAKE_STRING(buffer);
161,950✔
470
        if (off)
161,950✔
471
                iovec[n++] = IOVEC_MAKE_STRING(off);
139,070✔
472

473
        /* When writing to a TTY we output an extra '\r' (i.e. CR) first, to generate CRNL rather than just
474
         * NL. This is a robustness thing in case the TTY is currently in raw mode (specifically: has the
475
         * ONLCR flag off). We want that subsequent output definitely starts at the beginning of the line
476
         * again, after all. If the TTY is not in raw mode the extra CR should not hurt. If we're writing to
477
         * a dumb terminal, only write NL as CRNL might be interpreted as a double newline. */
478
        iovec[n++] = IOVEC_MAKE_STRING(check_console_fd_is_tty() && !dumb ? "\r\n" : "\n");
323,900✔
479

480
        if (writev(console_fd, iovec, n) < 0) {
161,950✔
481

482
                if (errno == EIO && getpid_cached() == 1) {
84✔
483

484
                        /* If somebody tried to kick us from our console tty (via vhangup() or suchlike), try
485
                         * to reconnect. */
486

487
                        log_close_console();
1✔
488
                        (void) log_open_console();
1✔
489
                        if (console_fd < 0)
1✔
490
                                return 0;
491

492
                        if (writev(console_fd, iovec, n) < 0)
1✔
UNCOV
493
                                return -errno;
×
494
                } else
495
                        return -errno;
83✔
496
        }
497

498
        return 1;
499
}
500

501
static int write_to_syslog(
180✔
502
                int level,
503
                int error,
504
                const char *file,
505
                int line,
506
                const char *func,
507
                const char *buffer) {
508

509
        char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
180✔
510
             header_time[64],
511
             header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
512
        struct tm tm;
180✔
513
        int r;
180✔
514

515
        if (syslog_fd < 0)
180✔
516
                return 0;
180✔
517

518
        if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_SYSLOG])
180✔
519
                return 0;
520

521
        xsprintf(header_priority, "<%i>", level);
180✔
522

523
        r = localtime_or_gmtime_usec(now(CLOCK_REALTIME), /* utc= */ false, &tm);
180✔
524
        if (r < 0)
180✔
525
                return r;
526

527
        if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0)
180✔
528
                return -EINVAL;
529

530
        xsprintf(header_pid, "["PID_FMT"]: ", getpid_cached());
180✔
531

532
        struct iovec iovec[] = {
1,440✔
533
                IOVEC_MAKE_STRING(header_priority),
180✔
534
                IOVEC_MAKE_STRING(header_time),
180✔
535
                IOVEC_MAKE_STRING(program_invocation_short_name),
180✔
536
                IOVEC_MAKE_STRING(header_pid),
180✔
537
                IOVEC_MAKE_STRING(strempty(log_prefix)),
292✔
538
                IOVEC_MAKE_STRING(log_prefix ? ": " : ""),
292✔
539
                IOVEC_MAKE_STRING(buffer),
180✔
540
        };
541
        const struct msghdr msghdr = {
180✔
542
                .msg_iov = iovec,
543
                .msg_iovlen = ELEMENTSOF(iovec),
544
        };
545

546
        /* When using syslog via SOCK_STREAM separate the messages by NUL chars */
547
        if (syslog_is_stream)
180✔
UNCOV
548
                iovec[ELEMENTSOF(iovec) - 1].iov_len++;
×
549

550
        for (;;) {
180✔
551
                ssize_t n;
180✔
552

553
                n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
180✔
554
                if (n < 0)
180✔
UNCOV
555
                        return -errno;
×
556

557
                if (!syslog_is_stream)
180✔
558
                        break;
559

UNCOV
560
                if (iovec_increment(iovec, ELEMENTSOF(iovec), n))
×
561
                        break;
562
        }
563

564
        return 1;
565
}
566

567
static int write_to_kmsg(
214,225✔
568
                int level,
569
                int error,
570
                const char *file,
571
                int line,
572
                const char *func,
573
                const char *buffer) {
574

575
        /* Set a ratelimit on the amount of messages logged to /dev/kmsg. This is mostly supposed to be a
576
         * safety catch for the case where start indiscriminately logging in a loop. It will not catch cases
577
         * where we log excessively, but not in a tight loop.
578
         *
579
         * Note that this ratelimit is per-emitter, so we might still overwhelm /dev/kmsg with multiple
580
         * loggers.
581
         */
582
        static thread_local RateLimit ratelimit = { 5 * USEC_PER_SEC, 200 };
214,225✔
583

584
        char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
214,225✔
585
             header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1];
586

587
        if (kmsg_fd < 0)
214,225✔
588
                return 0;
214,225✔
589

590
        if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_KMSG])
40,609✔
591
                return 0;
592

593
        if (ratelimit_kmsg && !ratelimit_below(&ratelimit)) {
40,609✔
UNCOV
594
                if (ratelimit_num_dropped(&ratelimit) > 1)
×
595
                        return 0;
596

597
                buffer = "Too many messages being logged to kmsg, ignoring";
598
        }
599

600
        xsprintf(header_priority, "<%i>", level);
40,609✔
601
        xsprintf(header_pid, "["PID_FMT"]: ", getpid_cached());
40,609✔
602

603
        const struct iovec iovec[] = {
324,872✔
604
                IOVEC_MAKE_STRING(header_priority),
40,609✔
605
                IOVEC_MAKE_STRING(program_invocation_short_name),
40,609✔
606
                IOVEC_MAKE_STRING(header_pid),
40,609✔
607
                IOVEC_MAKE_STRING(strempty(log_prefix)),
62,075✔
608
                IOVEC_MAKE_STRING(log_prefix ? ": " : ""),
62,075✔
609
                IOVEC_MAKE_STRING(buffer),
40,609✔
610
                IOVEC_MAKE_STRING("\n"),
40,609✔
611
        };
612

613
        if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
40,609✔
UNCOV
614
                return -errno;
×
615

616
        return 1;
617
}
618

619
static int log_do_header(
4,890,240✔
620
                char *header,
621
                size_t size,
622
                int level,
623
                int error,
624
                const char *file, int line, const char *func,
625
                const char *object_field, const char *object,
626
                const char *extra_field, const char *extra) {
627
        int r;
4,890,240✔
628

629
        error = IS_SYNTHETIC_ERRNO(error) ? 0 : ERRNO_VALUE(error);
4,890,240✔
630

631
        r = snprintf(header, size,
4,890,240✔
632
                     "PRIORITY=%i\n"
633
                     "SYSLOG_FACILITY=%i\n"
634
                     "TID=" PID_FMT "\n"
635
                     "%s%.256s%s"        /* CODE_FILE */
636
                     "%s%.*i%s"          /* CODE_LINE */
637
                     "%s%.256s%s"        /* CODE_FUNC */
638
                     "%s%.*i%s"          /* ERRNO */
639
                     "%s%.256s%s"        /* object */
640
                     "%s%.256s%s"        /* extra */
641
                     "SYSLOG_IDENTIFIER=%.256s\n",
642
                     LOG_PRI(level),
643
                     LOG_FAC(level),
4,890,240✔
644
                     gettid(),
645
                     isempty(file) ? "" : "CODE_FILE=",
4,890,240✔
646
                     isempty(file) ? "" : file,
4,890,240✔
647
                     isempty(file) ? "" : "\n",
4,890,240✔
648
                     line ? "CODE_LINE=" : "",
649
                     line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */
650
                     line ? "\n" : "",
651
                     isempty(func) ? "" : "CODE_FUNC=",
4,890,240✔
652
                     isempty(func) ? "" : func,
4,890,240✔
653
                     isempty(func) ? "" : "\n",
4,890,240✔
654
                     error ? "ERRNO=" : "",
655
                     error ? 1 : 0, error,
656
                     error ? "\n" : "",
657
                     isempty(object) ? "" : ASSERT_PTR(object_field),
4,890,240✔
658
                     isempty(object) ? "" : object,
4,890,240✔
659
                     isempty(object) ? "" : "\n",
4,890,240✔
660
                     isempty(extra) ? "" : ASSERT_PTR(extra_field),
4,890,240✔
661
                     isempty(extra) ? "" : extra,
4,890,240✔
662
                     isempty(extra) ? "" : "\n",
4,890,240✔
663
                     program_invocation_short_name);
664
        assert_raw((size_t) r < size);
4,890,240✔
665

666
        return 0;
4,890,240✔
667
}
668

669
static void log_do_context(struct iovec *iovec, size_t iovec_len, size_t *n) {
4,890,240✔
670
        assert(iovec);
4,890,240✔
671
        assert(n);
4,890,240✔
672

673
        LIST_FOREACH(ll, c, log_context_head()) {
6,882,813✔
674
                STRV_FOREACH(s, c->fields) {
18,561,556✔
675
                        if (*n + 2 >= iovec_len)
16,438,825✔
UNCOV
676
                                return;
×
677

678
                        iovec[(*n)++] = IOVEC_MAKE_STRING(*s);
16,438,825✔
679
                        iovec[(*n)++] = IOVEC_MAKE_STRING("\n");
16,438,825✔
680
                }
681

682
                for (size_t i = 0; i < c->n_input_iovec; i++) {
2,126,342✔
683
                        if (*n + 2 >= iovec_len)
3,611✔
UNCOV
684
                                return;
×
685

686
                        iovec[(*n)++] = c->input_iovec[i];
3,611✔
687
                        iovec[(*n)++] = IOVEC_MAKE_STRING("\n");
3,611✔
688
                }
689

690
                if (c->key && c->value) {
2,122,731✔
691
                        if (*n + 3 >= iovec_len)
284,870✔
692
                                return;
130,158✔
693

694
                        iovec[(*n)++] = IOVEC_MAKE_STRING(c->key);
154,712✔
695
                        iovec[(*n)++] = IOVEC_MAKE_STRING(c->value);
154,712✔
696
                        iovec[(*n)++] = IOVEC_MAKE_STRING("\n");
154,712✔
697
                }
698
        }
699
}
700

701
static int write_to_journal(
5,091,639✔
702
                int level,
703
                int error,
704
                const char *file,
705
                int line,
706
                const char *func,
707
                const char *object_field,
708
                const char *object,
709
                const char *extra_field,
710
                const char *extra,
711
                const char *buffer) {
712

713
        char header[LINE_MAX];
5,091,639✔
714
        size_t n = 0, iovec_len;
5,091,639✔
715
        struct iovec *iovec;
5,091,639✔
716

717
        if (journal_fd < 0)
5,091,639✔
718
                return 0;
5,091,639✔
719

720
        if (LOG_PRI(level) > log_target_max_level[LOG_TARGET_JOURNAL])
4,829,800✔
721
                return 0;
722

723
        iovec_len = MIN(6 + log_context_num_fields() * 3, IOVEC_MAX);
4,829,800✔
724
        iovec = newa(struct iovec, iovec_len);
4,829,800✔
725

726
        log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra);
4,829,800✔
727

728
        iovec[n++] = IOVEC_MAKE_STRING(header);
4,829,800✔
729
        iovec[n++] = IOVEC_MAKE_STRING("MESSAGE=");
4,829,800✔
730
        if (log_prefix) {
4,829,800✔
731
                iovec[n++] = IOVEC_MAKE_STRING(log_prefix);
1,783,277✔
732
                iovec[n++] = IOVEC_MAKE_STRING(": ");
1,783,277✔
733
        }
734
        iovec[n++] = IOVEC_MAKE_STRING(buffer);
4,829,800✔
735
        iovec[n++] = IOVEC_MAKE_STRING("\n");
4,829,800✔
736

737
        log_do_context(iovec, iovec_len, &n);
4,829,800✔
738

739
        const struct msghdr msghdr = {
4,829,800✔
740
                .msg_iov = iovec,
741
                .msg_iovlen = n,
742
        };
743

744
        if (sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL) < 0)
4,829,800✔
745
                return -errno;
189✔
746

747
        return 1;
748
}
749

750
int log_dispatch_internal(
5,108,112✔
751
                int level,
752
                int error,
753
                const char *file,
754
                int line,
755
                const char *func,
756
                const char *object_field,
757
                const char *object,
758
                const char *extra_field,
759
                const char *extra,
760
                char *buffer) {
761

762
        assert_raw(buffer);
5,108,112✔
763

764
        if (log_target == LOG_TARGET_NULL)
5,108,112✔
765
                return -ERRNO_VALUE(error);
10✔
766

767
        /* Patch in LOG_DAEMON facility if necessary */
768
        if (LOG_FAC(level) == 0)
5,108,102✔
769
                level |= log_facility;
5,099,724✔
770

771
        if (open_when_needed)
5,108,102✔
772
                (void) log_open();
141,343✔
773

774
        do {
5,162,975✔
775
                char *e;
5,162,975✔
776
                int k = 0;
5,162,975✔
777

778
                buffer += strspn(buffer, NEWLINE);
5,162,975✔
779

780
                if (buffer[0] == 0)
5,162,975✔
781
                        break;
782

783
                if ((e = strpbrk(buffer, NEWLINE)))
5,117,428✔
784
                        *(e++) = 0;
54,873✔
785

786
                if (IN_SET(log_target, LOG_TARGET_AUTO,
5,117,428✔
787
                                       LOG_TARGET_JOURNAL_OR_KMSG,
788
                                       LOG_TARGET_JOURNAL)) {
789

790
                        k = write_to_journal(level, error, file, line, func, object_field, object, extra_field, extra, buffer);
5,091,639✔
791
                        if (k < 0 && k != -EAGAIN)
5,091,639✔
792
                                log_close_journal();
3✔
793
                }
794

795
                if (IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
5,117,428✔
796
                                       LOG_TARGET_SYSLOG)) {
797

798
                        k = write_to_syslog(level, error, file, line, func, buffer);
180✔
799
                        if (k < 0 && k != -EAGAIN)
180✔
UNCOV
800
                                log_close_syslog();
×
801
                }
802

803
                if (k <= 0 &&
5,117,428✔
804
                    IN_SET(log_target, LOG_TARGET_AUTO,
287,637✔
805
                                       LOG_TARGET_SYSLOG_OR_KMSG,
806
                                       LOG_TARGET_JOURNAL_OR_KMSG,
807
                                       LOG_TARGET_KMSG)) {
808

809
                        if (k < 0)
214,225✔
810
                                log_open_kmsg();
129✔
811

812
                        k = write_to_kmsg(level, error, file, line, func, buffer);
214,225✔
813
                        if (k < 0) {
214,225✔
UNCOV
814
                                log_close_kmsg();
×
UNCOV
815
                                (void) log_open_console();
×
816
                        }
817
                }
818

819
                if (k <= 0)
287,637✔
820
                        (void) write_to_console(level, error, file, line, func, buffer);
247,028✔
821

822
                buffer = e;
5,117,428✔
823
        } while (buffer);
5,117,428✔
824

825
        if (open_when_needed)
5,108,102✔
826
                log_close();
141,343✔
827

828
        return -ERRNO_VALUE(error);
5,108,102✔
829
}
830

831
int log_dump_internal(
6✔
832
                int level,
833
                int error,
834
                const char *file,
835
                int line,
836
                const char *func,
837
                char *buffer) {
838

839
        PROTECT_ERRNO;
6✔
840

841
        /* This modifies the buffer... */
842

843
        if (_likely_(LOG_PRI(level) > log_max_level))
6✔
UNCOV
844
                return -ERRNO_VALUE(error);
×
845

846
        return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
6✔
847
}
848

849
int log_internalv(
3,399,501✔
850
                int level,
851
                int error,
852
                const char *file,
853
                int line,
854
                const char *func,
855
                const char *format,
856
                va_list ap) {
857

858
        if (_likely_(LOG_PRI(level) > log_max_level))
3,399,501✔
859
                return -ERRNO_VALUE(error);
3,399,501✔
860

861
        /* Make sure that %m maps to the specified error (or "Success"). */
862
        char buffer[LINE_MAX];
3,399,277✔
863
        LOCAL_ERRNO(ERRNO_VALUE(error));
3,399,277✔
864

865
        (void) vsnprintf(buffer, sizeof buffer, format, ap);
3,399,277✔
866

867
        return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
3,399,277✔
868
}
869

870
int log_internal(
3,397,569✔
871
                int level,
872
                int error,
873
                const char *file,
874
                int line,
875
                const char *func,
876
                const char *format, ...) {
877

878
        va_list ap;
3,397,569✔
879
        int r;
3,397,569✔
880

881
        va_start(ap, format);
3,397,569✔
882
        r = log_internalv(level, error, file, line, func, format, ap);
3,397,569✔
883
        va_end(ap);
3,397,569✔
884

885
        return r;
3,397,569✔
886
}
887

888
int log_object_internalv(
1,753,494✔
889
                int level,
890
                int error,
891
                const char *file,
892
                int line,
893
                const char *func,
894
                const char *object_field,
895
                const char *object,
896
                const char *extra_field,
897
                const char *extra,
898
                const char *format,
899
                va_list ap) {
900

901
        char *buffer, *b;
1,753,494✔
902

903
        if (_likely_(LOG_PRI(level) > log_max_level))
1,753,494✔
904
                return -ERRNO_VALUE(error);
1,753,494✔
905

906
        /* Make sure that %m maps to the specified error (or "Success"). */
UNCOV
907
        LOCAL_ERRNO(ERRNO_VALUE(error));
×
908

909
        LOG_SET_PREFIX(object);
3,401,376✔
910

911
        b = buffer = newa(char, LINE_MAX);
1,700,688✔
912
        (void) vsnprintf(b, LINE_MAX, format, ap);
1,700,688✔
913

914
        return log_dispatch_internal(level, error, file, line, func,
1,700,688✔
915
                                     object_field, object, extra_field, extra, buffer);
916
}
917

918
int log_object_internal(
1,749,176✔
919
                int level,
920
                int error,
921
                const char *file,
922
                int line,
923
                const char *func,
924
                const char *object_field,
925
                const char *object,
926
                const char *extra_field,
927
                const char *extra,
928
                const char *format, ...) {
929

930
        va_list ap;
1,749,176✔
931
        int r;
1,749,176✔
932

933
        va_start(ap, format);
1,749,176✔
934
        r = log_object_internalv(level, error, file, line, func, object_field, object, extra_field, extra, format, ap);
1,749,176✔
935
        va_end(ap);
1,749,176✔
936

937
        return r;
1,749,176✔
938
}
939

UNCOV
940
int log_oom_internal(int level, const char *file, int line, const char *func) {
×
UNCOV
941
        return log_internal(level, ENOMEM, file, line, func, "Out of memory.");
×
942
}
943

944
int log_format_iovec(
59,149✔
945
                struct iovec *iovec,
946
                size_t iovec_len,
947
                size_t *n,
948
                bool newline_separator,
949
                int error,
950
                const char *format,
951
                va_list ap) {
952

953
        static const char nl = '\n';
59,149✔
954

955
        while (format && *n + 1 < iovec_len) {
329,496✔
956
                va_list aq;
270,347✔
957
                char *m;
270,347✔
958
                int r;
270,347✔
959

960
                /* We need to copy the va_list structure,
961
                 * since vasprintf() leaves it afterwards at
962
                 * an undefined location */
963

964
                errno = ERRNO_VALUE(error);
270,347✔
965

966
                va_copy(aq, ap);
270,347✔
967
                r = vasprintf(&m, format, aq);
270,347✔
968
                va_end(aq);
270,347✔
969
                if (r < 0)
270,347✔
UNCOV
970
                        return -EINVAL;
×
971

972
                /* Now, jump enough ahead, so that we point to
973
                 * the next format string */
974
                VA_FORMAT_ADVANCE(format, ap);
873,017✔
975

976
                iovec[(*n)++] = IOVEC_MAKE_STRING(m);
270,347✔
977
                if (newline_separator)
270,347✔
978
                        iovec[(*n)++] = IOVEC_MAKE((char *)&nl, 1);
268,875✔
979

980
                format = va_arg(ap, char *);
270,347✔
981
        }
982
        return 0;
983
}
984

985
int log_struct_internal(
66,780✔
986
                int level,
987
                int error,
988
                const char *file,
989
                int line,
990
                const char *func,
991
                const char *format, ...) {
992

993
        char buf[LINE_MAX];
66,780✔
994
        bool found = false;
66,780✔
995
        PROTECT_ERRNO;
66,780✔
996
        va_list ap;
66,780✔
997

998
        if (_likely_(LOG_PRI(level) > log_max_level) ||
66,780✔
999
            log_target == LOG_TARGET_NULL)
66,780✔
1000
                return -ERRNO_VALUE(error);
50✔
1001

1002
        if (LOG_FAC(level) == 0)
66,730✔
1003
                level |= log_facility;
66,730✔
1004

1005
        if (IN_SET(log_target,
66,730✔
1006
                   LOG_TARGET_AUTO,
1007
                   LOG_TARGET_JOURNAL_OR_KMSG,
1008
                   LOG_TARGET_JOURNAL)) {
1009

1010
                if (open_when_needed)
64,614✔
1011
                        log_open_journal();
7,576✔
1012

1013
                if (journal_fd >= 0) {
64,614✔
1014
                        char header[LINE_MAX];
58,995✔
1015
                        struct iovec *iovec;
58,995✔
1016
                        size_t n = 0, m, iovec_len;
58,995✔
1017
                        int r;
58,995✔
1018
                        bool fallback = false;
58,995✔
1019

1020
                        iovec_len = MIN(17 + log_context_num_fields() * 3, IOVEC_MAX);
58,995✔
1021
                        iovec = newa(struct iovec, iovec_len);
58,995✔
1022

1023
                        /* If the journal is available do structured logging.
1024
                         * Do not report the errno if it is synthetic. */
1025
                        log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
58,995✔
1026
                        iovec[n++] = IOVEC_MAKE_STRING(header);
58,995✔
1027

1028
                        va_start(ap, format);
58,995✔
1029
                        DISABLE_WARNING_FORMAT_NONLITERAL;
58,995✔
1030
                        r = log_format_iovec(iovec, iovec_len, &n, true, error, format, ap);
58,995✔
1031
                        REENABLE_WARNING;
58,995✔
1032
                        m = n;
58,995✔
1033
                        if (r < 0)
58,995✔
1034
                                fallback = true;
1035
                        else {
1036
                                log_do_context(iovec, iovec_len, &n);
58,995✔
1037

1038
                                const struct msghdr msghdr = {
58,995✔
1039
                                        .msg_iov = iovec,
1040
                                        .msg_iovlen = n,
1041
                                };
1042

1043
                                (void) sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL);
58,995✔
1044
                        }
1045

1046
                        va_end(ap);
58,995✔
1047
                        for (size_t i = 1; i < m; i += 2)
327,870✔
1048
                                free(iovec[i].iov_base);
268,875✔
1049

1050
                        if (!fallback) {
58,995✔
1051
                                if (open_when_needed)
58,995✔
1052
                                        log_close();
7,563✔
1053

1054
                                return -ERRNO_VALUE(error);
58,995✔
1055
                        }
1056
                }
1057
        }
1058

1059
        /* Fallback if journal logging is not available or didn't work. */
1060

1061
        va_start(ap, format);
7,735✔
1062
        while (format) {
21,724✔
1063
                va_list aq;
21,724✔
1064

1065
                errno = ERRNO_VALUE(error);
21,724✔
1066

1067
                va_copy(aq, ap);
21,724✔
1068
                DISABLE_WARNING_FORMAT_NONLITERAL;
21,724✔
1069
                (void) vsnprintf(buf, sizeof buf, format, aq);
21,724✔
1070
                REENABLE_WARNING;
21,724✔
1071
                va_end(aq);
21,724✔
1072

1073
                if (startswith(buf, "MESSAGE=")) {
21,724✔
1074
                        found = true;
7,735✔
1075
                        break;
7,735✔
1076
                }
1077

1078
                VA_FORMAT_ADVANCE(format, ap);
37,879✔
1079

1080
                format = va_arg(ap, char *);
13,989✔
1081
        }
1082
        va_end(ap);
7,735✔
1083

1084
        if (!found) {
7,735✔
UNCOV
1085
                if (open_when_needed)
×
UNCOV
1086
                        log_close();
×
1087

UNCOV
1088
                return -ERRNO_VALUE(error);
×
1089
        }
1090

1091
        return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buf + 8);
7,735✔
1092
}
1093

1094
int log_struct_iovec_internal(
1,473✔
1095
                int level,
1096
                int error,
1097
                const char *file,
1098
                int line,
1099
                const char *func,
1100
                const struct iovec input_iovec[],
1101
                size_t n_input_iovec) {
1102

1103
        PROTECT_ERRNO;
1,473✔
1104

1105
        if (_likely_(LOG_PRI(level) > log_max_level) ||
1,473✔
1106
            log_target == LOG_TARGET_NULL)
1,473✔
UNCOV
1107
                return -ERRNO_VALUE(error);
×
1108

1109
        if (LOG_FAC(level) == 0)
1,473✔
1110
                level |= log_facility;
1,473✔
1111

1112
        if (IN_SET(log_target, LOG_TARGET_AUTO,
1,473✔
1113
                               LOG_TARGET_JOURNAL_OR_KMSG,
1114
                               LOG_TARGET_JOURNAL) &&
1,473✔
1115
            journal_fd >= 0) {
1,473✔
1116

1117
                char header[LINE_MAX];
1,445✔
1118
                struct iovec *iovec;
1,445✔
1119
                size_t n = 0, iovec_len;
1,445✔
1120

1121
                iovec_len = MIN(1 + n_input_iovec * 2 + log_context_num_fields() * 3, IOVEC_MAX);
1,445✔
1122
                iovec = newa(struct iovec, iovec_len);
1,445✔
1123

1124
                log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
1,445✔
1125

1126
                iovec[n++] = IOVEC_MAKE_STRING(header);
1,445✔
1127
                for (size_t i = 0; i < n_input_iovec; i++) {
11,510✔
1128
                        iovec[n++] = input_iovec[i];
10,065✔
1129
                        iovec[n++] = IOVEC_MAKE_STRING("\n");
10,065✔
1130
                }
1131

1132
                log_do_context(iovec, iovec_len, &n);
1,445✔
1133

1134
                const struct msghdr msghdr = {
1,445✔
1135
                        .msg_iov = iovec,
1136
                        .msg_iovlen = n,
1137
                };
1138

1139
                if (sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL) >= 0)
1,445✔
1140
                        return -ERRNO_VALUE(error);
1,445✔
1141
        }
1142

1143
        for (size_t i = 0; i < n_input_iovec; i++)
112✔
1144
                if (memory_startswith(input_iovec[i].iov_base, input_iovec[i].iov_len, "MESSAGE=")) {
112✔
1145
                        char *m;
28✔
1146

1147
                        m = strndupa_safe((char*) input_iovec[i].iov_base + STRLEN("MESSAGE="),
28✔
1148
                                          input_iovec[i].iov_len - STRLEN("MESSAGE="));
1149

1150
                        return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, m);
28✔
1151
                }
1152

1153
        /* Couldn't find MESSAGE=. */
UNCOV
1154
        return -ERRNO_VALUE(error);
×
1155
}
1156

1157
int log_set_target_from_string(const char *e) {
12,183✔
1158
        LogTarget t;
12,183✔
1159

1160
        t = log_target_from_string(e);
12,183✔
1161
        if (t < 0)
12,183✔
1162
                return t;
1163

1164
        log_set_target(t);
12,183✔
1165
        return 0;
12,183✔
1166
}
1167

1168
int log_set_max_level_from_string(const char *e) {
25,188✔
1169
        int r;
68,971✔
1170

1171
        for (;;) {
68,971✔
1172
                _cleanup_free_ char *word = NULL, *prefix = NULL;
68,971✔
1173
                LogTarget target;
68,971✔
1174
                const char *colon;
68,971✔
1175

1176
                r = extract_first_word(&e, &word, ",", 0);
68,971✔
1177
                if (r < 0)
68,971✔
1178
                        return r;
1179
                if (r == 0)
68,971✔
1180
                        break;
1181

1182
                colon = strchr(word, ':');
43,783✔
1183
                if (!colon) {
43,783✔
1184
                        r = log_level_from_string(word);
25,188✔
1185
                        if (r < 0)
25,188✔
1186
                                return r;
1187

1188
                        log_set_max_level(r);
25,188✔
1189
                        continue;
25,188✔
1190
                }
1191

1192
                prefix = strndup(word, colon - word);
18,595✔
1193
                if (!prefix)
18,595✔
1194
                        return -ENOMEM;
1195

1196
                target = log_target_from_string(prefix);
18,595✔
1197
                if (target < 0)
18,595✔
1198
                        return target;
1199

1200
                if (target >= _LOG_TARGET_SINGLE_MAX)
18,595✔
1201
                        return -EINVAL;
1202

1203
                r = log_level_from_string(colon + 1);
18,595✔
1204
                if (r < 0)
18,595✔
1205
                        return r;
1206

1207
                log_target_max_level[target] = r;
18,595✔
1208
        }
1209

1210
        return 0;
25,188✔
1211
}
1212

1213
int log_max_levels_to_string(int level, char **ret) {
2,227✔
1214
        _cleanup_free_ char *s = NULL;
2,227✔
1215
        int r;
2,227✔
1216

1217
        assert(ret);
2,227✔
1218

1219
        r = log_level_to_string_alloc(level, &s);
2,227✔
1220
        if (r < 0)
2,227✔
1221
                return r;
1222

1223
        for (LogTarget target = 0; target < _LOG_TARGET_SINGLE_MAX; target++) {
11,135✔
1224
                _cleanup_free_ char *l = NULL;
2,227✔
1225

1226
                if (log_target_max_level[target] == INT_MAX)
8,908✔
1227
                        continue;
6,681✔
1228

1229
                r = log_level_to_string_alloc(log_target_max_level[target], &l);
2,227✔
1230
                if (r < 0)
2,227✔
1231
                        return r;
1232

1233
                r = strextendf_with_separator(&s, ",", "%s:%s", log_target_to_string(target), l);
2,227✔
1234
                if (r < 0)
2,227✔
1235
                        return r;
1236
        }
1237

1238
        *ret = TAKE_PTR(s);
2,227✔
1239
        return 0;
2,227✔
1240
}
1241

1242
static int log_set_ratelimit_kmsg_from_string(const char *e) {
7,027✔
1243
        int r;
7,027✔
1244

1245
        r = parse_boolean(e);
7,027✔
1246
        if (r < 0)
7,027✔
1247
                return r;
1248

1249
        ratelimit_kmsg = r;
7,027✔
1250
        return 0;
7,027✔
1251
}
1252

1253
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
251,217✔
1254

1255
        /*
1256
         * The systemd.log_xyz= settings are parsed by all tools, and
1257
         * so is "debug".
1258
         *
1259
         * However, "quiet" is only parsed by PID 1, and only turns of
1260
         * status output to /dev/console, but does not alter the log
1261
         * level.
1262
         */
1263

1264
        if (streq(key, "debug") && !value)
251,217✔
UNCOV
1265
                log_set_max_level(LOG_DEBUG);
×
1266

1267
        else if (proc_cmdline_key_streq(key, "systemd.log_target")) {
251,217✔
1268

UNCOV
1269
                if (proc_cmdline_value_missing(key, value))
×
1270
                        return 0;
1271

UNCOV
1272
                if (log_set_target_from_string(value) < 0)
×
UNCOV
1273
                        log_warning("Failed to parse log target '%s', ignoring.", value);
×
1274

1275
        } else if (proc_cmdline_key_streq(key, "systemd.log_level")) {
251,217✔
1276

1277
                if (proc_cmdline_value_missing(key, value))
7,027✔
1278
                        return 0;
1279

1280
                if (log_set_max_level_from_string(value) < 0)
7,027✔
1281
                        log_warning("Failed to parse log level setting '%s', ignoring.", value);
×
1282

1283
        } else if (proc_cmdline_key_streq(key, "systemd.log_color")) {
244,190✔
1284

UNCOV
1285
                if (log_show_color_from_string(value ?: "1") < 0)
×
UNCOV
1286
                        log_warning("Failed to parse log color setting '%s', ignoring.", value);
×
1287

1288
        } else if (proc_cmdline_key_streq(key, "systemd.log_location")) {
244,190✔
1289

1290
                if (log_show_location_from_string(value ?: "1") < 0)
×
UNCOV
1291
                        log_warning("Failed to parse log location setting '%s', ignoring.", value);
×
1292

1293
        } else if (proc_cmdline_key_streq(key, "systemd.log_tid")) {
244,190✔
1294

1295
                if (log_show_tid_from_string(value ?: "1") < 0)
×
UNCOV
1296
                        log_warning("Failed to parse log tid setting '%s', ignoring.", value);
×
1297

1298
        } else if (proc_cmdline_key_streq(key, "systemd.log_time")) {
244,190✔
1299

1300
                if (log_show_time_from_string(value ?: "1") < 0)
×
UNCOV
1301
                        log_warning("Failed to parse log time setting '%s', ignoring.", value);
×
1302

1303
        } else if (proc_cmdline_key_streq(key, "systemd.log_ratelimit_kmsg")) {
244,190✔
1304

1305
                if (log_set_ratelimit_kmsg_from_string(value ?: "1") < 0)
7,027✔
UNCOV
1306
                        log_warning("Failed to parse log ratelimit kmsg boolean '%s', ignoring.", value);
×
1307
        }
1308

1309
        return 0;
1310
}
1311

1312
static bool should_parse_proc_cmdline(void) {
90,656✔
1313
        /* PID1 always reads the kernel command line. */
1314
        if (getpid_cached() == 1)
90,656✔
1315
                return true;
1316

1317
        /* Otherwise, parse the command line if invoked directly by systemd. */
1318
        return invoked_by_systemd();
90,467✔
1319
}
1320

1321
void log_parse_environment_variables(void) {
92,676✔
1322
        const char *e;
92,676✔
1323
        int r;
92,676✔
1324

1325
        e = getenv("SYSTEMD_LOG_TARGET");
92,676✔
1326
        if (e && log_set_target_from_string(e) < 0)
92,676✔
UNCOV
1327
                log_warning("Failed to parse log target '%s', ignoring.", e);
×
1328

1329
        e = getenv("SYSTEMD_LOG_LEVEL");
92,676✔
1330
        if (e) {
92,676✔
1331
                r = log_set_max_level_from_string(e);
6,593✔
1332
                if (r < 0)
6,593✔
UNCOV
1333
                        log_warning_errno(r, "Failed to parse log level '%s', ignoring: %m", e);
×
1334
        } else {
1335
                /* If no explicit log level is specified then let's see if this is a debug invocation, and if
1336
                 * so raise the log level to debug too. Note that this is not symmetric: just because
1337
                 * DEBUG_INVOCATION is explicitly set to 0 we won't lower the log level below debug. This
1338
                 * follows the logic that debug logging is an opt-in thing anyway, and if there's any reason
1339
                 * to enable it we should not disable it here automatically. */
1340
                r = getenv_bool("DEBUG_INVOCATION");
86,083✔
1341
                if (r < 0 && r != -ENXIO)
86,083✔
1342
                        log_warning_errno(r, "Failed to parse $DEBUG_INVOCATION value, ignoring: %m");
×
1343
                else if (r > 0)
86,083✔
UNCOV
1344
                        log_set_max_level(LOG_DEBUG);
×
1345
        }
1346

1347
        e = getenv("SYSTEMD_LOG_COLOR");
92,676✔
1348
        if (e && log_show_color_from_string(e) < 0)
92,676✔
UNCOV
1349
                log_warning("Failed to parse log color '%s', ignoring.", e);
×
1350

1351
        e = getenv("SYSTEMD_LOG_LOCATION");
92,676✔
1352
        if (e && log_show_location_from_string(e) < 0)
92,676✔
1353
                log_warning("Failed to parse log location '%s', ignoring.", e);
×
1354

1355
        e = getenv("SYSTEMD_LOG_TIME");
92,676✔
1356
        if (e && log_show_time_from_string(e) < 0)
92,676✔
UNCOV
1357
                log_warning("Failed to parse log time '%s', ignoring.", e);
×
1358

1359
        e = getenv("SYSTEMD_LOG_TID");
92,676✔
1360
        if (e && log_show_tid_from_string(e) < 0)
92,676✔
UNCOV
1361
                log_warning("Failed to parse log tid '%s', ignoring.", e);
×
1362

1363
        e = getenv("SYSTEMD_LOG_RATELIMIT_KMSG");
92,676✔
1364
        if (e && log_set_ratelimit_kmsg_from_string(e) < 0)
92,676✔
UNCOV
1365
                log_warning("Failed to parse log ratelimit kmsg boolean '%s', ignoring.", e);
×
1366
}
92,676✔
1367

1368
void log_parse_environment(void) {
90,656✔
1369
        /* Do not call from library code. */
1370

1371
        if (should_parse_proc_cmdline())
90,656✔
1372
                (void) proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
7,100✔
1373

1374
        log_parse_environment_variables();
90,656✔
1375
}
90,656✔
1376

1377
LogTarget log_get_target(void) {
16,354✔
1378
        return log_target;
16,354✔
1379
}
1380

1381
void log_settle_target(void) {
14,387✔
1382

1383
        /* If we're using LOG_TARGET_AUTO and opening the log again on every single log call, we'll check if
1384
         * stderr is attached to the journal every single log call. However, if we then close all file
1385
         * descriptors later, that will stop working because stderr will be closed as well. To avoid that
1386
         * problem, this function is used to permanently change the log target depending on whether stderr is
1387
         * connected to the journal or not. */
1388

1389
        LogTarget t = log_get_target();
14,387✔
1390

1391
        if (t != LOG_TARGET_AUTO)
14,387✔
1392
                return;
1393

1394
        t = getpid_cached() == 1 || stderr_is_journal() ? (prohibit_ipc ? LOG_TARGET_KMSG : LOG_TARGET_JOURNAL_OR_KMSG)
10,108✔
1395
                                                        : LOG_TARGET_CONSOLE;
6,695✔
1396
        log_set_target(t);
3,413✔
1397
}
1398

1399
int log_get_max_level(void) {
13,754,302✔
1400
        return log_max_level;
13,754,302✔
1401
}
1402

1403
int log_get_target_max_level(LogTarget target) {
4✔
1404
        assert(target >= 0);
4✔
1405
        assert(target < _LOG_TARGET_SINGLE_MAX);
4✔
1406
        return log_target_max_level[target];
4✔
1407
}
1408

1409
void log_show_color(bool b) {
81,872✔
1410
        show_color = b;
81,872✔
1411
}
81,872✔
1412

1413
bool log_get_show_color(void) {
189,987✔
1414
        return show_color > 0; /* Defaults to false. */
189,987✔
1415
}
1416

UNCOV
1417
void log_show_location(bool b) {
×
UNCOV
1418
        show_location = b;
×
UNCOV
1419
}
×
1420

1421
bool log_get_show_location(void) {
14✔
1422
        return show_location;
14✔
1423
}
1424

1425
void log_show_time(bool b) {
1✔
1426
        show_time = b;
1✔
1427
}
1✔
1428

1429
bool log_get_show_time(void) {
14✔
1430
        return show_time;
14✔
1431
}
1432

1433
void log_show_tid(bool b) {
1✔
1434
        show_tid = b;
1✔
1435
}
1✔
1436

UNCOV
1437
bool log_get_show_tid(void) {
×
UNCOV
1438
        return show_tid;
×
1439
}
1440

UNCOV
1441
int log_show_color_from_string(const char *e) {
×
UNCOV
1442
        int r;
×
1443

UNCOV
1444
        r = parse_boolean(e);
×
UNCOV
1445
        if (r < 0)
×
1446
                return r;
1447

UNCOV
1448
        log_show_color(r);
×
UNCOV
1449
        return 0;
×
1450
}
1451

UNCOV
1452
int log_show_location_from_string(const char *e) {
×
1453
        int r;
×
1454

UNCOV
1455
        r = parse_boolean(e);
×
UNCOV
1456
        if (r < 0)
×
1457
                return r;
1458

UNCOV
1459
        log_show_location(r);
×
UNCOV
1460
        return 0;
×
1461
}
1462

UNCOV
1463
int log_show_time_from_string(const char *e) {
×
1464
        int r;
×
1465

UNCOV
1466
        r = parse_boolean(e);
×
UNCOV
1467
        if (r < 0)
×
1468
                return r;
1469

UNCOV
1470
        log_show_time(r);
×
UNCOV
1471
        return 0;
×
1472
}
1473

UNCOV
1474
int log_show_tid_from_string(const char *e) {
×
1475
        int r;
×
1476

UNCOV
1477
        r = parse_boolean(e);
×
UNCOV
1478
        if (r < 0)
×
1479
                return r;
1480

UNCOV
1481
        log_show_tid(r);
×
UNCOV
1482
        return 0;
×
1483
}
1484

1485
bool log_on_console(void) {
106,386✔
1486
        if (IN_SET(log_target, LOG_TARGET_CONSOLE,
106,386✔
1487
                               LOG_TARGET_CONSOLE_PREFIXED))
1488
                return true;
1489

1490
        return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0;
129,487✔
1491
}
1492

1493
static const char *const log_target_table[_LOG_TARGET_MAX] = {
1494
        [LOG_TARGET_CONSOLE]          = "console",
1495
        [LOG_TARGET_CONSOLE_PREFIXED] = "console-prefixed",
1496
        [LOG_TARGET_KMSG]             = "kmsg",
1497
        [LOG_TARGET_JOURNAL]          = "journal",
1498
        [LOG_TARGET_JOURNAL_OR_KMSG]  = "journal-or-kmsg",
1499
        [LOG_TARGET_SYSLOG]           = "syslog",
1500
        [LOG_TARGET_SYSLOG_OR_KMSG]   = "syslog-or-kmsg",
1501
        [LOG_TARGET_AUTO]             = "auto",
1502
        [LOG_TARGET_NULL]             = "null",
1503
};
1504

1505
DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
35,351✔
1506

1507
void log_received_signal(int level, const struct signalfd_siginfo *si) {
3,615✔
1508
        assert(si);
3,615✔
1509

1510
        if (pid_is_valid(si->ssi_pid)) {
3,615✔
1511
                _cleanup_free_ char *p = NULL;
3,615✔
1512

1513
                (void) pid_get_comm(si->ssi_pid, &p);
3,615✔
1514

1515
                log_full(level,
4,359✔
1516
                         "Received SIG%s from PID %"PRIu32" (%s).",
1517
                         signal_to_string(si->ssi_signo),
1518
                         si->ssi_pid, strna(p));
1519
        } else
UNCOV
1520
                log_full(level,
×
1521
                         "Received SIG%s.",
1522
                         signal_to_string(si->ssi_signo));
1523
}
3,615✔
1524

1525
void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata) {
858✔
1526
        assert(!log_syntax_callback || !cb);
858✔
1527
        assert(!log_syntax_callback_userdata || !userdata);
858✔
1528

1529
        log_syntax_callback = cb;
858✔
1530
        log_syntax_callback_userdata = userdata;
858✔
1531
}
858✔
1532

1533
int log_syntax_internal(
11,551✔
1534
                const char *unit,
1535
                int level,
1536
                const char *config_file,
1537
                unsigned config_line,
1538
                int error,
1539
                const char *file,
1540
                int line,
1541
                const char *func,
1542
                const char *format, ...) {
1543

1544
        PROTECT_ERRNO;
11,551✔
1545

1546
        if (log_syntax_callback)
11,551✔
1547
                log_syntax_callback(unit, level, log_syntax_callback_userdata);
846✔
1548

1549
        if (_likely_(LOG_PRI(level) > log_max_level) ||
11,551✔
1550
            log_target == LOG_TARGET_NULL)
11,551✔
1551
                return -ERRNO_VALUE(error);
30✔
1552

1553
        char buffer[LINE_MAX];
11,521✔
1554
        va_list ap;
11,521✔
1555
        const char *unit_fmt = NULL;
11,521✔
1556

1557
        errno = ERRNO_VALUE(error);
11,521✔
1558

1559
        va_start(ap, format);
11,521✔
1560
        (void) vsnprintf(buffer, sizeof buffer, format, ap);
11,521✔
1561
        va_end(ap);
11,521✔
1562

1563
        if (unit)
11,521✔
1564
                unit_fmt = getpid_cached() == 1 ? "UNIT=%s" : "USER_UNIT=%s";
4,883✔
1565

1566
        if (config_file) {
11,521✔
1567
                if (config_line > 0)
11,511✔
1568
                        return log_struct_internal(
5,970✔
1569
                                        level,
1570
                                        error,
1571
                                        file, line, func,
1572
                                        LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION_STR),
5,970✔
1573
                                        LOG_ITEM("CONFIG_FILE=%s", config_file),
5,970✔
1574
                                        LOG_ITEM("CONFIG_LINE=%u", config_line),
5,970✔
1575
                                        LOG_MESSAGE("%s:%u: %s", config_file, config_line, buffer),
5,970✔
1576
                                        unit_fmt, unit,
1577
                                        NULL);
1578
                else
1579
                        return log_struct_internal(
5,541✔
1580
                                        level,
1581
                                        error,
1582
                                        file, line, func,
1583
                                        LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION_STR),
5,541✔
1584
                                        LOG_ITEM("CONFIG_FILE=%s", config_file),
5,541✔
1585
                                        LOG_MESSAGE("%s: %s", config_file, buffer),
5,541✔
1586
                                        unit_fmt, unit,
1587
                                        NULL);
1588
        } else if (unit)
10✔
UNCOV
1589
                return log_struct_internal(
×
1590
                                level,
1591
                                error,
1592
                                file, line, func,
UNCOV
1593
                                LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION_STR),
×
UNCOV
1594
                                LOG_MESSAGE("%s: %s", unit, buffer),
×
1595
                                unit_fmt, unit,
1596
                                NULL);
1597
        else
1598
                return log_struct_internal(
10✔
1599
                                level,
1600
                                error,
1601
                                file, line, func,
1602
                                LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION_STR),
10✔
1603
                                LOG_MESSAGE("%s", buffer),
10✔
1604
                                NULL);
1605
}
1606

1607
int log_syntax_invalid_utf8_internal(
1✔
1608
                const char *unit,
1609
                int level,
1610
                const char *config_file,
1611
                unsigned config_line,
1612
                const char *file,
1613
                int line,
1614
                const char *func,
1615
                const char *rvalue) {
1616

UNCOV
1617
        PROTECT_ERRNO;
×
1618
        _cleanup_free_ char *p = NULL;
1✔
1619

1620
        if (rvalue)
1✔
1621
                p = utf8_escape_invalid(rvalue);
1✔
1622

1623
        return log_syntax_internal(unit, level, config_file, config_line,
1✔
1624
                                   SYNTHETIC_ERRNO(EINVAL), file, line, func,
1625
                                   "String is not UTF-8 clean, ignoring assignment: %s", strna(p));
1626
}
1627

1628
int log_syntax_parse_error_internal(
110✔
1629
                const char *unit,
1630
                const char *config_file,
1631
                unsigned config_line,
1632
                int error,
1633
                bool critical,
1634
                const char *file,
1635
                int line,
1636
                const char *func,
1637
                const char *lvalue,
1638
                const char *rvalue) {
1639

UNCOV
1640
        PROTECT_ERRNO;
×
1641
        _cleanup_free_ char *escaped = NULL;
110✔
1642

1643
        /* OOM is always handled as critical. */
1644
        if (ERRNO_VALUE(error) == ENOMEM)
110✔
UNCOV
1645
                return log_oom_internal(LOG_ERR, file, line, func);
×
1646

1647
        if (rvalue && !utf8_is_valid(rvalue)) {
220✔
UNCOV
1648
                escaped = utf8_escape_invalid(rvalue);
×
1649
                if (!escaped)
×
1650
                        rvalue = "(oom)";
1651
                else
UNCOV
1652
                        rvalue = " (escaped)";
×
1653
        }
1654

1655
        log_syntax_internal(unit, critical ? LOG_ERR : LOG_WARNING, config_file, config_line, error,
550✔
1656
                            file, line, func,
1657
                            "Failed to parse %s=%s%s%s%s%s",
1658
                            strna(lvalue), strempty(escaped), strempty(rvalue),
1659
                            critical ? "" : ", ignoring",
1660
                            error == 0 ? "." : ": ",
1661
                            error == 0 ? "" : STRERROR(error));
110✔
1662

1663
        return critical ? -ERRNO_VALUE(error) : 0;
110✔
1664
}
1665

1666
void log_set_upgrade_syslog_to_journal(bool b) {
234✔
1667
        upgrade_syslog_to_journal = b;
234✔
1668

1669
        /* Make the change effective immediately */
1670
        if (b) {
234✔
1671
                if (log_target == LOG_TARGET_SYSLOG)
234✔
UNCOV
1672
                        log_target = LOG_TARGET_JOURNAL;
×
1673
                else if (log_target == LOG_TARGET_SYSLOG_OR_KMSG)
234✔
UNCOV
1674
                        log_target = LOG_TARGET_JOURNAL_OR_KMSG;
×
1675
        }
1676
}
234✔
1677

1678
void log_set_always_reopen_console(bool b) {
11,802✔
1679
        always_reopen_console = b;
11,802✔
1680
}
11,802✔
1681

1682
void log_set_open_when_needed(bool b) {
18,696✔
1683
        open_when_needed = b;
18,696✔
1684
}
18,696✔
1685

1686
void log_set_prohibit_ipc(bool b) {
34,058✔
1687
        prohibit_ipc = b;
34,058✔
1688
}
34,058✔
1689

UNCOV
1690
int log_emergency_level(void) {
×
1691
        /* Returns the log level to use for log_emergency() logging. We use LOG_EMERG only when we are PID 1, as only
1692
         * then the system of the whole system is obviously affected. */
1693

UNCOV
1694
        return getpid_cached() == 1 ? LOG_EMERG : LOG_ERR;
×
1695
}
1696

1697
int log_dup_console(void) {
99✔
1698
        int copy;
99✔
1699

1700
        /* Duplicate the fd we use for fd logging if it's < 3 and use the copy from now on. This call is useful
1701
         * whenever we want to continue logging through the original fd, but want to rearrange stderr. */
1702

1703
        if (console_fd < 0 || console_fd >= 3)
99✔
1704
                return 0;
1705

1706
        copy = fcntl(console_fd, F_DUPFD_CLOEXEC, 3);
1✔
1707
        if (copy < 0)
1✔
UNCOV
1708
                return -errno;
×
1709

1710
        console_fd = copy;
1✔
1711
        return 0;
1✔
1712
}
1713

1714
void log_setup(void) {
89,850✔
1715
        log_set_target(LOG_TARGET_AUTO);
89,850✔
1716
        log_parse_environment();
89,850✔
1717
        (void) log_open();
89,850✔
1718
        if (log_on_console() && show_color < 0)
89,850✔
1719
                log_show_color(true);
81,769✔
1720
}
89,850✔
1721

1722
const char* _log_set_prefix(const char *prefix, bool force) {
3,425,156✔
1723
        const char *old = log_prefix;
3,425,156✔
1724

1725
        if (prefix || force)
3,425,156✔
1726
                log_prefix = prefix;
3,423,269✔
1727

1728
        return old;
3,425,156✔
1729
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc