use strict;
use warnings;
+use POSIX qw(ENOENT S_ISBLK S_ISCHR);
+
use PVE::SafeSyslog;
# LXC introduced an `lxc.hook.version` property which allows hooks to be executed in different
$code->($ct_name, $common_vars, $namespaces, $args);
}
-sub for_current_devices($&) {
- my ($vmid, $code) = @_;
+sub for_devices {
+ my ($devlist_file, $vmid, $code) = @_;
- my $devlist_file = "/var/lib/lxc/$vmid/devices";
my $fd;
if (! open $fd, '<', $devlist_file) {
}
while (defined(my $line = <$fd>)) {
- if ($line !~ m@^(b):(\d+):(\d+):/dev/(\S+)\s*$@) {
- warn "invalid .pve-devices entry: $line\n";
+ if ($line !~ m@^(b|c):(\d+):(\d+):/dev/(\S+)\s*$@) {
+ warn "invalid $devlist_file entry: $line\n";
next;
}
close $fd;
}
+sub for_current_passthrough_mounts($&) {
+ my ($vmid, $code) = @_;
+
+ my $devlist_file = "/var/lib/lxc/$vmid/passthrough/mounts";
+
+ if (-e $devlist_file) {
+ for_devices($devlist_file, $vmid, $code);
+ } else {
+ # Fallback to the old device list file in case a package upgrade
+ # occurs between lxc-pve-prestart-hook and now.
+ for_devices("/var/lib/lxc/$vmid/devices", $vmid, $code);
+ }
+}
+
+sub for_current_passthrough_devices($&) {
+ my ($vmid, $code) = @_;
+
+ my $passthrough_devlist_file = "/var/lib/lxc/$vmid/passthrough/devices";
+ for_devices($passthrough_devlist_file, $vmid, $code);
+}
+
sub cgroup_do_write($$) {
my ($path, $value) = @_;
my $fd;
return 1;
}
+sub get_device_mode_and_rdev($) {
+ my ($path) = @_;
+
+ die "Path is not defined\n" if !defined($path);
+
+ my ($mode, $rdev) = (stat($path))[2, 6];
+ die "Device $path does not exist\n" if $! == ENOENT;
+ die "Error accessing device $path\n" if !defined($mode) || !defined($rdev);
+ die "$path is not a device\n" if !S_ISBLK($mode) && !S_ISCHR($mode);
+
+ return ($mode, $rdev);
+}
+
+# Tries to the architecture of an executable file based on its ELF header.
+sub detect_elf_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 $err if !defined($arch);
+
+ return $arch;
+}
+
1;