#!/pro/bin/perl

use strict;
use warnings;
use Sys::Hostname;

use Getopt::Long qw(:config bundling );
my $opt_t = 0;
my $opt_v = 1;
GetOptions (
    "t"   => \$opt_t,	# Separate field with tabs
    "v:2" => \$opt_v,
    ) or die "usage: ux [ -t ] [ -v [<level>] ]\n";

my @up_cpu;
eval {	# Use Unix::Processors if I have it
    require Unix::Processors;
    unless ($@) {
	push @up_cpu, {
	    id		=> $_->id,
	    state	=> $_->state,
	    type	=> $_->type,
	    clock	=> $_->clock,
	    } for @{Unix::Processors->new()->processors};
	}
    };

select ((select (STDERR), $| = 1)[0]);
$opt_v and print STDERR "h";
my ($host, $model, $arch, $os, $users, $cpu, $hw64, $k64, $mem, $ncpu) =
    (hostname, "", "", "", "?", "", 32, 32, 0, 0);
my $sep = $opt_t ? "\t" : " ";

if ($^O eq "hpux") {
    my @mi;
    my $mi_cmd = "/usr/contrib/bin/machinfo";
    $opt_v > 8 and print STDERR "Call $mi_cmd ...\n";
    -x $mi_cmd and chomp (@mi = `$mi_cmd`);
    $opt_v and print STDERR "m";
    $opt_v > 8 and print STDERR "Call model ...\n";
    ($model = `model`) =~ s/\s+$//;
    (my $m  = $model) =~ s:.*/::;
    $model =~ s/.* //;
    $opt_v and print STDERR "c";
    my @cpu;
    @cpu = grep s/^\s*processor model:\s+\d+\s+(.*) (?:processor|series)/$1/i,         @mi or
    @cpu = grep s/^\s*[0-9]+\s+(intel.r.*processor)\s*\(([0-9.]+\s*[GM]Hz).*/$1\@$2/i, @mi or
    @cpu = grep s/^\s*intel.r.\s*(.*?)\s*\(?([0-9.]+\s*[GM]Hz).*/$1\@$2/i, @mi;
    # http://wtec.cup.hp.com/~cpuhw/hppa/hversions.html
    # http://www.openpa.net/cpu.html#table
    if (@cpu == 0) {
	$opt_v > 8 and print "Scanning sched.models in /usr/sam ...\n";
	if (open my $lst, "<", "/usr/sam/lib/mo/sched.models") {
	    @cpu = grep m/$m/i, <$lst>;
	    }
	}
    if (@cpu == 0) {
	$opt_v > 8 and print "Scanning sched.models in /opt/langtools ...\n";
	if (open my $lst, "<", "/opt/langtools/lib/sched.models") {
	    @cpu = grep m/$m/i, <$lst>;
	    }
	}
    if (@cpu == 0) {
	$opt_v > 8 and print STDERR "Call cstm ...\n";
	if (open my $lst, "echo 'sc product cpu;il' | /usr/sbin/cstm |") {
	    while (<$lst>) {
		s/^\s*(PA)\s*(\d+)\s+CPU Module.*/$m 1.1 $1$2/ or next;
		$2 =~ m/^8/ and s/ 1.1 / 2.0 /;
		push @cpu, $_;
		}
	    }
	}
    for (@cpu) {
	s: series processor::;
	s:@([0-9.]+)\s*([GM])Hz.*:: or next;
	my $speed = $1;
	$2 eq "G" and $speed = int ($speed * 1024);
	$_ .= "/$speed";
	}
    if (my ($arc) = grep s/^\s*machine\s*[:=]\s*(\S+)/$1/i, @mi) {
	$arch = $arc;
	$cpu  = $cpu[0] // "?";
	@cpu  = ();
	if (my $speed = (grep s:^\s*Clock speed =\s+(\d+)\s*([MG])Hz:$1/$2:i, @mi)[0]) {
	    $speed =~ s:/(\w)::;
	    $1 eq "G" and $speed *= 1024;
	    $cpu .= "/$speed";
	    }
	}

    for (@cpu) { # Now let's hope I get only one.
	m/^\S+\s+(\d+\.\d+)\s+(\S+)/ or next;
	($arch, $cpu) = ("pa-$1", $2);
	$opt_v and print STDERR "s";
	# From the forum
	my $cpu_info  = pack "L30", (0) x 30;
	$opt_v > 8 and print STDERR "Doing a syscall 239/10 ...\n";
	syscall (239, 10, $cpu_info, length ($cpu_info), 1, 0);
	my $cpu_ticks = (unpack "L30", $cpu_info)[26];
	if ($cpu_ticks > 0) {
	    my $speed = int ($cpu_ticks / 10000);
	    $cpu .= "/$speed";
	    }
	else {
	    $opt_v > 8 and print STDERR "Call adb for CPU speed ...\n";
	    if (open my $lst, "echo 'itick_per_usec/D' | adb -k /stand/vmunix /dev/mem |") {
		while (<$lst>) {
		    m/\b(\d+)$/ or next;
		    my $speed = 0 + $1;
		    $cpu .= "/$speed";
		    last;
		    }
		}
	    }
	}
    # Check for more CPU's
    $opt_v and print STDERR "c";
    $ncpu = (grep s/^\s*Number of CPUs\s+=\s+(\d+)/$1/, @mi)[0] ||
            (grep s/^\s*(\d+)\s+cores,.*/$1/, @mi)[0];
    $ncpu or $ncpu = 0;
    if ($ncpu == 0) {
	$opt_v > 8 and print STDERR "Call ioscan ...\n";
	if (open my $lst, "ioscan -fnkC processor |") {
	    while (<$lst>) {
		m/^processor/ and $ncpu++;
		}
	    }
	}
    unless ($ncpu) {	# not root?
	$opt_v and print STDERR "c";
	$opt_v > 8 and print STDERR "Scanning syslog.log ...\n";
	if (open my $lst, "<", "/var/adm/syslog/syslog.log") {
	    while (<$lst>) {
		m/\bprocessor$/ and $ncpu++;
		}
	    }
	}
    $opt_v and print STDERR "m";
    $mem = (grep s/^Memory\s+=\s+(\d+\s*[GM])b.*/$1/i, @mi)[0];
    $mem or $mem = "";
    $mem =~ s/ (\w)$// && lc $1 eq "g" and $mem *= 1024;
    unless ($mem) {
	my $mem_info  = pack "LI4L", (0) x 6;
	$opt_v > 8 and print STDERR "Doing a syscall 239/2 ...\n";
	syscall (239, 2, $mem_info, length ($mem_info), 1, 0);
	my ($b, $bs) = (unpack "LI4L", $mem_info)[4,5];
	$b > 0 && $bs > 0 and $mem = int (($b * $bs) / (1024 * 1024));
	}
    if (!$mem) {
	$opt_v > 8 and print STDERR "Call adb for memory ... \n";
	if (open my $lst, "echo 'memory_installed_in_machine/D' | adb -k /stand/vmunix /dev/mem |") {
	    while (<$lst>) {
		m/\b(\d+)$/ or next;
		$mem += $1 * 4 / 1024;
		}
	    }
	}
    unless ($mem) {	# Older 10.20's don't work with -k
	$opt_v and print STDERR "m";
	$opt_v > 8 and print STDERR "Call adb for memory (without -k) ... \n";
	if (open my $lst, "echo 'memory_installed_in_machine/D' | adb /stand/vmunix /dev/mem |") {
	    while (<$lst>) {
		m/\b(\d+)$/ or next;
		$mem += $1 * 4 / 1024;
		}
	    }
	}
    $opt_v and print STDERR "o";
    chomp ($os = `uname -a`); # Don't rely on %Config
    $os =~ m/^HP-UX\s+\S+\s+(\S+\s[A-Z])\s+\S+\s+\d+\s+(\S+)/ and
	($os, $users) = ($1, $2);
    if ($os =~ m/(\d+)/ and $1 >= 11) {
	$opt_v and print STDERR "k";
	chomp ($k64 = `getconf KERNEL_BITS`);
	$opt_v and print STDERR "h";
	`getconf HW_32_64_CAPABLE` =~ m/^1/ and $hw64 = 64;
	$model = "$m/$hw64";
	}
    $model =~ s/^ia64 hp server //; # Already in $arch
    $os =~ s: :/$k64 :;
    $os =~ s/^[A-Z]\./HP-UX /;
    }
if ($^O eq "aix") {
    $opt_v and print STDERR "m";
    chomp ($model = `lsattr -El sys0 -a modelname -F value`);
    $opt_v and print STDERR "o";
    chomp ($os = `env LIBPATH="" oslevel -r`);
    if ($os =~ m/^(\d+)-(\d+)$/) {
	$os = (join ".", split //, $1) . "/ML$2";
	}
    else {
	chomp ($os = `oslevel`);
	# And try figuring out at what maintainance level we are
	my $ml = "00";
	$opt_v and print STDERR "l";
	for (grep m/ML\b/, `instfix -i`) {
	    if (m/All filesets for (\S+) were found/) {
		$ml = $1;
		$ml =~ m/^\d+-(\d+)_AIX_ML/ and $ml = "ML$1";
		next;
		}
	    $ml =~ s/\+*$/+/;
	    }
	$os .= "/$ml";
	}
    $os =~ s/^/AIX /;

    $opt_v and print STDERR "p";
    if (@up_cpu) {
	$cpu = "$up_cpu[0]{type}/$up_cpu[0]{clock}";
	@up_cpu > 1 and $cpu .= "(" . (scalar @up_cpu) . ")";
	}
    elsif (open my $lst, "lsdev -C -c processor -S Available |") {
	while (<$lst>) {
	    m/Available/ or next;
	    $ncpu++;
	    $cpu and next;
	    m/^(\S+)/ or next;
	    $opt_v and print STDERR "c";
	    if (open my $type, "lsattr -E -O -l $1 |") {
		while (<$type>) {
		    m/\benable:([^:\s]+)/ or next;
		    $cpu = $1;
		    last;
		    }
		}
	    }
	}
    $opt_v and print STDERR "m";
    if (open my $lst, "lsdev -C -c memory -S Available |") {
	while (<$lst>) {
	    m/^(\S+)\s+Available.*\bMemory$/ or next;
	    $opt_v and print STDERR "m";
	    if (open my $type, "lsattr -E -O -l $1 |") {
		while (<$type>) {
		    m/^\d+:(\d+)/ or next;
		    $mem += $1;
		    last;
		    }
		}
	    }
	}
    if ($> == 0) {	# bootinfo needs root
	$opt_v and print STDERR "o";
	if ($os =~ m/(\d+)\.(\d+)/ and $1 > 4 || ($1 >= 4 && $2 >= 3)) {
	    $opt_v and print STDERR "k";
	    chomp ($k64 = `bootinfo -K 2>/dev/null`);
	    $opt_v and print STDERR "h";
	    `bootinfo -y 2>/dev/null` =~ m/64/ and $hw64 = 64;
	    $cpu .= "/$hw64";
	    }
	$os =~ s:$:/$k64 :;
	}
    }
if ($^O eq "linux") {
    $opt_v and print STDERR "m";
    my $hal = `lshal -u /org/freedesktop/Hal/devices/computer 2>/dev/null`;
    $hal =~ m/\bsystem\./ or $hal = `lshal 2>/dev/null`;
    if ($hal =~ m/\bsystem\./) {
	$hal =~ m/ \b system\.(?:hardware\.)?product \s* = \s* ' ([^']+) ' /x and
	    $model = $1;
	$model =~ s/\s+workstation\b//i;
	}
    $model or chomp ($model = `uname -i`);
    $opt_v and print STDERR "o";
    chomp ($os = `uname -s -r`);
    (my $osv = `uname -v`) =~ s/\s.*//s;
    $osv =~ m/^#[0-9]+$/ or $os .= "/$osv";
    $opt_v and print STDERR "a";
    chomp ($arch = `uname -m`);
    $opt_v and print STDERR "p";
    {   my (@cpu, $ccpu);
	if (open my $cpuinfo, "<", "/proc/cpuinfo") {
	    while (<$cpuinfo>) {
		m/^processor.*\b(\d+)/	and $ccpu = $1, next;
		m/^(.*\S)\s+:\s+(.*)/	and $cpu[$ccpu]{$1} = $2;
		}
	    close $cpuinfo;
	    }
	if (@cpu) {
	    $ncpu = scalar @cpu;
	    $cpu  = sprintf "%s/%.0f", $cpu[0]{"model name"}, $cpu[0]{"cpu MHz"};
	    }
	else {
	    chomp ($cpu = `uname -p`);
	    }
	}
    $opt_v and print STDERR "m";
    `cat /proc/meminfo` =~ m/^MemTotal:\s+(\d+)/m and
	$mem = sprintf "%.0f", $1 / 1024;

    if (my ($dist_file) = grep { -s $_ && !/\blsb-/ }
			  glob ("/etc/*[-_][rRvV][eE][lLrR]*"), "/etc/issue") {
	(my $distro = $dist_file) =~ s{^/etc/}{};
	$distro =~ s{[-_](?:release|version)\b}{}i;
	if (open my $fh, "<", $dist_file) {
	    my @osi = grep m/\S/ => <$fh>;
	    close $fh;
	    my %os = map { m/^\s*\U(\S+)\E\s*=\s*(.*)\s*$/ } @osi;
	    s/^"\s*(.*?)\s*"$/$1/ for values %os;
	    if ($os{PRETTY_NAME}) {
		$distro = $os{PRETTY_NAME}; # "openSUSE 12.1 (Asparagus) (x86_64)"
		$distro =~ s/\)\s+\(\w+\)\s*$/)/; # remove architectural part
		}
	    elsif ($os{VERSION} && $os{NAME}) {
		$distro = qq{$os{NAME} $os{VERSION}};
		}
	    elsif ($os{VERSION} && $os{CODENAME}) {
		$distro .= qq{ $os{VERSION} "$os{CODENAME}"};
# I prefer the first line of /etc/SuSE-release if it has *only* VERSION as field
#		}
#           elsif ($os{VERSION}) {
#		$distro .= qq{ $os{VERSION}};
		}
	    elsif (@osi && $osi[0] =~ m{^\s*([-A-Za-z0-9. ""/]+)}) {
		# /etc/issue:
		#  Welcome to openSUSE 11.1 - Kernel \r (\l).
		#  Welcome to openSUSE 11.2 "Emerald" - Kernel \r (\l).
		#  Welcome to openSUSE 11.3 "Teal" - Kernel \r (\l).
		#  Welcome to openSUSE 11.4 "Celadon" - Kernel \r (\l).
		#  Welcome to openSUSE 12.1 "Asparagus" - Kernel \r (\l).
		#  Welcome to openSUSE 12.2 "Mantis" - Kernel \r (\l).
		#  Welcome to openSUSE 12.3 "Dartmouth" - Kernel \r (\l).
		#  Welcome to SUSE Linux Enterprise Server 11 SP1 for VMware  (x86_64) - Kernel \r (\l).
 		#  Ubuntu 10.04.4 LTS \n \l
		#  Debian GNU/Linux wheezy/sid \n \l
		#  Debian GNU/Linux 6.0 \n \l
		# /etc/redhat-release:
		#  CentOS release 5.7 (Final)
		#  Red Hat Enterprise Linux ES release 4 (Nahant Update 2)
		# /etc/debian_version:
		#  6.0.4
		#  wheezy/sid
		#  squeeze/sid
		($distro = $1) =~ s/^Welcome\s+to\s+//i;
		$distro =~ s/\s+-\s+Kernel.*//i;
		}
	    }
	$distro =~ s/^\s*(.*\S)\s*$/$1/ and $os .= " [$distro]";
	}
    }

