# # Support library for scripts parsing the sitesummary files. # package SiteSummary; require Exporter; our $VERSION = 0.01; our @ISA = qw(Exporter); our @EXPORT = qw( for_all_hosts get_age_group get_age_groups get_debian_edu_profile get_debian_edu_ver get_debian_ver get_default_route get_dns_address get_filepath_current get_hardware_info get_hostclass get_localhostname get_hostname get_linux_kernel_ver get_macaddresses get_primary_ip_address get_primary_macaddress get_unique_ether_id get_site get_sitegroup is_laptop is_pkg_installed ); my $pwd = "/var/lib/sitesummary/entries"; # Path to the entries # File for debian-edu configuration my $debian_edu_config = "/debian-edu/config"; # Provide mechanism to remap host names for hosts private networks # available via tunnels. my $hostmapfile = "/etc/sitesummary/hostmap"; my $hostmapdir = "/etc/sitesummary/hostmap.d"; my %hostmap; sub load_hostmap { my @files = ($hostmapfile); %hostmap = (); # Clear hostmap if (opendir(my $dh, $hostmapdir)) { push(@files, grep { /^\./ && -f "$some_dir/$_" } sort readdir($dh)); closedir $dh; } for my $file (@files) { if (open(my $fh, '<', $file)) { while (<$fh>) { chomp; s/\#.*$//; next if m/^\s*$/; my ($hostid, $newhostname) = split(/\s+/); $hostmap{$hostid} = $newhostname; } close $fh; } } return; } sub get_filepath_current { my ($hostid, $file) = @_; return "$pwd/$hostid$file"; } # # Return the value string from a file, ignoring comments # sub get_file_string { my ($hostid, $filename) = @_; my $path = get_filepath_current($hostid, $filename); my $string; if (open (FILE, $path)) { while() { chomp; s/\#.+$//; next if (/^\s*$/); $string = $_; } close(FILE); return $string; } else { return undef; } } # # Return the site string # sub get_site { my $hostid = shift; return get_file_string($hostid, "/siteinfo/site"); } # # Return the sitegroup string # sub get_sitegroup { my $hostid = shift; return get_file_string($hostid, "/siteinfo/sitegroup"); } # # Return the hostclass string # sub get_hostclass { my $hostid = shift; return get_file_string($hostid, "/siteinfo/hostclass"); } # # Return the IP address on the primary network interface # sub get_primary_ip_address { my $hostid = shift; my $path = get_filepath_current($hostid, "/system/ifconfig-a"); # XXX Not properly implemented, just pick the first non-local IP my $ip; if (open (FILE, $path)) { while() { chomp; if ((m/inet addr:(\S+)\s+/ || m/\s*inet\s+(\S+)\s+/) && "127.0.0.1" ne $1) { $ip = $1; last; } } close(FILE); return $ip; } else { return undef; } } # # Return all MAC addresses sub get_macaddresses_from_ifconfig { my $ifconfigoutput = shift; my %macs; open(IFCONFIG, $ifconfigoutput) || return (); my $line = ""; while () { chomp; if (m/^(\w+)\s+Link encap:Ethernet HWaddr (\S+)/) { # Old ifconfig format $macs{$1} = $2; while () { chomp; last if (/^\s*$/); } } elsif (m/flags=/) { # New ifconfig format my $line = $_; while () { chomp; $line .= $_; last if (/^\s*$/); } if ($line =~ m/^(\S+): .+\sether\s+(\S+)\s/) { $macs{$1} = $2; } } } close (IFCONFIG); my $if = (sort keys %macs)[0]; my $mac = $macs{$if}; return lc("$mac"); return undef; } # # Return all MAC addresses sub get_macaddresses { my $hostid = shift; my $path = get_filepath_current($hostid, "/system/ifconfig-a"); return get_macaddresses_from_ifconfig($path); } # Return current default route used on host sub get_default_route { my $hostid = shift; my $path = get_filepath_current($hostid, "/system/route-n"); if (open(my $fh, $path)) { while (<$fh>) { if (m/^0.0.0.0\s+(\S+)\s+/) { close($fh); return $1; } } close($fh); } return undef; } # # Return the MAC address on the primary network interface # sub get_primary_macaddress { my $hostid = shift; my $path = get_filepath_current($hostid, "/system/ifconfig-a"); my @macs = get_macaddresses_from_ifconfig($path); if (@macs) { return (sort @macs)[0]; } else { return undef; } } # # Given the output from 'ifconfig -a', return the unique host ID used # by sitesummary. # Use like this: # get_unique_ether_id(get_file_string($hostid, "/system/ifconfig-a")) # sub get_unique_ether_id { my $ifconfigoutput = shift; my @macs = get_macaddresses_from_ifconfig($ifconfigoutput); if (@macs) { my $mac = (sort @macs)[0]; return lc("ether-$mac"); } else { return undef; } } # # Return the hostname string as seen on the host itself # sub get_localhostname { my $hostid = shift; return get_file_string($hostid, "/system/hostname"); } # # Return the hostname string # sub get_hostname { my $hostid = shift; if (exists $hostmap{$hostid}) { return $hostmap{$hostid}; } else { return get_localhostname($hostid); } } # # Return an address that can be used to contact the host. Prefer DNS, # but fall back to the IP address if the IP address do not resolve in # DNS. # sub in_dns { my $hostname = shift; my $packed_ip = gethostbyname($hostname); return defined $packed_ip; } sub get_dns_address { my $hostid = shift; my $hostname = get_hostname($hostid); # Use IP address as hostname if the provided hostname is bogus or # missing in DNS. $hostname = get_primary_ip_address($hostid) if (! in_dns($hostname) || "localhost" eq $hostname); return $hostname; } # # Return Linux kernel version for the machines using Linux. # sub get_linux_kernel_ver { my $hostid = shift; my $path = get_filepath_current($hostid, "/system/uname-smr"); my $kver; if (open (FILE, $path)) { while() { chomp; s/\#.+$//; next if (/^\s*$/); my @f = (split(/\s+/, $_)); $kver = $f[1] if ("Linux" eq $f[0]); } close(FILE); return $kver; } else { return undef; } } sub get_debian_edu_profile { my $hostid = shift; my $path = get_filepath_current($hostid, $debian_edu_config); if ( ! -e $path ) { return undef; } if (open (FILE, $path)) { while () { chomp; s/\#.+$//; next if not (/PROFILE/); s/^PROFILE=//; return $_; } } close(FILE); } sub get_debian_edu_ver { my $hostid = shift; my $path = get_filepath_current($hostid, $debian_edu_config); if ( ! -e $path ) { return undef; } if (open (FILE, $path)) { while () { chomp; s/\#.+$//; next if not (/VERSION/); s/^VERSION=//; return $_; } } } sub get_debian_ver { my $hostid = shift; my $path = get_filepath_current($hostid, "/debian/debian_version"); if (open (my $fh, $path)) { my $version = <$fh>; chomp $version; close($fh); return $version; } else { return undef; } } sub get_hardware_info { my $hostid = shift; my $path = get_filepath_current($hostid, "/system/dmidecode"); if (open(FILE, "<", $path)) { my $sysinfo = 0; my ($vendor, $model, $version, $serial); while () { chomp; next unless ($sysinfo || m/^System Information/); $sysinfo = 1; $vendor = $1 if (m/Manufacturer: (.+\S)\s*$/); $model = $1 if (m/Product Name: (.+\S)\s*$/); $version = $1 if (m/Version: (.+\S)\s*$/); $serial = $1 if (m/Serial Number: (.+\S)\s*$/); last if (m/^Handle /); } close(FILE); # Avoid returning bogus vendor and model undef $vendor if (defined $vendor && ( "Not Specified" eq $vendor || "System Manufaturer" eq $vendor || "System manufaturer" eq $vendor || "System manufacturer" eq $vendor || "To Be Filled By O.E.M." eq $vendor || "To Be Filled By O.E.M. by More String" eq $vendor )); undef $model if (defined $model && ( "Not Specified" eq $model || "System Name" eq $model || "System Product Name" eq $model || "To Be Filled By O.E.M." eq $model || "To Be Filled By O.E.M. To Be Filled By O.E.M." eq $model )); # Append version string to get for example the thinkpad model # name, but ignore bogus entries. $model = "$model $version" if ($version && $version ne "Not Specified" && $version ne "Not Available" && $version ne "System Version" && $version ne "To Be Filled By O.E.M." && $version ne "None"); return ($vendor, $model, $serial); } else { return undef; } } sub is_pkg_installed { my ($hostid, $pkgname) = @_; # Check debian/dpkg-l for 'ii *pkgname ' my $path = get_filepath_current($hostid, "/debian/dpkg-l"); if (open (my $fh, $path)) { while(<$fh>) { if (m/^ii *$pkgname /) { close($fh); return 1 } } close($fh); } return undef; } sub is_laptop { my $hostid = shift; my $path = get_filepath_current($hostid, "/system/laptop"); return -e $path; } sub for_all_hosts { my $callback = shift; if ( ! -d $pwd ) { print STDERR "error: Missing $pwd directory.\n"; return undef; } opendir(DIR, $pwd) or die "$!: $pwd\nDied"; my $count = 0; foreach (readdir(DIR)) { chomp; next if m/^$/ || m/^.$/ || m/^..$/; my $hostid = $_; if ( -d "$pwd/$hostid" ) { $count ++ if ($callback->($hostid)); } else { print STDERR "warning: Junk in filelog: $pwd/$hostid\n"; } } closedir(DIR); return $count; } sub get_age_groups { return ( 0 => '>0 days', 3 => '>3 days', 7 => '>one week', 14 => '>14 days', 30 => '>30 days', 90 => '>90 days', 120 => '>120 days', 180 => '>180 days', ); } sub get_age_group { my $hostid = shift; my %agegroups = get_age_groups(); my $topdir = get_filepath_current($hostid, "/"); my $age = (time() - (stat($topdir))[9]) / (60 * 60 * 24); my $thisgroup; for my $group (sort { $a <=> $b; } keys %agegroups) { if ($age > $group) { $thisgroup = $group; } } return $thisgroup; } load_hostmap(); 1; ######################################################################## # Local Variables: # mode: perl # End: ########################################################################