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