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 |
---|
11 | import platform |
---|
12 | |
---|
13 | from subprocess import PIPE |
---|
14 | |
---|
15 | from src.utils.fs import find_mountpoint |
---|
16 | |
---|
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 | """ |
---|
23 | mountpoint = find_mountpoint(osrelease) |
---|
24 | |
---|
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('"') |
---|
33 | bits = ' 64 bits' if linux_is64bit(mountpoint) else '' |
---|
34 | return f'{value}{bits}' |
---|
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 | |
---|
61 | prodname = proc_prodname.stdout.decode().replace('\n', '') |
---|
62 | releaseid = proc_releaseid.stdout.decode().replace('\n', '') |
---|
63 | bits = ' 64 bits' if windows_is64bit(winreghives) else '' |
---|
64 | |
---|
65 | if proc_prodname.returncode == 0 and proc_releaseid.returncode == 0: |
---|
66 | return f'{prodname} {releaseid}{bits}' |
---|
67 | except FileNotFoundError: # hivexget command not found |
---|
68 | pass |
---|
69 | return 'Microsoft Windows' |
---|
70 | |
---|
71 | |
---|
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 | |
---|
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 | |
---|
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 | """ |
---|
128 | winreghives = f'{mountpoint}/Windows/System32/config' |
---|
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 '' |
---|