import os
import sys
import shutil
import subprocess
import uuid

from engine.InventoryLib import *
from engine.SystemLib import *
from engine.UEFILib import *


def ogBoot():
    FUNCNAME = ogBoot.__name__
    # Variables locales.
    PART = None
    TYPE = None
    MNTDIR = None
    PARAMS = None
    KERNEL = None
    INITRD = None
    APPEND = None
    FILE = None
    LOADER = None
    f = None
    NVRAMPERM = None
    EFIDISK = None
    EFIPART = None
    EFIDIR = None
    BOOTLABEL = None
    BOOTLOADER = None
    BOOTNO = None
    DIRGRUB = None
    b = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk int_nfilesys [ NVRAMPERM ] [str_kernel str_initrd str_kernelparams]", FUNCNAME + " 1 1", FUNCNAME + " 1 2 \"/boot/vmlinuz /boot/initrd.img root=/dev/sda2 ro\"", FUNCNAME + " 1 1", FUNCNAME + " 1 2 NVRAMPERM")
        return

    # Error si no se reciben 2 o 3 parámetros.
    if len(sys.argv) != 3 and len(sys.argv) != 4 and len(sys.argv) != 5:
        ogRaiseError(OG_ERR_FORMAT)
        return

    # Detectar tipo de sistema de archivos y montarlo.
    PART = ogDiskToDev(int(sys.argv[1]), int(sys.argv[2]))
    if PART is None:
        return
    TYPE = ogGetOsType(int(sys.argv[1]), int(sys.argv[2]))
    if TYPE is None:
        return
    # Error si no puede montar sistema de archivos.
    MNTDIR = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if MNTDIR is None:
        return
    if len(sys.argv) == 5 and sys.argv[4].upper() == "NVRAMPERM":
        NVRAMPERM = True
    else:
        NVRAMPERM = False
        PARAMS = sys.argv[4] if len(sys.argv) == 5 else sys.argv[3]

    if TYPE in ["Linux", "Android"]:
        # Si no se indican, obtiene los parámetros de arranque para Linux.
        PARAMS = PARAMS or ogLinuxBootParameters(int(sys.argv[1]), int(sys.argv[2]))
        # Si no existe y el UEFI buscar en particion ESP
        if not PARAMS and ogIsEfiActive():
            PARAMS = ogLinuxBootParameters(ogGetEsp())
        # Si no existe, buscar sistema de archivo /boot en /etc/fstab.
        if not PARAMS and os.path.exists(os.path.join(MNTDIR, "etc/fstab")):
            # Localizar S.F. /boot en /etc/fstab del S.F. actual.
            PART = ogDevToDisk(subprocess.check_output(["awk", '$1!="#" && $2=="/boot" {print $1}', os.path.join(MNTDIR, "etc/fstab")]).decode().strip())
            # Montar S.F. de /boot.
            MNTDIR = ogMount(PART)
            if MNTDIR is None:
                return
            # Buscar los datos de arranque.
            PARAMS = ogLinuxBootParameters(PART)
            if PARAMS is None:
                return
        KERNEL, INITRD, APPEND = PARAMS.split() if PARAMS else (None, None, None)
        # Si no hay kernel, no hay sistema operativo.
        if not KERNEL or not os.path.exists(os.path.join(MNTDIR, KERNEL)):
            ogRaiseError(OG_ERR_NOTOS, sys.argv[1], sys.argv[2], TYPE)
            return
        # Arrancar de partición distinta a la original.
        if os.path.exists(os.path.join(MNTDIR, "etc")):
            APPEND = APPEND.replace("root=[-+=_/a-zA-Z0-9]* ", "root={} ".format(PART))
        # Comprobar tipo de sistema.
        if ogIsEfiActive():
            # Comprobar si el Kernel está firmado.
            if not subprocess.check_output(["file", "-k", os.path.join(MNTDIR, KERNEL)]).decode().strip().startswith("EFI app"):
                ogRaiseError(OG_ERR_NOTOS, sys.argv[1], sys.argv[2], TYPE, "EFI")
                return

            BOOTLABEL = "Part-{:02d}-{:02d}".format(int(sys.argv[1]), int(sys.argv[2]))
            BOOTLOADER = "shimx64.efi"
            # Obtener parcición EFI.
            EFIDISK, EFIPART = ogGetEsp()
            # TODO: Comprobamos que existe la BOOTLABEL, si no buscamos por sistema operativo
            if not ogGetPath(EFIDISK, EFIPART, "EFI/{}".format(BOOTLABEL)):
                OSVERSION = ogGetOsVersion(int(sys.argv[1]), int(sys.argv[2]))
                if OSVERSION:
                    if "SUSE" in OSVERSION:
                        BOOTLABEL = "opensuse"
                    elif "Fedora" in OSVERSION:
                        BOOTLABEL = "fedora"
                    elif "Ubuntu" in OSVERSION:
                        BOOTLABEL = "ubuntu"
                    else:
                        ogRaiseError(OG_ERR_NOTFOUND, EFIDISK, EFIPART, "Boot loader")
                        return

            # Crear orden de arranque (con unos valores por defecto).
            ogNvramAddEntry(BOOTLABEL, "/EFI/{}/Boot/{}".format(BOOTLABEL, BOOTLOADER), NVRAMPERM)
            # Marcar próximo arranque y reiniciar.
            ogNvramSetNext(BOOTLABEL)
            reboot()
        else:
            # Arranque BIOS: configurar kernel Linux con los parámetros leídos de su GRUB.
            subprocess.call(["kexec", "-l", os.path.join(MNTDIR, KERNEL), "--append={}".format(APPEND), "--initrd={}".format(os.path.join(MNTDIR, INITRD))])
            subprocess.call(["kexec", "-e"])
    elif TYPE == "Windows":
        # Comprobar tipo de sistema.
        if ogIsEfiActive():
            BOOTLABEL = "Part-{:02d}-{:02d}".format(int(sys.argv[1]), int(sys.argv[2]))
            # Obtener parcición EFI.
            EFIDISK, EFIPART = ogGetEsp()
            if not EFIPART:
                ogRaiseError(OG_ERR_PARTITION, "ESP")
                return
            EFIDIR = ogMount(EFIDISK, EFIPART)
            if EFIDIR is None:
                return
            # Comprobar cargador (si no existe buscar por defecto en ESP).
            LOADER = ogGetPath(os.path.join(EFIDIR, "EFI", BOOTLABEL, "Boot/bootmgfw.efi"))
            if not LOADER:
                BOOTLABEL = "Microsoft"
                LOADER = ogGetPath(os.path.join(EFIDIR, "EFI", "Microsoft/Boot/bootmgfw.efi"))
            if not LOADER:
                ogRaiseError(OG_ERR_NOTOS, sys.argv[1], sys.argv[2], TYPE, "EFI")
                return

            # Crear orden de arranque (con unos valores por defecto).
            ogNvramAddEntry(BOOTLABEL, "/EFI{}".format(LOADER[LOADER.find("EFI"):]), NVRAMPERM)
            # Marcar próximo arranque y reiniciar.
            ogNvramSetNext(BOOTLABEL)
            reboot()
        else:
            # Arranque BIOS: comprueba si hay un cargador de Windows.
            for f in ["io.sys", "ntldr", "bootmgr"]:
                FILE = ogGetPath(int(sys.argv[1]), int(sys.argv[2]), f)
                if FILE:
                    LOADER = f
            if not LOADER:
                ogRaiseError(OG_ERR_NOTOS, sys.argv[1], sys.argv[2], TYPE)
                return
            if winboot == "kexec":
                # Modo de arranque en caliente (con kexec).
                shutil.copy(os.path.join(OGLIB, "grub4dos/*"), MNTDIR)  # */ (Comentario Doxygen)
                subprocess.call(["kexec", "-l", os.path.join(MNTDIR, "grub.exe"), "--append=--config-file=\"root (hd{}),{}); chainloader (hd{}),{}/{}; tpm --init\"".format(int(sys.argv[1])-1, int(sys.argv[2])-1, int(sys.argv[1])-1, int(sys.argv[2])-1, LOADER)])
                subprocess.call(["kexec", "-e"])
            else:
                # Modo de arranque por reinicio (con reboot).
                with open(os.path.join(MNTDIR, "ogboot.me"), "wb") as f:
                    f.write(b"\x00" * 1024 * 3)
                with open(os.path.join(MNTDIR, "ogboot.firstboot"), "wb") as f:
                    f.write(b"\x00" * 1024 * 3)
                with open(os.path.join(MNTDIR, "ogboot.secondboot"), "wb") as f:
                    f.write(b"\x00" * 1024 * 3)
                if not ogGetRegistryValue(MNTDIR, "SOFTWARE", "\\Microsoft\\Windows\\CurrentVersion\\Run\\ogcleannboot"):
                    ogAddRegistryValue(MNTDIR, "SOFTWARE", "\\Microsoft\\Windows\\CurrentVersion\\Run\\ogcleanboot")
                    ogSetRegistryValue(MNTDIR, "SOFTWARE", "\\Microsoft\\Windows\\CurrentVersion\\Run\\ogcleanboot", "cmd /c del c:\\ogboot.*")
                # Activar la partición.
                ogSetPartitionActive(int(sys.argv[1]), int(sys.argv[2]))
                reboot()
    elif TYPE == "MacOS":
        # Modo de arranque por reinicio.
        # Nota: el cliente tiene que tener configurado correctamente Grub.
        open(os.path.join(MNTDIR, "boot.mac"), "w").close()
        reboot()
    elif TYPE == "GrubLoader":
        # Reiniciar.
        #reboot()
        pass
    else:
        ogRaiseError(OG_ERR_NOTOS, sys.argv[1], sys.argv[2], TYPE)
        return
    
def ogGetWindowsName():
    FUNCNAME = ogGetWindowsName.__name__
    # Variables locales.
    MNTDIR = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk int_npartition", FUNCNAME + " 1 1  ==>  PRACTICA-PC")
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) != 3:
        ogRaiseError(OG_ERR_FORMAT)
        return

    # Montar el sistema de archivos.
    MNTDIR = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if MNTDIR is None:
        return

    # Obtener dato del valor de registro.
    ogGetRegistryValue(MNTDIR, "system", "\\ControlSet001\\Control\\ComputerName\\ComputerName\\ComputerName")

def ogLinuxBootParameters(ndisk, nfilesys):
    FUNCNAME = ogLinuxBootParameters.__name__
    # Variables locales.
    MNTDIR = None
    CONFDIR = None
    CONFFILE = None
    f = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk int_nfilesys", FUNCNAME + " 1 2  ==>  /vmlinuz-3.5.0-21-generic /initrd.img-3.5.0-21-generic root=/dev/sda2 ro splash")
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) != 3:
        ogRaiseError(OG_ERR_FORMAT)
        return

    # Detectar id. de tipo de partición y codificar al mnemonico.
    MNTDIR = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if MNTDIR is None:
        return

    # Fichero de configuración de GRUB.
    CONFDIR = MNTDIR  # Sistema de archivos de arranque (/boot).
    if os.path.isdir(os.path.join(MNTDIR, "boot")):
        CONFDIR = os.path.join(MNTDIR, "boot")  # Sist. archivos raíz con directorio boot.
    for f in [os.path.join(CONFDIR, "grubMBR/boot/menu.lst"), os.path.join(CONFDIR, "grubPARTITION/boot/menu.lst"), os.path.join(CONFDIR, "grub/boot/menu.lst"), os.path.join(CONFDIR, "grub2/boot/grub.cfg"), os.path.join(CONFDIR, "efi/EFI/*/grub.cfg")]:
        if os.path.isfile(f):
            CONFFILE = f
            break
    if CONFFILE is None:
        ogRaiseError(OG_ERR_NOTFOUND, "grub.cfg")
        return

    # Toma del fichero de configuracion los valores del kernel, initrd
    # y parámetros de arranque usando las cláusulas por defecto
    # ("default" en GRUB1, "set default" en GRUB2)
    # y los formatea para que sean compatibles con \c kexec .
    awk_script = r'''
    BEGIN {cont=-1;}
    $1~/^default$/     {sub(/=/," "); def=$2;}
    $1~/^set$/ && $2~/^default/ { gsub(/[="]/," "); def=$3;
                                    if (def ~ /saved_entry/) def=0;
                                }
    $1~/^(title|menuentry)$/ {cont++}
    $1~/^set$/ && $2~/^root=.\(hd'$[1-1]',(msdos|gpt)'$2'\).$/ { if (def==0) def=cont; }
    $1~/^(kernel|linux(16|efi)?)$/ { if (def==cont) {
                                        kern=$2;
                                        sub($1,""); sub($1,""); sub(/^[ \t]*/,""); app=$0
                                        }
                                    }
    $1~/^initrd(16|efi)?$/ {if (def==cont) init=$2}
    END {if (kern!="") printf("%s %s %s", kern,init,app)}
    '''
    awk_command = ["awk", awk_script, CONFFILE]
    output = subprocess.check_output(awk_command).decode().strip()
    return output

