]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/LXC/Config.pm
lxc_config: mount /sys as mixed for unprivileged by default
[pve-container.git] / src / PVE / LXC / Config.pm
index d624b0b2f2068f25579c09d8fc438418e2c25c9d..0909773f312432526d2f64f5ca42e1f06681b437 100644 (file)
@@ -5,6 +5,7 @@ use warnings;
 
 use PVE::AbstractConfig;
 use PVE::Cluster qw(cfs_register_file);
+use PVE::DataCenterConfig;
 use PVE::GuestHelpers;
 use PVE::INotify;
 use PVE::JSONSchema qw(get_standard_option);
@@ -322,6 +323,21 @@ my $features_desc = {
        description => "Allow using 'fuse' file systems in a container."
            ." Note that interactions between fuse and the freezer cgroup can potentially cause I/O deadlocks.",
     },
+    mknod => {
+       optional => 1,
+       type => 'boolean',
+       default => 0,
+       description => "Allow unprivileged containers to use mknod() to add certain device nodes."
+           ." This requires a kernel with seccomp trap to user space support (5.3 or newer)."
+           ." This is experimental.",
+    },
+    force_rw_sys => {
+       optional => 1,
+       type => 'boolean',
+       default => 0,
+       description => "Mount /sys in unprivileged containers as `rw` instead of `mixed`."
+           ." This can break networking under newer (>= v245) systemd-network use."
+    },
 };
 
 my $confdesc = {
@@ -473,6 +489,11 @@ my $confdesc = {
        format => 'pve-volume-id',
        description => 'Script that will be exectued during various steps in the containers lifetime.',
     },
+    tags => {
+       type => 'string', format => 'pve-tag-list',
+       description => 'Tags of the Container. This is only meta information.',
+       optional => 1,
+    },
 };
 
 my $valid_lxc_conf_keys = {
@@ -520,6 +541,7 @@ my $valid_lxc_conf_keys = {
     'lxc.hook.post-stop' => 1,
     'lxc.hook.clone' => 1,
     'lxc.hook.destroy' => 1,
+    'lxc.hook.version' => 1,
     'lxc.log.level' => 1,
     'lxc.log.file' => 1,
     'lxc.start.auto' => 1,
@@ -930,13 +952,13 @@ sub update_pct_config {
            if !$storage_config->{content}->{rootdir};
     };
 
-    foreach my $opt (keys %$param) { # add/change
+    foreach my $opt (sort keys %$param) { # add/change
        $modified->{$opt} = 1;
        my $value = $param->{$opt};
        if ($opt =~ m/^mp(\d+)$/ || $opt eq 'rootfs') {
            $class->check_protection($conf, "can't update CT $vmid drive '$opt'");
            my $mp = $opt eq 'rootfs' ? $class->parse_ct_rootfs($value) : $class->parse_ct_mountpoint($value);
-           $check_content_type->($mp);
+           $check_content_type->($mp) if ($mp->{type} eq 'volume');
        } elsif ($opt eq 'hookscript') {
            PVE::GuestHelpers::check_hookscript($value);
        } elsif ($opt eq 'nameserver') {
@@ -1121,7 +1143,7 @@ sub vmconfig_hotplug_pending {
     };
 
     my $changes;
-    foreach my $opt (keys %{$conf->{pending}}) { # add/change
+    foreach my $opt (sort keys %{$conf->{pending}}) { # add/change
        next if $selection && !$selection->{$opt};
        if ($LXC_FASTPLUG_OPTIONS->{$opt}) {
            $conf->{$opt} = delete $conf->{pending}->{$opt};
@@ -1181,7 +1203,7 @@ sub vmconfig_hotplug_pending {
                PVE::LXC::write_cgroup_value("cpu", $vmid, "cpu.cfs_period_us", -1);
                PVE::LXC::write_cgroup_value("cpu", $vmid, "cpu.cfs_quota_us", -1);
            } elsif ($opt eq 'cpuunits') {
-               PVE::LXC::write_cgroup_value("cpu", $vmid, "cpu.shared", $confdesc->{cpuunits}->{default});
+               PVE::LXC::write_cgroup_value("cpu", $vmid, "cpu.shares", $confdesc->{cpuunits}->{default});
            } elsif ($opt =~ m/^net(\d)$/) {
                my $netid = $1;
                PVE::Network::veth_delete("veth${vmid}i$netid");
@@ -1197,7 +1219,7 @@ sub vmconfig_hotplug_pending {
        }
     }
 
-    foreach my $opt (keys %{$conf->{pending}}) {
+    foreach my $opt (sort keys %{$conf->{pending}}) {
        next if $opt eq 'delete'; # just to be sure
        next if $selection && !$selection->{$opt};
        my $value = $conf->{pending}->{$opt};
@@ -1210,11 +1232,20 @@ sub vmconfig_hotplug_pending {
            } elsif ($opt =~ m/^net(\d+)$/) {
                my $netid = $1;
                my $net = $class->parse_lxc_network($value);
+               $value = $class->print_lxc_network($net);
                PVE::LXC::update_net($vmid, $conf, $opt, $net, $netid, $rootdir);
            } elsif ($opt eq 'memory' || $opt eq 'swap') {
                if (!$hotplug_memory_done) { # don't call twice if both opts are passed
                    $hotplug_memory->($conf->{pending}->{memory}, $conf->{pending}->{swap});
                }
+           } elsif ($opt =~ m/^mp(\d+)$/) {
+               if (!PVE::LXC::Tools::can_use_new_mount_api()) {
+                   die "skip\n";
+               }
+
+               $class->apply_pending_mountpoint($vmid, $conf, $opt, $storecfg, 1);
+               # apply_pending_mountpoint modifies the value if it creates a new disk
+               $value = $conf->{pending}->{$opt};
            } else {
                die "skip\n"; # skip non-hotpluggable
            }
@@ -1240,20 +1271,10 @@ sub vmconfig_apply_pending {
        warn $err_msg;
     };
 
-    my $rescan_volume = sub {
-       my ($mp) = @_;
-       eval {
-           $mp->{size} = PVE::Storage::volume_size_info($storecfg, $mp->{volume}, 5)
-               if !defined($mp->{size});
-       };
-       warn "Could not rescan volume size - $@\n" if $@;
-    };
-
     my $pending_delete_hash = $class->parse_pending_delete($conf->{pending}->{delete});
     # FIXME: $force deletion is not implemented for CTs
     foreach my $opt (sort keys %$pending_delete_hash) {
        next if $selection && !$selection->{$opt};
-       $class->cleanup_pending($conf);
        eval {
            if ($opt =~ m/^mp(\d+)$/) {
                my $mp = $class->parse_ct_mountpoint($conf->{$opt});
@@ -1274,34 +1295,23 @@ sub vmconfig_apply_pending {
        }
     }
 
-    foreach my $opt (keys %{$conf->{pending}}) { # add/change
+    $class->cleanup_pending($conf);
+
+    foreach my $opt (sort keys %{$conf->{pending}}) { # add/change
        next if $opt eq 'delete'; # just to be sure
        next if $selection && !$selection->{$opt};
        eval {
            if ($opt =~ m/^mp(\d+)$/) {
-               my $mp = $class->parse_ct_mountpoint($conf->{pending}->{$opt});
-               my $old = $conf->{$opt};
-               if ($mp->{type} eq 'volume') {
-                   if ($mp->{volume} =~ $PVE::LXC::NEW_DISK_RE) {
-                       PVE::LXC::create_disks($storecfg, $vmid, { $opt => $conf->{pending}->{$opt} }, $conf, 1);
-                   } else {
-                       $rescan_volume->($mp);
-                       $conf->{pending}->{$opt} = $class->print_ct_mountpoint($mp);
-                   }
-               }
-               if (defined($old)) {
-                   my $mp = $class->parse_ct_mountpoint($old);
-                   if ($mp->{type} eq 'volume') {
-                       $class->add_unused_volume($conf, $mp->{volume})
-                           if !$class->is_volume_in_use($conf, $conf->{$opt}, 1, 1);
-                   }
-               }
+               $class->apply_pending_mountpoint($vmid, $conf, $opt, $storecfg, 0);
+           } elsif ($opt =~ m/^net(\d+)$/) {
+               my $netid = $1;
+               my $net = $class->parse_lxc_network($conf->{pending}->{$opt});
+               $conf->{pending}->{$opt} = $class->print_lxc_network($net);
            }
        };
        if (my $err = $@) {
            $add_apply_error->($opt, $err);
        } else {
-           $class->cleanup_pending($conf);
            $conf->{$opt} = delete $conf->{pending}->{$opt};
        }
     }
@@ -1309,6 +1319,64 @@ sub vmconfig_apply_pending {
     $class->write_config($vmid, $conf);
 }
 
+my $rescan_volume = sub {
+    my ($storecfg, $mp) = @_;
+    eval {
+       $mp->{size} = PVE::Storage::volume_size_info($storecfg, $mp->{volume}, 5);
+    };
+    warn "Could not rescan volume size - $@\n" if $@;
+};
+
+sub apply_pending_mountpoint {
+    my ($class, $vmid, $conf, $opt, $storecfg, $running) = @_;
+
+    my $mp = $class->parse_ct_mountpoint($conf->{pending}->{$opt});
+    my $old = $conf->{$opt};
+    if ($mp->{type} eq 'volume') {
+       if ($mp->{volume} =~ $PVE::LXC::NEW_DISK_RE) {
+           my $original_value = $conf->{pending}->{$opt};
+           my $vollist = PVE::LXC::create_disks(
+               $storecfg,
+               $vmid,
+               { $opt => $original_value },
+               $conf,
+               1,
+           );
+           if ($running) {
+               # Re-parse mount point:
+               my $mp = $class->parse_ct_mountpoint($conf->{pending}->{$opt});
+               eval {
+                   PVE::LXC::mountpoint_hotplug($vmid, $conf, $opt, $mp, $storecfg);
+               };
+               my $err = $@;
+               if ($err) {
+                   PVE::LXC::destroy_disks($storecfg, $vollist);
+                   # The pending-changes code collects errors but keeps on looping through further
+                   # pending changes, so unroll the change in $conf as well if destroy_disks()
+                   # didn't die().
+                   $conf->{pending}->{$opt} = $original_value;
+                   die $err;
+               }
+           }
+       } else {
+           die "skip\n" if $running && defined($old); # TODO: "changing" mount points?
+           $rescan_volume->($storecfg, $mp);
+           if ($running) {
+               PVE::LXC::mountpoint_hotplug($vmid, $conf, $opt, $mp, $storecfg);
+           }
+           $conf->{pending}->{$opt} = $class->print_ct_mountpoint($mp);
+       }
+    }
+
+    if (defined($old)) {
+       my $mp = $class->parse_ct_mountpoint($old);
+       if ($mp->{type} eq 'volume') {
+           $class->add_unused_volume($conf, $mp->{volume})
+               if !$class->is_volume_in_use($conf, $conf->{$opt}, 1, 1);
+       }
+    }
+}
+
 sub classify_mountpoint {
     my ($class, $vol) = @_;
     if ($vol =~ m!^/!) {