chiark / gitweb /
a9cf9cd957445441b045b345b3ca22a89e1a74f5
[elogind.git] / src / journal / journalctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 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 <fcntl.h>
23 #include <errno.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <sys/poll.h>
30 #include <time.h>
31 #include <getopt.h>
32 #include <sys/stat.h>
33
34 #include <systemd/sd-journal.h>
35
36 #include "log.h"
37 #include "util.h"
38 #include "path-util.h"
39 #include "build.h"
40 #include "pager.h"
41 #include "logs-show.h"
42 #include "strv.h"
43 #include "journal-internal.h"
44
45 static OutputMode arg_output = OUTPUT_SHORT;
46 static bool arg_follow = false;
47 static bool arg_show_all = false;
48 static bool arg_no_pager = false;
49 static int arg_lines = -1;
50 static bool arg_no_tail = false;
51 static bool arg_new_id128 = false;
52 static bool arg_print_header = false;
53 static bool arg_quiet = false;
54 static bool arg_local = false;
55 static bool arg_this_boot = false;
56 static const char *arg_directory = NULL;
57
58 static int help(void) {
59
60         printf("%s [OPTIONS...] [MATCH]\n\n"
61                "Send control commands to or query the journal.\n\n"
62                "  -h --help           Show this help\n"
63                "     --version        Show package version\n"
64                "     --no-pager       Do not pipe output into a pager\n"
65                "  -a --all            Show all fields, including long and unprintable\n"
66                "  -f --follow         Follow journal\n"
67                "  -n --lines=INTEGER  Journal entries to show\n"
68                "     --no-tail        Show all lines, even in follow mode\n"
69                "  -o --output=STRING  Change journal output mode (short, short-monotonic,\n"
70                "                      verbose, export, json, cat)\n"
71                "  -q --quiet          Don't show privilege warning\n"
72                "  -l --local          Only local entries\n"
73                "  -b --this-boot      Show data only from current boot\n"
74                "  -D --directory=PATH Show journal files from directory\n"
75                "     --header         Show journal header information\n"
76                "     --new-id128      Generate a new 128 Bit id\n",
77                program_invocation_short_name);
78
79         return 0;
80 }
81
82 static int parse_argv(int argc, char *argv[]) {
83
84         enum {
85                 ARG_VERSION = 0x100,
86                 ARG_NO_PAGER,
87                 ARG_NO_TAIL,
88                 ARG_NEW_ID128,
89                 ARG_HEADER
90         };
91
92         static const struct option options[] = {
93                 { "help",      no_argument,       NULL, 'h'           },
94                 { "version" ,  no_argument,       NULL, ARG_VERSION   },
95                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
96                 { "follow",    no_argument,       NULL, 'f'           },
97                 { "output",    required_argument, NULL, 'o'           },
98                 { "all",       no_argument,       NULL, 'a'           },
99                 { "lines",     required_argument, NULL, 'n'           },
100                 { "no-tail",   no_argument,       NULL, ARG_NO_TAIL   },
101                 { "new-id128", no_argument,       NULL, ARG_NEW_ID128 },
102                 { "quiet",     no_argument,       NULL, 'q'           },
103                 { "local",     no_argument,       NULL, 'l'           },
104                 { "this-boot", no_argument,       NULL, 'b'           },
105                 { "directory", required_argument, NULL, 'D'           },
106                 { "header",    no_argument,       NULL, ARG_HEADER    },
107                 { NULL,        0,                 NULL, 0             }
108         };
109
110         int c, r;
111
112         assert(argc >= 0);
113         assert(argv);
114
115         while ((c = getopt_long(argc, argv, "hfo:an:qlbD:", options, NULL)) >= 0) {
116
117                 switch (c) {
118
119                 case 'h':
120                         help();
121                         return 0;
122
123                 case ARG_VERSION:
124                         puts(PACKAGE_STRING);
125                         puts(DISTRIBUTION);
126                         puts(SYSTEMD_FEATURES);
127                         return 0;
128
129                 case ARG_NO_PAGER:
130                         arg_no_pager = true;
131                         break;
132
133                 case 'f':
134                         arg_follow = true;
135                         break;
136
137                 case 'o':
138                         arg_output =  output_mode_from_string(optarg);
139                         if (arg_output < 0) {
140                                 log_error("Unknown output '%s'.", optarg);
141                                 return -EINVAL;
142                         }
143
144                         break;
145
146                 case 'a':
147                         arg_show_all = true;
148                         break;
149
150                 case 'n':
151                         r = safe_atoi(optarg, &arg_lines);
152                         if (r < 0 || arg_lines < 0) {
153                                 log_error("Failed to parse lines '%s'", optarg);
154                                 return -EINVAL;
155                         }
156                         break;
157
158                 case ARG_NO_TAIL:
159                         arg_no_tail = true;
160                         break;
161
162                 case ARG_NEW_ID128:
163                         arg_new_id128 = true;
164                         break;
165
166                 case 'q':
167                         arg_quiet = true;
168                         break;
169
170                 case 'l':
171                         arg_local = true;
172                         break;
173
174                 case 'b':
175                         arg_this_boot = true;
176                         break;
177
178                 case 'D':
179                         arg_directory = optarg;
180                         break;
181
182                 case ARG_HEADER:
183                         arg_print_header = true;
184                         break;
185
186                 case '?':
187                         return -EINVAL;
188
189                 default:
190                         log_error("Unknown option code %c", c);
191                         return -EINVAL;
192                 }
193         }
194
195         if (arg_follow && !arg_no_tail && arg_lines < 0)
196                 arg_lines = 10;
197
198         return 1;
199 }
200
201 static int generate_new_id128(void) {
202         sd_id128_t id;
203         int r;
204         unsigned i;
205
206         r = sd_id128_randomize(&id);
207         if (r < 0) {
208                 log_error("Failed to generate ID: %s", strerror(-r));
209                 return r;
210         }
211
212         printf("As string:\n"
213                SD_ID128_FORMAT_STR "\n\n"
214                "As UUID:\n"
215                "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
216                "As macro:\n"
217               "#define MESSAGE_XYZ SD_ID128_MAKE(",
218                SD_ID128_FORMAT_VAL(id),
219                SD_ID128_FORMAT_VAL(id));
220
221         for (i = 0; i < 16; i++)
222                 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
223
224         fputs(")\n", stdout);
225
226         return 0;
227 }
228
229 static int add_matches(sd_journal *j, char **args) {
230         char **i;
231         int r;
232
233         assert(j);
234
235         STRV_FOREACH(i, args) {
236
237                 if (streq(*i, "+"))
238                         r = sd_journal_add_disjunction(j);
239                 else if (path_is_absolute(*i)) {
240                         char *p;
241                         const char *path;
242                         struct stat st;
243
244                         p = canonicalize_file_name(*i);
245                         path = p ? p : *i;
246
247                         if (stat(path, &st) < 0)  {
248                                 free(p);
249                                 log_error("Couldn't stat file: %m");
250                                 return -errno;
251                         }
252
253                         if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
254                                 char *t;
255
256                                 t = strappend("_EXE=", path);
257                                 if (!t) {
258                                         free(p);
259                                         log_error("Out of memory.");
260                                         return -ENOMEM;
261                                 }
262
263                                 r = sd_journal_add_match(j, t, 0);
264                                 free(t);
265                         } else {
266                                 free(p);
267                                 log_error("File is not a regular file or is not executable: %s", *i);
268                                 return -EINVAL;
269                         }
270
271                         free(p);
272                 } else
273                         r = sd_journal_add_match(j, *i, 0);
274
275                 if (r < 0) {
276                         log_error("Failed to add match '%s': %s", *i, strerror(-r));
277                         return r;
278                 }
279         }
280
281         return 0;
282 }
283
284 static int add_this_boot(sd_journal *j) {
285         char match[9+32+1] = "_BOOT_ID=";
286         sd_id128_t boot_id;
287         int r;
288
289         if (!arg_this_boot)
290                 return 0;
291
292         r = sd_id128_get_boot(&boot_id);
293         if (r < 0) {
294                 log_error("Failed to get boot id: %s", strerror(-r));
295                 return r;
296         }
297
298         sd_id128_to_string(boot_id, match + 9);
299         r = sd_journal_add_match(j, match, strlen(match));
300         if (r < 0) {
301                 log_error("Failed to add match: %s", strerror(-r));
302                 return r;
303         }
304
305         return 0;
306 }
307
308 int main(int argc, char *argv[]) {
309         int r;
310         sd_journal *j = NULL;
311         unsigned line = 0;
312         bool need_seek = false;
313         sd_id128_t previous_boot_id;
314         bool previous_boot_id_valid = false;
315         bool have_pager;
316
317         log_parse_environment();
318         log_open();
319
320         r = parse_argv(argc, argv);
321         if (r <= 0)
322                 goto finish;
323
324         if (arg_new_id128) {
325                 r = generate_new_id128();
326                 goto finish;
327         }
328
329 #ifdef HAVE_ACL
330         if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
331                 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
332 #endif
333
334         if (arg_directory)
335                 r = sd_journal_open_directory(&j, arg_directory, 0);
336         else
337                 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
338
339         if (r < 0) {
340                 log_error("Failed to open journal: %s", strerror(-r));
341                 goto finish;
342         }
343
344         if (arg_print_header) {
345                 journal_print_header(j);
346                 r = 0;
347                 goto finish;
348         }
349
350         r = add_this_boot(j);
351         if (r < 0)
352                 goto finish;
353
354         r = add_matches(j, argv + optind);
355         if (r < 0)
356                 goto finish;
357
358         if (!arg_quiet) {
359                 usec_t start, end;
360                 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
361
362                 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
363                 if (r < 0) {
364                         log_error("Failed to get cutoff: %s", strerror(-r));
365                         goto finish;
366                 }
367
368                 if (r > 0) {
369                         if (arg_follow)
370                                 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
371                         else
372                                 printf("Logs begin at %s, end at %s.\n",
373                                        format_timestamp(start_buf, sizeof(start_buf), start),
374                                        format_timestamp(end_buf, sizeof(end_buf), end));
375                 }
376         }
377
378         if (arg_lines >= 0) {
379                 r = sd_journal_seek_tail(j);
380                 if (r < 0) {
381                         log_error("Failed to seek to tail: %s", strerror(-r));
382                         goto finish;
383                 }
384
385                 r = sd_journal_previous_skip(j, arg_lines);
386         } else {
387                 r = sd_journal_seek_head(j);
388                 if (r < 0) {
389                         log_error("Failed to seek to head: %s", strerror(-r));
390                         goto finish;
391                 }
392
393                 r = sd_journal_next(j);
394         }
395
396         if (r < 0) {
397                 log_error("Failed to iterate through journal: %s", strerror(-r));
398                 goto finish;
399         }
400
401         have_pager = !arg_no_pager && !arg_follow && pager_open();
402
403         if (arg_output == OUTPUT_JSON) {
404                 fputc('[', stdout);
405                 fflush(stdout);
406         }
407
408         for (;;) {
409                 for (;;) {
410                         sd_id128_t boot_id;
411                         int flags = (arg_show_all*OUTPUT_SHOW_ALL |
412                                      have_pager*OUTPUT_FULL_WIDTH);
413
414                         if (need_seek) {
415                                 r = sd_journal_next(j);
416                                 if (r < 0) {
417                                         log_error("Failed to iterate through journal: %s", strerror(-r));
418                                         goto finish;
419                                 }
420                         }
421
422                         if (r == 0)
423                                 break;
424
425                         r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
426                         if (r >= 0) {
427                                 if (previous_boot_id_valid &&
428                                     !sd_id128_equal(boot_id, previous_boot_id))
429                                         printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
430
431                                 previous_boot_id = boot_id;
432                                 previous_boot_id_valid = true;
433                         }
434
435                         line ++;
436
437                         r = output_journal(j, arg_output, line, 0, flags);
438                         if (r < 0)
439                                 goto finish;
440
441                         need_seek = true;
442                 }
443
444                 if (!arg_follow)
445                         break;
446
447                 r = sd_journal_wait(j, (uint64_t) -1);
448                 if (r < 0) {
449                         log_error("Couldn't wait for log event: %s", strerror(-r));
450                         goto finish;
451                 }
452         }
453
454         if (arg_output == OUTPUT_JSON)
455                 fputs("\n]\n", stdout);
456
457 finish:
458         if (j)
459                 sd_journal_close(j);
460
461         pager_close();
462
463         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
464 }