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