]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #10307 from poettering/portable-path
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 9 Oct 2018 12:58:22 +0000 (14:58 +0200)
committerGitHub <noreply@github.com>
Tue, 9 Oct 2018 12:58:22 +0000 (14:58 +0200)
finishing touches to portable services, and let's move portablectl to /usr/bin to make it official

54 files changed:
TODO
docs/ENVIRONMENT.md
hwdb/60-keyboard.hwdb
man/bootctl.xml
man/kernel-command-line.xml
man/systemd-hibernate-resume-generator.xml
man/systemd-sleep.conf.xml
src/basic/btrfs-util.c
src/basic/chattr-util.c
src/basic/chattr-util.h
src/basic/copy.c
src/basic/mount-util.c
src/basic/proc-cmdline.c
src/basic/proc-cmdline.h
src/basic/terminal-util.c
src/basic/util.c
src/boot/bootctl.c
src/core/killall.c
src/core/main.c
src/core/unit.c
src/hibernate-resume/hibernate-resume-generator.c
src/import/import-raw.c
src/import/pull-raw.c
src/journal/journal-file.c
src/journal/journalctl.c
src/journal/journald-context.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-hwdb/sd-hwdb.c
src/libsystemd/sd-netlink/sd-netlink.c
src/login/logind-dbus.c
src/machine/machine.c
src/machine/machine.h
src/machine/machinectl.c
src/nspawn/nspawn.c
src/shared/bootspec.c
src/shared/bootspec.h
src/shared/efivars.c
src/shared/efivars.h
src/shared/logs-show.c
src/shared/logs-show.h
src/shared/machine-image.c
src/shared/ptyfwd.c
src/shared/sleep-config.c
src/shared/sleep-config.h
src/sleep/sleep.c
src/systemctl/systemctl.c
src/test/test-boot-timestamps.c
src/test/test-proc-cmdline.c
src/test/test-sleep.c
src/test/test-udev.c
src/tmpfiles/tmpfiles.c
test/fuzz/meson.build
tools/meson-build.sh

diff --git a/TODO b/TODO
index 38f98c008e2e84ad6e12aec6b09378e8888d9e91..b78892a9168faec87084ee234c546effcf82daac 100644 (file)
--- a/TODO
+++ b/TODO
@@ -89,11 +89,8 @@ Features:
 
   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.
index 0c991520c4a69d67b6d4e34b6a22ffc6082806c7..016a89787dd14503b107dac25720534e34762a74 100644 (file)
@@ -37,6 +37,10 @@ All tools:
   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
@@ -108,6 +112,13 @@ systemd-timedated:
   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
index cd37fdc13493909b81ee0ed9dd21c55cc17a3590..a439a137862d61c715714c1fcec5cefd17aabe64 100644 (file)
@@ -669,6 +669,7 @@ evdev:name:ThinkPad Extra Buttons:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*
  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*
index defe095dde2f9b2ea02888ad427ec11e89b8602f..816b618087425d8de4b6d5fd72b9687b8034ecef 100644 (file)
   <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>
@@ -45,8 +45,6 @@
     <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>
index 0545f9d84b2ab43f0b4c8a99d476b191d5bb2b46..43b3a3667e6cbe7712d767a1ee259fa2e4937f64 100644 (file)
@@ -55,6 +55,7 @@
         <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>
index 86d45dc4af3c89b560ad2327d6ad56174580d6c6..8f0cc5d0445a6d5861e47fb73277c95b65c4fd55 100644 (file)
   <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>
 
index 10bbc8c76a0d43a36540ded1c255b57a83820d14..96e6d5e452369e95edc3190a5ac790a34e651928 100644 (file)
     <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>
index 0308048b7f8da9c49026d6d6e66d8f0a38d541af..1c9c5fd162b9ce890465a61fc5dd37add1889617 100644 (file)
@@ -1715,7 +1715,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
                                  * 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)
index 4ec14515eb38d5b44be64c8eac4766df8e78eb41..235cfb9bd752e19df84500bd1cffa7a9cf8d1076 100644 (file)
@@ -10,7 +10,7 @@
 #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;
 
