]>
Commit | Line | Data |
---|---|---|
7af97ad5 | 1 | package PVE::LXC::Setup::Debian; |
1c7f4f65 DM |
2 | |
3 | use strict; | |
4 | use warnings; | |
55fa4e09 | 5 | use Data::Dumper; |
a1b1a247 | 6 | use PVE::Tools qw($IPV6RE); |
55fa4e09 DM |
7 | use PVE::LXC; |
8 | use File::Path; | |
1c7f4f65 | 9 | |
7af97ad5 | 10 | use PVE::LXC::Setup::Base; |
1c7f4f65 | 11 | |
7af97ad5 | 12 | use base qw(PVE::LXC::Setup::Base); |
1c7f4f65 | 13 | |
633a7bd8 | 14 | sub new { |
5b4657d0 | 15 | my ($class, $conf, $rootdir) = @_; |
633a7bd8 | 16 | |
5b4657d0 | 17 | my $version = PVE::Tools::file_read_firstline("$rootdir/etc/debian_version"); |
633a7bd8 DM |
18 | |
19 | die "unable to read version info\n" if !defined($version); | |
adc8f577 DM |
20 | |
21 | die "unable to parse version info\n" | |
22 | if $version !~ m/^(\d+(\.\d+)?)(\.\d+)?/; | |
23 | ||
24 | $version = $1; | |
25 | ||
27916659 | 26 | die "unsupported debian version '$version'\n" |
4596e30d | 27 | if !($version >= 4 && $version < 9); |
633a7bd8 | 28 | |
5b4657d0 DM |
29 | my $self = { conf => $conf, rootdir => $rootdir, version => $version }; |
30 | ||
27916659 | 31 | $conf->{ostype} = "debian"; |
633a7bd8 DM |
32 | |
33 | return bless $self, $class; | |
34 | } | |
35 | ||
d66768a2 DM |
36 | my $default_inittab = <<__EOD__; |
37 | ||
38 | # The default runlevel. | |
39 | id:2:initdefault: | |
40 | ||
41 | # Boot-time system configuration/initialization script. | |
42 | # This is run first except when booting in emergency (-b) mode. | |
43 | si::sysinit:/etc/init.d/rcS | |
44 | ||
45 | # /etc/init.d executes the S and K scripts upon change | |
46 | # of runlevel. | |
47 | # | |
48 | # Runlevel 0 is halt. | |
49 | # Runlevel 1 is single-user. | |
50 | # Runlevels 2-5 are multi-user. | |
51 | # Runlevel 6 is reboot. | |
52 | ||
53 | l0:0:wait:/etc/init.d/rc 0 | |
54 | l1:1:wait:/etc/init.d/rc 1 | |
55 | l2:2:wait:/etc/init.d/rc 2 | |
56 | l3:3:wait:/etc/init.d/rc 3 | |
57 | l4:4:wait:/etc/init.d/rc 4 | |
58 | l5:5:wait:/etc/init.d/rc 5 | |
59 | l6:6:wait:/etc/init.d/rc 6 | |
60 | # Normally not reached, but fallthrough in case of emergency. | |
61 | z6:6:respawn:/sbin/sulogin | |
62 | ||
63 | # What to do when CTRL-ALT-DEL is pressed. | |
64 | ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now | |
65 | ||
66 | # What to do when the power fails/returns. | |
67 | p0::powerfail:/sbin/init 0 | |
68 | ||
69 | # /sbin/getty invocations for the runlevels. | |
70 | # | |
71 | # The "id" field MUST be the same as the last | |
72 | # characters of the device (after "tty"). | |
73 | # | |
74 | # Format: | |
75 | # <id>:<runlevels>:<action>:<process> | |
76 | # | |
77 | __EOD__ | |
78 | ||
79 | sub setup_init { | |
633a7bd8 | 80 | my ($self, $conf) = @_; |
d66768a2 | 81 | |
2063d380 | 82 | my $filename = "/etc/inittab"; |
d66768a2 | 83 | |
2063d380 | 84 | if ($self->ct_file_exists($filename)) { |
d66768a2 DM |
85 | my $inittab = $default_inittab; |
86 | ||
0d0ca400 | 87 | my $ttycount = PVE::LXC::get_tty_count($conf); |
d66768a2 DM |
88 | for (my $i = 1; $i <= $ttycount; $i++) { |
89 | next if $i == 7; # reserved for X11 | |
90 | my $levels = ($i == 1) ? '2345' : '23'; | |
5202218d DM |
91 | if ($self->{version} < 7) { |
92 | $inittab .= "$i:$levels:respawn:/sbin/getty -L 38400 tty$i\n"; | |
93 | } else { | |
94 | $inittab .= "$i:$levels:respawn:/sbin/getty --noclear 38400 tty$i\n"; | |
95 | } | |
d66768a2 DM |
96 | } |
97 | ||
2063d380 | 98 | $self->ct_file_set_contents($filename, $inittab); |
d66768a2 DM |
99 | } |
100 | } | |
101 | ||
55fa4e09 | 102 | sub setup_network { |
633a7bd8 | 103 | my ($self, $conf) = @_; |
55fa4e09 | 104 | |
55fa4e09 DM |
105 | my $networks = {}; |
106 | foreach my $k (keys %$conf) { | |
107 | next if $k !~ m/^net(\d+)$/; | |
93285df8 | 108 | my $ind = $1; |
27916659 | 109 | my $d = PVE::LXC::parse_lxc_network($conf->{$k}); |
55fa4e09 DM |
110 | if ($d->{name}) { |
111 | my $net = {}; | |
93285df8 | 112 | if (defined($d->{ip})) { |
ecf84da0 | 113 | if ($d->{ip} =~ /^(?:dhcp|manual)$/) { |
0178cffa WB |
114 | $net->{address} = $d->{ip}; |
115 | } else { | |
116 | my $ipinfo = PVE::LXC::parse_ipv4_cidr($d->{ip}); | |
117 | $net->{address} = $ipinfo->{address}; | |
118 | $net->{netmask} = $ipinfo->{netmask}; | |
119 | } | |
55fa4e09 | 120 | } |
93285df8 | 121 | if (defined($d->{'gw'})) { |
a1b1a247 | 122 | $net->{gateway} = $d->{'gw'}; |
55fa4e09 | 123 | } |
93285df8 | 124 | if (defined($d->{ip6})) { |
c6b8740b | 125 | if ($d->{ip6} =~ /^(?:auto|dhcp|manual)$/) { |
0178cffa WB |
126 | $net->{address6} = $d->{ip6}; |
127 | } elsif ($d->{ip6} !~ /^($IPV6RE)\/(\d+)$/) { | |
a1b1a247 | 128 | die "unable to parse ipv6 address/prefix\n"; |
0178cffa WB |
129 | } else { |
130 | $net->{address6} = $1; | |
131 | $net->{netmask6} = $2; | |
a1b1a247 | 132 | } |
a1b1a247 WB |
133 | } |
134 | if (defined($d->{'gw6'})) { | |
135 | $net->{gateway6} = $d->{'gw6'}; | |
55fa4e09 | 136 | } |
0178cffa | 137 | $networks->{$d->{name}} = $net if keys %$net; |
55fa4e09 DM |
138 | } |
139 | } | |
140 | ||
f44d23a5 | 141 | return if !scalar(keys %$networks); |
55fa4e09 | 142 | |
2063d380 | 143 | my $filename = "/etc/network/interfaces"; |
55fa4e09 DM |
144 | my $interfaces = ""; |
145 | ||
146 | my $section; | |
147 | ||
148 | my $done_v4_hash = {}; | |
149 | my $done_v6_hash = {}; | |
150 | ||
151 | my $print_section = sub { | |
152 | my ($new) = @_; | |
153 | ||
154 | return if !$section; | |
155 | ||
156 | my $net = $networks->{$section->{ifname}}; | |
157 | ||
158 | if ($section->{type} eq 'ipv4') { | |
159 | $done_v4_hash->{$section->{ifname}} = 1; | |
160 | ||
161 | $interfaces .= "auto $section->{ifname}\n" if $new; | |
162 | ||
ecf84da0 | 163 | if ($net->{address} =~ /^(dhcp|manual)$/) { |
0178cffa WB |
164 | $interfaces .= "iface $section->{ifname} inet $1\n"; |
165 | } elsif ($net->{address}) { | |
55fa4e09 | 166 | $interfaces .= "iface $section->{ifname} inet static\n"; |
a1b1a247 WB |
167 | $interfaces .= "\taddress $net->{address}\n" if defined($net->{address}); |
168 | $interfaces .= "\tnetmask $net->{netmask}\n" if defined($net->{netmask}); | |
169 | $interfaces .= "\tgateway $net->{gateway}\n" if defined($net->{gateway}); | |
55fa4e09 DM |
170 | foreach my $attr (@{$section->{attr}}) { |
171 | $interfaces .= "\t$attr\n"; | |
172 | } | |
55fa4e09 DM |
173 | } |
174 | ||
175 | $interfaces .= "\n"; | |
176 | ||
177 | } elsif ($section->{type} eq 'ipv6') { | |
178 | $done_v6_hash->{$section->{ifname}} = 1; | |
179 | ||
c6b8740b | 180 | if ($net->{address6} =~ /^(auto|dhcp|manual)$/) { |
0178cffa WB |
181 | $interfaces .= "iface $section->{ifname} inet6 $1\n"; |
182 | } elsif ($net->{address6}) { | |
55fa4e09 | 183 | $interfaces .= "iface $section->{ifname} inet6 static\n"; |
a1b1a247 WB |
184 | $interfaces .= "\taddress $net->{address6}\n" if defined($net->{address6}); |
185 | $interfaces .= "\tnetmask $net->{netmask6}\n" if defined($net->{netmask6}); | |
186 | $interfaces .= "\tgateway $net->{gateway6}\n" if defined($net->{gateway6}); | |
55fa4e09 DM |
187 | foreach my $attr (@{$section->{attr}}) { |
188 | $interfaces .= "\t$attr\n"; | |
189 | } | |
190 | } | |
191 | ||
192 | $interfaces .= "\n"; | |
193 | } else { | |
194 | die "unknown section type '$section->{type}'"; | |
195 | } | |
196 | ||
197 | $section = undef; | |
198 | }; | |
199 | ||
2063d380 | 200 | if (my $fh = $self->ct_open_file($filename, "r")) { |
55fa4e09 DM |
201 | while (defined (my $line = <$fh>)) { |
202 | chomp $line; | |
203 | if ($line =~ m/^#/) { | |
204 | $interfaces .= "$line\n"; | |
205 | next; | |
206 | } | |
207 | if ($line =~ m/^\s*$/) { | |
208 | if ($section) { | |
209 | &$print_section(); | |
210 | } else { | |
211 | $interfaces .= "$line\n"; | |
212 | } | |
213 | next; | |
214 | } | |
0178cffa | 215 | if ($line =~ m/^\s*iface\s+(\S+)\s+inet\s+(\S+)\s*$/) { |
55fa4e09 | 216 | my $ifname = $1; |
0178cffa | 217 | &$print_section(); # print previous section |
55fa4e09 DM |
218 | if (!$networks->{$ifname}) { |
219 | $interfaces .= "$line\n"; | |
220 | next; | |
221 | } | |
222 | $section = { type => 'ipv4', ifname => $ifname, attr => []}; | |
223 | next; | |
224 | } | |
0178cffa | 225 | if ($line =~ m/^\s*iface\s+(\S+)\s+inet6\s+(\S+)\s*$/) { |
55fa4e09 | 226 | my $ifname = $1; |
0178cffa | 227 | &$print_section(); # print previous section |
55fa4e09 DM |
228 | if (!$networks->{$ifname}) { |
229 | $interfaces .= "$line\n"; | |
230 | next; | |
231 | } | |
232 | $section = { type => 'ipv6', ifname => $ifname, attr => []}; | |
233 | next; | |
234 | } | |
0178cffa WB |
235 | # Handle other section delimiters: |
236 | if ($line =~ m/^\s*(?:mapping\s | |
237 | |auto\s | |
238 | |allow- | |
239 | |source\s | |
240 | |source-directory\s | |
241 | )/x) { | |
242 | &$print_section(); | |
243 | $interfaces .= "$line\n"; | |
244 | next; | |
245 | } | |
55fa4e09 DM |
246 | if ($section && $line =~ m/^\s*((\S+)\s(.*))$/) { |
247 | my ($adata, $aname) = ($1, $2); | |
248 | if ($aname eq 'address' || $aname eq 'netmask' || | |
249 | $aname eq 'gateway' || $aname eq 'broadcast') { | |
250 | # skip | |
251 | } else { | |
252 | push @{$section->{attr}}, $adata; | |
253 | } | |
254 | next; | |
255 | } | |
256 | ||
257 | $interfaces .= "$line\n"; | |
258 | } | |
259 | &$print_section(); | |
260 | ||
261 | } | |
262 | ||
93285df8 | 263 | my $need_separator = 1; |
55fa4e09 DM |
264 | foreach my $ifname (sort keys %$networks) { |
265 | my $net = $networks->{$ifname}; | |
93285df8 | 266 | |
55fa4e09 | 267 | if (!$done_v4_hash->{$ifname}) { |
93285df8 | 268 | if ($need_separator) { $interfaces .= "\n"; $need_separator = 0; }; |
55fa4e09 DM |
269 | $section = { type => 'ipv4', ifname => $ifname, attr => []}; |
270 | &$print_section(1); | |
271 | } | |
a1b1a247 | 272 | if (!$done_v6_hash->{$ifname} && defined($net->{address6})) { |
93285df8 | 273 | if ($need_separator) { $interfaces .= "\n"; $need_separator = 0; }; |
55fa4e09 DM |
274 | $section = { type => 'ipv6', ifname => $ifname, attr => []}; |
275 | &$print_section(1); | |
276 | } | |
277 | } | |
278 | ||
2063d380 | 279 | $self->ct_file_set_contents($filename, $interfaces); |
55fa4e09 | 280 | } |
1c7f4f65 DM |
281 | |
282 | 1; |