]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/logs-show.c
Rename F_TYPE_CMP() to F_TYPE_EQUAL()
[thirdparty/systemd.git] / src / shared / logs-show.c
CommitLineData
86aa7ba4
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
86aa7ba4
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
86aa7ba4 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
86aa7ba4
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <time.h>
23#include <assert.h>
24#include <errno.h>
df50185b 25#include <sys/poll.h>
55d7bfc1 26#include <string.h>
86aa7ba4
LP
27
28#include "logs-show.h"
29#include "log.h"
30#include "util.h"
ba961854 31#include "utf8.h"
d99ae53a 32#include "hashmap.h"
dfb33a97 33#include "journal-internal.h"
86aa7ba4 34
a6f0104a
ZJS
35/* up to three lines (each up to 100 characters),
36 or 300 characters, whichever is less */
37#define PRINT_LINE_THRESHOLD 3
38#define PRINT_CHAR_THRESHOLD 300
39
08ace05b 40#define JSON_THRESHOLD 4096
86aa7ba4 41
d4205751
LP
42static int print_catalog(FILE *f, sd_journal *j) {
43 int r;
44 _cleanup_free_ char *t = NULL, *z = NULL;
45
46
47 r = sd_journal_get_catalog(j, &t);
48 if (r < 0)
49 return r;
50
51 z = strreplace(strstrip(t), "\n", "\n-- ");
52 if (!z)
53 return log_oom();
54
55 fputs("-- ", f);
56 fputs(z, f);
57 fputc('\n', f);
58
59 return 0;
60}
61
55d7bfc1
LP
62static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
63 size_t fl, nl;
64 void *buf;
65
66 assert(data);
67 assert(field);
68 assert(target);
69 assert(target_size);
70
71 fl = strlen(field);
72 if (length < fl)
73 return 0;
74
75 if (memcmp(data, field, fl))
76 return 0;
77
78 nl = length - fl;
bf967366 79 buf = malloc(nl+1);
0d0f0c50
SL
80 if (!buf)
81 return log_oom();
55d7bfc1 82
46b0d922
LP
83 memcpy(buf, (const char*) data + fl, nl);
84 ((char*)buf)[nl] = 0;
85
6c1e6b98 86 free(*target);
55d7bfc1
LP
87 *target = buf;
88 *target_size = nl;
89
90 return 1;
91}
92
08ace05b
LP
93static bool shall_print(const char *p, size_t l, OutputFlags flags) {
94 assert(p);
95
96 if (flags & OUTPUT_SHOW_ALL)
55d7bfc1
LP
97 return true;
98
a6f0104a 99 if (l >= PRINT_CHAR_THRESHOLD)
55d7bfc1
LP
100 return false;
101
31f7bf19 102 if (!utf8_is_printable(p, l))
55d7bfc1
LP
103 return false;
104
105 return true;
106}
107
94e0bd7d 108static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputMode flags, int priority, const char* message, size_t message_len) {
31f7bf19
ZJS
109 const char *color_on = "", *color_off = "";
110 const char *pos, *end;
94e0bd7d 111 bool ellipsized = false;
a6f0104a 112 int line = 0;
31f7bf19
ZJS
113
114 if (flags & OUTPUT_COLOR) {
115 if (priority <= LOG_ERR) {
116 color_on = ANSI_HIGHLIGHT_RED_ON;
117 color_off = ANSI_HIGHLIGHT_OFF;
118 } else if (priority <= LOG_NOTICE) {
119 color_on = ANSI_HIGHLIGHT_ON;
120 color_off = ANSI_HIGHLIGHT_OFF;
121 }
122 }
123
a6f0104a
ZJS
124 for (pos = message;
125 pos < message + message_len;
126 pos = end + 1, line++) {
127 bool continuation = line > 0;
128 bool tail_line;
31f7bf19
ZJS
129 int len;
130 for (end = pos; end < message + message_len && *end != '\n'; end++)
131 ;
132 len = end - pos;
133 assert(len >= 0);
134
a6f0104a
ZJS
135 /* We need to figure out when we are showing the last line, and
136 * will skip subsequent lines. In that case, we will put the dots
137 * at the end of the line, instead of putting dots in the middle
138 * or not at all.
139 */
140 tail_line =
141 line + 1 == PRINT_LINE_THRESHOLD ||
142 end + 1 >= message + message_len;
143
144 if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
145 (prefix + len + 1 < n_columns && !tail_line)) {
31f7bf19
ZJS
146 fprintf(f, "%*s%s%.*s%s\n",
147 continuation * prefix, "",
148 color_on, len, pos, color_off);
a6f0104a
ZJS
149 continue;
150 }
31f7bf19 151
a6f0104a
ZJS
152 /* Beyond this point, ellipsization will happen. */
153 ellipsized = true;
31f7bf19 154
a6f0104a
ZJS
155 if (prefix < n_columns && n_columns - prefix >= 3) {
156 if (n_columns - prefix > (unsigned) len + 3)
157 fprintf(f, "%*s%s%.*s...%s\n",
b4b02cbe
ZJS
158 continuation * prefix, "",
159 color_on, len, pos, color_off);
a6f0104a
ZJS
160 else {
161 _cleanup_free_ char *e;
162
163 e = ellipsize_mem(pos, len, n_columns - prefix,
164 tail_line ? 100 : 90);
165 if (!e)
166 fprintf(f, "%*s%s%.*s%s\n",
167 continuation * prefix, "",
168 color_on, len, pos, color_off);
169 else
170 fprintf(f, "%*s%s%s%s\n",
171 continuation * prefix, "",
172 color_on, e, color_off);
173 }
174 } else
31f7bf19
ZJS
175 fputs("...\n", f);
176
a6f0104a
ZJS
177 if (tail_line)
178 break;
31f7bf19 179 }
94e0bd7d
ZJS
180
181 return ellipsized;
31f7bf19
ZJS
182}
183
08ace05b
LP
184static int output_short(
185 FILE *f,
186 sd_journal *j,
187 OutputMode mode,
188 unsigned n_columns,
189 OutputFlags flags) {
190
86aa7ba4 191 int r;
86aa7ba4
LP
192 const void *data;
193 size_t length;
194 size_t n = 0;
08ace05b 195 _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
49826187
LP
196 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;
197 int p = LOG_INFO;
94e0bd7d 198 bool ellipsized = false;
86aa7ba4 199
08ace05b 200 assert(f);
86aa7ba4
LP
201 assert(j);
202
a6f0104a
ZJS
203 /* Set the threshold to one bigger than the actual print
204 * treshold, so that if the line is actually longer than what
205 * we're willing to print, ellipsization will occur. This way
206 * we won't output a misleading line without any indication of
207 * truncation.
208 */
209 sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1);
93b73b06 210
a72b6353 211 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
55d7bfc1 212
49826187
LP
213 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
214 if (r < 0)
08ace05b 215 return r;
49826187
LP
216 else if (r > 0)
217 continue;
218
55d7bfc1
LP
219 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
220 if (r < 0)
08ace05b 221 return r;
55d7bfc1
LP
222 else if (r > 0)
223 continue;
224
4cd9a9d9 225 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
55d7bfc1 226 if (r < 0)
08ace05b 227 return r;
55d7bfc1
LP
228 else if (r > 0)
229 continue;
230
231 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
232 if (r < 0)
08ace05b 233 return r;
55d7bfc1
LP
234 else if (r > 0)
235 continue;
236
237 r = parse_field(data, length, "_PID=", &pid, &pid_len);
238 if (r < 0)
08ace05b 239 return r;
55d7bfc1
LP
240 else if (r > 0)
241 continue;
242
6c1e6b98
LP
243 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
244 if (r < 0)
08ace05b 245 return r;
6c1e6b98
LP
246 else if (r > 0)
247 continue;
248
bf967366
LP
249 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
250 if (r < 0)
08ace05b 251 return r;
bf967366
LP
252 else if (r > 0)
253 continue;
254
255 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
256 if (r < 0)
08ace05b 257 return r;
bf967366
LP
258 else if (r > 0)
259 continue;
260
55d7bfc1
LP
261 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
262 if (r < 0)
08ace05b 263 return r;
55d7bfc1
LP
264 }
265
a72b6353
ZJS
266 if (r < 0)
267 return r;
268
08ace05b
LP
269 if (!message)
270 return 0;
55d7bfc1 271
e8bc0ea2
LP
272 if (!(flags & OUTPUT_SHOW_ALL))
273 strip_tab_ansi(&message, &message_len);
274
49826187
LP
275 if (priority_len == 1 && *priority >= '0' && *priority <= '7')
276 p = *priority - '0';
277
a6e87e90 278 if (mode == OUTPUT_SHORT_MONOTONIC) {
67a12205 279 uint64_t t;
3ebcdf8c
LP
280 sd_id128_t boot_id;
281
bf967366
LP
282 r = -ENOENT;
283
284 if (monotonic)
285 r = safe_atou64(monotonic, &t);
286
287 if (r < 0)
3ebcdf8c 288 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
86aa7ba4 289
3ebcdf8c 290 if (r < 0) {
a72b6353 291 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
08ace05b 292 return r;
67a12205
LP
293 }
294
08ace05b
LP
295 fprintf(f, "[%5llu.%06llu]",
296 (unsigned long long) (t / USEC_PER_SEC),
297 (unsigned long long) (t % USEC_PER_SEC));
3ebcdf8c
LP
298
299 n += 1 + 5 + 1 + 6 + 1;
300
67a12205
LP
301 } else {
302 char buf[64];
bf967366 303 uint64_t x;
67a12205
LP
304 time_t t;
305 struct tm tm;
731a676c 306
bf967366
LP
307 r = -ENOENT;
308
309 if (realtime)
310 r = safe_atou64(realtime, &x);
311
312 if (r < 0)
313 r = sd_journal_get_realtime_usec(j, &x);
67a12205 314
67a12205 315 if (r < 0) {
a72b6353 316 log_error("Failed to get realtime timestamp: %s", strerror(-r));
08ace05b 317 return r;
67a12205
LP
318 }
319
bf967366 320 t = (time_t) (x / USEC_PER_SEC);
44bc6e1f
TT
321 if (mode == OUTPUT_SHORT_ISO)
322 r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", localtime_r(&t, &tm));
323 else
324 r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm));
325
326 if (r <= 0) {
67a12205 327 log_error("Failed to format time.");
44bc6e1f 328 return -EINVAL;
67a12205
LP
329 }
330
08ace05b 331 fputs(buf, f);
67a12205
LP
332 n += strlen(buf);
333 }
86aa7ba4 334
08ace05b
LP
335 if (hostname && shall_print(hostname, hostname_len, flags)) {
336 fprintf(f, " %.*s", (int) hostname_len, hostname);
55d7bfc1
LP
337 n += hostname_len + 1;
338 }
339
08ace05b
LP
340 if (identifier && shall_print(identifier, identifier_len, flags)) {
341 fprintf(f, " %.*s", (int) identifier_len, identifier);
4cd9a9d9 342 n += identifier_len + 1;
08ace05b
LP
343 } else if (comm && shall_print(comm, comm_len, flags)) {
344 fprintf(f, " %.*s", (int) comm_len, comm);
55d7bfc1 345 n += comm_len + 1;
b5936820 346 } else
08ace05b 347 fputc(' ', f);
86aa7ba4 348
08ace05b
LP
349 if (pid && shall_print(pid, pid_len, flags)) {
350 fprintf(f, "[%.*s]", (int) pid_len, pid);
55d7bfc1 351 n += pid_len + 2;
08ace05b
LP
352 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
353 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
6c1e6b98 354 n += fake_pid_len + 2;
86aa7ba4
LP
355 }
356
31f7bf19 357 if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
e6acda19 358 char bytes[FORMAT_BYTES_MAX];
08ace05b 359 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
31f7bf19
ZJS
360 } else {
361 fputs(": ", f);
94e0bd7d
ZJS
362 ellipsized |=
363 print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
31f7bf19 364 }
55d7bfc1 365
d4205751
LP
366 if (flags & OUTPUT_CATALOG)
367 print_catalog(f, j);
368
94e0bd7d 369 return ellipsized;
86aa7ba4
LP
370}
371
08ace05b
LP
372static int output_verbose(
373 FILE *f,
374 sd_journal *j,
375 OutputMode mode,
376 unsigned n_columns,
377 OutputFlags flags) {
378
86aa7ba4
LP
379 const void *data;
380 size_t length;
7fd1b19b 381 _cleanup_free_ char *cursor = NULL;
86aa7ba4
LP
382 uint64_t realtime;
383 char ts[FORMAT_TIMESTAMP_MAX];
384 int r;
385
08ace05b 386 assert(f);
86aa7ba4
LP
387 assert(j);
388
93b73b06
LP
389 sd_journal_set_data_threshold(j, 0);
390
86aa7ba4
LP
391 r = sd_journal_get_realtime_usec(j, &realtime);
392 if (r < 0) {
a72b6353
ZJS
393 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
394 "Failed to get realtime timestamp: %s", strerror(-r));
86aa7ba4
LP
395 return r;
396 }
397
398 r = sd_journal_get_cursor(j, &cursor);
399 if (r < 0) {
400 log_error("Failed to get cursor: %s", strerror(-r));
401 return r;
402 }
403
08ace05b
LP
404 fprintf(f, "%s [%s]\n",
405 format_timestamp(ts, sizeof(ts), realtime),
406 cursor);
86aa7ba4 407
a72b6353 408 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
31f7bf19
ZJS
409 const char *c;
410 int fieldlen;
7ac4fa7e
ZJS
411 const char *on = "", *off = "";
412
31f7bf19
ZJS
413 c = memchr(data, '=', length);
414 if (!c) {
415 log_error("Invalid field.");
416 return -EINVAL;
417 }
418 fieldlen = c - (const char*) data;
86aa7ba4 419
7ac4fa7e
ZJS
420 if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
421 on = ANSI_HIGHLIGHT_ON;
422 off = ANSI_HIGHLIGHT_OFF;
423 }
424
425 if (flags & OUTPUT_SHOW_ALL ||
a6f0104a
ZJS
426 (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
427 && utf8_is_printable(data, length))) {
7ac4fa7e 428 fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
31f7bf19 429 print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
7ac4fa7e 430 fputs(off, f);
31f7bf19
ZJS
431 } else {
432 char bytes[FORMAT_BYTES_MAX];
86aa7ba4 433
7ac4fa7e
ZJS
434 fprintf(f, " %s%.*s=[%s blob data]%s\n",
435 on,
31f7bf19
ZJS
436 (int) (c - (const char*) data),
437 (const char*) data,
7ac4fa7e
ZJS
438 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1),
439 off);
31f7bf19 440 }
86aa7ba4
LP
441 }
442
a72b6353
ZJS
443 if (r < 0)
444 return r;
445
d4205751
LP
446 if (flags & OUTPUT_CATALOG)
447 print_catalog(f, j);
448
86aa7ba4
LP
449 return 0;
450}
451
08ace05b
LP
452static int output_export(
453 FILE *f,
454 sd_journal *j,
455 OutputMode mode,
456 unsigned n_columns,
457 OutputFlags flags) {
458
86aa7ba4
LP
459 sd_id128_t boot_id;
460 char sid[33];
461 int r;
462 usec_t realtime, monotonic;
7fd1b19b 463 _cleanup_free_ char *cursor = NULL;
86aa7ba4
LP
464 const void *data;
465 size_t length;
466
467 assert(j);
468
93b73b06
LP
469 sd_journal_set_data_threshold(j, 0);
470
86aa7ba4
LP
471 r = sd_journal_get_realtime_usec(j, &realtime);
472 if (r < 0) {
473 log_error("Failed to get realtime timestamp: %s", strerror(-r));
474 return r;
475 }
476
477 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
478 if (r < 0) {
479 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
480 return r;
481 }
482
483 r = sd_journal_get_cursor(j, &cursor);
484 if (r < 0) {
485 log_error("Failed to get cursor: %s", strerror(-r));
486 return r;
487 }
488
08ace05b
LP
489 fprintf(f,
490 "__CURSOR=%s\n"
491 "__REALTIME_TIMESTAMP=%llu\n"
492 "__MONOTONIC_TIMESTAMP=%llu\n"
493 "_BOOT_ID=%s\n",
494 cursor,
495 (unsigned long long) realtime,
496 (unsigned long long) monotonic,
497 sd_id128_to_string(boot_id, sid));
86aa7ba4 498
a72b6353 499 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
86aa7ba4 500
112301ae
LP
501 /* We already printed the boot id, from the data in
502 * the header, hence let's suppress it here */
503 if (length >= 9 &&
fd59d9f2 504 hasprefix(data, "_BOOT_ID="))
112301ae
LP
505 continue;
506
31f7bf19 507 if (!utf8_is_printable(data, length)) {
86aa7ba4
LP
508 const char *c;
509 uint64_t le64;
510
511 c = memchr(data, '=', length);
512 if (!c) {
513 log_error("Invalid field.");
514 return -EINVAL;
515 }
516
08ace05b
LP
517 fwrite(data, c - (const char*) data, 1, f);
518 fputc('\n', f);
86aa7ba4 519 le64 = htole64(length - (c - (const char*) data) - 1);
08ace05b
LP
520 fwrite(&le64, sizeof(le64), 1, f);
521 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
86aa7ba4 522 } else
08ace05b 523 fwrite(data, length, 1, f);
86aa7ba4 524
08ace05b 525 fputc('\n', f);
86aa7ba4
LP
526 }
527
a72b6353
ZJS
528 if (r < 0)
529 return r;
530
08ace05b 531 fputc('\n', f);
86aa7ba4
LP
532
533 return 0;
534}
535
240a5fe8 536void json_escape(
08ace05b
LP
537 FILE *f,
538 const char* p,
539 size_t l,
540 OutputFlags flags) {
541
542 assert(f);
543 assert(p);
544
93b73b06 545 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
08ace05b
LP
546
547 fputs("null", f);
548
31f7bf19 549 else if (!utf8_is_printable(p, l)) {
86aa7ba4
LP
550 bool not_first = false;
551
08ace05b 552 fputs("[ ", f);
86aa7ba4
LP
553
554 while (l > 0) {
555 if (not_first)
08ace05b 556 fprintf(f, ", %u", (uint8_t) *p);
86aa7ba4
LP
557 else {
558 not_first = true;
08ace05b 559 fprintf(f, "%u", (uint8_t) *p);
86aa7ba4
LP
560 }
561
562 p++;
563 l--;
564 }
565
08ace05b 566 fputs(" ]", f);
86aa7ba4 567 } else {
08ace05b 568 fputc('\"', f);
86aa7ba4
LP
569
570 while (l > 0) {
571 if (*p == '"' || *p == '\\') {
08ace05b
LP
572 fputc('\\', f);
573 fputc(*p, f);
31f7bf19
ZJS
574 } else if (*p == '\n')
575 fputs("\\n", f);
576 else if (*p < ' ')
08ace05b
LP
577 fprintf(f, "\\u%04x", *p);
578 else
579 fputc(*p, f);
86aa7ba4
LP
580
581 p++;
582 l--;
583 }
584
08ace05b 585 fputc('\"', f);
86aa7ba4
LP
586 }
587}
588
08ace05b
LP
589static int output_json(
590 FILE *f,
591 sd_journal *j,
592 OutputMode mode,
593 unsigned n_columns,
594 OutputFlags flags) {
595
86aa7ba4 596 uint64_t realtime, monotonic;
7fd1b19b 597 _cleanup_free_ char *cursor = NULL;
86aa7ba4
LP
598 const void *data;
599 size_t length;
600 sd_id128_t boot_id;
2e729834 601 char sid[33], *k;
86aa7ba4 602 int r;
d99ae53a
LP
603 Hashmap *h = NULL;
604 bool done, separator;
86aa7ba4
LP
605
606 assert(j);
607
93b73b06
LP
608 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
609
86aa7ba4
LP
610 r = sd_journal_get_realtime_usec(j, &realtime);
611 if (r < 0) {
612 log_error("Failed to get realtime timestamp: %s", strerror(-r));
613 return r;
614 }
615
616 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
617 if (r < 0) {
618 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
619 return r;
620 }
621
622 r = sd_journal_get_cursor(j, &cursor);
623 if (r < 0) {
624 log_error("Failed to get cursor: %s", strerror(-r));
625 return r;
626 }
627
a6e87e90 628 if (mode == OUTPUT_JSON_PRETTY)
08ace05b
LP
629 fprintf(f,
630 "{\n"
631 "\t\"__CURSOR\" : \"%s\",\n"
632 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
633 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
634 "\t\"_BOOT_ID\" : \"%s\"",
635 cursor,
636 (unsigned long long) realtime,
637 (unsigned long long) monotonic,
638 sd_id128_to_string(boot_id, sid));
48383c25
LP
639 else {
640 if (mode == OUTPUT_JSON_SSE)
641 fputs("data: ", f);
642
08ace05b
LP
643 fprintf(f,
644 "{ \"__CURSOR\" : \"%s\", "
645 "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
646 "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
647 "\"_BOOT_ID\" : \"%s\"",
648 cursor,
649 (unsigned long long) realtime,
650 (unsigned long long) monotonic,
651 sd_id128_to_string(boot_id, sid));
48383c25 652 }
86aa7ba4 653
d99ae53a
LP
654 h = hashmap_new(string_hash_func, string_compare_func);
655 if (!h)
656 return -ENOMEM;
657
658 /* First round, iterate through the entry and count how often each field appears */
a72b6353 659 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
d99ae53a
LP
660 const char *eq;
661 char *n;
662 unsigned u;
86aa7ba4 663
112301ae
LP
664 if (length >= 9 &&
665 memcmp(data, "_BOOT_ID=", 9) == 0)
666 continue;
667
d99ae53a
LP
668 eq = memchr(data, '=', length);
669 if (!eq)
670 continue;
86aa7ba4 671
d99ae53a
LP
672 n = strndup(data, eq - (const char*) data);
673 if (!n) {
674 r = -ENOMEM;
675 goto finish;
676 }
a6e87e90 677
d99ae53a
LP
678 u = PTR_TO_UINT(hashmap_get(h, n));
679 if (u == 0) {
680 r = hashmap_put(h, n, UINT_TO_PTR(1));
681 if (r < 0) {
682 free(n);
683 goto finish;
684 }
685 } else {
686 r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
687 free(n);
688 if (r < 0)
689 goto finish;
690 }
86aa7ba4
LP
691 }
692
a72b6353
ZJS
693 if (r < 0)
694 return r;
695
d99ae53a
LP
696 separator = true;
697 do {
698 done = true;
699
700 SD_JOURNAL_FOREACH_DATA(j, data, length) {
701 const char *eq;
702 char *kk, *n;
703 size_t m;
704 unsigned u;
705
706 /* We already printed the boot id, from the data in
707 * the header, hence let's suppress it here */
708 if (length >= 9 &&
709 memcmp(data, "_BOOT_ID=", 9) == 0)
710 continue;
711
712 eq = memchr(data, '=', length);
713 if (!eq)
714 continue;
715
716 if (separator) {
717 if (mode == OUTPUT_JSON_PRETTY)
718 fputs(",\n\t", f);
719 else
720 fputs(", ", f);
721 }
722
723 m = eq - (const char*) data;
724
725 n = strndup(data, m);
726 if (!n) {
727 r = -ENOMEM;
728 goto finish;
729 }
730
731 u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
732 if (u == 0) {
733 /* We already printed this, let's jump to the next */
734 free(n);
735 separator = false;
736
737 continue;
738 } else if (u == 1) {
739 /* Field only appears once, output it directly */
740
741 json_escape(f, data, m, flags);
742 fputs(" : ", f);
743
744 json_escape(f, eq + 1, length - m - 1, flags);
745
746 hashmap_remove(h, n);
747 free(kk);
748 free(n);
749
750 separator = true;
751
752 continue;
753
754 } else {
755 /* Field appears multiple times, output it as array */
756 json_escape(f, data, m, flags);
757 fputs(" : [ ", f);
758 json_escape(f, eq + 1, length - m - 1, flags);
759
760 /* Iterate through the end of the list */
761
762 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
763 if (length < m + 1)
764 continue;
765
766 if (memcmp(data, n, m) != 0)
767 continue;
768
769 if (((const char*) data)[m] != '=')
770 continue;
771
772 fputs(", ", f);
773 json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
774 }
775
776 fputs(" ]", f);
777
778 hashmap_remove(h, n);
779 free(kk);
780 free(n);
781
782 /* Iterate data fields form the beginning */
783 done = false;
784 separator = true;
785
786 break;
787 }
788 }
789
790 } while (!done);
791
a6e87e90 792 if (mode == OUTPUT_JSON_PRETTY)
08ace05b 793 fputs("\n}\n", f);
48383c25
LP
794 else if (mode == OUTPUT_JSON_SSE)
795 fputs("}\n\n", f);
a6e87e90 796 else
08ace05b 797 fputs(" }\n", f);
86aa7ba4 798
d99ae53a
LP
799 r = 0;
800
801finish:
802 while ((k = hashmap_steal_first_key(h)))
803 free(k);
804
805 hashmap_free(h);
806
807 return r;
86aa7ba4
LP
808}
809
08ace05b
LP
810static int output_cat(
811 FILE *f,
812 sd_journal *j,
813 OutputMode mode,
814 unsigned n_columns,
815 OutputFlags flags) {
816
d3f2bdbf
LP
817 const void *data;
818 size_t l;
819 int r;
820
821 assert(j);
08ace05b 822 assert(f);
d3f2bdbf 823
93b73b06
LP
824 sd_journal_set_data_threshold(j, 0);
825
d3f2bdbf
LP
826 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
827 if (r < 0) {
c198300f
LP
828 /* An entry without MESSAGE=? */
829 if (r == -ENOENT)
830 return 0;
831
d3f2bdbf
LP
832 log_error("Failed to get data: %s", strerror(-r));
833 return r;
834 }
835
836 assert(l >= 8);
837
08ace05b
LP
838 fwrite((const char*) data + 8, 1, l - 8, f);
839 fputc('\n', f);
d3f2bdbf
LP
840
841 return 0;
842}
843
08ace05b
LP
844static int (*output_funcs[_OUTPUT_MODE_MAX])(
845 FILE *f,
846 sd_journal*j,
847 OutputMode mode,
848 unsigned n_columns,
849 OutputFlags flags) = {
850
a6e87e90
LP
851 [OUTPUT_SHORT] = output_short,
852 [OUTPUT_SHORT_MONOTONIC] = output_short,
44bc6e1f 853 [OUTPUT_SHORT_ISO] = output_short,
86aa7ba4
LP
854 [OUTPUT_VERBOSE] = output_verbose,
855 [OUTPUT_EXPORT] = output_export,
d3f2bdbf 856 [OUTPUT_JSON] = output_json,
a6e87e90 857 [OUTPUT_JSON_PRETTY] = output_json,
48383c25 858 [OUTPUT_JSON_SSE] = output_json,
d3f2bdbf 859 [OUTPUT_CAT] = output_cat
86aa7ba4
LP
860};
861
08ace05b
LP
862int output_journal(
863 FILE *f,
864 sd_journal *j,
865 OutputMode mode,
866 unsigned n_columns,
94e0bd7d
ZJS
867 OutputFlags flags,
868 bool *ellipsized) {
08ace05b 869
e268b81e 870 int ret;
df50185b 871 assert(mode >= 0);
86aa7ba4
LP
872 assert(mode < _OUTPUT_MODE_MAX);
873
34a35ece
LP
874 if (n_columns <= 0)
875 n_columns = columns();
876
08ace05b 877 ret = output_funcs[mode](f, j, mode, n_columns, flags);
e268b81e 878 fflush(stdout);
94e0bd7d
ZJS
879
880 if (ellipsized && ret > 0)
881 *ellipsized = true;
882
e268b81e 883 return ret;
86aa7ba4
LP
884}
885
1a6c43e9
MT
886static int show_journal(FILE *f,
887 sd_journal *j,
888 OutputMode mode,
889 unsigned n_columns,
890 usec_t not_before,
891 unsigned how_many,
94e0bd7d
ZJS
892 OutputFlags flags,
893 bool *ellipsized) {
86aa7ba4 894
86aa7ba4 895 int r;
df50185b
LP
896 unsigned line = 0;
897 bool need_seek = false;
085d7120 898 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
86aa7ba4 899
1a6c43e9 900 assert(j);
df50185b
LP
901 assert(mode >= 0);
902 assert(mode < _OUTPUT_MODE_MAX);
1946b0bd
LP
903
904 /* Seek to end */
86aa7ba4
LP
905 r = sd_journal_seek_tail(j);
906 if (r < 0)
907 goto finish;
908
df50185b
LP
909 r = sd_journal_previous_skip(j, how_many);
910 if (r < 0)
911 goto finish;
86aa7ba4 912
df50185b
LP
913 for (;;) {
914 for (;;) {
915 usec_t usec;
916
917 if (need_seek) {
918 r = sd_journal_next(j);
919 if (r < 0)
920 goto finish;
921 }
922
923 if (r == 0)
924 break;
86aa7ba4 925
df50185b
LP
926 need_seek = true;
927
928 if (not_before > 0) {
929 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
930
931 /* -ESTALE is returned if the
932 timestamp is not from this boot */
933 if (r == -ESTALE)
934 continue;
935 else if (r < 0)
936 goto finish;
937
938 if (usec < not_before)
939 continue;
940 }
941
942 line ++;
943
94e0bd7d 944 r = output_journal(f, j, mode, n_columns, flags, ellipsized);
df50185b
LP
945 if (r < 0)
946 goto finish;
947 }
948
08984293
LP
949 if (warn_cutoff && line < how_many && not_before > 0) {
950 sd_id128_t boot_id;
951 usec_t cutoff;
952
953 /* Check whether the cutoff line is too early */
954
955 r = sd_id128_get_boot(&boot_id);
956 if (r < 0)
957 goto finish;
958
959 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
960 if (r < 0)
961 goto finish;
962
b59866ae 963 if (r > 0 && not_before < cutoff)
08ace05b 964 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
08984293
LP
965
966 warn_cutoff = false;
967 }
968
085d7120 969 if (!(flags & OUTPUT_FOLLOW))
86aa7ba4
LP
970 break;
971
e02d1cf7 972 r = sd_journal_wait(j, (usec_t) -1);
df50185b
LP
973 if (r < 0)
974 goto finish;
975
86aa7ba4
LP
976 }
977
1a6c43e9
MT
978finish:
979 return r;
980}
981
886a64fe 982int add_matches_for_unit(sd_journal *j, const char *unit) {
1a6c43e9 983 int r;
2d0b2e87 984 char *m1, *m2, *m3, *m4;
1a6c43e9 985
886a64fe 986 assert(j);
1a6c43e9
MT
987 assert(unit);
988
2d0b2e87
ZJS
989 m1 = strappenda("_SYSTEMD_UNIT=", unit);
990 m2 = strappenda("COREDUMP_UNIT=", unit);
991 m3 = strappenda("UNIT=", unit);
992 m4 = strappenda("OBJECT_SYSTEMD_UNIT=", unit);
1a6c43e9 993
886a64fe
ZJS
994 (void)(
995 /* Look for messages from the service itself */
996 (r = sd_journal_add_match(j, m1, 0)) ||
997
998 /* Look for coredumps of the service */
999 (r = sd_journal_add_disjunction(j)) ||
fdcd37df
ZJS
1000 (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1001 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
886a64fe
ZJS
1002 (r = sd_journal_add_match(j, m2, 0)) ||
1003
1004 /* Look for messages from PID 1 about this service */
1005 (r = sd_journal_add_disjunction(j)) ||
1006 (r = sd_journal_add_match(j, "_PID=1", 0)) ||
2d0b2e87
ZJS
1007 (r = sd_journal_add_match(j, m3, 0)) ||
1008
1009 /* Look for messages from authorized daemons about this service */
1010 (r = sd_journal_add_disjunction(j)) ||
1011 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1012 (r = sd_journal_add_match(j, m4, 0))
886a64fe 1013 );
2d0b2e87 1014
886a64fe
ZJS
1015 return r;
1016}
1a6c43e9 1017
886a64fe
ZJS
1018int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
1019 int r;
2d0b2e87
ZJS
1020 char *m1, *m2, *m3, *m4;
1021 char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
1a6c43e9 1022
886a64fe
ZJS
1023 assert(j);
1024 assert(unit);
1a6c43e9 1025
2d0b2e87
ZJS
1026 m1 = strappenda("_SYSTEMD_USER_UNIT=", unit);
1027 m2 = strappenda("USER_UNIT=", unit);
1028 m3 = strappenda("COREDUMP_USER_UNIT=", unit);
1029 m4 = strappenda("OBJECT_SYSTEMD_USER_UNIT=", unit);
1030 sprintf(muid, "_UID=%lu", (unsigned long) uid);
1a6c43e9 1031
886a64fe
ZJS
1032 (void) (
1033 /* Look for messages from the user service itself */
1034 (r = sd_journal_add_match(j, m1, 0)) ||
2d0b2e87 1035 (r = sd_journal_add_match(j, muid, 0)) ||
886a64fe
ZJS
1036
1037 /* Look for messages from systemd about this service */
1038 (r = sd_journal_add_disjunction(j)) ||
1039 (r = sd_journal_add_match(j, m2, 0)) ||
2d0b2e87 1040 (r = sd_journal_add_match(j, muid, 0)) ||
886a64fe
ZJS
1041
1042 /* Look for coredumps of the service */
1043 (r = sd_journal_add_disjunction(j)) ||
1044 (r = sd_journal_add_match(j, m3, 0)) ||
2d0b2e87
ZJS
1045 (r = sd_journal_add_match(j, muid, 0)) ||
1046 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1047
1048 /* Look for messages from authorized daemons about this service */
1049 (r = sd_journal_add_disjunction(j)) ||
fdcd37df 1050 (r = sd_journal_add_match(j, m4, 0)) ||
2d0b2e87 1051 (r = sd_journal_add_match(j, muid, 0)) ||
fdcd37df 1052 (r = sd_journal_add_match(j, "_UID=0", 0))
886a64fe 1053 );
1a6c43e9
MT
1054 return r;
1055}
1056
5ec76417
ZJS
1057int add_match_this_boot(sd_journal *j) {
1058 char match[9+32+1] = "_BOOT_ID=";
1059 sd_id128_t boot_id;
1060 int r;
1061
1062 assert(j);
1063
1064 r = sd_id128_get_boot(&boot_id);
1065 if (r < 0) {
1066 log_error("Failed to get boot id: %s", strerror(-r));
1067 return r;
1068 }
1069
1070 sd_id128_to_string(boot_id, match + 9);
1071 r = sd_journal_add_match(j, match, strlen(match));
1072 if (r < 0) {
1073 log_error("Failed to add match: %s", strerror(-r));
1074 return r;
1075 }
1076
1077 r = sd_journal_add_conjunction(j);
1078 if (r < 0)
1079 return r;
1080
1081 return 0;
1082}
1083
886a64fe 1084int show_journal_by_unit(
1a6c43e9
MT
1085 FILE *f,
1086 const char *unit,
1087 OutputMode mode,
1088 unsigned n_columns,
1089 usec_t not_before,
1090 unsigned how_many,
1091 uid_t uid,
886a64fe 1092 OutputFlags flags,
94e0bd7d
ZJS
1093 bool system,
1094 bool *ellipsized) {
1a6c43e9 1095
7fd1b19b 1096 _cleanup_journal_close_ sd_journal*j = NULL;
1a6c43e9 1097 int r;
a688baa8 1098 int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM;
1a6c43e9
MT
1099
1100 assert(mode >= 0);
1101 assert(mode < _OUTPUT_MODE_MAX);
1102 assert(unit);
1103
1a6c43e9
MT
1104 if (how_many <= 0)
1105 return 0;
1106
886a64fe 1107 r = sd_journal_open(&j, jflags);
f9045468 1108 if (r < 0)
763c7aa2 1109 return r;
f9045468 1110
5ec76417
ZJS
1111 r = add_match_this_boot(j);
1112 if (r < 0)
1113 return r;
1114
886a64fe
ZJS
1115 if (system)
1116 r = add_matches_for_unit(j, unit);
1117 else
1118 r = add_matches_for_user_unit(j, unit, uid);
1a6c43e9 1119 if (r < 0)
763c7aa2 1120 return r;
1a6c43e9 1121
4ad16808
ZJS
1122 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
1123 _cleanup_free_ char *filter;
1124
1125 filter = journal_make_match_string(j);
1126 log_debug("Journal filter: %s", filter);
1127 }
5ec76417 1128
94e0bd7d 1129 return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
86aa7ba4 1130}
df50185b
LP
1131
1132static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1133 [OUTPUT_SHORT] = "short",
67a12205 1134 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
44bc6e1f 1135 [OUTPUT_SHORT_ISO] = "short-iso",
df50185b
LP
1136 [OUTPUT_VERBOSE] = "verbose",
1137 [OUTPUT_EXPORT] = "export",
d3f2bdbf 1138 [OUTPUT_JSON] = "json",
a6e87e90 1139 [OUTPUT_JSON_PRETTY] = "json-pretty",
48383c25 1140 [OUTPUT_JSON_SSE] = "json-sse",
d3f2bdbf 1141 [OUTPUT_CAT] = "cat"
df50185b
LP
1142};
1143
1144DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);