ogboot/client/engine/Inventory.lib

529 lines
20 KiB
Bash

#!/bin/bash
#/**
#@file Inventory.lib
#@brief Librería o clase Inventory
#@class Inventory
#@brief Funciones para recogida de datos de inventario de hardware y software de los clientes.
#@version 1.1.0
#@warning License: GNU GPLv3+
#*/
#/**
# ogGetArch
#@brief Devuelve el tipo de arquitectura del cliente.
#@return str_arch - Arquitectura (i386 para 32 bits, x86_64 para 64 bits).
#@version 0.9.2 - Primera versión para OpenGnSys.
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2010-07-17
#*/
function ogGetArch ()
{
if [ "$*" == "help" ]; then
ogHelp "$FUNCNAME" "$FUNCNAME" "$FUNCNAME => x86_64"
return
fi
[ -d /lib64 ] && echo "x86_64" || echo "i386"
}
#/**
# ogGetOsType int_ndisk int_npartition
#@brief Devuelve el tipo del sistema operativo instalado.
#@param int_ndisk nº de orden del disco
#@param int_npartition nº de orden de la partición
#@return OSType - Tipo de sistema operativo.
#@see ogGetOsVersion
#*/ ##
function ogGetOsType ()
{
# Si se solicita, mostrar ayuda.
if [ "$*" == "help" ]; then
ogHelp "$FUNCNAME" "$FUNCNAME int_ndisk int_npartition" \
"$FUNCNAME 1 2 => Linux"
return
fi
ogGetOsVersion "$@" | cut -sf1 -d:
}
#/**
# ogGetOsUuid int_ndisk int_nfilesys
#@brief Devuelve el UUID del sistema operativo instalado en un sistema de archivos.
#@param int_ndisk nº de orden del disco
#@param int_nfilesys nº de orden de la partición
#@return str_uuid - UUID del sistema operativo.
#@exception OG_ERR_FORMAT Formato incorrecto.
#@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositiv
#@version 1.1.0 - Primera versión para OpenGnsys
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2015-09-09
#*/ ##
function ogGetOsUuid ()
{
# Variables locales.
local MNTDIR
# Si se solicita, mostrar ayuda.
if [ "$*" == "help" ]; then
ogHelp "$FUNCNAME" "$FUNCNAME int_ndisk int_nfilesys" \
"$FUNCNAME 1 2 => 540e47c6-8e78-4178-aa46-042e4803fb16"
return
fi
# Error si no se reciben 2 parametros.
[ $# = 2 ] || ogRaiseError $OG_ERR_FORMAT || return $?
# Montar la particion, si no lo estaba previamente.
MNTDIR=$(ogMount $1 $2) || return $?
# Obtener UUID según el tipo de sistema operativo.
case "$(ogGetOsType $1 $2)" in
Linux)
# Leer el UUID del sistema de ficheros raíz o el fichero de identificador.
findmnt -no UUID $MNTDIR 2>/dev/null || cat $MNTDIR/etc/machine-id 2>/dev/null
;;
Windows)
# Leer identificador en clave de registro.
ogGetRegistryValue $MNTDIR SOFTWARE '\Microsoft\Cryptography\MachineGuid' 2>/dev/null
;;
esac
}
#/**
# ogGetSerialNumber
#@brief Obtiene el nº de serie del cliente.
#@version 1.1.0 - Primeras versión con OpenGnsys
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2015-06-08
#*/ ##
function ogGetSerialNumber ()
{
# Variables locales.
local SERIALNO
# Si se solicita, mostrar ayuda.
if [ "$*" == "help" ]; then
ogHelp "$FUNCNAME" "$FUNCNAME" "$FUNCNAME => 123456"
return
fi
# Obtener nº de serie (ignorar los no especificados).
SERIALNO=$(dmidecode -s system-serial-number | egrep -vi "(^[ 0]+$|not specified|to be filled|invalid entry|default string)")
# Quitar espacios y truncar cadena si >25 caracteres.
SERIALNO="${SERIALNO// /}"
[ ${#SERIALNO} -gt 25 ] && SERIALNO="${SERIALNO:0:22}..."
[ -n "$SERIALNO" ] && echo "$SERIALNO"
return 0
}
#/**
# ogIsEfiActive
#@brief Comprueba si el sistema tiene activo el arranque EFI.
#*/ ##
function ogIsEfiActive ()
{
test -d /sys/firmware/efi
}
#/**
# ogListHardwareInfo
#@brief Lista el inventario de hardware de la máquina cliente.
#@return TipoDispositivo:Modelo (por determinar)
#@warning Se ignoran los parámetros de entrada.
#@note TipoDispositivo = { bio, boa, bus, cha, cdr, cpu, dis, fir, mem, mod, mul, net, sto, usb, vga }
#@note Requisitos: dmidecode, lshw, awk
#@version 0.1 - Primeras pruebas con OpenGnSys
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2009-07-28
#@version 1.1.0 - Incluir nuevos componentes al inventario.
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2014-04-23
#*/ ##
function ogListHardwareInfo ()
{
# Si se solicita, mostrar ayuda.
if [ "$*" == "help" ]; then
ogHelp "$FUNCNAME" "$FUNCNAME"
return
fi
# Recopilación de dispositivos procesando la salida de \c lshw
ogEcho info "$MSG_HARDWAREINVENTORY}"
echo "cha=$(dmidecode -s chassis-type)" | grep -v "Other"
[ -e /sys/firmware/efi ] && echo "boo=UEFI" || echo "boo=BIOS"
lshw | awk 'BEGIN {type="mod";}
/product:/ {sub(/ *product: */,""); prod=$0;}
/vendor:/ {sub(/ *vendor: */,""); vend=$0;}
/version:/ {sub(/ *version: */,"v.");vers=$0;}
/size:/ {size=$2;}
/clock:/ {clock=$2;}
/slot:/ {sub(/ *slot: */,""); slot=$0;}
/\*-/ {if (type=="mem"){
if (size!=""){
numbank++;
print type"="vend,prod,size,clock" ("slot")";}
}else{
if (type=="totalmem"){
if (size!=""){
totalmemory="mem="size;}
}else{
if (type!="" && prod!=""){
if (prod=="v."vers)
vers="";
print type"="vend,prod,size,vers;} }
}
type=prod=vend=vers=size=clock=slot="";}
$1~/-core/ {type="boa";}
$1~/-firmware/ {type="bio";}
$1~/-cpu/ {type="cpu";}
$1~/-bank/ {type="mem";}
$1~/-memory/ {type="totalmem";}
$1~/-ide/ {type="ide";}
$1~/-storage/ {type="sto";}
$1~/-disk/ {type="dis";}
$1~/-cdrom/ {type="cdr";}
$1~/-display/ {type="vga";}
$1~/-network/ {type="net";}
$1~/-multimedia/ {type="mul";}
$1~/-usb/ {type="usb";}
$1~/-firewire/ {type="fir";}
$1~/-serial/ {type="bus";}
END {if (type!="" && prod!="")
print type"="vend,prod,size,vers;
if (length(numbank)==0 && length(totalmemory)>=4)
print totalmemory; }
'
# */ (comentario para Doxygen)
}
#/**
# ogListSoftware int_ndisk int_npartition
#@brief Lista el inventario de software instalado en un sistema operativo.
#@param int_ndisk nº de orden del disco
#@param int_npartition nº de orden de la partición
#@return programa versión ...
#@warning Se ignoran los parámetros de entrada.
#@note Requisitos: ...
#@todo Detectar software en Linux
#@version 0.1 - Primeras pruebas con OpenGnSys
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2009-09-23
#@version 1.0.5 - Aproximación para inventario de software de Mac OS.
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2013-10-08
#@version 1.0.6 - Proceso depende del tipo de SO y soporte para FreeBSD.
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2014-11-13
#@version 1.1.0 - Se muestra el sistema operativo en la primera línea de la salida
#@author Irina Gomez, ETSII Universidad de Sevilla
#@date 2016-04-26
#*/ ##
function ogListSoftware ()
{
# Variables locales.
local APPS HIVE k KEYS KEYS32 MNTDIR PKGDIR PROG VERS TMPFILE TYPE
# Si se solicita, mostrar ayuda.
if [ "$*" == "help" ]; then
ogHelp "$FUNCNAME" "$FUNCNAME 1 1"
return
fi
# Error si no se reciben 2 parametros.
[ $# = 2 ] || ogRaiseError $OG_ERR_FORMAT || return $?
# Obtener tipo de sistema de archivos y montarlo.
MNTDIR=$(ogMount $1 $2) || return $?
TYPE=$(ogGetOsType $1 $2) || return $?
# Ficheros temporales.
APPS=$(mktemp /tmp/apps.XXXXX)
TMPFILE=$(mktemp /tmp/tmp.XXXXX)
trap "rm -f $APPS $TMPFILE" 1 2 3 9 15
case "$TYPE" in
Linux) # Software de GNU/Linux.
# Procesar paquetes dpkg.
PKGDIR="${MNTDIR}/var/lib/dpkg"
if [ -r $PKGDIR ]; then
# Proceso de fichero en sistemas de 64 bits.
awk '/Package:/ {if (pack!="") print pack,vers;
sub(/-dev$/,"",$2);
pack=$2}
/Version:/ {sub(/^.*:/,"",$2); sub(/-.*$/,"",$2);
vers=$2}
/Status:/ {if ($2!="install") pack=vers=""}
END {if (pack!="") print pack,vers}
' $PKGDIR/status > $APPS
fi
# Procesar paquetes RPM.
PKGDIR="${MNTDIR}/var/lib/rpm"
if [ -r $PKGDIR ]; then
# Listar si está instalado el paquete "rpm" en el cliente.
if which rpm &>/dev/null; then
rm -f ${PKGDIR}/__db.*
rpm --dbpath $PKGDIR -qa --qf "%{NAME} %{VERSION}\n" 2>/dev/null | \
awk '$1!~/-devel$/ {sub(/-.*$/,"",$2); print $0}' > $APPS
rm -f ${PKGDIR}/__db.*
else
# Obtener el nombre de cada paquete en la BD de RPM.
python <<<"
import re;
import bsddb;
db=bsddb.hashopen('$PKGDIR/Name','r');
for k in db.keys():
print re.sub('-devel$','',k);" > $APPS
fi
fi
# Procesar paquetes pacman.
PKGDIR="${MNTDIR}/var/lib/pacman/local"
if [ -r $PKGDIR ]; then
ls $PKGDIR | awk -F- '/-/ {print gensub(/-/, " ", NF-2);}' > $APPS
fi
# Procesar aplicaciones Snappy.
PKGDIR="${MNTDIR}/snap"
find $PKGDIR/*/current/meta -name snap.yaml -exec \
awk '/name:/ {pack=$2}
/version:/ {vers=$2}
END {if (pack!="") print pack,"(snap)",vers}' {} 2>/dev/null \; >> $APPS
# Procesar aplicaciones Flatpak.
PKGDIR="${MNTDIR}/var/lib/flatpak"
ls -1 $PKGDIR/app/*/current/active/deploy 2> /dev/null | python -c "
import sys
for f in sys.stdin:
p = open(f.strip()).read().split('\0')
try:
if(p[0] != 'flathub'):
raise ValueError
print('{} (flatpak) {}'.format(p[p.index('appdata-name') + 4], p[p.index('appdata-version') + 1]))
except ValueError:
pass
" >> $APPS
;;
Windows) # Software de Windows.
# Comprobar tipo de proceso del registro de Windows.
if which hivexregedit &>/dev/null; then
# Nuevo proceso más rápido basado en "hivexregedit".
HIVE=$(ogGetHivePath $MNTDIR software 2>/dev/null)
if [ -n "$HIVE" ]; then
# Claves de registro para programas instalados.
hivexregedit --unsafe-printable-strings --export "$HIVE" '\Microsoft\Windows\CurrentVersion\Uninstall' > $TMPFILE 2>/dev/null
hivexregedit --unsafe-printable-strings --export "$HIVE" '\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall' >> $TMPFILE 2>/dev/null
# Mostrar los valores "DisplayName" y "DisplayVersion" para cada clave.
awk -F\" '$1~/^\[/ {n=""}
$2~/DisplayName/ {n=$4}
$2~/DisplayVersion/ {print n,$4}
' $TMPFILE > $APPS
fi
else
# Compatibilidad con clientes ogLive antiguos.
# Claves de registro para programas instalados: formato "{clave}".
KEYS=$(ogListRegistryKeys $MNTDIR software '\Microsoft\Windows\CurrentVersion\Uninstall')
KEYS32=$(ogListRegistryKeys $MNTDIR software '\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')
# Mostrar los valores "DisplayName" y "DisplayVersion" para cada clave.
for k in $KEYS; do
PROG=$(ogGetRegistryValue $MNTDIR software "\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$k\\DisplayName")
if [ -n "$PROG" ]; then
VERS=$(ogGetRegistryValue $MNTDIR software "\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$k\\DisplayVersion")
echo "$PROG $VERS"
fi
done > $APPS
for k in $KEYS32; do
PROG=$(ogGetRegistryValue $MNTDIR software "\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$k\\DisplayName")
if [ -n "$PROG" ]; then
VERS=$(ogGetRegistryValue $MNTDIR software "\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$k\\DisplayVersion")
echo "$PROG $VERS"
fi
done >> $APPS
fi
;;
MacOS) # Software de Mac OS.
# Listar directorios de aplicaciones e intentar obtener la versión del fichero .plist (tanto original como descomprimido).
find "${MNTDIR}/Applications" -type d -name "*.app" -prune -print | \
while read k; do
FILE="$k/Contents/version.plist"
[ -s "$FILE" ] || FILE="$k/Contents/version.plist.uncompress"
[ -s "$FILE" ] && VERSION=$(awk -F"[<>]" '/ShortVersionString/ {getline;v=$3}
END {print v}' "$FILE")
echo "$(basename "$k" .app) $VERSION"
done > $APPS
;;
BSD) # Software de FreeBSD.
sqlite3 $MNTDIR/var/db/pkg/local.sqlite <<<"SELECT name FROM pkg_search;" 2>/dev/null | \
sed 's/\(.*\)-\(.*\)/\1 \2/g' > $APPS
;;
*) ogRaiseError $OG_ERR_NOTOS "$1, $2 ${TYPE+($TYPE)}"
return $? ;;
esac
# Mostrar sistema Operativo y aplicaciones.
ogGetOsVersion $1 $2 | awk -F: '{print $2}'
sort $APPS | uniq | iconv -ct utf-8
rm -f $APPS $TMPFILE
}
#/**
# ogGetOsVersion int_ndisk int_nfilesys
#@brief Devuelve la versión del sistema operativo instalado en un sistema de archivos.
#@param int_ndisk nº de orden del disco
#@param int_nfilesys nº de orden de la partición
#@return OSType:OSVersion - tipo y versión del sistema operativo.
#@note OSType = { Android, BSD, GrubLoader, Hurd, Linux, MacOS, Solaris, Windows, WinLoader }
#@note Requisitos: awk, head, chroot
#@exception OG_ERR_FORMAT Formato incorrecto.
#@exception OG_ERR_NOTFOUND Disco o partición no corresponden con un dispositiv
#@exception OG_ERR_PARTITION Fallo al montar el sistema de archivos.
#@version 0.9 - Primera versión para OpenGnSys
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2009-09-15
#@version 1.0.4 - Incluir tipos BSD, MacOS y Solaris.
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2012-06-29
#@version 1.0.5 - Incluir tipos GrubLoader, Hurd y WinLoader, leer por defecto fichero /etc/os-release.
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2013-10-07
#@version 1.0.6 - Detectar GrubLoader al final y sistemas basados en EFI.
#@author Ramon Gomez, ETSII Universidad de Sevilla
#@date 2014-08-27
#*/ ##
function ogGetOsVersion ()
{
# Variables locales.
local MNTDIR TYPE DISTRIB VERSION IS64BIT FILE
# Si se solicita, mostrar ayuda.
if [ "$*" == "help" ]; then
ogHelp "$FUNCNAME" "$FUNCNAME int_ndisk int_nfilesys" \
"$FUNCNAME 1 2 => Linux:Ubuntu precise (12.04 LTS) 64 bits"
return
fi
# Error si no se reciben 2 parametros.
[ $# = 2 ] || ogRaiseError $OG_ERR_FORMAT || return $?
# Montar la particion, si no lo estaba previamente.
MNTDIR=$(ogMount $1 $2) || return $?
# Buscar tipo de sistema operativo.
# Para GNU/Linux: leer descripción.
TYPE="Linux"
FILE="$MNTDIR/etc/os-release"
[ -r $FILE ] && VERSION="$(awk -F= '$1~/PRETTY_NAME/ {gsub(/\"/,"",$2); print $2}' $FILE)"
# Si no se puede obtener, buscar en ficheros del sistema.
if [ -z "$VERSION" ]; then
FILE="$MNTDIR/etc/lsb-release"
[ -r $FILE ] && VERSION="$(awk -F= '$1~/DESCRIPTION/ {gsub(/\"/,"",$2); print $2}' $FILE)"
for DISTRIB in redhat SuSE mandrake gentoo; do
FILE="$MNTDIR/etc/${DISTRIB}-release"
[ -r $FILE ] && VERSION="$(head -1 $FILE)"
done
FILE="$MNTDIR/etc/arch-release"
[ -r $FILE ] && VERSION="Arch Linux"
FILE="$MNTDIR/etc/slackware-version"
[ -r $FILE ] && VERSION="Slackware $(cat $FILE)"
fi
# Si no se encuentra, intentar ejecutar "lsb_release".
[ -z "$VERSION" ] && VERSION=$(chroot $MNTDIR lsb_release -d 2>/dev/null | awk -F":\t" '{print $2}')
# Comprobar Linux de 64 bits.
[ -n "$VERSION" ] && [ -e $MNTDIR/lib64 ] && IS64BIT="$MSG_64BIT"
# Para Android, leer fichero de propiedades.
if [ -z "$VERSION" ]; then
TYPE="Android"
FILE="$MNTDIR/android*/system/build.prop"
[ -r $FILE ] && VERSION="Android $(awk -F= '$1~/(product.brand|build.version.release)/ {print $2}' $FILE | tr '\n' ' ')"
[ -e $MNTDIR/lib64 ] && IS64BIT="$MSG_64BIT"
fi
# Para GNU/Hurd, comprobar fichero de inicio (basado en os-prober).
if [ -z "$VERSION" ]; then
TYPE="Hurd"
FILE="$MNTDIR/hurd/init"
[ -r $FILE ] && VERSION="GNU/Hurd"
fi
# Para Windows: leer la version del registro.
if [ -z "$VERSION" ]; then
TYPE="Windows"
FILE="$(ogGetHivePath $MNTDIR SOFTWARE)"
if [ -n "$FILE" ]; then
# Nuevo método más rápido para acceder al registro de Windows..
VERSION=$(echo $(hivexsh << EOT 2>/dev/null
load $FILE
cd \Microsoft\Windows NT\CurrentVersion
lsval ProductName
lsval ReleaseId
EOT
))
[ -n "$(reglookup -H -p "Microsoft/Windows/CurrentVersion/ProgramW6432Dir" "$FILE" 2>/dev/null)" ] && IS64BIT="$MSG_64BIT"
if [ -z "$VERSION" ]; then
# Compatibilidad con métrodo antiguo y más lento de acceder al registro.
VERSION=$(ogGetRegistryValue $MNTDIR software '\Microsoft\Windows NT\CurrentVersion\ProductName' 2>/dev/null)
[ -n "$(ogGetRegistryValue $MNTDIR software '\Microsoft\Windows\CurrentVersion\ProgramW6432Dir' 2>/dev/null)" ] && IS64BIT="$MSG_64BIT"
fi
fi
fi
# Para cargador Windows: buscar versión en fichero BCD (basado en os-prober).
if [ -z "$VERSION" ]; then
TYPE="WinLoader"
FILE="$(ogGetPath $MNTDIR/boot/bcd)"
[ -z "$FILE" ] && FILE="$(ogGetPath $MNTDIR/EFI/Microsoft/boot/bcd)"
if [ -n "$FILE" ]; then
for DISTRIB in "Windows Recovery" "Windows Boot"; do
if grep -aqs "$(echo "$DISTRIB" | sed 's/./&./g')" $FILE; then
VERSION="$DISTRIB loader"
fi
done
fi
fi
# Para macOS: detectar kernel y completar con fichero plist de información del sistema.
if [ -z "$VERSION" ]; then
TYPE="MacOS"
# Kernel de Mac OS (no debe ser fichero de texto).
FILE="$MNTDIR/mach_kernel"
if [ -z "$(file -b $FILE | grep 'text')" ]; then
# Obtener tipo de kernel.
[ -n "$(file -b $FILE | grep 'Mach-O')" ] && VERSION="macOS"
[ -n "$(file -b $FILE | grep 'Mach-O 64-bit')" ] && IS64BIT="$MSG_64BIT"
# Datos de configuración de versión de Mac OS.
FILE="$MNTDIR/System/Library/CoreServices/SystemVersion.plist"
[ -r $FILE ] && VERSION=$(awk -F"[<>]" '
/ProductName/ {getline;s=$3}
/ProductVersion/ {getline;v=$3}
END {print s,v}' $FILE)
# Datos de recuperación de macOS.
FILE="$MNTDIR/com.apple.recovery.boot"
[ -r $FILE -a -n "$VERSION" ] && VERSION="$VERSION recovery"
fi
fi
# Para FreeBSD: obtener datos del Kernel.
### TODO Revisar solución.
if [ -z "$VERSION" ]; then
TYPE="BSD"
FILE="$MNTDIR/boot/kernel/kernel"
if [ -r $FILE ]; then
VERSION="$(strings $FILE|awk '/@.*RELEASE/ {sub(/@\(#\)/,""); print $1,$2}')"
[ -n "$(file -b $FILE | grep 'x86-64')" ] && IS64BIT="$MSG_64BIT"
fi
fi
# Para Solaris: leer el fichero de versión.
### TODO Revisar solución.
if [ -z "$VERSION" ]; then
TYPE="Solaris"
FILE="$MNTDIR/etc/release"
[ -r $FILE ] && VERSION="$(head -1 $FILE)"
fi
# Para cargador GRUB, comprobar fichero de configuración.
if [ -z "$VERSION" ]; then
TYPE="GrubLoader"
for FILE in $MNTDIR/{,boot/}grub/menu.lst; do
[ -r $FILE ] && VERSION="GRUB Loader"
done
#/* (comentario Doxygen)
for FILE in $MNTDIR/{,boot/}{grub{,2},EFI/*}/grub.cfg; do
[ -r $FILE ] && VERSION="GRUB2 Loader"
done
fi
#*/ (Comentario Doxygen)
# Mostrar resultado y salir sin errores.
[ -n "$VERSION" ] && echo "$TYPE:$VERSION $IS64BIT"
return 0
}