@@ -28,23 +28,29 @@ int chattr_fd(int fd, unsigned value, unsigned mask) {
         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);
@@ -56,7 +62,7 @@ int chattr_path(const char *p, unsigned value, unsigned mask) {
         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) {
index 0c0816344a2eca521e383e659447640845a9fa73..7570bba2fa44d96ea01cf61a3ddbff06c5297b0e 100644 (file)
@@ -1,8 +1,8 @@
 /* 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);
index e06a503a2948c4a57a6b25eab2d0007ccb30896a..714cbb8fc3d1dda179839ef61c0624a1a6d86679 100644 (file)
@@ -695,7 +695,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
         }
 
         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) {
@@ -743,7 +743,7 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned cha
         }
 
         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)
index ddf00addff3935042b47929e4fc0b5f9e3de4222..38edef9823c0cc6a866ab754bf207b777265d7f3 100644 (file)
@@ -840,8 +840,8 @@ int mount_verbose(
                           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;
 }
 
index add481c2ae61fd9c91694ef0a3985a5816f629fa..b386c705e118e4ed34612c603c5db9a422c14e83 100644 (file)
@@ -39,18 +39,12 @@ int proc_cmdline(char **ret) {
                 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;
@@ -86,15 +80,26 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned fla
         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);
 
index 4a9e6e0f6201173729a2411de00c22e92d3a2c0a..ffc45fddb964113e33a020f68f5f9d9f6d5a5313 100644 (file)
@@ -14,6 +14,7 @@ typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *da
 
 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);
index a6671542701f2dd30ec0913e2fd09fe52560552a..c2aa75c6a82a80ab36abdc14a70e3e7a97191d57 100644 (file)
@@ -819,11 +819,11 @@ unsigned columns(void) {
         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;
@@ -853,11 +853,11 @@ unsigned lines(void) {
         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;
index 081c63c898c12318a3c7379e82a3ddf33710d21a..0da963f4af876548846ebc02860aadcefdb07003 100644 (file)
@@ -23,6 +23,7 @@
 #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"
@@ -106,6 +107,7 @@ int prot_from_flags(int flags) {
 
 bool in_initrd(void) {
         struct statfs s;
+        int r;
 
         if (saved_in_initrd >= 0)
                 return saved_in_initrd;
@@ -120,9 +122,16 @@ bool in_initrd(void) {
          * 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;
 }
index 8680b3a6aeb5c66451be328d9a22101451b6bd32..c97d21c22862301f1de40a2cddc4fa1498899098 100644 (file)
@@ -29,6 +29,7 @@
 #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"
@@ -36,6 +37,7 @@
 #include "strv.h"
 #include "terminal-util.h"
 #include "umask-util.h"
+#include "utf8.h"
 #include "util.h"
 #include "verbs.h"
 #include "virt.h"
@@ -43,6 +45,7 @@
 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,
@@ -155,7 +158,7 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
                 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++;
@@ -167,7 +170,7 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
 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");
@@ -181,13 +184,13 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) {
 
         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;
 
@@ -213,7 +216,7 @@ static int print_efi_option(uint16_t id, bool in_order) {
 
         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));
@@ -224,9 +227,8 @@ static int print_efi_option(uint16_t id, bool in_order) {
 }
 
 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)
@@ -243,7 +245,7 @@ static int status_variables(void) {
                 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);
 
@@ -264,49 +266,65 @@ static int status_variables(void) {
         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;
@@ -806,9 +824,9 @@ static int install_loader_config(const char *esp_path) {
                 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)
@@ -840,16 +858,19 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --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;
 }
@@ -859,6 +880,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_PATH = 0x100,
                 ARG_VERSION,
                 ARG_NO_VARIABLES,
+                ARG_NO_PAGER,
         };
 
         static const struct option options[] = {
@@ -867,7 +889,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "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;
@@ -899,6 +922,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_touch_variables = false;
                         break;
 
+                case ARG_NO_PAGER:
+                        arg_no_pager = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -938,6 +965,8 @@ static int verb_status(int argc, char *argv[], void *userdata) {
         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;
@@ -956,13 +985,13 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                         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))
@@ -998,8 +1027,8 @@ static int verb_status(int argc, char *argv[], void *userdata) {
 
 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
@@ -1014,56 +1043,60 @@ static int verb_list(int argc, char *argv[], void *userdata) {
 
         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;
@@ -1090,6 +1123,8 @@ static int verb_install(int argc, char *argv[], void *userdata) {
                 }
         }
 
+        (void) sync_esp();
+
         if (arg_touch_variables)
                 r = install_variables(arg_path,
                                       part, pstart, psize, uuid,
@@ -1109,6 +1144,8 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
 
         r = remove_binaries(arg_path);
 
+        (void) sync_esp();
+
         if (arg_touch_variables) {
                 int q;
 
@@ -1120,15 +1157,66 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
         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 },
                 {}
         };
 
@@ -1152,6 +1240,8 @@ int main(int argc, char *argv[]) {
         r = bootctl_main(argc, argv);
 
  finish:
+        pager_close();
         free(arg_path);
+
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }
index 87d207fd3d1382577381f1ef4aa37a60af298924..f0ce9965567303d08c75b824575be605d3a164de 100644 (file)
 
 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 */
@@ -46,11 +50,10 @@ static bool ignore_proc(pid_t pid, bool warn_rootfs) {
         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.
          *
@@ -63,7 +66,7 @@ static bool ignore_proc(pid_t pid, bool warn_rootfs) {
 
                 _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 "
index 078decb432b37a9ed821a468bcf9606d17006a85..ce45f2ded234948c31564b90a7e23cf0dff62855 100644 (file)
@@ -109,6 +109,7 @@ static usec_t arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL;
 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] = {};
@@ -351,6 +352,16 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 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)
@@ -466,7 +477,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 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) {
 
@@ -1482,13 +1493,29 @@ static void initialize_coredump(bool skip_setup) {
         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[],
@@ -2345,6 +2372,9 @@ int main(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();
 
index 723efbbdbcc7a3a7def78422c103819a2ee97194..853db527f78a4e54b98243dda559c8d116d95e42 100644 (file)
@@ -2444,43 +2444,36 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
                         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 */
@@ -3245,6 +3238,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
 
         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));
@@ -3483,6 +3478,16 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
 
                         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);
