13 use Fcntl
qw(O_RDONLY);
15 use PVE
::Cluster
qw(cfs_register_file cfs_read_file);
16 use PVE
::Exception
qw(raise_perm_exc);
20 use PVE
::JSONSchema
qw(get_standard_option);
21 use PVE
::Tools
qw($IPV6RE $IPV4RE dir_glob_foreach lock_file lock_file_full);
23 use PVE
::AccessControl
;
26 use Time
::HiRes qw
(gettimeofday
);
30 my $nodename = PVE
::INotify
::nodename
();
32 my $cpuinfo= PVE
::ProcFSTools
::read_cpuinfo
();
34 our $COMMON_TAR_FLAGS = [ '--sparse', '--numeric-owner', '--acls',
36 '--xattrs-include=user.*',
37 '--xattrs-include=security.capability',
38 '--warning=no-xattr-write' ];
40 cfs_register_file
('/lxc/', \
&parse_pct_config
, \
&write_pct_config
);
46 format
=> 'pve-lxc-mp-string',
47 format_description
=> 'volume',
48 description
=> 'Volume, device or directory to mount into the container.',
52 format_description
=> '[1|0]',
53 description
=> 'Whether to include the mountpoint in backups.',
58 format
=> 'disk-size',
59 format_description
=> 'DiskSize',
60 description
=> 'Volume size (read only value).',
65 format_description
=> 'acl',
66 description
=> 'Explicitly enable or disable ACL support.',
71 format_description
=> 'ro',
72 description
=> 'Read-only mountpoint (not supported with bind mounts)',
77 format_description
=> '[0|1]',
78 description
=> 'Enable user quotas inside the container (not supported with zfs subvolumes)',
83 PVE
::JSONSchema
::register_standard_option
('pve-ct-rootfs', {
84 type
=> 'string', format
=> $rootfs_desc,
85 description
=> "Use volume as container root.",
89 PVE
::JSONSchema
::register_standard_option
('pve-lxc-snapshot-name', {
90 description
=> "The name of the snapshot.",
91 type
=> 'string', format
=> 'pve-configid',
99 description
=> "Lock/unlock the VM.",
100 enum
=> [qw(migrate backup snapshot rollback)],
105 description
=> "Specifies whether a VM will be started during system bootup.",
108 startup
=> get_standard_option
('pve-startup-order'),
112 description
=> "Enable/disable Template.",
118 enum
=> ['amd64', 'i386'],
119 description
=> "OS architecture type.",
125 enum
=> ['debian', 'ubuntu', 'centos', 'fedora', 'opensuse', 'archlinux', 'alpine', 'unmanaged'],
126 description
=> "OS type. This is used to setup configuration inside the container, and corresponds to lxc setup scripts in /usr/share/lxc/config/<ostype>.common.conf. Value 'unmanaged' can be used to skip and OS specific setup.",
131 description
=> "Attach a console device (/dev/console) to the container.",
137 description
=> "Specify the number of tty available to the container",
145 description
=> "Limit of CPU usage. Note if the computer has 2 CPUs, it has a total of '2' CPU time. Value '0' indicates no CPU limit.",
153 description
=> "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to the weights of all the other running VMs.\n\nNOTE: You can disable fair-scheduler configuration by setting this to 0.",
161 description
=> "Amount of RAM for the VM in MB.",
168 description
=> "Amount of SWAP for the VM in MB.",
174 description
=> "Set a host name for the container.",
175 type
=> 'string', format
=> 'dns-name',
181 description
=> "Container description. Only used on the configuration web interface.",
185 type
=> 'string', format
=> 'dns-name-list',
186 description
=> "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain nor nameserver.",
190 type
=> 'string', format
=> 'address-list',
191 description
=> "Sets DNS server IP address for a container. Create will automatically use the setting from the host if you neither set searchdomain nor nameserver.",
193 rootfs
=> get_standard_option
('pve-ct-rootfs'),
196 type
=> 'string', format
=> 'pve-configid',
198 description
=> "Parent snapshot name. This is used internally, and should not be modified.",
202 description
=> "Timestamp for snapshots.",
208 description
=> "Console mode. By default, the console command tries to open a connection to one of the available tty devices. By setting cmode to 'console' it tries to attach to /dev/console instead. If you set cmode to 'shell', it simply invokes a shell inside the container (no login).",
210 enum
=> ['shell', 'console', 'tty'],
216 description
=> "Sets the protection flag of the container. This will prevent the CT or CT's disk remove/update operation.",
222 description
=> "Makes the container run as unprivileged user. (Should not be modified manually.)",
227 my $valid_lxc_conf_keys = {
231 'lxc.haltsignal' => 1,
232 'lxc.rebootsignal' => 1,
233 'lxc.stopsignal' => 1,
235 'lxc.network.type' => 1,
236 'lxc.network.flags' => 1,
237 'lxc.network.link' => 1,
238 'lxc.network.mtu' => 1,
239 'lxc.network.name' => 1,
240 'lxc.network.hwaddr' => 1,
241 'lxc.network.ipv4' => 1,
242 'lxc.network.ipv4.gateway' => 1,
243 'lxc.network.ipv6' => 1,
244 'lxc.network.ipv6.gateway' => 1,
245 'lxc.network.script.up' => 1,
246 'lxc.network.script.down' => 1,
248 'lxc.console.logfile' => 1,
251 'lxc.devttydir' => 1,
252 'lxc.hook.autodev' => 1,
256 'lxc.mount.entry' => 1,
257 'lxc.mount.auto' => 1,
258 'lxc.rootfs' => 'lxc.rootfs is auto generated from rootfs',
259 'lxc.rootfs.mount' => 1,
260 'lxc.rootfs.options' => 'lxc.rootfs.options is not supported' .
261 ', please use mountpoint options in the "rootfs" key',
265 'lxc.aa_profile' => 1,
266 'lxc.aa_allow_incomplete' => 1,
267 'lxc.se_context' => 1,
270 'lxc.hook.pre-start' => 1,
271 'lxc.hook.pre-mount' => 1,
272 'lxc.hook.mount' => 1,
273 'lxc.hook.start' => 1,
274 'lxc.hook.stop' => 1,
275 'lxc.hook.post-stop' => 1,
276 'lxc.hook.clone' => 1,
277 'lxc.hook.destroy' => 1,
280 'lxc.start.auto' => 1,
281 'lxc.start.delay' => 1,
282 'lxc.start.order' => 1,
284 'lxc.environment' => 1,
291 description
=> "Network interface type.",
296 format_description
=> 'String',
297 description
=> 'Name of the network device as seen from inside the container. (lxc.network.name)',
298 pattern
=> '[-_.\w\d]+',
302 format_description
=> 'vmbr<Number>',
303 description
=> 'Bridge to attach the network device to.',
304 pattern
=> '[-_.\w\d]+',
309 format_description
=> 'MAC',
310 description
=> 'Bridge to attach the network device to. (lxc.network.hwaddr)',
311 pattern
=> qr/(?:[a-f0-9]{2}:){5}[a-f0-9]{2}/i,
316 format_description
=> 'Number',
317 description
=> 'Maximum transfer unit of the interface. (lxc.network.mtu)',
318 minimum
=> 64, # minimum ethernet frame is 64 bytes
323 format
=> 'pve-ipv4-config',
324 format_description
=> 'IPv4Format/CIDR',
325 description
=> 'IPv4 address in CIDR format.',
331 format_description
=> 'GatewayIPv4',
332 description
=> 'Default gateway for IPv4 traffic.',
337 format
=> 'pve-ipv6-config',
338 format_description
=> 'IPv6Format/CIDR',
339 description
=> 'IPv6 address in CIDR format.',
345 format_description
=> 'GatewayIPv6',
346 description
=> 'Default gateway for IPv6 traffic.',
351 format_description
=> '[1|0]',
352 description
=> "Controls whether this interface's firewall rules should be used.",
357 format_description
=> 'VlanNo',
360 description
=> "VLAN tag for this interface.",
365 pattern
=> qr/\d+(?:;\d+)*/,
366 format_description
=> 'vlanid[;vlanid...]',
367 description
=> "VLAN ids to pass through the interface",
371 PVE
::JSONSchema
::register_format
('pve-lxc-network', $netconf_desc);
373 my $MAX_LXC_NETWORKS = 10;
374 for (my $i = 0; $i < $MAX_LXC_NETWORKS; $i++) {
375 $confdesc->{"net$i"} = {
377 type
=> 'string', format
=> $netconf_desc,
378 description
=> "Specifies network interfaces for the container.",
382 PVE
::JSONSchema
::register_format
('pve-lxc-mp-string', \
&verify_lxc_mp_string
);
383 sub verify_lxc_mp_string
{
384 my ($mp, $noerr) = @_;
388 # /. or /.. at the end
389 # ../ at the beginning
391 if($mp =~ m
@/\.\
.?
/@ ||
394 return undef if $noerr;
395 die "$mp contains illegal character sequences\n";
404 format
=> 'pve-lxc-mp-string',
405 format_description
=> 'Path',
406 description
=> 'Path to the mountpoint as seen from inside the container.',
409 PVE
::JSONSchema
::register_format
('pve-ct-mountpoint', $mp_desc);
413 type
=> 'string', format
=> 'pve-volume-id',
414 description
=> "Reference to unused volumes.",
417 my $MAX_MOUNT_POINTS = 10;
418 for (my $i = 0; $i < $MAX_MOUNT_POINTS; $i++) {
419 $confdesc->{"mp$i"} = {
421 type
=> 'string', format
=> $mp_desc,
422 description
=> "Use volume as container mount point (experimental feature).",
427 my $MAX_UNUSED_DISKS = $MAX_MOUNT_POINTS;
428 for (my $i = 0; $i < $MAX_MOUNT_POINTS; $i++) {
429 $confdesc->{"unused$i"} = $unuseddesc;
432 sub write_pct_config
{
433 my ($filename, $conf) = @_;
435 delete $conf->{snapstate
}; # just to be sure
436 my $volidlist = get_vm_volumes
($conf);
437 my $used_volids = {};
438 foreach my $vid (@$volidlist) {
439 $used_volids->{$vid} = 1;
442 # remove 'unusedX' settings if the volume is still used
443 foreach my $key (keys %$conf) {
444 my $value = $conf->{$key};
445 if ($key =~ m/^unused/ && $used_volids->{$value}) {
446 delete $conf->{$key};
450 my $generate_raw_config = sub {
455 # add description as comment to top of file
456 my $descr = $conf->{description
} || '';
457 foreach my $cl (split(/\n/, $descr)) {
458 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
461 foreach my $key (sort keys %$conf) {
462 next if $key eq 'digest' || $key eq 'description' || $key eq 'pending' ||
463 $key eq 'snapshots' || $key eq 'snapname' || $key eq 'lxc';
464 my $value = $conf->{$key};
465 die "detected invalid newline inside property '$key'\n" if $value =~ m/\n/;
466 $raw .= "$key: $value\n";
469 if (my $lxcconf = $conf->{lxc
}) {
470 foreach my $entry (@$lxcconf) {
471 my ($k, $v) = @$entry;
479 my $raw = &$generate_raw_config($conf);
481 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
482 $raw .= "\n[$snapname]\n";
483 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
490 my ($key, $value) = @_;
492 die "unknown setting '$key'\n" if !$confdesc->{$key};
494 my $type = $confdesc->{$key}->{type
};
496 if (!defined($value)) {
497 die "got undefined value\n";
500 if ($value =~ m/[\n\r]/) {
501 die "property contains a line feed\n";
504 if ($type eq 'boolean') {
505 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
506 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
507 die "type check ('boolean') failed - got '$value'\n";
508 } elsif ($type eq 'integer') {
509 return int($1) if $value =~ m/^(\d+)$/;
510 die "type check ('integer') failed - got '$value'\n";
511 } elsif ($type eq 'number') {
512 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
513 die "type check ('number') failed - got '$value'\n";
514 } elsif ($type eq 'string') {
515 if (my $fmt = $confdesc->{$key}->{format
}) {
516 PVE
::JSONSchema
::check_format
($fmt, $value);
525 sub parse_pct_config
{
526 my ($filename, $raw) = @_;
528 return undef if !defined($raw);
531 digest
=> Digest
::SHA
::sha1_hex
($raw),
535 $filename =~ m
|/lxc/(\d
+).conf
$|
536 || die "got strange filename '$filename'";
544 my @lines = split(/\n/, $raw);
545 foreach my $line (@lines) {
546 next if $line =~ m/^\s*$/;
548 if ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
550 $conf->{description
} = $descr if $descr;
552 $conf = $res->{snapshots
}->{$section} = {};
556 if ($line =~ m/^\#(.*)\s*$/) {
557 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
561 if ($line =~ m/^(lxc\.[a-z0-9_\-\.]+)(:|\s*=)\s*(.*?)\s*$/) {
564 my $validity = $valid_lxc_conf_keys->{$key} || 0;
565 if ($validity eq 1 || $key =~ m/^lxc\.cgroup\./) {
566 push @{$conf->{lxc
}}, [$key, $value];
567 } elsif (my $errmsg = $validity) {
568 warn "vm $vmid - $key: $errmsg\n";
570 warn "vm $vmid - unable to parse config: $line\n";
572 } elsif ($line =~ m/^(description):\s*(.*\S)\s*$/) {
573 $descr .= PVE
::Tools
::decode_text
($2);
574 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
575 $conf->{snapstate
} = $1;
576 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(\S.*)\s*$/) {
579 eval { $value = check_type
($key, $value); };
580 warn "vm $vmid - unable to parse value of '$key' - $@" if $@;
581 $conf->{$key} = $value;
583 warn "vm $vmid - unable to parse config: $line\n";
587 $conf->{description
} = $descr if $descr;
589 delete $res->{snapstate
}; # just to be sure
595 my $vmlist = PVE
::Cluster
::get_vmlist
();
597 return $res if !$vmlist || !$vmlist->{ids
};
598 my $ids = $vmlist->{ids
};
600 foreach my $vmid (keys %$ids) {
601 next if !$vmid; # skip CT0
602 my $d = $ids->{$vmid};
603 next if !$d->{node
} || $d->{node
} ne $nodename;
604 next if !$d->{type
} || $d->{type
} ne 'lxc';
605 $res->{$vmid}->{type
} = 'lxc';
613 unlink PVE
::LXC
::Config-
>config_file($vmid, $nodename);
619 return defined($confdesc->{$name});
622 # add JSON properties for create and set function
623 sub json_config_properties
{
626 foreach my $opt (keys %$confdesc) {
627 next if $opt eq 'parent' || $opt eq 'snaptime';
628 next if $prop->{$opt};
629 $prop->{$opt} = $confdesc->{$opt};
635 # container status helpers
637 sub list_active_containers
{
639 my $filename = "/proc/net/unix";
641 # similar test is used by lcxcontainers.c: list_active_containers
644 my $fh = IO
::File-
>new ($filename, "r");
647 while (defined(my $line = <$fh>)) {
648 if ($line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/) {
650 if ($path =~ m!^@/var/lib/lxc/(\d+)/command$!) {
661 # warning: this is slow
665 my $active_hash = list_active_containers
();
667 return 1 if defined($active_hash->{$vmid});
672 sub get_container_disk_usage
{
673 my ($vmid, $pid) = @_;
675 return PVE
::Tools
::df
("/proc/$pid/root/", 1);
678 my $last_proc_vmid_stat;
680 my $parse_cpuacct_stat = sub {
683 my $raw = read_cgroup_value
('cpuacct', $vmid, 'cpuacct.stat', 1);
687 if ($raw =~ m/^user (\d+)\nsystem (\d+)\n/) {
700 my $list = $opt_vmid ?
{ $opt_vmid => { type
=> 'lxc' }} : config_list
();
702 my $active_hash = list_active_containers
();
704 my $cpucount = $cpuinfo->{cpus
} || 1;
706 my $cdtime = gettimeofday
;
708 my $uptime = (PVE
::ProcFSTools
::read_proc_uptime
(1))[0];
710 foreach my $vmid (keys %$list) {
711 my $d = $list->{$vmid};
713 eval { $d->{pid
} = find_lxc_pid
($vmid) if defined($active_hash->{$vmid}); };
714 warn $@ if $@; # ignore errors (consider them stopped)
716 $d->{status
} = $d->{pid
} ?
'running' : 'stopped';
718 my $cfspath = PVE
::LXC
::Config-
>cfs_config_path($vmid);
719 my $conf = PVE
::Cluster
::cfs_read_file
($cfspath) || {};
721 $d->{name
} = $conf->{'hostname'} || "CT$vmid";
722 $d->{name
} =~ s/[\s]//g;
724 $d->{cpus
} = $conf->{cpulimit
} || $cpucount;
726 $d->{lock} = $conf->{lock} || '';
729 my $res = get_container_disk_usage
($vmid, $d->{pid
});
730 $d->{disk
} = $res->{used
};
731 $d->{maxdisk
} = $res->{total
};
734 # use 4GB by default ??
735 if (my $rootfs = $conf->{rootfs
}) {
736 my $rootinfo = parse_ct_rootfs
($rootfs);
737 $d->{maxdisk
} = int(($rootinfo->{size
} || 4)*1024*1024)*1024;
739 $d->{maxdisk
} = 4*1024*1024*1024;
745 $d->{maxmem
} = ($conf->{memory
}||512)*1024*1024;
746 $d->{maxswap
} = ($conf->{swap
}//0)*1024*1024;
757 $d->{template
} = PVE
::LXC
::Config-
>is_template($conf);
760 foreach my $vmid (keys %$list) {
761 my $d = $list->{$vmid};
764 next if !$pid; # skip stopped CTs
766 my $ctime = (stat("/proc/$pid"))[10]; # 10 = ctime
767 $d->{uptime
} = time - $ctime; # the method lxcfs uses
769 $d->{mem
} = read_cgroup_value
('memory', $vmid, 'memory.usage_in_bytes');
770 $d->{swap
} = read_cgroup_value
('memory', $vmid, 'memory.memsw.usage_in_bytes') - $d->{mem
};
772 my $blkio_bytes = read_cgroup_value
('blkio', $vmid, 'blkio.throttle.io_service_bytes', 1);
773 my @bytes = split(/\n/, $blkio_bytes);
774 foreach my $byte (@bytes) {
775 if (my ($key, $value) = $byte =~ /(Read|Write)\s+(\d+)/) {
776 $d->{diskread
} = $2 if $key eq 'Read';
777 $d->{diskwrite
} = $2 if $key eq 'Write';
781 my $pstat = &$parse_cpuacct_stat($vmid);
783 my $used = $pstat->{utime} + $pstat->{stime
};
785 my $old = $last_proc_vmid_stat->{$vmid};
787 $last_proc_vmid_stat->{$vmid} = {
795 my $dtime = ($cdtime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
798 my $dutime = $used - $old->{used
};
800 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
801 $last_proc_vmid_stat->{$vmid} = {
807 $d->{cpu
} = $old->{cpu
};
811 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
813 foreach my $dev (keys %$netdev) {
814 next if $dev !~ m/^veth([1-9]\d*)i/;
816 my $d = $list->{$vmid};
820 $d->{netout
} += $netdev->{$dev}->{receive
};
821 $d->{netin
} += $netdev->{$dev}->{transmit
};
828 sub classify_mountpoint
{
831 return 'device' if $vol =~ m!^/dev/!;
837 my $parse_ct_mountpoint_full = sub {
838 my ($desc, $data, $noerr) = @_;
843 eval { $res = PVE
::JSONSchema
::parse_property_string
($desc, $data) };
845 return undef if $noerr;
849 if (defined(my $size = $res->{size
})) {
850 $size = PVE
::JSONSchema
::parse_size
($size);
851 if (!defined($size)) {
852 return undef if $noerr;
853 die "invalid size: $size\n";
855 $res->{size
} = $size;
858 $res->{type
} = classify_mountpoint
($res->{volume
});
863 sub parse_ct_rootfs
{
864 my ($data, $noerr) = @_;
866 my $res = &$parse_ct_mountpoint_full($rootfs_desc, $data, $noerr);
868 $res->{mp
} = '/' if defined($res);
873 sub parse_ct_mountpoint
{
874 my ($data, $noerr) = @_;
876 return &$parse_ct_mountpoint_full($mp_desc, $data, $noerr);
879 sub print_ct_mountpoint
{
880 my ($info, $nomp) = @_;
881 my $skip = [ 'type' ];
882 push @$skip, 'mp' if $nomp;
883 return PVE
::JSONSchema
::print_property_string
($info, $mp_desc, $skip);
886 sub print_lxc_network
{
888 return PVE
::JSONSchema
::print_property_string
($net, $netconf_desc);
891 sub parse_lxc_network
{
896 return $res if !$data;
898 $res = PVE
::JSONSchema
::parse_property_string
($netconf_desc, $data);
900 $res->{type
} = 'veth';
901 $res->{hwaddr
} = PVE
::Tools
::random_ether_addr
() if !$res->{hwaddr
};
906 sub read_cgroup_value
{
907 my ($group, $vmid, $name, $full) = @_;
909 my $path = "/sys/fs/cgroup/$group/lxc/$vmid/$name";
911 return PVE
::Tools
::file_get_contents
($path) if $full;
913 return PVE
::Tools
::file_read_firstline
($path);
916 sub write_cgroup_value
{
917 my ($group, $vmid, $name, $value) = @_;
919 my $path = "/sys/fs/cgroup/$group/lxc/$vmid/$name";
920 PVE
::ProcFSTools
::write_proc_entry
($path, $value) if -e
$path;
924 sub find_lxc_console_pids
{
928 PVE
::Tools
::dir_glob_foreach
('/proc', '\d+', sub {
931 my $cmdline = PVE
::Tools
::file_read_firstline
("/proc/$pid/cmdline");
934 my @args = split(/\0/, $cmdline);
936 # search for lxc-console -n <vmid>
937 return if scalar(@args) != 3;
938 return if $args[1] ne '-n';
939 return if $args[2] !~ m/^\d+$/;
940 return if $args[0] !~ m
|^(/usr/bin
/)?lxc-console
$|;
944 push @{$res->{$vmid}}, $pid;
956 $pid = $1 if $line =~ m/^PID:\s+(\d+)$/;
958 PVE
::Tools
::run_command
(['lxc-info', '-n', $vmid, '-p'], outfunc
=> $parser);
960 die "unable to get PID for CT $vmid (not running?)\n" if !$pid;
965 # Note: we cannot use Net:IP, because that only allows strict
967 sub parse_ipv4_cidr
{
968 my ($cidr, $noerr) = @_;
970 if ($cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ($2 > 7) && ($2 <= 32)) {
971 return { address
=> $1, netmask
=> $PVE::Network
::ipv4_reverse_mask-
>[$2] };
974 return undef if $noerr;
976 die "unable to parse ipv4 address/mask\n";
980 sub update_lxc_config
{
981 my ($storage_cfg, $vmid, $conf) = @_;
983 my $dir = "/var/lib/lxc/$vmid";
985 if ($conf->{template
}) {
987 unlink "$dir/config";
994 die "missing 'arch' - internal error" if !$conf->{arch
};
995 $raw .= "lxc.arch = $conf->{arch}\n";
997 my $unprivileged = $conf->{unprivileged
};
998 my $custom_idmap = grep { $_->[0] eq 'lxc.id_map' } @{$conf->{lxc
}};
1000 my $ostype = $conf->{ostype
} || die "missing 'ostype' - internal error";
1001 if ($ostype =~ /^(?:debian | ubuntu | centos | fedora | opensuse | archlinux | alpine | unmanaged)$/x) {
1002 my $inc ="/usr/share/lxc/config/$ostype.common.conf";
1003 $inc ="/usr/share/lxc/config/common.conf" if !-f
$inc;
1004 $raw .= "lxc.include = $inc\n";
1005 if ($unprivileged || $custom_idmap) {
1006 $inc = "/usr/share/lxc/config/$ostype.userns.conf";
1007 $inc = "/usr/share/lxc/config/userns.conf" if !-f
$inc;
1008 $raw .= "lxc.include = $inc\n"
1011 die "implement me (ostype $ostype)";
1014 # WARNING: DO NOT REMOVE this without making sure that loop device nodes
1015 # cannot be exposed to the container with r/w access (cgroup perms).
1016 # When this is enabled mounts will still remain in the monitor's namespace
1017 # after the container unmounted them and thus will not detach from their
1018 # files while the container is running!
1019 $raw .= "lxc.monitor.unshare = 1\n";
1021 # Should we read them from /etc/subuid?
1022 if ($unprivileged && !$custom_idmap) {
1023 $raw .= "lxc.id_map = u 0 100000 65536\n";
1024 $raw .= "lxc.id_map = g 0 100000 65536\n";
1027 if (!has_dev_console
($conf)) {
1028 $raw .= "lxc.console = none\n";
1029 $raw .= "lxc.cgroup.devices.deny = c 5:1 rwm\n";
1032 my $ttycount = get_tty_count
($conf);
1033 $raw .= "lxc.tty = $ttycount\n";
1035 # some init scripts expect a linux terminal (turnkey).
1036 $raw .= "lxc.environment = TERM=linux\n";
1038 my $utsname = $conf->{hostname
} || "CT$vmid";
1039 $raw .= "lxc.utsname = $utsname\n";
1041 my $memory = $conf->{memory
} || 512;
1042 my $swap = $conf->{swap
} // 0;
1044 my $lxcmem = int($memory*1024*1024);
1045 $raw .= "lxc.cgroup.memory.limit_in_bytes = $lxcmem\n";
1047 my $lxcswap = int(($memory + $swap)*1024*1024);
1048 $raw .= "lxc.cgroup.memory.memsw.limit_in_bytes = $lxcswap\n";
1050 if (my $cpulimit = $conf->{cpulimit
}) {
1051 $raw .= "lxc.cgroup.cpu.cfs_period_us = 100000\n";
1052 my $value = int(100000*$cpulimit);
1053 $raw .= "lxc.cgroup.cpu.cfs_quota_us = $value\n";
1056 my $shares = $conf->{cpuunits
} || 1024;
1057 $raw .= "lxc.cgroup.cpu.shares = $shares\n";
1059 my $mountpoint = parse_ct_rootfs
($conf->{rootfs
});
1061 $raw .= "lxc.rootfs = $dir/rootfs\n";
1064 foreach my $k (keys %$conf) {
1065 next if $k !~ m/^net(\d+)$/;
1067 my $d = parse_lxc_network
($conf->{$k});
1069 $raw .= "lxc.network.type = veth\n";
1070 $raw .= "lxc.network.veth.pair = veth${vmid}i${ind}\n";
1071 $raw .= "lxc.network.hwaddr = $d->{hwaddr}\n" if defined($d->{hwaddr
});
1072 $raw .= "lxc.network.name = $d->{name}\n" if defined($d->{name
});
1073 $raw .= "lxc.network.mtu = $d->{mtu}\n" if defined($d->{mtu
});
1076 if (my $lxcconf = $conf->{lxc
}) {
1077 foreach my $entry (@$lxcconf) {
1078 my ($k, $v) = @$entry;
1079 $netcount++ if $k eq 'lxc.network.type';
1080 $raw .= "$k = $v\n";
1084 $raw .= "lxc.network.type = empty\n" if !$netcount;
1086 File
::Path
::mkpath
("$dir/rootfs");
1088 PVE
::Tools
::file_set_contents
("$dir/config", $raw);
1091 # verify and cleanup nameserver list (replace \0 with ' ')
1092 sub verify_nameserver_list
{
1093 my ($nameserver_list) = @_;
1096 foreach my $server (PVE
::Tools
::split_list
($nameserver_list)) {
1097 PVE
::JSONSchema
::pve_verify_ip
($server);
1098 push @list, $server;
1101 return join(' ', @list);
1104 sub verify_searchdomain_list
{
1105 my ($searchdomain_list) = @_;
1108 foreach my $server (PVE
::Tools
::split_list
($searchdomain_list)) {
1109 # todo: should we add checks for valid dns domains?
1110 push @list, $server;
1113 return join(' ', @list);
1116 sub is_volume_in_use
{
1117 my ($config, $volid, $include_snapshots) = @_;
1120 foreach_mountpoint
($config, sub {
1121 my ($ms, $mountpoint) = @_;
1123 if ($mountpoint->{type
} eq 'volume' && $mountpoint->{volume
} eq $volid) {
1128 my $snapshots = $config->{snapshots
};
1129 if ($include_snapshots && $snapshots) {
1130 foreach my $snap (keys %$snapshots) {
1131 $used ||= is_volume_in_use
($snapshots->{$snap}, $volid);
1138 sub add_unused_volume
{
1139 my ($config, $volid) = @_;
1142 for (my $ind = $MAX_UNUSED_DISKS - 1; $ind >= 0; $ind--) {
1143 my $test = "unused$ind";
1144 if (my $vid = $config->{$test}) {
1145 return if $vid eq $volid; # do not add duplicates
1151 die "Too many unused volumes - please delete them first.\n" if !$key;
1153 $config->{$key} = $volid;
1158 sub update_pct_config
{
1159 my ($vmid, $conf, $running, $param, $delete) = @_;
1164 my @deleted_volumes;
1168 my $pid = find_lxc_pid
($vmid);
1169 $rootdir = "/proc/$pid/root";
1172 my $hotplug_error = sub {
1174 push @nohotplug, @_;
1181 if (defined($delete)) {
1182 foreach my $opt (@$delete) {
1183 if (!exists($conf->{$opt})) {
1184 warn "no such option: $opt\n";
1188 if ($opt eq 'hostname' || $opt eq 'memory' || $opt eq 'rootfs') {
1189 die "unable to delete required option '$opt'\n";
1190 } elsif ($opt eq 'swap') {
1191 delete $conf->{$opt};
1192 write_cgroup_value
("memory", $vmid, "memory.memsw.limit_in_bytes", -1);
1193 } elsif ($opt eq 'description' || $opt eq 'onboot' || $opt eq 'startup') {
1194 delete $conf->{$opt};
1195 } elsif ($opt eq 'nameserver' || $opt eq 'searchdomain' ||
1196 $opt eq 'tty' || $opt eq 'console' || $opt eq 'cmode') {
1197 next if $hotplug_error->($opt);
1198 delete $conf->{$opt};
1199 } elsif ($opt =~ m/^net(\d)$/) {
1200 delete $conf->{$opt};
1203 PVE
::Network
::veth_delete
("veth${vmid}i$netid");
1204 } elsif ($opt eq 'protection') {
1205 delete $conf->{$opt};
1206 } elsif ($opt =~ m/^unused(\d+)$/) {
1207 next if $hotplug_error->($opt);
1208 check_protection
($conf, "can't remove CT $vmid drive '$opt'");
1209 push @deleted_volumes, $conf->{$opt};
1210 delete $conf->{$opt};
1211 } elsif ($opt =~ m/^mp(\d+)$/) {
1212 next if $hotplug_error->($opt);
1213 check_protection
($conf, "can't remove CT $vmid drive '$opt'");
1214 my $mp = parse_ct_mountpoint
($conf->{$opt});
1215 delete $conf->{$opt};
1216 if ($mp->{type
} eq 'volume') {
1217 add_unused_volume
($conf, $mp->{volume
});
1219 } elsif ($opt eq 'unprivileged') {
1220 die "unable to delete read-only option: '$opt'\n";
1222 die "implement me (delete: $opt)"
1224 PVE
::LXC
::Config-
>write_config($vmid, $conf) if $running;
1228 # There's no separate swap size to configure, there's memory and "total"
1229 # memory (iow. memory+swap). This means we have to change them together.
1230 my $wanted_memory = PVE
::Tools
::extract_param
($param, 'memory');
1231 my $wanted_swap = PVE
::Tools
::extract_param
($param, 'swap');
1232 if (defined($wanted_memory) || defined($wanted_swap)) {
1234 my $old_memory = ($conf->{memory
} || 512);
1235 my $old_swap = ($conf->{swap
} || 0);
1237 $wanted_memory //= $old_memory;
1238 $wanted_swap //= $old_swap;
1240 my $total = $wanted_memory + $wanted_swap;
1242 my $old_total = $old_memory + $old_swap;
1243 if ($total > $old_total) {
1244 write_cgroup_value
("memory", $vmid, "memory.memsw.limit_in_bytes", int($total*1024*1024));
1245 write_cgroup_value
("memory", $vmid, "memory.limit_in_bytes", int($wanted_memory*1024*1024));
1247 write_cgroup_value
("memory", $vmid, "memory.limit_in_bytes", int($wanted_memory*1024*1024));
1248 write_cgroup_value
("memory", $vmid, "memory.memsw.limit_in_bytes", int($total*1024*1024));
1251 $conf->{memory
} = $wanted_memory;
1252 $conf->{swap
} = $wanted_swap;
1254 PVE
::LXC
::Config-
>write_config($vmid, $conf) if $running;
1257 my $used_volids = {};
1259 foreach my $opt (keys %$param) {
1260 my $value = $param->{$opt};
1261 if ($opt eq 'hostname') {
1262 $conf->{$opt} = $value;
1263 } elsif ($opt eq 'onboot') {
1264 $conf->{$opt} = $value ?
1 : 0;
1265 } elsif ($opt eq 'startup') {
1266 $conf->{$opt} = $value;
1267 } elsif ($opt eq 'tty' || $opt eq 'console' || $opt eq 'cmode') {
1268 next if $hotplug_error->($opt);
1269 $conf->{$opt} = $value;
1270 } elsif ($opt eq 'nameserver') {
1271 next if $hotplug_error->($opt);
1272 my $list = verify_nameserver_list
($value);
1273 $conf->{$opt} = $list;
1274 } elsif ($opt eq 'searchdomain') {
1275 next if $hotplug_error->($opt);
1276 my $list = verify_searchdomain_list
($value);
1277 $conf->{$opt} = $list;
1278 } elsif ($opt eq 'cpulimit') {
1279 next if $hotplug_error->($opt); # FIXME: hotplug
1280 $conf->{$opt} = $value;
1281 } elsif ($opt eq 'cpuunits') {
1282 $conf->{$opt} = $value;
1283 write_cgroup_value
("cpu", $vmid, "cpu.shares", $value);
1284 } elsif ($opt eq 'description') {
1285 $conf->{$opt} = PVE
::Tools
::encode_text
($value);
1286 } elsif ($opt =~ m/^net(\d+)$/) {
1288 my $net = parse_lxc_network
($value);
1290 $conf->{$opt} = print_lxc_network
($net);
1292 update_net
($vmid, $conf, $opt, $net, $netid, $rootdir);
1294 } elsif ($opt eq 'protection') {
1295 $conf->{$opt} = $value ?
1 : 0;
1296 } elsif ($opt =~ m/^mp(\d+)$/) {
1297 next if $hotplug_error->($opt);
1298 check_protection
($conf, "can't update CT $vmid drive '$opt'");
1299 my $old = $conf->{$opt};
1300 $conf->{$opt} = $value;
1301 if (defined($old)) {
1302 my $mp = parse_ct_mountpoint
($old);
1303 if ($mp->{type
} eq 'volume') {
1304 add_unused_volume
($conf, $mp->{volume
});
1308 my $mp = parse_ct_mountpoint
($value);
1309 $used_volids->{$mp->{volume
}} = 1;
1310 } elsif ($opt eq 'rootfs') {
1311 next if $hotplug_error->($opt);
1312 check_protection
($conf, "can't update CT $vmid drive '$opt'");
1313 my $old = $conf->{$opt};
1314 $conf->{$opt} = $value;
1315 if (defined($old)) {
1316 my $mp = parse_ct_rootfs
($old);
1317 if ($mp->{type
} eq 'volume') {
1318 add_unused_volume
($conf, $mp->{volume
});
1321 my $mp = parse_ct_rootfs
($value);
1322 $used_volids->{$mp->{volume
}} = 1;
1323 } elsif ($opt eq 'unprivileged') {
1324 die "unable to modify read-only option: '$opt'\n";
1325 } elsif ($opt eq 'ostype') {
1326 next if $hotplug_error->($opt);
1327 $conf->{$opt} = $value;
1329 die "implement me: $opt";
1331 PVE
::LXC
::Config-
>write_config($vmid, $conf) if $running;
1334 # Apply deletions and creations of new volumes
1335 if (@deleted_volumes) {
1336 my $storage_cfg = PVE
::Storage
::config
();
1337 foreach my $volume (@deleted_volumes) {
1338 next if $used_volids->{$volume}; # could have been re-added, too
1339 # also check for references in snapshots
1340 next if is_volume_in_use
($conf, $volume, 1);
1341 delete_mountpoint_volume
($storage_cfg, $vmid, $volume);
1346 my $storage_cfg = PVE
::Storage
::config
();
1347 create_disks
($storage_cfg, $vmid, $conf, $conf);
1350 # This should be the last thing we do here
1351 if ($running && scalar(@nohotplug)) {
1352 die "unable to modify " . join(',', @nohotplug) . " while container is running\n";
1356 sub has_dev_console
{
1359 return !(defined($conf->{console
}) && !$conf->{console
});
1365 return $conf->{tty
} // $confdesc->{tty
}->{default};
1371 return $conf->{cmode
} // $confdesc->{cmode
}->{default};
1374 sub get_console_command
{
1375 my ($vmid, $conf) = @_;
1377 my $cmode = get_cmode
($conf);
1379 if ($cmode eq 'console') {
1380 return ['lxc-console', '-n', $vmid, '-t', 0];
1381 } elsif ($cmode eq 'tty') {
1382 return ['lxc-console', '-n', $vmid];
1383 } elsif ($cmode eq 'shell') {
1384 return ['lxc-attach', '--clear-env', '-n', $vmid];
1386 die "internal error";
1390 sub get_primary_ips
{
1393 # return data from net0
1395 return undef if !defined($conf->{net0
});
1396 my $net = parse_lxc_network
($conf->{net0
});
1398 my $ipv4 = $net->{ip
};
1400 if ($ipv4 =~ /^(dhcp|manual)$/) {
1406 my $ipv6 = $net->{ip6
};
1408 if ($ipv6 =~ /^(auto|dhcp|manual)$/) {
1415 return ($ipv4, $ipv6);
1418 sub delete_mountpoint_volume
{
1419 my ($storage_cfg, $vmid, $volume) = @_;
1421 return if classify_mountpoint
($volume) ne 'volume';
1423 my ($vtype, $name, $owner) = PVE
::Storage
::parse_volname
($storage_cfg, $volume);
1424 PVE
::Storage
::vdisk_free
($storage_cfg, $volume) if $vmid == $owner;
1427 sub destroy_lxc_container
{
1428 my ($storage_cfg, $vmid, $conf) = @_;
1430 foreach_mountpoint
($conf, sub {
1431 my ($ms, $mountpoint) = @_;
1432 delete_mountpoint_volume
($storage_cfg, $vmid, $mountpoint->{volume
});
1435 rmdir "/var/lib/lxc/$vmid/rootfs";
1436 unlink "/var/lib/lxc/$vmid/config";
1437 rmdir "/var/lib/lxc/$vmid";
1438 destroy_config
($vmid);
1440 #my $cmd = ['lxc-destroy', '-n', $vmid ];
1441 #PVE::Tools::run_command($cmd);
1444 sub vm_stop_cleanup
{
1445 my ($storage_cfg, $vmid, $conf, $keepActive) = @_;
1450 my $vollist = get_vm_volumes
($conf);
1451 PVE
::Storage
::deactivate_volumes
($storage_cfg, $vollist);
1454 warn $@ if $@; # avoid errors - just warn
1457 my $safe_num_ne = sub {
1460 return 0 if !defined($a) && !defined($b);
1461 return 1 if !defined($a);
1462 return 1 if !defined($b);
1467 my $safe_string_ne = sub {
1470 return 0 if !defined($a) && !defined($b);
1471 return 1 if !defined($a);
1472 return 1 if !defined($b);
1478 my ($vmid, $conf, $opt, $newnet, $netid, $rootdir) = @_;
1480 if ($newnet->{type
} ne 'veth') {
1481 # for when there are physical interfaces
1482 die "cannot update interface of type $newnet->{type}";
1485 my $veth = "veth${vmid}i${netid}";
1486 my $eth = $newnet->{name
};
1488 if (my $oldnetcfg = $conf->{$opt}) {
1489 my $oldnet = parse_lxc_network
($oldnetcfg);
1491 if (&$safe_string_ne($oldnet->{hwaddr
}, $newnet->{hwaddr
}) ||
1492 &$safe_string_ne($oldnet->{name
}, $newnet->{name
})) {
1494 PVE
::Network
::veth_delete
($veth);
1495 delete $conf->{$opt};
1496 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1498 hotplug_net
($vmid, $conf, $opt, $newnet, $netid);
1500 } elsif (&$safe_string_ne($oldnet->{bridge
}, $newnet->{bridge
}) ||
1501 &$safe_num_ne($oldnet->{tag
}, $newnet->{tag
}) ||
1502 &$safe_num_ne($oldnet->{firewall
}, $newnet->{firewall
})) {
1504 if ($oldnet->{bridge
}) {
1505 PVE
::Network
::tap_unplug
($veth);
1506 foreach (qw(bridge tag firewall)) {
1507 delete $oldnet->{$_};
1509 $conf->{$opt} = print_lxc_network
($oldnet);
1510 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1513 PVE
::Network
::tap_plug
($veth, $newnet->{bridge
}, $newnet->{tag
}, $newnet->{firewall
}, $newnet->{trunks
});
1514 foreach (qw(bridge tag firewall)) {
1515 $oldnet->{$_} = $newnet->{$_} if $newnet->{$_};
1517 $conf->{$opt} = print_lxc_network
($oldnet);
1518 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1521 hotplug_net
($vmid, $conf, $opt, $newnet, $netid);
1524 update_ipconfig
($vmid, $conf, $opt, $eth, $newnet, $rootdir);
1528 my ($vmid, $conf, $opt, $newnet, $netid) = @_;
1530 my $veth = "veth${vmid}i${netid}";
1531 my $vethpeer = $veth . "p";
1532 my $eth = $newnet->{name
};
1534 PVE
::Network
::veth_create
($veth, $vethpeer, $newnet->{bridge
}, $newnet->{hwaddr
});
1535 PVE
::Network
::tap_plug
($veth, $newnet->{bridge
}, $newnet->{tag
}, $newnet->{firewall
}, $newnet->{trunks
});
1537 # attach peer in container
1538 my $cmd = ['lxc-device', '-n', $vmid, 'add', $vethpeer, "$eth" ];
1539 PVE
::Tools
::run_command
($cmd);
1541 # link up peer in container
1542 $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'link', 'set', $eth ,'up' ];
1543 PVE
::Tools
::run_command
($cmd);
1545 my $done = { type
=> 'veth' };
1546 foreach (qw(bridge tag firewall hwaddr name)) {
1547 $done->{$_} = $newnet->{$_} if $newnet->{$_};
1549 $conf->{$opt} = print_lxc_network
($done);
1551 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1554 sub update_ipconfig
{
1555 my ($vmid, $conf, $opt, $eth, $newnet, $rootdir) = @_;
1557 my $lxc_setup = PVE
::LXC
::Setup-
>new($conf, $rootdir);
1559 my $optdata = parse_lxc_network
($conf->{$opt});
1563 my $cmdargs = shift;
1564 PVE
::Tools
::run_command
(['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', @_], %$cmdargs);
1566 my $ipcmd = sub { &$nscmd({}, '/sbin/ip', @_) };
1568 my $change_ip_config = sub {
1569 my ($ipversion) = @_;
1571 my $family_opt = "-$ipversion";
1572 my $suffix = $ipversion == 4 ?
'' : $ipversion;
1573 my $gw= "gw$suffix";
1574 my $ip= "ip$suffix";
1576 my $newip = $newnet->{$ip};
1577 my $newgw = $newnet->{$gw};
1578 my $oldip = $optdata->{$ip};
1580 my $change_ip = &$safe_string_ne($oldip, $newip);
1581 my $change_gw = &$safe_string_ne($optdata->{$gw}, $newgw);
1583 return if !$change_ip && !$change_gw;
1585 # step 1: add new IP, if this fails we cancel
1586 my $is_real_ip = ($newip && $newip !~ /^(?:auto|dhcp|manual)$/);
1587 if ($change_ip && $is_real_ip) {
1588 eval { &$ipcmd($family_opt, 'addr', 'add', $newip, 'dev', $eth); };
1595 # step 2: replace gateway
1596 # If this fails we delete the added IP and cancel.
1597 # If it succeeds we save the config and delete the old IP, ignoring
1598 # errors. The config is then saved.
1599 # Note: 'ip route replace' can add
1603 if ($is_real_ip && !PVE
::Network
::is_ip_in_cidr
($newgw, $newip, $ipversion)) {
1604 &$ipcmd($family_opt, 'route', 'add', $newgw, 'dev', $eth);
1606 &$ipcmd($family_opt, 'route', 'replace', 'default', 'via', $newgw);
1610 # the route was not replaced, the old IP is still available
1611 # rollback (delete new IP) and cancel
1613 eval { &$ipcmd($family_opt, 'addr', 'del', $newip, 'dev', $eth); };
1614 warn $@ if $@; # no need to die here
1619 eval { &$ipcmd($family_opt, 'route', 'del', 'default'); };
1620 # if the route was not deleted, the guest might have deleted it manually
1626 # from this point on we save the configuration
1627 # step 3: delete old IP ignoring errors
1628 if ($change_ip && $oldip && $oldip !~ /^(?:auto|dhcp)$/) {
1629 # We need to enable promote_secondaries, otherwise our newly added
1630 # address will be removed along with the old one.
1633 if ($ipversion == 4) {
1634 &$nscmd({ outfunc
=> sub { $promote = int(shift) } },
1635 'cat', "/proc/sys/net/ipv4/conf/$eth/promote_secondaries");
1636 &$nscmd({}, 'sysctl', "net.ipv4.conf.$eth.promote_secondaries=1");
1638 &$ipcmd($family_opt, 'addr', 'del', $oldip, 'dev', $eth);
1640 warn $@ if $@; # no need to die here
1642 if ($ipversion == 4) {
1643 &$nscmd({}, 'sysctl', "net.ipv4.conf.$eth.promote_secondaries=$promote");
1647 foreach my $property ($ip, $gw) {
1648 if ($newnet->{$property}) {
1649 $optdata->{$property} = $newnet->{$property};
1651 delete $optdata->{$property};
1654 $conf->{$opt} = print_lxc_network
($optdata);
1655 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1656 $lxc_setup->setup_network($conf);
1659 &$change_ip_config(4);
1660 &$change_ip_config(6);
1664 # Internal snapshots
1666 # NOTE: Snapshot create/delete involves several non-atomic
1667 # actions, and can take a long time.
1668 # So we try to avoid locking the file and use the 'lock' variable
1669 # inside the config file instead.
1671 my $snapshot_copy_config = sub {
1672 my ($source, $dest) = @_;
1674 foreach my $k (keys %$source) {
1675 next if $k eq 'snapshots';
1676 next if $k eq 'snapstate';
1677 next if $k eq 'snaptime';
1678 next if $k eq 'vmstate';
1679 next if $k eq 'lock';
1680 next if $k eq 'digest';
1681 next if $k eq 'description';
1682 next if $k =~ m/^unused\d+$/;
1684 $dest->{$k} = $source->{$k};
1688 my $snapshot_apply_config = sub {
1689 my ($conf, $snap) = @_;
1691 # copy snapshot list
1693 snapshots
=> $conf->{snapshots
},
1696 # keep description and list of unused disks
1697 foreach my $k (keys %$conf) {
1698 next if !($k =~ m/^unused\d+$/ || $k eq 'description');
1699 $newconf->{$k} = $conf->{$k};
1702 &$snapshot_copy_config($snap, $newconf);
1707 sub snapshot_save_vmstate
{
1708 die "implement me - snapshot_save_vmstate\n";
1711 sub snapshot_prepare
{
1712 my ($vmid, $snapname, $save_vmstate, $comment) = @_;
1716 my $updatefn = sub {
1718 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1720 die "you can't take a snapshot if it's a template\n"
1721 if PVE
::LXC
::Config-
>is_template($conf);
1723 PVE
::LXC
::Config-
>check_lock($conf);
1725 $conf->{lock} = 'snapshot';
1727 die "snapshot name '$snapname' already used\n"
1728 if defined($conf->{snapshots
}->{$snapname});
1730 my $storecfg = PVE
::Storage
::config
();
1731 die "snapshot feature is not available\n"
1732 if !has_feature
('snapshot', $conf, $storecfg, undef, undef, $snapname eq 'vzdump');
1734 $snap = $conf->{snapshots
}->{$snapname} = {};
1736 if ($save_vmstate && check_running
($vmid)) {
1737 snapshot_save_vmstate
($vmid, $conf, $snapname, $storecfg);
1740 &$snapshot_copy_config($conf, $snap);
1742 $snap->{snapstate
} = "prepare";
1743 $snap->{snaptime
} = time();
1744 $snap->{description
} = $comment if $comment;
1746 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1749 PVE
::LXC
::Config-
>lock_config($vmid, $updatefn);
1754 sub snapshot_commit
{
1755 my ($vmid, $snapname) = @_;
1757 my $updatefn = sub {
1759 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1761 die "missing snapshot lock\n"
1762 if !($conf->{lock} && $conf->{lock} eq 'snapshot');
1764 my $snap = $conf->{snapshots
}->{$snapname};
1765 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1767 die "wrong snapshot state\n"
1768 if !($snap->{snapstate
} && $snap->{snapstate
} eq "prepare");
1770 delete $snap->{snapstate
};
1771 delete $conf->{lock};
1773 $conf->{parent
} = $snapname;
1775 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1778 PVE
::LXC
::Config-
>lock_config($vmid, $updatefn);
1782 my ($feature, $conf, $storecfg, $snapname, $running, $backup_only) = @_;
1786 foreach_mountpoint
($conf, sub {
1787 my ($ms, $mountpoint) = @_;
1789 return if $err; # skip further test
1790 return if $backup_only && $ms ne 'rootfs' && !$mountpoint->{backup
};
1792 $err = 1 if !PVE
::Storage
::volume_has_feature
($storecfg, $feature, $mountpoint->{volume
}, $snapname, $running);
1795 return $err ?
0 : 1;
1798 my $enter_namespace = sub {
1799 my ($vmid, $pid, $which, $type) = @_;
1800 sysopen my $fd, "/proc/$pid/ns/$which", O_RDONLY
1801 or die "failed to open $which namespace of container $vmid: $!\n";
1802 PVE
::Tools
::setns
(fileno($fd), $type)
1803 or die "failed to enter $which namespace of container $vmid: $!\n";
1807 my $do_syncfs = sub {
1808 my ($vmid, $pid, $socket) = @_;
1810 &$enter_namespace($vmid, $pid, 'mnt', PVE
::Tools
::CLONE_NEWNS
);
1812 # Tell the parent process to start reading our /proc/mounts
1813 print {$socket} "go\n";
1816 # Receive /proc/self/mounts
1817 my $mountdata = do { local $/ = undef; <$socket> };
1820 # Now sync all mountpoints...
1821 my $mounts = PVE
::ProcFSTools
::parse_mounts
($mountdata);
1822 foreach my $mp (@$mounts) {
1823 my ($what, $dir, $fs) = @$mp;
1824 next if $fs eq 'fuse.lxcfs';
1825 eval { PVE
::Tools
::sync_mountpoint
($dir); };
1830 sub sync_container_namespace
{
1832 my $pid = find_lxc_pid
($vmid);
1834 # SOCK_DGRAM is nicer for barriers but cannot be slurped
1835 socketpair my $pfd, my $cfd, AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
1836 or die "failed to create socketpair: $!\n";
1839 die "fork failed: $!\n" if !defined($child);
1844 &$do_syncfs($vmid, $pid, $cfd);
1854 die "failed to enter container namespace\n" if $go ne "go\n";
1856 open my $mounts, '<', "/proc/$child/mounts"
1857 or die "failed to open container's /proc/mounts: $!\n";
1858 my $mountdata = do { local $/ = undef; <$mounts> };
1860 print {$pfd} $mountdata;
1863 while (waitpid($child, 0) != $child) {}
1864 die "failed to sync container namespace\n" if $? != 0;
1867 sub check_freeze_needed
{
1868 my ($vmid, $config, $save_vmstate) = @_;
1870 my $ret = check_running
($vmid);
1871 return ($ret, $ret);
1874 sub snapshot_create
{
1875 my ($vmid, $snapname, $save_vmstate, $comment) = @_;
1877 my $snap = snapshot_prepare
($vmid, $snapname, $save_vmstate, $comment);
1879 $save_vmstate = 0 if !$snap->{vmstate
};
1881 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1883 my ($running, $freezefs) = check_freeze_needed
($vmid, $conf, $snap->{vmstate
});
1889 PVE
::Tools
::run_command
(['/usr/bin/lxc-freeze', '-n', $vmid]);
1890 sync_container_namespace
($vmid);
1893 my $storecfg = PVE
::Storage
::config
();
1894 foreach_mountpoint
($conf, sub {
1895 my ($ms, $mountpoint) = @_;
1897 return if $snapname eq 'vzdump' && $ms ne 'rootfs' && !$mountpoint->{backup
};
1898 PVE
::Storage
::volume_snapshot
($storecfg, $mountpoint->{volume
}, $snapname);
1899 $drivehash->{$ms} = 1;
1906 eval { PVE
::Tools
::run_command
(['/usr/bin/lxc-unfreeze', '-n', $vmid]); };
1912 warn "snapshot create failed: starting cleanup\n";
1913 eval { snapshot_delete
($vmid, $snapname, 1, $drivehash); };
1918 snapshot_commit
($vmid, $snapname);
1921 # Note: $drivehash is only set when called from snapshot_create.
1922 sub snapshot_delete
{
1923 my ($vmid, $snapname, $force, $drivehash) = @_;
1930 my $unlink_parent = sub {
1931 my ($confref, $new_parent) = @_;
1933 if ($confref->{parent
} && $confref->{parent
} eq $snapname) {
1935 $confref->{parent
} = $new_parent;
1937 delete $confref->{parent
};
1942 my $updatefn = sub {
1943 my ($remove_drive) = @_;
1945 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
1948 PVE
::LXC
::Config-
>check_lock($conf);
1949 die "you can't delete a snapshot if vm is a template\n"
1950 if PVE
::LXC
::Config-
>is_template($conf);
1953 $snap = $conf->{snapshots
}->{$snapname};
1955 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1957 # remove parent refs
1959 &$unlink_parent($conf, $snap->{parent
});
1960 foreach my $sn (keys %{$conf->{snapshots
}}) {
1961 next if $sn eq $snapname;
1962 &$unlink_parent($conf->{snapshots
}->{$sn}, $snap->{parent
});
1966 if ($remove_drive) {
1967 if ($remove_drive eq 'vmstate') {
1968 die "implement me - saving vmstate\n";
1970 my $value = $snap->{$remove_drive};
1971 my $mountpoint = $remove_drive eq 'rootfs' ? parse_ct_rootfs
($value, 1) : parse_ct_mountpoint
($value, 1);
1972 delete $snap->{$remove_drive};
1973 add_unused_volume
($snap, $mountpoint->{volume
});
1978 $snap->{snapstate
} = 'delete';
1980 delete $conf->{snapshots
}->{$snapname};
1981 delete $conf->{lock} if $drivehash;
1982 foreach my $volid (@$unused) {
1983 add_unused_volume
($conf, $volid);
1987 PVE
::LXC
::Config-
>write_config($vmid, $conf);
1990 PVE
::LXC
::Config-
>lock_config($vmid, $updatefn);
1992 # now remove vmstate file
1993 # never set for LXC!
1994 my $storecfg = PVE
::Storage
::config
();
1996 if ($snap->{vmstate
}) {
1997 die "implement me - saving vmstate\n";
2000 # now remove all volume snapshots
2001 foreach_mountpoint
($snap, sub {
2002 my ($ms, $mountpoint) = @_;
2004 return if $snapname eq 'vzdump' && $ms ne 'rootfs' && !$mountpoint->{backup
};
2005 if (!$drivehash || $drivehash->{$ms}) {
2006 eval { PVE
::Storage
::volume_snapshot_delete
($storecfg, $mountpoint->{volume
}, $snapname); };
2008 die $err if !$force;
2013 # save changes (remove mp from snapshot)
2014 PVE
::LXC
::Config-
>lock_config($vmid, $updatefn, $ms) if !$force;
2015 push @$unused, $mountpoint->{volume
};
2018 # now cleanup config
2020 PVE
::LXC
::Config-
>lock_config($vmid, $updatefn);
2023 sub snapshot_rollback
{
2024 my ($vmid, $snapname) = @_;
2028 my $storecfg = PVE
::Storage
::config
();
2030 my $conf = PVE
::LXC
::Config-
>load_config($vmid);
2032 my $get_snapshot_config = sub {
2034 die "you can't rollback if vm is a template\n" if PVE
::LXC
::Config-
>is_template($conf);
2036 my $res = $conf->{snapshots
}->{$snapname};
2038 die "snapshot '$snapname' does not exist\n" if !defined($res);
2043 my $snap = &$get_snapshot_config();
2045 foreach_mountpoint
($snap, sub {
2046 my ($ms, $mountpoint) = @_;
2048 PVE
::Storage
::volume_rollback_is_possible
($storecfg, $mountpoint->{volume
}, $snapname);
2051 my $updatefn = sub {
2053 $conf = PVE
::LXC
::Config-
>load_config($vmid);
2055 $snap = &$get_snapshot_config();
2057 die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
2058 if $snap->{snapstate
};
2061 PVE
::LXC
::Config-
>check_lock($conf);
2062 PVE
::Tools
::run_command
(['/usr/bin/lxc-stop', '-n', $vmid, '--kill'])
2063 if check_running
($vmid);
2066 die "unable to rollback vm $vmid: vm is running\n"
2067 if check_running
($vmid);
2070 $conf->{lock} = 'rollback';
2072 die "got wrong lock\n" if !($conf->{lock} && $conf->{lock} eq 'rollback');
2073 delete $conf->{lock};
2079 # copy snapshot config to current config
2080 $conf = &$snapshot_apply_config($conf, $snap);
2081 $conf->{parent
} = $snapname;
2084 PVE
::LXC
::Config-
>write_config($vmid, $conf);
2086 if (!$prepare && $snap->{vmstate
}) {
2087 die "implement me - save vmstate\n";
2091 PVE
::LXC
::Config-
>lock_config($vmid, $updatefn);
2093 foreach_mountpoint
($snap, sub {
2094 my ($ms, $mountpoint) = @_;
2096 PVE
::Storage
::volume_snapshot_rollback
($storecfg, $mountpoint->{volume
}, $snapname);
2100 PVE
::LXC
::Config-
>lock_config($vmid, $updatefn);
2103 sub template_create
{
2104 my ($vmid, $conf) = @_;
2106 my $storecfg = PVE
::Storage
::config
();
2108 my $rootinfo = parse_ct_rootfs
($conf->{rootfs
});
2109 my $volid = $rootinfo->{volume
};
2111 die "Template feature is not available for '$volid'\n"
2112 if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
2114 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
2116 my $template_volid = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
2117 $rootinfo->{volume
} = $template_volid;
2118 $conf->{rootfs
} = print_ct_mountpoint
($rootinfo, 1);
2120 PVE
::LXC
::Config-
>write_config($vmid, $conf);
2123 sub mountpoint_names
{
2126 my @names = ('rootfs');
2128 for (my $i = 0; $i < $MAX_MOUNT_POINTS; $i++) {
2129 push @names, "mp$i";
2132 return $reverse ?
reverse @names : @names;
2136 sub foreach_mountpoint_full
{
2137 my ($conf, $reverse, $func) = @_;
2139 foreach my $key (mountpoint_names
($reverse)) {
2140 my $value = $conf->{$key};
2141 next if !defined($value);
2142 my $mountpoint = $key eq 'rootfs' ? parse_ct_rootfs
($value, 1) : parse_ct_mountpoint
($value, 1);
2143 next if !defined($mountpoint);
2145 &$func($key, $mountpoint);
2149 sub foreach_mountpoint
{
2150 my ($conf, $func) = @_;
2152 foreach_mountpoint_full
($conf, 0, $func);
2155 sub foreach_mountpoint_reverse
{
2156 my ($conf, $func) = @_;
2158 foreach_mountpoint_full
($conf, 1, $func);
2161 sub check_ct_modify_config_perm
{
2162 my ($rpcenv, $authuser, $vmid, $pool, $newconf, $delete) = @_;
2164 return 1 if $authuser eq 'root@pam';
2167 my ($opt, $delete) = @_;
2168 if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
2169 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
2170 } elsif ($opt eq 'rootfs' || $opt =~ /^mp\d+$/) {
2171 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
2173 my $data = $opt eq 'rootfs' ? parse_ct_rootfs
($newconf->{$opt})
2174 : parse_ct_mountpoint
($newconf->{$opt});
2175 raise_perm_exc
("mountpoint type $data->{type}") if $data->{type
} ne 'volume';
2176 } elsif ($opt eq 'memory' || $opt eq 'swap') {
2177 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
2178 } elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
2179 $opt eq 'searchdomain' || $opt eq 'hostname') {
2180 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
2182 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
2186 foreach my $opt (keys %$newconf) {
2189 foreach my $opt (@$delete) {
2197 my ($vmid, $storage_cfg, $conf, $noerr) = @_;
2199 my $rootdir = "/var/lib/lxc/$vmid/rootfs";
2200 my $volid_list = get_vm_volumes
($conf);
2202 foreach_mountpoint_reverse
($conf, sub {
2203 my ($ms, $mountpoint) = @_;
2205 my $volid = $mountpoint->{volume
};
2206 my $mount = $mountpoint->{mp
};
2208 return if !$volid || !$mount;
2210 my $mount_path = "$rootdir/$mount";
2211 $mount_path =~ s!/+!/!g;
2213 return if !PVE
::ProcFSTools
::is_mounted
($mount_path);
2216 PVE
::Tools
::run_command
(['umount', '-d', $mount_path]);
2229 my ($vmid, $storage_cfg, $conf) = @_;
2231 my $rootdir = "/var/lib/lxc/$vmid/rootfs";
2232 File
::Path
::make_path
($rootdir);
2234 my $volid_list = get_vm_volumes
($conf);
2235 PVE
::Storage
::activate_volumes
($storage_cfg, $volid_list);
2238 foreach_mountpoint
($conf, sub {
2239 my ($ms, $mountpoint) = @_;
2241 mountpoint_mount
($mountpoint, $rootdir, $storage_cfg);
2245 warn "mounting container failed\n";
2246 umount_all
($vmid, $storage_cfg, $conf, 1);
2254 sub mountpoint_mount_path
{
2255 my ($mountpoint, $storage_cfg, $snapname) = @_;
2257 return mountpoint_mount
($mountpoint, undef, $storage_cfg, $snapname);
2260 my $check_mount_path = sub {
2262 $path = File
::Spec-
>canonpath($path);
2263 my $real = Cwd
::realpath
($path);
2264 if ($real ne $path) {
2265 die "mount path modified by symlink: $path != $real";
2274 if ($line =~ m
@^(/dev/loop\d
+):@) {
2278 my $cmd = ['losetup', '--associated', $path];
2279 PVE
::Tools
::run_command
($cmd, outfunc
=> $parser);
2283 # Run a function with a file attached to a loop device.
2284 # The loop device is always detached afterwards (or set to autoclear).
2285 # Returns the loop device.
2286 sub run_with_loopdev
{
2287 my ($func, $file) = @_;
2288 my $device = query_loopdev
($file);
2289 # Try to reuse an existing device
2291 # We assume that whoever setup the loop device is responsible for
2299 if ($line =~ m
@^(/dev/loop\d
+)$@) {
2303 PVE
::Tools
::run_command
(['losetup', '--show', '-f', $file], outfunc
=> $parser);
2304 die "failed to setup loop device for $file\n" if !$device;
2305 eval { &$func($device); };
2307 PVE
::Tools
::run_command
(['losetup', '-d', $device]);
2313 my ($dir, $dest, $ro, @extra_opts) = @_;
2314 PVE
::Tools
::run_command
(['mount', '-o', 'bind', @extra_opts, $dir, $dest]);
2316 eval { PVE
::Tools
::run_command
(['mount', '-o', 'bind,remount,ro', $dest]); };
2318 warn "bindmount error\n";
2319 # don't leave writable bind-mounts behind...
2320 PVE
::Tools
::run_command
(['umount', $dest]);
2326 # use $rootdir = undef to just return the corresponding mount path
2327 sub mountpoint_mount
{
2328 my ($mountpoint, $rootdir, $storage_cfg, $snapname) = @_;
2330 my $volid = $mountpoint->{volume
};
2331 my $mount = $mountpoint->{mp
};
2332 my $type = $mountpoint->{type
};
2333 my $quota = !$snapname && !$mountpoint->{ro
} && $mountpoint->{quota
};
2336 return if !$volid || !$mount;
2340 if (defined($rootdir)) {
2341 $rootdir =~ s!/+$!!;
2342 $mount_path = "$rootdir/$mount";
2343 $mount_path =~ s!/+!/!g;
2344 &$check_mount_path($mount_path);
2345 File
::Path
::mkpath
($mount_path);
2348 my ($storage, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2350 die "unknown snapshot path for '$volid'" if !$storage && defined($snapname);
2353 if (defined($mountpoint->{acl
})) {
2354 $optstring .= ($mountpoint->{acl
} ?
'acl' : 'noacl');
2356 my $readonly = $mountpoint->{ro
};
2358 my @extra_opts = ('-o', $optstring);
2362 my $scfg = PVE
::Storage
::storage_config
($storage_cfg, $storage);
2363 my $path = PVE
::Storage
::path
($storage_cfg, $volid, $snapname);
2365 my ($vtype, undef, undef, undef, undef, $isBase, $format) =
2366 PVE
::Storage
::parse_volname
($storage_cfg, $volid);
2368 $format = 'iso' if $vtype eq 'iso'; # allow to handle iso files
2370 if ($format eq 'subvol') {
2373 if ($scfg->{type
} eq 'zfspool') {
2374 my $path_arg = $path;
2375 $path_arg =~ s!^/+!!;
2376 PVE
::Tools
::run_command
(['mount', '-o', 'ro', @extra_opts, '-t', 'zfs', $path_arg, $mount_path]);
2378 die "cannot mount subvol snapshots for storage type '$scfg->{type}'\n";
2381 bindmount
($path, $mount_path, $readonly, @extra_opts);
2382 warn "cannot enable quota control for bind mounted subvolumes\n" if $quota;
2385 return wantarray ?
($path, 0, $mounted_dev) : $path;
2386 } elsif ($format eq 'raw' || $format eq 'iso') {
2390 if ($format eq 'iso') {
2391 PVE
::Tools
::run_command
(['mount', '-o', 'ro', @extra_opts, $path, $mount_path]);
2392 } elsif ($isBase || defined($snapname)) {
2393 PVE
::Tools
::run_command
(['mount', '-o', 'ro,noload', @extra_opts, $path, $mount_path]);
2396 push @extra_opts, '-o', 'usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv0';
2398 push @extra_opts, '-o', 'ro' if $readonly;
2399 PVE
::Tools
::run_command
(['mount', @extra_opts, $path, $mount_path]);
2403 my $use_loopdev = 0;
2404 if ($scfg->{path
}) {
2405 $mounted_dev = run_with_loopdev
($domount, $path);
2407 } elsif ($scfg->{type
} eq 'drbd' || $scfg->{type
} eq 'lvm' ||
2408 $scfg->{type
} eq 'rbd' || $scfg->{type
} eq 'lvmthin') {
2409 $mounted_dev = $path;
2412 die "unsupported storage type '$scfg->{type}'\n";
2414 return wantarray ?
($path, $use_loopdev, $mounted_dev) : $path;
2416 die "unsupported image format '$format'\n";
2418 } elsif ($type eq 'device') {
2419 push @extra_opts, '-o', 'ro' if $readonly;
2420 PVE
::Tools
::run_command
(['mount', @extra_opts, $volid, $mount_path]) if $mount_path;
2421 return wantarray ?
($volid, 0, $volid) : $volid;
2422 } elsif ($type eq 'bind') {
2423 die "directory '$volid' does not exist\n" if ! -d
$volid;
2424 &$check_mount_path($volid);
2425 bindmount
($volid, $mount_path, $readonly, @extra_opts) if $mount_path;
2426 warn "cannot enable quota control for bind mounts\n" if $quota;
2427 return wantarray ?
($volid, 0, undef) : $volid;
2430 die "unsupported storage";
2433 sub get_vm_volumes
{
2434 my ($conf, $excludes) = @_;
2438 foreach_mountpoint
($conf, sub {
2439 my ($ms, $mountpoint) = @_;
2441 return if $excludes && $ms eq $excludes;
2443 my $volid = $mountpoint->{volume
};
2445 return if !$volid || $mountpoint->{type
} ne 'volume';
2447 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2450 push @$vollist, $volid;
2457 my ($dev, $rootuid, $rootgid) = @_;
2459 PVE
::Tools
::run_command
(['mkfs.ext4', '-O', 'mmp',
2460 '-E', "root_owner=$rootuid:$rootgid",
2465 my ($storage_cfg, $volid, $rootuid, $rootgid) = @_;
2467 if ($volid =~ m!^/dev/.+!) {
2472 my ($storage, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2474 die "cannot format volume '$volid' with no storage\n" if !$storage;
2476 PVE
::Storage
::activate_volumes
($storage_cfg, [$volid]);
2478 my $path = PVE
::Storage
::path
($storage_cfg, $volid);
2480 my ($vtype, undef, undef, undef, undef, $isBase, $format) =
2481 PVE
::Storage
::parse_volname
($storage_cfg, $volid);
2483 die "cannot format volume '$volid' (format == $format)\n"
2484 if $format ne 'raw';
2486 mkfs
($path, $rootuid, $rootgid);
2490 my ($storecfg, $vollist) = @_;
2492 foreach my $volid (@$vollist) {
2493 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
2499 my ($storecfg, $vmid, $settings, $conf) = @_;
2504 my (undef, $rootuid, $rootgid) = PVE
::LXC
::parse_id_maps
($conf);
2505 my $chown_vollist = [];
2507 foreach_mountpoint
($settings, sub {
2508 my ($ms, $mountpoint) = @_;
2510 my $volid = $mountpoint->{volume
};
2511 my $mp = $mountpoint->{mp
};
2513 my ($storage, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2515 if ($storage && ($volid =~ m/^([^:\s]+):(\d+(\.\d+)?)$/)) {
2516 my ($storeid, $size_gb) = ($1, $2);
2518 my $size_kb = int(${size_gb
}*1024) * 1024;
2520 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storage);
2521 # fixme: use better naming ct-$vmid-disk-X.raw?
2523 if ($scfg->{type
} eq 'dir' || $scfg->{type
} eq 'nfs') {
2525 $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storage, $vmid, 'raw',
2527 format_disk
($storecfg, $volid, $rootuid, $rootgid);
2529 $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storage, $vmid, 'subvol',
2531 push @$chown_vollist, $volid;
2533 } elsif ($scfg->{type
} eq 'zfspool') {
2535 $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storage, $vmid, 'subvol',
2537 push @$chown_vollist, $volid;
2538 } elsif ($scfg->{type
} eq 'drbd' || $scfg->{type
} eq 'lvm' || $scfg->{type
} eq 'lvmthin') {
2540 $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storage, $vmid, 'raw', undef, $size_kb);
2541 format_disk
($storecfg, $volid, $rootuid, $rootgid);
2543 } elsif ($scfg->{type
} eq 'rbd') {
2545 die "krbd option must be enabled on storage type '$scfg->{type}'\n" if !$scfg->{krbd
};
2546 $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storage, $vmid, 'raw', undef, $size_kb);
2547 format_disk
($storecfg, $volid, $rootuid, $rootgid);
2549 die "unable to create containers on storage type '$scfg->{type}'\n";
2551 push @$vollist, $volid;
2552 $mountpoint->{volume
} = $volid;
2553 $mountpoint->{size
} = $size_kb * 1024;
2554 $conf->{$ms} = print_ct_mountpoint
($mountpoint, $ms eq 'rootfs');
2556 # use specified/existing volid/dir/device
2557 $conf->{$ms} = print_ct_mountpoint
($mountpoint, $ms eq 'rootfs');
2561 PVE
::Storage
::activate_volumes
($storecfg, $chown_vollist, undef);
2562 foreach my $volid (@$chown_vollist) {
2563 my $path = PVE
::Storage
::path
($storecfg, $volid, undef);
2564 chown($rootuid, $rootgid, $path);
2566 PVE
::Storage
::deactivate_volumes
($storecfg, $chown_vollist, undef);
2568 # free allocated images on error
2570 destroy_disks
($storecfg, $vollist);
2576 # bash completion helper
2578 sub complete_os_templates
{
2579 my ($cmdname, $pname, $cvalue) = @_;
2581 my $cfg = PVE
::Storage
::config
();
2585 if ($cvalue =~ m/^([^:]+):/) {
2589 my $vtype = $cmdname eq 'restore' ?
'backup' : 'vztmpl';
2590 my $data = PVE
::Storage
::template_list
($cfg, $storeid, $vtype);
2593 foreach my $id (keys %$data) {
2594 foreach my $item (@{$data->{$id}}) {
2595 push @$res, $item->{volid
} if defined($item->{volid
});
2602 my $complete_ctid_full = sub {
2605 my $idlist = vmstatus
();
2607 my $active_hash = list_active_containers
();
2611 foreach my $id (keys %$idlist) {
2612 my $d = $idlist->{$id};
2613 if (defined($running)) {
2614 next if $d->{template
};
2615 next if $running && !$active_hash->{$id};
2616 next if !$running && $active_hash->{$id};
2625 return &$complete_ctid_full();
2628 sub complete_ctid_stopped
{
2629 return &$complete_ctid_full(0);
2632 sub complete_ctid_running
{
2633 return &$complete_ctid_full(1);
2643 my $lxc = $conf->{lxc
};
2644 foreach my $entry (@$lxc) {
2645 my ($key, $value) = @$entry;
2646 next if $key ne 'lxc.id_map';
2647 if ($value =~ /^([ug])\s+(\d+)\s+(\d+)\s+(\d+)\s*$/) {
2648 my ($type, $ct, $host, $length) = ($1, $2, $3, $4);
2649 push @$id_map, [$type, $ct, $host, $length];
2651 $rootuid = $host if $type eq 'u';
2652 $rootgid = $host if $type eq 'g';
2655 die "failed to parse id_map: $value\n";
2659 if (!@$id_map && $conf->{unprivileged
}) {
2660 # Should we read them from /etc/subuid?
2661 $id_map = [ ['u', '0', '100000', '65536'],
2662 ['g', '0', '100000', '65536'] ];
2663 $rootuid = $rootgid = 100000;
2666 return ($id_map, $rootuid, $rootgid);
2669 sub userns_command
{
2672 return ['lxc-usernsexec', (map { ('-m', join(':', @$_)) } @$id_map), '--'];