chiark / gitweb /
systemctl: append .service to unit names lacking suffix
[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, 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 (flags & OUTPUT_MONOTONIC_MODE) {
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 < 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
277         return r;
278 }
279
280 static int output_short_realtime(sd_journal *j, unsigned line,
281                                  unsigned n_columns, OutputFlags flags) {
282         return output_short(j, line, n_columns, flags & ~OUTPUT_MONOTONIC_MODE);
283 }
284
285 static int output_short_monotonic(sd_journal *j, unsigned line,
286                                   unsigned n_columns, OutputFlags flags) {
287         return output_short(j, line, n_columns, flags | OUTPUT_MONOTONIC_MODE);
288 }
289
290 static int output_verbose(sd_journal *j, unsigned line,
291                           unsigned n_columns, OutputFlags flags) {
292         const void *data;
293         size_t length;
294         char *cursor;
295         uint64_t realtime;
296         char ts[FORMAT_TIMESTAMP_MAX];
297         int r;
298
299         assert(j);
300
301         r = sd_journal_get_realtime_usec(j, &realtime);
302         if (r < 0) {
303                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
304                 return r;
305         }
306
307         r = sd_journal_get_cursor(j, &cursor);
308         if (r < 0) {
309                 log_error("Failed to get cursor: %s", strerror(-r));
310                 return r;
311         }
312
313         printf("%s [%s]\n",
314                format_timestamp(ts, sizeof(ts), realtime),
315                cursor);
316
317         free(cursor);
318
319         SD_JOURNAL_FOREACH_DATA(j, data, length) {
320                 if (!(flags & OUTPUT_SHOW_ALL) && (length > PRINT_THRESHOLD ||
321                                   !utf8_is_printable_n(data, length))) {
322                         const char *c;
323                         char bytes[FORMAT_BYTES_MAX];
324
325                         c = memchr(data, '=', length);
326                         if (!c) {
327                                 log_error("Invalid field.");
328                                 return -EINVAL;
329                         }
330
331                         printf("\t%.*s=[%s blob data]\n",
332                                (int) (c - (const char*) data),
333                                (const char*) data,
334                                format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
335                 } else
336                         printf("\t%.*s\n", (int) length, (const char*) data);
337         }
338
339         return 0;
340 }
341
342 static int output_export(sd_journal *j, unsigned line,
343                          unsigned n_columns, OutputFlags flags) {
344         sd_id128_t boot_id;
345         char sid[33];
346         int r;
347         usec_t realtime, monotonic;
348         char *cursor;
349         const void *data;
350         size_t length;
351
352         assert(j);
353
354         r = sd_journal_get_realtime_usec(j, &realtime);
355         if (r < 0) {
356                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
357                 return r;
358         }
359
360         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
361         if (r < 0) {
362                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
363                 return r;
364         }
365
366         r = sd_journal_get_cursor(j, &cursor);
367         if (r < 0) {
368                 log_error("Failed to get cursor: %s", strerror(-r));
369                 return r;
370         }
371
372         printf("__CURSOR=%s\n"
373                "__REALTIME_TIMESTAMP=%llu\n"
374                "__MONOTONIC_TIMESTAMP=%llu\n"
375                "_BOOT_ID=%s\n",
376                cursor,
377                (unsigned long long) realtime,
378                (unsigned long long) monotonic,
379                sd_id128_to_string(boot_id, sid));
380
381         free(cursor);
382
383         SD_JOURNAL_FOREACH_DATA(j, data, length) {
384
385                 /* We already printed the boot id, from the data in
386                  * the header, hence let's suppress it here */
387                 if (length >= 9 &&
388                     memcmp(data, "_BOOT_ID=", 9) == 0)
389                         continue;
390
391                 if (!utf8_is_printable_n(data, length)) {
392                         const char *c;
393                         uint64_t le64;
394
395                         c = memchr(data, '=', length);
396                         if (!c) {
397                                 log_error("Invalid field.");
398                                 return -EINVAL;
399                         }
400
401                         fwrite(data, c - (const char*) data, 1, stdout);
402                         fputc('\n', stdout);
403                         le64 = htole64(length - (c - (const char*) data) - 1);
404                         fwrite(&le64, sizeof(le64), 1, stdout);
405                         fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
406                 } else
407                         fwrite(data, length, 1, stdout);
408
409                 fputc('\n', stdout);
410         }
411
412         fputc('\n', stdout);
413
414         return 0;
415 }
416
417 static void json_escape(const char* p, size_t l) {
418         if (!utf8_is_printable_n(p, l)) {
419                 bool not_first = false;
420
421                 fputs("[ ", stdout);
422
423                 while (l > 0) {
424                         if (not_first)
425                                 printf(", %u", (uint8_t) *p);
426                         else {
427                                 not_first = true;
428                                 printf("%u", (uint8_t) *p);
429                         }
430
431                         p++;
432                         l--;
433                 }
434
435                 fputs(" ]", stdout);
436         } else {
437                 fputc('\"', stdout);
438
439                 while (l > 0) {
440                         if (*p == '"' || *p == '\\') {
441                                 fputc('\\', stdout);
442                                 fputc(*p, stdout);
443                         } else
444                                 fputc(*p, stdout);
445
446                         p++;
447                         l--;
448                 }
449
450                 fputc('\"', stdout);
451         }
452 }
453
454 static int output_json(sd_journal *j, unsigned line,
455                        unsigned n_columns, OutputFlags flags) {
456         uint64_t realtime, monotonic;
457         char *cursor;
458         const void *data;
459         size_t length;
460         sd_id128_t boot_id;
461         char sid[33];
462         int r;
463
464         assert(j);
465
466         r = sd_journal_get_realtime_usec(j, &realtime);
467         if (r < 0) {
468                 log_error("Failed to get realtime timestamp: %s", strerror(-r));
469                 return r;
470         }
471
472         r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
473         if (r < 0) {
474                 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
475                 return r;
476         }
477
478         r = sd_journal_get_cursor(j, &cursor);
479         if (r < 0) {
480                 log_error("Failed to get cursor: %s", strerror(-r));
481                 return r;
482         }
483
484         if (line == 1)
485                 fputc('\n', stdout);
486         else
487                 fputs(",\n", stdout);
488
489         printf("{\n"
490                "\t\"__CURSOR\" : \"%s\",\n"
491                "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
492                "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
493                "\t\"_BOOT_ID\" : \"%s\"",
494                cursor,
495                (unsigned long long) realtime,
496                (unsigned long long) monotonic,
497                sd_id128_to_string(boot_id, sid));
498
499         free(cursor);
500
501         SD_JOURNAL_FOREACH_DATA(j, data, length) {
502                 const char *c;
503
504                 /* We already printed the boot id, from the data in
505                  * the header, hence let's suppress it here */
506                 if (length >= 9 &&
507                     memcmp(data, "_BOOT_ID=", 9) == 0)
508                         continue;
509
510                 c = memchr(data, '=', length);
511                 if (!c) {
512                         log_error("Invalid field.");
513                         return -EINVAL;
514                 }
515
516                 fputs(",\n\t", stdout);
517                 json_escape(data, c - (const char*) data);
518                 fputs(" : ", stdout);
519                 json_escape(c + 1, length - (c - (const char*) data) - 1);
520         }
521
522         fputs("\n}", stdout);
523         fflush(stdout);
524
525         return 0;
526 }
527
528 static int output_cat(sd_journal *j, unsigned line,
529                       unsigned n_columns, OutputFlags flags) {
530         const void *data;
531         size_t l;
532         int r;
533
534         assert(j);
535
536         r = sd_journal_get_data(j, "MESSAGE", &data, &l);
537         if (r < 0) {
538                 log_error("Failed to get data: %s", strerror(-r));
539                 return r;
540         }
541
542         assert(l >= 8);
543
544         fwrite((const char*) data + 8, 1, l - 8, stdout);
545         putchar('\n');
546
547         return 0;
548 }
549
550 static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line,
551                                              unsigned n_columns, OutputFlags flags) = {
552         [OUTPUT_SHORT] = output_short_realtime,
553         [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
554         [OUTPUT_VERBOSE] = output_verbose,
555         [OUTPUT_EXPORT] = output_export,
556         [OUTPUT_JSON] = output_json,
557         [OUTPUT_CAT] = output_cat
558 };
559
560 int output_journal(sd_journal *j, OutputMode mode, unsigned line,
561                    unsigned n_columns, OutputFlags flags) {
562         assert(mode >= 0);
563         assert(mode < _OUTPUT_MODE_MAX);
564
565         if (n_columns <= 0)
566                 n_columns = columns();
567
568         return output_funcs[mode](j, line, n_columns, flags);
569 }
570
571 int show_journal_by_unit(
572                 const char *unit,
573                 OutputMode mode,
574                 unsigned n_columns,
575                 usec_t not_before,
576                 unsigned how_many,
577                 OutputFlags flags) {
578
579         char *m = NULL;
580         sd_journal *j = NULL;
581         int r;
582         unsigned line = 0;
583         bool need_seek = false;
584         int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
585
586         assert(mode >= 0);
587         assert(mode < _OUTPUT_MODE_MAX);
588         assert(unit);
589
590         if (!endswith(unit, ".service") &&
591             !endswith(unit, ".socket") &&
592             !endswith(unit, ".mount") &&
593             !endswith(unit, ".swap"))
594                 return 0;
595
596         if (how_many <= 0)
597                 return 0;
598
599         if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) {
600                 r = -ENOMEM;
601                 goto finish;
602         }
603
604         r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
605         if (r < 0)
606                 goto finish;
607
608         r = sd_journal_add_match(j, m, strlen(m));
609         if (r < 0)
610                 goto finish;
611
612         r = sd_journal_seek_tail(j);
613         if (r < 0)
614                 goto finish;
615
616         r = sd_journal_previous_skip(j, how_many);
617         if (r < 0)
618                 goto finish;
619
620         if (mode == OUTPUT_JSON) {
621                 fputc('[', stdout);
622                 fflush(stdout);
623         }
624
625         for (;;) {
626                 for (;;) {
627                         usec_t usec;
628
629                         if (need_seek) {
630                                 r = sd_journal_next(j);
631                                 if (r < 0)
632                                         goto finish;
633                         }
634
635                         if (r == 0)
636                                 break;
637
638                         need_seek = true;
639
640                         if (not_before > 0) {
641                                 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
642
643                                 /* -ESTALE is returned if the
644                                    timestamp is not from this boot */
645                                 if (r == -ESTALE)
646                                         continue;
647                                 else if (r < 0)
648                                         goto finish;
649
650                                 if (usec < not_before)
651                                         continue;
652                         }
653
654                         line ++;
655
656                         r = output_journal(j, mode, line, n_columns, flags);
657                         if (r < 0)
658                                 goto finish;
659                 }
660
661                 if (warn_cutoff && line < how_many && not_before > 0) {
662                         sd_id128_t boot_id;
663                         usec_t cutoff;
664
665                         /* Check whether the cutoff line is too early */
666
667                         r = sd_id128_get_boot(&boot_id);
668                         if (r < 0)
669                                 goto finish;
670
671                         r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
672                         if (r < 0)
673                                 goto finish;
674
675                         if (r > 0 && not_before < cutoff)
676                                 printf("Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
677
678                         warn_cutoff = false;
679                 }
680
681                 if (!(flags & OUTPUT_FOLLOW))
682                         break;
683
684                 r = sd_journal_wait(j, (usec_t) -1);
685                 if (r < 0)
686                         goto finish;
687
688         }
689
690         if (mode == OUTPUT_JSON)
691                 fputs("\n]\n", stdout);
692
693 finish:
694         if (m)
695                 free(m);
696
697         if (j)
698                 sd_journal_close(j);
699
700         return r;
701 }
702
703 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
704         [OUTPUT_SHORT] = "short",
705         [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
706         [OUTPUT_VERBOSE] = "verbose",
707         [OUTPUT_EXPORT] = "export",
708         [OUTPUT_JSON] = "json",
709         [OUTPUT_CAT] = "cat"
710 };
711
712 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);