1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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.
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.
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/>.
34 #include <systemd/sd-journal.h>
38 #include "path-util.h"
41 #include "logs-show.h"
43 #include "journal-internal.h"
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 static int arg_priorities = 0xFF;
59 static int help(void) {
61 printf("%s [OPTIONS...] [MATCH]\n\n"
62 "Send control commands to or query the journal.\n\n"
63 " -h --help Show this help\n"
64 " --version Show package version\n"
65 " --no-pager Do not pipe output into a pager\n"
66 " -a --all Show all fields, including long and unprintable\n"
67 " -f --follow Follow journal\n"
68 " -n --lines=INTEGER Journal entries to show\n"
69 " --no-tail Show all lines, even in follow mode\n"
70 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
71 " verbose, export, json, cat)\n"
72 " -q --quiet Don't show privilege warning\n"
73 " -l --local Only local entries\n"
74 " -b --this-boot Show data only from current boot\n"
75 " -D --directory=PATH Show journal files from directory\n"
76 " -p --priority=RANGE Show only messages within the specified priority range\n"
77 " --header Show journal header information\n"
78 " --new-id128 Generate a new 128 Bit id\n",
79 program_invocation_short_name);
84 static int parse_argv(int argc, char *argv[]) {
94 static const struct option options[] = {
95 { "help", no_argument, NULL, 'h' },
96 { "version" , no_argument, NULL, ARG_VERSION },
97 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
98 { "follow", no_argument, NULL, 'f' },
99 { "output", required_argument, NULL, 'o' },
100 { "all", no_argument, NULL, 'a' },
101 { "lines", required_argument, NULL, 'n' },
102 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
103 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
104 { "quiet", no_argument, NULL, 'q' },
105 { "local", no_argument, NULL, 'l' },
106 { "this-boot", no_argument, NULL, 'b' },
107 { "directory", required_argument, NULL, 'D' },
108 { "header", no_argument, NULL, ARG_HEADER },
109 { "priority", no_argument, NULL, 'p' },
118 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
127 puts(PACKAGE_STRING);
129 puts(SYSTEMD_FEATURES);
141 arg_output = output_mode_from_string(optarg);
142 if (arg_output < 0) {
143 log_error("Unknown output '%s'.", optarg);
154 r = safe_atoi(optarg, &arg_lines);
155 if (r < 0 || arg_lines < 0) {
156 log_error("Failed to parse lines '%s'", optarg);
166 arg_new_id128 = true;
178 arg_this_boot = true;
182 arg_directory = optarg;
186 arg_print_header = true;
192 dots = strstr(optarg, "..");
198 a = strndup(optarg, dots - optarg);
202 from = log_level_from_string(a);
203 to = log_level_from_string(dots + 2);
206 if (from < 0 || to < 0) {
207 log_error("Failed to parse log level range %s", optarg);
214 for (i = from; i <= to; i++)
215 arg_priorities |= 1 << i;
217 for (i = to; i <= from; i++)
218 arg_priorities |= 1 << i;
224 p = log_level_from_string(optarg);
226 log_error("Unknown log level %s", optarg);
232 for (i = 0; i <= p; i++)
233 arg_priorities |= 1 << i;
243 log_error("Unknown option code %c", c);
248 if (arg_follow && !arg_no_tail && arg_lines < 0)
254 static bool on_tty(void) {
257 /* Note that this is invoked relatively early, before we start
258 * the pager. That means the value we return reflects whether
259 * we originally were started on a tty, not if we currently
260 * are. But this is intended, since we want colour and so on
261 * when run in our own pager. */
263 if (_unlikely_(t < 0))
264 t = isatty(STDOUT_FILENO) > 0;
269 static int generate_new_id128(void) {
274 r = sd_id128_randomize(&id);
276 log_error("Failed to generate ID: %s", strerror(-r));
280 printf("As string:\n"
281 SD_ID128_FORMAT_STR "\n\n"
283 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
285 "#define MESSAGE_XYZ SD_ID128_MAKE(",
286 SD_ID128_FORMAT_VAL(id),
287 SD_ID128_FORMAT_VAL(id));
289 for (i = 0; i < 16; i++)
290 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
292 fputs(")\n", stdout);
297 static int add_matches(sd_journal *j, char **args) {
303 STRV_FOREACH(i, args) {
306 r = sd_journal_add_disjunction(j);
307 else if (path_is_absolute(*i)) {
312 p = canonicalize_file_name(*i);
315 if (stat(path, &st) < 0) {
317 log_error("Couldn't stat file: %m");
321 if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
322 t = strappend("_EXE=", path);
323 else if (S_ISCHR(st.st_mode))
324 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
325 else if (S_ISBLK(st.st_mode))
326 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
329 log_error("File is not a device node, regular file or is not executable: %s", *i);
338 r = sd_journal_add_match(j, t, 0);
341 r = sd_journal_add_match(j, *i, 0);
344 log_error("Failed to add match '%s': %s", *i, strerror(-r));
352 static int add_this_boot(sd_journal *j) {
353 char match[9+32+1] = "_BOOT_ID=";
362 r = sd_id128_get_boot(&boot_id);
364 log_error("Failed to get boot id: %s", strerror(-r));
368 sd_id128_to_string(boot_id, match + 9);
369 r = sd_journal_add_match(j, match, strlen(match));
371 log_error("Failed to add match: %s", strerror(-r));
378 static int add_priorities(sd_journal *j) {
379 char match[] = "PRIORITY=0";
384 if (arg_priorities == 0xFF)
387 for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
388 if (arg_priorities & (1 << i)) {
389 match[sizeof(match)-2] = '0' + i;
391 log_info("adding match %s", match);
393 r = sd_journal_add_match(j, match, strlen(match));
395 log_error("Failed to add match: %s", strerror(-r));
403 int main(int argc, char *argv[]) {
405 sd_journal *j = NULL;
407 bool need_seek = false;
408 sd_id128_t previous_boot_id;
409 bool previous_boot_id_valid = false;
412 log_parse_environment();
415 r = parse_argv(argc, argv);
420 r = generate_new_id128();
425 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
426 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
430 r = sd_journal_open_directory(&j, arg_directory, 0);
432 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
435 log_error("Failed to open journal: %s", strerror(-r));
439 if (arg_print_header) {
440 journal_print_header(j);
445 r = add_this_boot(j);
449 r = add_matches(j, argv + optind);
453 r = add_priorities(j);
459 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
461 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
463 log_error("Failed to get cutoff: %s", strerror(-r));
469 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
471 printf("Logs begin at %s, end at %s.\n",
472 format_timestamp(start_buf, sizeof(start_buf), start),
473 format_timestamp(end_buf, sizeof(end_buf), end));
477 if (arg_lines >= 0) {
478 r = sd_journal_seek_tail(j);
480 log_error("Failed to seek to tail: %s", strerror(-r));
484 r = sd_journal_previous_skip(j, arg_lines);
486 r = sd_journal_seek_head(j);
488 log_error("Failed to seek to head: %s", strerror(-r));
492 r = sd_journal_next(j);
496 log_error("Failed to iterate through journal: %s", strerror(-r));
501 have_pager = !arg_no_pager && !arg_follow && pager_open();
503 if (arg_output == OUTPUT_JSON) {
512 arg_show_all * OUTPUT_SHOW_ALL |
513 have_pager * OUTPUT_FULL_WIDTH |
514 on_tty() * OUTPUT_COLOR;
517 r = sd_journal_next(j);
519 log_error("Failed to iterate through journal: %s", strerror(-r));
527 r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
529 if (previous_boot_id_valid &&
530 !sd_id128_equal(boot_id, previous_boot_id))
531 printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
533 previous_boot_id = boot_id;
534 previous_boot_id_valid = true;
539 r = output_journal(j, arg_output, line, 0, flags);
549 r = sd_journal_wait(j, (uint64_t) -1);
551 log_error("Couldn't wait for log event: %s", strerror(-r));
556 if (arg_output == OUTPUT_JSON)
557 fputs("\n]\n", stdout);
565 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;