1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <signal.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <sys/socket.h>
9 #include <syslog.h>
10 #include <unistd.h>
11 
12 #include "sd-id128.h"
13 #include "sd-journal.h"
14 
15 #include "alloc-util.h"
16 #include "fd-util.h"
17 #include "format-util.h"
18 #include "glyph-util.h"
19 #include "hashmap.h"
20 #include "hostname-util.h"
21 #include "id128-util.h"
22 #include "io-util.h"
23 #include "journal-internal.h"
24 #include "journal-util.h"
25 #include "json.h"
26 #include "locale-util.h"
27 #include "log.h"
28 #include "logs-show.h"
29 #include "macro.h"
30 #include "namespace-util.h"
31 #include "output-mode.h"
32 #include "parse-util.h"
33 #include "pretty-print.h"
34 #include "process-util.h"
35 #include "sparse-endian.h"
36 #include "stdio-util.h"
37 #include "string-table.h"
38 #include "string-util.h"
39 #include "strv.h"
40 #include "terminal-util.h"
41 #include "time-util.h"
42 #include "utf8.h"
43 #include "util.h"
44 #include "web-util.h"
45 
46 /* up to three lines (each up to 100 characters) or 300 characters, whichever is less */
47 #define PRINT_LINE_THRESHOLD 3
48 #define PRINT_CHAR_THRESHOLD 300
49 
50 #define JSON_THRESHOLD 4096U
51 
print_catalog(FILE * f,sd_journal * j)52 static int print_catalog(FILE *f, sd_journal *j) {
53         _cleanup_free_ char *t = NULL, *z = NULL;
54         const char *newline, *prefix;
55         int r;
56 
57         assert(j);
58 
59         r = sd_journal_get_catalog(j, &t);
60         if (r == -ENOENT)
61                 return 0;
62         if (r < 0)
63                 return log_error_errno(r, "Failed to find catalog entry: %m");
64 
65         if (is_locale_utf8())
66                 prefix = strjoina(special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), special_glyph(SPECIAL_GLYPH_LIGHT_SHADE));
67         else
68                 prefix = "--";
69 
70         newline = strjoina(ansi_normal(), "\n", ansi_grey(), prefix, ansi_normal(), " ", ansi_green());
71 
72         z = strreplace(strstrip(t), "\n", newline);
73         if (!z)
74                 return log_oom();
75 
76         fprintf(f, "%s%s %s%s", ansi_grey(), prefix, ansi_normal(), ansi_green());
77 
78         fputs(z, f);
79 
80         fprintf(f, "%s\n", ansi_normal());
81 
82         return 1;
83 }
84 
url_from_catalog(sd_journal * j,char ** ret)85 static int url_from_catalog(sd_journal *j, char **ret) {
86         _cleanup_free_ char *t = NULL, *url = NULL;
87         const char *weblink;
88         int r;
89 
90         assert(j);
91         assert(ret);
92 
93         r = sd_journal_get_catalog(j, &t);
94         if (r == -ENOENT)
95                 goto notfound;
96         if (r < 0)
97                 return log_error_errno(r, "Failed to find catalog entry: %m");
98 
99         weblink = startswith(t, "Documentation:");
100         if (!weblink) {
101                 weblink = strstr(t + 1, "\nDocumentation:");
102                 if (!weblink)
103                         goto notfound;
104 
105                 weblink += 15;
106         }
107 
108         /* Skip whitespace to value */
109         weblink += strspn(weblink, " \t");
110 
111         /* Cut out till next whitespace/newline */
112         url = strndup(weblink, strcspn(weblink, WHITESPACE));
113         if (!url)
114                 return log_oom();
115 
116         if (!documentation_url_is_valid(url))
117                 goto notfound;
118 
119         *ret = TAKE_PTR(url);
120         return 1;
121 
122 notfound:
123         *ret = NULL;
124         return 0;
125 }
126 
parse_field(const void * data,size_t length,const char * field,size_t field_len,char ** target,size_t * target_len)127 static int parse_field(const void *data, size_t length, const char *field, size_t field_len, char **target, size_t *target_len) {
128         size_t nl;
129         char *buf;
130 
131         assert(data);
132         assert(field);
133         assert(target);
134 
135         if (length < field_len)
136                 return 0;
137 
138         if (memcmp(data, field, field_len))
139                 return 0;
140 
141         nl = length - field_len;
142 
143         buf = newdup_suffix0(char, (const char*) data + field_len, nl);
144         if (!buf)
145                 return log_oom();
146 
147         free(*target);
148         *target = buf;
149 
150         if (target_len)
151                 *target_len = nl;
152 
153         return 1;
154 }
155 
156 typedef struct ParseFieldVec {
157         const char *field;
158         size_t field_len;
159         char **target;
160         size_t *target_len;
161 } ParseFieldVec;
162 
163 #define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) {           \
164                 .field = _field,                                        \
165                 .field_len = strlen(_field),                            \
166                 .target = _target,                                      \
167                 .target_len = _target_len                               \
168         }
169 
parse_fieldv(const void * data,size_t length,const ParseFieldVec * fields,unsigned n_fields)170 static int parse_fieldv(const void *data, size_t length, const ParseFieldVec *fields, unsigned n_fields) {
171         unsigned i;
172 
173         for (i = 0; i < n_fields; i++) {
174                 const ParseFieldVec *f = &fields[i];
175                 int r;
176 
177                 r = parse_field(data, length, f->field, f->field_len, f->target, f->target_len);
178                 if (r < 0)
179                         return r;
180                 else if (r > 0)
181                         break;
182         }
183 
184         return 0;
185 }
186 
field_set_test(const Set * fields,const char * name,size_t n)187 static int field_set_test(const Set *fields, const char *name, size_t n) {
188         char *s;
189 
190         if (!fields)
191                 return 1;
192 
193         s = strndupa_safe(name, n);
194         return set_contains(fields, s);
195 }
196 
shall_print(const char * p,size_t l,OutputFlags flags)197 static bool shall_print(const char *p, size_t l, OutputFlags flags) {
198         assert(p);
199 
200         if (flags & OUTPUT_SHOW_ALL)
201                 return true;
202 
203         if (l >= PRINT_CHAR_THRESHOLD)
204                 return false;
205 
206         if (!utf8_is_printable(p, l))
207                 return false;
208 
209         return true;
210 }
211 
print_multiline(FILE * f,unsigned prefix,unsigned n_columns,OutputFlags flags,int priority,bool audit,const char * message,size_t message_len,size_t highlight[2])212 static bool print_multiline(
213                 FILE *f,
214                 unsigned prefix,
215                 unsigned n_columns,
216                 OutputFlags flags,
217                 int priority,
218                 bool audit,
219                 const char* message,
220                 size_t message_len,
221                 size_t highlight[2]) {
222 
223         const char *color_on = "", *color_off = "", *highlight_on = "";
224         const char *pos, *end;
225         bool ellipsized = false;
226         int line = 0;
227 
228         if (flags & OUTPUT_COLOR) {
229                 get_log_colors(priority, &color_on, &color_off, &highlight_on);
230 
231                 if (audit && strempty(color_on)) {
232                         color_on = ANSI_BLUE;
233                         color_off = ANSI_NORMAL;
234                 }
235         }
236 
237         /* A special case: make sure that we print a newline when
238            the message is empty. */
239         if (message_len == 0)
240                 fputs("\n", f);
241 
242         for (pos = message;
243              pos < message + message_len;
244              pos = end + 1, line++) {
245                 bool continuation = line > 0;
246                 bool tail_line;
247                 int len;
248                 for (end = pos; end < message + message_len && *end != '\n'; end++)
249                         ;
250                 len = end - pos;
251                 assert(len >= 0);
252 
253                 /* We need to figure out when we are showing not-last line, *and*
254                  * will skip subsequent lines. In that case, we will put the dots
255                  * at the end of the line, instead of putting dots in the middle
256                  * or not at all.
257                  */
258                 tail_line =
259                         line + 1 == PRINT_LINE_THRESHOLD ||
260                         end + 1 >= message + PRINT_CHAR_THRESHOLD;
261 
262                 if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
263                     (prefix + len + 1 < n_columns && !tail_line)) {
264                         if (highlight &&
265                             (size_t) (pos - message) <= highlight[0] &&
266                             highlight[0] < (size_t) len) {
267 
268                                 fprintf(f, "%*s%s%.*s",
269                                         continuation * prefix, "",
270                                         color_on, (int) highlight[0], pos);
271                                 fprintf(f, "%s%.*s",
272                                         highlight_on,
273                                         (int) (MIN((size_t) len, highlight[1]) - highlight[0]),
274                                         pos + highlight[0]);
275                                 if ((size_t) len > highlight[1])
276                                         fprintf(f, "%s%.*s",
277                                                 color_on,
278                                                 (int) (len - highlight[1]),
279                                                 pos + highlight[1]);
280                                 fprintf(f, "%s\n", color_off);
281 
282                         } else
283                                 fprintf(f, "%*s%s%.*s%s\n",
284                                         continuation * prefix, "",
285                                         color_on, len, pos, color_off);
286                         continue;
287                 }
288 
289                 /* Beyond this point, ellipsization will happen. */
290                 ellipsized = true;
291 
292                 if (prefix < n_columns && n_columns - prefix >= 3) {
293                         if (n_columns - prefix > (unsigned) len + 3)
294                                 fprintf(f, "%*s%s%.*s...%s\n",
295                                         continuation * prefix, "",
296                                         color_on, len, pos, color_off);
297                         else {
298                                 _cleanup_free_ char *e = NULL;
299 
300                                 e = ellipsize_mem(pos, len, n_columns - prefix,
301                                                   tail_line ? 100 : 90);
302                                 if (!e)
303                                         fprintf(f, "%*s%s%.*s%s\n",
304                                                 continuation * prefix, "",
305                                                 color_on, len, pos, color_off);
306                                 else
307                                         fprintf(f, "%*s%s%s%s\n",
308                                                 continuation * prefix, "",
309                                                 color_on, e, color_off);
310                         }
311                 } else
312                         fputs("...\n", f);
313 
314                 if (tail_line)
315                         break;
316         }
317 
318         return ellipsized;
319 }
320 
output_timestamp_monotonic(FILE * f,sd_journal * j,const char * monotonic)321 static int output_timestamp_monotonic(FILE *f, sd_journal *j, const char *monotonic) {
322         sd_id128_t boot_id;
323         uint64_t t;
324         int r;
325 
326         assert(f);
327         assert(j);
328 
329         r = -ENXIO;
330         if (monotonic)
331                 r = safe_atou64(monotonic, &t);
332         if (r < 0)
333                 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
334         if (r < 0)
335                 return log_error_errno(r, "Failed to get monotonic timestamp: %m");
336 
337         fprintf(f, "[%5"PRI_USEC".%06"PRI_USEC"]", t / USEC_PER_SEC, t % USEC_PER_SEC);
338         return 1 + 5 + 1 + 6 + 1;
339 }
340 
output_timestamp_realtime(FILE * f,sd_journal * j,OutputMode mode,OutputFlags flags,const char * realtime)341 static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, OutputFlags flags, const char *realtime) {
342         char buf[MAX(FORMAT_TIMESTAMP_MAX, 64U)];
343         uint64_t x;
344         int r;
345 
346         assert(f);
347         assert(j);
348 
349         if (realtime)
350                 r = safe_atou64(realtime, &x);
351         if (!realtime || r < 0 || !VALID_REALTIME(x))
352                 r = sd_journal_get_realtime_usec(j, &x);
353         if (r < 0)
354                 return log_error_errno(r, "Failed to get realtime timestamp: %m");
355 
356         if (IN_SET(mode, OUTPUT_SHORT_FULL, OUTPUT_WITH_UNIT)) {
357                 const char *k;
358 
359                 if (flags & OUTPUT_UTC)
360                         k = format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_UTC);
361                 else
362                         k = format_timestamp(buf, sizeof(buf), x);
363                 if (!k)
364                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
365                                                "Failed to format timestamp: %" PRIu64, x);
366 
367         } else {
368                 struct tm tm;
369                 time_t t;
370 
371                 t = (time_t) (x / USEC_PER_SEC);
372 
373                 switch (mode) {
374 
375                 case OUTPUT_SHORT_UNIX:
376                         xsprintf(buf, "%10"PRI_TIME".%06"PRIu64, t, x % USEC_PER_SEC);
377                         break;
378 
379                 case OUTPUT_SHORT_ISO:
380                         if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z",
381                                      localtime_or_gmtime_r(&t, &tm, flags & OUTPUT_UTC)) <= 0)
382                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
383                                                        "Failed to format ISO time");
384                         break;
385 
386                 case OUTPUT_SHORT_ISO_PRECISE: {
387                         char usec[7];
388 
389                         /* No usec in strftime, so we leave space and copy over */
390                         if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.xxxxxx%z",
391                                      localtime_or_gmtime_r(&t, &tm, flags & OUTPUT_UTC)) <= 0)
392                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
393                                                        "Failed to format ISO-precise time");
394                         xsprintf(usec, "%06"PRI_USEC, x % USEC_PER_SEC);
395                         memcpy(buf + 20, usec, 6);
396                         break;
397                 }
398                 case OUTPUT_SHORT:
399                 case OUTPUT_SHORT_PRECISE:
400 
401                         if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S",
402                                      localtime_or_gmtime_r(&t, &tm, flags & OUTPUT_UTC)) <= 0)
403                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
404                                                        "Failed to format syslog time");
405 
406                         if (mode == OUTPUT_SHORT_PRECISE) {
407                                 size_t k;
408 
409                                 assert(sizeof(buf) > strlen(buf));
410                                 k = sizeof(buf) - strlen(buf);
411 
412                                 r = snprintf(buf + strlen(buf), k, ".%06"PRIu64, x % USEC_PER_SEC);
413                                 if (r <= 0 || (size_t) r >= k) /* too long? */
414                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
415                                                                "Failed to format precise time");
416                         }
417                         break;
418 
419                 default:
420                         assert_not_reached();
421                 }
422         }
423 
424         fputs(buf, f);
425         return (int) strlen(buf);
426 }
427 
output_short(FILE * f,sd_journal * j,OutputMode mode,unsigned n_columns,OutputFlags flags,const Set * output_fields,const size_t highlight[2])428 static int output_short(
429                 FILE *f,
430                 sd_journal *j,
431                 OutputMode mode,
432                 unsigned n_columns,
433                 OutputFlags flags,
434                 const Set *output_fields,
435                 const size_t highlight[2]) {
436 
437         int r;
438         const void *data;
439         size_t length, n = 0;
440         _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL,
441                 *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *transport = NULL,
442                 *config_file = NULL, *unit = NULL, *user_unit = NULL, *documentation_url = NULL;
443         size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0,
444                 realtime_len = 0, monotonic_len = 0, priority_len = 0, transport_len = 0, config_file_len = 0,
445                 unit_len = 0, user_unit_len = 0, documentation_url_len = 0;
446         int p = LOG_INFO;
447         bool ellipsized = false, audit;
448         const ParseFieldVec fields[] = {
449                 PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len),
450                 PARSE_FIELD_VEC_ENTRY("_COMM=", &comm, &comm_len),
451                 PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len),
452                 PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len),
453                 PARSE_FIELD_VEC_ENTRY("_TRANSPORT=", &transport, &transport_len),
454                 PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len),
455                 PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len),
456                 PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len),
457                 PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
458                 PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
459                 PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len),
460                 PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len),
461                 PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len),
462                 PARSE_FIELD_VEC_ENTRY("DOCUMENTATION=", &documentation_url, &documentation_url_len),
463         };
464         size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
465 
466         assert(f);
467         assert(j);
468 
469         /* Set the threshold to one bigger than the actual print
470          * threshold, so that if the line is actually longer than what
471          * we're willing to print, ellipsization will occur. This way
472          * we won't output a misleading line without any indication of
473          * truncation.
474          */
475         sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1);
476 
477         JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
478                 r = parse_fieldv(data, length, fields, ELEMENTSOF(fields));
479                 if (r < 0)
480                         return r;
481         }
482         if (r == -EBADMSG) {
483                 log_debug_errno(r, "Skipping message we can't read: %m");
484                 return 0;
485         }
486         if (r < 0)
487                 return log_error_errno(r, "Failed to get journal fields: %m");
488 
489         if (!message) {
490                 log_debug("Skipping message without MESSAGE= field.");
491                 return 0;
492         }
493 
494         if (!(flags & OUTPUT_SHOW_ALL))
495                 strip_tab_ansi(&message, &message_len, highlight_shifted);
496 
497         if (priority_len == 1 && *priority >= '0' && *priority <= '7')
498                 p = *priority - '0';
499 
500         audit = streq_ptr(transport, "audit");
501 
502         if (mode == OUTPUT_SHORT_MONOTONIC)
503                 r = output_timestamp_monotonic(f, j, monotonic);
504         else
505                 r = output_timestamp_realtime(f, j, mode, flags, realtime);
506         if (r < 0)
507                 return r;
508         n += r;
509 
510         if (flags & OUTPUT_NO_HOSTNAME) {
511                 /* Suppress display of the hostname if this is requested. */
512                 hostname = mfree(hostname);
513                 hostname_len = 0;
514         }
515 
516         if (hostname && shall_print(hostname, hostname_len, flags)) {
517                 fprintf(f, " %.*s", (int) hostname_len, hostname);
518                 n += hostname_len + 1;
519         }
520 
521         if (mode == OUTPUT_WITH_UNIT && ((unit && shall_print(unit, unit_len, flags)) ||
522                                          (user_unit && shall_print(user_unit, user_unit_len, flags)))) {
523                 if (unit) {
524                         fprintf(f, " %.*s", (int) unit_len, unit);
525                         n += unit_len + 1;
526                 }
527                 if (user_unit) {
528                         if (unit)
529                                 fprintf(f, "/%.*s", (int) user_unit_len, user_unit);
530                         else
531                                 fprintf(f, " %.*s", (int) user_unit_len, user_unit);
532                         n += unit_len + 1;
533                 }
534         } else if (identifier && shall_print(identifier, identifier_len, flags)) {
535                 fprintf(f, " %.*s", (int) identifier_len, identifier);
536                 n += identifier_len + 1;
537         } else if (comm && shall_print(comm, comm_len, flags)) {
538                 fprintf(f, " %.*s", (int) comm_len, comm);
539                 n += comm_len + 1;
540         } else
541                 fputs(" unknown", f);
542 
543         if (pid && shall_print(pid, pid_len, flags)) {
544                 fprintf(f, "[%.*s]", (int) pid_len, pid);
545                 n += pid_len + 2;
546         } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
547                 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
548                 n += fake_pid_len + 2;
549         }
550 
551         fputs(": ", f);
552 
553         if (urlify_enabled()) {
554                 _cleanup_free_ char *c = NULL;
555 
556                 /* Insert a hyperlink to a documentation URL before the message. Note that we don't make the
557                  * whole message a hyperlink, since otherwise the whole screen might end up being just
558                  * hyperlinks. Moreover, we want to be able to highlight parts of the message (such as the
559                  * config file, see below) hence let's keep the documentation URL link separate. */
560 
561                 if (documentation_url && shall_print(documentation_url, documentation_url_len, flags)) {
562                         c = strndup(documentation_url, documentation_url_len);
563                         if (!c)
564                                 return log_oom();
565 
566                         if (!documentation_url_is_valid(c)) /* Eat up invalid links */
567                                 c = mfree(c);
568                 }
569 
570                 if (!c)
571                         (void) url_from_catalog(j, &c); /* Acquire from catalog if not embedded in log message itself */
572 
573                 if (c) {
574                         _cleanup_free_ char *urlified = NULL;
575 
576                         if (terminal_urlify(c, special_glyph(SPECIAL_GLYPH_EXTERNAL_LINK), &urlified) >= 0) {
577                                 fputs(urlified, f);
578                                 fputc(' ', f);
579                         }
580                 }
581         }
582 
583         if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len))
584                 fprintf(f, "[%s blob data]\n", FORMAT_BYTES(message_len));
585         else {
586 
587                 /* URLify config_file string in message, if the message starts with it.
588                  * Skip URLification if the highlighted pattern overlaps. */
589                 if (config_file &&
590                     message_len >= config_file_len &&
591                     memcmp(message, config_file, config_file_len) == 0 &&
592                     (message_len == config_file_len || IN_SET(message[config_file_len], ':', ' ')) &&
593                     (!highlight || highlight_shifted[0] == 0 || highlight_shifted[0] > config_file_len)) {
594 
595                         _cleanup_free_ char *t = NULL, *urlified = NULL;
596 
597                         t = strndup(config_file, config_file_len);
598                         if (t && terminal_urlify_path(t, NULL, &urlified) >= 0) {
599                                 size_t urlified_len = strlen(urlified);
600                                 size_t shift = urlified_len - config_file_len;
601                                 char *joined;
602 
603                                 joined = realloc(urlified, message_len + shift);
604                                 if (joined) {
605                                         memcpy(joined + urlified_len, message + config_file_len, message_len - config_file_len);
606                                         free_and_replace(message, joined);
607                                         TAKE_PTR(urlified);
608                                         message_len += shift;
609                                         if (highlight) {
610                                                 highlight_shifted[0] += shift;
611                                                 highlight_shifted[1] += shift;
612                                         }
613                                 }
614                         }
615                 }
616 
617                 ellipsized |=
618                         print_multiline(f, n + 2, n_columns, flags, p, audit,
619                                         message, message_len,
620                                         highlight_shifted);
621         }
622 
623         if (flags & OUTPUT_CATALOG)
624                 (void) print_catalog(f, j);
625 
626         return ellipsized;
627 }
628 
output_verbose(FILE * f,sd_journal * j,OutputMode mode,unsigned n_columns,OutputFlags flags,const Set * output_fields,const size_t highlight[2])629 static int output_verbose(
630                 FILE *f,
631                 sd_journal *j,
632                 OutputMode mode,
633                 unsigned n_columns,
634                 OutputFlags flags,
635                 const Set *output_fields,
636                 const size_t highlight[2]) {
637 
638         const void *data;
639         size_t length;
640         _cleanup_free_ char *cursor = NULL;
641         uint64_t realtime = 0;
642         char ts[FORMAT_TIMESTAMP_MAX + 7];
643         const char *timestamp;
644         int r;
645 
646         assert(f);
647         assert(j);
648 
649         sd_journal_set_data_threshold(j, 0);
650 
651         r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length);
652         if (r == -ENOENT)
653                 log_debug("Source realtime timestamp not found");
654         else if (r < 0)
655                 return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get source realtime timestamp: %m");
656         else {
657                 _cleanup_free_ char *value = NULL;
658 
659                 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=",
660                                 STRLEN("_SOURCE_REALTIME_TIMESTAMP="), &value,
661                                 NULL);
662                 if (r < 0)
663                         return r;
664                 assert(r > 0);
665 
666                 r = safe_atou64(value, &realtime);
667                 if (r < 0)
668                         log_debug_errno(r, "Failed to parse realtime timestamp: %m");
669         }
670 
671         if (r < 0) {
672                 r = sd_journal_get_realtime_usec(j, &realtime);
673                 if (r < 0)
674                         return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get realtime timestamp: %m");
675         }
676 
677         r = sd_journal_get_cursor(j, &cursor);
678         if (r < 0)
679                 return log_error_errno(r, "Failed to get cursor: %m");
680 
681         timestamp = format_timestamp_style(ts, sizeof ts, realtime,
682                                            flags & OUTPUT_UTC ? TIMESTAMP_US_UTC : TIMESTAMP_US);
683         fprintf(f, "%s [%s]\n",
684                 timestamp ?: "(no timestamp)",
685                 cursor);
686 
687         JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
688                 const char *c, *p;
689                 int fieldlen;
690                 const char *on = "", *off = "";
691                 _cleanup_free_ char *urlified = NULL;
692                 size_t valuelen;
693 
694                 c = memchr(data, '=', length);
695                 if (!c)
696                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
697 
698                 fieldlen = c - (const char*) data;
699                 if (!journal_field_valid(data, fieldlen, true))
700                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
701 
702                 r = field_set_test(output_fields, data, fieldlen);
703                 if (r < 0)
704                         return r;
705                 if (r == 0)
706                         continue;
707 
708                 valuelen = length - 1 - fieldlen;
709 
710                 if ((flags & OUTPUT_COLOR) && (p = startswith(data, "MESSAGE="))) {
711                         on = ANSI_HIGHLIGHT;
712                         off = ANSI_NORMAL;
713                 } else if ((p = startswith(data, "CONFIG_FILE="))) {
714                         if (terminal_urlify_path(p, NULL, &urlified) >= 0) {
715                                 p = urlified;
716                                 valuelen = strlen(urlified);
717                         }
718                 } else
719                         p = c + 1;
720 
721                 if ((flags & OUTPUT_SHOW_ALL) ||
722                     (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
723                      && utf8_is_printable(data, length))) {
724                         fprintf(f, "    %s%.*s=", on, fieldlen, (const char*)data);
725                         print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, false,
726                                         p, valuelen,
727                                         NULL);
728                         fputs(off, f);
729                 } else
730                         fprintf(f, "    %s%.*s=[%s blob data]%s\n",
731                                 on,
732                                 (int) (c - (const char*) data),
733                                 (const char*) data,
734                                 FORMAT_BYTES(length - (c - (const char *) data) - 1),
735                                 off);
736         }
737 
738         if (r < 0)
739                 return r;
740 
741         if (flags & OUTPUT_CATALOG)
742                 (void) print_catalog(f, j);
743 
744         return 0;
745 }
746 
output_export(FILE * f,sd_journal * j,OutputMode mode,unsigned n_columns,OutputFlags flags,const Set * output_fields,const size_t highlight[2])747 static int output_export(
748                 FILE *f,
749                 sd_journal *j,
750                 OutputMode mode,
751                 unsigned n_columns,
752                 OutputFlags flags,
753                 const Set *output_fields,
754                 const size_t highlight[2]) {
755 
756         _cleanup_free_ char *cursor = NULL;
757         usec_t realtime, monotonic;
758         sd_id128_t boot_id;
759         const void *data;
760         size_t length;
761         int r;
762 
763         assert(j);
764 
765         sd_journal_set_data_threshold(j, 0);
766 
767         r = sd_journal_get_realtime_usec(j, &realtime);
768         if (r < 0)
769                 return log_error_errno(r, "Failed to get realtime timestamp: %m");
770 
771         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
772         if (r < 0)
773                 return log_error_errno(r, "Failed to get monotonic timestamp: %m");
774 
775         r = sd_journal_get_cursor(j, &cursor);
776         if (r < 0)
777                 return log_error_errno(r, "Failed to get cursor: %m");
778 
779         fprintf(f,
780                 "__CURSOR=%s\n"
781                 "__REALTIME_TIMESTAMP="USEC_FMT"\n"
782                 "__MONOTONIC_TIMESTAMP="USEC_FMT"\n"
783                 "_BOOT_ID=%s\n",
784                 cursor,
785                 realtime,
786                 monotonic,
787                 SD_ID128_TO_STRING(boot_id));
788 
789         JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
790                 size_t fieldlen;
791                 const char *c;
792 
793                 /* We already printed the boot id from the data in the header, hence let's suppress it here */
794                 if (memory_startswith(data, length, "_BOOT_ID="))
795                         continue;
796 
797                 c = memchr(data, '=', length);
798                 if (!c)
799                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
800 
801                 fieldlen = c - (const char*) data;
802                 if (!journal_field_valid(data, fieldlen, true))
803                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
804 
805                 r = field_set_test(output_fields, data, fieldlen);
806                 if (r < 0)
807                         return r;
808                 if (!r)
809                         continue;
810 
811                 if (utf8_is_printable_newline(data, length, false))
812                         fwrite(data, length, 1, f);
813                 else {
814                         uint64_t le64;
815 
816                         fwrite(data, fieldlen, 1, f);
817                         fputc('\n', f);
818                         le64 = htole64(length - fieldlen - 1);
819                         fwrite(&le64, sizeof(le64), 1, f);
820                         fwrite(c + 1, length - fieldlen - 1, 1, f);
821                 }
822 
823                 fputc('\n', f);
824         }
825         if (r == -EBADMSG) {
826                 log_debug_errno(r, "Skipping message we can't read: %m");
827                 return 0;
828         }
829 
830         if (r < 0)
831                 return r;
832 
833         fputc('\n', f);
834 
835         return 0;
836 }
837 
json_escape(FILE * f,const char * p,size_t l,OutputFlags flags)838 void json_escape(
839                 FILE *f,
840                 const char* p,
841                 size_t l,
842                 OutputFlags flags) {
843 
844         assert(f);
845         assert(p);
846 
847         if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
848                 fputs("null", f);
849 
850         else if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(p, l)) {
851                 bool not_first = false;
852 
853                 fputs("[ ", f);
854 
855                 while (l > 0) {
856                         if (not_first)
857                                 fprintf(f, ", %u", (uint8_t) *p);
858                         else {
859                                 not_first = true;
860                                 fprintf(f, "%u", (uint8_t) *p);
861                         }
862 
863                         p++;
864                         l--;
865                 }
866 
867                 fputs(" ]", f);
868         } else {
869                 fputc('"', f);
870 
871                 while (l > 0) {
872                         if (IN_SET(*p, '"', '\\')) {
873                                 fputc('\\', f);
874                                 fputc(*p, f);
875                         } else if (*p == '\n')
876                                 fputs("\\n", f);
877                         else if ((uint8_t) *p < ' ')
878                                 fprintf(f, "\\u%04x", (uint8_t) *p);
879                         else
880                                 fputc(*p, f);
881 
882                         p++;
883                         l--;
884                 }
885 
886                 fputc('"', f);
887         }
888 }
889 
890 struct json_data {
891         JsonVariant* name;
892         size_t n_values;
893         JsonVariant* values[];
894 };
895 
update_json_data(Hashmap * h,OutputFlags flags,const char * name,const void * value,size_t size)896 static int update_json_data(
897                 Hashmap *h,
898                 OutputFlags flags,
899                 const char *name,
900                 const void *value,
901                 size_t size) {
902 
903         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
904         struct json_data *d;
905         int r;
906 
907         if (!(flags & OUTPUT_SHOW_ALL) && strlen(name) + 1 + size >= JSON_THRESHOLD)
908                 r = json_variant_new_null(&v);
909         else if (utf8_is_printable(value, size))
910                 r = json_variant_new_stringn(&v, value, size);
911         else
912                 r = json_variant_new_array_bytes(&v, value, size);
913         if (r < 0)
914                 return log_error_errno(r, "Failed to allocate JSON data: %m");
915 
916         d = hashmap_get(h, name);
917         if (d) {
918                 struct json_data *w;
919 
920                 w = realloc(d, offsetof(struct json_data, values) + sizeof(JsonVariant*) * (d->n_values + 1));
921                 if (!w)
922                         return log_oom();
923 
924                 d = w;
925                 assert_se(hashmap_update(h, json_variant_string(d->name), d) >= 0);
926         } else {
927                 _cleanup_(json_variant_unrefp) JsonVariant *n = NULL;
928 
929                 r = json_variant_new_string(&n, name);
930                 if (r < 0)
931                         return log_error_errno(r, "Failed to allocate JSON name variant: %m");
932 
933                 d = malloc0(offsetof(struct json_data, values) + sizeof(JsonVariant*));
934                 if (!d)
935                         return log_oom();
936 
937                 r = hashmap_put(h, json_variant_string(n), d);
938                 if (r < 0) {
939                         free(d);
940                         return log_error_errno(r, "Failed to insert JSON name into hashmap: %m");
941                 }
942 
943                 d->name = TAKE_PTR(n);
944         }
945 
946         d->values[d->n_values++] = TAKE_PTR(v);
947         return 0;
948 }
949 
update_json_data_split(Hashmap * h,OutputFlags flags,const Set * output_fields,const void * data,size_t size)950 static int update_json_data_split(
951                 Hashmap *h,
952                 OutputFlags flags,
953                 const Set *output_fields,
954                 const void *data,
955                 size_t size) {
956 
957         size_t fieldlen;
958         const char *eq;
959         char *name;
960 
961         assert(h);
962         assert(data || size == 0);
963 
964         if (memory_startswith(data, size, "_BOOT_ID="))
965                 return 0;
966 
967         eq = memchr(data, '=', MIN(size, JSON_THRESHOLD));
968         if (!eq)
969                 return 0;
970 
971         fieldlen = eq - (const char*) data;
972         if (!journal_field_valid(data, fieldlen, true))
973                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
974 
975         name = strndupa_safe(data, fieldlen);
976         if (output_fields && !set_contains(output_fields, name))
977                 return 0;
978 
979         return update_json_data(h, flags, name, eq + 1, size - fieldlen - 1);
980 }
981 
output_json(FILE * f,sd_journal * j,OutputMode mode,unsigned n_columns,OutputFlags flags,const Set * output_fields,const size_t highlight[2])982 static int output_json(
983                 FILE *f,
984                 sd_journal *j,
985                 OutputMode mode,
986                 unsigned n_columns,
987                 OutputFlags flags,
988                 const Set *output_fields,
989                 const size_t highlight[2]) {
990 
991         char sid[SD_ID128_STRING_MAX], usecbuf[DECIMAL_STR_MAX(usec_t)];
992         _cleanup_(json_variant_unrefp) JsonVariant *object = NULL;
993         _cleanup_free_ char *cursor = NULL;
994         uint64_t realtime, monotonic;
995         JsonVariant **array = NULL;
996         struct json_data *d;
997         sd_id128_t boot_id;
998         Hashmap *h = NULL;
999         size_t n = 0;
1000         int r;
1001 
1002         assert(j);
1003 
1004         (void) sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
1005 
1006         r = sd_journal_get_realtime_usec(j, &realtime);
1007         if (r < 0)
1008                 return log_error_errno(r, "Failed to get realtime timestamp: %m");
1009 
1010         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
1011         if (r < 0)
1012                 return log_error_errno(r, "Failed to get monotonic timestamp: %m");
1013 
1014         r = sd_journal_get_cursor(j, &cursor);
1015         if (r < 0)
1016                 return log_error_errno(r, "Failed to get cursor: %m");
1017 
1018         h = hashmap_new(&string_hash_ops);
1019         if (!h)
1020                 return log_oom();
1021 
1022         r = update_json_data(h, flags, "__CURSOR", cursor, strlen(cursor));
1023         if (r < 0)
1024                 goto finish;
1025 
1026         xsprintf(usecbuf, USEC_FMT, realtime);
1027         r = update_json_data(h, flags, "__REALTIME_TIMESTAMP", usecbuf, strlen(usecbuf));
1028         if (r < 0)
1029                 goto finish;
1030 
1031         xsprintf(usecbuf, USEC_FMT, monotonic);
1032         r = update_json_data(h, flags, "__MONOTONIC_TIMESTAMP", usecbuf, strlen(usecbuf));
1033         if (r < 0)
1034                 goto finish;
1035 
1036         sd_id128_to_string(boot_id, sid);
1037         r = update_json_data(h, flags, "_BOOT_ID", sid, strlen(sid));
1038         if (r < 0)
1039                 goto finish;
1040 
1041         for (;;) {
1042                 const void *data;
1043                 size_t size;
1044 
1045                 r = sd_journal_enumerate_data(j, &data, &size);
1046                 if (r == -EBADMSG) {
1047                         log_debug_errno(r, "Skipping message we can't read: %m");
1048                         r = 0;
1049                         goto finish;
1050                 }
1051                 if (r < 0) {
1052                         log_error_errno(r, "Failed to read journal: %m");
1053                         goto finish;
1054                 }
1055                 if (r == 0)
1056                         break;
1057 
1058                 r = update_json_data_split(h, flags, output_fields, data, size);
1059                 if (r < 0)
1060                         goto finish;
1061         }
1062 
1063         array = new(JsonVariant*, hashmap_size(h)*2);
1064         if (!array) {
1065                 r = log_oom();
1066                 goto finish;
1067         }
1068 
1069         HASHMAP_FOREACH(d, h) {
1070                 assert(d->n_values > 0);
1071 
1072                 array[n++] = json_variant_ref(d->name);
1073 
1074                 if (d->n_values == 1)
1075                         array[n++] = json_variant_ref(d->values[0]);
1076                 else {
1077                         _cleanup_(json_variant_unrefp) JsonVariant *q = NULL;
1078 
1079                         r = json_variant_new_array(&q, d->values, d->n_values);
1080                         if (r < 0) {
1081                                 log_error_errno(r, "Failed to create JSON array: %m");
1082                                 goto finish;
1083                         }
1084 
1085                         array[n++] = TAKE_PTR(q);
1086                 }
1087         }
1088 
1089         r = json_variant_new_object(&object, array, n);
1090         if (r < 0) {
1091                 log_error_errno(r, "Failed to allocate JSON object: %m");
1092                 goto finish;
1093         }
1094 
1095         json_variant_dump(object,
1096                           output_mode_to_json_format_flags(mode) |
1097                           (FLAGS_SET(flags, OUTPUT_COLOR) ? JSON_FORMAT_COLOR : 0),
1098                           f, NULL);
1099 
1100         r = 0;
1101 
1102 finish:
1103         while ((d = hashmap_steal_first(h))) {
1104                 size_t k;
1105 
1106                 json_variant_unref(d->name);
1107                 for (k = 0; k < d->n_values; k++)
1108                         json_variant_unref(d->values[k]);
1109 
1110                 free(d);
1111         }
1112 
1113         hashmap_free(h);
1114 
1115         json_variant_unref_many(array, n);
1116         free(array);
1117 
1118         return r;
1119 }
1120 
output_cat_field(FILE * f,sd_journal * j,OutputFlags flags,int prio,const char * field,const size_t highlight[2])1121 static int output_cat_field(
1122                 FILE *f,
1123                 sd_journal *j,
1124                 OutputFlags flags,
1125                 int prio,
1126                 const char *field,
1127                 const size_t highlight[2]) {
1128 
1129         const char *color_on = "", *color_off = "", *highlight_on = "";
1130         const void *data;
1131         size_t l, fl;
1132         int r;
1133 
1134         if (FLAGS_SET(flags, OUTPUT_COLOR))
1135                 get_log_colors(prio, &color_on, &color_off, &highlight_on);
1136 
1137         r = sd_journal_get_data(j, field, &data, &l);
1138         if (r == -EBADMSG) {
1139                 log_debug_errno(r, "Skipping message we can't read: %m");
1140                 return 0;
1141         }
1142         if (r == -ENOENT) /* An entry without the requested field */
1143                 return 0;
1144         if (r < 0)
1145                 return log_error_errno(r, "Failed to get data: %m");
1146 
1147         fl = strlen(field);
1148         assert(l >= fl + 1);
1149         assert(((char*) data)[fl] == '=');
1150 
1151         data = (const uint8_t*) data + fl + 1;
1152         l -= fl + 1;
1153 
1154         if (FLAGS_SET(flags, OUTPUT_COLOR)) {
1155                 if (highlight) {
1156                         assert(highlight[0] <= highlight[1]);
1157                         assert(highlight[1] <= l);
1158 
1159                         fputs(color_on, f);
1160                         fwrite((const char*) data, 1, highlight[0], f);
1161                         fputs(highlight_on, f);
1162                         fwrite((const char*) data + highlight[0], 1, highlight[1] - highlight[0], f);
1163                         fputs(color_on, f);
1164                         fwrite((const char*) data + highlight[1], 1, l - highlight[1], f);
1165                         fputs(color_off, f);
1166                 } else {
1167                         fputs(color_on, f);
1168                         fwrite((const char*) data, 1, l, f);
1169                         fputs(color_off, f);
1170                 }
1171         } else
1172                 fwrite((const char*) data, 1, l, f);
1173 
1174         fputc('\n', f);
1175         return 0;
1176 }
1177 
output_cat(FILE * f,sd_journal * j,OutputMode mode,unsigned n_columns,OutputFlags flags,const Set * output_fields,const size_t highlight[2])1178 static int output_cat(
1179                 FILE *f,
1180                 sd_journal *j,
1181                 OutputMode mode,
1182                 unsigned n_columns,
1183                 OutputFlags flags,
1184                 const Set *output_fields,
1185                 const size_t highlight[2]) {
1186 
1187         int r, prio = LOG_INFO;
1188         const char *field;
1189 
1190         assert(j);
1191         assert(f);
1192 
1193         (void) sd_journal_set_data_threshold(j, 0);
1194 
1195         if (FLAGS_SET(flags, OUTPUT_COLOR)) {
1196                 const void *data;
1197                 size_t l;
1198 
1199                 /* Determine priority of this entry, so that we can color it nicely */
1200 
1201                 r = sd_journal_get_data(j, "PRIORITY", &data, &l);
1202                 if (r == -EBADMSG) {
1203                         log_debug_errno(r, "Skipping message we can't read: %m");
1204                         return 0;
1205                 }
1206                 if (r < 0) {
1207                         if (r != -ENOENT)
1208                                 return log_error_errno(r, "Failed to get data: %m");
1209 
1210                         /* An entry without PRIORITY */
1211                 } else if (l == 10 && memcmp(data, "PRIORITY=", 9) == 0) {
1212                         char c = ((char*) data)[9];
1213 
1214                         if (c >= '0' && c <= '7')
1215                                 prio = c - '0';
1216                 }
1217         }
1218 
1219         if (set_isempty(output_fields))
1220                 return output_cat_field(f, j, flags, prio, "MESSAGE", highlight);
1221 
1222         SET_FOREACH(field, output_fields) {
1223                 r = output_cat_field(f, j, flags, prio, field, streq(field, "MESSAGE") ? highlight : NULL);
1224                 if (r < 0)
1225                         return r;
1226         }
1227 
1228         return 0;
1229 }
1230 
1231 static int (*output_funcs[_OUTPUT_MODE_MAX])(
1232                 FILE *f,
1233                 sd_journal *j,
1234                 OutputMode mode,
1235                 unsigned n_columns,
1236                 OutputFlags flags,
1237                 const Set *output_fields,
1238                 const size_t highlight[2]) = {
1239 
1240         [OUTPUT_SHORT]             = output_short,
1241         [OUTPUT_SHORT_ISO]         = output_short,
1242         [OUTPUT_SHORT_ISO_PRECISE] = output_short,
1243         [OUTPUT_SHORT_PRECISE]     = output_short,
1244         [OUTPUT_SHORT_MONOTONIC]   = output_short,
1245         [OUTPUT_SHORT_UNIX]        = output_short,
1246         [OUTPUT_SHORT_FULL]        = output_short,
1247         [OUTPUT_VERBOSE]           = output_verbose,
1248         [OUTPUT_EXPORT]            = output_export,
1249         [OUTPUT_JSON]              = output_json,
1250         [OUTPUT_JSON_PRETTY]       = output_json,
1251         [OUTPUT_JSON_SSE]          = output_json,
1252         [OUTPUT_JSON_SEQ]          = output_json,
1253         [OUTPUT_CAT]               = output_cat,
1254         [OUTPUT_WITH_UNIT]         = output_short,
1255 };
1256 
show_journal_entry(FILE * f,sd_journal * j,OutputMode mode,unsigned n_columns,OutputFlags flags,char ** output_fields,const size_t highlight[2],bool * ellipsized)1257 int show_journal_entry(
1258                 FILE *f,
1259                 sd_journal *j,
1260                 OutputMode mode,
1261                 unsigned n_columns,
1262                 OutputFlags flags,
1263                 char **output_fields,
1264                 const size_t highlight[2],
1265                 bool *ellipsized) {
1266 
1267         _cleanup_set_free_ Set *fields = NULL;
1268         int r;
1269 
1270         assert(mode >= 0);
1271         assert(mode < _OUTPUT_MODE_MAX);
1272 
1273         if (n_columns <= 0)
1274                 n_columns = columns();
1275 
1276         r = set_put_strdupv(&fields, output_fields);
1277         if (r < 0)
1278                 return r;
1279 
1280         r = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight);
1281 
1282         if (ellipsized && r > 0)
1283                 *ellipsized = true;
1284 
1285         return r;
1286 }
1287 
maybe_print_begin_newline(FILE * f,OutputFlags * flags)1288 static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) {
1289         assert(f);
1290         assert(flags);
1291 
1292         if (!(*flags & OUTPUT_BEGIN_NEWLINE))
1293                 return 0;
1294 
1295         /* Print a beginning new line if that's request, but only once
1296          * on the first line we print. */
1297 
1298         fputc('\n', f);
1299         *flags &= ~OUTPUT_BEGIN_NEWLINE;
1300         return 0;
1301 }
1302 
show_journal(FILE * f,sd_journal * j,OutputMode mode,unsigned n_columns,usec_t not_before,unsigned how_many,OutputFlags flags,bool * ellipsized)1303 int show_journal(
1304                 FILE *f,
1305                 sd_journal *j,
1306                 OutputMode mode,
1307                 unsigned n_columns,
1308                 usec_t not_before,
1309                 unsigned how_many,
1310                 OutputFlags flags,
1311                 bool *ellipsized) {
1312 
1313         int r;
1314         unsigned line = 0;
1315         bool need_seek = false;
1316         int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
1317 
1318         assert(j);
1319         assert(mode >= 0);
1320         assert(mode < _OUTPUT_MODE_MAX);
1321 
1322         if (how_many == UINT_MAX)
1323                 need_seek = true;
1324         else {
1325                 /* Seek to end */
1326                 r = sd_journal_seek_tail(j);
1327                 if (r < 0)
1328                         return log_error_errno(r, "Failed to seek to tail: %m");
1329 
1330                 r = sd_journal_previous_skip(j, how_many);
1331                 if (r < 0)
1332                         return log_error_errno(r, "Failed to skip previous: %m");
1333         }
1334 
1335         for (;;) {
1336                 usec_t usec;
1337 
1338                 if (need_seek) {
1339                         r = sd_journal_next(j);
1340                         if (r < 0)
1341                                 return log_error_errno(r, "Failed to iterate through journal: %m");
1342                 }
1343 
1344                 if (r == 0)
1345                         break;
1346 
1347                 need_seek = true;
1348 
1349                 if (not_before > 0) {
1350                         r = sd_journal_get_monotonic_usec(j, &usec, NULL);
1351 
1352                         /* -ESTALE is returned if the timestamp is not from this boot */
1353                         if (r == -ESTALE)
1354                                 continue;
1355                         else if (r < 0)
1356                                 return log_error_errno(r, "Failed to get journal time: %m");
1357 
1358                         if (usec < not_before)
1359                                 continue;
1360                 }
1361 
1362                 line++;
1363                 maybe_print_begin_newline(f, &flags);
1364 
1365                 r = show_journal_entry(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
1366                 if (r < 0)
1367                         return r;
1368         }
1369 
1370         if (warn_cutoff && line < how_many && not_before > 0) {
1371                 sd_id128_t boot_id;
1372                 usec_t cutoff = 0;
1373 
1374                 /* Check whether the cutoff line is too early */
1375 
1376                 r = sd_id128_get_boot(&boot_id);
1377                 if (r < 0)
1378                         return log_error_errno(r, "Failed to get boot id: %m");
1379 
1380                 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
1381                 if (r < 0)
1382                         return log_error_errno(r, "Failed to get journal cutoff time: %m");
1383 
1384                 if (r > 0 && not_before < cutoff) {
1385                         maybe_print_begin_newline(f, &flags);
1386 
1387                         /* If we logged *something* and no permission error happened, than we can reliably
1388                          * emit the warning about rotation. If we didn't log anything and access errors
1389                          * happened, emit hint about permissions. Otherwise, give a generic message, since we
1390                          * can't diagnose the issue. */
1391 
1392                         bool noaccess = journal_access_blocked(j);
1393 
1394                         if (line == 0 && noaccess)
1395                                 fprintf(f, "Warning: some journal files were not opened due to insufficient permissions.\n");
1396                         else if (!noaccess)
1397                                 fprintf(f, "Notice: journal has been rotated since unit was started, output may be incomplete.\n");
1398                         else
1399                                 fprintf(f, "Warning: journal has been rotated since unit was started and some journal "
1400                                         "files were not opened due to insufficient permissions, output may be incomplete.\n");
1401                 }
1402 
1403                 warn_cutoff = false;
1404         }
1405 
1406         return 0;
1407 }
1408 
add_matches_for_unit(sd_journal * j,const char * unit)1409 int add_matches_for_unit(sd_journal *j, const char *unit) {
1410         const char *m1, *m2, *m3, *m4;
1411         int r;
1412 
1413         assert(j);
1414         assert(unit);
1415 
1416         m1 = strjoina("_SYSTEMD_UNIT=", unit);
1417         m2 = strjoina("COREDUMP_UNIT=", unit);
1418         m3 = strjoina("UNIT=", unit);
1419         m4 = strjoina("OBJECT_SYSTEMD_UNIT=", unit);
1420 
1421         (void)(
1422             /* Look for messages from the service itself */
1423             (r = sd_journal_add_match(j, m1, 0)) ||
1424 
1425             /* Look for coredumps of the service */
1426             (r = sd_journal_add_disjunction(j)) ||
1427             (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1428             (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1429             (r = sd_journal_add_match(j, m2, 0)) ||
1430 
1431              /* Look for messages from PID 1 about this service */
1432             (r = sd_journal_add_disjunction(j)) ||
1433             (r = sd_journal_add_match(j, "_PID=1", 0)) ||
1434             (r = sd_journal_add_match(j, m3, 0)) ||
1435 
1436             /* Look for messages from authorized daemons about this service */
1437             (r = sd_journal_add_disjunction(j)) ||
1438             (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1439             (r = sd_journal_add_match(j, m4, 0))
1440         );
1441 
1442         if (r == 0 && endswith(unit, ".slice")) {
1443                 const char *m5;
1444 
1445                 m5 = strjoina("_SYSTEMD_SLICE=", unit);
1446 
1447                 /* Show all messages belonging to a slice */
1448                 (void)(
1449                         (r = sd_journal_add_disjunction(j)) ||
1450                         (r = sd_journal_add_match(j, m5, 0))
1451                         );
1452         }
1453 
1454         return r;
1455 }
1456 
add_matches_for_user_unit(sd_journal * j,const char * unit,uid_t uid)1457 int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
1458         int r;
1459         char *m1, *m2, *m3, *m4;
1460         char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
1461 
1462         assert(j);
1463         assert(unit);
1464 
1465         m1 = strjoina("_SYSTEMD_USER_UNIT=", unit);
1466         m2 = strjoina("USER_UNIT=", unit);
1467         m3 = strjoina("COREDUMP_USER_UNIT=", unit);
1468         m4 = strjoina("OBJECT_SYSTEMD_USER_UNIT=", unit);
1469         sprintf(muid, "_UID="UID_FMT, uid);
1470 
1471         (void) (
1472                 /* Look for messages from the user service itself */
1473                 (r = sd_journal_add_match(j, m1, 0)) ||
1474                 (r = sd_journal_add_match(j, muid, 0)) ||
1475 
1476                 /* Look for messages from systemd about this service */
1477                 (r = sd_journal_add_disjunction(j)) ||
1478                 (r = sd_journal_add_match(j, m2, 0)) ||
1479                 (r = sd_journal_add_match(j, muid, 0)) ||
1480 
1481                 /* Look for coredumps of the service */
1482                 (r = sd_journal_add_disjunction(j)) ||
1483                 (r = sd_journal_add_match(j, m3, 0)) ||
1484                 (r = sd_journal_add_match(j, muid, 0)) ||
1485                 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1486 
1487                 /* Look for messages from authorized daemons about this service */
1488                 (r = sd_journal_add_disjunction(j)) ||
1489                 (r = sd_journal_add_match(j, m4, 0)) ||
1490                 (r = sd_journal_add_match(j, muid, 0)) ||
1491                 (r = sd_journal_add_match(j, "_UID=0", 0))
1492         );
1493 
1494         if (r == 0 && endswith(unit, ".slice")) {
1495                 const char *m5;
1496 
1497                 m5 = strjoina("_SYSTEMD_SLICE=", unit);
1498 
1499                 /* Show all messages belonging to a slice */
1500                 (void)(
1501                         (r = sd_journal_add_disjunction(j)) ||
1502                         (r = sd_journal_add_match(j, m5, 0)) ||
1503                         (r = sd_journal_add_match(j, muid, 0))
1504                         );
1505         }
1506 
1507         return r;
1508 }
1509 
get_boot_id_for_machine(const char * machine,sd_id128_t * boot_id)1510 static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
1511         _cleanup_close_pair_ int pair[2] = { -1, -1 };
1512         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
1513         char buf[SD_ID128_UUID_STRING_MAX];
1514         pid_t pid, child;
1515         ssize_t k;
1516         int r;
1517 
1518         assert(machine);
1519         assert(boot_id);
1520 
1521         r = container_get_leader(machine, &pid);
1522         if (r < 0)
1523                 return r;
1524 
1525         r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, NULL, &rootfd);
1526         if (r < 0)
1527                 return r;
1528 
1529         if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1530                 return -errno;
1531 
1532         r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
1533                            pidnsfd, mntnsfd, -1, -1, rootfd, &child);
1534         if (r < 0)
1535                 return r;
1536         if (r == 0) {
1537                 int fd;
1538 
1539                 pair[0] = safe_close(pair[0]);
1540 
1541                 fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
1542                 if (fd < 0)
1543                         _exit(EXIT_FAILURE);
1544 
1545                 r = loop_read_exact(fd, buf, 36, false);
1546                 safe_close(fd);
1547                 if (r < 0)
1548                         _exit(EXIT_FAILURE);
1549 
1550                 k = send(pair[1], buf, 36, MSG_NOSIGNAL);
1551                 if (k != 36)
1552                         _exit(EXIT_FAILURE);
1553 
1554                 _exit(EXIT_SUCCESS);
1555         }
1556 
1557         pair[1] = safe_close(pair[1]);
1558 
1559         r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
1560         if (r < 0)
1561                 return r;
1562         if (r != EXIT_SUCCESS)
1563                 return -EIO;
1564 
1565         k = recv(pair[0], buf, 36, 0);
1566         if (k != 36)
1567                 return -EIO;
1568 
1569         buf[36] = 0;
1570         r = sd_id128_from_string(buf, boot_id);
1571         if (r < 0)
1572                 return r;
1573 
1574         return 0;
1575 }
1576 
add_match_this_boot(sd_journal * j,const char * machine)1577 int add_match_this_boot(sd_journal *j, const char *machine) {
1578         char match[9+32+1] = "_BOOT_ID=";
1579         sd_id128_t boot_id;
1580         int r;
1581 
1582         assert(j);
1583 
1584         if (machine) {
1585                 r = get_boot_id_for_machine(machine, &boot_id);
1586                 if (r < 0)
1587                         return log_error_errno(r, "Failed to get boot id of container %s: %m", machine);
1588         } else {
1589                 r = sd_id128_get_boot(&boot_id);
1590                 if (r < 0)
1591                         return log_error_errno(r, "Failed to get boot id: %m");
1592         }
1593 
1594         sd_id128_to_string(boot_id, match + 9);
1595         r = sd_journal_add_match(j, match, strlen(match));
1596         if (r < 0)
1597                 return log_error_errno(r, "Failed to add match: %m");
1598 
1599         r = sd_journal_add_conjunction(j);
1600         if (r < 0)
1601                 return log_error_errno(r, "Failed to add conjunction: %m");
1602 
1603         return 0;
1604 }
1605 
show_journal_by_unit(FILE * f,const char * unit,const char * log_namespace,OutputMode mode,unsigned n_columns,usec_t not_before,unsigned how_many,uid_t uid,OutputFlags flags,int journal_open_flags,bool system_unit,bool * ellipsized)1606 int show_journal_by_unit(
1607                 FILE *f,
1608                 const char *unit,
1609                 const char *log_namespace,
1610                 OutputMode mode,
1611                 unsigned n_columns,
1612                 usec_t not_before,
1613                 unsigned how_many,
1614                 uid_t uid,
1615                 OutputFlags flags,
1616                 int journal_open_flags,
1617                 bool system_unit,
1618                 bool *ellipsized) {
1619 
1620         _cleanup_(sd_journal_closep) sd_journal *j = NULL;
1621         int r;
1622 
1623         assert(mode >= 0);
1624         assert(mode < _OUTPUT_MODE_MAX);
1625         assert(unit);
1626 
1627         if (how_many <= 0)
1628                 return 0;
1629 
1630         r = sd_journal_open_namespace(&j, log_namespace, journal_open_flags | SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE);
1631         if (r < 0)
1632                 return log_error_errno(r, "Failed to open journal: %m");
1633 
1634         if (system_unit)
1635                 r = add_matches_for_unit(j, unit);
1636         else
1637                 r = add_matches_for_user_unit(j, unit, uid);
1638         if (r < 0)
1639                 return log_error_errno(r, "Failed to add unit matches: %m");
1640 
1641         r = sd_journal_add_conjunction(j);
1642         if (r < 0)
1643                 return log_error_errno(r, "Failed to add conjunction: %m");
1644 
1645         r = add_match_this_boot(j, NULL);
1646         if (r < 0)
1647                 return r;
1648 
1649         if (DEBUG_LOGGING) {
1650                 _cleanup_free_ char *filter = NULL;
1651 
1652                 filter = journal_make_match_string(j);
1653                 if (!filter)
1654                         return log_oom();
1655 
1656                 log_debug("Journal filter: %s", filter);
1657         }
1658 
1659         return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
1660 }
1661