chiark / gitweb /
journalctl: strip TABs and ANSI color sequences from log messages when displaying...
[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 *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
289                 free(e);
290         } else
291                 fputs("\n", f);
292
293         if (flags & OUTPUT_CATALOG)
294                 print_catalog(f, j);
295
296         return 0;
297 }
298
299 static int output_verbose(
300                 FILE *f,
301                 sd_journal *j,
302                 OutputMode mode,
303                 unsigned n_columns,
304                 OutputFlags flags) {
305
306         const void *data;
307         size_t length;
308         char *cursor;
309         uint64_t realtime;
310         char ts[FORMAT_TIMESTAMP_MAX];
311         int r;
312
313         assert(f);
314         assert(j);
315
316         sd_journal_set_data_threshold(j, 0);
317
318         r = sd_journal_get_realtime_usec(j, &realtime);
319         if (r < 0) {
320                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
321                 return r;
322         }
323
324         r = sd_journal_get_cursor(j, &cursor);
325         if (r < 0) {
326                 log_error("Failed to get cursor: %s", strerror(-r));
327                 return r;
328         }
329
330         fprintf(f, "%s [%s]\n",
331                 format_timestamp(ts, sizeof(ts), realtime),
332                 cursor);
333
334         free(cursor);
335
336         SD_JOURNAL_FOREACH_DATA(j, data, length) {
337                 if (!shall_print(data, length, flags)) {
338                         const char *c;
339                         char bytes[FORMAT_BYTES_MAX];
340
341                         c = memchr(data, '=', length);
342                         if (!c) {
343                                 log_error("Invalid field.");
344                                 return -EINVAL;
345                         }
346
347                         fprintf(f, "\t%.*s=[%s blob data]\n",
348                                (int) (c - (const char*) data),
349                                (const char*) data,
350                                format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
351                 } else
352                         fprintf(f, "\t%.*s\n", (int) length, (const char*) data);
353         }
354
355         if (flags & OUTPUT_CATALOG)
356                 print_catalog(f, j);
357
358         return 0;
359 }
360
361 static int output_export(
362                 FILE *f,
363                 sd_journal *j,
364                 OutputMode mode,
365                 unsigned n_columns,
366                 OutputFlags flags) {
367
368         sd_id128_t boot_id;
369         char sid[33];
370         int r;
371         usec_t realtime, monotonic;
372         char *cursor;
373         const void *data;
374         size_t length;
375
376         assert(j);
377
378         sd_journal_set_data_threshold(j, 0);
379
380         r = sd_journal_get_realtime_usec(j, &realtime);
381         if (r < 0) {
382                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
383                 return r;
384         }
385
386         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
387         if (r < 0) {
388                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
389                 return r;
390         }
391
392         r = sd_journal_get_cursor(j, &cursor);
393         if (r < 0) {
394                 log_error("Failed to get cursor: %s", strerror(-r));
395                 return r;
396         }
397
398         fprintf(f,
399                 "__CURSOR=%s\n"
400                 "__REALTIME_TIMESTAMP=%llu\n"
401                 "__MONOTONIC_TIMESTAMP=%llu\n"
402                 "_BOOT_ID=%s\n",
403                 cursor,
404                 (unsigned long long) realtime,
405                 (unsigned long long) monotonic,
406                 sd_id128_to_string(boot_id, sid));
407
408         free(cursor);
409
410         SD_JOURNAL_FOREACH_DATA(j, data, length) {
411
412                 /* We already printed the boot id, from the data in
413                  * the header, hence let's suppress it here */
414                 if (length >= 9 &&
415                     memcmp(data, "_BOOT_ID=", 9) == 0)
416                         continue;
417
418                 if (!utf8_is_printable_n(data, length)) {
419                         const char *c;
420                         uint64_t le64;
421
422                         c = memchr(data, '=', length);
423                         if (!c) {
424                                 log_error("Invalid field.");
425                                 return -EINVAL;
426                         }
427
428                         fwrite(data, c - (const char*) data, 1, f);
429                         fputc('\n', f);
430                         le64 = htole64(length - (c - (const char*) data) - 1);
431                         fwrite(&le64, sizeof(le64), 1, f);
432                         fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
433                 } else
434                         fwrite(data, length, 1, f);
435
436                 fputc('\n', f);
437         }
438
439         fputc('\n', f);
440
441         return 0;
442 }
443
444 void json_escape(
445                 FILE *f,
446                 const char* p,
447                 size_t l,
448                 OutputFlags flags) {
449
450         assert(f);
451         assert(p);
452
453         if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
454
455                 fputs("null", f);
456
457         else if (!utf8_is_printable_n(p, l)) {
458                 bool not_first = false;
459
460                 fputs("[ ", f);
461
462                 while (l > 0) {
463                         if (not_first)
464                                 fprintf(f, ", %u", (uint8_t) *p);
465                         else {
466                                 not_first = true;
467                                 fprintf(f, "%u", (uint8_t) *p);
468                         }
469
470                         p++;
471                         l--;
472                 }
473
474                 fputs(" ]", f);
475         } else {
476                 fputc('\"', f);
477
478                 while (l > 0) {
479                         if (*p == '"' || *p == '\\') {
480                                 fputc('\\', f);
481                                 fputc(*p, f);
482                         } else if (*p < ' ')
483                                 fprintf(f, "\\u%04x", *p);
484                         else
485                                 fputc(*p, f);
486
487                         p++;
488                         l--;
489                 }
490
491                 fputc('\"', f);
492         }
493 }
494
495 static int output_json(
496                 FILE *f,
497                 sd_journal *j,
498                 OutputMode mode,
499                 unsigned n_columns,
500                 OutputFlags flags) {
501
502         uint64_t realtime, monotonic;
503         char *cursor, *k;
504         const void *data;
505         size_t length;
506         sd_id128_t boot_id;
507         char sid[33];
508         int r;
509         Hashmap *h = NULL;
510         bool done, separator;
511
512         assert(j);
513
514         sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
515
516         r = sd_journal_get_realtime_usec(j, &realtime);
517         if (r < 0) {
518                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
519                 return r;
520         }
521
522         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
523         if (r < 0) {
524                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
525                 return r;
526         }
527
528         r = sd_journal_get_cursor(j, &cursor);
529         if (r < 0) {
530                 log_error("Failed to get cursor: %s", strerror(-r));
531                 return r;
532         }
533
534         if (mode == OUTPUT_JSON_PRETTY)
535                 fprintf(f,
536                         "{\n"
537                         "\t\"__CURSOR\" : \"%s\",\n"
538                         "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
539                         "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
540                         "\t\"_BOOT_ID\" : \"%s\"",
541                         cursor,
542                         (unsigned long long) realtime,
543                         (unsigned long long) monotonic,
544                         sd_id128_to_string(boot_id, sid));
545         else {
546                 if (mode == OUTPUT_JSON_SSE)
547                         fputs("data: ", f);
548
549                 fprintf(f,
550                         "{ \"__CURSOR\" : \"%s\", "
551                         "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
552                         "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
553                         "\"_BOOT_ID\" : \"%s\"",
554                         cursor,
555                         (unsigned long long) realtime,
556                         (unsigned long long) monotonic,
557                         sd_id128_to_string(boot_id, sid));
558         }
559         free(cursor);
560
561         h = hashmap_new(string_hash_func, string_compare_func);
562         if (!h)
563                 return -ENOMEM;
564
565         /* First round, iterate through the entry and count how often each field appears */
566         SD_JOURNAL_FOREACH_DATA(j, data, length) {
567                 const char *eq;
568                 char *n;
569                 unsigned u;
570
571                 if (length >= 9 &&
572                     memcmp(data, "_BOOT_ID=", 9) == 0)
573                         continue;
574
575                 eq = memchr(data, '=', length);
576                 if (!eq)
577                         continue;
578
579                 n = strndup(data, eq - (const char*) data);
580                 if (!n) {
581                         r = -ENOMEM;
582                         goto finish;
583                 }
584
585                 u = PTR_TO_UINT(hashmap_get(h, n));
586                 if (u == 0) {
587                         r = hashmap_put(h, n, UINT_TO_PTR(1));
588                         if (r < 0) {
589                                 free(n);
590                                 goto finish;
591                         }
592                 } else {
593                         r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
594                         free(n);
595                         if (r < 0)
596                                 goto finish;
597                 }
598         }
599
600         separator = true;
601         do {
602                 done = true;
603
604                 SD_JOURNAL_FOREACH_DATA(j, data, length) {
605                         const char *eq;
606                         char *kk, *n;
607                         size_t m;
608                         unsigned u;
609
610                         /* We already printed the boot id, from the data in
611                          * the header, hence let's suppress it here */
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                         if (separator) {
621                                 if (mode == OUTPUT_JSON_PRETTY)
622                                         fputs(",\n\t", f);
623                                 else
624                                         fputs(", ", f);
625                         }
626
627                         m = eq - (const char*) data;
628
629                         n = strndup(data, m);
630                         if (!n) {
631                                 r = -ENOMEM;
632                                 goto finish;
633                         }
634
635                         u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
636                         if (u == 0) {
637                                 /* We already printed this, let's jump to the next */
638                                 free(n);
639                                 separator = false;
640
641                                 continue;
642                         } else if (u == 1) {
643                                 /* Field only appears once, output it directly */
644
645                                 json_escape(f, data, m, flags);
646                                 fputs(" : ", f);
647
648                                 json_escape(f, eq + 1, length - m - 1, flags);
649
650                                 hashmap_remove(h, n);
651                                 free(kk);
652                                 free(n);
653
654                                 separator = true;
655
656                                 continue;
657
658                         } else {
659                                 /* Field appears multiple times, output it as array */
660                                 json_escape(f, data, m, flags);
661                                 fputs(" : [ ", f);
662                                 json_escape(f, eq + 1, length - m - 1, flags);
663
664                                 /* Iterate through the end of the list */
665
666                                 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
667                                         if (length < m + 1)
668                                                 continue;
669
670                                         if (memcmp(data, n, m) != 0)
671                                                 continue;
672
673                                         if (((const char*) data)[m] != '=')
674                                                 continue;
675
676                                         fputs(", ", f);
677                                         json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
678                                 }
679
680                                 fputs(" ]", f);
681
682                                 hashmap_remove(h, n);
683                                 free(kk);
684                                 free(n);
685
686                                 /* Iterate data fields form the beginning */
687                                 done = false;
688                                 separator = true;
689
690                                 break;
691                         }
692                 }
693
694         } while (!done);
695
696         if (mode == OUTPUT_JSON_PRETTY)
697                 fputs("\n}\n", f);
698         else if (mode == OUTPUT_JSON_SSE)
699                 fputs("}\n\n", f);
700         else
701                 fputs(" }\n", f);
702
703         r = 0;
704
705 finish:
706         while ((k = hashmap_steal_first_key(h)))
707                 free(k);
708
709         hashmap_free(h);
710
711         return r;
712 }
713
714 static int output_cat(
715                 FILE *f,
716                 sd_journal *j,
717                 OutputMode mode,
718                 unsigned n_columns,
719                 OutputFlags flags) {
720
721         const void *data;
722         size_t l;
723         int r;
724
725         assert(j);
726         assert(f);
727
728         sd_journal_set_data_threshold(j, 0);
729
730         r = sd_journal_get_data(j, "MESSAGE", &data, &l);
731         if (r < 0) {
732                 /* An entry without MESSAGE=? */
733                 if (r == -ENOENT)
734                         return 0;
735
736                 log_error("Failed to get data: %s", strerror(-r));
737                 return r;
738         }
739
740         assert(l >= 8);
741
742         fwrite((const char*) data + 8, 1, l - 8, f);
743         fputc('\n', f);
744
745         return 0;
746 }
747
748 static int (*output_funcs[_OUTPUT_MODE_MAX])(
749                 FILE *f,
750                 sd_journal*j,
751                 OutputMode mode,
752                 unsigned n_columns,
753                 OutputFlags flags) = {
754
755         [OUTPUT_SHORT] = output_short,
756         [OUTPUT_SHORT_MONOTONIC] = output_short,
757         [OUTPUT_VERBOSE] = output_verbose,
758         [OUTPUT_EXPORT] = output_export,
759         [OUTPUT_JSON] = output_json,
760         [OUTPUT_JSON_PRETTY] = output_json,
761         [OUTPUT_JSON_SSE] = output_json,
762         [OUTPUT_CAT] = output_cat
763 };
764
765 int output_journal(
766                 FILE *f,
767                 sd_journal *j,
768                 OutputMode mode,
769                 unsigned n_columns,
770                 OutputFlags flags) {
771
772         int ret;
773         assert(mode >= 0);
774         assert(mode < _OUTPUT_MODE_MAX);
775
776         if (n_columns <= 0)
777                 n_columns = columns();
778
779         ret = output_funcs[mode](f, j, mode, n_columns, flags);
780         fflush(stdout);
781         return ret;
782 }
783
784 int show_journal_by_unit(
785                 FILE *f,
786                 const char *unit,
787                 OutputMode mode,
788                 unsigned n_columns,
789                 usec_t not_before,
790                 unsigned how_many,
791                 OutputFlags flags) {
792
793         _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
794         sd_journal *j = NULL;
795         int r;
796         unsigned line = 0;
797         bool need_seek = false;
798         int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
799
800         assert(mode >= 0);
801         assert(mode < _OUTPUT_MODE_MAX);
802         assert(unit);
803
804         if (!endswith(unit, ".service") &&
805             !endswith(unit, ".socket") &&
806             !endswith(unit, ".mount") &&
807             !endswith(unit, ".swap"))
808                 return 0;
809
810         if (how_many <= 0)
811                 return 0;
812
813         if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
814             asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
815             asprintf(&m3, "UNIT=%s", unit) < 0) {
816                 r = -ENOMEM;
817                 goto finish;
818         }
819
820         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
821         if (r < 0)
822                 goto finish;
823
824         /* Look for messages from the service itself */
825         r = sd_journal_add_match(j, m1, 0);
826         if (r < 0)
827                 goto finish;
828
829         /* Look for coredumps of the service */
830         r = sd_journal_add_disjunction(j);
831         if (r < 0)
832                 goto finish;
833         r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0);
834         if (r < 0)
835                 goto finish;
836         r = sd_journal_add_match(j, m2, 0);
837         if (r < 0)
838                 goto finish;
839
840         /* Look for messages from PID 1 about this service */
841         r = sd_journal_add_disjunction(j);
842         if (r < 0)
843                 goto finish;
844         r = sd_journal_add_match(j, "_PID=1", 0);
845         if (r < 0)
846                 goto finish;
847         r = sd_journal_add_match(j, m3, 0);
848         if (r < 0)
849                 goto finish;
850
851         /* Seek to end */
852         r = sd_journal_seek_tail(j);
853         if (r < 0)
854                 goto finish;
855
856         r = sd_journal_previous_skip(j, how_many);
857         if (r < 0)
858                 goto finish;
859
860         for (;;) {
861                 for (;;) {
862                         usec_t usec;
863
864                         if (need_seek) {
865                                 r = sd_journal_next(j);
866                                 if (r < 0)
867                                         goto finish;
868                         }
869
870                         if (r == 0)
871                                 break;
872
873                         need_seek = true;
874
875                         if (not_before > 0) {
876                                 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
877
878                                 /* -ESTALE is returned if the
879                                    timestamp is not from this boot */
880                                 if (r == -ESTALE)
881                                         continue;
882                                 else if (r < 0)
883                                         goto finish;
884
885                                 if (usec < not_before)
886                                         continue;
887                         }
888
889                         line ++;
890
891                         r = output_journal(f, j, mode, n_columns, flags);
892                         if (r < 0)
893                                 goto finish;
894                 }
895
896                 if (warn_cutoff && line < how_many && not_before > 0) {
897                         sd_id128_t boot_id;
898                         usec_t cutoff;
899
900                         /* Check whether the cutoff line is too early */
901
902                         r = sd_id128_get_boot(&boot_id);
903                         if (r < 0)
904                                 goto finish;
905
906                         r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
907                         if (r < 0)
908                                 goto finish;
909
910                         if (r > 0 && not_before < cutoff)
911                                 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
912
913                         warn_cutoff = false;
914                 }
915
916                 if (!(flags & OUTPUT_FOLLOW))
917                         break;
918
919                 r = sd_journal_wait(j, (usec_t) -1);
920                 if (r < 0)
921                         goto finish;
922
923         }
924
925 finish:
926         if (j)
927                 sd_journal_close(j);
928
929         return r;
930 }
931
932 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
933         [OUTPUT_SHORT] = "short",
934         [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
935         [OUTPUT_VERBOSE] = "verbose",
936         [OUTPUT_EXPORT] = "export",
937         [OUTPUT_JSON] = "json",
938         [OUTPUT_JSON_PRETTY] = "json-pretty",
939         [OUTPUT_JSON_SSE] = "json-sse",
940         [OUTPUT_CAT] = "cat"
941 };
942
943 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);