Which boots are "successful" is determined by the operating system. systemd
provides a generic mechanism that can be extended with arbitrary checks and
-actions, see [Automatic Boot Assesment](AUTOMATIC_BOOT_ASSESSMENT.md), but the
+actions, see [Automatic Boot Assessment](AUTOMATIC_BOOT_ASSESSMENT.md), but the
boot counting mechanism described in this specifaction can also be used with
other implementations.
- Use `typesafe_inet_ntop()`, `typesafe_inet_ntop4()`, and
`typesafe_inet_ntop6()` instead of `inet_ntop()`. But better yet, use the
`IN_ADDR_TO_STRING()`, `IN4_ADDR_TO_STRING()`, and `IN6_ADDR_TO_STRING()`
- macros which allocate an anynomous buffer internally.
+ macros which allocate an anonymous buffer internally.
- Please never use `dup()`. Use `fcntl(fd, F_DUPFD_CLOEXEC, 3)` instead. For
two reasons: first, you want `O_CLOEXEC` set on the new `fd` (see
--sd-brand-green: hsl(145, 66%, 51%); /* #30D475; */
--sd-brand-white: #fff;
+ --sd-black: hsl(270, 7%, 13%);
--sd-green: hsl(145, 66%, 43%); /* #26b763 */
--sd-gray-extralight: hsl(30, 10%, 96%); /* #f6f5f4 */
--sd-gray-light: hsl(30, 10%, 92%);
:root {
color-scheme: dark;
--sd-foreground-color: var(--sd-gray);
- --sd-background-color: var(--sd-brand-black);
+ --sd-background-color: var(--sd-black);
--sd-logo-color: var(--sd-brand-white);
--sd-link-color: var(--sd-brand-green);
--sd-small-color: var(--sd-gray);
</row>
</tbody>
</tgroup>
- </table></listitem>
+ </table>
+
+ <para>Supported glob wilcard patterns are <literal>?</literal>, <literal>*</literal>, and
+ <literal>[…]</literal> (including ranges). Note that these patterns use the same syntax as
+ <citerefentry><refentrytitle>glob</refentrytitle><manvolnum>7</manvolnum></citerefentry>, but do not
+ support all features. In particular, set negation and named character classes are not supported. The
+ matching is done case-insensitively on the entry ID (as shown by <command>bootctl
+ list</command>).</para></listitem>
</varlistentry>
<varlistentry>
possible_link_flags += '-Wl,--gc-sections'
endif
+if get_option('mode') == 'developer'
+ possible_cc_flags += '-fno-omit-frame-pointer'
+endif
+
add_project_arguments(cc.get_supported_arguments(basic_disabled_warnings), language : 'c')
add_project_arguments(cc.get_supported_arguments(possible_cc_flags), language : 'c')
add_project_link_arguments(cc.get_supported_link_arguments(possible_link_flags), language : 'c')
if want_bpf_framework == 'false' or not libbpf.found() or skip_deps
conf.set10('BPF_FRAMEWORK', false)
else
- # Support 'versioned' clang/llvm-strip binaries, as seen on Debian/Ubuntu
- # (like clang-10/llvm-strip-10)
- if meson.is_cross_build() or cc.get_id() != 'clang' or cc.cmd_array()[0].contains('afl-clang') or cc.cmd_array()[0].contains('hfuzz-clang')
- r = find_program('clang', required : bpf_framework_required, version : '>= 10.0.0')
- clang_found = r.found()
- if clang_found
- clang = r.path()
+ bpf_compiler = get_option('bpf-compiler')
+ clang_found = false
+ clang_supports_bpf = false
+ bpf_gcc_found = false
+ deps_found = false
+
+ if bpf_compiler == 'clang'
+ # Support 'versioned' clang/llvm-strip binaries, as seen on Debian/Ubuntu
+ # (like clang-10/llvm-strip-10)
+ if meson.is_cross_build() or cc.get_id() != 'clang' or cc.cmd_array()[0].contains('afl-clang') or cc.cmd_array()[0].contains('hfuzz-clang')
+ r = find_program('clang', required : bpf_framework_required, version : '>= 10.0.0')
+ clang_found = r.found()
+ if clang_found
+ clang = r.path()
+ endif
+ # Assume that the required flags are supported by the found clang.
+ clang_supports_flags = clang_found
+ else
+ clang_found = true
+ clang = cc.cmd_array()
+ clang_supports_flags = cc.has_argument('-Wno-compare-distinct-pointer-types')
endif
- # Assume that the required flags are supported by the found clang.
- clang_supports_flags = clang_found
- else
- clang_found = true
- clang = cc.cmd_array()
- clang_supports_flags = cc.has_argument('-Wno-compare-distinct-pointer-types')
- endif
- if clang_found
- # Check if 'clang -target bpf' is supported.
- clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus', check : false).returncode() == 0
- else
- clang_supports_bpf = false
+ if clang_found
+ # Check if 'clang -target bpf' is supported.
+ clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus', check : false).returncode() == 0
+ endif
+ elif bpf_compiler == 'gcc'
+ warning('GCC BPF Compiler support is experimental and not recommended.')
+ bpf_gcc = find_program('bpf-gcc',
+ required : true,
+ version : '>= 12.1.0')
+ bpf_gcc_found = bpf_gcc.found()
endif
- # Debian installs this in /usr/sbin/ which is not in $PATH.
- # We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian.
- # We use 'bpftool gen' subcommand, it was added by 985ead416df39d6fe8e89580cc1db6aa273e0175 (v5.6).
- bpftool = find_program('bpftool',
- '/usr/sbin/bpftool',
- required : false,
- version : '>= 5.13.0')
-
- if bpftool.found()
- bpftool_strip = true
- else
- bpftool_strip = false
+ if clang_supports_bpf or bpf_gcc_found
+ # Debian installs this in /usr/sbin/ which is not in $PATH.
+ # We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian.
+ # We use 'bpftool gen object' subcommand for bpftool strip, it was added by d80b2fcbe0a023619e0fc73112f2a02c2662f6ab (v5.13).
bpftool = find_program('bpftool',
'/usr/sbin/bpftool',
- required : bpf_framework_required,
- version : '>= 5.6.0')
- endif
+ required : false,
+ version : '>= 5.13.0')
- if not bpftool_strip
- if not meson.is_cross_build() and clang_found
- llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip',
- check : true).stdout().strip()
+ if bpftool.found()
+ bpftool_strip = true
+ deps_found = true
else
- llvm_strip_bin = 'llvm-strip'
+ bpftool_strip = false
+ # We require the 'bpftool gen skeleton' subcommand, it was added by 985ead416df39d6fe8e89580cc1db6aa273e0175 (v5.6).
+ bpftool = find_program('bpftool',
+ '/usr/sbin/bpftool',
+ required : bpf_framework_required,
+ version : '>= 5.6.0')
endif
- llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required, version : '>= 10.0.0')
- endif
- deps_found = clang_found and clang_supports_bpf and clang_supports_flags and (bpftool_strip or llvm_strip.found()) and bpftool.found()
+ # We use `llvm-strip` as a fallback if `bpftool gen object` strip support is not available.
+ if not bpftool_strip and bpftool.found() and clang_supports_bpf
+ if not meson.is_cross_build()
+ llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip',
+ check : true).stdout().strip()
+ else
+ llvm_strip_bin = 'llvm-strip'
+ endif
+ llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required, version : '>= 10.0.0')
+ deps_found = llvm_strip.found()
+ endif
+ endif
# Can build BPF program from source code in restricted C
conf.set10('BPF_FRAMEWORK', deps_found)
option('analyze', type: 'boolean', value: 'true',
description : 'install systemd-analyze')
+option('bpf-compiler', type : 'combo', choices : ['clang', 'gcc'],
+ description: 'compiler used to build BPF programs, note: gcc is experimental')
option('bpf-framework', type : 'combo', choices : ['auto', 'true', 'false'],
description: 'build BPF programs from source code in restricted C')
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
+"PO-Revision-Date: 2022-06-09 21:18+0000\n"
+"Last-Translator: H A <contact+fedora@hen.ee>\n"
+"Language-Team: Estonian <https://translate.fedoraproject.org/projects/"
+"systemd/master/et/>\n"
"Language: et\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.12.2\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#: src/login/org.freedesktop.login1.policy:406
msgid "Change Session"
-msgstr ""
+msgstr "Vaheta sessiooni"
#: src/login/org.freedesktop.login1.policy:407
msgid "Authentication is required to change the virtual terminal."
#: src/machine/org.freedesktop.machine1.policy:95
msgid "Manage local virtual machine and container images"
-msgstr ""
+msgstr "Halda kohalikke virtuaalmasinaid ja konteinerpilte"
#: src/machine/org.freedesktop.machine1.policy:96
msgid ""
"Authentication is required to manage local virtual machine and container "
"images."
msgstr ""
+"Autentimine on vajalik, et hallata kohalikke virtuaalmasinaid ja "
+"konteinerpilte."
#: src/network/org.freedesktop.network1.policy:22
msgid "Set NTP servers"
-msgstr ""
+msgstr "Säti NTP servereid"
#: src/network/org.freedesktop.network1.policy:23
msgid "Authentication is required to set NTP servers."
-msgstr ""
+msgstr "Autentimine on vajalik, et seadistada NTP servereid."
#: src/network/org.freedesktop.network1.policy:33
#: src/resolve/org.freedesktop.resolve1.policy:44
msgid "Set DNS servers"
-msgstr ""
+msgstr "Säti DNS serverid"
#: src/network/org.freedesktop.network1.policy:34
#: src/resolve/org.freedesktop.resolve1.policy:45
msgid "Authentication is required to set DNS servers."
-msgstr ""
+msgstr "Autentimine on vajalik, et seadistada DNS servereid."
#: src/network/org.freedesktop.network1.policy:44
#: src/resolve/org.freedesktop.resolve1.policy:55
msgid "Set domains"
-msgstr ""
+msgstr "Säti domeenid"
#: src/network/org.freedesktop.network1.policy:45
#: src/resolve/org.freedesktop.resolve1.policy:56
msgid "Authentication is required to set domains."
-msgstr ""
+msgstr "Autentimine on vajalik, et seadistada domeene."
#: src/network/org.freedesktop.network1.policy:55
#: src/resolve/org.freedesktop.resolve1.policy:66
msgid "Set default route"
-msgstr ""
+msgstr "Säti default route"
#: src/network/org.freedesktop.network1.policy:56
#: src/resolve/org.freedesktop.resolve1.policy:67
msgid "Authentication is required to set default route."
-msgstr ""
+msgstr "Autentimine on vajalik, et seadistada default route."
#: src/network/org.freedesktop.network1.policy:66
#: src/resolve/org.freedesktop.resolve1.policy:77
msgid "Enable/disable LLMNR"
-msgstr ""
+msgstr "Lülita LLMNR sisse/välja"
#: src/network/org.freedesktop.network1.policy:67
#: src/resolve/org.freedesktop.resolve1.policy:78
msgid "Authentication is required to enable or disable LLMNR."
-msgstr ""
+msgstr "Autentimine on vajalik, et LLMNR lülitada sisse või välja."
#: src/network/org.freedesktop.network1.policy:77
#: src/resolve/org.freedesktop.resolve1.policy:88
msgid "Enable/disable multicast DNS"
-msgstr ""
+msgstr "Lülita multicast DNS sisse/välja"
#: src/network/org.freedesktop.network1.policy:78
#: src/resolve/org.freedesktop.resolve1.policy:89
msgid "Authentication is required to enable or disable multicast DNS."
-msgstr ""
+msgstr "Autentimine on vajalik, et multicast DNS lülitada sisse või välja."
#: src/network/org.freedesktop.network1.policy:88
#: src/resolve/org.freedesktop.resolve1.policy:99
msgid "Enable/disable DNS over TLS"
-msgstr ""
+msgstr "Lülita DNS üle TLS sisse/välja"
#: src/network/org.freedesktop.network1.policy:89
#: src/resolve/org.freedesktop.resolve1.policy:100
msgid "Authentication is required to enable or disable DNS over TLS."
-msgstr ""
+msgstr "Autentimine on vajalik, et DNS üle TLS'i lülitada sisse või välja."
#: src/network/org.freedesktop.network1.policy:99
#: src/resolve/org.freedesktop.resolve1.policy:110
msgid "Enable/disable DNSSEC"
-msgstr ""
+msgstr "Lülita DNSSEC sisse/välja"
#: src/network/org.freedesktop.network1.policy:100
#: src/resolve/org.freedesktop.resolve1.policy:111
msgid "Authentication is required to enable or disable DNSSEC."
-msgstr ""
+msgstr "Autentimine on vajalik, et DNSSEC lülitada sisse või välja."
#: src/network/org.freedesktop.network1.policy:110
#: src/resolve/org.freedesktop.resolve1.policy:121
msgid "Set DNSSEC Negative Trust Anchors"
-msgstr ""
+msgstr "Säti DNSSEC negatiivsed usaldusankrud"
#: src/network/org.freedesktop.network1.policy:111
#: src/resolve/org.freedesktop.resolve1.policy:122
msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
-msgstr ""
+msgstr "Autentimine on vajalik, et seadistada DNSSEC negatiivsed usaldusankrud."
#: src/network/org.freedesktop.network1.policy:121
msgid "Revert NTP settings"
-msgstr ""
+msgstr "Taasta NTP sätted"
#: src/network/org.freedesktop.network1.policy:122
msgid "Authentication is required to reset NTP settings."
-msgstr ""
+msgstr "Autentimine on vajalik, et taastada NTP sätted."
#: src/network/org.freedesktop.network1.policy:132
msgid "Revert DNS settings"
-msgstr ""
+msgstr "Taasta DNS sätted"
#: src/network/org.freedesktop.network1.policy:133
msgid "Authentication is required to reset DNS settings."
-msgstr ""
+msgstr "Autentimine on vajalik, et lähtestada DNS sätted."
#: src/network/org.freedesktop.network1.policy:143
msgid "DHCP server sends force renew message"
-msgstr ""
+msgstr "DHCP server saadab sunduuendamise sõnumi"
#: src/network/org.freedesktop.network1.policy:144
msgid "Authentication is required to send force renew message."
-msgstr ""
+msgstr "Autentimine on vajalik, et saata sunduuendamis sõnumi."
#: src/network/org.freedesktop.network1.policy:154
msgid "Renew dynamic addresses"
-msgstr ""
+msgstr "Uuenda dünaamilist aadressit"
#: src/network/org.freedesktop.network1.policy:155
msgid "Authentication is required to renew dynamic addresses."
-msgstr ""
+msgstr "Autentimine on vajalik, et uuendada dünaamilist aadressit."
#: src/network/org.freedesktop.network1.policy:165
msgid "Reload network settings"
-msgstr ""
+msgstr "Lae võrgu seaded uuesti"
#: src/network/org.freedesktop.network1.policy:166
msgid "Authentication is required to reload network settings."
-msgstr ""
+msgstr "Autentimine on vajalik, et laadida võrgu seaded uuesti."
#: src/network/org.freedesktop.network1.policy:176
msgid "Reconfigure network interface"
-msgstr ""
+msgstr "Määra võrguliides"
#: src/network/org.freedesktop.network1.policy:177
msgid "Authentication is required to reconfigure network interface."
#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
-msgstr ""
+msgstr "Säti süsteemi aeg"
#: src/timedate/org.freedesktop.timedate1.policy:23
msgid "Authentication is required to set the system time."
-msgstr ""
+msgstr "Autentimine on vajalik, et seadistada süsteemi aeg."
#: src/timedate/org.freedesktop.timedate1.policy:33
msgid "Set system timezone"
-msgstr ""
+msgstr "Säti süsteemi ajavöönd"
#: src/timedate/org.freedesktop.timedate1.policy:34
msgid "Authentication is required to set the system timezone."
case KEYPRESS(0, CHAR_CARRIAGE_RETURN, 0): /* EZpad Mini 4s firmware sends malformed events */
case KEYPRESS(0, CHAR_CARRIAGE_RETURN, CHAR_CARRIAGE_RETURN): /* Teclast X98+ II firmware sends malformed events */
if (!streq16(line, *line_in)) {
- FreePool(*line_in);
+ free(*line_in);
*line_in = TAKE_PTR(line);
}
return TRUE;
}
if (timeout_remain > 0) {
- FreePool(status);
+ free(status);
status = xpool_print(L"Boot in %u s.", timeout_remain);
}
case KEYPRESS(0, 0, 'd'):
case KEYPRESS(0, 0, 'D'):
if (config->idx_default_efivar != idx_highlight) {
- FreePool(config->entry_default_efivar);
+ free(config->entry_default_efivar);
config->entry_default_efivar = xstrdup16(config->entries[idx_highlight]->id);
config->idx_default_efivar = idx_highlight;
status = xstrdup16(u"Default boot entry selected.");
assert(config->entry_count < IDX_MAX);
if ((config->entry_count & 15) == 0) {
- UINTN i = config->entry_count + 16;
- config->entries = xreallocate_pool(
+ config->entries = xrealloc(
config->entries,
sizeof(void *) * config->entry_count,
- sizeof(void *) * i);
+ sizeof(void *) * (config->entry_count + 16));
}
config->entries[config->entry_count++] = entry;
}
if (!entry)
return;
- FreePool(entry->id);
- FreePool(entry->title_show);
- FreePool(entry->title);
- FreePool(entry->sort_key);
- FreePool(entry->version);
- FreePool(entry->machine_id);
- FreePool(entry->loader);
- FreePool(entry->devicetree);
- FreePool(entry->options);
+ free(entry->id);
+ free(entry->title_show);
+ free(entry->title);
+ free(entry->sort_key);
+ free(entry->version);
+ free(entry->machine_id);
+ free(entry->loader);
+ free(entry->devicetree);
+ free(entry->options);
strv_free(entry->initrd);
- FreePool(entry->path);
- FreePool(entry->current_name);
- FreePool(entry->next_name);
- FreePool(entry);
+ free(entry->path);
+ free(entry->current_name);
+ free(entry->next_name);
+ free(entry);
}
static inline void config_entry_freep(ConfigEntry **entry) {
}
if (streq8((char *) key, "default")) {
- if (value[0] == '@' && !streq8((char *) value, "@saved")) {
+ if (value[0] == '@' && !strcaseeq8((char *) value, "@saved")) {
log_error_stall(L"Unsupported special entry identifier: %a", value);
continue;
}
- FreePool(config->entry_default_config);
+ free(config->entry_default_config);
config->entry_default_config = xstra_to_str(value);
continue;
}
/* If the file we just renamed is the loader path, then let's update that. */
if (streq16(entry->loader, old_path)) {
- FreePool(entry->loader);
+ free(entry->loader);
entry->loader = TAKE_PTR(new_path);
}
}
while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
if (streq8((char *) key, "title")) {
- FreePool(entry->title);
+ free(entry->title);
entry->title = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "sort-key")) {
- FreePool(entry->sort_key);
+ free(entry->sort_key);
entry->sort_key = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "version")) {
- FreePool(entry->version);
+ free(entry->version);
entry->version = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "machine-id")) {
- FreePool(entry->machine_id);
+ free(entry->machine_id);
entry->machine_id = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "linux")) {
- FreePool(entry->loader);
+ free(entry->loader);
entry->type = LOADER_LINUX;
entry->loader = xstra_to_path(value);
entry->key = 'l';
if (streq8((char *) key, "efi")) {
entry->type = LOADER_EFI;
- FreePool(entry->loader);
+ free(entry->loader);
entry->loader = xstra_to_path(value);
/* do not add an entry for ourselves */
}
if (streq8((char *) key, "devicetree")) {
- FreePool(entry->devicetree);
+ free(entry->devicetree);
entry->devicetree = xstra_to_path(value);
continue;
}
if (streq8((char *) key, "initrd")) {
- entry->initrd = xreallocate_pool(
+ entry->initrd = xrealloc(
entry->initrd,
n_initrd == 0 ? 0 : (n_initrd + 1) * sizeof(UINT16 *),
(n_initrd + 2) * sizeof(UINT16 *));
CHAR16 *s;
s = xpool_print(L"%s %s", entry->options, new);
- FreePool(entry->options);
+ free(entry->options);
entry->options = s;
} else
entry->options = TAKE_PTR(new);
(void) efivar_get(LOADER_GUID, L"LoaderEntryDefault", &config->entry_default_efivar);
+ strtolower16(config->entry_default_config);
+ strtolower16(config->entry_default_efivar);
+ strtolower16(config->entry_oneshot);
+ strtolower16(config->entry_saved);
+
config->use_saved_entry = streq16(config->entry_default_config, L"@saved");
config->use_saved_entry_efivar = streq16(config->entry_default_efivar, L"@saved");
if (config->use_saved_entry || config->use_saved_entry_efivar)
return CMP(a->tries_done, b->tries_done);
}
-static UINTN config_entry_find(Config *config, const CHAR16 *needle) {
+static UINTN config_entry_find(Config *config, const CHAR16 *pattern) {
assert(config);
- if (!needle)
+ /* We expect pattern and entry IDs to be already case folded. */
+
+ if (!pattern)
return IDX_INVALID;
for (UINTN i = 0; i < config->entry_count; i++)
- if (MetaiMatch(config->entries[i]->id, (CHAR16*) needle))
+ if (efi_fnmatch(pattern, config->entries[i]->id))
return i;
return IDX_INVALID;
if (!config->auto_entries)
return;
- err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &n_handles, &handles);
- if (EFI_ERROR(err))
+ err = BS->LocateHandleBuffer(ByProtocol, &FileSystemProtocol, NULL, &n_handles, &handles);
+ if (err != EFI_SUCCESS)
return;
for (UINTN i = 0; i < n_handles; i++) {
- _cleanup_(file_closep) EFI_FILE *root = LibOpenRoot(handles[i]);
- if (!root)
+ _cleanup_(file_closep) EFI_FILE *root = NULL;
+
+ if (open_volume(handles[i], &root) != EFI_SUCCESS)
continue;
if (config_entry_add_loader_auto(
/* read properties from the embedded os-release file */
while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
if (streq8((char *) key, "PRETTY_NAME")) {
- FreePool(os_pretty_name);
+ free(os_pretty_name);
os_pretty_name = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "IMAGE_ID")) {
- FreePool(os_image_id);
+ free(os_image_id);
os_image_id = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "NAME")) {
- FreePool(os_name);
+ free(os_name);
os_name = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "ID")) {
- FreePool(os_id);
+ free(os_id);
os_id = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "IMAGE_VERSION")) {
- FreePool(os_image_version);
+ free(os_image_version);
os_image_version = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "VERSION")) {
- FreePool(os_version);
+ free(os_version);
os_version = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "VERSION_ID")) {
- FreePool(os_version_id);
+ free(os_version_id);
os_version_id = xstra_to_str(value);
continue;
}
if (streq8((char *) key, "BUILD_ID")) {
- FreePool(os_build_id);
+ free(os_build_id);
os_build_id = xstra_to_str(value);
continue;
}
assert(device);
err = xbootldr_open(device, &new_device, &root_dir);
- if (EFI_ERROR(err))
+ if (err != EFI_SUCCESS)
return;
config_entry_add_unified(config, new_device, root_dir);
UINTN new_size, read_size = info->FileSize;
if (__builtin_add_overflow(size, read_size, &new_size))
return EFI_OUT_OF_RESOURCES;
- initrd = xreallocate_pool(initrd, size, new_size);
+ initrd = xrealloc(initrd, size, new_size);
err = handle->Read(handle, &read_size, initrd + size);
if (EFI_ERROR(err))
if (entry->call)
(void) entry->call();
- _cleanup_(file_closep) EFI_FILE *image_root = LibOpenRoot(entry->device);
- if (!image_root)
- return log_error_status_stall(EFI_DEVICE_ERROR, L"Error opening root path.");
+ _cleanup_(file_closep) EFI_FILE *image_root = NULL;
+ err = open_volume(entry->device, &image_root);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Error opening root path: %r", err);
- path = FileDevicePath(entry->device, entry->loader);
- if (!path)
- return log_error_status_stall(EFI_INVALID_PARAMETER, L"Error getting device path.");
+ err = make_file_device_path(entry->device, entry->loader, &path);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Error making file device path: %r", err);
UINTN initrd_size = 0;
_cleanup_freepool_ void *initrd = NULL;
assert(config);
for (UINTN i = 0; i < config->entry_count; i++)
config_entry_free(config->entries[i]);
- FreePool(config->entries);
- FreePool(config->entry_default_config);
- FreePool(config->entry_oneshot);
+ free(config->entries);
+ free(config->entry_default_config);
+ free(config->entry_oneshot);
}
static void config_write_entries_to_variable(Config *config) {
for (UINTN i = 0; i < config->entry_count; i++)
sz += strsize16(config->entries[i]->id);
- p = buffer = xallocate_pool(sz);
+ p = buffer = xmalloc(sz);
for (UINTN i = 0; i < config->entry_count; i++) {
UINTN l;
export_variables(loaded_image, loaded_image_path, init_usec);
- root_dir = LibOpenRoot(loaded_image->DeviceHandle);
- if (!root_dir)
- return log_error_status_stall(EFI_LOAD_ERROR, L"Unable to open root directory.", EFI_LOAD_ERROR);
+ err = open_volume(loaded_image->DeviceHandle, &root_dir);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Unable to open root directory: %r", err);
if (secure_boot_enabled() && shim_loaded()) {
err = security_policy_install();
if (!checked) {
/* Get the *first* TextInputEx device.*/
- err = LibLocateProtocol(&SimpleTextInputExProtocol, (void **) &extraInEx);
+ err = BS->LocateProtocol(&SimpleTextInputExProtocol, NULL, (void **) &extraInEx);
if (EFI_ERROR(err) || BS->CheckEvent(extraInEx->WaitForKeyEx) == EFI_INVALID_PARAMETER)
/* If WaitForKeyEx fails here, the firmware pretends it talks this
* protocol, but it really doesn't. */
EFI_STATUS err;
EFI_GRAPHICS_OUTPUT_PROTOCOL *go;
- err = LibLocateProtocol(&GraphicsOutputProtocol, (void **) &go);
+ err = BS->LocateProtocol(&GraphicsOutputProtocol, NULL, (void **) &go);
if (EFI_ERROR(err))
return err;
if (*cpio_buffer_size > UINTN_MAX - l) /* overflow check */
return EFI_OUT_OF_RESOURCES;
- a = xreallocate_pool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
+ a = xrealloc(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
*cpio_buffer = a;
a = (CHAR8*) *cpio_buffer + *cpio_buffer_size;
if (*cpio_buffer_size > UINTN_MAX - l) /* overflow check */
return EFI_OUT_OF_RESOURCES;
- *cpio_buffer = a = xreallocate_pool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
+ *cpio_buffer = a = xrealloc(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
a = (CHAR8*) *cpio_buffer + *cpio_buffer_size;
memcpy(a, "070701", 6); /* magic ID */
assert(cpio_buffer_size);
assert_cc(sizeof(trailer) % 4 == 0);
- *cpio_buffer = xreallocate_pool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + sizeof(trailer));
+ *cpio_buffer = xrealloc(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + sizeof(trailer));
memcpy((UINT8*) *cpio_buffer + *cpio_buffer_size, trailer, sizeof(trailer));
*cpio_buffer_size += sizeof(trailer);
return log_oom();
m = n_items + 16;
- items = xreallocate_pool(items, n_allocated * sizeof(UINT16*), m * sizeof(UINT16*));
+ items = xrealloc(items, n_allocated * sizeof(UINT16*), m * sizeof(UINT16*));
n_allocated = m;
}
#define FDT_V1_SIZE (7*4)
+static void *get_dtb_table(void) {
+ for (UINTN i = 0; i < ST->NumberOfTableEntries; i++)
+ if (memcmp(&EfiDtbTableGuid, &ST->ConfigurationTable[i].VendorGuid, sizeof(EfiDtbTableGuid)) == 0)
+ return ST->ConfigurationTable[i].VendorTable;
+ return NULL;
+}
+
static EFI_STATUS devicetree_allocate(struct devicetree_state *state, UINTN size) {
UINTN pages = DIV_ROUND_UP(size, EFI_PAGE_SIZE);
EFI_STATUS err;
assert(state);
- err = LibLocateProtocol(&EfiDtFixupProtocol, (void **)&fixup);
+ err = BS->LocateProtocol(&EfiDtFixupProtocol, NULL, (void **) &fixup);
if (EFI_ERROR(err))
return log_error_status_stall(EFI_SUCCESS,
L"Could not locate device tree fixup protocol, skipping.");
assert(root_dir);
assert(name);
- err = LibGetSystemConfigurationTable(&EfiDtbTableGuid, &state->orig);
- if (EFI_ERROR(err))
+ state->orig = get_dtb_table();
+ if (!state->orig)
return EFI_UNSUPPORTED;
err = root_dir->Open(root_dir, &handle, name, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY);
assert(state);
assert(dtb_buffer && dtb_length > 0);
- err = LibGetSystemConfigurationTable(&EfiDtbTableGuid, &state->orig);
- if (EFI_ERROR(err))
+ state->orig = get_dtb_table();
+ if (!state->orig)
return EFI_UNSUPPORTED;
err = devicetree_allocate(state, dtb_length);
#include "util.h"
EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[static 37]) {
- EFI_DEVICE_PATH *device_path;
- _cleanup_freepool_ EFI_DEVICE_PATH *paths = NULL;
-
- if (!handle)
- return EFI_NOT_FOUND;
+ EFI_STATUS err;
+ EFI_DEVICE_PATH *dp;
/* export the device path this image is started from */
- device_path = DevicePathFromHandle(handle);
- if (!device_path)
+
+ if (!handle)
return EFI_NOT_FOUND;
- paths = UnpackDevicePath(device_path);
- for (EFI_DEVICE_PATH *path = paths; !IsDevicePathEnd(path); path = NextDevicePathNode(path)) {
- HARDDRIVE_DEVICE_PATH *drive;
+ err = BS->HandleProtocol(handle, &DevicePathProtocol, (void **) &dp);
+ if (err != EFI_SUCCESS)
+ return err;
- if (DevicePathType(path) != MEDIA_DEVICE_PATH)
+ for (; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp)) {
+ if (DevicePathType(dp) != MEDIA_DEVICE_PATH)
continue;
- if (DevicePathSubType(path) != MEDIA_HARDDRIVE_DP)
+ if (DevicePathSubType(dp) != MEDIA_HARDDRIVE_DP)
continue;
- drive = (HARDDRIVE_DEVICE_PATH *)path;
- if (drive->SignatureType != SIGNATURE_TYPE_GUID)
+
+ HARDDRIVE_DEVICE_PATH *hd = (HARDDRIVE_DEVICE_PATH *) dp;
+ if (hd->SignatureType != SIGNATURE_TYPE_GUID)
continue;
- GuidToString(uuid, (EFI_GUID *)&drive->Signature);
+ /* Use memcpy in case the device path node is misaligned. */
+ EFI_GUID sig;
+ memcpy(&sig, hd->Signature, sizeof(hd->Signature));
+
+ GuidToString(uuid, &sig);
return EFI_SUCCESS;
}
assert(fname);
spath = xpool_print(L"\\EFI\\systemd\\drivers\\%s", fname);
- path = FileDevicePath(loaded_image->DeviceHandle, spath);
- if (!path)
- return log_oom();
+ err = make_file_device_path(loaded_image->DeviceHandle, spath, &path);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Error making file device path: %r", err);
err = BS->LoadImage(FALSE, parent_image, path, NULL, 0, &image);
if (EFI_ERROR(err))
#ifdef SD_BOOT
# include "util.h"
-# define xmalloc(n) xallocate_pool(n)
#else
# include <stdlib.h>
# include "macro.h"
DEFINE_STRNDUP(char, xstrndup8, strnlen8);
DEFINE_STRNDUP(char16_t, xstrndup16, strnlen16);
+/* Patterns are fnmatch-compatible (with reduced feature support). */
+static bool efi_fnmatch_internal(const char16_t *p, const char16_t *h, int max_depth) {
+ assert(p);
+ assert(h);
+
+ if (max_depth == 0)
+ return false;
+
+ for (;; p++, h++)
+ switch (*p) {
+ case '\0':
+ /* End of pattern. Check that haystack is now empty. */
+ return *h == '\0';
+
+ case '\\':
+ p++;
+ if (*p == '\0' || *p != *h)
+ /* Trailing escape or no match. */
+ return false;
+ break;
+
+ case '?':
+ if (*h == '\0')
+ /* Early end of haystack. */
+ return false;
+ break;
+
+ case '*':
+ /* No need to recurse for consecutive '*'. */
+ while (*p == '*')
+ p++;
+
+ for (; *h != '\0'; h++)
+ /* Try matching haystack with remaining pattern. */
+ if (efi_fnmatch_internal(p, h, max_depth - 1))
+ return true;
+
+ /* End of haystack. Pattern needs to be empty too for a match. */
+ return *p == '\0';
+
+ case '[':
+ if (*h == '\0')
+ /* Early end of haystack. */
+ return false;
+
+ bool first = true, can_range = true, match = false;
+ for (;; first = false) {
+ p++;
+ if (*p == '\0')
+ return false;
+
+ if (*p == '\\') {
+ p++;
+ if (*p == '\0')
+ return false;
+ match |= *p == *h;
+ can_range = true;
+ continue;
+ }
+
+ /* End of set unless it's the first char. */
+ if (*p == ']' && !first)
+ break;
+
+ /* Range pattern if '-' is not first or last in set. */
+ if (*p == '-' && can_range && !first && *(p + 1) != ']') {
+ char16_t low = *(p - 1);
+ p++;
+ if (*p == '\\')
+ p++;
+ if (*p == '\0')
+ return false;
+
+ if (low <= *h && *h <= *p)
+ match = true;
+
+ /* Ranges cannot be chained: [a-c-f] == [-abcf] */
+ can_range = false;
+ continue;
+ }
+
+ if (*p == *h)
+ match = true;
+ can_range = true;
+ }
+
+ if (!match)
+ return false;
+ break;
+
+ default:
+ if (*p != *h)
+ /* Single char mismatch. */
+ return false;
+ }
+}
+
+bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack) {
+ return efi_fnmatch_internal(pattern, haystack, 32);
+}
+
int efi_memcmp(const void *p1, const void *p2, size_t n) {
const uint8_t *up1 = p1, *up2 = p2;
int r;
return xstrndup16(s, SIZE_MAX);
}
+bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack);
+
#ifdef SD_BOOT
/* The compiler normally has knowledge about standard functions such as memcmp, but this is not the case when
* compiling with -ffreestanding. By referring to builtins, the compiler can check arguments and do
BOOLEAN stdin_locked;
EFI_STATUS err;
- err = LibLocateProtocol((EFI_GUID*) EFI_CONSOLE_CONTROL_GUID, (void **)&ConsoleControl);
+ err = BS->LocateProtocol((EFI_GUID *) EFI_CONSOLE_CONTROL_GUID, NULL, (void **) &ConsoleControl);
if (EFI_ERROR(err))
/* console control protocol is nonstandard and might not exist. */
return err == EFI_NOT_FOUND ? EFI_SUCCESS : err;
&EfiLoadFile2Protocol, loader,
NULL);
if (EFI_ERROR(err))
- FreePool(loader);
+ free(loader);
return err;
}
return err;
initrd_handle = NULL;
- FreePool(loader);
+ free(loader);
return EFI_SUCCESS;
}
assert(description);
desc_len = strsize16(description);
- tcg_event = xallocate_zero_pool(offsetof(TCG_PCR_EVENT, Event) + desc_len);
+ tcg_event = xmalloc(offsetof(TCG_PCR_EVENT, Event) + desc_len);
+ memset(tcg_event, 0, offsetof(TCG_PCR_EVENT, Event) + desc_len);
*tcg_event = (TCG_PCR_EVENT) {
.EventSize = desc_len,
.PCRIndex = pcrindex,
assert(description);
desc_len = strsize16(description);
- tcg_event = xallocate_zero_pool(offsetof(EFI_TCG2_EVENT, Event) + desc_len);
+ tcg_event = xmalloc(offsetof(EFI_TCG2_EVENT, Event) + desc_len);
+ memset(tcg_event, 0, offsetof(EFI_TCG2_EVENT, Event) + desc_len);
*tcg_event = (EFI_TCG2_EVENT) {
.Size = offsetof(EFI_TCG2_EVENT, Event) + desc_len,
.Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER),
UINT32 features;
EFI_TCG *tcg;
- status = LibLocateProtocol((EFI_GUID*) EFI_TCG_GUID, (void **) &tcg);
+ status = BS->LocateProtocol((EFI_GUID *) EFI_TCG_GUID, NULL, (void **) &tcg);
if (EFI_ERROR(status))
return NULL;
EFI_STATUS status;
EFI_TCG2 *tcg;
- status = LibLocateProtocol((EFI_GUID*) EFI_TCG2_GUID, (void **) &tcg);
+ status = BS->LocateProtocol((EFI_GUID *) EFI_TCG2_GUID, NULL, (void **) &tcg);
if (EFI_ERROR(status))
return NULL;
return EFI_LOAD_ERROR;
section_table_len = pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
- section_table = xallocate_pool(section_table_len);
+ section_table = xmalloc(section_table_len);
if (!section_table)
return EFI_OUT_OF_RESOURCES;
/* Try to acquire the specified number of bytes from the UEFI RNG */
- err = LibLocateProtocol((EFI_GUID*) EFI_RNG_GUID, (void**) &rng);
+ err = BS->LocateProtocol((EFI_GUID *) EFI_RNG_GUID, NULL, (void **) &rng);
if (EFI_ERROR(err))
return err;
if (!rng)
return EFI_UNSUPPORTED;
- data = xallocate_pool(size);
+ data = xmalloc(size);
err = rng->GetRNG(rng, NULL, size, data);
if (EFI_ERROR(err))
/* Hashes the specified parameters in counter mode, generating n hash values, with the counter in the
* range counter_start…counter_start+n-1. */
- output = xallocate_pool(n * HASH_VALUE_SIZE);
+ output = xmalloc_multiply(HASH_VALUE_SIZE, n);
for (UINTN i = 0; i < n; i++)
hash_once(old_seed, rng, size,
if (size > RANDOM_MAX_SIZE_MAX)
return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too large.");
- seed = xallocate_pool(size);
+ seed = xmalloc(size);
rsize = size;
err = handle->Read(handle, &rsize, seed);
static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROTOCOL *this, UINT32 authentication_status,
const EFI_DEVICE_PATH_PROTOCOL *device_path_const) {
EFI_STATUS status;
- _cleanup_freepool_ EFI_DEVICE_PATH *dev_path = NULL;
_cleanup_freepool_ CHAR16 *dev_path_str = NULL;
EFI_HANDLE h;
_cleanup_freepool_ CHAR8 *file_buffer = NULL;
if (!device_path_const)
return EFI_INVALID_PARAMETER;
- dev_path = DuplicateDevicePath((EFI_DEVICE_PATH*) device_path_const);
- if (!dev_path)
- return EFI_OUT_OF_RESOURCES;
-
- EFI_DEVICE_PATH *dp = dev_path;
+ EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) device_path_const;
status = BS->LocateDevicePath(&FileSystemProtocol, &dp, &h);
if (EFI_ERROR(status))
return status;
- _cleanup_(file_closep) EFI_FILE *root = LibOpenRoot(h);
- if (!root)
- return EFI_NOT_FOUND;
+ _cleanup_(file_closep) EFI_FILE *root = NULL;
+ status = open_volume(h, &root);
+ if (status != EFI_SUCCESS)
+ return status;
dev_path_str = DevicePathToStr(dp);
if (!dev_path_str)
background = &pixel;
}
- err = LibLocateProtocol(&GraphicsOutputProtocol, (void **)&GraphicsOutput);
+ err = BS->LocateProtocol(&GraphicsOutputProtocol, NULL, (void **) &GraphicsOutput);
if (EFI_ERROR(err))
return err;
if ((!secure_boot_enabled() || cmdline_len == 0) && loaded_image->LoadOptionsSize > 0 &&
*(CHAR16 *) loaded_image->LoadOptions > 0x1F) {
cmdline_len = (loaded_image->LoadOptionsSize / sizeof(CHAR16)) * sizeof(CHAR8);
- cmdline = cmdline_owned = xallocate_pool(cmdline_len);
+ cmdline = cmdline_owned = xmalloc(cmdline_len);
for (UINTN i = 0; i < cmdline_len; i++)
cmdline[i] = ((CHAR16 *) loaded_image->LoadOptions)[i];
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <fnmatch.h>
+
#include "efi-string.h"
#include "tests.h"
free(s);
}
+#define TEST_FNMATCH_ONE(pattern, haystack, expect) \
+ ({ \
+ assert_se(fnmatch(pattern, haystack, 0) == (expect ? 0 : FNM_NOMATCH)); \
+ assert_se(efi_fnmatch(u##pattern, u##haystack) == expect); \
+ })
+
+TEST(efi_fnmatch) {
+ TEST_FNMATCH_ONE("", "", true);
+ TEST_FNMATCH_ONE("abc", "abc", true);
+ TEST_FNMATCH_ONE("aBc", "abc", false);
+ TEST_FNMATCH_ONE("b", "a", false);
+ TEST_FNMATCH_ONE("b", "", false);
+ TEST_FNMATCH_ONE("abc", "a", false);
+ TEST_FNMATCH_ONE("a?c", "azc", true);
+ TEST_FNMATCH_ONE("???", "?.9", true);
+ TEST_FNMATCH_ONE("1?", "1", false);
+ TEST_FNMATCH_ONE("***", "", true);
+ TEST_FNMATCH_ONE("*", "123", true);
+ TEST_FNMATCH_ONE("**", "abcd", true);
+ TEST_FNMATCH_ONE("*b*", "abcd", true);
+ TEST_FNMATCH_ONE("abc*d", "abc", false);
+ TEST_FNMATCH_ONE("*.conf", "arch.conf", true);
+ TEST_FNMATCH_ONE("debian-*.conf", "debian-wheezy.conf", true);
+ TEST_FNMATCH_ONE("debian-*.*", "debian-wheezy.efi", true);
+ TEST_FNMATCH_ONE("ab*cde", "abzcd", false);
+ TEST_FNMATCH_ONE("\\*\\a\\[", "*a[", true);
+ TEST_FNMATCH_ONE("[abc] [abc] [abc]", "a b c", true);
+ TEST_FNMATCH_ONE("abc]", "abc]", true);
+ TEST_FNMATCH_ONE("[abc]", "z", false);
+ TEST_FNMATCH_ONE("[abc", "a", false);
+ TEST_FNMATCH_ONE("[][!] [][!] [][!]", "[ ] !", true);
+ TEST_FNMATCH_ONE("[]-] []-]", "] -", true);
+ TEST_FNMATCH_ONE("[1\\]] [1\\]]", "1 ]", true);
+ TEST_FNMATCH_ONE("[$-\\+]", "&", true);
+ TEST_FNMATCH_ONE("[1-3A-C] [1-3A-C]", "2 B", true);
+ TEST_FNMATCH_ONE("[3-5] [3-5] [3-5]", "3 4 5", true);
+ TEST_FNMATCH_ONE("[f-h] [f-h] [f-h]", "f g h", true);
+ TEST_FNMATCH_ONE("[a-c-f] [a-c-f] [a-c-f] [a-c-f] [a-c-f]", "a b c - f", true);
+ TEST_FNMATCH_ONE("[a-c-f]", "e", false);
+ TEST_FNMATCH_ONE("[--0] [--0] [--0]", "- . 0", true);
+ TEST_FNMATCH_ONE("[+--] [+--] [+--]", "+ , -", true);
+ TEST_FNMATCH_ONE("[f-l]", "m", false);
+ TEST_FNMATCH_ONE("[b]", "z-a", false);
+ TEST_FNMATCH_ONE("[a\\-z]", "b", false);
+ TEST_FNMATCH_ONE("?a*b[.-0]c", "/a/b/c", true);
+}
+
TEST(efi_memcmp) {
assert_se(efi_memcmp(NULL, NULL, 0) == 0);
assert_se(efi_memcmp(NULL, NULL, 1) == 0);
}
/* Make sure a terminating NUL is available at the end */
- val = xallocate_pool(size + sizeof(CHAR16));
+ val = xmalloc(size + sizeof(CHAR16));
memcpy(val, buf, size);
val[size / sizeof(CHAR16) - 1] = 0; /* NUL terminate */
assert(name);
l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
- buf = xallocate_pool(l);
+ buf = xmalloc(l);
err = RT->GetVariable((CHAR16 *) name, (EFI_GUID *) vendor, NULL, &l, buf);
if (!EFI_ERROR(err)) {
/* Allocate some extra bytes to guarantee the result is NUL-terminated for CHAR8 and CHAR16 strings. */
UINTN extra = size % sizeof(CHAR16) + sizeof(CHAR16);
- buf = xallocate_pool(size + extra);
+ buf = xmalloc(size + extra);
err = handle->Read(handle, &size, buf);
if (EFI_ERROR(err))
return err;
/* A lot like LibFileInfo() but with useful error propagation */
- fi = xallocate_pool(size);
+ fi = xmalloc(size);
err = handle->GetInfo(handle, &GenericFileInfo, &size, fi);
if (err == EFI_BUFFER_TOO_SMALL) {
- FreePool(fi);
- fi = xallocate_pool(size); /* GetInfo tells us the required size, let's use that now */
+ free(fi);
+ fi = xmalloc(size); /* GetInfo tells us the required size, let's use that now */
err = handle->GetInfo(handle, &GenericFileInfo, &size, fi);
}
* file name length.
* As a side effect, most readdir_harder() calls will now be slightly faster. */
sz = sizeof(EFI_FILE_INFO) + 256 * sizeof(CHAR16);
- *buffer = xallocate_pool(sz);
+ *buffer = xmalloc(sz);
*buffer_size = sz;
} else
sz = *buffer_size;
err = handle->Read(handle, &sz, *buffer);
if (err == EFI_BUFFER_TOO_SMALL) {
- FreePool(*buffer);
- *buffer = xallocate_pool(sz);
+ free(*buffer);
+ *buffer = xmalloc(sz);
*buffer_size = sz;
err = handle->Read(handle, &sz, *buffer);
}
if (sz == 0) {
/* End of directory */
- FreePool(*buffer);
+ free(*buffer);
*buffer = NULL;
*buffer_size = 0;
}
return NULL;
for (CHAR16 **i = v; *i; i++)
- FreePool(*i);
+ free(*i);
- FreePool(v);
+ free(v);
return NULL;
}
}
}
#endif
+
+EFI_STATUS open_volume(EFI_HANDLE device, EFI_FILE **ret_file) {
+ EFI_STATUS err;
+ EFI_FILE *file;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *volume;
+
+ assert(ret_file);
+
+ err = BS->HandleProtocol(device, &FileSystemProtocol, (void **) &volume);
+ if (err != EFI_SUCCESS)
+ return err;
+
+ err = volume->OpenVolume(volume, &file);
+ if (err != EFI_SUCCESS)
+ return err;
+
+ *ret_file = file;
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp) {
+ EFI_STATUS err;
+ EFI_DEVICE_PATH *dp;
+
+ assert(file);
+ assert(ret_dp);
+
+ err = BS->HandleProtocol(device, &DevicePathProtocol, (void **) &dp);
+ if (err != EFI_SUCCESS)
+ return err;
+
+ EFI_DEVICE_PATH *end_node = dp;
+ while (!IsDevicePathEnd(end_node))
+ end_node = NextDevicePathNode(end_node);
+
+ size_t file_size = strsize16(file);
+ size_t dp_size = ((uint8_t *) end_node - (uint8_t *) dp) + END_DEVICE_PATH_LENGTH;
+
+ /* Make a copy that can also hold a file media device path. */
+ *ret_dp = xmalloc(dp_size + file_size + SIZE_OF_FILEPATH_DEVICE_PATH);
+ memcpy(*ret_dp, dp, dp_size);
+
+ /* Point dp to the end node of the copied device path. */
+ dp = (EFI_DEVICE_PATH *) ((uint8_t *) *ret_dp + dp_size - END_DEVICE_PATH_LENGTH);
+
+ /* Replace end node with file media device path. */
+ FILEPATH_DEVICE_PATH *file_dp = (FILEPATH_DEVICE_PATH *) dp;
+ file_dp->Header.Type = MEDIA_DEVICE_PATH;
+ file_dp->Header.SubType = MEDIA_FILEPATH_DP;
+ memcpy(&file_dp->PathName, file, file_size);
+ SetDevicePathNodeLength(&file_dp->Header, SIZE_OF_FILEPATH_DEVICE_PATH + file_size);
+
+ dp = NextDevicePathNode(dp);
+ SetDevicePathEndNode(dp);
+ return EFI_SUCCESS;
+}
# error "Unexpected pointer size"
#endif
-#define xnew_alloc(type, n, alloc) \
- ({ \
- UINTN _alloc_size; \
- assert_se(!__builtin_mul_overflow(sizeof(type), (n), &_alloc_size)); \
- (type *) alloc(_alloc_size); \
- })
+static inline void free(void *p) {
+ if (!p)
+ return;
+
+ /* Debugging an invalid free requires trace logging to find the call site or a debugger attached. For
+ * release builds it is not worth the bother to even warn when we cannot even print a call stack. */
+#ifdef EFI_DEBUG
+ assert_se(BS->FreePool(p) == EFI_SUCCESS);
+#else
+ (void) BS->FreePool(p);
+#endif
+}
+
+static inline void freep(void *p) {
+ free(*(void **) p);
+}
+
+#define _cleanup_freepool_ _cleanup_free_
+#define _cleanup_free_ _cleanup_(freep)
+
+_malloc_ _alloc_(1) _returns_nonnull_ _warn_unused_result_
+static inline void *xmalloc(size_t size) {
+ void *p;
+ assert_se(BS->AllocatePool(EfiBootServicesData, size, &p) == EFI_SUCCESS);
+ return p;
+}
+
+_malloc_ _alloc_(1, 2) _returns_nonnull_ _warn_unused_result_
+static inline void *xmalloc_multiply(size_t size, size_t n) {
+ assert_se(!__builtin_mul_overflow(size, n, &size));
+ return xmalloc(size);
+}
+
+/* Use malloc attribute as this never returns p like userspace realloc. */
+_malloc_ _alloc_(3) _returns_nonnull_ _warn_unused_result_
+static inline void *xrealloc(void *p, size_t old_size, size_t new_size) {
+ void *r = xmalloc(new_size);
+ memcpy(r, p, MIN(old_size, new_size));
+ free(p);
+ return r;
+}
-#define xallocate_pool(size) ASSERT_SE_PTR(AllocatePool(size))
-#define xallocate_zero_pool(size) ASSERT_SE_PTR(AllocateZeroPool(size))
-#define xreallocate_pool(p, old_size, new_size) ASSERT_SE_PTR(ReallocatePool((p), (old_size), (new_size)))
#define xpool_print(fmt, ...) ((CHAR16 *) ASSERT_SE_PTR(PoolPrint((fmt), ##__VA_ARGS__)))
-#define xnew(type, n) xnew_alloc(type, (n), xallocate_pool)
-#define xnew0(type, n) xnew_alloc(type, (n), xallocate_zero_pool)
+#define xnew(type, n) ((type *) xmalloc_multiply(sizeof(type), (n)))
EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b);
EFI_STATUS file_read(EFI_FILE *dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size);
-static inline void free_poolp(void *p) {
- void *q = *(void**) p;
-
- if (!q)
- return;
-
- (void) BS->FreePool(q);
-}
-
-#define _cleanup_freepool_ _cleanup_(free_poolp)
-
static inline void file_closep(EFI_FILE **handle) {
if (!*handle)
return;
#else
static inline void beep(UINTN beep_count) {}
#endif
+
+EFI_STATUS open_volume(EFI_HANDLE device, EFI_FILE **ret_file);
+EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp);
assert(node);
UINTN len = (UINT8 *) node - (UINT8 *) path;
- EFI_DEVICE_PATH *chopped = xallocate_pool(len + END_DEVICE_PATH_LENGTH);
+ EFI_DEVICE_PATH *chopped = xmalloc(len + END_DEVICE_PATH_LENGTH);
memcpy(chopped, path, len);
SetDevicePathEndNode((EFI_DEVICE_PATH *) ((UINT8 *) chopped + len));
return chopped;
}
+static EFI_DEVICE_PATH *path_dup(const EFI_DEVICE_PATH *dp) {
+ assert(dp);
+
+ const EFI_DEVICE_PATH *node = dp;
+ size_t size = 0;
+ while (!IsDevicePathEnd(node)) {
+ size += DevicePathNodeLength(node);
+ node = NextDevicePathNode(node);
+ }
+ size += DevicePathNodeLength(node);
+
+ EFI_DEVICE_PATH *dup = xmalloc(size);
+ memcpy(dup, dp, size);
+ return dup;
+}
+
static BOOLEAN verify_gpt(union GptHeaderBuffer *gpt_header_buffer, EFI_LBA lba_expected) {
EFI_PARTITION_TABLE_HEADER *h;
UINT32 crc32, crc32_saved;
/* Now load the GPT entry table */
size = ALIGN_TO((UINTN) gpt.gpt_header.SizeOfPartitionEntry * (UINTN) gpt.gpt_header.NumberOfPartitionEntries, 512);
- entries = xallocate_pool(size);
+ entries = xmalloc(size);
err = block_io->ReadBlocks(
block_io,
assert(device);
assert(ret_device_path);
- EFI_DEVICE_PATH *partition_path = DevicePathFromHandle(device);
- if (!partition_path)
- return EFI_NOT_FOUND;
+ EFI_DEVICE_PATH *partition_path;
+ err = BS->HandleProtocol(device, &DevicePathProtocol, (void **) &partition_path);
+ if (err != EFI_SUCCESS)
+ return err;
/* Find the (last) partition node itself. */
EFI_DEVICE_PATH *part_node = NULL;
}
/* Patch in the data we found */
- EFI_DEVICE_PATH *xboot_path = ASSERT_SE_PTR(DuplicateDevicePath(partition_path));
+ EFI_DEVICE_PATH *xboot_path = path_dup(partition_path);
memcpy((UINT8 *) xboot_path + ((UINT8 *) part_node - (UINT8 *) partition_path), &hd, sizeof(hd));
*ret_device_path = xboot_path;
return EFI_SUCCESS;
assert(ret_root_dir);
err = find_device(device, &partition_path);
- if (EFI_ERROR(err))
+ if (err != EFI_SUCCESS)
return err;
EFI_DEVICE_PATH *dp = partition_path;
err = BS->LocateDevicePath(&BlockIoProtocol, &dp, &new_device);
- if (EFI_ERROR(err))
+ if (err != EFI_SUCCESS)
return err;
- root_dir = LibOpenRoot(new_device);
- if (!root_dir)
- return EFI_NOT_FOUND;
+ err = open_volume(new_device, &root_dir);
+ if (err != EFI_SUCCESS)
+ return err;
*ret_device = new_device;
*ret_root_dir = root_dir;
subdir_done()
endif
-clang_flags = [
+bpf_clang_flags = [
'-Wno-compare-distinct-pointer-types',
'-O2',
'-target',
'-c',
]
+bpf_gcc_flags = [
+ '-O2',
+ '-mkernel=5.2',
+ '-mcpu=v3',
+ '-mco-re',
+ '-gbtf',
+]
+
# Generate defines that are appropriate to tell the compiler what architecture
# we're compiling for. By default we just map meson's cpu_family to __<cpu_family>__.
# This dictionary contains the exceptions where this doesn't work.
'arm' : ['-D__arm__', '-D__ARM_PCS_VFP'],
}
-clang_arch_flags = cpu_arch_defines.get(host_machine.cpu_family(),
- ['-D__@0@__'.format(host_machine.cpu_family())])
+bpf_arch_flags = cpu_arch_defines.get(host_machine.cpu_family(),
+ ['-D__@0@__'.format(host_machine.cpu_family())])
+if bpf_compiler == 'gcc'
+ bpf_arch_flags += ['-m' + host_machine.endian() + '-endian']
+endif
libbpf_include_dir = libbpf.get_variable(pkgconfig : 'includedir')
-bpf_o_unstripped_cmd = [
- clang,
- clang_flags,
- clang_arch_flags,
- '-I.'
-]
+bpf_o_unstripped_cmd = []
+if bpf_compiler == 'clang'
+ bpf_o_unstripped_cmd += [
+ clang,
+ bpf_clang_flags,
+ bpf_arch_flags,
+ ]
+elif bpf_compiler == 'gcc'
+ bpf_o_unstripped_cmd += [
+ bpf_gcc,
+ bpf_gcc_flags,
+ bpf_arch_flags,
+ ]
+endif
+
+bpf_o_unstripped_cmd += ['-I.']
-if not meson.is_cross_build()
+if not meson.is_cross_build() and bpf_compiler == 'clang'
target_triplet_cmd = run_command('gcc', '-dumpmachine', check: false)
if target_triplet_cmd.returncode() == 0
target_triplet = target_triplet_cmd.stdout().strip()
'@OUTPUT@',
'@INPUT@'
]
-else
+elif bpf_compiler == 'clang'
bpf_o_cmd = [
llvm_strip,
'-g',
#define DROP 0
#define PASS 1
-static inline int restrict_network_interfaces_impl(const struct __sk_buff *sk) {
+static __always_inline int restrict_network_interfaces_impl(const struct __sk_buff *sk) {
__u32 zero = 0, ifindex;
__u8 *lookup_result;
assert(ret_uid);
assert(ret_lock_fd);
- /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with the lock
- * on the socket taken. */
+ /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with
+ * the lock on the socket taken. */
k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd);
if (k < 0)
assert(d);
- /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the storage socket, and pushes it back in right-away. */
+ /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the
+ * storage socket, and pushes it back in right-away. */
r = lockfp(d->storage_socket[0], &storage_socket0_lock);
if (r < 0)
if (!d)
return NULL;
- /* Note that this doesn't actually release any resources itself. If a dynamic user should be fully destroyed
- * and its UID released, use dynamic_user_destroy() instead. NB: the dynamic user table may contain entries
- * with no references, which is commonly the case right before a daemon reload. */
+ /* Note that this doesn't actually release any resources itself. If a dynamic user should be fully
+ * destroyed and its UID released, use dynamic_user_destroy() instead. NB: the dynamic user table may
+ * contain entries with no references, which is commonly the case right before a daemon reload. */
assert(d->n_ref > 0);
d->n_ref--;
uid_t uid;
int r;
- /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the user is
- * unrealized again, much like it was after it the DynamicUser object was first allocated. */
+ /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the
+ * user is unrealized again, much like it was after it the DynamicUser object was first allocated. */
r = lockfp(d->storage_socket[0], &storage_socket0_lock);
if (r < 0)
#define _public_ __attribute__((__visibility__("default")))
#define _pure_ __attribute__((__pure__))
#define _retain_ __attribute__((__retain__))
+#define _returns_nonnull_ __attribute__((__returns_nonnull__))
#define _section_(x) __attribute__((__section__(x)))
#define _sentinel_ __attribute__((__sentinel__))
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
#endif
#define static_assert _Static_assert
#define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
-
- #define free(a) FreePool(a)
#endif
/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
/* We first check the environment. The environment variable is primarily relevant for user
* services, and sufficiently safe as long as no privilege boundary is involved. */
r = get_invocation_from_environment(&saved_invocation_id);
- if (r < 0 && r != -ENXIO)
+ if (r >= 0) {
+ *ret = saved_invocation_id;
+ return 0;
+ } else if (r != -ENXIO)
return r;
/* The kernel keyring is relevant for system services (as for user services we don't store
return 1;
}
-int bus_connect_system_systemd(sd_bus **_bus) {
+int bus_connect_system_systemd(sd_bus **ret_bus) {
_cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
int r;
- assert(_bus);
+ assert(ret_bus);
if (geteuid() != 0)
- return sd_bus_default_system(_bus);
+ return sd_bus_default_system(ret_bus);
/* If we are root then let's talk directly to the system
* instance, instead of going via the bus */
r = sd_bus_start(bus);
if (r < 0)
- return sd_bus_default_system(_bus);
+ return sd_bus_default_system(ret_bus);
r = bus_check_peercred(bus);
if (r < 0)
return r;
- *_bus = TAKE_PTR(bus);
-
+ *ret_bus = TAKE_PTR(bus);
return 0;
}
-int bus_connect_user_systemd(sd_bus **_bus) {
+int bus_connect_user_systemd(sd_bus **ret_bus) {
_cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *ee = NULL;
const char *e;
int r;
- assert(_bus);
+ assert(ret_bus);
e = secure_getenv("XDG_RUNTIME_DIR");
if (!e)
- return sd_bus_default_user(_bus);
+ return sd_bus_default_user(ret_bus);
ee = bus_address_escape(e);
if (!ee)
r = sd_bus_start(bus);
if (r < 0)
- return sd_bus_default_user(_bus);
+ return sd_bus_default_user(ret_bus);
r = bus_check_peercred(bus);
if (r < 0)
return r;
- *_bus = TAKE_PTR(bus);
-
+ *ret_bus = TAKE_PTR(bus);
return 0;
}
}
int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
- int r;
-
assert(transport >= 0);
assert(transport < _BUS_TRANSPORT_MAX);
assert(bus);
case BUS_TRANSPORT_LOCAL:
if (user)
- r = bus_connect_user_systemd(bus);
- else {
- if (sd_booted() <= 0)
- /* Print a friendly message when the local system is actually not running systemd as PID 1. */
- return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
- "System has not been booted with systemd as init system (PID 1). Can't operate.");
- r = bus_connect_system_systemd(bus);
- }
- break;
+ return bus_connect_user_systemd(bus);
+
+ if (sd_booted() <= 0)
+ /* Print a friendly message when the local system is actually not running systemd as PID 1. */
+ return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
+ "System has not been booted with systemd as init system (PID 1). Can't operate.");
+ return bus_connect_system_systemd(bus);
case BUS_TRANSPORT_REMOTE:
- r = sd_bus_open_system_remote(bus, host);
- break;
+ return sd_bus_open_system_remote(bus, host);
case BUS_TRANSPORT_MACHINE:
- r = sd_bus_open_system_machine(bus, host);
- break;
+ return sd_bus_open_system_machine(bus, host);
default:
assert_not_reached();
}
-
- return r;
}
/**
int bus_check_peercred(sd_bus *c);
-int bus_connect_system_systemd(sd_bus **_bus);
-int bus_connect_user_systemd(sd_bus **_bus);
+int bus_connect_system_systemd(sd_bus **ret_bus);
+int bus_connect_user_systemd(sd_bus **ret_bus);
int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
r = nft_set_element_op_in_addr(nfnl, table, set,
add, nfproto, af, address, prefixlen);
- log_debug("%s NFT family %s table %s set %s IP addresss %s",
+ log_debug("%s NFT family %s table %s set %s IP address %s",
add ? "Added" : "Deleted",
nfproto_to_string(nfproto), table, set,
IN_ADDR_PREFIX_TO_STRING(af, address, prefixlen));
assert(path);
if (!sym_fido_dev_is_fido2(d))
- return log_full_errno(log_level,
- SYNTHETIC_ERRNO(ENODEV),
- "Specified device %s is not a FIDO2 device.", path);
+ return log_full_errno(log_level, SYNTHETIC_ERRNO(ENODEV),
+ "Specified device %s is not a FIDO2 device.", path);
di = sym_fido_cbor_info_new();
if (!di)
}
int main(int argc, char *argv[]) {
- bool need_umount, need_swapoff, need_loop_detach, need_dm_detach, need_md_detach, in_container, can_initrd;
_cleanup_free_ char *cgroup = NULL;
char *arguments[3];
- int cmd, r, umount_log_level = LOG_INFO;
+ int cmd, r;
static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
/* The log target defaults to console, but the original systemd process will pass its log target in through a
}
(void) cg_get_root_path(&cgroup);
- in_container = detect_container() > 0;
+ bool in_container = detect_container() > 0;
/* If the logging messages are going to KMSG, and if we are not running from a container, then try to
* update the sysctl kernel.printk current value in order to see "info" messages; This current log
log_info("Sending SIGKILL to remaining processes...");
broadcast_signal(SIGKILL, true, false, arg_timeout);
- need_umount = !in_container;
- need_swapoff = !in_container;
- need_loop_detach = !in_container;
- need_dm_detach = !in_container;
- need_md_detach = !in_container;
+ bool need_umount = !in_container, need_swapoff = !in_container, need_loop_detach = !in_container,
+ need_dm_detach = !in_container, need_md_detach = !in_container, can_initrd, last_try = false;
can_initrd = !in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0;
/* Unmount all mountpoints, swaps, and loopback devices */
if (need_umount) {
log_info("Unmounting file systems.");
- r = umount_all(&changed, umount_log_level);
+ r = umount_all(&changed, last_try);
if (r == 0) {
need_umount = false;
log_info("All filesystems unmounted.");
if (need_loop_detach) {
log_info("Detaching loop devices.");
- r = loopback_detach_all(&changed, umount_log_level);
+ r = loopback_detach_all(&changed, last_try);
if (r == 0) {
need_loop_detach = false;
log_info("All loop devices detached.");
if (need_md_detach) {
log_info("Stopping MD devices.");
- r = md_detach_all(&changed, umount_log_level);
+ r = md_detach_all(&changed, last_try);
if (r == 0) {
need_md_detach = false;
log_info("All MD devices stopped.");
if (need_dm_detach) {
log_info("Detaching DM devices.");
- r = dm_detach_all(&changed, umount_log_level);
+ r = dm_detach_all(&changed, last_try);
if (r == 0) {
need_dm_detach = false;
log_info("All DM devices detached.");
break;
}
- if (!changed && umount_log_level == LOG_INFO && !can_initrd) {
- /* There are things we cannot get rid of. Loop one more time
- * with LOG_ERR to inform the user. Note that we don't need
- * to do this if there is an initrd to switch to, because that
- * one is likely to get rid of the remaining mounts. If not,
- * it will log about them. */
- umount_log_level = LOG_ERR;
+ if (!changed && last_try && !can_initrd) {
+ /* There are things we cannot get rid of. Loop one more time in which we will log
+ * with higher priority to inform the user. Note that we don't need to do this if
+ * there is an initrd to switch to, because that one is likely to get rid of the
+ * remaining mounts. If not, it will log about them. */
+ last_try = true;
continue;
}
#include "blockdev-util.h"
#include "def.h"
#include "device-util.h"
+#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
#include "fstab-util.h"
#include "libmount-util.h"
#include "mount-setup.h"
#include "mount-util.h"
#include "mountpoint-util.h"
+#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "signal-util.h"
|| path_startswith(path, "/run/initramfs");
}
-static int remount_with_timeout(MountPoint *m, int umount_log_level) {
+static void log_umount_blockers(const char *mnt) {
+ _cleanup_closedir_ DIR *dir = opendir("/proc");
+ if (!dir)
+ return (void) log_warning_errno(errno, "opendir(/proc) failed: %m");
+
+ _cleanup_free_ char *blockers = NULL;
+
+ FOREACH_DIRENT_ALL(de, dir, break) {
+ if (!IN_SET(de->d_type, DT_DIR, DT_UNKNOWN))
+ continue;
+
+ pid_t pid;
+ if (parse_pid(de->d_name, &pid) < 0)
+ continue;
+
+ _cleanup_closedir_ DIR *pid_dir = xopendirat(dirfd(dir), de->d_name, 0);
+ if (!pid_dir)
+ continue;
+
+ _cleanup_closedir_ DIR *fd_dir = xopendirat(dirfd(pid_dir), "fd", 0);
+ if (!fd_dir)
+ continue;
+
+ FOREACH_DIRENT(fd_de, fd_dir, break) {
+ _cleanup_free_ char *open_file = NULL, *comm = NULL;
+
+ if (readlinkat_malloc(dirfd(fd_dir), fd_de->d_name, &open_file) < 0)
+ continue;
+
+ if (!path_startswith(open_file, mnt))
+ continue;
+
+ if (PATH_STARTSWITH_SET(open_file, "/dev", "/sys", "/proc"))
+ continue;
+
+ if (get_process_comm(pid, &comm) < 0)
+ continue;
+
+ if (!strextend_with_separator(&blockers, ", ", comm))
+ return (void) log_oom();
+
+ if (!strextend(&blockers, "(", de->d_name, ")"))
+ return (void) log_oom();
+
+ break;
+ }
+ }
+
+ if (blockers)
+ log_warning("Unmounting '%s' blocked by: %s", mnt, blockers);
+}
+
+static int remount_with_timeout(MountPoint *m, bool last_try) {
pid_t pid;
int r;
/* Start the mount operation here in the child */
r = mount(NULL, m->path, NULL, m->remount_flags, m->remount_options);
if (r < 0)
- log_full_errno(umount_log_level, errno, "Failed to remount '%s' read-only: %m", m->path);
+ log_full_errno(last_try ? LOG_ERR : LOG_INFO,
+ errno,
+ "Failed to remount '%s' read-only: %m",
+ m->path);
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
return r;
}
-static int umount_with_timeout(MountPoint *m, int umount_log_level) {
+static int umount_with_timeout(MountPoint *m, bool last_try) {
pid_t pid;
int r;
* "busy", this may allow processes to die, thus making the
* filesystem less busy so the unmount might succeed (rather
* than return EBUSY). */
- r = umount2(m->path, MNT_FORCE);
- if (r < 0)
- log_full_errno(umount_log_level, errno, "Failed to unmount %s: %m", m->path);
+ r = RET_NERRNO(umount2(m->path, MNT_FORCE));
+ if (r < 0) {
+ log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Failed to unmount %s: %m", m->path);
+
+ if (r == -EBUSY && last_try)
+ log_umount_blockers(m->path);
+
+ /* If API filesystems under /oldroot cannot be unmounted we can still lazily unmount
+ * them to unblock /oldroot. They serve no function to us anymore and should be
+ * memory-only and hence safe to unmount like this. */
+ if (in_initrd() &&
+ PATH_STARTSWITH_SET(m->path, "/oldroot/dev", "/oldroot/proc", "/oldroot/sys")) {
+ log_info("Lazily unmounting '%s' instead.", m->path);
+ r = umount2(m->path, MNT_FORCE | MNT_DETACH);
+ if (r < 0)
+ log_error_errno(errno, "Failed to lazily unmount %s: %m", m->path);
+ }
+ }
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
/* This includes remounting readonly, which changes the kernel mount options. Therefore the list passed to
* this function is invalidated, and should not be reused. */
-static int mount_points_list_umount(MountPoint **head, bool *changed, int umount_log_level) {
+static int mount_points_list_umount(MountPoint **head, bool *changed, bool last_try) {
int n_failed = 0;
assert(head);
*
* Since the remount can hang in the instance of remote filesystems, we remount
* asynchronously and skip the subsequent umount if it fails. */
- if (remount_with_timeout(m, umount_log_level) < 0) {
+ if (remount_with_timeout(m, last_try) < 0) {
/* Remount failed, but try unmounting anyway,
* unless this is a mount point we want to skip. */
if (nonunmountable_path(m->path)) {
continue;
/* Trying to umount */
- if (umount_with_timeout(m, umount_log_level) < 0)
+ if (umount_with_timeout(m, last_try) < 0)
n_failed++;
else
*changed = true;
return n_failed;
}
-static int loopback_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
+static int loopback_points_list_detach(MountPoint **head, bool *changed, bool last_try) {
int n_failed = 0, r;
dev_t rootdev = 0;
log_info("Detaching loopback %s.", m->path);
r = delete_loopback(m->path);
if (r < 0) {
- log_full_errno(umount_log_level, r, "Could not detach loopback %s: %m", m->path);
+ log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not detach loopback %s: %m", m->path);
n_failed++;
continue;
}
return n_failed;
}
-static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
+static int dm_points_list_detach(MountPoint **head, bool *changed, bool last_try) {
int n_failed = 0, r;
dev_t rootdev = 0;
log_info("Detaching DM %s (%u:%u).", m->path, major(m->devnum), minor(m->devnum));
r = delete_dm(m);
if (r < 0) {
- log_full_errno(umount_log_level, r, "Could not detach DM %s: %m", m->path);
+ log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not detach DM %s: %m", m->path);
n_failed++;
continue;
}
return n_failed;
}
-static int md_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
+static int md_points_list_detach(MountPoint **head, bool *changed, bool last_try) {
int n_failed = 0, r;
dev_t rootdev = 0;
log_info("Stopping MD %s (%u:%u).", m->path, major(m->devnum), minor(m->devnum));
r = delete_md(m);
if (r < 0) {
- log_full_errno(umount_log_level, r, "Could not stop MD %s: %m", m->path);
+ log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not stop MD %s: %m", m->path);
n_failed++;
continue;
}
return n_failed;
}
-static int umount_all_once(bool *changed, int umount_log_level) {
+static int umount_all_once(bool *changed, bool last_try) {
_cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
int r;
if (r < 0)
return r;
- return mount_points_list_umount(&mp_list_head, changed, umount_log_level);
+ return mount_points_list_umount(&mp_list_head, changed, last_try);
}
-int umount_all(bool *changed, int umount_log_level) {
+int umount_all(bool *changed, bool last_try) {
bool umount_changed;
int r;
do {
umount_changed = false;
- r = umount_all_once(&umount_changed, umount_log_level);
+ r = umount_all_once(&umount_changed, last_try);
if (umount_changed)
*changed = true;
} while (umount_changed);
return swap_points_list_off(&swap_list_head, changed);
}
-int loopback_detach_all(bool *changed, int umount_log_level) {
+int loopback_detach_all(bool *changed, bool last_try) {
_cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, loopback_list_head);
int r;
if (r < 0)
return r;
- return loopback_points_list_detach(&loopback_list_head, changed, umount_log_level);
+ return loopback_points_list_detach(&loopback_list_head, changed, last_try);
}
-int dm_detach_all(bool *changed, int umount_log_level) {
+int dm_detach_all(bool *changed, bool last_try) {
_cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, dm_list_head);
int r;
if (r < 0)
return r;
- return dm_points_list_detach(&dm_list_head, changed, umount_log_level);
+ return dm_points_list_detach(&dm_list_head, changed, last_try);
}
-int md_detach_all(bool *changed, int umount_log_level) {
+int md_detach_all(bool *changed, bool last_try) {
_cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, md_list_head);
int r;
if (r < 0)
return r;
- return md_points_list_detach(&md_list_head, changed, umount_log_level);
+ return md_points_list_detach(&md_list_head, changed, last_try);
}
#include "list.h"
-int umount_all(bool *changed, int umount_log_level);
-
+int umount_all(bool *changed, bool last_try);
int swapoff_all(bool *changed);
-
-int loopback_detach_all(bool *changed, int umount_log_level);
-
-int dm_detach_all(bool *changed, int umount_log_level);
-
-int md_detach_all(bool *changed, int umount_log_level);
+int loopback_detach_all(bool *changed, bool last_try);
+int dm_detach_all(bool *changed, bool last_try);
+int md_detach_all(bool *changed, bool last_try);
/* This is exported just for testing */
typedef struct MountPoint {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *n = NULL, *dbus_path = NULL, *load_state = NULL;
_cleanup_strv_free_ char **triggered_by = NULL;
- bool print_warning_label = true;
- UnitActiveState active_state;
int r;
r = unit_name_mangle(unit, 0, &n);
if (r < 0)
return log_error_errno(r, "Failed to get triggered by array of %s: %s", n, bus_error_message(&error, r));
+ bool first = true;
STRV_FOREACH(i, triggered_by) {
+ UnitActiveState active_state;
+
r = get_state_one_unit(bus, *i, &active_state);
if (r < 0)
return r;
if (!IN_SET(active_state, UNIT_ACTIVE, UNIT_RELOADING))
continue;
- if (print_warning_label) {
+ if (first) {
log_warning("Warning: Stopping %s, but it can still be activated by:", n);
- print_warning_label = false;
+ first = false;
}
log_warning(" %s", *i);
uint8_t buffer[buffer_sz];
int r;
- log_info("%s, %s, %zu, →%d", what, expect, buffer_sz, ret);
+ log_info("%s, %s, %zu, →%d", what, strnull(expect), buffer_sz, ret);
r = dns_name_to_wire_format(what, buffer, buffer_sz, false);
assert_se(r == ret);
- if (r < 0)
- return;
-
- assert_se(!memcmp(buffer, expect, r));
+ if (r >= 0) {
+ assert(expect); /* for gcc */
+ assert_se(memcmp(buffer, expect, r) == 0);
+ }
}
TEST(dns_name_to_wire_format) {
_cleanup_free_ char *t = NULL;
int r;
- log_info("%s, %zu, %s, →%d", what, l, expect, ret);
+ log_info("%s, %zu, %s, →%d", what, l, strnull(expect), ret);
r = dns_label_escape_new(what, l, &t);
assert_se(r == ret);
static void test_dns_service_join_one(const char *a, const char *b, const char *c, int r, const char *d) {
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL;
- log_info("%s, %s, %s, →%d, %s", a, b, c, r, d);
+ log_info("%s, %s, %s, →%d, %s", strnull(a), strnull(b), strnull(c), r, strnull(d));
assert_se(dns_service_join(a, b, c, &t) == r);
assert_se(streq_ptr(t, d));
static void test_dns_service_split_one(const char *joined, const char *a, const char *b, const char *c, int r) {
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *t = NULL;
- log_info("%s, %s, %s, %s, →%d", joined, a, b, c, r);
+ log_info("%s, %s, %s, %s, →%d", joined, strnull(a), strnull(b), strnull(c), r);
assert_se(dns_service_split(joined, &x, &y, &z) == r);
assert_se(streq_ptr(x, a));
static void test_dns_name_change_suffix_one(const char *name, const char *old_suffix, const char *new_suffix, int r, const char *result) {
_cleanup_free_ char *s = NULL;
- log_info("%s, %s, %s, →%s", name, old_suffix, new_suffix, result);
+ log_info("%s, %s, %s, →%s", name, old_suffix, new_suffix, strnull(result));
assert_se(dns_name_change_suffix(name, old_suffix, new_suffix, &s) == r);
assert_se(streq_ptr(s, result));
static void test_dns_name_suffix_one(const char *name, unsigned n_labels, const char *result, int ret) {
const char *p = NULL;
- log_info("%s, %d, →%s, %d", name, n_labels, result, ret);
+ log_info("%s, %d, →%s, %d", name, n_labels, strnull(result), ret);
assert_se(ret == dns_name_suffix(name, n_labels, &p));
assert_se(streq_ptr(p, result));
r = fstab_filter_options(opts, remove, &name, &value, NULL, &filtered);
log_info("1: \"%s\" → %d, \"%s\", \"%s\", \"%s\", expected %d, \"%s\", \"%s\", \"%s\"",
opts, r, strnull(name), value, filtered,
- r_expected, name_expected, value_expected, filtered_expected ?: opts);
+ r_expected, strnull(name_expected), strnull(value_expected), filtered_expected ?: opts);
assert_se(r == r_expected);
assert_se(streq_ptr(name, name_expected));
assert_se(streq_ptr(value, value_expected));
assert_se(joined = strv_join(values, ":"));
log_info("2: \"%s\" → %d, \"%s\", \"%s\", expected %d, \"%s\", \"%s\"",
opts, r, strnull(name), joined,
- r_values_expected, name_expected, values_expected);
+ r_values_expected, strnull(name_expected), strnull(values_expected));
assert_se(r == r_values_expected);
assert_se(streq_ptr(name, r_values_expected > 0 ? name_expected : NULL));
assert_se(streq_ptr(joined, values_expected));
r = fstab_filter_options(opts, remove, &name, NULL, NULL, NULL);
log_info("3: \"%s\" → %d, \"%s\", expected %d, \"%s\"\n-",
opts, r, strnull(name),
- r_expected, name_expected);
+ r_expected, strnull(name_expected));
assert_se(r == r_expected);
assert_se(streq_ptr(name, name_expected));
}
_cleanup_free_ char *comm = NULL, *cmdline = NULL;
int r;
- log_info("/* %s */", __func__);
+ log_info("/* %s(%s) */", __func__, p);
r = rename_process(p);
assert_se(r == ret ||
siginfo_t si;
pid_t pid;
- log_info("/* %s */", __func__);
+ log_info("/* %s(%s) */", __func__, p);
pid = fork();
assert_se(pid >= 0);
assert_se(si.si_status == EXIT_SUCCESS);
}
+TEST(rename_process_invalid) {
+ assert_se(rename_process(NULL) == -EINVAL);
+ assert_se(rename_process("") == -EINVAL);
+}
+
TEST(rename_process_multi) {
pid_t pid;
(void) setresuid(99, 99, 99); /* change uid when running privileged */
test_rename_process_now("time!", 0);
test_rename_process_now("0", 1); /* shorter than "one", should fit */
- test_rename_process_one("", -EINVAL);
- test_rename_process_one(NULL, -EINVAL);
_exit(EXIT_SUCCESS);
}
TEST(rename_process) {
- test_rename_process_one(NULL, -EINVAL);
- test_rename_process_one("", -EINVAL);
test_rename_process_one("foo", 1); /* should always fit */
test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */
test_rename_process_one("1234567", 1); /* should always fit */
}
assert_se(opinionated_personality(¤t) >= 0);
+ /* On ppc64le sanitizers disable ASLR (i.e. by setting ADDR_NO_RANDOMIZE),
+ * which opinionated_personality() doesn't return. Let's tweak the current
+ * personality ourselves in such cases.
+ * See: https://github.com/llvm/llvm-project/commit/78f7a6eaa601bfdd6ae70ffd3da2254c21ff77f9
+ */
+ if (FLAGS_SET(safe_personality(PERSONALITY_INVALID), ADDR_NO_RANDOMIZE))
+ current |= ADDR_NO_RANDOMIZE;
- log_info("current personality=%lu", current);
+ log_info("current personality=0x%lX", current);
pid = fork();
assert_se(pid >= 0);
assert_se((unsigned long) safe_personality(current) == current);
- /* Note, we also test that safe_personality() works correctly, by checkig whether errno is properly
+ /* Note, we also test that safe_personality() works correctly, by checking whether errno is properly
* set, in addition to the return value */
errno = 0;
- assert_se(safe_personality(PER_LINUX | ADDR_NO_RANDOMIZE) == -EPERM);
+ assert_se(safe_personality(PER_LINUX | MMAP_PAGE_ZERO) == -EPERM);
assert_se(errno == EPERM);
- assert_se(safe_personality(PER_LINUX | MMAP_PAGE_ZERO) == -EPERM);
+ if (!FLAGS_SET(current, ADDR_NO_RANDOMIZE))
+ assert_se(safe_personality(PER_LINUX | ADDR_NO_RANDOMIZE) == -EPERM);
assert_se(safe_personality(PER_LINUX | ADDR_COMPAT_LAYOUT) == -EPERM);
assert_se(safe_personality(PER_LINUX | READ_IMPLIES_EXEC) == -EPERM);
assert_se(safe_personality(PER_LINUX_32BIT) == -EPERM);
char *value = UINT_TO_PTR(0x12345678U);
char *endpos = UINT_TO_PTR(0x87654321U);
- log_info("/* %s (%s, %s, %d) */", __func__, in, expected_value, expected_retval);
+ log_info("/* %s (%s, %s, %d) */", __func__, in, strnull(expected_value), expected_retval);
assert_se(str = strdup(in));
assert_se(udev_rule_parse_value(str, &value, &endpos) == expected_retval);
/* input: "va'l\'id\"op\"erand"
* parsed: va'l\'id"op"erand */
test_udev_rule_parse_value_one("\"va'l\\'id\\\"op\\\"erand\"", "va'l\\'id\"op\"erand", 0);
- test_udev_rule_parse_value_one("no quotes", 0, -EINVAL);
+ test_udev_rule_parse_value_one("no quotes", NULL, -EINVAL);
test_udev_rule_parse_value_one("\"\\\\a\\b\\x\\y\"", "\\\\a\\b\\x\\y", 0);
- test_udev_rule_parse_value_one("\"reject\0nul\"", 0, -EINVAL);
+ test_udev_rule_parse_value_one("\"reject\0nul\"", NULL, -EINVAL);
/* input: e"" */
test_udev_rule_parse_value_one("e\"\"", "", 0);
/* input: e"1234" */
/* input: e"\"" */
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", 0);
/* input: e"\ */
- test_udev_rule_parse_value_one("e\"\\", 0, -EINVAL);
+ test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
/* input: e"\" */
- test_udev_rule_parse_value_one("e\"\\\"", 0, -EINVAL);
+ test_udev_rule_parse_value_one("e\"\\\"", NULL, -EINVAL);
/* input: e"\\" */
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", 0);
/* input: e"\\\" */
- test_udev_rule_parse_value_one("e\"\\\\\\\"", 0, -EINVAL);
+ test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, -EINVAL);
/* input: e"\\\"" */
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", 0);
/* input: e"\\\\" */
test_udev_rule_parse_value_one(
"e\"single\\rcharacter\\t\\aescape\\bsequence\"", "single\rcharacter\t\aescape\bsequence", 0);
/* input: e"reject\invalid escape sequence" */
- test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", 0, -EINVAL);
+ test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, -EINVAL);
/* input: e"\ */
- test_udev_rule_parse_value_one("e\"\\", 0, -EINVAL);
+ test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
/* input: "s\u1d1c\u1d04\u029c \u1d1c\u0274\u026a\u1d04\u1d0f\u1d05\u1d07 \U0001d568\U0001d560\U0001d568" */
test_udev_rule_parse_value_one(
"e\"s\\u1d1c\\u1d04\\u029c \\u1d1c\\u0274\\u026a\\u1d04\\u1d0f\\u1d05\\u1d07 \\U0001d568\\U0001d560\\U0001d568\"",
TEST_DESCRIPTION="Job-related tests"
TEST_NO_QEMU=1
-IMAGE_NAME="default"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
set -e
TEST_DESCRIPTION="UDEV"
-IMAGE_NAME="default"
TEST_NO_NSPAWN=1
# shellcheck source=test/test-functions
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+TEST_DESCRIPTION="test timedated"
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+do_test "$@"
set -e
TEST_DESCRIPTION="systemd-udev storage tests"
-IMAGE_NAME="default"
TEST_NO_NSPAWN=1
# Save only journals of failing test cases by default (to conserve space)
TEST_SAVE_JOURNAL="${TEST_SAVE_JOURNAL:-fail}"
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# utility functions for shell tests
+
+assert_true() {
+ local rc
+
+ set +e
+ "$@"
+ rc=$?
+ set -e
+ if [[ "$rc" != "0" ]]; then
+ echo "FAIL: command '$*' failed with exit code $rc" >&2
+ exit 1
+ fi
+}
+
+
+assert_eq() {
+ if [[ "$1" != "$2" ]]; then
+ echo "FAIL: expected: '$2' actual: '$1'" >&2
+ exit 1
+ fi
+}
+
+assert_in() {
+ if ! echo "$2" | grep -q "$1"; then
+ echo "FAIL: '$1' not found in:" >&2
+ echo "$2" >&2
+ exit 1
+ fi
+}
+
+assert_rc() {
+ local exp=$1
+ local rc
+ shift
+ set +e
+ "$@"
+ rc=$?
+ set -e
+ assert_eq "$rc" "$exp"
+}
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-45-TIMEDATE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+StandardOutput=journal+console
+StandardError=journal+console
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/assert.sh
+. "$(dirname "$0")"/assert.sh
+
+test_timezone() {
+ local ORIG_TZ=
+
+ if [[ -L /etc/localtime ]]; then
+ ORIG_TZ=$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')
+ echo "original tz: $ORIG_TZ"
+ fi
+
+ echo 'timedatectl works'
+ assert_in "Local time:" "$(timedatectl --no-pager)"
+
+ echo 'change timezone'
+ assert_eq "$(timedatectl --no-pager set-timezone Europe/Kiev 2>&1)" ""
+ assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "Europe/Kiev"
+ assert_in "Time.*zone: Europe/Kiev (EEST, +" "$(timedatectl --no-pager)"
+
+ if [[ -n "$ORIG_TZ" ]]; then
+ echo 'reset timezone to original'
+ assert_eq "$(timedatectl --no-pager set-timezone "$ORIG_TZ" 2>&1)" ""
+ assert_eq "$(readlink /etc/localtime | sed 's#^.*zoneinfo/##')" "$ORIG_TZ"
+ fi
+}
+
+restore_adjtime() {
+ if [[ -e /etc/adjtime.bak ]]; then
+ mv /etc/adjtime.bak /etc/adjtime
+ else
+ rm /etc/adjtime
+ fi
+}
+
+check_adjtime_not_exist() {
+ if [[ -e /etc/adjtime ]]; then
+ echo "/etc/adjtime unexpectedly exists." >&2
+ exit 1
+ fi
+}
+
+test_adjtime() {
+ # test setting UTC vs. LOCAL in /etc/adjtime
+ if [[ -e /etc/adjtime ]]; then
+ mv /etc/adjtime /etc/adjtime.bak
+ fi
+
+ trap restore_adjtime EXIT
+
+ echo 'no adjtime file'
+ rm -f /etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+
+ echo 'UTC set in adjtime file'
+ printf '0.0 0 0\n0\nUTC\n' > /etc/adjtime
+ timedatectl set-local-rtc 0
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+UTC"
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'non-zero values in adjtime file'
+ printf '0.1 123 0\n0\nLOCAL\n' > /etc/adjtime
+ timedatectl set-local-rtc 0
+ assert_eq "$(cat /etc/adjtime)" "0.1 123 0
+0
+UTC"
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.1 123 0
+0
+LOCAL"
+
+ echo 'fourth line adjtime file'
+ printf '0.0 0 0\n0\nLOCAL\nsomethingelse\n' > /etc/adjtime
+ timedatectl set-local-rtc 0
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+UTC
+somethingelse"
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL
+somethingelse"
+
+ echo 'no final newline in adjtime file'
+ printf '0.0 0 0\n0\nUTC' > /etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0\n0\nUTC' > /etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'only one line in adjtime file'
+ printf '0.0 0 0\n' > /etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0\n' > /etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'only one line in adjtime file, no final newline'
+ printf '0.0 0 0' > /etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0' > /etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'only two lines in adjtime file'
+ printf '0.0 0 0\n0\n' > /etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0\n0\n' > /etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'only two lines in adjtime file, no final newline'
+ printf '0.0 0 0\n0' > /etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0\n0' > /etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ echo 'unknown value in 3rd line of adjtime file'
+ printf '0.0 0 0\n0\nFOO\n' > /etc/adjtime
+ timedatectl set-local-rtc 0
+ check_adjtime_not_exist
+ printf '0.0 0 0\n0\nFOO\n' > /etc/adjtime
+ timedatectl set-local-rtc 1
+ assert_eq "$(cat /etc/adjtime)" "0.0 0 0
+0
+LOCAL"
+
+ restore_adjtime
+ trap - EXIT
+}
+
+assert_ntp() {
+ assert_eq "$(busctl get-property org.freedesktop.timedate1 /org/freedesktop/timedate1 org.freedesktop.timedate1 NTP)" "b $1"
+}
+
+start_mon() {
+ busctl monitor --match="type='signal',sender=org.freedesktop.timedate1,member='PropertiesChanged',path=/org/freedesktop/timedate1" >"$mon" &
+ MONPID=$!
+}
+
+wait_mon() {
+ for ((i=0;i<10;i++)); do
+ if (( i != 0 )); then sleep 1; fi
+ if grep -q "$1" "$mon"; then break; fi
+ done
+ assert_in "$2" "$(cat "$mon")"
+ kill "$MONPID"
+ wait "$MONPID" 2>/dev/null || true
+}
+
+test_ntp() {
+ # timesyncd has ConditionVirtualization=!container by default; drop/mock that for testing
+ if systemd-detect-virt --container --quiet; then
+ systemctl disable --quiet --now systemd-timesyncd
+ mkdir -p /run/systemd/system/systemd-timesyncd.service.d
+ cat >/run/systemd/system/systemd-timesyncd.service.d/container.conf <<EOF
+[Unit]
+ConditionVirtualization=
+
+[Service]
+Type=simple
+AmbientCapabilities=
+ExecStart=
+ExecStart=/bin/sleep infinity
+EOF
+ systemctl daemon-reload
+ fi
+
+ mon=$(mktemp -t dbusmon.XXXXXX)
+
+ echo 'disable NTP'
+ timedatectl set-ntp false
+ for ((i=0;i<10;i++)); do
+ if (( i != 0 )); then sleep 1; fi
+ if [[ "$(systemctl --no-pager show systemd-timesyncd --property ActiveState)" == "ActiveState=inactive" ]]; then
+ break;
+ fi
+ done
+ assert_eq "$(systemctl --no-pager show systemd-timesyncd --property ActiveState)" "ActiveState=inactive"
+ assert_ntp "false"
+ assert_rc 3 systemctl is-active --quiet systemd-timesyncd
+
+ echo 'enable NTP'
+ start_mon
+ timedatectl set-ntp true
+ wait_mon "NTP" "BOOLEAN true"
+ assert_ntp "true"
+ for ((i=0;i<10;i++)); do
+ if (( i != 0 )); then sleep 1; fi
+ if [[ "$(systemctl --no-pager show systemd-timesyncd --property ActiveState)" == "ActiveState=active" ]]; then
+ break;
+ fi
+ done
+ assert_eq "$(systemctl --no-pager show systemd-timesyncd --property ActiveState)" "ActiveState=active"
+ assert_rc 0 systemctl is-active --quiet systemd-timesyncd
+
+ echo 're-disable NTP'
+ start_mon
+ timedatectl set-ntp false
+ wait_mon "NTP" "BOOLEAN false"
+ assert_ntp "false"
+ assert_rc 3 systemctl is-active --quiet systemd-timesyncd
+}
+
+: >/failed
+
+test_timezone
+test_adjtime
+test_ntp
+
+touch /testok
+rm /failed