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