]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/logs-show.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / 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
32 #define PRINT_THRESHOLD 128
33
34 static 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
44 static 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;
61 buf = malloc(nl+1);
62 memcpy(buf, (const char*) data + fl, nl);
63 ((char*)buf)[nl] = 0;
64 if (!buf) {
65 log_error("Out of memory");
66 return -ENOMEM;
67 }
68
69 free(*target);
70 *target = buf;
71 *target_size = nl;
72
73 return 1;
74 }
75
76 static 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
89 static int output_short(sd_journal *j, unsigned line, unsigned n_columns, bool show_all, bool monotonic_mode) {
90 int r;
91 const void *data;
92 size_t length;
93 size_t n = 0;
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;
96
97 assert(j);
98
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
107 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
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
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
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
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
153 if (monotonic_mode) {
154 uint64_t t;
155 sd_id128_t boot_id;
156
157 r = -ENOENT;
158
159 if (monotonic)
160 r = safe_atou64(monotonic, &t);
161
162 if (r < 0)
163 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
164
165 if (r < 0) {
166 log_error("Failed to get monotonic: %s", strerror(-r));
167 goto finish;
168 }
169
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
176 } else {
177 char buf[64];
178 uint64_t x;
179 time_t t;
180 struct tm tm;
181
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);
189
190 if (r < 0) {
191 log_error("Failed to get realtime: %s", strerror(-r));
192 goto finish;
193 }
194
195 t = (time_t) (x / USEC_PER_SEC);
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 }
204
205 if (hostname && shall_print(show_all, hostname, hostname_len)) {
206 printf(" %.*s", (int) hostname_len, hostname);
207 n += hostname_len + 1;
208 }
209
210 if (identifier && shall_print(show_all, identifier, identifier_len)) {
211 printf(" %.*s", (int) identifier_len, identifier);
212 n += identifier_len + 1;
213 } else if (comm && shall_print(show_all, comm, comm_len)) {
214 printf(" %.*s", (int) comm_len, comm);
215 n += comm_len + 1;
216 } else
217 putchar(' ');
218
219 if (pid && shall_print(show_all, pid, pid_len)) {
220 printf("[%.*s]", (int) pid_len, pid);
221 n += pid_len + 2;
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;
225 }
226
227 if (show_all)
228 printf(": %.*s\n", (int) message_len, message);
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));
232 } else if (message_len + n < n_columns)
233 printf(": %.*s\n", (int) message_len, message);
234 else if (n < n_columns) {
235 char *e;
236
237 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
238
239 if (!e)
240 printf(": %.*s\n", (int) message_len, message);
241 else
242 printf(": %s\n", e);
243
244 free(e);
245 } else
246 fputs("\n", stdout);
247
248 r = 0;
249
250 finish:
251 free(hostname);
252 free(identifier);
253 free(comm);
254 free(pid);
255 free(fake_pid);
256 free(message);
257 free(monotonic);
258 free(realtime);
259
260 return r;
261 }
262
263 static 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);
265 }
266
267 static 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);
269 }
270
271 static int output_verbose(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
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;
303 char bytes[FORMAT_BYTES_MAX];
304
305 c = memchr(data, '=', length);
306 if (!c) {
307 log_error("Invalid field.");
308 return -EINVAL;
309 }
310
311 printf("\t%.*s=[%s blob data]\n",
312 (int) (c - (const char*) data),
313 (const char*) data,
314 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
315 } else
316 printf("\t%.*s\n", (int) length, (const char*) data);
317 }
318
319 return 0;
320 }
321
322 static int output_export(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
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
351 printf("__CURSOR=%s\n"
352 "__REALTIME_TIMESTAMP=%llu\n"
353 "__MONOTONIC_TIMESTAMP=%llu\n"
354 "_BOOT_ID=%s\n",
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
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
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
396 static 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
434 static int output_json(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
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"
469 "\t\"__CURSOR\" : \"%s\",\n"
470 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
471 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
472 "\t\"_BOOT_ID\" : \"%s\"",
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
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
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
507 static int output_cat(sd_journal *j, unsigned line, unsigned n_columns, bool show_all) {
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, unsigned n_columns, bool show_all) = {
529 [OUTPUT_SHORT] = output_short_realtime,
530 [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
531 [OUTPUT_VERBOSE] = output_verbose,
532 [OUTPUT_EXPORT] = output_export,
533 [OUTPUT_JSON] = output_json,
534 [OUTPUT_CAT] = output_cat
535 };
536
537 int output_journal(sd_journal *j, OutputMode mode, unsigned line, unsigned n_columns, bool show_all) {
538 assert(mode >= 0);
539 assert(mode < _OUTPUT_MODE_MAX);
540
541 if (n_columns <= 0)
542 n_columns = columns();
543
544 return output_funcs[mode](j, line, n_columns, show_all);
545 }
546
547 int show_journal_by_unit(
548 const char *unit,
549 OutputMode mode,
550 unsigned n_columns,
551 usec_t not_before,
552 unsigned how_many,
553 bool show_all,
554 bool follow) {
555
556 char *m = NULL;
557 sd_journal *j;
558 int r;
559 int fd;
560 unsigned line = 0;
561 bool need_seek = false;
562
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;
572
573 if (how_many <= 0)
574 return 0;
575
576 if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) {
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 fd = sd_journal_get_fd(j);
586 if (fd < 0)
587 goto finish;
588
589 r = sd_journal_add_match(j, m, strlen(m));
590 if (r < 0)
591 goto finish;
592
593 r = sd_journal_seek_tail(j);
594 if (r < 0)
595 goto finish;
596
597 r = sd_journal_previous_skip(j, how_many);
598 if (r < 0)
599 goto finish;
600
601 if (mode == OUTPUT_JSON) {
602 fputc('[', stdout);
603 fflush(stdout);
604 }
605
606 for (;;) {
607 for (;;) {
608 usec_t usec;
609
610 if (need_seek) {
611 r = sd_journal_next(j);
612 if (r < 0)
613 goto finish;
614 }
615
616 if (r == 0)
617 break;
618
619 need_seek = true;
620
621 if (not_before > 0) {
622 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
623
624 /* -ESTALE is returned if the
625 timestamp is not from this boot */
626 if (r == -ESTALE)
627 continue;
628 else if (r < 0)
629 goto finish;
630
631 if (usec < not_before)
632 continue;
633 }
634
635 line ++;
636
637 r = output_journal(j, mode, line, n_columns, show_all);
638 if (r < 0)
639 goto finish;
640 }
641
642 if (!follow)
643 break;
644
645 r = fd_wait_for_event(fd, POLLIN, (usec_t) -1);
646 if (r < 0)
647 goto finish;
648
649 r = sd_journal_process(j);
650 if (r < 0)
651 goto finish;
652
653 }
654
655 if (mode == OUTPUT_JSON)
656 fputs("\n]\n", stdout);
657
658 finish:
659 if (m)
660 free(m);
661
662 if (j)
663 sd_journal_close(j);
664
665 return r;
666 }
667
668 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
669 [OUTPUT_SHORT] = "short",
670 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
671 [OUTPUT_VERBOSE] = "verbose",
672 [OUTPUT_EXPORT] = "export",
673 [OUTPUT_JSON] = "json",
674 [OUTPUT_CAT] = "cat"
675 };
676
677 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);