* systemd-nspawn will tint the terminal background for containers in a
blueish color. This can be controller with the new --background=
- switch.
+ switch or the new $SYSTEMD_TINT_BACKGROUND environment variable.
* systemd-nspawn gained support for the 'owneridmap' option for --bind=
mounts to map the target directory owner from inside the container to
* systemd-boot's automatic SecureBoot enrollment support gained support
for enrolling "dbx" too (Previously, only db/KEK/PK enrollment was
- supported). It also now supports UEFI "Custom" mode.
+ supported). It also now supports UEFI "Custom" and "Audit" modes.
* The pcrlock policy is saved in an unencrypted credential file
"pcrlock.<entry-token>.cred" under XBOOTLDR/ESP in the
* systemd-run/run0 will now tint the terminal background on supported
terminals: in a reddish tone when invoking a root service, in a
yellowish tone otherwise. This may be controlled and turned off via
- the new --background= switch.
+ the new --background= switch or the new $SYSTEMD_TINT_BACKGROUND
+ environment variable.
* systemd-run gained a new option '--ignore-failure' to suppress
command failures.
* A new environment variable $SYSTEMD_VMSPAWN_QEMU_EXTRA may carry
additional qemu command line options to pass to qemu.
+ * systemd-machined gained a new GetMachineSSHInfo() D-Bus method that is
+ used by systemd-vmspawn to fetch the information needed to ssh into the
+ machine.
+
+ * systemd-machined gained a new Varlink interface that is used by
+ systemd-vmspawn to register machines with additional information and
+ metadata.
+
systemd-repart:
* systemd-repart gained new options --generate-fstab= and
libkmod might cause problems with boot. This affects dracut <= 101,
see https://github.com/dracut-ng/dracut-ng/commit/04b362d713235459cf.
+ * systemd ELF binaries that use libraries via dlopen() are now built with
+ a new ELF header note section, following a new specification defined at
+ docs/ELF_DLOPEN_METADATA.md, that provides information about which
+ sonames are loaded and used if found at runtime. This allows tools and
+ packagers to programmatically discover the list of optional
+ dependencies used by all systemd ELF binaries. A parser with packaging
+ integration tools is available at
+ https://github.com/systemd/package-notes
+
* The sd-journal API gained a new call
sd_journal_stream_fd_with_namespace() which is just like
sd_journal_stream_fd() but creates a log stream targeted at a
<varlistentry>
<term><option>--list-boots</option></term>
- <listitem><para>Show a tabular list of boot numbers (relative to the current boot), their IDs, and
- the timestamps of the first and last message pertaining to the boot.</para>
-
- <xi:include href="version-info.xml" xpointer="v209"/></listitem>
+ <listitem>
+ <para>Show a tabular list of boot numbers (relative to the current boot), their IDs, and the
+ timestamps of the first and last message pertaining to the boot. When specified with
+ <option>-n/--lines=<optional>+</optional><replaceable>N</replaceable></option> option, only the
+ first (when the number prefixed with <literal>+</literal>) or the last (without prefix)
+ <replaceable>N</replaceable> entries will be shown. When specified with
+ <option>-r/--reverse</option>, the list will be shown in the reverse order.</para>
+
+ <xi:include href="version-info.xml" xpointer="v209"/>
+ </listitem>
</varlistentry>
<varlistentry>
struct timespec tspec;
timespec_store(&tspec, ts);
- if (futimens(dfd, (const struct timespec[2]) { { .tv_nsec = UTIME_OMIT }, tspec }) < 0)
+ if (futimens(dfd, (const struct timespec[2]) { TIMESPEC_OMIT, tspec }) < 0)
return -errno;
if (futimens(nfd, (const struct timespec[2]) { tspec, tspec }) < 0)
#define DUAL_TIMESTAMP_INFINITY ((dual_timestamp) { USEC_INFINITY, USEC_INFINITY })
#define TRIPLE_TIMESTAMP_NULL ((triple_timestamp) {})
+#define TIMESPEC_OMIT ((const struct timespec) { .tv_nsec = UTIME_OMIT })
+
usec_t now(clockid_t clock);
nsec_t now_nsec(clockid_t clock);
return service_set_state(s, SERVICE_START);
default:
- ;
+ assert_not_reached();
}
- assert_not_reached();
-
fail:
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
* is run first. */
if (c->log_namespace) {
- FOREACH_STRING(s, "systemd-journald", "systemd-journald-varlink") {
- _cleanup_free_ char *socket_unit = NULL;
+ static const struct {
+ const char *template;
+ UnitType type;
+ } deps[] = {
+ { "systemd-journald", UNIT_SOCKET, },
+ { "systemd-journald-varlink", UNIT_SOCKET, },
+ { "systemd-journald-sync", UNIT_SERVICE, },
+ };
+
+ FOREACH_ELEMENT(i, deps) {
+ _cleanup_free_ char *unit = NULL;
- r = unit_name_build_from_type(s, c->log_namespace, UNIT_SOCKET, &socket_unit);
+ r = unit_name_build_from_type(i->template, c->log_namespace, i->type, &unit);
if (r < 0)
return r;
- r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, socket_unit, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, unit, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
-
- _cleanup_free_ char *sync_unit = NULL;
-
- r = unit_name_build_from_type("systemd-journald-sync", c->log_namespace, UNIT_SERVICE, &sync_unit);
- if (r < 0)
- return r;
-
- r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, sync_unit, true, UNIT_DEPENDENCY_FILE);
- if (r < 0)
- return r;
} else {
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
#include "journal-internal.h"
#include "journalctl.h"
#include "journalctl-filter.h"
+#include "journalctl-util.h"
#include "logs-show.h"
#include "missing_sched.h"
#include "nulstr-util.h"
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 we 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_root)
- return add_match_this_boot(j, arg_machine);
-
- if (sd_id128_is_null(arg_boot_id)) {
- r = journal_find_boot_by_offset(j, arg_boot_offset, &arg_boot_id);
- if (r < 0)
- return log_error_errno(r, "Failed to find journal entry from the specified boot offset (%+i): %m",
- arg_boot_offset);
- if (r == 0)
- return log_error_errno(SYNTHETIC_ERRNO(ENODATA),
- "No journal boot entry found from the specified boot offset (%+i).",
- arg_boot_offset);
- } else {
- r = journal_find_boot_by_id(j, arg_boot_id);
- if (r < 0)
- return log_error_errno(r, "Failed to find journal entry from the specified boot ID (%s): %m",
- SD_ID128_TO_STRING(arg_boot_id));
- if (r == 0)
- return log_error_errno(SYNTHETIC_ERRNO(ENODATA),
- "No journal boot entry found from the specified boot ID (%s).",
- SD_ID128_TO_STRING(arg_boot_id));
- }
+ assert(!sd_id128_is_null(arg_boot_id));
r = add_match_boot_id(j, arg_boot_id);
if (r < 0)
- return log_error_errno(r, "Failed to add match: %m");
-
- r = sd_journal_add_conjunction(j);
- if (r < 0)
- return log_error_errno(r, "Failed to add conjunction: %m");
+ return r;
- return 0;
+ return sd_journal_add_conjunction(j);
}
static int add_dmesg(sd_journal *j) {
if (r < 0)
return r;
} else {
- r = add_matches_for_user_unit(j, u, getuid());
+ r = add_matches_for_user_unit(j, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
return r;
SET_FOREACH(u, units) {
- r = add_matches_for_user_unit(j, u, getuid());
+ r = add_matches_for_user_unit(j, u);
if (r < 0)
return r;
r = sd_journal_add_disjunction(j);
assert(j);
- /* add_boot() must be called first!
- * It may need to seek the journal to find parent boot IDs. */
- r = add_boot(j);
+ /* First, search boot ID, as that may set and flush matches and seek journal. */
+ r = journal_acquire_boot(j);
if (r < 0)
return r;
+ /* Clear unexpected matches for safety. */
+ sd_journal_flush_matches(j);
+
+ /* Then, add filters in the below. */
+ r = add_boot(j);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add filter for boot: %m");
+
r = add_dmesg(j);
if (r < 0)
return log_error_errno(r, "Failed to add filter for dmesg: %m");
if (r < 0)
return r;
- r = journal_get_boots(j, &boots, &n_boots);
+ r = journal_get_boots(
+ j,
+ /* advance_older = */ arg_lines_needs_seek_end(),
+ /* max_ids = */ arg_lines >= 0 ? (size_t) arg_lines : SIZE_MAX,
+ &boots, &n_boots);
if (r < 0)
return log_error_errno(r, "Failed to determine boots: %m");
if (r == 0)
- return 0;
+ return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_ERR, SYNTHETIC_ERRNO(ENODATA),
+ "No boot found.");
table = table_new("idx", "boot id", "first entry", "last entry");
if (!table)
(void) table_set_sort(table, (size_t) 0);
(void) table_set_reverse(table, 0, arg_reverse);
- FOREACH_ARRAY(i, boots, n_boots) {
+ for (int i = 0; i < (int) n_boots; i++) {
+ int index;
+
+ if (arg_lines_needs_seek_end())
+ /* With --lines=N, we only know the negative index, and the older ID is located earlier. */
+ index = -i;
+ else if (arg_lines >= 0)
+ /* With --lines=+N, we only know the positive index, and the newer ID is located earlier. */
+ index = i + 1;
+ else
+ /* Otherwise, show negative index. Note, in this case, newer ID is located earlier. */
+ index = i + 1 - (int) n_boots;
+
r = table_add_many(table,
- TABLE_INT, (int)(i - boots) - (int) n_boots + 1,
+ TABLE_INT, index,
TABLE_SET_ALIGN_PERCENT, 100,
- TABLE_ID128, i->id,
- TABLE_TIMESTAMP, i->first_usec,
- TABLE_TIMESTAMP, i->last_usec);
+ TABLE_ID128, boots[i].id,
+ TABLE_TIMESTAMP, boots[i].first_usec,
+ TABLE_TIMESTAMP, boots[i].last_usec);
if (r < 0)
return table_log_add_error(r);
}
#include <unistd.h>
+#include "id128-util.h"
#include "journal-util.h"
#include "journalctl.h"
#include "journalctl-util.h"
+#include "logs-show.h"
#include "rlimit-util.h"
#include "sigbus.h"
#include "terminal-util.h"
return true;
}
+
+int journal_acquire_boot(sd_journal *j) {
+ int r;
+
+ assert(j);
+
+ if (!arg_boot) {
+ /* Clear relevant field for safety. */
+ arg_boot_id = SD_ID128_NULL;
+ arg_boot_offset = 0;
+ 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 from 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 from 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;
+}
char* format_timestamp_maybe_utc(char *buf, size_t l, usec_t t);
int acquire_journal(sd_journal **ret);
bool journal_boot_has_effect(sd_journal *j);
+int journal_acquire_boot(sd_journal *j);
STATIC_DESTRUCTOR_REGISTER(arg_compiled_pattern, pattern_freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
-static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset) {
+static int parse_id_descriptor(const char *x, sd_id128_t *ret_id, int *ret_offset) {
sd_id128_t id = SD_ID128_NULL;
int off = 0, r;
+ assert(x);
+ assert(ret_id);
+ assert(ret_offset);
+
if (streq(x, "all")) {
- *boot_id = SD_ID128_NULL;
- *offset = 0;
+ *ret_id = SD_ID128_NULL;
+ *ret_offset = 0;
return 0;
- } else if (strlen(x) >= SD_ID128_STRING_MAX - 1) {
+ }
+
+ if (strlen(x) >= SD_ID128_STRING_MAX - 1) {
char *t;
t = strndupa_safe(x, SD_ID128_STRING_MAX - 1);
return r;
}
- if (boot_id)
- *boot_id = id;
-
- if (offset)
- *offset = off;
-
+ *ret_id = id;
+ *ret_offset = off;
return 1;
}
arg_boot_offset = 0;
if (optarg) {
- r = parse_boot_descriptor(optarg, &arg_boot_id, &arg_boot_offset);
+ r = parse_id_descriptor(optarg, &arg_boot_id, &arg_boot_offset);
if (r < 0)
return log_error_errno(r, "Failed to parse boot descriptor '%s'", optarg);
arg_boot = r;
- /* Hmm, no argument? Maybe the next
- * word on the command line is
- * supposed to be the argument? Let's
- * see if there is one and is parsable
- * as a boot descriptor... */
} else if (optind < argc) {
- r = parse_boot_descriptor(argv[optind], &arg_boot_id, &arg_boot_offset);
+ /* Hmm, no argument? Maybe the next word on the command line is supposed to be the
+ * argument? Let's see if there is one and is parsable as a boot descriptor... */
+ r = parse_id_descriptor(argv[optind], &arg_boot_id, &arg_boot_offset);
if (r >= 0) {
arg_boot = r;
optind++;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Please specify either --reverse or --follow, not both.");
- if (arg_lines >= 0 && arg_lines_oldest && (arg_reverse || arg_follow))
+ if (arg_action == ACTION_SHOW && arg_lines >= 0 && arg_lines_oldest && (arg_reverse || arg_follow))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--lines=+N is unsupported when --reverse or --follow is specified.");
static void test_boot_id_one(void (*setup)(void), size_t n_boots_expected) {
char t[] = "/var/tmp/journal-boot-id-XXXXXX";
- sd_journal *j;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
_cleanup_free_ BootId *boots = NULL;
size_t n_boots;
setup();
assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
- assert_se(journal_get_boots(j, &boots, &n_boots) >= 0);
+ assert_se(journal_get_boots(
+ j,
+ /* advance_older = */ false, /* max_ids = */ SIZE_MAX,
+ &boots, &n_boots) >= 0);
assert_se(boots);
assert_se(n_boots == n_boots_expected);
- sd_journal_close(j);
- FOREACH_ARRAY(b, boots, n_boots) {
- assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
- assert_se(journal_find_boot_by_id(j, b->id) == 1);
- sd_journal_close(j);
+ for (size_t i = 0; i < n_boots; i++) {
+ sd_id128_t id;
+
+ /* positive offset */
+ assert_se(journal_find_boot(j, SD_ID128_NULL, (int) (i + 1), &id) == 1);
+ assert_se(sd_id128_equal(id, boots[i].id));
+
+ /* negative offset */
+ assert_se(journal_find_boot(j, SD_ID128_NULL, (int) (i + 1) - (int) n_boots, &id) == 1);
+ assert_se(sd_id128_equal(id, boots[i].id));
+
+ for (size_t k = 0; k < n_boots; k++) {
+ int offset = (int) k - (int) i;
+
+ /* relative offset */
+ assert_se(journal_find_boot(j, boots[i].id, offset, &id) == 1);
+ assert_se(sd_id128_equal(id, boots[k].id));
+ }
}
- for (int i = - (int) n_boots + 1; i <= (int) n_boots; i++) {
- sd_id128_t id;
+ for (size_t i = 0; i <= n_boots_expected + 1; i++) {
+ _cleanup_free_ BootId *boots_limited = NULL;
+ size_t n_boots_limited;
+
+ assert_se(journal_get_boots(
+ j,
+ /* advance_older = */ false, /* max_ids = */ i,
+ &boots_limited, &n_boots_limited) >= 0);
+ assert_se(boots_limited || i == 0);
+ assert_se(n_boots_limited == MIN(i, n_boots_expected));
+ assert_se(memcmp_safe(boots, boots_limited, n_boots_limited * sizeof(BootId)) == 0);
+ }
- assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
- assert_se(journal_find_boot_by_offset(j, i, &id) == 1);
- if (i <= 0)
- assert_se(sd_id128_equal(id, boots[n_boots + i - 1].id));
- else
- assert_se(sd_id128_equal(id, boots[i - 1].id));
- sd_journal_close(j);
+ for (size_t i = 0; i <= n_boots_expected + 1; i++) {
+ _cleanup_free_ BootId *boots_limited = NULL;
+ size_t n_boots_limited;
+
+ assert_se(journal_get_boots(
+ j,
+ /* advance_older = */ true, /* max_ids = */ i,
+ &boots_limited, &n_boots_limited) >= 0);
+ assert_se(boots_limited || i == 0);
+ assert_se(n_boots_limited == MIN(i, n_boots_expected));
+ for (size_t k = 0; k < n_boots_limited; k++)
+ assert_se(memcmp(&boots[n_boots - k - 1], &boots_limited[k], sizeof(BootId)) == 0);
}
test_done(t);
show_journal_by_unit(
stdout,
i.scope,
- NULL,
+ /* namespace = */ NULL,
arg_output,
- 0,
+ /* n_columns = */ 0,
i.timestamp.monotonic,
arg_lines,
- 0,
get_output_flags() | OUTPUT_BEGIN_NEWLINE,
SD_JOURNAL_LOCAL_ONLY,
- true,
- NULL);
+ /* system_unit = */ true,
+ /* ellipsized = */ NULL);
}
return 0;
show_journal_by_unit(
stdout,
i.slice,
- NULL,
+ /* namespace = */ NULL,
arg_output,
- 0,
+ /* n_columns = */ 0,
i.timestamp.monotonic,
arg_lines,
- 0,
get_output_flags() | OUTPUT_BEGIN_NEWLINE,
SD_JOURNAL_LOCAL_ONLY,
- true,
- NULL);
+ /* system_unit = */ true,
+ /* ellipsized = */ NULL);
}
return 0;
show_journal_by_unit(
stdout,
i->unit,
- NULL,
+ /* namespace = */ NULL,
arg_output,
- 0,
+ /* n_columns = */ 0,
i->timestamp.monotonic,
arg_lines,
- 0,
get_output_flags() | OUTPUT_BEGIN_NEWLINE,
SD_JOURNAL_LOCAL_ONLY,
- true,
- NULL);
+ /* system_unit = */ true,
+ /* ellipsized = */ NULL);
}
}
struct timespec tspec;
timespec_store(&tspec, ts);
- if (futimens(pfd, (const struct timespec[2]) { { .tv_nsec = UTIME_OMIT }, tspec }) < 0)
+ if (futimens(pfd, (const struct timespec[2]) { TIMESPEC_OMIT, tspec }) < 0)
return -errno;
}
}
return r;
}
-int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
+int add_matches_for_user_unit(sd_journal *j, const char *unit) {
+ uid_t uid = getuid();
int r;
assert(j);
r = id128_get_boot_for_machine(machine, &boot_id);
if (r < 0)
return log_error_errno(r, "Failed to get boot ID%s%s: %m",
- isempty(machine) ? "" : " of container ", machine);
+ isempty(machine) ? "" : " of container ", strempty(machine));
r = add_match_boot_id(j, boot_id);
if (r < 0)
unsigned n_columns,
usec_t not_before,
unsigned how_many,
- uid_t uid,
OutputFlags flags,
int journal_open_flags,
bool system_unit,
if (system_unit)
r = add_matches_for_unit(j, unit);
else
- r = add_matches_for_user_unit(j, unit, uid);
+ r = add_matches_for_user_unit(j, unit);
if (r < 0)
return log_error_errno(r, "Failed to add unit matches: %m");
if (r < 0)
return r;
if (r == 0) {
+ sd_journal_flush_matches(j);
*ret = (BootId) {};
return 0; /* End of journal, yay. */
}
goto try_again;
}
- r = sd_journal_get_realtime_usec(j, &boot.first_usec);
+ r = sd_journal_get_realtime_usec(j, advance_older ? &boot.last_usec : &boot.first_usec);
if (r < 0)
return r;
goto try_again;
}
- r = sd_journal_get_realtime_usec(j, &boot.last_usec);
+ r = sd_journal_get_realtime_usec(j, advance_older ? &boot.first_usec : &boot.last_usec);
if (r < 0)
return r;
}
}
-int journal_find_boot_by_id(sd_journal *j, sd_id128_t boot_id) {
- int r;
-
- assert(j);
- assert(!sd_id128_is_null(boot_id));
-
- sd_journal_flush_matches(j);
-
- r = add_match_boot_id(j, boot_id);
- if (r < 0)
- return r;
-
- r = sd_journal_seek_head(j); /* seek to oldest */
- if (r < 0)
- return r;
-
- r = sd_journal_next(j); /* read the oldest entry */
- if (r < 0)
- return r;
-
- /* At this point the read pointer is positioned at the oldest occurrence of the reference boot ID.
- * After flushing the matches, one more invocation of _previous() will hence place us at the
- * following entry, which must then have an older boot ID */
-
- sd_journal_flush_matches(j);
- return r > 0;
-}
-
-int journal_find_boot_by_offset(sd_journal *j, int offset, sd_id128_t *ret) {
+int journal_find_boot(sd_journal *j, sd_id128_t boot_id, int offset, sd_id128_t *ret) {
bool advance_older;
- int r;
+ int r, offset_start;
assert(j);
assert(ret);
* (chronological) first boot in the journal. */
advance_older = offset <= 0;
- if (advance_older)
- r = sd_journal_seek_tail(j); /* seek to newest */
- else
- r = sd_journal_seek_head(j); /* seek to oldest */
- if (r < 0)
- return r;
+ sd_journal_flush_matches(j);
- /* No sd_journal_next()/_previous() here.
- *
- * At this point the read pointer is positioned after the newest/before the oldest entry in the whole
- * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest
- * entry we have. */
+ if (!sd_id128_is_null(boot_id)) {
+ r = add_match_boot_id(j, boot_id);
+ if (r < 0)
+ return r;
- sd_id128_t boot_id = SD_ID128_NULL;
- for (int off = !advance_older; ; off += advance_older ? -1 : 1) {
+ if (advance_older)
+ r = sd_journal_seek_head(j); /* seek to oldest */
+ else
+ r = sd_journal_seek_tail(j); /* seek to newest */
+ if (r < 0)
+ return r;
+
+ r = sd_journal_step_one(j, advance_older);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ sd_journal_flush_matches(j);
+ *ret = SD_ID128_NULL;
+ return false;
+ }
+ if (offset == 0) {
+ /* If boot ID is specified without an offset, then let's short cut the loop below. */
+ sd_journal_flush_matches(j);
+ *ret = boot_id;
+ return true;
+ }
+
+ offset_start = advance_older ? -1 : 1;
+ } else {
+ if (advance_older)
+ r = sd_journal_seek_tail(j); /* seek to newest */
+ else
+ r = sd_journal_seek_head(j); /* seek to oldest */
+ if (r < 0)
+ return r;
+
+ offset_start = advance_older ? 0 : 1;
+ }
+
+ /* At this point the cursor is positioned at the newest/oldest entry of the reference boot ID if
+ * specified, or whole journal otherwise. The next invocation of _previous()/_next() will hence
+ * position us at the newest/oldest entry we have. */
+
+ for (int off = offset_start; ; off += advance_older ? -1 : 1) {
BootId boot;
r = discover_next_boot(j, boot_id, advance_older, &boot);
boot_id = boot.id;
log_debug("Found boot ID %s by offset %i", SD_ID128_TO_STRING(boot_id), off);
- if (off == offset)
- break;
+ if (off == offset) {
+ *ret = boot_id;
+ return true;
+ }
}
-
- *ret = boot_id;
- return true;
}
-int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots) {
+int journal_get_boots(
+ sd_journal *j,
+ bool advance_older,
+ size_t max_ids,
+ BootId **ret_boots,
+ size_t *ret_n_boots) {
+
_cleanup_free_ BootId *boots = NULL;
size_t n_boots = 0;
int r;
assert(ret_boots);
assert(ret_n_boots);
- r = sd_journal_seek_head(j); /* seek to oldest */
+ sd_journal_flush_matches(j);
+
+ if (advance_older)
+ r = sd_journal_seek_tail(j); /* seek to newest */
+ else
+ r = sd_journal_seek_head(j); /* seek to oldest */
if (r < 0)
return r;
for (;;) {
BootId boot;
- r = discover_next_boot(j, previous_boot_id, /* advance_older = */ false, &boot);
+ if (n_boots >= max_ids)
+ break;
+
+ r = discover_next_boot(j, previous_boot_id, advance_older, &boot);
if (r < 0)
return r;
if (r == 0)
* Exiting as otherwise this problem would cause an infinite loop. */
goto finish;
- if (!GREEDY_REALLOC(boots, n_boots + 1))
+ if (!GREEDY_REALLOC_APPEND(boots, n_boots, &boot, 1))
return -ENOMEM;
-
- boots[n_boots++] = boot;
}
finish:
int add_matches_for_user_unit(
sd_journal *j,
- const char *unit,
- uid_t uid);
+ const char *unit);
int show_journal_by_unit(
FILE *f,
unsigned n_columns,
usec_t not_before,
unsigned how_many,
- uid_t uid,
OutputFlags flags,
int journal_open_flags,
bool system_unit,
size_t l,
OutputFlags flags);
-int journal_find_boot_by_id(sd_journal *j, sd_id128_t boot_id);
-int journal_find_boot_by_offset(sd_journal *j, int offset, sd_id128_t *ret);
-int journal_get_boots(sd_journal *j, BootId **ret_boots, size_t *ret_n_boots);
+int journal_find_boot(sd_journal *j, sd_id128_t boot_id, int offset, sd_id128_t *ret);
+int journal_get_boots(
+ sd_journal *j,
+ bool advance_older,
+ size_t max_ids,
+ BootId **ret_boots,
+ size_t *ret_n_boots);
i->id,
i->log_namespace,
arg_output,
- 0,
+ /* n_columns = */ 0,
i->inactive_exit_timestamp_monotonic,
arg_lines,
- getuid(),
get_output_flags() | OUTPUT_BEGIN_NEWLINE,
SD_JOURNAL_LOCAL_ONLY,
arg_runtime_scope == RUNTIME_SCOPE_SYSTEM,
lsan_options = None
ubsan_options = None
with_coverage = False
+show_journal = True # When true, show journal on stopping networkd.
active_units = []
protected_links = {
return f
+def expectedFailureIfKernelReturnsInvalidFlags():
+ '''
+ This checks the kernel bug caused by 3ddc2231c8108302a8229d3c5849ee792a63230d.
+ It will be fixed by the following patch:
+ https://patchwork.kernel.org/project/netdevbpf/patch/20240510072932.2678952-1-edumazet@google.com/
+ '''
+ def f(func):
+ call_quiet('ip link add dummy98 type dummy')
+ call_quiet('ip link set up dev dummy98')
+ call_quiet('ip address add 192.0.2.1/24 dev dummy98 noprefixroute')
+ output = check_output('ip address show dev dummy98')
+ remove_link('dummy98')
+ return func if 'noprefixroute' in output else unittest.expectedFailure(func)
+
+ return f
+
# pylint: disable=C0415
def compare_kernel_version(min_kernel_version):
try:
return call_quiet('systemctl is-failed -q systemd-networkd.service') != 1
def stop_networkd(show_logs=True):
+ global show_journal
+ show_logs = show_logs and show_journal
if show_logs:
invocation_id = networkd_invocation_id()
check_output('systemctl stop systemd-networkd.socket')
check_output('systemctl start systemd-networkd')
def restart_networkd(show_logs=True):
+ global show_journal
+ show_logs = show_logs and show_journal
if show_logs:
invocation_id = networkd_invocation_id()
check_output('systemctl restart systemd-networkd.service')
check_json(networkctl_json())
+ @expectedFailureIfKernelReturnsInvalidFlags()
def test_address_static(self):
copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
self.setup_nftset('addr4', 'ipv4_addr')
self.assertIn('DHCPREPLY(veth-peer)', output)
self.assertIn('sent size: 0 option: 14 rapid-commit', output)
+ @expectedFailureIfKernelReturnsInvalidFlags()
def test_dhcp_client_ipv4_only(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options')
parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options')
parser.add_argument('--with-coverage', help='Loosen certain sandbox restrictions to make gcov happy', dest='with_coverage', type=bool, nargs='?', const=True, default=with_coverage)
+ parser.add_argument('--no-journal', help='Do not show journal of systemd-networkd on stop', dest='show_journal', action='store_false')
ns, unknown_args = parser.parse_known_args(namespace=unittest)
if ns.build_dir:
lsan_options = ns.lsan_options
ubsan_options = ns.ubsan_options
with_coverage = ns.with_coverage
+ show_journal = ns.show_journal
if use_valgrind:
# Do not forget the trailing space.