#!/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() { 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 <); 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 <); 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