#!/usr/bin/perl

# imvirt - I'm virtualized?
#
# $Id: imvirt 411 2009-09-07 18:25:54Z liske $
#
# Authors:
#   Thomas Liske <liske@ibh.de>
#
# Copyright Holder:
#   2008-2009 (C) IBH IT-Service GmbH [http://www.ibh.de/]
#
# License:
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this package; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
#

##
#
# Outputs:
#   Xen
#   Xen 3.x (PV|HVM)
#   VirtualBox
#   Virtual Machine
#   VMware
#   VMware (Express|ESX Server|GSX Server|Workstation)
#   QEMU
#   OpenVZ
#   UML
#   HVM: <signature>
#
# If no virtualization has been detected:
#   Physical
#
##

use strict;
use warnings;

# check for UML
unless(system('grep -q "User Mode Linux" /proc/cpuinfo') >> 8) {
    print "UML\n";
    exit;
}


my $helper = '/usr/lib/imvirt/detect';

if(-x $helper) {
    my $ret = system($helper) >> 8;
    exit(0) if($ret);
}

my $dmidecode = '/usr/sbin/dmidecode';

my $dmesg = '/bin/dmesg | head -n 750';

# OpenVZ/Virtuozzo
# taken from virt-what (Evgeniy Sokolov)
# /proc/vz - exists if OpenVZ kernel is running
# /proc/bc - exists on nodes, but not inside containers
if(-d '/proc/vz' && ! -d '/proc/bc') {
    print "OpenVZ\n";
    exit 0;
}

# paravirtualized oldstyle Xen - very simple ;)
if(-d '/proc/xen') {
    print "Xen\n";
    exit 0;
}

# newstyle Xen
if(-r '/sys/devices/system/clocksource/clocksource0/available_clocksource') {
    if(`cat /sys/devices/system/clocksource/clocksource0/available_clocksource` =~ /xen/) {
	print "Xen\n";
	exit 0;
    }
}

# dmidecode needs root to work :(
if (-r '/dev/mem' && -x $dmidecode) {
    my $sysprod = `$dmidecode -s system-product-name`;
    if ($sysprod =~ /^VMware/) {
	print "VMware\n";
	exit 0;
    }

    if ($sysprod =~ /^Virtual Machine/) {
	print "Virtual Machine\n";
	exit 0;
    }

    my $biosvend = `$dmidecode -s bios-vendor`;
    if ($biosvend =~ /^QEMU/) {
	print "QEMU\n";
	exit 0;
    }

    # virtualized Xen
    if ($biosvend =~ /^Xen/) {
	print "Xen\n";
	exit 0;
    }
}

# Parse loaded modules
my %modmap = (
    '^vmxnet\s' => 'VMware',
    '^xen_\w+front\s' => 'Xen',
);

if (open(HMODS, '/proc/modules')) {
    while(<HMODS>) {
	foreach my $str (keys %modmap) {
	    if (/$str/) {
		print "$modmap{$str}\n";
		exit 0;
	    }
	}
    }
    close(HMODS);
}

# Let's parse some logs & /proc files for well known strings
my %msgmap = (
    'VMware vmxnet virtual NIC driver' => 'VMware',
    'Vendor: VMware\s+Model: Virtual disk' => 'VMware',
    'Vendor: VMware,\s+Model: VMware Virtual ' => 'VMware',
    ': VMware Virtual IDE CDROM Drive' => 'VMware',

    ' QEMUAPIC ' => 'QEMU',
    'QEMU Virtual CPU' => 'QEMU',
    ': QEMU HARDDISK,' => 'QEMU',
    ': QEMU CD-ROM,' => 'QEMU',

    'Virtual HD' => 'Virtual Machine',
    'Virtual CD' => 'Virtual Machine',

    ' VBOXBIOS ' => 'VirtualBox',
    ': VBOX HARDDISK,' => 'VirtualBox',
    ': VBOX CD-ROM,' => 'VirtualBox',

    'Hypervisor signature: xen' => 'Xen',
    'Booting paravirtualized kernel on Xen' => 'Xen',
    'Xen virtual console successfully installed' => 'Xen',
    'Xen reported:' => 'Xen',
    'Xen: \d+ - \d+' => 'Xen',
    'xen-vbd: registered block device' => 'Xen',
    'ACPI: RSDP \(v\d+\s+Xen ' => 'Xen',
    'ACPI: XSDT \(v\d+\s+Xen ' => 'Xen',
    'ACPI: FADT \(v\d+\s+Xen ' => 'Xen',
    'ACPI: MADT \(v\d+\s+Xen ' => 'Xen',
    'ACPI: HPET \(v\d+\s+Xen ' => 'Xen',
    'ACPI: SSDT \(v\d+\s+Xen ' => 'Xen',
    'ACPI: DSDT \(v\d+\s+Xen ' => 'Xen',

    'UML Watchdog Timer' => 'UML',
);

if (open(HDMSG, '/var/log/dmesg')) {
    while(<HDMSG>) {
	foreach my $str (keys %msgmap) {
	    if (/$str/) {
		print "$msgmap{$str}\n";
		exit 0;
	    }
	}
    }
    close(HDMSG);
}

# Read kernel ringbuffer directly
if (open(HDMSG, "$dmesg |")) {
    while(<HDMSG>) {
	foreach my $str (keys %msgmap) {
	    if (/$str/) {
		print "$msgmap{$str}\n";
		exit 0;
	    }
	}
    }
    close(HDMSG);
}

# scan SCSI devices
if (open(HSCSI, '/proc/scsi/scsi')) {
    while(<HSCSI>) {
	foreach my $str (keys %msgmap) {
	    if (/$str/) {
		print "$msgmap{$str}\n";
		exit 0;
	    }
	}
    }
    close(HSCSI);
}

# scan IDE devices
foreach my $hd (</proc/ide/hd*/model>) {
    if (open(HSCSI, $hd)) {
	while(<HSCSI>) {
	    foreach my $str (keys %msgmap) {
		if (/$str/) {
		    print "$msgmap{$str}\n";
		    exit 0;
	        }
	    }
	}
        close(HSCSI);
    }
}

print "Physical\n";
exit 0;
