source: client/engine/InventoryLib.py @ af9a1d9

ogClonningEnginetest-python-scriptsticket-585ticket-693ticket-700
Last change on this file since af9a1d9 was 7260bdc2, checked in by Antonio Emmanuel Guerrero Silva <aguerrero@…>, 9 months ago

refs #585 Libraries convert to Python3

  • Property mode set to 100644
File size: 21.4 KB
Line 
1import glob
2import platform
3import sys
4import os
5import subprocess
6import tempfile
7
8from engine.FileLib import *
9from engine.RegistryLib import *
10from engine.SystemLib import *
11from engine.FileSystemLib import *
12
13def ogGetArch():
14    if len(sys.argv) > 1 and sys.argv[1] == "help":
15        ogHelp(sys.argv[0], sys.argv[0], sys.argv[0] + "  =>  x86_64")
16        return
17
18    if platform.machine().endswith("64"):
19        print("x86_64")
20    else:
21        print("i386")
22
23def ogGetOsType():
24    # Si se solicita, mostrar ayuda.
25    if len(sys.argv) > 1 and sys.argv[1] == "help":
26        ogHelp(sys.argv[0], sys.argv[0] + " int_ndisk int_npartition", sys.argv[0] + " 1 2  =>  Linux")
27        return
28    ogGetOsVersion(sys.argv[1:]).split(':')[0]
29
30def ogGetOsUuid():
31    # Si se solicita, mostrar ayuda.
32    if len(sys.argv) > 1 and sys.argv[1] == "help":
33        ogHelp(sys.argv[0], sys.argv[0] + " int_ndisk int_nfilesys", sys.argv[0] + " 1 2  =>  540e47c6-8e78-4178-aa46-042e4803fb16")
34        return
35
36    # Error si no se reciben 2 parametros.
37    if len(sys.argv) != 3:
38        ogRaiseError(OG_ERR_FORMAT)
39        return
40
41    # Montar la particion, si no lo estaba previamente.
42    MNTDIR = ogMount(sys.argv[1], sys.argv[2])
43    if not MNTDIR:
44        return
45
46    # Obtener UUID según el tipo de sistema operativo.
47    os_type = ogGetOsType(sys.argv[1], sys.argv[2])
48    if os_type == "Linux":
49        # Leer el UUID del sistema de ficheros raíz o el fichero de identificador.
50        uuid = subprocess.check_output(["findmnt", "-no", "UUID", MNTDIR], stderr=subprocess.DEVNULL).decode().strip() or open(os.path.join(MNTDIR, "etc", "machine-id")).read().strip()
51        print(uuid)
52    elif os_type == "Windows":
53        # Leer identificador en clave de registro.
54        uuid = ogGetRegistryValue(MNTDIR, "SOFTWARE", "\\Microsoft\\Cryptography\\MachineGuid")
55        print(uuid)
56       
57def ogGetSerialNumber():
58    # Si se solicita, mostrar ayuda.
59    if len(sys.argv) > 1 and sys.argv[1] == "help":
60        ogHelp(sys.argv[0], sys.argv[0], sys.argv[0] + "  =>  123456")
61        return
62
63    # Obtener nº de serie (ignorar los no especificados).
64    SERIALNO = subprocess.check_output(["dmidecode", "-s", "system-serial-number"]).decode().strip()
65    SERIALNO = re.sub(r"(not specified|to be filled|invalid entry|default string)", "", SERIALNO, flags=re.IGNORECASE)
66    SERIALNO = SERIALNO.replace(" ", "")
67    SERIALNO = SERIALNO[:25] if len(SERIALNO) > 25 else SERIALNO
68    if SERIALNO:
69        print(SERIALNO)
70
71    return 0
72
73def ogIsEfiActive():
74    return os.path.isdir("/sys/firmware/efi")
75
76def ogListHardwareInfo():
77    # Si se solicita, mostrar ayuda.
78    if len(sys.argv) > 1 and sys.argv[1] == "help":
79        ogHelp(sys.argv[0], sys.argv[0])
80        return
81
82    # Recopilación de dispositivos procesando la salida de \c lshw
83    ogEcho("info", MSG_HARDWAREINVENTORY)
84    output = subprocess.check_output('echo "cha=$(dmidecode -s chassis-type)" | grep -v "Other"', shell=True).decode().strip()
85    print(output)
86    if os.path.isdir("/sys/firmware/efi"):
87        print("boo=UEFI")
88    else:
89        print("boo=BIOS")
90    subprocess.call(["lshw", "|", "awk", "'BEGIN {type=\"mod\";}",
91                        "/product:/ {sub(/ *product: */,\"\", prod=$0);}",
92                        "/vendor:/  {sub(/ *vendor: */,\"\", vend=$0);}",
93                        "/version:/ {sub(/ *version: */,\"v.\", vers=$0);}",
94                        "/size:/    {size=$2;}",
95                        "/clock:/   {clock=$2;}",
96                        "/slot:/    {sub(/ *slot: */,\"\", slot=$0);}",
97                        "/\\*-/      {if (type==\"mem\"){",
98                        "if (size!=\"\"){",
99                        "numbank++;",
100                        "print type\"=\"vend,prod,size,clock\" (\"slot\")\";}",
101                        "}else{",
102                        "if (type==\"totalmem\"){",
103                        "if (size!=\"\"){",
104                        "totalmemory=\"mem=\"size;}",
105                        "}else{",
106                        "if (type!=\"\" && prod!=\"\"){",
107                        "if (prod==\"v.\"vers)",
108                        "vers=\"\";",
109                        "print type\"=\"vend,prod,size,vers;}}}",
110                        "type=prod=vend=vers=size=clock=slot=\"\";}",
111                        "$1~/-core/    {type=\"boa\";}",
112                        "$1~/-firmware/ {type=\"bio\";}",
113                        "$1~/-cpu/     {type=\"cpu\";}",
114                        "$1~/-bank/    {type=\"mem\";}",
115                        "$1~/-memory/  {type=\"totalmem\";}",
116                        "$1~/-ide/     {type=\"ide\";}",
117                        "$1~/-storage/ {type=\"sto\";}",
118                        "$1~/-disk/    {type=\"dis\";}",
119                        "$1~/-cdrom/   {type=\"cdr\";}",
120                        "$1~/-display/ {type=\"vga\";}",
121                        "$1~/-network/ {type=\"net\";}",
122                        "$1~/-multimedia/ {type=\"mul\";}",
123                        "$1~/-usb/     {type=\"usb\";}",
124                        "$1~/-firewire/ {type=\"fir\";}",
125                        "$1~/-serial/  {type=\"bus\";}",
126                        "END           {if (type!=\"\" && prod!=\"\")",
127                        "print type\"=\"vend,prod,size,vers;",
128                        "if (length(numbank)==0 && length(totalmemory)>=4)",
129                        "print totalmemory; }'",
130                        ])
131    # */ (comentario para Doxygen)
132
133def ogListSoftware():
134    # Si se solicita, mostrar ayuda.
135    if len(sys.argv) > 1 and sys.argv[1] == "help":
136        ogHelp(sys.argv[0], sys.argv[0] + " 1 1")
137        return
138
139    # Error si no se reciben 2 parametros.
140    if len(sys.argv) != 3:
141        ogRaiseError(OG_ERR_FORMAT)
142        return
143
144    # Obtener tipo de sistema de archivos y montarlo.
145    MNTDIR = ogMount(sys.argv[1], sys.argv[2])
146    if not MNTDIR:
147        return
148    TYPE = ogGetOsType(sys.argv[1], sys.argv[2])
149    if not TYPE:
150        return
151
152    # Ficheros temporales.
153    APPS = tempfile.mktemp()
154    TMPFILE = tempfile.mktemp()
155    try:
156        with open(APPS, "w") as apps_file:
157            if TYPE == "Linux":
158                # Procesar paquetes dpkg.
159                PKGDIR = os.path.join(MNTDIR, "var", "lib", "dpkg")
160                if os.path.exists(PKGDIR):
161                    # Proceso de fichero en sistemas de 64 bits.
162                    subprocess.call(["awk", "/Package:/ {if (pack!=\"\") print pack,vers;\
163                                                sub(/-dev$/,\"\",$2);\
164                                                pack=$2}\
165                                        /Version:/ {sub(/^.*:/,\"\",$2); sub(/-.*$/,\"\",$2);\
166                                                    vers=$2}\
167                                        /Status:/  {if ($2!=\"install\") pack=vers=\"\"}\
168                                        END        {if (pack!=\"\") print pack,vers}", os.path.join(PKGDIR, "status")], stdout=apps_file)
169               
170                # Procesar paquetes RPM.
171                PKGDIR = os.path.join(MNTDIR, "var", "lib", "rpm")
172                if os.path.exists(PKGDIR):
173                    # Listar si está instalado el paquete "rpm" en el cliente.
174                    if shutil.which("rpm"):
175                        subprocess.call(["rm", "-f", os.path.join(PKGDIR, "__db.*")])
176                        subprocess.call(["rpm", "--dbpath", PKGDIR, "-qa", "--qf", "%{NAME} %{VERSION}\n"], stdout=apps_file, stderr=subprocess.DEVNULL)
177                        subprocess.call(["rm", "-f", os.path.join(PKGDIR, "__db.*")])
178                    else:
179                        # Obtener el nombre de cada paquete en la BD de RPM.
180                        subprocess.call(["python", "-c", f"import re; import bsddb; db=bsddb.hashopen('{os.path.join(PKGDIR, 'Name')}','r');\
181                                                            for k in db.keys():\
182                                                                print(re.sub('-devel$','',k));"], stdout=apps_file)
183               
184                # Procesar paquetes pacman.
185                PKGDIR = os.path.join(MNTDIR, "var", "lib", "pacman", "local")
186                if os.path.exists(PKGDIR):
187                    subprocess.call(["ls", PKGDIR], stdout=subprocess.PIPE)
188                    subprocess.call(["awk", "-F-", "/-/ {print gensub(/-/, \" \", NF-2);}", os.path.join(PKGDIR, "*")], stdout=apps_file)
189               
190                # Procesar aplicaciones Snappy.
191                PKGDIR = os.path.join(MNTDIR, "snap")
192                subprocess.call(["find", PKGDIR, "-name", "snap.yaml", "-exec", "awk", "/name:/ {pack=$2}\
193                                                                                        /version:/ {vers=$2}\
194                                                                                        END {if (pack!=\"\") print pack,\"(snap)\",vers}", "{}", "+", "2>/dev/null"], stdout=apps_file)
195               
196                # Procesar aplicaciones Flatpak.
197                PKGDIR = os.path.join(MNTDIR, "var", "lib", "flatpak")
198                subprocess.call(["ls", "-1", os.path.join(PKGDIR, "app", "*", "current", "active", "deploy")], stderr=subprocess.DEVNULL)
199                subprocess.call(["python", "-c", "import sys; for f in sys.stdin:\
200                                                    p = open(f.strip()).read().split('\0');\
201                                                    try:\
202                                                        if(p[0] != 'flathub'):\
203                                                            raise ValueError;\
204                                                        print('{} (flatpak) {}'.format(p[p.index('appdata-name') + 4], p[p.index('appdata-version') + 1]));\
205                                                    except ValueError:\
206                                                        pass;"], stdout=apps_file)
207               
208            elif TYPE == "Windows":
209                # Comprobar tipo de proceso del registro de Windows.
210                if shutil.which("hivexregedit"):
211                    # Nuevo proceso más rápido basado en "hivexregedit".
212                    HIVE = ogGetHivePath(MNTDIR, "software")
213                    if HIVE:
214                        # Claves de registro para programas instalados.
215                        subprocess.call(["hivexregedit", "--unsafe-printable-strings", "--export", HIVE, "\\Microsoft\\Windows\\CurrentVersion\\Uninstall"], stdout=TMPFILE, stderr=subprocess.DEVNULL)
216                        subprocess.call(["hivexregedit", "--unsafe-printable-strings", "--export", HIVE, "\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"], stdout=TMPFILE, stderr=subprocess.DEVNULL)
217                        # Mostrar los valores "DisplayName" y "DisplayVersion" para cada clave.
218                        subprocess.call(["awk", "-F\"", "$1~/^\\[/ {n=\"\"}\
219                                                            $2~/DisplayName/ {n=$4}\
220                                                            $2~/DisplayVersion/ {print n,$4}", TMPFILE], stdout=apps_file)
221                else:
222                    # Compatibilidad con clientes ogLive antiguos.
223                    # Claves de registro para programas instalados: formato "{clave}".
224                    KEYS = ogListRegistryKeys(MNTDIR, "software", "\\Microsoft\\Windows\\CurrentVersion\\Uninstall")
225                    KEYS32 = ogListRegistryKeys(MNTDIR, "software", "\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall")
226                    # Mostrar los valores "DisplayName" y "DisplayVersion" para cada clave.
227                    for k in KEYS:
228                        PROG = ogGetRegistryValue(MNTDIR, "software", f"\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{k}\\DisplayName")
229                        if PROG:
230                            VERS = ogGetRegistryValue(MNTDIR, "software", f"\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{k}\\DisplayVersion")
231                            apps_file.write(f"{PROG} {VERS}\n")
232                    for k in KEYS32:
233                        PROG = ogGetRegistryValue(MNTDIR, "software", f"\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{k}\\DisplayName")
234                        if PROG:
235                            VERS = ogGetRegistryValue(MNTDIR, "software", f"\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{k}\\DisplayVersion")
236                            apps_file.write(f"{PROG} {VERS}\n")
237               
238            elif TYPE == "MacOS":
239                # Listar directorios de aplicaciones e intentar obtener la versión del fichero .plist (tanto original como descomprimido).
240                for root, dirs, files in os.walk(os.path.join(MNTDIR, "Applications")):
241                    for dir in dirs:
242                        if dir.endswith(".app"):
243                            file = os.path.join(root, dir, "Contents", "version.plist")
244                            if not os.path.exists(file):
245                                file = os.path.join(root, dir, "Contents", "version.plist.uncompress")
246                            if os.path.exists(file):
247                                with open(file, "r") as plist_file:
248                                    plist_data = plist_file.read()
249                                    version = re.search("<key>ShortVersionString</key>\s*<string>(.*?)</string>", plist_data)
250                                    if version:
251                                        apps_file.write(f"{os.path.basename(dir)}.app {version.group(1)}\n")
252               
253            elif TYPE == "BSD":
254                subprocess.call(["sqlite3", os.path.join(MNTDIR, "var", "db", "pkg", "local.sqlite")], input="SELECT name FROM pkg_search;", stdout=apps_file, stderr=subprocess.DEVNULL)
255       
256        # Mostrar sistema Operativo y aplicaciones.
257        ogGetOsVersion(sys.argv[1], sys.argv[2]).split(":")[1].strip()
258        with open(APPS, "r") as apps_file:
259            apps = apps_file.readlines()
260            apps = sorted(set(apps))
261            for app in apps:
262                print(app.strip())
263    finally:
264        os.remove(APPS)
265        os.remove(TMPFILE)
266
267def ogGetOsVersion(ndisk, nfilesys):
268    # Variables locales.
269    MNTDIR = ogMount(ndisk, nfilesys)
270    TYPE = ""
271    VERSION = ""
272    IS64BIT = ""
273
274    # Si se solicita, mostrar ayuda.
275    if len(sys.argv) > 1 and sys.argv[1] == "help":
276        ogHelp(sys.argv[0], sys.argv[0] + " int_ndisk int_nfilesys", sys.argv[0] + " 1 2  =>  Linux:Ubuntu precise (12.04 LTS) 64 bits")
277        return
278
279    # Error si no se reciben 2 parametros.
280    if len(sys.argv) != 3:
281        ogRaiseError(OG_ERR_FORMAT)
282        return
283
284    # Montar la particion, si no lo estaba previamente.
285    MNTDIR = ogMount(sys.argv[1], sys.argv[2])
286    if not MNTDIR:
287        return
288
289    # Buscar tipo de sistema operativo.
290    # Para GNU/Linux: leer descripción.
291    TYPE = "Linux"
292    FILE = os.path.join(MNTDIR, "etc", "os-release")
293    if os.path.exists(FILE):
294        with open(FILE, "r") as f:
295            for line in f:
296                if line.startswith("PRETTY_NAME"):
297                    VERSION = line.split("=")[1].strip().strip('"')
298                    break
299
300    # Si no se puede obtener, buscar en ficheros del sistema.
301    if not VERSION:
302        FILE = os.path.join(MNTDIR, "etc", "lsb-release")
303        if os.path.exists(FILE):
304            with open(FILE, "r") as f:
305                for line in f:
306                    if line.startswith("DESCRIPTION"):
307                        VERSION = line.split("=")[1].strip().strip('"')
308                        break
309        for DISTRIB in ["redhat", "SuSE", "mandrake", "gentoo"]:
310            FILE = os.path.join(MNTDIR, "etc", f"{DISTRIB}-release")
311            if os.path.exists(FILE):
312                with open(FILE, "r") as f:
313                    VERSION = f.readline().strip()
314                    break
315        FILE = os.path.join(MNTDIR, "etc", "arch-release")
316        if os.path.exists(FILE):
317            VERSION = "Arch Linux"
318        FILE = os.path.join(MNTDIR, "etc", "slackware-version")
319        if os.path.exists(FILE):
320            with open(FILE, "r") as f:
321                VERSION = f.read().strip()
322
323    # Si no se encuentra, intentar ejecutar "lsb_release".
324    if not VERSION:
325        try:
326            output = subprocess.check_output(["chroot", MNTDIR, "lsb_release", "-d"], stderr=subprocess.DEVNULL).decode().strip()
327            VERSION = output.split(":")[1].strip()
328        except subprocess.CalledProcessError:
329            pass
330
331    # Comprobar Linux de 64 bits.
332    if VERSION and os.path.exists(os.path.join(MNTDIR, "lib64")):
333        IS64BIT = "64 bits"
334
335    # Para Android, leer fichero de propiedades.
336    if not VERSION:
337        TYPE = "Android"
338        FILE = os.path.join(MNTDIR, "android*", "system", "build.prop")
339        if glob.glob(FILE):
340            with open(glob.glob(FILE)[0], "r") as f:
341                lines = f.readlines()
342                brand = ""
343                release = ""
344                for line in lines:
345                    if line.startswith("product.brand"):
346                        brand = line.split("=")[1].strip()
347                    elif line.startswith("build.version.release"):
348                        release = line.split("=")[1].strip()
349                VERSION = f"Android {brand} {release}"
350                if os.path.exists(os.path.join(MNTDIR, "lib64")):
351                    IS64BIT = "64 bits"
352
353    # Para GNU/Hurd, comprobar fichero de inicio (basado en os-prober).
354    if not VERSION:
355        TYPE = "Hurd"
356        FILE = os.path.join(MNTDIR, "hurd", "init")
357        if os.path.exists(FILE):
358            VERSION = "GNU/Hurd"
359
360    # Para Windows: leer la version del registro.
361    if not VERSION:
362        TYPE = "Windows"
363        FILE = ogGetHivePath(MNTDIR, "SOFTWARE")
364        if FILE:
365            try:
366                output = subprocess.check_output(["hivexsh", "load", FILE, "cd", "\\Microsoft\\Windows NT\\CurrentVersion", "lsval", "ProductName", "lsval", "DisplayVersion"], stderr=subprocess.DEVNULL).decode().strip()
367                lines = output.split("\n")
368                if len(lines) == 2:
369                    VERSION = lines[1].strip()
370                    if ogGetRegistryValue(MNTDIR, "SOFTWARE", "\\Microsoft\\Windows\\CurrentVersion\\ProgramW6432Dir"):
371                        IS64BIT = "64 bits"
372            except subprocess.CalledProcessError:
373                pass
374
375    # Para cargador Windows: buscar versión en fichero BCD (basado en os-prober).
376    if not VERSION:
377        TYPE = "WinLoader"
378        FILE = ogGetPath(MNTDIR, "boot", "bcd")
379        if not FILE:
380            FILE = ogGetPath(MNTDIR, "EFI", "Microsoft", "boot", "bcd")
381        if FILE:
382            for DISTRIB in ["Windows Recovery", "Windows Boot"]:
383                with open(FILE, "rb") as f:
384                    if re.search(DISTRIB.encode(), f.read()):
385                        VERSION = f"{DISTRIB} loader"
386                        break
387
388    # Para macOS: detectar kernel y completar con fichero plist de información del sistema.
389    if not VERSION:
390        TYPE = "MacOS"
391        FILE = os.path.join(MNTDIR, "mach_kernel")
392        if os.path.exists(FILE) and not subprocess.check_output(["file", "-b", FILE]).decode().strip().startswith("text"):
393            if subprocess.check_output(["file", "-b", FILE]).decode().strip().startswith("Mach-O"):
394                VERSION = "macOS"
395            if subprocess.check_output(["file", "-b", FILE]).decode().strip().startswith("Mach-O 64-bit"):
396                IS64BIT = "64 bits"
397            FILE = os.path.join(MNTDIR, "System", "Library", "CoreServices", "SystemVersion.plist")
398            if os.path.exists(FILE):
399                with open(FILE, "r") as f:
400                    plist_data = f.read()
401                    product_name = re.search("<key>ProductName</key>\s*<string>(.*?)</string>", plist_data)
402                    product_version = re.search("<key>ProductVersion</key>\s*<string>(.*?)</string>", plist_data)
403                    if product_name and product_version:
404                        VERSION = f"{product_name.group(1)} {product_version.group(1)}"
405            FILE = os.path.join(MNTDIR, "com.apple.recovery.boot")
406            if os.path.exists(FILE) and VERSION:
407                VERSION = f"{VERSION} recovery"
408
409    # Para FreeBSD: obtener datos del Kernel.
410    if not VERSION:
411        TYPE = "BSD"
412        FILE = os.path.join(MNTDIR, "boot", "kernel", "kernel")
413        if os.path.exists(FILE):
414            output = subprocess.check_output(["strings", FILE]).decode().strip()
415            match = re.search(r"@.*RELEASE", output)
416            if match:
417                VERSION = match.group().split("@")[0].strip()
418            if subprocess.check_output(["file", "-b", FILE]).decode().strip().endswith("x86-64"):
419                IS64BIT = "64 bits"
420
421    # Para Solaris: leer el fichero de versión.
422    if not VERSION:
423        TYPE = "Solaris"
424        FILE = os.path.join(MNTDIR, "etc", "release")
425        if os.path.exists(FILE):
426            with open(FILE, "r") as f:
427                VERSION = f.readline().strip()
428
429    # Para cargador GRUB, comprobar fichero de configuración.
430    if not VERSION:
431        TYPE = "GrubLoader"
432        for FILE in [os.path.join(MNTDIR, "grub", "menu.lst"), os.path.join(MNTDIR, "boot", "grub", "menu.lst")]:
433            if os.path.exists(FILE):
434                VERSION = "GRUB Loader"
435                break
436        for FILE in glob.glob(os.path.join(MNTDIR, "{,boot/}{grub{,2},EFI/*}/grub.cfg")):
437            if os.path.exists(FILE):
438                VERSION = "GRUB2 Loader"
439                break
440
441    # Mostrar resultado y salir sin errores.
442    if VERSION:
443        print(f"{TYPE}:{VERSION} {IS64BIT}")
444
445    return 0
Note: See TracBrowser for help on using the repository browser.