@@ -33,6 +33,10 @@ const SYSTEMD_KEY_DIR: &str = "loader/keys";
3333/// bootc does not use the entry-token at all.
3434const KERNEL_INSTALL_CONF_ROOT : & str = "/tmp" ;
3535
36+ /// First systemd release whose `bootctl install` accepts `--random-seed`.
37+ /// See: <https://www.freedesktop.org/software/systemd/man/latest/bootctl.html>
38+ const BOOTCTL_RANDOM_SEED_MIN_VERSION : u32 = 257 ;
39+
3640/// Mount the first ESP found among backing devices at /boot/efi.
3741///
3842/// This is used by the install-alongside path to clean stale bootloader
@@ -256,7 +260,16 @@ pub(crate) fn install_systemd_boot(
256260 ] ;
257261
258262 if configopts. generic_image {
259- bootctl_args. extend ( [ "--random-seed" , "no" , "--no-variables" ] ) ;
263+ bootctl_args. push ( "--no-variables" ) ;
264+ // `--random-seed` was only added to `bootctl install` in systemd 257.
265+ let systemd_version = bootctl_systemd_version ( ) ?;
266+ if systemd_version >= BOOTCTL_RANDOM_SEED_MIN_VERSION {
267+ bootctl_args. extend ( [ "--random-seed" , "no" ] ) ;
268+ } else {
269+ tracing:: debug!(
270+ "Skipping --random-seed: requires systemd >= {BOOTCTL_RANDOM_SEED_MIN_VERSION}, found {systemd_version}"
271+ ) ;
272+ }
260273 }
261274
262275 Command :: new ( "bootctl" )
@@ -312,6 +325,24 @@ pub(crate) fn install_systemd_boot(
312325 Ok ( ( ) )
313326}
314327
328+ #[ context( "Querying bootctl version" ) ]
329+ fn bootctl_systemd_version ( ) -> Result < u32 > {
330+ let out = Command :: new ( "bootctl" ) . arg ( "--version" ) . run_get_string ( ) ?;
331+ parse_systemd_version ( & out)
332+ }
333+
334+ /// Parse the systemd major version from `bootctl --version` output, whose first
335+ /// line looks like `systemd 259 (259.5-0ubuntu3)`.
336+ fn parse_systemd_version ( output : & str ) -> Result < u32 > {
337+ output
338+ . split_whitespace ( )
339+ . nth ( 1 )
340+ . and_then ( |s| s. parse :: < u32 > ( ) . ok ( ) )
341+ . ok_or_else ( || {
342+ anyhow ! ( "Could not parse systemd version from bootctl --version: {output:?}" )
343+ } )
344+ }
345+
315346#[ context( "Installing bootloader using zipl" ) ]
316347pub ( crate ) fn install_via_zipl ( device : & bootc_blockdev:: Device , boot_uuid : & str ) -> Result < ( ) > {
317348 // Identify the target boot partition from UUID
@@ -389,3 +420,31 @@ pub(crate) fn install_via_zipl(device: &bootc_blockdev::Device, boot_uuid: &str)
389420 . log_debug ( )
390421 . run_inherited_with_cmd_context ( )
391422}
423+
424+ #[ cfg( test) ]
425+ mod tests {
426+ use super :: * ;
427+
428+ #[ test]
429+ fn test_parse_systemd_version ( ) {
430+ // The first line of `bootctl --version`. the trailing feature line is ignored.
431+ let cases = [
432+ ( "systemd 259 (259.5-0ubuntu3)" , 259 ) ,
433+ ( "systemd 257 (257-26.el10-g1d19ad5)" , 257 ) ,
434+ ( "systemd 255 (255.4-1ubuntu8.16)" , 255 ) ,
435+ ] ;
436+ for ( input, expected) in cases {
437+ assert_eq ! (
438+ parse_systemd_version( input) . unwrap( ) ,
439+ expected,
440+ "input: {input:?}"
441+ ) ;
442+ }
443+ for bad in [ "" , "systemd" , "not a version string" ] {
444+ assert ! (
445+ parse_systemd_version( bad) . is_err( ) ,
446+ "should reject: {bad:?}"
447+ ) ;
448+ }
449+ }
450+ }
0 commit comments