diff --git a/.bin/Scripts/Launch.cmd b/.bin/Scripts/Launch.cmd index 0a0330c6..18c304f2 100644 --- a/.bin/Scripts/Launch.cmd +++ b/.bin/Scripts/Launch.cmd @@ -151,6 +151,7 @@ goto Exit call "%bin%\Scripts\init_client_dir.cmd" /Office set "_odt=False" if %L_PATH% equ 2016 (set "_odt=True") +if %L_PATH% equ 2019 (set "_odt=True") if "%_odt%" == "True" ( goto LaunchOfficeODT ) else ( @@ -161,8 +162,8 @@ if "%_odt%" == "True" ( rem Prep set "args=-aoa -bso0 -bse0 -bsp0 -p%ARCHIVE_PASSWORD%" set "config=%L_ITEM%" -set "dest=%client_dir%\Office\%L_PATH%" -set "odt_exe=%L_PATH%\setup.exe" +set "dest=%client_dir%\Office\ODT" +set "odt_exe=setup.exe" set "source=%cbin%\_Office.7z" rem Extract diff --git a/.bin/Scripts/activate.py b/.bin/Scripts/activate.py index 642e5edd..f9a9c69d 100644 --- a/.bin/Scripts/activate.py +++ b/.bin/Scripts/activate.py @@ -4,59 +4,60 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.activation import * init_global_vars() os.system('title {}: Windows Activation Tool'.format(KIT_NAME_FULL)) if __name__ == '__main__': - try: - stay_awake() - clear_screen() - print_info('{}: Windows Activation Tool\n'.format(KIT_NAME_FULL)) - # Bail early if already activated - if windows_is_activated(): - print_info('This system is already activated') - sleep(5) - exit_script() - other_results = { - 'Error': { - 'BIOSKeyNotFoundError': 'BIOS key not found.', - }} + try: + stay_awake() + clear_screen() + print_info('{}: Windows Activation Tool\n'.format(KIT_NAME_FULL)) + # Bail early if already activated + if windows_is_activated(): + print_info('This system is already activated') + sleep(5) + exit_script() + other_results = { + 'Error': { + 'BIOSKeyNotFoundError': 'BIOS key not found.', + }} - # Determine activation method - activation_methods = [ - {'Name': 'Activate with BIOS key', 'Function': activate_with_bios}, - ] - if global_vars['OS']['Version'] not in ('8', '8.1', '10'): - activation_methods[0]['Disabled'] = True - actions = [ - {'Name': 'Quit', 'Letter': 'Q'}, - ] + # Determine activation method + activation_methods = [ + {'Name': 'Activate with BIOS key', 'Function': activate_with_bios}, + ] + if global_vars['OS']['Version'] not in ('8', '8.1', '10'): + activation_methods[0]['Disabled'] = True + actions = [ + {'Name': 'Quit', 'Letter': 'Q'}, + ] - while True: - selection = menu_select( - '{}: Windows Activation Menu'.format(KIT_NAME_FULL), - main_entries=activation_methods, action_entries=actions) + while True: + selection = menu_select( + '{}: Windows Activation Menu'.format(KIT_NAME_FULL), + main_entries=activation_methods, action_entries=actions) - if (selection.isnumeric()): - result = try_and_print( - message = activation_methods[int(selection)-1]['Name'], - function = activation_methods[int(selection)-1]['Function'], - other_results=other_results) - if result['CS']: - break - else: - sleep(2) - elif selection == 'Q': - exit_script() - - # Done - print_success('\nDone.') - pause("Press Enter to exit...") + if (selection.isnumeric()): + result = try_and_print( + message = activation_methods[int(selection)-1]['Name'], + function = activation_methods[int(selection)-1]['Function'], + other_results=other_results) + if result['CS']: + break + else: + sleep(2) + elif selection == 'Q': exit_script() - except SystemExit: - pass - except: - major_exception() + + # Done + print_success('\nDone.') + pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/borrowed/sensors-README.md b/.bin/Scripts/borrowed/sensors-README.md deleted file mode 100644 index 11858382..00000000 --- a/.bin/Scripts/borrowed/sensors-README.md +++ /dev/null @@ -1,35 +0,0 @@ -sensors.py -========== -python bindings using ctypes for libsensors3 of the [lm-sensors project](https://github.com/groeck/lm-sensors). The code was written against libsensors 3.3.4. - -For documentation of the low level API see [sensors.h](https://github.com/groeck/lm-sensors/blob/master/lib/sensors.h). For an example of the high level API see [example.py](example.py). - -For a GUI application that displays the sensor readings and is based on this library, take a look at [sensors-unity](https://launchpad.net/sensors-unity). - -Features --------- -* Full access to low level libsensors3 API -* High level iterator API -* unicode handling -* Python2 and Python3 compatible - -Licensing ---------- -LGPLv2 (same as libsensors3) - -Usage Notes ------------ -As Python does not support call by reference for primitive types some of the libsensors API had to be adapted: - -```python -# nr is changed by refrence in the C API -chip_name, nr = sensors.get_detected_chips(None, nr) - -# returns the value. throws on error -val = sensors.get_value(chip, subfeature_nr) -``` - -Missing Features (pull requests are welcome): -* `sensors_subfeature_type` enum -* `sensors_get_subfeature` -* Error handlers diff --git a/.bin/Scripts/borrowed/sensors.py b/.bin/Scripts/borrowed/sensors.py deleted file mode 100644 index 39b00a4f..00000000 --- a/.bin/Scripts/borrowed/sensors.py +++ /dev/null @@ -1,236 +0,0 @@ -""" -@package sensors.py -Python Bindings for libsensors3 - -use the documentation of libsensors for the low level API. -see example.py for high level API usage. - -@author: Pavel Rojtberg (http://www.rojtberg.net) -@see: https://github.com/paroj/sensors.py -@copyright: LGPLv2 (same as libsensors) -""" - -from ctypes import * -import ctypes.util - -_libc = cdll.LoadLibrary(ctypes.util.find_library("c")) -# see https://github.com/paroj/sensors.py/issues/1 -_libc.free.argtypes = [c_void_p] - -_hdl = cdll.LoadLibrary(ctypes.util.find_library("sensors")) - -version = c_char_p.in_dll(_hdl, "libsensors_version").value.decode("ascii") - -class bus_id(Structure): - _fields_ = [("type", c_short), - ("nr", c_short)] - -class chip_name(Structure): - _fields_ = [("prefix", c_char_p), - ("bus", bus_id), - ("addr", c_int), - ("path", c_char_p)] - -class feature(Structure): - _fields_ = [("name", c_char_p), - ("number", c_int), - ("type", c_int)] - - # sensors_feature_type - IN = 0x00 - FAN = 0x01 - TEMP = 0x02 - POWER = 0x03 - ENERGY = 0x04 - CURR = 0x05 - HUMIDITY = 0x06 - MAX_MAIN = 0x7 - VID = 0x10 - INTRUSION = 0x11 - MAX_OTHER = 0x12 - BEEP_ENABLE = 0x18 - -class subfeature(Structure): - _fields_ = [("name", c_char_p), - ("number", c_int), - ("type", c_int), - ("mapping", c_int), - ("flags", c_uint)] - -_hdl.sensors_get_detected_chips.restype = POINTER(chip_name) -_hdl.sensors_get_features.restype = POINTER(feature) -_hdl.sensors_get_all_subfeatures.restype = POINTER(subfeature) -_hdl.sensors_get_label.restype = c_void_p # return pointer instead of str so we can free it -_hdl.sensors_get_adapter_name.restype = c_char_p # docs do not say whether to free this or not -_hdl.sensors_strerror.restype = c_char_p - -### RAW API ### -MODE_R = 1 -MODE_W = 2 -COMPUTE_MAPPING = 4 - -def init(cfg_file = None): - file = _libc.fopen(cfg_file.encode("utf-8"), "r") if cfg_file is not None else None - - if _hdl.sensors_init(file) != 0: - raise Exception("sensors_init failed") - - if file is not None: - _libc.fclose(file) - -def cleanup(): - _hdl.sensors_cleanup() - -def parse_chip_name(orig_name): - ret = chip_name() - err= _hdl.sensors_parse_chip_name(orig_name.encode("utf-8"), byref(ret)) - - if err < 0: - raise Exception(strerror(err)) - - return ret - -def strerror(errnum): - return _hdl.sensors_strerror(errnum).decode("utf-8") - -def free_chip_name(chip): - _hdl.sensors_free_chip_name(byref(chip)) - -def get_detected_chips(match, nr): - """ - @return: (chip, next nr to query) - """ - _nr = c_int(nr) - - if match is not None: - match = byref(match) - - chip = _hdl.sensors_get_detected_chips(match, byref(_nr)) - chip = chip.contents if bool(chip) else None - return chip, _nr.value - -def chip_snprintf_name(chip, buffer_size=200): - """ - @param buffer_size defaults to the size used in the sensors utility - """ - ret = create_string_buffer(buffer_size) - err = _hdl.sensors_snprintf_chip_name(ret, buffer_size, byref(chip)) - - if err < 0: - raise Exception(strerror(err)) - - return ret.value.decode("utf-8") - -def do_chip_sets(chip): - """ - @attention this function was not tested - """ - err = _hdl.sensors_do_chip_sets(byref(chip)) - if err < 0: - raise Exception(strerror(err)) - -def get_adapter_name(bus): - return _hdl.sensors_get_adapter_name(byref(bus)).decode("utf-8") - -def get_features(chip, nr): - """ - @return: (feature, next nr to query) - """ - _nr = c_int(nr) - feature = _hdl.sensors_get_features(byref(chip), byref(_nr)) - feature = feature.contents if bool(feature) else None - return feature, _nr.value - -def get_label(chip, feature): - ptr = _hdl.sensors_get_label(byref(chip), byref(feature)) - val = cast(ptr, c_char_p).value.decode("utf-8") - _libc.free(ptr) - return val - -def get_all_subfeatures(chip, feature, nr): - """ - @return: (subfeature, next nr to query) - """ - _nr = c_int(nr) - subfeature = _hdl.sensors_get_all_subfeatures(byref(chip), byref(feature), byref(_nr)) - subfeature = subfeature.contents if bool(subfeature) else None - return subfeature, _nr.value - -def get_value(chip, subfeature_nr): - val = c_double() - err = _hdl.sensors_get_value(byref(chip), subfeature_nr, byref(val)) - if err < 0: - raise Exception(strerror(err)) - return val.value - -def set_value(chip, subfeature_nr, value): - """ - @attention this function was not tested - """ - val = c_double(value) - err = _hdl.sensors_set_value(byref(chip), subfeature_nr, byref(val)) - if err < 0: - raise Exception(strerror(err)) - -### Convenience API ### -class ChipIterator: - def __init__(self, match = None): - self.match = parse_chip_name(match) if match is not None else None - self.nr = 0 - - def __iter__(self): - return self - - def __next__(self): - chip, self.nr = get_detected_chips(self.match, self.nr) - - if chip is None: - raise StopIteration - - return chip - - def __del__(self): - if self.match is not None: - free_chip_name(self.match) - - def next(self): # python2 compability - return self.__next__() - -class FeatureIterator: - def __init__(self, chip): - self.chip = chip - self.nr = 0 - - def __iter__(self): - return self - - def __next__(self): - feature, self.nr = get_features(self.chip, self.nr) - - if feature is None: - raise StopIteration - - return feature - - def next(self): # python2 compability - return self.__next__() - -class SubFeatureIterator: - def __init__(self, chip, feature): - self.chip = chip - self.feature = feature - self.nr = 0 - - def __iter__(self): - return self - - def __next__(self): - subfeature, self.nr = get_all_subfeatures(self.chip, self.feature, self.nr) - - if subfeature is None: - raise StopIteration - - return subfeature - - def next(self): # python2 compability - return self.__next__() diff --git a/.bin/Scripts/build-ufd b/.bin/Scripts/build-ufd index c606892b..1253472c 100755 --- a/.bin/Scripts/build-ufd +++ b/.bin/Scripts/build-ufd @@ -533,6 +533,9 @@ echo -e "${GREEN}Building Kit${CLEAR}" touch "${LOG_FILE}" tmux split-window -dl 10 tail -f "${LOG_FILE}" +# Zero beginning of device +dd bs=4M count=16 if=/dev/zero of="${DEST_DEV}" >> "${LOG_FILE}" 2>&1 + # Format echo "Formatting drive..." if [[ "${USE_MBR}" == "True" ]]; then diff --git a/.bin/Scripts/build_kit.ps1 b/.bin/Scripts/build_kit.ps1 index bd213578..08a050c9 100644 --- a/.bin/Scripts/build_kit.ps1 +++ b/.bin/Scripts/build_kit.ps1 @@ -79,21 +79,21 @@ if ($MyInvocation.InvocationName -ne ".") { $Path = $Temp # 7-Zip - DownloadFile -Path $Path -Name "7z-installer.msi" -Url "https://www.7-zip.org/a/7z1805.msi" - DownloadFile -Path $Path -Name "7z-extra.7z" -Url "https://www.7-zip.org/a/7z1805-extra.7z" + DownloadFile -Path $Path -Name "7z-installer.msi" -Url "https://www.7-zip.org/a/7z1806.msi" + DownloadFile -Path $Path -Name "7z-extra.7z" -Url "https://www.7-zip.org/a/7z1806-extra.7z" # ConEmu $Url = "https://github.com/Maximus5/ConEmu/releases/download/v18.06.26/ConEmuPack.180626.7z" DownloadFile -Path $Path -Name "ConEmuPack.7z" -Url $Url # Notepad++ - $Url = "https://notepad-plus-plus.org/repository/7.x/7.5.8/npp.7.5.8.bin.minimalist.7z" + $Url = "https://notepad-plus-plus.org/repository/7.x/7.6.2/npp.7.6.2.bin.minimalist.7z" DownloadFile -Path $Path -Name "npp.7z" -Url $Url # Python - $Url = "https://www.python.org/ftp/python/3.7.0/python-3.7.0-embed-win32.zip" + $Url = "https://www.python.org/ftp/python/3.7.2/python-3.7.2.post1-embed-win32.zip" DownloadFile -Path $Path -Name "python32.zip" -Url $Url - $Url = "https://www.python.org/ftp/python/3.7.0/python-3.7.0-embed-amd64.zip" + $Url = "https://www.python.org/ftp/python/3.7.2/python-3.7.2.post1-embed-amd64.zip" DownloadFile -Path $Path -Name "python64.zip" -Url $Url # Python: psutil diff --git a/.bin/Scripts/build_pe.ps1 b/.bin/Scripts/build_pe.ps1 index 560d570d..00405a12 100644 --- a/.bin/Scripts/build_pe.ps1 +++ b/.bin/Scripts/build_pe.ps1 @@ -131,25 +131,25 @@ if ($MyInvocation.InvocationName -ne ".") { ## Download Tools ## $ToolSources = @( # 7-Zip - @("7z-installer.msi", "https://www.7-zip.org/a/7z1805.msi"), - @("7z-extra.7z", "https://www.7-zip.org/a/7z1805-extra.7z"), + @("7z-installer.msi", "https://www.7-zip.org/a/7z1806.msi"), + @("7z-extra.7z", "https://www.7-zip.org/a/7z1806-extra.7z"), # Blue Screen View @("bluescreenview32.zip", "http://www.nirsoft.net/utils/bluescreenview.zip"), @("bluescreenview64.zip", "http://www.nirsoft.net/utils/bluescreenview-x64.zip"), # ConEmu @("ConEmuPack.7z", "https://github.com/Maximus5/ConEmu/releases/download/v18.06.26/ConEmuPack.180626.7z"), # Fast Copy - @("fastcopy.zip", "http://ftp.vector.co.jp/70/64/2323/FastCopy354_installer.zip"), + @("fastcopy.zip", "http://ftp.vector.co.jp/70/93/2323/FastCopy361_installer.exe"), # HWiNFO - @("hwinfo.zip", "http://app.oldfoss.com:81/download/HWiNFO/hwi_588.zip"), + @("hwinfo.zip", "https://www.fosshub.com/HWiNFO.html?dwl=hwi_600.zip"), # Killer Network Drivers @( "killerinf.zip", - ("http://www.killernetworking.com"+(FindDynamicUrl "http://www.killernetworking.com/driver-downloads/item/killer-drivers-inf" "Download Killer-Ethernet").replace('&', '&')) + ("https://www.killernetworking.com"+(FindDynamicUrl "https://www.killernetworking.com/killersupport/category/other-downloads" "Download Killer-Ethernet").replace('&', '&')) ), # Notepad++ - @("npp_x86.7z", "https://notepad-plus-plus.org/repository/7.x/7.5.8/npp.7.5.8.bin.minimalist.7z"), - @("npp_amd64.7z", "https://notepad-plus-plus.org/repository/7.x/7.5.8/npp.7.5.8.bin.minimalist.x64.7z"), + @("npp_x86.7z", "https://notepad-plus-plus.org/repository/7.x/7.6.2/npp.7.6.2.bin.minimalist.7z"), + @("npp_amd64.7z", "https://notepad-plus-plus.org/repository/7.x/7.6.2/npp.7.6.2.bin.minimalist.x64.7z"), # NT Password Editor @("ntpwed.zip", "http://cdslow.org.ru/files/ntpwedit/ntpwed07.zip"), # Prime95 @@ -159,8 +159,8 @@ if ($MyInvocation.InvocationName -ne ".") { @("produkey32.zip", "http://www.nirsoft.net/utils/produkey.zip"), @("produkey64.zip", "http://www.nirsoft.net/utils/produkey-x64.zip"), # Python - @("python32.zip", "https://www.python.org/ftp/python/3.7.0/python-3.7.0-embed-win32.zip"), - @("python64.zip", "https://www.python.org/ftp/python/3.7.0/python-3.7.0-embed-amd64.zip"), + @("python32.zip", "https://www.python.org/ftp/python/3.7.2/python-3.7.2.post1-embed-win32.zip"), + @("python64.zip", "https://www.python.org/ftp/python/3.7.2/python-3.7.2.post1-embed-amd64.zip"), # Python: psutil @( "psutil64.whl", @@ -182,8 +182,8 @@ if ($MyInvocation.InvocationName -ne ".") { @("vcredist_x86.exe", "https://aka.ms/vs/15/release/vc_redist.x86.exe"), @("vcredist_x64.exe", "https://aka.ms/vs/15/release/vc_redist.x64.exe"), # wimlib-imagex - @("wimlib32.zip", "https://wimlib.net/downloads/wimlib-1.12.0-windows-i686-bin.zip"), - @("wimlib64.zip", "https://wimlib.net/downloads/wimlib-1.12.0-windows-x86_64-bin.zip") + @("wimlib32.zip", "https://wimlib.net/downloads/wimlib-1.13.0-windows-i686-bin.zip"), + @("wimlib64.zip", "https://wimlib.net/downloads/wimlib-1.13.0-windows-x86_64-bin.zip") ) foreach ($Tool in $ToolSources) { DownloadFile -Path $Temp -Name $Tool[0] -Url $Tool[1] diff --git a/.bin/Scripts/cbs_fix.py b/.bin/Scripts/cbs_fix.py index 9a8ff10c..36a7906f 100644 --- a/.bin/Scripts/cbs_fix.py +++ b/.bin/Scripts/cbs_fix.py @@ -4,8 +4,7 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.cleanup import * from functions.data import * init_global_vars() @@ -13,30 +12,32 @@ os.system('title {}: CBS Cleanup'.format(KIT_NAME_FULL)) set_log_file('CBS Cleanup.log') if __name__ == '__main__': - try: - # Prep - stay_awake() - clear_screen() - folder_path = r'{}\Backups'.format(KIT_NAME_SHORT) - dest = select_destination(folder_path=folder_path, - prompt='Which disk are we using for temp data and backup?') + try: + # Prep + stay_awake() + clear_screen() + folder_path = r'{}\Backups'.format(KIT_NAME_SHORT) + dest = select_destination(folder_path=folder_path, + prompt='Which disk are we using for temp data and backup?') - # Show details - print_info('{}: CBS Cleanup Tool\n'.format(KIT_NAME_FULL)) - show_data('Backup / Temp path:', dest) - print_standard('\n') - if (not ask('Proceed with CBS cleanup?')): - abort() + # Show details + print_info('{}: CBS Cleanup Tool\n'.format(KIT_NAME_FULL)) + show_data('Backup / Temp path:', dest) + print_standard('\n') + if (not ask('Proceed with CBS cleanup?')): + abort() - # Run Cleanup - try_and_print(message='Running cleanup...', function=cleanup_cbs, - cs='Done', dest_folder=dest) + # Run Cleanup + try_and_print(message='Running cleanup...', function=cleanup_cbs, + cs='Done', dest_folder=dest) - # Done - print_standard('\nDone.') - pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + print_standard('\nDone.') + pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/check_disk.py b/.bin/Scripts/check_disk.py index 7e59fb2b..baee7460 100644 --- a/.bin/Scripts/check_disk.py +++ b/.bin/Scripts/check_disk.py @@ -4,53 +4,54 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.repairs import * init_global_vars() os.system('title {}: Check Disk Tool'.format(KIT_NAME_FULL)) set_log_file('Check Disk.log') if __name__ == '__main__': - try: - stay_awake() - clear_screen() - other_results = { - 'Error': { - 'CalledProcessError': 'Unknown Error', - }, - 'Warning': { - 'GenericRepair': 'Repaired', - 'UnsupportedOSError': 'Unsupported OS', - }} - options = [ - {'Name': 'Run CHKDSK scan (read-only)', 'Repair': False}, - {'Name': 'Schedule CHKDSK scan (offline repair)', 'Repair': True}] - actions = [{'Name': 'Quit', 'Letter': 'Q'}] - selection = menu_select( - '{}: Check Disk Menu\n'.format(KIT_NAME_FULL), - main_entries=options, - action_entries=actions) - print_info('{}: Check Disk Menu\n'.format(KIT_NAME_FULL)) - if selection == 'Q': - abort() - elif selection.isnumeric(): - repair = options[int(selection)-1]['Repair'] - if repair: - cs = 'Scheduled' - else: - cs = 'CS' - message = 'CHKDSK ({SYSTEMDRIVE})...'.format(**global_vars['Env']) - try_and_print(message=message, function=run_chkdsk, - cs=cs, other_results=other_results, repair=repair) - else: - abort() + try: + stay_awake() + clear_screen() + other_results = { + 'Error': { + 'CalledProcessError': 'Unknown Error', + }, + 'Warning': { + 'GenericRepair': 'Repaired', + 'UnsupportedOSError': 'Unsupported OS', + }} + options = [ + {'Name': 'Run CHKDSK scan (read-only)', 'Repair': False}, + {'Name': 'Schedule CHKDSK scan (offline repair)', 'Repair': True}] + actions = [{'Name': 'Quit', 'Letter': 'Q'}] + selection = menu_select( + '{}: Check Disk Menu\n'.format(KIT_NAME_FULL), + main_entries=options, + action_entries=actions) + print_info('{}: Check Disk Menu\n'.format(KIT_NAME_FULL)) + if selection == 'Q': + abort() + elif selection.isnumeric(): + repair = options[int(selection)-1]['Repair'] + if repair: + cs = 'Scheduled' + else: + cs = 'No issues' + message = 'CHKDSK ({SYSTEMDRIVE})...'.format(**global_vars['Env']) + try_and_print(message=message, function=run_chkdsk, + cs=cs, other_results=other_results, repair=repair) + else: + abort() - # Done - print_success('Done.') - pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + print_success('Done.') + pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/connect-to-network b/.bin/Scripts/connect-to-network index 0475c15c..02500b37 100755 --- a/.bin/Scripts/connect-to-network +++ b/.bin/Scripts/connect-to-network @@ -6,25 +6,25 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.network import * init_global_vars() if __name__ == '__main__': - try: - # Prep - clear_screen() + try: + # Prep + clear_screen() - # Connect - connect_to_network() - - # Done - print_standard('\nDone.') - #pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() + # Connect + connect_to_network() + # Done + print_standard('\nDone.') + #pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/ddrescue-tui b/.bin/Scripts/ddrescue-tui index d83b3a7e..e41c6bb1 100755 --- a/.bin/Scripts/ddrescue-tui +++ b/.bin/Scripts/ddrescue-tui @@ -3,7 +3,7 @@ ## Wizard Kit: ddrescue TUI Launcher SESSION_NAME="ddrescue-tui" -WINDOW_NAME="GNU ddrescue TUI" +WINDOW_NAME="ddrescue TUI" MENU="ddrescue-tui-menu" function ask() { diff --git a/.bin/Scripts/ddrescue-tui-menu b/.bin/Scripts/ddrescue-tui-menu index 4bec6230..a6014d76 100755 --- a/.bin/Scripts/ddrescue-tui-menu +++ b/.bin/Scripts/ddrescue-tui-menu @@ -7,57 +7,56 @@ import sys # Init sys.path.append(os.path.dirname(os.path.realpath(__file__))) - from functions.ddrescue import * from functions.hw_diags import * init_global_vars() if __name__ == '__main__': + try: + # Prep + clear_screen() + args = list(sys.argv) + run_mode = '' + source_path = None + dest_path = None + + # Parse args try: - # Prep - clear_screen() - args = list(sys.argv) - run_mode = '' - source_path = None - dest_path = None + script_name = os.path.basename(args.pop(0)) + run_mode = str(args.pop(0)).lower() + source_path = args.pop(0) + dest_path = args.pop(0) + except IndexError: + # We'll set the missing paths later + pass - # Parse args - try: - script_name = os.path.basename(args.pop(0)) - run_mode = str(args.pop(0)).lower() - source_path = args.pop(0) - dest_path = args.pop(0) - except IndexError: - # We'll set the missing paths later - pass + # Show usage + if re.search(r'-+(h|help)', str(sys.argv), re.IGNORECASE): + show_usage(script_name) + exit_script() - # Show usage - if re.search(r'-+(h|help)', str(sys.argv), re.IGNORECASE): - show_usage(script_name) - exit_script() + # Start cloning/imaging + if run_mode in ('clone', 'image'): + menu_ddrescue(source_path, dest_path, run_mode) + else: + if not re.search(r'^-*(h|help\?)', run_mode, re.IGNORECASE): + print_error('Invalid mode.') - # Start cloning/imaging - if run_mode in ('clone', 'image'): - menu_ddrescue(source_path, dest_path, run_mode) - else: - if not re.search(r'^-*(h|help\?)', run_mode, re.IGNORECASE): - print_error('Invalid mode.') - - # Done - print_standard('\nDone.') - pause("Press Enter to exit...") - exit_script() - except GenericAbort: - abort() - except GenericError as ge: - msg = 'Generic Error' - if str(ge): - msg = str(ge) - print_error(msg) - abort() - except SystemExit: - pass - except: - major_exception() + # Done + print_standard('\nDone.') + pause("Press Enter to exit...") + exit_script() + except GenericAbort: + abort() + except GenericError as ge: + msg = 'Generic Error' + if str(ge): + msg = str(ge) + print_error(msg) + abort() + except SystemExit: + pass + except: + major_exception() -# vim: sts=4 sw=4 ts=4 +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/ddrescue-tui-smart-display b/.bin/Scripts/ddrescue-tui-smart-display deleted file mode 100755 index 285229d6..00000000 --- a/.bin/Scripts/ddrescue-tui-smart-display +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/python3 -# -## Wizard Kit: SMART attributes display for ddrescue TUI - -import os -import sys -import time - -# Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) -from functions.hw_diags import * -#init_global_vars() - -if __name__ == '__main__': - try: - # Prep - clear_screen() - dev_path = sys.argv[1] - devs = scan_disks(True, dev_path) - - # Warn if SMART unavailable - if dev_path not in devs: - print_error('SMART data not available') - exit_script() - - # Initial screen - dev = devs[dev_path] - clear_screen() - show_disk_details(dev, only_attributes=True) - - # Done - exit_script() - except SystemExit: - pass - except: - major_exception() - -# vim: sts=4 sw=4 ts=4 diff --git a/.bin/Scripts/dism.py b/.bin/Scripts/dism.py index e49a9512..2ef3ff25 100644 --- a/.bin/Scripts/dism.py +++ b/.bin/Scripts/dism.py @@ -4,54 +4,55 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.repairs import * init_global_vars() os.system('title {}: DISM helper Tool'.format(KIT_NAME_FULL)) set_log_file('DISM Helper.log') if __name__ == '__main__': - try: - stay_awake() - clear_screen() - other_results = { - 'Error': { - 'CalledProcessError': 'Unknown Error', - }, - 'Warning': { - 'GenericRepair': 'Repaired', - 'UnsupportedOSError': 'Unsupported OS', - }} - disabled = bool(global_vars['OS']['Version'] not in ('8', '8.1', '10')) - options = [ - {'Name': 'Check Health', 'Repair': False, 'Disabled': disabled}, - {'Name': 'Restore Health', 'Repair': True, 'Disabled': disabled}] - actions = [{'Name': 'Quit', 'Letter': 'Q'}] - selection = menu_select( - '{}: DISM Menu\n'.format(KIT_NAME_FULL), - main_entries=options, - action_entries=actions) - print_info('{}: DISM Menu\n'.format(KIT_NAME_FULL)) - if selection == 'Q': - abort() - elif selection.isnumeric(): - repair = options[int(selection)-1]['Repair'] - if repair: - message='DISM RestoreHealth...' - else: - message='DISM ScanHealth...' - try_and_print(message=message, function=run_dism, - cs='No corruption', ns='Corruption detected', - other_results=other_results, repair=repair) - else: - abort() + try: + stay_awake() + clear_screen() + other_results = { + 'Error': { + 'CalledProcessError': 'Unknown Error', + }, + 'Warning': { + 'GenericRepair': 'Repaired', + 'UnsupportedOSError': 'Unsupported OS', + }} + disabled = bool(global_vars['OS']['Version'] not in ('8', '8.1', '10')) + options = [ + {'Name': 'Check Health', 'Repair': False, 'Disabled': disabled}, + {'Name': 'Restore Health', 'Repair': True, 'Disabled': disabled}] + actions = [{'Name': 'Quit', 'Letter': 'Q'}] + selection = menu_select( + '{}: DISM Menu\n'.format(KIT_NAME_FULL), + main_entries=options, + action_entries=actions) + print_info('{}: DISM Menu\n'.format(KIT_NAME_FULL)) + if selection == 'Q': + abort() + elif selection.isnumeric(): + repair = options[int(selection)-1]['Repair'] + if repair: + message='DISM RestoreHealth...' + else: + message='DISM ScanHealth...' + try_and_print(message=message, function=run_dism, + cs='No corruption', ns='Corruption detected', + other_results=other_results, repair=repair) + else: + abort() - # Done - print_success('Done.') - pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + print_success('Done.') + pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/find_user_wallpapers.py b/.bin/Scripts/find_user_wallpapers.py new file mode 100644 index 00000000..3ea182f5 --- /dev/null +++ b/.bin/Scripts/find_user_wallpapers.py @@ -0,0 +1,81 @@ +# Wizard Kit: Find user wallpaper(s) + +import os +import sys + +# STATIC VARIABLES +EXPLORER_LOCATIONS = [ + r'{APPDATA}\Microsoft\Internet Explorer\Internet Explorer Wallpaper.bmp', + r'{APPDATA}\Microsoft\Windows\Themes\TranscodedWallpaper.jpg', + r'{APPDATA}\Mozilla\Firefox\Desktop Background.bmp', + ] +REG_HKCU_LOCATIONS = { + r'Control Panel\Desktop': 'Wallpaper', + r'Software\Microsoft\Internet Explorer\Desktop\General': 'WallpaperSource', + } + +# Init +os.chdir(os.path.dirname(os.path.realpath(__file__))) +sys.path.append(os.getcwd()) +from functions.cleanup import * +init_global_vars() +os.system('title {}: Find user wallpaper(s)'.format(KIT_NAME_FULL)) + +if __name__ == '__main__': + try: + stay_awake() + clear_screen() + + # Build list of wallpaper paths + wall_paths = [s.format(**global_vars['Env']) for s in EXPLORER_LOCATIONS] + for k, v in REG_HKCU_LOCATIONS.items(): + with winreg.OpenKey(HKCU, k) as key: + try: + _path = winreg.QueryValueEx(key, v)[0] + except Exception: + # Meh, ignore + pass + else: + wall_paths.append(_path) + + # Copy files into ClientDir + dest_folder = r'{}\{}\Wallpapers'.format( + global_vars['BackupDir'], + global_vars['Env']['USERNAME'], + ) + os.makedirs(dest_folder, exist_ok=True) + for source_path in wall_paths: + if os.path.exists(source_path): + dest_path = '{}\{}'.format( + dest_folder, + re.sub(r'^.*\\', '', source_path), + ) + dest_path = non_clobber_rename(dest_path) + try: + shutil.copy(source_path, dest_path) + except Exception: + # Meh, ignore + pass + + # Open folder if wallpapers were copied or report error + delete_empty_folders(dest_folder.replace(r'\Wallpaper', '')) + if os.path.exists(dest_folder): + popen_program(['explorer', dest_folder]) + else: + print_warning('No wallpapers found.') + pause('Press Enter to exit...') + + # TODO: DELETEME + print('Wall paths:') + for p in wall_paths: + print(' ', p) + pause('Meh?') + + # Done + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/activation.py b/.bin/Scripts/functions/activation.py index 24436418..52ea8ecd 100644 --- a/.bin/Scripts/functions/activation.py +++ b/.bin/Scripts/functions/activation.py @@ -6,61 +6,68 @@ from borrowed import acpi from functions.common import * from os import environ -# Variables + +# STATIC VARIABLES SLMGR = r'{}\System32\slmgr.vbs'.format(environ.get('SYSTEMROOT')) + def activate_with_bios(): - """Attempt to activate Windows with a key stored in the BIOS.""" - # Code borrowed from https://github.com/aeruder/get_win8key - ##################################################### - #script to query windows 8.x OEM key from PC firmware - #ACPI -> table MSDM -> raw content -> byte offset 56 to end - #ck, 03-Jan-2014 (christian@korneck.de) - ##################################################### - bios_key = None - table = b"MSDM" - if acpi.FindAcpiTable(table) is True: - rawtable = acpi.GetAcpiTable(table) - #http://msdn.microsoft.com/library/windows/hardware/hh673514 - #byte offset 36 from beginning \ - # = Microsoft 'software licensing data structure' \ - # / 36 + 20 bytes offset from beginning = Win Key - bios_key = rawtable[56:len(rawtable)].decode("utf-8") - if bios_key is None: - raise BIOSKeyNotFoundError + """Attempt to activate Windows with a key stored in the BIOS.""" + # Code borrowed from https://github.com/aeruder/get_win8key + ##################################################### + #script to query windows 8.x OEM key from PC firmware + #ACPI -> table MSDM -> raw content -> byte offset 56 to end + #ck, 03-Jan-2014 (christian@korneck.de) + ##################################################### + bios_key = None + table = b"MSDM" + if acpi.FindAcpiTable(table) is True: + rawtable = acpi.GetAcpiTable(table) + #http://msdn.microsoft.com/library/windows/hardware/hh673514 + #byte offset 36 from beginning \ + # = Microsoft 'software licensing data structure' \ + # / 36 + 20 bytes offset from beginning = Win Key + bios_key = rawtable[56:len(rawtable)].decode("utf-8") + if bios_key is None: + raise BIOSKeyNotFoundError - # Install Key - cmd = ['cscript', '//nologo', SLMGR, '/ipk', bios_key] - subprocess.run(cmd, check=False) - sleep(5) + # Install Key + cmd = ['cscript', '//nologo', SLMGR, '/ipk', bios_key] + subprocess.run(cmd, check=False) + sleep(5) - # Attempt activation - cmd = ['cscript', '//nologo', SLMGR, '/ato'] - subprocess.run(cmd, check=False) - sleep(5) + # Attempt activation + cmd = ['cscript', '//nologo', SLMGR, '/ato'] + subprocess.run(cmd, check=False) + sleep(5) + + # Check status + if not windows_is_activated(): + raise Exception('Activation Failed') - # Check status - if not windows_is_activated(): - raise Exception('Activation Failed') def get_activation_string(): - """Get activation status, returns str.""" - act_str = subprocess.run( - ['cscript', '//nologo', SLMGR, '/xpr'], check=False, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - act_str = act_str.stdout.decode() - act_str = act_str.splitlines() - act_str = act_str[1].strip() - return act_str + """Get activation status, returns str.""" + act_str = subprocess.run( + ['cscript', '//nologo', SLMGR, '/xpr'], check=False, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + act_str = act_str.stdout.decode() + act_str = act_str.splitlines() + act_str = act_str[1].strip() + return act_str + def windows_is_activated(): - """Check if Windows is activated via slmgr.vbs and return bool.""" - activation_string = subprocess.run( - ['cscript', '//nologo', SLMGR, '/xpr'], check=False, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - activation_string = activation_string.stdout.decode() + """Check if Windows is activated via slmgr.vbs and return bool.""" + activation_string = subprocess.run( + ['cscript', '//nologo', SLMGR, '/xpr'], check=False, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + activation_string = activation_string.stdout.decode() + + return bool(activation_string and 'permanent' in activation_string) - return bool(activation_string and 'permanent' in activation_string) if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/backup.py b/.bin/Scripts/functions/backup.py index d872c4d0..ad03f822 100644 --- a/.bin/Scripts/functions/backup.py +++ b/.bin/Scripts/functions/backup.py @@ -4,202 +4,216 @@ import ctypes from functions.disk import * + # Regex REGEX_BAD_PATH_NAMES = re.compile( - r'([<>:"/\|\?\*]' - r'|^(CON|PRN|AUX|NUL|COM\d*|LPT\d*)$)' - r'|^\s+' - r'|[\s\.]+$', - re.IGNORECASE) + r'([<>:"/\|\?\*]' + r'|^(CON|PRN|AUX|NUL|COM\d*|LPT\d*)$)' + r'|^\s+' + r'|[\s\.]+$', + re.IGNORECASE) + def backup_partition(disk, par): - """Create a backup image of a partition.""" - if par.get('Image Exists', False) or par['Number'] in disk['Bad Partitions']: - raise GenericAbort + """Create a backup image of a partition.""" + if (par.get('Image Exists', False) + or par['Number'] in disk['Bad Partitions']): + raise GenericAbort + + cmd = [ + global_vars['Tools']['wimlib-imagex'], + 'capture', + '{}:\\'.format(par['Letter']), + par['Image Path'], + par['Image Name'], # Image name + par['Image Name'], # Image description + '--compress=none', + ] + dest_dir = re.sub(r'(.*)\\.*$', r'\1', par['Image Path'], re.IGNORECASE) + os.makedirs(dest_dir, exist_ok=True) + run_program(cmd) - cmd = [ - global_vars['Tools']['wimlib-imagex'], - 'capture', - '{}:\\'.format(par['Letter']), - par['Image Path'], - par['Image Name'], # Image name - par['Image Name'], # Image description - '--compress=none', - ] - dest_dir = re.sub(r'(.*)\\.*$', r'\1', par['Image Path'], re.IGNORECASE) - os.makedirs(dest_dir, exist_ok=True) - run_program(cmd) def fix_path(path): - """Replace invalid filename characters with underscores.""" - local_drive = path[1:2] == ':' - new_path = REGEX_BAD_PATH_NAMES.sub('_', path) - if local_drive: - new_path = '{}:{}'.format(new_path[0:1], new_path[2:]) - return new_path + """Replace invalid filename characters with underscores.""" + local_drive = path[1:2] == ':' + new_path = REGEX_BAD_PATH_NAMES.sub('_', path) + if local_drive: + new_path = '{}:{}'.format(new_path[0:1], new_path[2:]) + return new_path + def get_volume_display_name(mountpoint): - """Get display name from volume mountpoint and label, returns str.""" - name = mountpoint - try: - kernel32 = ctypes.windll.kernel32 - vol_name_buffer = ctypes.create_unicode_buffer(1024) - fs_name_buffer = ctypes.create_unicode_buffer(1024) - serial_number = None - max_component_length = None - file_system_flags = None + """Get display name from volume mountpoint and label, returns str.""" + name = mountpoint + try: + kernel32 = ctypes.windll.kernel32 + vol_name_buffer = ctypes.create_unicode_buffer(1024) + fs_name_buffer = ctypes.create_unicode_buffer(1024) + serial_number = None + max_component_length = None + file_system_flags = None - vol_info = kernel32.GetVolumeInformationW( - ctypes.c_wchar_p(mountpoint), - vol_name_buffer, - ctypes.sizeof(vol_name_buffer), - serial_number, - max_component_length, - file_system_flags, - fs_name_buffer, - ctypes.sizeof(fs_name_buffer) - ) + vol_info = kernel32.GetVolumeInformationW( + ctypes.c_wchar_p(mountpoint), + vol_name_buffer, + ctypes.sizeof(vol_name_buffer), + serial_number, + max_component_length, + file_system_flags, + fs_name_buffer, + ctypes.sizeof(fs_name_buffer) + ) - name = '{} "{}"'.format(name, vol_name_buffer.value) - except: - pass + name = '{} "{}"'.format(name, vol_name_buffer.value) + except: + pass + + return name - return name def prep_disk_for_backup(destination, disk, backup_prefix): - """Gather details about the disk and its partitions. + """Gather details about the disk and its partitions. - This includes partitions that can't be backed up, - whether backups already exist on the BACKUP_SERVER, - partition names/sizes/used space, etc.""" - disk['Clobber Risk'] = [] - width = len(str(len(disk['Partitions']))) + This includes partitions that can't be backed up, + whether backups already exist on the BACKUP_SERVER, + partition names/sizes/used space, etc.""" + disk['Clobber Risk'] = [] + width = len(str(len(disk['Partitions']))) - # Get partition totals - disk['Bad Partitions'] = [par['Number'] for par in disk['Partitions'] - if is_bad_partition(par)] - num_valid_partitions = len(disk['Partitions']) - len(disk['Bad Partitions']) - disk['Valid Partitions'] = num_valid_partitions - if disk['Valid Partitions'] <= 0: - print_error('ERROR: No partitions can be backed up for this disk') - raise GenericAbort + # Get partition totals + disk['Bad Partitions'] = [par['Number'] for par in disk['Partitions'] + if is_bad_partition(par)] + num_valid_partitions = len(disk['Partitions']) - len(disk['Bad Partitions']) + disk['Valid Partitions'] = num_valid_partitions + if disk['Valid Partitions'] <= 0: + print_error('ERROR: No partitions can be backed up for this disk') + raise GenericAbort - # Prep partitions - for par in disk['Partitions']: - display = '{size} {fs}'.format( - num = par['Number'], - width = width, - size = par['Size'], - fs = par['FileSystem']) + # Prep partitions + for par in disk['Partitions']: + display = '{size} {fs}'.format( + num = par['Number'], + width = width, + size = par['Size'], + fs = par['FileSystem']) - if par['Number'] in disk['Bad Partitions']: - # Set display string using partition description & OS type - display = '* {display}\t\t{q}{name}{q}\t{desc} ({os})'.format( - display = display, - q = '"' if par['Name'] != '' else '', - name = par['Name'], - desc = par['Description'], - os = par['OS']) - else: - # Update info for WIM capturing - par['Image Name'] = par['Name'] if par['Name'] else 'Unknown' - if 'IP' in destination: - par['Image Path'] = r'\\{}\{}\{}'.format( - destination['IP'], destination['Share'], backup_prefix) - else: - par['Image Path'] = r'{}:\{}'.format( - destination['Letter'], backup_prefix) - par['Image Path'] += r'\{}_{}.wim'.format( - par['Number'], par['Image Name']) - par['Image Path'] = fix_path(par['Image Path']) + if par['Number'] in disk['Bad Partitions']: + # Set display string using partition description & OS type + display = '* {display}\t\t{q}{name}{q}\t{desc} ({os})'.format( + display = display, + q = '"' if par['Name'] != '' else '', + name = par['Name'], + desc = par['Description'], + os = par['OS']) + else: + # Update info for WIM capturing + par['Image Name'] = par['Name'] if par['Name'] else 'Unknown' + if 'IP' in destination: + par['Image Path'] = r'\\{}\{}\{}'.format( + destination['IP'], destination['Share'], backup_prefix) + else: + par['Image Path'] = r'{}:\{}'.format( + destination['Letter'], backup_prefix) + par['Image Path'] += r'\{}_{}.wim'.format( + par['Number'], par['Image Name']) + par['Image Path'] = fix_path(par['Image Path']) - # Check for existing backups - par['Image Exists'] = os.path.exists(par['Image Path']) - if par['Image Exists']: - disk['Clobber Risk'].append(par['Number']) - display = '+ {}'.format(display) - else: - display = ' {}'.format(display) + # Check for existing backups + par['Image Exists'] = os.path.exists(par['Image Path']) + if par['Image Exists']: + disk['Clobber Risk'].append(par['Number']) + display = '+ {}'.format(display) + else: + display = ' {}'.format(display) - # Append rest of Display String for valid/clobber partitions - display += ' (Used: {used})\t{q}{name}{q}'.format( - used = par['Used Space'], - q = '"' if par['Name'] != '' else '', - name = par['Name']) - # For all partitions - par['Display String'] = display + # Append rest of Display String for valid/clobber partitions + display += ' (Used: {used})\t{q}{name}{q}'.format( + used = par['Used Space'], + q = '"' if par['Name'] != '' else '', + name = par['Name']) + # For all partitions + par['Display String'] = display + + # Set description for bad partitions + warnings = '\n' + if disk['Bad Partitions']: + warnings += '{} * Unsupported filesystem{}\n'.format( + COLORS['YELLOW'], COLORS['CLEAR']) + if disk['Clobber Risk']: + warnings += '{} + Backup exists on {}{}\n'.format( + COLORS['BLUE'], destination['Name'], COLORS['CLEAR']) + if disk['Bad Partitions'] or disk['Clobber Risk']: + warnings += '\n{}Marked partition(s) will NOT be backed up.{}\n'.format( + COLORS['YELLOW'], COLORS['CLEAR']) + disk['Backup Warnings'] = warnings - # Set description for bad partitions - warnings = '\n' - if disk['Bad Partitions']: - warnings += '{} * Unsupported filesystem{}\n'.format( - COLORS['YELLOW'], COLORS['CLEAR']) - if disk['Clobber Risk']: - warnings += '{} + Backup exists on {}{}\n'.format( - COLORS['BLUE'], destination['Name'], COLORS['CLEAR']) - if disk['Bad Partitions'] or disk['Clobber Risk']: - warnings += '\n{}Marked partition(s) will NOT be backed up.{}\n'.format( - COLORS['YELLOW'], COLORS['CLEAR']) - disk['Backup Warnings'] = warnings def select_backup_destination(auto_select=True): - """Select a backup destination from a menu, returns server dict.""" - destinations = [s for s in BACKUP_SERVERS if s['Mounted']] - actions = [ - {'Name': 'Main Menu', 'Letter': 'M'}, - ] + """Select a backup destination from a menu, returns server dict.""" + destinations = [s for s in BACKUP_SERVERS if s['Mounted']] + actions = [ + {'Name': 'Main Menu', 'Letter': 'M'}, + ] - # Add local disks - for d in psutil.disk_partitions(): - if re.search(r'^{}'.format(global_vars['Env']['SYSTEMDRIVE']), d.mountpoint, re.IGNORECASE): - # Skip current OS drive - pass - elif 'fixed' in d.opts: - # Skip DVD, etc - destinations.append({ - 'Name': 'Local Disk - {}'.format( - get_volume_display_name(d.mountpoint)), - 'Letter': re.sub(r'^(\w):\\.*$', r'\1', d.mountpoint), - }) + # Add local disks + for d in psutil.disk_partitions(): + if re.search( + r'^{}'.format(global_vars['Env']['SYSTEMDRIVE']), + d.mountpoint, + re.IGNORECASE): + # Skip current OS drive + pass + elif 'fixed' in d.opts: + # Skip DVD, etc + destinations.append({ + 'Name': 'Local Disk - {}'.format( + get_volume_display_name(d.mountpoint)), + 'Letter': re.sub(r'^(\w):\\.*$', r'\1', d.mountpoint), + }) - # Size check - for dest in destinations: - if 'IP' in dest: - dest['Usage'] = shutil.disk_usage(r'\\{IP}\{Share}'.format(**dest)) - else: - dest['Usage'] = shutil.disk_usage('{}:\\'.format(dest['Letter'])) - dest['Free Space'] = human_readable_size(dest['Usage'].free) - dest['Display Name'] = '{Name} ({Free Space} available)'.format(**dest) - - # Bail - if not destinations: - print_warning('No backup destinations found.') - raise GenericAbort - - # Skip menu? - if len(destinations) == 1 and auto_select: - return destinations[0] - - selection = menu_select( - title = 'Where are we backing up to?', - main_entries = destinations, - action_entries = actions) - if selection == 'M': - raise GenericAbort + # Size check + for dest in destinations: + if 'IP' in dest: + dest['Usage'] = shutil.disk_usage(r'\\{IP}\{Share}'.format(**dest)) else: - return destinations[int(selection)-1] + dest['Usage'] = shutil.disk_usage('{}:\\'.format(dest['Letter'])) + dest['Free Space'] = human_readable_size(dest['Usage'].free) + dest['Display Name'] = '{Name} ({Free Space} available)'.format(**dest) + + # Bail + if not destinations: + print_warning('No backup destinations found.') + raise GenericAbort + + # Skip menu? + if len(destinations) == 1 and auto_select: + return destinations[0] + + selection = menu_select( + title = 'Where are we backing up to?', + main_entries = destinations, + action_entries = actions) + if selection == 'M': + raise GenericAbort + else: + return destinations[int(selection)-1] + def verify_wim_backup(partition): - """Verify WIM integrity.""" - if not os.path.exists(partition['Image Path']): - raise PathNotFoundError - cmd = [ - global_vars['Tools']['wimlib-imagex'], - 'verify', - partition['Image Path'], - '--nocheck', - ] - run_program(cmd) + """Verify WIM integrity.""" + if not os.path.exists(partition['Image Path']): + raise PathNotFoundError + cmd = [ + global_vars['Tools']['wimlib-imagex'], + 'verify', + partition['Image Path'], + '--nocheck', + ] + run_program(cmd) + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/browsers.py b/.bin/Scripts/functions/browsers.py index 143d018d..34c0252f 100644 --- a/.bin/Scripts/functions/browsers.py +++ b/.bin/Scripts/functions/browsers.py @@ -1,48 +1,32 @@ # Wizard Kit: Functions - Browsers from functions.common import * - from operator import itemgetter + # Define other_results for later try_and_print browser_data = {} other_results = { - 'Error': { - 'MultipleInstallationsError': 'Multiple installations detected', - }, - 'Warning': { - 'NotInstalledError': 'Not installed', - 'NoProfilesError': 'No profiles found', - } + 'Error': { + 'MultipleInstallationsError': 'Multiple installations detected', + }, + 'Warning': { + 'NotInstalledError': 'Not installed', + 'NoProfilesError': 'No profiles found', } + } -# Regex -REGEX_BACKUP = re.compile( - r'\.\w*bak.*', - re.IGNORECASE) -REGEX_CHROMIUM_PROFILE = re.compile( - r'^(Default|Profile)', - re.IGNORECASE) -REGEX_CHROMIUM_ITEMS = re.compile( - r'^(Bookmarks|Cookies|Favicons|Google Profile' - r'|History|Login Data|Top Sites|TransportSecurity' - r'|Visited Links|Web Data)', - re.IGNORECASE) -REGEX_MOZILLA = re.compile( - r'^(bookmarkbackups|(cookies|formhistory|places).sqlite' - r'|key3.db|logins.json|persdict.dat)$', - re.IGNORECASE) # STATIC VARIABLES DEFAULT_HOMEPAGE = 'https://www.google.com/' IE_GALLERY = 'https://www.microsoft.com/en-us/iegallery' MOZILLA_PREFS = { - 'browser.search.defaultenginename': '"Google"', - 'browser.search.defaultenginename.US': '"Google"', - 'browser.search.geoSpecificDefaults': 'false', - 'browser.startup.homepage': '"{}"'.format(DEFAULT_HOMEPAGE), - 'extensions.ui.lastCategory': '"addons://list/extension"', - } + 'browser.search.defaultenginename': '"Google"', + 'browser.search.defaultenginename.US': '"Google"', + 'browser.search.geoSpecificDefaults': 'false', + 'browser.startup.homepage': '"{}"'.format(DEFAULT_HOMEPAGE), + 'extensions.ui.lastCategory': '"addons://list/extension"', + } UBO_CHROME = 'https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=en' UBO_CHROME_REG = r'Software\Wow6432Node\Google\Chrome\Extensions\cjpalhdlnbpafiamejdnhcphjbkeiagm' UBO_EXTRA_CHROME = 'https://chrome.google.com/webstore/detail/ublock-origin-extra/pgdnlhfefecpicbbihgmbmffkjpaplco?hl=en' @@ -53,456 +37,505 @@ UBO_MOZILLA_REG = r'Software\Mozilla\Firefox\Extensions' UBO_MOZILLA_REG_NAME = 'uBlock0@raymondhill.net' UBO_OPERA = 'https://addons.opera.com/en/extensions/details/ublock/?display=en' SUPPORTED_BROWSERS = { - 'Internet Explorer': { - 'base': 'ie', - 'exe_name': 'iexplore.exe', - 'rel_install_path': 'Internet Explorer', - 'user_data_path': r'{USERPROFILE}\Favorites', - }, - 'Google Chrome': { - 'base': 'chromium', - 'exe_name': 'chrome.exe', - 'rel_install_path': r'Google\Chrome\Application', - 'user_data_path': r'{LOCALAPPDATA}\Google\Chrome\User Data', - }, - 'Google Chrome Canary': { - 'base': 'chromium', - 'exe_name': 'chrome.exe', - 'rel_install_path': r'Google\Chrome SxS\Application', - 'user_data_path': r'{LOCALAPPDATA}\Google\Chrome SxS\User Data', - }, - 'Mozilla Firefox': { - 'base': 'mozilla', - 'exe_name': 'firefox.exe', - 'rel_install_path': 'Mozilla Firefox', - 'user_data_path': r'{APPDATA}\Mozilla\Firefox\Profiles', - }, - 'Mozilla Firefox Dev': { - 'base': 'mozilla', - 'exe_name': 'firefox.exe', - 'rel_install_path': 'Firefox Developer Edition', - 'user_data_path': r'{APPDATA}\Mozilla\Firefox\Profiles', - }, - 'Opera': { - 'base': 'chromium', - 'exe_name': 'launcher.exe', - 'rel_install_path': 'Opera', - 'user_data_path': r'{APPDATA}\Opera Software\Opera Stable', - }, - 'Opera Beta': { - 'base': 'chromium', - 'exe_name': 'launcher.exe', - 'rel_install_path': 'Opera beta', - 'user_data_path': r'{APPDATA}\Opera Software\Opera Next', - }, - 'Opera Dev': { - 'base': 'chromium', - 'exe_name': 'launcher.exe', - 'rel_install_path': 'Opera developer', - 'user_data_path': r'{APPDATA}\Opera Software\Opera Developer', - }, - } + 'Internet Explorer': { + 'base': 'ie', + 'exe_name': 'iexplore.exe', + 'rel_install_path': 'Internet Explorer', + 'user_data_path': r'{USERPROFILE}\Favorites', + }, + 'Google Chrome': { + 'base': 'chromium', + 'exe_name': 'chrome.exe', + 'rel_install_path': r'Google\Chrome\Application', + 'user_data_path': r'{LOCALAPPDATA}\Google\Chrome\User Data', + }, + 'Google Chrome Canary': { + 'base': 'chromium', + 'exe_name': 'chrome.exe', + 'rel_install_path': r'Google\Chrome SxS\Application', + 'user_data_path': r'{LOCALAPPDATA}\Google\Chrome SxS\User Data', + }, + 'Mozilla Firefox': { + 'base': 'mozilla', + 'exe_name': 'firefox.exe', + 'rel_install_path': 'Mozilla Firefox', + 'user_data_path': r'{APPDATA}\Mozilla\Firefox\Profiles', + }, + 'Mozilla Firefox Dev': { + 'base': 'mozilla', + 'exe_name': 'firefox.exe', + 'rel_install_path': 'Firefox Developer Edition', + 'user_data_path': r'{APPDATA}\Mozilla\Firefox\Profiles', + }, + 'Opera': { + 'base': 'chromium', + 'exe_name': 'launcher.exe', + 'rel_install_path': 'Opera', + 'user_data_path': r'{APPDATA}\Opera Software\Opera Stable', + }, + 'Opera Beta': { + 'base': 'chromium', + 'exe_name': 'launcher.exe', + 'rel_install_path': 'Opera beta', + 'user_data_path': r'{APPDATA}\Opera Software\Opera Next', + }, + 'Opera Dev': { + 'base': 'chromium', + 'exe_name': 'launcher.exe', + 'rel_install_path': 'Opera developer', + 'user_data_path': r'{APPDATA}\Opera Software\Opera Developer', + }, + } + + +# Regex +REGEX_BACKUP = re.compile( + r'\.\w*bak.*', + re.IGNORECASE) +REGEX_CHROMIUM_PROFILE = re.compile( + r'^(Default|Profile)', + re.IGNORECASE) +REGEX_CHROMIUM_ITEMS = re.compile( + r'^(Bookmarks|Cookies|Favicons|Google Profile' + r'|History|Login Data|Top Sites|TransportSecurity' + r'|Visited Links|Web Data)', + re.IGNORECASE) +REGEX_MOZILLA = re.compile( + r'^(bookmarkbackups|(cookies|formhistory|places).sqlite' + r'|key3.db|logins.json|persdict.dat)$', + re.IGNORECASE) + def archive_all_users(): - """Create backups for all browsers for all users.""" - users_root = r'{}\Users'.format(global_vars['Env']['SYSTEMDRIVE']) - user_envs = [] + """Create backups for all browsers for all users.""" + users_root = r'{}\Users'.format(global_vars['Env']['SYSTEMDRIVE']) + user_envs = [] - # Build list of valid users - for user_name in os.listdir(users_root): - valid_user = True - if user_name in ('Default', 'Default User'): - # Skip default users - continue - user_path = os.path.join(users_root, user_name) - appdata_local = os.path.join(user_path, r'AppData\Local') - appdata_roaming = os.path.join(user_path, r'AppData\Roaming') - valid_user &= os.path.exists(appdata_local) - valid_user &= os.path.exists(appdata_roaming) - if valid_user: - user_envs.append({ - 'USERNAME': user_name, - 'USERPROFILE': user_path, - 'APPDATA': appdata_roaming, - 'LOCALAPPDATA': appdata_local}) + # Build list of valid users + for user_name in os.listdir(users_root): + valid_user = True + if user_name in ('Default', 'Default User'): + # Skip default users + continue + user_path = os.path.join(users_root, user_name) + appdata_local = os.path.join(user_path, r'AppData\Local') + appdata_roaming = os.path.join(user_path, r'AppData\Roaming') + valid_user &= os.path.exists(appdata_local) + valid_user &= os.path.exists(appdata_roaming) + if valid_user: + user_envs.append({ + 'USERNAME': user_name, + 'USERPROFILE': user_path, + 'APPDATA': appdata_roaming, + 'LOCALAPPDATA': appdata_local}) - # Backup browsers for all valid users - print_info('Backing up browsers') - for fake_env in sorted(user_envs, key=itemgetter('USERPROFILE')): - print_standard(' {}'.format(fake_env['USERNAME'])) - for b_k, b_v in sorted(SUPPORTED_BROWSERS.items()): - if b_k == 'Mozilla Firefox Dev': - continue - source_path = b_v['user_data_path'].format(**fake_env) - if not os.path.exists(source_path): - continue - source_items = source_path + '*' - archive_path = r'{BackupDir}\Browsers ({USERNAME})\{Date}'.format( - **global_vars, **fake_env) - os.makedirs(archive_path, exist_ok=True) - archive_path += r'\{}.7z'.format(b_k) - cmd = [ - global_vars['Tools']['SevenZip'], - 'a', '-aoa', '-bso0', '-bse0', '-mx=1', - archive_path, source_items] - try_and_print(message='{}...'.format(b_k), - function=run_program, cmd=cmd) - print_standard(' ') - -def archive_browser(name): - """Create backup of Browser saved in the BackupDir.""" - source = '{}*'.format(browser_data[name]['user_data_path']) - dest = r'{BackupDir}\Browsers ({USERNAME})\{Date}'.format( - **global_vars, **global_vars['Env']) - archive = r'{}\{}.7z'.format(dest, name) - os.makedirs(dest, exist_ok=True) - cmd = [ + # Backup browsers for all valid users + print_info('Backing up browsers') + for fake_env in sorted(user_envs, key=itemgetter('USERPROFILE')): + print_standard(' {}'.format(fake_env['USERNAME'])) + for b_k, b_v in sorted(SUPPORTED_BROWSERS.items()): + if b_k == 'Mozilla Firefox Dev': + continue + source_path = b_v['user_data_path'].format(**fake_env) + if not os.path.exists(source_path): + continue + source_items = source_path + '*' + archive_path = r'{BackupDir}\Browsers ({USERNAME})\{Date}'.format( + **global_vars, **fake_env) + os.makedirs(archive_path, exist_ok=True) + archive_path += r'\{}.7z'.format(b_k) + cmd = [ global_vars['Tools']['SevenZip'], 'a', '-aoa', '-bso0', '-bse0', '-mx=1', - '-mhe=on', '-p{}'.format(ARCHIVE_PASSWORD), - archive, source] - run_program(cmd) + archive_path, source_items] + try_and_print(message='{}...'.format(b_k), + function=run_program, cmd=cmd) + print_standard(' ') + + +def archive_browser(name): + """Create backup of Browser saved in the BackupDir.""" + source = '{}*'.format(browser_data[name]['user_data_path']) + dest = r'{BackupDir}\Browsers ({USERNAME})\{Date}'.format( + **global_vars, **global_vars['Env']) + archive = r'{}\{}.7z'.format(dest, name) + os.makedirs(dest, exist_ok=True) + cmd = [ + global_vars['Tools']['SevenZip'], + 'a', '-aoa', '-bso0', '-bse0', '-mx=1', + '-mhe=on', '-p{}'.format(ARCHIVE_PASSWORD), + archive, source] + run_program(cmd) + def backup_browsers(): - """Create backup of all detected browser profiles.""" - for name in [k for k, v in sorted(browser_data.items()) if v['profiles']]: - try_and_print(message='{}...'.format(name), - function=archive_browser, name=name) + """Create backup of all detected browser profiles.""" + for name in [k for k, v in sorted(browser_data.items()) if v['profiles']]: + try_and_print(message='{}...'.format(name), + function=archive_browser, name=name) + def clean_chromium_profile(profile): - """Renames profile, creates a new folder, and copies the user data to it.""" - if profile is None: - raise Exception - backup_path = '{path}_{Date}.bak'.format( - path=profile['path'], **global_vars) - backup_path = non_clobber_rename(backup_path) - shutil.move(profile['path'], backup_path) - os.makedirs(profile['path'], exist_ok=True) + """Recreate profile with only the essential user data. + + This is done by renaming the existing profile, creating a new folder + with the original name, then copying the essential files from the + backup folder. This way the original state is preserved in case + something goes wrong. + """ + if profile is None: + raise Exception + backup_path = '{path}_{Date}.bak'.format( + path=profile['path'], **global_vars) + backup_path = non_clobber_rename(backup_path) + shutil.move(profile['path'], backup_path) + os.makedirs(profile['path'], exist_ok=True) + + # Restore essential files from backup_path + for entry in os.scandir(backup_path): + if REGEX_CHROMIUM_ITEMS.search(entry.name): + shutil.copy(entry.path, r'{}\{}'.format( + profile['path'], entry.name)) - # Restore essential files from backup_path - for entry in os.scandir(backup_path): - if REGEX_CHROMIUM_ITEMS.search(entry.name): - shutil.copy(entry.path, r'{}\{}'.format( - profile['path'], entry.name)) def clean_internet_explorer(**kwargs): - """Uses the built-in function to reset IE and sets the homepage. + """Uses the built-in function to reset IE and sets the homepage. - NOTE: kwargs set but unused as a workaround.""" - kill_process('iexplore.exe') - run_program(['rundll32.exe', 'inetcpl.cpl,ResetIEtoDefaults'], check=False) - key = r'Software\Microsoft\Internet Explorer\Main' + NOTE: kwargs set but unused as a workaround.""" + kill_process('iexplore.exe') + run_program(['rundll32.exe', 'inetcpl.cpl,ResetIEtoDefaults'], check=False) + key = r'Software\Microsoft\Internet Explorer\Main' + + # Set homepage + with winreg.OpenKey(HKCU, key, access=winreg.KEY_WRITE) as _key: + winreg.SetValueEx(_key, 'Start Page', 0, + winreg.REG_SZ, DEFAULT_HOMEPAGE) + try: + winreg.DeleteValue(_key, 'Secondary Start Pages') + except FileNotFoundError: + pass - # Set homepage - with winreg.OpenKey(HKCU, key, access=winreg.KEY_WRITE) as _key: - winreg.SetValueEx(_key, 'Start Page', 0, - winreg.REG_SZ, DEFAULT_HOMEPAGE) - try: - winreg.DeleteValue(_key, 'Secondary Start Pages') - except FileNotFoundError: - pass def clean_mozilla_profile(profile): - """Renames profile, creates a new folder, and copies the user data to it.""" - if profile is None: - raise Exception - backup_path = '{path}_{Date}.bak'.format( - path=profile['path'], **global_vars) - backup_path = non_clobber_rename(backup_path) - shutil.move(profile['path'], backup_path) - homepages = [] - os.makedirs(profile['path'], exist_ok=True) + """Recreate profile with only the essential user data. - # Restore essential files from backup_path - for entry in os.scandir(backup_path): - if REGEX_MOZILLA.search(entry.name): - if entry.is_dir(): - shutil.copytree(entry.path, r'{}\{}'.format( - profile['path'], entry.name)) - else: - shutil.copy(entry.path, r'{}\{}'.format( - profile['path'], entry.name)) + This is done by renaming the existing profile, creating a new folder + with the original name, then copying the essential files from the + backup folder. This way the original state is preserved in case + something goes wrong. + """ + if profile is None: + raise Exception + backup_path = '{path}_{Date}.bak'.format( + path=profile['path'], **global_vars) + backup_path = non_clobber_rename(backup_path) + shutil.move(profile['path'], backup_path) + homepages = [] + os.makedirs(profile['path'], exist_ok=True) + + # Restore essential files from backup_path + for entry in os.scandir(backup_path): + if REGEX_MOZILLA.search(entry.name): + if entry.is_dir(): + shutil.copytree(entry.path, r'{}\{}'.format( + profile['path'], entry.name)) + else: + shutil.copy(entry.path, r'{}\{}'.format( + profile['path'], entry.name)) + + # Set profile defaults + with open(r'{path}\prefs.js'.format(**profile), 'a', encoding='ascii') as f: + for k, v in MOZILLA_PREFS.items(): + f.write('user_pref("{}", {});\n'.format(k, v)) - # Set profile defaults - with open(r'{path}\prefs.js'.format(**profile), 'a', encoding='ascii') as f: - for k, v in MOZILLA_PREFS.items(): - f.write('user_pref("{}", {});\n'.format(k, v)) def get_browser_details(name): - """Get installation status and profile details for all supported browsers.""" - browser = SUPPORTED_BROWSERS[name].copy() + """Get installation and profile details for all supported browsers.""" + browser = SUPPORTED_BROWSERS[name].copy() - # Update user_data_path - browser['user_data_path'] = browser['user_data_path'].format( - **global_vars['Env']) + # Update user_data_path + browser['user_data_path'] = browser['user_data_path'].format( + **global_vars['Env']) - # Find executable (if multiple files are found, the last one is used) - exe_path = None - num_installs = 0 - for install_path in ['LOCALAPPDATA', 'PROGRAMFILES(X86)', 'PROGRAMFILES']: - test_path = r'{install_path}\{rel_install_path}\{exe_name}'.format( - install_path = global_vars['Env'].get(install_path, ''), - **browser) - if os.path.exists(test_path): - num_installs += 1 - exe_path = test_path + # Find executable (if multiple files are found, the last one is used) + exe_path = None + num_installs = 0 + for install_path in ['LOCALAPPDATA', 'PROGRAMFILES(X86)', 'PROGRAMFILES']: + test_path = r'{install_path}\{rel_install_path}\{exe_name}'.format( + install_path = global_vars['Env'].get(install_path, ''), + **browser) + if os.path.exists(test_path): + num_installs += 1 + exe_path = test_path - # Find profile(s) - profiles = [] - if browser['base'] == 'ie': - profiles.append({'name': 'Default', 'path': None}) - elif 'Google Chrome' in name: - profiles.extend( - get_chromium_profiles( - search_path=browser['user_data_path'])) - elif browser['base'] == 'mozilla': - dev = 'Dev' in name - profiles.extend( - get_mozilla_profiles( - search_path=browser['user_data_path'], dev=dev)) - if exe_path and not dev and len(profiles) == 0: - # e.g. If Firefox is installed but no profiles were found. - ## Rename profiles.ini and create a new default profile - profiles_ini_path = browser['user_data_path'].replace( - 'Profiles', 'profiles.ini') - if os.path.exists(profiles_ini_path): - backup_path = '{path}_{Date}.bak'.format( - path=profiles_ini_path, **global_vars) - backup_path = non_clobber_rename(backup_path) - shutil.move(profiles_ini_path, backup_path) - run_program([exe_path, '-createprofile', 'default'], check=False) - profiles.extend( - get_mozilla_profiles( - search_path=browser['user_data_path'], dev=dev)) + # Find profile(s) + profiles = [] + if browser['base'] == 'ie': + profiles.append({'name': 'Default', 'path': None}) + elif 'Google Chrome' in name: + profiles.extend( + get_chromium_profiles( + search_path=browser['user_data_path'])) + elif browser['base'] == 'mozilla': + dev = 'Dev' in name + profiles.extend( + get_mozilla_profiles( + search_path=browser['user_data_path'], dev=dev)) + if exe_path and not dev and len(profiles) == 0: + # e.g. If Firefox is installed but no profiles were found. + ## Rename profiles.ini and create a new default profile + profiles_ini_path = browser['user_data_path'].replace( + 'Profiles', 'profiles.ini') + if os.path.exists(profiles_ini_path): + backup_path = '{path}_{Date}.bak'.format( + path=profiles_ini_path, **global_vars) + backup_path = non_clobber_rename(backup_path) + shutil.move(profiles_ini_path, backup_path) + run_program([exe_path, '-createprofile', 'default'], check=False) + profiles.extend( + get_mozilla_profiles( + search_path=browser['user_data_path'], dev=dev)) - elif 'Opera' in name: - if os.path.exists(browser['user_data_path']): - profiles.append( - {'name': 'Default', 'path': browser['user_data_path']}) + elif 'Opera' in name: + if os.path.exists(browser['user_data_path']): + profiles.append( + {'name': 'Default', 'path': browser['user_data_path']}) - # Get homepages - if browser['base'] == 'ie': - # IE is set to only have one profile above - profiles[0]['homepages'] = get_ie_homepages() - elif browser['base'] == 'mozilla': - for profile in profiles: - prefs_path = r'{path}\prefs.js'.format(**profile) - profile['homepages'] = get_mozilla_homepages(prefs_path=prefs_path) + # Get homepages + if browser['base'] == 'ie': + # IE is set to only have one profile above + profiles[0]['homepages'] = get_ie_homepages() + elif browser['base'] == 'mozilla': + for profile in profiles: + prefs_path = r'{path}\prefs.js'.format(**profile) + profile['homepages'] = get_mozilla_homepages(prefs_path=prefs_path) - # Add to browser_data - browser_data[name] = browser - browser_data[name].update({ - 'exe_path': exe_path, - 'profiles': profiles, - }) + # Add to browser_data + browser_data[name] = browser + browser_data[name].update({ + 'exe_path': exe_path, + 'profiles': profiles, + }) + + # Raise installation warnings (if any) + if num_installs == 0: + raise NotInstalledError + elif num_installs > 1 and browser['base'] != 'ie': + raise MultipleInstallationsError - # Raise installation warnings (if any) - if num_installs == 0: - raise NotInstalledError - elif num_installs > 1 and browser['base'] != 'ie': - raise MultipleInstallationsError def get_chromium_profiles(search_path): - """Find any chromium-style profiles and return as a list of dicts.""" - profiles = [] - try: - for entry in os.scandir(search_path): - if entry.is_dir() and REGEX_CHROMIUM_PROFILE.search(entry.name): - profiles.append(entry) - REGEX_PROFILE_BACKUP = r'\.\w+bak.*' - profiles = [p for p in profiles if not REGEX_BACKUP.search(p.name)] - # Convert os.DirEntries to dicts - profiles = [{'name': p.name, 'path': p.path} for p in profiles] - except Exception: - pass + """Find any chromium-style profiles and return as a list of dicts.""" + profiles = [] + try: + for entry in os.scandir(search_path): + if entry.is_dir() and REGEX_CHROMIUM_PROFILE.search(entry.name): + profiles.append(entry) + REGEX_PROFILE_BACKUP = r'\.\w+bak.*' + profiles = [p for p in profiles if not REGEX_BACKUP.search(p.name)] + # Convert os.DirEntries to dicts + profiles = [{'name': p.name, 'path': p.path} for p in profiles] + except Exception: + pass + + return profiles - return profiles def get_ie_homepages(): - """Read homepages from the registry and return as a list.""" - homepages = [] - main_page = '' - extra_pages = [] - key = r'Software\Microsoft\Internet Explorer\Main' - with winreg.OpenKey(HKCU, key) as _key: - try: - main_page = winreg.QueryValueEx(_key, 'Start Page')[0] - except FileNotFoundError: - pass - try: - extra_pages = winreg.QueryValueEx(_key, 'Secondary Start Pages')[0] - except FileNotFoundError: - pass - if main_page != '': - homepages.append(main_page) - if len(extra_pages) > 0: - homepages.extend(extra_pages) + """Read homepages from the registry and return as a list.""" + homepages = [] + main_page = '' + extra_pages = [] + key = r'Software\Microsoft\Internet Explorer\Main' + with winreg.OpenKey(HKCU, key) as _key: + try: + main_page = winreg.QueryValueEx(_key, 'Start Page')[0] + except FileNotFoundError: + pass + try: + extra_pages = winreg.QueryValueEx(_key, 'Secondary Start Pages')[0] + except FileNotFoundError: + pass + if main_page != '': + homepages.append(main_page) + if len(extra_pages) > 0: + homepages.extend(extra_pages) + + # Remove all curly braces + homepages = [h.replace('{', '').replace('}', '') for h in homepages] + return homepages - # Remove all curly braces - homepages = [h.replace('{', '').replace('}', '') for h in homepages] - return homepages def get_mozilla_homepages(prefs_path): - """Read homepages from prefs.js and return as a list.""" - homepages = [] - try: - with open(prefs_path, 'r') as f: - search = re.search( - r'browser\.startup\.homepage", "([^"]*)"', - f.read(), re.IGNORECASE) - if search: - homepages = search.group(1).split('|') - except Exception: - pass + """Read homepages from prefs.js and return as a list.""" + homepages = [] + try: + with open(prefs_path, 'r') as f: + search = re.search( + r'browser\.startup\.homepage", "([^"]*)"', + f.read(), re.IGNORECASE) + if search: + homepages = search.group(1).split('|') + except Exception: + pass + + return homepages - return homepages def get_mozilla_profiles(search_path, dev=False): - """Find any mozilla-style profiles and return as a list of dicts.""" - profiles = [] - try: - for entry in os.scandir(search_path): - if entry.is_dir(): - if 'dev-edition' in entry.name: - # NOTE: Not always present which can lead - # to Dev profiles being marked as non-Dev - ## NOTE 2: It is possible that a non-Dev profile - ## to be created with 'dev-edition' in the name. - ## (It wouldn't make sense, but possible) - if dev: - profiles.append(entry) - elif not dev: - profiles.append(entry) - profiles = [p for p in profiles if not REGEX_BACKUP.search(p.name)] - # Convert os.DirEntries to dicts - profiles = [{'name': p.name, 'path': p.path} for p in profiles] - except Exception: - pass + """Find any mozilla-style profiles and return as a list of dicts.""" + profiles = [] + try: + for entry in os.scandir(search_path): + if entry.is_dir(): + if 'dev-edition' in entry.name: + # NOTE: Not always present which can lead + # to Dev profiles being marked as non-Dev + ## NOTE 2: It is possible that a non-Dev profile + ## to be created with 'dev-edition' in the name. + ## (It wouldn't make sense, but possible) + if dev: + profiles.append(entry) + elif not dev: + profiles.append(entry) + profiles = [p for p in profiles if not REGEX_BACKUP.search(p.name)] + # Convert os.DirEntries to dicts + profiles = [{'name': p.name, 'path': p.path} for p in profiles] + except Exception: + pass + + return profiles - return profiles def install_adblock(indent=8, width=32, just_firefox=False): - """Install adblock for all supported browsers.""" - for browser in sorted(browser_data): - if just_firefox and browser_data[browser]['base'] != 'mozilla': - continue - exe_path = browser_data[browser].get('exe_path', None) - function=run_program - if not exe_path: - if browser_data[browser]['profiles']: - print_standard( - '{indent}{browser:<{width}}'.format( - indent=' '*indent, width=width, browser=browser+'...'), - end='', flush=True) - print_warning('Profile(s) detected but browser not installed', - timestamp=False) - else: - # Only warn if profile(s) are detected. - pass + """Install adblock for all supported browsers.""" + for browser in sorted(browser_data): + if just_firefox and browser_data[browser]['base'] != 'mozilla': + continue + exe_path = browser_data[browser].get('exe_path', None) + function=run_program + if not exe_path: + if browser_data[browser]['profiles']: + print_standard( + '{indent}{browser:<{width}}'.format( + indent=' '*indent, width=width, browser=browser+'...'), + end='', flush=True) + print_warning('Profile(s) detected but browser not installed', + timestamp=False) + else: + # Only warn if profile(s) are detected. + pass + else: + # Set urls to open + urls = [] + if browser_data[browser]['base'] == 'chromium': + if browser == 'Google Chrome': + # Check for system exensions + try: + winreg.QueryValue(HKLM, UBO_CHROME_REG) + except FileNotFoundError: + urls.append(UBO_CHROME) + try: + winreg.QueryValue(HKLM, UBO_EXTRA_CHROME_REG) + except FileNotFoundError: + urls.append(UBO_EXTRA_CHROME) + + if len(urls) == 0: + urls = ['chrome://extensions'] + elif 'Opera' in browser: + urls.append(UBO_OPERA) else: - # Set urls to open - urls = [] - if browser_data[browser]['base'] == 'chromium': - if browser == 'Google Chrome': - # Check for system exensions - try: - winreg.QueryValue(HKLM, UBO_CHROME_REG) - except FileNotFoundError: - urls.append(UBO_CHROME) - try: - winreg.QueryValue(HKLM, UBO_EXTRA_CHROME_REG) - except FileNotFoundError: - urls.append(UBO_EXTRA_CHROME) + urls.append(UBO_CHROME) + urls.append(UBO_EXTRA_CHROME) - if len(urls) == 0: - urls = ['chrome://extensions'] - elif 'Opera' in browser: - urls.append(UBO_OPERA) - else: - urls.append(UBO_CHROME) - urls.append(UBO_EXTRA_CHROME) + elif browser_data[browser]['base'] == 'mozilla': + # Check for system extensions + try: + with winreg.OpenKey(HKLM, UBO_MOZILLA_REG) as key: + winreg.QueryValueEx(key, UBO_MOZILLA_REG_NAME) + except FileNotFoundError: + urls = [UBO_MOZILLA] + else: + if os.path.exists(UBO_MOZZILA_PATH): + urls = ['about:addons'] + else: + urls = [UBO_MOZILLA] - elif browser_data[browser]['base'] == 'mozilla': - # Check for system extensions - try: - with winreg.OpenKey(HKLM, UBO_MOZILLA_REG) as key: - winreg.QueryValueEx(key, UBO_MOZILLA_REG_NAME) - except FileNotFoundError: - urls = [UBO_MOZILLA] - else: - if os.path.exists(UBO_MOZZILA_PATH): - urls = ['about:addons'] - else: - urls = [UBO_MOZILLA] + elif browser_data[browser]['base'] == 'ie': + urls.append(IE_GALLERY) + function=popen_program - elif browser_data[browser]['base'] == 'ie': - urls.append(IE_GALLERY) - function=popen_program + # By using check=False we're skipping any return codes so + # it should only fail if the program can't be run + # (or can't be found). + # In other words, this isn't tracking the addon/extension's + # installation status. + try_and_print(message='{}...'.format(browser), + indent=indent, width=width, + cs='Done', function=function, + cmd=[exe_path, *urls], check=False) - # By using check=False we're skipping any return codes so - # it should only fail if the program can't be run - # (or can't be found). - # In other words, this isn't tracking the addon/extension's - # installation status. - try_and_print(message='{}...'.format(browser), - indent=indent, width=width, - cs='Done', function=function, - cmd=[exe_path, *urls], check=False) def list_homepages(indent=8, width=32): - """List current homepages for reference.""" + """List current homepages for reference.""" + browser_list = [k for k, v in sorted(browser_data.items()) if v['exe_path']] + for browser in browser_list: + # Skip Chromium-based browsers + if browser_data[browser]['base'] == 'chromium': + print_info( + '{indent}{browser:<{width}}'.format( + indent=' '*indent, width=width, browser=browser+'...'), + end='', flush=True) + print_warning('Not implemented', timestamp=False) + continue - for browser in [k for k, v in sorted(browser_data.items()) if v['exe_path']]: - # Skip Chromium-based browsers - if browser_data[browser]['base'] == 'chromium': - print_info( - '{indent}{browser:<{width}}'.format( - indent=' '*indent, width=width, browser=browser+'...'), - end='', flush=True) - print_warning('Not implemented', timestamp=False) - continue + # All other browsers + print_info('{indent}{browser:<{width}}'.format( + indent=' '*indent, width=width, browser=browser+'...')) + for profile in browser_data[browser].get('profiles', []): + name = profile.get('name', '?') + homepages = profile.get('homepages', []) + if len(homepages) == 0: + print_standard( + '{indent}{name:<{width}}'.format( + indent=' '*indent, width=width, name=name), + end='', flush=True) + print_warning('None found', timestamp=False) + else: + for page in homepages: + print_standard('{indent}{name:<{width}}{page}'.format( + indent=' '*indent, width=width, name=name, page=page)) - # All other browsers - print_info('{indent}{browser:<{width}}'.format( - indent=' '*indent, width=width, browser=browser+'...')) - for profile in browser_data[browser].get('profiles', []): - name = profile.get('name', '?') - homepages = profile.get('homepages', []) - if len(homepages) == 0: - print_standard( - '{indent}{name:<{width}}'.format( - indent=' '*indent, width=width, name=name), - end='', flush=True) - print_warning('None found', timestamp=False) - else: - for page in homepages: - print_standard('{indent}{name:<{width}}{page}'.format( - indent=' '*indent, width=width, name=name, page=page)) def reset_browsers(indent=8, width=32): - """Reset all detected browsers to safe defaults.""" - for browser in [k for k, v in sorted(browser_data.items()) if v['profiles']]: - print_info('{indent}{name}'.format(indent=' '*indent, name=browser)) - for profile in browser_data[browser]['profiles']: - if browser_data[browser]['base'] == 'chromium': - function = clean_chromium_profile - elif browser_data[browser]['base'] == 'ie': - function = clean_internet_explorer - elif browser_data[browser]['base'] == 'mozilla': - function = clean_mozilla_profile - try_and_print( - message='{}...'.format(profile['name']), - indent=indent, width=width, function=function, - other_results=other_results, profile=profile) + """Reset all detected browsers to safe defaults.""" + browser_list = [k for k, v in sorted(browser_data.items()) if v['profiles']] + for browser in browser_list: + print_info('{indent}{name}'.format(indent=' '*indent, name=browser)) + for profile in browser_data[browser]['profiles']: + if browser_data[browser]['base'] == 'chromium': + function = clean_chromium_profile + elif browser_data[browser]['base'] == 'ie': + function = clean_internet_explorer + elif browser_data[browser]['base'] == 'mozilla': + function = clean_mozilla_profile + try_and_print( + message='{}...'.format(profile['name']), + indent=indent, width=width, function=function, + other_results=other_results, profile=profile) + def scan_for_browsers(just_firefox=False): - """Scan system for any supported browsers.""" - for name, details in sorted(SUPPORTED_BROWSERS.items()): - if just_firefox and details['base'] != 'mozilla': - continue - try_and_print(message='{}...'.format(name), - function=get_browser_details, cs='Detected', - other_results=other_results, name=name) + """Scan system for any supported browsers.""" + for name, details in sorted(SUPPORTED_BROWSERS.items()): + if just_firefox and details['base'] != 'mozilla': + continue + try_and_print(message='{}...'.format(name), + function=get_browser_details, cs='Detected', + other_results=other_results, name=name) + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/cleanup.py b/.bin/Scripts/functions/cleanup.py index 67675cbb..7caf19dc 100644 --- a/.bin/Scripts/functions/cleanup.py +++ b/.bin/Scripts/functions/cleanup.py @@ -4,286 +4,293 @@ from functions.setup import * # STATIC VARIABLES D7_HKCR_CLEANUP = { - r'batfile\shell\!!RunWithParms': {'Recurse': True}, - r'batfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, - r'cmdfile\shell\!!RunWithParms': {'Recurse': True}, - r'cmdfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, - r'exefile\shell\!!RunWithParms': {'Recurse': True}, - r'exefile\shell\ResourceHacker': {'Recurse': True}, - r'regfile\shell\!!RunWithParms': {'Recurse': True}, - r'regfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, - } + r'batfile\shell\!!RunWithParms': {'Recurse': True}, + r'batfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, + r'cmdfile\shell\!!RunWithParms': {'Recurse': True}, + r'cmdfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, + r'exefile\shell\!!RunWithParms': {'Recurse': True}, + r'exefile\shell\ResourceHacker': {'Recurse': True}, + r'regfile\shell\!!RunWithParms': {'Recurse': True}, + r'regfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, + } D7_HKCU_CLEANUP = { - r'Software\Malwarebytes': {'Recurse': False}, - } + r'Software\Malwarebytes': {'Recurse': False}, + } D7_HKLM_CLEANUP = { - r'Software\Emsisoft': {'Recurse': False}, - } + r'Software\Emsisoft': {'Recurse': False}, + } HKU = winreg.HKEY_USERS HKCR = winreg.HKEY_CLASSES_ROOT HKCU = winreg.HKEY_CURRENT_USER HKLM = winreg.HKEY_LOCAL_MACHINE UAC_DEFAULTS_WIN7 = { - r'Software\Microsoft\Windows\CurrentVersion\Policies\System': { - 'DWORD Items': { - 'ConsentPromptBehaviorAdmin': 5, - 'EnableLUA': 1, - 'PromptOnSecureDesktop': 1, - }, - }, - } + r'Software\Microsoft\Windows\CurrentVersion\Policies\System': { + 'DWORD Items': { + 'ConsentPromptBehaviorAdmin': 5, + 'EnableLUA': 1, + 'PromptOnSecureDesktop': 1, + }, + }, + } UAC_DEFAULTS_WIN10 = { - r'Software\Microsoft\Windows\CurrentVersion\Policies\System': { - 'DWORD Items': { - 'ConsentPromptBehaviorAdmin': 5, - 'ConsentPromptBehaviorUser': 3, - 'EnableInstallerDetection': 1, - 'EnableLUA': 1, - 'EnableVirtualization': 1, - 'PromptOnSecureDesktop': 1, - }, - }, - } + r'Software\Microsoft\Windows\CurrentVersion\Policies\System': { + 'DWORD Items': { + 'ConsentPromptBehaviorAdmin': 5, + 'ConsentPromptBehaviorUser': 3, + 'EnableInstallerDetection': 1, + 'EnableLUA': 1, + 'EnableVirtualization': 1, + 'PromptOnSecureDesktop': 1, + }, + }, + } + def cleanup_adwcleaner(): - """Move AdwCleaner folders into the ClientDir.""" - source_path = r'{SYSTEMDRIVE}\AdwCleaner'.format(**global_vars['Env']) - source_quarantine = r'{}\Quarantine'.format(source_path) + """Move AdwCleaner folders into the ClientDir.""" + source_path = r'{SYSTEMDRIVE}\AdwCleaner'.format(**global_vars['Env']) + source_quarantine = r'{}\Quarantine'.format(source_path) - # Quarantine - if os.path.exists(source_quarantine): - os.makedirs(global_vars['QuarantineDir'], exist_ok=True) - dest_name = r'{QuarantineDir}\AdwCleaner_{Date-Time}'.format( - **global_vars) - dest_name = non_clobber_rename(dest_name) - shutil.move(source_quarantine, dest_name) + # Quarantine + if os.path.exists(source_quarantine): + os.makedirs(global_vars['QuarantineDir'], exist_ok=True) + dest_name = r'{QuarantineDir}\AdwCleaner_{Date-Time}'.format( + **global_vars) + dest_name = non_clobber_rename(dest_name) + shutil.move(source_quarantine, dest_name) - # Delete source folder if empty - delete_empty_folders(source_path) + # Delete source folder if empty + delete_empty_folders(source_path) + + # Main folder + if os.path.exists(source_path): + os.makedirs(global_vars['LogDir'], exist_ok=True) + dest_name = r'{LogDir}\Tools\AdwCleaner'.format( + **global_vars) + dest_name = non_clobber_rename(dest_name) + shutil.move(source_path, dest_name) - # Main folder - if os.path.exists(source_path): - os.makedirs(global_vars['LogDir'], exist_ok=True) - dest_name = r'{LogDir}\Tools\AdwCleaner'.format( - **global_vars) - dest_name = non_clobber_rename(dest_name) - shutil.move(source_path, dest_name) def cleanup_cbs(dest_folder): - """Safely cleanup a known CBS archive bug under Windows 7. + """Safely cleanup a known CBS archive bug under Windows 7. - If a CbsPersist file is larger than 2 Gb then the auto archive feature - continually fails and will fill up the system drive with temp files. + If a CbsPersist file is larger than 2 Gb then the auto archive feature + continually fails and will fill up the system drive with temp files. - This function moves the temp files and CbsPersist file to a temp folder, - compresses the CbsPersist files with 7-Zip, and then opens the temp folder - for the user to manually save the backup files and delete the temp files. - """ - backup_folder = r'{dest_folder}\CbsFix'.format(dest_folder=dest_folder) - temp_folder = r'{backup_folder}\Temp'.format(backup_folder=backup_folder) - os.makedirs(backup_folder, exist_ok=True) - os.makedirs(temp_folder, exist_ok=True) + This function moves the temp files and CbsPersist file to a temp folder, + compresses the CbsPersist files with 7-Zip, and then opens the temp folder + for the user to manually save the backup files and delete the temp files. + """ + backup_folder = r'{dest_folder}\CbsFix'.format(dest_folder=dest_folder) + temp_folder = r'{backup_folder}\Temp'.format(backup_folder=backup_folder) + os.makedirs(backup_folder, exist_ok=True) + os.makedirs(temp_folder, exist_ok=True) - # Move files into temp folder - cbs_path = r'{SYSTEMROOT}\Logs\CBS'.format(**global_vars['Env']) - for entry in os.scandir(cbs_path): - # CbsPersist files - if entry.name.lower().startswith('cbspersist'): - dest_name = r'{}\{}'.format(temp_folder, entry.name) - dest_name = non_clobber_rename(dest_name) - shutil.move(entry.path, dest_name) - temp_path = r'{SYSTEMROOT}\Temp'.format(**global_vars['Env']) - for entry in os.scandir(temp_path): - # cab_ files - if entry.name.lower().startswith('cab_'): - dest_name = r'{}\{}'.format(temp_folder, entry.name) - dest_name = non_clobber_rename(dest_name) - shutil.move(entry.path, dest_name) + # Move files into temp folder + cbs_path = r'{SYSTEMROOT}\Logs\CBS'.format(**global_vars['Env']) + for entry in os.scandir(cbs_path): + # CbsPersist files + if entry.name.lower().startswith('cbspersist'): + dest_name = r'{}\{}'.format(temp_folder, entry.name) + dest_name = non_clobber_rename(dest_name) + shutil.move(entry.path, dest_name) + temp_path = r'{SYSTEMROOT}\Temp'.format(**global_vars['Env']) + for entry in os.scandir(temp_path): + # cab_ files + if entry.name.lower().startswith('cab_'): + dest_name = r'{}\{}'.format(temp_folder, entry.name) + dest_name = non_clobber_rename(dest_name) + shutil.move(entry.path, dest_name) + + # Compress CbsPersist files with 7-Zip + cmd = [ + global_vars['Tools']['SevenZip'], + 'a', '-t7z', '-mx=3', '-bso0', '-bse0', + r'{}\CbsPersists.7z'.format(backup_folder), + r'{}\CbsPersist*'.format(temp_folder)] + run_program(cmd) - # Compress CbsPersist files with 7-Zip - cmd = [ - global_vars['Tools']['SevenZip'], - 'a', '-t7z', '-mx=3', '-bso0', '-bse0', - r'{}\CbsPersists.7z'.format(backup_folder), - r'{}\CbsPersist*'.format(temp_folder)] - run_program(cmd) def cleanup_d7ii(): - """Sort d7II logs and remove temp items.""" - d7_path = r'{}\d7II'.format(global_vars['ClientDir']) - d7_reports = r'{} Reports'.format(d7_path) - d7_temp = r'{}\Temp'.format(d7_path) + """Sort d7II logs and remove temp items.""" + d7_path = r'{}\d7II'.format(global_vars['ClientDir']) + d7_reports = r'{} Reports'.format(d7_path) + d7_temp = r'{}\Temp'.format(d7_path) - # Logs & Reports - if os.path.exists(d7_reports): - for entry in os.scandir(d7_reports): - r = re.match(r'(\d+)-(\d+)-(\d+)', entry.name) - d7_date = '{}-{:02d}-{:02d}'.format( - r.group(1), int(r.group(2)), int(r.group(3))) - d7_mlogs = r'{}\Malware Logs'.format(entry.path) - log_dest = r'{SYSTEMDRIVE}\{prefix}\Logs\{date}'.format( - prefix=KIT_NAME_SHORT, - date=d7_date, - **global_vars['Env']) - os.makedirs(r'{}\d7II'.format(log_dest), exist_ok=True) - os.makedirs(r'{}\Tools'.format(log_dest), exist_ok=True) + # Logs & Reports + if os.path.exists(d7_reports): + for entry in os.scandir(d7_reports): + r = re.match(r'(\d+)-(\d+)-(\d+)', entry.name) + d7_date = '{}-{:02d}-{:02d}'.format( + r.group(1), int(r.group(2)), int(r.group(3))) + d7_mlogs = r'{}\Malware Logs'.format(entry.path) + log_dest = r'{SYSTEMDRIVE}\{prefix}\Logs\{date}'.format( + prefix=KIT_NAME_SHORT, + date=d7_date, + **global_vars['Env']) + os.makedirs(r'{}\d7II'.format(log_dest), exist_ok=True) + os.makedirs(r'{}\Tools'.format(log_dest), exist_ok=True) - # Malware Logs - if os.path.exists(d7_mlogs): - m_report = 'MalwareScan_Report.txt' - for m_entry in os.scandir(d7_mlogs): - if m_entry.name == m_report: - dest_path = r'{}\d7II\{}'.format(log_dest, m_entry.name) - else: - dest_path = r'{}\Tools\{}'.format(log_dest, m_entry.name) - dest_path = non_clobber_rename(dest_path) - shutil.move(m_entry.path, dest_path) + # Malware Logs + if os.path.exists(d7_mlogs): + m_report = 'MalwareScan_Report.txt' + for m_entry in os.scandir(d7_mlogs): + if m_entry.name == m_report: + dest_path = r'{}\d7II\{}'.format(log_dest, m_entry.name) + else: + dest_path = r'{}\Tools\{}'.format(log_dest, m_entry.name) + dest_path = non_clobber_rename(dest_path) + shutil.move(m_entry.path, dest_path) - # Other items - for o_entry in os.scandir(entry.path): - dest_path = r'{log_dest}\d7II\{name}'.format( - log_dest=log_dest, - name=o_entry.name) - dest_path = non_clobber_rename(dest_path) + # Other items + for o_entry in os.scandir(entry.path): + dest_path = r'{log_dest}\d7II\{name}'.format( + log_dest=log_dest, + name=o_entry.name) + dest_path = non_clobber_rename(dest_path) - # Just remove empty folders - if o_entry.is_dir(): - try: - os.rmdir(o_entry.path) - except OSError: - pass - else: - continue - - # Move item - shutil.move(o_entry.path, dest_path) - - # Remove folder - delete_empty_folders(entry.path) - - # Registry Items - for key, settings in D7_HKCR_CLEANUP.items(): - delete_registry_key(HKCR, key, recurse=settings['Recurse']) - for key, settings in D7_HKCU_CLEANUP.items(): - delete_registry_key(HKCU, key, recurse=settings['Recurse']) - for key, settings in D7_HKLM_CLEANUP.items(): - delete_registry_key(HKLM, key, recurse=settings['Recurse']) - - # Temp items - if os.path.exists(d7_path): - if os.path.exists(d7_temp): - shutil.rmtree(d7_temp) - try: - os.rmdir(d7_path) - except OSError: + # Just remove empty folders + if o_entry.is_dir(): + try: + os.rmdir(o_entry.path) + except OSError: pass - - # Restore default UAC settings - if global_vars['OS']['Version'] == '10': - write_registry_settings(UAC_DEFAULTS_WIN10, all_users=True) - else: - # Haven't checked Win8 settings, only applying minimum set - write_registry_settings(UAC_DEFAULTS_WIN7, all_users=True) + else: + continue + + # Move item + shutil.move(o_entry.path, dest_path) + + # Remove folder + delete_empty_folders(entry.path) + + # Registry Items + for key, settings in D7_HKCR_CLEANUP.items(): + delete_registry_key(HKCR, key, recurse=settings['Recurse']) + for key, settings in D7_HKCU_CLEANUP.items(): + delete_registry_key(HKCU, key, recurse=settings['Recurse']) + for key, settings in D7_HKLM_CLEANUP.items(): + delete_registry_key(HKLM, key, recurse=settings['Recurse']) + + # Temp items + if os.path.exists(d7_path): + if os.path.exists(d7_temp): + shutil.rmtree(d7_temp) + try: + os.rmdir(d7_path) + except OSError: + pass + + # Restore default UAC settings + if global_vars['OS']['Version'] == '10': + write_registry_settings(UAC_DEFAULTS_WIN10, all_users=True) + else: + # Haven't checked Win8 settings, only applying minimum set + write_registry_settings(UAC_DEFAULTS_WIN7, all_users=True) def cleanup_desktop(): - """Move known backup files and reports into the ClientDir.""" - dest_folder = r'{LogDir}\Tools'.format(**global_vars) - os.makedirs(dest_folder, exist_ok=True) + """Move known backup files and reports into the ClientDir.""" + dest_folder = r'{LogDir}\Tools'.format(**global_vars) + os.makedirs(dest_folder, exist_ok=True) - desktop_path = r'{USERPROFILE}\Desktop'.format(**global_vars['Env']) - for entry in os.scandir(desktop_path): - # JRT, RKill, Shortcut cleaner - if re.search(r'^(JRT|RKill|sc-cleaner)', entry.name, re.IGNORECASE): - dest_name = r'{}\{}'.format(dest_folder, entry.name) - dest_name = non_clobber_rename(dest_name) - shutil.move(entry.path, dest_name) + desktop_path = r'{USERPROFILE}\Desktop'.format(**global_vars['Env']) + for entry in os.scandir(desktop_path): + # JRT, RKill, Shortcut cleaner + if re.search(r'^(JRT|RKill|sc-cleaner)', entry.name, re.IGNORECASE): + dest_name = r'{}\{}'.format(dest_folder, entry.name) + dest_name = non_clobber_rename(dest_name) + shutil.move(entry.path, dest_name) + + # Remove dir if empty + delete_empty_folders(dest_folder) - # Remove dir if empty - delete_empty_folders(dest_folder) def cleanup_emsisoft(): - """Remove EmsisoftCmd files from drive root.""" - source_path = r'{}\EmsisoftCmd'.format(global_vars['Env']['SYSTEMDRIVE']) - source_quarantine = r'{}\Quarantine'.format(source_path) + """Remove EmsisoftCmd files from drive root.""" + source_path = r'{}\EmsisoftCmd'.format(global_vars['Env']['SYSTEMDRIVE']) + source_quarantine = r'{}\Quarantine'.format(source_path) - # Quarantine - if os.path.exists(source_quarantine): - os.makedirs(global_vars['QuarantineDir'], exist_ok=True) - dest_name = r'{QuarantineDir}\Emsisoft_{Date-Time}'.format( - **global_vars) - dest_name = non_clobber_rename(dest_name) - shutil.move(source_quarantine, dest_name) + # Quarantine + if os.path.exists(source_quarantine): + os.makedirs(global_vars['QuarantineDir'], exist_ok=True) + dest_name = r'{QuarantineDir}\Emsisoft_{Date-Time}'.format( + **global_vars) + dest_name = non_clobber_rename(dest_name) + shutil.move(source_quarantine, dest_name) - # Remove program - if os.path.exists(source_path): - shutil.rmtree(source_path) + # Remove program + if os.path.exists(source_path): + shutil.rmtree(source_path) def cleanup_regbackups(): - """Move d7ii regbackups into backup folder.""" - source_path = r'{}\Support\RegBackups'.format( - global_vars['Env']['SYSTEMDRIVE']) + """Move d7ii regbackups into backup folder.""" + source_path = r'{}\Support\RegBackups'.format( + global_vars['Env']['SYSTEMDRIVE']) - # Bail early - if not os.path.exists(source_path): - return + # Bail early + if not os.path.exists(source_path): + return - # Make dest folder - dest_dir = r'{BackupDir}\Registry\{Date}'.format(**global_vars) - os.makedirs(dest_dir, exist_ok=True) + # Make dest folder + dest_dir = r'{BackupDir}\Registry\{Date}'.format(**global_vars) + os.makedirs(dest_dir, exist_ok=True) - # Move to backup folder - for entry in os.scandir(source_path): - dest_path = r'{dest}\{name}'.format(dest=dest_dir, name=entry.name) - dest_path = non_clobber_rename(dest_path) - shutil.move(entry.path, dest_path) + # Move to backup folder + for entry in os.scandir(source_path): + dest_path = r'{dest}\{name}'.format(dest=dest_dir, name=entry.name) + dest_path = non_clobber_rename(dest_path) + shutil.move(entry.path, dest_path) - # Delete source folders if empty - delete_empty_folders(r'{}\Support'.format( - global_vars['Env']['SYSTEMDRIVE'])) + # Delete source folders if empty + delete_empty_folders(r'{}\Support'.format( + global_vars['Env']['SYSTEMDRIVE'])) def delete_empty_folders(folder_path): - """Delete all empty folders in path (depth first).""" - if not os.path.exists(folder_path) or not os.path.isdir(folder_path): - # Bail early (silently) - return + """Delete all empty folders in path (depth first).""" + if not os.path.exists(folder_path) or not os.path.isdir(folder_path): + # Bail early (silently) + return - # Delete empty subfolders first - for item in os.scandir(folder_path): - if item.is_dir(): - delete_empty_folders(item.path) + # Delete empty subfolders first + for item in os.scandir(folder_path): + if item.is_dir(): + delete_empty_folders(item.path) + + # Remove top folder + try: + os.rmdir(folder_path) + except OSError: + pass - # Remove top folder - try: - os.rmdir(folder_path) - except OSError: - pass def delete_registry_key(hive, key, recurse=False): - """Delete a registry key and all it's subkeys.""" - access = winreg.KEY_ALL_ACCESS + """Delete a registry key and all it's subkeys.""" + access = winreg.KEY_ALL_ACCESS - try: - if recurse: - # Delete all subkeys first - with winreg.OpenKeyEx(hive, key, 0, access) as k: - key_info = winreg.QueryInfoKey(k) - for x in range(key_info[0]): - subkey = r'{}\{}'.format(key, winreg.EnumKey(k, 0)) - delete_registry_key(hive, subkey) + try: + if recurse: + # Delete all subkeys first + with winreg.OpenKeyEx(hive, key, 0, access) as k: + key_info = winreg.QueryInfoKey(k) + for x in range(key_info[0]): + subkey = r'{}\{}'.format(key, winreg.EnumKey(k, 0)) + delete_registry_key(hive, subkey) + + # Delete key + winreg.DeleteKey(hive, key) + except FileNotFoundError: + # Ignore + pass - # Delete key - winreg.DeleteKey(hive, key) - except FileNotFoundError: - # Ignore - pass def delete_registry_value(hive, key, value): - """Delete a registry value.""" - access = winreg.KEY_ALL_ACCESS - with winreg.OpenKeyEx(hive, key, 0, access) as k: - winreg.DeleteValue(k, value) + """Delete a registry value.""" + access = winreg.KEY_ALL_ACCESS + with winreg.OpenKeyEx(hive, key, 0, access) as k: + winreg.DeleteValue(k, value) + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") -# vim: sts=4 sw=4 ts=4 +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/common.py b/.bin/Scripts/functions/common.py index ee222937..5a14a3cf 100644 --- a/.bin/Scripts/functions/common.py +++ b/.bin/Scripts/functions/common.py @@ -9,804 +9,920 @@ import sys import time import traceback try: - import winreg + import winreg except ModuleNotFoundError: - if psutil.WINDOWS: - raise - -from subprocess import CalledProcessError + if psutil.WINDOWS: + raise from settings.main import * from settings.tools import * from settings.windows_builds import * +from subprocess import CalledProcessError + # Global variables global_vars = {} + # STATIC VARIABLES COLORS = { - 'CLEAR': '\033[0m', - 'RED': '\033[31m', - 'GREEN': '\033[32m', - 'YELLOW': '\033[33m', - 'BLUE': '\033[34m' -} + 'CLEAR': '\033[0m', + 'RED': '\033[31m', + 'ORANGE': '\033[31;1m', + 'GREEN': '\033[32m', + 'YELLOW': '\033[33m', + 'BLUE': '\033[34m', + 'CYAN': '\033[36m', + } try: - HKU = winreg.HKEY_USERS - HKCR = winreg.HKEY_CLASSES_ROOT - HKCU = winreg.HKEY_CURRENT_USER - HKLM = winreg.HKEY_LOCAL_MACHINE + HKU = winreg.HKEY_USERS + HKCR = winreg.HKEY_CLASSES_ROOT + HKCU = winreg.HKEY_CURRENT_USER + HKLM = winreg.HKEY_LOCAL_MACHINE except NameError: - if psutil.WINDOWS: - raise + if psutil.WINDOWS: + raise + # Error Classes class BIOSKeyNotFoundError(Exception): - pass + pass class BinNotFoundError(Exception): - pass + pass class GenericAbort(Exception): - pass + pass class GenericError(Exception): - pass + pass class GenericRepair(Exception): - pass + pass class MultipleInstallationsError(Exception): - pass + pass class NotInstalledError(Exception): - pass + pass class NoProfilesError(Exception): - pass + pass class OSInstalledLegacyError(Exception): - pass + pass class PathNotFoundError(Exception): - pass + pass class UnsupportedOSError(Exception): - pass + pass class SecureBootDisabledError(Exception): - pass + pass class SecureBootNotAvailError(Exception): - pass + pass class SecureBootUnknownError(Exception): - pass + pass + # General functions def abort(): - """Abort script.""" - print_warning('Aborted.') - sleep(1) - pause(prompt='Press Enter to exit... ') - exit_script() + """Abort script.""" + print_warning('Aborted.') + sleep(1) + pause(prompt='Press Enter to exit... ') + exit_script() + def ask(prompt='Kotaero!'): - """Prompt the user with a Y/N question, log answer, and return a bool.""" - answer = None - prompt = '{} [Y/N]: '.format(prompt) - while answer is None: - tmp = input(prompt) - if re.search(r'^y(es|)$', tmp, re.IGNORECASE): - answer = True - elif re.search(r'^n(o|ope|)$', tmp, re.IGNORECASE): - answer = False - message = '{prompt}{answer_text}'.format( - prompt = prompt, - answer_text = 'Yes' if answer else 'No') - print_log(message=message) - return answer + """Prompt the user with a Y/N question, returns bool.""" + answer = None + prompt = '{} [Y/N]: '.format(prompt) + while answer is None: + tmp = input(prompt) + if re.search(r'^y(es|)$', tmp, re.IGNORECASE): + answer = True + elif re.search(r'^n(o|ope|)$', tmp, re.IGNORECASE): + answer = False + message = '{prompt}{answer_text}'.format( + prompt = prompt, + answer_text = 'Yes' if answer else 'No') + print_log(message=message) + return answer + def choice(choices, prompt='Kotaero!'): - """Prompt the user with a choice question, log answer, and returns str.""" - answer = None - choices = [str(c) for c in choices] - choices_short = {c[:1].upper(): c for c in choices} - prompt = '{} [{}]: '.format(prompt, '/'.join(choices)) - regex = '^({}|{})$'.format( - '|'.join([c[:1] for c in choices]), - '|'.join(choices)) + """Prompt the user with a choice question, returns str.""" + answer = None + choices = [str(c) for c in choices] + choices_short = {c[:1].upper(): c for c in choices} + prompt = '{} [{}]: '.format(prompt, '/'.join(choices)) + regex = '^({}|{})$'.format( + '|'.join([c[:1] for c in choices]), + '|'.join(choices)) - # Get user's choice - while answer is None: - tmp = input(prompt) - if re.search(regex, tmp, re.IGNORECASE): - answer = tmp + # Get user's choice + while answer is None: + tmp = input(prompt) + if re.search(regex, tmp, re.IGNORECASE): + answer = tmp - # Log result - message = '{prompt}{answer_text}'.format( - prompt = prompt, - answer_text = 'Yes' if answer else 'No') - print_log(message=message) + # Log result + message = '{prompt}{answer_text}'.format( + prompt = prompt, + answer_text = 'Yes' if answer else 'No') + print_log(message=message) - # Fix answer formatting to match provided values - answer = choices_short[answer[:1].upper()] + # Fix answer formatting to match provided values + answer = choices_short[answer[:1].upper()] + + # Done + return answer - # Done - return answer def clear_screen(): - """Simple wrapper for cls/clear.""" - if psutil.WINDOWS: - os.system('cls') - else: - os.system('clear') + """Simple wrapper for cls/clear.""" + if psutil.WINDOWS: + os.system('cls') + else: + os.system('clear') + def convert_to_bytes(size): - """Convert human-readable size str to bytes and return an int.""" - size = str(size) - tmp = re.search(r'(\d+\.?\d*)\s+([KMGT]B)', size.upper()) - if tmp: - size = float(tmp.group(1)) - units = tmp.group(2) - if units == 'TB': - size *= 1099511627776 - elif units == 'GB': - size *= 1073741824 - elif units == 'MB': - size *= 1048576 - elif units == 'KB': - size *= 1024 - size = int(size) - else: - return -1 + """Convert human-readable size str to bytes and return an int.""" + size = str(size) + tmp = re.search(r'(\d+\.?\d*)\s+([KMGT]B)', size.upper()) + if tmp: + size = float(tmp.group(1)) + units = tmp.group(2) + if units == 'TB': + size *= 1099511627776 + elif units == 'GB': + size *= 1073741824 + elif units == 'MB': + size *= 1048576 + elif units == 'KB': + size *= 1024 + size = int(size) + else: + return -1 + + return size - return size def exit_script(return_value=0): - """Exits the script after some cleanup and opens the log (if set).""" - # Remove dirs (if empty) - for dir in ['BackupDir', 'LogDir', 'TmpDir']: - try: - os.rmdir(global_vars[dir]) - except Exception: - pass + """Exits the script after some cleanup and opens the log (if set).""" + # Remove dirs (if empty) + for dir in ['BackupDir', 'LogDir', 'TmpDir']: + try: + os.rmdir(global_vars[dir]) + except Exception: + pass - # Open Log (if it exists) - log = global_vars.get('LogFile', '') - if log and os.path.exists(log) and psutil.WINDOWS and ENABLED_OPEN_LOGS: - try: - extract_item('NotepadPlusPlus', silent=True) - popen_program( - [global_vars['Tools']['NotepadPlusPlus'], - global_vars['LogFile']]) - except Exception: - print_error('ERROR: Failed to extract Notepad++ and open log.') - pause('Press Enter to exit...') + # Open Log (if it exists) + log = global_vars.get('LogFile', '') + if log and os.path.exists(log) and psutil.WINDOWS and ENABLED_OPEN_LOGS: + try: + extract_item('NotepadPlusPlus', silent=True) + popen_program( + [global_vars['Tools']['NotepadPlusPlus'], + global_vars['LogFile']]) + except Exception: + print_error('ERROR: Failed to extract Notepad++ and open log.') + pause('Press Enter to exit...') - # Kill Caffeine if still running - kill_process('caffeine.exe') + # Kill Caffeine if still running + kill_process('caffeine.exe') + + # Exit + sys.exit(return_value) - # Exit - sys.exit(return_value) def extract_item(item, filter='', silent=False): - """Extract item from .cbin into .bin.""" - cmd = [ - global_vars['Tools']['SevenZip'], 'x', '-aos', '-bso0', '-bse0', - '-p{ArchivePassword}'.format(**global_vars), - r'-o{BinDir}\{item}'.format(item=item, **global_vars), - r'{CBinDir}\{item}.7z'.format(item=item, **global_vars), - filter] + """Extract item from .cbin into .bin.""" + cmd = [ + global_vars['Tools']['SevenZip'], 'x', '-aos', '-bso0', '-bse0', + '-p{ArchivePassword}'.format(**global_vars), + r'-o{BinDir}\{item}'.format(item=item, **global_vars), + r'{CBinDir}\{item}.7z'.format(item=item, **global_vars), + filter] + if not silent: + print_standard('Extracting "{item}"...'.format(item=item)) + try: + run_program(cmd) + except FileNotFoundError: if not silent: - print_standard('Extracting "{item}"...'.format(item=item)) - try: - run_program(cmd) - except FileNotFoundError: - if not silent: - print_warning('WARNING: Archive not found') - except subprocess.CalledProcessError: - if not silent: - print_warning('WARNING: Errors encountered while exctracting data') + print_warning('WARNING: Archive not found') + except subprocess.CalledProcessError: + if not silent: + print_warning('WARNING: Errors encountered while exctracting data') + def get_process(name=None): - """Get process by name, returns psutil.Process obj.""" - proc = None - if not name: - raise GenericError + """Get process by name, returns psutil.Process obj.""" + proc = None + if not name: + raise GenericError + + for p in psutil.process_iter(): + try: + if p.name() == name: + proc = p + except psutil._exceptions.NoSuchProcess: + # Process finished during iteration? Going to ignore + pass + return proc - for p in psutil.process_iter(): - try: - if p.name() == name: - proc = p - except psutil._exceptions.NoSuchProcess: - # Process finished during iteration? Going to ignore - pass - return proc def get_simple_string(prompt='Enter string'): - """Get string from user (minimal allowed character set) and return as str.""" - simple_string = None - while simple_string is None: - _input = input('{}: '.format(prompt)) - if re.match(r"^(\w|-| |\.|')+$", _input, re.ASCII): - simple_string = _input.strip() - return simple_string + """Get string from user (restricted character set), returns str.""" + simple_string = None + while simple_string is None: + _input = input('{}: '.format(prompt)) + if re.match(r"^(\w|-| |\.|')+$", _input, re.ASCII): + simple_string = _input.strip() + return simple_string + def get_ticket_number(): - """Get TicketNumber from user, save in LogDir, and return as str.""" - if not ENABLED_TICKET_NUMBERS: - return None - ticket_number = None - while ticket_number is None: - _input = input('Enter ticket number: ') - if re.match(r'^([0-9]+([-_]?\w+|))$', _input): - ticket_number = _input - out_file = r'{}\TicketNumber'.format(global_vars['LogDir']) - if not psutil.WINDOWS: - out_file = out_file.replace('\\', '/') - with open(out_file, 'w', encoding='utf-8') as f: - f.write(ticket_number) - return ticket_number + """Get TicketNumber from user, save in LogDir, and return as str.""" + if not ENABLED_TICKET_NUMBERS: + return None + ticket_number = None + while ticket_number is None: + _input = input('Enter ticket number: ') + if re.match(r'^([0-9]+([-_]?\w+|))$', _input): + ticket_number = _input + out_file = r'{}\TicketNumber'.format(global_vars['LogDir']) + if not psutil.WINDOWS: + out_file = out_file.replace('\\', '/') + with open(out_file, 'w', encoding='utf-8') as f: + f.write(ticket_number) + return ticket_number + def human_readable_size(size, decimals=0): - """Convert size in bytes to a human-readable format and return a str.""" - # Prep string formatting - width = 3+decimals - if decimals > 0: - width += 1 + """Convert size from bytes to a human-readable format, returns str.""" + # Prep string formatting + width = 3+decimals + if decimals > 0: + width += 1 - # Convert size to int - try: - size = int(size) - except ValueError: - size = convert_to_bytes(size) - except TypeError: - size = -1 + # Convert size to int + try: + size = int(size) + except ValueError: + size = convert_to_bytes(size) + except TypeError: + size = -1 - # Verify we have a valid size - if size < 0: - return '{size:>{width}} b'.format(size='???', width=width) + # Verify we have a valid size + if size < 0: + return '{size:>{width}} b'.format(size='???', width=width) - # Convert to sensible units - if size >= 1099511627776: - size /= 1099511627776 - units = 'Tb' - elif size >= 1073741824: - size /= 1073741824 - units = 'Gb' - elif size >= 1048576: - size /= 1048576 - units = 'Mb' - elif size >= 1024: - size /= 1024 - units = 'Kb' - else: - units = ' b' + # Convert to sensible units + if size >= 1099511627776: + size /= 1099511627776 + units = 'Tb' + elif size >= 1073741824: + size /= 1073741824 + units = 'Gb' + elif size >= 1048576: + size /= 1048576 + units = 'Mb' + elif size >= 1024: + size /= 1024 + units = 'Kb' + else: + units = ' b' + + # Return + return '{size:>{width}.{decimals}f} {units}'.format( + size=size, width=width, decimals=decimals, units=units) - # Return - return '{size:>{width}.{decimals}f} {units}'.format( - size=size, width=width, decimals=decimals, units=units) def kill_process(name): - """Kill any running caffeine.exe processes.""" - for proc in psutil.process_iter(): - if proc.name() == name: - proc.kill() + """Kill any running caffeine.exe processes.""" + for proc in psutil.process_iter(): + if proc.name() == name: + proc.kill() + def major_exception(): - """Display traceback and exit""" - print_error('Major exception') - print_warning(SUPPORT_MESSAGE) - print(traceback.format_exc()) - print_log(traceback.format_exc()) - try: - upload_crash_details() - except GenericAbort: - # User declined upload - print_warning('Upload: Aborted') - sleep(30) - except GenericError: - # No log file or uploading disabled - sleep(30) - except: - print_error('Upload: NS') - sleep(30) - else: - print_success('Upload: CS') - pause('Press Enter to exit...') - exit_script(1) + """Display traceback and exit""" + print_error('Major exception') + print_warning(SUPPORT_MESSAGE) + print(traceback.format_exc()) + print_log(traceback.format_exc()) + try: + upload_crash_details() + except GenericAbort: + # User declined upload + print_warning('Upload: Aborted') + sleep(10) + except GenericError: + # No log file or uploading disabled + sleep(10) + except: + print_error('Upload: Failed') + sleep(10) + else: + print_success('Upload: Complete') + pause('Press Enter to exit...') + exit_script(1) -def menu_select(title='~ Untitled Menu ~', - prompt='Please make a selection', secret_exit=False, + +def menu_select( + title='[Untitled Menu]', + prompt='Please make a selection', secret_actions=[], secret_exit=False, main_entries=[], action_entries=[], disabled_label='DISABLED', spacer=''): - """Display options in a menu and return selected option as a str.""" - # Bail early - if not main_entries and not action_entries: - raise Exception("MenuError: No items given") + """Display options in a menu and return selected option as a str.""" + # Bail early + if not main_entries and not action_entries: + raise Exception("MenuError: No items given") - # Set title - if 'Title' in global_vars: - title = '{}\n\n{}'.format(global_vars['Title'], title) + # Set title + if 'Title' in global_vars: + title = '{}\n\n{}'.format(global_vars['Title'], title) - # Build menu - menu_splash = '{}\n{}\n'.format(title, spacer) - width = len(str(len(main_entries))) - valid_answers = [] - if (secret_exit): - valid_answers.append('Q') + # Build menu + menu_splash = '{}\n{}\n'.format(title, spacer) + width = len(str(len(main_entries))) + valid_answers = [] + if secret_exit: + valid_answers.append('Q') + if secret_actions: + valid_answers.extend(secret_actions) - # Add main entries - for i in range(len(main_entries)): - entry = main_entries[i] - # Add Spacer - if ('CRLF' in entry): - menu_splash += '{}\n'.format(spacer) - entry_str = '{number:>{width}}: {name}'.format( - number = i+1, - width = width, - name = entry.get('Display Name', entry['Name'])) - if entry.get('Disabled', False): - entry_str = '{YELLOW}{entry_str} ({disabled}){CLEAR}'.format( - entry_str = entry_str, - disabled = disabled_label, - **COLORS) - else: - valid_answers.append(str(i+1)) - menu_splash += '{}\n'.format(entry_str) - menu_splash += '{}\n'.format(spacer) + # Add main entries + for i in range(len(main_entries)): + entry = main_entries[i] + # Add Spacer + if ('CRLF' in entry): + menu_splash += '{}\n'.format(spacer) + entry_str = '{number:>{width}}: {name}'.format( + number = i+1, + width = width, + name = entry.get('Display Name', entry['Name'])) + if entry.get('Disabled', False): + entry_str = '{YELLOW}{entry_str} ({disabled}){CLEAR}'.format( + entry_str = entry_str, + disabled = disabled_label, + **COLORS) + else: + valid_answers.append(str(i+1)) + menu_splash += '{}\n'.format(entry_str) + menu_splash += '{}\n'.format(spacer) - # Add action entries - for entry in action_entries: - # Add Spacer - if ('CRLF' in entry): - menu_splash += '{}\n'.format(spacer) - valid_answers.append(entry['Letter']) - menu_splash += '{letter:>{width}}: {name}\n'.format( - letter = entry['Letter'].upper(), - width = len(str(len(action_entries))), - name = entry['Name']) - menu_splash += '\n' + # Add action entries + for entry in action_entries: + # Add Spacer + if ('CRLF' in entry): + menu_splash += '{}\n'.format(spacer) + valid_answers.append(entry['Letter']) + menu_splash += '{letter:>{width}}: {name}\n'.format( + letter = entry['Letter'].upper(), + width = len(str(len(action_entries))), + name = entry['Name']) - answer = '' + answer = '' - while (answer.upper() not in valid_answers): - clear_screen() - print(menu_splash) - answer = input('{}: '.format(prompt)) + while (answer.upper() not in valid_answers): + clear_screen() + print(menu_splash) + answer = input('{}: '.format(prompt)) + + return answer.upper() - return answer.upper() def non_clobber_rename(full_path): - """Append suffix to path, if necessary, to avoid clobbering path""" - new_path = full_path - _i = 1; - while os.path.exists(new_path): - new_path = '{path}_{i}'.format(i=_i, path=full_path) - _i += 1 + """Append suffix to path, if necessary, to avoid clobbering path""" + new_path = full_path + _i = 1; + while os.path.exists(new_path): + new_path = '{path}_{i}'.format(i=_i, path=full_path) + _i += 1 + + return new_path - return new_path def pause(prompt='Press Enter to continue... '): - """Simple pause implementation.""" - input(prompt) + """Simple pause implementation.""" + input(prompt) + def ping(addr='google.com'): - """Attempt to ping addr.""" - cmd = [ - 'ping', - '-n' if psutil.WINDOWS else '-c', - '2', - addr] - run_program(cmd) + """Attempt to ping addr.""" + cmd = [ + 'ping', + '-n' if psutil.WINDOWS else '-c', + '2', + addr] + run_program(cmd) + def popen_program(cmd, pipe=False, minimized=False, shell=False, **kwargs): - """Run program and return a subprocess.Popen object.""" - startupinfo=None - if minimized: - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW - startupinfo.wShowWindow = 6 + """Run program and return a subprocess.Popen object.""" + cmd_kwargs = {'args': cmd, 'shell': shell} - if pipe: - popen_obj = subprocess.Popen(cmd, shell=shell, startupinfo=startupinfo, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - else: - popen_obj = subprocess.Popen(cmd, shell=shell, startupinfo=startupinfo) + if minimized: + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = 6 + cmd_kwargs['startupinfo'] = startupinfo + + if pipe: + cmd_kwargs.update({ + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + }) + + if 'cwd' in kwargs: + cmd_kwargs['cwd'] = kwargs['cwd'] + + return subprocess.Popen(**cmd_kwargs) - return popen_obj def print_error(*args, **kwargs): - """Prints message to screen in RED.""" - print_standard(*args, color=COLORS['RED'], **kwargs) + """Prints message to screen in RED.""" + print_standard(*args, color=COLORS['RED'], **kwargs) + def print_info(*args, **kwargs): - """Prints message to screen in BLUE.""" - print_standard(*args, color=COLORS['BLUE'], **kwargs) + """Prints message to screen in BLUE.""" + print_standard(*args, color=COLORS['BLUE'], **kwargs) + def print_standard(message='Generic info', - color=None, end='\n', timestamp=True, **kwargs): - """Prints message to screen and log (if set).""" - display_message = message - if color: - display_message = color + message + COLORS['CLEAR'] - # **COLORS is used below to support non-"standard" color printing - print(display_message.format(**COLORS), end=end, **kwargs) - print_log(message, end, timestamp) + color=None, end='\n', timestamp=True, **kwargs): + """Prints message to screen and log (if set).""" + display_message = message + if color: + display_message = color + message + COLORS['CLEAR'] + # **COLORS is used below to support non-"standard" color printing + print(display_message.format(**COLORS), end=end, **kwargs) + print_log(message, end, timestamp) + def print_success(*args, **kwargs): - """Prints message to screen in GREEN.""" - print_standard(*args, color=COLORS['GREEN'], **kwargs) + """Prints message to screen in GREEN.""" + print_standard(*args, color=COLORS['GREEN'], **kwargs) + def print_warning(*args, **kwargs): - """Prints message to screen in YELLOW.""" - print_standard(*args, color=COLORS['YELLOW'], **kwargs) + """Prints message to screen in YELLOW.""" + print_standard(*args, color=COLORS['YELLOW'], **kwargs) + def print_log(message='', end='\n', timestamp=True): - """Writes message to a log if LogFile is set.""" - time_str = time.strftime("%Y-%m-%d %H%M%z: ") if timestamp else '' - if 'LogFile' in global_vars and global_vars['LogFile']: - with open(global_vars['LogFile'], 'a', encoding='utf-8') as f: - for line in message.splitlines(): - f.write('{timestamp}{line}{end}'.format( - timestamp = time_str, - line = line, - end = end)) + """Writes message to a log if LogFile is set.""" + time_str = time.strftime("%Y-%m-%d %H%M%z: ") if timestamp else '' + if 'LogFile' in global_vars and global_vars['LogFile']: + with open(global_vars['LogFile'], 'a', encoding='utf-8') as f: + for line in message.splitlines(): + f.write('{timestamp}{line}{end}'.format( + timestamp = time_str, + line = line, + end = end)) -def run_program(cmd, args=[], check=True, pipe=True, shell=False): - """Run program and return a subprocess.CompletedProcess object.""" - if args: - # Deprecated so let's raise an exception to find & fix all occurances - print_error('ERROR: Using args is no longer supported.') - raise Exception - cmd = [c for c in cmd if c] - if shell: - cmd = ' '.join(cmd) - if pipe: - process_return = subprocess.run(cmd, check=check, shell=shell, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - else: - process_return = subprocess.run(cmd, check=check, shell=shell) +def run_program(cmd, check=True, pipe=True, shell=False, **kwargs): + """Run program and return a subprocess.CompletedProcess object.""" + cmd = [c for c in cmd if c] + if shell: + cmd = ' '.join(cmd) - return process_return + cmd_kwargs = {'args': cmd, 'check': check, 'shell': shell} -def set_title(title='~Some Title~'): - """Set title. + if pipe: + cmd_kwargs.update({ + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + }) - Used for window title and menu titles.""" - global_vars['Title'] = title - os.system('title {}'.format(title)) + if 'cwd' in kwargs: + cmd_kwargs['cwd'] = kwargs['cwd'] -def show_data(message='~Some message~', data='~Some data~', indent=8, width=32, + return subprocess.run(**cmd_kwargs) + + +def set_title(title='[Some Title]'): + """Set title. + + Used for window title and menu titles.""" + global_vars['Title'] = title + os.system('title {}'.format(title)) + + +def show_data( + message='[Some message]', data='[Some data]', + indent=8, width=32, info=False, warning=False, error=False): - """Display info with formatting.""" - message = '{indent}{message:<{width}}{data}'.format( - indent=' '*indent, width=width, message=message, data=data) - if error: - print_error(message) - elif warning: - print_warning(message) - elif info: - print_info(message) - else: - print_standard(message) + """Display info with formatting.""" + message = '{indent}{message:<{width}}{data}'.format( + indent=' '*indent, width=width, message=message, data=data) + if error: + print_error(message) + elif warning: + print_warning(message) + elif info: + print_info(message) + else: + print_standard(message) + def sleep(seconds=2): - """Wait for a while.""" - time.sleep(seconds) + """Wait for a while.""" + time.sleep(seconds) + def stay_awake(): - """Prevent the system from sleeping or hibernating.""" - # DISABLED due to VCR2008 dependency - return - # Bail if caffeine is already running - for proc in psutil.process_iter(): - if proc.name() == 'caffeine.exe': - return - # Extract and run - extract_item('Caffeine', silent=True) - try: - popen_program([global_vars['Tools']['Caffeine']]) - except Exception: - print_error('ERROR: No caffeine available.') - print_warning('Please set the power setting to High Performance.') + """Prevent the system from sleeping or hibernating.""" + # DISABLED due to VCR2008 dependency + return + # Bail if caffeine is already running + for proc in psutil.process_iter(): + if proc.name() == 'caffeine.exe': + return + # Extract and run + extract_item('Caffeine', silent=True) + try: + popen_program([global_vars['Tools']['Caffeine']]) + except Exception: + print_error('ERROR: No caffeine available.') + print_warning('Please set the power setting to High Performance.') + + +def strip_colors(s): + """Remove all ASCII color escapes from string, returns str.""" + for c in COLORS.values(): + s = s.replace(c, '') + return s + def get_exception(s): - """Get exception by name, returns Exception object.""" - try: - obj = getattr(sys.modules[__name__], s) - except AttributeError: - # Try builtin classes - obj = getattr(sys.modules['builtins'], s) - return obj + """Get exception by name, returns Exception object.""" + try: + obj = getattr(sys.modules[__name__], s) + except AttributeError: + # Try builtin classes + obj = getattr(sys.modules['builtins'], s) + return obj + def try_and_print(message='Trying...', - function=None, cs='CS', ns='NS', other_results={}, - catch_all=True, print_return=False, silent_function=True, - indent=8, width=32, *args, **kwargs): - """Run function, print if successful or not, and return dict. + function=None, cs='SUCCESS', ns='FAILED', other_results={}, + catch_all=True, print_return=False, silent_function=True, + indent=8, width=32, *args, **kwargs): + """Run function, print if successful or not, and return dict. - other_results is in the form of - { - 'Warning': {'ExceptionClassName': 'Result Message'}, - 'Error': {'ExceptionClassName': 'Result Message'} - } - The the ExceptionClassNames will be excepted conditions - and the result string will be printed in the correct color. - catch_all=False will result in unspecified exceptions being re-raised.""" - err = None - out = None - w_exceptions = other_results.get('Warning', {}).keys() - w_exceptions = tuple(get_exception(e) for e in w_exceptions) - e_exceptions = other_results.get('Error', {}).keys() - e_exceptions = tuple(get_exception(e) for e in e_exceptions) - w_results = other_results.get('Warning', {}) - e_results = other_results.get('Error', {}) + other_results is in the form of + { + 'Warning': {'ExceptionClassName': 'Result Message'}, + 'Error': {'ExceptionClassName': 'Result Message'} + } + The the ExceptionClassNames will be excepted conditions + and the result string will be printed in the correct color. + catch_all=False will re-raise unspecified exceptions.""" + err = None + out = None + w_exceptions = other_results.get('Warning', {}).keys() + w_exceptions = tuple(get_exception(e) for e in w_exceptions) + e_exceptions = other_results.get('Error', {}).keys() + e_exceptions = tuple(get_exception(e) for e in e_exceptions) + w_results = other_results.get('Warning', {}) + e_results = other_results.get('Error', {}) - # Run function and catch errors - print_standard('{indent}{message:<{width}}'.format( - indent=' '*indent, message=message, width=width), end='', flush=True) - try: - out = function(*args, **kwargs) - if print_return: - str_list = out - if isinstance(out, subprocess.CompletedProcess): - str_list = out.stdout.decode().strip().splitlines() - print_standard(str_list[0].strip(), timestamp=False) - for item in str_list[1:]: - print_standard('{indent}{item}'.format( - indent=' '*(indent+width), item=item.strip())) - elif silent_function: - print_success(cs, timestamp=False) - except w_exceptions as e: - _result = w_results.get(e.__class__.__name__, 'Warning') - print_warning(_result, timestamp=False) - err = e - except e_exceptions as e: - _result = e_results.get(e.__class__.__name__, 'Error') - print_error(_result, timestamp=False) - err = e - except Exception: - print_error(ns, timestamp=False) - err = traceback.format_exc() + # Run function and catch errors + print_standard('{indent}{message:<{width}}'.format( + indent=' '*indent, message=message, width=width), end='', flush=True) + try: + out = function(*args, **kwargs) + if print_return: + str_list = out + if isinstance(out, subprocess.CompletedProcess): + str_list = out.stdout.decode().strip().splitlines() + print_standard(str_list[0].strip(), timestamp=False) + for item in str_list[1:]: + print_standard('{indent}{item}'.format( + indent=' '*(indent+width), item=item.strip())) + elif silent_function: + print_success(cs, timestamp=False) + except w_exceptions as e: + _result = w_results.get(e.__class__.__name__, 'Warning') + print_warning(_result, timestamp=False) + err = e + except e_exceptions as e: + _result = e_results.get(e.__class__.__name__, 'Error') + print_error(_result, timestamp=False) + err = e + except Exception: + print_error(ns, timestamp=False) + err = traceback.format_exc() + + # Return or raise? + if err and not catch_all: + raise + else: + return {'CS': not bool(err), 'Error': err, 'Out': out} - # Return or raise? - if err and not catch_all: - raise - else: - return {'CS': not bool(err), 'Error': err, 'Out': out} def upload_crash_details(): - """Upload log and runtime data to the CRASH_SERVER. + """Upload log and runtime data to the CRASH_SERVER. - Intended for uploading to a public Nextcloud share.""" - if not ENABLED_UPLOAD_DATA: - raise GenericError + Intended for uploading to a public Nextcloud share.""" + if not ENABLED_UPLOAD_DATA: + raise GenericError - import requests - if 'LogFile' in global_vars and global_vars['LogFile']: - if ask('Upload crash details to {}?'.format(CRASH_SERVER['Name'])): - with open(global_vars['LogFile']) as f: - certificate_authority = r'{}\{}'.format( - global_vars['BinDir'], ROOT_CA_NAME) - data = '''{} -############################# -Runtime Details: - -sys.argv: {} - -global_vars: {}'''.format(f.read(), sys.argv, global_vars) - filename = global_vars.get('LogFile', 'Unknown') - filename = re.sub(r'.*(\\|/)', '', filename) - filename += '.txt' - url = '{}/Crash_{}__{}'.format( - CRASH_SERVER['Url'], - global_vars.get('Date-Time', 'Unknown Date-Time'), - filename) - r = requests.put( - url, data=data, - headers={'X-Requested-With': 'XMLHttpRequest'}, - auth=(CRASH_SERVER['User'], CRASH_SERVER['Pass']), - verify=certificate_authority) - # Raise exception if upload NS - if not r.ok: - raise Exception - else: - # User said no - raise GenericAbort + import requests + if 'LogFile' in global_vars and global_vars['LogFile']: + if ask('Upload crash details to {}?'.format(CRASH_SERVER['Name'])): + with open(global_vars['LogFile']) as f: + certificate_authority = r'{}\{}'.format( + global_vars['BinDir'], ROOT_CA_NAME) + if psutil.LINUX: + # Use system certificates + certificate_authority = True + data = '{}\n'.format(f.read()) + data += '#############################\n' + data += 'Runtime Details:\n\n' + data += 'sys.argv: {}\n\n'.format(sys.argv) + try: + data += generate_global_vars_report() + except Exception: + data += 'global_vars: {}\n'.format(global_vars) + filename = global_vars.get('LogFile', 'Unknown') + filename = re.sub(r'.*(\\|/)', '', filename) + filename += '.txt' + url = '{}/Crash_{}__{}'.format( + CRASH_SERVER['Url'], + global_vars.get('Date-Time', 'Unknown Date-Time'), + filename) + r = requests.put( + url, data=data, + headers={'X-Requested-With': 'XMLHttpRequest'}, + auth=(CRASH_SERVER['User'], CRASH_SERVER['Pass']), + verify=certificate_authority) + # Raise exception if upload failed + if not r.ok: + raise Exception else: - # No LogFile defined (or invalid LogFile) - raise GenericError + # User said no + raise GenericAbort + else: + # No LogFile defined (or invalid LogFile) + raise GenericError + def wait_for_process(name, poll_rate=3): - """Wait for process by name.""" - running = True - while running: - sleep(poll_rate) - running = False - for proc in psutil.process_iter(): - try: - if re.search(r'^{}'.format(name), proc.name(), re.IGNORECASE): - running = True - except psutil._exceptions.NoSuchProcess: - # Assuming process closed during iteration - pass - sleep(1) + """Wait for process by name.""" + running = True + while running: + sleep(poll_rate) + running = False + for proc in psutil.process_iter(): + try: + if re.search(r'^{}'.format(name), proc.name(), re.IGNORECASE): + running = True + except psutil._exceptions.NoSuchProcess: + # Assuming process closed during iteration + pass + sleep(1) + # global_vars functions -def init_global_vars(): - """Sets global variables based on system info.""" +def init_global_vars(silent=False): + """Sets global variables based on system info.""" + if not silent: print_info('Initializing') - if psutil.WINDOWS: - os.system('title Wizard Kit') - if psutil.LINUX: - init_functions = [ - ['Checking environment...', set_linux_vars], - ['Clearing collisions...', clean_env_vars], - ] + if psutil.WINDOWS: + os.system('title Wizard Kit') + if psutil.LINUX: + init_functions = [ + ['Checking environment...', set_linux_vars], + ['Clearing collisions...', clean_env_vars], + ] + else: + init_functions = [ + ['Checking .bin...', find_bin], + ['Checking environment...', set_common_vars], + ['Checking OS...', check_os], + ['Checking tools...', check_tools], + ['Creating folders...', make_tmp_dirs], + ['Clearing collisions...', clean_env_vars], + ] + try: + if silent: + for f in init_functions: + f[1]() else: - init_functions = [ - ['Checking .bin...', find_bin], - ['Checking environment...', set_common_vars], - ['Checking OS...', check_os], - ['Checking tools...', check_tools], - ['Creating folders...', make_tmp_dirs], - ['Clearing collisions...', clean_env_vars], - ] - try: - for f in init_functions: - try_and_print( - message=f[0], function=f[1], - cs='Done', ns='Error', catch_all=False) - except: - major_exception() + for f in init_functions: + try_and_print( + message=f[0], function=f[1], + cs='Done', ns='Error', catch_all=False) + except: + major_exception() + def check_os(): - """Set OS specific variables.""" - tmp = {} + """Set OS specific variables.""" + tmp = {} - # Query registry - path = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion' - with winreg.OpenKey(HKLM, path) as key: - for name in ['CurrentBuild', 'CurrentVersion', 'ProductName']: - try: - tmp[name] = winreg.QueryValueEx(key, name)[0] - except FileNotFoundError: - tmp[name] = 'Unknown' + # Query registry + path = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion' + with winreg.OpenKey(HKLM, path) as key: + for name in ['CurrentBuild', 'CurrentVersion', 'ProductName']: + try: + tmp[name] = winreg.QueryValueEx(key, name)[0] + except FileNotFoundError: + tmp[name] = 'Unknown' - # Handle CurrentBuild collision - if tmp['CurrentBuild'] == '9200': - if tmp['CurrentVersion'] == '6.2': - # Windown 8, set to fake build number - tmp['CurrentBuild'] = '9199' - else: - # Windows 8.1, leave alone - pass + # Handle CurrentBuild collision + if tmp['CurrentBuild'] == '9200': + if tmp['CurrentVersion'] == '6.2': + # Windown 8, set to fake build number + tmp['CurrentBuild'] = '9199' + else: + # Windows 8.1, leave alone + pass - # Check bit depth - tmp['Arch'] = 32 - if 'PROGRAMFILES(X86)' in global_vars['Env']: - tmp['Arch'] = 64 + # Check bit depth + tmp['Arch'] = 32 + if 'PROGRAMFILES(X86)' in global_vars['Env']: + tmp['Arch'] = 64 - # Get Windows build info - build_info = WINDOWS_BUILDS.get( - tmp['CurrentBuild'], - ('Unknown', 'Build {}'.format(tmp['CurrentBuild']), None, None, 'unrecognized')) + # Get Windows build info + build_info = WINDOWS_BUILDS.get(tmp['CurrentBuild'], None) + if build_info is None: + # Not in windows_builds.py + build_info = [ + 'Unknown', + 'Build {}'.format(tmp['CurrentBuild']), + None, + None, + 'unrecognized'] + else: build_info = list(build_info) - tmp['Version'] = build_info.pop(0) - tmp['Release'] = build_info.pop(0) - tmp['Codename'] = build_info.pop(0) - tmp['Marketing Name'] = build_info.pop(0) - tmp['Notes'] = build_info.pop(0) + tmp['Version'] = build_info.pop(0) + tmp['Release'] = build_info.pop(0) + tmp['Codename'] = build_info.pop(0) + tmp['Marketing Name'] = build_info.pop(0) + tmp['Notes'] = build_info.pop(0) - # Set name - tmp['Name'] = tmp['ProductName'] - if tmp['Release']: - tmp['Name'] += ' {}'.format(tmp['Release']) - if tmp['Codename']: - tmp['Name'] += ' "{}"'.format(tmp['Codename']) - if tmp['Marketing Name']: - tmp['Name'] += ' / "{}"'.format(tmp['Marketing Name']) - tmp['Name'] = re.sub(r'\s+', ' ', tmp['Name']) + # Set name + tmp['Name'] = tmp['ProductName'] + if tmp['Release']: + tmp['Name'] += ' {}'.format(tmp['Release']) + if tmp['Codename']: + tmp['Name'] += ' "{}"'.format(tmp['Codename']) + if tmp['Marketing Name']: + tmp['Name'] += ' / "{}"'.format(tmp['Marketing Name']) + tmp['Name'] = re.sub(r'\s+', ' ', tmp['Name']) - # Set display name - tmp['DisplayName'] = '{} x{}'.format(tmp['Name'], tmp['Arch']) - if tmp['Notes']: - tmp['DisplayName'] += ' ({})'.format(tmp['Notes']) + # Set display name + tmp['DisplayName'] = '{} x{}'.format(tmp['Name'], tmp['Arch']) + if tmp['Notes']: + tmp['DisplayName'] += ' ({})'.format(tmp['Notes']) + + global_vars['OS'] = tmp - global_vars['OS'] = tmp def check_tools(): - """Set tool variables based on OS bit-depth and tool availability.""" - if global_vars['OS'].get('Arch', 32) == 64: - global_vars['Tools'] = { - k: v.get('64', v.get('32')) for (k, v) in TOOLS.items()} - else: - global_vars['Tools'] = {k: v.get('32') for (k, v) in TOOLS.items()} + """Set tool variables based on OS bit-depth and tool availability.""" + if global_vars['OS'].get('Arch', 32) == 64: + global_vars['Tools'] = { + k: v.get('64', v.get('32')) for (k, v) in TOOLS.items()} + else: + global_vars['Tools'] = {k: v.get('32') for (k, v) in TOOLS.items()} + + # Fix paths + global_vars['Tools'] = {k: os.path.join(global_vars['BinDir'], v) + for (k, v) in global_vars['Tools'].items()} - # Fix paths - global_vars['Tools'] = {k: os.path.join(global_vars['BinDir'], v) - for (k, v) in global_vars['Tools'].items()} def clean_env_vars(): - """Remove conflicting global_vars and env variables. + """Remove conflicting global_vars and env variables. + + This fixes an issue where both global_vars and + global_vars['Env'] are expanded at the same time.""" + for key in global_vars.keys(): + global_vars['Env'].pop(key, None) - This fixes an issue where both global_vars and - global_vars['Env'] are expanded at the same time.""" - for key in global_vars.keys(): - global_vars['Env'].pop(key, None) def find_bin(): - """Find .bin folder in the cwd or it's parents.""" - wd = os.getcwd() - base = None - while base is None: - if os.path.exists('.bin'): - base = os.getcwd() - break - if re.fullmatch(r'\w:\\', os.getcwd()): - break - os.chdir('..') - os.chdir(wd) - if base is None: - raise BinNotFoundError - global_vars['BaseDir'] = base + """Find .bin folder in the cwd or it's parents.""" + wd = os.getcwd() + base = None + while base is None: + if os.path.exists('.bin'): + base = os.getcwd() + break + if re.fullmatch(r'\w:\\', os.getcwd()): + break + os.chdir('..') + os.chdir(wd) + if base is None: + raise BinNotFoundError + global_vars['BaseDir'] = base + + +def generate_global_vars_report(): + """Build readable string from global_vars, returns str.""" + report = ['global_vars: {'] + for k, v in sorted(global_vars.items()): + if k == 'Env': + continue + if isinstance(v, list): + report.append(' {}: ['.format(str(k))) + for item in v: + report.append(' {}'.format(str(v))) + report.append(' ]') + elif isinstance(v, dict): + report.append(' {}: {{'.format(str(k))) + for item_k, item_v in sorted(v.items()): + report.append(' {:<15} {}'.format( + str(item_k)+':', str(item_v))) + report.append(' }') + else: + report.append(' {:<18}{}'.format(str(k)+':', str(v))) + report.append(' Env:') + for k, v in sorted(global_vars.get('Env', {}).items()): + report.append(' {:<15} {}'.format( + str(k)+':', str(v))) + report.append('}') + + return '\n'.join(report) + def make_tmp_dirs(): - """Make temp directories.""" - os.makedirs(global_vars['BackupDir'], exist_ok=True) - os.makedirs(global_vars['LogDir'], exist_ok=True) - os.makedirs(r'{}\{}'.format( - global_vars['LogDir'], KIT_NAME_FULL), exist_ok=True) - os.makedirs(r'{}\Tools'.format(global_vars['LogDir']), exist_ok=True) - os.makedirs(global_vars['TmpDir'], exist_ok=True) + """Make temp directories.""" + os.makedirs(global_vars['BackupDir'], exist_ok=True) + os.makedirs(global_vars['LogDir'], exist_ok=True) + os.makedirs(r'{}\{}'.format( + global_vars['LogDir'], KIT_NAME_FULL), exist_ok=True) + os.makedirs(r'{}\Tools'.format(global_vars['LogDir']), exist_ok=True) + os.makedirs(global_vars['TmpDir'], exist_ok=True) + def set_common_vars(): - """Set common variables.""" - global_vars['Date'] = time.strftime("%Y-%m-%d") - global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z") - global_vars['Env'] = os.environ.copy() + """Set common variables.""" + global_vars['Date'] = time.strftime("%Y-%m-%d") + global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z") + global_vars['Env'] = os.environ.copy() + + global_vars['ArchivePassword'] = ARCHIVE_PASSWORD + global_vars['BinDir'] = r'{BaseDir}\.bin'.format( + **global_vars) + global_vars['CBinDir'] = r'{BaseDir}\.cbin'.format( + **global_vars) + global_vars['ClientDir'] = r'{SYSTEMDRIVE}\{prefix}'.format( + prefix=KIT_NAME_SHORT, **global_vars['Env']) + global_vars['BackupDir'] = r'{ClientDir}\Backups'.format( + **global_vars) + global_vars['LogDir'] = r'{ClientDir}\Logs\{Date}'.format( + **global_vars) + global_vars['QuarantineDir'] = r'{ClientDir}\Quarantine'.format( + **global_vars) + global_vars['TmpDir'] = r'{BinDir}\tmp'.format( + **global_vars) - global_vars['ArchivePassword'] = ARCHIVE_PASSWORD - global_vars['BinDir'] = r'{BaseDir}\.bin'.format( - **global_vars) - global_vars['CBinDir'] = r'{BaseDir}\.cbin'.format( - **global_vars) - global_vars['ClientDir'] = r'{SYSTEMDRIVE}\{prefix}'.format( - prefix=KIT_NAME_SHORT, **global_vars['Env']) - global_vars['BackupDir'] = r'{ClientDir}\Backups'.format( - **global_vars) - global_vars['LogDir'] = r'{ClientDir}\Logs\{Date}'.format( - **global_vars) - global_vars['QuarantineDir'] = r'{ClientDir}\Quarantine'.format( - **global_vars) - global_vars['TmpDir'] = r'{BinDir}\tmp'.format( - **global_vars) def set_linux_vars(): - """Set common variables in a Linux environment. + """Set common variables in a Linux environment. + + These assume we're running under a WK-Linux build.""" + result = run_program(['mktemp', '-d']) + global_vars['TmpDir'] = result.stdout.decode().strip() + global_vars['Date'] = time.strftime("%Y-%m-%d") + global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z") + global_vars['Env'] = os.environ.copy() + global_vars['BinDir'] = '/usr/local/bin' + global_vars['LogDir'] = global_vars['TmpDir'] + global_vars['Tools'] = { + 'wimlib-imagex': 'wimlib-imagex', + 'SevenZip': '7z', + } - These assume we're running under a WK-Linux build.""" - result = run_program(['mktemp', '-d']) - global_vars['TmpDir'] = result.stdout.decode().strip() - global_vars['Date'] = time.strftime("%Y-%m-%d") - global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z") - global_vars['Env'] = os.environ.copy() - global_vars['BinDir'] = '/usr/local/bin' - global_vars['LogDir'] = global_vars['TmpDir'] - global_vars['Tools'] = { - 'wimlib-imagex': 'wimlib-imagex', - 'SevenZip': '7z', - } def set_log_file(log_name): - """Sets global var LogFile and creates path as needed.""" - folder_path = r'{}\{}'.format(global_vars['LogDir'], KIT_NAME_FULL) - log_file = r'{}\{}'.format(folder_path, log_name) - os.makedirs(folder_path, exist_ok=True) - global_vars['LogFile'] = log_file + """Sets global var LogFile and creates path as needed.""" + folder_path = '{}{}{}'.format( + global_vars['LogDir'], + os.sep, + KIT_NAME_FULL) + log_file = '{}{}{}'.format( + folder_path, + os.sep, + log_name) + os.makedirs(folder_path, exist_ok=True) + global_vars['LogFile'] = log_file + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/data.py b/.bin/Scripts/functions/data.py index 9557ebdb..7dcee3cf 100644 --- a/.bin/Scripts/functions/data.py +++ b/.bin/Scripts/functions/data.py @@ -3,884 +3,911 @@ import ctypes import json +from functions.common import * from operator import itemgetter -from functions.common import * - -# Classes -class LocalDisk(): - def __init__(self, disk): - self.disk = disk - self.name = disk.mountpoint.upper() - self.path = self.name - def is_dir(self): - # Should always be true - return True - def is_file(self): - # Should always be false - return False - -class SourceItem(): - def __init__(self, name, path): - self.name = name - self.path = path - -# Regex -REGEX_EXCL_ITEMS = re.compile( - r'^(\.(AppleDB|AppleDesktop|AppleDouble' - r'|com\.apple\.timemachine\.supported|dbfseventsd' - r'|DocumentRevisions-V100.*|DS_Store|fseventsd|PKInstallSandboxManager' - r'|Spotlight.*|SymAV.*|symSchedScanLockxz|TemporaryItems|Trash.*' - r'|vol|VolumeIcon\.icns)|desktop\.(ini|.*DB|.*DF)' - r'|(hiberfil|pagefile)\.sys|lost\+found|Network\.*Trash\.*Folder' - r'|Recycle[dr]|System\.*Volume\.*Information|Temporary\.*Items' - r'|Thumbs\.db)$', - re.IGNORECASE) -REGEX_EXCL_ROOT_ITEMS = re.compile( - r'^(boot(mgr|nxt)$|Config.msi' - r'|(eula|globdata|install|vc_?red)' - r'|.*.sys$|System Volume Information|RECYCLER?|\$Recycle\.bin' - r'|\$?Win(dows(.old.*|\.~BT|)$|RE_)|\$GetCurrent|Windows10Upgrade' - r'|PerfLogs|Program Files|SYSTEM.SAV' - r'|.*\.(esd|swm|wim|dd|map|dmg|image)$)', - re.IGNORECASE) -REGEX_INCL_ROOT_ITEMS = re.compile( - r'^(AdwCleaner|(My\s*|)(Doc(uments?( and Settings|)|s?)|Downloads' - r'|Media|Music|Pic(ture|)s?|Vid(eo|)s?)' - r'|{prefix}(-?Info|-?Transfer|)' - r'|(ProgramData|Recovery|Temp.*|Users)$' - r'|.*\.(log|txt|rtf|qb\w*|avi|m4a|m4v|mp4|mkv|jpg|png|tiff?)$)' - r''.format(prefix=KIT_NAME_SHORT), - re.IGNORECASE) -REGEX_WIM_FILE = re.compile( - r'\.wim$', - re.IGNORECASE) -REGEX_WINDOWS_OLD = re.compile( - r'^Win(dows|)\.old', - re.IGNORECASE) # STATIC VARIABLES FAST_COPY_EXCLUDES = [ - r'\*.esd', - r'\*.swm', - r'\*.wim', - r'\*.dd', - r'\*.dd.tgz', - r'\*.dd.txz', - r'\*.map', - r'\*.dmg', - r'\*.image', - r'$RECYCLE.BIN', - r'$Recycle.Bin', - r'.AppleDB', - r'.AppleDesktop', - r'.AppleDouble', - r'.com.apple.timemachine.supported', - r'.dbfseventsd', - r'.DocumentRevisions-V100*', - r'.DS_Store', - r'.fseventsd', - r'.PKInstallSandboxManager', - r'.Spotlight*', - r'.SymAV*', - r'.symSchedScanLockxz', - r'.TemporaryItems', - r'.Trash*', - r'.vol', - r'.VolumeIcon.icns', - r'desktop.ini', - r'Desktop?DB', - r'Desktop?DF', - r'hiberfil.sys', - r'lost+found', - r'Network?Trash?Folder', - r'pagefile.sys', - r'Recycled', - r'RECYCLER', - r'System?Volume?Information', - r'Temporary?Items', - r'Thumbs.db', - ] + r'\*.esd', + r'\*.swm', + r'\*.wim', + r'\*.dd', + r'\*.dd.tgz', + r'\*.dd.txz', + r'\*.map', + r'\*.dmg', + r'\*.image', + r'$RECYCLE.BIN', + r'$Recycle.Bin', + r'.AppleDB', + r'.AppleDesktop', + r'.AppleDouble', + r'.com.apple.timemachine.supported', + r'.dbfseventsd', + r'.DocumentRevisions-V100*', + r'.DS_Store', + r'.fseventsd', + r'.PKInstallSandboxManager', + r'.Spotlight*', + r'.SymAV*', + r'.symSchedScanLockxz', + r'.TemporaryItems', + r'.Trash*', + r'.vol', + r'.VolumeIcon.icns', + r'desktop.ini', + r'Desktop?DB', + r'Desktop?DF', + r'hiberfil.sys', + r'lost+found', + r'Network?Trash?Folder', + r'pagefile.sys', + r'Recycled', + r'RECYCLER', + r'System?Volume?Information', + r'Temporary?Items', + r'Thumbs.db', + ] FAST_COPY_ARGS = [ - '/cmd=noexist_only', - '/utf8', - '/skip_empty_dir', - '/linkdest', - '/no_ui', - '/auto_close', - '/exclude={}'.format(';'.join(FAST_COPY_EXCLUDES)), - ] + '/cmd=noexist_only', + '/utf8', + '/skip_empty_dir', + '/linkdest', + '/no_ui', + '/auto_close', + '/exclude={}'.format(';'.join(FAST_COPY_EXCLUDES)), + ] # Code borrowed from: https://stackoverflow.com/a/29075319 SEM_NORMAL = ctypes.c_uint() SEM_FAILCRITICALERRORS = 1 SEM_NOOPENFILEERRORBOX = 0x8000 SEM_FAIL = SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS + +# Regex +REGEX_EXCL_ITEMS = re.compile( + r'^(\.(AppleDB|AppleDesktop|AppleDouble' + r'|com\.apple\.timemachine\.supported|dbfseventsd' + r'|DocumentRevisions-V100.*|DS_Store|fseventsd|PKInstallSandboxManager' + r'|Spotlight.*|SymAV.*|symSchedScanLockxz|TemporaryItems|Trash.*' + r'|vol|VolumeIcon\.icns)|desktop\.(ini|.*DB|.*DF)' + r'|(hiberfil|pagefile)\.sys|lost\+found|Network\.*Trash\.*Folder' + r'|Recycle[dr]|System\.*Volume\.*Information|Temporary\.*Items' + r'|Thumbs\.db)$', + re.IGNORECASE) +REGEX_EXCL_ROOT_ITEMS = re.compile( + r'^(boot(mgr|nxt)$|Config.msi' + r'|(eula|globdata|install|vc_?red)' + r'|.*.sys$|System Volume Information|RECYCLER?|\$Recycle\.bin' + r'|\$?Win(dows(.old.*|\. BT|)$|RE_)|\$GetCurrent|Windows10Upgrade' + r'|PerfLogs|Program Files|SYSTEM.SAV' + r'|.*\.(esd|swm|wim|dd|map|dmg|image)$)', + re.IGNORECASE) +REGEX_INCL_ROOT_ITEMS = re.compile( + r'^(AdwCleaner|(My\s*|)(Doc(uments?( and Settings|)|s?)|Downloads' + r'|Media|Music|Pic(ture|)s?|Vid(eo|)s?)' + r'|{prefix}(-?Info|-?Transfer|)' + r'|(ProgramData|Recovery|Temp.*|Users)$' + r'|.*\.(log|txt|rtf|qb\w*|avi|m4a|m4v|mp4|mkv|jpg|png|tiff?)$)' + r''.format(prefix=KIT_NAME_SHORT), + re.IGNORECASE) +REGEX_WIM_FILE = re.compile( + r'\.wim$', + re.IGNORECASE) +REGEX_WINDOWS_OLD = re.compile( + r'^Win(dows|)\.old', + re.IGNORECASE) + + +# Classes +class LocalDisk(): + def __init__(self, disk): + self.disk = disk + self.name = disk.mountpoint.upper() + self.path = self.name + def is_dir(self): + # Should always be true + return True + def is_file(self): + # Should always be false + return False + + +class SourceItem(): + def __init__(self, name, path): + self.name = name + self.path = path + + +# Functions def cleanup_transfer(dest_path): - """Fix attributes and move extraneous items outside the Transfer folder.""" - try: - # Remove dest_path if empty - os.rmdir(dest_path) - except OSError: + """Fix attributes and move excluded items to separate folder.""" + try: + # Remove dest_path if empty + os.rmdir(dest_path) + except OSError: + pass + if not os.path.exists(dest_path): + # Bail if dest_path was empty and removed + raise Exception + + # Fix attributes + cmd = ['attrib', '-a', '-h', '-r', '-s', dest_path] + run_program(cmd, check=False) + + for root, dirs, files in os.walk(dest_path, topdown=False): + for name in dirs: + # Remove empty directories and junction points + try: + os.rmdir(os.path.join(root, name)) + except OSError: pass - if not os.path.exists(dest_path): - # Bail if dest_path was empty and removed - raise Exception + for name in files: + # "Remove" files based on exclusion regex + if REGEX_EXCL_ITEMS.search(name): + # Make dest folder + dest_name = root.replace(dest_path, dest_path+'.Removed') + os.makedirs(dest_name, exist_ok=True) + # Set dest filename + dest_name = os.path.join(dest_name, name) + dest_name = non_clobber_rename(dest_name) + source_name = os.path.join(root, name) + try: + shutil.move(source_name, dest_name) + except Exception: + pass - # Fix attributes - cmd = ['attrib', '-a', '-h', '-r', '-s', dest_path] - run_program(cmd, check=False) - - for root, dirs, files in os.walk(dest_path, topdown=False): - for name in dirs: - # Remove empty directories and junction points - try: - os.rmdir(os.path.join(root, name)) - except OSError: - pass - for name in files: - # "Remove" files based on exclusion regex - if REGEX_EXCL_ITEMS.search(name): - # Make dest folder - dest_name = root.replace(dest_path, dest_path+'.Removed') - os.makedirs(dest_name, exist_ok=True) - # Set dest filename - dest_name = os.path.join(dest_name, name) - dest_name = non_clobber_rename(dest_name) - source_name = os.path.join(root, name) - try: - shutil.move(source_name, dest_name) - except Exception: - pass def find_core_storage_volumes(device_path=None): - """Try to create block devices for any Apple CoreStorage volumes.""" - corestorage_uuid = '53746f72-6167-11aa-aa11-00306543ecac' - dmsetup_cmd_file = '{TmpDir}/dmsetup_command'.format(**global_vars) + """Try to create block devices for any Apple CoreStorage volumes.""" + corestorage_uuid = '53746f72-6167-11aa-aa11-00306543ecac' + dmsetup_cmd_file = '{TmpDir}/dmsetup_command'.format(**global_vars) - # Get CoreStorage devices + # Get CoreStorage devices + cmd = [ + 'lsblk', '--json', '--list', '--paths', + '--output', 'NAME,PARTTYPE'] + if device_path: + cmd.append(device_path) + result = run_program(cmd) + json_data = json.loads(result.stdout.decode()) + devs = json_data.get('blockdevices', []) + devs = [d for d in devs if d.get('parttype', '') == corestorage_uuid] + if devs: + print_standard(' ') + print_standard('Detected CoreStorage partition{}'.format( + '' if len(devs) == 1 else 's')) + print_standard(' Scanning for inner volume(s)....') + + # Search for inner volumes and setup dev mappers + for dev in devs: + dev_path = dev.get('name', '') + if not dev_path: + # Can't setup block device without the dev path + continue + dev_name = re.sub(r'.*/', '', dev_path) + log_path = '{LogDir}/testdisk_{dev_name}.log'.format( + dev_name=dev_name, **global_vars) + + # Run TestDisk cmd = [ - 'lsblk', '--json', '--list', '--paths', - '--output', 'NAME,PARTTYPE'] - if device_path: - cmd.append(device_path) - result = run_program(cmd) - json_data = json.loads(result.stdout.decode()) - devs = json_data.get('blockdevices', []) - devs = [d for d in devs if d.get('parttype', '') == corestorage_uuid] - if devs: - print_standard(' ') - print_standard('Detected CoreStorage partition{}'.format( - '' if len(devs) == 1 else 's')) - print_standard(' Scanning for inner volume(s)....') + 'sudo', 'testdisk', + '/logname', log_path, '/debug', '/log', + '/cmd', dev_path, 'partition_none,analyze'] + result = run_program(cmd, check=False) + if result.returncode: + # i.e. return code is non-zero + continue + if not os.path.exists(log_path): + # TestDisk failed to write log + continue - # Search for inner volumes and setup dev mappers - for dev in devs: - dev_path = dev.get('name', '') - if not dev_path: - # Can't setup block device without the dev path - continue - dev_name = re.sub(r'.*/', '', dev_path) - log_path = '{LogDir}/testdisk_{dev_name}.log'.format( - dev_name=dev_name, **global_vars) + # Check log for found volumes + cs_vols = {} + with open(log_path, 'r') as f: + for line in f.readlines(): + r = re.match( + r'^.*echo "([^"]+)" . dmsetup create test(\d)$', + line.strip(), + re.IGNORECASE) + if r: + cs_name = 'CoreStorage_{}_{}'.format(dev_name, r.group(2)) + cs_vols[cs_name] = r.group(1) - # Run TestDisk - cmd = [ - 'sudo', 'testdisk', - '/logname', log_path, '/debug', '/log', - '/cmd', dev_path, 'partition_none,analyze'] - result = run_program(cmd, check=False) - if result.returncode: - # i.e. return code is non-zero - continue - if not os.path.exists(log_path): - # TestDisk failed to write log - continue + # Create mapper device(s) + for name, dm_cmd in sorted(cs_vols.items()): + with open(dmsetup_cmd_file, 'w') as f: + f.write(dm_cmd) + cmd = ['sudo', 'dmsetup', 'create', name, dmsetup_cmd_file] + run_program(cmd, check=False) - # Check log for found volumes - cs_vols = {} - with open(log_path, 'r') as f: - for line in f.readlines(): - r = re.match( - r'^.*echo "([^"]+)" . dmsetup create test(\d)$', - line.strip(), - re.IGNORECASE) - if r: - cs_name = 'CoreStorage_{}_{}'.format(dev_name, r.group(2)) - cs_vols[cs_name] = r.group(1) - - # Create mapper device(s) - for name, dm_cmd in sorted(cs_vols.items()): - with open(dmsetup_cmd_file, 'w') as f: - f.write(dm_cmd) - cmd = ['sudo', 'dmsetup', 'create', name, dmsetup_cmd_file] - run_program(cmd, check=False) def fix_path_sep(path_str): - """Replace non-native and duplicate dir separators, returns str.""" - return re.sub(r'(\\|/)+', lambda s: os.sep, path_str) + """Replace non-native and duplicate dir separators, returns str.""" + return re.sub(r'(\\|/)+', lambda s: os.sep, path_str) + def is_valid_wim_file(item): - """Checks if the provided os.DirEntry is a valid WIM file, returns bool.""" - valid = bool(item.is_file() and REGEX_WIM_FILE.search(item.name)) - if valid: - extract_item('wimlib', silent=True) - cmd = [global_vars['Tools']['wimlib-imagex'], 'info', item.path] - try: - run_program(cmd) - except subprocess.CalledProcessError: - valid = False - print_log('WARNING: Image "{}" damaged.'.format(item.name)) - return valid + """Checks if the item is a valid WIM file, returns bool.""" + valid = bool(item.is_file() and REGEX_WIM_FILE.search(item.name)) + if valid: + extract_item('wimlib', silent=True) + cmd = [global_vars['Tools']['wimlib-imagex'], 'info', item.path] + try: + run_program(cmd) + except subprocess.CalledProcessError: + valid = False + print_log('WARNING: Image "{}" damaged.'.format(item.name)) + return valid + def get_mounted_volumes(): - """Get mounted volumes, returns dict.""" - cmd = [ - 'findmnt', '-J', '-b', '-i', - '-t', ( - 'autofs,binfmt_misc,bpf,cgroup,cgroup2,configfs,debugfs,devpts,devtmpfs,' - 'hugetlbfs,mqueue,proc,pstore,securityfs,sysfs,tmpfs' - ), - '-o', 'SOURCE,TARGET,FSTYPE,LABEL,SIZE,AVAIL,USED'] - result = run_program(cmd) - json_data = json.loads(result.stdout.decode()) - mounted_volumes = [] - for item in json_data.get('filesystems', []): - mounted_volumes.append(item) - mounted_volumes.extend(item.get('children', [])) - return {item['source']: item for item in mounted_volumes} + """Get mounted volumes, returns dict.""" + cmd = [ + 'findmnt', '-J', '-b', '-i', + '-t', ( + 'autofs,binfmt_misc,bpf,cgroup,cgroup2,configfs,debugfs,devpts,' + 'devtmpfs,hugetlbfs,mqueue,proc,pstore,securityfs,sysfs,tmpfs' + ), + '-o', 'SOURCE,TARGET,FSTYPE,LABEL,SIZE,AVAIL,USED'] + result = run_program(cmd) + json_data = json.loads(result.stdout.decode()) + mounted_volumes = [] + for item in json_data.get('filesystems', []): + mounted_volumes.append(item) + mounted_volumes.extend(item.get('children', [])) + return {item['source']: item for item in mounted_volumes} + def mount_volumes( - all_devices=True, device_path=None, - read_write=False, core_storage=True): - """Mount all detected filesystems.""" - report = {} - cmd = [ - 'lsblk', '--json', '--paths', - '--output', 'NAME,FSTYPE,LABEL,UUID,PARTTYPE,TYPE,SIZE'] - if not all_devices and device_path: - # Only mount volumes for specific device - cmd.append(device_path) + all_devices=True, device_path=None, + read_write=False, core_storage=True): + """Mount all detected filesystems.""" + report = {} + cmd = [ + 'lsblk', '--json', '--paths', + '--output', 'NAME,FSTYPE,LABEL,UUID,PARTTYPE,TYPE,SIZE'] + if not all_devices and device_path: + # Only mount volumes for specific device + cmd.append(device_path) - # Check for Apple CoreStorage volumes first - if core_storage: - find_core_storage_volumes(device_path) + # Check for Apple CoreStorage volumes first + if core_storage: + find_core_storage_volumes(device_path) - # Get list of block devices - result = run_program(cmd) - json_data = json.loads(result.stdout.decode()) - devs = json_data.get('blockdevices', []) + # Get list of block devices + result = run_program(cmd) + json_data = json.loads(result.stdout.decode()) + devs = json_data.get('blockdevices', []) - # Get list of volumes - volumes = {} - for dev in devs: - if not dev.get('children', []): - volumes.update({dev['name']: dev}) - for child in dev.get('children', []): - if not child.get('children', []): - volumes.update({child['name']: child}) - for grandchild in child.get('children', []): - volumes.update({grandchild['name']: grandchild}) + # Get list of volumes + volumes = {} + for dev in devs: + if not dev.get('children', []): + volumes.update({dev['name']: dev}) + for child in dev.get('children', []): + if not child.get('children', []): + volumes.update({child['name']: child}) + for grandchild in child.get('children', []): + volumes.update({grandchild['name']: grandchild}) - # Get list of mounted volumes - mounted_volumes = get_mounted_volumes() + # Get list of mounted volumes + mounted_volumes = get_mounted_volumes() - # Loop over volumes - for vol_path, vol_data in volumes.items(): - vol_data['show_data'] = { - 'message': vol_path.replace('/dev/mapper/', ''), - 'data': None, - } - if re.search(r'^loop\d', vol_path, re.IGNORECASE): - # Skip loopback devices - vol_data['show_data']['data'] = 'Skipped' - vol_data['show_data']['warning'] = True - report[vol_path] = vol_data - elif 'children' in vol_data: - # Skip LVM/RAID partitions (the real volume is mounted separately) - vol_data['show_data']['data'] = vol_data.get('fstype', 'UNKNOWN') - if vol_data.get('label', None): - vol_data['show_data']['data'] += ' "{}"'.format(vol_data['label']) - vol_data['show_data']['info'] = True - report[vol_path] = vol_data - else: - if vol_path in mounted_volumes: - vol_data['show_data']['warning'] = True - else: - # Mount volume - cmd = ['udevil', 'mount', - '-o', 'rw' if read_write else 'ro', - vol_path] - try: - run_program(cmd) - except subprocess.CalledProcessError: - vol_data['show_data']['data'] = 'Failed to mount' - vol_data['show_data']['error'] = True - # Update mounted_volumes data - mounted_volumes = get_mounted_volumes() + # Loop over volumes + for vol_path, vol_data in volumes.items(): + vol_data['show_data'] = { + 'message': vol_path.replace('/dev/mapper/', ''), + 'data': None, + } + if re.search(r'^loop\d', vol_path, re.IGNORECASE): + # Skip loopback devices + vol_data['show_data']['data'] = 'Skipped' + vol_data['show_data']['warning'] = True + report[vol_path] = vol_data + elif 'children' in vol_data: + # Skip LVM/RAID partitions (the real volume is mounted separately) + vol_data['show_data']['data'] = vol_data.get('fstype', 'UNKNOWN') + if vol_data.get('label', None): + vol_data['show_data']['data'] += ' "{}"'.format(vol_data['label']) + vol_data['show_data']['info'] = True + report[vol_path] = vol_data + else: + if vol_path in mounted_volumes: + vol_data['show_data']['warning'] = True + else: + # Mount volume + cmd = ['udevil', 'mount', + '-o', 'rw' if read_write else 'ro', + vol_path] + try: + run_program(cmd) + except subprocess.CalledProcessError: + vol_data['show_data']['data'] = 'Failed to mount' + vol_data['show_data']['error'] = True + # Update mounted_volumes data + mounted_volumes = get_mounted_volumes() - # Format pretty result string - if vol_data['show_data']['data'] == 'Failed to mount': - vol_data['mount_point'] = None - else: - size_used = human_readable_size( - mounted_volumes[vol_path]['used']) - size_avail = human_readable_size( - mounted_volumes[vol_path]['avail']) - vol_data['size_avail'] = size_avail - vol_data['size_used'] = size_used - vol_data['mount_point'] = mounted_volumes[vol_path]['target'] - vol_data['show_data']['data'] = 'Mounted on {}'.format( - mounted_volumes[vol_path]['target']) - vol_data['show_data']['data'] = '{:40} ({} used, {} free)'.format( - vol_data['show_data']['data'], - size_used, - size_avail) + # Format pretty result string + if vol_data['show_data']['data'] == 'Failed to mount': + vol_data['mount_point'] = None + else: + size_used = human_readable_size( + mounted_volumes[vol_path]['used']) + size_avail = human_readable_size( + mounted_volumes[vol_path]['avail']) + vol_data['size_avail'] = size_avail + vol_data['size_used'] = size_used + vol_data['mount_point'] = mounted_volumes[vol_path]['target'] + vol_data['show_data']['data'] = 'Mounted on {}'.format( + mounted_volumes[vol_path]['target']) + vol_data['show_data']['data'] = '{:40} ({} used, {} free)'.format( + vol_data['show_data']['data'], + size_used, + size_avail) - # Update report - report[vol_path] = vol_data + # Update report + report[vol_path] = vol_data + + return report - return report def mount_backup_shares(read_write=False): - """Mount the backup shares unless labeled as already mounted.""" + """Mount the backup shares unless labeled as already mounted.""" + if psutil.LINUX: + mounted_volumes = get_mounted_volumes() + for server in BACKUP_SERVERS: if psutil.LINUX: - mounted_volumes = get_mounted_volumes() - for server in BACKUP_SERVERS: - if psutil.LINUX: - # Update mounted status - source = '//{IP}/{Share}'.format(**server) - dest = '/Backups/{Name}'.format(**server) - mounted_str = '(Already) Mounted {}'.format(dest) - data = mounted_volumes.get(source, {}) - if dest == data.get('target', ''): - server['Mounted'] = True - elif psutil.WINDOWS: - mounted_str = '(Already) Mounted {Name}'.format(**server) - if server['Mounted']: - print_warning(mounted_str) - continue + # Update mounted status + source = '//{IP}/{Share}'.format(**server) + dest = '/Backups/{Name}'.format(**server) + mounted_str = '(Already) Mounted {}'.format(dest) + data = mounted_volumes.get(source, {}) + if dest == data.get('target', ''): + server['Mounted'] = True + elif psutil.WINDOWS: + mounted_str = '(Already) Mounted {Name}'.format(**server) + if server['Mounted']: + print_warning(mounted_str) + continue + + mount_network_share(server, read_write) - mount_network_share(server, read_write) def mount_network_share(server, read_write=False): - """Mount a network share defined by server.""" - if read_write: - username = server['RW-User'] - password = server['RW-Pass'] - else: - username = server['User'] - password = server['Pass'] - if psutil.WINDOWS: - cmd = [ - 'net', 'use', r'\\{IP}\{Share}'.format(**server), - '/user:{}'.format(username), password] - warning = r'Failed to mount \\{Name}\{Share}, {IP} unreachable.'.format( - **server) - error = r'Failed to mount \\{Name}\{Share} ({IP})'.format(**server) - success = 'Mounted {Name}'.format(**server) - elif psutil.LINUX: - cmd = [ - 'sudo', 'mkdir', '-p', - '/Backups/{Name}'.format(**server)] - run_program(cmd) - cmd = [ - 'sudo', 'mount', - '//{IP}/{Share}'.format(**server), - '/Backups/{Name}'.format(**server), - '-o', '{}username={},password={}'.format( - '' if read_write else 'ro,', - username, - password)] - warning = 'Failed to mount /Backups/{Name}, {IP} unreachable.'.format( - **server) - error = 'Failed to mount /Backups/{Name}'.format(**server) - success = 'Mounted /Backups/{Name}'.format(**server) + """Mount a network share defined by server.""" + if read_write: + username = server['RW-User'] + password = server['RW-Pass'] + else: + username = server['User'] + password = server['Pass'] + if psutil.WINDOWS: + cmd = [ + 'net', 'use', r'\\{IP}\{Share}'.format(**server), + '/user:{}'.format(username), password] + warning = r'Failed to mount \\{Name}\{Share}, {IP} unreachable.'.format( + **server) + error = r'Failed to mount \\{Name}\{Share} ({IP})'.format(**server) + success = 'Mounted {Name}'.format(**server) + elif psutil.LINUX: + cmd = [ + 'sudo', 'mkdir', '-p', + '/Backups/{Name}'.format(**server)] + run_program(cmd) + cmd = [ + 'sudo', 'mount', + '//{IP}/{Share}'.format(**server), + '/Backups/{Name}'.format(**server), + '-o', '{}username={},password={}'.format( + '' if read_write else 'ro,', + username, + password)] + warning = 'Failed to mount /Backups/{Name}, {IP} unreachable.'.format( + **server) + error = 'Failed to mount /Backups/{Name}'.format(**server) + success = 'Mounted /Backups/{Name}'.format(**server) - # Test connection - try: - ping(server['IP']) - except subprocess.CalledProcessError: - print_warning(warning) - sleep(1) - return False + # Test connection + try: + ping(server['IP']) + except subprocess.CalledProcessError: + print_warning(warning) + sleep(1) + return False + + # Mount + try: + run_program(cmd) + except Exception: + print_error(error) + sleep(1) + else: + print_info(success) + server['Mounted'] = True - # Mount - try: - run_program(cmd) - except Exception: - print_error(error) - sleep(1) - else: - print_info(success) - server['Mounted'] = True def run_fast_copy(items, dest): - """Copy items to dest using FastCopy.""" - if not items: - raise Exception + """Copy items to dest using FastCopy.""" + if not items: + raise Exception - cmd = [global_vars['Tools']['FastCopy'], *FAST_COPY_ARGS] - cmd.append(r'/logfile={LogDir}\Tools\FastCopy.log'.format(**global_vars)) - cmd.extend(items) - cmd.append('/to={}\\'.format(dest)) + cmd = [global_vars['Tools']['FastCopy'], *FAST_COPY_ARGS] + cmd.append(r'/logfile={LogDir}\Tools\FastCopy.log'.format(**global_vars)) + cmd.extend(items) + cmd.append('/to={}\\'.format(dest)) + + run_program(cmd) - run_program(cmd) def run_wimextract(source, items, dest): - """Extract items from source WIM to dest folder.""" - if not items: - raise Exception - extract_item('wimlib', silent=True) + """Extract items from source WIM to dest folder.""" + if not items: + raise Exception + extract_item('wimlib', silent=True) - # Write files.txt - with open(r'{}\wim_files.txt'.format(global_vars['TmpDir']), 'w', - encoding='utf-8') as f: - # Defaults - for item in items: - f.write('{}\n'.format(item)) - sleep(1) # For safety? + # Write files.txt + with open(r'{}\wim_files.txt'.format(global_vars['TmpDir']), 'w', + encoding='utf-8') as f: + # Defaults + for item in items: + f.write('{}\n'.format(item)) + sleep(1) # For safety? + + # Extract files + cmd = [ + global_vars['Tools']['wimlib-imagex'], + 'extract', + source, '1', + r'@{}\wim_files.txt'.format(global_vars['TmpDir']), + '--dest-dir={}\\'.format(dest), + '--no-acls', + '--nullglob'] + run_program(cmd) - # Extract files - cmd = [ - global_vars['Tools']['wimlib-imagex'], - 'extract', - source, '1', - r'@{}\wim_files.txt'.format(global_vars['TmpDir']), - '--dest-dir={}\\'.format(dest), - '--no-acls', - '--nullglob'] - run_program(cmd) def list_source_items(source_obj, rel_path=None): - """List items in a dir or WIM, returns a list of SourceItem objects.""" - items = [] - rel_path = '{}{}'.format(os.sep, rel_path) if rel_path else '' - if source_obj.is_dir(): - source_path = '{}{}'.format(source_obj.path, rel_path) - items = [SourceItem(name=item.name, path=item.path) - for item in os.scandir(source_path)] - else: - # Prep wimlib-imagex - if psutil.WINDOWS: - extract_item('wimlib', silent=True) - cmd = [ - global_vars['Tools']['wimlib-imagex'], 'dir', - source_obj.path, '1'] - if rel_path: - cmd.append('--path={}'.format(rel_path)) + """List items in a dir or WIM, returns list of SourceItem objects.""" + items = [] + rel_path = '{}{}'.format(os.sep, rel_path) if rel_path else '' + if source_obj.is_dir(): + source_path = '{}{}'.format(source_obj.path, rel_path) + items = [SourceItem(name=item.name, path=item.path) + for item in os.scandir(source_path)] + else: + # Prep wimlib-imagex + if psutil.WINDOWS: + extract_item('wimlib', silent=True) + cmd = [ + global_vars['Tools']['wimlib-imagex'], 'dir', + source_obj.path, '1'] + if rel_path: + cmd.append('--path={}'.format(rel_path)) - # Get item list - try: - items = run_program(cmd) - except subprocess.CalledProcessError: - print_error('ERROR: Failed to get file list.') - raise + # Get item list + try: + items = run_program(cmd) + except subprocess.CalledProcessError: + print_error('ERROR: Failed to get file list.') + raise - # Strip non-root items - items = [fix_path_sep(i.strip()) - for i in items.stdout.decode('utf-8', 'ignore').splitlines()] - if rel_path: - items = [i.replace(rel_path, '') for i in items] - items = [i for i in items - if i.count(os.sep) == 1 and i.strip() != os.sep] - items = [SourceItem(name=i[1:], path=rel_path+i) for i in items] + # Strip non-root items + items = [fix_path_sep(i.strip()) + for i in items.stdout.decode('utf-8', 'ignore').splitlines()] + if rel_path: + items = [i.replace(rel_path, '') for i in items] + items = [i for i in items + if i.count(os.sep) == 1 and i.strip() != os.sep] + items = [SourceItem(name=i[1:], path=rel_path+i) for i in items] + + # Done + return items - # Done - return items def scan_source(source_obj, dest_path, rel_path='', interactive=True): - """Scan source for files/folders to transfer, returns list. + """Scan source for files/folders to transfer, returns list. - This will scan the root and (recursively) any Windows.old folders.""" - selected_items = [] - win_olds = [] + This will scan the root and (recursively) any Windows.old folders.""" + selected_items = [] + win_olds = [] - # Root Items - root_items = [] - item_list = list_source_items(source_obj, rel_path) - for item in item_list: - if REGEX_WINDOWS_OLD.search(item.name): - item.name = '{}{}{}'.format( - rel_path, - os.sep if rel_path else '', - item.name) - win_olds.append(item) - elif REGEX_INCL_ROOT_ITEMS.search(item.name): - print_success('Auto-Selected: {}'.format(item.path)) - root_items.append('{}'.format(item.path)) - elif not REGEX_EXCL_ROOT_ITEMS.search(item.name): - if not interactive: - print_success('Auto-Selected: {}'.format(item.path)) - root_items.append('{}'.format(item.path)) - else: - prompt = 'Transfer: "{}{}{}" ?'.format( - rel_path, - os.sep if rel_path else '', - item.name) - choices = ['Yes', 'No', 'All', 'Quit'] - answer = choice(prompt=prompt, choices=choices) - if answer == 'Quit': - abort() - elif answer == 'All': - interactive = False - if answer in ['Yes', 'All']: - root_items.append('{}'.format(item.path)) - if root_items: - selected_items.append({ - 'Message': '{}{}Root Items...'.format( - rel_path, - ' ' if rel_path else ''), - 'Items': root_items.copy(), - 'Destination': dest_path}) + # Root Items + root_items = [] + item_list = list_source_items(source_obj, rel_path) + for item in item_list: + if REGEX_WINDOWS_OLD.search(item.name): + item.name = '{}{}{}'.format( + rel_path, + os.sep if rel_path else '', + item.name) + win_olds.append(item) + elif REGEX_INCL_ROOT_ITEMS.search(item.name): + print_success('Auto-Selected: {}'.format(item.path)) + root_items.append('{}'.format(item.path)) + elif not REGEX_EXCL_ROOT_ITEMS.search(item.name): + if not interactive: + print_success('Auto-Selected: {}'.format(item.path)) + root_items.append('{}'.format(item.path)) + else: + prompt = 'Transfer: "{}{}{}" ?'.format( + rel_path, + os.sep if rel_path else '', + item.name) + choices = ['Yes', 'No', 'All', 'Quit'] + answer = choice(prompt=prompt, choices=choices) + if answer == 'Quit': + abort() + elif answer == 'All': + interactive = False + if answer in ['Yes', 'All']: + root_items.append('{}'.format(item.path)) + if root_items: + selected_items.append({ + 'Message': '{}{}Root Items...'.format( + rel_path, + ' ' if rel_path else ''), + 'Items': root_items.copy(), + 'Destination': dest_path}) - # Fonts - font_obj = get_source_item_obj(source_obj, rel_path, 'Windows/Fonts') - if font_obj: - selected_items.append({ - 'Message': '{}{}Fonts...'.format( - rel_path, - ' ' if rel_path else ''), - 'Items': [font_obj.path], - 'Destination': '{}{}Windows'.format( - dest_path, os.sep)}) + # Fonts + font_obj = get_source_item_obj(source_obj, rel_path, 'Windows/Fonts') + if font_obj: + selected_items.append({ + 'Message': '{}{}Fonts...'.format( + rel_path, + ' ' if rel_path else ''), + 'Items': [font_obj.path], + 'Destination': '{}{}Windows'.format( + dest_path, os.sep)}) - # Registry - registry_items = [] - for folder in ['config', 'OEM']: - folder_obj = get_source_item_obj( - source_obj, rel_path, 'Windows/System32/{}'.format(folder)) - if folder_obj: - registry_items.append(folder_obj.path) - if registry_items: - selected_items.append({ - 'Message': '{}{}Registry...'.format( - rel_path, - ' ' if rel_path else ''), - 'Items': registry_items.copy(), - 'Destination': '{}{}Windows{}System32'.format( - dest_path, os.sep, os.sep)}) + # Registry + registry_items = [] + for folder in ['config', 'OEM']: + folder_obj = get_source_item_obj( + source_obj, rel_path, 'Windows/System32/{}'.format(folder)) + if folder_obj: + registry_items.append(folder_obj.path) + if registry_items: + selected_items.append({ + 'Message': '{}{}Registry...'.format( + rel_path, + ' ' if rel_path else ''), + 'Items': registry_items.copy(), + 'Destination': '{}{}Windows{}System32'.format( + dest_path, os.sep, os.sep)}) - # Windows.old(s) - for old in win_olds: - selected_items.extend(scan_source( - source_obj, - '{}{}{}'.format(dest_path, os.sep, old.name), - rel_path=old.name, - interactive=False)) + # Windows.old(s) + for old in win_olds: + selected_items.extend(scan_source( + source_obj, + '{}{}{}'.format(dest_path, os.sep, old.name), + rel_path=old.name, + interactive=False)) + + # Done + return selected_items - # Done - return selected_items def get_source_item_obj(source_obj, rel_path, item_path): - """Check if the item exists and return a SourceItem object if it does.""" - item_obj = None - item_path = fix_path_sep(item_path) - if source_obj.is_dir(): - item_obj = SourceItem( - name = item_path, - path = '{}{}{}{}{}'.format( - source_obj.path, - os.sep, - rel_path, - os.sep if rel_path else '', - item_path)) - if not os.path.exists(item_obj.path): - item_obj = None + """Check if the item exists, returns SourceItem object or None.""" + item_obj = None + item_path = fix_path_sep(item_path) + if source_obj.is_dir(): + item_obj = SourceItem( + name = item_path, + path = '{}{}{}{}{}'.format( + source_obj.path, + os.sep, + rel_path, + os.sep if rel_path else '', + item_path)) + if not os.path.exists(item_obj.path): + item_obj = None + else: + # Assuming WIM file + if psutil.WINDOWS: + extract_item('wimlib', silent=True) + cmd = [ + global_vars['Tools']['wimlib-imagex'], 'dir', + source_obj.path, '1', + '--path={}'.format(item_path), + '--one-file-only'] + try: + run_program(cmd) + except subprocess.CalledProcessError: + # function will return None below + pass else: - # Assuming WIM file - if psutil.WINDOWS: - extract_item('wimlib', silent=True) - cmd = [ - global_vars['Tools']['wimlib-imagex'], 'dir', - source_obj.path, '1', - '--path={}'.format(item_path), - '--one-file-only'] - try: - run_program(cmd) - except subprocess.CalledProcessError: - # function will return None below - pass - else: - item_obj = SourceItem( - name = item_path, - path = '{}{}{}{}'.format( - os.sep, - rel_path, - os.sep if rel_path else '', - item_path)) - return item_obj + item_obj = SourceItem( + name = item_path, + path = '{}{}{}{}'.format( + os.sep, + rel_path, + os.sep if rel_path else '', + item_path)) + return item_obj + def select_destination(folder_path, prompt='Select destination'): - """Select destination drive, returns path as string.""" - disk = select_volume(prompt) - if 'fixed' not in disk['Disk'].opts: - folder_path = folder_path.replace('\\', '-') - path = '{disk}{folder_path}_{Date}'.format( - disk = disk['Disk'].mountpoint, - folder_path = folder_path, - **global_vars) + """Select destination drive, returns path as string.""" + disk = select_volume(prompt) + if 'fixed' not in disk['Disk'].opts: + folder_path = folder_path.replace('\\', '-') + path = '{disk}{folder_path}_{Date}'.format( + disk = disk['Disk'].mountpoint, + folder_path = folder_path, + **global_vars) - # Avoid merging with existing folder - path = non_clobber_rename(path) - os.makedirs(path, exist_ok=True) + # Avoid merging with existing folder + path = non_clobber_rename(path) + os.makedirs(path, exist_ok=True) + + return path - return path def select_source(backup_prefix): - """Select backup from those found on the BACKUP_SERVERS matching the prefix.""" - selected_source = None - local_sources = [] - remote_sources = [] - sources = [] - mount_backup_shares(read_write=False) + """Select matching backup from BACKUP_SERVERS, returns obj.""" + selected_source = None + local_sources = [] + remote_sources = [] + sources = [] + mount_backup_shares(read_write=False) - # Check for prefix folders on servers - for server in BACKUP_SERVERS: - if server['Mounted']: - print_standard('Scanning {}...'.format(server['Name'])) - for d in os.scandir(r'\\{IP}\{Share}'.format(**server)): - if (d.is_dir() - and d.name.lower().startswith(backup_prefix.lower())): - # Add folder to remote_sources - remote_sources.append({ - 'Name': '{:9}| File-Based: [DIR] {}'.format( - server['Name'], d.name), - 'Server': server, - 'Sort': d.name, - 'Source': d}) + # Check for prefix folders on servers + for server in BACKUP_SERVERS: + if server['Mounted']: + print_standard('Scanning {}...'.format(server['Name'])) + for d in os.scandir(r'\\{IP}\{Share}'.format(**server)): + if (d.is_dir() + and d.name.lower().startswith(backup_prefix.lower())): + # Add folder to remote_sources + remote_sources.append({ + 'Name': '{:9}| File-Based: [DIR] {}'.format( + server['Name'], d.name), + 'Server': server, + 'Sort': d.name, + 'Source': d}) - # Check for images and subfolders - for prefix_path in remote_sources.copy(): - for item in os.scandir(prefix_path['Source'].path): - if item.is_dir(): - # Add folder to remote_sources - remote_sources.append({ - 'Name': r'{:9}| File-Based: [DIR] {}\{}'.format( - prefix_path['Server']['Name'], # Server - prefix_path['Source'].name, # Prefix folder - item.name, # Sub-folder - ), - 'Server': prefix_path['Server'], - 'Sort': r'{}\{}'.format( - prefix_path['Source'].name, # Prefix folder - item.name, # Sub-folder - ), - 'Source': item}) + # Check for images and subfolders + for prefix_path in remote_sources.copy(): + for item in os.scandir(prefix_path['Source'].path): + if item.is_dir(): + # Add folder to remote_sources + remote_sources.append({ + 'Name': r'{:9}| File-Based: [DIR] {}\{}'.format( + prefix_path['Server']['Name'], # Server + prefix_path['Source'].name, # Prefix folder + item.name, # Sub-folder + ), + 'Server': prefix_path['Server'], + 'Sort': r'{}\{}'.format( + prefix_path['Source'].name, # Prefix folder + item.name, # Sub-folder + ), + 'Source': item}) - # Check for images in folder - for subitem in os.scandir(item.path): - if REGEX_WIM_FILE.search(item.name): - # Add image to remote_sources - try: - size = human_readable_size(item.stat().st_size) - except Exception: - size = ' ? ?' # unknown - remote_sources.append({ - 'Disabled': bool(not is_valid_wim_file(subitem)), - 'Name': r'{:9}| Image-Based: {:>7} {}\{}\{}'.format( - prefix_path['Server']['Name'], # Server - size, # Size (duh) - prefix_path['Source'].name, # Prefix folder - item.name, # Sub-folder - subitem.name, # Image file - ), - 'Server': prefix_path['Server'], - 'Sort': r'{}\{}\{}'.format( - prefix_path['Source'].name, # Prefix folder - item.name, # Sub-folder - subitem.name, # Image file - ), - 'Source': subitem}) - elif REGEX_WIM_FILE.search(item.name): - # Add image to remote_sources - try: - size = human_readable_size(item.stat().st_size) - except Exception: - size = ' ? ?' # unknown - remote_sources.append({ - 'Disabled': bool(not is_valid_wim_file(item)), - 'Name': r'{:9}| Image-Based: {:>7} {}\{}'.format( - prefix_path['Server']['Name'], # Server - size, # Size (duh) - prefix_path['Source'].name, # Prefix folder - item.name, # Image file - ), - 'Server': prefix_path['Server'], - 'Sort': r'{}\{}'.format( - prefix_path['Source'].name, # Prefix folder - item.name, # Image file - ), - 'Source': item}) + # Check for images in folder + for subitem in os.scandir(item.path): + if REGEX_WIM_FILE.search(item.name): + # Add image to remote_sources + try: + size = human_readable_size(item.stat().st_size) + except Exception: + size = ' ? ?' # unknown + remote_sources.append({ + 'Disabled': bool(not is_valid_wim_file(subitem)), + 'Name': r'{:9}| Image-Based: {:>7} {}\{}\{}'.format( + prefix_path['Server']['Name'], # Server + size, # Size (duh) + prefix_path['Source'].name, # Prefix folder + item.name, # Sub-folder + subitem.name, # Image file + ), + 'Server': prefix_path['Server'], + 'Sort': r'{}\{}\{}'.format( + prefix_path['Source'].name, # Prefix folder + item.name, # Sub-folder + subitem.name, # Image file + ), + 'Source': subitem}) + elif REGEX_WIM_FILE.search(item.name): + # Add image to remote_sources + try: + size = human_readable_size(item.stat().st_size) + except Exception: + size = ' ? ?' # unknown + remote_sources.append({ + 'Disabled': bool(not is_valid_wim_file(item)), + 'Name': r'{:9}| Image-Based: {:>7} {}\{}'.format( + prefix_path['Server']['Name'], # Server + size, # Size (duh) + prefix_path['Source'].name, # Prefix folder + item.name, # Image file + ), + 'Server': prefix_path['Server'], + 'Sort': r'{}\{}'.format( + prefix_path['Source'].name, # Prefix folder + item.name, # Image file + ), + 'Source': item}) - # Check for local sources - print_standard('Scanning for local sources...') - set_thread_error_mode(silent=True) # Prevents "No disk" popups - sys_drive = global_vars['Env']['SYSTEMDRIVE'] - for d in psutil.disk_partitions(): - if re.search(r'^{}'.format(sys_drive), d.mountpoint, re.IGNORECASE): - # Skip current OS drive - continue - if 'fixed' in d.opts: - # Skip DVD, etc - local_sources.append({ - 'Name': '{:9}| File-Based: [DISK] {}'.format( - ' Local', d.mountpoint), - 'Sort': d.mountpoint, - 'Source': LocalDisk(d)}) - # Check for images and subfolders - for item in os.scandir(d.mountpoint): - if REGEX_WIM_FILE.search(item.name): - try: - size = human_readable_size(item.stat().st_size) - except Exception: - size = ' ? ?' # unknown - local_sources.append({ - 'Disabled': bool(not is_valid_wim_file(item)), - 'Name': r'{:9}| Image-Based: {:>7} {}{}'.format( - ' Local', size, d.mountpoint, item.name), - 'Sort': r'{}{}'.format(d.mountpoint, item.name), - 'Source': item}) - elif REGEX_EXCL_ROOT_ITEMS.search(item.name): - pass - elif REGEX_EXCL_ITEMS.search(item.name): - pass - elif item.is_dir(): - # Add folder to local_sources - local_sources.append({ - 'Name': r'{:9}| File-Based: [DIR] {}{}'.format( - ' Local', d.mountpoint, item.name), - 'Sort': r'{}{}'.format(d.mountpoint, item.name), - 'Source': item}) + # Check for local sources + print_standard('Scanning for local sources...') + set_thread_error_mode(silent=True) # Prevents "No disk" popups + sys_drive = global_vars['Env']['SYSTEMDRIVE'] + for d in psutil.disk_partitions(): + if re.search(r'^{}'.format(sys_drive), d.mountpoint, re.IGNORECASE): + # Skip current OS drive + continue + if 'fixed' in d.opts: + # Skip DVD, etc + local_sources.append({ + 'Name': '{:9}| File-Based: [DISK] {}'.format( + ' Local', d.mountpoint), + 'Sort': d.mountpoint, + 'Source': LocalDisk(d)}) + # Check for images and subfolders + for item in os.scandir(d.mountpoint): + if REGEX_WIM_FILE.search(item.name): + try: + size = human_readable_size(item.stat().st_size) + except Exception: + size = ' ? ?' # unknown + local_sources.append({ + 'Disabled': bool(not is_valid_wim_file(item)), + 'Name': r'{:9}| Image-Based: {:>7} {}{}'.format( + ' Local', size, d.mountpoint, item.name), + 'Sort': r'{}{}'.format(d.mountpoint, item.name), + 'Source': item}) + elif REGEX_EXCL_ROOT_ITEMS.search(item.name): + pass + elif REGEX_EXCL_ITEMS.search(item.name): + pass + elif item.is_dir(): + # Add folder to local_sources + local_sources.append({ + 'Name': r'{:9}| File-Based: [DIR] {}{}'.format( + ' Local', d.mountpoint, item.name), + 'Sort': r'{}{}'.format(d.mountpoint, item.name), + 'Source': item}) - set_thread_error_mode(silent=False) # Return to normal + set_thread_error_mode(silent=False) # Return to normal - # Build Menu - local_sources.sort(key=itemgetter('Sort')) - remote_sources.sort(key=itemgetter('Sort')) - sources.extend(local_sources) - sources.extend(remote_sources) - actions = [{'Name': 'Quit', 'Letter': 'Q'}] + # Build Menu + local_sources.sort(key=itemgetter('Sort')) + remote_sources.sort(key=itemgetter('Sort')) + sources.extend(local_sources) + sources.extend(remote_sources) + actions = [{'Name': 'Quit', 'Letter': 'Q'}] - # Select backup from sources - if len(sources) > 0: - selection = menu_select( - 'Which backup are we using?', - main_entries=sources, - action_entries=actions, - disabled_label='DAMAGED') - if selection == 'Q': - umount_backup_shares() - exit_script() - else: - selected_source = sources[int(selection)-1]['Source'] + # Select backup from sources + if len(sources) > 0: + selection = menu_select( + 'Which backup are we using?', + main_entries=sources, + action_entries=actions, + disabled_label='DAMAGED') + if selection == 'Q': + umount_backup_shares() + exit_script() else: - print_error('ERROR: No backups found using prefix: {}.'.format( - backup_prefix)) - umount_backup_shares() - pause("Press Enter to exit...") - exit_script() + selected_source = sources[int(selection)-1]['Source'] + else: + print_error('ERROR: No backups found using prefix: {}.'.format( + backup_prefix)) + umount_backup_shares() + pause("Press Enter to exit...") + exit_script() - # Sanity check - if selected_source.is_file(): - # Image-Based - if not REGEX_WIM_FILE.search(selected_source.name): - print_error('ERROR: Unsupported image: {}'.format( - selected_source.path)) - raise GenericError + # Sanity check + if selected_source.is_file(): + # Image-Based + if not REGEX_WIM_FILE.search(selected_source.name): + print_error('ERROR: Unsupported image: {}'.format( + selected_source.path)) + raise GenericError + + # Done + return selected_source - # Done - return selected_source def select_volume(title='Select disk', auto_select=True): - """Select disk from attached disks. returns dict.""" - actions = [{'Name': 'Quit', 'Letter': 'Q'}] - disks = [] + """Select disk from attached disks. returns dict.""" + actions = [{'Name': 'Quit', 'Letter': 'Q'}] + disks = [] - # Build list of disks - set_thread_error_mode(silent=True) # Prevents "No disk" popups - for d in psutil.disk_partitions(): - info = { - 'Disk': d, - 'Name': d.mountpoint} - try: - usage = psutil.disk_usage(d.device) - free = '{free} / {total} available'.format( - free = human_readable_size(usage.free, 2), - total = human_readable_size(usage.total, 2)) - except Exception: - # Meh, leaving unsupported destinations out - pass - # free = 'Unknown' - # info['Disabled'] = True - else: - info['Display Name'] = '{} ({})'.format(info['Name'], free) - disks.append(info) - set_thread_error_mode(silent=False) # Return to normal - - # Skip menu? - if len(disks) == 1 and auto_select: - return disks[0] - - # Show menu - selection = menu_select(title, main_entries=disks, action_entries=actions) - if selection == 'Q': - exit_script() + # Build list of disks + set_thread_error_mode(silent=True) # Prevents "No disk" popups + for d in psutil.disk_partitions(): + info = { + 'Disk': d, + 'Name': d.mountpoint} + try: + usage = psutil.disk_usage(d.device) + free = '{free} / {total} available'.format( + free = human_readable_size(usage.free, 2), + total = human_readable_size(usage.total, 2)) + except Exception: + # Meh, leaving unsupported destinations out + pass + # free = 'Unknown' + # info['Disabled'] = True else: - return disks[int(selection)-1] + info['Display Name'] = '{} ({})'.format(info['Name'], free) + disks.append(info) + set_thread_error_mode(silent=False) # Return to normal + + # Skip menu? + if len(disks) == 1 and auto_select: + return disks[0] + + # Show menu + selection = menu_select(title, main_entries=disks, action_entries=actions) + if selection == 'Q': + exit_script() + else: + return disks[int(selection)-1] + def set_thread_error_mode(silent=True): - """Disable or Enable Windows error message dialogs. + """Disable or Enable Windows error message dialogs. - Disable when scanning for disks to avoid popups for empty cardreaders, etc - """ - # Code borrowed from: https://stackoverflow.com/a/29075319 - kernel32 = ctypes.WinDLL('kernel32') + Disable when scanning disks to avoid popups for empty cardreaders, etc + """ + # Code borrowed from: https://stackoverflow.com/a/29075319 + kernel32 = ctypes.WinDLL('kernel32') + + if silent: + kernel32.SetThreadErrorMode(SEM_FAIL, ctypes.byref(SEM_NORMAL)) + else: + kernel32.SetThreadErrorMode(SEM_NORMAL, ctypes.byref(SEM_NORMAL)) - if silent: - kernel32.SetThreadErrorMode(SEM_FAIL, ctypes.byref(SEM_NORMAL)) - else: - kernel32.SetThreadErrorMode(SEM_NORMAL, ctypes.byref(SEM_NORMAL)) def transfer_source(source_obj, dest_path, selected_items): - """Transfer, or extract, files/folders from source to destination.""" - if source_obj.is_dir(): - # Run FastCopy for each selection "group" - for group in selected_items: - try_and_print(message=group['Message'], - function=run_fast_copy, cs='Done', - items=group['Items'], - dest=group['Destination']) + """Transfer, or extract, files/folders from source to destination.""" + if source_obj.is_dir(): + # Run FastCopy for each selection "group" + for group in selected_items: + try_and_print(message=group['Message'], + function=run_fast_copy, cs='Done', + items=group['Items'], + dest=group['Destination']) + else: + if REGEX_WIM_FILE.search(source_obj.name): + # Extract files from WIM + for group in selected_items: + try_and_print(message=group['Message'], + function=run_wimextract, cs='Done', + source=source_obj.path, + items=group['Items'], + dest=dest_path) else: - if REGEX_WIM_FILE.search(source_obj.name): - # Extract files from WIM - for group in selected_items: - try_and_print(message=group['Message'], - function=run_wimextract, cs='Done', - source=source_obj.path, - items=group['Items'], - dest=dest_path) - else: - print_error('ERROR: Unsupported image: {}'.format(source_obj.path)) - raise GenericError + print_error('ERROR: Unsupported image: {}'.format(source_obj.path)) + raise GenericError + def umount_backup_shares(): - """Unmount the backup shares regardless of current status.""" - for server in BACKUP_SERVERS: - umount_network_share(server) + """Unmount the backup shares regardless of current status.""" + for server in BACKUP_SERVERS: + umount_network_share(server) + def umount_network_share(server): - """Unmount a network share defined by server.""" - cmd = r'net use \\{IP}\{Share} /delete'.format(**server) - cmd = cmd.split(' ') - try: - run_program(cmd) - except Exception: - print_error(r'Failed to umount \\{Name}\{Share}.'.format(**server)) - sleep(1) - else: - print_info('Umounted {Name}'.format(**server)) - server['Mounted'] = False + """Unmount a network share defined by server.""" + cmd = r'net use \\{IP}\{Share} /delete'.format(**server) + cmd = cmd.split(' ') + try: + run_program(cmd) + except Exception: + print_error(r'Failed to umount \\{Name}\{Share}.'.format(**server)) + sleep(1) + else: + print_info('Umounted {Name}'.format(**server)) + server['Mounted'] = False + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/ddrescue.py b/.bin/Scripts/functions/ddrescue.py index 7f1fc7aa..f6c30e0a 100644 --- a/.bin/Scripts/functions/ddrescue.py +++ b/.bin/Scripts/functions/ddrescue.py @@ -8,1232 +8,1360 @@ import signal import stat import time -from functions.common import * +from collections import OrderedDict from functions.data import * +from functions.hw_diags import * +from functions.tmux import * from operator import itemgetter + # STATIC VARIABLES AUTO_PASS_1_THRESHOLD = 95 AUTO_PASS_2_THRESHOLD = 98 DDRESCUE_SETTINGS = { - '--binary-prefixes': {'Enabled': True, 'Hidden': True}, - '--data-preview': {'Enabled': True, 'Hidden': True, 'Value': '5'}, - '--idirect': {'Enabled': True}, - '--odirect': {'Enabled': True}, - '--max-read-rate': {'Enabled': False, 'Value': '1MiB'}, - '--min-read-rate': {'Enabled': True, 'Value': '64KiB'}, - '--reopen-on-error': {'Enabled': True}, - '--retry-passes': {'Enabled': True, 'Value': '0'}, - '--test-mode': {'Enabled': False, 'Value': 'test.map'}, - '--timeout': {'Enabled': True, 'Value': '5m'}, - '-vvvv': {'Enabled': True, 'Hidden': True}, - } + '--binary-prefixes': {'Enabled': True, 'Hidden': True}, + '--data-preview': {'Enabled': True, 'Hidden': True, 'Value': '5'}, + '--idirect': {'Enabled': True}, + '--odirect': {'Enabled': True}, + '--max-read-rate': {'Enabled': False, 'Value': '1MiB'}, + '--min-read-rate': {'Enabled': True, 'Value': '64KiB'}, + '--reopen-on-error': {'Enabled': True}, + '--retry-passes': {'Enabled': True, 'Value': '0'}, + '--test-mode': {'Enabled': False, 'Value': 'test.map'}, + '--timeout': {'Enabled': True, 'Value': '5m'}, + '-vvvv': {'Enabled': True, 'Hidden': True}, + } RECOMMENDED_FSTYPES = ['ext3', 'ext4', 'xfs'] SIDE_PANE_WIDTH = 21 -USAGE = """ {script_name} clone [source [destination]] - {script_name} image [source [destination]] - (e.g. {script_name} clone /dev/sda /dev/sdb) +TMUX_LAYOUT = OrderedDict({ + 'Source': {'y': 2, 'Check': True}, + 'Started': {'x': SIDE_PANE_WIDTH, 'Check': True}, + 'Progress': {'x': SIDE_PANE_WIDTH, 'Check': True}, +}) +USAGE = """ {script_name} clone [source [destination]] + {script_name} image [source [destination]] + (e.g. {script_name} clone /dev/sda /dev/sdb) """ # Clases class BaseObj(): - """Base object used by DevObj, DirObj, and ImageObj.""" - def __init__(self, path): - self.type = 'base' - self.parent = None - self.path = os.path.realpath(path) - self.set_details() + """Base object used by DevObj, DirObj, and ImageObj.""" + def __init__(self, path): + self.type = 'base' + self.parent = None + self.path = os.path.realpath(path) + self.set_details() - def is_dev(self): - return self.type == 'dev' + def is_dev(self): + return self.type == 'dev' - def is_dir(self): - return self.type == 'dir' + def is_dir(self): + return self.type == 'dir' - def is_image(self): - return self.type == 'image' + def is_image(self): + return self.type == 'image' - def self_check(self): - pass + def self_check(self): + pass - def set_details(self): - self.details = {} + def set_details(self): + self.details = {} class BlockPair(): - """Object to track data and methods together for source and dest.""" - def __init__(self, mode, source, dest): - self.mode = mode - self.source = source - self.source_path = source.path - self.dest = dest - self.pass_done = [False, False, False] - self.resumed = False - self.rescued = 0 - self.rescued_percent = 0 - self.status = ['Pending', 'Pending', 'Pending'] - self.size = source.size - # Set dest paths - if self.mode == 'clone': - # Cloning - self.dest_path = dest.path - self.map_path = '{pwd}/Clone_{prefix}.map'.format( - pwd=os.path.realpath(global_vars['Env']['PWD']), - prefix=source.prefix) - else: - # Imaging - self.dest_path = '{path}/{prefix}.dd'.format( - path=dest.path, - prefix=source.prefix) - self.map_path = '{path}/{prefix}.map'.format( - path=dest.path, - prefix=source.prefix) - if os.path.exists(self.map_path): - self.load_map_data() - self.resumed = True - self.fix_status_strings() + """Object to track data and methods together for source and dest.""" + def __init__(self, mode, source, dest): + self.mode = mode + self.source = source + self.source_path = source.path + self.dest = dest + self.pass_done = [False, False, False] + self.resumed = False + self.rescued = 0 + self.rescued_percent = 0 + self.status = ['Pending', 'Pending', 'Pending'] + self.size = source.size + # Set dest paths + if self.mode == 'clone': + # Cloning + self.dest_path = dest.path + self.map_path = '{pwd}/Clone_{prefix}.map'.format( + pwd=os.path.realpath(global_vars['Env']['PWD']), + prefix=source.prefix) + else: + # Imaging + self.dest_path = '{path}/{prefix}.dd'.format( + path=dest.path, + prefix=source.prefix) + self.map_path = '{path}/{prefix}.map'.format( + path=dest.path, + prefix=source.prefix) + if os.path.exists(self.map_path): + self.load_map_data() + self.resumed = True + self.fix_status_strings() - def fix_status_strings(self): - """Format status strings via get_formatted_status().""" - for pass_num in [1, 2, 3]: - self.status[pass_num-1] = get_formatted_status( - label='Pass {}'.format(pass_num), - data=self.status[pass_num-1]) + def fix_status_strings(self): + """Format status strings via get_formatted_status().""" + for pass_num in [1, 2, 3]: + self.status[pass_num-1] = get_formatted_status( + label='Pass {}'.format(pass_num), + data=self.status[pass_num-1]) - def finish_pass(self, pass_num): - """Mark pass as done and check if 100% recovered.""" - map_data = read_map_file(self.map_path) - if map_data['full recovery']: - self.pass_done = [True, True, True] - self.rescued = self.size - self.status[pass_num] = get_formatted_status( - label='Pass {}'.format(pass_num+1), - data=100) - # Mark future passes as Skipped - pass_num += 1 - while pass_num <= 2: - self.status[pass_num] = get_formatted_status( - label='Pass {}'.format(pass_num+1), - data='Skipped') - pass_num += 1 - else: - self.pass_done[pass_num] = True + def finish_pass(self, pass_num): + """Mark pass as done and check if 100% recovered.""" + map_data = read_map_file(self.map_path) + if map_data['full recovery']: + self.pass_done = [True, True, True] + self.rescued = self.size + self.status[pass_num] = get_formatted_status( + label='Pass {}'.format(pass_num+1), + data=100) + # Mark future passes as Skipped + pass_num += 1 + while pass_num <= 2: + self.status[pass_num] = get_formatted_status( + label='Pass {}'.format(pass_num+1), + data='Skipped') + pass_num += 1 + else: + self.pass_done[pass_num] = True - def load_map_data(self): - """Load data from map file and set progress.""" - map_data = read_map_file(self.map_path) - self.rescued_percent = map_data['rescued'] - self.rescued = (self.rescued_percent * self.size) / 100 - if map_data['full recovery']: - self.pass_done = [True, True, True] - self.rescued = self.size - self.status = ['Skipped', 'Skipped', 'Skipped'] - elif map_data['non-tried'] > 0: - # Initial pass incomplete - pass - elif map_data['non-trimmed'] > 0: - self.pass_done = [True, False, False] - self.status = ['Skipped', 'Pending', 'Pending'] - elif map_data['non-scraped'] > 0: - self.pass_done = [True, True, False] - self.status = ['Skipped', 'Skipped', 'Pending'] - else: - self.pass_done = [True, True, True] - self.status = ['Skipped', 'Skipped', 'Skipped'] + def load_map_data(self): + """Load data from map file and set progress.""" + map_data = read_map_file(self.map_path) + self.rescued_percent = map_data['rescued'] + self.rescued = (self.rescued_percent * self.size) / 100 + if map_data['full recovery']: + self.pass_done = [True, True, True] + self.rescued = self.size + self.status = ['Skipped', 'Skipped', 'Skipped'] + elif map_data['non-tried'] > 0: + # Initial pass incomplete + pass + elif map_data['non-trimmed'] > 0: + self.pass_done = [True, False, False] + self.status = ['Skipped', 'Pending', 'Pending'] + elif map_data['non-scraped'] > 0: + self.pass_done = [True, True, False] + self.status = ['Skipped', 'Skipped', 'Pending'] + else: + self.pass_done = [True, True, True] + self.status = ['Skipped', 'Skipped', 'Skipped'] - def self_check(self): - """Self check to abort on bad dest/map combinations.""" - dest_exists = os.path.exists(self.dest_path) - map_exists = os.path.exists(self.map_path) - if self.mode == 'image': - if dest_exists and not map_exists: - raise GenericError( - 'Detected image "{}" but not the matching map'.format( - self.dest_path)) - elif not dest_exists and map_exists: - raise GenericError( - 'Detected map "{}" but not the matching image'.format( - self.map_path)) - elif not dest_exists: - raise GenericError('Destination device "{}" missing'.format( - self.dest_path)) + def self_check(self): + """Self check to abort on bad dest/map combinations.""" + dest_exists = os.path.exists(self.dest_path) + map_exists = os.path.exists(self.map_path) + if self.mode == 'image': + if dest_exists and not map_exists: + raise GenericError( + 'Detected image "{}" but not the matching map'.format( + self.dest_path)) + elif not dest_exists and map_exists: + raise GenericError( + 'Detected map "{}" but not the matching image'.format( + self.map_path)) + elif not dest_exists: + raise GenericError('Destination device "{}" missing'.format( + self.dest_path)) - def update_progress(self, pass_num): - """Update progress using map file.""" - if os.path.exists(self.map_path): - map_data = read_map_file(self.map_path) - self.rescued_percent = map_data.get('rescued', 0) - self.rescued = (self.rescued_percent * self.size) / 100 - self.status[pass_num] = get_formatted_status( - label='Pass {}'.format(pass_num+1), - data=(self.rescued/self.size)*100) + def update_progress(self, pass_num): + """Update progress using map file.""" + if os.path.exists(self.map_path): + map_data = read_map_file(self.map_path) + self.rescued_percent = map_data.get('rescued', 0) + self.rescued = (self.rescued_percent * self.size) / 100 + self.status[pass_num] = get_formatted_status( + label='Pass {}'.format(pass_num+1), + data=(self.rescued/self.size)*100) class DevObj(BaseObj): - """Block device object.""" - def self_check(self): - """Verify that self.path points to a block device.""" - if not pathlib.Path(self.path).is_block_device(): - raise GenericError('Path "{}" is not a block device.'.format( - self.path)) - if self.parent: - print_warning('"{}" is a child device.'.format(self.path)) - if ask('Use parent device "{}" instead?'.format(self.parent)): - self.path = os.path.realpath(self.parent) - self.set_details() + """Block device object.""" + def self_check(self): + """Verify that self.path points to a block device.""" + if not pathlib.Path(self.path).is_block_device(): + raise GenericError('Path "{}" is not a block device.'.format( + self.path)) + if self.parent: + print_warning('"{}" is a child device.'.format(self.path)) + if ask('Use parent device "{}" instead?'.format(self.parent)): + self.path = os.path.realpath(self.parent) + self.set_details() - def set_details(self): - """Set details via lsblk.""" - self.type = 'dev' - self.details = get_device_details(self.path) - self.name = '{name} {size} {model} {serial}'.format( - name=self.details.get('name', 'UNKNOWN'), - size=self.details.get('size', 'UNKNOWN'), - model=self.details.get('model', 'UNKNOWN'), - serial=self.details.get('serial', 'UNKNOWN')) - self.model = self.details.get('model', 'UNKNOWN') - self.model_size = self.details.get('size', 'UNKNOWN') - self.size = get_size_in_bytes(self.details.get('size', 'UNKNOWN')) - self.report = get_device_report(self.path) - self.parent = self.details.get('pkname', '') - self.label = self.details.get('label', '') - if not self.label: - # Force empty string in case it's set to None - self.label = '' - self.update_filename_prefix() + def set_details(self): + """Set details via lsblk.""" + self.type = 'dev' + self.details = get_device_details(self.path) + self.name = '{name} {size} {model} {serial}'.format( + name=self.details.get('name', 'UNKNOWN'), + size=self.details.get('size', 'UNKNOWN'), + model=self.details.get('model', 'UNKNOWN'), + serial=self.details.get('serial', 'UNKNOWN')) + self.model = self.details.get('model', 'UNKNOWN') + self.model_size = self.details.get('size', 'UNKNOWN') + self.size = get_size_in_bytes(self.details.get('size', 'UNKNOWN')) + self.report = get_device_report(self.path) + self.parent = self.details.get('pkname', '') + self.label = self.details.get('label', '') + if not self.label: + # Force empty string in case it's set to None + self.label = '' + self.update_filename_prefix() - def update_filename_prefix(self): - """Set filename prefix based on details.""" - self.prefix = '{m_size}_{model}'.format( - m_size=self.model_size, - model=self.model) - self.prefix = self.prefix.strip() - if self.parent: - # Add child device details - c_num = self.path.replace(self.parent, '') - self.prefix += '_{c_prefix}{c_num}_{c_size}{sep}{c_label}'.format( - c_prefix='p' if len(c_num) == 1 else '', - c_num=c_num, - c_size=self.details.get('size', 'UNKNOWN'), - sep='_' if self.label else '', - c_label=self.label) - self.prefix = self.prefix.strip().replace(' ', '_') - self.prefix = self.prefix.strip().replace('/', '_') + def update_filename_prefix(self): + """Set filename prefix based on details.""" + self.prefix = '{m_size}_{model}'.format( + m_size=self.model_size, + model=self.model) + self.prefix = self.prefix.strip() + if self.parent: + # Add child device details + c_num = self.path.replace(self.parent, '') + self.prefix += '_{c_prefix}{c_num}_{c_size}{sep}{c_label}'.format( + c_prefix='p' if len(c_num) == 1 else '', + c_num=c_num, + c_size=self.details.get('size', 'UNKNOWN'), + sep='_' if self.label else '', + c_label=self.label) + self.prefix = self.prefix.strip().replace(' ', '_') + self.prefix = self.prefix.strip().replace('/', '_') class DirObj(BaseObj): - def self_check(self): - """Verify that self.path points to a directory.""" - if not pathlib.Path(self.path).is_dir(): - raise GenericError('Path "{}" is not a directory.'.format( - self.path)) + def self_check(self): + """Verify that self.path points to a directory.""" + if not pathlib.Path(self.path).is_dir(): + raise GenericError('Path "{}" is not a directory.'.format( + self.path)) - def set_details(self): - """Set details via findmnt.""" - self.type = 'dir' - self.details = get_dir_details(self.path) - self.fstype = self.details.get('fstype', 'UNKNOWN') - self.name = self.path + '/' - self.size = get_size_in_bytes(self.details.get('avail', 'UNKNOWN')) - self.report = get_dir_report(self.path) + def set_details(self): + """Set details via findmnt.""" + self.type = 'dir' + self.details = get_dir_details(self.path) + self.fstype = self.details.get('fstype', 'UNKNOWN') + self.name = self.path + '/' + self.size = get_size_in_bytes(self.details.get('avail', 'UNKNOWN')) + self.report = get_dir_report(self.path) class ImageObj(BaseObj): - def self_check(self): - """Verify that self.path points to a file.""" - if not pathlib.Path(self.path).is_file(): - raise GenericError('Path "{}" is not an image file.'.format( - self.path)) + def self_check(self): + """Verify that self.path points to a file.""" + if not pathlib.Path(self.path).is_file(): + raise GenericError('Path "{}" is not an image file.'.format( + self.path)) - def set_details(self): - """Setup loopback device, set details via lsblk, then detach device.""" - self.type = 'image' - self.loop_dev = setup_loopback_device(self.path) - self.details = get_device_details(self.loop_dev) - self.details['model'] = 'ImageFile' - self.name = '{name} {size}'.format( - name=self.path[self.path.rfind('/')+1:], - size=self.details.get('size', 'UNKNOWN')) - self.prefix = '{}_ImageFile'.format( - self.details.get('size', 'UNKNOWN')) - self.size = get_size_in_bytes(self.details.get('size', 'UNKNOWN')) - self.report = get_device_report(self.loop_dev) - self.report = self.report.replace( - self.loop_dev[self.loop_dev.rfind('/')+1:], '(Img)') - run_program(['losetup', '--detach', self.loop_dev], check=False) + def set_details(self): + """Set details using a temp loopback device.""" + self.type = 'image' + self.loop_dev = setup_loopback_device(self.path) + self.details = get_device_details(self.loop_dev) + self.details['model'] = 'ImageFile' + self.name = '{name} {size}'.format( + name=self.path[self.path.rfind('/')+1:], + size=self.details.get('size', 'UNKNOWN')) + self.prefix = '{}_ImageFile'.format( + self.details.get('size', 'UNKNOWN')) + self.size = get_size_in_bytes(self.details.get('size', 'UNKNOWN')) + self.report = get_device_report(self.loop_dev) + self.report = self.report.replace( + self.loop_dev[self.loop_dev.rfind('/')+1:], '(Img)') + run_program(['losetup', '--detach', self.loop_dev], check=False) class RecoveryState(): - """Object to track BlockPair objects and overall state.""" - def __init__(self, mode, source, dest): - self.mode = mode.lower() - self.source = source - self.source_path = source.path - self.dest = dest - self.block_pairs = [] - self.current_pass = 0 - self.current_pass_str = '0: Initializing' - self.settings = DDRESCUE_SETTINGS.copy() - self.finished = False - self.progress_out = '{}/progress.out'.format(global_vars['LogDir']) - self.rescued = 0 - self.resumed = False - self.started = False - self.total_size = 0 - if mode not in ('clone', 'image'): - raise GenericError('Unsupported mode') + """Object to track BlockPair objects and overall state.""" + def __init__(self, mode, source, dest): + self.mode = mode.lower() + self.source = source + self.source_path = source.path + self.dest = dest + self.block_pairs = [] + self.current_pass = 0 + self.current_pass_str = '0: Initializing' + self.settings = DDRESCUE_SETTINGS.copy() + self.finished = False + self.panes = {} + self.progress_out = '{}/progress.out'.format(global_vars['LogDir']) + self.rescued = 0 + self.resumed = False + self.started = False + self.total_size = 0 + if mode not in ('clone', 'image'): + raise GenericError('Unsupported mode') + self.get_smart_source() - def add_block_pair(self, source, dest): - """Run safety checks and append new BlockPair to internal list.""" - if self.mode == 'clone': - # Cloning safety checks - if source.is_dir(): - raise GenericError('Invalid source "{}"'.format( - source.path)) - elif not dest.is_dev(): - raise GenericError('Invalid destination "{}"'.format( - dest.path)) - elif source.size > dest.size: - raise GenericError( - 'Destination is too small, refusing to continue.') - else: - # Imaging safety checks - if not source.is_dev(): - raise GenericError('Invalid source "{}"'.format( - source.path)) - elif not dest.is_dir(): - raise GenericError('Invalid destination "{}"'.format( - dest.path)) - elif (source.size * 1.2) > dest.size: - raise GenericError( - 'Not enough free space, refusing to continue.') - elif dest.fstype.lower() not in RECOMMENDED_FSTYPES: - print_error( - 'Destination filesystem "{}" is not recommended.'.format( - dest.fstype.upper())) - print_info('Recommended types are: {}'.format( - ' / '.join(RECOMMENDED_FSTYPES).upper())) - print_standard(' ') - if not ask('Proceed anyways? (Strongly discouraged)'): - raise GenericAbort() - elif not is_writable_dir(dest): - raise GenericError( - 'Destination is not writable, refusing to continue.') - elif not is_writable_filesystem(dest): - raise GenericError( - 'Destination is mounted read-only, refusing to continue.') + def add_block_pair(self, source, dest): + """Run safety checks and append new BlockPair to internal list.""" + if self.mode == 'clone': + # Cloning safety checks + if source.is_dir(): + raise GenericError('Invalid source "{}"'.format( + source.path)) + elif not dest.is_dev(): + raise GenericError('Invalid destination "{}"'.format( + dest.path)) + elif source.size > dest.size: + raise GenericError( + 'Destination is too small, refusing to continue.') + else: + # Imaging safety checks + if not source.is_dev(): + raise GenericError('Invalid source "{}"'.format( + source.path)) + elif not dest.is_dir(): + raise GenericError('Invalid destination "{}"'.format( + dest.path)) + elif (source.size * 1.2) > dest.size: + raise GenericError( + 'Not enough free space, refusing to continue.') + elif dest.fstype.lower() not in RECOMMENDED_FSTYPES: + print_error( + 'Destination filesystem "{}" is not recommended.'.format( + dest.fstype.upper())) + print_info('Recommended types are: {}'.format( + ' / '.join(RECOMMENDED_FSTYPES).upper())) + print_standard(' ') + if not ask('Proceed anyways? (Strongly discouraged)'): + raise GenericAbort() + elif not is_writable_dir(dest): + raise GenericError( + 'Destination is not writable, refusing to continue.') + elif not is_writable_filesystem(dest): + raise GenericError( + 'Destination is mounted read-only, refusing to continue.') - # Safety checks passed - self.block_pairs.append(BlockPair(self.mode, source, dest)) + # Safety checks passed + self.block_pairs.append(BlockPair(self.mode, source, dest)) - def current_pass_done(self): - """Checks if pass is done for all block-pairs, returns bool.""" - done = True - for bp in self.block_pairs: - done &= bp.pass_done[self.current_pass] - return done + def current_pass_done(self): + """Checks if pass is done for all block-pairs, returns bool.""" + done = True + for bp in self.block_pairs: + done &= bp.pass_done[self.current_pass] + return done - def current_pass_min(self): - """Gets minimum pass rescued percentage, returns float.""" - min_percent = 100 - for bp in self.block_pairs: - min_percent = min(min_percent, bp.rescued_percent) - return min_percent + def current_pass_min(self): + """Gets minimum pass rescued percentage, returns float.""" + min_percent = 100 + for bp in self.block_pairs: + min_percent = min(min_percent, bp.rescued_percent) + return min_percent - def retry_all_passes(self): - """Mark all passes as pending for all block-pairs.""" - self.finished = False - for bp in self.block_pairs: - bp.pass_done = [False, False, False] - bp.status = ['Pending', 'Pending', 'Pending'] - bp.fix_status_strings() - self.set_pass_num() + def get_smart_source(self): + """Get source for SMART dispay.""" + disk_path = self.source.path + if self.source.parent: + disk_path = self.source.parent - def self_checks(self): - """Run self-checks for each BlockPair and update state values.""" - self.total_size = 0 - for bp in self.block_pairs: - bp.self_check() - self.resumed |= bp.resumed - self.total_size += bp.size + self.smart_source = DiskObj(disk_path) - def set_pass_num(self): - """Set current pass based on all block-pair's progress.""" - self.current_pass = 0 - for pass_num in (2, 1, 0): - # Iterate backwards through passes - pass_done = True - for bp in self.block_pairs: - pass_done &= bp.pass_done[pass_num] - if pass_done: - # All block-pairs reported being done - # Set to next pass, unless we're on the last pass (2) - self.current_pass = min(2, pass_num + 1) - if pass_num == 2: - # Also mark overall recovery as finished if on last pass - self.finished = True - break - if self.finished: - self.current_pass_str = '- "Done"' - elif self.current_pass == 0: - self.current_pass_str = '1 "Initial Read"' - elif self.current_pass == 1: - self.current_pass_str = '2 "Trimming bad areas"' - elif self.current_pass == 2: - self.current_pass_str = '3 "Scraping bad areas"' + def retry_all_passes(self): + """Mark all passes as pending for all block-pairs.""" + self.finished = False + for bp in self.block_pairs: + bp.pass_done = [False, False, False] + bp.status = ['Pending', 'Pending', 'Pending'] + bp.fix_status_strings() + self.set_pass_num() - def update_progress(self): - """Update overall progress using block_pairs.""" - self.rescued = 0 - for bp in self.block_pairs: - self.rescued += bp.rescued - self.rescued_percent = (self.rescued / self.total_size) * 100 - self.status_percent = get_formatted_status( - label='Recovered:', data=self.rescued_percent) - self.status_amount = get_formatted_status( - label='', data=human_readable_size(self.rescued, decimals=2)) + def self_checks(self): + """Run self-checks and update state values.""" + cmd = ['findmnt', '--json', '--target', os.getcwd()] + map_allowed_fstypes = RECOMMENDED_FSTYPES.copy() + map_allowed_fstypes.extend(['cifs', 'ext2', 'vfat']) + map_allowed_fstypes.sort() + json_data = {} + + # Avoid saving map to non-persistent filesystem + try: + result = run_program(cmd) + json_data = json.loads(result.stdout.decode()) + except Exception: + print_error('ERROR: Failed to verify map path') + raise GenericAbort() + fstype = json_data.get( + 'filesystems', [{}])[0].get( + 'fstype', 'unknown') + if fstype not in map_allowed_fstypes: + print_error( + "Map isn't being saved to a recommended filesystem ({})".format( + fstype.upper())) + print_info('Recommended types are: {}'.format( + ' / '.join(map_allowed_fstypes).upper())) + print_standard(' ') + if not ask('Proceed anyways? (Strongly discouraged)'): + raise GenericAbort() + + # Run BlockPair self checks and get total size + self.total_size = 0 + for bp in self.block_pairs: + bp.self_check() + self.resumed |= bp.resumed + self.total_size += bp.size + + def set_pass_num(self): + """Set current pass based on all block-pair's progress.""" + self.current_pass = 0 + for pass_num in (2, 1, 0): + # Iterate backwards through passes + pass_done = True + for bp in self.block_pairs: + pass_done &= bp.pass_done[pass_num] + if pass_done: + # All block-pairs reported being done + # Set to next pass, unless we're on the last pass (2) + self.current_pass = min(2, pass_num + 1) + if pass_num == 2: + # Also mark overall recovery as finished if on last pass + self.finished = True + break + if self.finished: + self.current_pass_str = '- "Done"' + elif self.current_pass == 0: + self.current_pass_str = '1 "Initial Read"' + elif self.current_pass == 1: + self.current_pass_str = '2 "Trimming bad areas"' + elif self.current_pass == 2: + self.current_pass_str = '3 "Scraping bad areas"' + + def update_progress(self): + """Update overall progress using block_pairs.""" + self.rescued = 0 + for bp in self.block_pairs: + self.rescued += bp.rescued + self.rescued_percent = (self.rescued / self.total_size) * 100 + self.status_percent = get_formatted_status( + label='Recovered:', data=self.rescued_percent) + self.status_amount = get_formatted_status( + label='', data=human_readable_size(self.rescued, decimals=2)) # Functions def build_outer_panes(state): - """Build top and side panes.""" - clear_screen() - result = run_program(['tput', 'cols']) - width = int( - (int(result.stdout.decode().strip()) - SIDE_PANE_WIDTH) / 2) - 2 + """Build top and side panes.""" + state.panes['Source'] = tmux_split_window( + behind=True, vertical=True, lines=2, + text='{BLUE}Source{CLEAR}'.format(**COLORS)) + state.panes['Started'] = tmux_split_window( + lines=SIDE_PANE_WIDTH, target_pane=state.panes['Source'], + text='{BLUE}Started{CLEAR}\n{s}'.format( + s=time.strftime("%Y-%m-%d %H:%M %Z"), + **COLORS)) + state.panes['Destination'] = tmux_split_window( + percent=50, target_pane=state.panes['Source'], + text='{BLUE}Destination{CLEAR}'.format(**COLORS)) - # Top panes - source_str = state.source.name - if len(source_str) > width: - source_str = '{}...'.format(source_str[:width-3]) - dest_str = state.dest.name - if len(dest_str) > width: - if state.mode == 'clone': - dest_str = '{}...'.format(dest_str[:width-3]) - else: - dest_str = '...{}'.format(dest_str[-width+3:]) - source_pane = tmux_splitw( - '-bdvl', '2', - '-PF', '#D', - 'echo-and-hold "{BLUE}Source{CLEAR}\n{text}"'.format( - text=source_str, - **COLORS)) - tmux_splitw( - '-t', source_pane, - '-dhl', '{}'.format(SIDE_PANE_WIDTH), - 'echo-and-hold "{BLUE}Started{CLEAR}\n{text}"'.format( - text=time.strftime("%Y-%m-%d %H:%M %Z"), - **COLORS)) - tmux_splitw( - '-t', source_pane, - '-dhp', '50', - 'echo-and-hold "{BLUE}Destination{CLEAR}\n{text}"'.format( - text=dest_str, - **COLORS)) - - # Side pane - update_sidepane(state) - tmux_splitw( - '-dhl', str(SIDE_PANE_WIDTH), - 'watch', '--color', '--no-title', '--interval', '1', - 'cat', state.progress_out) + # Side pane + update_sidepane(state) + state.panes['Progress'] = tmux_split_window( + lines=SIDE_PANE_WIDTH, watch=state.progress_out) def create_path_obj(path): - """Create Dev, Dir, or Image obj based on path given.""" - obj = None - if pathlib.Path(path).is_block_device(): - obj = DevObj(path) - elif pathlib.Path(path).is_dir(): - obj = DirObj(path) - elif pathlib.Path(path).is_file(): - obj = ImageObj(path) - else: - raise GenericError('Invalid path "{}"'.format(path)) - return obj + """Create Dev, Dir, or Image obj based on path given.""" + obj = None + if pathlib.Path(path).is_block_device(): + obj = DevObj(path) + elif pathlib.Path(path).is_dir(): + obj = DirObj(path) + elif pathlib.Path(path).is_file(): + obj = ImageObj(path) + else: + raise GenericError('Invalid path "{}"'.format(path)) + return obj def double_confirm_clone(): - """Display warning and get 2nd confirmation from user, returns bool.""" - print_standard('\nSAFETY CHECK') - print_warning('All data will be DELETED from the ' - 'destination device and partition(s) listed above.') - print_warning('This is irreversible and will lead ' - 'to {CLEAR}{RED}DATA LOSS.'.format(**COLORS)) - return ask('Asking again to confirm, is this correct?') + """Display warning and get 2nd confirmation, returns bool.""" + print_standard('\nSAFETY CHECK') + print_warning('All data will be DELETED from the ' + 'destination device and partition(s) listed above.') + print_warning('This is irreversible and will lead ' + 'to {CLEAR}{RED}DATA LOSS.'.format(**COLORS)) + return ask('Asking again to confirm, is this correct?') + + +def fix_tmux_panes(state, forced=False): + """Fix pane sizes if the winodw has been resized.""" + needs_fixed = False + + # Check layout + for k, v in TMUX_LAYOUT.items(): + if not v.get('Check'): + # Not concerned with the size of this pane + continue + # Get target + target = None + if k != 'Current': + if k not in state.panes: + # Skip missing panes + continue + else: + target = state.panes[k] + + # Check pane size + x, y = tmux_get_pane_size(pane_id=target) + if v.get('x', False) and v['x'] != x: + needs_fixed = True + if v.get('y', False) and v['y'] != y: + needs_fixed = True + + # Bail? + if not needs_fixed and not forced: + return + + # Remove Destination pane (temporarily) + tmux_kill_pane(state.panes['Destination']) + + # Update layout + for k, v in TMUX_LAYOUT.items(): + # Get target + target = None + if k != 'Current': + if k not in state.panes: + # Skip missing panes + continue + else: + target = state.panes[k] + + # Resize pane + tmux_resize_pane(pane_id=target, **v) + + # Calc Source/Destination pane sizes + width, height = tmux_get_pane_size() + width = int(width / 2) - 1 + + # Update Source string + source_str = state.source.name + if len(source_str) > width: + source_str = '{}...'.format(source_str[:width-3]) + + # Update Destination string + dest_str = state.dest.name + if len(dest_str) > width: + if state.mode == 'clone': + dest_str = '{}...'.format(dest_str[:width-3]) + else: + dest_str = '...{}'.format(dest_str[-width+3:]) + + # Rebuild Source/Destination panes + tmux_update_pane( + pane_id=state.panes['Source'], + text='{BLUE}Source{CLEAR}\n{s}'.format( + s=source_str, **COLORS)) + state.panes['Destination'] = tmux_split_window( + percent=50, target_pane=state.panes['Source'], + text='{BLUE}Destination{CLEAR}\n{s}'.format( + s=dest_str, **COLORS)) + + if 'SMART' in state.panes: + # Calc SMART/ddrescue/Journal panes sizes + ratio = [12, 22, 4] + width, height = tmux_get_pane_size(pane_id=state.panes['Progress']) + height -= 2 + total = sum(ratio) + p_ratio = [int((x/total) * height) for x in ratio] + p_ratio[1] = height - p_ratio[0] - p_ratio[2] + + # Resize SMART/Journal panes + tmux_resize_pane(state.panes['SMART'], y=ratio[0]) + tmux_resize_pane(y=ratio[1]) + tmux_resize_pane(state.panes['Journal'], y=ratio[2]) def get_device_details(dev_path): - """Get device details via lsblk, returns JSON dict.""" - try: - cmd = ( - 'lsblk', - '--json', - '--output-all', - '--paths', - dev_path) - result = run_program(cmd) - except CalledProcessError: - # Return empty dict and let calling section deal with the issue - return {} + """Get device details via lsblk, returns JSON dict.""" + try: + cmd = ( + 'lsblk', + '--json', + '--output-all', + '--paths', + dev_path) + result = run_program(cmd) + except CalledProcessError: + # Return empty dict and let calling section deal with the issue + return {} - json_data = json.loads(result.stdout.decode()) - # Just return the first device (there should only be one) - return json_data['blockdevices'][0] + json_data = json.loads(result.stdout.decode()) + # Just return the first device (there should only be one) + return json_data['blockdevices'][0] def get_device_report(dev_path): - """Build colored device report using lsblk, returns str.""" - result = run_program([ - 'lsblk', '--nodeps', - '--output', 'NAME,TRAN,TYPE,SIZE,VENDOR,MODEL,SERIAL', - dev_path]) - lines = result.stdout.decode().strip().splitlines() - lines.append('') + """Build colored device report using lsblk, returns str.""" + result = run_program([ + 'lsblk', '--nodeps', + '--output', 'NAME,TRAN,TYPE,SIZE,VENDOR,MODEL,SERIAL', + dev_path]) + lines = result.stdout.decode().strip().splitlines() + lines.append('') - # FS details (if any) - result = run_program([ - 'lsblk', - '--output', 'NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT', - dev_path]) - lines.extend(result.stdout.decode().strip().splitlines()) + # FS details (if any) + result = run_program([ + 'lsblk', + '--output', 'NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT', + dev_path]) + lines.extend(result.stdout.decode().strip().splitlines()) - # Color label lines - output = [] - for line in lines: - if line[0:4] == 'NAME': - output.append('{BLUE}{line}{CLEAR}'.format(line=line, **COLORS)) - else: - output.append(line) + # Color label lines + output = [] + for line in lines: + if line[0:4] == 'NAME': + output.append('{BLUE}{line}{CLEAR}'.format(line=line, **COLORS)) + else: + output.append(line) - # Done - return '\n'.join(output) + # Done + return '\n'.join(output) def get_dir_details(dir_path): - """Get dir details via findmnt, returns JSON dict.""" - try: - result = run_program([ - 'findmnt', '-J', - '-o', 'SOURCE,TARGET,FSTYPE,OPTIONS,SIZE,AVAIL,USED', - '-T', dir_path]) - json_data = json.loads(result.stdout.decode()) - except Exception: - raise GenericError( - 'Failed to get directory details for "{}".'.format(self.path)) - else: - return json_data['filesystems'][0] + """Get dir details via findmnt, returns JSON dict.""" + try: + result = run_program([ + 'findmnt', '-J', + '-o', 'SOURCE,TARGET,FSTYPE,OPTIONS,SIZE,AVAIL,USED', + '-T', dir_path]) + json_data = json.loads(result.stdout.decode()) + except Exception: + raise GenericError( + 'Failed to get directory details for "{}".'.format(self.path)) + else: + return json_data['filesystems'][0] def get_dir_report(dir_path): - """Build colored dir report using findmnt, returns str.""" - dir_path = dir_path - output = [] - width = len(dir_path)+1 - result = run_program([ - 'findmnt', - '--output', 'SIZE,AVAIL,USED,FSTYPE,OPTIONS', - '--target', dir_path]) - for line in result.stdout.decode().splitlines(): - if 'FSTYPE' in line: - output.append('{BLUE}{label:<{width}}{line}{CLEAR}'.format( - label='PATH', - width=width, - line=line.replace('\n',''), - **COLORS)) - else: - output.append('{path:<{width}}{line}'.format( - path=dir_path, - width=width, - line=line.replace('\n',''))) + """Build colored dir report using findmnt, returns str.""" + dir_path = dir_path + output = [] + width = len(dir_path)+1 + result = run_program([ + 'findmnt', + '--output', 'SIZE,AVAIL,USED,FSTYPE,OPTIONS', + '--target', dir_path]) + for line in result.stdout.decode().splitlines(): + if 'FSTYPE' in line: + output.append('{BLUE}{label:<{width}}{line}{CLEAR}'.format( + label='PATH', + width=width, + line=line.replace('\n',''), + **COLORS)) + else: + output.append('{path:<{width}}{line}'.format( + path=dir_path, + width=width, + line=line.replace('\n',''))) - # Done - return '\n'.join(output) + # Done + return '\n'.join(output) def get_size_in_bytes(s): - """Convert size string from lsblk string to bytes, returns int.""" - s = re.sub(r'(\d+\.?\d*)\s*([KMGTB])B?', r'\1 \2B', s, re.IGNORECASE) - return convert_to_bytes(s) + """Convert size string from lsblk string to bytes, returns int.""" + s = re.sub(r'(\d+\.?\d*)\s*([KMGTB])B?', r'\1 \2B', s, re.IGNORECASE) + return convert_to_bytes(s) def get_formatted_status(label, data): - """Build status string using provided info, returns str.""" - data_width = SIDE_PANE_WIDTH - len(label) - try: - data_str = '{data:>{data_width}.2f} %'.format( - data=data, - data_width=data_width-2) - except ValueError: - # Assuming non-numeric data - data_str = '{data:>{data_width}}'.format( - data=data, - data_width=data_width) - status = '{label}{s_color}{data_str}{CLEAR}'.format( - label=label, - s_color=get_status_color(data), - data_str=data_str, - **COLORS) - return status + """Build status string using provided info, returns str.""" + data_width = SIDE_PANE_WIDTH - len(label) + try: + data_str = '{data:>{data_width}.2f} %'.format( + data=data, + data_width=data_width-2) + except ValueError: + # Assuming non-numeric data + data_str = '{data:>{data_width}}'.format( + data=data, + data_width=data_width) + status = '{label}{s_color}{data_str}{CLEAR}'.format( + label=label, + s_color=get_status_color(data), + data_str=data_str, + **COLORS) + return status def get_status_color(s, t_success=99, t_warn=90): - """Get color based on status, returns str.""" - color = COLORS['CLEAR'] - p_recovered = -1 - try: - p_recovered = float(s) - except ValueError: - # Status is either in lists below or will default to red - pass + """Get color based on status, returns str.""" + color = COLORS['CLEAR'] + p_recovered = -1 + try: + p_recovered = float(s) + except ValueError: + # Status is either in lists below or will default to red + pass - if s in ('Pending',) or str(s)[-2:] in (' b', 'Kb', 'Mb', 'Gb', 'Tb'): - color = COLORS['CLEAR'] - elif s in ('Skipped', 'Unknown'): - color = COLORS['YELLOW'] - elif p_recovered >= t_success: - color = COLORS['GREEN'] - elif p_recovered >= t_warn: - color = COLORS['YELLOW'] - else: - color = COLORS['RED'] - return color + if s in ('Pending',) or str(s)[-2:] in (' b', 'Kb', 'Mb', 'Gb', 'Tb'): + color = COLORS['CLEAR'] + elif s in ('Skipped', 'Unknown'): + color = COLORS['YELLOW'] + elif p_recovered >= t_success: + color = COLORS['GREEN'] + elif p_recovered >= t_warn: + color = COLORS['YELLOW'] + else: + color = COLORS['RED'] + return color def is_writable_dir(dir_obj): - """Check if we have read-write-execute permissions, returns bool.""" - is_ok = True - path_st_mode = os.stat(dir_obj.path).st_mode - is_ok == is_ok and path_st_mode & stat.S_IRUSR - is_ok == is_ok and path_st_mode & stat.S_IWUSR - is_ok == is_ok and path_st_mode & stat.S_IXUSR - return is_ok + """Check if we have read-write-execute permissions, returns bool.""" + is_ok = True + path_st_mode = os.stat(dir_obj.path).st_mode + is_ok == is_ok and path_st_mode & stat.S_IRUSR + is_ok == is_ok and path_st_mode & stat.S_IWUSR + is_ok == is_ok and path_st_mode & stat.S_IXUSR + return is_ok def is_writable_filesystem(dir_obj): - """Check if filesystem is mounted read-write, returns bool.""" - return 'rw' in dir_obj.details.get('options', '') + """Check if filesystem is mounted read-write, returns bool.""" + return 'rw' in dir_obj.details.get('options', '') def menu_ddrescue(source_path, dest_path, run_mode): - """ddrescue menu.""" - source = None - dest = None - if source_path: - source = create_path_obj(source_path) - else: - source = select_device('source') - source.self_check() - if dest_path: - dest = create_path_obj(dest_path) - else: - if run_mode == 'clone': - dest = select_device('destination', skip_device=source) - else: - dest = select_path(skip_device=source) - dest.self_check() - - # Build BlockPairs - state = RecoveryState(run_mode, source, dest) + """ddrescue menu.""" + source = None + dest = None + if source_path: + source = create_path_obj(source_path) + else: + source = select_device('source') + source.self_check() + if dest_path: + dest = create_path_obj(dest_path) + else: if run_mode == 'clone': - state.add_block_pair(source, dest) + dest = select_device('destination', skip_device=source) else: - for part in select_parts(source): - state.add_block_pair(part, dest) + dest = select_path(skip_device=source) + dest.self_check() - # Update state - state.self_checks() - state.set_pass_num() - state.update_progress() + # Build BlockPairs + state = RecoveryState(run_mode, source, dest) + if run_mode == 'clone': + state.add_block_pair(source, dest) + else: + for part in select_parts(source): + state.add_block_pair(part, dest) - # Confirmations - clear_screen() - show_selection_details(state) - prompt = 'Start {}?'.format(state.mode.replace('e', 'ing')) - if state.resumed: - print_info('Map data detected and loaded.') - prompt = prompt.replace('Start', 'Resume') - if not ask(prompt): - raise GenericAbort() - if state.mode == 'clone' and not double_confirm_clone(): - raise GenericAbort() + # Update state + state.self_checks() + state.set_pass_num() + state.update_progress() - # Main menu - build_outer_panes(state) - menu_main(state) + # Confirmations + clear_screen() + show_selection_details(state) + prompt = 'Start {}?'.format(state.mode.replace('e', 'ing')) + if state.resumed: + print_info('Map data detected and loaded.') + prompt = prompt.replace('Start', 'Resume') + if not ask(prompt): + raise GenericAbort() + if state.mode == 'clone' and not double_confirm_clone(): + raise GenericAbort() + + # Main menu + clear_screen() + build_outer_panes(state) + fix_tmux_panes(state, forced=True) + menu_main(state) + + # Done + run_program(['tmux', 'kill-window']) + exit_script() - # Done - run_program(['tmux', 'kill-window']) - exit_script() def menu_main(state): - """Main menu is used to set ddrescue settings.""" - title = '{GREEN}ddrescue TUI: Main Menu{CLEAR}\n\n'.format(**COLORS) - title += '{BLUE}Current pass: {CLEAR}'.format(**COLORS) + """Main menu is used to set ddrescue settings.""" + checkmark = '*' + if 'DISPLAY' in global_vars['Env']: + checkmark = '✓' + title = '{GREEN}ddrescue TUI: Main Menu{CLEAR}\n\n'.format(**COLORS) + title += '{BLUE}Current pass: {CLEAR}'.format(**COLORS) - # Build menu - main_options = [ - {'Base Name': 'Auto continue (if recovery % over threshold)', - 'Enabled': True}, - {'Base Name': 'Retry (mark non-rescued sectors "non-tried")', - 'Enabled': False}, - {'Base Name': 'Reverse direction', 'Enabled': False}, - ] - actions = [ - {'Name': 'Start', 'Letter': 'S'}, - {'Name': 'Change settings {YELLOW}(experts only){CLEAR}'.format( - **COLORS), - 'Letter': 'C'}, - {'Name': 'Quit', 'Letter': 'Q', 'CRLF': True}, - ] + # Build menu + main_options = [ + {'Base Name': 'Auto continue (if recovery % over threshold)', + 'Enabled': True}, + {'Base Name': 'Retry (mark non-rescued sectors "non-tried")', + 'Enabled': False}, + {'Base Name': 'Reverse direction', 'Enabled': False}, + ] + actions = [ + {'Name': 'Start', 'Letter': 'S'}, + {'Name': 'Change settings {YELLOW}(experts only){CLEAR}'.format( + **COLORS), + 'Letter': 'C'}, + {'Name': 'Quit', 'Letter': 'Q', 'CRLF': True}, + ] - # Show menu - while True: - # Update entries - for opt in main_options: - opt['Name'] = '{} {}'.format( - '[✓]' if opt['Enabled'] else '[ ]', - opt['Base Name']) + # Show menu + while True: + # Update entries + for opt in main_options: + opt['Name'] = '[{}] {}'.format( + checkmark if opt['Enabled'] else ' ', + opt['Base Name']) - selection = menu_select( - title=title+state.current_pass_str, - main_entries=main_options, - action_entries=actions) + selection = menu_select( + title=title+state.current_pass_str, + main_entries=main_options, + action_entries=actions) - if selection.isnumeric(): - # Toggle selection - index = int(selection) - 1 - main_options[index]['Enabled'] = not main_options[index]['Enabled'] - elif selection == 'S': - # Set settings for pass - pass_settings = [] - for k, v in state.settings.items(): - if not v['Enabled']: - continue - if 'Value' in v: - pass_settings.append('{}={}'.format(k, v['Value'])) - else: - pass_settings.append(k) - for opt in main_options: - if 'Auto' in opt['Base Name']: - auto_run = opt['Enabled'] - if 'Retry' in opt['Base Name'] and opt['Enabled']: - pass_settings.extend(['--retrim', '--try-again']) - state.retry_all_passes() - if 'Reverse' in opt['Base Name'] and opt['Enabled']: - pass_settings.append('--reverse') - # Disable for next pass - if 'Auto' not in opt['Base Name']: - opt['Enabled'] = False + if selection.isnumeric(): + # Toggle selection + index = int(selection) - 1 + main_options[index]['Enabled'] = not main_options[index]['Enabled'] + elif selection == 'S': + # Set settings for pass + pass_settings = [] + for k, v in state.settings.items(): + if not v['Enabled']: + continue + if 'Value' in v: + pass_settings.append('{}={}'.format(k, v['Value'])) + else: + pass_settings.append(k) + for opt in main_options: + if 'Auto' in opt['Base Name']: + auto_run = opt['Enabled'] + if 'Retry' in opt['Base Name'] and opt['Enabled']: + pass_settings.extend(['--retrim', '--try-again']) + state.retry_all_passes() + if 'Reverse' in opt['Base Name'] and opt['Enabled']: + pass_settings.append('--reverse') + # Disable for next pass + if 'Auto' not in opt['Base Name']: + opt['Enabled'] = False - # Run ddrescue - state.started = False - while auto_run or not state.started: - state.started = True - run_ddrescue(state, pass_settings) - if state.current_pass_done(): - if (state.current_pass == 0 and - state.current_pass_min() < AUTO_PASS_1_THRESHOLD): - auto_run = False - elif (state.current_pass == 1 and - state.current_pass_min() < AUTO_PASS_2_THRESHOLD): - auto_run = False - else: - auto_run = False - state.set_pass_num() - if state.finished: - break + # Run ddrescue + state.started = False + while auto_run or not state.started: + state.started = True + run_ddrescue(state, pass_settings) + if state.current_pass_done(): + if (state.current_pass == 0 and + state.current_pass_min() < AUTO_PASS_1_THRESHOLD): + auto_run = False + elif (state.current_pass == 1 and + state.current_pass_min() < AUTO_PASS_2_THRESHOLD): + auto_run = False + else: + auto_run = False + state.set_pass_num() + if state.finished: + break - elif selection == 'C': - menu_settings(state) - elif selection == 'Q': - if state.rescued_percent < 100: - print_warning('Recovery is less than 100%') - if ask('Are you sure you want to quit?'): - break - else: - break + elif selection == 'C': + menu_settings(state) + elif selection == 'Q': + if state.rescued_percent < 100: + print_warning('Recovery is less than 100%') + if ask('Are you sure you want to quit?'): + break + else: + break def menu_settings(state): - """Change advanced ddrescue settings.""" - title = '{GREEN}ddrescue TUI: Expert Settings{CLEAR}\n\n'.format(**COLORS) - title += '{YELLOW}These settings can cause {CLEAR}'.format(**COLORS) - title += '{RED}MAJOR DAMAGE{CLEAR}{YELLOW} to drives{CLEAR}\n'.format( - **COLORS) - title += 'Please read the manual before making any changes' + """Change advanced ddrescue settings.""" + title = '{GREEN}ddrescue TUI: Expert Settings{CLEAR}\n\n'.format(**COLORS) + title += '{YELLOW}These settings can cause {CLEAR}'.format(**COLORS) + title += '{RED}MAJOR DAMAGE{CLEAR}{YELLOW} to drives{CLEAR}\n'.format( + **COLORS) + title += 'Please read the manual before making any changes' - # Build menu - settings = [] - for k, v in sorted(state.settings.items()): - if not v.get('Hidden', False): - settings.append({'Base Name': k, 'Flag': k}) - actions = [{'Name': 'Main Menu', 'Letter': 'M'}] + # Build menu + settings = [] + for k, v in sorted(state.settings.items()): + if not v.get('Hidden', False): + settings.append({'Base Name': k, 'Flag': k}) + actions = [{'Name': 'Main Menu', 'Letter': 'M'}] - # Show menu - while True: - for s in settings: - s['Name'] = '{}{}{}'.format( - s['Base Name'], - ' = ' if 'Value' in state.settings[s['Flag']] else '', - state.settings[s['Flag']].get('Value', '')) - if not state.settings[s['Flag']]['Enabled']: - s['Name'] = '{YELLOW}{name} (Disabled){CLEAR}'.format( - name=s['Name'], - **COLORS) - selection = menu_select( - title=title, - main_entries=settings, - action_entries=actions) - if selection.isnumeric(): - index = int(selection) - 1 - flag = settings[index]['Flag'] - enabled = state.settings[flag]['Enabled'] - if 'Value' in state.settings[flag]: - answer = choice( - choices=['T', 'C'], - prompt='Toggle or change value for "{}"'.format(flag)) - if answer == 'T': - # Toggle - state.settings[flag]['Enabled'] = not enabled - else: - # Update value - state.settings[flag]['Value'] = get_simple_string( - prompt='Enter new value') - else: - state.settings[flag]['Enabled'] = not enabled - elif selection == 'M': - break + # Show menu + while True: + for s in settings: + s['Name'] = '{}{}{}'.format( + s['Base Name'], + ' = ' if 'Value' in state.settings[s['Flag']] else '', + state.settings[s['Flag']].get('Value', '')) + if not state.settings[s['Flag']]['Enabled']: + s['Name'] = '{YELLOW}{name} (Disabled){CLEAR}'.format( + name=s['Name'], + **COLORS) + selection = menu_select( + title=title, + main_entries=settings, + action_entries=actions) + if selection.isnumeric(): + index = int(selection) - 1 + flag = settings[index]['Flag'] + enabled = state.settings[flag]['Enabled'] + if 'Value' in state.settings[flag]: + answer = choice( + choices=['T', 'C'], + prompt='Toggle or change value for "{}"'.format(flag)) + if answer == 'T': + # Toggle + state.settings[flag]['Enabled'] = not enabled + else: + # Update value + state.settings[flag]['Value'] = get_simple_string( + prompt='Enter new value') + else: + state.settings[flag]['Enabled'] = not enabled + elif selection == 'M': + break def read_map_file(map_path): - """Read map file with ddrescuelog and return data as dict.""" - map_data = {'full recovery': False} - try: - result = run_program(['ddrescuelog', '-t', map_path]) - except CalledProcessError: - # (Grossly) assuming map_data hasn't been saved yet, return empty dict - return map_data - - # Parse output - for line in result.stdout.decode().splitlines(): - m = re.match( - r'^\s*(?P\S+):.*\(\s*(?P\d+\.?\d*)%.*', line.strip()) - if m: - try: - map_data[m.group('key')] = float(m.group('value')) - except ValueError: - raise GenericError('Failed to read map data') - m = re.match(r'.*current status:\s+(?P.*)', line.strip()) - if m: - map_data['pass completed'] = bool(m.group('status') == 'finished') - - # Check if 100% done - try: - run_program(['ddrescuelog', '-D', map_path]) - except CalledProcessError: - map_data['full recovery'] = False - else: - map_data['full recovery'] = True - + """Read map file with ddrescuelog and return data as dict.""" + map_data = {'full recovery': False} + try: + result = run_program(['ddrescuelog', '-t', map_path]) + except CalledProcessError: + # (Grossly) assuming map_data hasn't been saved yet, return empty dict return map_data + # Parse output + for line in result.stdout.decode().splitlines(): + m = re.match( + r'^\s*(?P\S+):.*\(\s*(?P\d+\.?\d*)%.*', line.strip()) + if m: + try: + map_data[m.group('key')] = float(m.group('value')) + except ValueError: + raise GenericError('Failed to read map data') + m = re.match(r'.*current status:\s+(?P.*)', line.strip()) + if m: + map_data['pass completed'] = bool(m.group('status') == 'finished') + + # Check if 100% done + try: + run_program(['ddrescuelog', '-D', map_path]) + except CalledProcessError: + map_data['full recovery'] = False + else: + map_data['full recovery'] = True + + return map_data + def run_ddrescue(state, pass_settings): - """Run ddrescue pass.""" - return_code = None + """Run ddrescue pass.""" + return_code = -1 + aborted = False - if state.finished: - clear_screen() - print_warning('Recovery already completed?') - pause('Press Enter to return to main menu...') - return + if state.finished: + clear_screen() + print_warning('Recovery already completed?') + pause('Press Enter to return to main menu...') + return - # Set heights - # NOTE: 12/33 is based on min heights for SMART/ddrescue panes (12+22+1sep) - result = run_program(['tput', 'lines']) - height = int(result.stdout.decode().strip()) - height_smart = int(height * (8 / 33)) - height_journal = int(height * (4 / 33)) - height_ddrescue = height - height_smart - height_journal + # Create SMART monitor pane + state.smart_out = '{}/smart_{}.out'.format( + global_vars['TmpDir'], state.smart_source.name) + with open(state.smart_out, 'w') as f: + f.write('Initializing...') + state.panes['SMART'] = tmux_split_window( + behind=True, lines=12, vertical=True, watch=state.smart_out) - # Show SMART status - smart_dev = state.source_path - if state.source.parent: - smart_dev = state.source.parent - smart_pane = tmux_splitw( - '-bdvl', str(height_smart), - '-PF', '#D', - 'watch', '--color', '--no-title', '--interval', '300', - 'ddrescue-tui-smart-display', smart_dev) + # Show systemd journal output + state.panes['Journal'] = tmux_split_window( + lines=4, vertical=True, + command=['sudo', 'journalctl', '-f']) - # Show systemd journal output - journal_pane = tmux_splitw( - '-dvl', str(height_journal), - '-PF', '#D', - 'journalctl', '-f') + # Fix layout + fix_tmux_panes(state, forced=True) - # Run pass for each block-pair - for bp in state.block_pairs: - if bp.pass_done[state.current_pass]: - # Skip to next block-pair - continue - update_sidepane(state) + # Run pass for each block-pair + for bp in state.block_pairs: + if bp.pass_done[state.current_pass]: + # Skip to next block-pair + continue + update_sidepane(state) - # Set ddrescue cmd - cmd = [ - 'ddrescue', *pass_settings, - bp.source_path, bp.dest_path, bp.map_path] - if state.mode == 'clone': - cmd.append('--force') - if state.current_pass == 0: - cmd.extend(['--no-trim', '--no-scrape']) - elif state.current_pass == 1: - # Allow trimming - cmd.append('--no-scrape') - elif state.current_pass == 2: - # Allow trimming and scraping - pass + # Set ddrescue cmd + cmd = [ + 'ddrescue', *pass_settings, + bp.source_path, bp.dest_path, bp.map_path] + if state.mode == 'clone': + cmd.append('--force') + if state.current_pass == 0: + cmd.extend(['--no-trim', '--no-scrape']) + elif state.current_pass == 1: + # Allow trimming + cmd.append('--no-scrape') + elif state.current_pass == 2: + # Allow trimming and scraping + pass - # Start ddrescue - try: - clear_screen() - print_info('Current dev: {}'.format(bp.source_path)) - ddrescue_proc = popen_program(cmd) - while True: - bp.update_progress(state.current_pass) - update_sidepane(state) - try: - ddrescue_proc.wait(timeout=10) - sleep(2) - bp.update_progress(state.current_pass) - update_sidepane(state) - break - except subprocess.TimeoutExpired: - # Catch to update bp/sidepane - pass - except KeyboardInterrupt: - # Catch user abort - pass + # Start ddrescue + try: + clear_screen() + print_info('Current dev: {}'.format(bp.source_path)) + ddrescue_proc = popen_program(cmd) + i = 0 + while True: + # Update SMART display (every 30 seconds) + if i % 30 == 0: + state.smart_source.get_smart_details() + with open(state.smart_out, 'w') as f: + report = state.smart_source.generate_attribute_report( + timestamp=True) + for line in report: + f.write('{}\n'.format(line)) + i += 1 - # Update progress/sidepane again + # Update progress bp.update_progress(state.current_pass) update_sidepane(state) - # Was ddrescue aborted? - return_code = ddrescue_proc.poll() - if return_code is None or return_code is 130: - clear_screen() - print_warning('Aborted') - break - elif return_code: - # i.e. not None and not 0 - print_error('Error(s) encountered, see message above.') - break - else: - # Mark pass finished - bp.finish_pass(state.current_pass) - update_sidepane(state) + # Fix panes + fix_tmux_panes(state) - # Done - if str(return_code) != '0': - # Pause on errors - pause('Press Enter to return to main menu... ') - run_program(['tmux', 'kill-pane', '-t', smart_pane]) - run_program(['tmux', 'kill-pane', '-t', journal_pane]) + # Check if ddrescue has finished + try: + ddrescue_proc.wait(timeout=1) + sleep(2) + bp.update_progress(state.current_pass) + update_sidepane(state) + break + except subprocess.TimeoutExpired: + # Catch to update smart/bp/sidepane + pass + + except KeyboardInterrupt: + # Catch user abort + aborted = True + ddrescue_proc.wait(timeout=10) + + # Update progress/sidepane again + bp.update_progress(state.current_pass) + update_sidepane(state) + + # Was ddrescue aborted? + return_code = ddrescue_proc.poll() + if aborted: + print_standard(' ') + print_standard(' ') + print_error('DDRESCUE PROCESS HALTED') + print_standard(' ') + print_warning('Aborted') + break + elif return_code: + # i.e. True when non-zero + print_standard(' ') + print_standard(' ') + print_error('DDRESCUE PROCESS HALTED') + print_standard(' ') + print_error('Error(s) encountered, see message above.') + break + else: + # Mark pass finished + bp.finish_pass(state.current_pass) + update_sidepane(state) + + # Done + if str(return_code) != '0': + # Pause on errors + pause('Press Enter to return to main menu... ') + + # Cleanup + tmux_kill_pane(state.panes['SMART'], state.panes['Journal']) def select_parts(source_device): - """Select partition(s) or whole device, returns list of DevObj()s.""" - selected_parts = [] - children = source_device.details.get('children', []) + """Select partition(s) or whole device, returns list of DevObj()s.""" + selected_parts = [] + children = source_device.details.get('children', []) - if not children: - # No partitions detected, auto-select whole device. - selected_parts = [source_device] - else: - # Build menu - dev_options = [{ - 'Base Name': '{:<14}(Whole device)'.format(source_device.path), - 'Dev': source_device, - 'Selected': True}] - for c_details in children: - dev_options.append({ - 'Base Name': '{:<14}({:>6} {})'.format( - c_details['name'], - c_details['size'], - c_details['fstype'] if c_details['fstype'] else 'Unknown'), - 'Details': c_details, - 'Dev': DevObj(c_details['name']), - 'Selected': False}) - actions = [ - {'Name': 'Proceed', 'Letter': 'P'}, - {'Name': 'Quit', 'Letter': 'Q'}] + if not children: + # No partitions detected, auto-select whole device. + selected_parts = [source_device] + else: + # Build menu + dev_options = [{ + 'Base Name': '{:<14}(Whole device)'.format(source_device.path), + 'Dev': source_device, + 'Selected': True}] + for c_details in children: + dev_options.append({ + 'Base Name': '{:<14}({:>6} {})'.format( + c_details['name'], + c_details['size'], + c_details['fstype'] if c_details['fstype'] else 'Unknown'), + 'Details': c_details, + 'Dev': DevObj(c_details['name']), + 'Selected': False}) + actions = [ + {'Name': 'Proceed', 'Letter': 'P'}, + {'Name': 'Quit', 'Letter': 'Q'}] - # Show menu - while True: - one_or_more_devs_selected = False - # Update entries - for dev in dev_options: - if dev['Selected']: - one_or_more_devs_selected = True - dev['Name'] = '* {}'.format(dev['Base Name']) - else: - dev['Name'] = ' {}'.format(dev['Base Name']) + # Show menu + while True: + one_or_more_devs_selected = False + # Update entries + for dev in dev_options: + if dev['Selected']: + one_or_more_devs_selected = True + dev['Name'] = '* {}'.format(dev['Base Name']) + else: + dev['Name'] = ' {}'.format(dev['Base Name']) - selection = menu_select( - title='Please select part(s) to image', - main_entries=dev_options, - action_entries=actions) + selection = menu_select( + title='Please select part(s) to image', + main_entries=dev_options, + action_entries=actions) - if selection.isnumeric(): - # Toggle selection - index = int(selection) - 1 - dev_options[index]['Selected'] = not dev_options[index]['Selected'] + if selection.isnumeric(): + # Toggle selection + index = int(selection) - 1 + dev_options[index]['Selected'] = not dev_options[index]['Selected'] - # Deselect whole device if child selected (this round) - if index > 0: - dev_options[0]['Selected'] = False + # Deselect whole device if child selected (this round) + if index > 0: + dev_options[0]['Selected'] = False - # Deselect all children if whole device selected - if dev_options[0]['Selected']: - for dev in dev_options[1:]: - dev['Selected'] = False - elif selection == 'P' and one_or_more_devs_selected: - break - elif selection == 'Q': - raise GenericAbort() + # Deselect all children if whole device selected + if dev_options[0]['Selected']: + for dev in dev_options[1:]: + dev['Selected'] = False + elif selection == 'P' and one_or_more_devs_selected: + break + elif selection == 'Q': + raise GenericAbort() - # Build list of selected parts - for d in dev_options: - if d['Selected']: - d['Dev'].model = source_device.model - d['Dev'].model_size = source_device.model_size - d['Dev'].update_filename_prefix() - selected_parts.append(d['Dev']) + # Build list of selected parts + for d in dev_options: + if d['Selected']: + d['Dev'].model = source_device.model + d['Dev'].model_size = source_device.model_size + d['Dev'].update_filename_prefix() + selected_parts.append(d['Dev']) - return selected_parts + return selected_parts def select_path(skip_device=None): - """Optionally mount local dev and select path, returns DirObj.""" - wd = os.path.realpath(global_vars['Env']['PWD']) - selected_path = None + """Optionally mount local dev and select path, returns DirObj.""" + wd = os.path.realpath(global_vars['Env']['PWD']) + selected_path = None - # Build menu - path_options = [ - {'Name': 'Current directory: {}'.format(wd), 'Path': wd}, - {'Name': 'Local device', 'Path': None}, - {'Name': 'Enter manually', 'Path': None}] - actions = [{'Name': 'Quit', 'Letter': 'Q'}] + # Build menu + path_options = [ + {'Name': 'Current directory: {}'.format(wd), 'Path': wd}, + {'Name': 'Local device', 'Path': None}, + {'Name': 'Enter manually', 'Path': None}] + actions = [{'Name': 'Quit', 'Letter': 'Q'}] - # Show Menu - selection = menu_select( - title='Please make a selection', - main_entries=path_options, + # Show Menu + selection = menu_select( + title='Please make a selection', + main_entries=path_options, + action_entries=actions) + + if selection == 'Q': + raise GenericAbort() + elif selection.isnumeric(): + index = int(selection) - 1 + if path_options[index]['Path'] == wd: + # Current directory + selected_path = DirObj(wd) + + elif path_options[index]['Name'] == 'Local device': + # Local device + local_device = select_device( + skip_device=skip_device) + s_path = '' + + # Mount device volume(s) + report = mount_volumes( + all_devices=False, + device_path=local_device.path, + read_write=True) + + # Select volume + vol_options = [] + for k, v in sorted(report.items()): + disabled = v['show_data']['data'] == 'Failed to mount' + if disabled: + name = '{name} (Failed to mount)'.format(**v) + else: + name = '{name} (mounted on "{mount_point}")'.format(**v) + vol_options.append({ + 'Name': name, + 'Path': v['mount_point'], + 'Disabled': disabled}) + selection = menu_select( + title='Please select a volume', + main_entries=vol_options, action_entries=actions) - - if selection == 'Q': + if selection.isnumeric(): + s_path = vol_options[int(selection)-1]['Path'] + elif selection == 'Q': raise GenericAbort() - elif selection.isnumeric(): - index = int(selection) - 1 - if path_options[index]['Path'] == wd: - # Current directory - selected_path = DirObj(wd) - elif path_options[index]['Name'] == 'Local device': - # Local device - local_device = select_device( - skip_device=skip_device) - s_path = '' + # Create folder + if ask('Create ticket folder?'): + ticket_folder = get_simple_string('Please enter folder name') + s_path = os.path.join(s_path, ticket_folder) + try: + os.makedirs(s_path, exist_ok=True) + except OSError: + raise GenericError( + 'Failed to create folder "{}"'.format(s_path)) - # Mount device volume(s) - report = mount_volumes( - all_devices=False, - device_path=local_device.path, - read_write=True) + # Create DirObj + selected_path = DirObj(s_path) - # Select volume - vol_options = [] - for k, v in sorted(report.items()): - disabled = v['show_data']['data'] == 'Failed to mount' - if disabled: - name = '{name} (Failed to mount)'.format(**v) - else: - name = '{name} (mounted on "{mount_point}")'.format(**v) - vol_options.append({ - 'Name': name, - 'Path': v['mount_point'], - 'Disabled': disabled}) - selection = menu_select( - title='Please select a volume', - main_entries=vol_options, - action_entries=actions) - if selection.isnumeric(): - s_path = vol_options[int(selection)-1]['Path'] - elif selection == 'Q': - raise GenericAbort() - - # Create folder - if ask('Create ticket folder?'): - ticket_folder = get_simple_string('Please enter folder name') - s_path = os.path.join(s_path, ticket_folder) - try: - os.makedirs(s_path, exist_ok=True) - except OSError: - raise GenericError( - 'Failed to create folder "{}"'.format(s_path)) - - # Create DirObj - selected_path = DirObj(s_path) - - elif path_options[index]['Name'] == 'Enter manually': - # Manual entry - while not selected_path: - manual_path = input('Please enter path: ').strip() - if manual_path and pathlib.Path(manual_path).is_dir(): - selected_path = DirObj(manual_path) - elif manual_path and pathlib.Path(manual_path).is_file(): - print_error('File "{}" exists'.format(manual_path)) - else: - print_error('Invalid path "{}"'.format(manual_path)) - return selected_path + elif path_options[index]['Name'] == 'Enter manually': + # Manual entry + while not selected_path: + manual_path = input('Please enter path: ').strip() + if manual_path and pathlib.Path(manual_path).is_dir(): + selected_path = DirObj(manual_path) + elif manual_path and pathlib.Path(manual_path).is_file(): + print_error('File "{}" exists'.format(manual_path)) + else: + print_error('Invalid path "{}"'.format(manual_path)) + return selected_path def select_device(description='device', skip_device=None): - """Select device via a menu, returns DevObj.""" - cmd = ( - 'lsblk', - '--json', - '--nodeps', - '--output-all', - '--paths') - result = run_program(cmd) - json_data = json.loads(result.stdout.decode()) - skip_names = [] - if skip_device: - skip_names.append(skip_device.path) - if skip_device.parent: - skip_names.append(skip_device.parent) + """Select device via a menu, returns DevObj.""" + cmd = ( + 'lsblk', + '--json', + '--nodeps', + '--output-all', + '--paths') + result = run_program(cmd) + json_data = json.loads(result.stdout.decode()) + skip_names = [] + if skip_device: + skip_names.append(skip_device.path) + if skip_device.parent: + skip_names.append(skip_device.parent) - # Build menu - dev_options = [] - for dev in json_data['blockdevices']: - # Disable dev if in skip_names - disabled = dev['name'] in skip_names or dev['pkname'] in skip_names + # Build menu + dev_options = [] + for dev in json_data['blockdevices']: + # Disable dev if in skip_names + disabled = dev['name'] in skip_names or dev['pkname'] in skip_names - # Add to options - dev_options.append({ - 'Name': '{name:12} {tran:5} {size:6} {model} {serial}'.format( - name=dev['name'], - tran=dev['tran'] if dev['tran'] else '', - size=dev['size'] if dev['size'] else '', - model=dev['model'] if dev['model'] else '', - serial=dev['serial'] if dev['serial'] else ''), - 'Dev': DevObj(dev['name']), - 'Disabled': disabled}) - dev_options = sorted(dev_options, key=itemgetter('Name')) - if not dev_options: - raise GenericError('No devices available.') + # Add to options + dev_options.append({ + 'Name': '{name:12} {tran:5} {size:6} {model} {serial}'.format( + name=dev['name'], + tran=dev['tran'] if dev['tran'] else '', + size=dev['size'] if dev['size'] else '', + model=dev['model'] if dev['model'] else '', + serial=dev['serial'] if dev['serial'] else ''), + 'Dev': DevObj(dev['name']), + 'Disabled': disabled}) + dev_options = sorted(dev_options, key=itemgetter('Name')) + if not dev_options: + raise GenericError('No devices available.') - # Show Menu - actions = [{'Name': 'Quit', 'Letter': 'Q'}] - selection = menu_select( - title='Please select the {} device'.format(description), - main_entries=dev_options, - action_entries=actions, - disabled_label='ALREADY SELECTED') + # Show Menu + actions = [{'Name': 'Quit', 'Letter': 'Q'}] + selection = menu_select( + title='Please select the {} device'.format(description), + main_entries=dev_options, + action_entries=actions, + disabled_label='ALREADY SELECTED') - if selection.isnumeric(): - return dev_options[int(selection)-1]['Dev'] - elif selection == 'Q': - raise GenericAbort() + if selection.isnumeric(): + return dev_options[int(selection)-1]['Dev'] + elif selection == 'Q': + raise GenericAbort() def setup_loopback_device(source_path): - """Setup a loopback device for source_path, returns dev_path as str.""" - cmd = ( - 'losetup', - '--find', - '--partscan', - '--show', - source_path) - try: - out = run_program(cmd, check=True) - dev_path = out.stdout.decode().strip() - sleep(1) - except CalledProcessError: - raise GenericError('Failed to setup loopback device for source.') - else: - return dev_path + """Setup loopback device for source_path, returns dev_path as str.""" + cmd = ( + 'losetup', + '--find', + '--partscan', + '--show', + source_path) + try: + out = run_program(cmd, check=True) + dev_path = out.stdout.decode().strip() + sleep(1) + except CalledProcessError: + raise GenericError('Failed to setup loopback device for source.') + else: + return dev_path def show_selection_details(state): - """Show selection details.""" - # Source - print_success('Source') - print_standard(state.source.report) - print_standard(' ') + """Show selection details.""" + # Source + print_success('Source') + print_standard(state.source.report) + print_standard(' ') - # Destination - if state.mode == 'clone': - print_success('Destination ', end='') - print_error('(ALL DATA WILL BE DELETED)', timestamp=False) - else: - print_success('Destination') - print_standard(state.dest.report) - print_standard(' ') + # Destination + if state.mode == 'clone': + print_success('Destination ', end='') + print_error('(ALL DATA WILL BE DELETED)', timestamp=False) + else: + print_success('Destination') + print_standard(state.dest.report) + print_standard(' ') def show_usage(script_name): - print_info('Usage:') - print_standard(USAGE.format(script_name=script_name)) - pause() - - -def tmux_splitw(*args): - """Run tmux split-window command and return output as str.""" - cmd = ['tmux', 'split-window', *args] - result = run_program(cmd) - return result.stdout.decode().strip() + print_info('Usage:') + print_standard(USAGE.format(script_name=script_name)) + pause() def update_sidepane(state): - """Update progress file for side pane.""" - output = [] - state.update_progress() - if state.mode == 'clone': - output.append(' {BLUE}Cloning Status{CLEAR}'.format(**COLORS)) + """Update progress file for side pane.""" + output = [] + state.update_progress() + if state.mode == 'clone': + output.append(' {BLUE}Cloning Status{CLEAR}'.format(**COLORS)) + else: + output.append(' {BLUE}Imaging Status{CLEAR}'.format(**COLORS)) + output.append('─────────────────────') + + # Overall progress + output.append('{BLUE}Overall Progress{CLEAR}'.format(**COLORS)) + output.append(state.status_percent) + output.append(state.status_amount) + output.append('─────────────────────') + + # Source(s) progress + for bp in state.block_pairs: + if state.source.is_image(): + output.append('{BLUE}Image File{CLEAR}'.format(**COLORS)) else: - output.append(' {BLUE}Imaging Status{CLEAR}'.format(**COLORS)) - output.append('─────────────────────') + output.append('{BLUE}{source}{CLEAR}'.format( + source=bp.source_path, + **COLORS)) + output.extend(bp.status) + output.append(' ') - # Overall progress - output.append('{BLUE}Overall Progress{CLEAR}'.format(**COLORS)) - output.append(state.status_percent) - output.append(state.status_amount) - output.append('─────────────────────') + # Add line-endings + output = ['{}\n'.format(line) for line in output] - # Source(s) progress - for bp in state.block_pairs: - if state.source.is_image(): - output.append('{BLUE}Image File{CLEAR}'.format(**COLORS)) - else: - output.append('{BLUE}{source}{CLEAR}'.format( - source=bp.source_path, - **COLORS)) - output.extend(bp.status) - output.append(' ') - - # Add line-endings - output = ['{}\n'.format(line) for line in output] - - with open(state.progress_out, 'w') as f: - f.writelines(output) + with open(state.progress_out, 'w') as f: + f.writelines(output) if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") -# vim: sts=4 sw=4 ts=4 +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/diags.py b/.bin/Scripts/functions/diags.py deleted file mode 100644 index e55f5b12..00000000 --- a/.bin/Scripts/functions/diags.py +++ /dev/null @@ -1,189 +0,0 @@ -# Wizard Kit: Functions - Diagnostics - -import ctypes - -from functions.common import * - -# STATIC VARIABLES -AUTORUNS_SETTINGS = { - r'Software\Sysinternals\AutoRuns': { - 'checkvirustotal': 1, - 'EulaAccepted': 1, - 'shownomicrosoft': 1, - 'shownowindows': 1, - 'showonlyvirustotal': 1, - 'submitvirustotal': 0, - 'verifysignatures': 1, - }, - r'Software\Sysinternals\AutoRuns\SigCheck': { - 'EulaAccepted': 1, - }, - r'Software\Sysinternals\AutoRuns\Streams': { - 'EulaAccepted': 1, - }, - r'Software\Sysinternals\AutoRuns\VirusTotal': { - 'VirusTotalTermsAccepted': 1, - }, - } - -def check_connection(): - """Check if the system is online and optionally abort the script.""" - while True: - result = try_and_print(message='Ping test...', function=ping, cs='OK') - if result['CS']: - break - if not ask('ERROR: System appears offline, try again?'): - if ask('Continue anyway?'): - break - else: - abort() - -def check_secure_boot_status(show_alert=False): - """Checks UEFI Secure Boot status via PowerShell.""" - boot_mode = get_boot_mode() - cmd = ['PowerShell', '-Command', 'Confirm-SecureBootUEFI'] - result = run_program(cmd, check=False) - - # Check results - if result.returncode == 0: - out = result.stdout.decode() - if 'True' in out: - # It's on, do nothing - return - elif 'False' in out: - if show_alert: - show_alert_box('Secure Boot DISABLED') - raise SecureBootDisabledError - else: - if show_alert: - show_alert_box('Secure Boot status UNKNOWN') - raise SecureBootUnknownError - else: - if boot_mode != 'UEFI': - if (show_alert and - global_vars['OS']['Version'] in ('8', '8.1', '10')): - # OS supports Secure Boot - show_alert_box('Secure Boot DISABLED\n\nOS installed LEGACY') - raise OSInstalledLegacyError - else: - # Check error message - err = result.stderr.decode() - if 'Cmdlet not supported' in err: - if show_alert: - show_alert_box('Secure Boot UNAVAILABLE?') - raise SecureBootNotAvailError - else: - if show_alert: - show_alert_box('Secure Boot ERROR') - raise GenericError - -def get_boot_mode(): - """Check if Windows is booted in UEFI or Legacy mode, returns str.""" - kernel = ctypes.windll.kernel32 - firmware_type = ctypes.c_uint() - - # Get value from kernel32 API - try: - kernel.GetFirmwareType(ctypes.byref(firmware_type)) - except: - # Just set to zero - firmware_type = ctypes.c_uint(0) - - # Set return value - type_str = 'Unknown' - if firmware_type.value == 1: - type_str = 'Legacy' - elif firmware_type.value == 2: - type_str = 'UEFI' - - return type_str - -def run_autoruns(): - """Run AutoRuns in the background with VirusTotal checks enabled.""" - extract_item('Autoruns', filter='autoruns*', silent=True) - # Update AutoRuns settings before running - for path, settings in AUTORUNS_SETTINGS.items(): - winreg.CreateKey(HKCU, path) - with winreg.OpenKey(HKCU, path, access=winreg.KEY_WRITE) as key: - for name, value in settings.items(): - winreg.SetValueEx(key, name, 0, winreg.REG_DWORD, value) - popen_program(global_vars['Tools']['AutoRuns'], minimized=True) - -def run_hwinfo_sensors(): - """Run HWiNFO sensors.""" - path = r'{BinDir}\HWiNFO'.format(**global_vars) - for bit in [32, 64]: - # Configure - source = r'{}\general.ini'.format(path) - dest = r'{}\HWiNFO{}.ini'.format(path, bit) - shutil.copy(source, dest) - with open(dest, 'a') as f: - f.write('SensorsOnly=1\n') - f.write('SummaryOnly=0\n') - popen_program(global_vars['Tools']['HWiNFO']) - -def run_nircmd(*cmd): - """Run custom NirCmd.""" - extract_item('NirCmd', silent=True) - cmd = [global_vars['Tools']['NirCmd'], *cmd] - run_program(cmd, check=False) - -def run_xmplay(): - """Run XMPlay to test audio.""" - extract_item('XMPlay', silent=True) - cmd = [global_vars['Tools']['XMPlay'], - r'{BinDir}\XMPlay\music.7z'.format(**global_vars)] - - # Unmute audio first - extract_item('NirCmd', silent=True) - run_nircmd('mutesysvolume', '0') - - # Open XMPlay - popen_program(cmd) - -def run_hitmanpro(): - """Run HitmanPro in the background.""" - extract_item('HitmanPro', silent=True) - cmd = [ - global_vars['Tools']['HitmanPro'], - '/quiet', '/noinstall', '/noupload', - r'/log={LogDir}\Tools\HitmanPro.txt'.format(**global_vars)] - popen_program(cmd) - -def run_process_killer(): - """Kill most running processes skipping those in the whitelist.txt.""" - # borrowed from TronScript (reddit.com/r/TronScript) - # credit to /u/cuddlychops06 - prev_dir = os.getcwd() - extract_item('ProcessKiller', silent=True) - os.chdir(r'{BinDir}\ProcessKiller'.format(**global_vars)) - run_program(['ProcessKiller.exe', '/silent'], check=False) - os.chdir(prev_dir) - -def run_rkill(): - """Run RKill and cleanup afterwards.""" - extract_item('RKill', silent=True) - cmd = [ - global_vars['Tools']['RKill'], - '-s', '-l', r'{LogDir}\Tools\RKill.log'.format(**global_vars), - '-new_console:n', '-new_console:s33V'] - run_program(cmd, check=False) - wait_for_process('RKill') - - # RKill cleanup - desktop_path = r'{USERPROFILE}\Desktop'.format(**global_vars['Env']) - if os.path.exists(desktop_path): - for item in os.scandir(desktop_path): - if re.search(r'^RKill', item.name, re.IGNORECASE): - dest = r'{LogDir}\Tools\{name}'.format( - name=dest, **global_vars) - dest = non_clobber_rename(dest) - shutil.move(item.path, dest) - -def show_alert_box(message, title='Wizard Kit Warning'): - """Show Windows alert box with message.""" - message_box = ctypes.windll.user32.MessageBoxW - message_box(None, message, title, 0x00001030) - -if __name__ == '__main__': - print("This file is not meant to be called directly.") diff --git a/.bin/Scripts/functions/disk.py b/.bin/Scripts/functions/disk.py index 75879ff8..31fe577d 100644 --- a/.bin/Scripts/functions/disk.py +++ b/.bin/Scripts/functions/disk.py @@ -1,395 +1,414 @@ # Wizard Kit: Functions - Disk from functions.common import * -from functions import partition_uids +from settings.partition_uids import * + # Regex REGEX_BAD_PARTITION = re.compile(r'(RAW|Unknown)', re.IGNORECASE) REGEX_DISK_GPT = re.compile( - r'Disk ID: {[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+}', - re.IGNORECASE) + r'Disk ID: {[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+-[A-Z0-9]+}', + re.IGNORECASE) REGEX_DISK_MBR = re.compile(r'Disk ID: [A-Z0-9]+', re.IGNORECASE) REGEX_DISK_RAW = re.compile(r'Disk ID: 00000000', re.IGNORECASE) + def assign_volume_letters(): - """Assign a volume letter to all available volumes.""" - remove_volume_letters() + """Assign a volume letter to all available volumes.""" + remove_volume_letters() - # Write script - script = [] - for vol in get_volumes(): - script.append('select volume {}'.format(vol['Number'])) - script.append('assign') + # Write script + script = [] + for vol in get_volumes(): + script.append('select volume {}'.format(vol['Number'])) + script.append('assign') + + # Run + run_diskpart(script) - # Run - run_diskpart(script) def get_boot_mode(): - """Check if the boot mode was UEFI or legacy.""" - boot_mode = 'Legacy' - try: - reg_key = winreg.OpenKey( - winreg.HKEY_LOCAL_MACHINE, r'System\CurrentControlSet\Control') - reg_value = winreg.QueryValueEx(reg_key, 'PEFirmwareType')[0] - if reg_value == 2: - boot_mode = 'UEFI' - except: - boot_mode = 'Unknown' + """Check if the boot mode was UEFI or legacy.""" + boot_mode = 'Legacy' + try: + reg_key = winreg.OpenKey( + winreg.HKEY_LOCAL_MACHINE, r'System\CurrentControlSet\Control') + reg_value = winreg.QueryValueEx(reg_key, 'PEFirmwareType')[0] + if reg_value == 2: + boot_mode = 'UEFI' + except: + boot_mode = 'Unknown' + + return boot_mode - return boot_mode def get_disk_details(disk): - """Get disk details using DiskPart.""" - details = {} - script = [ - 'select disk {}'.format(disk['Number']), - 'detail disk'] + """Get disk details using DiskPart.""" + details = {} + script = [ + 'select disk {}'.format(disk['Number']), + 'detail disk'] - # Run - try: - result = run_diskpart(script) - except subprocess.CalledProcessError: - pass - else: - output = result.stdout.decode().strip() - # Remove empty lines - tmp = [s.strip() for s in output.splitlines() if s.strip() != ''] - # Set disk name - details['Name'] = tmp[4] - # Split each line on ':' skipping those without ':' - tmp = [s.split(':') for s in tmp if ':' in s] - # Add key/value pairs to the details variable and return dict - details.update({key.strip(): value.strip() for (key, value) in tmp}) + # Run + try: + result = run_diskpart(script) + except subprocess.CalledProcessError: + pass + else: + output = result.stdout.decode().strip() + # Remove empty lines + tmp = [s.strip() for s in output.splitlines() if s.strip() != ''] + # Set disk name + details['Name'] = tmp[4] + # Split each line on ':' skipping those without ':' + tmp = [s.split(':') for s in tmp if ':' in s] + # Add key/value pairs to the details variable and return dict + details.update({key.strip(): value.strip() for (key, value) in tmp}) + + return details - return details def get_disks(): - """Get list of attached disks using DiskPart.""" - disks = [] + """Get list of attached disks using DiskPart.""" + disks = [] - try: - # Run script - result = run_diskpart(['list disk']) - except subprocess.CalledProcessError: - pass - else: - # Append disk numbers - output = result.stdout.decode().strip() - for tmp in re.findall(r'Disk (\d+)\s+\w+\s+(\d+\s+\w+)', output): - num = tmp[0] - size = human_readable_size(tmp[1]) - disks.append({'Number': num, 'Size': size}) + try: + # Run script + result = run_diskpart(['list disk']) + except subprocess.CalledProcessError: + pass + else: + # Append disk numbers + output = result.stdout.decode().strip() + for tmp in re.findall(r'Disk (\d+)\s+\w+\s+(\d+\s+\w+)', output): + num = tmp[0] + size = human_readable_size(tmp[1]) + disks.append({'Number': num, 'Size': size}) + + return disks - return disks def get_partition_details(disk, partition): - """Get partition details using DiskPart and fsutil.""" - details = {} - script = [ - 'select disk {}'.format(disk['Number']), - 'select partition {}'.format(partition['Number']), - 'detail partition'] + """Get partition details using DiskPart and fsutil.""" + details = {} + script = [ + 'select disk {}'.format(disk['Number']), + 'select partition {}'.format(partition['Number']), + 'detail partition'] - # Diskpart details + # Diskpart details + try: + # Run script + result = run_diskpart(script) + except subprocess.CalledProcessError: + pass + else: + # Get volume letter or RAW status + output = result.stdout.decode().strip() + tmp = re.search(r'Volume\s+\d+\s+(\w|RAW)\s+', output) + if tmp: + if tmp.group(1).upper() == 'RAW': + details['FileSystem'] = RAW + else: + details['Letter'] = tmp.group(1) + # Remove empty lines from output + tmp = [s.strip() for s in output.splitlines() if s.strip() != ''] + # Split each line on ':' skipping those without ':' + tmp = [s.split(':') for s in tmp if ':' in s] + # Add key/value pairs to the details variable and return dict + details.update({key.strip(): value.strip() for (key, value) in tmp}) + + # Get MBR type / GPT GUID for extra details on "Unknown" partitions + guid = PARTITION_UIDS.get(details.get('Type').upper(), {}) + if guid: + details.update({ + 'Description': guid.get('Description', '')[:29], + 'OS': guid.get('OS', 'Unknown')[:27]}) + + if 'Letter' in details: + # Disk usage try: - # Run script - result = run_diskpart(script) - except subprocess.CalledProcessError: - pass + tmp = psutil.disk_usage('{}:\\'.format(details['Letter'])) + except OSError as err: + details['FileSystem'] = 'Unknown' + details['Error'] = err.strerror else: - # Get volume letter or RAW status - output = result.stdout.decode().strip() - tmp = re.search(r'Volume\s+\d+\s+(\w|RAW)\s+', output) - if tmp: - if tmp.group(1).upper() == 'RAW': - details['FileSystem'] = RAW - else: - details['Letter'] = tmp.group(1) - # Remove empty lines from output - tmp = [s.strip() for s in output.splitlines() if s.strip() != ''] - # Split each line on ':' skipping those without ':' - tmp = [s.split(':') for s in tmp if ':' in s] - # Add key/value pairs to the details variable and return dict - details.update({key.strip(): value.strip() for (key, value) in tmp}) + details['Used Space'] = human_readable_size(tmp.used) - # Get MBR type / GPT GUID for extra details on "Unknown" partitions - guid = partition_uids.lookup_guid(details.get('Type')) - if guid: - details.update({ - 'Description': guid.get('Description', '')[:29], - 'OS': guid.get('OS', 'Unknown')[:27]}) + # fsutil details + cmd = [ + 'fsutil', + 'fsinfo', + 'volumeinfo', + '{}:'.format(details['Letter']) + ] + try: + result = run_program(cmd) + except subprocess.CalledProcessError: + pass + else: + output = result.stdout.decode().strip() + # Remove empty lines from output + tmp = [s.strip() for s in output.splitlines() if s.strip() != ''] + # Add "Feature" lines + details['File System Features'] = [s.strip() for s in tmp + if ':' not in s] + # Split each line on ':' skipping those without ':' + tmp = [s.split(':') for s in tmp if ':' in s] + # Add key/value pairs to the details variable and return dict + details.update({key.strip(): value.strip() for (key, value) in tmp}) - if 'Letter' in details: - # Disk usage - try: - tmp = psutil.disk_usage('{}:\\'.format(details['Letter'])) - except OSError as err: - details['FileSystem'] = 'Unknown' - details['Error'] = err.strerror - else: - details['Used Space'] = human_readable_size(tmp.used) + # Set Volume Name + details['Name'] = details.get('Volume Name', '') - # fsutil details - cmd = [ - 'fsutil', - 'fsinfo', - 'volumeinfo', - '{}:'.format(details['Letter']) - ] - try: - result = run_program(cmd) - except subprocess.CalledProcessError: - pass - else: - output = result.stdout.decode().strip() - # Remove empty lines from output - tmp = [s.strip() for s in output.splitlines() if s.strip() != ''] - # Add "Feature" lines - details['File System Features'] = [s.strip() for s in tmp - if ':' not in s] - # Split each line on ':' skipping those without ':' - tmp = [s.split(':') for s in tmp if ':' in s] - # Add key/value pairs to the details variable and return dict - details.update({key.strip(): value.strip() for (key, value) in tmp}) + # Set FileSystem Type + if details.get('FileSystem', '') not in ['RAW', 'Unknown']: + details['FileSystem'] = details.get('File System Name', 'Unknown') - # Set Volume Name - details['Name'] = details.get('Volume Name', '') + return details - # Set FileSystem Type - if details.get('FileSystem', '') not in ['RAW', 'Unknown']: - details['FileSystem'] = details.get('File System Name', 'Unknown') - - return details def get_partitions(disk): - """Get list of partition using DiskPart.""" - partitions = [] - script = [ - 'select disk {}'.format(disk['Number']), - 'list partition'] + """Get list of partition using DiskPart.""" + partitions = [] + script = [ + 'select disk {}'.format(disk['Number']), + 'list partition'] - try: - # Run script - result = run_diskpart(script) - except subprocess.CalledProcessError: - pass - else: - # Append partition numbers - output = result.stdout.decode().strip() - regex = r'Partition\s+(\d+)\s+\w+\s+(\d+\s+\w+)\s+' - for tmp in re.findall(regex, output, re.IGNORECASE): - num = tmp[0] - size = human_readable_size(tmp[1]) - partitions.append({'Number': num, 'Size': size}) + try: + # Run script + result = run_diskpart(script) + except subprocess.CalledProcessError: + pass + else: + # Append partition numbers + output = result.stdout.decode().strip() + regex = r'Partition\s+(\d+)\s+\w+\s+(\d+\s+\w+)\s+' + for tmp in re.findall(regex, output, re.IGNORECASE): + num = tmp[0] + size = human_readable_size(tmp[1]) + partitions.append({'Number': num, 'Size': size}) + + return partitions - return partitions def get_table_type(disk): - """Get disk partition table type using DiskPart.""" - part_type = 'Unknown' - script = [ - 'select disk {}'.format(disk['Number']), - 'uniqueid disk'] + """Get disk partition table type using DiskPart.""" + part_type = 'Unknown' + script = [ + 'select disk {}'.format(disk['Number']), + 'uniqueid disk'] - try: - result = run_diskpart(script) - except subprocess.CalledProcessError: - pass - else: - output = result.stdout.decode().strip() - if REGEX_DISK_GPT.search(output): - part_type = 'GPT' - elif REGEX_DISK_MBR.search(output): - part_type = 'MBR' - elif REGEX_DISK_RAW.search(output): - part_type = 'RAW' + try: + result = run_diskpart(script) + except subprocess.CalledProcessError: + pass + else: + output = result.stdout.decode().strip() + if REGEX_DISK_GPT.search(output): + part_type = 'GPT' + elif REGEX_DISK_MBR.search(output): + part_type = 'MBR' + elif REGEX_DISK_RAW.search(output): + part_type = 'RAW' + + return part_type - return part_type def get_volumes(): - """Get list of volumes using DiskPart.""" - vols = [] - try: - result = run_diskpart(['list volume']) - except subprocess.CalledProcessError: - pass - else: - # Append volume numbers - output = result.stdout.decode().strip() - for tmp in re.findall(r'Volume (\d+)\s+([A-Za-z]?)\s+', output): - vols.append({'Number': tmp[0], 'Letter': tmp[1]}) + """Get list of volumes using DiskPart.""" + vols = [] + try: + result = run_diskpart(['list volume']) + except subprocess.CalledProcessError: + pass + else: + # Append volume numbers + output = result.stdout.decode().strip() + for tmp in re.findall(r'Volume (\d+)\s+([A-Za-z]?)\s+', output): + vols.append({'Number': tmp[0], 'Letter': tmp[1]}) + + return vols - return vols def is_bad_partition(par): - """Check if the partition is accessible.""" - return 'Letter' not in par or REGEX_BAD_PARTITION.search(par['FileSystem']) + """Check if the partition is accessible.""" + return 'Letter' not in par or REGEX_BAD_PARTITION.search(par['FileSystem']) + def prep_disk_for_formatting(disk=None): - """Gather details about the disk and its partitions.""" - disk['Format Warnings'] = '\n' - width = len(str(len(disk['Partitions']))) + """Gather details about the disk and its partitions.""" + disk['Format Warnings'] = '\n' + width = len(str(len(disk['Partitions']))) - # Bail early - if disk is None: - raise Exception('Disk not provided.') + # Bail early + if disk is None: + raise Exception('Disk not provided.') - # Set boot method and partition table type - disk['Use GPT'] = True - if (get_boot_mode() == 'UEFI'): - if (not ask("Setup Windows to use UEFI booting?")): - disk['Use GPT'] = False + # Set boot method and partition table type + disk['Use GPT'] = True + if (get_boot_mode() == 'UEFI'): + if (not ask("Setup Windows to use UEFI booting?")): + disk['Use GPT'] = False + else: + if (ask("Setup Windows to use BIOS/Legacy booting?")): + disk['Use GPT'] = False + + # Set Display and Warning Strings + if len(disk['Partitions']) == 0: + disk['Format Warnings'] += 'No partitions found\n' + for partition in disk['Partitions']: + display = '{size} {fs}'.format( + num = partition['Number'], + width = width, + size = partition['Size'], + fs = partition['FileSystem']) + + if is_bad_partition(partition): + # Set display string using partition description & OS type + display += '\t\t{q}{name}{q}\t{desc} ({os})'.format( + display = display, + q = '"' if partition['Name'] != '' else '', + name = partition['Name'], + desc = partition['Description'], + os = partition['OS']) else: - if (ask("Setup Windows to use BIOS/Legacy booting?")): - disk['Use GPT'] = False + # List space used instead of partition description & OS type + display += ' (Used: {used})\t{q}{name}{q}'.format( + used = partition['Used Space'], + q = '"' if partition['Name'] != '' else '', + name = partition['Name']) + # For all partitions + partition['Display String'] = display - # Set Display and Warning Strings - if len(disk['Partitions']) == 0: - disk['Format Warnings'] += 'No partitions found\n' - for partition in disk['Partitions']: - display = '{size} {fs}'.format( - num = partition['Number'], - width = width, - size = partition['Size'], - fs = partition['FileSystem']) - - if is_bad_partition(partition): - # Set display string using partition description & OS type - display += '\t\t{q}{name}{q}\t{desc} ({os})'.format( - display = display, - q = '"' if partition['Name'] != '' else '', - name = partition['Name'], - desc = partition['Description'], - os = partition['OS']) - else: - # List space used instead of partition description & OS type - display += ' (Used: {used})\t{q}{name}{q}'.format( - used = partition['Used Space'], - q = '"' if partition['Name'] != '' else '', - name = partition['Name']) - # For all partitions - partition['Display String'] = display def reassign_volume_letter(letter, new_letter='I'): - """Assign a new letter to a volume using DiskPart.""" - if not letter: - # Ignore - return None - script = [ - 'select volume {}'.format(letter), - 'remove noerr', - 'assign letter={}'.format(new_letter)] - try: - run_diskpart(script) - except subprocess.CalledProcessError: - pass - else: - return new_letter + """Assign a new letter to a volume using DiskPart.""" + if not letter: + # Ignore + return None + script = [ + 'select volume {}'.format(letter), + 'remove noerr', + 'assign letter={}'.format(new_letter)] + try: + run_diskpart(script) + except subprocess.CalledProcessError: + pass + else: + return new_letter + def remove_volume_letters(keep=None): - """Remove all assigned volume letters using DiskPart.""" - if not keep: - keep = '' + """Remove all assigned volume letters using DiskPart.""" + if not keep: + keep = '' - script = [] - for vol in get_volumes(): - if vol['Letter'].upper() != keep.upper(): - script.append('select volume {}'.format(vol['Number'])) - script.append('remove noerr') + script = [] + for vol in get_volumes(): + if vol['Letter'].upper() != keep.upper(): + script.append('select volume {}'.format(vol['Number'])) + script.append('remove noerr') + + # Run script + try: + run_diskpart(script) + except subprocess.CalledProcessError: + pass - # Run script - try: - run_diskpart(script) - except subprocess.CalledProcessError: - pass def run_diskpart(script): - """Run DiskPart script.""" - tempfile = r'{}\diskpart.script'.format(global_vars['Env']['TMP']) + """Run DiskPart script.""" + tempfile = r'{}\diskpart.script'.format(global_vars['Env']['TMP']) - # Write script - with open(tempfile, 'w') as f: - for line in script: - f.write('{}\n'.format(line)) + # Write script + with open(tempfile, 'w') as f: + for line in script: + f.write('{}\n'.format(line)) + + # Run script + cmd = [ + r'{}\Windows\System32\diskpart.exe'.format( + global_vars['Env']['SYSTEMDRIVE']), + '/s', tempfile] + result = run_program(cmd) + sleep(2) + return result - # Run script - cmd = [ - r'{}\Windows\System32\diskpart.exe'.format( - global_vars['Env']['SYSTEMDRIVE']), - '/s', tempfile] - result = run_program(cmd) - sleep(2) - return result def scan_disks(): - """Get details about the attached disks""" - disks = get_disks() + """Get details about the attached disks""" + disks = get_disks() - # Get disk details - for disk in disks: - # Get partition style - disk['Table'] = get_table_type(disk) + # Get disk details + for disk in disks: + # Get partition style + disk['Table'] = get_table_type(disk) - # Get disk name/model and physical details - disk.update(get_disk_details(disk)) + # Get disk name/model and physical details + disk.update(get_disk_details(disk)) - # Get partition info for disk - disk['Partitions'] = get_partitions(disk) + # Get partition info for disk + disk['Partitions'] = get_partitions(disk) - for partition in disk['Partitions']: - # Get partition details - partition.update(get_partition_details(disk, partition)) + for partition in disk['Partitions']: + # Get partition details + partition.update(get_partition_details(disk, partition)) + + # Done + return disks - # Done - return disks def select_disk(title='Which disk?', disks=[]): - """Select a disk from the attached disks""" - # Build menu - disk_options = [] - for disk in disks: - display_name = '{}\t[{}] ({}) {}'.format( - disk.get('Size', ''), - disk.get('Table', ''), - disk.get('Type', ''), - disk.get('Name', 'Unknown'), - ) - pwidth=len(str(len(disk['Partitions']))) - for partition in disk['Partitions']: - # Main text - p_name = 'Partition {num:>{width}}: {size} ({fs})'.format( - num = partition['Number'], - width = pwidth, - size = partition['Size'], - fs = partition['FileSystem']) - if partition['Name']: - p_name += '\t"{}"'.format(partition['Name']) + """Select a disk from the attached disks""" + # Build menu + disk_options = [] + for disk in disks: + display_name = '{}\t[{}] ({}) {}'.format( + disk.get('Size', ''), + disk.get('Table', ''), + disk.get('Type', ''), + disk.get('Name', 'Unknown'), + ) + pwidth=len(str(len(disk['Partitions']))) + for partition in disk['Partitions']: + # Main text + p_name = 'Partition {num:>{width}}: {size} ({fs})'.format( + num = partition['Number'], + width = pwidth, + size = partition['Size'], + fs = partition['FileSystem']) + if partition['Name']: + p_name += '\t"{}"'.format(partition['Name']) - # Show unsupported partition(s) - if is_bad_partition(partition): - p_name = '{YELLOW}{p_name}{CLEAR}'.format( - p_name=p_name, **COLORS) + # Show unsupported partition(s) + if is_bad_partition(partition): + p_name = '{YELLOW}{p_name}{CLEAR}'.format( + p_name=p_name, **COLORS) - display_name += '\n\t\t\t{}'.format(p_name) - if not disk['Partitions']: - display_name += '\n\t\t\t{}No partitions found.{}'.format( - COLORS['YELLOW'], COLORS['CLEAR']) + display_name += '\n\t\t\t{}'.format(p_name) + if not disk['Partitions']: + display_name += '\n\t\t\t{}No partitions found.{}'.format( + COLORS['YELLOW'], COLORS['CLEAR']) - disk_options.append({'Name': display_name, 'Disk': disk}) - actions = [ - {'Name': 'Main Menu', 'Letter': 'M'}, - ] + disk_options.append({'Name': display_name, 'Disk': disk}) + actions = [ + {'Name': 'Main Menu', 'Letter': 'M'}, + ] - # Menu loop - selection = menu_select( - title = title, - main_entries = disk_options, - action_entries = actions) + # Menu loop + selection = menu_select( + title = title, + main_entries = disk_options, + action_entries = actions) + + if (selection.isnumeric()): + return disk_options[int(selection)-1]['Disk'] + elif (selection == 'M'): + raise GenericAbort - if (selection.isnumeric()): - return disk_options[int(selection)-1]['Disk'] - elif (selection == 'M'): - raise GenericAbort if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/hw_diags.py b/.bin/Scripts/functions/hw_diags.py index 39789149..18446489 100644 --- a/.bin/Scripts/functions/hw_diags.py +++ b/.bin/Scripts/functions/hw_diags.py @@ -1,1670 +1,1789 @@ # Wizard Kit: Functions - HW Diagnostics -import base64 -import Gnuplot import json -import math -import mysql.connector as mariadb -import requests +import re import time -from functions.data import * -from numpy import * +from collections import OrderedDict +from functions.osticket import * +from functions.sensors import * +from functions.threading import * +from functions.tmux import * -# Database connection -ost_db = { - 'Connection': None, - 'Cursor': None, - 'Errors': False, - 'Tunnel': None, - } # STATIC VARIABLES ATTRIBUTES = { - 'NVMe': { - 'critical_warning': {'Error': 1}, - 'media_errors': {'Error': 1}, - 'power_on_hours': {'Warning': 12000, 'Error': 18000, 'Ignore': True}, - 'unsafe_shutdowns': {'Warning': 1}, - }, - 'SMART': { - 5: {'Hex': '05', 'Error': 1}, - 9: {'Hex': '09', 'Warning': 12000, 'Error': 18000, 'Ignore': True}, - 10: {'Hex': '0A', 'Error': 1}, - 184: {'Hex': 'B8', 'Error': 1}, - 187: {'Hex': 'BB', 'Error': 1}, - 188: {'Hex': 'BC', 'Error': 1}, - 196: {'Hex': 'C4', 'Error': 1}, - 197: {'Hex': 'C5', 'Error': 1}, - 198: {'Hex': 'C6', 'Error': 1}, - 199: {'Hex': 'C7', 'Error': 1, 'Ignore': True}, - 201: {'Hex': 'C9', 'Error': 1}, - }, - } + 'NVMe': { + 'critical_warning': {'Error': 1, 'Critical': True}, + 'media_errors': {'Error': 1, 'Critical': True}, + 'power_on_hours': {'Warning': 12000, 'Error': 26298, 'Ignore': True}, + 'unsafe_shutdowns': {'Warning': 1}, + }, + 'SMART': { + 5: {'Hex': '05', 'Error': 1, 'Critical': True}, + 9: {'Hex': '09', 'Warning': 12000, 'Error': 26298, 'Ignore': True}, + 10: {'Hex': '0A', 'Error': 1}, + 184: {'Hex': 'B8', 'Error': 1}, + 187: {'Hex': 'BB', 'Error': 1}, + 188: {'Hex': 'BC', 'Error': 1}, + 196: {'Hex': 'C4', 'Error': 1}, + 197: {'Hex': 'C5', 'Error': 1, 'Critical': True}, + 198: {'Hex': 'C6', 'Error': 1, 'Critical': True}, + 199: {'Hex': 'C7', 'Error': 1, 'Ignore': True}, + 201: {'Hex': 'C9', 'Error': 1}, + }, + } +HW_OVERRIDES_FORCED = HW_OVERRIDES_FORCED and not HW_OVERRIDES_LIMITED IO_VARS = { - 'Block Size': 512*1024, - 'Chunk Size': 32*1024**2, - 'Minimum Dev Size': 8*1024**3, - 'Minimum Test Size': 10*1024**3, - 'Alt Test Size Factor': 0.01, - 'Progress Refresh Rate': 5, - 'Scale 8': [2**(0.56*(x+1))+(16*(x+1)) for x in range(8)], - 'Scale 16': [2**(0.56*(x+1))+(16*(x+1)) for x in range(16)], - 'Scale 32': [2**(0.56*(x+1)/2)+(16*(x+1)/2) for x in range(32)], - 'Threshold Graph Fail': 65*1024**2, - 'Threshold Graph Warn': 135*1024**2, - 'Threshold Graph Great': 750*1024**2, - 'Threshold HDD Min': 50*1024**2, - 'Threshold HDD High Avg': 75*1024**2, - 'Threshold HDD Low Avg': 65*1024**2, - 'Threshold SSD Min': 90*1024**2, - 'Threshold SSD High Avg': 135*1024**2, - 'Threshold SSD Low Avg': 100*1024**2, - 'Graph Horizontal': ('▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'), - 'Graph Horizontal Width': 40, - 'Graph Vertical': ( - '▏', '▎', '▍', '▌', - '▋', '▊', '▉', '█', - '█▏', '█▎', '█▍', '█▌', - '█▋', '█▊', '█▉', '██', - '██▏', '██▎', '██▍', '██▌', - '██▋', '██▊', '██▉', '███', - '███▏', '███▎', '███▍', '███▌', - '███▋', '███▊', '███▉', '████'), - } -OST_STAFF_ID = '23' -OST_STAFF_NAME = 'Wizard Kit' -OST_SQL_SET_HOLD = "UPDATE `{db_name}`.`ost_ticket` SET `hold` = '{hold_type}' WHERE `ost_ticket`.`ticket_id` = {ticket_id};" -OST_SQL_SET_FLAG = "UPDATE `{db_name}`.`ost_ticket` SET `{flag}` = '{value}' WHERE `ost_ticket`.`ticket_id` = {ticket_id};" -OST_SQL_POST_REPLY = ("INSERT INTO `{db_name}`.`ost_ticket_response` (ticket_id, staff_id, staff_name, response, created) " - "VALUES ('{ticket_id}', '{staff_id}', '{staff_name}', '{response}', '{created}');") -OST_DRIVE_FLAG = 'zHDTune' -OST_DRIVE_PASSED = 1 -OST_DRIVE_FAILED = 2 -OST_NEEDS_ATTENTION = 4 -TESTS = { - 'Prime95': { - 'Enabled': False, - 'Status': 'Pending', - }, - 'NVMe/SMART': { - 'Enabled': False, - 'Quick': False, - 'Short Test': {}, - 'Status': {}, - }, - 'badblocks': { - 'Enabled': False, - 'Results': {}, - 'Status': {}, - }, - 'iobenchmark': { - 'Data': {}, - 'Enabled': False, - 'Results': {}, - 'Status': {}, - }, - } + 'Block Size': 512*1024, + 'Chunk Size': 32*1024**2, + 'Minimum Test Size': 10*1024**3, + 'Alt Test Size Factor': 0.01, + 'Progress Refresh Rate': 5, + 'Scale 8': [2**(0.56*(x+1))+(16*(x+1)) for x in range(8)], + 'Scale 16': [2**(0.56*(x+1))+(16*(x+1)) for x in range(16)], + 'Scale 32': [2**(0.56*(x+1)/2)+(16*(x+1)/2) for x in range(32)], + 'Threshold Graph Fail': 65*1024**2, + 'Threshold Graph Warn': 135*1024**2, + 'Threshold Graph Great': 750*1024**2, + 'Threshold HDD Min': 50*1024**2, + 'Threshold HDD High Avg': 75*1024**2, + 'Threshold HDD Low Avg': 65*1024**2, + 'Threshold SSD Min': 90*1024**2, + 'Threshold SSD High Avg': 135*1024**2, + 'Threshold SSD Low Avg': 100*1024**2, + 'Graph Horizontal': ('▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'), + 'Graph Horizontal Width': 40, + 'Graph Vertical': ( + '▏', '▎', '▍', '▌', + '▋', '▊', '▉', '█', + '█▏', '█▎', '█▍', '█▌', + '█▋', '█▊', '█▉', '██', + '██▏', '██▎', '██▍', '██▌', + '██▋', '██▊', '██▉', '███', + '███▏', '███▎', '███▍', '███▌', + '███▋', '███▊', '███▉', '████'), + } +KEY_NVME = 'nvme_smart_health_information_log' +KEY_SMART = 'ata_smart_attributes' +QUICK_LABEL = '{YELLOW}(Quick){CLEAR}'.format(**COLORS) +SIDE_PANE_WIDTH = 20 +STATUSES = { + 'RED': ['Denied', 'ERROR', 'FAIL', 'TimedOut'], + 'YELLOW': ['Aborted', 'N/A', 'OVERRIDE', 'Unknown', 'Working'], + 'GREEN': ['PASS'], +} +TESTS_CPU = ['Prime95'] +TESTS_DISK = [ + 'I/O Benchmark', + 'NVMe / SMART', + 'badblocks', + ] +TOP_PANE_TEXT = '{GREEN}Hardware Diagnostics{CLEAR}'.format(**COLORS) +TMUX_LAYOUT = OrderedDict({ + 'Top': {'y': 2, 'Check': True}, + 'Started': {'x': SIDE_PANE_WIDTH, 'Check': True}, + 'Progress': {'x': SIDE_PANE_WIDTH, 'Check': True}, + # Testing panes + 'Prime95': {'y': 11, 'Check': False}, + 'Temps': {'y': 1000, 'Check': False}, + 'SMART': {'y': 3, 'Check': True}, + 'badblocks': {'y': 5, 'Check': True}, + 'I/O Benchmark': {'y': 1000, 'Check': False}, +}) -def connect_to_db(): - """Connect to osTicket database via SSH tunnel.""" - cmd = [ - 'ssh', '-N', '-p', SSH_PORT, '-L3306:127.0.0.1:3306', - '{user}@{host}'.format(user=SSH_USER, host=DB_HOST), - ] - # Establish SSH tunnel unless one already exists - if not ost_db['Tunnel'] or ost_db['Tunnel'].poll() is not None: - ost_db['Tunnel'] = popen_program(cmd) +# Regex +REGEX_ERROR_STATUS = re.compile('|'.join(STATUSES['RED'])) - # Establish SQL connection (try a few times in case SSH is slow) - for x in range(5): - sleep(2) - try: - ost_db['Connection'] = mariadb.connect( - user=DB_USER, password=DB_PASS, database=DB_NAME) - ost_db['Cursor'] = ost_db['Connection'].cursor() - except: - # Just try again - pass - else: - break -def disconnect_from_db(reset_errors=False): - """Disconnect from SQL DB.""" - for c in ['Cursor', 'Connection']: - try: - ost_db[c].close() - except: - # Ignore - pass - ost_db[c] = None - if reset_errors: - ost_db['Errors'] = False +# Error Classes +class DeviceTooSmallError(Exception): + pass -def export_png_graph(name, dev): - """Exports PNG graph using gnuplot, returns file path as str.""" - max_rate = max(TESTS['iobenchmark']['Data'][name]['Read Rates']) - max_rate /= (1024**2) - max_rate = max(800, max_rate) - out_path = '{}/iobenchmark-{}.png'.format(global_vars['LogDir'], name) - plot_data = '{}/iobenchmark-{}-raw.log'.format(global_vars['LogDir'], name) - # Adjust Y-axis range to either 1000 or roughly max rate + 200 - ## Round up to the nearest 100 and then add 200 - y_range = (math.ceil(max_rate/100)*100) + 200 +# Classes +class CpuObj(): + """Object for tracking CPU specific data.""" + def __init__(self): + self.lscpu = {} + self.tests = OrderedDict() + self.get_details() + self.name = self.lscpu.get('Model name', 'Unknown CPU') + self.description = self.name - # Run gnuplot commands - g = Gnuplot.Gnuplot() - g('reset') - g('set output "{}"'.format(out_path)) - g('set terminal png large size 660,300 truecolor font "Noto Sans,11"') - g('set title "I/O Benchmark"') - g('set yrange [0:{}]'.format(y_range)) - g('set style data lines') - g('plot "{data}" title "{size} ({tran}) {model} {serial}"'.format( - data=plot_data, - size=dev['lsblk'].get('size', '???b'), - tran=dev['lsblk'].get('tran', '???'), - model=dev['lsblk'].get('model', 'Unknown Model'), - serial=dev['lsblk'].get('serial', 'Unknown Serial'), - )) - - # Cleanup - g.close() - del(g) - - return out_path - -def generate_horizontal_graph(rates, oneline=False): - """Generate two-line horizontal graph from rates, returns str.""" - line_1 = '' - line_2 = '' - line_3 = '' - line_4 = '' - for r in rates: - step = get_graph_step(r, scale=32) - if oneline: - step = get_graph_step(r, scale=8) - - # Set color - r_color = COLORS['CLEAR'] - if r < IO_VARS['Threshold Graph Fail']: - r_color = COLORS['RED'] - elif r < IO_VARS['Threshold Graph Warn']: - r_color = COLORS['YELLOW'] - elif r > IO_VARS['Threshold Graph Great']: - r_color = COLORS['GREEN'] - - # Build graph - full_block = '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][-1]) - if step >= 24: - line_1 += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step-24]) - line_2 += full_block - line_3 += full_block - line_4 += full_block - elif step >= 16: - line_1 += ' ' - line_2 += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step-16]) - line_3 += full_block - line_4 += full_block - elif step >= 8: - line_1 += ' ' - line_2 += ' ' - line_3 += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step-8]) - line_4 += full_block - else: - line_1 += ' ' - line_2 += ' ' - line_3 += ' ' - line_4 += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step]) - line_1 += COLORS['CLEAR'] - line_2 += COLORS['CLEAR'] - line_3 += COLORS['CLEAR'] - line_4 += COLORS['CLEAR'] - if oneline: - return line_4 - else: - return '\n'.join([line_1, line_2, line_3, line_4]) - -def get_graph_step(rate, scale=16): - """Get graph step based on rate and scale, returns int.""" - m_rate = rate / (1024**2) - step = 0 - scale_name = 'Scale {}'.format(scale) - for x in range(scale-1, -1, -1): - # Iterate over scale backwards - if m_rate >= IO_VARS[scale_name][x]: - step = x - break - return step - -def get_osticket_number(): - """Get ticket number and confirm with name from osTicket DB.""" - ticket_number = None - if not ost_db['Cursor']: - # No DB access, return None to disable integration - return None - while ticket_number is None: - print_standard(' ') - _input = input('Enter ticket number (or leave blank to disable): ') - if re.match(r'^\s*$', _input): - if ask('Disable osTicket integration for this run?'): - return None - else: - continue - if not re.match(r'^([0-9]+)$', _input): - continue - _name = osticket_get_ticket_name(_input) - if _name: - print_standard('You have selected ticket #{} {}'.format( - _input, _name)) - if ask('Is this correct?'): - ticket_number = _input - return ticket_number - -def get_read_rate(s): - """Get read rate in bytes/s from dd progress output.""" - real_rate = None - if re.search(r'[KMGT]B/s', s): - human_rate = re.sub(r'^.*\s+(\d+\.?\d*)\s+(.B)/s\s*$', r'\1 \2', s) - real_rate = convert_to_bytes(human_rate) - return real_rate - -def get_smart_details(dev): - """Get SMART data for dev if possible, returns dict.""" - cmd = 'sudo smartctl --all --json {}{}'.format( - '' if '/dev/' in dev else '/dev/', - dev).split() - result = run_program(cmd, check=False) + def get_details(self): + """Get CPU details from lscpu.""" + cmd = ['lscpu', '--json'] try: - return json.loads(result.stdout.decode()) + result = run_program(cmd, check=False) + json_data = json.loads(result.stdout.decode()) except Exception: - # Let other sections deal with the missing data - return {} - -def get_smart_value(smart_data, smart_id): - """Get SMART value from table, returns int or None.""" - value = None - table = smart_data.get('ata_smart_attributes', {}).get('table', []) - for row in table: - if str(row.get('id', '?')) == str(smart_id): - value = row.get('raw', {}).get('value', None) - return value - -def get_status_color(s): - """Get color based on status, returns str.""" - color = COLORS['CLEAR'] - if s in ['Denied', 'ERROR', 'NS', 'OVERRIDE']: - color = COLORS['RED'] - elif s in ['Aborted', 'Unknown', 'Working', 'Skipped']: - color = COLORS['YELLOW'] - elif s in ['CS']: - color = COLORS['GREEN'] - return color - -def menu_diags(*args): - """Main HW-Diagnostic menu.""" - diag_modes = [ - {'Name': 'All tests', - 'Tests': ['Prime95', 'NVMe/SMART', 'badblocks', 'iobenchmark']}, - {'Name': 'Prime95', - 'Tests': ['Prime95']}, - {'Name': 'All drive tests', - 'Tests': ['NVMe/SMART', 'badblocks', 'iobenchmark']}, - {'Name': 'NVMe/SMART', - 'Tests': ['NVMe/SMART']}, - {'Name': 'badblocks', - 'Tests': ['badblocks']}, - {'Name': 'I/O Benchmark', - 'Tests': ['iobenchmark']}, - {'Name': 'Quick drive test', - 'Tests': ['Quick', 'NVMe/SMART']}, - ] - actions = [ - {'Letter': 'A', 'Name': 'Audio test'}, - {'Letter': 'K', 'Name': 'Keyboard test'}, - {'Letter': 'N', 'Name': 'Network test'}, - {'Letter': 'M', 'Name': 'Screen Saver - Matrix', 'CRLF': True}, - {'Letter': 'P', 'Name': 'Screen Saver - Pipes'}, - {'Letter': 'Q', 'Name': 'Quit', 'CRLF': True}, - ] - - # CLI-mode actions - if 'DISPLAY' not in global_vars['Env']: - actions.extend([ - {'Letter': 'R', 'Name': 'Reboot', 'CRLF': True}, - {'Letter': 'S', 'Name': 'Shutdown'}, - ]) - - # Quick disk check - if 'quick' in args: - run_tests(['Quick', 'NVMe/SMART']) - exit_script() - - # Show menu - while True: - selection = menu_select( - title = 'Hardware Diagnostics: Menu', - main_entries = diag_modes, - action_entries = actions, - spacer = '──────────────────────────') - if selection.isnumeric(): - ticket_number = None - if diag_modes[int(selection)-1]['Name'] != 'Quick drive test': - clear_screen() - print_standard(' ') - result = try_and_print( - message='Connecting to osTicket database...', - function=connect_to_db, - width=40) - if not result['CS']: - print_warning('osTicket integration disabled for this run.') - pause() - ticket_number = get_osticket_number() - disconnect_from_db() - # Save log for non-quick tests - global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z") - global_vars['LogDir'] = '{}/Logs/{}_{}'.format( - global_vars['Env']['HOME'], - ticket_number, - global_vars['Date-Time']) - os.makedirs(global_vars['LogDir'], exist_ok=True) - global_vars['LogFile'] = '{}/Hardware Diagnostics.log'.format( - global_vars['LogDir']) - run_tests(diag_modes[int(selection)-1]['Tests'], ticket_number) - elif selection == 'A': - run_program(['hw-diags-audio'], check=False, pipe=False) - pause('Press Enter to return to main menu... ') - elif selection == 'K': - run_program(['xev', '-event', 'keyboard'], check=False, pipe=False) - elif selection == 'N': - run_program(['hw-diags-network'], check=False, pipe=False) - pause('Press Enter to return to main menu... ') - elif selection == 'M': - run_program(['cmatrix', '-abs'], check=False, pipe=False) - elif selection == 'P': - run_program( - 'pipes -t 0 -t 1 -t 2 -t 3 -p 5 -R -r 4000'.split(), - check=False, pipe=False) - elif selection == 'R': - run_program(['systemctl', 'reboot']) - elif selection == 'S': - run_program(['systemctl', 'poweroff']) - elif selection == 'Q': - break - - # Done - disconnect_from_db(reset_errors=True) - -def osticket_get_ticket_name(ticket_id): - """Lookup ticket and return name as str.""" - ticket_name = 'Unknown' - if not ticket_id: - raise GenericError - if not ost_db['Cursor']: - # Skip section - return - - # Set command - sql_cmd = "SELECT name FROM `ost_ticket` WHERE `ticket_id` = {}".format( - ticket_id) - - # Run command - try: - ost_db['Cursor'].execute(sql_cmd) - for name in ost_db['Cursor']: - ticket_name = name[0] - return ticket_name - except: - ost_db['Errors'] = True - -def osticket_needs_attention(ticket_id): - """[DISABLED] Marks the ticket as "NEEDS ATTENTION" in osTicket.""" - return # This function has been DISABLED due to a repurposing of that flag - if not ticket_id: - raise GenericError - - # Connect to DB - connect_to_db() - if not ost_db['Cursor']: - # Skip section - return - - # Set command - sql_cmd = OST_SQL_SET_HOLD.format( - db_name=DB_NAME, - hold_type=OST_NEEDS_ATTENTION, - ticket_id=ticket_id) - - # Run command - try: - ost_db['Cursor'].execute(sql_cmd) - except: - ost_db['Errors'] = True - disconnect_from_db() - -def osticket_post_reply(ticket_id, response): - """Post a reply to a ticket in osTicket.""" - if not ticket_id: - raise GenericError - - # Connect to DB - connect_to_db() - if not ost_db['Cursor']: - # Skip section - return - - # Set command - sql_cmd = OST_SQL_POST_REPLY.format( - db_name=DB_NAME, - ticket_id=ticket_id, - staff_id=OST_STAFF_ID, - staff_name=OST_STAFF_NAME, - response=response, - created=time.strftime("%Y-%m-%d %H:%M:%S")) - - # Run command - try: - ost_db['Cursor'].execute(sql_cmd) - except: - ost_db['Errors'] = True - disconnect_from_db() - -def osticket_set_drive_result(ticket_id, passed): - """Marks the pass/fail box for the drive(s) in osTicket.""" - if not ticket_id: - raise GenericError - - # Connect to DB - connect_to_db() - if not ost_db['Cursor']: - # Skip section - return - - # Set command - sql_cmd = OST_SQL_SET_FLAG.format( - db_name=DB_NAME, - flag=OST_DRIVE_FLAG, - value=OST_DRIVE_PASSED if passed else OST_DRIVE_FAILED, - ticket_id=ticket_id) - - # Run command - try: - ost_db['Cursor'].execute(sql_cmd) - except: - ost_db['Errors'] = True - disconnect_from_db() - -def pad_with_dots(s, left_pad=True): - """Replace ' ' padding with '..' for osTicket posts.""" - s = str(s).replace(' ', '..') - if '.' in s: - if left_pad: - s = '.' + s - else: - s = s + '.' - return s - -def post_drive_results(ticket_number): - """Post drive test results to osTicket.""" - tested = False - - # Check if test(s) were run - for t in ['NVMe/SMART', 'badblocks', 'iobenchmark']: - tested |= TESTS[t]['Enabled'] - if not tested or TESTS['NVMe/SMART']['Quick']: - # No tests were run so no post necessary - return - - # Build reports for all tested devices - for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()): - dev_failed = False - dev_passed = True - dev_unknown = False - report = [] - - # Check all test results for dev - for t in ['NVMe/SMART', 'badblocks', 'iobenchmark']: - if not TESTS[t]['Enabled']: - continue - status = TESTS[t]['Status'].get(name, 'Unknown') - dev_failed |= status == 'NS' - dev_passed &= status == 'CS' - dev_unknown |= status in ('Working', 'Unknown') - - # Start drive report - if dev_failed: - report.append('Drive hardware diagnostics tests: FAILED') - elif dev_unknown: - report.append('Drive hardware diagnostics tests: UNKNOWN') - elif dev_passed: - report.append('Drive hardware diagnostics tests: Passed') - else: - report.append('Drive hardware diagnostics tests: INCOMPLETE') - report.append('') - - # Drive description - report.append('{size} ({tran}) {model} {serial}'.format( - size=dev['lsblk'].get('size', '???b'), - tran=dev['lsblk'].get('tran', '???'), - model=dev['lsblk'].get('model', 'Unknown Model'), - serial=dev['lsblk'].get('serial', 'Unknown Serial'), - )) - report.append('') - - # Warnings (if any) - if dev.get('NVMe Disk', False): - if dev['Quick Health OK']: - report.append('WARNING: NVMe support is still experimental') - else: - report.append('ERROR: NVMe disk is reporting critical warnings') - report.append('') - elif not dev['SMART Support']: - report.append('ERROR: Unable to retrieve SMART data') - report.append('') - elif not dev['SMART Pass']: - report.append('ERROR: SMART overall-health assessment result: FAILED') - report.append('') - - # NVMe/SMART Attributes - if dev.get('NVMe Disk', False): - report.append('NVMe Attributes ({}):'.format( - TESTS['NVMe/SMART']['Status'][name])) - for attrib in sorted(ATTRIBUTES['NVMe'].keys()): - if attrib in dev['nvme-cli']: - report.append('{attrib:30}{value}'.format( - attrib=attrib, - value=dev['nvme-cli'][attrib], - )) - report[-1] = report[-1].strip().replace(' ', '.') - report[-1] = report[-1].replace('_', ' ') - elif dev['smartctl'].get('ata_smart_attributes', None): - report.append('SMART Attributes ({}):'.format( - TESTS['NVMe/SMART']['Status'][name])) - s_table = dev['smartctl'].get('ata_smart_attributes', {}).get( - 'table', {}) - s_table = {a.get('id', 'Unknown'): a for a in s_table} - for attrib in sorted(ATTRIBUTES['SMART'].keys()): - if attrib in s_table: - # Pad attributewith dots for osTicket - hex_str = str(hex(int(attrib))).upper()[2:] - hex_str = pad_with_dots('{:>2}'.format(hex_str)) - dec_str = pad_with_dots('{:>3}'.format(attrib)) - val_str = '{:<20}'.format(s_table[attrib]['raw']['string']) - val_str = pad_with_dots(val_str, left_pad=False) - report.append('{:>2}/{:>3}: {} ({})'.format( - hex_str, - dec_str, - val_str, - s_table[attrib]['name'], - )) - report[-1] = report[-1].replace('_', ' ') - report.append('') - - # SMART Short test result - if TESTS['NVMe/SMART']['Short Test'].get(name, False): - report.append('SMART short test result: {}'.format( - TESTS['NVMe/SMART']['Short Test'][name])) - report.append('') - - # badblocks - bb_status = TESTS['badblocks']['Status'].get(name, None) - if TESTS['badblocks']['Enabled'] and bb_status not in ['Denied', 'Skipped', 'Aborted']: - report.append('badblocks ({}):'.format( - TESTS['badblocks']['Status'][name])) - bb_result = TESTS['badblocks']['Results'].get( - name, - 'ERROR: Failed to read log.') - for line in bb_result.splitlines(): - line = line.strip() - if not line: - continue - if re.search('Pass completed', line, re.IGNORECASE): - line = re.sub( - r'Pass completed,?\s+', - r'', - line, - re.IGNORECASE) - report.append(line) - report.append('') - - # I/O Benchmark - io_status = TESTS['iobenchmark']['Status'].get(name, None) - if TESTS['iobenchmark']['Enabled'] and io_status not in ['Denied', 'ERROR', 'Skipped', 'Aborted']: - one_line_graph = generate_horizontal_graph( - rates=TESTS['iobenchmark']['Data'][name]['Merged Rates'], - oneline=True) - for c in COLORS.values(): - one_line_graph = one_line_graph.replace(c, '') - report.append('I/O Benchmark ({}):'.format( - TESTS['iobenchmark']['Status'][name])) - report.append(one_line_graph) - report.append('{}'.format( - TESTS['iobenchmark']['Data'][name]['Avg/Min/Max'])) - - # Export PNG - try: - png_path = export_png_graph(name, dev) - except: - png_path = None - - # imgur - try: - url = upload_to_imgur(png_path) - report.append('Imgur: {}'.format(url)) - except: - # Oh well - pass - - # Nextcloud - try: - url = upload_to_nextcloud(png_path, ticket_number, name) - report.append('Nextcloud: {}'.format(url)) - except: - # Oh well - pass - - # Used space - report.append('') - report.append('Volumes:') - core_storage = dev_passed and not dev_failed and not dev_unknown - volume_report = mount_volumes( - all_devices=False, - device_path='/dev/{}'.format(name), - core_storage=core_storage) - for vol_path, vol_data in sorted(volume_report.items()): - vol_report = [ - vol_path, - '{q}{label}{q}'.format( - label=vol_data.get('label', ''), - q='"' if vol_data.get('label', '') else ''), - '{}'.format( - vol_data.get('size', 'UNKNOWN').upper()), - '{}'.format( - vol_data.get('size_used', 'UNKNOWN').upper()), - '{}'.format( - vol_data.get('size_avail', 'UNKNOWN').upper()), - ] - if vol_report[2][-1:] != 'N': - vol_report[2] = '{} {}B'.format( - vol_report[2][:-1], - vol_report[2][-1:]) - vol_report = [v.strip().replace(' ', '_') for v in vol_report] - for i in range(5): - pad = 8 - if i < 2: - pad += 4 * (2 - i) - vol_report[i] = pad_with_dots( - left_pad=False, - s='{s:<{p}}'.format( - s=vol_report[i], - p=pad)) - vol_report[-1] = re.sub(r'\.*$', '', vol_report[-1]) - vol_report = [v.replace('_', ' ') for v in vol_report] - line = '{}..{}..Total..{}..(Used..{}..Free..{})'.format( - *vol_report) - report.append(line) - - # Post reply for drive - osticket_post_reply( - ticket_id=ticket_number, - response='\n'.join(report)) - - # Mark ticket HDD/SSD pass/fail checkbox (as needed) - if dev_failed: - osticket_set_drive_result( - ticket_id=ticket_number, - passed=False) - elif dev_unknown: - pass - elif dev_passed: - osticket_set_drive_result( - ticket_id=ticket_number, - passed=True) - - # Mark ticket as NEEDS ATTENTION - osticket_needs_attention(ticket_id=ticket_number) - -def run_badblocks(ticket_number): - """Run a read-only test for all detected disks.""" - aborted = False - clear_screen() - print_log('\nStart badblocks test(s)\n') - progress_file = '{}/badblocks_progress.out'.format(global_vars['LogDir']) - update_progress() - - # Set Window layout and start test - run_program('tmux split-window -dhl 15 watch -c -n1 -t cat {}'.format( - TESTS['Progress Out']).split()) - - # Show disk details - for name, dev in sorted(TESTS['badblocks']['Devices'].items()): - show_disk_details(dev) - print_standard(' ') - update_progress() - - # Run - print_standard('Running badblock test(s):') - for name, dev in sorted(TESTS['badblocks']['Devices'].items()): - cur_status = TESTS['badblocks']['Status'][name] - nvme_smart_status = TESTS['NVMe/SMART']['Status'].get(name, None) - if cur_status == 'Denied': - # Skip denied disks - continue - if nvme_smart_status == 'NS': - TESTS['badblocks']['Status'][name] = 'Skipped' - else: - # Not testing SMART, SMART CS, or SMART OVERRIDE - TESTS['badblocks']['Status'][name] = 'Working' - update_progress() - print_standard(' /dev/{:11} '.format(name+'...'), end='', flush=True) - run_program('tmux split-window -dl 5 {} {} {}'.format( - 'hw-diags-badblocks', - '/dev/{}'.format(name), - progress_file).split()) - wait_for_process('badblocks') - print_standard('Done', timestamp=False) - - # Check results - if os.path.exists(progress_file): - with open(progress_file, 'r') as f: - text = f.read() - TESTS['badblocks']['Results'][name] = text - r = re.search(r'Pass completed.*0/0/0 errors', text) - if r: - TESTS['badblocks']['Status'][name] = 'CS' - else: - TESTS['badblocks']['Status'][name] = 'NS' - - # Move temp file - shutil.move(progress_file, '{}/badblocks-{}.log'.format( - global_vars['LogDir'], name)) - else: - TESTS['badblocks']['Status'][name] = 'NS' - update_progress() - - # Done - run_program('tmux kill-pane -a'.split(), check=False) - pass - -def run_iobenchmark(ticket_number): - """Run a read-only test for all detected disks.""" - aborted = False - clear_screen() - print_log('\nStart I/O Benchmark test(s)\n') - progress_file = '{}/iobenchmark_progress.out'.format(global_vars['LogDir']) - update_progress() - - # Set Window layout and start test - run_program('tmux split-window -dhl 15 watch -c -n1 -t cat {}'.format( - TESTS['Progress Out']).split()) - - # Show disk details - for name, dev in sorted(TESTS['iobenchmark']['Devices'].items()): - show_disk_details(dev) - print_standard(' ') - update_progress() - - # Run - print_standard('Running benchmark test(s):') - for name, dev in sorted(TESTS['iobenchmark']['Devices'].items()): - cur_status = TESTS['iobenchmark']['Status'][name] - nvme_smart_status = TESTS['NVMe/SMART']['Status'].get(name, None) - bb_status = TESTS['badblocks']['Status'].get(name, None) - if cur_status == 'Denied': - # Skip denied disks - continue - if nvme_smart_status == 'NS': - TESTS['iobenchmark']['Status'][name] = 'Skipped' - elif bb_status in ['NS', 'Skipped']: - TESTS['iobenchmark']['Status'][name] = 'Skipped' - else: - # (SMART tests not run or CS/OVERRIDE) - # AND (BADBLOCKS tests not run or CS) - TESTS['iobenchmark']['Status'][name] = 'Working' - update_progress() - print_standard(' /dev/{:11} '.format(name+'...'), end='', flush=True) - - # Get dev size - cmd = 'sudo lsblk -bdno size /dev/{}'.format(name) - try: - result = run_program(cmd.split()) - dev_size = result.stdout.decode().strip() - dev_size = int(dev_size) - except: - # Failed to get dev size, requires manual testing instead - TESTS['iobenchmark']['Status'][name] = 'ERROR' - continue - if dev_size < IO_VARS['Minimum Dev Size']: - TESTS['iobenchmark']['Status'][name] = 'ERROR' - continue - - # Calculate dd values - ## test_size is the area to be read in bytes - ## If the dev is < 10Gb then it's the whole dev - ## Otherwise it's the larger of 10Gb or 1% of the dev - ## - ## test_chunks is the number of groups of "Chunk Size" in test_size - ## This number is reduced to a multiple of the graph width in - ## order to allow for the data to be condensed cleanly - ## - ## skip_blocks is the number of "Block Size" groups not tested - ## skip_count is the number of blocks to skip per test_chunk - ## skip_extra is how often to add an additional skip block - ## This is needed to ensure an even testing across the dev - ## This is calculated by using the fractional amount left off - ## of the skip_count variable - test_size = min(IO_VARS['Minimum Test Size'], dev_size) - test_size = max( - test_size, dev_size*IO_VARS['Alt Test Size Factor']) - test_chunks = int(test_size // IO_VARS['Chunk Size']) - test_chunks -= test_chunks % IO_VARS['Graph Horizontal Width'] - test_size = test_chunks * IO_VARS['Chunk Size'] - skip_blocks = int((dev_size - test_size) // IO_VARS['Block Size']) - skip_count = int((skip_blocks / test_chunks) // 1) - skip_extra = 0 - try: - skip_extra = 1 + int(1 / ((skip_blocks / test_chunks) % 1)) - except ZeroDivisionError: - # skip_extra == 0 is fine - pass - - # Open dd progress pane after initializing file - with open(progress_file, 'w') as f: - f.write('') - sleep(1) - cmd = 'tmux split-window -dp 75 -PF #D tail -f {}'.format( - progress_file) - result = run_program(cmd.split()) - bottom_pane = result.stdout.decode().strip() - - # Run dd read tests - offset = 0 - TESTS['iobenchmark']['Data'][name] = { - 'Graph': [], - 'Read Rates': []} - for i in range(test_chunks): - i += 1 - s = skip_count - c = int(IO_VARS['Chunk Size'] / IO_VARS['Block Size']) - if skip_extra and i % skip_extra == 0: - s += 1 - cmd = 'sudo dd bs={b} skip={s} count={c} if=/dev/{n} of={o} iflag=direct'.format( - b=IO_VARS['Block Size'], - s=offset+s, - c=c, - n=name, - o='/dev/null') - result = run_program(cmd.split()) - result_str = result.stderr.decode().replace('\n', '') - cur_rate = get_read_rate(result_str) - TESTS['iobenchmark']['Data'][name]['Read Rates'].append( - cur_rate) - TESTS['iobenchmark']['Data'][name]['Graph'].append( - '{percent:0.1f} {rate}'.format( - percent=i/test_chunks*100, - rate=int(cur_rate/(1024**2)))) - if i % IO_VARS['Progress Refresh Rate'] == 0: - # Update vertical graph - update_io_progress( - percent=i/test_chunks*100, - rate=cur_rate, - progress_file=progress_file) - # Update offset - offset += s + c - print_standard('Done', timestamp=False) - - # Close bottom pane - run_program(['tmux', 'kill-pane', '-t', bottom_pane]) - - # Build report - avg_min_max = 'Average read speed: {:3.1f} MB/s (Min: {:3.1f}, Max: {:3.1f})'.format( - sum(TESTS['iobenchmark']['Data'][name]['Read Rates'])/len( - TESTS['iobenchmark']['Data'][name]['Read Rates'])/(1024**2), - min(TESTS['iobenchmark']['Data'][name]['Read Rates'])/(1024**2), - max(TESTS['iobenchmark']['Data'][name]['Read Rates'])/(1024**2)) - TESTS['iobenchmark']['Data'][name]['Avg/Min/Max'] = avg_min_max - TESTS['iobenchmark']['Data'][name]['Merged Rates'] = [] - pos = 0 - width = int(test_chunks / IO_VARS['Graph Horizontal Width']) - for i in range(IO_VARS['Graph Horizontal Width']): - # Append average rate for WIDTH number of rates to new array - TESTS['iobenchmark']['Data'][name]['Merged Rates'].append(sum( - TESTS['iobenchmark']['Data'][name]['Read Rates'][pos:pos+width])/width) - pos += width - report = generate_horizontal_graph( - TESTS['iobenchmark']['Data'][name]['Merged Rates']) - report += '\n{}'.format(avg_min_max) - TESTS['iobenchmark']['Results'][name] = report - - # Set CS/NS - min_read = min(TESTS['iobenchmark']['Data'][name]['Read Rates']) - avg_read = sum( - TESTS['iobenchmark']['Data'][name]['Read Rates'])/len( - TESTS['iobenchmark']['Data'][name]['Read Rates']) - dev_rotational = dev['lsblk'].get('rota', None) - if dev_rotational == "0": - # Use SSD scale - thresh_min = IO_VARS['Threshold SSD Min'] - thresh_high_avg = IO_VARS['Threshold SSD High Avg'] - thresh_low_avg = IO_VARS['Threshold SSD Low Avg'] - else: - # Use HDD scale - thresh_min = IO_VARS['Threshold HDD Min'] - thresh_high_avg = IO_VARS['Threshold HDD High Avg'] - thresh_low_avg = IO_VARS['Threshold HDD Low Avg'] - if min_read <= thresh_min and avg_read <= thresh_high_avg: - TESTS['iobenchmark']['Status'][name] = 'NS' - elif avg_read <= thresh_low_avg: - TESTS['iobenchmark']['Status'][name] = 'NS' - else: - TESTS['iobenchmark']['Status'][name] = 'CS' - - # Save logs - dest_filename = '{}/iobenchmark-{}.log'.format(global_vars['LogDir'], name) - shutil.move(progress_file, dest_filename) - with open(dest_filename.replace('.', '-raw.'), 'a') as f: - f.write('\n'.join(TESTS['iobenchmark']['Data'][name]['Graph'])) - update_progress() - - # Done - run_program('tmux kill-pane -a'.split(), check=False) - pass - -def run_mprime(ticket_number): - """Run Prime95 for MPRIME_LIMIT minutes while showing the temps.""" - aborted = False - print_log('\nStart Prime95 test') - TESTS['Prime95']['Status'] = 'Working' - update_progress() - - # Set Window layout and start test - run_program('tmux split-window -dl 10 -c {wd} {cmd} {wd}'.format( - wd=global_vars['TmpDir'], cmd='hw-diags-prime95').split()) - run_program('tmux split-window -dhl 15 watch -c -n1 -t cat {}'.format( - TESTS['Progress Out']).split()) - run_program('tmux split-window -bd watch -c -n1 -t hw-sensors'.split()) - run_program('tmux resize-pane -y 3'.split()) - - # Start test - run_program(['apple-fans', 'max']) - try: - for i in range(int(MPRIME_LIMIT)): - clear_screen() - min_left = int(MPRIME_LIMIT) - i - print_standard('Running Prime95 ({} minute{} left)'.format( - min_left, - 's' if min_left != 1 else '')) - print_warning('If running too hot, press CTRL+c to abort the test') - sleep(60) - except KeyboardInterrupt: - # Catch CTRL+C - aborted = True - TESTS['Prime95']['Status'] = 'Aborted' - print_warning('\nAborted.') - update_progress() - - # Save "final" temps - run_program( - cmd = 'hw-sensors >> "{}/Final Temps.out"'.format( - global_vars['LogDir']).split(), - check = False, - pipe = False, - shell = True) - run_program( - cmd = 'hw-sensors --nocolor >> "{}/Final Temps.log"'.format( - global_vars['LogDir']).split(), - check = False, - pipe = False, - shell = True) - - # Stop test - run_program('killall -s INT mprime'.split(), check=False) - run_program(['apple-fans', 'auto']) - - # Move logs to Ticket folder - for item in os.scandir(global_vars['TmpDir']): - try: - shutil.move(item.path, global_vars['LogDir']) - except Exception: - print_error('ERROR: Failed to move "{}" to "{}"'.format( - item.path, - global_vars['LogDir'])) - - # Check logs - TESTS['Prime95']['NS'] = False - TESTS['Prime95']['CS'] = False - log = '{}/results.txt'.format(global_vars['LogDir']) - if os.path.exists(log): - with open(log, 'r') as f: - text = f.read() - TESTS['Prime95']['results.txt'] = text - r = re.search(r'(error|fail)', text) - TESTS['Prime95']['NS'] = bool(r) - log = '{}/prime.log'.format(global_vars['LogDir']) - if os.path.exists(log): - with open(log, 'r') as f: - text = f.read() - TESTS['Prime95']['prime.log'] = text - r = re.search(r'completed.*0 errors, 0 warnings', text) - TESTS['Prime95']['CS'] = bool(r) - - # Update status - if not aborted: - if TESTS['Prime95']['NS']: - TESTS['Prime95']['Status'] = 'NS' - elif TESTS['Prime95']['CS']: - TESTS['Prime95']['Status'] = 'CS' - else: - TESTS['Prime95']['Status'] = 'Unknown' - update_progress() - - # Build osTicket report - if ticket_number: - report = ['Prime95 ({}):'.format(TESTS['Prime95']['Status'])] - log_path = '{}/prime.log'.format(global_vars['LogDir']) - try: - with open(log_path, 'r') as f: - for line in f.readlines(): - line = line.strip() - r = re.search('(completed \d+ tests.*)', line, re.IGNORECASE) - if r: - report.append(r.group(1)) - except: - report.append('ERROR: Failed to read log.') - report.append('') - report.append('Final temps:') - log_path = '{}/Final Temps.log'.format(global_vars['LogDir']) - try: - with open(log_path, 'r') as f: - for line in f.readlines(): - line = line.strip() - if not line: - # Stop after CPU temp(s) - break - report.append(line) - except: - report.append('ERROR: Failed to read log.') - - # Upload osTicket report - osticket_post_reply( - ticket_id=ticket_number, - response='\n'.join(report)) - - if aborted: - if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled']: - if not ask('Proceed to next test?'): - for name in TESTS['NVMe/SMART']['Devices'].keys(): - for t in ['NVMe/SMART', 'badblocks', 'iobenchmark']: - cur_status = TESTS[t]['Status'][name] - if cur_status not in ['CS', 'Denied', 'NS']: - TESTS[t]['Status'][name] = 'Aborted' - run_program('tmux kill-pane -a'.split()) - raise GenericError - - # Done - run_program('tmux kill-pane -a'.split()) - -def run_nvme_smart(ticket_number): - """Run the built-in NVMe or SMART test for all detected disks.""" - aborted = False - clear_screen() - print_log('\nStart NVMe/SMART test(s)\n') - progress_file = '{}/selftest_progress.out'.format(global_vars['LogDir']) - update_progress() - - # Set Window layout and start test - run_program('tmux split-window -dl 3 watch -c -n1 -t cat {}'.format( - progress_file).split()) - run_program('tmux split-window -dhl 15 watch -c -n1 -t cat {}'.format( - TESTS['Progress Out']).split()) - - # Show disk details - for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()): - show_disk_details(dev) - print_standard(' ') - update_progress() - - # Run - for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()): - TESTS['NVMe/SMART']['Short Test'][name] = None - cur_status = TESTS['NVMe/SMART']['Status'][name] - if cur_status == 'OVERRIDE': - # Skipping test per user request - continue - if TESTS['NVMe/SMART']['Quick'] or dev.get('NVMe Disk', False): - # Skip SMART self-tests for quick checks and NVMe disks - if dev['Quick Health OK']: - TESTS['NVMe/SMART']['Status'][name] = 'CS' - else: - TESTS['NVMe/SMART']['Status'][name] = 'NS' - elif not dev['Quick Health OK']: - # SMART overall == Failed or attributes bad, avoid self-test - TESTS['NVMe/SMART']['Status'][name] = 'NS' - else: - # Start SMART short self-test - test_length = dev['smartctl'].get( - 'ata_smart_data', {}).get( - 'self_test', {}).get( - 'polling_minutes', {}).get( - 'short', 5) - test_length = int(test_length) + 5 - TESTS['NVMe/SMART']['Status'][name] = 'Working' - update_progress() - print_standard('Running SMART short self-test(s):') - print_standard( - ' /dev/{:8}({} minutes)... '.format(name, test_length), - end='', flush=True) - run_program( - 'sudo smartctl -t short /dev/{}'.format(name).split(), - check=False) - - # Wait and show progress (in 10 second increments) - for iteration in range(int(test_length*60/10)): - # Update SMART data - dev['smartctl'] = get_smart_details(name) - - # Check if test is complete - if iteration >= 6: - done = dev['smartctl'].get( - 'ata_smart_data', {}).get( - 'self_test', {}).get( - 'status', {}).get( - 'passed', False) - if done: - break - - # Update progress_file - with open(progress_file, 'w') as f: - f.write('SMART self-test status:\n {}'.format( - dev['smartctl'].get( - 'ata_smart_data', {}).get( - 'self_test', {}).get( - 'status', {}).get( - 'string', 'unknown'))) - sleep(10) - os.remove(progress_file) - - # Check result - test_passed = dev['smartctl'].get( - 'ata_smart_data', {}).get( - 'self_test', {}).get( - 'status', {}).get( - 'passed', False) - if test_passed: - TESTS['NVMe/SMART']['Status'][name] = 'CS' - TESTS['NVMe/SMART']['Short Test'][name] = 'CS' - else: - TESTS['NVMe/SMART']['Status'][name] = 'NS' - TESTS['NVMe/SMART']['Short Test'][name] = 'NS' - update_progress() - print_standard('Done', timestamp=False) - - # Done - run_program('tmux kill-pane -a'.split(), check=False) - -def run_tests(tests, ticket_number=None): - """Run selected hardware test(s).""" - clear_screen() - print_standard('Starting Hardware Diagnostics') - if ticket_number: - print_standard(' For osTicket #{}'.format(ticket_number)) - print_standard(' ') - print_standard('Running tests: {}'.format(', '.join(tests))) - # Enable selected tests - for t in ['Prime95', 'NVMe/SMART', 'badblocks', 'iobenchmark']: - TESTS[t]['Enabled'] = t in tests - TESTS['NVMe/SMART']['Quick'] = 'Quick' in tests - - # Initialize - if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled'] or TESTS['iobenchmark']['Enabled']: - print_standard(' ') - scan_disks() - update_progress() - - # Run - mprime_aborted = False - if TESTS['Prime95']['Enabled']: - try: - run_mprime(ticket_number) - except GenericError: - mprime_aborted = True - if not mprime_aborted: - if TESTS['NVMe/SMART']['Enabled']: - run_nvme_smart(ticket_number) - if TESTS['badblocks']['Enabled']: - run_badblocks(ticket_number) - if TESTS['iobenchmark']['Enabled']: - run_iobenchmark(ticket_number) - - # Show results - if ticket_number: - post_drive_results(ticket_number) - show_results() - - # Open log - if not TESTS['NVMe/SMART']['Quick'] and ENABLED_OPEN_LOGS: - try: - popen_program(['nohup', 'leafpad', global_vars['LogFile']], pipe=True) - except Exception: - print_error('ERROR: Failed to open log: {}'.format( - global_vars['LogFile'])) - pause('Press Enter to exit...') - -def scan_disks(full_paths=False, only_path=None): - """Scan for disks eligible for hardware testing.""" - - # Get eligible disk list - cmd = ['lsblk', '-J', '-O'] - if full_paths: - cmd.append('-p') - if only_path: - cmd.append(only_path) + # Ignore and leave self.lscpu empty + return + for line in json_data.get('lscpu', []): + _field = line.get('field', None).replace(':', '') + _data = line.get('data', None) + if not _field and not _data: + # Skip + print_warning(_field, _data) + pause() + continue + self.lscpu[_field] = _data + + def generate_cpu_report(self): + """Generate CPU report with data from all tests.""" + report = [] + report.append('{BLUE}Device{CLEAR}'.format(**COLORS)) + report.append(' {}'.format(self.name)) + + # Tests + for test in self.tests.values(): + report.extend(test.report) + + return report + + +class DiskObj(): + """Object for tracking disk specific data.""" + def __init__(self, disk_path): + self.checkbox = None + self.disk_ok = True + self.labels = [] + self.lsblk = {} + self.name = re.sub(r'^.*/(.*)', r'\1', disk_path) + self.nvme_attributes = {} + self.path = disk_path + self.smart_attributes = {} + self.smart_timeout = False + self.smart_self_test = {} + self.smartctl = {} + self.tests = OrderedDict() + self.get_details() + + # Try enabling SMART + run_program(['sudo', 'smartctl', '--smart=on', self.path], check=False) + + # Get NVMe/SMART data and set description + self.get_smart_details() + self.description = '{size} ({tran}) {model} {serial}'.format( + **self.lsblk) + + def calc_io_dd_values(self): + """Calcualte I/O benchmark dd values.""" + # Get real disk size + cmd = ['lsblk', + '--bytes', '--nodeps', '--noheadings', + '--output', 'size', self.path] result = run_program(cmd) - json_data = json.loads(result.stdout.decode()) - devs = {} - for d in json_data.get('blockdevices', []): - if d['type'] == 'disk': - if d['hotplug'] == '0': - devs[d['name']] = {'lsblk': d} - TESTS['NVMe/SMART']['Status'][d['name']] = 'Pending' - TESTS['badblocks']['Status'][d['name']] = 'Pending' - TESTS['iobenchmark']['Status'][d['name']] = 'Pending' - else: - # Skip WizardKit devices - skip_dev=False - wk_label_regex = r'{}_(LINUX|UFD)'.format(KIT_NAME_SHORT) - for c in d.get('children', []): - r = re.search( - wk_label_regex, c.get('label', ''), re.IGNORECASE) - skip_dev = bool(r) - if not skip_dev: - devs[d['name']] = {'lsblk': d} - TESTS['NVMe/SMART']['Status'][d['name']] = 'Pending' - TESTS['badblocks']['Status'][d['name']] = 'Pending' - TESTS['iobenchmark']['Status'][d['name']] = 'Pending' + self.size_bytes = int(result.stdout.decode().strip()) - for dev, data in devs.items(): - # Get SMART attributes - run_program( - cmd = 'sudo smartctl -s on {}{}'.format( - '' if full_paths else '/dev/', - dev).split(), - check = False) - data['smartctl'] = get_smart_details(dev) + # dd calculations + ## The minimum dev size is 'Graph Horizontal Width' * 'Chunk Size' + ## (e.g. 1.25 GB for a width of 40 and a chunk size of 32MB) + ## If the device is smaller than the minimum dd_chunks would be set + ## to zero which would cause a divide by zero error. + ## If the device is below the minimum size an Exception will be raised + ## + ## dd_size is the area to be read in bytes + ## If the dev is < 10Gb then it's the whole dev + ## Otherwise it's the larger of 10Gb or 1% of the dev + ## + ## dd_chunks is the number of groups of "Chunk Size" in self.dd_size + ## This number is reduced to a multiple of the graph width in + ## order to allow for the data to be condensed cleanly + ## + ## dd_chunk_blocks is the chunk size in number of blocks + ## (e.g. 64 if block size is 512KB and chunk size is 32MB + ## + ## dd_skip_blocks is the number of "Block Size" groups not tested + ## dd_skip_count is the number of blocks to skip per self.dd_chunk + ## dd_skip_extra is how often to add an additional skip block + ## This is needed to ensure an even testing across the dev + ## This is calculated by using the fractional amount left off + ## of the dd_skip_count variable + self.dd_size = min(IO_VARS['Minimum Test Size'], self.size_bytes) + self.dd_size = max( + self.dd_size, + self.size_bytes * IO_VARS['Alt Test Size Factor']) + self.dd_chunks = int(self.dd_size // IO_VARS['Chunk Size']) + self.dd_chunks -= self.dd_chunks % IO_VARS['Graph Horizontal Width'] + if self.dd_chunks < IO_VARS['Graph Horizontal Width']: + raise DeviceTooSmallError + self.dd_chunk_blocks = int(IO_VARS['Chunk Size'] / IO_VARS['Block Size']) + self.dd_size = self.dd_chunks * IO_VARS['Chunk Size'] + self.dd_skip_blocks = int( + (self.size_bytes - self.dd_size) // IO_VARS['Block Size']) + self.dd_skip_count = int((self.dd_skip_blocks / self.dd_chunks) // 1) + self.dd_skip_extra = 0 + try: + self.dd_skip_extra = 1 + int( + 1 / ((self.dd_skip_blocks / self.dd_chunks) % 1)) + except ZeroDivisionError: + # self.dd_skip_extra == 0 is fine + pass - # Get NVMe attributes - if data['lsblk']['tran'] == 'nvme': - cmd = 'sudo nvme smart-log /dev/{} -o json'.format(dev).split() - cmd = 'sudo nvme smart-log {}{} -o json'.format( - '' if full_paths else '/dev/', - dev).split() - result = run_program(cmd, check=False) - try: - data['nvme-cli'] = json.loads(result.stdout.decode()) - except Exception: - # Let other sections deal with the missing data - data['nvme-cli'] = {} - data['NVMe Disk'] = True + def check_attributes(self, silent=False): + """Check NVMe / SMART attributes for errors.""" + override_disabled = False + if self.nvme_attributes: + attr_type = 'NVMe' + items = self.nvme_attributes.items() + elif self.smart_attributes: + attr_type = 'SMART' + items = self.smart_attributes.items() + for k, v in items: + if k in ATTRIBUTES[attr_type]: + if 'Error' not in ATTRIBUTES[attr_type][k]: + # Only worried about error thresholds + continue + if ATTRIBUTES[attr_type][k].get('Ignore', False): + # Attribute is non-failing, skip + continue + if v['raw'] >= ATTRIBUTES[attr_type][k]['Error']: + self.disk_ok = False - # Set "Quick Health OK" value - ## NOTE: If False then require override for badblocks test - wanted_smart_list = [ - 'ata_smart_attributes', - 'ata_smart_data', - 'smart_status', - ] - if data.get('NVMe Disk', False): - crit_warn = data['nvme-cli'].get('critical_warning', 1) - if crit_warn == 0: - dev_name = data['lsblk']['name'] - data['Quick Health OK'] = True - TESTS['NVMe/SMART']['Status'][dev_name] = 'CS' - else: - data['Quick Health OK'] = False - data['Quick Health OK'] = True if crit_warn == 0 else False - elif set(wanted_smart_list).issubset(data['smartctl'].keys()): - data['SMART Pass'] = data['smartctl'].get('smart_status', {}).get( - 'passed', False) - data['Quick Health OK'] = data['SMART Pass'] - data['SMART Support'] = True - else: - data['Quick Health OK'] = False - data['SMART Support'] = False + # Disable override if necessary + override_disabled |= ATTRIBUTES[attr_type][k].get( + 'Critical', False) - # Ask for manual overrides if necessary - if TESTS['badblocks']['Enabled'] or TESTS['iobenchmark']['Enabled']: - show_disk_details(data) - needs_override = False - if not data['Quick Health OK']: - needs_override = True - print_warning( - "WARNING: Health can't be confirmed for: /dev/{}".format(dev)) - if get_smart_value(data['smartctl'], '199'): - # SMART attribute present and it's value is non-zero - needs_override = True - print_warning( - 'WARNING: SMART 199/C7 error detected on /dev/{}'.format(dev)) - print_standard(' (Have you tried swapping the drive cable?)') - if needs_override: - dev_name = data['lsblk']['name'] - print_standard(' ') - if ask('Run tests on this device anyway?'): - TESTS['NVMe/SMART']['Status'][dev_name] = 'OVERRIDE' - else: - TESTS['NVMe/SMART']['Status'][dev_name] = 'Skipped' - TESTS['badblocks']['Status'][dev_name] = 'Denied' - TESTS['iobenchmark']['Status'][dev_name] = 'Denied' - print_standard(' ') # In case there's more than one "OVERRIDE" disk + # SMART overall assessment + ## NOTE: Only fail drives if the overall value exists and reports failed + if not self.smartctl.get('smart_status', {}).get('passed', True): + self.disk_ok = False + override_disabled = True - TESTS['NVMe/SMART']['Devices'] = devs - TESTS['badblocks']['Devices'] = devs - TESTS['iobenchmark']['Devices'] = devs - return devs - -def show_disk_details(dev, only_attributes=False): - """Display disk details.""" - dev_name = dev['lsblk']['name'] - if not only_attributes: - # Device description - print_info('Device: {}{}'.format( - '' if '/dev/' in dev['lsblk']['name'] else '/dev/', - dev['lsblk']['name'])) - print_standard(' {:>4} ({}) {} {}'.format( - str(dev['lsblk'].get('size', '???b')).strip(), - str(dev['lsblk'].get('tran', '???')).strip().upper().replace( - 'NVME', 'NVMe'), - str(dev['lsblk'].get('model', 'Unknown Model')).strip(), - str(dev['lsblk'].get('serial', 'Unknown Serial')).strip(), - )) - - # Warnings - if dev.get('NVMe Disk', False): - if dev['Quick Health OK']: - print_warning('WARNING: NVMe support is still experimental') - else: - print_error('ERROR: NVMe disk is reporting critical warnings') - elif not dev['SMART Support']: - print_error('ERROR: Unable to retrieve SMART data') - elif not dev['SMART Pass']: - print_error('ERROR: SMART overall-health assessment result: FAILED') - - # Attributes - if dev.get('NVMe Disk', False): - if only_attributes: - print_info('SMART Attributes:', end='') - print_warning(' Updated: {}'.format( - time.strftime('%Y-%m-%d %H:%M %Z'))) - else: - print_info('Attributes:') - for attrib, threshold in sorted(ATTRIBUTES['NVMe'].items()): - if attrib in dev['nvme-cli']: - print_standard( - ' {:37}'.format(attrib.replace('_', ' ').title()), - end='', flush=True) - raw_num = dev['nvme-cli'][attrib] - raw_str = str(raw_num) - if (threshold.get('Error', False) and - raw_num >= threshold.get('Error', -1)): - print_error(raw_str, timestamp=False) - if not threshold.get('Ignore', False): - dev['Quick Health OK'] = False - TESTS['NVMe/SMART']['Status'][dev_name] = 'NS' - elif (threshold.get('Warning', False) and - raw_num >= threshold.get('Warning', -1)): - print_warning(raw_str, timestamp=False) - else: - print_success(raw_str, timestamp=False) - elif dev['smartctl'].get('ata_smart_attributes', None): - # SMART attributes - if only_attributes: - print_info('SMART Attributes:', end='') - print_warning(' Updated: {}'.format( - time.strftime('%Y-%m-%d %H:%M %Z'))) - else: - print_info('Attributes:') - s_table = dev['smartctl'].get('ata_smart_attributes', {}).get( - 'table', {}) - s_table = {a.get('id', 'Unknown'): a for a in s_table} - for attrib, threshold in sorted(ATTRIBUTES['SMART'].items()): - if attrib in s_table: - print_standard( - ' {:>3} {:32}'.format( - attrib, - s_table[attrib]['name']).replace('_', ' ').title(), - end='', flush=True) - raw_str = s_table[attrib]['raw']['string'] - raw_num = re.sub(r'^(\d+).*$', r'\1', raw_str) - try: - raw_num = float(raw_num) - except ValueError: - # Not sure about this one, print raw_str without color? - print_standard(raw_str, timestamp=False) - continue - if (threshold.get('Error', False) and - raw_num >= threshold.get('Error', -1)): - print_error(raw_str, timestamp=False) - if not threshold.get('Ignore', False): - dev['Quick Health OK'] = False - TESTS['NVMe/SMART']['Status'][dev_name] = 'NS' - elif (threshold.get('Warning', False) and - raw_num >= threshold.get('Warning', -1)): - print_warning(raw_str, timestamp=False) - else: - print_success(raw_str, timestamp=False) - -def show_results(): - """Show results for selected test(s).""" - clear_screen() - print_log('\n───────────────────────────') - print_standard('Hardware Diagnostic Results') - update_progress() - - # Set Window layout and show progress - run_program('tmux split-window -dhl 15 watch -c -n1 -t cat {}'.format( - TESTS['Progress Out']).split()) - - # Prime95 - if TESTS['Prime95']['Enabled']: - print_success('\nPrime95:') - for log, regex in [ - ['results.txt', r'(error|fail)'], - ['prime.log', r'completed.*0 errors, 0 warnings']]: - if log in TESTS['Prime95']: - print_info('Log: {}'.format(log)) - lines = [line.strip() for line - in TESTS['Prime95'][log].splitlines() - if re.search(regex, line, re.IGNORECASE)] - for line in lines[-4:]: - line = re.sub(r'^.*Worker #\d.*Torture Test (.*)', r'\1', - line, re.IGNORECASE) - if TESTS['Prime95'].get('NS', False): - print_error(' {}'.format(line)) - else: - print_standard(' {}'.format(line)) - print_info('Final temps') - print_log(' See Final Temps.log') - with open('{}/Final Temps.out'.format(global_vars['LogDir']), 'r') as f: - for line in f.readlines(): - if re.search(r'^\s*$', line.strip()): - # Stop after coretemps (which should be first) - break - print(' {}'.format(line.strip())) - print_standard(' ') - - # NVMe/SMART / badblocks / iobenchmark - if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled'] or TESTS['iobenchmark']['Enabled']: - print_success('Disks:') - for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()): - show_disk_details(dev) - bb_status = TESTS['badblocks']['Status'].get(name, None) - if (TESTS['badblocks']['Enabled'] - and bb_status not in ['Denied', 'OVERRIDE', 'Skipped']): - print_info('badblocks:') - result = TESTS['badblocks']['Results'].get(name, '') - for line in result.splitlines(): - if re.search(r'Pass completed', line, re.IGNORECASE): - line = re.sub( - r'Pass completed,?\s+', r'', - line.strip(), re.IGNORECASE) - if TESTS['badblocks']['Status'][name] == 'CS': - print_standard(' {}'.format(line)) - else: - print_error(' {}'.format(line)) - io_status = TESTS['iobenchmark']['Status'].get(name, None) - if (TESTS['iobenchmark']['Enabled'] - and io_status not in ['Denied', 'OVERRIDE', 'Skipped']): - print_info('Benchmark:') - result = TESTS['iobenchmark']['Results'].get(name, '') - for line in result.split('\n'): - print_standard(' {}'.format(line)) + # Print errors + if not silent: + if self.disk_ok: + # 199/C7 warning + if self.smart_attributes.get(199, {}).get('raw', 0) > 0: + print_warning('199/C7 error detected') + print_standard(' (Have you tried swapping the disk cable?)') + else: + # Override? + show_report( + self.generate_attribute_report(description=True), + log_report=True) + print_warning(' {} error(s) detected.'.format(attr_type)) + if override_disabled: + print_standard('Tests disabled for this device') + pause() + elif not (len(self.tests) == 3 and HW_OVERRIDES_LIMITED): + if HW_OVERRIDES_FORCED or ask('Run tests on this device anyway?'): + self.disk_ok = True + if 'NVMe / SMART' in self.tests: + self.disable_test('NVMe / SMART', 'OVERRIDE') + if not self.nvme_attributes and self.smart_attributes: + # Re-enable for SMART short-tests + self.tests['NVMe / SMART'].disabled = False + else: + self.tests['NVMe / SMART'].failed = True print_standard(' ') - # osTicket - if ost_db['Errors']: - print_warning('WARNING: Failed to post result(s) to osTicket') - print_standard(' ') + def disable_test(self, name, status): + """Disable test by name and update status.""" + if name in self.tests: + self.tests[name].update_status(status) + self.tests[name].disabled = True + + def generate_attribute_report( + self, description=False, short_test=False, timestamp=False): + """Generate NVMe / SMART report, returns list.""" + report = [] + if description: + report.append('{BLUE}Device ({name}){CLEAR}'.format( + name=self.name, **COLORS)) + report.append(' {}'.format(self.description)) + + # Warnings + if self.nvme_attributes: + attr_type = 'NVMe' + report.append( + ' {YELLOW}NVMe disk support is still experimental{CLEAR}'.format( + **COLORS)) + elif self.smart_attributes: + attr_type = 'SMART' + else: + # No attribute data available, return short report + report.append( + ' {YELLOW}No NVMe or SMART data available{CLEAR}'.format( + **COLORS)) + return report + if not self.smartctl.get('smart_status', {}).get('passed', True): + report.append( + ' {RED}SMART overall self-assessment: Failed{CLEAR}'.format( + **COLORS)) + + # Attributes + report.append('{BLUE}{a} Attributes{YELLOW}{u:>23} {t}{CLEAR}'.format( + a=attr_type, + u='Updated:' if timestamp else '', + t=time.strftime('%Y-%m-%d %H:%M %Z') if timestamp else '', + **COLORS)) + if self.nvme_attributes: + attr_type = 'NVMe' + items = self.nvme_attributes.items() + elif self.smart_attributes: + attr_type = 'SMART' + items = self.smart_attributes.items() + for k, v in items: + if k in ATTRIBUTES[attr_type]: + _note = '' + _color = COLORS['GREEN'] + + # Attribute ID & Name + if attr_type == 'NVMe': + _line = ' {:38}'.format(k.replace('_', ' ').title()) + else: + _line = ' {i:>3} / {h}: {n:28}'.format( + i=k, + h=ATTRIBUTES[attr_type][k]['Hex'], + n=v['name'][:28]) + + # Set color + for _t, _c in [['Warning', 'YELLOW'], ['Error', 'RED']]: + if _t in ATTRIBUTES[attr_type][k]: + if v['raw'] >= ATTRIBUTES[attr_type][k][_t]: + _color = COLORS[_c] + + # 199/C7 warning + if str(k) == '199' and v['raw'] > 0: + _note = '(bad cable?)' + + # Attribute value + _line += '{c}{v} {YELLOW}{n}{CLEAR}'.format( + c=_color, + v=v['raw_str'], + n=_note, + **COLORS) + + # Add line to report + report.append(_line) + + # SMART short-test + if short_test: + report.append('{BLUE}SMART Short self-test{CLEAR}'.format(**COLORS)) + report.append(' {}'.format( + self.smart_self_test['status'].get( + 'string', 'UNKNOWN').capitalize())) + if self.smart_timeout: + report.append(' {YELLOW}Timed out{CLEAR}'.format(**COLORS)) # Done + return report + + def generate_disk_report(self): + """Generate disk report with data from all tests.""" + report = [] + report.append('{BLUE}Device ({name}){CLEAR}'.format( + name=self.name, **COLORS)) + report.append(' {}'.format(self.description)) + + # Attributes + if 'NVMe / SMART' not in self.tests: + report.extend(self.generate_attribute_report()) + elif not self.tests['NVMe / SMART'].report: + report.extend(self.generate_attribute_report()) + + # Tests + for test in self.tests.values(): + report.extend(test.report) + + return report + + def get_details(self): + """Get data from lsblk.""" + cmd = ['lsblk', '--json', '--output-all', '--paths', self.path] + try: + result = run_program(cmd, check=False) + json_data = json.loads(result.stdout.decode()) + self.lsblk = json_data['blockdevices'][0] + except Exception: + # Leave self.lsblk empty + pass + + # Set necessary details + self.lsblk['model'] = self.lsblk.get('model', 'Unknown Model') + self.lsblk['name'] = self.lsblk.get('name', self.path) + self.lsblk['rota'] = self.lsblk.get('rota', True) + self.lsblk['serial'] = self.lsblk.get('serial', 'Unknown Serial') + self.lsblk['size'] = self.lsblk.get('size', '???b') + self.lsblk['tran'] = self.lsblk.get('tran', '???') + + # Ensure certain attributes are strings + for attr in ['model', 'name', 'rota', 'serial', 'size', 'tran']: + if not isinstance(self.lsblk[attr], str): + self.lsblk[attr] = str(self.lsblk[attr]) + self.lsblk['tran'] = self.lsblk['tran'].upper().replace('NVME', 'NVMe') + + # Build list of labels + for disk in [self.lsblk, *self.lsblk.get('children', [])]: + self.labels.append(disk.get('label', '')) + self.labels.append(disk.get('partlabel', '')) + self.labels = [str(label) for label in self.labels if label] + + def get_smart_details(self): + """Get data from smartctl.""" + cmd = ['sudo', 'smartctl', '--all', '--json', self.path] + try: + result = run_program(cmd, check=False) + self.smartctl = json.loads(result.stdout.decode()) + except Exception: + # Leave self.smartctl empty + pass + + # Check for attributes + if KEY_NVME in self.smartctl: + self.nvme_attributes = { + k: {'name': k, 'raw': int(v), 'raw_str': str(v)} + for k, v in self.smartctl[KEY_NVME].items()} + elif KEY_SMART in self.smartctl: + for a in self.smartctl[KEY_SMART].get('table', {}): + try: + _id = int(a.get('id', -1)) + except ValueError: + # Ignoring invalid attribute + continue + _name = str(a.get('name', 'UNKNOWN')).replace('_', ' ').title() + _raw = int(a.get('raw', {}).get('value', -1)) + _raw_str = a.get('raw', {}).get('string', 'UNKNOWN') + + # Fix power-on time + _r = re.match(r'^(\d+)[Hh].*', _raw_str) + if _id == 9 and _r: + _raw = int(_r.group(1)) + + # Add to dict + self.smart_attributes[_id] = { + 'name': _name, 'raw': _raw, 'raw_str': _raw_str} + + # Self-test data + self.smart_self_test = {} + for k in ['polling_minutes', 'status']: + self.smart_self_test[k] = self.smartctl.get( + 'ata_smart_data', {}).get( + 'self_test', {}).get( + k, {}) + + def safety_check(self, silent=False): + """Run safety checks and disable tests if necessary.""" + if self.nvme_attributes or self.smart_attributes: + self.check_attributes(silent) + + # Check if a self-test is currently running + if 'remaining_percent' in self.smart_self_test.get('status', ''): + _msg = 'SMART self-test in progress, all tests disabled' + + # Ask to abort + if not silent: + print_warning('WARNING: {}'.format(_msg)) + print_standard(' ') + if ask('Abort HW Diagnostics?'): + exit_script() + + # Add warning to report + if 'NVMe / SMART' in self.tests: + self.tests['NVMe / SMART'].report = self.generate_attribute_report() + self.tests['NVMe / SMART'].report.append( + '{YELLOW}WARNING: {msg}{CLEAR}'.format(msg=_msg, **COLORS)) + + # Disable all tests for this disk + for t in self.tests.keys(): + self.disable_test(t, 'Denied') + else: + # No NVMe/SMART details + self.disable_test('NVMe / SMART', 'N/A') + if silent: + self.disk_ok = HW_OVERRIDES_FORCED + else: + print_info('Device ({})'.format(self.name)) + print_standard(' {}'.format(self.description)) + print_warning(' No NVMe or SMART data available') + self.disk_ok = HW_OVERRIDES_FORCED or ask( + 'Run tests on this device anyway?') + print_standard(' ') + + if not self.disk_ok: + if 'NVMe / SMART' in self.tests: + # NOTE: This will not overwrite the existing status if set + self.disable_test('NVMe / SMART', 'FAIL') + if not self.tests['NVMe / SMART'].report: + self.tests['NVMe / SMART'].report = self.generate_attribute_report() + for t in ['badblocks', 'I/O Benchmark']: + self.disable_test(t, 'Denied') + + +class State(): + """Object to track device objects and overall state.""" + def __init__(self): + self.cpu = None + self.disks = [] + self.ost = osTicket(TESTS_CPU, TESTS_DISK) + self.panes = {} + self.quick_mode = False + self.tests = OrderedDict({ + 'Prime95': { + 'Enabled': False, + 'Function': run_mprime_test, + 'Objects': [], + }, + 'NVMe / SMART': { + 'Enabled': False, + 'Function': run_nvme_smart_tests, + 'Objects': [], + }, + 'badblocks': { + 'Enabled': False, + 'Function': run_badblocks_test, + 'Objects': [], + }, + 'I/O Benchmark': { + 'Enabled': False, + 'Function': run_io_benchmark, + 'Objects': [], + }, + }) + self.ticket_id = None + + def init(self): + """Remove test objects, set log, and add devices.""" + self.disks = [] + for k, v in self.tests.items(): + v['Objects'] = [] + + # Update LogDir + if not self.quick_mode: + global_vars['LogDir'] = '{}/Logs/{}_{}'.format( + global_vars['Env']['HOME'], + get_ticket_number(), + time.strftime('%Y-%m-%d_%H%M_%z')) + os.makedirs(global_vars['LogDir'], exist_ok=True) + global_vars['LogFile'] = '{}/Hardware Diagnostics.log'.format( + global_vars['LogDir']) + self.progress_out = '{}/progress.out'.format(global_vars['LogDir']) + + # Add CPU + self.cpu = CpuObj() + + # Add block devices + cmd = ['lsblk', '--json', '--nodeps', '--paths'] + result = run_program(cmd, check=False) + json_data = json.loads(result.stdout.decode()) + for disk in json_data['blockdevices']: + skip_disk = False + disk_obj = DiskObj(disk['name']) + + # Skip loopback devices, optical devices, etc + if disk_obj.lsblk['type'] != 'disk': + skip_disk = True + + # Skip WK disks + wk_label_regex = r'{}_(LINUX|UFD)'.format(KIT_NAME_SHORT) + for label in disk_obj.labels: + if re.search(wk_label_regex, label, re.IGNORECASE): + skip_disk = True + + # Add disk + if not skip_disk: + self.disks.append(disk_obj) + + # Start tmux thread + self.tmux_layout = TMUX_LAYOUT.copy() + start_thread(fix_tmux_panes_loop, args=[self]) + + +class TestObj(): + """Object to track test data.""" + def __init__(self, dev, label=None, info_label=False): + self.aborted = False + self.dev = dev + self.label = label + self.info_label = info_label + self.disabled = False + self.failed = False + self.passed = False + self.report = [] + self.started = False + self.status = '' + self.update_status() + + def update_status(self, new_status=None): + """Update status strings.""" + if self.disabled or REGEX_ERROR_STATUS.search(self.status): + # Don't update error statuses if test is enabled + return + if new_status: + self.status = build_status_string( + self.label, new_status, self.info_label) + elif not self.status: + self.status = build_status_string( + self.label, 'Pending', self.info_label) + elif self.started and 'Pending' in self.status: + self.status = build_status_string( + self.label, 'Working', self.info_label) + + +# Functions +def build_outer_panes(state): + """Build top and side panes.""" + clear_screen() + + # Top + state.panes['Top'] = tmux_split_window( + behind=True, lines=2, vertical=True, + text=TOP_PANE_TEXT) + + # Started + state.panes['Started'] = tmux_split_window( + lines=SIDE_PANE_WIDTH, target_pane=state.panes['Top'], + text='{BLUE}Started{CLEAR}\n{s}'.format( + s=time.strftime("%Y-%m-%d %H:%M %Z"), + **COLORS)) + + # Progress + state.panes['Progress'] = tmux_split_window( + lines=SIDE_PANE_WIDTH, + watch=state.progress_out) + + +def build_status_string(label, status, info_label=False): + """Build status string with appropriate colors.""" + status_color = COLORS['CLEAR'] + for k, v in STATUSES.items(): + if status in v: + status_color = COLORS[k] + + return '{l_c}{l}{CLEAR}{s_c}{s:>{s_w}}{CLEAR}'.format( + l_c=COLORS['BLUE'] if info_label else '', + l=label, + s_c=status_color, + s=status, + s_w=SIDE_PANE_WIDTH-len(label), + **COLORS) + + +def fix_tmux_panes_loop(state): + while True: + try: + fix_tmux_panes(state) + sleep(1) + except RuntimeError: + # Assuming layout definitions changes mid-run, ignoring + pass + + +def fix_tmux_panes(state): + """Fix pane sizes if the window has been resized.""" + needs_fixed = False + + # Bail? + if not state.panes: + return + + # Check layout + for k, v in state.tmux_layout.items(): + if not v.get('Check'): + # Not concerned with the size of this pane + continue + # Get target + target = None + if k != 'Current': + if k not in state.panes: + # Skip missing panes + continue + else: + target = state.panes[k] + + # Check pane size + x, y = tmux_get_pane_size(pane_id=target) + if v.get('x', False) and v['x'] != x: + needs_fixed = True + if v.get('y', False) and v['y'] != y: + needs_fixed = True + + # Bail? + if not needs_fixed: + return + + # Update layout + for k, v in state.tmux_layout.items(): + # Get target + target = None + if k != 'Current': + if k not in state.panes: + # Skip missing panes + continue + else: + target = state.panes[k] + + # Resize pane + tmux_resize_pane(pane_id=target, **v) + + +def generate_horizontal_graph(rates, oneline=False): + """Generate horizontal graph from rates, returns list.""" + graph = ['', '', '', ''] + for r in rates: + step = get_graph_step(r, scale=32) + if oneline: + step = get_graph_step(r, scale=8) + + # Set color + r_color = COLORS['CLEAR'] + if r < IO_VARS['Threshold Graph Fail']: + r_color = COLORS['RED'] + elif r < IO_VARS['Threshold Graph Warn']: + r_color = COLORS['YELLOW'] + elif r > IO_VARS['Threshold Graph Great']: + r_color = COLORS['GREEN'] + + # Build graph + full_block = '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][-1]) + if step >= 24: + graph[0] += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step-24]) + graph[1] += full_block + graph[2] += full_block + graph[3] += full_block + elif step >= 16: + graph[0] += ' ' + graph[1] += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step-16]) + graph[2] += full_block + graph[3] += full_block + elif step >= 8: + graph[0] += ' ' + graph[1] += ' ' + graph[2] += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step-8]) + graph[3] += full_block + else: + graph[0] += ' ' + graph[1] += ' ' + graph[2] += ' ' + graph[3] += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step]) + graph = [line+COLORS['CLEAR'] for line in graph] + if oneline: + return graph[:-1] + else: + return graph + + +def get_graph_step(rate, scale=16): + """Get graph step based on rate and scale, returns int.""" + m_rate = rate / (1024**2) + step = 0 + scale_name = 'Scale {}'.format(scale) + for x in range(scale-1, -1, -1): + # Iterate over scale backwards + if m_rate >= IO_VARS[scale_name][x]: + step = x + break + return step + + +def get_read_rate(s): + """Get read rate in bytes/s from dd progress output.""" + real_rate = None + if re.search(r'[KMGT]B/s', s): + human_rate = re.sub(r'^.*\s+(\d+\.?\d*)\s+(.B)/s\s*$', r'\1 \2', s) + real_rate = convert_to_bytes(human_rate) + return real_rate + + +def menu_diags(state, args): + """Main menu to select and run HW tests.""" + args = [a.lower() for a in args] + checkmark = '*' + if 'DISPLAY' in global_vars['Env']: + checkmark = '✓' + title = '{}\nMain Menu'.format(TOP_PANE_TEXT) + # NOTE: Changing the order of main_options will break everything + main_options = [ + {'Base Name': 'Full Diagnostic', 'Enabled': False}, + {'Base Name': 'Disk Diagnostic', 'Enabled': False}, + {'Base Name': 'Disk Diagnostic (Quick)', 'Enabled': False}, + {'Base Name': 'osTicket Integration', 'Enabled': True}, + {'Base Name': 'Prime95', 'Enabled': False, 'CRLF': True}, + {'Base Name': 'NVMe / SMART', 'Enabled': False}, + {'Base Name': 'badblocks', 'Enabled': False}, + {'Base Name': 'I/O Benchmark', 'Enabled': False}, + ] + actions = [ + {'Letter': 'A', 'Name': 'Audio Test'}, + {'Letter': 'K', 'Name': 'Keyboard Test'}, + {'Letter': 'N', 'Name': 'Network Test'}, + {'Letter': 'S', 'Name': 'Start', 'CRLF': True}, + {'Letter': 'Q', 'Name': 'Quit'}, + ] + secret_actions = ['M', 'T'] + + # Set initial selections + update_main_options(state, '1', main_options) + + # CLI mode check + if '--cli' in args or 'DISPLAY' not in global_vars['Env']: + actions.append({'Letter': 'R', 'Name': 'Reboot'}) + actions.append({'Letter': 'P', 'Name': 'Power Off'}) + + # Skip menu if running quick check + if '--quick' in args: + update_main_options(state, '3', main_options) + state.quick_mode = True + run_hw_tests(state) + return True + + while True: + # Set quick mode as necessary + if main_options[2]['Enabled'] and main_options[5]['Enabled']: + # Check if only Disk Diags (Quick) and NVMe/SMART are enabled + # If so, verify no other tests are enabled and set quick_mode + state.quick_mode = True + for opt in main_options[4:5] + main_options[6:]: + state.quick_mode &= not opt['Enabled'] + else: + state.quick_mode = False + + # Deselect presets + slice_end = 3 + if state.quick_mode: + slice_end = 2 + for opt in main_options[:slice_end]: + opt['Enabled'] = False + + # Verify preset selections + num_tests_selected = 0 + for opt in main_options[4:]: + if opt['Enabled']: + num_tests_selected += 1 + if num_tests_selected == 4: + # Full + main_options[0]['Enabled'] = True + elif num_tests_selected == 3 and not main_options[4]['Enabled']: + # Disk + main_options[1]['Enabled'] = True + + # Update checkboxes + for opt in main_options: + _nvme_smart = opt['Base Name'] == 'NVMe / SMART' + opt['Name'] = '[{}] {} {}'.format( + checkmark if opt['Enabled'] else ' ', + opt['Base Name'], + QUICK_LABEL if state.quick_mode and _nvme_smart else '') + + # Show menu + selection = menu_select( + title=title, + main_entries=main_options, + action_entries=actions, + secret_actions=secret_actions, + spacer='───────────────────────────────') + + if selection.isnumeric(): + update_main_options(state, selection, main_options) + elif selection == 'A': + run_audio_test() + elif selection == 'K': + run_keyboard_test() + elif selection == 'N': + run_network_test() + elif selection == 'M': + secret_screensaver('matrix') + elif selection == 'T': + # Tubes is close to pipes right? + secret_screensaver('pipes') + elif selection == 'R': + run_program(['/usr/local/bin/wk-power-command', 'reboot']) + elif selection == 'P': + run_program(['/usr/local/bin/wk-power-command', 'poweroff']) + elif selection == 'Q': + break + elif selection == 'S': + run_hw_tests(state) + + +def run_audio_test(): + """Run audio test.""" + clear_screen() + run_program(['hw-diags-audio'], check=False, pipe=False) + pause('Press Enter to return to main menu... ') + + +def run_badblocks_test(state, test): + """Run a read-only surface scan with badblocks.""" + # Bail early + if test.disabled: + return + + def _save_badblocks_output(read_all=False, timeout=0.1): + """Get badblocks output and append to both file and var.""" + _output = '' + while _output is not None: + _output = test.badblocks_nbsr.read(0.1) + if _output is not None: + test.badblocks_stderr += _output.decode() + with open(test.badblocks_out, 'a') as f: + f.write(_output.decode()) + if not read_all: + break + + # Prep + print_log('Starting badblocks test for {}'.format(test.dev.path)) + test.started = True + test.update_status() + update_progress_pane(state) + + # Update tmux layout + tmux_update_pane( + state.panes['Top'], + text='{}\n{}'.format( + TOP_PANE_TEXT, test.dev.description)) + + # Create monitor pane + test.badblocks_out = '{}/badblocks_{}.out'.format( + global_vars['LogDir'], test.dev.name) + state.panes['badblocks'] = tmux_split_window( + lines=5, vertical=True, watch=test.badblocks_out, watch_cmd='tail') + + # Show disk details + clear_screen() + show_report(test.dev.generate_attribute_report()) + print_standard(' ') + + # Start badblocks + print_standard('Running badblocks test...') + test.badblocks_proc = popen_program( + ['sudo', 'badblocks', '-sv', '-e', '1', test.dev.path], + pipe=True, bufsize=1) + test.badblocks_nbsr = NonBlockingStreamReader(test.badblocks_proc.stderr) + test.badblocks_stderr = '' + + # Update progress loop + try: + while test.badblocks_proc.poll() is None: + _save_badblocks_output() + except KeyboardInterrupt: + run_program(['killall', 'badblocks'], check=False) + test.aborted = True + + # Save remaining badblocks output + _save_badblocks_output(read_all=True) + + # Check result and build report + test.report.append('{BLUE}badblocks{CLEAR}'.format(**COLORS)) + for line in test.badblocks_stderr.splitlines(): + line = line.strip() + if not line or re.search(r'^Checking', line, re.IGNORECASE): + # Skip empty and progress lines + continue + if re.search(r'^Pass completed.*0.*0/0/0', line, re.IGNORECASE): + test.report.append(' {}'.format(line)) + if not test.aborted: + test.passed = True + else: + test.report.append(' {YELLOW}{line}{CLEAR}'.format( + line=line, **COLORS)) + test.failed = True + if test.aborted: + test.report.append(' {YELLOW}Aborted{CLEAR}'.format(**COLORS)) + test.update_status('Aborted') + raise GenericAbort('Aborted') + + # Disable other drive tests if necessary + if not test.passed: + test.dev.disable_test('I/O Benchmark', 'Denied') + + # Update status + if test.failed: + test.update_status('FAIL') + elif test.passed: + test.update_status('PASS') + else: + test.update_status('Unknown') + + # Done + update_progress_pane(state) + + # Cleanup + tmux_kill_pane(state.panes.pop('badblocks', None)) + + +def run_hw_tests(state): + """Run enabled hardware tests.""" + print_standard('Scanning devices...') + state.init() + tests_enabled = False + + # Disable osTicket Integration if in quick mode + state.ost.disabled |= state.quick_mode + + # Build Panes + update_progress_pane(state) + build_outer_panes(state) + + # Show selected tests and create TestObj()s + print_info('Selected Tests:') + for k, v in state.tests.items(): + print_standard(' {:<15} {}{}{} {}'.format( + k, + COLORS['GREEN'] if v['Enabled'] else COLORS['RED'], + 'Enabled' if v['Enabled'] else 'Disabled', + COLORS['CLEAR'], + QUICK_LABEL if state.quick_mode and 'NVMe' in k else '')) + if v['Enabled']: + tests_enabled = True + + # Create TestObj and track under both CpuObj/DiskObj and State + if k in TESTS_CPU: + test_obj = TestObj( + dev=state.cpu, label='Prime95', info_label=True) + state.cpu.tests[k] = test_obj + v['Objects'].append(test_obj) + elif k in TESTS_DISK: + for disk in state.disks: + test_obj = TestObj(dev=disk, label=disk.name) + disk.tests[k] = test_obj + v['Objects'].append(test_obj) + print_standard('') + + # Bail if no tests selected + if not tests_enabled: + tmux_kill_pane(*state.panes.values()) + return + + # Get ticket_number + if not state.ost.disabled: + state.ticket_id = state.ost.get_ticket_number() + + # Run disk safety checks (if necessary) + _disk_tests_enabled = False + for k in TESTS_DISK: + _disk_tests_enabled |= state.tests[k]['Enabled'] + if _disk_tests_enabled: + for disk in state.disks: + disk.safety_check(silent=state.quick_mode) + + # Run tests + ## Because state.tests is an OrderedDict and the disks were added + ## in order, the tests will be run in order. + try: + for k, v in state.tests.items(): + if v['Enabled']: + f = v['Function'] + for test_obj in v['Objects']: + f(state, test_obj) + if not v['Objects']: + # No devices available + v['Objects'].append(TestObj(dev=None, label='')) + v['Objects'][-1].update_status('N/A') + if k == TESTS_CPU[-1]: + # Last CPU test run, post CPU results + state.ost.post_device_results(state.cpu, state.ticket_id) + except GenericAbort: + # Cleanup + tmux_kill_pane(*state.panes.values()) + state.panes.clear() + state.tmux_layout.pop('Current', None) + + # Rebuild panes + update_progress_pane(state) + build_outer_panes(state) + + # Mark unfinished tests as aborted + for k, v in state.tests.items(): + if v['Enabled']: + for test_obj in v['Objects']: + if re.search(r'(Pending|Working)', test_obj.status): + test_obj.update_status('Aborted') + + # Update side pane + update_progress_pane(state) + + # Show results + show_results(state) + + # Post disk results + if not state.ost.disabled: + print_standard('Posting results to osTicket...') + for disk in state.disks: + state.ost.post_device_results(disk, state.ticket_id) + + # Check if disk checkbox needs updating + all_disks_passed = True + disk_failures = False + for disk in state.disks: + if disk.checkbox is None: + # Aborted/Unknown/etc + all_disks_passed = False + else: + all_disks_passed &= disk.checkbox + disk_failures |= not disk.checkbox + + # Update checkbox if necessary + if disk_failures: + state.ost.set_disk_failed(state.ticket_id) + elif all_disks_passed: + state.ost.set_disk_passed(state.ticket_id) + + # Spacer + print_standard(' ') + + # Check for osTicket errors + if state.ost.errors: + print_warning('Errors encountered posting results to osTicket.') + print_standard(' ') + + # Done + sleep(1) + if state.quick_mode: + pause('Press Enter to exit... ') + else: pause('Press Enter to return to main menu... ') - run_program('tmux kill-pane -a'.split()) + + # Cleanup + state.ost.disconnect(full=True) + tmux_kill_pane(*state.panes.values()) + state.panes.clear() + + +def run_io_benchmark(state, test): + """Run a read-only I/O benchmark using dd.""" + # Bail early + if test.disabled: + return + + # Prep + print_log('Starting I/O benchmark test for {}'.format(test.dev.path)) + test.started = True + test.update_status() + update_progress_pane(state) + + # Update tmux layout + tmux_update_pane( + state.panes['Top'], + text='{}\n{}'.format( + TOP_PANE_TEXT, test.dev.description)) + state.tmux_layout['Current'] = {'y': 15, 'Check': True} + + # Create monitor pane + test.io_benchmark_out = '{}/io_benchmark_{}.out'.format( + global_vars['LogDir'], test.dev.name) + state.panes['io_benchmark'] = tmux_split_window( + percent=75, vertical=True, + watch=test.io_benchmark_out, watch_cmd='tail') + tmux_resize_pane(y=15) + + # Show disk details + clear_screen() + show_report(test.dev.generate_attribute_report()) + print_standard(' ') + + # Start I/O Benchmark + print_standard('Running I/O benchmark test...') + try: + test.merged_rates = [] + test.read_rates = [] + test.dev.calc_io_dd_values() + + # Run dd read tests + offset = 0 + for i in range(test.dev.dd_chunks): + # Build cmd + i += 1 + skip = test.dev.dd_skip_count + if test.dev.dd_skip_extra and i % test.dev.dd_skip_extra == 0: + skip += 1 + cmd = [ + 'sudo', 'dd', + 'bs={}'.format(IO_VARS['Block Size']), + 'skip={}'.format(offset+skip), + 'count={}'.format(test.dev.dd_chunk_blocks), + 'iflag=direct', + 'if={}'.format(test.dev.path), + 'of=/dev/null'] + + # Run cmd and get read rate + result = run_program(cmd) + result_str = result.stderr.decode().replace('\n', '') + cur_rate = get_read_rate(result_str) + + # Add rate to lists + test.read_rates.append(cur_rate) + + # Show progress + if i % IO_VARS['Progress Refresh Rate'] == 0: + update_io_progress( + percent=(i/test.dev.dd_chunks)*100, + rate=cur_rate, + progress_file=test.io_benchmark_out) + + # Update offset + offset += test.dev.dd_chunk_blocks + skip + + except DeviceTooSmallError: + # Device too small, skipping test + test.update_status('N/A') + except KeyboardInterrupt: + test.aborted = True + except (subprocess.CalledProcessError, TypeError, ValueError): + # Something went wrong, results unknown + test.update_status('ERROR') + + # Check result and build report + test.report.append('{BLUE}I/O Benchmark{CLEAR}'.format(**COLORS)) + if test.aborted: + test.report.append(' {YELLOW}Aborted{CLEAR}'.format(**COLORS)) + raise GenericAbort('Aborted') + elif not test.read_rates: + if 'ERROR' in test.status: + test.report.append(' {RED}Unknown error{CLEAR}'.format(**COLORS)) + elif 'N/A' in test.status: + # Device too small + test.report.append(' {YELLOW}Disk too small to test{CLEAR}'.format( + **COLORS)) + else: + # Merge rates for horizontal graph + offset = 0 + width = int(test.dev.dd_chunks / IO_VARS['Graph Horizontal Width']) + for i in range(IO_VARS['Graph Horizontal Width']): + test.merged_rates.append( + sum(test.read_rates[offset:offset+width])/width) + offset += width + + # Add horizontal graph to report + for line in generate_horizontal_graph(test.merged_rates): + if not re.match(r'^\s+$', strip_colors(line)): + test.report.append(line) + + # Add read speeds to report + avg_read = sum(test.read_rates) / len(test.read_rates) + min_read = min(test.read_rates) + max_read = max(test.read_rates) + avg_min_max = 'Read speeds avg: {:3.1f}'.format(avg_read/(1024**2)) + avg_min_max += ' min: {:3.1f}'.format(min_read/(1024**2)) + avg_min_max += ' max: {:3.1f}'.format(max_read/(1024**2)) + test.report.append(avg_min_max) + + # Compare read speeds to thresholds + if test.dev.lsblk['rota']: + # Use HDD scale + thresh_min = IO_VARS['Threshold HDD Min'] + thresh_high_avg = IO_VARS['Threshold HDD High Avg'] + thresh_low_avg = IO_VARS['Threshold HDD Low Avg'] + else: + # Use SSD scale + thresh_min = IO_VARS['Threshold SSD Min'] + thresh_high_avg = IO_VARS['Threshold SSD High Avg'] + thresh_low_avg = IO_VARS['Threshold SSD Low Avg'] + if min_read <= thresh_min and avg_read <= thresh_high_avg: + test.failed = True + elif avg_read <= thresh_low_avg: + test.failed = True + else: + test.passed = True + + # Update status + if test.failed: + test.update_status('FAIL') + elif test.passed: + test.update_status('PASS') + elif not 'N/A' in test.status: + test.update_status('Unknown') + + # Done + update_progress_pane(state) + + # Cleanup + state.tmux_layout.pop('Current', None) + tmux_kill_pane(state.panes.pop('io_benchmark', None)) + + +def run_keyboard_test(): + """Run keyboard test.""" + clear_screen() + run_program(['xev', '-event', 'keyboard'], check=False, pipe=False) + + +def run_mprime_test(state, test): + """Test CPU with Prime95 and track temps.""" + # Bail early + if test.disabled: + return + + # Prep + print_log('Starting Prime95 test') + test.started = True + test.update_status() + update_progress_pane(state) + test.sensor_data = get_sensor_data() + + # Update tmux layout + tmux_update_pane( + state.panes['Top'], + text='{}\n{}'.format(TOP_PANE_TEXT, test.dev.name)) + + # Start live sensor monitor + test.sensors_out = '{}/sensors.out'.format(global_vars['TmpDir']) + with open(test.sensors_out, 'w') as f: + f.write(' ') + f.flush() + sleep(0.5) + test.monitor_proc = popen_program( + ['hw-sensors-monitor', test.sensors_out], + pipe=True) + + # Create monitor and worker panes + state.panes['Prime95'] = tmux_split_window( + lines=10, vertical=True, text=' ') + state.panes['Temps'] = tmux_split_window( + behind=True, percent=80, vertical=True, watch=test.sensors_out) + tmux_resize_pane(global_vars['Env']['TMUX_PANE'], y=3) + state.tmux_layout['Current'] = {'y': 3, 'Check': True} + + # Get idle temps + clear_screen() + try_and_print( + message='Getting idle temps...', indent=0, + function=save_average_temp, cs='Done', + sensor_data=test.sensor_data, temp_label='Idle', + seconds=5) + + # Stress CPU + print_log('Starting Prime95') + test.abort_msg = 'If running too hot, press CTRL+c to abort the test' + run_program(['apple-fans', 'max']) + tmux_update_pane( + state.panes['Prime95'], + command=['hw-diags-prime95', global_vars['TmpDir']], + working_dir=global_vars['TmpDir']) + time_limit = int(MPRIME_LIMIT) * 60 + try: + for i in range(time_limit): + clear_screen() + sec_left = (time_limit - i) % 60 + min_left = int( (time_limit - i) / 60) + _status_str = 'Running Prime95 (' + if min_left > 0: + _status_str += '{} minute{}, '.format( + min_left, + 's' if min_left != 1 else '') + _status_str += '{} second{} left)'.format( + sec_left, + 's' if sec_left != 1 else '') + # Not using print wrappers to avoid flooding the log + print(_status_str) + print('{YELLOW}{msg}{CLEAR}'.format(msg=test.abort_msg, **COLORS)) + update_sensor_data(test.sensor_data) + + # Wait + sleep(1) + except KeyboardInterrupt: + # Catch CTRL+C + test.aborted = True + test.update_status('Aborted') + print_warning('\nAborted.') + update_progress_pane(state) + + # Restart live monitor + test.monitor_proc = popen_program( + ['hw-sensors-monitor', test.sensors_out], + pipe=True) + + # Stop Prime95 (twice for good measure) + run_program(['killall', '-s', 'INT', 'mprime'], check=False) + sleep(1) + tmux_kill_pane(state.panes.pop('Prime95', None)) + + # Get cooldown temp + run_program(['apple-fans', 'auto']) + clear_screen() + try_and_print( + message='Letting CPU cooldown for bit...', indent=0, + function=sleep, cs='Done', seconds=10) + try_and_print( + message='Getting cooldown temps...', indent=0, + function=save_average_temp, cs='Done', + sensor_data=test.sensor_data, temp_label='Cooldown', + seconds=5) + + # Move logs to Ticket folder + for item in os.scandir(global_vars['TmpDir']): + try: + shutil.move(item.path, global_vars['LogDir']) + except Exception: + print_error('ERROR: Failed to move "{}" to "{}"'.format( + item.path, + global_vars['LogDir'])) + + # Check results and build report + test.report.append('{BLUE}Prime95{CLEAR}'.format(**COLORS)) + test.logs = {} + for log in ['results.txt', 'prime.log']: + lines = [] + log_path = '{}/{}'.format(global_vars['LogDir'], log) + + # Read and save log + try: + with open(log_path, 'r') as f: + lines = f.read().splitlines() + test.logs[log] = lines + except FileNotFoundError: + # Ignore since files may be missing for slower CPUs + pass + + # results.txt (NS check) + if log == 'results.txt': + for line in lines: + line = line.strip() + if re.search(r'(error|fail)', line, re.IGNORECASE): + test.failed = True + test.update_status('FAIL') + test.report.append( + ' {YELLOW}{line}{CLEAR}'.format(line=line, **COLORS)) + + # prime.log (CS check) + if log == 'prime.log': + _tmp = {'Pass': {}, 'Warn': {}} + for line in lines: + line = line.strip() + _r = re.search( + r'(completed.*(\d+) errors, (\d+) warnings)', + line, + re.IGNORECASE) + if _r: + if int(_r.group(2)) + int(_r.group(3)) > 0: + # Encountered errors and/or warnings + _tmp['Warn'][_r.group(1)] = None + else: + # No errors + _tmp['Pass'][_r.group(1)] = None + if len(_tmp['Warn']) > 0: + # NS + test.failed = True + test.passed = False + test.update_status('FAIL') + elif len(_tmp['Pass']) > 0 and not test.aborted: + test.passed = True + test.update_status('PASS') + for line in sorted(_tmp['Pass'].keys()): + test.report.append(' {}'.format(line)) + for line in sorted(_tmp['Warn'].keys()): + test.report.append( + ' {YELLOW}{line}{CLEAR}'.format(line=line, **COLORS)) + + # Unknown result + if not (test.aborted or test.failed or test.passed): + test.report.append(' {YELLOW}Unknown result{CLEAR}'.format(**COLORS)) + test.update_status('Unknown') + + # Add temps to report + test.report.append('{BLUE}Temps{CLEAR}'.format(**COLORS)) + for line in generate_sensor_report( + test.sensor_data, 'Idle', 'Max', 'Cooldown', core_only=True): + test.report.append(' {}'.format(line)) + + # Done + update_progress_pane(state) + + # Cleanup + state.tmux_layout.pop('Current', None) + tmux_kill_pane( + state.panes.pop('Prime95', None), + state.panes.pop('Temps', None), + ) + test.monitor_proc.kill() + + +def run_network_test(): + """Run network test.""" + clear_screen() + run_program(['hw-diags-network'], check=False, pipe=False) + pause('Press Enter to return to main menu... ') + + +def run_nvme_smart_tests(state, test): + """Run NVMe or SMART test for test.dev.""" + # Bail early + if test.disabled: + return + + # Prep + print_log('Starting NVMe/SMART test for {}'.format(test.dev.path)) + _include_short_test = False + test.started = True + test.update_status() + update_progress_pane(state) + + # Update tmux layout + tmux_update_pane( + state.panes['Top'], + text='{}\n{}'.format( + TOP_PANE_TEXT, test.dev.description)) + + # NVMe + if test.dev.nvme_attributes: + # NOTE: Pass/Fail is just the attribute check + if test.dev.disk_ok: + test.passed = True + test.update_status('PASS') + else: + # NOTE: Other test(s) should've been disabled by DiskObj.safety_check() + test.failed = True + test.update_status('FAIL') + + # SMART + elif test.dev.smart_attributes: + # NOTE: Pass/Fail based on both attributes and SMART short self-test + if not (test.dev.disk_ok or 'OVERRIDE' in test.status): + test.failed = True + test.update_status('FAIL') + elif state.quick_mode: + if test.dev.disk_ok: + test.passed = True + test.update_status('PASS') + else: + test.failed = True + test.update_status('FAIL') + else: + # Prep + test.timeout = test.dev.smart_self_test['polling_minutes'].get( + 'short', 5) + test.timeout = int(test.timeout) + 5 + _include_short_test = True + _self_test_started = False + _self_test_finished = False + + # Create monitor pane + test.smart_out = '{}/smart_{}.out'.format( + global_vars['LogDir'], test.dev.name) + with open(test.smart_out, 'w') as f: + f.write('SMART self-test status:\n Starting...') + state.panes['SMART'] = tmux_split_window( + lines=3, vertical=True, watch=test.smart_out) + + # Show attributes + clear_screen() + show_report(test.dev.generate_attribute_report()) + print_standard(' ') + + # Start short test + print_standard('Running self-test...') + cmd = ['sudo', 'smartctl', '--test=short', test.dev.path] + run_program(cmd, check=False) + + # Monitor progress + try: + for i in range(int(test.timeout*60/5)): + sleep(5) + + # Update SMART data + test.dev.get_smart_details() + + if _self_test_started: + # Update progress file + with open(test.smart_out, 'w') as f: + f.write('SMART self-test status:\n {}'.format( + test.dev.smart_self_test['status'].get( + 'string', 'UNKNOWN').capitalize())) + + # Check if test has finished + if 'remaining_percent' not in test.dev.smart_self_test['status']: + _self_test_finished = True + break + + else: + # Check if test has started + if 'remaining_percent' in test.dev.smart_self_test['status']: + _self_test_started = True + + except KeyboardInterrupt: + test.aborted = True + test.report = test.dev.generate_attribute_report() + test.report.append('{BLUE}SMART Short self-test{CLEAR}'.format( + **COLORS)) + test.report.append(' {YELLOW}Aborted{CLEAR}'.format(**COLORS)) + test.update_status('Aborted') + raise GenericAbort('Aborted') + + # Check if timed out + if _self_test_finished: + if test.dev.smart_self_test['status'].get('passed', False): + if 'OVERRIDE' not in test.status: + test.passed = True + test.update_status('PASS') + else: + test.failed = True + test.update_status('FAIL') + else: + test.dev.smart_timeout = True + test.update_status('TimedOut') + + # Disable other drive tests if necessary + if test.failed: + for t in ['badblocks', 'I/O Benchmark']: + test.dev.disable_test(t, 'Denied') + + # Cleanup + tmux_kill_pane(state.panes.pop('SMART', None)) + + # Save report + test.report = test.dev.generate_attribute_report( + short_test=_include_short_test) + + # Done + update_progress_pane(state) + + +def secret_screensaver(screensaver=None): + """Show screensaver.""" + if screensaver == 'matrix': + cmd = 'cmatrix -abs'.split() + elif screensaver == 'pipes': + cmd = 'pipes -t 0 -t 1 -t 2 -t 3 -p 5 -R -r 4000'.split() + else: + raise Exception('Invalid screensaver') + run_program(cmd, check=False, pipe=False) + + +def show_report(report, log_report=False): + """Show report on screen and optionally save to log w/out color.""" + for line in report: + print(line) + if log_report: + print_log(strip_colors(line)) + + +def show_results(state): + """Show results for all tests.""" + clear_screen() + tmux_update_pane( + state.panes['Top'], + text='{}\nResults'.format(TOP_PANE_TEXT)) + + # CPU tests + _enabled = False + for k in TESTS_CPU: + _enabled |= state.tests[k]['Enabled'] + if _enabled: + print_success('CPU:'.format(k)) + show_report(state.cpu.generate_cpu_report(), log_report=True) + print_standard(' ') + + # Disk tests + _enabled = False + for k in TESTS_DISK: + _enabled |= state.tests[k]['Enabled'] + if _enabled: + print_success('Disk{}:'.format( + '' if len(state.disks) == 1 else 's')) + for disk in state.disks: + show_report(disk.generate_disk_report(), log_report=True) + print_standard(' ') + if not state.disks: + print_warning('No devices') + print_standard(' ') + + # Update progress + update_progress_pane(state) + + +def update_main_options(state, selection, main_options): + """Update menu and state based on selection.""" + index = int(selection) - 1 + main_options[index]['Enabled'] = not main_options[index]['Enabled'] + + # Handle presets + if index == 0: + # Full + if main_options[index]['Enabled']: + for opt in main_options[1:3]: + opt['Enabled'] = False + for opt in main_options[4:]: + opt['Enabled'] = True + else: + for opt in main_options[4:]: + opt['Enabled'] = False + elif index == 1: + # Disk + if main_options[index]['Enabled']: + main_options[0]['Enabled'] = False + main_options[2]['Enabled'] = False + main_options[4]['Enabled'] = False + for opt in main_options[5:]: + opt['Enabled'] = True + else: + for opt in main_options[5:]: + opt['Enabled'] = False + elif index == 2: + # Disk (Quick) + if main_options[index]['Enabled']: + for opt in main_options[:2] + main_options[4:]: + opt['Enabled'] = False + main_options[5]['Enabled'] = True + else: + main_options[5]['Enabled'] = False + + # Update state + state.ost.disabled = not main_options[3]['Enabled'] + for opt in main_options[4:]: + state.tests[opt['Base Name']]['Enabled'] = opt['Enabled'] + + # Done + return main_options + def update_io_progress(percent, rate, progress_file): - """Update I/O progress file.""" - bar_color = COLORS['CLEAR'] - rate_color = COLORS['CLEAR'] - step = get_graph_step(rate, scale=32) - if rate < IO_VARS['Threshold Graph Fail']: - bar_color = COLORS['RED'] - rate_color = COLORS['YELLOW'] - elif rate < IO_VARS['Threshold Graph Warn']: - bar_color = COLORS['YELLOW'] - rate_color = COLORS['YELLOW'] - elif rate > IO_VARS['Threshold Graph Great']: - bar_color = COLORS['GREEN'] - rate_color = COLORS['GREEN'] - line = ' {p:5.1f}% {b_color}{b:<4} {r_color}{r:6.1f} Mb/s{c}\n'.format( - p=percent, - b_color=bar_color, - b=IO_VARS['Graph Vertical'][step], - r_color=rate_color, - r=rate/(1024**2), - c=COLORS['CLEAR']) - with open(progress_file, 'a') as f: - f.write(line) + """Update I/O progress file.""" + bar_color = COLORS['CLEAR'] + rate_color = COLORS['CLEAR'] + step = get_graph_step(rate, scale=32) + if rate < IO_VARS['Threshold Graph Fail']: + bar_color = COLORS['RED'] + rate_color = COLORS['YELLOW'] + elif rate < IO_VARS['Threshold Graph Warn']: + bar_color = COLORS['YELLOW'] + rate_color = COLORS['YELLOW'] + elif rate > IO_VARS['Threshold Graph Great']: + bar_color = COLORS['GREEN'] + rate_color = COLORS['GREEN'] + line = ' {p:5.1f}% {b_color}{b:<4} {r_color}{r:6.1f} Mb/s{c}\n'.format( + p=percent, + b_color=bar_color, + b=IO_VARS['Graph Vertical'][step], + r_color=rate_color, + r=rate/(1024**2), + c=COLORS['CLEAR']) + with open(progress_file, 'a') as f: + f.write(line) -def update_progress(): - """Update progress file.""" - if 'Progress Out' not in TESTS: - TESTS['Progress Out'] = '{}/progress.out'.format(global_vars['LogDir']) - output = [] - output.append('{BLUE}HW Diagnostics{CLEAR}'.format(**COLORS)) - output.append('───────────────') - if TESTS['Prime95']['Enabled']: - output.append(' ') - output.append('{BLUE}Prime95{s_color}{status:>8}{CLEAR}'.format( - s_color = get_status_color(TESTS['Prime95']['Status']), - status = TESTS['Prime95']['Status'], - **COLORS)) - if TESTS['NVMe/SMART']['Enabled']: - output.append(' ') - output.append('{BLUE}NVMe / SMART{CLEAR}'.format(**COLORS)) - if TESTS['NVMe/SMART']['Quick']: - output.append('{YELLOW} (Quick Check){CLEAR}'.format(**COLORS)) - for dev, status in sorted(TESTS['NVMe/SMART']['Status'].items()): - output.append('{dev}{s_color}{status:>{pad}}{CLEAR}'.format( - dev = dev, - pad = 15-len(dev), - s_color = get_status_color(status), - status = status, - **COLORS)) - if TESTS['badblocks']['Enabled']: - output.append(' ') - output.append('{BLUE}badblocks{CLEAR}'.format(**COLORS)) - for dev, status in sorted(TESTS['badblocks']['Status'].items()): - output.append('{dev}{s_color}{status:>{pad}}{CLEAR}'.format( - dev = dev, - pad = 15-len(dev), - s_color = get_status_color(status), - status = status, - **COLORS)) - if TESTS['iobenchmark']['Enabled']: - output.append(' ') - output.append('{BLUE}I/O Benchmark{CLEAR}'.format(**COLORS)) - for dev, status in sorted(TESTS['iobenchmark']['Status'].items()): - output.append('{dev}{s_color}{status:>{pad}}{CLEAR}'.format( - dev = dev, - pad = 15-len(dev), - s_color = get_status_color(status), - status = status, - **COLORS)) - # Add line-endings - output = ['{}\n'.format(line) for line in output] +def update_progress_pane(state): + """Update progress file for side pane.""" + output = [] + for k, v in state.tests.items(): + # Skip disabled sections + if not v['Enabled']: + continue - with open(TESTS['Progress Out'], 'w') as f: - f.writelines(output) + # Add section name + if k != 'Prime95': + output.append('{BLUE}{name}{CLEAR}'.format(name=k, **COLORS)) + if 'SMART' in k and state.quick_mode: + output[-1] += ' {}'.format(QUICK_LABEL) -def upload_to_imgur(image_path): - """Upload image to Imgur and return image url as str.""" - image_data = None - image_link = None + # Add status from test object(s) + for test in v['Objects']: + output.append(test.status) - # Bail early - if not image_path: - raise GenericError + # Add spacer before next section + output.append(' ') - # Read image file and convert to base64 then convert to str - with open(image_path, 'rb') as f: - image_data = base64.b64encode(f.read()).decode() + # Add line-endings + output = ['{}\n'.format(line) for line in output] - # POST image - url = "https://api.imgur.com/3/image" - boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW' - payload = ('--{boundary}\r\nContent-Disposition: form-data; ' - 'name="image"\r\n\r\n{data}\r\n--{boundary}--') - payload = payload.format(boundary=boundary, data=image_data) - headers = { - 'content-type': 'multipart/form-data; boundary={}'.format(boundary), - 'Authorization': 'Client-ID {}'.format(IMGUR_CLIENT_ID), - } - response = requests.request("POST", url, data=payload, headers=headers) + with open(state.progress_out, 'w') as f: + f.writelines(output) - # Return image link - if response.ok: - json_data = json.loads(response.text) - image_link = json_data['data']['link'] - return image_link - -def upload_to_nextcloud(image_path, ticket_number, dev_name): - """Upload image to Nextcloud server and return folder url as str.""" - image_data = None - - # Bail early - if not image_path: - raise GenericError - - # Read image file and convert to base64 - with open(image_path, 'rb') as f: - image_data = f.read() - - # PUT image - url = '{base_url}/{ticket_number}_iobenchmark_{dev_name}_{date}.png'.format( - base_url=BENCHMARK_SERVER['Url'], - ticket_number=ticket_number, - dev_name=dev_name, - date=global_vars.get('Date-Time', 'Unknown Date-Time')) - requests.put( - url, - data=image_data, - headers = {'X-Requested-With': 'XMLHttpRequest'}, - auth = (BENCHMARK_SERVER['User'], BENCHMARK_SERVER['Pass'])) - - # Return folder link - return BENCHMARK_SERVER['Short Url'] if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") -# vim: sts=4 sw=4 ts=4 +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/info.py b/.bin/Scripts/functions/info.py index 7630a766..ad71bce7 100644 --- a/.bin/Scripts/functions/info.py +++ b/.bin/Scripts/functions/info.py @@ -1,520 +1,547 @@ # Wizard Kit: Functions - Information from borrowed import knownpaths +from functions.activation import * from operator import itemgetter -from functions.common import * -from functions.activation import * - -# Regex -REGEX_OFFICE = re.compile( - r'(Microsoft (Office\s+' - r'(365|Enterprise|Home|Pro(\s|fessional)' - r'|Single|Small|Standard|Starter|Ultimate|system)' - r'|Works[-\s\d]+\d)' - r'|(Libre|Open|Star)\s*Office' - r'|WordPerfect|Gnumeric|Abiword)', - re.IGNORECASE) # STATIC VARIABLES REG_PROFILE_LIST = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' REG_SHELL_FOLDERS = r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders' TMP_HIVE_PATH = 'TEMP_HIVE_MOUNT' EXTRA_FOLDERS = [ - 'Dropbox', - 'Google Drive', - 'OneDrive', - 'SkyDrive', + 'Dropbox', + 'Google Drive', + 'OneDrive', + 'SkyDrive', ] SHELL_FOLDERS = { - #GUIDs from: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx - 'Desktop': ( - '{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}', - ), - 'Documents': ( - 'Personal', - '{FDD39AD0-238F-46AF-ADB4-6C85480369C7}', - ), - 'Downloads': ( - '{374DE290-123F-4565-9164-39C4925E467B}', - ), - 'Favorites': ( - '{1777F761-68AD-4D8A-87BD-30B759FA33DD}', - ), - 'Music': ( - 'My Music', - '{4BD8D571-6D19-48D3-BE97-422220080E43}', - ), - 'Pictures': ( - 'My Pictures', - '{33E28130-4E1E-4676-835A-98395C3BC3BB}', - ), - 'Videos': ( - 'My Video', - '{18989B1D-99B5-455B-841C-AB7C74E4DDFC}', - ), + #GUIDs from: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx + 'Desktop': ( + '{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}', + ), + 'Documents': ( + 'Personal', + '{FDD39AD0-238F-46AF-ADB4-6C85480369C7}', + ), + 'Downloads': ( + '{374DE290-123F-4565-9164-39C4925E467B}', + ), + 'Favorites': ( + '{1777F761-68AD-4D8A-87BD-30B759FA33DD}', + ), + 'Music': ( + 'My Music', + '{4BD8D571-6D19-48D3-BE97-422220080E43}', + ), + 'Pictures': ( + 'My Pictures', + '{33E28130-4E1E-4676-835A-98395C3BC3BB}', + ), + 'Videos': ( + 'My Video', + '{18989B1D-99B5-455B-841C-AB7C74E4DDFC}', + ), } + +# Regex +REGEX_OFFICE = re.compile( + r'(Microsoft (Office\s+' + r'(365|Enterprise|Home|Pro(\s|fessional)' + r'|Single|Small|Standard|Starter|Ultimate|system)' + r'|Works[-\s\d]+\d)' + r'|(Libre|Open|Star)\s*Office' + r'|WordPerfect|Gnumeric|Abiword)', + re.IGNORECASE) + + def backup_file_list(): - """Export current file listing for the system.""" - extract_item('Everything', silent=True) - cmd = [ - global_vars['Tools']['Everything'], - '-nodb', - '-create-filelist', - r'{LogDir}\File List.txt'.format(**global_vars), - global_vars['Env']['SYSTEMDRIVE']] - run_program(cmd) + """Export current file listing for the system.""" + extract_item('Everything', silent=True) + cmd = [ + global_vars['Tools']['Everything'], + '-nodb', + '-create-filelist', + r'{LogDir}\File List.txt'.format(**global_vars), + global_vars['Env']['SYSTEMDRIVE']] + run_program(cmd) + def backup_power_plans(): - """Export current power plans.""" - os.makedirs(r'{BackupDir}\Power Plans\{Date}'.format( - **global_vars), exist_ok=True) - plans = run_program(['powercfg', '/L']) - plans = plans.stdout.decode().splitlines() - plans = [p for p in plans if re.search(r'^Power Scheme', p)] - for p in plans: - guid = re.sub(r'Power Scheme GUID:\s+([0-9a-f\-]+).*', r'\1', p) - name = re.sub( - r'Power Scheme GUID:\s+[0-9a-f\-]+\s+\(([^\)]+)\).*', r'\1', p) - out = r'{BackupDir}\Power Plans\{Date}\{name}.pow'.format( - name=name, **global_vars) - if not os.path.exists(out): - cmd = ['powercfg', '-export', out, guid] - run_program(cmd, check=False) + """Export current power plans.""" + os.makedirs(r'{BackupDir}\Power Plans\{Date}'.format( + **global_vars), exist_ok=True) + plans = run_program(['powercfg', '/L']) + plans = plans.stdout.decode().splitlines() + plans = [p for p in plans if re.search(r'^Power Scheme', p)] + for p in plans: + guid = re.sub(r'Power Scheme GUID:\s+([0-9a-f\-]+).*', r'\1', p) + name = re.sub( + r'Power Scheme GUID:\s+[0-9a-f\-]+\s+\(([^\)]+)\).*', r'\1', p) + out = r'{BackupDir}\Power Plans\{Date}\{name}.pow'.format( + name=name, **global_vars) + if not os.path.exists(out): + cmd = ['powercfg', '-export', out, guid] + run_program(cmd, check=False) + def backup_registry(overwrite=False): - """Backup registry including user hives.""" - extract_item('erunt', silent=True) - cmd = [ - global_vars['Tools']['ERUNT'], - r'{BackupDir}\Registry\{Date}'.format(**global_vars), - 'sysreg', - 'curuser', - 'otherusers', - '/noprogresswindow'] - if overwrite: - cmd.append('/noconfirmdelete') - run_program(cmd) + """Backup registry including user hives.""" + extract_item('erunt', silent=True) + cmd = [ + global_vars['Tools']['ERUNT'], + r'{BackupDir}\Registry\{Date}'.format(**global_vars), + 'sysreg', + 'curuser', + 'otherusers', + '/noprogresswindow'] + if overwrite: + cmd.append('/noconfirmdelete') + run_program(cmd) + def get_folder_size(path): - """Get (human-readable) size of folder passed, returns str.""" - size = 'Unknown' - cmd = [global_vars['Tools']['Du'], '-c', '-nobanner', '-q', path] + """Get (human-readable) size of folder passed, returns str.""" + size = 'Unknown' + cmd = [global_vars['Tools']['Du'], '-c', '-nobanner', '-q', path] + try: + out = run_program(cmd) + except FileNotFoundError: + # Failed to find folder + pass + except subprocess.CalledProcessError: + # Failed to get folder size + pass + else: try: - out = run_program(cmd) - except FileNotFoundError: - # Failed to find folder - pass - except subprocess.CalledProcessError: - # Failed to get folder size - pass + size = out.stdout.decode().split(',')[-2] + except IndexError: + # Failed to parse csv data + pass else: - try: - size = out.stdout.decode().split(',')[-2] - except IndexError: - # Failed to parse csv data - pass - else: - size = human_readable_size(size) - return size + size = human_readable_size(size) + return size + def get_installed_antivirus(): - """Get list of installed Antivirus programs.""" - programs = [] + """Get list of installed Antivirus programs.""" + programs = [] + cmd = ['WMIC', r'/namespace:\\root\SecurityCenter2', + 'path', 'AntivirusProduct', + 'get', 'displayName', '/value'] + out = run_program(cmd) + out = out.stdout.decode().strip() + products = out.splitlines() + products = [p.split('=')[1] for p in products if p] + for prod in sorted(products): + # Get product state and check if it's enabled + # credit: https://jdhitsolutions.com/blog/powershell/5187/get-antivirus-product-status-with-powershell/ cmd = ['WMIC', r'/namespace:\\root\SecurityCenter2', - 'path', 'AntivirusProduct', - 'get', 'displayName', '/value'] + 'path', 'AntivirusProduct', + 'where', 'displayName="{}"'.format(prod), + 'get', 'productState', '/value'] out = run_program(cmd) out = out.stdout.decode().strip() - products = out.splitlines() - products = [p.split('=')[1] for p in products if p] - for prod in sorted(products): - # Get product state and check if it's enabled - # credit: https://jdhitsolutions.com/blog/powershell/5187/get-antivirus-product-status-with-powershell/ - cmd = ['WMIC', r'/namespace:\\root\SecurityCenter2', - 'path', 'AntivirusProduct', - 'where', 'displayName="{}"'.format(prod), - 'get', 'productState', '/value'] - out = run_program(cmd) - out = out.stdout.decode().strip() - state = out.split('=')[1] - state = hex(int(state)) - if str(state)[3:5] != '10': - programs.append('[Disabled] {}'.format(prod)) - else: - programs.append(prod) + state = out.split('=')[1] + state = hex(int(state)) + if str(state)[3:5] != '10': + programs.append('[Disabled] {}'.format(prod)) + else: + programs.append(prod) + + if len(programs) == 0: + programs = ['No programs found'] + return programs - if len(programs) == 0: - programs = ['No programs found'] - return programs def get_installed_office(): - """Get list of installed Office programs.""" - programs = [] - log_file = r'{LogDir}\Installed Program List (AIDA64).txt'.format( - **global_vars) - with open (log_file, 'r') as f: - for line in sorted(f.readlines()): - if REGEX_OFFICE.search(line): - programs.append(line[4:82].strip()) + """Get list of installed Office programs.""" + programs = [] + log_file = r'{LogDir}\Installed Program List (AIDA64).txt'.format( + **global_vars) + with open (log_file, 'r') as f: + for line in sorted(f.readlines()): + if REGEX_OFFICE.search(line): + programs.append(line[4:82].strip()) + + if len(programs) == 0: + programs = ['No programs found'] + return programs - if len(programs) == 0: - programs = ['No programs found'] - return programs def get_shell_path(folder, user='current'): - """Get shell path using SHGetKnownFolderPath via knownpaths, returns str. + """Get shell path using knownpaths, returns str. - NOTE: Only works for the current user. - Code based on https://gist.github.com/mkropat/7550097 - """ - path = None - folderid = None - if user.lower() == 'public': - user = 'common' + NOTE: Only works for the current user. + Code based on https://gist.github.com/mkropat/7550097 + """ + path = None + folderid = None + if user.lower() == 'public': + user = 'common' + try: + folderid = getattr(knownpaths.FOLDERID, folder) + except AttributeError: + # Unknown folder ID, ignore and return None + pass + + if folderid: try: - folderid = getattr(knownpaths.FOLDERID, folder) - except AttributeError: - # Unknown folder ID, ignore and return None - pass + path = knownpaths.get_path( + folderid, getattr(knownpaths.UserHandle, user)) + except PathNotFoundError: + # Folder not found, ignore and return None + pass - if folderid: - try: - path = knownpaths.get_path(folderid, getattr(knownpaths.UserHandle, user)) - except PathNotFoundError: - # Folder not found, ignore and return None - pass + return path - return path def get_user_data_paths(user): - """Get user data paths for provided user, returns dict.""" - hive_path = user['SID'] - paths = { - 'Profile': { - 'Path': None, - }, - 'Shell Folders': {}, - 'Extra Folders': {}, - } - unload_hive = False + """Get user data paths for provided user, returns dict.""" + hive_path = user['SID'] + paths = { + 'Profile': { + 'Path': None, + }, + 'Shell Folders': {}, + 'Extra Folders': {}, + } + unload_hive = False - if user['Name'] == global_vars['Env']['USERNAME']: - # We can use SHGetKnownFolderPath for the current user - paths['Profile']['Path'] = get_shell_path('Profile') - paths['Shell Folders'] = {f: {'Path': get_shell_path(f)} - for f in SHELL_FOLDERS.keys()} - else: - # We have to use the NTUSER.dat hives which isn't recommended by MS - try: - key_path = r'{}\{}'.format(REG_PROFILE_LIST, user['SID']) - with winreg.OpenKey(HKLM, key_path) as key: - paths['Profile']['Path'] = winreg.QueryValueEx( - key, 'ProfileImagePath')[0] - except Exception: - # Profile path not found, leaving as None. - pass + if user['Name'] == global_vars['Env']['USERNAME']: + # We can use SHGetKnownFolderPath for the current user + paths['Profile']['Path'] = get_shell_path('Profile') + paths['Shell Folders'] = {f: {'Path': get_shell_path(f)} + for f in SHELL_FOLDERS.keys()} + else: + # We have to use the NTUSER.dat hives which isn't recommended by MS + try: + key_path = r'{}\{}'.format(REG_PROFILE_LIST, user['SID']) + with winreg.OpenKey(HKLM, key_path) as key: + paths['Profile']['Path'] = winreg.QueryValueEx( + key, 'ProfileImagePath')[0] + except Exception: + # Profile path not found, leaving as None. + pass - # Shell folders (Prep) - if not reg_path_exists(HKU, hive_path) and paths['Profile']['Path']: - # User not logged-in, loading hive - # Also setting unload_hive so it will be unloaded later. - hive_path = TMP_HIVE_PATH - cmd = ['reg', 'load', r'HKU\{}'.format(TMP_HIVE_PATH), - r'{}\NTUSER.DAT'.format(paths['Profile']['Path'])] - unload_hive = True + # Shell folders (Prep) + if not reg_path_exists(HKU, hive_path) and paths['Profile']['Path']: + # User not logged-in, loading hive + # Also setting unload_hive so it will be unloaded later. + hive_path = TMP_HIVE_PATH + cmd = ['reg', 'load', r'HKU\{}'.format(TMP_HIVE_PATH), + r'{}\NTUSER.DAT'.format(paths['Profile']['Path'])] + unload_hive = True + try: + run_program(cmd) + except subprocess.CalledProcessError: + # Failed to load user hive + pass + + # Shell folders + shell_folders = r'{}\{}'.format(hive_path, REG_SHELL_FOLDERS) + if (reg_path_exists(HKU, hive_path) + and reg_path_exists(HKU, shell_folders)): + with winreg.OpenKey(HKU, shell_folders) as key: + for folder, values in SHELL_FOLDERS.items(): + for value in values: try: - run_program(cmd) - except subprocess.CalledProcessError: - # Failed to load user hive - pass + path = winreg.QueryValueEx(key, value)[0] + except FileNotFoundError: + # Skip missing values + pass + else: + paths['Shell Folders'][folder] = {'Path': path} + # Stop checking values for this folder + break - # Shell folders - shell_folders = r'{}\{}'.format(hive_path, REG_SHELL_FOLDERS) - if (reg_path_exists(HKU, hive_path) - and reg_path_exists(HKU, shell_folders)): - with winreg.OpenKey(HKU, shell_folders) as key: - for folder, values in SHELL_FOLDERS.items(): - for value in values: - try: - path = winreg.QueryValueEx(key, value)[0] - except FileNotFoundError: - # Skip missing values - pass - else: - paths['Shell Folders'][folder] = {'Path': path} - # Stop checking values for this folder - break - - # Shell folder (extra check) - if paths['Profile']['Path']: - for folder in SHELL_FOLDERS.keys(): - folder_path = r'{Path}\{folder}'.format( - folder=folder, **paths['Profile']) - if (folder not in paths['Shell Folders'] - and os.path.exists(folder_path)): - paths['Shell Folders'][folder] = {'Path': folder_path} - - # Extra folders + # Shell folder (extra check) if paths['Profile']['Path']: - for folder in EXTRA_FOLDERS: - folder_path = r'{Path}\{folder}'.format( - folder=folder, **paths['Profile']) - if os.path.exists(folder_path): - paths['Extra Folders'][folder] = {'Path': folder_path} + for folder in SHELL_FOLDERS.keys(): + folder_path = r'{Path}\{folder}'.format( + folder=folder, **paths['Profile']) + if (folder not in paths['Shell Folders'] + and os.path.exists(folder_path)): + paths['Shell Folders'][folder] = {'Path': folder_path} - # Shell folders (cleanup) - if unload_hive: - cmd = ['reg', 'unload', r'HKU\{}'.format(TMP_HIVE_PATH)] - run_program(cmd, check=False) + # Extra folders + if paths['Profile']['Path']: + for folder in EXTRA_FOLDERS: + folder_path = r'{Path}\{folder}'.format( + folder=folder, **paths['Profile']) + if os.path.exists(folder_path): + paths['Extra Folders'][folder] = {'Path': folder_path} + + # Shell folders (cleanup) + if unload_hive: + cmd = ['reg', 'unload', r'HKU\{}'.format(TMP_HIVE_PATH)] + run_program(cmd, check=False) + + # Done + return paths - # Done - return paths def get_user_folder_sizes(users): - """Update list(users) to include folder paths and sizes.""" - extract_item('du', filter='du*', silent=True) - # Configure Du - winreg.CreateKey(HKCU, r'Software\Sysinternals\Du') - with winreg.OpenKey(HKCU, - r'Software\Sysinternals\Du', access=winreg.KEY_WRITE) as key: - winreg.SetValueEx(key, 'EulaAccepted', 0, winreg.REG_DWORD, 1) + """Update list(users) to include folder paths and sizes.""" + extract_item('du', filter='du*', silent=True) + # Configure Du + winreg.CreateKey(HKCU, r'Software\Sysinternals\Du') + with winreg.OpenKey(HKCU, + r'Software\Sysinternals\Du', access=winreg.KEY_WRITE) as key: + winreg.SetValueEx(key, 'EulaAccepted', 0, winreg.REG_DWORD, 1) + + for u in users: + u.update(get_user_data_paths(u)) + if u['Profile']['Path']: + u['Profile']['Size'] = get_folder_size(u['Profile']['Path']) + for folder in u['Shell Folders'].keys(): + u['Shell Folders'][folder]['Size'] = get_folder_size( + u['Shell Folders'][folder]['Path']) + for folder in u['Extra Folders'].keys(): + u['Extra Folders'][folder]['Size'] = get_folder_size( + u['Extra Folders'][folder]['Path']) - for u in users: - u.update(get_user_data_paths(u)) - if u['Profile']['Path']: - u['Profile']['Size'] = get_folder_size(u['Profile']['Path']) - for folder in u['Shell Folders'].keys(): - u['Shell Folders'][folder]['Size'] = get_folder_size( - u['Shell Folders'][folder]['Path']) - for folder in u['Extra Folders'].keys(): - u['Extra Folders'][folder]['Size'] = get_folder_size( - u['Extra Folders'][folder]['Path']) def get_user_list(): - """Get user list via WMIC, returns list of dicts.""" - users = [] + """Get user list via WMIC, returns list of dicts.""" + users = [] - # Get user info from WMI - cmd = ['wmic', 'useraccount', 'get', '/format:csv'] - try: - out = run_program(cmd) - except subprocess.CalledProcessError: - # Meh, return empty list to avoid a full crash - return users - - entries = out.stdout.decode().splitlines() - entries = [e.strip().split(',') for e in entries if e.strip()] - - # Add user(s) to dict - keys = entries[0] - for e in entries[1:]: - # Create dict using 1st line (keys) - e = dict(zip(keys, e)) - # Set Active status via 'Disabled' TRUE/FALSE str - e['Active'] = bool(e['Disabled'].upper() == 'FALSE') - # Assume SIDs ending with 1000+ are "Standard" and others are "System" - e['Type'] = 'Standard' if re.search(r'-1\d+$', e['SID']) else 'System' - users.append(e) - - # Sort list - users.sort(key=itemgetter('Name')) - - # Done + # Get user info from WMI + cmd = ['wmic', 'useraccount', 'get', '/format:csv'] + try: + out = run_program(cmd) + except subprocess.CalledProcessError: + # Meh, return empty list to avoid a full crash return users + entries = out.stdout.decode().splitlines() + entries = [e.strip().split(',') for e in entries if e.strip()] + + # Add user(s) to dict + keys = entries[0] + for e in entries[1:]: + # Create dict using 1st line (keys) + e = dict(zip(keys, e)) + # Set Active status via 'Disabled' TRUE/FALSE str + e['Active'] = bool(e['Disabled'].upper() == 'FALSE') + # Assume SIDs ending with 1000+ are "Standard" and others are "System" + e['Type'] = 'Standard' if re.search(r'-1\d+$', e['SID']) else 'System' + users.append(e) + + # Sort list + users.sort(key=itemgetter('Name')) + + # Done + return users + + def reg_path_exists(hive, path): - """Test if specified path exists, returns bool.""" - try: - winreg.QueryValue(hive, path) - except FileNotFoundError: - return False - else: - return True + """Test if specified path exists, returns bool.""" + try: + winreg.QueryValue(hive, path) + except FileNotFoundError: + return False + else: + return True + def run_aida64(): - """Run AIDA64 to save system reports.""" - extract_item('AIDA64', silent=True) - # All system info - config = r'{BinDir}\AIDA64\full.rpf'.format(**global_vars) - report_file = r'{LogDir}\System Information (AIDA64).html'.format( - **global_vars) - if not os.path.exists(report_file): - cmd = [ - global_vars['Tools']['AIDA64'], - '/R', report_file, - '/CUSTOM', config, - '/HTML', '/SILENT', '/SAFEST'] - run_program(cmd, check=False) + """Run AIDA64 to save system reports.""" + extract_item('AIDA64', silent=True) + # All system info + config = r'{BinDir}\AIDA64\full.rpf'.format(**global_vars) + report_file = r'{LogDir}\System Information (AIDA64).html'.format( + **global_vars) + if not os.path.exists(report_file): + cmd = [ + global_vars['Tools']['AIDA64'], + '/R', report_file, + '/CUSTOM', config, + '/HTML', '/SILENT', '/SAFEST'] + run_program(cmd, check=False) - # Installed Programs - config = r'{BinDir}\AIDA64\installed_programs.rpf'.format(**global_vars) - report_file = r'{LogDir}\Installed Program List (AIDA64).txt'.format( - **global_vars) - if not os.path.exists(report_file): - cmd = [ - global_vars['Tools']['AIDA64'], - '/R', report_file, - '/CUSTOM', config, - '/TEXT', '/SILENT', '/SAFEST'] - run_program(cmd, check=False) + # Installed Programs + config = r'{BinDir}\AIDA64\installed_programs.rpf'.format(**global_vars) + report_file = r'{LogDir}\Installed Program List (AIDA64).txt'.format( + **global_vars) + if not os.path.exists(report_file): + cmd = [ + global_vars['Tools']['AIDA64'], + '/R', report_file, + '/CUSTOM', config, + '/TEXT', '/SILENT', '/SAFEST'] + run_program(cmd, check=False) + + # Product Keys + config = r'{BinDir}\AIDA64\licenses.rpf'.format(**global_vars) + report_file = r'{LogDir}\Product Keys (AIDA64).txt'.format(**global_vars) + if not os.path.exists(report_file): + cmd = [ + global_vars['Tools']['AIDA64'], + '/R', report_file, + '/CUSTOM', config, + '/TEXT', '/SILENT', '/SAFEST'] + run_program(cmd, check=False) - # Product Keys - config = r'{BinDir}\AIDA64\licenses.rpf'.format(**global_vars) - report_file = r'{LogDir}\Product Keys (AIDA64).txt'.format(**global_vars) - if not os.path.exists(report_file): - cmd = [ - global_vars['Tools']['AIDA64'], - '/R', report_file, - '/CUSTOM', config, - '/TEXT', '/SILENT', '/SAFEST'] - run_program(cmd, check=False) def run_bleachbit(cleaners=None, preview=True): - """Run BleachBit preview and save log. + """Run BleachBit preview and save log. - If preview is True then no files should be deleted.""" - error_path = r'{}\Tools\BleachBit.err'.format(global_vars['LogDir']) - log_path = error_path.replace('err', 'log') - extract_item('BleachBit', silent=True) + If preview is True then no files should be deleted.""" + error_path = r'{}\Tools\BleachBit.err'.format(global_vars['LogDir']) + log_path = error_path.replace('err', 'log') + extract_item('BleachBit', silent=True) - # Safety check - if not cleaners: - # Disable cleaning and use preset config - cleaners = ['--preset'] - preview = True + # Safety check + if not cleaners: + # Disable cleaning and use preset config + cleaners = ['--preset'] + preview = True - # Run - cmd = [ - global_vars['Tools']['BleachBit'], - '--preview' if preview else '--clean'] - cmd.extend(cleaners) - out = run_program(cmd, check=False) + # Run + cmd = [ + global_vars['Tools']['BleachBit'], + '--preview' if preview else '--clean'] + cmd.extend(cleaners) + out = run_program(cmd, check=False) - # Save stderr - if out.stderr.decode().splitlines(): - with open(error_path, 'a', encoding='utf-8') as f: - for line in out.stderr.decode().splitlines(): - f.write(line.strip() + '\n') + # Save stderr + if out.stderr.decode().splitlines(): + with open(error_path, 'a', encoding='utf-8') as f: + for line in out.stderr.decode().splitlines(): + f.write(line.strip() + '\n') + + # Save stdout + with open(log_path, 'a', encoding='utf-8') as f: + for line in out.stdout.decode().splitlines(): + f.write(line.strip() + '\n') - # Save stdout - with open(log_path, 'a', encoding='utf-8') as f: - for line in out.stdout.decode().splitlines(): - f.write(line.strip() + '\n') def show_disk_usage(disk): - """Show free and used space for a specified disk.""" - print_standard('{:5}'.format(disk.device.replace('/', ' ')), - end='', flush=True, timestamp=False) - try: - usage = psutil.disk_usage(disk.device) - display_string = '{percent:>5.2f}% Free ({free} / {total})'.format( - percent = 100 - usage.percent, - free = human_readable_size(usage.free, 2), - total = human_readable_size(usage.total, 2)) - if usage.percent > 85: - print_error(display_string, timestamp=False) - elif usage.percent > 75: - print_warning(display_string, timestamp=False) - else: - print_standard(display_string, timestamp=False) - except Exception: - print_warning('Unknown', timestamp=False) + """Show free and used space for a specified disk.""" + print_standard('{:5}'.format(disk.device.replace('/', ' ')), + end='', flush=True, timestamp=False) + try: + usage = psutil.disk_usage(disk.device) + display_string = '{percent:>5.2f}% Free ({free} / {total})'.format( + percent = 100 - usage.percent, + free = human_readable_size(usage.free, 2), + total = human_readable_size(usage.total, 2)) + if usage.percent > 85: + print_error(display_string, timestamp=False) + elif usage.percent > 75: + print_warning(display_string, timestamp=False) + else: + print_standard(display_string, timestamp=False) + except Exception: + print_warning('Unknown', timestamp=False) + def show_free_space(indent=8, width=32): - """Show free space info for all fixed disks.""" - message = 'Free Space:' - for disk in psutil.disk_partitions(): - try: - if 'fixed' in disk.opts: - try_and_print(message=message, function=show_disk_usage, - ns='Unknown', silent_function=False, - indent=indent, width=width, disk=disk) - message = '' - except Exception: - pass + """Show free space info for all fixed disks.""" + message = 'Free Space:' + for disk in psutil.disk_partitions(): + try: + if 'fixed' in disk.opts: + try_and_print(message=message, function=show_disk_usage, + ns='Unknown', silent_function=False, + indent=indent, width=width, disk=disk) + message = '' + except Exception: + pass + def show_installed_ram(): - """Show installed RAM.""" - mem = psutil.virtual_memory() - if mem.total > 5905580032: - # > 5.5 Gb so 6Gb or greater - print_standard(human_readable_size(mem.total).strip(), timestamp=False) - elif mem.total > 3758096384: - # > 3.5 Gb so 4Gb or greater - print_warning(human_readable_size(mem.total).strip(), timestamp=False) - else: - print_error(human_readable_size(mem.total).strip(), timestamp=False) + """Show installed RAM.""" + mem = psutil.virtual_memory() + if mem.total > 5905580032: + # > 5.5 Gb so 6Gb or greater + print_standard(human_readable_size(mem.total).strip(), timestamp=False) + elif mem.total > 3758096384: + # > 3.5 Gb so 4Gb or greater + print_warning(human_readable_size(mem.total).strip(), timestamp=False) + else: + print_error(human_readable_size(mem.total).strip(), timestamp=False) + def show_os_activation(): - """Show OS activation info.""" - act_str = get_activation_string() - if windows_is_activated(): - print_standard(act_str, timestamp=False) - elif re.search(r'unavailable', act_str, re.IGNORECASE): - print_warning(act_str, timestamp=False) - else: - print_error(act_str, timestamp=False) + """Show OS activation info.""" + act_str = get_activation_string() + if windows_is_activated(): + print_standard(act_str, timestamp=False) + elif re.search(r'unavailable', act_str, re.IGNORECASE): + print_warning(act_str, timestamp=False) + else: + print_error(act_str, timestamp=False) + def show_os_name(): - """Show extended OS name (including warnings).""" - os_name = global_vars['OS']['DisplayName'] - if global_vars['OS']['Arch'] == 32: - # Show all 32-bit installs as an error message - print_error(os_name, timestamp=False) + """Show extended OS name (including warnings).""" + os_name = global_vars['OS']['DisplayName'] + if global_vars['OS']['Arch'] == 32: + # Show all 32-bit installs as an error message + print_error(os_name, timestamp=False) + else: + if re.search( + r'(preview build|unrecognized|unsupported)', + os_name, + re.IGNORECASE): + print_error(os_name, timestamp=False) + elif re.search(r'outdated', os_name, re.IGNORECASE): + print_warning(os_name, timestamp=False) else: - if re.search(r'(preview build|unrecognized|unsupported)', os_name, re.IGNORECASE): - print_error(os_name, timestamp=False) - elif re.search(r'outdated', os_name, re.IGNORECASE): - print_warning(os_name, timestamp=False) - else: - print_standard(os_name, timestamp=False) + print_standard(os_name, timestamp=False) + def show_temp_files_size(): - """Show total size of temp files identified by BleachBit.""" - size = None - with open(r'{LogDir}\Tools\BleachBit.log'.format(**global_vars), 'r') as f: - for line in f.readlines(): - if re.search(r'^disk space to be recovered:', line, re.IGNORECASE): - size = re.sub(r'.*: ', '', line.strip()) - size = re.sub(r'(\w)iB$', r' \1b', size) - if size is None: - print_warning(size, timestamp=False) - else: - print_standard(size, timestamp=False) + """Show total size of temp files identified by BleachBit.""" + size = None + with open(r'{LogDir}\Tools\BleachBit.log'.format(**global_vars), 'r') as f: + for line in f.readlines(): + if re.search(r'^disk space to be recovered:', line, re.IGNORECASE): + size = re.sub(r'.*: ', '', line.strip()) + size = re.sub(r'(\w)iB$', r' \1b', size) + if size is None: + print_warning(size, timestamp=False) + else: + print_standard(size, timestamp=False) + def show_user_data_summary(indent=8, width=32): - """Print user data folder sizes for all users.""" - users = get_user_list() - users = [u for u in users if u['Active']] - get_user_folder_sizes(users) - for user in users: - if ('Size' not in user['Profile'] - and not any(user['Shell Folders']) - and not any(user['Extra Folders'])): - # Skip empty users - continue - print_success('{indent}User: {user}'.format( - indent = ' '*int(indent/2), - user = user['Name'])) - for section in ['Profile', None, 'Shell Folders', 'Extra Folders']: - folders = [] - if section is None: - # Divider - print_standard('{}{}'.format(' '*indent, '-'*(width+6))) - elif section == 'Profile': - folders = {'Profile': user['Profile']} - else: - folders = user[section] - for folder in folders: - print_standard( - '{indent}{folder:<{width}}{size:>6} ({path})'.format( - indent = ' ' * indent, - width = width, - folder = folder, - size = folders[folder].get('Size', 'Unknown'), - path = folders[folder].get('Path', 'Unknown'))) + """Print user data folder sizes for all users.""" + users = get_user_list() + users = [u for u in users if u['Active']] + get_user_folder_sizes(users) + for user in users: + if ('Size' not in user['Profile'] + and not any(user['Shell Folders']) + and not any(user['Extra Folders'])): + # Skip empty users + continue + print_success('{indent}User: {user}'.format( + indent = ' '*int(indent/2), + user = user['Name'])) + for section in ['Profile', None, 'Shell Folders', 'Extra Folders']: + folders = [] + if section is None: + # Divider + print_standard('{}{}'.format(' '*indent, '-'*(width+6))) + elif section == 'Profile': + folders = {'Profile': user['Profile']} + else: + folders = user[section] + for folder in folders: + print_standard( + '{indent}{folder:<{width}}{size:>6} ({path})'.format( + indent = ' ' * indent, + width = width, + folder = folder, + size = folders[folder].get('Size', 'Unknown'), + path = folders[folder].get('Path', 'Unknown'))) + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/io_graph.py b/.bin/Scripts/functions/io_graph.py new file mode 100644 index 00000000..57048bfd --- /dev/null +++ b/.bin/Scripts/functions/io_graph.py @@ -0,0 +1,120 @@ +# Wizard Kit: Functions - PNG graph for I/O Benchmark + +import base64 +import Gnuplot +import json +import math +import requests + +from functions.common import * + + +# Functions +def export_io_graph(disk): + """Exports PNG graph using gnuplot, returns file path as str.""" + read_rates = disk.tests['I/O Benchmark'].read_rates + if not read_rates: + # No data, aborting + raise GenericError + max_rate = max(read_rates) / (1024**2) + max_rate = max(800, max_rate) + out_path = '{}/iobenchmark-{}.png'.format( + global_vars['LogDir'], disk.name) + plot_data = '{}/iobenchmark-{}-plot.data'.format( + global_vars['LogDir'], disk.name) + + # Adjust Y-axis range to either 1000 or roughly max rate + 200 + ## Round up to the nearest 100 and then add 200 + y_range = (math.ceil(max_rate/100)*100) + 200 + + # Save plot data to file for Gnuplot + with open(plot_data, 'w') as f: + for i in range(len(read_rates)): + _percent = ( (i+1) / len(read_rates) ) * 100 + _rate = int( read_rates[i] / (1024**2) ) + f.write('{:0.1f} {}\n'.format(_percent, _rate)) + + # Run gnuplot commands + g = Gnuplot.Gnuplot() + g('reset') + g('set output "{}"'.format(out_path)) + g('set terminal png large size 660,300 truecolor font "Noto Sans,11"') + g('set title "I/O Benchmark"') + g('set yrange [0:{}]'.format(y_range)) + g('set style data lines') + g('plot "{}" title "{}"'.format( + plot_data, + disk.description.replace('_', ' '), + )) + + # Cleanup + g.close() + del(g) + + return out_path + + +def upload_to_imgur(image_path): + """Upload image to Imgur and return image url as str.""" + image_data = None + image_link = None + + # Bail early + if not image_path: + raise GenericError + + # Read image file and convert to base64 then convert to str + with open(image_path, 'rb') as f: + image_data = base64.b64encode(f.read()).decode() + + # POST image + url = "https://api.imgur.com/3/image" + boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW' + payload = ('--{boundary}\r\nContent-Disposition: form-data; ' + 'name="image"\r\n\r\n{data}\r\n--{boundary}--') + payload = payload.format(boundary=boundary, data=image_data) + headers = { + 'content-type': 'multipart/form-data; boundary={}'.format(boundary), + 'Authorization': 'Client-ID {}'.format(IMGUR_CLIENT_ID), + } + response = requests.request("POST", url, data=payload, headers=headers) + + # Return image link + if response.ok: + json_data = json.loads(response.text) + image_link = json_data['data']['link'] + return image_link + + +def upload_to_nextcloud(image_path, ticket_number, dev_name): + """Upload image to Nextcloud server and return folder url as str.""" + image_data = None + + # Bail early + if not image_path: + raise GenericError + + # Read image file and convert to base64 + with open(image_path, 'rb') as f: + image_data = f.read() + + # PUT image + url = '{base_url}/{ticket_number}_iobenchmark_{dev_name}_{date}.png'.format( + base_url=BENCHMARK_SERVER['Url'], + ticket_number=ticket_number, + dev_name=dev_name, + date=global_vars.get('Date-Time', 'Unknown Date-Time')) + requests.put( + url, + data=image_data, + headers = {'X-Requested-With': 'XMLHttpRequest'}, + auth = (BENCHMARK_SERVER['User'], BENCHMARK_SERVER['Pass'])) + + # Return folder link + return BENCHMARK_SERVER['Short Url'] + + +if __name__ == '__main__': + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/network.py b/.bin/Scripts/functions/network.py index 5735c486..492ba16f 100644 --- a/.bin/Scripts/functions/network.py +++ b/.bin/Scripts/functions/network.py @@ -1,72 +1,74 @@ -#!/bin/python3 -# -## Wizard Kit: Functions - Network +# Wizard Kit: Functions - Network import os import shutil import sys -# Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) from functions.common import * + # REGEX REGEX_VALID_IP = re.compile( - r'(10.\d+.\d+.\d+' - r'|172.(1[6-9]|2\d|3[0-1])' - r'|192.168.\d+.\d+)', - re.IGNORECASE) + r'(10.\d+.\d+.\d+' + r'|172.(1[6-9]|2\d|3[0-1])' + r'|192.168.\d+.\d+)', + re.IGNORECASE) + def connect_to_network(): - """Connect to network if not already connected.""" - net_ifs = psutil.net_if_addrs() - net_ifs = [i[:2] for i in net_ifs.keys()] + """Connect to network if not already connected.""" + net_ifs = psutil.net_if_addrs() + net_ifs = [i[:2] for i in net_ifs.keys()] - # Bail if currently connected - if is_connected(): - return + # Bail if currently connected + if is_connected(): + return + + # WiFi + if 'wl' in net_ifs: + cmd = [ + 'nmcli', 'dev', 'wifi', + 'connect', WIFI_SSID, + 'password', WIFI_PASSWORD] + try_and_print( + message = 'Connecting to {}...'.format(WIFI_SSID), + function = run_program, + cmd = cmd) - # WiFi - if 'wl' in net_ifs: - cmd = [ - 'nmcli', 'dev', 'wifi', - 'connect', WIFI_SSID, - 'password', WIFI_PASSWORD] - try_and_print( - message = 'Connecting to {}...'.format(WIFI_SSID), - function = run_program, - cmd = cmd) def is_connected(): - """Check for a valid private IP.""" - devs = psutil.net_if_addrs() - for dev in devs.values(): - for family in dev: - if REGEX_VALID_IP.search(family.address): - # Valid IP found - return True - # Else - return False + """Check for a valid private IP.""" + devs = psutil.net_if_addrs() + for dev in devs.values(): + for family in dev: + if REGEX_VALID_IP.search(family.address): + # Valid IP found + return True + # Else + return False + def show_valid_addresses(): - """Show all valid private IP addresses assigned to the system.""" - devs = psutil.net_if_addrs() - for dev, families in sorted(devs.items()): - for family in families: - if REGEX_VALID_IP.search(family.address): - # Valid IP found - show_data(message=dev, data=family.address) + """Show all valid private IP addresses assigned to the system.""" + devs = psutil.net_if_addrs() + for dev, families in sorted(devs.items()): + for family in families: + if REGEX_VALID_IP.search(family.address): + # Valid IP found + show_data(message=dev, data=family.address) + def speedtest(): - """Run a network speedtest using speedtest-cli.""" - result = run_program(['speedtest-cli', '--simple']) - output = [line.strip() for line in result.stdout.decode().splitlines() - if line.strip()] - output = [line.split() for line in output] - output = [(a, float(b), c) for a, b, c in output] - return ['{:10}{:6.2f} {}'.format(*line) for line in output] + """Run a network speedtest using speedtest-cli.""" + result = run_program(['speedtest-cli', '--simple']) + output = [line.strip() for line in result.stdout.decode().splitlines() + if line.strip()] + output = [line.split() for line in output] + output = [(a, float(b), c) for a, b, c in output] + return ['{:10}{:6.2f} {}'.format(*line) for line in output] + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/osticket.py b/.bin/Scripts/functions/osticket.py new file mode 100644 index 00000000..bc207b04 --- /dev/null +++ b/.bin/Scripts/functions/osticket.py @@ -0,0 +1,501 @@ +# Wizard Kit: Functions - osTicket + +import mysql.connector as mariadb + +from functions.data import * +from functions.io_graph import * +from settings.osticket import * + + +# STATIC VARIABLES +KNOWN_DEV_TYPES = ('CPU', 'Disk') + + +# Regex +REGEX_BLOCK_GRAPH = re.compile(r'(▁|▂|▃|▄|▅|▆|▇|█)') +REGEX_NVME_SMART_ATTRIBUTES = re.compile(r'^\s*(\d+) / (\w+): (.{28})(.*)$') +REGEX_TEMPS = re.compile(r'^\s*(.*?)\s+(idle:)(.*)$') +REGEX_SENSOR = re.compile(r'^(.*?)(\s*)$') + + +# Classes +class osTicket(): + """Class to track osTicket data and functions.""" + def __init__(self, tests_cpu, tests_disk): + self.db_connection = None + self.db_cursor = None + self.disabled = False + self.errors = False + self.tests_cpu = tests_cpu + self.tests_disk = tests_disk + self.tunnel_proc = None + + def connect(self, silent=True): + """Establish connection to osTicket via a SSH tunnel.""" + cmd = [ + 'ssh', '-N', + '-p', OSTICKET['SSH']['Port'], + '-L3306:127.0.0.1:{Port}'.format(**OSTICKET['Database']), + '{User}@{Host}'.format(**OSTICKET['SSH']), + ] + + # Bail if disabled + if self.disabled: + return + + # Only open tunnel if one doesn't exist + if self.tunnel_proc is None or self.tunnel_proc.poll() is not None: + self.tunnel_proc = popen_program(cmd) + + # Connect to database + for x in range(5): + sleep(2) + try: + self.db_connection = mariadb.connect( + user=OSTICKET['Database']['User'], + password=OSTICKET['Database']['Pass'], + database=OSTICKET['Database']['Name'], + ) + self.db_cursor = self.db_connection.cursor() + except mariadb.errors.InterfaceError: + # SSH issue?, try again + pass + except mariadb.errors.Error: + # Bad creds or other SQL error, bail + break + except Exception: + # Unknown error + break + else: + # Connection established + break + + # Disable if necessary + if self.db_cursor is None: + self.disabled = True + self.tunnel_proc.kill() + if not silent: + print_warning('Failed to connect to osTicket') + print_standard('Integration disabled for this session') + print_standard(' ') + pause() + + def convert_report(self, name, test): + """Convert report into an osTicket friendly format, returns list.""" + out_report = [] + source_report = test.report + status = strip_colors(test.status) + status = status.replace(test.label, '').strip() + + # Header + index = 1 + if name == 'NVMe / SMART': + out_report.append('{} ({})'.format(name, status)) + if not source_report: + index = 0 + source_report = test.dev.generate_attribute_report() + elif not source_report: + index = 0 + out_report.append('{} ({})'.format(name, status)) + else: + out_report.append('{} ({})'.format(strip_colors(source_report[0]), status)) + + # Body + for line in source_report[index:]: + # Remove colors and leading spaces + line = strip_colors(line) + if line[:2] == ' ': + line = line[2:] + + # Test-specific modifications + if name == 'Prime95': + r = REGEX_TEMPS.match(line) + if r: + _sensor = '{:<20}'.format(r.group(1)) + _temps = r.group(2) + _temps += re.sub(r'\s+(\w+:)', r' ...... \1', r.group(3)) + r2 = REGEX_SENSOR.match(_sensor) + _sensor = r2.group(1) + _spacer = pad_with_dots(r2.group(2)) + line = '{}{} {}'.format(_sensor, _spacer, _temps) + if line == 'Temps': + out_report.append(' ') + elif name == 'NVMe / SMART': + r = REGEX_NVME_SMART_ATTRIBUTES.match(line) + if r: + _dec = '{:>3}'.format(r.group(1)) + _hex = r.group(2) + _atr = r.group(3).strip() + _val = '{:<20}'.format(r.group(4)) + line = '{}/{}: {} {}'.format( + _hex, + pad_with_dots(_dec), + pad_with_dots(_val, pad_right=True), + _atr) + elif name == 'I/O Benchmark': + line = REGEX_BLOCK_GRAPH.sub('', line) + line = line.strip() + if not line: + continue + + # Remove extra spaces + line = line.strip() + line = re.sub(r'(\s+)', ' ', line) + + # Add line to report + out_report.append(line) + + # Done + return out_report + + def disconnect(self, full=False): + """Close osTicket connection.""" + try: + self.db_cursor.close() + self.db_connection.close() + except Exception: + # Ignore errors since vars will be reset below + pass + + # Reset errors + if full: + self.errors = False + + # Reset vars + self.db_cursor = None + self.db_connection = None + + # Close tunnel + if full: + try: + self.tunnel_proc.kill() + self.tunnel_proc.wait(timeout=2) + except (AttributeError, subprocess.TimeoutExpired): + # Ignore and continue + pass + self.tunnel_proc = None + + def generate_report(self, dev, ticket_id): + """Generate device report for osTicket, returns list.""" + report = [] + results = self.get_device_overall_results(dev) + + # Header + if results['Full Diag']: + report.append( + '{Dev Type} hardware diagnostic tests: {Status}'.format(**results)) + report.append(' ') + + # Device + report.append(dev.description) + report.append(' ') + + # Test reports + for name, test in dev.tests.items(): + report.extend(self.convert_report(name, test)) + if name == 'I/O Benchmark': + # Create PNG graph + try: + graph_file = export_io_graph(dev) + except GenericError: + # No data to build graph, ignoring + pass + except (AttributeError, KeyError): + report.append('Failed to export graph') + else: + # Upload to Imgur + try: + url = upload_to_imgur(graph_file) + report.append('Imgur: {}'.format(url)) + except Exception: + report.append('Imgur: Failed to upload graph') + + # Upload to Nextcloud + try: + url = upload_to_nextcloud(graph_file, ticket_id, dev.name) + report.append('Nextcloud: {}'.format(url)) + except Exception: + report.append('Nextcloud: Failed to upload graph') + report.append(' ') + + # Volumes + if results['Dev Type'] == 'Disk' and results['Full Diag']: + # Mount all volumes and extend report + report.append('Volumes:') + report.extend(self.generate_volume_report(dev, results)) + report.append(' ') + + # Asterisk + if results['Asterisk']: + report.append('* NOTE: One or more tests were not run on this device') + + # Done + return report + + def generate_volume_report(self, dev, results): + """Mount all volumes for dev and generate report, returns list.""" + report = [] + mount_report = mount_volumes( + all_devices=False, + device_path='{}'.format(dev.path), + core_storage=results['Core']) + + # Format report + for v_path, v_data in sorted(mount_report.items()): + label = v_data.get('label', '') + if label: + label = '"{}"'.format(label) + else: + # Ensure string type + label = '' + size = v_data.get('size', '') + if size: + size = '{} {}B'.format(size[:-1], size[-1:]).upper() + else: + size = 'UNKNOWN' + size_used = v_data.get('size_used', 'UNKNOWN').upper() + size_avail = v_data.get('size_avail', 'UNKNOWN').upper() + v_data = [v_path, label, size, size_used, size_avail] + v_data = [v.strip().replace(' ', '_') for v in v_data] + for i in range(len(v_data)): + pad = 8 + if i < 2: + pad += 4 * (2 - i) + v_data[i] = pad_with_dots( + '{s:<{p}}'.format(s=v_data[i], p=pad), + pad_right=True) + v_data[-1] = re.sub(r'\.*$', '', v_data[-1]) + v_data = [v.replace('_', ' ') for v in v_data] + report.append( + '{}..{}..Total..{}..(Used..{}..Free..{})'.format(*v_data)) + + # Done + return report + + def get_device_overall_results(self, dev): + """Get overall results from tests for device, returns dict.""" + results = { + 'Core': False, + 'Dev Type': self.get_device_type(dev), + 'Full Diag': False, + 'Asterisk': None, + 'Failed': 0, + 'N/A': 0, + 'OVERRIDE': 0, + 'Passed': 0, + 'Status': 'Unknown', + } + + # Bail on unknown device type + if results['Dev Type'] not in KNOWN_DEV_TYPES: + raise GenericError( + 'Unrecognized device type: {}.'.format(results['Dev Type'])) + + # Get test list for device type + test_list = [] + if results['Dev Type'] == 'CPU': + test_list = self.tests_cpu + elif results['Dev Type'] == 'Disk': + test_list = self.tests_disk + + # Check if a full diag was run (i.e. all dev tests were enabled) + results['Full Diag'] = len(dev.tests) == len(test_list) + + # Tally test results + for test in dev.tests.values(): + if test.failed: + results['Failed'] += 1 + if test.passed: + results['Passed'] += 1 + if 'N/A' in test.status: + results['N/A'] += 1 + if 'OVERRIDE' in test.status: + results['OVERRIDE'] += 1 + + # Set overall status + if results['Failed'] > 0: + dev.checkbox = False + results['Status'] = 'FAILED' + elif results['Passed'] + results['N/A'] == len(test_list): + # Only mark true if all tests are enabled and all are "Passed" / "N/A" + dev.checkbox = True + results['Status'] = 'PASSED' + else: + results['Status'] = 'UNKNOWN' + if results['Full Diag'] and results['N/A'] > 0: + results['Asterisk'] = True + results['Status'] += '*' + + # Enable CoreStorage searches + results['Core'] = (results['Full Diag'] and + results['Passed']+results['N/A']+results['OVERRIDE'] == len(test_list)) + + # Done + return results + + def get_device_type(self, dev): + """Terrible hack to determine device type, returns str.""" + # TODO: Fix with proper isinstance() call + type_str = str(dev.__class__) + if 'CpuObj' in type_str: + type_str = 'CPU' + elif 'DiskObj' in type_str: + type_str = 'Disk' + + return type_str + + def get_ticket_name(self, ticket_id): + """Lookup ticket and return name as str.""" + name = None + sql_cmd = "SELECT name FROM `{Ticket}`".format(**OSTICKET['Tables']) + sql_cmd += " WHERE `ticket_id` = {}".format(ticket_id) + sql_cmd += ";" + + # Lookup name + # NOTE: If multiple entries are found it will return the last + self.db_cursor.execute(sql_cmd) + for s in self.db_cursor: + name = s[0] + + # Done + return name + + def get_ticket_number(self): + """Get ticket number and confirm with name from osTicket DB.""" + ticket_number = None + self.connect(silent=False) + + # Bail if disabled + if self.disabled: + return None + + # Main loop + while ticket_number is None: + print_standard(' ') + _input = input('Enter ticket number (or leave blank to disable): ') + _input = _input.strip() + + # No ticket ID entered + if re.match(r'^\s*$', _input): + if ask('Disable osTicket integration for this run?'): + self.disabled = True + break + + # Invalid ID entered + if not re.match(r'^(\d+)$', _input): + continue + + # Valid ID entered, lookup name and verify + try: + _name = self.get_ticket_name(_input) + except Exception: + # Ignore and return None below + break + if _name: + print_standard('You have selected ticket #{} {}'.format( + _input, _name)) + if ask('Is this correct?'): + ticket_number = _input + + # Done + self.disconnect() + return ticket_number + + def post_device_results(self, dev, ticket_id): + """Generate osTicket friendly report and post as response to ticket.""" + if not dev.tests: + # No test results available, aborting post + return + response = self.generate_report(dev, ticket_id) + self.post_response(response, ticket_id) + + def post_response(self, response, ticket_id): + """Post a reply to a ticket in osTicket.""" + self.connect() + + # Bail if disabled + if self.disabled: + return + + # Convert response to string + response = '\n'.join(response) + response = response.replace('`', '') + + # Build SQL cmd + sql_cmd = "INSERT INTO `{Name}`.`{Response}`".format( + **OSTICKET['Database'], **OSTICKET['Tables']) + sql_cmd += " (ticket_id, staff_id, staff_name, response, created)" + sql_cmd += " VALUES (" + sql_cmd += " '{}',".format(ticket_id) + sql_cmd += " '{ID}', '{Name}',".format(**OSTICKET['Staff']) + sql_cmd += " '{}',".format(response) + sql_cmd += " '{}'".format(time.strftime("%Y-%m-%d %H:%M:%S")) + sql_cmd += " );" + + # Run SQL cmd + try: + self.db_cursor.execute(sql_cmd) + except mariadb.errors.Error: + # Set self.errors to enable warning line on results screen + self.errors = True + + # Done + self.disconnect() + + def set_disk_failed(self, ticket_id): + """Mark disk as failed in osTicket.""" + self.set_flag( + ticket_id, + OSTICKET['Disk Flag']['Name'], + OSTICKET['Disk Flag']['Fail']) + + def set_disk_passed(self, ticket_id): + """Mark disk as passed in osTicket.""" + self.set_flag( + ticket_id, + OSTICKET['Disk Flag']['Name'], + OSTICKET['Disk Flag']['Pass']) + + def set_flag(self, ticket_id, flag_name, flag_value): + """Set flag in osTicket.""" + self.connect() + + # Bail if disabled + if self.disabled: + return + + # Build SQL cmd + sql_cmd = "UPDATE `{Name}`.`{Ticket}`".format( + **OSTICKET['Database'], **OSTICKET['Tables']) + sql_cmd += " SET `{}` = '{}'".format(flag_name, flag_value) + sql_cmd += " WHERE `{Ticket}`.`ticket_id` = {ticket_id}".format( + ticket_id=ticket_id, **OSTICKET['Tables']) + sql_cmd += ";" + + # Run SQL cmd + try: + self.db_cursor.execute(sql_cmd) + except mariadb.errors.Error: + # Set self.errors to enable warning line on results screen + self.errors = True + + # Done + self.disconnect() + + +# Functions +def pad_with_dots(s, pad_right=False): + """Replace space padding with dots, returns str.""" + s = str(s).replace(' ', '..') + if '.' in s: + if pad_right: + s = s + '.' + else: + s = '.' + s + return s + + +if __name__ == '__main__': + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/partition_uids.py b/.bin/Scripts/functions/partition_uids.py deleted file mode 100644 index 38fba0db..00000000 --- a/.bin/Scripts/functions/partition_uids.py +++ /dev/null @@ -1,326 +0,0 @@ -# Wizard Kit: Functions - PARTITION UIDs -# sources: https://en.wikipedia.org/wiki/GUID_Partition_Table -# https://en.wikipedia.org/wiki/Partition_type -# NOTE: Info has been trimmed for brevity. As such, there may be some inaccuracy. - -PARTITION_UIDS = { - '00': {'OS': 'All','Description': 'Empty partition entry'}, - '01': {'OS': 'DOS','Description': 'FAT12 as primary partition'}, - '02': {'OS': 'XENIX','Description': 'XENIX root'}, - '03': {'OS': 'XENIX','Description': 'XENIX usr'}, - '04': {'OS': 'DOS','Description': 'FAT16 with less than 32 MB'}, - '05': {'OS': 'DOS / SpeedStor','Description': 'Extended partition'}, - '06': {'OS': 'DOS1+','Description': 'FAT16B [over 65K sectors]'}, - '07': {'OS': 'Windows / OS/2 / QNX 2','Description': 'NTFS/exFAT/HPFS/IFS/QNX'}, - '08': {'OS': 'CBM / DOS / OS/2 / AIX /QNX','Description': 'FAT12-16/AIX/QNY/SplitDrive'}, - '09': {'OS': 'AIX / QNX / Coherent / OS-9','Description': 'AIX/QNZ/Coherent/RBF'}, - '0A': {'OS': 'OS/2 / Coherent','Description': 'Boot Manager / Swap'}, - '0B': {'OS': 'DOS','Description': 'FAT32 with CHS addressing'}, - '0C': {'OS': 'DOS','Description': 'FAT32 with LBA'}, - '0D': {'OS': 'Silicon Safe','Description': 'Reserved'}, - '0E': {'OS': 'DOS','Description': 'FAT16B with LBA'}, - '0F': {'OS': 'DOS','Description': 'Extended partition with LBA'}, - '10': {'OS': 'OPUS','Description': 'Unknown'}, - '11': {'OS': 'Leading Edge MS-DOS / OS/2','Description': 'FAT12/FAT16'}, - '12': {'OS': 'Compaq Contura','Description': 'conf/diag/hiber/rescue/serv'}, - '14': {'OS': 'AST DOS / OS/2 / MaverickOS','Description': 'FAT12/FAT16/Omega'}, - '15': {'OS': 'OS/2 / Maverick OS','Description': 'Hidden extended / Swap'}, - '16': {'OS': 'OS/2 Boot Manager','Description': 'Hidden FAT16B'}, - '17': {'OS': 'OS/2 Boot Manager','Description': 'Hidden IFS/HPFS/NTFS/exFAT'}, - '18': {'OS': 'AST Windows','Description': '0-Volt Suspend/SmartSleep'}, - '19': {'OS': 'Willowtech Photon coS','Description': 'Willowtech Photon coS'}, - '1B': {'OS': 'OS/2 Boot Manager','Description': 'Hidden FAT32'}, - '1C': {'OS': 'OS/2 Boot Manager','Description': 'Hidden FAT32 with LBA'}, - '1E': {'OS': 'OS/2 Boot Manager','Description': 'Hidden FAT16 with LBA'}, - '1F': {'OS': 'OS/2 Boot Manager','Description': 'Hidden extended with LBA'}, - '20': {'OS': 'Windows Mobile','Description': 'update XIP/Willowsoft OFS1'}, - '21': {'OS': 'Oxygen','Description': 'SpeedStor / FSo2'}, - '22': {'OS': 'Oxygen','Description': 'Oxygen Extended Partition'}, - '23': {'OS': 'Windows Mobile','Description': 'Reserved / boot XIP'}, - '24': {'OS': 'NEC MS-DOS0','Description': 'Logical FAT12 or FAT16'}, - '25': {'OS': 'Windows Mobile','Description': 'IMGFS[citation needed]'}, - '26': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, - '27': {'OS': 'Win/PQserv/MirOS/RooterBOOT','Description': 'WinRE/Rescue/MirOS/RooterBOOT'}, - '2A': {'OS': 'AtheOS','Description': 'AthFS/AFS/Reserved'}, - '2B': {'OS': 'SyllableOS','Description': 'SyllableSecure (SylStor)'}, - '31': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, - '32': {'OS': 'NOS','Description': 'Unknown'}, - '33': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, - '34': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, - '35': {'OS': 'OS/2 Server /eComStation','Description': 'JFS'}, - '36': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, - '38': {'OS': 'THEOS','Description': 'THEOS version 3.2, 2 GB'}, - '39': {'OS': 'Plan 9 / THEOS','Description': 'Plan 9 edition 3 / THEOS v4'}, - '3A': {'OS': 'THEOS','Description': 'THEOS v4, 4 GB'}, - '3B': {'OS': 'THEOS','Description': 'THEOS v4 extended'}, - '3C': {'OS': 'PartitionMagic','Description': 'PqRP (image in progress)'}, - '3D': {'OS': 'PartitionMagic','Description': 'Hidden NetWare'}, - '3F': {'OS': 'OS/32','Description': 'Unknown'}, - '40': {'OS': 'PICK / Venix','Description': 'PICK R83 / Venix 80286'}, - '41': {'OS': 'RISC / Linux / PowerPC','Description': 'Boot / Old Linux/Minix'}, - '42': {'OS': 'SFS / Linux / Win2K/XP/etc','Description': 'SFS / Old Linux Swap'}, - '43': {'OS': 'Linux','Description': 'Old Linux native'}, - '44': {'OS': 'GoBack','Description': 'Norton/WildFire/Adaptec/Roxio'}, - '45': {'OS': 'Boot-US / EUMEL/ELAN','Description': 'Priam/Boot/EUMEL/ELAN (L2)'}, - '46': {'OS': 'EUMEL/ELAN','Description': 'EUMEL/ELAN (L2)'}, - '47': {'OS': 'EUMEL/ELAN','Description': 'EUMEL/ELAN (L2)'}, - '48': {'OS': 'EUMEL/ELAN','Description': 'EUMEL/ELAN (L2), ERGOS L3'}, - '4A': {'OS': 'AdaOS / ALFS/THIN','Description': 'Aquila / ALFS/THIN'}, - '4C': {'OS': 'ETH Oberon','Description': 'Aos (A2) file system (76)'}, - '4D': {'OS': 'QNX Neutrino','Description': 'Primary QNX POSIX volume'}, - '4E': {'OS': 'QNX Neutrino','Description': 'Secondary QNX POSIX volume'}, - '4F': {'OS': 'QNX Neutrino / ETH Oberon','Description': '3rd QNX POSIX/Boot/Native'}, - '50': {'OS': 'DiskMan4/ETH/LynxOS/Novell','Description': 'Alt FS/Read-only/Lynx RTOS'}, - '51': {'OS': 'Disk Manager 4-6','Description': 'R/W partition (Aux 1)'}, - '52': {'OS': 'CP/M-80/ System V/AT, V/386','Description': 'CP/M-80'}, - '53': {'OS': 'Disk Manager 6','Description': 'Auxiliary 3 (WO)'}, - '54': {'OS': 'Disk Manager 6','Description': 'Dynamic Drive Overlay (DDO)'}, - '55': {'OS': 'EZ-Drive','Description': 'Maxtor/MaxBlast/DriveGuide'}, - '56': {'OS': 'AT&T DOS/EZ-Drive/VFeature','Description': 'FAT12~16/EZ-BIOS/VFeature'}, - '57': {'OS': 'DrivePro','Description': 'VNDI partition'}, - '5C': {'OS': 'EDISK','Description': 'Priam EDisk Volume'}, - '61': {'OS': 'SpeedStor','Description': 'Unknown'}, - '63': {'OS': 'Unix','Description': 'Unix,ISC,SysV,ix,BSD,HURD'}, - '64': {'OS': 'SpeedStor / NetWare','Description': 'NetWare FS 286/2,PC-ARMOUR'}, - '65': {'OS': 'NetWare','Description': 'NetWare File System 386'}, - '66': {'OS': 'NetWare / NetWare','Description': 'NetWare FS 386 / SMS'}, - '67': {'OS': 'NetWare','Description': 'Wolf Mountain'}, - '68': {'OS': 'NetWare','Description': 'Unknown'}, - '69': {'OS': 'NetWare 5 / NetWare','Description': 'Novell Storage Services'}, - '6E': {'Description': 'Unknown'}, - '70': {'OS': 'DiskSecure','Description': 'DiskSecure multiboot'}, - '71': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, - '72': {'OS': 'APTI systems / Unix V7/x86','Description': 'APTI altFAT12 / V7 / x86'}, - '73': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, - '74': {'OS': 'Microsoft, IBM','Description': 'Reserved / Scramdisk'}, - '75': {'OS': 'PC/IX','Description': 'Unknown'}, - '76': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, - '77': {'OS': 'Novell','Description': 'VNDI, M2FS, M2CS'}, - '78': {'OS': 'Geurt Vos','Description': 'XOSL bootloader file system'}, - '79': {'OS': 'APTI conformant systems','Description': 'APTI altFAT16 (CHS, SFN)'}, - '7A': {'OS': 'APTI conformant systems','Description': 'APTI altFAT16 (LBA, SFN)'}, - '7B': {'OS': 'APTI conformant systems','Description': 'APTI altFAT16B (CHS, SFN)'}, - '7C': {'OS': 'APTI conformant systems','Description': 'APTI altFAT32 (LBA, SFN)'}, - '7D': {'OS': 'APTI conformant systems','Description': 'APTI altFAT32 (CHS, SFN)'}, - '7E': {'OS': 'F.I.X. (claim) / PrimoCache','Description': 'Level 2 cache'}, - '7F': {'OS': 'Varies','Description': 'AltOS DevPartition Standard'}, - '80': {'OS': 'Minix 1.1-1.4a','Description': 'Minix file system (old)'}, - '81': {'OS': 'Minix 1.4b+ / Linux','Description': 'MINIX FS/Mitac AdvDiskManager'}, - '82': {'OS': 'Linux / Sun Microsystems','Description': 'Swap / Solaris x86 / Prime'}, - '83': {'OS': 'GNU/Linux','Description': 'Any native Linux FS'}, - '84': {'OS': 'OS/2 / Windows 7','Description': 'Hibernat/HiddenC/RapidStart'}, - '85': {'OS': 'GNU/Linux','Description': 'Linux extended'}, - '86': {'OS': 'Windows NT 4 Server / Linux','Description': 'FAT16B mirror/LinuxRAID-old'}, - '87': {'OS': 'Windows NT 4 Server','Description': 'HPFS/NTFS mirrored volume'}, - '88': {'OS': 'GNU/Linux','Description': 'Plaintext partition table'}, - '8A': {'OS': 'AiR-BOOT','Description': 'Linux kernel image'}, - '8B': {'OS': 'Windows NT 4 Server','Description': 'FAT32 mirrored volume set'}, - '8C': {'OS': 'Windows NT 4 Server','Description': 'FAT32 mirrored volume set'}, - '8D': {'OS': 'Free FDISK','Description': 'Hidden FAT12'}, - '8E': {'OS': 'Linux','Description': 'Linux LVM'}, - '90': {'OS': 'Free FDISK','Description': 'Hidden FAT16'}, - '91': {'OS': 'Free FDISK','Description': 'Hidden extended partition'}, - '92': {'OS': 'Free FDISK','Description': 'Hidden FAT16B'}, - '93': {'OS': 'Amoeba / Linux','Description': 'Amoeba native/Hidden Linux'}, - '94': {'OS': 'Amoeba','Description': 'Amoeba bad block table'}, - '95': {'OS': 'EXOPC','Description': 'EXOPC native'}, - '96': {'OS': 'CHRP','Description': 'ISO-9660 file system'}, - '97': {'OS': 'Free FDISK','Description': 'Hidden FAT32'}, - '98': {'OS': 'Free FDISK / ROM-DOS','Description': 'Hidden FAT32 / service part'}, - '99': {'OS': 'early Unix','Description': 'Unknown'}, - '9A': {'OS': 'Free FDISK','Description': 'Hidden FAT16'}, - '9B': {'OS': 'Free FDISK','Description': 'Hidden extended partition'}, - '9E': {'OS': 'VSTA / ForthOS','Description': 'ForthOS (eForth port)'}, - '9F': {'OS': 'BSD/OS 3.0+, BSDI','Description': 'Unknown'}, - 'A0': {'OS': 'HP/Phoenix/IBM/Toshiba/Sony','Description': 'Diagnostic for HP/Hibernate'}, - 'A1': {'OS': 'HP / Phoenix, NEC','Description': 'HP Vol Expansion/Hibernate'}, - 'A2': {'OS': 'Cyclone V','Description': 'Hard Processor System (HPS)'}, - 'A3': {'OS': 'HP','Description': 'HP Vol Expansion(SpeedStor)'}, - 'A4': {'OS': 'HP','Description': 'HP Vol Expansion(SpeedStor)'}, - 'A5': {'OS': 'BSD','Description': 'BSD slice'}, - 'A6': {'OS': 'OpenBSD','Description': 'HP Vol Expansion/BSD slice'}, - 'A7': {'OS': 'NeXT','Description': 'NeXTSTEP'}, - 'A8': {'OS': 'Darwin, Mac OS X','Description': 'Apple Darwin, Mac OS X UFS'}, - 'A9': {'OS': 'NetBSD','Description': 'NetBSD slice'}, - 'AA': {'OS': 'MS-DOS','Description': 'Olivetti DOS FAT12(1.44 MB)'}, - 'AB': {'OS': 'Darwin, Mac OS X / GO! OS','Description': 'Apple Darwin/OS X boot/GO!'}, - 'AD': {'OS': 'RISC OS','Description': 'ADFS / FileCore format'}, - 'AE': {'OS': 'ShagOS','Description': 'ShagOS file system'}, - 'AF': {'OS': 'ShagOS','Description': 'OS X HFS & HFS+/ShagOS Swap'}, - 'B0': {'OS': 'Boot-Star','Description': 'Boot-Star dummy partition'}, - 'B1': {'OS': 'QNX 6.x','Description': 'HPVolExpansion/QNX Neutrino'}, - 'B2': {'OS': 'QNX 6.x','Description': 'QNX Neutrino power-safe FS'}, - 'B3': {'OS': 'QNX 6.x','Description': 'HPVolExpansion/QNX Neutrino'}, - 'B4': {'OS': 'HP','Description': 'HP Vol Expansion(SpeedStor)'}, - 'B6': {'OS': 'Windows NT 4 Server','Description': 'HPVolExpansion/FAT16Bmirror'}, - 'B7': {'OS': 'BSDI / Windows NT 4 Server','Description': 'BSDI,Swap,HPFS/NTFS mirror'}, - 'B8': {'OS': 'BSDI (before 3.0)','Description': 'BSDI Swap / native FS'}, - 'BB': {'OS': 'Acronis/BootWizard/WinNT 4','Description': 'BootWizard/OEM/FAT32 mirror'}, - 'BC': {'OS': 'Acronis/WinNT/BackupCapsule','Description': 'FAT32RAID/SecureZone/Backup'}, - 'BD': {'OS': 'BonnyDOS/286','Description': 'Unknown'}, - 'BE': {'OS': 'Solaris 8','Description': 'Solaris 8 boot'}, - 'BF': {'OS': 'Solaris','Description': 'Solaris x86'}, - 'C0': {'OS': 'DR-DOS,MultiuserDOS,REAL/32','Description': 'Secured FAT (under 32 MB)'}, - 'C1': {'OS': 'DR DOS','Description': 'Secured FAT12'}, - 'C2': {'OS': 'Power Boot','Description': 'Hidden Linux native FS'}, - 'C3': {'OS': 'Power Boot','Description': 'Hidden Linux Swap'}, - 'C4': {'OS': 'DR DOS','Description': 'Secured FAT16'}, - 'C5': {'OS': 'DR DOS','Description': 'Secured extended partition'}, - 'C6': {'OS': 'DR DOS / WinNT 4 Server','Description': 'Secured FAT16B/FAT16Bmirror'}, - 'C7': {'OS': 'Syrinx / WinNT 4 Server','Description': 'Syrinx boot/HPFS/NTFSmirror'}, - 'C8': {'Description': "DR-DOS Reserved (since '97)"}, - 'C9': {'Description': "DR-DOS Reserved (since '97)"}, - 'CA': {'Description': "DR-DOS Reserved (since '97)"}, - 'CB': {'OS': 'DR-DOSx / WinNT 4 Server','Description': 'Secured FAT32/FAT32 mirror'}, - 'CC': {'OS': 'DR-DOSx / WinNT 4 Server','Description': 'Secured FAT32/FAT32 mirror'}, - 'CD': {'OS': 'CTOS','Description': 'Memory dump'}, - 'CE': {'OS': 'DR-DOSx','Description': 'Secured FAT16B'}, - 'CF': {'OS': 'DR-DOSx','Description': 'Secured extended partition'}, - 'D0': {'OS': 'Multiuser DOS, REAL/32','Description': 'Secured FAT (over 32 MB)'}, - 'D1': {'OS': 'Multiuser DOS','Description': 'Secured FAT12'}, - 'D4': {'OS': 'Multiuser DOS','Description': 'Secured FAT16'}, - 'D5': {'OS': 'Multiuser DOS','Description': 'Secured extended partition'}, - 'D6': {'OS': 'Multiuser DOS','Description': 'Secured FAT16B'}, - 'D8': {'OS': 'Digital Research','Description': 'CP/M-86 [citation needed]'}, - 'DA': {'OS': 'Powercopy Backup','Description': 'Non-FS data / Shielded disk'}, - 'DB': {'OS': 'CP/M-86/CDOS/CTOS/D800/DRMK','Description': 'CP/M-86/ConcDOS/Boot/FAT32'}, - 'DD': {'OS': 'CTOS','Description': 'Hidden memory dump'}, - 'DE': {'OS': 'Dell','Description': 'FAT16 utility/diagnostic'}, - 'DF': {'OS': 'DG/UX / BootIt / Aviion','Description': 'DG/UX Virt DiskMan / EMBRM'}, - 'E0': {'OS': 'STMicroelectronics','Description': 'ST AVFS'}, - 'E1': {'OS': 'SpeedStor','Description': 'ExtendedFAT12 >1023cylinder'}, - 'E2': {'Description': 'DOS read-only (XFDISK)'}, - 'E3': {'OS': 'SpeedStor','Description': 'DOS read-only'}, - 'E4': {'OS': 'SpeedStor','Description': 'ExtendedFAT16 <1024cylinder'}, - 'E5': {'OS': 'Tandy MS-DOS','Description': 'Logical FAT12 or FAT16'}, - 'E6': {'OS': 'SpeedStor','Description': 'Unknown'}, - 'E8': {'OS': 'LUKS','Description': 'Linux Unified Key Setup'}, - 'EB': {'OS': 'BeOS, Haiku','Description': 'BFS'}, - 'EC': {'OS': 'SkyOS','Description': 'SkyFS'}, - 'ED': {'OS': 'Sprytix / EDD 4','Description': 'EDC loader / GPT hybrid MBR'}, - 'EE': {'OS': 'EFI','Description': 'GPT protective MBR'}, - 'EF': {'OS': 'EFI','Description': 'EFI system partition'}, - 'F0': {'OS': 'Linux / OS/32','Description': 'PA-RISC Linux boot loader.'}, - 'F1': {'OS': 'SpeedStor','Description': 'Unknown'}, - 'F2': {'OS': 'SperryIT DOS/Unisys DOS','Description': 'Logical FAT12/FAT16'}, - 'F3': {'OS': 'SpeedStor','Description': 'Unknown'}, - 'F4': {'OS': 'SpeedStor / Prologue','Description': '"large"DOS part/NGF/TwinFS'}, - 'F5': {'OS': 'Prologue','Description': 'MD0-MD9 part for NGF/TwinFS'}, - 'F6': {'OS': 'SpeedStor','Description': 'Unknown'}, - 'F7': {'OS': 'O.S.G. / X1','Description': 'EFAT / Solid State FS'}, - 'F9': {'OS': 'Linux','Description': 'pCache ext2/ext3 cache'}, - 'FA': {'OS': 'Bochs','Description': 'x86 emulator'}, - 'FB': {'OS': 'VMware','Description': 'VMware VMFS partition'}, - 'FC': {'OS': 'VMware','Description': 'Swap / VMKCORE kernel dump'}, - 'FD': {'OS': 'Linux / FreeDOS','Description': 'LinuxRAID/Reserved4FreeDOS'}, - 'FE': {'OS': 'SpeedStor/LANstep/NT/Linux','Description': 'PS/2/DiskAdmin/old LinuxLVM'}, - 'FF': {'OS': 'XENIX','Description': 'XENIX bad block table'}, - '00000000-0000-0000-0000-000000000000': {'Description': 'Unused entry'}, - '024DEE41-33E7-11D3-9D69-0008C781F39F': {'Description': 'MBR partition scheme'}, - 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B': {'Description': 'EFI System partition'}, - '21686148-6449-6E6F-744E-656564454649': {'Description': 'BIOS Boot partition'}, - 'D3BFE2DE-3DAF-11DF-BA40-E3A556D89593': {'Description': 'Intel Fast Flash (iFFS) partition (for Intel Rapid Start technology)'}, - 'F4019732-066E-4E12-8273-346C5641494F': {'Description': 'Sony boot partition'}, - 'BFBFAFE7-A34F-448A-9A5B-6213EB736C22': {'Description': 'Lenovo boot partition'}, - 'E3C9E316-0B5C-4DB8-817D-F92DF00215AE': {'OS': 'Windows', 'Description': 'Microsoft Reserved Partition (MSR)'}, - 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7': {'OS': 'Windows', 'Description': 'Basic data partition'}, - '5808C8AA-7E8F-42E0-85D2-E1E90434CFB3': {'OS': 'Windows', 'Description': 'Logical Disk Manager (LDM) metadata partition'}, - 'AF9B60A0-1431-4F62-BC68-3311714A69AD': {'OS': 'Windows', 'Description': 'Logical Disk Manager data partition'}, - 'DE94BBA4-06D1-4D40-A16A-BFD50179D6AC': {'OS': 'Windows', 'Description': 'Windows Recovery Environment'}, - '37AFFC90-EF7D-4E96-91C3-2D7AE055B174': {'OS': 'Windows', 'Description': 'IBM General Parallel File System (GPFS) partition'}, - 'E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D': {'OS': 'Windows', 'Description': 'Storage Spaces partition'}, - '75894C1E-3AEB-11D3-B7C1-7B03A0000000': {'OS': 'HP-UX', 'Description': 'Data partition'}, - 'E2A1E728-32E3-11D6-A682-7B03A0000000': {'OS': 'HP-UX', 'Description': 'Service Partition'}, - '0FC63DAF-8483-4772-8E79-3D69D8477DE4': {'OS': 'Linux', 'Description': 'Linux filesystem data'}, - 'A19D880F-05FC-4D3B-A006-743F0F84911E': {'OS': 'Linux', 'Description': 'RAID partition'}, - '44479540-F297-41B2-9AF7-D131D5F0458A': {'OS': 'Linux', 'Description': 'Root partition (x86)'}, - '4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709': {'OS': 'Linux', 'Description': 'Root partition (x86-64)'}, - '69DAD710-2CE4-4E3C-B16C-21A1D49ABED3': {'OS': 'Linux', 'Description': 'Root partition (32-bit ARM)'}, - 'B921B045-1DF0-41C3-AF44-4C6F280D3FAE': {'OS': 'Linux', 'Description': 'Root partition (64-bit ARM)/AArch64)'}, - '0657FD6D-A4AB-43C4-84E5-0933C84B4F4F': {'OS': 'Linux', 'Description': 'Swap partition'}, - 'E6D6D379-F507-44C2-A23C-238F2A3DF928': {'OS': 'Linux', 'Description': 'Logical Volume Manager (LVM) partition'}, - '933AC7E1-2EB4-4F13-B844-0E14E2AEF915': {'OS': 'Linux', 'Description': '/home partition'}, - '3B8F8425-20E0-4F3B-907F-1A25A76F98E8': {'OS': 'Linux', 'Description': '/srv (server data) partition'}, - '7FFEC5C9-2D00-49B7-8941-3EA10A5586B7': {'OS': 'Linux', 'Description': 'Plain dm-crypt partition'}, - 'CA7D7CCB-63ED-4C53-861C-1742536059CC': {'OS': 'Linux', 'Description': 'LUKS partition'}, - '8DA63339-0007-60C0-C436-083AC8230908': {'OS': 'Linux', 'Description': 'Reserved'}, - '83BD6B9D-7F41-11DC-BE0B-001560B84F0F': {'OS': 'FreeBSD', 'Description': 'Boot partition'}, - '516E7CB4-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Data partition'}, - '516E7CB5-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Swap partition'}, - '516E7CB6-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Unix File System (UFS) partition'}, - '516E7CB8-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Vinum volume manager partition'}, - '516E7CBA-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'ZFS partition'}, - '48465300-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Hierarchical File System Plus (HFS+) partition'}, - '55465300-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple UFS'}, - '6A898CC3-1DD2-11B2-99A6-080020736631': {'OS': 'OS X Darwin', 'Description': 'ZFS'}, - '52414944-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple RAID partition'}, - '52414944-5F4F-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple RAID partition, offline'}, - '426F6F74-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple Boot partition (Recovery HD)'}, - '4C616265-6C00-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple Label'}, - '5265636F-7665-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple TV Recovery partition'}, - '53746F72-6167-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple Core Storage (i.e. Lion FileVault) partition'}, - '6A82CB45-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Boot partition'}, - '6A85CF4D-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Root partition'}, - '6A87C46F-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Swap partition'}, - '6A8B642B-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Backup partition'}, - '6A898CC3-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': '/usr partition'}, - '6A8EF2E9-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': '/var partition'}, - '6A90BA39-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': '/home partition'}, - '6A9283A5-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Alternate sector'}, - '6A945A3B-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Reserved partition'}, - '6A9630D1-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'}, - '6A980767-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'}, - '6A96237F-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'}, - '6A8D2AC7-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'}, - '49F48D32-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'Swap partition'}, - '49F48D5A-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'FFS partition'}, - '49F48D82-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'LFS partition'}, - '49F48DAA-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'RAID partition'}, - '2DB519C4-B10F-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'Concatenated partition'}, - '2DB519EC-B10F-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'Encrypted partition'}, - 'FE3A2A5D-4F32-41A7-B725-ACCC3285A309': {'OS': 'ChromeOS', 'Description': 'ChromeOS kernel'}, - '3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC': {'OS': 'ChromeOS', 'Description': 'ChromeOS rootfs'}, - '2E0A753D-9E48-43B0-8337-B15192CB1B5E': {'OS': 'ChromeOS', 'Description': 'ChromeOS future use'}, - '42465331-3BA3-10F1-802A-4861696B7521': {'OS': 'Haiku', 'Description': 'Haiku BFS'}, - '85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Boot partition'}, - '85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Data partition'}, - '85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Swap partition'}, - '0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Unix File System (UFS) partition'}, - '85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Vinum volume manager partition'}, - '85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'ZFS partition'}, - '45B0969E-9B03-4F30-B4C6-B4B80CEFF106': {'OS': 'Ceph', 'Description': 'Ceph Journal'}, - '45B0969E-9B03-4F30-B4C6-5EC00CEFF106': {'OS': 'Ceph', 'Description': 'Ceph dm-crypt Encrypted Journal'}, - '4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D': {'OS': 'Ceph', 'Description': 'Ceph OSD'}, - '4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D': {'OS': 'Ceph', 'Description': 'Ceph dm-crypt OSD'}, - '89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE': {'OS': 'Ceph', 'Description': 'Ceph disk in creation'}, - '89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE': {'OS': 'Ceph', 'Description': 'Ceph dm-crypt disk in creation'}, - '824CC7A0-36A8-11E3-890A-952519AD3F61': {'OS': 'OpenBSD', 'Description': 'Data partition'}, - 'CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1': {'OS': 'QNX', 'Description': 'Power-safe (QNX6) file system'}, - 'C91818F9-8025-47AF-89D2-F030D7000C2C': {'OS': 'Plan 9', 'Description': 'Plan 9 partition'}, - '9D275380-40AD-11DB-BF97-000C2911D1B8': {'OS': 'VMware ESX', 'Description': 'vmkcore (coredump partition)'}, - 'AA31E02A-400F-11DB-9590-000C2911D1B8': {'OS': 'VMware ESX', 'Description': 'VMFS filesystem partition'}, - '9198EFFC-31C0-11DB-8F78-000C2911D1B8': {'OS': 'VMware ESX', 'Description': 'VMware Reserved'}, - '2568845D-2332-4675-BC39-8FA5A4748D15': {'OS': 'Android-IA', 'Description': 'Bootloader'}, - '114EAFFE-1552-4022-B26E-9B053604CF84': {'OS': 'Android-IA', 'Description': 'Bootloader2'}, - '49A4D17F-93A3-45C1-A0DE-F50B2EBE2599': {'OS': 'Android-IA', 'Description': 'Boot'}, - '4177C722-9E92-4AAB-8644-43502BFD5506': {'OS': 'Android-IA', 'Description': 'Recovery'}, - 'EF32A33B-A409-486C-9141-9FFB711F6266': {'OS': 'Android-IA', 'Description': 'Misc'}, - '20AC26BE-20B7-11E3-84C5-6CFDB94711E9': {'OS': 'Android-IA', 'Description': 'Metadata'}, - '38F428E6-D326-425D-9140-6E0EA133647C': {'OS': 'Android-IA', 'Description': 'System'}, - 'A893EF21-E428-470A-9E55-0668FD91A2D9': {'OS': 'Android-IA', 'Description': 'Cache'}, - 'DC76DDA9-5AC1-491C-AF42-A82591580C0D': {'OS': 'Android-IA', 'Description': 'Data'}, - 'EBC597D0-2053-4B15-8B64-E0AAC75F4DB1': {'OS': 'Android-IA', 'Description': 'Persistent'}, - '8F68CC74-C5E5-48DA-BE91-A0C8C15E9C80': {'OS': 'Android-IA', 'Description': 'Factory'}, - '767941D0-2085-11E3-AD3B-6CFDB94711E9': {'OS': 'Android-IA', 'Description': 'Fastboot / Tertiary'}, - 'AC6D7924-EB71-4DF8-B48D-E267B27148FF': {'OS': 'Android-IA', 'Description': 'OEM'}, - '7412F7D5-A156-4B13-81DC-867174929325': {'OS': 'ONIE', 'Description': 'Boot'}, - 'D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149': {'OS': 'ONIE', 'Description': 'Config'}, - '9E1A2D38-C612-4316-AA26-8B49521E5A8B': {'OS': 'PowerPC', 'Description': 'PReP boot'}, - 'BC13C2FF-59E6-4262-A352-B275FD6F7172': {'OS': 'Freedesktop', 'Description': 'Extended Boot Partition ($BOOT)'}, -} - -def lookup_guid(guid): - return PARTITION_UIDS.get(guid.upper(), {}) - -if __name__ == '__main__': - print("This file is not meant to be called directly.") diff --git a/.bin/Scripts/functions/product_keys.py b/.bin/Scripts/functions/product_keys.py index 988d36fd..950e509b 100644 --- a/.bin/Scripts/functions/product_keys.py +++ b/.bin/Scripts/functions/product_keys.py @@ -2,110 +2,119 @@ from functions.common import * + # Regex REGEX_REGISTRY_DIRS = re.compile( - r'^(config$|RegBack$|System32$|Transfer|Win)', - re.IGNORECASE) + r'^(config$|RegBack$|System32$|Transfer|Win)', + re.IGNORECASE) REGEX_SOFTWARE_HIVE = re.compile(r'^Software$', re.IGNORECASE) + def extract_keys(): - """Extract keys from provided hives and return a dict.""" - keys = {} + """Extract keys from provided hives and return a dict.""" + keys = {} - # Extract keys - extract_item('ProduKey', silent=True) - for hive in find_software_hives(): - cmd = [ - global_vars['Tools']['ProduKey'], - '/IEKeys', '0', - '/WindowsKeys', '1', - '/OfficeKeys', '1', - '/ExtractEdition', '1', - '/nosavereg', - '/regfile', hive, - '/scomma', ''] - try: - out = run_program(cmd) - except subprocess.CalledProcessError: - # Ignore and return empty dict - pass - else: - for line in out.stdout.decode().splitlines(): - # Add key to keys under product only if unique - tmp = line.split(',') - product = tmp[0] - key = tmp[2] - if product not in keys: - keys[product] = [] - if key not in keys[product]: - keys[product].append(key) + # Extract keys + extract_item('ProduKey', silent=True) + for hive in find_software_hives(): + cmd = [ + global_vars['Tools']['ProduKey'], + '/IEKeys', '0', + '/WindowsKeys', '1', + '/OfficeKeys', '1', + '/ExtractEdition', '1', + '/nosavereg', + '/regfile', hive, + '/scomma', ''] + try: + out = run_program(cmd) + except subprocess.CalledProcessError: + # Ignore and return empty dict + pass + else: + for line in out.stdout.decode().splitlines(): + # Add key to keys under product only if unique + tmp = line.split(',') + product = tmp[0] + key = tmp[2] + if product not in keys: + keys[product] = [] + if key not in keys[product]: + keys[product].append(key) + + # Done + return keys - # Done - return keys def list_clientdir_keys(): - """List product keys found in hives inside the ClientDir.""" - keys = extract_keys() - key_list = [] - if keys: - for product in sorted(keys): - key_list.append(product) - for key in sorted(keys[product]): - key_list.append(' {key}'.format(key=key)) - else: - key_list.append('No keys found.') + """List product keys found in hives inside the ClientDir.""" + keys = extract_keys() + key_list = [] + if keys: + for product in sorted(keys): + key_list.append(product) + for key in sorted(keys[product]): + key_list.append(' {key}'.format(key=key)) + else: + key_list.append('No keys found.') + + return key_list - return key_list def find_software_hives(): - """Search for transferred SW hives and return a list.""" - hives = [] - search_paths = [global_vars['ClientDir']] + """Search for transferred SW hives and return a list.""" + hives = [] + search_paths = [global_vars['ClientDir']] - while len(search_paths) > 0: - for item in os.scandir(search_paths.pop(0)): - if item.is_dir() and REGEX_REGISTRY_DIRS.search(item.name): - search_paths.append(item.path) - if item.is_file() and REGEX_SOFTWARE_HIVE.search(item.name): - hives.append(item.path) + while len(search_paths) > 0: + for item in os.scandir(search_paths.pop(0)): + if item.is_dir() and REGEX_REGISTRY_DIRS.search(item.name): + search_paths.append(item.path) + if item.is_file() and REGEX_SOFTWARE_HIVE.search(item.name): + hives.append(item.path) + + return hives - return hives def get_product_keys(): - """List product keys from saved report.""" - keys = [] - log_file = r'{LogDir}\Product Keys (ProduKey).txt'.format(**global_vars) - with open (log_file, 'r') as f: - for line in f.readlines(): - if re.search(r'^Product Name', line): - line = re.sub(r'^Product Name\s+:\s+(.*)', r'\1', line.strip()) - keys.append(line) + """List product keys from saved report.""" + keys = [] + log_file = r'{LogDir}\Product Keys (ProduKey).txt'.format(**global_vars) + with open (log_file, 'r') as f: + for line in f.readlines(): + if re.search(r'^Product Name', line): + line = re.sub(r'^Product Name\s+:\s+(.*)', r'\1', line.strip()) + keys.append(line) + + if keys: + return keys + else: + return ['No product keys found'] - if keys: - return keys - else: - return ['No product keys found'] def run_produkey(): - """Run ProduKey and save report in the ClientDir.""" - extract_item('ProduKey', silent=True) - log_file = r'{LogDir}\Product Keys (ProduKey).txt'.format(**global_vars) - if not os.path.exists(log_file): - # Clear current configuration - for config in ['ProduKey.cfg', 'ProduKey64.cfg']: - config = r'{BinDir}\ProduKey\{config}'.format( - config=config, **global_vars) - try: - if os.path.exists(config): - os.remove(config) - except Exception: - pass - cmd = [ - global_vars['Tools']['ProduKey'], - '/nosavereg', - '/stext', - log_file] - run_program(cmd, check=False) + """Run ProduKey and save report in the ClientDir.""" + extract_item('ProduKey', silent=True) + log_file = r'{LogDir}\Product Keys (ProduKey).txt'.format(**global_vars) + if not os.path.exists(log_file): + # Clear current configuration + for config in ['ProduKey.cfg', 'ProduKey64.cfg']: + config = r'{BinDir}\ProduKey\{config}'.format( + config=config, **global_vars) + try: + if os.path.exists(config): + os.remove(config) + except Exception: + pass + cmd = [ + global_vars['Tools']['ProduKey'], + '/nosavereg', + '/stext', + log_file] + run_program(cmd, check=False) + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/repairs.py b/.bin/Scripts/functions/repairs.py index 589dccc3..f1e10a4a 100644 --- a/.bin/Scripts/functions/repairs.py +++ b/.bin/Scripts/functions/repairs.py @@ -2,125 +2,135 @@ from functions.common import * + def run_chkdsk(repair=False): - """Run CHKDSK scan or schedule offline repairs.""" - if repair: - run_chkdsk_offline() - else: - run_chkdsk_scan() + """Run CHKDSK scan or schedule offline repairs.""" + if repair: + run_chkdsk_offline() + else: + run_chkdsk_scan() + def run_chkdsk_scan(): - """Run CHKDSK in a "split window" and report errors.""" - if global_vars['OS']['Version'] in ('8', '8.1', '10'): - cmd = ['chkdsk', global_vars['Env']['SYSTEMDRIVE'], '/scan', '/perf'] - else: - cmd = ['chkdsk', global_vars['Env']['SYSTEMDRIVE']] - out = run_program(cmd, check=False) - # retcode == 0: no issues - # retcode == 1: fixed issues (also happens when chkdsk.exe is killed?) - # retcode == 2: issues - if int(out.returncode) > 0: - # print_error(' ERROR: CHKDSK encountered errors') - raise GenericError + """Run CHKDSK in a "split window" and report errors.""" + if global_vars['OS']['Version'] in ('8', '8.1', '10'): + cmd = ['chkdsk', global_vars['Env']['SYSTEMDRIVE'], '/scan', '/perf'] + else: + cmd = ['chkdsk', global_vars['Env']['SYSTEMDRIVE']] + out = run_program(cmd, check=False) + # retcode == 0: no issues + # retcode == 1: fixed issues (also happens when chkdsk.exe is killed?) + # retcode == 2: issues + if int(out.returncode) > 0: + # print_error(' ERROR: CHKDSK encountered errors') + raise GenericError + + # Save stderr + with open(r'{LogDir}\Tools\CHKDSK.err'.format(**global_vars), 'a') as f: + for line in out.stderr.decode().splitlines(): + f.write(line.strip() + '\n') + # Save stdout + with open(r'{LogDir}\Tools\CHKDSK.log'.format(**global_vars), 'a') as f: + for line in out.stdout.decode().splitlines(): + f.write(line.strip() + '\n') - # Save stderr - with open(r'{LogDir}\Tools\CHKDSK.err'.format(**global_vars), 'a') as f: - for line in out.stderr.decode().splitlines(): - f.write(line.strip() + '\n') - # Save stdout - with open(r'{LogDir}\Tools\CHKDSK.log'.format(**global_vars), 'a') as f: - for line in out.stdout.decode().splitlines(): - f.write(line.strip() + '\n') def run_chkdsk_offline(): - """Set filesystem 'dirty bit' to force a chkdsk during next boot.""" - cmd = [ - 'fsutil', 'dirty', - 'set', - global_vars['Env']['SYSTEMDRIVE']] - out = run_program(cmd, check=False) - if int(out.returncode) > 0: - raise GenericError + """Set filesystem 'dirty bit' to force a chkdsk during next boot.""" + cmd = [ + 'fsutil', 'dirty', + 'set', + global_vars['Env']['SYSTEMDRIVE']] + out = run_program(cmd, check=False) + if int(out.returncode) > 0: + raise GenericError + def run_dism(repair=False): - """Run DISM /RestoreHealth, then /CheckHealth, and then report errors.""" - if global_vars['OS']['Version'] in ('8', '8.1', '10'): - if repair: - # Restore Health - cmd = [ - 'DISM', '/Online', - '/Cleanup-Image', '/RestoreHealth', - r'/LogPath:"{LogDir}\Tools\DISM_RestoreHealth.log"'.format( - **global_vars), - '-new_console:n', '-new_console:s33V'] - else: - # Scan Health - cmd = [ - 'DISM', '/Online', - '/Cleanup-Image', '/ScanHealth', - r'/LogPath:"{LogDir}\Tools\DISM_ScanHealth.log"'.format( - **global_vars), - '-new_console:n', '-new_console:s33V'] - run_program(cmd, pipe=False, check=False, shell=True) - wait_for_process('dism') - # Now check health - cmd = [ - 'DISM', '/Online', - '/Cleanup-Image', '/CheckHealth', - r'/LogPath:"{LogDir}\Tools\DISM_CheckHealth.log"'.format(**global_vars)] - result = run_program(cmd, shell=True).stdout.decode() - # Check result - if 'no component store corruption detected' not in result.lower(): - raise GenericError + """Run DISM to either scan or repair component store health.""" + if global_vars['OS']['Version'] in ('8', '8.1', '10'): + if repair: + # Restore Health + cmd = [ + 'DISM', '/Online', + '/Cleanup-Image', '/RestoreHealth', + r'/LogPath:"{LogDir}\Tools\DISM_RestoreHealth.log"'.format( + **global_vars), + '-new_console:n', '-new_console:s33V'] else: - raise UnsupportedOSError + # Scan Health + cmd = [ + 'DISM', '/Online', + '/Cleanup-Image', '/ScanHealth', + r'/LogPath:"{LogDir}\Tools\DISM_ScanHealth.log"'.format( + **global_vars), + '-new_console:n', '-new_console:s33V'] + run_program(cmd, pipe=False, check=False, shell=True) + wait_for_process('dism') + # Now check health + cmd = [ + 'DISM', '/Online', + '/Cleanup-Image', '/CheckHealth', + r'/LogPath:"{LogDir}\Tools\DISM_CheckHealth.log"'.format(**global_vars)] + result = run_program(cmd, shell=True).stdout.decode() + # Check result + if 'no component store corruption detected' not in result.lower(): + raise GenericError + else: + raise UnsupportedOSError + def run_kvrt(): - """Run KVRT.""" - extract_item('KVRT', silent=True) - os.makedirs(global_vars['QuarantineDir'], exist_ok=True) - cmd = [ - global_vars['Tools']['KVRT'], - '-accepteula', '-dontcryptsupportinfo', '-fixednames', - '-d', global_vars['QuarantineDir'], - '-processlevel', '3'] - popen_program(cmd, pipe=False) + """Run KVRT.""" + extract_item('KVRT', silent=True) + os.makedirs(global_vars['QuarantineDir'], exist_ok=True) + cmd = [ + global_vars['Tools']['KVRT'], + '-accepteula', '-dontcryptsupportinfo', '-fixednames', + '-d', global_vars['QuarantineDir'], + '-processlevel', '3'] + popen_program(cmd, pipe=False) + def run_sfc_scan(): - """Run SFC in a "split window" and report errors.""" - cmd = [ - r'{SYSTEMROOT}\System32\sfc.exe'.format(**global_vars['Env']), - '/scannow'] - out = run_program(cmd, check=False) - # Save stderr - with open(r'{LogDir}\Tools\SFC.err'.format(**global_vars), 'a') as f: - for line in out.stderr.decode('utf-8', 'ignore').splitlines(): - f.write(line.strip() + '\n') - # Save stdout - with open(r'{LogDir}\Tools\SFC.log'.format(**global_vars), 'a') as f: - for line in out.stdout.decode('utf-8', 'ignore').splitlines(): - f.write(line.strip() + '\n') - # Check result - log_text = out.stdout.decode('utf-8', 'ignore').replace('\0', '') - if re.findall(r'did\s+not\s+find\s+any\s+integrity\s+violations', log_text): - pass - elif re.findall(r'successfully\s+repaired\s+them', log_text): - raise GenericRepair - else: - raise GenericError + """Run SFC in a "split window" and report errors.""" + cmd = [ + r'{SYSTEMROOT}\System32\sfc.exe'.format(**global_vars['Env']), + '/scannow'] + out = run_program(cmd, check=False) + # Save stderr + with open(r'{LogDir}\Tools\SFC.err'.format(**global_vars), 'a') as f: + for line in out.stderr.decode('utf-8', 'ignore').splitlines(): + f.write(line.strip() + '\n') + # Save stdout + with open(r'{LogDir}\Tools\SFC.log'.format(**global_vars), 'a') as f: + for line in out.stdout.decode('utf-8', 'ignore').splitlines(): + f.write(line.strip() + '\n') + # Check result + log_text = out.stdout.decode('utf-8', 'ignore').replace('\0', '') + if re.findall(r'did\s+not\s+find\s+any\s+integrity\s+violations', log_text): + pass + elif re.findall(r'successfully\s+repaired\s+them', log_text): + raise GenericRepair + else: + raise GenericError + def run_tdsskiller(): - """Run TDSSKiller.""" - extract_item('TDSSKiller', silent=True) - os.makedirs(r'{QuarantineDir}\TDSSKiller'.format( - **global_vars), exist_ok=True) - cmd = [ - global_vars['Tools']['TDSSKiller'], - '-l', r'{LogDir}\Tools\TDSSKiller.log'.format(**global_vars), - '-qpath', r'{QuarantineDir}\TDSSKiller'.format(**global_vars), - '-accepteula', '-accepteulaksn', - '-dcexact', '-tdlfs'] - run_program(cmd, pipe=False) + """Run TDSSKiller.""" + extract_item('TDSSKiller', silent=True) + os.makedirs(r'{QuarantineDir}\TDSSKiller'.format( + **global_vars), exist_ok=True) + cmd = [ + global_vars['Tools']['TDSSKiller'], + '-l', r'{LogDir}\Tools\TDSSKiller.log'.format(**global_vars), + '-qpath', r'{QuarantineDir}\TDSSKiller'.format(**global_vars), + '-accepteula', '-accepteulaksn', + '-dcexact', '-tdlfs'] + run_program(cmd, pipe=False) + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/safemode.py b/.bin/Scripts/functions/safemode.py index 9f44aa04..041cd506 100644 --- a/.bin/Scripts/functions/safemode.py +++ b/.bin/Scripts/functions/safemode.py @@ -2,35 +2,44 @@ from functions.common import * + # STATIC VARIABLES REG_MSISERVER = r'HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot\Network\MSIServer' + def disable_safemode_msi(): - """Disable MSI access under safemode.""" - cmd = ['reg', 'delete', REG_MSISERVER, '/f'] - run_program(cmd) + """Disable MSI access under safemode.""" + cmd = ['reg', 'delete', REG_MSISERVER, '/f'] + run_program(cmd) + def disable_safemode(): - """Edit BCD to remove safeboot value.""" - cmd = ['bcdedit', '/deletevalue', '{default}', 'safeboot'] - run_program(cmd) + """Edit BCD to remove safeboot value.""" + cmd = ['bcdedit', '/deletevalue', '{default}', 'safeboot'] + run_program(cmd) + def enable_safemode_msi(): - """Enable MSI access under safemode.""" - cmd = ['reg', 'add', REG_MSISERVER, '/f'] - run_program(cmd) - cmd = ['reg', 'add', REG_MSISERVER, '/ve', - '/t', 'REG_SZ', '/d', 'Service', '/f'] - run_program(cmd) + """Enable MSI access under safemode.""" + cmd = ['reg', 'add', REG_MSISERVER, '/f'] + run_program(cmd) + cmd = ['reg', 'add', REG_MSISERVER, '/ve', + '/t', 'REG_SZ', '/d', 'Service', '/f'] + run_program(cmd) + def enable_safemode(): - """Edit BCD to set safeboot as default.""" - cmd = ['bcdedit', '/set', '{default}', 'safeboot', 'network'] - run_program(cmd) + """Edit BCD to set safeboot as default.""" + cmd = ['bcdedit', '/set', '{default}', 'safeboot', 'network'] + run_program(cmd) + def reboot(delay=3): - cmd = ['shutdown', '-r', '-t', '{}'.format(delay)] - run_program(cmd, check=False) + cmd = ['shutdown', '-r', '-t', '{}'.format(delay)] + run_program(cmd, check=False) + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/sensors.py b/.bin/Scripts/functions/sensors.py new file mode 100644 index 00000000..7a470703 --- /dev/null +++ b/.bin/Scripts/functions/sensors.py @@ -0,0 +1,244 @@ +# Wizard Kit: Functions - Sensors + +import json +import re + +from functions.tmux import * + + +# STATIC VARIABLES +TEMP_LIMITS = { + 'GREEN': 60, + 'YELLOW': 70, + 'ORANGE': 80, + 'RED': 90, + } + + +# REGEX +REGEX_COLORS = re.compile(r'\033\[\d+;?1?m') + + +def clear_temps(sensor_data): + """Clear saved temps but keep structure, returns dict.""" + for _section, _adapters in sensor_data.items(): + for _adapter, _sources in _adapters.items(): + for _source, _data in _sources.items(): + _data['Temps'] = [] + + +def fix_sensor_str(s): + """Cleanup string and return str.""" + s = re.sub(r'^(\w+)-(\w+)-(\w+)', r'\1 (\2 \3)', s, re.IGNORECASE) + s = s.title() + s = s.replace('Coretemp', 'CoreTemp') + s = s.replace('Acpi', 'ACPI') + s = s.replace('ACPItz', 'ACPI TZ') + s = s.replace('Isa ', 'ISA ') + s = s.replace('Id ', 'ID ') + s = re.sub(r'(\D+)(\d+)', r'\1 \2', s, re.IGNORECASE) + s = s.replace(' ', ' ') + return s + + +def generate_sensor_report( + sensor_data, *temp_labels, + colors=True, core_only=False): + """Generate report based on temp_labels, returns list if str.""" + report = [] + for _section, _adapters in sorted(sensor_data.items()): + # CoreTemps then Other temps + if core_only and 'Core' not in _section: + continue + for _adapter, _sources in sorted(_adapters.items()): + # Adapter + report.append(fix_sensor_str(_adapter)) + for _source, _data in sorted(_sources.items()): + # Source + _line = '{:18} '.format(fix_sensor_str(_source)) + # Temps (skip label for Current) + for _label in temp_labels: + _line += '{}{}{} '.format( + _label.lower() if _label != 'Current' else '', + ': ' if _label != 'Current' else '', + get_temp_str(_data.get(_label, '???'), colors=colors)) + report.append(_line) + if not core_only: + report.append(' ') + + # Handle empty reports (i.e. no sensors detected) + if not report: + report = [ + '{}WARNING: No sensors found{}'.format( + COLORS['YELLOW'] if colors else '', + COLORS['CLEAR'] if colors else ''), + ' ', + 'Please monitor temps manually'] + + # Done + return report + + +def get_colored_temp_str(temp): + """Get colored string based on temp, returns str.""" + try: + temp = float(temp) + except ValueError: + return '{YELLOW}{temp}{CLEAR}'.format(temp=temp, **COLORS) + if temp > TEMP_LIMITS['RED']: + color = COLORS['RED'] + elif temp > TEMP_LIMITS['ORANGE']: + color = COLORS['ORANGE'] + elif temp > TEMP_LIMITS['YELLOW']: + color = COLORS['YELLOW'] + elif temp > TEMP_LIMITS['GREEN']: + color = COLORS['GREEN'] + elif temp > 0: + color = COLORS['BLUE'] + else: + color = COLORS['CLEAR'] + return '{color}{prefix}{temp:2.0f}°C{CLEAR}'.format( + color = color, + prefix = '-' if temp < 0 else '', + temp = temp, + **COLORS) + + +def get_raw_sensor_data(): + """Read sensor data and return dict.""" + data = {} + cmd = ['sensors', '-j'] + + # Get raw data + try: + result = run_program(cmd) + result = result.stdout.decode().splitlines() + except subprocess.CalledProcessError: + # Assuming no sensors available, set to empty list + result = [] + + # Workaround for bad sensors + raw_data = [] + for line in result: + if line.strip() == ',': + # Assuming malformatted line caused by missing data + continue + raw_data.append(line) + + # Parse JSON data + try: + json_data = json.loads('\n'.join(raw_data)) + except json.JSONDecodeError: + # Still broken, just set to empty dict + json_data = {} + + # Done + return json_data + + +def get_sensor_data(): + """Parse raw sensor data and return new dict.""" + json_data = get_raw_sensor_data() + sensor_data = {'CoreTemps': {}, 'Other': {}} + for _adapter, _sources in json_data.items(): + if 'coretemp' in _adapter: + _section = 'CoreTemps' + else: + _section = 'Other' + sensor_data[_section][_adapter] = {} + _sources.pop('Adapter', None) + + # Find current temp and add to dict + ## current temp is labeled xxxx_input + for _source, _labels in _sources.items(): + for _label, _temp in _labels.items(): + if _label.startswith('fan'): + # Skip fan RPMs + continue + if 'input' in _label: + sensor_data[_section][_adapter][_source] = { + 'Current': _temp, + 'Label': _label, + 'Max': _temp, + 'Temps': [_temp], + } + + # Remove empty sections + for k, v in sensor_data.items(): + v = {k2: v2 for k2, v2 in v.items() if v2} + + # Done + return sensor_data + + +def get_temp_str(temp, colors=True): + """Get temp string, returns str.""" + if colors: + return get_colored_temp_str(temp) + try: + temp = float(temp) + except ValueError: + return '{}'.format(temp) + else: + return '{}{:2.0f}°C'.format( + '-' if temp < 0 else '', + temp) + + +def monitor_sensors(monitor_pane, monitor_file): + """Continually update sensor data and report to screen.""" + sensor_data = get_sensor_data() + while True: + update_sensor_data(sensor_data) + with open(monitor_file, 'w') as f: + report = generate_sensor_report(sensor_data, 'Current', 'Max') + f.write('\n'.join(report)) + sleep(1) + if monitor_pane and not tmux_poll_pane(monitor_pane): + break + + +def save_average_temp(sensor_data, temp_label, seconds=10): + """Save average temps under temp_label, returns dict.""" + clear_temps(sensor_data) + + # Get temps + for i in range(seconds): + update_sensor_data(sensor_data) + sleep(1) + + # Calculate averages + for _section, _adapters in sensor_data.items(): + for _adapter, _sources in _adapters.items(): + for _source, _data in _sources.items(): + _data[temp_label] = sum(_data['Temps']) / len(_data['Temps']) + + +def update_sensor_data(sensor_data): + """Read sensors and update existing sensor_data, returns dict.""" + json_data = get_raw_sensor_data() + for _section, _adapters in sensor_data.items(): + for _adapter, _sources in _adapters.items(): + for _source, _data in _sources.items(): + try: + _label = _data['Label'] + _temp = json_data[_adapter][_source][_label] + _data['Current'] = _temp + _data['Max'] = max(_temp, _data['Max']) + _data['Temps'].append(_temp) + except Exception: + # Dumb workound for Dell sensors with changing source names + pass + + +def join_columns(column1, column2, width=55): + return '{:<{}}{}'.format( + column1, + 55+len(column1)-len(REGEX_COLORS.sub('', column1)), + column2) + + +if __name__ == '__main__': + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/setup.py b/.bin/Scripts/functions/setup.py index 8a53aac6..0acb52ad 100644 --- a/.bin/Scripts/functions/setup.py +++ b/.bin/Scripts/functions/setup.py @@ -1,389 +1,436 @@ # Wizard Kit: Functions - Setup -from functions.common import * from functions.update import * from settings.sources import * + # STATIC VARIABLES HKU = winreg.HKEY_USERS HKCR = winreg.HKEY_CLASSES_ROOT HKCU = winreg.HKEY_CURRENT_USER HKLM = winreg.HKEY_LOCAL_MACHINE MOZILLA_FIREFOX_UBO_PATH = r'{}\{}\ublock_origin.xpi'.format( - os.environ.get('PROGRAMFILES'), - r'Mozilla Firefox\distribution\extensions') + os.environ.get('PROGRAMFILES'), + r'Mozilla Firefox\distribution\extensions') OTHER_RESULTS = { - 'Error': { - 'CalledProcessError': 'Unknown Error', - 'FileNotFoundError': 'File not found', - }, - 'Warning': {}} + 'Error': { + 'CalledProcessError': 'Unknown Error', + 'FileNotFoundError': 'File not found', + }, + 'Warning': {}} SETTINGS_CLASSIC_START = { - r'Software\IvoSoft\ClassicShell\Settings': {}, - r'Software\IvoSoft\ClassicStartMenu': { - 'DWORD Items': {'ShowedStyle2': 1}, - }, - r'Software\IvoSoft\ClassicStartMenu\MRU': {}, - r'Software\IvoSoft\ClassicStartMenu\Settings': { - 'DWORD Items': {'SkipMetro': 1}, - 'SZ Items': { - 'MenuStyle': 'Win7', - 'RecentPrograms': 'Recent', - }, - }, - } + r'Software\IvoSoft\ClassicShell\Settings': {}, + r'Software\IvoSoft\ClassicStartMenu': { + 'DWORD Items': {'ShowedStyle2': 1}, + }, + r'Software\IvoSoft\ClassicStartMenu\MRU': {}, + r'Software\IvoSoft\ClassicStartMenu\Settings': { + 'DWORD Items': {'SkipMetro': 1}, + 'SZ Items': { + 'MenuStyle': 'Win7', + 'RecentPrograms': 'Recent', + }, + }, + } SETTINGS_ESET = { - r'Software\ESET\ESET Security\CurrentVersion\gui\UI_CONFIG': { - 'DWORD Items': { - 'FullScreenMode': 0, - 'ShowDesktopAlert': 0, - 'ShowSplash': 0, - }, - }, - } + r'Software\ESET\ESET Security\CurrentVersion\gui\UI_CONFIG': { + 'DWORD Items': { + 'FullScreenMode': 0, + 'ShowDesktopAlert': 0, + 'ShowSplash': 0, + }, + }, + } SETTINGS_EXPLORER_SYSTEM = { - # Disable Location Tracking - r'Software\Microsoft\Windows NT\CurrentVersion\Sensor\Overrides\{BFA794E4-F964-4FDB-90F6-51056BFE4B44}': { - 'DWORD Items': {'SensorPermissionState': 0}, - }, - r'System\CurrentControlSet\Services\lfsvc\Service\Configuration': { - 'Status': {'Value': 0}, - }, - # Disable Telemetry - r'Software\Microsoft\Windows\CurrentVersion\Policies\DataCollection': { - 'DWORD Items': {'AllowTelemetry': 0}, - }, - r'Software\Microsoft\Windows\CurrentVersion\Policies\DataCollection': { - 'DWORD Items': {'AllowTelemetry': 0}, - 'WOW64_32': True, - }, - r'Software\Policies\Microsoft\Windows\DataCollection': { - 'DWORD Items': {'AllowTelemetry': 0}, - }, - # Disable Wi-Fi Sense - r'Software\Microsoft\PolicyManager\default\WiFi\AllowWiFiHotSpotReporting': { - 'DWORD Items': {'Value': 0}, - }, - r'Software\Microsoft\PolicyManager\default\WiFi\AllowAutoConnectToWiFiSenseHotspots': { - 'DWORD Items': {'Value': 0}, - }, - } + # Disable Location Tracking + r'Software\Microsoft\Windows NT\CurrentVersion\Sensor\Overrides\{BFA794E4-F964-4FDB-90F6-51056BFE4B44}': { + 'DWORD Items': {'SensorPermissionState': 0}, + }, + r'System\CurrentControlSet\Services\lfsvc\Service\Configuration': { + 'Status': {'Value': 0}, + }, + # Disable Telemetry + r'Software\Microsoft\Windows\CurrentVersion\Policies\DataCollection': { + 'DWORD Items': {'AllowTelemetry': 0}, + }, + r'Software\Microsoft\Windows\CurrentVersion\Policies\DataCollection': { + 'DWORD Items': {'AllowTelemetry': 0}, + 'WOW64_32': True, + }, + r'Software\Policies\Microsoft\Windows\DataCollection': { + 'DWORD Items': {'AllowTelemetry': 0}, + }, + # Disable Wi-Fi Sense + r'Software\Microsoft\PolicyManager\default\WiFi\AllowWiFiHotSpotReporting': { + 'DWORD Items': {'Value': 0}, + }, + r'Software\Microsoft\PolicyManager\default\WiFi\AllowAutoConnectToWiFiSenseHotspots': { + 'DWORD Items': {'Value': 0}, + }, + } SETTINGS_EXPLORER_USER = { - # Disable silently installed apps - r'Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager': { - 'DWORD Items': {'SilentInstalledAppsEnabled': 0}, - }, - # Disable Tips and Tricks - r'Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager': { - 'DWORD Items': {'SoftLandingEnabled ': 0}, - }, - # Hide People bar - r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced\People': { - 'DWORD Items': {'PeopleBand': 0}, - }, - # Hide Search button / box - r'Software\Microsoft\Windows\CurrentVersion\Search': { - 'DWORD Items': {'SearchboxTaskbarMode': 0}, - }, - # Change default Explorer view to "Computer" - r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced': { - 'DWORD Items': {'LaunchTo': 1}, - }, - } + # Disable silently installed apps + r'Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager': { + 'DWORD Items': {'SilentInstalledAppsEnabled': 0}, + }, + # Disable Tips and Tricks + r'Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager': { + 'DWORD Items': {'SoftLandingEnabled ': 0}, + }, + # Hide People bar + r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced\People': { + 'DWORD Items': {'PeopleBand': 0}, + }, + # Hide Search button / box + r'Software\Microsoft\Windows\CurrentVersion\Search': { + 'DWORD Items': {'SearchboxTaskbarMode': 0}, + }, + # Change default Explorer view to "Computer" + r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced': { + 'DWORD Items': {'LaunchTo': 1}, + }, + } SETTINGS_GOOGLE_CHROME = { - r'Software\Google\Chrome\Extensions\cjpalhdlnbpafiamejdnhcphjbkeiagm': { - 'SZ Items': { - 'update_url': 'https://clients2.google.com/service/update2/crx'}, - 'WOW64_32': True, - }, - r'Software\Google\Chrome\Extensions\pgdnlhfefecpicbbihgmbmffkjpaplco': { - 'SZ Items': { - 'update_url': 'https://clients2.google.com/service/update2/crx'}, - 'WOW64_32': True, - }, - } + r'Software\Google\Chrome\Extensions\cjpalhdlnbpafiamejdnhcphjbkeiagm': { + 'SZ Items': { + 'update_url': 'https://clients2.google.com/service/update2/crx'}, + 'WOW64_32': True, + }, + r'Software\Google\Chrome\Extensions\pgdnlhfefecpicbbihgmbmffkjpaplco': { + 'SZ Items': { + 'update_url': 'https://clients2.google.com/service/update2/crx'}, + 'WOW64_32': True, + }, + } SETTINGS_MOZILLA_FIREFOX_32 = { - r'Software\Mozilla\Firefox\Extensions': { - 'SZ Items': { - 'uBlock0@raymondhill.net': MOZILLA_FIREFOX_UBO_PATH}, - 'WOW64_32': True, - }, - } + r'Software\Mozilla\Firefox\Extensions': { + 'SZ Items': { + 'uBlock0@raymondhill.net': MOZILLA_FIREFOX_UBO_PATH}, + 'WOW64_32': True, + }, + } SETTINGS_MOZILLA_FIREFOX_64 = { - r'Software\Mozilla\Firefox\Extensions': { - 'SZ Items': { - 'uBlock0@raymondhill.net': MOZILLA_FIREFOX_UBO_PATH}, - }, - } + r'Software\Mozilla\Firefox\Extensions': { + 'SZ Items': { + 'uBlock0@raymondhill.net': MOZILLA_FIREFOX_UBO_PATH}, + }, + } SETTINGS_REGBACK = { - # Enable RegBack - r'System\CurrentControlSet\Control\Session Manager\Configuration Manager': { - 'DWORD Items': {'EnablePeriodicBackup': 1}, - }, - } + # Enable RegBack + r'System\CurrentControlSet\Control\Session Manager\Configuration Manager': { + 'DWORD Items': {'EnablePeriodicBackup': 1}, + }, + } VCR_REDISTS = [ - {'Name': 'Visual C++ 2010 x32...', - 'Cmd': [r'2010sp1\x32\vcredist.exe', '/passive', '/norestart']}, - {'Name': 'Visual C++ 2010 x64...', - 'Cmd': [r'2010sp1\x64\vcredist.exe', '/passive', '/norestart']}, - {'Name': 'Visual C++ 2012 Update 4 x32...', - 'Cmd': [r'2012u4\x32\vcredist.exe', '/passive', '/norestart']}, - {'Name': 'Visual C++ 2012 Update 4 x64...', - 'Cmd': [r'2012u4\x64\vcredist.exe', '/passive', '/norestart']}, - {'Name': 'Visual C++ 2013 x32...', - 'Cmd': [r'2013\x32\vcredist.exe', '/install', - '/passive', '/norestart']}, - {'Name': 'Visual C++ 2013 x64...', - 'Cmd': [r'2013\x64\vcredist.exe', '/install', - '/passive', '/norestart']}, - {'Name': 'Visual C++ 2017 x32...', - 'Cmd': [r'2017\x32\vcredist.exe', '/install', - '/passive', '/norestart']}, - {'Name': 'Visual C++ 2017 x64...', - 'Cmd': [r'2017\x64\vcredist.exe', '/install', - '/passive', '/norestart']}, - ] + {'Name': 'Visual C++ 2010 x32...', + 'Cmd': [r'2010sp1\x32\vcredist.exe', '/passive', '/norestart']}, + {'Name': 'Visual C++ 2010 x64...', + 'Cmd': [r'2010sp1\x64\vcredist.exe', '/passive', '/norestart']}, + {'Name': 'Visual C++ 2012 Update 4 x32...', + 'Cmd': [r'2012u4\x32\vcredist.exe', '/passive', '/norestart']}, + {'Name': 'Visual C++ 2012 Update 4 x64...', + 'Cmd': [r'2012u4\x64\vcredist.exe', '/passive', '/norestart']}, + {'Name': 'Visual C++ 2013 x32...', + 'Cmd': [r'2013\x32\vcredist.exe', '/install', + '/passive', '/norestart']}, + {'Name': 'Visual C++ 2013 x64...', + 'Cmd': [r'2013\x64\vcredist.exe', '/install', + '/passive', '/norestart']}, + {'Name': 'Visual C++ 2017 x32...', + 'Cmd': [r'2017\x32\vcredist.exe', '/install', + '/passive', '/norestart']}, + {'Name': 'Visual C++ 2017 x64...', + 'Cmd': [r'2017\x64\vcredist.exe', '/install', + '/passive', '/norestart']}, + ] + def config_classicstart(): - """Configure ClassicStart.""" - # User level, not system level - cs_exe = r'{PROGRAMFILES}\Classic Shell\ClassicStartMenu.exe'.format( - **global_vars['Env']) - skin = r'{PROGRAMFILES}\Classic Shell\Skins\Metro-Win10-Black.skin7'.format( - **global_vars['Env']) - extract_item('ClassicStartSkin', silent=True) + """Configure ClassicStart.""" + # User level, not system level + cs_exe = r'{PROGRAMFILES}\Classic Shell\ClassicStartMenu.exe'.format( + **global_vars['Env']) + skin = r'{PROGRAMFILES}\Classic Shell\Skins\Metro-Win10-Black.skin7'.format( + **global_vars['Env']) + extract_item('ClassicStartSkin', silent=True) - # Stop Classic Start - run_program([cs_exe, '-exit'], check=False) - sleep(1) - kill_process('ClassicStartMenu.exe') + # Stop Classic Start + run_program([cs_exe, '-exit'], check=False) + sleep(1) + kill_process('ClassicStartMenu.exe') - # Configure - write_registry_settings(SETTINGS_CLASSIC_START, all_users=False) - if global_vars['OS']['Version'] == '10' and os.path.exists(skin): - # Enable Win10 theme if on Win10 - key_path = r'Software\IvoSoft\ClassicStartMenu\Settings' - with winreg.OpenKey(HKCU, key_path, access=winreg.KEY_WRITE) as key: - winreg.SetValueEx( - key, 'SkinW7', 0, winreg.REG_SZ, 'Metro-Win10-Black') - winreg.SetValueEx(key, 'SkinVariationW7', 0, winreg.REG_SZ, '') + # Configure + write_registry_settings(SETTINGS_CLASSIC_START, all_users=False) + if global_vars['OS']['Version'] == '10' and os.path.exists(skin): + # Enable Win10 theme if on Win10 + key_path = r'Software\IvoSoft\ClassicStartMenu\Settings' + with winreg.OpenKey(HKCU, key_path, access=winreg.KEY_WRITE) as key: + winreg.SetValueEx( + key, 'SkinW7', 0, winreg.REG_SZ, 'Metro-Win10-Black') + winreg.SetValueEx(key, 'SkinVariationW7', 0, winreg.REG_SZ, '') - # Pin Browser to Start Menu (Classic) - firefox = r'{PROGRAMDATA}\Start Menu\Programs\Mozilla Firefox.lnk'.format( - **global_vars['Env']) - chrome = r'{PROGRAMDATA}\Start Menu\Programs\Google Chrome.lnk'.format( - **global_vars['Env']) - dest_path = r'{APPDATA}\ClassicShell\Pinned'.format(**global_vars['Env']) - source = None - dest = None - if os.path.exists(firefox): - source = firefox - dest = r'{}\Mozilla Firefox.lnk'.format(dest_path) - elif os.path.exists(chrome): - source = chrome - dest = r'{}\Google Chrome.lnk'.format(dest_path) - if source: - try: - os.makedirs(dest_path, exist_ok=True) - shutil.copy(source, dest) - except Exception: - pass # Meh, it's fine without + # Pin Browser to Start Menu (Classic) + firefox = r'{PROGRAMDATA}\Start Menu\Programs\Mozilla Firefox.lnk'.format( + **global_vars['Env']) + chrome = r'{PROGRAMDATA}\Start Menu\Programs\Google Chrome.lnk'.format( + **global_vars['Env']) + dest_path = r'{APPDATA}\ClassicShell\Pinned'.format(**global_vars['Env']) + source = None + dest = None + if os.path.exists(firefox): + source = firefox + dest = r'{}\Mozilla Firefox.lnk'.format(dest_path) + elif os.path.exists(chrome): + source = chrome + dest = r'{}\Google Chrome.lnk'.format(dest_path) + if source: + try: + os.makedirs(dest_path, exist_ok=True) + shutil.copy(source, dest) + except Exception: + pass # Meh, it's fine without + + # (Re)start Classic Start + run_program([cs_exe, '-exit'], check=False) + sleep(1) + kill_process('ClassicStartMenu.exe') + sleep(1) + popen_program(cs_exe) - # (Re)start Classic Start - run_program([cs_exe, '-exit'], check=False) - sleep(1) - kill_process('ClassicStartMenu.exe') - sleep(1) - popen_program(cs_exe) def config_explorer_system(): - """Configure Windows Explorer for all users via Registry settings.""" - write_registry_settings(SETTINGS_EXPLORER_SYSTEM, all_users=True) + """Configure Windows Explorer for all users.""" + write_registry_settings(SETTINGS_EXPLORER_SYSTEM, all_users=True) + def config_explorer_user(): - """Configure Windows Explorer for current user via Registry settings.""" - write_registry_settings(SETTINGS_EXPLORER_USER, all_users=False) + """Configure Windows Explorer for current user.""" + write_registry_settings(SETTINGS_EXPLORER_USER, all_users=False) + + +def create_system_restore_point(): + """Create system restore point.""" + cmd = [ + 'PowerShell', + '-Command', 'Checkpoint-Computer', + '-Description', '1201-Checklist', + ] + run_program(cmd) + def disable_windows_telemetry(): - """Disable Windows 10 telemetry settings with O&O ShutUp10.""" - extract_item('ShutUp10', silent=True) - cmd = [ - r'{BinDir}\ShutUp10\OOSU10.exe'.format(**global_vars), - r'{BinDir}\ShutUp10\1201.cfg'.format(**global_vars), - '/quiet'] - run_program(cmd) + """Disable Windows 10 telemetry settings with O&O ShutUp10.""" + extract_item('ShutUp10', silent=True) + cmd = [ + r'{BinDir}\ShutUp10\OOSU10.exe'.format(**global_vars), + r'{BinDir}\ShutUp10\1201.cfg'.format(**global_vars), + '/quiet'] + run_program(cmd) + def enable_regback(): - """Enable RegBack.""" - write_registry_settings(SETTINGS_REGBACK, all_users=True) + """Enable RegBack.""" + write_registry_settings(SETTINGS_REGBACK, all_users=True) + + +def enable_mini_dumps(): + """Configure Windows to save mini dumps during BSoDs.""" + cmd = ['wmic', 'RECOVEROS', 'set', 'DebugInfoType', '=', '3'] + run_program(cmd) + def enable_system_restore(): - """Enable System Restore and set disk usage to 5%""" - cmd = [ - 'PowerShell', - '-Command', 'Enable-ComputerRestore', - '-Drive', '{}\\'.format(global_vars['Env']['SYSTEMDRIVE'])] - run_program(cmd) + """Enable System Restore and set disk usage to 5%""" + cmd = [ + 'PowerShell', + '-Command', 'Enable-ComputerRestore', + '-Drive', '{}\\'.format(global_vars['Env']['SYSTEMDRIVE'])] + run_program(cmd) - # Set disk usage - cmd = [ - r'{}\System32\vssadmin.exe'.format(global_vars['Env']['SYSTEMROOT']), - 'resize', 'shadowstorage', - '/on={}'.format(global_vars['Env']['SYSTEMDRIVE']), - '/for={}'.format(global_vars['Env']['SYSTEMDRIVE']), - '/maxsize=5%'] - run_program(cmd) + # Set disk usage + cmd = [ + r'{}\System32\vssadmin.exe'.format(global_vars['Env']['SYSTEMROOT']), + 'resize', 'shadowstorage', + '/on={}'.format(global_vars['Env']['SYSTEMDRIVE']), + '/for={}'.format(global_vars['Env']['SYSTEMDRIVE']), + '/maxsize=8%'] + run_program(cmd) def update_clock(): - """Set Timezone and sync clock.""" - run_program(['tzutil' ,'/s', WINDOWS_TIME_ZONE], check=False) - run_program(['net', 'stop', 'w32ime'], check=False) - run_program( - ['w32tm', '/config', '/syncfromflags:manual', - '/manualpeerlist:"us.pool.ntp.org time.nist.gov time.windows.com"', - ], - check=False) - run_program(['net', 'start', 'w32ime'], check=False) - run_program(['w32tm', '/resync', '/nowait'], check=False) + """Set Timezone and sync clock.""" + run_program(['tzutil' ,'/s', WINDOWS_TIME_ZONE], check=False) + run_program(['net', 'stop', 'w32ime'], check=False) + run_program( + ['w32tm', '/config', '/syncfromflags:manual', + '/manualpeerlist:"us.pool.ntp.org time.nist.gov time.windows.com"', + ], + check=False) + run_program(['net', 'start', 'w32ime'], check=False) + run_program(['w32tm', '/resync', '/nowait'], check=False) + def write_registry_settings(settings, all_users=False): - """Write registry values from custom dict of dicts.""" - hive = HKCU - if all_users: - hive = HKLM - for k, v in settings.items(): - # CreateKey - access = winreg.KEY_WRITE - if 'WOW64_32' in v: - access = access | winreg.KEY_WOW64_32KEY - winreg.CreateKeyEx(hive, k, 0, access) + """Write registry values from custom dict of dicts.""" + hive = HKCU + if all_users: + hive = HKLM + for k, v in settings.items(): + # CreateKey + access = winreg.KEY_WRITE + if 'WOW64_32' in v: + access = access | winreg.KEY_WOW64_32KEY + winreg.CreateKeyEx(hive, k, 0, access) + + # Create values + with winreg.OpenKeyEx(hive, k, 0, access) as key: + for name, value in v.get('DWORD Items', {}).items(): + winreg.SetValueEx(key, name, 0, winreg.REG_DWORD, value) + for name, value in v.get('SZ Items', {}).items(): + winreg.SetValueEx(key, name, 0, winreg.REG_SZ, value) - # Create values - with winreg.OpenKeyEx(hive, k, 0, access) as key: - for name, value in v.get('DWORD Items', {}).items(): - winreg.SetValueEx(key, name, 0, winreg.REG_DWORD, value) - for name, value in v.get('SZ Items', {}).items(): - winreg.SetValueEx(key, name, 0, winreg.REG_SZ, value) # Installations def install_adobe_reader(): - """Install Adobe Reader.""" - cmd = [ - r'{BaseDir}\Installers\Extras\Office\Adobe Reader DC.exe'.format( - **global_vars), - '/sAll', - '/msi', '/norestart', '/quiet', - 'ALLUSERS=1', - 'EULA_ACCEPT=YES'] - run_program(cmd) + """Install Adobe Reader.""" + cmd = [ + r'{BaseDir}\Installers\Extras\Office\Adobe Reader DC.exe'.format( + **global_vars), + '/sAll', + '/msi', '/norestart', '/quiet', + 'ALLUSERS=1', + 'EULA_ACCEPT=YES'] + run_program(cmd) + def install_chrome_extensions(): - """Update registry to install Google Chrome extensions for all users.""" - write_registry_settings(SETTINGS_GOOGLE_CHROME, all_users=True) + """Install Google Chrome extensions for all users.""" + write_registry_settings(SETTINGS_GOOGLE_CHROME, all_users=True) + def install_classicstart_skin(): - """Extract ClassicStart skin to installation folder.""" - if global_vars['OS']['Version'] not in ('8', '8.1', '10'): - raise UnsupportedOSError - extract_item('ClassicStartSkin', silent=True) - source = r'{BinDir}\ClassicStartSkin\Metro-Win10-Black.skin7'.format( - **global_vars) - dest_path = r'{PROGRAMFILES}\Classic Shell\Skins'.format( - **global_vars['Env']) - dest = r'{}\Metro-Win10-Black.skin7'.format(dest_path) - os.makedirs(dest_path, exist_ok=True) - shutil.copy(source, dest) + """Extract ClassicStart skin to installation folder.""" + if global_vars['OS']['Version'] not in ('8', '8.1', '10'): + raise UnsupportedOSError + extract_item('ClassicStartSkin', silent=True) + source = r'{BinDir}\ClassicStartSkin\Metro-Win10-Black.skin7'.format( + **global_vars) + dest_path = r'{PROGRAMFILES}\Classic Shell\Skins'.format( + **global_vars['Env']) + dest = r'{}\Metro-Win10-Black.skin7'.format(dest_path) + os.makedirs(dest_path, exist_ok=True) + shutil.copy(source, dest) + def install_eset_nod32_av(scan_pups=True): - """Install ESET NOD32 AV with custom config.""" - extract_item('ESETConfigs', silent=True) - config_file = r'{BinDir}\ESETConfigs\{config_file}.xml'.format( - config_file='eset-config' if scan_pups else 'eset-config-no-pup', - **global_vars) + """Install ESET NOD32 AV with custom config.""" + extract_item('ESETConfigs', silent=True) + config_file = r'{BinDir}\ESETConfigs\{config_file}.xml'.format( + config_file='eset-config' if scan_pups else 'eset-config-no-pup', + **global_vars) - # Apply user configuration - write_registry_settings(SETTINGS_ESET, all_users=False) + # Apply user configuration + write_registry_settings(SETTINGS_ESET, all_users=False) - # Download - result = try_and_print(message='Downloading Setup...', cs='Done', - other_results=OTHER_RESULTS, function=download_generic, - out_dir=global_vars['ClientDir'], - out_name='eav_nt64.exe', - source_url=SOURCE_URLS['ESET NOD32 AV']) - installer = r'{ClientDir}\eav_nt64.exe'.format(**global_vars) - if not result['CS']: - raise GenericError('Failed to download ESET NOD32 AV') + # Download + result = try_and_print(message='Downloading Setup...', cs='Done', + other_results=OTHER_RESULTS, function=download_generic, + out_dir=global_vars['ClientDir'], + out_name='eav_nt64.exe', + source_url=SOURCE_URLS['ESET NOD32 AV']) + installer = r'{ClientDir}\eav_nt64.exe'.format(**global_vars) + if not result['CS']: + raise GenericError('Failed to download ESET NOD32 AV') - # Install - cmd = [installer, - '--silent', '--accepteula', '--msi-property', - 'PRODUCTTYPE=eav', 'PRODUCT_LANG=1033', 'PRODUCT_LANG_CODE=en-US', - 'ADMINCFG="{}"'.format(config_file)] - try_and_print(message='Installing ESET NOD32 AV...', - other_results=OTHER_RESULTS, function=run_program, cmd=cmd) - - # Delete installer - remove_item(installer) + # Install + cmd = [installer, + '--silent', '--accepteula', '--msi-property', + 'PRODUCTTYPE=eav', 'PRODUCT_LANG=1033', 'PRODUCT_LANG_CODE=en-US', + 'ADMINCFG="{}"'.format(config_file)] + try_and_print(message='Installing ESET NOD32 AV...', + other_results=OTHER_RESULTS, function=run_program, cmd=cmd) + + # Delete installer + remove_item(installer) def install_firefox_extensions(): - """Update registry to install Firefox extensions for all users.""" - dist_path = r'{PROGRAMFILES}\Mozilla Firefox\distribution\extensions'.format( - **global_vars['Env']) - source_path = r'{CBinDir}\FirefoxExtensions.7z'.format(**global_vars) - if not os.path.exists(source_path): - raise FileNotFoundError + """Install Firefox extensions for all users.""" + dist_path = r'{PROGRAMFILES}\Mozilla Firefox\distribution\extensions'.format( + **global_vars['Env']) + source_path = r'{CBinDir}\FirefoxExtensions.7z'.format(**global_vars) + if not os.path.exists(source_path): + raise FileNotFoundError - # Update registry - write_registry_settings(SETTINGS_MOZILLA_FIREFOX_32, all_users=True) - write_registry_settings(SETTINGS_MOZILLA_FIREFOX_64, all_users=True) + # Update registry + write_registry_settings(SETTINGS_MOZILLA_FIREFOX_32, all_users=True) + write_registry_settings(SETTINGS_MOZILLA_FIREFOX_64, all_users=True) - # Extract extension(s) to distribution folder - cmd = [ - global_vars['Tools']['SevenZip'], 'e', '-aos', '-bso0', '-bse0', - '-p{ArchivePassword}'.format(**global_vars), - '-o{dist_path}'.format(dist_path=dist_path), - source_path] - run_program(cmd) + # Extract extension(s) to distribution folder + cmd = [ + global_vars['Tools']['SevenZip'], 'e', '-aos', '-bso0', '-bse0', + '-p{ArchivePassword}'.format(**global_vars), + '-o{dist_path}'.format(dist_path=dist_path), + source_path] + run_program(cmd) + + +def install_ninite_bundle(mse=False, libreoffice=False): + """Run Ninite installer(s), returns list of Popen objects.""" + popen_objects = [] + if global_vars['OS']['Version'] in ('8', '8.1', '10'): + # Modern selection + popen_objects.append( + popen_program(r'{BaseDir}\Installers\Extras\Bundles\Modern.exe'.format( + **global_vars))) + else: + # Legacy selection + if mse: + cmd = r'{BaseDir}\Installers\Extras\Security'.format(**global_vars) + cmd += r'\Microsoft Security Essentials.exe' + popen_objects.append(popen_program(cmd)) + popen_objects.append( + popen_program(r'{BaseDir}\Installers\Extras\Bundles\Legacy.exe'.format( + **global_vars))) + + # LibreOffice + if libreoffice: + cmd = r'{BaseDir}\Installers\Extras\Office'.format(**global_vars) + cmd += r'\LibreOffice.exe' + popen_objects.append(popen_program(cmd)) + + # Done + return popen_objects -def install_ninite_bundle(mse=False): - """Run Ninite file(s) based on OS version.""" - if global_vars['OS']['Version'] in ('8', '8.1', '10'): - # Modern selection - popen_program(r'{BaseDir}\Installers\Extras\Bundles\Modern.exe'.format( - **global_vars)) - else: - # Legacy selection - if mse: - cmd = r'{BaseDir}\Installers\Extras\Security'.format(**global_vars) - cmd += r'\Microsoft Security Essentials.exe' - popen_program(cmd) - popen_program(r'{BaseDir}\Installers\Extras\Bundles\Legacy.exe'.format( - **global_vars)) def install_vcredists(): - """Install all supported Visual C++ runtimes.""" - extract_item('_vcredists', silent=True) - prev_dir = os.getcwd() - try: - os.chdir(r'{BinDir}\_vcredists'.format(**global_vars)) - except FileNotFoundError: - # Ignored since the loop below will report the errors - pass - for vcr in VCR_REDISTS: - try_and_print(message=vcr['Name'], function=run_program, - cmd=vcr['Cmd'], other_results=OTHER_RESULTS) + """Install all supported Visual C++ runtimes.""" + extract_item('_vcredists', silent=True) + prev_dir = os.getcwd() + try: + os.chdir(r'{BinDir}\_vcredists'.format(**global_vars)) + except FileNotFoundError: + # Ignored since the loop below will report the errors + pass + for vcr in VCR_REDISTS: + try_and_print(message=vcr['Name'], function=run_program, + cmd=vcr['Cmd'], other_results=OTHER_RESULTS) + + os.chdir(prev_dir) - os.chdir(prev_dir) # Misc def open_device_manager(): - popen_program(['mmc', 'devmgmt.msc']) + popen_program(['mmc', 'devmgmt.msc']) + def open_windows_activation(): - popen_program(['slui']) + popen_program(['slui']) + def open_windows_updates(): - popen_program(['control', '/name', 'Microsoft.WindowsUpdate']) + popen_program(['control', '/name', 'Microsoft.WindowsUpdate']) + if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/sw_diags.py b/.bin/Scripts/functions/sw_diags.py new file mode 100644 index 00000000..100e5ad5 --- /dev/null +++ b/.bin/Scripts/functions/sw_diags.py @@ -0,0 +1,231 @@ +# Wizard Kit: Functions - Diagnostics + +import ctypes + +from functions.common import * + + +# STATIC VARIABLES +AUTORUNS_SETTINGS = { + r'Software\Sysinternals\AutoRuns': { + 'checkvirustotal': 1, + 'EulaAccepted': 1, + 'shownomicrosoft': 1, + 'shownowindows': 1, + 'showonlyvirustotal': 1, + 'submitvirustotal': 0, + 'verifysignatures': 1, + }, + r'Software\Sysinternals\AutoRuns\SigCheck': { + 'EulaAccepted': 1, + }, + r'Software\Sysinternals\AutoRuns\Streams': { + 'EulaAccepted': 1, + }, + r'Software\Sysinternals\AutoRuns\VirusTotal': { + 'VirusTotalTermsAccepted': 1, + }, + } + + +def check_connection(): + """Check if the system is online and optionally abort the script.""" + while True: + result = try_and_print(message='Ping test...', function=ping, cs='OK') + if result['CS']: + break + if not ask('ERROR: System appears offline, try again?'): + if ask('Continue anyway?'): + break + else: + abort() + + +def check_secure_boot_status(show_alert=False): + """Checks UEFI Secure Boot status via PowerShell.""" + boot_mode = get_boot_mode() + cmd = ['PowerShell', '-Command', 'Confirm-SecureBootUEFI'] + result = run_program(cmd, check=False) + + # Check results + if result.returncode == 0: + out = result.stdout.decode() + if 'True' in out: + # It's on, do nothing + return + elif 'False' in out: + if show_alert: + show_alert_box('Secure Boot DISABLED') + raise SecureBootDisabledError + else: + if show_alert: + show_alert_box('Secure Boot status UNKNOWN') + raise SecureBootUnknownError + else: + if boot_mode != 'UEFI': + if (show_alert and + global_vars['OS']['Version'] in ('8', '8.1', '10')): + # OS supports Secure Boot + show_alert_box('Secure Boot DISABLED\n\nOS installed LEGACY') + raise OSInstalledLegacyError + else: + # Check error message + err = result.stderr.decode() + if 'Cmdlet not supported' in err: + if show_alert: + show_alert_box('Secure Boot UNAVAILABLE?') + raise SecureBootNotAvailError + else: + if show_alert: + show_alert_box('Secure Boot ERROR') + raise GenericError + + +def get_boot_mode(): + """Check if Windows is booted in UEFI or Legacy mode, returns str.""" + kernel = ctypes.windll.kernel32 + firmware_type = ctypes.c_uint() + + # Get value from kernel32 API + try: + kernel.GetFirmwareType(ctypes.byref(firmware_type)) + except: + # Just set to zero + firmware_type = ctypes.c_uint(0) + + # Set return value + type_str = 'Unknown' + if firmware_type.value == 1: + type_str = 'Legacy' + elif firmware_type.value == 2: + type_str = 'UEFI' + + return type_str + + +def os_is_unsupported(show_alert=False): + """Checks if the current OS is unsupported, returns bool.""" + msg = '' + unsupported = False + + # Check OS version/notes + os_info = global_vars['OS'].copy() + if os_info['Notes'] == 'unsupported': + msg = 'The installed version of Windows is no longer supported' + unsupported = True + elif os_info['Notes'] == 'preview build': + msg = 'Preview builds are not officially supported' + unsupported = True + elif os_info['Version'] == '10' and os_info['Notes'] == 'outdated': + msg = 'The installed version of Windows is outdated' + unsupported = True + if 'Preview' not in msg: + msg += '\n\nPlease consider upgrading before continuing setup.' + + # Show alert + if unsupported and show_alert: + show_alert_box(msg) + + # Done + return unsupported + + +def run_autoruns(): + """Run AutoRuns in the background with VirusTotal checks enabled.""" + extract_item('Autoruns', filter='autoruns*', silent=True) + # Update AutoRuns settings before running + for path, settings in AUTORUNS_SETTINGS.items(): + winreg.CreateKey(HKCU, path) + with winreg.OpenKey(HKCU, path, access=winreg.KEY_WRITE) as key: + for name, value in settings.items(): + winreg.SetValueEx(key, name, 0, winreg.REG_DWORD, value) + popen_program(global_vars['Tools']['AutoRuns'], minimized=True) + + +def run_hwinfo_sensors(): + """Run HWiNFO sensors.""" + path = r'{BinDir}\HWiNFO'.format(**global_vars) + for bit in [32, 64]: + # Configure + source = r'{}\general.ini'.format(path) + dest = r'{}\HWiNFO{}.ini'.format(path, bit) + shutil.copy(source, dest) + with open(dest, 'a') as f: + f.write('SensorsOnly=1\n') + f.write('SummaryOnly=0\n') + popen_program(global_vars['Tools']['HWiNFO']) + + +def run_nircmd(*cmd): + """Run custom NirCmd.""" + extract_item('NirCmd', silent=True) + cmd = [global_vars['Tools']['NirCmd'], *cmd] + run_program(cmd, check=False) + + +def run_xmplay(): + """Run XMPlay to test audio.""" + extract_item('XMPlay', silent=True) + cmd = [global_vars['Tools']['XMPlay'], + r'{BinDir}\XMPlay\music.7z'.format(**global_vars)] + + # Unmute audio first + extract_item('NirCmd', silent=True) + run_nircmd('mutesysvolume', '0') + + # Open XMPlay + popen_program(cmd) + + +def run_hitmanpro(): + """Run HitmanPro in the background.""" + extract_item('HitmanPro', silent=True) + cmd = [ + global_vars['Tools']['HitmanPro'], + '/quiet', '/noinstall', '/noupload', + r'/log={LogDir}\Tools\HitmanPro.txt'.format(**global_vars)] + popen_program(cmd) + + +def run_process_killer(): + """Kill most running processes skipping those in the whitelist.txt.""" + # borrowed from TronScript (reddit.com/r/TronScript) + # credit to /u/cuddlychops06 + prev_dir = os.getcwd() + extract_item('ProcessKiller', silent=True) + os.chdir(r'{BinDir}\ProcessKiller'.format(**global_vars)) + run_program(['ProcessKiller.exe', '/silent'], check=False) + os.chdir(prev_dir) + + +def run_rkill(): + """Run RKill and cleanup afterwards.""" + extract_item('RKill', silent=True) + cmd = [ + global_vars['Tools']['RKill'], + '-s', '-l', r'{LogDir}\Tools\RKill.log'.format(**global_vars), + '-new_console:n', '-new_console:s33V'] + run_program(cmd, check=False) + wait_for_process('RKill') + + # RKill cleanup + desktop_path = r'{USERPROFILE}\Desktop'.format(**global_vars['Env']) + if os.path.exists(desktop_path): + for item in os.scandir(desktop_path): + if re.search(r'^RKill', item.name, re.IGNORECASE): + dest = r'{LogDir}\Tools\{name}'.format( + name=dest, **global_vars) + dest = non_clobber_rename(dest) + shutil.move(item.path, dest) + + +def show_alert_box(message, title='Wizard Kit Warning'): + """Show Windows alert box with message.""" + message_box = ctypes.windll.user32.MessageBoxW + message_box(None, message, title, 0x00001030) + + +if __name__ == '__main__': + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/threading.py b/.bin/Scripts/functions/threading.py new file mode 100644 index 00000000..dfac69c7 --- /dev/null +++ b/.bin/Scripts/functions/threading.py @@ -0,0 +1,47 @@ +# Wizard Kit: Functions - Threading + +from threading import Thread +from queue import Queue, Empty + +# Classes +class NonBlockingStreamReader(): + """Class to allow non-blocking reads from a stream.""" + # Credits: + ## https://gist.github.com/EyalAr/7915597 + ## https://stackoverflow.com/a/4896288 + + def __init__(self, stream): + self.stream = stream + self.queue = Queue() + + def populate_queue(stream, queue): + """Collect lines from stream and put them in queue.""" + while True: + line = stream.read(1) + if line: + queue.put(line) + + self.thread = start_thread( + populate_queue, + args=(self.stream, self.queue)) + + def read(self, timeout=None): + try: + return self.queue.get(block = timeout is not None, + timeout = timeout) + except Empty: + return None + + +# Functions +def start_thread(function, args=[], daemon=True): + """Run function as thread in background, returns Thread object.""" + thread = Thread(target=function, args=args, daemon=daemon) + thread.start() + return thread + + +if __name__ == '__main__': + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/tmux.py b/.bin/Scripts/functions/tmux.py new file mode 100644 index 00000000..e2d1b333 --- /dev/null +++ b/.bin/Scripts/functions/tmux.py @@ -0,0 +1,160 @@ +# Wizard Kit: Functions - tmux + +from functions.common import * + + +def create_file(filepath): + """Create file if it doesn't exist.""" + if not os.path.exists(filepath): + with open(filepath, 'w') as f: + f.write('') + + +def tmux_get_pane_size(pane_id=None): + """Get target, or current, pane size, returns tuple.""" + x = -1 + y = -1 + cmd = ['tmux', 'display', '-p'] + if pane_id: + cmd.extend(['-t', pane_id]) + cmd.append('#{pane_width} #{pane_height}') + + # Run cmd and set x & y + result = run_program(cmd, check=False) + try: + x, y = result.stdout.decode().strip().split() + x = int(x) + y = int(y) + except Exception: + # Ignore and return unrealistic values + pass + + return (x, y) + + +def tmux_kill_all_panes(pane_id=None): + """Kill all tmux panes except the active pane or pane_id if specified.""" + cmd = ['tmux', 'kill-pane', '-a'] + if pane_id: + cmd.extend(['-t', pane_id]) + run_program(cmd, check=False) + + +def tmux_kill_pane(*panes): + """Kill tmux pane by id.""" + cmd = ['tmux', 'kill-pane', '-t'] + for pane_id in panes: + if not pane_id: + # Skip empty strings, None values, etc + continue + run_program(cmd+[pane_id], check=False) + + +def tmux_poll_pane(pane_id): + """Check if pane exists, returns bool.""" + cmd = ['tmux', 'list-panes', '-F', '#D'] + result = run_program(cmd, check=False) + panes = result.stdout.decode().splitlines() + return pane_id in panes + + +def tmux_resize_pane(pane_id=None, x=None, y=None, **kwargs): + """Resize pane to specific hieght or width.""" + if not x and not y: + raise Exception('Neither height nor width specified.') + + cmd = ['tmux', 'resize-pane'] + if pane_id: + # NOTE: If pane_id not specified then the current pane will be resized + cmd.extend(['-t', pane_id]) + if x: + cmd.extend(['-x', str(x)]) + elif y: + cmd.extend(['-y', str(y)]) + + run_program(cmd, check=False) + + +def tmux_split_window( + lines=None, percent=None, + behind=False, vertical=False, + follow=False, target_pane=None, + command=None, working_dir=None, + text=None, watch=None, watch_cmd='cat'): + """Run tmux split-window command and return pane_id as str.""" + # Bail early + if not lines and not percent: + raise Exception('Neither lines nor percent specified.') + if not command and not text and not watch: + raise Exception('No command, text, or watch file specified.') + + # Build cmd + cmd = ['tmux', 'split-window', '-PF', '#D'] + if behind: + cmd.append('-b') + if vertical: + cmd.append('-v') + else: + cmd.append('-h') + if not follow: + cmd.append('-d') + if lines is not None: + cmd.extend(['-l', str(lines)]) + elif percent is not None: + cmd.extend(['-p', str(percent)]) + if target_pane: + cmd.extend(['-t', str(target_pane)]) + + if working_dir: + cmd.extend(['-c', working_dir]) + if command: + cmd.extend(command) + elif text: + cmd.extend(['echo-and-hold "{}"'.format(text)]) + elif watch: + create_file(watch) + if watch_cmd == 'cat': + cmd.extend([ + 'watch', '--color', '--no-title', + '--interval', '1', + 'cat', watch]) + elif watch_cmd == 'tail': + cmd.extend(['tail', '-f', watch]) + + # Run and return pane_id + result = run_program(cmd) + return result.stdout.decode().strip() + + +def tmux_update_pane( + pane_id, command=None, working_dir=None, + text=None, watch=None, watch_cmd='cat'): + """Respawn with either a new command or new text.""" + # Bail early + if not command and not text and not watch: + raise Exception('No command, text, or watch file specified.') + + cmd = ['tmux', 'respawn-pane', '-k', '-t', pane_id] + if working_dir: + cmd.extend(['-c', working_dir]) + if command: + cmd.extend(command) + elif text: + cmd.extend(['echo-and-hold "{}"'.format(text)]) + elif watch: + create_file(watch) + if watch_cmd == 'cat': + cmd.extend([ + 'watch', '--color', '--no-title', + '--interval', '1', + 'cat', watch]) + elif watch_cmd == 'tail': + cmd.extend(['tail', '-f', watch]) + + run_program(cmd) + + +if __name__ == '__main__': + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/update.py b/.bin/Scripts/functions/update.py index fcd997d8..9ea639ce 100644 --- a/.bin/Scripts/functions/update.py +++ b/.bin/Scripts/functions/update.py @@ -2,1093 +2,1115 @@ import requests -from functions.common import * from functions.data import * from settings.launchers import * from settings.music import * from settings.sources import * + def compress_and_remove_item(item): - """Compress and delete an item unless an error is encountered.""" - try: - compress_item(item) - except: - raise GenericError - else: - remove_item(item.path) + """Compress and delete an item unless an error is encountered.""" + try: + compress_item(item) + except: + raise GenericError + else: + remove_item(item.path) + def compress_item(item): - """Compress an item in a 7-Zip archive using the ARCHIVE_PASSWORD.""" - # Prep - prev_dir = os.getcwd() - dest = '{}.7z'.format(item.path) - wd = item.path - include_str = '*' - if os.path.isfile(wd): - wd = os.path.abspath(r'{}\{}'.format(wd, os.path.pardir)) - include_str = item.name - os.chdir(wd) + """Compress an item in a 7-Zip archive using the ARCHIVE_PASSWORD.""" + # Prep + prev_dir = os.getcwd() + dest = '{}.7z'.format(item.path) + wd = item.path + include_str = '*' + if os.path.isfile(wd): + wd = os.path.abspath(r'{}\{}'.format(wd, os.path.pardir)) + include_str = item.name + os.chdir(wd) - # Compress - cmd = [ - global_vars['Tools']['SevenZip'], - 'a', dest, - '-t7z', '-mx=7', '-myx=7', '-ms=on', '-mhe', '-bso0', '-bse0', - '-p{}'.format(ARCHIVE_PASSWORD), - include_str, - ] - run_program(cmd) + # Compress + cmd = [ + global_vars['Tools']['SevenZip'], + 'a', dest, + '-t7z', '-mx=7', '-myx=7', '-ms=on', '-mhe', '-bso0', '-bse0', + '-p{}'.format(ARCHIVE_PASSWORD), + include_str, + ] + run_program(cmd) + + # Done + os.chdir(prev_dir) - # Done - os.chdir(prev_dir) def download_generic(out_dir, out_name, source_url): - """Downloads a file using requests.""" - ## Code based on this Q&A: https://stackoverflow.com/q/16694907 - ### Asked by: https://stackoverflow.com/users/427457/roman-podlinov - ### Edited by: https://stackoverflow.com/users/657427/christophe-roussy - ### Using answer: https://stackoverflow.com/a/39217788 - ### Answer from: https://stackoverflow.com/users/4323/john-zwinck - os.makedirs(out_dir, exist_ok=True) - out_path = '{}/{}'.format(out_dir, out_name) - try: - r = requests.get(source_url, stream=True) - with open(out_path, 'wb') as f: - shutil.copyfileobj(r.raw, f) - r.close() - except: - raise GenericError('Failed to download file.') + """Downloads a file using requests.""" + ## Code based on this Q&A: https://stackoverflow.com/q/16694907 + ### Asked by: https://stackoverflow.com/users/427457/roman-podlinov + ### Edited by: https://stackoverflow.com/users/657427/christophe-roussy + ### Using answer: https://stackoverflow.com/a/39217788 + ### Answer from: https://stackoverflow.com/users/4323/john-zwinck + os.makedirs(out_dir, exist_ok=True) + out_path = '{}/{}'.format(out_dir, out_name) + try: + r = requests.get(source_url, stream=True) + with open(out_path, 'wb') as f: + shutil.copyfileobj(r.raw, f) + r.close() + except: + raise GenericError('Failed to download file.') + def download_to_temp(out_name, source_url): - """Download a file to the TmpDir.""" - download_generic(global_vars['TmpDir'], out_name, source_url) + """Download a file to the TmpDir.""" + download_generic(global_vars['TmpDir'], out_name, source_url) + def extract_generic(source, dest, mode='x', sz_args=[]): - """Extract a file to a destination.""" - cmd = [ - global_vars['Tools']['SevenZip'], - mode, source, r'-o{}'.format(dest), - '-aoa', '-bso0', '-bse0', - ] - cmd.extend(sz_args) - run_program(cmd) + """Extract a file to a destination.""" + cmd = [ + global_vars['Tools']['SevenZip'], + mode, source, r'-o{}'.format(dest), + '-aoa', '-bso0', '-bse0', + ] + cmd.extend(sz_args) + run_program(cmd) + def extract_temp_to_bin(source, item, mode='x', sz_args=[]): - """Extract a file to the .bin folder.""" - source = r'{}\{}'.format(global_vars['TmpDir'], source) - dest = r'{}\{}'.format(global_vars['BinDir'], item) - extract_generic(source, dest, mode, sz_args) + """Extract a file to the .bin folder.""" + source = r'{}\{}'.format(global_vars['TmpDir'], source) + dest = r'{}\{}'.format(global_vars['BinDir'], item) + extract_generic(source, dest, mode, sz_args) + def extract_temp_to_cbin(source, item, mode='x', sz_args=[]): - """Extract a file to the .cbin folder.""" - source = r'{}\{}'.format(global_vars['TmpDir'], source) - dest = r'{}\{}'.format(global_vars['CBinDir'], item) - include_path = r'{}\_include\{}'.format(global_vars['CBinDir'], item) - if os.path.exists(include_path): - shutil.copytree(include_path, dest) - extract_generic(source, dest, mode, sz_args) + """Extract a file to the .cbin folder.""" + source = r'{}\{}'.format(global_vars['TmpDir'], source) + dest = r'{}\{}'.format(global_vars['CBinDir'], item) + include_path = r'{}\_include\{}'.format(global_vars['CBinDir'], item) + if os.path.exists(include_path): + shutil.copytree(include_path, dest) + extract_generic(source, dest, mode, sz_args) + def generate_launcher(section, name, options): - """Generate a launcher script.""" - # Prep - dest = r'{}\{}'.format(global_vars['BaseDir'], section) - if section == '(Root)': - dest = global_vars['BaseDir'] - full_path = r'{}\{}.cmd'.format(dest, name) - template = r'{}\Scripts\Launcher_Template.cmd'.format(global_vars['BinDir']) + """Generate a launcher script.""" + # Prep + dest = r'{}\{}'.format(global_vars['BaseDir'], section) + if section == '(Root)': + dest = global_vars['BaseDir'] + full_path = r'{}\{}.cmd'.format(dest, name) + template = r'{}\Scripts\Launcher_Template.cmd'.format(global_vars['BinDir']) - # Format options - f_options = {} - for opt in options.keys(): - # Values need to be a list to support the multi-line extra code sections - if opt == 'Extra Code': - f_options['rem EXTRA_CODE'] = options['Extra Code'] - elif re.search(r'^L_\w+', opt, re.IGNORECASE): - new_opt = 'set {}='.format(opt) - f_options[new_opt] = ['set {}={}'.format(opt, options[opt])] + # Format options + f_options = {} + for opt in options.keys(): + # Values need to be a list to support the multi-line extra code sections + if opt == 'Extra Code': + f_options['rem EXTRA_CODE'] = options['Extra Code'] + elif re.search(r'^L_\w+', opt, re.IGNORECASE): + new_opt = 'set {}='.format(opt) + f_options[new_opt] = ['set {}={}'.format(opt, options[opt])] - # Read template and update using f_options - out_text = [] - with open(template, 'r') as f: - for line in f.readlines(): - # Strip all lines to let Python handle/correct the CRLF endings - line = line.strip() - if line in f_options: - # Extend instead of append to support extra code sections - out_text.extend(f_options[line]) - else: - out_text.append(line) + # Read template and update using f_options + out_text = [] + with open(template, 'r') as f: + for line in f.readlines(): + # Strip all lines to let Python handle/correct the CRLF endings + line = line.strip() + if line in f_options: + # Extend instead of append to support extra code sections + out_text.extend(f_options[line]) + else: + out_text.append(line) + + # Write file + os.makedirs(dest, exist_ok=True) + with open(full_path, 'w') as f: + # f.writelines(out_text) + f.write('\n'.join(out_text)) - # Write file - os.makedirs(dest, exist_ok=True) - with open(full_path, 'w') as f: - # f.writelines(out_text) - f.write('\n'.join(out_text)) def remove_item(item_path): - """Delete a file or folder.""" - if os.path.exists(item_path): - if os.path.isdir(item_path): - shutil.rmtree(item_path, ignore_errors=True) - else: - os.remove(item_path) + """Delete a file or folder.""" + if os.path.exists(item_path): + if os.path.isdir(item_path): + shutil.rmtree(item_path, ignore_errors=True) + else: + os.remove(item_path) + def remove_from_kit(item): - """Delete a file or folder from the .bin/.cbin folders.""" - item_locations = [] - for p in [global_vars['BinDir'], global_vars['CBinDir']]: - item_locations.append(r'{}\{}'.format(p, item)) - item_locations.append(r'{}\{}.7z'.format(p, item)) - item_locations.append(r'{}\_Drivers\{}'.format(p, item)) - item_locations.append(r'{}\_Drivers\{}.7z'.format(p, item)) - for item_path in item_locations: - remove_item(item_path) - -def remove_from_temp(item): - """Delete a file or folder from the TmpDir folder.""" - item_path = r'{}\{}'.format(global_vars['TmpDir'], item) + """Delete a file or folder from the .bin/.cbin folders.""" + item_locations = [] + for p in [global_vars['BinDir'], global_vars['CBinDir']]: + item_locations.append(r'{}\{}'.format(p, item)) + item_locations.append(r'{}\{}.7z'.format(p, item)) + item_locations.append(r'{}\_Drivers\{}'.format(p, item)) + item_locations.append(r'{}\_Drivers\{}.7z'.format(p, item)) + for item_path in item_locations: remove_item(item_path) + +def remove_from_temp(item): + """Delete a file or folder from the TmpDir folder.""" + item_path = r'{}\{}'.format(global_vars['TmpDir'], item) + remove_item(item_path) + + def resolve_dynamic_url(source_url, regex): - """Scan source_url for a url using the regex provided; returns str.""" - # Load the download page - try: - download_page = requests.get(source_url) - except Exception: - # "Fail silently as the download_to_temp() function will catch it - return None + """Scan source_url for a url using the regex provided; returns str.""" + # Load the download page + try: + download_page = requests.get(source_url) + except Exception: + # "Fail silently as the download_to_temp() function will catch it + return None - # Scan for the url using the regex provided - url = None - for line in download_page.content.decode().splitlines(): - if re.search(regex, line): - url = line.strip() - url = re.sub(r'.*(a |)href="([^"]+)".*', r'\2', url) - url = re.sub(r".*(a |)href='([^']+)'.*", r'\2', url) - break + # Scan for the url using the regex provided + url = None + for line in download_page.content.decode().splitlines(): + if re.search(regex, line): + url = line.strip() + url = re.sub(r'.*(a |)href="([^"]+)".*', r'\2', url) + url = re.sub(r".*(a |)href='([^']+)'.*", r'\2', url) + break - # Return - return url + # Return + return url -def scan_for_net_installers(server, family_name, min_year): - """Scan network shares for installers.""" - if not server['Mounted']: - mount_network_share(server) - if server['Mounted']: - for year in os.scandir(r'\\{IP}\{Share}'.format(**server)): - try: - year_ok = int(year.name) < min_year - except ValueError: - year_ok = False # Skip non-year items - if year_ok: - # Don't support outdated installers - continue - for version in os.scandir(year.path): - section = r'Installers\Extras\{}\{}'.format( - family_name, year.name) - if section not in LAUNCHERS: - LAUNCHERS[section] = {} - name = version.name - if re.search(r'(exe|msi)$', name, re.IGNORECASE): - name = name[:-4] - if name not in LAUNCHERS[section]: - LAUNCHERS[section][name] = { - 'L_TYPE': family_name, - 'L_PATH': year.name, - 'L_ITEM': version.name, - } - umount_network_share(server) - -## Data Recovery ## +# Data Recovery def update_testdisk(): - # Stop running processes - for exe in ['fidentify_win.exe', 'photorec_win.exe', - 'qphotorec_win.exe', 'testdisk_win.exe']: - kill_process(exe) + # Stop running processes + for exe in ['fidentify_win.exe', 'photorec_win.exe', + 'qphotorec_win.exe', 'testdisk_win.exe']: + kill_process(exe) - # Remove existing folders - remove_from_kit('TestDisk') + # Remove existing folders + remove_from_kit('TestDisk') - # Download - download_to_temp('testdisk_wip.zip', SOURCE_URLS['TestDisk']) + # Download + download_to_temp('testdisk_wip.zip', SOURCE_URLS['TestDisk']) - # Extract files - extract_temp_to_cbin('testdisk_wip.zip', 'TestDisk') - dest = r'{}\TestDisk'.format(global_vars['CBinDir']) - for item in os.scandir(r'{}\testdisk-7.1-WIP'.format(dest)): - dest_item = '{}\{}'.format(dest, item.name) - if not os.path.exists(dest_item): - shutil.move(item.path, dest_item) - shutil.rmtree( - r'{}\TestDisk\testdisk-7.1-WIP'.format(global_vars['CBinDir'])) + # Extract files + extract_temp_to_cbin('testdisk_wip.zip', 'TestDisk') + dest = r'{}\TestDisk'.format(global_vars['CBinDir']) + for item in os.scandir(r'{}\testdisk-7.1-WIP'.format(dest)): + dest_item = '{}\{}'.format(dest, item.name) + if not os.path.exists(dest_item): + shutil.move(item.path, dest_item) + shutil.rmtree( + r'{}\TestDisk\testdisk-7.1-WIP'.format(global_vars['CBinDir'])) - # Cleanup - remove_from_temp('testdisk_wip.zip') + # Cleanup + remove_from_temp('testdisk_wip.zip') -## Data Transfers ## + +# Data Transfers def update_fastcopy(): - ## NOTE: Lives in .bin uncompressed - # Stop running processes - for process in ['FastCopy.exe', 'FastCopy64.exe']: - kill_process(process) + ## NOTE: Lives in .bin uncompressed + # Stop running processes + for process in ['FastCopy.exe', 'FastCopy64.exe']: + kill_process(process) - # Remove existing folders - remove_from_kit('FastCopy') + # Remove existing folders + remove_from_kit('FastCopy') - # Download - download_to_temp('FastCopy.zip', SOURCE_URLS['FastCopy']) + # Download installer + download_to_temp('FastCopy.exe', SOURCE_URLS['FastCopy']) + _installer = r'{}\FastCopy.exe'.format(global_vars['TmpDir']) - # Extract installer - extract_temp_to_bin('FastCopy.zip', 'FastCopy') - _path = r'{}\FastCopy'.format(global_vars['BinDir']) - _installer = 'FastCopy354_installer.exe' + # Extract 32-bit + _path32 = r'{}\FastCopy'.format(global_vars['BinDir']) + cmd = [ + _installer, + '/NOSUBDIR', '/DIR={}'.format(_path32), + '/EXTRACT32'] + run_program(cmd) - # Extract 64-bit - cmd = [ - r'{}\{}'.format(_path, _installer), - '/NOSUBDIR', '/DIR={}'.format(_path), - '/EXTRACT64'] - run_program(cmd) - shutil.move( - r'{}\FastCopy\FastCopy.exe'.format(global_vars['BinDir']), - r'{}\FastCopy\FastCopy64.exe'.format(global_vars['BinDir'])) + # Extract 64-bit + _path64 = r'{}\FastCopyTmp'.format(global_vars['TmpDir']) + cmd = [ + _installer, + '/NOSUBDIR', '/DIR={}'.format(_path64), + '/EXTRACT64'] + run_program(cmd) + shutil.move( + r'{}\FastCopy.exe'.format(_path64), + r'{}\FastCopy64.exe'.format(_path32)) - # Extract 32-bit - cmd = [ - r'{}\{}'.format(_path, _installer), - '/NOSUBDIR', '/DIR={}'.format(_path), - '/EXTRACT32'] - run_program(cmd) + # Cleanup + remove_item(r'{}\setup.exe'.format(_path32)) + remove_item(_path64) + remove_from_temp('FastCopy.exe') - # Cleanup - os.remove(r'{}\{}'.format(_path, _installer)) - os.remove(r'{}\setup.exe'.format(_path, _installer)) - remove_from_temp('FastCopy.zip') def update_linux_reader(): - # Stop running processes - for exe in ['LinuxReader.exe', 'LinuxReader64.exe']: - kill_process(exe) + # Stop running processes + for exe in ['LinuxReader.exe', 'LinuxReader64.exe']: + kill_process(exe) - # Remove existing folders - remove_from_kit('LinuxReader') + # Remove existing folders + remove_from_kit('LinuxReader') - # Prep - install_dir = r'{}\LinuxReaderTemp'.format(global_vars['TmpDir']) - dest = r'{}\LinuxReader'.format(global_vars['CBinDir']) - uninstaller = None + # Prep + install_dir = r'{}\LinuxReaderTemp'.format(global_vars['TmpDir']) + dest = r'{}\LinuxReader'.format(global_vars['CBinDir']) + uninstaller = None - # Download - download_to_temp('LinuxReader.exe', SOURCE_URLS['Linux Reader']) + # Download + download_to_temp('LinuxReader.exe', SOURCE_URLS['Linux Reader']) - # Install to temp - cmd = [ - r'{}\LinuxReader.exe'.format(global_vars['TmpDir']), - '/S', - '/D={}'.format(install_dir)] + # Install to temp + cmd = [ + r'{}\LinuxReader.exe'.format(global_vars['TmpDir']), + '/S', + '/D={}'.format(install_dir)] + run_program(cmd) + + # Copy files + shutil.copytree(install_dir, dest) + for item in os.scandir(dest): + r = re.search(r'^uninstall.*(dat|exe)$', item.name, re.IGNORECASE) + if r: + if 'exe' in item.name: + uninstaller = r'{}\{}'.format(install_dir, item.name) + remove_item(item.path) + + # Uninstall from temp + if uninstaller: + cmd = [uninstaller, '/S'] run_program(cmd) - # Copy files - shutil.copytree(install_dir, dest) - for item in os.scandir(dest): - r = re.search(r'^uninstall.*(dat|exe)$', item.name, re.IGNORECASE) - if r: - if 'exe' in item.name: - uninstaller = r'{}\{}'.format(install_dir, item.name) - remove_item(item.path) - - # Uninstall from temp - if uninstaller: - cmd = [uninstaller, '/S'] - run_program(cmd) - - # Cleanup - remove_from_temp('LinuxReader.exe') + # Cleanup + remove_from_temp('LinuxReader.exe') def update_wimlib(): - # Stop running processes - kill_process('wimlib-imagex.exe') + # Stop running processes + kill_process('wimlib-imagex.exe') - # Remove existing folders - remove_from_kit('wimlib') + # Remove existing folders + remove_from_kit('wimlib') - # Download - download_to_temp('wimlib32.zip', SOURCE_URLS['wimlib32']) - download_to_temp('wimlib64.zip', SOURCE_URLS['wimlib64']) + # Download + download_to_temp('wimlib32.zip', SOURCE_URLS['wimlib32']) + download_to_temp('wimlib64.zip', SOURCE_URLS['wimlib64']) - # Extract - extract_generic( - r'{}\wimlib32.zip'.format(global_vars['TmpDir']), - r'{}\wimlib\x32'.format(global_vars['CBinDir'])) - extract_generic( - r'{}\wimlib64.zip'.format(global_vars['TmpDir']), - r'{}\wimlib\x64'.format(global_vars['CBinDir'])) + # Extract + extract_generic( + r'{}\wimlib32.zip'.format(global_vars['TmpDir']), + r'{}\wimlib\x32'.format(global_vars['CBinDir'])) + extract_generic( + r'{}\wimlib64.zip'.format(global_vars['TmpDir']), + r'{}\wimlib\x64'.format(global_vars['CBinDir'])) + + # Cleanup + remove_from_temp('wimlib32.zip') + remove_from_temp('wimlib64.zip') - # Cleanup - remove_from_temp('wimlib32.zip') - remove_from_temp('wimlib64.zip') def update_xyplorer(): - # Stop running processes - kill_process('XYplorerFree.exe') + # Stop running processes + kill_process('XYplorerFree.exe') - # Remove existing folders - remove_from_kit('XYplorerFree') + # Remove existing folders + remove_from_kit('XYplorerFree') - # Download - download_to_temp('xyplorer_free.zip', SOURCE_URLS['XYplorerFree']) + # Download + download_to_temp('xyplorer_free.zip', SOURCE_URLS['XYplorerFree']) - # Extract files - extract_temp_to_cbin('xyplorer_free.zip', 'XYplorerFree') + # Extract files + extract_temp_to_cbin('xyplorer_free.zip', 'XYplorerFree') - # Cleanup - remove_from_temp('xyplorer_free.zip') + # Cleanup + remove_from_temp('xyplorer_free.zip') -## Diagnostics ## + +# Diagnostics def update_aida64(): - # Stop running processes - kill_process('notepadplusplus.exe') + # Stop running processes + kill_process('notepadplusplus.exe') - # Remove existing folders - remove_from_kit('AIDA64') + # Remove existing folders + remove_from_kit('AIDA64') - # Download - download_to_temp('aida64.zip', SOURCE_URLS['AIDA64']) + # Download + download_to_temp('aida64.zip', SOURCE_URLS['AIDA64']) - # Extract files - extract_temp_to_cbin('aida64.zip', 'AIDA64') + # Extract files + extract_temp_to_cbin('aida64.zip', 'AIDA64') + + # Cleanup + remove_from_temp('aida64.zip') - # Cleanup - remove_from_temp('aida64.zip') def update_autoruns(): - # Stop running processes - kill_process('Autoruns.exe') - kill_process('Autoruns64.exe') + # Stop running processes + kill_process('Autoruns.exe') + kill_process('Autoruns64.exe') - # Remove existing folders - remove_from_kit('Autoruns') + # Remove existing folders + remove_from_kit('Autoruns') - # Download - download_to_temp('Autoruns.zip', SOURCE_URLS['Autoruns']) + # Download + download_to_temp('Autoruns.zip', SOURCE_URLS['Autoruns']) - # Extract files - extract_temp_to_cbin('Autoruns.zip', 'Autoruns') + # Extract files + extract_temp_to_cbin('Autoruns.zip', 'Autoruns') + + # Cleanup + remove_from_temp('Autoruns.zip') - # Cleanup - remove_from_temp('Autoruns.zip') def update_bleachbit(): - # Stop running processes - kill_process('bleachbit.exe') + # Stop running processes + kill_process('bleachbit.exe') - # Remove existing folders - remove_from_kit('BleachBit') + # Remove existing folders + remove_from_kit('BleachBit') - # Download - download_to_temp('bleachbit.zip', SOURCE_URLS['BleachBit']) - download_to_temp('Winapp2.zip', SOURCE_URLS['Winapp2']) + # Download + download_to_temp('bleachbit.zip', SOURCE_URLS['BleachBit']) + download_to_temp('Winapp2.zip', SOURCE_URLS['Winapp2']) - # Extract files - extract_temp_to_cbin('bleachbit.zip', 'BleachBit') - extract_generic( - r'{}\Winapp2.zip'.format(global_vars['TmpDir']), - r'{}\BleachBit\cleaners'.format(global_vars['CBinDir']), - mode='e', sz_args=[r'Winapp2-master\Non-CCleaner\Winapp2.ini']) + # Extract files + extract_temp_to_cbin('bleachbit.zip', 'BleachBit') + extract_generic( + r'{}\Winapp2.zip'.format(global_vars['TmpDir']), + r'{}\BleachBit\cleaners'.format(global_vars['CBinDir']), + mode='e', sz_args=[r'Winapp2-master\Non-CCleaner\Winapp2.ini']) - # Move files into place - dest = r'{}\BleachBit'.format(global_vars['CBinDir']) - for item in os.scandir(r'{}\BleachBit-Portable'.format(dest)): - dest_item = '{}\{}'.format(dest, item.name) - if not os.path.exists(dest_item): - shutil.move(item.path, dest_item) - shutil.rmtree( - r'{}\BleachBit\BleachBit-Portable'.format(global_vars['CBinDir'])) + # Move files into place + dest = r'{}\BleachBit'.format(global_vars['CBinDir']) + for item in os.scandir(r'{}\BleachBit-Portable'.format(dest)): + dest_item = '{}\{}'.format(dest, item.name) + if not os.path.exists(dest_item): + shutil.move(item.path, dest_item) + shutil.rmtree( + r'{}\BleachBit\BleachBit-Portable'.format(global_vars['CBinDir'])) + + # Cleanup + remove_from_temp('bleachbit.zip') + remove_from_temp('Winapp2.zip') - # Cleanup - remove_from_temp('bleachbit.zip') - remove_from_temp('Winapp2.zip') def update_bluescreenview(): - # Stop running processes - for exe in ['BlueScreenView.exe', 'BlueScreenView64.exe']: - kill_process(exe) + # Stop running processes + for exe in ['BlueScreenView.exe', 'BlueScreenView64.exe']: + kill_process(exe) - # Remove existing folders - remove_from_kit('BlueScreenView') + # Remove existing folders + remove_from_kit('BlueScreenView') - # Download - download_to_temp('bluescreenview32.zip', SOURCE_URLS['BlueScreenView32']) - download_to_temp('bluescreenview64.zip', SOURCE_URLS['BlueScreenView64']) + # Download + download_to_temp('bluescreenview32.zip', SOURCE_URLS['BlueScreenView32']) + download_to_temp('bluescreenview64.zip', SOURCE_URLS['BlueScreenView64']) - # Extract files - extract_temp_to_cbin('bluescreenview64.zip', 'BlueScreenView', sz_args=['BlueScreenView.exe']) - shutil.move( - r'{}\BlueScreenView\BlueScreenView.exe'.format(global_vars['CBinDir']), - r'{}\BlueScreenView\BlueScreenView64.exe'.format(global_vars['CBinDir'])) - extract_temp_to_cbin('bluescreenview32.zip', 'BlueScreenView') + # Extract files + extract_temp_to_cbin( + 'bluescreenview64.zip', 'BlueScreenView', sz_args=['BlueScreenView.exe']) + shutil.move( + r'{}\BlueScreenView\BlueScreenView.exe'.format(global_vars['CBinDir']), + r'{}\BlueScreenView\BlueScreenView64.exe'.format(global_vars['CBinDir'])) + extract_temp_to_cbin('bluescreenview32.zip', 'BlueScreenView') + + # Cleanup + remove_from_temp('bluescreenview32.zip') + remove_from_temp('bluescreenview64.zip') - # Cleanup - remove_from_temp('bluescreenview32.zip') - remove_from_temp('bluescreenview64.zip') def update_erunt(): - # Stop running processes - kill_process('ERUNT.EXE') + # Stop running processes + kill_process('ERUNT.EXE') - # Remove existing folders - remove_from_kit('ERUNT') + # Remove existing folders + remove_from_kit('ERUNT') - # Download - download_to_temp('erunt.zip', SOURCE_URLS['ERUNT']) + # Download + download_to_temp('erunt.zip', SOURCE_URLS['ERUNT']) - # Extract files - extract_temp_to_cbin('erunt.zip', 'ERUNT') + # Extract files + extract_temp_to_cbin('erunt.zip', 'ERUNT') + + # Cleanup + remove_from_temp('erunt.zip') - # Cleanup - remove_from_temp('erunt.zip') def update_furmark(): - # Stop running processes - for exe in ['cpuburner.exe', 'FurMark.exe', 'gpushark.exe', 'gpuz.exe']: - kill_process(exe) + # Stop running processes + for exe in ['cpuburner.exe', 'FurMark.exe', 'gpushark.exe', 'gpuz.exe']: + kill_process(exe) - # Remove existing folders - remove_from_kit('FurMark') + # Remove existing folders + remove_from_kit('FurMark') - # Prep - install_dir = r'{}\FurMarkTemp'.format(global_vars['TmpDir']) - dest = r'{}\FurMark'.format(global_vars['CBinDir']) - uninstaller = None + # Prep + install_dir = r'{}\FurMarkTemp'.format(global_vars['TmpDir']) + dest = r'{}\FurMark'.format(global_vars['CBinDir']) + uninstaller = None - # Download - download_to_temp('furmark_setup.exe', SOURCE_URLS['FurMark']) + # Download + download_to_temp('furmark_setup.exe', SOURCE_URLS['FurMark']) - # Install to temp - cmd = [ - r'{}\furmark_setup.exe'.format(global_vars['TmpDir']), - '/DIR={}'.format(install_dir), - '/SILENT'] + # Install to temp + cmd = [ + r'{}\furmark_setup.exe'.format(global_vars['TmpDir']), + '/DIR={}'.format(install_dir), + '/SILENT'] + run_program(cmd) + + # Copy files + shutil.copytree(install_dir, dest) + for item in os.scandir(dest): + r = re.search(r'^unins\d+\.(dat|exe)$', item.name, re.IGNORECASE) + if r: + if 'exe' in item.name: + uninstaller = r'{}\{}'.format(install_dir, item.name) + remove_item(item.path) + + # Uninstall from temp + if uninstaller: + cmd = [uninstaller, '/SILENT'] run_program(cmd) - # Copy files - shutil.copytree(install_dir, dest) - for item in os.scandir(dest): - r = re.search(r'^unins\d+\.(dat|exe)$', item.name, re.IGNORECASE) - if r: - if 'exe' in item.name: - uninstaller = r'{}\{}'.format(install_dir, item.name) - remove_item(item.path) - - # Uninstall from temp - if uninstaller: - cmd = [uninstaller, '/SILENT'] - run_program(cmd) - - # Cleanup - remove_from_temp('furmark_setup.exe') + # Cleanup + remove_from_temp('furmark_setup.exe') def update_hitmanpro(): - # Stop running processes - for exe in ['HitmanPro.exe', 'HitmanPro64.exe']: - kill_process(exe) + # Stop running processes + for exe in ['HitmanPro.exe', 'HitmanPro64.exe']: + kill_process(exe) - # Remove existing folders - remove_from_kit('HitmanPro') + # Remove existing folders + remove_from_kit('HitmanPro') + + # Download + dest = r'{}\HitmanPro'.format(global_vars['CBinDir']) + download_generic(dest, 'HitmanPro.exe', SOURCE_URLS['HitmanPro32']) + download_generic(dest, 'HitmanPro64.exe', SOURCE_URLS['HitmanPro64']) - # Download - dest = r'{}\HitmanPro'.format(global_vars['CBinDir']) - download_generic(dest, 'HitmanPro.exe', SOURCE_URLS['HitmanPro32']) - download_generic(dest, 'HitmanPro64.exe', SOURCE_URLS['HitmanPro64']) def update_hwinfo(): - ## NOTE: Lives in .bin uncompressed - # Stop running processes - for exe in ['HWiNFO32.exe', 'HWiNFO64.exe']: - kill_process(exe) + ## NOTE: Lives in .bin uncompressed + # Stop running processes + for exe in ['HWiNFO32.exe', 'HWiNFO64.exe']: + kill_process(exe) - # Download - download_to_temp('HWiNFO.zip', SOURCE_URLS['HWiNFO']) + # Download + download_to_temp('HWiNFO.zip', SOURCE_URLS['HWiNFO']) - # Extract files - extract_temp_to_bin('HWiNFO.zip', 'HWiNFO') + # Extract files + extract_temp_to_bin('HWiNFO.zip', 'HWiNFO') + + # Cleanup + remove_from_temp('HWiNFO.zip') - # Cleanup - remove_from_temp('HWiNFO.zip') def update_nircmd(): - # Stop running processes - for exe in ['nircmdc.exe', 'nircmdc64.exe']: - kill_process(exe) + # Stop running processes + for exe in ['nircmdc.exe', 'nircmdc64.exe']: + kill_process(exe) - # Remove existing folders - remove_from_kit('NirCmd') + # Remove existing folders + remove_from_kit('NirCmd') - # Download - download_to_temp('nircmd32.zip', SOURCE_URLS['NirCmd32']) - download_to_temp('nircmd64.zip', SOURCE_URLS['NirCmd64']) + # Download + download_to_temp('nircmd32.zip', SOURCE_URLS['NirCmd32']) + download_to_temp('nircmd64.zip', SOURCE_URLS['NirCmd64']) - # Extract files - extract_temp_to_cbin('nircmd64.zip', 'NirCmd', sz_args=['nircmdc.exe']) - shutil.move( - r'{}\NirCmd\nircmdc.exe'.format(global_vars['CBinDir']), - r'{}\NirCmd\nircmdc64.exe'.format(global_vars['CBinDir'])) - extract_temp_to_cbin('nircmd32.zip', 'NirCmd', sz_args=['nircmdc.exe']) + # Extract files + extract_temp_to_cbin('nircmd64.zip', 'NirCmd', sz_args=['nircmdc.exe']) + shutil.move( + r'{}\NirCmd\nircmdc.exe'.format(global_vars['CBinDir']), + r'{}\NirCmd\nircmdc64.exe'.format(global_vars['CBinDir'])) + extract_temp_to_cbin('nircmd32.zip', 'NirCmd', sz_args=['nircmdc.exe']) + + # Cleanup + remove_from_temp('nircmd32.zip') + remove_from_temp('nircmd64.zip') - # Cleanup - remove_from_temp('nircmd32.zip') - remove_from_temp('nircmd64.zip') def update_produkey(): - # Stop running processes - for exe in ['ProduKey.exe', 'ProduKey64.exe']: - kill_process(exe) + # Stop running processes + for exe in ['ProduKey.exe', 'ProduKey64.exe']: + kill_process(exe) - # Remove existing folders - remove_from_kit('ProduKey') + # Remove existing folders + remove_from_kit('ProduKey') - # Download - download_to_temp('produkey32.zip', SOURCE_URLS['ProduKey32']) - download_to_temp('produkey64.zip', SOURCE_URLS['ProduKey64']) + # Download + download_to_temp('produkey32.zip', SOURCE_URLS['ProduKey32']) + download_to_temp('produkey64.zip', SOURCE_URLS['ProduKey64']) - # Extract files - extract_temp_to_cbin('produkey64.zip', 'ProduKey', sz_args=['ProduKey.exe']) - shutil.move( - r'{}\ProduKey\ProduKey.exe'.format(global_vars['CBinDir']), - r'{}\ProduKey\ProduKey64.exe'.format(global_vars['CBinDir'])) - extract_temp_to_cbin('produkey32.zip', 'ProduKey') + # Extract files + extract_temp_to_cbin('produkey64.zip', 'ProduKey', sz_args=['ProduKey.exe']) + shutil.move( + r'{}\ProduKey\ProduKey.exe'.format(global_vars['CBinDir']), + r'{}\ProduKey\ProduKey64.exe'.format(global_vars['CBinDir'])) + extract_temp_to_cbin('produkey32.zip', 'ProduKey') - # Cleanup - remove_from_temp('produkey32.zip') - remove_from_temp('produkey64.zip') + # Cleanup + remove_from_temp('produkey32.zip') + remove_from_temp('produkey64.zip') -## Drivers ## + +# Drivers def update_intel_rst(): - # Remove existing folders - remove_from_kit('Intel RST') + # Remove existing folders + remove_from_kit('Intel RST') - # Prep - dest = r'{}\_Drivers\Intel RST'.format(global_vars['CBinDir']) - include_path = r'{}\_include\_Drivers\Intel RST'.format( - global_vars['CBinDir']) - if os.path.exists(include_path): - shutil.copytree(include_path, dest) + # Prep + dest = r'{}\_Drivers\Intel RST'.format(global_vars['CBinDir']) + include_path = r'{}\_include\_Drivers\Intel RST'.format( + global_vars['CBinDir']) + if os.path.exists(include_path): + shutil.copytree(include_path, dest) + + # Download + for name, url in RST_SOURCES.items(): + download_generic(dest, name, url) - # Download - for name, url in RST_SOURCES.items(): - download_generic(dest, name, url) def update_intel_ssd_toolbox(): - # Remove existing folders - remove_from_kit('Intel SSD Toolbox.exe') + # Remove existing folders + remove_from_kit('Intel SSD Toolbox.exe') + + # Download + download_generic( + r'{}\_Drivers\Intel SSD Toolbox'.format(global_vars['CBinDir']), + 'Intel SSD Toolbox.exe', + SOURCE_URLS['Intel SSD Toolbox']) - # Download - download_generic( - r'{}\_Drivers\Intel SSD Toolbox'.format(global_vars['CBinDir']), - 'Intel SSD Toolbox.exe', - SOURCE_URLS['Intel SSD Toolbox']) def update_samsung_magician(): - # Remove existing folders - remove_from_kit('Samsung Magician.exe') + # Remove existing folders + remove_from_kit('Samsung Magician.exe') - # Download - download_to_temp('Samsung Magician.zip', SOURCE_URLS['Samsung Magician']) + # Download + download_to_temp('Samsung Magician.zip', SOURCE_URLS['Samsung Magician']) - # Extract - extract_temp_to_cbin('Samsung Magician.zip', '_Drivers\Samsung Magician') - shutil.move( - r'{}\_Drivers\Samsung Magician\Samsung_Magician_Installer.exe'.format( - global_vars['CBinDir']), - r'{}\_Drivers\Samsung Magician\Samsung Magician.exe'.format( - global_vars['CBinDir'])) + # Extract + extract_temp_to_cbin('Samsung Magician.zip', '_Drivers\Samsung Magician') + shutil.move( + r'{}\_Drivers\Samsung Magician\Samsung_Magician_Installer.exe'.format( + global_vars['CBinDir']), + r'{}\_Drivers\Samsung Magician\Samsung Magician.exe'.format( + global_vars['CBinDir'])) + + # Cleanup + remove_from_temp('Samsung Magician.zip') - # Cleanup - remove_from_temp('Samsung Magician.zip') def update_sdi_origin(): - # Download aria2 - download_to_temp('aria2.zip', SOURCE_URLS['aria2']) - aria_source = r'{}\aria2.zip'.format(global_vars['TmpDir']) - aria_dest = r'{}\aria2'.format(global_vars['TmpDir']) - aria = r'{}\aria2c.exe'.format(aria_dest) - extract_generic(aria_source, aria_dest, mode='e') + # Download aria2 + download_to_temp('aria2.zip', SOURCE_URLS['aria2']) + aria_source = r'{}\aria2.zip'.format(global_vars['TmpDir']) + aria_dest = r'{}\aria2'.format(global_vars['TmpDir']) + aria = r'{}\aria2c.exe'.format(aria_dest) + extract_generic(aria_source, aria_dest, mode='e') - # Prep for torrent download - download_to_temp('sdio.torrent', SOURCE_URLS['SDIO Torrent']) - sdio_torrent = r'{}\sdio.torrent'.format(global_vars['TmpDir']) - out = run_program([aria, sdio_torrent, '-S']) - indexes = [] - for line in out.stdout.decode().splitlines(): - r = re.search(r'^\s*(\d+)\|(.*)', line) - if r and not re.search(r'(\.(bat|inf)|Video|Server|Printer|XP)', line, re.IGNORECASE): - indexes.append(int(r.group(1))) - indexes = [str(i) for i in sorted(indexes)] + # Prep for torrent download + download_to_temp('sdio.torrent', SOURCE_URLS['SDIO Torrent']) + sdio_torrent = r'{}\sdio.torrent'.format(global_vars['TmpDir']) + out = run_program([aria, sdio_torrent, '-S']) + indexes = [] + for line in out.stdout.decode().splitlines(): + r = re.search(r'^\s*(\d+)\|(.*)', line) + if (r and not re.search( + r'(\.(bat|inf)|Video|Server|Printer|XP)', line, re.IGNORECASE)): + indexes.append(int(r.group(1))) + indexes = [str(i) for i in sorted(indexes)] - # Download SDI Origin - cmd = [ - aria, - '--select-file={}'.format(','.join(indexes)), - '-d', aria_dest, - '--seed-time=0', - sdio_torrent, - '-new_console:n', '-new_console:s33V', - ] - run_program(cmd, pipe=False, check=False, shell=True) - sleep(1) - wait_for_process('aria2c') + # Download SDI Origin + cmd = [ + aria, + '--select-file={}'.format(','.join(indexes)), + '-d', aria_dest, + '--seed-time=0', + sdio_torrent, + '-new_console:n', '-new_console:s33V', + ] + run_program(cmd, pipe=False, check=False, shell=True) + sleep(1) + wait_for_process('aria2c') - # Download SDI Origin extra themes - download_to_temp('sdio_themes.zip', SOURCE_URLS['SDIO Themes']) - theme_source = r'{}\sdio_themes.zip'.format(global_vars['TmpDir']) - theme_dest = r'{}\SDIO_Update\tools\SDI\themes'.format(aria_dest) - extract_generic(theme_source, theme_dest) + # Download SDI Origin extra themes + download_to_temp('sdio_themes.zip', SOURCE_URLS['SDIO Themes']) + theme_source = r'{}\sdio_themes.zip'.format(global_vars['TmpDir']) + theme_dest = r'{}\SDIO_Update\tools\SDI\themes'.format(aria_dest) + extract_generic(theme_source, theme_dest) - # Move files into place - for item in os.scandir(r'{}\SDIO_Update'.format(aria_dest)): - dest_item = '{}\_Drivers\SDIO\{}'.format( - global_vars['BinDir'], item.name) - r = re.search(r'^SDIO_x?(64|)_?R.*exe$', item.name, re.IGNORECASE) - if r: - dest_item = dest_item.replace(item.name, 'SDIO{}.exe'.format( - r.group(1))) - if (not os.path.exists(dest_item) - and not re.search(r'\.(inf|bat)$', item.name, re.IGNORECASE)): - shutil.move(item.path, dest_item) + # Move files into place + for item in os.scandir(r'{}\SDIO_Update'.format(aria_dest)): + dest_item = '{}\_Drivers\SDIO\{}'.format( + global_vars['BinDir'], item.name) + r = re.search(r'^SDIO_x?(64|)_?R.*exe$', item.name, re.IGNORECASE) + if r: + dest_item = dest_item.replace(item.name, 'SDIO{}.exe'.format( + r.group(1))) + if (not os.path.exists(dest_item) + and not re.search(r'\.(inf|bat)$', item.name, re.IGNORECASE)): + shutil.move(item.path, dest_item) - # Cleanup - remove_from_temp('aria2') - remove_from_temp('aria2.zip') - remove_from_temp('sdio.torrent') - remove_from_temp('sdio_themes.zip') + # Cleanup + remove_from_temp('aria2') + remove_from_temp('aria2.zip') + remove_from_temp('sdio.torrent') + remove_from_temp('sdio_themes.zip') -## Installers ## + +# Installers def update_adobe_reader_dc(): - # Prep - dest = r'{}\Installers\Extras\Office'.format( - global_vars['BaseDir']) + # Prep + dest = r'{}\Installers\Extras\Office'.format( + global_vars['BaseDir']) - # Remove existing installer - try: - os.remove(r'{}\Adobe Reader DC.exe'.format(dest)) - except FileNotFoundError: - pass + # Remove existing installer + try: + os.remove(r'{}\Adobe Reader DC.exe'.format(dest)) + except FileNotFoundError: + pass + + # Download + download_generic( + dest, 'Adobe Reader DC.exe', SOURCE_URLS['Adobe Reader DC']) - # Download - download_generic( - dest, 'Adobe Reader DC.exe', SOURCE_URLS['Adobe Reader DC']) def update_eset_config(): - """Copy config files to .cbin before compress_item""" - dest = r'{}\ESETConfigs'.format(global_vars['CBinDir']) - include_path = r'{}\_include\ESETConfigs'.format(global_vars['CBinDir']) - if os.path.exists(include_path): - shutil.copytree(include_path, dest) + """Copy config files to .cbin before compress_item""" + dest = r'{}\ESETConfigs'.format(global_vars['CBinDir']) + include_path = r'{}\_include\ESETConfigs'.format(global_vars['CBinDir']) + if os.path.exists(include_path): + shutil.copytree(include_path, dest) def update_macs_fan_control(): - # Prep - dest = r'{}\Installers'.format( - global_vars['BaseDir']) + # Prep + dest = r'{}\Installers'.format( + global_vars['BaseDir']) - # Remove existing installer - try: - os.remove(r'{}\Macs Fan Control.exe'.format(dest)) - except FileNotFoundError: - pass + # Remove existing installer + try: + os.remove(r'{}\Macs Fan Control.exe'.format(dest)) + except FileNotFoundError: + pass + + # Download + download_generic( + dest, 'Macs Fan Control.exe', SOURCE_URLS['Macs Fan Control']) - # Download - download_generic( - dest, 'Macs Fan Control.exe', SOURCE_URLS['Macs Fan Control']) def update_office(): - # Remove existing folders - remove_from_kit('_Office') + # Remove existing folders + remove_from_kit('_Office') - # Prep - dest = r'{}\_Office'.format(global_vars['CBinDir']) - include_path = r'{}\_include\_Office'.format(global_vars['CBinDir']) - if os.path.exists(include_path): - shutil.copytree(include_path, dest) + # Prep + dest = r'{}\_Office'.format(global_vars['CBinDir']) + include_path = r'{}\_include\_Office'.format(global_vars['CBinDir']) + if os.path.exists(include_path): + shutil.copytree(include_path, dest) - for year in ['2016']: - # Download and extract - name = 'odt{}.exe'.format(year) - url = 'Office Deployment Tool {}'.format(year) - download_to_temp(name, SOURCE_URLS[url]) - cmd = [ - r'{}\odt{}.exe'.format(global_vars['TmpDir'], year), - r'/extract:{}\{}'.format(global_vars['TmpDir'], year), - '/quiet', - ] - run_program(cmd) - shutil.move( - r'{}\{}'.format(global_vars['TmpDir'], year), - r'{}\_Office\{}'.format(global_vars['CBinDir'], year)) + # Download and extract + _out_path = r'{}\odt'.format(global_vars['TmpDir']) + download_to_temp('odt.exe', SOURCE_URLS['Office Deployment Tool']) + cmd = [ + r'{}\odt.exe'.format(global_vars['TmpDir']), + r'/extract:{}'.format(_out_path), + '/quiet', + ] + run_program(cmd) + shutil.move( + r'{}\setup.exe'.format(_out_path), + r'{}\_Office'.format(global_vars['CBinDir'])) + + # Cleanup + remove_from_temp('odt') + remove_from_temp('odt.exe') - # Cleanup - remove_from_temp('odt{}.exe'.format(year)) def update_classic_start_skin(): - # Remove existing folders - remove_from_kit('ClassicStartSkin') + # Remove existing folders + remove_from_kit('ClassicStartSkin') + + # Download + download_generic( + r'{}\ClassicStartSkin'.format(global_vars['CBinDir']), + 'Metro-Win10-Black.skin7', + SOURCE_URLS['ClassicStartSkin']) - # Download - download_generic( - r'{}\ClassicStartSkin'.format(global_vars['CBinDir']), - 'Metro-Win10-Black.skin7', - SOURCE_URLS['ClassicStartSkin']) def update_vcredists(): - # Remove existing folders - remove_from_kit('_vcredists') + # Remove existing folders + remove_from_kit('_vcredists') - # Prep - dest = r'{}\_vcredists'.format(global_vars['CBinDir']) - include_path = r'{}\_include\_vcredists'.format(global_vars['CBinDir']) - if os.path.exists(include_path): - shutil.copytree(include_path, dest) + # Prep + dest = r'{}\_vcredists'.format(global_vars['CBinDir']) + include_path = r'{}\_include\_vcredists'.format(global_vars['CBinDir']) + if os.path.exists(include_path): + shutil.copytree(include_path, dest) + + # Download + for year in VCREDIST_SOURCES.keys(): + for bit in ['32', '64']: + dest = r'{}\_vcredists\{}\x{}'.format( + global_vars['CBinDir'], year, bit) + download_generic( + dest, + 'vcredist.exe', + VCREDIST_SOURCES[year][bit]) - # Download - for year in VCREDIST_SOURCES.keys(): - for bit in ['32', '64']: - dest = r'{}\_vcredists\{}\x{}'.format( - global_vars['CBinDir'], year, bit) - download_generic( - dest, - 'vcredist.exe', - VCREDIST_SOURCES[year][bit]) def update_one_ninite(section, dest, name, url, indent=8, width=40): - # Prep - url = 'https://ninite.com/{}/ninite.exe'.format(url) + # Prep + url = 'https://ninite.com/{}/ninite.exe'.format(url) - # Download - download_generic(out_dir=dest, out_name=name, source_url=url) + # Download + download_generic(out_dir=dest, out_name=name, source_url=url) + + # Copy to Installers folder + installer_parent = r'{}\Installers\Extras\{}'.format( + global_vars['BaseDir'], section) + installer_dest = r'{}\{}'.format(installer_parent, name) + os.makedirs(installer_parent, exist_ok=True) + if os.path.exists(installer_dest): + remove_item(installer_dest) + shutil.copy(r'{}\{}'.format(dest, name), installer_dest) - # Copy to Installers folder - installer_parent = r'{}\Installers\Extras\{}'.format( - global_vars['BaseDir'], section) - installer_dest = r'{}\{}'.format(installer_parent, name) - os.makedirs(installer_parent, exist_ok=True) - if os.path.exists(installer_dest): - remove_item(installer_dest) - shutil.copy(r'{}\{}'.format(dest, name), installer_dest) def update_all_ninite(indent=8, width=40, other_results={}): - print_info('{}Ninite'.format(' '*int(indent/2))) - for section in sorted(NINITE_SOURCES.keys()): - print_success('{}{}'.format(' '*int(indent/4*3), section)) - dest = r'{}\_Ninite\{}'.format(global_vars['CBinDir'], section) - for name, url in sorted(NINITE_SOURCES[section].items()): - try_and_print(message=name, function=update_one_ninite, - other_results=other_results, indent=indent, width=width, - section=section, dest=dest, name=name, url=url) + print_info('{}Ninite'.format(' '*int(indent/2))) + for section in sorted(NINITE_SOURCES.keys()): + print_success('{}{}'.format(' '*int(indent/4*3), section)) + dest = r'{}\_Ninite\{}'.format(global_vars['CBinDir'], section) + for name, url in sorted(NINITE_SOURCES[section].items()): + try_and_print(message=name, function=update_one_ninite, + other_results=other_results, indent=indent, width=width, + section=section, dest=dest, name=name, url=url) -## Misc ## + +# Misc def update_caffeine(): - # Stop running processes - kill_process('caffeine.exe') + # Stop running processes + kill_process('caffeine.exe') - # Remove existing folders - remove_from_kit('Caffeine') + # Remove existing folders + remove_from_kit('Caffeine') - # Download - download_to_temp('caffeine.zip', SOURCE_URLS['Caffeine']) + # Download + download_to_temp('caffeine.zip', SOURCE_URLS['Caffeine']) - # Extract files - extract_temp_to_cbin('caffeine.zip', 'Caffeine') + # Extract files + extract_temp_to_cbin('caffeine.zip', 'Caffeine') + + # Cleanup + remove_from_temp('caffeine.zip') - # Cleanup - remove_from_temp('caffeine.zip') def update_du(): - # Stop running processes - kill_process('du.exe') - kill_process('du64.exe') + # Stop running processes + kill_process('du.exe') + kill_process('du64.exe') - # Remove existing folders - remove_from_kit('Du') + # Remove existing folders + remove_from_kit('Du') - # Download - download_to_temp('du.zip', SOURCE_URLS['Du']) + # Download + download_to_temp('du.zip', SOURCE_URLS['Du']) - # Extract files - extract_temp_to_cbin('du.zip', 'Du') + # Extract files + extract_temp_to_cbin('du.zip', 'Du') + + # Cleanup + remove_from_temp('du.zip') - # Cleanup - remove_from_temp('du.zip') def update_everything(): - # Stop running processes - for exe in ['Everything.exe', 'Everything64.exe']: - kill_process(exe) + # Stop running processes + for exe in ['Everything.exe', 'Everything64.exe']: + kill_process(exe) - # Remove existing folders - remove_from_kit('Everything') + # Remove existing folders + remove_from_kit('Everything') - # Download - download_to_temp('everything32.zip', SOURCE_URLS['Everything32']) - download_to_temp('everything64.zip', SOURCE_URLS['Everything64']) + # Download + download_to_temp('everything32.zip', SOURCE_URLS['Everything32']) + download_to_temp('everything64.zip', SOURCE_URLS['Everything64']) - # Extract files - extract_temp_to_cbin('everything64.zip', 'Everything', sz_args=['Everything.exe']) - shutil.move( - r'{}\Everything\Everything.exe'.format(global_vars['CBinDir']), - r'{}\Everything\Everything64.exe'.format(global_vars['CBinDir'])) - extract_temp_to_cbin('everything32.zip', 'Everything') + # Extract files + extract_temp_to_cbin( + 'everything64.zip', 'Everything', sz_args=['Everything.exe']) + shutil.move( + r'{}\Everything\Everything.exe'.format(global_vars['CBinDir']), + r'{}\Everything\Everything64.exe'.format(global_vars['CBinDir'])) + extract_temp_to_cbin('everything32.zip', 'Everything') + + # Cleanup + remove_from_temp('everything32.zip') + remove_from_temp('everything64.zip') - # Cleanup - remove_from_temp('everything32.zip') - remove_from_temp('everything64.zip') def update_firefox_ublock_origin(): - # Remove existing folders - remove_from_kit('FirefoxExtensions') + # Remove existing folders + remove_from_kit('FirefoxExtensions') + + # Download + download_generic( + r'{}\FirefoxExtensions'.format(global_vars['CBinDir']), + 'ublock_origin.xpi', + SOURCE_URLS['Firefox uBO']) - # Download - download_generic( - r'{}\FirefoxExtensions'.format(global_vars['CBinDir']), - 'ublock_origin.xpi', - SOURCE_URLS['Firefox uBO']) def update_notepadplusplus(): - # Stop running processes - kill_process('notepadplusplus.exe') + # Stop running processes + kill_process('notepadplusplus.exe') - # Remove existing folders - remove_from_kit('NotepadPlusPlus') + # Remove existing folders + remove_from_kit('NotepadPlusPlus') - # Download - download_to_temp('npp.7z', SOURCE_URLS['NotepadPlusPlus']) + # Download + download_to_temp('npp.7z', SOURCE_URLS['NotepadPlusPlus']) - # Extract files - extract_temp_to_cbin('npp.7z', 'NotepadPlusPlus') - shutil.move( - r'{}\NotepadPlusPlus\notepad++.exe'.format(global_vars['CBinDir']), - r'{}\NotepadPlusPlus\notepadplusplus.exe'.format(global_vars['CBinDir']) - ) + # Extract files + extract_temp_to_cbin('npp.7z', 'NotepadPlusPlus') + shutil.move( + r'{}\NotepadPlusPlus\notepad++.exe'.format(global_vars['CBinDir']), + r'{}\NotepadPlusPlus\notepadplusplus.exe'.format(global_vars['CBinDir']) + ) + + # Cleanup + remove_from_temp('npp.7z') - # Cleanup - remove_from_temp('npp.7z') def update_putty(): - # Stop running processes - kill_process('PUTTY.EXE') + # Stop running processes + kill_process('PUTTY.EXE') - # Remove existing folders - remove_from_kit('PuTTY') + # Remove existing folders + remove_from_kit('PuTTY') - # Download - download_to_temp('putty.zip', SOURCE_URLS['PuTTY']) + # Download + download_to_temp('putty.zip', SOURCE_URLS['PuTTY']) - # Extract files - extract_temp_to_cbin('putty.zip', 'PuTTY') + # Extract files + extract_temp_to_cbin('putty.zip', 'PuTTY') + + # Cleanup + remove_from_temp('putty.zip') - # Cleanup - remove_from_temp('putty.zip') def update_shutup10(): - # Stop running processes - kill_process('OOSU10.exe') + # Stop running processes + kill_process('OOSU10.exe') - # Remove existing folders - remove_from_kit('ShutUp10') + # Remove existing folders + remove_from_kit('ShutUp10') - # Copy settings - dest = r'{}\ShutUp10'.format(global_vars['CBinDir']) - include_path = r'{}\_include\ShutUp10'.format(global_vars['CBinDir']) - if os.path.exists(include_path): - shutil.copytree(include_path, dest) + # Copy settings + dest = r'{}\ShutUp10'.format(global_vars['CBinDir']) + include_path = r'{}\_include\ShutUp10'.format(global_vars['CBinDir']) + if os.path.exists(include_path): + shutil.copytree(include_path, dest) - # Download - download_generic( - r'{}\ShutUp10'.format(global_vars['CBinDir']), - 'OOSU10.exe', - SOURCE_URLS['ShutUp10']) + # Download + download_generic( + r'{}\ShutUp10'.format(global_vars['CBinDir']), + 'OOSU10.exe', + SOURCE_URLS['ShutUp10']) def update_wiztree(): - # Stop running processes - for process in ['WizTree.exe', 'WizTree64.exe']: - kill_process(process) + # Stop running processes + for process in ['WizTree.exe', 'WizTree64.exe']: + kill_process(process) - # Remove existing folders - remove_from_kit('WizTree') + # Remove existing folders + remove_from_kit('WizTree') - # Download - download_to_temp( - 'wiztree.zip', SOURCE_URLS['WizTree']) + # Download + download_to_temp( + 'wiztree.zip', SOURCE_URLS['WizTree']) - # Extract files - extract_temp_to_cbin('wiztree.zip', 'WizTree') + # Extract files + extract_temp_to_cbin('wiztree.zip', 'WizTree') + + # Cleanup + remove_from_temp('wiztree.zip') - # Cleanup - remove_from_temp('wiztree.zip') def update_xmplay(): - # Stop running processes - kill_process('xmplay.exe') + # Stop running processes + kill_process('xmplay.exe') - # Remove existing folders - remove_from_kit('XMPlay') + # Remove existing folders + remove_from_kit('XMPlay') - # Download - download_to_temp('xmplay.zip', SOURCE_URLS['XMPlay']) - download_to_temp('xmp-7z.zip', SOURCE_URLS['XMPlay 7z']) - download_to_temp('xmp-gme.zip', SOURCE_URLS['XMPlay Game']) - download_to_temp('xmp-rar.zip', SOURCE_URLS['XMPlay RAR']) - download_to_temp('WAModern.zip', SOURCE_URLS['XMPlay WAModern']) + # Download + download_to_temp('xmplay.zip', SOURCE_URLS['XMPlay']) + download_to_temp('xmp-7z.zip', SOURCE_URLS['XMPlay 7z']) + download_to_temp('xmp-gme.zip', SOURCE_URLS['XMPlay Game']) + download_to_temp('xmp-rar.zip', SOURCE_URLS['XMPlay RAR']) + download_to_temp('WAModern.zip', SOURCE_URLS['XMPlay WAModern']) - # Extract files - extract_temp_to_cbin('xmplay.zip', 'XMPlay', - mode='e', sz_args=['xmplay.exe', 'xmplay.txt']) - for item in ['xmp-7z', 'xmp-gme', 'xmp-rar', 'WAModern']: - filter = [] - if item == 'WAModern': - filter.append('WAModern NightVision.xmpskin') - extract_generic( - r'{}\{}.zip'.format(global_vars['TmpDir'], item), - r'{}\XMPlay\plugins'.format(global_vars['CBinDir']), - mode='e', sz_args=filter) + # Extract files + extract_temp_to_cbin('xmplay.zip', 'XMPlay', + mode='e', sz_args=['xmplay.exe', 'xmplay.txt']) + for item in ['xmp-7z', 'xmp-gme', 'xmp-rar', 'WAModern']: + filter = [] + if item == 'WAModern': + filter.append('WAModern NightVision.xmpskin') + extract_generic( + r'{}\{}.zip'.format(global_vars['TmpDir'], item), + r'{}\XMPlay\plugins'.format(global_vars['CBinDir']), + mode='e', sz_args=filter) - # Download Music - dest = r'{}\XMPlay\music_tmp\MOD'.format(global_vars['CBinDir']) - for mod in MUSIC_MOD: - name = mod.split('#')[-1] - url = 'https://api.modarchive.org/downloads.php?moduleid={}'.format(mod) - download_generic(dest, name, url) - dest = r'{}\XMPlay\music_tmp\SNES'.format(global_vars['CBinDir']) - for game in MUSIC_SNES: - name = '{}.rsn'.format(game) - url = 'http://snesmusic.org/v2/download.php?spcNow={}'.format(game) - download_generic(dest, name, url) + # Download Music + dest = r'{}\XMPlay\music_tmp\MOD'.format(global_vars['CBinDir']) + for mod in MUSIC_MOD: + name = mod.split('#')[-1] + url = 'https://api.modarchive.org/downloads.php?moduleid={}'.format(mod) + download_generic(dest, name, url) + dest = r'{}\XMPlay\music_tmp\SNES'.format(global_vars['CBinDir']) + for game in MUSIC_SNES: + name = '{}.rsn'.format(game) + url = 'http://snesmusic.org/v2/download.php?spcNow={}'.format(game) + download_generic(dest, name, url) - # Compress Music - cmd = [ - global_vars['Tools']['SevenZip'], - 'a', r'{}\XMPlay\music.7z'.format(global_vars['CBinDir']), - '-t7z', '-mx=9', '-bso0', '-bse0', - r'{}\XMPlay\music_tmp\*'.format(global_vars['CBinDir']), - ] - run_program(cmd) + # Compress Music + cmd = [ + global_vars['Tools']['SevenZip'], + 'a', r'{}\XMPlay\music.7z'.format(global_vars['CBinDir']), + '-t7z', '-mx=9', '-bso0', '-bse0', + r'{}\XMPlay\music_tmp\*'.format(global_vars['CBinDir']), + ] + run_program(cmd) - # Cleanup - remove_item(r'{}\XMPlay\music_tmp'.format(global_vars['CBinDir'])) - remove_from_temp('xmplay.zip') - remove_from_temp('xmp-7z.zip') - remove_from_temp('xmp-gme.zip') - remove_from_temp('xmp-rar.zip') - remove_from_temp('WAModern.zip') + # Cleanup + remove_item(r'{}\XMPlay\music_tmp'.format(global_vars['CBinDir'])) + remove_from_temp('xmplay.zip') + remove_from_temp('xmp-7z.zip') + remove_from_temp('xmp-gme.zip') + remove_from_temp('xmp-rar.zip') + remove_from_temp('WAModern.zip') -## Repairs ## + +# Repairs def update_adwcleaner(): - # Stop running processes - kill_process('AdwCleaner.exe') + # Stop running processes + kill_process('AdwCleaner.exe') - # Remove existing folders - remove_from_kit('AdwCleaner') + # Remove existing folders + remove_from_kit('AdwCleaner') + + # Download + download_generic( + r'{}\AdwCleaner'.format(global_vars['CBinDir']), + 'AdwCleaner.exe', + SOURCE_URLS['AdwCleaner']) - # Download - download_generic( - r'{}\AdwCleaner'.format(global_vars['CBinDir']), - 'AdwCleaner.exe', - SOURCE_URLS['AdwCleaner']) def update_eset_online_scanner(): - # Stop running processes - kill_process('ESET.exe') + # Stop running processes + kill_process('ESET.exe') - # Remove existing folders - remove_from_kit('ESET') + # Remove existing folders + remove_from_kit('ESET') - # Download - download_generic( - r'{}\ESET'.format(global_vars['CBinDir']), - 'ESET.exe', - SOURCE_URLS['ESET Online Scanner']) + # Download + download_generic( + r'{}\ESET'.format(global_vars['CBinDir']), + 'ESET.exe', + SOURCE_URLS['ESET Online Scanner']) def update_kvrt(): - # Stop running processes - kill_process('KVRT.exe') + # Stop running processes + kill_process('KVRT.exe') - # Remove existing folders - remove_from_kit('KVRT') + # Remove existing folders + remove_from_kit('KVRT') + + # Download + download_generic( + r'{}\KVRT'.format(global_vars['CBinDir']), + 'KVRT.exe', + SOURCE_URLS['KVRT']) - # Download - download_generic( - r'{}\KVRT'.format(global_vars['CBinDir']), - 'KVRT.exe', - SOURCE_URLS['KVRT']) def update_rkill(): - # Stop running processes - kill_process('RKill.exe') + # Stop running processes + kill_process('RKill.exe') - # Remove existing folders - remove_from_kit('RKill') + # Remove existing folders + remove_from_kit('RKill') + + # Download + url = resolve_dynamic_url( + SOURCE_URLS['RKill'], + 'href.*rkill\.exe') + download_generic( + r'{}\RKill'.format(global_vars['CBinDir']), 'RKill.exe', url) - # Download - url = resolve_dynamic_url( - SOURCE_URLS['RKill'], - 'href.*rkill\.exe') - download_generic( - r'{}\RKill'.format(global_vars['CBinDir']), 'RKill.exe', url) def update_tdsskiller(): - # Stop running processes - kill_process('TDSSKiller.exe') + # Stop running processes + kill_process('TDSSKiller.exe') - # Remove existing folders - remove_from_kit('TDSSKiller') + # Remove existing folders + remove_from_kit('TDSSKiller') + + # Download + download_generic( + r'{}\TDSSKiller'.format(global_vars['CBinDir']), + 'TDSSKiller.exe', + SOURCE_URLS['TDSSKiller']) - # Download - download_generic( - r'{}\TDSSKiller'.format(global_vars['CBinDir']), - 'TDSSKiller.exe', - SOURCE_URLS['TDSSKiller']) def update_winaiorepair(): - # Stop running processes - kill_process('Repair_Windows.exe') + # Stop running processes + kill_process('Repair_Windows.exe') - # Download - download_to_temp('winaio.zip', SOURCE_URLS['WinAIO Repair']) + # Download + download_to_temp('winaio.zip', SOURCE_URLS['WinAIO Repair']) - # Extract - extract_temp_to_cbin('winaio.zip', 'WinAIORepair') - dest = r'{}\WinAIORepair'.format(global_vars['CBinDir']) - for item in os.scandir(r'{}\Tweaking.com - Windows Repair'.format(dest)): - dest_item = '{}\{}'.format(dest, item.name) - if not os.path.exists(dest_item): - shutil.move(item.path, dest_item) - shutil.rmtree( - r'{}\WinAIORepair\Tweaking.com - Windows Repair'.format(global_vars['CBinDir'])) + # Extract + extract_temp_to_cbin('winaio.zip', 'WinAIORepair') + dest = r'{}\WinAIORepair'.format(global_vars['CBinDir']) + for item in os.scandir(r'{}\Tweaking.com - Windows Repair'.format(dest)): + dest_item = '{}\{}'.format(dest, item.name) + if not os.path.exists(dest_item): + shutil.move(item.path, dest_item) + shutil.rmtree( + r'{}\WinAIORepair\Tweaking.com - Windows Repair'.format(global_vars['CBinDir'])) - # Cleanup - remove_from_temp('winaio.zip') + # Cleanup + remove_from_temp('winaio.zip') -## Uninstallers ## + +# Uninstallers def update_iobit_uninstaller(): - # Stop running processes - kill_process('IObitUninstallerPortable.exe') + # Stop running processes + kill_process('IObitUninstallerPortable.exe') - # Remove existing folders - remove_from_kit('IObitUninstallerPortable') + # Remove existing folders + remove_from_kit('IObitUninstallerPortable') - # Download - download_generic( - global_vars['CBinDir'], - 'IObitUninstallerPortable.exe', - SOURCE_URLS['IOBit_Uninstaller']) + # Download + download_generic( + global_vars['CBinDir'], + 'IObitUninstallerPortable.exe', + SOURCE_URLS['IOBit_Uninstaller']) - # "Install" - cmd = r'{}\IObitUninstallerPortable.exe'.format(global_vars['CBinDir']) - popen_program(cmd) - sleep(1) - wait_for_process('IObitUninstallerPortable') + # "Install" + cmd = r'{}\IObitUninstallerPortable.exe'.format(global_vars['CBinDir']) + popen_program(cmd) + sleep(1) + wait_for_process('IObitUninstallerPortable') + + # Cleanup + remove_from_kit('IObitUninstallerPortable.exe') - # Cleanup - remove_from_kit('IObitUninstallerPortable.exe') if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/windows_setup.py b/.bin/Scripts/functions/windows_setup.py index 0952e64d..e790a4b6 100644 --- a/.bin/Scripts/functions/windows_setup.py +++ b/.bin/Scripts/functions/windows_setup.py @@ -3,236 +3,250 @@ from functions.data import * from functions.disk import * + # STATIC VARIABLES WINDOWS_VERSIONS = [ - {'Name': 'Windows 7 Home Basic', - 'Image File': 'Win7', - 'Image Name': 'Windows 7 HOMEBASIC'}, - {'Name': 'Windows 7 Home Premium', - 'Image File': 'Win7', - 'Image Name': 'Windows 7 HOMEPREMIUM'}, - {'Name': 'Windows 7 Professional', - 'Image File': 'Win7', - 'Image Name': 'Windows 7 PROFESSIONAL'}, - {'Name': 'Windows 7 Ultimate', - 'Image File': 'Win7', - 'Image Name': 'Windows 7 ULTIMATE'}, + {'Name': 'Windows 7 Home Basic', + 'Image File': 'Win7', + 'Image Name': 'Windows 7 HOMEBASIC'}, + {'Name': 'Windows 7 Home Premium', + 'Image File': 'Win7', + 'Image Name': 'Windows 7 HOMEPREMIUM'}, + {'Name': 'Windows 7 Professional', + 'Image File': 'Win7', + 'Image Name': 'Windows 7 PROFESSIONAL'}, + {'Name': 'Windows 7 Ultimate', + 'Image File': 'Win7', + 'Image Name': 'Windows 7 ULTIMATE'}, - {'Name': 'Windows 8.1', - 'Image File': 'Win8', - 'Image Name': 'Windows 8.1', - 'CRLF': True}, - {'Name': 'Windows 8.1 Pro', - 'Image File': 'Win8', - 'Image Name': 'Windows 8.1 Pro'}, + {'Name': 'Windows 8.1', + 'Image File': 'Win8', + 'Image Name': 'Windows 8.1', + 'CRLF': True}, + {'Name': 'Windows 8.1 Pro', + 'Image File': 'Win8', + 'Image Name': 'Windows 8.1 Pro'}, + + {'Name': 'Windows 10 Home', + 'Image File': 'Win10', + 'Image Name': 'Windows 10 Home', + 'CRLF': True}, + {'Name': 'Windows 10 Pro', + 'Image File': 'Win10', + 'Image Name': 'Windows 10 Pro'}, + ] - {'Name': 'Windows 10 Home', - 'Image File': 'Win10', - 'Image Name': 'Windows 10 Home', - 'CRLF': True}, - {'Name': 'Windows 10 Pro', - 'Image File': 'Win10', - 'Image Name': 'Windows 10 Pro'}, - ] def find_windows_image(windows_version): - """Search for a Windows source image file, returns dict. + """Search for a Windows source image file, returns dict. - Searches on local disks and then the WINDOWS_SERVER share.""" - image = {} - imagefile = windows_version['Image File'] - imagename = windows_version['Image Name'] + Searches on local disks and then the WINDOWS_SERVER share.""" + image = {} + imagefile = windows_version['Image File'] + imagename = windows_version['Image Name'] - # Search local source - set_thread_error_mode(silent=True) # Prevents "No disk" popups - for d in psutil.disk_partitions(): - for ext in ['esd', 'wim', 'swm']: - path = '{}images\{}.{}'.format(d.mountpoint, imagefile, ext) - if os.path.isfile(path) and wim_contains_image(path, imagename): - image['Path'] = path - image['Letter'] = d.mountpoint[:1].upper() - image['Local'] = True - if ext == 'swm': - image['Glob'] = '--ref="{}*.swm"'.format(image['Path'][:-4]) - break - set_thread_error_mode(silent=False) # Return to normal + # Search local source + set_thread_error_mode(silent=True) # Prevents "No disk" popups + for d in psutil.disk_partitions(): + for ext in ['esd', 'wim', 'swm']: + path = '{}images\{}.{}'.format(d.mountpoint, imagefile, ext) + if os.path.isfile(path) and wim_contains_image(path, imagename): + image['Path'] = path + image['Letter'] = d.mountpoint[:1].upper() + image['Local'] = True + if ext == 'swm': + image['Glob'] = '--ref="{}*.swm"'.format(image['Path'][:-4]) + break + set_thread_error_mode(silent=False) # Return to normal - # Check for network source - if not image: - mount_windows_share() - if WINDOWS_SERVER['Mounted']: - for ext in ['esd', 'wim', 'swm']: - path = r'\\{}\{}\images\{}.{}'.format( - WINDOWS_SERVER['IP'], - WINDOWS_SERVER['Share'], - imagefile, - ext) - if os.path.isfile(path) and wim_contains_image(path, imagename): - image['Path'] = path - image['Letter'] = None - image['Local'] = False - if ext == 'swm': - image['Glob'] = '--ref="{}*.swm"'.format( - image['Path'][:-4]) - break + # Check for network source + if not image: + mount_windows_share() + if WINDOWS_SERVER['Mounted']: + for ext in ['esd', 'wim', 'swm']: + path = r'\\{}\{}\images\{}.{}'.format( + WINDOWS_SERVER['IP'], + WINDOWS_SERVER['Share'], + imagefile, + ext) + if os.path.isfile(path) and wim_contains_image(path, imagename): + image['Path'] = path + image['Letter'] = None + image['Local'] = False + if ext == 'swm': + image['Glob'] = '--ref="{}*.swm"'.format( + image['Path'][:-4]) + break + + # Display image to be used (if any) and return + if image: + print_info('Using image: {}'.format(image['Path'])) + return image + else: + print_error('Failed to find Windows source image for {}'.format( + windows_version['Name'])) + raise GenericAbort - # Display image to be used (if any) and return - if image: - print_info('Using image: {}'.format(image['Path'])) - return image - else: - print_error('Failed to find Windows source image for {}'.format( - windows_version['Name'])) - raise GenericAbort def format_disk(disk, use_gpt): - """Format disk for use as a Windows OS disk.""" - if use_gpt: - format_gpt(disk) - else: - format_mbr(disk) + """Format disk for use as a Windows OS disk.""" + if use_gpt: + format_gpt(disk) + else: + format_mbr(disk) + def format_gpt(disk): - """Format disk for use as a Windows OS disk using the GPT layout.""" - script = [ - # Partition table - 'select disk {}'.format(disk['Number']), - 'clean', - 'convert gpt', + """Format disk for use as a Windows OS disk using the GPT layout.""" + script = [ + # Partition table + 'select disk {}'.format(disk['Number']), + 'clean', + 'convert gpt', - # System partition - # NOTE: ESP needs to be >= 260 for Advanced Format 4K disks - 'create partition efi size=500', - 'format quick fs=fat32 label="System"', - 'assign letter="S"', + # System partition + # NOTE: ESP needs to be >= 260 for Advanced Format 4K disks + 'create partition efi size=500', + 'format quick fs=fat32 label="System"', + 'assign letter="S"', - # Microsoft Reserved (MSR) partition - 'create partition msr size=128', + # Microsoft Reserved (MSR) partition + 'create partition msr size=128', - # Windows partition - 'create partition primary', - 'format quick fs=ntfs label="Windows"', - 'assign letter="W"', + # Windows partition + 'create partition primary', + 'format quick fs=ntfs label="Windows"', + 'assign letter="W"', - # Recovery Tools partition - 'shrink minimum=500', - 'create partition primary', - 'format quick fs=ntfs label="Recovery Tools"', - 'assign letter="T"', - 'set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac"', - 'gpt attributes=0x8000000000000001', - ] + # Recovery Tools partition + 'shrink minimum=500', + 'create partition primary', + 'format quick fs=ntfs label="Recovery Tools"', + 'assign letter="T"', + 'set id="de94bba4-06d1-4d40-a16a-bfd50179d6ac"', + 'gpt attributes=0x8000000000000001', + ] + + # Run + run_diskpart(script) - # Run - run_diskpart(script) def format_mbr(disk): - """Format disk for use as a Windows OS disk using the MBR layout.""" - script = [ - # Partition table - 'select disk {}'.format(disk['Number']), - 'clean', + """Format disk for use as a Windows OS disk using the MBR layout.""" + script = [ + # Partition table + 'select disk {}'.format(disk['Number']), + 'clean', - # System partition - 'create partition primary size=100', - 'format fs=ntfs quick label="System Reserved"', - 'active', - 'assign letter="S"', + # System partition + 'create partition primary size=100', + 'format fs=ntfs quick label="System Reserved"', + 'active', + 'assign letter="S"', - # Windows partition - 'create partition primary', - 'format fs=ntfs quick label="Windows"', - 'assign letter="W"', + # Windows partition + 'create partition primary', + 'format fs=ntfs quick label="Windows"', + 'assign letter="W"', - # Recovery Tools partition - 'shrink minimum=500', - 'create partition primary', - 'format quick fs=ntfs label="Recovery"', - 'assign letter="T"', - 'set id=27', - ] + # Recovery Tools partition + 'shrink minimum=500', + 'create partition primary', + 'format quick fs=ntfs label="Recovery"', + 'assign letter="T"', + 'set id=27', + ] + + # Run + run_diskpart(script) - # Run - run_diskpart(script) def mount_windows_share(): - """Mount the Windows images share unless labeled as already mounted.""" - if not WINDOWS_SERVER['Mounted']: - # Mounting read-write in case a backup was done in the same session - # and the server was left mounted read-write. This avoids throwing an - # error by trying to mount the same server with multiple credentials. - mount_network_share(WINDOWS_SERVER, read_write=True) + """Mount the Windows images share unless already mounted.""" + if not WINDOWS_SERVER['Mounted']: + # Mounting read-write in case a backup was done in the same session + # and the server was left mounted read-write. This avoids throwing an + # error by trying to mount the same server with multiple credentials. + mount_network_share(WINDOWS_SERVER, read_write=True) + def select_windows_version(): - """Select Windows version from a menu, returns dict.""" - actions = [ - {'Name': 'Main Menu', 'Letter': 'M'}, - ] + """Select Windows version from a menu, returns dict.""" + actions = [ + {'Name': 'Main Menu', 'Letter': 'M'}, + ] - # Menu loop - selection = menu_select( - title = 'Which version of Windows are we installing?', - main_entries = WINDOWS_VERSIONS, - action_entries = actions) + # Menu loop + selection = menu_select( + title = 'Which version of Windows are we installing?', + main_entries = WINDOWS_VERSIONS, + action_entries = actions) + + if selection.isnumeric(): + return WINDOWS_VERSIONS[int(selection)-1] + elif selection == 'M': + raise GenericAbort - if selection.isnumeric(): - return WINDOWS_VERSIONS[int(selection)-1] - elif selection == 'M': - raise GenericAbort def setup_windows(windows_image, windows_version): - """Apply a Windows image to W:""" - cmd = [ - global_vars['Tools']['wimlib-imagex'], - 'apply', - windows_image['Path'], - windows_version['Image Name'], - 'W:\\'] - if 'Glob' in windows_image: - cmd.extend(windows_image['Glob']) - run_program(cmd) + """Apply a Windows image to W:""" + cmd = [ + global_vars['Tools']['wimlib-imagex'], + 'apply', + windows_image['Path'], + windows_version['Image Name'], + 'W:\\'] + if 'Glob' in windows_image: + cmd.extend(windows_image['Glob']) + run_program(cmd) + def setup_windows_re(windows_version, windows_letter='W', tools_letter='T'): - """Setup the WinRE partition.""" - win = r'{}:\Windows'.format(windows_letter) - winre = r'{}\System32\Recovery\WinRE.wim'.format(win) - dest = r'{}:\Recovery\WindowsRE'.format(tools_letter) + """Setup the WinRE partition.""" + win = r'{}:\Windows'.format(windows_letter) + winre = r'{}\System32\Recovery\WinRE.wim'.format(win) + dest = r'{}:\Recovery\WindowsRE'.format(tools_letter) - # Copy WinRE.wim - os.makedirs(dest, exist_ok=True) - shutil.copy(winre, r'{}\WinRE.wim'.format(dest)) + # Copy WinRE.wim + os.makedirs(dest, exist_ok=True) + shutil.copy(winre, r'{}\WinRE.wim'.format(dest)) + + # Set location + cmd = [ + r'{}\System32\ReAgentc.exe'.format(win), + '/setreimage', + '/path', dest, + '/target', win] + run_program(cmd) - # Set location - cmd = [ - r'{}\System32\ReAgentc.exe'.format(win), - '/setreimage', - '/path', dest, - '/target', win] - run_program(cmd) def update_boot_partition(system_letter='S', windows_letter='W', mode='ALL'): - """Setup the Windows boot partition.""" - cmd = [ - r'{}\Windows\System32\bcdboot.exe'.format( - global_vars['Env']['SYSTEMDRIVE']), - r'{}:\Windows'.format(windows_letter), - '/s', '{}:'.format(system_letter), - '/f', mode] - run_program(cmd) + """Setup the Windows boot partition.""" + cmd = [ + r'{}\Windows\System32\bcdboot.exe'.format( + global_vars['Env']['SYSTEMDRIVE']), + r'{}:\Windows'.format(windows_letter), + '/s', '{}:'.format(system_letter), + '/f', mode] + run_program(cmd) + def wim_contains_image(filename, imagename): - """Check if an ESD/WIM contains the specified image, returns bool.""" - cmd = [ - global_vars['Tools']['wimlib-imagex'], - 'info', - filename, - imagename] - try: - run_program(cmd) - except subprocess.CalledProcessError: - return False + """Check if an ESD/WIM contains the specified image, returns bool.""" + cmd = [ + global_vars['Tools']['wimlib-imagex'], + 'info', + filename, + imagename] + try: + run_program(cmd) + except subprocess.CalledProcessError: + return False + + return True - return True if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/windows_updates.py b/.bin/Scripts/functions/windows_updates.py new file mode 100644 index 00000000..4257f066 --- /dev/null +++ b/.bin/Scripts/functions/windows_updates.py @@ -0,0 +1,52 @@ +# Wizard Kit: Functions - Windows updates + +from functions.common import * + + +# Functions +def disable_service(service_name): + """Set service startup to disabled.""" + run_program(['sc', 'config', service_name, 'start=', 'disabled']) + + +def disable_windows_updates(): + """Disable windows updates and clear SoftwareDistribution folder.""" + update_folders = [ + r'{WINDIR}\SoftwareDistribution'.format(**global_vars['Env']), + r'{SYSTEMDRIVE}\$WINDOWS.~BT'.format(**global_vars['Env']), + ] + + # Stop services + for service in ('wuauserv', 'bits'): + stop_service(service) + disable_service(service) + + # Delete update folders + for folder_path in update_folders: + if os.path.exists(folder_path): + shutil.rmtree(folder_path) + + +def enable_service(service_name): + """Set service startup to enabled.""" + try: + run_program(['sc', 'config', service_name, 'start=', 'automatic']) + except subprocess.CalledProcessError: + run_program(['sc', 'config', service_name, 'start=', 'auto']) + + +def enable_windows_updates(): + """Enable windows updates""" + enable_service('bits') + enable_service('wuauserv') + + +def stop_service(service_name): + """Stop service.""" + run_program(['net', 'stop', service_name], check=False) + + +if __name__ == '__main__': + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/functions/winpe_menus.py b/.bin/Scripts/functions/winpe_menus.py index 1c732eca..22df7449 100644 --- a/.bin/Scripts/functions/winpe_menus.py +++ b/.bin/Scripts/functions/winpe_menus.py @@ -4,468 +4,478 @@ from functions.backup import * from functions.disk import * from functions.windows_setup import * + # STATIC VARIABLES FAST_COPY_PE_ARGS = [ - '/cmd=noexist_only', - '/utf8', - '/skip_empty_dir', - '/linkdest', - '/no_ui', - '/auto_close', - '/exclude={}'.format(';'.join(FAST_COPY_EXCLUDES)), - ] + '/cmd=noexist_only', + '/utf8', + '/skip_empty_dir', + '/linkdest', + '/no_ui', + '/auto_close', + '/exclude={}'.format(';'.join(FAST_COPY_EXCLUDES)), + ] PE_TOOLS = { - 'BlueScreenView': { - 'Path': r'BlueScreenView\BlueScreenView.exe', - }, - 'FastCopy': { - 'Path': r'FastCopy\FastCopy.exe', - 'Args': FAST_COPY_PE_ARGS, - }, - 'HWiNFO': { - 'Path': r'HWiNFO\HWiNFO.exe', - }, - 'NT Password Editor': { - 'Path': r'NT Password Editor\ntpwedit.exe', - }, - 'Notepad++': { - 'Path': r'NotepadPlusPlus\NotepadPlusPlus.exe', - }, - 'PhotoRec': { - 'Path': r'TestDisk\photorec_win.exe', - 'Args': ['-new_console:n'], - }, - 'Prime95': { - 'Path': r'Prime95\prime95.exe', - }, - 'ProduKey': { - 'Path': r'ProduKey\ProduKey.exe', - }, - 'Q-Dir': { - 'Path': r'Q-Dir\Q-Dir.exe', - }, - 'TestDisk': { - 'Path': r'TestDisk\testdisk_win.exe', - 'Args': ['-new_console:n'], - }, - } + 'BlueScreenView': { + 'Path': r'BlueScreenView\BlueScreenView.exe', + }, + 'FastCopy': { + 'Path': r'FastCopy\FastCopy.exe', + 'Args': FAST_COPY_PE_ARGS, + }, + 'HWiNFO': { + 'Path': r'HWiNFO\HWiNFO.exe', + }, + 'NT Password Editor': { + 'Path': r'NT Password Editor\ntpwedit.exe', + }, + 'Notepad++': { + 'Path': r'NotepadPlusPlus\NotepadPlusPlus.exe', + }, + 'PhotoRec': { + 'Path': r'TestDisk\photorec_win.exe', + 'Args': ['-new_console:n'], + }, + 'Prime95': { + 'Path': r'Prime95\prime95.exe', + }, + 'ProduKey': { + 'Path': r'ProduKey\ProduKey.exe', + }, + 'Q-Dir': { + 'Path': r'Q-Dir\Q-Dir.exe', + }, + 'TestDisk': { + 'Path': r'TestDisk\testdisk_win.exe', + 'Args': ['-new_console:n'], + }, + } + def check_pe_tools(): - """Fix tool paths for WinPE layout.""" - for k in PE_TOOLS.keys(): - PE_TOOLS[k]['Path'] = r'{}\{}'.format( - global_vars['BinDir'], PE_TOOLS[k]['Path']) - global_vars['Tools']['wimlib-imagex'] = re.sub( - r'\\x(32|64)', - r'', - global_vars['Tools']['wimlib-imagex'], - re.IGNORECASE) + """Fix tool paths for WinPE layout.""" + for k in PE_TOOLS.keys(): + PE_TOOLS[k]['Path'] = r'{}\{}'.format( + global_vars['BinDir'], PE_TOOLS[k]['Path']) + global_vars['Tools']['wimlib-imagex'] = re.sub( + r'\\x(32|64)', + r'', + global_vars['Tools']['wimlib-imagex'], + re.IGNORECASE) + def menu_backup(): - """Take backup images of partition(s) in the WIM format.""" - errors = False - other_results = { - 'Error': { - 'CalledProcessError': 'Unknown Error', - 'PathNotFoundError': 'Missing', - }, - 'Warning': { - 'GenericAbort': 'Skipped', - 'GenericRepair': 'Repaired', - }} - set_title('{}: Backup Menu'.format(KIT_NAME_FULL)) + """Take backup images of partition(s) in the WIM format.""" + errors = False + other_results = { + 'Error': { + 'CalledProcessError': 'Unknown Error', + 'PathNotFoundError': 'Missing', + }, + 'Warning': { + 'GenericAbort': 'Skipped', + 'GenericRepair': 'Repaired', + }} + set_title('{}: Backup Menu'.format(KIT_NAME_FULL)) - # Set backup prefix - clear_screen() - print_standard('{}\n'.format(global_vars['Title'])) - ticket_number = get_ticket_number() - if ENABLED_TICKET_NUMBERS: - backup_prefix = ticket_number + # Set backup prefix + clear_screen() + print_standard('{}\n'.format(global_vars['Title'])) + ticket_number = get_ticket_number() + if ENABLED_TICKET_NUMBERS: + backup_prefix = ticket_number + else: + backup_prefix = get_simple_string(prompt='Enter backup name prefix') + backup_prefix = backup_prefix.replace(' ', '_') + + # Assign drive letters + try_and_print( + message = 'Assigning letters...', + function = assign_volume_letters, + other_results = other_results) + + # Mount backup shares + mount_backup_shares(read_write=True) + + # Select destination + destination = select_backup_destination(auto_select=False) + + # Scan disks + result = try_and_print( + message = 'Getting disk info...', + function = scan_disks, + other_results = other_results) + if result['CS']: + disks = result['Out'] + else: + print_error('ERROR: No disks found.') + raise GenericAbort + + # Select disk to backup + disk = select_disk('For which disk are we creating backups?', disks) + if not disk: + raise GenericAbort + + # "Prep" disk + prep_disk_for_backup(destination, disk, backup_prefix) + + # Display details for backup task + clear_screen() + print_info('Create Backup - Details:\n') + if ENABLED_TICKET_NUMBERS: + show_data(message='Ticket:', data=ticket_number) + show_data( + message = 'Source:', + data = '[{}] ({}) {} {}'.format( + disk.get('Table', ''), + disk.get('Type', ''), + disk.get('Name', 'Unknown'), + disk.get('Size', ''), + ), + ) + show_data( + message = 'Destination:', + data = destination.get('Display Name', destination['Name']), + ) + for par in disk['Partitions']: + message = 'Partition {}:'.format(par['Number']) + data = par['Display String'] + if par['Number'] in disk['Bad Partitions']: + show_data(message=message, data=data, width=30, warning=True) + if 'Error' in par: + show_data(message='', data=par['Error'], error=True) + elif par['Image Exists']: + show_data(message=message, data=data, width=30, info=True) else: - backup_prefix = get_simple_string(prompt='Enter backup name prefix') - backup_prefix = backup_prefix.replace(' ', '_') + show_data(message=message, data=data, width=30) + print_standard(disk['Backup Warnings']) - # Assign drive letters - try_and_print( - message = 'Assigning letters...', - function = assign_volume_letters, - other_results = other_results) + # Ask to proceed + if (not ask('Proceed with backup?')): + raise GenericAbort - # Mount backup shares - mount_backup_shares(read_write=True) - - # Select destination - destination = select_backup_destination(auto_select=False) - - # Scan disks + # Backup partition(s) + print_info('\n\nStarting task.\n') + for par in disk['Partitions']: result = try_and_print( - message = 'Getting disk info...', - function = scan_disks, - other_results = other_results) - if result['CS']: - disks = result['Out'] - else: - print_error('ERROR: No disks found.') - raise GenericAbort + message = 'Partition {} Backup...'.format(par['Number']), + function = backup_partition, + other_results = other_results, + disk = disk, + par = par) + if not result['CS'] and not isinstance(result['Error'], GenericAbort): + errors = True + par['Error'] = result['Error'] - # Select disk to backup - disk = select_disk('For which disk are we creating backups?', disks) - if not disk: - raise GenericAbort - - # "Prep" disk - prep_disk_for_backup(destination, disk, backup_prefix) - - # Display details for backup task - clear_screen() - print_info('Create Backup - Details:\n') - if ENABLED_TICKET_NUMBERS: - show_data(message='Ticket:', data=ticket_number) - show_data( - message = 'Source:', - data = '[{}] ({}) {} {}'.format( - disk.get('Table', ''), - disk.get('Type', ''), - disk.get('Name', 'Unknown'), - disk.get('Size', ''), - ), - ) - show_data( - message = 'Destination:', - data = destination.get('Display Name', destination['Name']), - ) + # Verify backup(s) + if disk['Valid Partitions']: + print_info('\n\nVerifying backup images(s)\n') for par in disk['Partitions']: - message = 'Partition {}:'.format(par['Number']) - data = par['Display String'] - if par['Number'] in disk['Bad Partitions']: - show_data(message=message, data=data, width=30, warning=True) - if 'Error' in par: - show_data(message='', data=par['Error'], error=True) - elif par['Image Exists']: - show_data(message=message, data=data, width=30, info=True) - else: - show_data(message=message, data=data, width=30) - print_standard(disk['Backup Warnings']) + if par['Number'] in disk['Bad Partitions']: + continue # Skip verification + result = try_and_print( + message = 'Partition {} Image...'.format(par['Number']), + function = verify_wim_backup, + other_results = other_results, + partition = par) + if not result['CS']: + errors = True + par['Error'] = result['Error'] - # Ask to proceed - if (not ask('Proceed with backup?')): - raise GenericAbort - - # Backup partition(s) - print_info('\n\nStarting task.\n') - for par in disk['Partitions']: - result = try_and_print( - message = 'Partition {} Backup...'.format(par['Number']), - function = backup_partition, - other_results = other_results, - disk = disk, - par = par) - if not result['CS'] and not isinstance(result['Error'], GenericAbort): - errors = True - par['Error'] = result['Error'] - - # Verify backup(s) - if disk['Valid Partitions']: - print_info('\n\nVerifying backup images(s)\n') - for par in disk['Partitions']: - if par['Number'] in disk['Bad Partitions']: - continue # Skip verification - result = try_and_print( - message = 'Partition {} Image...'.format(par['Number']), - function = verify_wim_backup, - other_results = other_results, - partition = par) - if not result['CS']: - errors = True - par['Error'] = result['Error'] - - # Print summary - if errors: - print_warning('\nErrors were encountered and are detailed below.') - for par in [p for p in disk['Partitions'] if 'Error' in p]: - print_standard(' Partition {} Error:'.format(par['Number'])) - if hasattr(par['Error'], 'stderr'): - try: - par['Error'] = par['Error'].stderr.decode() - except: - # Deal with badly formatted error message - pass - try: - par['Error'] = par['Error'].splitlines() - par['Error'] = [line.strip() for line in par['Error']] - par['Error'] = [line for line in par['Error'] if line] - for line in par['Error']: - print_error('\t{}'.format(line)) - except: - print_error('\t{}'.format(par['Error'])) - else: - print_success('\nNo errors were encountered during imaging.') - if 'LogFile' in global_vars and ask('\nReview log?'): - cmd = [ - global_vars['Tools']['NotepadPlusPlus'], - global_vars['LogFile']] + # Print summary + if errors: + print_warning('\nErrors were encountered and are detailed below.') + for par in [p for p in disk['Partitions'] if 'Error' in p]: + print_standard(' Partition {} Error:'.format(par['Number'])) + if hasattr(par['Error'], 'stderr'): try: - popen_program(cmd) - except Exception: - print_error('ERROR: Failed to open log.') - sleep(30) - pause('\nPress Enter to return to main menu... ') + par['Error'] = par['Error'].stderr.decode() + except: + # Deal with badly formatted error message + pass + try: + par['Error'] = par['Error'].splitlines() + par['Error'] = [line.strip() for line in par['Error']] + par['Error'] = [line for line in par['Error'] if line] + for line in par['Error']: + print_error('\t{}'.format(line)) + except: + print_error('\t{}'.format(par['Error'])) + else: + print_success('\nNo errors were encountered during imaging.') + if 'LogFile' in global_vars and ask('\nReview log?'): + cmd = [ + global_vars['Tools']['NotepadPlusPlus'], + global_vars['LogFile']] + try: + popen_program(cmd) + except Exception: + print_error('ERROR: Failed to open log.') + sleep(30) + pause('\nPress Enter to return to main menu... ') + def menu_root(): - """Main WinPE menu.""" - check_pe_tools() - menus = [ - {'Name': 'Create Backups', 'Menu': menu_backup}, - {'Name': 'Setup Windows', 'Menu': menu_setup}, - {'Name': 'Misc Tools', 'Menu': menu_tools}, - ] - actions = [ - {'Name': 'Command Prompt', 'Letter': 'C'}, - {'Name': 'Reboot', 'Letter': 'R'}, - {'Name': 'Shutdown', 'Letter': 'S'}, - ] + """Main WinPE menu.""" + check_pe_tools() + menus = [ + {'Name': 'Create Backups', 'Menu': menu_backup}, + {'Name': 'Setup Windows', 'Menu': menu_setup}, + {'Name': 'Misc Tools', 'Menu': menu_tools}, + ] + actions = [ + {'Name': 'Command Prompt', 'Letter': 'C'}, + {'Name': 'Reboot', 'Letter': 'R'}, + {'Name': 'Shutdown', 'Letter': 'S'}, + ] - # Main loop - while True: - set_title(KIT_NAME_FULL) - selection = menu_select( - title = 'Main Menu', - main_entries = menus, - action_entries = actions, - secret_exit = True) + # Main loop + while True: + set_title(KIT_NAME_FULL) + selection = menu_select( + title = 'Main Menu', + main_entries = menus, + action_entries = actions, + secret_exit = True) + + if (selection.isnumeric()): + try: + menus[int(selection)-1]['Menu']() + except GenericAbort: + print_warning('\nAborted\n') + pause('Press Enter to return to main menu... ') + elif (selection == 'C'): + run_program(['cmd', '-new_console:n'], check=False) + elif (selection == 'R'): + run_program(['wpeutil', 'reboot']) + elif (selection == 'S'): + run_program(['wpeutil', 'shutdown']) + else: + sys.exit() - if (selection.isnumeric()): - try: - menus[int(selection)-1]['Menu']() - except GenericAbort: - print_warning('\nAborted\n') - pause('Press Enter to return to main menu... ') - elif (selection == 'C'): - run_program(['cmd', '-new_console:n'], check=False) - elif (selection == 'R'): - run_program(['wpeutil', 'reboot']) - elif (selection == 'S'): - run_program(['wpeutil', 'shutdown']) - else: - sys.exit() def menu_setup(): - """Format a disk (MBR/GPT), apply a Windows image, and setup boot files.""" - errors = False - other_results = { - 'Error': { - 'CalledProcessError': 'Unknown Error', - 'PathNotFoundError': 'Missing', - }, - 'Warning': { - 'GenericAbort': 'Skipped', - 'GenericRepair': 'Repaired', - }} - set_title('{}: Setup Menu'.format(KIT_NAME_FULL)) + """Format a disk, apply a Windows image, and create boot files.""" + errors = False + other_results = { + 'Error': { + 'CalledProcessError': 'Unknown Error', + 'PathNotFoundError': 'Missing', + }, + 'Warning': { + 'GenericAbort': 'Skipped', + 'GenericRepair': 'Repaired', + }} + set_title('{}: Setup Menu'.format(KIT_NAME_FULL)) - # Set ticket ID - clear_screen() - print_standard('{}\n'.format(global_vars['Title'])) - ticket_number = get_ticket_number() + # Set ticket ID + clear_screen() + print_standard('{}\n'.format(global_vars['Title'])) + ticket_number = get_ticket_number() - # Select the version of Windows to apply - windows_version = select_windows_version() + # Select the version of Windows to apply + windows_version = select_windows_version() - # Find Windows image - # NOTE: Reassign volume letters to ensure all devices are scanned - try_and_print( - message = 'Assigning volume letters...', - function = assign_volume_letters, - other_results = other_results) - windows_image = find_windows_image(windows_version) + # Find Windows image + # NOTE: Reassign volume letters to ensure all devices are scanned + try_and_print( + message = 'Assigning volume letters...', + function = assign_volume_letters, + other_results = other_results) + windows_image = find_windows_image(windows_version) - # Scan disks - result = try_and_print( - message = 'Getting disk info...', - function = scan_disks, - other_results = other_results) - if result['CS']: - disks = result['Out'] - else: - print_error('ERROR: No disks found.') - raise GenericAbort + # Scan disks + result = try_and_print( + message = 'Getting disk info...', + function = scan_disks, + other_results = other_results) + if result['CS']: + disks = result['Out'] + else: + print_error('ERROR: No disks found.') + raise GenericAbort - # Select disk to use as the OS disk - dest_disk = select_disk('To which disk are we installing Windows?', disks) - if not dest_disk: - raise GenericAbort + # Select disk to use as the OS disk + dest_disk = select_disk('To which disk are we installing Windows?', disks) + if not dest_disk: + raise GenericAbort - # "Prep" disk - prep_disk_for_formatting(dest_disk) + # "Prep" disk + prep_disk_for_formatting(dest_disk) - # Display details for setup task - clear_screen() - print_info('Setup Windows - Details:\n') - if ENABLED_TICKET_NUMBERS: - show_data(message='Ticket:', data=ticket_number) - show_data(message='Installing:', data=windows_version['Name']) + # Display details for setup task + clear_screen() + print_info('Setup Windows - Details:\n') + if ENABLED_TICKET_NUMBERS: + show_data(message='Ticket:', data=ticket_number) + show_data(message='Installing:', data=windows_version['Name']) + show_data( + message = 'Boot Method:', + data = 'UEFI (GPT)' if dest_disk['Use GPT'] else 'Legacy (MBR)') + show_data(message='Using Image:', data=windows_image['Path']) + show_data( + message = 'ERASING:', + data = '[{}] ({}) {} {}\n'.format( + dest_disk.get('Table', ''), + dest_disk.get('Type', ''), + dest_disk.get('Name', 'Unknown'), + dest_disk.get('Size', ''), + ), + warning = True) + for par in dest_disk['Partitions']: show_data( - message = 'Boot Method:', - data = 'UEFI (GPT)' if dest_disk['Use GPT'] else 'Legacy (MBR)') - show_data(message='Using Image:', data=windows_image['Path']) - show_data( - message = 'ERASING:', - data = '[{}] ({}) {} {}\n'.format( - dest_disk.get('Table', ''), - dest_disk.get('Type', ''), - dest_disk.get('Name', 'Unknown'), - dest_disk.get('Size', ''), - ), - warning = True) - for par in dest_disk['Partitions']: - show_data( - message = 'Partition {}:'.format(par['Number']), - data = par['Display String'], - warning = True) - print_warning(dest_disk['Format Warnings']) + message = 'Partition {}:'.format(par['Number']), + data = par['Display String'], + warning = True) + print_warning(dest_disk['Format Warnings']) - if (not ask('Is this correct?')): - raise GenericAbort + if (not ask('Is this correct?')): + raise GenericAbort - # Safety check - print_standard('\nSAFETY CHECK') - print_warning('All data will be DELETED from the ' - 'disk and partition(s) listed above.') - print_warning('This is irreversible and will lead ' - 'to {CLEAR}{RED}DATA LOSS.'.format(**COLORS)) - if (not ask('Asking again to confirm, is this correct?')): - raise GenericAbort + # Safety check + print_standard('\nSAFETY CHECK') + print_warning('All data will be DELETED from the ' + 'disk and partition(s) listed above.') + print_warning('This is irreversible and will lead ' + 'to {CLEAR}{RED}DATA LOSS.'.format(**COLORS)) + if (not ask('Asking again to confirm, is this correct?')): + raise GenericAbort - # Remove volume letters so S, T, & W can be used below - try_and_print( - message = 'Removing volume letters...', - function = remove_volume_letters, - other_results = other_results, - keep=windows_image['Letter']) + # Remove volume letters so S, T, & W can be used below + try_and_print( + message = 'Removing volume letters...', + function = remove_volume_letters, + other_results = other_results, + keep=windows_image['Letter']) - # Assign new letter for local source if necessary - if windows_image['Local'] and windows_image['Letter'] in ['S', 'T', 'W']: - new_letter = try_and_print( - message = 'Reassigning source volume letter...', - function = reassign_volume_letter, - other_results = other_results, - letter=windows_image['Letter']) - windows_image['Path'] = '{}{}'.format( - new_letter, windows_image['Path'][1:]) - windows_image['Letter'] = new_letter + # Assign new letter for local source if necessary + if windows_image['Local'] and windows_image['Letter'] in ['S', 'T', 'W']: + new_letter = try_and_print( + message = 'Reassigning source volume letter...', + function = reassign_volume_letter, + other_results = other_results, + letter=windows_image['Letter']) + windows_image['Path'] = '{}{}'.format( + new_letter, windows_image['Path'][1:]) + windows_image['Letter'] = new_letter - # Format and partition disk - result = try_and_print( - message = 'Formatting disk...', - function = format_disk, - other_results = other_results, - disk = dest_disk, - use_gpt = dest_disk['Use GPT']) - if not result['CS']: - # We need to crash as the disk is in an unknown state - print_error('ERROR: Failed to format disk.') - raise GenericAbort + # Format and partition disk + result = try_and_print( + message = 'Formatting disk...', + function = format_disk, + other_results = other_results, + disk = dest_disk, + use_gpt = dest_disk['Use GPT']) + if not result['CS']: + # We need to crash as the disk is in an unknown state + print_error('ERROR: Failed to format disk.') + raise GenericAbort - # Apply Image - result = try_and_print( - message = 'Applying image...', - function = setup_windows, - other_results = other_results, - windows_image = windows_image, - windows_version = windows_version) - if not result['CS']: - # We need to crash as the disk is in an unknown state - print_error('ERROR: Failed to apply image.') - raise GenericAbort + # Apply Image + result = try_and_print( + message = 'Applying image...', + function = setup_windows, + other_results = other_results, + windows_image = windows_image, + windows_version = windows_version) + if not result['CS']: + # We need to crash as the disk is in an unknown state + print_error('ERROR: Failed to apply image.') + raise GenericAbort - # Create Boot files - try_and_print( - message = 'Updating boot files...', - function = update_boot_partition, - other_results = other_results) + # Create Boot files + try_and_print( + message = 'Updating boot files...', + function = update_boot_partition, + other_results = other_results) - # Setup WinRE - try_and_print( - message = 'Updating recovery tools...', - function = setup_windows_re, - other_results = other_results, - windows_version = windows_version) + # Setup WinRE + try_and_print( + message = 'Updating recovery tools...', + function = setup_windows_re, + other_results = other_results, + windows_version = windows_version) - # Copy WinPE log(s) - source = r'{}\Logs'.format(global_vars['ClientDir']) - dest = r'W:\{}\Logs\WinPE'.format(KIT_NAME_SHORT) - shutil.copytree(source, dest) + # Copy WinPE log(s) + source = r'{}\Logs'.format(global_vars['ClientDir']) + dest = r'W:\{}\Logs\WinPE'.format(KIT_NAME_SHORT) + shutil.copytree(source, dest) + + # Print summary + print_standard('\nDone.') + if 'LogFile' in global_vars and ask('\nReview log?'): + cmd = [ + global_vars['Tools']['NotepadPlusPlus'], + global_vars['LogFile']] + try: + popen_program(cmd) + except Exception: + print_error('ERROR: Failed to open log.') + sleep(30) + pause('\nPress Enter to return to main menu... ') - # Print summary - print_standard('\nDone.') - if 'LogFile' in global_vars and ask('\nReview log?'): - cmd = [ - global_vars['Tools']['NotepadPlusPlus'], - global_vars['LogFile']] - try: - popen_program(cmd) - except Exception: - print_error('ERROR: Failed to open log.') - sleep(30) - pause('\nPress Enter to return to main menu... ') def menu_tools(): - """Tool launcher menu.""" - tools = [{'Name': k} for k in sorted(PE_TOOLS.keys())] - actions = [{'Name': 'Main Menu', 'Letter': 'M'},] - set_title(KIT_NAME_FULL) + """Tool launcher menu.""" + tools = [{'Name': k} for k in sorted(PE_TOOLS.keys())] + actions = [{'Name': 'Main Menu', 'Letter': 'M'},] + set_title(KIT_NAME_FULL) + + # Menu loop + while True: + selection = menu_select( + title = 'Tools Menu', + main_entries = tools, + action_entries = actions) + if (selection.isnumeric()): + name = tools[int(selection)-1]['Name'] + cmd = [PE_TOOLS[name]['Path']] + PE_TOOLS[name].get('Args', []) + if name == 'Blue Screen View': + # Select path to scan + minidump_path = select_minidump_path() + if minidump_path: + cmd.extend(['/MiniDumpFolder', minidump_path]) + try: + popen_program(cmd) + except Exception: + print_error('Failed to run {}'.format(name)) + sleep(2) + pause() + elif (selection == 'M'): + break - # Menu loop - while True: - selection = menu_select( - title = 'Tools Menu', - main_entries = tools, - action_entries = actions) - if (selection.isnumeric()): - name = tools[int(selection)-1]['Name'] - cmd = [PE_TOOLS[name]['Path']] + PE_TOOLS[name].get('Args', []) - if name == 'Blue Screen View': - # Select path to scan - minidump_path = select_minidump_path() - if minidump_path: - cmd.extend(['/MiniDumpFolder', minidump_path]) - try: - popen_program(cmd) - except Exception: - print_error('Failed to run {}'.format(name)) - sleep(2) - pause() - elif (selection == 'M'): - break def select_minidump_path(): - """Select BSOD minidump path from a menu.""" - dumps = [] + """Select BSOD minidump path from a menu.""" + dumps = [] - # Assign volume letters first - assign_volume_letters() + # Assign volume letters first + assign_volume_letters() - # Search for minidumps - set_thread_error_mode(silent=True) # Prevents "No disk" popups - for d in psutil.disk_partitions(): - if global_vars['Env']['SYSTEMDRIVE'].upper() in d.mountpoint: - # Skip RAMDisk - continue - if os.path.exists(r'{}Windows\MiniDump'.format(d.mountpoint)): - dumps.append({'Name': r'{}Windows\MiniDump'.format(d.mountpoint)}) - set_thread_error_mode(silent=False) # Return to normal + # Search for minidumps + set_thread_error_mode(silent=True) # Prevents "No disk" popups + for d in psutil.disk_partitions(): + if global_vars['Env']['SYSTEMDRIVE'].upper() in d.mountpoint: + # Skip RAMDisk + continue + if os.path.exists(r'{}Windows\MiniDump'.format(d.mountpoint)): + dumps.append({'Name': r'{}Windows\MiniDump'.format(d.mountpoint)}) + set_thread_error_mode(silent=False) # Return to normal - # Check results before showing menu - if len(dumps) == 0: - print_error('ERROR: No BSoD / MiniDump paths found') - sleep(2) - return None + # Check results before showing menu + if len(dumps) == 0: + print_error('ERROR: No BSoD / MiniDump paths found') + sleep(2) + return None + + # Menu + selection = menu_select( + title = 'Which BSoD / MiniDump path are we scanning?', + main_entries = dumps) + return dumps[int(selection) - 1]['Name'] - # Menu - selection = menu_select( - title = 'Which BSoD / MiniDump path are we scanning?', - main_entries = dumps) - return dumps[int(selection) - 1]['Name'] if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/hw-diags-audio b/.bin/Scripts/hw-diags-audio index 88168263..3d3a5991 100755 --- a/.bin/Scripts/hw-diags-audio +++ b/.bin/Scripts/hw-diags-audio @@ -6,37 +6,37 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.common import * init_global_vars() if __name__ == '__main__': + try: + # Prep + clear_screen() + print_standard('Hardware Diagnostics: Audio\n') + + # Set volume try: - # Prep - clear_screen() - print_standard('Hardware Diagnostics: Audio\n') + run_program('amixer -q set "Master" 80% unmute'.split()) + run_program('amixer -q set "PCM" 90% unmute'.split()) + except subprocess.CalledProcessError: + print_error('Failed to set volume') - # Set volume - try: - run_program('amixer -q set "Master" 80% unmute'.split()) - run_program('amixer -q set "PCM" 90% unmute'.split()) - except subprocess.CalledProcessError: - print_error('Failed to set volume') - - # Run tests - for mode in ['pink', 'wav']: - run_program( - cmd = 'speaker-test -c 2 -l 1 -t {}'.format(mode).split(), - check = False, - pipe = False) - - # Done - #print_standard('\nDone.') - #pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() + # Run tests + for mode in ['pink', 'wav']: + run_program( + cmd = 'speaker-test -c 2 -l 1 -t {}'.format(mode).split(), + check = False, + pipe = False) + # Done + #print_standard('\nDone.') + #pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/hw-diags-badblocks b/.bin/Scripts/hw-diags-badblocks deleted file mode 100755 index 2d915766..00000000 --- a/.bin/Scripts/hw-diags-badblocks +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -## Wizard Kit: HW Diagnostics - badblocks - -function usage { - echo "Usage: $0 device log-file" - echo " e.g. $0 /dev/sda /tmp/tmp.XXXXXXX/badblocks.log" -} - -# Bail early -if [ ! -b "$1" ]; then - usage - exit 1 -fi - -# Run Badblocks -sudo badblocks -sv -e 1 "$1" 2>&1 | tee -a "$2" - diff --git a/.bin/Scripts/hw-diags-menu b/.bin/Scripts/hw-diags-menu index f7c1739c..1c3cfc42 100755 --- a/.bin/Scripts/hw-diags-menu +++ b/.bin/Scripts/hw-diags-menu @@ -6,25 +6,31 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.hw_diags import * +from functions.tmux import * init_global_vars() if __name__ == '__main__': - try: - # Prep - clear_screen() + # Show menu + try: + state = State() + menu_diags(state, sys.argv) + except KeyboardInterrupt: + print_standard(' ') + print_warning('Aborted') + print_standard(' ') + sleep(1) + pause('Press Enter to exit...') + except SystemExit: + # Normal exit + pass + except: + tmux_kill_all_panes() + major_exception() - # Show menu - menu_diags(*sys.argv) - - # Done - #print_standard('\nDone.') - #pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + tmux_kill_all_panes() + exit_script() +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/hw-diags-network b/.bin/Scripts/hw-diags-network index 5fdd26a4..3047e131 100755 --- a/.bin/Scripts/hw-diags-network +++ b/.bin/Scripts/hw-diags-network @@ -6,41 +6,43 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.network import * + def check_connection(): - if not is_connected(): - # Raise to cause NS in try_and_print() - raise Exception + if not is_connected(): + # Raise to cause NS in try_and_print() + raise Exception + if __name__ == '__main__': - try: - # Prep - clear_screen() - print_standard('Hardware Diagnostics: Network\n') + try: + # Prep + clear_screen() + print_standard('Hardware Diagnostics: Network\n') - # Connect - print_standard('Initializing...') - connect_to_network() + # Connect + print_standard('Initializing...') + connect_to_network() - # Tests - try_and_print( - message='Network connection:', function=check_connection, cs='OK') - show_valid_addresses() - try_and_print(message='Internet connection:', function=ping, - addr='8.8.8.8', cs='OK') - try_and_print(message='DNS Resolution:', function=ping, cs='OK') - try_and_print(message='Speedtest:', function=speedtest, - print_return=True) - - # Done - print_standard('\nDone.') - #pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() + # Tests + try_and_print( + message='Network connection:', function=check_connection, cs='OK') + show_valid_addresses() + try_and_print(message='Internet connection:', function=ping, + addr='8.8.8.8', cs='OK') + try_and_print(message='DNS Resolution:', function=ping, cs='OK') + try_and_print(message='Speedtest:', function=speedtest, + print_return=True) + # Done + print_standard('\nDone.') + #pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/hw-diags-prime95 b/.bin/Scripts/hw-diags-prime95 index 30c6994d..4927da76 100755 --- a/.bin/Scripts/hw-diags-prime95 +++ b/.bin/Scripts/hw-diags-prime95 @@ -14,5 +14,6 @@ if [ ! -d "$1" ]; then fi # Run Prime95 -mprime -t | grep -iv --line-buffered 'stress.txt' | tee -a "$1/prime.log" +cd "$1" +mprime -t | grep -iv --line-buffered 'stress.txt' | tee -a "prime.log" diff --git a/.bin/Scripts/hw-info b/.bin/Scripts/hw-info index 98514e3e..8321e7aa 100755 --- a/.bin/Scripts/hw-info +++ b/.bin/Scripts/hw-info @@ -105,8 +105,3 @@ echo -e "${BLUE}Drives${CLEAR}" hw-drive-info | sed 's/^/ /' echo "" -# Sensors -echo -e "${BLUE}Sensors${CLEAR}" -hw-sensors | sed 's/^/ /' -echo "" - diff --git a/.bin/Scripts/hw-sensors b/.bin/Scripts/hw-sensors index f251c589..39ca7147 100755 --- a/.bin/Scripts/hw-sensors +++ b/.bin/Scripts/hw-sensors @@ -1,168 +1,10 @@ -#!/bin/python3 +#!/bin/bash # ## Wizard Kit: Sensor monitoring tool -import itertools -import os -import shutil -import sys +WINDOW_NAME="Hardware Sensors" +MONITOR="hw-sensors-monitor" -# Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) -from functions.common import * -from borrowed import sensors - -# STATIC VARIABLES -COLORS = { - 'CLEAR': '\033[0m', - 'RED': '\033[31m', - 'GREEN': '\033[32m', - 'YELLOW': '\033[33m', - 'ORANGE': '\033[31;1m', - 'BLUE': '\033[34m' - } -TEMP_LIMITS = { - 'GREEN': 60, - 'YELLOW': 70, - 'ORANGE': 80, - 'RED': 90, - } - -# REGEX -REGEX_COLORS = re.compile(r'\033\[\d+;?1?m') - -def color_temp(temp): - try: - temp = float(temp) - except ValueError: - return '{YELLOW}{temp}{CLEAR}'.format(temp=temp, **COLORS) - if temp > TEMP_LIMITS['RED']: - color = COLORS['RED'] - elif temp > TEMP_LIMITS['ORANGE']: - color = COLORS['ORANGE'] - elif temp > TEMP_LIMITS['YELLOW']: - color = COLORS['YELLOW'] - elif temp > TEMP_LIMITS['GREEN']: - color = COLORS['GREEN'] - elif temp > 0: - color = COLORS['BLUE'] - else: - color = COLORS['CLEAR'] - return '{color}{prefix}{temp:2.0f}°C{CLEAR}'.format( - color = color, - prefix = '-' if temp < 0 else '', - temp = temp, - **COLORS) - -def get_feature_string(chip, feature): - sfs = list(sensors.SubFeatureIterator(chip, feature)) # get a list of all subfeatures - label = sensors.get_label(chip, feature) - skipname = len(feature.name)+1 # skip common prefix - data = {} - - if feature.type != sensors.feature.TEMP: - # Skip non-temperature sensors - return None - - for sf in sfs: - name = sf.name[skipname:].decode("utf-8").strip() - try: - val = sensors.get_value(chip, sf.number) - except Exception: - # Ignore upstream sensor bugs and lie instead - val = -123456789 - if 'alarm' in name: - # Skip - continue - if '--nocolor' in sys.argv: - try: - temp = float(val) - except ValueError: - data[name] = ' {}°C'.format(val) - else: - data[name] = '{}{:2.0f}°C'.format( - '-' if temp < 0 else '', - temp) - else: - data[name] = color_temp(val) - - main_temp = data.pop('input', None) - if main_temp: - list_data = [] - for item in ['max', 'crit']: - if item in data: - list_data.append('{}: {}'.format(item, data.pop(item))) - list_data.extend( - ['{}: {}'.format(k, v) for k, v in sorted(data.items())]) - data_str = '{:18} {} {}'.format( - label, main_temp, ', '.join(list_data)) - else: - list_data.extend(sorted(data.items())) - list_data = ['{}: {}'.format(item[0], item[1]) for item in list_data] - data_str = '{:18} {}'.format(label, ', '.join(list_data)) - return data_str - -def join_columns(column1, column2, width=55): - return '{:<{}}{}'.format( - column1, - 55+len(column1)-len(REGEX_COLORS.sub('', column1)), - column2) - -if __name__ == '__main__': - try: - # Prep - sensors.init() - - # Get sensor data - chip_temps = {} - for chip in sensors.ChipIterator(): - chip_name = '{} ({})'.format( - sensors.chip_snprintf_name(chip), - sensors.get_adapter_name(chip.bus)) - chip_feats = [get_feature_string(chip, feature) - for feature in sensors.FeatureIterator(chip)] - # Strip empty/None items - chip_feats = [f for f in chip_feats if f] - - if chip_feats: - chip_temps[chip_name] = [chip_name, *chip_feats, ''] - - # Sort chips - sensor_temps = [] - for chip in [k for k in sorted(chip_temps.keys()) if 'coretemp' in k]: - sensor_temps.extend(chip_temps[chip]) - for chip in sorted(chip_temps.keys()): - if 'coretemp' not in chip: - sensor_temps.extend(chip_temps[chip]) - - # Wrap columns as needed - screen_size = shutil.get_terminal_size() - rows = screen_size.lines - 1 - if len(sensor_temps) > rows and screen_size.columns > 55*2: - sensor_temps = list(itertools.zip_longest( - sensor_temps[:rows], sensor_temps[rows:], fillvalue='')) - sensor_temps = [join_columns(a, b) for a, b in sensor_temps] - - # Print data - if sensor_temps: - for line in sensor_temps: - print_standard(line) - else: - if '--nocolor' in sys.argv: - print_standard('WARNING: No sensors found') - print_standard('\nPlease monitor temps manually') - else: - print_warning('WARNING: No sensors found') - print_standard('\nPlease monitor temps manually') - - # Done - sensors.cleanup() - exit_script() - except SystemExit: - sensors.cleanup() - pass - except: - sensors.cleanup() - major_exception() +# Start session +tmux new-session -n "$WINDOW_NAME" "$MONITOR" diff --git a/.bin/Scripts/hw-sensors-monitor b/.bin/Scripts/hw-sensors-monitor new file mode 100755 index 00000000..c27d786a --- /dev/null +++ b/.bin/Scripts/hw-sensors-monitor @@ -0,0 +1,36 @@ +#!/bin/python3 +# +## Wizard Kit: Sensor monitoring tool + +import os +import sys + +# Init +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from functions.sensors import * +from functions.tmux import * +init_global_vars(silent=True) + +if __name__ == '__main__': + background = False + try: + if len(sys.argv) > 1 and os.path.exists(sys.argv[1]): + background = True + monitor_file = sys.argv[1] + monitor_pane = None + else: + result = run_program(['mktemp']) + monitor_file = result.stdout.decode().strip() + if not background: + monitor_pane = tmux_split_window( + percent=1, vertical=True, watch=monitor_file) + cmd = ['tmux', 'resize-pane', '-Z', '-t', monitor_pane] + run_program(cmd, check=False) + monitor_sensors(monitor_pane, monitor_file) + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/install_eset_nod32_av.py b/.bin/Scripts/install_eset_nod32_av.py index b53adfb6..ecdd9da6 100644 --- a/.bin/Scripts/install_eset_nod32_av.py +++ b/.bin/Scripts/install_eset_nod32_av.py @@ -12,15 +12,17 @@ os.system('title {}: Install ESET NOD32 AV'.format(KIT_NAME_FULL)) set_log_file('Install ESET NOD32 AV.log') if __name__ == '__main__': - try: - stay_awake() - clear_screen() - print_info('{}: Install ESET NOD32 AV\n'.format(KIT_NAME_FULL)) - scan_pups = ask('Enable PUP scans in ESET?') - install_eset_nod32_av(scan_pups) - print_standard('\nDone.') - exit_script() - except SystemExit: - pass - except: - major_exception() + try: + stay_awake() + clear_screen() + print_info('{}: Install ESET NOD32 AV\n'.format(KIT_NAME_FULL)) + scan_pups = ask('Enable PUP scans in ESET?') + install_eset_nod32_av(scan_pups) + print_standard('\nDone.') + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/install_sw_bundle.py b/.bin/Scripts/install_sw_bundle.py index e04ea88a..9b51c432 100644 --- a/.bin/Scripts/install_sw_bundle.py +++ b/.bin/Scripts/install_sw_bundle.py @@ -4,8 +4,7 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.setup import * init_global_vars() os.system('title {}: SW Bundle Tool'.format(KIT_NAME_FULL)) @@ -13,52 +12,55 @@ set_log_file('Install SW Bundle.log') D7_MODE = 'd7mode' in sys.argv if __name__ == '__main__': - try: - stay_awake() - clear_screen() - print_info('{}: SW Bundle Tool\n'.format(KIT_NAME_FULL)) - other_results = { - 'Error': { - 'CalledProcessError': 'Unknown Error', - 'FileNotFoundError': 'File not found', - }, - 'Warning': { - 'GenericRepair': 'Repaired', - 'UnsupportedOSError': 'Unsupported OS', - }} - answer_extensions = D7_MODE or ask('Install Extensions?') - answer_vcr = D7_MODE or ask('Install Visual C++ Runtimes?') - answer_ninite = D7_MODE or ask('Install Ninite Bundle?') - if not D7_MODE and ( - answer_ninite and global_vars['OS']['Version'] in ['7']): - # Vista is dead, not going to check for it - answer_mse = ask('Install MSE?') - else: - answer_mse = False + try: + stay_awake() + clear_screen() + print_info('{}: SW Bundle Tool\n'.format(KIT_NAME_FULL)) + other_results = { + 'Error': { + 'CalledProcessError': 'Unknown Error', + 'FileNotFoundError': 'File not found', + }, + 'Warning': { + 'GenericRepair': 'Repaired', + 'UnsupportedOSError': 'Unsupported OS', + }} + answer_extensions = D7_MODE or ask('Install Extensions?') + answer_vcr = D7_MODE or ask('Install Visual C++ Runtimes?') + if D7_MODE: + answer_ninite = False + answer_mse = False + else: + answer_ninite = ask('Install Ninite Bundle?') + if answer_ninite and global_vars['OS']['Version'] in ['7']: + # Vista is dead, not going to check for it + answer_mse = ask('Install MSE?') - print_info('Installing Programs') - if answer_vcr: - install_vcredists() - if answer_ninite: - try_and_print(message='Ninite bundle...', - function=install_ninite_bundle, cs='Started', - mse=answer_mse, other_results=other_results) - if answer_extensions: - wait_for_process('ninite.exe') - print_info('Installing Extensions') - try_and_print(message='Classic Shell skin...', - function=install_classicstart_skin, - other_results=other_results) - try_and_print(message='Google Chrome extensions...', - function=install_chrome_extensions) - try_and_print(message='Mozilla Firefox extensions...', - function=install_firefox_extensions, - other_results=other_results) - print_standard('\nDone.') - exit_script() - except SystemExit: - pass - except: - major_exception() + print_info('Installing Programs') + if answer_vcr: + install_vcredists() + if answer_ninite: + result = try_and_print(message='Ninite bundle...', + function=install_ninite_bundle, cs='Started', + mse=answer_mse, other_results=other_results) + for proc in result['Out']: + # Wait for all processes to finish + proc.wait() + if answer_extensions: + print_info('Installing Extensions') + try_and_print(message='Classic Shell skin...', + function=install_classicstart_skin, + other_results=other_results) + try_and_print(message='Google Chrome extensions...', + function=install_chrome_extensions) + try_and_print(message='Mozilla Firefox extensions...', + function=install_firefox_extensions, + other_results=other_results) + print_standard('\nDone.') + exit_script() + except SystemExit: + pass + except: + major_exception() -# vim: sts=4 sw=4 ts=4 +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/install_vcredists.py b/.bin/Scripts/install_vcredists.py index fd953551..a22cc729 100644 --- a/.bin/Scripts/install_vcredists.py +++ b/.bin/Scripts/install_vcredists.py @@ -4,31 +4,32 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.setup import * init_global_vars() os.system('title {}: Install Visual C++ Runtimes'.format(KIT_NAME_FULL)) set_log_file('Install Visual C++ Runtimes.log') if __name__ == '__main__': - try: - stay_awake() - clear_screen() - print_info('{}: Install Visual C++ Runtimes\n'.format(KIT_NAME_FULL)) - other_results = { - 'Error': { - 'CalledProcessError': 'Unknown Error', - }} + try: + stay_awake() + clear_screen() + print_info('{}: Install Visual C++ Runtimes\n'.format(KIT_NAME_FULL)) + other_results = { + 'Error': { + 'CalledProcessError': 'Unknown Error', + }} - if ask('Install Visual C++ Runtimes?'): - install_vcredists() - else: - abort() + if ask('Install Visual C++ Runtimes?'): + install_vcredists() + else: + abort() - print_standard('\nDone.') - exit_script() - except SystemExit: - pass - except: - major_exception() + print_standard('\nDone.') + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/mount-all-volumes b/.bin/Scripts/mount-all-volumes index 9e5de0ea..f49439f2 100755 --- a/.bin/Scripts/mount-all-volumes +++ b/.bin/Scripts/mount-all-volumes @@ -6,33 +6,33 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.data import * init_global_vars() if __name__ == '__main__': - try: - # Prep - clear_screen() - print_standard('{}: Volume mount tool'.format(KIT_NAME_FULL)) + try: + # Prep + clear_screen() + print_standard('{}: Volume mount tool'.format(KIT_NAME_FULL)) - # Mount volumes - report = mount_volumes(all_devices=True) + # Mount volumes + report = mount_volumes(all_devices=True) - # Print report - print_info('\nResults') - for vol_name, vol_data in sorted(report.items()): - show_data(indent=4, width=20, **vol_data['show_data']) + # Print report + print_info('\nResults') + for vol_name, vol_data in sorted(report.items()): + show_data(indent=4, width=20, **vol_data['show_data']) - # Done - print_standard('\nDone.') - if 'gui' in sys.argv: - pause("Press Enter to exit...") - popen_program(['nohup', 'thunar', '/media'], pipe=True) - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + print_standard('\nDone.') + if 'gui' in sys.argv: + pause("Press Enter to exit...") + popen_program(['nohup', 'thunar', '/media'], pipe=True) + exit_script() + except SystemExit: + pass + except: + major_exception() +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/mount-backup-shares b/.bin/Scripts/mount-backup-shares index 9f3612b6..57fbe572 100755 --- a/.bin/Scripts/mount-backup-shares +++ b/.bin/Scripts/mount-backup-shares @@ -6,33 +6,33 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.data import * from functions.network import * init_global_vars() if __name__ == '__main__': - try: - # Prep - clear_screen() + try: + # Prep + clear_screen() - # Connect - connect_to_network() + # Connect + connect_to_network() - # Mount - if is_connected(): - mount_backup_shares(read_write=True) - else: - # Couldn't connect - print_error('ERROR: No network connectivity.') - - # Done - print_standard('\nDone.') - #pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() + # Mount + if is_connected(): + mount_backup_shares(read_write=True) + else: + # Couldn't connect + print_error('ERROR: No network connectivity.') + # Done + print_standard('\nDone.') + #pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/msword-search b/.bin/Scripts/msword-search index 3e2c175c..b005c3c1 100755 --- a/.bin/Scripts/msword-search +++ b/.bin/Scripts/msword-search @@ -9,73 +9,76 @@ import sys # STATIC VARIABLES SCANDIR = os.getcwd() USAGE = '''Usage: {script} ... - e.g. {script} "Book Title" "Keyword" "etc" + e.g. {script} "Book Title" "Keyword" "etc" - This script will search all doc/docx files below the current directory for - the search-terms provided (case-insensitive).'''.format(script=__file__) + This script will search all doc/docx files below the current directory for + the search-terms provided (case-insensitive).'''.format(script=__file__) # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.network import * init_global_vars() REGEX_DOC_FILES = re.compile(r'\.docx?$', re.IGNORECASE) + def scan_for_docs(path): - for entry in os.scandir(path): - if entry.is_dir(follow_symlinks=False): - yield from scantree(entry.path) - elif entry.is_file and REGEX_DOC_FILES.search(entry.name): - yield entry + for entry in os.scandir(path): + if entry.is_dir(follow_symlinks=False): + yield from scan_for_docs(entry.path) + elif entry.is_file and REGEX_DOC_FILES.search(entry.name): + yield entry + def scan_file(file_path, search): - match = False - try: - if entry.name.lower().endswith('.docx'): - result = run_program(['unzip', '-p', entry.path]) - else: - # Assuming .doc - result = run_program(['antiword', entry.path]) - out = result.stdout.decode() - match = re.search(search, out, re.IGNORECASE) - except Exception: - # Ignore errors since files may be corrupted - pass - - return entry.path if match else None + match = False + try: + if entry.name.lower().endswith('.docx'): + result = run_program(['unzip', '-p', entry.path]) + else: + # Assuming .doc + result = run_program(['antiword', entry.path]) + out = result.stdout.decode() + match = re.search(search, out, re.IGNORECASE) + except Exception: + # Ignore errors since files may be corrupted + pass + + return entry.path if match else None + if __name__ == '__main__': - try: - # Prep - clear_screen() - terms = [re.sub(r'\s+', r'\s*', t) for t in sys.argv[1:]] - search = '({})'.format('|'.join(terms)) + try: + # Prep + clear_screen() + terms = [re.sub(r'\s+', r'\s*', t) for t in sys.argv[1:]] + search = '({})'.format('|'.join(terms)) - if len(sys.argv) == 1: - # Print usage - print_standard(USAGE) - else: - matches = [] - for entry in scan_for_docs(SCANDIR): - matches.append(scan_file(entry.path, search)) - # Strip None values (i.e. non-matching entries) - matches = [m for m in matches if m] - if matches: - print_success('Found {} {}:'.format( - len(matches), - 'Matches' if len(matches) > 1 else 'Match')) - for match in matches: - print_standard(match) - else: - print_error('No matches found.') - - # Done - print_standard('\nDone.') - #pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() + if len(sys.argv) == 1: + # Print usage + print_standard(USAGE) + else: + matches = [] + for entry in scan_for_docs(SCANDIR): + matches.append(scan_file(entry.path, search)) + # Strip None values (i.e. non-matching entries) + matches = [m for m in matches if m] + if matches: + print_success('Found {} {}:'.format( + len(matches), + 'Matches' if len(matches) > 1 else 'Match')) + for match in matches: + print_standard(match) + else: + print_error('No matches found.') + # Done + print_standard('\nDone.') + #pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/new_system_setup.py b/.bin/Scripts/new_system_setup.py new file mode 100644 index 00000000..5320403d --- /dev/null +++ b/.bin/Scripts/new_system_setup.py @@ -0,0 +1,180 @@ +# Wizard Kit: New system setup + +import os +import sys + +# Init +os.chdir(os.path.dirname(os.path.realpath(__file__))) +sys.path.append(os.getcwd()) +from functions.activation import * +from functions.browsers import * +from functions.cleanup import * +from functions.info import * +from functions.product_keys import * +from functions.setup import * +from functions.sw_diags import * +init_global_vars() +os.system('title {}: New System Setup'.format(KIT_NAME_FULL)) +set_log_file('New System Setup.log') + +if __name__ == '__main__': + other_results = { + 'Error': { + 'BIOSKeyNotFoundError': 'BIOS key not found', + 'CalledProcessError': 'Unknown Error', + 'FileNotFoundError': 'File not found', + 'GenericError': 'Unknown Error', + 'SecureBootDisabledError': 'Disabled', + }, + 'Warning': { + 'GenericRepair': 'Repaired', + 'NoProfilesError': 'No profiles found', + 'NotInstalledError': 'Not installed', + 'OSInstalledLegacyError': 'OS installed Legacy', + 'SecureBootNotAvailError': 'Not available', + 'SecureBootUnknownError': 'Unknown', + 'UnsupportedOSError': 'Unsupported OS', + }} + try: + stay_awake() + clear_screen() + + # Check installed OS + if os_is_unsupported(show_alert=True): + print_warning('OS version not supported by this script') + if not ask('Continue anyway? (NOT RECOMMENDED)'): + abort() + + # Select AV software + # NOTE: Truth tuple is (ESET, ESET_PUPS, MSE) + av_options = [ + {'Name': 'ESET NOD32', + 'Truths': (True, True, False),}, + {'Name': 'ESET NOD32 (no PUP/PUW scans)', + 'Truths': (True, False, False),}, + {'Name': 'Microsoft Security Essentials', + 'Disabled': global_vars['OS']['Version'] not in ['7'], + 'Truths': (False, False, True),}, + ] + actions = [ + {'Name': 'None', 'Letter': 'N'}, + {'Name': 'Quit', 'Letter': 'Q'}, + ] + selection = menu_select( + 'Please select an option to install', + main_entries=av_options, + action_entries=actions) + if selection.isnumeric(): + index = int(selection) - 1 + answer_eset, answer_pups, answer_mse = av_options[index]['Truths'] + elif selection == 'Q': + abort() + else: + answer_eset = False + answer_pups = False + answer_mse = False + + # Install LibreOffice? + answer_libreoffice = ask('Install LibreOffice?') + + # Install software + print_info('Installing Programs') + install_vcredists() + if answer_eset: + install_eset_nod32_av(scan_pups=answer_pups) + result = try_and_print( + message='Ninite bundle...', + function=install_ninite_bundle, cs='Started', + mse=answer_mse, libreoffice=answer_libreoffice, + other_results=other_results) + for proc in result['Out']: + # Wait for all processes to finish + proc.wait() + + # Scan for supported browsers + print_info('Scanning for browsers') + scan_for_browsers() + + # Install extensions + print_info('Installing Extensions') + try_and_print(message='Classic Shell skin...', + function=install_classicstart_skin, + other_results=other_results) + try_and_print(message='Google Chrome extensions...', + function=install_chrome_extensions) + try_and_print(message='Mozilla Firefox extensions...', + function=install_firefox_extensions, + other_results=other_results) + + # Configure software + print_info('Configuring programs') + install_adblock() + if global_vars['OS']['Version'] == '10': + try_and_print(message='ClassicStart...', + function=config_classicstart, cs='Done') + try_and_print(message='Explorer...', + function=config_explorer_user, cs='Done') + + # Configure system + print_info('Configuring system') + if global_vars['OS']['Version'] == '10': + try_and_print(message='Explorer...', + function=config_explorer_system, cs='Done') + try_and_print(message='Disabling telemetry...', + function=disable_windows_telemetry, cs='Done') + try_and_print(message='Enabling RegBack...', + function=enable_regback, cs='Done') + try_and_print(message='Enabling System Restore...', + function=enable_system_restore, cs='Done') + try_and_print(message='Updating Clock...', + function=update_clock, cs='Done') + + # Summary + print_info('Summary') + try_and_print(message='Operating System:', + function=show_os_name, ns='Unknown', silent_function=False) + try_and_print(message='Activation:', + function=show_os_activation, ns='Unknown', silent_function=False) + if (not windows_is_activated() + and global_vars['OS']['Version'] in ('8', '8.1', '10')): + try_and_print(message='BIOS Activation:', + function=activate_with_bios, + other_results=other_results) + try_and_print(message='Secure Boot Status:', + function=check_secure_boot_status, other_results=other_results) + try_and_print(message='Installed RAM:', + function=show_installed_ram, ns='Unknown', silent_function=False) + show_free_space() + try_and_print(message='Installed Antivirus:', + function=get_installed_antivirus, ns='Unknown', + other_results=other_results, print_return=True) + + # Play audio, show devices, open Windows updates, and open Activation + try_and_print(message='Opening Device Manager...', + function=open_device_manager, cs='Started') + try_and_print(message='Opening HWiNFO (Sensors)...', + function=run_hwinfo_sensors, cs='Started', other_results=other_results) + try_and_print(message='Opening Windows Updates...', + function=open_windows_updates, cs='Started') + if not windows_is_activated(): + try_and_print(message='Opening Windows Activation...', + function=open_windows_activation, cs='Started') + sleep(3) + try_and_print(message='Running XMPlay...', + function=run_xmplay, cs='Started', other_results=other_results) + try: + check_secure_boot_status(show_alert=True) + except: + # Only trying to open alert message boxes + pass + + # Done + print_standard('\nDone.') + pause('Press Enter to exit...') + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/safemode_enter.py b/.bin/Scripts/safemode_enter.py index cce7e28a..bc6d659d 100644 --- a/.bin/Scripts/safemode_enter.py +++ b/.bin/Scripts/safemode_enter.py @@ -4,35 +4,36 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.safemode import * init_global_vars() os.system('title {}: SafeMode Tool'.format(KIT_NAME_FULL)) if __name__ == '__main__': - try: - clear_screen() - print_info('{}: SafeMode Tool\n'.format(KIT_NAME_FULL)) - other_results = { - 'Error': {'CalledProcessError': 'Unknown Error'}, - 'Warning': {}} + try: + clear_screen() + print_info('{}: SafeMode Tool\n'.format(KIT_NAME_FULL)) + other_results = { + 'Error': {'CalledProcessError': 'Unknown Error'}, + 'Warning': {}} - if not ask('Enable booting to SafeMode (with Networking)?'): - abort() + if not ask('Enable booting to SafeMode (with Networking)?'): + abort() - # Configure SafeMode - try_and_print(message='Set BCD option...', - function=enable_safemode, other_results=other_results) - try_and_print(message='Enable MSI in SafeMode...', - function=enable_safemode_msi, other_results=other_results) + # Configure SafeMode + try_and_print(message='Set BCD option...', + function=enable_safemode, other_results=other_results) + try_and_print(message='Enable MSI in SafeMode...', + function=enable_safemode_msi, other_results=other_results) - # Done - print_standard('\nDone.') - pause('Press Enter to reboot...') - reboot() - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + print_standard('\nDone.') + pause('Press Enter to reboot...') + reboot() + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/safemode_exit.py b/.bin/Scripts/safemode_exit.py index af66222e..bbbdbcf8 100644 --- a/.bin/Scripts/safemode_exit.py +++ b/.bin/Scripts/safemode_exit.py @@ -4,35 +4,36 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.safemode import * init_global_vars() os.system('title {}: SafeMode Tool'.format(KIT_NAME_FULL)) if __name__ == '__main__': - try: - clear_screen() - print_info('{}: SafeMode Tool\n'.format(KIT_NAME_FULL)) - other_results = { - 'Error': {'CalledProcessError': 'Unknown Error'}, - 'Warning': {}} + try: + clear_screen() + print_info('{}: SafeMode Tool\n'.format(KIT_NAME_FULL)) + other_results = { + 'Error': {'CalledProcessError': 'Unknown Error'}, + 'Warning': {}} - if not ask('Disable booting to SafeMode?'): - abort() + if not ask('Disable booting to SafeMode?'): + abort() - # Configure SafeMode - try_and_print(message='Remove BCD option...', - function=disable_safemode, other_results=other_results) - try_and_print(message='Disable MSI in SafeMode...', - function=disable_safemode_msi, other_results=other_results) + # Configure SafeMode + try_and_print(message='Remove BCD option...', + function=disable_safemode, other_results=other_results) + try_and_print(message='Disable MSI in SafeMode...', + function=disable_safemode_msi, other_results=other_results) - # Done - print_standard('\nDone.') - pause('Press Enter to reboot...') - reboot() - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + print_standard('\nDone.') + pause('Press Enter to reboot...') + reboot() + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/settings/launchers.py b/.bin/Scripts/settings/launchers.py index ef0f6c16..5272b64d 100644 --- a/.bin/Scripts/settings/launchers.py +++ b/.bin/Scripts/settings/launchers.py @@ -1,690 +1,743 @@ # Wizard Kit: Settings - Launchers LAUNCHERS = { - r'(Root)': { - 'Activate Windows': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'activate.py', - 'L_ELEV': 'True', - }, - 'd7II': { - 'L_TYPE': 'Executable', - 'L_PATH': 'd7II', - 'L_ITEM': 'd7II.exe', - }, - 'Post-d7II Work': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'post_d7.py', - 'L_ELEV': 'True', - }, - 'System Checklist': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'system_checklist.py', - 'L_ELEV': 'True', - }, - 'System Checklist (HW)': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'system_checklist_hw.py', - 'L_ELEV': 'True', - }, - 'User Checklist': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'user_checklist.py', - }, - }, - r'.bin\Scripts\launchers_for_d7': { - 'Browser Reset': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'reset_browsers.py', - 'L_ARGS': 'd7mode', - }, - 'Install SW Bundle': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'install_sw_bundle.py', - 'L_ARGS': 'd7mode', - 'L_ELEV': 'True', - }, - 'System Checklist': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'system_checklist.py', - 'L_ARGS': 'd7mode', - 'L_ELEV': 'True', - }, - 'System Diagnostics': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'system_diagnostics.py', - 'L_ARGS': 'd7mode', - 'L_ELEV': 'True', - }, - 'User Checklist': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'user_checklist.py', - 'L_ARGS': 'd7mode', - }, - }, - r'Data Recovery': { - 'PhotoRec (CLI)': { - 'L_TYPE': 'Executable', - 'L_PATH': 'TestDisk', - 'L_ITEM': 'photorec_win.exe', - 'L_ELEV': 'True', - 'L__CLI': 'True', - }, - 'PhotoRec': { - 'L_TYPE': 'Executable', - 'L_PATH': 'TestDisk', - 'L_ITEM': 'qphotorec_win.exe', - 'L_ELEV': 'True', - }, - 'TestDisk': { - 'L_TYPE': 'Executable', - 'L_PATH': 'TestDisk', - 'L_ITEM': 'testdisk_win.exe', - 'L_ELEV': 'True', - 'L__CLI': 'True', - }, - }, - r'Data Transfers': { - "Fab's Autobackup Pro": { - 'L_TYPE': 'Executable', - 'L_PATH': 'AutoBackupPro', - 'L_ITEM': 'autobackup6pro.exe', - }, - 'FastCopy (as ADMIN)': { - 'L_TYPE': 'Executable', - 'L_PATH': 'FastCopy', - 'L_ITEM': 'FastCopy.exe', - 'L_ARGS': ( - r' /logfile=%log_dir%\Tools\FastCopy.log' - r' /cmd=noexist_only' - r' /utf8' - r' /skip_empty_dir' - r' /linkdest' - r' /exclude=' - r'$RECYCLE.BIN;' - r'$Recycle.Bin;' - r'.AppleDB;' - r'.AppleDesktop;' - r'.AppleDouble;' - r'.com.apple.timemachine.supported;' - r'.dbfseventsd;' - r'.DocumentRevisions-V100*;' - r'.DS_Store;' - r'.fseventsd;' - r'.PKInstallSandboxManager;' - r'.Spotlight*;' - r'.SymAV*;' - r'.symSchedScanLockxz;' - r'.TemporaryItems;' - r'.Trash*;' - r'.vol;' - r'.VolumeIcon.icns;' - r'desktop.ini;' - r'Desktop?DB;' - r'Desktop?DF;' - r'hiberfil.sys;' - r'lost+found;' - r'Network?Trash?Folder;' - r'pagefile.sys;' - r'Recycled;' - r'RECYCLER;' - r'System?Volume?Information;' - r'Temporary?Items;' - r'Thumbs.db' - r' /to=%client_dir%\Transfer_%iso_date%\ ' - ), - 'L_ELEV': 'True', - 'Extra Code': [ - r'call "%bin%\Scripts\init_client_dir.cmd" /Logs /Transfer', - ], - }, - 'FastCopy': { - 'L_TYPE': 'Executable', - 'L_PATH': 'FastCopy', - 'L_ITEM': 'FastCopy.exe', - 'L_ARGS': ( - r' /logfile=%log_dir%\Tools\FastCopy.log' - r' /cmd=noexist_only' - r' /utf8' - r' /skip_empty_dir' - r' /linkdest' - r' /exclude=' - r'$RECYCLE.BIN;' - r'$Recycle.Bin;' - r'.AppleDB;' - r'.AppleDesktop;' - r'.AppleDouble;' - r'.com.apple.timemachine.supported;' - r'.dbfseventsd;' - r'.DocumentRevisions-V100*;' - r'.DS_Store;' - r'.fseventsd;' - r'.PKInstallSandboxManager;' - r'.Spotlight*;' - r'.SymAV*;' - r'.symSchedScanLockxz;' - r'.TemporaryItems;' - r'.Trash*;' - r'.vol;' - r'.VolumeIcon.icns;' - r'desktop.ini;' - r'Desktop?DB;' - r'Desktop?DF;' - r'hiberfil.sys;' - r'lost+found;' - r'Network?Trash?Folder;' - r'pagefile.sys;' - r'Recycled;' - r'RECYCLER;' - r'System?Volume?Information;' - r'Temporary?Items;' - r'Thumbs.db' - r' /to=%client_dir%\Transfer_%iso_date%\ ' - ), - 'Extra Code': [ - r'call "%bin%\Scripts\init_client_dir.cmd" /Logs /Transfer', - ], - }, - 'KVRT': { - 'L_TYPE': 'Executable', - 'L_PATH': 'KVRT', - 'L_ITEM': 'KVRT.exe', - 'L_ARGS': ( - r' -accepteula' - r' -d %q_dir%' - r' -processlevel 3' - r' -dontcryptsupportinfo' - r' -fixednames' - ), - 'Extra Code': [ - r'call "%bin%\Scripts\init_client_dir.cmd" /Quarantine', - r'set "q_dir=%client_dir%\Quarantine\KVRT"', - r'mkdir "%q_dir%">nul 2>&1', - ], - }, - 'Mac & Linux Reader': { - 'L_TYPE': 'Executable', - 'L_PATH': 'LinuxReader', - 'L_ITEM': 'LinuxReader.exe', - 'L_ELEV': 'True', - }, - 'Transferred Keys': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'transferred_keys.py', - 'L_ELEV': 'True', - }, - 'User Data Transfer': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'user_data_transfer.py', - 'L_ELEV': 'True', - }, - 'XYplorer (as ADMIN)': { - 'L_TYPE': 'Executable', - 'L_PATH': 'XYplorerFree', - 'L_ITEM': 'XYplorerFree.exe', - 'L_ARGS': r'/exp /win=max %userprofile%', - 'L_ELEV': 'True', - }, - 'XYplorer': { - 'L_TYPE': 'Executable', - 'L_PATH': 'XYplorerFree', - 'L_ITEM': 'XYplorerFree.exe', - 'L_ARGS': r'/exp /win=max %userprofile%', - }, - }, - r'Diagnostics': { - 'AIDA64': { - 'L_TYPE': 'Executable', - 'L_PATH': 'AIDA64', - 'L_ITEM': 'aida64.exe', - }, - 'ProduKey': { - 'L_TYPE': 'Executable', - 'L_PATH': 'ProduKey', - 'L_ITEM': 'ProduKey.exe', - 'L_ELEV': 'True', - 'Extra Code': [ - r'if exist "%bin%\ProduKey" (', - r' del "%bin%\ProduKey\ProduKey.cfg" 2>nul', - r' del "%bin%\ProduKey\ProduKey64.cfg" 2>nul', - r')', - ], - }, - 'System Diagnostics': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'system_diagnostics.py', - 'L_ELEV': 'True', - }, - }, - r'Diagnostics\Extras': { - 'Autoruns (with VirusTotal Scan)': { - 'L_TYPE': 'Executable', - 'L_PATH': 'Autoruns', - 'L_ITEM': 'Autoruns.exe', - 'L_ARGS': '-e', - 'Extra Code': [ - r'reg add HKCU\Software\Sysinternals\AutoRuns /v checkvirustotal /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v EulaAccepted /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v shownomicrosoft /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v shownowindows /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v showonlyvirustotal /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v submitvirustotal /t REG_DWORD /d 0 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v verifysignatures /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns\SigCheck /v EulaAccepted /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns\Streams /v EulaAccepted /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns\VirusTotal /v VirusTotalTermsAccepted /t REG_DWORD /d 1 /f >nul', - ], - }, - 'BleachBit': { - 'L_TYPE': 'Executable', - 'L_PATH': 'BleachBit', - 'L_ITEM': 'bleachbit.exe', - }, - 'BlueScreenView': { - 'L_TYPE': 'Executable', - 'L_PATH': 'BlueScreenView', - 'L_ITEM': 'BlueScreenView.exe', - }, - 'ERUNT': { - 'L_TYPE': 'Executable', - 'L_PATH': 'erunt', - 'L_ITEM': 'ERUNT.EXE', - 'L_ARGS': '%client_dir%\Backups\Registry\%iso_date% sysreg curuser otherusers', - 'L_ELEV': 'True', - 'Extra Code': [ - r'call "%bin%\Scripts\init_client_dir.cmd" /Logs', - ], - }, - 'FurMark': { - 'L_TYPE': 'Executable', - 'L_PATH': 'FurMark', - 'L_ITEM': 'FurMark.exe', - }, - 'HDTune Pro': { - 'L_TYPE': 'Executable', - 'L_PATH': 'HDTunePro', - 'L_ITEM': 'HDTunePro.exe', - }, - 'HitmanPro': { - 'L_TYPE': 'Executable', - 'L_PATH': 'HitmanPro', - 'L_ITEM': 'HitmanPro.exe', - 'Extra Code': [ - r'call "%bin%\Scripts\init_client_dir.cmd" /Logs', - ], - }, - 'HWiNFO': { - 'L_TYPE': 'Executable', - 'L_PATH': 'HWiNFO', - 'L_ITEM': 'HWiNFO.exe', - 'Extra Code': [ - r'for %%a in (32 64) do (', - r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"', - r' (echo SensorsOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', - r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', - r')', - ], - }, - 'HWiNFO (Sensors)': { - 'L_TYPE': 'Executable', - 'L_PATH': 'HWiNFO', - 'L_ITEM': 'HWiNFO.exe', - 'Extra Code': [ - r'for %%a in (32 64) do (', - r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"', - r' (echo SensorsOnly=1)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', - r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', - r')', - ], - }, - }, - r'Drivers': { - 'Intel RST (Current Release)': { - 'L_TYPE': 'Executable', - 'L_PATH': '_Drivers\Intel RST', - 'L_ITEM': 'SetupRST_16.5.exe', - 'L_7ZIP': 'SetupRST_16.5.exe', - }, - 'Intel RST (Previous Releases)': { - 'L_TYPE': 'Folder', - 'L_PATH': '_Drivers\Intel RST', - 'L_ITEM': '.', - 'L_NCMD': 'True', - }, - 'Intel SSD Toolbox': { - 'L_TYPE': 'Executable', - 'L_PATH': r'_Drivers\Intel SSD Toolbox', - 'L_ITEM': 'Intel SSD Toolbox.exe', - }, - 'Samsing Magician': { - 'L_TYPE': 'Executable', - 'L_PATH': r'_Drivers\Samsung Magician', - 'L_ITEM': 'Samsung Magician.exe', - }, - 'Snappy Driver Installer Origin': { - 'L_TYPE': 'Executable', - 'L_PATH': '_Drivers\SDIO', - 'L_ITEM': 'SDIO.exe', - }, - }, - r'Drivers\Extras': { - 'Acer': { - 'L_TYPE': 'Executable', - 'L_PATH': 'HWiNFO', - 'L_ITEM': 'HWiNFO.exe', - 'Extra Code': [ - r'for %%a in (32 64) do (', - r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"', - r' (echo SensorsOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', - r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', - r')', - r'start "" "http://us.acer.com/ac/en/US/content/drivers"', - ], - }, - 'Lenovo': { - 'L_TYPE': 'Executable', - 'L_PATH': 'HWiNFO', - 'L_ITEM': 'HWiNFO.exe', - 'Extra Code': [ - r'for %%a in (32 64) do (', - r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"', - r' (echo SensorsOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', - r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', - r')', - r'start "" "https://pcsupport.lenovo.com/us/en/"', - ], - }, - 'Toshiba': { - 'L_TYPE': 'Executable', - 'L_PATH': 'HWiNFO', - 'L_ITEM': 'HWiNFO.exe', - 'Extra Code': [ - r'for %%a in (32 64) do (', - r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"', - r' (echo SensorsOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', - r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', - r')', - r'start "" "http://support.toshiba.com/drivers"', - ], - }, - }, - r'Installers': { - 'ESET NOD32 AV': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'install_eset_nod32_av.py', - 'L_ELEV': 'True', - }, - 'SW Bundle': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'install_sw_bundle.py', - 'L_ELEV': 'True', - }, - }, - r'Installers\Extras\Office\2016': { - 'Home and Business 2016 (x32)': { - 'L_TYPE': 'Office', - 'L_PATH': '2016', - 'L_ITEM': 'hb_32.xml', - 'L_NCMD': 'True', - }, - 'Home and Business 2016 (x64)': { - 'L_TYPE': 'Office', - 'L_PATH': '2016', - 'L_ITEM': 'hb_64.xml', - 'L_NCMD': 'True', - }, - 'Home and Student 2016 (x32)': { - 'L_TYPE': 'Office', - 'L_PATH': '2016', - 'L_ITEM': 'hs_32.xml', - 'L_NCMD': 'True', - }, - 'Home and Student 2016 (x64)': { - 'L_TYPE': 'Office', - 'L_PATH': '2016', - 'L_ITEM': 'hs_64.xml', - 'L_NCMD': 'True', - }, - 'Office 365 2016 (x32)': { - 'L_TYPE': 'Office', - 'L_PATH': '2016', - 'L_ITEM': '365_32.xml', - 'L_NCMD': 'True', - }, - 'Office 365 2016 (x64)': { - 'L_TYPE': 'Office', - 'L_PATH': '2016', - 'L_ITEM': '365_64.xml', - 'L_NCMD': 'True', - }, - }, - r'Installers\Extras\Runtimes': { - 'Visual C++ Runtimes': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'install_vcredists.py', - 'L_ELEV': 'True', - }, - }, - r'Misc': { - 'Cleanup CBS Temp Files': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'cbs_fix.py', - 'L_ELEV': 'True', - }, - 'ConEmu (as ADMIN)': { - 'L_TYPE': 'Executable', - 'L_PATH': 'ConEmu', - 'L_ITEM': 'ConEmu.exe', - 'L_ELEV': 'True', - }, - 'ConEmu': { - 'L_TYPE': 'Executable', - 'L_PATH': 'ConEmu', - 'L_ITEM': 'ConEmu.exe', - }, - 'Enter SafeMode': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'safemode_enter.py', - 'L_ELEV': 'True', - }, - 'Everything': { - 'L_TYPE': 'Executable', - 'L_PATH': 'Everything', - 'L_ITEM': 'Everything.exe', - 'L_ARGS': '-nodb', - 'L_ELEV': 'True', - }, - 'Exit SafeMode': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'safemode_exit.py', - 'L_ELEV': 'True', - }, - 'Network Stability Test': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'network_stability_test.py', - }, - 'Notepad++': { - 'L_TYPE': 'Executable', - 'L_PATH': 'notepadplusplus', - 'L_ITEM': 'notepadplusplus.exe', - }, - 'PuTTY': { - 'L_TYPE': 'Executable', - 'L_PATH': 'PuTTY', - 'L_ITEM': 'PUTTY.EXE', - }, - 'ShutUp10': { - 'L_TYPE': 'Executable', - 'L_PATH': 'ShutUp10', - 'L_ITEM': 'OOSU10.exe', - }, - 'ShutUp10 (1201 Minimal Selection)': { - 'L_TYPE': 'Executable', - 'L_PATH': 'ShutUp10', - 'L_ITEM': 'OOSU10.exe', - 'L_ARGS': '1201.cfg', - }, - 'WizTree': { - 'L_TYPE': 'Executable', - 'L_PATH': 'WizTree', - 'L_ITEM': 'WizTree.exe', - 'L_ELEV': 'True', - }, - 'XMPlay': { - 'L_TYPE': 'Executable', - 'L_PATH': 'XMPlay', - 'L_ITEM': 'xmplay.exe', - 'L_ARGS': '"%bin%\XMPlay\music.7z"', - }, - }, - r'Repairs': { - 'AdwCleaner': { - 'L_TYPE': 'Executable', - 'L_PATH': 'AdwCleaner', - 'L_ITEM': 'AdwCleaner.exe', - }, - 'Autoruns': { - 'L_TYPE': 'Executable', - 'L_PATH': 'Autoruns', - 'L_ITEM': 'Autoruns.exe', - 'L_ARGS': '-e', - 'Extra Code': [ - r'reg add HKCU\Software\Sysinternals\AutoRuns /v checkvirustotal /t REG_DWORD /d 0 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v EulaAccepted /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v shownomicrosoft /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v shownowindows /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v showonlyvirustotal /t REG_DWORD /d 0 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v submitvirustotal /t REG_DWORD /d 0 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns /v verifysignatures /t REG_DWORD /d 0 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns\SigCheck /v EulaAccepted /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns\Streams /v EulaAccepted /t REG_DWORD /d 1 /f >nul', - r'reg add HKCU\Software\Sysinternals\AutoRuns\VirusTotal /v VirusTotalTermsAccepted /t REG_DWORD /d 1 /f >nul', - ], - }, - 'CHKDSK': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'check_disk.py', - 'L_ELEV': 'True', - }, - 'DISM': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'dism.py', - 'L_ELEV': 'True', - }, - 'ESET Online Scanner': { - 'L_TYPE': 'Executable', - 'L_PATH': 'ESET', - 'L_ITEM': 'ESET.exe', - }, - 'KVRT': { - 'L_TYPE': 'Executable', - 'L_PATH': 'KVRT', - 'L_ITEM': 'KVRT.exe', - 'L_ARGS': ( - r' -accepteula' - r' -d %q_dir%' - r' -processlevel 3' - r' -dontcryptsupportinfo' - r' -fixednames' - ), - 'Extra Code': [ - r'call "%bin%\Scripts\init_client_dir.cmd" /Quarantine', - r'set "q_dir=%client_dir%\Quarantine\KVRT"', - r'mkdir "%q_dir%">nul 2>&1', - ], - }, - 'RKill': { - 'L_TYPE': 'Executable', - 'L_PATH': 'RKill', - 'L_ITEM': 'RKill.exe', - 'L_ARGS': '-s -l %log_dir%\Tools\RKill.log', - 'L_ELEV': 'True', - 'Extra Code': [ - r'call "%bin%\Scripts\init_client_dir.cmd" /Logs', - ], - }, - 'SFC Scan': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'sfc_scan.py', - 'L_ELEV': 'True', - }, - 'TDSSKiller': { - 'L_TYPE': 'Executable', - 'L_PATH': 'TDSSKiller', - 'L_ITEM': 'TDSSKiller.exe', - 'L_ARGS': ( - r' -l %log_dir%\Tools\TDSSKiller.log' - r' -qpath %q_dir%' - r' -accepteula' - r' -accepteulaksn' - r' -dcexact' - r' -tdlfs' - ), - 'Extra Code': [ - r'call "%bin%\Scripts\init_client_dir.cmd" /Quarantine', - r'set "q_dir=%client_dir%\Quarantine\TDSSKiller"', - r'mkdir "%q_dir%">nul 2>&1', - ], - }, - 'WinAIO Repair': { - 'L_TYPE': 'Executable', - 'L_PATH': 'WinAIORepair', - 'L_ITEM': 'Repair_Windows.exe', - 'L_ELEV': 'True', - 'Extra Code': [ - r'copy /y "%bin%\WinAIORepair\__empty.ini" "%bin%\WinAIORepair\settings.ini"', - ], - }, - 'WinAIO Repair (Fix Associations)': { - 'L_TYPE': 'Executable', - 'L_PATH': 'WinAIORepair', - 'L_ITEM': 'Repair_Windows.exe', - 'L_ELEV': 'True', - 'Extra Code': [ - r'copy /y "%bin%\WinAIORepair\__associations.ini" "%bin%\WinAIORepair\settings.ini"', - ], - }, - 'WinAIO Repair (Fix Permissions)': { - 'L_TYPE': 'Executable', - 'L_PATH': 'WinAIORepair', - 'L_ITEM': 'Repair_Windows.exe', - 'L_ELEV': 'True', - 'Extra Code': [ - r'copy /y "%bin%\WinAIORepair\__permissions.ini" "%bin%\WinAIORepair\settings.ini"', - ], - }, - }, - r'Uninstallers': { - 'IObit Uninstaller': { - 'L_TYPE': 'Executable', - 'L_PATH': 'IObitUninstallerPortable', - 'L_ITEM': 'IObitUninstallerPortable.exe', - }, - }, - } + r'(Root)': { + 'Activate Windows': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'activate.py', + 'L_ELEV': 'True', + }, + 'd7II': { + 'L_TYPE': 'Executable', + 'L_PATH': 'd7II', + 'L_ITEM': 'd7II.exe', + }, + 'New System Setup': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'new_system_setup.py', + 'L_ELEV': 'True', + }, + 'Post-d7II Work': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'post_d7.py', + 'L_ELEV': 'True', + }, + 'System Checklist': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'system_checklist.py', + 'L_ELEV': 'True', + }, + 'System Checklist (HW)': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'system_checklist_hw.py', + 'L_ELEV': 'True', + }, + 'User Checklist': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'user_checklist.py', + }, + }, + r'.bin\Scripts\launchers_for_d7': { + 'Browser Reset': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'reset_browsers.py', + 'L_ARGS': 'd7mode', + }, + 'Install SW Bundle': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'install_sw_bundle.py', + 'L_ARGS': 'd7mode', + 'L_ELEV': 'True', + }, + 'System Checklist': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'system_checklist.py', + 'L_ARGS': 'd7mode', + 'L_ELEV': 'True', + }, + 'System Diagnostics': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'system_diagnostics.py', + 'L_ARGS': 'd7mode', + 'L_ELEV': 'True', + }, + 'User Checklist': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'user_checklist.py', + 'L_ARGS': 'd7mode', + }, + 'Disable Windows Updates': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'windows_updates.py', + 'L_ARGS': '--disable', + 'L_ELEV': 'True', + }, + 'Enable Windows Updates': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'windows_updates.py', + 'L_ARGS': '--enable', + 'L_ELEV': 'True', + }, + }, + r'Data Recovery': { + 'PhotoRec (CLI)': { + 'L_TYPE': 'Executable', + 'L_PATH': 'TestDisk', + 'L_ITEM': 'photorec_win.exe', + 'L_ELEV': 'True', + 'L__CLI': 'True', + }, + 'PhotoRec': { + 'L_TYPE': 'Executable', + 'L_PATH': 'TestDisk', + 'L_ITEM': 'qphotorec_win.exe', + 'L_ELEV': 'True', + }, + 'TestDisk': { + 'L_TYPE': 'Executable', + 'L_PATH': 'TestDisk', + 'L_ITEM': 'testdisk_win.exe', + 'L_ELEV': 'True', + 'L__CLI': 'True', + }, + }, + r'Data Transfers': { + "Fab's Autobackup Pro": { + 'L_TYPE': 'Executable', + 'L_PATH': 'AutoBackupPro', + 'L_ITEM': 'autobackup6pro.exe', + }, + 'FastCopy (as ADMIN)': { + 'L_TYPE': 'Executable', + 'L_PATH': 'FastCopy', + 'L_ITEM': 'FastCopy.exe', + 'L_ARGS': ( + r' /logfile=%log_dir%\Tools\FastCopy.log' + r' /cmd=noexist_only' + r' /utf8' + r' /skip_empty_dir' + r' /linkdest' + r' /exclude=' + r'$RECYCLE.BIN;' + r'$Recycle.Bin;' + r'.AppleDB;' + r'.AppleDesktop;' + r'.AppleDouble;' + r'.com.apple.timemachine.supported;' + r'.dbfseventsd;' + r'.DocumentRevisions-V100*;' + r'.DS_Store;' + r'.fseventsd;' + r'.PKInstallSandboxManager;' + r'.Spotlight*;' + r'.SymAV*;' + r'.symSchedScanLockxz;' + r'.TemporaryItems;' + r'.Trash*;' + r'.vol;' + r'.VolumeIcon.icns;' + r'desktop.ini;' + r'Desktop?DB;' + r'Desktop?DF;' + r'hiberfil.sys;' + r'lost+found;' + r'Network?Trash?Folder;' + r'pagefile.sys;' + r'Recycled;' + r'RECYCLER;' + r'System?Volume?Information;' + r'Temporary?Items;' + r'Thumbs.db' + r' /to=%client_dir%\Transfer_%iso_date%\ ' + ), + 'L_ELEV': 'True', + 'Extra Code': [ + r'call "%bin%\Scripts\init_client_dir.cmd" /Logs /Transfer', + ], + }, + 'FastCopy': { + 'L_TYPE': 'Executable', + 'L_PATH': 'FastCopy', + 'L_ITEM': 'FastCopy.exe', + 'L_ARGS': ( + r' /logfile=%log_dir%\Tools\FastCopy.log' + r' /cmd=noexist_only' + r' /utf8' + r' /skip_empty_dir' + r' /linkdest' + r' /exclude=' + r'$RECYCLE.BIN;' + r'$Recycle.Bin;' + r'.AppleDB;' + r'.AppleDesktop;' + r'.AppleDouble;' + r'.com.apple.timemachine.supported;' + r'.dbfseventsd;' + r'.DocumentRevisions-V100*;' + r'.DS_Store;' + r'.fseventsd;' + r'.PKInstallSandboxManager;' + r'.Spotlight*;' + r'.SymAV*;' + r'.symSchedScanLockxz;' + r'.TemporaryItems;' + r'.Trash*;' + r'.vol;' + r'.VolumeIcon.icns;' + r'desktop.ini;' + r'Desktop?DB;' + r'Desktop?DF;' + r'hiberfil.sys;' + r'lost+found;' + r'Network?Trash?Folder;' + r'pagefile.sys;' + r'Recycled;' + r'RECYCLER;' + r'System?Volume?Information;' + r'Temporary?Items;' + r'Thumbs.db' + r' /to=%client_dir%\Transfer_%iso_date%\ ' + ), + 'Extra Code': [ + r'call "%bin%\Scripts\init_client_dir.cmd" /Logs /Transfer', + ], + }, + 'Find User Wallpapers': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'find_user_wallpapers.py', + }, + 'KVRT': { + 'L_TYPE': 'Executable', + 'L_PATH': 'KVRT', + 'L_ITEM': 'KVRT.exe', + 'L_ARGS': ( + r' -accepteula' + r' -d %q_dir%' + r' -processlevel 3' + r' -dontcryptsupportinfo' + r' -fixednames' + ), + 'Extra Code': [ + r'call "%bin%\Scripts\init_client_dir.cmd" /Quarantine', + r'set "q_dir=%client_dir%\Quarantine\KVRT"', + r'mkdir "%q_dir%">nul 2>&1', + ], + }, + 'Mac & Linux Reader': { + 'L_TYPE': 'Executable', + 'L_PATH': 'LinuxReader', + 'L_ITEM': 'LinuxReader.exe', + 'L_ELEV': 'True', + }, + 'Transferred Keys': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'transferred_keys.py', + 'L_ELEV': 'True', + }, + 'User Data Transfer': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'user_data_transfer.py', + 'L_ELEV': 'True', + }, + 'XYplorer (as ADMIN)': { + 'L_TYPE': 'Executable', + 'L_PATH': 'XYplorerFree', + 'L_ITEM': 'XYplorerFree.exe', + 'L_ARGS': r'/exp /win=max %userprofile%', + 'L_ELEV': 'True', + }, + 'XYplorer': { + 'L_TYPE': 'Executable', + 'L_PATH': 'XYplorerFree', + 'L_ITEM': 'XYplorerFree.exe', + 'L_ARGS': r'/exp /win=max %userprofile%', + }, + }, + r'Diagnostics': { + 'AIDA64': { + 'L_TYPE': 'Executable', + 'L_PATH': 'AIDA64', + 'L_ITEM': 'aida64.exe', + }, + 'ProduKey': { + 'L_TYPE': 'Executable', + 'L_PATH': 'ProduKey', + 'L_ITEM': 'ProduKey.exe', + 'L_ELEV': 'True', + 'Extra Code': [ + r'if exist "%bin%\ProduKey" (', + r' del "%bin%\ProduKey\ProduKey.cfg" 2>nul', + r' del "%bin%\ProduKey\ProduKey64.cfg" 2>nul', + r')', + ], + }, + 'System Diagnostics': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'system_diagnostics.py', + 'L_ELEV': 'True', + }, + }, + r'Diagnostics\Extras': { + 'Autoruns (with VirusTotal Scan)': { + 'L_TYPE': 'Executable', + 'L_PATH': 'Autoruns', + 'L_ITEM': 'Autoruns.exe', + 'L_ARGS': '-e', + 'Extra Code': [ + r'reg add HKCU\Software\Sysinternals\AutoRuns /v checkvirustotal /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v EulaAccepted /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v shownomicrosoft /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v shownowindows /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v showonlyvirustotal /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v submitvirustotal /t REG_DWORD /d 0 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v verifysignatures /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns\SigCheck /v EulaAccepted /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns\Streams /v EulaAccepted /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns\VirusTotal /v VirusTotalTermsAccepted /t REG_DWORD /d 1 /f >nul', + ], + }, + 'BleachBit': { + 'L_TYPE': 'Executable', + 'L_PATH': 'BleachBit', + 'L_ITEM': 'bleachbit.exe', + }, + 'BlueScreenView': { + 'L_TYPE': 'Executable', + 'L_PATH': 'BlueScreenView', + 'L_ITEM': 'BlueScreenView.exe', + }, + 'ERUNT': { + 'L_TYPE': 'Executable', + 'L_PATH': 'erunt', + 'L_ITEM': 'ERUNT.EXE', + 'L_ARGS': '%client_dir%\Backups\Registry\%iso_date% sysreg curuser otherusers', + 'L_ELEV': 'True', + 'Extra Code': [ + r'call "%bin%\Scripts\init_client_dir.cmd" /Logs', + ], + }, + 'FurMark': { + 'L_TYPE': 'Executable', + 'L_PATH': 'FurMark', + 'L_ITEM': 'FurMark.exe', + }, + 'HDTune Pro': { + 'L_TYPE': 'Executable', + 'L_PATH': 'HDTunePro', + 'L_ITEM': 'HDTunePro.exe', + }, + 'HitmanPro': { + 'L_TYPE': 'Executable', + 'L_PATH': 'HitmanPro', + 'L_ITEM': 'HitmanPro.exe', + 'Extra Code': [ + r'call "%bin%\Scripts\init_client_dir.cmd" /Logs', + ], + }, + 'HWiNFO': { + 'L_TYPE': 'Executable', + 'L_PATH': 'HWiNFO', + 'L_ITEM': 'HWiNFO.exe', + 'Extra Code': [ + r'for %%a in (32 64) do (', + r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"', + r' (echo SensorsOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', + r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', + r')', + ], + }, + 'HWiNFO (Sensors)': { + 'L_TYPE': 'Executable', + 'L_PATH': 'HWiNFO', + 'L_ITEM': 'HWiNFO.exe', + 'Extra Code': [ + r'for %%a in (32 64) do (', + r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"', + r' (echo SensorsOnly=1)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', + r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', + r')', + ], + }, + }, + r'Drivers': { + 'Intel RST (Current Release)': { + 'L_TYPE': 'Executable', + 'L_PATH': '_Drivers\Intel RST', + 'L_ITEM': 'SetupRST_16.8.exe', + 'L_7ZIP': 'SetupRST_16.8.exe', + }, + 'Intel RST (Previous Releases)': { + 'L_TYPE': 'Folder', + 'L_PATH': '_Drivers\Intel RST', + 'L_ITEM': '.', + 'L_NCMD': 'True', + }, + 'Intel SSD Toolbox': { + 'L_TYPE': 'Executable', + 'L_PATH': r'_Drivers\Intel SSD Toolbox', + 'L_ITEM': 'Intel SSD Toolbox.exe', + }, + 'Samsing Magician': { + 'L_TYPE': 'Executable', + 'L_PATH': r'_Drivers\Samsung Magician', + 'L_ITEM': 'Samsung Magician.exe', + }, + 'Snappy Driver Installer Origin': { + 'L_TYPE': 'Executable', + 'L_PATH': '_Drivers\SDIO', + 'L_ITEM': 'SDIO.exe', + }, + }, + r'Drivers\Extras': { + 'Acer': { + 'L_TYPE': 'Executable', + 'L_PATH': 'HWiNFO', + 'L_ITEM': 'HWiNFO.exe', + 'Extra Code': [ + r'for %%a in (32 64) do (', + r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"', + r' (echo SensorsOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', + r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', + r')', + r'start "" "http://us.acer.com/ac/en/US/content/drivers"', + ], + }, + 'Lenovo': { + 'L_TYPE': 'Executable', + 'L_PATH': 'HWiNFO', + 'L_ITEM': 'HWiNFO.exe', + 'Extra Code': [ + r'for %%a in (32 64) do (', + r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"', + r' (echo SensorsOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', + r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', + r')', + r'start "" "https://pcsupport.lenovo.com/us/en/"', + ], + }, + 'Toshiba': { + 'L_TYPE': 'Executable', + 'L_PATH': 'HWiNFO', + 'L_ITEM': 'HWiNFO.exe', + 'Extra Code': [ + r'for %%a in (32 64) do (', + r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"', + r' (echo SensorsOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', + r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"', + r')', + r'start "" "http://support.toshiba.com/drivers"', + ], + }, + }, + r'Installers': { + 'ESET NOD32 AV': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'install_eset_nod32_av.py', + 'L_ELEV': 'True', + }, + 'SW Bundle': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'install_sw_bundle.py', + 'L_ELEV': 'True', + }, + }, + r'Installers\Extras\Office\2016': { + 'Home and Business (x32)': { + 'L_TYPE': 'Office', + 'L_PATH': '2016', + 'L_ITEM': '2016_hb_32.xml', + 'L_NCMD': 'True', + }, + 'Home and Business (x64)': { + 'L_TYPE': 'Office', + 'L_PATH': '2016', + 'L_ITEM': '2016_hb_64.xml', + 'L_NCMD': 'True', + }, + 'Home and Student (x32)': { + 'L_TYPE': 'Office', + 'L_PATH': '2016', + 'L_ITEM': '2016_hs_32.xml', + 'L_NCMD': 'True', + }, + 'Home and Student (x64)': { + 'L_TYPE': 'Office', + 'L_PATH': '2016', + 'L_ITEM': '2016_hs_64.xml', + 'L_NCMD': 'True', + }, + }, + r'Installers\Extras\Office\2019': { + 'Home and Business (x32)': { + 'L_TYPE': 'Office', + 'L_PATH': '2019', + 'L_ITEM': '2019_hb_32.xml', + 'L_NCMD': 'True', + }, + 'Home and Business (x64)': { + 'L_TYPE': 'Office', + 'L_PATH': '2019', + 'L_ITEM': '2019_hb_64.xml', + 'L_NCMD': 'True', + }, + 'Home and Student (x32)': { + 'L_TYPE': 'Office', + 'L_PATH': '2019', + 'L_ITEM': '2019_hs_32.xml', + 'L_NCMD': 'True', + }, + 'Home and Student (x64)': { + 'L_TYPE': 'Office', + 'L_PATH': '2019', + 'L_ITEM': '2019_hs_64.xml', + 'L_NCMD': 'True', + }, + 'Office 365 (x32)': { + 'L_TYPE': 'Office', + 'L_PATH': '2019', + 'L_ITEM': '365_32.xml', + 'L_NCMD': 'True', + }, + 'Office 365 (x64)': { + 'L_TYPE': 'Office', + 'L_PATH': '2019', + 'L_ITEM': '365_64.xml', + 'L_NCMD': 'True', + }, + }, + r'Installers\Extras\Runtimes': { + 'Visual C++ Runtimes': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'install_vcredists.py', + 'L_ELEV': 'True', + }, + }, + r'Misc': { + 'Cleanup CBS Temp Files': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'cbs_fix.py', + 'L_ELEV': 'True', + }, + 'ConEmu (as ADMIN)': { + 'L_TYPE': 'Executable', + 'L_PATH': 'ConEmu', + 'L_ITEM': 'ConEmu.exe', + 'L_ELEV': 'True', + }, + 'ConEmu': { + 'L_TYPE': 'Executable', + 'L_PATH': 'ConEmu', + 'L_ITEM': 'ConEmu.exe', + }, + 'Enter SafeMode': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'safemode_enter.py', + 'L_ELEV': 'True', + }, + 'Everything': { + 'L_TYPE': 'Executable', + 'L_PATH': 'Everything', + 'L_ITEM': 'Everything.exe', + 'L_ARGS': '-nodb', + 'L_ELEV': 'True', + }, + 'Exit SafeMode': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'safemode_exit.py', + 'L_ELEV': 'True', + }, + 'Network Stability Test': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'network_stability_test.py', + }, + 'Notepad++': { + 'L_TYPE': 'Executable', + 'L_PATH': 'notepadplusplus', + 'L_ITEM': 'notepadplusplus.exe', + }, + 'PuTTY': { + 'L_TYPE': 'Executable', + 'L_PATH': 'PuTTY', + 'L_ITEM': 'PUTTY.EXE', + }, + 'ShutUp10': { + 'L_TYPE': 'Executable', + 'L_PATH': 'ShutUp10', + 'L_ITEM': 'OOSU10.exe', + }, + 'ShutUp10 (1201 Minimal Selection)': { + 'L_TYPE': 'Executable', + 'L_PATH': 'ShutUp10', + 'L_ITEM': 'OOSU10.exe', + 'L_ARGS': '1201.cfg', + }, + 'WizTree': { + 'L_TYPE': 'Executable', + 'L_PATH': 'WizTree', + 'L_ITEM': 'WizTree.exe', + 'L_ELEV': 'True', + }, + 'XMPlay': { + 'L_TYPE': 'Executable', + 'L_PATH': 'XMPlay', + 'L_ITEM': 'xmplay.exe', + 'L_ARGS': '"%bin%\XMPlay\music.7z"', + }, + }, + r'Repairs': { + 'AdwCleaner': { + 'L_TYPE': 'Executable', + 'L_PATH': 'AdwCleaner', + 'L_ITEM': 'AdwCleaner.exe', + }, + 'Autoruns': { + 'L_TYPE': 'Executable', + 'L_PATH': 'Autoruns', + 'L_ITEM': 'Autoruns.exe', + 'L_ARGS': '-e', + 'Extra Code': [ + r'reg add HKCU\Software\Sysinternals\AutoRuns /v checkvirustotal /t REG_DWORD /d 0 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v EulaAccepted /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v shownomicrosoft /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v shownowindows /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v showonlyvirustotal /t REG_DWORD /d 0 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v submitvirustotal /t REG_DWORD /d 0 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns /v verifysignatures /t REG_DWORD /d 0 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns\SigCheck /v EulaAccepted /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns\Streams /v EulaAccepted /t REG_DWORD /d 1 /f >nul', + r'reg add HKCU\Software\Sysinternals\AutoRuns\VirusTotal /v VirusTotalTermsAccepted /t REG_DWORD /d 1 /f >nul', + ], + }, + 'CHKDSK': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'check_disk.py', + 'L_ELEV': 'True', + }, + 'DISM': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'dism.py', + 'L_ELEV': 'True', + }, + 'ESET Online Scanner': { + 'L_TYPE': 'Executable', + 'L_PATH': 'ESET', + 'L_ITEM': 'ESET.exe', + }, + 'KVRT': { + 'L_TYPE': 'Executable', + 'L_PATH': 'KVRT', + 'L_ITEM': 'KVRT.exe', + 'L_ARGS': ( + r' -accepteula' + r' -d %q_dir%' + r' -processlevel 3' + r' -dontcryptsupportinfo' + r' -fixednames' + ), + 'Extra Code': [ + r'call "%bin%\Scripts\init_client_dir.cmd" /Quarantine', + r'set "q_dir=%client_dir%\Quarantine\KVRT"', + r'mkdir "%q_dir%">nul 2>&1', + ], + }, + 'RKill': { + 'L_TYPE': 'Executable', + 'L_PATH': 'RKill', + 'L_ITEM': 'RKill.exe', + 'L_ARGS': '-s -l %log_dir%\Tools\RKill.log', + 'L_ELEV': 'True', + 'Extra Code': [ + r'call "%bin%\Scripts\init_client_dir.cmd" /Logs', + ], + }, + 'SFC Scan': { + 'L_TYPE': 'PyScript', + 'L_PATH': 'Scripts', + 'L_ITEM': 'sfc_scan.py', + 'L_ELEV': 'True', + }, + 'TDSSKiller': { + 'L_TYPE': 'Executable', + 'L_PATH': 'TDSSKiller', + 'L_ITEM': 'TDSSKiller.exe', + 'L_ARGS': ( + r' -l %log_dir%\Tools\TDSSKiller.log' + r' -qpath %q_dir%' + r' -accepteula' + r' -accepteulaksn' + r' -dcexact' + r' -tdlfs' + ), + 'Extra Code': [ + r'call "%bin%\Scripts\init_client_dir.cmd" /Quarantine', + r'set "q_dir=%client_dir%\Quarantine\TDSSKiller"', + r'mkdir "%q_dir%">nul 2>&1', + ], + }, + 'WinAIO Repair': { + 'L_TYPE': 'Executable', + 'L_PATH': 'WinAIORepair', + 'L_ITEM': 'Repair_Windows.exe', + 'L_ELEV': 'True', + 'Extra Code': [ + r'copy /y "%bin%\WinAIORepair\__empty.ini" "%bin%\WinAIORepair\settings.ini"', + ], + }, + 'WinAIO Repair (Fix Associations)': { + 'L_TYPE': 'Executable', + 'L_PATH': 'WinAIORepair', + 'L_ITEM': 'Repair_Windows.exe', + 'L_ELEV': 'True', + 'Extra Code': [ + r'copy /y "%bin%\WinAIORepair\__associations.ini" "%bin%\WinAIORepair\settings.ini"', + ], + }, + 'WinAIO Repair (Fix Permissions)': { + 'L_TYPE': 'Executable', + 'L_PATH': 'WinAIORepair', + 'L_ITEM': 'Repair_Windows.exe', + 'L_ELEV': 'True', + 'Extra Code': [ + r'copy /y "%bin%\WinAIORepair\__permissions.ini" "%bin%\WinAIORepair\settings.ini"', + ], + }, + }, + r'Uninstallers': { + 'IObit Uninstaller': { + 'L_TYPE': 'Executable', + 'L_PATH': 'IObitUninstallerPortable', + 'L_ITEM': 'IObitUninstallerPortable.exe', + }, + }, + } if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/settings/main.py b/.bin/Scripts/settings/main.py index b59dce45..5869580b 100644 --- a/.bin/Scripts/settings/main.py +++ b/.bin/Scripts/settings/main.py @@ -4,6 +4,8 @@ ENABLED_OPEN_LOGS = False ENABLED_TICKET_NUMBERS = False ENABLED_UPLOAD_DATA = True +HW_OVERRIDES_FORCED = False +HW_OVERRIDES_LIMITED = True # If True this disables HW_OVERRIDE_FORCED # STATIC VARIABLES (also used by BASH and BATCH files) ## NOTE: There are no spaces around the = for easier parsing in BASH and BATCH @@ -12,13 +14,6 @@ ARCHIVE_PASSWORD='Sorted1201' KIT_NAME_FULL='1201-WizardKit' KIT_NAME_SHORT='1201' SUPPORT_MESSAGE='Please let support know by opening an issue on Gogs' -# osTicket -DB_HOST='osticket.1201.com' -DB_NAME='osticket' -DB_USER='wizardkit' -DB_PASS='U9bJnF9eamVkfsVw' -SSH_PORT='22' -SSH_USER='sql_tunnel' # imgur IMGUR_CLIENT_ID='3d1ee1d38707b85' # Live Linux @@ -42,59 +37,61 @@ WIFI_PASSWORD='justintime!' ## one server serves multiple shares then you have to use the same ## user/password for all of those shares. BACKUP_SERVERS = [ - { 'IP': '10.11.1.20', - 'Name': 'Anaconda', - 'Mounted': False, - 'Share': 'Backups', - 'User': 'cx', - 'Pass': 'cx', - 'RW-User': 'backup', - 'RW-Pass': '1201 loves computers!', - }, + { 'IP': '10.11.1.20', + 'Name': 'Anaconda', + 'Mounted': False, + 'Share': 'Backups', + 'User': 'cx', + 'Pass': 'cx', + 'RW-User': 'backup', + 'RW-Pass': '1201 loves computers!', + }, ] BENCHMARK_SERVER = { - 'Name': 'Nextcloud', - 'Short Url': 'https://1201north.ddns.net:8001/index.php/f/27892', - 'Url': 'https://1201north.ddns.net:8001/public.php/webdav/Benchmarks', - 'User': 'RAE7ajRk25CBnW6', - 'Pass': '', + 'Name': 'Nextcloud', + 'Short Url': 'https://1201north.ddns.net:8001/index.php/f/27892', + 'Url': 'https://1201north.ddns.net:8001/public.php/webdav/Benchmarks', + 'User': 'RAE7ajRk25CBnW6', + 'Pass': '', } CRASH_SERVER = { - 'Name': 'Nextcloud', - 'Url': 'https://1201north.ddns.net:8001/public.php/webdav/WizardKit_Issues', - 'User': 'LoQ97J3r6CFGT2T', - 'Pass': '', + 'Name': 'Nextcloud', + 'Url': 'https://1201north.ddns.net:8001/public.php/webdav/WizardKit_Issues', + 'User': 'LoQ97J3r6CFGT2T', + 'Pass': '', } OFFICE_SERVER = { - 'IP': OFFICE_SERVER_IP, - 'Name': 'Anaconda', - 'Mounted': False, - 'Share': r'Public\Office\MS Office', - 'User': 'cx', - 'Pass': 'cx', - 'RW-User': 'backup', - 'RW-Pass': '1201 loves computers!', + 'IP': OFFICE_SERVER_IP, + 'Name': 'Anaconda', + 'Mounted': False, + 'Share': r'Public\Office\MS Office', + 'User': 'cx', + 'Pass': 'cx', + 'RW-User': 'backup', + 'RW-Pass': '1201 loves computers!', } QUICKBOOKS_SERVER = { - 'IP': QUICKBOOKS_SERVER_IP, - 'Name': 'Anaconda', - 'Mounted': False, - 'Share': r'Public\QuickBooks', - 'User': 'cx', - 'Pass': 'cx', - 'RW-User': 'backup', - 'RW-Pass': '1201 loves computers!', + 'IP': QUICKBOOKS_SERVER_IP, + 'Name': 'Anaconda', + 'Mounted': False, + 'Share': r'Public\QuickBooks', + 'User': 'cx', + 'Pass': 'cx', + 'RW-User': 'backup', + 'RW-Pass': '1201 loves computers!', } WINDOWS_SERVER = { - 'IP': '10.11.1.20', - 'Name': 'Anaconda', - 'Mounted': False, - 'Share': r'Public\Windows', - 'User': 'cx', - 'Pass': 'cx', - 'RW-User': 'backup', - 'RW-Pass': '1201 loves computers!', + 'IP': '10.11.1.20', + 'Name': 'Anaconda', + 'Mounted': False, + 'Share': r'Public\Windows', + 'User': 'cx', + 'Pass': 'cx', + 'RW-User': 'backup', + 'RW-Pass': '1201 loves computers!', } if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/settings/music.py b/.bin/Scripts/settings/music.py index a2d49f31..37f5d178 100644 --- a/.bin/Scripts/settings/music.py +++ b/.bin/Scripts/settings/music.py @@ -1,70 +1,72 @@ # Wizard Kit: Settings - Music MUSIC_MOD = [ - '104208#banana_boat.mod', - '114971#tilbury_fair.mod', - '132563#ufo_tune.mod', - '135906#magnetik_girl.xm', - '140628#autumn_in_budapest.xm', - '143198#summer_memories_3.xm', - '144405#hillbilly_billyboy.xm', - '154795#4mat_-_eternity.xm', - '155845#bookworm.mo3', - '155914#battleofsteel.xm', - '158975#1_channel_moog.it', - '165495#trans.s3m', - '168513#necros_-_introspection.s3m', - '169628#radix_-_feng_shui_schematics.xm', - '175238#unknown48_-_twilight.mod', - '33432#ambrozia.xm', - '33460#amigatre.mod', - '34594#CHARIOT.S3M', - '34596#BUTTERFL.XM', - '34654#CTGOBLIN.S3M', - '35151#bananasplit.mod', - '35280#DEADLOCK.XM', - '38591#compo_liam.xm', - '39987#crystald.s3m', - '40475#ELYSIUM.MOD', - '42146#enigma.mod', - '42519#GHOST2.MOD', - '42560#GSLINGER.MOD', - '42872#existing.xm', - '50427#nf-stven.xm', - '51549#overture.mod', - '54250#SATELL.S3M', - '54313#realmk.s3m', - '55789#scrambld.mod', - '57934#spacedeb.mod', - '59344#stardstm.mod', - '60395#2ND_PM.S3M', - '66187#external.xm', - '66343#beek-substitutionology.it', - '67561#radix-unreal_superhero.xm', - '70829#inside_out.s3m', - '83779#beyond_music.mod', - ] + '104208#banana_boat.mod', + '114971#tilbury_fair.mod', + '132563#ufo_tune.mod', + '135906#magnetik_girl.xm', + '140628#autumn_in_budapest.xm', + '143198#summer_memories_3.xm', + '144405#hillbilly_billyboy.xm', + '154795#4mat_-_eternity.xm', + '155845#bookworm.mo3', + '155914#battleofsteel.xm', + '158975#1_channel_moog.it', + '165495#trans.s3m', + '168513#necros_-_introspection.s3m', + '169628#radix_-_feng_shui_schematics.xm', + '175238#unknown48_-_twilight.mod', + '33432#ambrozia.xm', + '33460#amigatre.mod', + '34594#CHARIOT.S3M', + '34596#BUTTERFL.XM', + '34654#CTGOBLIN.S3M', + '35151#bananasplit.mod', + '35280#DEADLOCK.XM', + '38591#compo_liam.xm', + '39987#crystald.s3m', + '40475#ELYSIUM.MOD', + '42146#enigma.mod', + '42519#GHOST2.MOD', + '42560#GSLINGER.MOD', + '42872#existing.xm', + '50427#nf-stven.xm', + '51549#overture.mod', + '54250#SATELL.S3M', + '54313#realmk.s3m', + '55789#scrambld.mod', + '57934#spacedeb.mod', + '59344#stardstm.mod', + '60395#2ND_PM.S3M', + '66187#external.xm', + '66343#beek-substitutionology.it', + '67561#radix-unreal_superhero.xm', + '70829#inside_out.s3m', + '83779#beyond_music.mod', + ] MUSIC_SNES = [ - 'actr', - 'crock', - 'ct', - 'dkc', - 'dkq', - 'ff6', - 'fz', - 'loz3', - 'mmx', - 'ptws', - 'scv4', - 'sf', - 'sf2', - 'sgng', - 'smk', - 'smw', - 'yi', - 'zamn' - ] + 'actr', + 'crock', + 'ct', + 'dkc', + 'dkq', + 'ff6', + 'fz', + 'loz3', + 'mmx', + 'ptws', + 'scv4', + 'sf', + 'sf2', + 'sgng', + 'smk', + 'smw', + 'yi', + 'zamn' + ] if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/settings/osticket.py b/.bin/Scripts/settings/osticket.py new file mode 100644 index 00000000..6360a579 --- /dev/null +++ b/.bin/Scripts/settings/osticket.py @@ -0,0 +1,33 @@ +# Wizard Kit: Settings - osTicket + +OSTICKET = { + 'Database': { + 'Name': 'osticket', + 'User': 'wizardkit', + 'Pass': 'U9bJnF9eamVkfsVw', + 'Port': '3306', + }, + 'Disk Flag': { + 'Name': 'zHDTune', + 'Pass': 1, + 'Fail': 2, + }, + 'SSH': { + 'Host': 'osticket.1201.com', + 'Port': '22', + 'User': 'sql_tunnel', + }, + 'Staff': { + 'ID': '23', + 'Name': 'Wizard Kit', + }, + 'Tables': { + 'Response': 'ost_ticket_response', + 'Ticket': 'ost_ticket', + }, + } + +if __name__ == '__main__': + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/settings/partition_uids.py b/.bin/Scripts/settings/partition_uids.py new file mode 100644 index 00000000..f4b21df0 --- /dev/null +++ b/.bin/Scripts/settings/partition_uids.py @@ -0,0 +1,325 @@ +# Wizard Kit: Settings - Partition UIDs +# sources: https://en.wikipedia.org/wiki/GUID_Partition_Table +# https://en.wikipedia.org/wiki/Partition_type +# NOTE: Info has been trimmed for brevity. As such, there may be some inaccuracy. + +PARTITION_UIDS = { + '00': {'OS': 'All','Description': 'Empty partition entry'}, + '01': {'OS': 'DOS','Description': 'FAT12 as primary partition'}, + '02': {'OS': 'XENIX','Description': 'XENIX root'}, + '03': {'OS': 'XENIX','Description': 'XENIX usr'}, + '04': {'OS': 'DOS','Description': 'FAT16 with less than 32 MB'}, + '05': {'OS': 'DOS / SpeedStor','Description': 'Extended partition'}, + '06': {'OS': 'DOS1+','Description': 'FAT16B [over 65K sectors]'}, + '07': {'OS': 'Windows / OS/2 / QNX 2','Description': 'NTFS/exFAT/HPFS/IFS/QNX'}, + '08': {'OS': 'CBM / DOS / OS/2 / AIX /QNX','Description': 'FAT12-16/AIX/QNY/SplitDrive'}, + '09': {'OS': 'AIX / QNX / Coherent / OS-9','Description': 'AIX/QNZ/Coherent/RBF'}, + '0A': {'OS': 'OS/2 / Coherent','Description': 'Boot Manager / Swap'}, + '0B': {'OS': 'DOS','Description': 'FAT32 with CHS addressing'}, + '0C': {'OS': 'DOS','Description': 'FAT32 with LBA'}, + '0D': {'OS': 'Silicon Safe','Description': 'Reserved'}, + '0E': {'OS': 'DOS','Description': 'FAT16B with LBA'}, + '0F': {'OS': 'DOS','Description': 'Extended partition with LBA'}, + '10': {'OS': 'OPUS','Description': 'Unknown'}, + '11': {'OS': 'Leading Edge MS-DOS / OS/2','Description': 'FAT12/FAT16'}, + '12': {'OS': 'Compaq Contura','Description': 'conf/diag/hiber/rescue/serv'}, + '14': {'OS': 'AST DOS / OS/2 / MaverickOS','Description': 'FAT12/FAT16/Omega'}, + '15': {'OS': 'OS/2 / Maverick OS','Description': 'Hidden extended / Swap'}, + '16': {'OS': 'OS/2 Boot Manager','Description': 'Hidden FAT16B'}, + '17': {'OS': 'OS/2 Boot Manager','Description': 'Hidden IFS/HPFS/NTFS/exFAT'}, + '18': {'OS': 'AST Windows','Description': '0-Volt Suspend/SmartSleep'}, + '19': {'OS': 'Willowtech Photon coS','Description': 'Willowtech Photon coS'}, + '1B': {'OS': 'OS/2 Boot Manager','Description': 'Hidden FAT32'}, + '1C': {'OS': 'OS/2 Boot Manager','Description': 'Hidden FAT32 with LBA'}, + '1E': {'OS': 'OS/2 Boot Manager','Description': 'Hidden FAT16 with LBA'}, + '1F': {'OS': 'OS/2 Boot Manager','Description': 'Hidden extended with LBA'}, + '20': {'OS': 'Windows Mobile','Description': 'update XIP/Willowsoft OFS1'}, + '21': {'OS': 'Oxygen','Description': 'SpeedStor / FSo2'}, + '22': {'OS': 'Oxygen','Description': 'Oxygen Extended Partition'}, + '23': {'OS': 'Windows Mobile','Description': 'Reserved / boot XIP'}, + '24': {'OS': 'NEC MS-DOS0','Description': 'Logical FAT12 or FAT16'}, + '25': {'OS': 'Windows Mobile','Description': 'IMGFS[citation needed]'}, + '26': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, + '27': {'OS': 'Win/PQserv/MirOS/RooterBOOT','Description': 'WinRE/Rescue/MirOS/RooterBOOT'}, + '2A': {'OS': 'AtheOS','Description': 'AthFS/AFS/Reserved'}, + '2B': {'OS': 'SyllableOS','Description': 'SyllableSecure (SylStor)'}, + '31': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, + '32': {'OS': 'NOS','Description': 'Unknown'}, + '33': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, + '34': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, + '35': {'OS': 'OS/2 Server /eComStation','Description': 'JFS'}, + '36': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, + '38': {'OS': 'THEOS','Description': 'THEOS version 3.2, 2 GB'}, + '39': {'OS': 'Plan 9 / THEOS','Description': 'Plan 9 edition 3 / THEOS v4'}, + '3A': {'OS': 'THEOS','Description': 'THEOS v4, 4 GB'}, + '3B': {'OS': 'THEOS','Description': 'THEOS v4 extended'}, + '3C': {'OS': 'PartitionMagic','Description': 'PqRP (image in progress)'}, + '3D': {'OS': 'PartitionMagic','Description': 'Hidden NetWare'}, + '3F': {'OS': 'OS/32','Description': 'Unknown'}, + '40': {'OS': 'PICK / Venix','Description': 'PICK R83 / Venix 80286'}, + '41': {'OS': 'RISC / Linux / PowerPC','Description': 'Boot / Old Linux/Minix'}, + '42': {'OS': 'SFS / Linux / Win2K/XP/etc','Description': 'SFS / Old Linux Swap'}, + '43': {'OS': 'Linux','Description': 'Old Linux native'}, + '44': {'OS': 'GoBack','Description': 'Norton/WildFire/Adaptec/Roxio'}, + '45': {'OS': 'Boot-US / EUMEL/ELAN','Description': 'Priam/Boot/EUMEL/ELAN (L2)'}, + '46': {'OS': 'EUMEL/ELAN','Description': 'EUMEL/ELAN (L2)'}, + '47': {'OS': 'EUMEL/ELAN','Description': 'EUMEL/ELAN (L2)'}, + '48': {'OS': 'EUMEL/ELAN','Description': 'EUMEL/ELAN (L2), ERGOS L3'}, + '4A': {'OS': 'AdaOS / ALFS/THIN','Description': 'Aquila / ALFS/THIN'}, + '4C': {'OS': 'ETH Oberon','Description': 'Aos (A2) file system (76)'}, + '4D': {'OS': 'QNX Neutrino','Description': 'Primary QNX POSIX volume'}, + '4E': {'OS': 'QNX Neutrino','Description': 'Secondary QNX POSIX volume'}, + '4F': {'OS': 'QNX Neutrino / ETH Oberon','Description': '3rd QNX POSIX/Boot/Native'}, + '50': {'OS': 'DiskMan4/ETH/LynxOS/Novell','Description': 'Alt FS/Read-only/Lynx RTOS'}, + '51': {'OS': 'Disk Manager 4-6','Description': 'R/W partition (Aux 1)'}, + '52': {'OS': 'CP/M-80/ System V/AT, V/386','Description': 'CP/M-80'}, + '53': {'OS': 'Disk Manager 6','Description': 'Auxiliary 3 (WO)'}, + '54': {'OS': 'Disk Manager 6','Description': 'Dynamic Drive Overlay (DDO)'}, + '55': {'OS': 'EZ-Drive','Description': 'Maxtor/MaxBlast/DriveGuide'}, + '56': {'OS': 'AT&T DOS/EZ-Drive/VFeature','Description': 'FAT12 16/EZ-BIOS/VFeature'}, + '57': {'OS': 'DrivePro','Description': 'VNDI partition'}, + '5C': {'OS': 'EDISK','Description': 'Priam EDisk Volume'}, + '61': {'OS': 'SpeedStor','Description': 'Unknown'}, + '63': {'OS': 'Unix','Description': 'Unix,ISC,SysV,ix,BSD,HURD'}, + '64': {'OS': 'SpeedStor / NetWare','Description': 'NetWare FS 286/2,PC-ARMOUR'}, + '65': {'OS': 'NetWare','Description': 'NetWare File System 386'}, + '66': {'OS': 'NetWare / NetWare','Description': 'NetWare FS 386 / SMS'}, + '67': {'OS': 'NetWare','Description': 'Wolf Mountain'}, + '68': {'OS': 'NetWare','Description': 'Unknown'}, + '69': {'OS': 'NetWare 5 / NetWare','Description': 'Novell Storage Services'}, + '6E': {'Description': 'Unknown'}, + '70': {'OS': 'DiskSecure','Description': 'DiskSecure multiboot'}, + '71': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, + '72': {'OS': 'APTI systems / Unix V7/x86','Description': 'APTI altFAT12 / V7 / x86'}, + '73': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, + '74': {'OS': 'Microsoft, IBM','Description': 'Reserved / Scramdisk'}, + '75': {'OS': 'PC/IX','Description': 'Unknown'}, + '76': {'OS': 'Microsoft, IBM','Description': 'Reserved'}, + '77': {'OS': 'Novell','Description': 'VNDI, M2FS, M2CS'}, + '78': {'OS': 'Geurt Vos','Description': 'XOSL bootloader file system'}, + '79': {'OS': 'APTI conformant systems','Description': 'APTI altFAT16 (CHS, SFN)'}, + '7A': {'OS': 'APTI conformant systems','Description': 'APTI altFAT16 (LBA, SFN)'}, + '7B': {'OS': 'APTI conformant systems','Description': 'APTI altFAT16B (CHS, SFN)'}, + '7C': {'OS': 'APTI conformant systems','Description': 'APTI altFAT32 (LBA, SFN)'}, + '7D': {'OS': 'APTI conformant systems','Description': 'APTI altFAT32 (CHS, SFN)'}, + '7E': {'OS': 'F.I.X. (claim) / PrimoCache','Description': 'Level 2 cache'}, + '7F': {'OS': 'Varies','Description': 'AltOS DevPartition Standard'}, + '80': {'OS': 'Minix 1.1-1.4a','Description': 'Minix file system (old)'}, + '81': {'OS': 'Minix 1.4b+ / Linux','Description': 'MINIX FS/Mitac AdvDiskManager'}, + '82': {'OS': 'Linux / Sun Microsystems','Description': 'Swap / Solaris x86 / Prime'}, + '83': {'OS': 'GNU/Linux','Description': 'Any native Linux FS'}, + '84': {'OS': 'OS/2 / Windows 7','Description': 'Hibernat/HiddenC/RapidStart'}, + '85': {'OS': 'GNU/Linux','Description': 'Linux extended'}, + '86': {'OS': 'Windows NT 4 Server / Linux','Description': 'FAT16B mirror/LinuxRAID-old'}, + '87': {'OS': 'Windows NT 4 Server','Description': 'HPFS/NTFS mirrored volume'}, + '88': {'OS': 'GNU/Linux','Description': 'Plaintext partition table'}, + '8A': {'OS': 'AiR-BOOT','Description': 'Linux kernel image'}, + '8B': {'OS': 'Windows NT 4 Server','Description': 'FAT32 mirrored volume set'}, + '8C': {'OS': 'Windows NT 4 Server','Description': 'FAT32 mirrored volume set'}, + '8D': {'OS': 'Free FDISK','Description': 'Hidden FAT12'}, + '8E': {'OS': 'Linux','Description': 'Linux LVM'}, + '90': {'OS': 'Free FDISK','Description': 'Hidden FAT16'}, + '91': {'OS': 'Free FDISK','Description': 'Hidden extended partition'}, + '92': {'OS': 'Free FDISK','Description': 'Hidden FAT16B'}, + '93': {'OS': 'Amoeba / Linux','Description': 'Amoeba native/Hidden Linux'}, + '94': {'OS': 'Amoeba','Description': 'Amoeba bad block table'}, + '95': {'OS': 'EXOPC','Description': 'EXOPC native'}, + '96': {'OS': 'CHRP','Description': 'ISO-9660 file system'}, + '97': {'OS': 'Free FDISK','Description': 'Hidden FAT32'}, + '98': {'OS': 'Free FDISK / ROM-DOS','Description': 'Hidden FAT32 / service part'}, + '99': {'OS': 'early Unix','Description': 'Unknown'}, + '9A': {'OS': 'Free FDISK','Description': 'Hidden FAT16'}, + '9B': {'OS': 'Free FDISK','Description': 'Hidden extended partition'}, + '9E': {'OS': 'VSTA / ForthOS','Description': 'ForthOS (eForth port)'}, + '9F': {'OS': 'BSD/OS 3.0+, BSDI','Description': 'Unknown'}, + 'A0': {'OS': 'HP/Phoenix/IBM/Toshiba/Sony','Description': 'Diagnostic for HP/Hibernate'}, + 'A1': {'OS': 'HP / Phoenix, NEC','Description': 'HP Vol Expansion/Hibernate'}, + 'A2': {'OS': 'Cyclone V','Description': 'Hard Processor System (HPS)'}, + 'A3': {'OS': 'HP','Description': 'HP Vol Expansion(SpeedStor)'}, + 'A4': {'OS': 'HP','Description': 'HP Vol Expansion(SpeedStor)'}, + 'A5': {'OS': 'BSD','Description': 'BSD slice'}, + 'A6': {'OS': 'OpenBSD','Description': 'HP Vol Expansion/BSD slice'}, + 'A7': {'OS': 'NeXT','Description': 'NeXTSTEP'}, + 'A8': {'OS': 'Darwin, Mac OS X','Description': 'Apple Darwin, Mac OS X UFS'}, + 'A9': {'OS': 'NetBSD','Description': 'NetBSD slice'}, + 'AA': {'OS': 'MS-DOS','Description': 'Olivetti DOS FAT12(1.44 MB)'}, + 'AB': {'OS': 'Darwin, Mac OS X / GO! OS','Description': 'Apple Darwin/OS X boot/GO!'}, + 'AD': {'OS': 'RISC OS','Description': 'ADFS / FileCore format'}, + 'AE': {'OS': 'ShagOS','Description': 'ShagOS file system'}, + 'AF': {'OS': 'ShagOS','Description': 'OS X HFS & HFS+/ShagOS Swap'}, + 'B0': {'OS': 'Boot-Star','Description': 'Boot-Star dummy partition'}, + 'B1': {'OS': 'QNX 6.x','Description': 'HPVolExpansion/QNX Neutrino'}, + 'B2': {'OS': 'QNX 6.x','Description': 'QNX Neutrino power-safe FS'}, + 'B3': {'OS': 'QNX 6.x','Description': 'HPVolExpansion/QNX Neutrino'}, + 'B4': {'OS': 'HP','Description': 'HP Vol Expansion(SpeedStor)'}, + 'B6': {'OS': 'Windows NT 4 Server','Description': 'HPVolExpansion/FAT16Bmirror'}, + 'B7': {'OS': 'BSDI / Windows NT 4 Server','Description': 'BSDI,Swap,HPFS/NTFS mirror'}, + 'B8': {'OS': 'BSDI (before 3.0)','Description': 'BSDI Swap / native FS'}, + 'BB': {'OS': 'Acronis/BootWizard/WinNT 4','Description': 'BootWizard/OEM/FAT32 mirror'}, + 'BC': {'OS': 'Acronis/WinNT/BackupCapsule','Description': 'FAT32RAID/SecureZone/Backup'}, + 'BD': {'OS': 'BonnyDOS/286','Description': 'Unknown'}, + 'BE': {'OS': 'Solaris 8','Description': 'Solaris 8 boot'}, + 'BF': {'OS': 'Solaris','Description': 'Solaris x86'}, + 'C0': {'OS': 'DR-DOS,MultiuserDOS,REAL/32','Description': 'Secured FAT (under 32 MB)'}, + 'C1': {'OS': 'DR DOS','Description': 'Secured FAT12'}, + 'C2': {'OS': 'Power Boot','Description': 'Hidden Linux native FS'}, + 'C3': {'OS': 'Power Boot','Description': 'Hidden Linux Swap'}, + 'C4': {'OS': 'DR DOS','Description': 'Secured FAT16'}, + 'C5': {'OS': 'DR DOS','Description': 'Secured extended partition'}, + 'C6': {'OS': 'DR DOS / WinNT 4 Server','Description': 'Secured FAT16B/FAT16Bmirror'}, + 'C7': {'OS': 'Syrinx / WinNT 4 Server','Description': 'Syrinx boot/HPFS/NTFSmirror'}, + 'C8': {'Description': "DR-DOS Reserved (since '97)"}, + 'C9': {'Description': "DR-DOS Reserved (since '97)"}, + 'CA': {'Description': "DR-DOS Reserved (since '97)"}, + 'CB': {'OS': 'DR-DOSx / WinNT 4 Server','Description': 'Secured FAT32/FAT32 mirror'}, + 'CC': {'OS': 'DR-DOSx / WinNT 4 Server','Description': 'Secured FAT32/FAT32 mirror'}, + 'CD': {'OS': 'CTOS','Description': 'Memory dump'}, + 'CE': {'OS': 'DR-DOSx','Description': 'Secured FAT16B'}, + 'CF': {'OS': 'DR-DOSx','Description': 'Secured extended partition'}, + 'D0': {'OS': 'Multiuser DOS, REAL/32','Description': 'Secured FAT (over 32 MB)'}, + 'D1': {'OS': 'Multiuser DOS','Description': 'Secured FAT12'}, + 'D4': {'OS': 'Multiuser DOS','Description': 'Secured FAT16'}, + 'D5': {'OS': 'Multiuser DOS','Description': 'Secured extended partition'}, + 'D6': {'OS': 'Multiuser DOS','Description': 'Secured FAT16B'}, + 'D8': {'OS': 'Digital Research','Description': 'CP/M-86 [citation needed]'}, + 'DA': {'OS': 'Powercopy Backup','Description': 'Non-FS data / Shielded disk'}, + 'DB': {'OS': 'CP/M-86/CDOS/CTOS/D800/DRMK','Description': 'CP/M-86/ConcDOS/Boot/FAT32'}, + 'DD': {'OS': 'CTOS','Description': 'Hidden memory dump'}, + 'DE': {'OS': 'Dell','Description': 'FAT16 utility/diagnostic'}, + 'DF': {'OS': 'DG/UX / BootIt / Aviion','Description': 'DG/UX Virt DiskMan / EMBRM'}, + 'E0': {'OS': 'STMicroelectronics','Description': 'ST AVFS'}, + 'E1': {'OS': 'SpeedStor','Description': 'ExtendedFAT12 >1023cylinder'}, + 'E2': {'Description': 'DOS read-only (XFDISK)'}, + 'E3': {'OS': 'SpeedStor','Description': 'DOS read-only'}, + 'E4': {'OS': 'SpeedStor','Description': 'ExtendedFAT16 <1024cylinder'}, + 'E5': {'OS': 'Tandy MS-DOS','Description': 'Logical FAT12 or FAT16'}, + 'E6': {'OS': 'SpeedStor','Description': 'Unknown'}, + 'E8': {'OS': 'LUKS','Description': 'Linux Unified Key Setup'}, + 'EB': {'OS': 'BeOS, Haiku','Description': 'BFS'}, + 'EC': {'OS': 'SkyOS','Description': 'SkyFS'}, + 'ED': {'OS': 'Sprytix / EDD 4','Description': 'EDC loader / GPT hybrid MBR'}, + 'EE': {'OS': 'EFI','Description': 'GPT protective MBR'}, + 'EF': {'OS': 'EFI','Description': 'EFI system partition'}, + 'F0': {'OS': 'Linux / OS/32','Description': 'PA-RISC Linux boot loader.'}, + 'F1': {'OS': 'SpeedStor','Description': 'Unknown'}, + 'F2': {'OS': 'SperryIT DOS/Unisys DOS','Description': 'Logical FAT12/FAT16'}, + 'F3': {'OS': 'SpeedStor','Description': 'Unknown'}, + 'F4': {'OS': 'SpeedStor / Prologue','Description': '"large"DOS part/NGF/TwinFS'}, + 'F5': {'OS': 'Prologue','Description': 'MD0-MD9 part for NGF/TwinFS'}, + 'F6': {'OS': 'SpeedStor','Description': 'Unknown'}, + 'F7': {'OS': 'O.S.G. / X1','Description': 'EFAT / Solid State FS'}, + 'F9': {'OS': 'Linux','Description': 'pCache ext2/ext3 cache'}, + 'FA': {'OS': 'Bochs','Description': 'x86 emulator'}, + 'FB': {'OS': 'VMware','Description': 'VMware VMFS partition'}, + 'FC': {'OS': 'VMware','Description': 'Swap / VMKCORE kernel dump'}, + 'FD': {'OS': 'Linux / FreeDOS','Description': 'LinuxRAID/Reserved4FreeDOS'}, + 'FE': {'OS': 'SpeedStor/LANstep/NT/Linux','Description': 'PS/2/DiskAdmin/old LinuxLVM'}, + 'FF': {'OS': 'XENIX','Description': 'XENIX bad block table'}, + '00000000-0000-0000-0000-000000000000': {'Description': 'Unused entry'}, + '024DEE41-33E7-11D3-9D69-0008C781F39F': {'Description': 'MBR partition scheme'}, + 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B': {'Description': 'EFI System partition'}, + '21686148-6449-6E6F-744E-656564454649': {'Description': 'BIOS Boot partition'}, + 'D3BFE2DE-3DAF-11DF-BA40-E3A556D89593': {'Description': 'Intel Fast Flash (iFFS) partition (for Intel Rapid Start technology)'}, + 'F4019732-066E-4E12-8273-346C5641494F': {'Description': 'Sony boot partition'}, + 'BFBFAFE7-A34F-448A-9A5B-6213EB736C22': {'Description': 'Lenovo boot partition'}, + 'E3C9E316-0B5C-4DB8-817D-F92DF00215AE': {'OS': 'Windows', 'Description': 'Microsoft Reserved Partition (MSR)'}, + 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7': {'OS': 'Windows', 'Description': 'Basic data partition'}, + '5808C8AA-7E8F-42E0-85D2-E1E90434CFB3': {'OS': 'Windows', 'Description': 'Logical Disk Manager (LDM) metadata partition'}, + 'AF9B60A0-1431-4F62-BC68-3311714A69AD': {'OS': 'Windows', 'Description': 'Logical Disk Manager data partition'}, + 'DE94BBA4-06D1-4D40-A16A-BFD50179D6AC': {'OS': 'Windows', 'Description': 'Windows Recovery Environment'}, + '37AFFC90-EF7D-4E96-91C3-2D7AE055B174': {'OS': 'Windows', 'Description': 'IBM General Parallel File System (GPFS) partition'}, + 'E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D': {'OS': 'Windows', 'Description': 'Storage Spaces partition'}, + '75894C1E-3AEB-11D3-B7C1-7B03A0000000': {'OS': 'HP-UX', 'Description': 'Data partition'}, + 'E2A1E728-32E3-11D6-A682-7B03A0000000': {'OS': 'HP-UX', 'Description': 'Service Partition'}, + '0FC63DAF-8483-4772-8E79-3D69D8477DE4': {'OS': 'Linux', 'Description': 'Linux filesystem data'}, + 'A19D880F-05FC-4D3B-A006-743F0F84911E': {'OS': 'Linux', 'Description': 'RAID partition'}, + '44479540-F297-41B2-9AF7-D131D5F0458A': {'OS': 'Linux', 'Description': 'Root partition (x86)'}, + '4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709': {'OS': 'Linux', 'Description': 'Root partition (x86-64)'}, + '69DAD710-2CE4-4E3C-B16C-21A1D49ABED3': {'OS': 'Linux', 'Description': 'Root partition (32-bit ARM)'}, + 'B921B045-1DF0-41C3-AF44-4C6F280D3FAE': {'OS': 'Linux', 'Description': 'Root partition (64-bit ARM)/AArch64)'}, + '0657FD6D-A4AB-43C4-84E5-0933C84B4F4F': {'OS': 'Linux', 'Description': 'Swap partition'}, + 'E6D6D379-F507-44C2-A23C-238F2A3DF928': {'OS': 'Linux', 'Description': 'Logical Volume Manager (LVM) partition'}, + '933AC7E1-2EB4-4F13-B844-0E14E2AEF915': {'OS': 'Linux', 'Description': '/home partition'}, + '3B8F8425-20E0-4F3B-907F-1A25A76F98E8': {'OS': 'Linux', 'Description': '/srv (server data) partition'}, + '7FFEC5C9-2D00-49B7-8941-3EA10A5586B7': {'OS': 'Linux', 'Description': 'Plain dm-crypt partition'}, + 'CA7D7CCB-63ED-4C53-861C-1742536059CC': {'OS': 'Linux', 'Description': 'LUKS partition'}, + '8DA63339-0007-60C0-C436-083AC8230908': {'OS': 'Linux', 'Description': 'Reserved'}, + '83BD6B9D-7F41-11DC-BE0B-001560B84F0F': {'OS': 'FreeBSD', 'Description': 'Boot partition'}, + '516E7CB4-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Data partition'}, + '516E7CB5-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Swap partition'}, + '516E7CB6-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Unix File System (UFS) partition'}, + '516E7CB8-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Vinum volume manager partition'}, + '516E7CBA-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'ZFS partition'}, + '48465300-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Hierarchical File System Plus (HFS+) partition'}, + '55465300-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple UFS'}, + '6A898CC3-1DD2-11B2-99A6-080020736631': {'OS': 'OS X Darwin', 'Description': 'ZFS'}, + '52414944-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple RAID partition'}, + '52414944-5F4F-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple RAID partition, offline'}, + '426F6F74-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple Boot partition (Recovery HD)'}, + '4C616265-6C00-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple Label'}, + '5265636F-7665-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple TV Recovery partition'}, + '53746F72-6167-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple Core Storage (i.e. Lion FileVault) partition'}, + '6A82CB45-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Boot partition'}, + '6A85CF4D-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Root partition'}, + '6A87C46F-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Swap partition'}, + '6A8B642B-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Backup partition'}, + '6A898CC3-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': '/usr partition'}, + '6A8EF2E9-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': '/var partition'}, + '6A90BA39-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': '/home partition'}, + '6A9283A5-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Alternate sector'}, + '6A945A3B-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Reserved partition'}, + '6A9630D1-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'}, + '6A980767-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'}, + '6A96237F-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'}, + '6A8D2AC7-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'}, + '49F48D32-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'Swap partition'}, + '49F48D5A-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'FFS partition'}, + '49F48D82-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'LFS partition'}, + '49F48DAA-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'RAID partition'}, + '2DB519C4-B10F-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'Concatenated partition'}, + '2DB519EC-B10F-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'Encrypted partition'}, + 'FE3A2A5D-4F32-41A7-B725-ACCC3285A309': {'OS': 'ChromeOS', 'Description': 'ChromeOS kernel'}, + '3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC': {'OS': 'ChromeOS', 'Description': 'ChromeOS rootfs'}, + '2E0A753D-9E48-43B0-8337-B15192CB1B5E': {'OS': 'ChromeOS', 'Description': 'ChromeOS future use'}, + '42465331-3BA3-10F1-802A-4861696B7521': {'OS': 'Haiku', 'Description': 'Haiku BFS'}, + '85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Boot partition'}, + '85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Data partition'}, + '85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Swap partition'}, + '0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Unix File System (UFS) partition'}, + '85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Vinum volume manager partition'}, + '85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'ZFS partition'}, + '45B0969E-9B03-4F30-B4C6-B4B80CEFF106': {'OS': 'Ceph', 'Description': 'Ceph Journal'}, + '45B0969E-9B03-4F30-B4C6-5EC00CEFF106': {'OS': 'Ceph', 'Description': 'Ceph dm-crypt Encrypted Journal'}, + '4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D': {'OS': 'Ceph', 'Description': 'Ceph OSD'}, + '4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D': {'OS': 'Ceph', 'Description': 'Ceph dm-crypt OSD'}, + '89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE': {'OS': 'Ceph', 'Description': 'Ceph disk in creation'}, + '89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE': {'OS': 'Ceph', 'Description': 'Ceph dm-crypt disk in creation'}, + '824CC7A0-36A8-11E3-890A-952519AD3F61': {'OS': 'OpenBSD', 'Description': 'Data partition'}, + 'CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1': {'OS': 'QNX', 'Description': 'Power-safe (QNX6) file system'}, + 'C91818F9-8025-47AF-89D2-F030D7000C2C': {'OS': 'Plan 9', 'Description': 'Plan 9 partition'}, + '9D275380-40AD-11DB-BF97-000C2911D1B8': {'OS': 'VMware ESX', 'Description': 'vmkcore (coredump partition)'}, + 'AA31E02A-400F-11DB-9590-000C2911D1B8': {'OS': 'VMware ESX', 'Description': 'VMFS filesystem partition'}, + '9198EFFC-31C0-11DB-8F78-000C2911D1B8': {'OS': 'VMware ESX', 'Description': 'VMware Reserved'}, + '2568845D-2332-4675-BC39-8FA5A4748D15': {'OS': 'Android-IA', 'Description': 'Bootloader'}, + '114EAFFE-1552-4022-B26E-9B053604CF84': {'OS': 'Android-IA', 'Description': 'Bootloader2'}, + '49A4D17F-93A3-45C1-A0DE-F50B2EBE2599': {'OS': 'Android-IA', 'Description': 'Boot'}, + '4177C722-9E92-4AAB-8644-43502BFD5506': {'OS': 'Android-IA', 'Description': 'Recovery'}, + 'EF32A33B-A409-486C-9141-9FFB711F6266': {'OS': 'Android-IA', 'Description': 'Misc'}, + '20AC26BE-20B7-11E3-84C5-6CFDB94711E9': {'OS': 'Android-IA', 'Description': 'Metadata'}, + '38F428E6-D326-425D-9140-6E0EA133647C': {'OS': 'Android-IA', 'Description': 'System'}, + 'A893EF21-E428-470A-9E55-0668FD91A2D9': {'OS': 'Android-IA', 'Description': 'Cache'}, + 'DC76DDA9-5AC1-491C-AF42-A82591580C0D': {'OS': 'Android-IA', 'Description': 'Data'}, + 'EBC597D0-2053-4B15-8B64-E0AAC75F4DB1': {'OS': 'Android-IA', 'Description': 'Persistent'}, + '8F68CC74-C5E5-48DA-BE91-A0C8C15E9C80': {'OS': 'Android-IA', 'Description': 'Factory'}, + '767941D0-2085-11E3-AD3B-6CFDB94711E9': {'OS': 'Android-IA', 'Description': 'Fastboot / Tertiary'}, + 'AC6D7924-EB71-4DF8-B48D-E267B27148FF': {'OS': 'Android-IA', 'Description': 'OEM'}, + '7412F7D5-A156-4B13-81DC-867174929325': {'OS': 'ONIE', 'Description': 'Boot'}, + 'D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149': {'OS': 'ONIE', 'Description': 'Config'}, + '9E1A2D38-C612-4316-AA26-8B49521E5A8B': {'OS': 'PowerPC', 'Description': 'PReP boot'}, + 'BC13C2FF-59E6-4262-A352-B275FD6F7172': {'OS': 'Freedesktop', 'Description': 'Extended Boot Partition ($BOOT)'}, +} + +if __name__ == '__main__': + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/settings/sources.py b/.bin/Scripts/settings/sources.py index 1dc650f2..cc77f066 100644 --- a/.bin/Scripts/settings/sources.py +++ b/.bin/Scripts/settings/sources.py @@ -1,208 +1,210 @@ # Wizard Kit: Settings - Sources SOURCE_URLS = { - 'Adobe Reader DC': 'http://ardownload.adobe.com/pub/adobe/reader/win/AcrobatDC/1801120058/AcroRdrDC1801120058_en_US.exe', - 'AdwCleaner': 'https://downloads.malwarebytes.com/file/adwcleaner', - 'AIDA64': 'http://download.aida64.com/aida64engineer597.zip', - 'aria2': 'https://github.com/aria2/aria2/releases/download/release-1.34.0/aria2-1.34.0-win-32bit-build1.zip', - 'Autoruns': 'https://download.sysinternals.com/files/Autoruns.zip', - 'BleachBit': 'https://download.bleachbit.org/BleachBit-2.0-portable.zip', - 'BlueScreenView32': 'http://www.nirsoft.net/utils/bluescreenview.zip', - 'BlueScreenView64': 'http://www.nirsoft.net/utils/bluescreenview-x64.zip', - 'Caffeine': 'http://www.zhornsoftware.co.uk/caffeine/caffeine.zip', - 'ClassicStartSkin': 'http://www.classicshell.net/forum/download/file.php?id=3001&sid=9a195960d98fd754867dcb63d9315335', - 'Du': 'https://download.sysinternals.com/files/DU.zip', - 'ERUNT': 'http://www.aumha.org/downloads/erunt.zip', - 'ESET Online Scanner': 'https://download.eset.com/com/eset/tools/online_scanner/latest/esetonlinescanner_enu.exe', - 'ESET NOD32 AV': 'https://download.eset.com/com/eset/apps/home/eav/windows/latest/eav_nt64.exe', - 'Everything32': 'https://www.voidtools.com/Everything-1.4.1.895.x86.zip', - 'Everything64': 'https://www.voidtools.com/Everything-1.4.1.895.x64.zip', - 'FastCopy': 'http://ftp.vector.co.jp/70/64/2323/FastCopy354_installer.zip', - 'FurMark': 'https://geeks3d.com/dl/get/569', - 'Firefox uBO': 'https://addons.mozilla.org/firefox/downloads/file/1056733/ublock_origin-1.16.20-an+fx.xpi', - 'HitmanPro32': 'https://dl.surfright.nl/HitmanPro.exe', - 'HitmanPro64': 'https://dl.surfright.nl/HitmanPro_x64.exe', - 'HWiNFO': 'http://app.oldfoss.com:81/download/HWiNFO/hwi_588.zip', - 'Intel SSD Toolbox': r'https://downloadmirror.intel.com/27656/eng/Intel%20SSD%20Toolbox%20-%20v3.5.2.exe', - 'IOBit_Uninstaller': 'https://portableapps.duckduckgo.com/IObitUninstallerPortable_7.5.0.7.paf.exe', - 'KVRT': 'http://devbuilds.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe', - 'Linux Reader': 'https://www.diskinternals.com/download/Linux_Reader.exe', - 'Macs Fan Control': 'https://www.crystalidea.com/downloads/macsfancontrol_setup.exe', - 'NirCmd32': 'https://www.nirsoft.net/utils/nircmd.zip', - 'NirCmd64': 'https://www.nirsoft.net/utils/nircmd-x64.zip', - 'NotepadPlusPlus': 'https://notepad-plus-plus.org/repository/7.x/7.5.8/npp.7.5.8.bin.minimalist.7z', - 'Office Deployment Tool 2016': 'https://download.microsoft.com/download/2/7/A/27AF1BE6-DD20-4CB4-B154-EBAB8A7D4A7E/officedeploymenttool_10810.33603.exe', - 'ProduKey32': 'http://www.nirsoft.net/utils/produkey.zip', - 'ProduKey64': 'http://www.nirsoft.net/utils/produkey-x64.zip', - 'PuTTY': 'https://the.earth.li/~sgtatham/putty/latest/w32/putty.zip', - 'RKill': 'https://www.bleepingcomputer.com/download/rkill/dl/10/', - 'Samsung Magician': 'https://s3.ap-northeast-2.amazonaws.com/global.semi.static/SAMSUNG_SSD_v5_2_1_180523/CD0CFAC4675B9E502899B41BE00525C3909ECE3AD57CC1A2FB6B74A766B2A1EA/Samsung_Magician_Installer.zip', - 'SDIO Themes': 'http://snappy-driver-installer.org/downloads/SDIO_Themes.zip', - 'SDIO Torrent': 'http://snappy-driver-installer.org/downloads/SDIO_Update.torrent', - 'ShutUp10': 'https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe', - 'TDSSKiller': 'https://media.kaspersky.com/utilities/VirusUtilities/EN/tdsskiller.exe', - 'TestDisk': 'https://www.cgsecurity.org/testdisk-7.1-WIP.win.zip', - 'wimlib32': 'https://wimlib.net/downloads/wimlib-1.12.0-windows-i686-bin.zip', - 'wimlib64': 'https://wimlib.net/downloads/wimlib-1.12.0-windows-x86_64-bin.zip', - 'WinAIO Repair': 'http://www.tweaking.com/files/setups/tweaking.com_windows_repair_aio.zip', - 'Winapp2': 'https://github.com/MoscaDotTo/Winapp2/archive/master.zip', - 'WizTree': 'https://antibody-software.com/files/wiztree_3_26_portable.zip', - 'XMPlay 7z': 'https://support.xmplay.com/files/16/xmp-7z.zip?v=800962', - 'XMPlay Game': 'https://support.xmplay.com/files/12/xmp-gme.zip?v=515637', - 'XMPlay RAR': 'https://support.xmplay.com/files/16/xmp-rar.zip?v=409646', - 'XMPlay WAModern': 'https://support.xmplay.com/files/10/WAModern.zip?v=207099', - 'XMPlay': 'https://support.xmplay.com/files/20/xmplay383.zip?v=298195', - 'XYplorerFree': 'https://www.xyplorer.com/download/xyplorer_free_noinstall.zip', - } + 'Adobe Reader DC': 'http://ardownload.adobe.com/pub/adobe/reader/win/AcrobatDC/1901020069/AcroRdrDC1901020069_en_US.exe', + 'AdwCleaner': 'https://downloads.malwarebytes.com/file/adwcleaner', + 'AIDA64': 'http://download.aida64.com/aida64engineer599.zip', + 'aria2': 'https://github.com/aria2/aria2/releases/download/release-1.34.0/aria2-1.34.0-win-32bit-build1.zip', + 'Autoruns': 'https://download.sysinternals.com/files/Autoruns.zip', + 'BleachBit': 'https://download.bleachbit.org/BleachBit-2.0-portable.zip', + 'BlueScreenView32': 'http://www.nirsoft.net/utils/bluescreenview.zip', + 'BlueScreenView64': 'http://www.nirsoft.net/utils/bluescreenview-x64.zip', + 'Caffeine': 'http://www.zhornsoftware.co.uk/caffeine/caffeine.zip', + 'ClassicStartSkin': 'http://www.classicshell.net/forum/download/file.php?id=3001&sid=9a195960d98fd754867dcb63d9315335', + 'Du': 'https://download.sysinternals.com/files/DU.zip', + 'ERUNT': 'http://www.aumha.org/downloads/erunt.zip', + 'ESET Online Scanner': 'https://download.eset.com/com/eset/tools/online_scanner/latest/esetonlinescanner_enu.exe', + 'ESET NOD32 AV': 'https://download.eset.com/com/eset/apps/home/eav/windows/latest/eav_nt64.exe', + 'Everything32': 'https://www.voidtools.com/Everything-1.4.1.924.x86.zip', + 'Everything64': 'https://www.voidtools.com/Everything-1.4.1.924.x64.zip', + 'FastCopy': 'http://ftp.vector.co.jp/70/93/2323/FastCopy361_installer.exe', + 'FurMark': 'https://geeks3d.com/dl/get/569', + 'Firefox uBO': 'https://addons.mozilla.org/firefox/downloads/file/1166954/ublock_origin-1.17.4-an+fx.xpi', + 'HitmanPro32': 'https://dl.surfright.nl/HitmanPro.exe', + 'HitmanPro64': 'https://dl.surfright.nl/HitmanPro_x64.exe', + 'HWiNFO': 'http://files2.majorgeeks.com/caae8849cf31a8d77c51283b720e60e49ce1dc78/systeminfo/hwi_600.zip', + 'Intel SSD Toolbox': r'https://downloadmirror.intel.com/28447/eng/Intel%20SSD%20Toolbox%20-%20v3.5.8.exe', + 'IOBit_Uninstaller': r'https://portableapps.com/redirect/?a=IObitUninstallerPortable&s=s&d=pa&f=IObitUninstallerPortable_7.5.0.7.paf.exe', + 'KVRT': 'http://devbuilds.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe', + 'Linux Reader': 'https://www.diskinternals.com/download/Linux_Reader.exe', + 'Macs Fan Control': 'https://www.crystalidea.com/downloads/macsfancontrol_setup.exe', + 'NirCmd32': 'https://www.nirsoft.net/utils/nircmd.zip', + 'NirCmd64': 'https://www.nirsoft.net/utils/nircmd-x64.zip', + 'NotepadPlusPlus': 'https://notepad-plus-plus.org/repository/7.x/7.6.2/npp.7.6.2.bin.minimalist.7z', + 'Office Deployment Tool': 'https://download.microsoft.com/download/2/7/A/27AF1BE6-DD20-4CB4-B154-EBAB8A7D4A7E/officedeploymenttool_11107-33602.exe', + 'ProduKey32': 'http://www.nirsoft.net/utils/produkey.zip', + 'ProduKey64': 'http://www.nirsoft.net/utils/produkey-x64.zip', + 'PuTTY': 'https://the.earth.li/~sgtatham/putty/latest/w32/putty.zip', + 'RKill': 'https://www.bleepingcomputer.com/download/rkill/dl/10/', + 'Samsung Magician': 'https://s3.ap-northeast-2.amazonaws.com/global.semi.static/SAMSUNG_SSD_v5_3_0_181121/CD0C7CC1BE00525FAC4675B9E502899B41D5C3909ECE3AA2FB6B74A766B2A1EA/Samsung_Magician_Installer.zip', + 'SDIO Themes': 'http://snappy-driver-installer.org/downloads/SDIO_Themes.zip', + 'SDIO Torrent': 'http://snappy-driver-installer.org/downloads/SDIO_Update.torrent', + 'ShutUp10': 'https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe', + 'TDSSKiller': 'https://media.kaspersky.com/utilities/VirusUtilities/EN/tdsskiller.exe', + 'TestDisk': 'https://www.cgsecurity.org/testdisk-7.1-WIP.win.zip', + 'wimlib32': 'https://wimlib.net/downloads/wimlib-1.13.0-windows-i686-bin.zip', + 'wimlib64': 'https://wimlib.net/downloads/wimlib-1.13.0-windows-x86_64-bin.zip', + 'WinAIO Repair': 'http://www.tweaking.com/files/setups/tweaking.com_windows_repair_aio.zip', + 'Winapp2': 'https://github.com/MoscaDotTo/Winapp2/archive/master.zip', + 'WizTree': 'https://antibody-software.com/files/wiztree_3_26_portable.zip', + 'XMPlay 7z': 'https://support.xmplay.com/files/16/xmp-7z.zip?v=800962', + 'XMPlay Game': 'https://support.xmplay.com/files/12/xmp-gme.zip?v=515637', + 'XMPlay RAR': 'https://support.xmplay.com/files/16/xmp-rar.zip?v=409646', + 'XMPlay WAModern': 'https://support.xmplay.com/files/10/WAModern.zip?v=207099', + 'XMPlay': 'https://support.xmplay.com/files/20/xmplay383.zip?v=298195', + 'XYplorerFree': 'https://www.xyplorer.com/download/xyplorer_free_noinstall.zip', + } VCREDIST_SOURCES = { - '2010sp1': { - '32': 'https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe', - '64': 'https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x64.exe', - }, - '2012u4': { - '32': 'https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x86.exe', - '64': 'https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe', - }, - '2013': { - '32': 'https://download.microsoft.com/download/0/5/6/056dcda9-d667-4e27-8001-8a0c6971d6b1/vcredist_x86.exe', - '64': 'https://download.microsoft.com/download/0/5/6/056dcda9-d667-4e27-8001-8a0c6971d6b1/vcredist_x64.exe', - }, - '2017': { - '32': 'https://aka.ms/vs/15/release/vc_redist.x86.exe', - '64': 'https://aka.ms/vs/15/release/vc_redist.x64.exe', - }, - } + '2010sp1': { + '32': 'https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe', + '64': 'https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x64.exe', + }, + '2012u4': { + '32': 'https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x86.exe', + '64': 'https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe', + }, + '2013': { + '32': 'https://download.microsoft.com/download/0/5/6/056dcda9-d667-4e27-8001-8a0c6971d6b1/vcredist_x86.exe', + '64': 'https://download.microsoft.com/download/0/5/6/056dcda9-d667-4e27-8001-8a0c6971d6b1/vcredist_x64.exe', + }, + '2017': { + '32': 'https://aka.ms/vs/15/release/vc_redist.x86.exe', + '64': 'https://aka.ms/vs/15/release/vc_redist.x64.exe', + }, + } NINITE_SOURCES = { - 'Bundles': { - 'Legacy.exe': '.net4.7.2-7zip-chrome-firefox-sumatrapdf-vlc', - 'Modern.exe': '.net4.7.2-7zip-chrome-classicstart-firefox-sumatrapdf-vlc', - }, - 'Audio-Video': { - 'AIMP.exe': 'aimp', - 'Audacity.exe': 'audacity', - 'CCCP.exe': 'cccp', - 'Foobar2000.exe': 'foobar', - 'GOM.exe': 'gom', - 'HandBrake.exe': 'handbrake', - 'iTunes.exe': 'itunes', - 'K-Lite Codecs.exe': 'klitecodecs', - 'MediaMonkey.exe': 'mediamonkey', - 'MusicBee.exe': 'musicbee', - 'Spotify.exe': 'spotify', - 'VLC.exe': 'vlc', - 'Winamp.exe': 'winamp', - }, - 'Cloud Storage': { - 'Dropbox.exe': 'dropbox', - 'Google Backup & Sync.exe': 'googlebackupandsync', - 'Mozy.exe': 'mozy', - 'OneDrive.exe': 'onedrive', - 'SugarSync.exe': 'sugarsync', - }, - 'Communication': { - 'Discord': 'discord', - 'Pidgin.exe': 'pidgin', - 'Skype.exe': 'skype', - 'Trillian.exe': 'trillian', - }, - 'Compression': { - '7-Zip.exe': '7zip', - 'PeaZip.exe': 'peazip', - 'WinRAR.exe': 'winrar', - }, - 'Developer': { - 'Eclipse.exe': 'eclipse', - 'JDK 8.exe': 'jdk8', - 'JDK 8 (x64).exe': 'jdkx8', - 'Notepad++.exe': 'notepadplusplus', - 'PuTTY.exe': 'putty', - 'Python 2.exe': 'python', - 'Visual Studio Code.exe': 'vscode', - 'WinMerge.exe': 'winmerge', - 'WinSCP.exe': 'winscp', - }, - 'File Sharing': { - 'qBittorrent.exe': 'qbittorrent', - }, - 'Image-Photo': { - 'Blender.exe': 'blender', - 'FastStone.exe': 'faststone', - 'GIMP.exe': 'gimp', - 'Greenshot.exe': 'greenshot', - 'Inkscape.exe': 'inkscape', - 'IrfanView.exe': 'irfanview', - 'Krita.exe': 'krita', - 'Paint.NET.exe': 'paint.net', - 'ShareX.exe': 'sharex', - 'XnView.exe': 'xnview', - }, - 'Misc': { - 'Evernote.exe': 'evernote', - 'Everything.exe': 'everything', - 'KeePass 2.exe': 'keepass2', - 'Google Earth.exe': 'googleearth', - 'NV Access.exe': 'nvda', - 'Steam.exe': 'steam', - }, - 'Office': { - 'CutePDF.exe': 'cutepdf', - 'Foxit Reader.exe': 'foxit', - 'LibreOffice.exe': 'libreoffice', - 'OpenOffice.exe': 'openoffice', - 'PDFCreator.exe': 'pdfcreator', - 'SumatraPDF.exe': 'sumatrapdf', - 'Thunderbird.exe': 'thunderbird', - }, - 'Runtimes': { - 'Adobe Air.exe': 'air', - 'dotNET.exe': '.net4.7.2', - 'Java 8.exe': 'java8', - 'Shockwave.exe': 'shockwave', - 'Silverlight.exe': 'silverlight', - }, - 'Security': { - 'Avast.exe': 'avast', - 'AVG.exe': 'avg', - 'Avira.exe': 'avira', - 'Microsoft Security Essentials.exe': 'essentials', - 'Malwarebytes Anti-Malware.exe': 'malwarebytes', - 'Spybot 2.exe': 'spybot2', - 'SUPERAntiSpyware.exe': 'super', - }, - 'Utilities': { - 'CDBurnerXP.exe': 'cdburnerxp', - 'Classic Start.exe': 'classicstart', - 'Glary Utilities.exe': 'glary', - 'ImgBurn.exe': 'imgburn', - 'InfraRecorder.exe': 'infrarecorder', - 'Launchy.exe': 'launchy', - 'RealVNC.exe': 'realvnc', - 'Revo Uninstaller.exe': 'revo', - 'TeamViewer 13.exe': 'teamviewer13', - 'TeraCopy.exe': 'teracopy', - 'WinDirStat.exe': 'windirstat', - }, - 'Web Browsers': { - 'Google Chrome.exe': 'chrome', - 'Mozilla Firefox.exe': 'firefox', - 'Opera Chromium.exe': 'operaChromium', - }, - } + 'Bundles': { + 'Legacy.exe': '.net4.7.2-7zip-chrome-firefox-sumatrapdf-vlc', + 'Modern.exe': '.net4.7.2-7zip-chrome-classicstart-firefox-sumatrapdf-vlc', + }, + 'Audio-Video': { + 'AIMP.exe': 'aimp', + 'Audacity.exe': 'audacity', + 'CCCP.exe': 'cccp', + 'Foobar2000.exe': 'foobar', + 'GOM.exe': 'gom', + 'HandBrake.exe': 'handbrake', + 'iTunes.exe': 'itunes', + 'K-Lite Codecs.exe': 'klitecodecs', + 'MediaMonkey.exe': 'mediamonkey', + 'MusicBee.exe': 'musicbee', + 'Spotify.exe': 'spotify', + 'VLC.exe': 'vlc', + 'Winamp.exe': 'winamp', + }, + 'Cloud Storage': { + 'Dropbox.exe': 'dropbox', + 'Google Backup & Sync.exe': 'googlebackupandsync', + 'Mozy.exe': 'mozy', + 'OneDrive.exe': 'onedrive', + 'SugarSync.exe': 'sugarsync', + }, + 'Communication': { + 'Discord': 'discord', + 'Pidgin.exe': 'pidgin', + 'Skype.exe': 'skype', + 'Trillian.exe': 'trillian', + }, + 'Compression': { + '7-Zip.exe': '7zip', + 'PeaZip.exe': 'peazip', + 'WinRAR.exe': 'winrar', + }, + 'Developer': { + 'Eclipse.exe': 'eclipse', + 'JDK 8.exe': 'jdk8', + 'JDK 8 (x64).exe': 'jdkx8', + 'Notepad++.exe': 'notepadplusplus', + 'PuTTY.exe': 'putty', + 'Python 2.exe': 'python', + 'Visual Studio Code.exe': 'vscode', + 'WinMerge.exe': 'winmerge', + 'WinSCP.exe': 'winscp', + }, + 'File Sharing': { + 'qBittorrent.exe': 'qbittorrent', + }, + 'Image-Photo': { + 'Blender.exe': 'blender', + 'FastStone.exe': 'faststone', + 'GIMP.exe': 'gimp', + 'Greenshot.exe': 'greenshot', + 'Inkscape.exe': 'inkscape', + 'IrfanView.exe': 'irfanview', + 'Krita.exe': 'krita', + 'Paint.NET.exe': 'paint.net', + 'ShareX.exe': 'sharex', + 'XnView.exe': 'xnview', + }, + 'Misc': { + 'Evernote.exe': 'evernote', + 'Everything.exe': 'everything', + 'KeePass 2.exe': 'keepass2', + 'Google Earth.exe': 'googleearth', + 'NV Access.exe': 'nvda', + 'Steam.exe': 'steam', + }, + 'Office': { + 'CutePDF.exe': 'cutepdf', + 'Foxit Reader.exe': 'foxit', + 'LibreOffice.exe': 'libreoffice', + 'OpenOffice.exe': 'openoffice', + 'PDFCreator.exe': 'pdfcreator', + 'SumatraPDF.exe': 'sumatrapdf', + 'Thunderbird.exe': 'thunderbird', + }, + 'Runtimes': { + 'Adobe Air.exe': 'air', + 'dotNET.exe': '.net4.7.2', + 'Java 8.exe': 'java8', + 'Shockwave.exe': 'shockwave', + 'Silverlight.exe': 'silverlight', + }, + 'Security': { + 'Avast.exe': 'avast', + 'AVG.exe': 'avg', + 'Avira.exe': 'avira', + 'Microsoft Security Essentials.exe': 'essentials', + 'Malwarebytes Anti-Malware.exe': 'malwarebytes', + 'Spybot 2.exe': 'spybot2', + 'SUPERAntiSpyware.exe': 'super', + }, + 'Utilities': { + 'CDBurnerXP.exe': 'cdburnerxp', + 'Classic Start.exe': 'classicstart', + 'Glary Utilities.exe': 'glary', + 'ImgBurn.exe': 'imgburn', + 'InfraRecorder.exe': 'infrarecorder', + 'Launchy.exe': 'launchy', + 'RealVNC.exe': 'realvnc', + 'Revo Uninstaller.exe': 'revo', + 'TeamViewer 14.exe': 'teamviewer14', + 'TeraCopy.exe': 'teracopy', + 'WinDirStat.exe': 'windirstat', + }, + 'Web Browsers': { + 'Google Chrome.exe': 'chrome', + 'Mozilla Firefox.exe': 'firefox', + 'Opera Chromium.exe': 'operaChromium', + }, + } RST_SOURCES = { - #SetupRST_12.0.exe : Removed from download center? - #SetupRST_12.5.exe : Removed from download center? - #SetupRST_12.8.exe : Removed from download center? - 'SetupRST_12.9.exe': 'https://downloadmirror.intel.com/23496/eng/SetupRST.exe', - #SetupRST_13.x.exe : Broken, doesn't support > .NET 4.5 - 'SetupRST_14.0.exe': 'https://downloadmirror.intel.com/25091/eng/SetupRST.exe', - 'SetupRST_14.8.exe': 'https://downloadmirror.intel.com/26759/eng/setuprst.exe', - 'SetupRST_15.8.exe': 'https://downloadmirror.intel.com/27442/eng/SetupRST.exe', - 'SetupRST_15.9.exe': 'https://downloadmirror.intel.com/27400/eng/SetupRST.exe', - 'SetupRST_16.0.exe': 'https://downloadmirror.intel.com/27681/eng/SetupRST.exe', - 'SetupRST_16.5.exe': 'https://downloadmirror.intel.com/27984/eng/SetupRST.exe', - } + #SetupRST_12.0.exe : Removed from download center? + #SetupRST_12.5.exe : Removed from download center? + #SetupRST_12.8.exe : Removed from download center? + 'SetupRST_12.9.exe': 'https://downloadmirror.intel.com/23496/eng/SetupRST.exe', + #SetupRST_13.x.exe : Broken, doesn't support > .NET 4.5 + 'SetupRST_14.0.exe': 'https://downloadmirror.intel.com/25091/eng/SetupRST.exe', + 'SetupRST_14.8.exe': 'https://downloadmirror.intel.com/26759/eng/setuprst.exe', + 'SetupRST_15.8.exe': 'https://downloadmirror.intel.com/27442/eng/SetupRST.exe', + #SetupRST_15.9.exe : Deprecated by Intel + #SetupRST_16.0.exe : Deprecated by Intel + #SetupRST_16.5.exe : Deprecated by Intel + #SetupRST_16.7.exe : Deprecated by Intel + 'SetupRST_16.8.exe': 'https://downloadmirror.intel.com/28400/eng/SetupRST.exe', + } if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") -# vim: sts=4 sw=4 ts=4 tw=0 nowrap +# vim: sts=2 sw=2 ts=2 tw=0 diff --git a/.bin/Scripts/settings/tools.py b/.bin/Scripts/settings/tools.py index b470603c..0ee74dec 100644 --- a/.bin/Scripts/settings/tools.py +++ b/.bin/Scripts/settings/tools.py @@ -1,56 +1,58 @@ # Wizard Kit: Settings - Tools TOOLS = { - # NOTE: BinDir will be prepended to these paths at runtime - 'AIDA64': { - '32': r'AIDA64\aida64.exe'}, - 'AutoRuns': { - '32': r'Autoruns\autoruns.exe', - '64': r'Autoruns\autoruns64.exe'}, - 'BleachBit': { - '32': r'BleachBit\bleachbit_console.exe'}, - 'Caffeine': { - '32': r'Caffeine\caffeine.exe'}, - 'Du': { - '32': r'Du\du.exe', - '64': r'Du\du64.exe'}, - 'ERUNT': { - '32': r'ERUNT\ERUNT.EXE'}, - 'Everything': { - '32': r'Everything\Everything.exe', - '64': r'Everything\Everything64.exe'}, - 'FastCopy': { - '32': r'FastCopy\FastCopy.exe', - '64': r'FastCopy\FastCopy64.exe'}, - 'HitmanPro': { - '32': r'HitmanPro\HitmanPro.exe', - '64': r'HitmanPro\HitmanPro64.exe'}, - 'HWiNFO': { - '32': r'HWiNFO\HWiNFO.exe', - '64': r'HWiNFO\HWiNFO64.exe'}, - 'KVRT': { - '32': r'KVRT\KVRT.exe'}, - 'NirCmd': { - '32': r'NirCmd\nircmdc.exe', - '64': r'NirCmd\nircmdc64.exe'}, - 'NotepadPlusPlus': { - '32': r'NotepadPlusPlus\notepadplusplus.exe'}, - 'ProduKey': { - '32': r'ProduKey\ProduKey.exe', - '64': r'ProduKey\ProduKey64.exe'}, - 'RKill': { - '32': r'RKill\RKill.exe'}, - 'SevenZip': { - '32': r'7-Zip\7za.exe', - '64': r'7-Zip\7za64.exe'}, - 'TDSSKiller': { - '32': r'TDSSKiller\TDSSKiller.exe'}, - 'wimlib-imagex': { - '32': r'wimlib\x32\wimlib-imagex.exe', - '64': r'wimlib\x64\wimlib-imagex.exe'}, - 'XMPlay': { - '32': r'XMPlay\xmplay.exe'}, - } + # NOTE: BinDir will be prepended to these paths at runtime + 'AIDA64': { + '32': r'AIDA64\aida64.exe'}, + 'AutoRuns': { + '32': r'Autoruns\autoruns.exe', + '64': r'Autoruns\autoruns64.exe'}, + 'BleachBit': { + '32': r'BleachBit\bleachbit_console.exe'}, + 'Caffeine': { + '32': r'Caffeine\caffeine.exe'}, + 'Du': { + '32': r'Du\du.exe', + '64': r'Du\du64.exe'}, + 'ERUNT': { + '32': r'ERUNT\ERUNT.EXE'}, + 'Everything': { + '32': r'Everything\Everything.exe', + '64': r'Everything\Everything64.exe'}, + 'FastCopy': { + '32': r'FastCopy\FastCopy.exe', + '64': r'FastCopy\FastCopy64.exe'}, + 'HitmanPro': { + '32': r'HitmanPro\HitmanPro.exe', + '64': r'HitmanPro\HitmanPro64.exe'}, + 'HWiNFO': { + '32': r'HWiNFO\HWiNFO.exe', + '64': r'HWiNFO\HWiNFO64.exe'}, + 'KVRT': { + '32': r'KVRT\KVRT.exe'}, + 'NirCmd': { + '32': r'NirCmd\nircmdc.exe', + '64': r'NirCmd\nircmdc64.exe'}, + 'NotepadPlusPlus': { + '32': r'NotepadPlusPlus\notepadplusplus.exe'}, + 'ProduKey': { + '32': r'ProduKey\ProduKey.exe', + '64': r'ProduKey\ProduKey64.exe'}, + 'RKill': { + '32': r'RKill\RKill.exe'}, + 'SevenZip': { + '32': r'7-Zip\7za.exe', + '64': r'7-Zip\7za64.exe'}, + 'TDSSKiller': { + '32': r'TDSSKiller\TDSSKiller.exe'}, + 'wimlib-imagex': { + '32': r'wimlib\x32\wimlib-imagex.exe', + '64': r'wimlib\x64\wimlib-imagex.exe'}, + 'XMPlay': { + '32': r'XMPlay\xmplay.exe'}, + } if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/settings/windows_builds.py b/.bin/Scripts/settings/windows_builds.py index 2db18b6f..216be21f 100644 --- a/.bin/Scripts/settings/windows_builds.py +++ b/.bin/Scripts/settings/windows_builds.py @@ -1,185 +1,195 @@ # Wizard Kit: Settings - Windows Builds WINDOWS_BUILDS = { -# Build Version Release Codename Marketing Name Notes - '6000': ( 'Vista', 'RTM', 'Longhorn', None, 'unsupported'), - '6000': ( 'Vista', 'RTM', 'Longhorn', None, 'unsupported'), - '6001': ( 'Vista', 'SP1', 'Longhorn', None, 'unsupported'), - '6002': ( 'Vista', 'SP2', 'Longhorn', None, 'unsupported'), + # Build, Version, Release, Codename, Marketing Name, Notes + '6000': ('Vista', 'RTM', 'Longhorn', None, 'unsupported'), + '6000': ('Vista', 'RTM', 'Longhorn', None, 'unsupported'), + '6001': ('Vista', 'SP1', 'Longhorn', None, 'unsupported'), + '6002': ('Vista', 'SP2', 'Longhorn', None, 'unsupported'), - '7600': ( '7', 'RTM', 'Vienna', None, 'unsupported'), - '7601': ( '7', 'SP1', 'Vienna', None, 'outdated'), + '7600': ('7', 'RTM', 'Vienna', None, 'unsupported'), + '7601': ('7', 'SP1', 'Vienna', None, 'outdated'), - #9199 is a fake build since Win 8 is 6.2.9200 but that collides with Win 8.1 (6.3.9200) - '9199': ( '8', 'RTM', None, None, 'unsupported'), + #9199 is a fake build since Win 8 is 6.2.9200 but that collides with Win 8.1 (6.3.9200) + '9199': ('8', 'RTM', None, None, 'unsupported'), - '9200': ( '8.1', None, 'Blue', None, 'outdated'), - '9600': ( '8.1', None, 'Update', None, None), + '9200': ('8.1', None, 'Blue', None, 'outdated'), + '9600': ('8.1', None, 'Update', None, None), - '9841': ( '10', None, 'Threshold 1', None, 'preview build'), - '9860': ( '10', None, 'Threshold 1', None, 'preview build'), - '9879': ( '10', None, 'Threshold 1', None, 'preview build'), - '9926': ( '10', None, 'Threshold 1', None, 'preview build'), - '10041': ( '10', None, 'Threshold 1', None, 'preview build'), - '10049': ( '10', None, 'Threshold 1', None, 'preview build'), - '10061': ( '10', None, 'Threshold 1', None, 'preview build'), - '10074': ( '10', None, 'Threshold 1', None, 'preview build'), - '10122': ( '10', None, 'Threshold 1', None, 'preview build'), - '10130': ( '10', None, 'Threshold 1', None, 'preview build'), - '10158': ( '10', None, 'Threshold 1', None, 'preview build'), - '10159': ( '10', None, 'Threshold 1', None, 'preview build'), - '10162': ( '10', None, 'Threshold 1', None, 'preview build'), - '10166': ( '10', None, 'Threshold 1', None, 'preview build'), - '10240': ( '10', 'v1507', 'Threshold 1', None, 'unsupported'), - '10525': ( '10', None, 'Threshold 2', None, 'preview build'), - '10532': ( '10', None, 'Threshold 2', None, 'preview build'), - '10547': ( '10', None, 'Threshold 2', None, 'preview build'), - '10565': ( '10', None, 'Threshold 2', None, 'preview build'), - '10576': ( '10', None, 'Threshold 2', None, 'preview build'), - '10586': ( '10', 'v1511', 'Threshold 2', 'November Update', 'unsupported'), - '11082': ( '10', None, 'Redstone 1', None, 'preview build'), - '11099': ( '10', None, 'Redstone 1', None, 'preview build'), - '11102': ( '10', None, 'Redstone 1', None, 'preview build'), - '14251': ( '10', None, 'Redstone 1', None, 'preview build'), - '14257': ( '10', None, 'Redstone 1', None, 'preview build'), - '14271': ( '10', None, 'Redstone 1', None, 'preview build'), - '14279': ( '10', None, 'Redstone 1', None, 'preview build'), - '14291': ( '10', None, 'Redstone 1', None, 'preview build'), - '14295': ( '10', None, 'Redstone 1', None, 'preview build'), - '14316': ( '10', None, 'Redstone 1', None, 'preview build'), - '14328': ( '10', None, 'Redstone 1', None, 'preview build'), - '14332': ( '10', None, 'Redstone 1', None, 'preview build'), - '14342': ( '10', None, 'Redstone 1', None, 'preview build'), - '14352': ( '10', None, 'Redstone 1', None, 'preview build'), - '14361': ( '10', None, 'Redstone 1', None, 'preview build'), - '14366': ( '10', None, 'Redstone 1', None, 'preview build'), - '14367': ( '10', None, 'Redstone 1', None, 'preview build'), - '14371': ( '10', None, 'Redstone 1', None, 'preview build'), - '14372': ( '10', None, 'Redstone 1', None, 'preview build'), - '14376': ( '10', None, 'Redstone 1', None, 'preview build'), - '14379': ( '10', None, 'Redstone 1', None, 'preview build'), - '14383': ( '10', None, 'Redstone 1', None, 'preview build'), - '14385': ( '10', None, 'Redstone 1', None, 'preview build'), - '14388': ( '10', None, 'Redstone 1', None, 'preview build'), - '14390': ( '10', None, 'Redstone 1', None, 'preview build'), - '14393': ( '10', 'v1607', 'Redstone 1', 'Anniversary Update', 'unsupported'), - '14901': ( '10', None, 'Redstone 2', None, 'preview build'), - '14905': ( '10', None, 'Redstone 2', None, 'preview build'), - '14915': ( '10', None, 'Redstone 2', None, 'preview build'), - '14926': ( '10', None, 'Redstone 2', None, 'preview build'), - '14931': ( '10', None, 'Redstone 2', None, 'preview build'), - '14936': ( '10', None, 'Redstone 2', None, 'preview build'), - '14942': ( '10', None, 'Redstone 2', None, 'preview build'), - '14946': ( '10', None, 'Redstone 2', None, 'preview build'), - '14951': ( '10', None, 'Redstone 2', None, 'preview build'), - '14955': ( '10', None, 'Redstone 2', None, 'preview build'), - '14959': ( '10', None, 'Redstone 2', None, 'preview build'), - '14965': ( '10', None, 'Redstone 2', None, 'preview build'), - '14971': ( '10', None, 'Redstone 2', None, 'preview build'), - '14986': ( '10', None, 'Redstone 2', None, 'preview build'), - '15002': ( '10', None, 'Redstone 2', None, 'preview build'), - '15007': ( '10', None, 'Redstone 2', None, 'preview build'), - '15014': ( '10', None, 'Redstone 2', None, 'preview build'), - '15019': ( '10', None, 'Redstone 2', None, 'preview build'), - '15025': ( '10', None, 'Redstone 2', None, 'preview build'), - '15031': ( '10', None, 'Redstone 2', None, 'preview build'), - '15042': ( '10', None, 'Redstone 2', None, 'preview build'), - '15046': ( '10', None, 'Redstone 2', None, 'preview build'), - '15048': ( '10', None, 'Redstone 2', None, 'preview build'), - '15055': ( '10', None, 'Redstone 2', None, 'preview build'), - '15058': ( '10', None, 'Redstone 2', None, 'preview build'), - '15060': ( '10', None, 'Redstone 2', None, 'preview build'), - '15061': ( '10', None, 'Redstone 2', None, 'preview build'), - '15063': ( '10', 'v1703', 'Redstone 2', 'Creators Update', 'outdated'), - '16170': ( '10', None, 'Redstone 3', None, 'preview build'), - '16176': ( '10', None, 'Redstone 3', None, 'preview build'), - '16179': ( '10', None, 'Redstone 3', None, 'preview build'), - '16184': ( '10', None, 'Redstone 3', None, 'preview build'), - '16188': ( '10', None, 'Redstone 3', None, 'preview build'), - '16193': ( '10', None, 'Redstone 3', None, 'preview build'), - '16199': ( '10', None, 'Redstone 3', None, 'preview build'), - '16212': ( '10', None, 'Redstone 3', None, 'preview build'), - '16215': ( '10', None, 'Redstone 3', None, 'preview build'), - '16226': ( '10', None, 'Redstone 3', None, 'preview build'), - '16232': ( '10', None, 'Redstone 3', None, 'preview build'), - '16237': ( '10', None, 'Redstone 3', None, 'preview build'), - '16241': ( '10', None, 'Redstone 3', None, 'preview build'), - '16251': ( '10', None, 'Redstone 3', None, 'preview build'), - '16257': ( '10', None, 'Redstone 3', None, 'preview build'), - '16273': ( '10', None, 'Redstone 3', None, 'preview build'), - '16275': ( '10', None, 'Redstone 3', None, 'preview build'), - '16278': ( '10', None, 'Redstone 3', None, 'preview build'), - '16281': ( '10', None, 'Redstone 3', None, 'preview build'), - '16288': ( '10', None, 'Redstone 3', None, 'preview build'), - '16291': ( '10', None, 'Redstone 3', None, 'preview build'), - '16294': ( '10', None, 'Redstone 3', None, 'preview build'), - '16296': ( '10', None, 'Redstone 3', None, 'preview build'), - '16299': ( '10', 'v1709', 'Redstone 3', 'Fall Creators Update', 'outdated'), - '16353': ( '10', None, 'Redstone 4', None, 'preview build'), - '16362': ( '10', None, 'Redstone 4', None, 'preview build'), - '17004': ( '10', None, 'Redstone 4', None, 'preview build'), - '17017': ( '10', None, 'Redstone 4', None, 'preview build'), - '17025': ( '10', None, 'Redstone 4', None, 'preview build'), - '17035': ( '10', None, 'Redstone 4', None, 'preview build'), - '17040': ( '10', None, 'Redstone 4', None, 'preview build'), - '17046': ( '10', None, 'Redstone 4', None, 'preview build'), - '17063': ( '10', None, 'Redstone 4', None, 'preview build'), - '17074': ( '10', None, 'Redstone 4', None, 'preview build'), - '17083': ( '10', None, 'Redstone 4', None, 'preview build'), - '17093': ( '10', None, 'Redstone 4', None, 'preview build'), - '17101': ( '10', None, 'Redstone 4', None, 'preview build'), - '17107': ( '10', None, 'Redstone 4', None, 'preview build'), - '17110': ( '10', None, 'Redstone 4', None, 'preview build'), - '17112': ( '10', None, 'Redstone 4', None, 'preview build'), - '17115': ( '10', None, 'Redstone 4', None, 'preview build'), - '17120': ( '10', None, 'Redstone 4', None, 'preview build'), - '17123': ( '10', None, 'Redstone 4', None, 'preview build'), - '17127': ( '10', None, 'Redstone 4', None, 'preview build'), - '17128': ( '10', None, 'Redstone 4', None, 'preview build'), - '17133': ( '10', None, 'Redstone 4', None, 'preview build'), - '17134': ( '10', 'v1803', 'Redstone 4', 'April 2018 Update', None), - '17604': ( '10', None, 'Redstone 5', None, 'preview build'), - '17618': ( '10', None, 'Redstone 5', None, 'preview build'), - '17623': ( '10', None, 'Redstone 5', None, 'preview build'), - '17627': ( '10', None, 'Redstone 5', None, 'preview build'), - '17634': ( '10', None, 'Redstone 5', None, 'preview build'), - '17639': ( '10', None, 'Redstone 5', None, 'preview build'), - '17643': ( '10', None, 'Redstone 5', None, 'preview build'), - '17650': ( '10', None, 'Redstone 5', None, 'preview build'), - '17655': ( '10', None, 'Redstone 5', None, 'preview build'), - '17661': ( '10', None, 'Redstone 5', None, 'preview build'), - '17666': ( '10', None, 'Redstone 5', None, 'preview build'), - '17677': ( '10', None, 'Redstone 5', None, 'preview build'), - '17682': ( '10', None, 'Redstone 5', None, 'preview build'), - '17686': ( '10', None, 'Redstone 5', None, 'preview build'), - '17692': ( '10', None, 'Redstone 5', None, 'preview build'), - '17704': ( '10', None, 'Redstone 5', None, 'preview build'), - '17711': ( '10', None, 'Redstone 5', None, 'preview build'), - '17713': ( '10', None, 'Redstone 5', None, 'preview build'), - '17723': ( '10', None, 'Redstone 5', None, 'preview build'), - '17728': ( '10', None, 'Redstone 5', None, 'preview build'), - '17730': ( '10', None, 'Redstone 5', None, 'preview build'), - '17733': ( '10', None, 'Redstone 5', None, 'preview build'), - '17735': ( '10', None, 'Redstone 5', None, 'preview build'), - '17738': ( '10', None, 'Redstone 5', None, 'preview build'), - '17741': ( '10', None, 'Redstone 5', None, 'preview build'), - '17744': ( '10', None, 'Redstone 5', None, 'preview build'), - '17746': ( '10', None, 'Redstone 5', None, 'preview build'), - '17751': ( '10', None, 'Redstone 5', None, 'preview build'), - '17754': ( '10', None, 'Redstone 5', None, 'preview build'), - '17755': ( '10', None, 'Redstone 5', None, 'preview build'), - '17758': ( '10', None, 'Redstone 5', None, 'preview build'), - '17760': ( '10', None, 'Redstone 5', None, 'preview build'), - '17763': ( '10', 'v1809', 'Redstone 5', 'October 2018 Update', 'preview build'), - '18204': ( '10', None, '19H1', None, 'preview build'), - '18214': ( '10', None, '19H1', None, 'preview build'), - '18219': ( '10', None, '19H1', None, 'preview build'), - '18234': ( '10', None, '19H1', None, 'preview build'), - '18237': ( '10', None, '19H1', None, 'preview build'), - '18242': ( '10', None, '19H1', None, 'preview build'), - '18247': ( '10', None, '19H1', None, 'preview build'), - '18252': ( '10', None, '19H1', None, 'preview build'), + '9841': ('10', None, 'Threshold 1', None, 'preview build'), + '9860': ('10', None, 'Threshold 1', None, 'preview build'), + '9879': ('10', None, 'Threshold 1', None, 'preview build'), + '9926': ('10', None, 'Threshold 1', None, 'preview build'), + '10041': ('10', None, 'Threshold 1', None, 'preview build'), + '10049': ('10', None, 'Threshold 1', None, 'preview build'), + '10061': ('10', None, 'Threshold 1', None, 'preview build'), + '10074': ('10', None, 'Threshold 1', None, 'preview build'), + '10122': ('10', None, 'Threshold 1', None, 'preview build'), + '10130': ('10', None, 'Threshold 1', None, 'preview build'), + '10158': ('10', None, 'Threshold 1', None, 'preview build'), + '10159': ('10', None, 'Threshold 1', None, 'preview build'), + '10162': ('10', None, 'Threshold 1', None, 'preview build'), + '10166': ('10', None, 'Threshold 1', None, 'preview build'), + '10240': ('10', 'v1507', 'Threshold 1', None, 'unsupported'), + '10525': ('10', None, 'Threshold 2', None, 'preview build'), + '10532': ('10', None, 'Threshold 2', None, 'preview build'), + '10547': ('10', None, 'Threshold 2', None, 'preview build'), + '10565': ('10', None, 'Threshold 2', None, 'preview build'), + '10576': ('10', None, 'Threshold 2', None, 'preview build'), + '10586': ('10', 'v1511', 'Threshold 2', 'November Update', 'unsupported'), + '11082': ('10', None, 'Redstone 1', None, 'preview build'), + '11099': ('10', None, 'Redstone 1', None, 'preview build'), + '11102': ('10', None, 'Redstone 1', None, 'preview build'), + '14251': ('10', None, 'Redstone 1', None, 'preview build'), + '14257': ('10', None, 'Redstone 1', None, 'preview build'), + '14271': ('10', None, 'Redstone 1', None, 'preview build'), + '14279': ('10', None, 'Redstone 1', None, 'preview build'), + '14291': ('10', None, 'Redstone 1', None, 'preview build'), + '14295': ('10', None, 'Redstone 1', None, 'preview build'), + '14316': ('10', None, 'Redstone 1', None, 'preview build'), + '14328': ('10', None, 'Redstone 1', None, 'preview build'), + '14332': ('10', None, 'Redstone 1', None, 'preview build'), + '14342': ('10', None, 'Redstone 1', None, 'preview build'), + '14352': ('10', None, 'Redstone 1', None, 'preview build'), + '14361': ('10', None, 'Redstone 1', None, 'preview build'), + '14366': ('10', None, 'Redstone 1', None, 'preview build'), + '14367': ('10', None, 'Redstone 1', None, 'preview build'), + '14371': ('10', None, 'Redstone 1', None, 'preview build'), + '14372': ('10', None, 'Redstone 1', None, 'preview build'), + '14376': ('10', None, 'Redstone 1', None, 'preview build'), + '14379': ('10', None, 'Redstone 1', None, 'preview build'), + '14383': ('10', None, 'Redstone 1', None, 'preview build'), + '14385': ('10', None, 'Redstone 1', None, 'preview build'), + '14388': ('10', None, 'Redstone 1', None, 'preview build'), + '14390': ('10', None, 'Redstone 1', None, 'preview build'), + '14393': ('10', 'v1607', 'Redstone 1', 'Anniversary Update', 'unsupported'), + '14901': ('10', None, 'Redstone 2', None, 'preview build'), + '14905': ('10', None, 'Redstone 2', None, 'preview build'), + '14915': ('10', None, 'Redstone 2', None, 'preview build'), + '14926': ('10', None, 'Redstone 2', None, 'preview build'), + '14931': ('10', None, 'Redstone 2', None, 'preview build'), + '14936': ('10', None, 'Redstone 2', None, 'preview build'), + '14942': ('10', None, 'Redstone 2', None, 'preview build'), + '14946': ('10', None, 'Redstone 2', None, 'preview build'), + '14951': ('10', None, 'Redstone 2', None, 'preview build'), + '14955': ('10', None, 'Redstone 2', None, 'preview build'), + '14959': ('10', None, 'Redstone 2', None, 'preview build'), + '14965': ('10', None, 'Redstone 2', None, 'preview build'), + '14971': ('10', None, 'Redstone 2', None, 'preview build'), + '14986': ('10', None, 'Redstone 2', None, 'preview build'), + '15002': ('10', None, 'Redstone 2', None, 'preview build'), + '15007': ('10', None, 'Redstone 2', None, 'preview build'), + '15014': ('10', None, 'Redstone 2', None, 'preview build'), + '15019': ('10', None, 'Redstone 2', None, 'preview build'), + '15025': ('10', None, 'Redstone 2', None, 'preview build'), + '15031': ('10', None, 'Redstone 2', None, 'preview build'), + '15042': ('10', None, 'Redstone 2', None, 'preview build'), + '15046': ('10', None, 'Redstone 2', None, 'preview build'), + '15048': ('10', None, 'Redstone 2', None, 'preview build'), + '15055': ('10', None, 'Redstone 2', None, 'preview build'), + '15058': ('10', None, 'Redstone 2', None, 'preview build'), + '15060': ('10', None, 'Redstone 2', None, 'preview build'), + '15061': ('10', None, 'Redstone 2', None, 'preview build'), + '15063': ('10', 'v1703', 'Redstone 2', 'Creators Update', 'unsupported'), + '16170': ('10', None, 'Redstone 3', None, 'preview build'), + '16176': ('10', None, 'Redstone 3', None, 'preview build'), + '16179': ('10', None, 'Redstone 3', None, 'preview build'), + '16184': ('10', None, 'Redstone 3', None, 'preview build'), + '16188': ('10', None, 'Redstone 3', None, 'preview build'), + '16193': ('10', None, 'Redstone 3', None, 'preview build'), + '16199': ('10', None, 'Redstone 3', None, 'preview build'), + '16212': ('10', None, 'Redstone 3', None, 'preview build'), + '16215': ('10', None, 'Redstone 3', None, 'preview build'), + '16226': ('10', None, 'Redstone 3', None, 'preview build'), + '16232': ('10', None, 'Redstone 3', None, 'preview build'), + '16237': ('10', None, 'Redstone 3', None, 'preview build'), + '16241': ('10', None, 'Redstone 3', None, 'preview build'), + '16251': ('10', None, 'Redstone 3', None, 'preview build'), + '16257': ('10', None, 'Redstone 3', None, 'preview build'), + '16273': ('10', None, 'Redstone 3', None, 'preview build'), + '16275': ('10', None, 'Redstone 3', None, 'preview build'), + '16278': ('10', None, 'Redstone 3', None, 'preview build'), + '16281': ('10', None, 'Redstone 3', None, 'preview build'), + '16288': ('10', None, 'Redstone 3', None, 'preview build'), + '16291': ('10', None, 'Redstone 3', None, 'preview build'), + '16294': ('10', None, 'Redstone 3', None, 'preview build'), + '16296': ('10', None, 'Redstone 3', None, 'preview build'), + '16299': ('10', 'v1709', 'Redstone 3', 'Fall Creators Update', 'outdated'), + '16353': ('10', None, 'Redstone 4', None, 'preview build'), + '16362': ('10', None, 'Redstone 4', None, 'preview build'), + '17004': ('10', None, 'Redstone 4', None, 'preview build'), + '17017': ('10', None, 'Redstone 4', None, 'preview build'), + '17025': ('10', None, 'Redstone 4', None, 'preview build'), + '17035': ('10', None, 'Redstone 4', None, 'preview build'), + '17040': ('10', None, 'Redstone 4', None, 'preview build'), + '17046': ('10', None, 'Redstone 4', None, 'preview build'), + '17063': ('10', None, 'Redstone 4', None, 'preview build'), + '17074': ('10', None, 'Redstone 4', None, 'preview build'), + '17083': ('10', None, 'Redstone 4', None, 'preview build'), + '17093': ('10', None, 'Redstone 4', None, 'preview build'), + '17101': ('10', None, 'Redstone 4', None, 'preview build'), + '17107': ('10', None, 'Redstone 4', None, 'preview build'), + '17110': ('10', None, 'Redstone 4', None, 'preview build'), + '17112': ('10', None, 'Redstone 4', None, 'preview build'), + '17115': ('10', None, 'Redstone 4', None, 'preview build'), + '17120': ('10', None, 'Redstone 4', None, 'preview build'), + '17123': ('10', None, 'Redstone 4', None, 'preview build'), + '17127': ('10', None, 'Redstone 4', None, 'preview build'), + '17128': ('10', None, 'Redstone 4', None, 'preview build'), + '17133': ('10', None, 'Redstone 4', None, 'preview build'), + '17134': ('10', 'v1803', 'Redstone 4', 'April 2018 Update', 'outdated'), + '17604': ('10', None, 'Redstone 5', None, 'preview build'), + '17618': ('10', None, 'Redstone 5', None, 'preview build'), + '17623': ('10', None, 'Redstone 5', None, 'preview build'), + '17627': ('10', None, 'Redstone 5', None, 'preview build'), + '17634': ('10', None, 'Redstone 5', None, 'preview build'), + '17639': ('10', None, 'Redstone 5', None, 'preview build'), + '17643': ('10', None, 'Redstone 5', None, 'preview build'), + '17650': ('10', None, 'Redstone 5', None, 'preview build'), + '17655': ('10', None, 'Redstone 5', None, 'preview build'), + '17661': ('10', None, 'Redstone 5', None, 'preview build'), + '17666': ('10', None, 'Redstone 5', None, 'preview build'), + '17677': ('10', None, 'Redstone 5', None, 'preview build'), + '17682': ('10', None, 'Redstone 5', None, 'preview build'), + '17686': ('10', None, 'Redstone 5', None, 'preview build'), + '17692': ('10', None, 'Redstone 5', None, 'preview build'), + '17704': ('10', None, 'Redstone 5', None, 'preview build'), + '17711': ('10', None, 'Redstone 5', None, 'preview build'), + '17713': ('10', None, 'Redstone 5', None, 'preview build'), + '17723': ('10', None, 'Redstone 5', None, 'preview build'), + '17728': ('10', None, 'Redstone 5', None, 'preview build'), + '17730': ('10', None, 'Redstone 5', None, 'preview build'), + '17733': ('10', None, 'Redstone 5', None, 'preview build'), + '17735': ('10', None, 'Redstone 5', None, 'preview build'), + '17738': ('10', None, 'Redstone 5', None, 'preview build'), + '17741': ('10', None, 'Redstone 5', None, 'preview build'), + '17744': ('10', None, 'Redstone 5', None, 'preview build'), + '17746': ('10', None, 'Redstone 5', None, 'preview build'), + '17751': ('10', None, 'Redstone 5', None, 'preview build'), + '17754': ('10', None, 'Redstone 5', None, 'preview build'), + '17755': ('10', None, 'Redstone 5', None, 'preview build'), + '17758': ('10', None, 'Redstone 5', None, 'preview build'), + '17760': ('10', None, 'Redstone 5', None, 'preview build'), + '17763': ('10', 'v1809', 'Redstone 5', 'October 2018 Update', None), + '18204': ('10', None, '19H1', None, 'preview build'), + '18214': ('10', None, '19H1', None, 'preview build'), + '18219': ('10', None, '19H1', None, 'preview build'), + '18234': ('10', None, '19H1', None, 'preview build'), + '18237': ('10', None, '19H1', None, 'preview build'), + '18242': ('10', None, '19H1', None, 'preview build'), + '18247': ('10', None, '19H1', None, 'preview build'), + '18252': ('10', None, '19H1', None, 'preview build'), + '18262': ('10', None, '19H1', None, 'preview build'), + '18267': ('10', None, '19H1', None, 'preview build'), + '18272': ('10', None, '19H1', None, 'preview build'), + '18277': ('10', None, '19H1', None, 'preview build'), + '18282': ('10', None, '19H1', None, 'preview build'), + '18290': ('10', None, '19H1', None, 'preview build'), + '18298': ('10', None, '19H1', None, 'preview build'), + '18305': ('10', None, '19H1', None, 'preview build'), } if __name__ == '__main__': - print("This file is not meant to be called directly.") + print("This file is not meant to be called directly.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/sfc_scan.py b/.bin/Scripts/sfc_scan.py index d7a3d3fc..884694f5 100644 --- a/.bin/Scripts/sfc_scan.py +++ b/.bin/Scripts/sfc_scan.py @@ -4,36 +4,37 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.repairs import * init_global_vars() os.system('title {}: SFC Tool'.format(KIT_NAME_FULL)) set_log_file('SFC Tool.log') if __name__ == '__main__': - try: - stay_awake() - clear_screen() - print_info('{}: SFC Tool\n'.format(KIT_NAME_FULL)) - other_results = { - 'Error': { - 'CalledProcessError': 'Unknown Error', - }, - 'Warning': { - 'GenericRepair': 'Repaired', - }} - if ask('Run a SFC scan now?'): - try_and_print(message='SFC scan...', - function=run_sfc_scan, other_results=other_results) - else: - abort() + try: + stay_awake() + clear_screen() + print_info('{}: SFC Tool\n'.format(KIT_NAME_FULL)) + other_results = { + 'Error': { + 'CalledProcessError': 'Unknown Error', + }, + 'Warning': { + 'GenericRepair': 'Repaired', + }} + if ask('Run a SFC scan now?'): + try_and_print(message='SFC scan...', + function=run_sfc_scan, other_results=other_results) + else: + abort() - # Done - print_standard('\nDone.') - pause('Press Enter to exit...') - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + print_standard('\nDone.') + pause('Press Enter to exit...') + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/system_checklist.py b/.bin/Scripts/system_checklist.py index aa4e68b7..7474a07c 100644 --- a/.bin/Scripts/system_checklist.py +++ b/.bin/Scripts/system_checklist.py @@ -4,138 +4,146 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.activation import * from functions.cleanup import * -from functions.diags import * from functions.info import * from functions.product_keys import * from functions.setup import * +from functions.sw_diags import * +from functions.windows_updates import * init_global_vars() os.system('title {}: System Checklist Tool'.format(KIT_NAME_FULL)) set_log_file('System Checklist.log') D7_MODE = 'd7mode' in sys.argv if __name__ == '__main__': + try: + stay_awake() + clear_screen() + print_info('{}: System Checklist Tool\n'.format(KIT_NAME_FULL)) + ticket_number = get_ticket_number() + other_results = { + 'Error': { + 'BIOSKeyNotFoundError': 'BIOS key not found', + 'CalledProcessError': 'Unknown Error', + 'FileNotFoundError': 'File not found', + 'GenericError': 'Unknown Error', + 'SecureBootDisabledError': 'Disabled', + }, + 'Warning': { + 'OSInstalledLegacyError': 'OS installed Legacy', + 'SecureBootNotAvailError': 'Not available', + 'SecureBootUnknownError': 'Unknown', + }} + if ENABLED_TICKET_NUMBERS: + print_info('Starting System Checklist for Ticket #{}\n'.format( + ticket_number)) + + # Configure + print_info('Configure') + if global_vars['OS']['Version'] == '10': + try_and_print(message='Explorer...', + function=config_explorer_system, cs='Done') + try_and_print(message='Disabling telemetry...', + function=disable_windows_telemetry, cs='Done') + try_and_print(message='Enabling RegBack...', + function=enable_regback, cs='Done') + try_and_print(message='Enabling BSoD mini dumps...', + function=enable_mini_dumps, cs='Done') + try_and_print(message='Enabling System Restore...', + function=enable_system_restore, cs='Done') + try_and_print(message='Create System Restore point...', + function=create_system_restore_point, cs='Done') + try_and_print(message='Enabling Windows Updates...', + function=enable_windows_updates, cs='Done') + try_and_print(message='Updating Clock...', + function=update_clock, cs='Done') + + # Cleanup + print_info('Cleanup') + try_and_print(message='AdwCleaner...', + function=cleanup_adwcleaner, cs='Done', other_results=other_results) + try_and_print(message='Desktop...', + function=cleanup_desktop, cs='Done') + try_and_print(message='Emsisoft a2cmd...', + function=cleanup_emsisoft, cs='Done') + try_and_print(message='Registry Backup(s)...', + function=cleanup_regbackups, cs='Done') + try_and_print(message='{}...'.format(KIT_NAME_FULL), + function=delete_empty_folders, cs='Done', + folder_path=global_vars['ClientDir']) + + # Export system info + if not D7_MODE: + print_info('Backup System Information') + try_and_print(message='AIDA64 reports...', + function=run_aida64, cs='Done', other_results=other_results) + try_and_print(message='File listing...', + function=backup_file_list, cs='Done', other_results=other_results) + try_and_print(message='Power plans...', + function=backup_power_plans, cs='Done') + try_and_print(message='Product Keys...', other_results=other_results, + function=run_produkey, cs='Done') + try_and_print(message='Registry...', + function=backup_registry, cs='Done', other_results=other_results) + + # User data + print_info('User Data') + show_user_data_summary() + + # Summary + print_info('Summary') + try_and_print(message='Operating System:', + function=show_os_name, ns='Unknown', silent_function=False) + try_and_print(message='Activation:', + function=show_os_activation, ns='Unknown', silent_function=False) + if (not windows_is_activated() + and global_vars['OS']['Version'] in ('8', '8.1', '10')): + try_and_print(message='BIOS Activation:', + function=activate_with_bios, + other_results=other_results) + try_and_print(message='Secure Boot Status:', + function=check_secure_boot_status, other_results=other_results) + try_and_print(message='Installed RAM:', + function=show_installed_ram, ns='Unknown', silent_function=False) + show_free_space() + try_and_print(message='Installed Antivirus:', + function=get_installed_antivirus, ns='Unknown', + other_results=other_results, print_return=True) + try_and_print(message='Installed Office:', + function=get_installed_office, ns='Unknown', + other_results=other_results, print_return=True) + if D7_MODE: + try_and_print(message='Product Keys:', + function=get_product_keys, ns='Unknown', print_return=True) + + # Play audio, show devices, open Windows updates, and open Activation + try_and_print(message='Opening Device Manager...', + function=open_device_manager, cs='Started') + try_and_print(message='Opening HWiNFO (Sensors)...', + function=run_hwinfo_sensors, cs='Started', other_results=other_results) + try_and_print(message='Opening Windows Updates...', + function=open_windows_updates, cs='Started') + if not windows_is_activated(): + try_and_print(message='Opening Windows Activation...', + function=open_windows_activation, cs='Started') + sleep(3) + try_and_print(message='Running XMPlay...', + function=run_xmplay, cs='Started', other_results=other_results) try: - stay_awake() - clear_screen() - print_info('{}: System Checklist Tool\n'.format(KIT_NAME_FULL)) - ticket_number = get_ticket_number() - other_results = { - 'Error': { - 'BIOSKeyNotFoundError': 'BIOS key not found', - 'CalledProcessError': 'Unknown Error', - 'FileNotFoundError': 'File not found', - 'GenericError': 'Unknown Error', - 'SecureBootDisabledError': 'Disabled', - }, - 'Warning': { - 'OSInstalledLegacyError': 'OS installed Legacy', - 'SecureBootNotAvailError': 'Not available', - 'SecureBootUnknownError': 'Unknown', - }} - if ENABLED_TICKET_NUMBERS: - print_info('Starting System Checklist for Ticket #{}\n'.format( - ticket_number)) - - # Configure - print_info('Configure') - if global_vars['OS']['Version'] == '10': - try_and_print(message='Explorer...', - function=config_explorer_system, cs='Done') - try_and_print(message='Disabling telemetry...', - function=disable_windows_telemetry, cs='Done') - try_and_print(message='Enabling RegBack...', - function=enable_regback, cs='Done') - try_and_print(message='Enabling System Restore...', - function=enable_system_restore, cs='Done') - try_and_print(message='Updating Clock...', - function=update_clock, cs='Done') - - # Cleanup - print_info('Cleanup') - try_and_print(message='AdwCleaner...', - function=cleanup_adwcleaner, cs='Done', other_results=other_results) - try_and_print(message='Desktop...', - function=cleanup_desktop, cs='Done') - try_and_print(message='Emsisoft a2cmd...', - function=cleanup_emsisoft, cs='Done') - try_and_print(message='Registry Backup(s)...', - function=cleanup_regbackups, cs='Done') - try_and_print(message='{}...'.format(KIT_NAME_FULL), - function=delete_empty_folders, cs='Done', - folder_path=global_vars['ClientDir']) - - # Export system info - if not D7_MODE: - print_info('Backup System Information') - try_and_print(message='AIDA64 reports...', - function=run_aida64, cs='Done', other_results=other_results) - try_and_print(message='File listing...', - function=backup_file_list, cs='Done', other_results=other_results) - try_and_print(message='Power plans...', - function=backup_power_plans, cs='Done') - try_and_print(message='Product Keys...', other_results=other_results, - function=run_produkey, cs='Done') - try_and_print(message='Registry...', - function=backup_registry, cs='Done', other_results=other_results) - - # User data - print_info('User Data') - show_user_data_summary() - - # Summary - print_info('Summary') - try_and_print(message='Operating System:', - function=show_os_name, ns='Unknown', silent_function=False) - try_and_print(message='Activation:', - function=show_os_activation, ns='Unknown', silent_function=False) - if (not windows_is_activated() - and global_vars['OS']['Version'] in ('8', '8.1', '10')): - try_and_print(message='BIOS Activation:', - function=activate_with_bios, - other_results=other_results) - try_and_print(message='Secure Boot Status:', - function=check_secure_boot_status, other_results=other_results) - try_and_print(message='Installed RAM:', - function=show_installed_ram, ns='Unknown', silent_function=False) - show_free_space() - try_and_print(message='Installed Antivirus:', - function=get_installed_antivirus, ns='Unknown', - other_results=other_results, print_return=True) - try_and_print(message='Installed Office:', - function=get_installed_office, ns='Unknown', - other_results=other_results, print_return=True) - if D7_MODE: - try_and_print(message='Product Keys:', - function=get_product_keys, ns='Unknown', print_return=True) - - # Play audio, show devices, open Windows updates, and open Activation - try_and_print(message='Opening Device Manager...', - function=open_device_manager, cs='Started') - try_and_print(message='Opening HWiNFO (Sensors)...', - function=run_hwinfo_sensors, cs='Started', other_results=other_results) - try_and_print(message='Opening Windows Updates...', - function=open_windows_updates, cs='Started') - if not windows_is_activated(): - try_and_print(message='Opening Windows Activation...', - function=open_windows_activation, cs='Started') - sleep(3) - try_and_print(message='Running XMPlay...', - function=run_xmplay, cs='Started', other_results=other_results) - try: - check_secure_boot_status(show_alert=True) - except: - # Only trying to open alert message boxes - pass - - # Done - print_standard('\nDone.') - pause('Press Enter exit...') - exit_script() - except SystemExit: - pass + check_secure_boot_status(show_alert=True) except: - major_exception() + # Only trying to open alert message boxes + pass + + # Done + print_standard('\nDone.') + pause('Press Enter exit...') + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/system_checklist_hw.py b/.bin/Scripts/system_checklist_hw.py index 01d6d551..fd08fe10 100644 --- a/.bin/Scripts/system_checklist_hw.py +++ b/.bin/Scripts/system_checklist_hw.py @@ -17,101 +17,107 @@ os.system('title {}: System HW Checklist Tool'.format(KIT_NAME_FULL)) set_log_file('System HW Checklist.log') if __name__ == '__main__': + try: + stay_awake() + clear_screen() + print_info('{}: System HW Checklist Tool\n'.format(KIT_NAME_FULL)) + ticket_number = get_ticket_number() + other_results = { + 'Error': { + 'BIOSKeyNotFoundError': 'BIOS key not found', + 'CalledProcessError': 'Unknown Error', + 'FileNotFoundError': 'File not found', + 'GenericError': 'Unknown Error', + 'SecureBootDisabledError': 'Disabled', + }, + 'Warning': { + 'OSInstalledLegacyError': 'OS installed Legacy', + 'SecureBootNotAvailError': 'Not available', + 'SecureBootUnknownError': 'Unknown', + }} + if ENABLED_TICKET_NUMBERS: + print_info('Starting System Checklist for Ticket #{}\n'.format( + ticket_number)) + + # Configure + print_info('Configure') + if global_vars['OS']['Version'] == '10': + try_and_print(message='Enabling RegBack...', + function=enable_regback, cs='Done') + try_and_print(message='Enabling BSoD mini dumps...', + function=enable_mini_dumps, cs='Done') + try_and_print(message='Enabling System Restore...', + function=enable_system_restore, cs='Done') + try_and_print(message='Create System Restore point...', + function=create_system_restore_point, cs='Done') + + # Export system info + print_info('Backup System Information') + try_and_print(message='AIDA64 reports...', + function=run_aida64, cs='Done', other_results=other_results) + try_and_print(message='File listing...', + function=backup_file_list, cs='Done', other_results=other_results) + try_and_print(message='Power plans...', + function=backup_power_plans, cs='Done') + try_and_print(message='Product Keys...', other_results=other_results, + function=run_produkey, cs='Done') + try_and_print(message='Registry...', + function=backup_registry, cs='Done', other_results=other_results) + + # Cleanup + print_info('Cleanup') + try_and_print(message='{}...'.format(KIT_NAME_FULL), + function=delete_empty_folders, cs='Done', + folder_path=global_vars['ClientDir']) + + # User data + print_info('User Data') + show_user_data_summary() + + # Summary + print_info('Summary') + try_and_print(message='Operating System:', + function=show_os_name, ns='Unknown', silent_function=False) + try_and_print(message='Activation:', + function=show_os_activation, ns='Unknown', silent_function=False) + try_and_print(message='Secure Boot Status:', + function=check_secure_boot_status, other_results=other_results) + try_and_print(message='Installed RAM:', + function=show_installed_ram, ns='Unknown', silent_function=False) + show_free_space() + try_and_print(message='Installed Antivirus:', + function=get_installed_antivirus, ns='Unknown', + other_results=other_results, print_return=True) + try_and_print(message='Installed Office:', + function=get_installed_office, ns='Unknown', + other_results=other_results, print_return=True) + + # Play audio, show devices, open Windows updates, and open Activation + try_and_print(message='Opening Device Manager...', + function=open_device_manager, cs='Started') + try_and_print(message='Opening HWiNFO (Sensors)...', + function=run_hwinfo_sensors, cs='Started', other_results=other_results) + try_and_print(message='Opening Windows Updates...', + function=open_windows_updates, cs='Started') + if not windows_is_activated(): + try_and_print(message='Opening Windows Activation...', + function=open_windows_activation, cs='Started') + sleep(3) + try_and_print(message='Running XMPlay...', + function=run_xmplay, cs='Started', other_results=other_results) try: - stay_awake() - clear_screen() - print_info('{}: System HW Checklist Tool\n'.format(KIT_NAME_FULL)) - ticket_number = get_ticket_number() - other_results = { - 'Error': { - 'BIOSKeyNotFoundError': 'BIOS key not found', - 'CalledProcessError': 'Unknown Error', - 'FileNotFoundError': 'File not found', - 'GenericError': 'Unknown Error', - 'SecureBootDisabledError': 'Disabled', - }, - 'Warning': { - 'OSInstalledLegacyError': 'OS installed Legacy', - 'SecureBootNotAvailError': 'Not available', - 'SecureBootUnknownError': 'Unknown', - }} - if ENABLED_TICKET_NUMBERS: - print_info('Starting System Checklist for Ticket #{}\n'.format( - ticket_number)) - - # Configure - print_info('Configure') - if global_vars['OS']['Version'] == '10': - try_and_print(message='Enabling RegBack...', - function=enable_regback, cs='Done') - try_and_print(message='Enabling System Restore...', - function=enable_system_restore, cs='Done') - - # Export system info - print_info('Backup System Information') - try_and_print(message='AIDA64 reports...', - function=run_aida64, cs='Done', other_results=other_results) - try_and_print(message='File listing...', - function=backup_file_list, cs='Done', other_results=other_results) - try_and_print(message='Power plans...', - function=backup_power_plans, cs='Done') - try_and_print(message='Product Keys...', other_results=other_results, - function=run_produkey, cs='Done') - try_and_print(message='Registry...', - function=backup_registry, cs='Done', other_results=other_results) - - # Cleanup - print_info('Cleanup') - try_and_print(message='{}...'.format(KIT_NAME_FULL), - function=delete_empty_folders, cs='Done', - folder_path=global_vars['ClientDir']) - - # User data - print_info('User Data') - show_user_data_summary() - - # Summary - print_info('Summary') - try_and_print(message='Operating System:', - function=show_os_name, ns='Unknown', silent_function=False) - try_and_print(message='Activation:', - function=show_os_activation, ns='Unknown', silent_function=False) - try_and_print(message='Secure Boot Status:', - function=check_secure_boot_status, other_results=other_results) - try_and_print(message='Installed RAM:', - function=show_installed_ram, ns='Unknown', silent_function=False) - show_free_space() - try_and_print(message='Installed Antivirus:', - function=get_installed_antivirus, ns='Unknown', - other_results=other_results, print_return=True) - try_and_print(message='Installed Office:', - function=get_installed_office, ns='Unknown', - other_results=other_results, print_return=True) - - # Play audio, show devices, open Windows updates, and open Activation - try_and_print(message='Opening Device Manager...', - function=open_device_manager, cs='Started') - try_and_print(message='Opening HWiNFO (Sensors)...', - function=run_hwinfo_sensors, cs='Started', other_results=other_results) - try_and_print(message='Opening Windows Updates...', - function=open_windows_updates, cs='Started') - if not windows_is_activated(): - try_and_print(message='Opening Windows Activation...', - function=open_windows_activation, cs='Started') - sleep(3) - try_and_print(message='Running XMPlay...', - function=run_xmplay, cs='Started', other_results=other_results) - try: - check_secure_boot_status(show_alert=True) - except: - # Only trying to open alert message boxes - pass - - # Done - print_standard('\nDone.') - pause('Press Enter exit...') - exit_script() - except SystemExit: - pass + check_secure_boot_status(show_alert=True) except: - major_exception() + # Only trying to open alert message boxes + pass + + # Done + print_standard('\nDone.') + pause('Press Enter exit...') + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/system_diagnostics.py b/.bin/Scripts/system_diagnostics.py index 442dbc5e..2e717ed5 100644 --- a/.bin/Scripts/system_diagnostics.py +++ b/.bin/Scripts/system_diagnostics.py @@ -3,206 +3,208 @@ import os import sys + # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.browsers import * -from functions.diags import * from functions.info import * from functions.product_keys import * from functions.repairs import * +from functions.sw_diags import * init_global_vars() os.system('title {}: System Diagnostics Tool'.format(KIT_NAME_FULL)) set_log_file('System Diagnostics.log') D7_MODE = 'd7mode' in sys.argv + # Static Variables BLEACH_BIT_CLEANERS = { - 'Applications': ( - 'adobe_reader.cache', - 'adobe_reader.tmp', - 'amule.tmp', - 'flash.cache', - 'gimp.tmp', - 'hippo_opensim_viewer.cache', - 'java.cache', - 'libreoffice.cache', - 'liferea.cache', - 'miro.cache', - 'openofficeorg.cache', - 'pidgin.cache', - 'secondlife_viewer.Cache', - 'thunderbird.cache', - 'vuze.backup_files', - 'vuze.cache', - 'vuze.tmp', - 'yahoo_messenger.cache', - ), - 'Browsers': ( - 'chromium.cache', - 'chromium.current_session', - 'firefox.cache', - 'firefox.session_restore', - 'google_chrome.cache', - 'google_chrome.session', - 'google_earth.temporary_files', - 'internet_explorer.temporary_files', - 'opera.cache', - 'opera.current_session', - 'safari.cache', - 'seamonkey.cache', - ), - 'System': ( - 'system.clipboard', - 'system.tmp', - 'winapp2_windows.jump_lists', - 'winapp2_windows.ms_search', - 'windows_explorer.run', - 'windows_explorer.search_history', - 'windows_explorer.thumbnails', - ), + 'Applications': ( + 'adobe_reader.cache', + 'adobe_reader.tmp', + 'amule.tmp', + 'flash.cache', + 'gimp.tmp', + 'hippo_opensim_viewer.cache', + 'java.cache', + 'libreoffice.cache', + 'liferea.cache', + 'miro.cache', + 'openofficeorg.cache', + 'pidgin.cache', + 'secondlife_viewer.Cache', + 'thunderbird.cache', + 'vuze.backup_files', + 'vuze.cache', + 'vuze.tmp', + 'yahoo_messenger.cache', + ), + 'Browsers': ( + 'chromium.cache', + 'chromium.current_session', + 'firefox.cache', + 'firefox.session_restore', + 'google_chrome.cache', + 'google_chrome.session', + 'google_earth.temporary_files', + 'internet_explorer.temporary_files', + 'opera.cache', + 'opera.current_session', + 'safari.cache', + 'seamonkey.cache', + ), + 'System': ( + 'system.clipboard', + 'system.tmp', + 'winapp2_windows.jump_lists', + 'winapp2_windows.ms_search', + 'windows_explorer.run', + 'windows_explorer.search_history', + 'windows_explorer.thumbnails', + ), } def check_result(result, other_results): - """Check result for warnings and errors.""" - result_ok = True - if not result['CS']: - for warning in other_results.get('Warning', {}).keys(): - if warning in str(result['Error']): - # Ignore warnings and repair statements - return True - # Error is not a warning - result_ok = False - return result_ok + """Check result for warnings and errors.""" + result_ok = True + if not result['CS']: + for warning in other_results.get('Warning', {}).keys(): + if warning in str(result['Error']): + # Ignore warnings and repair statements + return True + # Error is not a warning + result_ok = False + return result_ok + if __name__ == '__main__': - try: - stay_awake() - clear_screen() - print_info('{}: System Diagnostics Tool\n'.format(KIT_NAME_FULL)) - ticket_number = get_ticket_number() - system_ok = True - other_results = { - 'Error': { - 'CalledProcessError': 'Unknown Error', - 'FileNotFoundError': 'File not found', - }, - 'Warning': { - 'GenericRepair': 'Repaired', - 'UnsupportedOSError': 'Unsupported OS', - }} - if ENABLED_TICKET_NUMBERS: - print_info('Starting System Diagnostics for Ticket #{}\n'.format( - ticket_number)) + try: + stay_awake() + clear_screen() + print_info('{}: System Diagnostics Tool\n'.format(KIT_NAME_FULL)) + ticket_number = get_ticket_number() + system_ok = True + other_results = { + 'Error': { + 'CalledProcessError': 'Unknown Error', + 'FileNotFoundError': 'File not found', + }, + 'Warning': { + 'GenericRepair': 'Repaired', + 'UnsupportedOSError': 'Unsupported OS', + }} + if ENABLED_TICKET_NUMBERS: + print_info('Starting System Diagnostics for Ticket #{}\n'.format( + ticket_number)) - # Sanitize Environment - print_info('Sanitizing Environment') - if not D7_MODE: - try_and_print(message='Running RKill...', - function=run_rkill, cs='Done', other_results=other_results) - try_and_print(message='Running TDSSKiller...', - function=run_tdsskiller, cs='Done', other_results=other_results) + # Sanitize Environment + print_info('Sanitizing Environment') + if not D7_MODE: + try_and_print(message='Running RKill...', + function=run_rkill, cs='Done', other_results=other_results) + try_and_print(message='Running TDSSKiller...', + function=run_tdsskiller, cs='Done', other_results=other_results) - # Re-run if earlier process was stopped. - stay_awake() + # Re-run if earlier process was stopped. + stay_awake() - # Start diags - if not D7_MODE: - print_info('Starting Background Scans') - check_connection() - try_and_print(message='Running HitmanPro...', - function=run_hitmanpro, cs='Started', other_results=other_results) - try_and_print(message='Running Autoruns...', - function=run_autoruns, cs='Started', other_results=other_results) + # Start diags + if not D7_MODE: + print_info('Starting Background Scans') + check_connection() + try_and_print(message='Running HitmanPro...', + function=run_hitmanpro, cs='Started', other_results=other_results) + try_and_print(message='Running Autoruns...', + function=run_autoruns, cs='Started', other_results=other_results) - # OS Health Checks - print_info('OS Health Checks') - result = try_and_print( - message='CHKDSK ({SYSTEMDRIVE})...'.format(**global_vars['Env']), - function=run_chkdsk, other_results=other_results) + # OS Health Checks + print_info('OS Health Checks') + result = try_and_print( + message='CHKDSK ({SYSTEMDRIVE})...'.format(**global_vars['Env']), + function=run_chkdsk, other_results=other_results) + system_ok &= check_result(result, other_results) + result = try_and_print(message='SFC scan...', + function=run_sfc_scan, other_results=other_results) + system_ok &= check_result(result, other_results) + if D7_MODE: + result = try_and_print(message='DISM RestoreHealth...', + function=run_dism, other_results=other_results, repair=True) + if global_vars['OS']['Version'] in ('8', '8.1', '10'): system_ok &= check_result(result, other_results) - result = try_and_print(message='SFC scan...', - function=run_sfc_scan, other_results=other_results) - system_ok &= check_result(result, other_results) - if D7_MODE: - result = try_and_print(message='DISM RestoreHealth...', - function=run_dism, other_results=other_results, repair=True) - if global_vars['OS']['Version'] in ('8', '8.1', '10'): - system_ok &= check_result(result, other_results) - else: - try_and_print(message='DISM CheckHealth...', - function=run_dism, other_results=other_results, repair=False) + else: + try_and_print(message='DISM CheckHealth...', + function=run_dism, other_results=other_results, repair=False) - if D7_MODE: - # Archive all browsers for all users - archive_all_users() - else: - # Scan for supported browsers - print_info('Scanning for browsers') - scan_for_browsers() + if D7_MODE: + # Archive all browsers for all users + archive_all_users() + else: + # Scan for supported browsers + print_info('Scanning for browsers') + scan_for_browsers() - # Run BleachBit cleaners - print_info('BleachBit Cleanup') - for k, v in sorted(BLEACH_BIT_CLEANERS.items()): - try_and_print(message='{}...'.format(k), - function=run_bleachbit, - cs='Done', other_results=other_results, - cleaners=v, preview=bool(not D7_MODE)) + # Run BleachBit cleaners + print_info('BleachBit Cleanup') + for k, v in sorted(BLEACH_BIT_CLEANERS.items()): + try_and_print(message='{}...'.format(k), + function=run_bleachbit, + cs='Done', other_results=other_results, + cleaners=v, preview=bool(not D7_MODE)) - # Export system info - print_info('Backup System Information') - try_and_print(message='AIDA64 reports...', - function=run_aida64, cs='Done', other_results=other_results) - if not D7_MODE: - backup_browsers() - try_and_print(message='File listing...', - function=backup_file_list, cs='Done', other_results=other_results) - try_and_print(message='Power plans...', - function=backup_power_plans, cs='Done') - try_and_print(message='Product Keys...', - function=run_produkey, cs='Done', other_results=other_results) - try_and_print(message='Registry...', - function=backup_registry, cs='Done', other_results=other_results, - overwrite=True) + # Export system info + print_info('Backup System Information') + try_and_print(message='AIDA64 reports...', + function=run_aida64, cs='Done', other_results=other_results) + if not D7_MODE: + backup_browsers() + try_and_print(message='File listing...', + function=backup_file_list, cs='Done', other_results=other_results) + try_and_print(message='Power plans...', + function=backup_power_plans, cs='Done') + try_and_print(message='Product Keys...', + function=run_produkey, cs='Done', other_results=other_results) + try_and_print(message='Registry...', + function=backup_registry, cs='Done', other_results=other_results, + overwrite=True) - # Summary - if not D7_MODE: - print_info('Summary') - try_and_print(message='Operating System:', - function=show_os_name, ns='Unknown', silent_function=False) - try_and_print(message='Activation:', - function=show_os_activation, ns='Unknown', silent_function=False) - try_and_print(message='Installed RAM:', - function=show_installed_ram, ns='Unknown', silent_function=False) - show_free_space() - try_and_print(message='Temp Size:', - function=show_temp_files_size, silent_function=False) - try_and_print(message='Installed Antivirus:', - function=get_installed_antivirus, ns='Unknown', - other_results=other_results, print_return=True) - try_and_print(message='Installed Office:', - function=get_installed_office, ns='Unknown', - other_results=other_results, print_return=True) - try_and_print(message='Product Keys:', - function=get_product_keys, ns='Unknown', print_return=True) + # Summary + if not D7_MODE: + print_info('Summary') + try_and_print(message='Operating System:', + function=show_os_name, ns='Unknown', silent_function=False) + try_and_print(message='Activation:', + function=show_os_activation, ns='Unknown', silent_function=False) + try_and_print(message='Installed RAM:', + function=show_installed_ram, ns='Unknown', silent_function=False) + show_free_space() + try_and_print(message='Temp Size:', + function=show_temp_files_size, silent_function=False) + try_and_print(message='Installed Antivirus:', + function=get_installed_antivirus, ns='Unknown', + other_results=other_results, print_return=True) + try_and_print(message='Installed Office:', + function=get_installed_office, ns='Unknown', + other_results=other_results, print_return=True) + try_and_print(message='Product Keys:', + function=get_product_keys, ns='Unknown', print_return=True) - # User data - if not D7_MODE: - print_info('User Data') - try: - show_user_data_summary() - except Exception: - print_error(' Unknown error.') + # User data + if not D7_MODE: + print_info('User Data') + try: + show_user_data_summary() + except Exception: + print_error(' Unknown error.') - # Done - if not D7_MODE or not system_ok: - print_standard('\nDone.') - pause('Press Enter to exit...') - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + if not D7_MODE or not system_ok: + print_standard('\nDone.') + pause('Press Enter to exit...') + exit_script() + except SystemExit: + pass + except: + major_exception() -# vim: sts=4 sw=4 ts=4 +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/transferred_keys.py b/.bin/Scripts/transferred_keys.py index b95ff3c9..6dab114d 100644 --- a/.bin/Scripts/transferred_keys.py +++ b/.bin/Scripts/transferred_keys.py @@ -4,25 +4,26 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.product_keys import * init_global_vars() os.system('title {}: Transferred Key Finder'.format(KIT_NAME_FULL)) set_log_file('Transferred Keys.log') if __name__ == '__main__': - try: - stay_awake() - clear_screen() - print_info('{}: Transferred Key Finder\n'.format(KIT_NAME_FULL)) - try_and_print(message='Searching for keys...', - function=list_clientdir_keys, print_return=True) + try: + stay_awake() + clear_screen() + print_info('{}: Transferred Key Finder\n'.format(KIT_NAME_FULL)) + try_and_print(message='Searching for keys...', + function=list_clientdir_keys, print_return=True) - # Done - print_standard('\nDone.') - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + print_standard('\nDone.') + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/update_kit.py b/.bin/Scripts/update_kit.py index 63ed62c3..8f21e122 100644 --- a/.bin/Scripts/update_kit.py +++ b/.bin/Scripts/update_kit.py @@ -4,148 +4,144 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.update import * init_global_vars() os.system('title {}: Kit Update Tool'.format(KIT_NAME_FULL)) if __name__ == '__main__': - try: - clear_screen() - print_info('{}: Kit Update Tool\n'.format(KIT_NAME_FULL)) - other_results = { - 'Error': { - 'CalledProcessError': 'Unknown Error', - }} + try: + clear_screen() + print_info('{}: Kit Update Tool\n'.format(KIT_NAME_FULL)) + other_results = { + 'Error': { + 'CalledProcessError': 'Unknown Error', + }} - ## Prep ## - update_sdio = ask('Update SDI Origin?') + ## Prep ## + update_sdio = ask('Update SDI Origin?') - ## Download ## - print_success('Downloading tools') + ## Download ## + print_success('Downloading tools') - # Data Recovery - print_info(' Data Recovery') - try_and_print(message='TestDisk / PhotoRec...', function=update_testdisk, other_results=other_results, width=40) + # Data Recovery + print_info(' Data Recovery') + try_and_print(message='TestDisk / PhotoRec...', function=update_testdisk, other_results=other_results, width=40) - # Data Transfers - print_info(' Data Transfers') - try_and_print(message='FastCopy...', function=update_fastcopy, other_results=other_results, width=40) - try_and_print(message='Linux Reader...', function=update_linux_reader, other_results=other_results, width=40) - try_and_print(message='wimlib...', function=update_wimlib, other_results=other_results, width=40) - try_and_print(message='XYplorer...', function=update_xyplorer, other_results=other_results, width=40) + # Data Transfers + print_info(' Data Transfers') + try_and_print(message='FastCopy...', function=update_fastcopy, other_results=other_results, width=40) + try_and_print(message='Linux Reader...', function=update_linux_reader, other_results=other_results, width=40) + try_and_print(message='wimlib...', function=update_wimlib, other_results=other_results, width=40) + try_and_print(message='XYplorer...', function=update_xyplorer, other_results=other_results, width=40) - # Diagnostics - print_info(' Diagnostics') - try_and_print(message='AIDA64...', function=update_aida64, other_results=other_results, width=40) - try_and_print(message='Autoruns...', function=update_autoruns, other_results=other_results, width=40) - try_and_print(message='BleachBit...', function=update_bleachbit, other_results=other_results, width=40) - try_and_print(message='Blue Screen View...', function=update_bluescreenview, other_results=other_results, width=40) - try_and_print(message='ERUNT...', function=update_erunt, other_results=other_results, width=40) - try_and_print(message='FurMark...', function=update_furmark, other_results=other_results, width=40) - try_and_print(message='Hitman Pro...', function=update_hitmanpro, other_results=other_results, width=40) - try_and_print(message='HWiNFO...', function=update_hwinfo, other_results=other_results, width=40) - try_and_print(message='NirCmd...', function=update_nircmd, other_results=other_results, width=40) - try_and_print(message='ProduKey...', function=update_produkey, other_results=other_results, width=40) + # Diagnostics + print_info(' Diagnostics') + try_and_print(message='AIDA64...', function=update_aida64, other_results=other_results, width=40) + try_and_print(message='Autoruns...', function=update_autoruns, other_results=other_results, width=40) + try_and_print(message='BleachBit...', function=update_bleachbit, other_results=other_results, width=40) + try_and_print(message='Blue Screen View...', function=update_bluescreenview, other_results=other_results, width=40) + try_and_print(message='ERUNT...', function=update_erunt, other_results=other_results, width=40) + try_and_print(message='FurMark...', function=update_furmark, other_results=other_results, width=40) + try_and_print(message='Hitman Pro...', function=update_hitmanpro, other_results=other_results, width=40) + try_and_print(message='HWiNFO...', function=update_hwinfo, other_results=other_results, width=40) + try_and_print(message='NirCmd...', function=update_nircmd, other_results=other_results, width=40) + try_and_print(message='ProduKey...', function=update_produkey, other_results=other_results, width=40) - # Drivers - print_info(' Drivers') - try_and_print(message='Intel RST...', function=update_intel_rst, other_results=other_results, width=40) - try_and_print(message='Intel SSD Toolbox...', function=update_intel_ssd_toolbox, other_results=other_results, width=40) - try_and_print(message='Samsing Magician...', function=update_samsung_magician, other_results=other_results, width=40) - if update_sdio: - try_and_print(message='Snappy Driver Installer Origin...', function=update_sdi_origin, other_results=other_results, width=40) + # Drivers + print_info(' Drivers') + try_and_print(message='Intel RST...', function=update_intel_rst, other_results=other_results, width=40) + try_and_print(message='Intel SSD Toolbox...', function=update_intel_ssd_toolbox, other_results=other_results, width=40) + try_and_print(message='Samsing Magician...', function=update_samsung_magician, other_results=other_results, width=40) + if update_sdio: + try_and_print(message='Snappy Driver Installer Origin...', function=update_sdi_origin, other_results=other_results, width=40) - # Installers - print_info(' Installers') - try_and_print(message='Adobe Reader DC...', function=update_adobe_reader_dc, other_results=other_results, width=40) - try_and_print(message='ESET Configs...', function=update_eset_config, other_results=other_results, width=40) - try_and_print(message='Macs Fan Control...', function=update_macs_fan_control, other_results=other_results, width=40) - try_and_print(message='MS Office...', function=update_office, other_results=other_results, width=40) - try_and_print(message='Visual C++ Runtimes...', function=update_vcredists, other_results=other_results, width=40) - update_all_ninite(other_results=other_results, width=40) + # Installers + print_info(' Installers') + try_and_print(message='Adobe Reader DC...', function=update_adobe_reader_dc, other_results=other_results, width=40) + try_and_print(message='ESET Configs...', function=update_eset_config, other_results=other_results, width=40) + try_and_print(message='Macs Fan Control...', function=update_macs_fan_control, other_results=other_results, width=40) + try_and_print(message='MS Office...', function=update_office, other_results=other_results, width=40) + try_and_print(message='Visual C++ Runtimes...', function=update_vcredists, other_results=other_results, width=40) + update_all_ninite(other_results=other_results, width=40) - # Misc - print_info(' Misc') - try_and_print(message='Caffeine...', function=update_caffeine, other_results=other_results, width=40) - try_and_print(message='Classic Start Skin...', function=update_classic_start_skin, other_results=other_results, width=40) - try_and_print(message='Du...', function=update_du, other_results=other_results, width=40) - try_and_print(message='Everything...', function=update_everything, other_results=other_results, width=40) - try_and_print(message='Firefox Extensions...', function=update_firefox_ublock_origin, other_results=other_results, width=40) - try_and_print(message='PuTTY...', function=update_putty, other_results=other_results, width=40) - try_and_print(message='ShutUp10...', function=update_shutup10, other_results=other_results, width=40) - try_and_print(message='Notepad++...', function=update_notepadplusplus, other_results=other_results, width=40) - try_and_print(message='WizTree...', function=update_wiztree, other_results=other_results, width=40) - try_and_print(message='XMPlay...', function=update_xmplay, other_results=other_results, width=40) + # Misc + print_info(' Misc') + try_and_print(message='Caffeine...', function=update_caffeine, other_results=other_results, width=40) + try_and_print(message='Classic Start Skin...', function=update_classic_start_skin, other_results=other_results, width=40) + try_and_print(message='Du...', function=update_du, other_results=other_results, width=40) + try_and_print(message='Everything...', function=update_everything, other_results=other_results, width=40) + try_and_print(message='Firefox Extensions...', function=update_firefox_ublock_origin, other_results=other_results, width=40) + try_and_print(message='PuTTY...', function=update_putty, other_results=other_results, width=40) + try_and_print(message='ShutUp10...', function=update_shutup10, other_results=other_results, width=40) + try_and_print(message='Notepad++...', function=update_notepadplusplus, other_results=other_results, width=40) + try_and_print(message='WizTree...', function=update_wiztree, other_results=other_results, width=40) + try_and_print(message='XMPlay...', function=update_xmplay, other_results=other_results, width=40) - # Repairs - print_info(' Repairs') - try_and_print(message='AdwCleaner...', function=update_adwcleaner, other_results=other_results, width=40) - try_and_print(message='ESET Online Scanner...', function=update_eset_online_scanner, other_results=other_results, width=40) - try_and_print(message='KVRT...', function=update_kvrt, other_results=other_results, width=40) - try_and_print(message='RKill...', function=update_rkill, other_results=other_results, width=40) - try_and_print(message='TDSS Killer...', function=update_tdsskiller, other_results=other_results, width=40) - try_and_print(message='WinAIO Repair...', function=update_winaiorepair, other_results=other_results, width=40) + # Repairs + print_info(' Repairs') + try_and_print(message='AdwCleaner...', function=update_adwcleaner, other_results=other_results, width=40) + try_and_print(message='ESET Online Scanner...', function=update_eset_online_scanner, other_results=other_results, width=40) + try_and_print(message='KVRT...', function=update_kvrt, other_results=other_results, width=40) + try_and_print(message='RKill...', function=update_rkill, other_results=other_results, width=40) + try_and_print(message='TDSS Killer...', function=update_tdsskiller, other_results=other_results, width=40) + try_and_print(message='WinAIO Repair...', function=update_winaiorepair, other_results=other_results, width=40) - # Uninstallers - print_info(' Uninstallers') - try_and_print(message='IObit Uninstaller...', function=update_iobit_uninstaller, other_results=other_results, width=40) + # Uninstallers + print_info(' Uninstallers') + try_and_print(message='IObit Uninstaller...', function=update_iobit_uninstaller, other_results=other_results, width=40) - ## Review ## - print_standard('Please review the results and download/extract any missing items to .cbin') - pause('Press Enter to compress the .cbin items') + ## Review ## + print_standard('Please review the results and download/extract any missing items to .cbin') + pause('Press Enter to compress the .cbin items') - ## Compress ## - print_success('Compressing tools') - print_info(' _Drivers') - for item in os.scandir(r'{}\_Drivers'.format(global_vars['CBinDir'])): - if not re.search(r'^(_Drivers|.*7z)$', item.name, re.IGNORECASE): - try_and_print( - message='{}...'.format(item.name), - function=compress_and_remove_item, - other_results = other_results, - width=40, - item = item) - print_info(' .cbin') - for item in os.scandir(global_vars['CBinDir']): - if not re.search(r'^(_Drivers|_include|.*7z)$', item.name, re.IGNORECASE): - try_and_print( - message='{}...'.format(item.name), - function=compress_and_remove_item, - other_results = other_results, - width=40, - item = item) + ## Compress ## + print_success('Compressing tools') + print_info(' _Drivers') + for item in os.scandir(r'{}\_Drivers'.format(global_vars['CBinDir'])): + if not re.search(r'^(_Drivers|.*7z)$', item.name, re.IGNORECASE): + try_and_print( + message='{}...'.format(item.name), + function=compress_and_remove_item, + other_results = other_results, + width=40, + item = item) + print_info(' .cbin') + for item in os.scandir(global_vars['CBinDir']): + if not re.search(r'^(_Drivers|_include|.*7z)$', item.name, re.IGNORECASE): + try_and_print( + message='{}...'.format(item.name), + function=compress_and_remove_item, + other_results = other_results, + width=40, + item = item) - ## Search for network Office/QuickBooks installers & add to LAUNCHERS - print_success('Scanning for network installers') - scan_for_net_installers(OFFICE_SERVER, 'Office', min_year=2010) - scan_for_net_installers(QUICKBOOKS_SERVER, 'QuickBooks', min_year=2015) + ## Generate Launchers + print_success('Generating launchers') + for section in sorted(LAUNCHERS.keys()): + print_info(' {}'.format(section)) + for name, options in sorted(LAUNCHERS[section].items()): + try_and_print(message=name, function=generate_launcher, + section=section, name=name, options=options, + other_results=other_results, width=40) - ## Generate Launchers - print_success('Generating launchers') - for section in sorted(LAUNCHERS.keys()): - print_info(' {}'.format(section)) - for name, options in sorted(LAUNCHERS[section].items()): - try_and_print(message=name, function=generate_launcher, - section=section, name=name, options=options, - other_results=other_results, width=40) + # Rename "Copy WizardKit.cmd" (if necessary) + source = r'{}\Scripts\Copy WizardKit.cmd'.format(global_vars['BinDir']) + dest = r'{}\Copy {}.cmd'.format(global_vars['BaseDir'], KIT_NAME_FULL) + if os.path.exists(source): + try: + shutil.move(source, dest) + except Exception: + print_error(' Failed to rename "{}.cmd" to "{}.cmd"'.format( + 'Copy WizardKit', KIT_NAME_FULL)) - # Rename "Copy WizardKit.cmd" (if necessary) - source = r'{}\Scripts\Copy WizardKit.cmd'.format(global_vars['BinDir']) - dest = r'{}\Copy {}.cmd'.format(global_vars['BaseDir'], KIT_NAME_FULL) - if os.path.exists(source): - try: - shutil.move(source, dest) - except Exception: - print_error(' Failed to rename "{}.cmd" to "{}.cmd"'.format( - 'Copy WizardKit', KIT_NAME_FULL)) + # Done + print_standard('\nDone.') + pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() - # Done - print_standard('\nDone.') - pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/user_checklist.py b/.bin/Scripts/user_checklist.py index 29accf82..b292ed83 100644 --- a/.bin/Scripts/user_checklist.py +++ b/.bin/Scripts/user_checklist.py @@ -4,8 +4,7 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.browsers import * from functions.cleanup import * from functions.setup import * @@ -15,83 +14,83 @@ set_log_file('User Checklist ({USERNAME}).log'.format(**global_vars['Env'])) D7_MODE = 'd7mode' in sys.argv if __name__ == '__main__': - try: - stay_awake() - clear_screen() - print_info('{}: User Checklist\n'.format(KIT_NAME_FULL)) - other_results = { - 'Warning': { - 'NotInstalledError': 'Not installed', - 'NoProfilesError': 'No profiles found', - }} - #answer_config_browsers = ask('Install adblock?') - answer_config_browsers = True - if answer_config_browsers: - if D7_MODE: - # This is handled by another script option in d7ii - answer_reset_browsers = False - else: - answer_reset_browsers = ask( - 'Reset browsers to safe defaults first?') - if global_vars['OS']['Version'] == '10': - #answer_config_classicshell = ask('Configure ClassicShell?') - #answer_config_explorer_user = ask('Configure Explorer?') - answer_config_classicshell = True - answer_config_explorer_user = True + try: + stay_awake() + clear_screen() + print_info('{}: User Checklist\n'.format(KIT_NAME_FULL)) + other_results = { + 'Warning': { + 'NotInstalledError': 'Not installed', + 'NoProfilesError': 'No profiles found', + }} + #answer_config_browsers = ask('Install adblock?') + answer_config_browsers = True + if answer_config_browsers: + if D7_MODE: + # This is handled by another script option in d7ii + answer_reset_browsers = False + else: + answer_reset_browsers = ask( + 'Reset browsers to safe defaults first?') + if global_vars['OS']['Version'] == '10': + answer_config_classicshell = True + answer_config_explorer_user = True - # Cleanup - print_info('Cleanup') - try_and_print(message='Desktop...', - function=cleanup_desktop, cs='Done') + # Cleanup + print_info('Cleanup') + try_and_print(message='Desktop...', + function=cleanup_desktop, cs='Done') - # Scan for supported browsers - print_info('Scanning for browsers') - scan_for_browsers() + # Scan for supported browsers + print_info('Scanning for browsers') + scan_for_browsers() - # Homepages - if not D7_MODE: - print_info('Current homepages') - list_homepages() + # Homepages + if not D7_MODE: + print_info('Current homepages') + list_homepages() - # Backup - if not D7_MODE: - # Done during system_diagnostics - print_info('Backing up browsers') - backup_browsers() + # Backup + if not D7_MODE: + # Done during system_diagnostics + print_info('Backing up browsers') + backup_browsers() - # Reset - if answer_config_browsers and answer_reset_browsers: - print_info('Resetting browsers') - reset_browsers() + # Reset + if answer_config_browsers and answer_reset_browsers: + print_info('Resetting browsers') + reset_browsers() - # Configure - print_info('Configuring programs') - if answer_config_browsers: - install_adblock() - if global_vars['OS']['Version'] == '10': - if answer_config_classicshell: - try_and_print(message='ClassicStart...', - function=config_classicstart, cs='Done') - if answer_config_explorer_user: - try_and_print(message='Explorer...', - function=config_explorer_user, cs='Done') - if (not answer_config_browsers - and not answer_config_classicshell - and not answer_config_explorer_user): - print_warning(' Skipped') - else: - if not answer_config_browsers: - print_warning(' Skipped') + # Configure + print_info('Configuring programs') + if answer_config_browsers: + install_adblock() + if global_vars['OS']['Version'] == '10': + if answer_config_classicshell: + try_and_print(message='ClassicStart...', + function=config_classicstart, cs='Done') + if answer_config_explorer_user: + try_and_print(message='Explorer...', + function=config_explorer_user, cs='Done') + if (not answer_config_browsers + and not answer_config_classicshell + and not answer_config_explorer_user): + print_warning(' Skipped') + else: + if not answer_config_browsers: + print_warning(' Skipped') - # Run speedtest - popen_program(['start', '', 'https://fast.com'], shell=True) + # Run speedtest + popen_program(['start', '', 'https://fast.com'], shell=True) - # Done - if not D7_MODE: - print_standard('\nDone.') - pause('Press Enter to exit...') - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + if not D7_MODE: + print_standard('\nDone.') + pause('Press Enter to exit...') + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/user_data_transfer.py b/.bin/Scripts/user_data_transfer.py index 981e235a..e63e0d7d 100644 --- a/.bin/Scripts/user_data_transfer.py +++ b/.bin/Scripts/user_data_transfer.py @@ -4,8 +4,7 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.data import * from functions.repairs import * init_global_vars() @@ -13,54 +12,56 @@ os.system('title {}: User Data Transfer Tool'.format(KIT_NAME_FULL)) set_log_file('User Data Transfer.log') if __name__ == '__main__': - try: - # Prep - stay_awake() - clear_screen() - print_info('{}: User Data Transfer Tool\n'.format(KIT_NAME_FULL)) + try: + # Prep + stay_awake() + clear_screen() + print_info('{}: User Data Transfer Tool\n'.format(KIT_NAME_FULL)) - # Get backup name prefix - ticket_number = get_ticket_number() - if ENABLED_TICKET_NUMBERS: - backup_prefix = ticket_number - else: - backup_prefix = get_simple_string(prompt='Enter backup name prefix') - backup_prefix = backup_prefix.replace(' ', '_') + # Get backup name prefix + ticket_number = get_ticket_number() + if ENABLED_TICKET_NUMBERS: + backup_prefix = ticket_number + else: + backup_prefix = get_simple_string(prompt='Enter backup name prefix') + backup_prefix = backup_prefix.replace(' ', '_') - # Set destination - folder_path = r'{}\Transfer'.format(KIT_NAME_SHORT) - dest = select_destination(folder_path=folder_path, - prompt='Which disk are we transferring to?') + # Set destination + folder_path = r'{}\Transfer'.format(KIT_NAME_SHORT) + dest = select_destination(folder_path=folder_path, + prompt='Which disk are we transferring to?') - # Set source items - source = select_source(backup_prefix) - items = scan_source(source, dest) + # Set source items + source = select_source(backup_prefix) + items = scan_source(source, dest) - # Transfer - clear_screen() - print_info('Transfer Details:\n') - if ENABLED_TICKET_NUMBERS: - show_data('Ticket:', ticket_number) - show_data('Source:', source.path) - show_data('Destination:', dest) + # Transfer + clear_screen() + print_info('Transfer Details:\n') + if ENABLED_TICKET_NUMBERS: + show_data('Ticket:', ticket_number) + show_data('Source:', source.path) + show_data('Destination:', dest) - if (not ask('Proceed with transfer?')): - umount_backup_shares() - abort() + if (not ask('Proceed with transfer?')): + umount_backup_shares() + abort() - print_info('Transferring Data') - transfer_source(source, dest, items) - try_and_print(message='Removing extra files...', - function=cleanup_transfer, cs='Done', dest_path=dest) - umount_backup_shares() + print_info('Transferring Data') + transfer_source(source, dest, items) + try_and_print(message='Removing extra files...', + function=cleanup_transfer, cs='Done', dest_path=dest) + umount_backup_shares() - # Done - try_and_print(message='Running KVRT...', - function=run_kvrt, cs='Started') - print_standard('\nDone.') - pause("Press Enter to exit...") - exit_script() - except SystemExit: - pass - except: - major_exception() + # Done + try_and_print(message='Running KVRT...', + function=run_kvrt, cs='Started') + print_standard('\nDone.') + pause("Press Enter to exit...") + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/windows_updates.py b/.bin/Scripts/windows_updates.py new file mode 100644 index 00000000..53ab7172 --- /dev/null +++ b/.bin/Scripts/windows_updates.py @@ -0,0 +1,48 @@ +# Wizard Kit: Windows updates + +import os +import sys + +# Init +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from functions.windows_updates import * +init_global_vars() +os.system('title {}: Windows Updates Tool'.format(KIT_NAME_FULL)) +set_log_file('Windows Updates Tool.log') + +if __name__ == '__main__': + try: + clear_screen() + print_info('{}: Windows Updates Tool\n'.format(KIT_NAME_FULL)) + + # Check args + if '--disable' in sys.argv: + result = try_and_print( + message='Disabling Windows Updates...', + function=disable_windows_updates) + elif '--enable' in sys.argv: + result = try_and_print( + message='Enabling Windows Updates...', + function=enable_windows_updates) + else: + print_error('Bad mode.') + abort() + + # Check for errors + if not result['CS']: + for line in str(result['Error']).splitlines(): + print_standard(line) + print_standard(' ') + print_error('Error(s) encountered, see above.') + print_standard(' ') + pause('Press Enter to exit... ') + exit_script(1) + + # Done + exit_script() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/winpe_root_menu.py b/.bin/Scripts/winpe_root_menu.py index 743d987b..a9555e07 100644 --- a/.bin/Scripts/winpe_root_menu.py +++ b/.bin/Scripts/winpe_root_menu.py @@ -4,8 +4,7 @@ import os import sys # Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) +sys.path.append(os.path.dirname(os.path.realpath(__file__))) from functions.winpe_menus import * # Fix 7-Zip name TOOLS['SevenZip'].pop('64') @@ -14,9 +13,11 @@ set_title('{}: Root Menu'.format(KIT_NAME_FULL)) set_log_file('WinPE.log') if __name__ == '__main__': - try: - menu_root() - except SystemExit: - pass - except: - major_exception() + try: + menu_root() + except SystemExit: + pass + except: + major_exception() + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/d7ii/Config/CustomApps/Disable Windows Updates.cfg b/.bin/d7ii/Config/CustomApps/Disable Windows Updates.cfg new file mode 100644 index 00000000..8b47cecc --- /dev/null +++ b/.bin/d7ii/Config/CustomApps/Disable Windows Updates.cfg @@ -0,0 +1,34 @@ +[Config] +LastEditDate=8/30/2018 10:49:46 AM +PostRunApp= +AppParms=.bin\Scripts\launchers_for_d7\Disable Windows Updates.cmd +UseFTPServer=0 +AlwaysAttemptDownload=0 +DLafterXdays=5 +AppWait=1 +EmailBeforeExecution=0 +PriorAlert=0 +ServiceWait=0 +AppMsgBox=1 +AppRandomize=0 +SaveConfigAfter=0 +MoveSnatchReports=0 +SnatchReportsToMalwareLogs=1 +RunInCMD=0 +SendEnter=0 +RunWithSystemAccess=0 +IsDLInstaller=0 +32=1 +64=1 +XP=1 +Vista=1 +7=1 +8=1 +Servers=1 +NonDirectURLs=0 +App=WizardKit Launcher.cmd +AutoFlag=0 +WaitOnProcesses=ConEmu.exe;ConEmuC.exe;ConEmu64.exe;ConEmuC64.exe;python.exe +AppDesc=Disable Windows Updates +LogVerbiage=Disable Windows Update services and delete update folders +AppWaitTime=30 diff --git a/.bin/d7ii/Config/CustomApps/Enable Windows Updates.cfg b/.bin/d7ii/Config/CustomApps/Enable Windows Updates.cfg new file mode 100644 index 00000000..bedccff7 --- /dev/null +++ b/.bin/d7ii/Config/CustomApps/Enable Windows Updates.cfg @@ -0,0 +1,34 @@ +[Config] +LastEditDate=8/30/2018 10:49:46 AM +PostRunApp= +AppParms=.bin\Scripts\launchers_for_d7\Enable Windows Updates.cmd +UseFTPServer=0 +AlwaysAttemptDownload=0 +DLafterXdays=5 +AppWait=1 +EmailBeforeExecution=0 +PriorAlert=0 +ServiceWait=0 +AppMsgBox=1 +AppRandomize=0 +SaveConfigAfter=0 +MoveSnatchReports=0 +SnatchReportsToMalwareLogs=1 +RunInCMD=0 +SendEnter=0 +RunWithSystemAccess=0 +IsDLInstaller=0 +32=1 +64=1 +XP=1 +Vista=1 +7=1 +8=1 +Servers=1 +NonDirectURLs=0 +App=WizardKit Launcher.cmd +AutoFlag=0 +WaitOnProcesses=ConEmu.exe;ConEmuC.exe;ConEmu64.exe;ConEmuC64.exe;python.exe +AppDesc=Enable Windows Updates +LogVerbiage=Enable Windows Update services +AppWaitTime=30 diff --git a/.bin/d7ii/Config/Profiles/Default.cfg b/.bin/d7ii/Config/Profiles/Default.cfg index ed1be24b..161e77e5 100644 --- a/.bin/d7ii/Config/Profiles/Default.cfg +++ b/.bin/d7ii/Config/Profiles/Default.cfg @@ -781,6 +781,7 @@ Windows Repair AIO (Auto)=0 WizardKit System Diagnostics=1 1=1 RKill (Auto)=1 +Disable Windows Updates=1 [Malware3] ComboFix=0 ComboFix (Uninstall)=0 diff --git a/.bin/d7ii/Config/SortOrder/MalwareBox2.cfg b/.bin/d7ii/Config/SortOrder/MalwareBox2.cfg index 6e95d5ec..3ef28f76 100644 --- a/.bin/d7ii/Config/SortOrder/MalwareBox2.cfg +++ b/.bin/d7ii/Config/SortOrder/MalwareBox2.cfg @@ -1 +1 @@ -RKill (Auto)|Kaspersky TDSSKiller (Silent)|WizardKit System Diagnostics|34|Emsisoft a2cmd Deep Scan|HitmanPro|1|98| +Disable Windows Updates|98|RKill (Auto)|Kaspersky TDSSKiller (Silent)|WizardKit System Diagnostics|34|Emsisoft a2cmd Deep Scan|HitmanPro|1|98| diff --git a/.cbin/_include/_Office/hb_32.xml b/.cbin/_include/_Office/2016_hb_32.xml similarity index 100% rename from .cbin/_include/_Office/hb_32.xml rename to .cbin/_include/_Office/2016_hb_32.xml diff --git a/.cbin/_include/_Office/hb_64.xml b/.cbin/_include/_Office/2016_hb_64.xml similarity index 100% rename from .cbin/_include/_Office/hb_64.xml rename to .cbin/_include/_Office/2016_hb_64.xml diff --git a/.cbin/_include/_Office/hs_32.xml b/.cbin/_include/_Office/2016_hs_32.xml similarity index 100% rename from .cbin/_include/_Office/hs_32.xml rename to .cbin/_include/_Office/2016_hs_32.xml diff --git a/.cbin/_include/_Office/hs_64.xml b/.cbin/_include/_Office/2016_hs_64.xml similarity index 100% rename from .cbin/_include/_Office/hs_64.xml rename to .cbin/_include/_Office/2016_hs_64.xml diff --git a/.cbin/_include/_Office/2019_hb_32.xml b/.cbin/_include/_Office/2019_hb_32.xml new file mode 100644 index 00000000..d98dd66a --- /dev/null +++ b/.cbin/_include/_Office/2019_hb_32.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.cbin/_include/_Office/2019_hb_64.xml b/.cbin/_include/_Office/2019_hb_64.xml new file mode 100644 index 00000000..29e10c4f --- /dev/null +++ b/.cbin/_include/_Office/2019_hb_64.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.cbin/_include/_Office/2019_hs_32.xml b/.cbin/_include/_Office/2019_hs_32.xml new file mode 100644 index 00000000..3fc1606a --- /dev/null +++ b/.cbin/_include/_Office/2019_hs_32.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.cbin/_include/_Office/2019_hs_64.xml b/.cbin/_include/_Office/2019_hs_64.xml new file mode 100644 index 00000000..3d2956fd --- /dev/null +++ b/.cbin/_include/_Office/2019_hs_64.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.linux_items/authorized_keys b/.linux_items/authorized_keys index e8b34ed3..2f293acb 100644 --- a/.linux_items/authorized_keys +++ b/.linux_items/authorized_keys @@ -1,6 +1,7 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDY12S71BKYLYkVG+D5kXl+Ce76t5Taw35yifV/nYfzYpWEUgEpASsLOfmBIo/rRX6DfESuA1KEqlto0MEdF47WAISIYDGak9U6HTGrpLdGiOa7iLuZL3lDoJW5SR5RmvMGCmukyxnB3H7qAzzMAhJJAM0NkDwfHVDCVwIAcEdgsyFK+d1j2BjbYWKKAXunsJjdigrCPYfrdJnXU6wn9d8DqdPYjpIEay76amhCNiYgBbtFIrKfRNNDfXdDEP2o1APbgq46RswSlTmZ4kStvGhI+gKBfHiWEUIvW8bOCm3PYzvBKWRkLetvzSN7/8M+o8gJ7G41PPISd+mbrp55bGRpwjVGS2qlut6gvDsEJmGOi1XQxMDNqf5N7oCJcMGOwkeaSIR+vQSsRULEz3AWfcMxjGXHoxAfU2Tt3hCCRhsHKxkTV5rmjdIqFeh2pA2aEfEDgfFdHWrNnoccYeiTyhj+62wB83iBUNd7DZ2O5+jUGWyN/yxy7GX/VNsfMJlmEWnjyWX9fLgIJzSzqrLDisskxCWbMh9o7SmLo9zeWRUGs5PVa1QrP9dUGenm9qXBRmcrK3XfLfpFuQnpLutHZWS4SbOHHPGHbnLF/Psoi07O1JxY+reaLUVYAIedQwVYxS49m/ryc0BoLjkOlgv6kCsPC+Fys4sK+0xHO1PMEEUv8Q== twoshirt@dinraal.2shirt.work +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC7MHLxoozeHOmFpjIGaXi8m0LuHZb4eUjX6x50XUlP81oVHbl4iAqw/N4r4j+Lg+lga7xcTeBMNWsa/tGxYkAfF8TP8M0S8XEMOR04r9iA4V6wQ8nGhv99y24NZMJPfiseNjDEwoPYPP5+zpCNfvxeMXxz3EhLFwzSp1dz+f1tBcDd8OtxDAGPn1cIM4Ms9gmIv3/HxOinW4IHxEbsZUWtKGPumsuIYwObfBYZPK0WtK23LPYqtReLYbzQmsf5SPpJAx20mwtZm8Y+LBXAKuxWEKzlF4l/ubqjTfMff5t+8FPEyCgRwm8VcMqxA1b04Pkcuq08AuooJuLKHOJ+aSyDWGXrSf3RlUAx8GafYOpZrcMEhQ0Q1a0XqZNTUXrf13qVPBqnKV8asOXrYIP56Z3zp8qoHasT8t5PbYrwvtV7weW/X61cQf/nn1B/BJbJr/rrOOxdqRCsH44OlElrhjwHsuzQbMc4U4wwoazlMmfNKsKUaoBmLPDTfmMBwnCuBrngNY9JsSB778fqwxsyLSfDTcIDW2kGuQ4P/4u5cKNw/q8Zo15Ghw2327kyTK9e4EGPCpBk+s5832FWS0uJLV269R5aZ5dYzYU13djfVOw2ypZTdr55nTlIH/31iSmJS0PBKyRLa2a7YxErt37W3f2U1eUt+j91LeaP5cMKHkWAsw== twoshirt@naydra.2shirt.work ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC593iDKb9EPKHs98Zr+w0+JJZUgQ6fk4EFLQ2dB6Tro26a3tGeNQhgFuic1M04J/W/dHb+tHONHil8abmIfUEw34if/iQdhZDAkSTda6j+8oVrKXKGYRqtt15kAXZ7JxuLmvcrHwyzVfo/YHEpliYVaSU4dp8LJxJPjwiZ3/9SLcphmHqzkiDsGk1J8D2z0rFyPXv0w1sXWSX2JJ/p+viwy0lX9Yj+e1e2ulay0nICbVCoqLDDmzOZBY3SPNAq8pflz9QkXE1jhN3DCjzTox/6O0w1OSHehaRGfU4ob0Uz1kS+TMFB2/xvsvvDl9BLi8UVRZej1anJLb9KhTf3x0VtdavN1T086xCNndQ204HW785upjF9LHSqdFQF7i+tN/Ui56aBJ3v+ONYvfKSylMYaRGIvhyhFGTpMRpwfWJh2LCUGnpEHRbO9Z2f2QpPB7ko2tm0wnjK/9xat1JKM2Q/NeAGfDDTbPm7OGnG1NKBxbNXQpdqTh95d8aRbcXi1jFKlJki1bqKE6UlfiXBvhu+ORtXB3oVPBCQxLPdUvnB44Lfr5j8J+0y/PNTOhz+RlXmLeDHbXtNXhBmm6Jq9x7JwaVC6GFqbfhElerhrCMxK4DZiS0xxPZtnOez4MIPkUfKo7K1gYeFUpV/sn2q1P3jLAEeVKsOGc/aME6xIE11FHw== twoshirt@paya ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDaJkYcxi7eSpRE7i0n2N2q4CHebPqxYc5eLQ2mDbrLpAIRUSdVjqe8RiVLJ5LKejkGMnWbaiDZ59cLWJhDTfqZFCRexVQkA9v8MVHUWINzxqfJFOnz7dKXP0PmggDbuKMIUkY65KtOLC2Cnqs3epmysQxrSf33W3IqPwL2XjJQXRbFLd4UG1bSZtBAAF3C/i7hBzb2iQPIpr1v1rCrW6gVhrgyoN7ZTfMqcKHnTPsUn95Qtyp6NUYKh4ctrPmTcTHFfwmgQiAYQ6jMcH8bNBf1XDmJuEIwaILYVzzrWOXlx84rqaEUdeRbvd4S/G9FcRt9eO3o+jD4hupdyvIDCttP3ct6yeWQVotreZE/5OVwfxr0BNXPBmTiyF2c6cRrMlXpQMoRQyjnD5Eua0VEUJzj2ows5APGmciJudDw4rXoRLOZRX/nDuXqgNJJ6q+MgRx3hor/etbV2mXcmSMdoI8DuybFzHuEb9GRwYhLxzDdDe+zmevx0rl01W8EJImqgrTwMblADbM4VHpC974d5w2s9uoXJg1NY4UVkSjJdK0De0kyGh8hK2pqTqCBh1OznB43Yla5PwM+05JIMySVRuivvZ1+tBh8ZURzIbFFIrdgNiZIooI4hA/m8qz4CTWIxKWstXR08eOufIbjV+R6VJjEssvLmgYw93rUJ4WbQNbaiw== twoshirt@anaconda.1201.com -ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAgEAlnTlZGWMLjyJ2iBwc62m3+ZgfsXMCaCiQox+lbbKDzYwTdr2a0UU4293z4DHEbG/wgBXl1FB17ac/XGfLeJIBjLhQy7FRgbQ/z8PNttyNDvrb48ld2zq5C0ZhTxEn2v63mPzxsyEKVKd+YgaUrSvJHtw+3HgJpXruc5zcKCZf9BNjPW1Bq4lCt5gDc95DTWFsuLIfc0Kk1KntqMNgyuprp7zPIrolTR4FvPCoZyvcp5mXhM+Q1SNVs+XgvGKVW1fZrwvUoRrriqctnfFKPXIo9ChgRfvhbmsOx1MirShNHP7/5qbG1C3Ju0eEEiV8YLndvkLgprkyG8RYJG6CnXBjI3no7bxhHPfRnAcflPEaGpuvGnDzE5V8K8mhVk+kFI13nPQcuuvJajX2hAiz4xaJD5JUsAv64Gpj6J6cc1/8rOT1wlWzA0r3QYjD6A5dheMwAzdIO1p4TGO2XlUkvM/Hs4LOHG1BodsCpDr5GIEnKawms+9CLV4VeBhnik6W45ZkY1dqp/Qhfjju2pWXYyisrsinUHB2TPuTTQHxjEjqlJQJ8i6wjiB4p0ruyIups24kXXENiwCRjCqhm5WRXdr5h79uzYZMUJCejY9Dn9o953410XjzwFLY4Q6w5FjQb+MjoKACUnYaHPCpK2qxzQN9CR2GUPqDi3dRKMw8TgblV0= Ryokou\2Shirt ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAgEAhF5u/wn04dSDI1mg6HlbDhmM3orJtu2kZdOTBd+/kvCxdC4h+AuwRSWjbIhGi9s8R3iq64gC6TJ2dX4skJ5fMeYqHMZieGgfXderRiPgMURCDWONLZCWyowBun5qGaXkYQo5VbLUNGXua9+RMXaxoJSCOhxq/6CzdleD6pkNNgL5a1ZarD3wLCNRsS50OmyGytXsYTClAY+F9lTd4VXjKuZpjqGfhA5Xz09Rad3Me2Tsd07cdO3LxDNlr969Q/hEwfZ/g+ePaIO0Z/zFHIO4J6Mnt8POTU1fM99tgqUQHWZRP2A+9OYx7nApdA4IFWVUhNfsVkrgcgYdCLD8U16Cdfwm1i1RiBhlYBfzA+EDjD6cbegaaMCKhsCZMZKQB4LyJTDgjvCrk1Q7dE0Nc1Nq8qD/BMbZkIKQcQH8xrX1hONayUMdOPnpDpi6IP2NIYFTbdJD70JQ8ru+gIDRQJR6g0AwnjMoJYNRKgAtWlcbKQ8YQW/FNREtyUhh8tquCyFbofGUiJxmTbWki7u7VJxRLNSnp+NQhPNT4FUbWy1vPrJi0l3MxuXdG3nZ0Brnggn72tnGcAUOmRTPubNlkhFStqcCM+tAVTHeLwHobdMewKQMWGPt6UXLtEJINqW9nCh1SAOA3cjsZ3ugXLRQQc82rEgTfTp2RtuN+NSf/vgoDbk= 1201 PuTTY Key ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGZqPA7/T3zw8YipngM6vKpxX+IMpwqm2jad+NcJWxC0Hh09oVUalgbO/eWRloQlNgxHTosdIkSp++xnyeEHI3vL0pbko/c0OJbs6vEsHp8ljzt61HKKsscVADCI/ogGwHkPIHENgbiXkBPutEBLYFPtMcJQw6GBIkagW+dTyqEw/8c1cDmTq7AM74tHqgrpYjJy35Nne0K+zGIeqzTJdtg3fkRCN9JH24c3VbYoeIpMlgfxxgRG44DC/o0BRoMr3wWiS/sTrutlluB03vaQWe+4o8p789wq+fDqKhBTisH96RQ1Y1D5eZcf0DngBJtnCLWPG+z6YP0DFW5DYeQRoYNneRkR10RUqN0Eufok/UMNnxWcW+TztTxO5JSucXt0MYpLsWO1U7B22MR1W7oVlbznUrVENSQGRqyOS4PdqmI4faF0vEKis68S7NaXg88zwBr612HwFBianXvvxhkyEKv8ECbeXSvHcIajepdzbGLwaussDQiNv8vyX/8o3K7Si/ktdgIK4UzfaMq7G0vX1KEvuKAfMPYn4z0zuc/ahxTTfvHqfO+cnYgH8Pnzi1DGziRveeKIFT4OKOP5C3wHntsfv1CStK/LXhH3j4PNRuet0Fm5+jUJ22DQqlXtFiwFz4E+Bt+67exhm4Out4rAUqCzjDW5+mw0WU2P7u01cT+Q== admin@anaconda.1201.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCatoW5AZa8JlvA7WZD3GkB3IMXtCClxGEtpeEb2q/6Ltd8+/EngGgfjm2buBdHsRY4y+8Q/dZnMXbD1kfSw8pv7q4WUy2RHccM6xNpkhhioRzLebWc3m8C3ySFx0uTipZCgAG0IBGINeHHl3zeWo5M21WSPNAjZsUUX24z1TszXP/nkDMh24593OGbyqDydXIz6xWPtZOo0Ot8Y4yplXipO6yJUjGianSJ1DVQCwFj5G2XgDdqHJDTsKXcHDAVZU4WyB7br6ONZLj8How4FX15o68Zu/9m5EVuoR1vf7iBZev6sE9/1rub5IIW7f3ADXf+z4N2cuhOJSwoKxxf669U2Fm3ZegQdn3+P5yONuPzI2w3f5FZtyQE22NBLMzqRv+9WZxILPq4ocGojMck9rbvYXEaDYTOYBbjWjVZK9qhCFilUwSzv9mtBN6/3thHumCod7PVuVcldd2BJutIEwJ+lRqTKsuHkj9ybSo+kh3ycDtZhpERLNE5EAaJEnXTGraSz8MgKaI9FsCDFVwdJDKz897vgjd8WyFgGSEKI8pmqZcpGK4243yCZ5Xuqe73i18sPZrQtg6nfyYR0GksH7qBW9zbmgw4D0Zmctxvg8imsMrfHGIhipHS8OozEuabbxAqgCIWzjcVSMWAnontu7DcLOzQ3GW+mFVd/aItXsnfQw== admin@management-1 diff --git a/.linux_items/include/EFI/boot/icons/1201_hdclone.png b/.linux_items/include/EFI/boot/icons/1201_hdclone.png index 997ffe5d..3c4a5f35 100644 Binary files a/.linux_items/include/EFI/boot/icons/1201_hdclone.png and b/.linux_items/include/EFI/boot/icons/1201_hdclone.png differ diff --git a/.linux_items/include/EFI/boot/refind.conf b/.linux_items/include/EFI/boot/refind.conf index 5ca39701..86b1a835 100644 --- a/.linux_items/include/EFI/boot/refind.conf +++ b/.linux_items/include/EFI/boot/refind.conf @@ -32,42 +32,6 @@ menuentry "Linux" { submenuentry "Linux (CLI)" { add_options "loglevel=4 nomodeset nox" } - submenuentry "Linux (Mac CLI)" { - add_options "loglevel=5 nomodeset nox" - } - submenuentry "Linux (MacBook9,1)" { - add_options "loglevel=5 intremap=nosid noacpi nomodeset" - } - submenuentry "Linux (MacBookAir5,2)" { - add_options "loglevel=5 intremap=off" - } - submenuentry "Linux (MacBookAir6,x)" { - add_options "loglevel=5 libata.force=1:noncq" - } - submenuentry "Linux (MacBookPro7,1)" { - add_options "loglevel=5 acpi_osi=! acpi_osi="Darwin" intremap=off nomodeset" - } - submenuentry "Linux (MacBookPro10,x)" { - add_options "loglevel=5 noapic" - } - submenuentry "Linux (MacBookPro11,x)" { - add_options "loglevel=5 acpi_osi=" - } - submenuentry "Linux (Mac Generic Fix 1)" { - add_options "loglevel=5 acpi=force irqpoll noapic" - } - submenuentry "Linux (Mac Generic Fix 2)" { - add_options "loglevel=5 acpi=off" - } - submenuentry "Linux (Mac Generic Fix 3)" { - add_options "loglevel=5 acpi_osi=! acpi_osi="Darwin"" - } - submenuentry "Linux (Mac Generic Fix 4)" { - add_options "loglevel=5 add_efi_memmap" - } - submenuentry "Linux (DEBUG)" { - add_options "loglevel=7 nomodeset nox" - } } #UFD#menuentry "WindowsPE" { #UFD# ostype windows @@ -80,7 +44,7 @@ menuentry "Linux" { #UFD#} #UFD#menuentry "HDClone" { #UFD# icon /EFI/boot/icons/1201_hdclone.png -#UFD# loader /EFI/HDClone/grub.efi +#UFD# loader /EFI/HDClone/bootx64.efi #UFD#} #UFD#menuentry "Mac dGPU Disable Tool" { #UFD# icon /EFI/boot/icons/1201_mac-dgpu.png diff --git a/.linux_items/include/airootfs/etc/pydfrc b/.linux_items/include/airootfs/etc/pydfrc deleted file mode 100644 index 100b1f05..00000000 --- a/.linux_items/include/airootfs/etc/pydfrc +++ /dev/null @@ -1,24 +0,0 @@ -normal_colour = 'default' -header_colour = 'blue' -local_fs_colour = 'default' -remote_fs_colour = 'green' -special_fs_colour = 'yellow' -readonly_fs_colour = 'cyan' -filled_fs_colour = 'red' -full_fs_colour = 'on_red', 'green', 'blink' -sizeformat = "-h" -column_separator = ' ' -column_separator_colour = 'none' -stretch_screen = 0.3 -FILL_THRESH = 75.0 -FULL_THRESH = 85.0 -format = [ - ('fs', 10, "l"), ('size', 5, "r"), - ('used', 5, "r"), ('avail', 5, "r"), ('perc', 5, "r"), - ('bar', 0.1, "l"), ('on', 11, "l") - ] -barchar = '#' -bar_fillchar = '.' -hidebinds = True -mountfile = ['/etc/mtab', '/etc/mnttab', '/proc/mounts'] - diff --git a/.linux_items/include/airootfs/etc/skel/.zlogin b/.linux_items/include/airootfs/etc/skel/.zlogin index ecdc1bbf..20e22819 100644 --- a/.linux_items/include/airootfs/etc/skel/.zlogin +++ b/.linux_items/include/airootfs/etc/skel/.zlogin @@ -6,19 +6,6 @@ if [ "$(fgconsole 2>/dev/null)" -eq "1" ]; then # Trust added root CAs sudo trust extract-compat - # Update settings if using i3 - if fgrep -q "i3" /proc/cmdline; then - sed -i -r 's/#(own_window_type override)/\1/' ~/.conkyrc - sed -i -r 's/openbox-session/i3/' ~/.xinitrc - fi - - # Update Conky - $HOME/.update_conky - - # Start X or HW-diags - if ! fgrep -q "nox" /proc/cmdline; then - startx >/dev/null - else - hw-diags cli - fi + # Start HW-diags + hw-diags --cli fi diff --git a/.linux_items/include/syslinux/1201_hdclone.cfg b/.linux_items/include/syslinux/1201_hdclone.cfg index a97b3db9..84c40c4b 100644 --- a/.linux_items/include/syslinux/1201_hdclone.cfg +++ b/.linux_items/include/syslinux/1201_hdclone.cfg @@ -3,7 +3,7 @@ TEXT HELP HDClone by Miray Software * Backups, cloning, etc ENDTEXT -MENU LABEL HDClone 6 +MENU LABEL HDClone 5 LINUX boot/syslinux/memdisk INITRD ../sources/hdclone.iso APPEND iso diff --git a/.linux_items/include/syslinux/wk_sys_linux.cfg b/.linux_items/include/syslinux/wk_sys_linux.cfg index b65047ad..9c229249 100644 --- a/.linux_items/include/syslinux/wk_sys_linux.cfg +++ b/.linux_items/include/syslinux/wk_sys_linux.cfg @@ -12,6 +12,19 @@ LABEL wk_sys_linux_extras TEXT HELP Show extra Linux options ENDTEXT -MENU LABEL Linux (Extras) -KERNEL vesamenu.c32 -APPEND boot/syslinux/wk_sys_linux_extras.cfg +MENU LABEL Linux (i3) +LINUX boot/x86_64/vmlinuz +INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img +APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=3 i3 +SYSAPPEND 3 + +LABEL wk_linux_cli +TEXT HELP +A live Linux environment (CLI) + * HW diagnostics, file-based backups, data recovery, etc +ENDTEXT +MENU LABEL Linux (CLI) +LINUX boot/x86_64/vmlinuz +INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img +APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram nox nomodeset +SYSAPPEND 3 diff --git a/.linux_items/include/syslinux/wk_sys_linux_extras.cfg b/.linux_items/include/syslinux/wk_sys_linux_extras.cfg deleted file mode 100644 index 979be787..00000000 --- a/.linux_items/include/syslinux/wk_sys_linux_extras.cfg +++ /dev/null @@ -1,198 +0,0 @@ -INCLUDE boot/syslinux/wk_head.cfg - -LABEL wk_linux -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=3 - -LABEL wk_linux_i3 -TEXT HELP -A live Linux environment (i3) - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (i3) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=3 i3 -SYSAPPEND 3 - -LABEL wk_linux_cli -TEXT HELP -A live Linux environment (CLI) - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (CLI) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=4 nomodeset nox -SYSAPPEND 3 - -LABEL wk_linux_mac_generic -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (Mac) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 reboot=pci -SYSAPPEND 3 - -LABEL wk_linux_mac_cli -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (Mac CLI) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 nomodeset nox reboot=pci -SYSAPPEND 3 - -MENU SEPARATOR - -LABEL wk_linux_macbook52 -TEXT HELP -A live Linux environment - * WARNING System will be limited to one CPU/thread - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (MacBook5,2) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 acpi=off irqpoll maxcpus=1 noapic reboot=pci -SYSAPPEND 3 - -LABEL wk_linux_macbook91 -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (MacBook9,1) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 intremap=nosid noacpi nomodeset reboot=pci -SYSAPPEND 3 - -MENU SEPARATOR - -LABEL wk_linux_macbookair52 -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (MacBookAir5,2) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 intremap=off reboot=pci -SYSAPPEND 3 - -LABEL wk_linux_macbookair6_ -TEXT HELP -A live Linux environment - * WARNING Drive I/O performance will be impacted - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (MacBookAir6,x) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 libata.force=1:noncq reboot=pci -SYSAPPEND 3 - -MENU SEPARATOR - -LABEL wk_linux_macbookpro71 -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (MacBookPro7,1) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 acpi_osi=! acpi_osi="Darwin" intremap=off nomodeset reboot=pci -SYSAPPEND 3 - -LABEL wk_linux_macbookpro10_ -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (MacBookPro10,x) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 noapic reboot=pci -SYSAPPEND 3 - -LABEL wk_linux_macbookpro11_ -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (MacBookPro11,x) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 acpi_osi= reboot=pci -SYSAPPEND 3 - -MENU SEPARATOR - -LABEL wk_linux_mac_misc1 -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (Misc Mac Fix 1) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 acpi=force irqpoll noapic reboot=pci -SYSAPPEND 3 - -LABEL wk_linux_mac_misc2 -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (Misc Mac Fix 2) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 acpi=off reboot=pci -SYSAPPEND 3 - -LABEL wk_linux_mac_misc3 -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (Misc Mac Fix 3) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=5 acpi_osi=! acpi_osi="Darwin" reboot=pci -SYSAPPEND 3 - -MENU SEPARATOR - -LABEL wk_linux_debug -TEXT HELP -A live Linux environment - * HW diagnostics, file-based backups, data recovery, etc -ENDTEXT -MENU LABEL Linux (DEBUG) -LINUX boot/x86_64/vmlinuz -INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=7 nomodeset nox -SYSAPPEND 3 - -MENU SEPARATOR - -LABEL wk_return -TEXT HELP -Show Return to the main menu -ENDTEXT -MENU LABEL Main Menu -KERNEL vesamenu.c32 -APPEND boot/syslinux/wk_sys.cfg diff --git a/.linux_items/include/airootfs/etc/oblogout.conf b/.linux_items/include_x/airootfs/etc/oblogout.conf similarity index 100% rename from .linux_items/include/airootfs/etc/oblogout.conf rename to .linux_items/include_x/airootfs/etc/oblogout.conf diff --git a/.linux_items/include/airootfs/etc/skel/.Xresources b/.linux_items/include_x/airootfs/etc/skel/.Xresources similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.Xresources rename to .linux_items/include_x/airootfs/etc/skel/.Xresources diff --git a/.linux_items/include/airootfs/etc/skel/.config/Thunar/accels.scm b/.linux_items/include_x/airootfs/etc/skel/.config/Thunar/accels.scm similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/Thunar/accels.scm rename to .linux_items/include_x/airootfs/etc/skel/.config/Thunar/accels.scm diff --git a/.linux_items/include/airootfs/etc/skel/.config/Thunar/uca.xml b/.linux_items/include_x/airootfs/etc/skel/.config/Thunar/uca.xml similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/Thunar/uca.xml rename to .linux_items/include_x/airootfs/etc/skel/.config/Thunar/uca.xml diff --git a/.linux_items/include/airootfs/etc/skel/.config/dunst/dunstrc b/.linux_items/include_x/airootfs/etc/skel/.config/dunst/dunstrc similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/dunst/dunstrc rename to .linux_items/include_x/airootfs/etc/skel/.config/dunst/dunstrc diff --git a/.linux_items/include/airootfs/etc/skel/.config/gtk-3.0/settings.ini b/.linux_items/include_x/airootfs/etc/skel/.config/gtk-3.0/settings.ini similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/gtk-3.0/settings.ini rename to .linux_items/include_x/airootfs/etc/skel/.config/gtk-3.0/settings.ini diff --git a/.linux_items/include/airootfs/etc/skel/.config/i3/config b/.linux_items/include_x/airootfs/etc/skel/.config/i3/config similarity index 99% rename from .linux_items/include/airootfs/etc/skel/.config/i3/config rename to .linux_items/include_x/airootfs/etc/skel/.config/i3/config index ca1a5439..102b50f5 100644 --- a/.linux_items/include/airootfs/etc/skel/.config/i3/config +++ b/.linux_items/include_x/airootfs/etc/skel/.config/i3/config @@ -72,7 +72,7 @@ bindsym $mod+d exec "urxvt -title 'Hardware Diagnostics' -e hw-diags" bindsym $mod+f exec "thunar ~" bindsym $mod+i exec "hardinfo" bindsym $mod+m exec "urxvt -title 'Mount All Volumes' -e mount-all-volumes gui" -bindsym $mod+s exec "urxvt -title 'Hardware Diagnostics' -e hw-diags quick" +bindsym $mod+s exec "urxvt -title 'Hardware Diagnostics' -e hw-diags --quick" bindsym $mod+t exec "urxvt -e zsh -c 'tmux new-session -A -t general; zsh'" bindsym $mod+v exec "urxvt -title 'Hardware Sensors' -e watch -c -n1 -t hw-sensors" bindsym $mod+w exec "firefox" diff --git a/.linux_items/include/airootfs/etc/skel/.config/i3status/config b/.linux_items/include_x/airootfs/etc/skel/.config/i3status/config similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/i3status/config rename to .linux_items/include_x/airootfs/etc/skel/.config/i3status/config diff --git a/.linux_items/include/airootfs/etc/skel/.config/mimeapps.list b/.linux_items/include_x/airootfs/etc/skel/.config/mimeapps.list similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/mimeapps.list rename to .linux_items/include_x/airootfs/etc/skel/.config/mimeapps.list diff --git a/.linux_items/include/airootfs/etc/skel/.config/openbox/autostart b/.linux_items/include_x/airootfs/etc/skel/.config/openbox/autostart similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/openbox/autostart rename to .linux_items/include_x/airootfs/etc/skel/.config/openbox/autostart diff --git a/.linux_items/include/airootfs/etc/skel/.config/openbox/environment b/.linux_items/include_x/airootfs/etc/skel/.config/openbox/environment similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/openbox/environment rename to .linux_items/include_x/airootfs/etc/skel/.config/openbox/environment diff --git a/.linux_items/include/airootfs/etc/skel/.config/openbox/menu.xml b/.linux_items/include_x/airootfs/etc/skel/.config/openbox/menu.xml similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/openbox/menu.xml rename to .linux_items/include_x/airootfs/etc/skel/.config/openbox/menu.xml diff --git a/.linux_items/include/airootfs/etc/skel/.config/openbox/rc.xml b/.linux_items/include_x/airootfs/etc/skel/.config/openbox/rc.xml similarity index 99% rename from .linux_items/include/airootfs/etc/skel/.config/openbox/rc.xml rename to .linux_items/include_x/airootfs/etc/skel/.config/openbox/rc.xml index 90c7b0e0..e563ec04 100644 --- a/.linux_items/include/airootfs/etc/skel/.config/openbox/rc.xml +++ b/.linux_items/include_x/airootfs/etc/skel/.config/openbox/rc.xml @@ -324,7 +324,7 @@ - urxvt -title "Hardware Diagnostics" -e hw-diags quick + urxvt -title "Hardware Diagnostics" -e hw-diags --quick diff --git a/.linux_items/include/airootfs/etc/skel/.config/rofi/config b/.linux_items/include_x/airootfs/etc/skel/.config/rofi/config similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/rofi/config rename to .linux_items/include_x/airootfs/etc/skel/.config/rofi/config diff --git a/.linux_items/include/airootfs/etc/skel/.config/tint2/tint2rc b/.linux_items/include_x/airootfs/etc/skel/.config/tint2/tint2rc similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/tint2/tint2rc rename to .linux_items/include_x/airootfs/etc/skel/.config/tint2/tint2rc diff --git a/.linux_items/include/airootfs/etc/skel/.config/volumeicon/volumeicon b/.linux_items/include_x/airootfs/etc/skel/.config/volumeicon/volumeicon similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.config/volumeicon/volumeicon rename to .linux_items/include_x/airootfs/etc/skel/.config/volumeicon/volumeicon diff --git a/.linux_items/include/airootfs/etc/skel/.conky_start b/.linux_items/include_x/airootfs/etc/skel/.conky_start similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.conky_start rename to .linux_items/include_x/airootfs/etc/skel/.conky_start diff --git a/.linux_items/include/airootfs/etc/skel/.conkyrc b/.linux_items/include_x/airootfs/etc/skel/.conkyrc similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.conkyrc rename to .linux_items/include_x/airootfs/etc/skel/.conkyrc diff --git a/.linux_items/include/airootfs/etc/skel/.gtkrc-2.0 b/.linux_items/include_x/airootfs/etc/skel/.gtkrc-2.0 similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.gtkrc-2.0 rename to .linux_items/include_x/airootfs/etc/skel/.gtkrc-2.0 diff --git a/.linux_items/include/airootfs/etc/skel/.update_conky b/.linux_items/include_x/airootfs/etc/skel/.update_conky similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.update_conky rename to .linux_items/include_x/airootfs/etc/skel/.update_conky diff --git a/.linux_items/include/airootfs/etc/skel/.update_x b/.linux_items/include_x/airootfs/etc/skel/.update_x similarity index 97% rename from .linux_items/include/airootfs/etc/skel/.update_x rename to .linux_items/include_x/airootfs/etc/skel/.update_x index 4763a175..650d3162 100755 --- a/.linux_items/include/airootfs/etc/skel/.update_x +++ b/.linux_items/include_x/airootfs/etc/skel/.update_x @@ -92,3 +92,6 @@ else cbatticon --hide-notification & fi +# Prevent Xorg from being killed by .zlogin +touch "/tmp/x_ok" + diff --git a/.linux_items/include/airootfs/etc/skel/.wallpaper b/.linux_items/include_x/airootfs/etc/skel/.wallpaper similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.wallpaper rename to .linux_items/include_x/airootfs/etc/skel/.wallpaper diff --git a/.linux_items/include/airootfs/etc/skel/.xinitrc b/.linux_items/include_x/airootfs/etc/skel/.xinitrc similarity index 100% rename from .linux_items/include/airootfs/etc/skel/.xinitrc rename to .linux_items/include_x/airootfs/etc/skel/.xinitrc diff --git a/.linux_items/include_x/airootfs/etc/skel/.zlogin b/.linux_items/include_x/airootfs/etc/skel/.zlogin new file mode 100644 index 00000000..775ac320 --- /dev/null +++ b/.linux_items/include_x/airootfs/etc/skel/.zlogin @@ -0,0 +1,35 @@ +setterm -blank 0 -powerdown 0 2>/dev/null +if [ "$(fgconsole 2>/dev/null)" -eq "1" ]; then + # Connect to network and update hostname + $HOME/.update_network + + # Trust added root CAs + sudo trust extract-compat + + # Update settings if using i3 + if fgrep -q "i3" /proc/cmdline; then + sed -i -r 's/#(own_window_type override)/\1/' ~/.conkyrc + sed -i -r 's/openbox-session/i3/' ~/.xinitrc + fi + + # Update Conky + $HOME/.update_conky + + # Start X or HW-diags + if ! fgrep -q "nox" /proc/cmdline; then + # Kill Xorg after 30 seconds if it doesn't fully initialize + (sleep 30s; if ! [[ -f "/tmp/x_ok" ]]; then pkill '(Xorg|startx)'; fi) & + + # Try starting X + startx >/dev/null + + # Run Hw-Diags CLI if necessary + if ! [[ -f "/tmp/x_ok" ]]; then + echo "There was an issue starting Xorg, starting CLI interface..." + sleep 2s + hw-diags --cli + fi + else + hw-diags --cli + fi +fi diff --git a/.linux_items/include/airootfs/usr/share/applications/Hardware Diagnostics.desktop b/.linux_items/include_x/airootfs/usr/share/applications/Hardware Diagnostics.desktop similarity index 100% rename from .linux_items/include/airootfs/usr/share/applications/Hardware Diagnostics.desktop rename to .linux_items/include_x/airootfs/usr/share/applications/Hardware Diagnostics.desktop diff --git a/.linux_items/include/airootfs/usr/share/applications/Hardware Information.desktop b/.linux_items/include_x/airootfs/usr/share/applications/Hardware Information.desktop similarity index 100% rename from .linux_items/include/airootfs/usr/share/applications/Hardware Information.desktop rename to .linux_items/include_x/airootfs/usr/share/applications/Hardware Information.desktop diff --git a/.linux_items/include/airootfs/usr/share/applications/NetworkTest.desktop b/.linux_items/include_x/airootfs/usr/share/applications/NetworkTest.desktop similarity index 100% rename from .linux_items/include/airootfs/usr/share/applications/NetworkTest.desktop rename to .linux_items/include_x/airootfs/usr/share/applications/NetworkTest.desktop diff --git a/.linux_items/packages/aur b/.linux_items/packages/aur index 326ca732..9588b129 100644 --- a/.linux_items/packages/aur +++ b/.linux_items/packages/aur @@ -2,8 +2,8 @@ aic94xx-firmware bash-pipes hfsprogs i3lock-fancy-git +macbook12-spi-driver-dkms mprime -nvme-cli openbox-patched smartmontools-svn testdisk-wip diff --git a/.linux_items/packages/live_add b/.linux_items/packages/live_add index a23f2884..1c22daa2 100644 --- a/.linux_items/packages/live_add +++ b/.linux_items/packages/live_add @@ -1,90 +1,50 @@ aic94xx-firmware alsa-utils antiword -arandr -arc-gtk-theme bash-pipes bc bluez bluez-utils -cbatticon chntpw cmatrix colordiff -compton -conky cpio curl dmidecode dos2unix -dunst e2fsprogs -evince -feh -ffmpeg -firefox -gnome-keyring -gparted -gpicview-gtk3 -gsmartcontrol -hardinfo hexedit hfsprogs htop -i3-gaps -i3lock-fancy-git -i3status ldns -leafpad lha libewf -libinput linux-firmware lm_sensors lzip mariadb-clients mdadm mediainfo -mesa-demos -mkvtoolnix-cli mprime -mpv ncdu -network-manager-applet networkmanager -noto-fonts -noto-fonts-cjk -nvme-cli -oblogout -openbox-patched -otf-font-awesome-4 p7zip -papirus-icon-theme progsreiserfs python python-gnuplot python-mysql-connector python-psutil python-requests -qemu-guest-agent reiserfsprogs rfkill rng-tools -rofi -rxvt-unicode +rxvt-unicode-terminfo smartmontools-svn speedtest-cli -spice-vdagent terminus-font testdisk-wip -thunar -tigervnc -tint2 -tk tmux tree -ttf-font-awesome-4 -ttf-inconsolata udevil udisks2 ufw @@ -92,23 +52,8 @@ unarj unrar unzip util-linux -veracrypt vim -virtualbox-guest-modules-arch -virtualbox-guest-utils -volumeicon wd719x-firmware wimlib -xarchiver -xf86-input-libinput -xf86-video-amdgpu -xf86-video-fbdev -xf86-video-nouveau -xf86-video-vesa -xorg-server -xorg-xdpyinfo -xorg-xev -xorg-xinit -xorg-xinput zip zsh diff --git a/.linux_items/packages/live_add_min b/.linux_items/packages/live_add_min new file mode 100644 index 00000000..b37bdc74 --- /dev/null +++ b/.linux_items/packages/live_add_min @@ -0,0 +1,2 @@ +linux-headers +macbook12-spi-driver-dkms diff --git a/.linux_items/packages/live_add_x b/.linux_items/packages/live_add_x new file mode 100644 index 00000000..ab7d23b3 --- /dev/null +++ b/.linux_items/packages/live_add_x @@ -0,0 +1,55 @@ +arandr +arc-gtk-theme +cbatticon +compton +conky +dunst +evince +feh +ffmpeg +firefox +gnome-keyring +gparted +gpicview-gtk3 +gsmartcontrol +hardinfo +i3-gaps +i3lock-fancy-git +i3status +leafpad +libinput +mesa-demos +mkvtoolnix-cli +mpv +network-manager-applet +noto-fonts +noto-fonts-cjk +oblogout +openbox-patched +otf-font-awesome-4 +papirus-icon-theme +qemu-guest-agent +rofi +rxvt-unicode +spice-vdagent +thunar +tigervnc +tint2 +tk +ttf-font-awesome-4 +ttf-inconsolata +veracrypt +virtualbox-guest-modules-arch +virtualbox-guest-utils +volumeicon +xarchiver +xf86-input-libinput +xf86-video-amdgpu +xf86-video-fbdev +xf86-video-nouveau +xf86-video-vesa +xorg-server +xorg-xdpyinfo +xorg-xev +xorg-xinit +xorg-xinput diff --git a/Build Linux b/Build Linux index 56233d51..4ccd1729 100755 --- a/Build Linux +++ b/Build Linux @@ -119,6 +119,9 @@ function copy_live_env() { # Add items rsync -aI "$ROOT_DIR/.linux_items/include/" "$LIVE_DIR/" + if [[ "${1:-}" != "--minimal" ]]; then + rsync -aI "$ROOT_DIR/.linux_items/include_x/" "$LIVE_DIR/" + fi mkdir -p "$LIVE_DIR/airootfs/usr/local/bin" rsync -aI "$ROOT_DIR/.bin/Scripts/" "$LIVE_DIR/airootfs/usr/local/bin/" cp -a "$BUILD_DIR/main.py" "$LIVE_DIR/airootfs/usr/local/bin/settings/" @@ -170,13 +173,15 @@ function update_live_env() { # Memtest86 mkdir -p "$LIVE_DIR/EFI/memtest86/Benchmark" mkdir -p "$TEMP_DIR/memtest86" - curl -Lo "$TEMP_DIR/memtest86/memtest86.iso.tar.gz" "https://www.memtest86.com/downloads/memtest86-iso.tar.gz" - tar xvf "$TEMP_DIR/memtest86/memtest86.iso.tar.gz" -C "$TEMP_DIR/memtest86" - 7z x "$TEMP_DIR/memtest86"/*.iso -o"$TEMP_DIR/memtest86" - mv "$TEMP_DIR/memtest86/EFI/BOOT/BLACKLIS.CFG" "$LIVE_DIR/EFI/memtest86/blacklist.cfg" - mv "$TEMP_DIR/memtest86/EFI/BOOT/BOOTX64.EFI" "$LIVE_DIR/EFI/memtest86/memtestx64.efi" - mv "$TEMP_DIR/memtest86/EFI/BOOT/MT86.PNG" "$LIVE_DIR/EFI/memtest86/mt86.png" - mv "$TEMP_DIR/memtest86/EFI/BOOT/UNIFONT.BIN" "$LIVE_DIR/EFI/memtest86/unifont.bin" + curl -Lo "$TEMP_DIR/memtest86/memtest86-usb.zip" "https://www.memtest86.com/downloads/memtest86-usb.zip" + 7z e "$TEMP_DIR/memtest86/memtest86-usb.zip" -o"$TEMP_DIR/memtest86" "memtest86-usb.img" + 7z e "$TEMP_DIR/memtest86/memtest86-usb.img" -o"$TEMP_DIR/memtest86" "MemTest86.img" + 7z x "$TEMP_DIR/memtest86/MemTest86.img" -o"$TEMP_DIR/memtest86" + rm "$TEMP_DIR/memtest86/EFI/BOOT/BOOTIA32.efi" + mv "$TEMP_DIR/memtest86/EFI/BOOT/BOOTX64.efi" "$LIVE_DIR/EFI/memtest86/memtestx64.efi" + mv "$TEMP_DIR/memtest86/EFI/BOOT"/* "$LIVE_DIR/EFI/memtest86"/ + mv "$TEMP_DIR/memtest86/help"/* "$LIVE_DIR/EFI/memtest86"/ + mv "$TEMP_DIR/memtest86/license.rtf" "$LIVE_DIR/EFI/memtest86"/ # build.sh if ! grep -iq 'wizardkit additions' "$LIVE_DIR/build.sh"; then @@ -193,6 +198,11 @@ function update_live_env() { sed -i "/$p/d" "$LIVE_DIR/packages.x86_64" done < "$ROOT_DIR/.linux_items/packages/live_remove" cat "$ROOT_DIR/.linux_items/packages/live_add" >> "$LIVE_DIR/packages.x86_64" + if [[ "${1:-}" == "--minimal" ]]; then + cat "$ROOT_DIR/.linux_items/packages/live_add_min" >> "$LIVE_DIR/packages.x86_64" + else + cat "$ROOT_DIR/.linux_items/packages/live_add_x" >> "$LIVE_DIR/packages.x86_64" + fi echo "[custom]" >> "$LIVE_DIR/pacman.conf" echo "SigLevel = Optional TrustAll" >> "$LIVE_DIR/pacman.conf" echo "Server = file://$REPO_DIR" >> "$LIVE_DIR/pacman.conf" @@ -211,10 +221,12 @@ function update_live_env() { rm -Rf "$SKEL_DIR/.oh-my-zsh/.git" curl -o "$SKEL_DIR/.oh-my-zsh/themes/lean.zsh-theme" https://raw.githubusercontent.com/miekg/lean/master/prompt_lean_setup - # Openbox theme - git clone --depth=1 https://github.com/addy-dclxvi/Openbox-Theme-Collections.git "$TEMP_DIR/ob-themes" - mkdir -p "$LIVE_DIR/airootfs/usr/share/themes" - cp -a "$TEMP_DIR/ob-themes/Triste-Orange" "$LIVE_DIR/airootfs/usr/share/themes/" + if [[ "${1:-}" != "--minimal" ]]; then + # Openbox theme + git clone --depth=1 https://github.com/addy-dclxvi/Openbox-Theme-Collections.git "$TEMP_DIR/ob-themes" + mkdir -p "$LIVE_DIR/airootfs/usr/share/themes" + cp -a "$TEMP_DIR/ob-themes/Triste-Orange" "$LIVE_DIR/airootfs/usr/share/themes/" + fi # Services sed -i -r 's/^(.*pacman-init.*)$/#NOPE#\1/' "$LIVE_DIR/airootfs/root/customize_airootfs.sh" @@ -257,13 +269,15 @@ function update_live_env() { # udevil fix echo "mkdir /media" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" - # VNC password - echo "mkdir '/home/$username/.vnc'" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" - echo "echo '$TECH_PASSWORD' | vncpasswd -f > '/home/$username/.vnc/passwd'" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" + if [[ "${1:-}" != "--minimal" ]]; then + # VNC password + echo "mkdir '/home/$username/.vnc'" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" + echo "echo '$TECH_PASSWORD' | vncpasswd -f > '/home/$username/.vnc/passwd'" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" - # Wallpaper - mkdir -p "$LIVE_DIR/airootfs/usr/share/wallpaper" - cp "$ROOT_DIR/Images/Linux.png" "$LIVE_DIR/airootfs/usr/share/wallpaper/burned.in" + # Wallpaper + mkdir -p "$LIVE_DIR/airootfs/usr/share/wallpaper" + cp "$ROOT_DIR/Images/Linux.png" "$LIVE_DIR/airootfs/usr/share/wallpaper/burned.in" + fi } function update_repo() { @@ -384,6 +398,13 @@ case ${1:-} in echo Done ;; + -m|--prep-minimal-env) + load_settings --edit + copy_live_env --minimal + update_live_env --minimal + echo Done + ;; + -o|--build-iso) load_settings build_iso @@ -412,6 +433,7 @@ case ${1:-} in echo "Advanced options:" echo " -f --fix-perms Fix folder permissions" echo " -i --install-deps Install build dependencies" + echo " -m --prep-minimal-env Prep live & airootfs folders (minimal packages)" echo " -o --build-iso Build ISO (using current setup)" echo " -p --prep-live-env Prep live & airootfs folders" echo " -u --update-repo Update custom pacman repo" diff --git a/Images/ConEmu.png b/Images/ConEmu.png index 01f23aec..ed9d0cab 100644 Binary files a/Images/ConEmu.png and b/Images/ConEmu.png differ diff --git a/Images/Linux.png b/Images/Linux.png index b06d66c2..5b4d311e 100644 Binary files a/Images/Linux.png and b/Images/Linux.png differ diff --git a/Images/Pxelinux.png b/Images/Pxelinux.png index 9a306916..3308d059 100644 Binary files a/Images/Pxelinux.png and b/Images/Pxelinux.png differ diff --git a/Images/Syslinux.png b/Images/Syslinux.png index 9a306916..dfef6907 100644 Binary files a/Images/Syslinux.png and b/Images/Syslinux.png differ diff --git a/Images/WinPE.jpg b/Images/WinPE.jpg index a611559a..c3c75900 100644 Binary files a/Images/WinPE.jpg and b/Images/WinPE.jpg differ diff --git a/Images/rEFInd.png b/Images/rEFInd.png index c36919a1..0e9ef5d1 100644 Binary files a/Images/rEFInd.png and b/Images/rEFInd.png differ