def ogGetLinuxBootParameters(ndisk, nfilesys):
    FUNCNAME = ogGetLinuxBootParameters.__name__
    # Variables locales.
    MNTDIR = None
    CONFDIR = None
    CONFFILE = None
    f = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk int_nfilesys", FUNCNAME + " 1 2  ==>  /vmlinuz-3.5.0-21-generic /initrd.img-3.5.0-21-generic root=/dev/sda2 ro splash")
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) != 3:
        ogRaiseError(OG_ERR_FORMAT)
        return

    # Detectar id. de tipo de partición y codificar al mnemonico.
    MNTDIR = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if MNTDIR is None:
        return

    # Fichero de configuración de GRUB.
    CONFDIR = MNTDIR  # Sistema de archivos de arranque (/boot).
    if os.path.isdir(os.path.join(MNTDIR, "boot")):
        CONFDIR = os.path.join(MNTDIR, "boot")  # Sist. archivos raíz con directorio boot.
    for f in [os.path.join(CONFDIR, "grubMBR/boot/menu.lst"), os.path.join(CONFDIR, "grubPARTITION/boot/menu.lst"), os.path.join(CONFDIR, "grub/boot/menu.lst"), os.path.join(CONFDIR, "grub2/boot/grub.cfg"), os.path.join(CONFDIR, "efi/EFI/*/grub.cfg")]:
        if os.path.isfile(f):
            CONFFILE = f
            break
    if CONFFILE is None:
        ogRaiseError(OG_ERR_NOTFOUND, "grub.cfg")
        return

    # Toma del fichero de configuracion los valores del kernel, initrd
    # y parámetros de arranque usando las cláusulas por defecto
    # ("default" en GRUB1, "set default" en GRUB2)
    # y los formatea para que sean compatibles con \c kexec .
    awk_script = r'''
    BEGIN {cont=-1;}
    $1~/^default$/     {sub(/=/," "); def=$2;}
    $1~/^set$/ && $2~/^default/ { gsub(/[="]/," "); def=$3;
                                    if (def ~ /saved_entry/) def=0;
                                }
    $1~/^(title|menuentry)$/ {cont++}
    $1~/^set$/ && $2~/^root=.\(hd'$[1-1]',(msdos|gpt)'$2'\).$/ { if (def==0) def=cont; }
    $1~/^(kernel|linux(16|efi)?)$/ { if (def==cont) {
                                        kern=$2;
                                        sub($1,""); sub($1,""); sub(/^[ \t]*/,""); app=$0
                                        }
                                    }
    $1~/^initrd(16|efi)?$/ {if (def==cont) init=$2}
    END {if (kern!="") printf("%s %s %s", kern,init,app)}
    '''
    awk_command = ["awk", awk_script, CONFFILE]
    output = subprocess.check_output(awk_command).decode().strip()
    return output

def ogSetWindowsName():
    FUNCNAME = ogSetWindowsName.__name__
    # Variables locales.
    PART = None
    MNTDIR = None
    NAME = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk int_filesys str_name", FUNCNAME + " 1 1 PRACTICA-PC")
        return

    # Error si no se reciben 3 parámetros.
    if len(sys.argv) != 4:
        ogRaiseError(OG_ERR_FORMAT)
        return

    # Error si el nombre supera los 15 caracteres.
    if len(sys.argv[3]) > 15:
        ogRaiseError(OG_ERR_OUTOFLIMIT, sys.argv[3][:15] + "...")
        return

    # Montar el sistema de archivos.
    MNTDIR = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if MNTDIR is None:
        return

    # Asignar nombre.
    NAME = sys.argv[3]

    # Modificar datos de los valores de registro.
    ogSetRegistryValue(MNTDIR, "system", "\\ControlSet001\\Control\\ComputerName\\ComputerName\\ComputerName", NAME)
    ogSetRegistryValue(MNTDIR, "system", "\\ControlSet001\\Services\\Tcpip\\Parameters\\Hostname", NAME)
    ogSetRegistryValue(MNTDIR, "system", "\\ControlSet001\\Services\\Tcpip\\Parameters\\HostName", NAME)
    ogSetRegistryValue(MNTDIR, "system", "\\ControlSet001\\services\\Tcpip\\Parameters\\Hostname", NAME)
    ogSetRegistryValue(MNTDIR, "system", "\\ControlSet001\\Services\\Tcpip\\Parameters\\NV Hostname", NAME)
    ogSetRegistryValue(MNTDIR, "system", "\\ControlSet001\\Services\\Tcpip\\Parameters\\NV HostName", NAME)
    ogSetRegistryValue(MNTDIR, "system", "\\ControlSet001\\services\\Tcpip\\Parameters\\NV Hostname", NAME)

def ogSetWinlogonUser():
    FUNCNAME = ogSetWinlogonUser.__name__
    # Variables locales.
    PART = None
    MNTDIR = None
    NAME = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk int_npartition str_username", FUNCNAME + " 1 1 USUARIO")
        return

    # Error si no se reciben 3 parámetros.
    if len(sys.argv) != 4:
        ogRaiseError(OG_ERR_FORMAT)
        return

    # Montar el sistema de archivos.
    MNTDIR = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if MNTDIR is None:
        return

    # Asignar nombre.
    NAME = sys.argv[3]

    # Modificar datos en el registro.
    ogSetRegistryValue(MNTDIR, "SOFTWARE", "\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\DefaultUserName", NAME)

def ogBootMbrXP():
    FUNCNAME = ogBootMbrXP.__name__
    # Variables locales.
    DISK = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk ", FUNCNAME + " 1")
        return

    # Error si no se recibe 1 parámetro.
    if len(sys.argv) != 2:
        ogRaiseError(OG_ERR_FORMAT)
        return

    DISK = ogDiskToDev(int(sys.argv[1]))
    if DISK is None:
        return
    subprocess.call(["ms-sys", "-z", "-f", DISK])
    subprocess.call(["ms-sys", "-m", "-f", DISK])

def ogBootMbrGeneric():
    FUNCNAME = ogBootMbrGeneric.__name__
    # Variables locales.
    DISK = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk ", FUNCNAME + " 1")
        return

    # Error si no se recibe 1 parámetro.
    if len(sys.argv) != 2:
        ogRaiseError(OG_ERR_FORMAT)
        return

    DISK = ogDiskToDev(int(sys.argv[1]))
    if DISK is None:
        return
    subprocess.call(["ms-sys", "-z", "-f", DISK])
    subprocess.call(["ms-sys", "-s", "-f", DISK])

    # Firma necesaria para Windows equipos UEFI
    SIGNATURE = "0x" + str(uuid.uuid4()).split('-')[0]
    subprocess.call(["ms-sys", "-S", SIGNATURE, DISK])

def ogFixBootSector():
    FUNCNAME = ogFixBootSector.__name__
    # Variables locales.
    PARTYPE = None
    DISK = None
    PART = None
    FILE = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk int_partition ", FUNCNAME + " 1 1 ")
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) != 3:
        ogRaiseError(OG_ERR_FORMAT)
        return

    # TODO, solo si la particion existe
    # TODO, solo si es ntfs o fat
    PARTYPE = ogGetPartitionId(int(sys.argv[1]), int(sys.argv[2]))
    if PARTYPE not in [1, 4, 6, 7, 11, 12, 14, 15, 23, 1792, 61440, 61440]:
        ogRaiseError(OG_ERR_PARTITION)
        return

    ogUnmount(int(sys.argv[1]), int(sys.argv[2])) or ogRaiseError(OG_ERR_PARTITION)

    # Preparando instruccion
    DISK = int(sys.argv[1]) - 1
    PART = int(sys.argv[2])
    FILE = "/tmp/temp$$"
    with open(FILE, "w") as f:
        f.write(f"disk={DISK}\nmain_part={PART}\nfix_first_sector=yes\n")

    subprocess.run(["timeout", "--foreground", "--signal=SIGKILL", "5s", "spartlnx.run", "-cui", "-nm", "-a", "-f", FILE])
    os.remove(FILE)

def ogGetBootMbr():
    FUNCNAME = ogGetBootMbr.__name__
    # Variables locales.
    DISK = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk ", FUNCNAME + " 1")
        return

    # Error si no se recibe 1 parámetro.
    if len(sys.argv) != 2:
        ogRaiseError(OG_ERR_FORMAT)
        return

    DISK = ogDiskToDev(int(sys.argv[1]))
    if DISK is None:
        return
    subprocess.call(["ms-sys", "-f", DISK])

def ogWindowsBootParameters():
    FUNCNAME = ogWindowsBootParameters.__name__
    # Variables locales.
    PART = None
    DISK = None
    BOOTLABEL = None
    BCDFILE = None
    BOOTDISK = None
    BOOTPART = None
    FILE = None
    WINVER = None
    MOUNT = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk int_partition ", FUNCNAME + " 1 1 ")
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) != 3:
        ogRaiseError(OG_ERR_FORMAT)
        return

    ogDiskToDev(int(sys.argv[1]), int(sys.argv[2])) or ogRaiseError(OG_ERR_PARTITION)
    DISK = int(sys.argv[1]) - 1
    PART = int(sys.argv[2])

    # Preparando variables adaptadas a sintaxis windows.
    if ogIsEfiActive():
        BOOTDISK, BOOTPART = ogGetEsp()
        ogUnmount(BOOTDISK, BOOTPART) or ogRaiseError(OG_ERR_PARTITION, "ESP: {} {}".format(BOOTDISK, BOOTPART))
        BOOTDISK -= 1
        BOOTLABEL = "Part-{:02d}-{:02d}".format(int(sys.argv[1]), int(sys.argv[2]))
        BCDFILE = "boot_BCD_file=/EFI/{}/Boot/BCD".format(BOOTLABEL)
    else:
        BOOTDISK = DISK
        BOOTPART = PART
        BCDFILE = ""

    # Obtener versión de Windows.
    WINVER = ogGetOsVersion(int(sys.argv[1]), int(sys.argv[2]))
    if WINVER is None or not WINVER.startswith("Windows"):
        ogRaiseError(OG_ERR_NOTOS, "Windows")
        return

    # Acciones para Windows XP.
    if "XP" in WINVER:
        MOUNT = ogMount(int(sys.argv[1]), int(sys.argv[2]))
        if not os.path.isfile(os.path.join(MOUNT, "boot.ini")):
            ogRaiseError(OG_ERR_NOTFOUND, "boot.ini")
            return
        with open(os.path.join(MOUNT, "boot.ini"), "r") as f:
            content = f.read()
        content = content.replace("partition[0-9]", "partition[{}]".format(PART)).replace("rdisk[0-9]", "rdisk[{}]".format(DISK))
        with open(os.path.join(MOUNT, "tmp.boot.ini"), "w") as f:
            f.write(content)
        os.rename(os.path.join(MOUNT, "tmp.boot.ini"), os.path.join(MOUNT, "boot.ini"))
        return

    ogUnmount(int(sys.argv[1]), int(sys.argv[2])) or ogRaiseError(OG_ERR_PARTITION)

    # Preparando instrucciones para Windows Resume Application, tipo windows, Ramdisk Options, Recovery Environment, Recovery, Windows Boot Manager, Herramienta de diagnóstico de memoria de Windows
    instructions = [
        ("Windows Resume Application", ""),
        (WINVER, ""),
        ("Ramdisk Options", ""),
        ("Windows Recovery Environment", ""),
        ("Windows Recovery", ""),
        ("Windows Boot Manager", ""),
        ("Herramienta de diagnóstico de memoria de Windows", ""),
        ("Herramienta de diagnóstico de memoria de Windows", "")
    ]

    for entry, options in instructions:
        with open(FILE, "w") as f:
            f.write("boot_disk={}\n".format(BOOTDISK))
            f.write("boot_main_part={}\n".format(BOOTPART))
            f.write("{}\n".format(BCDFILE))
            f.write("disk={}\n".format(DISK))
            f.write("main_part={}\n".format(PART))
            f.write("boot_entry={}\n".format(entry))
        subprocess.run(["timeout", "--foreground", "--signal=SIGKILL", "5s", "spartlnx.run", "-cui", "-nm", "-w", "-f", FILE])

    os.remove(FILE)

def ogWindowsRegisterPartition():
    FUNCNAME = ogWindowsRegisterPartition.__name__
    # Variables locales.
    PART = None
    DISK = None
    FILE = None
    REGISTREDDISK = None
    REGISTREDPART = None
    REGISTREDVOL = None
    VERSION = None
    SYSTEMROOT = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndisk_TO_registre int_partition_TO_registre str_NewVolume int_disk int_parition ",
                FUNCNAME + " 1 1 c: 1 1")
        return

    # Error si no se reciben 5 parámetros.
    if len(sys.argv) != 6:
        return ogRaiseError(OG_ERR_FORMAT)

    REGISTREDDISK = int(sys.argv[1])
    REGISTREDPART = int(sys.argv[2])
    REGISTREDVOL = sys.argv[3][0].upper()
    DISK = int(sys.argv[4])
    PART = int(sys.argv[5])
    FILE = "/tmp/temp$$"

    ogDiskToDev(REGISTREDDISK, REGISTREDPART) or ogRaiseError(OG_ERR_PARTITION, "particion a registrar")
    ogDiskToDev(DISK, PART) or ogRaiseError(OG_ERR_PARTITION, "particion de windows")

    TYPE = ogGetOsType(DISK, PART)
    if "Windows" not in TYPE:
        return ogRaiseError(OG_ERR_NOTOS, "no es windows")

    VERSION = ogGetOsVersion(DISK, PART)

    # Systemroot
    if ogGetPath(DISK, PART, "WINDOWS"):
        SYSTEMROOT = "Windows"
    elif ogGetPath(DISK, PART, "WINNT"):
        SYSTEMROOT = "winnt"
    else:
        return ogRaiseError(OG_ERR_NOTOS)

    ogUnmount(DISK, PART)
    DISK -= 1
    REGISTREDDISK -= 1

    # Preparando instruccion Windows Boot Manager
    with open(FILE, "w") as f:
        f.write(f"windows_disk={DISK}\n")
        f.write(f"windows_main_part={PART}\n")
        f.write(f"windows_dir={SYSTEMROOT}\n")
        f.write(f"disk={REGISTREDDISK}\n")
        f.write(f"main_part={REGISTREDPART}\n")
        f.write(f";ext_part\n")
        f.write(f"part_letter={REGISTREDVOL}\n")

    subprocess.run(["timeout", "--foreground", "--signal=SIGKILL", "5s", "spartlnx.run", "-cui", "-nm", "-u", "-f", FILE])

