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