[902e019] | 1 | # |
---|
| 2 | # Copyright (C) 2022 Soleta Networks <info@soleta.eu> |
---|
| 3 | # |
---|
| 4 | # This program is free software: you can redistribute it and/or modify it under |
---|
| 5 | # the terms of the GNU Affero General Public License as published by the |
---|
| 6 | # Free Software Foundation; either version 3 of the License, or |
---|
| 7 | # (at your option) any later version. |
---|
| 8 | |
---|
| 9 | import os |
---|
| 10 | import subprocess |
---|
[4e5b17c] | 11 | import platform |
---|
[902e019] | 12 | |
---|
| 13 | from subprocess import PIPE |
---|
| 14 | |
---|
[4e5b17c] | 15 | from src.utils.fs import find_mountpoint |
---|
| 16 | |
---|
[902e019] | 17 | def getlinuxversion(osrelease): |
---|
| 18 | """ |
---|
| 19 | Parses a os-release file to fetch 'PRETTY_NAME' key. |
---|
| 20 | If file or key are not found, then returns generic |
---|
| 21 | 'Linux' string. |
---|
| 22 | """ |
---|
[4e5b17c] | 23 | mountpoint = find_mountpoint(osrelease) |
---|
| 24 | |
---|
[902e019] | 25 | with open(osrelease, 'r') as f: |
---|
| 26 | for line in f: |
---|
| 27 | if line.find('=') == -1: |
---|
| 28 | continue |
---|
| 29 | key, value = line.split('=') |
---|
| 30 | if key == 'PRETTY_NAME': |
---|
| 31 | value = value.replace('\n', '') |
---|
| 32 | value = value.strip('"') |
---|
[4e5b17c] | 33 | bits = ' 64 bits' if linux_is64bit(mountpoint) else '' |
---|
| 34 | return f'{value}{bits}' |
---|
[902e019] | 35 | return 'Linux' |
---|
| 36 | |
---|
| 37 | |
---|
| 38 | def getwindowsversion(winreghives): |
---|
| 39 | """ |
---|
| 40 | Tries to obtain windows version information by |
---|
| 41 | querying the SOFTWARE registry hive. Registry |
---|
| 42 | hives path is a required parameter. |
---|
| 43 | |
---|
| 44 | Runs hivexget(1) to fetch ProductName and |
---|
| 45 | ReleaseId. If something fails (hivexget is |
---|
| 46 | not installed, or registry is not found) it |
---|
| 47 | returns a generic "Microsoft Windows" string. |
---|
| 48 | """ |
---|
| 49 | |
---|
| 50 | # XXX: 3.6 friendly |
---|
| 51 | try: |
---|
| 52 | proc_prodname = subprocess.run(['hivexget', |
---|
| 53 | f'{winreghives}/SOFTWARE', |
---|
| 54 | 'microsoft\windows nt\currentversion', |
---|
| 55 | 'ProductName'], stdout=PIPE) |
---|
| 56 | proc_releaseid = subprocess.run(['hivexget', |
---|
| 57 | f'{winreghives}/SOFTWARE', |
---|
| 58 | 'microsoft\windows nt\currentversion', |
---|
| 59 | 'ReleaseId'], stdout=PIPE) |
---|
| 60 | |
---|
[ad3d3b2] | 61 | prodname = proc_prodname.stdout.decode().replace('\n', '') |
---|
| 62 | releaseid = proc_releaseid.stdout.decode().replace('\n', '') |
---|
[4e5b17c] | 63 | bits = ' 64 bits' if windows_is64bit(winreghives) else '' |
---|
[902e019] | 64 | |
---|
| 65 | if proc_prodname.returncode == 0 and proc_releaseid.returncode == 0: |
---|
[4e5b17c] | 66 | return f'{prodname} {releaseid}{bits}' |
---|
[902e019] | 67 | except FileNotFoundError: # hivexget command not found |
---|
| 68 | pass |
---|
| 69 | return 'Microsoft Windows' |
---|
| 70 | |
---|
| 71 | |
---|
[4e5b17c] | 72 | def windows_is64bit(winreghives): |
---|
| 73 | """ |
---|
| 74 | Check for 64 bit Windows by means of retrieving the value of |
---|
| 75 | ProgramW6432Dir. This key is set if Windows is running 64 bit. |
---|
| 76 | |
---|
| 77 | If set returns True. |
---|
| 78 | If not set or hivexget exits with non-zero, returns False. |
---|
| 79 | """ |
---|
| 80 | try: |
---|
| 81 | proc_hivexget = subprocess.run(['hivexget', |
---|
| 82 | f'{winreghives}/SOFTWARE', |
---|
| 83 | 'Microsoft\Windows\CurrentVersion', |
---|
| 84 | 'ProgramW6432Dir'], stdout=PIPE) |
---|
| 85 | stdout = proc_hivexget.stdout.decode().replace('\n', '') |
---|
| 86 | |
---|
| 87 | if proc_hivexget.returncode == 0 and stdout: |
---|
| 88 | return True |
---|
| 89 | except FileNotFoundError: # hivexget command not found |
---|
| 90 | pass |
---|
| 91 | return False |
---|
| 92 | |
---|
| 93 | |
---|
| 94 | def linux_is64bit(mountpoint): |
---|
| 95 | """ |
---|
| 96 | If /sbin/init is detected, check if compiled for 64-bit machine. |
---|
| 97 | |
---|
| 98 | If init executable is not found, check for /lib64. |
---|
| 99 | If /lib64 is found returns True, otherwise False. |
---|
| 100 | """ |
---|
| 101 | init_path = f'{mountpoint}/sbin/init' |
---|
| 102 | lib64_path = f'{mountpoint}/lib64' |
---|
| 103 | if os.path.exists(init_path): |
---|
| 104 | bits, linkage = platform.architecture(executable=init_path) |
---|
| 105 | return True if bits == '64bit' else False |
---|
| 106 | return os.path.exists(lib64_path) |
---|
| 107 | |
---|
| 108 | |
---|
[e6079c4] | 109 | def cache_probe(): |
---|
| 110 | """ |
---|
| 111 | Runs 'blkid -L CACHE' and returns stripped stdout |
---|
| 112 | """ |
---|
| 113 | proc_blkid = subprocess.run(['blkid', '-L', 'CACHE'], |
---|
| 114 | stdout=subprocess.PIPE) |
---|
| 115 | stdout = proc_blkid.stdout.decode().strip() |
---|
| 116 | return stdout |
---|
| 117 | |
---|
[902e019] | 118 | def os_probe(mountpoint): |
---|
| 119 | """ |
---|
| 120 | Probes mountpoint for typical OS dependant files. |
---|
| 121 | |
---|
| 122 | Windows: Tries finding and querying the software |
---|
| 123 | registry hive. |
---|
| 124 | Linux: Looks for /etc/os-release file. |
---|
| 125 | |
---|
| 126 | Returns a string depending on the OS it detects. |
---|
| 127 | """ |
---|
[ad2d4a1] | 128 | winreghives = f'{mountpoint}/Windows/System32/config' |
---|
[902e019] | 129 | osrelease = f'{mountpoint}/etc/os-release' |
---|
| 130 | |
---|
| 131 | if os.path.exists(osrelease): |
---|
| 132 | return getlinuxversion(osrelease) |
---|
| 133 | elif os.path.exists(winreghives): |
---|
| 134 | return getwindowsversion(winreghives) |
---|
| 135 | else: |
---|
| 136 | return '' |
---|