def ogGrubInstallMbr():
    FUNCNAME = ogGrubInstallMbr.__name__
    # Variables locales.
    PART = None
    DISK = None
    VERSION = None
    FIRSTSTAGE = None
    SECONDSTAGE = None
    CHECKOS = None
    KERNELPARAM = None
    BACKUPNAME = ".backup.og"
    EFIDISK = None
    EFIPART = None
    EFISECONDSTAGE = None
    EFISUBDIR = None
    EFIOPTGRUB = ""
    GRUBENTRY = None
    NEWORDER = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndiskSecondStage int_partitionSecondStage bolean_Configure_2ndStage   \"param param \"  ",
                FUNCNAME + " 1 1 FALSE ",
                FUNCNAME + " 1 1 TRUE \"nomodeset irqpoll pci=noacpi quiet splash \" ")
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) < 3:
        return ogRaiseError(OG_ERR_FORMAT)

    DISK = int(sys.argv[1])
    PART = int(sys.argv[2])
    CHECKOS = sys.argv[3].upper() if len(sys.argv) > 3 else "FALSE"
    KERNELPARAM = sys.argv[4] if len(sys.argv) > 4 else ""

    # Error si no es linux.
    VERSION = ogGetOsVersion(DISK, PART)
    if not VERSION.startswith("Linux"):
        return ogRaiseError(OG_ERR_NOTOS, "no es linux")

    # La primera etapa del grub se fija en el primer disco duro
    FIRSTSTAGE = ogDiskToDev(1)

    # Localizar disco segunda etapa del grub
    SECONDSTAGE = ogMount(DISK, PART)
    if SECONDSTAGE is None:
        return

    # Prepara el directorio principal de la segunda etapa
    os.makedirs(os.path.join(SECONDSTAGE, "boot/grub"), exist_ok=True)

    # Localizar directorio segunda etapa del grub
    PREFIXSECONDSTAGE = "/boot/grubMBR"

    # Instalamos grub para EFI en ESP
    if ogIsEfiActive():
        EFIDISK, EFIPART = ogGetEsp()
        # Comprobamos que exista ESP y el directorio para ubuntu
        EFISECONDSTAGE = ogMount(EFIDISK, EFIPART)
        if EFISECONDSTAGE is None:
            ogFormat(EFIDISK, EFIPART, "FAT32")
            EFISECONDSTAGE = ogMount(EFIDISK, EFIPART)
            if EFISECONDSTAGE is None:
                return ogRaiseError(OG_ERR_PARTITION, "ESP")

        EFISUBDIR = "grub"
        # Borramos la configuración anterior
        if os.path.isdir(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR)):
            shutil.rmtree(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR))
        os.makedirs(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "Boot"), exist_ok=True)
        EFIOPTGRUB = f"--removable --no-nvram --uefi-secure-boot --target {ogGetArch()}-efi --efi-directory=os.path.join(EFISECONDSTAGE, 'EFI', EFISUBDIR')"

    # Si Reconfigurar segunda etapa (grub.cfg) == FALSE
    if CHECKOS == "FALSE" and (os.path.isfile(os.path.join(SECONDSTAGE, "boot/grub/grub.cfg")) or os.path.isfile(os.path.join(SECONDSTAGE, "boot/grub/grub.cfg" + BACKUPNAME))):
        # Si no se reconfigura se utiliza el grub.cfg orginal
        if os.path.isfile(os.path.join(SECONDSTAGE, "boot/grub/grub.cfg" + BACKUPNAME)):
            os.rename(os.path.join(SECONDSTAGE, "boot/grub/grub.cfg" + BACKUPNAME), os.path.join(SECONDSTAGE, "boot/grub/grub.cfg"))
        # Si no se reconfigure se borra los ficheros previos de configuración específicos de opengnsys.
        if os.path.isdir(os.path.join(SECONDSTAGE, PREFIXSECONDSTAGE)):
            shutil.rmtree(os.path.join(SECONDSTAGE, PREFIXSECONDSTAGE))
        PREFIXSECONDSTAGE = ""
    else:
        # SI Reconfigurar segunda etapa (grub.cfg) == TRUE

        # llamada a updateBootCache para que aloje la primera fase del ogLive
        updateBootCache()

        if ogIsEfiActive():
            # UEFI: grubSintax necesita grub.cfg para detectar los kernels: si no existe recupero backup.
            if not os.path.isfile(os.path.join(SECONDSTAGE, "boot/grub/grub.cfg")):
                if os.path.isfile(os.path.join(SECONDSTAGE, "boot/grub/grub.cfg" + BACKUPNAME)):
                    os.rename(os.path.join(SECONDSTAGE, "boot/grub/grub.cfg" + BACKUPNAME), os.path.join(SECONDSTAGE, "boot/grub/grub.cfg"))
        else:
            # Evitar detectar modo recovery - mover grub.cfg original a grub.cfg.backup
            os.rename(os.path.join(SECONDSTAGE, "boot/grub/grub.cfg"), os.path.join(SECONDSTAGE, "boot/grub/grub.cfg" + BACKUPNAME))

        # Configurar la sintaxis grub para evitar menus de "recovery" en el OGLive
        with open("/etc/default/grub", "a") as f:
            f.write("GRUB_DISABLE_RECOVERY=\"true\"\n")
            f.write("GRUB_DISABLE_LINUX_UUID=\"true\"\n")

        # Preparar configuración segunda etapa: crear ubicacion
        os.makedirs(os.path.join(SECONDSTAGE, PREFIXSECONDSTAGE, "boot/grub"), exist_ok=True)
        # Preparar configuración segunda etapa: crear cabecera del fichero (ignorar errores)
        subprocess.run(["sed", "-i", "s/^set -e/#set -e/", "/etc/grub.d/00_header"])
        # (ogLive 5.0) Si 'pkgdatadir' está vacía ponemos valor de otros ogLive
        subprocess.run(["sed", "-i", "/grub-mkconfig_lib/i\pkgdatadir=${pkgdatadir:-\"${datarootdir}/grub\"}'", "/etc/grub.d/00_header"])
        subprocess.run(["/etc/grub.d/00_header"], stdout=open(os.path.join(SECONDSTAGE, PREFIXSECONDSTAGE, "boot/grub/grub.cfg"), "w"), stderr=subprocess.DEVNULL)

        # Preparar configuración segunda etapa: crear entrada del sistema operativo
        grubSyntax(KERNELPARAM, stdout=open(os.path.join(SECONDSTAGE, PREFIXSECONDSTAGE, "boot/grub/grub.cfg"), "a"))

        # Renombramos la configuración de grub antigua
        if os.path.isfile(os.path.join(SECONDSTAGE, "boot/grub/grub.cfg")):
            os.rename(os.path.join(SECONDSTAGE, "boot/grub/grub.cfg"), os.path.join(SECONDSTAGE, "boot/grub/grub.cfg" + BACKUPNAME))

    # Instalar el grub
    subprocess.run(["grub-install", "--force", EFIOPTGRUB, f"--root-directory={os.path.join(SECONDSTAGE, PREFIXSECONDSTAGE)}", FIRSTSTAGE])
    EVAL = subprocess.run(["echo", "$?"], capture_output=True, text=True).stdout.strip()

    # Movemos el grubx64.efi
    if ogIsEfiActive():
        shutil.move(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "EFI", "BOOT", "*"), os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "Boot"))
        shutil.rmtree(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "EFI"))
        shutil.copy("/usr/lib/shim/shimx64.efi.signed", os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "Boot", "shimx64.efi"))
        # Nombre OpenGnsys para cargador
        shutil.copy(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "Boot", "grubx64.efi"), os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "Boot", "ogloader.efi"))

        # Creamos entrada NVRAM y la ponemos en segundo lugar
        ogNvramAddEntry("grub", "/EFI/grub/Boot/shimx64.efi")
        GRUBENTRY = ogNvramList().get("grub")
        NEWORDER = ogNvramGetOrder()
        NEWORDER[GRUBENTRY] = NEWORDER.pop(0)
        ogNvramSetOrder(NEWORDER)

    return EVAL

def ogGrubInstallPartition():
    FUNCNAME = ogGrubInstallPartition.__name__
    # Variables locales.
    PART = None
    DISK = None
    VERSION = None
    FIRSTAGE = None
    SECONSTAGE = None
    CHECKOS = None
    KERNELPARAM = None
    BACKUPNAME = ".backup.og"
    EFIDISK = None
    EFIPART = None
    EFISECONDSTAGE = None
    EFISUBDIR = None
    EFIOPTGRUB = ""
    EFIBOOTDIR = ""

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, FUNCNAME + " int_ndiskSecondStage int_partitionSecondStage bolean_Configure_2ndStage   \"param param \" ",
                FUNCNAME + " 1 1 FALSE ",
                FUNCNAME + " 1 1 TRUE \"nomodeset irqpoll pci=noacpi quiet splash \" ")
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) < 3:
        return ogRaiseError(OG_ERR_FORMAT)

    DISK = int(sys.argv[1])
    PART = int(sys.argv[2])
    CHECKOS = sys.argv[3].upper() if len(sys.argv) > 3 else "FALSE"
    KERNELPARAM = sys.argv[4] if len(sys.argv) > 4 else ""
    BACKUPNAME = ".backup.og"

    # Error si no es linux.
    VERSION = ogGetOsVersion(DISK, PART)
    if not VERSION.startswith("Linux"):
        return ogRaiseError(OG_ERR_NOTOS, "no es linux")

    # Localizar primera etapa del grub
    FIRSTAGE = ogDiskToDev(DISK, PART)

    # Localizar disco segunda etapa del grub
    SECONSTAGE = ogMount(DISK, PART)

    # Localizar directorio segunda etapa del grub
    PREFIXSECONDSTAGE = "/boot/grubPARTITION"

    # Si es EFI instalamos el grub en la ESP
    EFIOPTGRUB = ""
    EFIBOOTDIR = ""
    if ogIsEfiActive():
        EFIDISK, EFIPART = ogGetEsp()
        # Comprobamos que exista ESP y el directorio para ubuntu
        EFISECONDSTAGE = ogMount(EFIDISK, EFIPART)
        if EFISECONDSTAGE is None:
            ogFormat(EFIDISK, EFIPART, "FAT32")
            EFISECONDSTAGE = ogMount(EFIDISK, EFIPART)
            if EFISECONDSTAGE is None:
                return ogRaiseError(OG_ERR_PARTITION, "ESP")

        EFISUBDIR = "Part-{:02d}-{:02d}".format(DISK, PART)
        # Borramos la configuración anterior
        if os.path.isdir(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR)):
            shutil.rmtree(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR))
        os.makedirs(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "Boot"), exist_ok=True)
        EFIOPTGRUB = "--removable --no-nvram --uefi-secure-boot --target {}-efi --efi-directory={}".format(ogGetArch(), os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR))
        EFIBOOTDIR = "/boot"

    # Si Reconfigurar segunda etapa (grub.cfg) == FALSE
    if CHECKOS == "FALSE" and (os.path.isfile(os.path.join(SECONSTAGE, "boot/grub/grub.cfg")) or os.path.isfile(os.path.join(SECONSTAGE, "boot/grub/grub.cfg" + BACKUPNAME))):
        # Si no se reconfigura se utiliza el grub.cfg orginal
        if os.path.isfile(os.path.join(SECONSTAGE, "boot/grub/grub.cfg" + BACKUPNAME)):
            os.rename(os.path.join(SECONSTAGE, "boot/grub/grub.cfg" + BACKUPNAME), os.path.join(SECONSTAGE, "boot/grub/grub.cfg"))
        # Si no se reconfigure se borra los ficheros previos de configuración específicos de opengnsys.
        if os.path.isdir(os.path.join(SECONSTAGE, PREFIXSECONDSTAGE)):
            shutil.rmtree(os.path.join(SECONSTAGE, PREFIXSECONDSTAGE))
        PREFIXSECONDSTAGE = ""
    else:
        # SI Reconfigurar segunda etapa (grub.cfg) == TRUE

        if ogIsEfiActive():
            # UEFI: grubSintax necesita grub.cfg para detectar los kernels: si no existe recupero backup.
            if not os.path.isfile(os.path.join(SECONSTAGE, "boot/grub/grub.cfg")):
                if os.path.isfile(os.path.join(SECONSTAGE, "boot/grub/grub.cfg" + BACKUPNAME)):
                    os.rename(os.path.join(SECONSTAGE, "boot/grub/grub.cfg" + BACKUPNAME), os.path.join(SECONSTAGE, "boot/grub/grub.cfg"))
        else:
            # Evitar detectar modo recovery - mover grub.cfg original a grub.cfg.backup
            os.rename(os.path.join(SECONSTAGE, "boot/grub/grub.cfg"), os.path.join(SECONSTAGE, "boot/grub/grub.cfg" + BACKUPNAME))

        # Configurar la sintaxis grub para evitar menus de "recovery" en el OGLive
        with open("/etc/default/grub", "a") as f:
            f.write("GRUB_DISABLE_RECOVERY=\"true\"\n")
            f.write("GRUB_DISABLE_LINUX_UUID=\"true\"\n")

        # Preparar configuración segunda etapa: crear ubicacion
        os.makedirs(os.path.join(SECONSTAGE, PREFIXSECONDSTAGE, "boot/grub"), exist_ok=True)
        # Preparar configuración segunda etapa: crear cabecera del fichero (ignorar errores)
        subprocess.run(["sed", "-i", "s/^set -e/#set -e/", "/etc/grub.d/00_header"])
        # (ogLive 5.0) Si 'pkgdatadir' está vacía ponemos valor de otros ogLive
        subprocess.run(["sed", "-i", "/grub-mkconfig_lib/i\pkgdatadir=${pkgdatadir:-\"${datarootdir}/grub\"}'", "/etc/grub.d/00_header"])
        subprocess.run(["/etc/grub.d/00_header"], stdout=open(os.path.join(SECONSTAGE, PREFIXSECONDSTAGE, "boot/grub/grub.cfg"), "w"), stderr=subprocess.DEVNULL)

        # Preparar configuración segunda etapa: crear entrada del sistema operativo
        grubSyntax(KERNELPARAM, stdout=open(os.path.join(SECONSTAGE, PREFIXSECONDSTAGE, "boot/grub/grub.cfg"), "a"))

        # Renombramos la configuración de grub antigua
        if os.path.isfile(os.path.join(SECONSTAGE, "boot/grub/grub.cfg")):
            os.rename(os.path.join(SECONSTAGE, "boot/grub/grub.cfg"), os.path.join(SECONSTAGE, "boot/grub/grub.cfg" + BACKUPNAME))

    # Instalar el grub
    subprocess.run(["grub-install", "--force", EFIOPTGRUB, "--root-directory={}".format(os.path.join(SECONSTAGE, PREFIXSECONDSTAGE)), FIRSTAGE])
    EVAL = subprocess.run(["echo", "$?"], capture_output=True, text=True).stdout.strip()

    # Movemos el grubx64.efi
    if ogIsEfiActive():
        shutil.move(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "EFI", "BOOT", "*"), os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "Boot"))
        shutil.rmtree(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "EFI"))
        shutil.copy("/usr/lib/shim/shimx64.efi.signed", os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "Boot", "shimx64.efi"))
        # Nombre OpenGnsys para cargador
        shutil.copy(os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "Boot", "grubx64.efi"), os.path.join(EFISECONDSTAGE, "EFI", EFISUBDIR, "Boot", "ogloader.efi"))

    return EVAL

