]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
import-generator: add new option 'bootorigin' to derive URL from efi boot url
authorLennart Poettering <lennart@poettering.net>
Sun, 9 Feb 2025 23:23:21 +0000 (00:23 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 21 Feb 2025 09:03:32 +0000 (10:03 +0100)
man/systemd-import-generator.xml
src/import/import-generator.c

index f140873552bbed05f2604f21e24f8bd236b90c7d..d60c9c96fedc2850a600b307fccf167bc1c20a3b 100644 (file)
 
             <xi:include href="version-info.xml" xpointer="v258"/></listitem>
           </varlistentry>
+
+          <varlistentry>
+            <term>bootorigin</term>
+
+            <listitem><para>If this option is specified, in place of the URL a simple filename may be
+            specified. If the system is booted via UEFI HTTP network booting the last component of the
+            network boot origin URL is replaced by this filename and used as download source. This hence
+            allows to automatically derive the URLs for disk images from the original boot URL used to invoke
+            the kernel or boot loader.</para>
+
+            <para>If this option is used and the system is not actually booted via UEFI HTTP network booting,
+            the download is gracefully skipped. Or in other words without other modifications it is possible
+            to put together an initrd image that will boot from a local disk if available, or from downloaded
+            disk image if used via network booting.</para>
+
+            <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+          </varlistentry>
         </variablelist>
 
         <xi:include href="version-info.xml" xpointer="v257"/></listitem>
       <literal>image</literal>, and attaches it to a loopback block device on completion. It then boots from
       the 2nd partition in the image.</para>
     </example>
+
+    <example>
+      <title>Boot into disk image (raw), with URL derived from UEFI HTTP network booting</title>
+
+      <programlisting>rd.systemd.pull=raw,machine,verify=no,blockdev,bootorigin:rootdisk:image.raw.xz root=/dev/disk/by-loop-ref/rootdisk.raw-part2</programlisting>
+
+      <para>This is similar to the previous example, but this time the source URL is automatically derived
+      from the UEFI HTTP network boot URL. For example, if an UKI is booted from an URL
+      <literal>http://example.com/image.efi</literal> this would result in a root disk being downloaded from
+      <literal>http://example.com/image.raw.xz</literal>.</para>
+    </example>
   </refsect1>
 
   <refsect1>
index a778d064a93d5d62721dabc810ec3cff81b9ef5c..5d2f37107e8a14ad95cd2d6abb9c51c0ef12551a 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "creds-util.h"
 #include "discover-image.h"
+#include "efivars.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "generator.h"
@@ -65,8 +66,6 @@ static int parse_pull_expression(const char *v) {
         if (r == 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No local string in pull expression '%s': %m", v);
 
-        if (!http_url_is_valid(p) && !file_url_is_valid(p))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid URL, refusing: %s", p);
         _cleanup_free_ char *remote = strdup(p);
         if (!remote)
                 return log_oom();
@@ -79,7 +78,7 @@ static int parse_pull_expression(const char *v) {
         ImportType type = _IMPORT_TYPE_INVALID;
         ImageClass class = _IMAGE_CLASS_INVALID;
         ImportVerify verify = IMPORT_VERIFY_SIGNATURE;
-        bool ro = false, blockdev = false;
+        bool ro = false, blockdev = false, bootorigin = false;
 
         const char *o = options;
         for (;;) {
@@ -99,8 +98,9 @@ static int parse_pull_expression(const char *v) {
                         ro = false;
                 else if (streq(opt, "blockdev"))
                         blockdev = true;
+                else if (streq(opt, "bootorigin"))
+                        bootorigin = true;
                 else if ((suffix = startswith(opt, "verify="))) {
-
                         ImportVerify w = import_verify_from_string(suffix);
 
                         if (w < 0)
@@ -124,6 +124,32 @@ static int parse_pull_expression(const char *v) {
                 }
         }
 
+        if (bootorigin) {
+                _cleanup_free_ char *stub_url = NULL;
+
+                r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("StubDeviceURL"), &stub_url);
+                if (r == -ENOENT) {
+                        log_debug("Option 'bootorigin' specified, but StubDeviceURL EFI variable not set, not scheduling import job for '%s'.", remote);
+                        return 0;
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read 'StubDeviceURL' EFI variable: %m");
+
+                if (!http_url_is_valid(stub_url))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Value of 'StubDeviceURL' is not a valid URL, refusing: %s", stub_url);
+
+                _cleanup_free_ char *result = NULL;
+                r = import_url_change_last_component(stub_url, remote, &result);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to replace last component of URL '%s': %m", stub_url);
+
+                log_info("URL reported by StubDeviceURL is '%s', derived download URL '%s' from it.", stub_url, result);
+                free_and_replace(remote, result);
+        }
+
+        if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid URL, refusing: %s", remote);
+
         if (type < 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No image type (raw, tar) specified in pull expression, refusing: %s", v);
         if (class < 0)