chiark / gitweb /
016750c20153cbaca13462d68526415ed051fdba
[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
316         log_parse_environment();
317         log_open();
318
319         r = parse_argv(argc, argv);
320         if (r <= 0)
321                 goto finish;
322
323         if (arg_new_id128) {
324                 r = generate_new_id128();
325                 goto finish;
326         }
327
328 #ifdef HAVE_ACL
329         if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
330                 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
331 #endif
332
333         if (arg_directory)
334                 r = sd_journal_open_directory(&j, arg_directory, 0);
335         else
336                 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
337
338         if (r < 0) {
339                 log_error("Failed to open journal: %s", strerror(-r));
340                 goto finish;
341         }
342
343         if (arg_print_header) {
344                 journal_print_header(j);
345                 r = 0;
346                 goto finish;
347         }
348
349         r = add_this_boot(j);
350         if (r < 0)
351                 goto finish;
352
353         r = add_matches(j, argv + optind);
354         if (r < 0)
355                 goto finish;
356
357         if (!arg_quiet) {
358                 usec_t start, end;
359                 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
360
361                 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
362                 if (r < 0) {
363                         log_error("Failed to get cutoff: %s", strerror(-r));
364                         goto finish;
365                 }
366
367                 if (r > 0) {
368                         if (arg_follow)
369                                 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
370                         else
371                                 printf("Logs begin at %s, end at %s.\n",
372                                        format_timestamp(start_buf, sizeof(start_buf), start),
373                                        format_timestamp(end_buf, sizeof(end_buf), end));
374                 }
375         }
376
377         if (arg_lines >= 0) {
378                 r = sd_journal_seek_tail(j);
379                 if (r < 0) {
380                         log_error("Failed to seek to tail: %s", strerror(-r));
381                         goto finish;
382                 }
383
384                 r = sd_journal_previous_skip(j, arg_lines);
385         } else {
386                 r = sd_journal_seek_head(j);
387                 if (r < 0) {
388                         log_error("Failed to seek to head: %s", strerror(-r));
389                         goto finish;
390                 }
391
392                 r = sd_journal_next(j);
393         }
394
395         if (r < 0) {
396                 log_error("Failed to iterate through journal: %s", strerror(-r));
397                 goto finish;
398         }
399
400         if (!arg_no_pager && !arg_follow) {
401                 columns();
402                 pager_open();
403         }
404
405         if (arg_output == OUTPUT_JSON) {
406                 fputc('[', stdout);
407                 fflush(stdout);
408         }
409
410         for (;;) {
411                 for (;;) {
412                         sd_id128_t boot_id;
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,
438                                            arg_show_all ? OUTPUT_SHOW_ALL : 0);
439                         if (r < 0)
440                                 goto finish;
441
442                         need_seek = true;
443                 }
444
445                 if (!arg_follow)
446                         break;
447
448                 r = sd_journal_wait(j, (uint64_t) -1);
449                 if (r < 0) {
450                         log_error("Couldn't wait for log event: %s", strerror(-r));
451                         goto finish;
452                 }
453         }
454
455         if (arg_output == OUTPUT_JSON)
456                 fputs("\n]\n", stdout);
457
458 finish:
459         if (j)
460                 sd_journal_close(j);
461
462         pager_close();
463
464         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
465 }