531 lines
22 KiB
Python
531 lines
22 KiB
Python
#/**
|
|
#@file InventoryLib.py
|
|
#@brief Librería o clase Inventory
|
|
#@class Inventory
|
|
#@brief Funciones para recogida de datos de inventario de hardware y software de los clientes.
|
|
#@warning License: GNU GPLv3+
|
|
#*/
|
|
|
|
import platform
|
|
import sys
|
|
import os
|
|
import subprocess
|
|
import re
|
|
import json
|
|
import shutil
|
|
import glob
|
|
import plistlib
|
|
#import bsddb
|
|
|
|
import ogGlobals
|
|
import SystemLib
|
|
import FileSystemLib
|
|
import RegistryLib
|
|
import FileLib
|
|
|
|
|
|
#/**
|
|
# ogGetArch
|
|
#@brief Devuelve el tipo de arquitectura del cliente.
|
|
#@return str_arch - Arquitectura (i386 para 32 bits, x86_64 para 64 bits).
|
|
#*/
|
|
def ogGetArch():
|
|
if os.path.isdir ('/lib64'):
|
|
return 'x86_64'
|
|
else:
|
|
return '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
|
|
#*/ ##
|
|
def ogGetOsType(disk, partition):
|
|
try:
|
|
os_version = ogGetOsVersion(disk, partition)
|
|
if os_version:
|
|
return os_version.split(":", 1)[0]
|
|
else:
|
|
return None
|
|
except Exception as e:
|
|
print(f"Error en ogGetOsType: {e}")
|
|
return "Unknown"
|
|
|
|
|
|
#/**
|
|
# 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
|
|
#*/ ##
|
|
def ogGetOsUuid (disk, par):
|
|
mntdir = FileSystemLib.ogMount (disk, par)
|
|
if not mntdir: return None
|
|
|
|
# Obtener UUID según el tipo de sistema operativo.
|
|
os_type = ogGetOsType (disk, par)
|
|
if 'Linux' == os_type:
|
|
# Leer el UUID del sistema de ficheros raíz o el fichero de identificador.
|
|
uuid = subprocess.run (['findmnt', '-no', 'UUID', mntdir], capture_output=True, text=True).stdout.strip()
|
|
if not uuid:
|
|
uuid = open (os.path.join (mntdir, 'etc', 'machine-id')).read().strip()
|
|
return uuid
|
|
elif 'Windows' == os_type:
|
|
# Leer identificador en clave de registro.
|
|
uuid = RegistryLib.ogGetRegistryValue (mntdir, 'SOFTWARE', r'\Microsoft\Cryptography\MachineGuid')
|
|
return uuid
|
|
|
|
|
|
#/**
|
|
# ogGetSerialNumber
|
|
#@brief Obtiene el nº de serie del cliente.
|
|
#*/ ##
|
|
def ogGetSerialNumber():
|
|
SERIALNO = subprocess.check_output(["dmidecode", "-s", "system-serial-number"]).decode().strip()
|
|
SERIALNO = re.sub(r"(not specified|to be filled|invalid entry|default string)", "", SERIALNO, flags=re.IGNORECASE)
|
|
SERIALNO = SERIALNO.replace(" ", "")
|
|
SERIALNO = SERIALNO[:25] if len(SERIALNO) > 25 else SERIALNO
|
|
if SERIALNO: return SERIALNO
|
|
return None
|
|
|
|
|
|
#/**
|
|
# ogIsEfiActive
|
|
#@brief Comprueba si el sistema tiene activo el arranque EFI.
|
|
#*/ ##
|
|
def ogIsEfiActive():
|
|
return os.path.isdir("/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
|
|
#*/ ##
|
|
def ogListHardwareInfo():
|
|
ret = ''
|
|
|
|
# Ejecutar dmidecode y obtener tipo de chasis
|
|
dmi_out = subprocess.run (['dmidecode', '-s', 'chassis-type'], capture_output=True, text=True).stdout
|
|
dmi_out = '\n'.join ([ x for x in dmi_out.splitlines() if 'Other' not in x ])
|
|
ret += f'cha={dmi_out}\n'
|
|
|
|
if os.path.exists ('/sys/firmware/efi'):
|
|
ret += f'boo=UEFI\n'
|
|
else:
|
|
ret += f'boo=BIOS\n'
|
|
|
|
awk_script = r'''
|
|
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; }
|
|
'''
|
|
lshw_out = subprocess.run (['lshw'], capture_output=True, text=True).stdout
|
|
awk_out = subprocess.run (['awk', awk_script], input=lshw_out, capture_output=True, text=True).stdout
|
|
|
|
ret += awk_out
|
|
|
|
return ret
|
|
|
|
|
|
#/**
|
|
# 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
|
|
#*/ ##
|
|
def ogListSoftware (disk, par):
|
|
mntdir = FileSystemLib.ogMount (disk, par)
|
|
if not mntdir: return None
|
|
ostype = ogGetOsType (disk, par)
|
|
if not ostype: return None
|
|
|
|
apps = []
|
|
|
|
if 'Linux' == ostype:
|
|
# Procesar paquetes dpkg.
|
|
pkgdir = f'{mntdir}/var/lib/dpkg'
|
|
if os.path.exists (pkgdir):
|
|
status_file = os.path.join(pkgdir, "status")
|
|
awk_script = '''
|
|
/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}
|
|
'''
|
|
awk_out = subprocess.run (['awk', awk_script, status_file], capture_output=True, text=True).stdout
|
|
apps = awk_out.splitlines()
|
|
|
|
# Procesar paquetes RPM.
|
|
pkgdir = f'{mntdir}/var/lib/rpm'
|
|
if os.path.exists (pkgdir):
|
|
if shutil.which ('rpm'):
|
|
for f in glob.glob (f'{pkgdir}/__db.*'):
|
|
os.unlink (f)
|
|
rpm_out = subprocess.run (['rpm', '--dbpath', pkgdir, '-qa', '--qf', '%{NAME} %{VERSION}\n'], capture_output=True, text=True).stdout
|
|
for l in rpm_out.splitlines():
|
|
words = l.split()
|
|
if (not re.search ('-devel$', words[0])):
|
|
words[1] = re.sub ('-.*', '', words[1])
|
|
apps.append (' '.join (words))
|
|
for f in glob.glob (f'{pkgdir}/__db.*'):
|
|
os.unlink (f)
|
|
else:
|
|
pass
|
|
#db = bsddb.hashopen (f'{pkgdir}/Name', 'r');
|
|
#for k in db.keys():
|
|
# apps.append (re.sub ('-devel$', '', k))
|
|
|
|
# Procesar paquetes pacman.
|
|
pkgdir = f'{mntdir}/var/lib/pacman/local'
|
|
if os.path.exists (pkgdir):
|
|
for f in glob.glob (f'{pkgdir}/*'):
|
|
if '-' not in f: continue
|
|
idx = f[0:f.rfind ('-')].rfind ('-') ## index of 2nd-to-last dash
|
|
apps.append (f[0:idx] + ' ' + f[idx+1:])
|
|
# Procesar aplicaciones Snappy.
|
|
pkgdir = f'{mntdir}/snap'
|
|
out = ''
|
|
awk_script = '''
|
|
/name:/ {pack=$2}
|
|
/version:/ {vers=$2}
|
|
END {if (pack!="") print pack,"(snap)",vers}
|
|
'''
|
|
files = subprocess.run (['find', f'{pkgdir}/*/current/meta', '-name', 'snap.yaml'], capture_output=True, text=True).stdout.splitlines()
|
|
for f in files:
|
|
awk_out = subprocess.run (['awk', awk_script, f], capture_output=True, text=True).stdout
|
|
out += awk_out
|
|
apps += out.splitlines()
|
|
# Procesar aplicaciones Flatpak.
|
|
pkgdir = f'{mntdir}/var/lib/flatpak'
|
|
files = glob.glob (f'{pkgdir}/app/*/current/active/deploy/*')
|
|
for f in files:
|
|
p = open (f.strip()).read().split ('\0')
|
|
try:
|
|
if (p[0] != 'flathub'): raise ValueError
|
|
apps.append ('{} (flatpak) {}'.format (p[p.index('appdata-name') + 4], p[p.index('appdata-version') + 1]))
|
|
except ValueError:
|
|
pass
|
|
|
|
elif 'Windows' == ostype:
|
|
if shutil.which ('hivexregedit'):
|
|
hive = RegistryLib.ogGetHivePath (mntdir, 'software')
|
|
if hive:
|
|
cmd1_out = subprocess.run (['hivexregedit', '--unsafe-printable-strings', '--export', hive, r'\Microsoft\Windows\CurrentVersion\Uninstall'], capture_output=True, text=True).stdout
|
|
cmd1_out += subprocess.run (['hivexregedit', '--unsafe-printable-strings', '--export', hive, r'\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'], capture_output=True, text=True).stdout
|
|
out = name = ''
|
|
for l in cmd1_out.splitlines():
|
|
words = l.split ('"')
|
|
if len(words) < 4: continue
|
|
if (re.match (r'\[', words[0])): name=''
|
|
if (re.search ('DisplayName', words[1])): name=words[3]
|
|
if (re.search ('DisplayVersion', words[1])): apps.append (f'{name} {words[3]}')
|
|
else:
|
|
keys = RegistryLib.ogListRegistryKeys (mntdir, 'software', r'\Microsoft\Windows\CurrentVersion\Uninstall')
|
|
keys32 = RegistryLib.ogListRegistryKeys (mntdir, 'software', r'\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')
|
|
for k in keys:
|
|
prog = RegistryLib.ogGetRegistryValue (mntdir, 'software', rf'\Microsoft\Windows\CurrentVersion\Uninstall\{k}\DisplayName')
|
|
if prog:
|
|
vers = RegistryLib.ogGetRegistryValue (mntdir, 'software', rf'\Microsoft\Windows\CurrentVersion\Uninstall\{k}\DisplayVersion')
|
|
apps.append (f'{prog} {vers}')
|
|
for k in keys32:
|
|
prog = RegistryLib.gGetRegistryValue (mntdir, 'software', rf'\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{k}\DisplayName')
|
|
if prog:
|
|
vers = RegistryLib.ogGetRegistryValue (mntdir, 'software', rf'\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{k}\DisplayVersion')
|
|
apps.append (f'{prog} {vers}')
|
|
|
|
elif 'MacOS' == ostype:
|
|
files = subprocess.run (['find', f'{mntdir}/Applications', '-type', 'd', '-name', '*.app', '-prune', '-print'], capture_output=True, text=True).stdout.splitlines()
|
|
for k in files:
|
|
FILE = f'{k}/Contents/version.plist'
|
|
if not os.stat (FILE).st_size:
|
|
FILE = f'{k}/Contents/version.plist.uncompress'
|
|
if os.stat (FILE).st_size:
|
|
VERSION = subprocess.run (['awk', '-F[<>]', '/ShortVersionString/ {getline;v=$3} END {print v}', FILE], capture_output=True, text=True).stdout.strip()
|
|
bn = os.path.basename (k)
|
|
bn = re.sub ('.app$', '', bn)
|
|
apps.append (f'{bn} {VERSION}')
|
|
|
|
elif 'BSD' == ostype:
|
|
sqlite_out = subprocess.run (['sqlite3', f'{mntdir}/var/db/pkg/local.sqlite'], input='SELECT name FROM pkg_search;\n', capture_output=True, text=True).stdout
|
|
for l in sqlite_out.splitlines():
|
|
apps.append (' '.join (re.search ('(.*)-(.*)', l).groups()))
|
|
|
|
else:
|
|
SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk}, {par} ({ostype})')
|
|
return None
|
|
|
|
os_version = ogGetOsVersion (disk, par).split (':')[1]
|
|
return [os_version] + sorted (set (apps))
|
|
|
|
## https://stackoverflow.com/questions/2522651/find-a-key-inside-a-deeply-nested-dictionary/2522706#2522706
|
|
def _find_key_recursive(plist_dict, key_substr):
|
|
for k in plist_dict.keys():
|
|
if key_substr in k: return plist_dict[k]
|
|
for k, v in plist_dict.items():
|
|
if type(v) is dict: # Only recurse if we hit a dict value
|
|
value = _find_key_recursive(v, key_substr)
|
|
if value:
|
|
return value
|
|
return ''
|
|
|
|
#/**
|
|
# 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.
|
|
#*/ ##
|
|
#ogGetOsVersion ("1", "2") => "Linux:Ubuntu precise (12.04 LTS) 64 bits"
|
|
def ogGetOsVersion(disk, part):
|
|
mntdir = FileSystemLib.ogMount (disk, part)
|
|
if not mntdir:
|
|
return None
|
|
|
|
type = version = None
|
|
is64bit = ''
|
|
|
|
# Buscar tipo de sistema operativo.
|
|
# Para GNU/Linux: leer descripción.
|
|
os_release = os.path.join(mntdir, "etc/os-release")
|
|
if os.path.isfile(os_release):
|
|
type = 'Linux'
|
|
with open(os_release, "r") as f:
|
|
for line in f:
|
|
if line.startswith("PRETTY_NAME"):
|
|
version = line.split("=", 1)[1].strip().strip('"')
|
|
break
|
|
|
|
if not version:
|
|
lsb_release = os.path.join(mntdir, "etc/lsb-release")
|
|
if os.path.isfile(lsb_release):
|
|
type = 'Linux'
|
|
with open(lsb_release, "r") as f:
|
|
for line in f:
|
|
if line.startswith("DISTRIB_DESCRIPTION"):
|
|
version = line.split("=", 1)[1].strip().strip('"')
|
|
break
|
|
|
|
if not version:
|
|
for distrib in ["redhat", "SuSE", "mandrake", "gentoo"]:
|
|
distrib_file = os.path.join(mntdir, f"etc/{distrib}-release")
|
|
if os.path.isfile(distrib_file):
|
|
type = 'Linux'
|
|
with open(distrib_file, "r") as f:
|
|
version = f.readline().strip()
|
|
break
|
|
|
|
if not version:
|
|
arch_release_file = os.path.join(mntdir, "etc/arch-release")
|
|
if os.path.isfile(arch_release_file):
|
|
type = 'Linux'
|
|
version = "Arch Linux"
|
|
|
|
if not version:
|
|
slack_release_file = os.path.join(mntdir, "slackware-version")
|
|
if os.path.isfile(slack_release_file):
|
|
type = 'Linux'
|
|
with open (slack_release_file, 'r') as fd:
|
|
c = fd.read()
|
|
version = "Slackware {c}"
|
|
|
|
# Si no se encuentra, intentar ejecutar "lsb_release".
|
|
if not version:
|
|
out = subprocess.run (['chroot', mntdir, 'lsb_release', '-d'], capture_output=True, text=True).stdout
|
|
m = re.search (':\t(.*)', out)
|
|
if m:
|
|
type = 'Linux'
|
|
version = m.group(1)
|
|
# Comprobar Linux de 64 bits.
|
|
if version and os.path.exists(os.path.join(mntdir, "lib64")):
|
|
is64bit = ogGlobals.lang.MSG_64BIT
|
|
# Para Android, leer fichero de propiedades.
|
|
if not version:
|
|
type = 'Android'
|
|
files = glob.glob (os.path.join (mntdir, 'android*/system/build.prop'))
|
|
if files and os.path.isfile (files[0]):
|
|
v = []
|
|
with open (files[0], 'r') as f:
|
|
for line in f:
|
|
if 'product.brand' in line or 'build.version.release' in line:
|
|
v.append (line.split('=')[1].strip())
|
|
version = ' '.join (v)
|
|
if os.path.exists(os.path.join(mntdir, "lib64")):
|
|
is64bit = ogGlobals.lang.MSG_64BIT
|
|
# Para GNU/Hurd, comprobar fichero de inicio (basado en os-prober).
|
|
if not version:
|
|
type = 'Hurd'
|
|
if os.path.exists(os.path.join(mntdir, "hurd/init")):
|
|
version = 'GNU/Hurd'
|
|
# Para Windows: leer la version del registro.
|
|
if not version:
|
|
type = 'Windows'
|
|
build = 0
|
|
file = RegistryLib.ogGetHivePath (mntdir, 'SOFTWARE')
|
|
if file:
|
|
# Nuevo método más rápido para acceder al registro de Windows..
|
|
i = '\n'.join ([
|
|
f'load {file}',
|
|
r'cd \Microsoft\Windows NT\CurrentVersion',
|
|
'lsval ProductName',
|
|
'lsval DisplayVersion',
|
|
])
|
|
version = subprocess.run (['hivexsh'], input=i, capture_output=True, text=True).stdout
|
|
version = version.replace ('\n', ' ')
|
|
# Recoge el valor del número de compilación para ver si es Windows 10/11
|
|
i = '\n'.join ([
|
|
f'load {file}',
|
|
r'cd \Microsoft\Windows NT\CurrentVersion',
|
|
'lsval CurrentBuildNumber',
|
|
])
|
|
build = subprocess.run (['hivexsh'], input=i, capture_output=True, text=True).stdout
|
|
|
|
if subprocess.run (['reglookup', '-H', '-p', 'Microsoft/Windows/CurrentVersion/ProgramW6432Dir', file], capture_output=True, text=True).stdout:
|
|
is64bit = ogGlobals.lang.MSG_64BIT
|
|
|
|
if not version:
|
|
# Compatibilidad con métrodo antiguo y más lento de acceder al registro.
|
|
version = RegistryLib.ogGetRegistryValue (mntdir, 'software', r'\Microsoft\Windows NT\CurrentVersion\ProductName')
|
|
if RegistryLib.ogGetRegistryValue (mntdir, 'software', r'\Microsoft\Windows\CurrentVersion\ProgramW6432Dir'):
|
|
is64bit = ogGlobals.lang.MSG_64BIT
|
|
# Si la compilación es mayor o igual a 22000 es Windows 11
|
|
if int (build) >= 22000:
|
|
version = version.replace ('10', '11')
|
|
# Para cargador Windows: buscar versión en fichero BCD (basado en os-prober).
|
|
if not version:
|
|
type = 'WinLoader'
|
|
file = FileLib.ogGetPath (file=f'{mntdir}/boot/bcd')
|
|
if not file:
|
|
file = FileLib.ogGetPath (file=f'{mntdir}/EFI/Microsoft/boot/bcd')
|
|
if file:
|
|
for distrib in 'Windows Recovery', 'Windows Boot':
|
|
with open (file, 'rb') as fd:
|
|
contents = fd.read()
|
|
distrib_utf16_regex = re.sub (r'(.)', '\\1.', distrib)
|
|
distrib_utf16_regex = bytes (distrib_utf16_regex, 'ascii')
|
|
if re.search (distrib_utf16_regex, contents):
|
|
version = f'{distrib} loader'
|
|
# Para macOS: detectar kernel y completar con fichero plist de información del sistema.
|
|
if not version:
|
|
type = 'MacOS'
|
|
# Kernel de Mac OS (no debe ser fichero de texto).
|
|
file = f'{mntdir}/mach_kernel'
|
|
out = subprocess.run (['file', '--brief', file], capture_output=True, text=True).stdout
|
|
if not 'text' in out:
|
|
# Obtener tipo de kernel.
|
|
if 'Mach-O' in out: version = 'macOS'
|
|
if 'Mach-O 64-bit' in out: is64bit = ogGlobals.lang.MSG_64BIT
|
|
# Datos de configuración de versión de Mac OS.
|
|
file = f'{mntdir}/System/Library/CoreServices/SystemVersion.plist'
|
|
if os.path.exists (file):
|
|
with open (file, 'rb') as fd:
|
|
contents = fd.read()
|
|
plist_dict = plistlib.loads (contents)
|
|
n = _find_key_recursive (plist_dict, 'ProductName')
|
|
v = _find_key_recursive (plist_dict, 'ProductVersion')
|
|
version = f'{n} {v}'.strip()
|
|
# Datos de recuperación de macOS.
|
|
if version and os.path.exists (f'{mntdir}/com.apple.recovery.boot'):
|
|
version += ' recovery'
|
|
|
|
# Para FreeBSD: obtener datos del Kernel.
|
|
### TODO Revisar solución.
|
|
if not version:
|
|
type = 'BSD'
|
|
file = f'{mntdir}/boot/kernel/kernel'
|
|
if os.path.exists (file):
|
|
lines = subprocess.run (['strings', file], capture_output=True, text=True).stdout.splitlines()
|
|
release_search = list (filter (lambda x: re.search ('@.*RELEASE', x), lines))
|
|
if release_search:
|
|
first, second, *rest = release_search[0].split()
|
|
first = first.replace ('@(#)', '')
|
|
version = f'{first} {second}'
|
|
out = subprocess.run (['file', '--brief', file], capture_output=True, text=True).stdout
|
|
if 'x86-64' in out: is64bit = ogGlobals.lang.MSG_64BIT
|
|
# Para Solaris: leer el fichero de versión.
|
|
### TODO Revisar solución.
|
|
if not version:
|
|
type = 'Solaris'
|
|
file = f'{mntdir}/etc/release'
|
|
if os.path.exists (file):
|
|
with open (file, 'r') as fd:
|
|
version = fd.readline().strip
|
|
# Para cargador GRUB, comprobar fichero de configuración.
|
|
if not version:
|
|
type = 'GrubLoader'
|
|
for file in f'{mntdir}/grub/menu.lst', f'{mntdir}/boot/grub/menu.lst':
|
|
if os.path.exists (file):
|
|
VERSION = 'GRUB Loader'
|
|
for entry in f'{mntdir}/grub/grub.cfg', f'{mntdir}/grub2/grub.cfg', f'{mntdir}/EFI/*/grub.cfg', f'{mntdir}/boot/grub/grub.cfg', f'{mntdir}/boot/grub2/grub.cfg', f'{mntdir}/boot/EFI/*/grub.cfg':
|
|
for file in glob.glob (entry):
|
|
if os.path.exists (file):
|
|
version = 'GRUB2 Loader'
|
|
|
|
|
|
|
|
|
|
|
|
# Mostrar resultado y salir sin errores.
|
|
if version: return f"{type}:{version} {is64bit}"
|
|
return None
|