]>
Commit | Line | Data |
---|---|---|
7af97ad5 | 1 | package PVE::LXC::Create; |
5b4657d0 DM |
2 | |
3 | use strict; | |
4 | use warnings; | |
5 | use File::Basename; | |
6 | use File::Path; | |
7 | use Data::Dumper; | |
07084526 | 8 | use Fcntl; |
5b4657d0 DM |
9 | |
10 | use PVE::Storage; | |
11 | use PVE::LXC; | |
7af97ad5 | 12 | use PVE::LXC::Setup; |
f507c3a7 | 13 | use PVE::VZDump::ConvertOVZ; |
580b6916 | 14 | use PVE::Tools; |
dc6e862c | 15 | use POSIX; |
5b4657d0 | 16 | |
fbb31447 TL |
17 | sub detect_architecture { |
18 | my ($rootdir) = @_; | |
dc6e862c | 19 | |
fbb31447 TL |
20 | my $supported_elf_class = { |
21 | 1 => 'i386', | |
22 | 2 => 'amd64', | |
23 | }; | |
dc6e862c | 24 | |
fbb31447 TL |
25 | my $elf_fn = '/bin/sh'; # '/bin/sh' is POSIX mandatory |
26 | my $detect_arch = sub { | |
dc6e862c TL |
27 | # chroot avoids a problem where we check the binary of the host system |
28 | # if $elf_fn is an absolut symlink (e.g. $rootdir/bin/sh -> /bin/bash) | |
29 | chroot($rootdir) or die "chroot '$rootdir' failed: $!\n"; | |
30 | chdir('/') or die "failed to change to root directory\n"; | |
31 | ||
fbb31447 | 32 | open(my $fh, "<", $elf_fn) or die "open '$elf_fn' failed: $!\n"; |
dc6e862c TL |
33 | binmode($fh); |
34 | ||
fbb31447 | 35 | my $length = read($fh, my $data, 5) or die "read failed: $!\n"; |
dc6e862c TL |
36 | |
37 | # 4 bytes ELF magic number and 1 byte ELF class | |
38 | my ($magic, $class) = unpack("A4C", $data); | |
39 | ||
40 | die "'$elf_fn' does not resolve to an ELF!\n" | |
41 | if (!defined($class) || !defined($magic) || $magic ne "\177ELF"); | |
42 | ||
43 | die "'$elf_fn' has unknown ELF class '$class'!\n" | |
fbb31447 | 44 | if !defined($supported_elf_class->{$class}); |
dc6e862c | 45 | |
fbb31447 TL |
46 | return $supported_elf_class->{$class}; |
47 | }; | |
dc6e862c | 48 | |
fbb31447 TL |
49 | my $arch = eval { PVE::Tools::run_fork_with_timeout(5, $detect_arch) }; |
50 | if (my $err = $@) { | |
51 | $arch = 'amd64'; | |
52 | print "Architecture detection failed: $err\nFalling back to amd64.\n" . | |
53 | "Use `pct set VMID --arch ARCH` to change.\n"; | |
dc6e862c | 54 | } else { |
fbb31447 | 55 | print "Detected container architecture: $arch\n"; |
dc6e862c TL |
56 | } |
57 | ||
fbb31447 | 58 | return $arch; |
dc6e862c TL |
59 | } |
60 | ||
5b4657d0 | 61 | sub restore_archive { |
9c23d567 | 62 | my ($archive, $rootdir, $conf, $no_unpack_error) = @_; |
5b4657d0 | 63 | |
c6a605f9 | 64 | my ($id_map, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf); |
01dce99b | 65 | my $userns_cmd = PVE::LXC::userns_command($id_map); |
5b4657d0 | 66 | |
07084526 | 67 | my $archive_fh; |
f70a3d47 WB |
68 | my $tar_input = '<&STDIN'; |
69 | my @compression_opt; | |
07084526 | 70 | if ($archive ne '-') { |
f70a3d47 WB |
71 | # GNU tar refuses to autodetect this... *sigh* |
72 | my %compression_map = ( | |
73 | '.gz' => '-z', | |
74 | '.bz2' => '-j', | |
75 | '.xz' => '-J', | |
76 | ); | |
77 | if ($archive =~ /\.tar(\.[^.]+)?$/) { | |
78 | if (defined($1)) { | |
79 | @compression_opt = $compression_map{$1} | |
80 | or die "unrecognized compression format: $1\n"; | |
81 | } | |
82 | } else { | |
83 | die "file does not look like a template archive: $archive\n"; | |
84 | } | |
07084526 WB |
85 | sysopen($archive_fh, $archive, O_RDONLY) |
86 | or die "failed to open '$archive': $!\n"; | |
07084526 WB |
87 | my $flags = $archive_fh->fcntl(Fcntl::F_GETFD(), 0); |
88 | $archive_fh->fcntl(Fcntl::F_SETFD(), $flags & ~(Fcntl::FD_CLOEXEC())); | |
f70a3d47 | 89 | $tar_input = '<&'.fileno($archive_fh); |
07084526 WB |
90 | } |
91 | ||
f70a3d47 | 92 | my $cmd = [@$userns_cmd, 'tar', 'xpf', '-', @compression_opt, '--totals', |
5fa038ab | 93 | @PVE::Storage::Plugin::COMMON_TAR_FLAGS, |
fc4e132e | 94 | '-C', $rootdir]; |
5b4657d0 | 95 | |
112aeeb4 WB |
96 | # skip-old-files doesn't have anything to do with time (old/new), but is |
97 | # simply -k (annoyingly also called --keep-old-files) without the 'treat | |
98 | # existing files as errors' part... iow. it's bsdtar's interpretation of -k | |
99 | # *sigh*, gnu... | |
100 | push @$cmd, '--skip-old-files'; | |
5b4657d0 DM |
101 | push @$cmd, '--anchored'; |
102 | push @$cmd, '--exclude' , './dev/*'; | |
103 | ||
6034ae50 DM |
104 | if ($archive eq '-') { |
105 | print "extracting archive from STDIN\n"; | |
27916659 | 106 | } else { |
6034ae50 | 107 | print "extracting archive '$archive'\n"; |
27916659 | 108 | } |
f70a3d47 | 109 | eval { PVE::Tools::run_command($cmd, input => $tar_input); }; |
07084526 WB |
110 | my $err = $@; |
111 | close($archive_fh) if defined $archive_fh; | |
112 | die $err if $err && !$no_unpack_error; | |
f31bd6ae DC |
113 | |
114 | # if arch is set, we do not try to autodetect it | |
115 | return if defined($conf->{arch}); | |
116 | ||
fbb31447 | 117 | $conf->{arch} = detect_architecture($rootdir); |
5b4657d0 DM |
118 | } |
119 | ||
f507c3a7 | 120 | sub recover_config { |
effa4f43 | 121 | my ($archive) = @_; |
f507c3a7 | 122 | |
0550795a | 123 | my ($raw, $conf_file) = PVE::Storage::extract_vzdump_config_tar($archive, qr!(\./etc/vzdump/(pct|vps)\.conf)$!); |
effa4f43 | 124 | my $conf; |
db18c1e4 | 125 | my $mp_param = {}; |
f507c3a7 | 126 | |
27916659 | 127 | if ($conf_file =~ m/pct\.conf/) { |
f507c3a7 | 128 | |
1b4cf758 | 129 | $conf = PVE::LXC::Config::parse_pct_config("/lxc/0.conf" , $raw); |
f507c3a7 | 130 | |
27916659 | 131 | delete $conf->{snapshots}; |
bb1ac2de | 132 | delete $conf->{template}; # restored CT is never a template |
db18c1e4 FG |
133 | |
134 | PVE::LXC::Config->foreach_mountpoint($conf, sub { | |
135 | my ($ms, $mountpoint) = @_; | |
136 | $mp_param->{$ms} = $conf->{$ms}; | |
137 | }); | |
138 | ||
effa4f43 | 139 | } elsif ($conf_file =~ m/vps\.conf/) { |
db18c1e4 FG |
140 | |
141 | ($conf, $mp_param) = PVE::VZDump::ConvertOVZ::convert_ovz($raw); | |
142 | ||
effa4f43 DM |
143 | } else { |
144 | ||
27916659 | 145 | die "internal error"; |
f507c3a7 WL |
146 | } |
147 | ||
db18c1e4 | 148 | return wantarray ? ($conf, $mp_param) : $conf; |
f507c3a7 WL |
149 | } |
150 | ||
51665c2d | 151 | sub restore_configuration { |
ba0e2930 | 152 | my ($vmid, $rootdir, $conf, $restricted) = @_; |
51665c2d FG |
153 | |
154 | # restore: try to extract configuration from archive | |
155 | ||
156 | my $pct_cfg_fn = "$rootdir/etc/vzdump/pct.conf"; | |
157 | my $pct_fwcfg_fn = "$rootdir/etc/vzdump/pct.fw"; | |
158 | my $ovz_cfg_fn = "$rootdir/etc/vzdump/vps.conf"; | |
159 | if (-f $pct_cfg_fn) { | |
160 | my $raw = PVE::Tools::file_get_contents($pct_cfg_fn); | |
161 | my $oldconf = PVE::LXC::Config::parse_pct_config("/lxc/$vmid.conf", $raw); | |
162 | ||
163 | foreach my $key (keys %$oldconf) { | |
adcaa73e | 164 | next if $key eq 'digest' || $key eq 'rootfs' || $key eq 'snapshots' || $key eq 'unprivileged' || $key eq 'parent'; |
51665c2d FG |
165 | next if $key =~ /^mp\d+$/; # don't recover mountpoints |
166 | next if $key =~ /^unused\d+$/; # don't recover unused disks | |
ba0e2930 FG |
167 | if ($restricted && $key eq 'lxc') { |
168 | warn "skipping custom lxc options, restore manually as root:\n"; | |
169 | warn "--------------------------------\n"; | |
170 | my $lxc_list = $oldconf->{'lxc'}; | |
171 | foreach my $lxc_opt (@$lxc_list) { | |
172 | warn "$lxc_opt->[0]: $lxc_opt->[1]\n" | |
173 | } | |
174 | warn "--------------------------------\n"; | |
175 | next; | |
176 | } | |
51665c2d FG |
177 | $conf->{$key} = $oldconf->{$key} if !defined($conf->{$key}); |
178 | } | |
179 | unlink($pct_cfg_fn); | |
5b4657d0 | 180 | |
51665c2d FG |
181 | if (-f $pct_fwcfg_fn) { |
182 | my $pve_firewall_dir = '/etc/pve/firewall'; | |
183 | mkdir $pve_firewall_dir; # make sure the directory exists | |
184 | PVE::Tools::file_copy($pct_fwcfg_fn, "${pve_firewall_dir}/$vmid.fw"); | |
185 | unlink $pct_fwcfg_fn; | |
27916659 | 186 | } |
5b4657d0 | 187 | |
51665c2d FG |
188 | } elsif (-f $ovz_cfg_fn) { |
189 | print "###########################################################\n"; | |
190 | print "Converting OpenVZ configuration to LXC.\n"; | |
191 | print "Please check the configuration and reconfigure the network.\n"; | |
192 | print "###########################################################\n"; | |
148d1cb4 | 193 | |
51665c2d FG |
194 | my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir); # detect OS |
195 | $conf->{ostype} = $lxc_setup->{conf}->{ostype}; | |
196 | my $raw = PVE::Tools::file_get_contents($ovz_cfg_fn); | |
197 | my $oldconf = PVE::VZDump::ConvertOVZ::convert_ovz($raw); | |
198 | foreach my $key (keys %$oldconf) { | |
199 | $conf->{$key} = $oldconf->{$key} if !defined($conf->{$key}); | |
200 | } | |
201 | unlink($ovz_cfg_fn); | |
148d1cb4 | 202 | |
51665c2d FG |
203 | } else { |
204 | print "###########################################################\n"; | |
205 | print "Backup archive does not contain any configuration\n"; | |
206 | print "###########################################################\n"; | |
148d1cb4 | 207 | } |
5b4657d0 DM |
208 | } |
209 | ||
210 | 1; |