chiark / gitweb /
logs-show: various cleanups
[elogind.git] / src / shared / logs-show.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2012 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <time.h>
23 #include <assert.h>
24 #include <errno.h>
25 #include <sys/poll.h>
26 #include <string.h>
27
28 #include "logs-show.h"
29 #include "log.h"
30 #include "util.h"
31 #include "utf8.h"
32
33 #define PRINT_THRESHOLD 128
34 #define JSON_THRESHOLD 4096
35
36 static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
37         size_t fl, nl;
38         void *buf;
39
40         assert(data);
41         assert(field);
42         assert(target);
43         assert(target_size);
44
45         fl = strlen(field);
46         if (length < fl)
47                 return 0;
48
49         if (memcmp(data, field, fl))
50                 return 0;
51
52         nl = length - fl;
53         buf = malloc(nl+1);
54         if (!buf)
55                 return log_oom();
56
57         memcpy(buf, (const char*) data + fl, nl);
58         ((char*)buf)[nl] = 0;
59
60         free(*target);
61         *target = buf;
62         *target_size = nl;
63
64         return 1;
65 }
66
67 static bool shall_print(const char *p, size_t l, OutputFlags flags) {
68         assert(p);
69
70         if (flags & OUTPUT_SHOW_ALL)
71                 return true;
72
73         if (l > PRINT_THRESHOLD)
74                 return false;
75
76         if (!utf8_is_printable_n(p, l))
77                 return false;
78
79         return true;
80 }
81
82 static int output_short(
83                 FILE *f,
84                 sd_journal *j,
85                 OutputMode mode,
86                 unsigned n_columns,
87                 OutputFlags flags) {
88
89         int r;
90         const void *data;
91         size_t length;
92         size_t n = 0;
93         _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
94         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;
95         int p = LOG_INFO;
96         const char *color_on = "", *color_off = "";
97
98         assert(f);
99         assert(j);
100
101         SD_JOURNAL_FOREACH_DATA(j, data, length) {
102
103                 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
104                 if (r < 0)
105                         return r;
106                 else if (r > 0)
107                         continue;
108
109                 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
110                 if (r < 0)
111                         return r;
112                 else if (r > 0)
113                         continue;
114
115                 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
116                 if (r < 0)
117                         return r;
118                 else if (r > 0)
119                         continue;
120
121                 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
122                 if (r < 0)
123                         return r;
124                 else if (r > 0)
125                         continue;
126
127                 r = parse_field(data, length, "_PID=", &pid, &pid_len);
128                 if (r < 0)
129                         return r;
130                 else if (r > 0)
131                         continue;
132
133                 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
134                 if (r < 0)
135                         return r;
136                 else if (r > 0)
137                         continue;
138
139                 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
140                 if (r < 0)
141                         return r;
142                 else if (r > 0)
143                         continue;
144
145                 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
146                 if (r < 0)
147                         return r;
148                 else if (r > 0)
149                         continue;
150
151                 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
152                 if (r < 0)
153                         return r;
154         }
155
156         if (!message)
157                 return 0;
158
159         if (priority_len == 1 && *priority >= '0' && *priority <= '7')
160                 p = *priority - '0';
161
162         if (mode == OUTPUT_SHORT_MONOTONIC) {
163                 uint64_t t;
164                 sd_id128_t boot_id;
165
166                 r = -ENOENT;
167
168                 if (monotonic)
169                         r = safe_atou64(monotonic, &t);
170
171                 if (r < 0)
172                         r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
173
174                 if (r < 0) {
175                         log_error("Failed to get monotonic: %s", strerror(-r));
176                         return r;
177                 }
178
179                 fprintf(f, "[%5llu.%06llu]",
180                         (unsigned long long) (t / USEC_PER_SEC),
181                         (unsigned long long) (t % USEC_PER_SEC));
182
183                 n += 1 + 5 + 1 + 6 + 1;
184
185         } else {
186                 char buf[64];
187                 uint64_t x;
188                 time_t t;
189                 struct tm tm;
190
191                 r = -ENOENT;
192
193                 if (realtime)
194                         r = safe_atou64(realtime, &x);
195
196                 if (r < 0)
197                         r = sd_journal_get_realtime_usec(j, &x);
198
199                 if (r < 0) {
200                         log_error("Failed to get realtime: %s", strerror(-r));
201                         return r;
202                 }
203
204                 t = (time_t) (x / USEC_PER_SEC);
205                 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
206                         log_error("Failed to format time.");
207                         return r;
208                 }
209
210                 fputs(buf, f);
211                 n += strlen(buf);
212         }
213
214         if (hostname && shall_print(hostname, hostname_len, flags)) {
215                 fprintf(f, " %.*s", (int) hostname_len, hostname);
216                 n += hostname_len + 1;
217         }
218
219         if (identifier && shall_print(identifier, identifier_len, flags)) {
220                 fprintf(f, " %.*s", (int) identifier_len, identifier);
221                 n += identifier_len + 1;
222         } else if (comm && shall_print(comm, comm_len, flags)) {
223                 fprintf(f, " %.*s", (int) comm_len, comm);
224                 n += comm_len + 1;
225         } else
226                 fputc(' ', f);
227
228         if (pid && shall_print(pid, pid_len, flags)) {
229                 fprintf(f, "[%.*s]", (int) pid_len, pid);
230                 n += pid_len + 2;
231         } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
232                 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
233                 n += fake_pid_len + 2;
234         }
235
236         if (flags & OUTPUT_COLOR) {
237                 if (p <= LOG_ERR) {
238                         color_on = ANSI_HIGHLIGHT_RED_ON;
239                         color_off = ANSI_HIGHLIGHT_OFF;
240                 } else if (p <= LOG_NOTICE) {
241                         color_on = ANSI_HIGHLIGHT_ON;
242                         color_off = ANSI_HIGHLIGHT_OFF;
243                 }
244         }
245
246         if (flags)
247                 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
248         else if (!utf8_is_printable_n(message, message_len)) {
249                 char bytes[FORMAT_BYTES_MAX];
250                 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
251         } else if ((flags & OUTPUT_FULL_WIDTH) || (message_len + n + 1 < n_columns))
252                 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
253         else if (n < n_columns && n_columns - n - 2 >= 3) {
254                 char *e;
255
256                 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
257
258                 if (!e)
259                         fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
260                 else
261                         fprintf(f, ": %s%s%s\n", color_on, e, color_off);
262
263                 free(e);
264         } else
265                 fputs("\n", f);
266
267         return 0;
268 }
269
270 static int output_verbose(
271                 FILE *f,
272                 sd_journal *j,
273                 OutputMode mode,
274                 unsigned n_columns,
275                 OutputFlags flags) {
276
277         const void *data;
278         size_t length;
279         char *cursor;
280         uint64_t realtime;
281         char ts[FORMAT_TIMESTAMP_MAX];
282         int r;
283
284         assert(f);
285         assert(j);
286
287         r = sd_journal_get_realtime_usec(j, &realtime);
288         if (r < 0) {
289                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
290                 return r;
291         }
292
293         r = sd_journal_get_cursor(j, &cursor);
294         if (r < 0) {
295                 log_error("Failed to get cursor: %s", strerror(-r));
296                 return r;
297         }
298
299         fprintf(f, "%s [%s]\n",
300                 format_timestamp(ts, sizeof(ts), realtime),
301                 cursor);
302
303         free(cursor);
304
305         SD_JOURNAL_FOREACH_DATA(j, data, length) {
306                 if (!shall_print(data, length, flags)) {
307                         const char *c;
308                         char bytes[FORMAT_BYTES_MAX];
309
310                         c = memchr(data, '=', length);
311                         if (!c) {
312                                 log_error("Invalid field.");
313                                 return -EINVAL;
314                         }
315
316                         fprintf(f, "\t%.*s=[%s blob data]\n",
317                                (int) (c - (const char*) data),
318                                (const char*) data,
319                                format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
320                 } else
321                         fprintf(f, "\t%.*s\n", (int) length, (const char*) data);
322         }
323
324         return 0;
325 }
326
327 static int output_export(
328                 FILE *f,
329                 sd_journal *j,
330                 OutputMode mode,
331                 unsigned n_columns,
332                 OutputFlags flags) {
333
334         sd_id128_t boot_id;
335         char sid[33];
336         int r;
337         usec_t realtime, monotonic;
338         char *cursor;
339         const void *data;
340         size_t length;
341
342         assert(j);
343
344         r = sd_journal_get_realtime_usec(j, &realtime);
345         if (r < 0) {
346                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
347                 return r;
348         }
349
350         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
351         if (r < 0) {
352                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
353                 return r;
354         }
355
356         r = sd_journal_get_cursor(j, &cursor);
357         if (r < 0) {
358                 log_error("Failed to get cursor: %s", strerror(-r));
359                 return r;
360         }
361
362         fprintf(f,
363                 "__CURSOR=%s\n"
364                 "__REALTIME_TIMESTAMP=%llu\n"
365                 "__MONOTONIC_TIMESTAMP=%llu\n"
366                 "_BOOT_ID=%s\n",
367                 cursor,
368                 (unsigned long long) realtime,
369                 (unsigned long long) monotonic,
370                 sd_id128_to_string(boot_id, sid));
371
372         free(cursor);
373
374         SD_JOURNAL_FOREACH_DATA(j, data, length) {
375
376                 /* We already printed the boot id, from the data in
377                  * the header, hence let's suppress it here */
378                 if (length >= 9 &&
379                     memcmp(data, "_BOOT_ID=", 9) == 0)
380                         continue;
381
382                 if (!utf8_is_printable_n(data, length)) {
383                         const char *c;
384                         uint64_t le64;
385
386                         c = memchr(data, '=', length);
387                         if (!c) {
388                                 log_error("Invalid field.");
389                                 return -EINVAL;
390                         }
391
392                         fwrite(data, c - (const char*) data, 1, f);
393                         fputc('\n', f);
394                         le64 = htole64(length - (c - (const char*) data) - 1);
395                         fwrite(&le64, sizeof(le64), 1, f);
396                         fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
397                 } else
398                         fwrite(data, length, 1, f);
399
400                 fputc('\n', f);
401         }
402
403         fputc('\n', f);
404
405         return 0;
406 }
407
408 static void json_escape(
409                 FILE *f,
410                 const char* p,
411                 size_t l,
412                 OutputFlags flags) {
413
414         assert(f);
415         assert(p);
416
417         if (!(flags & OUTPUT_SHOW_ALL) && l > JSON_THRESHOLD)
418
419                 fputs("null", f);
420
421         else if (!utf8_is_printable_n(p, l)) {
422                 bool not_first = false;
423
424                 fputs("[ ", f);
425
426                 while (l > 0) {
427                         if (not_first)
428                                 fprintf(f, ", %u", (uint8_t) *p);
429                         else {
430                                 not_first = true;
431                                 fprintf(f, "%u", (uint8_t) *p);
432                         }
433
434                         p++;
435                         l--;
436                 }
437
438                 fputs(" ]", f);
439         } else {
440                 fputc('\"', f);
441
442                 while (l > 0) {
443                         if (*p == '"' || *p == '\\') {
444                                 fputc('\\', f);
445                                 fputc(*p, f);
446                         } else if (*p < ' ')
447                                 fprintf(f, "\\u%04x", *p);
448                         else
449                                 fputc(*p, f);
450
451                         p++;
452                         l--;
453                 }
454
455                 fputc('\"', f);
456         }
457 }
458
459 static int output_json(
460                 FILE *f,
461                 sd_journal *j,
462                 OutputMode mode,
463                 unsigned n_columns,
464                 OutputFlags flags) {
465
466         uint64_t realtime, monotonic;
467         char *cursor;
468         const void *data;
469         size_t length;
470         sd_id128_t boot_id;
471         char sid[33];
472         int r;
473
474         assert(j);
475
476         r = sd_journal_get_realtime_usec(j, &realtime);
477         if (r < 0) {
478                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
479                 return r;
480         }
481
482         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
483         if (r < 0) {
484                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
485                 return r;
486         }
487
488         r = sd_journal_get_cursor(j, &cursor);
489         if (r < 0) {
490                 log_error("Failed to get cursor: %s", strerror(-r));
491                 return r;
492         }
493
494         if (mode == OUTPUT_JSON_PRETTY)
495                 fprintf(f,
496                         "{\n"
497                         "\t\"__CURSOR\" : \"%s\",\n"
498                         "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
499                         "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
500                         "\t\"_BOOT_ID\" : \"%s\"",
501                         cursor,
502                         (unsigned long long) realtime,
503                         (unsigned long long) monotonic,
504                         sd_id128_to_string(boot_id, sid));
505         else
506                 fprintf(f,
507                         "{ \"__CURSOR\" : \"%s\", "
508                         "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
509                         "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
510                         "\"_BOOT_ID\" : \"%s\"",
511                         cursor,
512                         (unsigned long long) realtime,
513                         (unsigned long long) monotonic,
514                         sd_id128_to_string(boot_id, sid));
515         free(cursor);
516
517         SD_JOURNAL_FOREACH_DATA(j, data, length) {
518                 const char *c;
519
520                 /* We already printed the boot id, from the data in
521                  * the header, hence let's suppress it here */
522                 if (length >= 9 &&
523                     memcmp(data, "_BOOT_ID=", 9) == 0)
524                         continue;
525
526                 c = memchr(data, '=', length);
527                 if (!c) {
528                         log_error("Invalid field.");
529                         return -EINVAL;
530                 }
531
532                 if (mode == OUTPUT_JSON_PRETTY)
533                         fputs(",\n\t", f);
534                 else
535                         fputs(", ", f);
536
537                 json_escape(f, data, c - (const char*) data, flags);
538                 fputs(" : ", f);
539                 json_escape(f, c + 1, length - (c - (const char*) data) - 1, flags);
540         }
541
542         if (mode == OUTPUT_JSON_PRETTY)
543                 fputs("\n}\n", f);
544         else
545                 fputs(" }\n", f);
546
547         return 0;
548 }
549
550 static int output_cat(
551                 FILE *f,
552                 sd_journal *j,
553                 OutputMode mode,
554                 unsigned n_columns,
555                 OutputFlags flags) {
556
557         const void *data;
558         size_t l;
559         int r;
560
561         assert(j);
562         assert(f);
563
564         r = sd_journal_get_data(j, "MESSAGE", &data, &l);
565         if (r < 0) {
566                 /* An entry without MESSAGE=? */
567                 if (r == -ENOENT)
568                         return 0;
569
570                 log_error("Failed to get data: %s", strerror(-r));
571                 return r;
572         }
573
574         assert(l >= 8);
575
576         fwrite((const char*) data + 8, 1, l - 8, f);
577         fputc('\n', f);
578
579         return 0;
580 }
581
582 static int (*output_funcs[_OUTPUT_MODE_MAX])(
583                 FILE *f,
584                 sd_journal*j,
585                 OutputMode mode,
586                 unsigned n_columns,
587                 OutputFlags flags) = {
588
589         [OUTPUT_SHORT] = output_short,
590         [OUTPUT_SHORT_MONOTONIC] = output_short,
591         [OUTPUT_VERBOSE] = output_verbose,
592         [OUTPUT_EXPORT] = output_export,
593         [OUTPUT_JSON] = output_json,
594         [OUTPUT_JSON_PRETTY] = output_json,
595         [OUTPUT_CAT] = output_cat
596 };
597
598 int output_journal(
599                 FILE *f,
600                 sd_journal *j,
601                 OutputMode mode,
602                 unsigned n_columns,
603                 OutputFlags flags) {
604
605         int ret;
606         assert(mode >= 0);
607         assert(mode < _OUTPUT_MODE_MAX);
608
609         if (n_columns <= 0)
610                 n_columns = columns();
611
612         ret = output_funcs[mode](f, j, mode, n_columns, flags);
613         fflush(stdout);
614         return ret;
615 }
616
617 int show_journal_by_unit(
618                 FILE *f,
619                 const char *unit,
620                 OutputMode mode,
621                 unsigned n_columns,
622                 usec_t not_before,
623                 unsigned how_many,
624                 OutputFlags flags) {
625
626         _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
627         sd_journal *j = NULL;
628         int r;
629         unsigned line = 0;
630         bool need_seek = false;
631         int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
632
633         assert(mode >= 0);
634         assert(mode < _OUTPUT_MODE_MAX);
635         assert(unit);
636
637         if (!endswith(unit, ".service") &&
638             !endswith(unit, ".socket") &&
639             !endswith(unit, ".mount") &&
640             !endswith(unit, ".swap"))
641                 return 0;
642
643         if (how_many <= 0)
644                 return 0;
645
646         if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
647             asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
648             asprintf(&m3, "UNIT=%s", unit) < 0) {
649                 r = -ENOMEM;
650                 goto finish;
651         }
652
653         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
654         if (r < 0)
655                 goto finish;
656
657         /* Look for messages from the service itself */
658         r = sd_journal_add_match(j, m1, 0);
659         if (r < 0)
660                 goto finish;
661
662         /* Look for coredumps of the service */
663         r = sd_journal_add_disjunction(j);
664         if (r < 0)
665                 goto finish;
666         r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0);
667         if (r < 0)
668                 goto finish;
669         r = sd_journal_add_match(j, m2, 0);
670         if (r < 0)
671                 goto finish;
672
673         /* Look for messages from PID 1 about this service */
674         r = sd_journal_add_disjunction(j);
675         if (r < 0)
676                 goto finish;
677         r = sd_journal_add_match(j, "_PID=1", 0);
678         if (r < 0)
679                 goto finish;
680         r = sd_journal_add_match(j, m3, 0);
681         if (r < 0)
682                 goto finish;
683
684         /* Seek to end */
685         r = sd_journal_seek_tail(j);
686         if (r < 0)
687                 goto finish;
688
689         r = sd_journal_previous_skip(j, how_many);
690         if (r < 0)
691                 goto finish;
692
693         for (;;) {
694                 for (;;) {
695                         usec_t usec;
696
697                         if (need_seek) {
698                                 r = sd_journal_next(j);
699                                 if (r < 0)
700                                         goto finish;
701                         }
702
703                         if (r == 0)
704                                 break;
705
706                         need_seek = true;
707
708                         if (not_before > 0) {
709                                 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
710
711                                 /* -ESTALE is returned if the
712                                    timestamp is not from this boot */
713                                 if (r == -ESTALE)
714                                         continue;
715                                 else if (r < 0)
716                                         goto finish;
717
718                                 if (usec < not_before)
719                                         continue;
720                         }
721
722                         line ++;
723
724                         r = output_journal(f, j, mode, n_columns, flags);
725                         if (r < 0)
726                                 goto finish;
727                 }
728
729                 if (warn_cutoff && line < how_many && not_before > 0) {
730                         sd_id128_t boot_id;
731                         usec_t cutoff;
732
733                         /* Check whether the cutoff line is too early */
734
735                         r = sd_id128_get_boot(&boot_id);
736                         if (r < 0)
737                                 goto finish;
738
739                         r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
740                         if (r < 0)
741                                 goto finish;
742
743                         if (r > 0 && not_before < cutoff)
744                                 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
745
746                         warn_cutoff = false;
747                 }
748
749                 if (!(flags & OUTPUT_FOLLOW))
750                         break;
751
752                 r = sd_journal_wait(j, (usec_t) -1);
753                 if (r < 0)
754                         goto finish;
755
756         }
757
758 finish:
759         if (j)
760                 sd_journal_close(j);
761
762         return r;
763 }
764
765 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
766         [OUTPUT_SHORT] = "short",
767         [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
768         [OUTPUT_VERBOSE] = "verbose",
769         [OUTPUT_EXPORT] = "export",
770         [OUTPUT_JSON] = "json",
771         [OUTPUT_JSON_PRETTY] = "json-pretty",
772         [OUTPUT_CAT] = "cat"
773 };
774
775 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);