index 4b79e3def4827b2b0205df18061db7ab92b592aa..036493a38987224655823f40aa89ef52b45ed5b8 100644 (file)
@@ -15,6 +15,7 @@
 
 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) {
 
@@ -28,8 +29,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 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;
@@ -60,6 +68,13 @@ static int process_resume(void) {
 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;
@@ -68,21 +83,21 @@ int main(int argc, char *argv[]) {
         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);
 
index 4537c4210cadd2e425309e0bb3ae235817203786..f62247cabdf12b74b04577480ed84da54766a069 100644 (file)
@@ -175,7 +175,7 @@ static int raw_import_maybe_convert_qcow2(RawImport *i) {
         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);
 
@@ -260,7 +260,7 @@ static int raw_import_open_disk(RawImport *i) {
         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);
 
index e68f197c7971eb2ea10b9bd26889cc70f4957e98..96800ac8a6d9f5522ccbf68cd34a193baaf5c302 100644 (file)
@@ -237,7 +237,7 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) {
         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);
 
@@ -353,7 +353,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
          * 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);
 
@@ -595,7 +595,7 @@ static int raw_pull_job_on_open_disk_raw(PullJob *j) {
         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);
 
index ee6e25e5e82fccf0b217dde1ca10ee9bed2cc54e..e9dddbc0d79ed8df94d769bfd757ac6104e93411 100644 (file)
@@ -372,7 +372,7 @@ JournalFile* journal_file_close(JournalFile *f) {
                  * 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);
         }
 
