]> git.proxmox.com Git - pve-container.git/blame - src/PVE/LXC.pm
added quota flag to mountpoints
[pve-container.git] / src / PVE / LXC.pm
CommitLineData
f76a2828
DM
1package PVE::LXC;
2
3use strict;
4use warnings;
d14a9a1b 5use POSIX qw(EINTR);
f76a2828
DM
6
7use File::Path;
2cfae16e
WB
8use File::Spec;
9use Cwd qw();
f76a2828
DM
10use Fcntl ':flock';
11
12use PVE::Cluster qw(cfs_register_file cfs_read_file);
c65e0a6d 13use PVE::Storage;
f76a2828
DM
14use PVE::SafeSyslog;
15use PVE::INotify;
a3249355 16use PVE::JSONSchema qw(get_standard_option);
6aca6740 17use PVE::Tools qw($IPV6RE $IPV4RE dir_glob_foreach);
68fba17b 18use PVE::Network;
52389a07 19use PVE::AccessControl;
228a5a1d 20use PVE::ProcFSTools;
688afc63 21use Time::HiRes qw (gettimeofday);
f76a2828
DM
22
23use Data::Dumper;
24
27916659
DM
25my $nodename = PVE::INotify::nodename();
26
688afc63
WL
27my $cpuinfo= PVE::ProcFSTools::read_cpuinfo();
28
f9897acd 29our $COMMON_TAR_FLAGS = [ '--sparse', '--numeric-owner', '--acls',
fc4e132e
WB
30 '--xattrs',
31 '--xattrs-include=user.*',
4132377b
WB
32 '--xattrs-include=security.capability',
33 '--warning=no-xattr-write' ];
fc4e132e 34
27916659 35cfs_register_file('/lxc/', \&parse_pct_config, \&write_pct_config);
f76a2828 36
769fbfab
WB
37my $rootfs_desc = {
38 volume => {
39 type => 'string',
40 default_key => 1,
da990295 41 format => 'pve-lxc-mp-string',
769fbfab
WB
42 format_description => 'volume',
43 description => 'Volume, device or directory to mount into the container.',
44 },
45 backup => {
46 type => 'boolean',
47 format_description => '[1|0]',
48 description => 'Whether to include the mountpoint in backups.',
49 optional => 1,
50 },
51 size => {
72d583ff
WB
52 type => 'string',
53 format => 'disk-size',
769fbfab 54 format_description => 'DiskSize',
769fbfab
WB
55 description => 'Volume size (read only value).',
56 optional => 1,
57 },
471dd315
WB
58 acl => {
59 type => 'boolean',
60 format_description => 'acl',
61 description => 'Explicitly enable or disable ACL support.',
62 optional => 1,
63 },
64 ro => {
65 type => 'boolean',
66 format_description => 'ro',
67 description => 'Read-only mountpoint (not supported with bind mounts)',
68 optional => 1,
69 },
50df544c
WB
70 quota => {
71 type => 'boolean',
72 format_description => '[0|1]',
73 description => 'Enable user quotas inside the container (not supported with zfs subvolumes)',
74 optional => 1,
75 },
769fbfab 76};
822de0c3 77
27916659 78PVE::JSONSchema::register_standard_option('pve-ct-rootfs', {
769fbfab 79 type => 'string', format => $rootfs_desc,
8fbd2935 80 description => "Use volume as container root.",
27916659
DM
81 optional => 1,
82});
83
52389a07
DM
84PVE::JSONSchema::register_standard_option('pve-lxc-snapshot-name', {
85 description => "The name of the snapshot.",
86 type => 'string', format => 'pve-configid',
87 maxLength => 40,
88});
89
27916659 90my $confdesc = {
09d3ec42
DM
91 lock => {
92 optional => 1,
93 type => 'string',
94 description => "Lock/unlock the VM.",
95 enum => [qw(migrate backup snapshot rollback)],
96 },
27916659
DM
97 onboot => {
98 optional => 1,
99 type => 'boolean',
100 description => "Specifies whether a VM will be started during system bootup.",
101 default => 0,
117636e5 102 },
27916659 103 startup => get_standard_option('pve-startup-order'),
bb1ac2de
DM
104 template => {
105 optional => 1,
106 type => 'boolean',
107 description => "Enable/disable Template.",
108 default => 0,
109 },
27916659
DM
110 arch => {
111 optional => 1,
112 type => 'string',
113 enum => ['amd64', 'i386'],
114 description => "OS architecture type.",
115 default => 'amd64',
117636e5 116 },
27916659
DM
117 ostype => {
118 optional => 1,
119 type => 'string',
fa7cb12b 120 enum => ['debian', 'ubuntu', 'centos', 'fedora', 'opensuse', 'archlinux'],
27916659 121 description => "OS type. Corresponds to lxc setup scripts in /usr/share/lxc/config/<ostype>.common.conf.",
a3249355 122 },
4f958489
DM
123 console => {
124 optional => 1,
125 type => 'boolean',
126 description => "Attach a console device (/dev/console) to the container.",
127 default => 1,
128 },
27916659
DM
129 tty => {
130 optional => 1,
131 type => 'integer',
132 description => "Specify the number of tty available to the container",
133 minimum => 0,
134 maximum => 6,
0d0ca400 135 default => 2,
611fe3aa 136 },
27916659
DM
137 cpulimit => {
138 optional => 1,
139 type => 'number',
c31ad455 140 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.",
27916659
DM
141 minimum => 0,
142 maximum => 128,
143 default => 0,
144 },
145 cpuunits => {
146 optional => 1,
147 type => 'integer',
c31ad455 148 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.",
27916659
DM
149 minimum => 0,
150 maximum => 500000,
81bee809 151 default => 1024,
27916659
DM
152 },
153 memory => {
154 optional => 1,
155 type => 'integer',
156 description => "Amount of RAM for the VM in MB.",
157 minimum => 16,
158 default => 512,
159 },
160 swap => {
161 optional => 1,
162 type => 'integer',
163 description => "Amount of SWAP for the VM in MB.",
164 minimum => 0,
165 default => 512,
166 },
167 hostname => {
168 optional => 1,
169 description => "Set a host name for the container.",
159aad3e 170 type => 'string', format => 'dns-name',
27916659
DM
171 maxLength => 255,
172 },
173 description => {
174 optional => 1,
175 type => 'string',
176 description => "Container description. Only used on the configuration web interface.",
177 },
178 searchdomain => {
179 optional => 1,
159aad3e 180 type => 'string', format => 'dns-name-list',
c31ad455 181 description => "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain nor nameserver.",
27916659
DM
182 },
183 nameserver => {
184 optional => 1,
159aad3e 185 type => 'string', format => 'address-list',
c31ad455 186 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.",
27916659
DM
187 },
188 rootfs => get_standard_option('pve-ct-rootfs'),
09d3ec42
DM
189 parent => {
190 optional => 1,
191 type => 'string', format => 'pve-configid',
192 maxLength => 40,
193 description => "Parent snapshot name. This is used internally, and should not be modified.",
194 },
195 snaptime => {
196 optional => 1,
197 description => "Timestamp for snapshots.",
198 type => 'integer',
199 minimum => 0,
200 },
aca816ad
DM
201 cmode => {
202 optional => 1,
203 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).",
204 type => 'string',
205 enum => ['shell', 'console', 'tty'],
206 default => 'tty',
207 },
7e806596
AG
208 protection => {
209 optional => 1,
210 type => 'boolean',
c31ad455 211 description => "Sets the protection flag of the container. This will prevent the CT or CT's disk remove/update operation.",
7e806596
AG
212 default => 0,
213 },
425b62cb
WB
214 unprivileged => {
215 optional => 1,
216 type => 'boolean',
217 description => "Makes the container run as unprivileged user. (Should not be modified manually.)",
218 default => 0,
219 },
f76a2828
DM
220};
221
e576f689
DM
222my $valid_lxc_conf_keys = {
223 'lxc.include' => 1,
224 'lxc.arch' => 1,
225 'lxc.utsname' => 1,
226 'lxc.haltsignal' => 1,
227 'lxc.rebootsignal' => 1,
228 'lxc.stopsignal' => 1,
229 'lxc.init_cmd' => 1,
230 'lxc.network.type' => 1,
231 'lxc.network.flags' => 1,
232 'lxc.network.link' => 1,
233 'lxc.network.mtu' => 1,
234 'lxc.network.name' => 1,
235 'lxc.network.hwaddr' => 1,
236 'lxc.network.ipv4' => 1,
237 'lxc.network.ipv4.gateway' => 1,
238 'lxc.network.ipv6' => 1,
239 'lxc.network.ipv6.gateway' => 1,
240 'lxc.network.script.up' => 1,
241 'lxc.network.script.down' => 1,
242 'lxc.pts' => 1,
243 'lxc.console.logfile' => 1,
244 'lxc.console' => 1,
245 'lxc.tty' => 1,
246 'lxc.devttydir' => 1,
247 'lxc.hook.autodev' => 1,
248 'lxc.autodev' => 1,
249 'lxc.kmsg' => 1,
250 'lxc.mount' => 1,
251 'lxc.mount.entry' => 1,
252 'lxc.mount.auto' => 1,
312e9850 253 'lxc.rootfs' => 'lxc.rootfs is auto generated from rootfs',
e576f689 254 'lxc.rootfs.mount' => 1,
312e9850
WB
255 'lxc.rootfs.options' => 'lxc.rootfs.options is not supported' .
256 ', please use mountpoint options in the "rootfs" key',
e576f689
DM
257 # lxc.cgroup.*
258 'lxc.cap.drop' => 1,
259 'lxc.cap.keep' => 1,
260 'lxc.aa_profile' => 1,
261 'lxc.aa_allow_incomplete' => 1,
262 'lxc.se_context' => 1,
263 'lxc.seccomp' => 1,
264 'lxc.id_map' => 1,
265 'lxc.hook.pre-start' => 1,
266 'lxc.hook.pre-mount' => 1,
267 'lxc.hook.mount' => 1,
268 'lxc.hook.start' => 1,
53775872 269 'lxc.hook.stop' => 1,
e576f689
DM
270 'lxc.hook.post-stop' => 1,
271 'lxc.hook.clone' => 1,
272 'lxc.hook.destroy' => 1,
273 'lxc.loglevel' => 1,
274 'lxc.logfile' => 1,
275 'lxc.start.auto' => 1,
276 'lxc.start.delay' => 1,
277 'lxc.start.order' => 1,
278 'lxc.group' => 1,
279 'lxc.environment' => 1,
e576f689
DM
280};
281
769fbfab
WB
282my $netconf_desc = {
283 type => {
284 type => 'string',
285 optional => 1,
286 description => "Network interface type.",
287 enum => [qw(veth)],
288 },
289 name => {
290 type => 'string',
291 format_description => 'String',
292 description => 'Name of the network device as seen from inside the container. (lxc.network.name)',
293 pattern => '[-_.\w\d]+',
294 },
295 bridge => {
296 type => 'string',
297 format_description => 'vmbr<Number>',
298 description => 'Bridge to attach the network device to.',
299 pattern => '[-_.\w\d]+',
a7c080a7 300 optional => 1,
769fbfab
WB
301 },
302 hwaddr => {
303 type => 'string',
304 format_description => 'MAC',
305 description => 'Bridge to attach the network device to. (lxc.network.hwaddr)',
306 pattern => qr/(?:[a-f0-9]{2}:){5}[a-f0-9]{2}/i,
307 optional => 1,
308 },
309 mtu => {
310 type => 'integer',
311 format_description => 'Number',
312 description => 'Maximum transfer unit of the interface. (lxc.network.mtu)',
07521af1 313 minimum => 64, # minimum ethernet frame is 64 bytes
769fbfab
WB
314 optional => 1,
315 },
316 ip => {
317 type => 'string',
318 format => 'pve-ipv4-config',
319 format_description => 'IPv4Format/CIDR',
320 description => 'IPv4 address in CIDR format.',
321 optional => 1,
322 },
323 gw => {
324 type => 'string',
325 format => 'ipv4',
326 format_description => 'GatewayIPv4',
327 description => 'Default gateway for IPv4 traffic.',
328 optional => 1,
329 },
330 ip6 => {
331 type => 'string',
332 format => 'pve-ipv6-config',
333 format_description => 'IPv6Format/CIDR',
334 description => 'IPv6 address in CIDR format.',
335 optional => 1,
336 },
337 gw6 => {
338 type => 'string',
339 format => 'ipv6',
340 format_description => 'GatewayIPv6',
341 description => 'Default gateway for IPv6 traffic.',
342 optional => 1,
343 },
344 firewall => {
345 type => 'boolean',
346 format_description => '[1|0]',
347 description => "Controls whether this interface's firewall rules should be used.",
348 optional => 1,
349 },
350 tag => {
351 type => 'integer',
352 format_description => 'VlanNo',
353 minimum => '2',
354 maximum => '4094',
23eb2244
WB
355 description => "VLAN tag for this interface.",
356 optional => 1,
357 },
358 trunks => {
359 type => 'string',
360 pattern => qr/\d+(?:;\d+)*/,
361 format_description => 'vlanid[;vlanid...]',
362 description => "VLAN ids to pass through the interface",
769fbfab
WB
363 optional => 1,
364 },
365};
366PVE::JSONSchema::register_format('pve-lxc-network', $netconf_desc);
367
27916659
DM
368my $MAX_LXC_NETWORKS = 10;
369for (my $i = 0; $i < $MAX_LXC_NETWORKS; $i++) {
370 $confdesc->{"net$i"} = {
371 optional => 1,
1a0a239c 372 type => 'string', format => $netconf_desc,
769fbfab 373 description => "Specifies network interfaces for the container.",
27916659 374 };
90bc31f7
DM
375}
376
da990295
DC
377PVE::JSONSchema::register_format('pve-lxc-mp-string', \&verify_lxc_mp_string);
378sub verify_lxc_mp_string{
379 my ($mp, $noerr) = @_;
380
381 # do not allow:
382 # /./ or /../
383 # /. or /.. at the end
384 # ../ at the beginning
385
386 if($mp =~ m@/\.\.?/@ ||
387 $mp =~ m@/\.\.?$@ ||
388 $mp =~ m@^\.\./@){
389 return undef if $noerr;
390 die "$mp contains illegal character sequences\n";
391 }
392 return $mp;
393}
394
769fbfab
WB
395my $mp_desc = {
396 %$rootfs_desc,
397 mp => {
398 type => 'string',
da990295 399 format => 'pve-lxc-mp-string',
769fbfab
WB
400 format_description => 'Path',
401 description => 'Path to the mountpoint as seen from inside the container.',
769fbfab
WB
402 },
403};
404PVE::JSONSchema::register_format('pve-ct-mountpoint', $mp_desc);
405
69202f71
WB
406my $unuseddesc = {
407 optional => 1,
408 type => 'string', format => 'pve-volume-id',
409 description => "Reference to unused volumes.",
410};
411
02c9d10c
AD
412my $MAX_MOUNT_POINTS = 10;
413for (my $i = 0; $i < $MAX_MOUNT_POINTS; $i++) {
414 $confdesc->{"mp$i"} = {
415 optional => 1,
769fbfab 416 type => 'string', format => $mp_desc,
566d5f81 417 description => "Use volume as container mount point (experimental feature).",
02c9d10c
AD
418 optional => 1,
419 };
420}
421
69202f71
WB
422my $MAX_UNUSED_DISKS = $MAX_MOUNT_POINTS;
423for (my $i = 0; $i < $MAX_MOUNT_POINTS; $i++) {
424 $confdesc->{"unused$i"} = $unuseddesc;
425}
426
27916659
DM
427sub write_pct_config {
428 my ($filename, $conf) = @_;
f76a2828 429
27916659 430 delete $conf->{snapstate}; # just to be sure
f76a2828 431
27916659
DM
432 my $generate_raw_config = sub {
433 my ($conf) = @_;
f76a2828 434
27916659 435 my $raw = '';
cbb03fea 436
27916659
DM
437 # add description as comment to top of file
438 my $descr = $conf->{description} || '';
439 foreach my $cl (split(/\n/, $descr)) {
440 $raw .= '#' . PVE::Tools::encode_text($cl) . "\n";
a12a36e0 441 }
fff3a342 442
27916659 443 foreach my $key (sort keys %$conf) {
09d3ec42 444 next if $key eq 'digest' || $key eq 'description' || $key eq 'pending' ||
e576f689 445 $key eq 'snapshots' || $key eq 'snapname' || $key eq 'lxc';
545d10ba
DM
446 my $value = $conf->{$key};
447 die "detected invalid newline inside property '$key'\n" if $value =~ m/\n/;
448 $raw .= "$key: $value\n";
a12a36e0 449 }
e576f689
DM
450
451 if (my $lxcconf = $conf->{lxc}) {
452 foreach my $entry (@$lxcconf) {
453 my ($k, $v) = @$entry;
454 $raw .= "$k: $v\n";
455 }
456 }
457
27916659 458 return $raw;
a12a36e0 459 };
160f0941 460
27916659 461 my $raw = &$generate_raw_config($conf);
a12a36e0 462
27916659
DM
463 foreach my $snapname (sort keys %{$conf->{snapshots}}) {
464 $raw .= "\n[$snapname]\n";
465 $raw .= &$generate_raw_config($conf->{snapshots}->{$snapname});
f76a2828
DM
466 }
467
f76a2828
DM
468 return $raw;
469}
470
27916659
DM
471sub check_type {
472 my ($key, $value) = @_;
822de0c3 473
27916659 474 die "unknown setting '$key'\n" if !$confdesc->{$key};
822de0c3 475
27916659
DM
476 my $type = $confdesc->{$key}->{type};
477
478 if (!defined($value)) {
479 die "got undefined value\n";
480 }
481
482 if ($value =~ m/[\n\r]/) {
483 die "property contains a line feed\n";
484 }
822de0c3 485
27916659
DM
486 if ($type eq 'boolean') {
487 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
488 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
489 die "type check ('boolean') failed - got '$value'\n";
490 } elsif ($type eq 'integer') {
491 return int($1) if $value =~ m/^(\d+)$/;
492 die "type check ('integer') failed - got '$value'\n";
493 } elsif ($type eq 'number') {
494 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
495 die "type check ('number') failed - got '$value'\n";
496 } elsif ($type eq 'string') {
497 if (my $fmt = $confdesc->{$key}->{format}) {
498 PVE::JSONSchema::check_format($fmt, $value);
499 return $value;
500 }
cbb03fea 501 return $value;
822de0c3 502 } else {
27916659 503 die "internal error"
822de0c3 504 }
822de0c3
DM
505}
506
27916659 507sub parse_pct_config {
f76a2828
DM
508 my ($filename, $raw) = @_;
509
510 return undef if !defined($raw);
511
27916659 512 my $res = {
f76a2828 513 digest => Digest::SHA::sha1_hex($raw),
27916659 514 snapshots => {},
f76a2828
DM
515 };
516
27916659 517 $filename =~ m|/lxc/(\d+).conf$|
f76a2828
DM
518 || die "got strange filename '$filename'";
519
520 my $vmid = $1;
521
27916659
DM
522 my $conf = $res;
523 my $descr = '';
524 my $section = '';
525
526 my @lines = split(/\n/, $raw);
527 foreach my $line (@lines) {
528 next if $line =~ m/^\s*$/;
529
530 if ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
531 $section = $1;
532 $conf->{description} = $descr if $descr;
533 $descr = '';
534 $conf = $res->{snapshots}->{$section} = {};
535 next;
a12a36e0 536 }
a12a36e0 537
27916659
DM
538 if ($line =~ m/^\#(.*)\s*$/) {
539 $descr .= PVE::Tools::decode_text($1) . "\n";
540 next;
f76a2828 541 }
5d186e16 542
545d10ba 543 if ($line =~ m/^(lxc\.[a-z0-9_\-\.]+)(:|\s*=)\s*(.*?)\s*$/) {
e576f689
DM
544 my $key = $1;
545 my $value = $3;
a23d627d
WB
546 my $validity = $valid_lxc_conf_keys->{$key} || 0;
547 if ($validity eq 1 || $key =~ m/^lxc\.cgroup\./) {
e576f689 548 push @{$conf->{lxc}}, [$key, $value];
a23d627d 549 } elsif (my $errmsg = $validity) {
312e9850 550 warn "vm $vmid - $key: $errmsg\n";
e576f689
DM
551 } else {
552 warn "vm $vmid - unable to parse config: $line\n";
553 }
554 } elsif ($line =~ m/^(description):\s*(.*\S)\s*$/) {
27916659
DM
555 $descr .= PVE::Tools::decode_text($2);
556 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
557 $conf->{snapstate} = $1;
fe9a4ab3 558 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(\S.*)\s*$/) {
27916659 559 my $key = $1;
5d186e16 560 my $value = $2;
27916659
DM
561 eval { $value = check_type($key, $value); };
562 warn "vm $vmid - unable to parse value of '$key' - $@" if $@;
563 $conf->{$key} = $value;
5d186e16 564 } else {
27916659 565 warn "vm $vmid - unable to parse config: $line\n";
5d186e16 566 }
7dfc49cc
DM
567 }
568
27916659 569 $conf->{description} = $descr if $descr;
5d186e16 570
27916659
DM
571 delete $res->{snapstate}; # just to be sure
572
573 return $res;
f76a2828
DM
574}
575
576sub config_list {
577 my $vmlist = PVE::Cluster::get_vmlist();
578 my $res = {};
579 return $res if !$vmlist || !$vmlist->{ids};
580 my $ids = $vmlist->{ids};
581
582 foreach my $vmid (keys %$ids) {
583 next if !$vmid; # skip CT0
584 my $d = $ids->{$vmid};
585 next if !$d->{node} || $d->{node} ne $nodename;
586 next if !$d->{type} || $d->{type} ne 'lxc';
587 $res->{$vmid}->{type} = 'lxc';
588 }
589 return $res;
590}
591
592sub cfs_config_path {
593 my ($vmid, $node) = @_;
594
595 $node = $nodename if !$node;
27916659 596 return "nodes/$node/lxc/$vmid.conf";
f76a2828
DM
597}
598
9c2d4ce9
DM
599sub config_file {
600 my ($vmid, $node) = @_;
601
602 my $cfspath = cfs_config_path($vmid, $node);
603 return "/etc/pve/$cfspath";
604}
605
f76a2828 606sub load_config {
d18499cf 607 my ($vmid, $node) = @_;
f76a2828 608
d18499cf
TL
609 $node = $nodename if !$node;
610 my $cfspath = cfs_config_path($vmid, $node);
f76a2828
DM
611
612 my $conf = PVE::Cluster::cfs_read_file($cfspath);
c31ad455 613 die "container $vmid does not exist\n" if !defined($conf);
f76a2828
DM
614
615 return $conf;
616}
617
5b4657d0
DM
618sub create_config {
619 my ($vmid, $conf) = @_;
620
621 my $dir = "/etc/pve/nodes/$nodename/lxc";
622 mkdir $dir;
623
5b4657d0
DM
624 write_config($vmid, $conf);
625}
626
627sub destroy_config {
628 my ($vmid) = @_;
629
27916659 630 unlink config_file($vmid, $nodename);
5b4657d0
DM
631}
632
f76a2828
DM
633sub write_config {
634 my ($vmid, $conf) = @_;
635
636 my $cfspath = cfs_config_path($vmid);
637
638 PVE::Cluster::cfs_write_file($cfspath, $conf);
639}
640
d14a9a1b 641# flock: we use one file handle per process, so lock file
c31ad455 642# can be called multiple times and will succeed for the same process.
d14a9a1b
DM
643
644my $lock_handles = {};
645my $lockdir = "/run/lock/lxc";
646
647sub lock_filename {
648 my ($vmid) = @_;
cbb03fea 649
53396388 650 return "$lockdir/pve-config-${vmid}.lock";
d14a9a1b
DM
651}
652
2d3f23be
FG
653sub lock_container {
654 my ($vmid, $timeout, $code, @param) = @_;
d14a9a1b
DM
655
656 $timeout = 10 if !$timeout;
d14a9a1b
DM
657
658 my $filename = lock_filename($vmid);
659
f99e8278
AD
660 mkdir $lockdir if !-d $lockdir;
661
2d3f23be 662 my $res = PVE::Tools::lock_file_full($filename, $timeout, 0, $code, @param);
f76a2828 663
2d3f23be 664 die $@ if $@;
f76a2828
DM
665
666 return $res;
667}
668
ec52ac21
DM
669sub option_exists {
670 my ($name) = @_;
671
672 return defined($confdesc->{$name});
673}
f76a2828
DM
674
675# add JSON properties for create and set function
676sub json_config_properties {
677 my $prop = shift;
678
679 foreach my $opt (keys %$confdesc) {
09d3ec42 680 next if $opt eq 'parent' || $opt eq 'snaptime';
27916659
DM
681 next if $prop->{$opt};
682 $prop->{$opt} = $confdesc->{$opt};
683 }
684
685 return $prop;
686}
687
688sub json_config_properties_no_rootfs {
689 my $prop = shift;
690
691 foreach my $opt (keys %$confdesc) {
692 next if $prop->{$opt};
09d3ec42 693 next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'rootfs';
f76a2828
DM
694 $prop->{$opt} = $confdesc->{$opt};
695 }
696
697 return $prop;
698}
699
822de0c3
DM
700# container status helpers
701
702sub list_active_containers {
cbb03fea 703
822de0c3
DM
704 my $filename = "/proc/net/unix";
705
706 # similar test is used by lcxcontainers.c: list_active_containers
707 my $res = {};
cbb03fea 708
822de0c3
DM
709 my $fh = IO::File->new ($filename, "r");
710 return $res if !$fh;
711
712 while (defined(my $line = <$fh>)) {
713 if ($line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/) {
714 my $path = $1;
27916659 715 if ($path =~ m!^@/var/lib/lxc/(\d+)/command$!) {
822de0c3
DM
716 $res->{$1} = 1;
717 }
718 }
719 }
720
721 close($fh);
cbb03fea 722
822de0c3
DM
723 return $res;
724}
f76a2828 725
5c752bbf
DM
726# warning: this is slow
727sub check_running {
728 my ($vmid) = @_;
729
730 my $active_hash = list_active_containers();
731
732 return 1 if defined($active_hash->{$vmid});
cbb03fea 733
5c752bbf
DM
734 return undef;
735}
736
10fc3ba5 737sub get_container_disk_usage {
73e03cb7 738 my ($vmid, $pid) = @_;
10fc3ba5 739
73e03cb7 740 return PVE::Tools::df("/proc/$pid/root/", 1);
10fc3ba5
DM
741}
742
688afc63
WL
743my $last_proc_vmid_stat;
744
745my $parse_cpuacct_stat = sub {
746 my ($vmid) = @_;
747
748 my $raw = read_cgroup_value('cpuacct', $vmid, 'cpuacct.stat', 1);
749
750 my $stat = {};
751
752 if ($raw =~ m/^user (\d+)\nsystem (\d+)\n/) {
753
754 $stat->{utime} = $1;
755 $stat->{stime} = $2;
756
757 }
758
759 return $stat;
760};
761
f76a2828
DM
762sub vmstatus {
763 my ($opt_vmid) = @_;
764
765 my $list = $opt_vmid ? { $opt_vmid => { type => 'lxc' }} : config_list();
766
822de0c3 767 my $active_hash = list_active_containers();
cbb03fea 768
688afc63
WL
769 my $cpucount = $cpuinfo->{cpus} || 1;
770
771 my $cdtime = gettimeofday;
772
773 my $uptime = (PVE::ProcFSTools::read_proc_uptime(1))[0];
774
f76a2828 775 foreach my $vmid (keys %$list) {
f76a2828 776 my $d = $list->{$vmid};
10fc3ba5 777
d5588ee3
DM
778 eval { $d->{pid} = find_lxc_pid($vmid) if defined($active_hash->{$vmid}); };
779 warn $@ if $@; # ignore errors (consider them stopped)
cbb03fea 780
d5588ee3 781 $d->{status} = $d->{pid} ? 'running' : 'stopped';
f76a2828
DM
782
783 my $cfspath = cfs_config_path($vmid);
238a56cb 784 my $conf = PVE::Cluster::cfs_read_file($cfspath) || {};
cbb03fea 785
27916659 786 $d->{name} = $conf->{'hostname'} || "CT$vmid";
238a56cb 787 $d->{name} =~ s/[\s]//g;
cbb03fea 788
9db5687d 789 $d->{cpus} = $conf->{cpulimit} || $cpucount;
44da0641 790
d5588ee3
DM
791 if ($d->{pid}) {
792 my $res = get_container_disk_usage($vmid, $d->{pid});
27916659
DM
793 $d->{disk} = $res->{used};
794 $d->{maxdisk} = $res->{total};
795 } else {
796 $d->{disk} = 0;
797 # use 4GB by default ??
798 if (my $rootfs = $conf->{rootfs}) {
44a9face 799 my $rootinfo = parse_ct_rootfs($rootfs);
27916659
DM
800 $d->{maxdisk} = int(($rootinfo->{size} || 4)*1024*1024)*1024;
801 } else {
802 $d->{maxdisk} = 4*1024*1024*1024;
10fc3ba5 803 }
238a56cb 804 }
cbb03fea 805
238a56cb
DM
806 $d->{mem} = 0;
807 $d->{swap} = 0;
95df9a12
DM
808 $d->{maxmem} = ($conf->{memory}||512)*1024*1024;
809 $d->{maxswap} = ($conf->{swap}//0)*1024*1024;
e901d418 810
238a56cb
DM
811 $d->{uptime} = 0;
812 $d->{cpu} = 0;
e901d418 813
238a56cb
DM
814 $d->{netout} = 0;
815 $d->{netin} = 0;
f76a2828 816
238a56cb
DM
817 $d->{diskread} = 0;
818 $d->{diskwrite} = 0;
bb1ac2de
DM
819
820 $d->{template} = is_template($conf);
f76a2828 821 }
cbb03fea 822
238a56cb
DM
823 foreach my $vmid (keys %$list) {
824 my $d = $list->{$vmid};
d5588ee3
DM
825 my $pid = $d->{pid};
826
827 next if !$pid; # skip stopped CTs
f76a2828 828
88a8696b
TL
829 my $ctime = (stat("/proc/$pid"))[10]; # 10 = ctime
830 $d->{uptime} = time - $ctime; # the method lxcfs uses
22a77285 831
238a56cb
DM
832 $d->{mem} = read_cgroup_value('memory', $vmid, 'memory.usage_in_bytes');
833 $d->{swap} = read_cgroup_value('memory', $vmid, 'memory.memsw.usage_in_bytes') - $d->{mem};
b5289322
AD
834
835 my $blkio_bytes = read_cgroup_value('blkio', $vmid, 'blkio.throttle.io_service_bytes', 1);
1e647c7c 836 my @bytes = split(/\n/, $blkio_bytes);
b5289322 837 foreach my $byte (@bytes) {
1e647c7c
DM
838 if (my ($key, $value) = $byte =~ /(Read|Write)\s+(\d+)/) {
839 $d->{diskread} = $2 if $key eq 'Read';
840 $d->{diskwrite} = $2 if $key eq 'Write';
841 }
b5289322 842 }
688afc63
WL
843
844 my $pstat = &$parse_cpuacct_stat($vmid);
845
846 my $used = $pstat->{utime} + $pstat->{stime};
847
848 my $old = $last_proc_vmid_stat->{$vmid};
849 if (!$old) {
850 $last_proc_vmid_stat->{$vmid} = {
851 time => $cdtime,
852 used => $used,
853 cpu => 0,
854 };
855 next;
856 }
857
858 my $dtime = ($cdtime - $old->{time}) * $cpucount * $cpuinfo->{user_hz};
859
860 if ($dtime > 1000) {
861 my $dutime = $used - $old->{used};
862
863 $d->{cpu} = (($dutime/$dtime)* $cpucount) / $d->{cpus};
864 $last_proc_vmid_stat->{$vmid} = {
865 time => $cdtime,
866 used => $used,
867 cpu => $d->{cpu},
868 };
869 } else {
870 $d->{cpu} = $old->{cpu};
871 }
238a56cb 872 }
cbb03fea 873
68b8f4d1
WL
874 my $netdev = PVE::ProcFSTools::read_proc_net_dev();
875
876 foreach my $dev (keys %$netdev) {
877 next if $dev !~ m/^veth([1-9]\d*)i/;
878 my $vmid = $1;
879 my $d = $list->{$vmid};
880
881 next if !$d;
882
883 $d->{netout} += $netdev->{$dev}->{receive};
884 $d->{netin} += $netdev->{$dev}->{transmit};
885
886 }
887
f76a2828
DM
888 return $list;
889}
890
7c921c80
WB
891sub classify_mountpoint {
892 my ($vol) = @_;
893 if ($vol =~ m!^/!) {
894 return 'device' if $vol =~ m!^/dev/!;
895 return 'bind';
896 }
897 return 'volume';
898}
899
44a9face
DM
900my $parse_ct_mountpoint_full = sub {
901 my ($desc, $data, $noerr) = @_;
27916659
DM
902
903 $data //= '';
904
1b2c1e8c 905 my $res;
44a9face 906 eval { $res = PVE::JSONSchema::parse_property_string($desc, $data) };
1b2c1e8c 907 if ($@) {
ca7feb1a
WB
908 return undef if $noerr;
909 die $@;
27916659
DM
910 }
911
bf4a209a 912 if (defined(my $size = $res->{size})) {
ca7feb1a
WB
913 $size = PVE::JSONSchema::parse_size($size);
914 if (!defined($size)) {
915 return undef if $noerr;
916 die "invalid size: $size\n";
917 }
918 $res->{size} = $size;
27916659
DM
919 }
920
7c921c80
WB
921 $res->{type} = classify_mountpoint($res->{volume});
922
27916659 923 return $res;
44a9face
DM
924};
925
926sub parse_ct_rootfs {
927 my ($data, $noerr) = @_;
928
929 my $res = &$parse_ct_mountpoint_full($rootfs_desc, $data, $noerr);
930
931 $res->{mp} = '/' if defined($res);
932
933 return $res;
934}
935
936sub parse_ct_mountpoint {
937 my ($data, $noerr) = @_;
938
939 return &$parse_ct_mountpoint_full($mp_desc, $data, $noerr);
27916659 940}
7dfc49cc 941
dde7b02b 942sub print_ct_mountpoint {
4fee75fd 943 my ($info, $nomp) = @_;
7c921c80
WB
944 my $skip = [ 'type' ];
945 push @$skip, 'mp' if $nomp;
6708ba93 946 return PVE::JSONSchema::print_property_string($info, $mp_desc, $skip);
bb1ac2de
DM
947}
948
7dfc49cc 949sub print_lxc_network {
f76a2828 950 my $net = shift;
6708ba93 951 return PVE::JSONSchema::print_property_string($net, $netconf_desc);
f76a2828
DM
952}
953
7dfc49cc
DM
954sub parse_lxc_network {
955 my ($data) = @_;
956
957 my $res = {};
958
959 return $res if !$data;
960
ca7feb1a 961 $res = PVE::JSONSchema::parse_property_string($netconf_desc, $data);
7dfc49cc
DM
962
963 $res->{type} = 'veth';
93cdbbfb 964 $res->{hwaddr} = PVE::Tools::random_ether_addr() if !$res->{hwaddr};
cbb03fea 965
7dfc49cc
DM
966 return $res;
967}
f76a2828 968
238a56cb
DM
969sub read_cgroup_value {
970 my ($group, $vmid, $name, $full) = @_;
971
972 my $path = "/sys/fs/cgroup/$group/lxc/$vmid/$name";
973
974 return PVE::Tools::file_get_contents($path) if $full;
975
976 return PVE::Tools::file_read_firstline($path);
977}
978
bf0b8c43
AD
979sub write_cgroup_value {
980 my ($group, $vmid, $name, $value) = @_;
981
982 my $path = "/sys/fs/cgroup/$group/lxc/$vmid/$name";
983 PVE::ProcFSTools::write_proc_entry($path, $value) if -e $path;
984
985}
986
52f1d76b
DM
987sub find_lxc_console_pids {
988
989 my $res = {};
990
991 PVE::Tools::dir_glob_foreach('/proc', '\d+', sub {
992 my ($pid) = @_;
993
994 my $cmdline = PVE::Tools::file_read_firstline("/proc/$pid/cmdline");
995 return if !$cmdline;
996
997 my @args = split(/\0/, $cmdline);
998
c31ad455 999 # search for lxc-console -n <vmid>
cbb03fea 1000 return if scalar(@args) != 3;
52f1d76b
DM
1001 return if $args[1] ne '-n';
1002 return if $args[2] !~ m/^\d+$/;
1003 return if $args[0] !~ m|^(/usr/bin/)?lxc-console$|;
cbb03fea 1004
52f1d76b 1005 my $vmid = $args[2];
cbb03fea 1006
52f1d76b
DM
1007 push @{$res->{$vmid}}, $pid;
1008 });
1009
1010 return $res;
1011}
1012
bedeaaf1
AD
1013sub find_lxc_pid {
1014 my ($vmid) = @_;
1015
1016 my $pid = undef;
1017 my $parser = sub {
1018 my $line = shift;
8b25977f 1019 $pid = $1 if $line =~ m/^PID:\s+(\d+)$/;
bedeaaf1 1020 };
c39aa40a 1021 PVE::Tools::run_command(['lxc-info', '-n', $vmid, '-p'], outfunc => $parser);
bedeaaf1 1022
8b25977f 1023 die "unable to get PID for CT $vmid (not running?)\n" if !$pid;
cbb03fea 1024
8b25977f 1025 return $pid;
bedeaaf1
AD
1026}
1027
cbb03fea 1028# Note: we cannot use Net:IP, because that only allows strict
55fa4e09
DM
1029# CIDR networks
1030sub parse_ipv4_cidr {
1031 my ($cidr, $noerr) = @_;
1032
f7a7b413
WB
1033 if ($cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ($2 > 7) && ($2 <= 32)) {
1034 return { address => $1, netmask => $PVE::Network::ipv4_reverse_mask->[$2] };
55fa4e09 1035 }
cbb03fea 1036
55fa4e09 1037 return undef if $noerr;
cbb03fea 1038
55fa4e09
DM
1039 die "unable to parse ipv4 address/mask\n";
1040}
93285df8 1041
a12a36e0
WL
1042sub check_lock {
1043 my ($conf) = @_;
1044
27916659 1045 die "VM is locked ($conf->{'lock'})\n" if $conf->{'lock'};
a12a36e0
WL
1046}
1047
e22af68f
AG
1048sub check_protection {
1049 my ($vm_conf, $err_msg) = @_;
1050
1051 if ($vm_conf->{protection}) {
1052 die "$err_msg - protection mode enabled\n";
1053 }
1054}
1055
27916659 1056sub update_lxc_config {
c628ffa1 1057 my ($storage_cfg, $vmid, $conf) = @_;
b80dd50a 1058
bb1ac2de
DM
1059 my $dir = "/var/lib/lxc/$vmid";
1060
1061 if ($conf->{template}) {
1062
1063 unlink "$dir/config";
1064
1065 return;
1066 }
1067
27916659 1068 my $raw = '';
b80dd50a 1069
27916659
DM
1070 die "missing 'arch' - internal error" if !$conf->{arch};
1071 $raw .= "lxc.arch = $conf->{arch}\n";
b80dd50a 1072
425b62cb
WB
1073 my $unprivileged = $conf->{unprivileged};
1074 my $custom_idmap = grep { $_->[0] eq 'lxc.id_map' } @{$conf->{lxc}};
1075
27916659 1076 my $ostype = $conf->{ostype} || die "missing 'ostype' - internal error";
fa7cb12b 1077 if ($ostype =~ /^(?:debian | ubuntu | centos | fedora | opensuse | archlinux)$/x) {
27916659 1078 $raw .= "lxc.include = /usr/share/lxc/config/$ostype.common.conf\n";
425b62cb
WB
1079 if ($unprivileged || $custom_idmap) {
1080 $raw .= "lxc.include = /usr/share/lxc/config/$ostype.userns.conf\n"
1081 }
27916659 1082 } else {
9a7a910b 1083 die "implement me (ostype $ostype)";
27916659 1084 }
b80dd50a 1085
50df544c
WB
1086 # WARNING: DO NOT REMOVE this without making sure that loop device nodes
1087 # cannot be exposed to the container with r/w access (cgroup perms).
1088 # When this is enabled mounts will still remain in the monitor's namespace
1089 # after the container unmounted them and thus will not detach from their
1090 # files while the container is running!
c16b8890 1091 $raw .= "lxc.monitor.unshare = 1\n";
58cc92a9 1092
425b62cb
WB
1093 # Should we read them from /etc/subuid?
1094 if ($unprivileged && !$custom_idmap) {
1095 $raw .= "lxc.id_map = u 0 100000 65536\n";
1096 $raw .= "lxc.id_map = g 0 100000 65536\n";
1097 }
1098
6f035afe 1099 if (!has_dev_console($conf)) {
eeaea429
DM
1100 $raw .= "lxc.console = none\n";
1101 $raw .= "lxc.cgroup.devices.deny = c 5:1 rwm\n";
1102 }
4f958489 1103
0d0ca400 1104 my $ttycount = get_tty_count($conf);
27916659 1105 $raw .= "lxc.tty = $ttycount\n";
cbb03fea 1106
c31ad455 1107 # some init scripts expect a linux terminal (turnkey).
a691a5a3
DM
1108 $raw .= "lxc.environment = TERM=linux\n";
1109
27916659
DM
1110 my $utsname = $conf->{hostname} || "CT$vmid";
1111 $raw .= "lxc.utsname = $utsname\n";
cbb03fea 1112
27916659
DM
1113 my $memory = $conf->{memory} || 512;
1114 my $swap = $conf->{swap} // 0;
1115
1116 my $lxcmem = int($memory*1024*1024);
1117 $raw .= "lxc.cgroup.memory.limit_in_bytes = $lxcmem\n";
a12a36e0 1118
27916659
DM
1119 my $lxcswap = int(($memory + $swap)*1024*1024);
1120 $raw .= "lxc.cgroup.memory.memsw.limit_in_bytes = $lxcswap\n";
1121
1122 if (my $cpulimit = $conf->{cpulimit}) {
1123 $raw .= "lxc.cgroup.cpu.cfs_period_us = 100000\n";
1124 my $value = int(100000*$cpulimit);
1125 $raw .= "lxc.cgroup.cpu.cfs_quota_us = $value\n";
a12a36e0
WL
1126 }
1127
27916659
DM
1128 my $shares = $conf->{cpuunits} || 1024;
1129 $raw .= "lxc.cgroup.cpu.shares = $shares\n";
1130
44a9face 1131 my $mountpoint = parse_ct_rootfs($conf->{rootfs});
a3076d81 1132
c9a5774b 1133 $raw .= "lxc.rootfs = $dir/rootfs\n";
27916659
DM
1134
1135 my $netcount = 0;
1136 foreach my $k (keys %$conf) {
1137 next if $k !~ m/^net(\d+)$/;
1138 my $ind = $1;
a16d94c8 1139 my $d = parse_lxc_network($conf->{$k});
27916659
DM
1140 $netcount++;
1141 $raw .= "lxc.network.type = veth\n";
18862537 1142 $raw .= "lxc.network.veth.pair = veth${vmid}i${ind}\n";
27916659
DM
1143 $raw .= "lxc.network.hwaddr = $d->{hwaddr}\n" if defined($d->{hwaddr});
1144 $raw .= "lxc.network.name = $d->{name}\n" if defined($d->{name});
1145 $raw .= "lxc.network.mtu = $d->{mtu}\n" if defined($d->{mtu});
a12a36e0
WL
1146 }
1147
e576f689
DM
1148 if (my $lxcconf = $conf->{lxc}) {
1149 foreach my $entry (@$lxcconf) {
1150 my ($k, $v) = @$entry;
1151 $netcount++ if $k eq 'lxc.network.type';
1152 $raw .= "$k = $v\n";
1153 }
1154 }
27916659 1155
e576f689
DM
1156 $raw .= "lxc.network.type = empty\n" if !$netcount;
1157
27916659
DM
1158 File::Path::mkpath("$dir/rootfs");
1159
1160 PVE::Tools::file_set_contents("$dir/config", $raw);
b80dd50a
DM
1161}
1162
117636e5
DM
1163# verify and cleanup nameserver list (replace \0 with ' ')
1164sub verify_nameserver_list {
1165 my ($nameserver_list) = @_;
1166
1167 my @list = ();
1168 foreach my $server (PVE::Tools::split_list($nameserver_list)) {
1169 PVE::JSONSchema::pve_verify_ip($server);
1170 push @list, $server;
1171 }
1172
1173 return join(' ', @list);
1174}
1175
1176sub verify_searchdomain_list {
1177 my ($searchdomain_list) = @_;
1178
1179 my @list = ();
1180 foreach my $server (PVE::Tools::split_list($searchdomain_list)) {
1181 # todo: should we add checks for valid dns domains?
1182 push @list, $server;
1183 }
1184
1185 return join(' ', @list);
1186}
1187
69202f71
WB
1188sub add_unused_volume {
1189 my ($config, $volid) = @_;
1190
1191 my $key;
1192 for (my $ind = $MAX_UNUSED_DISKS - 1; $ind >= 0; $ind--) {
1193 my $test = "unused$ind";
1194 if (my $vid = $config->{$test}) {
1195 return if $vid eq $volid; # do not add duplicates
1196 } else {
1197 $key = $test;
1198 }
1199 }
1200
c31ad455 1201 die "Too many unused volumes - please delete them first.\n" if !$key;
69202f71
WB
1202
1203 $config->{$key} = $volid;
1204
1205 return $key;
1206}
1207
27916659 1208sub update_pct_config {
93285df8
DM
1209 my ($vmid, $conf, $running, $param, $delete) = @_;
1210
bf0b8c43
AD
1211 my @nohotplug;
1212
7b49dfe0 1213 my $new_disks = 0;
69202f71 1214 my @deleted_volumes;
4fee75fd 1215
cbb03fea
DM
1216 my $rootdir;
1217 if ($running) {
bedeaaf1 1218 my $pid = find_lxc_pid($vmid);
cbb03fea 1219 $rootdir = "/proc/$pid/root";
bedeaaf1
AD
1220 }
1221
7a168607
DM
1222 my $hotplug_error = sub {
1223 if ($running) {
a6a77cfa
WB
1224 push @nohotplug, @_;
1225 return 1;
7a168607
DM
1226 } else {
1227 return 0;
a6a77cfa 1228 }
7a168607 1229 };
a6a77cfa 1230
93285df8
DM
1231 if (defined($delete)) {
1232 foreach my $opt (@$delete) {
a61a5448
WB
1233 if (!exists($conf->{$opt})) {
1234 warn "no such option: $opt\n";
1235 next;
1236 }
1237
27916659 1238 if ($opt eq 'hostname' || $opt eq 'memory' || $opt eq 'rootfs') {
93285df8
DM
1239 die "unable to delete required option '$opt'\n";
1240 } elsif ($opt eq 'swap') {
27916659 1241 delete $conf->{$opt};
bf0b8c43 1242 write_cgroup_value("memory", $vmid, "memory.memsw.limit_in_bytes", -1);
40603eb3 1243 } elsif ($opt eq 'description' || $opt eq 'onboot' || $opt eq 'startup') {
27916659 1244 delete $conf->{$opt};
4f958489 1245 } elsif ($opt eq 'nameserver' || $opt eq 'searchdomain' ||
40603eb3 1246 $opt eq 'tty' || $opt eq 'console' || $opt eq 'cmode') {
a6a77cfa 1247 next if $hotplug_error->($opt);
27916659 1248 delete $conf->{$opt};
68fba17b 1249 } elsif ($opt =~ m/^net(\d)$/) {
93285df8 1250 delete $conf->{$opt};
68fba17b
AD
1251 next if !$running;
1252 my $netid = $1;
18862537 1253 PVE::Network::veth_delete("veth${vmid}i$netid");
7e806596
AG
1254 } elsif ($opt eq 'protection') {
1255 delete $conf->{$opt};
69202f71 1256 } elsif ($opt =~ m/^unused(\d+)$/) {
a6a77cfa 1257 next if $hotplug_error->($opt);
69202f71
WB
1258 check_protection($conf, "can't remove CT $vmid drive '$opt'");
1259 push @deleted_volumes, $conf->{$opt};
1260 delete $conf->{$opt};
4fee75fd 1261 } elsif ($opt =~ m/^mp(\d+)$/) {
a6a77cfa 1262 next if $hotplug_error->($opt);
e22af68f 1263 check_protection($conf, "can't remove CT $vmid drive '$opt'");
69202f71 1264 my $mountpoint = parse_ct_mountpoint($conf->{$opt});
7c921c80
WB
1265 if ($mountpoint->{type} eq 'volume') {
1266 add_unused_volume($conf, $mountpoint->{volume})
1267 }
4fee75fd 1268 delete $conf->{$opt};
425b62cb
WB
1269 } elsif ($opt eq 'unprivileged') {
1270 die "unable to delete read-only option: '$opt'\n";
93285df8 1271 } else {
9a7a910b 1272 die "implement me (delete: $opt)"
93285df8 1273 }
706c9791 1274 write_config($vmid, $conf) if $running;
93285df8
DM
1275 }
1276 }
1277
be6383d7
WB
1278 # There's no separate swap size to configure, there's memory and "total"
1279 # memory (iow. memory+swap). This means we have to change them together.
27916659
DM
1280 my $wanted_memory = PVE::Tools::extract_param($param, 'memory');
1281 my $wanted_swap = PVE::Tools::extract_param($param, 'swap');
be6383d7 1282 if (defined($wanted_memory) || defined($wanted_swap)) {
27916659 1283
a2c57b0c
WB
1284 my $old_memory = ($conf->{memory} || 512);
1285 my $old_swap = ($conf->{swap} || 0);
1286
1287 $wanted_memory //= $old_memory;
1288 $wanted_swap //= $old_swap;
27916659
DM
1289
1290 my $total = $wanted_memory + $wanted_swap;
1291 if ($running) {
a2c57b0c
WB
1292 my $old_total = $old_memory + $old_swap;
1293 if ($total > $old_total) {
1294 write_cgroup_value("memory", $vmid, "memory.memsw.limit_in_bytes", int($total*1024*1024));
1295 write_cgroup_value("memory", $vmid, "memory.limit_in_bytes", int($wanted_memory*1024*1024));
1296 } else {
1297 write_cgroup_value("memory", $vmid, "memory.limit_in_bytes", int($wanted_memory*1024*1024));
1298 write_cgroup_value("memory", $vmid, "memory.memsw.limit_in_bytes", int($total*1024*1024));
1299 }
be6383d7 1300 }
27916659
DM
1301 $conf->{memory} = $wanted_memory;
1302 $conf->{swap} = $wanted_swap;
1303
706c9791 1304 write_config($vmid, $conf) if $running;
be6383d7
WB
1305 }
1306
93285df8
DM
1307 foreach my $opt (keys %$param) {
1308 my $value = $param->{$opt};
1309 if ($opt eq 'hostname') {
27916659 1310 $conf->{$opt} = $value;
a99b3509 1311 } elsif ($opt eq 'onboot') {
27916659 1312 $conf->{$opt} = $value ? 1 : 0;
a3249355 1313 } elsif ($opt eq 'startup') {
27916659 1314 $conf->{$opt} = $value;
40603eb3 1315 } elsif ($opt eq 'tty' || $opt eq 'console' || $opt eq 'cmode') {
a6a77cfa 1316 next if $hotplug_error->($opt);
e576f689 1317 $conf->{$opt} = $value;
ffa1d001 1318 } elsif ($opt eq 'nameserver') {
a6a77cfa 1319 next if $hotplug_error->($opt);
117636e5 1320 my $list = verify_nameserver_list($value);
27916659 1321 $conf->{$opt} = $list;
ffa1d001 1322 } elsif ($opt eq 'searchdomain') {
a6a77cfa 1323 next if $hotplug_error->($opt);
117636e5 1324 my $list = verify_searchdomain_list($value);
27916659 1325 $conf->{$opt} = $list;
45573f7c 1326 } elsif ($opt eq 'cpulimit') {
a6a77cfa 1327 next if $hotplug_error->($opt); # FIXME: hotplug
27916659 1328 $conf->{$opt} = $value;
b80dd50a 1329 } elsif ($opt eq 'cpuunits') {
27916659 1330 $conf->{$opt} = $value;
bf0b8c43 1331 write_cgroup_value("cpu", $vmid, "cpu.shares", $value);
93285df8 1332 } elsif ($opt eq 'description') {
27916659 1333 $conf->{$opt} = PVE::Tools::encode_text($value);
93285df8
DM
1334 } elsif ($opt =~ m/^net(\d+)$/) {
1335 my $netid = $1;
a16d94c8 1336 my $net = parse_lxc_network($value);
27916659
DM
1337 if (!$running) {
1338 $conf->{$opt} = print_lxc_network($net);
cbb03fea 1339 } else {
bedeaaf1
AD
1340 update_net($vmid, $conf, $opt, $net, $netid, $rootdir);
1341 }
7e806596
AG
1342 } elsif ($opt eq 'protection') {
1343 $conf->{$opt} = $value ? 1 : 0;
4fee75fd 1344 } elsif ($opt =~ m/^mp(\d+)$/) {
a6a77cfa 1345 next if $hotplug_error->($opt);
e22af68f 1346 check_protection($conf, "can't update CT $vmid drive '$opt'");
4fee75fd 1347 $conf->{$opt} = $value;
7b49dfe0 1348 $new_disks = 1;
4fee75fd 1349 } elsif ($opt eq 'rootfs') {
e22af68f 1350 check_protection($conf, "can't update CT $vmid drive '$opt'");
b51a98d4 1351 die "implement me: $opt";
425b62cb
WB
1352 } elsif ($opt eq 'unprivileged') {
1353 die "unable to modify read-only option: '$opt'\n";
93285df8 1354 } else {
a92f66c9 1355 die "implement me: $opt";
93285df8 1356 }
706c9791 1357 write_config($vmid, $conf) if $running;
93285df8 1358 }
bf0b8c43 1359
69202f71
WB
1360 if (@deleted_volumes) {
1361 my $storage_cfg = PVE::Storage::config();
1362 foreach my $volume (@deleted_volumes) {
1363 delete_mountpoint_volume($storage_cfg, $vmid, $volume);
1364 }
1365 }
1366
7b49dfe0 1367 if ($new_disks) {
4fee75fd 1368 my $storage_cfg = PVE::Storage::config();
6c871c36 1369 create_disks($storage_cfg, $vmid, $conf, $conf);
4fee75fd 1370 }
694c25df
WB
1371
1372 # This should be the last thing we do here
1373 if ($running && scalar(@nohotplug)) {
1374 die "unable to modify " . join(',', @nohotplug) . " while container is running\n";
1375 }
93285df8 1376}
c325b32f 1377
6f035afe
DM
1378sub has_dev_console {
1379 my ($conf) = @_;
1380
1381 return !(defined($conf->{console}) && !$conf->{console});
1382}
1383
0d0ca400
DM
1384sub get_tty_count {
1385 my ($conf) = @_;
1386
1387 return $conf->{tty} // $confdesc->{tty}->{default};
1388}
1389
aca816ad
DM
1390sub get_cmode {
1391 my ($conf) = @_;
1392
1393 return $conf->{cmode} // $confdesc->{cmode}->{default};
1394}
1395
1396sub get_console_command {
1397 my ($vmid, $conf) = @_;
1398
1399 my $cmode = get_cmode($conf);
1400
1401 if ($cmode eq 'console') {
1402 return ['lxc-console', '-n', $vmid, '-t', 0];
1403 } elsif ($cmode eq 'tty') {
1404 return ['lxc-console', '-n', $vmid];
1405 } elsif ($cmode eq 'shell') {
1406 return ['lxc-attach', '--clear-env', '-n', $vmid];
1407 } else {
1408 die "internal error";
1409 }
1410}
1411
c325b32f
DM
1412sub get_primary_ips {
1413 my ($conf) = @_;
1414
1415 # return data from net0
cbb03fea 1416
27916659 1417 return undef if !defined($conf->{net0});
a16d94c8 1418 my $net = parse_lxc_network($conf->{net0});
c325b32f
DM
1419
1420 my $ipv4 = $net->{ip};
db78a181
WB
1421 if ($ipv4) {
1422 if ($ipv4 =~ /^(dhcp|manual)$/) {
1423 $ipv4 = undef
1424 } else {
1425 $ipv4 =~ s!/\d+$!!;
1426 }
1427 }
65e5eaa3 1428 my $ipv6 = $net->{ip6};
db78a181 1429 if ($ipv6) {
5f291c7d 1430 if ($ipv6 =~ /^(auto|dhcp|manual)$/) {
db78a181
WB
1431 $ipv6 = undef;
1432 } else {
1433 $ipv6 =~ s!/\d+$!!;
1434 }
1435 }
cbb03fea 1436
c325b32f
DM
1437 return ($ipv4, $ipv6);
1438}
148d1cb4 1439
b407293b
WB
1440sub delete_mountpoint_volume {
1441 my ($storage_cfg, $vmid, $volume) = @_;
1442
7c921c80 1443 return if classify_mountpoint($volume) ne 'volume';
b407293b
WB
1444
1445 my ($vtype, $name, $owner) = PVE::Storage::parse_volname($storage_cfg, $volume);
1446 PVE::Storage::vdisk_free($storage_cfg, $volume) if $vmid == $owner;
1447}
ef241384 1448
27916659 1449sub destroy_lxc_container {
148d1cb4
DM
1450 my ($storage_cfg, $vmid, $conf) = @_;
1451
db8989e1
WB
1452 foreach_mountpoint($conf, sub {
1453 my ($ms, $mountpoint) = @_;
b407293b 1454 delete_mountpoint_volume($storage_cfg, $vmid, $mountpoint->{volume});
db8989e1
WB
1455 });
1456
27916659
DM
1457 rmdir "/var/lib/lxc/$vmid/rootfs";
1458 unlink "/var/lib/lxc/$vmid/config";
1459 rmdir "/var/lib/lxc/$vmid";
1460 destroy_config($vmid);
1461
1462 #my $cmd = ['lxc-destroy', '-n', $vmid ];
1463 #PVE::Tools::run_command($cmd);
148d1cb4 1464}
68fba17b 1465
ef241384 1466sub vm_stop_cleanup {
5fa890f0 1467 my ($storage_cfg, $vmid, $conf, $keepActive) = @_;
ef241384
DM
1468
1469 eval {
1470 if (!$keepActive) {
bf9d912c 1471
09aa32fd 1472 my $vollist = get_vm_volumes($conf);
a8b6b8a7 1473 PVE::Storage::deactivate_volumes($storage_cfg, $vollist);
ef241384
DM
1474 }
1475 };
1476 warn $@ if $@; # avoid errors - just warn
1477}
1478
93cdbbfb
AD
1479my $safe_num_ne = sub {
1480 my ($a, $b) = @_;
1481
1482 return 0 if !defined($a) && !defined($b);
1483 return 1 if !defined($a);
1484 return 1 if !defined($b);
1485
1486 return $a != $b;
1487};
1488
1489my $safe_string_ne = sub {
1490 my ($a, $b) = @_;
1491
1492 return 0 if !defined($a) && !defined($b);
1493 return 1 if !defined($a);
1494 return 1 if !defined($b);
1495
1496 return $a ne $b;
1497};
1498
1499sub update_net {
bedeaaf1 1500 my ($vmid, $conf, $opt, $newnet, $netid, $rootdir) = @_;
93cdbbfb 1501
18862537
WB
1502 if ($newnet->{type} ne 'veth') {
1503 # for when there are physical interfaces
1504 die "cannot update interface of type $newnet->{type}";
1505 }
1506
1507 my $veth = "veth${vmid}i${netid}";
93cdbbfb
AD
1508 my $eth = $newnet->{name};
1509
18862537
WB
1510 if (my $oldnetcfg = $conf->{$opt}) {
1511 my $oldnet = parse_lxc_network($oldnetcfg);
1512
1513 if (&$safe_string_ne($oldnet->{hwaddr}, $newnet->{hwaddr}) ||
1514 &$safe_string_ne($oldnet->{name}, $newnet->{name})) {
93cdbbfb 1515
18862537 1516 PVE::Network::veth_delete($veth);
bedeaaf1 1517 delete $conf->{$opt};
706c9791 1518 write_config($vmid, $conf);
93cdbbfb 1519
18862537 1520 hotplug_net($vmid, $conf, $opt, $newnet, $netid);
bedeaaf1 1521
18862537
WB
1522 } elsif (&$safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
1523 &$safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
1524 &$safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
bedeaaf1 1525
18862537 1526 if ($oldnet->{bridge}) {
bedeaaf1 1527 PVE::Network::tap_unplug($veth);
18862537
WB
1528 foreach (qw(bridge tag firewall)) {
1529 delete $oldnet->{$_};
1530 }
1531 $conf->{$opt} = print_lxc_network($oldnet);
706c9791 1532 write_config($vmid, $conf);
bedeaaf1 1533 }
93cdbbfb 1534
23eb2244 1535 PVE::Network::tap_plug($veth, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks});
18862537
WB
1536 foreach (qw(bridge tag firewall)) {
1537 $oldnet->{$_} = $newnet->{$_} if $newnet->{$_};
1538 }
1539 $conf->{$opt} = print_lxc_network($oldnet);
706c9791 1540 write_config($vmid, $conf);
93cdbbfb
AD
1541 }
1542 } else {
18862537 1543 hotplug_net($vmid, $conf, $opt, $newnet, $netid);
93cdbbfb
AD
1544 }
1545
bedeaaf1 1546 update_ipconfig($vmid, $conf, $opt, $eth, $newnet, $rootdir);
93cdbbfb
AD
1547}
1548
1549sub hotplug_net {
18862537 1550 my ($vmid, $conf, $opt, $newnet, $netid) = @_;
93cdbbfb 1551
18862537 1552 my $veth = "veth${vmid}i${netid}";
cbb03fea 1553 my $vethpeer = $veth . "p";
93cdbbfb
AD
1554 my $eth = $newnet->{name};
1555
1556 PVE::Network::veth_create($veth, $vethpeer, $newnet->{bridge}, $newnet->{hwaddr});
23eb2244 1557 PVE::Network::tap_plug($veth, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks});
93cdbbfb 1558
cbb03fea 1559 # attach peer in container
93cdbbfb
AD
1560 my $cmd = ['lxc-device', '-n', $vmid, 'add', $vethpeer, "$eth" ];
1561 PVE::Tools::run_command($cmd);
1562
cbb03fea 1563 # link up peer in container
93cdbbfb
AD
1564 $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'link', 'set', $eth ,'up' ];
1565 PVE::Tools::run_command($cmd);
bedeaaf1 1566
18862537
WB
1567 my $done = { type => 'veth' };
1568 foreach (qw(bridge tag firewall hwaddr name)) {
1569 $done->{$_} = $newnet->{$_} if $newnet->{$_};
1570 }
1571 $conf->{$opt} = print_lxc_network($done);
bedeaaf1 1572
706c9791 1573 write_config($vmid, $conf);
93cdbbfb
AD
1574}
1575
68a05bb3 1576sub update_ipconfig {
bedeaaf1
AD
1577 my ($vmid, $conf, $opt, $eth, $newnet, $rootdir) = @_;
1578
f2104b80 1579 my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir);
bedeaaf1 1580
18862537 1581 my $optdata = parse_lxc_network($conf->{$opt});
84e0c123
WB
1582 my $deleted = [];
1583 my $added = [];
8d723477
WB
1584 my $nscmd = sub {
1585 my $cmdargs = shift;
1586 PVE::Tools::run_command(['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', @_], %$cmdargs);
84e0c123 1587 };
8d723477 1588 my $ipcmd = sub { &$nscmd({}, '/sbin/ip', @_) };
2bfd1615 1589
84e0c123 1590 my $change_ip_config = sub {
f39002a6
DM
1591 my ($ipversion) = @_;
1592
1593 my $family_opt = "-$ipversion";
1594 my $suffix = $ipversion == 4 ? '' : $ipversion;
84e0c123
WB
1595 my $gw= "gw$suffix";
1596 my $ip= "ip$suffix";
bedeaaf1 1597
6178b0dd
WB
1598 my $newip = $newnet->{$ip};
1599 my $newgw = $newnet->{$gw};
1600 my $oldip = $optdata->{$ip};
1601
1602 my $change_ip = &$safe_string_ne($oldip, $newip);
1603 my $change_gw = &$safe_string_ne($optdata->{$gw}, $newgw);
bedeaaf1 1604
84e0c123 1605 return if !$change_ip && !$change_gw;
68a05bb3 1606
84e0c123 1607 # step 1: add new IP, if this fails we cancel
292aff54
WB
1608 my $is_real_ip = ($newip && $newip !~ /^(?:auto|dhcp|manual)$/);
1609 if ($change_ip && $is_real_ip) {
8d723477 1610 eval { &$ipcmd($family_opt, 'addr', 'add', $newip, 'dev', $eth); };
84e0c123
WB
1611 if (my $err = $@) {
1612 warn $err;
1613 return;
1614 }
bedeaaf1 1615 }
bedeaaf1 1616
84e0c123
WB
1617 # step 2: replace gateway
1618 # If this fails we delete the added IP and cancel.
1619 # If it succeeds we save the config and delete the old IP, ignoring
1620 # errors. The config is then saved.
1621 # Note: 'ip route replace' can add
1622 if ($change_gw) {
6178b0dd 1623 if ($newgw) {
292aff54
WB
1624 eval {
1625 if ($is_real_ip && !PVE::Network::is_ip_in_cidr($newgw, $newip, $ipversion)) {
1626 &$ipcmd($family_opt, 'route', 'add', $newgw, 'dev', $eth);
1627 }
1628 &$ipcmd($family_opt, 'route', 'replace', 'default', 'via', $newgw);
1629 };
84e0c123
WB
1630 if (my $err = $@) {
1631 warn $err;
1632 # the route was not replaced, the old IP is still available
1633 # rollback (delete new IP) and cancel
1634 if ($change_ip) {
8d723477 1635 eval { &$ipcmd($family_opt, 'addr', 'del', $newip, 'dev', $eth); };
84e0c123
WB
1636 warn $@ if $@; # no need to die here
1637 }
1638 return;
1639 }
1640 } else {
8d723477 1641 eval { &$ipcmd($family_opt, 'route', 'del', 'default'); };
84e0c123
WB
1642 # if the route was not deleted, the guest might have deleted it manually
1643 # warn and continue
1644 warn $@ if $@;
1645 }
2bfd1615 1646 }
2bfd1615 1647
6178b0dd 1648 # from this point on we save the configuration
84e0c123 1649 # step 3: delete old IP ignoring errors
6178b0dd 1650 if ($change_ip && $oldip && $oldip !~ /^(?:auto|dhcp)$/) {
8d723477
WB
1651 # We need to enable promote_secondaries, otherwise our newly added
1652 # address will be removed along with the old one.
1653 my $promote = 0;
1654 eval {
1655 if ($ipversion == 4) {
1656 &$nscmd({ outfunc => sub { $promote = int(shift) } },
1657 'cat', "/proc/sys/net/ipv4/conf/$eth/promote_secondaries");
1658 &$nscmd({}, 'sysctl', "net.ipv4.conf.$eth.promote_secondaries=1");
1659 }
1660 &$ipcmd($family_opt, 'addr', 'del', $oldip, 'dev', $eth);
1661 };
84e0c123 1662 warn $@ if $@; # no need to die here
8d723477
WB
1663
1664 if ($ipversion == 4) {
1665 &$nscmd({}, 'sysctl', "net.ipv4.conf.$eth.promote_secondaries=$promote");
1666 }
bedeaaf1
AD
1667 }
1668
84e0c123
WB
1669 foreach my $property ($ip, $gw) {
1670 if ($newnet->{$property}) {
1671 $optdata->{$property} = $newnet->{$property};
1672 } else {
1673 delete $optdata->{$property};
1674 }
bedeaaf1 1675 }
18862537 1676 $conf->{$opt} = print_lxc_network($optdata);
706c9791 1677 write_config($vmid, $conf);
84e0c123
WB
1678 $lxc_setup->setup_network($conf);
1679 };
bedeaaf1 1680
f39002a6
DM
1681 &$change_ip_config(4);
1682 &$change_ip_config(6);
489e960d
WL
1683
1684}
1685
a92f66c9
WL
1686# Internal snapshots
1687
1688# NOTE: Snapshot create/delete involves several non-atomic
c31ad455
FG
1689# actions, and can take a long time.
1690# So we try to avoid locking the file and use the 'lock' variable
a92f66c9
WL
1691# inside the config file instead.
1692
1693my $snapshot_copy_config = sub {
1694 my ($source, $dest) = @_;
1695
1696 foreach my $k (keys %$source) {
1697 next if $k eq 'snapshots';
09d3ec42
DM
1698 next if $k eq 'snapstate';
1699 next if $k eq 'snaptime';
1700 next if $k eq 'vmstate';
1701 next if $k eq 'lock';
a92f66c9 1702 next if $k eq 'digest';
09d3ec42 1703 next if $k eq 'description';
a92f66c9
WL
1704
1705 $dest->{$k} = $source->{$k};
1706 }
1707};
1708
1709my $snapshot_prepare = sub {
1710 my ($vmid, $snapname, $comment) = @_;
1711
1712 my $snap;
1713
1714 my $updatefn = sub {
1715
1716 my $conf = load_config($vmid);
1717
bb1ac2de
DM
1718 die "you can't take a snapshot if it's a template\n"
1719 if is_template($conf);
1720
a92f66c9
WL
1721 check_lock($conf);
1722
09d3ec42 1723 $conf->{lock} = 'snapshot';
a92f66c9
WL
1724
1725 die "snapshot name '$snapname' already used\n"
1726 if defined($conf->{snapshots}->{$snapname});
1727
1728 my $storecfg = PVE::Storage::config();
5d385379
FG
1729 my $feature = $snapname eq 'vzdump' ? 'vzdump' : 'snapshot';
1730 die "snapshot feature is not available\n" if !has_feature($feature, $conf, $storecfg);
a92f66c9
WL
1731
1732 $snap = $conf->{snapshots}->{$snapname} = {};
1733
1734 &$snapshot_copy_config($conf, $snap);
1735
09d3ec42
DM
1736 $snap->{'snapstate'} = "prepare";
1737 $snap->{'snaptime'} = time();
1738 $snap->{'description'} = $comment if $comment;
a92f66c9
WL
1739 $conf->{snapshots}->{$snapname} = $snap;
1740
706c9791 1741 write_config($vmid, $conf);
a92f66c9
WL
1742 };
1743
1744 lock_container($vmid, 10, $updatefn);
1745
1746 return $snap;
1747};
1748
1749my $snapshot_commit = sub {
1750 my ($vmid, $snapname) = @_;
1751
1752 my $updatefn = sub {
1753
1754 my $conf = load_config($vmid);
1755
1756 die "missing snapshot lock\n"
09d3ec42 1757 if !($conf->{lock} && $conf->{lock} eq 'snapshot');
a92f66c9 1758
27916659 1759 die "snapshot '$snapname' does not exist\n"
a92f66c9
WL
1760 if !defined($conf->{snapshots}->{$snapname});
1761
1762 die "wrong snapshot state\n"
09d3ec42
DM
1763 if !($conf->{snapshots}->{$snapname}->{'snapstate'} &&
1764 $conf->{snapshots}->{$snapname}->{'snapstate'} eq "prepare");
a92f66c9 1765
09d3ec42
DM
1766 delete $conf->{snapshots}->{$snapname}->{'snapstate'};
1767 delete $conf->{lock};
1768 $conf->{parent} = $snapname;
a92f66c9 1769
706c9791 1770 write_config($vmid, $conf);
a92f66c9
WL
1771 };
1772
1773 lock_container($vmid, 10 ,$updatefn);
1774};
1775
1776sub has_feature {
1777 my ($feature, $conf, $storecfg, $snapname) = @_;
09d3ec42 1778
a92f66c9 1779 my $err;
5d385379
FG
1780 my $vzdump = $feature eq 'vzdump';
1781 $feature = 'snapshot' if $vzdump;
09d3ec42 1782
8bf50651
DM
1783 foreach_mountpoint($conf, sub {
1784 my ($ms, $mountpoint) = @_;
1785
2c3ed8c4 1786 return if $err; # skip further test
5d385379 1787 return if $vzdump && $ms ne 'rootfs' && !$mountpoint->{backup};
2c3ed8c4 1788
8bf50651
DM
1789 $err = 1 if !PVE::Storage::volume_has_feature($storecfg, $feature, $mountpoint->{volume}, $snapname);
1790
1791 # TODO: implement support for mountpoints
1792 die "unable to handle mountpoint '$ms' - feature not implemented\n"
1793 if $ms ne 'rootfs';
1794 });
a92f66c9
WL
1795
1796 return $err ? 0 : 1;
1797}
1798
489e960d
WL
1799sub snapshot_create {
1800 my ($vmid, $snapname, $comment) = @_;
1801
a92f66c9
WL
1802 my $snap = &$snapshot_prepare($vmid, $snapname, $comment);
1803
09d3ec42 1804 my $conf = load_config($vmid);
a92f66c9 1805
a92f66c9 1806 my $running = check_running($vmid);
2477a7f1
DM
1807
1808 my $unfreeze = 0;
74bf6d37
FG
1809
1810 my $drivehash = {};
1811
a92f66c9
WL
1812 eval {
1813 if ($running) {
2477a7f1 1814 $unfreeze = 1;
74bf6d37 1815 PVE::Tools::run_command(['/usr/bin/lxc-freeze', '-n', $vmid]);
4db769cf 1816 PVE::Tools::run_command(['/bin/sync']);
a92f66c9
WL
1817 };
1818
1819 my $storecfg = PVE::Storage::config();
44a9face 1820 my $rootinfo = parse_ct_rootfs($conf->{rootfs});
09d3ec42 1821 my $volid = $rootinfo->{volume};
a92f66c9 1822
a92f66c9 1823 PVE::Storage::volume_snapshot($storecfg, $volid, $snapname);
74bf6d37 1824 $drivehash->{rootfs} = 1;
a92f66c9 1825 };
2477a7f1
DM
1826 my $err = $@;
1827
1828 if ($unfreeze) {
1829 eval { PVE::Tools::run_command(['/usr/bin/lxc-unfreeze', '-n', $vmid]); };
1830 warn $@ if $@;
1831 }
1832
1833 if ($err) {
74bf6d37
FG
1834 eval { snapshot_delete($vmid, $snapname, 1, $drivehash); };
1835 warn "$@\n" if $@;
a92f66c9
WL
1836 die "$err\n";
1837 }
74bf6d37
FG
1838
1839 &$snapshot_commit($vmid, $snapname);
68a05bb3
AD
1840}
1841
74bf6d37 1842# Note: $drivehash is only set when called from snapshot_create.
57ccb3f8 1843sub snapshot_delete {
74bf6d37 1844 my ($vmid, $snapname, $force, $drivehash) = @_;
57ccb3f8 1845
31429832
WL
1846 my $snap;
1847
1848 my $conf;
1849
1850 my $updatefn = sub {
1851
1852 $conf = load_config($vmid);
1853
bb1ac2de
DM
1854 die "you can't delete a snapshot if vm is a template\n"
1855 if is_template($conf);
1856
31429832
WL
1857 $snap = $conf->{snapshots}->{$snapname};
1858
74bf6d37
FG
1859 if (!$drivehash) {
1860 check_lock($conf);
1861 }
31429832
WL
1862
1863 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1864
09d3ec42 1865 $snap->{snapstate} = 'delete';
31429832 1866
706c9791 1867 write_config($vmid, $conf);
31429832
WL
1868 };
1869
1870 lock_container($vmid, 10, $updatefn);
1871
1872 my $storecfg = PVE::Storage::config();
1873
7b2eb379 1874 my $unlink_parent = sub {
31429832 1875
7b2eb379 1876 my ($confref, $new_parent) = @_;
31429832 1877
7b2eb379
FG
1878 if ($confref->{parent} && $confref->{parent} eq $snapname) {
1879 if ($new_parent) {
1880 $confref->{parent} = $new_parent;
31429832 1881 } else {
7b2eb379 1882 delete $confref->{parent};
31429832
WL
1883 }
1884 }
7b2eb379
FG
1885 };
1886
1887 my $del_snap = sub {
1888
74bf6d37
FG
1889 $conf = load_config($vmid);
1890
1891 if ($drivehash) {
1892 delete $conf->{lock};
1893 } else {
1894 check_lock($conf);
1895 }
7b2eb379
FG
1896
1897 my $parent = $conf->{snapshots}->{$snapname}->{parent};
1898 foreach my $snapkey (keys %{$conf->{snapshots}}) {
1899 &$unlink_parent($conf->{snapshots}->{$snapkey}, $parent);
1900 }
1901
1902 &$unlink_parent($conf, $parent);
31429832
WL
1903
1904 delete $conf->{snapshots}->{$snapname};
1905
706c9791 1906 write_config($vmid, $conf);
31429832
WL
1907 };
1908
09d3ec42 1909 my $rootfs = $conf->{snapshots}->{$snapname}->{rootfs};
44a9face 1910 my $rootinfo = parse_ct_rootfs($rootfs);
09d3ec42 1911 my $volid = $rootinfo->{volume};
31429832
WL
1912
1913 eval {
1914 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snapname);
1915 };
1916 my $err = $@;
1917
1918 if(!$err || ($err && $force)) {
1919 lock_container($vmid, 10, $del_snap);
1920 if ($err) {
1921 die "Can't delete snapshot: $vmid $snapname $err\n";
1922 }
1923 }
57ccb3f8
WL
1924}
1925
723157f6
WL
1926sub snapshot_rollback {
1927 my ($vmid, $snapname) = @_;
1928
6860ba0c
WL
1929 my $storecfg = PVE::Storage::config();
1930
1931 my $conf = load_config($vmid);
1932
bb1ac2de
DM
1933 die "you can't rollback if vm is a template\n" if is_template($conf);
1934
6860ba0c
WL
1935 my $snap = $conf->{snapshots}->{$snapname};
1936
1937 die "snapshot '$snapname' does not exist\n" if !defined($snap);
1938
09d3ec42 1939 my $rootfs = $snap->{rootfs};
44a9face 1940 my $rootinfo = parse_ct_rootfs($rootfs);
09d3ec42
DM
1941 my $volid = $rootinfo->{volume};
1942
1943 PVE::Storage::volume_rollback_is_possible($storecfg, $volid, $snapname);
6860ba0c
WL
1944
1945 my $updatefn = sub {
1946
09d3ec42
DM
1947 die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
1948 if $snap->{snapstate};
6860ba0c
WL
1949
1950 check_lock($conf);
6860ba0c 1951
b935932a 1952 system("lxc-stop -n $vmid --kill") if check_running($vmid);
6860ba0c
WL
1953
1954 die "unable to rollback vm $vmid: vm is running\n"
1955 if check_running($vmid);
1956
09d3ec42 1957 $conf->{lock} = 'rollback';
6860ba0c
WL
1958
1959 my $forcemachine;
1960
1961 # copy snapshot config to current config
1962
1963 my $tmp_conf = $conf;
1964 &$snapshot_copy_config($tmp_conf->{snapshots}->{$snapname}, $conf);
6860ba0c 1965 $conf->{snapshots} = $tmp_conf->{snapshots};
09d3ec42
DM
1966 delete $conf->{snaptime};
1967 delete $conf->{snapname};
1968 $conf->{parent} = $snapname;
6860ba0c 1969
706c9791 1970 write_config($vmid, $conf);
6860ba0c
WL
1971 };
1972
1973 my $unlockfn = sub {
09d3ec42 1974 delete $conf->{lock};
706c9791 1975 write_config($vmid, $conf);
6860ba0c
WL
1976 };
1977
1978 lock_container($vmid, 10, $updatefn);
1979
09d3ec42 1980 PVE::Storage::volume_snapshot_rollback($storecfg, $volid, $snapname);
6860ba0c
WL
1981
1982 lock_container($vmid, 5, $unlockfn);
723157f6 1983}
b935932a 1984
bb1ac2de
DM
1985sub template_create {
1986 my ($vmid, $conf) = @_;
1987
1988 my $storecfg = PVE::Storage::config();
1989
44a9face 1990 my $rootinfo = parse_ct_rootfs($conf->{rootfs});
bb1ac2de
DM
1991 my $volid = $rootinfo->{volume};
1992
1993 die "Template feature is not available for '$volid'\n"
1994 if !PVE::Storage::volume_has_feature($storecfg, 'template', $volid);
1995
1996 PVE::Storage::activate_volumes($storecfg, [$volid]);
1997
1998 my $template_volid = PVE::Storage::vdisk_create_base($storecfg, $volid);
1999 $rootinfo->{volume} = $template_volid;
4fee75fd 2000 $conf->{rootfs} = print_ct_mountpoint($rootinfo, 1);
bb1ac2de
DM
2001
2002 write_config($vmid, $conf);
2003}
2004
2005sub is_template {
2006 my ($conf) = @_;
2007
2008 return 1 if defined $conf->{template} && $conf->{template} == 1;
2009}
2010
9622e848
DM
2011sub mountpoint_names {
2012 my ($reverse) = @_;
ced7fddb 2013
9622e848 2014 my @names = ('rootfs');
eaebef36
DM
2015
2016 for (my $i = 0; $i < $MAX_MOUNT_POINTS; $i++) {
9622e848
DM
2017 push @names, "mp$i";
2018 }
2019
2020 return $reverse ? reverse @names : @names;
2021}
2022
3c9dbfa9 2023
9622e848
DM
2024sub foreach_mountpoint_full {
2025 my ($conf, $reverse, $func) = @_;
2026
2027 foreach my $key (mountpoint_names($reverse)) {
2028 my $value = $conf->{$key};
2029 next if !defined($value);
44a9face 2030 my $mountpoint = $key eq 'rootfs' ? parse_ct_rootfs($value, 1) : parse_ct_mountpoint($value, 1);
ca7feb1a 2031 next if !defined($mountpoint);
3c9dbfa9 2032
eaebef36 2033 &$func($key, $mountpoint);
ced7fddb
AD
2034 }
2035}
2036
9622e848
DM
2037sub foreach_mountpoint {
2038 my ($conf, $func) = @_;
2039
2040 foreach_mountpoint_full($conf, 0, $func);
2041}
2042
2043sub foreach_mountpoint_reverse {
2044 my ($conf, $func) = @_;
2045
2046 foreach_mountpoint_full($conf, 1, $func);
2047}
2048
52389a07
DM
2049sub check_ct_modify_config_perm {
2050 my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_;
2051
2052 return 1 if $authuser ne 'root@pam';
2053
2054 foreach my $opt (@$key_list) {
2055
2056 if ($opt eq 'cpus' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
2057 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
e59a61ed 2058 } elsif ($opt eq 'rootfs' || $opt =~ /^mp\d+$/) {
52389a07
DM
2059 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
2060 } elsif ($opt eq 'memory' || $opt eq 'swap') {
2061 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
2062 } elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
2063 $opt eq 'searchdomain' || $opt eq 'hostname') {
2064 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
2065 } else {
2066 $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
2067 }
2068 }
2069
2070 return 1;
2071}
2072
9622e848 2073sub umount_all {
da629848 2074 my ($vmid, $storage_cfg, $conf, $noerr) = @_;
9622e848
DM
2075
2076 my $rootdir = "/var/lib/lxc/$vmid/rootfs";
2077 my $volid_list = get_vm_volumes($conf);
2078
2079 foreach_mountpoint_reverse($conf, sub {
2080 my ($ms, $mountpoint) = @_;
2081
2082 my $volid = $mountpoint->{volume};
2083 my $mount = $mountpoint->{mp};
2084
2085 return if !$volid || !$mount;
2086
d18f96b4 2087 my $mount_path = "$rootdir/$mount";
f845a93d 2088 $mount_path =~ s!/+!/!g;
9622e848 2089
228a5a1d
WL
2090 return if !PVE::ProcFSTools::is_mounted($mount_path);
2091
9622e848 2092 eval {
d18f96b4 2093 PVE::Tools::run_command(['umount', '-d', $mount_path]);
9622e848
DM
2094 };
2095 if (my $err = $@) {
2096 if ($noerr) {
2097 warn $err;
2098 } else {
2099 die $err;
2100 }
2101 }
2102 });
9622e848
DM
2103}
2104
2105sub mount_all {
7b49dfe0 2106 my ($vmid, $storage_cfg, $conf) = @_;
9622e848
DM
2107
2108 my $rootdir = "/var/lib/lxc/$vmid/rootfs";
1adc7e53 2109 File::Path::make_path($rootdir);
9622e848
DM
2110
2111 my $volid_list = get_vm_volumes($conf);
2112 PVE::Storage::activate_volumes($storage_cfg, $volid_list);
2113
2114 eval {
9622e848
DM
2115 foreach_mountpoint($conf, sub {
2116 my ($ms, $mountpoint) = @_;
2117
da629848 2118 mountpoint_mount($mountpoint, $rootdir, $storage_cfg);
9622e848
DM
2119 });
2120 };
2121 if (my $err = $@) {
e2007ac2 2122 warn "mounting container failed\n";
9622e848 2123 umount_all($vmid, $storage_cfg, $conf, 1);
e2007ac2 2124 die $err;
9622e848
DM
2125 }
2126
da629848 2127 return $rootdir;
9622e848
DM
2128}
2129
2130
b15c75fc 2131sub mountpoint_mount_path {
da629848 2132 my ($mountpoint, $storage_cfg, $snapname) = @_;
b15c75fc 2133
da629848 2134 return mountpoint_mount($mountpoint, undef, $storage_cfg, $snapname);
b15c75fc 2135}
cc6b0307 2136
2cfae16e
WB
2137my $check_mount_path = sub {
2138 my ($path) = @_;
2139 $path = File::Spec->canonpath($path);
2140 my $real = Cwd::realpath($path);
2141 if ($real ne $path) {
2142 die "mount path modified by symlink: $path != $real";
2143 }
2144};
2145
21f292ff
WB
2146sub query_loopdev {
2147 my ($path) = @_;
2148 my $found;
2149 my $parser = sub {
2150 my $line = shift;
2151 if ($line =~ m@^(/dev/loop\d+):@) {
2152 $found = $1;
2153 }
2154 };
2155 my $cmd = ['losetup', '--associated', $path];
2156 PVE::Tools::run_command($cmd, outfunc => $parser);
2157 return $found;
2158}
2159
50df544c
WB
2160# Run a function with a file attached to a loop device.
2161# The loop device is always detached afterwards (or set to autoclear).
2162# Returns the loop device.
2163sub run_with_loopdev {
2164 my ($func, $file) = @_;
2165 my $device;
2166 my $parser = sub {
2167 my $line = shift;
2168 if ($line =~ m@^(/dev/loop\d+)$@) {
2169 $device = $1;
2170 }
2171 };
2172 PVE::Tools::run_command(['losetup', '--show', '-f', $file], outfunc => $parser);
2173 die "failed to setup loop device for $file\n" if !$device;
2174 eval { &$func($device); };
2175 my $err = $@;
2176 PVE::Tools::run_command(['losetup', '-d', $device]);
2177 die $err if $err;
2178 return $device;
2179}
2180
b15c75fc 2181# use $rootdir = undef to just return the corresponding mount path
cc6b0307 2182sub mountpoint_mount {
da629848 2183 my ($mountpoint, $rootdir, $storage_cfg, $snapname) = @_;
cc6b0307
AD
2184
2185 my $volid = $mountpoint->{volume};
2186 my $mount = $mountpoint->{mp};
7c921c80 2187 my $type = $mountpoint->{type};
50df544c
WB
2188 my $quota = !$snapname && !$mountpoint->{ro} && $mountpoint->{quota};
2189 my $mounted_dev;
b15c75fc 2190
cc6b0307
AD
2191 return if !$volid || !$mount;
2192
b15c75fc
DM
2193 my $mount_path;
2194
2195 if (defined($rootdir)) {
2196 $rootdir =~ s!/+$!!;
2197 $mount_path = "$rootdir/$mount";
f845a93d 2198 $mount_path =~ s!/+!/!g;
2cfae16e 2199 &$check_mount_path($mount_path);
b15c75fc 2200 File::Path::mkpath($mount_path);
116ce06f 2201 }
b15c75fc
DM
2202
2203 my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
cc6b0307 2204
b15c75fc 2205 die "unknown snapshot path for '$volid'" if !$storage && defined($snapname);
cc6b0307 2206
471dd315
WB
2207 my $optstring = '';
2208 if (defined($mountpoint->{acl})) {
2209 $optstring .= ($mountpoint->{acl} ? 'acl' : 'noacl');
2210 }
2211 if ($mountpoint->{ro}) {
2212 $optstring .= ',' if $optstring;
2213 $optstring .= 'ro';
2214 }
2215
2216 my @extra_opts = ('-o', $optstring);
2217
b15c75fc
DM
2218 if ($storage) {
2219
2220 my $scfg = PVE::Storage::storage_config($storage_cfg, $storage);
2221 my $path = PVE::Storage::path($storage_cfg, $volid, $snapname);
2222
2223 my ($vtype, undef, undef, undef, undef, $isBase, $format) =
2224 PVE::Storage::parse_volname($storage_cfg, $volid);
2225
c87b9dd8
DM
2226 $format = 'iso' if $vtype eq 'iso'; # allow to handle iso files
2227
b15c75fc 2228 if ($format eq 'subvol') {
30de33be
DM
2229 if ($mount_path) {
2230 if ($snapname) {
e84f7f5d
DM
2231 if ($scfg->{type} eq 'zfspool') {
2232 my $path_arg = $path;
2233 $path_arg =~ s!^/+!!;
471dd315 2234 PVE::Tools::run_command(['mount', '-o', 'ro', @extra_opts, '-t', 'zfs', $path_arg, $mount_path]);
e84f7f5d 2235 } else {
30de33be
DM
2236 die "cannot mount subvol snapshots for storage type '$scfg->{type}'\n";
2237 }
e84f7f5d 2238 } else {
471dd315
WB
2239 if ($mountpoint->{ro}) {
2240 die "read-only bind mounts not supported\n";
2241 }
2242 PVE::Tools::run_command(['mount', '-o', 'bind', @extra_opts, $path, $mount_path]);
50df544c 2243 warn "cannot enable quota control for bind mounted subvolumes\n" if $quota;
30de33be 2244 }
b15c75fc 2245 }
50df544c 2246 return wantarray ? ($path, 0, $mounted_dev) : $path;
c87b9dd8 2247 } elsif ($format eq 'raw' || $format eq 'iso') {
50df544c
WB
2248 my $domount = sub {
2249 my ($path) = @_;
2250 if ($mount_path) {
2251 if ($format eq 'iso') {
2252 PVE::Tools::run_command(['mount', '-o', 'ro', @extra_opts, $path, $mount_path]);
2253 } elsif ($isBase || defined($snapname)) {
2254 PVE::Tools::run_command(['mount', '-o', 'ro,noload', @extra_opts, $path, $mount_path]);
2255 } else {
2256 if ($quota) {
2257 push @extra_opts, '-o', 'usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv0';
2258 }
2259 PVE::Tools::run_command(['mount', @extra_opts, $path, $mount_path]);
2260 }
2261 }
2262 };
30de33be 2263 my $use_loopdev = 0;
b15c75fc 2264 if ($scfg->{path}) {
50df544c 2265 $mounted_dev = run_with_loopdev($domount, $path);
30de33be 2266 $use_loopdev = 1;
2e879877
DM
2267 } elsif ($scfg->{type} eq 'drbd' || $scfg->{type} eq 'lvm' ||
2268 $scfg->{type} eq 'rbd' || $scfg->{type} eq 'lvmthin') {
50df544c
WB
2269 $mounted_dev = $path;
2270 &$domount($path);
b15c75fc
DM
2271 } else {
2272 die "unsupported storage type '$scfg->{type}'\n";
2273 }
50df544c 2274 return wantarray ? ($path, $use_loopdev, $mounted_dev) : $path;
b15c75fc
DM
2275 } else {
2276 die "unsupported image format '$format'\n";
2277 }
7c921c80 2278 } elsif ($type eq 'device') {
471dd315 2279 PVE::Tools::run_command(['mount', @extra_opts, $volid, $mount_path]) if $mount_path;
50df544c 2280 return wantarray ? ($volid, 0, $volid) : $volid;
e2007ac2 2281 } elsif ($type eq 'bind') {
471dd315
WB
2282 if ($mountpoint->{ro}) {
2283 die "read-only bind mounts not supported\n";
2284 # Theoretically we'd have to execute both:
2285 # mount -o bind $a $b
2286 # mount -o bind,remount,ro $a $b
2287 }
e2007ac2 2288 die "directory '$volid' does not exist\n" if ! -d $volid;
2cfae16e 2289 &$check_mount_path($volid);
471dd315 2290 PVE::Tools::run_command(['mount', '-o', 'bind', @extra_opts, $volid, $mount_path]) if $mount_path;
50df544c
WB
2291 warn "cannot enable quota control for bind mounts\n" if $quota;
2292 return wantarray ? ($volid, 0, undef) : $volid;
b15c75fc
DM
2293 }
2294
2295 die "unsupported storage";
cc6b0307
AD
2296}
2297
9205e9d0
AD
2298sub get_vm_volumes {
2299 my ($conf, $excludes) = @_;
2300
2301 my $vollist = [];
2302
706c9791 2303 foreach_mountpoint($conf, sub {
9205e9d0
AD
2304 my ($ms, $mountpoint) = @_;
2305
2306 return if $excludes && $ms eq $excludes;
2307
2308 my $volid = $mountpoint->{volume};
2309
7c921c80 2310 return if !$volid || $mountpoint->{type} ne 'volume';
9205e9d0
AD
2311
2312 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2313 return if !$sid;
2314
2315 push @$vollist, $volid;
2316 });
2317
2318 return $vollist;
2319}
2320
6c871c36 2321sub mkfs {
d216e891 2322 my ($dev, $rootuid, $rootgid) = @_;
6c871c36 2323
d216e891
WB
2324 PVE::Tools::run_command(['mkfs.ext4', '-O', 'mmp',
2325 '-E', "root_owner=$rootuid:$rootgid",
2326 $dev]);
6c871c36
DM
2327}
2328
2329sub format_disk {
d216e891 2330 my ($storage_cfg, $volid, $rootuid, $rootgid) = @_;
6c871c36
DM
2331
2332 if ($volid =~ m!^/dev/.+!) {
2333 mkfs($volid);
2334 return;
2335 }
2336
2337 my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2338
2339 die "cannot format volume '$volid' with no storage\n" if !$storage;
2340
08ca136d
DM
2341 PVE::Storage::activate_volumes($storage_cfg, [$volid]);
2342
6c871c36
DM
2343 my $path = PVE::Storage::path($storage_cfg, $volid);
2344
2345 my ($vtype, undef, undef, undef, undef, $isBase, $format) =
2346 PVE::Storage::parse_volname($storage_cfg, $volid);
2347
2348 die "cannot format volume '$volid' (format == $format)\n"
2349 if $format ne 'raw';
2350
d216e891 2351 mkfs($path, $rootuid, $rootgid);
6c871c36
DM
2352}
2353
2354sub destroy_disks {
2355 my ($storecfg, $vollist) = @_;
2356
2357 foreach my $volid (@$vollist) {
2358 eval { PVE::Storage::vdisk_free($storecfg, $volid); };
2359 warn $@ if $@;
2360 }
2361}
2362
2363sub create_disks {
2364 my ($storecfg, $vmid, $settings, $conf) = @_;
2365
2366 my $vollist = [];
2367
2368 eval {
d216e891
WB
2369 my (undef, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf);
2370 my $chown_vollist = [];
2371
6c871c36
DM
2372 foreach_mountpoint($settings, sub {
2373 my ($ms, $mountpoint) = @_;
2374
2375 my $volid = $mountpoint->{volume};
2376 my $mp = $mountpoint->{mp};
2377
2378 my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2379
e2007ac2 2380 if ($storage && ($volid =~ m/^([^:\s]+):(\d+(\.\d+)?)$/)) {
8ed5ff9d 2381 my ($storeid, $size_gb) = ($1, $2);
6c871c36 2382
8ed5ff9d 2383 my $size_kb = int(${size_gb}*1024) * 1024;
6c871c36
DM
2384
2385 my $scfg = PVE::Storage::storage_config($storecfg, $storage);
2386 # fixme: use better naming ct-$vmid-disk-X.raw?
2387
2388 if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') {
8ed5ff9d 2389 if ($size_kb > 0) {
6c871c36 2390 $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw',
8ed5ff9d 2391 undef, $size_kb);
d216e891 2392 format_disk($storecfg, $volid, $rootuid, $rootgid);
6c871c36
DM
2393 } else {
2394 $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'subvol',
2395 undef, 0);
d216e891 2396 push @$chown_vollist, $volid;
6c871c36
DM
2397 }
2398 } elsif ($scfg->{type} eq 'zfspool') {
2399
2400 $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'subvol',
8ed5ff9d 2401 undef, $size_kb);
d216e891 2402 push @$chown_vollist, $volid;
2e879877 2403 } elsif ($scfg->{type} eq 'drbd' || $scfg->{type} eq 'lvm' || $scfg->{type} eq 'lvmthin') {
6c871c36 2404
8ed5ff9d 2405 $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', undef, $size_kb);
d216e891 2406 format_disk($storecfg, $volid, $rootuid, $rootgid);
6c871c36
DM
2407
2408 } elsif ($scfg->{type} eq 'rbd') {
2409
2410 die "krbd option must be enabled on storage type '$scfg->{type}'\n" if !$scfg->{krbd};
8ed5ff9d 2411 $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', undef, $size_kb);
d216e891 2412 format_disk($storecfg, $volid, $rootuid, $rootgid);
6c871c36
DM
2413 } else {
2414 die "unable to create containers on storage type '$scfg->{type}'\n";
2415 }
2416 push @$vollist, $volid;
71c780b9
WB
2417 $mountpoint->{volume} = $volid;
2418 $mountpoint->{size} = $size_kb * 1024;
2419 $conf->{$ms} = print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
6c871c36 2420 } else {
e2007ac2
DM
2421 # use specified/existing volid/dir/device
2422 $conf->{$ms} = print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
6c871c36
DM
2423 }
2424 });
d216e891
WB
2425
2426 PVE::Storage::activate_volumes($storecfg, $chown_vollist, undef);
2427 foreach my $volid (@$chown_vollist) {
2428 my $path = PVE::Storage::path($storecfg, $volid, undef);
2429 chown($rootuid, $rootgid, $path);
2430 }
2431 PVE::Storage::deactivate_volumes($storecfg, $chown_vollist, undef);
6c871c36
DM
2432 };
2433 # free allocated images on error
2434 if (my $err = $@) {
2435 destroy_disks($storecfg, $vollist);
2436 die $err;
2437 }
2438 return $vollist;
2439}
2440
68e8f3c5
DM
2441# bash completion helper
2442
2443sub complete_os_templates {
2444 my ($cmdname, $pname, $cvalue) = @_;
2445
2446 my $cfg = PVE::Storage::config();
2447
9e9bc3a6 2448 my $storeid;
68e8f3c5
DM
2449
2450 if ($cvalue =~ m/^([^:]+):/) {
2451 $storeid = $1;
2452 }
2453
2454 my $vtype = $cmdname eq 'restore' ? 'backup' : 'vztmpl';
2455 my $data = PVE::Storage::template_list($cfg, $storeid, $vtype);
2456
2457 my $res = [];
2458 foreach my $id (keys %$data) {
2459 foreach my $item (@{$data->{$id}}) {
2460 push @$res, $item->{volid} if defined($item->{volid});
2461 }
2462 }
2463
2464 return $res;
2465}
2466
68e8f3c5
DM
2467my $complete_ctid_full = sub {
2468 my ($running) = @_;
2469
2470 my $idlist = vmstatus();
2471
2472 my $active_hash = list_active_containers();
2473
2474 my $res = [];
2475
2476 foreach my $id (keys %$idlist) {
2477 my $d = $idlist->{$id};
2478 if (defined($running)) {
2479 next if $d->{template};
2480 next if $running && !$active_hash->{$id};
2481 next if !$running && $active_hash->{$id};
2482 }
2483 push @$res, $id;
2484
2485 }
2486 return $res;
2487};
2488
2489sub complete_ctid {
2490 return &$complete_ctid_full();
2491}
2492
2493sub complete_ctid_stopped {
2494 return &$complete_ctid_full(0);
2495}
2496
2497sub complete_ctid_running {
2498 return &$complete_ctid_full(1);
2499}
2500
c6a605f9
WB
2501sub parse_id_maps {
2502 my ($conf) = @_;
2503
2504 my $id_map = [];
2505 my $rootuid = 0;
2506 my $rootgid = 0;
2507
2508 my $lxc = $conf->{lxc};
2509 foreach my $entry (@$lxc) {
2510 my ($key, $value) = @$entry;
2511 next if $key ne 'lxc.id_map';
2512 if ($value =~ /^([ug])\s+(\d+)\s+(\d+)\s+(\d+)\s*$/) {
2513 my ($type, $ct, $host, $length) = ($1, $2, $3, $4);
2514 push @$id_map, [$type, $ct, $host, $length];
2515 if ($ct == 0) {
2516 $rootuid = $host if $type eq 'u';
2517 $rootgid = $host if $type eq 'g';
2518 }
2519 } else {
2520 die "failed to parse id_map: $value\n";
2521 }
2522 }
2523
2524 if (!@$id_map && $conf->{unprivileged}) {
2525 # Should we read them from /etc/subuid?
2526 $id_map = [ ['u', '0', '100000', '65536'],
2527 ['g', '0', '100000', '65536'] ];
2528 $rootuid = $rootgid = 100000;
2529 }
2530
2531 return ($id_map, $rootuid, $rootgid);
2532}
2533
01dce99b
WB
2534sub userns_command {
2535 my ($id_map) = @_;
2536 if (@$id_map) {
2537 return ['lxc-usernsexec', (map { ('-m', join(':', @$_)) } @$id_map), '--'];
2538 }
2539 return [];
2540}
2541
f76a2828 25421;