use File::Path;
use POSIX;
+use PVE::CGroup;
use PVE::Cluster;
use PVE::LXC::Config;
use PVE::LXC::Setup;
use PVE::LXC::Tools;
use PVE::LXC;
+use PVE::RESTEnvironment;
+use PVE::SafeSyslog;
use PVE::Storage;
use PVE::Syscall qw(:fsmount);
use PVE::Tools qw(AT_FDCWD O_PATH);
+my $WARNFD;
+sub log_warn {
+ my ($vmid, $message) = @_;
+
+ if (!defined($WARNFD)) {
+ open($WARNFD, '>', "/run/pve/ct-${vmid}.warnings");
+ }
+ print $WARNFD "$message\n";
+}
+
PVE::LXC::Tools::lxc_hook('pre-start', 'lxc', sub {
my ($vmid, $vars, undef, undef) = @_;
my $skiplock_flag_fn = "/run/lxc/skiplock-$vmid";
- my $skiplock = 1 if -e $skiplock_flag_fn;
+ my $skiplock = -e $skiplock_flag_fn;
unlink $skiplock_flag_fn if $skiplock;
PVE::Cluster::check_cfs_quorum(); # only start if we have quorum
+ PVE::RESTEnvironment->setup_default_cli_env();
+
return undef if ! -f PVE::LXC::Config->config_file($vmid);
my $conf = PVE::LXC::Config->load_config($vmid);
PVE::LXC::Config->check_lock($conf);
}
- my $storage_cfg = PVE::Storage::config();
-
- my $vollist = PVE::LXC::Config->get_vm_volumes($conf);
- my $loopdevlist = PVE::LXC::Config->get_vm_volumes($conf, 'rootfs');
+ cleanup_cgroups($vmid);
- PVE::Storage::activate_volumes($storage_cfg, $vollist);
+ my $storage_cfg = PVE::Storage::config();
my $rootdir = $vars->{ROOTFS_PATH};
};
}
- PVE::LXC::Config->foreach_mountpoint($conf, $setup_mountpoint);
+ PVE::LXC::Config->foreach_volume($conf, $setup_mountpoint);
my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir);
$lxc_setup->pre_start_hook();
+ if (PVE::CGroup::cgroup_mode() == 2) {
+ if (!$lxc_setup->unified_cgroupv2_support()) {
+ log_warn($vmid, "old systemd (< v232) detected, container won't run in a pure cgroupv2"
+ ." environment! Please see documentation -> container -> cgroup version.");
+ syslog('err', "CT $vmid does not support running in a pure cgroupv2 environment\n");
+ }
+ }
+
if (@$devices) {
my $devlist = '';
foreach my $dev (@$devices) {
PVE::Tools::file_set_contents($devlist_file, $devlist);
}
});
+
+# Leftover cgroups prevent lxc from starting without any useful information
+# showing up in the journal, it is also often unable to properly clean them up
+# at shutdown, so we do this here.
+sub cleanup_cgroups($) {
+ my ($vmid) = @_;
+
+ if (PVE::CGroup::cgroup_mode() == 2) {
+ rmdir_recursive("/sys/fs/cgroup/lxc/$vmid");
+ rmdir_recursive("/sys/fs/cgroup/lxc.monitor/$vmid");
+ } else {
+ my ($v1, $v2) = PVE::CGroup::get_cgroup_controllers();
+
+ my @controllers_cgv1 = keys %$v1;
+ foreach my $controller (@controllers_cgv1) {
+ $controller =~ s/^name=//; # `name=systemd` is mounted just as `systemd`
+ rmdir_recursive("/sys/fs/cgroup/$controller/lxc/$vmid");
+ rmdir_recursive("/sys/fs/cgroup/$controller/lxc.monitor/$vmid");
+ }
+
+ if ($v2) {
+ rmdir_recursive("/sys/fs/cgroup/unified/lxc/$vmid");
+ rmdir_recursive("/sys/fs/cgroup/unified/lxc.monitor/$vmid");
+ }
+ }
+}
+
+# FIXME: This is an ugly version without openat() because perl has no equivalent
+# of fdopendir() so we cannot readdir from an openat() opened handle.
+sub rmdir_recursive {
+ my ($path) = @_;
+
+ my $dh;
+ if (!opendir($dh, $path)) {
+ return if $!{ENOENT};
+ die "failed to open directory '$path': $!\n";
+ }
+
+ while (defined(my $entry = readdir($dh))) {
+ next if $entry eq '.' || $entry eq '..';
+ my $next = "$path/$entry";
+ next if ! -d $next;
+ rmdir_recursive($next);
+ }
+
+ rmdir($path) or die "failed to remove directory '$path': $!\n";
+}