Handle hostnames with upper-case letters
[webmin.git] / net / gentoo-linux-lib.pl
1 # Networking functions for Gentoo 2006+
2
3 do 'linux-lib.pl';
4
5 $gentoo_net_config = "/etc/conf.d/net";
6 $min_virtual_number = 1;
7
8 # parse_gentoo_net()
9 # Parses the Gentoo net config file into an array of named sections
10 sub parse_gentoo_net
11 {
12 local @rv;
13 local $sect;
14 local $lnum = 0;
15 open(CONF, $gentoo_net_config);
16 while(<CONF>) {
17         s/\r|\n//g;
18         s/#.*$//;
19         if (/^\s*(\S+)\s*=\s*\((.*)/) {
20                 # Start of some section, which may span several lines
21                 $sect = { 'name' => $1,
22                           'line' => $lnum };
23                 push(@rv, $sect);
24                 local $v = $2;
25                 if ($v =~ /^(.*)\)/) {
26                         # Ends on same line
27                         $sect->{'values'} = [ &split_gentoo_values("$1") ];
28                         $sect->{'eline'} = $lnum;
29                         $sect = undef;
30                         }
31                 else {
32                         # May span multiple
33                         $sect->{'values'} = [ &split_gentoo_values($v) ];
34                         }
35                 }
36         elsif (/^\s*\)/ && $sect) {
37                 # End of a section
38                 $sect->{'eline'} = $lnum;
39                 $sect = undef;
40                 }
41         elsif (/^\s*(".*")\s*\)/ && $sect) {
42                 # End of a section, but with some values before it
43                 push(@{$sect->{'values'}}, &split_gentoo_values("$1"));
44                 $sect->{'eline'} = $lnum;
45                 $sect = undef;
46                 }
47         elsif (/^\s*(".*")/ && $sect) {
48                 # Values within a section
49                 push(@{$sect->{'values'}}, &split_gentoo_values("$1"));
50                 }
51         $lnum++;
52         }
53 close(CONF);
54 return @rv;
55 }
56
57 # save_gentoo_net(&old, &new)
58 # Update or create a Gentoo net config file section
59 sub save_gentoo_net
60 {
61 local ($old, $new) = @_;
62 local @lines;
63 if ($new) {
64         push(@lines, $new->{'name'}."=(");
65         foreach my $v (@{$new->{'values'}}) {
66                 push(@lines,"  \"$v\"");
67                 }
68         push(@lines, ")");
69         }
70 local $lref = &read_file_lines($gentoo_net_config);
71 if ($old && $new) {
72         # Replace section
73         splice(@$lref, $old->{'line'}, $old->{'eline'}-$old->{'line'}+1,
74                @lines);
75         $new->{'eline'} = $new->{'line'}+scalar(@lines)-1;
76         }
77 elsif ($old && !$new) {
78         # Delete section
79         splice(@$lref, $old->{'line'}, $old->{'eline'}-$old->{'line'}+1);
80         }
81 elsif (!$old && $new) {
82         # Add section
83         $new->{'line'} = scalar(@$lref);
84         $new->{'eline'} = $new->{'line'}+scalar(@lines)-1;
85         push(@$lref, @lines);
86         }
87 &flush_file_lines($gentoo_net_config);
88 }
89
90 # split_gentoo_values(string)
91 # Splits a string like "foo bar" "smeg spod" into an array
92 sub split_gentoo_values
93 {
94 local ($str) = @_;
95 local @rv;
96 while($str =~ /^\s*"([^"]+)",?(.*)/) {
97         push(@rv, $1);
98         $str = $2;
99         }
100 return @rv;
101 }
102
103 # boot_interfaces()
104 # Returns a list of interfaces brought up at boot time
105 sub boot_interfaces
106 {
107 local @rv;
108 foreach my $g (&parse_gentoo_net()) {
109         if ($g->{'name'} =~ /^config_(\S+)/) {
110                 local $gn = $1;
111                 local $n = 0;
112                 foreach my $v (@{$g->{'values'}}) {
113                         # An interface definition
114                         local $iface = { 'name' => $gn,
115                                          'up' => 1,
116                                          'edit' => 1,
117                                          'index' => scalar(@rv) };
118                         if ($n == 0) {
119                                 $iface->{'fullname'} = $gn;
120                                 }
121                         else {
122                                 $iface->{'fullname'} = $gn.":".$n;
123                                 $iface->{'virtual'} = $n;
124                                 }
125                         local @w = split(/\s+/, $v);
126                         if ($w[0] eq "dhcp") {
127                                 $iface->{'dhcp'} = 1;
128                                 }
129                         elsif ($w[0] eq "noop") {
130                                 # Skipped, but still uses a up a number
131                                 $n++;
132                                 next;
133                                 }
134                         if (&check_ipaddress($w[0])) {
135                                 $iface->{'address'} = $w[0];
136                                 }
137                         elsif ($w[0] =~ /^([0-9\.]+)\/(\d+)$/) {
138                                 $iface->{'address'} = $1;
139                                 $iface->{'netmask'} = &prefix_to_mask($2);
140                                 }
141                         for($i=1; $i<@w; $i++) {
142                                 if ($w[$i] eq "netmask") {
143                                         $iface->{'netmask'} = $w[++$i];
144                                         }
145                                 elsif ($w[$i] eq "broadcast") {
146                                         $iface->{'broadcast'} = $w[++$i];
147                                         }
148                                 elsif ($w[$i] eq "mtu") {
149                                         $iface->{'mtu'} = $w[++$i];
150                                         }
151                                 }
152                         if ($iface->{'address'} && $iface->{'netmask'}) {
153                                 $iface->{'broadcast'} ||= &compute_broadcast(
154                                     $iface->{'address'}, $iface->{'netmask'}); 
155                                 }
156                         $iface->{'gentoo'} = $g;
157                         push(@rv, $iface);
158                         $n++;
159                         }
160                 }
161         elsif ($g->{'name'} =~ /^routes_(\S+)/) {
162                 # A route definition for an interface
163                 local ($iface) = grep { $_->{'fullname'} eq $1 } @rv;
164                 local $spec = $g->{'values'}->[0];
165                 if ($iface) {
166                         if ($spec =~ /default\s+via\s+([0-9\.]+)/ ||
167                             $spec =~ /default\s+([0-9\.]+)/) {
168                                 $iface->{'gateway'} = $1;
169                                 $iface->{'gentoogw'} = $g;
170                                 }
171                         }
172                 }
173         $lnum++;
174         }
175 return @rv;
176 }
177
178 # save_interface(&details)
179 # Create or update a boot-time interface
180 sub save_interface
181 {
182 local ($iface) = @_;
183 &lock_file($gentoo_net_config);
184
185 # Build the interface line
186 local @w;
187 if ($iface->{'dhcp'}) {
188         push(@w, "dhcp");
189         }
190 else {
191         push(@w, $iface->{'address'});
192         if ($iface->{'netmask'}) {
193                 push(@w, "netmask", $iface->{'netmask'});
194                 }
195         if ($iface->{'broadcast'}) {
196                 push(@w, "broadcast", $iface->{'broadcast'});
197                 }
198         if ($iface->{'mtu'}) {
199                 push(@w, "mtu", $iface->{'mtu'});
200                 }
201         }
202
203 # Find the current block for this interface
204 local @gentoo = &parse_gentoo_net();
205 local ($g) = grep { $_->{'name'} eq 'config_'.$iface->{'name'} } @gentoo;
206 if ($g) {
207         # Found it .. append or replace
208         while (!$g->{'values'}->[$iface->{'virtual'}]) {
209                 push(@{$g->{'values'}}, "noop");
210                 }
211         $g->{'values'}->[$iface->{'virtual'}] = join(" ", @w);
212         &save_gentoo_net($g, $g);
213         }
214 else {
215         # Needs a new block
216         $g = { 'name' => $iface->{'name'},
217                'values' => [ join(" ", @w) ] };
218         &save_gentoo_net(undef, $g);
219         }
220 &unlock_file($gentoo_net_config);
221 }
222
223 # delete_interface(&details)
224 # Delete a boot-time interface
225 sub delete_interface
226 {
227 local ($iface) = @_;
228
229 # Find the current block for this interface
230 &lock_file($gentoo_net_config);
231 local @gentoo = &parse_gentoo_net();
232 local ($g) = grep { $_->{'name'} eq 'config_'.$iface->{'name'} } @gentoo;
233 if ($g) {
234         # Found it .. take out the interface
235         if ($iface->{'virtual'} == scalar(@{$g->{'values'}})-1) {
236                 # Last one
237                 pop(@{$g->{'values'}});
238                 }
239         else {
240                 $g->{'values'}->[$iface->{'virtual'}] = 'noop';
241                 }
242         &save_gentoo_net($g, $g);
243         }
244 &unlock_file($gentoo_net_config);
245 }
246
247 # can_edit(what)
248 # Can some boot-time interface parameter be edited?
249 sub can_edit
250 {
251 return $_[0] ne 'up' && $_[0] ne 'bootp';
252 }
253
254 # valid_boot_address(address)
255 # Is some address valid for a bootup interface
256 sub valid_boot_address
257 {
258 return &check_ipaddress($_[0]);
259 }
260
261 # get_hostname()
262 sub get_hostname
263 {
264 local %host;
265 &read_env_file("/etc/conf.d/hostname", \%host);
266 if ($host{'HOSTNAME'}) {
267         return $host{'HOSTNAME'};
268         }
269 return &get_system_hostname(1);
270 }
271
272 # save_hostname(name)
273 sub save_hostname
274 {
275 local %host;
276 &read_env_file("/etc/conf.d/hostname", \%host);
277 $host{'HOSTNAME'} = $_[0];
278 &write_env_file("/etc/conf.d/hostname", \%host);
279 &system_logged("hostname $_[0] >/dev/null 2>&1");
280 }
281
282 # routing_input()
283 # Prints HTML for editing routing settings
284 sub routing_input
285 {
286 local ($gw, $dev) = &get_default_gateway();
287 local @ifaces = grep { $_->{'virtual'} eq '' } &boot_interfaces();
288 print &ui_table_row($text{'routes_def'},
289       &ui_radio("route_def", $gw ? 0 : 1,
290                 [ [ 1, $text{'routes_nogw'}."<br>" ],
291                   [ 0, &text('routes_ggw',
292                           &ui_textbox("gw", $gw, 20),
293                           &ui_select("dev", $dev,
294                             [ map { $_->{'name'} } @ifaces ])) ] ]));
295 }
296
297 # parse_routing()
298 # Applies settings from routing_input form
299 sub parse_routing
300 {
301 if ($in{'route_def'}) {
302         &set_default_gateway();
303         }
304 else {
305         &check_ipaddress($in{'gw'}) ||
306                 &error(&text('routes_edefault', $in{'gw'}));
307         &set_default_gateway($in{'gw'}, $in{'dev'});
308         }
309 }
310
311 # apply_network()
312 # Apply the interface and routing settings
313 sub apply_network
314 {
315 opendir(DIR, "/etc/init.d");
316 foreach my $f (readdir(DIR)) {
317         if ($f =~ /^net.(\S+)/ && $1 ne "lo") {
318                 &system_logged("cd / ; /etc/init.d/$f restart >/dev/null 2>&1 </dev/null");
319                 }
320         }
321 closedir(DIR);
322 }
323
324 # get_default_gateway()
325 # Returns the default gateway IP (if one is set) and device (if set) boot time
326 # settings.
327 sub get_default_gateway
328 {
329 local @ifaces = &boot_interfaces();
330 local ($iface) = grep { $_->{'gateway'} } @ifaces;
331 if ($iface) {
332         return ( $iface->{'gateway'}, $iface->{'name'} );
333         }
334 else {
335         return ( );
336         }
337 }
338
339 # set_default_gateway(gateway, device)
340 # Sets the default gateway to the given IP accessible via the given device,
341 # in the boot time settings.
342 sub set_default_gateway
343 {
344 local ($gw, $dev) = @_;
345 &lock_file($gentoo_net_config);
346 local @ifaces = &boot_interfaces();
347 local ($iface) = grep { $_->{'gateway'} } @ifaces;
348 if ($iface && $gw) {
349         # Change existing default route
350         $g = $iface->{'gentoogw'};
351         $g->{'name'} = 'routes_'.$dev;
352         $g->{'values'}->[0] = "default via $gw";
353         &save_gentoo_net($g, $g);
354         }
355 elsif ($iface && !$gw) {
356         # Deleting existing default route
357         $g = $iface->{'gentoogw'};
358         &save_gentoo_net($g, undef);
359         }
360 elsif (!$iface && $gw) {
361         # Adding new default route
362         $g = { 'name' => 'routes_'.$dev,
363                'values' => [ "default via $gw" ] };
364         &save_gentoo_net(undef, $g);
365         }
366 &unlock_file($gentoo_net_config);
367 }
368
369 # supports_address6([&iface])
370 # Returns 1 if managing IPv6 interfaces is supported
371 sub supports_address6
372 {
373 local ($iface) = @_;
374 return 0;
375 }
376
377