Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <fcntl.h>
5 : : #include <signal.h>
6 : : #include <stdint.h>
7 : : #include <stdlib.h>
8 : : #include <string.h>
9 : : #include <sys/socket.h>
10 : : #include <syslog.h>
11 : : #include <time.h>
12 : : #include <unistd.h>
13 : :
14 : : #include "sd-id128.h"
15 : : #include "sd-journal.h"
16 : :
17 : : #include "alloc-util.h"
18 : : #include "fd-util.h"
19 : : #include "format-util.h"
20 : : #include "hashmap.h"
21 : : #include "hostname-util.h"
22 : : #include "io-util.h"
23 : : #include "journal-internal.h"
24 : : #include "json.h"
25 : : #include "log.h"
26 : : #include "logs-show.h"
27 : : #include "macro.h"
28 : : #include "namespace-util.h"
29 : : #include "output-mode.h"
30 : : #include "parse-util.h"
31 : : #include "process-util.h"
32 : : #include "pretty-print.h"
33 : : #include "sparse-endian.h"
34 : : #include "stdio-util.h"
35 : : #include "string-table.h"
36 : : #include "string-util.h"
37 : : #include "strv.h"
38 : : #include "terminal-util.h"
39 : : #include "time-util.h"
40 : : #include "utf8.h"
41 : : #include "util.h"
42 : :
43 : : /* up to three lines (each up to 100 characters) or 300 characters, whichever is less */
44 : : #define PRINT_LINE_THRESHOLD 3
45 : : #define PRINT_CHAR_THRESHOLD 300
46 : :
47 : : #define JSON_THRESHOLD 4096U
48 : :
49 : 0 : static int print_catalog(FILE *f, sd_journal *j) {
50 : : int r;
51 : 0 : _cleanup_free_ char *t = NULL, *z = NULL;
52 : :
53 : 0 : r = sd_journal_get_catalog(j, &t);
54 [ # # ]: 0 : if (r < 0)
55 : 0 : return r;
56 : :
57 : 0 : z = strreplace(strstrip(t), "\n", "\n-- ");
58 [ # # ]: 0 : if (!z)
59 : 0 : return log_oom();
60 : :
61 : 0 : fputs("-- ", f);
62 : 0 : fputs(z, f);
63 : 0 : fputc('\n', f);
64 : :
65 : 0 : return 0;
66 : : }
67 : :
68 : 0 : static int parse_field(const void *data, size_t length, const char *field, size_t field_len, char **target, size_t *target_len) {
69 : : size_t nl;
70 : : char *buf;
71 : :
72 [ # # ]: 0 : assert(data);
73 [ # # ]: 0 : assert(field);
74 [ # # ]: 0 : assert(target);
75 : :
76 [ # # ]: 0 : if (length < field_len)
77 : 0 : return 0;
78 : :
79 [ # # ]: 0 : if (memcmp(data, field, field_len))
80 : 0 : return 0;
81 : :
82 : 0 : nl = length - field_len;
83 : :
84 : 0 : buf = newdup_suffix0(char, (const char*) data + field_len, nl);
85 [ # # ]: 0 : if (!buf)
86 : 0 : return log_oom();
87 : :
88 : 0 : free(*target);
89 : 0 : *target = buf;
90 : :
91 [ # # ]: 0 : if (target_len)
92 : 0 : *target_len = nl;
93 : :
94 : 0 : return 1;
95 : : }
96 : :
97 : : typedef struct ParseFieldVec {
98 : : const char *field;
99 : : size_t field_len;
100 : : char **target;
101 : : size_t *target_len;
102 : : } ParseFieldVec;
103 : :
104 : : #define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) \
105 : : { .field = _field, .field_len = strlen(_field), .target = _target, .target_len = _target_len }
106 : :
107 : 0 : static int parse_fieldv(const void *data, size_t length, const ParseFieldVec *fields, unsigned n_fields) {
108 : : unsigned i;
109 : :
110 [ # # ]: 0 : for (i = 0; i < n_fields; i++) {
111 : 0 : const ParseFieldVec *f = &fields[i];
112 : : int r;
113 : :
114 : 0 : r = parse_field(data, length, f->field, f->field_len, f->target, f->target_len);
115 [ # # ]: 0 : if (r < 0)
116 : 0 : return r;
117 [ # # ]: 0 : else if (r > 0)
118 : 0 : break;
119 : : }
120 : :
121 : 0 : return 0;
122 : : }
123 : :
124 : 0 : static int field_set_test(Set *fields, const char *name, size_t n) {
125 : 0 : char *s = NULL;
126 : :
127 [ # # ]: 0 : if (!fields)
128 : 0 : return 1;
129 : :
130 : 0 : s = strndupa(name, n);
131 [ # # ]: 0 : if (!s)
132 : 0 : return log_oom();
133 : :
134 : 0 : return set_get(fields, s) ? 1 : 0;
135 : : }
136 : :
137 : 0 : static bool shall_print(const char *p, size_t l, OutputFlags flags) {
138 [ # # ]: 0 : assert(p);
139 : :
140 [ # # ]: 0 : if (flags & OUTPUT_SHOW_ALL)
141 : 0 : return true;
142 : :
143 [ # # ]: 0 : if (l >= PRINT_CHAR_THRESHOLD)
144 : 0 : return false;
145 : :
146 [ # # ]: 0 : if (!utf8_is_printable(p, l))
147 : 0 : return false;
148 : :
149 : 0 : return true;
150 : : }
151 : :
152 : 0 : static bool print_multiline(
153 : : FILE *f,
154 : : unsigned prefix,
155 : : unsigned n_columns,
156 : : OutputFlags flags,
157 : : int priority,
158 : : bool audit,
159 : : const char* message,
160 : : size_t message_len,
161 : : size_t highlight[2]) {
162 : :
163 : 0 : const char *color_on = "", *color_off = "", *highlight_on = "";
164 : : const char *pos, *end;
165 : 0 : bool ellipsized = false;
166 : 0 : int line = 0;
167 : :
168 [ # # ]: 0 : if (flags & OUTPUT_COLOR) {
169 : 0 : get_log_colors(priority, &color_on, &color_off, &highlight_on);
170 : :
171 [ # # # # ]: 0 : if (audit && strempty(color_on)) {
172 : 0 : color_on = ANSI_BLUE;
173 : 0 : color_off = ANSI_NORMAL;
174 : : }
175 : : }
176 : :
177 : : /* A special case: make sure that we print a newline when
178 : : the message is empty. */
179 [ # # ]: 0 : if (message_len == 0)
180 : 0 : fputs("\n", f);
181 : :
182 : 0 : for (pos = message;
183 [ # # ]: 0 : pos < message + message_len;
184 : 0 : pos = end + 1, line++) {
185 : 0 : bool continuation = line > 0;
186 : : bool tail_line;
187 : : int len;
188 [ # # # # ]: 0 : for (end = pos; end < message + message_len && *end != '\n'; end++)
189 : : ;
190 : 0 : len = end - pos;
191 [ # # ]: 0 : assert(len >= 0);
192 : :
193 : : /* We need to figure out when we are showing not-last line, *and*
194 : : * will skip subsequent lines. In that case, we will put the dots
195 : : * at the end of the line, instead of putting dots in the middle
196 : : * or not at all.
197 : : */
198 : 0 : tail_line =
199 [ # # ]: 0 : line + 1 == PRINT_LINE_THRESHOLD ||
200 [ # # ]: 0 : end + 1 >= message + PRINT_CHAR_THRESHOLD;
201 : :
202 [ # # ]: 0 : if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
203 [ # # # # ]: 0 : (prefix + len + 1 < n_columns && !tail_line)) {
204 [ # # ]: 0 : if (highlight &&
205 [ # # ]: 0 : (size_t) (pos - message) <= highlight[0] &&
206 [ # # ]: 0 : highlight[0] < (size_t) len) {
207 : :
208 : 0 : fprintf(f, "%*s%s%.*s",
209 : : continuation * prefix, "",
210 : 0 : color_on, (int) highlight[0], pos);
211 : 0 : fprintf(f, "%s%.*s",
212 : : highlight_on,
213 : 0 : (int) (MIN((size_t) len, highlight[1]) - highlight[0]),
214 : 0 : pos + highlight[0]);
215 [ # # ]: 0 : if ((size_t) len > highlight[1])
216 : 0 : fprintf(f, "%s%.*s",
217 : : color_on,
218 : 0 : (int) (len - highlight[1]),
219 : 0 : pos + highlight[1]);
220 : 0 : fprintf(f, "%s\n", color_off);
221 : :
222 : : } else
223 : 0 : fprintf(f, "%*s%s%.*s%s\n",
224 : : continuation * prefix, "",
225 : : color_on, len, pos, color_off);
226 : 0 : continue;
227 : : }
228 : :
229 : : /* Beyond this point, ellipsization will happen. */
230 : 0 : ellipsized = true;
231 : :
232 [ # # # # ]: 0 : if (prefix < n_columns && n_columns - prefix >= 3) {
233 [ # # ]: 0 : if (n_columns - prefix > (unsigned) len + 3)
234 : 0 : fprintf(f, "%*s%s%.*s...%s\n",
235 : : continuation * prefix, "",
236 : : color_on, len, pos, color_off);
237 : : else {
238 : 0 : _cleanup_free_ char *e;
239 : :
240 [ # # ]: 0 : e = ellipsize_mem(pos, len, n_columns - prefix,
241 : : tail_line ? 100 : 90);
242 [ # # ]: 0 : if (!e)
243 : 0 : fprintf(f, "%*s%s%.*s%s\n",
244 : : continuation * prefix, "",
245 : : color_on, len, pos, color_off);
246 : : else
247 : 0 : fprintf(f, "%*s%s%s%s\n",
248 : : continuation * prefix, "",
249 : : color_on, e, color_off);
250 : : }
251 : : } else
252 : 0 : fputs("...\n", f);
253 : :
254 [ # # ]: 0 : if (tail_line)
255 : 0 : break;
256 : : }
257 : :
258 : 0 : return ellipsized;
259 : : }
260 : :
261 : 0 : static int output_timestamp_monotonic(FILE *f, sd_journal *j, const char *monotonic) {
262 : : sd_id128_t boot_id;
263 : : uint64_t t;
264 : : int r;
265 : :
266 [ # # ]: 0 : assert(f);
267 [ # # ]: 0 : assert(j);
268 : :
269 : 0 : r = -ENXIO;
270 [ # # ]: 0 : if (monotonic)
271 : 0 : r = safe_atou64(monotonic, &t);
272 [ # # ]: 0 : if (r < 0)
273 : 0 : r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
274 [ # # ]: 0 : if (r < 0)
275 [ # # ]: 0 : return log_error_errno(r, "Failed to get monotonic timestamp: %m");
276 : :
277 : 0 : fprintf(f, "[%5"PRI_USEC".%06"PRI_USEC"]", t / USEC_PER_SEC, t % USEC_PER_SEC);
278 : 0 : return 1 + 5 + 1 + 6 + 1;
279 : : }
280 : :
281 : 0 : static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, OutputFlags flags, const char *realtime) {
282 : 0 : char buf[MAX(FORMAT_TIMESTAMP_MAX, 64)];
283 : : struct tm *(*gettime_r)(const time_t *, struct tm *);
284 : : struct tm tm;
285 : : uint64_t x;
286 : : time_t t;
287 : : int r;
288 : :
289 [ # # ]: 0 : assert(f);
290 [ # # ]: 0 : assert(j);
291 : :
292 [ # # ]: 0 : if (realtime)
293 : 0 : r = safe_atou64(realtime, &x);
294 [ # # # # : 0 : if (!realtime || r < 0 || !VALID_REALTIME(x))
# # ]
295 : 0 : r = sd_journal_get_realtime_usec(j, &x);
296 [ # # ]: 0 : if (r < 0)
297 [ # # ]: 0 : return log_error_errno(r, "Failed to get realtime timestamp: %m");
298 : :
299 [ # # # # ]: 0 : if (IN_SET(mode, OUTPUT_SHORT_FULL, OUTPUT_WITH_UNIT)) {
300 : : const char *k;
301 : :
302 [ # # ]: 0 : if (flags & OUTPUT_UTC)
303 : 0 : k = format_timestamp_utc(buf, sizeof(buf), x);
304 : : else
305 : 0 : k = format_timestamp(buf, sizeof(buf), x);
306 [ # # ]: 0 : if (!k)
307 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
308 : : "Failed to format timestamp: %" PRIu64, x);
309 : :
310 : : } else {
311 : : char usec[7];
312 : :
313 [ # # ]: 0 : gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r;
314 : 0 : t = (time_t) (x / USEC_PER_SEC);
315 : :
316 [ # # # # : 0 : switch (mode) {
# ]
317 : :
318 : 0 : case OUTPUT_SHORT_UNIX:
319 [ # # ]: 0 : xsprintf(buf, "%10"PRI_TIME".%06"PRIu64, t, x % USEC_PER_SEC);
320 : 0 : break;
321 : :
322 : 0 : case OUTPUT_SHORT_ISO:
323 [ # # ]: 0 : if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0)
324 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
325 : : "Failed to format ISO time");
326 : 0 : break;
327 : :
328 : 0 : case OUTPUT_SHORT_ISO_PRECISE:
329 : : /* No usec in strftime, so we leave space and copy over */
330 [ # # ]: 0 : if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t, &tm)) <= 0)
331 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
332 : : "Failed to format ISO-precise time");
333 [ # # ]: 0 : xsprintf(usec, "%06"PRI_USEC, x % USEC_PER_SEC);
334 : 0 : memcpy(buf + 20, usec, 6);
335 : 0 : break;
336 : :
337 : 0 : case OUTPUT_SHORT:
338 : : case OUTPUT_SHORT_PRECISE:
339 : :
340 [ # # ]: 0 : if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0)
341 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
342 : : "Failed to format syslog time");
343 : :
344 [ # # ]: 0 : if (mode == OUTPUT_SHORT_PRECISE) {
345 : : size_t k;
346 : :
347 [ # # ]: 0 : assert(sizeof(buf) > strlen(buf));
348 : 0 : k = sizeof(buf) - strlen(buf);
349 : :
350 : 0 : r = snprintf(buf + strlen(buf), k, ".%06"PRIu64, x % USEC_PER_SEC);
351 [ # # # # ]: 0 : if (r <= 0 || (size_t) r >= k) /* too long? */
352 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
353 : : "Failed to format precise time");
354 : : }
355 : 0 : break;
356 : :
357 : 0 : default:
358 : 0 : assert_not_reached("Unknown time format");
359 : : }
360 : : }
361 : :
362 : 0 : fputs(buf, f);
363 : 0 : return (int) strlen(buf);
364 : : }
365 : :
366 : 0 : static int output_short(
367 : : FILE *f,
368 : : sd_journal *j,
369 : : OutputMode mode,
370 : : unsigned n_columns,
371 : : OutputFlags flags,
372 : : Set *output_fields,
373 : : const size_t highlight[2]) {
374 : :
375 : : int r;
376 : : const void *data;
377 : : size_t length;
378 : 0 : size_t n = 0;
379 : 0 : _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *transport = NULL, *config_file = NULL, *unit = NULL, *user_unit = NULL;
380 : 0 : size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0, transport_len = 0, config_file_len = 0, unit_len = 0, user_unit_len = 0;
381 : 0 : int p = LOG_INFO;
382 : 0 : bool ellipsized = false, audit;
383 : 0 : const ParseFieldVec fields[] = {
384 : : PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len),
385 : : PARSE_FIELD_VEC_ENTRY("_COMM=", &comm, &comm_len),
386 : : PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len),
387 : : PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len),
388 : : PARSE_FIELD_VEC_ENTRY("_TRANSPORT=", &transport, &transport_len),
389 : : PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len),
390 : : PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len),
391 : : PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len),
392 : : PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
393 : : PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
394 : : PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len),
395 : : PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len),
396 : : PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len),
397 : : };
398 [ # # # # ]: 0 : size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
399 : :
400 [ # # ]: 0 : assert(f);
401 [ # # ]: 0 : assert(j);
402 : :
403 : : /* Set the threshold to one bigger than the actual print
404 : : * threshold, so that if the line is actually longer than what
405 : : * we're willing to print, ellipsization will occur. This way
406 : : * we won't output a misleading line without any indication of
407 : : * truncation.
408 : : */
409 [ # # ]: 0 : sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1);
410 : :
411 [ # # ]: 0 : JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
412 : 0 : r = parse_fieldv(data, length, fields, ELEMENTSOF(fields));
413 [ # # ]: 0 : if (r < 0)
414 : 0 : return r;
415 : : }
416 [ # # ]: 0 : if (r == -EBADMSG) {
417 [ # # ]: 0 : log_debug_errno(r, "Skipping message we can't read: %m");
418 : 0 : return 0;
419 : : }
420 [ # # ]: 0 : if (r < 0)
421 [ # # ]: 0 : return log_error_errno(r, "Failed to get journal fields: %m");
422 : :
423 [ # # ]: 0 : if (!message) {
424 [ # # ]: 0 : log_debug("Skipping message without MESSAGE= field.");
425 : 0 : return 0;
426 : : }
427 : :
428 [ # # ]: 0 : if (!(flags & OUTPUT_SHOW_ALL))
429 : 0 : strip_tab_ansi(&message, &message_len, highlight_shifted);
430 : :
431 [ # # # # : 0 : if (priority_len == 1 && *priority >= '0' && *priority <= '7')
# # ]
432 : 0 : p = *priority - '0';
433 : :
434 : 0 : audit = streq_ptr(transport, "audit");
435 : :
436 [ # # ]: 0 : if (mode == OUTPUT_SHORT_MONOTONIC)
437 : 0 : r = output_timestamp_monotonic(f, j, monotonic);
438 : : else
439 : 0 : r = output_timestamp_realtime(f, j, mode, flags, realtime);
440 [ # # ]: 0 : if (r < 0)
441 : 0 : return r;
442 : 0 : n += r;
443 : :
444 [ # # ]: 0 : if (flags & OUTPUT_NO_HOSTNAME) {
445 : : /* Suppress display of the hostname if this is requested. */
446 : 0 : hostname = mfree(hostname);
447 : 0 : hostname_len = 0;
448 : : }
449 : :
450 [ # # # # ]: 0 : if (hostname && shall_print(hostname, hostname_len, flags)) {
451 : 0 : fprintf(f, " %.*s", (int) hostname_len, hostname);
452 : 0 : n += hostname_len + 1;
453 : : }
454 : :
455 [ # # # # : 0 : if (mode == OUTPUT_WITH_UNIT && ((unit && shall_print(unit, unit_len, flags)) ||
# # ]
456 [ # # # # ]: 0 : (user_unit && shall_print(user_unit, user_unit_len, flags)))) {
457 [ # # ]: 0 : if (unit) {
458 : 0 : fprintf(f, " %.*s", (int) unit_len, unit);
459 : 0 : n += unit_len + 1;
460 : : }
461 [ # # ]: 0 : if (user_unit) {
462 [ # # ]: 0 : if (unit)
463 : 0 : fprintf(f, "/%.*s", (int) user_unit_len, user_unit);
464 : : else
465 : 0 : fprintf(f, " %.*s", (int) user_unit_len, user_unit);
466 : 0 : n += unit_len + 1;
467 : : }
468 [ # # # # ]: 0 : } else if (identifier && shall_print(identifier, identifier_len, flags)) {
469 : 0 : fprintf(f, " %.*s", (int) identifier_len, identifier);
470 : 0 : n += identifier_len + 1;
471 [ # # # # ]: 0 : } else if (comm && shall_print(comm, comm_len, flags)) {
472 : 0 : fprintf(f, " %.*s", (int) comm_len, comm);
473 : 0 : n += comm_len + 1;
474 : : } else
475 : 0 : fputs(" unknown", f);
476 : :
477 [ # # # # ]: 0 : if (pid && shall_print(pid, pid_len, flags)) {
478 : 0 : fprintf(f, "[%.*s]", (int) pid_len, pid);
479 : 0 : n += pid_len + 2;
480 [ # # # # ]: 0 : } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
481 : 0 : fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
482 : 0 : n += fake_pid_len + 2;
483 : : }
484 : :
485 [ # # # # ]: 0 : if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
486 : : char bytes[FORMAT_BYTES_MAX];
487 : 0 : fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
488 : : } else {
489 : 0 : fputs(": ", f);
490 : :
491 : : /* URLify config_file string in message, if the message starts with it.
492 : : * Skip URLification if the highlighted pattern overlaps. */
493 [ # # ]: 0 : if (config_file &&
494 [ # # ]: 0 : message_len >= config_file_len &&
495 [ # # ]: 0 : memcmp(message, config_file, config_file_len) == 0 &&
496 [ # # # # : 0 : IN_SET(message[config_file_len], ':', ' ', '\0') &&
# # ]
497 [ # # # # ]: 0 : (!highlight || highlight_shifted[0] == 0 || highlight_shifted[0] > config_file_len)) {
498 : :
499 : 0 : _cleanup_free_ char *t = NULL, *urlified = NULL;
500 : :
501 : 0 : t = strndup(config_file, config_file_len);
502 [ # # # # ]: 0 : if (t && terminal_urlify_path(t, NULL, &urlified) >= 0) {
503 : 0 : size_t shift = strlen(urlified) - config_file_len;
504 : : char *joined;
505 : :
506 : 0 : joined = strjoin(urlified, message + config_file_len);
507 [ # # ]: 0 : if (joined) {
508 : 0 : free_and_replace(message, joined);
509 : 0 : message_len += shift;
510 [ # # ]: 0 : if (highlight) {
511 : 0 : highlight_shifted[0] += shift;
512 : 0 : highlight_shifted[1] += shift;
513 : : }
514 : : }
515 : : }
516 : : }
517 : :
518 : 0 : ellipsized |=
519 : 0 : print_multiline(f, n + 2, n_columns, flags, p, audit,
520 : : message, message_len,
521 : : highlight_shifted);
522 : : }
523 : :
524 [ # # ]: 0 : if (flags & OUTPUT_CATALOG)
525 : 0 : print_catalog(f, j);
526 : :
527 : 0 : return ellipsized;
528 : : }
529 : :
530 : 0 : static int output_verbose(
531 : : FILE *f,
532 : : sd_journal *j,
533 : : OutputMode mode,
534 : : unsigned n_columns,
535 : : OutputFlags flags,
536 : : Set *output_fields,
537 : : const size_t highlight[2]) {
538 : :
539 : : const void *data;
540 : : size_t length;
541 : 0 : _cleanup_free_ char *cursor = NULL;
542 : 0 : uint64_t realtime = 0;
543 : : char ts[FORMAT_TIMESTAMP_MAX + 7];
544 : : const char *timestamp;
545 : : int r;
546 : :
547 [ # # ]: 0 : assert(f);
548 [ # # ]: 0 : assert(j);
549 : :
550 : 0 : sd_journal_set_data_threshold(j, 0);
551 : :
552 : 0 : r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length);
553 [ # # ]: 0 : if (r == -ENOENT)
554 [ # # ]: 0 : log_debug("Source realtime timestamp not found");
555 [ # # ]: 0 : else if (r < 0)
556 [ # # # # ]: 0 : return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get source realtime timestamp: %m");
557 : : else {
558 [ # # ]: 0 : _cleanup_free_ char *value = NULL;
559 : :
560 : 0 : r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=",
561 : : STRLEN("_SOURCE_REALTIME_TIMESTAMP="), &value,
562 : : NULL);
563 [ # # ]: 0 : if (r < 0)
564 : 0 : return r;
565 [ # # ]: 0 : assert(r > 0);
566 : :
567 : 0 : r = safe_atou64(value, &realtime);
568 [ # # ]: 0 : if (r < 0)
569 [ # # ]: 0 : log_debug_errno(r, "Failed to parse realtime timestamp: %m");
570 : : }
571 : :
572 [ # # ]: 0 : if (r < 0) {
573 : 0 : r = sd_journal_get_realtime_usec(j, &realtime);
574 [ # # ]: 0 : if (r < 0)
575 [ # # # # ]: 0 : return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get realtime timestamp: %m");
576 : : }
577 : :
578 : 0 : r = sd_journal_get_cursor(j, &cursor);
579 [ # # ]: 0 : if (r < 0)
580 [ # # ]: 0 : return log_error_errno(r, "Failed to get cursor: %m");
581 : :
582 [ # # ]: 0 : timestamp = flags & OUTPUT_UTC ? format_timestamp_us_utc(ts, sizeof ts, realtime)
583 : 0 : : format_timestamp_us(ts, sizeof ts, realtime);
584 : 0 : fprintf(f, "%s [%s]\n",
585 [ # # ]: 0 : timestamp ?: "(no timestamp)",
586 : : cursor);
587 : :
588 [ # # ]: 0 : JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
589 : : const char *c, *p;
590 : : int fieldlen;
591 : 0 : const char *on = "", *off = "";
592 [ # # # ]: 0 : _cleanup_free_ char *urlified = NULL;
593 : : size_t valuelen;
594 : :
595 : 0 : c = memchr(data, '=', length);
596 [ # # ]: 0 : if (!c)
597 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
598 : : "Invalid field.");
599 : 0 : fieldlen = c - (const char*) data;
600 : :
601 : 0 : r = field_set_test(output_fields, data, fieldlen);
602 [ # # ]: 0 : if (r < 0)
603 : 0 : return r;
604 [ # # ]: 0 : if (r == 0)
605 : 0 : continue;
606 : :
607 : 0 : valuelen = length - 1 - fieldlen;
608 : :
609 [ # # # # ]: 0 : if ((flags & OUTPUT_COLOR) && (p = startswith(data, "MESSAGE="))) {
610 : 0 : on = ANSI_HIGHLIGHT;
611 : 0 : off = ANSI_NORMAL;
612 [ # # ]: 0 : } else if ((p = startswith(data, "CONFIG_FILE="))) {
613 [ # # ]: 0 : if (terminal_urlify_path(p, NULL, &urlified) >= 0) {
614 : 0 : p = urlified;
615 : 0 : valuelen = strlen(urlified);
616 : : }
617 : : } else
618 : 0 : p = c + 1;
619 : :
620 [ # # ]: 0 : if ((flags & OUTPUT_SHOW_ALL) ||
621 [ # # # # ]: 0 : (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
622 [ # # ]: 0 : && utf8_is_printable(data, length))) {
623 : 0 : fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
624 : 0 : print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, false,
625 : : p, valuelen,
626 : : NULL);
627 : 0 : fputs(off, f);
628 : : } else {
629 : : char bytes[FORMAT_BYTES_MAX];
630 : :
631 : 0 : fprintf(f, " %s%.*s=[%s blob data]%s\n",
632 : : on,
633 : 0 : (int) (c - (const char*) data),
634 : : (const char*) data,
635 : 0 : format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1),
636 : : off);
637 : : }
638 : : }
639 : :
640 [ # # ]: 0 : if (r < 0)
641 : 0 : return r;
642 : :
643 [ # # ]: 0 : if (flags & OUTPUT_CATALOG)
644 : 0 : print_catalog(f, j);
645 : :
646 : 0 : return 0;
647 : : }
648 : :
649 : 0 : static int output_export(
650 : : FILE *f,
651 : : sd_journal *j,
652 : : OutputMode mode,
653 : : unsigned n_columns,
654 : : OutputFlags flags,
655 : : Set *output_fields,
656 : : const size_t highlight[2]) {
657 : :
658 : : sd_id128_t boot_id;
659 : : char sid[33];
660 : : int r;
661 : : usec_t realtime, monotonic;
662 : 0 : _cleanup_free_ char *cursor = NULL;
663 : : const void *data;
664 : : size_t length;
665 : :
666 [ # # ]: 0 : assert(j);
667 : :
668 : 0 : sd_journal_set_data_threshold(j, 0);
669 : :
670 : 0 : r = sd_journal_get_realtime_usec(j, &realtime);
671 [ # # ]: 0 : if (r < 0)
672 [ # # ]: 0 : return log_error_errno(r, "Failed to get realtime timestamp: %m");
673 : :
674 : 0 : r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
675 [ # # ]: 0 : if (r < 0)
676 [ # # ]: 0 : return log_error_errno(r, "Failed to get monotonic timestamp: %m");
677 : :
678 : 0 : r = sd_journal_get_cursor(j, &cursor);
679 [ # # ]: 0 : if (r < 0)
680 [ # # ]: 0 : return log_error_errno(r, "Failed to get cursor: %m");
681 : :
682 : 0 : fprintf(f,
683 : : "__CURSOR=%s\n"
684 : : "__REALTIME_TIMESTAMP="USEC_FMT"\n"
685 : : "__MONOTONIC_TIMESTAMP="USEC_FMT"\n"
686 : : "_BOOT_ID=%s\n",
687 : : cursor,
688 : : realtime,
689 : : monotonic,
690 : : sd_id128_to_string(boot_id, sid));
691 : :
692 [ # # ]: 0 : JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
693 : : const char *c;
694 : :
695 : : /* We already printed the boot id from the data in the header, hence let's suppress it here */
696 [ # # ]: 0 : if (memory_startswith(data, length, "_BOOT_ID="))
697 : 0 : continue;
698 : :
699 : 0 : c = memchr(data, '=', length);
700 [ # # ]: 0 : if (!c)
701 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
702 : : "Invalid field.");
703 : :
704 : 0 : r = field_set_test(output_fields, data, c - (const char *) data);
705 [ # # ]: 0 : if (r < 0)
706 : 0 : return r;
707 [ # # ]: 0 : if (!r)
708 : 0 : continue;
709 : :
710 [ # # ]: 0 : if (utf8_is_printable_newline(data, length, false))
711 : 0 : fwrite(data, length, 1, f);
712 : : else {
713 : : uint64_t le64;
714 : :
715 : 0 : fwrite(data, c - (const char*) data, 1, f);
716 : 0 : fputc('\n', f);
717 : 0 : le64 = htole64(length - (c - (const char*) data) - 1);
718 : 0 : fwrite(&le64, sizeof(le64), 1, f);
719 : 0 : fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
720 : : }
721 : :
722 : 0 : fputc('\n', f);
723 : : }
724 [ # # ]: 0 : if (r == -EBADMSG) {
725 [ # # ]: 0 : log_debug_errno(r, "Skipping message we can't read: %m");
726 : 0 : return 0;
727 : : }
728 : :
729 [ # # ]: 0 : if (r < 0)
730 : 0 : return r;
731 : :
732 : 0 : fputc('\n', f);
733 : :
734 : 0 : return 0;
735 : : }
736 : :
737 : 0 : void json_escape(
738 : : FILE *f,
739 : : const char* p,
740 : : size_t l,
741 : : OutputFlags flags) {
742 : :
743 [ # # ]: 0 : assert(f);
744 [ # # ]: 0 : assert(p);
745 : :
746 [ # # # # ]: 0 : if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
747 : 0 : fputs("null", f);
748 : :
749 [ # # # # ]: 0 : else if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(p, l)) {
750 : 0 : bool not_first = false;
751 : :
752 : 0 : fputs("[ ", f);
753 : :
754 [ # # ]: 0 : while (l > 0) {
755 [ # # ]: 0 : if (not_first)
756 : 0 : fprintf(f, ", %u", (uint8_t) *p);
757 : : else {
758 : 0 : not_first = true;
759 : 0 : fprintf(f, "%u", (uint8_t) *p);
760 : : }
761 : :
762 : 0 : p++;
763 : 0 : l--;
764 : : }
765 : :
766 : 0 : fputs(" ]", f);
767 : : } else {
768 : 0 : fputc('"', f);
769 : :
770 [ # # ]: 0 : while (l > 0) {
771 [ # # # # ]: 0 : if (IN_SET(*p, '"', '\\')) {
772 : 0 : fputc('\\', f);
773 : 0 : fputc(*p, f);
774 [ # # ]: 0 : } else if (*p == '\n')
775 : 0 : fputs("\\n", f);
776 [ # # ]: 0 : else if ((uint8_t) *p < ' ')
777 : 0 : fprintf(f, "\\u%04x", (uint8_t) *p);
778 : : else
779 : 0 : fputc(*p, f);
780 : :
781 : 0 : p++;
782 : 0 : l--;
783 : : }
784 : :
785 : 0 : fputc('"', f);
786 : : }
787 : 0 : }
788 : :
789 : : struct json_data {
790 : : JsonVariant* name;
791 : : size_t n_values;
792 : : JsonVariant* values[];
793 : : };
794 : :
795 : 0 : static int update_json_data(
796 : : Hashmap *h,
797 : : OutputFlags flags,
798 : : const char *name,
799 : : const void *value,
800 : : size_t size) {
801 : :
802 : 0 : _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
803 : : struct json_data *d;
804 : : int r;
805 : :
806 [ # # # # ]: 0 : if (!(flags & OUTPUT_SHOW_ALL) && strlen(name) + 1 + size >= JSON_THRESHOLD)
807 : 0 : r = json_variant_new_null(&v);
808 [ # # ]: 0 : else if (utf8_is_printable(value, size))
809 : 0 : r = json_variant_new_stringn(&v, value, size);
810 : : else
811 : 0 : r = json_variant_new_array_bytes(&v, value, size);
812 [ # # ]: 0 : if (r < 0)
813 [ # # ]: 0 : return log_error_errno(r, "Failed to allocate JSON data: %m");
814 : :
815 : 0 : d = hashmap_get(h, name);
816 [ # # ]: 0 : if (d) {
817 : : struct json_data *w;
818 : :
819 : 0 : w = realloc(d, offsetof(struct json_data, values) + sizeof(JsonVariant*) * (d->n_values + 1));
820 [ # # ]: 0 : if (!w)
821 : 0 : return log_oom();
822 : :
823 : 0 : d = w;
824 [ # # ]: 0 : assert_se(hashmap_update(h, json_variant_string(d->name), d) >= 0);
825 : : } else {
826 [ # # ]: 0 : _cleanup_(json_variant_unrefp) JsonVariant *n = NULL;
827 : :
828 : 0 : r = json_variant_new_string(&n, name);
829 [ # # ]: 0 : if (r < 0)
830 [ # # ]: 0 : return log_error_errno(r, "Failed to allocate JSON name variant: %m");
831 : :
832 : 0 : d = malloc0(offsetof(struct json_data, values) + sizeof(JsonVariant*));
833 [ # # ]: 0 : if (!d)
834 : 0 : return log_oom();
835 : :
836 : 0 : r = hashmap_put(h, json_variant_string(n), d);
837 [ # # ]: 0 : if (r < 0) {
838 : 0 : free(d);
839 [ # # ]: 0 : return log_error_errno(r, "Failed to insert JSON name into hashmap: %m");
840 : : }
841 : :
842 : 0 : d->name = TAKE_PTR(n);
843 : : }
844 : :
845 : 0 : d->values[d->n_values++] = TAKE_PTR(v);
846 : 0 : return 0;
847 : : }
848 : :
849 : 0 : static int update_json_data_split(
850 : : Hashmap *h,
851 : : OutputFlags flags,
852 : : Set *output_fields,
853 : : const void *data,
854 : : size_t size) {
855 : :
856 : : const char *eq;
857 : : char *name;
858 : :
859 [ # # ]: 0 : assert(h);
860 [ # # # # ]: 0 : assert(data || size == 0);
861 : :
862 [ # # ]: 0 : if (memory_startswith(data, size, "_BOOT_ID="))
863 : 0 : return 0;
864 : :
865 : 0 : eq = memchr(data, '=', MIN(size, JSON_THRESHOLD));
866 [ # # ]: 0 : if (!eq)
867 : 0 : return 0;
868 : :
869 [ # # ]: 0 : if (eq == data)
870 : 0 : return 0;
871 : :
872 : 0 : name = strndupa(data, eq - (const char*) data);
873 [ # # # # ]: 0 : if (output_fields && !set_get(output_fields, name))
874 : 0 : return 0;
875 : :
876 : 0 : return update_json_data(h, flags, name, eq + 1, size - (eq - (const char*) data) - 1);
877 : : }
878 : :
879 : 0 : static int output_json(
880 : : FILE *f,
881 : : sd_journal *j,
882 : : OutputMode mode,
883 : : unsigned n_columns,
884 : : OutputFlags flags,
885 : : Set *output_fields,
886 : : const size_t highlight[2]) {
887 : :
888 : : char sid[SD_ID128_STRING_MAX], usecbuf[DECIMAL_STR_MAX(usec_t)];
889 : 0 : _cleanup_(json_variant_unrefp) JsonVariant *object = NULL;
890 : 0 : _cleanup_free_ char *cursor = NULL;
891 : : uint64_t realtime, monotonic;
892 : 0 : JsonVariant **array = NULL;
893 : : struct json_data *d;
894 : : sd_id128_t boot_id;
895 : 0 : Hashmap *h = NULL;
896 : 0 : size_t n = 0;
897 : : Iterator i;
898 : : int r;
899 : :
900 [ # # ]: 0 : assert(j);
901 : :
902 [ # # ]: 0 : (void) sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
903 : :
904 : 0 : r = sd_journal_get_realtime_usec(j, &realtime);
905 [ # # ]: 0 : if (r < 0)
906 [ # # ]: 0 : return log_error_errno(r, "Failed to get realtime timestamp: %m");
907 : :
908 : 0 : r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
909 [ # # ]: 0 : if (r < 0)
910 [ # # ]: 0 : return log_error_errno(r, "Failed to get monotonic timestamp: %m");
911 : :
912 : 0 : r = sd_journal_get_cursor(j, &cursor);
913 [ # # ]: 0 : if (r < 0)
914 [ # # ]: 0 : return log_error_errno(r, "Failed to get cursor: %m");
915 : :
916 : 0 : h = hashmap_new(&string_hash_ops);
917 [ # # ]: 0 : if (!h)
918 : 0 : return log_oom();
919 : :
920 : 0 : r = update_json_data(h, flags, "__CURSOR", cursor, strlen(cursor));
921 [ # # ]: 0 : if (r < 0)
922 : 0 : goto finish;
923 : :
924 [ # # ]: 0 : xsprintf(usecbuf, USEC_FMT, realtime);
925 : 0 : r = update_json_data(h, flags, "__REALTIME_TIMESTAMP", usecbuf, strlen(usecbuf));
926 [ # # ]: 0 : if (r < 0)
927 : 0 : goto finish;
928 : :
929 [ # # ]: 0 : xsprintf(usecbuf, USEC_FMT, monotonic);
930 : 0 : r = update_json_data(h, flags, "__MONOTONIC_TIMESTAMP", usecbuf, strlen(usecbuf));
931 [ # # ]: 0 : if (r < 0)
932 : 0 : goto finish;
933 : :
934 : 0 : sd_id128_to_string(boot_id, sid);
935 : 0 : r = update_json_data(h, flags, "_BOOT_ID", sid, strlen(sid));
936 [ # # ]: 0 : if (r < 0)
937 : 0 : goto finish;
938 : :
939 : 0 : for (;;) {
940 : : const void *data;
941 : : size_t size;
942 : :
943 : 0 : r = sd_journal_enumerate_data(j, &data, &size);
944 [ # # ]: 0 : if (r == -EBADMSG) {
945 [ # # ]: 0 : log_debug_errno(r, "Skipping message we can't read: %m");
946 : 0 : r = 0;
947 : 0 : goto finish;
948 : : }
949 [ # # ]: 0 : if (r < 0) {
950 [ # # ]: 0 : log_error_errno(r, "Failed to read journal: %m");
951 : 0 : goto finish;
952 : : }
953 [ # # ]: 0 : if (r == 0)
954 : 0 : break;
955 : :
956 : 0 : r = update_json_data_split(h, flags, output_fields, data, size);
957 [ # # ]: 0 : if (r < 0)
958 : 0 : goto finish;
959 : : }
960 : :
961 : 0 : array = new(JsonVariant*, hashmap_size(h)*2);
962 [ # # ]: 0 : if (!array) {
963 : 0 : r = log_oom();
964 : 0 : goto finish;
965 : : }
966 : :
967 [ # # ]: 0 : HASHMAP_FOREACH(d, h, i) {
968 [ # # ]: 0 : assert(d->n_values > 0);
969 : :
970 : 0 : array[n++] = json_variant_ref(d->name);
971 : :
972 [ # # ]: 0 : if (d->n_values == 1)
973 : 0 : array[n++] = json_variant_ref(d->values[0]);
974 : : else {
975 [ # # ]: 0 : _cleanup_(json_variant_unrefp) JsonVariant *q = NULL;
976 : :
977 : 0 : r = json_variant_new_array(&q, d->values, d->n_values);
978 [ # # ]: 0 : if (r < 0) {
979 [ # # ]: 0 : log_error_errno(r, "Failed to create JSON array: %m");
980 : 0 : goto finish;
981 : : }
982 : :
983 : 0 : array[n++] = TAKE_PTR(q);
984 : : }
985 : : }
986 : :
987 : 0 : r = json_variant_new_object(&object, array, n);
988 [ # # ]: 0 : if (r < 0) {
989 [ # # ]: 0 : log_error_errno(r, "Failed to allocate JSON object: %m");
990 : 0 : goto finish;
991 : : }
992 : :
993 : 0 : json_variant_dump(object,
994 : 0 : output_mode_to_json_format_flags(mode) |
995 : 0 : (FLAGS_SET(flags, OUTPUT_COLOR) ? JSON_FORMAT_COLOR : 0),
996 : : f, NULL);
997 : :
998 : 0 : r = 0;
999 : :
1000 : 0 : finish:
1001 [ # # ]: 0 : while ((d = hashmap_steal_first(h))) {
1002 : : size_t k;
1003 : :
1004 : 0 : json_variant_unref(d->name);
1005 [ # # ]: 0 : for (k = 0; k < d->n_values; k++)
1006 : 0 : json_variant_unref(d->values[k]);
1007 : :
1008 : 0 : free(d);
1009 : : }
1010 : :
1011 : 0 : hashmap_free(h);
1012 : :
1013 : 0 : json_variant_unref_many(array, n);
1014 : 0 : free(array);
1015 : :
1016 : 0 : return r;
1017 : : }
1018 : :
1019 : 0 : static int output_cat(
1020 : : FILE *f,
1021 : : sd_journal *j,
1022 : : OutputMode mode,
1023 : : unsigned n_columns,
1024 : : OutputFlags flags,
1025 : : Set *output_fields,
1026 : : const size_t highlight[2]) {
1027 : :
1028 : : const void *data;
1029 : : size_t l;
1030 : : int r;
1031 : 0 : const char *highlight_on = "", *highlight_off = "";
1032 : :
1033 [ # # ]: 0 : assert(j);
1034 [ # # ]: 0 : assert(f);
1035 : :
1036 [ # # ]: 0 : if (flags & OUTPUT_COLOR) {
1037 : 0 : highlight_on = ANSI_HIGHLIGHT_RED;
1038 : 0 : highlight_off = ANSI_NORMAL;
1039 : : }
1040 : :
1041 : 0 : sd_journal_set_data_threshold(j, 0);
1042 : :
1043 : 0 : r = sd_journal_get_data(j, "MESSAGE", &data, &l);
1044 [ # # ]: 0 : if (r == -EBADMSG) {
1045 [ # # ]: 0 : log_debug_errno(r, "Skipping message we can't read: %m");
1046 : 0 : return 0;
1047 : : }
1048 [ # # ]: 0 : if (r < 0) {
1049 : : /* An entry without MESSAGE=? */
1050 [ # # ]: 0 : if (r == -ENOENT)
1051 : 0 : return 0;
1052 : :
1053 [ # # ]: 0 : return log_error_errno(r, "Failed to get data: %m");
1054 : : }
1055 : :
1056 [ # # ]: 0 : assert(l >= 8);
1057 : :
1058 [ # # # # ]: 0 : if (highlight && (flags & OUTPUT_COLOR)) {
1059 [ # # ]: 0 : assert(highlight[0] <= highlight[1]);
1060 [ # # ]: 0 : assert(highlight[1] <= l - 8);
1061 : :
1062 : 0 : fwrite((const char*) data + 8, 1, highlight[0], f);
1063 : 0 : fwrite(highlight_on, 1, strlen(highlight_on), f);
1064 : 0 : fwrite((const char*) data + 8 + highlight[0], 1, highlight[1] - highlight[0], f);
1065 : 0 : fwrite(highlight_off, 1, strlen(highlight_off), f);
1066 : 0 : fwrite((const char*) data + 8 + highlight[1], 1, l - 8 - highlight[1], f);
1067 : : } else
1068 : 0 : fwrite((const char*) data + 8, 1, l - 8, f);
1069 : 0 : fputc('\n', f);
1070 : :
1071 : 0 : return 0;
1072 : : }
1073 : :
1074 : : static int (*output_funcs[_OUTPUT_MODE_MAX])(
1075 : : FILE *f,
1076 : : sd_journal *j,
1077 : : OutputMode mode,
1078 : : unsigned n_columns,
1079 : : OutputFlags flags,
1080 : : Set *output_fields,
1081 : : const size_t highlight[2]) = {
1082 : :
1083 : : [OUTPUT_SHORT] = output_short,
1084 : : [OUTPUT_SHORT_ISO] = output_short,
1085 : : [OUTPUT_SHORT_ISO_PRECISE] = output_short,
1086 : : [OUTPUT_SHORT_PRECISE] = output_short,
1087 : : [OUTPUT_SHORT_MONOTONIC] = output_short,
1088 : : [OUTPUT_SHORT_UNIX] = output_short,
1089 : : [OUTPUT_SHORT_FULL] = output_short,
1090 : : [OUTPUT_VERBOSE] = output_verbose,
1091 : : [OUTPUT_EXPORT] = output_export,
1092 : : [OUTPUT_JSON] = output_json,
1093 : : [OUTPUT_JSON_PRETTY] = output_json,
1094 : : [OUTPUT_JSON_SSE] = output_json,
1095 : : [OUTPUT_JSON_SEQ] = output_json,
1096 : : [OUTPUT_CAT] = output_cat,
1097 : : [OUTPUT_WITH_UNIT] = output_short,
1098 : : };
1099 : :
1100 : 0 : int show_journal_entry(
1101 : : FILE *f,
1102 : : sd_journal *j,
1103 : : OutputMode mode,
1104 : : unsigned n_columns,
1105 : : OutputFlags flags,
1106 : : char **output_fields,
1107 : : const size_t highlight[2],
1108 : : bool *ellipsized) {
1109 : :
1110 : : int ret;
1111 : 0 : _cleanup_set_free_free_ Set *fields = NULL;
1112 [ # # ]: 0 : assert(mode >= 0);
1113 [ # # ]: 0 : assert(mode < _OUTPUT_MODE_MAX);
1114 : :
1115 [ # # ]: 0 : if (n_columns <= 0)
1116 : 0 : n_columns = columns();
1117 : :
1118 [ # # ]: 0 : if (output_fields) {
1119 : 0 : fields = set_new(&string_hash_ops);
1120 [ # # ]: 0 : if (!fields)
1121 : 0 : return log_oom();
1122 : :
1123 : 0 : ret = set_put_strdupv(fields, output_fields);
1124 [ # # ]: 0 : if (ret < 0)
1125 : 0 : return ret;
1126 : : }
1127 : :
1128 : 0 : ret = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight);
1129 : :
1130 [ # # # # ]: 0 : if (ellipsized && ret > 0)
1131 : 0 : *ellipsized = true;
1132 : :
1133 : 0 : return ret;
1134 : : }
1135 : :
1136 : 0 : static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) {
1137 [ # # ]: 0 : assert(f);
1138 [ # # ]: 0 : assert(flags);
1139 : :
1140 [ # # ]: 0 : if (!(*flags & OUTPUT_BEGIN_NEWLINE))
1141 : 0 : return 0;
1142 : :
1143 : : /* Print a beginning new line if that's request, but only once
1144 : : * on the first line we print. */
1145 : :
1146 : 0 : fputc('\n', f);
1147 : 0 : *flags &= ~OUTPUT_BEGIN_NEWLINE;
1148 : 0 : return 0;
1149 : : }
1150 : :
1151 : 0 : int show_journal(
1152 : : FILE *f,
1153 : : sd_journal *j,
1154 : : OutputMode mode,
1155 : : unsigned n_columns,
1156 : : usec_t not_before,
1157 : : unsigned how_many,
1158 : : OutputFlags flags,
1159 : : bool *ellipsized) {
1160 : :
1161 : : int r;
1162 : 0 : unsigned line = 0;
1163 : 0 : bool need_seek = false;
1164 : 0 : int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
1165 : :
1166 [ # # ]: 0 : assert(j);
1167 [ # # ]: 0 : assert(mode >= 0);
1168 [ # # ]: 0 : assert(mode < _OUTPUT_MODE_MAX);
1169 : :
1170 [ # # ]: 0 : if (how_many == (unsigned) -1)
1171 : 0 : need_seek = true;
1172 : : else {
1173 : : /* Seek to end */
1174 : 0 : r = sd_journal_seek_tail(j);
1175 [ # # ]: 0 : if (r < 0)
1176 [ # # ]: 0 : return log_error_errno(r, "Failed to seek to tail: %m");
1177 : :
1178 : 0 : r = sd_journal_previous_skip(j, how_many);
1179 [ # # ]: 0 : if (r < 0)
1180 [ # # ]: 0 : return log_error_errno(r, "Failed to skip previous: %m");
1181 : : }
1182 : :
1183 : 0 : for (;;) {
1184 : 0 : for (;;) {
1185 : : usec_t usec;
1186 : :
1187 [ # # ]: 0 : if (need_seek) {
1188 : 0 : r = sd_journal_next(j);
1189 [ # # ]: 0 : if (r < 0)
1190 [ # # ]: 0 : return log_error_errno(r, "Failed to iterate through journal: %m");
1191 : : }
1192 : :
1193 [ # # ]: 0 : if (r == 0)
1194 : 0 : break;
1195 : :
1196 : 0 : need_seek = true;
1197 : :
1198 [ # # ]: 0 : if (not_before > 0) {
1199 : 0 : r = sd_journal_get_monotonic_usec(j, &usec, NULL);
1200 : :
1201 : : /* -ESTALE is returned if the
1202 : : timestamp is not from this boot */
1203 [ # # ]: 0 : if (r == -ESTALE)
1204 : 0 : continue;
1205 [ # # ]: 0 : else if (r < 0)
1206 [ # # ]: 0 : return log_error_errno(r, "Failed to get journal time: %m");
1207 : :
1208 [ # # ]: 0 : if (usec < not_before)
1209 : 0 : continue;
1210 : : }
1211 : :
1212 : 0 : line++;
1213 : 0 : maybe_print_begin_newline(f, &flags);
1214 : :
1215 : 0 : r = show_journal_entry(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
1216 [ # # ]: 0 : if (r < 0)
1217 : 0 : return r;
1218 : : }
1219 : :
1220 [ # # # # : 0 : if (warn_cutoff && line < how_many && not_before > 0) {
# # ]
1221 : : sd_id128_t boot_id;
1222 : 0 : usec_t cutoff = 0;
1223 : :
1224 : : /* Check whether the cutoff line is too early */
1225 : :
1226 : 0 : r = sd_id128_get_boot(&boot_id);
1227 [ # # ]: 0 : if (r < 0)
1228 [ # # ]: 0 : return log_error_errno(r, "Failed to get boot id: %m");
1229 : :
1230 : 0 : r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
1231 [ # # ]: 0 : if (r < 0)
1232 [ # # ]: 0 : return log_error_errno(r, "Failed to get journal cutoff time: %m");
1233 : :
1234 [ # # # # ]: 0 : if (r > 0 && not_before < cutoff) {
1235 : 0 : maybe_print_begin_newline(f, &flags);
1236 : 0 : fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
1237 : : }
1238 : :
1239 : 0 : warn_cutoff = false;
1240 : : }
1241 : :
1242 [ # # ]: 0 : if (!(flags & OUTPUT_FOLLOW))
1243 : 0 : break;
1244 : :
1245 : 0 : r = sd_journal_wait(j, USEC_INFINITY);
1246 [ # # ]: 0 : if (r < 0)
1247 [ # # ]: 0 : return log_error_errno(r, "Failed to wait for journal: %m");
1248 : :
1249 : : }
1250 : :
1251 : 0 : return 0;
1252 : : }
1253 : :
1254 : 0 : int add_matches_for_unit(sd_journal *j, const char *unit) {
1255 : : const char *m1, *m2, *m3, *m4;
1256 : : int r;
1257 : :
1258 [ # # ]: 0 : assert(j);
1259 [ # # ]: 0 : assert(unit);
1260 : :
1261 [ # # # # : 0 : m1 = strjoina("_SYSTEMD_UNIT=", unit);
# # # # #
# # # ]
1262 [ # # # # : 0 : m2 = strjoina("COREDUMP_UNIT=", unit);
# # # # #
# # # ]
1263 [ # # # # : 0 : m3 = strjoina("UNIT=", unit);
# # # # #
# # # ]
1264 [ # # # # : 0 : m4 = strjoina("OBJECT_SYSTEMD_UNIT=", unit);
# # # # #
# # # ]
1265 : :
1266 [ # # ]: 0 : (void)(
1267 : : /* Look for messages from the service itself */
1268 [ # # ]: 0 : (r = sd_journal_add_match(j, m1, 0)) ||
1269 : :
1270 : : /* Look for coredumps of the service */
1271 [ # # ]: 0 : (r = sd_journal_add_disjunction(j)) ||
1272 [ # # ]: 0 : (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1273 [ # # ]: 0 : (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1274 [ # # ]: 0 : (r = sd_journal_add_match(j, m2, 0)) ||
1275 : :
1276 : : /* Look for messages from PID 1 about this service */
1277 [ # # ]: 0 : (r = sd_journal_add_disjunction(j)) ||
1278 [ # # ]: 0 : (r = sd_journal_add_match(j, "_PID=1", 0)) ||
1279 [ # # ]: 0 : (r = sd_journal_add_match(j, m3, 0)) ||
1280 : :
1281 : : /* Look for messages from authorized daemons about this service */
1282 [ # # ]: 0 : (r = sd_journal_add_disjunction(j)) ||
1283 [ # # ]: 0 : (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1284 : 0 : (r = sd_journal_add_match(j, m4, 0))
1285 : : );
1286 : :
1287 [ # # # # ]: 0 : if (r == 0 && endswith(unit, ".slice")) {
1288 : : const char *m5;
1289 : :
1290 [ # # # # : 0 : m5 = strjoina("_SYSTEMD_SLICE=", unit);
# # # # #
# # # ]
1291 : :
1292 : : /* Show all messages belonging to a slice */
1293 [ # # ]: 0 : (void)(
1294 [ # # ]: 0 : (r = sd_journal_add_disjunction(j)) ||
1295 : 0 : (r = sd_journal_add_match(j, m5, 0))
1296 : : );
1297 : : }
1298 : :
1299 : 0 : return r;
1300 : : }
1301 : :
1302 : 0 : int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
1303 : : int r;
1304 : : char *m1, *m2, *m3, *m4;
1305 : : char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
1306 : :
1307 [ # # ]: 0 : assert(j);
1308 [ # # ]: 0 : assert(unit);
1309 : :
1310 [ # # # # : 0 : m1 = strjoina("_SYSTEMD_USER_UNIT=", unit);
# # # # #
# # # ]
1311 [ # # # # : 0 : m2 = strjoina("USER_UNIT=", unit);
# # # # #
# # # ]
1312 [ # # # # : 0 : m3 = strjoina("COREDUMP_USER_UNIT=", unit);
# # # # #
# # # ]
1313 [ # # # # : 0 : m4 = strjoina("OBJECT_SYSTEMD_USER_UNIT=", unit);
# # # # #
# # # ]
1314 : 0 : sprintf(muid, "_UID="UID_FMT, uid);
1315 : :
1316 [ # # ]: 0 : (void) (
1317 : : /* Look for messages from the user service itself */
1318 [ # # ]: 0 : (r = sd_journal_add_match(j, m1, 0)) ||
1319 [ # # ]: 0 : (r = sd_journal_add_match(j, muid, 0)) ||
1320 : :
1321 : : /* Look for messages from systemd about this service */
1322 [ # # ]: 0 : (r = sd_journal_add_disjunction(j)) ||
1323 [ # # ]: 0 : (r = sd_journal_add_match(j, m2, 0)) ||
1324 [ # # ]: 0 : (r = sd_journal_add_match(j, muid, 0)) ||
1325 : :
1326 : : /* Look for coredumps of the service */
1327 [ # # ]: 0 : (r = sd_journal_add_disjunction(j)) ||
1328 [ # # ]: 0 : (r = sd_journal_add_match(j, m3, 0)) ||
1329 [ # # ]: 0 : (r = sd_journal_add_match(j, muid, 0)) ||
1330 [ # # ]: 0 : (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1331 : :
1332 : : /* Look for messages from authorized daemons about this service */
1333 [ # # ]: 0 : (r = sd_journal_add_disjunction(j)) ||
1334 [ # # ]: 0 : (r = sd_journal_add_match(j, m4, 0)) ||
1335 [ # # ]: 0 : (r = sd_journal_add_match(j, muid, 0)) ||
1336 : 0 : (r = sd_journal_add_match(j, "_UID=0", 0))
1337 : : );
1338 : :
1339 [ # # # # ]: 0 : if (r == 0 && endswith(unit, ".slice")) {
1340 : : const char *m5;
1341 : :
1342 [ # # # # : 0 : m5 = strjoina("_SYSTEMD_SLICE=", unit);
# # # # #
# # # ]
1343 : :
1344 : : /* Show all messages belonging to a slice */
1345 [ # # ]: 0 : (void)(
1346 [ # # ]: 0 : (r = sd_journal_add_disjunction(j)) ||
1347 [ # # ]: 0 : (r = sd_journal_add_match(j, m5, 0)) ||
1348 : 0 : (r = sd_journal_add_match(j, muid, 0))
1349 : : );
1350 : : }
1351 : :
1352 : 0 : return r;
1353 : : }
1354 : :
1355 : 0 : static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
1356 : 0 : _cleanup_close_pair_ int pair[2] = { -1, -1 };
1357 : 0 : _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
1358 : : pid_t pid, child;
1359 : : char buf[37];
1360 : : ssize_t k;
1361 : : int r;
1362 : :
1363 [ # # ]: 0 : assert(machine);
1364 [ # # ]: 0 : assert(boot_id);
1365 : :
1366 [ # # ]: 0 : if (!machine_name_is_valid(machine))
1367 : 0 : return -EINVAL;
1368 : :
1369 : 0 : r = container_get_leader(machine, &pid);
1370 [ # # ]: 0 : if (r < 0)
1371 : 0 : return r;
1372 : :
1373 : 0 : r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, NULL, &rootfd);
1374 [ # # ]: 0 : if (r < 0)
1375 : 0 : return r;
1376 : :
1377 [ # # ]: 0 : if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1378 : 0 : return -errno;
1379 : :
1380 : 0 : r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
1381 : : pidnsfd, mntnsfd, -1, -1, rootfd, &child);
1382 [ # # ]: 0 : if (r < 0)
1383 : 0 : return r;
1384 [ # # ]: 0 : if (r == 0) {
1385 : : int fd;
1386 : :
1387 : 0 : pair[0] = safe_close(pair[0]);
1388 : :
1389 : 0 : fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
1390 [ # # ]: 0 : if (fd < 0)
1391 : 0 : _exit(EXIT_FAILURE);
1392 : :
1393 : 0 : r = loop_read_exact(fd, buf, 36, false);
1394 : 0 : safe_close(fd);
1395 [ # # ]: 0 : if (r < 0)
1396 : 0 : _exit(EXIT_FAILURE);
1397 : :
1398 : 0 : k = send(pair[1], buf, 36, MSG_NOSIGNAL);
1399 [ # # ]: 0 : if (k != 36)
1400 : 0 : _exit(EXIT_FAILURE);
1401 : :
1402 : 0 : _exit(EXIT_SUCCESS);
1403 : : }
1404 : :
1405 : 0 : pair[1] = safe_close(pair[1]);
1406 : :
1407 : 0 : r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
1408 [ # # ]: 0 : if (r < 0)
1409 : 0 : return r;
1410 [ # # ]: 0 : if (r != EXIT_SUCCESS)
1411 : 0 : return -EIO;
1412 : :
1413 : 0 : k = recv(pair[0], buf, 36, 0);
1414 [ # # ]: 0 : if (k != 36)
1415 : 0 : return -EIO;
1416 : :
1417 : 0 : buf[36] = 0;
1418 : 0 : r = sd_id128_from_string(buf, boot_id);
1419 [ # # ]: 0 : if (r < 0)
1420 : 0 : return r;
1421 : :
1422 : 0 : return 0;
1423 : : }
1424 : :
1425 : 0 : int add_match_this_boot(sd_journal *j, const char *machine) {
1426 : 0 : char match[9+32+1] = "_BOOT_ID=";
1427 : : sd_id128_t boot_id;
1428 : : int r;
1429 : :
1430 [ # # ]: 0 : assert(j);
1431 : :
1432 [ # # ]: 0 : if (machine) {
1433 : 0 : r = get_boot_id_for_machine(machine, &boot_id);
1434 [ # # ]: 0 : if (r < 0)
1435 [ # # ]: 0 : return log_error_errno(r, "Failed to get boot id of container %s: %m", machine);
1436 : : } else {
1437 : 0 : r = sd_id128_get_boot(&boot_id);
1438 [ # # ]: 0 : if (r < 0)
1439 [ # # ]: 0 : return log_error_errno(r, "Failed to get boot id: %m");
1440 : : }
1441 : :
1442 : 0 : sd_id128_to_string(boot_id, match + 9);
1443 : 0 : r = sd_journal_add_match(j, match, strlen(match));
1444 [ # # ]: 0 : if (r < 0)
1445 [ # # ]: 0 : return log_error_errno(r, "Failed to add match: %m");
1446 : :
1447 : 0 : r = sd_journal_add_conjunction(j);
1448 [ # # ]: 0 : if (r < 0)
1449 [ # # ]: 0 : return log_error_errno(r, "Failed to add conjunction: %m");
1450 : :
1451 : 0 : return 0;
1452 : : }
1453 : :
1454 : 0 : int show_journal_by_unit(
1455 : : FILE *f,
1456 : : const char *unit,
1457 : : OutputMode mode,
1458 : : unsigned n_columns,
1459 : : usec_t not_before,
1460 : : unsigned how_many,
1461 : : uid_t uid,
1462 : : OutputFlags flags,
1463 : : int journal_open_flags,
1464 : : bool system_unit,
1465 : : bool *ellipsized) {
1466 : :
1467 : 0 : _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1468 : : int r;
1469 : :
1470 [ # # ]: 0 : assert(mode >= 0);
1471 [ # # ]: 0 : assert(mode < _OUTPUT_MODE_MAX);
1472 [ # # ]: 0 : assert(unit);
1473 : :
1474 [ # # ]: 0 : if (how_many <= 0)
1475 : 0 : return 0;
1476 : :
1477 : 0 : r = sd_journal_open(&j, journal_open_flags);
1478 [ # # ]: 0 : if (r < 0)
1479 [ # # ]: 0 : return log_error_errno(r, "Failed to open journal: %m");
1480 : :
1481 : 0 : r = add_match_this_boot(j, NULL);
1482 [ # # ]: 0 : if (r < 0)
1483 : 0 : return r;
1484 : :
1485 [ # # ]: 0 : if (system_unit)
1486 : 0 : r = add_matches_for_unit(j, unit);
1487 : : else
1488 : 0 : r = add_matches_for_user_unit(j, unit, uid);
1489 [ # # ]: 0 : if (r < 0)
1490 [ # # ]: 0 : return log_error_errno(r, "Failed to add unit matches: %m");
1491 : :
1492 [ # # ]: 0 : if (DEBUG_LOGGING) {
1493 [ # # ]: 0 : _cleanup_free_ char *filter;
1494 : :
1495 : 0 : filter = journal_make_match_string(j);
1496 [ # # ]: 0 : if (!filter)
1497 : 0 : return log_oom();
1498 : :
1499 [ # # ]: 0 : log_debug("Journal filter: %s", filter);
1500 : : }
1501 : :
1502 : 0 : return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
1503 : : }
|