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