chiark / gitweb /
5a1cb6e88a7ab8169d985f1aba913634fa67ec96
[elogind.git] / src / journal / journalctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 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 <fcntl.h>
23 #include <errno.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <sys/poll.h>
30 #include <time.h>
31 #include <getopt.h>
32
33 #include "sd-journal.h"
34 #include "log.h"
35 #include "util.h"
36 #include "build.h"
37 #include "pager.h"
38
39 #define PRINT_THRESHOLD 128
40
41 static enum {
42         OUTPUT_SHORT,
43         OUTPUT_VERBOSE,
44         OUTPUT_EXPORT,
45         OUTPUT_JSON,
46         _OUTPUT_MAX
47 } arg_output = OUTPUT_SHORT;
48
49 static bool arg_follow = false;
50 static bool arg_show_all = false;
51 static bool arg_no_pager = false;
52
53 static bool contains_unprintable(const void *p, size_t l) {
54         const char *j;
55
56         for (j = p; j < (const char *) p + l; j++)
57                 if (*j < ' ' || *j >= 127)
58                         return true;
59
60         return false;
61 }
62
63 static int output_short(sd_journal *j, unsigned line) {
64         int r;
65         uint64_t realtime;
66         time_t t;
67         struct tm tm;
68         char buf[64];
69         const void *data;
70         size_t length;
71         size_t n = 0;
72
73         assert(j);
74
75         r = sd_journal_get_realtime_usec(j, &realtime);
76         if (r < 0) {
77                 log_error("Failed to get realtime: %s", strerror(-r));
78                 return r;
79         }
80
81         t = (time_t) (realtime / USEC_PER_SEC);
82         if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
83                 log_error("Failed to format time.");
84                 return -EINVAL;
85         }
86
87         fputs(buf, stdout);
88         n += strlen(buf);
89
90         if (sd_journal_get_data(j, "_HOSTNAME", &data, &length) >= 0 &&
91             (arg_show_all || (!contains_unprintable(data, length) &&
92                               length < PRINT_THRESHOLD))) {
93                 printf(" %.*s", (int) length - 10, ((const char*) data) + 10);
94                 n += length - 10 + 1;
95         }
96
97         if (sd_journal_get_data(j, "MESSAGE", &data, &length) >= 0) {
98                 if (arg_show_all)
99                         printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
100                 else if (contains_unprintable(data, length))
101                         fputs(" [blob data]", stdout);
102                 else if (length - 8 + n < columns())
103                         printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
104                 else if (n < columns()) {
105                         char *e;
106
107                         e = ellipsize_mem((const char *) data + 8, length - 8, columns() - n - 2, 90);
108
109                         if (!e)
110                                 printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
111                         else
112                                 printf(" %s", e);
113
114                         free(e);
115                 }
116         }
117
118         fputc('\n', stdout);
119
120         return 0;
121 }
122
123 static int output_verbose(sd_journal *j, unsigned line) {
124         const void *data;
125         size_t length;
126         char *cursor;
127         uint64_t realtime;
128         char ts[FORMAT_TIMESTAMP_MAX];
129         int r;
130
131         assert(j);
132
133         r = sd_journal_get_realtime_usec(j, &realtime);
134         if (r < 0) {
135                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
136                 return r;
137         }
138
139         r = sd_journal_get_cursor(j, &cursor);
140         if (r < 0) {
141                 log_error("Failed to get cursor: %s", strerror(-r));
142                 return r;
143         }
144
145         printf("%s [%s]\n",
146                format_timestamp(ts, sizeof(ts), realtime),
147                cursor);
148
149         free(cursor);
150
151         SD_JOURNAL_FOREACH_DATA(j, data, length) {
152                 if (!arg_show_all && (length > PRINT_THRESHOLD ||
153                                       contains_unprintable(data, length))) {
154                         const char *c;
155
156                         c = memchr(data, '=', length);
157                         if (!c) {
158                                 log_error("Invalid field.");
159                                 return -EINVAL;
160                         }
161
162                         printf("\t%.*s=[blob data]\n",
163                                (int) (c - (const char*) data),
164                                (const char*) data);
165                 } else
166                         printf("\t%.*s\n", (int) length, (const char*) data);
167         }
168
169         return 0;
170 }
171
172 static int output_export(sd_journal *j, unsigned line) {
173         sd_id128_t boot_id;
174         char sid[33];
175         int r;
176         usec_t realtime, monotonic;
177         char *cursor;
178         const void *data;
179         size_t length;
180
181         assert(j);
182
183         r = sd_journal_get_realtime_usec(j, &realtime);
184         if (r < 0) {
185                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
186                 return r;
187         }
188
189         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
190         if (r < 0) {
191                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
192                 return r;
193         }
194
195         r = sd_journal_get_cursor(j, &cursor);
196         if (r < 0) {
197                 log_error("Failed to get cursor: %s", strerror(-r));
198                 return r;
199         }
200
201         printf(".cursor=%s\n"
202                ".realtime=%llu\n"
203                ".monotonic=%llu\n"
204                ".boot_id=%s\n",
205                cursor,
206                (unsigned long long) realtime,
207                (unsigned long long) monotonic,
208                sd_id128_to_string(boot_id, sid));
209
210         free(cursor);
211
212         SD_JOURNAL_FOREACH_DATA(j, data, length) {
213
214                 if (contains_unprintable(data, length)) {
215                         const char *c;
216                         uint64_t le64;
217
218                         c = memchr(data, '=', length);
219                         if (!c) {
220                                 log_error("Invalid field.");
221                                 return -EINVAL;
222                         }
223
224                         fwrite(data, c - (const char*) data, 1, stdout);
225                         fputc('\n', stdout);
226                         le64 = htole64(length - (c - (const char*) data) - 1);
227                         fwrite(&le64, sizeof(le64), 1, stdout);
228                         fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
229                 } else
230                         fwrite(data, length, 1, stdout);
231
232                 fputc('\n', stdout);
233         }
234
235         fputc('\n', stdout);
236
237         return 0;
238 }
239
240 static void json_escape(const char* p, size_t l) {
241
242         if (contains_unprintable(p, l)) {
243                 bool not_first = false;
244
245                 fputs("[ ", stdout);
246
247                 while (l > 0) {
248                         if (not_first)
249                                 printf(", %u", (uint8_t) *p);
250                         else {
251                                 not_first = true;
252                                 printf("%u", (uint8_t) *p);
253                         }
254
255                         p++;
256                         l--;
257                 }
258
259                 fputs(" ]", stdout);
260         } else {
261                 fputc('\"', stdout);
262
263                 while (l > 0) {
264                         if (*p == '"' || *p == '\\') {
265                                 fputc('\\', stdout);
266                                 fputc(*p, stdout);
267                         } else
268                                 fputc(*p, stdout);
269
270                         p++;
271                         l--;
272                 }
273
274                 fputc('\"', stdout);
275         }
276 }
277
278 static int output_json(sd_journal *j, unsigned line) {
279         uint64_t realtime, monotonic;
280         char *cursor;
281         const void *data;
282         size_t length;
283         sd_id128_t boot_id;
284         char sid[33];
285         int r;
286
287         assert(j);
288
289         r = sd_journal_get_realtime_usec(j, &realtime);
290         if (r < 0) {
291                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
292                 return r;
293         }
294
295         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
296         if (r < 0) {
297                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
298                 return r;
299         }
300
301         r = sd_journal_get_cursor(j, &cursor);
302         if (r < 0) {
303                 log_error("Failed to get cursor: %s", strerror(-r));
304                 return r;
305         }
306
307         if (line == 1)
308                 fputc('\n', stdout);
309         else
310                 fputs(",\n", stdout);
311
312         printf("{\n"
313                "\t\".cursor\" : \"%s\",\n"
314                "\t\".realtime\" : %llu,\n"
315                "\t\".monotonic\" : %llu,\n"
316                "\t\".boot_id\" : \"%s\"",
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                 const char *c;
326
327                 c = memchr(data, '=', length);
328                 if (!c) {
329                         log_error("Invalid field.");
330                         return -EINVAL;
331                 }
332
333                 fputs(",\n\t", stdout);
334                 json_escape(data, c - (const char*) data);
335                 fputs(" : ", stdout);
336                 json_escape(c + 1, length - (c - (const char*) data) - 1);
337         }
338
339         fputs("\n}", stdout);
340         fflush(stdout);
341
342         return 0;
343 }
344
345 static int (*output_funcs[_OUTPUT_MAX])(sd_journal*j, unsigned line) = {
346         [OUTPUT_SHORT] = output_short,
347         [OUTPUT_VERBOSE] = output_verbose,
348         [OUTPUT_EXPORT] = output_export,
349         [OUTPUT_JSON] = output_json
350 };
351
352 static int help(void) {
353
354         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
355                "Send control commands to or query the login manager.\n\n"
356                "  -h --help           Show this help\n"
357                "     --version        Show package version\n"
358                "     --no-pager       Do not pipe output into a pager\n"
359                "  -a --all            Show all properties, including long and unprintable\n"
360                "  -f --follow         Follow journal\n"
361                "  -o --output=STRING  Change output mode (short, verbose, export, json)\n",
362                program_invocation_short_name);
363
364         return 0;
365 }
366
367 static int parse_argv(int argc, char *argv[]) {
368
369         enum {
370                 ARG_VERSION = 0x100,
371                 ARG_NO_PAGER
372         };
373
374         static const struct option options[] = {
375                 { "help",      no_argument,       NULL, 'h'           },
376                 { "version" ,  no_argument,       NULL, ARG_VERSION   },
377                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
378                 { "follow",    no_argument,       NULL, 'f'           },
379                 { "output",    required_argument, NULL, 'o'           },
380                 { "all",       no_argument,       NULL, 'a'           },
381                 { NULL,        0,                 NULL, 0             }
382         };
383
384         int c;
385
386         assert(argc >= 0);
387         assert(argv);
388
389         while ((c = getopt_long(argc, argv, "hfo:a", options, NULL)) >= 0) {
390
391                 switch (c) {
392
393                 case 'h':
394                         help();
395                         return 0;
396
397                 case ARG_VERSION:
398                         puts(PACKAGE_STRING);
399                         puts(DISTRIBUTION);
400                         puts(SYSTEMD_FEATURES);
401                         return 0;
402
403                 case ARG_NO_PAGER:
404                         arg_no_pager = true;
405                         break;
406
407                 case 'f':
408                         arg_follow = true;
409                         break;
410
411                 case 'o':
412                         if (streq(optarg, "short"))
413                                 arg_output = OUTPUT_SHORT;
414                         else if (streq(optarg, "verbose"))
415                                 arg_output = OUTPUT_VERBOSE;
416                         else if (streq(optarg, "export"))
417                                 arg_output = OUTPUT_EXPORT;
418                         else if (streq(optarg, "json"))
419                                 arg_output = OUTPUT_JSON;
420                         else {
421                                 log_error("Unknown output '%s'.", optarg);
422                                 return -EINVAL;
423                         }
424                         break;
425
426                 case 'a':
427                         arg_show_all = true;
428                         break;
429
430                 case '?':
431                         return -EINVAL;
432
433                 default:
434                         log_error("Unknown option code %c", c);
435                         return -EINVAL;
436                 }
437         }
438
439         return 1;
440 }
441
442 int main(int argc, char *argv[]) {
443         int r, i, fd;
444         sd_journal *j = NULL;
445         unsigned line = 0;
446
447         log_set_max_level(LOG_DEBUG);
448         log_set_target(LOG_TARGET_CONSOLE);
449
450         log_parse_environment();
451         log_open();
452
453         r = parse_argv(argc, argv);
454         if (r <= 0)
455                 goto finish;
456
457         r = sd_journal_open(&j);
458         if (r < 0) {
459                 log_error("Failed to open journal: %s", strerror(-r));
460                 goto finish;
461         }
462
463         for (i = optind; i < argc; i++) {
464                 r = sd_journal_add_match(j, argv[i], strlen(argv[i]));
465                 if (r < 0) {
466                         log_error("Failed to add match: %s", strerror(-r));
467                         goto finish;
468                 }
469         }
470
471         fd = sd_journal_get_fd(j);
472         if (fd < 0) {
473                 log_error("Failed to get wakeup fd: %s", strerror(-fd));
474                 goto finish;
475         }
476
477         r = sd_journal_seek_head(j);
478         if (r < 0) {
479                 log_error("Failed to seek to head: %s", strerror(-r));
480                 goto finish;
481         }
482
483         if (!arg_no_pager && !arg_follow) {
484                 columns();
485                 pager_open();
486         }
487
488         if (arg_output == OUTPUT_JSON) {
489                 fputc('[', stdout);
490                 fflush(stdout);
491         }
492
493         for (;;) {
494                 struct pollfd pollfd;
495
496                 for (;;) {
497                         r = sd_journal_next(j);
498
499                         if (r < 0) {
500                                 log_error("Failed to iterate through journal: %s", strerror(-r));
501                                 goto finish;
502                         }
503
504                         if (r == 0)
505                                 break;
506
507                         line ++;
508
509                         r = output_funcs[arg_output](j, line);
510                         if (r < 0)
511                                 goto finish;
512                 }
513
514                 if (!arg_follow)
515                         break;
516
517                 zero(pollfd);
518                 pollfd.fd = fd;
519                 pollfd.events = POLLIN;
520
521                 if (poll(&pollfd, 1, -1) < 0) {
522                         if (errno == EINTR)
523                                 break;
524
525                         log_error("poll(): %m");
526                         r = -errno;
527                         goto finish;
528                 }
529
530                 r = sd_journal_process(j);
531                 if (r < 0) {
532                         log_error("Failed to process: %s", strerror(-r));
533                         goto finish;
534                 }
535         }
536
537         if (arg_output == OUTPUT_JSON)
538                 fputs("\n]\n", stdout);
539
540 finish:
541         if (j)
542                 sd_journal_close(j);
543
544         pager_close();
545
546         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
547 }