]> git.proxmox.com Git - pve-container.git/blame - src/PVE/LXC/Setup/Debian.pm
setup: more general approach to tty paths
[pve-container.git] / src / PVE / LXC / Setup / Debian.pm
CommitLineData
7af97ad5 1package PVE::LXC::Setup::Debian;
1c7f4f65
DM
2
3use strict;
4use warnings;
55fa4e09 5use Data::Dumper;
a1b1a247 6use PVE::Tools qw($IPV6RE);
55fa4e09 7use PVE::LXC;
b206c519 8use PVE::Network;
55fa4e09 9use File::Path;
1c7f4f65 10
7af97ad5 11use PVE::LXC::Setup::Base;
1c7f4f65 12
7af97ad5 13use base qw(PVE::LXC::Setup::Base);
1c7f4f65 14
633a7bd8 15sub new {
5b4657d0 16 my ($class, $conf, $rootdir) = @_;
633a7bd8 17
5b4657d0 18 my $version = PVE::Tools::file_read_firstline("$rootdir/etc/debian_version");
633a7bd8
DM
19
20 die "unable to read version info\n" if !defined($version);
adc8f577 21
9a3bd509
DM
22 # translate stretch/sid => 9.0 (used on debian testing repository)
23 $version = 9.0 if $version eq 'stretch/sid';
24
25 die "unable to parse version info '$version'\n"
adc8f577
DM
26 if $version !~ m/^(\d+(\.\d+)?)(\.\d+)?/;
27
28 $version = $1;
29
1c0a5ac7 30 die "unsupported debian version '$version'\n"
9a3bd509 31 if !($version >= 4 && $version <= 9);
633a7bd8 32
5b4657d0
DM
33 my $self = { conf => $conf, rootdir => $rootdir, version => $version };
34
27916659 35 $conf->{ostype} = "debian";
633a7bd8
DM
36
37 return bless $self, $class;
38}
39
d66768a2 40sub setup_init {
633a7bd8 41 my ($self, $conf) = @_;
d66768a2 42
b6876f11
DM
43 my $systemd = $self->ct_readlink('/sbin/init');
44 if (defined($systemd) && $systemd =~ m@/systemd$@) {
5e84bdc8 45 $self->setup_container_getty_service($conf);
b6876f11
DM
46 }
47
2063d380 48 my $filename = "/etc/inittab";
fe8a16b8 49 return if !$self->ct_file_exists($filename);
d66768a2 50
1b4cf758 51 my $ttycount = PVE::LXC::Config->get_tty_count($conf);
fe8a16b8
WB
52 my $inittab = $self->ct_file_get_contents($filename);
53
54 my @lines = grep {
55 # remove getty lines
f4cd6178 56 !/^\s*\d+:\d*:[^:]*:.*getty/ &&
fe8a16b8
WB
57 # remove power lines
58 !/^\s*p[fno0]:/
59 } split(/\n/, $inittab);
60
61 $inittab = join("\n", @lines) . "\n";
62
63 $inittab .= "p0::powerfail:/sbin/init 0\n";
64
65 my $version = $self->{version};
39630c0d
DM
66 for (my $id = 1; $id <= $ttycount; $id++) {
67 next if $id == 7; # reserved for X11
68 my $levels = ($id == 1) ? '2345' : '23';
fe8a16b8
WB
69 if ($version < 7) {
70 $inittab .= "$id:$levels:respawn:/sbin/getty -L 38400 tty$id\n";
71 } else {
72 $inittab .= "$id:$levels:respawn:/sbin/getty --noclear 38400 tty$id\n";
d66768a2 73 }
d66768a2 74 }
fe8a16b8
WB
75
76 $self->ct_file_set_contents($filename, $inittab);
d66768a2
DM
77}
78
b206c519
WB
79sub remove_gateway_scripts {
80 my ($attr) = @_;
81 my $length = scalar(@$attr);
a5d9039e
WB
82
83 my $found_section = 0;
84 my $keep = 1;
85 @$attr = grep {
86 if ($_ eq '# --- BEGIN PVE ---') {
87 $found_section = 1;
88 $keep = 0;
89 0; # remove this line
90 } elsif ($_ eq '# --- END PVE ---') {
91 $found_section = 1;
92 $keep = 1;
93 0; # remove this line
94 } else {
95 $keep;
b206c519 96 }
a5d9039e
WB
97 } @$attr;
98
99 return if $found_section;
100 # XXX: To deal with existing setups we perform two types of removal for
101 # now. Newly started containers have their routing sections marked with
102 # begin/end comments. For older containers we perform a strict matching on
103 # the routing rules we added. We can probably remove this part at some point
104 # when it is unlikely that old debian setups are still around.
105
106 for (my $i = 0; $i < $length-3; ++$i) {
107 next if $attr->[$i+0] !~ m@^\s*post-up\s+ip\s+route\s+add\s+(\S+)\s+dev\s+(\S+)$@;
108 my ($ip, $dev) = ($1, $2);
109 if ($attr->[$i+1] =~ m@^\s*post-up\s+ip\s+route\s+add\s+default\s+via\s+(\S+)\s+dev\s+(\S+)$@ &&
110 ($ip eq $1 && $dev eq $2) &&
111 $attr->[$i+2] =~ m@^\s*pre-down\s+ip\s+route\s+del\s+default\s+via\s+(\S+)\s+dev\s+(\S+)$@ &&
112 ($ip eq $1 && $dev eq $2) &&
113 $attr->[$i+3] =~ m@^\s*pre-down\s+ip\s+route\s+del\s+(\S+)\s+dev\s+(\S+)$@ &&
114 ($ip eq $1 && $dev eq $2))
115 {
116 splice @$attr, $i, 4;
117 $length -= 4;
b206c519 118 --$i;
b206c519
WB
119 }
120 }
121}
122
123sub make_gateway_scripts {
124 my ($ifname, $gw) = @_;
125 return <<"SCRIPTS";
a5d9039e 126# --- BEGIN PVE ---
b206c519 127\tpost-up ip route add $gw dev $ifname
d54c0145
WB
128\tpost-up ip route add default via $gw dev $ifname
129\tpre-down ip route del default via $gw dev $ifname
b206c519 130\tpre-down ip route del $gw dev $ifname
a5d9039e 131# --- END PVE ---
b206c519
WB
132SCRIPTS
133}
134
55fa4e09 135sub setup_network {
633a7bd8 136 my ($self, $conf) = @_;
55fa4e09 137
55fa4e09
DM
138 my $networks = {};
139 foreach my $k (keys %$conf) {
140 next if $k !~ m/^net(\d+)$/;
93285df8 141 my $ind = $1;
1b4cf758 142 my $d = PVE::LXC::Config->parse_lxc_network($conf->{$k});
55fa4e09
DM
143 if ($d->{name}) {
144 my $net = {};
b206c519 145 my $cidr;
93285df8 146 if (defined($d->{ip})) {
ecf84da0 147 if ($d->{ip} =~ /^(?:dhcp|manual)$/) {
0178cffa
WB
148 $net->{address} = $d->{ip};
149 } else {
150 my $ipinfo = PVE::LXC::parse_ipv4_cidr($d->{ip});
151 $net->{address} = $ipinfo->{address};
152 $net->{netmask} = $ipinfo->{netmask};
b206c519 153 $cidr = $d->{ip};
0178cffa 154 }
55fa4e09 155 }
93285df8 156 if (defined($d->{'gw'})) {
a1b1a247 157 $net->{gateway} = $d->{'gw'};
b206c519
WB
158 if (defined($cidr) && !PVE::Network::is_ip_in_cidr($d->{gw}, $cidr, 4)) {
159 # gateway is not reachable, need an extra route
160 $net->{needsroute} = 1;
161 }
55fa4e09 162 }
b206c519 163 $cidr = undef;
93285df8 164 if (defined($d->{ip6})) {
c6b8740b 165 if ($d->{ip6} =~ /^(?:auto|dhcp|manual)$/) {
0178cffa
WB
166 $net->{address6} = $d->{ip6};
167 } elsif ($d->{ip6} !~ /^($IPV6RE)\/(\d+)$/) {
a1b1a247 168 die "unable to parse ipv6 address/prefix\n";
0178cffa
WB
169 } else {
170 $net->{address6} = $1;
171 $net->{netmask6} = $2;
b206c519 172 $cidr = $d->{ip6};
a1b1a247 173 }
a1b1a247
WB
174 }
175 if (defined($d->{'gw6'})) {
176 $net->{gateway6} = $d->{'gw6'};
d13fd23a
WB
177 if (defined($cidr) && !PVE::Network::is_ip_in_cidr($d->{gw6}, $cidr, 6) &&
178 !PVE::Network::is_ip_in_cidr($d->{gw6}, 'fe80::/10', 6)) {
b206c519
WB
179 # gateway is not reachable, need an extra route
180 $net->{needsroute6} = 1;
181 }
55fa4e09 182 }
0178cffa 183 $networks->{$d->{name}} = $net if keys %$net;
55fa4e09
DM
184 }
185 }
186
f44d23a5 187 return if !scalar(keys %$networks);
55fa4e09 188
2063d380 189 my $filename = "/etc/network/interfaces";
55fa4e09
DM
190 my $interfaces = "";
191
192 my $section;
193
56be201e 194 my $done_auto = {};
55fa4e09
DM
195 my $done_v4_hash = {};
196 my $done_v6_hash = {};
1c0a5ac7 197
55fa4e09 198 my $print_section = sub {
55fa4e09
DM
199 return if !$section;
200
6fd46881
WB
201 my $ifname = $section->{ifname};
202 my $net = $networks->{$ifname};
55fa4e09 203
6b0a0043 204 if (!$done_auto->{$ifname}) {
6fd46881
WB
205 $interfaces .= "auto $ifname\n";
206 $done_auto->{$ifname} = 1;
56be201e
WB
207 }
208
55fa4e09 209 if ($section->{type} eq 'ipv4') {
6fd46881 210 $done_v4_hash->{$ifname} = 1;
55fa4e09 211
b06e65b1
WB
212 if (!defined($net->{address})) {
213 # no address => no iface line
214 } elsif ($net->{address} =~ /^(dhcp|manual)$/) {
215 $interfaces .= "iface $ifname inet $1\n\n";
fcaca113 216 } else {
6fd46881 217 $interfaces .= "iface $ifname inet static\n";
a1b1a247
WB
218 $interfaces .= "\taddress $net->{address}\n" if defined($net->{address});
219 $interfaces .= "\tnetmask $net->{netmask}\n" if defined($net->{netmask});
46cf092c
FG
220
221 remove_gateway_scripts($section->{attr});
b206c519 222 if (defined(my $gw = $net->{gateway})) {
b206c519
WB
223 if ($net->{needsroute}) {
224 $interfaces .= make_gateway_scripts($ifname, $gw);
225 } else {
226 $interfaces .= "\tgateway $gw\n";
227 }
228 }
55fa4e09
DM
229 foreach my $attr (@{$section->{attr}}) {
230 $interfaces .= "\t$attr\n";
231 }
b06e65b1 232 $interfaces .= "\n";
55fa4e09 233 }
55fa4e09 234 } elsif ($section->{type} eq 'ipv6') {
6fd46881 235 $done_v6_hash->{$ifname} = 1;
1c0a5ac7 236
b06e65b1
WB
237 if (!defined($net->{address6})) {
238 # no address => no iface line
239 } elsif ($net->{address6} =~ /^(auto|dhcp|manual)$/) {
240 $interfaces .= "iface $ifname inet6 $1\n\n";
fcaca113 241 } else {
6fd46881 242 $interfaces .= "iface $ifname inet6 static\n";
a1b1a247
WB
243 $interfaces .= "\taddress $net->{address6}\n" if defined($net->{address6});
244 $interfaces .= "\tnetmask $net->{netmask6}\n" if defined($net->{netmask6});
46cf092c 245 remove_gateway_scripts($section->{attr});
b206c519 246 if (defined(my $gw = $net->{gateway6})) {
b206c519
WB
247 if ($net->{needsroute6}) {
248 $interfaces .= make_gateway_scripts($ifname, $gw);
249 } else {
250 $interfaces .= "\tgateway $net->{gateway6}\n" if defined($net->{gateway6});
251 }
252 }
55fa4e09
DM
253 foreach my $attr (@{$section->{attr}}) {
254 $interfaces .= "\t$attr\n";
255 }
b06e65b1 256 $interfaces .= "\n";
55fa4e09 257 }
55fa4e09
DM
258 } else {
259 die "unknown section type '$section->{type}'";
260 }
261
262 $section = undef;
263 };
1c0a5ac7 264
f08b2779 265 if (my $fh = $self->ct_open_file_read($filename)) {
55fa4e09
DM
266 while (defined (my $line = <$fh>)) {
267 chomp $line;
a5d9039e
WB
268 if ($line =~ m/^# --- (?:BEGIN|END) PVE ---/) {
269 # Include markers in the attribute section so
270 # remove_gateway_scripts() can find them.
271 push @{$section->{attr}}, $line if $section;
272 next;
273 }
55fa4e09
DM
274 if ($line =~ m/^#/) {
275 $interfaces .= "$line\n";
276 next;
277 }
278 if ($line =~ m/^\s*$/) {
279 if ($section) {
280 &$print_section();
281 } else {
282 $interfaces .= "$line\n";
283 }
284 next;
285 }
0178cffa 286 if ($line =~ m/^\s*iface\s+(\S+)\s+inet\s+(\S+)\s*$/) {
55fa4e09 287 my $ifname = $1;
0178cffa 288 &$print_section(); # print previous section
55fa4e09
DM
289 if (!$networks->{$ifname}) {
290 $interfaces .= "$line\n";
291 next;
292 }
293 $section = { type => 'ipv4', ifname => $ifname, attr => []};
294 next;
295 }
0178cffa 296 if ($line =~ m/^\s*iface\s+(\S+)\s+inet6\s+(\S+)\s*$/) {
55fa4e09 297 my $ifname = $1;
0178cffa 298 &$print_section(); # print previous section
55fa4e09
DM
299 if (!$networks->{$ifname}) {
300 $interfaces .= "$line\n";
301 next;
302 }
303 $section = { type => 'ipv6', ifname => $ifname, attr => []};
304 next;
305 }
56be201e
WB
306 # Handle 'auto'
307 if ($line =~ m/^\s*auto\s*(.*)$/) {
308 foreach my $iface (split(/\s+/, $1)) {
309 $done_auto->{$iface} = 1;
310 }
311 &$print_section();
312 $interfaces .= "$line\n";
313 next;
314 }
0178cffa
WB
315 # Handle other section delimiters:
316 if ($line =~ m/^\s*(?:mapping\s
0178cffa
WB
317 |allow-
318 |source\s
319 |source-directory\s
320 )/x) {
321 &$print_section();
322 $interfaces .= "$line\n";
323 next;
324 }
55fa4e09
DM
325 if ($section && $line =~ m/^\s*((\S+)\s(.*))$/) {
326 my ($adata, $aname) = ($1, $2);
327 if ($aname eq 'address' || $aname eq 'netmask' ||
328 $aname eq 'gateway' || $aname eq 'broadcast') {
329 # skip
330 } else {
1c0a5ac7 331 push @{$section->{attr}}, $adata;
55fa4e09
DM
332 }
333 next;
334 }
1c0a5ac7
WB
335
336 $interfaces .= "$line\n";
55fa4e09
DM
337 }
338 &$print_section();
55fa4e09
DM
339 }
340
e90673a8 341 my $need_separator = length($interfaces) && ($interfaces !~ /\n\n$/);
55fa4e09
DM
342 foreach my $ifname (sort keys %$networks) {
343 my $net = $networks->{$ifname};
1c0a5ac7 344
fcaca113 345 if (!$done_v4_hash->{$ifname} && defined($net->{address})) {
1c0a5ac7 346 if ($need_separator) { $interfaces .= "\n"; $need_separator = 0; };
55fa4e09 347 $section = { type => 'ipv4', ifname => $ifname, attr => []};
6b0a0043 348 &$print_section();
55fa4e09 349 }
a1b1a247 350 if (!$done_v6_hash->{$ifname} && defined($net->{address6})) {
1c0a5ac7 351 if ($need_separator) { $interfaces .= "\n"; $need_separator = 0; };
55fa4e09 352 $section = { type => 'ipv6', ifname => $ifname, attr => []};
6b0a0043 353 &$print_section();
55fa4e09
DM
354 }
355 }
1c0a5ac7 356
b1382595
TL
357 # older templates (< Debian 8) do not configure the loopback interface
358 # if not explicitly told to do so
359 if (!$done_auto->{lo}) {
360 $interfaces = "auto lo\niface lo inet loopback\n" .
361 "iface lo inet6 loopback\n\n" .
362 $interfaces;
363 }
364
2063d380 365 $self->ct_file_set_contents($filename, $interfaces);
55fa4e09 366}
1c7f4f65
DM
367
3681;