chiark / gitweb /
journalctl: highlight MESSAGE= in verbose output
[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 #include "hashmap.h"
33 #include "journal-internal.h"
34
35 #define PRINT_THRESHOLD 128
36 #define JSON_THRESHOLD 4096
37
38 static int print_catalog(FILE *f, sd_journal *j) {
39         int r;
40         _cleanup_free_ char *t = NULL, *z = NULL;
41
42
43         r = sd_journal_get_catalog(j, &t);
44         if (r < 0)
45                 return r;
46
47         z = strreplace(strstrip(t), "\n", "\n-- ");
48         if (!z)
49                 return log_oom();
50
51         fputs("-- ", f);
52         fputs(z, f);
53         fputc('\n', f);
54
55         return 0;
56 }
57
58 static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
59         size_t fl, nl;
60         void *buf;
61
62         assert(data);
63         assert(field);
64         assert(target);
65         assert(target_size);
66
67         fl = strlen(field);
68         if (length < fl)
69                 return 0;
70
71         if (memcmp(data, field, fl))
72                 return 0;
73
74         nl = length - fl;
75         buf = malloc(nl+1);
76         if (!buf)
77                 return log_oom();
78
79         memcpy(buf, (const char*) data + fl, nl);
80         ((char*)buf)[nl] = 0;
81
82         free(*target);
83         *target = buf;
84         *target_size = nl;
85
86         return 1;
87 }
88
89 static bool shall_print(const char *p, size_t l, OutputFlags flags) {
90         assert(p);
91
92         if (flags & OUTPUT_SHOW_ALL)
93                 return true;
94
95         if (l >= PRINT_THRESHOLD)
96                 return false;
97
98         if (!utf8_is_printable(p, l))
99                 return false;
100
101         return true;
102 }
103
104 static void print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputMode flags, int priority, const char* message, size_t message_len) {
105         const char *color_on = "", *color_off = "";
106         const char *pos, *end;
107         bool continuation = false;
108
109         if (flags & OUTPUT_COLOR) {
110                 if (priority <= LOG_ERR) {
111                         color_on = ANSI_HIGHLIGHT_RED_ON;
112                         color_off = ANSI_HIGHLIGHT_OFF;
113                 } else if (priority <= LOG_NOTICE) {
114                         color_on = ANSI_HIGHLIGHT_ON;
115                         color_off = ANSI_HIGHLIGHT_OFF;
116                 }
117         }
118
119         for (pos = message; pos < message + message_len; pos = end + 1) {
120                 int len;
121                 for (end = pos; end < message + message_len && *end != '\n'; end++)
122                         ;
123                 len = end - pos;
124                 assert(len >= 0);
125
126                 if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) || prefix + len + 1 < n_columns)
127                         fprintf(f, "%*s%s%.*s%s\n",
128                                 continuation * prefix, "",
129                                 color_on, len, pos, color_off);
130                 else if (prefix < n_columns && n_columns - prefix >= 3) {
131                         _cleanup_free_ char *e;
132
133                         e = ellipsize_mem(pos, len, n_columns - prefix, 90);
134
135                         if (!e)
136                                 fprintf(f, "%s%.*s%s\n", color_on, len, pos, color_off);
137                         else
138                                 fprintf(f, "%s%s%s\n", color_on, e, color_off);
139                 } else
140                         fputs("...\n", f);
141
142                 continuation = true;
143         }
144 }
145
146 static int output_short(
147                 FILE *f,
148                 sd_journal *j,
149                 OutputMode mode,
150                 unsigned n_columns,
151                 OutputFlags flags) {
152
153         int r;
154         const void *data;
155         size_t length;
156         size_t n = 0;
157         _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
158         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;
159         int p = LOG_INFO;
160
161         assert(f);
162         assert(j);
163
164         sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : PRINT_THRESHOLD);
165
166         JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
167
168                 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
169                 if (r < 0)
170                         return r;
171                 else if (r > 0)
172                         continue;
173
174                 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
175                 if (r < 0)
176                         return r;
177                 else if (r > 0)
178                         continue;
179
180                 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
181                 if (r < 0)
182                         return r;
183                 else if (r > 0)
184                         continue;
185
186                 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
187                 if (r < 0)
188                         return r;
189                 else if (r > 0)
190                         continue;
191
192                 r = parse_field(data, length, "_PID=", &pid, &pid_len);
193                 if (r < 0)
194                         return r;
195                 else if (r > 0)
196                         continue;
197
198                 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
199                 if (r < 0)
200                         return r;
201                 else if (r > 0)
202                         continue;
203
204                 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
205                 if (r < 0)
206                         return r;
207                 else if (r > 0)
208                         continue;
209
210                 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
211                 if (r < 0)
212                         return r;
213                 else if (r > 0)
214                         continue;
215
216                 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
217                 if (r < 0)
218                         return r;
219         }
220
221         if (r < 0)
222                 return r;
223
224         if (!message)
225                 return 0;
226
227         if (!(flags & OUTPUT_SHOW_ALL))
228                 strip_tab_ansi(&message, &message_len);
229
230         if (priority_len == 1 && *priority >= '0' && *priority <= '7')
231                 p = *priority - '0';
232
233         if (mode == OUTPUT_SHORT_MONOTONIC) {
234                 uint64_t t;
235                 sd_id128_t boot_id;
236
237                 r = -ENOENT;
238
239                 if (monotonic)
240                         r = safe_atou64(monotonic, &t);
241
242                 if (r < 0)
243                         r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
244
245                 if (r < 0) {
246                         log_error("Failed to get monotonic timestamp: %s", strerror(-r));
247                         return r;
248                 }
249
250                 fprintf(f, "[%5llu.%06llu]",
251                         (unsigned long long) (t / USEC_PER_SEC),
252                         (unsigned long long) (t % USEC_PER_SEC));
253
254                 n += 1 + 5 + 1 + 6 + 1;
255
256         } else {
257                 char buf[64];
258                 uint64_t x;
259                 time_t t;
260                 struct tm tm;
261
262                 r = -ENOENT;
263
264                 if (realtime)
265                         r = safe_atou64(realtime, &x);
266
267                 if (r < 0)
268                         r = sd_journal_get_realtime_usec(j, &x);
269
270                 if (r < 0) {
271                         log_error("Failed to get realtime timestamp: %s", strerror(-r));
272                         return r;
273                 }
274
275                 t = (time_t) (x / USEC_PER_SEC);
276                 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
277                         log_error("Failed to format time.");
278                         return r;
279                 }
280
281                 fputs(buf, f);
282                 n += strlen(buf);
283         }
284
285         if (hostname && shall_print(hostname, hostname_len, flags)) {
286                 fprintf(f, " %.*s", (int) hostname_len, hostname);
287                 n += hostname_len + 1;
288         }
289
290         if (identifier && shall_print(identifier, identifier_len, flags)) {
291                 fprintf(f, " %.*s", (int) identifier_len, identifier);
292                 n += identifier_len + 1;
293         } else if (comm && shall_print(comm, comm_len, flags)) {
294                 fprintf(f, " %.*s", (int) comm_len, comm);
295                 n += comm_len + 1;
296         } else
297                 fputc(' ', f);
298
299         if (pid && shall_print(pid, pid_len, flags)) {
300                 fprintf(f, "[%.*s]", (int) pid_len, pid);
301                 n += pid_len + 2;
302         } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
303                 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
304                 n += fake_pid_len + 2;
305         }
306
307         if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
308                 char bytes[FORMAT_BYTES_MAX];
309                 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
310         } else {
311                 fputs(": ", f);
312                 print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
313         }
314
315         if (flags & OUTPUT_CATALOG)
316                 print_catalog(f, j);
317
318         return 0;
319 }
320
321 static int output_verbose(
322                 FILE *f,
323                 sd_journal *j,
324                 OutputMode mode,
325                 unsigned n_columns,
326                 OutputFlags flags) {
327
328         const void *data;
329         size_t length;
330         _cleanup_free_ char *cursor = NULL;
331         uint64_t realtime;
332         char ts[FORMAT_TIMESTAMP_MAX];
333         int r;
334
335         assert(f);
336         assert(j);
337
338         sd_journal_set_data_threshold(j, 0);
339
340         r = sd_journal_get_realtime_usec(j, &realtime);
341         if (r < 0) {
342                 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
343                          "Failed to get realtime timestamp: %s", strerror(-r));
344                 return r;
345         }
346
347         r = sd_journal_get_cursor(j, &cursor);
348         if (r < 0) {
349                 log_error("Failed to get cursor: %s", strerror(-r));
350                 return r;
351         }
352
353         fprintf(f, "%s [%s]\n",
354                 format_timestamp(ts, sizeof(ts), realtime),
355                 cursor);
356
357         JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
358                 const char *c;
359                 int fieldlen;
360                 const char *on = "", *off = "";
361
362                 c = memchr(data, '=', length);
363                 if (!c) {
364                         log_error("Invalid field.");
365                         return -EINVAL;
366                 }
367                 fieldlen = c - (const char*) data;
368
369                 if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
370                         on = ANSI_HIGHLIGHT_ON;
371                         off = ANSI_HIGHLIGHT_OFF;
372                 }
373
374                 if (flags & OUTPUT_SHOW_ALL ||
375                     (((length < PRINT_THRESHOLD) || flags & OUTPUT_FULL_WIDTH) && utf8_is_printable(data, length))) {
376                         fprintf(f, "    %s%.*s=", on, fieldlen, (const char*)data);
377                         print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
378                         fputs(off, f);
379                 } else {
380                         char bytes[FORMAT_BYTES_MAX];
381
382                         fprintf(f, "    %s%.*s=[%s blob data]%s\n",
383                                 on,
384                                 (int) (c - (const char*) data),
385                                 (const char*) data,
386                                 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1),
387                                 off);
388                 }
389         }
390
391         if (r < 0)
392                 return r;
393
394         if (flags & OUTPUT_CATALOG)
395                 print_catalog(f, j);
396
397         return 0;
398 }
399
400 static int output_export(
401                 FILE *f,
402                 sd_journal *j,
403                 OutputMode mode,
404                 unsigned n_columns,
405                 OutputFlags flags) {
406
407         sd_id128_t boot_id;
408         char sid[33];
409         int r;
410         usec_t realtime, monotonic;
411         _cleanup_free_ char *cursor = NULL;
412         const void *data;
413         size_t length;
414
415         assert(j);
416
417         sd_journal_set_data_threshold(j, 0);
418
419         r = sd_journal_get_realtime_usec(j, &realtime);
420         if (r < 0) {
421                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
422                 return r;
423         }
424
425         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
426         if (r < 0) {
427                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
428                 return r;
429         }
430
431         r = sd_journal_get_cursor(j, &cursor);
432         if (r < 0) {
433                 log_error("Failed to get cursor: %s", strerror(-r));
434                 return r;
435         }
436
437         fprintf(f,
438                 "__CURSOR=%s\n"
439                 "__REALTIME_TIMESTAMP=%llu\n"
440                 "__MONOTONIC_TIMESTAMP=%llu\n"
441                 "_BOOT_ID=%s\n",
442                 cursor,
443                 (unsigned long long) realtime,
444                 (unsigned long long) monotonic,
445                 sd_id128_to_string(boot_id, sid));
446
447         JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
448
449                 /* We already printed the boot id, from the data in
450                  * the header, hence let's suppress it here */
451                 if (length >= 9 &&
452                     hasprefix(data, "_BOOT_ID="))
453                         continue;
454
455                 if (!utf8_is_printable(data, length)) {
456                         const char *c;
457                         uint64_t le64;
458
459                         c = memchr(data, '=', length);
460                         if (!c) {
461                                 log_error("Invalid field.");
462                                 return -EINVAL;
463                         }
464
465                         fwrite(data, c - (const char*) data, 1, f);
466                         fputc('\n', f);
467                         le64 = htole64(length - (c - (const char*) data) - 1);
468                         fwrite(&le64, sizeof(le64), 1, f);
469                         fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
470                 } else
471                         fwrite(data, length, 1, f);
472
473                 fputc('\n', f);
474         }
475
476         if (r < 0)
477                 return r;
478
479         fputc('\n', f);
480
481         return 0;
482 }
483
484 void json_escape(
485                 FILE *f,
486                 const char* p,
487                 size_t l,
488                 OutputFlags flags) {
489
490         assert(f);
491         assert(p);
492
493         if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
494
495                 fputs("null", f);
496
497         else if (!utf8_is_printable(p, l)) {
498                 bool not_first = false;
499
500                 fputs("[ ", f);
501
502                 while (l > 0) {
503                         if (not_first)
504                                 fprintf(f, ", %u", (uint8_t) *p);
505                         else {
506                                 not_first = true;
507                                 fprintf(f, "%u", (uint8_t) *p);
508                         }
509
510                         p++;
511                         l--;
512                 }
513
514                 fputs(" ]", f);
515         } else {
516                 fputc('\"', f);
517
518                 while (l > 0) {
519                         if (*p == '"' || *p == '\\') {
520                                 fputc('\\', f);
521                                 fputc(*p, f);
522                         } else if (*p == '\n')
523                                 fputs("\\n", f);
524                         else if (*p < ' ')
525                                 fprintf(f, "\\u%04x", *p);
526                         else
527                                 fputc(*p, f);
528
529                         p++;
530                         l--;
531                 }
532
533                 fputc('\"', f);
534         }
535 }
536
537 static int output_json(
538                 FILE *f,
539                 sd_journal *j,
540                 OutputMode mode,
541                 unsigned n_columns,
542                 OutputFlags flags) {
543
544         uint64_t realtime, monotonic;
545         _cleanup_free_ char *cursor = NULL;
546         const void *data;
547         size_t length;
548         sd_id128_t boot_id;
549         char sid[33], *k;
550         int r;
551         Hashmap *h = NULL;
552         bool done, separator;
553
554         assert(j);
555
556         sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
557
558         r = sd_journal_get_realtime_usec(j, &realtime);
559         if (r < 0) {
560                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
561                 return r;
562         }
563
564         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
565         if (r < 0) {
566                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
567                 return r;
568         }
569
570         r = sd_journal_get_cursor(j, &cursor);
571         if (r < 0) {
572                 log_error("Failed to get cursor: %s", strerror(-r));
573                 return r;
574         }
575
576         if (mode == OUTPUT_JSON_PRETTY)
577                 fprintf(f,
578                         "{\n"
579                         "\t\"__CURSOR\" : \"%s\",\n"
580                         "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
581                         "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
582                         "\t\"_BOOT_ID\" : \"%s\"",
583                         cursor,
584                         (unsigned long long) realtime,
585                         (unsigned long long) monotonic,
586                         sd_id128_to_string(boot_id, sid));
587         else {
588                 if (mode == OUTPUT_JSON_SSE)
589                         fputs("data: ", f);
590
591                 fprintf(f,
592                         "{ \"__CURSOR\" : \"%s\", "
593                         "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
594                         "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
595                         "\"_BOOT_ID\" : \"%s\"",
596                         cursor,
597                         (unsigned long long) realtime,
598                         (unsigned long long) monotonic,
599                         sd_id128_to_string(boot_id, sid));
600         }
601
602         h = hashmap_new(string_hash_func, string_compare_func);
603         if (!h)
604                 return -ENOMEM;
605
606         /* First round, iterate through the entry and count how often each field appears */
607         JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
608                 const char *eq;
609                 char *n;
610                 unsigned u;
611
612                 if (length >= 9 &&
613                     memcmp(data, "_BOOT_ID=", 9) == 0)
614                         continue;
615
616                 eq = memchr(data, '=', length);
617                 if (!eq)
618                         continue;
619
620                 n = strndup(data, eq - (const char*) data);
621                 if (!n) {
622                         r = -ENOMEM;
623                         goto finish;
624                 }
625
626                 u = PTR_TO_UINT(hashmap_get(h, n));
627                 if (u == 0) {
628                         r = hashmap_put(h, n, UINT_TO_PTR(1));
629                         if (r < 0) {
630                                 free(n);
631                                 goto finish;
632                         }
633                 } else {
634                         r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
635                         free(n);
636                         if (r < 0)
637                                 goto finish;
638                 }
639         }
640
641         if (r < 0)
642                 return r;
643
644         separator = true;
645         do {
646                 done = true;
647
648                 SD_JOURNAL_FOREACH_DATA(j, data, length) {
649                         const char *eq;
650                         char *kk, *n;
651                         size_t m;
652                         unsigned u;
653
654                         /* We already printed the boot id, from the data in
655                          * the header, hence let's suppress it here */
656                         if (length >= 9 &&
657                             memcmp(data, "_BOOT_ID=", 9) == 0)
658                                 continue;
659
660                         eq = memchr(data, '=', length);
661                         if (!eq)
662                                 continue;
663
664                         if (separator) {
665                                 if (mode == OUTPUT_JSON_PRETTY)
666                                         fputs(",\n\t", f);
667                                 else
668                                         fputs(", ", f);
669                         }
670
671                         m = eq - (const char*) data;
672
673                         n = strndup(data, m);
674                         if (!n) {
675                                 r = -ENOMEM;
676                                 goto finish;
677                         }
678
679                         u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
680                         if (u == 0) {
681                                 /* We already printed this, let's jump to the next */
682                                 free(n);
683                                 separator = false;
684
685                                 continue;
686                         } else if (u == 1) {
687                                 /* Field only appears once, output it directly */
688
689                                 json_escape(f, data, m, flags);
690                                 fputs(" : ", f);
691
692                                 json_escape(f, eq + 1, length - m - 1, flags);
693
694                                 hashmap_remove(h, n);
695                                 free(kk);
696                                 free(n);
697
698                                 separator = true;
699
700                                 continue;
701
702                         } else {
703                                 /* Field appears multiple times, output it as array */
704                                 json_escape(f, data, m, flags);
705                                 fputs(" : [ ", f);
706                                 json_escape(f, eq + 1, length - m - 1, flags);
707
708                                 /* Iterate through the end of the list */
709
710                                 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
711                                         if (length < m + 1)
712                                                 continue;
713
714                                         if (memcmp(data, n, m) != 0)
715                                                 continue;
716
717                                         if (((const char*) data)[m] != '=')
718                                                 continue;
719
720                                         fputs(", ", f);
721                                         json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
722                                 }
723
724                                 fputs(" ]", f);
725
726                                 hashmap_remove(h, n);
727                                 free(kk);
728                                 free(n);
729
730                                 /* Iterate data fields form the beginning */
731                                 done = false;
732                                 separator = true;
733
734                                 break;
735                         }
736                 }
737
738         } while (!done);
739
740         if (mode == OUTPUT_JSON_PRETTY)
741                 fputs("\n}\n", f);
742         else if (mode == OUTPUT_JSON_SSE)
743                 fputs("}\n\n", f);
744         else
745                 fputs(" }\n", f);
746
747         r = 0;
748
749 finish:
750         while ((k = hashmap_steal_first_key(h)))
751                 free(k);
752
753         hashmap_free(h);
754
755         return r;
756 }
757
758 static int output_cat(
759                 FILE *f,
760                 sd_journal *j,
761                 OutputMode mode,
762                 unsigned n_columns,
763                 OutputFlags flags) {
764
765         const void *data;
766         size_t l;
767         int r;
768
769         assert(j);
770         assert(f);
771
772         sd_journal_set_data_threshold(j, 0);
773
774         r = sd_journal_get_data(j, "MESSAGE", &data, &l);
775         if (r < 0) {
776                 /* An entry without MESSAGE=? */
777                 if (r == -ENOENT)
778                         return 0;
779
780                 log_error("Failed to get data: %s", strerror(-r));
781                 return r;
782         }
783
784         assert(l >= 8);
785
786         fwrite((const char*) data + 8, 1, l - 8, f);
787         fputc('\n', f);
788
789         return 0;
790 }
791
792 static int (*output_funcs[_OUTPUT_MODE_MAX])(
793                 FILE *f,
794                 sd_journal*j,
795                 OutputMode mode,
796                 unsigned n_columns,
797                 OutputFlags flags) = {
798
799         [OUTPUT_SHORT] = output_short,
800         [OUTPUT_SHORT_MONOTONIC] = output_short,
801         [OUTPUT_VERBOSE] = output_verbose,
802         [OUTPUT_EXPORT] = output_export,
803         [OUTPUT_JSON] = output_json,
804         [OUTPUT_JSON_PRETTY] = output_json,
805         [OUTPUT_JSON_SSE] = output_json,
806         [OUTPUT_CAT] = output_cat
807 };
808
809 int output_journal(
810                 FILE *f,
811                 sd_journal *j,
812                 OutputMode mode,
813                 unsigned n_columns,
814                 OutputFlags flags) {
815
816         int ret;
817         assert(mode >= 0);
818         assert(mode < _OUTPUT_MODE_MAX);
819
820         if (n_columns <= 0)
821                 n_columns = columns();
822
823         ret = output_funcs[mode](f, j, mode, n_columns, flags);
824         fflush(stdout);
825         return ret;
826 }
827
828 static int show_journal(FILE *f,
829                         sd_journal *j,
830                         OutputMode mode,
831                         unsigned n_columns,
832                         usec_t not_before,
833                         unsigned how_many,
834                         OutputFlags flags) {
835
836         int r;
837         unsigned line = 0;
838         bool need_seek = false;
839         int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
840
841         assert(j);
842         assert(mode >= 0);
843         assert(mode < _OUTPUT_MODE_MAX);
844
845         /* Seek to end */
846         r = sd_journal_seek_tail(j);
847         if (r < 0)
848                 goto finish;
849
850         r = sd_journal_previous_skip(j, how_many);
851         if (r < 0)
852                 goto finish;
853
854         for (;;) {
855                 for (;;) {
856                         usec_t usec;
857
858                         if (need_seek) {
859                                 r = sd_journal_next(j);
860                                 if (r < 0)
861                                         goto finish;
862                         }
863
864                         if (r == 0)
865                                 break;
866
867                         need_seek = true;
868
869                         if (not_before > 0) {
870                                 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
871
872                                 /* -ESTALE is returned if the
873                                    timestamp is not from this boot */
874                                 if (r == -ESTALE)
875                                         continue;
876                                 else if (r < 0)
877                                         goto finish;
878
879                                 if (usec < not_before)
880                                         continue;
881                         }
882
883                         line ++;
884
885                         r = output_journal(f, j, mode, n_columns, flags);
886                         if (r < 0)
887                                 goto finish;
888                 }
889
890                 if (warn_cutoff && line < how_many && not_before > 0) {
891                         sd_id128_t boot_id;
892                         usec_t cutoff;
893
894                         /* Check whether the cutoff line is too early */
895
896                         r = sd_id128_get_boot(&boot_id);
897                         if (r < 0)
898                                 goto finish;
899
900                         r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
901                         if (r < 0)
902                                 goto finish;
903
904                         if (r > 0 && not_before < cutoff)
905                                 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
906
907                         warn_cutoff = false;
908                 }
909
910                 if (!(flags & OUTPUT_FOLLOW))
911                         break;
912
913                 r = sd_journal_wait(j, (usec_t) -1);
914                 if (r < 0)
915                         goto finish;
916
917         }
918
919 finish:
920         return r;
921 }
922
923 int add_matches_for_unit(sd_journal *j, const char *unit) {
924         int r;
925         char *m1, *m2, *m3, *m4;
926
927         assert(j);
928         assert(unit);
929
930         m1 = strappenda("_SYSTEMD_UNIT=", unit);
931         m2 = strappenda("COREDUMP_UNIT=", unit);
932         m3 = strappenda("UNIT=", unit);
933         m4 = strappenda("OBJECT_SYSTEMD_UNIT=", unit);
934
935         (void)(
936             /* Look for messages from the service itself */
937             (r = sd_journal_add_match(j, m1, 0)) ||
938
939             /* Look for coredumps of the service */
940             (r = sd_journal_add_disjunction(j)) ||
941             (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
942             (r = sd_journal_add_match(j, "_UID=0", 0)) ||
943             (r = sd_journal_add_match(j, m2, 0)) ||
944
945              /* Look for messages from PID 1 about this service */
946             (r = sd_journal_add_disjunction(j)) ||
947             (r = sd_journal_add_match(j, "_PID=1", 0)) ||
948             (r = sd_journal_add_match(j, m3, 0)) ||
949
950             /* Look for messages from authorized daemons about this service */
951             (r = sd_journal_add_disjunction(j)) ||
952             (r = sd_journal_add_match(j, "_UID=0", 0)) ||
953             (r = sd_journal_add_match(j, m4, 0))
954         );
955
956         return r;
957 }
958
959 int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
960         int r;
961         char *m1, *m2, *m3, *m4;
962         char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
963
964         assert(j);
965         assert(unit);
966
967         m1 = strappenda("_SYSTEMD_USER_UNIT=", unit);
968         m2 = strappenda("USER_UNIT=", unit);
969         m3 = strappenda("COREDUMP_USER_UNIT=", unit);
970         m4 = strappenda("OBJECT_SYSTEMD_USER_UNIT=", unit);
971         sprintf(muid, "_UID=%lu", (unsigned long) uid);
972
973         (void) (
974                 /* Look for messages from the user service itself */
975                 (r = sd_journal_add_match(j, m1, 0)) ||
976                 (r = sd_journal_add_match(j, muid, 0)) ||
977
978                 /* Look for messages from systemd about this service */
979                 (r = sd_journal_add_disjunction(j)) ||
980                 (r = sd_journal_add_match(j, m2, 0)) ||
981                 (r = sd_journal_add_match(j, muid, 0)) ||
982
983                 /* Look for coredumps of the service */
984                 (r = sd_journal_add_disjunction(j)) ||
985                 (r = sd_journal_add_match(j, m3, 0)) ||
986                 (r = sd_journal_add_match(j, muid, 0)) ||
987                 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
988
989                 /* Look for messages from authorized daemons about this service */
990                 (r = sd_journal_add_disjunction(j)) ||
991                 (r = sd_journal_add_match(j, m4, 0)) ||
992                 (r = sd_journal_add_match(j, muid, 0)) ||
993                 (r = sd_journal_add_match(j, "_UID=0", 0))
994         );
995         return r;
996 }
997
998 int add_match_this_boot(sd_journal *j) {
999         char match[9+32+1] = "_BOOT_ID=";
1000         sd_id128_t boot_id;
1001         int r;
1002
1003         assert(j);
1004
1005         r = sd_id128_get_boot(&boot_id);
1006         if (r < 0) {
1007                 log_error("Failed to get boot id: %s", strerror(-r));
1008                 return r;
1009         }
1010
1011         sd_id128_to_string(boot_id, match + 9);
1012         r = sd_journal_add_match(j, match, strlen(match));
1013         if (r < 0) {
1014                 log_error("Failed to add match: %s", strerror(-r));
1015                 return r;
1016         }
1017
1018         r = sd_journal_add_conjunction(j);
1019         if (r < 0)
1020                 return r;
1021
1022         return 0;
1023 }
1024
1025 int show_journal_by_unit(
1026                 FILE *f,
1027                 const char *unit,
1028                 OutputMode mode,
1029                 unsigned n_columns,
1030                 usec_t not_before,
1031                 unsigned how_many,
1032                 uid_t uid,
1033                 OutputFlags flags,
1034                 bool system) {
1035
1036         _cleanup_journal_close_ sd_journal*j = NULL;
1037         int r;
1038         int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM;
1039
1040         assert(mode >= 0);
1041         assert(mode < _OUTPUT_MODE_MAX);
1042         assert(unit);
1043
1044         if (how_many <= 0)
1045                 return 0;
1046
1047         r = sd_journal_open(&j, jflags);
1048         if (r < 0)
1049                 return r;
1050
1051         r = add_match_this_boot(j);
1052         if (r < 0)
1053                 return r;
1054
1055         if (system)
1056                 r = add_matches_for_unit(j, unit);
1057         else
1058                 r = add_matches_for_user_unit(j, unit, uid);
1059         if (r < 0)
1060                 return r;
1061
1062         log_debug("Journal filter: %s", journal_make_match_string(j));
1063
1064         r = show_journal(f, j, mode, n_columns, not_before, how_many, flags);
1065         if (r < 0)
1066                 return r;
1067
1068         return 0;
1069 }
1070
1071 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1072         [OUTPUT_SHORT] = "short",
1073         [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
1074         [OUTPUT_VERBOSE] = "verbose",
1075         [OUTPUT_EXPORT] = "export",
1076         [OUTPUT_JSON] = "json",
1077         [OUTPUT_JSON_PRETTY] = "json-pretty",
1078         [OUTPUT_JSON_SSE] = "json-sse",
1079         [OUTPUT_CAT] = "cat"
1080 };
1081
1082 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);