]> git.proxmox.com Git - pve-container.git/blame - src/PVE/LXC/Tools.pm
config: implement method to calculate derived properties from a config
[pve-container.git] / src / PVE / LXC / Tools.pm
CommitLineData
d92d0afd 1# Module for lxc related functionality used mostly by our hooks.
d92d0afd
WB
2package PVE::LXC::Tools;
3
abcab32d
TL
4use strict;
5use warnings;
6
f4c0ba30
WB
7use Errno qw(ENOSYS);
8
d92d0afd
WB
9use PVE::SafeSyslog;
10
11# LXC introduced an `lxc.hook.version` property which allows hooks to be executed in different
12# manners. The old way passes a lot of stuff as command line parameter, the new way passes
13# environment variables.
14#
15# This is supposed to be a common entry point for hooks, consuming the parameters passed by lxc and
16# passing them on to a subroutine in a defined way.
17sub lxc_hook($$&) {
18 my ($expected_type, $expected_section, $code) = @_;
19
20 my ($ct_name, $section, $type);
21 my $namespaces = {};
22 my $args;
23
24 my $version = $ENV{LXC_HOOK_VERSION} // '0';
25 if ($version eq '0') {
26 # Old style hook:
27 $ct_name = shift @ARGV;
28 $section = shift @ARGV;
29 $type = shift @ARGV;
30
31 if (!defined($ct_name) || !defined($section) || !defined($type)) {
32 die "missing hook parameters, expected to be called by lxc as hook\n";
33 }
34
35 if ($ct_name !~ /^\d+$/ || $section ne $expected_section || $type ne $expected_type) {
36 return;
37 }
38
39 if ($type eq 'stop') {
40 foreach my $ns (@ARGV) {
41 if ($ns =~ /^([^:]+):(.+)$/) {
42 $namespaces->{$1} = $2;
43 } else {
44 die "unrecognized 'stop' hook parameter: $ns\n";
45 }
46 }
47 } elsif ($type eq 'clone') {
48 $args = [@ARGV];
49 }
50 } elsif ($version eq '1') {
51 $ct_name = $ENV{LXC_NAME}
52 or die "missing LXC_NAME environment variable\n";
53 $section = $ENV{LXC_HOOK_SECTION}
54 or die "missing LXC_HOOK_SECTION environment variable\n";
55 $type = $ENV{LXC_HOOK_TYPE}
56 or die "missing LXC_HOOK_TYPE environment variable\n";
57
58 if ($ct_name !~ /^\d+$/ || $section ne $expected_section || $type ne $expected_type) {
59 return;
60 }
61
62 foreach my $var (keys %$ENV) {
63 if ($var =~ /^LXC_([A-Z]+)_NS$/) {
64 $namespaces->{lc($1)} = $ENV{$1};
65 }
66 }
67 } else {
68 die "lxc.hook.version $version not supported!\n";
69 }
70
71 my $logid = $ENV{PVE_LOG_ID} || "pve-lxc-hook-$section-$type";
72 initlog($logid);
73
74 my $common_vars = {
75 ROOTFS_MOUNT => ($ENV{LXC_ROOTFS_MOUNT} or die "missing LXC_ROOTFS_MOUNT env var\n"),
76 ROOTFS_PATH => ($ENV{LXC_ROOTFS_PATH} or die "missing LXC_ROOTFS_PATH env var\n"),
77 CONFIG_FILE => ($ENV{LXC_CONFIG_FILE} or die "missing LXC_CONFIG_FILE env var\n"),
78 };
79 if (defined(my $target = $ENV{LXC_TARGET})) {
80 $common_vars->{TARGET} = $target;
81 }
82
83 $code->($ct_name, $common_vars, $namespaces, $args);
84}
85
39f13cd7
WB
86sub for_current_devices($&) {
87 my ($vmid, $code) = @_;
88
89 my $devlist_file = "/var/lib/lxc/$vmid/devices";
90 my $fd;
91
92 if (! open $fd, '<', $devlist_file) {
93 exit 0 if $!{ENOENT}; # If the list is empty the file might not exist.
94 die "failed to open device list: $!\n";
95 }
96
97 while (defined(my $line = <$fd>)) {
98 if ($line !~ m@^(b):(\d+):(\d+):/dev/(\S+)\s*$@) {
99 warn "invalid .pve-devices entry: $line\n";
100 next;
101 }
102
103 my ($type, $major, $minor, $dev) = ($1, $2, $3, $4);
104
105 # Don't break out of $root/dev/
106 if ($dev =~ /\.\./) {
107 warn "skipping illegal device node entry: $dev\n";
108 next;
109 }
110
111 # Never expose /dev/loop-control
112 if ($major == 10 && $minor == 237) {
113 warn "skipping illegal device entry (loop-control) for: $dev\n";
114 next;
115 }
116
117 $code->($type, $major, $minor, $dev);
118 }
119 close $fd;
120}
121
122sub cgroup_do_write($$) {
123 my ($path, $value) = @_;
124 my $fd;
125 if (!open($fd, '>', $path)) {
126 warn "failed to open cgroup file $path: $!\n";
127 return 0;
128 }
129 if (!defined syswrite($fd, $value)) {
130 warn "failed to write value $value to cgroup file $path: $!\n";
131 return 0;
132 }
133 close($fd);
134 return 1;
135}
136
f4c0ba30
WB
137# Check whether the kernel supports the new mount api. This is used in the pre-start hook and in
138# the hotplugging code.
139my $cached_can_use_new_mount_api = undef;
140sub can_use_new_mount_api() {
141 if (!defined($cached_can_use_new_mount_api)) {
142 if (PVE::Tools::move_mount(-1, 0, -1, 0, 0)) {
143 # This should not be possible...
144 die "kernel behaved unexpectedly: move_mount(-1, NULL, -1, NULL) did not fail!\n";
145 }
146 # On older kernels the syscall doesn't exist and we get ENOSYS. (For newer kernels this call
147 # will fail with EFAULT instead, since we pass in a NULL pointer as file system name.)
148 $cached_can_use_new_mount_api = ($! != ENOSYS);
149 }
150 return $cached_can_use_new_mount_api;
151}
152
d92d0afd 1531;