--- /dev/null
+---
+title: Dlopen Metadata for ELF Files
+category: Interfaces
+layout: default
+SPDX-License-Identifier: LGPL-2.1-or-later
+---
+
+# `dlopen()` Metadata for ELF Files
+
+*Intended audience: hackers working on packaging ELF files that use dlopen to load libraries.*
+
+## Motivation
+
+Using `dlopen()` to load optional dependencies brings several advantages: programs can gracefully downgrade
+a feature when a library is not available, and the shared library is only loaded into the process (and its
+ELF constructors are run) only when the requested feature is actually used. But it also has some drawbacks,
+and the main one is that it is harder to track a program's dependencies, since unlike build-time dynamic
+linking there will not be a mention in the ELF metadata. This specification aims to solve this problem by
+providing a standardized specification for a custom ELF note that can be used to list `dlopen()`
+dependencies.
+
+## Implementation
+
+This document will attempt to define a common metadata format specification, so that multiple implementers
+might use it when coding upstream software, and packagers might use it when building packages and setting
+dependencies.
+
+The metadata will be embedded in a series of new, 4-byte-aligned, allocated, 0-padded, read-only ELF header
+sections, in a JSON array containing name-value objects, either one ELF note per dependency or as a single
+note listing multiple dependencies in the top-level array. Implementers working on parsing ELF files should
+not assume a specific list of names, but parse anything that is included in the section, and should look for
+the note using the `note type`. Implementers working on build tools should strive to use the same names, for
+consistency. The most common will be listed here.
+
+* Section header
+
+```
+SECTION: `.note.dlopen`
+note type: `0x407c0c0a`
+Owner: `FDO` (FreeDesktop.org)
+Value: an array of JSON objects encoded as a zero-terminated UTF-8 string
+```
+
+* JSON payload
+
+```json
+[
+ {
+ "soname": ["libfoo.so.1"],
+ "feature": "foo",
+ "description": "Enables the foo feature",
+ "priority": "recommended"
+ }
+]
+```
+
+The format is a single JSON array containing objects, encoded as a zero-terminated `UTF-8` string. Each key
+in each object shall be unique as per recommendations of [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259#section-4).
+Strings shall not contain any control characters or use `\uXXX` escaping.
+
+Reference implementations of [packaging tools for `.deb` and `.rpm`](https://github.com/systemd/package-notes)
+are available, and provide macros/helpers to parse the note when building packages and adding dependencies.
+
+## Well-known keys
+
+The metadata format is intentionally extensible, so that upstreams and later revisions of this spec can add
+their own information. The 'soname' array is required, with at least one element, everything else is
+optional. If alternative soname versions for the same library are supported at the same time, an array can
+be used, listing the most preferred first, and parsers are expected to select only the first one that is
+available on the system, as it is a mechanism to specify alternatives. If the `priority` field is used, it
+must follow the specification and use one of the values specified in the table. If it is not specified, a
+parser should assume 'recommended' if a priority is needed. If the `feature` field is used, it will identify
+an individual feature, and multiple entries using the same `feature` denote functionality that requires all
+of the libraries they specify in order to be enabled.
+
+| Key name | Key type | Mandatory | Key description | Example value |
+|-------------|----------------------------|-----------|--------------------------------------------------------------------------|----------------------------------|
+| soname | array of strings | yes | The library names loaded by `dlopen()` | [ "libfoo.so.1", "libfoo.so.0" ] |
+| feature | string | no | A keyword identifying the feature that the library contributes to enable | "foo" |
+| description | string | no | A human-readable text string describing the feature | "Enables the foo feature" |
+| priority | string | no | The priority of the feature, one of: required, recommended, suggested | "recommended" |
+
+### Priority definition
+
+| Priority | Semantics |
+|-------------|--------------------------------------------------------------------------------------------------------------------------------------|
+| required | Core functionality needs the dependency, the binary will not work if it cannot be found |
+| recommended | Important functionality needs the dependency, the binary will work but in most cases the dependency should be provided |
+| suggested | Secondary functionality needs the dependency, the binary will work and the dependency is only needed for full-featured installations |
MOUSE_WHEEL_CLICK_COUNT=24
MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL=14
+# Logitech MX Master 3S (via Bolt Receiver)
+mouse:usb:v046dpc548:name:Logitech USB Receiver Mouse:*
+ MOUSE_DPI=1000@142
+
# Logitech MX Ergo
mouse:usb:v046dp406f:name:Logitech MX Ergo:*
mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:406f:*
</listitem>
</varlistentry>
- <xi:include href="user-system-options.xml" xpointer="machine" />
+ <varlistentry>
+ <term><option>-M</option></term>
+ <term><option>--machine=</option></term>
+
+ <listitem>
+ <para>Execute operation on a local container. Specify a container name to connect to, optionally
+ prefixed by a user name to connect as and a separating <literal>@</literal> character. If the special
+ string <literal>.host</literal> is used in place of the container name, a connection to the local
+ system is made. If the <literal>@</literal> syntax is not used, the connection is made as root user.
+ If the <literal>@</literal> syntax is used either the left hand side or the right hand side may be
+ omitted (but not both) in which case the local user name and <literal>.host</literal> are implied.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
<para>Suspend the system. This will trigger activation of the special target unit
<filename>suspend.target</filename>. This command is asynchronous, and will return after the suspend
operation is successfully enqueued. It will not wait for the suspend/resume cycle to complete.</para>
+
+ <para>If <option>--force</option> is specified, and <command>systemd-logind</command> returned
+ error for the operation, the error will be ignored and the operation will be tried again directly
+ through starting the target unit.</para>
</listitem>
</varlistentry>
<para>Hibernate the system. This will trigger activation of the special target unit
<filename>hibernate.target</filename>. This command is asynchronous, and will return after the hibernation
operation is successfully enqueued. It will not wait for the hibernate/thaw cycle to complete.</para>
+
+ <para>This command honors <option>--force</option> in the same way as <command>suspend</command>.</para>
</listitem>
</varlistentry>
<filename>hybrid-sleep.target</filename>. This command is asynchronous, and will return after the hybrid
sleep operation is successfully enqueued. It will not wait for the sleep/wake-up cycle to complete.</para>
+ <para>This command honors <option>--force</option> in the same way as <command>suspend</command>.</para>
+
<xi:include href="version-info.xml" xpointer="v196"/>
</listitem>
</varlistentry>
<term><command>suspend-then-hibernate</command></term>
<listitem>
- <para>Suspend the system and hibernate it after the delay specified in <filename>systemd-sleep.conf</filename>.
- This will trigger activation of the special target unit <filename>suspend-then-hibernate.target</filename>.
- This command is asynchronous, and will return after the hybrid sleep operation is successfully enqueued.
+ <para>Suspend the system and hibernate it when the battery is low, or when the delay specified
+ in <filename>systemd-sleep.conf</filename> elapsed. This will trigger activation of the special
+ target unit <filename>suspend-then-hibernate.target</filename>. This command is asynchronous,
+ and will return after the hybrid sleep operation is successfully enqueued.
It will not wait for the sleep/wake-up or hibernate/thaw cycle to complete.</para>
- <xi:include href="version-info.xml" xpointer="v240"/>
+ <para>This command honors <option>--force</option> in the same way as <command>suspend</command>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v240"/>
</listitem>
</varlistentry>
</variablelist>
<term><option>--force</option></term>
<listitem>
- <para>When used with <command>enable</command>, overwrite
- any existing conflicting symlinks.</para>
+ <para>When used with <command>enable</command>, overwrite any existing conflicting symlinks.</para>
+
+ <para>When used with <command>edit</command>, create all of the specified units which do not already exist.</para>
- <para>When used with <command>edit</command>, create all of the
- specified units which do not already exist.</para>
+ <para>When used with <command>suspend</command>, <command>hibernate</command>, <command>hybrid-sleep</command>,
+ or <command>suspend-then-hibernate</command>, the error returned by <command>systemd-logind</command>
+ will be ignored, and the operation will be performed directly through starting the corresponding units.
+ </para>
- <para>When used with <command>halt</command>, <command>poweroff</command>, <command>reboot</command> or
- <command>kexec</command>, execute the selected operation without shutting down all units. However, all
- processes will be killed forcibly and all file systems are unmounted or remounted read-only. This is hence a
- drastic but relatively safe option to request an immediate reboot. If <option>--force</option> is specified
- twice for these operations (with the exception of <command>kexec</command>), they will be executed
- immediately, without terminating any processes or unmounting any file systems.</para>
+ <para>When used with <command>halt</command>, <command>poweroff</command>, <command>reboot</command>,
+ or <command>kexec</command>, execute the selected operation without shutting down all units. However,
+ all processes will be killed forcibly and all file systems are unmounted or remounted read-only.
+ This is hence a drastic but relatively safe option to request an immediate reboot. If <option>--force</option>
+ is specified twice for these operations (with the exception of <command>kexec</command>), they will
+ be executed immediately, without terminating any processes or unmounting any file systems.</para>
<warning>
- <para>Specifying
- <option>--force</option> twice with any of these operations might result in data loss. Note that when
- <option>--force</option> is specified twice the selected operation is executed by
- <command>systemctl</command> itself, and the system manager is not contacted. This means the command should
- succeed even when the system manager has crashed.</para>
+ <para>Specifying <option>--force</option> twice with any of these operations might result in data loss.
+ Note that when <option>--force</option> is specified twice the selected operation is executed by
+ <command>systemctl</command> itself, and the system manager is not contacted. This means the command
+ should succeed even when the system manager has crashed.</para>
</warning>
</listitem>
</varlistentry>
<term><command>cel</command></term>
<listitem><para>This reads the combined TPM2 event log and writes it to STDOUT in <ulink
- url="https://trustedcomputinggroup.org/resource/canonical-event-log-format/">TCG Common Event Log
+ url="https://trustedcomputinggroup.org/resource/canonical-event-log-format/">TCG Canonical Event Log
Format (CEL-JSON)</ulink> format.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
<filename>/run/log/systemd/tpm2-measure.log</filename>, which contains a <ulink
url="https://www.rfc-editor.org/rfc/rfc7464.html">JSON-SEQ</ulink> series of objects that follow the
general structure of the <ulink
- url="https://trustedcomputinggroup.org/resource/canonical-event-log-format/">TCG Common Event Log
+ url="https://trustedcomputinggroup.org/resource/canonical-event-log-format/">TCG Canonical Event Log
Format (CEL-JSON)</ulink> event objects (but lack the <literal>recnum</literal>
field).</para>
process. <citerefentry><refentrytitle>systemd-pcrlock</refentrytitle><manvolnum>1</manvolnum></citerefentry>
uses such pcrlock files to analyze and predict TPM2 PCR measurements. The pcrlock files are JSON arrays
that follow a subset of the <ulink
- url="https://trustedcomputinggroup.org/resource/canonical-event-log-format/">TCG Common Event Log Format
+ url="https://trustedcomputinggroup.org/resource/canonical-event-log-format/">TCG Canonical Event Log Format
(CEL-JSON)</ulink> specification. Specifically the <literal>recnum</literal>, <literal>content</literal>,
and <literal>content_type</literal> record fields are not used and ignored if present. Each pcrlock file
defines one set of expected, ordered PCR measurements of a specific component of the boot.</para>
elif compression == 'xz' and not libxz.found()
error('default-compression=xz requires xz')
endif
+# In the dlopen ELF note we save the default compression library with a
+# higher priority, so that packages can give it priority over the
+# secondary libraries.
+conf.set_quoted('COMPRESSION_PRIORITY_ZSTD',
+ compression == 'zstd' ? 'recommended' : 'suggested')
+conf.set_quoted('COMPRESSION_PRIORITY_LZ4',
+ compression == 'lz4' ? 'recommended' : 'suggested')
+conf.set_quoted('COMPRESSION_PRIORITY_XZ',
+ compression == 'xz' ? 'recommended' : 'suggested')
conf.set('DEFAULT_COMPRESSION', 'COMPRESSION_@0@'.format(compression.to_upper()))
libarchive = dependency('libarchive',
#if HAVE_XZ
int dlopen_lzma(void) {
+ ELF_NOTE_DLOPEN("lzma",
+ "Support lzma compression in journal and coredump files",
+ COMPRESSION_PRIORITY_XZ,
+ "liblzma.so.5");
+
return dlopen_many_sym_or_warn(
&lzma_dl,
"liblzma.so.5", LOG_DEBUG,
#if HAVE_LZ4
int dlopen_lz4(void) {
+ ELF_NOTE_DLOPEN("lz4",
+ "Support lz4 compression in journal and coredump files",
+ COMPRESSION_PRIORITY_LZ4,
+ "liblz4.so.1");
+
return dlopen_many_sym_or_warn(
&lz4_dl,
"liblz4.so.1", LOG_DEBUG,
#if HAVE_ZSTD
int dlopen_zstd(void) {
+ ELF_NOTE_DLOPEN("zstd",
+ "Support zstd compression in journal and coredump files",
+ COMPRESSION_PRIORITY_ZSTD,
+ "libzstd.so.1");
+
return dlopen_many_sym_or_warn(
&zstd_dl,
"libzstd.so.1", LOG_DEBUG,
/* libbpf is a bit confused about type-safety and API compatibility. Provide a macro that can tape over that mess. Sad. */
#define DLSYM_ARG_FORCE(arg) \
&sym_##arg, STRINGIFY(arg)
+
+#define ELF_NOTE_DLOPEN_VENDOR "FDO"
+#define ELF_NOTE_DLOPEN_TYPE UINT32_C(0x407c0c0a)
+#define ELF_NOTE_DLOPEN_PRIORITY_REQUIRED "required"
+#define ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED "recommended"
+#define ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED "suggested"
+
+/* Add an ".note.dlopen" ELF note to our binary that declares our weak dlopen() dependency. This
+ * information can be read from an ELF file via "readelf -p .note.dlopen" or an equivalent command. */
+#define _ELF_NOTE_DLOPEN(json, variable_name) \
+ __attribute__((used, section(".note.dlopen"))) _Alignas(sizeof(uint32_t)) static const struct { \
+ struct { \
+ uint32_t n_namesz, n_descsz, n_type; \
+ } nhdr; \
+ char name[sizeof(ELF_NOTE_DLOPEN_VENDOR)]; \
+ _Alignas(sizeof(uint32_t)) char dlopen_json[sizeof(json)]; \
+ } variable_name = { \
+ .nhdr = { \
+ .n_namesz = sizeof(ELF_NOTE_DLOPEN_VENDOR), \
+ .n_descsz = sizeof(json), \
+ .n_type = ELF_NOTE_DLOPEN_TYPE, \
+ }, \
+ .name = ELF_NOTE_DLOPEN_VENDOR, \
+ .dlopen_json = json, \
+ }
+
+#define _SONAME_ARRAY1(a) "[\""a"\"]"
+#define _SONAME_ARRAY2(a, b) "[\""a"\",\""b"\"]"
+#define _SONAME_ARRAY3(a, b, c) "[\""a"\",\""b"\",\""c"\"]"
+#define _SONAME_ARRAY4(a, b, c, d) "[\""a"\",\""b"\",\""c"\"",\""d"\"]"
+#define _SONAME_ARRAY5(a, b, c, d, e) "[\""a"\",\""b"\",\""c"\"",\""d"\",\""e"\"]"
+#define _SONAME_ARRAY_GET(_1,_2,_3,_4,_5,NAME,...) NAME
+#define _SONAME_ARRAY(...) _SONAME_ARRAY_GET(__VA_ARGS__, _SONAME_ARRAY5, _SONAME_ARRAY4, _SONAME_ARRAY3, _SONAME_ARRAY2, _SONAME_ARRAY1)(__VA_ARGS__)
+
+/* The 'priority' must be one of 'required', 'recommended' or 'suggested' as per specification, use the
+ * macro defined above to specify it.
+ * Multiple sonames can be passed and they will be automatically contructed into a json array (but note that
+ * due to preprocessor language limitations if more than the limit defined above is used, a new
+ * _SONAME_ARRAY<X+1> will need to be added). */
+#define ELF_NOTE_DLOPEN(feature, description, priority, ...) \
+ _ELF_NOTE_DLOPEN("[{\"feature\":\"" feature "\",\"description\":\"" description "\",\"priority\":\"" priority "\",\"soname\":" _SONAME_ARRAY(__VA_ARGS__) "}]", UNIQ_T(s, UNIQ))
DLSYM_FUNCTION(gcry_strerror);
static int dlopen_gcrypt(void) {
+ ELF_NOTE_DLOPEN("gcrypt",
+ "Support for journald forward-sealing",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libgcrypt.so.20");
+
return dlopen_many_sym_or_warn(
&gcrypt_dl,
"libgcrypt.so.20", LOG_DEBUG,
if (r < 0)
return log_error_errno(r, "Failed to glob unit name: %m");
- r = strv_consume(&arg_mask, n);
- if (r < 0)
+ if (strv_consume(&arg_mask, n) < 0)
return log_oom();
} else if (streq(key, "systemd.wants")) {
if (r < 0)
return log_error_errno(r, "Failed to glob unit name: %m");
- r = strv_consume(&arg_wants, n);
- if (r < 0)
+ if (strv_consume(&arg_wants, n) < 0)
return log_oom();
} else if (proc_cmdline_key_streq(key, "systemd.debug_shell")) {
+
r = value ? parse_boolean(value) : 1;
arg_debug_shell = r != 0;
if (r >= 0)
return free_and_strdup_warn(&arg_debug_tty, skip_dev_prefix(value));
} else if (proc_cmdline_key_streq(key, "systemd.default_debug_tty")) {
+
if (proc_cmdline_value_missing(key, value))
return 0;
STRV_FOREACH(u, arg_mask) {
_cleanup_free_ char *p = NULL;
- p = path_join(empty_to_root(arg_dest), *u);
+ p = path_join(arg_dest, *u);
if (!p)
return log_oom();
if (symlink("/dev/null", p) < 0)
- r = log_error_errno(errno,
- "Failed to create mask symlink %s: %m",
- p);
+ RET_GATHER(r, log_error_errno(errno, "Failed to create mask symlink '%s': %m", p));
}
return r;
if (!f)
return log_oom();
- r = generator_add_symlink(arg_dest, target, "wants", f);
- if (r < 0)
- return r;
+ RET_GATHER(r, generator_add_symlink(arg_dest, target, "wants", f));
}
return r;
}
-static void install_debug_shell_dropin(void) {
+static int install_debug_shell_dropin(void) {
const char *tty = arg_debug_tty ?: arg_default_debug_tty;
int r;
if (!tty || path_equal(tty, skip_dev_prefix(DEBUGTTY)))
- return;
+ return 0;
r = write_drop_in_format(arg_dest, "debug-shell.service", 50, "tty",
- "[Unit]\n"
- "Description=Early root shell on /dev/%s FOR DEBUGGING ONLY\n"
- "ConditionPathExists=\n"
- "[Service]\n"
- "TTYPath=/dev/%s",
- tty, tty);
+ "# Automatically generated by systemd-debug-generator\n\n"
+ "[Unit]\n"
+ "Description=Early root shell on /dev/%s FOR DEBUGGING ONLY\n"
+ "ConditionPathExists=\n"
+ "\n[Service]\n"
+ "TTYPath=/dev/%s\n",
+ tty, tty);
if (r < 0)
- log_warning_errno(r, "Failed to write drop-in for debug-shell.service, ignoring: %m");
+ return log_warning_errno(r, "Failed to write drop-in for debug-shell.service: %m");
+
+ return 1;
}
static int process_unit_credentials(const char *credentials_dir) {
+ _cleanup_free_ DirectoryEntries *des = NULL;
int r;
assert(credentials_dir);
- _cleanup_free_ DirectoryEntries *des = NULL;
r = readdir_all_at(AT_FDCWD, credentials_dir, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
if (r < 0)
return log_error_errno(r, "Failed to enumerate credentials from credentials directory '%s': %m", credentials_dir);
FOREACH_ARRAY(i, des->entries, des->n_entries) {
- _cleanup_free_ void *d = NULL;
struct dirent *de = *i;
const char *unit, *dropin;
continue;
}
- r = read_credential_with_decryption(de->d_name, &d, NULL);
- if (r < 0)
+ _cleanup_free_ char *d = NULL;
+
+ r = read_credential_with_decryption(de->d_name, (void**) &d, NULL);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to read credential '%s', ignoring: %m", de->d_name);
continue;
+ }
if (unit) {
_cleanup_free_ char *p = NULL;
log_debug("Wrote unit file '%s' from credential '%s'", unit, de->d_name);
- } else {
+ } else if (dropin) {
r = write_drop_in(arg_dest, dropin, 50, "credential", d);
if (r < 0) {
log_warning_errno(r, "Failed to write drop-in for unit '%s' from credential '%s', ignoring: %m",
}
log_debug("Wrote drop-in for unit '%s' from credential '%s'", dropin, de->d_name);
- }
+ } else
+ assert_not_reached();
}
return 0;
static int run(const char *dest, const char *dest_early, const char *dest_late) {
const char *credentials_dir;
- int r = 0;
+ int r;
assert_se(arg_dest = dest_early);
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
if (arg_debug_shell) {
- r = strv_extend(&arg_wants, "debug-shell.service");
- if (r < 0)
+ if (strv_extend(&arg_wants, "debug-shell.service") < 0)
return log_oom();
- install_debug_shell_dropin();
+ RET_GATHER(r, install_debug_shell_dropin());
}
if (get_credentials_dir(&credentials_dir) >= 0)
DLSYM_FUNCTION(xkb_keymap_unref);
static int dlopen_xkbcommon(void) {
+ ELF_NOTE_DLOPEN("xkbcommon",
+ "Support for keyboard locale descriptions",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libxkbcommon.so.0");
+
return dlopen_many_sym_or_warn(
&xkbcommon_dl, "libxkbcommon.so.0", LOG_DEBUG,
DLSYM_ARG(xkb_context_new),
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Not running on EFI and resume= is not set, or noresume is set. No available method to resume from hibernation");
+ case SLEEP_RESUME_DEVICE_MISSING:
+ return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
+ "Specified resume device is missing or is not an active swap device");
+
+ case SLEEP_RESUME_MISCONFIGURED:
+ return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
+ "Invalid resume config: resume= is not populated yet resume_offset= is");
+
case SLEEP_NOT_ENOUGH_SWAP_SPACE:
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Not enough suitable swap space for hibernation available on compatible block devices and file systems");
void *dl;
int r;
+ ELF_NOTE_DLOPEN("bpf",
+ "Support firewalling and sandboxing with BPF",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libbpf.so.1", "libbpf.so.0");
+
DISABLE_WARNING_DEPRECATED_DECLARATIONS;
dl = dlopen("libbpf.so.1", RTLD_LAZY);
DISABLE_WARNING_DEPRECATED_DECLARATIONS;
+ ELF_NOTE_DLOPEN("cryptsetup",
+ "Support for disk encryption, integrity, and authentication",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libcryptsetup.so.12");
+
r = dlopen_many_sym_or_warn(
&cryptsetup_dl, "libcryptsetup.so.12", LOG_DEBUG,
DLSYM_ARG(crypt_activate_by_passphrase),
int dlopen_dw(void) {
int r;
+ ELF_NOTE_DLOPEN("dw",
+ "Support for backtrace and ELF package metadata decoding from core files",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libdw.so.1");
+
r = dlopen_many_sym_or_warn(
&dw_dl, "libdw.so.1", LOG_DEBUG,
DLSYM_ARG(dwarf_getscopes),
int dlopen_elf(void) {
int r;
+ ELF_NOTE_DLOPEN("elf",
+ "Support for backtraces and reading ELF package metadata from core files",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libelf.so.1");
+
r = dlopen_many_sym_or_warn(
&elf_dl, "libelf.so.1", LOG_DEBUG,
DLSYM_ARG(elf_begin),
}
static int dlopen_iptc(void) {
+ ELF_NOTE_DLOPEN("ip4tc",
+ "Support for firewall rules",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libip4tc.so.2");
+
return dlopen_many_sym_or_warn(
&iptc_dl,
"libip4tc.so.2", LOG_DEBUG,
}
if (devno == 0 && offset > 0 && offset != UINT64_MAX)
- return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
"Found populated /sys/power/resume_offset (%" PRIu64 ") but /sys/power/resume is not set, refusing.",
offset);
if (!entry) {
/* No need to check n_swaps == 0, since it's rejected early */
assert(resume_config_devno > 0);
- return log_debug_errno(SYNTHETIC_ERRNO(ENOSPC), "Cannot find swap entry corresponding to /sys/power/resume.");
+ return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Cannot find swap entry corresponding to /sys/power/resume.");
}
if (ret_device) {
bypass_space_check = getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0;
r = find_suitable_hibernation_device_full(NULL, &size, &used);
- if (r == -ENOSPC && bypass_space_check)
- /* If we don't have any available swap space at all, and SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK
- * is set, skip all remaining checks since we can't do that properly anyway. It is quite
- * possible that the user is using a setup similar to #30083. When we actually perform
- * hibernation in sleep.c we'll check everything again. */
+ if (IN_SET(r, -ENOSPC, -ESTALE) && bypass_space_check)
+ /* If we don't have any available swap space at all, or the specified resume device is missing,
+ * and $SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK is set, skip all remaining checks since
+ * we can't do that properly anyway. It is quite possible that the user is using a setup
+ * similar to #30083. When we actually perform hibernation in sleep.c we'll check everything again. */
return 0;
if (r < 0)
return r;
DLSYM_FUNCTION(idn2_to_unicode_8z8z);
int dlopen_idn(void) {
+ ELF_NOTE_DLOPEN("idn",
+ "Support for internationalized domain names",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libidn2.so.0");
+
return dlopen_many_sym_or_warn(
&idn_dl, "libidn2.so.0", LOG_DEBUG,
DLSYM_ARG(idn2_lookup_u8),
_cleanup_(dlclosep) void *dl = NULL;
int r;
+ ELF_NOTE_DLOPEN("idn",
+ "Support for internationalized domain names",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libidn.so.12", "libidn.so.11");
+
if (idn_dl)
return 0; /* Already loaded */
bus_error = BUS_ERROR_NO_SUCH_UNIT;
break;
+ case -ENOLINK:
+ m = strjoin("Unit ", change->path, " is an unresolvable alias");
+ bus_error = BUS_ERROR_NO_SUCH_UNIT;
+ break;
+
case -EUNATCH:
m = strjoin("Cannot resolve specifiers in unit ", change->path);
bus_error = BUS_ERROR_BAD_UNIT_SETTING;
if (r < 0)
return r;
+ r = 0;
STRV_FOREACH(i, lp.search_path) {
_cleanup_closedir_ DIR *d = NULL;
d = opendir(*i);
if (!d) {
- if (errno == ENOENT)
- continue;
-
- return -errno;
+ if (errno != ENOENT)
+ RET_GATHER(r, -errno);
+ continue;
}
- FOREACH_DIRENT(de, d, return -errno) {
+ FOREACH_DIRENT(de, d, RET_GATHER(r, -errno)) {
+ int k;
if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
if (!IN_SET(de->d_type, DT_LNK, DT_REG))
continue;
- r = preset_prepare_one(scope, &plus, &minus, &lp, de->d_name, &presets, changes, n_changes);
- if (r < 0 &&
- !IN_SET(r, -EEXIST, -ERFKILL, -EADDRNOTAVAIL, -EBADSLT, -EIDRM, -EUCLEAN, -ELOOP, -ENOENT, -EUNATCH, -EXDEV))
+ k = preset_prepare_one(scope, &plus, &minus, &lp, de->d_name, &presets, changes, n_changes);
+ if (k < 0 &&
+ !IN_SET(k, -EEXIST,
+ -ERFKILL,
+ -EADDRNOTAVAIL,
+ -ETXTBSY,
+ -EBADSLT,
+ -EIDRM,
+ -EUCLEAN,
+ -ELOOP,
+ -EXDEV,
+ -ENOENT,
+ -ENOLINK,
+ -EUNATCH))
/* Ignore generated/transient/missing/invalid units when applying preset, propagate other errors.
- * Coordinate with install_changes_dump() above. */
- return r;
+ * Coordinate with install_change_dump_error() above. */
+ RET_GATHER(r, k);
}
}
DLSYM_FUNCTION(archive_write_set_format_gnutar);
int dlopen_libarchive(void) {
+ ELF_NOTE_DLOPEN("archive",
+ "Support for decompressing archive files",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libarchive.so.13");
+
return dlopen_many_sym_or_warn(
&libarchive_dl,
"libarchive.so.13",
int dlopen_libfido2(void) {
int r;
+ ELF_NOTE_DLOPEN("fido2",
+ "Support fido2 for encryption and authentication",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libfido2.so.1");
+
r = dlopen_many_sym_or_warn(
&libfido2_dl, "libfido2.so.1", LOG_DEBUG,
DLSYM_ARG(fido_assert_allow_cred),
DLSYM_FUNCTION(kmod_validate_resources);
int dlopen_libkmod(void) {
+ ELF_NOTE_DLOPEN("kmod",
+ "Support for loading kernel modules",
+ ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED,
+ "libkmod.so.2");
+
return dlopen_many_sym_or_warn(
&libkmod_dl,
"libkmod.so.2",
DLSYM_FUNCTION(passwdqc_random);
int dlopen_passwdqc(void) {
+ ELF_NOTE_DLOPEN("passwdqc",
+ "Support for password quality checks",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libpasswdqc.so.1");
+
return dlopen_many_sym_or_warn(
&passwdqc_dl, "libpasswdqc.so.1", LOG_DEBUG,
DLSYM_ARG(passwdqc_params_reset),
DLSYM_FUNCTION(pwquality_strerror);
int dlopen_pwquality(void) {
+ ELF_NOTE_DLOPEN("pwquality",
+ "Support for password quality checks",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libpwquality.so.1");
+
return dlopen_many_sym_or_warn(
&pwquality_dl, "libpwquality.so.1", LOG_DEBUG,
DLSYM_ARG(pwquality_check),
int dlopen_pcre2(void) {
#if HAVE_PCRE2
+ ELF_NOTE_DLOPEN("pcre2",
+ "Support for regular expressions",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libpcre2-8.so.0");
+
/* So here's something weird: PCRE2 actually renames the symbols exported by the library via C
* macros, so that the exported symbols carry a suffix "_8" but when used from C the suffix is
* gone. In the argument list below we ignore this mangling. Surprisingly (at least to me), we
DLSYM_FUNCTION(p11_kit_uri_parse);
int dlopen_p11kit(void) {
+ ELF_NOTE_DLOPEN("p11-kit",
+ "Support for PKCS11 hardware tokens",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libp11-kit.so.0");
+
return dlopen_many_sym_or_warn(
&p11kit_dl,
"libp11-kit.so.0", LOG_DEBUG,
int dlopen_qrencode(void) {
int r;
+ ELF_NOTE_DLOPEN("qrencode",
+ "Support for generating QR codes",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libqrencode.so.4", "libqrencode.so.3");
+
FOREACH_STRING(s, "libqrencode.so.4", "libqrencode.so.3") {
r = dlopen_many_sym_or_warn(
&qrcode_dl, s, LOG_DEBUG,
}
r = hibernation_is_safe();
- if (r == -ENOTRECOVERABLE) {
+ switch (r) {
+
+ case -ENOTRECOVERABLE:
*ret_support = SLEEP_RESUME_NOT_SUPPORTED;
return false;
- }
- if (r == -ENOSPC) {
+
+ case -ESTALE:
+ *ret_support = SLEEP_RESUME_DEVICE_MISSING;
+ return false;
+
+ case -ENOMEDIUM:
+ *ret_support = SLEEP_RESUME_MISCONFIGURED;
+ return false;
+
+ case -ENOSPC:
*ret_support = SLEEP_NOT_ENOUGH_SWAP_SPACE;
return false;
+
+ default:
+ if (r < 0)
+ return r;
}
- if (r < 0)
- return r;
} else
assert(!sleep_config->modes[operation]);
SLEEP_NOT_CONFIGURED, /* SleepConfig.states is not configured */
SLEEP_STATE_OR_MODE_NOT_SUPPORTED, /* SleepConfig.states/modes are not supported by kernel */
SLEEP_RESUME_NOT_SUPPORTED,
+ SLEEP_RESUME_DEVICE_MISSING, /* resume= is specified, but the device cannot be found in /proc/swaps */
+ SLEEP_RESUME_MISCONFIGURED, /* resume= is not set yet resume_offset= is configured */
SLEEP_NOT_ENOUGH_SWAP_SPACE,
SLEEP_ALARM_NOT_SUPPORTED, /* CLOCK_BOOTTIME_ALARM is unsupported by kernel (only used by s2h) */
} SleepSupport;
int dlopen_tpm2(void) {
int r;
+ ELF_NOTE_DLOPEN("tpm",
+ "Support for TPM",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libtss2-esys.so.0");
+
r = dlopen_many_sym_or_warn(
&libtss2_esys_dl, "libtss2-esys.so.0", LOG_DEBUG,
DLSYM_ARG(Esys_Create),
if (r < 0)
log_debug("libtss2-esys too old, does not include Esys_TR_GetTpmHandle.");
+ ELF_NOTE_DLOPEN("tpm",
+ "Support for TPM",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libtss2-rc.so.0");
+
r = dlopen_many_sym_or_warn(
&libtss2_rc_dl, "libtss2-rc.so.0", LOG_DEBUG,
DLSYM_ARG(Tss2_RC_Decode));
if (r < 0)
return r;
+ ELF_NOTE_DLOPEN("tpm",
+ "Support for TPM",
+ ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+ "libtss2-mu.so.0");
+
return dlopen_many_sym_or_warn(
&libtss2_mu_dl, "libtss2-mu.so.0", LOG_DEBUG,
DLSYM_ARG(Tss2_MU_TPM2_CC_Marshal),
size_t sz = 0;
int r;
- r = read_credential_with_decryption("ssh.listen", (void*) &b, &sz);
+ r = read_credential_with_decryption("ssh.listen", (void**) &b, &sz);
if (r <= 0)
return r;
case ACTION_HYBRID_SLEEP:
case ACTION_SUSPEND_THEN_HIBERNATE:
+ /* For sleep operations, do not automatically fall back to low-level operation for
+ * errors other than logind not available. There's a high chance that logind did
+ * some extra sanity check and that didn't pass. */
r = logind_reboot(a);
- if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
+ if (r >= 0 || (r != -ENOSYS && arg_force == 0))
return r;
arg_no_block = true;