# linux-lib.pl
# Active interface functions for all flavours of linux

# active_interfaces()
# Returns a list of currently ifconfig'd interfaces
sub active_interfaces
{
local(@rv, @lines, $l);
&open_execute_command(IFC, "ifconfig -a", 1, 1);
while(<IFC>) {
	s/\r|\n//g;
	if (/^\S+/) { push(@lines, $_); }
	else { $lines[$#lines] .= $_; }
	}
close(IFC);
foreach $l (@lines) {
	local %ifc;
	$l =~ /^([^:\s]+)/; $ifc{'name'} = $1;
	$l =~ /^(\S+)/; $ifc{'fullname'} = $1;
	if ($l =~ /^(\S+):(\d+)/) { $ifc{'virtual'} = $2; }
	if ($l =~ /inet addr:(\S+)/) { $ifc{'address'} = $1; }
	elsif (!$_[0]) { next; }
	if ($l =~ /Mask:(\S+)/) { $ifc{'netmask'} = $1; }
	if ($l =~ /Bcast:(\S+)/) { $ifc{'broadcast'} = $1; }
	if ($l =~ /HWaddr (\S+)/) { $ifc{'ether'} = $1; }
	if ($l =~ /MTU:(\d+)/) { $ifc{'mtu'} = $1; }
	if ($l =~ /P-t-P:(\S+)/) { $ifc{'ptp'} = $1; }
	$ifc{'up'}++ if ($l =~ /\sUP\s/);
	$ifc{'promisc'}++ if ($l =~ /\sPROMISC\s/);
	$ifc{'edit'} = ($ifc{'name'} !~ /^ppp/);
	$ifc{'index'} = scalar(@rv);
	push(@rv, \%ifc);
	}
return @rv;
}

# activate_interface(&details)
# Create or modify an interface
sub activate_interface
{
local $a = $_[0];
local $cmd = "ifconfig $a->{'name'}";
if ($a->{'virtual'} ne "") { $cmd .= ":$a->{'virtual'}"; }
$cmd .= " $a->{'address'}";
if ($a->{'netmask'}) { $cmd .= " netmask $a->{'netmask'}"; }
if ($a->{'broadcast'}) { $cmd .= " broadcast $a->{'broadcast'}"; }
if ($a->{'mtu'} && $a->{'virtual'} eq "") { $cmd .= " mtu $a->{'mtu'}"; }
if ($a->{'up'}) { $cmd .= " up"; }
else { $cmd .= " down"; }
local $out = &backquote_logged("$cmd 2>&1");
if ($?) { &error($out); }
if ($a->{'ether'}) {
	$out = &backquote_logged(
		"ifconfig $a->{'name'} hw ether $a->{'ether'} 2>&1");
	if ($?) { &error($out); }
	}
}

# deactivate_interface(&details)
# Shutdown some active interface
sub deactivate_interface
{
local $name = $_[0]->{'name'}.
	      ($_[0]->{'virtual'} ne "" ? ":$_[0]->{'virtual'}" : "");
if ($_[0]->{'virtual'} ne "") {
	# Shutdown virtual interface by setting address to 0
	local $out = &backquote_logged("ifconfig $name 0 2>&1");
	}
local ($still) = grep { $_->{'fullname'} eq $name } &active_interfaces();
if ($still) {
	# Old version of ifconfig or non-virtual interface.. down it
	local $out = &backquote_logged("ifconfig $name down 2>&1");
	local ($still) = grep { $_->{'fullname'} eq $name }
			      &active_interfaces();
	if ($still) {
		&error("<pre>$out</pre>");
		}
	}
}

# iface_type(name)
# Returns a human-readable interface type name
sub iface_type
{
if ($_[0] =~ /^(.*)\.(\d+)$/) {
	return &iface_type("$1")." VLAN";
	}
return "PPP" if ($_[0] =~ /^ppp/);
return "SLIP" if ($_[0] =~ /^sl/);
return "PLIP" if ($_[0] =~ /^plip/);
return "Ethernet" if ($_[0] =~ /^eth/);
return "Wireless Ethernet" if ($_[0] =~ /^(wlan|ath)/);
return "Arcnet" if ($_[0] =~ /^arc/);
return "Token Ring" if ($_[0] =~ /^tr/);
return "Pocket/ATP" if ($_[0] =~ /^atp/);
return "Loopback" if ($_[0] =~ /^lo/);
return "ISDN rawIP" if ($_[0] =~ /^isdn/);
return "ISDN syncPPP" if ($_[0] =~ /^ippp/);
return "CIPE" if ($_[0] =~ /^cip/);
return "VmWare" if ($_[0] =~ /^vmnet/);
return "Wireless" if ($_[0] =~ /^wlan/);
return $text{'ifcs_unknown'};
}

# list_routes()
# Returns a list of active routes
sub list_routes
{
local @rv;
&open_execute_command(ROUTES, "netstat -rn", 1, 1);
while(<ROUTES>) {
	s/\s+$//;
	if (/^([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)$/) {
		push(@rv, { 'dest' => $1,
			    'gateway' => $2,
			    'netmask' => $3,
			    'iface' => $4 });
		}
	}
close(ROUTES);
return @rv;
}

# iface_hardware(name)
# Does some interface have an editable hardware address
sub iface_hardware
{
return $_[0] =~ /^eth/;
}

# allow_interface_clash()
# Returns 0 to indicate that two virtual interfaces with the same IP
# are not allowed
sub allow_interface_clash
{
return 0;
}

# get_dns_config()
# Returns a hashtable containing keys nameserver, domain, search & order
sub get_dns_config
{
local $dns;
local $rc;
if ($use_suse_dns && ($rc = &parse_rc_config()) && $rc->{'NAMESERVER'}) {
	# Special case - get DNS settings from SuSE config
	local @ns = split(/\s+/, $rc->{'NAMESERVER'}->{'value'});
	$dns->{'nameserver'} = [ grep { $_ ne "YAST_ASK" } @ns ];
	local $src = $rc->{'SEARCHLIST'};
	$dns->{'domain'} = [ split(/\s+/, $src->{'value'}) ] if ($src);
	$dnsfile = $rc_config;
	}
else {
	&open_readfile(RESOLV, "/etc/resolv.conf");
	while(<RESOLV>) {
		s/\r|\n//g;
		s/#.*$//;
		if (/nameserver\s+(.*)/) {
			push(@{$dns->{'nameserver'}}, split(/\s+/, $1));
			}
		elsif (/domain\s+(\S+)/) {
			$dns->{'domain'} = [ $1 ];
			}
		elsif (/search\s+(.*)/) {
			$dns->{'domain'} = [ split(/\s+/, $1) ];
			}
		}
	close(RESOLV);
	$dnsfile = "/etc/resolv.conf";
	}
&open_readfile(SWITCH, "/etc/nsswitch.conf");
while(<SWITCH>) {
	s/\r|\n//g;
	if (/^\s*hosts:\s+(.*)/) {
		$dns->{'order'} = $1;
		}
	}
close(SWITCH);
$dns->{'files'} = [ $dnsfile, "/etc/nsswitch.conf" ];
return $dns;
}

# save_dns_config(&config)
# Writes out the resolv.conf and nsswitch.conf files
sub save_dns_config
{
local $rc;
&lock_file($rc_config) if ($suse_dns_config);
if ($use_suse_dns && ($rc = &parse_rc_config()) && $rc->{'NAMESERVER'}) {
	&save_rc_config($rc, "NAMESERVER", join(" ", @{$_[0]->{'nameserver'}}));
	&save_rc_config($rc, "SEARCHLIST", join(" ", @{$_[0]->{'domain'}}));
	}
else {
	&lock_file("/etc/resolv.conf");
	&open_readfile(RESOLV, "/etc/resolv.conf");
	local @resolv = <RESOLV>;
	close(RESOLV);
	&open_tempfile(RESOLV, ">/etc/resolv.conf");
	foreach (@{$_[0]->{'nameserver'}}) {
		&print_tempfile(RESOLV, "nameserver $_\n");
		}
	if ($_[0]->{'domain'}) {
		if ($_[0]->{'domain'}->[1]) {
			&print_tempfile(RESOLV, "search ",join(" ", @{$_[0]->{'domain'}}),"\n");
			}
		else {
			&print_tempfile(RESOLV, "domain $_[0]->{'domain'}->[0]\n");
			}
		}
	foreach (@resolv) {
		&print_tempfile(RESOLV, $_) if (!/^\s*(nameserver|domain|search)\s+/);
		}
	&close_tempfile(RESOLV);
	&unlock_file("/etc/resolv.conf");
	}

&lock_file("/etc/nsswitch.conf");
&open_readfile(SWITCH, "/etc/nsswitch.conf");
local @switch = <SWITCH>;
close(SWITCH);
&open_tempfile(SWITCH, ">/etc/nsswitch.conf");
foreach (@switch) {
	if (/^\s*hosts:\s+/) {
		&print_tempfile(SWITCH, "hosts:\t$_[0]->{'order'}\n");
		}
	else {
		&print_tempfile(SWITCH, $_);
		}
	}
&close_tempfile(SWITCH);
&unlock_file("/etc/nsswitch.conf");
if ($suse_dns_config && $rc->{'USE_NIS_FOR_RESOLVING'}) {
	if ($_[0]->{'order'} =~ /nis/) {
		&save_rc_config($rc, "USE_NIS_FOR_RESOLVING", "yes");
		}
	else {
		&save_rc_config($rc, "USE_NIS_FOR_RESOLVING", "no");
		}
	}
&unlock_file($rc_config) if ($suse_dns_config);
}

$max_dns_servers = 3;

# order_input(&dns)
# Returns HTML for selecting the name resolution order
sub order_input
{
if ($_[0]->{'order'} =~ /\[/) {
	# Using a complex resolve list
	return "<input name=order size=45 value=\"$_[0]->{'order'}\">\n";
	}
else {
	# Can select by menus
	local @o = split(/\s+/, $_[0]->{'order'});
	@o = map { s/nis\+/nisplus/; s/yp/nis/; $_; } @o;
	local ($rv, $i, $j);
	local @srcs = ( "", "files", "dns", "nis", "nisplus", "ldap", "db" );
	local @srcn = ( "", "Hosts", "DNS", "NIS", "NIS+", "LDAP", "DB" );
	for($i=1; $i<@srcs; $i++) {
		local $ii = $i-1;
		$rv .= "<select name=order_$ii>\n";
		for($j=0; $j<@srcs; $j++) {
			$rv .= sprintf "<option value=\"%s\" %s>%s\n",
					$srcs[$j],
					$o[$ii] eq $srcs[$j] ? "selected" : "",
					$srcn[$j] ? $srcn[$j] : "&nbsp;";
			}
		$rv .= "</select>\n";
		}
	return $rv;
	}
}

# parse_order(&dns)
# Parses the form created by order_input()
sub parse_order
{
if (defined($in{'order'})) {
	$in{'order'} =~ /\S/ || &error($text{'dns_eorder'});
	$_[0]->{'order'} = $in{'order'};
	}
else {
	local($i, @order);
	for($i=0; defined($in{"order_$i"}); $i++) {
		push(@order, $in{"order_$i"}) if ($in{"order_$i"});
		}
	$_[0]->{'order'} = join(" ", @order);
	}
}

1;

