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