def ogConfigureFstab():
    # Variables locales.
    FSTAB = None
    DEFROOT = None
    PARTROOT = None
    DEFSWAP = None
    PARTSWAP = None
    EFIDISK = None
    EFIPART = None
    EFIDEV = None
    EFIOPT = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) != 3:
        return ogRaiseError(OG_ERR_FORMAT)

    # Error si no se encuentra un fichero etc/fstab en el sistema de archivos.
    FSTAB = ogGetPath(int(sys.argv[1]), int(sys.argv[2]), "/etc/fstab")
    if not FSTAB:
        return ogRaiseError(OG_ERR_NOTFOUND, f"{sys.argv[1]},{sys.argv[2]},/etc/fstab")

    # Hacer copia de seguridad del fichero fstab original.
    shutil.copy2(FSTAB, f"{FSTAB}.backup")

    # Dispositivo del raíz en fichero fstab: 1er campo (si no tiene "#") con 2º campo = "/".
    with open(FSTAB, "r") as f:
        for line in f:
            if not line.startswith("#"):
                fields = line.split()
                if len(fields) >= 2 and fields[1] == "/":
                    DEFROOT = fields[0]
                    break

    PARTROOT = ogDiskToDev(int(sys.argv[1]), int(sys.argv[2]))

    # Configuración de swap (solo 1ª partición detectada).
    PARTSWAP = subprocess.run(["blkid", "-t", "TYPE=swap"], capture_output=True, text=True).stdout.split(":")[0]

    if PARTSWAP:
        # Dispositivo de swap en fichero fstab: 1er campo (si no tiene "#") con 3er campo = "swap".
        with open(FSTAB, "r") as f:
            for line in f:
                if not line.startswith("#"):
                    fields = line.split()
                    if len(fields) >= 3 and fields[2] == "swap":
                        DEFSWAP = fields[0]
                        break

        if DEFSWAP:
            print(f"Hay definicion de SWAP en el FSTAB {DEFSWAP} -> modificamos fichero con nuevo valor {DEFSWAP}->{PARTSWAP}")  # Mensaje temporal.
            subprocess.run(["sed", f"s|{DEFSWAP}|{PARTSWAP}|g ; s|{DEFROOT}|{PARTROOT}|g", f"{FSTAB}.backup"], check=True, text=True, capture_output=True)
        else:
            print("No hay definicion de SWAP y si hay partición SWAP -> moficamos fichero")  # Mensaje temporal.
            subprocess.run(["sed", f"s|{DEFROOT}|{PARTROOT}|g", f"{FSTAB}.backup"], check=True, text=True, capture_output=True)
            with open(FSTAB, "a") as f:
                f.write(f"{PARTSWAP}\tnone\tswap\tsw\t0\t0\n")
    else:
        print("No hay partición SWAP -> configuramos FSTAB")  # Mensaje temporal.
        subprocess.run(["sed", "/swap/d", f"{FSTAB}.backup"], check=True, text=True, capture_output=True)

    # Si es un sistema EFI incluimos partición ESP (Si existe la modificamos)
    if ogIsEfiActive():
        EFIDISK, EFIPART = ogGetEsp()
        EFIDEV = ogDiskToDev(EFIDISK, EFIPART)

        # Opciones de la partición ESP: si no existe ponemos un valor por defecto
        with open(FSTAB, "r") as f:
            for line in f:
                if not line.startswith("#"):
                    fields = line.split()
                    if len(fields) >= 2 and fields[1] == "/boot/efi":
                        EFIOPT = "\t".join(fields[2:6])
                        break

        if not EFIOPT:
            EFIOPT = "vfat\tumask=0077\t0\t1"

        subprocess.run(["sed", "-i", "/boot\/efi/d", FSTAB], check=True, text=True, capture_output=True)
        with open(FSTAB, "a") as f:
            f.write(f"{EFIDEV}\t/boot/efi\t{EFIOPT}\n")

def ogSetLinuxName():
    # Variables locales.
    MNTDIR = None
    ETC = None
    NAME = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        return

    # Error si no se reciben 2 o 3 parámetros.
    if len(sys.argv) == 3:
        # Asignar nombre automático (por defecto, "pc").
        NAME = ogGetHostname() or "pc"
    elif len(sys.argv) == 4:
        # Asignar nombre del 3er parámetro.
        NAME = sys.argv[3]
    else:
        # Formato de ejecución incorrecto.
        return ogRaiseError(OG_ERR_FORMAT)

    # Montar el sistema de archivos.
    MNTDIR = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if not MNTDIR:
        return

    ETC = ogGetPath(int(sys.argv[1]), int(sys.argv[2]), "/etc")
    if os.path.isdir(ETC):
        # cambio de nombre en hostname
        with open(os.path.join(ETC, "hostname"), "w") as f:
            f.write(NAME)

        # cambio de nombre en hosts
        with open(os.path.join(ETC, "hosts"), "w") as f:
            f.write("127.0.0.1       localhost\n")
            f.write("127.0.1.1       {}\n".format(NAME))
            f.write("\n")
            f.write("# The following lines are desirable for IPv6 capable hosts\n")
            f.write("::1     ip6-localhost ip6-loopback\n")
            f.write("fe00::0 ip6-localnet\n")
            f.write("ff00::0 ip6-mcastprefix\n")
            f.write("ff02::1 ip6-allnodes\n")
            f.write("ff02::2 ip6-allrouters\n")

def ogCleanLinuxDevices():
    # Variables locales.
    MNTDIR = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) != 3:
        return ogRaiseError(OG_ERR_FORMAT)

    # Montar el sistema de archivos.
    MNTDIR = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if not MNTDIR:
        return

    # Eliminar fichero de configuración de udev para dispositivos fijos de red.
    if os.path.isfile(os.path.join(MNTDIR, "etc/udev/rules.d/70-persistent-net.rules")):
        os.remove(os.path.join(MNTDIR, "etc/udev/rules.d/70-persistent-net.rules"))

    # Eliminar fichero resume (estado previo de hibernación) utilizado por el initrd scripts-premount
    if os.path.isfile(os.path.join(MNTDIR, "etc/initramfs-tools/conf.d/resume")):
        os.remove(os.path.join(MNTDIR, "etc/initramfs-tools/conf.d/resume"))

def ogGrubAddOgLive():
    FUNCNAME = ogGrubAddOgLive.__name__
    TIMEOUT = None
    DIRMOUNT = None
    GRUBGFC = None
    PARTTABLETYPE = None
    NUMDISK = None
    NUMPART = None
    KERNEL = None
    STATUS = None
    NUMLINE = None
    MENUENTRY = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, f"{FUNCNAME} int_ndisk int_npartition [ time_out ] [ offline|online ]",
                f"{FUNCNAME} 1 1",
                f"{FUNCNAME} 1 6 15 offline")
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) < 3:
        return ogRaiseError(OG_ERR_FORMAT)

    if sys.argv[3].isdigit():
        TIMEOUT = int(sys.argv[3])

    # Error si no existe el kernel y el initrd en la cache.
    # Falta crear nuevo codigo de error.
    if not os.path.isfile(f"{OGCAC}/boot/{oglivedir}/ogvmlinuz") or not os.path.isfile(f"{OGCAC}/boot/{oglivedir}/oginitrd.img"):
        return ogRaiseError(OG_ERR_NOTFOUND, "CACHE: ogvmlinuz, oginitrd.img")

    # Archivo de configuracion del grub
    DIRMOUNT = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    GRUBGFC = f"{DIRMOUNT}/boot/grubMBR/boot/grub/grub.cfg"

    # Error si no existe archivo del grub
    if not os.path.isfile(GRUBGFC):
        return ogRaiseError(OG_ERR_NOTFOUND, GRUBGFC)

    # Si existe la entrada de opengnsys, se borra
    with open(GRUBGFC, "r") as f:
        lines = f.readlines()
    with open(GRUBGFC, "w") as f:
        for line in lines:
            if "menuentry Opengnsys" not in line:
                f.write(line)

    # Tipo de tabla de particiones
    PARTTABLETYPE = ogGetPartitionTableType(int(sys.argv[1])).lower()

    # Localizacion de la cache
    NUMDISK, NUMPART = ogFindCache()
    NUMDISK -= 1

    # kernel y sus opciones. Pasamos a modo usuario
    KERNEL = f"/boot/{oglivedir}/ogvmlinuz {' '.join([arg for arg in sys.argv[4:] if not arg.startswith('ogactiveadmin=')])}"

    # Configuracion offline si existe parametro
    if "offline" in sys.argv:
        STATUS = "offline"
    elif "online" in sys.argv:
        STATUS = "online"
    if STATUS:
        KERNEL = KERNEL.replace("ogprotocol=", "ogprotocol=local ")
        KERNEL += f" ogstatus={STATUS}"

    # Numero de línea de la primera entrada del grub.
    with open(GRUBGFC, "r") as f:
        lines = f.readlines()
    for i, line in enumerate(lines):
        if line.startswith("menuentry"):
            NUMLINE = i + 1
            break

    # Texto de la entrada de opengnsys
    MENUENTRY = f"""menuentry "OpenGnsys" --class opengnsys --class gnu --class os {{
    insmod part_{PARTTABLETYPE}
    insmod ext2
    set root='(hd{NUMDISK},{PARTTABLETYPE}{NUMPART})'
    linux {KERNEL}
    initrd /boot/{oglivedir}/oginitrd.img
    }}
    """

    # Insertamos la entrada de opengnsys antes de la primera entrada existente.
    lines.insert(NUMLINE, MENUENTRY + "\n")
    with open(GRUBGFC, "w") as f:
        f.writelines(lines)

    # Ponemos que la entrada por defecto sea la primera.
    with open(GRUBGFC, "r") as f:
        lines = f.readlines()
    with open(GRUBGFC, "w") as f:
        for line in lines:
            if line.startswith("set default="):
                f.write("set default=\"0\"\n")
            else:
                f.write(line)

    # Si me dan valor para timeout lo cambio en el grub.
    if TIMEOUT:
        with open(GRUBGFC, "r") as f:
            lines = f.readlines()
        with open(GRUBGFC, "w") as f:
            for line in lines:
                if line.startswith("timeout="):
                    f.write(f"timeout={TIMEOUT}\n")
                else:
                    f.write(line)

def ogGrubHidePartitions():
    FUNCNAME = ogGrubHidePartitions.__name__
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, f"{FUNCNAME} int_ndisk int_npartition [ num_disk_partdata num_partdata ]",
                f"{FUNCNAME} 1 2",
                f"{FUNCNAME} 1 2 1 3")
        return
    ogBootLoaderHidePartitions(*sys.argv[1:])
    return

def ogBurgHidePartitions():
    FUNCNAME = ogBurgHidePartitions.__name__
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, f"{FUNCNAME} int_ndisk int_npartition [ num_disk_partdata num_partdata ]",
                f"{FUNCNAME} 1 2",
                f"{FUNCNAME} 1 2 1 3")
        return
    ogBootLoaderHidePartitions(*sys.argv[1:])
    return

