import os.path import re import subprocess import shutil import ogGlobals import SystemLib import FileSystemLib import DiskLib import FileLib import InventoryLib #!/bin/bash # Libreria provisional para uso de UEFI # Las funciones se incluirán las librerías ya existentes #/** # ogNvramActiveEntry #@brief Activa entrada de la NVRAM identificada por la etiqueta o el orden #@param Num_order_entry | Label_entry Número de orden o la etiqueta de la entrada a borrar. #@return (nada) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTUEFI UEFI no activa. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. #*/ ## #ogNvramActiveEntry ('2') #ogNvramActiveEntry ('Windows Boot Manager') def ogNvramActiveEntry (entry): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return numentries = [] efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout try: foo = int (entry, 16) ## raises ValueError if entry doesn't look like hexadecimal num = f'{foo:04x}'.upper() for l in efibootmgr_out.splitlines(): words = l.split() if len(words) < 2: continue if num in words[0]: numentries.append (words[0][4:8]) except ValueError: for l in efibootmgr_out.splitlines(): words = l.split (maxsplit=1) if len(words) < 2: continue if words[1] == entry: numentries.append (words[0][4:8]) if not numentries: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry "{entry}"') return if 1 != len(numentries): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'more than one entry found') return subprocess.run (['efibootmgr', '-a', '-b', numentries[0]], capture_output=True, text=True) #/** # ogNvramAddEntry #@brief Crea nueva entrada en el gestor de arranque (NVRAM), opcionalmente la incluye al final del orden de arranque. #@param Str_Label_entry Número de disco o etiqueta de la entrada a crear. #@param Str_BootLoader Número de partición o cargador de arranque. #@param Bool_Incluir_Arranque Incluir en el orden de arranque (por defecto FALSE) (opcional) #@return (nada) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTUEFI UEFI no activa. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. #*/ ## #ogNvramAddEntry ('1', '2', True) #ogNvramAddEntry ('grub', '/EFI/grub/grubx64.efi', True) #ogNvramAddEntry ('Windows', '/EFI/Microsoft/Boot/bootmgfw.efi') def ogNvramAddEntry (bootlbl, bootldr, nvram_set=False): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return esp = DiskLib.ogGetEsp() if not esp: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, 'ESP') return efidisk, efipar = esp.split() try: foo = int(bootlbl) + int(bootldr) ## raises ValueError if bootlbl/bootldr don't look like numbers bootlabel = f'Part-{int(bootlbl):02d}-{int(bootldr):02d}' bootloader = f'/EFI/{bootlabel}/Boot/ogloader.efi' except ValueError: bootlabel = bootlbl bootloader = bootldr ogNvramDeleteEntry (bootlabel) dev = DiskLib.ogDiskToDev (efidisk) subprocess.run (['efibootmgr', '-C', '-d', dev, '-p', efipar, '-L', bootlabel, '-l', bootloader]) if nvram_set: efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout for l in efibootmgr_out.splitlines(): words = l.split (maxsplit=1) if len(words) < 2: continue if words[1] == bootlabel: numentry = words[0][4:8] order = ogNvramGetOrder() ogNvramSetOrder (order + [numentry]) #/** # ogCopyEfiBootLoader int_ndisk str_repo path_image #@brief Copia el cargador de arranque desde la partición EFI a la de sistema. #@param int_ndisk nº de orden del disco #@param int_part nº de partición #@return (nada, por determinar) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. #@note Si existe el cargador en la partición de sistema no es válido #*/ ## def ogCopyEfiBootLoader (disk, par): mntdir = FileSystemLib.ogMount (disk, par) if not mntdir: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') return None esp = DiskLib.ogGetEsp() if not esp: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, 'EFI partition') return esp_disk, esp_par = esp.split() efidir = FileSystemLib.ogMount (esp_disk, esp_par) if not efidir: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'ESP') return bootlabel = f'Part-{int(disk):02d}-{int(par):02d}' osversion = InventoryLib.ogGetOsVersion (disk, par) print (f'bootlabel ({bootlabel})') print (f'osversion ({osversion})') if 'Windows 1' in osversion: loader = None for i in f'{efidir}/EFI/Microsoft/Boot/bootmgfw.efi', f'{efidir}/EFI/{bootlabel}/Boot/bootmgfw.efi': if os.path.exists (i): loader = i if not loader: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk} {par} ({osversion}, EFI)') return None if (os.path.isdir (f'{mntdir}/ogBoot')): shutil.rmtree (f'{mntdir}/ogBoot') dirloader = os.path.realpath (os.path.dirname (loader) + '/..') shutil.copytree (f'{dirloader}/Boot', f'{mntdir}/ogBoot') #/** # ogNvramDeleteEntry #@brief Borra entrada de la NVRAM identificada por la etiqueta o el orden #@param Num_order_entry | Label_entry Número de orden o la etiqueta de la entrada a borrar. #@return (nada) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTUEFI UEFI no activa. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado (entrada en NVRAM). #*/ ## #ogNvramDeleteEntry ('2') #ogNvramDeleteEntry ('Windows Boot Manager') def ogNvramDeleteEntry (entry): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return numentries = [] efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout try: foo = int (entry, 16) ## raises ValueError if entry doesn't look like hexadecimal num = f'{foo:04x}'.upper() for l in efibootmgr_out.splitlines(): words = l.split() if len(words) < 2: continue if num in words[0]: numentries.append (words[0][4:8]) except ValueError: for l in efibootmgr_out.splitlines(): words = l.split (maxsplit=1) if len(words) < 2: continue if words[1] == entry: numentries.append (words[0][4:8]) if not numentries: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry "{entry}"') return if 1 != len(numentries): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'more than one entry found') return subprocess.run (['efibootmgr', '-B', '-b', numentries[0]], capture_output=True, text=True) #/** # ogNvramGetCurrent #@brief Muestra la entrada del gestor de arranque (NVRAM) que ha iniciado el equipo. #@return Entrada con la que se ha iniciado el equipo #@exception OG_ERR_NOTUEFI UEFI no activa. #*/ ## def ogNvramGetCurrent(): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return bootentry = '9999' ret = None efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout for l in efibootmgr_out.splitlines(): words = l.split (maxsplit=1) if len(words) < 2: continue if 'BootCurrent' in words[0]: bootentry = words[1] continue if bootentry in words[0]: num = words[0][4:8].strip ('0') or '0' ret = f'{num} {words[1]}' return ret # ogNvramGetNext #@brief Muestra la entrada del gestor de arranque (NVRAM) que se utilizará en el próximo arranque. #@return Entrada que se utilizará en el próximo arranque #@exception OG_ERR_NOTUEFI UEFI no activa. #*/ ## def ogNvramGetNext(): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return ret = None efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout for l in efibootmgr_out.splitlines(): words = l.split (maxsplit=1) if len(words) < 2: continue if 'BootNext' in words[0]: ret = words[1] return ret # ogNvramGetOrder #@brief Muestra el orden de las entradas del gestor de arranque (NVRAM) #@return Array, orden de las entradas #@exception OG_ERR_NOTUEFI UEFI no activa. #*/ ## def ogNvramGetOrder(): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return ret = [] efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout for l in efibootmgr_out.splitlines(): words = l.split() if len(words) < 2: continue if 'BootOrder:' == words[0]: ret = words[1].split (',') return ret #/** # ogNvramGetTimeout #@brief Muestra el tiempo de espera del gestor de arranque (NVRAM) #@return Timeout de la NVRAM #@exception OG_ERR_NOTUEFI UEFI no activa. #*/ ## def ogNvramGetTimeout(): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return ret = None efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout for l in efibootmgr_out.splitlines(): words = l.split (maxsplit=1) if len(words) < 2: continue if 'Timeout:' == words[0]: ret = words[1] return ret #/** # ogGrubUefiConf int_ndisk int_part str_dir_grub #@brief Genera el fichero grub.cfg de la ESP #@param int_ndisk nº de orden del disco #@param int_part nº de partición #@param str_dir_grub prefijo del directorio de grub en la partición de sistema. ej: /boot/grubPARTITION #@return (nada, por determinar) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. #@TODO Confirmar si el fichero "$EFIDIR/EFI/$BOOTLABEL/grub.cfg" es necesario. #*/ ## #/** # ogNvramInactiveEntry #@brief Inactiva entrada de la NVRAM identificada por la etiqueta o el orden #@param Num_order_entry | Label_entry Número de orden o la etiqueta de la entrada a borrar. #@return (nada) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. #@exception OG_ERR_NOTUEFI UEFI no activa. #*/ ## #ogNvramInactiveEntry ('2') #ogNvramInactiveEntry ('Windows Boot Manager') def ogNvramInactiveEntry (entry): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return numentries = [] efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout try: foo = int (entry, 16) ## raises ValueError if entry doesn't look like hexadecimal num = f'{foo:04x}'.upper() for l in efibootmgr_out.splitlines(): words = l.split() if len(words) < 2: continue if num in words[0]: numentries.append (words[0][4:8]) except ValueError: for l in efibootmgr_out.splitlines(): words = l.split (maxsplit=1) if len(words) < 2: continue if words[1] == entry: numentries.append (words[0][4:8]) if not numentries: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry "{entry}"') return if 1 != len(numentries): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'more than one entry found') return subprocess.run (['efibootmgr', '-A', '-b', numentries[0]], capture_output=True, text=True) #/** # ogNvramList #@brief Lista las entradas de la NVRAN (sólo equipos UEFI) #@return Multiline string: entradas de la NVRAM con el formato: orden etiqueta [* (si está activa) ] #@exception OG_ERR_NOTUEFI UEFI no activa. #*/ ## def ogNvramList(): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return ret = '' efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout for l in efibootmgr_out.splitlines(): words = l.split (maxsplit=1) if len(words) < 2: continue if re.search ('Boot[0-9]', words[0]): active = '*' if '*' in words[0] else '' num = words[0][4:8].strip ('0') or '0' ret += '{:>4s} {} {}\n'.format (num, words[1], active) return ret #/** # ogNvramPxeFirstEntry #@brief Sitúa la entrada de la tarjeta de red en el primer lugar en la NVRAM. #@return (nada) #@exception OG_ERR_NOTUEFI UEFI no activa. #*/ ## def ogNvramPxeFirstEntry(): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return num = '' efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout for l in efibootmgr_out.splitlines(): if re.search ('IP[vV]{0,1}4', l): num = l[4:8].strip ('0') or '0' numentry = f'{int(num):04x}'.upper() o = ogNvramGetOrder() if not o: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'ogNvramGetOrder returned an empty list') return # Si la entrada es la primera nos salimos. if numentry == o[0]: return True # Si la entrada ya existe la borramos. order = [numentry] + list (filter (lambda x: x if x!=numentry else [], o)) ogNvramSetOrder (order) return True #/** # ogRestoreEfiBootLoader int_ndisk str_repo #@brief Copia el cargador de arranque de la partición de sistema a la partición EFI. #@param int_ndisk nº de orden del disco #@param int_part nº de partición #@return (nada, por determinar) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado (partición de sistema o EFI). #@exception OG_ERR_NOTOS sin sistema operativo. #*/ ## def ogRestoreEfiBootLoader (disk, par): mntdir = FileSystemLib.ogMount (disk, par) if not mntdir: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, f'{disk} {par}') return esp = DiskLib.ogGetEsp() if not esp: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, 'EFI partition') return esp_disk, esp_par = esp.split() efidir = FileSystemLib.ogMount (esp_disk, esp_par) if not efidir: FileSystemLib.ogFormat (esp_disk, esp_par, 'FAT32') efidir = FileSystemLib.ogMount (esp_disk, esp_par) if not efidir: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'ESP') return osversion = InventoryLib.ogGetOsVersion (disk, par) if 'Windows 1' in osversion: bootlabel = f'Part-{int(disk):02d}-{int(par):02d}' loader = FileLib.ogGetPath (file=f'{mntdir}/ogBoot/bootmgfw.efi') if not loader: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk} {par} ({osversion}, EFI)') return efi_bl = f'{efidir}/EFI/{bootlabel}' if os.path.exists (efi_bl): shutil.rmtree (efi_bl) os.makedirs (efi_bl, exist_ok=True) shutil.copytree (os.path.dirname (loader), f'{efi_bl}/Boot', symlinks=True) shutil.copy (loader, f'{efi_bl}/Boot/ogloader.efi') if '' != FileLib.ogGetPath (file=f'{efidir}/EFI/Microsoft'): os.rename (f'{efidir}/EFI/Microsoft', f'{efidir}/EFI/Microsoft.backup.og') return #/** # ogRestoreUuidPartitions #@brief Restaura los uuid de las particiones y la tabla de particiones #@param int_ndisk nº de orden del disco #@param int_nfilesys nº de orden del sistema de archivos #@param REPO|CACHE repositorio #@param str_imgname nombre de la imagen #@return (nada) #@exception OG_ERR_FORMAT Formato incorrecto. #@exception OG_ERR_NOTFOUND No encontrado fichero de información de la imagen (con uuid) #*/ ## #/** # ogNvramSetNext #@brief Configura el próximo arranque con la entrada del gestor de arranque (NVRAM) identificada por la etiqueta o el orden. #@param Num_order_entry | Label_entry Número de orden o la etiqueta de la entrada a borrar. #@return (nada) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTUEFI UEFI no activa. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. #*/ ## #ogNvramSetNext ('2') #ogNvramSetNext ('Windows Boot Manager') def ogNvramSetNext (entry): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return numentries = [] efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout try: foo = int (entry, 16) ## raises ValueError if entry doesn't look like hexadecimal num = f'{foo:04x}'.upper() for l in efibootmgr_out.splitlines(): words = l.split() if len(words) < 2: continue if num in words[0]: numentries.append (words[0][4:8]) except ValueError: for l in efibootmgr_out.splitlines(): words = l.split (maxsplit=1) if len(words) < 2: continue if words[1] == entry: numentries.append (words[0][4:8]) if not numentries: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry "{entry}"') return if 1 != len(numentries): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'more than one entry found') return subprocess.run (['efibootmgr', '-n', numentries[0]], capture_output=True, text=True) #/** # ogNvramSetOrder #@brief Configura el orden de las entradas de la NVRAM #@param Orden de las entradas separadas por espacios #@return (nada) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTUEFI UEFI no activa. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado (entrada NVRAM). #*/ ## #ogNvramSetOrder (['1', '3']) def ogNvramSetOrder (order): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return try: for i in order: foo = int (i, 16) except ValueError: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'ogNvramSetOrder ([array or hex values])') return # Entradas de la NVRAM actuales numentries = [] efibootmgr_out = subprocess.run (['efibootmgr'], capture_output=True, text=True).stdout for l in efibootmgr_out.splitlines(): words = l.split() if len(words) < 2: continue if re.search ('Boot[0-9a-fA-F]{4}', words[0]): numentries.append ('0' + words[0][4:8]) new_order = [] for o in order: h = f'{int(o,16):05x}'.upper() if h not in numentries: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry order "{h}"') return new_order.append (h) subprocess.run (['efibootmgr', '-o', ','.join (new_order)]) #/** # ogNvramSetTimeout #@brief Configura el tiempo de espera de la NVRAM #@param Orden de las entradas separadas por espacios #@return (nada) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. #*/ ## #ogNvramSetTimeout ('2') def ogNvramSetTimeout (t): if not InventoryLib.ogIsEfiActive(): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTUEFI, '') return try: num = int (t) ## raises ValueError if t doesn't look like a number except ValueError: SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_FORMAT, 'ogNvramSetTimeout (timeout)') return subprocess.run (['efibootmgr', '-t', t]) #/** # ogUuidChange int_ndisk str_repo #@brief Reemplaza el UUID de un sistema de ficheros. #@param int_ndisk nº de orden del disco #@param int_part nº de partición #@return (nada, por determinar) #@exception OG_ERR_FORMAT formato incorrecto. #@exception OG_ERR_NOTFOUND fichero o dispositivo no encontrado. #*/ ##