chiark / gitweb /
core: move ManagerRunningAs to shared
[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                 /* An entry without MESSAGE=? */
540                 if (r == -ENOENT)
541                         return 0;
542
543                 log_error("Failed to get data: %s", strerror(-r));
544                 return r;
545         }
546
547         assert(l >= 8);
548
549         fwrite((const char*) data + 8, 1, l - 8, stdout);
550         putchar('\n');
551
552         return 0;
553 }
554
555 static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, OutputMode mode, unsigned line,
556                                              unsigned n_columns, OutputFlags flags) = {
557         [OUTPUT_SHORT] = output_short,
558         [OUTPUT_SHORT_MONOTONIC] = output_short,
559         [OUTPUT_VERBOSE] = output_verbose,
560         [OUTPUT_EXPORT] = output_export,
561         [OUTPUT_JSON] = output_json,
562         [OUTPUT_JSON_PRETTY] = output_json,
563         [OUTPUT_CAT] = output_cat
564 };
565
566 int output_journal(sd_journal *j, OutputMode mode, unsigned line,
567                    unsigned n_columns, OutputFlags flags) {
568         int ret;
569         assert(mode >= 0);
570         assert(mode < _OUTPUT_MODE_MAX);
571
572         if (n_columns <= 0)
573                 n_columns = columns();
574
575         ret = output_funcs[mode](j, mode, line, n_columns, flags);
576         fflush(stdout);
577         return ret;
578 }
579
580 int show_journal_by_unit(
581                 const char *unit,
582                 OutputMode mode,
583                 unsigned n_columns,
584                 usec_t not_before,
585                 unsigned how_many,
586                 OutputFlags flags) {
587
588         char *m1 = NULL, *m2 = NULL, *m3 = NULL;
589         sd_journal *j = NULL;
590         int r;
591         unsigned line = 0;
592         bool need_seek = false;
593         int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
594
595         assert(mode >= 0);
596         assert(mode < _OUTPUT_MODE_MAX);
597         assert(unit);
598
599         if (!endswith(unit, ".service") &&
600             !endswith(unit, ".socket") &&
601             !endswith(unit, ".mount") &&
602             !endswith(unit, ".swap"))
603                 return 0;
604
605         if (how_many <= 0)
606                 return 0;
607
608         if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
609             asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
610             asprintf(&m3, "UNIT=%s", unit) < 0) {
611                 r = -ENOMEM;
612                 goto finish;
613         }
614
615         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
616         if (r < 0)
617                 goto finish;
618
619         /* Look for messages from the service itself */
620         r = sd_journal_add_match(j, m1, 0);
621         if (r < 0)
622                 goto finish;
623
624         /* Look for coredumps of the service */
625         r = sd_journal_add_disjunction(j);
626         if (r < 0)
627                 goto finish;
628         r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0);
629         if (r < 0)
630                 goto finish;
631         r = sd_journal_add_match(j, m2, 0);
632         if (r < 0)
633                 goto finish;
634
635         /* Look for messages from PID 1 about this service */
636         r = sd_journal_add_disjunction(j);
637         if (r < 0)
638                 goto finish;
639         r = sd_journal_add_match(j, "_PID=1", 0);
640         if (r < 0)
641                 goto finish;
642         r = sd_journal_add_match(j, m3, 0);
643         if (r < 0)
644                 goto finish;
645
646         /* Seek to end */
647         r = sd_journal_seek_tail(j);
648         if (r < 0)
649                 goto finish;
650
651         r = sd_journal_previous_skip(j, how_many);
652         if (r < 0)
653                 goto finish;
654
655         if (mode == OUTPUT_JSON) {
656                 fputc('[', stdout);
657                 fflush(stdout);
658         }
659
660         for (;;) {
661                 for (;;) {
662                         usec_t usec;
663
664                         if (need_seek) {
665                                 r = sd_journal_next(j);
666                                 if (r < 0)
667                                         goto finish;
668                         }
669
670                         if (r == 0)
671                                 break;
672
673                         need_seek = true;
674
675                         if (not_before > 0) {
676                                 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
677
678                                 /* -ESTALE is returned if the
679                                    timestamp is not from this boot */
680                                 if (r == -ESTALE)
681                                         continue;
682                                 else if (r < 0)
683                                         goto finish;
684
685                                 if (usec < not_before)
686                                         continue;
687                         }
688
689                         line ++;
690
691                         r = output_journal(j, mode, line, n_columns, flags);
692                         if (r < 0)
693                                 goto finish;
694                 }
695
696                 if (warn_cutoff && line < how_many && not_before > 0) {
697                         sd_id128_t boot_id;
698                         usec_t cutoff;
699
700                         /* Check whether the cutoff line is too early */
701
702                         r = sd_id128_get_boot(&boot_id);
703                         if (r < 0)
704                                 goto finish;
705
706                         r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
707                         if (r < 0)
708                                 goto finish;
709
710                         if (r > 0 && not_before < cutoff)
711                                 printf("Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
712
713                         warn_cutoff = false;
714                 }
715
716                 if (!(flags & OUTPUT_FOLLOW))
717                         break;
718
719                 r = sd_journal_wait(j, (usec_t) -1);
720                 if (r < 0)
721                         goto finish;
722
723         }
724
725         if (mode == OUTPUT_JSON)
726                 fputs("\n]\n", stdout);
727
728 finish:
729         free(m1);
730         free(m2);
731         free(m3);
732
733         if (j)
734                 sd_journal_close(j);
735
736         return r;
737 }
738
739 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
740         [OUTPUT_SHORT] = "short",
741         [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
742         [OUTPUT_VERBOSE] = "verbose",
743         [OUTPUT_EXPORT] = "export",
744         [OUTPUT_JSON] = "json",
745         [OUTPUT_JSON_PRETTY] = "json-pretty",
746         [OUTPUT_CAT] = "cat"
747 };
748
749 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);