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