We currently cannot support that (supporting that would probably require some active component in the machine, or alternatively idmapped mounts or so), hence politely refuse it. See: https://github.com/systemd/systemd/issues/32997#issuecomment-2127700945
286 lines
11 KiB
C
286 lines
11 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include <fnmatch.h>
|
|
#include <unistd.h>
|
|
|
|
#include "alloc-util.h"
|
|
#include "glob-util.h"
|
|
#include "hostname-util.h"
|
|
#include "id128-util.h"
|
|
#include "journal-util.h"
|
|
#include "journalctl.h"
|
|
#include "journalctl-util.h"
|
|
#include "log.h"
|
|
#include "logs-show.h"
|
|
#include "nulstr-util.h"
|
|
#include "set.h"
|
|
#include "string-util.h"
|
|
#include "strv.h"
|
|
#include "time-util.h"
|
|
#include "unit-name.h"
|
|
|
|
char* format_timestamp_maybe_utc(char *buf, size_t l, usec_t t) {
|
|
assert(buf);
|
|
|
|
if (arg_utc)
|
|
return format_timestamp_style(buf, l, t, TIMESTAMP_UTC);
|
|
|
|
return format_timestamp(buf, l, t);
|
|
}
|
|
|
|
int acquire_journal(sd_journal **ret) {
|
|
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
|
int r;
|
|
|
|
assert(ret);
|
|
|
|
journal_browse_prepare();
|
|
|
|
if (arg_directory)
|
|
r = sd_journal_open_directory(&j, arg_directory, arg_journal_type | arg_journal_additional_open_flags);
|
|
else if (arg_root)
|
|
r = sd_journal_open_directory(&j, arg_root, arg_journal_type | arg_journal_additional_open_flags | SD_JOURNAL_OS_ROOT);
|
|
else if (arg_file_stdin)
|
|
r = sd_journal_open_files_fd(&j, (int[]) { STDIN_FILENO }, 1, arg_journal_additional_open_flags);
|
|
else if (arg_file)
|
|
r = sd_journal_open_files(&j, (const char**) arg_file, arg_journal_additional_open_flags);
|
|
else if (arg_machine) {
|
|
_cleanup_free_ char *u = NULL, *h = NULL;
|
|
|
|
r = split_user_at_host(arg_machine, &u, &h);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to split machine specification '%s': %m", arg_machine);
|
|
|
|
if (!isempty(u) && !streq(u, "root"))
|
|
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Connecting to a machine as non-root is not supported.");
|
|
|
|
r = journal_open_machine(&j, h ?: ".host", arg_journal_additional_open_flags);
|
|
} else
|
|
r = sd_journal_open_namespace(
|
|
&j,
|
|
arg_namespace,
|
|
(arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY) |
|
|
arg_namespace_flags | arg_journal_type | arg_journal_additional_open_flags);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal");
|
|
|
|
r = journal_access_check_and_warn(j, arg_quiet,
|
|
!(arg_journal_type == SD_JOURNAL_CURRENT_USER || arg_user_units));
|
|
if (r < 0)
|
|
return r;
|
|
|
|
*ret = TAKE_PTR(j);
|
|
return 0;
|
|
}
|
|
|
|
bool journal_boot_has_effect(sd_journal *j) {
|
|
assert(j);
|
|
|
|
if (arg_boot_offset != 0 &&
|
|
sd_journal_has_runtime_files(j) > 0 &&
|
|
sd_journal_has_persistent_files(j) == 0) {
|
|
log_info("Specifying boot ID or boot offset has no effect, no persistent journal was found.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int journal_acquire_boot(sd_journal *j) {
|
|
int r;
|
|
|
|
assert(j);
|
|
|
|
if (!arg_boot)
|
|
return 0;
|
|
|
|
/* Take a shortcut and use the current boot_id, which we can do very quickly.
|
|
* We can do this only when the logs are coming from the current machine,
|
|
* so take the slow path if log location is specified. */
|
|
if (arg_boot_offset == 0 && sd_id128_is_null(arg_boot_id) &&
|
|
!arg_directory && !arg_file && !arg_file_stdin && !arg_root) {
|
|
r = id128_get_boot_for_machine(arg_machine, &arg_boot_id);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to get boot ID%s%s: %m",
|
|
isempty(arg_machine) ? "" : " of container ", strempty(arg_machine));
|
|
} else {
|
|
sd_id128_t boot_id;
|
|
|
|
r = journal_find_boot(j, arg_boot_id, arg_boot_offset, &boot_id);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to find journal entry for the specified boot (%s%+i): %m",
|
|
sd_id128_is_null(arg_boot_id) ? "" : SD_ID128_TO_STRING(arg_boot_id),
|
|
arg_boot_offset);
|
|
if (r == 0)
|
|
return log_error_errno(SYNTHETIC_ERRNO(ENODATA),
|
|
"No journal boot entry found for the specified boot (%s%+i).",
|
|
sd_id128_is_null(arg_boot_id) ? "" : SD_ID128_TO_STRING(arg_boot_id),
|
|
arg_boot_offset);
|
|
|
|
log_debug("Found boot %s for %s%+i",
|
|
SD_ID128_TO_STRING(boot_id),
|
|
sd_id128_is_null(arg_boot_id) ? "" : SD_ID128_TO_STRING(arg_boot_id),
|
|
arg_boot_offset);
|
|
|
|
arg_boot_id = boot_id;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int get_possible_units(
|
|
sd_journal *j,
|
|
const char *fields,
|
|
char * const *patterns,
|
|
Set **ret) {
|
|
|
|
_cleanup_set_free_ Set *found = NULL;
|
|
int r;
|
|
|
|
assert(j);
|
|
assert(fields);
|
|
assert(ret);
|
|
|
|
NULSTR_FOREACH(field, fields) {
|
|
const void *data;
|
|
size_t size;
|
|
|
|
r = sd_journal_query_unique(j, field);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
SD_JOURNAL_FOREACH_UNIQUE(j, data, size) {
|
|
_cleanup_free_ char *u = NULL;
|
|
char *eq;
|
|
|
|
eq = memchr(data, '=', size);
|
|
if (eq) {
|
|
size -= eq - (char*) data + 1;
|
|
data = ++eq;
|
|
}
|
|
|
|
u = strndup(data, size);
|
|
if (!u)
|
|
return -ENOMEM;
|
|
|
|
size_t i;
|
|
if (!strv_fnmatch_full(patterns, u, FNM_NOESCAPE, &i))
|
|
continue;
|
|
|
|
log_debug("Matched %s with pattern %s=%s", u, field, patterns[i]);
|
|
r = set_ensure_consume(&found, &string_hash_ops_free, TAKE_PTR(u));
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
*ret = TAKE_PTR(found);
|
|
return 0;
|
|
}
|
|
|
|
int acquire_unit(sd_journal *j, const char *option_name, const char **ret_unit, LogIdType *ret_type) {
|
|
size_t n;
|
|
int r;
|
|
|
|
assert(j);
|
|
assert(option_name);
|
|
assert(ret_unit);
|
|
assert(ret_type);
|
|
|
|
n = strv_length(arg_system_units) + strv_length(arg_user_units);
|
|
if (n <= 0)
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
"Using %s requires a unit. Please specify a unit name with -u/--unit=/--user-unit=.",
|
|
option_name);
|
|
if (n > 1)
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
"Using %s with multiple units is not supported.",
|
|
option_name);
|
|
|
|
LogIdType type;
|
|
char **units;
|
|
if (!strv_isempty(arg_system_units)) {
|
|
type = LOG_SYSTEM_UNIT_INVOCATION_ID;
|
|
units = arg_system_units;
|
|
} else {
|
|
assert(!strv_isempty(arg_user_units));
|
|
type = LOG_USER_UNIT_INVOCATION_ID;
|
|
units = arg_user_units;
|
|
}
|
|
|
|
_cleanup_free_ char *u = NULL;
|
|
r = unit_name_mangle(units[0], UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN), &u);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to mangle unit name '%s': %m", units[0]);
|
|
|
|
if (string_is_glob(u)) {
|
|
_cleanup_set_free_ Set *s = NULL;
|
|
|
|
r = get_possible_units(j, type == LOG_SYSTEM_UNIT_INVOCATION_ID ? SYSTEM_UNITS : USER_UNITS,
|
|
STRV_MAKE(u), &s);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to get matching unit '%s' from journal: %m", u);
|
|
if (set_isempty(s))
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No matching unit found for '%s' in journal.", u);
|
|
if (set_size(s) > 1)
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple matching units found for '%s' in journal.", u);
|
|
|
|
char *found = set_steal_first(s);
|
|
log_debug("Found matching unit '%s' for '%s'.", found, u);
|
|
|
|
free_and_replace(units[0], found);
|
|
assert(set_isempty(s));
|
|
} else
|
|
free_and_replace(units[0], u);
|
|
|
|
*ret_type = type;
|
|
*ret_unit = units[0];
|
|
return 0;
|
|
}
|
|
|
|
int journal_acquire_invocation(sd_journal *j) {
|
|
LogIdType type = LOG_SYSTEM_UNIT_INVOCATION_ID;
|
|
const char *unit = NULL;
|
|
sd_id128_t id;
|
|
int r;
|
|
|
|
assert(j);
|
|
|
|
/* journal_acquire_boot() must be called before this. */
|
|
|
|
if (!arg_invocation) {
|
|
/* Clear relevant field for safety. */
|
|
arg_invocation_id = SD_ID128_NULL;
|
|
arg_invocation_offset = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* When an invocation ID is explicitly specified without an offset, we do not care the ID is about
|
|
* system unit or user unit, and calling without unit name is allowed. Otherwise, a unit name must
|
|
* be specified. */
|
|
if (arg_invocation_offset != 0 || sd_id128_is_null(arg_invocation_id)) {
|
|
r = acquire_unit(j, "-I/--invocation= with an offset", &unit, &type);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
r = journal_find_log_id(j, type, arg_boot_id, unit, arg_invocation_id, arg_invocation_offset, &id);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to find journal entry for the invocation (%s%+i): %m",
|
|
sd_id128_is_null(arg_invocation_id) ? "" : SD_ID128_TO_STRING(arg_invocation_id),
|
|
arg_invocation_offset);
|
|
if (r == 0)
|
|
return log_error_errno(SYNTHETIC_ERRNO(ENODATA),
|
|
"No journal entry found for the invocation (%s%+i).",
|
|
sd_id128_is_null(arg_invocation_id) ? "" : SD_ID128_TO_STRING(arg_invocation_id),
|
|
arg_invocation_offset);
|
|
|
|
log_debug("Found invocation ID %s for %s%+i",
|
|
SD_ID128_TO_STRING(id),
|
|
sd_id128_is_null(arg_invocation_id) ? "" : SD_ID128_TO_STRING(arg_invocation_id),
|
|
arg_invocation_offset);
|
|
|
|
arg_invocation_id = id;
|
|
return 1;
|
|
}
|