finishing touches to portable services, and let's move portablectl to /usr/bin to make it official
1. add resume_offset support to the resume code (i.e. support swap files
properly)
- 2. check of swap is on weird storage and refuse if so
- 3. add env-var based option to disable hibernation
- 4. figure out what to do with swap-on-luks
- 5. add autodetection of hibernation images, and add "noresume" to disable
- this
+ 2. check if swap is on weird storage and refuse if so
+ 3. add autodetection of hibernation images
* cgroups: use inotify to get notified when somebody else modifies cgroups
owned by us, then log a friendly warning.
useful for debugging, in order to test generators and other code against
specific kernel command lines.
+* `$SYSTEMD_IN_INITRD` — takes a boolean. If set, overrides initrd detection.
+ This is useful for debugging and testing initrd-only programs in the main
+ system.
+
* `$SYSTEMD_BUS_TIMEOUT=SECS` — specifies the maximum time to wait for method call
completion. If no time unit is specified, assumes seconds. The usual other units
are understood, too (us, ms, s, min, h, d, w, month, y). If it is not set or set
first existing unit listed in the environment variable, and
`timedatectl set-ntp off` disables and stops all listed units.
+bootctl and other tools that access the EFI System Partition (ESP):
+
+* `$SYSTEMD_RELAX_ESP_CHECKS=1` — if set, the ESP validation checks are
+ relaxed. Specifically, validation checks that ensure the specified ESP path
+ is a FAT file system are turned off, as are checks that the path is located
+ on a GPT partition with the correct type UUID.
+
systemd itself:
* `$SYSTEMD_ACTIVATION_UNIT` — set for all NSS and PAM module invocations that
KEYBOARD_KEY_16=mute
KEYBOARD_KEY_17=prog1
KEYBOARD_KEY_1a=f20 # Microphone mute button; should be micmute
+ KEYBOARD_KEY_45=bookmarks
# ThinkPad Keyboard with TrackPoint
evdev:input:b0003v17EFp6009*
<refsect1>
<title>Description</title>
- <para><command>bootctl</command> can check the EFI boot loader status, list
- available entries, and install, update, or remove the
- <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- boot loader on the current system.</para>
+ <para><command>bootctl</command> can check the EFI boot loader status, list available boot loaders and boot loader
+ entries, and install, update, or remove the
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> boot loader on the
+ current system.</para>
</refsect1>
<refsect1>
<para>The following options are understood:</para>
<variablelist>
- <xi:include href="standard-options.xml" xpointer="help" />
- <xi:include href="standard-options.xml" xpointer="version" />
<varlistentry>
<term><option>--path=</option></term>
<listitem><para>Path to the EFI System Partition (ESP). If not specified, <filename>/efi</filename>,
<varlistentry>
<term><option>--no-variables</option></term>
- <listitem><para>Do not touch the EFI boot variables.</para></listitem>
+ <listitem><para>Do not touch the firmware's boot loader list stored in EFI variables.</para></listitem>
</varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="no-pager"/>
+ <xi:include href="standard-options.xml" xpointer="help"/>
+ <xi:include href="standard-options.xml" xpointer="version"/>
</variablelist>
</refsect1>
<varlistentry>
<term><option>status</option></term>
- <listitem><para>Shows the currently installed versions of the boot loader binaries and all current
- EFI boot variables. If no command is specified, this is the implied default.</para></listitem>
+ <listitem><para>Shows brief information about the system firmware, the boot loader that was used to boot the
+ system, the boot loaders currently available in the ESP, the boot loaders listed in the firmware's list of boot
+ loaders and the current default boot loader entry. If no command is specified, this is the implied
+ default.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>list</option></term>
+ <term><option>install</option></term>
- <listitem><para>Shows all configured boot loader entries.</para></listitem>
+ <listitem><para>Installs systemd-boot into the EFI system partition. A copy of <command>systemd-boot</command>
+ will be stored as the EFI default/fallback loader at
+ <filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. The boot loader is then added to the
+ top of the firmware's boot loader list.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>update</option></term>
<listitem><para>Updates all installed versions of
- <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
- if the current version is newer than the version installed in the EFI system
- partition. This also includes the EFI default/fallback loader at
- <filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. A
- systemd-boot entry in the EFI boot variables is created if there is no current
- entry. The created entry will be added to the end of the boot order list.</para></listitem>
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>, if the
+ available version is newer than the version installed in the EFI system partition. This also includes the EFI
+ default/fallback loader at <filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. The boot
+ loader is then added to end of the firmware's boot loader list if missing.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>install</option></term>
+ <term><option>remove</option></term>
- <listitem><para>Installs systemd-boot into the EFI system partition. A copy of systemd-boot will
- be stored as the EFI default/fallback loader at
- <filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. A systemd-boot entry in
- the EFI boot variables is created and added to the top of the boot order list.</para></listitem>
+ <listitem><para>Removes all installed versions of <command>systemd-boot</command> from the EFI system partition
+ and the firmware's boot loader list.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>remove</option></term>
+ <term><option>list</option></term>
- <listitem><para>Removes all installed versions of systemd-boot from the EFI system partition,
- and removes systemd-boot from the EFI boot variables.</para></listitem>
+ <listitem><para>Shows all available boot loader entries implementing the <ulink
+ url="https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md">Boot Loader
+ Specification</ulink>, as well as any other entries discovered or automatically generated by the boot
+ loader.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>set-default</option> <replaceable>ID</replaceable></term>
+ <term><option>set-oneshot</option> <replaceable>ID</replaceable></term>
+
+ <listitem><para>Sets the default boot loader entry. Takes a single boot loader entry ID string as argument. The
+ <option>set-oneshot</option> command will set the default entry only for the next boot, the
+ <option>set-default</option> will set it persistently for all future boots.</para></listitem>
</varlistentry>
</variablelist>
<para>On success, 0 is returned, a non-zero failure code otherwise.</para>
</refsect1>
+ <refsect1>
+ <title>Environment</title>
+ <para>If <varname>$SYSTEMD_RELAX_ESP_CHECKS=1</varname> is set the validation checks for the ESP are relaxed, and
+ the path specified with <option>--path=</option> may refer to any kind of file system on any kind of
+ partition.</para>
+ </refsect1>
+
<refsect1>
<title>See Also</title>
<para>
<term><varname>systemd.unit=</varname></term>
<term><varname>rd.systemd.unit=</varname></term>
<term><varname>systemd.dump_core</varname></term>
+ <term><varname>systemd.early_core_pattern=</varname></term>
<term><varname>systemd.crash_chvt</varname></term>
<term><varname>systemd.crash_shell</varname></term>
<term><varname>systemd.crash_reboot</varname></term>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>systemd.early_core_pattern=</varname></term>
+ <listitem>
+ <para>During early boot, the generation of core dump files is disabled until a core dump handler (if any)
+ takes over. This parameter allows to specifies an absolute path where core dump files should be stored until
+ a handler is installed. The path should be absolute and may contain specifiers, see
+ <citerefentry><refentrytitle>core</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>systemd.restore_state=</varname></term>
<listitem>
<refsect1>
<title>Description</title>
- <para><filename>systemd-hibernate-resume-generator</filename> is a
- generator that instantiates
+ <para><command>systemd-hibernate-resume-generator</command> is a
+ generator that initiates the procedure to resume the system from hibernation.
+ It instantiates the
<citerefentry><refentrytitle>systemd-hibernate-resume@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
unit according to the value of <option>resume=</option> parameter
- specified on the kernel command line.</para>
+ specified on the kernel command line, which will instruct the kernel
+ to resume the system from the hibernation image on that device.</para>
</refsect1>
<refsect1>
supported.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>noresume</varname></term>
+
+ <listitem><para>Do not try to resume from hibernation. If this parameter is
+ present, <varname>resume=</varname> is ignored.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<filename>sleep.conf.d</filename> file:</para>
<variablelist class='systemd-directives'>
+ <varlistentry>
+ <term><varname>AllowSuspend=</varname></term>
+ <term><varname>AllowHibernation=</varname></term>
+ <term><varname>AllowSuspendThenHibernate=</varname></term>
+ <term><varname>AllowHybridSleep=</varname></term>
+
+ <listitem><para>By default any power-saving mode is advertised if possible (i.e.
+ the kernel supports that mode, the necessary resources are available). Those
+ switches can be used to disable specific modes.</para>
+
+ <para>If <varname>AllowHibernation=no</varname> or <varname>AllowSuspend=no</varname> is
+ used, this implies <varname>AllowSuspendThenHibernate=no</varname> and
+ <varname>AllowHybridSleep=no</varname>, since those methods use both suspend and hibernation
+ internally. <varname>AllowSuspendThenHibernate=yes</varname> and
+ <varname>AllowHybridSleep=yes</varname> can be used to override and enable those specific
+ modes.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>SuspendMode=</varname></term>
<term><varname>HibernateMode=</varname></term>
* it: the IMMUTABLE bit. Let's use this here, if this is requested. */
if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
- (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
+ (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
} else {
r = btrfs_subvol_set_read_only(new_path, true);
if (r < 0)
#include "fd-util.h"
#include "macro.h"
-int chattr_fd(int fd, unsigned value, unsigned mask) {
+int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) {
unsigned old_attr, new_attr;
struct stat st;
if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
return -ENOTTY;
- if (mask == 0)
+ if (mask == 0 && !previous)
return 0;
if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
return -errno;
new_attr = (old_attr & ~mask) | (value & mask);
- if (new_attr == old_attr)
+ if (new_attr == old_attr) {
+ if (previous)
+ *previous = old_attr;
return 0;
+ }
if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
return -errno;
+ if (previous)
+ *previous = old_attr;
+
return 1;
}
-int chattr_path(const char *p, unsigned value, unsigned mask) {
+int chattr_path(const char *p, unsigned value, unsigned mask, unsigned *previous) {
_cleanup_close_ int fd = -1;
assert(p);
if (fd < 0)
return -errno;
- return chattr_fd(fd, value, mask);
+ return chattr_fd(fd, value, mask, previous);
}
int read_attr_fd(int fd, unsigned *ret) {
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-int chattr_fd(int fd, unsigned value, unsigned mask);
-int chattr_path(const char *p, unsigned value, unsigned mask);
+int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous);
+int chattr_path(const char *p, unsigned value, unsigned mask, unsigned *previous);
int read_attr_fd(int fd, unsigned *ret);
int read_attr_path(const char *p, unsigned *ret);
}
if (chattr_flags != 0)
- (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
+ (void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL);
r = copy_file_fd(from, fdt, copy_flags);
if (r < 0) {
}
if (chattr_flags != 0)
- (void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
+ (void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL);
r = copy_file_fd(from, fdt, copy_flags);
if (r < 0)
strna(type), where, strnull(fl), strempty(o));
if (mount(what, where, type, f, o) < 0)
return log_full_errno(error_log_level, errno,
- "Failed to mount %s on %s (%s \"%s\"): %m",
- strna(type), where, strnull(fl), strempty(o));
+ "Failed to mount %s (type %s) on %s (%s \"%s\"): %m",
+ strna(what), strna(type), where, strnull(fl), strempty(o));
return 0;
}
return read_one_line_file("/proc/cmdline", ret);
}
-int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) {
-
- _cleanup_free_ char *line = NULL;
+int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse_item, void *data, unsigned flags) {
const char *p;
int r;
assert(parse_item);
- r = proc_cmdline(&line);
- if (r < 0)
- return r;
-
p = line;
for (;;) {
_cleanup_free_ char *word = NULL;
return 0;
}
-static bool relaxed_equal_char(char a, char b) {
+int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) {
+ _cleanup_free_ char *line = NULL;
+ int r;
+ assert(parse_item);
+
+ r = proc_cmdline(&line);
+ if (r < 0)
+ return r;
+
+ return proc_cmdline_parse_given(line, parse_item, data, flags);
+}
+
+static bool relaxed_equal_char(char a, char b) {
return a == b ||
(a == '_' && b == '-') ||
(a == '-' && b == '_');
}
char *proc_cmdline_key_startswith(const char *s, const char *prefix) {
-
assert(s);
assert(prefix);
int proc_cmdline(char **ret);
+int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse_item, void *data, unsigned flags);
int proc_cmdline_parse(const proc_cmdline_parse_t parse, void *userdata, unsigned flags);
int proc_cmdline_get_key(const char *parameter, unsigned flags, char **value);
if (e)
(void) safe_atoi(e, &c);
- if (c <= 0)
+ if (c <= 0 || c > USHRT_MAX) {
c = fd_columns(STDOUT_FILENO);
-
- if (c <= 0)
- c = 80;
+ if (c <= 0)
+ c = 80;
+ }
cached_columns = c;
return cached_columns;
if (e)
(void) safe_atoi(e, &l);
- if (l <= 0)
+ if (l <= 0 || l > USHRT_MAX) {
l = fd_lines(STDOUT_FILENO);
-
- if (l <= 0)
- l = 24;
+ if (l <= 0)
+ l = 24;
+ }
cached_lines = l;
return cached_lines;
#include "def.h"
#include "device-nodes.h"
#include "dirent-util.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
bool in_initrd(void) {
struct statfs s;
+ int r;
if (saved_in_initrd >= 0)
return saved_in_initrd;
* emptying when transititioning to the main systemd.
*/
- saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
- statfs("/", &s) >= 0 &&
- is_temporary_fs(&s);
+ r = getenv_bool_secure("SYSTEMD_IN_INITRD");
+ if (r < 0 && r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m");
+
+ if (r >= 0)
+ saved_in_initrd = r > 0;
+ else
+ saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
+ statfs("/", &s) >= 0 &&
+ is_temporary_fs(&s);
return saved_in_initrd;
}
#include "fileio.h"
#include "fs-util.h"
#include "locale-util.h"
+#include "pager.h"
#include "parse-util.h"
#include "rm-rf.h"
#include "stat-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "umask-util.h"
+#include "utf8.h"
#include "util.h"
#include "verbs.h"
#include "virt.h"
static char *arg_path = NULL;
static bool arg_print_path = false;
static bool arg_touch_variables = true;
+static bool arg_no_pager = false;
static int acquire_esp(
bool unprivileged_mode,
if (r < 0)
return r;
if (r > 0)
- printf(" File: %s/%s/%s (%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, v);
+ printf(" File: %s/%s/%s (%s%s%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, ansi_highlight(), v, ansi_normal());
else
printf(" File: %s/%s/%s\n", special_glyph(TREE_RIGHT), path, de->d_name);
c++;
static int status_binaries(const char *esp_path, sd_id128_t partition) {
int r;
- printf("Boot Loader Binaries:\n");
+ printf("Available Boot Loaders on ESP:\n");
if (!esp_path) {
printf(" ESP: Cannot find or access mount point of ESP.\n\n");
r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
if (r == 0)
- log_error("systemd-boot not installed in ESP.");
+ log_info("systemd-boot not installed in ESP.");
else if (r < 0)
return r;
r = enumerate_binaries(esp_path, "EFI/BOOT", "boot");
if (r == 0)
- log_error("No default/fallback boot loader installed in ESP.");
+ log_info("No default/fallback boot loader installed in ESP.");
else if (r < 0)
return r;
efi_tilt_backslashes(path);
- printf(" Title: %s\n", strna(title));
+ printf(" Title: %s%s%s\n", ansi_highlight(), strna(title), ansi_normal());
printf(" ID: 0x%04X\n", id);
printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition));
}
static int status_variables(void) {
- int n_options, n_order;
_cleanup_free_ uint16_t *options = NULL, *order = NULL;
- int i;
+ int n_options, n_order, i;
n_options = efi_get_boot_options(&options);
if (n_options == -ENOENT)
return log_error_errno(n_order, "Failed to read EFI boot order.");
/* print entries in BootOrder first */
- printf("Boot Loader Entries in EFI Variables:\n");
+ printf("Boot Loaders Listed in EFI Variables:\n");
for (i = 0; i < n_order; i++)
print_efi_option(order[i], true);
return 0;
}
-static int status_entries(const char *esp_path, sd_id128_t partition) {
- int r;
+static int boot_entry_show(const BootEntry *e, bool show_as_default) {
+ assert(e);
+
+ printf(" title: %s%s%s%s%s%s\n",
+ ansi_highlight(),
+ boot_entry_title(e),
+ ansi_normal(),
+ ansi_highlight_green(),
+ show_as_default ? " (default)" : "",
+ ansi_normal());
+
+ if (e->id)
+ printf(" id: %s\n", e->id);
+ if (e->version)
+ printf(" version: %s\n", e->version);
+ if (e->machine_id)
+ printf(" machine-id: %s\n", e->machine_id);
+ if (e->architecture)
+ printf(" architecture: %s\n", e->architecture);
+ if (e->kernel)
+ printf(" linux: %s\n", e->kernel);
+ if (!strv_isempty(e->initrd)) {
+ _cleanup_free_ char *t;
+
+ t = strv_join(e->initrd, " ");
+ if (!t)
+ return log_oom();
+
+ printf(" initrd: %s\n", t);
+ }
+ if (!strv_isempty(e->options)) {
+ _cleanup_free_ char *t;
- _cleanup_(boot_config_free) BootConfig config = {};
+ t = strv_join(e->options, " ");
+ if (!t)
+ return log_oom();
+
+ printf(" options: %s\n", t);
+ }
+ if (e->device_tree)
+ printf(" devicetree: %s\n", e->device_tree);
+
+ return 0;
+}
- printf("Default Boot Entry:\n");
+static int status_entries(const char *esp_path, sd_id128_t partition) {
+ _cleanup_(boot_config_free) BootConfig config = {};
+ int r;
r = boot_entries_load_config(esp_path, &config);
if (r < 0)
- return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m",
- esp_path);
+ return r;
if (config.default_entry < 0)
- printf("%zu entries, no entry suitable as default\n", config.n_entries);
+ printf("%zu entries, no entry could be determined as default.\n", config.n_entries);
else {
- const BootEntry *e = &config.entries[config.default_entry];
-
- printf(" title: %s\n", boot_entry_title(e));
- if (e->version)
- printf(" version: %s\n", e->version);
- if (e->kernel)
- printf(" linux: %s\n", e->kernel);
- if (!strv_isempty(e->initrd)) {
- _cleanup_free_ char *t;
-
- t = strv_join(e->initrd, " ");
- if (!t)
- return log_oom();
-
- printf(" initrd: %s\n", t);
- }
- if (!strv_isempty(e->options)) {
- _cleanup_free_ char *t;
-
- t = strv_join(e->options, " ");
- if (!t)
- return log_oom();
+ printf("Default Boot Loader Entry:\n");
- printf(" options: %s\n", t);
- }
- if (e->device_tree)
- printf(" devicetree: %s\n", e->device_tree);
- puts("");
+ boot_entry_show(config.entries + config.default_entry, false);
}
return 0;
return log_oom();
}
- fprintf(f, "#timeout 3\n");
- fprintf(f, "#console-mode keep\n");
- fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
+ fprintf(f, "#timeout 3\n"
+ "#console-mode keep\n"
+ "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
r = fflush_sync_and_check(f);
if (r < 0)
" --path=PATH Path to the EFI System Partition (ESP)\n"
" -p --print-path Print path to the EFI partition\n"
" --no-variables Don't touch EFI variables\n"
- "\nCommands:\n"
+ " --no-pager Do not pipe output into a pager\n"
+ "\nBoot Loader Commands:\n"
" status Show status of installed systemd-boot and EFI variables\n"
- " list List boot entries\n"
" install Install systemd-boot to the ESP and EFI variables\n"
" update Update systemd-boot in the ESP and EFI variables\n"
" remove Remove systemd-boot from the ESP and EFI variables\n"
+ "\nBoot Loader Entries Commands:\n"
+ " list List boot loader entries\n"
+ " set-default ID Set default boot loader entry\n"
+ " set-oneshot ID Set default boot loader entry, for next boot only\n"
"\nSee the %s for details.\n"
, program_invocation_short_name
- , link
- );
+ , link);
return 0;
}
ARG_PATH = 0x100,
ARG_VERSION,
ARG_NO_VARIABLES,
+ ARG_NO_PAGER,
};
static const struct option options[] = {
{ "path", required_argument, NULL, ARG_PATH },
{ "print-path", no_argument, NULL, 'p' },
{ "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
- { NULL, 0, NULL, 0 }
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ {}
};
int c, r;
arg_touch_variables = false;
break;
+ case ARG_NO_PAGER:
+ arg_no_pager = true;
+ break;
+
case '?':
return -EINVAL;
r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just show what we
* can show */
+ (void) pager_open(arg_no_pager, false);
+
if (is_efi_boot()) {
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
sd_id128_t loader_part_uuid = SD_ID128_NULL;
r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
printf("System:\n");
- printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info));
+ printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
printf(" Secure Boot: %sd\n", enable_disable(is_efi_secure_boot()));
printf(" Setup Mode: %s\n", is_efi_secure_boot_setup_mode() ? "setup" : "user");
printf("\n");
- printf("Current Loader:\n");
- printf(" Product: %s\n", strna(loader));
+ printf("Current Boot Loader:\n");
+ printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
if (stub)
printf(" Stub: %s\n", stub);
if (!sd_id128_is_null(loader_part_uuid))
static int verb_list(int argc, char *argv[], void *userdata) {
_cleanup_(boot_config_free) BootConfig config = {};
+ _cleanup_free_ char **found_by_loader = NULL;
sd_id128_t uuid = SD_ID128_NULL;
- unsigned n;
int r;
/* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn
r = boot_entries_load_config(arg_path, &config);
if (r < 0)
- return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m",
- arg_path);
-
- printf("Available boot entries:\n");
-
- for (n = 0; n < config.n_entries; n++) {
- const BootEntry *e = &config.entries[n];
-
- printf(" title: %s%s%s%s%s%s\n",
- ansi_highlight(),
- boot_entry_title(e),
- ansi_normal(),
- ansi_highlight_green(),
- n == (unsigned) config.default_entry ? " (default)" : "",
- ansi_normal());
- if (e->version)
- printf(" version: %s\n", e->version);
- if (e->machine_id)
- printf(" machine-id: %s\n", e->machine_id);
- if (e->architecture)
- printf(" architecture: %s\n", e->architecture);
- if (e->kernel)
- printf(" linux: %s\n", e->kernel);
- if (!strv_isempty(e->initrd)) {
- _cleanup_free_ char *t;
-
- t = strv_join(e->initrd, " ");
- if (!t)
- return log_oom();
+ return r;
- printf(" initrd: %s\n", t);
- }
- if (!strv_isempty(e->options)) {
- _cleanup_free_ char *t;
+ r = efi_loader_get_entries(&found_by_loader);
+ if (r < 0 && !IN_SET(r, -ENOENT, -EOPNOTSUPP))
+ log_debug_errno(r, "Failed to acquire boot loader discovered entries: %m");
- t = strv_join(e->options, " ");
- if (!t)
- return log_oom();
+ if (config.n_entries == 0)
+ log_info("No boot loader entries found.");
+ else {
+ size_t n;
+
+ (void) pager_open(arg_no_pager, false);
+
+ printf("Boot Loader Entries:\n");
- printf(" options: %s\n", t);
+ for (n = 0; n < config.n_entries; n++) {
+ r = boot_entry_show(config.entries + n, n == (size_t) config.default_entry);
+ if (r < 0)
+ return r;
+
+ puts("");
+
+ strv_remove(found_by_loader, config.entries[n].id);
}
- if (e->device_tree)
- printf(" devicetree: %s\n", e->device_tree);
+ }
+
+ if (!strv_isempty(found_by_loader)) {
+ char **i;
- puts("");
+ printf("Automatic/Other Entries Found by Boot Loader:\n\n");
+
+ STRV_FOREACH(i, found_by_loader)
+ puts(*i);
}
return 0;
}
+static int sync_esp(void) {
+ _cleanup_close_ int fd = -1;
+
+ if (!arg_path)
+ return 0;
+
+ fd = open(arg_path, O_CLOEXEC|O_DIRECTORY|O_RDONLY);
+ if (fd < 0)
+ return log_error_errno(errno, "Couldn't open ESP '%s' for synchronization: %m", arg_path);
+
+ if (syncfs(fd) < 0)
+ return log_error_errno(errno, "Failed to synchronize the ESP '%s': %m", arg_path);
+
+ return 1;
+}
+
static int verb_install(int argc, char *argv[], void *userdata) {
sd_id128_t uuid = SD_ID128_NULL;
}
}
+ (void) sync_esp();
+
if (arg_touch_variables)
r = install_variables(arg_path,
part, pstart, psize, uuid,
r = remove_binaries(arg_path);
+ (void) sync_esp();
+
if (arg_touch_variables) {
int q;
return r;
}
+static int verb_set_default(int argc, char *argv[], void *userdata) {
+ const char *name;
+ int r;
+
+ if (!is_efi_boot()) {
+ log_error("Not booted with UEFI.");
+ return -EOPNOTSUPP;
+ }
+
+ if (access("/sys/firmware/efi/efivars/LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f", F_OK) < 0) {
+ if (errno == ENOENT) {
+ log_error_errno(errno, "Not booted with a supported boot loader.");
+ return -EOPNOTSUPP;
+ }
+
+ return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]);
+ }
+
+ if (detect_container() > 0) {
+ log_error("'%s' operation not supported in a container.", argv[0]);
+ return -EOPNOTSUPP;
+ }
+
+ if (!arg_touch_variables) {
+ log_error("'%s' operation cannot be combined with --touch-variables=no.", argv[0]);
+ return -EINVAL;
+ }
+
+ name = streq(argv[0], "set-default") ? "LoaderEntryDefault" : "LoaderEntryOneShot";
+
+ if (isempty(argv[1])) {
+ r = efi_set_variable(EFI_VENDOR_LOADER, name, NULL, 0);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to remove EFI variale: %m");
+ } else {
+ _cleanup_free_ char16_t *encoded = NULL;
+
+ encoded = utf8_to_utf16(argv[1], strlen(argv[1]));
+ if (!encoded)
+ return log_oom();
+
+ r = efi_set_variable(EFI_VENDOR_LOADER, name, encoded, char16_strlen(encoded) * 2 + 2);
+ if (r < 0)
+ return log_error_errno(r, "Failed to update EFI variable: %m");
+ }
+
+ return 0;
+}
+
static int bootctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
- { "help", VERB_ANY, VERB_ANY, 0, help },
- { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
- { "list", VERB_ANY, 1, 0, verb_list },
- { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
- { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
- { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove },
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
+ { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
+ { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
+ { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove },
+ { "list", VERB_ANY, 1, 0, verb_list },
+ { "set-default", 2, 2, VERB_MUST_BE_ROOT, verb_set_default },
+ { "set-oneshot", 2, 2, VERB_MUST_BE_ROOT, verb_set_default },
{}
};
r = bootctl_main(argc, argv);
finish:
+ pager_close();
free(arg_path);
+
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
static bool ignore_proc(pid_t pid, bool warn_rootfs) {
_cleanup_fclose_ FILE *f = NULL;
- char c;
const char *p;
- size_t count;
+ char c = 0;
uid_t uid;
int r;
/* We are PID 1, let's not commit suicide */
- if (pid == 1)
+ if (pid <= 1)
return true;
+ /* Ignore kernel threads */
+ r = is_kernel_thread(pid);
+ if (r != 0)
+ return true; /* also ignore processes where we can't determine this */
+
r = get_process_uid(pid, &uid);
if (r < 0)
return true; /* not really, but better safe than sorry */
if (!f)
return true; /* not really, but has the desired effect */
- count = fread(&c, 1, 1, f);
-
- /* Kernel threads have an empty cmdline */
- if (count <= 0)
- return true;
+ /* Try to read the first character of the command line. If the cmdline is empty (which might be the case for
+ * kernel threads but potentially also other stuff), this line won't do anything, but we don't care much, as
+ * actual kernel threads are already filtered out above. */
+ (void) fread(&c, 1, 1, f);
/* Processes with argv[0][0] = '@' we ignore from the killing spree.
*
_cleanup_free_ char *comm = NULL;
- get_process_comm(pid, &comm);
+ (void) get_process_comm(pid, &comm);
log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is "
"running from the root file system, and thus likely to block re-mounting of the "
static unsigned arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST;
static usec_t arg_runtime_watchdog = 0;
static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
+static char *arg_early_core_pattern = NULL;
static char *arg_watchdog_device = NULL;
static char **arg_default_environment = NULL;
static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
else
arg_dump_core = r;
+ } else if (proc_cmdline_key_streq(key, "systemd.early_core_pattern")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ if (path_is_absolute(value))
+ (void) parse_path_argument_and_warn(value, false, &arg_early_core_pattern);
+ else
+ log_warning("Specified core pattern '%s' is not an absolute path, ignoring.", value);
+
} else if (proc_cmdline_key_streq(key, "systemd.crash_chvt")) {
if (!value)
if (proc_cmdline_value_missing(key, value))
return 0;
- parse_path_argument_and_warn(value, false, &arg_watchdog_device);
+ (void) parse_path_argument_and_warn(value, false, &arg_watchdog_device);
} else if (streq(key, "quiet") && !value) {
if (setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)) < 0)
log_warning_errno(errno, "Failed to set RLIMIT_CORE: %m");
- /* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored
- * until the systemd-coredump tool is enabled via sysctl. */
+ /* But at the same time, turn off the core_pattern logic by default, so that no
+ * coredumps are stored until the systemd-coredump tool is enabled via
+ * sysctl. However it can be changed via the kernel command line later so core
+ * dumps can still be generated during early startup and in initramfs. */
if (!skip_setup)
disable_coredumps();
#endif
}
+static void initialize_core_pattern(bool skip_setup) {
+ int r;
+
+ if (skip_setup || !arg_early_core_pattern)
+ return;
+
+ if (getpid_cached() != 1)
+ return;
+
+ r = write_string_file("/proc/sys/kernel/core_pattern", arg_early_core_pattern, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to write '%s' to /proc/sys/kernel/core_pattern, ignoring: %m", arg_early_core_pattern);
+}
+
static void do_reexecute(
int argc,
char *argv[],
if (arg_action == ACTION_RUN) {
+ /* A core pattern might have been specified via the cmdline. */
+ initialize_core_pattern(skip_setup);
+
/* Close logging fds, in order not to confuse collecting passed fds and terminal logic below */
log_close();
if (!(flags & UNIT_NOTIFY_WILL_AUTO_RESTART))
unit_start_on_failure(u);
}
- }
- if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
+ if (UNIT_IS_ACTIVE_OR_RELOADING(ns) && !UNIT_IS_ACTIVE_OR_RELOADING(os)) {
+ /* This unit just finished starting up */
- if (u->type == UNIT_SERVICE &&
- !UNIT_IS_ACTIVE_OR_RELOADING(os) &&
- !MANAGER_IS_RELOADING(m)) {
- /* Write audit record if we have just finished starting up */
- manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
- u->in_audit = true;
- }
+ if (u->type == UNIT_SERVICE) {
+ /* Write audit record if we have just finished starting up */
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
+ u->in_audit = true;
+ }
- if (!UNIT_IS_ACTIVE_OR_RELOADING(os))
manager_send_unit_plymouth(m, u);
+ }
- } else {
-
- if (UNIT_IS_INACTIVE_OR_FAILED(ns) &&
- !UNIT_IS_INACTIVE_OR_FAILED(os)
- && !MANAGER_IS_RELOADING(m)) {
-
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns) && !UNIT_IS_INACTIVE_OR_FAILED(os)) {
/* This unit just stopped/failed. */
+
if (u->type == UNIT_SERVICE) {
- /* Hmm, if there was no start record written
- * write it now, so that we always have a nice
- * pair */
- if (!u->in_audit) {
+ if (u->in_audit) {
+ /* Write audit record if we have just finished shutting down */
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
+ u->in_audit = false;
+ } else {
+ /* Hmm, if there was no start record written write it now, so that we always
+ * have a nice pair */
manager_send_unit_audit(m, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
if (ns == UNIT_INACTIVE)
manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, true);
- } else
- /* Write audit record if we have just finished shutting down */
- manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
-
- u->in_audit = false;
+ }
}
/* Write a log message about consumed resources */
unit_serialize_item(u, f, "transient", yes_no(u->transient));
+ unit_serialize_item(u, f, "in-audit", yes_no(u->in_audit));
+
unit_serialize_item(u, f, "exported-invocation-id", yes_no(u->exported_invocation_id));
unit_serialize_item(u, f, "exported-log-level-max", yes_no(u->exported_log_level_max));
unit_serialize_item(u, f, "exported-log-extra-fields", yes_no(u->exported_log_extra_fields));
continue;
+ } else if (streq(l, "in-audit")) {
+
+ r = parse_boolean(v);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse in-audit bool %s, ignoring.", v);
+ else
+ u->in_audit = r;
+
+ continue;
+
} else if (streq(l, "exported-invocation-id")) {
r = parse_boolean(v);
static const char *arg_dest = "/tmp";
static char *arg_resume_device = NULL;
+static bool arg_noresume = false;
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
if (!s)
return log_oom();
- free(arg_resume_device);
- arg_resume_device = s;
+ free_and_replace(arg_resume_device, s);
+
+ } else if (streq(key, "noresume")) {
+ if (value) {
+ log_warning("\"noresume\" kernel command line switch specified with an argument, ignoring.");
+ return 0;
+ }
+
+ arg_noresume = true;
}
return 0;
int main(int argc, char *argv[]) {
int r = 0;
+ log_set_prohibit_ipc(true);
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
if (argc > 1 && argc != 4) {
log_error("This program takes three or no arguments.");
return EXIT_FAILURE;
if (argc > 1)
arg_dest = argv[1];
- log_set_prohibit_ipc(true);
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
/* Don't even consider resuming outside of initramfs. */
- if (!in_initrd())
+ if (!in_initrd()) {
+ log_debug("Not running in an initrd, quitting.");
return EXIT_SUCCESS;
+ }
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+ if (arg_noresume) {
+ log_notice("Found \"noresume\" on the kernel command line, quitting.");
+ return EXIT_SUCCESS;
+ }
+
r = process_resume();
free(arg_resume_device);
if (converted_fd < 0)
return log_error_errno(errno, "Failed to create %s: %m", t);
- r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
+ r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes on %s: %m", t);
if (i->output_fd < 0)
return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path);
- r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL);
+ r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path);
if (converted_fd < 0)
return log_error_errno(errno, "Failed to create %s: %m", t);
- r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
+ r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes on %s: %m", t);
* performance on COW file systems like btrfs, since it
* reduces fragmentation caused by not allowing in-place
* writes. */
- r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL);
+ r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes on %s: %m", tp);
if (r < 0)
return r;
- r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL);
+ r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", i->temp_path);
* reenable all the good bits COW usually provides
* (such as data checksumming). */
- (void) chattr_fd(f->fd, 0, FS_NOCOW_FL);
+ (void) chattr_fd(f->fd, 0, FS_NOCOW_FL, NULL);
(void) btrfs_defrag_fd(f->fd);
}
/* btrfs doesn't cope well with our write pattern and
* fragments heavily. Let's defrag all files we rotate */
- (void) chattr_path(p, 0, FS_NOCOW_FL);
+ (void) chattr_path(p, 0, FS_NOCOW_FL, NULL);
(void) btrfs_defrag(p);
log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
/* Enable secure remove, exclusion from dump, synchronous
* writing and in-place updating */
- r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL);
+ r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, NULL);
if (r < 0)
log_warning_errno(r, "Failed to set file attributes: %m");
static int client_context_compare(const void *a, const void *b) {
const ClientContext *x = a, *y = b;
+ int r;
- if (x->timestamp < y->timestamp)
- return -1;
- if (x->timestamp > y->timestamp)
- return 1;
-
- if (x->pid < y->pid)
- return -1;
- if (x->pid > y->pid)
- return 1;
+ r = CMP(x->timestamp, y->timestamp);
+ if (r != 0)
+ return r;
- return 0;
+ return CMP(x->pid, y->pid);
}
static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
if (x->timeout_usec == 0 && y->timeout_usec != 0)
return 1;
- if (x->timeout_usec < y->timeout_usec)
- return -1;
-
- if (x->timeout_usec > y->timeout_usec)
- return 1;
-
- return 0;
+ return CMP(x->timeout_usec, y->timeout_usec);
}
_public_ int sd_bus_call_async(
static int pending_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
+ int r;
assert(x->pending);
assert(y->pending);
return 1;
/* Lower priority values first */
- if (x->priority < y->priority)
- return -1;
- if (x->priority > y->priority)
- return 1;
+ r = CMP(x->priority, y->priority);
+ if (r != 0)
+ return r;
/* Older entries first */
- if (x->pending_iteration < y->pending_iteration)
- return -1;
- if (x->pending_iteration > y->pending_iteration)
- return 1;
-
- return 0;
+ return CMP(x->pending_iteration, y->pending_iteration);
}
static int prepare_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
+ int r;
assert(x->prepare);
assert(y->prepare);
/* Move most recently prepared ones last, so that we can stop
* preparing as soon as we hit one that has already been
* prepared in the current iteration */
- if (x->prepare_iteration < y->prepare_iteration)
- return -1;
- if (x->prepare_iteration > y->prepare_iteration)
- return 1;
+ r = CMP(x->prepare_iteration, y->prepare_iteration);
+ if (r != 0)
+ return r;
/* Lower priority values first */
- if (x->priority < y->priority)
- return -1;
- if (x->priority > y->priority)
- return 1;
-
- return 0;
+ return CMP(x->priority, y->priority);
}
static int earliest_time_prioq_compare(const void *a, const void *b) {
return 1;
/* Order by time */
- if (x->time.next < y->time.next)
- return -1;
- if (x->time.next > y->time.next)
- return 1;
-
- return 0;
+ return CMP(x->time.next, y->time.next);
}
static usec_t time_event_source_latest(const sd_event_source *s) {
return 1;
/* Order by time */
- if (time_event_source_latest(x) < time_event_source_latest(y))
- return -1;
- if (time_event_source_latest(x) > time_event_source_latest(y))
- return 1;
-
- return 0;
+ return CMP(time_event_source_latest(x), time_event_source_latest(y));
}
static int exit_prioq_compare(const void *a, const void *b) {
}
if (!hwdb->f) {
- log_debug("hwdb.bin does not exist, please run systemd-hwdb update");
+ log_debug("hwdb.bin does not exist, please run 'systemd-hwdb update'");
return -ENOENT;
}
if (x->timeout == 0 && y->timeout != 0)
return 1;
- if (x->timeout < y->timeout)
- return -1;
-
- if (x->timeout > y->timeout)
- return 1;
-
- return 0;
+ return CMP(x->timeout, y->timeout);
}
int sd_netlink_call_async(sd_netlink *nl,
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Not enough swap space for hibernation");
if (r == -ENOMEDIUM)
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Kernel image has been removed, can't hibernate");
+ if (r == -EADV)
+ return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Resume not configured, can't hibernate");
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Sleep verb \"%s\" not supported", sleep_verb);
if (r < 0)
if (sleep_verb) {
r = can_sleep(sleep_verb);
- if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM))
+ if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM, -EADV))
return sd_bus_reply_method_return(message, "s", "na");
if (r < 0)
return r;
m->timestamp.monotonic);
if (m->n_netif > 0) {
- unsigned i;
+ size_t i;
fputs("NETIF=", f);
sd_bus_message *create_message;
int *netif;
- unsigned n_netif;
+ size_t n_netif;
LIST_HEAD(Operation, operations);
pid_t leader;
struct dual_timestamp timestamp;
int *netif;
- unsigned n_netif;
+ size_t n_netif;
} MachineStatusInfo;
static void machine_status_info_clear(MachineStatusInfo *info) {
printf("\t Root: %s\n", i->root_directory);
if (i->n_netif > 0) {
- unsigned c;
+ size_t c;
fputs("\t Iface:", stdout);
_cleanup_strv_free_ char **env_use = NULL;
int r;
+ /* This is the "inner" child process, i.e. the one forked off by the "outer" child process, which is the one
+ * the container manager itself forked off. At the time of clone() it gained its own CLONE_NEWNS, CLONE_NEWPID,
+ * CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWUSER namespaces. Note that it has its own CLONE_NEWNS namespace,
+ * separate from the CLONE_NEWNS created for the "outer" child, and also separate from the host's CLONE_NEWNS
+ * namespace. The reason for having two levels of CLONE_NEWNS namespaces is that the "inner" one is owned by
+ * the CLONE_NEWUSER namespace of the container, while the "outer" one is owned by the host's CLONE_NEWUSER
+ * namespace.
+ *
+ * Note at this point we have no CLONE_NEWNET namespace yet. We'll acquire that one later through
+ * unshare(). See below. */
+
assert(barrier);
assert(directory);
assert(kmsg_socket >= 0);
pid_t pid;
ssize_t l;
+ /* This is the "outer" child process, i.e the one forked off by the container manager itself. It already has
+ * its own CLONE_NEWNS namespace (which was created by the clone()). It still lives in the host's CLONE_NEWPID,
+ * CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWUSER and CLONE_NEWNET namespaces. After it completed a number of
+ * initializations a second child (the "inner" one) is forked off it, and it exits. */
+
assert(barrier);
assert(directory);
assert(console);
#include "def.h"
#include "device-nodes.h"
#include "efivars.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "parse-util.h"
#include "strv.h"
#include "virt.h"
-void boot_entry_free(BootEntry *entry) {
+static void boot_entry_free(BootEntry *entry) {
assert(entry);
- free(entry->filename);
+ free(entry->id);
+ free(entry->path);
free(entry->title);
free(entry->show_title);
free(entry->version);
free(entry->device_tree);
}
-int boot_entry_load(const char *path, BootEntry *entry) {
+static int boot_entry_load(const char *path, BootEntry *entry) {
_cleanup_(boot_entry_free) BootEntry tmp = {};
_cleanup_fclose_ FILE *f = NULL;
unsigned line = 1;
}
b = basename(path);
- tmp.filename = strndup(b, c - b);
- if (!tmp.filename)
+ tmp.id = strndup(b, c - b);
+ if (!tmp.id)
+ return log_oom();
+
+ tmp.path = strdup(path);
+ if (!tmp.path)
return log_oom();
f = fopen(path, "re");
free(config->entries);
}
-int boot_loader_read_conf(const char *path, BootConfig *config) {
+static int boot_loader_read_conf(const char *path, BootConfig *config) {
_cleanup_fclose_ FILE *f = NULL;
unsigned line = 1;
int r;
assert(config);
f = fopen(path, "re");
- if (!f)
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
+
return log_error_errno(errno, "Failed to open \"%s\": %m", path);
+ }
for (;;) {
_cleanup_free_ char *buf = NULL, *field = NULL;
return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
}
- return 0;
+ return 1;
}
static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
- return str_verscmp(a->filename, b->filename);
+ return str_verscmp(a->id, b->id);
}
-int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) {
+static int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) {
_cleanup_strv_free_ char **files = NULL;
char **f;
int r;
/* Add file name to non-unique titles */
for (i = 0; i < n_entries; i++)
if (arr[i]) {
- r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].filename);
+ r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id);
if (r < 0)
return -ENOMEM;
if (config->entry_oneshot)
for (i = config->n_entries - 1; i >= 0; i--)
- if (streq(config->entry_oneshot, config->entries[i].filename)) {
- log_debug("Found default: filename \"%s\" is matched by LoaderEntryOneShot",
- config->entries[i].filename);
+ if (streq(config->entry_oneshot, config->entries[i].id)) {
+ log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot",
+ config->entries[i].id);
return i;
}
if (config->entry_default)
for (i = config->n_entries - 1; i >= 0; i--)
- if (streq(config->entry_default, config->entries[i].filename)) {
- log_debug("Found default: filename \"%s\" is matched by LoaderEntryDefault",
- config->entries[i].filename);
+ if (streq(config->entry_default, config->entries[i].id)) {
+ log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault",
+ config->entries[i].id);
return i;
}
if (config->default_pattern)
for (i = config->n_entries - 1; i >= 0; i--)
- if (fnmatch(config->default_pattern, config->entries[i].filename, FNM_CASEFOLD) == 0) {
- log_debug("Found default: filename \"%s\" is matched by pattern \"%s\"",
- config->entries[i].filename, config->default_pattern);
+ if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) {
+ log_debug("Found default: id \"%s\" is matched by pattern \"%s\"",
+ config->entries[i].id, config->default_pattern);
return i;
}
if (config->n_entries > 0)
- log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].filename);
+ log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id);
else
log_debug("Found no default boot entry :(");
p = strjoina(esp_path, "/loader/loader.conf");
r = boot_loader_read_conf(p, config);
if (r < 0)
- return log_error_errno(r, "Failed to read boot config from \"%s\": %m", p);
+ return r;
p = strjoina(esp_path, "/loader/entries");
r = boot_entries_find(p, &config->entries, &config->n_entries);
if (r < 0)
- return log_error_errno(r, "Failed to read boot entries from \"%s\": %m", p);
+ return r;
r = boot_entries_uniquify(config->entries, config->n_entries);
if (r < 0)
return log_error_errno(r, "Failed to uniquify boot entries: %m");
- r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
- if (r < 0 && r != -ENOENT)
- return log_error_errno(r, "Failed to read EFI var \"LoaderEntryOneShot\": %m");
+ if (is_efi_boot()) {
+ r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to read EFI var \"LoaderEntryOneShot\": %m");
- r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
- if (r < 0 && r != -ENOENT)
- return log_error_errno(r, "Failed to read EFI var \"LoaderEntryDefault\": %m");
+ r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to read EFI var \"LoaderEntryDefault\": %m");
+ }
config->default_entry = boot_entries_select_default(config);
return 0;
struct statfs sfs;
sd_id128_t uuid = SD_ID128_NULL;
uint32_t part = 0;
+ bool relax_checks;
int r;
assert(p);
+ relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
+
/* Non-root user can only check the status, so if an error occured in the following, it does not cause any
* issues. Let's also, silence the error messages. */
- if (statfs(p, &sfs) < 0) {
- /* If we are searching for the mount point, don't generate a log message if we can't find the path */
- if (errno == ENOENT && searching)
- return -ENOENT;
+ if (!relax_checks) {
+ if (statfs(p, &sfs) < 0) {
+ /* If we are searching for the mount point, don't generate a log message if we can't find the path */
+ if (errno == ENOENT && searching)
+ return -ENOENT;
- return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
- "Failed to check file system type of \"%s\": %m", p);
- }
+ return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
+ "Failed to check file system type of \"%s\": %m", p);
+ }
- if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) {
- if (searching)
- return -EADDRNOTAVAIL;
+ if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) {
+ if (searching)
+ return -EADDRNOTAVAIL;
- log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
- return -ENODEV;
+ log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
+ return -ENODEV;
+ }
}
if (stat(p, &st) < 0)
/* In a container we don't have access to block devices, skip this part of the verification, we trust the
* container manager set everything up correctly on its own. Also skip the following verification for non-root user. */
- if (detect_container() > 0 || unprivileged_mode)
+ if (detect_container() > 0 || unprivileged_mode || relax_checks)
goto finish;
#if HAVE_BLKID
return 0;
}
+
+int find_default_boot_entry(
+ const char *esp_path,
+ char **esp_where,
+ BootConfig *config,
+ const BootEntry **e) {
+
+ _cleanup_free_ char *where = NULL;
+ int r;
+
+ assert(config);
+ assert(e);
+
+ r = find_esp_and_warn(esp_path, false, &where, NULL, NULL, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ r = boot_entries_load_config(where, config);
+ if (r < 0)
+ return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", where);
+
+ if (config->default_entry < 0) {
+ log_error("No entry suitable as default, refusing to guess.");
+ return -ENOENT;
+ }
+
+ *e = &config->entries[config->default_entry];
+ log_debug("Found default boot entry in file \"%s\"", (*e)->path);
+
+ if (esp_where)
+ *esp_where = TAKE_PTR(where);
+
+ return 0;
+}
#pragma once
-#include <stdlib.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
-typedef struct BootEntry {
- char *filename;
+#include "sd-id128.h"
+typedef struct BootEntry {
+ char *id; /* This is the file basename without extension */
+ char *path; /* This is the full path to the file */
char *title;
char *show_title;
char *version;
ssize_t default_entry;
} BootConfig;
-void boot_entry_free(BootEntry *entry);
-int boot_entry_load(const char *path, BootEntry *entry);
-int boot_entries_find(const char *dir, BootEntry **entries, size_t *n_entries);
-
-int boot_loader_read_conf(const char *path, BootConfig *config);
void boot_config_free(BootConfig *config);
int boot_entries_load_config(const char *esp_path, BootConfig *config);
static inline const char* boot_entry_title(const BootEntry *entry) {
- return entry->show_title ?: entry->title ?: entry->filename;
+ return entry->show_title ?: entry->title ?: entry->id;
}
int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid);
+
+int find_default_boot_entry(const char *esp_path, char **esp_where, BootConfig *config, const BootEntry **e);
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <linux/fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sd-id128.h"
#include "alloc-util.h"
+#include "chattr-util.h"
#include "dirent-util.h"
#include "efivars.h"
#include "fd-util.h"
#include "macro.h"
#include "parse-util.h"
#include "stdio-util.h"
+#include "strv.h"
#include "time-util.h"
#include "utf8.h"
#include "util.h"
} _packed_;
bool is_efi_boot(void) {
- return access("/sys/firmware/efi", F_OK) >= 0;
+ if (detect_container() > 0)
+ return false;
+
+ return access("/sys/firmware/efi/", F_OK) >= 0;
}
static int read_flag(const char *varname) {
size_t s;
int r;
+ if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
+ return 0;
+
r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
if (r < 0)
return r;
return -EINVAL;
b = *(uint8_t *)v;
- return b > 0;
+ return !!b;
}
bool is_efi_secure_boot(void) {
size_t s;
int r;
- if (!is_efi_boot() || detect_container() > 0)
+ if (!is_efi_boot())
return -EOPNOTSUPP;
r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
size_t s;
int r;
+ /* Let's verify general support first */
r = efi_reboot_to_firmware_supported();
if (r < 0)
return r;
r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
if (r == -ENOENT) {
- /* Some firmware implementations that do support
- * OsIndications and report that with
- * OsIndicationsSupported will remove the
- * OsIndications variable when it is unset. Let's
- * pretend it's 0 then, to hide this implementation
- * detail. Note that this call will return -ENOENT
- * then only if the support for OsIndications is
- * missing entirely, as determined by
- * efi_reboot_to_firmware_supported() above. */
+ /* Some firmware implementations that do support OsIndications and report that with
+ * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's pretend it's 0
+ * then, to hide this implementation detail. Note that this call will return -ENOENT then only if the
+ * support for OsIndications is missing entirely, as determined by efi_reboot_to_firmware_supported()
+ * above. */
*os_indication = 0;
return 0;
} else if (r < 0)
} _packed_ * _cleanup_free_ buf = NULL;
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -1;
+ bool saved_flags_valid = false;
+ unsigned saved_flags;
+ int r;
assert(name);
assert(value || size == 0);
name, SD_ID128_FORMAT_VAL(vendor)) < 0)
return -ENOMEM;
+ /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
+ * them for accidental removal and modification. We are not changing these variables accidentally however,
+ * hence let's unset the bit first. */
+
+ r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
+
+ saved_flags_valid = r >= 0;
+
if (size == 0) {
- if (unlink(p) < 0)
- return -errno;
+ if (unlink(p) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
return 0;
}
fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
- if (fd < 0)
- return -errno;
+ if (fd < 0) {
+ r = -errno;
+ goto finish;
+ }
buf = malloc(sizeof(uint32_t) + size);
- if (!buf)
- return -ENOMEM;
+ if (!buf) {
+ r = -ENOMEM;
+ goto finish;
+ }
buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
memcpy(buf->buf, value, size);
- return loop_write(fd, buf, sizeof(uint32_t) + size, false);
+ r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
+ if (r < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+ if (saved_flags_valid) {
+ int q;
+
+ /* Restore the original flags field, just in case */
+ if (fd < 0)
+ q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
+ else
+ q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
+ if (q < 0)
+ log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
+ }
+
+ return r;
}
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
sd_id128_t p_uuid = SD_ID128_NULL;
int r;
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
xsprintf(boot_id, "Boot%04X", id);
r = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
if (r < 0)
return s;
}
-int efi_add_boot_option(uint16_t id, const char *title,
- uint32_t part, uint64_t pstart, uint64_t psize,
- sd_id128_t part_uuid, const char *path) {
- char boot_id[9];
- size_t size;
- size_t title_len;
- size_t path_len;
+int efi_add_boot_option(
+ uint16_t id,
+ const char *title,
+ uint32_t part,
+ uint64_t pstart,
+ uint64_t psize,
+ sd_id128_t part_uuid,
+ const char *path) {
+
+ size_t size, title_len, path_len;
+ _cleanup_free_ char *buf = NULL;
struct boot_option *option;
struct device_path *devicep;
- _cleanup_free_ char *buf = NULL;
+ char boot_id[9];
+
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
title_len = (strlen(title)+1) * 2;
path_len = (strlen(path)+1) * 2;
- buf = calloc(sizeof(struct boot_option) + title_len +
- sizeof(struct drive_path) +
- sizeof(struct device_path) + path_len, 1);
+ buf = malloc0(sizeof(struct boot_option) + title_len +
+ sizeof(struct drive_path) +
+ sizeof(struct device_path) + path_len);
if (!buf)
return -ENOMEM;
int efi_remove_boot_option(uint16_t id) {
char boot_id[9];
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
xsprintf(boot_id, "Boot%04X", id);
return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0);
}
size_t l;
int r;
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
if (r < 0)
return r;
}
int efi_set_boot_order(uint16_t *order, size_t n) {
+
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t));
}
static int boot_id_hex(const char s[4]) {
- int i;
- int id = 0;
+ int id = 0, i;
for (i = 0; i < 4; i++)
if (s[i] >= '0' && s[i] <= '9')
int efi_get_boot_options(uint16_t **options) {
_cleanup_closedir_ DIR *dir = NULL;
- struct dirent *de;
_cleanup_free_ uint16_t *list = NULL;
+ struct dirent *de;
size_t alloc = 0;
int count = 0;
assert(options);
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
dir = opendir("/sys/firmware/efi/efivars/");
if (!dir)
return -errno;
assert(firmware);
assert(loader);
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
if (r < 0)
return r;
_cleanup_free_ char *p = NULL;
int r, parsed[16];
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
if (r < 0)
return r;
return 0;
}
+int efi_loader_get_entries(char ***ret) {
+ _cleanup_free_ char16_t *entries = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ size_t size, i, start;
+ int r;
+
+ assert(ret);
+
+ if (!is_efi_boot())
+ return -EOPNOTSUPP;
+
+ r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderEntries", NULL, (void**) &entries, &size);
+ if (r < 0)
+ return r;
+
+ /* The variable contains a series of individually NUL terminated UTF-16 strings. */
+
+ for (i = 0, start = 0;; i++) {
+ char *decoded;
+ bool end;
+
+ /* Is this the end of the variable's data? */
+ end = i * sizeof(char16_t) >= size;
+
+ /* Are we in the middle of a string? (i.e. not at the end of the variable, nor at a NUL terminator?) If
+ * so, let's go to the next entry. */
+ if (!end && entries[i] != 0)
+ continue;
+
+ /* We reached the end of a string, let's decode it into UTF-8 */
+ decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
+ if (!decoded)
+ return -ENOMEM;
+
+ r = strv_consume(&l, decoded);
+ if (r < 0)
+ return r;
+
+ /* We reached the end of the variable */
+ if (end)
+ break;
+
+ /* Continue after the NUL byte */
+ start = i + 1;
+ }
+
+ *ret = TAKE_PTR(l);
+ return 0;
+}
+
#endif
char *efi_tilt_backslashes(char *s) {
int efi_loader_get_device_part_uuid(sd_id128_t *u);
int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader);
+int efi_loader_get_entries(char ***ret);
+
#else
static inline bool is_efi_boot(void) {
return -EOPNOTSUPP;
}
+static inline int efi_loader_get_entries(char ***ret) {
+ return -EOPNOTSUPP;
+}
+
#endif
char *efi_tilt_backslashes(char *s);
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
int r;
const void *data;
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
const void *data;
size_t length;
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
sd_id128_t boot_id;
char sid[33];
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
uint64_t realtime, monotonic;
_cleanup_free_ char *cursor = NULL;
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) {
+ const size_t highlight[2]) {
const void *data;
size_t l;
unsigned n_columns,
OutputFlags flags,
Set *output_fields,
- size_t highlight[2]) = {
+ const size_t highlight[2]) = {
[OUTPUT_SHORT] = output_short,
[OUTPUT_SHORT_ISO] = output_short,
unsigned n_columns,
OutputFlags flags,
char **output_fields,
- size_t highlight[2],
+ const size_t highlight[2],
bool *ellipsized) {
int ret;
unsigned n_columns,
OutputFlags flags,
char **output_fields,
- size_t highlight[2],
+ const size_t highlight[2],
bool *ellipsized);
int show_journal(
FILE *f,
case IMAGE_DIRECTORY:
/* Allow deletion of read-only directories */
- (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL);
+ (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL, NULL);
r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
if (r < 0)
return r;
(void) read_attr_path(i->path, &file_attr);
if (file_attr & FS_IMMUTABLE_FL)
- (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL);
+ (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL, NULL);
_fallthrough_;
case IMAGE_SUBVOLUME:
/* Restore the immutable bit, if it was set before */
if (file_attr & FS_IMMUTABLE_FL)
- (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
+ (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
free_and_replace(i->path, new_path);
free_and_replace(i->name, nn);
a read-only subvolume, but at least something, and
we can read the value back. */
- r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL);
+ r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL, NULL);
if (r < 0)
return r;
#include "log.h"
#include "macro.h"
#include "ptyfwd.h"
+#include "terminal-util.h"
#include "time-util.h"
struct PTYForward {
}
/* STDIN/STDOUT should not be nonblocking normally, so let's unconditionally reset it */
- fd_nonblock(STDIN_FILENO, false);
- fd_nonblock(STDOUT_FILENO, false);
+ (void) fd_nonblock(STDIN_FILENO, false);
+ (void) fd_nonblock(STDOUT_FILENO, false);
}
static int pty_forward_done(PTYForward *f, int rcode) {
f->master = master;
- if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
- (void) ioctl(master, TIOCSWINSZ, &ws);
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
+ /* If we can't get the resolution from the output fd, then use our internal, regular width/height,
+ * i.e. something derived from $COLUMNS and $LINES if set. */
+
+ ws = (struct winsize) {
+ .ws_row = lines(),
+ .ws_col = columns(),
+ };
+ }
+
+ (void) ioctl(master, TIOCSWINSZ, &ws);
if (!(flags & PTY_FORWARD_READ_ONLY)) {
if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) {
#include "sd-id128.h"
#include "alloc-util.h"
+#include "bootspec.h"
#include "conf-parser.h"
#include "def.h"
#include "env-util.h"
#include "macro.h"
#include "parse-util.h"
#include "path-util.h"
+#include "proc-cmdline.h"
#include "sleep-config.h"
#include "string-util.h"
#include "strv.h"
-int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t *_delay) {
-
+int parse_sleep_config(const char *verb, bool *ret_allow, char ***ret_modes, char ***ret_states, usec_t *ret_delay) {
+ int allow_suspend = -1, allow_hibernate = -1,
+ allow_s2h = -1, allow_hybrid_sleep = -1;
+ bool allow;
_cleanup_strv_free_ char
**suspend_mode = NULL, **suspend_state = NULL,
**hibernate_mode = NULL, **hibernate_state = NULL,
usec_t delay = 180 * USEC_PER_MINUTE;
const ConfigTableItem items[] = {
- { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
- { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state },
- { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode },
- { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state },
- { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode },
- { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state },
- { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &delay},
+ { "Sleep", "AllowSuspend", config_parse_tristate, 0, &allow_suspend },
+ { "Sleep", "AllowHibernation", config_parse_tristate, 0, &allow_hibernate },
+ { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h },
+ { "Sleep", "AllowHybridSleep", config_parse_tristate, 0, &allow_hybrid_sleep },
+
+ { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
+ { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state },
+ { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode },
+ { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state },
+ { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode },
+ { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state },
+
+ { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &delay},
{}
};
CONFIG_PARSE_WARN, NULL);
if (streq(verb, "suspend")) {
+ allow = allow_suspend != 0;
+
/* empty by default */
modes = TAKE_PTR(suspend_mode);
states = strv_new("mem", "standby", "freeze", NULL);
} else if (streq(verb, "hibernate")) {
+ allow = allow_hibernate != 0;
+
if (hibernate_mode)
modes = TAKE_PTR(hibernate_mode);
else
states = strv_new("disk", NULL);
} else if (streq(verb, "hybrid-sleep")) {
+ allow = allow_hybrid_sleep > 0 ||
+ (allow_suspend != 0 && allow_hibernate != 0);
+
if (hybrid_mode)
modes = TAKE_PTR(hybrid_mode);
else
else
states = strv_new("disk", NULL);
- } else if (streq(verb, "suspend-then-hibernate"))
+ } else if (streq(verb, "suspend-then-hibernate")) {
+ allow = allow_s2h > 0 ||
+ (allow_suspend != 0 && allow_hibernate != 0);
+
modes = states = NULL;
- else
+ } else
assert_not_reached("what verb");
if ((!modes && STR_IN_SET(verb, "hibernate", "hybrid-sleep")) ||
(!states && !streq(verb, "suspend-then-hibernate")))
return log_oom();
- if (_modes)
- *_modes = TAKE_PTR(modes);
- if (_states)
- *_states = TAKE_PTR(states);
- if (_delay)
- *_delay = delay;
+ if (ret_allow)
+ *ret_allow = allow;
+ if (ret_modes)
+ *ret_modes = TAKE_PTR(modes);
+ if (ret_states)
+ *ret_states = TAKE_PTR(states);
+ if (ret_delay)
+ *ret_delay = delay;
return 0;
}
}
r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD;
- log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
- r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
+ log_debug("%s swap for hibernation, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
+ r ? "Enough" : "Not enough", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
return r;
}
+static int check_resume_keys(const char *key, const char *value, void *data) {
+ assert_se(key);
+ assert_se(data);
+
+ int *resume = data;
+
+ if (*resume == 0)
+ /* Exit if we already know we can't resume. */
+ return 0;
+
+ if (streq(key, "noresume")) {
+ log_debug("Found \"noresume\" on the kernel command line, hibernation is disabled.");
+ *resume = 0;
+
+ } else if (streq(key, "resume")) {
+ log_debug("Found resume= option on the kernel command line, hibernation is possible.");
+ *resume = 1;
+ }
+
+ return 0;
+}
+
+static int resume_configured_in_options(const char *options) {
+ int resume = -1, r;
+
+ /* We don't use PROC_CMDLINE_STRIP_RD_PREFIX here, so rd.resume is *not* supported. */
+ r = proc_cmdline_parse_given(options, check_resume_keys, &resume, 0);
+ if (r < 0)
+ return r;
+
+ if (resume < 0)
+ log_debug("Couldn't find resume= option, hibernation is disabled.");
+ return resume > 0;
+}
+
+static int resume_configured(void) {
+ _cleanup_(boot_config_free) BootConfig config = {};
+ const BootEntry *e;
+ int r;
+
+ /* Check whether a valid resume= option is present. If possible, we query the boot options
+ * for the default kernel. If the system is not using sd-boot, fall back to checking the
+ * current kernel command line. This is not perfect, but should suffice for most cases. */
+
+ r = find_default_boot_entry(NULL, NULL, &config, &e);
+ if (r == -ENOKEY)
+ log_debug_errno(r, "Cannot find the ESP partition mount point, falling back to other checks.");
+ else if (r < 0)
+ return log_debug_errno(r, "Cannot read boot configuration from ESP, assuming hibernation is not possible.");
+ else {
+ _cleanup_free_ char *options = NULL;
+
+ options = strv_join(e->options, " ");
+ if (!options)
+ return log_oom();
+
+ r = resume_configured_in_options(options);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse kernel options in \"%s\": %m",
+ strnull(e->path));
+ return r;
+ }
+
+ /* If we can't figure out the default boot entry, let's fall back to current kernel cmdline */
+ _cleanup_free_ char *line = NULL;
+ r = proc_cmdline(&line);
+ if (IN_SET(r, -EPERM, -EACCES, -ENOENT))
+ log_debug_errno(r, "Cannot access /proc/cmdline: %m");
+ else if (r < 0)
+ return log_error_errno(r, "Failed to query /proc/cmdline: %m");
+ else {
+ r = resume_configured_in_options(line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse kernel proc cmdline: %m");
+
+ return r;
+ }
+
+ log_debug("Couldn't detect any resume mechanism, hibernation is disabled.");
+ return false;
+}
+
static int kernel_exists(void) {
struct utsname u;
sd_id128_t m;
return 0;
}
+static int can_sleep_internal(const char *verb, bool check_allowed);
+
static bool can_s2h(void) {
const char *p;
int r;
}
FOREACH_STRING(p, "suspend", "hibernate") {
- r = can_sleep(p);
- if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM)) {
+ r = can_sleep_internal(p, false);
+ if (IN_SET(r, 0, -ENOSPC, -ENOMEDIUM, -EADV)) {
log_debug("Unable to %s system.", p);
return false;
}
return true;
}
-int can_sleep(const char *verb) {
+static int can_sleep_internal(const char *verb, bool check_allowed) {
+ bool allow;
_cleanup_strv_free_ char **modes = NULL, **states = NULL;
int r;
assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"));
- if (streq(verb, "suspend-then-hibernate"))
- return can_s2h();
-
- r = parse_sleep_config(verb, &modes, &states, NULL);
+ r = parse_sleep_config(verb, &allow, &modes, &states, NULL);
if (r < 0)
return false;
+ if (check_allowed && !allow) {
+ log_debug("Sleep mode \"%s\" is disabled by configuration.", verb);
+ return false;
+ }
+
+ if (streq(verb, "suspend-then-hibernate"))
+ return can_s2h();
+
if (!can_sleep_state(states) || !can_sleep_disk(modes))
return false;
if (!enough_swap_for_hibernation())
return -ENOSPC;
+ r = resume_configured();
+ if (r <= 0)
+ /* We squash all errors (e.g. EPERM) into a single value for reporting. */
+ return -EADV;
+
return true;
}
+
+int can_sleep(const char *verb) {
+ return can_sleep_internal(verb, true);
+}
#include "time-util.h"
int read_fiemap(int fd, struct fiemap **ret);
-int parse_sleep_config(const char *verb, char ***modes, char ***states, usec_t *delay);
+int parse_sleep_config(const char *verb, bool *ret_allow, char ***ret_modes, char ***ret_states, usec_t *ret_delay);
int find_hibernate_location(char **device, char **type, size_t *size, size_t *used);
int can_sleep(const char *verb);
}
static int execute(char **modes, char **states) {
-
char *arguments[] = {
NULL,
(char*) "pre",
char time_str[DECIMAL_STR_MAX(uint64_t)];
int r;
- r = parse_sleep_config("suspend", &suspend_modes, &suspend_states,
- NULL);
+ r = parse_sleep_config("suspend", NULL, &suspend_modes, &suspend_states, NULL);
if (r < 0)
return r;
- r = parse_sleep_config("hibernate", &hibernate_modes,
- &hibernate_states, NULL);
+ r = parse_sleep_config("hibernate", NULL, &hibernate_modes, &hibernate_states, NULL);
if (r < 0)
return r;
}
int main(int argc, char *argv[]) {
+ bool allow;
_cleanup_strv_free_ char **modes = NULL, **states = NULL;
usec_t delay = 0;
int r;
if (r <= 0)
goto finish;
- r = parse_sleep_config(arg_verb, &modes, &states, &delay);
+ r = parse_sleep_config(arg_verb, &allow, &modes, &states, &delay);
if (r < 0)
goto finish;
+ if (!allow) {
+ log_error("Sleep mode \"%s\" is disabled by configuration, refusing.", arg_verb);
+ return EXIT_FAILURE;
+ }
+
if (streq(arg_verb, "suspend-then-hibernate"))
r = execute_s2h(delay);
else
if (access(KEXEC, X_OK) < 0)
return log_error_errno(errno, KEXEC" is not available: %m");
- r = find_esp_and_warn(arg_esp_path, false, &where, NULL, NULL, NULL, NULL);
- if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn about this case */
+ r = find_default_boot_entry(arg_esp_path, &where, &config, &e);
+ if (r == -ENOKEY) /* find_default_boot_entry() doesn't warn about this case */
return log_error_errno(r, "Cannot find the ESP partition mount point.");
- if (r < 0) /* But it logs about all these cases, hence don't log here again */
- return r;
-
- r = boot_entries_load_config(where, &config);
if (r < 0)
- return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m", where);
-
- if (config.default_entry < 0) {
- log_error("No entry suitable as default, refusing to guess.");
- return -ENOENT;
- }
- e = &config.entries[config.default_entry];
+ /* But it logs about all these cases, hence don't log here again */
+ return r;
if (strv_length(e->initrd) > 1) {
log_error("Boot entry specifies multiple initrds, which is not supported currently.");
#include "util.h"
static int test_acpi_fpdt(void) {
- usec_t loader_start;
- usec_t loader_exit;
- char ts_start[FORMAT_TIMESPAN_MAX];
- char ts_exit[FORMAT_TIMESPAN_MAX];
- char ts_span[FORMAT_TIMESPAN_MAX];
+ char ts_start[FORMAT_TIMESPAN_MAX], ts_exit[FORMAT_TIMESPAN_MAX], ts_span[FORMAT_TIMESPAN_MAX];
+ usec_t loader_start, loader_exit;
int r;
r = acpi_get_boot_usec(&loader_start, &loader_exit);
if (r < 0) {
bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES) || r == -ENODATA;
- log_full_errno(ok ? LOG_DEBUG : LOG_ERR,
- r, "Failed to read ACPI FPDT: %m");
+ log_full_errno(ok ? LOG_DEBUG : LOG_ERR, r, "Failed to read ACPI FPDT: %m");
return ok ? 0 : r;
}
}
static int test_efi_loader(void) {
- usec_t loader_start;
- usec_t loader_exit;
- char ts_start[FORMAT_TIMESPAN_MAX];
- char ts_exit[FORMAT_TIMESPAN_MAX];
- char ts_span[FORMAT_TIMESPAN_MAX];
+ char ts_start[FORMAT_TIMESPAN_MAX], ts_exit[FORMAT_TIMESPAN_MAX], ts_span[FORMAT_TIMESPAN_MAX];
+ usec_t loader_start, loader_exit;
int r;
r = efi_loader_get_boot_usec(&loader_start, &loader_exit);
if (r < 0) {
- bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES);
+ bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES) || r == -EOPNOTSUPP;
- log_full_errno(ok ? LOG_DEBUG : LOG_ERR,
- r, "Failed to read EFI loader data: %m");
+ log_full_errno(ok ? LOG_DEBUG : LOG_ERR, r, "Failed to read EFI loader data: %m");
return ok ? 0 : r;
}
static int test_boot_timestamps(void) {
char s[MAX(FORMAT_TIMESPAN_MAX, FORMAT_TIMESTAMP_MAX)];
- int r;
dual_timestamp fw, l, k;
+ int r;
dual_timestamp_from_monotonic(&k, 0);
r = boot_timestamps(NULL, &fw, &l);
if (r < 0) {
- bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES);
+ bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES) || r == -EOPNOTSUPP;
- log_full_errno(ok ? LOG_DEBUG : LOG_ERR,
- r, "Failed to read variables: %m");
+ log_full_errno(ok ? LOG_DEBUG : LOG_ERR, r, "Failed to read variables: %m");
return ok ? 0 : r;
}
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
+#include "env-util.h"
#include "log.h"
#include "macro.h"
#include "proc-cmdline.h"
}
static void test_proc_cmdline_parse(void) {
- assert_se(proc_cmdline_parse(parse_item, &obj, true) >= 0);
+ log_info("/* %s */", __func__);
+
+ assert_se(proc_cmdline_parse(parse_item, &obj, PROC_CMDLINE_STRIP_RD_PREFIX) >= 0);
}
-static void test_runlevel_to_target(void) {
- in_initrd_force(false);
- assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
- assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET));
- assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL));
+static void test_proc_cmdline_override(void) {
+ log_info("/* %s */", __func__);
- in_initrd_force(true);
- assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
- assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("3"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
+ assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm") == 0);
+
+ /* Test if the override works */
+ _cleanup_free_ char *line = NULL, *value = NULL;
+ assert_se(proc_cmdline(&line) >= 0);
+
+ /* Test if parsing makes uses of the override */
+ assert_se(streq(line, "foo_bar=quux wuff-piep=tuet zumm"));
+ assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
+}
+
+static int parse_item_given(const char *key, const char *value, void *data) {
+ assert_se(key);
+ assert_se(data);
+
+ bool *strip = data;
+
+ log_info("%s: option <%s> = <%s>", __func__, key, strna(value));
+ if (streq(key, "foo_bar"))
+ assert_se(streq(value, "quux"));
+ else if (streq(key, "wuff-piep"))
+ assert_se(streq(value, "tuet "));
+ else if (in_initrd() && *strip && streq(key, "zumm"))
+ assert_se(!value);
+ else if (in_initrd() && !*strip && streq(key, "rd.zumm"))
+ assert_se(!value);
+ else
+ assert_not_reached("Bad key!");
+
+ return 0;
+}
+
+static void test_proc_cmdline_given(bool flip_initrd) {
+ log_info("/* %s (flip: %s) */", __func__, yes_no(flip_initrd));
+
+ if (flip_initrd)
+ in_initrd_force(!in_initrd());
+
+ bool t = true, f = false;
+ assert_se(proc_cmdline_parse_given("foo_bar=quux wuff-piep=\"tuet \" rd.zumm",
+ parse_item_given, &t, PROC_CMDLINE_STRIP_RD_PREFIX) >= 0);
+
+ assert_se(proc_cmdline_parse_given("foo_bar=quux wuff-piep=\"tuet \" rd.zumm",
+ parse_item_given, &f, 0) >= 0);
+
+
+ if (flip_initrd)
+ in_initrd_force(!in_initrd());
}
static void test_proc_cmdline_get_key(void) {
_cleanup_free_ char *value = NULL;
+ log_info("/* %s */", __func__);
putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm");
assert_se(proc_cmdline_get_key("", 0, &value) == -EINVAL);
static void test_proc_cmdline_get_bool(void) {
bool value = false;
+ log_info("/* %s */", __func__);
putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep");
assert_se(proc_cmdline_get_bool("", &value) == -EINVAL);
}
static void test_proc_cmdline_key_streq(void) {
+ log_info("/* %s */", __func__);
assert_se(proc_cmdline_key_streq("", ""));
assert_se(proc_cmdline_key_streq("a", "a"));
}
static void test_proc_cmdline_key_startswith(void) {
+ log_info("/* %s */", __func__);
assert_se(proc_cmdline_key_startswith("", ""));
assert_se(proc_cmdline_key_startswith("x", ""));
assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx"));
}
+static void test_runlevel_to_target(void) {
+ log_info("/* %s */", __func__);
+
+ in_initrd_force(false);
+ assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
+ assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET));
+ assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL));
+
+ in_initrd_force(true);
+ assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
+ assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("3"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
+}
+
int main(void) {
log_parse_environment();
log_open();
test_proc_cmdline_parse();
+ test_proc_cmdline_override();
+ test_proc_cmdline_given(false);
+ /* Repeat the same thing, but now flip our ininitrdness */
+ test_proc_cmdline_given(true);
test_proc_cmdline_key_streq();
test_proc_cmdline_key_startswith();
test_proc_cmdline_get_key();
static void test_parse_sleep_config(void) {
const char *verb;
+ log_info("/* %s */", __func__);
+
FOREACH_STRING(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate")
- assert_se(parse_sleep_config(verb, NULL, NULL, NULL) == 0);
+ assert_se(parse_sleep_config(verb, NULL, NULL, NULL, NULL) == 0);
}
static int test_fiemap(const char *path) {
_cleanup_close_ int fd = -1;
int r;
+ log_info("/* %s */", __func__);
+
fd = open(path, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
if (fd < 0)
return log_error_errno(errno, "failed to open %s: %m", path);
**freez = strv_new("freeze", NULL);
int r;
- log_info("/* configuration */");
+ log_info("/* %s */", __func__);
+
+ log_info("/= configuration =/");
log_info("Standby configured: %s", yes_no(can_sleep_state(standby) > 0));
log_info("Suspend configured: %s", yes_no(can_sleep_state(mem) > 0));
log_info("Hibernate configured: %s", yes_no(can_sleep_state(disk) > 0));
log_info("Hibernate+Shutdown configured: %s", yes_no(can_sleep_disk(shutdown) > 0));
log_info("Freeze configured: %s", yes_no(can_sleep_state(freez) > 0));
- log_info("/* running system */");
+ log_info("/= running system =/");
r = can_sleep("suspend");
log_info("Suspend configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r));
r = can_sleep("hibernate");
_cleanup_(udev_device_unrefp) struct udev_device *dev = NULL;
_cleanup_(udev_rules_unrefp) struct udev_rules *rules = NULL;
const char *devpath, *action;
- int r;
test_setup_logging(LOG_INFO);
- r = fake_filesystems();
- if (r < 0)
+ if (argc != 3) {
+ log_error("This program needs two arguments, %d given", argc - 1);
+ return EXIT_FAILURE;
+ }
+
+ if (fake_filesystems() < 0)
return EXIT_FAILURE;
log_debug("version %s", PACKAGE_VERSION);
mac_selinux_init();
action = argv[1];
- if (!action) {
- log_error("action missing");
- goto out;
- }
-
devpath = argv[2];
- if (!devpath) {
- log_error("devpath missing");
- goto out;
- }
rules = udev_rules_new(1);
goto out;
}
- event = udev_event_new(dev);
- assert_se(event);
+ assert_se(event = udev_event_new(dev));
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0);
mode |= S_IFCHR;
if (!streq(action, "remove")) {
- mkdir_parents_label(udev_device_get_devnode(dev), 0755);
- mknod(udev_device_get_devnode(dev), mode, udev_device_get_devnum(dev));
+ (void) mkdir_parents_label(udev_device_get_devnode(dev), 0755);
+ assert_se(mknod(udev_device_get_devnode(dev), mode, udev_device_get_devnum(dev)) == 0);
} else {
- unlink(udev_device_get_devnode(dev));
- rmdir_parents(udev_device_get_devnode(dev), "/");
+ assert_se(unlink(udev_device_get_devnode(dev)) == 0);
+ (void) rmdir_parents(udev_device_get_devnode(dev), "/");
}
}
- udev_event_execute_rules(event,
- 3 * USEC_PER_SEC, USEC_PER_SEC,
- NULL,
- rules);
- udev_event_execute_run(event,
- 3 * USEC_PER_SEC, USEC_PER_SEC);
+ udev_event_execute_rules(event, 3 * USEC_PER_SEC, USEC_PER_SEC, NULL, rules);
+ udev_event_execute_run(event, 3 * USEC_PER_SEC, USEC_PER_SEC);
out:
mac_selinux_finish();
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return EXIT_SUCCESS;
}
if (procfs_fd < 0)
return log_error_errno(procfs_fd, "Failed to re-open '%s': %m", path);
- r = chattr_fd(procfs_fd, f, item->attribute_mask);
+ r = chattr_fd(procfs_fd, f, item->attribute_mask, NULL);
if (r < 0)
log_full_errno(IN_SET(r, -ENOTTY, -EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING,
r,
meson.source_root(),
'@OUTPUT@',
'fuzzers',
- '-Db_lundef=false -Db_sanitize=address'])
+ '-Db_lundef=false -Db_sanitize=address',
+ ' '.join(cc.cmd_array()),
+ ' '.join(meson.get_compiler('cpp').cmd_array())])
sanitizers = [['address', sanitize_address]]
dst="$2"
target="$3"
options="$4"
+CC="$5"
+CXX="$6"
-[ -f "$dst/ninja.build" ] || meson "$src" "$dst" $options
+[ -f "$dst/ninja.build" ] || CC="$CC" CXX="$CXX" meson "$src" "$dst" $options
# Locate ninja binary, on CentOS 7 it is called ninja-build, so
# use that name if available.