my $cdtime = gettimeofday;
my $uptime = (PVE::ProcFSTools::read_proc_uptime(1))[0];
+ my $clock_ticks = POSIX::sysconf(&POSIX::_SC_CLK_TCK);
my $unprivileged = {};
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};
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
}
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 {
}
}
+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) = @_;
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;
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
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;