section, then all assigned VLAN IDs on the interface that are not
configured in the .network file are removed.
+ * systemd-gpt-auto-generator will stop generating units for ESP or
+ XBOOTLDR partitions if it finds mount entries in the /boot/ or /efi/
+ hierarchies in fstab. This is to prevent the generator from
+ interfering with systems where ESP is explicitly configured to be
+ mounted at some path, for example /boot/efi/ (this type of setup is
+ obsolete but is still commonly found).
+
Network Management:
* systemd-networkd's proxy support gained a new option to configure
<para><filename>systemd-gpt-auto-generator</filename> is a unit generator that automatically discovers
the root partition, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>,
- <filename>/var/tmp/</filename>, the EFI System Partition, the Extended Boot Loader Partition, and swap
- partitions and creates mount and swap units for them, based on the partition type GUIDs of GUID partition
- tables (GPT). See <ulink url="https://uefi.org/specifications">UEFI Specification</ulink>, chapter 5 for
- more details. It implements the <ulink
+ <filename>/var/tmp/</filename>, the EFI System Partition (ESP), the Extended Boot Loader Partition
+ (XBOOTLDR), and swap partitions and creates mount and swap units for them, based on the partition type
+ GUIDs of GUID partition tables (GPT). See <ulink url="https://uefi.org/specifications">UEFI
+ Specification</ulink>, chapter 5 for more details. It implements the <ulink
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable
Partitions Specification</ulink>.</para>
<para>Note that this generator has no effect on non-GPT systems. It will also not create mount point
configuration for directories which already contain files or if the mount point is explicitly configured
in <citerefentry
- project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>. If
- the units this generator creates are overridden, for example by units in directories with higher
+ project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Additionally
+ no unit will be created for the ESP or the XBOOTLDR partition if mount entries are found in the
+ <filename>/boot/</filename> or <filename>/efi/</filename> hierarchies in <citerefentry
+ project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
+ <para>If the units this generator creates are overridden, for example by units in directories with higher
precedence, drop-ins and additional dependencies created by this generator might still be used.</para>
<para>This generator will only look for the root partition on the same physical disk where the EFI System
<varlistentry>
<term><varname>Restart=</varname></term>
- <listitem><para>Configures whether the service shall be
- restarted when the service process exits, is killed, or a
- timeout is reached. The service process may be the main
- service process, but it may also be one of the processes
- specified with <varname>ExecStartPre=</varname>,
- <varname>ExecStartPost=</varname>,
- <varname>ExecStop=</varname>,
- <varname>ExecStopPost=</varname>, or
- <varname>ExecReload=</varname>. When the death of the process
- is a result of systemd operation (e.g. service stop or
- restart), the service will not be restarted. Timeouts include
- missing the watchdog "keep-alive ping" deadline and a service
- start, reload, and stop operation timeouts.</para>
-
- <para>Takes one of
- <option>no</option>,
- <option>on-success</option>,
- <option>on-failure</option>,
- <option>on-abnormal</option>,
- <option>on-watchdog</option>,
- <option>on-abort</option>, or
- <option>always</option>.
- If set to <option>no</option> (the default), the service will
- not be restarted. If set to <option>on-success</option>, it
- will be restarted only when the service process exits cleanly.
+ <listitem><para>Configures whether the service shall be restarted when the service process exits,
+ is killed, or a timeout is reached. The service process may be the main service process, but it may
+ also be one of the processes specified with <varname>ExecStartPre=</varname>,
+ <varname>ExecStartPost=</varname>, <varname>ExecStop=</varname>, <varname>ExecStopPost=</varname>,
+ or <varname>ExecReload=</varname>. When the death of the process is a result of systemd operation
+ (e.g. service stop or restart), the service will not be restarted. Timeouts include missing the watchdog
+ "keep-alive ping" deadline and a service start, reload, and stop operation timeouts.</para>
+
+ <para>Takes one of <option>no</option>, <option>on-success</option>, <option>on-failure</option>,
+ <option>on-abnormal</option>, <option>on-watchdog</option>, <option>on-abort</option>, or
+ <option>always</option>. If set to <option>no</option> (the default), the service will not be restarted.
+ If set to <option>on-success</option>, it will be restarted only when the service process exits cleanly.
In this context, a clean exit means any of the following:
<itemizedlist>
<listitem><simpara>exit code of 0;</simpara></listitem>
- <listitem><simpara>for types other than
- <varname>Type=oneshot</varname>, one of the signals
- <constant>SIGHUP</constant>,
- <constant>SIGINT</constant>,
- <constant>SIGTERM</constant>, or
- <constant>SIGPIPE</constant>;</simpara></listitem>
+ <listitem><simpara>for types other than <varname>Type=oneshot</varname>, one of the signals
+ <constant>SIGHUP</constant>, <constant>SIGINT</constant>,
+ <constant>SIGTERM</constant>, or <constant>SIGPIPE</constant>;
+ </simpara></listitem>
<listitem><simpara>exit statuses and signals specified in
<varname>SuccessExitStatus=</varname>.</simpara></listitem>
</itemizedlist>
- If set to
- <option>on-failure</option>, the service will be restarted
- when the process exits with a non-zero exit code, is
- terminated by a signal (including on core dump, but excluding
- the aforementioned four signals), when an operation (such as
- service reload) times out, and when the configured watchdog
- timeout is triggered. If set to <option>on-abnormal</option>,
- the service will be restarted when the process is terminated
- by a signal (including on core dump, excluding the
- aforementioned four signals), when an operation times out, or
- when the watchdog timeout is triggered. If set to
- <option>on-abort</option>, the service will be restarted only
- if the service process exits due to an uncaught signal not
- specified as a clean exit status. If set to
- <option>on-watchdog</option>, the service will be restarted
- only if the watchdog timeout for the service expires. If set
- to <option>always</option>, the service will be restarted
- regardless of whether it exited cleanly or not, got terminated
- abnormally by a signal, or hit a timeout.</para>
+ If set to <option>on-failure</option>, the service will be restarted when the process exits with
+ a non-zero exit code, is terminated by a signal (including on core dump, but excluding the aforementioned
+ four signals), when an operation (such as service reload) times out, and when the configured watchdog
+ timeout is triggered. If set to <option>on-abnormal</option>, the service will be restarted when
+ the process is terminated by a signal (including on core dump, excluding the aforementioned four signals),
+ when an operation times out, or when the watchdog timeout is triggered. If set to <option>on-abort</option>,
+ the service will be restarted only if the service process exits due to an uncaught signal not specified
+ as a clean exit status. If set to <option>on-watchdog</option>, the service will be restarted
+ only if the watchdog timeout for the service expires. If set to <option>always</option>, the service
+ will be restarted regardless of whether it exited cleanly or not, got terminated abnormally by
+ a signal, or hit a timeout. Note that <varname>Type=oneshot</varname> services will never be restarted
+ on a clean exit status, i.e. <option>always</option> and <option>on-success</option> are rejected
+ for them.</para>
<table>
<title>Exit causes and the effect of the <varname>Restart=</varname> settings</title>
<varlistentry>
<term><varname>RestartForceExitStatus=</varname></term>
- <listitem><para>Takes a list of exit status definitions that,
- when returned by the main service process, will force automatic
- service restarts, regardless of the restart setting configured
- with <varname>Restart=</varname>. The argument format is
- similar to
- <varname>RestartPreventExitStatus=</varname>.</para>
+
+ <listitem><para>Takes a list of exit status definitions that, when returned by the main service
+ process, will force automatic service restarts, regardless of the restart setting configured with
+ <varname>Restart=</varname>. The argument format is similar to <varname>RestartPreventExitStatus=</varname>.
+ </para>
+
+ <para>Note that for <varname>Type=oneshot</varname> services, a success exit status will prevent
+ them from auto-restarting, no matter whether the corresponding exit statuses are listed in this
+ option or not.</para>
<xi:include href="version-info.xml" xpointer="v215"/></listitem>
</varlistentry>
if (s->type != SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_START]->command_next)
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.");
- if (s->type == SERVICE_ONESHOT &&
- !IN_SET(s->restart, SERVICE_RESTART_NO, SERVICE_RESTART_ON_FAILURE, SERVICE_RESTART_ON_ABNORMAL, SERVICE_RESTART_ON_WATCHDOG, SERVICE_RESTART_ON_ABORT))
+ if (s->type == SERVICE_ONESHOT && IN_SET(s->restart, SERVICE_RESTART_ALWAYS, SERVICE_RESTART_ON_SUCCESS))
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has Restart= set to either always or on-success, which isn't allowed for Type=oneshot services. Refusing.");
- if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status))
- return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has RestartForceExitStatus= set, which isn't allowed for Type=oneshot services. Refusing.");
-
if (s->type == SERVICE_ONESHOT && s->exit_type == SERVICE_EXIT_CGROUP)
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has ExitType=cgroup set, which isn't allowed for Type=oneshot services. Refusing.");
static bool service_shall_restart(Service *s, const char **reason) {
assert(s);
+ assert(reason);
/* Don't restart after manual stops */
if (s->forbid_restart) {
/* Restart if the exit code/status are configured as restart triggers */
if (exit_status_set_test(&s->restart_force_status, s->main_exec_status.code, s->main_exec_status.status)) {
+ /* Don't allow Type=oneshot services to restart on success. Note that Restart=always/on-success
+ * is already rejected in service_verify. */
+ if (s->type == SERVICE_ONESHOT && s->result == SERVICE_SUCCESS) {
+ *reason = "service type and exit status";
+ return false;
+ }
+
*reason = "forced by exit status";
return true;
}
assert(ret);
if (arg_directory) {
- r = sd_journal_open_directory(&j, arg_directory, 0);
+ r = sd_journal_open_directory(&j, arg_directory, SD_JOURNAL_ASSUME_IMMUTABLE);
if (r < 0)
return log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory);
} else if (arg_root) {
- r = sd_journal_open_directory(&j, arg_root, SD_JOURNAL_OS_ROOT);
+ r = sd_journal_open_directory(&j, arg_root, SD_JOURNAL_OS_ROOT | SD_JOURNAL_ASSUME_IMMUTABLE);
if (r < 0)
return log_error_errno(r, "Failed to open journals in root directory: %s: %m", arg_root);
} else if (arg_file) {
- r = sd_journal_open_files(&j, (const char**)arg_file, 0);
+ r = sd_journal_open_files(&j, (const char**)arg_file, SD_JOURNAL_ASSUME_IMMUTABLE);
if (r < 0)
return log_error_errno(r, "Failed to open journal files: %m");
} else {
- r = sd_journal_open(&j, arg_all ? 0 : SD_JOURNAL_LOCAL_ONLY);
+ r = sd_journal_open(&j, arg_all ? 0 : SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE);
if (r < 0)
return log_error_errno(r, "Failed to open journal: %m");
}
return 1;
}
+static bool keymap_exists_bool(const char *name) {
+ return keymap_exists(name) > 0;
+}
+
+static typeof(&keymap_is_valid) determine_keymap_validity_func(int rfd) {
+ int r;
+
+ r = dir_fd_is_root(rfd);
+ if (r < 0)
+ log_debug_errno(r, "Unable to determine if operating on host root directory, assuming we are: %m");
+
+ return r != 0 ? keymap_exists_bool : keymap_is_valid;
+}
+
static int prompt_keymap(int rfd) {
_cleanup_strv_free_ char **kmaps = NULL;
int r;
print_welcome(rfd);
return prompt_loop("Please enter system keymap name or number",
- kmaps, 60, keymap_is_valid, &arg_keymap);
+ kmaps, 60, determine_keymap_validity_func(rfd), &arg_keymap);
}
static int process_keymap(int rfd) {
/* We check these conditions here instead of in parse_argv() so that we can take the root directory
* into account. */
+ if (arg_keymap && !determine_keymap_validity_func(rfd)(arg_keymap))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Keymap %s is not installed.", arg_keymap);
if (arg_locale && !locale_is_ok(rfd, arg_locale))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale);
if (arg_locale_messages && !locale_is_ok(rfd, arg_locale_messages))
return generator_add_symlink(arg_dest, SPECIAL_LOCAL_FS_TARGET, "wants", unit);
}
-static int slash_boot_in_fstab(void) {
- static int cache = -1;
-
- if (cache >= 0)
- return cache;
-
- cache = fstab_is_mount_point("/boot");
- if (cache < 0)
- return log_error_errno(cache, "Failed to parse fstab: %m");
- return cache;
-}
-
static int add_partition_xbootldr(DissectedPartition *p) {
_cleanup_free_ char *options = NULL;
int r;
return 0;
}
- r = slash_boot_in_fstab();
- if (r < 0)
- return r;
- if (r > 0) {
- log_debug("/boot/ specified in fstab, ignoring XBOOTLDR partition.");
- return 0;
- }
-
r = path_is_busy("/boot");
if (r < 0)
return r;
}
#if ENABLE_EFI
-static int slash_efi_in_fstab(void) {
- static int cache = -1;
-
- if (cache >= 0)
- return cache;
-
- cache = fstab_is_mount_point("/efi");
- if (cache < 0)
- return log_error_errno(cache, "Failed to parse fstab: %m");
- return cache;
-}
-
static bool slash_boot_exists(void) {
static int cache = -1;
* Otherwise, if /efi/ is unused and empty (or missing), we'll take that.
* Otherwise, we do nothing. */
if (!has_xbootldr && slash_boot_exists()) {
- r = slash_boot_in_fstab();
+ r = path_is_busy("/boot");
if (r < 0)
return r;
if (r == 0) {
- r = path_is_busy("/boot");
- if (r < 0)
- return r;
- if (r == 0) {
- esp_path = "/boot";
- id = "boot";
- }
+ esp_path = "/boot";
+ id = "boot";
}
}
if (!esp_path) {
- r = slash_efi_in_fstab();
- if (r < 0)
- return r;
- if (r > 0)
- return 0;
-
r = path_is_busy("/efi");
if (r < 0)
return r;
assert(esp);
assert(xbootldr);
+ /* If any paths in fstab look similar to our favorite paths for ESP or XBOOTLDR, we just exit
+ * early. We also don't bother with cases where one is configured explicitly and the other shall be
+ * mounted automatically. */
+
+ r = fstab_has_mount_point_prefix_strv(STRV_MAKE("/boot", "/efi"));
+ if (r > 0) {
+ log_debug("Found mount entries in the /boot/ or /efi/ hierarchies in fstab, not generating ESP or XBOOTLDR mounts.");
+ return 0;
+ }
+ if (r < 0)
+ log_debug_errno(r, "Failed to check fstab existing paths, ignoring: %m");
+
if (!is_efi_boot()) {
log_debug("Not an EFI boot, skipping loader partition UUID check.");
goto mount;
/* Out */
- r = sd_journal_open_files(&j, (const char**) STRV_MAKE(name), 0);
+ r = sd_journal_open_files(&j, (const char**) STRV_MAKE(name), SD_JOURNAL_ASSUME_IMMUTABLE);
if (r < 0) {
log_error_errno(r, "sd_journal_open_files([\"%s\"]) failed: %m", name);
assert_se(IN_SET(r, -ENOMEM, -EMFILE, -ENFILE, -ENODATA));
else if (arg_file)
r = sd_journal_open_files(j, (const char**) arg_file, 0);
else if (arg_machine)
- r = journal_open_machine(j, arg_machine);
+ r = journal_open_machine(j, arg_machine, 0);
else
r = sd_journal_open_namespace(j, arg_namespace,
(arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY) | arg_namespace_flags | arg_journal_type);
assert(ret);
- r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
+ r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE);
if (r < 0)
return log_error_errno(r, "Failed to open journal: %m");
static bool arg_catalog = false;
static bool arg_reverse = false;
static int arg_journal_type = 0;
+static int arg_journal_additional_open_flags = 0;
static int arg_namespace_flags = 0;
static char *arg_root = NULL;
static char *arg_image = NULL;
arg_reverse = true;
}
+ if (!arg_follow)
+ arg_journal_additional_open_flags = SD_JOURNAL_ASSUME_IMMUTABLE;
+
return 1;
}
}
if (arg_directory)
- r = sd_journal_open_directory(&j, arg_directory, arg_journal_type);
+ 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 | SD_JOURNAL_OS_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, 0);
+ 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, 0);
+ r = sd_journal_open_files(&j, (const char**) arg_file, arg_journal_additional_open_flags);
else if (arg_machine)
- r = journal_open_machine(&j, arg_machine);
+ r = journal_open_machine(&j, arg_machine, 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_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");
f->header->tail_entry_monotonic = o->entry.monotonic;
if (JOURNAL_HEADER_CONTAINS(f->header, tail_entry_offset))
f->header->tail_entry_offset = htole64(offset);
- f->newest_mtime = 0; /* we have a new tail entry now, explicitly invalidate newest boot id/timestamp info */
/* Link up the items */
for (uint64_t i = 0; i < n_items; i++) {
uint64_t newest_monotonic_usec;
uint64_t newest_realtime_usec;
unsigned newest_boot_id_prioq_idx;
- usec_t newest_mtime;
+ uint64_t newest_entry_offset;
+ uint8_t newest_state;
} JournalFile;
typedef enum JournalFileFlags {
#include "journal-def.h"
#include "journal-file.h"
#include "list.h"
-#include "set.h"
+#include "prioq.h"
#define JOURNAL_FILES_MAX 7168u
unsigned last_seen_generation;
};
+typedef struct NewestByBootId {
+ sd_id128_t boot_id;
+ Prioq *prioq; /* JournalFile objects ordered by monotonic timestamp of last update. */
+} NewestByBootId;
+
struct sd_journal {
int toplevel_fd;
OrderedHashmap *files;
IteratedCache *files_cache;
MMapCache *mmap;
- Hashmap *newest_by_boot_id; /* key: boot_id, value: prioq, ordered by monotonic timestamp of last update */
+
+ /* a bisectable array of NewestByBootId, ordered by boot id. */
+ NewestByBootId *newest_by_boot_id;
+ size_t n_newest_by_boot_id;
Location current_location;
#include "prioq.h"
#include "process-util.h"
#include "replace-var.h"
+#include "sort-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
detach_location(j);
}
+static int newest_by_boot_id_compare(const NewestByBootId *a, const NewestByBootId *b) {
+ return id128_compare_func(&a->boot_id, &b->boot_id);
+}
+
+static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f) {
+ NewestByBootId *found;
+
+ assert(j);
+ assert(f);
+
+ if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) /* not linked currently, hence this is a NOP */
+ return;
+
+ found = typesafe_bsearch(&(NewestByBootId) { .boot_id = f->newest_boot_id },
+ j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
+ assert(found);
+
+ assert_se(prioq_remove(found->prioq, f, &f->newest_boot_id_prioq_idx) > 0);
+ f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
+
+ /* The prioq may be empty, but that should not cause any issue. Let's keep it. */
+}
+
+static void journal_clear_newest_by_boot_id(sd_journal *j) {
+ FOREACH_ARRAY(i, j->newest_by_boot_id, j->n_newest_by_boot_id) {
+ JournalFile *f;
+
+ while ((f = prioq_peek(i->prioq)))
+ journal_file_unlink_newest_by_boot_id(j, f);
+
+ prioq_free(i->prioq);
+ }
+
+ j->newest_by_boot_id = mfree(j->newest_by_boot_id);
+ j->n_newest_by_boot_id = 0;
+}
+
+static int journal_file_newest_monotonic_compare(const void *a, const void *b) {
+ const JournalFile *x = a, *y = b;
+
+ return -CMP(x->newest_monotonic_usec, y->newest_monotonic_usec); /* Invert order, we want newest first! */
+}
+
+static int journal_file_reshuffle_newest_by_boot_id(sd_journal *j, JournalFile *f) {
+ NewestByBootId *found;
+ int r;
+
+ assert(j);
+ assert(f);
+
+ found = typesafe_bsearch(&(NewestByBootId) { .boot_id = f->newest_boot_id },
+ j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
+ if (found) {
+ /* There's already a priority queue for this boot ID */
+
+ if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) {
+ r = prioq_put(found->prioq, f, &f->newest_boot_id_prioq_idx); /* Insert if we aren't in there yet */
+ if (r < 0)
+ return r;
+ } else
+ prioq_reshuffle(found->prioq, f, &f->newest_boot_id_prioq_idx); /* Reshuffle otherwise */
+
+ } else {
+ _cleanup_(prioq_freep) Prioq *q = NULL;
+
+ /* No priority queue yet, then allocate one */
+
+ assert(f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL); /* we can't be a member either */
+
+ q = prioq_new(journal_file_newest_monotonic_compare);
+ if (!q)
+ return -ENOMEM;
+
+ r = prioq_put(q, f, &f->newest_boot_id_prioq_idx);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC(j->newest_by_boot_id, j->n_newest_by_boot_id + 1)) {
+ f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
+ return -ENOMEM;
+ }
+
+ j->newest_by_boot_id[j->n_newest_by_boot_id++] = (NewestByBootId) {
+ .boot_id = f->newest_boot_id,
+ .prioq = TAKE_PTR(q),
+ };
+
+ typesafe_qsort(j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
+ }
+
+ return 0;
+}
+
static int journal_file_find_newest_for_boot_id(
sd_journal *j,
sd_id128_t id,
/* Before we use it, let's refresh the timestamp from the header, and reshuffle our prioq
* accordingly. We do this only a bunch of times, to not be caught in some update loop. */
for (unsigned n_tries = 0;; n_tries++) {
+ NewestByBootId *found;
JournalFile *f;
- Prioq *q;
- q = hashmap_get(j->newest_by_boot_id, &id);
- if (!q)
+ found = typesafe_bsearch(&(NewestByBootId) { .boot_id = id },
+ j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
+
+ f = found ? prioq_peek(found->prioq) : NULL;
+ if (!f)
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
"Requested delta for boot ID %s, but we have no information about that boot ID.", SD_ID128_TO_STRING(id));
- assert_se(f = prioq_peek(q)); /* we delete hashmap entries once the prioq is empty, so this must hold */
-
if (f == prev || n_tries >= 5) {
/* This was already the best answer in the previous run, or we tried too often, use it */
*ret = f;
r = journal_file_read_tail_timestamp(j, f);
if (r < 0)
return log_debug_errno(r, "Failed to read tail timestamp while trying to find newest journal file for boot ID %s.", SD_ID128_TO_STRING(id));
+ if (r == 0) {
+ /* No new entry found. */
+ *ret = f;
+ return 0;
+ }
/* Refreshing the timestamp we read might have reshuffled the prioq, hence let's check the
* prioq again and only use the information once we reached an equilibrium or hit a limit */
SD_JOURNAL_SYSTEM | \
SD_JOURNAL_CURRENT_USER | \
SD_JOURNAL_ALL_NAMESPACES | \
- SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE)
+ SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE | \
+ SD_JOURNAL_ASSUME_IMMUTABLE)
_public_ int sd_journal_open_namespace(sd_journal **ret, const char *namespace, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
}
#define OPEN_CONTAINER_ALLOWED_FLAGS \
- (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
+ (SD_JOURNAL_LOCAL_ONLY | \
+ SD_JOURNAL_SYSTEM | \
+ SD_JOURNAL_ASSUME_IMMUTABLE)
_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
_cleanup_free_ char *root = NULL, *class = NULL;
#define OPEN_DIRECTORY_ALLOWED_FLAGS \
(SD_JOURNAL_OS_ROOT | \
- SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
+ SD_JOURNAL_SYSTEM | \
+ SD_JOURNAL_CURRENT_USER | \
+ SD_JOURNAL_ASSUME_IMMUTABLE)
_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
return 0;
}
+#define OPEN_FILES_ALLOWED_FLAGS \
+ (SD_JOURNAL_ASSUME_IMMUTABLE)
+
_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
int r;
assert_return(ret, -EINVAL);
- assert_return(flags == 0, -EINVAL);
+ assert_return((flags & ~OPEN_FILES_ALLOWED_FLAGS) == 0, -EINVAL);
j = journal_new(flags, NULL, NULL);
if (!j)
(SD_JOURNAL_OS_ROOT | \
SD_JOURNAL_SYSTEM | \
SD_JOURNAL_CURRENT_USER | \
- SD_JOURNAL_TAKE_DIRECTORY_FD)
+ SD_JOURNAL_TAKE_DIRECTORY_FD | \
+ SD_JOURNAL_ASSUME_IMMUTABLE)
_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
return 0;
}
+#define OPEN_FILES_FD_ALLOWED_FLAGS \
+ (SD_JOURNAL_ASSUME_IMMUTABLE)
+
_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
JournalFile *f;
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
assert_return(ret, -EINVAL);
assert_return(n_fds > 0, -EBADF);
- assert_return(flags == 0, -EINVAL);
+ assert_return((flags & ~OPEN_FILES_FD_ALLOWED_FLAGS) == 0, -EINVAL);
j = journal_new(flags, NULL, NULL);
if (!j)
}
_public_ void sd_journal_close(sd_journal *j) {
- Prioq *p;
-
if (!j || journal_origin_changed(j))
return;
- while ((p = hashmap_first(j->newest_by_boot_id)))
- journal_file_unlink_newest_by_boot_id(j, prioq_peek(p));
- hashmap_free(j->newest_by_boot_id);
+ journal_clear_newest_by_boot_id(j);
sd_journal_flush_matches(j);
free(j);
}
-static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f) {
- JournalFile *nf;
- Prioq *p;
-
- assert(j);
- assert(f);
-
- if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) /* not linked currently, hence this is a NOP */
- return;
-
- assert_se(p = hashmap_get(j->newest_by_boot_id, &f->newest_boot_id));
- assert_se(prioq_remove(p, f, &f->newest_boot_id_prioq_idx) > 0);
-
- nf = prioq_peek(p);
- if (nf)
- /* There's still a member in the prioq? Then make sure the hashmap key now points to its
- * .newest_boot_id field (and not ours!). Not we only replace the memory of the key here, the
- * value of the key (and the data associated with it) remain the same. */
- assert_se(hashmap_replace(j->newest_by_boot_id, &nf->newest_boot_id, p) >= 0);
- else {
- assert_se(hashmap_remove(j->newest_by_boot_id, &f->newest_boot_id) == p);
- prioq_free(p);
- }
-
- f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
-}
-
-static int journal_file_newest_monotonic_compare(const void *a, const void *b) {
- const JournalFile *x = a, *y = b;
-
- return -CMP(x->newest_monotonic_usec, y->newest_monotonic_usec); /* Invert order, we want newest first! */
-}
-
-static int journal_file_reshuffle_newest_by_boot_id(sd_journal *j, JournalFile *f) {
- Prioq *p;
- int r;
-
- assert(j);
- assert(f);
-
- p = hashmap_get(j->newest_by_boot_id, &f->newest_boot_id);
- if (p) {
- /* There's already a priority queue for this boot ID */
-
- if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) {
- r = prioq_put(p, f, &f->newest_boot_id_prioq_idx); /* Insert if we aren't in there yet */
- if (r < 0)
- return r;
- } else
- prioq_reshuffle(p, f, &f->newest_boot_id_prioq_idx); /* Reshuffle otherwise */
-
- } else {
- _cleanup_(prioq_freep) Prioq *q = NULL;
-
- /* No priority queue yet, then allocate one */
-
- assert(f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL); /* we can't be a member either */
-
- q = prioq_new(journal_file_newest_monotonic_compare);
- if (!q)
- return -ENOMEM;
-
- r = prioq_put(q, f, &f->newest_boot_id_prioq_idx);
- if (r < 0)
- return r;
-
- r = hashmap_ensure_put(&j->newest_by_boot_id, &id128_hash_ops, &f->newest_boot_id, q);
- if (r < 0) {
- f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
- return r;
- }
-
- TAKE_PTR(q);
- }
-
- return 0;
-}
-
static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
uint64_t offset, mo, rt;
sd_id128_t id;
/* Tries to read the timestamp of the most recently written entry. */
- r = journal_file_fstat(f);
- if (r < 0)
- return r;
- if (f->newest_mtime == timespec_load(&f->last_stat.st_mtim))
- return 0; /* mtime didn't change since last time, don't bother */
+ if (FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE) && f->newest_entry_offset != 0)
+ return 0; /* We have already read the file, and we assume that the file is immutable. */
+
+ if (f->header->state == f->newest_state &&
+ f->header->state == STATE_ARCHIVED &&
+ f->newest_entry_offset != 0)
+ return 0; /* We have already read archived file. */
if (JOURNAL_HEADER_CONTAINS(f->header, tail_entry_offset)) {
offset = le64toh(READ_NOW(f->header->tail_entry_offset));
}
if (offset == 0)
return -ENODATA; /* not a single object/entry, hence no tail timestamp */
+ if (offset == f->newest_entry_offset)
+ return 0; /* No new entry is added after we read last time. */
/* Move to the last object in the journal file, in the hope it is an entry (which it usually will
* be). If we lack the "tail_entry_offset" field in the header, we specify the type as OBJECT_UNUSED
if (r < 0) {
log_debug_errno(r, "Failed to move to last object in journal file, ignoring: %m");
o = NULL;
+ offset = 0;
}
if (o && o->object.type == OBJECT_ENTRY) {
/* Yay, last object is an entry, let's use the data. */
mo = le64toh(f->header->tail_entry_monotonic);
rt = le64toh(f->header->tail_entry_realtime);
id = f->header->tail_entry_boot_id;
+ offset = UINT64_MAX;
} else {
/* Otherwise let's find the last entry manually (this possibly means traversing the
* chain of entry arrays, till the end */
- r = journal_file_next_entry(f, 0, DIRECTION_UP, &o, NULL);
+ r = journal_file_next_entry(f, 0, DIRECTION_UP, &o, offset == 0 ? &offset : NULL);
if (r < 0)
return r;
if (r == 0)
if (mo > rt) /* monotonic clock is further ahead than realtime? that's weird, refuse to use the data */
return -ENODATA;
+ if (offset == f->newest_entry_offset) {
+ /* Cached data and the current one should be equivalent. */
+ if (!sd_id128_equal(f->newest_machine_id, f->header->machine_id) ||
+ !sd_id128_equal(f->newest_boot_id, id) ||
+ f->newest_monotonic_usec != mo ||
+ f->newest_realtime_usec != rt)
+ return -EBADMSG;
+
+ return 0; /* No new entry is added after we read last time. */
+ }
+
if (!sd_id128_equal(f->newest_boot_id, id))
journal_file_unlink_newest_by_boot_id(j, f);
f->newest_monotonic_usec = mo;
f->newest_realtime_usec = rt;
f->newest_machine_id = f->header->machine_id;
- f->newest_mtime = timespec_load(&f->last_stat.st_mtim);
+ f->newest_entry_offset = offset;
+ f->newest_state = f->header->state;
r = journal_file_reshuffle_newest_by_boot_id(j, f);
if (r < 0)
return r;
- return 0;
+ return 1; /* Updated. */
}
_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
assert_return(j, -EINVAL);
assert_return(!journal_origin_changed(j), -ECHILD);
+ assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
if (j->no_inotify)
return -EMEDIUMTYPE;
assert_return(j, -EINVAL);
assert_return(!journal_origin_changed(j), -ECHILD);
+ assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
fd = sd_journal_get_fd(j);
if (fd < 0)
assert_return(j, -EINVAL);
assert_return(!journal_origin_changed(j), -ECHILD);
+ assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
assert_return(timeout_usec, -EINVAL);
fd = sd_journal_get_fd(j);
if (j->inotify_fd < 0) /* We have no inotify fd yet? Then there's noting to process. */
return 0;
+ assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
+
j->last_process_usec = now(CLOCK_MONOTONIC);
j->last_invalidate_counter = j->current_invalidate_counter;
assert_return(j, -EINVAL);
assert_return(!journal_origin_changed(j), -ECHILD);
+ assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
if (j->inotify_fd < 0) {
JournalFile *f;
test_setup_logging(LOG_DEBUG);
- assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY) >= 0);
+ assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", 0) >= 0);
assert_se(sd_journal_add_match(j, "_UID=0", 0) >= 0);
assert_se(r >= 0);
if (argc > 1)
- r = sd_journal_open_files(&j, (const char **) strv_skip(argv, 1), 0);
+ r = sd_journal_open_files(&j, (const char **) strv_skip(argv, 1), SD_JOURNAL_ASSUME_IMMUTABLE);
else
- r = sd_journal_open(&j, 0);
+ r = sd_journal_open(&j, SD_JOURNAL_ASSUME_IMMUTABLE);
assert_se(r == 0);
sd_journal_set_data_threshold(j, 0);
/* Open the new journal before archiving and offlining the file. */
sd_journal_close(j);
- assert_se(sd_journal_open_directory(&j, dn, 0) >= 0);
+ assert_se(sd_journal_open_directory(&j, dn, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
/* Read the online journal. */
assert_se(sd_journal_seek_tail(j) >= 0);
(void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
for (i = 0; i < I; i++) {
- r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
+ r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE);
assert_se(r == 0);
sd_journal_close(j);
- r = sd_journal_open_directory(&j, t, 0);
+ r = sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE);
assert_se(r == 0);
assert_se(sd_journal_seek_head(j) == 0);
setup();
/* Seek to head, iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to head, iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
assert_se(sd_journal_previous(j) == 0); /* no-op */
sd_journal_close(j);
/* Seek to head twice, iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
assert_ret(sd_journal_seek_head(j));
sd_journal_close(j);
/* Seek to head, move to previous, then iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_previous(j) == 0); /* no-op */
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
sd_journal_close(j);
/* Seek to head, walk several steps, then iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_previous(j) == 0); /* no-op */
assert_se(sd_journal_previous(j) == 0); /* no-op */
sd_journal_close(j);
/* Seek to tail, iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
test_check_numbers_up(j, 9);
sd_journal_close(j);
/* Seek to tail twice, iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
assert_ret(sd_journal_seek_tail(j));
sd_journal_close(j);
/* Seek to tail, move to next, then iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_next(j) == 0); /* no-op */
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
sd_journal_close(j);
/* Seek to tail, walk several steps, then iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_next(j) == 0); /* no-op */
assert_se(sd_journal_next(j) == 0); /* no-op */
sd_journal_close(j);
/* Seek to tail, skip to head, iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_previous_skip(j, 9) == 9); /* pointing to the first entry. */
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to tail, skip to head in a more complex way, then iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_next(j) == 0);
assert_se(sd_journal_previous_skip(j, 4) == 4);
sd_journal_close(j);
/* Seek to head, skip to tail, iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next_skip(j, 9) == 9);
test_check_numbers_up(j, 9);
sd_journal_close(j);
/* Seek to head, skip to tail in a more complex way, then iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_previous(j) == 0);
assert_se(sd_journal_next_skip(j, 4) == 4);
setup();
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_se(journal_get_boots(j, &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, 0));
+ 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 (int i = - (int) n_boots + 1; i <= (int) n_boots; i++) {
sd_id128_t id;
- assert_ret(sd_journal_open_directory(&j, t, 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));
test_setup_logging(LOG_DEBUG);
- assert_se(sd_journal_open(&j, 0) >= 0);
+ assert_se(sd_journal_open(&j, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
assert_se(sd_journal_add_match(j, "foobar", 0) < 0);
assert_se(sd_journal_add_match(j, "foobar=waldo", 0) < 0);
(void) journal_file_offline_close(two);
(void) journal_file_offline_close(three);
- assert_se(sd_journal_open_directory(&j, t, 0) >= 0);
+ assert_se(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
SD_JOURNAL_FOREACH_BACKWARDS(j) {
if (arg_lines == 0)
return 0;
- r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
+ r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE);
if (r < 0)
return log_error_errno(r, "Failed to open journal: %m");
return 0;
}
+ r = device_is_processing(device);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to determine whether the device is being processed: %m");
+ if (r > 0) {
+ log_link_debug(link, "Interface is being processed by udevd, pending initialization.");
+ return 0;
+ }
+
return link_initialized(link, device);
}
return false;
if (!need_fstab(context)) {
- log_notice("MountPoint= is not specified for any elligible partitions, not generating %s",
+ log_notice("MountPoint= is not specified for any eligible partitions, not generating %s",
arg_generate_fstab);
return 0;
}
return false;
if (!need_crypttab(context)) {
- log_notice("EncryptedVolume= is not specified for any elligible partitions, not generating %s",
+ log_notice("EncryptedVolume= is not specified for any eligible partitions, not generating %s",
arg_generate_crypttab);
return 0;
}
return false;
}
+int fstab_has_mount_point_prefix_strv(char **prefixes) {
+ _cleanup_endmntent_ FILE *f = NULL;
+
+ assert(prefixes);
+
+ /* This function returns true if at least one entry in fstab has a mount point that starts with one
+ * of the passed prefixes. */
+
+ if (!fstab_enabled())
+ return false;
+
+ f = setmntent(fstab_path(), "re");
+ if (!f)
+ return errno == ENOENT ? false : -errno;
+
+ for (;;) {
+ struct mntent *me;
+
+ errno = 0;
+ me = getmntent(f);
+ if (!me)
+ return errno != 0 ? -errno : false;
+
+ if (path_startswith_strv(me->mnt_dir, prefixes))
+ return true;
+ }
+}
+
int fstab_is_mount_point_full(const char *where, const char *path) {
_cleanup_endmntent_ FILE *f = NULL;
int r;
return fstab_is_mount_point_full(NULL, path);
}
+int fstab_has_mount_point_prefix_strv(char **prefixes);
+
int fstab_filter_options(
const char *opts,
const char *names,
return r;
}
-int journal_open_machine(sd_journal **ret, const char *machine) {
+int journal_open_machine(sd_journal **ret, const char *machine, int flags) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
if (machine_fd < 0)
return log_error_errno(errno, "Failed to duplicate file descriptor: %m");
- r = sd_journal_open_directory_fd(&j, machine_fd, SD_JOURNAL_OS_ROOT | SD_JOURNAL_TAKE_DIRECTORY_FD);
+ r = sd_journal_open_directory_fd(&j, machine_fd, SD_JOURNAL_OS_ROOT | SD_JOURNAL_TAKE_DIRECTORY_FD | flags);
if (r < 0)
return log_error_errno(r, "Failed to open journal in machine '%s': %m", machine);
int journal_access_blocked(sd_journal *j);
int journal_access_check_and_warn(sd_journal *j, bool quiet, bool want_other_users);
-int journal_open_machine(sd_journal **ret, const char *machine);
+int journal_open_machine(sd_journal **ret, const char *machine, int flags);
return (int) strlen(buf);
}
+static void parse_display_timestamp(
+ sd_journal *j,
+ const char *realtime,
+ const char *monotonic,
+ dual_timestamp *ret_display_ts,
+ sd_id128_t *ret_boot_id) {
+
+ bool realtime_good = false, monotonic_good = false, boot_id_good = false;
+
+ assert(j);
+ assert(ret_display_ts);
+ assert(ret_boot_id);
+
+ if (realtime)
+ realtime_good = safe_atou64(realtime, &ret_display_ts->realtime) >= 0;
+ if (!realtime_good || !VALID_REALTIME(ret_display_ts->realtime))
+ realtime_good = sd_journal_get_realtime_usec(j, &ret_display_ts->realtime) >= 0;
+ if (!realtime_good)
+ ret_display_ts->realtime = USEC_INFINITY;
+
+ if (monotonic)
+ monotonic_good = safe_atou64(monotonic, &ret_display_ts->monotonic) >= 0;
+ if (!monotonic_good || !VALID_MONOTONIC(ret_display_ts->monotonic))
+ monotonic_good = boot_id_good = sd_journal_get_monotonic_usec(j, &ret_display_ts->monotonic, ret_boot_id) >= 0;
+ if (!monotonic_good)
+ ret_display_ts->monotonic = USEC_INFINITY;
+
+ if (!boot_id_good)
+ boot_id_good = sd_journal_get_monotonic_usec(j, NULL, ret_boot_id) >= 0;
+ if (!boot_id_good)
+ *ret_boot_id = SD_ID128_NULL;
+}
+
static int output_short(
FILE *f,
sd_journal *j,
OutputFlags flags,
const Set *output_fields,
const size_t highlight[2],
- const dual_timestamp *display_ts,
- const sd_id128_t *boot_id,
- const dual_timestamp *previous_display_ts,
- const sd_id128_t *previous_boot_id) {
+ dual_timestamp *previous_display_ts, /* in and out */
+ sd_id128_t *previous_boot_id) { /* in and out */
int r;
const void *data;
size_t length, n = 0;
_cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL,
*message = NULL, *priority = NULL, *transport = NULL,
- *config_file = NULL, *unit = NULL, *user_unit = NULL, *documentation_url = NULL;
+ *config_file = NULL, *unit = NULL, *user_unit = NULL, *documentation_url = NULL,
+ *realtime = NULL, *monotonic = NULL;
size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0,
priority_len = 0, transport_len = 0, config_file_len = 0,
unit_len = 0, user_unit_len = 0, documentation_url_len = 0;
+ dual_timestamp display_ts;
+ sd_id128_t boot_id;
int p = LOG_INFO;
bool ellipsized = false, audit;
const ParseFieldVec fields[] = {
- PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len),
- PARSE_FIELD_VEC_ENTRY("_COMM=", &comm, &comm_len),
- PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len),
- PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len),
- PARSE_FIELD_VEC_ENTRY("_TRANSPORT=", &transport, &transport_len),
- PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len),
- PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len),
- PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len),
- PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len),
- PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len),
- PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len),
- PARSE_FIELD_VEC_ENTRY("DOCUMENTATION=", &documentation_url, &documentation_url_len),
+ PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len ),
+ PARSE_FIELD_VEC_ENTRY("_COMM=", &comm, &comm_len ),
+ PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len ),
+ PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len ),
+ PARSE_FIELD_VEC_ENTRY("_TRANSPORT=", &transport, &transport_len ),
+ PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len ),
+ PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len ),
+ PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len ),
+ PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len ),
+ PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len ),
+ PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len ),
+ PARSE_FIELD_VEC_ENTRY("DOCUMENTATION=", &documentation_url, &documentation_url_len),
+ PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, NULL ),
+ PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, NULL ),
};
size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
assert(f);
assert(j);
- assert(display_ts);
- assert(boot_id);
assert(previous_display_ts);
assert(previous_boot_id);
if (identifier && set_contains(j->exclude_syslog_identifiers, identifier))
return 0;
+ parse_display_timestamp(j, realtime, monotonic, &display_ts, &boot_id);
+
if (!(flags & OUTPUT_SHOW_ALL))
strip_tab_ansi(&message, &message_len, highlight_shifted);
audit = streq_ptr(transport, "audit");
if (IN_SET(mode, OUTPUT_SHORT_MONOTONIC, OUTPUT_SHORT_DELTA))
- r = output_timestamp_monotonic(f, mode, display_ts, boot_id, previous_display_ts, previous_boot_id);
+ r = output_timestamp_monotonic(f, mode, &display_ts, &boot_id, previous_display_ts, previous_boot_id);
else
- r = output_timestamp_realtime(f, j, mode, flags, display_ts);
+ r = output_timestamp_realtime(f, j, mode, flags, &display_ts);
if (r < 0)
return r;
n += r;
if (flags & OUTPUT_CATALOG)
(void) print_catalog(f, j);
+ *previous_display_ts = display_ts;
+ *previous_boot_id = boot_id;
+
return ellipsized;
}
+static int get_display_timestamp(
+ sd_journal *j,
+ dual_timestamp *ret_display_ts,
+ sd_id128_t *ret_boot_id) {
+
+ const void *data;
+ _cleanup_free_ char *realtime = NULL, *monotonic = NULL;
+ size_t length;
+ const ParseFieldVec message_fields[] = {
+ PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, NULL),
+ PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, NULL),
+ };
+ int r;
+
+ assert(j);
+ assert(ret_display_ts);
+ assert(ret_boot_id);
+
+ JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
+ r = parse_fieldv(data, length, message_fields, ELEMENTSOF(message_fields));
+ if (r < 0)
+ return r;
+
+ if (realtime && monotonic)
+ break;
+ }
+ if (r < 0)
+ return r;
+
+ (void) parse_display_timestamp(j, realtime, monotonic, ret_display_ts, ret_boot_id);
+
+ /* Restart all data before */
+ sd_journal_restart_data(j);
+ sd_journal_restart_unique(j);
+ sd_journal_restart_fields(j);
+
+ return 0;
+}
+
static int output_verbose(
FILE *f,
sd_journal *j,
OutputFlags flags,
const Set *output_fields,
const size_t highlight[2],
- const dual_timestamp *display_ts,
- const sd_id128_t *boot_id,
- const dual_timestamp *previous_display_ts,
- const sd_id128_t *previous_boot_id) {
+ dual_timestamp *previous_display_ts, /* unused */
+ sd_id128_t *previous_boot_id) { /* unused */
const void *data;
size_t length;
_cleanup_free_ char *cursor = NULL;
char buf[FORMAT_TIMESTAMP_MAX + 7];
+ dual_timestamp display_ts;
+ sd_id128_t boot_id;
const char *timestamp;
int r;
assert(f);
assert(j);
- assert(display_ts);
- assert(boot_id);
- assert(previous_display_ts);
- assert(previous_boot_id);
(void) sd_journal_set_data_threshold(j, 0);
- if (!VALID_REALTIME(display_ts->realtime))
+ r = get_display_timestamp(j, &display_ts, &boot_id);
+ if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) {
+ log_debug_errno(r, "Skipping message we can't read: %m");
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get journal fields: %m");
+
+ if (!VALID_REALTIME(display_ts.realtime))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid realtime timestamp available");
r = sd_journal_get_cursor(j, &cursor);
if (r < 0)
return log_error_errno(r, "Failed to get cursor: %m");
- timestamp = format_timestamp_style(buf, sizeof buf, display_ts->realtime,
+ timestamp = format_timestamp_style(buf, sizeof buf, display_ts.realtime,
flags & OUTPUT_UTC ? TIMESTAMP_US_UTC : TIMESTAMP_US);
fprintf(f, "%s%s%s %s[%s]%s\n",
timestamp && (flags & OUTPUT_COLOR) ? ANSI_UNDERLINE : "",
OutputFlags flags,
const Set *output_fields,
const size_t highlight[2],
- const dual_timestamp *display_ts,
- const sd_id128_t *boot_id,
- const dual_timestamp *previous_display_ts,
- const sd_id128_t *previous_boot_id) {
+ dual_timestamp *previous_display_ts, /* unused */
+ sd_id128_t *previous_boot_id) { /* unused */
sd_id128_t journal_boot_id, seqnum_id;
_cleanup_free_ char *cursor = NULL;
int r;
assert(j);
- assert(display_ts);
- assert(boot_id);
- assert(previous_display_ts);
- assert(previous_boot_id);
(void) sd_journal_set_data_threshold(j, 0);
OutputFlags flags,
const Set *output_fields,
const size_t highlight[2],
- const dual_timestamp *display_ts,
- const sd_id128_t *boot_id,
- const dual_timestamp *previous_display_ts,
- const sd_id128_t *previous_boot_id) {
+ dual_timestamp *previous_display_ts, /* unused */
+ sd_id128_t *previous_boot_id) { /* unused */
char usecbuf[CONST_MAX(DECIMAL_STR_MAX(usec_t), DECIMAL_STR_MAX(uint64_t))];
_cleanup_(json_variant_unrefp) JsonVariant *object = NULL;
int r;
assert(j);
- assert(display_ts);
- assert(boot_id);
- assert(previous_display_ts);
- assert(previous_boot_id);
(void) sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
OutputFlags flags,
const Set *output_fields,
const size_t highlight[2],
- const dual_timestamp *display_ts,
- const sd_id128_t *boot_id,
- const dual_timestamp *previous_display_ts,
- const sd_id128_t *previous_boot_id) {
+ dual_timestamp *previous_display_ts, /* unused */
+ sd_id128_t *previous_boot_id) { /* unused */
int r, prio = LOG_INFO;
const char *field;
assert(j);
assert(f);
- assert(display_ts);
- assert(boot_id);
- assert(previous_display_ts);
- assert(previous_boot_id);
(void) sd_journal_set_data_threshold(j, 0);
return 0;
}
-static int get_display_timestamp(
- sd_journal *j,
- dual_timestamp *ret_display_ts,
- sd_id128_t *ret_boot_id) {
-
- const void *data;
- _cleanup_free_ char *realtime = NULL, *monotonic = NULL;
- size_t length = 0, realtime_len = 0, monotonic_len = 0;
- const ParseFieldVec message_fields[] = {
- PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
- PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
- };
- int r;
- bool realtime_good = false, monotonic_good = false, boot_id_good = false;
-
- assert(j);
- assert(ret_display_ts);
- assert(ret_boot_id);
-
- JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
- r = parse_fieldv(data, length, message_fields, ELEMENTSOF(message_fields));
- if (r < 0)
- return r;
-
- if (realtime && monotonic)
- break;
- }
- if (r < 0)
- return r;
-
- if (realtime)
- realtime_good = safe_atou64(realtime, &ret_display_ts->realtime) >= 0;
- if (!realtime_good || !VALID_REALTIME(ret_display_ts->realtime))
- realtime_good = sd_journal_get_realtime_usec(j, &ret_display_ts->realtime) >= 0;
- if (!realtime_good)
- ret_display_ts->realtime = USEC_INFINITY;
-
- if (monotonic)
- monotonic_good = safe_atou64(monotonic, &ret_display_ts->monotonic) >= 0;
- if (!monotonic_good || !VALID_MONOTONIC(ret_display_ts->monotonic))
- monotonic_good = boot_id_good = sd_journal_get_monotonic_usec(j, &ret_display_ts->monotonic, ret_boot_id) >= 0;
- if (!monotonic_good)
- ret_display_ts->monotonic = USEC_INFINITY;
-
- if (!boot_id_good)
- boot_id_good = sd_journal_get_monotonic_usec(j, NULL, ret_boot_id) >= 0;
- if (!boot_id_good)
- *ret_boot_id = SD_ID128_NULL;
-
- /* Restart all data before */
- sd_journal_restart_data(j);
- sd_journal_restart_unique(j);
- sd_journal_restart_fields(j);
-
- return 0;
-}
-
typedef int (*output_func_t)(
FILE *f,
sd_journal *j,
OutputFlags flags,
const Set *output_fields,
const size_t highlight[2],
- const dual_timestamp *display_ts,
- const sd_id128_t *boot_id,
- const dual_timestamp *previous_display_ts,
- const sd_id128_t *previous_boot_id);
+ dual_timestamp *previous_display_ts,
+ sd_id128_t *previous_boot_id);
static output_func_t output_funcs[_OUTPUT_MODE_MAX] = {
dual_timestamp *previous_display_ts,
sd_id128_t *previous_boot_id) {
- dual_timestamp display_ts = DUAL_TIMESTAMP_NULL;
- sd_id128_t boot_id = SD_ID128_NULL;
int r;
assert(mode >= 0);
if (n_columns <= 0)
n_columns = columns();
- r = get_display_timestamp(j, &display_ts, &boot_id);
- if (IN_SET(r, -EBADMSG, -EADDRNOTAVAIL)) {
- log_debug_errno(r, "Skipping message we can't read: %m");
- return 0;
- }
- if (r < 0)
- return log_error_errno(r, "Failed to get journal fields: %m");
-
r = output_funcs[mode](
f,
j,
flags,
output_fields,
highlight,
- &display_ts,
- &boot_id,
previous_display_ts,
previous_boot_id);
- /* Store timestamp and boot ID for next iteration */
- *previous_display_ts = display_ts;
- *previous_boot_id = boot_id;
-
if (ellipsized && r > 0)
*ellipsized = true;
if (how_many <= 0)
return 0;
- r = sd_journal_open_namespace(&j, log_namespace, journal_open_flags | SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE);
+ r = sd_journal_open_namespace(&j, log_namespace,
+ journal_open_flags |
+ SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE |
+ SD_JOURNAL_ASSUME_IMMUTABLE);
if (r < 0)
return log_error_errno(r, "Failed to open journal: %m");
assert(dev);
- r = sd_device_get_property_value(dev, "ID_RENAMING", NULL);
+ r = device_get_property_bool(dev, "ID_RENAMING");
if (r == -ENOENT)
- return false;
- if (r < 0)
- return r;
+ return false; /* defaults to false */
- return true;
+ return r;
+}
+
+int device_is_processing(sd_device *dev) {
+ int r;
+
+ assert(dev);
+
+ r = device_get_property_bool(dev, "ID_PROCESSING");
+ if (r == -ENOENT)
+ return false; /* defaults to false */
+
+ return r;
}
bool device_for_action(sd_device *dev, sd_device_action_t a) {
int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout_usec, sd_device **ret);
int device_wait_for_devlink(const char *path, const char *subsystem, usec_t timeout_usec, sd_device **ret);
int device_is_renaming(sd_device *dev);
+int device_is_processing(sd_device *dev);
bool device_for_action(sd_device *dev, sd_device_action_t action);
SD_JOURNAL_ALL_NAMESPACES = 1 << 5, /* Show all namespaces, not just the default or specified one */
SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE = 1 << 6, /* Show default namespace in addition to specified one */
SD_JOURNAL_TAKE_DIRECTORY_FD = 1 << 7, /* sd_journal_open_directory_fd() will take ownership of the provided file descriptor. */
+ SD_JOURNAL_ASSUME_IMMUTABLE = 1 << 8, /* Assume the opened journal files are immutable. Journal entries added later may be ignored. */
SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* old name */
};
goto revert;
}
+ r = device_add_property(event->dev_db_clone, "ID_PROCESSING", "1");
+ if (r < 0) {
+ log_device_warning_errno(event->dev_db_clone, r, "Failed to add 'ID_PROCESSING' property: %m");
+ goto revert;
+ }
+
r = device_update_db(event->dev_db_clone);
if (r < 0) {
log_device_debug_errno(event->dev_db_clone, r, "Failed to update database under /run/udev/data/: %m");
revert:
/* Restore 'dev_db_clone' */
(void) device_add_property(event->dev_db_clone, "ID_RENAMING", NULL);
+ (void) device_add_property(event->dev_db_clone, "ID_PROCESSING", NULL);
(void) device_update_db(event->dev_db_clone);
/* Restore 'dev' */
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to remove 'ID_RENAMING' property: %m");
+ /* If the database file already exists, append ID_PROCESSING property to the existing database,
+ * to indicate that the device is being processed by udevd. */
+ if (device_has_db(event->dev_db_clone) > 0) {
+ r = device_add_property(event->dev_db_clone, "ID_PROCESSING", "1");
+ if (r < 0)
+ return log_device_warning_errno(event->dev_db_clone, r, "Failed to add 'ID_PROCESSING' property: %m");
+
+ r = device_update_db(event->dev_db_clone);
+ if (r < 0)
+ return log_device_warning_errno(event->dev_db_clone, r, "Failed to update database under /run/udev/data/: %m");
+ }
+
DEVICE_TRACE_POINT(rules_start, dev);
r = udev_rules_apply_to_event(rules, event);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to update tags under /run/udev/tag/: %m");
+ /* If the database file for the device will be created below, add ID_PROCESSING=1 to indicate that
+ * the device is still being processed by udevd, as commands specified in RUN are invoked after
+ * the database is created. See issue #30056. */
+ if (device_should_have_db(dev) && !ordered_hashmap_isempty(event->run_list)) {
+ r = device_add_property(dev, "ID_PROCESSING", "1");
+ if (r < 0)
+ return log_device_warning_errno(dev, r, "Failed to add 'ID_PROCESSING' property: %m");
+ }
+
r = device_update_db(dev);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
log_device_warning_errno(dev, r, "Failed to add inotify watch, ignoring: %m");
}
+ /* Finalize database. */
+ r = device_add_property(dev, "ID_PROCESSING", NULL);
+ if (r < 0)
+ return log_device_warning_errno(dev, r, "Failed to remove 'ID_PROCESSING' property: %m");
+
+ r = device_update_db(dev);
+ if (r < 0)
+ return log_device_warning_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+
log_device_uevent(dev, "Device processed");
return 0;
}
stderr="$WORK_DIR/stderr"
if ! "$GENERATOR_BIN" --root "$WORK_DIR" 2>"$stderr"; then
echo >&2 "Generator failed when parsing $SYSTEMD_PROC_CMDLINE"
- cat "$stderr"
+ cat >&2 "$stderr"
return 1
fi
if [[ -s "$stderr" ]]; then
echo >&2 "Generator generated unexpected messages on stderr"
- cat "$stderr"
+ cat >&2 "$stderr"
return 1
fi
"$GENERATOR_BIN" --root "$out" -- $(cat "$f")
if ! diff -u "$out/run/systemd/network" "${f%.input}.expected"; then
- echo "**** Unexpected output for $f"
+ echo >&2 "**** Unexpected output for $f"
exit 1
fi
print(output)
self.assertRegex(output, 'inet6 .* scope link')
- @unittest.skip("Re-enable once https://github.com/systemd/systemd/issues/30056 is resolved")
def test_sysctl(self):
copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins=False)
compare() {
if ! diff -u "$TESTDIR/etc/passwd" <(preprocess "$1.expected-passwd" "$3"); then
- echo "**** Unexpected output for $f $2"
+ echo >&2 "**** Unexpected output for $f $2"
exit 1
fi
if ! diff -u "$TESTDIR/etc/group" <(preprocess "$1.expected-group" "$3"); then
- echo "**** Unexpected output for $f $2"
+ echo >&2 "**** Unexpected output for $f $2"
exit 1
fi
}
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
$SYSUSERS --root="$TESTDIR" 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >"$TESTDIR/err"
if ! diff -u "$TESTDIR/err" "${f%.*}.expected-err"; then
- echo "**** Unexpected error output for $f"
- cat "$TESTDIR/err"
+ echo >&2 "**** Unexpected error output for $f"
+ cat >&2 "$TESTDIR/err"
exit 1
fi
done
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if [[ -f "$1" ]]; then
+ exit 0
+fi
+
+touch "$1"
+exit 2
set -eux
set -o pipefail
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
# Test oneshot unit restart on failure
# wait this many secs for each test service to succeed in what is being tested
MAX_SECS=60
-systemd-analyze log-level debug
+systemctl log-level debug
# test one: Restart=on-failure should restart the service
(! systemd-run --unit=oneshot-restart-one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1")
exit 1
fi
-TMP_FILE="/tmp/test-41-oneshot-restart-test"
+TMP_FILE="/tmp/test-23-oneshot-restart-test$RANDOM"
: >$TMP_FILE
-p StartLimitBurst=3 \
-p Type=oneshot \
-p Restart=on-failure \
- -p ExecStart="/bin/bash -c \"printf a >>$TMP_FILE\"" /bin/bash -c "exit 1")
+ -p ExecStart="/bin/bash -c 'printf a >>$TMP_FILE'" /bin/bash -c "exit 1")
# wait for at least 3 restarts
for ((secs = 0; secs < MAX_SECS; secs++)); do
if [[ $(cat $TMP_FILE) != "aaa" ]]; then
exit 1
fi
+rm "$TMP_FILE"
+
+# Test RestartForceExitStatus=. Note that success exit statuses are meant to be skipped
+
+TMP_FILE="/tmp/test-23-oneshot-restart-test$RANDOM"
+UNIT_NAME="testsuite-23-oneshot-restartforce.service"
+ONSUCCESS_UNIT_NAME="testsuite-23-oneshot-restartforce-onsuccess.service"
+FIFO_FILE="/tmp/test-23-oneshot-restart-test-fifo"
+
+cat >"/run/systemd/system/$UNIT_NAME" <<EOF
+[Unit]
+OnSuccess=$ONSUCCESS_UNIT_NAME
+
+[Service]
+Type=oneshot
+RestartForceExitStatus=0 2
+ExecStart=/usr/lib/systemd/tests/testdata/testsuite-23.units/testsuite-23-oneshot-restartforce.sh "$TMP_FILE"
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+cat >"/run/systemd/system/$ONSUCCESS_UNIT_NAME" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c 'echo finished >$FIFO_FILE'
+EOF
+
+mkfifo "$FIFO_FILE"
+
+# Pin the unit in memory
+systemctl enable "$UNIT_NAME"
+# Initial run should fail
+(! systemctl start "$UNIT_NAME")
+# Wait for OnSuccess=
+read -r x <"$FIFO_FILE"
+assert_eq "$x" "finished"
+
+cmp -b <(systemctl show "$UNIT_NAME" -p Result -p NRestarts -p SubState) <<EOF
+Result=success
+NRestarts=1
+SubState=dead
+EOF
+
+systemctl disable "$UNIT_NAME"
+rm "$TMP_FILE" /run/systemd/system/{"$UNIT_NAME","$ONSUCCESS_UNIT_NAME"} "$FIFO_FILE"
-systemd-analyze log-level info
+systemctl log-level info