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