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