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