journalctl.c revision 7560fffcd2531786b9c1ca657667a43e90331326
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <fcntl.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <getopt.h>
#include <systemd/sd-journal.h>
#include "log.h"
#include "util.h"
#include "path-util.h"
#include "build.h"
#include "pager.h"
#include "logs-show.h"
#include "strv.h"
#include "journal-internal.h"
#include "fsprg.h"
#include "journal-def.h"
static bool arg_follow = false;
static bool arg_show_all = false;
static bool arg_no_pager = false;
static int arg_lines = -1;
static bool arg_no_tail = false;
static bool arg_quiet = false;
static bool arg_local = false;
static bool arg_this_boot = false;
static const char *arg_directory = NULL;
static int arg_priorities = 0xFF;
static enum {
} arg_action = ACTION_SHOW;
static int help(void) {
printf("%s [OPTIONS...] [MATCH]\n\n"
"Send control commands to or query the journal.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" -a --all Show all fields, including long and unprintable\n"
" -f --follow Follow journal\n"
" -n --lines=INTEGER Journal entries to show\n"
" --no-tail Show all lines, even in follow mode\n"
" -o --output=STRING Change journal output mode (short, short-monotonic,\n"
" verbose, export, json, cat)\n"
" -q --quiet Don't show privilege warning\n"
" -l --local Only local entries\n"
" -b --this-boot Show data only from current boot\n"
" -D --directory=PATH Show journal files from directory\n"
" -p --priority=RANGE Show only messages within the specified priority range\n\n"
"Commands:\n"
" --new-id128 Generate a new 128 Bit id\n"
" --header Show journal header information\n"
" --setup-keys Generate new FSPRG key pair\n",
return 0;
}
enum {
ARG_VERSION = 0x100,
};
};
int c, r;
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
return 0;
case ARG_NO_PAGER:
arg_no_pager = true;
break;
case 'f':
arg_follow = true;
break;
case 'o':
if (arg_output < 0) {
return -EINVAL;
}
break;
case 'a':
arg_show_all = true;
break;
case 'n':
if (r < 0 || arg_lines < 0) {
return -EINVAL;
}
break;
case ARG_NO_TAIL:
arg_no_tail = true;
break;
case ARG_NEW_ID128:
break;
case 'q':
arg_quiet = true;
break;
case 'l':
arg_local = true;
break;
case 'b':
arg_this_boot = true;
break;
case 'D':
break;
case ARG_HEADER:
break;
case ARG_SETUP_KEYS:
break;
case 'p': {
const char *dots;
if (dots) {
char *a;
/* a range */
if (!a)
return log_oom();
from = log_level_from_string(a);
free(a);
return -EINVAL;
}
arg_priorities = 0;
arg_priorities |= 1 << i;
} else {
arg_priorities |= 1 << i;
}
} else {
int p, i;
p = log_level_from_string(optarg);
if (p < 0) {
return -EINVAL;
}
arg_priorities = 0;
for (i = 0; i <= p; i++)
arg_priorities |= 1 << i;
}
break;
}
case '?':
return -EINVAL;
default:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
arg_lines = 10;
return 1;
}
static bool on_tty(void) {
static int t = -1;
/* Note that this is invoked relatively early, before we start
* the pager. That means the value we return reflects whether
* we originally were started on a tty, not if we currently
* are. But this is intended, since we want colour and so on
* when run in our own pager. */
if (_unlikely_(t < 0))
t = isatty(STDOUT_FILENO) > 0;
return t;
}
static int generate_new_id128(void) {
int r;
unsigned i;
r = sd_id128_randomize(&id);
if (r < 0) {
return r;
}
printf("As string:\n"
SD_ID128_FORMAT_STR "\n\n"
"As UUID:\n"
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
"As macro:\n"
"#define MESSAGE_XYZ SD_ID128_MAKE(",
for (i = 0; i < 16; i++)
return 0;
}
char **i;
int r;
assert(j);
STRV_FOREACH(i, args) {
if (streq(*i, "+"))
r = sd_journal_add_disjunction(j);
else if (path_is_absolute(*i)) {
char *p, *t = NULL;
const char *path;
p = canonicalize_file_name(*i);
path = p ? p : *i;
free(p);
log_error("Couldn't stat file: %m");
return -errno;
}
else {
free(p);
log_error("File is not a device node, regular file or is not executable: %s", *i);
return -EINVAL;
}
free(p);
if (!t)
return log_oom();
r = sd_journal_add_match(j, t, 0);
free(t);
} else
r = sd_journal_add_match(j, *i, 0);
if (r < 0) {
return r;
}
}
return 0;
}
static int add_this_boot(sd_journal *j) {
int r;
assert(j);
if (!arg_this_boot)
return 0;
r = sd_id128_get_boot(&boot_id);
if (r < 0) {
return r;
}
if (r < 0) {
return r;
}
return 0;
}
static int add_priorities(sd_journal *j) {
char match[] = "PRIORITY=0";
int i, r;
assert(j);
if (arg_priorities == 0xFF)
return 0;
if (arg_priorities & (1 << i)) {
if (r < 0) {
return r;
}
}
return 0;
}
static int setup_keys(void) {
#ifdef HAVE_GCRYPT
ssize_t l;
int fd = -1, r;
struct FSPRGHeader h;
r = sd_id128_get_machine(&machine);
if (r < 0) {
return r;
}
r = sd_id128_get_boot(&boot);
if (r < 0) {
return r;
}
SD_ID128_FORMAT_VAL(machine)) < 0)
return log_oom();
log_error("Evolving key file %s exists already.", p);
r = -EEXIST;
goto finish;
}
SD_ID128_FORMAT_VAL(machine)) < 0) {
r = log_oom();
goto finish;
}
if (fd < 0) {
r = -errno;
goto finish;
}
log_info("Generating seed...");
r = -EIO;
goto finish;
}
log_info("Generating key pair...");
log_info("Generating evolving key...");
n = now(CLOCK_REALTIME);
n /= interval;
if (fd < 0) {
log_error("Failed to open %s: %m", k);
r = -errno;
goto finish;
}
zero(h);
h.machine_id = machine;
h.header_size = htole64(sizeof(h));
l = loop_write(fd, &h, sizeof(h), false);
if (l < 0 || (size_t) l != sizeof(h)) {
r = -EIO;
goto finish;
}
if (l < 0 || (size_t) l != state_size) {
r = -EIO;
goto finish;
}
if (link(k, p) < 0) {
log_error("Failed to link file: %m");
r = -errno;
goto finish;
}
if (isatty(STDOUT_FILENO)) {
"\n"
"The new key pair has been generated. The evolving key has been written to the\n"
"following file. It will be used to protect local journal files. This file does\n"
"not need to be kept secret. It should not be used on multiple hosts.\n"
"\n"
"\t%s\n"
"\n"
"Please write down the following " ANSI_HIGHLIGHT_ON "secret" ANSI_HIGHLIGHT_OFF " seed value. It should not be stored\n"
"locally on disk, and may be used to verify journal files from this host.\n"
"\n\t" ANSI_HIGHLIGHT_RED_ON, p);
}
for (i = 0; i < seed_size; i++) {
if (i > 0 && i % 3 == 0)
putchar('-');
}
if (isatty(STDOUT_FILENO))
r = 0;
if (fd >= 0)
if (k) {
unlink(k);
free(k);
}
free(p);
return r;
#else
log_error("Forward-secure journal verification not available.");
#endif
}
int r;
sd_journal *j = NULL;
unsigned line = 0;
bool need_seek = false;
bool previous_boot_id_valid = false;
bool have_pager;
log_open();
if (r <= 0)
goto finish;
if (arg_action == ACTION_NEW_ID128) {
r = generate_new_id128();
goto finish;
}
if (arg_action == ACTION_SETUP_KEYS) {
r = setup_keys();
goto finish;
}
#ifdef HAVE_ACL
log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
#endif
if (arg_directory)
r = sd_journal_open_directory(&j, arg_directory, 0);
else
if (r < 0) {
goto finish;
}
if (arg_action == ACTION_PRINT_HEADER) {
r = 0;
goto finish;
}
r = add_this_boot(j);
if (r < 0)
goto finish;
if (r < 0)
goto finish;
r = add_priorities(j);
if (r < 0)
goto finish;
if (!arg_quiet) {
if (r < 0) {
goto finish;
}
if (r > 0) {
if (arg_follow)
else
printf("Logs begin at %s, end at %s.\n",
}
}
if (arg_lines >= 0) {
r = sd_journal_seek_tail(j);
if (r < 0) {
goto finish;
}
r = sd_journal_previous_skip(j, arg_lines);
} else {
r = sd_journal_seek_head(j);
if (r < 0) {
goto finish;
}
r = sd_journal_next(j);
}
if (r < 0) {
goto finish;
}
on_tty();
if (arg_output == OUTPUT_JSON) {
}
for (;;) {
for (;;) {
int flags =
on_tty() * OUTPUT_COLOR;
if (need_seek) {
r = sd_journal_next(j);
if (r < 0) {
goto finish;
}
}
if (r == 0)
break;
if (r >= 0) {
if (previous_boot_id_valid &&
previous_boot_id_valid = true;
}
line ++;
if (r < 0)
goto finish;
need_seek = true;
}
if (!arg_follow)
break;
if (r < 0) {
goto finish;
}
}
if (arg_output == OUTPUT_JSON)
if (j)
sd_journal_close(j);
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}