From c113468f71e4ec0c961a19f8c589ef770efdba62 Mon Sep 17 00:00:00 2001 From: aguerrero Date: Wed, 16 Oct 2024 05:10:24 +0000 Subject: [PATCH] refs #840 Review and correction of bugs in migrated code --- client/etc/preinit/NetLib.py | 254 +++++ client/etc/preinit/__init__.py | 0 client/etc/preinit/default.py | 59 + client/etc/preinit/fileslinks.py | 74 ++ client/etc/preinit/loadenviron.py | 215 ++++ client/etc/preinit/loadmodules.py | 29 + client/etc/preinit/metadevs.py | 32 + client/etc/preinit/mountrepo.py | 62 ++ client/etc/preinit/otherservices.py | 44 + client/etc/preinit/poweroff.py | 52 + client/lib/engine/bin/CacheLib.py | 212 ++++ client/lib/engine/bin/DiskLib.py | 1368 ++++++++++++++++++++++++ client/lib/engine/bin/FileLib.py | 293 +++++ client/lib/engine/bin/FileSystemLib.py | 831 ++++++++++++++ client/lib/engine/bin/NetLib.py | 254 +++++ client/lib/engine/bin/StringLib.py | 45 + client/lib/engine/bin/SystemLib.py | 301 ++++++ client/lib/engine/bin/__init__.py | 0 18 files changed, 4125 insertions(+) create mode 100755 client/etc/preinit/NetLib.py create mode 100644 client/etc/preinit/__init__.py create mode 100644 client/etc/preinit/default.py create mode 100644 client/etc/preinit/fileslinks.py create mode 100644 client/etc/preinit/loadenviron.py create mode 100644 client/etc/preinit/loadmodules.py create mode 100644 client/etc/preinit/metadevs.py create mode 100644 client/etc/preinit/mountrepo.py create mode 100644 client/etc/preinit/otherservices.py create mode 100644 client/etc/preinit/poweroff.py create mode 100644 client/lib/engine/bin/CacheLib.py create mode 100644 client/lib/engine/bin/DiskLib.py create mode 100644 client/lib/engine/bin/FileLib.py create mode 100644 client/lib/engine/bin/FileSystemLib.py create mode 100755 client/lib/engine/bin/NetLib.py create mode 100755 client/lib/engine/bin/StringLib.py create mode 100644 client/lib/engine/bin/SystemLib.py create mode 100644 client/lib/engine/bin/__init__.py diff --git a/client/etc/preinit/NetLib.py b/client/etc/preinit/NetLib.py new file mode 100755 index 0000000..40e7fa9 --- /dev/null +++ b/client/etc/preinit/NetLib.py @@ -0,0 +1,254 @@ +import subprocess +import sys +import os + +from engine.FileLib import * +from engine.SystemLib import * + +def ogChangeRepo(): + SRCIMG = "" + NEWREPO = "" + REPO = "" + OGUNIT = "" + + if len(sys.argv) < 2: + print("Usage: ogChangeRepo IPREPO [ OgUnit ]") + print("Example: ogChangeRepo 10.1.120.3") + print("Example: ogChangeRepo 10.1.120.3 cdc") + return + + if sys.argv[1] == "help": + print("Usage: ogChangeRepo IPREPO [ OgUnit ]") + print("Example: ogChangeRepo 10.1.120.3") + print("Example: ogChangeRepo 10.1.120.3 cdc") + return + + if len(sys.argv) >= 2: + NEWREPO = sys.argv[1] + + # Opciones de montaje: lectura o escritura + subprocess.run(["mount", "|", "grep", "ogimages.*rw,"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + RW = ",rw" if subprocess.returncode == 0 else ",ro" + + # Si REPO tomamos el repositorio y la unidad organizativa actual + REPO = ogGetRepoIp() + OGUNIT = subprocess.run(["df", "|", "awk", "-F", " ", "'/ogimages/ {sub(\"//.*/ogimages\",\"\",$1); sub(\"/\",\"\",$1); print $1}'"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().strip() + + # Parametros de entrada. Si $1 = "REPO" dejo el repositorio actual + if sys.argv[1].upper() == "REPO": + NEWREPO = REPO + + # Si $1 y $2 son el repositorio y la OU actual me salgo + if NEWREPO == REPO and sys.argv[2] == OGUNIT: + return 0 + + subprocess.run(["source", "/scripts/functions"], shell=True) + subprocess.run(["source", "/scripts/ogfunctions"], shell=True) + subprocess.run(["umount", OGIMG]) + + if sys.argv[2] == "": + SRCIMG = "ogimages" + else: + SRCIMG = "ogimages/" + sys.argv[2] + + subprocess.run(["eval", "$(grep \"OPTIONS=\" /scripts/ogfunctions)"]) + + ogEcho("session", "log", MSG_HELP_ogChangeRepo + " " + NEWREPO + " " + sys.argv[2].rstrip()) + ogConnect(NEWREPO, ogprotocol, SRCIMG, OGIMG, RW) + + # Si da error volvemos a montar el inicial + if subprocess.returncode != 0: + ogConnect(REPO, ogprotocol, SRCIMG, OGIMG, RW) + ogRaiseError("session", OG_ERR_REPO, NEWREPO) + return subprocess.returncode + +def ogGetGroupDir(): + REPO = "" + DIR = "" + GROUP = "" + + if len(sys.argv) < 2: + ogHelp("ogGetGroupDir", "ogGetGroupDir str_repo", "ogGetGroupDir REPO ==> /opt/opengnsys/images/groups/Grupo1") + return + + if len(sys.argv) == 1: + REPO = "REPO" + else: + REPO = sys.argv[1] + + GROUP = ogGetGroupName() + if GROUP: + DIR = ogGetPath(REPO, "/groups/" + GROUP, stderr=subprocess.DEVNULL) + if os.path.isdir(DIR): + print(DIR) + + return 0 + +def ogGetGroupName(): + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetGroupName", "ogGetGroupName", "ogGetGroupName => Grupo1") + return + + if "group" in globals() and group: + print(group) + + return 0 + +def ogGetHostname(): + HOST = "" + + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetHostname", "ogGetHostname", "ogGetHostname => pc1") + return + + # Tomar nombre de la variable HOSTNAME + HOST = os.getenv("HOSTNAME") + + # Si no, tomar del DHCP, opción host-name + if not HOST: + with open("/var/lib/dhcp3/dhclient.leases", "r") as f: + for line in f: + if "option host-name" in line: + HOST = line.split('"')[1] + break + + # Si no, leer el parámetro del kernel hostname + if not HOST: + with open("/proc/cmdline", "r") as f: + cmdline = f.read() + HOST = re.search(r"hostname=([^ ]+)", cmdline) + if HOST: + HOST = HOST.group(1) + + if HOSTNAME != HOST: + os.environ["HOSTNAME"] = HOST + + if HOST: + print(HOST) + +def ogGetIpAddress(): + IP = "" + + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetIpAddress", "ogGetIpAddress", "ogGetIpAddress => 192.168.0.10") + return + + if "IPV4ADDR" in os.environ: + IP = os.environ["IPV4ADDR"] + else: + # Obtener direcciones IP. + if "DEVICE" in os.environ: + IP = subprocess.run(["ip", "-o", "address", "show", "up", "dev", os.environ["DEVICE"]], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().split() + else: + IP = subprocess.run(["ip", "-o", "address", "show", "up"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().split() + + IP = [addr.split("/")[0] for addr in IP if "inet" in addr] + + # Mostrar solo la primera. + if IP: + print(IP[0]) + + return 0 + +def ogGetMacAddress(): + MAC = "" + + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetMacAddress", "ogGetMacAddress", "ogGetMacAddress => 00:11:22:33:44:55") + return + + # Obtener direcciones Ethernet. + if "DEVICE" in os.environ: + MAC = subprocess.run(["ip", "-o", "link", "show", "up", "dev", os.environ["DEVICE"]], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().split() + MAC = [addr.upper() for addr in MAC if "ether" in addr] + else: + MAC = subprocess.run(["ip", "-o", "link", "show", "up"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().split() + MAC = [addr.upper() for addr in MAC if "ether" in addr and "lo" not in addr] + + # Mostrar solo la primera. + if MAC: + print(MAC[0]) + + return 0 + +def ogGetNetInterface(): + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetNetInterface", "ogGetNetInterface", "ogGetNetInterface => eth0") + return + + if "DEVICE" in os.environ: + print(os.environ["DEVICE"]) + + return 0 + +def ogGetRepoIp(): + # Variables locales. + SOURCE = "" + FSTYPE = "" + + # Mostrar ayuda. + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetRepoIp", "ogGetRepoIp", "ogGetRepoIp => 192.168.0.2") + return + + # Obtener direcciones IP, según el tipo de montaje. + output = subprocess.run(["findmnt", "-P", "-o", "SOURCE,FSTYPE", OGIMG], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().strip() + lines = output.split("\n") + for line in lines: + fields = line.split() + if len(fields) == 2: + if fields[1] == "nfs": + SOURCE = fields[0].split(":")[0] + elif fields[1] == "cifs": + SOURCE = fields[0].split("/")[2] + + if SOURCE: + print(SOURCE) + + return 0 + +def ogGetServerIp(): + # Variables locales. + SOURCE = "" + FSTYPE = "" + + # Mostrar ayuda. + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetServerIp", "ogGetServerIp", "ogGetServerIp => 192.168.0.2") + return + + # Obtener direcciones IP, según el tipo de montaje. + output = subprocess.run(["findmnt", "-P", "-o", "SOURCE,FSTYPE", OGIMG], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().strip() + lines = output.split("\n") + for line in lines: + fields = line.split() + if len(fields) == 2: + if fields[1] == "nfs": + SOURCE = fields[0].split(":")[0] + elif fields[1] == "cifs": + SOURCE = fields[0].split("/")[2] + + if SOURCE: + print(SOURCE) + + return 0 + +def ogMakeGroupDir(): + REPO = "" + DIR = "" + GROUP = "" + + if len(sys.argv) < 2: + ogHelp("ogMakeGroupDir", "ogMakeGroupDir str_repo", "ogMakeGroupDir", "ogMakeGroupDir REPO") + return + + if len(sys.argv) == 1: + REPO = "REPO" + else: + REPO = sys.argv[1] + + DIR = ogGetPath(REPO, "/groups/" + ogGetGroupName(), stderr=subprocess.DEVNULL) + if DIR: + subprocess.run(["mkdir", "-p", DIR], stderr=subprocess.DEVNULL) + + return 0 diff --git a/client/etc/preinit/__init__.py b/client/etc/preinit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/client/etc/preinit/default.py b/client/etc/preinit/default.py new file mode 100644 index 0000000..7914407 --- /dev/null +++ b/client/etc/preinit/default.py @@ -0,0 +1,59 @@ +import os +import subprocess + +print (">>>>>>>>>>>>>>>>>>>> Load ", __name__, " <<<<<<<<<<<<<<<<<<<<<<") + +print("==============================================") +print("OpenGnsys Clonning Engine Start...") +# Cargar entorno de OpenGnsys +#os.environ['OGETC'] = '/opt/opengnsys/etc' #Entorno opengnsys +os.environ['OGETC'] = 'etc' #Pruebas locales +print (f"OGETC: {os.environ['OGETC']}") + +os.environ['PYTHONUNBUFFERED'] = '1' +print (f"PYTHONUNBUFFERED: {os.environ['PYTHONUNBUFFERED']}") + +#loadenviron_path = os.path.join(os.environ['OGETC'], 'preinit', 'loadenviron.sh') +loadenviron_path = os.path.join(os.environ['OGETC'], 'preinit', 'loadenviron.py') +print (f"loadenviron_path: {loadenviron_path}") +print ("s//////////////////////////////////////////////////") +# Ejecutar el script y cargar las variables de entorno en Python +exec(open(loadenviron_path).read()) + +# Configurar las variables de entorno globales +for key, value in globals().items(): + if isinstance(value, str): + os.environ[key] = value + +print ("Variables de entorno cargadas desde loadenviron.py") + +# Ejecutar un subproceso que utilizará las nuevas variables de entorno +################################################################################subprocess.run(['bash', '-c', 'env'], shell=True) +# Scripts de inicio +print ("step 2.1 >>>>>>>>>>>>>>>>>>>>>>>>>>") +scripts = ['fileslinks', 'loadmodules', 'metadevs', 'mountrepo', 'poweroff', 'otherservices'] +for script in scripts: + script_path = os.path.join(os.environ['OGETC'], 'preinit', f'{script}.py') + print (f"<<<<<< script_path: {script_path}") + #subprocess.run(['bash', script_path]) + subprocess.run(['python3', script_path]) + +print ("step 2.2 >>>>>>>>>>>>>>>>>>>>>>>>>>") +# Check and run the appropriate init script +init_scripts = [ + os.path.join(os.environ['OGETC'], 'init', f'{os.environ.get("IPV4ADDR", "")}.sh'), + os.path.join(os.environ['OGETC'], 'init', f'{os.environ.get("OGGROUP", "")}.sh'), + os.path.join(os.environ['OGETC'], 'init', 'default.sh') +] + +print ("step 2.3 >>>>>>>>>>>>>>>>>>>>>>>>>>") +for script in init_scripts: + if os.path.isfile(script): + subprocess.run(['bash', script]) + break +else: + print("No se ha encontrado script de inicio (RUN halt)") + #subprocess.run(['halt']) + +print("OpenGnsys Clonning Engine End.") +print("==============================================") diff --git a/client/etc/preinit/fileslinks.py b/client/etc/preinit/fileslinks.py new file mode 100644 index 0000000..527dfd8 --- /dev/null +++ b/client/etc/preinit/fileslinks.py @@ -0,0 +1,74 @@ +import os +import shutil +import stat + +# Si está configurado OpenGnsys ... +if os.getenv("OPENGNSYS"): + print(os.getenv("MSG_MAKELINKS", ".")) + + # Shell BASH por defecto (para usar "runtest") + try: + os.symlink('/bin/bash', '/bin/sh') + except FileExistsError: + pass + + # Crear directorio de bloqueos + os.makedirs('/var/lock', exist_ok=True) + if not os.path.exists('/var/lock'): + os.makedirs('/run/lock', exist_ok=True) + + # Crear ficheros temporales. + oglogcommand = os.getenv("OGLOGCOMMAND") + oglogsession = os.getenv("OGLOGSESSION") + temp_files = [oglogcommand, f"{oglogcommand}.tmp", oglogsession, "/tmp/menu.tmp"] + for temp_file in temp_files: + with open(temp_file, 'a'): + os.utime(temp_file, None) + os.chmod(temp_file, 0o777) +##################################################################################### +##### Pendiente instalar Qt5 en el sistema y crear enlaces simbólicos a las librerías + # Enlaces para Qt Embeded. ###################################################### + qtdir = "/usr/local" + os.makedirs(os.path.join(qtdir, 'etc'), exist_ok=True) + os.makedirs(os.path.join(qtdir, 'lib'), exist_ok=True) + os.makedirs(os.path.join(qtdir, 'plugins'), exist_ok=True) + + oglib = os.getenv("OGLIB") + for i in os.listdir(os.path.join(oglib, 'qtlib')) + [os.path.join(oglib, 'fonts')]: + src = os.path.join(oglib, 'qtlib', i) + dst = os.path.join(qtdir, 'lib', i) + if not os.path.exists(dst): + try: + os.symlink(src, dst) + except FileExistsError: + pass + + for i in os.listdir(os.path.join(oglib, 'qtplugins')): + src = os.path.join(oglib, 'qtplugins', i) + dst = os.path.join(qtdir, 'plugins', i) + if not os.path.exists(dst): + try: + os.symlink(src, dst) + except FileExistsError: + pass + + ogetc = os.getenv("OGETC") + for i in os.listdir(ogetc): + if i.endswith('.qmap'): + src = os.path.join(ogetc, i) + dst = os.path.join(qtdir, 'etc', i) + if not os.path.exists(dst): + try: + os.symlink(src, dst) + except FileExistsError: + pass + + # Autenticación con clave pública para SSH + if os.path.isfile('/scripts/ssl/authorized_keys'): + for file in os.listdir('/scripts/ssl'): + shutil.copy(os.path.join('/scripts/ssl', file), '/root/.ssh') + +else: + # FIXME Error: entorno de OpenGnsys no configurado. + print("Error: OpenGnsys environment is not configured.") # FIXME: definir mensaje. + exit(1) \ No newline at end of file diff --git a/client/etc/preinit/loadenviron.py b/client/etc/preinit/loadenviron.py new file mode 100644 index 0000000..6ea85b9 --- /dev/null +++ b/client/etc/preinit/loadenviron.py @@ -0,0 +1,215 @@ +import os +import subprocess +import sys + +sys.path.append('/opt/opengnsys/client/lib/engine/bin') +from NetLib import * + +print(f"##################+++++++++++++++ {sys.path} ++++++++++++++################3") +print(" ") +print("=============== path =================") +print("-- step 0") +print(sys.path) +#!/usr/bin/env python3 + +# Cargar API de funciones. +def execute_lib_file(filepath): + with open(filepath) as f: + code = compile(f.read(), filepath, 'exec') + exec(code, globals()) + +print("=============== START LOAD ENVIRONMENT =================") +# Idioma por defecto. +os.environ["LANG"] = os.getenv("LANG", "es_ES") + +print("-- step 2") +os.environ["LC_ALL"] = os.getenv("LC_ALL", os.environ["LANG"]) + +print("-- step 3") +result = subprocess.run(["locale-gen", os.environ["LANG"]], capture_output=True, text=True) +if result.returncode != 0: + print(f"Error generating locale: {result.stderr}") + +print("-- step 4") +print("-- step 5") + +# Directorios del proyecto OpenGnsys. +os.environ["OPENGNSYS"] = os.getenv("OPENGNSYS", "/opt/opengnsys") + +opengnsys_path = os.environ['OPENGNSYS'] +print(f"OPENGNSYS Directory: {opengnsys_path}") + +print("-- step 6") + +print (f"OPENGNSYS: {os.environ['OPENGNSYS']}") + +if os.path.isdir(os.environ["OPENGNSYS"]): + print("OPENGNSYS directory found") + os.environ["OGBIN"] = os.path.join(os.environ["OPENGNSYS"], "bin") + os.environ["OGETC"] = os.path.join(os.environ["OPENGNSYS"], "etc") + os.environ["OGLIB"] = os.path.join(os.environ["OPENGNSYS"], "lib") + os.environ["OGAPI"] = os.path.join(os.environ["OGLIB"], "engine", "bin") + os.environ["OGSCRIPTS"] = os.path.join(os.environ["OPENGNSYS"], "scripts") + os.environ["OGIMG"] = os.path.join(os.environ["OPENGNSYS"], "images") + os.environ["OGCAC"] = os.path.join(os.environ["OPENGNSYS"], "cache") + os.environ["OGLOG"] = os.path.join(os.environ["OPENGNSYS"], "log") + + os.environ["PATH"] = os.pathsep.join([ + os.environ["PATH"], + "/sbin", + "/usr/sbin", + "/usr/local/sbin", + "/bin", + "/usr/bin", + "/usr/local/bin", + "/opt/oglive/rootfs/opt/drbl/sbin", + os.environ["OGSCRIPTS"], + os.environ["OGAPI"], + os.environ["OGBIN"] + ]) + print("-- step 7") + # Exportar parámetros del kernel. + with open("/proc/cmdline") as f: + for i in f.read().split(): + if "=" in i: + key, value = i.split("=", 1) + os.environ[key] = value + + print("-- step 8") + # Cargar fichero de idioma. + lang_file = os.path.join(os.environ["OGETC"], f"lang.{os.environ['LANG'].split('@')[0]}.conf") + if os.path.isfile(lang_file): + with open(lang_file) as f: + for line in f: + if "=" in line: + key, value = line.strip().split("=", 1) + os.environ[key] = value + print("-- step 9") + # Mensaje de carga del entorno. + print(os.getenv("MSG_LOADAPI", ".")) + + print("-- step 10") + # Cargar mapa de teclado. + subprocess.run(["loadkeys", os.environ["LANG"].split("_")[0]], stdout=subprocess.DEVNULL) + + + print("-- step 10.1") + # Imprimir todas las variables de entorno declaradas hasta el momento. + for key, value in os.environ.items(): + print(f"{key}: {value}") + + + print("-- step 11") + for lib_file in os.listdir(os.environ["OGAPI"]): + if lib_file.endswith(".lib"): + execute_lib_file(os.path.join(os.environ["OGAPI"], lib_file)) + + + +# for lib_file in os.listdir(os.environ["OGAPI"]): +# if lib_file.endswith(".lib"): +# exec(open(os.path.join(os.environ["OGAPI"], lib_file)).read()) + + print("-- step 12") + # Cargar configuración del engine. + engine_cfg = os.path.join(os.environ["OGETC"], "engine.cfg") + if os.path.isfile(engine_cfg): + exec(open(engine_cfg).read()) + os.environ["OGLOGCOMMAND"] = os.getenv("OGLOGCOMMAND", "/tmp/command.log") + os.environ["OGLOGSESSION"] = os.getenv("OGLOGSESSION", "/tmp/session.log") + + + print("-- step 13") + # Cargar las APIs según engine. + ogengine = os.getenv("ogengine") + if ogengine: + for api_file in os.listdir(os.environ["OGAPI"]): + if api_file.endswith(f".{ogengine}"): + exec(open(os.path.join(os.environ["OGAPI"], api_file)).read()) + + print("-- step 14") + # Configuración de la red (modo offline). + initrd_cfg = "/tmp/initrd.cfg" + if os.path.isfile(initrd_cfg): + with open(initrd_cfg) as f: + for line in f: + if line.startswith("DEVICECFG="): + device_cfg = line.strip().split("=", 1)[1] + os.environ["DEVICECFG"] = device_cfg + if os.path.isfile(device_cfg): + exec(open(device_cfg).read()) + + print("-- step 15") + # FIXME Pruebas para grupos de ordenadores + os.environ["OGGROUP"] = os.getenv("group", "") + + print("-- step 16") + root_repo = os.getenv("ROOTREPO", os.getenv("OGSERVERIMAGES")) + + print(f"-- step 17",ogGetIpAddress()) + # Fichero de registros. + og_log_file = os.path.join(os.environ["OGLOG"], f"{ogGetIpAddress()}.log") + os.environ["OGLOGFILE"] = og_log_file +else: + print("ERROR: OPENGNSYS directory not found") + +print("-- step 18") +# Compatibilidad para usar proxy en clientes ogLive. +if not os.getenv("http_proxy") and os.getenv("ogproxy"): + os.environ["http_proxy"] = os.getenv("ogproxy") + +print("-- step 19") +# Compatibilidad para usar servidor DNS en clientes ogLive. +if not os.path.isfile("/run/resolvconf/resolv.conf") and os.getenv("ogdns"): + os.makedirs("/run/resolvconf", exist_ok=True) + with open("/run/resolvconf/resolv.conf", "w") as f: + f.write(f"nameserver {os.getenv('ogdns')}\n") + +print("-- step 20") +# Declaración de códigos de error. +error_codes = { + "OG_ERR_FORMAT": 1, + "OG_ERR_NOTFOUND": 2, + "OG_ERR_PARTITION": 3, + "OG_ERR_LOCKED": 4, + "OG_ERR_IMAGE": 5, + "OG_ERR_NOTOS": 6, + "OG_ERR_NOTEXEC": 7, + "OG_ERR_NOTWRITE": 14, + "OG_ERR_NOTCACHE": 15, + "OG_ERR_CACHESIZE": 16, + "OG_ERR_REDUCEFS": 17, + "OG_ERR_EXTENDFS": 18, + "OG_ERR_OUTOFLIMIT": 19, + "OG_ERR_FILESYS": 20, + "OG_ERR_CACHE": 21, + "OG_ERR_NOGPT": 22, + "OG_ERR_REPO": 23, + "OG_ERR_NOMSDOS": 24, + "OG_ERR_IMGSIZEPARTITION": 30, + "OG_ERR_UPDATECACHE": 31, + "OG_ERR_DONTFORMAT": 32, + "OG_ERR_IMAGEFILE": 33, + "OG_ERR_GENERIC": 40, + "OG_ERR_UCASTSYNTAXT": 50, + "OG_ERR_UCASTSENDPARTITION": 51, + "OG_ERR_UCASTSENDFILE": 52, + "OG_ERR_UCASTRECEIVERPARTITION": 53, + "OG_ERR_UCASTRECEIVERFILE": 54, + "OG_ERR_MCASTSYNTAXT": 55, + "OG_ERR_MCASTSENDFILE": 56, + "OG_ERR_MCASTRECEIVERFILE": 57, + "OG_ERR_MCASTSENDPARTITION": 58, + "OG_ERR_MCASTRECEIVERPARTITION": 59, + "OG_ERR_PROTOCOLJOINMASTER": 60, + "OG_ERR_DONTMOUNT_IMAGE": 70, + "OG_ERR_DONTSYNC_IMAGE": 71, + "OG_ERR_DONTUNMOUNT_IMAGE": 72, + "OG_ERR_NOTDIFFERENT": 73, + "OG_ERR_SYNCHRONIZING": 74, + "OG_ERR_NOTUEFI": 80, + "OG_ERR_NOTBIOS": 81 +} +print("-- step 20") +for key, value in error_codes.items(): + os.environ[key] = str(value) diff --git a/client/etc/preinit/loadmodules.py b/client/etc/preinit/loadmodules.py new file mode 100644 index 0000000..bae5ec7 --- /dev/null +++ b/client/etc/preinit/loadmodules.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import glob + +""" +@file loadmodules.py +@brief Script de inicio para cargar módulos complementarios del kernel. +@version 1.0.5 - Cargar módulos específicos para el cliente. +""" + + +def main(): + msg_loadmodules = os.getenv('MSG_LOADMODULES', '.') + print(msg_loadmodules) + + # Módulo del ratón. + subprocess.run(['modprobe', 'psmouse'], stderr=subprocess.DEVNULL) + + # Cargar módulos específicos del kernel del cliente. + kernel_version = os.uname().release + module_path = os.path.join(os.getenv('OGLIB', ''), 'modules', kernel_version, '*.ko') + for module in glob.glob(module_path): + if os.access(module, os.R_OK): + subprocess.run(['insmod', module], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/client/etc/preinit/metadevs.py b/client/etc/preinit/metadevs.py new file mode 100644 index 0000000..3d433a6 --- /dev/null +++ b/client/etc/preinit/metadevs.py @@ -0,0 +1,32 @@ +import os +import subprocess +import sys + +#!/usr/bin/env python3 +""" +@file metadevs.py +@brief Script de inicio para detectar metadispositivos LVM y RAID. +@note Desglose del script "loadenviron.sh". +@warning License: GNU GPLv3+ +""" + +def main(): + opengnsys = os.getenv('OPENGNSYS') + print(f"____________________________________ OpenGnsys environment: {opengnsys}") + msg_detectlvmraid = os.getenv('MSG_DETECTLVMRAID', '') + + print(f"____________________________________ Message: {msg_detectlvmraid}") + + if opengnsys: + print(msg_detectlvmraid) + # Detectar metadispositivos LVM. + subprocess.run(['vgchange', '-ay'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + # Detectar metadispositivos RAID. + subprocess.run(['dmraid', '-ay'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + else: + # FIXME Error: entorno de OpenGnsys no configurado. + print("Error: OpenGnsys environment is not configured.") # FIXME: definir mensaje. + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/client/etc/preinit/mountrepo.py b/client/etc/preinit/mountrepo.py new file mode 100644 index 0000000..488c6ad --- /dev/null +++ b/client/etc/preinit/mountrepo.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +import os +import subprocess + +#/** +# @file mountrepo.py +# @brief Script para montar el repositorio de datos remoto. +#*/ + +OGIMG = os.getenv('OGIMG', '/opt/opengnsys/images') +ROOTREPO = os.getenv('ROOTREPO', os.getenv('ROOTSERVER')) +ogactiveadmin = os.getenv('ogactiveadmin') +ogprotocol = os.getenv('ogprotocol', 'smb') +ogunit = os.getenv('ogunit', '') +ogstatus = os.getenv('ogstatus') +SERVER = os.getenv('SERVER') +OGCAC = os.getenv('OGCAC') +MSG_MOUNTREPO = "Mounting repository using protocol: {} in mode: {}" + +def mount_repo(): + if ogactiveadmin == "true": + os.environ['boot'] = 'admin' # ATENCIÓN: siempre en modo "admin". + subprocess.run(['umount', OGIMG], stderr=subprocess.DEVNULL) + + protocol = ogprotocol + OGUNIT = f"/{ogunit}" if ogunit else "" + print(MSG_MOUNTREPO.format(protocol, 'admin')) + + if protocol == 'nfs': + subprocess.run(['mount.nfs', f'{ROOTREPO}:{OGIMG}{OGUNIT}', OGIMG, '-o', 'rw,nolock']) + elif protocol == 'smb': + PASS = get_password() + subprocess.run(['mount.cifs', f'//{ROOTREPO}/ogimages{OGUNIT}', OGIMG, '-o', f'rw,serverino,acl,username=opengnsys,password={PASS}']) + elif protocol == 'local': + handle_local_mount() + +def get_password(): + try: + with open('/scripts/ogfunctions') as f: + for line in f: + if 'OPTIONS=' in line: + return line.split('pass=')[1].split()[0] + except Exception: + pass + return 'og' + +def handle_local_mount(): + if ogstatus == "offline" or not SERVER: + TYPE = subprocess.getoutput("blkid | grep REPO | awk -F'TYPE=' '{print $2}' | tr -d '\"'") + if not TYPE: + if os.path.isdir(f'{OGCAC}/{OGIMG}'): + subprocess.run(['mount', '--bind', f'{OGCAC}/{OGIMG}', OGIMG]) + else: + subprocess.run(['mount', '-t', TYPE, 'LABEL=REPO', OGIMG], stderr=subprocess.DEVNULL) + else: + if subprocess.run(['smbclient', '-L', SERVER, '-N'], stderr=subprocess.DEVNULL).returncode == 0: + PASS = get_password() + subprocess.run(['mount.cifs', f'//{ROOTREPO}/ogimages', OGIMG, '-o', f'rw,serverino,acl,username=opengnsys,password={PASS}']) + +if __name__ == "__main__": + mount_repo() \ No newline at end of file diff --git a/client/etc/preinit/otherservices.py b/client/etc/preinit/otherservices.py new file mode 100644 index 0000000..30f7a6d --- /dev/null +++ b/client/etc/preinit/otherservices.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import os +import subprocess + +""" +@file otherservices.py +@brief Script de inicio para cargar otros servicios complementarios. +""" + +# Lanzar servicios complementarios del cliente. +print(os.getenv('MSG_OTHERSERVICES', '.')) + +# Iniciar rsyslog, si es necesario. +if not os.path.exists('/dev/log'): + subprocess.run(['service', 'rsyslog', 'start']) + +# Adpatar la clave de "root" para acceso SSH. +with open('/scripts/ogfunctions', 'r') as file: + for line in file: + if 'OPTIONS=' in line: + pass_option = line.split('pass=')[1].split()[0] + break + else: + pass_option = 'og' + +passwd = pass_option or 'og' +subprocess.run(['passwd', 'root'], input=f'{passwd}\n{passwd}\n', text=True) + +# Cargar el entorno OpenGnsys en conexión SSH. +subprocess.run(['cp', '-a', f'{os.getenv("OPENGNSYS")}/etc/preinit/loadenviron.py', '/etc/profile.d/']) + +# Arrancar SSH. +subprocess.run(['/etc/init.d/ssh', 'start'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + +# Desactivado apagado de monitor. +# subprocess.run(['setterm', '-blank', '0', '-powersave', 'off', '-powerdown', '0'], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + +# Activado WOL en la interfaz usada en arranque PXE. +subprocess.run(['ethtool', '-s', os.getenv('DEVICE'), 'wol', 'g'], stderr=subprocess.DEVNULL) + +# TODO Localizar correctamente el script de arranque. +if os.path.isfile('/opt/opengnsys/scripts/runhttplog.sh'): + subprocess.run(['/opt/opengnsys/scripts/runhttplog.sh'], stderr=subprocess.DEVNULL) \ No newline at end of file diff --git a/client/etc/preinit/poweroff.py b/client/etc/preinit/poweroff.py new file mode 100644 index 0000000..8b372dd --- /dev/null +++ b/client/etc/preinit/poweroff.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import sys + +""" +@file poweroff.py +@brief Script de inicio para cargar el proceso comprobación de clientes inactivos. +@note Arranca y configura el proceso "cron". +""" + +def main(): + opengnsys = os.getenv('OPENGNSYS') + if opengnsys: + msg_poweroffconf = os.getenv('MSG_POWEROFFCONF', '.') + print(msg_poweroffconf) + + ogntp = os.getenv('ogntp') + status = os.getenv('status') + + # Sincronización horaria con servidor NTP. + if ogntp and status != "offline": + subprocess.run(['ntpdate', ogntp]) + + # Crear fichero de configuración por defecto (30 min. de espera). + poweroff_conf = '/etc/poweroff.conf' + with open(poweroff_conf, 'w') as f: + f.write("POWEROFFSLEEP=30\nPOWEROFFTIME=\n") + + # Incluir zona horaria en el fichero de configuración. + with open('/proc/cmdline') as f: + cmdline = f.read() + tz = ' '.join([x for x in cmdline.split() if x.startswith('TZ=')]) + with open(poweroff_conf, 'a') as f: + f.write(tz + '\n') + + # Lanzar el proceso "cron". + subprocess.run(['cron', '-l']) + + # Definir la "crontab" lanzando el proceso de comprobación cada minuto. + ogbin = os.getenv('OGBIN') + crontab_line = f"* * * * * [ -x {ogbin}/poweroffconf ] && {ogbin}/poweroffconf\n" + subprocess.run(['crontab', '-'], input=crontab_line, text=True) + + else: + # FIXME Error: entorno de OpenGnsys no configurado. + print("Error: OpenGnsys environment is not configured.") # FIXME: definir mensaje. + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/client/lib/engine/bin/CacheLib.py b/client/lib/engine/bin/CacheLib.py new file mode 100644 index 0000000..b67d530 --- /dev/null +++ b/client/lib/engine/bin/CacheLib.py @@ -0,0 +1,212 @@ +import os +import re +import subprocess +import shutil +import sys +import platform + +from FileSystemLib import * +from SystemLib import * +from DiskLib import * + +print (">>>>>>>>>>>>>>>>>>>> Load ", __name__, " <<<<<<<<<<<<<<<<<<<<<<") + +def ogCreateCache(ndisk=1, npart=4, partsize=None): + """ + Define la caché local, por defecto en partición 4 del disco 1. + + :param ndisk: número de disco donde crear la caché, por defecto es 1. + :param npart: número de partición (opcional, 4 por defecto). + :param partsize: tamaño de la partición en KB. + :raises ValueError: Si el formato de los parámetros es incorrecto. + :raises RuntimeError: Si ocurre un error durante la creación de la caché. + """ + + if partsize is None: + raise ValueError("El tamaño de la partición debe especificarse.") + + # Verifica si las herramientas necesarias están instaladas + required_tools = ["sfdisk", "parted", "awk", "sed"] + for tool in required_tools: + if not shutil.which(tool): + raise RuntimeError(f"La herramienta {tool} no está instalada.") + + # Preparar los comandos para crear la caché + disk = f"/dev/sd{chr(96 + ndisk)}" + size_in_sectors = partsize * 2 # Asumiendo 512B por sector + + try: + # Lógica simplificada para crear la caché en la partición + subprocess.run(["parted", disk, "mkpart", "primary", str(npart), str(partsize)], check=True) + except subprocess.CalledProcessError as e: + raise RuntimeError(f"Error al crear la caché: {e}") + +def ogDeleteCache(): + """ + Borra la partición utilizada para caché. + + :raises RuntimeError: Si ocurre un error durante la eliminación de la partición de caché. + """ + cachepart = ogFindCache() + if cachepart is None: + raise RuntimeError("No se encontró la partición de caché.") + + disk = f"/dev/sd{chr(96 + int(cachepart))}" + try: + subprocess.run(["parted", disk, "rm", cachepart], check=True) + except subprocess.CalledProcessError as e: + raise RuntimeError(f"Error al borrar la partición de caché: {e}") + +def ogFindCache(): + """ + Encuentra la partición que se usa como caché. + + :return: El nombre de la partición de caché si se encuentra, de lo contrario None. + """ + try: + for disk in [f"/dev/sd{chr(97 + i)}" for i in range(26)]: # /dev/sda to /dev/sdz + result = subprocess.run(["sfdisk", "-d", disk], capture_output=True, text=True, check=True) + if "CA00" in result.stdout: + cachepart = [line.split()[0] for line in result.stdout.splitlines() if "CA00" in line][0] + return cachepart + except subprocess.CalledProcessError: + return None + + return None + +def ogFormatCache(): + """ + Formatea la partición de caché. + + :raises RuntimeError: Si ocurre un error durante el formateo de la partición. + """ + # Si se solicita, mostrar ayuda. + if len(sys.argv) > 1 and sys.argv[1] == "help": + ogHelp("ogFormatCache", "ogFormatCache") + return + + # Error si no hay definida partición de caché. + cachepart = ogFindCache() + if cachepart is None: + ogRaiseError(OG_ERR_PARTITION, MSG_NOCACHE) + return + + disk = ogDiskToDev(cachepart) + + # Formatear sistema de ficheros. + ogUnmountCache() + options = "extent,large_file" + if re.match("^5", platform.release()): + options += ",uninit_bg,^metadata_csum,^64bit" + try: + subprocess.run(["mkfs.ext4", "-q", "-F", disk, "-L", "CACHE", "-O", options], check=True) + except subprocess.CalledProcessError as e: + raise RuntimeError(f"Error al formatear la partición de caché: {e}") + + # Crear estructura básica. + mntdir = ogMountCache() + os.makedirs(os.path.join(mntdir, OGIMG), exist_ok=True) + + # Incluir kernel e Initrd del ogLive + updateBootCache() + +def ogGetCacheSize(): + """ + Obtiene el tamaño de la partición de caché. + + :return: Tamaño de la partición de caché en kilobytes. + :raises RuntimeError: Si ocurre un error al obtener el tamaño de la partición de caché. + """ + # Si se solicita, mostrar ayuda. + if len(sys.argv) > 1 and sys.argv[1] == "help": + ogHelp("ogGetCacheSize", "help", "ogGetCacheSize") + + # Error si no se encuentra partición de caché. + cachepart = ogFindCache() + if cachepart is None: + raise RuntimeError(MSG_NOCACHE) + + # Devuelve tamaño de la partición de caché. + return ogGetPartitionSize(cachepart) + +def ogGetCacheSpace(): + """ + Obtiene el espacio libre en la partición de caché en kilobytes. + + :return: Espacio libre en kilobytes. + :raises RuntimeError: Si ocurre un error al obtener el espacio libre. + """ + # Si se solicita, mostrar ayuda. + if len(sys.argv) > 1 and sys.argv[1] == "help": + ogHelp("ogGetCacheSpace", "help", "ogGetCacheSpace") + + # Error si no se encuentra partición de caché. + cachepart = ogFindCache() + if cachepart is None: + raise RuntimeError(MSG_NOCACHE) + + # Obtener el tamaño del disco y el número de sectores. + disk = ogDiskToDev(cachepart) + try: + result = subprocess.run(["sfdisk", "-g", disk], capture_output=True, text=True, check=True) + output = result.stdout + sectors_per_cylinder = int(output.splitlines()[1].split()[1]) + total_sectors = int(output.splitlines()[1].split()[0]) * sectors_per_cylinder - 1 + except subprocess.CalledProcessError as e: + raise RuntimeError(f"Error al obtener el espacio libre: {e}") + + # Obtener el último sector de la partición 3. + try: + result = subprocess.run(["sfdisk", "-uS", "-l", disk], capture_output=True, text=True, check=True) + output = result.stdout + end_sector_part3 = int([line.split()[2] for line in output.splitlines() if cachepart + "3" in line][0]) + except subprocess.CalledProcessError as e: + raise RuntimeError(f"Error al obtener el espacio libre: {e}") + + # Calcular el espacio libre en kilobytes. + if end_sector_part3 > total_sectors // 2: + free_space_kb = (total_sectors - end_sector_part3) // 2 + else: + free_space_kb = total_sectors // 4 + + return free_space_kb + +def ogMountCache(): + """ + Monta la partición de caché en el directorio /mnt/sda4. + + :raises RuntimeError: Si ocurre un error durante el montaje de la partición de caché. + """ + # Si se solicita, mostrar ayuda. + if len(sys.argv) > 1 and sys.argv[1] == "help": + ogHelp("ogMountCache", "help", "ogMountCache") + + # Montar la partición de caché en /mnt/sda4. + try: + subprocess.run(["mount", ogFindCache(), "/mnt/sda4"], check=True) + except subprocess.CalledProcessError as e: + raise RuntimeError(f"Error al montar la partición de caché: {e}") + +def ogUnmountCache(): + """ + Desmonta la partición de caché. + + :raises RuntimeError: Si ocurre un error durante el desmontaje de la partición de caché. + """ + # Si se solicita, mostrar ayuda. + if len(sys.argv) > 1 and sys.argv[1] == "help": + ogHelp("ogUnmountCache", "help", "ogUnmountCache") + + # Obtener la partición de caché. + cachepart = ogFindCache() + if cachepart is None: + raise RuntimeError("No se encontró la partición de caché.") + + # Desmontar la partición de caché. + try: + subprocess.run(["umount", cachepart], check=True) + except subprocess.CalledProcessError as e: + raise RuntimeError(f"Error al desmontar la partición de caché: {e}") + + # Eliminar el enlace simbólico de /mnt/ParticiónCache. + os.remove(f"/mnt/{cachepart}") \ No newline at end of file diff --git a/client/lib/engine/bin/DiskLib.py b/client/lib/engine/bin/DiskLib.py new file mode 100644 index 0000000..81ad6a5 --- /dev/null +++ b/client/lib/engine/bin/DiskLib.py @@ -0,0 +1,1368 @@ +import filecmp +import subprocess +import shutil +import os + +from CacheLib import * +from FileSystemLib import * +from SystemLib import * + +print (">>>>>>>>>>>>>>>>>>>> Load ", __name__, " <<<<<<<<<<<<<<<<<<<<<<") + +def parted(*args): + parted_path = shutil.which("parted") + if parted_path: + try: + result = subprocess.run( + [parted_path] + list(args), + timeout=3, + capture_output=True, + text=True + ) + return result.stdout + except subprocess.TimeoutExpired: + return "Error: Command 'parted' timed out" + else: + return "Error: 'parted' command not found" + +def ogCreatePartitions(*args): + # Variables locales + ND = DISK = PTTYPE = PART = SECTORS = START = SIZE = TYPE = CACHEPART = None + IODISCO = IOSIZE = CACHESIZE = EXTSTART = EXTSIZE = NVME_PREFIX = tmpsfdisk = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogCreatePartitions', 'ogCreatePartitions int_ndisk str_parttype:int_partsize ...', + 'ogCreatePartitions 1 NTFS:10000000 EXT3:5000000 LINUX-SWAP:1000000') + return + + # Error si no se reciben al menos 2 parámetros. + if len(args) < 2: + ogRaiseError(OG_ERR_FORMAT) + return + + # Nº total de sectores, para evitar desbordamiento (evitar redondeo). + ND = args[0] + DISK = ogDiskToDev(ND) + if DISK is None: + return + + PTTYPE = ogGetPartitionTableType(ND) or "MSDOS" # Por defecto para discos vacíos. + + if PTTYPE == "GPT": + ogCreateGptPartitions(*args) + return + elif PTTYPE != "MSDOS": + ogRaiseError(OG_ERR_PARTITION, PTTYPE) + return + + SECTORS = ogGetLastSector(ND) + + # Se recalcula el nº de sectores del disco 1, si existe partición de caché. + CACHEPART = ogFindCache() + if CACHEPART and ND == CACHEPART.split()[0]: + CACHESIZE = int(ogGetCacheSize()) * 2 + + # Sector de inicio (la partición 1 empieza en el sector 63). + IODISCO = ogDiskToDev(ND) + IOSIZE = subprocess.getoutput(f"fdisk -l {IODISCO} | awk '/I\\/O/ {{print $4}}'") + + if IOSIZE == "4096": + START = 4096 + SECTORS -= 8192 + if CACHESIZE: + SECTORS = SECTORS - CACHESIZE + 2048 - (SECTORS - CACHESIZE) % 2048 - 1 + else: + START = 63 + if CACHESIZE: + SECTORS -= CACHESIZE + + PART = 1 + + # Fichero temporal de entrada para "sfdisk" + tmpsfdisk = f"/tmp/sfdisk{os.getpid()}" + try: + with open(tmpsfdisk, 'w') as f: + f.write("unit: sectors\n\n") + + NVME_PREFIX = "p" if "nvme" in DISK else "" + + # Generar fichero de entrada para "sfdisk" con las particiones. + args = args[1:] # Shift + while args: + # Conservar los datos de la partición de caché. + if f"{ND} {PART}" == CACHEPART and CACHESIZE: + with open(tmpsfdisk, 'a') as f: + f.write(f"{DISK}{NVME_PREFIX}{PART} : start={SECTORS + 1}, size={CACHESIZE}, Id=ca\n") + PART += 1 + + # Leer formato de cada parámetro - Tipo:Tamaño + TYPE = args[0].split(":")[0] + SIZE = int(args[0].split(":")[1]) + + # Obtener identificador de tipo de partición válido. + ID = ogTypeToId(TYPE, "MSDOS") + with open(tmpsfdisk, 'a') as f: + f.write(f"{DISK}{NVME_PREFIX}{PART} : start={START}, size={SIZE}, Id={ID}\n") + START += SIZE + SECTORS -= SIZE + PART += 1 + args = args[1:] + + # Ejecutar "sfdisk" con el fichero temporal. + subprocess.run(["sfdisk", DISK], input=open(tmpsfdisk, 'r').read(), text=True) + subprocess.run(["partprobe", DISK]) + + finally: + # Eliminar fichero temporal. + if os.path.exists(tmpsfdisk): + os.remove(tmpsfdisk) + + if CACHESIZE: + ogMountCache() + return 0 + +def ogCreateGptPartitions(*args): + # Variables locales + ND = DISK = PART = SECTORS = ALIGN = START = SIZE = TYPE = CACHEPART = CACHESIZE = DELOPTIONS = OPTIONS = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogCreateGptPartitions', 'ogCreateGptPartitions int_ndisk str_parttype:int_partsize ...', + 'ogCreateGptPartitions 1 NTFS:10000000 EXT3:5000000 LINUX-SWAP:1000000') + return + + # Error si no se reciben menos de 2 parámetros. + if len(args) < 2: + ogRaiseError(OG_ERR_FORMAT) + return + + # Nº total de sectores, para evitar desbordamiento (evitar redondeo). + ND = args[0] + DISK = ogDiskToDev(ND) + if DISK is None: + return + + # Se calcula el ultimo sector del disco (total de sectores usables) + SECTORS = ogGetLastSector(ND) + + # Se recalcula el nº de sectores del disco si existe partición de caché. + CACHEPART = ogFindCache() + if ND == CACHEPART.split()[0]: + CACHESIZE = int(ogGetCacheSize()) * 2 + if CACHESIZE: + SECTORS -= CACHESIZE + + # Si el disco es GPT empieza en el sector 2048 por defecto, pero podria cambiarse + ALIGN = subprocess.getoutput(f"sgdisk -D {DISK} 2>/dev/null") + START = ALIGN + PART = 1 + + # Leer parámetros con definición de particionado. + args = args[1:] # Shift + while args: + # Si PART es la cache, nos la saltamos y seguimos con el siguiente numero para conservar los datos de la partición de caché. + if f"{ND} {PART}" == CACHEPART and CACHESIZE: + PART += 1 + + # Leer formato de cada parámetro - Tipo:Tamaño + TYPE = args[0].split(":")[0] + SIZE = int(args[0].split(":")[1]) + + # Error si la partición es extendida (no válida en discos GPT). + if TYPE == "EXTENDED": + ogRaiseError(OG_ERR_PARTITION, "EXTENDED") + return + + # Comprobar si existe la particion actual, capturamos su tamaño para ver si cambio o no + PARTSIZE = ogGetPartitionSize(ND, PART) + # En sgdisk no se pueden redimensionar las particiones, es necesario borrarlas y volver a crealas + if PARTSIZE: + DELOPTIONS += f" -d{PART}" + + # Creamos la particion + # Obtener identificador de tipo de partición válido. + ID = ogTypeToId(TYPE, "GPT") + if TYPE != "CACHE" and ID: + OPTIONS += f" -n{PART}:{START}:+{SIZE} -t{PART}:{ID} " + + START += SIZE + + # Error si se supera el nº total de sectores. + if START > SECTORS: + ogRaiseError(OG_ERR_FORMAT, f"{START//2} > {SECTORS//2}") + return + + PART += 1 + args = args[1:] + + # Desmontar los sistemas de archivos del disco antes de realizar las operaciones. + ogUnmountAll(ND) + if CACHESIZE: + ogUnmountCache() + + # Si la tabla de particiones no es valida, volver a generarla. + ogCreatePartitionTable(ND) + + # Definir particiones y notificar al kernel. + # Borramos primero las particiones y luego creamos las nuevas + subprocess.run(["sgdisk"] + DELOPTIONS.split() + OPTIONS.split() + [DISK], stderr=subprocess.DEVNULL) + subprocess.run(["partprobe", DISK], stderr=subprocess.DEVNULL) + + if CACHESIZE: + ogMountCache() + return 0 + +def ogCreatePartitionTable(*args): + # Variables locales + DISK = PTTYPE = CREATE = CREATEPTT = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogCreatePartitionTable', 'ogCreatePartitionTable int_ndisk [str_partype]', + 'ogCreatePartitionTable 1 GPT', 'ogCreatePartitionTable 1') + return + + # Error si no se reciben 1 o 2 parámetros. + if len(args) == 1: + CREATEPTT = "" + elif len(args) == 2: + CREATEPTT = args[1] + else: + ogRaiseError(OG_ERR_FORMAT) + return + + # Capturamos el tipo de tabla de particiones actual + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + PTTYPE = ogGetPartitionTableType(args[0]) or "MSDOS" # Por defecto para discos vacíos. + CREATEPTT = CREATEPTT or PTTYPE + + # Si la tabla actual y la que se indica son iguales, se comprueba si hay que regenerarla. + if CREATEPTT == PTTYPE: + if PTTYPE == "GPT": + try: + result = subprocess.run( + ["sgdisk", "-p", DISK], + stderr=subprocess.PIPE, + stdout=subprocess.DEVNULL + ) + if result.returncode != 0: + CREATE = "GPT" + except subprocess.CalledProcessError: + CREATE = "GPT" + elif PTTYPE == "MSDOS": + try: + result = subprocess.run( + ["parted", "-s", DISK, "print"], + stderr=subprocess.PIPE, + stdout=subprocess.DEVNULL + ) + if result.returncode != 0: + CREATE = "MSDOS" + except subprocess.CalledProcessError: + CREATE = "MSDOS" + else: + CREATE = CREATEPTT + + # Dependiendo del valor de CREATE, creamos la tabla de particiones en cada caso. + if CREATE == "GPT": + if PTTYPE == "MSDOS": + subprocess.run(["sgdisk", "-go", DISK]) + else: + subprocess.run(["gdisk", DISK], input="2\nw\nY\n", text=True) + subprocess.run(["partprobe", DISK], stderr=subprocess.DEVNULL) + elif CREATE == "MSDOS": + if PTTYPE == "GPT": + subprocess.run(["sgdisk", "-Z", DISK]) + subprocess.run(["fdisk", DISK], input="o\nn\np\n\n\n\nd\n\nw", text=True) + subprocess.run(["partprobe", DISK], stderr=subprocess.DEVNULL) + return + +def ogDeletePartitionTable(*args): + # Variables locales + DISK = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogDeletePartitionTable', 'ogDeletePartitionTable int_ndisk', 'ogDeletePartitionTable 1') + return + + # Error si no se reciben 1 parámetros. + if len(args) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obteniendo Identificador linux del disco. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + + # Crear una tabla de particiones vacía. + PTTYPE = ogGetPartitionTableType(args[0]) + if PTTYPE == "GPT": + subprocess.run(["sgdisk", "-o", DISK]) + elif PTTYPE == "MSDOS": + subprocess.run(["fdisk", DISK], input="o\nw", text=True) + return + +def ogDevToDisk(dev): + # Variables locales + CACHEFILE = "/var/cache/disks.cfg" + PART = None + n = 1 + + # Si se solicita, mostrar ayuda. + if dev == "help": + ogHelp("ogDevToDisk", "ogDevToDisk path_device | LABEL=str_label | UUID=str_uuid", + "ogDevToDisk /dev/sda => 1", + "ogDevToDisk /dev/sda1 => 1 1", + "ogDevToDisk LABEL=CACHE => 1 4") + return + + # Error si no se recibe 1 parámetro. + if len(dev) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener dispositivo a partir de camino, etiqueta o UUID. + DEV = dev[0] + if DEV.startswith("LABEL="): + DEV = subprocess.getoutput(f"blkid -L {DEV[6:]}") + elif DEV.startswith("PARTLABEL="): + DEV = subprocess.getoutput(f"realpath /dev/disk/by-partlabel/{DEV[11:]} 2>/dev/null") + elif DEV.startswith("PARTUUID="): + DEV = subprocess.getoutput(f"realpath /dev/disk/by-partuuid/{DEV[10:]} 2>/dev/null") + elif DEV.startswith("UUID="): + DEV = subprocess.getoutput(f"blkid -U {DEV[5:]}") + + # Error si no es fichero de bloques o directorio (para LVM). + if not os.path.exists(DEV): + ogRaiseError(OG_ERR_NOTFOUND, dev) + return + + # Buscar en fichero de caché de discos. + if os.path.exists(CACHEFILE): + with open(CACHEFILE, 'r') as f: + for line in f: + parts = line.strip().split(':') + if len(parts) == 2 and parts[1] == DEV: + PART = parts[0] + break + + if PART: + return PART + + # Si no se encuentra, procesa todos los discos para devolver su nº de orden y de partición. + disks = ogDiskToDev() + for d in disks: + NVME_PREFIX = "p" if "nvme" in d else "" + if DEV.startswith(d): + return f"{n} {DEV[len(d) + len(NVME_PREFIX):]}" + n += 1 + + ogRaiseError(OG_ERR_NOTFOUND, dev) + return OG_ERR_NOTFOUND + +def ogDiskToDev(*args): + # Variables locales + CACHEFILE = "/var/cache/disks.cfg" + ALLDISKS = [] + MPATH = VOLGROUPS = ZFSVOLS = DISK = PART = ZPOOL = None + i = 1 + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogDiskToDev', 'ogDiskToDev int_ndisk [int_npartition]', + 'ogDiskToDev => /dev/sda /dev/sdb', + 'ogDiskToDev 1 => /dev/sda', + 'ogDiskToDev 1 1 => /dev/sda1') + return + + # Borrar fichero de caché de configuración si hay cambios en las particiones. + if not filecmp.cmp('/proc/partitions', '/tmp/.partitions'): + # Guardar copia de las particiones definidas para comprobar cambios. + shutil.copy2('/proc/partitions', '/tmp/.partitions') + os.remove(CACHEFILE) + + # Si existe una correspondencia con disco/dispositivo en el caché; mostrarlo y salir. + with open(CACHEFILE, 'r') as f: + for line in f: + parts = line.strip().split(':') + if len(parts) == 2 and parts[0] == ":".join(args): + print(parts[1]) + return + + # Continuar para detectar nuevos dispositivos. + # Listar dispositivos de discos. + all_disks = subprocess.getoutput("lsblk -n -e 1,2 -x MAJ:MIN 2>/dev/null || lsblk -n -e 1,2").splitlines() + for line in all_disks: + parts = line.split() + if parts[5] == "disk": + ALLDISKS.append(f"/dev/{parts[0]}") + + # Listar volúmenes lógicos. + VOLGROUPS = subprocess.getoutput("vgs -a --noheadings 2>/dev/null").splitlines() + VOLGROUPS = [f"/dev/{line.split()[0]}" for line in VOLGROUPS] + + # Detectar caminos múltiples (ignorar mensaje si no está configurado Multipath). + try: + MPATH = subprocess.getoutput("multipath -l -v 1 2>/dev/null").splitlines() + MPATH = [f"/dev/mapper/{line.split()[0]}" for line in MPATH] + # Quitar de la lista los discos que forman parte de Multipath. + for line in subprocess.getoutput("multipath -ll").splitlines(): + if line.split()[5] == "ready": + disk = f"/dev/{line.split()[2]}" + if disk in ALLDISKS: + ALLDISKS.remove(disk) + except subprocess.CalledProcessError: + MPATH = [] + + # Detectar volúmenes ZFS. + ZFSVOLS = subprocess.getoutput("blkid").splitlines() + ZFSVOLS = [line.split(":")[0] for line in ZFSVOLS if "zfs" in line] + + # Mostrar salidas segun el número de parametros. + if len(args) == 0: + print(" ".join(ALLDISKS)) + elif len(args) == 1: + # Error si el parámetro no es un número positivo. + if not args[0].isdigit() or int(args[0]) <= 0: + ogRaiseError(OG_ERR_FORMAT, args[0]) + return + disk = ALLDISKS[int(args[0])-1] + # Error si el fichero no existe. + if not os.path.exists(disk): + ogRaiseError(OG_ERR_NOTFOUND, args[0]) + return + # Actualizar caché de configuración y mostrar dispositivo. + with open(CACHEFILE, 'a') as f: + f.write(f"{args[0]}:{disk}\n") + print(disk) + elif len(args) == 2: + # Error si los 2 parámetros no son números positivos. + if not args[0].isdigit() or int(args[0]) <= 0 or not args[1].isdigit() or int(args[1]) <= 0: + ogRaiseError(OG_ERR_FORMAT, f"{args[0]} {args[1]}") + return + disk = ALLDISKS[int(args[0])-1] + # Error si el fichero no existe. + if not os.path.exists(disk): + ogRaiseError(OG_ERR_NOTFOUND, args[0]) + return + part = f"{disk}{args[1]}" + # Comprobar si es partición. + if os.path.exists(part): + # Actualizar caché de configuración y mostrar dispositivo. + with open(CACHEFILE, 'a') as f: + f.write(f"{args[0]} {args[1]}:{part}\n") + print(part) + else: + # Comprobar si RAID o Multipath (tener en cuenta enlace simbólico). + part = f"{disk}p{args[1]}" + if os.path.exists(part) and os.stat(part, follow_symlinks=True).st_mode[0] == "b": + # Actualizar caché de configuración y mostrar dispositivo. + with open(CACHEFILE, 'a') as f: + f.write(f"{args[0]} {args[1]}:{part}\n") + print(part) + else: + part = "" + # Comprobar si volumen lógico. + if disk in VOLGROUPS: + lvscan = subprocess.getoutput("lvscan -a 2>/dev/null").splitlines() + for line in lvscan: + parts = line.split("'") + if parts[1].startswith(f"{disk}/") and i == int(args[1]): + part = parts[1] + break + i += 1 + # Comprobar si volumen ZFS que puede ser montado. + if disk in ZFSVOLS: + subprocess.run(["zpool", "import", "-f", "-R", "/mnt", "-N", "-a"], stderr=subprocess.DEVNULL) + zpool = subprocess.getoutput(f"blkid -s LABEL -o value {disk}") + zfs_list = subprocess.getoutput(f"zfs list -Hp -o name,canmount,mountpoint -r {zpool}").splitlines() + for line in zfs_list: + parts = line.split() + if parts[1] == "on" and parts[2] != "none": + if i == int(args[1]): + part = parts[0] + break + i += 1 + # Salir si no se encuentra dispositivo. + if not part: + ogRaiseError(OG_ERR_NOTFOUND, f"{args[0]} {args[1]}") + return + # Devolver camino al dispositivo. + # Actualizar caché de configuración y mostrar dispositivo. + with open(CACHEFILE, 'a') as f: + f.write(f"{args[0]} {args[1]}:{part}\n") + print(part) + else: + ogRaiseError(OG_ERR_FORMAT) + return + +def ogGetDiskSize(*args): + # Variables locales + DISK = SIZE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogGetDiskSize', 'ogGetDiskSize int_ndisk', 'ogGetDiskSize 1 => 244198584') + return + + # Error si no se recibe 1 parámetro. + if len(args) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener el tamaño del disco. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + SIZE = subprocess.getoutput(f"lsblk -n -b -o SIZE {DISK}") + + # Mostrar salida. + if SIZE: + print(SIZE) + return + +def ogGetDiskType(*args): + # Variables locales + DEV = MAJOR = TYPE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogGetDiskType', 'ogGetDiskType path_device', 'ogGetDiskType /dev/sdb => USB') + return + + # Error si no se recibe 1 parámetro. + if len(args) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener el driver del dispositivo de bloques. + DEV = args[0].split("/dev/")[1] + MAJOR = subprocess.getoutput(f"awk -v D='{DEV}' '{{if ($4==D) print $1;}}' /proc/partitions") + TYPE = subprocess.getoutput(f"awk -v D={MAJOR} '/Block/ {{bl=1}} {{if ($1==D&&bl) print toupper($2)}}' /proc/devices") + # Devolver mnemónico del driver de dispositivo. + if TYPE == "SD": + TYPE = "DISK" + if subprocess.getoutput(f"udevadm info -q property {args[0]} 2>/dev/null | grep -q '^ID_BUS=usb'"): + TYPE = "USB" + elif TYPE == "BLKEXT": + TYPE = "NVM" + elif TYPE == "SR" or TYPE.startswith("IDE"): + TYPE = "CDROM" # FIXME Comprobar discos IDE. + elif TYPE == "MD" or TYPE.startswith("CCISS"): + TYPE = "RAID" + elif TYPE == "DEVICE-MAPPER": + TYPE = "MAPPER" # FIXME Comprobar LVM y RAID. + print(TYPE) + return + +def ogGetEsp(): + + for d in subprocess.getoutput("blkid -o device").split(): + # Previene error para /dev/loop0 + PART = ogDevToDisk([d]) + # En discos NVMe blkid devuelve una salida del tipo: + # >/dev/loop0 + # >/dev/nvme0n1 + # >/dev/nvme0n1p1 + # al analizar la particion nvme0n1, PART solo tiene un argumento y hace que ogGetPartitionId lance un error + LEN = len(PART) + if LEN > 1: + if ogGetPartitionId(PART) == ogTypeToId("EFI", "GPT"): + return PART + return None + +def ogGetLastSector(*args): + # Variables locales + DISK = None + PART = None + LASTSECTOR = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp("ogGetLastSector", "ogGetLastSector int_ndisk [int_npart]", + "ogGetLastSector 1 => 488392064", + "ogGetLastSector 1 1 => 102400062") + return + + # Obtener último sector. + if len(args) == 1: # Para un disco. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + LASTSECTOR = subprocess.getoutput(f"sgdisk -p {DISK} | awk '/last usable sector/ {{print($(NF))}}'") + elif len(args) == 2: # Para una partición. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + PART = ogDiskToDev(args[0], args[1]) + if PART is None: + return + LASTSECTOR = subprocess.getoutput(f"sgdisk -p {DISK} | awk -v P='{args[1]}' '{{if ($1==P) print $3}}'") + else: # Error si se reciben más parámetros. + ogRaiseError(OG_ERR_FORMAT) + return + + print(LASTSECTOR) + return + +def ogGetPartitionActive(*args): + # Variables locales + DISK = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogGetPartitionActive', 'ogGetPartitionActive int_ndisk', 'ogGetPartitionActive 1 => 1') + return + + # Error si no se recibe 1 parámetro. + if len(args) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + # Comprobar que el disco existe y listar su partición activa. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + output = subprocess.getoutput(f"parted -sm {DISK} print 2>/dev/null | awk -F: '$7~/boot/ {{print $1}}'") + print(output) + return + +def ogGetPartitionId(*args): + # Variables locales + DISK = None + ID = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogGetPartitionId', 'ogGetPartitionId int_ndisk int_npartition', 'ogGetPartitionId 1 1 => 7') + return + + # Error si no se reciben 2 parámetros. + if len(args) != 2: + ogRaiseError(OG_ERR_FORMAT) + return + + # Detectar y mostrar el id. de tipo de partición. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + PTTYPE = ogGetPartitionTableType(args[0]) + if PTTYPE == "GPT": + ID = subprocess.getoutput(f"sgdisk -p {DISK} 2>/dev/null | awk -v p={args[1]} '{{if ($1==p) print $6;}}'") + if ID == "8300" and f"{args[0]} {args[1]}" == ogFindCache(): + ID = "CA00" + elif PTTYPE == "MSDOS": + ID = subprocess.getoutput(f"sfdisk --id {DISK} {args[1]} 2>/dev/null") + elif PTTYPE == "LVM": + ID = "10000" + elif PTTYPE == "ZPOOL": + ID = "10010" + + print(ID) + return + +def ogGetPartitionSize(*args): + # Variables locales + PART = None + SIZE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogGetPartitionSize', 'ogGetPartitionSize int_ndisk int_npartition', 'ogGetPartitionSize 1 1 => 10000000') + return + + # Error si no se reciben 2 parámetros. + if len(args) != 2: + ogRaiseError(OG_ERR_FORMAT) + return + + # Devolver tamaño de partición, del volumen lógico o del sistema de archivos (para ZFS). + PART = ogDiskToDev(args[0], args[1]) + if PART is None: + return + SIZE = subprocess.getoutput(f"partx -gbo SIZE {PART} 2>/dev/null | awk '{{print int($1/1024)}}'") + if not SIZE: + SIZE = subprocess.getoutput(f"lvs --noheadings -o lv_size --units k {PART} | awk '{{printf \"%d\",$0}}'") + if not SIZE: + SIZE = ogGetFsSize(args[0], args[1]) + print(SIZE or 0) + return + +def ogGetPartitionsNumber(*args): + # Variables locales + DISK = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogGetPartitionsNumber', 'ogGetPartitionsNumber int_ndisk', 'ogGetPartitionsNumber 1 => 3') + return + + # Error si no se recibe 1 parámetro. + if len(args) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + # Contar el número de veces que aparece el disco en su lista de particiones. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + PTTYPE = ogGetPartitionTableType(args[0]) + if PTTYPE in ["GPT", "MSDOS"]: + output = subprocess.getoutput(f"partx -gso NR {DISK} 2>/dev/null | awk -v p=0 '{{p=$1}} END {{print p}}'") + elif PTTYPE == "LVM": + output = subprocess.getoutput(f"lvs --noheadings {DISK} 2>/dev/null | wc -l") + elif PTTYPE == "ZPOOL": + subprocess.run(["zpool", "list"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # Check if zpool command exists + subprocess.run(["modprobe", "zfs"]) # Load zfs module if not already loaded + subprocess.run(["zpool", "import", "-f", "-R", "/mnt", "-N", "-a"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # Import all zpools + output = subprocess.getoutput(f"zfs list -Hp -o name,canmount,mountpoint -r $(blkid -s LABEL -o value {DISK}) | awk '$2==\"on\" && $3!=\"none\" {{c++}} END {{print c}}'") + else: + output = None + + print(output) + return + +def ogGetPartitionTableType(*args): + # Variables locales + DISK = None + TYPE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogGetPartitionTableType', 'ogGetPartitionTableType int_ndisk', 'ogGetPartitionTableType 1 => MSDOS') + return + + # Error si no se recibe 1 parámetro. + if len(args) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + # Sustituye n de disco por su dispositivo. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + + # Comprobar tabla de particiones. + if os.path.exists(DISK): + output = subprocess.getoutput(f"parted -sm {DISK} print 2>/dev/null | awk -F: -v D={DISK} '{{ if($1 == D) print toupper($6)}}'") + if not output: + output = subprocess.getoutput(f"parted -sm {DISK} print 2>/dev/null | awk -F: -v D={DISK} '{{ if($1 == D) print toupper($6)}}'") + # Comprobar si es volumen lógico. + if os.path.isdir(DISK) and subprocess.run(["vgs", DISK], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0: + output = "LVM" + # Comprobar si es pool de ZFS. + if not output or output == "UNKNOWN": + blkid_output = subprocess.getoutput(f"blkid -s TYPE {DISK} | grep zfs") + if blkid_output: + output = "ZPOOL" + + # Mostrar salida. + if output: + print(output) + return + +def ogGetPartitionType(*args): + # Variables locales + ID = None + TYPE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogGetPartitionType', 'ogGetPartitionType int_ndisk int_npartition', 'ogGetPartitionType 1 1 => NTFS') + return + + # Error si no se reciben 2 parámetros. + if len(args) != 2: + ogRaiseError(OG_ERR_FORMAT) + return + + # Detectar id. de tipo de partición y codificar al mnemónico. + ID = ogGetPartitionId(args[0], args[1]) + if ID is None: + return + TYPE = ogIdToType(ID) + print(TYPE) + return + +def ogHidePartition(*args): + # Variables locales + PART = None + TYPE = None + NEWTYPE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogHidePartition', 'ogHidePartition int_ndisk int_npartition', 'ogHidePartition 1 1') + return + + # Error si no se reciben 2 parámetros. + if len(args) != 2: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener el dispositivo de la partición. + PART = ogDiskToDev(args[0], args[1]) + if PART is None: + return + + # Obtener tipo de partición. + TYPE = ogGetPartitionType(args[0], args[1]) + if TYPE == "NTFS": + NEWTYPE = "HNTFS" + elif TYPE == "FAT32": + NEWTYPE = "HFAT32" + elif TYPE == "FAT16": + NEWTYPE = "HFAT16" + elif TYPE == "FAT12": + NEWTYPE = "HFAT12" + elif TYPE == "WINDOWS": + NEWTYPE = "WIN-RESERV" + else: + ogRaiseError(OG_ERR_PARTITION, TYPE) + return + + # Cambiar tipo de partición. + ogSetPartitionType(args[0], args[1], NEWTYPE) + return + +def ogIdToType(ID): + # Obtener valor hexadecimal de 4 caracteres rellenado con 0 por delante. + ID = ID.zfill(4) + TYPE = None + + # Asignar el tipo de partición según el ID. + if ID == "0000": + TYPE = "EMPTY" + elif ID == "0001": + TYPE = "FAT12" + elif ID in ["0005", "000f"]: + TYPE = "EXTENDED" + elif ID in ["0006", "000e"]: + TYPE = "FAT16" + elif ID == "0007": + TYPE = "NTFS" + elif ID in ["000b", "000c"]: + TYPE = "FAT32" + elif ID == "0011": + TYPE = "HFAT12" + elif ID == "0012": + TYPE = "COMPAQDIAG" + elif ID in ["0016", "001e"]: + TYPE = "HFAT16" + elif ID == "0017": + TYPE = "HNTFS" + elif ID in ["001b", "001c"]: + TYPE = "HFAT32" + elif ID == "0042": + TYPE = "WIN-DYNAMIC" + elif ID in ["0082", "8200"]: + TYPE = "LINUX-SWAP" + elif ID in ["0083", "8300"]: + TYPE = "LINUX" + elif ID in ["008e", "8E00"]: + TYPE = "LINUX-LVM" + elif ID in ["00a5", "a503"]: + TYPE = "FREEBSD" + elif ID == "00a6": + TYPE = "OPENBSD" + elif ID == "00a7": + TYPE = "CACHE" # (compatibilidad con Brutalix) + elif ID in ["00af", "af00"]: + TYPE = "HFS" + elif ID in ["00be", "be00"]: + TYPE = "SOLARIS-BOOT" + elif ID in ["00bf", "bf00145"]: + TYPE = "SOLARIS" + elif ID in ["00ca", "ca00"]: + TYPE = "CACHE" + elif ID == "00da": + TYPE = "DATA" + elif ID == "00ee": + TYPE = "GPT" + elif ID in ["00ef", "ef00"]: + TYPE = "EFI" + elif ID == "00fb": + TYPE = "VMFS" + elif ID in ["00fd", "fd00"]: + TYPE = "LINUX-RAID" + elif ID == "0700": + TYPE = "WINDOWS" + elif ID == "0c01": + TYPE = "WIN-RESERV" + elif ID == "7f00": + TYPE = "CHROMEOS-KRN" + elif ID == "7f01": + TYPE = "CHROMEOS" + elif ID == "7f02": + TYPE = "CHROMEOS-RESERV" + elif ID == "8301": + TYPE = "LINUX-RESERV" + elif ID == "a500": + TYPE = "FREEBSD-DISK" + elif ID == "a501": + TYPE = "FREEBSD-BOOT" + elif ID == "a502": + TYPE = "FREEBSD-SWAP" + elif ID == "ab00": + TYPE = "HFS-BOOT" + elif ID == "af01": + TYPE = "HFS-RAID" + elif ID == "bf02": + TYPE = "SOLARIS-SWAP" + elif ID == "bf03": + TYPE = "SOLARIS-DISK" + elif ID == "ef01": + TYPE = "MBR" + elif ID == "ef02": + TYPE = "BIOS-BOOT" + elif ID == "10000": + TYPE = "LVM-LV" + elif ID == "10010": + TYPE = "ZFS-VOL" + else: + TYPE = "UNKNOWN" + + return TYPE + +def ogIsDiskLocked(*args): + # Variables locales + DISK = None + LOCKFILE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogIsDiskLocked', 'ogIsDiskLocked int_ndisk', 'if ogIsDiskLocked(1): ...') + return + + # Falso, en caso de error. + if len(args) != 1: + return False + DISK = ogDiskToDev(args[0], 2) + if DISK is None: + return False + + # Comprobar existencia de fichero de bloqueo para el disco. + LOCKFILE = f"/var/lock/lock{DISK.replace('/', '-')}" + return os.path.isfile(LOCKFILE) + +def ogListPartitions(*args): + # Variables locales + DISK = None + PART = None + NPARTS = None + TYPE = None + SIZE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogListPartitions', 'ogListPartitions int_ndisk', 'ogListPartitions 1 => NTFS:10000000 EXT3:5000000 LINUX-SWAP:1000000') + return + + # Error si no se recibe 1 parámetro. + if len(args) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + # Procesar la salida de parted. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + NPARTS = ogGetPartitionsNumber(args[0]) + for PART in range(1, NPARTS + 1): + TYPE = ogGetPartitionType(args[0], PART) or "EMPTY" + SIZE = ogGetPartitionSize(args[0], PART) or 0 + print(f"{TYPE}:{SIZE} ", end="") + print() + return + +def ogListPrimaryPartitions(*args): + # Variables locales + PTTYPE = None + PARTS = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogListPrimaryPartitions', 'ogListPrimaryPartitions int_ndisk', 'ogListPrimaryPartitions 1 => NTFS:10000000 EXT3:5000000 EXTENDED:1000000') + return + + PTTYPE = ogGetPartitionTableType(args[0]) + if PTTYPE is None: + return + PARTS = ogListPartitions(*args) + if PARTS is None: + return + + if PTTYPE == "GPT": + print(PARTS.rstrip(" EMPTY:0")) + elif PTTYPE == "MSDOS": + print(PARTS.split(" ")[0:4]) + return + +def ogListLogicalPartitions(*args): + # Variables locales + PTTYPE = None + PARTS = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogListLogicalPartitions', 'ogListLogicalPartitions int_ndisk', 'ogListLogicalPartitions 1 => LINUX-SWAP:999998') + return + + PTTYPE = ogGetPartitionTableType(args[0]) + if PTTYPE is None: + return + PARTS = ogListPartitions(*args) + if PARTS is None: + return + + return PARTS.split(" ")[4:] + +def ogLockDisk(*args): + # Variables locales + DISK = None + LOCKFILE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogLockDisk', 'ogLockDisk int_ndisk', 'ogLockDisk 1') + return + + # Error si no se recibe 1 parámetro. + if len(args) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + + # Crear archivo de bloqueo exclusivo. + LOCKFILE = f"/var/lock/lock{DISK.replace('/', '-')}" + open(LOCKFILE, 'a').close() + +def ogSetPartitionActive(*args): + # Variables locales + DISK = None + PART = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogSetPartitionActive', 'ogSetPartitionActive int_ndisk int_npartition', 'ogSetPartitionActive 1 1') + return + + # Error si no se reciben 2 parámetros. + if len(args) != 2: + ogRaiseError(OG_ERR_FORMAT) + return + + # Comprobar que el disco existe y activar la partición indicada. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + PART = ogDiskToDev(args[0], args[1]) + if PART is None: + return + subprocess.run(["parted", "-s", DISK, "set", args[1], "boot", "on"], stderr=subprocess.DEVNULL) + return + +def ogSetPartitionId(*args): + # Variables locales + DISK = None + PART = None + PTTYPE = None + ID = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogSetPartitionId', 'ogSetPartitionId int_ndisk int_npartition hex_partid', 'ogSetPartitionId 1 1 7') + return + + # Error si no se reciben 3 parámetros. + if len(args) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Sustituye nº de disco y nº partición por su dispositivo. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + PART = ogDiskToDev(args[0], args[1]) + if PART is None: + return + # Error si el id. de partición no es hexadecimal. + ID = args[2].upper() + if not re.match("^[0-9A-F]+$", ID): + ogRaiseError(OG_ERR_OUTOFLIMIT, args[2]) + return + + # Elección del tipo de partición. + PTTYPE = ogGetPartitionTableType(args[0]) + if PTTYPE == "GPT": + subprocess.run(["sgdisk", f"-t{args[1]}:{ID}", DISK], stderr=subprocess.DEVNULL) + elif PTTYPE == "MSDOS": + subprocess.run(["sfdisk", f"--id", DISK, args[1], ID], stderr=subprocess.DEVNULL) + else: + ogRaiseError(OG_ERR_OUTOFLIMIT, f"{args[0]},{PTTYPE}") + return + + # MSDOS) Correcto si fdisk sin error o con error pero realiza Syncing + if subprocess.run(["partprobe", DISK], stderr=subprocess.DEVNULL).returncode == 0: + return + else: + ogRaiseError(OG_ERR_PARTITION, f"{args[0]},{args[1]},{args[2]}") + return + +def ogSetPartitionSize(*args): + # Variables locales + DISK = None + PART = None + SIZE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogSetPartitionSize', 'ogSetPartitionSize int_ndisk int_npartition int_size', 'ogSetPartitionSize 1 1 10000000') + return + + # Error si no se reciben 3 parámetros. + if len(args) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener el tamaño de la partición. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + PART = ogDiskToDev(args[0], args[1]) + if PART is None: + return + # Convertir tamaño en KB a sectores de 512 B. + SIZE = int(args[2]) * 2 + # Redefinir el tamaño de la partición. + subprocess.run(["sfdisk", "-f", "-uS", f"-N{args[1]}", DISK], input=f",{SIZE}", text=True, stderr=subprocess.DEVNULL) + subprocess.run(["partprobe", DISK], stderr=subprocess.DEVNULL) + return + +def ogSetPartitionType(*args): + # Variables locales + DISK = None + PART = None + PTTYPE = None + TYPE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogSetPartitionType', 'ogSetPartitionType int_ndisk int_npartition str_type', 'ogSetPartitionType 1 1 NTFS') + return + + # Error si no se reciben 3 parámetros. + if len(args) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Sustituye nº de disco por su dispositivo. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + PART = ogDiskToDev(args[0], args[1]) + if PART is None: + return + + # Elección del tipo de partición. + PTTYPE = ogGetPartitionTableType(args[0]) + if PTTYPE is None: + return + TYPE = args[2] + ID = ogTypeToId(TYPE, PTTYPE) + if ID is None: + ogRaiseError(OG_ERR_FORMAT, f"{TYPE},{PTTYPE}") + return + + ogSetPartitionId(args[0], args[1], ID) + return + +def ogTypeToId(TYPE, PTTYPE="MSDOS"): + # Asociar id. de partición para su mnemónico. + ID = "" + + # Elección del tipo de partición. + if PTTYPE == "GPT": + if TYPE == "EMPTY": + ID = "0" + elif TYPE in ["WINDOWS", "NTFS", "EXFAT", "FAT32", "FAT16", "FAT12", "HNTFS", "HFAT32", "HFAT16", "HFAT12"]: + ID = "0700" + elif TYPE == "WIN-RESERV": + ID = "0C01" + elif TYPE == "CHROMEOS-KRN": + ID = "7F00" + elif TYPE == "CHROMEOS": + ID = "7F01" + elif TYPE == "CHROMEOS-RESERV": + ID = "7F02" + elif TYPE == "LINUX-SWAP": + ID = "8200" + elif TYPE in ["LINUX", "EXT2", "EXT3", "EXT4", "REISERFS", "REISER4", "XFS", "JFS"]: + ID = "8300" + elif TYPE == "LINUX-RESERV": + ID = "8301" + elif TYPE == "LINUX-LVM": + ID = "8E00" + elif TYPE == "FREEBSD-DISK": + ID = "A500" + elif TYPE == "FREEBSD-BOOT": + ID = "A501" + elif TYPE == "FREEBSD-SWAP": + ID = "A502" + elif TYPE == "FREEBSD": + ID = "A503" + elif TYPE == "HFS-BOOT": + ID = "AB00" + elif TYPE in ["HFS", "HFS+"]: + ID = "AF00" + elif TYPE == "HFS-RAID": + ID = "AF01" + elif TYPE == "SOLARIS-BOOT": + ID = "BE00" + elif TYPE == "SOLARIS": + ID = "BF00" + elif TYPE == "SOLARIS-SWAP": + ID = "BF02" + elif TYPE == "SOLARIS-DISK": + ID = "BF03" + elif TYPE == "CACHE": + ID = "CA00" + elif TYPE == "EFI": + ID = "EF00" + elif TYPE == "LINUX-RAID": + ID = "FD00" + elif PTTYPE == "MSDOS": + if TYPE == "EMPTY": + ID = "0" + elif TYPE == "FAT12": + ID = "1" + elif TYPE == "EXTENDED": + ID = "5" + elif TYPE == "FAT16": + ID = "6" + elif TYPE in ["WINDOWS", "NTFS", "EXFAT"]: + ID = "7" + elif TYPE == "FAT32": + ID = "b" + elif TYPE == "HFAT12": + ID = "11" + elif TYPE == "HFAT16": + ID = "16" + elif TYPE == "HNTFS": + ID = "17" + elif TYPE == "HFAT32": + ID = "1b" + elif TYPE == "LINUX-SWAP": + ID = "82" + elif TYPE in ["LINUX", "EXT2", "EXT3", "EXT4", "REISERFS", "REISER4", "XFS", "JFS"]: + ID = "83" + elif TYPE == "LINUX-LVM": + ID = "8e" + elif TYPE == "FREEBSD": + ID = "a5" + elif TYPE == "OPENBSD": + ID = "a6" + elif TYPE in ["HFS", "HFS+"]: + ID = "af" + elif TYPE == "SOLARIS-BOOT": + ID = "be" + elif TYPE == "SOLARIS": + ID = "bf" + elif TYPE == "CACHE": + ID = "ca" + elif TYPE == "DATA": + ID = "da" + elif TYPE == "GPT": + ID = "ee" + elif TYPE == "EFI": + ID = "ef" + elif TYPE == "VMFS": + ID = "fb" + elif TYPE == "LINUX-RAID": + ID = "fd" + elif PTTYPE == "LVM": + if TYPE == "LVM-LV": + ID = "10000" + elif PTTYPE == "ZVOL": + if TYPE == "ZFS-VOL": + ID = "10010" + + return ID + +def ogUnhidePartition(*args): + # Variables locales + PART = None + TYPE = None + NEWTYPE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogUnhidePartition', 'ogUnhidePartition int_ndisk int_npartition', 'ogUnhidePartition 1 1') + return + + # Error si no se reciben 2 parámetros. + if len(args) != 2: + ogRaiseError(OG_ERR_FORMAT) + return + + PART = ogDiskToDev(args[0], args[1]) + if PART is None: + return + + # Obtener tipo de partición. + TYPE = ogGetPartitionType(args[0], args[1]) + if TYPE == "HNTFS": + NEWTYPE = "NTFS" + elif TYPE == "HFAT32": + NEWTYPE = "FAT32" + elif TYPE == "HFAT16": + NEWTYPE = "FAT16" + elif TYPE == "HFAT12": + NEWTYPE = "FAT12" + elif TYPE == "WIN-RESERV": + NEWTYPE = "WINDOWS" + else: + ogRaiseError(OG_ERR_PARTITION, TYPE) + return + + # Cambiar tipo de partición. + ogSetPartitionType(args[0], args[1], NEWTYPE) + return + +def ogUnlockDisk(*args): + # Variables locales + DISK = None + LOCKFILE = None + + # Si se solicita, mostrar ayuda. + if len(args) == 1 and args[0] == "help": + ogHelp('ogUnlockDisk', 'ogUnlockDisk int_ndisk', 'ogUnlockDisk 1') + return + + # Error si no se recibe 1 parámetro. + if len(args) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + DISK = ogDiskToDev(args[0]) + if DISK is None: + return + + # Borrar archivo de bloqueo exclusivo. + LOCKFILE = f"/var/lock/lock{DISK.replace('/', '-')}" + os.remove(LOCKFILE) + return + +def ogUpdatePartitionTable(): + for disk in ogDiskToDev(): + subprocess.run(["partprobe", disk]) \ No newline at end of file diff --git a/client/lib/engine/bin/FileLib.py b/client/lib/engine/bin/FileLib.py new file mode 100644 index 0000000..5ff7f36 --- /dev/null +++ b/client/lib/engine/bin/FileLib.py @@ -0,0 +1,293 @@ +import subprocess +import os +import shutil + +print (">>>>>>>>>>>>>>>>>>>> Load ", __name__, " <<<<<<<<<<<<<<<<<<<<<<") + +def ogCalculateChecksum(*args): + # Check if help is requested + if "help" in args: + print("ogCalculateChecksum [ str_repo | int_ndisk int_npartition ] path_filepath") + print("ogCalculateChecksum REPO ubuntu.img ==> ef899299caf8b517ce36f1157a93d8bf") + return + + # Get the file path + file_path = ogGetPath(*args) + if not file_path: + ogRaiseError(OG_ERR_NOTFOUND, *args) + return + + # Calculate the checksum + result = subprocess.run(["tail", "-c1M", file_path], capture_output=True) + checksum = result.stdout.decode().split()[0] + + return checksum + +def ogCompareChecksumFiles(*args): + # Variables locales. + ARGS = args + if "help" in args: + ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_filepath", "if $FUNCNAME REPO ubuntu.img CACHE ubuntu.img; then ...; fi") + return + + ARGS = args + if args[0].startswith("/"): + # Camino completo. */ (Comentario Doxygen) + SOURCE = ogGetPath(*args[:1]) + args = args[1:] + elif args[0].isdigit(): + # ndisco npartición. + SOURCE = ogGetPath(*args[:3]) + args = args[3:] + else: + # Otros: repo, cache, cdrom (no se permiten caminos relativos). + SOURCE = ogGetPath(*args[:2]) + args = args[2:] + + TARGET = ogGetPath(*args) + + try: + with open(f"{SOURCE}.sum", "r") as source_file: + source_checksum = source_file.read().strip() + with open(f"{TARGET}.sum", "r") as target_file: + target_checksum = target_file.read().strip() + + return source_checksum == target_checksum + except FileNotFoundError: + return False + +def ogCalculateFullChecksum(*args): + # Variables locales. + FILE = None + if "help" in args: + ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_filepath", "$FUNCNAME REPO ubuntu.img ==> ef899299caf8b517ce36f1157a93d8bf") + return + + # Comprobar que existe el fichero y devolver sus datos. + FILE = ogGetPath(*args) + if not FILE: + ogRaiseError(OG_ERR_NOTFOUND, *args) + return + + # Calculate the checksum + result = subprocess.run(["md5sum", FILE, "-b"], capture_output=True) + checksum = result.stdout.decode().split()[0] + + return checksum + +def ogCopyFile(*args): + # Variables locales. + ARGS = args + if "help" in args: + ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_source [ str_repo | int_ndisk int_npartition ] path_target", "$FUNCNAME REPO newfile.txt 1 2 /tmp/newfile.txt") + return + + ARGS = args + if args[0].startswith("/"): + # Camino completo. */ (Comentrio Doxygen) + SOURCE = ogGetPath(*args[:1]) + args = args[1:] + elif args[0].isdigit(): + # ndisco npartición. + SOURCE = ogGetPath(*args[:3]) + args = args[3:] + else: + # Otros: repo, cache, cdrom (no se permiten caminos relativos). + SOURCE = ogGetPath(*args[:2]) + args = args[2:] + + TARGET = ogGetPath(*args) + + # Comprobar fichero origen y directorio destino. + if not SOURCE: + ogRaiseError(OG_ERR_NOTFOUND, *args[:-1]) + return + if not TARGET: + ogRaiseError(OG_ERR_NOTFOUND, *args) + return + + # Copiar fichero (para evitar problemas de comunicaciones las copias se hacen con rsync en vez de cp). + result = subprocess.run(["rsync", "--progress", "--inplace", "-avh", SOURCE, TARGET]) + return result.returncode + +def ogDeleteFile(*args): + # Variables locales. + FILE = None + if "help" in args: + ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_file", "$FUNCNAME 1 2 /tmp/newfile.txt") + return + + # Comprobar que existe el fichero y borrarlo. + FILE = ogGetPath(*args) + if not FILE: + ogRaiseError(OG_ERR_NOTFOUND, *args) + return + try: + os.remove(FILE) + except OSError as e: + ogRaiseError(OG_ERR_NOTFOUND, *args) + return + +def ogDeleteTree(*args): + # Variables locales. + DIR = None + if "help" in args: + ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_dir", "$FUNCNAME 1 2 /tmp/newdir") + return + + # Comprobar que existe el directorio y borrarlo con su contenido. + DIR = ogGetPath(*args) + if not DIR: + ogRaiseError(OG_ERR_NOTFOUND, *args) + return + try: + shutil.rmtree(DIR) + except OSError as e: + ogRaiseError(OG_ERR_NOTFOUND, *args) + return + +def ogGetPath(*args): + # Variables locales. + MNTDIR = None + FILE = None + PREVFILE = None + FILEPATH = None + CURRENTDIR = None + + # Si se solicita, mostrar ayuda. + if "help" in args: + ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_filepath", "$FUNCNAME \"/mnt/sda1/windows/system32\" ==> /mnt/sda1/WINDOWS/System32", "$FUNCNAME REPO /etc/fstab ==> /opt/opengnsys/images/etc/fstab", "$FUNCNAME 1 1 \"/windows/system32\" ==> /mnt/sda1/WINDOWS/System32") + return + + # Procesar camino según el número de parámetros. + if len(args) == 1: + FILE = args[0] + elif len(args) == 2: + if args[0].upper() == "REPO": + FILE = os.path.join(OGIMG, args[1]) + elif args[0].upper() == "CACHE": + MNTDIR = ogMountCache() + if not MNTDIR: + return + FILE = os.path.join(MNTDIR, OGIMG, args[1]) + elif args[0].upper() == "CDROM": + MNTDIR = ogMountCdrom() + if not MNTDIR: + return + FILE = os.path.join(MNTDIR, args[1]) + else: + ogRaiseError(OG_ERR_FORMAT) + return + elif len(args) == 3: + MNTDIR = ogMount(args[0], args[1]) + if not MNTDIR: + return + FILE = os.path.join(MNTDIR, args[2]) + else: + ogRaiseError(OG_ERR_FORMAT) + return + + # Eliminar caracteres \c / duplicados y finales. + FILE = os.path.normpath(FILE) + + # Comprobar si existe el fichero para reducir tiempos. + if os.path.exists(FILE): + FILEPATH = FILE + else: + # Buscar el nombre correcto en cada subdirectorio del camino. + FILEPATH = "/" + while FILE != PREVFILE: + FILEPATH = os.path.join(FILEPATH.rstrip("/"), FILE.split("/")[0]) + PREVFILE = FILE + FILE = "/".join(FILE.split("/")[1:]) + + if FILEPATH: + return FILEPATH + else: + return None + +def ogGetParentPath(*args): + # Variables locales. + PARENT = None + if "help" in args: + ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_filepath", "$FUNCNAME \"/mnt/sda1/windows/system32\" ==> /mnt/sda1/WINDOWS", "$FUNCNAME REPO /etc/fstab ==> /opt/opengnsys/images/etc", "$FUNCNAME 1 1 \"/windows/system32\" ==> /mnt/sda1/WINDOWS") + return + + # Procesar camino según el número de parámetros. + if len(args) == 1: + PARENT = os.path.dirname(args[0]) + elif len(args) == 2: + PARENT = f"{args[0]} {os.path.dirname(f'/{args[1]}')}" + elif len(args) == 3: + PARENT = f"{args[0]} {args[1]} {os.path.dirname(f'/{args[2]}')}" + else: + ogRaiseError(OG_ERR_FORMAT) + return + + return ogGetPath(PARENT) + +def ogIsNewerFile(*args): + # Variables locales. + ARGS = args + if "help" in args: + ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_source [ str_repo | int_ndisk int_npartition ] path_target", "if $FUNCNAME REPO ubuntu.img CACHE ubuntu.img; then ... fi") + return + + ARGS = args + if args[0].startswith("/"): + # Camino completo. */ (Comentrio Doxygen) + SOURCE = ogGetPath(*args[:1]) + args = args[1:] + elif args[0].isdigit(): + # ndisco npartición. + SOURCE = ogGetPath(*args[:3]) + args = args[3:] + else: + # Otros: repo, cache, cdrom (no se permiten caminos relativos). + SOURCE = ogGetPath(*args[:2]) + args = args[2:] + + TARGET = ogGetPath(*args) + + # Comprobar que existen los ficheros origen y destino. + if not SOURCE: + ogRaiseError(OG_ERR_NOTFOUND, *args[:-1]) + return + if not TARGET: + ogRaiseError(OG_ERR_NOTFOUND, *args) + return + + # Devolver si el primer fichero se ha modificado después que el segundo. + return os.path.getmtime(SOURCE) > os.path.getmtime(TARGET) + +def ogMakeChecksumFile(*args): + # Variables locales. + FILE = None + if "help" in args: + ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_filepath", "$FUNCNAME REPO ubuntu.img") + return + + # Comprobar que existe el fichero y guardar su checksum. + FILE = ogGetPath(*args) + if not FILE: + ogRaiseError(OG_ERR_NOTFOUND, *args) + return + checksum = ogCalculateChecksum(FILE) + with open(f"{FILE}.sum", "w") as f: + f.write(checksum) + +def ogMakeDir(*args): + # Variables locales. + PARENT = None + DIR = None + if "help" in args: + ogHelp("$FUNCNAME", "$FUNCNAME [ str_repo | int_ndisk int_npartition ] path_dir", "$FUNCNAME 1 2 /tmp/newdir") + return + + PARENT = ogGetParentPath(*args) + if not PARENT: + ogRaiseError(OG_ERR_NOTFOUND, *args) + return + + DIR = os.path.basename(args[-1]) + os.makedirs(os.path.join(PARENT, DIR), exist_ok=True) \ No newline at end of file diff --git a/client/lib/engine/bin/FileSystemLib.py b/client/lib/engine/bin/FileSystemLib.py new file mode 100644 index 0000000..33d6c8c --- /dev/null +++ b/client/lib/engine/bin/FileSystemLib.py @@ -0,0 +1,831 @@ +import subprocess +import sys + +from SystemLib import * +from DiskLib import * +from CacheLib import * + +print (">>>>>>>>>>>>>>>>>>>> Load ", __name__, " <<<<<<<<<<<<<<<<<<<<<<") + +def ogCheckFs(int_ndisk, int_nfilesys): + # Si se solicita, mostrar ayuda. + if int_ndisk == "help": + ogHelp("ogCheckFs", "ogCheckFs int_ndisk int_nfilesys", "ogCheckFs 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_nfilesys) + if not PART: + return + + TYPE = ogGetFsType(int_ndisk, int_nfilesys) + if TYPE == "EXT[234]" or TYPE == "CACHE": + PROG = "e2fsck" + PARAMS = "-y" + CODES = [1, 2] + elif TYPE == "BTRFS": + PROG = "btrfsck" + CODES = [1] + elif TYPE == "REISERFS": + PROG = "fsck.reiserfs" + PARAMS = "<<<\"Yes\"" + CODES = [1, 2] + elif TYPE == "REISER4": + PROG = "fsck.reiser4" + PARAMS = "-ay" + elif TYPE == "JFS": + PROG = "fsck.jfs" + CODES = [1, 2] + elif TYPE == "XFS": + PROG = "xfs_repair" + elif TYPE == "F2FS": + PROG = "fsck.f2fs" + elif TYPE == "NTFS": + PROG = "ntfsfix" + elif TYPE == "EXFAT": + PROG = "fsck.exfat" + elif TYPE == "FAT32": + PROG = "dosfsck" + PARAMS = "-a" + CODES = [1] + elif TYPE == "FAT16": + PROG = "dosfsck" + PARAMS = "-a" + CODES = [1] + elif TYPE == "FAT12": + PROG = "dosfsck" + PARAMS = "-a" + CODES = [1] + elif TYPE == "HFS": + PROG = "fsck.hfs" + PARAMS = "-f" + elif TYPE == "HFSPLUS": + PROG = "fsck.hfs" + PARAMS = "-f" + elif TYPE == "UFS": + PROG = "fsck.ufs" + elif TYPE == "ZFS": + PROG = "fsck.zfs" + else: + ogRaiseError(OG_ERR_PARTITION, f"{int_ndisk}, {int_nfilesys}") + return + + # Error si el sistema de archivos esta montado o bloqueado. + ogUnmount(int_ndisk, int_nfilesys) + if ogIsMounted(int_ndisk, int_nfilesys): + ogRaiseError(OG_ERR_PARTITION, f"{int_ndisk} {int_nfilesys}") + return + if ogIsLocked(int_ndisk, int_nfilesys): + ogRaiseError(OG_ERR_LOCKED, f"{int_ndisk} {int_nfilesys}") + return + + # Comprobar en modo uso exclusivo. + ogLock(int_ndisk, int_nfilesys) + try: + result = subprocess.run([PROG, PARAMS, PART], capture_output=True) + ERRCODE = result.returncode + except FileNotFoundError: + ogRaiseError(OG_ERR_NOTEXEC, PROG) + ERRCODE = OG_ERR_NOTEXEC + except: + ogRaiseError(OG_ERR_PARTITION, f"{int_ndisk} {int_nfilesys}") + ERRCODE = OG_ERR_PARTITION + + ogUnlock(int_ndisk, int_nfilesys) + return ERRCODE + +def ogExtendFs(): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 2 and sys.argv[1] == "help": + ogHelp("ogExtendFs", "ogExtendFs int_ndisk int_nfilesys", "ogExtendFs 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int(sys.argv[1]), int(sys.argv[2])) + if not PART: + return + + # Redimensionar al tamaño máximo según el tipo de partición. + TYPE = ogGetFsType(int(sys.argv[1]), int(sys.argv[2])) + if TYPE == "EXT[234]": + PROG = "resize2fs" + PARAMS = "-f" + elif TYPE == "BTRFS": + PROG = "btrfs" + PARAMS = "filesystem resize max" + DOMOUNT = True # Debe estar montado. + elif TYPE == "REISERFS" or TYPE == "REISER4": + PROG = "resize_reiserfs" + PARAMS = "-f" + elif TYPE == "F2FS" or TYPE == "JFS" or TYPE == "NILFS2" or TYPE == "XFS" or TYPE == "EXFAT" or TYPE == "FAT32" or TYPE == "FAT16" or TYPE == "HFS" or TYPE == "HFSPLUS" or TYPE == "UFS": + return # No se reduce (por el momento). + elif TYPE == "NTFS": + PROG = "ntfsresize" + PARAMS = "<<<\"y\" -f" + else: + ogRaiseError(OG_ERR_PARTITION, f"{int(sys.argv[1])} {int(sys.argv[2])} {TYPE}") + return + + # Salida normal si no se va a aplicar la operación. + if not PROG: + return + + # Error si el sistema de archivos no se queda en el estado de montaje adecuado. + if DOMOUNT: + PART = ogMount(int(sys.argv[1]), int(sys.argv[2])) + if not PART: + return + else: + ogUnmount(int(sys.argv[1]), int(sys.argv[2])) + if ogIsMounted(int(sys.argv[1]), int(sys.argv[2])): + ogRaiseError(OG_ERR_PARTITION, f"{int(sys.argv[1])} {int(sys.argv[2])}") + return + + # Error si el sistema de archivos está bloqueado. + if ogIsLocked(int(sys.argv[1]), int(sys.argv[2])): + ogRaiseError(OG_ERR_LOCKED, f"{int(sys.argv[1])} {int(sys.argv[2])}") + return + + # Redimensionar en modo uso exclusivo. + ogLock(int(sys.argv[1]), int(sys.argv[2])) + try: + subprocess.run([PROG, PARAMS, PART], capture_output=True) + ERRCODE = 0 + except FileNotFoundError: + ogRaiseError(OG_ERR_NOTEXEC, PROG) + ERRCODE = OG_ERR_NOTEXEC + except: + ogRaiseError(OG_ERR_PARTITION, f"{int(sys.argv[1])} {int(sys.argv[2])}") + ERRCODE = OG_ERR_PARTITION + + ogUnlock(int(sys.argv[1]), int(sys.argv[2])) + return ERRCODE + +def ogFormat(int_ndisk, int_nfilesys): + if int_nfilesys.lower() == "cache": + ogFormatCache() + else: + ogFormatFs(int_ndisk, int_nfilesys) + +def ogFormatFs(int_ndisk, int_nfilesys, str_label=None): + # Si se solicita, mostrar ayuda. + if str_label == "help": + ogHelp("ogFormatFs", "ogFormatFs int_ndisk int_nfilesys [str_label]", "ogFormatFs 1 1", "ogFormatFs 1 1 EXT4", "ogFormatFs 1 1 \"DATA\"", "ogFormatFs 1 1 EXT4 \"DATA\"") + return + + # Error si no se reciben entre 2 y 4 parámetros. + if not (2 <= len(sys.argv) <= 4): + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener fichero de dispositivo. + PART = ogDiskToDev(int_ndisk, int_nfilesys) + if not PART: + return + + # Error si la partición está montada o bloqueada. + if ogIsMounted(int_ndisk, int_nfilesys): + ogRaiseError(OG_ERR_DONTFORMAT, f"{MSG_MOUNT}: {int_ndisk} {int_nfilesys}") + return + if ogIsLocked(int_ndisk, int_nfilesys): + ogRaiseError(OG_ERR_LOCKED, f"{int_ndisk} {int_nfilesys}") + return + + # Si no se indica el tipo de sistema de archivos, intentar obtenerlo. + TYPE = ogGetFsType(int_ndisk, int_nfilesys) + if not TYPE: + TYPE = sys.argv[3] + + # Error, si no especifica el tipo de sistema de archivos a formatear. + if not TYPE: + ogRaiseError(OG_ERR_FORMAT, f"{int_ndisk} {int_nfilesys} ...") + return + + # Elegir tipo de formato. + if TYPE == "EXT2": + PROG = "mkfs.ext2" + PARAMS = "-F" + elif TYPE == "EXT3": + PROG = "mkfs.ext3" + PARAMS = "-F" + elif TYPE == "EXT4": + PROG = "mkfs.ext4" + PARAMS = "-F" + elif TYPE == "BTRFS": + PROG = "mkfs.btrfs" + PARAMS = "-f" + elif TYPE == "REISERFS": + PROG = "mkfs.reiserfs" + PARAMS = "-f" + LABELPARAM = "-l" + elif TYPE == "REISER4": + PROG = "mkfs.reiser4" + PARAMS = "-f <<<\"y\"" + elif TYPE == "XFS": + PROG = "mkfs.xfs" + PARAMS = "-f" + elif TYPE == "JFS": + PROG = "mkfs.jfs" + PARAMS = "<<<\"y\"" + elif TYPE == "F2FS": + PROG = "mkfs.f2fs" + LABELPARAM = "-l" + elif TYPE == "NILFS2": + PROG = "mkfs.nilfs2" + PARAMS = "-f" + elif TYPE == "LINUX-SWAP": + PROG = "mkswap" + elif TYPE == "NTFS": + PROG = "mkntfs" + PARAMS = "-f" + elif TYPE == "EXFAT": + PROG = "mkfs.exfat" + LABELPARAM = "-n" + elif TYPE == "FAT32": + PROG = "mkdosfs" + PARAMS = "-F 32" + LABELPARAM = "-n" + elif TYPE == "FAT16": + PROG = "mkdosfs" + PARAMS = "-F 16" + LABELPARAM = "-n" + elif TYPE == "FAT12": + PROG = "mkdosfs" + PARAMS = "-F 12" + LABELPARAM = "-n" + elif TYPE == "HFS": + PROG = "mkfs.hfs" + elif TYPE == "HFSPLUS": + PROG = "mkfs.hfsplus" + LABELPARAM = "-v" + elif TYPE == "UFS": + PROG = "mkfs.ufs" + PARAMS = "-O 2" + else: + ogRaiseError(OG_ERR_PARTITION, f"{int_ndisk} {int_nfilesys} {TYPE}") + return + + # Etiquetas de particion. + if not str_label: + if sys.argv[4] == "CACHE": + ogRaiseError(OG_ERR_FORMAT, f"{MSG_RESERVEDVALUE}: CACHE") + return + if len(sys.argv) >= 4: + PARAMS = f"{PARAMS} {LABELPARAM or '-L'} {sys.argv[4]}" + else: + PARAMS = f"{PARAMS} {LABELPARAM or '-L'} {str_label}" + + # Formatear en modo uso exclusivo (desmontar siempre). + ogLock(int_ndisk, int_nfilesys) + try: + subprocess.run([PROG, PARAMS, PART], capture_output=True) + ERRCODE = 0 + except FileNotFoundError: + ogRaiseError(OG_ERR_NOTEXEC, PROG) + ERRCODE = OG_ERR_NOTEXEC + except: + ogRaiseError(OG_ERR_PARTITION, f"{int_ndisk} {int_nfilesys}") + ERRCODE = OG_ERR_PARTITION + + ogUnlock(int_ndisk, int_nfilesys) + return ERRCODE + +def ogGetFsSize(int_ndisk, int_npartition, str_unit=None): + # Si se solicita, mostrar ayuda. + if str_unit == "help": + ogHelp("ogGetFsSize", "ogGetFsSize int_ndisk int_npartition [str_unit]", "ogGetFsSize 1 1", "ogGetFsSize 1 1 KB") + return + + # Error si no se reciben 2 o 3 parámetros. + if not (2 <= len(sys.argv) <= 3): + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener unidad y factor de medida. + UNIT = str_unit or "KB" + FACTOR = 1 + if UNIT.upper() == "MB": + FACTOR = 1024 + elif UNIT.upper() == "GB": + FACTOR = 1024 * 1024 + elif UNIT.upper() == "TB": + FACTOR = 1024 * 1024 * 1024 + elif UNIT.upper() != "KB": + ogRaiseError(OG_ERR_FORMAT, f"{UNIT} != {{ KB, MB, GB, TB }}") + return + + # Obtener el tamaño del sistema de archivo (si no está formateado; tamaño = 0). + MNTDIR = ogMount(int_ndisk, int_npartition) + if MNTDIR: + result = subprocess.run(["df", "-BK", MNTDIR], capture_output=True, text=True) + VALUE = result.stdout.split("\n")[1].split()[1] + SIZE = float(VALUE) / FACTOR + else: + SIZE = 0 + + # Devolver el tamaño (quitar decimales si son 0). + return int(SIZE) if SIZE.is_integer() else SIZE + +def ogGetFsType(int_ndisk, int_nfilesys): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogGetFsType", "ogGetFsType int_ndisk int_nfilesys", "ogGetFsType 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_nfilesys) + if not PART: + return + + # Detectar tipo de sistema de archivo (independientemente del tipo de partición). + if PART.startswith("/"): + result = subprocess.run(["blkid", "-o", "export", PART], capture_output=True, text=True) + TYPE = "" + for line in result.stdout.split("\n"): + if line.startswith("TYPE="): + TYPE = line.split("=")[1].upper() + break + else: + subprocess.run(["zfs", "mount", PART], stderr=subprocess.DEVNULL) + result = subprocess.run(["mount"], capture_output=True, text=True) + TYPE = "" + for line in result.stdout.split("\n"): + if line.startswith(PART): + TYPE = line.split()[4].upper() + break + + # Componer valores correctos. + if TYPE == "EXT4": + if f"{int_ndisk} {int_nfilesys}" == ogFindCache(): + if ogIsFormated(int_ndisk, int_nfilesys): + TYPE = "CACHE" + elif TYPE == "VFAT": + result = subprocess.run(["blkid", "-po", "export", PART], capture_output=True, text=True) + for line in result.stdout.split("\n"): + if line.startswith("VERSION="): + TYPE = line.split("=")[1].upper() + break + elif TYPE == "SWAP": + TYPE = "LINUX-SWAP" + elif TYPE.startswith("LVM"): + TYPE = "LINUX-LVM" + elif "RAID" in TYPE: + TYPE = "LINUX-RAID" + elif TYPE == "ZFS_MEMBER": + TYPE = "ZVOL" + elif "_MEMBER" in TYPE: + TYPE = TYPE.replace("_MEMBER", "") + + if TYPE: + return TYPE + +def ogGetMountPoint(int_ndisk, int_nfilesys): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogGetMountPoint", "ogGetMountPoint int_ndisk int_nfilesys", "ogGetMountPoint 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_nfilesys) + if not PART: + return + + # Devolver punto de montaje. + result = subprocess.run(["findmnt", "-n", "-o", "TARGET", PART], capture_output=True, text=True) + return result.stdout.strip() + +def ogIsFormated(int_ndisk, int_nfilesys): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogIsFormated", "ogIsFormated int_ndisk int_nfilesys", "ogIsFormated 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_nfilesys) + if not PART: + return + + # Revisar tipo de sistema de archivos. + if PART.startswith("/"): + result = subprocess.run(["blkid", "-s", "TYPE", PART], capture_output=True, text=True) + return bool(result.stdout.strip()) + else: + result = subprocess.run(["zfs", "list", "-Hp", "-o", "canmount", PART], capture_output=True, text=True) + return result.stdout.strip() == "on" + +def ogIsLocked(int_ndisk, int_nfilesys): + return ogIsPartitionLocked(int_ndisk, int_nfilesys) + +def ogIsPartitionLocked(int_ndisk, int_npartition): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogIsPartitionLocked", "ogIsPartitionLocked int_ndisk int_npartition", "ogIsPartitionLocked 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_npartition) + if not PART: + return + + # Comprobar existencia de fichero de bloqueo de la partición o de su disco. + LOCKDISK = f"/var/lock/lock{ogDiskToDev(int_ndisk).replace('/', '-')}" + LOCKPART = f"/var/lock/lock{PART.replace('/', '-')}" + return os.path.isfile(LOCKDISK) or os.path.isfile(LOCKPART) + +def ogIsMounted(int_ndisk, int_nfilesys): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogIsMounted", "ogIsMounted int_ndisk int_nfilesys", "ogIsMounted 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener punto de montaje. + MNTDIR = ogGetMountPoint(int_ndisk, int_nfilesys) + return bool(MNTDIR) + +def ogIsReadonly(int_ndisk, int_nfilesys): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogIsReadonly", "ogIsReadonly int_ndisk int_nfilesys", "ogIsReadonly 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_nfilesys) + if not PART: + return + + # Comprobar si la partición está montada en modo de solo lectura. + result = subprocess.run(["findmnt", "-n", "-o", "OPTIONS", PART], capture_output=True, text=True) + options = result.stdout.strip().split(",") + return "ro" in options + +def ogIsWritable(int_ndisk, int_nfilesys): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogIsWritable", "ogIsWritable int_ndisk int_nfilesys", "ogIsWritable 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_nfilesys) + if not PART: + return + + # Comprobar si la partición está montada en modo de escritura. + result = subprocess.run(["findmnt", "-n", "-o", "OPTIONS", PART], capture_output=True, text=True) + options = result.stdout.strip().split(",") + return "rw" in options + +def ogLock(int_ndisk, int_nfilesys): + ogLockPartition(int_ndisk, int_nfilesys) + +def ogLockPartition(int_ndisk, int_npartition): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogLockPartition", "ogLockPartition int_ndisk int_npartition", "ogLockPartition 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_npartition) + if not PART: + return + + # Crear archivo de bloqueo exclusivo. + LOCKFILE = f"/var/lock/lock{PART.replace('/', '-')}" + open(LOCKFILE, 'a').close() + +def ogMount(): + args = sys.argv[2:] + if args == ["CACHE"] or args == ["cache"]: + ogMountCache() + elif args == ["CDROM"] or args == ["cdrom"]: + ogMountCdrom() + else: + ogMountFs(*args) + +def ogMountFirstFs(int_ndisk): + # Obtener número de particiones del disco. + NPARTS = ogGetPartitionsNumber(int_ndisk) + for PART in range(1, NPARTS + 1): + MNTDIR = ogMount(int_ndisk, PART) + if MNTDIR: + return MNTDIR + ogRaiseError(OG_ERR_NOTFOUND, int_ndisk) + return OG_ERR_NOTFOUND + +def ogMountFs(int_ndisk, int_nfilesys): + FUNCNAME = ogExecAndLog.__name__ + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp(f"{FUNCNAME}", "{FUNCNAME} int_ndisk int_nfilesys", "{FUNCNAME} 1 1 => /mnt/sda1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_nfilesys) + if not PART: + return + + # Comprobar si el sistema de archivos ya está montada. + MNTDIR = ogGetMountPoint(int_ndisk, int_nfilesys) + # Si no, montarlo en un directorio de sistema. + if not MNTDIR: + # Error si la particion esta bloqueada. + if ogIsLocked(int_ndisk, int_nfilesys): + ogRaiseError(OG_ERR_LOCKED, f"{MSG_PARTITION}, {int_ndisk} {int_nfilesys}") + return + + # El camino de un dispositivo normal comienza por el carácter "/". + if PART.startswith("/"): + # Crear punto de montaje o enlace simbólico para caché local. + MNTDIR = PART.replace("/dev", "/mnt") + DEBUG = "no" + if f"{int_ndisk} {int_nfilesys}" == ogFindCache() and OGCAC: + os.makedirs(OGCAC, exist_ok=True) + os.symlink(OGCAC, MNTDIR) + else: + os.makedirs(MNTDIR, exist_ok=True) + del DEBUG + + # Montar sistema de archivos. + try: + subprocess.run(["mount", PART, MNTDIR], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError: + try: + subprocess.run(["mount", PART, MNTDIR, "-o", "force,remove_hiberfile"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError: + ogRaiseError(OG_ERR_PARTITION, f"{int_ndisk}, {int_nfilesys}") + return + # Aviso de montaje de solo lectura. + if ogIsReadonly(int_ndisk, int_nfilesys): + ogEcho("warning", f"{FUNCNAME}: {MSG_MOUNTREADONLY}: \"{int_ndisk}, {int_nfilesys}\"") + else: + # Montar sistema de archivos ZFS (un ZPOOL no comienza por "/"). + try: + subprocess.run(["zfs", "mount", PART], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError: + pass + + return MNTDIR + +def ogMountCdrom(): + DEV = "/dev/cdrom" # Por defecto + MNTDIR = subprocess.run(["mount", "-l", "-t", "iso9660", DEV], capture_output=True, text=True) + MNTDIR = MNTDIR.stdout.strip().split(" ")[2] + if not MNTDIR: + MNTDIR = DEV.replace("/dev", "/mnt") + os.makedirs(MNTDIR, exist_ok=True) + try: + subprocess.run(["mount", "-t", "iso9660", DEV, MNTDIR], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError: + ogRaiseError(OG_ERR_PARTITION, "cdrom") + return + return MNTDIR + +def ogReduceFs(int_ndisk, int_nfilesys): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogReduceFs", "ogReduceFs int_ndisk int_nfilesys", "ogReduceFs 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_nfilesys) + if not PART: + return + + # Redimensionar según el tipo de partición. + TYPE = ogGetFsType(int_ndisk, int_nfilesys) + if TYPE == "EXT4": + ogUnmount(int_ndisk, int_nfilesys) + subprocess.run(["resize2fs", "-fpM", PART], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + elif TYPE == "BTRFS": + MNTDIR = ogMount(int_ndisk, int_nfilesys) + SIZE = subprocess.run(["btrfs", "filesystem", "show", MNTDIR], capture_output=True, text=True) + SIZE = SIZE.stdout.strip().split(" ")[6] + SIZE = int(float(SIZE) * 1.1 + 1) + subprocess.run(["btrfs", "filesystem", "resize", str(SIZE), MNTDIR], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + elif TYPE in ["REISERFS", "REISER4"]: + MNTDIR = ogMount(int_ndisk, int_nfilesys) + SIZE = int(subprocess.run(["df", "-k", MNTDIR], capture_output=True, text=True).stdout.strip().split("\n")[1].split()[2]) + SIZE = SIZE * 110 // 100 + ogUnmount(int_ndisk, int_nfilesys) + subprocess.run(["resize_reiserfs", "-s" + str(SIZE) + "K", PART], input="y\n", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + elif TYPE == "NTFS": + ogUnmount(int_ndisk, int_nfilesys) + MAXSIZE, SIZE = subprocess.run(["ntfsresize", "-fi", PART], capture_output=True, text=True) + MAXSIZE = MAXSIZE.strip().split(" ")[3] + SIZE = SIZE.strip().split(" ")[4] + SIZE = int(float(SIZE) * 1.1 / 1024 + 1) * 1024 + RETVAL = 1 + while RETVAL != 0 and SIZE + EXTRASIZE < MAXSIZE: + EXTRASIZE = subprocess.run(["ntfsresize", "-fns", str(SIZE), PART], capture_output=True, text=True) + EXTRASIZE = int(EXTRASIZE.stdout.strip()) if EXTRASIZE.stdout.strip() else 0 + RETVAL = EXTRASIZE != 0 + SIZE += EXTRASIZE + if SIZE < MAXSIZE: + subprocess.run(["ntfsresize", "-fs", str(SIZE), PART], input="y\n", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + elif TYPE in ["FAT32", "FAT16"]: + # No se reduce (por el momento). + pass + elif TYPE == "HFS" or TYPE == "HFSPLUS": + # No se reduce (por el momento). + pass + elif TYPE == "UFS": + # No se reduce (por el momento). + pass + else: + ogRaiseError(OG_ERR_PARTITION, f"{int_ndisk}, {int_nfilesys}") + + # Devuelve tamaño del sistema de ficheros. + return ogGetFsSize(int_ndisk, int_nfilesys) + +def ogUnlock(int_ndisk, int_npartition): + ogUnlockPartition(int_ndisk, int_npartition) + +def ogUnlockPartition(int_ndisk, int_npartition): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogUnlockPartition", "ogUnlockPartition int_ndisk int_npartition", "ogUnlockPartition 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición. + PART = ogDiskToDev(int_ndisk, int_npartition) + if not PART: + return + + # Borrar archivo de bloqueo exclusivo. + LOCKFILE = f"/var/lock/lock{PART.replace('/', '-')}" + os.remove(LOCKFILE) + +def ogUnmount(): + ogUnmountFs(*sys.argv[2:]) + +def ogUnmountFs(int_ndisk, int_npartition): + FUNCNAME = ogUnmountFs.__name__ + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogUnmountFs", "ogUnmountFs int_ndisk int_npartition", "ogUnmountFs 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición y punto de montaje. + PART = ogDiskToDev(int_ndisk, int_npartition) + MNTDIR = ogGetMountPoint(int_ndisk, int_npartition) + + # Si está montada, desmontarla. + if MNTDIR: + # Error si la particion está bloqueada. + if ogIsPartitionLocked(int_ndisk, int_npartition): + ogRaiseError(OG_ERR_LOCKED, f"{MSG_PARTITION}, {int_ndisk} {int_npartition}") + return + + # Desmontar y borrar punto de montaje. + try: + subprocess.run(["umount", PART], check=True, stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError: + ogEcho("warning", f"{FUNCNAME}: {MSG_DONTUNMOUNT}: \"{int_ndisk}, {int_npartition}\"") + try: + os.rmdir(MNTDIR) + except OSError: + os.remove(MNTDIR) + else: + ogEcho("warning", f"{MSG_DONTMOUNT}: \"{int_ndisk},{int_npartition}\"") + ogUnmountFs(int_ndisk, int_npartition) + +def ogUnmountAll(int_ndisk): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogUnmountAll", "ogUnmountAll int_ndisk", "ogUnmountAll 1") + return + + # Error si no se recibe 1 parámetro. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición y punto de montaje. + DISK = ogDiskToDev(int_ndisk) + for PART in range(1, ogGetPartitionsNumber(int_ndisk) + 1): + if ogGetFsType(int_ndisk, PART) != "CACHE": + ogUnmount(int_ndisk, PART) + +def ogUnsetDirtyBit(int_ndisk, int_nfilesys): + # Si se solicita, mostrar ayuda. + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogUnsetDirtyBit", "ogUnsetDirtyBit int_ndisk int_nfilesys", "ogUnsetDirtyBit 1 1") + return + + # Error si no se reciben 2 parámetros. + if len(sys.argv) != 3: + ogRaiseError(OG_ERR_FORMAT) + return + + # Obtener partición y punto de montaje. + PART = ogDiskToDev(int_ndisk, int_nfilesys) + if not PART: + return + + # Realizar acciones específicas según el tipo de sistema de archivos. + TYPE = ogGetFsType(int_ndisk, int_nfilesys) + if TYPE == "NTFS": + ogUnmount(int_ndisk, int_nfilesys) + subprocess.run(["ntfsfix", "-d", PART], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + else: + pass # Add more specific actions for other file systems if needed. + +def ogGetFreeSize(int_ndisk, int_npartition, str_SizeOutput): + if len(sys.argv) == 3 and sys.argv[2] == "help": + ogHelp("ogGetFreeSize", "ogGetFreeSize int_ndisk int_npartition str_SizeOutput", "ogGetFreeSize 1 1 GB") + return + + if len(sys.argv) < 3: + ogRaiseError(OG_ERR_FORMAT) + return + + PART = ogDiskToDev(int_ndisk, int_npartition) + if not PART: + return + + unit = str_SizeOutput + if not unit: + unit = "GB" + + factor = 1.024 / 1000000 + if unit == "kB": + factor = 1.024 + elif unit == "MB": + factor = 1.024 / 1000 + + result = subprocess.run(["df", PART], capture_output=True, text=True) + output = result.stdout.strip().split("\n")[1].split() + size = float(output[1]) * factor + used = float(output[2]) * factor + free = float(output[3]) * factor + + return free \ No newline at end of file diff --git a/client/lib/engine/bin/NetLib.py b/client/lib/engine/bin/NetLib.py new file mode 100755 index 0000000..083bb22 --- /dev/null +++ b/client/lib/engine/bin/NetLib.py @@ -0,0 +1,254 @@ +import subprocess +import sys +import os + +from FileLib import * +from SystemLib import * + +def ogChangeRepo(): + SRCIMG = "" + NEWREPO = "" + REPO = "" + OGUNIT = "" + + if len(sys.argv) < 2: + print("Usage: ogChangeRepo IPREPO [ OgUnit ]") + print("Example: ogChangeRepo 10.1.120.3") + print("Example: ogChangeRepo 10.1.120.3 cdc") + return + + if sys.argv[1] == "help": + print("Usage: ogChangeRepo IPREPO [ OgUnit ]") + print("Example: ogChangeRepo 10.1.120.3") + print("Example: ogChangeRepo 10.1.120.3 cdc") + return + + if len(sys.argv) >= 2: + NEWREPO = sys.argv[1] + + # Opciones de montaje: lectura o escritura + subprocess.run(["mount", "|", "grep", "ogimages.*rw,"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + RW = ",rw" if subprocess.returncode == 0 else ",ro" + + # Si REPO tomamos el repositorio y la unidad organizativa actual + REPO = ogGetRepoIp() + OGUNIT = subprocess.run(["df", "|", "awk", "-F", " ", "'/ogimages/ {sub(\"//.*/ogimages\",\"\",$1); sub(\"/\",\"\",$1); print $1}'"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().strip() + + # Parametros de entrada. Si $1 = "REPO" dejo el repositorio actual + if sys.argv[1].upper() == "REPO": + NEWREPO = REPO + + # Si $1 y $2 son el repositorio y la OU actual me salgo + if NEWREPO == REPO and sys.argv[2] == OGUNIT: + return 0 + + subprocess.run(["source", "/scripts/functions"], shell=True) + subprocess.run(["source", "/scripts/ogfunctions"], shell=True) + subprocess.run(["umount", OGIMG]) + + if sys.argv[2] == "": + SRCIMG = "ogimages" + else: + SRCIMG = "ogimages/" + sys.argv[2] + + subprocess.run(["eval", "$(grep \"OPTIONS=\" /scripts/ogfunctions)"]) + + ogEcho("session", "log", MSG_HELP_ogChangeRepo + " " + NEWREPO + " " + sys.argv[2].rstrip()) + ogConnect(NEWREPO, ogprotocol, SRCIMG, OGIMG, RW) + + # Si da error volvemos a montar el inicial + if subprocess.returncode != 0: + ogConnect(REPO, ogprotocol, SRCIMG, OGIMG, RW) + ogRaiseError("session", OG_ERR_REPO, NEWREPO) + return subprocess.returncode + +def ogGetGroupDir(): + REPO = "" + DIR = "" + GROUP = "" + + if len(sys.argv) < 2: + ogHelp("ogGetGroupDir", "ogGetGroupDir str_repo", "ogGetGroupDir REPO ==> /opt/opengnsys/images/groups/Grupo1") + return + + if len(sys.argv) == 1: + REPO = "REPO" + else: + REPO = sys.argv[1] + + GROUP = ogGetGroupName() + if GROUP: + DIR = ogGetPath(REPO, "/groups/" + GROUP, stderr=subprocess.DEVNULL) + if os.path.isdir(DIR): + print(DIR) + + return 0 + +def ogGetGroupName(): + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetGroupName", "ogGetGroupName", "ogGetGroupName => Grupo1") + return + + if "group" in globals() and group: + print(group) + + return 0 + +def ogGetHostname(): + HOST = "" + + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetHostname", "ogGetHostname", "ogGetHostname => pc1") + return + + # Tomar nombre de la variable HOSTNAME + HOST = os.getenv("HOSTNAME") + + # Si no, tomar del DHCP, opción host-name + if not HOST: + with open("/var/lib/dhcp3/dhclient.leases", "r") as f: + for line in f: + if "option host-name" in line: + HOST = line.split('"')[1] + break + + # Si no, leer el parámetro del kernel hostname + if not HOST: + with open("/proc/cmdline", "r") as f: + cmdline = f.read() + HOST = re.search(r"hostname=([^ ]+)", cmdline) + if HOST: + HOST = HOST.group(1) + + if HOSTNAME != HOST: + os.environ["HOSTNAME"] = HOST + + if HOST: + print(HOST) + +def ogGetIpAddress(): + IP = "" + + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetIpAddress", "ogGetIpAddress", "ogGetIpAddress => 192.168.0.10") + return + + if "IPV4ADDR" in os.environ: + IP = os.environ["IPV4ADDR"] + else: + # Obtener direcciones IP. + if "DEVICE" in os.environ: + IP = subprocess.run(["ip", "-o", "address", "show", "up", "dev", os.environ["DEVICE"]], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().split() + else: + IP = subprocess.run(["ip", "-o", "address", "show", "up"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().split() + + IP = [addr.split("/")[0] for addr in IP if "inet" in addr] + + # Mostrar solo la primera. + if IP: + print(IP[0]) + + return 0 + +def ogGetMacAddress(): + MAC = "" + + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetMacAddress", "ogGetMacAddress", "ogGetMacAddress => 00:11:22:33:44:55") + return + + # Obtener direcciones Ethernet. + if "DEVICE" in os.environ: + MAC = subprocess.run(["ip", "-o", "link", "show", "up", "dev", os.environ["DEVICE"]], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().split() + MAC = [addr.upper() for addr in MAC if "ether" in addr] + else: + MAC = subprocess.run(["ip", "-o", "link", "show", "up"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().split() + MAC = [addr.upper() for addr in MAC if "ether" in addr and "lo" not in addr] + + # Mostrar solo la primera. + if MAC: + print(MAC[0]) + + return 0 + +def ogGetNetInterface(): + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetNetInterface", "ogGetNetInterface", "ogGetNetInterface => eth0") + return + + if "DEVICE" in os.environ: + print(os.environ["DEVICE"]) + + return 0 + +def ogGetRepoIp(): + # Variables locales. + SOURCE = "" + FSTYPE = "" + + # Mostrar ayuda. + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetRepoIp", "ogGetRepoIp", "ogGetRepoIp => 192.168.0.2") + return + + # Obtener direcciones IP, según el tipo de montaje. + output = subprocess.run(["findmnt", "-P", "-o", "SOURCE,FSTYPE", OGIMG], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().strip() + lines = output.split("\n") + for line in lines: + fields = line.split() + if len(fields) == 2: + if fields[1] == "nfs": + SOURCE = fields[0].split(":")[0] + elif fields[1] == "cifs": + SOURCE = fields[0].split("/")[2] + + if SOURCE: + print(SOURCE) + + return 0 + +def ogGetServerIp(): + # Variables locales. + SOURCE = "" + FSTYPE = "" + + # Mostrar ayuda. + if len(sys.argv) >= 2 and sys.argv[1] == "help": + ogHelp("ogGetServerIp", "ogGetServerIp", "ogGetServerIp => 192.168.0.2") + return + + # Obtener direcciones IP, según el tipo de montaje. + output = subprocess.run(["findmnt", "-P", "-o", "SOURCE,FSTYPE", OGIMG], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode().strip() + lines = output.split("\n") + for line in lines: + fields = line.split() + if len(fields) == 2: + if fields[1] == "nfs": + SOURCE = fields[0].split(":")[0] + elif fields[1] == "cifs": + SOURCE = fields[0].split("/")[2] + + if SOURCE: + print(SOURCE) + + return 0 + +def ogMakeGroupDir(): + REPO = "" + DIR = "" + GROUP = "" + + if len(sys.argv) < 2: + ogHelp("ogMakeGroupDir", "ogMakeGroupDir str_repo", "ogMakeGroupDir", "ogMakeGroupDir REPO") + return + + if len(sys.argv) == 1: + REPO = "REPO" + else: + REPO = sys.argv[1] + + DIR = ogGetPath(REPO, "/groups/" + ogGetGroupName(), stderr=subprocess.DEVNULL) + if DIR: + subprocess.run(["mkdir", "-p", DIR], stderr=subprocess.DEVNULL) + + return 0 diff --git a/client/lib/engine/bin/StringLib.py b/client/lib/engine/bin/StringLib.py new file mode 100755 index 0000000..2c9e150 --- /dev/null +++ b/client/lib/engine/bin/StringLib.py @@ -0,0 +1,45 @@ +import re + +def ogCheckStringInGroup(element, group): + """ + Función para determinar si el elemento pertenece a un conjunto. + + :param element: elemento a comprobar + :param group: grupo de elementos para comprobar tipo "valor1 valor2 valor3" + :return: True si pertenece al grupo, False si NO pertenece al grupo + """ + if not isinstance(element, str) or not isinstance(group, str): + raise ValueError("Formato incorrecto, ambos parámetros deben ser cadenas.") + + return element in group.split() + +def ogCheckStringInReg(element, regex): + """ + Función para determinar si el elemento contiene una "expresión regular". + + :param element: elemento a comprobar + :param regex: expresión regular + :return: True si coincide con la expresión, False si NO coincide con la expresión + """ + if not isinstance(element, str) or not isinstance(regex, str): + raise ValueError("Formato incorrecto, ambos parámetros deben ser cadenas.") + + return re.match(regex, element) is not None + +def ogCheckIpAddress(ip): + """ + Función para determinar si una cadena es una dirección ipv4 válida. + + :param ip: string de la ip a comprobar + :return: True si es una dirección válida, False si NO es una dirección válida + """ + if not isinstance(ip, str): + raise ValueError("Formato incorrecto, el parámetro debe ser una cadena.") + + regex = r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" + if re.match(regex, ip): + parts = ip.split('.') + if all(0 <= int(part) <= 255 for part in parts): + return True + return False + diff --git a/client/lib/engine/bin/SystemLib.py b/client/lib/engine/bin/SystemLib.py new file mode 100644 index 0000000..65476ae --- /dev/null +++ b/client/lib/engine/bin/SystemLib.py @@ -0,0 +1,301 @@ +import subprocess +import datetime +import sys +import os +import shutil + +from DiskLib import * +from CacheLib import * +from StringLib import * + +print (">>>>>>>>>>>>>>>>>>>> Load ", __name__, " <<<<<<<<<<<<<<<<<<<<<<") + +#NODEBUGFUNCTIONS, OGIMG, OG_ERR_CACHESIZE, OG_ERR_NOTCACHE, OG_ERR_NOTWRITE, OG_ERR_FILESYS +#OG_ERR_REPO, OG_ERR_NOTOS, OG_ERR_NOGPT, OG_ERR_OUTOFLIMIT, OG_ERR_IMAGE, OG_ERR_CACHE +#OGLOGSESSION, OGLOGCOMMAND, OGLOGFILE, OG_ERR_LOCKED, OG_ERR_PARTITION, OG_ERR_FORMAT, OG_ERR_NOTEXEC, OG_ERR_NOTFOUND + +def ogEcho(*args): + # Variables locales + CONT = 1 + LOGS = "" + LOGLEVEL = "" + DATETIME = "" + + # Selección de ficheros de registro de incidencias. + while CONT: + arg = args.pop(0).lower() + if arg == "log": + LOGS += " " + OGLOGFILE + elif arg == "command": + LOGS += " " + OGLOGCOMMAND + elif arg == "session": + LOGS += " " + OGLOGSESSION + else: + CONT = 0 + + # Selección del nivel de registro (opcional). + arg = args.pop(0).lower() + if arg == "help": + pass + elif arg == "info" or arg == "warning" or arg == "error": + LOGLEVEL = arg + + if LOGLEVEL: + DATETIME = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + # Registrar mensajes en fichero de log si la depuración no está desactivada. + if DEBUG.lower() != "no": + LOGS += " " + OGLOGFILE + subprocess.call(f"logger -t OpenGnsys {LOGLEVEL} {DATETIME} {' '.join(args)}", shell=True) + else: + print(' '.join(args)) + +def ogExecAndLog(*args): + # Variables locales + ISCOMMAND = False + ISLOG = False + ISSESSION = False + COMMAND = "" + CONTINUE = 1 + FILES = "" + REDIREC = "" + FUNCNAME = ogExecAndLog.__name__ + + # Si se solicita, mostrar ayuda. + if len(args) > 0 and args[0] == "help": + ogHelp(f"{FUNCNAME} str_logfile ... str_command ...", + f"{FUNCNAME} COMMAND ls -al /") + return + + # Procesar parámetros. + while CONTINUE: + arg = args.pop(0).lower() + if arg == "command": + ISCOMMAND = True + continue + elif arg == "log": + ISLOG = True + continue + elif arg == "session": + ISSESSION = True + continue + else: + COMMAND = " ".join(args) + CONTINUE = 0 + + # Error si no se recibe un comando que ejecutar. + if not COMMAND: + ogRaiseError(OG_ERR_FORMAT) + return + + # Componer lista de ficheros de registro. + if ISCOMMAND: + FILES = OGLOGCOMMAND + open(FILES, "w").close() + REDIREC = "2>&1" + if ISLOG: + FILES += " " + OGLOGFILE + if ISSESSION: + FILES += " " + OGLOGSESSION + + # Ejecutar comando. + subprocess.call(f"{COMMAND} {REDIREC} | tee -a {FILES}", shell=True) + # Salida de error del comando ejecutado. + return subprocess.PIPESTATUS[0] + +def ogGetCaller(): + # Obtener el nombre del programa o del script que ha llamado al proceso actual. + output = subprocess.check_output(["ps", "hp", str(os.getppid()), "-o", "args"]).decode("utf-8") + lines = output.split("\n") + caller = "" + for line in lines: + if "bash" in line and line.split()[1] != "": + caller = line.split()[1] + else: + caller = line.split()[0].lstrip("-") + return os.path.basename(caller) + +def ogHelp(*args): + # Variables locales + FUNC = "" + MSG = "" + FUNCNAME = ogHelp.__name__ + + # Mostrar función, descripción y formato. + FUNC = args[0] if len(args) > 0 else FUNCNAME[-1] + MSG = f"MSG_HELP_{FUNC}" + ogEcho("help", f"{MSG_FUNCTION} {FUNC}: {globals()[MSG]}") + if len(args) > 1: + ogEcho("help", f" {MSG_FORMAT}: {args[1]}") + + # Mostrar ejemplos (si existen). + for example in args[2:]: + ogEcho("help", f" {MSG_EXAMPLE}: {example}") + +def ogRaiseError(*args): + # Variables locales + CONT = 1 + LOGS = "" + MSG = "" + CODE = "" + FUNCS = "" + FUNCNAME = ogRaiseError.__name__ + + # Si se solicita, mostrar ayuda. + if len(args) > 0 and args[0] == "help": + ogHelp(f"{FUNCNAME}", f"{FUNCNAME} [str_logfile ...] int_errorcode str_errormessage") + return + + # Selección de registros de incidencias. + while CONT: + arg = args.pop(0).lower() + if arg == "log" or arg == "command" or arg == "session": + LOGS += " " + arg + else: + CONT = 0 + + # Obtener código y mensaje de error. + CODE = args.pop(0) + if CODE == OG_ERR_FORMAT: + MSG = f"{MSG_ERR_FORMAT} \"{args.pop(0)}\"" + elif CODE == OG_ERR_NOTFOUND: + MSG = f"{MSG_ERR_NOTFOUND} \"{args.pop(0)}\"" + elif CODE == OG_ERR_OUTOFLIMIT: + MSG = f"{MSG_ERR_OUTOFLIMIT} \"{args.pop(0)}\"" + elif CODE == OG_ERR_PARTITION: + MSG = f"{MSG_ERR_PARTITION} \"{args.pop(0)}\"" + elif CODE == OG_ERR_LOCKED: + MSG = f"{MSG_ERR_LOCKED} \"{args.pop(0)}\"" + elif CODE == OG_ERR_CACHE: + MSG = f"{MSG_ERR_CACHE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_NOGPT: + MSG = f"{MSG_ERR_NOGPT} \"{args.pop(0)}\"" + elif CODE == OG_ERR_REPO: + MSG = f"{MSG_ERR_REPO} \"{args.pop(0)}\"" + elif CODE == OG_ERR_FILESYS: + MSG = f"{MSG_ERR_FILESYS} \"{args.pop(0)}\"" + elif CODE == OG_ERR_IMAGE: + MSG = f"{MSG_ERR_IMAGE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_NOTOS: + MSG = f"{MSG_ERR_NOTOS} \"{args.pop(0)}\"" + elif CODE == OG_ERR_NOTEXEC: + MSG = f"{MSG_ERR_NOTEXEC} \"{args.pop(0)}\"" + elif CODE == OG_ERR_NOTWRITE: + MSG = f"{MSG_ERR_NOTWRITE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_NOTCACHE: + MSG = f"{MSG_ERR_NOTCACHE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_CACHESIZE: + MSG = f"{MSG_ERR_CACHESIZE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_REDUCEFS: + MSG = f"{MSG_ERR_REDUCEFS} \"{args.pop(0)}\"" + elif CODE == OG_ERR_EXTENDFS: + MSG = f"{MSG_ERR_EXTENDFS} \"{args.pop(0)}\"" + elif CODE == OG_ERR_IMGSIZEPARTITION: + MSG = f"{MSG_ERR_IMGSIZEPARTITION} \"{args.pop(0)}\"" + elif CODE == OG_ERR_UPDATECACHE: + MSG = f"{MSG_ERR_UPDATECACHE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_DONTFORMAT: + MSG = f"{MSG_ERR_DONTFORMAT} \"{args.pop(0)}\"" + elif CODE == OG_ERR_IMAGEFILE: + MSG = f"{MSG_ERR_IMAGEFILE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_UCASTSYNTAXT: + MSG = f"{MSG_ERR_UCASTSYNTAXT} \"{args.pop(0)}\"" + elif CODE == OG_ERR_UCASTSENDPARTITION: + MSG = f"{MSG_ERR_UCASTSENDPARTITION} \"{args.pop(0)}\"" + elif CODE == OG_ERR_UCASTSENDFILE: + MSG = f"{MSG_ERR_UCASTSENDFILE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_UCASTRECEIVERPARTITION: + MSG = f"{MSG_ERR_UCASTRECEIVERPARTITION} \"{args.pop(0)}\"" + elif CODE == OG_ERR_UCASTRECEIVERFILE: + MSG = f"{MSG_ERR_UCASTRECEIVERFILE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_MCASTSYNTAXT: + MSG = f"{MSG_ERR_MCASTSYNTAXT} \"{args.pop(0)}\"" + elif CODE == OG_ERR_MCASTSENDFILE: + MSG = f"{MSG_ERR_MCASTSENDFILE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_MCASTRECEIVERFILE: + MSG = f"{MSG_ERR_MCASTRECEIVERFILE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_MCASTSENDPARTITION: + MSG = f"{MSG_ERR_MCASTSENDPARTITION} \"{args.pop(0)}\"" + elif CODE == OG_ERR_MCASTRECEIVERPARTITION: + MSG = f"{MSG_ERR_MCASTRECEIVERPARTITION} \"{args.pop(0)}\"" + elif CODE == OG_ERR_PROTOCOLJOINMASTER: + MSG = f"{MSG_ERR_PROTOCOLJOINMASTER} \"{args.pop(0)}\"" + elif CODE == OG_ERR_DONTMOUNT_IMAGE: + MSG = f"{MSG_ERR_DONTMOUNT_IMAGE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_DONTUNMOUNT_IMAGE: + MSG = f"{MSG_ERR_DONTUNMOUNT_IMAGE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_DONTSYNC_IMAGE: + MSG = f"{MSG_ERR_DONTSYNC_IMAGE} \"{args.pop(0)}\"" + elif CODE == OG_ERR_NOTDIFFERENT: + MSG = f"{MSG_ERR_NOTDIFFERENT} \"{args.pop(0)}\"" + elif CODE == OG_ERR_SYNCHRONIZING: + MSG = f"{MSG_ERR_SYNCHRONIZING} \"{args.pop(0)}\"" + elif CODE == OG_ERR_NOTUEFI: + MSG = f"{MSG_ERR_NOTUEFI} \"{args.pop(0)}\"" + elif CODE == OG_ERR_NOMSDOS: + MSG = f"{MSG_ERR_NOMSDOS} \"{args.pop(0)}\"" + elif CODE == OG_ERR_NOTBIOS: + MSG = f"{MSG_ERR_NOTBIOS} \"{args.pop(0)}\"" + else: + MSG = MSG_ERR_GENERIC + CODE = OG_ERR_GENERIC + + # Obtener lista de funciones afectadas, incluyendo el script que las llama. + FUNCS = " ".join(FUNCNAME[1:]) + FUNCS = FUNCS.replace("main", os.path.basename(sys.argv[0])) + + # Mostrar mensaje de error si es función depurable y salir con el código indicado. + if CODE == OG_ERR_FORMAT or ogCheckStringInGroup(FUNCS, NODEBUGFUNCTIONS) or not ogCheckStringInGroup(FUNCS.split()[0], NODEBUGFUNCTIONS): + ogEcho(LOGS, "error", f"{FUNCS.replace(' ', '<-')}: {MSG}", file=sys.stderr) + + return CODE + +def ogIsRepoLocked(): + # Variables locales + FILES = "" + FUNCNAME = ogIsRepoLocked.__name__ + + # Si se solicita, mostrar ayuda. + if len(sys.argv) > 1 and sys.argv[1] == "help": + ogHelp(f"{FUNCNAME}", f"{FUNCNAME}", f"if {FUNCNAME}(): ...") + + # No hacer nada, si no está definido el punto de montaje del repositorio. + if not OGIMG: + return 1 + + # Comprobar si alguno de los ficheros abiertos por los procesos activos está en el + # punto de montaje del repositorio de imágenes. + FILES = subprocess.check_output(["find", "/proc", "-maxdepth", "2", "-type", "f", "-lname", f"{OGIMG}/*"]).decode("utf-8") + return bool(FILES) + +def ogCheckProgram(*args): + FUNCNAME = ogCheckProgram.__name__ + # Si se solicita, mostrar ayuda. + if len(args) > 0 and args[0] == "help": + ogHelp(f"{FUNCNAME} \"str_program ...\"", + f"{FUNCNAME} \"partimage partclone mbuffer\"") + return + + # Error si no se recibe 1 parámetro. + if len(args) != 1: + ogRaiseError(OG_ERR_FORMAT) + return + + PERROR = 0 + PLOG = " " + for i in args[0].split(): + if not shutil.which(i): + PERROR = 1 + PLOG += f" {i}" + + if PERROR == 1: + ogRaiseError(OG_ERR_NOTEXEC, PLOG) + return + else: + return 0 + +def ogIsVirtualMachine(): + output = subprocess.check_output(["dmidecode", "-s", "system-product-name"]).decode("utf-8") + if "KVM" in output or "VirtualBox" in output: + return 1 + else: + return 0 \ No newline at end of file diff --git a/client/lib/engine/bin/__init__.py b/client/lib/engine/bin/__init__.py new file mode 100644 index 0000000..e69de29