]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journalctl.c
e633dd34d77841437ee1e73a9e463cee2b076d2e
[thirdparty/systemd.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 }