@@ -3563,7 +3563,7 @@ int journal_file_open_reliably(
         /* 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);
index 2f8d2e9596f201c2433dee918db11864939ffd3b..8bed6df891a973f85529bff0c96d1dbe7051c59a 100644 (file)
@@ -1732,7 +1732,7 @@ static int setup_keys(void) {
 
         /* 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");
 
index ce07de1bfba7480a4d1ab6ca491b7d276871ce2b..8037480304c980beadd7addb0dc01f13feeacb36 100644 (file)
 
 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) {
index 3ea71ea13d264eaaacfa740e376f31b3e81a6d27..1437df6cf36ced4a8029343b40680875dcf7982e 100644 (file)
@@ -1945,13 +1945,7 @@ static int timeout_compare(const void *a, const void *b) {
         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(
index 880e127686f37290a4badb8cb66b284328541d7a..720acfb0c79892f864df75d17cefe9a7ecd05b77 100644 (file)
@@ -314,6 +314,7 @@ static sd_event *event_resolve(sd_event *e) {
 
 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);
@@ -325,22 +326,17 @@ static int pending_prioq_compare(const void *a, const void *b) {
                 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);
@@ -354,18 +350,12 @@ static int prepare_prioq_compare(const void *a, const void *b) {
         /* 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) {
@@ -387,12 +377,7 @@ 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) {
@@ -418,12 +403,7 @@ static int latest_time_prioq_compare(const void *a, const void *b) {
                 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) {
index 2bbbbd03e2287a43ab629e9a250047f4507a8dea..418acca66407cf7832f2c60f517be6fd84f50d65 100644 (file)
@@ -330,7 +330,7 @@ _public_ int sd_hwdb_new(sd_hwdb **ret) {
         }
 
         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;
         }
 
index 15830d30c3d713ef6815a2129e790b6d3df8e4db..08b5707351034f780bfe03ed64cc0eab90afc2db 100644 (file)
@@ -490,13 +490,7 @@ static int timeout_compare(const void *a, const void *b) {
         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,
index 5035bb5bdfed1c57960fa486a492060a05c6ad89..8e22538ac31e88a07da875aa573e5d575dcd52bb 100644 (file)
@@ -1773,6 +1773,8 @@ static int method_do_shutdown_or_sleep(
                         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)
@@ -2199,7 +2201,7 @@ static int method_can_shutdown_or_sleep(
 
         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;
index 6ab33d3bea9128a27f63b56713aed87cfca405ec..215ce12ae94a410a0c1a64fd5ec9a957b04ea5ef 100644 (file)
@@ -183,7 +183,7 @@ int machine_save(Machine *m) {
                         m->timestamp.monotonic);
 
         if (m->n_netif > 0) {
-                unsigned i;
+                size_t i;
 
                 fputs("NETIF=", f);
 
index 946838f86f8ec33575316d9412f6236cab8e6715..967c11039b77be69ce324459d2982ecaa2a11fd2 100644 (file)
@@ -57,7 +57,7 @@ struct Machine {
         sd_bus_message *create_message;
 
         int *netif;
-        unsigned n_netif;
+        size_t n_netif;
 
         LIST_HEAD(Operation, operations);
 
index 47ccb352bf2549c6439e857c7bf24b797787aeb3..5a88d252e741b40021976b535a81226b9ef332a1 100644 (file)
@@ -540,7 +540,7 @@ typedef struct MachineStatusInfo {
         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) {
@@ -600,7 +600,7 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) {
                 printf("\t    Root: %s\n", i->root_directory);
 
         if (i->n_netif > 0) {
-                unsigned c;
+                size_t c;
 
                 fputs("\t   Iface:", stdout);
 
index 3afc66f981707d9815e9e891a91af269812f61f7..27547051c8bf4da21cfddb8a9b7beedc72147837 100644 (file)
@@ -2575,6 +2575,17 @@ static int inner_child(
         _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);
@@ -2859,6 +2870,11 @@ static int outer_child(
         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);
index 8d5a4211448298c3f8450a4b8ad1941219f36206..df15b660ecd72df3f22dbe3d7a7774adfffc3a86 100644 (file)
@@ -12,6 +12,7 @@
 #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);
@@ -36,7 +38,7 @@ void boot_entry_free(BootEntry *entry) {
         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;
@@ -53,8 +55,12 @@ int boot_entry_load(const char *path, BootEntry *entry) {
         }
 
         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");
@@ -139,7 +145,7 @@ void boot_config_free(BootConfig *config) {
         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;
@@ -148,8 +154,12 @@ int boot_loader_read_conf(const char *path, BootConfig *config) {
         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;
@@ -199,14 +209,14 @@ int boot_loader_read_conf(const char *path, BootConfig *config) {
                         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;
@@ -300,7 +310,7 @@ static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) {
         /* 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;
 
@@ -317,30 +327,30 @@ static int boot_entries_select_default(const BootConfig *config) {
 
         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 :(");
 
@@ -357,24 +367,26 @@ int boot_entries_load_config(const char *esp_path, BootConfig *config) {
         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;
@@ -401,28 +413,33 @@ static int verify_esp(
         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)
@@ -447,7 +464,7 @@ static int verify_esp(
 
         /* 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
@@ -598,3 +615,37 @@ found:
 
         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;
+}
index f47c073a4688ca11227f0902856ea4e7239330aa..ed576210fec55ff35788afb9a297d9462122320b 100644 (file)
@@ -2,11 +2,15 @@
 
 #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;
@@ -35,16 +39,13 @@ typedef struct BootConfig {
         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);
index e96748efde0cb5e44f25062be12282c5f5faea54..3931bee559867ebda53719f267cfcd59e4ed1199 100644 (file)
@@ -4,6 +4,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <linux/fs.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -13,6 +14,7 @@
 #include "sd-id128.h"
 
 #include "alloc-util.h"
+#include "chattr-util.h"
 #include "dirent-util.h"
 #include "efivars.h"
 #include "fd-util.h"
@@ -20,6 +22,7 @@
 #include "macro.h"
 #include "parse-util.h"
 #include "stdio-util.h"
+#include "strv.h"
 #include "time-util.h"
 #include "utf8.h"
 #include "util.h"
@@ -63,7 +66,10 @@ struct device_path {
 } _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) {
@@ -72,6 +78,9 @@ 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;
@@ -80,7 +89,7 @@ static int read_flag(const char *varname) {
                 return -EINVAL;
 
         b = *(uint8_t *)v;
-        return b > 0;
+        return !!b;
 }
 
 bool is_efi_secure_boot(void) {
@@ -97,7 +106,7 @@ int efi_reboot_to_firmware_supported(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);
@@ -120,21 +129,18 @@ static int get_os_indications(uint64_t *os_indication) {
         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)
@@ -252,6 +258,9 @@ int efi_set_variable(
         } _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);
@@ -261,24 +270,60 @@ int efi_set_variable(
                      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) {
@@ -344,6 +389,9 @@ int efi_get_boot_option(
         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)
@@ -455,23 +503,30 @@ static uint16_t *tilt_slashes(uint16_t *s) {
         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;
 
@@ -520,6 +575,9 @@ int efi_add_boot_option(uint16_t id, const char *title,
 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);
 }
@@ -529,6 +587,9 @@ int efi_get_boot_order(uint16_t **order) {
         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;
@@ -545,12 +606,15 @@ int efi_get_boot_order(uint16_t **order) {
 }
 
 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')
@@ -569,13 +633,16 @@ static int cmp_uint16(const uint16_t *a, const uint16_t *b) {
 
 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;
@@ -636,6 +703,9 @@ int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
         assert(firmware);
         assert(loader);
 
+        if (!is_efi_boot())
+                return -EOPNOTSUPP;
+
         r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
         if (r < 0)
                 return r;
@@ -660,6 +730,9 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
         _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;
@@ -681,6 +754,56 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
         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) {
index dac66da9f8ac9a4a23c0c49860e54db50f300ca1..9b5ab39fee2d3616032e3bd2836df7ad000aa729 100644 (file)
@@ -41,6 +41,8 @@ int efi_get_boot_options(uint16_t **options);
 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) {
@@ -111,6 +113,10 @@ static inline int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
         return -EOPNOTSUPP;
 }
 
+static inline int efi_loader_get_entries(char ***ret) {
+        return -EOPNOTSUPP;
+}
+
 #endif
 
 char *efi_tilt_backslashes(char *s);
index 33afbe2f7f92c788e073fecd77b34c81b5f00e6e..6a6fc1fe24715eae54586eb33b1084d1809ca114 100644 (file)
@@ -374,7 +374,7 @@ static int output_short(
                 unsigned n_columns,
                 OutputFlags flags,
                 Set *output_fields,
-                size_t highlight[2]) {
+                const size_t highlight[2]) {
 
         int r;
         const void *data;
@@ -505,7 +505,7 @@ static int output_verbose(
                 unsigned n_columns,
                 OutputFlags flags,
                 Set *output_fields,
-                size_t highlight[2]) {
+                const size_t highlight[2]) {
 
         const void *data;
         size_t length;
@@ -613,7 +613,7 @@ static int output_export(
                 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];
@@ -754,7 +754,7 @@ static int output_json(
                 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;
@@ -968,7 +968,7 @@ static int output_cat(
                 unsigned n_columns,
                 OutputFlags flags,
                 Set *output_fields,
-                size_t highlight[2]) {
+                const size_t highlight[2]) {
 
         const void *data;
         size_t l;
@@ -1023,7 +1023,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
                 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,
@@ -1048,7 +1048,7 @@ int show_journal_entry(
                 unsigned n_columns,
                 OutputFlags flags,
                 char **output_fields,
-                size_t highlight[2],
+                const size_t highlight[2],
                 bool *ellipsized) {
 
         int ret;
index 002d06af84ea771481f882f5be56cb54071ccb40..1e0c4ea14646586436f9561eaf33e5fd7b7bdb2f 100644 (file)
@@ -20,7 +20,7 @@ int show_journal_entry(
                 unsigned n_columns,
                 OutputFlags flags,
                 char **output_fields,
-                size_t highlight[2],
+                const size_t highlight[2],
                 bool *ellipsized);
 int show_journal(
                 FILE *f,
index 52cf461109f9e6a823832f1306a7acd56a9d5b46..0344771a6093f245978cb2d891eade9df71b64d7 100644 (file)
@@ -637,7 +637,7 @@ int image_remove(Image *i) {
 
         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;
@@ -736,7 +736,7 @@ int image_rename(Image *i, const char *new_name) {
                 (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:
@@ -777,7 +777,7 @@ int image_rename(Image *i, const char *new_name) {
 
         /* 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);
@@ -927,7 +927,7 @@ int image_read_only(Image *i, bool b) {
                    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;
 
index c38ec81bbc1805d67467ddd1ade223f069f9fdaa..1cb7ea3a197f9c251fb0e38724d3e605e721bdc2 100644 (file)
@@ -20,6 +20,7 @@
 #include "log.h"
 #include "macro.h"
 #include "ptyfwd.h"
+#include "terminal-util.h"
 #include "time-util.h"
 
 struct PTYForward {
@@ -88,8 +89,8 @@ static void pty_forward_disconnect(PTYForward *f) {
         }
 
         /* 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) {
@@ -421,8 +422,17 @@ int pty_forward_new(
 
         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) {
index 2ffd32bf999155bb83d861a6da23855a8c13cd4b..67783991887de1eab32e36f50728cd4bcdbaeb24 100644 (file)
@@ -16,6 +16,7 @@
 #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,
@@ -39,13 +43,19 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
         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},
                 {}
         };
 
@@ -55,6 +65,8 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
                                         CONFIG_PARSE_WARN, NULL);
 
         if (streq(verb, "suspend")) {
+                allow = allow_suspend != 0;
+
                 /* empty by default */
                 modes = TAKE_PTR(suspend_mode);
 
@@ -64,6 +76,8 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
                         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
@@ -75,6 +89,9 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
                         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
@@ -85,21 +102,26 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states, usec_t
                 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;
 }