def ogBootLoaderHidePartitions():
    FUNCNAME = ogBootLoaderHidePartitions.__name__
    FUNC = None
    DIRMOUNT = None
    GFCFILE = None
    PARTTABLETYPE = None
    WINENTRY = None
    WINPART = None
    ENTRY = None
    LINE = None
    PART = None
    PARTDATA = None
    TEXT = None
    PARTHIDDEN = None
    HIDDEN = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, f"{FUNCNAME} num_disk num_part [ num_disk_partdata num_partdata ]",
                f"{FUNCNAME} 1 2",
                f"{FUNCNAME} 1 2 1 3")
        return

    # Nombre de la función que llama a esta.
    FUNC = sys._getframe().f_back.f_code.co_name

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) < 3:
        return ogRaiseError(OG_ERR_FORMAT)

    if len(sys.argv) == 4:
        PARTDATA = ogDiskToDev(int(sys.argv[3]), int(sys.argv[4]))
    else:
        PARTDATA = 0

    # Archivo de configuracion del grub
    DIRMOUNT = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    # La función debe ser llamada desde ogGrubHidePartitions or ogBurgHidePartitions.
    if FUNC == "ogGrubHidePartitions":
        GFCFILE = f"{DIRMOUNT}/boot/grubMBR/boot/grub/grub.cfg"
    elif FUNC == "ogBurgHidePartitions":
        GFCFILE = f"{DIRMOUNT}/boot/burg/burg.cfg"
    else:
        return ogRaiseError(OG_ERR_FORMAT, "Use ogGrubHidePartitions or ogBurgHidePartitions.")

    # Error si no existe archivo del grub
    if not os.path.isfile(GFCFILE):
        return ogRaiseError(OG_ERR_NOTFOUND, GFCFILE)

    # Si solo hay una particion de Windows me salgo
    if subprocess.run(["fdisk", "-l", ogDiskToDev(), "|", "grep", "NTFS", "|", "wc", "-l"], capture_output=True, text=True).stdout.strip() == "1":
        return 0

    # Elimino llamadas a parttool, se han incluido en otras ejecuciones de esta funcion.
    subprocess.run(["sed", "-i", "/parttool/d", GFCFILE], check=True)

    PARTTABLETYPE = ogGetPartitionTableType(int(sys.argv[1])).lower()

    # Entradas de Windows: numero de linea y particion. De mayor a menor.
    WINENTRY = subprocess.run(["awk", "/menuentry.*Windows/ {gsub(/\\)\"/, \"\"); gsub(/^.*dev/,\"\", \"\"); print NR\":/dev\"$1}", CFGFILE], capture_output=True, text=True).stdout.strip().split("\n")

    # Particiones de Windows, pueden no estar en el grub.
    WINPART = subprocess.run(["fdisk", "-l", ogDiskToDev(), "|", "awk", "/NTFS/ {print $1}", "|", "sed", "1!G;h;$!d"], capture_output=True, text=True).stdout.strip().split("\n")

    # Modifico todas las entradas de Windows.
    for ENTRY in WINENTRY:
        LINE, PART = ENTRY.split(":")
        TEXT = ""
        for PARTHIDDEN in WINPART:
            # Muestro la particion de la entrada actual y la de datos.
            if PARTHIDDEN == PART or PARTHIDDEN == PARTDATA:
                HIDDEN = "-"
            else:
                HIDDEN = "+"
            NUMDISK, NUMPART = ogDevToDisk(PARTHIDDEN)
            TEXT = f"\tparttool (hd{NUMDISK-1},{PARTTABLETYPE}{NUMPART}) hidden{HIDDEN} \n{TEXT}"
        subprocess.run(["sed", "-i", f"{LINE}a\ {TEXT}", GFCFILE], check=True)

    # Activamos la particion que se inicia en todas las entradas de windows.
    subprocess.run(["sed", "-i", "/chainloader/i\\\tparttool ${root} boot+", GFCFILE], check=True)

    CFGFILE = "path/to/config/file"

def ogGrubDeleteEntry():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndisk int_npartition int_disk_delete int_npartition_delete",
                f"{sys.argv[0]} 1 6 2 1")
        return
    ogBootLoaderDeleteEntry(*sys.argv[1:])
    return

def ogBurgDeleteEntry():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndisk int_npartition int_disk_delete int_npartition_delete",
                f"{sys.argv[0]} 1 6 2 1")
        return
    ogBootLoaderDeleteEntry(*sys.argv[1:])
    return

def ogRefindDeleteEntry():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp("ogRefindDeleteEntry", "ogRefindDeleteEntry int_disk_delete int_npartition_delete", "ogRefindDeleteEntry 2 1")
        return
    EFIDISK, EFIPART = ogGetEsp().split()
    ogBootLoaderDeleteEntry(EFIDISK, EFIPART, *sys.argv[1:])
    return

def ogBootLoaderDeleteEntry():
    FUNC = None
    DIRMOUNT = None
    CFGFILE = None
    LABEL = None
    MENUENTRY = None
    DELETEENTRY = None
    ENDENTRY = None
    ENTRY = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndisk int_npartition int_disk_delete int_npartition_delete",
                f"{sys.argv[0]} 1 6 2 1")
        return

    # Si el número de parámetros es menor que 4, salimos.
    if len(sys.argv) < 5:
        return ogRaiseError(OG_ERR_FORMAT)

    # Nombre de la función que llama a esta.
    FUNC = sys._getframe().f_back.f_code.co_name

    # Archivo de configuracion del grub
    DIRMOUNT = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    # La función debe ser llamada desde ogGrubDeleteEntry, ogBurgDeleteEntry or ogRefindDeleteEntry.
    if FUNC == "ogGrubDeleteEntry":
        CFGFILE = f"{DIRMOUNT}/boot/grubMBR/boot/grub/grub.cfg"
    elif FUNC == "ogBurgDeleteEntry":
        CFGFILE = f"{DIRMOUNT}/boot/burg/burg.cfg"
    elif FUNC == "ogRefindDeleteEntry":
        CFGFILE = f"{DIRMOUNT}/EFI/refind/refind.conf"
    else:
        return ogRaiseError(OG_ERR_FORMAT, "Use ogGrubDeleteEntry, ogBurgDeleteEntry or ogRefindDeleteEntry.")

    # Dispositivo
    if os.path.basename(CFGFILE) == "refind.conf":
        LABEL = f"Part-{int(sys.argv[3]):02d}-{int(sys.argv[4]):02d}"
    else:
        LABEL = ogDiskToDev(int(sys.argv[3]), int(sys.argv[4]))

    # Error si no existe archivo de configuración
    if not os.path.isfile(CFGFILE):
        return ogRaiseError(OG_ERR_NOTFOUND, CFGFILE)

    # Numero de linea de cada entrada.
    with open(CFGFILE, "r") as f:
        lines = f.readlines()
    MENUENTRY = [str(i+1) for i, line in enumerate(lines) if "menuentry" in line]

    # Entradas que hay que borrar.
    DELETEENTRY = [str(i+1) for i, line in enumerate(lines) if f"menuentry {LABEL}" in line]

    # Si no hay entradas para borrar, salimos con aviso
    if not DELETEENTRY:
        return ogRaiseError(OG_ERR_NOTFOUND, f"Menuentry {LABEL}")

    # Recorremos el fichero del final hacia el principio.
    ENDENTRY = len(lines)
    for ENTRY in MENUENTRY:
        # Comprobamos si hay que borrar la entrada.
        if ENTRY in DELETEENTRY:
            ENDENTRY -= 1
            lines = lines[:int(ENTRY)-1] + lines[int(ENDENTRY):]

        # Guardamos el número de línea de la entrada, que sera el final de la siguiente.
        ENDENTRY = ENTRY

    with open(CFGFILE, "w") as f:
        f.writelines(lines)

def ogBurgInstallMbr():
    FUNCNAME = ogBurgInstallMbr.__name__
    # Variables locales.
    BINARYAVAILABLE = None
    PART = None
    DISK = None
    DEVICE = None
    MOUNTDISK = None
    FIRSTAGE = None
    SECONSTAGE = None
    PREFIXSECONDSTAGE = None
    CHECKOS = None
    KERNELPARAM = None
    BACKUPNAME = ".backup.og"
    FILECFG = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(FUNCNAME, f"{FUNCNAME}  int_ndiskSecondStage int_partitionSecondStage bolean_Configure_2ndStage   \"param param \"  ",
                f"{FUNCNAME} 1 1 FALSE ",
                f"{FUNCNAME} 1 1 TRUE \"nomodeset irqpoll pci=noacpi quiet splash \" ")
        return

    # Error si no se reciben 2 parametros.
    if len(sys.argv) < 3:
        return ogRaiseError(OG_ERR_FORMAT)

    # Error si no tenemos el binario burg
    BINARYAVAILABLE = subprocess.run(["burg-install", "-v"], capture_output=True, text=True).stdout.strip()
    if BINARYAVAILABLE == "NO":
        if os.path.exists(f"{OGLIB}/burg/burg.tgz"):
            subprocess.run(["tar", "xzvf", f"{OGLIB}/burg/burg.tgz", "--strip", "1"], cwd="/", capture_output=True, text=True)
        else:
            return ogRaiseError(OG_ERR_NOTEXEC, "Binary burg not found")

    DISK = int(sys.argv[1])
    PART = int(sys.argv[2])
    CHECKOS = sys.argv[3].upper() if len(sys.argv) > 3 else "FALSE"
    KERNELPARAM = sys.argv[4] if len(sys.argv) > 4 else ""

    # Controlar disco no uefi
    if ogIsEfiActive():
        return ogRaiseError(OG_ERR_NOTBIOS, "grub4dos solo soporta PC con bios legacy")

    # Controlar particionado tipo msdos
    if not ogCheckStringInGroup(ogGetPartitionTableType(DISK), "MSDOS"):
        return ogRaiseError(OG_ERR_NOMSDOS, "grub2dos requiere particionado tipo MSDOS")

    # Controlar existencia de disco y particion
    DEVICE = ogDiskToDev(DISK)
    if not DEVICE:
        return ogRaiseError(OG_ERR_NOTFOUND)
    MOUNTDISK = ogMount(DISK, PART)
    if not MOUNTDISK:
        return ogRaiseError(OG_ERR_PARTITION)

    # Controlar particion segunda etapa del burg
    if not ogCheckStringInGroup(ogGetFsType(DISK, PART), "CACHE EXT4 EXT3 EXT2"):
        return ogRaiseError(OG_ERR_PARTITION, "burg.cfg soporta solo particiones linux")

    # Controlar acceso de escritura a la particion segunda etapa del burg
    if ogIsReadonly(DISK, PART):
        return ogRaiseError(OG_ERR_NOTWRITE, f"{DISK} {PART}")

    # Asigar la primera etapa del grub en el primer disco duro
    FIRSTAGE = ogDiskToDev(1)

    # Localizar disco segunda etapa del grub
    SECONSTAGE = ogMount(DISK, PART)

    # Preparar el directorio principal de la segunda etapa (y copia los binarios)
    if not os.path.isdir(f"{SECONSTAGE}/boot/burg/"):
        os.makedirs(f"{SECONSTAGE}/boot/burg/")
    shutil.copytree("/boot/burg/", f"{SECONSTAGE}/boot/burg/")
    shutil.copytree(f"{OGLIB}/burg/themes", f"{SECONSTAGE}/boot/burg/themes")

    # No configurar la segunda etapa (grub.cfg). Parámetro FALSE
    if os.path.isfile(f"{SECONSTAGE}/boot/burg/burg.cfg") or os.path.isfile(f"{SECONSTAGE}/boot/burg/burg.cfg{BACKUPNAME}"):
        if CHECKOS == "FALSE":
            subprocess.run(["burg-install", "--force", f"--root-directory={SECONSTAGE}", FIRSTAGE], capture_output=True, text=True)
            return

    # Configurar la segunda etapa (burg.cfg) == tercer parámetro TRUE

    # Llamar a updateBootCache para que aloje la primera fase del ogLive
    updateBootCache()

    # Configurar la sintaxis grub para evitar menus de "recovery" en el OGLive
    with open("/etc/default/grub", "a") as f:
        f.write("GRUB_DISABLE_RECOVERY=\"true\"\n")
        f.write("GRUB_DISABLE_LINUX_UUID=\"true\"\n")

    # Preparar configuración segunda etapa: crear ubicacion
    os.makedirs(f"{SECONSTAGE}{PREFIXSECONDSTAGE}/boot/burg/")

    # Preparar configuración segunda etapa: crear cabecera del fichero
    FILECFG = f"{SECONSTAGE}{PREFIXSECONDSTAGE}/boot/burg/burg.cfg"

    # Preparar configuración segunda etapa: crear entrada del sistema operativo
    with open(FILECFG, "w") as f:
        f.write("set theme_name=OpenGnsys\n")
        f.write("set gfxmode=1024x768\n")
        f.write("set locale_dir=($root)/boot/burg/locale\n")
        f.write("set default=0\n")
        f.write("set timeout=25\n")
        f.write("set lang=es\n")
        f.write("insmod ext2\n")
        f.write("insmod gettext\n")
        f.write("if [ -s $prefix/burgenv ]; then\n")
        f.write("  load_env\n")
        f.write("fi\n")
        f.write("if [ ${prev_saved_entry} ]; then\n")
        f.write("  set saved_entry=${prev_saved_entry}\n")
        f.write("  save_env saved_entry\n")
        f.write("  set prev_saved_entry=\n")
        f.write("  save_env prev_saved_entry\n")
        f.write("  set boot_once=true\n")
        f.write("fi\n")
        f.write("function savedefault {\n")
        f.write("  if [ -z ${boot_once} ]; then\n")
        f.write("    saved_entry=${chosen}\n")
        f.write("    save_env saved_entry\n")
        f.write("  fi\n")
        f.write("}\n")
        f.write("function select_menu {\n")
        f.write("  if menu_popup -t template_popup theme_menu ; then\n")
        f.write("    free_config template_popup template_subitem menu class screen\n")
        f.write("    load_config ${prefix}/themes/${theme_name}/theme ${prefix}/themes/custom/theme_${theme_name}\n")
        f.write("    save_env theme_name\n")
        f.write("    menu_refresh\n")
        f.write("  fi\n")
        f.write("}\n")
        f.write("function toggle_fold {\n")
        f.write("  if test -z $theme_fold ; then\n")
        f.write("    set theme_fold=1\n")
        f.write("  else\n")
        f.write("    set theme_fold=\n")
        f.write("  fi\n")
        f.write("  save_env theme_fold\n")
        f.write("  menu_refresh\n")
        f.write("}\n")
        f.write("function select_resolution {\n")
        f.write("  if menu_popup -t template_popup resolution_menu ; then\n")
        f.write("    menu_reload_mode\n")
        f.write("    save_env gfxmode\n")
        f.write("  fi\n")
        f.write("}\n")
        f.write("if test -f ${prefix}/themes/${theme_name}/theme ; then\n")
        f.write("  insmod coreui\n")
        f.write("  menu_region.text\n")
        f.write("  load_string '+theme_menu { -OpenGnsys { command=\"set theme_name=OpenGnsys\" }}'\n")
        f.write("  load_config ${prefix}/themes/conf.d/10_hotkey\n")
        f.write("  load_config ${prefix}/themes/${theme_name}/theme ${prefix}/themes/custom/theme_${theme_name}\n")
        f.write("  insmod vbe\n")
        f.write("  insmod png\n")
        f.write("  insmod jpeg\n")
        f.write("  set gfxfont=\"Unifont Regular 16\"\n")
        f.write("  menu_region.gfx\n")
        f.write("  vmenu resolution_menu\n")
        f.write("  controller.ext\n")
        f.write("fi\n")

    # Preparar configuración segunda etapa: crear entrada del sistema operativo
    grubSyntax(KERNELPARAM, FILECFG)

    # Instalar el burg
    subprocess.run(["burg-install", "--force", f"--root-directory={SECONSTAGE}", FIRSTAGE], capture_output=True, text=True)

