X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2FPVE%2FLXC.pm;h=4f9674d64221437de690d95f0881478062ba7f19;hb=be6c3dfab7d9fa9497a68eab6b285d557e334f8b;hp=ebe369dd71c9899385f6a6880f13ab2fd06d4769;hpb=b1bad293c4f7a6024bbd363b6784b3875ca5d098;p=pve-container.git diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm index ebe369d..4f9674d 100644 --- a/src/PVE/LXC.pm +++ b/src/PVE/LXC.pm @@ -127,6 +127,7 @@ sub vmstatus { my $cdtime = gettimeofday; my $uptime = (PVE::ProcFSTools::read_proc_uptime(1))[0]; + my $clock_ticks = POSIX::sysconf(&POSIX::_SC_CLK_TCK); my $unprivileged = {}; @@ -189,8 +190,8 @@ sub vmstatus { next if !$pid; # skip stopped CTs - my $ctime = (stat("/proc/$pid"))[10]; # 10 = ctime - $d->{uptime} = time - $ctime; # the method lxcfs uses + my $proc_pid_stat = PVE::ProcFSTools::read_proc_pid_stat($pid); + $d->{uptime} = int(($uptime - $proc_pid_stat->{starttime}) / $clock_ticks); # the method lxcfs uses my $unpriv = $unprivileged->{$vmid}; @@ -361,13 +362,15 @@ sub update_lxc_config { my $ostype = $conf->{ostype} || die "missing 'ostype' - internal error"; - my $inc ="/usr/share/lxc/config/$ostype.common.conf"; - $inc ="/usr/share/lxc/config/common.conf" if !-f $inc; + my $cfgpath = '/usr/share/lxc/config'; + my $inc = "$cfgpath/$ostype.common.conf"; + $inc ="$cfgpath/common.conf" if !-f $inc; $raw .= "lxc.include = $inc\n"; if ($unprivileged || $custom_idmap) { - $inc = "/usr/share/lxc/config/$ostype.userns.conf"; - $inc = "/usr/share/lxc/config/userns.conf" if !-f $inc; - $raw .= "lxc.include = $inc\n" + $inc = "$cfgpath/$ostype.userns.conf"; + $inc = "$cfgpath/userns.conf" if !-f $inc; + $raw .= "lxc.include = $inc\n"; + $raw .= "lxc.seccomp.profile = $cfgpath/pve-userns.seccomp\n"; } # WARNING: DO NOT REMOVE this without making sure that loop device nodes @@ -486,19 +489,24 @@ sub verify_searchdomain_list { } sub get_console_command { - my ($vmid, $conf) = @_; + my ($vmid, $conf, $noescapechar) = @_; my $cmode = PVE::LXC::Config->get_cmode($conf); + my $cmd = []; if ($cmode eq 'console') { - return ['lxc-console', '-n', $vmid, '-t', 0]; + push @$cmd, 'lxc-console', '-n', $vmid, '-t', 0; + push @$cmd, '-e', -1 if $noescapechar; } elsif ($cmode eq 'tty') { - return ['lxc-console', '-n', $vmid]; + push @$cmd, 'lxc-console', '-n', $vmid; + push @$cmd, '-e', -1 if $noescapechar; } elsif ($cmode eq 'shell') { - return ['lxc-attach', '--clear-env', '-n', $vmid]; + push @$cmd, 'lxc-attach', '--clear-env', '-n', $vmid; } else { die "internal error"; } + + return $cmd; } sub get_primary_ips { @@ -1329,6 +1337,59 @@ sub destroy_disks { } } +sub alloc_disk { + my ($storecfg, $vmid, $storage, $size_kb, $rootuid, $rootgid) = @_; + + my $needs_chown = 0; + my $volid; + + my $scfg = PVE::Storage::storage_config($storecfg, $storage); + # fixme: use better naming ct-$vmid-disk-X.raw? + + eval { + my $do_format = 0; + if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' || $scfg->{type} eq 'cifs' ) { + if ($size_kb > 0) { + $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', + undef, $size_kb); + $do_format = 1; + } else { + $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'subvol', + undef, 0); + $needs_chown = 1; + } + } elsif ($scfg->{type} eq 'zfspool') { + + $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'subvol', + undef, $size_kb); + $needs_chown = 1; + } elsif ($scfg->{type} eq 'drbd' || $scfg->{type} eq 'lvm' || $scfg->{type} eq 'lvmthin') { + + $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', undef, $size_kb); + $do_format = 1; + + } elsif ($scfg->{type} eq 'rbd') { + + die "krbd option must be enabled on storage type '$scfg->{type}'\n" if !$scfg->{krbd}; + $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', undef, $size_kb); + $do_format = 1; + } else { + die "unable to create containers on storage type '$scfg->{type}'\n"; + } + format_disk($storecfg, $volid, $rootuid, $rootgid) if $do_format; + }; + if (my $err = $@) { + # in case formatting got interrupted: + if (defined($volid)) { + eval { PVE::Storage::vdisk_free($storecfg, $volid); }; + warn $@ if $@; + } + die $err; + } + + return ($volid, $needs_chown); +} + our $NEW_DISK_RE = qr/^([^:\s]+):(\d+(\.\d+)?)$/; sub create_disks { my ($storecfg, $vmid, $settings, $conf) = @_; @@ -1352,37 +1413,9 @@ sub create_disks { my $size_kb = int(${size_gb}*1024) * 1024; - my $scfg = PVE::Storage::storage_config($storecfg, $storage); - # fixme: use better naming ct-$vmid-disk-X.raw? - - if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') { - if ($size_kb > 0) { - $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', - undef, $size_kb); - format_disk($storecfg, $volid, $rootuid, $rootgid); - } else { - $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'subvol', - undef, 0); - push @$chown_vollist, $volid; - } - } elsif ($scfg->{type} eq 'zfspool') { - - $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'subvol', - undef, $size_kb); - push @$chown_vollist, $volid; - } elsif ($scfg->{type} eq 'drbd' || $scfg->{type} eq 'lvm' || $scfg->{type} eq 'lvmthin') { - - $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', undef, $size_kb); - format_disk($storecfg, $volid, $rootuid, $rootgid); - - } elsif ($scfg->{type} eq 'rbd') { - - die "krbd option must be enabled on storage type '$scfg->{type}'\n" if !$scfg->{krbd}; - $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', undef, $size_kb); - format_disk($storecfg, $volid, $rootuid, $rootgid); - } else { - die "unable to create containers on storage type '$scfg->{type}'\n"; - } + my $needs_chown = 0; + ($volid, $needs_chown) = alloc_disk($storecfg, $vmid, $storage, $size_kb, $rootuid, $rootgid); + push @$chown_vollist, $volid if $needs_chown; push @$vollist, $volid; $mountpoint->{volume} = $volid; $mountpoint->{size} = $size_kb * 1024; @@ -1510,6 +1543,29 @@ sub userns_command { return []; } +sub vm_start { + my ($vmid, $conf, $skiplock) = @_; + + update_lxc_config($vmid, $conf); + + my $skiplock_flag_fn = "/run/lxc/skiplock-$vmid"; + + if ($skiplock) { + open(my $fh, '>', $skiplock_flag_fn) || die "failed to open $skiplock_flag_fn for writing: $!\n"; + close($fh); + } + + my $cmd = ['systemctl', 'start', "pve-container\@$vmid"]; + + eval { PVE::Tools::run_command($cmd); }; + if (my $err = $@) { + unlink $skiplock_flag_fn; + die $err; + } + + return; +} + # Helper to stop a container completely and make sure it has stopped completely. # This is necessary because we want the post-stop hook to have completed its # unmount-all step, but post-stop happens after lxc puts the container into the @@ -1557,4 +1613,93 @@ sub vm_stop { die "container did not stop\n"; } +sub run_unshared { + my ($code) = @_; + + return PVE::Tools::run_fork(sub { + # Unshare the mount namespace + die "failed to unshare mount namespace: $!\n" + if !PVE::Tools::unshare(PVE::Tools::CLONE_NEWNS); + PVE::Tools::run_command(['mount', '--make-rslave', '/']); + return $code->(); + }); +} + +my $copy_volume = sub { + my ($src_volid, $src, $dst_volid, $dest, $storage_cfg, $snapname) = @_; + + my $src_mp = { volume => $src_volid, mp => '/' }; + $src_mp->{type} = PVE::LXC::Config->classify_mountpoint($src_volid); + + my $dst_mp = { volume => $dst_volid, mp => '/' }; + $dst_mp->{type} = PVE::LXC::Config->classify_mountpoint($dst_volid); + + my @mounted; + eval { + # mount and copy + mkdir $src; + mountpoint_mount($src_mp, $src, $storage_cfg, $snapname); + push @mounted, $src; + mkdir $dest; + mountpoint_mount($dst_mp, $dest, $storage_cfg); + push @mounted, $dest; + + PVE::Tools::run_command(['/usr/bin/rsync', '--stats', '-X', '-A', '--numeric-ids', + '-aH', '--whole-file', '--sparse', '--one-file-system', + "$src/", $dest]); + }; + my $err = $@; + foreach my $mount (reverse @mounted) { + eval { PVE::Tools::run_command(['/bin/umount', '--lazy', $mount], errfunc => sub{})}; + warn "Can't umount $mount\n" if $@; + } + + # If this fails they're used as mount points in a concurrent operation + # (which should not happen but there's also no real need to get rid of them). + rmdir $dest; + rmdir $src; + + die $err if $err; +}; + +# Should not be called after unsharing the mount namespace! +sub copy_volume { + my ($mp, $vmid, $storage, $storage_cfg, $conf, $snapname) = @_; + + die "cannot copy volumes of type $mp->{type}\n" if $mp->{type} ne 'volume'; + File::Path::make_path("/var/lib/lxc/$vmid"); + my $dest = "/var/lib/lxc/$vmid/.copy-volume-1"; + my $src = "/var/lib/lxc/$vmid/.copy-volume-2"; + + # get id's for unprivileged container + my (undef, $rootuid, $rootgid) = parse_id_maps($conf); + + # Allocate the disk before unsharing in order to make sure zfs subvolumes + # are visible in this namespace, otherwise the host only sees the empty + # (not-mounted) directory. + my $new_volid; + eval { + # Make sure $mp contains a correct size. + $mp->{size} = PVE::Storage::volume_size_info($storage_cfg, $mp->{volume}); + my $needs_chown; + ($new_volid, $needs_chown) = alloc_disk($storage_cfg, $vmid, $storage, $mp->{size}/1024, $rootuid, $rootgid); + if ($needs_chown) { + PVE::Storage::activate_volumes($storage_cfg, [$new_volid], undef); + my $path = PVE::Storage::path($storage_cfg, $new_volid, undef); + chown($rootuid, $rootgid, $path); + } + + run_unshared(sub { + $copy_volume->($mp->{volume}, $src, $new_volid, $dest, $storage_cfg, $snapname); + }); + }; + if (my $err = $@) { + PVE::Storage::vdisk_free($storage_cfg, $new_volid) + if defined($new_volid); + die $err; + } + + return $new_volid; +} + 1;