$cpu =~ s/\bIntel\(R\)\s+//i;
$cpu =~ s/\s+processor\s+/ /i;
$ncpu > 1 and $cpu .= "($ncpu)";
$mem ||= "";
$mem and $mem .= " Mb";

$opt_t and $os =~ s/\s+/\t/;
$arch = join $sep, grep m/\S/, $model, $cpu, $arch;
$sep =~ s/ /  /;
$opt_v and print STDERR "\r                      \r";
s/ +/ /g for $host, $os, $arch, $mem;
print join $sep, $host, $os, $arch, "$mem\n";

__END__

HP-UX 11.23 (Itanium)

----- rx2600 -------------------------------------------------------------------
rx2600:/ # /usr/contrib/bin/machinfo
CPU info:
   Number of CPUs = 2
   Clock speed = 1500 MHz
   CPUID registers
      vendor information =       "GenuineIntel"
      processor serial number =  0x0000000000000000
      processor version info =   0x000000001f010504
         architecture revision:       0
         processor family:           31   Intel(R) Itanium 2 Family Processors
         processor model:             1   Intel(R) Itanium 2 processor
         processor revision:          5   stepping B1
         largest CPUID reg:           4
      processor capabilities =   0x0000000000000001
         implements long branch:      1
   Bus features
      implemented =  0xbdf0000060000000
      selected    =  0x0000000040000000
         Bus Lock Signal masked