def ogGrubDefaultEntry():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndisk int_npartition int_disk_default_entry int_npartition_default_entry",
                f"{sys.argv[0]} 1 6 1 1")
        return
    ogBootLoaderDefaultEntry(*sys.argv[1:])
    return

def ogBurgDefaultEntry():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndisk int_npartition int_disk_default_entry int_npartition_default_entry",
                f"{sys.argv[0]} 1 6 1 1")
        return
    ogBootLoaderDefaultEntry(*sys.argv[1:])
    return

def ogRefindDefaultEntry():
    EFIDISK, EFIPART = ogGetEsp().split()
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp("ogRefindDefaultEntry", "ogRefindDefaultEntry int_disk_default_entry int_npartition_default_entry",
                "ogRefindDefaultEntry 1 1")
        return
    ogBootLoaderDefaultEntry(EFIDISK, EFIPART, *sys.argv[1:])
    return

def ogBootLoaderDefaultEntry():
    # Variables locales.
    PART = None
    FUNC = None
    DIRMOUNT = None
    LABEL = None
    CFGFILE = None
    DEFAULTENTRY = None
    MENUENTRY = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage int_disk_default_entry int_partitions_default_entry",
                f"{sys.argv[0]} 1 6 1 1")
        return

    # Nombre de la función que llama a esta.
    FUNC = sys._getframe().f_back.f_code.co_name

    # Error si no se reciben 4 parámetros.
    if len(sys.argv) < 5:
        return ogRaiseError(OG_ERR_FORMAT)

    # Error si no puede montar sistema de archivos.
    DIRMOUNT = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if not DIRMOUNT:
        return

    # Comprobamos que exista fichero de configuración
    # La función debe ser llamada desde ogGrubDefaultEntry, ogBurgDefaultEntry or ogRefindDefaultEntry.
    if FUNC == "ogGrubDefaultEntry":
        CFGFILE = f"{DIRMOUNT}/boot/grubMBR/boot/grub/grub.cfg"
    elif FUNC == "ogBurgDefaultEntry":
        CFGFILE = f"{DIRMOUNT}/boot/burg/burg.cfg"
    elif FUNC == "ogRefindDefaultEntry":
        CFGFILE = f"{DIRMOUNT}/EFI/refind/refind.conf"
    else:
        return ogRaiseError(OG_ERR_FORMAT, "Use ogGrubDefaultEntry, ogBurgDefaultEntry or ogRefindDefaultEntry.")

    # Error si no existe archivo de configuración
    if not os.path.isfile(CFGFILE):
        return ogRaiseError(OG_ERR_NOTFOUND, CFGFILE)

    # Dispositivo
    if os.path.basename(CFGFILE) == "refind.conf":
        LABEL = f"Part-{int(sys.argv[3]):02d}-{int(sys.argv[4]):02d}"
    else:
        LABEL = ogDiskToDev(int(sys.argv[3]), int(sys.argv[4]))

    # Número de línea de la entrada por defecto en CFGFILE (primera de la partición).
    with open(CFGFILE, "r") as f:
        lines = f.readlines()
    for i, line in enumerate(lines):
        if f"menuentry {LABEL}" in line:
            DEFAULTENTRY = i + 1
            break

    # Si no hay entradas para borrar me salgo con aviso
    if not DEFAULTENTRY:
        return ogRaiseError(OG_ERR_NOTFOUND, f"No menuentry {LABEL}")

    # Número de la de linea por defecto en el menú de usuario
    with open(CFGFILE, "r") as f:
        menu_entries = [i for i, line in enumerate(f) if "menuentry" in line]
    MENUENTRY = menu_entries.index(DEFAULTENTRY) + 1

    if os.path.basename(CFGFILE) == "refind.conf":
        subprocess.run(["sed", "-i", "/default_selection.*$/d", CFGFILE], check=True)
        subprocess.run(["sed", "-i", f"1 i\\default_selection {MENUENTRY}", CFGFILE], check=True)
    else:
        # En grub y burg las líneas empiezan a contar desde cero
        MENUENTRY -= 1
        subprocess.run(["sed", "-i", f"s/set default=\"?[0-9]*\"?$/set default=\"{MENUENTRY}\"/g", CFGFILE], check=True)

def ogGrubOgliveDefaultEntry():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage",
                f"{sys.argv[0]} 1 6")
        return
    ogBootLoaderOgliveDefaultEntry(*sys.argv[1:])
    return

def ogBurgOgliveDefaultEntry():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage",
                f"{sys.argv[0]} 1 6")
        return
    ogBootLoaderOgliveDefaultEntry(*sys.argv[1:])
    return

def ogRefindOgliveDefaultEntry():
    EFIDISK, EFIPART = ogGetEsp().split()
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp("ogRefindOgliveDefaultEntry", "ogRefindOgliveDefaultEntry",
                "ogRefindOgliveDefaultEntry")
        return
    ogBootLoaderOgliveDefaultEntry(EFIDISK, EFIPART)
    return

def ogBootLoaderOgliveDefaultEntry():
    # Variables locales.
    PART = None
    FUNC = None
    CFGFILE = None
    NUMENTRY = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage",
                f"{sys.argv[0]} 1 6")
        return

    # Nombre de la función que llama a esta.
    FUNC = sys._getframe().f_back.f_code.co_name

    # Error si no se reciben 2 parametros.
    if len(sys.argv) < 3:
        return ogRaiseError(OG_ERR_FORMAT)

    # Error si no puede montar sistema de archivos.
    PART = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if not PART:
        return

    # La función debe ser llamada desde ogGrubOgliveDefaultEntry, ogBurgOgliveDefaultEntry or ogRefindOgliveDefaultEntry.
    if FUNC == "ogGrubOgliveDefaultEntry":
        CFGFILE = f"{PART}/boot/grubMBR/boot/grub/grub.cfg"
    elif FUNC == "ogBurgOgliveDefaultEntry":
        CFGFILE = f"{PART}/boot/burg/burg.cfg"
    elif FUNC == "ogRefindOgliveDefaultEntry":
        CFGFILE = f"{PART}/EFI/refind/refind.conf"
    else:
        return ogRaiseError(OG_ERR_FORMAT, "Use ogGrubOgliveDefaultEntry, ogBurgOgliveDefaultEntry or ogRefindOgliveDefaultEntry.")

    # Comprobamos que exista fichero de configuración
    if not os.path.isfile(CFGFILE):
        return ogRaiseError(OG_ERR_NOTFOUND, CFGFILE)

    # Detectamos cual es la entrada de ogLive
    with open(CFGFILE, "r") as f:
        lines = f.readlines()
    for i, line in enumerate(lines):
        if "menuentry" in line and "OpenGnsys Live" in line:
            NUMENTRY = i + 1
            break

    # Si no existe entrada de ogLive nos salimos
    if not NUMENTRY:
        return ogRaiseError(OG_ERR_NOTFOUND, f"menuentry OpenGnsys Live in {CFGFILE}")

    if os.path.basename(CFGFILE) == "refind.conf":
        subprocess.run(["sed", "-i", "/default_selection.*$/d", CFGFILE], check=True)
        subprocess.run(["sed", "-i", f"1 i\\default_selection {NUMENTRY}", CFGFILE], check=True)
    else:
        NUMENTRY -= 1
        subprocess.run(["sed", "-i", f"s/set default=\"?[0-9]+\"?/set default=\"{NUMENTRY}\"/g", CFGFILE], check=True)

    # Generate the help message based on the function name
    MSG = f"MSG_HELP_{FUNC}"
    help_message = f"{globals()[MSG].split('.')[0]}: {' '.join(sys.argv[1:])}"
    print(help_message)

def ogGrubSecurity():
    # Variables locales.
    SECONDSTAGE = None
    GRUBGFC = None
    FILE = None
    USER = None
    PASSWD = None
    ENCRYPTPASSWD = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage [USER] [PASSWORD]",
                f"{sys.argv[0]} 1 1",
                f"{sys.argv[0]} 1 2 user clave")
        return

    # Error si no se reciben 2 parámetros.
    if len(sys.argv) < 3:
        return ogRaiseError(OG_ERR_FORMAT, f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage [USER] [PASSWORD]")

    # localizar disco segunda etapa del grub
    SECONDSTAGE = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if not SECONDSTAGE:
        return

    GRUBGFC = subprocess.run(["ls", f"{SECONDSTAGE}/{{,boot/}}"
                                            "{{grubMBR,grubPARTITION}/boot/,}}"
                                            "{{grub{,2},{,efi/}EFI/*}}/"
                                            "{{menu.lst,grub.cfg,grub.cfg.backup.og}}"],
                                capture_output=True, text=True, shell=True).stdout.strip().split("\n")

    # comprobamos que exista el archivo de configuración.
    if not GRUBGFC:
        return ogRaiseError(OG_ERR_NOTFOUND, "grub.cfg")

    USER = sys.argv[3] if len(sys.argv) > 3 else "root"
    PASSWD = sys.argv[4] if len(sys.argv) > 4 else ""

    ENCRYPTPASSWD = subprocess.run(["echo", "-e", f"{PASSWD}\n{PASSWD}"],
                                    capture_output=True, text=True).stdout.strip().split("\n")
    ENCRYPTPASSWD = subprocess.run(["grub-mkpasswd-pbkdf2"],
                                    input="\n".join(ENCRYPTPASSWD),
                                    capture_output=True, text=True).stdout.strip().split("\n")
    ENCRYPTPASSWD = subprocess.run(["sed", "-e", "1,2d", "-e", "s/^.*grub/grub/"],
                                    input="\n".join(ENCRYPTPASSWD),
                                    capture_output=True, text=True).stdout.strip()

    for FILE in GRUBGFC:
        # Eliminamos configuración anterior
        subprocess.run(["sed", "-i", "-e", "/superusers/d", "-e", "/password_pbkdf2/d", FILE], check=True)

        # Configuramos grub.cfg para que sólo permita editar o entrar en línea de comandos al usuario especificado
        if PASSWD != "":
            subprocess.run(["sed", "-i", f"1i\\password_pbkdf2 {USER} {ENCRYPTPASSWD}", FILE], check=True)
        subprocess.run(["sed", "-i", f"1i\\set superusers=\"{USER}\"", FILE], check=True)

        # Permitimos que se seleccionen las entradas
        subprocess.run(["sed", "-i", "/\"menuentry \"/s/\"{\"/--unrestricted {\"/", FILE], check=True)

def ogGrubSetTheme():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage str_themeName",
                f"{sys.argv[0]} 1 4 ThemeBasic",
                f"{sys.argv[0]} $(ogFindCache) ThemeBasic")
        return
    ogBootLoaderSetTheme(*sys.argv[1:])
    return

def ogBurgSetTheme():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage str_themeName",
                f"{sys.argv[0]} 1 4 ThemeBasic",
                f"{sys.argv[0]} $(ogFindCache) ThemeBasic")
        print("Temas disponibles:", os.listdir(f"{OGLIB}/boot/burg/themes/"))
        return
    ogBootLoaderSetTheme(*sys.argv[1:])
    return

