chiark / gitweb /
6f4342e59725c24a40198bf9137f671092450f06
[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
32 #include "sd-journal.h"
33 #include "log.h"
34 #include "util.h"
35
36 #define PRINT_THRESHOLD 128
37
38 static enum {
39         OUTPUT_SHORT,
40         OUTPUT_VERBOSE,
41         OUTPUT_EXPORT,
42         OUTPUT_JSON,
43         _OUTPUT_MAX
44 } arg_output = OUTPUT_JSON;
45
46 static bool arg_follow = false;
47 static bool arg_show_all = false;
48
49 static bool contains_unprintable(const void *p, size_t l) {
50         const char *j;
51
52         for (j = p; j < (const char *) p + l; j++)
53                 if (*j < ' ' || *j >= 127)
54                         return true;
55
56         return false;
57 }
58
59 static int output_short(sd_journal *j, unsigned line) {
60         int r;
61         uint64_t realtime;
62         time_t t;
63         struct tm tm;
64         char buf[64];
65         const void *data;
66         size_t length;
67         size_t n = 0;
68
69         assert(j);
70
71         r = sd_journal_get_realtime_usec(j, &realtime);
72         if (r < 0) {
73                 log_error("Failed to get realtime: %s", strerror(-r));
74                 return r;
75         }
76
77         t = (time_t) (realtime / USEC_PER_SEC);
78         if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
79                 log_error("Failed to format time.");
80                 return -EINVAL;
81         }
82
83         fputs(buf, stdout);
84         n += strlen(buf);
85
86         if (sd_journal_get_data(j, "_HOSTNAME", &data, &length) >= 0 &&
87             (arg_show_all || (!contains_unprintable(data, length) &&
88                               length < PRINT_THRESHOLD))) {
89                 printf(" %.*s", (int) length - 10, ((const char*) data) + 10);
90                 n += length - 10 + 1;
91         }
92
93         if (sd_journal_get_data(j, "MESSAGE", &data, &length) >= 0) {
94                 if (arg_show_all)
95                         printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
96                 else if (contains_unprintable(data, length))
97                         fputs(" [blob data]", stdout);
98                 else if (length - 8 + n < columns())
99                         printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
100                 else if (n < columns()) {
101                         char *e;
102
103                         e = ellipsize_mem((const char *) data + 8, length - 8, columns() - n - 2, 90);
104
105                         if (!e)
106                                 printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
107                         else
108                                 printf(" %s", e);
109
110                         free(e);
111                 }
112         }
113
114         fputc('\n', stdout);
115
116         return 0;
117 }
118
119 static int output_verbose(sd_journal *j, unsigned line) {
120         const void *data;
121         size_t length;
122         char *cursor;
123         uint64_t realtime;
124         char ts[FORMAT_TIMESTAMP_MAX];
125         int r;
126
127         assert(j);
128
129         r = sd_journal_get_realtime_usec(j, &realtime);
130         if (r < 0) {
131                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
132                 return r;
133         }
134
135         r = sd_journal_get_cursor(j, &cursor);
136         if (r < 0) {
137                 log_error("Failed to get cursor: %s", strerror(-r));
138                 return r;
139         }
140
141         printf("%s [%s]\n",
142                format_timestamp(ts, sizeof(ts), realtime),
143                cursor);
144
145         free(cursor);
146
147         SD_JOURNAL_FOREACH_DATA(j, data, length) {
148                 if (!arg_show_all && (length > PRINT_THRESHOLD ||
149                                       contains_unprintable(data, length))) {
150                         const char *c;
151
152                         c = memchr(data, '=', length);
153                         if (!c) {
154                                 log_error("Invalid field.");
155                                 return -EINVAL;
156                         }
157
158                         printf("\t%.*s=[blob data]\n",
159                                (int) (c - (const char*) data),
160                                (const char*) data);
161                 } else
162                         printf("\t%.*s\n", (int) length, (const char*) data);
163         }
164
165         return 0;
166 }
167
168 static int output_export(sd_journal *j, unsigned line) {
169         sd_id128_t boot_id;
170         char sid[33];
171         int r;
172         usec_t realtime, monotonic;
173         char *cursor;
174         const void *data;
175         size_t length;
176
177         assert(j);
178
179         r = sd_journal_get_realtime_usec(j, &realtime);
180         if (r < 0) {
181                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
182                 return r;
183         }
184
185         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
186         if (r < 0) {
187                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
188                 return r;
189         }
190
191         r = sd_journal_get_cursor(j, &cursor);
192         if (r < 0) {
193                 log_error("Failed to get cursor: %s", strerror(-r));
194                 return r;
195         }
196
197         printf(".cursor=%s\n"
198                ".realtime=%llu\n"
199                ".monotonic=%llu\n"
200                ".boot_id=%s\n",
201                cursor,
202                (unsigned long long) realtime,
203                (unsigned long long) monotonic,
204                sd_id128_to_string(boot_id, sid));
205
206         free(cursor);
207
208         SD_JOURNAL_FOREACH_DATA(j, data, length) {
209
210                 if (contains_unprintable(data, length)) {
211                         const char *c;
212                         uint64_t le64;
213
214                         c = memchr(data, '=', length);
215                         if (!c) {
216                                 log_error("Invalid field.");
217                                 return -EINVAL;
218                         }
219
220                         fwrite(data, c - (const char*) data, 1, stdout);
221                         fputc('\n', stdout);
222                         le64 = htole64(length - (c - (const char*) data) - 1);
223                         fwrite(&le64, sizeof(le64), 1, stdout);
224                         fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
225                 } else
226                         fwrite(data, length, 1, stdout);
227
228                 fputc('\n', stdout);
229         }
230
231         fputc('\n', stdout);
232
233         return 0;
234 }
235
236 static void json_escape(const char* p, size_t l) {
237
238         if (contains_unprintable(p, l)) {
239                 bool not_first = false;
240
241                 fputs("[ ", stdout);
242
243                 while (l > 0) {
244                         if (not_first)
245                                 printf(", %u", (uint8_t) *p);
246                         else {
247                                 not_first = true;
248                                 printf("%u", (uint8_t) *p);
249                         }
250
251                         p++;
252                         l--;
253                 }
254
255                 fputs(" ]", stdout);
256         } else {
257                 fputc('\"', stdout);
258
259                 while (l > 0) {
260                         if (*p == '"' || *p == '\\') {
261                                 fputc('\\', stdout);
262                                 fputc(*p, stdout);
263                         } else
264                                 fputc(*p, stdout);
265
266                         p++;
267                         l--;
268                 }
269
270                 fputc('\"', stdout);
271         }
272 }
273
274 static int output_json(sd_journal *j, unsigned line) {
275         uint64_t realtime, monotonic;
276         char *cursor;
277         const void *data;
278         size_t length;
279         sd_id128_t boot_id;
280         char sid[33];
281         int r;
282
283         assert(j);
284
285         r = sd_journal_get_realtime_usec(j, &realtime);
286         if (r < 0) {
287                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
288                 return r;
289         }
290
291         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
292         if (r < 0) {
293                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
294                 return r;
295         }
296
297         r = sd_journal_get_cursor(j, &cursor);
298         if (r < 0) {
299                 log_error("Failed to get cursor: %s", strerror(-r));
300                 return r;
301         }
302
303         if (line == 1)
304                 fputc('\n', stdout);
305         else
306                 fputs(",\n", stdout);
307
308         printf("{\n"
309                "\t\".cursor\" : \"%s\",\n"
310                "\t\".realtime\" : %llu,\n"
311                "\t\".monotonic\" : %llu,\n"
312                "\t\".boot_id\" : \"%s\"",
313                cursor,
314                (unsigned long long) realtime,
315                (unsigned long long) monotonic,
316                sd_id128_to_string(boot_id, sid));
317
318         free(cursor);
319
320         SD_JOURNAL_FOREACH_DATA(j, data, length) {
321                 const char *c;
322
323                 c = memchr(data, '=', length);
324                 if (!c) {
325                         log_error("Invalid field.");
326                         return -EINVAL;
327                 }
328
329                 fputs(",\n\t", stdout);
330                 json_escape(data, c - (const char*) data);
331                 fputs(" : ", stdout);
332                 json_escape(c + 1, length - (c - (const char*) data) - 1);
333         }
334
335         fputs("\n}", stdout);
336         fflush(stdout);
337
338         return 0;
339 }
340
341 static int (*output_funcs[_OUTPUT_MAX])(sd_journal*j, unsigned line) = {
342         [OUTPUT_SHORT] = output_short,
343         [OUTPUT_VERBOSE] = output_verbose,
344         [OUTPUT_EXPORT] = output_export,
345         [OUTPUT_JSON] = output_json
346 };
347
348 int main(int argc, char *argv[]) {
349         int r, i, fd;
350         sd_journal *j = NULL;
351         unsigned line = 0;
352
353         log_set_max_level(LOG_DEBUG);
354         log_set_target(LOG_TARGET_CONSOLE);
355
356         log_parse_environment();
357         log_open();
358
359         r = sd_journal_open(&j);
360         if (r < 0) {
361                 log_error("Failed to open journal: %s", strerror(-r));
362                 goto finish;
363         }
364
365         for (i = 1; i < argc; i++) {
366                 r = sd_journal_add_match(j, argv[i], strlen(argv[i]));
367                 if (r < 0) {
368                         log_error("Failed to add match: %s", strerror(-r));
369                         goto finish;
370                 }
371         }
372
373         fd = sd_journal_get_fd(j);
374         if (fd < 0) {
375                 log_error("Failed to get wakeup fd: %s", strerror(-fd));
376                 goto finish;
377         }
378
379         r = sd_journal_seek_head(j);
380         if (r < 0) {
381                 log_error("Failed to seek to head: %s", strerror(-r));
382                 goto finish;
383         }
384
385         if (arg_output == OUTPUT_JSON)
386                 fputc('[', stdout);
387
388         for (;;) {
389                 struct pollfd pollfd;
390
391                 while (sd_journal_next(j) > 0) {
392                         line ++;
393
394                         r = output_funcs[arg_output](j, line);
395                         if (r < 0)
396                                 goto finish;
397                 }
398
399                 if (!arg_follow)
400                         break;
401
402                 zero(pollfd);
403                 pollfd.fd = fd;
404                 pollfd.events = POLLIN;
405
406                 if (poll(&pollfd, 1, -1) < 0) {
407                         if (errno == EINTR)
408                                 break;
409
410                         log_error("poll(): %m");
411                         r = -errno;
412                         goto finish;
413                 }
414
415                 r = sd_journal_process(j);
416                 if (r < 0) {
417                         log_error("Failed to process: %s", strerror(-r));
418                         goto finish;
419                 }
420         }
421
422         if (arg_output == OUTPUT_JSON)
423                 fputs("\n]\n", stdout);
424
425 finish:
426         if (j)
427                 sd_journal_close(j);
428
429         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
430 }