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