chiark / gitweb /
bbfb74c2bdfd87583486907e0309cb8fe929f3c7
[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
35 static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
36         size_t fl, nl;
37         void *buf;
38
39         assert(data);
40         assert(field);
41         assert(target);
42         assert(target_size);
43
44         fl = strlen(field);
45         if (length < fl)
46                 return 0;
47
48         if (memcmp(data, field, fl))
49                 return 0;
50
51         nl = length - fl;
52         buf = malloc(nl+1);
53         memcpy(buf, (const char*) data + fl, nl);
54         ((char*)buf)[nl] = 0;
55         if (!buf) {
56                 log_error("Out of memory");
57                 return -ENOMEM;
58         }
59
60         free(*target);
61         *target = buf;
62         *target_size = nl;
63
64         return 1;
65 }
66
67 static bool shall_print(bool show_all, char *p, size_t l) {
68         if (show_all)
69                 return true;
70
71         if (l > PRINT_THRESHOLD)
72                 return false;
73
74         if (!utf8_is_printable_n(p, l))
75                 return false;
76
77         return true;
78 }
79
80 static int output_short(sd_journal *j, unsigned line, unsigned n_columns,
81                         OutputFlags flags) {
82         int r;
83         const void *data;
84         size_t length;
85         size_t n = 0;
86         char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL;
87         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;
88
89         assert(j);
90
91         SD_JOURNAL_FOREACH_DATA(j, data, length) {
92
93                 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
94                 if (r < 0)
95                         goto finish;
96                 else if (r > 0)
97                         continue;
98
99                 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
100                 if (r < 0)
101                         goto finish;
102                 else if (r > 0)
103                         continue;
104
105                 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
106                 if (r < 0)
107                         goto finish;
108                 else if (r > 0)
109                         continue;
110
111                 r = parse_field(data, length, "_PID=", &pid, &pid_len);
112                 if (r < 0)
113                         goto finish;
114                 else if (r > 0)
115                         continue;
116
117                 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
118                 if (r < 0)
119                         goto finish;
120                 else if (r > 0)
121                         continue;
122
123                 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
124                 if (r < 0)
125                         goto finish;
126                 else if (r > 0)
127                         continue;
128
129                 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
130                 if (r < 0)
131                         goto finish;
132                 else if (r > 0)
133                         continue;
134
135                 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
136                 if (r < 0)
137                         goto finish;
138         }
139
140         if (!message) {
141                 r = 0;
142                 goto finish;
143         }
144
145         if (flags & OUTPUT_MONOTONIC_MODE) {
146                 uint64_t t;
147                 sd_id128_t boot_id;
148
149                 r = -ENOENT;
150
151                 if (monotonic)
152                         r = safe_atou64(monotonic, &t);
153
154                 if (r < 0)
155                         r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
156
157                 if (r < 0) {
158                         log_error("Failed to get monotonic: %s", strerror(-r));
159                         goto finish;
160                 }
161
162                 printf("[%5llu.%06llu]",
163                        (unsigned long long) (t / USEC_PER_SEC),
164                        (unsigned long long) (t % USEC_PER_SEC));
165
166                 n += 1 + 5 + 1 + 6 + 1;
167
168         } else {
169                 char buf[64];
170                 uint64_t x;
171                 time_t t;
172                 struct tm tm;
173
174                 r = -ENOENT;
175
176                 if (realtime)
177                         r = safe_atou64(realtime, &x);
178
179                 if (r < 0)
180                         r = sd_journal_get_realtime_usec(j, &x);
181
182                 if (r < 0) {
183                         log_error("Failed to get realtime: %s", strerror(-r));
184                         goto finish;
185                 }
186
187                 t = (time_t) (x / USEC_PER_SEC);
188                 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
189                         log_error("Failed to format time.");
190                         goto finish;
191                 }
192
193                 fputs(buf, stdout);
194                 n += strlen(buf);
195         }
196
197         if (hostname && shall_print(flags & OUTPUT_SHOW_ALL,
198                                     hostname, hostname_len)) {
199                 printf(" %.*s", (int) hostname_len, hostname);
200                 n += hostname_len + 1;
201         }
202
203         if (identifier && shall_print(flags & OUTPUT_SHOW_ALL,
204                                       identifier, identifier_len)) {
205                 printf(" %.*s", (int) identifier_len, identifier);
206                 n += identifier_len + 1;
207         } else if (comm && shall_print(flags & OUTPUT_SHOW_ALL,
208                                        comm, comm_len)) {
209                 printf(" %.*s", (int) comm_len, comm);
210                 n += comm_len + 1;
211         } else
212                 putchar(' ');
213
214         if (pid && shall_print(flags & OUTPUT_SHOW_ALL, pid, pid_len)) {
215                 printf("[%.*s]", (int) pid_len, pid);
216                 n += pid_len + 2;
217         } else if (fake_pid && shall_print(flags & OUTPUT_SHOW_ALL,
218                                            fake_pid, fake_pid_len)) {
219                 printf("[%.*s]", (int) fake_pid_len, fake_pid);
220                 n += fake_pid_len + 2;
221         }
222
223         if (flags & OUTPUT_SHOW_ALL)
224                 printf(": %.*s\n", (int) message_len, message);
225         else if (!utf8_is_printable_n(message, message_len)) {
226                 char bytes[FORMAT_BYTES_MAX];
227                 printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
228         } else if (message_len + n < n_columns)
229                 printf(": %.*s\n", (int) message_len, message);
230         else if (n < n_columns) {
231                 char *e;
232
233                 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
234
235                 if (!e)
236                         printf(": %.*s\n", (int) message_len, message);
237                 else
238                         printf(": %s\n", e);
239
240                 free(e);
241         } else
242                 fputs("\n", stdout);
243
244         r = 0;
245
246 finish:
247         free(hostname);
248         free(identifier);
249         free(comm);
250         free(pid);
251         free(fake_pid);
252         free(message);
253         free(monotonic);
254         free(realtime);
255
256         return r;
257 }
258
259 static int output_short_realtime(sd_journal *j, unsigned line,
260                                  unsigned n_columns, OutputFlags flags) {
261         return output_short(j, line, n_columns, flags & ~OUTPUT_MONOTONIC_MODE);
262 }
263
264 static int output_short_monotonic(sd_journal *j, unsigned line,
265                                   unsigned n_columns, OutputFlags flags) {
266         return output_short(j, line, n_columns, flags | OUTPUT_MONOTONIC_MODE);
267 }
268
269 static int output_verbose(sd_journal *j, unsigned line,
270                           unsigned n_columns, OutputFlags flags) {
271         const void *data;
272         size_t length;
273         char *cursor;
274         uint64_t realtime;
275         char ts[FORMAT_TIMESTAMP_MAX];
276         int r;
277
278         assert(j);
279
280         r = sd_journal_get_realtime_usec(j, &realtime);
281         if (r < 0) {
282                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
283                 return r;
284         }
285
286         r = sd_journal_get_cursor(j, &cursor);
287         if (r < 0) {
288                 log_error("Failed to get cursor: %s", strerror(-r));
289                 return r;
290         }
291
292         printf("%s [%s]\n",
293                format_timestamp(ts, sizeof(ts), realtime),
294                cursor);
295
296         free(cursor);
297
298         SD_JOURNAL_FOREACH_DATA(j, data, length) {
299                 if (!(flags & OUTPUT_SHOW_ALL) && (length > PRINT_THRESHOLD ||
300                                   !utf8_is_printable_n(data, length))) {
301                         const char *c;
302                         char bytes[FORMAT_BYTES_MAX];
303
304                         c = memchr(data, '=', length);
305                         if (!c) {
306                                 log_error("Invalid field.");
307                                 return -EINVAL;
308                         }
309
310                         printf("\t%.*s=[%s blob data]\n",
311                                (int) (c - (const char*) data),
312                                (const char*) data,
313                                format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
314                 } else
315                         printf("\t%.*s\n", (int) length, (const char*) data);
316         }
317
318         return 0;
319 }
320
321 static int output_export(sd_journal *j, unsigned line,
322                          unsigned n_columns, OutputFlags flags) {
323         sd_id128_t boot_id;
324         char sid[33];
325         int r;
326         usec_t realtime, monotonic;
327         char *cursor;
328         const void *data;
329         size_t length;
330
331         assert(j);
332
333         r = sd_journal_get_realtime_usec(j, &realtime);
334         if (r < 0) {
335                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
336                 return r;
337         }
338
339         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
340         if (r < 0) {
341                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
342                 return r;
343         }
344
345         r = sd_journal_get_cursor(j, &cursor);
346         if (r < 0) {
347                 log_error("Failed to get cursor: %s", strerror(-r));
348                 return r;
349         }
350
351         printf("__CURSOR=%s\n"
352                "__REALTIME_TIMESTAMP=%llu\n"
353                "__MONOTONIC_TIMESTAMP=%llu\n"
354                "_BOOT_ID=%s\n",
355                cursor,
356                (unsigned long long) realtime,
357                (unsigned long long) monotonic,
358                sd_id128_to_string(boot_id, sid));
359
360         free(cursor);
361
362         SD_JOURNAL_FOREACH_DATA(j, data, length) {
363
364                 /* We already printed the boot id, from the data in
365                  * the header, hence let's suppress it here */
366                 if (length >= 9 &&
367                     memcmp(data, "_BOOT_ID=", 9) == 0)
368                         continue;
369
370                 if (!utf8_is_printable_n(data, length)) {
371                         const char *c;
372                         uint64_t le64;
373
374                         c = memchr(data, '=', length);
375                         if (!c) {
376                                 log_error("Invalid field.");
377                                 return -EINVAL;
378                         }
379
380                         fwrite(data, c - (const char*) data, 1, stdout);
381                         fputc('\n', stdout);
382                         le64 = htole64(length - (c - (const char*) data) - 1);
383                         fwrite(&le64, sizeof(le64), 1, stdout);
384                         fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
385                 } else
386                         fwrite(data, length, 1, stdout);
387
388                 fputc('\n', stdout);
389         }
390
391         fputc('\n', stdout);
392
393         return 0;
394 }
395
396 static void json_escape(const char* p, size_t l) {
397         if (!utf8_is_printable_n(p, l)) {
398                 bool not_first = false;
399
400                 fputs("[ ", stdout);
401
402                 while (l > 0) {
403                         if (not_first)
404                                 printf(", %u", (uint8_t) *p);
405                         else {
406                                 not_first = true;
407                                 printf("%u", (uint8_t) *p);
408                         }
409
410                         p++;
411                         l--;
412                 }
413
414                 fputs(" ]", stdout);
415         } else {
416                 fputc('\"', stdout);
417
418                 while (l > 0) {
419                         if (*p == '"' || *p == '\\') {
420                                 fputc('\\', stdout);
421                                 fputc(*p, stdout);
422                         } else
423                                 fputc(*p, stdout);
424
425                         p++;
426                         l--;
427                 }
428
429                 fputc('\"', stdout);
430         }
431 }
432
433 static int output_json(sd_journal *j, unsigned line,
434                        unsigned n_columns, OutputFlags flags) {
435         uint64_t realtime, monotonic;
436         char *cursor;
437         const void *data;
438         size_t length;
439         sd_id128_t boot_id;
440         char sid[33];
441         int r;
442
443         assert(j);
444
445         r = sd_journal_get_realtime_usec(j, &realtime);
446         if (r < 0) {
447                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
448                 return r;
449         }
450
451         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
452         if (r < 0) {
453                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
454                 return r;
455         }
456
457         r = sd_journal_get_cursor(j, &cursor);
458         if (r < 0) {
459                 log_error("Failed to get cursor: %s", strerror(-r));
460                 return r;
461         }
462
463         if (line == 1)
464                 fputc('\n', stdout);
465         else
466                 fputs(",\n", stdout);
467
468         printf("{\n"
469                "\t\"__CURSOR\" : \"%s\",\n"
470                "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
471                "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
472                "\t\"_BOOT_ID\" : \"%s\"",
473                cursor,
474                (unsigned long long) realtime,
475                (unsigned long long) monotonic,
476                sd_id128_to_string(boot_id, sid));
477
478         free(cursor);
479
480         SD_JOURNAL_FOREACH_DATA(j, data, length) {
481                 const char *c;
482
483                 /* We already printed the boot id, from the data in
484                  * the header, hence let's suppress it here */
485                 if (length >= 9 &&
486                     memcmp(data, "_BOOT_ID=", 9) == 0)
487                         continue;
488
489                 c = memchr(data, '=', length);
490                 if (!c) {
491                         log_error("Invalid field.");
492                         return -EINVAL;
493                 }
494
495                 fputs(",\n\t", stdout);
496                 json_escape(data, c - (const char*) data);
497                 fputs(" : ", stdout);
498                 json_escape(c + 1, length - (c - (const char*) data) - 1);
499         }
500
501         fputs("\n}", stdout);
502         fflush(stdout);
503
504         return 0;
505 }
506
507 static int output_cat(sd_journal *j, unsigned line,
508                       unsigned n_columns, OutputFlags flags) {
509         const void *data;
510         size_t l;
511         int r;
512
513         assert(j);
514
515         r = sd_journal_get_data(j, "MESSAGE", &data, &l);
516         if (r < 0) {
517                 log_error("Failed to get data: %s", strerror(-r));
518                 return r;
519         }
520
521         assert(l >= 8);
522
523         fwrite((const char*) data + 8, 1, l - 8, stdout);
524         putchar('\n');
525
526         return 0;
527 }
528
529 static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line,
530                                              unsigned n_columns, OutputFlags flags) = {
531         [OUTPUT_SHORT] = output_short_realtime,
532         [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
533         [OUTPUT_VERBOSE] = output_verbose,
534         [OUTPUT_EXPORT] = output_export,
535         [OUTPUT_JSON] = output_json,
536         [OUTPUT_CAT] = output_cat
537 };
538
539 int output_journal(sd_journal *j, OutputMode mode, unsigned line,
540                    unsigned n_columns, OutputFlags flags) {
541         assert(mode >= 0);
542         assert(mode < _OUTPUT_MODE_MAX);
543
544         if (n_columns <= 0)
545                 n_columns = columns();
546
547         return output_funcs[mode](j, line, n_columns, flags);
548 }
549
550 int show_journal_by_unit(
551                 const char *unit,
552                 OutputMode mode,
553                 unsigned n_columns,
554                 usec_t not_before,
555                 unsigned how_many,
556                 OutputFlags flags) {
557
558         char *m = NULL;
559         sd_journal *j = NULL;
560         int r;
561         unsigned line = 0;
562         bool need_seek = false;
563         int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
564
565         assert(mode >= 0);
566         assert(mode < _OUTPUT_MODE_MAX);
567         assert(unit);
568
569         if (!endswith(unit, ".service") &&
570             !endswith(unit, ".socket") &&
571             !endswith(unit, ".mount") &&
572             !endswith(unit, ".swap"))
573                 return 0;
574
575         if (how_many <= 0)
576                 return 0;
577
578         if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) {
579                 r = -ENOMEM;
580                 goto finish;
581         }
582
583         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
584         if (r < 0)
585                 goto finish;
586
587         r = sd_journal_add_match(j, m, strlen(m));
588         if (r < 0)
589                 goto finish;
590
591         r = sd_journal_seek_tail(j);
592         if (r < 0)
593                 goto finish;
594
595         r = sd_journal_previous_skip(j, how_many);
596         if (r < 0)
597                 goto finish;
598
599         if (mode == OUTPUT_JSON) {
600                 fputc('[', stdout);
601                 fflush(stdout);
602         }
603
604         for (;;) {
605                 for (;;) {
606                         usec_t usec;
607
608                         if (need_seek) {
609                                 r = sd_journal_next(j);
610                                 if (r < 0)
611                                         goto finish;
612                         }
613
614                         if (r == 0)
615                                 break;
616
617                         need_seek = true;
618
619                         if (not_before > 0) {
620                                 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
621
622                                 /* -ESTALE is returned if the
623                                    timestamp is not from this boot */
624                                 if (r == -ESTALE)
625                                         continue;
626                                 else if (r < 0)
627                                         goto finish;
628
629                                 if (usec < not_before)
630                                         continue;
631                         }
632
633                         line ++;
634
635                         r = output_journal(j, mode, line, n_columns, flags);
636                         if (r < 0)
637                                 goto finish;
638                 }
639
640                 if (warn_cutoff && line < how_many && not_before > 0) {
641                         sd_id128_t boot_id;
642                         usec_t cutoff;
643
644                         /* Check whether the cutoff line is too early */
645
646                         r = sd_id128_get_boot(&boot_id);
647                         if (r < 0)
648                                 goto finish;
649
650                         r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
651                         if (r < 0)
652                                 goto finish;
653
654                         if (r > 0 && not_before < cutoff)
655                                 printf("Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
656
657                         warn_cutoff = false;
658                 }
659
660                 if (!(flags & OUTPUT_FOLLOW))
661                         break;
662
663                 r = sd_journal_wait(j, (usec_t) -1);
664                 if (r < 0)
665                         goto finish;
666
667         }
668
669         if (mode == OUTPUT_JSON)
670                 fputs("\n]\n", stdout);
671
672 finish:
673         if (m)
674                 free(m);
675
676         if (j)
677                 sd_journal_close(j);
678
679         return r;
680 }
681
682 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
683         [OUTPUT_SHORT] = "short",
684         [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
685         [OUTPUT_VERBOSE] = "verbose",
686         [OUTPUT_EXPORT] = "export",
687         [OUTPUT_JSON] = "json",
688         [OUTPUT_CAT] = "cat"
689 };
690
691 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);