Make Slackware detection more specific, add OsChooser.pm
authorJoe Cooper <joe@virtualmin.com>
Mon, 11 Aug 2008 23:28:08 +0000 (23:28 +0000)
committerJoe Cooper <joe@virtualmin.com>
Mon, 11 Aug 2008 23:28:08 +0000 (23:28 +0000)
OsChooser.pm [new file with mode: 0755]
os_list.txt

diff --git a/OsChooser.pm b/OsChooser.pm
new file mode 100755 (executable)
index 0000000..a90f5f2
--- /dev/null
@@ -0,0 +1,225 @@
+#!/usr/local/bin/perl -w
+use strict;
+# Detect the operating system and version.
+
+package OsChooser;
+
+# Package scoped for mapping short names to long "proper" names
+my %NAMES_TO_REAL;
+
+# main
+sub main {
+       if ($#ARGV < 1) { die "Usage: $0 os_list.txt outfile [0|1|2|3] [issue]\n"; }
+       my ($oslist, $out, $auto, $issue) = @ARGV;
+       return write_file($out, oschooser($oslist, $auto, $issue));
+       }
+main() unless caller(); # make it testable and usable as a library
+
+$| = 1;
+
+sub oschooser {
+my ($oslist, $auto, $issue) = @_;
+my $ver_ref;
+
+my ($list_ref, $names_ref) = parse_patterns($oslist);
+
+if ($auto && ($ver_ref = auto_detect($oslist, $issue, $list_ref, $names_ref))) {
+       return ($ver_ref->[2], $ver_ref->[3], $ver_ref->[0], $ver_ref->[1]);
+       }
+elsif (!$auto || ($auto == 3 && have_tty()) || $auto == 2) {
+       $ver_ref = ask_user($names_ref, $list_ref);
+       return ($ver_ref->[2], $ver_ref->[3], $ver_ref->[0], $ver_ref->[1]);
+       }
+else {
+       print "Failed to detect operating system\n";
+       exit 1;
+       }
+}
+
+# Return a reference to a pre-parsed list array, and a ref to a names array
+sub parse_patterns {
+my ($oslist) = @_;
+my @list;
+my @names;
+my %donename;
+# Parse the patterns file
+open(OS, "<$oslist") || die "failed to open $oslist : $!";
+while(<OS>) {
+       chop;
+       if (/^([^\t]+)\t+([^\t]+)\t+([^\t]+)\t+([^\t]+)\t*(.*)$/) {
+               push(@list, [ $1, $2, $3, $4, $5 ]);
+               push(@names, $1) if (!$donename{$1}++);
+               $NAMES_TO_REAL{$1} ||= $3;
+               }
+       }
+close(OS);
+return (\@list, \@names);
+}
+
+# auto_detect($oslist, $issue)
+# Returns detected OS details in a hash ref
+sub auto_detect {
+my ($oslist, $issue, $list_ref) = @_;
+my $ver_ref;
+my @list = @$list_ref;
+
+# Try to guess the OS name and version
+my $etc_issue;
+my $uname = `uname -a`;
+
+if ($issue) {
+       $etc_issue = `cat $issue`;
+       $uname = $etc_issue; # Strangely, I think this will work fine.
+       }
+elsif (-r "/etc/.issue") {
+       $etc_issue = `cat /etc/.issue`;
+       }
+elsif (-r "/etc/issue") {
+       $etc_issue = `cat /etc/issue`;
+       }
+
+foreach my $o_ref (@list) {
+       if ($issue && $o_ref->[4]) {
+               $o_ref->[4] =~ s#cat [/a-zA-Z\-\s]*\s2#cat $issue 2#g;
+               } # Testable, but this regex substitution is dumb.XXX
+       local $^W = 0; # Disable warnings for evals, which may have undefined vars
+       if ($o_ref->[4] && eval "$o_ref->[4]") {
+               # Got a match! Resolve the versions
+               print "$o_ref->[4]\n";
+               $ver_ref = $o_ref;
+               if ($ver_ref->[1] =~ /\$/) {
+                       $ver_ref->[1] = eval "($o_ref->[4]); $ver_ref->[1]";
+                       }
+               if ($ver_ref->[3] =~ /\$/) {
+                       $ver_ref->[3] = eval "($o_ref->[4]); $ver_ref->[3]";
+                       }
+               last;
+               }
+       if ($@) {
+               print STDERR "Error parsing $o_ref->[4]\n";
+               }
+       }
+       return $ver_ref;
+}
+
+sub ask_user {
+my ($names_ref, $list_ref) = @_;
+my @names = @$names_ref;
+my @list = @$list_ref;
+my $vnum;
+my $osnum;
+# ask for the operating system name ourselves
+my $dashes = "-" x 75;
+print <<EOF;
+For Webmin to work properly, it needs to know which operating system
+type and version you are running. Please select your system type by
+entering the number next to it from the list below
+$dashes
+EOF
+{
+my $i;
+for($i=0; $i<@names; $i++) {
+       printf " %2d) %-20.20s ", $i+1, $names[$i];
+       print "\n" if ($i%3 == 2);
+       }
+print "\n" if ($i%3);
+}
+print $dashes,"\n";
+print "Operating system: ";
+chop($osnum = <STDIN>);
+if ($osnum !~ /^\d+$/) {
+       print "ERROR: You must enter the number next to your operating\n";
+       print "system, not its name or version number.\n\n";
+       exit 9;
+       }
+if ($osnum < 1 || $osnum > @names) {
+       print "ERROR: $osnum is not a valid operating system number.\n\n";
+       exit 10;
+       }
+print "\n";
+
+# Ask for the operating system version
+my $name = $names[$osnum-1];
+print <<EOF;
+Please enter the version of $name you are running
+EOF
+print "Version: ";
+chop($vnum = <STDIN>);
+if ($vnum !~ /^\S+$/) {
+       print "ERROR: An operating system number cannot contain\n\n";
+       print "spaces. It must be like 2.1 or ES4.0.\n";
+       exit 10;
+       }
+print "\n";
+return [ $name, $vnum,
+         $NAMES_TO_REAL{$name}, $vnum ];
+}
+
+# write_file($out, $os_type, $os_version, $real_os_type, $real_os_version)
+# Write the name, version and real name and version to a file
+sub write_file {
+my ($out, $os_type, $os_version, $real_os_type, $real_os_version) = @_;
+open(OUT, ">$out") or die "Failed to open $out for writing.";
+print OUT "os_type='",$os_type,"'\n";
+print OUT "os_version='",$os_version,"'\n";
+print OUT "real_os_type='",$real_os_type,"'\n";
+print OUT "real_os_version='",$real_os_version,"'\n";
+return close(OUT);
+}
+
+sub have_tty
+{
+# Do we have a tty?
+my $rv = system("tty >/dev/null 2>&1");
+if ($?) {
+       return 0;
+       }
+else {
+       return 1;
+       }
+}
+
+1;
+
+__END__
+
+=head1 OsChooser.pm
+
+Attempt to detect operating system and version, or ask the user to select
+from a list.  Works from the command line, for usage from shell scripts,
+or as a library for use within Perl scripts.
+
+=head2 COMMAND LINE USE
+
+OsChooser.pm os_list.txt outfile [auto] [issue]
+
+Where "auto" can be the following values:
+
+=over 4
+
+=item 0
+
+always ask user
+
+=item 1
+
+automatic, give up if fails
+
+=item 2
+
+automatic, ask user if fails
+
+=item 3
+
+automatic, ask user if fails and if a TTY
+
+=back
+
+=head2 SYNOPSIS
+
+    use OsChooser;
+    my ($os_type, $version, $real_os_type, $real_os_version) =
+       OsChooser->oschooser("os_list.txt", "outfile", $auto, [$issue]);
+
+=cut
+
index 8d10abc..6164f1b 100644 (file)
@@ -52,7 +52,7 @@ White Dwarf Linux             2.1.0   slackware-linux 8.1     `cat /tmp/wd/version 2>/dev/null` =
 Slamd64 Linux                  $1      slackware-linux 11.1    `cat /etc/slamd64-version 2>/dev/null` =~ /\s([0-9\.]+)/
 
 # Slackware Linux
-Slackware Linux                        $1      slackware-linux $1      `cat /etc/slackware-version 2>/dev/null` =~ /([0-9\.]+)/
+Slackware Linux                        $1      slackware-linux $1      `cat /etc/slackware-version 2>/dev/null` =~ /Slackware ([0-9\.]+)/i
 
 # Debian clones
 Xandros Linux                  2.0     debian-linux    3.0     $etc_issue =~ /Xandros.*\s2\.0/i