def ogRefindSetTheme():
    PART = None
    DIRTHEME = None
    CFGFILE = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} str_themeName",
                f"{sys.argv[0]} ThemeBasic")
        print(f"\nThemes in {OGLIB}/refind:\n{os.listdir(f'{OGLIB}/refind/themes/')}")
        return

    # Detectamos partición ESP
    EFIDISK, EFIPART = ogGetEsp().split()
    PART = ogMount(int(EFIDISK), int(EFIPART))
    if not PART:
        return

    DIRTHEME = f"{PART}/EFI/refind/themes"
    CFGFILE = f"{PART}/EFI/refind/refind.conf"

    # Para utilizar ogBootLoaderSetTheme es necesario la entrada set theme_name
    if os.path.isfile(CFGFILE):
        subprocess.run(["sed", "-i", "1 i\\set theme_name=none", CFGFILE], check=True)
    else:
        return ogRaiseError(OG_ERR_NOTFOUND, CFGFILE)

    # Creamos el directorio para los temas
    if not os.path.isdir(DIRTHEME):
        os.makedirs(DIRTHEME)

    ogBootLoaderSetTheme(EFIDISK, EFIPART, *sys.argv[1:])
    return

def ogBootLoaderSetTheme():
    # Variables locales.
    PART = None
    CFGFILE = None
    THEME = None
    NEWTHEME = None
    BOOTLOADER = None
    MSG = None
    NEWTHEMECFG = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage str_themeName",
                f"{sys.argv[0]} 1 4 ThemeBasic",
                f"{sys.argv[0]} $(ogFindCache) ThemeBasic")
        return

    NEWTHEME = sys.argv[3]

    # Nombre de la función que llama a esta.
    FUNC = sys._getframe().f_back.f_code.co_name
    FUNC = FUNC.split()[1]

    # Error si no se reciben 3 parametros.
    if len(sys.argv) < 4:
        return ogRaiseError(OG_ERR_FORMAT, f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage str_themeName")

    # Error si no puede montar sistema de archivos.
    PART = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if not PART:
        return

    # La función debe ser llamada desde ogGrubSetTheme, ogBurgSetTheme or ogRefindSetTheme.
    if FUNC == "ogGrubSetTheme":
        BOOTLOADER = "grub"
        BOOTLOADERDIR = "boot/grubMBR"
        CFGFILE = f"{PART}/boot/grubMBR/boot/grub/grub.cfg"
        return ogRaiseError(OG_ERR_FORMAT, "ogGrubSetTheme not supported")
    elif FUNC == "ogBurgSetTheme":
        BOOTLOADER = "burg"
        BOOTLOADERDIR = "boot/burg"
        CFGFILE = f"{PART}/boot/burg/burg.cfg"
    elif FUNC == "ogRefindSetTheme":
        BOOTLOADER = "refind"
        BOOTLOADERDIR = "EFI/refind"
        CFGFILE = f"{PART}/EFI/refind/refind.conf"
    else:
        return ogRaiseError(OG_ERR_FORMAT, "Use ogGrubSetTheme, ogBurgSetTheme or ogRefindSetTheme.")

    # Comprobamos que exista fichero de configuración
    if not os.path.isfile(CFGFILE):
        return ogRaiseError(OG_ERR_NOTFOUND, CFGFILE)

    # Detectamos cual es el tema asignado
    with open(CFGFILE, "r") as f:
        lines = f.readlines()
    for line in lines:
        if line.startswith("set theme_name="):
            THEME = line.split("=")[1].strip()
            break

    # Si no existe entrada de theme_name nos salimos
    if not THEME:
        return ogRaiseError(OG_ERR_NOTFOUND, f"theme_name in {CFGFILE}")

    # Actualizamos el tema del servidor a la particion
    if os.path.isdir(f"{OGLIB}/{BOOTLOADER}/themes/{NEWTHEME}"):
        # Para refind es necesario que exista theme.conf en el directorio del tema.
        if BOOTLOADER == "refind":
            NEWTHEMECFG = f"{OGLIB}/{BOOTLOADER}/themes/{NEWTHEME}/theme.conf"
            if not os.path.isfile(NEWTHEMECFG):
                return ogRaiseError(OG_ERR_NOTFOUND, "theme.conf")
            with open(NEWTHEMECFG, "r") as f:
                theme_lines = f.readlines()
            with open(CFGFILE, "a") as f:
                for line in theme_lines:
                    if not line.startswith("#"):
                        f.write(line)
            # eliminamos "set theme" es de grub y no de refind
            subprocess.run(["sed", "-i", "/theme_name/d", CFGFILE], check=True)
        shutil.copytree(f"{OGLIB}/{BOOTLOADER}/themes/{NEWTHEME}", f"{PART}/{BOOTLOADERDIR}/themes/")

    # Verificamos que el tema esta en la particion
    if not os.path.isdir(f"{PART}/{BOOTLOADERDIR}/themes/{NEWTHEME}"):
        return ogRaiseError(OG_ERR_NOTFOUND, f"theme_name={NEWTHEME} in {PART}/{BOOTLOADERDIR}/themes/")

    # Cambiamos la entrada el fichero de configuración.
    subprocess.run(["sed", "-i", f"s/set theme_name={THEME}/set theme_name={NEWTHEME}/g", CFGFILE], check=True)

def ogGrubSetAdminKeys():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage str_bolean",
                f"{sys.argv[0]} 1 4 FALSE",
                f"{sys.argv[0]} $(ogFindCache) ThemeBasic")
        return
    ogBootLoaderSetAdminKeys(*sys.argv[1:])
    return

def ogBurgSetAdminKeys():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage str_bolean",
                f"{sys.argv[0]} 1 4 TRUE",
                f"{sys.argv[0]} $(ogFindCache) FALSE")
        return
    ogBootLoaderSetAdminKeys(*sys.argv[1:])
    return

def ogBootLoaderSetAdminKeys():
    # Variables locales.
    PART = None
    CFGFILE = None
    BOOTLOADER = None
    BOOTLOADERDIR = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage str_bolean",
                f"{sys.argv[0]} 1 4 TRUE",
                f"{sys.argv[0]} $(ogFindCache) FALSE")
        return

    # Nombre de la función que llama a esta.
    FUNC = sys._getframe().f_back.f_code.co_name
    FUNC = FUNC.split()[1]

    # Error si no se reciben 3 parametros.
    if len(sys.argv) < 4:
        return ogRaiseError(OG_ERR_FORMAT, f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage str_bolean")

    # Error si no puede montar sistema de archivos.
    PART = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if not PART:
        return

    # La función debe ser llamada desde ogGrubSetAdminKeys or ogBurgSetAdminKeys.
    if FUNC == "ogGrubSetAdminKeys":
        BOOTLOADER = "grub"
        BOOTLOADERDIR = "grubMBR"
        CFGFILE = f"{PART}/boot/grubMBR/boot/grub/grub.cfg"
        return ogRaiseError(OG_ERR_FORMAT, "ogGrubSetAdminKeys not supported")
    elif FUNC == "ogBurgSetAdminKeys":
        BOOTLOADER = "burg"
        BOOTLOADERDIR = "burg"
        CFGFILE = f"{PART}/boot/burg/burg.cfg"
    else:
        return ogRaiseError(OG_ERR_FORMAT, "Use ogGrubSetAdminKeys")

    # Comprobamos que exista fichero de configuración
    if not os.path.isfile(CFGFILE):
        return ogRaiseError(OG_ERR_NOTFOUND, CFGFILE)

    if sys.argv[3].lower() == "true":
        shutil.move(f"{OGCAC}/boot/{BOOTLOADERDIR}/themes/conf.d/10_hotkey.disabled", f"{OGCAC}/boot/{BOOTLOADERDIR}/themes/conf.d/10_hotkey")
    elif sys.argv[3].lower() == "false":
        shutil.move(f"{OGCAC}/boot/{BOOTLOADERDIR}/themes/conf.d/10_hotkey", f"{OGCAC}/boot/{BOOTLOADERDIR}/themes/conf.d/10_hotkey.disabled")
    else:
        return ogRaiseError(OG_ERR_FORMAT, "str bolean unknown")
    
def ogGrubSetTimeOut():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage int_timeout_seconds",
                f"{sys.argv[0]} 1 4 50",
                f"{sys.argv[0]} $(ogFindCache) 50")
        return
    ogBootLoaderSetTimeOut(*sys.argv[1:])
    return

def ogBurgSetTimeOut():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage str_timeout_seconds",
                f"{sys.argv[0]} 1 4 50",
                f"{sys.argv[0]} $(ogFindCache) 50")
        return
    ogBootLoaderSetTimeOut(*sys.argv[1:])
    return

def ogRefindSetTimeOut():
    EFIDISK, EFIPART = ogGetEsp().split()
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_timeout_seconds",
                f"{sys.argv[0]} 50")
        return
    ogBootLoaderSetTimeOut(EFIDISK, EFIPART, *sys.argv[1:])
    return

def ogBootLoaderSetTimeOut():
    # Variables locales.
    PART = None
    CFGFILE = None
    TIMEOUT = None
    BOOTLOADER = None
    BOOTLOADERDIR = None
    MSG = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage int_timeout_seconds",
                f"{sys.argv[0]} 1 4 50",
                f"{sys.argv[0]} $(ogFindCache) 50")
        return

    # Nombre de la función que llama a esta.
    FUNC = sys._getframe().f_back.f_code.co_name
    FUNC = FUNC.split()[1]

    # Error si no se reciben 3 parametros.
    if len(sys.argv) < 4:
        return ogRaiseError(OG_ERR_FORMAT, f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage int_timeout_seconds")

    # Error si no puede montar sistema de archivos.
    PART = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if not PART:
        return

    # La función debe ser llamada desde ogGrubSetTimeOut, ogBurgSetTimeOut or ogRefindSetTimeOut.
    if FUNC == "ogGrubSetTimeOut":
        BOOTLOADER = "grub"
        BOOTLOADERDIR = "boot/grubMBR"
        CFGFILE = f"{PART}/boot/grubMBR/boot/grub/grub.cfg"
    elif FUNC == "ogBurgSetTimeOut":
        BOOTLOADER = "burg"
        BOOTLOADERDIR = "boot/burg"
        CFGFILE = f"{PART}/boot/burg/burg.cfg"
    elif FUNC == "ogRefindSetTimeOut":
        BOOTLOADER = "refind"
        BOOTLOADERDIR = "EFI/refind"
        CFGFILE = f"{PART}/EFI/refind/refind.conf"
    else:
        return ogRaiseError(OG_ERR_FORMAT, "Use ogGrubSetTimeOut, ogBurgSetTimeOut or ogRefindSetTimeOut.")

    # Comprobamos que exista fichero de configuración
    if not os.path.isfile(CFGFILE):
        return ogRaiseError(OG_ERR_NOTFOUND, CFGFILE)

    # Asignamos el timeOut.
    if BOOTLOADER == "refind":
        subprocess.run(["sed", "-i", f"s/timeout.*$/timeout {sys.argv[3]}/g", CFGFILE], check=True)
    else:
        subprocess.run(["sed", "-i", f"s/timeout=.*$/timeout={sys.argv[3]}/g", CFGFILE], check=True)

def ogGrubSetResolution():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage [str_resolution]",
                f"{sys.argv[0]} 1 4 1024x768",
                f"{sys.argv[0]} $(ogFindCache) 1024x768",
                f"{sys.argv[0]} 1 4")
        return
    ogBootLoaderSetResolution(*sys.argv[1:])
    return

def ogBurgSetResolution():
    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage [str_resolution]",
                f"{sys.argv[0]} 1 4 1024x768",
                f"{sys.argv[0]} $(ogFindCache) 1024x768",
                f"{sys.argv[0]} 1 4")
        return
    ogBootLoaderSetResolution(*sys.argv[1:])
    return

def ogBootLoaderSetResolution():
    # Variables locales.
    PART = None
    CFGFILE = None
    RESOLUTION = None
    NEWRESOLUTION = None
    DEFAULTRESOLUTION = "1024x768"
    BOOTLOADER = None
    BOOTLOADERDIR = None
    MSG = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage [str_resolution]",
                f"{sys.argv[0]} 1 4 1024x768",
                f"{sys.argv[0]} $(ogFindCache) 1024x768",
                f"{sys.argv[0]} 1 4")
        return

    # Nombre de la función que llama a esta.
    FUNC = sys._getframe().f_back.f_code.co_name
    FUNC = FUNC.split()[1]

    # Error si no se reciben 2 parametros.
    if len(sys.argv) < 3:
        return ogRaiseError(OG_ERR_FORMAT, f"{sys.argv[0]} int_ndiskSecondStage int_partitionSecondStage [str_resolution]")

    # Error si no puede montar sistema de archivos.
    PART = ogMount(int(sys.argv[1]), int(sys.argv[2]))
    if not PART:
        return

    # La función debe ser llamada desde ogGrubSetResolution, ogBurgSetResolution or ogRefindSetResolution.
    if FUNC == "ogGrubSetResolution":
        BOOTLOADER = "grub"
        BOOTLOADERDIR = "boot/grubMBR"
        CFGFILE = f"{PART}/boot/grubMBR/boot/grub/grub.cfg"
        return ogRaiseError(OG_ERR_FORMAT, "ogGrubSetResolution not supported")
    elif FUNC == "ogBurgSetResolution":
        BOOTLOADER = "burg"
        BOOTLOADERDIR = "boot/burg"
        CFGFILE = f"{PART}/boot/burg/burg.cfg"
    else:
        return ogRaiseError(OG_ERR_FORMAT, "Use ogGrubSetResolution, ogBurgSetResolution or ogRefindSetResolution.")

    DEFAULTRESOLUTION = "1024x768"

    # Comprobamos que exista fichero de configuración
    if not os.path.isfile(CFGFILE):
        return ogRaiseError(OG_ERR_NOTFOUND, CFGFILE)

    # Controlar variable a considerar vga (default template) o video (menu)
    # Si solo dos parametros autoconfiguracion basado en el parametro vga de las propiedad menu. si no hay menu asignado es 788 por defecto
    if len(sys.argv) == 3:
        if "video" in globals() and video:
            NEWRESOLUTION = video.split(":")[1].split("-")[0]
        if "vga" in globals() and vga:
            if vga in ["788", "789", "814"]:
                NEWRESOLUTION = "800x600"
            elif vga in ["791", "792", "824"]:
                NEWRESOLUTION = "1024x768"
            elif vga == "355":
                NEWRESOLUTION = "1152x864"
            elif vga in ["794", "795", "829"]:
                NEWRESOLUTION = "1280x1024"
    elif len(sys.argv) == 4:
        # Comprobamos que el parametro 3 cumple formato NNNNxNNNN
        if re.match(r"[0-9]{3,4}[x][0-9]{3,4}$", sys.argv[3]):
            NEWRESOLUTION = sys.argv[3]
        else:
            return ogRaiseError(OG_ERR_FORMAT, "param 3 is not a valid resolution: 800x600, 1024x768, 1152x864, 1280x1024, 1600x1200")

    # Si no existe NEWRESOLUTION asignamos la DEFAULTRESOLUTION
    if not NEWRESOLUTION:
        NEWRESOLUTION = DEFAULTRESOLUTION

    # Cambiamos la entrada el fichero de configuración.
    subprocess.run(["sed", "-i", f"s/gfxmode=.*$/gfxmode={NEWRESOLUTION}/g", CFGFILE], check=True)

