all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Christoph Heiss <c.heiss@proxmox.com>
To: Proxmox VE development discussion <pve-devel@lists.proxmox.com>
Subject: Re: [pve-devel] [PATCH container] setup: Fix architecture detection for NixOS containers
Date: Mon, 3 Jul 2023 12:00:08 +0200	[thread overview]
Message-ID: <mlktyxmzowpeztykm2nokx5x4dixoe3cocuforpzkatt2albpo@tlgg5izmkrum> (raw)
In-Reply-To: <20230228105911.217085-1-c.heiss@proxmox.com>

Ping? Still applies & works fine on current master.

On Tue, Feb 28, 2023 at 11:59:11AM +0100, Christoph Heiss wrote:
> NixOS is special and deviates in many places from a "standard" Linux
> system. In this case, /bin/sh does not exist on the actual filesystem,
> at least not before the initial activation - which creates a symlink at
> /bin/sh.
>
> Thus try a bit harder to detect the architecture for NixOS containers by
> inspecting the init script, which contains a shebang-line with the full
> path to the system shell.
>
> This moves the architecture detection code to the end of the container
> creation lifecycle, so that it can be implemented as a plugin
> subroutine. Therefore this mechanism is now generic enough that it can
> be adapted to other container OS's in the future if needed. AFAICS
> `arch` is only used when writing the actual LXC config, so determining
> it later during creation does not change anything.
>
> Tested by creating a NixOS and a Debian container (to verify that
> nothing regressed) and checking if the warning "Architecure detection
> failed: [..]" no longer appears for the NixOS CT and if  `arch` in the
> CT config is correct. Also tested restoring both containers from a local
> and a PBS backup, as well as migrating both container.
>
> Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
> ---
>  src/PVE/LXC/Create.pm       | 76 -------------------------------------
>  src/PVE/LXC/Setup.pm        | 64 +++++++++++++++++++++++++++++++
>  src/PVE/LXC/Setup/Base.pm   |  8 ++++
>  src/PVE/LXC/Setup/NixOS.pm  | 17 +++++++++
>  src/PVE/LXC/Setup/Plugin.pm |  5 +++
>  5 files changed, 94 insertions(+), 76 deletions(-)
>
> diff --git a/src/PVE/LXC/Create.pm b/src/PVE/LXC/Create.pm
> index b2e3d00..adf917c 100644
> --- a/src/PVE/LXC/Create.pm
> +++ b/src/PVE/LXC/Create.pm
> @@ -16,72 +16,6 @@ use PVE::VZDump::ConvertOVZ;
>  use PVE::Tools;
>  use POSIX;
>
> -sub detect_architecture {
> -    my ($rootdir) = @_;
> -
> -    # see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
> -
> -    my $supported_elf_machine = {
> -	0x03 => 'i386',
> -	0x3e => 'amd64',
> -	0x28 => 'armhf',
> -	0xb7 => 'arm64',
> -	0xf3 => 'riscv',
> -    };
> -
> -    my $elf_fn = '/bin/sh'; # '/bin/sh' is POSIX mandatory
> -    my $detect_arch = sub {
> -	# chroot avoids a problem where we check the binary of the host system
> -	# if $elf_fn is an absolut symlink (e.g. $rootdir/bin/sh -> /bin/bash)
> -	chroot($rootdir) or die "chroot '$rootdir' failed: $!\n";
> -	chdir('/') or die "failed to change to root directory\n";
> -
> -	open(my $fh, "<", $elf_fn) or die "open '$elf_fn' failed: $!\n";
> -	binmode($fh);
> -
> -	my $length = read($fh, my $data, 20) or die "read failed: $!\n";
> -
> -	# 4 bytes ELF magic number and 1 byte ELF class, padding, machine
> -	my ($magic, $class, undef, $machine) = unpack("A4CA12n", $data);
> -
> -	die "'$elf_fn' does not resolve to an ELF!\n"
> -	    if (!defined($class) || !defined($magic) || $magic ne "\177ELF");
> -
> -	my $arch = $supported_elf_machine->{$machine};
> -	die "'$elf_fn' has unknown ELF machine '$machine'!\n"
> -	    if !defined($arch);
> -
> -	if ($arch eq 'riscv') {
> -	    if ($class eq 1) {
> -		$arch = 'riscv32';
> -	    } elsif ($class eq 2) {
> -		$arch = 'riscv64';
> -	    } else {
> -		die "'$elf_fn' has invalid class '$class'!\n";
> -	    }
> -	}
> -
> -	return $arch;
> -    };
> -
> -    my $arch = eval { PVE::Tools::run_fork_with_timeout(10, $detect_arch); };
> -    my $err = $@;
> -
> -    if (!defined($arch) && !defined($err)) {
> -	# on timeout
> -	die "Architecture detection failed: timeout\n";
> -    } elsif ($err) {
> -	# any other error
> -	$arch = 'amd64';
> -	print "Architecture detection failed: $err\nFalling back to $arch.\n" .
> -	      "Use `pct set VMID --arch ARCH` to change.\n";
> -    } else {
> -	print "Detected container architecture: $arch\n";
> -    }
> -
> -    return $arch;
> -}
> -
>  sub restore_archive {
>      my ($storage_cfg, $archive, $rootdir, $conf, $no_unpack_error, $bwlimit) = @_;
>
> @@ -118,11 +52,6 @@ sub restore_proxmox_backup_archive {
>
>      PVE::Storage::PBSPlugin::run_raw_client_cmd(
>  	$scfg, $storeid, $cmd, $param, userns_cmd => $userns_cmd);
> -
> -    # if arch is set, we do not try to autodetect it
> -    return if defined($conf->{arch});
> -
> -    $conf->{arch} = detect_architecture($rootdir);
>  }
>
>  sub restore_tar_archive {
> @@ -183,11 +112,6 @@ sub restore_tar_archive {
>      my $err = $@;
>      close($archive_fh) if defined $archive_fh;
>      die $err if $err && !$no_unpack_error;
> -
> -    # if arch is set, we do not try to autodetect it
> -    return if defined($conf->{arch});
> -
> -    $conf->{arch} = detect_architecture($rootdir);
>  }
>
>  sub recover_config {
> diff --git a/src/PVE/LXC/Setup.pm b/src/PVE/LXC/Setup.pm
> index 891231f..4346c5e 100644
> --- a/src/PVE/LXC/Setup.pm
> +++ b/src/PVE/LXC/Setup.pm
> @@ -131,6 +131,20 @@ sub new {
>  	$plugin->{rootgid} = $rootgid;
>      }
>
> +    # if arch is unset, we try to autodetect it
> +    if (!defined($conf->{arch})) {
> +	my $arch = eval { $self->protected_call(sub { $plugin->detect_architecture() }) };
> +
> +	if (!defined($arch)) {
> +	    $arch = 'amd64';
> +	    print "Falling back to $arch.\nUse `pct set VMID --arch ARCH` to change.\n";
> +	} else {
> +	    print "Detected container architecture: $arch\n";
> +	}
> +
> +	$conf->{arch} = $arch;
> +    }
> +
>      return $self;
>  }
>
> @@ -346,4 +360,54 @@ sub get_ct_init_path {
>      return $init;
>  }
>
> +sub detect_architecture {
> +    my ($elf_fn) = @_;
> +
> +    # see https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
> +
> +    my $supported_elf_machine = {
> +	0x03 => 'i386',
> +	0x3e => 'amd64',
> +	0x28 => 'armhf',
> +	0xb7 => 'arm64',
> +	0xf3 => 'riscv',
> +    };
> +
> +    my $detect_arch = sub {
> +	open(my $fh, "<", $elf_fn) or die "open '$elf_fn' failed: $!\n";
> +	binmode($fh);
> +
> +	my $length = read($fh, my $data, 20) or die "read failed: $!\n";
> +
> +	# 4 bytes ELF magic number and 1 byte ELF class, padding, machine
> +	my ($magic, $class, undef, $machine) = unpack("A4CA12n", $data);
> +
> +	die "'$elf_fn' does not resolve to an ELF!\n"
> +	    if (!defined($class) || !defined($magic) || $magic ne "\177ELF");
> +
> +	my $arch = $supported_elf_machine->{$machine};
> +	die "'$elf_fn' has unknown ELF machine '$machine'!\n"
> +	    if !defined($arch);
> +
> +	if ($arch eq 'riscv') {
> +	    if ($class eq 1) {
> +		$arch = 'riscv32';
> +	    } elsif ($class eq 2) {
> +		$arch = 'riscv64';
> +	    } else {
> +		die "'$elf_fn' has invalid class '$class'!\n";
> +	    }
> +	}
> +
> +	return $arch;
> +    };
> +
> +    my $arch = eval { PVE::Tools::run_fork_with_timeout(10, $detect_arch); };
> +    my $err = $@ // "timeout\n";
> +
> +    die "Architecture detection failed: $err" if !defined($arch);
> +
> +    return $arch;
> +}
> +
>  1;
> diff --git a/src/PVE/LXC/Setup/Base.pm b/src/PVE/LXC/Setup/Base.pm
> index 547abdb..93c1d34 100644
> --- a/src/PVE/LXC/Setup/Base.pm
> +++ b/src/PVE/LXC/Setup/Base.pm
> @@ -18,6 +18,7 @@ use PVE::INotify;
>  use PVE::Tools;
>  use PVE::Network;
>
> +use PVE::LXC::Setup;
>  use PVE::LXC::Setup::Plugin;
>  use base qw(PVE::LXC::Setup::Plugin);
>
> @@ -574,6 +575,13 @@ sub ssh_host_key_types_to_generate {
>      };
>  }
>
> +sub detect_architecture {
> +    my ($self) = @_;
> +
> +    # '/bin/sh' is POSIX mandatory
> +    return PVE::LXC::Setup::detect_architecture('/bin/sh');
> +}
> +
>  sub pre_start_hook {
>      my ($self, $conf) = @_;
>
> diff --git a/src/PVE/LXC/Setup/NixOS.pm b/src/PVE/LXC/Setup/NixOS.pm
> index 845d2d5..4f7b93e 100644
> --- a/src/PVE/LXC/Setup/NixOS.pm
> +++ b/src/PVE/LXC/Setup/NixOS.pm
> @@ -5,6 +5,7 @@ use warnings;
>
>  use File::Path 'make_path';
>
> +use PVE::LXC::Setup;
>  use PVE::LXC::Setup::Base;
>
>  use base qw(PVE::LXC::Setup::Base);
> @@ -37,4 +38,20 @@ sub setup_init {
>      my ($self, $conf) = @_;
>  }
>
> +sub detect_architecture {
> +    my ($self) = @_;
> +
> +    # /bin/sh only exists as a symlink after the initial system activaction on first boot.
> +    # To detect the actual architecture of the system, examine the shebang line of the /sbin/init
> +    # script, which has the full path to the system shell.
> +    my $init_path = '/sbin/init';
> +    open(my $fh, '<', $init_path) or die "open '$init_path' failed: $!\n";
> +
> +    if (<$fh> =~ /^#! ?(\S*)/) {
> +	return PVE::LXC::Setup::detect_architecture($1);
> +    }
> +
> +    die "Architecture detection failed: could not find a shell\n";
> +}
> +
>  1;
> diff --git a/src/PVE/LXC/Setup/Plugin.pm b/src/PVE/LXC/Setup/Plugin.pm
> index 3d968e7..b9d9c2d 100644
> --- a/src/PVE/LXC/Setup/Plugin.pm
> +++ b/src/PVE/LXC/Setup/Plugin.pm
> @@ -62,6 +62,11 @@ sub ssh_host_key_types_to_generate {
>      croak "implement me in sub-class\n";
>  }
>
> +sub detect_architecture {
> +    my ($self) = @_;
> +    croak "implement me in sub-class\n";
> +}
> +
>  # hooks
>
>  sub pre_start_hook {
> --
> 2.39.2
>
>
>
> _______________________________________________
> pve-devel mailing list
> pve-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
>
>




  reply	other threads:[~2023-07-03 10:00 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-28 10:59 Christoph Heiss
2023-07-03 10:00 ` Christoph Heiss [this message]
2023-08-21 12:50 ` Fiona Ebner
2023-08-22 12:35   ` Christoph Heiss

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=mlktyxmzowpeztykm2nokx5x4dixoe3cocuforpzkatt2albpo@tlgg5izmkrum \
    --to=c.heiss@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal