chiark / gitweb /
when determining unit file list, include invalid unit names in an "invalid" state
[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 #include "utf8.h"
32
33 #define PRINT_THRESHOLD 128
34
35 static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
36         size_t fl, nl;
37         void *buf;
38
39         assert(data);
40         assert(field);
41         assert(target);
42         assert(target_size);
43
44         fl = strlen(field);
45         if (length < fl)
46                 return 0;
47
48         if (memcmp(data, field, fl))
49                 return 0;
50
51         nl = length - fl;
52         buf = malloc(nl+1);
53         if (!buf)
54                 return log_oom();
55
56         memcpy(buf, (const char*) data + fl, nl);
57         ((char*)buf)[nl] = 0;
58
59         free(*target);
60         *target = buf;
61         *target_size = nl;
62
63         return 1;
64 }
65
66 static bool shall_print(bool show_all, char *p, size_t l) {
67         if (show_all)
68                 return true;
69
70         if (l > PRINT_THRESHOLD)
71                 return false;
72
73         if (!utf8_is_printable_n(p, l))
74                 return false;
75
76         return true;
77 }
78
79 static int output_short(sd_journal *j, OutputMode mode, unsigned line, unsigned n_columns,
80                         OutputFlags flags) {
81         int r;
82         const void *data;
83         size_t length;
84         size_t n = 0;
85         char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
86         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, priority_len = 0;
87         int p = LOG_INFO;
88         const char *color_on = "", *color_off = "";
89
90         assert(j);
91
92         SD_JOURNAL_FOREACH_DATA(j, data, length) {
93
94                 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
95                 if (r < 0)
96                         goto finish;
97                 else if (r > 0)
98                         continue;
99
100                 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
101                 if (r < 0)
102                         goto finish;
103                 else if (r > 0)
104                         continue;
105
106                 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
107                 if (r < 0)
108                         goto finish;
109                 else if (r > 0)
110                         continue;
111
112                 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
113                 if (r < 0)
114                         goto finish;
115                 else if (r > 0)
116                         continue;
117
118                 r = parse_field(data, length, "_PID=", &pid, &pid_len);
119                 if (r < 0)
120                         goto finish;
121                 else if (r > 0)
122                         continue;
123
124                 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
125                 if (r < 0)
126                         goto finish;
127                 else if (r > 0)
128                         continue;
129
130                 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
131                 if (r < 0)
132                         goto finish;
133                 else if (r > 0)
134                         continue;
135
136                 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
137                 if (r < 0)
138                         goto finish;
139                 else if (r > 0)
140                         continue;
141
142                 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
143                 if (r < 0)
144                         goto finish;
145         }
146
147         if (!message) {
148                 r = 0;
149                 goto finish;
150         }
151
152         if (priority_len == 1 && *priority >= '0' && *priority <= '7')
153                 p = *priority - '0';
154
155         if (mode == OUTPUT_SHORT_MONOTONIC) {
156                 uint64_t t;
157                 sd_id128_t boot_id;
158
159                 r = -ENOENT;
160
161                 if (monotonic)
162                         r = safe_atou64(monotonic, &t);
163
164                 if (r < 0)
165                         r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
166
167                 if (r < 0) {
168                         log_error("Failed to get monotonic: %s", strerror(-r));
169                         goto finish;
170                 }
171
172                 printf("[%5llu.%06llu]",
173                        (unsigned long long) (t / USEC_PER_SEC),
174                        (unsigned long long) (t % USEC_PER_SEC));
175
176                 n += 1 + 5 + 1 + 6 + 1;
177
178         } else {
179                 char buf[64];
180                 uint64_t x;
181                 time_t t;
182                 struct tm tm;
183
184                 r = -ENOENT;
185
186                 if (realtime)
187                         r = safe_atou64(realtime, &x);
188
189                 if (r < 0)
190                         r = sd_journal_get_realtime_usec(j, &x);
191
192                 if (r < 0) {
193                         log_error("Failed to get realtime: %s", strerror(-r));
194                         goto finish;
195                 }
196
197                 t = (time_t) (x / USEC_PER_SEC);
198                 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
199                         log_error("Failed to format time.");
200                         goto finish;
201                 }
202
203                 fputs(buf, stdout);
204                 n += strlen(buf);
205         }
206
207         if (hostname && shall_print(flags & OUTPUT_SHOW_ALL,
208                                     hostname, hostname_len)) {
209                 printf(" %.*s", (int) hostname_len, hostname);
210                 n += hostname_len + 1;
211         }
212
213         if (identifier && shall_print(flags & OUTPUT_SHOW_ALL,
214                                       identifier, identifier_len)) {
215                 printf(" %.*s", (int) identifier_len, identifier);
216                 n += identifier_len + 1;
217         } else if (comm && shall_print(flags & OUTPUT_SHOW_ALL,
218                                        comm, comm_len)) {
219                 printf(" %.*s", (int) comm_len, comm);
220                 n += comm_len + 1;
221         } else
222                 putchar(' ');
223
224         if (pid && shall_print(flags & OUTPUT_SHOW_ALL, pid, pid_len)) {
225                 printf("[%.*s]", (int) pid_len, pid);
226                 n += pid_len + 2;
227         } else if (fake_pid && shall_print(flags & OUTPUT_SHOW_ALL,
228                                            fake_pid, fake_pid_len)) {
229                 printf("[%.*s]", (int) fake_pid_len, fake_pid);
230                 n += fake_pid_len + 2;
231         }
232
233         if (flags & OUTPUT_COLOR) {
234                 if (p <= LOG_ERR) {
235                         color_on = ANSI_HIGHLIGHT_RED_ON;
236                         color_off = ANSI_HIGHLIGHT_OFF;
237                 } else if (p <= LOG_NOTICE) {
238                         color_on = ANSI_HIGHLIGHT_ON;
239                         color_off = ANSI_HIGHLIGHT_OFF;
240                 }
241         }
242
243         if (flags & OUTPUT_SHOW_ALL)
244                 printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
245         else if (!utf8_is_printable_n(message, message_len)) {
246                 char bytes[FORMAT_BYTES_MAX];
247                 printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
248         } else if ((flags & OUTPUT_FULL_WIDTH) ||
249                    (message_len + n + 1 < n_columns))
250                 printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
251         else if (n < n_columns && n_columns - n - 2 >= 3) {
252                 char *e;
253
254                 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
255
256                 if (!e)
257                         printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
258                 else
259                         printf(": %s%s%s\n", color_on, e, color_off);
260
261                 free(e);
262         } else
263                 fputs("\n", stdout);
264
265         r = 0;
266
267 finish:
268         free(hostname);
269         free(identifier);
270         free(comm);
271         free(pid);
272         free(fake_pid);
273         free(message);
274         free(monotonic);
275         free(realtime);
276         free(priority);
277
278         return r;
279 }
280
281 static int output_verbose(sd_journal *j, OutputMode mode, unsigned line,
282                           unsigned n_columns, OutputFlags flags) {
283         const void *data;
284         size_t length;
285         char *cursor;
286         uint64_t realtime;
287         char ts[FORMAT_TIMESTAMP_MAX];
288         int r;
289
290         assert(j);
291
292         r = sd_journal_get_realtime_usec(j, &realtime);
293         if (r < 0) {
294                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
295                 return r;
296         }
297
298         r = sd_journal_get_cursor(j, &cursor);
299         if (r < 0) {
300                 log_error("Failed to get cursor: %s", strerror(-r));
301                 return r;
302         }
303
304         printf("%s [%s]\n",
305                format_timestamp(ts, sizeof(ts), realtime),
306                cursor);
307
308         free(cursor);
309
310         SD_JOURNAL_FOREACH_DATA(j, data, length) {
311                 if (!(flags & OUTPUT_SHOW_ALL) && (length > PRINT_THRESHOLD ||
312                                   !utf8_is_printable_n(data, length))) {
313                         const char *c;
314                         char bytes[FORMAT_BYTES_MAX];
315
316                         c = memchr(data, '=', length);
317                         if (!c) {
318                                 log_error("Invalid field.");
319                                 return -EINVAL;
320                         }
321
322                         printf("\t%.*s=[%s blob data]\n",
323                                (int) (c - (const char*) data),
324                                (const char*) data,
325                                format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
326                 } else
327                         printf("\t%.*s\n", (int) length, (const char*) data);
328         }
329
330         return 0;
331 }
332
333 static int output_export(sd_journal *j, OutputMode mode, unsigned line,
334                          unsigned n_columns, OutputFlags flags) {
335         sd_id128_t boot_id;
336         char sid[33];
337         int r;
338         usec_t realtime, monotonic;
339         char *cursor;
340         const void *data;
341         size_t length;
342
343         assert(j);
344
345         r = sd_journal_get_realtime_usec(j, &realtime);
346         if (r < 0) {
347                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
348                 return r;
349         }
350
351         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
352         if (r < 0) {
353                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
354                 return r;
355         }
356
357         r = sd_journal_get_cursor(j, &cursor);
358         if (r < 0) {
359                 log_error("Failed to get cursor: %s", strerror(-r));
360                 return r;
361         }
362
363         printf("__CURSOR=%s\n"
364                "__REALTIME_TIMESTAMP=%llu\n"
365                "__MONOTONIC_TIMESTAMP=%llu\n"
366                "_BOOT_ID=%s\n",
367                cursor,
368                (unsigned long long) realtime,
369                (unsigned long long) monotonic,
370                sd_id128_to_string(boot_id, sid));
371
372         free(cursor);
373
374         SD_JOURNAL_FOREACH_DATA(j, data, length) {
375
376                 /* We already printed the boot id, from the data in
377                  * the header, hence let's suppress it here */
378                 if (length >= 9 &&
379                     memcmp(data, "_BOOT_ID=", 9) == 0)
380                         continue;
381
382                 if (!utf8_is_printable_n(data, length)) {
383                         const char *c;
384                         uint64_t le64;
385
386                         c = memchr(data, '=', length);
387                         if (!c) {
388                                 log_error("Invalid field.");
389                                 return -EINVAL;
390                         }
391
392                         fwrite(data, c - (const char*) data, 1, stdout);
393                         fputc('\n', stdout);
394                         le64 = htole64(length - (c - (const char*) data) - 1);
395                         fwrite(&le64, sizeof(le64), 1, stdout);
396                         fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
397                 } else
398                         fwrite(data, length, 1, stdout);
399
400                 fputc('\n', stdout);
401         }
402
403         fputc('\n', stdout);
404
405         return 0;
406 }
407
408 static void json_escape(const char* p, size_t l) {
409         if (!utf8_is_printable_n(p, l)) {
410                 bool not_first = false;
411
412                 fputs("[ ", stdout);
413
414                 while (l > 0) {
415                         if (not_first)
416                                 printf(", %u", (uint8_t) *p);
417                         else {
418                                 not_first = true;
419                                 printf("%u", (uint8_t) *p);
420                         }
421
422                         p++;
423                         l--;
424                 }
425
426                 fputs(" ]", stdout);
427         } else {
428                 fputc('\"', stdout);
429
430                 while (l > 0) {
431                         if (*p == '"' || *p == '\\') {
432                                 fputc('\\', stdout);
433                                 fputc(*p, stdout);
434                         } else
435                                 fputc(*p, stdout);
436
437                         p++;
438                         l--;
439                 }
440
441                 fputc('\"', stdout);
442         }
443 }
444
445 static int output_json(sd_journal *j, OutputMode mode, unsigned line,
446                        unsigned n_columns, OutputFlags flags) {
447         uint64_t realtime, monotonic;
448         char *cursor;
449         const void *data;
450         size_t length;
451         sd_id128_t boot_id;
452         char sid[33];
453         int r;
454
455         assert(j);
456
457         r = sd_journal_get_realtime_usec(j, &realtime);
458         if (r < 0) {
459                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
460                 return r;
461         }
462
463         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
464         if (r < 0) {
465                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
466                 return r;
467         }
468
469         r = sd_journal_get_cursor(j, &cursor);
470         if (r < 0) {
471                 log_error("Failed to get cursor: %s", strerror(-r));
472                 return r;
473         }
474
475         if (mode == OUTPUT_JSON_PRETTY)
476                 printf("{\n"
477                        "\t\"__CURSOR\" : \"%s\",\n"
478                        "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
479                        "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
480                        "\t\"_BOOT_ID\" : \"%s\"",
481                        cursor,
482                        (unsigned long long) realtime,
483                        (unsigned long long) monotonic,
484                        sd_id128_to_string(boot_id, sid));
485         else
486                 printf("{ \"__CURSOR\" : \"%s\", "
487                        "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
488                        "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
489                        "\"_BOOT_ID\" : \"%s\"",
490                        cursor,
491                        (unsigned long long) realtime,
492                        (unsigned long long) monotonic,
493                        sd_id128_to_string(boot_id, sid));
494         free(cursor);
495
496         SD_JOURNAL_FOREACH_DATA(j, data, length) {
497                 const char *c;
498
499                 /* We already printed the boot id, from the data in
500                  * the header, hence let's suppress it here */
501                 if (length >= 9 &&
502                     memcmp(data, "_BOOT_ID=", 9) == 0)
503                         continue;
504
505                 c = memchr(data, '=', length);
506                 if (!c) {
507                         log_error("Invalid field.");
508                         return -EINVAL;
509                 }
510
511                 if (mode == OUTPUT_JSON_PRETTY)
512                         fputs(",\n\t", stdout);
513                 else
514                         fputs(", ", stdout);
515
516                 json_escape(data, c - (const char*) data);
517                 fputs(" : ", stdout);
518                 json_escape(c + 1, length - (c - (const char*) data) - 1);
519         }
520
521         if (mode == OUTPUT_JSON_PRETTY)
522                 fputs("\n}\n", stdout);
523         else
524                 fputs(" }\n", stdout);
525
526         return 0;
527 }
528
529 static int output_cat(sd_journal *j, OutputMode mode, unsigned line,
530                       unsigned n_columns, OutputFlags flags) {
531         const void *data;
532         size_t l;
533         int r;
534
535         assert(j);
536
537         r = sd_journal_get_data(j, "MESSAGE", &data, &l);
538         if (r < 0) {
539                 log_error("Failed to get data: %s", strerror(-r));
540                 return r;
541         }
542
543         assert(l >= 8);
544
545         fwrite((const char*) data + 8, 1, l - 8, stdout);
546         putchar('\n');
547
548         return 0;
549 }
550
551 static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, OutputMode mode, unsigned line,
552                                              unsigned n_columns, OutputFlags flags) = {
553         [OUTPUT_SHORT] = output_short,
554         [OUTPUT_SHORT_MONOTONIC] = output_short,
555         [OUTPUT_VERBOSE] = output_verbose,
556         [OUTPUT_EXPORT] = output_export,
557         [OUTPUT_JSON] = output_json,
558         [OUTPUT_JSON_PRETTY] = output_json,
559         [OUTPUT_CAT] = output_cat
560 };
561
562 int output_journal(sd_journal *j, OutputMode mode, unsigned line,
563                    unsigned n_columns, OutputFlags flags) {
564         int ret;
565         assert(mode >= 0);
566         assert(mode < _OUTPUT_MODE_MAX);
567
568         if (n_columns <= 0)
569                 n_columns = columns();
570
571         ret = output_funcs[mode](j, mode, line, n_columns, flags);
572         fflush(stdout);
573         return ret;
574 }
575
576 int show_journal_by_unit(
577                 const char *unit,
578                 OutputMode mode,
579                 unsigned n_columns,
580                 usec_t not_before,
581                 unsigned how_many,
582                 OutputFlags flags) {
583
584         char *m1 = NULL, *m2 = NULL, *m3 = NULL;
585         sd_journal *j = NULL;
586         int r;
587         unsigned line = 0;
588         bool need_seek = false;
589         int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
590
591         assert(mode >= 0);
592         assert(mode < _OUTPUT_MODE_MAX);
593         assert(unit);
594
595         if (!endswith(unit, ".service") &&
596             !endswith(unit, ".socket") &&
597             !endswith(unit, ".mount") &&
598             !endswith(unit, ".swap"))
599                 return 0;
600
601         if (how_many <= 0)
602                 return 0;
603
604         if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
605             asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
606             asprintf(&m3, "UNIT=%s", unit) < 0) {
607                 r = -ENOMEM;
608                 goto finish;
609         }
610
611         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
612         if (r < 0)
613                 goto finish;
614
615         /* Look for messages from the service itself */
616         r = sd_journal_add_match(j, m1, 0);
617         if (r < 0)
618                 goto finish;
619
620         /* Look for coredumps of the service */
621         r = sd_journal_add_disjunction(j);
622         if (r < 0)
623                 goto finish;
624         r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0);
625         if (r < 0)
626                 goto finish;
627         r = sd_journal_add_match(j, m2, 0);
628         if (r < 0)
629                 goto finish;
630
631         /* Look for messages from PID 1 about this service */
632         r = sd_journal_add_disjunction(j);
633         if (r < 0)
634                 goto finish;
635         r = sd_journal_add_match(j, "_PID=1", 0);
636         if (r < 0)
637                 goto finish;
638         r = sd_journal_add_match(j, m3, 0);
639         if (r < 0)
640                 goto finish;
641
642         /* Seek to end */
643         r = sd_journal_seek_tail(j);
644         if (r < 0)
645                 goto finish;
646
647         r = sd_journal_previous_skip(j, how_many);
648         if (r < 0)
649                 goto finish;
650
651         if (mode == OUTPUT_JSON) {
652                 fputc('[', stdout);
653                 fflush(stdout);
654         }
655
656         for (;;) {
657                 for (;;) {
658                         usec_t usec;
659
660                         if (need_seek) {
661                                 r = sd_journal_next(j);
662                                 if (r < 0)
663                                         goto finish;
664                         }
665
666                         if (r == 0)
667                                 break;
668
669                         need_seek = true;
670
671                         if (not_before > 0) {
672                                 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
673
674                                 /* -ESTALE is returned if the
675                                    timestamp is not from this boot */
676                                 if (r == -ESTALE)
677                                         continue;
678                                 else if (r < 0)
679                                         goto finish;
680
681                                 if (usec < not_before)
682                                         continue;
683                         }
684
685                         line ++;
686
687                         r = output_journal(j, mode, line, n_columns, flags);
688                         if (r < 0)
689                                 goto finish;
690                 }
691
692                 if (warn_cutoff && line < how_many && not_before > 0) {
693                         sd_id128_t boot_id;
694                         usec_t cutoff;
695
696                         /* Check whether the cutoff line is too early */
697
698                         r = sd_id128_get_boot(&boot_id);
699                         if (r < 0)
700                                 goto finish;
701
702                         r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
703                         if (r < 0)
704                                 goto finish;
705
706                         if (r > 0 && not_before < cutoff)
707                                 printf("Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
708
709                         warn_cutoff = false;
710                 }
711
712                 if (!(flags & OUTPUT_FOLLOW))
713                         break;
714
715                 r = sd_journal_wait(j, (usec_t) -1);
716                 if (r < 0)
717                         goto finish;
718
719         }
720
721         if (mode == OUTPUT_JSON)
722                 fputs("\n]\n", stdout);
723
724 finish:
725         free(m1);
726         free(m2);
727         free(m3);
728
729         if (j)
730                 sd_journal_close(j);
731
732         return r;
733 }
734
735 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
736         [OUTPUT_SHORT] = "short",
737         [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
738         [OUTPUT_VERBOSE] = "verbose",
739         [OUTPUT_EXPORT] = "export",
740         [OUTPUT_JSON] = "json",
741         [OUTPUT_JSON_PRETTY] = "json-pretty",
742         [OUTPUT_CAT] = "cat"
743 };
744
745 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);