]> git.proxmox.com Git - pve-container.git/blob - src/PVE/LXCSetup/Base.pm
751cf86a16bbc1d2bb0acda99cc336ff0cd9a6cf
[pve-container.git] / src / PVE / LXCSetup / Base.pm
1 package PVE::LXCSetup::Base;
2
3 use strict;
4 use warnings;
5
6 use File::stat;
7 use Digest::SHA;
8 use IO::File;
9 use Encode;
10
11 use PVE::INotify;
12 use PVE::Tools;
13
14 sub new {
15 my ($class, $conf) = @_;
16
17 return bless { conf => $conf }, $class;
18 }
19
20 my $lookup_dns_conf = sub {
21 my ($conf) = @_;
22
23 my $nameserver = $conf->{'pve.nameserver'};
24 my $searchdomains = $conf->{'pve.searchdomain'};
25
26 if (!($nameserver && $searchdomains)) {
27
28 if ($conf->{'pve.test_mode'}) {
29
30 $nameserver = "8.8.8.8 8.8.8.9";
31 $searchdomains = "promxox.com";
32
33 } else {
34
35 my $host_resolv_conf = PVE::INotify::read_file('resolvconf');
36
37 $searchdomains = $host_resolv_conf->{search};
38
39 my @list = ();
40 foreach my $k ("dns1", "dns2", "dns3") {
41 if (my $ns = $host_resolv_conf->{$k}) {
42 push @list, $ns;
43 }
44 }
45 $nameserver = join(' ', @list);
46 }
47 }
48
49 return ($searchdomains, $nameserver);
50 };
51
52 my $update_etc_hosts = sub {
53 my ($etc_hosts_data, $hostip, $oldname, $newname, $searchdomains) = @_;
54
55 my $done = 0;
56
57 my @lines;
58
59 my $extra_names = '';
60 foreach my $domain (PVE::Tools::split_list($searchdomains)) {
61 $extra_names .= ' ' if $extra_names;
62 $extra_names .= "$newname.$domain";
63 }
64
65 foreach my $line (split(/\n/, $etc_hosts_data)) {
66 if ($line =~ m/^#/ || $line =~ m/^\s*$/) {
67 push @lines, $line;
68 next;
69 }
70
71 my ($ip, @names) = split(/\s+/, $line);
72 if (($ip eq '127.0.0.1') || ($ip eq '::1')) {
73 push @lines, $line;
74 next;
75 }
76
77 my $found = 0;
78 foreach my $name (@names) {
79 if ($name eq $oldname || $name eq $newname) {
80 $found = 1;
81 } else {
82 # fixme: record extra names?
83 }
84 }
85 $found = 1 if defined($hostip) && ($ip eq $hostip);
86
87 if ($found) {
88 if (!$done) {
89 if (defined($hostip)) {
90 push @lines, "$hostip $extra_names $newname";
91 } else {
92 push @lines, "127.0.1.1 $newname";
93 }
94 $done = 1;
95 }
96 next;
97 } else {
98 push @lines, $line;
99 }
100 }
101
102 if (!$done) {
103 if (defined($hostip)) {
104 push @lines, "$hostip $extra_names $newname";
105 } else {
106 push @lines, "127.0.1.1 $newname";
107 }
108 }
109
110 my $found_localhost = 0;
111 foreach my $line (@lines) {
112 if ($line =~ m/^127.0.0.1\s/) {
113 $found_localhost = 1;
114 last;
115 }
116 }
117
118 if (!$found_localhost) {
119 unshift @lines, "127.0.0.1 localhost.localnet localhost";
120 }
121
122 $etc_hosts_data = join("\n", @lines) . "\n";
123
124 return $etc_hosts_data;
125 };
126
127 sub set_dns {
128 my ($self, $conf) = @_;
129
130 my ($searchdomains, $nameserver) = &$lookup_dns_conf($conf);
131
132 my $rootfs = $conf->{'lxc.rootfs'};
133
134 my $filename = "$rootfs/etc/resolv.conf";
135
136 my $data = '';
137
138 $data .= "search " . join(' ', PVE::Tools::split_list($searchdomains)) . "\n"
139 if $searchdomains;
140
141 foreach my $ns ( PVE::Tools::split_list($nameserver)) {
142 $data .= "nameserver $ns\n";
143 }
144
145 PVE::Tools::file_set_contents($filename, $data);
146 }
147
148 sub set_hostname {
149 my ($self, $conf) = @_;
150
151 my $hostname = $conf->{'lxc.utsname'} || 'localhost';
152
153 $hostname =~ s/\..*$//;
154
155 my $rootfs = $conf->{'lxc.rootfs'};
156
157 my $hostname_fn = "$rootfs/etc/hostname";
158
159 my $oldname = PVE::Tools::file_read_firstline($hostname_fn) || 'localhost';
160
161 my $hosts_fn = "$rootfs/etc/hosts";
162 my $etc_hosts_data = '';
163
164 if (-f $hosts_fn) {
165 $etc_hosts_data = PVE::Tools::file_get_contents($hosts_fn);
166 }
167
168 my ($ipv4, $ipv6) = PVE::LXC::get_primary_ips($conf);
169 my $hostip = $ipv4 || $ipv6;
170
171 my ($searchdomains) = &$lookup_dns_conf($conf);
172
173 $etc_hosts_data = &$update_etc_hosts($etc_hosts_data, $hostip, $oldname,
174 $hostname, $searchdomains);
175
176 PVE::Tools::file_set_contents($hostname_fn, "$hostname\n");
177 PVE::Tools::file_set_contents($hosts_fn, $etc_hosts_data);
178 }
179
180 sub setup_network {
181 my ($self, $conf) = @_;
182
183 die "please implement this inside subclass"
184 }
185
186 sub setup_init {
187 my ($self, $conf) = @_;
188
189 die "please implement this inside subclass"
190 }
191
192 my $replacepw = sub {
193 my ($file, $user, $epw) = @_;
194
195 my $tmpfile = "$file.$$";
196
197 eval {
198 my $src = IO::File->new("<$file") ||
199 die "unable to open file '$file' - $!";
200
201 my $st = File::stat::stat($src) ||
202 die "unable to stat file - $!";
203
204 my $dst = IO::File->new(">$tmpfile") ||
205 die "unable to open file '$tmpfile' - $!";
206
207 # copy owner and permissions
208 chmod $st->mode, $dst;
209 chown $st->uid, $st->gid, $dst;
210
211 while (defined (my $line = <$src>)) {
212 $line =~ s/^${user}:[^:]*:/${user}:${epw}:/;
213 print $dst $line;
214 }
215
216 $src->close() || die "close '$file' failed - $!\n";
217 $dst->close() || die "close '$tmpfile' failed - $!\n";
218 };
219 if (my $err = $@) {
220 unlink $tmpfile;
221 } else {
222 rename $tmpfile, $file;
223 unlink $tmpfile; # in case rename fails
224 }
225 };
226
227 sub set_user_password {
228 my ($self, $conf, $user, $opt_password) = @_;
229
230 my $rootfs = $conf->{'lxc.rootfs'};
231
232 my $pwfile = "$rootfs/etc/passwd";
233
234 return if ! -f $pwfile;
235
236 my $shadow = "$rootfs/etc/shadow";
237
238 if (defined($opt_password)) {
239 if ($opt_password !~ m/^\$/) {
240 my $time = substr (Digest::SHA::sha1_base64 (time), 0, 8);
241 $opt_password = crypt(encode("utf8", $opt_password), "\$1\$$time\$");
242 };
243 } else {
244 $opt_password = '*';
245 }
246
247 if (-f $shadow) {
248 &$replacepw ($shadow, $user, $opt_password);
249 &$replacepw ($pwfile, $user, 'x');
250 } else {
251 &$replacepw ($pwfile, $user, $opt_password);
252 }
253 }
254
255 sub pre_start_hook {
256 my ($self, $conf) = @_;
257
258 $self->setup_init($conf);
259 $self->setup_network($conf);
260 $self->set_hostname($conf);
261 $self->set_dns($conf);
262
263 # fixme: what else ?
264 }
265
266 sub post_create_hook {
267 my ($self, $conf, $root_password) = @_;
268
269 $self->set_user_password($conf, 'root', $root_password);
270 $self->setup_init($conf);
271 $self->setup_network($conf);
272 $self->set_hostname($conf);
273 $self->set_dns($conf);
274
275 # fixme: what else ?
276 }
277
278 1;