#!/pro/bin/perl

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

use Getopt::Long;
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;
    -x "/usr/contrib/bin/machinfo" and chomp (@mi = `/usr/contrib/bin/machinfo`);
    $opt_v and print STDERR "m";
    ($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;
    # http://wtec.cup.hp.com/~cpuhw/hppa/hversions.html
    # http://www.openpa.net/cpu.html#table
    if (@cpu == 0 && open my $lst, "<", "/usr/sam/lib/mo/sched.models") {
	@cpu = grep m/$m/i, <$lst>;
	}
    if (@cpu == 0 && open my $lst, "<", "/opt/langtools/lib/sched.models") {
	@cpu = grep m/$m/i, <$lst>;
	}
    if (@cpu == 0 && 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;
	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";
	    }
	elsif (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];
    $ncpu or $ncpu = 0;
    if ($ncpu == 0 && open my $lst, "ioscan -fnkC processor |") {
	while (<$lst>) {
	    m/^processor/ and $ncpu++;
	    }
	}
    unless ($ncpu) {	# not root?
	$opt_v and print STDERR "c";
	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;
	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 && 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";
	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;
    $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) {
	    $cpu = sprintf "%s/%.0f(%d)",
		$cpu[0]{"model name"}, $cpu[0]{"cpu MHz"}, scalar @cpu;
	    }
	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;
    }

$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)

spe173:/house/procura 4 > /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 $
spe173:/house/procura 5 > model
ia64 hp server rx2600
spe173:/house/procura 6 >
