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
;
58 static int help(void) {
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
);
82 static int parse_argv(int argc
, char *argv
[]) {
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
},
115 while ((c
= getopt_long(argc
, argv
, "hfo:an:qlbD:", options
, NULL
)) >= 0) {
124 puts(PACKAGE_STRING
);
126 puts(SYSTEMD_FEATURES
);
138 arg_output
= output_mode_from_string(optarg
);
139 if (arg_output
< 0) {
140 log_error("Unknown output '%s'.", optarg
);
151 r
= safe_atoi(optarg
, &arg_lines
);
152 if (r
< 0 || arg_lines
< 0) {
153 log_error("Failed to parse lines '%s'", optarg
);
163 arg_new_id128
= true;
175 arg_this_boot
= true;
179 arg_directory
= optarg
;
183 arg_print_header
= true;
190 log_error("Unknown option code %c", c
);
195 if (arg_follow
&& !arg_no_tail
&& arg_lines
< 0)
201 static bool on_tty(void) {
204 /* Note that this is invoked relatively early, before we start
205 * the pager. That means the value we return reflects whether
206 * we originally were started on a tty, not if we currently
207 * are. But this is intended, since we want colour and so on
208 * when run in our own pager. */
210 if (_unlikely_(t
< 0))
211 t
= isatty(STDOUT_FILENO
) > 0;
216 static int generate_new_id128(void) {
221 r
= sd_id128_randomize(&id
);
223 log_error("Failed to generate ID: %s", strerror(-r
));
227 printf("As string:\n"
228 SD_ID128_FORMAT_STR
"\n\n"
230 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
232 "#define MESSAGE_XYZ SD_ID128_MAKE(",
233 SD_ID128_FORMAT_VAL(id
),
234 SD_ID128_FORMAT_VAL(id
));
236 for (i
= 0; i
< 16; i
++)
237 printf("%02x%s", id
.bytes
[i
], i
!= 15 ? "," : "");
239 fputs(")\n", stdout
);
244 static int add_matches(sd_journal
*j
, char **args
) {
250 STRV_FOREACH(i
, args
) {
253 r
= sd_journal_add_disjunction(j
);
254 else if (path_is_absolute(*i
)) {
259 p
= canonicalize_file_name(*i
);
262 if (stat(path
, &st
) < 0) {
264 log_error("Couldn't stat file: %m");
268 if (S_ISREG(st
.st_mode
) && (0111 & st
.st_mode
)) {
271 t
= strappend("_EXE=", path
);
277 r
= sd_journal_add_match(j
, t
, 0);
281 log_error("File is not a regular file or is not executable: %s", *i
);
287 r
= sd_journal_add_match(j
, *i
, 0);
290 log_error("Failed to add match '%s': %s", *i
, strerror(-r
));
298 static int add_this_boot(sd_journal
*j
) {
299 char match
[9+32+1] = "_BOOT_ID=";
306 r
= sd_id128_get_boot(&boot_id
);
308 log_error("Failed to get boot id: %s", strerror(-r
));
312 sd_id128_to_string(boot_id
, match
+ 9);
313 r
= sd_journal_add_match(j
, match
, strlen(match
));
315 log_error("Failed to add match: %s", strerror(-r
));
322 int main(int argc
, char *argv
[]) {
324 sd_journal
*j
= NULL
;
326 bool need_seek
= false;
327 sd_id128_t previous_boot_id
;
328 bool previous_boot_id_valid
= false;
331 log_parse_environment();
334 r
= parse_argv(argc
, argv
);
339 r
= generate_new_id128();
344 if (!arg_quiet
&& geteuid() != 0 && in_group("adm") <= 0)
345 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
349 r
= sd_journal_open_directory(&j
, arg_directory
, 0);
351 r
= sd_journal_open(&j
, arg_local
? SD_JOURNAL_LOCAL_ONLY
: 0);
354 log_error("Failed to open journal: %s", strerror(-r
));
358 if (arg_print_header
) {
359 journal_print_header(j
);
364 r
= add_this_boot(j
);
368 r
= add_matches(j
, argv
+ optind
);
374 char start_buf
[FORMAT_TIMESTAMP_MAX
], end_buf
[FORMAT_TIMESTAMP_MAX
];
376 r
= sd_journal_get_cutoff_realtime_usec(j
, &start
, &end
);
378 log_error("Failed to get cutoff: %s", strerror(-r
));
384 printf("Logs begin at %s.\n", format_timestamp(start_buf
, sizeof(start_buf
), start
));
386 printf("Logs begin at %s, end at %s.\n",
387 format_timestamp(start_buf
, sizeof(start_buf
), start
),
388 format_timestamp(end_buf
, sizeof(end_buf
), end
));
392 if (arg_lines
>= 0) {
393 r
= sd_journal_seek_tail(j
);
395 log_error("Failed to seek to tail: %s", strerror(-r
));
399 r
= sd_journal_previous_skip(j
, arg_lines
);
401 r
= sd_journal_seek_head(j
);
403 log_error("Failed to seek to head: %s", strerror(-r
));
407 r
= sd_journal_next(j
);
411 log_error("Failed to iterate through journal: %s", strerror(-r
));
416 have_pager
= !arg_no_pager
&& !arg_follow
&& pager_open();
418 if (arg_output
== OUTPUT_JSON
) {
427 arg_show_all
* OUTPUT_SHOW_ALL
|
428 have_pager
* OUTPUT_FULL_WIDTH
|
429 on_tty() * OUTPUT_COLOR
;
432 r
= sd_journal_next(j
);
434 log_error("Failed to iterate through journal: %s", strerror(-r
));
442 r
= sd_journal_get_monotonic_usec(j
, NULL
, &boot_id
);
444 if (previous_boot_id_valid
&&
445 !sd_id128_equal(boot_id
, previous_boot_id
))
446 printf(ANSI_HIGHLIGHT_ON
"----- Reboot -----" ANSI_HIGHLIGHT_OFF
"\n");
448 previous_boot_id
= boot_id
;
449 previous_boot_id_valid
= true;
454 r
= output_journal(j
, arg_output
, line
, 0, flags
);
464 r
= sd_journal_wait(j
, (uint64_t) -1);
466 log_error("Couldn't wait for log event: %s", strerror(-r
));
471 if (arg_output
== OUTPUT_JSON
)
472 fputs("\n]\n", stdout
);
480 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;