]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/logs-show.c
build-sys: use correct cpp
[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"
86aa7ba4
LP
32
33#define PRINT_THRESHOLD 128
34
55d7bfc1
LP
35static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
36 size_t fl, nl;
37 void *buf;
38
39 assert(data);
40 assert(field);
41 assert(target);
42 assert(target_size);
43
44 fl = strlen(field);
45 if (length < fl)
46 return 0;
47
48 if (memcmp(data, field, fl))
49 return 0;
50
51 nl = length - fl;
bf967366
LP
52 buf = malloc(nl+1);
53 memcpy(buf, (const char*) data + fl, nl);
54 ((char*)buf)[nl] = 0;
0d0f0c50
SL
55 if (!buf)
56 return log_oom();
55d7bfc1 57
6c1e6b98 58 free(*target);
55d7bfc1
LP
59 *target = buf;
60 *target_size = nl;
61
62 return 1;
63}
64
65static bool shall_print(bool show_all, char *p, size_t l) {
66 if (show_all)
67 return true;
68
69 if (l > PRINT_THRESHOLD)
70 return false;
71
ba961854 72 if (!utf8_is_printable_n(p, l))
55d7bfc1
LP
73 return false;
74
75 return true;
76}
77
25277cd7
ZJS
78static int output_short(sd_journal *j, unsigned line, unsigned n_columns,
79 OutputFlags flags) {
86aa7ba4 80 int r;
86aa7ba4
LP
81 const void *data;
82 size_t length;
83 size_t n = 0;
bf967366
LP
84 char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL;
85 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;
86aa7ba4
LP
86
87 assert(j);
88
55d7bfc1
LP
89 SD_JOURNAL_FOREACH_DATA(j, data, length) {
90
91 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
92 if (r < 0)
93 goto finish;
94 else if (r > 0)
95 continue;
96
4cd9a9d9 97 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
55d7bfc1
LP
98 if (r < 0)
99 goto finish;
100 else if (r > 0)
101 continue;
102
103 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
104 if (r < 0)
105 goto finish;
106 else if (r > 0)
107 continue;
108
109 r = parse_field(data, length, "_PID=", &pid, &pid_len);
110 if (r < 0)
111 goto finish;
112 else if (r > 0)
113 continue;
114
6c1e6b98
LP
115 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
116 if (r < 0)
117 goto finish;
118 else if (r > 0)
119 continue;
120
bf967366
LP
121 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
122 if (r < 0)
123 goto finish;
124 else if (r > 0)
125 continue;
126
127 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
128 if (r < 0)
129 goto finish;
130 else if (r > 0)
131 continue;
132
55d7bfc1
LP
133 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
134 if (r < 0)
135 goto finish;
136 }
137
138 if (!message) {
139 r = 0;
140 goto finish;
141 }
142
25277cd7 143 if (flags & OUTPUT_MONOTONIC_MODE) {
67a12205 144 uint64_t t;
3ebcdf8c
LP
145 sd_id128_t boot_id;
146
bf967366
LP
147 r = -ENOENT;
148
149 if (monotonic)
150 r = safe_atou64(monotonic, &t);
151
152 if (r < 0)
3ebcdf8c 153 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
86aa7ba4 154
3ebcdf8c
LP
155 if (r < 0) {
156 log_error("Failed to get monotonic: %s", strerror(-r));
157 goto finish;
67a12205
LP
158 }
159
3ebcdf8c
LP
160 printf("[%5llu.%06llu]",
161 (unsigned long long) (t / USEC_PER_SEC),
162 (unsigned long long) (t % USEC_PER_SEC));
163
164 n += 1 + 5 + 1 + 6 + 1;
165
67a12205
LP
166 } else {
167 char buf[64];
bf967366 168 uint64_t x;
67a12205
LP
169 time_t t;
170 struct tm tm;
731a676c 171
bf967366
LP
172 r = -ENOENT;
173
174 if (realtime)
175 r = safe_atou64(realtime, &x);
176
177 if (r < 0)
178 r = sd_journal_get_realtime_usec(j, &x);
67a12205 179
67a12205
LP
180 if (r < 0) {
181 log_error("Failed to get realtime: %s", strerror(-r));
182 goto finish;
183 }
184
bf967366 185 t = (time_t) (x / USEC_PER_SEC);
67a12205
LP
186 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
187 log_error("Failed to format time.");
188 goto finish;
189 }
190
191 fputs(buf, stdout);
192 n += strlen(buf);
193 }
86aa7ba4 194
25277cd7
ZJS
195 if (hostname && shall_print(flags & OUTPUT_SHOW_ALL,
196 hostname, hostname_len)) {
55d7bfc1
LP
197 printf(" %.*s", (int) hostname_len, hostname);
198 n += hostname_len + 1;
199 }
200
25277cd7
ZJS
201 if (identifier && shall_print(flags & OUTPUT_SHOW_ALL,
202 identifier, identifier_len)) {
4cd9a9d9
LP
203 printf(" %.*s", (int) identifier_len, identifier);
204 n += identifier_len + 1;
25277cd7
ZJS
205 } else if (comm && shall_print(flags & OUTPUT_SHOW_ALL,
206 comm, comm_len)) {
55d7bfc1
LP
207 printf(" %.*s", (int) comm_len, comm);
208 n += comm_len + 1;
b5936820
LP
209 } else
210 putchar(' ');
86aa7ba4 211
25277cd7 212 if (pid && shall_print(flags & OUTPUT_SHOW_ALL, pid, pid_len)) {
55d7bfc1
LP
213 printf("[%.*s]", (int) pid_len, pid);
214 n += pid_len + 2;
25277cd7
ZJS
215 } else if (fake_pid && shall_print(flags & OUTPUT_SHOW_ALL,
216 fake_pid, fake_pid_len)) {
6c1e6b98
LP
217 printf("[%.*s]", (int) fake_pid_len, fake_pid);
218 n += fake_pid_len + 2;
86aa7ba4
LP
219 }
220
25277cd7 221 if (flags & OUTPUT_SHOW_ALL)
55d7bfc1 222 printf(": %.*s\n", (int) message_len, message);
ba961854 223 else if (!utf8_is_printable_n(message, message_len)) {
e6acda19
LP
224 char bytes[FORMAT_BYTES_MAX];
225 printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
92a1fd9e
ZJS
226 } else if ((flags & OUTPUT_FULL_WIDTH) ||
227 (message_len + n < n_columns))
55d7bfc1 228 printf(": %.*s\n", (int) message_len, message);
b61a4660 229 else if (n < n_columns && n_columns - n - 2 >= 3) {
55d7bfc1
LP
230 char *e;
231
34a35ece 232 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
55d7bfc1
LP
233
234 if (!e)
235 printf(": %.*s\n", (int) message_len, message);
236 else
52beb2c3 237 printf(": %s\n", e);
55d7bfc1
LP
238
239 free(e);
240 } else
241 fputs("\n", stdout);
242
243 r = 0;
244
245finish:
246 free(hostname);
4cd9a9d9 247 free(identifier);
55d7bfc1
LP
248 free(comm);
249 free(pid);
6c1e6b98 250 free(fake_pid);
55d7bfc1 251 free(message);
bf967366
LP
252 free(monotonic);
253 free(realtime);
86aa7ba4 254
55d7bfc1 255 return r;
86aa7ba4
LP
256}
257
25277cd7
ZJS
258static int output_short_realtime(sd_journal *j, unsigned line,
259 unsigned n_columns, OutputFlags flags) {
260 return output_short(j, line, n_columns, flags & ~OUTPUT_MONOTONIC_MODE);
67a12205
LP
261}
262
25277cd7
ZJS
263static int output_short_monotonic(sd_journal *j, unsigned line,
264 unsigned n_columns, OutputFlags flags) {
265 return output_short(j, line, n_columns, flags | OUTPUT_MONOTONIC_MODE);
67a12205
LP
266}
267
25277cd7
ZJS
268static int output_verbose(sd_journal *j, unsigned line,
269 unsigned n_columns, OutputFlags flags) {
86aa7ba4
LP
270 const void *data;
271 size_t length;
272 char *cursor;
273 uint64_t realtime;
274 char ts[FORMAT_TIMESTAMP_MAX];
275 int r;
276
277 assert(j);
278
279 r = sd_journal_get_realtime_usec(j, &realtime);
280 if (r < 0) {
281 log_error("Failed to get realtime timestamp: %s", strerror(-r));
282 return r;
283 }
284
285 r = sd_journal_get_cursor(j, &cursor);
286 if (r < 0) {
287 log_error("Failed to get cursor: %s", strerror(-r));
288 return r;
289 }
290
291 printf("%s [%s]\n",
292 format_timestamp(ts, sizeof(ts), realtime),
293 cursor);
294
295 free(cursor);
296
297 SD_JOURNAL_FOREACH_DATA(j, data, length) {
25277cd7 298 if (!(flags & OUTPUT_SHOW_ALL) && (length > PRINT_THRESHOLD ||
ba961854 299 !utf8_is_printable_n(data, length))) {
86aa7ba4 300 const char *c;
e6acda19 301 char bytes[FORMAT_BYTES_MAX];
86aa7ba4
LP
302
303 c = memchr(data, '=', length);
304 if (!c) {
305 log_error("Invalid field.");
306 return -EINVAL;
307 }
308
e6acda19 309 printf("\t%.*s=[%s blob data]\n",
86aa7ba4 310 (int) (c - (const char*) data),
e6acda19
LP
311 (const char*) data,
312 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
86aa7ba4
LP
313 } else
314 printf("\t%.*s\n", (int) length, (const char*) data);
315 }
316
317 return 0;
318}
319
25277cd7
ZJS
320static int output_export(sd_journal *j, unsigned line,
321 unsigned n_columns, OutputFlags flags) {
86aa7ba4
LP
322 sd_id128_t boot_id;
323 char sid[33];
324 int r;
325 usec_t realtime, monotonic;
326 char *cursor;
327 const void *data;
328 size_t length;
329
330 assert(j);
331
332 r = sd_journal_get_realtime_usec(j, &realtime);
333 if (r < 0) {
334 log_error("Failed to get realtime timestamp: %s", strerror(-r));
335 return r;
336 }
337
338 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
339 if (r < 0) {
340 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
341 return r;
342 }
343
344 r = sd_journal_get_cursor(j, &cursor);
345 if (r < 0) {
346 log_error("Failed to get cursor: %s", strerror(-r));
347 return r;
348 }
349
ba8d3790 350 printf("__CURSOR=%s\n"
ffa16db0
LP
351 "__REALTIME_TIMESTAMP=%llu\n"
352 "__MONOTONIC_TIMESTAMP=%llu\n"
112301ae 353 "_BOOT_ID=%s\n",
86aa7ba4
LP
354 cursor,
355 (unsigned long long) realtime,
356 (unsigned long long) monotonic,
357 sd_id128_to_string(boot_id, sid));
358
359 free(cursor);
360
361 SD_JOURNAL_FOREACH_DATA(j, data, length) {
362
112301ae
LP
363 /* We already printed the boot id, from the data in
364 * the header, hence let's suppress it here */
365 if (length >= 9 &&
366 memcmp(data, "_BOOT_ID=", 9) == 0)
367 continue;
368
ba961854 369 if (!utf8_is_printable_n(data, length)) {
86aa7ba4
LP
370 const char *c;
371 uint64_t le64;
372
373 c = memchr(data, '=', length);
374 if (!c) {
375 log_error("Invalid field.");
376 return -EINVAL;
377 }
378
379 fwrite(data, c - (const char*) data, 1, stdout);
380 fputc('\n', stdout);
381 le64 = htole64(length - (c - (const char*) data) - 1);
382 fwrite(&le64, sizeof(le64), 1, stdout);
383 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
384 } else
385 fwrite(data, length, 1, stdout);
386
387 fputc('\n', stdout);
388 }
389
390 fputc('\n', stdout);
391
392 return 0;
393}
394
395static void json_escape(const char* p, size_t l) {
ba961854 396 if (!utf8_is_printable_n(p, l)) {
86aa7ba4
LP
397 bool not_first = false;
398
399 fputs("[ ", stdout);
400
401 while (l > 0) {
402 if (not_first)
403 printf(", %u", (uint8_t) *p);
404 else {
405 not_first = true;
406 printf("%u", (uint8_t) *p);
407 }
408
409 p++;
410 l--;
411 }
412
413 fputs(" ]", stdout);
414 } else {
415 fputc('\"', stdout);
416
417 while (l > 0) {
418 if (*p == '"' || *p == '\\') {
419 fputc('\\', stdout);
420 fputc(*p, stdout);
421 } else
422 fputc(*p, stdout);
423
424 p++;
425 l--;
426 }
427
428 fputc('\"', stdout);
429 }
430}
431
25277cd7
ZJS
432static int output_json(sd_journal *j, unsigned line,
433 unsigned n_columns, OutputFlags flags) {
86aa7ba4
LP
434 uint64_t realtime, monotonic;
435 char *cursor;
436 const void *data;
437 size_t length;
438 sd_id128_t boot_id;
439 char sid[33];
440 int r;
441
442 assert(j);
443
444 r = sd_journal_get_realtime_usec(j, &realtime);
445 if (r < 0) {
446 log_error("Failed to get realtime timestamp: %s", strerror(-r));
447 return r;
448 }
449
450 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
451 if (r < 0) {
452 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
453 return r;
454 }
455
456 r = sd_journal_get_cursor(j, &cursor);
457 if (r < 0) {
458 log_error("Failed to get cursor: %s", strerror(-r));
459 return r;
460 }
461
462 if (line == 1)
463 fputc('\n', stdout);
464 else
465 fputs(",\n", stdout);
466
467 printf("{\n"
ba8d3790 468 "\t\"__CURSOR\" : \"%s\",\n"
ffa16db0
LP
469 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
470 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
112301ae 471 "\t\"_BOOT_ID\" : \"%s\"",
86aa7ba4
LP
472 cursor,
473 (unsigned long long) realtime,
474 (unsigned long long) monotonic,
475 sd_id128_to_string(boot_id, sid));
476
477 free(cursor);
478
479 SD_JOURNAL_FOREACH_DATA(j, data, length) {
480 const char *c;
481
112301ae
LP
482 /* We already printed the boot id, from the data in
483 * the header, hence let's suppress it here */
484 if (length >= 9 &&
485 memcmp(data, "_BOOT_ID=", 9) == 0)
486 continue;
487
86aa7ba4
LP
488 c = memchr(data, '=', length);
489 if (!c) {
490 log_error("Invalid field.");
491 return -EINVAL;
492 }
493
494 fputs(",\n\t", stdout);
495 json_escape(data, c - (const char*) data);
496 fputs(" : ", stdout);
497 json_escape(c + 1, length - (c - (const char*) data) - 1);
498 }
499
500 fputs("\n}", stdout);
501 fflush(stdout);
502
503 return 0;
504}
505
25277cd7
ZJS
506static int output_cat(sd_journal *j, unsigned line,
507 unsigned n_columns, OutputFlags flags) {
d3f2bdbf
LP
508 const void *data;
509 size_t l;
510 int r;
511
512 assert(j);
513
514 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
515 if (r < 0) {
516 log_error("Failed to get data: %s", strerror(-r));
517 return r;
518 }
519
520 assert(l >= 8);
521
522 fwrite((const char*) data + 8, 1, l - 8, stdout);
523 putchar('\n');
524
525 return 0;
526}
527
25277cd7
ZJS
528static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line,
529 unsigned n_columns, OutputFlags flags) = {
67a12205
LP
530 [OUTPUT_SHORT] = output_short_realtime,
531 [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
86aa7ba4
LP
532 [OUTPUT_VERBOSE] = output_verbose,
533 [OUTPUT_EXPORT] = output_export,
d3f2bdbf
LP
534 [OUTPUT_JSON] = output_json,
535 [OUTPUT_CAT] = output_cat
86aa7ba4
LP
536};
537
25277cd7
ZJS
538int output_journal(sd_journal *j, OutputMode mode, unsigned line,
539 unsigned n_columns, OutputFlags flags) {
df50185b 540 assert(mode >= 0);
86aa7ba4
LP
541 assert(mode < _OUTPUT_MODE_MAX);
542
34a35ece
LP
543 if (n_columns <= 0)
544 n_columns = columns();
545
25277cd7 546 return output_funcs[mode](j, line, n_columns, flags);
86aa7ba4
LP
547}
548
df50185b
LP
549int show_journal_by_unit(
550 const char *unit,
551 OutputMode mode,
86aa7ba4
LP
552 unsigned n_columns,
553 usec_t not_before,
554 unsigned how_many,
085d7120 555 OutputFlags flags) {
86aa7ba4
LP
556
557 char *m = NULL;
7ea07dcd 558 sd_journal *j = NULL;
86aa7ba4 559 int r;
df50185b
LP
560 unsigned line = 0;
561 bool need_seek = false;
085d7120 562 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
86aa7ba4 563
df50185b
LP
564 assert(mode >= 0);
565 assert(mode < _OUTPUT_MODE_MAX);
566 assert(unit);
567
568 if (!endswith(unit, ".service") &&
569 !endswith(unit, ".socket") &&
570 !endswith(unit, ".mount") &&
571 !endswith(unit, ".swap"))
572 return 0;
86aa7ba4 573
df50185b 574 if (how_many <= 0)
f4fb21c1
LP
575 return 0;
576
df50185b 577 if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) {
86aa7ba4
LP
578 r = -ENOMEM;
579 goto finish;
580 }
581
582 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
583 if (r < 0)
584 goto finish;
585
586 r = sd_journal_add_match(j, m, strlen(m));
587 if (r < 0)
588 goto finish;
589
590 r = sd_journal_seek_tail(j);
591 if (r < 0)
592 goto finish;
593
df50185b
LP
594 r = sd_journal_previous_skip(j, how_many);
595 if (r < 0)
596 goto finish;
86aa7ba4 597
df50185b
LP
598 if (mode == OUTPUT_JSON) {
599 fputc('[', stdout);
600 fflush(stdout);
601 }
86aa7ba4 602
df50185b
LP
603 for (;;) {
604 for (;;) {
605 usec_t usec;
606
607 if (need_seek) {
608 r = sd_journal_next(j);
609 if (r < 0)
610 goto finish;
611 }
612
613 if (r == 0)
614 break;
86aa7ba4 615
df50185b
LP
616 need_seek = true;
617
618 if (not_before > 0) {
619 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
620
621 /* -ESTALE is returned if the
622 timestamp is not from this boot */
623 if (r == -ESTALE)
624 continue;
625 else if (r < 0)
626 goto finish;
627
628 if (usec < not_before)
629 continue;
630 }
631
632 line ++;
633
085d7120 634 r = output_journal(j, mode, line, n_columns, flags);
df50185b
LP
635 if (r < 0)
636 goto finish;
637 }
638
08984293
LP
639 if (warn_cutoff && line < how_many && not_before > 0) {
640 sd_id128_t boot_id;
641 usec_t cutoff;
642
643 /* Check whether the cutoff line is too early */
644
645 r = sd_id128_get_boot(&boot_id);
646 if (r < 0)
647 goto finish;
648
649 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
650 if (r < 0)
651 goto finish;
652
b59866ae 653 if (r > 0 && not_before < cutoff)
08984293
LP
654 printf("Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
655
656 warn_cutoff = false;
657 }
658
085d7120 659 if (!(flags & OUTPUT_FOLLOW))
86aa7ba4
LP
660 break;
661
e02d1cf7 662 r = sd_journal_wait(j, (usec_t) -1);
df50185b
LP
663 if (r < 0)
664 goto finish;
665
86aa7ba4
LP
666 }
667
df50185b
LP
668 if (mode == OUTPUT_JSON)
669 fputs("\n]\n", stdout);
670
86aa7ba4
LP
671finish:
672 if (m)
673 free(m);
674
675 if (j)
676 sd_journal_close(j);
677
678 return r;
679}
df50185b
LP
680
681static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
682 [OUTPUT_SHORT] = "short",
67a12205 683 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
df50185b
LP
684 [OUTPUT_VERBOSE] = "verbose",
685 [OUTPUT_EXPORT] = "export",
d3f2bdbf
LP
686 [OUTPUT_JSON] = "json",
687 [OUTPUT_CAT] = "cat"
df50185b
LP
688};
689
690DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);