@@ -266,12 +288,94 @@ static bool enough_swap_for_hibernation(void) {
         }
 
         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;
@@ -422,6 +526,8 @@ int read_fiemap(int fd, struct fiemap **ret) {
         return 0;
 }
 
+static int can_sleep_internal(const char *verb, bool check_allowed);
+
 static bool can_s2h(void) {
         const char *p;
         int r;
@@ -434,8 +540,8 @@ static bool can_s2h(void) {
         }
 
         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;
                 }
@@ -446,19 +552,25 @@ static bool can_s2h(void) {
         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;
 
@@ -473,5 +585,14 @@ int can_sleep(const char *verb) {
         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);
+}
index 6bf035969a6c578cea6c41d039c2eeb15701d1c7..c584f44d39dbc57d23e548d995c1f31ae276e5ae 100644 (file)
@@ -5,7 +5,7 @@
 #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);
index 042a44656fe1ff2462c177a73057ae7ab36638c2..0085cb0196fe8698bbd52c68f0b3868cb6cb933e 100644 (file)
@@ -136,7 +136,6 @@ static int write_state(FILE **f, char **states) {
 }
 
 static int execute(char **modes, char **states) {
-
         char *arguments[] = {
                 NULL,
                 (char*) "pre",
@@ -221,13 +220,11 @@ static int execute_s2h(usec_t hibernate_delay_sec) {
         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;
 
@@ -340,6 +337,7 @@ static int parse_argv(int argc, char *argv[]) {
 }
 
 int main(int argc, char *argv[]) {
+        bool allow;
         _cleanup_strv_free_ char **modes = NULL, **states = NULL;
         usec_t delay = 0;
         int r;
@@ -352,10 +350,15 @@ int main(int argc, char *argv[]) {
         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
index ce5cbe7c13d11d116d198c86caed0a5fd887c0bf..be1b7375afa17d2ac5af5c81244b0ed4440af766 100644 (file)
@@ -3455,21 +3455,12 @@ static int load_kexec_kernel(void) {
         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.");
index d45ca8c9204fc7e29bec69ca09d3048aba5fd930..a79e0cf16b89ba910013bcc92d0ea86d977089c3 100644 (file)
@@ -8,19 +8,15 @@
 #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;
         }
 
@@ -32,19 +28,15 @@ static int test_acpi_fpdt(void) {
 }
 
 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;
         }
 
@@ -57,17 +49,16 @@ static int test_efi_loader(void) {
 
 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;
         }
 
index 8f77e084b6cba3b2d2f335865828e9380d752937..5db103bd22a7212e1c52954359b8abf6174033c3 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "alloc-util.h"
+#include "env-util.h"
 #include "log.h"
 #include "macro.h"
 #include "proc-cmdline.h"
@@ -19,28 +20,68 @@ static int parse_item(const char *key, const char *value, void *data) {
 }
 
 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);
@@ -78,6 +119,7 @@ static void test_proc_cmdline_get_key(void) {
 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);
@@ -94,6 +136,7 @@ static void test_proc_cmdline_get_bool(void) {
 }
 
 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"));
@@ -110,6 +153,7 @@ static void test_proc_cmdline_key_streq(void) {
 }
 
 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", ""));
@@ -124,11 +168,33 @@ static void test_proc_cmdline_key_startswith(void) {
         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();
index 2ce79f8345c460012bfdb0de0c91ebdda1ea63a1..442541a298d1639869de4b627b5e2a048f200a44 100644 (file)
 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) {
@@ -23,6 +25,8 @@ 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);
@@ -56,7 +60,9 @@ static void test_sleep(void) {
                 **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));
@@ -66,7 +72,7 @@ static void test_sleep(void) {
         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");
index 03cd99f000a1b903881df5ba57f7b1b8e571f9c4..2c67e13833a5ee868744113c73a718e788ca43da 100644 (file)
@@ -56,28 +56,22 @@ int main(int argc, char *argv[]) {
         _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);
 
@@ -88,8 +82,7 @@ int main(int argc, char *argv[]) {
                 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);
 
@@ -103,22 +96,18 @@ int main(int argc, char *argv[]) {
                         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;
 }
index e994be66df47e9d69e890f1e02a1ef110662adfd..1b3cb5fbd3780bcfb0354a5eccc60682bc1b3789 100644 (file)
@@ -1232,7 +1232,7 @@ static int fd_set_attribute(Item *item, int fd, const char *path, const struct s
         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,
index 56d0f6966000ed89ebc912106ab9ad370eaa4c80..b4aadaab2ec9b54f31d96bab7ee536b512d7cdd2 100644 (file)
@@ -7,7 +7,9 @@ sanitize_address = custom_target(
                    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]]
 
index 317dad932d9b25a54a625b8e82746f454b4a0701..dea554177db6dff208bb007e2f378332202fcc83 100755 (executable)
@@ -5,8 +5,10 @@ src="$1"
 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.