diff --git a/.bin/ConEmu/ConEmu.xml b/.bin/ConEmu/ConEmu.xml
index dd6ee9b1..3a00062b 100644
--- a/.bin/ConEmu/ConEmu.xml
+++ b/.bin/ConEmu/ConEmu.xml
@@ -140,7 +140,7 @@
-
+
diff --git a/.bin/Scripts/Launch.cmd b/.bin/Scripts/Launch.cmd
index 18c304f2..faea56f8 100644
--- a/.bin/Scripts/Launch.cmd
+++ b/.bin/Scripts/Launch.cmd
@@ -263,6 +263,7 @@ call :ExtractOrFindPath || goto ErrorProgramNotFound
set "script=%_path%\%L_ITEM%"
rem Verify
+"%PYTHON%" --version >nul || goto ErrorPythonUnsupported
if not exist "%script%" goto ErrorScriptNotFound
rem Run
@@ -435,6 +436,16 @@ echo ERROR: Office version not supported by this script.
start "" "explorer.exe" "%client_dir%\Office"
goto Abort
+:ErrorPythonUnsupported
+rem The Windows installation lacks Windows update KB2999226 needed to run Python
+echo.
+echo ERROR: Failed to run Python, try installing Windows update KB2999226.
+echo NOTE: That update is from October 2015 so this system is SEVERELY outdated
+if exist "%bin%\..\Installers\Extras\Windows Updates" (
+ start "" "explorer.exe" "%bin%\..\Installers\Extras\Windows Updates"
+)
+goto Abort
+
:ErrorQuickBooksSourceNotFound
echo.
echo ERROR: QuickBooks source "%L_ITEM%" not found.
@@ -486,7 +497,7 @@ echo Press any key to exit...
pause>nul
rem reset color and reset errorlevel to 0
rem NOTE: This is done to avoid causing a ErrorLaunchCMD in the launcher.cmd
-color
+color 07
goto Exit
:: Cleanup and exit ::
diff --git a/.bin/Scripts/build-ufd b/.bin/Scripts/build-ufd
index 1253472c..5f272d7d 100755
--- a/.bin/Scripts/build-ufd
+++ b/.bin/Scripts/build-ufd
@@ -143,11 +143,11 @@ function help () {
[[ "${__usage+x}" ]] || read -r -d '' __usage <<-'EOF' || true # exits non-zero when EOF encountered
OPTIONS:
-u --ufd-device [arg] Device to which the kit will be applied
- -m --main-kit [arg] Path to the Main Kit
-l --linux-iso [arg] Path to the Linux ISO
- -w --winpe-iso [arg] Path to the WinPE ISO
-e --extra-dir [arg] Path to the Extra folder (optional)
+ -m --main-kit [arg] Path to the Main Kit (optional)
+ -w --winpe-iso [arg] Path to the WinPE ISO (optional)
-h --help This page
ADVANCED:
@@ -390,15 +390,11 @@ if [[ "${arg_h:?}" == 1 ]]; then
else
# Print warning line
[[ "${arg_u:-}" ]] || echo " -u or --ufd-device is required"
- [[ "${arg_m:-}" ]] || echo " -m or --main-kit is required"
[[ "${arg_l:-}" ]] || echo " -l or --linux-iso is required"
- [[ "${arg_w:-}" ]] || echo " -w or --winpe-iso is required"
# Bail if necessary
[[ "${arg_u:-}" ]] || help "${__usage_example}"
[[ "${arg_l:-}" ]] || help "${__usage_example}"
- [[ "${arg_m:-}" ]] || help "${__usage_example}"
- [[ "${arg_w:-}" ]] || help "${__usage_example}"
fi
[[ "${LOG_LEVEL:-}" ]] || emergency "Cannot continue without LOG_LEVEL. "
@@ -492,26 +488,30 @@ echo ""
# Verify sources
[[ -b "${DEST_DEV}" ]] || abort "${DEST_DEV} is not a valid device."
-[[ -d "${MAIN_KIT}/.bin" ]] || abort "Invalid Main Kit, ${MAIN_KIT}/.bin not found."
[[ -e "${LINUX_ISO}" ]] || abort "Linux ISO not found."
-[[ -e "${WINPE_ISO}" ]] || abort "WinPE ISO not found."
+if [[ ! -z "${arg_m:-}" ]]; then
+ [[ -d "${MAIN_KIT}/.bin" ]] || abort "Invalid Main Kit, ${MAIN_KIT}/.bin not found."
+fi
+if [[ ! -z "${arg_w:-}" ]]; then
+ [[ -e "${WINPE_ISO}" ]] || abort "WinPE ISO not found."
+fi
if [[ ! -z "${arg_e:-}" ]]; then
[[ -d "${EXTRA_DIR}" ]] || abort "Extra Dir not found."
fi
# Print Info
-echo -e "${BLUE}Sources${CLEAR}"
-echo "Main Kit: ${MAIN_KIT}"
-echo "Linux ISO: ${LINUX_ISO}"
-echo "WinPE ISO: ${WINPE_ISO}"
-echo "Extra Dir: ${EXTRA_DIR:-(Not Specified)}"
-echo ""
-echo -e "${BLUE}Destination${CLEAR}"
-lsblk -n -o NAME,LABEL,SIZE,MODEL,SERIAL "${DEST_DEV}"
+echo -e "${BLUE}Sources${CLEAR}" | tee -a "${LOG_FILE}"
+echo "Main Kit: ${MAIN_KIT}" | tee -a "${LOG_FILE}"
+echo "Linux ISO: ${LINUX_ISO}" | tee -a "${LOG_FILE}"
+echo "WinPE ISO: ${WINPE_ISO}" | tee -a "${LOG_FILE}"
+echo "Extra Dir: ${EXTRA_DIR:-(Not Specified)}" | tee -a "${LOG_FILE}"
+echo "" | tee -a "${LOG_FILE}"
+echo -e "${BLUE}Destination${CLEAR}" | tee -a "${LOG_FILE}"
+lsblk -n -o NAME,LABEL,SIZE,MODEL,SERIAL "${DEST_DEV}" | tee -a "${LOG_FILE}"
if [[ "${USE_MBR}" == "True" ]]; then
- echo -e "${YELLOW}Formatting using legacy MBR${CLEAR}"
+ echo -e "${YELLOW}Formatting using legacy MBR${CLEAR}" | tee -a "${LOG_FILE}"
fi
-echo ""
+echo "" | tee -a "${LOG_FILE}"
# Ask before starting job
echo ""
@@ -528,8 +528,8 @@ else
fi
# Start Build
-echo ""
-echo -e "${GREEN}Building Kit${CLEAR}"
+echo "" | tee -a "${LOG_FILE}"
+echo -e "${GREEN}Building Kit${CLEAR}" | tee -a "${LOG_FILE}"
touch "${LOG_FILE}"
tmux split-window -dl 10 tail -f "${LOG_FILE}"
@@ -537,7 +537,7 @@ tmux split-window -dl 10 tail -f "${LOG_FILE}"
dd bs=4M count=16 if=/dev/zero of="${DEST_DEV}" >> "${LOG_FILE}" 2>&1
# Format
-echo "Formatting drive..."
+echo "Formatting drive..." | tee -a "${LOG_FILE}"
if [[ "${USE_MBR}" == "True" ]]; then
parted "${DEST_DEV}" --script -- mklabel msdos mkpart primary fat32 4MiB -1s >> "${LOG_FILE}" 2>&1
parted "${DEST_DEV}" set 1 boot on >> "${LOG_FILE}" 2>&1
@@ -550,45 +550,94 @@ fi
mkfs.vfat -F 32 -n "${UFD_LABEL}" "${DEST_PAR}" >> "${LOG_FILE}" 2>&1
# Mount sources and dest
-echo "Mounting sources and destination..."
+echo "Mounting sources and destination..." | tee -a "${LOG_FILE}"
mkdir /mnt/{Dest,Linux,WinPE} -p >> "${LOG_FILE}" 2>&1
mount ${DEST_PAR} /mnt/Dest >> "${LOG_FILE}" 2>&1
mount "${LINUX_ISO}" /mnt/Linux -r >> "${LOG_FILE}" 2>&1
-mount "${WINPE_ISO}" /mnt/WinPE -r >> "${LOG_FILE}" 2>&1
+if [[ ! -z "${arg_w:-}" ]]; then
+ mount "${WINPE_ISO}" /mnt/WinPE -r >> "${LOG_FILE}" 2>&1
+fi
+
+# Find WinPE source
+w_boot="$(find /mnt/WinPE -iwholename "/mnt/WinPE/Boot")"
+w_boot_bcd="$(find /mnt/WinPE -iwholename "/mnt/WinPE/Boot/BCD")"
+w_boot_sdi="$(find /mnt/WinPE -iwholename "/mnt/WinPE/Boot/boot.sdi")"
+w_bootmgr="$(find /mnt/WinPE -iwholename "/mnt/WinPE/bootmgr")"
+w_bootmgr_efi="$(find /mnt/WinPE -iwholename "/mnt/WinPE/bootmgr.efi")"
+w_efi_boot="$(find /mnt/WinPE -iwholename "/mnt/WinPE/EFI/Boot")"
+w_efi_microsoft="$(find /mnt/WinPE -iwholename "/mnt/WinPE/EFI/Microsoft")"
+w_en_us="$(find /mnt/WinPE -iwholename "/mnt/WinPE/en-us")"
+w_sources="$(find /mnt/WinPE -iwholename "/mnt/WinPE/sources")"
# Copy files
-echo "Copying Linux files..."
+echo "Copying Linux files..." | tee -a "${LOG_FILE}"
rsync ${RSYNC_ARGS} /mnt/Linux/* /mnt/Dest/ >> "${LOG_FILE}" 2>&1
sed -i "s/${ISO_LABEL}/${UFD_LABEL}/" /mnt/Dest/EFI/boot/refind.conf
-sed -i "s/#UFD#//" /mnt/Dest/EFI/boot/refind.conf
sed -i "s/${ISO_LABEL}/${UFD_LABEL}/" /mnt/Dest/arch/boot/syslinux/*cfg
-sed -i "s/#UFD#//" /mnt/Dest/arch/boot/syslinux/*cfg
-echo "Copying WinPE files..."
-rsync ${RSYNC_ARGS} /mnt/WinPE/{Boot,bootmgr{,.efi},en-us,sources} /mnt/Dest/ >> "${LOG_FILE}" 2>&1
-rsync ${RSYNC_ARGS} /mnt/WinPE/EFI/Microsoft /mnt/Dest/EFI/ >> "${LOG_FILE}" 2>&1
-rsync ${RSYNC_ARGS} /mnt/WinPE/EFI/Boot/* /mnt/Dest/EFI/Microsoft/ >> "${LOG_FILE}" 2>&1
-rsync ${RSYNC_ARGS} /mnt/WinPE/{Boot/{BCD,boot.sdi},bootmgr} /mnt/Dest/sources/ >> "${LOG_FILE}" 2>&1
+echo "Copying WinPE files..." | tee -a "${LOG_FILE}"
+if [[ ! -z "${arg_w:-}" ]]; then
+ if [[ ! -z "${w_bootmgr:-}" ]]; then
+ rsync ${RSYNC_ARGS} "${w_bootmgr}" /mnt/Dest/ >> "${LOG_FILE}" 2>&1
+ fi
+ if [[ ! -z "${w_bootmgr_efi:-}" ]]; then
+ rsync ${RSYNC_ARGS} "${w_bootmgr_efi}" /mnt/Dest/ >> "${LOG_FILE}" 2>&1
+ fi
+ if [[ ! -z "${w_en_us:-}" ]]; then
+ rsync ${RSYNC_ARGS} "${w_en_us}" /mnt/Dest/ >> "${LOG_FILE}" 2>&1
+ fi
+ if [[ ! -z "${w_boot:-}" ]]; then
+ rsync ${RSYNC_ARGS} "${w_boot}"/* /mnt/Dest/Boot/ >> "${LOG_FILE}" 2>&1
+ fi
+ if [[ ! -z "${w_efi_boot:-}" ]]; then
+ rsync ${RSYNC_ARGS} "${w_efi_boot}"/* /mnt/Dest/EFI/Microsoft/ >> "${LOG_FILE}" 2>&1
+ fi
+ if [[ ! -z "${w_efi_microsoft:-}" ]]; then
+ rsync ${RSYNC_ARGS} "${w_efi_microsoft}"/* /mnt/Dest/EFI/Microsoft/ >> "${LOG_FILE}" 2>&1
+ fi
+ if [[ ! -z "${w_boot_bcd:-}" ]]; then
+ rsync ${RSYNC_ARGS} "${w_boot_bcd}" /mnt/Dest/sources/ >> "${LOG_FILE}" 2>&1
+ fi
+ if [[ ! -z "${w_boot_sdi:-}" ]]; then
+ rsync ${RSYNC_ARGS} "${w_boot_sdi}" /mnt/Dest/sources/ >> "${LOG_FILE}" 2>&1
+ fi
+ if [[ ! -z "${w_bootmgr:-}" ]]; then
+ rsync ${RSYNC_ARGS} "${w_bootmgr}" /mnt/Dest/sources/ >> "${LOG_FILE}" 2>&1
+ fi
+ if [[ ! -z "${w_sources:-}" ]]; then
+ rsync ${RSYNC_ARGS} "${w_sources}"/* /mnt/Dest/sources/ >> "${LOG_FILE}" 2>&1
+ fi
-echo "Copying Main Kit..."
-rsync ${RSYNC_ARGS} "${MAIN_KIT}/" "/mnt/Dest/${KIT_NAME_FULL}/" >> "${LOG_FILE}" 2>&1
+ # Uncomment boot entries
+ sed -i "s/#UFD-WINPE#//" /mnt/Dest/EFI/boot/refind.conf
+ sed -i "s/#UFD-WINPE#//" /mnt/Dest/arch/boot/syslinux/*cfg
+fi
+
+echo "Copying Main Kit..." | tee -a "${LOG_FILE}"
+if [[ ! -z "${arg_m:-}" ]]; then
+ rsync ${RSYNC_ARGS} \
+ "${MAIN_KIT}/" \
+ "/mnt/Dest/${KIT_NAME_FULL}/" >> "${LOG_FILE}" 2>&1
+fi
if [[ ! -z "${EXTRA_DIR:-}" ]]; then
- echo "Copying Extra files..."
- rsync ${RSYNC_ARGS} "${EXTRA_DIR}"/ /mnt/Dest/ >> "${LOG_FILE}" 2>&1
+ echo "Copying Extra files..." | tee -a "${LOG_FILE}"
+ rsync ${RSYNC_ARGS} \
+ "${EXTRA_DIR}"/ \
+ /mnt/Dest/ >> "${LOG_FILE}" 2>&1
fi
# Install syslinux
-echo "Copying Syslinux files..."
+echo "Copying Syslinux files..." | tee -a "${LOG_FILE}"
rsync ${RSYNC_ARGS} /usr/lib/syslinux/bios/*.c32 /mnt/Dest/arch/boot/syslinux/ >> "${LOG_FILE}" 2>&1
syslinux --install -d /arch/boot/syslinux/ ${DEST_PAR} >> "${LOG_FILE}" 2>&1
-echo "Unmounting destination..."
+echo "Unmounting destination..." | tee -a "${LOG_FILE}"
umount /mnt/Dest >> "${LOG_FILE}" 2>&1
rmdir /mnt/Dest >> "${LOG_FILE}" 2>&1
sync
-echo "Installing Syslinux MBR..."
+echo "Installing Syslinux MBR..." | tee -a "${LOG_FILE}"
if [[ "${USE_MBR}" == "True" ]]; then
dd bs=440 count=1 if=/usr/lib/syslinux/bios/mbr.bin of=${DEST_DEV} >> "${LOG_FILE}" 2>&1
else
@@ -597,7 +646,7 @@ fi
sync
# Cleanup
-echo "Hiding boot files..."
+echo "Hiding boot files..." | tee -a "${LOG_FILE}"
echo "drive s: file=\"${DEST_PAR}\"" > /root/.mtoolsrc
echo 'mtools_skip_check=1' >> /root/.mtoolsrc
for item in arch Boot bootmgr{,.efi} EFI en-us images isolinux sources "${KIT_NAME_FULL}"/{.bin,.cbin}; do
@@ -606,7 +655,7 @@ done
sync
# Unmount Sources
-echo "Unmounting sources..."
+echo "Unmounting sources..." | tee -a "${LOG_FILE}"
for d in Linux WinPE; do
umount "/mnt/${d}" >> "${LOG_FILE}" 2>&1 || true
rmdir "/mnt/${d}" >> "${LOG_FILE}" 2>&1 || true
@@ -616,7 +665,7 @@ done
pkill -f "tail.*${LOG_FILE}"
# Done
-echo ""
-echo "Done."
+echo "" | tee -a "${LOG_FILE}"
+echo "Done." | tee -a "${LOG_FILE}"
echo ""
exit 0
diff --git a/.bin/Scripts/build_kit.ps1 b/.bin/Scripts/build_kit.ps1
index 08a050c9..76e69966 100644
--- a/.bin/Scripts/build_kit.ps1
+++ b/.bin/Scripts/build_kit.ps1
@@ -79,15 +79,15 @@ if ($MyInvocation.InvocationName -ne ".") {
$Path = $Temp
# 7-Zip
- 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"
+ DownloadFile -Path $Path -Name "7z-installer.msi" -Url "https://www.7-zip.org/a/7z1900.msi"
+ DownloadFile -Path $Path -Name "7z-extra.7z" -Url "https://www.7-zip.org/a/7z1900-extra.7z"
# ConEmu
- $Url = "https://github.com/Maximus5/ConEmu/releases/download/v18.06.26/ConEmuPack.180626.7z"
+ $Url = "https://github.com/Maximus5/ConEmu/releases/download/v19.03.10/ConEmuPack.190310.7z"
DownloadFile -Path $Path -Name "ConEmuPack.7z" -Url $Url
# Notepad++
- $Url = "https://notepad-plus-plus.org/repository/7.x/7.6.2/npp.7.6.2.bin.minimalist.7z"
+ $Url = "https://notepad-plus-plus.org/repository/7.x/7.6.4/npp.7.6.4.bin.minimalist.7z"
DownloadFile -Path $Path -Name "npp.7z" -Url $Url
# Python
diff --git a/.bin/Scripts/build_pe.ps1 b/.bin/Scripts/build_pe.ps1
index 00405a12..e783e39e 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/7z1806.msi"),
- @("7z-extra.7z", "https://www.7-zip.org/a/7z1806-extra.7z"),
+ @("7z-installer.msi", "https://www.7-zip.org/a/7z1900.msi"),
+ @("7z-extra.7z", "https://www.7-zip.org/a/7z1900-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"),
+ @("ConEmuPack.7z", "https://github.com/Maximus5/ConEmu/releases/download/v19.03.10/ConEmuPack.190310.7z"),
# Fast Copy
- @("fastcopy.zip", "http://ftp.vector.co.jp/70/93/2323/FastCopy361_installer.exe"),
+ @("fastcopy.zip", "http://ftp.vector.co.jp/71/31/2323/FastCopy363_installer.exe"),
# HWiNFO
- @("hwinfo.zip", "https://www.fosshub.com/HWiNFO.html?dwl=hwi_600.zip"),
+ @("hwinfo.zip", "http://files2.majorgeeks.com/377527622c5325acc1cb937fb149d0de922320c0/systeminfo/hwi_602.zip"),
# Killer Network Drivers
@(
"killerinf.zip",
("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.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"),
+ @("npp_x86.7z", "https://notepad-plus-plus.org/repository/7.x/7.6.4/npp.7.6.4.bin.minimalist.7z"),
+ @("npp_amd64.7z", "https://notepad-plus-plus.org/repository/7.x/7.6.4/npp.7.6.4.bin.minimalist.x64.7z"),
# NT Password Editor
@("ntpwed.zip", "http://cdslow.org.ru/files/ntpwedit/ntpwed07.zip"),
# Prime95
diff --git a/.bin/Scripts/debug/hw_diags.py b/.bin/Scripts/debug/hw_diags.py
new file mode 100644
index 00000000..87a35990
--- /dev/null
+++ b/.bin/Scripts/debug/hw_diags.py
@@ -0,0 +1,183 @@
+# Wizard Kit: Debug - HW Diagnostics
+
+import base64
+import requests
+
+from functions.common import *
+
+def debug_report_cpu(cpu_obj):
+ """Generate report for CpuObj, returns list."""
+ report = []
+
+ # CPU Info
+ report.append('CPU: {}'.format(cpu_obj.name))
+ report.append('lscpu:')
+ for k, v in sorted(cpu_obj.lscpu.items()):
+ report.append(' {}: {}'.format(k, v))
+
+ # Tests
+ report.append('Tests:')
+ for k, v in cpu_obj.tests.items():
+ report.extend(debug_report_test(v, k))
+
+ # Done
+ return report
+
+
+def debug_report_disk(disk_obj):
+ """Generate report for DiskObj, returns list."""
+ report = []
+ expand = [
+ 'lsblk',
+ 'nvme_attributes',
+ 'nvme_smart_notes',
+ 'smart_attributes',
+ 'smart_self_test',
+ 'smartctl',
+ ]
+ skip = [
+ 'add_nvme_smart_note',
+ 'calc_io_dd_values',
+ 'check_attributes',
+ 'check_smart_self_test',
+ 'description',
+ 'disable_test',
+ 'generate_attribute_report',
+ 'generate_disk_report',
+ 'get_details',
+ 'get_size',
+ 'get_smart_details',
+ 'name',
+ 'safety_check',
+ 'tests',
+ ]
+
+ # Disk Info
+ report.append('Disk: {} {}'.format(
+ disk_obj.name, disk_obj.description))
+ for a in dir(disk_obj):
+ if a.startswith('_') or a in skip:
+ continue
+ if a in expand:
+ report.append('{}:'.format(a))
+ attr = getattr(disk_obj, a)
+ try:
+ for k, v in sorted(attr.items()):
+ report.append(' {}: {}'.format(k, v))
+ except Exception:
+ # Ignore
+ pass
+ else:
+ report.append('{}: {}'.format(a, getattr(disk_obj, a)))
+
+ # Tests
+ report.append('Tests:')
+ for k, v in disk_obj.tests.items():
+ report.extend(debug_report_test(v, k))
+
+ # Done
+ return report
+
+
+def debug_report_state(state):
+ """Generate report for State, returns list."""
+ report = []
+
+ # Devs
+ report.append('CPU: {}'.format(state.cpu))
+ report.append('Disks: {}'.format(state.disks))
+
+ # Settings
+ report.append('Progress Out: {}'.format(state.progress_out))
+ report.append('Quick Mode: {}'.format(state.quick_mode))
+
+ # Tests
+ report.append('Tests:')
+ for k, v in state.tests.items():
+ report.append(' {}:'.format(k))
+ for k2, v2 in sorted(v.items()):
+ report.append(' {}: {}'.format(k2, v2))
+
+ # tmux
+ if hasattr(state, 'tmux_layout'):
+ report.append('tmux Layout:')
+ for k, v in state.tmux_layout.items():
+ report.append(' {}: {}'.format(k, str(v)))
+ if hasattr(state, 'panes'):
+ report.append('tmux Panes:')
+ for k, v in state.panes.items():
+ report.append(' {}: {}'.format(k, str(v)))
+
+ # Done
+ return report
+
+
+def debug_report_test(test_obj, test_name):
+ """Generate report for TestObj, returns list."""
+ report = [' {}:'.format(test_name)]
+ skip = ['update_status']
+
+ # Attributes
+ for a in [a for a in dir(test_obj) if not a.startswith('_')]:
+ if a in skip:
+ continue
+ report.append(' {}: {}'.format(a, getattr(test_obj, a)))
+
+ # Done
+ return report
+
+
+def save_debug_reports(state, global_vars):
+ """Save debug reports if possible."""
+ debug_dest = '{}/debug'.format(global_vars['LogDir'])
+ os.makedirs(debug_dest, exist_ok=True)
+
+ # State
+ with open('{}/state.report'.format(debug_dest), 'a') as f:
+ for line in debug_report_state(state):
+ f.write('{}\n'.format(line))
+
+ # CPU
+ with open('{}/cpu.report'.format(debug_dest), 'a') as f:
+ for line in debug_report_cpu(state.cpu):
+ f.write('{}\n'.format(line))
+
+ # Disk(s)
+ for disk in state.disks:
+ with open('{}/disk_{}.report'.format(debug_dest, disk.name), 'a') as f:
+ for line in debug_report_disk(disk):
+ f.write('{}\n'.format(line))
+
+
+def upload_logdir(global_vars):
+ """Upload compressed LogDir to CRASH_SERVER."""
+ source = global_vars['LogDir']
+ source = source[source.rfind('/')+1:]
+ dest = '{}.txz'.format(source)
+ data = None
+
+ # Compress LogDir
+ os.chdir('{}/..'.format(global_vars['LogDir']))
+ cmd = ['tar', 'caf', dest, source]
+ run_program(cmd)
+
+ # Read file
+ with open(dest, 'rb') as f:
+ data = f.read()
+
+ # Upload data
+ url = '{}/Crash_{}.txz'.format(CRASH_SERVER['Url'], source)
+ r = requests.put(
+ url,
+ data=data,
+ headers={'X-Requested-With': 'XMLHttpRequest'},
+ auth=(CRASH_SERVER['User'], CRASH_SERVER['Pass']))
+ # Raise exception if upload NS
+ if not r.ok:
+ raise GenericError
+
+
+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/browsers.py b/.bin/Scripts/functions/browsers.py
index 34c0252f..9f51b1e6 100644
--- a/.bin/Scripts/functions/browsers.py
+++ b/.bin/Scripts/functions/browsers.py
@@ -2,6 +2,7 @@
from functions.common import *
from operator import itemgetter
+from settings.browsers import *
# Define other_results for later try_and_print
@@ -17,95 +18,6 @@ other_results = {
}
-# 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"',
- }
-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'
-UBO_EXTRA_CHROME_REG = r'Software\Wow6432Node\Google\Chrome\Extensions\pgdnlhfefecpicbbihgmbmffkjpaplco'
-UBO_MOZILLA = 'https://addons.mozilla.org/en-us/firefox/addon/ublock-origin/'
-UBO_MOZZILA_PATH = r'{}\Mozilla Firefox\distribution\extensions\ublock_origin.xpi'.format(os.environ.get('PROGRAMFILES'))
-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',
- },
- }
-
-
-# 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'])
@@ -476,6 +388,12 @@ def install_adblock(indent=8, width=32, just_firefox=False):
cmd=[exe_path, *urls], check=False)
+def is_installed(browser_name):
+ """Checks if browser is installed based on exe_path, returns bool."""
+ browser_name = browser_name.replace(' Chromium', '')
+ return bool(browser_data.get(browser_name, {}).get('exe_path', False))
+
+
def list_homepages(indent=8, width=32):
"""List current homepages for reference."""
browser_list = [k for k, v in sorted(browser_data.items()) if v['exe_path']]
@@ -525,14 +443,23 @@ def reset_browsers(indent=8, width=32):
other_results=other_results, profile=profile)
-def scan_for_browsers(just_firefox=False):
+def scan_for_browsers(just_firefox=False, silent=False, skip_ie=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)
+ if skip_ie and details['base'] == 'ie':
+ continue
+ if silent:
+ try:
+ get_browser_details(name)
+ except Exception:
+ # Ignore errors in silent mode
+ pass
+ else:
+ try_and_print(message='{}...'.format(name),
+ function=get_browser_details, cs='Detected',
+ other_results=other_results, name=name)
if __name__ == '__main__':
diff --git a/.bin/Scripts/functions/common.py b/.bin/Scripts/functions/common.py
index 5a14a3cf..20f0f9f8 100644
--- a/.bin/Scripts/functions/common.py
+++ b/.bin/Scripts/functions/common.py
@@ -27,11 +27,12 @@ global_vars = {}
# STATIC VARIABLES
COLORS = {
'CLEAR': '\033[0m',
- 'RED': '\033[31m',
+ 'RED': '\033[31m',
'ORANGE': '\033[31;1m',
'GREEN': '\033[32m',
'YELLOW': '\033[33m',
'BLUE': '\033[34m',
+ 'PURPLE': '\033[35m',
'CYAN': '\033[36m',
}
try:
@@ -114,6 +115,14 @@ def ask(prompt='Kotaero!'):
return answer
+def beep(repeat=1):
+ """Play system bell with optional repeat."""
+ for i in range(repeat):
+ # Print bell char
+ print('\a')
+ sleep(0.5)
+
+
def choice(choices, prompt='Kotaero!'):
"""Prompt the user with a choice question, returns str."""
answer = None
@@ -428,6 +437,9 @@ def ping(addr='google.com'):
def popen_program(cmd, pipe=False, minimized=False, shell=False, **kwargs):
"""Run program and return a subprocess.Popen object."""
cmd_kwargs = {'args': cmd, 'shell': shell}
+ for kw in ('encoding', 'errors'):
+ if kw in kwargs:
+ cmd_kwargs[kw] = kwargs[kw]
if minimized:
startupinfo = subprocess.STARTUPINFO()
@@ -497,6 +509,9 @@ def run_program(cmd, check=True, pipe=True, shell=False, **kwargs):
cmd = ' '.join(cmd)
cmd_kwargs = {'args': cmd, 'check': check, 'shell': shell}
+ for kw in ('encoding', 'errors'):
+ if kw in kwargs:
+ cmd_kwargs[kw] = kwargs[kw]
if pipe:
cmd_kwargs.update({
diff --git a/.bin/Scripts/functions/data.py b/.bin/Scripts/functions/data.py
index 7dcee3cf..a2e5662c 100644
--- a/.bin/Scripts/functions/data.py
+++ b/.bin/Scripts/functions/data.py
@@ -1,103 +1,10 @@
# Wizard Kit: Functions - Data
import ctypes
-import json
-from functions.common import *
+from functions.json import *
from operator import itemgetter
-
-
-# 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',
- ]
-FAST_COPY_ARGS = [
- '/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)
+from settings.data import *
# Classes
@@ -170,8 +77,7 @@ def find_core_storage_volumes(device_path=None):
'--output', 'NAME,PARTTYPE']
if device_path:
cmd.append(device_path)
- result = run_program(cmd)
- json_data = json.loads(result.stdout.decode())
+ json_data = get_json_from_command(cmd)
devs = json_data.get('blockdevices', [])
devs = [d for d in devs if d.get('parttype', '') == corestorage_uuid]
if devs:
@@ -251,8 +157,7 @@ def get_mounted_volumes():
'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())
+ json_data = get_json_from_command(cmd)
mounted_volumes = []
for item in json_data.get('filesystems', []):
mounted_volumes.append(item)
@@ -277,8 +182,7 @@ def mount_volumes(
find_core_storage_volumes(device_path)
# Get list of block devices
- result = run_program(cmd)
- json_data = json.loads(result.stdout.decode())
+ json_data = get_json_from_command(cmd)
devs = json_data.get('blockdevices', [])
# Get list of volumes
@@ -333,6 +237,7 @@ def mount_volumes(
if vol_data['show_data']['data'] == 'Failed to mount':
vol_data['mount_point'] = None
else:
+ fstype = vol_data.get('fstype', 'UNKNOWN FS')
size_used = human_readable_size(
mounted_volumes[vol_path]['used'])
size_avail = human_readable_size(
@@ -342,8 +247,9 @@ def mount_volumes(
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'] = '{:40} ({}, {} used, {} free)'.format(
vol_data['show_data']['data'],
+ fstype,
size_used,
size_avail)
diff --git a/.bin/Scripts/functions/ddrescue.py b/.bin/Scripts/functions/ddrescue.py
index f6c30e0a..545d08e0 100644
--- a/.bin/Scripts/functions/ddrescue.py
+++ b/.bin/Scripts/functions/ddrescue.py
@@ -1,8 +1,9 @@
-# Wizard Kit: Functions - ddrescue
+# Wizard Kit: Functions - ddrescue-tui
-import json
+import datetime
import pathlib
import psutil
+import pytz
import re
import signal
import stat
@@ -11,37 +12,10 @@ import time
from collections import OrderedDict
from functions.data import *
from functions.hw_diags import *
+from functions.json 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},
- }
-RECOMMENDED_FSTYPES = ['ext3', 'ext4', 'xfs']
-SIDE_PANE_WIDTH = 21
-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)
-"""
+from settings.ddrescue import *
# Clases
@@ -282,6 +256,7 @@ class RecoveryState():
self.block_pairs = []
self.current_pass = 0
self.current_pass_str = '0: Initializing'
+ self.etoc = ''
self.settings = DDRESCUE_SETTINGS.copy()
self.finished = False
self.panes = {}
@@ -289,6 +264,8 @@ class RecoveryState():
self.rescued = 0
self.resumed = False
self.started = False
+ self.status = 'Inactive'
+ self.timezone = pytz.timezone(LINUX_TIME_ZONE)
self.total_size = 0
if mode not in ('clone', 'image'):
raise GenericError('Unsupported mode')
@@ -374,15 +351,14 @@ class RecoveryState():
map_allowed_fstypes = RECOMMENDED_FSTYPES.copy()
map_allowed_fstypes.extend(['cifs', 'ext2', 'vfat'])
map_allowed_fstypes.sort()
- json_data = {}
+ json_data = get_json_from_command(cmd)
- # Avoid saving map to non-persistent filesystem
- try:
- result = run_program(cmd)
- json_data = json.loads(result.stdout.decode())
- except Exception:
+ # Abort if json_data is empty
+ if not json_data:
print_error('ERROR: Failed to verify map path')
raise GenericAbort()
+
+ # Avoid saving map to non-persistent filesystem
fstype = json_data.get(
'filesystems', [{}])[0].get(
'fstype', 'unknown')
@@ -428,6 +404,66 @@ class RecoveryState():
elif self.current_pass == 2:
self.current_pass_str = '3 "Scraping bad areas"'
+ def update_etoc(self):
+ """Search ddrescue output for the current EToC, returns str."""
+ now = datetime.datetime.now(tz=self.timezone)
+
+ # Bail early
+ if 'NEEDS ATTENTION' in self.status:
+ # Just set to N/A (NOTE: this overrules the refresh rate below)
+ self.etoc = 'N/A'
+ return
+ elif 'In Progress' not in self.status:
+ # Don't update when EToC is hidden
+ return
+ if now.second % ETOC_REFRESH_RATE != 0:
+ # Limit updates based on settings/ddrescue.py
+ return
+
+ self.etoc = 'Unknown'
+ etoc_delta = None
+ text = ''
+
+ # Capture main tmux pane
+ try:
+ text = tmux_capture_pane()
+ except Exception:
+ # Ignore
+ pass
+
+ # Search for EToC delta
+ matches = re.findall(r'remaining time:.*$', text, re.MULTILINE)
+ if matches:
+ r = REGEX_REMAINING_TIME.search(matches[-1])
+ if r.group('na'):
+ self.etoc = 'N/A'
+ else:
+ self.etoc = r.string
+ days = r.group('days') if r.group('days') else 0
+ hours = r.group('hours') if r.group('hours') else 0
+ minutes = r.group('minutes') if r.group('minutes') else 0
+ seconds = r.group('seconds') if r.group('seconds') else 0
+ try:
+ etoc_delta = datetime.timedelta(
+ days=int(days),
+ hours=int(hours),
+ minutes=int(minutes),
+ seconds=int(seconds),
+ )
+ except Exception:
+ # Ignore and leave as raw string
+ pass
+
+ # Calc finish time if EToC delta found
+ if etoc_delta:
+ try:
+ now = datetime.datetime.now(tz=self.timezone)
+ _etoc = now + etoc_delta
+ self.etoc = _etoc.strftime('%Y-%m-%d %H:%M %Z')
+ except Exception:
+ # Ignore and leave as current string
+ pass
+
def update_progress(self):
"""Update overall progress using block_pairs."""
self.rescued = 0
@@ -575,21 +611,11 @@ def fix_tmux_panes(state, forced=False):
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 {}
+ cmd = ['lsblk', '--json', '--output-all', '--paths', dev_path]
+ json_data = get_json_from_command(cmd)
- json_data = json.loads(result.stdout.decode())
# Just return the first device (there should only be one)
- return json_data['blockdevices'][0]
+ return json_data.get('blockdevices', [{}])[0]
def get_device_report(dev_path):
@@ -622,17 +648,19 @@ def get_device_report(dev_path):
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:
+ cmd = [
+ 'findmnt', '-J',
+ '-o', 'SOURCE,TARGET,FSTYPE,OPTIONS,SIZE,AVAIL,USED',
+ '-T', dir_path,
+ ]
+ json_data = get_json_from_command(cmd)
+
+ # Raise exception if json_data is empty
+ if not json_data:
raise GenericError(
- 'Failed to get directory details for "{}".'.format(self.path))
- else:
- return json_data['filesystems'][0]
+ 'Failed to get directory details for "{}".'.format(dir_path))
+
+ return json_data.get('filesystems', [{}])[0]
def get_dir_report(dir_path):
@@ -805,6 +833,13 @@ def menu_main(state):
# Show menu
while True:
+ # Update status
+ if state.finished:
+ state.status = ' Finished'
+ else:
+ state.status = ' Inactive'
+ update_sidepane(state)
+
# Update entries
for opt in main_options:
opt['Name'] = '[{}] {}'.format(
@@ -959,6 +994,7 @@ def run_ddrescue(state, pass_settings):
"""Run ddrescue pass."""
return_code = -1
aborted = False
+ state.status = ' In Progress'
if state.finished:
clear_screen()
@@ -1073,6 +1109,9 @@ def run_ddrescue(state, pass_settings):
# Done
if str(return_code) != '0':
# Pause on errors
+ state.status = ' {YELLOW}NEEDS ATTENTION{CLEAR}'.format(**COLORS)
+ state.status = state.status.replace('33m', '33;5m')
+ update_sidepane(state)
pause('Press Enter to return to main menu... ')
# Cleanup
@@ -1238,14 +1277,8 @@ def select_path(skip_device=None):
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())
+ cmd = ['lsblk', '--json', '--nodeps', '--output-all', '--paths']
+ json_data = get_json_from_command(cmd)
skip_names = []
if skip_device:
skip_names.append(skip_device.path)
@@ -1254,7 +1287,7 @@ def select_device(description='device', skip_device=None):
# Build menu
dev_options = []
- for dev in json_data['blockdevices']:
+ for dev in json_data.get('blockdevices', []):
# Disable dev if in skip_names
disabled = dev['name'] in skip_names or dev['pkname'] in skip_names
@@ -1335,6 +1368,7 @@ def update_sidepane(state):
output.append(' {BLUE}Cloning Status{CLEAR}'.format(**COLORS))
else:
output.append(' {BLUE}Imaging Status{CLEAR}'.format(**COLORS))
+ output.append(state.status)
output.append('─────────────────────')
# Overall progress
@@ -1354,6 +1388,19 @@ def update_sidepane(state):
output.extend(bp.status)
output.append(' ')
+ # EToC
+ if re.search(r'(In Progress|NEEDS ATTENTION)', state.status):
+ if not output[-1].strip():
+ # Last line is empty
+ output.pop()
+ output.append('─────────────────────')
+ output.append('{BLUE}Estimated Pass Finish{CLEAR}'.format(**COLORS))
+ state.update_etoc()
+ if 'N/A' in state.etoc.upper():
+ output.append('{YELLOW}N/A{CLEAR}'.format(**COLORS))
+ else:
+ output.append(state.etoc)
+
# Add line-endings
output = ['{}\n'.format(line) for line in output]
diff --git a/.bin/Scripts/functions/hw_diags.py b/.bin/Scripts/functions/hw_diags.py
index 1a5bed02..8a5a441f 100644
--- a/.bin/Scripts/functions/hw_diags.py
+++ b/.bin/Scripts/functions/hw_diags.py
@@ -1,96 +1,23 @@
# Wizard Kit: Functions - HW Diagnostics
-import json
import re
import time
from collections import OrderedDict
+from functions.json import *
from functions.osticket import *
from functions.sensors import *
from functions.threading import *
from functions.tmux import *
+from settings.hw_diags import *
+if DEBUG_MODE:
+ from debug.hw_diags import *
-# STATIC VARIABLES
-ATTRIBUTES = {
- '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 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},
-})
+# Fix settings
+OVERRIDES_FORCED = OVERRIDES_FORCED and not OVERRIDES_LIMITED
+QUICK_LABEL = QUICK_LABEL.format(**COLORS)
+TOP_PANE_TEXT = TOP_PANE_TEXT.format(**COLORS)
# Regex
@@ -115,15 +42,10 @@ class CpuObj():
def get_details(self):
"""Get CPU details from lscpu."""
cmd = ['lscpu', '--json']
- try:
- result = run_program(cmd, check=False)
- json_data = json.loads(result.stdout.decode())
- except Exception:
- # 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)
+ json_data = get_json_from_command(cmd)
+ for line in json_data.get('lscpu', [{}]):
+ _field = line.get('field', '').replace(':', '')
+ _data = line.get('data', '')
if not _field and not _data:
# Skip
print_warning(_field, _data)
@@ -148,18 +70,21 @@ class DiskObj():
"""Object for tracking disk specific data."""
def __init__(self, disk_path):
self.checkbox = None
+ self.attr_type = 'UNKNOWN'
self.disk_ok = True
self.labels = []
self.lsblk = {}
self.name = re.sub(r'^.*/(.*)', r'\1', disk_path)
self.nvme_attributes = {}
+ self.nvme_smart_notes = {}
+ self.override_disabled = False
self.path = disk_path
self.smart_attributes = {}
- self.smart_timeout = False
self.smart_self_test = {}
self.smartctl = {}
self.tests = OrderedDict()
self.get_details()
+ self.get_size()
# Try enabling SMART
run_program(['sudo', 'smartctl', '--smart=on', self.path], check=False)
@@ -169,39 +94,39 @@ class DiskObj():
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)
- self.size_bytes = int(result.stdout.decode().strip())
+ def add_nvme_smart_note(self, note):
+ """Add note that will be included in the NVMe / SMART report."""
+ # A dict is used to avoid duplicate notes
+ self.nvme_smart_notes[note] = None
- # 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
+ def calc_io_dd_values(self):
+ """Calcualte I/O benchmark dd values.
+
+ 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,
@@ -223,97 +148,96 @@ class DiskObj():
# self.dd_skip_extra == 0 is fine
pass
- def check_attributes(self, silent=False):
- """Check NVMe / SMART attributes for errors."""
- override_disabled = False
+ def check_attributes(self):
+ """Check NVMe / SMART attributes for errors, returns bool."""
+ attr_type = self.attr_type
+ disk_ok = True
+
+ # Get updated attributes
+ self.get_smart_details()
+
+ # Check attributes
if self.nvme_attributes:
- attr_type = 'NVMe'
+ self.add_nvme_smart_note(
+ ' {YELLOW}NVMe disk support is still experimental{CLEAR}'.format(
+ **COLORS))
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
+ if not ATTRIBUTES[attr_type][k]['Error']:
+ # Informational attribute, skip
continue
- if ATTRIBUTES[attr_type][k].get('Ignore', False):
+ if ATTRIBUTES[attr_type][k]['Ignore']:
# Attribute is non-failing, skip
continue
if v['raw'] >= ATTRIBUTES[attr_type][k]['Error']:
- self.disk_ok = False
+ if (ATTRIBUTES[attr_type][k]['Maximum']
+ and v['raw'] >= ATTRIBUTES[attr_type][k]['Maximum']):
+ # Non-standard value, skip
+ continue
+ else:
+ disk_ok = False
- # Disable override if necessary
- override_disabled |= ATTRIBUTES[attr_type][k].get(
- 'Critical', False)
+ # Disable override if necessary
+ self.override_disabled |= ATTRIBUTES[attr_type][k].get(
+ 'Critical', False)
# 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
+ disk_ok = False
+ self.override_disabled = True
+ self.add_nvme_smart_note(
+ ' {RED}SMART overall self-assessment: Failed{CLEAR}'.format(**COLORS))
- # 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(' ')
+ # Done
+ return disk_ok
- def disable_test(self, name, status):
+ def check_smart_self_test(self, silent=False):
+ """Check if a SMART self-test is currently running, returns bool."""
+ msg = 'SMART self-test in progress'
+ test_running = 'remaining_percent' in self.smart_self_test.get('status', '')
+
+ if test_running:
+ # Ask to abort
+ if not silent:
+ print_warning('WARNING: {}'.format(msg))
+ print_standard(' ')
+ if ask('Abort HW Diagnostics?'):
+ raise GenericAbort('Bail')
+
+ # Add warning note
+ self.add_nvme_smart_note(
+ ' {YELLOW}WARNING: {msg}{CLEAR}'.format(msg=msg, **COLORS))
+
+ # Done
+ return test_running
+
+ def disable_test(self, name, status, test_failed=False):
"""Disable test by name and update status."""
if name in self.tests:
self.tests[name].update_status(status)
self.tests[name].disabled = True
+ self.tests[name].failed = test_failed
def generate_attribute_report(
- self, description=False, short_test=False, timestamp=False):
+ self, description=False, timestamp=False):
"""Generate NVMe / SMART report, returns list."""
+ attr_type = self.attr_type
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
+ # Skip attributes if they don't exist
+ if not (self.nvme_attributes or self.smart_attributes):
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(
@@ -342,10 +266,12 @@ class DiskObj():
n=v['name'][:28])
# Set color
- for _t, _c in [['Warning', 'YELLOW'], ['Error', 'RED']]:
- if _t in ATTRIBUTES[attr_type][k]:
+ for _t, _c in ATTRIBUTE_COLORS:
+ if ATTRIBUTES[attr_type][k][_t]:
if v['raw'] >= ATTRIBUTES[attr_type][k][_t]:
_color = COLORS[_c]
+ if _t == 'Maximum':
+ _note = '(invalid?)'
# 199/C7 warning
if str(k) == '199' and v['raw'] > 0:
@@ -361,30 +287,21 @@ class DiskObj():
# 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())
+ report.extend(self.generate_attribute_report(description=True))
+
+ # Notes
+ if self.nvme_smart_notes:
+ report.append('{BLUE}{attr_type} Notes{CLEAR}'.format(
+ attr_type=self.attr_type, **COLORS))
+ report.extend(sorted(self.nvme_smart_notes.keys()))
# Tests
for test in self.tests.values():
@@ -395,26 +312,25 @@ class DiskObj():
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
+ json_data = get_json_from_command(cmd)
+ self.lsblk = json_data.get('blockdevices', [{}])[0]
# Set necessary details
self.lsblk['model'] = self.lsblk.get('model', 'Unknown Model')
self.lsblk['name'] = self.lsblk.get('name', self.path)
+ self.lsblk['phy-sec'] = self.lsblk.get('phy-sec', -1)
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
+ # Ensure certain attributes types
for attr in ['model', 'name', 'rota', 'serial', 'size', 'tran']:
if not isinstance(self.lsblk[attr], str):
self.lsblk[attr] = str(self.lsblk[attr])
+ for attr in ['phy-sec']:
+ if not isinstance(self.lsblk[attr], int):
+ self.lsblk[attr] = int(self.lsblk[attr])
self.lsblk['tran'] = self.lsblk['tran'].upper().replace('NVME', 'NVMe')
# Build list of labels
@@ -423,18 +339,26 @@ class DiskObj():
self.labels.append(disk.get('partlabel', ''))
self.labels = [str(label) for label in self.labels if label]
+ def get_size(self):
+ """Get real disk size."""
+ cmd = ['lsblk',
+ '--bytes', '--nodeps', '--noheadings',
+ '--output', 'size', self.path]
+ try:
+ result = run_program(cmd)
+ self.size_bytes = int(result.stdout.decode().strip())
+ except Exception:
+ # Setting to impossible value for now
+ self.size_bytes = -1
+
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
+ self.smartctl = get_json_from_command(cmd)
# Check for attributes
if KEY_NVME in self.smartctl:
+ self.attr_type = 'NVMe'
self.nvme_attributes = {}
for k, v in self.smartctl[KEY_NVME].items():
try:
@@ -447,6 +371,7 @@ class DiskObj():
# TODO: Limit this check
pass
elif KEY_SMART in self.smartctl:
+ self.attr_type = 'SMART'
for a in self.smartctl[KEY_SMART].get('table', {}):
try:
_id = int(a.get('id', -1))
@@ -476,48 +401,57 @@ class DiskObj():
def safety_check(self, silent=False):
"""Run safety checks and disable tests if necessary."""
+ test_running = False
if self.nvme_attributes or self.smart_attributes:
- self.check_attributes(silent)
+ disk_ok = self.check_attributes()
+ test_running = self.check_smart_self_test(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')
+ # Show errors (unless a SMART self-test is running)
+ if not (silent or test_running):
+ if 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(self.attr_type))
+ if self.override_disabled:
+ print_standard('Tests disabled for this device')
+ pause()
+ elif not (len(self.tests) == 3 and OVERRIDES_LIMITED):
+ if OVERRIDES_FORCED or ask('Run tests on this device anyway?'):
+ 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
+ print_standard(' ')
else:
# No NVMe/SMART details
self.disable_test('NVMe / SMART', 'N/A')
if silent:
- self.disk_ok = HW_OVERRIDES_FORCED
+ disk_ok = 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?')
+ show_report(
+ self.generate_attribute_report(description=True),
+ log_report=True)
+ disk_ok = 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()
+ # Disable tests if necessary (statuses won't be overwritten)
+ if test_running:
+ if not silent:
+ # silent is only True in quick_mode
+ self.disable_test('NVMe / SMART', 'Denied')
+ for t in ['badblocks', 'I/O Benchmark']:
+ self.disable_test(t, 'Denied')
+ elif not disk_ok:
+ self.disable_test('NVMe / SMART', 'FAIL', test_failed=True)
for t in ['badblocks', 'I/O Benchmark']:
self.disable_test(t, 'Denied')
@@ -552,11 +486,14 @@ class State():
'Objects': [],
},
})
+ self.ticket_name = None
self.ticket_id = None
def init(self):
"""Remove test objects, set log, and add devices."""
self.disks = []
+ self.ticket_id = None
+ self.ticket_name = None
for k, v in self.tests.items():
v['Objects'] = []
@@ -576,9 +513,8 @@ class State():
# 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']:
+ json_data = get_json_from_command(cmd)
+ for disk in json_data.get('blockdevices', []):
skip_disk = False
disk_obj = DiskObj(disk['name'])
@@ -600,6 +536,15 @@ class State():
self.tmux_layout = TMUX_LAYOUT.copy()
start_thread(fix_tmux_panes_loop, args=[self])
+ def set_top_pane_text(self, text):
+ """Set top pane text using TOP_PANE_TEXT and provided text."""
+ ticket_details = ''
+ if self.ticket_id and self.ticket_name:
+ ticket_details = '#{} {}'.format(self.ticket_id, self.ticket_name)
+ tmux_update_pane(
+ self.panes['Top'],
+ text='{} {}\n{}'.format(TOP_PANE_TEXT, ticket_details, text))
+
class TestObj():
"""Object to track test data."""
@@ -917,6 +862,8 @@ def run_audio_test():
def run_badblocks_test(state, test):
"""Run a read-only surface scan with badblocks."""
+ dev = test.dev
+
# Bail early
if test.disabled:
return
@@ -934,32 +881,36 @@ def run_badblocks_test(state, test):
break
# Prep
- print_log('Starting badblocks test for {}'.format(test.dev.path))
+ print_log('Starting badblocks test for {}'.format(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.set_top_pane_text(dev.description)
# Create monitor pane
test.badblocks_out = '{}/badblocks_{}.out'.format(
- global_vars['LogDir'], test.dev.name)
+ global_vars['LogDir'], 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())
+ show_report(dev.generate_attribute_report())
print_standard(' ')
+ # Set read block size
+ if dev.lsblk['phy-sec'] == 4096 or dev.size_bytes >= BADBLOCKS_LARGE_DISK:
+ block_size = '4096'
+ else:
+ # Use default value
+ block_size = '1024'
+
# Start badblocks
print_standard('Running badblocks test...')
test.badblocks_proc = popen_program(
- ['sudo', 'badblocks', '-sv', '-e', '1', test.dev.path],
+ ['sudo', 'badblocks', '-sv', '-b', block_size, '-e', '1', dev.path],
pipe=True, bufsize=1)
test.badblocks_nbsr = NonBlockingStreamReader(test.badblocks_proc.stderr)
test.badblocks_stderr = ''
@@ -989,7 +940,8 @@ def run_badblocks_test(state, test):
else:
test.report.append(' {YELLOW}{line}{CLEAR}'.format(
line=line, **COLORS))
- test.failed = True
+ if not test.aborted:
+ test.failed = True
if test.aborted:
test.report.append(' {YELLOW}Aborted{CLEAR}'.format(**COLORS))
test.update_status('Aborted')
@@ -997,7 +949,7 @@ def run_badblocks_test(state, test):
# Disable other drive tests if necessary
if not test.passed:
- test.dev.disable_test('I/O Benchmark', 'Denied')
+ dev.disable_test('I/O Benchmark', 'Denied')
# Update status
if test.failed:
@@ -1057,9 +1009,13 @@ def run_hw_tests(state):
tmux_kill_pane(*state.panes.values())
return
- # Get ticket_number
+ # Get ticket ID and name
if not state.ost.disabled:
- state.ticket_id = state.ost.get_ticket_number()
+ try:
+ state.ticket_id, state.ticket_name = state.ost.get_ticket_details()
+ except TypeError:
+ # Happens if connection fails and retry is not attempted
+ pass
# Run disk safety checks (if necessary)
_disk_tests_enabled = False
@@ -1067,7 +1023,12 @@ def run_hw_tests(state):
_disk_tests_enabled |= state.tests[k]['Enabled']
if _disk_tests_enabled:
for disk in state.disks:
- disk.safety_check(silent=state.quick_mode)
+ try:
+ disk.safety_check(silent=state.quick_mode)
+ except GenericAbort:
+ tmux_kill_pane(*state.panes.values())
+ state.panes.clear()
+ return
# Run tests
## Because state.tests is an OrderedDict and the disks were added
@@ -1084,7 +1045,15 @@ def run_hw_tests(state):
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)
+ state.ost.post_device_results(
+ state.cpu, state.ticket_id, state.ticket_name)
+ # Recheck attributes
+ if state.tests['NVMe / SMART']['Enabled']:
+ for test_obj in state.tests['NVMe / SMART']['Objects']:
+ if test_obj.dev is not None:
+ # dev == None for the 'N/A' lines set above
+ run_nvme_smart_tests(state, test_obj, update_mode=True)
+
except GenericAbort:
# Cleanup
tmux_kill_pane(*state.panes.values())
@@ -1112,7 +1081,8 @@ def run_hw_tests(state):
if _disk_tests_enabled and state.disks and not state.ost.disabled:
print_standard('Posting results to osTicket...')
for disk in state.disks:
- state.ost.post_device_results(disk, state.ticket_id)
+ state.ost.post_device_results(
+ disk, state.ticket_id, state.ticket_name)
# Check if disk checkbox needs updating
all_disks_passed = True
@@ -1154,26 +1124,25 @@ def run_hw_tests(state):
def run_io_benchmark(state, test):
"""Run a read-only I/O benchmark using dd."""
+ dev = test.dev
+
# Bail early
if test.disabled:
return
# Prep
- print_log('Starting I/O benchmark test for {}'.format(test.dev.path))
+ print_log('Starting I/O benchmark test for {}'.format(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.set_top_pane_text(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)
+ global_vars['LogDir'], dev.name)
state.panes['io_benchmark'] = tmux_split_window(
percent=75, vertical=True,
watch=test.io_benchmark_out, watch_cmd='tail')
@@ -1181,7 +1150,7 @@ def run_io_benchmark(state, test):
# Show disk details
clear_screen()
- show_report(test.dev.generate_attribute_report())
+ show_report(dev.generate_attribute_report())
print_standard(' ')
# Start I/O Benchmark
@@ -1189,23 +1158,23 @@ def run_io_benchmark(state, test):
try:
test.merged_rates = []
test.read_rates = []
- test.dev.calc_io_dd_values()
+ dev.calc_io_dd_values()
# Run dd read tests
offset = 0
- for i in range(test.dev.dd_chunks):
+ for i in range(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 = dev.dd_skip_count
+ if dev.dd_skip_extra and i % 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),
+ 'count={}'.format(dev.dd_chunk_blocks),
'iflag=direct',
- 'if={}'.format(test.dev.path),
+ 'if={}'.format(dev.path),
'of=/dev/null']
# Run cmd and get read rate
@@ -1219,12 +1188,12 @@ def run_io_benchmark(state, test):
# Show progress
if i % IO_VARS['Progress Refresh Rate'] == 0:
update_io_progress(
- percent=(i/test.dev.dd_chunks)*100,
+ percent=(i/dev.dd_chunks)*100,
rate=cur_rate,
progress_file=test.io_benchmark_out)
# Update offset
- offset += test.dev.dd_chunk_blocks + skip
+ offset += dev.dd_chunk_blocks + skip
except DeviceTooSmallError:
# Device too small, skipping test
@@ -1250,7 +1219,7 @@ def run_io_benchmark(state, test):
else:
# Merge rates for horizontal graph
offset = 0
- width = int(test.dev.dd_chunks / IO_VARS['Graph Horizontal Width'])
+ width = int(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)
@@ -1271,7 +1240,7 @@ def run_io_benchmark(state, test):
test.report.append(avg_min_max)
# Compare read speeds to thresholds
- if test.dev.lsblk['rota']:
+ if dev.lsblk['rota']:
# Use HDD scale
thresh_min = IO_VARS['Threshold HDD Min']
thresh_high_avg = IO_VARS['Threshold HDD High Avg']
@@ -1312,6 +1281,8 @@ def run_keyboard_test():
def run_mprime_test(state, test):
"""Test CPU with Prime95 and track temps."""
+ dev = test.dev
+
# Bail early
if test.disabled:
return
@@ -1322,11 +1293,10 @@ def run_mprime_test(state, test):
test.update_status()
update_progress_pane(state)
test.sensor_data = get_sensor_data()
+ test.thermal_abort = False
# Update tmux layout
- tmux_update_pane(
- state.panes['Top'],
- text='{}\n{}'.format(TOP_PANE_TEXT, test.dev.name))
+ state.set_top_pane_text(dev.name)
# Start live sensor monitor
test.sensors_out = '{}/sensors.out'.format(global_vars['TmpDir'])
@@ -1357,12 +1327,12 @@ def run_mprime_test(state, test):
# 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'])
+ run_program(['apple-fans', 'max'], check=False)
tmux_update_pane(
state.panes['Prime95'],
command=['hw-diags-prime95', global_vars['TmpDir']],
working_dir=global_vars['TmpDir'])
- time_limit = int(MPRIME_LIMIT) * 60
+ time_limit = MPRIME_LIMIT * 60
try:
for i in range(time_limit):
clear_screen()
@@ -1379,15 +1349,19 @@ def run_mprime_test(state, test):
# 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)
+ update_sensor_data(test.sensor_data, THERMAL_LIMIT)
# Wait
sleep(1)
- except KeyboardInterrupt:
- # Catch CTRL+C
+ except (KeyboardInterrupt, ThermalLimitReachedError) as err:
+ # CTRL+c pressed or thermal limit reached
test.aborted = True
- test.update_status('Aborted')
- print_warning('\nAborted.')
+ if isinstance(err, KeyboardInterrupt):
+ test.update_status('Aborted')
+ elif isinstance(err, ThermalLimitReachedError):
+ test.failed = True
+ test.thermal_abort = True
+ test.update_status('FAIL')
update_progress_pane(state)
# Restart live monitor
@@ -1401,7 +1375,7 @@ def run_mprime_test(state, test):
tmux_kill_pane(state.panes.pop('Prime95', None))
# Get cooldown temp
- run_program(['apple-fans', 'auto'])
+ run_program(['apple-fans', 'auto'], check=False)
clear_screen()
try_and_print(
message='Letting CPU cooldown for bit...', indent=0,
@@ -1488,6 +1462,16 @@ def run_mprime_test(state, test):
test.sensor_data, 'Idle', 'Max', 'Cooldown', core_only=True):
test.report.append(' {}'.format(line))
+ # Add abort message(s)
+ if test.aborted:
+ test.report.append(
+ ' {YELLOW}Aborted{CLEAR}'.format(**COLORS))
+ if test.thermal_abort:
+ test.report.append(
+ ' {RED}CPU reached temperature limit of {temp}°C{CLEAR}'.format(
+ temp=THERMAL_LIMIT,
+ **COLORS))
+
# Done
update_progress_pane(state)
@@ -1507,139 +1491,141 @@ def run_network_test():
pause('Press Enter to return to main menu... ')
-def run_nvme_smart_tests(state, test):
- """Run NVMe or SMART test for test.dev."""
+def run_nvme_smart_tests(state, test, update_mode=False):
+ """Run NVMe or SMART test for test.dev.
+
+ Update mode is used to refresh the attributes and recheck them.
+ (i.e. no self-test and don't disable other tests)"""
+ dev = test.dev
+
# Bail early
if test.disabled:
return
# Prep
- print_log('Starting NVMe/SMART test for {}'.format(test.dev.path))
- _include_short_test = False
+ print_log('Starting NVMe/SMART test for {}'.format(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.set_top_pane_text(dev.description)
- # NVMe
- if test.dev.nvme_attributes:
- # NOTE: Pass/Fail is just the attribute check
- if test.dev.disk_ok:
+ # SMART short self-test
+ if dev.smart_attributes and not (state.quick_mode or update_mode):
+ run_smart_short_test(state, test)
+
+ # Attribute check
+ dev.check_attributes()
+
+ # Check results
+ if dev.nvme_attributes or state.quick_mode:
+ if 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):
+ elif dev.smart_attributes:
+ if dev.disk_ok and dev.self_test_passed and 'OVERRIDE' not in test.status:
+ test.passed = True
+ test.update_status('PASS')
+ elif test.aborted:
+ test.update_status('Aborted')
+ raise GenericAbort('Aborted')
+ elif dev.self_test_timed_out:
+ test.failed = True
+ test.update_status('TimedOut')
+ elif dev.override_disabled or 'OVERRIDE' not in test.status:
+ # override_disabled is set to True if one or more critical attributes
+ # have exceeded the Error threshold. This overrules an override.
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
+ else:
+ # This dev lacks both NVMe and SMART data. This test should've been
+ # disabled during the safety_check().
+ pass
- # 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)
+ # Disable other disk tests if necessary
+ if test.failed and not update_mode:
+ for t in ['badblocks', 'I/O Benchmark']:
+ dev.disable_test(t, 'Denied')
# Done
update_progress_pane(state)
+def run_smart_short_test(state, test):
+ """Run SMART short self-test for test.dev."""
+ dev = test.dev
+ dev.self_test_started = False
+ dev.self_test_finished = False
+ dev.self_test_passed = False
+ dev.self_test_timed_out = False
+ test.timeout = dev.smart_self_test['polling_minutes'].get('short', 5)
+ test.timeout = int(test.timeout) + 5
+
+ # Create monitor pane
+ test.smart_out = '{}/smart_{}.out'.format(global_vars['LogDir'], 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(dev.generate_attribute_report())
+ print_standard(' ')
+
+ # Start short test
+ print_standard('Running self-test...')
+ cmd = ['sudo', 'smartctl', '--test=short', dev.path]
+ run_program(cmd, check=False)
+
+ # Monitor progress
+ try:
+ for i in range(int(test.timeout*60/5)):
+ sleep(5)
+
+ # Update SMART data
+ dev.get_smart_details()
+
+ if dev.self_test_started:
+ # Update progress file
+ with open(test.smart_out, 'w') as f:
+ f.write('SMART self-test status:\n {}'.format(
+ dev.smart_self_test['status'].get(
+ 'string', 'UNKNOWN').capitalize()))
+
+ # Check if test has finished
+ if 'remaining_percent' not in dev.smart_self_test['status']:
+ dev.self_test_finished = True
+ break
+
+ else:
+ # Check if test has started
+ if 'remaining_percent' in dev.smart_self_test['status']:
+ dev.self_test_started = True
+ except KeyboardInterrupt:
+ # Will be handled in run_nvme_smart_tests()
+ test.aborted = True
+
+ # Save report
+ test.report.append('{BLUE}SMART Short self-test{CLEAR}'.format(**COLORS))
+ test.report.append(' {}'.format(
+ dev.smart_self_test['status'].get('string', 'UNKNOWN').capitalize()))
+ if dev.self_test_finished:
+ dev.self_test_passed = dev.smart_self_test['status'].get('passed', False)
+ elif test.aborted:
+ test.report.append(' {YELLOW}Aborted{CLEAR}'.format(**COLORS))
+ else:
+ dev.self_test_timed_out = True
+ test.report.append(' {YELLOW}Timed out{CLEAR}'.format(**COLORS))
+
+ # Cleanup
+ tmux_kill_pane(state.panes.pop('SMART', None))
+
+
def secret_screensaver(screensaver=None):
"""Show screensaver."""
if screensaver == 'matrix':
@@ -1662,9 +1648,7 @@ def show_report(report, log_report=False):
def show_results(state):
"""Show results for all tests."""
clear_screen()
- tmux_update_pane(
- state.panes['Top'],
- text='{}\nResults'.format(TOP_PANE_TEXT))
+ state.set_top_pane_text('Results')
# CPU tests
_enabled = False
diff --git a/.bin/Scripts/functions/info.py b/.bin/Scripts/functions/info.py
index ad71bce7..84d92663 100644
--- a/.bin/Scripts/functions/info.py
+++ b/.bin/Scripts/functions/info.py
@@ -3,57 +3,7 @@
from borrowed import knownpaths
from functions.activation import *
from operator import itemgetter
-
-
-# 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',
-]
-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}',
- ),
-}
-
-
-# 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)
+from settings.info import *
def backup_file_list():
diff --git a/.bin/Scripts/functions/json.py b/.bin/Scripts/functions/json.py
new file mode 100644
index 00000000..b4527c0d
--- /dev/null
+++ b/.bin/Scripts/functions/json.py
@@ -0,0 +1,32 @@
+# Wizard Kit: Functions - JSON
+
+import json
+
+from functions.common import *
+
+def get_json_from_command(cmd, ignore_errors=True):
+ """Capture JSON content from cmd output, returns dict.
+
+ If the data can't be decoded then either an exception is raised
+ or an empty dict is returned depending on ignore_errors.
+ """
+ errors = 'strict'
+ json_data = {}
+
+ if ignore_errors:
+ errors = 'ignore'
+
+ try:
+ result = run_program(cmd, encoding='utf-8', errors=errors)
+ json_data = json.loads(result.stdout)
+ except (subprocess.CalledProcessError, json.decoder.JSONDecodeError):
+ if not ignore_errors:
+ raise
+
+ return json_data
+
+
+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/osticket.py b/.bin/Scripts/functions/osticket.py
index 0c051974..aed84f92 100644
--- a/.bin/Scripts/functions/osticket.py
+++ b/.bin/Scripts/functions/osticket.py
@@ -18,6 +18,11 @@ REGEX_TEMPS = re.compile(r'^\s*(.*?)\s+(idle:)(.*)$')
REGEX_SENSOR = re.compile(r'^(.*?)(\s*)$')
+# Error Classes
+class osTicketConnectionError(Exception):
+ pass
+
+
# Classes
class osTicket():
"""Class to track osTicket data and functions."""
@@ -70,20 +75,15 @@ class osTicket():
# 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()
+ # Raise exception unless silenced
+ if self.db_cursor is None and not silent:
+ raise osTicketConnectionError('Failed to connect to osTicket.')
def convert_report(self, name, test):
"""Convert report into an osTicket friendly format, returns list."""
+ dev = test.dev
out_report = []
- source_report = test.report
+ source_report = test.report.copy()
status = strip_colors(test.status)
status = status.replace(test.label, '').strip()
@@ -91,9 +91,15 @@ class osTicket():
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()
+ source_report = dev.generate_attribute_report()
+
+ # Notes
+ if dev.nvme_smart_notes:
+ source_report.append('{} Notes'.format(dev.attr_type))
+ source_report.extend(sorted(dev.nvme_smart_notes.keys()))
+
+ # Test Report
+ source_report.extend(test.report.copy())
elif not source_report:
index = 0
out_report.append('{} ({})'.format(name, status))
@@ -120,6 +126,8 @@ class osTicket():
line = '{}{} {}'.format(_sensor, _spacer, _temps)
if line == 'Temps':
out_report.append(' ')
+ out_report.append('Temps')
+ continue
elif name == 'NVMe / SMART':
r = REGEX_NVME_SMART_ATTRIBUTES.match(line)
if r:
@@ -142,6 +150,10 @@ class osTicket():
line = line.strip()
line = re.sub(r'(\s+)', ' ', line)
+ # Indent line
+ if not re.match(r'^(NVMe|SMART)', line):
+ line = '... {}'.format(line)
+
# Add line to report
out_report.append(line)
@@ -175,12 +187,13 @@ class osTicket():
pass
self.tunnel_proc = None
- def generate_report(self, dev, ticket_id):
+ def generate_report(self, dev, ticket_id, ticket_name):
"""Generate device report for osTicket, returns list."""
report = []
results = self.get_device_overall_results(dev)
# Header
+ report.append('[Report for ticket #{} {}]'.format(ticket_id, ticket_name))
if results['Full Diag']:
report.append(
'{Dev Type} hardware diagnostic tests: {Status}'.format(**results))
@@ -193,7 +206,7 @@ class osTicket():
# Test reports
for name, test in dev.tests.items():
report.extend(self.convert_report(name, test))
- if name == 'I/O Benchmark':
+ if name == 'I/O Benchmark' and not test.disabled:
# Create PNG graph
try:
graph_file = export_io_graph(dev)
@@ -248,6 +261,11 @@ class osTicket():
else:
# Ensure string type
label = ''
+ fstype = v_data.get('fstype', 'UNKNOWN FS')
+ if not fstype:
+ # Either empty string or None
+ fstype = 'UNKNOWN FS'
+ fstype = str(fstype).upper()
size = v_data.get('size', '')
if size:
size = '{} {}B'.format(size[:-1], size[-1:]).upper()
@@ -255,7 +273,8 @@ class osTicket():
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_path, label, size, fstype, size_used, size_avail]
+ v_data = [str(v) for v in v_data]
v_data = [v.strip().replace(' ', '_') for v in v_data]
for i in range(len(v_data)):
pad = 8
@@ -267,7 +286,7 @@ class osTicket():
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))
+ '... {}..{}..Total..{}..({}..Used..{}..Free..{})'.format(*v_data))
# Done
return report
@@ -347,7 +366,7 @@ class osTicket():
def get_flag(self, ticket_id, flag_name):
"""Get flag in osTicket."""
flag_value = None
- self.connect(silent=True)
+ self.connect()
# Bail if disabled
if self.disabled:
@@ -392,14 +411,25 @@ class osTicket():
# Done
return name
- def get_ticket_number(self):
- """Get ticket number and confirm with name from osTicket DB."""
+ def get_ticket_details(self):
+ """Get ticket number and name from osTicket DB, returns tuple."""
+ ticket_name = None
ticket_number = None
- self.connect(silent=False)
- # Bail if disabled
- if self.disabled:
- return None
+ # Connect
+ while True:
+ try:
+ self.connect(silent=False)
+ except osTicketConnectionError:
+ print_warning('Failed to connect to osTicket')
+ if not ask('Try again?'):
+ print_standard('Integration disabled for this session')
+ self.disabled = True
+ self.tunnel_proc.kill()
+ return None
+ else:
+ # Connection successful
+ break
# Main loop
while ticket_number is None:
@@ -427,18 +457,19 @@ class osTicket():
print_standard('You have selected ticket #{} {}'.format(
_input, _name))
if ask('Is this correct?'):
+ ticket_name = _name
ticket_number = _input
# Done
self.disconnect()
- return ticket_number
+ return (ticket_number, ticket_name)
- def post_device_results(self, dev, ticket_id):
+ def post_device_results(self, dev, ticket_id, ticket_name):
"""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)
+ response = self.generate_report(dev, ticket_id, ticket_name)
self.post_response(response, ticket_id)
def post_response(self, response, ticket_id):
@@ -498,7 +529,7 @@ class osTicket():
def set_flag(self, ticket_id, flag_name, flag_value):
"""Set flag in osTicket."""
- self.connect(silent=True)
+ self.connect()
# Bail if disabled
if self.disabled:
diff --git a/.bin/Scripts/functions/sensors.py b/.bin/Scripts/functions/sensors.py
index 7a470703..8525deb8 100644
--- a/.bin/Scripts/functions/sensors.py
+++ b/.bin/Scripts/functions/sensors.py
@@ -4,19 +4,12 @@ import json
import re
from functions.tmux import *
+from settings.sensors import *
-# STATIC VARIABLES
-TEMP_LIMITS = {
- 'GREEN': 60,
- 'YELLOW': 70,
- 'ORANGE': 80,
- 'RED': 90,
- }
-
-
-# REGEX
-REGEX_COLORS = re.compile(r'\033\[\d+;?1?m')
+# Error Classes
+class ThermalLimitReachedError(Exception):
+ pass
def clear_temps(sensor_data):
@@ -112,7 +105,7 @@ def get_raw_sensor_data():
# Get raw data
try:
result = run_program(cmd)
- result = result.stdout.decode().splitlines()
+ result = result.stdout.decode('utf-8', errors='ignore').splitlines()
except subprocess.CalledProcessError:
# Assuming no sensors available, set to empty list
result = []
@@ -214,7 +207,7 @@ def save_average_temp(sensor_data, temp_label, seconds=10):
_data[temp_label] = sum(_data['Temps']) / len(_data['Temps'])
-def update_sensor_data(sensor_data):
+def update_sensor_data(sensor_data, thermal_limit=None):
"""Read sensors and update existing sensor_data, returns dict."""
json_data = get_raw_sensor_data()
for _section, _adapters in sensor_data.items():
@@ -230,6 +223,11 @@ def update_sensor_data(sensor_data):
# Dumb workound for Dell sensors with changing source names
pass
+ # Check if thermal limit reached
+ if thermal_limit and _section == 'CoreTemps':
+ if max(_data['Current'], _data['Max']) >= thermal_limit:
+ raise ThermalLimitReachedError('CoreTemps reached limit')
+
def join_columns(column1, column2, width=55):
return '{:<{}}{}'.format(
diff --git a/.bin/Scripts/functions/setup.py b/.bin/Scripts/functions/setup.py
index 0acb52ad..7c2062ac 100644
--- a/.bin/Scripts/functions/setup.py
+++ b/.bin/Scripts/functions/setup.py
@@ -1,150 +1,12 @@
# Wizard Kit: Functions - Setup
+from functions.browsers import *
from functions.update import *
+from settings.setup 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')
-OTHER_RESULTS = {
- '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',
- },
- },
- }
-SETTINGS_ESET = {
- 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},
- },
- }
-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},
- },
- }
-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,
- },
- }
-SETTINGS_MOZILLA_FIREFOX_32 = {
- 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},
- },
- }
-SETTINGS_REGBACK = {
- # 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']},
- ]
-
-
+# Configuration
def config_classicstart():
"""Configure ClassicStart."""
# User level, not system level
@@ -208,6 +70,11 @@ def config_explorer_user():
write_registry_settings(SETTINGS_EXPLORER_USER, all_users=False)
+def config_windows_updates():
+ """Configure Windows updates."""
+ write_registry_settings(SETTINGS_WINDOWS_UPDATES, all_users=True)
+
+
def create_system_restore_point():
"""Create system restore point."""
cmd = [
@@ -240,7 +107,7 @@ def enable_mini_dumps():
def enable_system_restore():
- """Enable System Restore and set disk usage to 5%"""
+ """Enable System Restore and set disk usage to 8%"""
cmd = [
'PowerShell',
'-Command', 'Enable-ComputerRestore',
@@ -373,10 +240,18 @@ def install_firefox_extensions():
run_program(cmd)
-def install_ninite_bundle(mse=False, libreoffice=False):
+def install_ninite_bundle(browsers_only=False, 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'):
+ if browsers_only:
+ installer_path = r'{BaseDir}\Installers\Extras\Web Browsers'.format(
+ **global_vars)
+ scan_for_browsers(skip_ie=True, silent=True)
+ for browser in ('Google Chrome', 'Mozilla Firefox', 'Opera Chromium'):
+ if is_installed(browser):
+ cmd = r'{}\{}.exe'.format(installer_path, browser)
+ popen_objects.append(popen_program(cmd))
+ elif global_vars['OS']['Version'] in ('8', '8.1', '10'):
# Modern selection
popen_objects.append(
popen_program(r'{BaseDir}\Installers\Extras\Bundles\Modern.exe'.format(
@@ -422,6 +297,12 @@ def open_device_manager():
popen_program(['mmc', 'devmgmt.msc'])
+def open_snappy_driver_origin():
+ cwd = r'{BinDir}\_Drivers\SDIO'.format(**global_vars)
+ cmd = [r'{}\SDIO.exe'.format(cwd)]
+ popen_program(cmd, cwd=cwd, pipe=True)
+
+
def open_windows_activation():
popen_program(['slui'])
@@ -430,6 +311,13 @@ def open_windows_updates():
popen_program(['control', '/name', 'Microsoft.WindowsUpdate'])
+def restart_explorer():
+ """Restart Explorer."""
+ kill_process('explorer.exe')
+ sleep(2)
+ kill_process('explorer.exe')
+
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
diff --git a/.bin/Scripts/functions/sw_diags.py b/.bin/Scripts/functions/sw_diags.py
index 100e5ad5..3aa2838c 100644
--- a/.bin/Scripts/functions/sw_diags.py
+++ b/.bin/Scripts/functions/sw_diags.py
@@ -1,31 +1,9 @@
-# Wizard Kit: Functions - Diagnostics
+# Wizard Kit: Functions - SW 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,
- },
- }
+from settings.sw_diags import *
def check_connection():
@@ -219,8 +197,10 @@ def run_rkill():
shutil.move(item.path, dest)
-def show_alert_box(message, title='Wizard Kit Warning'):
+def show_alert_box(message, title=None):
"""Show Windows alert box with message."""
+ if not title:
+ title = '{} Warning'.format(KIT_NAME_FULL)
message_box = ctypes.windll.user32.MessageBoxW
message_box(None, message, title, 0x00001030)
diff --git a/.bin/Scripts/functions/tmux.py b/.bin/Scripts/functions/tmux.py
index e2d1b333..81522268 100644
--- a/.bin/Scripts/functions/tmux.py
+++ b/.bin/Scripts/functions/tmux.py
@@ -10,6 +10,21 @@ def create_file(filepath):
f.write('')
+def tmux_capture_pane(pane_id=None):
+ """Capture text from target, or current, pane, returns str."""
+ cmd = ['tmux', 'capture-pane', '-p']
+ if pane_id:
+ cmd.extend(['-t', pane_id])
+ text = ''
+
+ # Capture
+ result = run_program(cmd, check=False, encoding='utf-8', errors='ignore')
+ text = result.stdout
+
+ # Done
+ return str(text)
+
+
def tmux_get_pane_size(pane_id=None):
"""Get target, or current, pane size, returns tuple."""
x = -1
diff --git a/.bin/Scripts/functions/update.py b/.bin/Scripts/functions/update.py
old mode 100644
new mode 100755
index 9ea639ce..6ebc52f2
--- a/.bin/Scripts/functions/update.py
+++ b/.bin/Scripts/functions/update.py
@@ -67,6 +67,22 @@ def download_to_temp(out_name, source_url):
download_generic(global_vars['TmpDir'], out_name, source_url)
+def download_windows_updates():
+ """Download stand alone Windows Update installers."""
+ # Prep
+ dest = r'{}\Installers\Extras\Windows Updates'.format(
+ global_vars['BaseDir'])
+
+
+ # Download
+ for kb, v in WINDOWS_UPDATE_SOURCES.items():
+ for winver, v2 in v.items():
+ for arch, url in v2.items():
+ name = 'KB{}-Windows{}-x{}.msu'.format(kb, winver, arch)
+ if not os.path.exists(r'{}\{}'.format(dest, name)):
+ download_generic(dest, name, url)
+
+
def extract_generic(source, dest, mode='x', sz_args=[]):
"""Extract a file to a destination."""
cmd = [
@@ -933,8 +949,13 @@ def update_wiztree():
# Extract files
extract_temp_to_cbin('wiztree.zip', 'WizTree')
+ ## NOTE: It's double-zipped for some reason?
+ extract_generic(
+ r'{}\WizTree\wiztree'.format(global_vars['CBinDir']),
+ r'{}\WizTree'.format(global_vars['CBinDir']))
# Cleanup
+ remove_item(r'{}\WizTree\wiztree'.format(global_vars['CBinDir']))
remove_from_temp('wiztree.zip')
diff --git a/.bin/Scripts/functions/windows_setup.py b/.bin/Scripts/functions/windows_setup.py
index e790a4b6..eabaac40 100644
--- a/.bin/Scripts/functions/windows_setup.py
+++ b/.bin/Scripts/functions/windows_setup.py
@@ -2,39 +2,7 @@
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 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'},
- ]
+from settings.windows_setup import *
def find_windows_image(windows_version):
diff --git a/.bin/Scripts/functions/windows_updates.py b/.bin/Scripts/functions/windows_updates.py
index 4257f066..3618fbb2 100644
--- a/.bin/Scripts/functions/windows_updates.py
+++ b/.bin/Scripts/functions/windows_updates.py
@@ -4,47 +4,138 @@ from functions.common import *
# Functions
+def delete_folder(folder_path):
+ """Near-useless wrapper for shutil.rmtree."""
+ shutil.rmtree(folder_path)
+
+
def disable_service(service_name):
"""Set service startup to disabled."""
run_program(['sc', 'config', service_name, 'start=', 'disabled'])
+ # Verify service was disabled
+ start_type = get_service_start_type(service_name)
+ if not start_type.lower().startswith('disabled'):
+ raise GenericError('Failed to disable service {}'.format(service_name))
+
def disable_windows_updates():
"""Disable windows updates and clear SoftwareDistribution folder."""
+ indent=2
+ width=52
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)
+ # Stop service
+ result = try_and_print(
+ 'Stopping service {}...'.format(service),
+ indent=indent, width=width,
+ function=stop_service, service_name=service)
+ if not result['CS']:
+ result = try_and_print(
+ 'Stopping service {}...'.format(service),
+ indent=indent, width=width,
+ function=stop_service, service_name=service)
+ if not result['CS']:
+ raise GenericError('Service {} could not be stopped.'.format(service))
+
+ # Disable service
+ result = try_and_print(
+ 'Disabling service {}...'.format(service),
+ indent=indent, width=width,
+ function=disable_service, service_name=service)
+ if not result['CS']:
+ result = try_and_print(
+ 'Disabling service {}...'.format(service),
+ indent=indent, width=width,
+ function=disable_service, service_name=service)
+ if not result['CS']:
+ raise GenericError('Service {} could not be disabled.'.format(service))
# Delete update folders
for folder_path in update_folders:
if os.path.exists(folder_path):
- shutil.rmtree(folder_path)
+ result = try_and_print(
+ 'Deleting folder {}...'.format(folder_path),
+ indent=indent, width=width,
+ function=delete_folder, folder_path=folder_path)
+ if not result['CS']:
+ raise GenericError('Failed to remove folder {}'.format(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_service(service_name, start_type='auto'):
+ """Enable service by setting start type."""
+ run_program(['sc', 'config', service_name, 'start=', start_type])
-def enable_windows_updates():
+def enable_windows_updates(silent=False):
"""Enable windows updates"""
- enable_service('bits')
- enable_service('wuauserv')
+ indent=2
+ width=52
+
+ for service in ('bits', 'wuauserv'):
+ # Enable service
+ start_type = 'auto'
+ if service == 'wuauserv':
+ start_type = 'demand'
+ if silent:
+ try:
+ enable_service(service, start_type=start_type)
+ except Exception:
+ # Try again
+ enable_service(service, start_type=start_type)
+ else:
+ result = try_and_print(
+ 'Enabling service {}...'.format(service),
+ indent=indent, width=width,
+ function=enable_service, service_name=service, start_type=start_type)
+ if not result['CS']:
+ result = try_and_print(
+ 'Enabling service {}...'.format(service),
+ indent=indent, width=width,
+ function=enable_service, service_name=service, start_type=start_type)
+ if not result['CS']:
+ raise GenericError('Service {} could not be enabled.'.format(service))
+
+
+def get_service_status(service_name):
+ """Get service status using psutil, returns str."""
+ status = 'Unknown'
+ try:
+ service = psutil.win_service_get(service_name)
+ status = service.status()
+ except psutil.NoSuchProcess:
+ # Ignore and return 'Unknown' below
+ pass
+
+ return status
+
+
+def get_service_start_type(service_name):
+ """Get service startup type using psutil, returns str."""
+ start_type = 'Unknown'
+ try:
+ service = psutil.win_service_get(service_name)
+ start_type = service.start_type()
+ except psutil.NoSuchProcess:
+ # Ignore and return 'Unknown' below
+ pass
+
+ return start_type
def stop_service(service_name):
"""Stop service."""
run_program(['net', 'stop', service_name], check=False)
+ # Verify service was stopped
+ status = get_service_status(service_name)
+ if not status.lower().startswith('stopped'):
+ raise GenericError('Failed to stop service {}'.format(service_name))
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
diff --git a/.bin/Scripts/functions/winpe_menus.py b/.bin/Scripts/functions/winpe_menus.py
index 22df7449..08622e4e 100644
--- a/.bin/Scripts/functions/winpe_menus.py
+++ b/.bin/Scripts/functions/winpe_menus.py
@@ -3,53 +3,7 @@
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)),
- ]
-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'],
- },
- }
+from settings.winpe import *
def check_pe_tools():
diff --git a/.bin/Scripts/hw-diags-menu b/.bin/Scripts/hw-diags-menu
index 1c3cfc42..58bf5280 100755
--- a/.bin/Scripts/hw-diags-menu
+++ b/.bin/Scripts/hw-diags-menu
@@ -26,8 +26,37 @@ if __name__ == '__main__':
# Normal exit
pass
except:
+ # Cleanup
tmux_kill_all_panes()
- major_exception()
+
+ if DEBUG_MODE:
+ # Custom major exception
+ print_standard(' ')
+ print_error('Major exception')
+ print_warning(SUPPORT_MESSAGE)
+ print(traceback.format_exc())
+ print_log(traceback.format_exc())
+
+ # Save debug reports and upload data
+ try_and_print(
+ message='Saving debug reports...',
+ function=save_debug_reports,
+ state=state, global_vars=global_vars)
+ question = 'Upload crash details to {}?'.format(CRASH_SERVER['Name'])
+ if ENABLED_UPLOAD_DATA and ask(question):
+ try_and_print(
+ message='Uploading Data...',
+ function=upload_logdir,
+ global_vars=global_vars)
+
+ # Done
+ sleep(1)
+ pause('Press Enter to exit...')
+ exit_script(1)
+
+ else:
+ # "Normal" major exception
+ major_exception()
# Done
tmux_kill_all_panes()
diff --git a/.bin/Scripts/install_sw_bundle.py b/.bin/Scripts/install_sw_bundle.py
index a12a3a00..25f45638 100644
--- a/.bin/Scripts/install_sw_bundle.py
+++ b/.bin/Scripts/install_sw_bundle.py
@@ -28,7 +28,7 @@ if __name__ == '__main__':
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_ninite = True
answer_mse = False
else:
answer_ninite = ask('Install Ninite Bundle?')
@@ -42,9 +42,13 @@ if __name__ == '__main__':
if answer_vcr:
install_vcredists()
if answer_ninite:
- result = try_and_print(message='Ninite bundle...',
+ message='Ninite bundle...'
+ if D7_MODE:
+ message='Updating browsers...'
+ result = try_and_print(message=message,
function=install_ninite_bundle, cs='Started',
- mse=answer_mse, other_results=other_results)
+ browsers_only=D7_MODE, mse=answer_mse,
+ other_results=other_results)
for proc in result['Out']:
# Wait for all processes to finish
proc.wait()
diff --git a/.bin/Scripts/new_system_setup.py b/.bin/Scripts/new_system_setup.py
index 5320403d..f3e3e9b2 100644
--- a/.bin/Scripts/new_system_setup.py
+++ b/.bin/Scripts/new_system_setup.py
@@ -87,13 +87,17 @@ if __name__ == '__main__':
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()
+ try:
+ for proc in result['Out']:
+ # Wait for all processes to finish
+ print_standard('Waiting for installations to finish...')
+ proc.wait()
+ except KeyboardInterrupt:
+ pass
# Scan for supported browsers
print_info('Scanning for browsers')
- scan_for_browsers()
+ scan_for_browsers(skip_ie=True)
# Install extensions
print_info('Installing Extensions')
@@ -108,27 +112,40 @@ if __name__ == '__main__':
# Configure software
print_info('Configuring programs')
- install_adblock()
+ print_standard(' (if stuck press CTRL+c to cancel this step).')
+ try:
+ install_adblock()
+ except KeyboardInterrupt:
+ print_warning('Configuration interrupted.')
+ print_standard('Please confirm all browsers have adblock installed.')
+ if not ask('Continue to next step?'):
+ abort()
if global_vars['OS']['Version'] == '10':
try_and_print(message='ClassicStart...',
function=config_classicstart, cs='Done')
- try_and_print(message='Explorer...',
+ try_and_print(message='Explorer (user)...',
function=config_explorer_user, cs='Done')
# Configure system
print_info('Configuring system')
if global_vars['OS']['Version'] == '10':
- try_and_print(message='Explorer...',
+ try_and_print(message='Explorer (system)...',
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='Windows Updates...',
+ function=config_windows_updates, 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')
+ # Restart Explorer
+ try_and_print(message='Restarting Explorer...',
+ function=restart_explorer, cs='Done')
+
# Summary
print_info('Summary')
try_and_print(message='Operating System:',
@@ -154,6 +171,8 @@ if __name__ == '__main__':
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 SDI Origin...',
+ function=open_snappy_driver_origin, cs='Started')
try_and_print(message='Opening Windows Updates...',
function=open_windows_updates, cs='Started')
if not windows_is_activated():
diff --git a/.bin/Scripts/post_d7.py b/.bin/Scripts/post_d7.py
index e72c5869..2fa8f9f2 100644
--- a/.bin/Scripts/post_d7.py
+++ b/.bin/Scripts/post_d7.py
@@ -14,37 +14,42 @@ os.system('title {}: Post-d7II Work'.format(KIT_NAME_FULL))
set_log_file('Post-d7II Work.log')
if __name__ == '__main__':
- try:
- stay_awake()
- clear_screen()
- print_info('{}: Post-d7II Work\n'.format(KIT_NAME_FULL))
- other_results = {
- 'Warning': {
- 'NotInstalledError': 'Not installed',
- 'NoProfilesError': 'No profiles found',
- }}
+ try:
+ stay_awake()
+ clear_screen()
+ print_info('{}: Post-d7II Work\n'.format(KIT_NAME_FULL))
+ other_results = {
+ 'Warning': {
+ 'NotInstalledError': 'Not installed',
+ 'NoProfilesError': 'No profiles found',
+ }}
- # Scan for Firefox browsers
- print_info('Scanning for Firefox browsers')
- scan_for_browsers(just_firefox=True)
+ # Scan for Firefox browsers
+ print_info('Scanning for Firefox browsers')
+ scan_for_browsers(just_firefox=True)
- # Install uBlock Origin
- print_info('Installing uBlock Origin')
- install_adblock(just_firefox=True)
+ # Install uBlock Origin
+ print_info('Installing uBlock Origin')
+ install_adblock(just_firefox=True)
- # Cleanup
- print_info('Cleanup')
- try_and_print(message='d7II...',
- function=cleanup_d7ii, cs='Done')
- try_and_print(message='{}...'.format(KIT_NAME_FULL),
- function=delete_empty_folders, cs='Done',
- folder_path=global_vars['ClientDir'])
+ # Cleanup
+ print_info('Cleanup')
+ try_and_print(message='d7II...',
+ function=cleanup_d7ii, cs='Done')
+ try_and_print(message='{}...'.format(KIT_NAME_FULL),
+ function=delete_empty_folders, cs='Done',
+ folder_path=global_vars['ClientDir'])
- # Done
- print_standard('\nDone.')
- pause('Press Enter to exit...')
- exit_script()
- except SystemExit:
- pass
- except:
- major_exception()
+ # Run speedtest
+ popen_program(['start', '', 'https://fast.com'], shell=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/reset_browsers.py b/.bin/Scripts/reset_browsers.py
index 4f1efbf9..58f15f42 100644
--- a/.bin/Scripts/reset_browsers.py
+++ b/.bin/Scripts/reset_browsers.py
@@ -31,7 +31,7 @@ if __name__ == '__main__':
# Scan for supported browsers
print_info('Scanning for browsers')
- scan_for_browsers()
+ scan_for_browsers(skip_ie=True)
# Homepages
print_info('Current homepages')
diff --git a/.bin/Scripts/settings/browsers.py b/.bin/Scripts/settings/browsers.py
new file mode 100644
index 00000000..dc394342
--- /dev/null
+++ b/.bin/Scripts/settings/browsers.py
@@ -0,0 +1,99 @@
+# Wizard Kit: Settings - Browsers
+
+import os
+import re
+
+# General
+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"',
+ }
+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',
+ },
+ }
+
+# 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)
+
+# uBlock Origin
+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'
+UBO_EXTRA_CHROME_REG = r'Software\Wow6432Node\Google\Chrome\Extensions\pgdnlhfefecpicbbihgmbmffkjpaplco'
+UBO_MOZILLA = 'https://addons.mozilla.org/en-us/firefox/addon/ublock-origin/'
+UBO_MOZZILA_PATH = r'{}\Mozilla Firefox\distribution\extensions\ublock_origin.xpi'.format(os.environ.get('PROGRAMFILES'))
+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'
+
+
+if __name__ == '__main__':
+ print("This file is not meant to be called directly.")
+
+# vim: sts=2 sw=2 ts=2 tw=0
diff --git a/.bin/Scripts/settings/data.py b/.bin/Scripts/settings/data.py
new file mode 100644
index 00000000..4dd29d03
--- /dev/null
+++ b/.bin/Scripts/settings/data.py
@@ -0,0 +1,105 @@
+# Wizard Kit: Settings - Data
+
+import ctypes
+import re
+
+from settings.main import *
+
+# FastCopy
+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',
+ ]
+FAST_COPY_ARGS = [
+ '/cmd=noexist_only',
+ '/utf8',
+ '/skip_empty_dir',
+ '/linkdest',
+ '/no_ui',
+ '/auto_close',
+ '/exclude={}'.format(';'.join(FAST_COPY_EXCLUDES)),
+ ]
+
+# 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)
+
+# Thread error modes
+## 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
+
+
+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/ddrescue.py b/.bin/Scripts/settings/ddrescue.py
new file mode 100644
index 00000000..675019ca
--- /dev/null
+++ b/.bin/Scripts/settings/ddrescue.py
@@ -0,0 +1,53 @@
+# Wizard Kit: Settings - ddrescue-tui
+
+import re
+
+from collections import OrderedDict
+
+# General
+RECOMMENDED_FSTYPES = ['ext3', 'ext4', 'xfs']
+USAGE = """ {script_name} clone [source [destination]]
+ {script_name} image [source [destination]]
+ (e.g. {script_name} clone /dev/sda /dev/sdb)
+"""
+
+# Layout
+SIDE_PANE_WIDTH = 21
+TMUX_LAYOUT = OrderedDict({
+ 'Source': {'y': 2, 'Check': True},
+ 'Started': {'x': SIDE_PANE_WIDTH, 'Check': True},
+ 'Progress': {'x': SIDE_PANE_WIDTH, 'Check': True},
+})
+
+# ddrescue
+AUTO_PASS_1_THRESHOLD = 95
+AUTO_PASS_2_THRESHOLD = 98
+DDRESCUE_SETTINGS = {
+ '--binary-prefixes': {'Enabled': True, 'Hidden': True, },
+ '--data-preview': {'Enabled': True, 'Value': '5', 'Hidden': True, },
+ '--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, },
+ }
+ETOC_REFRESH_RATE = 30 # in seconds
+REGEX_REMAINING_TIME = re.compile(
+ r'remaining time:'
+ r'\s*((?P\d+)d)?'
+ r'\s*((?P\d+)h)?'
+ r'\s*((?P\d+)m)?'
+ r'\s*((?P\d+)s)?'
+ r'\s*(?Pn/a)?',
+ re.IGNORECASE
+ )
+
+
+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/hw_diags.py b/.bin/Scripts/settings/hw_diags.py
new file mode 100644
index 00000000..9f0e1391
--- /dev/null
+++ b/.bin/Scripts/settings/hw_diags.py
@@ -0,0 +1,112 @@
+# Wizard Kit: Settings - HW Diagnostics
+
+from collections import OrderedDict
+
+# General
+DEBUG_MODE = True
+OVERRIDES_FORCED = False
+OVERRIDES_LIMITED = True # If True this disables OVERRIDE_FORCED
+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',
+ ]
+
+# Layout
+## NOTE: Colors will be applied in functions/hw_diags.py
+QUICK_LABEL = '{YELLOW}(Quick){CLEAR}'
+SIDE_PANE_WIDTH = 20
+TOP_PANE_TEXT = '{GREEN}Hardware Diagnostics{CLEAR}'
+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},
+})
+
+# Tests: badblocks
+## NOTE: Force 4K read block size for disks >= to 3TB
+BADBLOCKS_LARGE_DISK = 3*1024**4
+
+# Tests: I/O Benchmark
+IO_VARS = {
+ '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': (
+ '▏', '▎', '▍', '▌',
+ '▋', '▊', '▉', '█',
+ '█▏', '█▎', '█▍', '█▌',
+ '█▋', '█▊', '█▉', '██',
+ '██▏', '██▎', '██▍', '██▌',
+ '██▋', '██▊', '██▉', '███',
+ '███▏', '███▎', '███▍', '███▌',
+ '███▋', '███▊', '███▉', '████'),
+ }
+
+# Tests: NVMe/SMART
+ATTRIBUTES = {
+ 'NVMe': {
+ 'critical_warning': {'Critical': True, 'Ignore': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
+ 'media_errors': {'Critical': True, 'Ignore': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
+ 'power_on_hours': {'Critical': False, 'Ignore': True, 'Warning': 17532, 'Error': 26298, 'Maximum': None, },
+ 'unsafe_shutdowns': {'Critical': False, 'Ignore': True, 'Warning': 1, 'Error': None, 'Maximum': None, },
+ },
+ 'SMART': {
+ 5: {'Hex': '05', 'Critical': True, 'Ignore': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
+ 9: {'Hex': '09', 'Critical': False, 'Ignore': True, 'Warning': 17532, 'Error': 26298, 'Maximum': None, },
+ 10: {'Hex': '10', 'Critical': False, 'Ignore': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
+ 184: {'Hex': 'B8', 'Critical': False, 'Ignore': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
+ 187: {'Hex': 'BB', 'Critical': False, 'Ignore': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
+ 188: {'Hex': 'BC', 'Critical': False, 'Ignore': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
+ 196: {'Hex': 'C4', 'Critical': False, 'Ignore': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
+ 197: {'Hex': 'C5', 'Critical': True, 'Ignore': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
+ 198: {'Hex': 'C6', 'Critical': True, 'Ignore': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
+ 199: {'Hex': 'C7', 'Critical': False, 'Ignore': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
+ 201: {'Hex': 'C9', 'Critical': False, 'Ignore': False, 'Warning': None, 'Error': 1, 'Maximum': 10000, },
+ },
+ }
+ATTRIBUTE_COLORS = (
+ ('Error', 'RED'),
+ ('Maximum', 'PURPLE'),
+ ('Warning', 'YELLOW'),
+ )
+KEY_NVME = 'nvme_smart_health_information_log'
+KEY_SMART = 'ata_smart_attributes'
+
+# Tests: Prime95
+MPRIME_LIMIT = 7 # of minutes to run Prime95
+THERMAL_LIMIT = 99 # Abort temperature in Celsius
+
+
+if __name__ == '__main__':
+ print("This file is not meant to be called directly.")
+
+# vim: sts=2 sw=2 ts=2 tw=0
diff --git a/.bin/Scripts/settings/info.py b/.bin/Scripts/settings/info.py
new file mode 100644
index 00000000..bb1f1421
--- /dev/null
+++ b/.bin/Scripts/settings/info.py
@@ -0,0 +1,58 @@
+# Wizard Kit: Settings - Information
+
+import re
+
+# General
+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',
+]
+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}',
+ ),
+}
+
+# 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)
+
+
+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/launchers.py b/.bin/Scripts/settings/launchers.py
index 5272b64d..76dbd7d9 100644
--- a/.bin/Scripts/settings/launchers.py
+++ b/.bin/Scripts/settings/launchers.py
@@ -81,14 +81,14 @@ LAUNCHERS = {
'L_TYPE': 'PyScript',
'L_PATH': 'Scripts',
'L_ITEM': 'windows_updates.py',
- 'L_ARGS': '--disable',
+ 'L_ARGS': 'd7mode --disable',
'L_ELEV': 'True',
},
'Enable Windows Updates': {
'L_TYPE': 'PyScript',
'L_PATH': 'Scripts',
'L_ITEM': 'windows_updates.py',
- 'L_ARGS': '--enable',
+ 'L_ARGS': 'd7mode --enable',
'L_ELEV': 'True',
},
},
@@ -380,8 +380,8 @@ LAUNCHERS = {
'Intel RST (Current Release)': {
'L_TYPE': 'Executable',
'L_PATH': '_Drivers\Intel RST',
- 'L_ITEM': 'SetupRST_16.8.exe',
- 'L_7ZIP': 'SetupRST_16.8.exe',
+ 'L_ITEM': 'SetupRST_17.2.exe',
+ 'L_7ZIP': 'SetupRST_17.2.exe',
},
'Intel RST (Previous Releases)': {
'L_TYPE': 'Folder',
@@ -737,6 +737,7 @@ LAUNCHERS = {
},
}
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
diff --git a/.bin/Scripts/settings/main.py b/.bin/Scripts/settings/main.py
index 5869580b..c104a45f 100644
--- a/.bin/Scripts/settings/main.py
+++ b/.bin/Scripts/settings/main.py
@@ -4,8 +4,6 @@
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
@@ -17,7 +15,6 @@ SUPPORT_MESSAGE='Please let support know by opening an issue on Gogs'
# imgur
IMGUR_CLIENT_ID='3d1ee1d38707b85'
# Live Linux
-MPRIME_LIMIT='7' # of minutes to run Prime95 during hw-diags
ROOT_PASSWORD='1201 loves computers!'
TECH_PASSWORD='Sorted1201'
# Root Certificate Authority
@@ -91,6 +88,7 @@ WINDOWS_SERVER = {
'RW-Pass': '1201 loves computers!',
}
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
diff --git a/.bin/Scripts/settings/music.py b/.bin/Scripts/settings/music.py
index 37f5d178..63163a40 100644
--- a/.bin/Scripts/settings/music.py
+++ b/.bin/Scripts/settings/music.py
@@ -66,6 +66,7 @@ MUSIC_SNES = [
'zamn'
]
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
diff --git a/.bin/Scripts/settings/partition_uids.py b/.bin/Scripts/settings/partition_uids.py
index f4b21df0..29817b2c 100644
--- a/.bin/Scripts/settings/partition_uids.py
+++ b/.bin/Scripts/settings/partition_uids.py
@@ -319,6 +319,7 @@ PARTITION_UIDS = {
'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.")
diff --git a/.bin/Scripts/settings/sensors.py b/.bin/Scripts/settings/sensors.py
new file mode 100644
index 00000000..cdfdf708
--- /dev/null
+++ b/.bin/Scripts/settings/sensors.py
@@ -0,0 +1,21 @@
+# Wizard Kit: Settings - Sensors
+
+import re
+
+# General
+TEMP_LIMITS = {
+ 'GREEN': 60,
+ 'YELLOW': 70,
+ 'ORANGE': 80,
+ 'RED': 90,
+ }
+
+
+# Regex
+REGEX_COLORS = re.compile(r'\033\[\d+;?1?m')
+
+
+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/setup.py b/.bin/Scripts/settings/setup.py
new file mode 100644
index 00000000..db30fe19
--- /dev/null
+++ b/.bin/Scripts/settings/setup.py
@@ -0,0 +1,205 @@
+# Wizard Kit: Settings - Setup
+
+import os
+import winreg
+
+# General
+HKU = winreg.HKEY_USERS
+HKCR = winreg.HKEY_CLASSES_ROOT
+HKCU = winreg.HKEY_CURRENT_USER
+HKLM = winreg.HKEY_LOCAL_MACHINE
+OTHER_RESULTS = {
+ 'Error': {
+ 'CalledProcessError': 'Unknown Error',
+ 'FileNotFoundError': 'File not found',
+ },
+ 'Warning': {},
+ }
+
+# Browsers
+MOZILLA_FIREFOX_UBO_PATH = r'{}\{}\ublock_origin.xpi'.format(
+ os.environ.get('PROGRAMFILES'),
+ r'Mozilla Firefox\distribution\extensions')
+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,
+ },
+ }
+SETTINGS_MOZILLA_FIREFOX_32 = {
+ 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},
+ },
+ }
+
+# Classic Start
+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',
+ },
+ },
+ }
+
+# ESET
+SETTINGS_ESET = {
+ r'Software\ESET\ESET Security\CurrentVersion\gui\UI_CONFIG': {
+ 'DWORD Items': {
+ 'FullScreenMode': 0,
+ 'ShowDesktopAlert': 0,
+ 'ShowSplash': 0,
+ },
+ },
+ }
+
+# Explorer
+SETTINGS_EXPLORER_SYSTEM = {
+ # Disable edge swipe
+ r'Software\Policies\Microsoft\Windows\EdgeUI': {
+ 'DWORD Items': {'AllowEdgeSwipe': 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': {
+ # Using SOFTWARE in all caps to avoid collision with 32-bit setting below
+ '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 = {
+ # Desktop icons
+ r'Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\ClassicStartMenu': {
+ 'DWORD Items': {
+ # This PC
+ '{20D04FE0-3AEA-1069-A2D8-08002B30309D}': 0,
+ # User's Folder
+ '{59031a47-3f72-44a7-89c5-5595fe6b30ee}': 0,
+ },
+ },
+ r'Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel': {
+ 'DWORD Items': {
+ # This PC
+ '{20D04FE0-3AEA-1069-A2D8-08002B30309D}': 0,
+ # User's Folder
+ '{59031a47-3f72-44a7-89c5-5595fe6b30ee}': 0,
+ },
+ },
+ # Disable features
+ r'Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager': {
+ 'DWORD Items': {
+ # Silently installed apps
+ 'SilentInstalledAppsEnabled': 0,
+ # Tips and Tricks
+ 'SoftLandingEnabled ': 0,
+ 'SubscribedContent-338389Enabled': 0,
+ },
+ },
+ # File Explorer
+ r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced': {
+ 'DWORD Items': {
+ # Replace PowerShell with CMD in Win+X menu
+ 'DontUsePowerShellOnWinX': 1,
+ # Change default Explorer view to "Computer"
+ 'LaunchTo': 1,
+ # Launch Folder Windows in a Separate Process
+ 'SeparateProcess': 1,
+ },
+ },
+ # 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': 1},
+ },
+ }
+
+# Registry
+SETTINGS_REGBACK = {
+ # Enable RegBack
+ r'System\CurrentControlSet\Control\Session Manager\Configuration Manager': {
+ 'DWORD Items': {'EnablePeriodicBackup': 1},
+ },
+ }
+
+# Visual C++ Runtimes
+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']},
+ ]
+
+# Windows Updates
+SETTINGS_WINDOWS_UPDATES = {
+ r'Software\Microsoft\WindowsUpdate\UX\Settings': {
+ 'DWORD Items': {
+ # Set to non-targeted readiness level
+ 'BranchReadinessLevel': 32,
+ 'DeferFeatureUpdatesPeriodInDays': 60,
+ },
+ }
+ }
+
+
+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 cc77f066..517add65 100644
--- a/.bin/Scripts/settings/sources.py
+++ b/.bin/Scripts/settings/sources.py
@@ -1,7 +1,7 @@
# Wizard Kit: Settings - Sources
SOURCE_URLS = {
- 'Adobe Reader DC': 'http://ardownload.adobe.com/pub/adobe/reader/win/AcrobatDC/1901020069/AcroRdrDC1901020069_en_US.exe',
+ 'Adobe Reader DC': 'http://ardownload.adobe.com/pub/adobe/reader/win/AcrobatDC/1901020098/AcroRdrDC1901020098_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',
@@ -15,23 +15,23 @@ SOURCE_URLS = {
'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',
+ 'Everything32': 'https://www.voidtools.com/Everything-1.4.1.935.x86.en-US.zip',
+ 'Everything64': 'https://www.voidtools.com/Everything-1.4.1.935.x64.en-US.zip',
+ 'FastCopy': 'http://ftp.vector.co.jp/71/31/2323/FastCopy363_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',
+ 'Firefox uBO': 'https://addons.mozilla.org/firefox/downloads/file/1709472/ublock_origin-1.18.6-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',
+ 'HWiNFO': 'http://files2.majorgeeks.com/377527622c5325acc1cb937fb149d0de922320c0/systeminfo/hwi_602.zip',
+ 'Intel SSD Toolbox': r'https://downloadmirror.intel.com/28593/eng/Intel%20SSD%20Toolbox%20-%20v3.5.9.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',
+ 'NotepadPlusPlus': 'https://notepad-plus-plus.org/repository/7.x/7.6.4/npp.7.6.4.bin.minimalist.7z',
+ 'Office Deployment Tool': 'https://download.microsoft.com/download/2/7/A/27AF1BE6-DD20-4CB4-B154-EBAB8A7D4A7E/officedeploymenttool_11509-33604.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',
@@ -46,7 +46,7 @@ SOURCE_URLS = {
'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',
+ 'WizTree': 'https://antibody-software.com/files/wiztree_3_28_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',
@@ -100,7 +100,7 @@ NINITE_SOURCES = {
'SugarSync.exe': 'sugarsync',
},
'Communication': {
- 'Discord': 'discord',
+ 'Discord.exe': 'discord',
'Pidgin.exe': 'pidgin',
'Skype.exe': 'skype',
'Trillian.exe': 'trillian',
@@ -112,8 +112,6 @@ NINITE_SOURCES = {
},
'Developer': {
'Eclipse.exe': 'eclipse',
- 'JDK 8.exe': 'jdk8',
- 'JDK 8 (x64).exe': 'jdkx8',
'Notepad++.exe': 'notepadplusplus',
'PuTTY.exe': 'putty',
'Python 2.exe': 'python',
@@ -156,7 +154,6 @@ NINITE_SOURCES = {
'Runtimes': {
'Adobe Air.exe': 'air',
'dotNET.exe': '.net4.7.2',
- 'Java 8.exe': 'java8',
'Shockwave.exe': 'shockwave',
'Silverlight.exe': 'silverlight',
},
@@ -197,12 +194,31 @@ RST_SOURCES = {
'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_15.9.exe': 'https://downloadmirror.intel.com/28656/eng/SetupRST.exe',
#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',
+ 'SetupRST_16.8.exe': 'https://downloadmirror.intel.com/28653/eng/SetupRST.exe',
+ 'SetupRST_17.2.exe': 'https://downloadmirror.intel.com/28650/eng/SetupRST.exe',
}
+WINDOWS_UPDATE_SOURCES = {
+ '2999226': {
+ # https://support.microsoft.com/en-us/help/2999226/update-for-universal-c-runtime-in-windows
+ '7': {
+ '32': 'https://download.microsoft.com/download/4/F/E/4FE73868-5EDD-4B47-8B33-CE1BB7B2B16A/Windows6.1-KB2999226-x86.msu',
+ '64': 'https://download.microsoft.com/download/1/1/5/11565A9A-EA09-4F0A-A57E-520D5D138140/Windows6.1-KB2999226-x64.msu',
+ },
+ '8': {
+ '32': 'https://download.microsoft.com/download/1/E/8/1E8AFE90-5217-464D-9292-7D0B95A56CE4/Windows8-RT-KB2999226-x86.msu',
+ '64': 'https://download.microsoft.com/download/A/C/1/AC15393F-A6E6-469B-B222-C44B3BB6ECCC/Windows8-RT-KB2999226-x64.msu',
+ },
+ '8.1': {
+ '32': 'https://download.microsoft.com/download/E/4/6/E4694323-8290-4A08-82DB-81F2EB9452C2/Windows8.1-KB2999226-x86.msu',
+ '64': 'https://download.microsoft.com/download/9/6/F/96FD0525-3DDF-423D-8845-5F92F4A6883E/Windows8.1-KB2999226-x64.msu',
+ },
+ },
+ }
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
diff --git a/.bin/Scripts/settings/sw_diags.py b/.bin/Scripts/settings/sw_diags.py
new file mode 100644
index 00000000..00892943
--- /dev/null
+++ b/.bin/Scripts/settings/sw_diags.py
@@ -0,0 +1,29 @@
+# Wizard Kit: Settings - SW Diagnostics
+
+# General
+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,
+ },
+ }
+
+
+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/tools.py b/.bin/Scripts/settings/tools.py
index 0ee74dec..dd3812e1 100644
--- a/.bin/Scripts/settings/tools.py
+++ b/.bin/Scripts/settings/tools.py
@@ -52,6 +52,7 @@ TOOLS = {
'32': r'XMPlay\xmplay.exe'},
}
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
diff --git a/.bin/Scripts/settings/windows_builds.py b/.bin/Scripts/settings/windows_builds.py
index 216be21f..db538bf2 100644
--- a/.bin/Scripts/settings/windows_builds.py
+++ b/.bin/Scripts/settings/windows_builds.py
@@ -187,8 +187,29 @@ WINDOWS_BUILDS = {
'18290': ('10', None, '19H1', None, 'preview build'),
'18298': ('10', None, '19H1', None, 'preview build'),
'18305': ('10', None, '19H1', None, 'preview build'),
+ '18309': ('10', None, '19H1', None, 'preview build'),
+ '18312': ('10', None, '19H1', None, 'preview build'),
+ '18317': ('10', None, '19H1', None, 'preview build'),
+ '18323': ('10', None, '19H1', None, 'preview build'),
+ '18329': ('10', None, '19H1', None, 'preview build'),
+ '18334': ('10', None, '19H1', None, 'preview build'),
+ '18342': ('10', None, '19H1', None, 'preview build'),
+ '18343': ('10', None, '19H1', None, 'preview build'),
+ '18346': ('10', None, '19H1', None, 'preview build'),
+ '18348': ('10', None, '19H1', None, 'preview build'),
+ '18351': ('10', None, '19H1', None, 'preview build'),
+ '18353': ('10', None, '19H1', None, 'preview build'),
+ '18356': ('10', None, '19H1', None, 'preview build'),
+ '18358': ('10', None, '19H1', None, 'preview build'),
+ '18361': ('10', None, '19H1', None, 'preview build'),
+ '18836': ('10', None, '20H1', None, 'preview build'),
+ '18841': ('10', None, '20H1', None, 'preview build'),
+ '18845': ('10', None, '20H1', None, 'preview build'),
+ '18850': ('10', None, '20H1', None, 'preview build'),
+ '18855': ('10', None, '20H1', None, 'preview build'),
}
+
if __name__ == '__main__':
print("This file is not meant to be called directly.")
diff --git a/.bin/Scripts/settings/windows_setup.py b/.bin/Scripts/settings/windows_setup.py
new file mode 100644
index 00000000..2f01c506
--- /dev/null
+++ b/.bin/Scripts/settings/windows_setup.py
@@ -0,0 +1,39 @@
+# Wizard Kit: Settings - Windows Setup
+
+# General
+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 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'},
+ ]
+
+
+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/winpe.py b/.bin/Scripts/settings/winpe.py
new file mode 100644
index 00000000..2d52353b
--- /dev/null
+++ b/.bin/Scripts/settings/winpe.py
@@ -0,0 +1,57 @@
+# Wizard Kit: Settings - WinPE
+
+from settings.data import *
+
+# FastCopy
+FAST_COPY_PE_ARGS = [
+ '/cmd=noexist_only',
+ '/utf8',
+ '/skip_empty_dir',
+ '/linkdest',
+ '/no_ui',
+ '/auto_close',
+ '/exclude={}'.format(';'.join(FAST_COPY_EXCLUDES)),
+ ]
+
+# General
+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'],
+ },
+ }
+
+
+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/system_checklist.py b/.bin/Scripts/system_checklist.py
index 7474a07c..1088cf5d 100644
--- a/.bin/Scripts/system_checklist.py
+++ b/.bin/Scripts/system_checklist.py
@@ -49,6 +49,8 @@ if __name__ == '__main__':
function=disable_windows_telemetry, cs='Done')
try_and_print(message='Enabling RegBack...',
function=enable_regback, cs='Done')
+ try_and_print(message='Windows Updates...',
+ function=config_windows_updates, cs='Done')
try_and_print(message='Enabling BSoD mini dumps...',
function=enable_mini_dumps, cs='Done')
try_and_print(message='Enabling System Restore...',
@@ -56,10 +58,14 @@ if __name__ == '__main__':
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')
+ function=enable_windows_updates, cs='Done', silent=True)
try_and_print(message='Updating Clock...',
function=update_clock, cs='Done')
+ # Restart Explorer
+ try_and_print(message='Restarting Explorer...',
+ function=restart_explorer, cs='Done')
+
# Cleanup
print_info('Cleanup')
try_and_print(message='AdwCleaner...',
@@ -133,13 +139,18 @@ if __name__ == '__main__':
function=run_xmplay, cs='Started', other_results=other_results)
try:
check_secure_boot_status(show_alert=True)
- except:
+ except Exception:
# Only trying to open alert message boxes
pass
# Done
print_standard('\nDone.')
pause('Press Enter exit...')
+ if D7_MODE:
+ show_alert_box(
+ message='Please run the Post-d7II script after ending the session.',
+ title='{} Notice'.format(KIT_NAME_FULL),
+ )
exit_script()
except SystemExit:
pass
diff --git a/.bin/Scripts/system_checklist_hw.py b/.bin/Scripts/system_checklist_hw.py
index fd08fe10..1edb0e96 100644
--- a/.bin/Scripts/system_checklist_hw.py
+++ b/.bin/Scripts/system_checklist_hw.py
@@ -4,14 +4,13 @@ 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 *
init_global_vars()
os.system('title {}: System HW Checklist Tool'.format(KIT_NAME_FULL))
set_log_file('System HW Checklist.log')
@@ -107,7 +106,7 @@ if __name__ == '__main__':
function=run_xmplay, cs='Started', other_results=other_results)
try:
check_secure_boot_status(show_alert=True)
- except:
+ except Exception:
# Only trying to open alert message boxes
pass
diff --git a/.bin/Scripts/update_kit.py b/.bin/Scripts/update_kit.py
index 8f21e122..13520d5c 100644
--- a/.bin/Scripts/update_kit.py
+++ b/.bin/Scripts/update_kit.py
@@ -63,6 +63,7 @@ if __name__ == '__main__':
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)
+ try_and_print(message='Windows Updates...', function=download_windows_updates, other_results=other_results, width=40)
update_all_ninite(other_results=other_results, width=40)
# Misc
diff --git a/.bin/Scripts/user_checklist.py b/.bin/Scripts/user_checklist.py
index b292ed83..30a78730 100644
--- a/.bin/Scripts/user_checklist.py
+++ b/.bin/Scripts/user_checklist.py
@@ -43,7 +43,7 @@ if __name__ == '__main__':
# Scan for supported browsers
print_info('Scanning for browsers')
- scan_for_browsers()
+ scan_for_browsers(skip_ie=True)
# Homepages
if not D7_MODE:
@@ -80,8 +80,13 @@ if __name__ == '__main__':
if not answer_config_browsers:
print_warning(' Skipped')
+ # Restart Explorer
+ try_and_print(message='Restarting Explorer...',
+ function=restart_explorer, cs='Done')
+
# Run speedtest
- popen_program(['start', '', 'https://fast.com'], shell=True)
+ if not D7_MODE:
+ popen_program(['start', '', 'https://fast.com'], shell=True)
# Done
if not D7_MODE:
diff --git a/.bin/Scripts/windows_updates.py b/.bin/Scripts/windows_updates.py
index 53ab7172..a9856cd0 100644
--- a/.bin/Scripts/windows_updates.py
+++ b/.bin/Scripts/windows_updates.py
@@ -9,6 +9,7 @@ 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')
+D7_MODE = 'd7mode' in sys.argv
if __name__ == '__main__':
try:
@@ -17,29 +18,31 @@ if __name__ == '__main__':
# Check args
if '--disable' in sys.argv:
- result = try_and_print(
- message='Disabling Windows Updates...',
- function=disable_windows_updates)
+ disable_windows_updates()
elif '--enable' in sys.argv:
- result = try_and_print(
- message='Enabling Windows Updates...',
- function=enable_windows_updates)
+ 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 GenericError as err:
+ # Failed to complete request, show error(s) and prompt tech
+ print_standard(' ')
+ for line in str(err).splitlines():
+ print_warning(line)
+ print_standard(' ')
+ print_error('Error(s) encountered, see above.')
+ print_standard(' ')
+ if '--disable' in sys.argv:
+ if D7_MODE:
+ print_warning('Please disable d7II auto mode!')
+ print_standard('Then reboot, re-enable this step, and try again.')
+ else:
+ print_standard('Please reboot and try again.')
+ pause('Press Enter to exit... ')
+ exit_script(1)
except SystemExit:
pass
except:
diff --git a/.linux_items/include/EFI/boot/refind.conf b/.linux_items/include/EFI/boot/refind.conf
index 86b1a835..e7dcbfb6 100644
--- a/.linux_items/include/EFI/boot/refind.conf
+++ b/.linux_items/include/EFI/boot/refind.conf
@@ -33,11 +33,11 @@ menuentry "Linux" {
add_options "loglevel=4 nomodeset nox"
}
}
-#UFD#menuentry "WindowsPE" {
-#UFD# ostype windows
-#UFD# icon /EFI/boot/icons/wk_win.png
-#UFD# loader /EFI/microsoft/bootx64.efi
-#UFD#}
+#UFD-WINPE#menuentry "WindowsPE" {
+#UFD-WINPE# ostype windows
+#UFD-WINPE# icon /EFI/boot/icons/wk_win.png
+#UFD-WINPE# loader /EFI/microsoft/bootx64.efi
+#UFD-WINPE#}
#UFD#menuentry "ESET SysRescue Live" {
#UFD# icon /EFI/boot/icons/1201_eset.png
#UFD# loader /EFI/ESET/grubx64.efi
diff --git a/.linux_items/include/syslinux/wk_pxe.cfg b/.linux_items/include/syslinux/wk_pxe.cfg
index f8a78220..10112666 100644
--- a/.linux_items/include/syslinux/wk_pxe.cfg
+++ b/.linux_items/include/syslinux/wk_pxe.cfg
@@ -2,7 +2,7 @@ INCLUDE boot/syslinux/wk_head.cfg
MENU BACKGROUND pxelinux.png
INCLUDE boot/syslinux/wk_pxe_linux.cfg
-#UFD#INCLUDE boot/syslinux/wk_pxe_winpe.cfg
+#UFD-WINPE#INCLUDE boot/syslinux/wk_pxe_winpe.cfg
#DISABLED_UPSTREAM_BUG#INCLUDE boot/syslinux/wk_hdt.cfg
INCLUDE boot/syslinux/wk_tail.cfg
diff --git a/.linux_items/include/syslinux/wk_sys.cfg b/.linux_items/include/syslinux/wk_sys.cfg
index af388ed3..442ec2a3 100644
--- a/.linux_items/include/syslinux/wk_sys.cfg
+++ b/.linux_items/include/syslinux/wk_sys.cfg
@@ -1,9 +1,8 @@
INCLUDE boot/syslinux/wk_head.cfg
INCLUDE boot/syslinux/wk_sys_linux.cfg
-#UFD#INCLUDE boot/syslinux/wk_sys_winpe.cfg
+#UFD-WINPE#INCLUDE boot/syslinux/wk_sys_winpe.cfg
#UFD#INCLUDE boot/syslinux/1201_hdclone.cfg
#UFD#INCLUDE boot/syslinux/1201_eset.cfg
-
#DISABLED_UPSTREAM_BUG#INCLUDE boot/syslinux/wk_hdt.cfg
INCLUDE boot/syslinux/wk_tail.cfg
diff --git a/.linux_items/include_x/airootfs/etc/skel/.Xresources b/.linux_items/include_x/airootfs/etc/skel/.Xresources
index d659b735..8e303276 100755
--- a/.linux_items/include_x/airootfs/etc/skel/.Xresources
+++ b/.linux_items/include_x/airootfs/etc/skel/.Xresources
@@ -23,7 +23,7 @@ URxvt*externalBorder: 0
!URxvt.colorUL: #87afd7
URxvt.geometry: 92x16
URxvt.internalBorder: 8
-URxvt.shading: 10
+URxvt.shading: 7
URxvt.transparent: true
! Base16 Isotope
diff --git a/.linux_items/packages/aur b/.linux_items/packages/aur
index 9588b129..60272aea 100644
--- a/.linux_items/packages/aur
+++ b/.linux_items/packages/aur
@@ -2,6 +2,7 @@ aic94xx-firmware
bash-pipes
hfsprogs
i3lock-fancy-git
+ldmtool
macbook12-spi-driver-dkms
mprime
openbox-patched
diff --git a/.linux_items/packages/dependencies b/.linux_items/packages/dependencies
index edf99bb6..4558e9eb 100644
--- a/.linux_items/packages/dependencies
+++ b/.linux_items/packages/dependencies
@@ -4,8 +4,10 @@ base-devel
curl
dos2unix
git
+gtk-doc
hwloc
imlib2
+json-glib
lhasa
libbsd
libewf
diff --git a/.linux_items/packages/live_add b/.linux_items/packages/live_add
index 1c22daa2..5d28f9f9 100644
--- a/.linux_items/packages/live_add
+++ b/.linux_items/packages/live_add
@@ -16,6 +16,7 @@ e2fsprogs
hexedit
hfsprogs
htop
+ldmtool
ldns
lha
libewf
diff --git a/Build Linux b/Build Linux
index 4ccd1729..8ed965b5 100755
--- a/Build Linux
+++ b/Build Linux
@@ -170,6 +170,9 @@ function update_live_env() {
rsync -aI "/usr/share/refind/icons/" "$LIVE_DIR/EFI/boot/icons/" --exclude "/usr/share/refind/icons/svg"
sed -i "s/%ARCHISO_LABEL%/${label}/" "$LIVE_DIR/EFI/boot/refind.conf"
+ # Customize_airootfs.sh
+ sed -i -r 's/set -e -u/set -o errexit\nset -o errtrace\nset -o nounset\nset -o pipefail/' "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
+
# Memtest86
mkdir -p "$LIVE_DIR/EFI/memtest86/Benchmark"
mkdir -p "$TEMP_DIR/memtest86"
diff --git a/Images/ConEmu.png b/Images/ConEmu.png
index ed9d0cab..c5bdb72e 100644
Binary files a/Images/ConEmu.png and b/Images/ConEmu.png differ
diff --git a/Images/Linux.png b/Images/Linux.png
index 5b4d311e..7fcf0056 100644
Binary files a/Images/Linux.png and b/Images/Linux.png differ
diff --git a/Images/Pxelinux.png b/Images/Pxelinux.png
index 3308d059..f3a6a6d9 100644
Binary files a/Images/Pxelinux.png and b/Images/Pxelinux.png differ
diff --git a/Images/Syslinux.png b/Images/Syslinux.png
index dfef6907..de82aa33 100644
Binary files a/Images/Syslinux.png and b/Images/Syslinux.png differ
diff --git a/Images/WinPE.jpg b/Images/WinPE.jpg
index c3c75900..5bc27ca2 100644
Binary files a/Images/WinPE.jpg and b/Images/WinPE.jpg differ
diff --git a/Images/rEFInd.png b/Images/rEFInd.png
index 0e9ef5d1..7e8e2fe5 100644
Binary files a/Images/rEFInd.png and b/Images/rEFInd.png differ