]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/LXC.pm
Add cifs as allowed storage type.
[pve-container.git] / src / PVE / LXC.pm
index ebe369dd71c9899385f6a6880f13ab2fd06d4769..4f9674d64221437de690d95f0881478062ba7f19 100644 (file)
@@ -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;