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