diff --git a/ogclient/functions/ogBoot b/ogclient/functions/ogBoot new file mode 100755 index 0000000..23756c1 --- /dev/null +++ b/ogclient/functions/ogBoot @@ -0,0 +1,45 @@ +#!/usr/bin/python3 + +import sys +import argparse +from SystemLib import ogHelp +from BootLib import ogBoot + +if 2 == len (sys.argv) and 'help' == sys.argv[1]: + #parser.print_help() sale en inglés aunque la locale indique otra cosa + ogHelp ('ogBoot', 'ogBoot int_ndisk int_nfilesys [ NVRAMPERM ] [str_kernel str_initrd str_kernelparams]', ['ogBoot 1 2 "/boot/vmlinuz /boot/initrd.img root=/dev/sda2 ro"', 'ogBoot 1 2 NVRAMPERM']) + sys.exit (0) + +parser = argparse.ArgumentParser (add_help=False) +if 3 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('par') +elif 4 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('par') + parser.add_argument ('nvramperm_or_params') +elif 5 == len (sys.argv): + parser.add_argument ('disk') + parser.add_argument ('par') + parser.add_argument ('nvramperm') + parser.add_argument ('params') + +args = parser.parse_args() + +if 3 == len (sys.argv): + ret = ogBoot (args.disk, args.par) +elif 4 == len (sys.argv): + if 'NVRAMPERM' == args.nvramperm_or_params.upper(): + nvramperm = True + params = '' + else: + nvramperm = False + params = args.nvramperm_or_params + ret = ogBoot (args.disk, args.par, nvramperm, params) +elif 5 == len (sys.argv): + ret = ogBoot (args.disk, args.par, not not args.nvramperm, args.params) + +if ret is not None: + if ret == True: sys.exit (0) + elif ret == False: sys.exit (1) + else: print (ret) diff --git a/ogclient/lib/python3/BootLib.py b/ogclient/lib/python3/BootLib.py index dd1da9a..7bf91b6 100644 --- a/ogclient/lib/python3/BootLib.py +++ b/ogclient/lib/python3/BootLib.py @@ -39,6 +39,168 @@ import CacheLib #@note En Linux, si no se indican los parámetros de arranque se detectan de la opción por defecto del cargador GRUB. #@note En Linux, debe arrancarse la partición del directorio \c /boot #*/ ## +def ogBoot (disk, par, nvramperm=False, params=''): +# Detectar tipo de sistema de archivos y montarlo. + part = DiskLib.ogDiskToDev (disk, par) + if not part: return None + type = InventoryLib.ogGetOsType (disk, par) + if not type: return None +# Error si no puede montar sistema de archivos. + mntdir = FileSystemLib.ogMount (disk, par) + if not mntdir: return None + #params = None + #if 'NVRAMPERM' == nvramperm.upper(): + # nvramperm = True + #else: + # params = nvramperm + # nvramperm = False + + if 'Linux' == type or 'Android' == type: + # Si no se indican, obtiene los parámetros de arranque para Linux. + if not params: + params = ogLinuxBootParameters (disk, par) + # Si no existe y el UEFI buscar en particion ESP + if not params and InventoryLib.ogIsEfiActive(): + esp = DiskLib.ogGetEsp() + efidisk, efipart = esp.split() + params = ogLinuxBootParameters (efidisk, efipart) + params += ' ' + esp + # Si no existe, buscar sistema de archivo /boot en /etc/fstab. + if not params and os.path.exists (f'{mntdir}/etc/fstab'): + # Localizar S.F. /boot en /etc/fstab del S.F. actual. + dev = None + with open (f'{mntdir}/etc/fstab', 'r') as fd: + while True: + l = fd.readline() + if not l: break + parts = l.split() + if '#' != parts[0] and '/boot' == parts[1]: + dev = parts[0] + break + if dev: + fstab_part = DiskLib.ogDevToDisk (dev) + else: + return None + # Montar S.F. de /boot. + fstab_disk, fstab_par = fstab_part.split() + mntdir = FileSystemLib.ogMount (fstab_disk, fstab_par) + if not mntdir: return None + # Buscar los datos de arranque. + params = ogLinuxBootParameters (fstab_disk, fstab_par) + kernel = initrd = append = None + if params: + kernel, initrd, append = params.split (maxsplit=2) + # Si no hay kernel, no hay sistema operativo. + if not kernel or not os.path.exists (f'{mntdir}/{kernel}'): + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk} {par} ({type})') + return None + # Arrancar de partición distinta a la original. + if os.path.exists (f'{mntdir}/etc'): + append = re.sub ('root=[-+=_/a-zA-Z0-9]* ', f'root={part} ', append) + # Comprobar tipo de sistema. + if InventoryLib.ogIsEfiActive(): + # Comprobar si el Kernel está firmado. + file_out = subprocess.run (['file', '-k', f'{mntdir}/{kernel}'], capture_output=True, text=True).stdout + if not file_out or not 'EFI app' in file_out: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk} {par} ({type}, EFI)') + return None + + bootlabel = f'Part-{int(disk):02d}-{int(par):02d}' + bootloader = 'shimx64.efi' + # Obtener parcición EFI. + esp = DiskLib.ogGetEsp() + #efidisk, efipart = esp.split() + # TODO: Comprobamos que existe la BOOTLABEL, si no buscamos por sistema operativo + if '' == FileLib.ogGetPath (src=esp, file=f'EFI/{bootlabel}'): + osversion = InventoryLib.ogGetOsVersion (disk, par) + if 'SUSE' in osversion: + bootlabel = 'opensuse' + elif 'Fedora' in osversion: + bootlabel = 'fedora' + elif 'Ubuntu' in osversion: + bootlabel = 'ubuntu' + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'{esp} Boot loader') + return None + + # Crear orden de arranque (con unos valores por defecto). + UEFILib.ogNvramAddEntry (bootlabel, f'/EFI/{bootlabel}/Boot/{bootloader}', nvramperm) + # Marcar próximo arranque y reiniciar. + UEFILib.ogNvramSetNext (bootlabel) + subprocess.run (['reboot']) + else: + # Arranque BIOS: configurar kernel Linux con los parámetros leídos de su GRUB. + subprocess.run (['kexec', '-l', f'{mntdir}{kernel}', f'--append={append}', f'--initrd={mntdir}{initrd}']) + subprocess.Popen (['kexec', '-e']) + elif 'Windows' == type: + # Comprobar tipo de sistema. + if InventoryLib.ogIsEfiActive(): + bootlabel = f'Part-{int(disk):02d}-{int(par):02d}' + # Obtener parcición EFI. + esp = DiskLib.ogGetEsp() + efidisk, efipart = esp.split() + if not efipart: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'ESP') + return None + efidir = FileSystemLib.ogMount (efidisk, efipart) + if not efidir: return None + # Comprobar cargador (si no existe buscar por defecto en ESP). + loader = FileLib.ogGetPath (file=f'{efidir}/EFI/{bootlabel}/Boot/bootmgfw.efi') + if not loader: + bootlabel = 'Microsoft' + loader = FileLib.ogGetPath (file=f'{efidir}/EFI/Microsoft/Boot/bootmgfw.efi') + if not loader: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk} {par} ({type}, EFI)') + return None + + # Crear orden de arranque (con unos valores por defecto). + l = re.sub ('^.*EFI(.*)$', r'\1', loader) + UEFILib.ogNvramAddEntry (bootlabel, l, nvramperm) + # Marcar próximo arranque y reiniciar. + UEFILib.ogNvramSetNext (bootlabel) + subprocess.run (['reboot']) + else: + # Arranque BIOS: comprueba si hay un cargador de Windows. + for f in ['io.sys', 'ntldr', 'bootmgr']: + file = FileLib.ogGetPath (src=f'{disk} {par}', file=f) + if file: loader=f + if not loader: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk} {par} ({type})') + return None + winboot = os.environ.get ('winboot', '') + if 'kexec' == winboot: + # Modo de arranque en caliente (con kexec). + for f in glob.glob (f'{ogGlobals.OGLIB}/grub4dos/*'): + shutil.copy2 (f, mntdir) + disk0 = int(disk)-1 + par0 = int(par)-1 + subprocess.run (['kexec', '-l', f'{mntdir}/grub.exe', '--append=--config-file=root (hd{disk0},{par0}); chainloader (hd{disk0},{par0})/{loader}; tpm --init']) + subprocess.Popen (['kexec', '-e']) + else: + # Modo de arranque por reinicio (con reboot). + subprocess.run (['dd', 'if=/dev/zero', f'of={mntdir}/ogboot.me', 'bs=1024', 'count=3']) + subprocess.run (['dd', 'if=/dev/zero', f'of={mntdir}/ogboot.firstboot', 'bs=1024', 'count=3']) + subprocess.run (['dd', 'if=/dev/zero', f'of={mntdir}/ogboot.secondboot', 'bs=1024', 'count=3']) + v = RegistryLib.ogGetRegistryValue (mntdir, 'SOFTWARE', r'\Microsoft\Windows\CurrentVersion\Run\ogcleannboot') + if not v: + RegistryLib.ogAddRegistryValue (mntdir, 'SOFTWARE', r'\Microsoft\Windows\CurrentVersion\Run\ogcleanboot') + RegistryLib.ogSetRegistryValue (mntdir, 'SOFTWARE', r'\Microsoft\Windows\CurrentVersion\Run\ogcleanboot', r'cmd /c del c:\ogboot.*') + # Activar la partición. + DiskLib.ogSetPartitionActive (disk, par) + subprocess.run (['reboot']) + elif 'MacOS' == type: + # Modo de arranque por reinicio. + # Nota: el cliente tiene que tener configurado correctamente Grub. + if not os.path.exists (f'{mntdir}/boot.mac'): + open (f'{mntdir}/boot.mac', 'w').close() + subprocess.run (['reboot']) + elif 'GrubLoader' == type: + # Reiniciar. + #subprocess.run (['reboot']) + pass + else: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTOS, f'{disk} {par} ({type})') + return None #/** @@ -492,6 +654,9 @@ def ogGrubInstallMbr (disk, par, checkos='FALSE', kernelparam=''): if InventoryLib.ogIsEfiActive(): esp = DiskLib.ogGetEsp() + if not esp: + SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_PARTITION, 'ESP') + return None efidisk, efipart = esp.split() # Comprobamos que exista ESP y el directorio para ubuntu efisecondstage = FileSystemLib.ogMount (efidisk, efipart) diff --git a/ogclient/lib/python3/UEFILib.py b/ogclient/lib/python3/UEFILib.py index 94eebf2..3e61486 100644 --- a/ogclient/lib/python3/UEFILib.py +++ b/ogclient/lib/python3/UEFILib.py @@ -42,8 +42,8 @@ def ogNvramActiveEntry (entry): numentries.append (words[0][4:8]) except ValueError: for l in efibootmgr_out.splitlines(): - words = l.split (maxsplit=1) - if len(words) < 2: continue + words = l.split (maxsplit=2) + if len(words) < 3: continue if words[1] == entry: numentries.append (words[0][4:8]) @@ -135,8 +135,6 @@ def ogCopyEfiBootLoader (disk, par): 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': @@ -180,8 +178,8 @@ def ogNvramDeleteEntry (entry): numentries.append (words[0][4:8]) except ValueError: for l in efibootmgr_out.splitlines(): - words = l.split (maxsplit=1) - if len(words) < 2: continue + words = l.split (maxsplit=2) + if len(words) < 3: continue if words[1] == entry: numentries.append (words[0][4:8]) @@ -325,8 +323,8 @@ def ogNvramInactiveEntry (entry): numentries.append (words[0][4:8]) except ValueError: for l in efibootmgr_out.splitlines(): - words = l.split (maxsplit=1) - if len(words) < 2: continue + words = l.split (maxsplit=2) + if len(words) < 3: continue if words[1] == entry: numentries.append (words[0][4:8]) @@ -487,8 +485,8 @@ def ogNvramSetNext (entry): numentries.append (words[0][4:8]) except ValueError: for l in efibootmgr_out.splitlines(): - words = l.split (maxsplit=1) - if len(words) < 2: continue + words = l.split (maxsplit=2) + if len(words) < 3: continue if words[1] == entry: numentries.append (words[0][4:8]) @@ -496,7 +494,7 @@ def ogNvramSetNext (entry): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_NOTFOUND, f'NVRAM entry "{entry}"') return - if 1 != len(numentries): + if 1 != len (numentries): SystemLib.ogRaiseError ([], ogGlobals.OG_ERR_GENERIC, f'more than one entry found') return