def ogRefindSetResolution():
    # Variables locales.
    PART = None
    CFGFILE = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_resolution1 [int_resolution2]",
                f"{sys.argv[0]} 1366 768",
                f"{sys.argv[0]} 1")
        return

    # Error si no se reciben 2 parametros.
    if len(sys.argv) < 2:
        return ogRaiseError(OG_ERR_FORMAT, f"{sys.argv[0]} int_resolution1 [int_resolution2]")

    # Error si no puede montar sistema de archivos.
    PART = ogMount(ogGetEsp())
    if not PART:
        return

    # Comprobamos que exista fichero de configuración
    CFGFILE = f"{PART}/EFI/refind/refind.conf"
    if not os.path.isfile(CFGFILE):
        return ogRaiseError(OG_ERR_NOTFOUND, CFGFILE)

    # Borramos resolucion anterior y configuramos la nueva
    subprocess.run(["sed", "-i", "/^resolution/d", CFGFILE], check=True)
    subprocess.run(["sed", "-i", f"1 i\\resolution {sys.argv[1]} {sys.argv[2]}", CFGFILE], check=True)

def ogRefindInstall(bool_autoconfig):
    # Variables locales.
    CONFIG = bool_autoconfig
    EFIDISK, EFIPART = ogGetEsp().split()
    EFIDEVICE = ogDiskToDev(EFIDISK, EFIPART)
    EFIMNT = ogMount(EFIDISK, EFIPART)
    EFIDIR = f"{EFIMNT}/EFI"
    SHIM = ogGetPath("/usr/lib/shim/shimx64.efi.signed")

    # Si se solicita, mostrar ayuda.
    if CONFIG == "help":
        ogHelp("ogRefindInstall", "ogRefindInstall boolean_autoconfig", "ogRefindInstall TRUE")
        return

    # Error si no se reciben 2 parametros.
    if not bool_autoconfig:
        ogRaiseError(OG_ERR_FORMAT, "ogRefindInstall boolean_autoconfig")
        return

    # Error si no puede montar sistema de archivos.
    if not EFIMNT:
        ogRaiseError(OG_ERR_PARTITION, "mount ESP")
        return

    # Comprobamos que exista shimx64
    if not SHIM:
        ogRaiseError(OG_ERR_NOTFOUND, "shimx64.efi.signed")
        return

    # Si existe configuración anterior de refind la borro
    if os.path.isdir(f"{EFIDIR}/refind"):
        shutil.rmtree(f"{EFIDIR}/refind")

    # Instalamos rEFInd.
    subprocess.run(["refind-install", "--yes", "--alldrivers", "--root", EFIMNT, "--shim", SHIM])

    # Firmo refind con certificado de OpenGnsys
    shutil.move(f"{EFIDIR}/refind/grubx64.efi", f"{EFIDIR}/refind/grubx64.efi-unsigned")
    subprocess.run(["sbsign", "--key", f"{OGETC}/ssl/private/opengnsys.key", "--cert", f"{OGETC}/ssl/certs/opengnsys.crt", "--output", f"{EFIDIR}/refind/grubx64.efi", f"{EFIDIR}/refind/grubx64.efi-unsigned"])

    # Copio los certificados
    shutil.copytree("/etc/refind.d/keys", f"{EFIDIR}/refind/keys")
    # Copio certificado opengnsys
    shutil.copy(f"{OGETC}/ssl/certs/opengnsys.*", f"{EFIDIR}/refind/keys")

    # Ponemos la entrada en NVRAM en el segundo lugar del orden de arranque
    NEWORDER = ogNvramGetOrder().split(",")
    NEWORDER[0], NEWORDER[1] = NEWORDER[1], NEWORDER[0]
    ogNvramSetOrder(",".join(NEWORDER))

    # Borramos configuración linux
    if os.path.isfile(f"{EFIMNT}/boot/refind_linux.conf"):
        shutil.move(f"{EFIMNT}/boot/refind_linux.conf", f"{EFIMNT}/boot/refind_linux.conf.ogbackup")

    # Eliminamos punto de montaje (por si ejecutamos más de una vez)
    subprocess.run(["umount", f"{EFIMNT}/boot/efi"])

    # Para la configuración del ogLive
    ogMountCache()
    if ogMountCache() == 0:
        # Detectamos si hay ogLive
        CACHEDEVICE = ogDiskToDev(ogFindCache())
        OGLIVE = glob.glob(f"{OGCAC}/boot/ogvmlinuz*")[0]
        # Obtenemos parametros del kernel y sustituimos root
        # La línea de opciones no puede contener la cadena initrd.
        CMDLINE = " ".join(sys.argv[3:])
        CMDLINE = f"root={CACHEDEVICE} {CMDLINE.replace('ogvmlinuz', '')}"

        # Icono para la entrada de menú
        OGICON = glob.glob(f"{OGLIB}/refind/icons/so_opengnsys.png")
        OGICON = OGICON[0] if OGICON else f"{EFIDIR}/refind/icons/os_unknown.png"
        shutil.copy(OGICON, f"{OGCAC}/.VolumeIcon.png")

    # Configuramos rEFInd si es necesario
    CFGFILE = f"{EFIDIR}/refind/refind.conf"
    if CONFIG == "TRUE":
        with open(CFGFILE, "a") as f:
            f.write("\n\n# Configuración OpenGnsys\n")
            # Excluimos dispositivos distintos de ESP y CACHE
            DEVICES = subprocess.run(["blkid", "-s", "PARTUUID"], capture_output=True, text=True).stdout
            DEVICES = DEVICES.split("\n")
            DEVICES = [device.split("=")[1].replace('"', '') for device in DEVICES if device]
            DEVICES = [device for device in DEVICES if device != EFIDEVICE and device != CACHEDEVICE]
            DEVICES = " ".join(DEVICES)
            f.write(f"dont_scan_volumes {DEVICES}\n")
            # Excluimos en la ESP los directorios de los sistemas operativos
            f.write("dont_scan_dirs EFI/microsoft,EFI/ubuntu,EFI/grub\n")
            f.write("use_graphics_for osx,linux,windows\n")
            f.write("showtools reboot, shutdown\n")

        # Configuramos ogLive
        if OGLIVE:
            # Cambiamos nombre de kernel e initrd para que lo detecte refind
            OGLIVEDIR = os.path.dirname(OGLIVE)
            shutil.copy(OGLIVE, f"{OGLIVE}.efi")
            shutil.copy(f"{OGLIVEDIR}/oginitrd.img", f"{OGLIVEDIR}/initrd.img")

            # Incluimos el directorio de ogLive.
            with open(CFGFILE, "a") as f:
                f.write(f"also_scan_dirs +,boot/{os.path.basename(OGLIVEDIR)}\n")
            # Fichero de configuración de refind para kernel de linux.
            LNXCFGFILE = f"{OGLIVEDIR}/refind_linux.conf"
            with open(LNXCFGFILE, "w") as f:
                f.write(f"\"OpenGnsys Live\" \"{CMDLINE}\"\n")

            # Ponemos ogLive como la entrada por defecto
            NUMENTRY = len(glob.glob(f"{EFIDIR}/Part-??-??"))
            with open(CFGFILE, "a") as f:
                f.write(f"default_selection {NUMENTRY+1}\n")
    else:
        # Renombramos la configuración por defecto
        shutil.move(CFGFILE, f"{CFGFILE}.auto")

        # Creamos nueva configuración
        with open(CFGFILE, "w") as f:
            f.write("# Configuración OpenGnsys\n")
            f.write("timeout 20\n")
            f.write("showtools reboot, shutdown\n")
            f.write("\nscanfor manual\n")
            # Configuración para sistemas restaurados con OpenGnsys
            for DIR in glob.glob("/mnt/sda1/EFI/Part-*-*"):
                DIRNAME = os.path.basename(DIR)
                f.write(f"menuentry \"{DIRNAME}\" {{\n")
                f.write(f"    loader /EFI/{DIRNAME}/Boot/ogloader.efi\n")
                if os.path.isfile(f"{DIR}/Boot/bootmgfw.efi"):
                    f.write("    icon /EFI/refind/icons/os_win8.png\n")
                if os.path.isfile(f"{DIR}/Boot/grubx64.efi"):
                    f.write("    icon /EFI/refind/icons/os_linux.png\n")
                f.write("}\n")
            # Configuración ogLive si secureboot no está activado
            if "secureboot" not in subprocess.run(["dmesg"], capture_output=True, text=True).stdout:
                if OGLIVE:
                    f.write("menuentry \"OpenGnsys Live\" {\n")
                    f.write("    volume CACHE\n")
                    f.write("    ostype Linux\n")
                    f.write(f"    loader /boot/{os.path.basename(os.path.dirname(OGLIVE))}/ogvmlinuz\n")
                    f.write(f"    initrd /boot/{os.path.basename(os.path.dirname(OGLIVE))}/oginitrd.img\n")
                    f.write(f"    options \"{CMDLINE}\"\n")
                    f.write("}\n")

                    # Ponemos ogLive como la entrada por defecto
                    subprocess.run(["sed", "-i", "1 i\default_selection \"OpenGnsys Live\"", CFGFILE], check=True)

def ogGrub4dosInstallMbr():
    # Variables locales.
    DISK = None
    PART = None
    DEVICE = None
    MOUNTDISK = None
    GRUBDISK = None
    BINBDIR = None

    # Si se solicita, mostrar ayuda.
    if len(sys.argv) > 1 and sys.argv[1] == "help":
        ogHelp(sys.argv[0], f"{sys.argv[0]} int_ndisk int_part",
                f"{sys.argv[0]} 1 1")
        return

    # Error si no se recibe 2 parámetros.
    if len(sys.argv) < 3:
        return ogRaiseError(OG_ERR_FORMAT, f"{sys.argv[0]} int_ndisk int_part")

    DISK = sys.argv[1]
    PART = sys.argv[2]

    # Controlar existencia de disco y particion
    DEVICE = ogDiskToDev(DISK)
    if not DEVICE:
        return ogRaiseError(OG_ERR_NOTFOUND)
    MOUNTDISK = ogMount(DISK, PART)
    if not MOUNTDISK:
        return ogRaiseError(OG_ERR_PARTITION)

    # Controlar acceso de escritura a la particion
    if ogIsReadonly(DISK, PART):
        return ogRaiseError(OG_ERR_NOTWRITE, f": {DISK} {PART}")

    # Controlar disco no uefi
    if ogIsEfiActive():
        return ogRaiseError(OG_ERR_NOTBIOS, ": grub4dos solo soporta PC con bios legacy")

    # Controlar particionado tipo msdos
    if not ogCheckStringInGroup(ogGetPartitionTableType(DISK), "MSDOS"):
        return ogRaiseError(OG_ERR_NOMSDOS, ": grub2dos requiere particionado tipo MSDOS")

    # Controlar la existencia del grub4dos con acceso a ntfs
    BINDIR = f"{OGLIB}/grub4dos/grub4dos-0.4.6a"
    if not os.path.isfile(f"{BINDIR}/bootlace.com"):
        return ogRaiseError(OG_ERR_NOTFOUND, f": {BINDIR}/bootlace.com")

    # instalar el bootloader de grlrd en el MBR
    subprocess.run([f"{BINDIR}/bootlace64.com", DEVICE], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

    # copiar grld a la particion
    shutil.copy(f"{BINDIR}/grldr", MOUNTDISK)

    # Instalar y configurar grub4dos
    if os.path.isfile(f"{MOUNTDISK}/boot/grub/menu.lst"):
        os.remove(f"{MOUNTDISK}/boot/grub/menu.lst")
        os.rmdir(f"{MOUNTDISK}/boot/grub")

    if not os.path.isfile(f"{MOUNTDISK}/boot/grub/menu.lst"):
        os.makedirs(f"{MOUNTDISK}/boot/grub")
        with open(f"{MOUNTDISK}/boot/grub/menu.lst", "w") as f:
            GRUBDISK = int(DISK) - 1
            f.write(f"##NO-TOCAR-ESTA-LINEA MBR\n")
            f.write(f"timeout 0\n")
            f.write(f"title  MBR\n")
            f.write(f"root (hd{GRUBDISK},0)\n")
            f.write(f"chainloader (hd{GRUBDISK},0)+1\n")
            f.write(f"boot\n")