+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
#include "journal-def.h"
#include "journal-internal.h"
#include "journal-qrcode.h"
+#include "journal-util.h"
#include "journal-vacuum.h"
#include "journal-verify.h"
#include "locale-util.h"
static char **arg_file = NULL;
static bool arg_file_stdin = false;
static int arg_priorities = 0xFF;
-static const char *arg_verify_key = NULL;
-#ifdef HAVE_GCRYPT
+static char *arg_verify_key = NULL;
+#if HAVE_GCRYPT
static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
static bool arg_force = false;
#endif
static uint64_t arg_vacuum_size = 0;
static uint64_t arg_vacuum_n_files = 0;
static usec_t arg_vacuum_time = 0;
+static char **arg_output_fields = NULL;
static enum {
ACTION_SHOW,
if (r >= 0)
x += 32;
- if (*x != '-' && *x != '+' && *x != 0)
+ if (!IN_SET(*x, 0, '-', '+'))
return -EINVAL;
if (*x != 0) {
" --no-tail Show all lines, even in follow mode\n"
" -r --reverse Show the newest entries first\n"
" -o --output=STRING Change journal output mode (short, short-precise,\n"
- " short-iso, short-full, short-monotonic, short-unix,\n"
- " verbose, export, json, json-pretty, json-sse, cat)\n"
+ " short-iso, short-iso-precise, short-full,\n"
+ " short-monotonic, short-unix, verbose, export,\n"
+ " json, json-pretty, json-sse, cat)\n"
" --utc Express time in Coordinated Universal Time (UTC)\n"
" -x --catalog Add message explanations where available\n"
" --no-full Ellipsize fields\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"
-#ifdef HAVE_GCRYPT
+#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"
" --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"
-#ifdef HAVE_GCRYPT
+#if HAVE_GCRYPT
" --setup-keys Generate a new FSS key pair\n"
#endif
, program_invocation_short_name);
ARG_VACUUM_FILES,
ARG_VACUUM_TIME,
ARG_NO_HOSTNAME,
+ ARG_OUTPUT_FIELDS,
};
static const struct option options[] = {
{ "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES },
{ "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
{ "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME },
+ { "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS },
{}
};
return -EINVAL;
}
- if (arg_output == OUTPUT_EXPORT ||
- arg_output == OUTPUT_JSON ||
- arg_output == OUTPUT_JSON_PRETTY ||
- arg_output == OUTPUT_JSON_SSE ||
- arg_output == OUTPUT_CAT)
+ if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_CAT))
arg_quiet = true;
break;
arg_action = ACTION_VACUUM;
break;
-#ifdef HAVE_GCRYPT
+#if HAVE_GCRYPT
case ARG_FORCE:
arg_force = true;
break;
case ARG_VERIFY_KEY:
arg_action = ACTION_VERIFY;
- arg_verify_key = optarg;
+ r = free_and_strdup(&arg_verify_key, optarg);
+ if (r < 0)
+ return r;
+ /* Use memset not string_erase so this doesn't look confusing
+ * in ps or htop output. */
+ memset(optarg, 'x', strlen(optarg));
+
arg_merge = false;
break;
arg_action = ACTION_SYNC;
break;
+ case ARG_OUTPUT_FIELDS: {
+ _cleanup_strv_free_ char **v = NULL;
+
+ v = strv_split(optarg, ",");
+ if (!v)
+ return log_oom();
+
+ if (!arg_output_fields) {
+ arg_output_fields = v;
+ v = NULL;
+ } else {
+ r = strv_extend_strv(&arg_output_fields, v, true);
+ if (r < 0)
+ return log_oom();
+ }
+ break;
+ }
+
case '?':
return -EINVAL;
* to users, and automatically turn --unit= into --user-unit= if combined with --user. */
r = strv_extend_strv(&arg_user_units, arg_system_units, true);
if (r < 0)
- return -ENOMEM;
+ return r;
arg_system_units = strv_free(arg_system_units);
}
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"
+ "As man:sd-id128(3) macro:\n"
"#define MESSAGE_XYZ SD_ID128_MAKE(",
SD_ID128_FORMAT_VAL(id),
SD_ID128_FORMAT_VAL(id));
_cleanup_free_ char *p = NULL, *t = NULL, *t2 = NULL, *interpreter = NULL;
struct stat st;
- r = chase_symlinks(*i, NULL, &p);
+ r = chase_symlinks(*i, NULL, 0, &p);
if (r < 0)
return log_error_errno(r, "Couldn't canonicalize path: %m");
}
static int setup_keys(void) {
-#ifdef HAVE_GCRYPT
+#if HAVE_GCRYPT
size_t mpk_size, seed_size, state_size, i;
uint8_t *mpk, *seed, *state;
int fd = -1, r;
struct stat st;
r = stat("/var/log/journal", &st);
- if (r < 0 && errno != ENOENT && errno != ENOTDIR)
+ if (r < 0 && !IN_SET(errno, ENOENT, ENOTDIR))
return log_error_errno(errno, "stat(\"%s\") failed: %m", "/var/log/journal");
if (r < 0 || !S_ISDIR(st.st_mode)) {
} else
fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine));
-#ifdef HAVE_QRENCODE
+#if 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);
int k;
usec_t first = 0, validated = 0, last = 0;
-#ifdef HAVE_GCRYPT
+#if 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
return r;
}
-static int access_check_var_log_journal(sd_journal *j) {
-#ifdef HAVE_ACL
- _cleanup_strv_free_ char **g = NULL;
- const char* dir;
-#endif
- int r;
-
- assert(j);
-
- if (arg_quiet)
- return 0;
-
- /* If we are root, we should have access, don't warn. */
- if (getuid() == 0)
- return 0;
-
- /* If we are in the 'systemd-journal' group, we should have
- * access too. */
- r = in_group("systemd-journal");
- if (r < 0)
- return log_error_errno(r, "Failed to check if we are in the 'systemd-journal' group: %m");
- if (r > 0)
- return 0;
-
-#ifdef HAVE_ACL
- if (laccess("/run/log/journal", F_OK) >= 0)
- dir = "/run/log/journal";
- else
- dir = "/var/log/journal";
-
- /* If we are in any of the groups listed in the journal ACLs,
- * then all is good, too. Let's enumerate all groups from the
- * default ACL of the directory, which generally should allow
- * access to most journal files too. */
- r = acl_search_groups(dir, &g);
- if (r < 0)
- return log_error_errno(r, "Failed to search journal ACL: %m");
- if (r > 0)
- return 0;
-
- /* Print a pretty list, if there were ACLs set. */
- if (!strv_isempty(g)) {
- _cleanup_free_ char *s = NULL;
-
- /* Thre are groups in the ACL, let's list them */
- 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 groups '%s' can see all messages.\n"
- " Pass -q to turn off this notice.", s);
- return 1;
- }
-#endif
-
- /* If no ACLs were found, print a short version of the message. */
- 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.");
-
- return 1;
-}
-
-static int access_check(sd_journal *j) {
- Iterator it;
- void *code;
- char *path;
- int r = 0;
-
- assert(j);
-
- if (hashmap_isempty(j->errors)) {
- if (ordered_hashmap_isempty(j->files))
- log_notice("No journal files were found.");
-
- return 0;
- }
-
- if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) {
- (void) access_check_var_log_journal(j);
-
- if (ordered_hashmap_isempty(j->files))
- r = log_error_errno(EACCES, "No journal files were opened due to insufficient permissions.");
- }
-
- HASHMAP_FOREACH_KEY(path, code, j->errors, it) {
- int err;
-
- err = abs(PTR_TO_INT(code));
-
- switch (err) {
- case EACCES:
- continue;
-
- case ENODATA:
- log_warning_errno(err, "Journal file %s is truncated, ignoring file.", path);
- break;
-
- case EPROTONOSUPPORT:
- log_warning_errno(err, "Journal file %s uses an unsupported feature, ignoring file.", path);
- break;
-
- case EBADMSG:
- log_warning_errno(err, "Journal file %s corrupted, ignoring file.", path);
- break;
-
- default:
- log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path);
- break;
- }
- }
-
- return r;
-}
-
static int flush_to_var(void) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
goto finish;
}
- r = access_check(j);
+ r = journal_access_check_and_warn(j, arg_quiet);
if (r < 0)
goto finish;
if (d->is_root)
continue;
- q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_n_files, arg_vacuum_time, NULL, true);
+ q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_n_files, arg_vacuum_time, NULL, !arg_quiet);
if (q < 0) {
log_error_errno(q, "Failed to vacuum %s: %m", d->path);
r = q;
if (arg_boot_offset != 0 &&
sd_journal_has_runtime_files(j) > 0 &&
sd_journal_has_persistent_files(j) == 0) {
- log_info("Specifying boot ID has no effect, no persistent journal was found");
+ log_info("Specifying boot ID or boot offset has no effect, no persistent journal was found.");
r = 0;
goto finish;
}
log_error_errno(r, "Failed to iterate through journal: %m");
goto finish;
}
- if (r == 0) {
- if (arg_follow)
- need_seek = true;
- else {
- if (!arg_quiet)
- printf("-- No entries --\n");
- goto finish;
- }
- }
+ if (r == 0)
+ need_seek = true;
if (!arg_follow)
pager_open(arg_no_pager, arg_pager_end);
- if (!arg_quiet) {
+ if (!arg_quiet && (arg_lines != 0 || arg_follow)) {
usec_t start, end;
char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
arg_utc * OUTPUT_UTC |
arg_no_hostname * OUTPUT_NO_HOSTNAME;
- r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized);
+ r = output_journal(stdout, j, arg_output, 0, flags, arg_output_fields, &ellipsized);
need_seek = true;
if (r == -EADDRNOTAVAIL)
break;
}
if (!arg_follow) {
+ if (n_shown == 0 && !arg_quiet)
+ printf("-- No entries --\n");
+
if (arg_show_cursor) {
_cleanup_free_ char *cursor = NULL;
break;
}
+ fflush(stdout);
r = sd_journal_wait(j, (uint64_t) -1);
if (r < 0) {
log_error_errno(r, "Couldn't wait for journal event: %m");
}
finish:
+ fflush(stdout);
pager_close();
strv_free(arg_file);
strv_free(arg_syslog_identifier);
strv_free(arg_system_units);
strv_free(arg_user_units);
+ strv_free(arg_output_fields);
free(arg_root);
+ free(arg_verify_key);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}