Line data Source code
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 <errno.h>
24 : #include <sys/socket.h>
25 : #include <string.h>
26 : #include <fcntl.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 : #include "formats-util.h"
35 : #include "process-util.h"
36 : #include "terminal-util.h"
37 :
38 : /* up to three lines (each up to 100 characters),
39 : or 300 characters, whichever is less */
40 : #define PRINT_LINE_THRESHOLD 3
41 : #define PRINT_CHAR_THRESHOLD 300
42 :
43 : #define JSON_THRESHOLD 4096
44 :
45 0 : static int print_catalog(FILE *f, sd_journal *j) {
46 : int r;
47 0 : _cleanup_free_ char *t = NULL, *z = NULL;
48 :
49 :
50 0 : r = sd_journal_get_catalog(j, &t);
51 0 : if (r < 0)
52 0 : return r;
53 :
54 0 : z = strreplace(strstrip(t), "\n", "\n-- ");
55 0 : if (!z)
56 0 : return log_oom();
57 :
58 0 : fputs("-- ", f);
59 0 : fputs(z, f);
60 0 : fputc('\n', f);
61 :
62 0 : return 0;
63 : }
64 :
65 0 : static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
66 : size_t fl, nl;
67 : void *buf;
68 :
69 0 : assert(data);
70 0 : assert(field);
71 0 : assert(target);
72 0 : assert(target_size);
73 :
74 0 : fl = strlen(field);
75 0 : if (length < fl)
76 0 : return 0;
77 :
78 0 : if (memcmp(data, field, fl))
79 0 : return 0;
80 :
81 0 : nl = length - fl;
82 0 : buf = malloc(nl+1);
83 0 : if (!buf)
84 0 : return log_oom();
85 :
86 0 : memcpy(buf, (const char*) data + fl, nl);
87 0 : ((char*)buf)[nl] = 0;
88 :
89 0 : free(*target);
90 0 : *target = buf;
91 0 : *target_size = nl;
92 :
93 0 : return 1;
94 : }
95 :
96 0 : static bool shall_print(const char *p, size_t l, OutputFlags flags) {
97 0 : assert(p);
98 :
99 0 : if (flags & OUTPUT_SHOW_ALL)
100 0 : return true;
101 :
102 0 : if (l >= PRINT_CHAR_THRESHOLD)
103 0 : return false;
104 :
105 0 : if (!utf8_is_printable(p, l))
106 0 : return false;
107 :
108 0 : return true;
109 : }
110 :
111 0 : static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputFlags flags, int priority, const char* message, size_t message_len) {
112 0 : const char *color_on = "", *color_off = "";
113 : const char *pos, *end;
114 0 : bool ellipsized = false;
115 0 : int line = 0;
116 :
117 0 : if (flags & OUTPUT_COLOR) {
118 0 : if (priority <= LOG_ERR) {
119 0 : color_on = ANSI_HIGHLIGHT_RED_ON;
120 0 : color_off = ANSI_HIGHLIGHT_OFF;
121 0 : } else if (priority <= LOG_NOTICE) {
122 0 : color_on = ANSI_HIGHLIGHT_ON;
123 0 : color_off = ANSI_HIGHLIGHT_OFF;
124 : }
125 : }
126 :
127 : /* A special case: make sure that we print a newline when
128 : the message is empty. */
129 0 : if (message_len == 0)
130 0 : fputs("\n", f);
131 :
132 0 : for (pos = message;
133 0 : pos < message + message_len;
134 0 : pos = end + 1, line++) {
135 0 : bool continuation = line > 0;
136 : bool tail_line;
137 : int len;
138 0 : for (end = pos; end < message + message_len && *end != '\n'; end++)
139 : ;
140 0 : len = end - pos;
141 0 : assert(len >= 0);
142 :
143 : /* We need to figure out when we are showing not-last line, *and*
144 : * will skip subsequent lines. In that case, we will put the dots
145 : * at the end of the line, instead of putting dots in the middle
146 : * or not at all.
147 : */
148 0 : tail_line =
149 0 : line + 1 == PRINT_LINE_THRESHOLD ||
150 0 : end + 1 >= message + PRINT_CHAR_THRESHOLD;
151 :
152 0 : if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
153 0 : (prefix + len + 1 < n_columns && !tail_line)) {
154 0 : fprintf(f, "%*s%s%.*s%s\n",
155 : continuation * prefix, "",
156 : color_on, len, pos, color_off);
157 0 : continue;
158 : }
159 :
160 : /* Beyond this point, ellipsization will happen. */
161 0 : ellipsized = true;
162 :
163 0 : if (prefix < n_columns && n_columns - prefix >= 3) {
164 0 : if (n_columns - prefix > (unsigned) len + 3)
165 0 : fprintf(f, "%*s%s%.*s...%s\n",
166 : continuation * prefix, "",
167 : color_on, len, pos, color_off);
168 : else {
169 0 : _cleanup_free_ char *e;
170 :
171 0 : e = ellipsize_mem(pos, len, n_columns - prefix,
172 : tail_line ? 100 : 90);
173 0 : if (!e)
174 0 : fprintf(f, "%*s%s%.*s%s\n",
175 : continuation * prefix, "",
176 : color_on, len, pos, color_off);
177 : else
178 0 : fprintf(f, "%*s%s%s%s\n",
179 : continuation * prefix, "",
180 : color_on, e, color_off);
181 : }
182 : } else
183 0 : fputs("...\n", f);
184 :
185 0 : if (tail_line)
186 0 : break;
187 : }
188 :
189 0 : return ellipsized;
190 : }
191 :
192 0 : static int output_short(
193 : FILE *f,
194 : sd_journal *j,
195 : OutputMode mode,
196 : unsigned n_columns,
197 : OutputFlags flags) {
198 :
199 : int r;
200 : const void *data;
201 : size_t length;
202 0 : size_t n = 0;
203 0 : _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
204 0 : 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;
205 0 : int p = LOG_INFO;
206 0 : bool ellipsized = false;
207 :
208 0 : assert(f);
209 0 : assert(j);
210 :
211 : /* Set the threshold to one bigger than the actual print
212 : * threshold, so that if the line is actually longer than what
213 : * we're willing to print, ellipsization will occur. This way
214 : * we won't output a misleading line without any indication of
215 : * truncation.
216 : */
217 0 : sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1);
218 :
219 0 : JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
220 :
221 0 : r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
222 0 : if (r < 0)
223 0 : return r;
224 0 : else if (r > 0)
225 0 : continue;
226 :
227 0 : r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
228 0 : if (r < 0)
229 0 : return r;
230 0 : else if (r > 0)
231 0 : continue;
232 :
233 0 : r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
234 0 : if (r < 0)
235 0 : return r;
236 0 : else if (r > 0)
237 0 : continue;
238 :
239 0 : r = parse_field(data, length, "_COMM=", &comm, &comm_len);
240 0 : if (r < 0)
241 0 : return r;
242 0 : else if (r > 0)
243 0 : continue;
244 :
245 0 : r = parse_field(data, length, "_PID=", &pid, &pid_len);
246 0 : if (r < 0)
247 0 : return r;
248 0 : else if (r > 0)
249 0 : continue;
250 :
251 0 : r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
252 0 : if (r < 0)
253 0 : return r;
254 0 : else if (r > 0)
255 0 : continue;
256 :
257 0 : r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
258 0 : if (r < 0)
259 0 : return r;
260 0 : else if (r > 0)
261 0 : continue;
262 :
263 0 : r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
264 0 : if (r < 0)
265 0 : return r;
266 0 : else if (r > 0)
267 0 : continue;
268 :
269 0 : r = parse_field(data, length, "MESSAGE=", &message, &message_len);
270 0 : if (r < 0)
271 0 : return r;
272 : }
273 :
274 0 : if (r < 0)
275 0 : return log_error_errno(r, "Failed to get journal fields: %m");
276 :
277 0 : if (!message) {
278 0 : log_debug("Skipping message without MESSAGE= field.");
279 0 : return 0;
280 : }
281 :
282 0 : if (!(flags & OUTPUT_SHOW_ALL))
283 0 : strip_tab_ansi(&message, &message_len);
284 :
285 0 : if (priority_len == 1 && *priority >= '0' && *priority <= '7')
286 0 : p = *priority - '0';
287 :
288 0 : if (mode == OUTPUT_SHORT_MONOTONIC) {
289 : uint64_t t;
290 : sd_id128_t boot_id;
291 :
292 0 : r = -ENOENT;
293 :
294 0 : if (monotonic)
295 0 : r = safe_atou64(monotonic, &t);
296 :
297 0 : if (r < 0)
298 0 : r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
299 :
300 0 : if (r < 0)
301 0 : return log_error_errno(r, "Failed to get monotonic timestamp: %m");
302 :
303 0 : fprintf(f, "[%5llu.%06llu]",
304 0 : (unsigned long long) (t / USEC_PER_SEC),
305 0 : (unsigned long long) (t % USEC_PER_SEC));
306 :
307 0 : n += 1 + 5 + 1 + 6 + 1;
308 :
309 : } else {
310 : char buf[64];
311 : uint64_t x;
312 : time_t t;
313 : struct tm tm;
314 : struct tm *(*gettime_r)(const time_t *, struct tm *);
315 :
316 0 : r = -ENOENT;
317 0 : gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r;
318 :
319 0 : if (realtime)
320 0 : r = safe_atou64(realtime, &x);
321 :
322 0 : if (r < 0)
323 0 : r = sd_journal_get_realtime_usec(j, &x);
324 :
325 0 : if (r < 0)
326 0 : return log_error_errno(r, "Failed to get realtime timestamp: %m");
327 :
328 0 : t = (time_t) (x / USEC_PER_SEC);
329 :
330 0 : switch(mode) {
331 : case OUTPUT_SHORT_ISO:
332 0 : r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm));
333 0 : break;
334 : case OUTPUT_SHORT_PRECISE:
335 0 : r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));
336 0 : if (r > 0) {
337 0 : snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
338 0 : ".%06llu", (unsigned long long) (x % USEC_PER_SEC));
339 : }
340 0 : break;
341 : default:
342 0 : r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));
343 : }
344 :
345 0 : if (r <= 0) {
346 0 : log_error("Failed to format time.");
347 0 : return -EINVAL;
348 : }
349 :
350 0 : fputs(buf, f);
351 0 : n += strlen(buf);
352 : }
353 :
354 0 : if (hostname && shall_print(hostname, hostname_len, flags)) {
355 0 : fprintf(f, " %.*s", (int) hostname_len, hostname);
356 0 : n += hostname_len + 1;
357 : }
358 :
359 0 : if (identifier && shall_print(identifier, identifier_len, flags)) {
360 0 : fprintf(f, " %.*s", (int) identifier_len, identifier);
361 0 : n += identifier_len + 1;
362 0 : } else if (comm && shall_print(comm, comm_len, flags)) {
363 0 : fprintf(f, " %.*s", (int) comm_len, comm);
364 0 : n += comm_len + 1;
365 : } else
366 0 : fputs(" unknown", f);
367 :
368 0 : if (pid && shall_print(pid, pid_len, flags)) {
369 0 : fprintf(f, "[%.*s]", (int) pid_len, pid);
370 0 : n += pid_len + 2;
371 0 : } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
372 0 : fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
373 0 : n += fake_pid_len + 2;
374 : }
375 :
376 0 : if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
377 : char bytes[FORMAT_BYTES_MAX];
378 0 : fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
379 : } else {
380 0 : fputs(": ", f);
381 0 : ellipsized |=
382 0 : print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
383 : }
384 :
385 0 : if (flags & OUTPUT_CATALOG)
386 0 : print_catalog(f, j);
387 :
388 0 : return ellipsized;
389 : }
390 :
391 0 : static int output_verbose(
392 : FILE *f,
393 : sd_journal *j,
394 : OutputMode mode,
395 : unsigned n_columns,
396 : OutputFlags flags) {
397 :
398 : const void *data;
399 : size_t length;
400 0 : _cleanup_free_ char *cursor = NULL;
401 : uint64_t realtime;
402 : char ts[FORMAT_TIMESTAMP_MAX + 7];
403 : int r;
404 :
405 0 : assert(f);
406 0 : assert(j);
407 :
408 0 : sd_journal_set_data_threshold(j, 0);
409 :
410 0 : r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length);
411 0 : if (r == -ENOENT)
412 0 : log_debug("Source realtime timestamp not found");
413 0 : else if (r < 0)
414 0 : return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get source realtime timestamp: %m");
415 : else {
416 0 : _cleanup_free_ char *value = NULL;
417 : size_t size;
418 :
419 0 : r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, &size);
420 0 : if (r < 0)
421 0 : log_debug_errno(r, "_SOURCE_REALTIME_TIMESTAMP invalid: %m");
422 : else {
423 0 : r = safe_atou64(value, &realtime);
424 0 : if (r < 0)
425 0 : log_debug_errno(r, "Failed to parse realtime timestamp: %m");
426 : }
427 : }
428 :
429 0 : if (r < 0) {
430 0 : r = sd_journal_get_realtime_usec(j, &realtime);
431 0 : if (r < 0)
432 0 : return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get realtime timestamp: %m");
433 : }
434 :
435 0 : r = sd_journal_get_cursor(j, &cursor);
436 0 : if (r < 0)
437 0 : return log_error_errno(r, "Failed to get cursor: %m");
438 :
439 0 : fprintf(f, "%s [%s]\n",
440 0 : flags & OUTPUT_UTC ?
441 0 : format_timestamp_us_utc(ts, sizeof(ts), realtime) :
442 0 : format_timestamp_us(ts, sizeof(ts), realtime),
443 : cursor);
444 :
445 0 : JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
446 : const char *c;
447 : int fieldlen;
448 0 : const char *on = "", *off = "";
449 :
450 0 : c = memchr(data, '=', length);
451 0 : if (!c) {
452 0 : log_error("Invalid field.");
453 0 : return -EINVAL;
454 : }
455 0 : fieldlen = c - (const char*) data;
456 :
457 0 : if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
458 0 : on = ANSI_HIGHLIGHT_ON;
459 0 : off = ANSI_HIGHLIGHT_OFF;
460 : }
461 :
462 0 : if (flags & OUTPUT_SHOW_ALL ||
463 0 : (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
464 0 : && utf8_is_printable(data, length))) {
465 0 : fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
466 0 : print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
467 0 : fputs(off, f);
468 : } else {
469 : char bytes[FORMAT_BYTES_MAX];
470 :
471 0 : fprintf(f, " %s%.*s=[%s blob data]%s\n",
472 : on,
473 0 : (int) (c - (const char*) data),
474 : (const char*) data,
475 0 : format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1),
476 : off);
477 : }
478 : }
479 :
480 0 : if (r < 0)
481 0 : return r;
482 :
483 0 : if (flags & OUTPUT_CATALOG)
484 0 : print_catalog(f, j);
485 :
486 0 : return 0;
487 : }
488 :
489 0 : static int output_export(
490 : FILE *f,
491 : sd_journal *j,
492 : OutputMode mode,
493 : unsigned n_columns,
494 : OutputFlags flags) {
495 :
496 : sd_id128_t boot_id;
497 : char sid[33];
498 : int r;
499 : usec_t realtime, monotonic;
500 0 : _cleanup_free_ char *cursor = NULL;
501 : const void *data;
502 : size_t length;
503 :
504 0 : assert(j);
505 :
506 0 : sd_journal_set_data_threshold(j, 0);
507 :
508 0 : r = sd_journal_get_realtime_usec(j, &realtime);
509 0 : if (r < 0)
510 0 : return log_error_errno(r, "Failed to get realtime timestamp: %m");
511 :
512 0 : r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
513 0 : if (r < 0)
514 0 : return log_error_errno(r, "Failed to get monotonic timestamp: %m");
515 :
516 0 : r = sd_journal_get_cursor(j, &cursor);
517 0 : if (r < 0)
518 0 : return log_error_errno(r, "Failed to get cursor: %m");
519 :
520 0 : fprintf(f,
521 : "__CURSOR=%s\n"
522 : "__REALTIME_TIMESTAMP="USEC_FMT"\n"
523 : "__MONOTONIC_TIMESTAMP="USEC_FMT"\n"
524 : "_BOOT_ID=%s\n",
525 : cursor,
526 : realtime,
527 : monotonic,
528 : sd_id128_to_string(boot_id, sid));
529 :
530 0 : JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
531 :
532 : /* We already printed the boot id, from the data in
533 : * the header, hence let's suppress it here */
534 0 : if (length >= 9 &&
535 0 : startswith(data, "_BOOT_ID="))
536 0 : continue;
537 :
538 0 : if (utf8_is_printable_newline(data, length, false))
539 0 : fwrite(data, length, 1, f);
540 : else {
541 : const char *c;
542 : uint64_t le64;
543 :
544 0 : c = memchr(data, '=', length);
545 0 : if (!c) {
546 0 : log_error("Invalid field.");
547 0 : return -EINVAL;
548 : }
549 :
550 0 : fwrite(data, c - (const char*) data, 1, f);
551 0 : fputc('\n', f);
552 0 : le64 = htole64(length - (c - (const char*) data) - 1);
553 0 : fwrite(&le64, sizeof(le64), 1, f);
554 0 : fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
555 : }
556 :
557 0 : fputc('\n', f);
558 : }
559 :
560 0 : if (r < 0)
561 0 : return r;
562 :
563 0 : fputc('\n', f);
564 :
565 0 : return 0;
566 : }
567 :
568 0 : void json_escape(
569 : FILE *f,
570 : const char* p,
571 : size_t l,
572 : OutputFlags flags) {
573 :
574 0 : assert(f);
575 0 : assert(p);
576 :
577 0 : if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
578 :
579 0 : fputs("null", f);
580 :
581 0 : else if (!utf8_is_printable(p, l)) {
582 0 : bool not_first = false;
583 :
584 0 : fputs("[ ", f);
585 :
586 0 : while (l > 0) {
587 0 : if (not_first)
588 0 : fprintf(f, ", %u", (uint8_t) *p);
589 : else {
590 0 : not_first = true;
591 0 : fprintf(f, "%u", (uint8_t) *p);
592 : }
593 :
594 0 : p++;
595 0 : l--;
596 : }
597 :
598 0 : fputs(" ]", f);
599 : } else {
600 0 : fputc('\"', f);
601 :
602 0 : while (l > 0) {
603 0 : if (*p == '"' || *p == '\\') {
604 0 : fputc('\\', f);
605 0 : fputc(*p, f);
606 0 : } else if (*p == '\n')
607 0 : fputs("\\n", f);
608 0 : else if (*p < ' ')
609 0 : fprintf(f, "\\u%04x", *p);
610 : else
611 0 : fputc(*p, f);
612 :
613 0 : p++;
614 0 : l--;
615 : }
616 :
617 0 : fputc('\"', f);
618 : }
619 0 : }
620 :
621 0 : static int output_json(
622 : FILE *f,
623 : sd_journal *j,
624 : OutputMode mode,
625 : unsigned n_columns,
626 : OutputFlags flags) {
627 :
628 : uint64_t realtime, monotonic;
629 0 : _cleanup_free_ char *cursor = NULL;
630 : const void *data;
631 : size_t length;
632 : sd_id128_t boot_id;
633 : char sid[33], *k;
634 : int r;
635 0 : Hashmap *h = NULL;
636 : bool done, separator;
637 :
638 0 : assert(j);
639 :
640 0 : sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
641 :
642 0 : r = sd_journal_get_realtime_usec(j, &realtime);
643 0 : if (r < 0)
644 0 : return log_error_errno(r, "Failed to get realtime timestamp: %m");
645 :
646 0 : r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
647 0 : if (r < 0)
648 0 : return log_error_errno(r, "Failed to get monotonic timestamp: %m");
649 :
650 0 : r = sd_journal_get_cursor(j, &cursor);
651 0 : if (r < 0)
652 0 : return log_error_errno(r, "Failed to get cursor: %m");
653 :
654 0 : if (mode == OUTPUT_JSON_PRETTY)
655 0 : fprintf(f,
656 : "{\n"
657 : "\t\"__CURSOR\" : \"%s\",\n"
658 : "\t\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\",\n"
659 : "\t\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\",\n"
660 : "\t\"_BOOT_ID\" : \"%s\"",
661 : cursor,
662 : realtime,
663 : monotonic,
664 : sd_id128_to_string(boot_id, sid));
665 : else {
666 0 : if (mode == OUTPUT_JSON_SSE)
667 0 : fputs("data: ", f);
668 :
669 0 : fprintf(f,
670 : "{ \"__CURSOR\" : \"%s\", "
671 : "\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\", "
672 : "\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\", "
673 : "\"_BOOT_ID\" : \"%s\"",
674 : cursor,
675 : realtime,
676 : monotonic,
677 : sd_id128_to_string(boot_id, sid));
678 : }
679 :
680 0 : h = hashmap_new(&string_hash_ops);
681 0 : if (!h)
682 0 : return log_oom();
683 :
684 : /* First round, iterate through the entry and count how often each field appears */
685 0 : JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
686 : const char *eq;
687 : char *n;
688 : unsigned u;
689 :
690 0 : if (length >= 9 &&
691 0 : memcmp(data, "_BOOT_ID=", 9) == 0)
692 0 : continue;
693 :
694 0 : eq = memchr(data, '=', length);
695 0 : if (!eq)
696 0 : continue;
697 :
698 0 : n = strndup(data, eq - (const char*) data);
699 0 : if (!n) {
700 0 : r = log_oom();
701 0 : goto finish;
702 : }
703 :
704 0 : u = PTR_TO_UINT(hashmap_get(h, n));
705 0 : if (u == 0) {
706 0 : r = hashmap_put(h, n, UINT_TO_PTR(1));
707 0 : if (r < 0) {
708 0 : free(n);
709 0 : log_oom();
710 0 : goto finish;
711 : }
712 : } else {
713 0 : r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
714 0 : free(n);
715 0 : if (r < 0) {
716 0 : log_oom();
717 0 : goto finish;
718 : }
719 : }
720 : }
721 :
722 0 : if (r < 0)
723 0 : return r;
724 :
725 0 : separator = true;
726 : do {
727 0 : done = true;
728 :
729 0 : SD_JOURNAL_FOREACH_DATA(j, data, length) {
730 : const char *eq;
731 : char *kk, *n;
732 : size_t m;
733 : unsigned u;
734 :
735 : /* We already printed the boot id, from the data in
736 : * the header, hence let's suppress it here */
737 0 : if (length >= 9 &&
738 0 : memcmp(data, "_BOOT_ID=", 9) == 0)
739 0 : continue;
740 :
741 0 : eq = memchr(data, '=', length);
742 0 : if (!eq)
743 0 : continue;
744 :
745 0 : if (separator) {
746 0 : if (mode == OUTPUT_JSON_PRETTY)
747 0 : fputs(",\n\t", f);
748 : else
749 0 : fputs(", ", f);
750 : }
751 :
752 0 : m = eq - (const char*) data;
753 :
754 0 : n = strndup(data, m);
755 0 : if (!n) {
756 0 : r = log_oom();
757 0 : goto finish;
758 : }
759 :
760 0 : u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
761 0 : if (u == 0) {
762 : /* We already printed this, let's jump to the next */
763 0 : free(n);
764 0 : separator = false;
765 :
766 0 : continue;
767 0 : } else if (u == 1) {
768 : /* Field only appears once, output it directly */
769 :
770 0 : json_escape(f, data, m, flags);
771 0 : fputs(" : ", f);
772 :
773 0 : json_escape(f, eq + 1, length - m - 1, flags);
774 :
775 0 : hashmap_remove(h, n);
776 0 : free(kk);
777 0 : free(n);
778 :
779 0 : separator = true;
780 :
781 0 : continue;
782 :
783 : } else {
784 : /* Field appears multiple times, output it as array */
785 0 : json_escape(f, data, m, flags);
786 0 : fputs(" : [ ", f);
787 0 : json_escape(f, eq + 1, length - m - 1, flags);
788 :
789 : /* Iterate through the end of the list */
790 :
791 0 : while (sd_journal_enumerate_data(j, &data, &length) > 0) {
792 0 : if (length < m + 1)
793 0 : continue;
794 :
795 0 : if (memcmp(data, n, m) != 0)
796 0 : continue;
797 :
798 0 : if (((const char*) data)[m] != '=')
799 0 : continue;
800 :
801 0 : fputs(", ", f);
802 0 : json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
803 : }
804 :
805 0 : fputs(" ]", f);
806 :
807 0 : hashmap_remove(h, n);
808 0 : free(kk);
809 0 : free(n);
810 :
811 : /* Iterate data fields form the beginning */
812 0 : done = false;
813 0 : separator = true;
814 :
815 0 : break;
816 : }
817 : }
818 :
819 0 : } while (!done);
820 :
821 0 : if (mode == OUTPUT_JSON_PRETTY)
822 0 : fputs("\n}\n", f);
823 0 : else if (mode == OUTPUT_JSON_SSE)
824 0 : fputs("}\n\n", f);
825 : else
826 0 : fputs(" }\n", f);
827 :
828 0 : r = 0;
829 :
830 : finish:
831 0 : while ((k = hashmap_steal_first_key(h)))
832 0 : free(k);
833 :
834 0 : hashmap_free(h);
835 :
836 0 : return r;
837 : }
838 :
839 0 : static int output_cat(
840 : FILE *f,
841 : sd_journal *j,
842 : OutputMode mode,
843 : unsigned n_columns,
844 : OutputFlags flags) {
845 :
846 : const void *data;
847 : size_t l;
848 : int r;
849 :
850 0 : assert(j);
851 0 : assert(f);
852 :
853 0 : sd_journal_set_data_threshold(j, 0);
854 :
855 0 : r = sd_journal_get_data(j, "MESSAGE", &data, &l);
856 0 : if (r < 0) {
857 : /* An entry without MESSAGE=? */
858 0 : if (r == -ENOENT)
859 0 : return 0;
860 :
861 0 : return log_error_errno(r, "Failed to get data: %m");
862 : }
863 :
864 0 : assert(l >= 8);
865 :
866 0 : fwrite((const char*) data + 8, 1, l - 8, f);
867 0 : fputc('\n', f);
868 :
869 0 : return 0;
870 : }
871 :
872 : static int (*output_funcs[_OUTPUT_MODE_MAX])(
873 : FILE *f,
874 : sd_journal*j,
875 : OutputMode mode,
876 : unsigned n_columns,
877 : OutputFlags flags) = {
878 :
879 : [OUTPUT_SHORT] = output_short,
880 : [OUTPUT_SHORT_ISO] = output_short,
881 : [OUTPUT_SHORT_PRECISE] = output_short,
882 : [OUTPUT_SHORT_MONOTONIC] = output_short,
883 : [OUTPUT_VERBOSE] = output_verbose,
884 : [OUTPUT_EXPORT] = output_export,
885 : [OUTPUT_JSON] = output_json,
886 : [OUTPUT_JSON_PRETTY] = output_json,
887 : [OUTPUT_JSON_SSE] = output_json,
888 : [OUTPUT_CAT] = output_cat
889 : };
890 :
891 0 : int output_journal(
892 : FILE *f,
893 : sd_journal *j,
894 : OutputMode mode,
895 : unsigned n_columns,
896 : OutputFlags flags,
897 : bool *ellipsized) {
898 :
899 : int ret;
900 0 : assert(mode >= 0);
901 0 : assert(mode < _OUTPUT_MODE_MAX);
902 :
903 0 : if (n_columns <= 0)
904 0 : n_columns = columns();
905 :
906 0 : ret = output_funcs[mode](f, j, mode, n_columns, flags);
907 0 : fflush(stdout);
908 :
909 0 : if (ellipsized && ret > 0)
910 0 : *ellipsized = true;
911 :
912 0 : return ret;
913 : }
914 :
915 0 : static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) {
916 0 : assert(f);
917 0 : assert(flags);
918 :
919 0 : if (!(*flags & OUTPUT_BEGIN_NEWLINE))
920 0 : return 0;
921 :
922 : /* Print a beginning new line if that's request, but only once
923 : * on the first line we print. */
924 :
925 0 : fputc('\n', f);
926 0 : *flags &= ~OUTPUT_BEGIN_NEWLINE;
927 0 : return 0;
928 : }
929 :
930 0 : static int show_journal(FILE *f,
931 : sd_journal *j,
932 : OutputMode mode,
933 : unsigned n_columns,
934 : usec_t not_before,
935 : unsigned how_many,
936 : OutputFlags flags,
937 : bool *ellipsized) {
938 :
939 : int r;
940 0 : unsigned line = 0;
941 0 : bool need_seek = false;
942 0 : int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
943 :
944 0 : assert(j);
945 0 : assert(mode >= 0);
946 0 : assert(mode < _OUTPUT_MODE_MAX);
947 :
948 : /* Seek to end */
949 0 : r = sd_journal_seek_tail(j);
950 0 : if (r < 0)
951 0 : return log_error_errno(r, "Failed to seek to tail: %m");
952 :
953 0 : r = sd_journal_previous_skip(j, how_many);
954 0 : if (r < 0)
955 0 : return log_error_errno(r, "Failed to skip previous: %m");
956 :
957 : for (;;) {
958 : for (;;) {
959 : usec_t usec;
960 :
961 0 : if (need_seek) {
962 0 : r = sd_journal_next(j);
963 0 : if (r < 0)
964 0 : return log_error_errno(r, "Failed to iterate through journal: %m");
965 : }
966 :
967 0 : if (r == 0)
968 0 : break;
969 :
970 0 : need_seek = true;
971 :
972 0 : if (not_before > 0) {
973 0 : r = sd_journal_get_monotonic_usec(j, &usec, NULL);
974 :
975 : /* -ESTALE is returned if the
976 : timestamp is not from this boot */
977 0 : if (r == -ESTALE)
978 0 : continue;
979 0 : else if (r < 0)
980 0 : return log_error_errno(r, "Failed to get journal time: %m");
981 :
982 0 : if (usec < not_before)
983 0 : continue;
984 : }
985 :
986 0 : line ++;
987 0 : maybe_print_begin_newline(f, &flags);
988 :
989 0 : r = output_journal(f, j, mode, n_columns, flags, ellipsized);
990 0 : if (r < 0)
991 0 : return r;
992 0 : }
993 :
994 0 : if (warn_cutoff && line < how_many && not_before > 0) {
995 : sd_id128_t boot_id;
996 0 : usec_t cutoff = 0;
997 :
998 : /* Check whether the cutoff line is too early */
999 :
1000 0 : r = sd_id128_get_boot(&boot_id);
1001 0 : if (r < 0)
1002 0 : return log_error_errno(r, "Failed to get boot id: %m");
1003 :
1004 0 : r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
1005 0 : if (r < 0)
1006 0 : return log_error_errno(r, "Failed to get journal cutoff time: %m");
1007 :
1008 0 : if (r > 0 && not_before < cutoff) {
1009 0 : maybe_print_begin_newline(f, &flags);
1010 0 : fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
1011 : }
1012 :
1013 0 : warn_cutoff = false;
1014 : }
1015 :
1016 0 : if (!(flags & OUTPUT_FOLLOW))
1017 0 : break;
1018 :
1019 0 : r = sd_journal_wait(j, USEC_INFINITY);
1020 0 : if (r < 0)
1021 0 : return log_error_errno(r, "Failed to wait for journal: %m");
1022 :
1023 0 : }
1024 :
1025 0 : return 0;
1026 : }
1027 :
1028 0 : int add_matches_for_unit(sd_journal *j, const char *unit) {
1029 : int r;
1030 : char *m1, *m2, *m3, *m4;
1031 :
1032 0 : assert(j);
1033 0 : assert(unit);
1034 :
1035 0 : m1 = strjoina("_SYSTEMD_UNIT=", unit);
1036 0 : m2 = strjoina("COREDUMP_UNIT=", unit);
1037 0 : m3 = strjoina("UNIT=", unit);
1038 0 : m4 = strjoina("OBJECT_SYSTEMD_UNIT=", unit);
1039 :
1040 0 : (void)(
1041 : /* Look for messages from the service itself */
1042 0 : (r = sd_journal_add_match(j, m1, 0)) ||
1043 :
1044 : /* Look for coredumps of the service */
1045 0 : (r = sd_journal_add_disjunction(j)) ||
1046 0 : (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1047 0 : (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1048 0 : (r = sd_journal_add_match(j, m2, 0)) ||
1049 :
1050 : /* Look for messages from PID 1 about this service */
1051 0 : (r = sd_journal_add_disjunction(j)) ||
1052 0 : (r = sd_journal_add_match(j, "_PID=1", 0)) ||
1053 0 : (r = sd_journal_add_match(j, m3, 0)) ||
1054 :
1055 : /* Look for messages from authorized daemons about this service */
1056 0 : (r = sd_journal_add_disjunction(j)) ||
1057 0 : (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1058 : (r = sd_journal_add_match(j, m4, 0))
1059 : );
1060 :
1061 0 : if (r == 0 && endswith(unit, ".slice")) {
1062 0 : char *m5 = strappend("_SYSTEMD_SLICE=", unit);
1063 :
1064 : /* Show all messages belonging to a slice */
1065 0 : (void)(
1066 0 : (r = sd_journal_add_disjunction(j)) ||
1067 : (r = sd_journal_add_match(j, m5, 0))
1068 : );
1069 : }
1070 :
1071 0 : return r;
1072 : }
1073 :
1074 0 : int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
1075 : int r;
1076 : char *m1, *m2, *m3, *m4;
1077 : char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
1078 :
1079 0 : assert(j);
1080 0 : assert(unit);
1081 :
1082 0 : m1 = strjoina("_SYSTEMD_USER_UNIT=", unit);
1083 0 : m2 = strjoina("USER_UNIT=", unit);
1084 0 : m3 = strjoina("COREDUMP_USER_UNIT=", unit);
1085 0 : m4 = strjoina("OBJECT_SYSTEMD_USER_UNIT=", unit);
1086 0 : sprintf(muid, "_UID="UID_FMT, uid);
1087 :
1088 0 : (void) (
1089 : /* Look for messages from the user service itself */
1090 0 : (r = sd_journal_add_match(j, m1, 0)) ||
1091 0 : (r = sd_journal_add_match(j, muid, 0)) ||
1092 :
1093 : /* Look for messages from systemd about this service */
1094 0 : (r = sd_journal_add_disjunction(j)) ||
1095 0 : (r = sd_journal_add_match(j, m2, 0)) ||
1096 0 : (r = sd_journal_add_match(j, muid, 0)) ||
1097 :
1098 : /* Look for coredumps of the service */
1099 0 : (r = sd_journal_add_disjunction(j)) ||
1100 0 : (r = sd_journal_add_match(j, m3, 0)) ||
1101 0 : (r = sd_journal_add_match(j, muid, 0)) ||
1102 0 : (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1103 :
1104 : /* Look for messages from authorized daemons about this service */
1105 0 : (r = sd_journal_add_disjunction(j)) ||
1106 0 : (r = sd_journal_add_match(j, m4, 0)) ||
1107 0 : (r = sd_journal_add_match(j, muid, 0)) ||
1108 : (r = sd_journal_add_match(j, "_UID=0", 0))
1109 : );
1110 :
1111 0 : if (r == 0 && endswith(unit, ".slice")) {
1112 0 : char *m5 = strappend("_SYSTEMD_SLICE=", unit);
1113 :
1114 : /* Show all messages belonging to a slice */
1115 0 : (void)(
1116 0 : (r = sd_journal_add_disjunction(j)) ||
1117 0 : (r = sd_journal_add_match(j, m5, 0)) ||
1118 : (r = sd_journal_add_match(j, muid, 0))
1119 : );
1120 : }
1121 :
1122 0 : return r;
1123 : }
1124 :
1125 0 : static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
1126 0 : _cleanup_close_pair_ int pair[2] = { -1, -1 };
1127 0 : _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
1128 : pid_t pid, child;
1129 : siginfo_t si;
1130 : char buf[37];
1131 : ssize_t k;
1132 : int r;
1133 :
1134 0 : assert(machine);
1135 0 : assert(boot_id);
1136 :
1137 0 : if (!machine_name_is_valid(machine))
1138 0 : return -EINVAL;
1139 :
1140 0 : r = container_get_leader(machine, &pid);
1141 0 : if (r < 0)
1142 0 : return r;
1143 :
1144 0 : r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd);
1145 0 : if (r < 0)
1146 0 : return r;
1147 :
1148 0 : if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1149 0 : return -errno;
1150 :
1151 0 : child = fork();
1152 0 : if (child < 0)
1153 0 : return -errno;
1154 :
1155 0 : if (child == 0) {
1156 : int fd;
1157 :
1158 0 : pair[0] = safe_close(pair[0]);
1159 :
1160 0 : r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
1161 0 : if (r < 0)
1162 0 : _exit(EXIT_FAILURE);
1163 :
1164 0 : fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
1165 0 : if (fd < 0)
1166 0 : _exit(EXIT_FAILURE);
1167 :
1168 0 : r = loop_read_exact(fd, buf, 36, false);
1169 0 : safe_close(fd);
1170 0 : if (r < 0)
1171 0 : _exit(EXIT_FAILURE);
1172 :
1173 0 : k = send(pair[1], buf, 36, MSG_NOSIGNAL);
1174 0 : if (k != 36)
1175 0 : _exit(EXIT_FAILURE);
1176 :
1177 0 : _exit(EXIT_SUCCESS);
1178 : }
1179 :
1180 0 : pair[1] = safe_close(pair[1]);
1181 :
1182 0 : r = wait_for_terminate(child, &si);
1183 0 : if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
1184 0 : return r < 0 ? r : -EIO;
1185 :
1186 0 : k = recv(pair[0], buf, 36, 0);
1187 0 : if (k != 36)
1188 0 : return -EIO;
1189 :
1190 0 : buf[36] = 0;
1191 0 : r = sd_id128_from_string(buf, boot_id);
1192 0 : if (r < 0)
1193 0 : return r;
1194 :
1195 0 : return 0;
1196 : }
1197 :
1198 0 : int add_match_this_boot(sd_journal *j, const char *machine) {
1199 0 : char match[9+32+1] = "_BOOT_ID=";
1200 : sd_id128_t boot_id;
1201 : int r;
1202 :
1203 0 : assert(j);
1204 :
1205 0 : if (machine) {
1206 0 : r = get_boot_id_for_machine(machine, &boot_id);
1207 0 : if (r < 0)
1208 0 : return log_error_errno(r, "Failed to get boot id of container %s: %m", machine);
1209 : } else {
1210 0 : r = sd_id128_get_boot(&boot_id);
1211 0 : if (r < 0)
1212 0 : return log_error_errno(r, "Failed to get boot id: %m");
1213 : }
1214 :
1215 0 : sd_id128_to_string(boot_id, match + 9);
1216 0 : r = sd_journal_add_match(j, match, strlen(match));
1217 0 : if (r < 0)
1218 0 : return log_error_errno(r, "Failed to add match: %m");
1219 :
1220 0 : r = sd_journal_add_conjunction(j);
1221 0 : if (r < 0)
1222 0 : return log_error_errno(r, "Failed to add conjunction: %m");
1223 :
1224 0 : return 0;
1225 : }
1226 :
1227 0 : int show_journal_by_unit(
1228 : FILE *f,
1229 : const char *unit,
1230 : OutputMode mode,
1231 : unsigned n_columns,
1232 : usec_t not_before,
1233 : unsigned how_many,
1234 : uid_t uid,
1235 : OutputFlags flags,
1236 : int journal_open_flags,
1237 : bool system_unit,
1238 : bool *ellipsized) {
1239 :
1240 0 : _cleanup_journal_close_ sd_journal*j = NULL;
1241 : int r;
1242 :
1243 0 : assert(mode >= 0);
1244 0 : assert(mode < _OUTPUT_MODE_MAX);
1245 0 : assert(unit);
1246 :
1247 0 : if (how_many <= 0)
1248 0 : return 0;
1249 :
1250 0 : r = sd_journal_open(&j, journal_open_flags);
1251 0 : if (r < 0)
1252 0 : return log_error_errno(r, "Failed to open journal: %m");
1253 :
1254 0 : r = add_match_this_boot(j, NULL);
1255 0 : if (r < 0)
1256 0 : return r;
1257 :
1258 0 : if (system_unit)
1259 0 : r = add_matches_for_unit(j, unit);
1260 : else
1261 0 : r = add_matches_for_user_unit(j, unit, uid);
1262 0 : if (r < 0)
1263 0 : return log_error_errno(r, "Failed to add unit matches: %m");
1264 :
1265 0 : if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
1266 0 : _cleanup_free_ char *filter;
1267 :
1268 0 : filter = journal_make_match_string(j);
1269 0 : if (!filter)
1270 0 : return log_oom();
1271 :
1272 0 : log_debug("Journal filter: %s", filter);
1273 : }
1274 :
1275 0 : return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
1276 : }
1277 :
1278 : static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1279 : [OUTPUT_SHORT] = "short",
1280 : [OUTPUT_SHORT_ISO] = "short-iso",
1281 : [OUTPUT_SHORT_PRECISE] = "short-precise",
1282 : [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
1283 : [OUTPUT_VERBOSE] = "verbose",
1284 : [OUTPUT_EXPORT] = "export",
1285 : [OUTPUT_JSON] = "json",
1286 : [OUTPUT_JSON_PRETTY] = "json-pretty",
1287 : [OUTPUT_JSON_SSE] = "json-sse",
1288 : [OUTPUT_CAT] = "cat"
1289 : };
1290 :
1291 24 : DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);
|