journalctl.c revision a54e3b3d6a4fe28a39729f4c5ad11cd26706110c
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen/***
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen This file is part of systemd.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen Copyright 2011 Lennart Poettering
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen systemd is free software; you can redistribute it and/or modify it
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen under the terms of the GNU Lesser General Public License as published by
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen (at your option) any later version.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen systemd is distributed in the hope that it will be useful, but
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen Lesser General Public License for more details.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen You should have received a copy of the GNU Lesser General Public License
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen***/
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <locale.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <fcntl.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <errno.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <stddef.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <string.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <stdio.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <unistd.h>
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen#include <stdlib.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <time.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <getopt.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <signal.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <sys/stat.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <sys/ioctl.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <linux/fs.h>
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#ifdef HAVE_ACL
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <sys/acl.h>
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include "acl-util.h"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#endif
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#include <systemd/sd-journal.h>
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "log.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "logs-show.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "util.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "path-util.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "build.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "pager.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "logs-show.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "strv.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "journal-internal.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "journal-def.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "journal-verify.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "journal-authenticate.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "journal-qrcode.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "fsprg.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "unit-name.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#include "catalog.h"
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic OutputMode arg_output = OUTPUT_SHORT;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic bool arg_pager_end = false;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic bool arg_follow = false;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic bool arg_full = false;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic bool arg_all = false;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic bool arg_no_pager = false;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic int arg_lines = -1;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic bool arg_no_tail = false;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic bool arg_quiet = false;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic bool arg_merge = false;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersenstatic bool arg_boot = false;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic char *arg_boot_descriptor = NULL;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic bool arg_dmesg = false;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic const char *arg_cursor = NULL;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic const char *arg_after_cursor = NULL;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic bool arg_show_cursor = false;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic const char *arg_directory = NULL;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic char **arg_file = NULL;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic int arg_priorities = 0xFF;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic const char *arg_verify_key = NULL;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen#ifdef HAVE_GCRYPT
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic bool arg_force = false;
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen#endif
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersenstatic usec_t arg_since, arg_until;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic bool arg_since_set = false, arg_until_set = false;
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersenstatic char **arg_system_units = NULL;
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersenstatic char **arg_user_units = NULL;
3bef724f7e7f7eaca69881548b06e221b77d7031Tom Gundersenstatic const char *arg_field = NULL;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic bool arg_catalog = false;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersenstatic bool arg_reverse = false;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersenstatic int arg_journal_type = 0;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersenstatic const char *arg_root = NULL;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic enum {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ACTION_SHOW,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ACTION_NEW_ID128,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ACTION_PRINT_HEADER,
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen ACTION_SETUP_KEYS,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ACTION_VERIFY,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ACTION_DISK_USAGE,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ACTION_LIST_CATALOG,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ACTION_DUMP_CATALOG,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ACTION_UPDATE_CATALOG
8cd11a0f0f4ca05199e1166f6a07472b296f7455Tom Gundersen} arg_action = ACTION_SHOW;
8cd11a0f0f4ca05199e1166f6a07472b296f7455Tom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersentypedef struct boot_id_t {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen sd_id128_t id;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen uint64_t timestamp;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen} boot_id_t;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersenstatic int help(void) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen printf("%s [OPTIONS...] [MATCHES...]\n\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen "Query the journal.\n\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen "Flags:\n"
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen " --system Show only the system journal\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --user Show only the user journal for current user\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --since=DATE Start showing entries newer or of the specified date\n"
b3070dc0258831c7e2b13624f75fa3dbd80d9833Tom Gundersen " --until=DATE Stop showing entries older or of the specified date\n"
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen " -c --cursor=CURSOR Start showing entries from specified cursor\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --after-cursor=CURSOR Start showing entries from specified cursor\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --show-cursor Print the cursor after all the entries\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " -b --boot[=ID] Show data only from ID or current boot if unspecified\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " -k --dmesg Show kernel message log from current boot\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " -u --unit=UNIT Show data only from the specified unit\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --user-unit=UNIT Show data only from the specified user session unit\n"
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen " -p --priority=RANGE Show only messages within the specified priority range\n"
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen " -e --pager-end Immediately jump to end of the journal in the pager\n"
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen " -f --follow Follow journal\n"
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen " -n --lines[=INTEGER] Number of journal entries to show\n"
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen " --no-tail Show all lines, even in follow mode\n"
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen " -r --reverse Show the newest entries first\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " verbose, export, json, json-pretty, json-sse, cat)\n"
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen " -x --catalog Add message explanations where available\n"
ef1ba6065c6ccea94d4ee867f36df7bbc53a5224Tom Gundersen " -l --full Do not ellipsize fields\n"
ef1ba6065c6ccea94d4ee867f36df7bbc53a5224Tom Gundersen " -a --all Show all fields, including long and unprintable\n"
ef1ba6065c6ccea94d4ee867f36df7bbc53a5224Tom Gundersen " -q --quiet Don't show privilege warning\n"
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen " --no-pager Do not pipe output into a pager\n"
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen " -m --merge Show entries from all available journals\n"
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen " -D --directory=PATH Show journal files from directory\n"
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen " --file=PATH Show journal file\n"
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen " --root=ROOT Operate on catalog files underneath the root ROOT\n"
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen#ifdef HAVE_GCRYPT
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --interval=TIME Time interval for changing the FSS sealing key\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --verify-key=KEY Specify FSS verification key\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --force Force overriding new FSS key pair with --setup-keys\n"
0617ffabe86a6d366252477eafbe59a888b149d4Tom Gundersen#endif
c166a070553511e402de5ad216d3fb62b49bdacbTom Gundersen "\nCommands:\n"
8cd11a0f0f4ca05199e1166f6a07472b296f7455Tom Gundersen " -h --help Show this help\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --version Show package version\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --new-id128 Generate a new 128 Bit ID\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --header Show journal header information\n"
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen " --disk-usage Show total disk usage\n"
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen " -F --field=FIELD List all values a certain field takes\n"
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen " --list-catalog Show message IDs of all entries in the message catalog\n"
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen " --dump-catalog Show entries in the message catalog\n"
3bef724f7e7f7eaca69881548b06e221b77d7031Tom Gundersen " --update-catalog Update the message catalog database\n"
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen#ifdef HAVE_GCRYPT
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen " --setup-keys Generate new FSS key pair\n"
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen " --verify Verify journal file consistency\n"
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen#endif
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen , program_invocation_short_name);
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen
f5be560181d092c5f52a2b819aedcd48220f36abTom Gundersen return 0;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen}
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersenstatic int parse_argv(int argc, char *argv[]) {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen enum {
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_VERSION = 0x100,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_NO_PAGER,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_NO_TAIL,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_NEW_ID128,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_USER,
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen ARG_SYSTEM,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_ROOT,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_HEADER,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_SETUP_KEYS,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_FILE,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_INTERVAL,
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek ARG_VERIFY,
2ad8416dd057e7e3185169609ca3006e7649f576Zbigniew Jędrzejewski-Szmek ARG_VERIFY_KEY,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_DISK_USAGE,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_SINCE,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_UNTIL,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_AFTER_CURSOR,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_SHOW_CURSOR,
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen ARG_USER_UNIT,
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen ARG_LIST_CATALOG,
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen ARG_DUMP_CATALOG,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_UPDATE_CATALOG,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen ARG_FORCE,
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen };
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen static const struct option options[] = {
3bef724f7e7f7eaca69881548b06e221b77d7031Tom Gundersen { "help", no_argument, NULL, 'h' },
3bef724f7e7f7eaca69881548b06e221b77d7031Tom Gundersen { "version" , no_argument, NULL, ARG_VERSION },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "no-pager", no_argument, NULL, ARG_NO_PAGER },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "pager-end", no_argument, NULL, 'e' },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "follow", no_argument, NULL, 'f' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "force", no_argument, NULL, ARG_FORCE },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "output", required_argument, NULL, 'o' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "all", no_argument, NULL, 'a' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "full", no_argument, NULL, 'l' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "lines", optional_argument, NULL, 'n' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "no-tail", no_argument, NULL, ARG_NO_TAIL },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "quiet", no_argument, NULL, 'q' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "merge", no_argument, NULL, 'm' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "boot", optional_argument, NULL, 'b' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "this-boot", optional_argument, NULL, 'b' }, /* deprecated */
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "dmesg", no_argument, NULL, 'k' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "system", no_argument, NULL, ARG_SYSTEM },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "user", no_argument, NULL, ARG_USER },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "directory", required_argument, NULL, 'D' },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "file", required_argument, NULL, ARG_FILE },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "root", required_argument, NULL, ARG_ROOT },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "header", no_argument, NULL, ARG_HEADER },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "priority", required_argument, NULL, 'p' },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "interval", required_argument, NULL, ARG_INTERVAL },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "verify", no_argument, NULL, ARG_VERIFY },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "verify-key", required_argument, NULL, ARG_VERIFY_KEY },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "disk-usage", no_argument, NULL, ARG_DISK_USAGE },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "cursor", required_argument, NULL, 'c' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "since", required_argument, NULL, ARG_SINCE },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "until", required_argument, NULL, ARG_UNTIL },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "unit", required_argument, NULL, 'u' },
02b59d57e0c08231645120077f651151f5bb2babTom Gundersen { "user-unit", required_argument, NULL, ARG_USER_UNIT },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "field", required_argument, NULL, 'F' },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "catalog", no_argument, NULL, 'x' },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG },
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen { "reverse", no_argument, NULL, 'r' },
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen { NULL, 0, NULL, 0 }
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen };
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen int c, r;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen assert(argc >= 0);
71a6151083d842b2f5bf04e50239f0bf85d34d2eTom Gundersen assert(argv);
71a6151083d842b2f5bf04e50239f0bf85d34d2eTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:u:F:xr", options, NULL)) >= 0) {
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen switch (c) {
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen case 'h':
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen help();
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen return 0;
f048a16b464295a4e0a4f4c1210f06343ad31231Tom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen case ARG_VERSION:
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen puts(PACKAGE_STRING);
407fe036a24834203aca6c1eec7d74d9ad3e9ee0Tom Gundersen puts(SYSTEMD_FEATURES);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return 0;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen case ARG_NO_PAGER:
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen arg_no_pager = true;
3bef724f7e7f7eaca69881548b06e221b77d7031Tom Gundersen break;
3bef724f7e7f7eaca69881548b06e221b77d7031Tom Gundersen
3bef724f7e7f7eaca69881548b06e221b77d7031Tom Gundersen case 'e':
3bef724f7e7f7eaca69881548b06e221b77d7031Tom Gundersen arg_pager_end = true;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
71a6151083d842b2f5bf04e50239f0bf85d34d2eTom Gundersen if (arg_lines < 0)
71a6151083d842b2f5bf04e50239f0bf85d34d2eTom Gundersen arg_lines = 1000;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen break;
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen case 'f':
6ae115c1fe95611b39d2f20cfcea3d385429f59eTom Gundersen arg_follow = true;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen break;
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen case 'o':
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen arg_output = output_mode_from_string(optarg);
aa3437a573ed2a2b2a03c9649fe11d27b40a772bTom Gundersen if (arg_output < 0) {
f882c247ad59776c3a7753bb963c1f8e2386cb79Tom Gundersen log_error("Unknown output format '%s'.", optarg);
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen return -EINVAL;
22936833e177d9e477e059ec2b4f4fa9213b4e19Tom Gundersen }
dd3efc0993b6e95ff04775e9125e2fc9d30fe261Tom Gundersen
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen if (arg_output == OUTPUT_EXPORT ||
f579559b3a14c1f1ef96c372e7626c4733e6ef7dTom Gundersen arg_output == OUTPUT_JSON ||
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek arg_output == OUTPUT_JSON_PRETTY ||
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek arg_output == OUTPUT_JSON_SSE ||
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek arg_output == OUTPUT_CAT)
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek arg_quiet = true;
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek break;
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek case 'l':
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek arg_full = true;
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek break;
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek case 'a':
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek arg_all = true;
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek break;
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek case 'n':
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek if (optarg) {
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek r = safe_atoi(optarg, &arg_lines);
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek if (r < 0 || arg_lines < 0) {
39032b87779323a244dd89f4832949d462b2ac68Zbigniew Jędrzejewski-Szmek log_error("Failed to parse lines '%s'", optarg);
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek return -EINVAL;
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek }
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek } else {
3333d748facc15f49935b6b793490ba0824976e6Zbigniew Jędrzejewski-Szmek int n;
628706137efbca8aaf753ccd063e5abf7e31aed5Zbigniew Jędrzejewski-Szmek
628706137efbca8aaf753ccd063e5abf7e31aed5Zbigniew Jędrzejewski-Szmek /* Hmm, no argument? Maybe the next
628706137efbca8aaf753ccd063e5abf7e31aed5Zbigniew Jędrzejewski-Szmek * word on the command line is
628706137efbca8aaf753ccd063e5abf7e31aed5Zbigniew Jędrzejewski-Szmek * supposed to be the argument? Let's
628706137efbca8aaf753ccd063e5abf7e31aed5Zbigniew Jędrzejewski-Szmek * see if there is one, and is
* parsable as a positive
* integer... */
if (optind < argc &&
safe_atoi(argv[optind], &n) >= 0 &&
n >= 0) {
arg_lines = n;
optind++;
} else
arg_lines = 10;
}
break;
case ARG_NO_TAIL:
arg_no_tail = true;
break;
case ARG_NEW_ID128:
arg_action = ACTION_NEW_ID128;
break;
case 'q':
arg_quiet = true;
break;
case 'm':
arg_merge = true;
break;
case 'b':
arg_boot = true;
if (optarg)
arg_boot_descriptor = optarg;
else if (optind < argc) {
int boot;
if (argv[optind][0] != '-' ||
safe_atoi(argv[optind], &boot) >= 0) {
arg_boot_descriptor = argv[optind];
optind++;
}
}
break;
case 'k':
arg_boot = arg_dmesg = true;
break;
case ARG_SYSTEM:
arg_journal_type |= SD_JOURNAL_SYSTEM;
break;
case ARG_USER:
arg_journal_type |= SD_JOURNAL_CURRENT_USER;
break;
case 'D':
arg_directory = optarg;
break;
case ARG_FILE:
r = glob_extend(&arg_file, optarg);
if (r < 0) {
log_error("Failed to add paths: %s", strerror(-r));
return r;
};
break;
case ARG_ROOT:
arg_root = optarg;
break;
case 'c':
arg_cursor = optarg;
break;
case ARG_AFTER_CURSOR:
arg_after_cursor = optarg;
break;
case ARG_SHOW_CURSOR:
arg_show_cursor = true;
break;
case ARG_HEADER:
arg_action = ACTION_PRINT_HEADER;
break;
case ARG_VERIFY:
arg_action = ACTION_VERIFY;
break;
case ARG_DISK_USAGE:
arg_action = ACTION_DISK_USAGE;
break;
#ifdef HAVE_GCRYPT
case ARG_FORCE:
arg_force = true;
break;
case ARG_SETUP_KEYS:
arg_action = ACTION_SETUP_KEYS;
break;
case ARG_VERIFY_KEY:
arg_action = ACTION_VERIFY;
arg_verify_key = optarg;
arg_merge = false;
break;
case ARG_INTERVAL:
r = parse_sec(optarg, &arg_interval);
if (r < 0 || arg_interval <= 0) {
log_error("Failed to parse sealing key change interval: %s", optarg);
return -EINVAL;
}
break;
#else
case ARG_SETUP_KEYS:
case ARG_VERIFY_KEY:
case ARG_INTERVAL:
case ARG_FORCE:
log_error("Forward-secure sealing not available.");
return -ENOTSUP;
#endif
case 'p': {
const char *dots;
dots = strstr(optarg, "..");
if (dots) {
char *a;
int from, to, i;
/* a range */
a = strndup(optarg, dots - optarg);
if (!a)
return log_oom();
from = log_level_from_string(a);
to = log_level_from_string(dots + 2);
free(a);
if (from < 0 || to < 0) {
log_error("Failed to parse log level range %s", optarg);
return -EINVAL;
}
arg_priorities = 0;
if (from < to) {
for (i = from; i <= to; i++)
arg_priorities |= 1 << i;
} else {
for (i = to; i <= from; i++)
arg_priorities |= 1 << i;
}
} else {
int p, i;
p = log_level_from_string(optarg);
if (p < 0) {
log_error("Unknown log level %s", optarg);
return -EINVAL;
}
arg_priorities = 0;
for (i = 0; i <= p; i++)
arg_priorities |= 1 << i;
}
break;
}
case ARG_SINCE:
r = parse_timestamp(optarg, &arg_since);
if (r < 0) {
log_error("Failed to parse timestamp: %s", optarg);
return -EINVAL;
}
arg_since_set = true;
break;
case ARG_UNTIL:
r = parse_timestamp(optarg, &arg_until);
if (r < 0) {
log_error("Failed to parse timestamp: %s", optarg);
return -EINVAL;
}
arg_until_set = true;
break;
case 'u':
r = strv_extend(&arg_system_units, optarg);
if (r < 0)
return log_oom();
break;
case ARG_USER_UNIT:
r = strv_extend(&arg_user_units, optarg);
if (r < 0)
return log_oom();
break;
case '?':
return -EINVAL;
case 'F':
arg_field = optarg;
break;
case 'x':
arg_catalog = true;
break;
case ARG_LIST_CATALOG:
arg_action = ACTION_LIST_CATALOG;
break;
case ARG_DUMP_CATALOG:
arg_action = ACTION_DUMP_CATALOG;
break;
case ARG_UPDATE_CATALOG:
arg_action = ACTION_UPDATE_CATALOG;
break;
case 'r':
arg_reverse = true;
break;
default:
log_error("Unknown option code %c", c);
return -EINVAL;
}
}
if (arg_follow && !arg_no_tail && arg_lines < 0)
arg_lines = 10;
if (arg_directory && arg_file) {
log_error("Please specify either -D/--directory= or --file=, not both.");
return -EINVAL;
}
if (arg_since_set && arg_until_set && arg_since > arg_until) {
log_error("--since= must be before --until=.");
return -EINVAL;
}
if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1) {
log_error("Please specify only one of --since=, --cursor=, and --after-cursor.");
return -EINVAL;
}
if (arg_follow && arg_reverse) {
log_error("Please specify either --reverse= or --follow=, not both.");
return -EINVAL;
}
return 1;
}
static int generate_new_id128(void) {
sd_id128_t id;
int r;
unsigned i;
r = sd_id128_randomize(&id);
if (r < 0) {
log_error("Failed to generate ID: %s", strerror(-r));
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(",
SD_ID128_FORMAT_VAL(id),
SD_ID128_FORMAT_VAL(id));
for (i = 0; i < 16; i++)
printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
fputs(")\n\n", stdout);
printf("As Python constant:\n"
">>> import uuid\n"
">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n",
SD_ID128_FORMAT_VAL(id));
return 0;
}
static int add_matches(sd_journal *j, char **args) {
char **i;
assert(j);
STRV_FOREACH(i, args) {
int r;
if (streq(*i, "+"))
r = sd_journal_add_disjunction(j);
else if (path_is_absolute(*i)) {
_cleanup_free_ char *p, *t = NULL;
const char *path;
struct stat st;
p = canonicalize_file_name(*i);
path = p ? p : *i;
if (stat(path, &st) < 0) {
log_error("Couldn't stat file: %m");
return -errno;
}
if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
t = strappend("_EXE=", path);
else if (S_ISCHR(st.st_mode))
asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
else if (S_ISBLK(st.st_mode))
asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
else {
log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
return -EINVAL;
}
if (!t)
return log_oom();
r = sd_journal_add_match(j, t, 0);
} else
r = sd_journal_add_match(j, *i, 0);
if (r < 0) {
log_error("Failed to add match '%s': %s", *i, strerror(-r));
return r;
}
}
return 0;
}
static int boot_id_cmp(const void *a, const void *b) {
uint64_t _a, _b;
_a = ((const boot_id_t *)a)->timestamp;
_b = ((const boot_id_t *)b)->timestamp;
return _a < _b ? -1 : (_a > _b ? 1 : 0);
}
static int get_relative_boot_id(sd_journal *j, sd_id128_t *boot_id, int relative) {
int r;
const void *data;
unsigned int count = 0;
size_t length, allocated = 0;
boot_id_t ref_boot_id = {SD_ID128_NULL}, *id;
_cleanup_free_ boot_id_t *all_ids = NULL;
assert(j);
assert(boot_id);
if (relative == 0 && !sd_id128_equal(*boot_id, SD_ID128_NULL))
return 0;
r = sd_journal_query_unique(j, "_BOOT_ID");
if (r < 0)
return r;
SD_JOURNAL_FOREACH_UNIQUE(j, data, length) {
if (length < strlen("_BOOT_ID="))
continue;
if (!GREEDY_REALLOC(all_ids, allocated, count + 1))
return log_oom();
id = &all_ids[count];
r = sd_id128_from_string(((const char *)data) + strlen("_BOOT_ID="), &id->id);
if (r < 0)
continue;
r = sd_journal_add_match(j, data, length);
if (r < 0)
return r;
r = sd_journal_seek_head(j);
if (r < 0)
return r;
r = sd_journal_next(j);
if (r < 0)
return r;
else if (r == 0)
goto flush;
r = sd_journal_get_realtime_usec(j, &id->timestamp);
if (r < 0)
return r;
if (sd_id128_equal(id->id, *boot_id))
ref_boot_id = *id;
count++;
flush:
sd_journal_flush_matches(j);
}
qsort(all_ids, count, sizeof(boot_id_t), boot_id_cmp);
if (sd_id128_equal(*boot_id, SD_ID128_NULL)) {
if (relative > (int) count || relative <= -(int)count)
return -EADDRNOTAVAIL;
*boot_id = all_ids[(relative <= 0)*count + relative - 1].id;
} else {
id = bsearch(&ref_boot_id, all_ids, count, sizeof(boot_id_t), boot_id_cmp);
if (!id ||
relative <= 0 ? (id - all_ids) + relative < 0 :
(id - all_ids) + relative >= (int) count)
return -EADDRNOTAVAIL;
*boot_id = (id + relative)->id;
}
return 0;
}
static int add_boot(sd_journal *j) {
char match[9+32+1] = "_BOOT_ID=";
char *offset;
sd_id128_t boot_id = SD_ID128_NULL;
int r, relative = 0;
assert(j);
if (!arg_boot)
return 0;
if (!arg_boot_descriptor)
return add_match_this_boot(j);
if (strlen(arg_boot_descriptor) >= 32) {
char tmp = arg_boot_descriptor[32];
arg_boot_descriptor[32] = '\0';
r = sd_id128_from_string(arg_boot_descriptor, &boot_id);
arg_boot_descriptor[32] = tmp;
if (r < 0) {
log_error("Failed to parse boot ID '%.32s': %s",
arg_boot_descriptor, strerror(-r));
return r;
}
offset = arg_boot_descriptor + 32;
if (*offset && *offset != '-' && *offset != '+') {
log_error("Relative boot ID offset must start with a '+' or a '-', found '%s' ", offset);
return -EINVAL;
}
} else
offset = arg_boot_descriptor;
if (*offset) {
r = safe_atoi(offset, &relative);
if (r < 0) {
log_error("Failed to parse relative boot ID number '%s'", offset);
return -EINVAL;
}
}
r = get_relative_boot_id(j, &boot_id, relative);
if (r < 0) {
if (sd_id128_equal(boot_id, SD_ID128_NULL))
log_error("Failed to look up boot %+d: %s", relative, strerror(-r));
else
log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+d: %s",
SD_ID128_FORMAT_VAL(boot_id), relative, strerror(-r));
return r;
}
sd_id128_to_string(boot_id, match + 9);
r = sd_journal_add_match(j, match, sizeof(match) - 1);
if (r < 0) {
log_error("Failed to add match: %s", strerror(-r));
return r;
}
r = sd_journal_add_conjunction(j);
if (r < 0)
return r;
return 0;
}
static int add_dmesg(sd_journal *j) {
int r;
assert(j);
if (!arg_dmesg)
return 0;
r = sd_journal_add_match(j, "_TRANSPORT=kernel", strlen("_TRANSPORT=kernel"));
if (r < 0) {
log_error("Failed to add match: %s", strerror(-r));
return r;
}
r = sd_journal_add_conjunction(j);
if (r < 0)
return r;
return 0;
}
static int add_units(sd_journal *j) {
_cleanup_free_ char *u = NULL;
int r;
char **i;
assert(j);
STRV_FOREACH(i, arg_system_units) {
u = unit_name_mangle(*i);
if (!u)
return log_oom();
r = add_matches_for_unit(j, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
}
STRV_FOREACH(i, arg_user_units) {
u = unit_name_mangle(*i);
if (!u)
return log_oom();
r = add_matches_for_user_unit(j, u, getuid());
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
if (r < 0)
return r;
}
r = sd_journal_add_conjunction(j);
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;
for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
if (arg_priorities & (1 << i)) {
match[sizeof(match)-2] = '0' + i;
r = sd_journal_add_match(j, match, strlen(match));
if (r < 0) {
log_error("Failed to add match: %s", strerror(-r));
return r;
}
}
r = sd_journal_add_conjunction(j);
if (r < 0)
return r;
return 0;
}
static int setup_keys(void) {
#ifdef HAVE_GCRYPT
size_t mpk_size, seed_size, state_size, i;
uint8_t *mpk, *seed, *state;
ssize_t l;
int fd = -1, r, attr = 0;
sd_id128_t machine, boot;
char *p = NULL, *k = NULL;
struct FSSHeader h;
uint64_t n;
struct stat st;
r = stat("/var/log/journal", &st);
if (r < 0 && errno != ENOENT && errno != ENOTDIR) {
log_error("stat(\"%s\") failed: %m", "/var/log/journal");
return -errno;
}
if (r < 0 || !S_ISDIR(st.st_mode)) {
log_error("%s is not a directory, must be using persistent logging for FSS.",
"/var/log/journal");
return r < 0 ? -errno : -ENOTDIR;
}
r = sd_id128_get_machine(&machine);
if (r < 0) {
log_error("Failed to get machine ID: %s", strerror(-r));
return r;
}
r = sd_id128_get_boot(&boot);
if (r < 0) {
log_error("Failed to get boot ID: %s", strerror(-r));
return r;
}
if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss",
SD_ID128_FORMAT_VAL(machine)) < 0)
return log_oom();
if (access(p, F_OK) >= 0) {
if (arg_force) {
r = unlink(p);
if (r < 0) {
log_error("unlink(\"%s\") failed: %m", p);
r = -errno;
goto finish;
}
} else {
log_error("Sealing key file %s exists already. (--force to recreate)", p);
r = -EEXIST;
goto finish;
}
}
if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss.tmp.XXXXXX",
SD_ID128_FORMAT_VAL(machine)) < 0) {
r = log_oom();
goto finish;
}
mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
mpk = alloca(mpk_size);
seed_size = FSPRG_RECOMMENDED_SEEDLEN;
seed = alloca(seed_size);
state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
state = alloca(state_size);
fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
log_error("Failed to open /dev/random: %m");
r = -errno;
goto finish;
}
log_info("Generating seed...");
l = loop_read(fd, seed, seed_size, true);
if (l < 0 || (size_t) l != seed_size) {
log_error("Failed to read random seed: %s", strerror(EIO));
r = -EIO;
goto finish;
}
log_info("Generating key pair...");
FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
log_info("Generating sealing key...");
FSPRG_GenState0(state, mpk, seed, seed_size);
assert(arg_interval > 0);
n = now(CLOCK_REALTIME);
n /= arg_interval;
close_nointr_nofail(fd);
fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
log_error("Failed to open %s: %m", k);
r = -errno;
goto finish;
}
/* Enable secure remove, exclusion from dump, synchronous
* writing and in-place updating */
if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0)
log_warning("FS_IOC_GETFLAGS failed: %m");
attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL;
if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0)
log_warning("FS_IOC_SETFLAGS failed: %m");
zero(h);
memcpy(h.signature, "KSHHRHLP", 8);
h.machine_id = machine;
h.boot_id = boot;
h.header_size = htole64(sizeof(h));
h.start_usec = htole64(n * arg_interval);
h.interval_usec = htole64(arg_interval);
h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
h.fsprg_state_size = htole64(state_size);
l = loop_write(fd, &h, sizeof(h), false);
if (l < 0 || (size_t) l != sizeof(h)) {
log_error("Failed to write header: %s", strerror(EIO));
r = -EIO;
goto finish;
}
l = loop_write(fd, state, state_size, false);
if (l < 0 || (size_t) l != state_size) {
log_error("Failed to write state: %s", strerror(EIO));
r = -EIO;
goto finish;
}
if (link(k, p) < 0) {
log_error("Failed to link file: %m");
r = -errno;
goto finish;
}
if (on_tty()) {
fprintf(stderr,
"\n"
"The new key pair has been generated. The " ANSI_HIGHLIGHT_ON "secret sealing key" ANSI_HIGHLIGHT_OFF " has been written to\n"
"the following local file. This key file is automatically updated when the\n"
"sealing key is advanced. It should not be used on multiple hosts.\n"
"\n"
"\t%s\n"
"\n"
"Please write down the following " ANSI_HIGHLIGHT_ON "secret verification key" ANSI_HIGHLIGHT_OFF ". It should be stored\n"
"at a safe location and should not be saved locally on disk.\n"
"\n\t" ANSI_HIGHLIGHT_RED_ON, p);
fflush(stderr);
}
for (i = 0; i < seed_size; i++) {
if (i > 0 && i % 3 == 0)
putchar('-');
printf("%02x", ((uint8_t*) seed)[i]);
}
printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) arg_interval);
if (on_tty()) {
char tsb[FORMAT_TIMESPAN_MAX], *hn;
fprintf(stderr,
ANSI_HIGHLIGHT_OFF "\n"
"The sealing key is automatically changed every %s.\n",
format_timespan(tsb, sizeof(tsb), arg_interval, 0));
hn = gethostname_malloc();
if (hn) {
hostname_cleanup(hn, false);
fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine));
} else
fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
#ifdef HAVE_QRENCODE
/* If this is not an UTF-8 system don't print any QR codes */
if (is_locale_utf8()) {
fputs("\nTo transfer the verification key to your phone please scan the QR code below:\n\n", stderr);
print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
}
#endif
free(hn);
}
r = 0;
finish:
if (fd >= 0)
close_nointr_nofail(fd);
if (k) {
unlink(k);
free(k);
}
free(p);
return r;
#else
log_error("Forward-secure sealing not available.");
return -ENOTSUP;
#endif
}
static int verify(sd_journal *j) {
int r = 0;
Iterator i;
JournalFile *f;
assert(j);
log_show_color(true);
HASHMAP_FOREACH(f, j->files, i) {
int k;
usec_t first, validated, last;
#ifdef HAVE_GCRYPT
if (!arg_verify_key && JOURNAL_HEADER_SEALED(f->header))
log_notice("Journal file %s has sealing enabled but verification key has not been passed using --verify-key=.", f->path);
#endif
k = journal_file_verify(f, arg_verify_key, &first, &validated, &last, true);
if (k == -EINVAL) {
/* If the key was invalid give up right-away. */
return k;
} else if (k < 0) {
log_warning("FAIL: %s (%s)", f->path, strerror(-k));
r = k;
} else {
char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX], c[FORMAT_TIMESPAN_MAX];
log_info("PASS: %s", f->path);
if (arg_verify_key && JOURNAL_HEADER_SEALED(f->header)) {
if (validated > 0) {
log_info("=> Validated from %s to %s, final %s entries not sealed.",
format_timestamp(a, sizeof(a), first),
format_timestamp(b, sizeof(b), validated),
format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0));
} else if (last > 0)
log_info("=> No sealing yet, %s of entries not sealed.",
format_timespan(c, sizeof(c), last - first, 0));
else
log_info("=> No sealing yet, no entries in file.");
}
}
}
return r;
}
#ifdef HAVE_ACL
static int access_check_var_log_journal(sd_journal *j) {
_cleanup_strv_free_ char **g = NULL;
bool have_access;
int r;
assert(j);
have_access = in_group("systemd-journal") > 0;
if (!have_access) {
/* Let's enumerate all groups from the default ACL of
* the directory, which generally should allow access
* to most journal files too */
r = search_acl_groups(&g, "/var/log/journal/", &have_access);
if (r < 0)
return r;
}
if (!have_access) {
if (strv_isempty(g))
log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
" Users in the 'systemd-journal' group can see all messages. Pass -q to\n"
" turn off this notice.");
else {
_cleanup_free_ char *s = NULL;
r = strv_extend(&g, "systemd-journal");
if (r < 0)
return log_oom();
strv_sort(g);
strv_uniq(g);
s = strv_join(g, "', '");
if (!s)
return log_oom();
log_notice("Hint: You are currently not seeing messages from other users and the system.\n"
" Users in the groups '%s' can see all messages.\n"
" Pass -q to turn off this notice.", s);
}
}
return 0;
}
#endif
static int access_check(sd_journal *j) {
Iterator it;
void *code;
int r = 0;
assert(j);
if (set_isempty(j->errors)) {
if (hashmap_isempty(j->files))
log_notice("No journal files were found.");
return 0;
}
if (set_contains(j->errors, INT_TO_PTR(-EACCES))) {
#ifdef HAVE_ACL
/* If /var/log/journal doesn't even exist,
* unprivileged users have no access at all */
if (access("/var/log/journal", F_OK) < 0 &&
geteuid() != 0 &&
in_group("systemd-journal") <= 0) {
log_error("Unprivileged users cannot access messages, unless persistent log storage is\n"
"enabled. Users in the 'systemd-journal' group may always access messages.");
return -EACCES;
}
/* If /var/log/journal exists, try to pring a nice
notice if the user lacks access to it */
if (!arg_quiet && geteuid() != 0) {
r = access_check_var_log_journal(j);
if (r < 0)
return r;
}
#else
if (geteuid() != 0 && in_group("systemd-journal") <= 0) {
log_error("Unprivileged users cannot access messages. Users in the 'systemd-journal' group\n"
"group may access messages.");
return -EACCES;
}
#endif
if (hashmap_isempty(j->files)) {
log_error("No journal files were opened due to insufficient permissions.");
r = -EACCES;
}
}
SET_FOREACH(code, j->errors, it) {
int err;
err = -PTR_TO_INT(code);
assert(err > 0);
if (err != EACCES)
log_warning("Error was encountered while opening journal files: %s",
strerror(err));
}
return r;
}
int main(int argc, char *argv[]) {
int r;
_cleanup_journal_close_ sd_journal*j = NULL;
bool need_seek = false;
sd_id128_t previous_boot_id;
bool previous_boot_id_valid = false, first_line = true;
int n_shown = 0;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
signal(SIGWINCH, columns_lines_cache_reset);
if (arg_action == ACTION_NEW_ID128) {
r = generate_new_id128();
goto finish;
}
if (arg_action == ACTION_SETUP_KEYS) {
r = setup_keys();
goto finish;
}
if (arg_action == ACTION_UPDATE_CATALOG ||
arg_action == ACTION_LIST_CATALOG ||
arg_action == ACTION_DUMP_CATALOG) {
const char* database = CATALOG_DATABASE;
_cleanup_free_ char *copy = NULL;
if (arg_root) {
copy = strjoin(arg_root, "/", CATALOG_DATABASE, NULL);
if (!copy) {
r = log_oom();
goto finish;
}
path_kill_slashes(copy);
database = copy;
}
if (arg_action == ACTION_UPDATE_CATALOG) {
r = catalog_update(database, arg_root, catalog_file_dirs);
if (r < 0)
log_error("Failed to list catalog: %s", strerror(-r));
} else {
bool oneline = arg_action == ACTION_LIST_CATALOG;
if (optind < argc)
r = catalog_list_items(stdout, database,
oneline, argv + optind);
else
r = catalog_list(stdout, database, oneline);
if (r < 0)
log_error("Failed to list catalog: %s", strerror(-r));
}
goto finish;
}
if (arg_directory)
r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
else if (arg_file)
r = sd_journal_open_files(&j, (const char**) arg_file, 0);
else
r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
if (r < 0) {
log_error("Failed to open %s: %s",
arg_directory ? arg_directory : arg_file ? "files" : "journal",
strerror(-r));
return EXIT_FAILURE;
}
r = access_check(j);
if (r < 0)
return EXIT_FAILURE;
if (arg_action == ACTION_VERIFY) {
r = verify(j);
goto finish;
}
if (arg_action == ACTION_PRINT_HEADER) {
journal_print_header(j);
return EXIT_SUCCESS;
}
if (arg_action == ACTION_DISK_USAGE) {
uint64_t bytes;
char sbytes[FORMAT_BYTES_MAX];
r = sd_journal_get_usage(j, &bytes);
if (r < 0)
return EXIT_FAILURE;
printf("Journals take up %s on disk.\n",
format_bytes(sbytes, sizeof(sbytes), bytes));
return EXIT_SUCCESS;
}
/* add_boot() must be called first!
* It may need to seek the journal to find parent boot IDs. */
r = add_boot(j);
if (r < 0)
return EXIT_FAILURE;
r = add_dmesg(j);
if (r < 0)
return EXIT_FAILURE;
r = add_units(j);
strv_free(arg_system_units);
strv_free(arg_user_units);
if (r < 0)
return EXIT_FAILURE;
r = add_priorities(j);
if (r < 0)
return EXIT_FAILURE;
r = add_matches(j, argv + optind);
if (r < 0)
return EXIT_FAILURE;
if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
_cleanup_free_ char *filter;
filter = journal_make_match_string(j);
log_debug("Journal filter: %s", filter);
}
if (arg_field) {
const void *data;
size_t size;
r = sd_journal_set_data_threshold(j, 0);
if (r < 0) {
log_error("Failed to unset data size threshold");
return EXIT_FAILURE;
}
r = sd_journal_query_unique(j, arg_field);
if (r < 0) {
log_error("Failed to query unique data objects: %s", strerror(-r));
return EXIT_FAILURE;
}
SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
const void *eq;
if (arg_lines >= 0 && n_shown >= arg_lines)
break;
eq = memchr(data, '=', size);
if (eq)
printf("%.*s\n", (int) (size - ((const uint8_t*) eq - (const uint8_t*) data + 1)), (const char*) eq + 1);
else
printf("%.*s\n", (int) size, (const char*) data);
n_shown ++;
}
return EXIT_SUCCESS;
}
/* Opening the fd now means the first sd_journal_wait() will actually wait */
if (arg_follow) {
r = sd_journal_get_fd(j);
if (r < 0)
return EXIT_FAILURE;
}
if (arg_cursor || arg_after_cursor) {
r = sd_journal_seek_cursor(j, arg_cursor ? arg_cursor : arg_after_cursor);
if (r < 0) {
log_error("Failed to seek to cursor: %s", strerror(-r));
return EXIT_FAILURE;
}
if (!arg_reverse)
r = sd_journal_next_skip(j, 1 + !!arg_after_cursor);
else
r = sd_journal_previous_skip(j, 1 + !!arg_after_cursor);
if (arg_after_cursor && r < 2 && !arg_follow)
/* We couldn't find the next entry after the cursor. */
arg_lines = 0;
} else if (arg_since_set && !arg_reverse) {
r = sd_journal_seek_realtime_usec(j, arg_since);
if (r < 0) {
log_error("Failed to seek to date: %s", strerror(-r));
return EXIT_FAILURE;
}
r = sd_journal_next(j);
} else if (arg_until_set && arg_reverse) {
r = sd_journal_seek_realtime_usec(j, arg_until);
if (r < 0) {
log_error("Failed to seek to date: %s", strerror(-r));
return EXIT_FAILURE;
}
r = sd_journal_previous(j);
} else if (arg_lines >= 0) {
r = sd_journal_seek_tail(j);
if (r < 0) {
log_error("Failed to seek to tail: %s", strerror(-r));
return EXIT_FAILURE;
}
r = sd_journal_previous_skip(j, arg_lines);
} else if (arg_reverse) {
r = sd_journal_seek_tail(j);
if (r < 0) {
log_error("Failed to seek to tail: %s", strerror(-r));
return EXIT_FAILURE;
}
r = sd_journal_previous(j);
} else {
r = sd_journal_seek_head(j);
if (r < 0) {
log_error("Failed to seek to head: %s", strerror(-r));
return EXIT_FAILURE;
}
r = sd_journal_next(j);
}
if (r < 0) {
log_error("Failed to iterate through journal: %s", strerror(-r));
return EXIT_FAILURE;
}
if (!arg_no_pager && !arg_follow)
pager_open(arg_pager_end);
if (!arg_quiet) {
usec_t start, end;
char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
if (r < 0) {
log_error("Failed to get cutoff: %s", strerror(-r));
goto finish;
}
if (r > 0) {
if (arg_follow)
printf("-- Logs begin at %s. --\n",
format_timestamp(start_buf, sizeof(start_buf), start));
else
printf("-- Logs begin at %s, end at %s. --\n",
format_timestamp(start_buf, sizeof(start_buf), start),
format_timestamp(end_buf, sizeof(end_buf), end));
}
}
for (;;) {
while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
int flags;
if (need_seek) {
if (!arg_reverse)
r = sd_journal_next(j);
else
r = sd_journal_previous(j);
if (r < 0) {
log_error("Failed to iterate through journal: %s", strerror(-r));
goto finish;
}
if (r == 0)
break;
}
if (arg_until_set && !arg_reverse) {
usec_t usec;
r = sd_journal_get_realtime_usec(j, &usec);
if (r < 0) {
log_error("Failed to determine timestamp: %s", strerror(-r));
goto finish;
}
if (usec > arg_until)
goto finish;
}
if (arg_since_set && arg_reverse) {
usec_t usec;
r = sd_journal_get_realtime_usec(j, &usec);
if (r < 0) {
log_error("Failed to determine timestamp: %s", strerror(-r));
goto finish;
}
if (usec < arg_since)
goto finish;
}
if (!arg_merge) {
sd_id128_t boot_id;
const char *color_on = on_tty() ? ANSI_HIGHLIGHT_ON : "",
*color_off = on_tty() ? ANSI_HIGHLIGHT_OFF : "";
r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
if (r >= 0) {
if (previous_boot_id_valid &&
!sd_id128_equal(boot_id, previous_boot_id))
printf("%s-- Reboot --%s\n", color_on, color_off);
previous_boot_id = boot_id;
previous_boot_id_valid = true;
}
}
flags =
arg_all * OUTPUT_SHOW_ALL |
(arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
on_tty() * OUTPUT_COLOR |
arg_catalog * OUTPUT_CATALOG;
r = output_journal(stdout, j, arg_output, 0, flags);
need_seek = true;
if (r == -EADDRNOTAVAIL)
break;
else if (r < 0 || ferror(stdout))
goto finish;
n_shown++;
}
if (!arg_follow) {
if (arg_show_cursor) {
_cleanup_free_ char *cursor = NULL;
r = sd_journal_get_cursor(j, &cursor);
if (r < 0 && r != -EADDRNOTAVAIL)
log_error("Failed to get cursor: %s", strerror(-r));
else if (r >= 0)
printf("-- cursor: %s\n", cursor);
}
break;
}
r = sd_journal_wait(j, (uint64_t) -1);
if (r < 0) {
log_error("Couldn't wait for journal event: %s", strerror(-r));
goto finish;
}
first_line = false;
}
finish:
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}