/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-***/
#include <errno.h>
#include <fcntl.h>
#endif
#include "sd-bus.h"
+#include "sd-device.h"
#include "sd-journal.h"
#include "acl-util.h"
#include "bus-util.h"
#include "catalog.h"
#include "chattr-util.h"
+#include "def.h"
+#include "device-private.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "fsprg.h"
#include "glob-util.h"
#include "hostname-util.h"
+#include "id128-print.h"
#include "io-util.h"
#include "journal-def.h"
#include "journal-internal.h"
#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "rlimit-util.h"
#include "set.h"
#include "sigbus.h"
+#include "string-table.h"
#include "strv.h"
#include "syslog-util.h"
#include "terminal-util.h"
-#include "udev.h"
-#include "udev-util.h"
#include "unit-name.h"
#include "user-util.h"
r = pcre2_get_error_message(errorcode, buf, sizeof buf);
- log_error("Bad pattern \"%s\": %s",
- pattern,
- r < 0 ? "unknown error" : (char*) buf);
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Bad pattern \"%s\": %s", pattern,
+ r < 0 ? "unknown error" : (char *)buf);
}
*out = p;
static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_utc = false;
-static bool arg_pager_end = false;
static bool arg_follow = false;
static bool arg_full = true;
static bool arg_all = false;
-static bool arg_no_pager = false;
+static PagerFlags arg_pager_flags = 0;
static int arg_lines = ARG_LINES_DEFAULT;
static bool arg_no_tail = false;
static bool arg_quiet = false;
ACTION_SYNC,
ACTION_ROTATE,
ACTION_VACUUM,
+ ACTION_ROTATE_AND_VACUUM,
ACTION_LIST_FIELDS,
ACTION_LIST_FIELD_NAMES,
} arg_action = ACTION_SHOW;
} BootId;
static int add_matches_for_device(sd_journal *j, const char *devpath) {
- int r;
- _cleanup_(udev_unrefp) struct udev *udev = NULL;
- _cleanup_(udev_device_unrefp) struct udev_device *device = NULL;
- struct udev_device *d = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ sd_device *d = NULL;
struct stat st;
+ int r;
assert(j);
assert(devpath);
return -EINVAL;
}
- udev = udev_new();
- if (!udev)
- return log_oom();
+ if (stat(devpath, &st) < 0)
+ return log_error_errno(errno, "Couldn't stat file: %m");
- r = stat(devpath, &st);
+ r = device_new_from_stat_rdev(&device, &st);
if (r < 0)
- log_error_errno(errno, "Couldn't stat file: %m");
+ return log_error_errno(r, "Failed to get device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
- d = device = udev_device_new_from_devnum(udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
- if (!device)
- return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
-
- while (d) {
+ for (d = device; d; ) {
_cleanup_free_ char *match = NULL;
const char *subsys, *sysname, *devnode;
+ sd_device *parent;
- subsys = udev_device_get_subsystem(d);
- if (!subsys) {
- d = udev_device_get_parent(d);
- continue;
- }
+ r = sd_device_get_subsystem(d, &subsys);
+ if (r < 0)
+ goto get_parent;
- sysname = udev_device_get_sysname(d);
- if (!sysname) {
- d = udev_device_get_parent(d);
- continue;
- }
+ r = sd_device_get_sysname(d, &sysname);
+ if (r < 0)
+ goto get_parent;
match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname);
if (!match)
if (r < 0)
return log_error_errno(r, "Failed to add match: %m");
- devnode = udev_device_get_devnode(d);
- if (devnode) {
+ if (sd_device_get_devname(d, &devnode) >= 0) {
_cleanup_free_ char *match1 = NULL;
r = stat(devnode, &st);
return log_error_errno(r, "Failed to add match: %m");
}
- d = udev_device_get_parent(d);
+get_parent:
+ if (sd_device_get_parent(d, &parent) < 0)
+ break;
+
+ d = parent;
}
r = add_match_this_boot(j, arg_machine);
return 0;
}
-static void help(void) {
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
+
+ r = terminal_urlify_man("journalctl", "1", &link);
+ if (r < 0)
+ return log_oom();
printf("%s [OPTIONS...] [MATCHES...]\n\n"
"Query the journal.\n\n"
" -o --output=STRING Change journal output mode (short, short-precise,\n"
" short-iso, short-iso-precise, short-full,\n"
" short-monotonic, short-unix, verbose, export,\n"
- " json, json-pretty, json-sse, cat)\n"
+ " json, json-pretty, json-sse, json-seq, cat,\n"
+ " with-unit)\n"
" --output-fields=LIST Select fields to print in verbose/export/json modes\n"
" --utc Express time in Coordinated Universal Time (UTC)\n"
" -x --catalog Add message explanations where available\n"
" -D --directory=PATH Show journal files from directory\n"
" --file=PATH Show journal file\n"
" --root=ROOT Operate on files below a root directory\n"
-#if HAVE_GCRYPT
" --interval=TIME Time interval for changing the FSS sealing key\n"
" --verify-key=KEY Specify FSS verification key\n"
" --force Override of the FSS key pair with --setup-keys\n"
-#endif
"\nCommands:\n"
" -h --help Show this help text\n"
" --version Show package version\n"
" --list-catalog Show all message IDs in the catalog\n"
" --dump-catalog Show entries in the message catalog\n"
" --update-catalog Update the message catalog database\n"
- " --new-id128 Generate a new 128-bit ID\n"
-#if HAVE_GCRYPT
" --setup-keys Generate a new FSS key pair\n"
-#endif
- , program_invocation_short_name);
+ "\nSee the %s for details.\n"
+ , program_invocation_short_name
+ , link
+ );
+
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
{ "no-full", no_argument, NULL, ARG_NO_FULL },
{ "lines", optional_argument, NULL, 'n' },
{ "no-tail", no_argument, NULL, ARG_NO_TAIL },
- { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
+ { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, /* deprecated */
{ "quiet", no_argument, NULL, 'q' },
{ "merge", no_argument, NULL, 'm' },
{ "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */
switch (c) {
case 'h':
- help();
- return 0;
+ return help();
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
- arg_no_pager = true;
+ arg_pager_flags |= PAGER_DISABLE;
break;
case 'e':
- arg_pager_end = true;
+ arg_pager_flags |= PAGER_JUMP_TO_END;
if (arg_lines == ARG_LINES_DEFAULT)
arg_lines = 1000;
break;
case 'o':
+ if (streq(optarg, "help")) {
+ DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX);
+ return 0;
+ }
+
arg_output = output_mode_from_string(optarg);
if (arg_output < 0) {
log_error("Unknown output format '%s'.", optarg);
return -EINVAL;
}
- if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_CAT))
+ if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT))
arg_quiet = true;
break;
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_FILES:
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
case ARG_VACUUM_TIME:
return r;
}
- arg_action = ACTION_VACUUM;
+ arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM;
break;
#if HAVE_GCRYPT
case ARG_VERIFY_KEY:
case ARG_INTERVAL:
case ARG_FORCE:
- log_error("Forward-secure sealing not available.");
+ log_error("Compiled without forward-secure sealing support.");
return -EOPNOTSUPP;
#endif
break;
case ARG_ROTATE:
- arg_action = ACTION_ROTATE;
+ arg_action = arg_action == ACTION_VACUUM ? ACTION_ROTATE_AND_VACUUM : ACTION_ROTATE;
break;
case ARG_SYNC:
return 1;
}
-static int generate_new_id128(void) {
- sd_id128_t id;
- int r;
- unsigned i;
-
- r = sd_id128_randomize(&id);
- if (r < 0)
- return log_error_errno(r, "Failed to generate ID: %m");
-
- 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 man:sd-id128(3) 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;
bool have_term = false;
r = add_matches_for_device(j, p);
if (r < 0)
return r;
- } else {
- log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
- return -EINVAL;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "File is neither a device node, nor regular file, nor executable: %s",
+ *i);
have_term = true;
} else {
return log_error_errno(r, "Failed to add match '%s': %m", *i);
}
- if (!strv_isempty(args) && !have_term) {
- log_error("\"+\" can only be used between terms");
- return -EINVAL;
- }
+ if (!strv_isempty(args) && !have_term)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "\"+\" can only be used between terms");
return 0;
}
r = sd_journal_previous(j);
if (r < 0)
return r;
- else if (r == 0) {
- log_debug("Whoopsie! We found a boot ID but can't read its last entry.");
- return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */
- }
+ else if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
+ "Whoopsie! We found a boot ID but can't read its last entry."); /* This shouldn't happen. We just came from this very boot ID. */
r = sd_journal_get_realtime_usec(j, &next_boot->last);
if (r < 0)
if (count == 0)
return count;
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
/* numbers are one less, but we need an extra char for the sign */
w = DECIMAL_STR_WIDTH(count - 1) + 1;
/* Enable secure remove, exclusion from dump, synchronous
* writing and in-place updating */
- r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL);
+ r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, NULL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes: %m");
return r;
#else
- log_error("Forward-secure sealing not available.");
- return -EOPNOTSUPP;
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Forward-secure sealing not available.");
#endif
}
/* See if a sync happened by now. */
r = read_timestamp_file(watch_path, &tstamp);
if (r < 0 && r != -ENOENT)
- return log_error_errno(errno, "Failed to read %s: %m", watch_path);
+ return log_error_errno(r, "Failed to read %s: %m", watch_path);
if (r >= 0 && tstamp >= start)
return 0;
return send_signal_and_wait(SIGRTMIN+1, "/run/systemd/journal/synced");
}
-int main(int argc, char *argv[]) {
+static int wait_for_change(sd_journal *j, int poll_fd) {
+ struct pollfd pollfds[] = {
+ { .fd = poll_fd, .events = POLLIN },
+ { .fd = STDOUT_FILENO },
+ };
+
+ struct timespec ts;
+ usec_t timeout;
int r;
+
+ assert(j);
+ assert(poll_fd >= 0);
+
+ /* Much like sd_journal_wait() but also keeps an eye on STDOUT, and exits as soon as we see a POLLHUP on that,
+ * i.e. when it is closed. */
+
+ r = sd_journal_get_timeout(j, &timeout);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine journal waiting time: %m");
+
+ if (ppoll(pollfds, ELEMENTSOF(pollfds),
+ timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL) < 0) {
+ if (errno == EINTR)
+ return 0;
+
+ return log_error_errno(errno, "Couldn't wait for journal event: %m");
+ }
+
+ if (pollfds[1].revents & (POLLHUP|POLLERR)) /* STDOUT has been closed? */
+ return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED),
+ "Standard output has been closed.");
+
+ r = sd_journal_process(j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to process journal events: %m");
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ bool previous_boot_id_valid = false, first_line = true, ellipsized = false, need_seek = false;
_cleanup_(sd_journal_closep) 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;
- bool ellipsized = false;
+ int n_shown = 0, r, poll_fd = -1;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
+ /* Increase max number of open files if we can, we might needs this when browsing journal files, which might be
+ * split up into many files. */
+ (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
+
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
signal(SIGWINCH, columns_lines_cache_reset);
sigbus_install();
- /* Increase max number of open files to 16K if we can, we
- * might needs this when browsing journal files, which might
- * be split up into many files. */
- setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384));
-
switch (arg_action) {
case ACTION_NEW_ID128:
- r = generate_new_id128();
+ r = id128_print_new(true);
goto finish;
case ACTION_SETUP_KEYS:
} else {
bool oneline = arg_action == ACTION_LIST_CATALOG;
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
if (optind < argc)
r = catalog_list_items(stdout, database, oneline, argv + optind);
case ACTION_DISK_USAGE:
case ACTION_LIST_BOOTS:
case ACTION_VACUUM:
+ case ACTION_ROTATE_AND_VACUUM:
case ACTION_LIST_FIELDS:
case ACTION_LIST_FIELD_NAMES:
/* These ones require access to the journal files, continue below. */
r = list_boots(j);
goto finish;
+ case ACTION_ROTATE_AND_VACUUM:
+
+ r = rotate();
+ if (r < 0)
+ goto finish;
+
+ _fallthrough_;
+
case ACTION_VACUUM: {
Directory *d;
Iterator i;
/* Opening the fd now means the first sd_journal_wait() will actually wait */
if (arg_follow) {
- r = sd_journal_get_fd(j);
- if (r == -EMEDIUMTYPE) {
- log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN.");
+ poll_fd = sd_journal_get_fd(j);
+ if (poll_fd == -EMFILE) {
+ log_warning_errno(poll_fd, "Insufficent watch descriptors available. Reverting to -n.");
+ arg_follow = false;
+ } else if (poll_fd == -EMEDIUMTYPE) {
+ log_error_errno(poll_fd, "The --follow switch is not supported in conjunction with reading from STDIN.");
goto finish;
- }
- if (r < 0) {
- log_error_errno(r, "Failed to get journal fd: %m");
+ } else if (poll_fd < 0) {
+ log_error_errno(poll_fd, "Failed to get journal fd: %m");
goto finish;
}
}
need_seek = true;
if (!arg_follow)
- (void) pager_open(arg_no_pager, arg_pager_end);
+ (void) pager_open(arg_pager_flags);
if (!arg_quiet && (arg_lines != 0 || arg_follow)) {
usec_t start, end;
arg_utc * OUTPUT_UTC |
arg_no_hostname * OUTPUT_NO_HOSTNAME;
- r = output_journal(stdout, j, arg_output, 0, flags,
- arg_output_fields, highlight, &ellipsized);
+ r = show_journal_entry(stdout, j, arg_output, 0, flags,
+ arg_output_fields, highlight, &ellipsized);
need_seek = true;
if (r == -EADDRNOTAVAIL)
break;
- else if (r < 0 || ferror(stdout))
+ else if (r < 0)
goto finish;
n_shown++;
}
fflush(stdout);
- r = sd_journal_wait(j, (uint64_t) -1);
- if (r < 0) {
- log_error_errno(r, "Couldn't wait for journal event: %m");
+
+ r = wait_for_change(j, poll_fd);
+ if (r < 0)
goto finish;
- }
first_line = false;
}