Cache info:
   L1 Instruction: size =   16 KB, associativity = 4
   L1 Data:        size =   16 KB, associativity = 4
   L2 Unified:     size =  256 KB, associativity = 8
   L3 Unified:     size = 6144 KB, associativity = 24

Memory = 4084 MB (3.988281 GB)

Firmware info:
   Firmware revision = 02.21
   FP SWA driver revision: 1.18
   IPMI is supported on this system.
   ERROR: Unable to obtain manageability firmware revision info.

Platform info:
   model string =          "ia64 hp server rx2600"
   machine id number =     7ea3cef8-4491-11d8-8973-de7adaca906d
   machine serial number = US35074826

OS info:
   sysname  = HP-UX
   nodename = spe173
   release  = B.11.23
   version  = U (unlimited-user license)
   machine  = ia64
   idnumber = 2124664568
   vmunix _release_version:
@(#) $Revision: vmunix:    B11.23_LR FLAVOR=perf Fri Aug 29 22:35:38 PDT 2003 $
rx2600:/ # model
ia64 hp server rx2600
rx2600:/ #

----- rx2800 -------------------------------------------------------------------
rx2800:/ # /usr/contrib/bin/machinfo
CPU info:
   Intel(R)  Itanium(R)  Processor 9320 (1.33 GHz, 16 MB)
   4 cores, 8 logical processors per socket
   4.79 GT/s QPI, CPU version E0
          Active processor count:
          1 socket
          4 cores (4 per socket)
          4 logical processors (4 per socket)
          LCPU attribute is disabled

Memory: 8091 MB (7.9 GB)

Firmware info:
   Firmware revision:  01.90
   FP SWA driver revision: 1.18
   IPMI is supported on this system.
   BMC firmware revision: 1.51

Platform info:
   Model:                  "ia64 hp Integrity rx2800 i2"
   Machine ID number:      70e84dfd-e14d-11e1-a0b1-11b55753b811
   Machine serial number:  CZ3227NEXS

OS info:
   Nodename:  rx2800
   Release:   HP-UX B.11.31
   Version:   U (unlimited-user license)
   Machine:   ia64
   ID Number: 1894272509
   vmunix _release_version:
@(#) $Revision: vmunix:    B.11.31_LR FLAVOR=perf
rx2800:/ # model
ia64 hp Integrity rx2800 i2

