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