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