]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/logs-show.c
build-sys: use correct cpp
[thirdparty/systemd.git] / src / shared / logs-show.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
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>
25 #include <sys/poll.h>
26 #include <string.h>
27
28 #include "logs-show.h"
29 #include "log.h"
30 #include "util.h"
31 #include "utf8.h"
32
33 #define PRINT_THRESHOLD 128
34
35 static 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;
52 buf = malloc(nl+1);
53 memcpy(buf, (const char*) data + fl, nl);
54 ((char*)buf)[nl] = 0;
55 if (!buf)
56 return log_oom();
57
58 free(*target);
59 *target = buf;
60 *target_size = nl;
61
62 return 1;
63 }
64
65 static 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
72 if (!utf8_is_printable_n(p, l))
73 return false;
74
75 return true;
76 }
77
78 static int output_short(sd_journal *j, unsigned line, unsigned n_columns,
79 OutputFlags flags) {
80 int r;
81 const void *data;
82 size_t length;
83 size_t n = 0;
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;
86
87 assert(j);
88
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
97 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
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
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
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
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
143 if (flags & OUTPUT_MONOTONIC_MODE) {
144 uint64_t t;
145 sd_id128_t boot_id;
146
147 r = -ENOENT;
148
149 if (monotonic)
150 r = safe_atou64(monotonic, &t);
151
152 if (r < 0)
153 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
154
155 if (r < 0) {
156 log_error("Failed to get monotonic: %s", strerror(-r));
157 goto finish;
158 }
159
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
166 } else {
167 char buf[64];
168 uint64_t x;
169 time_t t;
170 struct tm tm;
171
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);
179
180 if (r < 0) {
181 log_error("Failed to get realtime: %s", strerror(-r));
182 goto finish;
183 }
184
185 t = (time_t) (x / USEC_PER_SEC);
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 }
194
195 if (hostname && shall_print(flags & OUTPUT_SHOW_ALL,
196 hostname, hostname_len)) {
197 printf(" %.*s", (int) hostname_len, hostname);
198 n += hostname_len + 1;
199 }
200
201 if (identifier && shall_print(flags & OUTPUT_SHOW_ALL,
202 identifier, identifier_len)) {
203 printf(" %.*s", (int) identifier_len, identifier);
204 n += identifier_len + 1;
205 } else if (comm && shall_print(flags & OUTPUT_SHOW_ALL,
206 comm, comm_len)) {
207 printf(" %.*s", (int) comm_len, comm);
208 n += comm_len + 1;
209 } else
210 putchar(' ');
211
212 if (pid && shall_print(flags & OUTPUT_SHOW_ALL, pid, pid_len)) {
213 printf("[%.*s]", (int) pid_len, pid);
214 n += pid_len + 2;
215 } else if (fake_pid && shall_print(flags & OUTPUT_SHOW_ALL,
216 fake_pid, fake_pid_len)) {
217 printf("[%.*s]", (int) fake_pid_len, fake_pid);
218 n += fake_pid_len + 2;
219 }
220
221 if (flags & OUTPUT_SHOW_ALL)
222 printf(": %.*s\n", (int) message_len, message);
223 else if (!utf8_is_printable_n(message, message_len)) {
224 char bytes[FORMAT_BYTES_MAX];
225 printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
226 } else if ((flags & OUTPUT_FULL_WIDTH) ||
227 (message_len + n < n_columns))
228 printf(": %.*s\n", (int) message_len, message);
229 else if (n < n_columns && n_columns - n - 2 >= 3) {
230 char *e;
231
232 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
233
234 if (!e)
235 printf(": %.*s\n", (int) message_len, message);
236 else
237 printf(": %s\n", e);
238
239 free(e);
240 } else
241 fputs("\n", stdout);
242
243 r = 0;
244
245 finish:
246 free(hostname);
247 free(identifier);
248 free(comm);
249 free(pid);
250 free(fake_pid);
251 free(message);
252 free(monotonic);
253 free(realtime);
254
255 return r;
256 }
257
258 static 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);
261 }
262
263 static 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);
266 }
267
268 static int output_verbose(sd_journal *j, unsigned line,
269 unsigned n_columns, OutputFlags flags) {
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) {
298 if (!(flags & OUTPUT_SHOW_ALL) && (length > PRINT_THRESHOLD ||
299 !utf8_is_printable_n(data, length))) {
300 const char *c;
301 char bytes[FORMAT_BYTES_MAX];
302
303 c = memchr(data, '=', length);
304 if (!c) {
305 log_error("Invalid field.");
306 return -EINVAL;
307 }
308
309 printf("\t%.*s=[%s blob data]\n",
310 (int) (c - (const char*) data),
311 (const char*) data,
312 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
313 } else
314 printf("\t%.*s\n", (int) length, (const char*) data);
315 }
316
317 return 0;
318 }
319
320 static int output_export(sd_journal *j, unsigned line,
321 unsigned n_columns, OutputFlags flags) {
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
350 printf("__CURSOR=%s\n"
351 "__REALTIME_TIMESTAMP=%llu\n"
352 "__MONOTONIC_TIMESTAMP=%llu\n"
353 "_BOOT_ID=%s\n",
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
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
369 if (!utf8_is_printable_n(data, length)) {
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
395 static void json_escape(const char* p, size_t l) {
396 if (!utf8_is_printable_n(p, l)) {
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
432 static int output_json(sd_journal *j, unsigned line,
433 unsigned n_columns, OutputFlags flags) {
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"
468 "\t\"__CURSOR\" : \"%s\",\n"
469 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
470 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
471 "\t\"_BOOT_ID\" : \"%s\"",
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
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
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
506 static int output_cat(sd_journal *j, unsigned line,
507 unsigned n_columns, OutputFlags flags) {
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
528 static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line,
529 unsigned n_columns, OutputFlags flags) = {
530 [OUTPUT_SHORT] = output_short_realtime,
531 [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
532 [OUTPUT_VERBOSE] = output_verbose,
533 [OUTPUT_EXPORT] = output_export,
534 [OUTPUT_JSON] = output_json,
535 [OUTPUT_CAT] = output_cat
536 };
537
538 int output_journal(sd_journal *j, OutputMode mode, unsigned line,
539 unsigned n_columns, OutputFlags flags) {
540 assert(mode >= 0);
541 assert(mode < _OUTPUT_MODE_MAX);
542
543 if (n_columns <= 0)
544 n_columns = columns();
545
546 return output_funcs[mode](j, line, n_columns, flags);
547 }
548
549 int show_journal_by_unit(
550 const char *unit,
551 OutputMode mode,
552 unsigned n_columns,
553 usec_t not_before,
554 unsigned how_many,
555 OutputFlags flags) {
556
557 char *m = NULL;
558 sd_journal *j = NULL;
559 int r;
560 unsigned line = 0;
561 bool need_seek = false;
562 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
563
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;
573
574 if (how_many <= 0)
575 return 0;
576
577 if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) {
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
594 r = sd_journal_previous_skip(j, how_many);
595 if (r < 0)
596 goto finish;
597
598 if (mode == OUTPUT_JSON) {
599 fputc('[', stdout);
600 fflush(stdout);
601 }
602
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;
615
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
634 r = output_journal(j, mode, line, n_columns, flags);
635 if (r < 0)
636 goto finish;
637 }
638
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
653 if (r > 0 && not_before < cutoff)
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
659 if (!(flags & OUTPUT_FOLLOW))
660 break;
661
662 r = sd_journal_wait(j, (usec_t) -1);
663 if (r < 0)
664 goto finish;
665
666 }
667
668 if (mode == OUTPUT_JSON)
669 fputs("\n]\n", stdout);
670
671 finish:
672 if (m)
673 free(m);
674
675 if (j)
676 sd_journal_close(j);
677
678 return r;
679 }
680
681 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
682 [OUTPUT_SHORT] = "short",
683 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
684 [OUTPUT_VERBOSE] = "verbose",
685 [OUTPUT_EXPORT] = "export",
686 [OUTPUT_JSON] = "json",
687 [OUTPUT_CAT] = "cat"
688 };
689
690 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);