chiark / gitweb /
journalctl: always show monotonic timestamp even if it's from an old boot
[elogind.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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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, 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                 r = -ENOENT;
182
183                 if (realtime)
184                         r = safe_atou64(realtime, &x);
185
186                 if (r < 0)
187                         r = sd_journal_get_realtime_usec(j, &x);
188
189                 if (r < 0) {
190                         log_error("Failed to get realtime: %s", strerror(-r));
191                         goto finish;
192                 }
193
194                 t = (time_t) (x / USEC_PER_SEC);
195                 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
196                         log_error("Failed to format time.");
197                         goto finish;
198                 }
199
200                 fputs(buf, stdout);
201                 n += strlen(buf);
202         }
203
204         if (hostname && shall_print(show_all, hostname, hostname_len)) {
205                 printf(" %.*s", (int) hostname_len, hostname);
206                 n += hostname_len + 1;
207         }
208
209         if (identifier && shall_print(show_all, identifier, identifier_len)) {
210                 printf(" %.*s", (int) identifier_len, identifier);
211                 n += identifier_len + 1;
212         } else if (comm && shall_print(show_all, comm, comm_len)) {
213                 printf(" %.*s", (int) comm_len, comm);
214                 n += comm_len + 1;
215         }
216
217         if (pid && shall_print(show_all, pid, pid_len)) {
218                 printf("[%.*s]", (int) pid_len, pid);
219                 n += pid_len + 2;
220         } else if (fake_pid && shall_print(show_all, fake_pid, fake_pid_len)) {
221                 printf("[%.*s]", (int) fake_pid_len, fake_pid);
222                 n += fake_pid_len + 2;
223         }
224
225         if (show_all)
226                 printf(": %.*s\n", (int) message_len, message);
227         else if (contains_unprintable(message, message_len))
228                 fputs(": [blob data]\n", stdout);
229         else if (message_len + n < columns())
230                 printf(": %.*s\n", (int) message_len, message);
231         else if (n < columns()) {
232                 char *e;
233
234                 e = ellipsize_mem(message, message_len, columns() - n - 2, 90);
235
236                 if (!e)
237                         printf(": %.*s\n", (int) message_len, message);
238                 else
239                         printf(": %s", e);
240
241                 free(e);
242         } else
243                 fputs("\n", stdout);
244
245         r = 0;
246
247 finish:
248         free(hostname);
249         free(identifier);
250         free(comm);
251         free(pid);
252         free(fake_pid);
253         free(message);
254         free(monotonic);
255         free(realtime);
256
257         return r;
258 }
259
260 static int output_short_realtime(sd_journal *j, unsigned line, bool show_all) {
261         return output_short(j, line, show_all, false);
262 }
263
264 static int output_short_monotonic(sd_journal *j, unsigned line, bool show_all) {
265         return output_short(j, line, show_all, true);
266 }
267
268 static int output_verbose(sd_journal *j, unsigned line, bool show_all) {
269         const void *data;
270         size_t length;
271         char *cursor;
272         uint64_t realtime;
273         char ts[FORMAT_TIMESTAMP_MAX];
274         int r;
275
276         assert(j);
277
278         r = sd_journal_get_realtime_usec(j, &realtime);
279         if (r < 0) {
280                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
281                 return r;
282         }
283
284         r = sd_journal_get_cursor(j, &cursor);
285         if (r < 0) {
286                 log_error("Failed to get cursor: %s", strerror(-r));
287                 return r;
288         }
289
290         printf("%s [%s]\n",
291                format_timestamp(ts, sizeof(ts), realtime),
292                cursor);
293
294         free(cursor);
295
296         SD_JOURNAL_FOREACH_DATA(j, data, length) {
297                 if (!show_all && (length > PRINT_THRESHOLD ||
298                                   contains_unprintable(data, length))) {
299                         const char *c;
300
301                         c = memchr(data, '=', length);
302                         if (!c) {
303                                 log_error("Invalid field.");
304                                 return -EINVAL;
305                         }
306
307                         printf("\t%.*s=[blob data]\n",
308                                (int) (c - (const char*) data),
309                                (const char*) data);
310                 } else
311                         printf("\t%.*s\n", (int) length, (const char*) data);
312         }
313
314         return 0;
315 }
316
317 static int output_export(sd_journal *j, unsigned line, bool show_all) {
318         sd_id128_t boot_id;
319         char sid[33];
320         int r;
321         usec_t realtime, monotonic;
322         char *cursor;
323         const void *data;
324         size_t length;
325
326         assert(j);
327
328         r = sd_journal_get_realtime_usec(j, &realtime);
329         if (r < 0) {
330                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
331                 return r;
332         }
333
334         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
335         if (r < 0) {
336                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
337                 return r;
338         }
339
340         r = sd_journal_get_cursor(j, &cursor);
341         if (r < 0) {
342                 log_error("Failed to get cursor: %s", strerror(-r));
343                 return r;
344         }
345
346         printf(".cursor=%s\n"
347                ".realtime=%llu\n"
348                ".monotonic=%llu\n"
349                ".boot_id=%s\n",
350                cursor,
351                (unsigned long long) realtime,
352                (unsigned long long) monotonic,
353                sd_id128_to_string(boot_id, sid));
354
355         free(cursor);
356
357         SD_JOURNAL_FOREACH_DATA(j, data, length) {
358
359                 if (contains_unprintable(data, length)) {
360                         const char *c;
361                         uint64_t le64;
362
363                         c = memchr(data, '=', length);
364                         if (!c) {
365                                 log_error("Invalid field.");
366                                 return -EINVAL;
367                         }
368
369                         fwrite(data, c - (const char*) data, 1, stdout);
370                         fputc('\n', stdout);
371                         le64 = htole64(length - (c - (const char*) data) - 1);
372                         fwrite(&le64, sizeof(le64), 1, stdout);
373                         fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
374                 } else
375                         fwrite(data, length, 1, stdout);
376
377                 fputc('\n', stdout);
378         }
379
380         fputc('\n', stdout);
381
382         return 0;
383 }
384
385 static void json_escape(const char* p, size_t l) {
386
387         if (contains_unprintable(p, l)) {
388                 bool not_first = false;
389
390                 fputs("[ ", stdout);
391
392                 while (l > 0) {
393                         if (not_first)
394                                 printf(", %u", (uint8_t) *p);
395                         else {
396                                 not_first = true;
397                                 printf("%u", (uint8_t) *p);
398                         }
399
400                         p++;
401                         l--;
402                 }
403
404                 fputs(" ]", stdout);
405         } else {
406                 fputc('\"', stdout);
407
408                 while (l > 0) {
409                         if (*p == '"' || *p == '\\') {
410                                 fputc('\\', stdout);
411                                 fputc(*p, stdout);
412                         } else
413                                 fputc(*p, stdout);
414
415                         p++;
416                         l--;
417                 }
418
419                 fputc('\"', stdout);
420         }
421 }
422
423 static int output_json(sd_journal *j, unsigned line, bool show_all) {
424         uint64_t realtime, monotonic;
425         char *cursor;
426         const void *data;
427         size_t length;
428         sd_id128_t boot_id;
429         char sid[33];
430         int r;
431
432         assert(j);
433
434         r = sd_journal_get_realtime_usec(j, &realtime);
435         if (r < 0) {
436                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
437                 return r;
438         }
439
440         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
441         if (r < 0) {
442                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
443                 return r;
444         }
445
446         r = sd_journal_get_cursor(j, &cursor);
447         if (r < 0) {
448                 log_error("Failed to get cursor: %s", strerror(-r));
449                 return r;
450         }
451
452         if (line == 1)
453                 fputc('\n', stdout);
454         else
455                 fputs(",\n", stdout);
456
457         printf("{\n"
458                "\t\".cursor\" : \"%s\",\n"
459                "\t\".realtime\" : %llu,\n"
460                "\t\".monotonic\" : %llu,\n"
461                "\t\".boot_id\" : \"%s\"",
462                cursor,
463                (unsigned long long) realtime,
464                (unsigned long long) monotonic,
465                sd_id128_to_string(boot_id, sid));
466
467         free(cursor);
468
469         SD_JOURNAL_FOREACH_DATA(j, data, length) {
470                 const char *c;
471
472                 c = memchr(data, '=', length);
473                 if (!c) {
474                         log_error("Invalid field.");
475                         return -EINVAL;
476                 }
477
478                 fputs(",\n\t", stdout);
479                 json_escape(data, c - (const char*) data);
480                 fputs(" : ", stdout);
481                 json_escape(c + 1, length - (c - (const char*) data) - 1);
482         }
483
484         fputs("\n}", stdout);
485         fflush(stdout);
486
487         return 0;
488 }
489
490 static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line, bool show_all) = {
491         [OUTPUT_SHORT] = output_short_realtime,
492         [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
493         [OUTPUT_VERBOSE] = output_verbose,
494         [OUTPUT_EXPORT] = output_export,
495         [OUTPUT_JSON] = output_json
496 };
497
498 int output_journal(sd_journal *j, OutputMode mode, unsigned line, bool show_all) {
499         assert(mode >= 0);
500         assert(mode < _OUTPUT_MODE_MAX);
501
502         return output_funcs[mode](j, line, show_all);
503 }
504
505 int show_journal_by_unit(
506                 const char *unit,
507                 OutputMode mode,
508                 const char *prefix,
509                 unsigned n_columns,
510                 usec_t not_before,
511                 unsigned how_many,
512                 bool show_all,
513                 bool follow) {
514
515         char *m = NULL;
516         sd_journal *j;
517         int r;
518         int fd;
519         unsigned line = 0;
520         bool need_seek = false;
521
522         assert(mode >= 0);
523         assert(mode < _OUTPUT_MODE_MAX);
524         assert(unit);
525
526         if (!endswith(unit, ".service") &&
527             !endswith(unit, ".socket") &&
528             !endswith(unit, ".mount") &&
529             !endswith(unit, ".swap"))
530                 return 0;
531
532         if (how_many <= 0)
533                 return 0;
534
535         if (n_columns <= 0)
536                 n_columns = columns();
537
538         if (!prefix)
539                 prefix = "";
540
541         if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) {
542                 r = -ENOMEM;
543                 goto finish;
544         }
545
546         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
547         if (r < 0)
548                 goto finish;
549
550         fd = sd_journal_get_fd(j);
551         if (fd < 0)
552                 goto finish;
553
554         r = sd_journal_add_match(j, m, strlen(m));
555         if (r < 0)
556                 goto finish;
557
558         r = sd_journal_seek_tail(j);
559         if (r < 0)
560                 goto finish;
561
562         r = sd_journal_previous_skip(j, how_many);
563         if (r < 0)
564                 goto finish;
565
566         if (mode == OUTPUT_JSON) {
567                 fputc('[', stdout);
568                 fflush(stdout);
569         }
570
571         for (;;) {
572                 for (;;) {
573                         usec_t usec;
574
575                         if (need_seek) {
576                                 r = sd_journal_next(j);
577                                 if (r < 0)
578                                         goto finish;
579                         }
580
581                         if (r == 0)
582                                 break;
583
584                         need_seek = true;
585
586                         if (not_before > 0) {
587                                 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
588
589                                 /* -ESTALE is returned if the
590                                    timestamp is not from this boot */
591                                 if (r == -ESTALE)
592                                         continue;
593                                 else if (r < 0)
594                                         goto finish;
595
596                                 if (usec < not_before)
597                                         continue;
598                         }
599
600                         line ++;
601
602                         r = output_journal(j, mode, line, show_all);
603                         if (r < 0)
604                                 goto finish;
605                 }
606
607                 if (!follow)
608                         break;
609
610                 r = fd_wait_for_event(fd, POLLIN);
611                 if (r < 0)
612                         goto finish;
613
614                 r = sd_journal_process(j);
615                 if (r < 0)
616                         goto finish;
617
618         }
619
620         if (mode == OUTPUT_JSON)
621                 fputs("\n]\n", stdout);
622
623 finish:
624         if (m)
625                 free(m);
626
627         if (j)
628                 sd_journal_close(j);
629
630         return r;
631 }
632
633 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
634         [OUTPUT_SHORT] = "short",
635         [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
636         [OUTPUT_VERBOSE] = "verbose",
637         [OUTPUT_EXPORT] = "export",
638         [OUTPUT_JSON] = "json"
639 };
640
641 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);