Compare commits
No commits in common. "dev" and "new-build-script" have entirely different histories.
dev
...
new-build-
262
.bin/Scripts/build_pe.ps1
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
# Wizard Kit: Windows PE Build Tool
|
||||
|
||||
## Init ##
|
||||
#Requires -Version 3.0
|
||||
#Requires -RunAsAdministrator
|
||||
if (Test-Path Env:\DEBUG) {
|
||||
Set-PSDebug -Trace 1
|
||||
}
|
||||
$Host.UI.RawUI.WindowTitle = "Wizard Kit: Windows PE Build Tool"
|
||||
$WD = $(Split-Path $MyInvocation.MyCommand.Path)
|
||||
$Bin = (Get-Item $WD -Force).Parent.FullName
|
||||
$Root = (Get-Item $Bin -Force).Parent.FullName
|
||||
$Temp = "{0}\tmp" -f $Bin
|
||||
$Date = Get-Date -UFormat "%Y-%m-%d"
|
||||
$Host.UI.RawUI.BackgroundColor = "Black"
|
||||
$Host.UI.RawUI.ForegroundColor = "White"
|
||||
# $ProgressPreference = "silentlyContinue"
|
||||
$SplitWindow = @()
|
||||
$WinPEPackages = @(
|
||||
"WinPE-EnhancedStorage.cab",
|
||||
"en-us\WinPE-EnhancedStorage_en-us.cab",
|
||||
"WinPE-FMAPI.cab",
|
||||
"WinPE-WMI.cab",
|
||||
"en-us\WinPE-WMI_en-us.cab"
|
||||
)
|
||||
# Install WinPE-WMI before you install WinPE-NetFX.
|
||||
# "WinPE-NetFx.cab",
|
||||
# "en-us\WinPE-NetFx_en-us.cab",
|
||||
|
||||
# Install WinPE-WMI and WinPE-NetFX before you install WinPE-Scripting.
|
||||
# "WinPE-Scripting.cab",
|
||||
# "en-us\WinPE-Scripting_en-us.cab",
|
||||
|
||||
# Install WinPE-WMI, WinPE-NetFX, and WinPE-Scripting before you install WinPE-PowerShell.
|
||||
# "WinPE-PowerShell.cab",
|
||||
# "en-us\WinPE-PowerShell_en-us.cab",
|
||||
|
||||
# Install WinPE-WMI, WinPE-NetFX, WinPE-Scripting, and WinPE-PowerShell before you install WinPE-DismCmdlets.
|
||||
# "WinPE-DismCmdlets.cab",
|
||||
# "en-us\WinPE-DismCmdlets_en-us.cab",
|
||||
|
||||
# Install WinPE-WMI, WinPE-NetFX, WinPE-Scripting, and WinPE-PowerShell before you install WinPE-SecureBootCmdlets.
|
||||
# "WinPE-SecureBootCmdlets.cab",
|
||||
|
||||
# Install WinPE-WMI, WinPE-NetFX, WinPE-Scripting, and WinPE-PowerShell before you install WinPE-StorageWMI.
|
||||
# "WinPE-StorageWMI.cab",
|
||||
# "en-us\WinPE-StorageWMI_en-us.cab",
|
||||
|
||||
## Fake DandISetEnv.bat ##
|
||||
# $DVars = @(
|
||||
# @("DISMRoot", "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\DISM"),
|
||||
# @("BCDBootRoot", "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\BCDBoot"),
|
||||
# @("OSCDImgRoot", "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg"),
|
||||
# @("WdsmcastRoot", "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Wdsmcast"),
|
||||
# @("HelpIndexerRoot", "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\HelpIndexer"),
|
||||
# @("WSIMRoot", "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\WSIM"),
|
||||
# @("WinPERoot", "C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment")
|
||||
# )
|
||||
# foreach ($d in $DVars) {
|
||||
# $varName = $d[0]
|
||||
# $varValue = $d[1]
|
||||
# Set-Item -Path Env:$varName -Value $varValue
|
||||
# Set-Item -Path Env:PATH -Value ($Env:PATH + ";$varValue")
|
||||
# }
|
||||
$DISM = "{0}\DISM.exe" -f $Env:DISMRoot
|
||||
|
||||
## Functions ##
|
||||
function Ask-User ($text = "Kotaero") {
|
||||
$text += " [Y/N]"
|
||||
while ($true) {
|
||||
$answer = read-host $text
|
||||
if ($answer -imatch "^(y|yes)$") {
|
||||
$answer = $true
|
||||
break
|
||||
} elseif ($answer -imatch "^(n|no|nope)$") {
|
||||
$answer = $false
|
||||
break
|
||||
}
|
||||
}
|
||||
$answer
|
||||
}
|
||||
function Abort {
|
||||
Write-Host -ForegroundColor "Red" "`nAborted."
|
||||
WKPause "Press Enter to exit... "
|
||||
exit
|
||||
}
|
||||
function MakeClean {
|
||||
$Folders = @(
|
||||
"$Root\Mount",
|
||||
"$Root\PEFiles")
|
||||
$Clean = $false
|
||||
foreach ($f in $Folders) {
|
||||
if (Test-Path $f) {
|
||||
Write-Host -ForegroundColor "Yellow" ("Found: {0}" -f $f)
|
||||
$Clean = $true
|
||||
}
|
||||
}
|
||||
if (($Clean) -and (Ask-User "Delete the above folder(s)?")) {
|
||||
foreach ($f in $Folders) {
|
||||
if (Test-Path $f) {
|
||||
Remove-Item -Path $f -Recurse -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function DownloadFile ($Path, $Name, $Url) {
|
||||
$OutFile = "{0}\{1}" -f $Path, $Name
|
||||
|
||||
Write-Host ("Downloading: {0}" -f $Name)
|
||||
New-Item -Type Directory $Path 2>&1 | Out-Null
|
||||
try {
|
||||
Invoke-Webrequest -Uri $Url -OutFile $OutFile
|
||||
}
|
||||
catch {
|
||||
Write-Host (" ERROR: Failed to download file." ) -ForegroundColor "Red"
|
||||
}
|
||||
}
|
||||
function FindDynamicUrl ($SourcePage, $RegEx) {
|
||||
$Url = ""
|
||||
|
||||
# Get source page
|
||||
Invoke-Webrequest -Uri $SourcePage -OutFile "tmp_page"
|
||||
|
||||
# Search for real url
|
||||
$Url = Get-Content "tmp_page" | Where-Object {$_ -imatch $RegEx}
|
||||
$Url = $Url -ireplace '.*(a |)href="([^"]+)".*', "$2"
|
||||
$Url = $Url -ireplace ".*(a |)href='([^']+)'.*", "$2"
|
||||
|
||||
# Remove tmp_page
|
||||
Remove-Item "tmp_page"
|
||||
|
||||
return $Url
|
||||
}
|
||||
function WKPause ($Message = "Press Enter to continue... ") {
|
||||
Write-Host $Message -NoNewLine
|
||||
Read-Host
|
||||
}
|
||||
|
||||
## PowerShell equivalent of Python's "if __name__ == '__main__'"
|
||||
# Code based on StackOverflow comments
|
||||
# Question: https://stackoverflow.com/q/4693947
|
||||
# Using answer: https://stackoverflow.com/a/5582692
|
||||
# Asked by: https://stackoverflow.com/users/65164/mark-mascolino
|
||||
# Answer by: https://stackoverflow.com/users/696808/bacon-bits
|
||||
if ($MyInvocation.InvocationName -ne ".") {
|
||||
# Clear-Host
|
||||
Write-Host "Wizard Kit: Windows PE Build Tool`n"
|
||||
|
||||
## Prep ##
|
||||
try {
|
||||
Import-Module -Name $Env:DISMRoot -ErrorAction "stop"
|
||||
}
|
||||
catch {
|
||||
Write-Host -ForegroundColor "Red" "ERROR: Failed to load DISM CmdLet"
|
||||
Abort
|
||||
}
|
||||
Push-Location "$WD"
|
||||
MakeClean
|
||||
|
||||
## Build ##
|
||||
foreach ($Arch in @("amd64", "x86")) {
|
||||
$Drivers = "$Root\Drivers\%arch"
|
||||
$Mount = "$Root\Mount"
|
||||
$PEFiles = "$Root\PEFiles\$arch"
|
||||
|
||||
# Copy WinPE files
|
||||
Write-Host "Copying files..."
|
||||
$Cmd = ("{0}\copype.cmd" -f $Env:WinPERoot)
|
||||
Start-Process $Cmd -ArgumentList @($Arch, $PEFiles) -NoNewWindow -Wait
|
||||
|
||||
# Remove unwanted items
|
||||
foreach ($SubDir in @("media", "media\Boot", "media\EFI\Microsoft\Boot")) {
|
||||
foreach ($Item in Get-ChildItem "$PEFiles\$SubDir") {
|
||||
if ($Item.Name -inotmatch "^(boot|efi|en-us|sources|fonts|resources|bcd|memtest)") {
|
||||
Remove-Item -Path $Item.FullName -Recurse -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Mount image
|
||||
Write-Host "Mounting image..."
|
||||
New-Item -Path $Mount -ItemType "directory" -Force | Out-Null
|
||||
Mount-WindowsImage -Path $Mount -ImagePath "$PEFiles\media\sources\boot.wim" -Index 1 | Out-Null
|
||||
|
||||
# Add packages
|
||||
Write-Host "Adding packages:"
|
||||
foreach ($Package in $WinPEPackages) {
|
||||
$PackagePath = ("{0}\{1}\WinPE_OCs\{2}" -f $Env:WinPERoot, $Arch, $Package)
|
||||
Write-Host " $Package..."
|
||||
Add-WindowsPackage –PackagePath $PackagePath –Path $Mount | Out-Null
|
||||
}
|
||||
|
||||
# Set RamDisk size
|
||||
$ArgumentList = @(
|
||||
('/Image:"{0}"' -f $Mount),
|
||||
"/Set-ScratchSpace:512"
|
||||
)
|
||||
Start-Process $DISM -ArgumentList $ArgumentList -NoNewWindow -Wait
|
||||
|
||||
# Add WK tools
|
||||
Write-Host "Copying tools..."
|
||||
Copy-Item -Path "$Root\WK\$Arch" -Destination "$Mount\WK" -Recurse -Force
|
||||
Copy-Item -Path "$Root\WK\_include\*" -Destination "$Mount\WK" -Recurse -Force
|
||||
if ($Arch -eq "amd64") {
|
||||
$DestIni = "$Mount\WK\HWiNFO\HWiNFO64.INI"
|
||||
} else {
|
||||
$DestIni = "$Mount\WK\HWiNFO\HWiNFO32.INI"
|
||||
}
|
||||
Move-Item -Path "$Mount\WK\HWiNFO\HWiNFO.INI" -Destination $DestIni -Force
|
||||
Copy-Item -Path "$Root\WinPE.jpg" -Destination "$Mount\WK\ConEmu\ConEmu.jpg" -Recurse -Force
|
||||
Copy-Item -Path "$Root\Scripts" -Destination "$Mount\WK\Scripts" -Recurse -Force
|
||||
|
||||
# Add System32 items
|
||||
Copy-Item -Path "$Root\System32\*" -Destination "$Mount\Windows\System32" -Recurse -Force
|
||||
$ArgumentList = @("/f", "$Mount\Windows\System32\winpe.jpg", "/a")
|
||||
Start-Process "C:\Windows\System32\takeown.exe" -ArgumentList $ArgumentList -NoNewWindow -Wait
|
||||
$ArgumentList = @("$Mount\Windows\System32\winpe.jpg", "/grant", "Administrators:F")
|
||||
Start-Process "C:\Windows\System32\icacls.exe" -ArgumentList $ArgumentList -NoNewWindow -Wait
|
||||
Copy-Item -Path "$Root\WinPE.jpg" -Destination "$Mount\Windows\System32\winpe.jpg" -Recurse -Force
|
||||
|
||||
# Update registry
|
||||
Write-Host "Updating Registry..."
|
||||
$Reg = "C:\Windows\System32\reg.exe"
|
||||
Start-Process $Reg -ArgumentList @("load", "HKLM\WinPE-SW", "$Mount\Windows\System32\config\SOFTWARE") -NoNewWindow -Wait
|
||||
Start-Process $Reg -ArgumentList @("load", "HKLM\WinPE-SYS", "$Mount\Windows\System32\config\SYSTEM") -NoNewWindow -Wait
|
||||
|
||||
# Add 7-Zip and Python to path
|
||||
$RegPath = "HKLM:\WinPE-SYS\ControlSet001\Control\Session Manager\Environment"
|
||||
$RegKey = Get-ItemProperty -Path $RegPath
|
||||
$NewValue = "{0};%SystemDrive%\WK\7-Zip;%SystemDrive%\WK\python;%SystemDrive%\WK\wimlib" -f $RegKey.Path
|
||||
Set-ItemProperty -Path $RegPath -Name "Path" -Value $NewValue -Force | Out-Null
|
||||
|
||||
# Replace Notepad
|
||||
$RegPath = "HKLM:\WinPE-SW\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe"
|
||||
$NewValue = 'wscript "X:\WK\NotepadPlusPlus\npp.vbs"'
|
||||
New-Item -Path $RegPath -Force | Out-Null
|
||||
New-ItemProperty -Path $RegPath -Name "Debugger" -Value $NewValue -Force | Out-Null
|
||||
|
||||
# Run garbage collection to release potential stale handles
|
||||
## Credit: https://jrich523.wordpress.com/2012/03/06/powershell-loading-and-unloading-registry-hives/
|
||||
Start-Sleep -Seconds 2
|
||||
[gc]::collect()
|
||||
|
||||
# Unload registry hives
|
||||
Start-Sleep -Seconds 2
|
||||
Start-Process $Reg -ArgumentList @("unload", "HKLM\WinPE-SW") -NoNewWindow -Wait
|
||||
Start-Process $Reg -ArgumentList @("unload", "HKLM\WinPE-SYS") -NoNewWindow -Wait
|
||||
|
||||
# Unmount image
|
||||
Write-Host "Dismounting image..."
|
||||
Dismount-WindowsImage -Path $Mount -Save
|
||||
|
||||
# Create ISO
|
||||
$ArgumentList = @("/iso", $PEFiles, "$Root\wk-winpe-$Date-$Arch.iso")
|
||||
$Cmd = "{0}\MakeWinPEMedia.cmd" -f $Env:WinPERoot
|
||||
Start-Process $Cmd -ArgumentList $ArgumentList -NoNewWindow -Wait
|
||||
}
|
||||
|
||||
## Done ##
|
||||
Pop-Location
|
||||
WKPause "Press Enter to exit... "
|
||||
}
|
||||
17
.gitignore
vendored
|
|
@ -1,8 +1,9 @@
|
|||
**/__pycache__
|
||||
**/*.7z
|
||||
**/*.DS_Store
|
||||
**/*.bak
|
||||
**/*.exe
|
||||
**/*.swp
|
||||
setup/BUILD*
|
||||
setup/OUT*
|
||||
*.bak
|
||||
*.iso
|
||||
.bin/Scripts/__pycache__
|
||||
.bin/tmp
|
||||
Drivers
|
||||
Logs
|
||||
PEFiles/
|
||||
Scripts/__pycache__
|
||||
mount
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.257'
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
|
@ -1,23 +1,24 @@
|
|||
:: WizardKit: Windows PE Build Tool ::
|
||||
:: Wizard Kit: Windows PE Build Tool Launcher ::
|
||||
|
||||
@echo off
|
||||
|
||||
:Init
|
||||
setlocal EnableDelayedExpansion
|
||||
title WizardKit: Build Tool
|
||||
title Wizard Kit: Windows PE Build Tool
|
||||
call :CheckFlags %*
|
||||
call :CheckElevation || goto Exit
|
||||
call :FindKitsRoot || goto ErrorKitNotFound
|
||||
set "dandi_set_env=%adk_root%\Deployment Tools\DandISetEnv.bat"
|
||||
set "ps_script=%~dp0\.bin\Scripts\build_pe.ps1"
|
||||
|
||||
:LaunchPrep
|
||||
rem Update environment using WADK script
|
||||
set "dandi_set_env=%adk_root%\Deployment Tools\DandISetEnv.bat"
|
||||
rem Verify scripts exists
|
||||
if not exist "%dandi_set_env%" (goto ErrorKitNotFound)
|
||||
if not exist "%ps_script%" (goto ErrorPSScriptMissing)
|
||||
call "%dandi_set_env%" || goto ErrorUnknown
|
||||
|
||||
:Launch
|
||||
set "script=%~dp0\pe\build_pe.ps1"
|
||||
powershell -executionpolicy bypass -noprofile -file %script% || goto ErrorUnknown
|
||||
PowerShell -ExecutionPolicy bypass -File %ps_script%"
|
||||
goto Exit
|
||||
|
||||
:: Functions ::
|
||||
|
|
@ -85,6 +86,11 @@ echo.
|
|||
echo ERROR: Windows ADK installation not found.
|
||||
goto Abort
|
||||
|
||||
:ErrorPSScriptMissing
|
||||
echo.
|
||||
echo ERROR: build_pe.ps1 script not found.
|
||||
goto Abort
|
||||
|
||||
:ErrorUnknown
|
||||
echo.
|
||||
echo ERROR: Encountered an unknown error.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2023 Alan Mason
|
||||
Copyright (c) 2017 Alan Mason
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
|
|
|||
43
README.md
|
|
@ -1,39 +1,18 @@
|
|||
# WizardKit #
|
||||
# Wizard Kit PE #
|
||||
|
||||
A collection of tools to help technicians service computers.
|
||||
A collection of scripts to help technicians service Windows systems.
|
||||
|
||||
## Overview ##
|
||||
# NOTICE: Currently under maintenance #
|
||||
|
||||
There are a few main parts to this project and their uses:
|
||||
*These scripts are being reviewed and updated at the moment.*
|
||||
Things may or may not work until this is completed. This warning will be removed once the maintenance is finished.
|
||||
|
||||
* Live Linux image
|
||||
* Hardware diagnostics
|
||||
* CPU stress tests with temperature monitoring
|
||||
* Health checks/tests for storage drives
|
||||
* Misc other diagnostics
|
||||
* Data recovery
|
||||
* General data transfers from many possible filesystems
|
||||
* Bit-level drive duplication based on ddrescue
|
||||
* Live macOS image
|
||||
* Hardware diagnostics
|
||||
* CPU stress tests with temperature monitoring
|
||||
* Health checks/tests for storage drives
|
||||
* Data recovery
|
||||
* _(Currently under development)_
|
||||
* Live WinPE image
|
||||
* _(Currently under development)_
|
||||
* Windows Kit _(intended for UFDs)_
|
||||
* Automated repairs
|
||||
* AV scans
|
||||
* Windows health checks
|
||||
* Automated setup
|
||||
* Install software
|
||||
* System configuration
|
||||
## Requirements ##
|
||||
|
||||
## Combined UFD ##
|
||||
* Windows Assessment and Deployment Kit for Windows 10
|
||||
|
||||
All parts can be combined onto a single UFD!
|
||||
## Initial Setup ##
|
||||
|
||||
* Compatible with most legacy and UEFI bootloaders
|
||||
* Custom boot menus
|
||||
* To get started run `build-ufd` under the live Linux image
|
||||
* Install Windows ADK for Windows 10
|
||||
* Run "Deployment and Imaging Tools Environment" as admin
|
||||
* Run `make.cmd` to build a new Windows 10 PE image
|
||||
1006
Scripts/functions.py
Normal file
277
Scripts/menu.py
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
# WK WinPE Menu
|
||||
|
||||
# Init
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
bin = os.path.abspath('..\\')
|
||||
sys.path.append(os.getcwd())
|
||||
from functions import *
|
||||
|
||||
## Colors
|
||||
COLORS = {
|
||||
'CLEAR': '\033[0m',
|
||||
'RED': '\033[31m',
|
||||
'GREEN': '\033[32m',
|
||||
'YELLOW': '\033[33m',
|
||||
'BLUE': '\033[34m'}
|
||||
|
||||
def menu_backup_imaging():
|
||||
"""Take backup images of partition(s) in the WIM format and save them to a backup share"""
|
||||
errors = False
|
||||
|
||||
# Set ticket ID
|
||||
os.system('cls')
|
||||
ticket_id = get_ticket_id()
|
||||
|
||||
# Mount backup shares
|
||||
mount_backup_shares()
|
||||
|
||||
# Select destination
|
||||
dest = select_destination()
|
||||
if dest is None:
|
||||
abort_to_main_menu('Aborting Backup Creation')
|
||||
|
||||
# Select disk to backup
|
||||
disk = select_disk('For which drive are we creating backups?')
|
||||
if disk is None:
|
||||
abort_to_main_menu()
|
||||
prep_disk_for_backup(dest, disk, ticket_id)
|
||||
|
||||
# Display details for backup task
|
||||
os.system('cls')
|
||||
print('Create Backup - Details:\n')
|
||||
print(' Ticket: \t{ticket_id}'.format(ticket_id=ticket_id))
|
||||
print(' Source: \t[{Table}] ({Type}) {Name} {Size}\n'.format(**disk))
|
||||
print(' Destination:\t{name}'.format(name=dest.get('Display Name', dest['Name'])))
|
||||
for par in disk['Partitions']:
|
||||
print(par['Display String'])
|
||||
print(disk['Backup Warnings'])
|
||||
|
||||
# Ask to proceed
|
||||
if (not ask('Proceed with backup?')):
|
||||
abort_to_main_menu('Aborting Backup Creation')
|
||||
|
||||
# Backup partition(s)
|
||||
print('\n\nStarting task.\n')
|
||||
for par in disk['Partitions']:
|
||||
try:
|
||||
backup_partition(bin, disk, par)
|
||||
except BackupError:
|
||||
errors = True
|
||||
|
||||
# Verify backup(s)
|
||||
if disk['Valid Partitions'] > 1:
|
||||
print('\n\n Verifying backups\n')
|
||||
else:
|
||||
print('\n\n Verifying backup\n')
|
||||
for par in disk['Partitions']:
|
||||
if par['Number'] in disk['Bad Partitions']:
|
||||
continue # Skip verification
|
||||
try:
|
||||
verify_wim_backup(bin, par)
|
||||
except BackupError:
|
||||
errors = True
|
||||
|
||||
# Print summary
|
||||
if errors:
|
||||
print_warning('\nErrors were encountered and are detailed below.')
|
||||
for par in [p for p in disk['Partitions'] if 'Error' in p]:
|
||||
print(' Partition {Number} Error:'.format(**par))
|
||||
for line in [line.strip() for line in par['Error'] if line.strip() != '']:
|
||||
print_error('\t{line}'.format(line=line))
|
||||
time.sleep(30)
|
||||
else:
|
||||
print_success('\nNo errors were encountered during imaging.')
|
||||
time.sleep(5)
|
||||
pause('\nPress Enter to return to main menu... ')
|
||||
|
||||
def menu_windows_setup():
|
||||
"""Format a drive, partition for MBR or GPT, apply a Windows image, and rebuild the boot files"""
|
||||
errors = False
|
||||
|
||||
# Set ticket ID
|
||||
os.system('cls')
|
||||
ticket_id = get_ticket_id()
|
||||
|
||||
# Select the version of Windows to apply
|
||||
windows_version = select_windows_version()
|
||||
|
||||
# Select drive to use as the OS drive
|
||||
dest_disk = select_disk('To which drive are we installing Windows?')
|
||||
prep_disk_for_formatting(dest_disk)
|
||||
|
||||
# Find Windows image
|
||||
## NOTE: Needs to happen AFTER select_disk() is called as there's a hidden assign_volume_letters().
|
||||
## This changes the current letters thus preventing installing from a local source.
|
||||
windows_image = find_windows_image(bin, windows_version)
|
||||
|
||||
# Display details for setup task
|
||||
os.system('cls')
|
||||
print('Setup Windows - Details:\n')
|
||||
print(' Ticket: \t{ticket_id}'.format(ticket_id=ticket_id))
|
||||
print(' Installing: \t{winver}'.format(winver=windows_version['Name']))
|
||||
print(' Boot Method:\t{_type}'.format(
|
||||
_type='UEFI (GPT)' if dest_disk['Use GPT'] else 'Legacy (MBR)'))
|
||||
print(' Using Image:\t{File}.{Ext}'.format(**windows_image))
|
||||
print_warning(' ERASING: \t[{Table}] ({Type}) {Name} {Size}\n'.format(**dest_disk))
|
||||
for par in dest_disk['Partitions']:
|
||||
print_warning(par['Display String'])
|
||||
print_warning(dest_disk['Format Warnings'])
|
||||
|
||||
if (not ask('Is this correct?')):
|
||||
abort_to_main_menu('Aborting Windows setup')
|
||||
|
||||
# Safety check
|
||||
print('\nSAFETY CHECK')
|
||||
print_warning('All data will be DELETED from the drive and partition(s) listed above.')
|
||||
print_warning('This is irreversible and will lead to {CLEAR}{RED}DATA LOSS.'.format(**COLORS))
|
||||
if (not ask('Asking again to confirm, is this correct?')):
|
||||
abort_to_main_menu('Aborting Windows setup')
|
||||
|
||||
# Release currently used volume letters (ensures that the drives will get S, T, & W as needed below)
|
||||
remove_volume_letters(keep=windows_image['Source'])
|
||||
|
||||
# Format and partition drive
|
||||
print('\n Formatting Drive... \t\t', end='', flush=True)
|
||||
try:
|
||||
if (dest_disk['Use GPT']):
|
||||
format_gpt(dest_disk, windows_version['Family'])
|
||||
else:
|
||||
format_mbr(dest_disk, windows_version['Family'])
|
||||
print_success('Complete.')
|
||||
except:
|
||||
# We need to crash as the drive is in an unknown state
|
||||
print_error('Failed.')
|
||||
raise
|
||||
|
||||
# Apply Image
|
||||
print(' Applying Image... \t\t', end='', flush=True)
|
||||
try:
|
||||
setup_windows(bin, windows_image, windows_version)
|
||||
print_success('Complete.')
|
||||
except subprocess.CalledProcessError:
|
||||
print_error('Failed.')
|
||||
errors = True
|
||||
except:
|
||||
# We need to crash as the OS is in an unknown state
|
||||
print_error('Failed.')
|
||||
raise
|
||||
|
||||
# Create Boot files
|
||||
print(' Update Boot Partition...\t\t', end='', flush=True)
|
||||
try:
|
||||
update_boot_partition()
|
||||
print_success('Complete.')
|
||||
except subprocess.CalledProcessError:
|
||||
# Don't need to crash as this is (potentially) recoverable
|
||||
print_error('Failed.')
|
||||
errors = True
|
||||
except:
|
||||
print_error('Failed.')
|
||||
raise
|
||||
|
||||
# Setup WinRE
|
||||
print(' Update Recovery Tools...\t\t', end='', flush=True)
|
||||
try:
|
||||
setup_windows_re(windows_version)
|
||||
print_success('Complete.')
|
||||
except SetupError:
|
||||
print('Skipped.')
|
||||
except:
|
||||
# Don't need to crash as this is (potentially) recoverable
|
||||
print_error('Failed.')
|
||||
errors = True
|
||||
|
||||
# Print summary
|
||||
if errors:
|
||||
print_warning('\nErrors were encountered during setup.')
|
||||
time.sleep(30)
|
||||
else:
|
||||
print_success('\nNo errors were encountered during setup.')
|
||||
time.sleep(5)
|
||||
pause('\nPress Enter to return to main menu... ')
|
||||
|
||||
def menu_tools():
|
||||
tools = [
|
||||
{'Name': 'Blue Screen View', 'Folder': 'BlueScreenView', 'File': 'BlueScreenView.exe'},
|
||||
{'Name': 'CPU-Z', 'Folder': 'CPU-Z', 'File': 'cpuz.exe'},
|
||||
{'Name': 'Fast Copy', 'Folder': 'FastCopy', 'File': 'FastCopy.exe', 'Args': ['/log', '/logfile=X:\WK\Info\FastCopy.log', '/cmd=noexist_only', '/utf8', '/skip_empty_dir', '/linkdest', '/open_window', '/balloon=FALSE', r'/exclude=$RECYCLE.BIN;$Recycle.Bin;.AppleDB;.AppleDesktop;.AppleDouble;.com.apple.timemachine.supported;.dbfseventsd;.DocumentRevisions-V100*;.DS_Store;.fseventsd;.PKInstallSandboxManager;.Spotlight*;.SymAV*;.symSchedScanLockxz;.TemporaryItems;.Trash*;.vol;.VolumeIcon.icns;desktop.ini;Desktop?DB;Desktop?DF;hiberfil.sys;lost+found;Network?Trash?Folder;pagefile.sys;Recycled;RECYCLER;System?Volume?Information;Temporary?Items;Thumbs.db']},
|
||||
{'Name': 'HWiNFO', 'Folder': 'HWiNFO', 'File': 'HWiNFO.exe'},
|
||||
{'Name': 'NT Password Editor', 'Folder': 'NT Password Editor', 'File': 'ntpwedit.exe'},
|
||||
{'Name': 'Notepad++', 'Folder': 'NotepadPlusPlus', 'File': 'notepadplusplus.exe'},
|
||||
{'Name': 'PhotoRec', 'Folder': 'TestDisk', 'File': 'photorec_win.exe', 'Args': ['-new_console:n']},
|
||||
{'Name': 'Prime95', 'Folder': 'Prime95', 'File': 'prime95.exe'},
|
||||
{'Name': 'ProduKey', 'Folder': 'ProduKey', 'File': 'ProduKey.exe', 'Args': ['/external', '/ExtractEdition:1']},
|
||||
{'Name': 'Q-Dir', 'Folder': 'Q-Dir', 'File': 'Q-Dir.exe'},
|
||||
{'Name': 'TestDisk', 'Folder': 'TestDisk', 'File': 'testdisk_win.exe', 'Args': ['-new_console:n']},
|
||||
]
|
||||
actions = [
|
||||
{'Name': 'Main Menu', 'Letter': 'M'},
|
||||
]
|
||||
|
||||
# Menu loop
|
||||
while True:
|
||||
selection = menu_select('Tools Menu', tools, actions)
|
||||
|
||||
if (selection.isnumeric()):
|
||||
tool = tools[int(selection)-1]
|
||||
cmd = ['{bin}\\{folder}\\{file}'.format(bin=bin, folder=tool['Folder'], file=tool['File'])]
|
||||
if tool['Name'] == 'Blue Screen View':
|
||||
# Select path to scan
|
||||
minidump_path = select_minidump_path()
|
||||
if minidump_path is not None:
|
||||
tool['Args'] = ['/MiniDumpFolder', minidump_path]
|
||||
if 'Args' in tool:
|
||||
cmd += tool['Args']
|
||||
try:
|
||||
subprocess.Popen(cmd)
|
||||
except:
|
||||
print_error('Failed to run {prog}'.format(prog=tool['Name']))
|
||||
time.sleep(2)
|
||||
elif (selection == 'M'):
|
||||
break
|
||||
|
||||
def menu_main():
|
||||
menus = [
|
||||
{'Name': 'Create Backups', 'Menu': menu_backup_imaging},
|
||||
{'Name': 'Setup Windows', 'Menu': menu_windows_setup},
|
||||
{'Name': 'Misc Tools', 'Menu': menu_tools},
|
||||
]
|
||||
actions = [
|
||||
{'Name': 'Command Prompt', 'Letter': 'C'},
|
||||
{'Name': 'Reboot', 'Letter': 'R'},
|
||||
{'Name': 'Shutdown', 'Letter': 'S'},
|
||||
]
|
||||
|
||||
# Main loop
|
||||
while True:
|
||||
selection = menu_select('Main Menu', menus, actions, secret_exit=True)
|
||||
|
||||
if (selection.isnumeric()):
|
||||
try:
|
||||
menus[int(selection)-1]['Menu']()
|
||||
except AbortError:
|
||||
pass
|
||||
except:
|
||||
print_error('Major exception in: {menu}'.format(menu=menus[int(selection)-1]['Name']))
|
||||
print_warning(' Please let The Wizard know and he\'ll look into it (Please include the details below).')
|
||||
print(traceback.print_exc())
|
||||
time.sleep(5)
|
||||
print_info(' You can retry but if this crashes again an alternative approach may be required.')
|
||||
pause('\nPress enter to return to the main menu')
|
||||
elif (selection == 'C'):
|
||||
run_program(['cmd', '-new_console:n'], check=False)
|
||||
elif (selection == 'R'):
|
||||
run_program(['wpeutil', 'reboot'])
|
||||
elif (selection == 'S'):
|
||||
run_program(['wpeutil', 'shutdown'])
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
menu_main()
|
||||
325
Scripts/partition_uids.py
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
# WK WinPE - PARTITION UIDs
|
||||
# sources: https://en.wikipedia.org/wiki/GUID_Partition_Table
|
||||
# https://en.wikipedia.org/wiki/Partition_type
|
||||
|
||||
PARTITION_UIDS = {
|
||||
'00': {'OS': 'All', 'Description': 'Empty partition entry'},
|
||||
'01': {'OS': 'DOS 2.0+', 'Description': 'FAT12 as primary partition in first physical 32 MB of disk or as logical drive anywhere on disk (else use 06hinstead)'},
|
||||
'02': {'OS': 'XENIX', 'Description': 'XENIX root'},
|
||||
'03': {'OS': 'XENIX', 'Description': 'XENIX usr'},
|
||||
'04': {'OS': 'DOS 3.0+', 'Description': 'FAT16 with less than 65536 sectors (32 MB). As primary partition it must reside in first physical 32 MB of disk, or as logical drive anywhere on disk (else use 06h instead).'},
|
||||
'05': {'OS': 'DOS (3.2) 3.3+ / SpeedStor', 'Description': 'Extended partition with CHS addressing. It must reside in first physical 8 GB of disk, else use 0Fh instead / can occur in SpeedStor MBRs'},
|
||||
'06': {'OS': 'DOS 3.31+', 'Description': 'FAT16B with 65536 or more sectors. It must reside in first physical 8 GB of disk, unless used for logical drives in an 0Fh extended partition (else use 0Eh instead). Also used for FAT12 and FAT16 volumes in primary partitions if they are not residing in first physical 32 MB of disk.'},
|
||||
'07': {'OS': 'OS/2 1.2+ / OS/2 1.2+, Windows NT / Windows NT / Windows Embedded CE / QNX 2', 'Description': 'IFS / HPFS / NTFS / exFAT / QNX "qnx" (7) (pre-1988 only)'},
|
||||
'08': {'OS': 'Commodore MS-DOS 3.x / OS/2 1.0-1.3 / AIX / QNX 1.x/2.x', 'Description': 'Logical sectored FAT12 or FAT16 / OS/2 (FAT?) / AIX boot/split / SplitDrive / QNX "qny" (8) / partition spanning multiple drives'},
|
||||
'09': {'OS': 'AIX / QNX 1.x/2.x / Coherent / OS-9', 'Description': 'AIX data/boot / QNX "qnz" (9) / Coherent file system / OS-9 RBF'},
|
||||
'0A': {'OS': 'OS/2 / Coherent', 'Description': 'OS/2 Boot Manager / Coherent swap partition'},
|
||||
'0B': {'OS': 'DOS 7.1+', 'Description': 'FAT32 with CHS addressing'},
|
||||
'0C': {'OS': 'DOS 7.1+', 'Description': 'FAT32 with LBA'},
|
||||
'0D': {'OS': 'Silicon Safe', 'Description': 'Reserved'},
|
||||
'0E': {'OS': 'DOS 7.0+', 'Description': 'FAT16B with LBA'},
|
||||
'0F': {'OS': 'DOS 7.0+', 'Description': 'Extended partition with LBA'},
|
||||
'10': {'OS': 'OPUS', 'Description': 'Unknown'},
|
||||
'11': {'OS': 'Leading Edge MS-DOS 3.x / OS/2 Boot Manager', 'Description': 'Logical sectored FAT12 or FAT16 / Hidden FAT12 '},
|
||||
'12': {'OS': 'Compaq Contura', 'Description': 'configuration partition (bootable FAT) / configuration partition / hibernation partition / diagnostics and firmware partition (bootable FAT) / service partition (bootable FAT) / Rescue and Recovery partition'},
|
||||
'14': {'OS': 'AST MS-DOS 3.x / OS/2 Boot Manager / Maverick OS', 'Description': 'Logical sectored FAT12 or FAT16 / Hidden FAT16 / Omega file system'},
|
||||
'15': {'OS': 'OS/2 Boot Manager / Maverick OS', 'Description': 'Hidden extended partition with CHS addressing / swap'},
|
||||
'16': {'OS': 'OS/2 Boot Manager', 'Description': 'Hidden FAT16B '},
|
||||
'17': {'OS': 'OS/2 Boot Manager', 'Description': 'Hidden IFS / Hidden HPFS / Hidden NTFS / Hidden exFAT '},
|
||||
'18': {'OS': 'AST Windows', 'Description': 'AST Zero Volt Suspend or SmartSleep partition'},
|
||||
'19': {'OS': 'Willowtech Photon coS', 'Description': 'Willowtech Photon coS'},
|
||||
'1B': {'OS': 'OS/2 Boot Manager', 'Description': 'Hidden FAT32 '},
|
||||
'1C': {'OS': 'OS/2 Boot Manager', 'Description': 'Hidden FAT32 with LBA '},
|
||||
'1E': {'OS': 'OS/2 Boot Manager', 'Description': 'Hidden FAT16 with LBA '},
|
||||
'1F': {'OS': 'OS/2 Boot Manager', 'Description': 'Hidden extended partition with LBA addressing '},
|
||||
'20': {'OS': 'Windows Mobile', 'Description': 'Windows Mobile update XIP / Willowsoft Overture File System (OFS1)'},
|
||||
'21': {'OS': 'Oxygen', 'Description': 'HP Volume Expansion (SpeedStor) / FSo2 (Oxygen File System)'},
|
||||
'22': {'OS': 'Oxygen', 'Description': 'Oxygen Extended Partition Table'},
|
||||
'23': {'OS': 'Windows Mobile', 'Description': 'Reserved / Windows Mobile boot XIP'},
|
||||
'24': {'OS': 'NEC MS-DOS 3.30', 'Description': 'Logical sectored FAT12 or FAT16 '},
|
||||
'25': {'OS': 'Windows Mobile', 'Description': 'Windows Mobile IMGFS[citation needed]'},
|
||||
'26': {'OS': 'Microsoft, IBM', 'Description': 'Reserved'},
|
||||
'27': {'OS': 'Windows / PQservice / MirOS BSD / RooterBOOT', 'Description': 'Windows Recovery Environment (RE) partition (hidden NTFS partition type 07h) / FAT32 or NTFS rescue partition / MirOS partition / RooterBOOT kernel partition (contains a raw ELF Linux kernel, no file system)'},
|
||||
'2A': {'OS': 'AtheOS', 'Description': 'AtheOS file system (AthFS, AFS) (an extension of BFS, see 2Bh and EBh) / Reserved'},
|
||||
'2B': {'OS': 'SyllableOS', 'Description': 'SyllableSecure (SylStor), a variant of AthFS (an extension of BFS, see 2Ah and EBh)'},
|
||||
'31': {'OS': 'Microsoft, IBM', 'Description': 'Reserved'},
|
||||
'32': {'OS': 'NOS', 'Description': 'Unknown'},
|
||||
'33': {'OS': 'Microsoft, IBM', 'Description': 'Reserved'},
|
||||
'34': {'OS': 'Microsoft, IBM', 'Description': 'Reserved'},
|
||||
'35': {'OS': 'OS/2 Warp Server /eComStation', 'Description': 'JFS (OS/2 implementation of AIX Journaling File system)'},
|
||||
'36': {'OS': 'Microsoft, IBM', 'Description': 'Reserved'},
|
||||
'38': {'OS': 'THEOS', 'Description': 'THEOS version 3.2, 2 GB partition'},
|
||||
'39': {'OS': 'Plan 9 / THEOS', 'Description': 'Plan 9 edition 3 partition (sub-partitions described in second sector of partition) / THEOS version 4 spanned partition'},
|
||||
'3A': {'OS': 'THEOS', 'Description': 'THEOS version 4, 4 GB partition'},
|
||||
'3B': {'OS': 'THEOS', 'Description': 'THEOS version 4 extended partition'},
|
||||
'3C': {'OS': 'PartitionMagic', 'Description': 'PqRP (PartitionMagic or DriveImage in progress)'},
|
||||
'3D': {'OS': 'PartitionMagic', 'Description': 'Hidden NetWare'},
|
||||
'3F': {'OS': 'OS/32', 'Description': 'Unknown'},
|
||||
'40': {'OS': 'PICK / Venix', 'Description': 'PICK R83 / Venix 80286'},
|
||||
'41': {'OS': 'Personal RISC / Linux / PowerPC', 'Description': 'Personal RISC Boot / Old Linux/Minix (disk shared with DR DOS 6.0) / PPC PReP (Power PC Reference Platform) Boot'},
|
||||
'42': {'OS': 'SFS / Linux / Windows 2000, XP, etc.', 'Description': 'Secure File system (SFS) / Old Linux swap (disk shared with DR DOS 6.0) / Dynamic extended partition marker'},
|
||||
'43': {'OS': 'Linux', 'Description': 'Old Linux native (disk shared with DR DOS 6.0) '},
|
||||
'44': {'OS': 'GoBack', 'Description': 'Norton GoBack, WildFile GoBack, Adaptec GoBack, Roxio GoBack'},
|
||||
'45': {'OS': 'Boot-US / EUMEL/ELAN', 'Description': 'Priam / Boot-US boot manager (1 cylinder) / EUMEL/ELAN (L2)'},
|
||||
'46': {'OS': 'EUMEL/ELAN', 'Description': 'EUMEL/ELAN (L2)'},
|
||||
'47': {'OS': 'EUMEL/ELAN', 'Description': 'EUMEL/ELAN (L2)'},
|
||||
'48': {'OS': 'EUMEL/ELAN', 'Description': 'EUMEL/ELAN (L2), ERGOS L3'},
|
||||
'4A': {'OS': 'AdaOS / ALFS/THIN', 'Description': 'Aquila / ALFS/THIN advanced lightweight file system for DOS'},
|
||||
'4C': {'OS': 'ETH Oberon', 'Description': 'Aos (A2) file system (76)'},
|
||||
'4D': {'OS': 'QNX 4.x, Neutrino', 'Description': 'Primary QNX POSIX volume on disk (77)'},
|
||||
'4E': {'OS': 'QNX 4.x, Neutrino', 'Description': 'Secondary QNX POSIX volume on disk (78)'},
|
||||
'4F': {'OS': 'QNX 4.x, Neutrino / ETH Oberon', 'Description': 'Tertiary QNX POSIX volume on disk (79) / boot / native file system (79)'},
|
||||
'50': {'OS': 'ETH Oberon / Disk Manager 4 / LynxOS / Novell', 'Description': 'Alternative native file system (80) / Read-only partition (old) / Lynx RTOS'},
|
||||
'51': {'OS': 'Disk Manager 4-6', 'Description': 'Read-write partition (Aux 1)'},
|
||||
'52': {'OS': 'CP/M-80 / System V/AT, V/386', 'Description': 'CP/M-80'},
|
||||
'53': {'OS': 'Disk Manager 6', 'Description': 'Auxiliary 3 (WO)'},
|
||||
'54': {'OS': 'Disk Manager 6', 'Description': 'Dynamic Drive Overlay (DDO)'},
|
||||
'55': {'OS': 'EZ-Drive', 'Description': 'EZ-Drive, Maxtor, MaxBlast, or DriveGuide INT 13h redirector volume'},
|
||||
'56': {'OS': 'AT&T MS-DOS 3.x / EZ-Drive / VFeature', 'Description': 'Logical sectored FAT12 or FAT16 / Disk Manager partition converted to EZ-BIOS / VFeature partitionned volume'},
|
||||
'57': {'OS': 'DrivePro', 'Description': 'VNDI partition'},
|
||||
'5C': {'OS': 'EDISK', 'Description': 'Priam EDisk Partitioned Volume '},
|
||||
'61': {'OS': 'SpeedStor', 'Description': 'Unknown'},
|
||||
'63': {'OS': 'Unix', 'Description': 'SCO Unix, ISC, UnixWare, AT&T System V/386, ix, MtXinu BSD 4.3 on Mach, GNU HURD'},
|
||||
'64': {'OS': 'SpeedStor / NetWare', 'Description': 'NetWare File System 286/2 / PC-ARMOUR'},
|
||||
'65': {'OS': 'NetWare', 'Description': 'NetWare File System 386'},
|
||||
'66': {'OS': 'NetWare / NetWare', 'Description': 'NetWare File System 386 / Storage Management Services (SMS)'},
|
||||
'67': {'OS': 'NetWare', 'Description': 'Wolf Mountain'},
|
||||
'68': {'OS': 'NetWare', 'Description': 'Unknown'},
|
||||
'69': {'OS': 'NetWare 5 / NetWare', 'Description': 'Novell Storage Services (NSS)'},
|
||||
'6E': {'Description': 'Unknown'},
|
||||
'70': {'OS': 'DiskSecure', 'Description': 'DiskSecure multiboot'},
|
||||
'71': {'OS': 'Microsoft, IBM', 'Description': 'Reserved'},
|
||||
'72': {'OS': 'APTI conformant systems / Unix V7/x86', 'Description': 'APTI alternative FAT12 (CHS, SFN) / V7/x86'},
|
||||
'73': {'OS': 'Microsoft, IBM', 'Description': 'Reserved'},
|
||||
'74': {'OS': 'Microsoft, IBM', 'Description': 'Reserved / Scramdisk'},
|
||||
'75': {'OS': 'PC/IX', 'Description': 'Unknown'},
|
||||
'76': {'OS': 'Microsoft, IBM', 'Description': 'Reserved'},
|
||||
'77': {'OS': 'Novell', 'Description': 'VNDI, M2FS, M2CS'},
|
||||
'78': {'OS': 'Geurt Vos', 'Description': 'XOSL bootloader file system'},
|
||||
'79': {'OS': 'APTI conformant systems', 'Description': 'APTI alternative FAT16 (CHS, SFN) '},
|
||||
'7A': {'OS': 'APTI conformant systems', 'Description': 'APTI alternative FAT16 (LBA, SFN) '},
|
||||
'7B': {'OS': 'APTI conformant systems', 'Description': 'APTI alternative FAT16B (CHS, SFN) '},
|
||||
'7C': {'OS': 'APTI conformant systems', 'Description': 'APTI alternative FAT32 (LBA, SFN) '},
|
||||
'7D': {'OS': 'APTI conformant systems', 'Description': 'APTI alternative FAT32 (CHS, SFN) '},
|
||||
'7E': {'OS': 'F.I.X. (claim) / PrimoCache', 'Description': 'Level 2 cache'},
|
||||
'7F': {'OS': 'Varies', 'Description': 'Alternative OS Development Partition Standard - reserved for individual or local use and temporary or experimental projects'},
|
||||
'80': {'OS': 'Minix 1.1-1.4a', 'Description': 'Minix file system (old)'},
|
||||
'81': {'OS': 'Minix 1.4b+ / Linux', 'Description': 'MINIX file system / Mitac Advanced Disk Manager'},
|
||||
'82': {'OS': 'Linux / Sun Microsystems', 'Description': 'Linux swap space / Solaris x86 (for Sun disklabels up to 2005) / Prime'},
|
||||
'83': {'OS': 'GNU/Linux', 'Description': 'Any native Linux file system '},
|
||||
'84': {'OS': 'OS/2 / Windows 7', 'Description': 'APM hibernation (suspend to disk, S2D) / Hidden C: (FAT16) / Rapid Start technology'},
|
||||
'85': {'OS': 'GNU/Linux', 'Description': 'Linux extended '},
|
||||
'86': {'OS': 'Windows NT 4 Server / Linux', 'Description': 'Fault-tolerant FAT16B mirrored volume set / Linux RAID superblock with auto-detect (old)'},
|
||||
'87': {'OS': 'Windows NT 4 Server', 'Description': 'Fault-tolerant HPFS/NTFS mirrored volume set '},
|
||||
'88': {'OS': 'GNU/Linux', 'Description': 'Linux plaintext partition table'},
|
||||
'8A': {'OS': 'AiR-BOOT', 'Description': 'Linux kernel image'},
|
||||
'8B': {'OS': 'Windows NT 4 Server', 'Description': 'Legacy fault-tolerant FAT32 mirrored volume set '},
|
||||
'8C': {'OS': 'Windows NT 4 Server', 'Description': 'Legacy fault-tolerant FAT32 mirrored volume set '},
|
||||
'8D': {'OS': 'Free FDISK', 'Description': 'Hidden FAT12 '},
|
||||
'8E': {'OS': 'Linux', 'Description': 'Linux LVM'},
|
||||
'90': {'OS': 'Free FDISK', 'Description': 'Hidden FAT16 '},
|
||||
'91': {'OS': 'Free FDISK', 'Description': 'Hidden extended partition with CHS addressing '},
|
||||
'92': {'OS': 'Free FDISK', 'Description': 'Hidden FAT16B '},
|
||||
'93': {'OS': 'Amoeba / Linux', 'Description': 'Amoeba native file system / Hidden Linux file system'},
|
||||
'94': {'OS': 'Amoeba', 'Description': 'Amoeba bad block table'},
|
||||
'95': {'OS': 'EXOPC', 'Description': 'EXOPC native'},
|
||||
'96': {'OS': 'CHRP', 'Description': 'ISO-9660 file system'},
|
||||
'97': {'OS': 'Free FDISK', 'Description': 'Hidden FAT32 '},
|
||||
'98': {'OS': 'Free FDISK / ROM-DOS', 'Description': 'Hidden FAT32 / service partition (bootable FAT) ROM-DOS SuperBoot / service partition (bootable FAT)'},
|
||||
'99': {'OS': 'early Unix', 'Description': 'Unknown'},
|
||||
'9A': {'OS': 'Free FDISK', 'Description': 'Hidden FAT16 '},
|
||||
'9B': {'OS': 'Free FDISK', 'Description': 'Hidden extended partition with LBA '},
|
||||
'9E': {'OS': 'VSTA / ForthOS', 'Description': 'ForthOS (eForth port)'},
|
||||
'9F': {'OS': 'BSD/OS 3.0+, BSDI', 'Description': 'Unknown'},
|
||||
'A0': {'OS': 'Hewlett Packard / Phoenix, IBM, Toshiba, Sony', 'Description': 'Diagnostic partition for HP laptops / Hibernate partition'},
|
||||
'A1': {'OS': 'Hewlett Packard / Phoenix, NEC', 'Description': 'HP Volume Expansion (SpeedStor) / Hibernate partition'},
|
||||
'A2': {'OS': 'Cyclone V', 'Description': 'Hard Processor System (HPS) ARM preloader'},
|
||||
'A3': {'OS': 'Hewlett Packard', 'Description': 'HP Volume Expansion (SpeedStor)'},
|
||||
'A4': {'OS': 'Hewlett Packard', 'Description': 'HP Volume Expansion (SpeedStor)'},
|
||||
'A5': {'OS': 'BSD', 'Description': 'BSD slice (BSD/386, 386BSD, NetBSD (old), FreeBSD)'},
|
||||
'A6': {'OS': 'OpenBSD', 'Description': 'HP Volume Expansion (SpeedStor) / OpenBSD slice'},
|
||||
'A7': {'OS': 'NeXT', 'Description': 'NeXTSTEP'},
|
||||
'A8': {'OS': 'Darwin, Mac OS X', 'Description': 'Apple Darwin, Mac OS X UFS'},
|
||||
'A9': {'OS': 'NetBSD', 'Description': 'NetBSD slice'},
|
||||
'AA': {'OS': 'MS-DOS', 'Description': 'Olivetti MS-DOS FAT12 (1.44 MB) '},
|
||||
'AB': {'OS': 'Darwin, Mac OS X / GO! OS', 'Description': 'Apple Darwin, Mac OS X boot / GO!'},
|
||||
'AD': {'OS': 'RISC OS', 'Description': 'ADFS / FileCore format'},
|
||||
'AE': {'OS': 'ShagOS', 'Description': 'ShagOS file system'},
|
||||
'AF': {'OS': 'ShagOS', 'Description': 'Apple Mac OS X HFS and HFS+ / ShagOS swap'},
|
||||
'B0': {'OS': 'Boot-Star', 'Description': 'Boot-Star dummy partition'},
|
||||
'B1': {'OS': 'QNX 6.x', 'Description': 'HP Volume Expansion (SpeedStor) / QNX Neutrino power-safe file system'},
|
||||
'B2': {'OS': 'QNX 6.x', 'Description': 'QNX Neutrino power-safe file system'},
|
||||
'B3': {'OS': 'QNX 6.x', 'Description': 'HP Volume Expansion (SpeedStor) / QNX Neutrino power-safe file system'},
|
||||
'B4': {'OS': 'Hewlett Packard', 'Description': 'HP Volume Expansion (SpeedStor)'},
|
||||
'B6': {'OS': 'Windows NT 4 Server', 'Description': 'HP Volume Expansion (SpeedStor) / Corrupted fault-tolerant FAT16B mirrored master volume '},
|
||||
'B7': {'OS': 'BSDI (before 3.0) / Windows NT 4 Server', 'Description': 'BSDI native file system / swap / Corrupted fault-tolerant HPFS/NTFS mirrored master volume '},
|
||||
'B8': {'OS': 'BSDI (before 3.0)', 'Description': 'BSDI swap / native file system'},
|
||||
'BB': {'OS': 'BootWizard, OS Selector / Acronis True Image / Windows NT 4 Server', 'Description': 'PTS BootWizard 4 / OS Selector 5 for hidden partitions other than 01h, 04h, 06h, 07h, 0Bh, 0Ch, 0Eh and unformatted partitions / OEM Secure Zone (corresponds to BCh) / Corrupted fault-tolerant FAT32 mirrored master volume '},
|
||||
'BC': {'OS': 'Windows NT 4 Server / Acronis True Image / Backup Capsule', 'Description': 'Corrupted fault-tolerant FAT32 mirrored master volume / Acronis Secure Zone / Backup Capsule'},
|
||||
'BD': {'OS': 'BonnyDOS/286', 'Description': 'Unknown'},
|
||||
'BE': {'OS': 'Solaris 8', 'Description': 'Solaris 8 boot'},
|
||||
'BF': {'OS': 'Solaris', 'Description': 'Solaris x86 (for Sun disklabels, since 2005)'},
|
||||
'C0': {'OS': 'DR-DOS, Multiuser DOS,REAL/32', 'Description': 'Secured FAT partition (smaller than 32 MB)'},
|
||||
'C1': {'OS': 'DR DOS 6.0+', 'Description': 'Secured FAT12 '},
|
||||
'C2': {'OS': 'Power Boot', 'Description': 'Hidden Linux native file system'},
|
||||
'C3': {'OS': 'Power Boot', 'Description': 'Hidden Linux swap'},
|
||||
'C4': {'OS': 'DR DOS 6.0+', 'Description': 'Secured FAT16 '},
|
||||
'C5': {'OS': 'DR DOS 6.0+', 'Description': 'Secured extended partition with CHS addressing '},
|
||||
'C6': {'OS': 'DR DOS 6.0+ / Windows NT 4 Server', 'Description': 'Secured FAT16B / Corrupted fault-tolerant FAT16B mirrored slave volume '},
|
||||
'C7': {'OS': 'Syrinx / Windows NT 4 Server', 'Description': 'Syrinx boot / Corrupted fault-tolerant HPFS/NTFS mirrored slave volume '},
|
||||
'C8': {'Description': 'Reserved for DR-DOS since 1997'},
|
||||
'C9': {'Description': 'Reserved for DR-DOS since 1997'},
|
||||
'CA': {'Description': 'Reserved for DR-DOS since 1997'},
|
||||
'CB': {'OS': 'DR-DOS 7.0x / Windows NT 4 Server', 'Description': 'Secured FAT32 / Corrupted fault-tolerant FAT32 mirrored slave volume '},
|
||||
'CC': {'OS': 'DR-DOS 7.0x / Windows NT 4 Server', 'Description': 'Secured FAT32 / Corrupted fault-tolerant FAT32 mirrored slave volume '},
|
||||
'CD': {'OS': 'CTOS', 'Description': 'Memory dump'},
|
||||
'CE': {'OS': 'DR-DOS 7.0x', 'Description': 'Secured FAT16B '},
|
||||
'CF': {'OS': 'DR-DOS 7.0x', 'Description': 'Secured extended partition with LBA '},
|
||||
'D0': {'OS': 'Multiuser DOS, REAL/32', 'Description': 'Secured FAT partition (larger than 32 MB)'},
|
||||
'D1': {'OS': 'Multiuser DOS', 'Description': 'Secured FAT12 '},
|
||||
'D4': {'OS': 'Multiuser DOS', 'Description': 'Secured FAT16 '},
|
||||
'D5': {'OS': 'Multiuser DOS', 'Description': 'Secured extended partition with CHS addressing '},
|
||||
'D6': {'OS': 'Multiuser DOS', 'Description': 'Secured FAT16B '},
|
||||
'D8': {'OS': 'Digital Research', 'Description': 'CP/M-86 [citation needed]'},
|
||||
'DA': {'OS': 'Powercopy Backup', 'Description': 'Non-file system data / Shielded disk'},
|
||||
'DB': {'OS': 'CP/M-86,Concurrent CP/M-86,Concurrent DOS / CTOS / D800 / DRMK', 'Description': 'CP/M-86, Concurrent CP/M-86, Concurrent DOS / boot image for x86 supervisor CPU (SCPU) module / FAT32 system restore partition (DSR)'},
|
||||
'DD': {'OS': 'CTOS', 'Description': 'Hidden memory dump'},
|
||||
'DE': {'OS': 'Dell', 'Description': 'FAT16 utility/diagnostic partition'},
|
||||
'DF': {'OS': 'DG/UX / BootIt / Aviion', 'Description': 'DG/UX virtual disk manager / EMBRM'},
|
||||
'E0': {'OS': 'STMicroelectronics', 'Description': 'ST AVFS'},
|
||||
'E1': {'OS': 'SpeedStor', 'Description': 'Extended FAT12 (> 1023 cylinder)'},
|
||||
'E2': {'Description': 'DOS read-only (XFDISK)'},
|
||||
'E3': {'OS': 'SpeedStor', 'Description': 'DOS read-only'},
|
||||
'E4': {'OS': 'SpeedStor', 'Description': 'Extended FAT16 (< 1024 cylinder)'},
|
||||
'E5': {'OS': 'Tandy MS-DOS', 'Description': 'Logical sectored FAT12 or FAT16'},
|
||||
'E6': {'OS': 'SpeedStor', 'Description': 'Unknown'},
|
||||
'E8': {'OS': 'LUKS', 'Description': 'Linux Unified Key Setup'},
|
||||
'EB': {'OS': 'BeOS, Haiku', 'Description': 'BFS'},
|
||||
'EC': {'OS': 'SkyOS', 'Description': 'SkyFS'},
|
||||
'ED': {'OS': 'Sprytix / EDD 4', 'Description': 'EDC loader / GPT hybrid MBR'},
|
||||
'EE': {'OS': 'EFI', 'Description': 'GPT protective MBR'},
|
||||
'EF': {'OS': 'EFI', 'Description': 'EFI system partition can be a FAT12, FAT16, FAT32 (or other) file system'},
|
||||
'F0': {'OS': 'Linux / OS/32', 'Description': 'PA-RISC Linux boot loader. It must reside in first physical 2 GB. / floppy'},
|
||||
'F1': {'OS': 'SpeedStor', 'Description': 'Unknown'},
|
||||
'F2': {'OS': 'Sperry IT MS-DOS 3.x, Unisys MS-DOS 3.3, Digital ResearchDOS Plus 2.1', 'Description': 'Logical sectored FAT12 or FAT16 secondary partition'},
|
||||
'F3': {'OS': 'SpeedStor', 'Description': 'Unknown'},
|
||||
'F4': {'OS': 'SpeedStor / Prologue', 'Description': '"large" DOS partition / single volume partition for NGF or TwinFS'},
|
||||
'F5': {'OS': 'Prologue', 'Description': 'MD0-MD9 multi volume partition for NGF or TwinFS'},
|
||||
'F6': {'OS': 'SpeedStor', 'Description': 'Unknown'},
|
||||
'F7': {'OS': 'O.S.G. / X1', 'Description': 'EFAT / Solid State file system'},
|
||||
'F9': {'OS': 'Linux', 'Description': 'pCache ext2/ext3 persistent cache'},
|
||||
'FA': {'OS': 'Bochs', 'Description': 'x86 emulator'},
|
||||
'FB': {'OS': 'VMware', 'Description': 'VMware VMFS file system partition'},
|
||||
'FC': {'OS': 'VMware', 'Description': 'VMware swap / VMKCORE kernel dump partition'},
|
||||
'FD': {'OS': 'Linux / FreeDOS', 'Description': 'Linux RAID superblock with auto-detect / Reserved for FreeDOS'},
|
||||
'FE': {'OS': 'SpeedStor / LANstep / Windows NT / Linux', 'Description': 'partition > 1024 cylinder / PS/2 IML partition / PS/2 recovery partition (FAT12 reference disk floppy image), / Disk Administration hidden partition / old Linux LVM'},
|
||||
'FF': {'OS': 'XENIX', 'Description': 'XENIX bad block table'},
|
||||
'00000000-0000-0000-0000-000000000000': {'Description': 'Unused entry'},
|
||||
'024DEE41-33E7-11D3-9D69-0008C781F39F': {'Description': 'MBR partition scheme'},
|
||||
'C12A7328-F81F-11D2-BA4B-00A0C93EC93B': {'Description': 'EFI System partition'},
|
||||
'21686148-6449-6E6F-744E-656564454649': {'Description': 'BIOS Boot partition'},
|
||||
'D3BFE2DE-3DAF-11DF-BA40-E3A556D89593': {'Description': 'Intel Fast Flash (iFFS) partition (for Intel Rapid Start technology)'},
|
||||
'F4019732-066E-4E12-8273-346C5641494F': {'Description': 'Sony boot partition'},
|
||||
'BFBFAFE7-A34F-448A-9A5B-6213EB736C22': {'Description': 'Lenovo boot partition'},
|
||||
'E3C9E316-0B5C-4DB8-817D-F92DF00215AE': {'OS': 'Windows', 'Description': 'Microsoft Reserved Partition (MSR)'},
|
||||
'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7': {'OS': 'Windows', 'Description': 'Basic data partition'},
|
||||
'5808C8AA-7E8F-42E0-85D2-E1E90434CFB3': {'OS': 'Windows', 'Description': 'Logical Disk Manager (LDM) metadata partition'},
|
||||
'AF9B60A0-1431-4F62-BC68-3311714A69AD': {'OS': 'Windows', 'Description': 'Logical Disk Manager data partition'},
|
||||
'DE94BBA4-06D1-4D40-A16A-BFD50179D6AC': {'OS': 'Windows', 'Description': 'Windows Recovery Environment'},
|
||||
'37AFFC90-EF7D-4E96-91C3-2D7AE055B174': {'OS': 'Windows', 'Description': 'IBM General Parallel File System (GPFS) partition'},
|
||||
'E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D': {'OS': 'Windows', 'Description': 'Storage Spaces partition'},
|
||||
'75894C1E-3AEB-11D3-B7C1-7B03A0000000': {'OS': 'HP-UX', 'Description': 'Data partition'},
|
||||
'E2A1E728-32E3-11D6-A682-7B03A0000000': {'OS': 'HP-UX', 'Description': 'Service Partition'},
|
||||
'0FC63DAF-8483-4772-8E79-3D69D8477DE4': {'OS': 'Linux', 'Description': 'Linux filesystem data'},
|
||||
'A19D880F-05FC-4D3B-A006-743F0F84911E': {'OS': 'Linux', 'Description': 'RAID partition'},
|
||||
'44479540-F297-41B2-9AF7-D131D5F0458A': {'OS': 'Linux', 'Description': 'Root partition (x86)'},
|
||||
'4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709': {'OS': 'Linux', 'Description': 'Root partition (x86-64)'},
|
||||
'69DAD710-2CE4-4E3C-B16C-21A1D49ABED3': {'OS': 'Linux', 'Description': 'Root partition (32-bit ARM)'},
|
||||
'B921B045-1DF0-41C3-AF44-4C6F280D3FAE': {'OS': 'Linux', 'Description': 'Root partition (64-bit ARM/AArch64)'},
|
||||
'0657FD6D-A4AB-43C4-84E5-0933C84B4F4F': {'OS': 'Linux', 'Description': 'Swap partition'},
|
||||
'E6D6D379-F507-44C2-A23C-238F2A3DF928': {'OS': 'Linux', 'Description': 'Logical Volume Manager (LVM) partition'},
|
||||
'933AC7E1-2EB4-4F13-B844-0E14E2AEF915': {'OS': 'Linux', 'Description': '/home partition'},
|
||||
'3B8F8425-20E0-4F3B-907F-1A25A76F98E8': {'OS': 'Linux', 'Description': '/srv (server data) partition'},
|
||||
'7FFEC5C9-2D00-49B7-8941-3EA10A5586B7': {'OS': 'Linux', 'Description': 'Plain dm-crypt partition'},
|
||||
'CA7D7CCB-63ED-4C53-861C-1742536059CC': {'OS': 'Linux', 'Description': 'LUKS partition'},
|
||||
'8DA63339-0007-60C0-C436-083AC8230908': {'OS': 'Linux', 'Description': 'Reserved'},
|
||||
'83BD6B9D-7F41-11DC-BE0B-001560B84F0F': {'OS': 'FreeBSD', 'Description': 'Boot partition'},
|
||||
'516E7CB4-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Data partition'},
|
||||
'516E7CB5-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Swap partition'},
|
||||
'516E7CB6-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Unix File System (UFS) partition'},
|
||||
'516E7CB8-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'Vinum volume manager partition'},
|
||||
'516E7CBA-6ECF-11D6-8FF8-00022D09712B': {'OS': 'FreeBSD', 'Description': 'ZFS partition'},
|
||||
'48465300-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Hierarchical File System Plus (HFS+) partition'},
|
||||
'55465300-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple UFS'},
|
||||
'6A898CC3-1DD2-11B2-99A6-080020736631': {'OS': 'OS X Darwin', 'Description': 'ZFS'},
|
||||
'52414944-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple RAID partition'},
|
||||
'52414944-5F4F-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple RAID partition, offline'},
|
||||
'426F6F74-0000-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple Boot partition (Recovery HD)'},
|
||||
'4C616265-6C00-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple Label'},
|
||||
'5265636F-7665-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple TV Recovery partition'},
|
||||
'53746F72-6167-11AA-AA11-00306543ECAC': {'OS': 'OS X Darwin', 'Description': 'Apple Core Storage (i.e. Lion FileVault) partition'},
|
||||
'6A82CB45-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Boot partition'},
|
||||
'6A85CF4D-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Root partition'},
|
||||
'6A87C46F-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Swap partition'},
|
||||
'6A8B642B-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Backup partition'},
|
||||
'6A898CC3-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': '/usr partition'},
|
||||
'6A8EF2E9-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': '/var partition'},
|
||||
'6A90BA39-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': '/home partition'},
|
||||
'6A9283A5-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Alternate sector'},
|
||||
'6A945A3B-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos', 'Description': 'Reserved partition'},
|
||||
'6A9630D1-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'},
|
||||
'6A980767-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'},
|
||||
'6A96237F-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'},
|
||||
'6A8D2AC7-1DD2-11B2-99A6-080020736631': {'OS': 'Solaris illumos'},
|
||||
'49F48D32-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'Swap partition'},
|
||||
'49F48D5A-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'FFS partition'},
|
||||
'49F48D82-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'LFS partition'},
|
||||
'49F48DAA-B10E-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'RAID partition'},
|
||||
'2DB519C4-B10F-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'Concatenated partition'},
|
||||
'2DB519EC-B10F-11DC-B99B-0019D1879648': {'OS': 'NetBSD', 'Description': 'Encrypted partition'},
|
||||
'FE3A2A5D-4F32-41A7-B725-ACCC3285A309': {'OS': 'ChromeOS', 'Description': 'ChromeOS kernel'},
|
||||
'3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC': {'OS': 'ChromeOS', 'Description': 'ChromeOS rootfs'},
|
||||
'2E0A753D-9E48-43B0-8337-B15192CB1B5E': {'OS': 'ChromeOS', 'Description': 'ChromeOS future use'},
|
||||
'42465331-3BA3-10F1-802A-4861696B7521': {'OS': 'Haiku', 'Description': 'Haiku BFS'},
|
||||
'85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Boot partition'},
|
||||
'85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Data partition'},
|
||||
'85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Swap partition'},
|
||||
'0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Unix File System (UFS) partition'},
|
||||
'85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'Vinum volume manager partition'},
|
||||
'85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7': {'OS': 'MidnightBSD', 'Description': 'ZFS partition'},
|
||||
'45B0969E-9B03-4F30-B4C6-B4B80CEFF106': {'OS': 'Ceph', 'Description': 'Ceph Journal'},
|
||||
'45B0969E-9B03-4F30-B4C6-5EC00CEFF106': {'OS': 'Ceph', 'Description': 'Ceph dm-crypt Encrypted Journal'},
|
||||
'4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D': {'OS': 'Ceph', 'Description': 'Ceph OSD'},
|
||||
'4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D': {'OS': 'Ceph', 'Description': 'Ceph dm-crypt OSD'},
|
||||
'89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE': {'OS': 'Ceph', 'Description': 'Ceph disk in creation'},
|
||||
'89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE': {'OS': 'Ceph', 'Description': 'Ceph dm-crypt disk in creation'},
|
||||
'824CC7A0-36A8-11E3-890A-952519AD3F61': {'OS': 'OpenBSD', 'Description': 'Data partition'},
|
||||
'CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1': {'OS': 'QNX', 'Description': 'Power-safe (QNX6) file system'},
|
||||
'C91818F9-8025-47AF-89D2-F030D7000C2C': {'OS': 'Plan 9', 'Description': 'Plan 9 partition'},
|
||||
'9D275380-40AD-11DB-BF97-000C2911D1B8': {'OS': 'VMware ESX', 'Description': 'vmkcore (coredump partition)'},
|
||||
'AA31E02A-400F-11DB-9590-000C2911D1B8': {'OS': 'VMware ESX', 'Description': 'VMFS filesystem partition'},
|
||||
'9198EFFC-31C0-11DB-8F78-000C2911D1B8': {'OS': 'VMware ESX', 'Description': 'VMware Reserved'},
|
||||
'2568845D-2332-4675-BC39-8FA5A4748D15': {'OS': 'Android-IA', 'Description': 'Bootloader'},
|
||||
'114EAFFE-1552-4022-B26E-9B053604CF84': {'OS': 'Android-IA', 'Description': 'Bootloader2'},
|
||||
'49A4D17F-93A3-45C1-A0DE-F50B2EBE2599': {'OS': 'Android-IA', 'Description': 'Boot'},
|
||||
'4177C722-9E92-4AAB-8644-43502BFD5506': {'OS': 'Android-IA', 'Description': 'Recovery'},
|
||||
'EF32A33B-A409-486C-9141-9FFB711F6266': {'OS': 'Android-IA', 'Description': 'Misc'},
|
||||
'20AC26BE-20B7-11E3-84C5-6CFDB94711E9': {'OS': 'Android-IA', 'Description': 'Metadata'},
|
||||
'38F428E6-D326-425D-9140-6E0EA133647C': {'OS': 'Android-IA', 'Description': 'System'},
|
||||
'A893EF21-E428-470A-9E55-0668FD91A2D9': {'OS': 'Android-IA', 'Description': 'Cache'},
|
||||
'DC76DDA9-5AC1-491C-AF42-A82591580C0D': {'OS': 'Android-IA', 'Description': 'Data'},
|
||||
'EBC597D0-2053-4B15-8B64-E0AAC75F4DB1': {'OS': 'Android-IA', 'Description': 'Persistent'},
|
||||
'8F68CC74-C5E5-48DA-BE91-A0C8C15E9C80': {'OS': 'Android-IA', 'Description': 'Factory'},
|
||||
'767941D0-2085-11E3-AD3B-6CFDB94711E9': {'OS': 'Android-IA', 'Description': 'Fastboot / Tertiary'},
|
||||
'AC6D7924-EB71-4DF8-B48D-E267B27148FF': {'OS': 'Android-IA', 'Description': 'OEM'},
|
||||
'7412F7D5-A156-4B13-81DC-867174929325': {'OS': 'ONIE', 'Description': 'Boot'},
|
||||
'D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149': {'OS': 'ONIE', 'Description': 'Config'},
|
||||
'9E1A2D38-C612-4316-AA26-8B49521E5A8B': {'OS': 'PowerPC', 'Description': 'PReP boot'},
|
||||
'BC13C2FF-59E6-4262-A352-B275FD6F7172': {'OS': 'Freedesktop', 'Description': 'Extended Boot Partition ($BOOT)'},
|
||||
}
|
||||
|
||||
def lookup_guid(guid):
|
||||
return PARTITION_UIDS.get(guid.upper(), None)
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
6
System32/Winpeshl.ini
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[LaunchApp]
|
||||
[LaunchApps]
|
||||
wpeinit
|
||||
wpeutil updatebootinfo
|
||||
cd /d "%SystemDrive%\WK"
|
||||
"%SystemDrive%\WK\ConEmu\ConEmu.exe", /cmd cmd /k cd "%SystemDrive%\WK" & python "%SystemDrive%\WK\Scripts\menu.py"
|
||||
2
System32/menu.cmd
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
@echo off
|
||||
python "%SystemDrive%\WK\Scripts\menu.py"
|
||||
20
WK/_include/CPU-Z/cpuz.ini
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[CPU-Z]
|
||||
VERSION=1.7.7.0
|
||||
TextFontName=
|
||||
TextFontSize=14
|
||||
TextFontColor=000080
|
||||
LabelFontName=
|
||||
LabelFontSize=14
|
||||
ACPI=1
|
||||
PCI=1
|
||||
MaxPCIBus=256
|
||||
DMI=1
|
||||
Sensor=1
|
||||
SMBus=1
|
||||
Display=1
|
||||
UseDisplayAPI=1
|
||||
BusClock=1
|
||||
Chipset=1
|
||||
SPD=1
|
||||
XOC=0
|
||||
CheckUpdates=0
|
||||
|
|
@ -617,50 +617,55 @@ BATTERY=1
|
|||
SENSORS=1
|
||||
|
||||
[Settings]
|
||||
AC97CodecID=1
|
||||
AcpiEnum=0
|
||||
AcpiEval=1
|
||||
AutoUpdate=0
|
||||
AutoUpdateBetaDisable=1
|
||||
BusClkPolling=1
|
||||
CpuClkFromBusClk=1
|
||||
DebugMode=0
|
||||
DefReportType=5
|
||||
EC=1
|
||||
FlushBuffers=1
|
||||
GPUI2C=1
|
||||
GPUI2CADL=0
|
||||
GPUI2CBusExclude=00000000
|
||||
GPUI2CNVAPI=1
|
||||
GPUI2Ccaching=1
|
||||
HighestIdeAddress=0
|
||||
IoctlKernel=0
|
||||
KeepTheme=0
|
||||
AcpiEnum=0
|
||||
SWSMI=1
|
||||
DebugMode=0
|
||||
SMBus=1
|
||||
TempScale=C
|
||||
AC97CodecID=1
|
||||
SkipProblematicPciDev=0
|
||||
GPUI2C=1
|
||||
LPC=1
|
||||
DefReportType=5
|
||||
TPM=0
|
||||
PCIdirect=1
|
||||
OpenSystemSummary=0
|
||||
RememberPreferences=1
|
||||
LargeFonts=0
|
||||
OpenSensors=0
|
||||
MinimalizeMainWnd=0
|
||||
MinimalizeSensors=0
|
||||
OpenSensors=0
|
||||
OpenSystemSummary=0
|
||||
PCIdirect=1
|
||||
PersistentDriver=0
|
||||
RememberPreferences=1
|
||||
SMBus=1
|
||||
SMBusAdrExclude=11111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000
|
||||
SWSMI=1
|
||||
SensorsSM=1
|
||||
ShowWelcomeAndProgress=0
|
||||
SkipProblematicPciDev=0
|
||||
TPM=0
|
||||
TempScale=C
|
||||
UseHPET=1
|
||||
AutoUpdate=0
|
||||
GPUI2CNVAPI=1
|
||||
GPUI2CADL=0
|
||||
SensorsOnly=0
|
||||
AcpiEval=1
|
||||
CpuClkFromBusClk=1
|
||||
BusClkPolling=1
|
||||
SMBusAdrExclude=11111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000
|
||||
GPUI2CBusExclude=00000000
|
||||
SensorsSM=1
|
||||
IoctlKernel=0
|
||||
SummaryOnly=0
|
||||
WakeGPUs=1
|
||||
KeepTheme=0
|
||||
FlushBuffers=1
|
||||
iMEsupport=1
|
||||
MinimizeGraphs=1
|
||||
SensorRowShading=1
|
||||
SensorsTopmost=0
|
||||
GPUI2Ccaching=1
|
||||
CSMI_SAS_Support=1
|
||||
DebugDirect=1
|
||||
MinimalizeSensorsClose=0
|
||||
WakeGPUsExt=0
|
||||
PollSleepingGPUs=0
|
||||
ShowWelcomeAndProgress=1
|
||||
EnablePchTherm=0
|
||||
ReorderGPUs=1
|
||||
NvmlSupport=1
|
||||
DecimalSeparator=.
|
||||
ThousandsSeparator=,
|
||||
CsvSeparator=,
|
||||
MinimizeGraphs=1
|
||||
TextButtons=0
|
||||
SensorToolTips=0
|
||||
|
||||
|
|
@ -5,10 +5,10 @@
|
|||
<GUIConfigs>
|
||||
<GUIConfig name="ToolBar" visible="no">standard</GUIConfig>
|
||||
<GUIConfig name="StatusBar">hide</GUIConfig>
|
||||
<GUIConfig name="TabBar" dragAndDrop="yes" drawTopBar="yes" drawInactiveTab="yes" reduce="yes" closeButton="yes" doubleClick2Close="no" vertical="no" multiLine="no" hide="no" quitOnEmpty="no" />
|
||||
<GUIConfig name="TabBar" dragAndDrop="yes" drawTopBar="yes" drawInactiveTab="yes" reduce="yes" closeButton="yes" doubleClick2Close="no" vertical="no" multiLine="no" hide="yes" quitOnEmpty="no" />
|
||||
<GUIConfig name="ScintillaViewsSplitter">vertical</GUIConfig>
|
||||
<GUIConfig name="UserDefineDlg" position="undocked">hide</GUIConfig>
|
||||
<GUIConfig name="TabSetting" replaceBySpace="yes" size="4" />
|
||||
<GUIConfig name="TabSetting" replaceBySpace="no" size="4" />
|
||||
<GUIConfig name="noUpdate" intervalDays="15" nextUpdateDate="20080426">no</GUIConfig>
|
||||
<GUIConfig name="Auto-detection">yes</GUIConfig>
|
||||
<GUIConfig name="CheckHistoryFiles">no</GUIConfig>
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
<GUIConfig name="TaskList">yes</GUIConfig>
|
||||
<GUIConfig name="MRU">yes</GUIConfig>
|
||||
<GUIConfig name="URL">2</GUIConfig>
|
||||
<GUIConfig name="globalOverride" fg="no" bg="no" font="yes" fontSize="no" bold="no" italic="no" underline="no" />
|
||||
<GUIConfig name="globalOverride" fg="no" bg="no" font="no" fontSize="no" bold="no" italic="no" underline="no" />
|
||||
<GUIConfig name="auto-completion" autoCAction="3" triggerFromNbChar="1" autoCIgnoreNumbers="yes" funcParams="yes" />
|
||||
<GUIConfig name="auto-insert" parentheses="no" brackets="no" curlyBrackets="no" quotes="no" doubleQuotes="no" htmlXmlTag="no" />
|
||||
<GUIConfig name="sessionExt"></GUIConfig>
|
||||
33
WK/_include/NotepadPlusPlus/npp.vbs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
'// DISCLAIMER
|
||||
'// THIS COMES WITH NO WARRANTY, IMPLIED OR OTHERWISE. USE AT YOUR OWN RISK
|
||||
'// IF YOU ARE NOT COMFORTABLE EDITING THE REGISTRY THEN DO NOT USE THIS SCRIPT
|
||||
'//
|
||||
'// NOTES:
|
||||
'// This affects all users.
|
||||
'// This will prevent ANY executable named notepad.exe from running located anywhere on this computer!!
|
||||
'//
|
||||
'// Save this text to your notepad++ folder as a text file named npp.vbs (some AV don't like vbs, get a different AV :-P )
|
||||
'//
|
||||
'// USAGE
|
||||
'// 1)
|
||||
'// Navigate to registry key HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\
|
||||
'//
|
||||
' // 2)
|
||||
'// Add new subkey called notepad.exe
|
||||
'// This step is what tells windows to use the notepad++ exe, to undo simply delete this key
|
||||
'//
|
||||
'// 3)
|
||||
'// Create new Sting Value called Debugger
|
||||
'//
|
||||
'// 4)
|
||||
'// Modify value and enter wscript.exe "path to npp.vbs" e.g. wscript.exe "C:\Program Files\Notepad++\npp.vbs"
|
||||
|
||||
Option Explicit
|
||||
Dim sCmd, x
|
||||
sCmd = """" & LeftB(WScript.ScriptFullName, LenB(WScript.ScriptFullName) - LenB(WScript.ScriptName)) & "notepad++.exe" & """ """
|
||||
For x = 1 To WScript.Arguments.Count - 1
|
||||
sCmd = sCmd & WScript.Arguments(x) & " "
|
||||
Next
|
||||
sCmd = sCmd & """"
|
||||
CreateObject("WScript.Shell").Run sCmd, 1, True
|
||||
WScript.Quit
|
||||
68
WK/_include/Q-Dir/Q-Dir.ini
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
[Start]
|
||||
m_lang_id=1
|
||||
QDir_Id=0
|
||||
useColorStart=1
|
||||
Als=12
|
||||
designe_mode=2
|
||||
showCmd=3
|
||||
StartCrash=0
|
||||
default_tab=
|
||||
Max=1
|
||||
show_ext_in_type=1
|
||||
title_info=1
|
||||
adresbar_style=1
|
||||
useTreeColor=1
|
||||
useColor=1
|
||||
[Favoriten]
|
||||
Ordner=.\Favoriten\
|
||||
[Q]
|
||||
Link=.\Favoriten\Quick-Link\
|
||||
[Q-Dir]
|
||||
Lizenz=1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[Vorschau]
|
||||
Filter=*.avi;*.bmp;*.gif;*.ico;*.jpeg;*.jpg;*.mp*;*.pdf;*.png;*.wm*;
|
||||
[Filter2]
|
||||
0=#Hidden=1=-1=1=1=-1=0
|
||||
1=*.zip;*.rar;*.gz;*.7z;=1=00AA00=-1=1=-1=0
|
||||
2=*.mp*;*.avi;*.wma;=1=DD0058=-1=1=-1=0
|
||||
3=#DIR=1=008800=-1=1=-1=0
|
||||
4=*.jpg;*.jpeg;*.png,*.gif;*.bmp;*.ico=1=BB00BB=-1=1=-1=0
|
||||
5=*.html;*.htm;*.url=1=456789=-1=1=-1=0
|
||||
6=*.pl;*.cgi;*.php;*.pdf;*.doc;*.rtf;*.xls=1=BB8844=-1=1=-1=0
|
||||
7=*.cpp;*.hpp;*.h=1=DD0058=-1=1=-1=0
|
||||
8=*.exe;*.dll;*.bat=1=FF0000=-1=1=-1=0
|
||||
9=*=1=0000BB=-1=1=-1=0
|
||||
10=#BG=1=-1=-1=1=-1=0
|
||||
11=#BG-A=1=-1=-1=1=-1=0
|
||||
[Ordner]
|
||||
Filter=
|
||||
[Column_OS_6.1_Ploder1]
|
||||
Spatlen_::{20D04FE0-3AEA-1069-A2D8-08002B30309D}=%1C%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%F1%F1%F1%F1%14%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%D0%02%00%00%CC%02%00%00%31%53%50%53%05%D5%CD%D5%9C%2E%1B%10%93%97%08%00%2B%2C%F9%AE%83%00%00%00%22%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%4B%00%65%00%79%00%3A%00%46%00%4D%00%54%00%49%00%44%00%00%00%08%00%00%00%4E%00%00%00%7B%00%42%00%37%00%32%00%35%00%46%00%31%00%33%00%30%00%2D%00%34%00%37%00%45%00%46%00%2D%00%31%00%30%00%31%00%41%00%2D%00%41%00%35%00%46%00%31%00%2D%00%30%00%32%00%36%00%30%00%38%00%43%00%39%00%45%00%45%00%42%00%41%00%43%00%7D%00%00%00%00%00%33%00%00%00%22%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%44%00%69%00%72%00%65%00%63%00%74%00%69%00%6F%00%6E%00%00%00%13%00%00%00%01%00%00%00%5B%00%00%00%0A%00%00%00%00%53%00%6F%00%72%00%74%00%00%00%42%00%00%00%1E%00%00%00%70%00%72%00%6F%00%70%00%34%00%32%00%39%00%34%00%39%00%36%00%37%00%32%00%39%00%35%00%00%00%00%00%1C%00%00%00%01%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0A%00%00%00%01%00%00%00%25%00%00%00%14%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%56%00%69%00%65%00%77%00%00%00%0B%00%00%00%FF%FF%00%00%1B%00%00%00%0A%00%00%00%00%4D%00%6F%00%64%00%65%00%00%00%13%00%00%00%04%00%00%00%23%00%00%00%12%00%00%00%00%49%00%63%00%6F%00%6E%00%53%00%69%00%7A%00%65%00%00%00%13%00%00%00%10%00%00%00%BD%00%00%00%10%00%00%00%00%43%00%6F%00%6C%00%49%00%6E%00%66%00%6F%00%00%00%42%00%00%00%1E%00%00%00%70%00%72%00%6F%00%70%00%34%00%32%00%39%00%34%00%39%00%36%00%37%00%32%00%39%00%35%00%00%00%00%00%78%00%00%00%FD%DF%DF%FD%10%00%00%00%00%00%00%00%00%00%00%00%04%00%00%00%18%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0A%00%00%00%8C%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%04%00%00%00%AF%00%00%00%35%4B%17%9B%FF%40%D2%11%A2%7E%00%C0%4F%C3%08%71%03%00%00%00%70%00%00%00%35%4B%17%9B%FF%40%D2%11%A2%7E%00%C0%4F%C3%08%71%02%00%00%00%70%00%00%00%2F%00%00%00%1E%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%4B%00%65%00%79%00%3A%00%50%00%49%00%44%00%00%00%13%00%00%00%04%00%00%00%1F%00%00%00%0E%00%00%00%00%46%00%46%00%6C%00%61%00%67%00%73%00%00%00%13%00%00%00%05%00%20%40%31%00%00%00%20%00%00%00%00%4C%00%6F%00%67%00%69%00%63%00%61%00%6C%00%56%00%69%00%65%00%77%00%4D%00%6F%00%64%00%65%00%00%00%13%00%00%00%01%00%00%00%00%00%00%00%00%00%00%00|CODEMODE1|-1905896973|772
|
||||
Spatlen_291=%1C%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%F1%F1%F1%F1%14%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%D0%02%00%00%CC%02%00%00%31%53%50%53%05%D5%CD%D5%9C%2E%1B%10%93%97%08%00%2B%2C%F9%AE%83%00%00%00%22%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%4B%00%65%00%79%00%3A%00%46%00%4D%00%54%00%49%00%44%00%00%00%08%00%00%00%4E%00%00%00%7B%00%30%00%30%00%30%00%30%00%30%00%30%00%30%00%30%00%2D%00%30%00%30%00%30%00%30%00%2D%00%30%00%30%00%30%00%30%00%2D%00%30%00%30%00%30%00%30%00%2D%00%30%00%30%00%30%00%30%00%30%00%30%00%30%00%30%00%30%00%30%00%30%00%30%00%7D%00%00%00%00%00%33%00%00%00%22%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%44%00%69%00%72%00%65%00%63%00%74%00%69%00%6F%00%6E%00%00%00%13%00%00%00%01%00%00%00%5B%00%00%00%0A%00%00%00%00%53%00%6F%00%72%00%74%00%00%00%42%00%00%00%1E%00%00%00%70%00%72%00%6F%00%70%00%34%00%32%00%39%00%34%00%39%00%36%00%37%00%32%00%39%00%35%00%00%00%00%00%1C%00%00%00%01%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0A%00%00%00%01%00%00%00%25%00%00%00%14%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%56%00%69%00%65%00%77%00%00%00%0B%00%00%00%00%00%00%00%1B%00%00%00%0A%00%00%00%00%4D%00%6F%00%64%00%65%00%00%00%13%00%00%00%04%00%00%00%23%00%00%00%12%00%00%00%00%49%00%63%00%6F%00%6E%00%53%00%69%00%7A%00%65%00%00%00%13%00%00%00%10%00%00%00%BD%00%00%00%10%00%00%00%00%43%00%6F%00%6C%00%49%00%6E%00%66%00%6F%00%00%00%42%00%00%00%1E%00%00%00%70%00%72%00%6F%00%70%00%34%00%32%00%39%00%34%00%39%00%36%00%37%00%32%00%39%00%35%00%00%00%00%00%78%00%00%00%FD%DF%DF%FD%10%00%00%00%00%00%00%00%00%00%00%00%04%00%00%00%18%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0A%00%00%00%EE%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0E%00%00%00%69%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%04%00%00%00%91%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0C%00%00%00%46%00%00%00%2F%00%00%00%1E%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%4B%00%65%00%79%00%3A%00%50%00%49%00%44%00%00%00%13%00%00%00%00%00%00%00%1F%00%00%00%0E%00%00%00%00%46%00%46%00%6C%00%61%00%67%00%73%00%00%00%13%00%00%00%05%00%20%40%31%00%00%00%20%00%00%00%00%4C%00%6F%00%67%00%69%00%63%00%61%00%6C%00%56%00%69%00%65%00%77%00%4D%00%6F%00%64%00%65%00%00%00%13%00%00%00%01%00%00%00%00%00%00%00%00%00%00%00|CODEMODE1|-563719693|772
|
||||
[Column_OS_6.1_Ploder2]
|
||||
Spatlen_::{20D04FE0-3AEA-1069-A2D8-08002B30309D}=%1C%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%F1%F1%F1%F1%14%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%D0%02%00%00%CC%02%00%00%31%53%50%53%05%D5%CD%D5%9C%2E%1B%10%93%97%08%00%2B%2C%F9%AE%83%00%00%00%22%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%4B%00%65%00%79%00%3A%00%46%00%4D%00%54%00%49%00%44%00%00%00%08%00%00%00%4E%00%00%00%7B%00%42%00%37%00%32%00%35%00%46%00%31%00%33%00%30%00%2D%00%34%00%37%00%45%00%46%00%2D%00%31%00%30%00%31%00%41%00%2D%00%41%00%35%00%46%00%31%00%2D%00%30%00%32%00%36%00%30%00%38%00%43%00%39%00%45%00%45%00%42%00%41%00%43%00%7D%00%00%00%00%00%33%00%00%00%22%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%44%00%69%00%72%00%65%00%63%00%74%00%69%00%6F%00%6E%00%00%00%13%00%00%00%01%00%00%00%5B%00%00%00%0A%00%00%00%00%53%00%6F%00%72%00%74%00%00%00%42%00%00%00%1E%00%00%00%70%00%72%00%6F%00%70%00%34%00%32%00%39%00%34%00%39%00%36%00%37%00%32%00%39%00%35%00%00%00%00%00%1C%00%00%00%01%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0A%00%00%00%01%00%00%00%25%00%00%00%14%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%56%00%69%00%65%00%77%00%00%00%0B%00%00%00%FF%FF%00%00%1B%00%00%00%0A%00%00%00%00%4D%00%6F%00%64%00%65%00%00%00%13%00%00%00%04%00%00%00%23%00%00%00%12%00%00%00%00%49%00%63%00%6F%00%6E%00%53%00%69%00%7A%00%65%00%00%00%13%00%00%00%10%00%00%00%BD%00%00%00%10%00%00%00%00%43%00%6F%00%6C%00%49%00%6E%00%66%00%6F%00%00%00%42%00%00%00%1E%00%00%00%70%00%72%00%6F%00%70%00%34%00%32%00%39%00%34%00%39%00%36%00%37%00%32%00%39%00%35%00%00%00%00%00%78%00%00%00%FD%DF%DF%FD%10%00%00%00%00%00%00%00%00%00%00%00%04%00%00%00%18%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0A%00%00%00%8C%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%04%00%00%00%AF%00%00%00%35%4B%17%9B%FF%40%D2%11%A2%7E%00%C0%4F%C3%08%71%03%00%00%00%70%00%00%00%35%4B%17%9B%FF%40%D2%11%A2%7E%00%C0%4F%C3%08%71%02%00%00%00%70%00%00%00%2F%00%00%00%1E%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%4B%00%65%00%79%00%3A%00%50%00%49%00%44%00%00%00%13%00%00%00%04%00%00%00%1F%00%00%00%0E%00%00%00%00%46%00%46%00%6C%00%61%00%67%00%73%00%00%00%13%00%00%00%05%00%20%40%31%00%00%00%20%00%00%00%00%4C%00%6F%00%67%00%69%00%63%00%61%00%6C%00%56%00%69%00%65%00%77%00%4D%00%6F%00%64%00%65%00%00%00%13%00%00%00%01%00%00%00%00%00%00%00%00%00%00%00|CODEMODE1|-1905896973|772
|
||||
[Column_OS_6.1_Ploder3]
|
||||
Spatlen_::{20D04FE0-3AEA-1069-A2D8-08002B30309D}=%1C%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%F1%F1%F1%F1%14%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%D0%02%00%00%CC%02%00%00%31%53%50%53%05%D5%CD%D5%9C%2E%1B%10%93%97%08%00%2B%2C%F9%AE%83%00%00%00%22%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%4B%00%65%00%79%00%3A%00%46%00%4D%00%54%00%49%00%44%00%00%00%08%00%00%00%4E%00%00%00%7B%00%42%00%37%00%32%00%35%00%46%00%31%00%33%00%30%00%2D%00%34%00%37%00%45%00%46%00%2D%00%31%00%30%00%31%00%41%00%2D%00%41%00%35%00%46%00%31%00%2D%00%30%00%32%00%36%00%30%00%38%00%43%00%39%00%45%00%45%00%42%00%41%00%43%00%7D%00%00%00%00%00%33%00%00%00%22%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%44%00%69%00%72%00%65%00%63%00%74%00%69%00%6F%00%6E%00%00%00%13%00%00%00%01%00%00%00%5B%00%00%00%0A%00%00%00%00%53%00%6F%00%72%00%74%00%00%00%42%00%00%00%1E%00%00%00%70%00%72%00%6F%00%70%00%34%00%32%00%39%00%34%00%39%00%36%00%37%00%32%00%39%00%35%00%00%00%00%00%1C%00%00%00%01%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0A%00%00%00%01%00%00%00%25%00%00%00%14%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%56%00%69%00%65%00%77%00%00%00%0B%00%00%00%FF%FF%00%00%1B%00%00%00%0A%00%00%00%00%4D%00%6F%00%64%00%65%00%00%00%13%00%00%00%04%00%00%00%23%00%00%00%12%00%00%00%00%49%00%63%00%6F%00%6E%00%53%00%69%00%7A%00%65%00%00%00%13%00%00%00%10%00%00%00%BD%00%00%00%10%00%00%00%00%43%00%6F%00%6C%00%49%00%6E%00%66%00%6F%00%00%00%42%00%00%00%1E%00%00%00%70%00%72%00%6F%00%70%00%34%00%32%00%39%00%34%00%39%00%36%00%37%00%32%00%39%00%35%00%00%00%00%00%78%00%00%00%FD%DF%DF%FD%10%00%00%00%00%00%00%00%00%00%00%00%04%00%00%00%18%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0A%00%00%00%8C%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%04%00%00%00%AF%00%00%00%35%4B%17%9B%FF%40%D2%11%A2%7E%00%C0%4F%C3%08%71%03%00%00%00%70%00%00%00%35%4B%17%9B%FF%40%D2%11%A2%7E%00%C0%4F%C3%08%71%02%00%00%00%70%00%00%00%2F%00%00%00%1E%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%4B%00%65%00%79%00%3A%00%50%00%49%00%44%00%00%00%13%00%00%00%04%00%00%00%1F%00%00%00%0E%00%00%00%00%46%00%46%00%6C%00%61%00%67%00%73%00%00%00%13%00%00%00%05%00%20%40%31%00%00%00%20%00%00%00%00%4C%00%6F%00%67%00%69%00%63%00%61%00%6C%00%56%00%69%00%65%00%77%00%4D%00%6F%00%64%00%65%00%00%00%13%00%00%00%01%00%00%00%00%00%00%00%00%00%00%00|CODEMODE1|-1905896973|772
|
||||
[Column_OS_6.1_Ploder4]
|
||||
Spatlen_::{20D04FE0-3AEA-1069-A2D8-08002B30309D}=%1C%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%F1%F1%F1%F1%14%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%D0%02%00%00%CC%02%00%00%31%53%50%53%05%D5%CD%D5%9C%2E%1B%10%93%97%08%00%2B%2C%F9%AE%83%00%00%00%22%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%4B%00%65%00%79%00%3A%00%46%00%4D%00%54%00%49%00%44%00%00%00%08%00%00%00%4E%00%00%00%7B%00%42%00%37%00%32%00%35%00%46%00%31%00%33%00%30%00%2D%00%34%00%37%00%45%00%46%00%2D%00%31%00%30%00%31%00%41%00%2D%00%41%00%35%00%46%00%31%00%2D%00%30%00%32%00%36%00%30%00%38%00%43%00%39%00%45%00%45%00%42%00%41%00%43%00%7D%00%00%00%00%00%33%00%00%00%22%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%44%00%69%00%72%00%65%00%63%00%74%00%69%00%6F%00%6E%00%00%00%13%00%00%00%01%00%00%00%5B%00%00%00%0A%00%00%00%00%53%00%6F%00%72%00%74%00%00%00%42%00%00%00%1E%00%00%00%70%00%72%00%6F%00%70%00%34%00%32%00%39%00%34%00%39%00%36%00%37%00%32%00%39%00%35%00%00%00%00%00%1C%00%00%00%01%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0A%00%00%00%01%00%00%00%25%00%00%00%14%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%56%00%69%00%65%00%77%00%00%00%0B%00%00%00%FF%FF%00%00%1B%00%00%00%0A%00%00%00%00%4D%00%6F%00%64%00%65%00%00%00%13%00%00%00%04%00%00%00%23%00%00%00%12%00%00%00%00%49%00%63%00%6F%00%6E%00%53%00%69%00%7A%00%65%00%00%00%13%00%00%00%10%00%00%00%BD%00%00%00%10%00%00%00%00%43%00%6F%00%6C%00%49%00%6E%00%66%00%6F%00%00%00%42%00%00%00%1E%00%00%00%70%00%72%00%6F%00%70%00%34%00%32%00%39%00%34%00%39%00%36%00%37%00%32%00%39%00%35%00%00%00%00%00%78%00%00%00%FD%DF%DF%FD%10%00%00%00%00%00%00%00%00%00%00%00%04%00%00%00%18%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%0A%00%00%00%8C%00%00%00%30%F1%25%B7%EF%47%1A%10%A5%F1%02%60%8C%9E%EB%AC%04%00%00%00%AF%00%00%00%35%4B%17%9B%FF%40%D2%11%A2%7E%00%C0%4F%C3%08%71%03%00%00%00%70%00%00%00%35%4B%17%9B%FF%40%D2%11%A2%7E%00%C0%4F%C3%08%71%02%00%00%00%70%00%00%00%2F%00%00%00%1E%00%00%00%00%47%00%72%00%6F%00%75%00%70%00%42%00%79%00%4B%00%65%00%79%00%3A%00%50%00%49%00%44%00%00%00%13%00%00%00%04%00%00%00%1F%00%00%00%0E%00%00%00%00%46%00%46%00%6C%00%61%00%67%00%73%00%00%00%13%00%00%00%05%00%20%40%31%00%00%00%20%00%00%00%00%4C%00%6F%00%67%00%69%00%63%00%61%00%6C%00%56%00%69%00%65%00%77%00%4D%00%6F%00%64%00%65%00%00%00%13%00%00%00%01%00%00%00%00%00%00%00%00%00%00%00|CODEMODE1|-1905896973|772
|
||||
[Programs_State]
|
||||
Disable=0
|
||||
[Quick-Links]
|
||||
WK=%systemdrive%/WK
|
||||
[Options]
|
||||
Start=7
|
||||
[X-Size]
|
||||
mode=1
|
||||
dig=0
|
||||
fld_size=1
|
||||
ths_sep=1
|
||||
type=0
|
||||
precent=1
|
||||
ff_cnt=1
|
||||
block_no_focus=1
|
||||
nosort_fld_size=1
|
||||
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 113 KiB |
BIN
images/Linux.png
|
Before Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 15 KiB |
122
images/logo.svg
|
|
@ -1,122 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32"
|
||||
height="32"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r"
|
||||
sodipodi:docname="logo.svg"
|
||||
inkscape:export-filename="/home/thewizardpp/projects/logos/logo512x512.png"
|
||||
inkscape:export-xdpi="1440"
|
||||
inkscape:export-ydpi="1440">
|
||||
<defs
|
||||
id="defs4">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 526.18109 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
id="perspective10" />
|
||||
<filter
|
||||
id="filter3668"
|
||||
inkscape:label="Drop shadow"
|
||||
width="1.5"
|
||||
height="1.5"
|
||||
x="-.25"
|
||||
y="-.25">
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur3670"
|
||||
in="SourceAlpha"
|
||||
stdDeviation="1,000000"
|
||||
result="blur" />
|
||||
<feColorMatrix
|
||||
id="feColorMatrix3672"
|
||||
result="bluralpha"
|
||||
type="matrix"
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0,500000 0 " />
|
||||
<feOffset
|
||||
id="feOffset3674"
|
||||
in="bluralpha"
|
||||
dx="1,000000"
|
||||
dy="1,000000"
|
||||
result="offsetBlur" />
|
||||
<feMerge
|
||||
id="feMerge3676">
|
||||
<feMergeNode
|
||||
id="feMergeNode3678"
|
||||
in="offsetBlur" />
|
||||
<feMergeNode
|
||||
id="feMergeNode3680"
|
||||
in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="16.469461"
|
||||
inkscape:cy="15.775995"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer2"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1152"
|
||||
inkscape:window-height="844"
|
||||
inkscape:window-x="-2"
|
||||
inkscape:window-y="93"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer"
|
||||
style="display:inline">
|
||||
<g
|
||||
id="g3613"
|
||||
transform="matrix(1.0696952,0,0,1.0696952,-1.9682871,1.2767394)">
|
||||
<path
|
||||
sodipodi:nodetypes="cssssss"
|
||||
d="m 28.466519,15.480445 c -1.690444,-0.411311 -3.880242,0.0024 -6.862802,1.703057 -4.343818,2.477 -5.647804,4.7124 -10.531132,6.5262 -2.7416801,1.0184 -7.1725478,1.2727 -6.7296333,-1.9563 0.4055207,-2.9564 4.8746766,-5.683963 10.7473903,-5.268022 7.253753,0.513753 7.780294,2.643843 11.236758,2.445771 4.073631,-0.233438 3.02577,-3.235043 2.139419,-3.450706 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="path2822"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccscsc"
|
||||
id="path2832"
|
||||
d="m 22.349625,16.595174 c -5.498466,2.959917 -4.603518,5.10607 -10.999048,3.821601 1.40216,-4.418086 4.962036,-16.95097 7.147841,-17.2692571 1.878431,-0.2735287 4.924495,4.2931483 4.924495,4.2931483 0,0 -3.661803,-2.9673231 -4.16688,-1.7046325 -0.593183,1.4829546 2.39459,8.4145833 3.093592,10.8591403 z"
|
||||
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccc"
|
||||
id="path3611"
|
||||
d="m 22.074942,15.74979 c 1.515307,-0.313608 1.831341,-0.3546 3.377477,-0.485523 1.799175,-0.173029 3.187957,0.237433 3.187957,0.237433"
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.4 KiB |
BIN
images/macOS.png
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
|
@ -1,508 +0,0 @@
|
|||
:: WizardKit: Wrapper for launching programs and scripts.
|
||||
::
|
||||
:: Some features:
|
||||
:: * If the OS is 64-bit then the WorkingDir is scanned for a 64-bit version of the programs
|
||||
:: * Allows for centralized terminal emulation settings management
|
||||
:: * Allows for smaller "launcher" scripts to be used as they will rely on this script.
|
||||
|
||||
@echo off
|
||||
if defined DEBUG (@echo on)
|
||||
|
||||
:Init
|
||||
setlocal EnableDelayedExpansion
|
||||
title WizardKit: Launcher
|
||||
pushd "%~dp0"
|
||||
call :FindBin
|
||||
call :DeQuote L_ITEM
|
||||
call :DeQuote L_PATH
|
||||
call :DeQuote L_TYPE
|
||||
|
||||
:SetVariables
|
||||
rem Set variables using settings\main.py file
|
||||
set "SETTINGS=%bin%\Scripts\wk\cfg\main.py"
|
||||
for %%v in (ARCHIVE_PASSWORD KIT_NAME_FULL OFFICE_SERVER_IP QUICKBOOKS_SERVER_IP) do (
|
||||
set "var=%%v"
|
||||
for /f "tokens=* usebackq" %%f in (`findstr "!var!=" "%SETTINGS%"`) do (
|
||||
set "_v=%%f"
|
||||
set "_v=!_v:*'=!"
|
||||
set "%%v=!_v:~0,-1!"
|
||||
)
|
||||
)
|
||||
rem Set ARCH to 32 as a gross assumption and check for x86_64 status
|
||||
set ARCH=32
|
||||
if /i "%PROCESSOR_ARCHITECTURE%" == "AMD64" set "ARCH=64"
|
||||
set "SEVEN_ZIP=%bin%\7-Zip\7z.exe"
|
||||
set "CON=%bin%\ConEmu\ConEmu.exe"
|
||||
set "FASTCOPY=%bin%\FastCopy\FastCopy.exe"
|
||||
set "POWERSHELL=%systemroot%\system32\WindowsPowerShell\v1.0\powershell.exe"
|
||||
set "PYTHON=%bin%\Python\x32\python.exe"
|
||||
if %ARCH% equ 64 (
|
||||
set "CON=%bin%\ConEmu\ConEmu64.exe"
|
||||
set "FASTCOPY=%bin%\FastCopy\FastCopy64.exe"
|
||||
set "PYTHON=%bin%\Python\x64\python.exe"
|
||||
)
|
||||
|
||||
:UpdateTitle
|
||||
rem Sets title using KIT_NAME_FULL from settings\main.py (unless %window_title% already set)
|
||||
if defined window_title (
|
||||
title %window_title%
|
||||
) else (
|
||||
set "window_title=%*"
|
||||
if not defined window_title set "window_title=Launcher"
|
||||
set "window_title=%KIT_NAME_FULL%: %window_title%"
|
||||
title %window_title%
|
||||
)
|
||||
|
||||
:CheckUsage
|
||||
rem Check for empty passed variables
|
||||
if not defined L_TYPE (goto Usage)
|
||||
if not defined L_PATH (goto Usage)
|
||||
if not defined L_ITEM (goto Usage)
|
||||
rem Assume if not "True" then False (i.e. undefine variable)
|
||||
if /i not "%L_ELEV%" == "True" (set "L_ELEV=")
|
||||
if /i not "%L_NCMD%" == "True" (set "L_NCMD=")
|
||||
if /i not "%L__CLI%" == "True" (set "L__CLI=")
|
||||
|
||||
:RelaunchInConEmu
|
||||
set RELOAD_IN_CONEMU=True
|
||||
if defined ConEmuBuild set "RELOAD_IN_CONEMU="
|
||||
if defined L_NCMD set "RELOAD_IN_CONEMU="
|
||||
if "%L_TYPE%" == "Executable" set "RELOAD_IN_CONEMU="
|
||||
if "%L_TYPE%" == "PSScript" set "RELOAD_IN_CONEMU="
|
||||
if "%L_TYPE%" == "PyScript" set "RELOAD_IN_CONEMU="
|
||||
|
||||
if defined RELOAD_IN_CONEMU (
|
||||
set "con_args=-new_console:n"
|
||||
rem If in DEBUG state then force ConEmu to stay open
|
||||
if defined DEBUG (set "con_args=!con_args! -new_console:c")
|
||||
start "" "%CON%" -run ""%~0" %*" !con_args! || goto ErrorUnknown
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
:CheckLaunchType
|
||||
rem Jump to the selected launch type or show usage
|
||||
if /i "%L_TYPE%" == "Executable" (goto LaunchExecutable)
|
||||
if /i "%L_TYPE%" == "Folder" (goto LaunchFolder)
|
||||
if /i "%L_TYPE%" == "Office" (goto LaunchOffice)
|
||||
if /i "%L_TYPE%" == "PSScript" (goto LaunchPSScript)
|
||||
if /i "%L_TYPE%" == "PyScript" (goto LaunchPyScript)
|
||||
if /i "%L_TYPE%" == "QuickBooks" (goto LaunchQuickBooksSetup)
|
||||
goto Usage
|
||||
|
||||
:LaunchExecutable
|
||||
rem Prep
|
||||
call :ExtractOrFindPath || goto ErrorProgramNotFound
|
||||
|
||||
rem Check for 64-bit prog (if running on 64-bit system)
|
||||
set "prog=%_path%\%L_ITEM%"
|
||||
if %ARCH% equ 64 (
|
||||
if exist "%_path%\%L_ITEM:.=64.%" set "prog=%_path%\%L_ITEM:.=64.%"
|
||||
) else (
|
||||
if exist "%_path%\%L_ITEM:.=32.%" set "prog=%_path%\%L_ITEM:.=32.%"
|
||||
)
|
||||
if not exist "%prog%" goto ErrorProgramNotFound
|
||||
|
||||
rem Run
|
||||
popd && pushd "%_path%"
|
||||
if defined L__CLI goto LaunchExecutableCLI
|
||||
if defined L_ELEV (
|
||||
goto LaunchExecutableElev
|
||||
) else (
|
||||
goto LaunchExecutableUser
|
||||
)
|
||||
|
||||
:LaunchExecutableCLI
|
||||
rem Prep
|
||||
set "con_args=-new_console:n"
|
||||
if defined DEBUG (set "con_args=%con_args% -new_console:c")
|
||||
if defined L_ELEV (set "con_args=%con_args% -new_console:a")
|
||||
|
||||
rem Run
|
||||
start "" "%CON%" -run "%prog%" %L_ARGS% %con_args% || goto ErrorUnknown
|
||||
goto Exit
|
||||
|
||||
:LaunchExecutableElev
|
||||
rem Prep
|
||||
call :DeQuote prog
|
||||
call :DeQuote L_ARGS
|
||||
|
||||
rem Create VB script
|
||||
mkdir "%bin%\tmp" 2>nul
|
||||
echo Set UAC = CreateObject^("Shell.Application"^) > "%bin%\tmp\Elevate.vbs"
|
||||
echo UAC.ShellExecute "%prog%", "%L_ARGS%", "", "runas", 1 >> "%bin%\tmp\Elevate.vbs"
|
||||
|
||||
rem Run
|
||||
"%systemroot%\System32\cscript.exe" //nologo "%bin%\tmp\Elevate.vbs" || goto ErrorUnknown
|
||||
goto Exit
|
||||
|
||||
:LaunchExecutableUser
|
||||
rem Run
|
||||
start "" "%prog%" %L_ARGS% || goto ErrorUnknown
|
||||
goto Exit
|
||||
|
||||
:LaunchFolder
|
||||
rem Prep
|
||||
call :ExtractOrFindPath || goto ErrorProgramNotFound
|
||||
|
||||
rem Run
|
||||
start "" "explorer.exe" "%_path%" || goto ErrorUnknown
|
||||
goto Exit
|
||||
|
||||
:LaunchOffice
|
||||
call "%bin%\Scripts\init_client_dir.cmd" /Office
|
||||
set "_odt=False"
|
||||
if %L_PATH% equ 2016 (set "_odt=True")
|
||||
if %L_PATH% equ 2019 (set "_odt=True")
|
||||
if "%_odt%" == "True" (
|
||||
goto LaunchOfficeODT
|
||||
) else (
|
||||
goto LaunchOfficeSetup
|
||||
)
|
||||
|
||||
:LaunchOfficeODT
|
||||
rem Prep
|
||||
set "args=-aoa -bso0 -bse0 -bsp0 -p%ARCHIVE_PASSWORD%"
|
||||
set "config=%L_ITEM%"
|
||||
set "dest=%client_dir%\Office\ODT"
|
||||
set "odt_exe=setup.exe"
|
||||
set "source=%cbin%\_Office.7z"
|
||||
|
||||
rem Extract
|
||||
if not exist "%source%" (goto ErrorODTSourceNotFound)
|
||||
"%SEVEN_ZIP%" e "%source%" %args% -o"%dest%" %odt_exe% %config% || exit /b 1
|
||||
"%systemroot%\System32\ping.exe" -n 2 127.0.0.1>nul
|
||||
|
||||
rem Verify
|
||||
if not exist "%dest%\setup.exe" (goto ErrorODTSourceNotFound)
|
||||
if not exist "%dest%\%config%" (goto ErrorODTSourceNotFound)
|
||||
pushd "%dest%"
|
||||
|
||||
rem Run
|
||||
rem # The line below jumps to ErrorUnknown even when it runs correctly??
|
||||
rem start "" "setup.exe" /configure %L_ITEM% || popd & goto ErrorUnknown
|
||||
rem # Going to assume it extracted correctly and blindly start setup.exe
|
||||
start "" "setup.exe" /configure %config%
|
||||
popd
|
||||
goto Exit
|
||||
|
||||
:LaunchOfficeSetup
|
||||
rem Prep
|
||||
set "fastcopy_args=/cmd=diff /no_ui /auto_close"
|
||||
set "product=%L_PATH%\%L_ITEM%"
|
||||
set "product_name=%L_ITEM%"
|
||||
call :GetBasename product_name || goto ErrorBasename
|
||||
set "source=\\%OFFICE_SERVER_IP%\Office\%product%"
|
||||
set "dest=%client_dir%\Office"
|
||||
|
||||
rem Verify
|
||||
if not exist "%source%" (goto ErrorOfficeSourceNotFound)
|
||||
|
||||
rem Copy
|
||||
echo Copying setup file(s) for %product_name%...
|
||||
start "" /wait "%FASTCOPY%" %fastcopy_args% "%source%" /to="%dest%\"
|
||||
|
||||
rem Run
|
||||
if exist "%dest%\%product_name%\setup.exe" (
|
||||
start "" "%dest%\%product_name%\setup.exe" || goto ErrorUnknown
|
||||
) else if "%product_name:~-3,3%" == "exe" (
|
||||
start "" "%dest%\%product_name%" || goto ErrorUnknown
|
||||
) else if "%product_name:~-3,3%" == "msi" (
|
||||
start "" "%dest%\%product_name%" || goto ErrorUnknown
|
||||
) else (
|
||||
rem Office source not supported by this script
|
||||
goto ErrorOfficeUnsupported
|
||||
)
|
||||
goto Exit
|
||||
|
||||
:LaunchPSScript
|
||||
rem Prep
|
||||
call :ExtractOrFindPath || goto ErrorProgramNotFound
|
||||
set "script=%_path%\%L_ITEM%"
|
||||
set "ps_args=-ExecutionPolicy Bypass -NoProfile"
|
||||
|
||||
rem Verify
|
||||
if not exist "%script%" goto ErrorScriptNotFound
|
||||
|
||||
rem Run
|
||||
popd && pushd "%_path%"
|
||||
if defined L_ELEV (
|
||||
goto LaunchPSScriptElev
|
||||
) else (
|
||||
goto LaunchPSScriptUser
|
||||
)
|
||||
|
||||
:LaunchPSScriptElev
|
||||
rem Prep
|
||||
call :DeQuote script
|
||||
|
||||
rem Create VB script
|
||||
mkdir "%bin%\tmp" 2>nul
|
||||
echo Set UAC = CreateObject^("Shell.Application"^) > "%bin%\tmp\Elevate.vbs"
|
||||
if defined L_NCMD (
|
||||
rem use Powershell's window instead of %CON%
|
||||
echo UAC.ShellExecute "%POWERSHELL%", "%ps_args% -File "%script%"", "", "runas", 3 >> "%bin%\tmp\Elevate.vbs"
|
||||
) else (
|
||||
echo UAC.ShellExecute "%CON%", "-run %POWERSHELL% %ps_args% -File "^"%script%^"" -new_console:n", "", "runas", 1 >> "%bin%\tmp\Elevate.vbs"
|
||||
)
|
||||
|
||||
rem Run
|
||||
"%systemroot%\System32\cscript.exe" //nologo "%bin%\tmp\Elevate.vbs" || goto ErrorUnknown
|
||||
goto Exit
|
||||
|
||||
:LaunchPSScriptUser
|
||||
rem Run
|
||||
if defined L_NCMD (
|
||||
start "" "%POWERSHELL%" %ps_args% -File "%script%" || goto ErrorUnknown
|
||||
) else (
|
||||
start "" "%CON%" -run "%POWERSHELL%" %ps_args% -File "%script%" -new_console:n || goto ErrorUnknown
|
||||
)
|
||||
goto Exit
|
||||
|
||||
:LaunchPyScript
|
||||
rem Prep
|
||||
call :ExtractOrFindPath || goto ErrorProgramNotFound
|
||||
set "script=%_path%\%L_ITEM%"
|
||||
|
||||
rem Verify
|
||||
"%PYTHON%" --version >nul || goto ErrorPythonUnsupported
|
||||
if not exist "%script%" goto ErrorScriptNotFound
|
||||
|
||||
rem Run
|
||||
if defined L_ELEV (
|
||||
goto LaunchPyScriptElev
|
||||
) else (
|
||||
goto LaunchPyScriptUser
|
||||
)
|
||||
|
||||
:LaunchPyScriptElev
|
||||
rem Prep
|
||||
call :DeQuote script
|
||||
|
||||
rem Create VB script
|
||||
mkdir "%bin%\tmp" 2>nul
|
||||
echo Set UAC = CreateObject^("Shell.Application"^) > "%bin%\tmp\Elevate.vbs"
|
||||
if defined L_NCMD (
|
||||
echo UAC.ShellExecute "%PYTHON%", """%script%"" %L_ARGS%", "", "runas", 3 >> "%bin%\tmp\Elevate.vbs"
|
||||
) else (
|
||||
echo UAC.ShellExecute "%CON%", "-run ""%PYTHON%"" ""%script%"" %L_ARGS% -new_console:n", "", "runas", 1 >> "%bin%\tmp\Elevate.vbs"
|
||||
)
|
||||
|
||||
rem Run
|
||||
"%systemroot%\System32\cscript.exe" //nologo "%bin%\tmp\Elevate.vbs" || goto ErrorUnknown
|
||||
goto Exit
|
||||
|
||||
:LaunchPyScriptUser
|
||||
if defined L_NCMD (
|
||||
start "" "%PYTHON%" "%script%" %L_ARGS% || goto ErrorUnknown
|
||||
) else (
|
||||
start "" "%CON%" -run "%PYTHON%" "%script%" %L_ARGS% -new_console:n || goto ErrorUnknown
|
||||
)
|
||||
goto Exit
|
||||
|
||||
:LaunchQuickBooksSetup
|
||||
rem Prep
|
||||
call "%bin%\Scripts\init_client_dir.cmd" /QuickBooks
|
||||
set "fastcopy_args=/cmd=diff /no_ui /auto_close"
|
||||
set "product=%L_PATH%\%L_ITEM%"
|
||||
set "product_name=%L_ITEM%"
|
||||
call :GetBasename product_name || goto ErrorBasename
|
||||
set "source=\\%QUICKBOOKS_SERVER_IP%\QuickBooks\%product%"
|
||||
set "dest=%client_dir%\QuickBooks"
|
||||
|
||||
rem Verify
|
||||
if not exist "%source%" (goto ErrorQuickBooksSourceNotFound)
|
||||
|
||||
rem Copy
|
||||
echo Copying setup file(s) for %L_ITEM%...
|
||||
start "" /wait "%FASTCOPY%" %fastcopy_args% "%source%" /to="%dest%\"
|
||||
|
||||
rem Run
|
||||
if exist "%dest%\%product_name%\Setup.exe" (
|
||||
pushd "%dest%\%product_name%"
|
||||
start "" "%dest%\%product_name%\Setup.exe" || goto ErrorUnknown
|
||||
popd
|
||||
) else (
|
||||
rem QuickBooks source not supported by this script
|
||||
goto ErrorQuickBooksUnsupported
|
||||
)
|
||||
goto Exit
|
||||
|
||||
:Usage
|
||||
echo.
|
||||
echo.Usage (via defined variables):
|
||||
echo. L_TYPE L_PATH L_ITEM L_ARGS
|
||||
echo. Executable Working Dir Program Args [L_7ZIP] [L_ELEV] [L__CLI]
|
||||
echo. Folder Folder '.' [L_7ZIP]
|
||||
echo. Office Year Product [L_7ZIP]
|
||||
echo. PSScript Scripts Script [L_7ZIP] [L_ELEV] [L_NCMD]
|
||||
echo. PyScript Scripts Script Args [L_7ZIP] [L_ELEV] [L_NCMD]
|
||||
echo. QuickBooks Year Product [L_7ZIP]
|
||||
echo.
|
||||
echo.L_7ZIP: Extra arguments for 7-Zip (in the :ExtractCBin label)
|
||||
echo.L_ELEV: Elevate to run as Admin
|
||||
echo.L_NCMD: Do not run script inside ConEmu (i.e. use the native window)
|
||||
echo.L__CLI: Run executable in ConEmu
|
||||
echo.
|
||||
goto Abort
|
||||
|
||||
:: Functions ::
|
||||
:DeQuote
|
||||
rem Code taken from http://ss64.com/nt/syntax-dequote.html
|
||||
if not defined %1 (@exit /b 1)
|
||||
for /f "delims=" %%a in ('echo %%%1%%') do set %1=%%~a
|
||||
@exit /b 0
|
||||
|
||||
:ExtractCBin
|
||||
rem Extract %cbin% archive into %bin%
|
||||
echo Extracting "%L_PATH%"...
|
||||
set "source=%cbin%\%L_PATH%.7z"
|
||||
set "dest=%bin%\%L_PATH%"
|
||||
set "args=-aos -bso0 -bse0 -bsp0 -p%ARCHIVE_PASSWORD%"
|
||||
if defined DEBUG (set "args=-aos -p%ARCHIVE_PASSWORD%")
|
||||
"%SEVEN_ZIP%" x "%source%" %args% -o"%dest%" %L_7ZIP% || exit /b 1
|
||||
ping.exe -n 2 127.0.0.1>nul
|
||||
exit /b 0
|
||||
|
||||
:FindBin
|
||||
rem Checks the current directory and all parents for the ".bin" folder
|
||||
rem NOTE: Has not been tested for UNC paths
|
||||
set bin=
|
||||
pushd "%~dp0"
|
||||
:FindBinInner
|
||||
if exist ".bin" (goto FindBinDone)
|
||||
if "%~d0\" == "%cd%" (popd & @exit /b 1)
|
||||
cd ..
|
||||
goto FindBinInner
|
||||
:FindBinDone
|
||||
set "bin=%cd%\.bin"
|
||||
set "cbin=%cd%\.cbin"
|
||||
popd
|
||||
@exit /b 0
|
||||
|
||||
:GetBasename
|
||||
rem Loop over passed variable to remove all text left of the last '\' character
|
||||
rem NOTE: This function should be called as 'call :GetBasename VarName || goto ErrorBasename' to catch variables that become empty.
|
||||
for /f "delims=" %%a in ('echo %%%1%%') do (set "_tmp=%%~a")
|
||||
:GetBasenameInner
|
||||
set "_tmp=%_tmp:*\=%"
|
||||
if not defined _tmp (@exit /b 1)
|
||||
if not "%_tmp%" == "%_tmp:*\=%" (goto GetBasenameInner)
|
||||
:GetBasenameDone
|
||||
set "%1=%_tmp%"
|
||||
@exit /b 0
|
||||
|
||||
:ExtractOrFindPath
|
||||
rem Test L_PATH in the following order:
|
||||
rem 1: %cbin%\L_PATH.7z (which will be extracted to %bin%\L_PATH)
|
||||
rem 2: %bin%\L_PATH
|
||||
rem 3. %L_PATH% (i.e. treat L_PATH as an absolute path)
|
||||
rem NOTE: This function should be called as 'call :ExtractOrFindPath || goto ErrorProgramNotFound' to catch invalid paths.
|
||||
set _path=
|
||||
if exist "%cbin%\%L_PATH%.7z" (
|
||||
call :ExtractCBin
|
||||
) else if exist "%cbin%\%L_PATH%\%L_ITEM:~0,-4%.7z" (
|
||||
call :ExtractCBin
|
||||
)
|
||||
if exist "%bin%\%L_PATH%" (set "_path=%bin%\%L_PATH%")
|
||||
if not defined _path (set "_path=%L_PATH%")
|
||||
rem Raise error if path is still not available
|
||||
if not exist "%_path%" (exit /b 1)
|
||||
exit /b 0
|
||||
|
||||
:: Errors ::
|
||||
:ErrorBasename
|
||||
echo.
|
||||
echo ERROR: GetBasename resulted in an empty variable.
|
||||
goto Abort
|
||||
|
||||
:ErrorNoBin
|
||||
echo.
|
||||
echo ERROR: ".bin" folder not found.
|
||||
goto Abort
|
||||
|
||||
:ErrorODTSourceNotFound
|
||||
echo.
|
||||
echo ERROR: Office Deployment Tool source not found.
|
||||
goto Abort
|
||||
|
||||
:ErrorOfficeSourceNotFound
|
||||
echo.
|
||||
echo ERROR: Office source "%L_ITEM%" not found.
|
||||
goto Abort
|
||||
|
||||
:ErrorOfficeUnsupported
|
||||
rem Source is not an executable nor is a folder with a setup.exe file inside. Open explorer to local setup file(s) instead.
|
||||
echo.
|
||||
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.
|
||||
goto Abort
|
||||
|
||||
:ErrorQuickBooksUnsupported
|
||||
rem Source is not an executable nor is a folder with a setup.exe file inside. Open explorer to local setup file(s) instead.
|
||||
echo.
|
||||
echo ERROR: QuickBooks version not supported by this script.
|
||||
start "" "explorer.exe" "%client_dir%\QuickBooks"
|
||||
goto Abort
|
||||
|
||||
:ErrorProgramNotFound
|
||||
echo.
|
||||
echo ERROR: Program "%prog%" not found.
|
||||
goto Abort
|
||||
|
||||
:ErrorScriptNotFound
|
||||
echo.
|
||||
echo ERROR: Script "%script%" not found.
|
||||
goto Abort
|
||||
|
||||
:ErrorUnknown
|
||||
echo.
|
||||
echo ERROR: Unknown error encountered.
|
||||
goto Abort
|
||||
|
||||
:Abort
|
||||
rem Handle color theme for both the native console and ConEmu
|
||||
if defined ConEmuBuild (
|
||||
color c4
|
||||
) else (
|
||||
color 4e
|
||||
)
|
||||
echo Aborted.
|
||||
echo.
|
||||
echo DETAILS: L_TYPE: %L_TYPE%
|
||||
echo. L_PATH: %L_PATH%
|
||||
echo. L_ITEM: %L_ITEM%
|
||||
echo. L_ARGS: %L_ARGS%
|
||||
echo. L_7ZIP: %L_7ZIP%
|
||||
echo. L_ELEV: %L_ELEV%
|
||||
echo. L_NCMD: %L_NCMD%
|
||||
echo. L__CLI: %L__CLI%
|
||||
echo. CON: %CON%
|
||||
echo. DEBUG: %DEBUG%
|
||||
echo. PYTHON: %PYTHON%
|
||||
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 07
|
||||
goto Exit
|
||||
|
||||
:: Cleanup and exit ::
|
||||
:Exit
|
||||
popd
|
||||
endlocal
|
||||
exit /b %errorlevel%
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
:: WizardKit: Launcher Script ::
|
||||
::
|
||||
:: This script works by setting env variables and then calling Launch.cmd
|
||||
:: which inherits the variables. This bypasses batch file argument parsing
|
||||
:: which is awful.
|
||||
@echo off
|
||||
|
||||
:Init
|
||||
setlocal EnableDelayedExpansion
|
||||
title WizardKit: Launcher
|
||||
call :CheckFlags %*
|
||||
call :FindBin
|
||||
call :SetTitle Launcher
|
||||
|
||||
:Optional
|
||||
:: This section is for any work that needs done before launching L_ITEM
|
||||
rem EXTRA_CODE
|
||||
|
||||
:DefineLaunch
|
||||
:: See %bin%\Scripts\Launch.cmd for details under :Usage label
|
||||
set L_TYPE=
|
||||
set L_PATH=
|
||||
set L_ITEM=
|
||||
set L_ARGS=
|
||||
set L__CLI=
|
||||
set L_7ZIP=
|
||||
set L_ELEV=
|
||||
set L_NCMD=
|
||||
|
||||
:::::::::::::::::::::::::::::::::::::::::::
|
||||
:: Do not edit anything below this line! ::
|
||||
:::::::::::::::::::::::::::::::::::::::::::
|
||||
|
||||
:LaunchPrep
|
||||
rem Verifies the environment before launching item
|
||||
if not defined bin (goto ErrorNoBin)
|
||||
if not exist "%bin%\Scripts\Launch.cmd" (goto ErrorLaunchCMDMissing)
|
||||
|
||||
:Launch
|
||||
rem Calls the Launch.cmd script using the variables defined above
|
||||
call "%bin%\Scripts\Launch.cmd" || goto ErrorLaunchCMD
|
||||
goto Exit
|
||||
|
||||
:: Functions ::
|
||||
:CheckFlags
|
||||
rem Loops through all arguments to check for accepted flags
|
||||
set DEBUG=
|
||||
for %%f in (%*) do (
|
||||
if /i "%%f" == "/DEBUG" (@echo on & set "DEBUG=/DEBUG")
|
||||
)
|
||||
@exit /b 0
|
||||
|
||||
:FindBin
|
||||
rem Checks the current directory and all parents for the ".bin" folder
|
||||
rem NOTE: Has not been tested for UNC paths
|
||||
set bin=
|
||||
pushd "%~dp0"
|
||||
:FindBinInner
|
||||
if exist ".bin" (goto FindBinDone)
|
||||
if "%~d0\" == "%cd%" (popd & @exit /b 1)
|
||||
cd ..
|
||||
goto FindBinInner
|
||||
:FindBinDone
|
||||
set "bin=%cd%\.bin"
|
||||
set "cbin=%cd%\.cbin"
|
||||
popd
|
||||
@exit /b 0
|
||||
|
||||
:SetTitle
|
||||
rem Sets title using KIT_NAME_FULL from wk\cfg\main.py
|
||||
set "SETTINGS=%bin%\Scripts\wk\cfg\main.py"
|
||||
for /f "tokens=* usebackq" %%f in (`findstr KIT_NAME_FULL "%SETTINGS%"`) do (
|
||||
set "_v=%%f"
|
||||
set "_v=!_v:*'=!"
|
||||
set "KIT_NAME_FULL=!_v:~0,-1!"
|
||||
)
|
||||
set "window_title=%*"
|
||||
if not defined window_title set "window_title=Launcher"
|
||||
set "window_title=%KIT_NAME_FULL%: %window_title%"
|
||||
title %window_title%
|
||||
@exit /b 0
|
||||
|
||||
:: Errors ::
|
||||
:ErrorLaunchCMD
|
||||
echo.
|
||||
echo ERROR: Launch.cmd did not run correctly. Try using the /DEBUG flag?
|
||||
goto Abort
|
||||
|
||||
:ErrorLaunchCMDMissing
|
||||
echo.
|
||||
echo ERROR: Launch.cmd script not found.
|
||||
goto Abort
|
||||
|
||||
:ErrorNoBin
|
||||
echo.
|
||||
echo ERROR: ".bin" folder not found.
|
||||
goto Abort
|
||||
|
||||
:Abort
|
||||
color 4e
|
||||
echo Aborted.
|
||||
echo.
|
||||
echo Press any key to exit...
|
||||
pause>nul
|
||||
color
|
||||
rem Set errorlevel to 1 by calling color incorrectly
|
||||
color 00
|
||||
goto Exit
|
||||
|
||||
:: Cleanup and exit ::
|
||||
:Exit
|
||||
endlocal
|
||||
exit /b %errorlevel%
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
# WizardKit: Scripts #
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WizardKit: Apple fan speed tool
|
||||
|
||||
SMCPATH="/sys/devices/platform/applesmc.768"
|
||||
SET_MAX="True"
|
||||
|
||||
function usage {
|
||||
echo "Usage: $(basename "$0") auto|max"
|
||||
echo " e.g. $(basename "$0") max"
|
||||
}
|
||||
|
||||
# Set mode
|
||||
case $1 in
|
||||
auto)
|
||||
SET_MAX="False";;
|
||||
max)
|
||||
SET_MAX="True";;
|
||||
*)
|
||||
usage
|
||||
exit 1;;
|
||||
esac
|
||||
|
||||
if [[ -e "$SMCPATH" ]]; then
|
||||
if [[ "$SET_MAX" == "True" ]]; then
|
||||
# Set fans to max RPM
|
||||
for fan in $SMCPATH/fan*max; do
|
||||
echo '1' | sudo tee ${fan:0:-4}_manual > /dev/null
|
||||
cat $fan | sudo tee ${fan:0:-4}_output > /dev/null
|
||||
done
|
||||
else
|
||||
# Set fans to auto
|
||||
for fan in $SMCPATH/fan*manual; do
|
||||
echo '0' | sudo tee $fan > /dev/null
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
"""WizardKit: Auto Repair Tool"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
from typing import Any
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
# Classes
|
||||
REBOOT_STR = wk.ui.ansi.color_string('Reboot', 'YELLOW')
|
||||
class MenuEntry():
|
||||
"""Simple class to allow cleaner code below."""
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
function: str | None = None,
|
||||
selected: bool = True,
|
||||
**kwargs):
|
||||
self.name: str = name
|
||||
self.details: dict[str, Any] = {
|
||||
'Function': function,
|
||||
'Selected': selected,
|
||||
**kwargs,
|
||||
}
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
BASE_MENUS = {
|
||||
'Groups': {
|
||||
'Backup Settings': (
|
||||
MenuEntry('Enable RegBack', 'auto_enable_regback'),
|
||||
MenuEntry('Enable System Restore', 'auto_system_restore_enable'),
|
||||
MenuEntry('Set System Restore Size', 'auto_system_restore_set_size'),
|
||||
MenuEntry('Create System Restore', 'auto_system_restore_create'),
|
||||
MenuEntry('Backup Browsers', 'auto_backup_browser_profiles'),
|
||||
MenuEntry('Backup Power Plans', 'auto_backup_power_plans'),
|
||||
MenuEntry('Reset Power Plans', 'auto_reset_power_plans'),
|
||||
MenuEntry('Set Custom Power Plan', 'auto_set_custom_power_plan'),
|
||||
MenuEntry('Backup Registry', 'auto_backup_registry'),
|
||||
),
|
||||
'Windows Repairs': (
|
||||
MenuEntry('Disable Windows Updates', 'auto_windows_updates_disable'),
|
||||
MenuEntry('Reset Windows Updates', 'auto_windows_updates_reset'),
|
||||
MenuEntry(REBOOT_STR, 'auto_reboot'),
|
||||
MenuEntry('CHKDSK', 'auto_chkdsk'),
|
||||
MenuEntry('DISM RestoreHealth', 'auto_dism'),
|
||||
MenuEntry('SFC Scan', 'auto_sfc'),
|
||||
MenuEntry('Clear Proxy Settings', 'auto_reset_proxy'),
|
||||
MenuEntry('Disable Pending Renames', 'auto_disable_pending_renames'),
|
||||
MenuEntry('Registry Repairs', 'auto_repair_registry'),
|
||||
MenuEntry('Reset UAC', 'auto_restore_uac_defaults'),
|
||||
MenuEntry('Reset Windows Policies', 'auto_reset_windows_policies'),
|
||||
),
|
||||
'Malware Cleanup': (
|
||||
MenuEntry('BleachBit', 'auto_bleachbit'),
|
||||
MenuEntry('HitmanPro', 'auto_hitmanpro'),
|
||||
MenuEntry('KVRT', 'auto_kvrt'),
|
||||
MenuEntry('Windows Defender', 'auto_microsoft_defender'),
|
||||
MenuEntry('Remove Custom Power Plan', 'auto_remove_power_plan'),
|
||||
MenuEntry(REBOOT_STR, 'auto_reboot'),
|
||||
),
|
||||
'Manual Steps': (
|
||||
MenuEntry('AdwCleaner', 'auto_adwcleaner'),
|
||||
MenuEntry('Bulk Crap Uninstaller', 'auto_bcuninstaller'),
|
||||
MenuEntry('Enable Windows Updates', 'auto_windows_updates_enable'),
|
||||
),
|
||||
},
|
||||
'Options': (
|
||||
MenuEntry('Kill Explorer', selected=False),
|
||||
MenuEntry('Run AVRemover (once)'),
|
||||
MenuEntry('Run RKill'),
|
||||
MenuEntry('Sync Clock'),
|
||||
MenuEntry('Use Autologon', selected=False),
|
||||
),
|
||||
'Actions': (
|
||||
MenuEntry('Load Preset'),
|
||||
MenuEntry('Options'),
|
||||
MenuEntry('Start', Separator=True),
|
||||
MenuEntry('Quit'),
|
||||
),
|
||||
}
|
||||
PRESETS = {
|
||||
'Default': { # Will be expanded at runtime using BASE_MENUS
|
||||
'Options': (
|
||||
'Run RKill',
|
||||
'Sync Clock',
|
||||
),
|
||||
},
|
||||
'Custom': {}, # Will remain empty at runtime
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
wk.repairs.win.run_auto_repairs(BASE_MENUS, PRESETS)
|
||||
except KeyboardInterrupt:
|
||||
wk.ui.cli.abort()
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
"""WizardKit: Auto System Setup Tool"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
from typing import Any
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
# Classes
|
||||
class MenuEntry():
|
||||
"""Simple class to allow cleaner code below."""
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
function: str | None = None,
|
||||
selected: bool = True,
|
||||
**kwargs):
|
||||
self.name: str = name
|
||||
self.details: dict[str, Any] = {
|
||||
'Function': function,
|
||||
'Selected': selected,
|
||||
**kwargs,
|
||||
}
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
BASE_MENUS = {
|
||||
'Groups': {
|
||||
'Backup Settings': (
|
||||
MenuEntry('Backup Browsers', 'auto_backup_browser_profiles'),
|
||||
MenuEntry('Backup Power Plans', 'auto_backup_power_plans'),
|
||||
MenuEntry('Reset Power Plans', 'auto_reset_power_plans'),
|
||||
MenuEntry('Set Custom Power Plan', 'auto_set_custom_power_plan'),
|
||||
),
|
||||
'Install Software': (
|
||||
MenuEntry('Winget', 'auto_install_winget'),
|
||||
MenuEntry('Firefox', 'auto_install_firefox'),
|
||||
MenuEntry('LibreOffice', 'auto_install_libreoffice', selected=False),
|
||||
MenuEntry('Open Shell', 'auto_install_open_shell'),
|
||||
MenuEntry('Software Bundle', 'auto_install_software_bundle'),
|
||||
MenuEntry('Software Upgrades', 'auto_install_software_upgrades'),
|
||||
MenuEntry('Visual C++ Runtimes', 'auto_install_vcredists'),
|
||||
),
|
||||
'Configure System': (
|
||||
MenuEntry('Open Shell', 'auto_config_open_shell'),
|
||||
MenuEntry('Disable Password Expiration', 'auto_disable_password_expiration'),
|
||||
MenuEntry('Enable BSoD MiniDumps', 'auto_enable_bsod_minidumps'),
|
||||
MenuEntry('Enable RegBack', 'auto_enable_regback'),
|
||||
MenuEntry('Enable System Restore', 'auto_system_restore_enable'),
|
||||
MenuEntry('Set System Restore Size', 'auto_system_restore_set_size'),
|
||||
MenuEntry('Enable Windows Updates', 'auto_windows_updates_enable'),
|
||||
MenuEntry('Windows Activation', 'auto_activate_windows'),
|
||||
MenuEntry('Windows Explorer', 'auto_config_explorer'),
|
||||
MenuEntry(r'Windows\Temp Fix', 'auto_windows_temp_fix'),
|
||||
MenuEntry('Configure Browsers', 'auto_config_browsers'),
|
||||
MenuEntry('Create System Restore', 'auto_system_restore_create'),
|
||||
),
|
||||
'System Information': (
|
||||
MenuEntry('AIDA64 Report', 'auto_export_aida64_report'),
|
||||
MenuEntry('Backup Registry', 'auto_backup_registry'),
|
||||
),
|
||||
'System Summary': (
|
||||
MenuEntry('Operating System', 'auto_show_os_name'),
|
||||
MenuEntry('Windows Activation', 'auto_show_os_activation'),
|
||||
MenuEntry('Secure Boot', 'auto_show_secure_boot_status'),
|
||||
MenuEntry('Installed RAM', 'auto_show_installed_ram'),
|
||||
MenuEntry('Storage Status', 'auto_show_storage_status'),
|
||||
MenuEntry('Virus Protection', 'auto_show_installed_antivirus'),
|
||||
MenuEntry('Partitions 4K Aligned', 'auto_show_4k_alignment_check'),
|
||||
),
|
||||
'Run Programs': (
|
||||
MenuEntry('Device Manager', 'auto_open_device_manager'),
|
||||
MenuEntry('HWiNFO Sensors', 'auto_open_hwinfo_sensors'),
|
||||
MenuEntry('Microsoft Store Updates', 'auto_open_microsoft_store_updates'),
|
||||
MenuEntry('Snappy Driver Installer', 'auto_open_snappy_driver_installer_origin'),
|
||||
MenuEntry('Windows Activation', 'auto_open_windows_activation'),
|
||||
MenuEntry('Windows Updates', 'auto_open_windows_updates'),
|
||||
MenuEntry('XMPlay', 'auto_open_xmplay'),
|
||||
),
|
||||
},
|
||||
'Actions': (
|
||||
MenuEntry('Load Preset'),
|
||||
MenuEntry('Start', Separator=True),
|
||||
MenuEntry('Quit'),
|
||||
),
|
||||
}
|
||||
PRESETS = {
|
||||
'Default': {}, # Will be built at runtime using BASE_MENUS
|
||||
'Additional User': {
|
||||
'Configure System': (
|
||||
'Configure Browsers',
|
||||
'Open Shell',
|
||||
'uBlock Origin',
|
||||
'Enable BSoD MiniDumps',
|
||||
'Enable RegBack',
|
||||
'Enable System Restore',
|
||||
'Set System Restore Size',
|
||||
'Enable Windows Updates',
|
||||
'Windows Explorer',
|
||||
),
|
||||
'Install Software': (
|
||||
'Firefox', # Needed to handle profile upgrade nonsense
|
||||
),
|
||||
'Run Programs': (
|
||||
'Microsoft Store Updates',
|
||||
),
|
||||
'System Summary': (
|
||||
'Operating System',
|
||||
'Windows Activation',
|
||||
'Secure Boot',
|
||||
'Installed RAM',
|
||||
'Storage Status',
|
||||
'Virus Protection',
|
||||
'Partitions 4K Aligned',
|
||||
),
|
||||
},
|
||||
'Hardware': {
|
||||
'Configure System': (
|
||||
'Enable BSoD MiniDumps',
|
||||
'Enable RegBack',
|
||||
'Enable System Restore',
|
||||
'Set System Restore Size',
|
||||
'Enable Windows Updates',
|
||||
),
|
||||
'System Information': (
|
||||
'Backup Registry',
|
||||
),
|
||||
'System Summary': (
|
||||
'Operating System',
|
||||
'Windows Activation',
|
||||
'Secure Boot',
|
||||
'Installed RAM',
|
||||
'Storage Status',
|
||||
'Virus Protection',
|
||||
'Partitions 4K Aligned',
|
||||
),
|
||||
'Run Programs': (
|
||||
'Device Manager',
|
||||
'HWiNFO Sensors',
|
||||
'XMPlay',
|
||||
),
|
||||
},
|
||||
'Verify': {
|
||||
'Configure System': (
|
||||
'Enable BSoD MiniDumps',
|
||||
'Enable RegBack',
|
||||
'Enable System Restore',
|
||||
'Set System Restore Size',
|
||||
'Enable Windows Updates',
|
||||
'Windows Explorer',
|
||||
),
|
||||
'System Summary': (
|
||||
'Operating System',
|
||||
'Windows Activation',
|
||||
'Secure Boot',
|
||||
'Installed RAM',
|
||||
'Storage Status',
|
||||
'Virus Protection',
|
||||
'Installed Office',
|
||||
'Partitions 4K Aligned',
|
||||
),
|
||||
},
|
||||
'Custom': {}, # Will remain empty at runtime
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
wk.setup.win.run_auto_setup(BASE_MENUS, PRESETS)
|
||||
except KeyboardInterrupt:
|
||||
wk.ui.cli.abort()
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""WizardKit: Build UFD Tool"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
wk.kit.ufd.build_ufd()
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
"""WizardKit: Build Kit (Windows)."""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
wk.kit.build.build_kit()
|
||||
except KeyboardInterrupt:
|
||||
wk.ui.cli.abort()
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# WizardKit: Check Antivirus
|
||||
|
||||
#Requires -Version 3.0
|
||||
if (Test-Path Env:\DEBUG) {
|
||||
Set-PSDebug -Trace 1
|
||||
}
|
||||
$Host.UI.RawUI.WindowTitle = "WizardKit: Check Antivirus"
|
||||
$Host.UI.RawUI.BackgroundColor = "black"
|
||||
$Host.UI.RawUI.ForegroundColor = "white"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
# Main
|
||||
Get-CimInstance -Namespace "root\SecurityCenter2" -ClassName AntivirusProduct | select displayName,productState | ConvertTo-Json
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# WizardKit: Check Partition Alignment
|
||||
|
||||
#Requires -Version 3.0
|
||||
if (Test-Path Env:\DEBUG) {
|
||||
Set-PSDebug -Trace 1
|
||||
}
|
||||
$Host.UI.RawUI.WindowTitle = "WizardKit: Check Partition Alignment"
|
||||
$Host.UI.RawUI.BackgroundColor = "black"
|
||||
$Host.UI.RawUI.ForegroundColor = "white"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
# Main
|
||||
Get-CimInstance -Query "Select * from Win32_DiskPartition" | select Name,Size,StartingOffset | ConvertTo-Json
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WizardKit: ddrescue TUI Launcher
|
||||
|
||||
__OS_NAME="$(uname -s)"
|
||||
if [[ "$__OS_NAME" == "Darwin" ]]; then
|
||||
__OS_NAME="macOS"
|
||||
fi
|
||||
__NOTICE="This script is not fully supported under $__OS_NAME!
|
||||
|
||||
Limitations:
|
||||
Map files are saved to a RAM disk so you can't resume after a restart.
|
||||
Only whole devices are supported.
|
||||
|
||||
Press Enter to continue..."
|
||||
|
||||
# Check if running under Linux
|
||||
if [[ "$__OS_NAME" != "Linux" ]]; then
|
||||
echo "${__NOTICE}"
|
||||
read -r _dontcare
|
||||
fi
|
||||
|
||||
source launch-in-tmux
|
||||
|
||||
SESSION_NAME="ddrescue-tui"
|
||||
WINDOW_NAME="ddrescue TUI"
|
||||
TMUX_CMD="ddrescue-tui.py"
|
||||
|
||||
launch_in_tmux "$@"
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""WizardKit: ddrescue TUI"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
wk.clone.ddrescue.main()
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# WizardKit: Disable Password Expiration (Local Accounts)
|
||||
|
||||
#Requires -Version 3.0
|
||||
if (Test-Path Env:\DEBUG) {
|
||||
Set-PSDebug -Trace 1
|
||||
}
|
||||
$Host.UI.RawUI.WindowTitle = "Disable Password Expiration"
|
||||
$Host.UI.RawUI.BackgroundColor = "black"
|
||||
$Host.UI.RawUI.ForegroundColor = "white"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
# Main
|
||||
Get-LocalUser | Set-LocalUser -PasswordNeverExpires $true
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WizardKit: "echo" text to screen and "hold" by waiting for user input
|
||||
|
||||
function usage {
|
||||
echo "Usage: $(basename "$0") \"text\""
|
||||
echo " e.g. $(basename "$0") \"Some text to show\""
|
||||
}
|
||||
|
||||
echo -en "$@" && read -r __dont_care
|
||||
exit 0
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
"""WizardKit: Embedded Python helper.
|
||||
|
||||
This saves the keystrokes needed to fix the path and import wk. To use:
|
||||
python.exe -i embedded_python_env.py
|
||||
"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import pickle
|
||||
import wk
|
||||
|
||||
|
||||
# Functions
|
||||
def load_state():
|
||||
with open('debug/state.pickle', 'rb') as f:
|
||||
return pickle.load(f)
|
||||
|
||||
|
||||
# Main
|
||||
wk.ui.cli.print_colored(
|
||||
(wk.cfg.main.KIT_NAME_FULL, ': ', 'Debug Console'),
|
||||
('GREEN', None, 'YELLOW'),
|
||||
sep='',
|
||||
)
|
||||
print('')
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
"""WizardKit: Export Bitlocker Tool"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import wk
|
||||
|
||||
wk.os.win.export_bitlocker_info()
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# WizardKit: Get RAW disks
|
||||
|
||||
Get-Disk | Where-Object {$_.PartitionStyle -eq "RAW"} | Select FriendlyName,Size,PartitionStyle | ConvertTo-JSON
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WizardKit: HW Diagnostics Launcher
|
||||
|
||||
source launch-in-tmux
|
||||
|
||||
SESSION_NAME="hw-diags"
|
||||
WINDOW_NAME="Hardware Diagnostics"
|
||||
TMUX_CMD="hw-diags.py"
|
||||
|
||||
launch_in_tmux "$@"
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""WizardKit: Hardware Diagnostics"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
wk.hw.diags.main()
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
|
||||
BLUE='\033[34m'
|
||||
CLEAR='\033[0m'
|
||||
IFS=$'\n'
|
||||
|
||||
# Check if running under Linux
|
||||
os_name="$(uname -s)"
|
||||
if [[ "$os_name" == "Darwin" ]]; then
|
||||
os_name="macOS"
|
||||
fi
|
||||
if [[ "$os_name" != "Linux" ]]; then
|
||||
echo "This script is not supported under $os_name." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# List devices
|
||||
for line in $(lsblk -do NAME,TRAN,SIZE,VENDOR,MODEL,SERIAL); do
|
||||
if [[ "${line:0:4}" == "NAME" ]]; then
|
||||
echo -e "${BLUE}${line}${CLEAR}"
|
||||
else
|
||||
echo "${line}"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
# List loopback devices
|
||||
if [[ "$(losetup -l | wc -l)" > 0 ]]; then
|
||||
for line in $(losetup -lO NAME,PARTSCAN,RO,BACK-FILE); do
|
||||
if [[ "${line:0:4}" == "NAME" ]]; then
|
||||
echo -e "${BLUE}${line}${CLEAR}"
|
||||
else
|
||||
echo "${line}" | sed -r 's#/dev/(loop[0-9]+)#\1 #'
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# List partitions
|
||||
for line in $(lsblk -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT); do
|
||||
if [[ "${line:0:4}" == "NAME" ]]; then
|
||||
echo -e "${BLUE}${line}${CLEAR}"
|
||||
else
|
||||
echo "${line}"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
117
scripts/hw-info
|
|
@ -1,117 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
|
||||
# COLORS
|
||||
CLEAR="\e[0m"
|
||||
RED="\e[31m"
|
||||
GREEN="\e[32m"
|
||||
YELLOW="\e[33m"
|
||||
BLUE="\e[34m"
|
||||
|
||||
function print_in_columns() {
|
||||
string="$1"
|
||||
label="$(echo "$string" | sed -r 's/^\s*(.*:).*/\1/')"
|
||||
value="$(echo "$string" | sed -r 's/^\s*.*:\s*(.*)/\1/')"
|
||||
printf ' %-18s%s\n' "$label" "$value"
|
||||
}
|
||||
|
||||
function print_dmi_value() {
|
||||
name="$1"
|
||||
file="/sys/devices/virtual/dmi/id/$2"
|
||||
value="UNKNOWN"
|
||||
if [[ -e "$file" ]]; then
|
||||
value="$(cat "$file")"
|
||||
fi
|
||||
print_in_columns "$name: $value"
|
||||
}
|
||||
|
||||
# Check if running under Linux
|
||||
os_name="$(uname -s)"
|
||||
if [[ "$os_name" == "Darwin" ]]; then
|
||||
os_name="macOS"
|
||||
fi
|
||||
if [[ "$os_name" != "Linux" ]]; then
|
||||
echo "This script is not supported under $os_name." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# System
|
||||
echo -e "${BLUE}System Information${CLEAR}"
|
||||
print_dmi_value "Vendor" "sys_vendor"
|
||||
print_dmi_value "Name" "product_name"
|
||||
print_dmi_value "Serial" "product_serial"
|
||||
echo ""
|
||||
|
||||
# Motherboard
|
||||
echo -e "${BLUE}Motherboard${CLEAR}"
|
||||
print_dmi_value "Vendor" "board_vendor"
|
||||
print_dmi_value "Name" "board_name"
|
||||
print_dmi_value "Version" "board_version"
|
||||
print_dmi_value "Serial" "board_serial"
|
||||
echo ""
|
||||
|
||||
# BIOS
|
||||
echo -e "${BLUE}BIOS${CLEAR}"
|
||||
print_dmi_value "Vendor" "bios_vendor"
|
||||
print_dmi_value "Version" "bios_version"
|
||||
print_dmi_value "Release Date" "bios_date"
|
||||
echo ""
|
||||
|
||||
# Processor
|
||||
echo -e "${BLUE}Processor${CLEAR}"
|
||||
lscpu | grep -E '^(Arch|CPU.s.|Core|Thread|Model name|Virt)' \
|
||||
| sed -r 's/\(s\)(.*:)/s\1 /' \
|
||||
| sed -r 's/CPUs: /Threads:/' \
|
||||
| sed -r 's/^(.*:) / \1/'
|
||||
echo ""
|
||||
|
||||
# Memory
|
||||
echo -e "${BLUE}Memory${CLEAR}"
|
||||
first_device="True"
|
||||
while read -r line; do
|
||||
if [[ "$line" == "Memory Device" ]]; then
|
||||
if [[ "$first_device" == "True" ]]; then
|
||||
first_device="False"
|
||||
else
|
||||
# Add space between devices
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
print_in_columns "$line"
|
||||
fi
|
||||
done <<< $(sudo dmidecode -t memory \
|
||||
| grep -E '^(Memory Device|\s+(Type|Size|Speed|Manuf.*|Locator|Part Number):)')
|
||||
echo ""
|
||||
|
||||
# Graphics
|
||||
echo -e "${BLUE}Graphics${CLEAR}"
|
||||
lspci | grep 'VGA' | sed -r 's/^.*:/ Device: /' \
|
||||
| sed 's/Intel Corporation/Intel/' \
|
||||
| sed 's/Generation Core Processor Family/Gen/' \
|
||||
| sed 's/Integrated Graphics Controller.*/iGPU/'
|
||||
glxinfo 2>/dev/null | grep 'OpenGL renderer' | sed -r 's/^.*:/ OpenGL Renderer: /' \
|
||||
| sed 's/Mesa DRI //'
|
||||
echo ""
|
||||
|
||||
# Audio
|
||||
echo -e "${BLUE}Audio${CLEAR}"
|
||||
while read -r line; do
|
||||
if [[ "$line" = .*no.soundcards.found.* ]]; then
|
||||
echo " No soundcards found"
|
||||
else
|
||||
print_in_columns "$line"
|
||||
fi
|
||||
done <<< $(aplay -l 2>&1 | grep -Ei '(^card|no soundcards found)' | sed -r 's/.*\[(.*)\].*\[(.*)\].*/\1: \2/')
|
||||
echo ""
|
||||
|
||||
# Network
|
||||
echo -e "${BLUE}Network${CLEAR}"
|
||||
lspci | grep -Ei '(ethernet|network|wireless|wifi)' \
|
||||
| sed -r 's/.*: (.*)$/ \1/'
|
||||
echo ""
|
||||
|
||||
# Drives
|
||||
echo -e "${BLUE}Drives${CLEAR}"
|
||||
hw-drive-info | sed 's/^/ /'
|
||||
echo ""
|
||||
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""WizardKit: Hardware Sensors"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import platform
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Show sensor data on screen."""
|
||||
sensors = wk.hw.sensors.Sensors()
|
||||
if platform.system() == 'Darwin':
|
||||
wk.ui.cli.clear_screen()
|
||||
while True:
|
||||
print('\033[100A', end='')
|
||||
sensors.update_sensor_data()
|
||||
wk.ui.cli.print_report(sensors.generate_report('Current', 'Max'))
|
||||
wk.std.sleep(1)
|
||||
elif platform.system() == 'Linux':
|
||||
proc = wk.exe.run_program(cmd=['mktemp'])
|
||||
sensors.start_background_monitor(
|
||||
out_path=proc.stdout.strip(),
|
||||
exit_on_thermal_limit=False,
|
||||
temp_labels=('Current', 'Max'),
|
||||
)
|
||||
watch_cmd = [
|
||||
'watch',
|
||||
'--color',
|
||||
'--exec',
|
||||
'--no-title',
|
||||
'--interval', '1',
|
||||
'cat',
|
||||
proc.stdout.strip(),
|
||||
]
|
||||
wk.exe.run_program(watch_cmd, check=False, pipe=False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
:: WizardKit: Create client_dir folder(s)
|
||||
|
||||
@echo off
|
||||
if defined DEBUG (@echo on)
|
||||
|
||||
:SafetyCheck
|
||||
if not defined bin (goto Abort)
|
||||
|
||||
:GetDate
|
||||
:: Credit to SS64.com Code taken from http://ss64.com/nt/syntax-getdate.html
|
||||
:: Use WMIC to retrieve date and time in ISO 8601 format.
|
||||
for /f "skip=1 tokens=1-6" %%G in ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') do (
|
||||
if "%%~L"=="" goto s_done
|
||||
set _yyyy=%%L
|
||||
set _mm=00%%J
|
||||
set _dd=00%%G
|
||||
set _hour=00%%H
|
||||
set _minute=00%%I
|
||||
)
|
||||
:s_done
|
||||
:: Pad digits with leading zeros
|
||||
set _mm=%_mm:~-2%
|
||||
set _dd=%_dd:~-2%
|
||||
set _hour=%_hour:~-2%
|
||||
set _minute=%_minute:~-2%
|
||||
set iso_date=%_yyyy%-%_mm%-%_dd%
|
||||
|
||||
:SetVars
|
||||
set "SETTINGS=%bin%\Scripts\wk\cfg\main.py"
|
||||
for /f "tokens=* usebackq" %%f in (`findstr KIT_NAME_SHORT "%SETTINGS%"`) do (
|
||||
set "_v=%%f"
|
||||
set "_v=!_v:*'=!"
|
||||
set "KIT_NAME_SHORT=!_v:~0,-1!"
|
||||
)
|
||||
set "client_dir=%systemdrive%\%KIT_NAME_SHORT%"
|
||||
set "log_dir=%client_dir%\Logs\%iso_date%"
|
||||
|
||||
:Flags
|
||||
set _backups=
|
||||
set _info=
|
||||
set _office=
|
||||
set _quarantine=
|
||||
set _quickbooks=
|
||||
set _transfer=
|
||||
for %%f in (%*) do (
|
||||
if /i "%%f" == "/DEBUG" (@echo on)
|
||||
if /i "%%f" == "/Backups" set _backups=True
|
||||
if /i "%%f" == "/Logs" set _logs=True
|
||||
if /i "%%f" == "/Office" set _office=True
|
||||
if /i "%%f" == "/Quarantine" set _quarantine=True
|
||||
if /i "%%f" == "/QuickBooks" set _quickbooks=True
|
||||
if /i "%%f" == "/Transfer" set _transfer=True
|
||||
)
|
||||
|
||||
:CreateDirs
|
||||
if defined _backups mkdir "%client_dir%\Backups">nul 2>&1
|
||||
if defined _logs (
|
||||
mkdir "%log_dir%\%KIT_NAME_FULL%">nul 2>&1
|
||||
mkdir "%log_dir%\Tools">nul 2>&1)
|
||||
if defined _office mkdir "%client_dir%\Office">nul 2>&1
|
||||
if defined _quarantine mkdir "%client_dir%\Quarantine">nul 2>&1
|
||||
if defined _quickbooks mkdir "%client_dir%\QuickBooks">nul 2>&1
|
||||
if defined _transfer mkdir "%client_dir%\Transfer_%iso_date%">nul 2>&1
|
||||
goto Done
|
||||
|
||||
:Abort
|
||||
color 4e
|
||||
echo Aborted.
|
||||
echo.
|
||||
echo Press any key to exit...
|
||||
pause>nul
|
||||
color
|
||||
rem Set errorlevel to 1 by calling color incorrectly
|
||||
color 00
|
||||
goto Exit
|
||||
|
||||
:Done
|
||||
goto Exit
|
||||
|
||||
:Exit
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
# WizardKit: Install winget (if needed)
|
||||
|
||||
#Requires -Version 3.0
|
||||
if (Test-Path Env:\DEBUG) {
|
||||
Set-PSDebug -Trace 1
|
||||
}
|
||||
$Host.UI.RawUI.WindowTitle = "WizardKit: Winget installer"
|
||||
$Host.UI.RawUI.BackgroundColor = "black"
|
||||
$Host.UI.RawUI.ForegroundColor = "white"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
# STATIC VARIABLES
|
||||
$EXIT_OK = 0
|
||||
$EXIT_INSTALLED = 1
|
||||
$EXIT_FAILED_TO_INSTALL = 2
|
||||
|
||||
# Main
|
||||
$NeedsInstalled = $false
|
||||
try {
|
||||
$_ = $(winget --version)
|
||||
}
|
||||
catch {
|
||||
$NeedsInstalled = $true
|
||||
}
|
||||
|
||||
# Install
|
||||
if (! $NeedsInstalled) {
|
||||
exit $EXIT_INSTALLED
|
||||
}
|
||||
try {
|
||||
Add-AppxPackage -ErrorAction Stop -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe
|
||||
}
|
||||
catch {
|
||||
exit $EXIT_FAILED_TO_INSTALL
|
||||
}
|
||||
|
||||
exit $EXIT_OK
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## Monitor journal log for data recovery related events
|
||||
|
||||
echo -e 'Monitoring journal output...\n'
|
||||
journalctl -kf \
|
||||
| grep -Ei --color=always 'ata|nvme|scsi|sd[a..z]+|usb|comreset|critical|error'
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WizardKit: TMUX Launcher
|
||||
|
||||
# Live macOS env workaround
|
||||
tmux_args=()
|
||||
if [[ -e "/.wk-live-macos" ]]; then
|
||||
tmux_args=(-f "/etc/tmux.conf" -S "/Volumes/RAM_Disk/.tmux.socket")
|
||||
fi
|
||||
|
||||
function ask() {
|
||||
while :; do
|
||||
read -p "$1 [Y/N] " -r answer
|
||||
if echo "$answer" | grep -Eiq '^(y|yes|sure)$'; then
|
||||
return 0
|
||||
elif echo "$answer" | grep -Eiq '^(n|no|nope)$'; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
function err () {
|
||||
echo "$0:" "$@" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
function launch_in_tmux() {
|
||||
# Check for required vars
|
||||
[[ -n "${SESSION_NAME:-}" ]] || return $(err "Required variable missing (SESSION_NAME)")
|
||||
[[ -n "${WINDOW_NAME:-}" ]] || return $(err "Required variable missing (WINDOW_NAME)")
|
||||
[[ -n "${TMUX_CMD:-}" ]] || return $(err "Required variable missing (TMUX_CMD)")
|
||||
|
||||
# Check for running session
|
||||
if tmux "${tmux_args[@]}" list-session 2>&1 | grep -q "$SESSION_NAME"; then
|
||||
echo "WARNING: tmux session $SESSION_NAME already exists."
|
||||
echo ""
|
||||
if ask "Connect to current session?"; then
|
||||
if [[ -n "${TMUX:-}" ]]; then
|
||||
# Running inside TMUX, switch to session
|
||||
tmux "${tmux_args[@]}" switch-client -t "$SESSION_NAME"
|
||||
if ! jobs %% >/dev/null 2>&1; then
|
||||
# No running jobs, try exiting abandoned tmux session
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
# Running outside TMUX, attach to session
|
||||
tmux "${tmux_args[@]}" attach-session -t "$SESSION_NAME"
|
||||
fi
|
||||
return 0
|
||||
elif ask "Kill current session and start new session?"; then
|
||||
tmux "${tmux_args[@]}" kill-session -t "$SESSION_NAME" || \
|
||||
die "Failed to kill session: $SESSION_NAME"
|
||||
else
|
||||
echo "Aborted."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start session
|
||||
if [[ -n "${TMUX:-}" ]]; then
|
||||
# Running inside TMUX, save current session/window names
|
||||
ORIGINAL_SESSION_NAME="$(tmux "${tmux_args[@]}" display-message -p '#S')"
|
||||
ORIGINAL_WINDOW_NAME="$(tmux "${tmux_args[@]}" display-message -p '#W')"
|
||||
tmux "${tmux_args[@]}" rename-session "$SESSION_NAME"
|
||||
tmux "${tmux_args[@]}" rename-window "$WINDOW_NAME"
|
||||
"$TMUX_CMD" "$@"
|
||||
# Restore previous session/window names
|
||||
tmux "${tmux_args[@]}" rename-session "${ORIGINAL_SESSION_NAME}"
|
||||
tmux "${tmux_args[@]}" rename-window "${ORIGINAL_WINDOW_NAME}"
|
||||
else
|
||||
# Running outside TMUX, start/attach to session
|
||||
tmux "${tmux_args[@]}" new-session -s "$SESSION_NAME" -n "$WINDOW_NAME" "$TMUX_CMD" "$@"
|
||||
fi
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
"""WizardKit: Launch Snappy Driver Installer Origin"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
from subprocess import CompletedProcess
|
||||
|
||||
import wk
|
||||
from wk.cfg.net import SDIO_SERVER
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
MOUNT_EXCEPTIONS = (
|
||||
RuntimeError,
|
||||
wk.exe.subprocess.CalledProcessError,
|
||||
)
|
||||
SDIO_LOCAL_PATH = wk.kit.tools.get_tool_path("SDIO", "SDIO")
|
||||
SDIO_REMOTE_PATH = wk.io.get_path_obj(
|
||||
(
|
||||
fr'\\{SDIO_SERVER["Address"]}\{SDIO_SERVER["Share"]}\{SDIO_SERVER["Path"]}'
|
||||
fr'\SDIO{"64" if wk.os.win.ARCH == "64" else ""}.exe'
|
||||
),
|
||||
resolve=False,
|
||||
)
|
||||
|
||||
# Functions
|
||||
def try_again() -> bool:
|
||||
"""Ask to try again or quit."""
|
||||
if wk.ui.cli.ask(' Try again?'):
|
||||
return True
|
||||
if not wk.ui.cli.ask(' Use local version?'):
|
||||
wk.ui.cli.abort()
|
||||
return False
|
||||
|
||||
|
||||
def use_network_sdio() -> bool:
|
||||
"""Try to mount SDIO server."""
|
||||
use_network = False
|
||||
def _mount_server() -> CompletedProcess:
|
||||
print('Connecting to server... (Press CTRL+c to use local copy)')
|
||||
return wk.net.mount_network_share(SDIO_SERVER, read_write=False)
|
||||
|
||||
# Bail early
|
||||
if not SDIO_SERVER['Address']:
|
||||
return use_network
|
||||
|
||||
# Main loop
|
||||
while True:
|
||||
try:
|
||||
proc = _mount_server()
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
except MOUNT_EXCEPTIONS as err:
|
||||
wk.ui.cli.print_error(f' {err}')
|
||||
if not try_again():
|
||||
break
|
||||
else:
|
||||
if proc.returncode == 0:
|
||||
# Network copy available
|
||||
use_network = True
|
||||
break
|
||||
|
||||
# Failed to mount
|
||||
wk.ui.cli.print_error(' Failed to mount server')
|
||||
if not try_again():
|
||||
break
|
||||
|
||||
# Done
|
||||
return use_network
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
wk.ui.cli.set_title(
|
||||
f'{wk.cfg.main.KIT_NAME_FULL}: Snappy Driver Installer Origin Launcher',
|
||||
)
|
||||
log_dir = wk.log.format_log_path(tool=True).parent
|
||||
USE_NETWORK = False
|
||||
|
||||
# Windows 11 workaround
|
||||
if wk.os.win.OS_VERSION == 11:
|
||||
appid_services = ['appid', 'appidsvc', 'applockerfltr']
|
||||
for svc in appid_services:
|
||||
wk.os.win.stop_service(svc)
|
||||
if any([wk.os.win.get_service_status(s) != 'stopped' for s in appid_services]):
|
||||
raise wk.std.GenericWarning('Failed to stop AppID services')
|
||||
|
||||
# Try to mount server
|
||||
try:
|
||||
USE_NETWORK = use_network_sdio()
|
||||
except KeyboardInterrupt:
|
||||
wk.ui.cli.abort()
|
||||
|
||||
# Run SDIO
|
||||
EXE_PATH = SDIO_LOCAL_PATH
|
||||
if USE_NETWORK:
|
||||
EXE_PATH = SDIO_REMOTE_PATH
|
||||
print('Using network copy!')
|
||||
else:
|
||||
print('Using local copy!')
|
||||
cmd = [EXE_PATH, '-log_dir', log_dir]
|
||||
wk.exe.run_program(cmd, check=False, cwd=EXE_PATH.parent)
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#!/bin/env python3
|
||||
#
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
from typing import Any
|
||||
|
||||
CPU_REGEX = re.compile(r'(core|k\d+)temp', re.IGNORECASE)
|
||||
NON_TEMP_REGEX = re.compile(r'^(fan|in|curr)', re.IGNORECASE)
|
||||
|
||||
def get_data() -> dict[Any, Any]:
|
||||
cmd = ('sensors', '-j')
|
||||
data = {}
|
||||
raw_data = []
|
||||
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
args=cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
encoding='utf-8',
|
||||
check=True,
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
return data
|
||||
|
||||
for line in proc.stdout.splitlines():
|
||||
if line.strip() == ',':
|
||||
# Assuming malformatted line caused by missing data
|
||||
continue
|
||||
raw_data.append(line)
|
||||
|
||||
try:
|
||||
data = json.loads('\n'.join(raw_data))
|
||||
except json.JSONDecodeError:
|
||||
# Still broken, just return the empty dict
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
def get_max_temp(data) -> str:
|
||||
cpu_temps = []
|
||||
max_cpu_temp = '??° C'
|
||||
for adapter, sources in data.items():
|
||||
if not CPU_REGEX.search(adapter):
|
||||
continue
|
||||
sources.pop('Adapter', None)
|
||||
|
||||
for labels in sources.values():
|
||||
for label, temp in sorted(labels.items()):
|
||||
if 'input' not in label or NON_TEMP_REGEX.search(label):
|
||||
continue
|
||||
cpu_temps.append(temp)
|
||||
|
||||
# Format data
|
||||
if cpu_temps:
|
||||
max_cpu_temp = int(max(cpu_temps))
|
||||
max_cpu_temp = f'{max_cpu_temp:02d}° C'
|
||||
|
||||
# Done
|
||||
return max_cpu_temp
|
||||
|
||||
if __name__ == '__main__':
|
||||
sensor_data = get_data()
|
||||
print(get_max_temp(sensor_data))
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""WizardKit: Mount all volumes"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import sys
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
# Functions
|
||||
def main() -> None:
|
||||
"""Mount all volumes and show results."""
|
||||
wk.ui.cli.print_standard(f'{wk.cfg.main.KIT_NAME_FULL}: Volume mount tool')
|
||||
wk.ui.cli.print_standard(' ')
|
||||
|
||||
# Mount volumes and get report
|
||||
wk.ui.cli.print_standard('Mounting volumes...')
|
||||
wk.os.linux.mount_volumes()
|
||||
report = wk.os.linux.build_volume_report()
|
||||
|
||||
# Show results
|
||||
wk.ui.cli.print_info('Results')
|
||||
wk.ui.cli.print_report(report)
|
||||
|
||||
# GUI mode
|
||||
if 'gui' in sys.argv:
|
||||
wk.ui.cli.pause('Press Enter to exit...')
|
||||
wk.exe.popen_program(['nohup', 'thunar', '/media'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if wk.std.PLATFORM != 'Linux':
|
||||
os_name = wk.std.PLATFORM.replace('Darwin', 'macOS')
|
||||
wk.ui.cli.print_error(f'This script is not supported under {os_name}.')
|
||||
wk.ui.cli.abort()
|
||||
try:
|
||||
main()
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""WizardKit: Mount Backup Shares"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
# Functions
|
||||
def main() -> None:
|
||||
"""Attempt to mount backup shares and print report."""
|
||||
wk.ui.cli.print_info('Mounting Backup Shares')
|
||||
report = wk.net.mount_backup_shares()
|
||||
for line in report:
|
||||
color = 'GREEN'
|
||||
line = f' {line}'
|
||||
if 'Failed' in line:
|
||||
color = 'RED'
|
||||
elif 'Already' in line:
|
||||
color = 'YELLOW'
|
||||
print(wk.ansi.color_string(line, color))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WizardKit: RAW image mounting tool
|
||||
|
||||
set -o errexit
|
||||
set -o errtrace
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
LOOPDEV="$(losetup -f)"
|
||||
|
||||
function usage {
|
||||
echo "Usage: $(basename "$0") [image]"
|
||||
echo " e.g. $(basename "$0") HDD.dd"
|
||||
}
|
||||
|
||||
if [[ -f "${1:-}" ]]; then
|
||||
sudo losetup -P "${LOOPDEV}" "${1:-}"
|
||||
sleep 1
|
||||
if [[ -b "${LOOPDEV}p1" ]]; then
|
||||
# losetup detected partitions
|
||||
for dev in "${LOOPDEV}p"*; do
|
||||
udevil mount -o ro "${dev}" || true
|
||||
done
|
||||
else
|
||||
# losetup did not detect partitions, attempt whole image
|
||||
udevil mount -o ro "${LOOPDEV}" || true
|
||||
fi
|
||||
else
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## WizardKit: MS Word content search tool
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import wk
|
||||
|
||||
# STATIC VARIABLES
|
||||
SCANDIR = os.getcwd()
|
||||
USAGE = '''Usage: {script} <search-terms>...
|
||||
e.g. {script} "Book Title" "Keyword" "etc"
|
||||
|
||||
This script will search all doc/docx files below the current directory for
|
||||
the search-terms provided (case-insensitive).'''.format(script=__file__)
|
||||
REGEX_DOC_FILES = re.compile(r'\.docx?$', re.IGNORECASE)
|
||||
|
||||
|
||||
def scan_for_docs(path):
|
||||
for entry in os.scandir(path):
|
||||
if entry.is_dir(follow_symlinks=False):
|
||||
yield from scan_for_docs(entry.path)
|
||||
elif entry.is_file and REGEX_DOC_FILES.search(entry.name):
|
||||
yield entry
|
||||
|
||||
|
||||
def scan_file(file_path, search):
|
||||
match = False
|
||||
try:
|
||||
if entry.name.lower().endswith('.docx'):
|
||||
result = wk.exe.run_program(['unzip', '-p', entry.path])
|
||||
else:
|
||||
# Assuming .doc
|
||||
result = wk.exe.run_program(['antiword', entry.path])
|
||||
out = result.stdout.decode()
|
||||
match = re.search(search, out, re.IGNORECASE)
|
||||
except Exception:
|
||||
# Ignore errors since files may be corrupted
|
||||
pass
|
||||
|
||||
return entry.path if match else None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
# Prep
|
||||
wk.ui.cli.clear_screen()
|
||||
terms = [re.sub(r'\s+', r'\s*', t) for t in sys.argv[1:]]
|
||||
search = '({})'.format('|'.join(terms))
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
# Print usage
|
||||
wk.ui.cli.print_standard(USAGE)
|
||||
else:
|
||||
matches = []
|
||||
for entry in scan_for_docs(SCANDIR):
|
||||
matches.append(scan_file(entry.path, search))
|
||||
# Strip None values (i.e. non-matching entries)
|
||||
matches = [m for m in matches if m]
|
||||
if matches:
|
||||
wk.ui.cli.print_success('Found {} {}:'.format(
|
||||
len(matches),
|
||||
'Matches' if len(matches) > 1 else 'Match'))
|
||||
for match in matches:
|
||||
wk.ui.cli.print_standard(match)
|
||||
else:
|
||||
wk.ui.cli.print_error('No matches found.')
|
||||
|
||||
# Done
|
||||
wk.ui.cli.print_standard('\nDone.')
|
||||
#pause("Press Enter to exit...")
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/env bash
|
||||
#
|
||||
## Enable numlock if no battery is detected
|
||||
## Credit: https://wiki.archlinux.org/title/Activating_numlock_on_bootup#With_systemd_service
|
||||
|
||||
if ! compgen -G "/sys/class/power_supply/BAT*" >/dev/null; then
|
||||
for tty in /dev/tty{1..6}; do
|
||||
/usr/bin/setleds -D +num < "$tty"
|
||||
done
|
||||
fi
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WizardKit: Update pacman settings to usage in live sessions
|
||||
|
||||
# Disable custom repo (used at build-time)
|
||||
sudo sed -i -r "s/^(\[custom\])/#\1/" /etc/pacman.conf
|
||||
sudo sed -i -r "s/^(SigLevel = Optional TrustAll)/#\1/" /etc/pacman.conf
|
||||
sudo sed -i -r "s/^(Server = )/#\1/" /etc/pacman.conf
|
||||
|
||||
# Disable signature checks
|
||||
sudo sed -i -r "s/^SigLevel.*/SigLevel = Never/" /etc/pacman.conf
|
||||
|
||||
# Init Pacman keyring
|
||||
sudo systemctl start pacman-init.service
|
||||
|
||||
# Refresh package databases and install packages (if provided)
|
||||
sudo pacman -Sy "$@"
|
||||
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
[tool.ruff.per-file-ignores]
|
||||
# Init files
|
||||
"wk/__init__.py" = ["F401"]
|
||||
"wk/cfg/__init__.py" = ["F401"]
|
||||
"wk/clone/__init__.py" = ["F401"]
|
||||
"wk/hw/__init__.py" = ["F401"]
|
||||
"wk/kit/__init__.py" = ["F401"]
|
||||
"wk/os/__init__.py" = ["F401"]
|
||||
"wk/repairs/__init__.py" = ["F401"]
|
||||
"wk/setup/__init__.py" = ["F401"]
|
||||
"wk/ui/__init__.py" = ["F401"]
|
||||
|
||||
# Long lines
|
||||
"wk/borrowed/acpi.py" = ["E501", "F841"]
|
||||
"wk/cfg/ddrescue.py" = ["E501"]
|
||||
"wk/cfg/hw.py" = ["E501"]
|
||||
"wk/cfg/launchers.py" = ["E501"]
|
||||
"wk/cfg/setup.py" = ["E501"]
|
||||
"wk/cfg/sources.py" = ["E501"]
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WizardKit: Volume remount tool
|
||||
|
||||
if ! mount | grep -q "$1"; then
|
||||
echo "ERROR: Can't remount $1"
|
||||
sleep 2s
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEVICE=$(mount | grep "$1" | cut -d' ' -f1)
|
||||
|
||||
# Remount read-write
|
||||
echo "Remounting: $DEVICE"
|
||||
udevil umount $DEVICE
|
||||
if udevil mount $DEVICE; then
|
||||
echo "Done"
|
||||
else
|
||||
echo "Failed"
|
||||
fi
|
||||
exit 0
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
|
||||
# Magic numbers:
|
||||
## Width: | 20 | term_x | 20 | 180 (conky) | 20 |
|
||||
## Height: | 24 | 10 (titlebar) | term_y | 24 | 30 (Tint2) |
|
||||
## X Offset: 20 - 5 (shadow?)
|
||||
## Y Offset: 24 - 5 (shadow?)
|
||||
conky_width=180
|
||||
gap_x=20
|
||||
gap_y=24
|
||||
picom_shadow=5
|
||||
tint2_height=30
|
||||
titlebar_height=10
|
||||
|
||||
source ~/.screen_data
|
||||
|
||||
if [[ "${dpi}" -ge 192 ]]; then
|
||||
conky_width=360
|
||||
gap_x=40
|
||||
gap_y=48
|
||||
picom_shadow=5
|
||||
tint2_height=60
|
||||
titlebar_height=20
|
||||
fi
|
||||
|
||||
offset_x=$(echo "$gap_x - $picom_shadow" | bc)
|
||||
offset_y=$(echo "$gap_y - $picom_shadow" | bc)
|
||||
term_width="$(echo "$width_px - ($gap_x * 3) - $conky_width" | bc)"
|
||||
term_height="$(echo "$height_px - ($gap_y * 2) - $titlebar_height - $tint2_height" | bc)"
|
||||
|
||||
sleep 0.1s
|
||||
wmctrl -r :ACTIVE: -e "0,$offset_x,$offset_y,$term_width,$term_height" && "$@"
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
|
||||
wmctrl -r:ACTIVE: -b toggle,maximized_vert,maximized_horz && "$@"
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""WizardKit: Unmount Backup Shares"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
# Functions
|
||||
def main() -> None:
|
||||
"""Attempt to mount backup shares and print report."""
|
||||
wk.ui.cli.print_info('Unmounting Backup Shares')
|
||||
report = wk.net.unmount_backup_shares()
|
||||
for line in report:
|
||||
color = 'GREEN'
|
||||
line = f' {line}'
|
||||
if 'Not mounted' in line:
|
||||
color = 'YELLOW'
|
||||
print(wk.ui.ansi.color_string(line, color))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except SystemExit:
|
||||
raise
|
||||
except: # noqa: E722
|
||||
wk.ui.cli.major_exception()
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
"""WizardKit: Upload Logs"""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
import pytz
|
||||
import requests
|
||||
|
||||
import wk
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG_DIR = pathlib.Path('~/Logs').expanduser().resolve()
|
||||
PLATFORM = wk.std.PLATFORM.replace('Darwin', 'macOS')
|
||||
TIMEZONE = pytz.timezone(wk.cfg.main.LINUX_TIME_ZONE)
|
||||
NOW = datetime.datetime.now(tz=TIMEZONE)
|
||||
|
||||
|
||||
# Safety check
|
||||
if PLATFORM not in ('macOS', 'Linux'):
|
||||
raise OSError(f'This script is not supported under {PLATFORM}')
|
||||
|
||||
|
||||
# Functions
|
||||
def main() -> None:
|
||||
"""Upload logs for review."""
|
||||
lines = []
|
||||
try_and_print = wk.ui.cli.TryAndPrint()
|
||||
|
||||
# Set log
|
||||
wk.log.update_log_path(dest_name='Upload-Logs', timestamp=True)
|
||||
|
||||
# Instructions
|
||||
wk.ui.cli.print_success(f'{wk.cfg.main.KIT_NAME_FULL}: Upload Logs')
|
||||
wk.ui.cli.print_standard('')
|
||||
wk.ui.cli.print_standard('Please state the reason for the review.')
|
||||
wk.ui.cli.print_info(' End note with an empty line.')
|
||||
wk.ui.cli.print_standard('')
|
||||
|
||||
# Get reason note
|
||||
while True:
|
||||
text = wk.ui.cli.input_text('> ')
|
||||
if not text:
|
||||
lines.append('')
|
||||
break
|
||||
lines.append(text)
|
||||
with open(f'{LOG_DIR}/__reason__.txt', 'a') as _f:
|
||||
_f.write('\n'.join(lines))
|
||||
|
||||
# Compress and upload logs
|
||||
result = try_and_print.run(
|
||||
message='Uploading logs...',
|
||||
function=upload_log_dir,
|
||||
reason='Review',
|
||||
)
|
||||
if not result['Failed']:
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def upload_log_dir(reason='Testing') -> None:
|
||||
"""Upload compressed log_dir to the crash server."""
|
||||
server = wk.cfg.net.CRASH_SERVER
|
||||
dest = pathlib.Path(f'~/{reason}_{NOW.strftime("%Y-%m-%dT%H%M%S%z")}.txz')
|
||||
dest = dest.expanduser().resolve()
|
||||
|
||||
# Compress LOG_DIR (relative to parent dir)
|
||||
os.chdir(LOG_DIR.parent)
|
||||
cmd = ['tar', 'caf', dest.name, LOG_DIR.name]
|
||||
wk.exe.run_program(cmd, check=False)
|
||||
|
||||
# Upload compressed data
|
||||
url = f'{server["Url"]}/{dest.name}'
|
||||
result = requests.put(
|
||||
url,
|
||||
data=dest.read_bytes(),
|
||||
headers=server['Headers'],
|
||||
auth=(server['User'], server['Pass']),
|
||||
)
|
||||
|
||||
# Check result
|
||||
if not result.ok:
|
||||
raise wk.std.GenericError('Failed to upload logs')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/zsh
|
||||
#
|
||||
## watch-like utility
|
||||
|
||||
WATCH_FILE="${1}"
|
||||
|
||||
while :; do
|
||||
echo -n "\e[100A"
|
||||
cat "${WATCH_FILE}"
|
||||
sleep 1s
|
||||
done
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WizardKit: Debug Launcher
|
||||
|
||||
python3 -i /usr/local/bin/wk_debug.py
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## Wizard Kit: Wrapper for logout, reboot, & poweroff
|
||||
|
||||
set -o errexit
|
||||
set -o errtrace
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
# Functions
|
||||
function linux_power_cmd() {
|
||||
case "${1:-x}" in
|
||||
poweroff)
|
||||
sudo systemctl poweroff;;
|
||||
reboot)
|
||||
sudo systemctl reboot;;
|
||||
*)
|
||||
openbox --exit;;
|
||||
esac
|
||||
}
|
||||
|
||||
function macos_power_cmd() {
|
||||
case "${1:-x}" in
|
||||
poweroff)
|
||||
shutdown -h now;;
|
||||
reboot)
|
||||
shutdown -r now;;
|
||||
*)
|
||||
exit;;
|
||||
esac
|
||||
}
|
||||
|
||||
# "Main"
|
||||
if [[ -e "/.wk-live-macos" ]]; then
|
||||
# Flush write cache
|
||||
sync
|
||||
|
||||
# Perform requested action
|
||||
macos_power_cmd "${1:-x}"
|
||||
else
|
||||
# Unmount filesystems
|
||||
find /media -maxdepth 1 -mindepth 1 -type d \
|
||||
-exec udevil umount "{}" \;
|
||||
|
||||
# Flush write cache
|
||||
sudo sync
|
||||
|
||||
# Perform requested action
|
||||
linux_power_cmd "${1:-x}"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
"""WizardKit: wk module init"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
from sys import stderr, version_info
|
||||
|
||||
from . import cfg
|
||||
from . import clone
|
||||
from . import debug
|
||||
from . import exe
|
||||
from . import graph
|
||||
from . import hw
|
||||
from . import io
|
||||
from . import kit
|
||||
from . import log
|
||||
from . import net
|
||||
from . import os
|
||||
from . import repairs
|
||||
from . import setup
|
||||
from . import std
|
||||
from . import ui
|
||||
|
||||
|
||||
# Check env
|
||||
if version_info < (3, 10):
|
||||
# Unsupported
|
||||
raise RuntimeError(
|
||||
'This package is unsupported on Python '
|
||||
f'{version_info.major}.{version_info.minor}'
|
||||
)
|
||||
|
||||
# Init
|
||||
try:
|
||||
log.start()
|
||||
except UserWarning as err:
|
||||
print(err, file=stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
import sys
|
||||
|
||||
# Code borrowed from https://github.com/aeruder/get_win8key
|
||||
|
||||
if sys.platform.startswith('win32'):
|
||||
import ctypes
|
||||
import ctypes.wintypes
|
||||
|
||||
def EnumAcpiTables():
|
||||
#returns a list of the names of the ACPI tables on this system
|
||||
FirmwareTableProviderSignature=ctypes.wintypes.DWORD(1094930505)
|
||||
pFirmwareTableBuffer=ctypes.create_string_buffer(0)
|
||||
BufferSize=ctypes.wintypes.DWORD(0)
|
||||
#http://msdn.microsoft.com/en-us/library/windows/desktop/ms724259
|
||||
EnumSystemFirmwareTables=ctypes.WinDLL("Kernel32").EnumSystemFirmwareTables
|
||||
ret=EnumSystemFirmwareTables(FirmwareTableProviderSignature, pFirmwareTableBuffer, BufferSize)
|
||||
pFirmwareTableBuffer=None
|
||||
pFirmwareTableBuffer=ctypes.create_string_buffer(ret)
|
||||
BufferSize.value=ret
|
||||
ret2=EnumSystemFirmwareTables(FirmwareTableProviderSignature, pFirmwareTableBuffer, BufferSize)
|
||||
return [pFirmwareTableBuffer.value[i:i+4] for i in range(0, len(pFirmwareTableBuffer.value), 4)]
|
||||
|
||||
def GetAcpiTable(table):
|
||||
#returns raw contents of ACPI table
|
||||
#http://msdn.microsoft.com/en-us/library/windows/desktop/ms724379x
|
||||
tableID = 0
|
||||
for b in reversed(table):
|
||||
tableID = (tableID << 8) + b
|
||||
GetSystemFirmwareTable=ctypes.WinDLL("Kernel32").GetSystemFirmwareTable
|
||||
FirmwareTableProviderSignature=ctypes.wintypes.DWORD(1094930505)
|
||||
FirmwareTableID=ctypes.wintypes.DWORD(int(tableID))
|
||||
pFirmwareTableBuffer=ctypes.create_string_buffer(0)
|
||||
BufferSize=ctypes.wintypes.DWORD(0)
|
||||
ret = GetSystemFirmwareTable(FirmwareTableProviderSignature, FirmwareTableID, pFirmwareTableBuffer, BufferSize)
|
||||
pFirmwareTableBuffer=None
|
||||
pFirmwareTableBuffer=ctypes.create_string_buffer(ret)
|
||||
BufferSize.value=ret
|
||||
ret2 = GetSystemFirmwareTable(FirmwareTableProviderSignature, FirmwareTableID, pFirmwareTableBuffer, BufferSize)
|
||||
return pFirmwareTableBuffer.raw
|
||||
elif sys.platform.startswith('linux'):
|
||||
import os
|
||||
TABLE_ROOT = b'/sys/firmware/acpi/tables'
|
||||
def EnumAcpiTables():
|
||||
return os.listdir(TABLE_ROOT)
|
||||
def GetAcpiTable(table):
|
||||
with open(os.path.join(TABLE_ROOT, table), 'rb') as o:
|
||||
return o.read()
|
||||
else:
|
||||
raise NotImplementedError('acpi support only implemented for linux and win32')
|
||||
|
||||
def FindAcpiTable(table):
|
||||
#checks if specific ACPI table exists and returns True/False
|
||||
tables = EnumAcpiTables()
|
||||
if table in tables:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
"""WizardKit: cfg module init"""
|
||||
|
||||
from . import ddrescue
|
||||
from . import hw
|
||||
from . import launchers
|
||||
from . import log
|
||||
from . import main
|
||||
from . import music
|
||||
from . import net
|
||||
from . import repairs
|
||||
from . import setup
|
||||
from . import sources
|
||||
from . import ufd
|
||||
from . import windows_builds
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
"""WizardKit: Config - ddrescue"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
|
||||
# Layout
|
||||
TMUX_SIDE_WIDTH = 21
|
||||
TMUX_LAYOUT = {
|
||||
'Source': {'height': 2, 'Check': True},
|
||||
'Started': {'width': TMUX_SIDE_WIDTH, 'Check': True},
|
||||
'Progress': {'width': TMUX_SIDE_WIDTH, 'Check': True},
|
||||
}
|
||||
|
||||
# ddrescue
|
||||
AUTO_PASS_THRESHOLDS = {
|
||||
# NOTE: The scrape key is set to infinity to force a break
|
||||
'read-skip': 50,
|
||||
'read-full': 95,
|
||||
'trim': 98,
|
||||
'scrape': float('inf'),
|
||||
}
|
||||
DDRESCUE_MAP_TEMPLATE = '''# Mapfile. Created by {name}
|
||||
0x0 ? 1
|
||||
0x0 {size:#x} ?
|
||||
'''
|
||||
DDRESCUE_SETTINGS = {
|
||||
'Default': {
|
||||
'--binary-prefixes': {'Selected': True, 'Hidden': True, },
|
||||
'--complete-only': {'Selected': True, 'Hidden': True, },
|
||||
'--data-preview': {'Selected': True, 'Value': '5', 'Hidden': True, },
|
||||
'--idirect': {'Selected': True, },
|
||||
'--odirect': {'Selected': True, },
|
||||
'--input-position': {'Selected': False, 'Value': '0', },
|
||||
'--max-error-rate': {'Selected': True, 'Value': '100MiB', },
|
||||
'--max-read-rate': {'Selected': False, 'Value': '1MiB', },
|
||||
'--min-read-rate': {'Selected': True, 'Value': '64KiB', },
|
||||
'--reopen-on-error': {'Selected': False, },
|
||||
'--retry-passes': {'Selected': True, 'Value': '0', },
|
||||
'--reverse': {'Selected': False, },
|
||||
'--skip-size': {'Selected': True, 'Value': '0.001,0.02', }, # Percentages of source size
|
||||
'--test-mode': {'Selected': False, },
|
||||
'--timeout': {'Selected': True, 'Value': '30m', },
|
||||
'-vvvv': {'Selected': True, 'Hidden': True, },
|
||||
},
|
||||
'Fast': {
|
||||
'--max-error-rate': {'Selected': True, 'Value': '32MiB', },
|
||||
'--min-read-rate': {'Selected': True, 'Value': '1MiB', },
|
||||
'--timeout': {'Selected': True, 'Value': '5m', },
|
||||
},
|
||||
'Safe': {
|
||||
'--max-read-rate': {'Selected': True, 'Value': '64MiB', },
|
||||
'--min-read-rate': {'Selected': True, 'Value': '1KiB', },
|
||||
'--timeout': {'Selected': False, 'Value': '30m', },
|
||||
},
|
||||
}
|
||||
DDRESCUE_SPECIFIC_PASS_SETTINGS = {
|
||||
'read-skip': ['--no-scrape', '--no-trim', '--cpass=1,2'],
|
||||
'read-full': ['--no-scrape', '--no-trim'],
|
||||
'trim': ['--no-scrape'],
|
||||
}
|
||||
DRIVE_POWEROFF_TIMEOUT = 90
|
||||
PARTITION_TYPES = {
|
||||
'GPT': {
|
||||
'NTFS': 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7', # Basic Data Partition
|
||||
'VFAT': 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7', # Basic Data Partition
|
||||
'EXFAT': 'EBD0A0A2-B9E5-4433-87C0-68B6B72699C7', # Basic Data Partition
|
||||
},
|
||||
'MBR': {
|
||||
'EXFAT': '7',
|
||||
'NTFS': '7',
|
||||
'VFAT': 'b',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
"""WizardKit: Config - Hardware"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import re
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
ATTRIBUTE_COLORS = (
|
||||
# NOTE: Ordered by ascending importance
|
||||
('Warning', 'YELLOW'),
|
||||
('Error', 'RED'),
|
||||
('Maximum', 'PURPLE'),
|
||||
)
|
||||
# NOTE: Force 4K read block size for disks >= 3TB
|
||||
BADBLOCKS_EXTRA_LARGE_DISK = 15 * 1024**4
|
||||
BADBLOCKS_LARGE_DISK = 3 * 1024**4
|
||||
BADBLOCKS_REGEX = re.compile(
|
||||
r'^Pass completed, (\d+) bad blocks found. .(\d+)/(\d+)/(\d+) errors',
|
||||
re.IGNORECASE,
|
||||
)
|
||||
BADBLOCKS_RESULTS_REGEX = re.compile(r'^(.*?)\x08.*\x08(.*)')
|
||||
BADBLOCKS_SKIP_REGEX = re.compile(r'^(Checking|\[)', re.IGNORECASE)
|
||||
CPU_TEMPS = {
|
||||
'Cooling Delta': 25,
|
||||
'Cooling Low Cutoff': 50,
|
||||
'Critical': 100,
|
||||
'Idle Delta': 25,
|
||||
'Idle High': 70,
|
||||
}
|
||||
CPU_TEST_MINUTES = 7
|
||||
IO_GRAPH_WIDTH = 40
|
||||
IO_ALT_TEST_SIZE_FACTOR = 0.01
|
||||
IO_BLOCK_SIZE = 512 * 1024
|
||||
IO_CHUNK_SIZE = 32 * 1024**2
|
||||
IO_MINIMUM_TEST_SIZE = 10 * 1024**3
|
||||
IO_RATE_REGEX = re.compile(
|
||||
r'(?P<bytes>\d+) bytes.* (?P<seconds>\S+) s(?:,|ecs )',
|
||||
)
|
||||
KEY_NVME = 'nvme_smart_health_information_log'
|
||||
KEY_SMART = 'ata_smart_attributes'
|
||||
KNOWN_DISK_ATTRIBUTES = {
|
||||
# NVMe
|
||||
'critical_warning': {'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||
'media_errors': {'Blocking': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||
'power_on_hours': {'Blocking': False, 'Warning': 17532, 'Error': 26298, 'Maximum': 100000,},
|
||||
'unsafe_shutdowns': {'Blocking': False, 'Warning': 1, 'Error': None, 'Maximum': None, },
|
||||
# SMART
|
||||
5: {'Hex': '05', 'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||
9: {'Hex': '09', 'Blocking': False, 'Warning': 17532, 'Error': 26298, 'Maximum': 100000,},
|
||||
10: {'Hex': '10', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
||||
184: {'Hex': 'B8', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
||||
187: {'Hex': 'BB', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
||||
188: {'Hex': 'BC', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
||||
196: {'Hex': 'C4', 'Blocking': False, 'Warning': 1, 'Error': 10, 'Maximum': 10000, },
|
||||
197: {'Hex': 'C5', 'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||
198: {'Hex': 'C6', 'Blocking': True, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||
199: {'Hex': 'C7', 'Blocking': False, 'Warning': None, 'Error': 1, 'Maximum': None, },
|
||||
201: {'Hex': 'C9', 'Blocking': False, 'Warning': None, 'Error': 1, 'Maximum': 10000, },
|
||||
}
|
||||
KNOWN_DISK_MODELS = {
|
||||
# model_regex: model_attributes
|
||||
r'CT(250|500|1000|2000)MX500SSD(1|4)': {
|
||||
197: {'Warning': 1, 'Error': 2, 'Note': '(MX500 thresholds)',},
|
||||
},
|
||||
r'MZ(7|N)LN(128|256|512|1T0)HA(HQ|JQ|LR)-000H(1|7)': {
|
||||
# Source: https://www.smartmontools.org/ticket/920
|
||||
201: {'Error': 99, 'PercentageLife': True, 'Note': '(PM871b thresholds)'},
|
||||
},
|
||||
}
|
||||
KNOWN_RAM_VENDOR_IDS = {
|
||||
# https://github.com/hewigovens/hewigovens.github.com/wiki/Memory-vendor-code
|
||||
'0x014F': 'Transcend',
|
||||
'0x2C00': 'Micron',
|
||||
'0x802C': 'Micron',
|
||||
'0x80AD': 'Hynix',
|
||||
'0x80CE': 'Samsung',
|
||||
'0xAD00': 'Hynix',
|
||||
'0xCE00': 'Samsung',
|
||||
}
|
||||
NVME_WARNING_KEYS = (
|
||||
'spare_below_threshold',
|
||||
'reliability_degraded',
|
||||
'volatile_memory_backup_failed',
|
||||
)
|
||||
REGEX_POWER_ON_TIME = re.compile(
|
||||
r'^(\d+)([Hh].*|\s+\(\d+\s+\d+\s+\d+\).*)'
|
||||
)
|
||||
SMART_SELF_TEST_START_TIMEOUT_IN_SECONDS = 120
|
||||
SMC_IDS = {
|
||||
# Sources: https://github.com/beltex/SMCKit/blob/master/SMCKit/SMC.swift
|
||||
# http://www.opensource.apple.com/source/net_snmp/
|
||||
# https://github.com/jedda/OSX-Monitoring-Tools
|
||||
'TA0P': {'CPU Temp': False, 'Source': 'Ambient'},
|
||||
'TA0S': {'CPU Temp': False, 'Source': 'PCIE Slot 1 Ambient'},
|
||||
'TA1P': {'CPU Temp': False, 'Source': 'Ambient'},
|
||||
'TA1S': {'CPU Temp': False, 'Source': 'PCIE Slot 1 PCB'},
|
||||
'TA2S': {'CPU Temp': False, 'Source': 'PCIE Slot 2 Ambient'},
|
||||
'TA3S': {'CPU Temp': False, 'Source': 'PCIE Slot 2 PCB'},
|
||||
'TC0C': {'CPU Temp': True, 'Source': 'CPU Core 1'},
|
||||
'TC0D': {'CPU Temp': True, 'Source': 'CPU Diode'},
|
||||
'TC0H': {'CPU Temp': True, 'Source': 'CPU Heatsink'},
|
||||
'TC0P': {'CPU Temp': True, 'Source': 'CPU Proximity'},
|
||||
'TC1C': {'CPU Temp': True, 'Source': 'CPU Core 2'},
|
||||
'TC1P': {'CPU Temp': True, 'Source': 'CPU Proximity 2'},
|
||||
'TC2C': {'CPU Temp': True, 'Source': 'CPU Core 3'},
|
||||
'TC2P': {'CPU Temp': True, 'Source': 'CPU Proximity 3'},
|
||||
'TC3C': {'CPU Temp': True, 'Source': 'CPU Core 4'},
|
||||
'TC3P': {'CPU Temp': True, 'Source': 'CPU Proximity 4'},
|
||||
'TCAC': {'CPU Temp': True, 'Source': 'CPU core from PCECI'},
|
||||
'TCAH': {'CPU Temp': True, 'Source': 'CPU HeatSink'},
|
||||
'TCBC': {'CPU Temp': True, 'Source': 'CPU B core from PCECI'},
|
||||
'TCBH': {'CPU Temp': True, 'Source': 'CPU HeatSink'},
|
||||
'Te1P': {'CPU Temp': False, 'Source': 'PCIE Ambient'},
|
||||
'Te1S': {'CPU Temp': False, 'Source': 'PCIE slot 1'},
|
||||
'Te2S': {'CPU Temp': False, 'Source': 'PCIE slot 2'},
|
||||
'Te3S': {'CPU Temp': False, 'Source': 'PCIE slot 3'},
|
||||
'Te4S': {'CPU Temp': False, 'Source': 'PCIE slot 4'},
|
||||
'TG0C': {'CPU Temp': False, 'Source': 'Mezzanine GPU Core'},
|
||||
'TG0P': {'CPU Temp': False, 'Source': 'Mezzanine GPU Exhaust'},
|
||||
'TH0P': {'CPU Temp': False, 'Source': 'Drive Bay 0'},
|
||||
'TH1P': {'CPU Temp': False, 'Source': 'Drive Bay 1'},
|
||||
'TH2P': {'CPU Temp': False, 'Source': 'Drive Bay 2'},
|
||||
'TH3P': {'CPU Temp': False, 'Source': 'Drive Bay 3'},
|
||||
'TH4P': {'CPU Temp': False, 'Source': 'Drive Bay 4'},
|
||||
'TM0P': {'CPU Temp': False, 'Source': 'CPU DIMM Exit Ambient'},
|
||||
'Tp0C': {'CPU Temp': False, 'Source': 'PSU1 Inlet Ambient'},
|
||||
'Tp0P': {'CPU Temp': False, 'Source': 'PSU1 Inlet Ambient'},
|
||||
'Tp1C': {'CPU Temp': False, 'Source': 'PSU1 Secondary Component'},
|
||||
'Tp1P': {'CPU Temp': False, 'Source': 'PSU1 Primary Component'},
|
||||
'Tp2P': {'CPU Temp': False, 'Source': 'PSU1 Secondary Component'},
|
||||
'Tp3P': {'CPU Temp': False, 'Source': 'PSU2 Inlet Ambient'},
|
||||
'Tp4P': {'CPU Temp': False, 'Source': 'PSU2 Primary Component'},
|
||||
'Tp5P': {'CPU Temp': False, 'Source': 'PSU2 Secondary Component'},
|
||||
'TS0C': {'CPU Temp': False, 'Source': 'CPU B DIMM Exit Ambient'},
|
||||
}
|
||||
STATUS_COLORS = {
|
||||
'Passed': 'GREEN',
|
||||
'Aborted': 'YELLOW',
|
||||
'N/A': 'YELLOW',
|
||||
'Skipped': 'YELLOW',
|
||||
'Unknown': 'YELLOW',
|
||||
'Working': 'YELLOW',
|
||||
'Denied': 'RED',
|
||||
'ERROR': 'RED',
|
||||
'Failed': 'RED',
|
||||
'TimedOut': 'RED',
|
||||
}
|
||||
TEMP_COLORS = {
|
||||
float('-inf'): 'CYAN',
|
||||
00: 'BLUE',
|
||||
60: 'GREEN',
|
||||
70: 'YELLOW',
|
||||
80: 'ORANGE',
|
||||
90: 'RED',
|
||||
100: 'ORANGE_RED',
|
||||
}
|
||||
TESTSTATION_FILE = '/run/archiso/bootmnt/teststation.name'
|
||||
TEST_MODE_BADBLOCKS_LIMIT = '10000' # Last block to read
|
||||
TEST_MODE_CPU_LIMIT = 0.25 # Number of minutes to test
|
||||
# THRESHOLDS: Rates used to determine HDD/SSD pass/fail
|
||||
THRESH_HDD_MIN = 50 * 1024**2
|
||||
THRESH_HDD_AVG_HIGH = 75 * 1024**2
|
||||
THRESH_HDD_AVG_LOW = 65 * 1024**2
|
||||
THRESH_SSD_MIN = 90 * 1024**2
|
||||
THRESH_SSD_AVG_HIGH = 135 * 1024**2
|
||||
THRESH_SSD_AVG_LOW = 100 * 1024**2
|
||||
# VOLUME THRESHOLDS in percent
|
||||
VOLUME_WARNING_THRESHOLD = 70
|
||||
VOLUME_FAILURE_THRESHOLD = 85
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,287 +0,0 @@
|
|||
"""WizardKit: Config - Launchers (Windows)"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
LAUNCHERS = {
|
||||
r'': { # Root Dir
|
||||
'0) Export BitLocker': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
'L_ITEM': 'export_bitlocker.py',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'1) Auto Repairs': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
'L_ITEM': 'auto_repairs.py',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'2) Store & Windows Updates': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': r'%SystemRoot%\System32',
|
||||
'L_ITEM': 'control.exe',
|
||||
'L_ARGS': 'update',
|
||||
'Extra Code': ['explorer ms-windows-store:updates'],
|
||||
},
|
||||
'3) Snappy Driver Installer Origin': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
'L_ITEM': 'launch_sdio.py',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'4) Auto Setup': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
'L_ITEM': 'auto_setup.py',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
},
|
||||
r'Tools': {
|
||||
'AIDA64': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'AIDA64',
|
||||
'L_ITEM': 'aida64.exe',
|
||||
},
|
||||
'Autoruns (with VirusTotal Scan)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'Sysinternals',
|
||||
'L_ITEM': 'Autoruns.exe',
|
||||
'L_ARGS': '-e',
|
||||
'Extra Code': [
|
||||
r'reg add HKCU\Software\Sysinternals\AutoRuns /v checkvirustotal /t REG_DWORD /d 1 /f >nul',
|
||||
r'reg add HKCU\Software\Sysinternals\AutoRuns /v EulaAccepted /t REG_DWORD /d 1 /f >nul',
|
||||
r'reg add HKCU\Software\Sysinternals\AutoRuns /v shownomicrosoft /t REG_DWORD /d 1 /f >nul',
|
||||
r'reg add HKCU\Software\Sysinternals\AutoRuns /v shownowindows /t REG_DWORD /d 1 /f >nul',
|
||||
r'reg add HKCU\Software\Sysinternals\AutoRuns /v showonlyvirustotal /t REG_DWORD /d 1 /f >nul',
|
||||
r'reg add HKCU\Software\Sysinternals\AutoRuns /v submitvirustotal /t REG_DWORD /d 0 /f >nul',
|
||||
r'reg add HKCU\Software\Sysinternals\AutoRuns /v verifysignatures /t REG_DWORD /d 1 /f >nul',
|
||||
r'reg add HKCU\Software\Sysinternals\AutoRuns\SigCheck /v EulaAccepted /t REG_DWORD /d 1 /f >nul',
|
||||
r'reg add HKCU\Software\Sysinternals\AutoRuns\Streams /v EulaAccepted /t REG_DWORD /d 1 /f >nul',
|
||||
r'reg add HKCU\Software\Sysinternals\AutoRuns\VirusTotal /v VirusTotalTermsAccepted /t REG_DWORD /d 1 /f >nul',
|
||||
],
|
||||
},
|
||||
'BleachBit': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'BleachBit',
|
||||
'L_ITEM': 'bleachbit.exe',
|
||||
},
|
||||
'BlueScreenView': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'BlueScreenView',
|
||||
'L_ITEM': 'BlueScreenView.exe',
|
||||
},
|
||||
'BCUninstaller': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'BCUninstaller',
|
||||
'L_ITEM': 'BCUninstaller.exe',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'ConEmu (as ADMIN)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'ConEmu',
|
||||
'L_ITEM': 'ConEmu.exe',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'ConEmu': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'ConEmu',
|
||||
'L_ITEM': 'ConEmu.exe',
|
||||
},
|
||||
'Debug Console (Command Prompt)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'ConEmu',
|
||||
'L_ITEM': 'ConEmu.exe',
|
||||
'L_ARGS': r'-Dir %bin%\Scripts',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'Debug Console (Python)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'ConEmu',
|
||||
'L_ITEM': 'ConEmu.exe',
|
||||
'L_ARGS': r'-Dir %bin%\Scripts -Run ..\Python\x%ARCH%\python.exe -i embedded_python_env.py',
|
||||
'L_ELEV': 'True',
|
||||
'Extra Code': [
|
||||
'set ARCH=32',
|
||||
'if /i "%PROCESSOR_ARCHITECTURE%" == "AMD64" set "ARCH=64"',
|
||||
],
|
||||
},
|
||||
'Device Cleanup': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'DeviceCleanup',
|
||||
'L_ITEM': 'DeviceCleanup.exe',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'Display Driver Uninstaller': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'DDU',
|
||||
'L_ITEM': 'Display Driver Uninstaller.exe',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'ERUNT': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'erunt',
|
||||
'L_ITEM': 'ERUNT.EXE',
|
||||
'L_ARGS': r'%client_dir%\Backups\Registry\%iso_date% sysreg curuser otherusers',
|
||||
'L_ELEV': 'True',
|
||||
'Extra Code': [
|
||||
r'call "%bin%\Scripts\init_client_dir.cmd" /Logs',
|
||||
],
|
||||
},
|
||||
'Everything': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'Everything',
|
||||
'L_ITEM': 'Everything.exe',
|
||||
'L_ARGS': '-nodb',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'FastCopy (as ADMIN)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'FastCopy',
|
||||
'L_ITEM': 'FastCopy.exe',
|
||||
'L_ARGS': (
|
||||
r' /logfile=%log_dir%\Tools\FastCopy.log'
|
||||
r' /acl'
|
||||
r' /cmd=noexist_only'
|
||||
r' /skip_empty_dir'
|
||||
r' /linkdest'
|
||||
r' /exclude='
|
||||
r'$RECYCLE.BIN;'
|
||||
r'$Recycle.Bin;'
|
||||
r'.AppleDB;'
|
||||
r'.AppleDesktop;'
|
||||
r'.AppleDouble;'
|
||||
r'.com.apple.timemachine.supported;'
|
||||
r'.dbfseventsd;'
|
||||
r'.DocumentRevisions-V100*;'
|
||||
r'.DS_Store;'
|
||||
r'.fseventsd;'
|
||||
r'.PKInstallSandboxManager;'
|
||||
r'.Spotlight*;'
|
||||
r'.SymAV*;'
|
||||
r'.symSchedScanLockxz;'
|
||||
r'.TemporaryItems;'
|
||||
r'.Trash*;'
|
||||
r'.vol;'
|
||||
r'.VolumeIcon.icns;'
|
||||
r'desktop.ini;'
|
||||
r'Desktop?DB;'
|
||||
r'Desktop?DF;'
|
||||
r'hiberfil.sys;'
|
||||
r'lost+found;'
|
||||
r'Network?Trash?Folder;'
|
||||
r'pagefile.sys;'
|
||||
r'Recycled;'
|
||||
r'RECYCLER;'
|
||||
r'System?Volume?Information;'
|
||||
r'Temporary?Items;'
|
||||
r'Thumbs.db'
|
||||
r' /to=%client_dir%\Transfer_%iso_date%\ '
|
||||
),
|
||||
'L_ELEV': 'True',
|
||||
'Extra Code': [
|
||||
r'call "%bin%\Scripts\init_client_dir.cmd" /Logs /Transfer',
|
||||
],
|
||||
},
|
||||
'FastCopy': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'FastCopy',
|
||||
'L_ITEM': 'FastCopy.exe',
|
||||
'L_ARGS': (
|
||||
r' /logfile=%log_dir%\Tools\FastCopy.log'
|
||||
r' /acl'
|
||||
r' /cmd=noexist_only'
|
||||
r' /skip_empty_dir'
|
||||
r' /linkdest'
|
||||
r' /exclude='
|
||||
r'$RECYCLE.BIN;'
|
||||
r'$Recycle.Bin;'
|
||||
r'.AppleDB;'
|
||||
r'.AppleDesktop;'
|
||||
r'.AppleDouble;'
|
||||
r'.com.apple.timemachine.supported;'
|
||||
r'.dbfseventsd;'
|
||||
r'.DocumentRevisions-V100*;'
|
||||
r'.DS_Store;'
|
||||
r'.fseventsd;'
|
||||
r'.PKInstallSandboxManager;'
|
||||
r'.Spotlight*;'
|
||||
r'.SymAV*;'
|
||||
r'.symSchedScanLockxz;'
|
||||
r'.TemporaryItems;'
|
||||
r'.Trash*;'
|
||||
r'.vol;'
|
||||
r'.VolumeIcon.icns;'
|
||||
r'desktop.ini;'
|
||||
r'Desktop?DB;'
|
||||
r'Desktop?DF;'
|
||||
r'hiberfil.sys;'
|
||||
r'lost+found;'
|
||||
r'Network?Trash?Folder;'
|
||||
r'pagefile.sys;'
|
||||
r'Recycled;'
|
||||
r'RECYCLER;'
|
||||
r'System?Volume?Information;'
|
||||
r'Temporary?Items;'
|
||||
r'Thumbs.db'
|
||||
r' /to=%client_dir%\Transfer_%iso_date%\ '
|
||||
),
|
||||
'Extra Code': [
|
||||
r'call "%bin%\Scripts\init_client_dir.cmd" /Logs /Transfer',
|
||||
],
|
||||
},
|
||||
'FurMark': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'FurMark',
|
||||
'L_ITEM': 'FurMark.exe',
|
||||
},
|
||||
'HWiNFO': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'HWiNFO',
|
||||
'L_ITEM': 'HWiNFO.exe',
|
||||
'Extra Code': [
|
||||
r'for %%a in (32 64) do (',
|
||||
r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r' (echo SensorsOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r')',
|
||||
],
|
||||
},
|
||||
'HWiNFO (Sensors)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'HWiNFO',
|
||||
'L_ITEM': 'HWiNFO.exe',
|
||||
'Extra Code': [
|
||||
r'for %%a in (32 64) do (',
|
||||
r' copy /y "%bin%\HWiNFO\general.ini" "%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r' (echo SensorsOnly=1)>>"%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r' (echo SummaryOnly=0)>>"%bin%\HWiNFO\HWiNFO%%a.ini"',
|
||||
r')',
|
||||
],
|
||||
},
|
||||
'Notepad++': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'notepadplusplus',
|
||||
'L_ITEM': 'notepadplusplus.exe',
|
||||
},
|
||||
'PuTTY': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'PuTTY',
|
||||
'L_ITEM': 'PUTTY.EXE',
|
||||
},
|
||||
'WizTree': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'WizTree',
|
||||
'L_ITEM': 'WizTree.exe',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'XMPlay': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'XMPlay',
|
||||
'L_ITEM': 'xmplay.exe',
|
||||
'L_ARGS': r'"%bin%\XMPlay\music.7z"',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
"""WizardKit: Config - Log"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
|
||||
DEBUG = {
|
||||
'level': 'DEBUG',
|
||||
'format': '[%(asctime)s %(levelname)s] [%(name)s.%(funcName)s] %(message)s',
|
||||
'datefmt': '%Y-%m-%d %H%M%S%z',
|
||||
}
|
||||
DEFAULT = {
|
||||
'level': 'INFO',
|
||||
'format': '[%(asctime)s %(levelname)s] %(message)s',
|
||||
'datefmt': '%Y-%m-%d %H%M%z',
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
"""WizardKit: Config - Main
|
||||
|
||||
NOTE: Non-standard formating is used for BASH/BATCH/PYTHON compatibility
|
||||
"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
|
||||
# Features
|
||||
ENABLED_OPEN_LOGS=False
|
||||
ENABLED_TICKET_NUMBERS=False
|
||||
ENABLED_UPLOAD_DATA=False
|
||||
|
||||
# Main Kit
|
||||
ARCHIVE_PASSWORD='Abracadabra'
|
||||
KIT_NAME_FULL='WizardKit'
|
||||
KIT_NAME_SHORT='WK'
|
||||
SUPPORT_MESSAGE='Please let 2Shirt know by opening an issue on Gitea'
|
||||
|
||||
# Text Formatting
|
||||
INDENT=4
|
||||
WIDTH=32
|
||||
|
||||
# Live Linux
|
||||
ROOT_PASSWORD='Abracadabra'
|
||||
TECH_PASSWORD='Abracadabra'
|
||||
|
||||
# Time Zones
|
||||
## See 'timedatectl list-timezones' for valid Linux values
|
||||
## See 'tzutil /l' for valid Windows values
|
||||
LINUX_TIME_ZONE='America/Los_Angeles'
|
||||
WINDOWS_TIME_ZONE='Pacific Standard Time'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
"""WizardKit: Config - Music Sources"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
MUSIC_MOD = (
|
||||
('33432', 'ambrozia.xm'),
|
||||
('33460', 'amigatre.mod'),
|
||||
('34594', 'CHARIOT.S3M'),
|
||||
('34596', 'BUTTERFL.XM'),
|
||||
('34654', 'CTGOBLIN.S3M'),
|
||||
('35151', 'bananasplit.mod'),
|
||||
('35280', 'DEADLOCK.XM'),
|
||||
('38591', 'compo_liam.xm'),
|
||||
('39987', 'crystald.s3m'),
|
||||
('40475', 'ELYSIUM.MOD'),
|
||||
('42146', 'enigma.mod'),
|
||||
('42519', 'GHOST2.MOD'),
|
||||
('42560', 'GSLINGER.MOD'),
|
||||
('42872', 'existing.xm'),
|
||||
('50427', 'nf-stven.xm'),
|
||||
('51549', 'overture.mod'),
|
||||
('54250', 'SATELL.S3M'),
|
||||
('54313', 'realmk.s3m'),
|
||||
('55789', 'scrambld.mod'),
|
||||
('57934', 'spacedeb.mod'),
|
||||
('59344', 'stardstm.mod'),
|
||||
('60395', '2ND_PM.S3M'),
|
||||
('66187', 'external.xm'),
|
||||
('66343', 'beek-substitutionology.it'),
|
||||
('67561', 'radix-unreal_superhero.xm'),
|
||||
('70829', 'inside_out.s3m'),
|
||||
('83779', 'beyond_music.mod'),
|
||||
('104208', 'banana_boat.mod'),
|
||||
('114971', 'tilbury_fair.mod'),
|
||||
('132563', 'ufo_tune.mod'),
|
||||
('135906', 'magnetik_girl.xm'),
|
||||
('140628', 'autumn_in_budapest.xm'),
|
||||
('143198', 'summer_memories_3.xm'),
|
||||
('144405', 'hillbilly_billyboy.xm'),
|
||||
('154795', '4mat_-_eternity.xm'),
|
||||
('155845', 'bookworm.mo3'),
|
||||
('155914', 'battleofsteel.xm'),
|
||||
('158975', '1_channel_moog.it'),
|
||||
('165495', 'trans.s3m'),
|
||||
('168513', 'necros_-_introspection.s3m'),
|
||||
('169628', 'radix_-_feng_shui_schematics.xm'),
|
||||
('175238', 'unknown48_-_twilight.mod'),
|
||||
)
|
||||
MUSIC_SNES = (
|
||||
'actr',
|
||||
'crock',
|
||||
'ct',
|
||||
'dkc',
|
||||
'dkq',
|
||||
'ff6',
|
||||
'fz',
|
||||
'loz3',
|
||||
'mmx',
|
||||
'ptws',
|
||||
'scv4',
|
||||
'sf',
|
||||
'sf2',
|
||||
'sgng',
|
||||
'smk',
|
||||
'smw',
|
||||
'yi',
|
||||
'zamn',
|
||||
)
|
||||
MUSIC_SNES_BAD = {
|
||||
'ct': ['ct-s*', 'ct-v*'],
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
"""WizardKit: Config - Net"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
|
||||
# Servers
|
||||
BACKUP_SERVERS = {
|
||||
#'Server One': {
|
||||
# 'Address': '10.0.0.10',
|
||||
# 'Share': 'Backups',
|
||||
# 'RO-User': 'restore',
|
||||
# 'RO-Pass': 'Abracadabra',
|
||||
# 'RW-User': 'backup',
|
||||
# 'RW-Pass': 'Abracadabra',
|
||||
# },
|
||||
#'Server Two': {
|
||||
# 'Address': 'servertwo.example.com',
|
||||
# 'Share': 'Backups',
|
||||
# 'RO-User': 'restore',
|
||||
# 'RO-Pass': 'Abracadabra',
|
||||
# 'RW-User': 'backup',
|
||||
# 'RW-Pass': 'Abracadabra',
|
||||
# },
|
||||
}
|
||||
CRASH_SERVER = {
|
||||
#'Name': 'CrashServer',
|
||||
#'Url': '',
|
||||
#'User': '',
|
||||
#'Pass': '',
|
||||
#'Headers': {'X-Requested-With': 'XMLHttpRequest'},
|
||||
}
|
||||
SDIO_SERVER = {
|
||||
'Address': '',
|
||||
'Share': '',
|
||||
'Path': '',
|
||||
'RO-User': '',
|
||||
'RO-Pass': '',
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
"""WizardKit: Config - Repairs"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
from wk.cfg.main import KIT_NAME_FULL
|
||||
|
||||
AUTO_REPAIR_DELAY_IN_SECONDS = 3
|
||||
AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs'
|
||||
BLEACH_BIT_CLEANERS = (
|
||||
# Applications
|
||||
'adobe_reader.cache',
|
||||
'adobe_reader.tmp',
|
||||
'amule.temp',
|
||||
'discord.cache',
|
||||
'flash.cache',
|
||||
'gimp.tmp',
|
||||
'google_earth.temporary_files',
|
||||
'gpodder.cache',
|
||||
'hippo_opensim_viewer.cache',
|
||||
'java.cache',
|
||||
'miro.cache',
|
||||
'openofficeorg.cache',
|
||||
'pidgin.cache',
|
||||
'seamonkey.cache',
|
||||
'secondlife_viewer.Cache',
|
||||
'silverlight.temp',
|
||||
'slack.cache',
|
||||
'smartftp.cache',
|
||||
'thunderbird.cache',
|
||||
'vuze.cache',
|
||||
'vuze.temp',
|
||||
'windows_media_player.cache',
|
||||
'winrar.temp',
|
||||
'yahoo_messenger.cache',
|
||||
'zoom.cache',
|
||||
# Browsers
|
||||
'brave.cache',
|
||||
'brave.session',
|
||||
'chromium.cache',
|
||||
'chromium.search_engines',
|
||||
'chromium.session',
|
||||
'firefox.cache',
|
||||
'firefox.session_restore',
|
||||
'google_chrome.cache',
|
||||
'google_chrome.session',
|
||||
'internet_explorer.cache',
|
||||
'microsoft_edge.cache',
|
||||
'microsoft_edge.session',
|
||||
'opera.cache',
|
||||
'opera.session',
|
||||
'palemoon.cache',
|
||||
'palemoon.session_restore',
|
||||
'safari.cache',
|
||||
'waterfox.cache',
|
||||
'waterfox.session_restore',
|
||||
# System
|
||||
'system.clipboard',
|
||||
'system.tmp',
|
||||
'windows_defender.temp',
|
||||
'windows_explorer.run',
|
||||
'windows_explorer.thumbnails',
|
||||
)
|
||||
CUSTOM_POWER_PLAN_NAME = f'{KIT_NAME_FULL} Power Plan'
|
||||
CUSTOM_POWER_PLAN_DESC = 'Customized for the best experience.'
|
||||
POWER_PLANS = {
|
||||
'Balanced': '381b4222-f694-41f0-9685-ff5bb260df2e',
|
||||
'Custom': '01189998-8199-9119-725c-ccccccccccc3',
|
||||
'High Performance': '8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c',
|
||||
}
|
||||
POWER_PLAN_SLEEP_TIMEOUTS = {
|
||||
'Balanced': ('1800', '900'),
|
||||
'High Performance': ('0', '0'),
|
||||
}
|
||||
REG_UAC_DEFAULTS_WIN7 = {
|
||||
'HKLM': {
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Policies\System': (
|
||||
('ConsentPromptBehaviorAdmin', 5, 'DWORD'),
|
||||
('EnableLUA', 1, 'DWORD'),
|
||||
('PromptOnSecureDesktop', 1, 'DWORD'),
|
||||
),
|
||||
},
|
||||
}
|
||||
REG_UAC_DEFAULTS_WIN10 = {
|
||||
'HKLM': {
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Policies\System': (
|
||||
('ConsentPromptBehaviorAdmin', 5, 'DWORD'),
|
||||
('ConsentPromptBehaviorUser', 3, 'DWORD'),
|
||||
('EnableInstallerDetection', 1, 'DWORD'),
|
||||
('EnableLUA', 1, 'DWORD'),
|
||||
('EnableVirtualization', 1, 'DWORD'),
|
||||
('PromptOnSecureDesktop', 1, 'DWORD'),
|
||||
),
|
||||
},
|
||||
}
|
||||
WIDTH = 50
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,219 +0,0 @@
|
|||
"""WizardKit: Config - Setup"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
|
||||
BROWSER_PATHS = {
|
||||
# Relative to PROGRAMFILES_64, PROGRAMFILES_32, LOCALAPPDATA (in that order)
|
||||
'Google Chrome': 'Google/Chrome/Application/chrome.exe',
|
||||
'Mozilla Firefox': 'Mozilla Firefox/firefox.exe',
|
||||
'Microsoft Edge': 'Microsoft/Edge/Application/msedge.exe',
|
||||
'Opera': 'Opera/launcher.exe',
|
||||
}
|
||||
DISABLED_ENTRIES_WINDOWS_11 = {
|
||||
# Group Name: Option Name
|
||||
'Install Software': 'Open Shell',
|
||||
'Configure System': 'Open Shell',
|
||||
}
|
||||
LIBREOFFICE_XCU_DATA = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<item oor:path="/org.openoffice.Setup/Office/Factories/org.openoffice.Setup:Factory['com.sun.star.presentation.PresentationDocument']"><prop oor:name="ooSetupFactoryDefaultFilter" oor:op="fuse"><value>Impress MS PowerPoint 2007 XML</value></prop></item>
|
||||
<item oor:path="/org.openoffice.Setup/Office/Factories/org.openoffice.Setup:Factory['com.sun.star.sheet.SpreadsheetDocument']"><prop oor:name="ooSetupFactoryDefaultFilter" oor:op="fuse"><value>Calc MS Excel 2007 XML</value></prop></item>
|
||||
<item oor:path="/org.openoffice.Setup/Office/Factories/org.openoffice.Setup:Factory['com.sun.star.text.TextDocument']"><prop oor:name="ooSetupFactoryDefaultFilter" oor:op="fuse"><value>MS Word 2007 XML</value></prop></item>
|
||||
<item oor:path="/org.openoffice.Office.Common/Save/Document"><prop oor:name="WarnAlienFormat" oor:op="fuse"><value>false</value></prop></item>
|
||||
</oor:items>
|
||||
'''
|
||||
REG_CHROME_UBLOCK_ORIGIN = {
|
||||
'HKLM': {
|
||||
r'Software\Google\Chrome\Extensions\cjpalhdlnbpafiamejdnhcphjbkeiagm': (
|
||||
('update_url', 'https://clients2.google.com/service/update2/crx', 'SZ', '32'),
|
||||
)
|
||||
},
|
||||
}
|
||||
REG_WINDOWS_BSOD_MINIDUMPS = {
|
||||
'HKLM': {
|
||||
# Enable small memory dumps
|
||||
r'SYSTEM\CurrentControlSet\Control\CrashControl': (
|
||||
('CrashDumpEnabled', 3, 'DWORD'),
|
||||
)
|
||||
}
|
||||
}
|
||||
REG_WINDOWS_EXPLORER = {
|
||||
'HKLM': {
|
||||
# Allow password sign-in for MS accounts
|
||||
r'Software\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device': (
|
||||
('DevicePasswordLessBuildVersion', 0, 'DWORD'),
|
||||
),
|
||||
# Disable Location Tracking
|
||||
r'Software\Microsoft\Windows NT\CurrentVersion\Sensor\Overrides\{BFA794E4-F964-4FDB-90F6-51056BFE4B44}': (
|
||||
('SensorPermissionState', 0, 'DWORD'),
|
||||
),
|
||||
r'System\CurrentControlSet\Services\lfsvc\Service\Configuration': (
|
||||
('Status', 0, 'DWORD'),
|
||||
),
|
||||
# Disable Telemetry
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Policies\DataCollection': (
|
||||
('AllowTelemetry', 0, 'DWORD'),
|
||||
('AllowTelemetry', 0, 'DWORD', '32'),
|
||||
),
|
||||
r'Software\Policies\Microsoft\Windows\DataCollection': (
|
||||
('AllowTelemetry', 0, 'DWORD'),
|
||||
),
|
||||
# Disable floating Bing search widget
|
||||
r'Software\Policies\Microsoft\Edge': (
|
||||
('WebWidgetAllowed', 0, 'DWORD'),
|
||||
),
|
||||
# Disable Edge first run screen
|
||||
r'Software\Policies\Microsoft\MicrosoftEdge\Main': (
|
||||
('PreventFirstRunPage', 1, 'DWORD'),
|
||||
),
|
||||
# Disable Wi-Fi Sense
|
||||
r'Software\Microsoft\PolicyManager\default\WiFi\AllowWiFiHotSpotReporting': (
|
||||
('Value', 0, 'DWORD'),
|
||||
),
|
||||
r'Software\Microsoft\PolicyManager\default\WiFi\AllowAutoConnectToWiFiSenseHotspots': (
|
||||
('Value', 0, 'DWORD'),
|
||||
),
|
||||
},
|
||||
'HKCU': {
|
||||
# Desktop theme (<= v1809 default)
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Themes\Personalize': (
|
||||
('AppsUseLightTheme', 1, 'DWORD'),
|
||||
('SystemUsesLightTheme', 0, 'DWORD'),
|
||||
),
|
||||
# Disable features
|
||||
r'Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager': (
|
||||
('SilentInstalledAppsEnabled', 0, 'DWORD'),
|
||||
# Tips and Tricks
|
||||
('SoftLandingEnabled ', 0, 'DWORD'),
|
||||
('SubscribedContent-338389Enabled', 0, 'DWORD'),
|
||||
),
|
||||
# Disable news and interests from opening on hover
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Feeds': (
|
||||
('ShellFeedsTaskbarOpenOnHover', 0, 'DWORD'),
|
||||
),
|
||||
# Disable search highlights
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Feeds\DSB': (
|
||||
('ShowDynamicContent', 0, 'DWORD'),
|
||||
),
|
||||
# File Explorer
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced': (
|
||||
# Change default Explorer view to "Computer"
|
||||
('LaunchTo', 1, 'DWORD'),
|
||||
('SeparateProcess', 1, 'DWORD'),
|
||||
),
|
||||
# Hide People bar
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced\People': (
|
||||
('PeopleBand', 0, 'DWORD'),
|
||||
),
|
||||
# Hide Search button / box
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Search': (
|
||||
('SearchboxTaskbarMode', 1, 'DWORD'),
|
||||
),
|
||||
# Disable search highlights from opening on hover
|
||||
r'Software\Microsoft\Windows\CurrentVersion\SearchSettings': (
|
||||
('IsDynamicSearchBoxEnabled', 0, 'DWORD'),
|
||||
),
|
||||
# Disable "Let's make Windows even better" screens
|
||||
r'Software\Microsoft\Windows\CurrentVersion\UserProfileEngagement': (
|
||||
('ScoobeSystemSettingEnabled', 0, 'DWORD'),
|
||||
),
|
||||
},
|
||||
}
|
||||
REG_OPEN_SHELL_SETTINGS = {
|
||||
'HKCU': {
|
||||
r'Software\OpenShell\StartMenu': (
|
||||
('ShowedStyle2', 1, 'DWORD'),
|
||||
),
|
||||
r'Software\OpenShell\StartMenu\Settings': (
|
||||
('HighlightNew', 0, 'DWORD'),
|
||||
('MenuStyle', 'Win7', 'SZ'),
|
||||
('RecentPrograms', 'Recent', 'SZ'),
|
||||
('SkinW7', 'Fluent-Metro', 'SZ'),
|
||||
('SkinVariationW7', '', 'SZ'),
|
||||
('SkipMetro', 1, 'DWORD'),
|
||||
(
|
||||
'SkinOptionsW7',
|
||||
[
|
||||
# NOTE: All options need to be specified to work
|
||||
'DARK_MAIN=1', 'METRO_MAIN=0', 'LIGHT_MAIN=0', 'AUTOMODE_MAIN=0',
|
||||
'DARK_SUBMENU=0', 'METRO_SUBMENU=0', 'LIGHT_SUBMENU=0', 'AUTOMODE_SUBMENU=1',
|
||||
'SUBMENU_SEPARATORS=1', 'DARK_SEARCH=0', 'METRO_SEARCH=0', 'LIGHT_SEARCH=1',
|
||||
'AUTOMODE_SEARCH=0', 'SEARCH_FRAME=1', 'SEARCH_COLOR=0', 'SMALL_SEARCH=0',
|
||||
'MODERN_SEARCH=1', 'SEARCH_ITALICS=0', 'NONE=0', 'SEPARATOR=0',
|
||||
'TWO_TONE=1', 'CLASSIC_SELECTOR=1', 'HALF_SELECTOR=0', 'CURVED_MENUSEL=1',
|
||||
'CURVED_SUBMENU=0', 'SELECTOR_REVEAL=0', 'TRANSPARENT=0', 'OPAQUE_SUBMENU=1',
|
||||
'OPAQUE_MENU=0', 'OPAQUE=0', 'STANDARD=1', 'SMALL_MAIN2=0',
|
||||
'SMALL_ICONS=0', 'COMPACT_SUBMENU=0', 'PRESERVE_MAIN2=0', 'LESS_PADDING=0',
|
||||
'EXTRA_PADDING=1', '24_PADDING=0', 'LARGE_PROGRAMS=0', 'TRANSPARENT_SHUTDOWN=0',
|
||||
'OUTLINE_SHUTDOWN=0', 'BUTTON_SHUTDOWN=1', 'EXPERIMENTAL_SHUTDOWN=0', 'LARGE_FONT=0',
|
||||
'CONNECTED_BORDER=1', 'FLOATING_BORDER=0', 'LARGE_SUBMENU=0', 'LARGE_LISTS=0',
|
||||
'THIN_MAIN2=0', 'EXPERIMENTAL_MAIN2=1', 'USER_IMAGE=1', 'USER_OUTSIDE=0',
|
||||
'SCALING_USER=1', '56=0', '64=0', 'TRANSPARENT_USER=0',
|
||||
'UWP_SCROLLBAR=1', 'MODERN_SCROLLBAR=0', 'OLD_ICONS=0', 'NEW_ICONS=1',
|
||||
'SMALL_ARROWS=0', 'ICON_FRAME=0', 'SEARCH_SEPARATOR=0', 'NO_PROGRAMS_BUTTON=0',
|
||||
],
|
||||
'MULTI_SZ',
|
||||
),
|
||||
),
|
||||
},
|
||||
}
|
||||
REG_OPEN_SHELL_LOW_POWER_IDLE = {
|
||||
'HKCU': {
|
||||
r'Software\OpenShell\StartMenu': (
|
||||
(
|
||||
'CSettingsDlg',
|
||||
b'h\x02\x00\x00\xa7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006\r\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00',
|
||||
'BINARY',
|
||||
),
|
||||
(
|
||||
'CEditMenuDlg7',
|
||||
b'\xde\x02\x00\x00\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
'BINARY',
|
||||
),
|
||||
),
|
||||
r'Software\OpenShell\StartMenu\Settings': (
|
||||
('ShutdownW7', 'switch_user, logoff, lock, restart, hibernate', 'SZ'),
|
||||
(
|
||||
'MenuItems7',
|
||||
[
|
||||
'Item1.Command=user_files', 'Item1.Settings=NOEXPAND',
|
||||
'Item2.Command=user_documents', 'Item2.Settings=NOEXPAND',
|
||||
'Item3.Command=user_pictures', 'Item3.Settings=NOEXPAND',
|
||||
'Item4.Command=user_music', 'Item4.Settings=NOEXPAND',
|
||||
'Item5.Command=user_videos', 'Item5.Settings=ITEM_DISABLED',
|
||||
'Item6.Command=downloads', 'Item6.Settings=ITEM_DISABLED',
|
||||
'Item7.Command=homegroup', 'Item7.Settings=ITEM_DISABLED',
|
||||
'Item8.Command=separator',
|
||||
'Item9.Command=games', 'Item9.Settings=TRACK_RECENT|NOEXPAND|ITEM_DISABLED',
|
||||
'Item10.Command=favorites', 'Item10.Settings=ITEM_DISABLED',
|
||||
'Item11.Command=recent_documents',
|
||||
'Item12.Command=computer', 'Item12.Settings=NOEXPAND',
|
||||
'Item13.Command=network', 'Item13.Settings=ITEM_DISABLED',
|
||||
'Item14.Command=network_connections', 'Item14.Settings=ITEM_DISABLED',
|
||||
'Item15.Command=separator',
|
||||
'Item16.Command=control_panel', 'Item16.Settings=TRACK_RECENT',
|
||||
'Item17.Command=pc_settings', 'Item17.Settings=TRACK_RECENT',
|
||||
'Item18.Command=admin', 'Item18.Settings=TRACK_RECENT|ITEM_DISABLED',
|
||||
'Item19.Command=devices', 'Item19.Settings=NOEXPAND',
|
||||
'Item20.Command=defaults',
|
||||
'Item21.Command=help',
|
||||
'Item22.Command=run',
|
||||
'Item23.Command=monitor_off', 'Item23.Label=Sleep', 'Item23.Settings=NOEXPAND',
|
||||
'Item24.Command=apps', 'Item24.Settings=ITEM_DISABLED',
|
||||
'Item25.Command=windows_security',
|
||||
],
|
||||
'MULTI_SZ',
|
||||
),
|
||||
),
|
||||
},
|
||||
}
|
||||
UBLOCK_ORIGIN_URLS = {
|
||||
'Google Chrome': 'https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm',
|
||||
'Microsoft Edge': 'https://microsoftedge.microsoft.com/addons/detail/ublock-origin/odfafepnkmbhccpbejgmiehpchacaeak',
|
||||
'Mozilla Firefox': 'https://addons.mozilla.org/addon/ublock-origin/',
|
||||
'Opera': 'https://addons.opera.com/extensions/details/ublock/',
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
"""WizardKit: Config - Tool Sources"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
|
||||
# Download frequency in days
|
||||
DOWNLOAD_FREQUENCY = 7
|
||||
|
||||
|
||||
# Sources
|
||||
SOURCES = {
|
||||
# Main
|
||||
'AVRemover32': 'https://download.eset.com/com/eset/tools/installers/av_remover/latest/avremover_nt32_enu.exe',
|
||||
'AVRemover64': 'https://download.eset.com/com/eset/tools/installers/av_remover/latest/avremover_nt64_enu.exe',
|
||||
'AdwCleaner': 'https://downloads.malwarebytes.com/file/adwcleaner',
|
||||
'Autologon32': 'http://live.sysinternals.com/Autologon.exe',
|
||||
'Autologon64': 'http://live.sysinternals.com/Autologon64.exe',
|
||||
'Firefox32': 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win&lang=en-US',
|
||||
'Firefox64': 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=en-US',
|
||||
'HitmanPro32': 'https://dl.surfright.nl/HitmanPro.exe',
|
||||
'HitmanPro64': 'https://dl.surfright.nl/HitmanPro_x64.exe',
|
||||
'KVRT': 'https://devbuilds.s.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe',
|
||||
'RKill': 'https://download.bleepingcomputer.com/grinler/rkill.exe',
|
||||
'RegDelNull': 'https://live.sysinternals.com/RegDelNull.exe',
|
||||
'RegDelNull64': 'https://live.sysinternals.com/RegDelNull64.exe',
|
||||
|
||||
# Build Kit
|
||||
'AIDA64': 'https://download.aida64.com/aida64engineer692.zip',
|
||||
'Adobe Reader DC': 'https://ardownload2.adobe.com/pub/adobe/reader/win/AcrobatDC/2300620360/AcroRdrDC2300620360_en_US.exe',
|
||||
'Aria2': 'https://github.com/aria2/aria2/releases/download/release-1.36.0/aria2-1.36.0-win-32bit-build1.zip',
|
||||
'Autoruns32': 'http://live.sysinternals.com/Autoruns.exe',
|
||||
'Autoruns64': 'http://live.sysinternals.com/Autoruns64.exe',
|
||||
'BleachBit': 'https://download.bleachbit.org/BleachBit-4.4.2-portable.zip',
|
||||
'BlueScreenView32': 'http://www.nirsoft.net/utils/bluescreenview.zip',
|
||||
'BlueScreenView64': 'http://www.nirsoft.net/utils/bluescreenview-x64.zip',
|
||||
'BCUninstaller': 'https://github.com/Klocman/Bulk-Crap-Uninstaller/releases/download/v5.7/BCUninstaller_5.7_portable.zip',
|
||||
'DDU': 'https://www.wagnardsoft.com/DDU/download/DDU%20v18.0.6.8.exe',
|
||||
'ERUNT': 'http://www.aumha.org/downloads/erunt.zip',
|
||||
'Everything32': 'https://www.voidtools.com/Everything-1.4.1.1024.x86.zip',
|
||||
'Everything64': 'https://www.voidtools.com/Everything-1.4.1.1024.x64.zip',
|
||||
'FastCopy': 'https://github.com/FastCopyLab/FastCopyDist2/raw/main/FastCopy5.4.2_installer.exe',
|
||||
'Fluent-Metro': 'https://github.com/bonzibudd/Fluent-Metro/releases/download/v1.5.3/Fluent-Metro_1.5.3.zip',
|
||||
'FurMark': 'https://geeks3d.com/dl/get/728',
|
||||
'HWiNFO': 'https://www.sac.sk/download/utildiag/hwi_764.zip',
|
||||
'LibreOffice32': 'https://download.documentfoundation.org/libreoffice/stable/7.6.2/win/x86/LibreOffice_7.6.2_Win_x86.msi',
|
||||
'LibreOffice64': 'https://download.documentfoundation.org/libreoffice/stable/7.6.2/win/x86_64/LibreOffice_7.6.2_Win_x86-64.msi',
|
||||
'Macs Fan Control': 'https://www.crystalidea.com/downloads/macsfancontrol_setup.exe',
|
||||
'Neutron': 'http://keir.net/download/neutron.zip',
|
||||
'Notepad++': 'https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v8.5.8/npp.8.5.8.portable.minimalist.7z',
|
||||
'OpenShell': 'https://github.com/Open-Shell/Open-Shell-Menu/releases/download/v4.4.191/OpenShellSetup_4_4_191.exe',
|
||||
'PuTTY': 'https://the.earth.li/~sgtatham/putty/latest/w32/putty.zip',
|
||||
'SDIO Torrent': 'https://www.glenn.delahoy.com/downloads/sdio/SDIO_Update.torrent',
|
||||
'WizTree': 'https://diskanalyzer.com/files/wiztree_4_15_portable.zip',
|
||||
'XMPlay': 'https://support.xmplay.com/files/20/xmplay385.zip?v=47090',
|
||||
'XMPlay 7z': 'https://support.xmplay.com/files/16/xmp-7z.zip?v=800962',
|
||||
'XMPlay Game': 'https://support.xmplay.com/files/12/xmp-gme.zip?v=515637',
|
||||
'XMPlay RAR': 'https://support.xmplay.com/files/16/xmp-rar.zip?v=409646',
|
||||
'XMPlay Innocuous': 'https://support.xmplay.com/files/10/Innocuous%20(v1.7).zip?v=645281',
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
"""WizardKit: Config - UFD"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
from wk.cfg.main import KIT_NAME_FULL
|
||||
|
||||
|
||||
# General
|
||||
SOURCES = {
|
||||
'Linux': {'Arg': '--linux', 'Type': 'ISO'},
|
||||
'WinPE': {'Arg': '--winpe', 'Type': 'ISO'},
|
||||
'Main Kit': {'Arg': '--main-kit', 'Type': 'KIT'},
|
||||
'Extra Dir': {'Arg': '--extra-dir', 'Type': 'DIR'},
|
||||
}
|
||||
|
||||
# Definitions: Boot entries
|
||||
BOOT_ENTRIES = {
|
||||
# Path to check: Comment to remove
|
||||
'/sources/boot.wim': 'UFD-WINPE',
|
||||
}
|
||||
BOOT_FILES = {
|
||||
# Directory: extension
|
||||
'/syslinux': 'cfg',
|
||||
'/EFI/boot': 'conf',
|
||||
}
|
||||
IMAGE_BOOT_ENTRIES = {
|
||||
'El Capitan': 'UFD-MACOS-10.11',
|
||||
'High Sierra': 'UFD-MACOS-10.13',
|
||||
'Catalina': 'UFD-MACOS-10.15',
|
||||
}
|
||||
|
||||
# Definitions: Sources and Destinations
|
||||
## NOTES: Paths are relative to the root of the ISO/UFD
|
||||
## Sources use rsync's trailing slash syntax
|
||||
ITEMS = {
|
||||
'Extra Dir': (
|
||||
('/', '/'),
|
||||
),
|
||||
'Linux': (
|
||||
('/arch', '/'),
|
||||
),
|
||||
'Main Kit': (
|
||||
('/', f'/{KIT_NAME_FULL}/'),
|
||||
),
|
||||
'WinPE': (
|
||||
('/bootmgr', '/'),
|
||||
('/bootmgr.efi', '/'),
|
||||
('/en_us', '/'),
|
||||
('/Boot/', '/boot/'),
|
||||
('/EFI/Boot/', '/EFI/Microsoft/'),
|
||||
('/EFI/Microsoft/', '/EFI/Microsoft/'),
|
||||
('/Boot/BCD', '/sources/'),
|
||||
('/Boot/boot.sdi', '/sources/'),
|
||||
('/bootmgr', '/sources/'),
|
||||
('/sources/boot.wim', '/sources/'),
|
||||
),
|
||||
}
|
||||
ITEMS_FROM_LIVE = {
|
||||
'WizardKit UFD base': (
|
||||
('/usr/share/WizardKit/', '/'),
|
||||
),
|
||||
'rEFInd': (
|
||||
('/usr/share/refind/drivers_x64/', '/EFI/Boot/drivers_x64/'),
|
||||
('/usr/share/refind/icons/', '/EFI/Boot/icons/'),
|
||||
('/usr/share/refind/refind_x64.efi', '/EFI/Boot/'),
|
||||
),
|
||||
'Syslinux': (
|
||||
('/usr/lib/syslinux/bios/', '/syslinux/'),
|
||||
),
|
||||
'Memtest86': (
|
||||
('/usr/share/memtest86-efi/', '/EFI/Memtest86/'),
|
||||
),
|
||||
'Wimboot': (
|
||||
('/usr/share/wimboot/', '/syslinux/'),
|
||||
),
|
||||
}
|
||||
ITEMS_HIDDEN = (
|
||||
# Linux (all versions)
|
||||
'arch',
|
||||
'EFI',
|
||||
'syslinux',
|
||||
# Main Kit
|
||||
f'{KIT_NAME_FULL}/.bin',
|
||||
f'{KIT_NAME_FULL}/.cbin',
|
||||
# WinPE
|
||||
'boot',
|
||||
'bootmgr',
|
||||
'bootmgr.efi',
|
||||
'en-us',
|
||||
'images',
|
||||
'sources',
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
"""WizardKit: Config - Windows Builds"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
|
||||
OLDEST_SUPPORTED_BUILD = 19041 # Windows 10 20H1
|
||||
OUTDATED_BUILD_NUMBERS = (
|
||||
9600, # Windows 8.1 Update
|
||||
18363, # Windows 10 19H2
|
||||
)
|
||||
WINDOWS_BUILDS = {
|
||||
# Windows 7
|
||||
'6.1.7600': 'RTM "Vienna"',
|
||||
'6.1.7601': 'SP1 "Vienna"',
|
||||
|
||||
# Windows 8
|
||||
'6.2.9200': 'RTM',
|
||||
|
||||
# Widnows 8.1
|
||||
'6.3.9200': '"Blue"',
|
||||
'6.3.9600': '"Update"',
|
||||
|
||||
# Windows 10
|
||||
'10.0.10240': '1507 "Threshold 1"',
|
||||
'10.0.10586': '1511 "Threshold 2"',
|
||||
'10.0.14393': '1607 "Redstone 1"',
|
||||
'10.0.15063': '1703 "Redstone 2"',
|
||||
'10.0.16299': '1709 "Redstone 3"',
|
||||
'10.0.17134': '1803 "Redstone 4"',
|
||||
'10.0.17763': '1809 "Redstone 5"',
|
||||
'10.0.18362': '1903 / 19H1',
|
||||
'10.0.18363': '1909 / 19H2',
|
||||
'10.0.19041': '2004 / 20H1',
|
||||
'10.0.19042': '20H2',
|
||||
'10.0.19043': '21H1',
|
||||
'10.0.19044': '21H2',
|
||||
'10.0.19045': '22H2',
|
||||
|
||||
# Windows 11
|
||||
'10.0.22000': '21H2',
|
||||
'10.0.22621': '22H2',
|
||||
'10.0.22631': '23H2',
|
||||
'10.0.26100': '24H2',
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"$schema": "https://aka.ms/winget-packages.schema.2.0.json",
|
||||
"CreationDate": "2023-06-25T01:40:45.003-00:00",
|
||||
"Sources": [
|
||||
{
|
||||
"Packages": [
|
||||
{
|
||||
"PackageIdentifier": "7zip.7zip"
|
||||
},
|
||||
{
|
||||
"PackageIdentifier": "Google.Chrome"
|
||||
},
|
||||
{
|
||||
"PackageIdentifier": "Microsoft.Edge"
|
||||
},
|
||||
{
|
||||
"PackageIdentifier": "Mozilla.Firefox"
|
||||
},
|
||||
{
|
||||
"PackageIdentifier": "VideoLAN.VLC"
|
||||
}
|
||||
],
|
||||
"SourceDetails": {
|
||||
"Argument": "https://cdn.winget.microsoft.com/cache",
|
||||
"Identifier": "Microsoft.Winget.Source_8wekyb3d8bbwe",
|
||||
"Name": "winget",
|
||||
"Type": "Microsoft.PreIndexed.Package"
|
||||
}
|
||||
}
|
||||
],
|
||||
"WinGetVersion": "1.4.11071"
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
{
|
||||
"$schema": "https://aka.ms/winget-packages.schema.2.0.json",
|
||||
"CreationDate": "2023-06-25T01:40:45.003-00:00",
|
||||
"Sources": [
|
||||
{
|
||||
"Packages": [
|
||||
{
|
||||
"PackageIdentifier": "Microsoft.VCRedist.2013.x64"
|
||||
},
|
||||
{
|
||||
"PackageIdentifier": "Microsoft.VCRedist.2013.x86"
|
||||
},
|
||||
{
|
||||
"PackageIdentifier": "Microsoft.VCRedist.2015+.x64"
|
||||
},
|
||||
{
|
||||
"PackageIdentifier": "Microsoft.VCRedist.2015+.x86"
|
||||
}
|
||||
],
|
||||
"SourceDetails": {
|
||||
"Argument": "https://cdn.winget.microsoft.com/cache",
|
||||
"Identifier": "Microsoft.Winget.Source_8wekyb3d8bbwe",
|
||||
"Name": "winget",
|
||||
"Type": "Microsoft.PreIndexed.Package"
|
||||
}
|
||||
}
|
||||
],
|
||||
"WinGetVersion": "1.4.11071"
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
"""WizardKit: ddrescue-tui module init"""
|
||||
|
||||
from . import block_pair
|
||||
from . import ddrescue
|
||||
from . import image
|
||||
from . import menus
|
||||
from . import state
|
||||
|
|
@ -1,575 +0,0 @@
|
|||
"""WizardKit: ddrescue TUI - Block Pairs"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import pathlib
|
||||
import plistlib
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
from wk import cfg, exe, std
|
||||
from wk.clone import menus
|
||||
from wk.hw import disk as hw_disk
|
||||
from wk.ui import ansi, cli
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
DDRESCUE_LOG_REGEX = re.compile(
|
||||
r'^\s*(?P<key>\S+):\s+'
|
||||
r'(?P<size>\d+)\s+'
|
||||
r'(?P<unit>[PTGMKB]i?B?)'
|
||||
r'.*\(\s*(?P<percent>\d+\.?\d*)%\)$',
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
# Classes
|
||||
class BlockPair():
|
||||
"""Object for tracking source to dest recovery data."""
|
||||
def __init__(
|
||||
self,
|
||||
source_dev: hw_disk.Disk,
|
||||
destination: pathlib.Path,
|
||||
working_dir: pathlib.Path,
|
||||
):
|
||||
self.sector_size: int = source_dev.phy_sec
|
||||
self.source: pathlib.Path = pathlib.Path(source_dev.path)
|
||||
self.destination: pathlib.Path = destination
|
||||
self.map_data: dict[str, bool | int] = {}
|
||||
self.map_path: pathlib.Path = pathlib.Path()
|
||||
self.size: int = source_dev.size
|
||||
self.status: dict[str, float | int | str] = {
|
||||
'read-skip': 'Pending',
|
||||
'read-full': 'Pending',
|
||||
'trim': 'Pending',
|
||||
'scrape': 'Pending',
|
||||
}
|
||||
self.test_map: pathlib.Path | None = None
|
||||
self.view_map: bool = 'DISPLAY' in os.environ or 'WAYLAND_DISPLAY' in os.environ
|
||||
self.view_proc: subprocess.Popen | None = None
|
||||
|
||||
# Set map path
|
||||
# e.g. '(Clone|Image)_Model_Serial[_p#]_Size[_Label].map'
|
||||
map_name = f'{source_dev.model}_{source_dev.serial}'
|
||||
if source_dev.bus == 'Image':
|
||||
map_name = 'Image'
|
||||
if source_dev.parent:
|
||||
part_num = re.sub(r"^.*?(\d+)$", r"\1", self.source.name)
|
||||
map_name += f'_p{part_num}'
|
||||
size_str = std.bytes_to_string(
|
||||
size=self.size,
|
||||
use_binary=False,
|
||||
)
|
||||
map_name += f'_{size_str.replace(" ", "")}'
|
||||
if source_dev.raw_details.get('label', ''):
|
||||
map_name += f'_{source_dev.raw_details["label"]}'
|
||||
map_name = map_name.replace(' ', '_')
|
||||
map_name = map_name.replace('/', '_')
|
||||
map_name = map_name.replace('\\', '_')
|
||||
if destination.is_dir():
|
||||
# Imaging
|
||||
self.map_path = pathlib.Path(f'{destination}/Image_{map_name}.map')
|
||||
self.destination = self.map_path.with_suffix('.dd')
|
||||
self.destination.touch()
|
||||
else:
|
||||
# Cloning
|
||||
self.map_path = pathlib.Path(f'{working_dir}/Clone_{map_name}.map')
|
||||
|
||||
# Create map file if needed
|
||||
# NOTE: We need to set the domain size for --complete-only to work
|
||||
if not self.map_path.exists():
|
||||
self.map_path.write_text(
|
||||
data=cfg.ddrescue.DDRESCUE_MAP_TEMPLATE.format(
|
||||
name=cfg.main.KIT_NAME_FULL,
|
||||
size=self.size,
|
||||
),
|
||||
encoding='utf-8',
|
||||
)
|
||||
|
||||
# Set initial status
|
||||
self.set_initial_status()
|
||||
|
||||
def __getstate__(self):
|
||||
"""Override to allow pickling ddrescue.State() objects."""
|
||||
bp_state = self.__dict__.copy()
|
||||
del bp_state['view_proc']
|
||||
return bp_state
|
||||
|
||||
def get_error_size(self) -> int:
|
||||
"""Get error size in bytes, returns int."""
|
||||
return self.size - self.get_rescued_size()
|
||||
|
||||
def get_percent_recovered(self) -> float:
|
||||
"""Get percent rescued from map_data, returns float."""
|
||||
return 100 * self.map_data.get('rescued', 0) / self.size
|
||||
|
||||
def get_rescued_size(self) -> int:
|
||||
"""Get rescued size using map data.
|
||||
|
||||
NOTE: Returns 0 if no map data is available.
|
||||
"""
|
||||
self.load_map_data()
|
||||
return self.map_data.get('rescued', 0)
|
||||
|
||||
def load_map_data(self) -> None:
|
||||
"""Load map data from file.
|
||||
|
||||
NOTE: If the file is missing it is assumed that recovery hasn't
|
||||
started yet so default values will be returned instead.
|
||||
"""
|
||||
data: dict[str, bool | int] = {'full recovery': False, 'pass completed': False}
|
||||
|
||||
# Get output from ddrescuelog
|
||||
cmd = [
|
||||
'ddrescuelog',
|
||||
'--binary-prefixes',
|
||||
'--show-status',
|
||||
f'--size={self.size}',
|
||||
self.map_path,
|
||||
]
|
||||
proc = exe.run_program(cmd, check=False)
|
||||
|
||||
# Parse output
|
||||
for line in proc.stdout.splitlines():
|
||||
_r = DDRESCUE_LOG_REGEX.search(line)
|
||||
if _r:
|
||||
if _r.group('key') == 'rescued' and _r.group('percent') == '100':
|
||||
# Fix rounding errors from ddrescuelog output
|
||||
data['rescued'] = self.size
|
||||
else:
|
||||
data[_r.group('key')] = std.string_to_bytes(
|
||||
f'{_r.group("size")} {_r.group("unit")}',
|
||||
)
|
||||
data['pass completed'] = 'current status: finished' in line.lower()
|
||||
|
||||
# Check if 100% done (only if map is present and non-zero size
|
||||
# NOTE: ddrescuelog returns 0 (i.e. 100% done) for empty files
|
||||
if self.map_path.exists() and self.map_path.stat().st_size != 0:
|
||||
cmd = [
|
||||
'ddrescuelog',
|
||||
'--done-status',
|
||||
f'--size={self.size}',
|
||||
self.map_path,
|
||||
]
|
||||
proc = exe.run_program(cmd, check=False)
|
||||
data['full recovery'] = proc.returncode == 0
|
||||
|
||||
# Done
|
||||
self.map_data.update(data)
|
||||
|
||||
def pass_complete(self, pass_name) -> bool:
|
||||
"""Check if pass_name is complete based on map data, returns bool."""
|
||||
pending_size = self.map_data['non-tried']
|
||||
|
||||
# Full recovery
|
||||
if self.map_data.get('full recovery', False):
|
||||
return True
|
||||
|
||||
# New recovery
|
||||
if 'non-tried' not in self.map_data:
|
||||
return False
|
||||
|
||||
# Initial read skip pass
|
||||
if pass_name == 'read-skip':
|
||||
pass_threshold = cfg.ddrescue.AUTO_PASS_THRESHOLDS[pass_name]
|
||||
if self.get_percent_recovered() >= pass_threshold:
|
||||
return True
|
||||
|
||||
# Recovery in progress
|
||||
if pass_name in ('trim', 'scrape'):
|
||||
pending_size += self.map_data['non-trimmed']
|
||||
if pass_name == 'scrape':
|
||||
pending_size += self.map_data['non-scraped']
|
||||
if pending_size == 0:
|
||||
# This is true when the previous and current passes are complete
|
||||
return True
|
||||
|
||||
# This should never be reached
|
||||
return False
|
||||
|
||||
def safety_check(self) -> None:
|
||||
"""Run safety check and abort if necessary."""
|
||||
# TODO: Expand section to support non-Linux systems
|
||||
dest_size = -1
|
||||
if self.destination.is_block_device():
|
||||
cmd = [
|
||||
'lsblk', '--bytes', '--json',
|
||||
'--nodeps', '--noheadings', '--output=size',
|
||||
self.destination,
|
||||
]
|
||||
json_data = exe.get_json_from_command(cmd)
|
||||
dest_size = json_data['blockdevices'][0]['size']
|
||||
del json_data
|
||||
|
||||
# Check destination size if cloning
|
||||
if not self.destination.is_file() and dest_size < self.size:
|
||||
cli.print_error(f'Invalid destination: {self.destination}')
|
||||
raise std.GenericAbort()
|
||||
|
||||
def set_initial_status(self) -> None:
|
||||
"""Read map data and set initial statuses."""
|
||||
self.load_map_data()
|
||||
percent = self.get_percent_recovered()
|
||||
for name in self.status:
|
||||
if self.pass_complete(name):
|
||||
self.status[name] = percent
|
||||
else:
|
||||
# Stop checking
|
||||
if percent > 0:
|
||||
self.status[name] = percent
|
||||
break
|
||||
|
||||
def skip_pass(self, pass_name) -> None:
|
||||
"""Mark pass as skipped if applicable."""
|
||||
if self.status[pass_name] == 'Pending':
|
||||
self.status[pass_name] = 'Skipped'
|
||||
|
||||
def update_progress(self, pass_name) -> None:
|
||||
"""Update progress via map data."""
|
||||
self.load_map_data()
|
||||
|
||||
# Update status
|
||||
percent = self.get_percent_recovered()
|
||||
if percent > 0:
|
||||
self.status[pass_name] = percent
|
||||
|
||||
# Mark future passes as skipped if applicable
|
||||
if percent == 100:
|
||||
status_keys = list(self.status.keys())
|
||||
for pass_n in status_keys[status_keys.index(pass_name)+1:]:
|
||||
self.status[pass_n] = 'Skipped'
|
||||
|
||||
|
||||
# Functions
|
||||
def add_clone_block_pairs(state) -> list[hw_disk.Disk]:
|
||||
"""Add device to device block pairs and set settings if necessary."""
|
||||
source_sep = get_partition_separator(state.source.path.name)
|
||||
dest_sep = get_partition_separator(state.destination.path.name)
|
||||
settings = {}
|
||||
|
||||
# Clone settings
|
||||
settings = state.load_settings(discard_unused_settings=True)
|
||||
|
||||
# Add pairs from previous run
|
||||
if settings['Partition Mapping']:
|
||||
source_parts = []
|
||||
for part_map in settings['Partition Mapping']:
|
||||
bp_source = hw_disk.Disk(
|
||||
f'{state.source.path}{source_sep}{part_map[0]}',
|
||||
)
|
||||
bp_dest = pathlib.Path(
|
||||
f'{state.destination.path}{dest_sep}{part_map[1]}',
|
||||
)
|
||||
source_parts.append(bp_source)
|
||||
state.add_block_pair(bp_source, bp_dest)
|
||||
return source_parts
|
||||
|
||||
# Add pairs from selection
|
||||
source_parts = menus.select_disk_parts('Clone', state.source)
|
||||
if state.source.path.samefile(source_parts[0].path):
|
||||
# Whole disk (or single partition via args), skip settings
|
||||
bp_dest = state.destination.path
|
||||
state.add_block_pair(state.source, bp_dest)
|
||||
return source_parts
|
||||
|
||||
# New run, use new settings file
|
||||
settings['Needs Format'] = True
|
||||
offset = 0
|
||||
user_choice = cli.choice(
|
||||
'Format clone using GPT, MBR, or match Source type?',
|
||||
['G', 'M', 'S'],
|
||||
)
|
||||
if user_choice == 'G':
|
||||
settings['Table Type'] = 'GPT'
|
||||
elif user_choice == 'M':
|
||||
settings['Table Type'] = 'MBR'
|
||||
else:
|
||||
# Match source type
|
||||
settings['Table Type'] = get_table_type(state.source.path)
|
||||
if cli.ask('Create an empty Windows boot partition on the clone?'):
|
||||
settings['Create Boot Partition'] = True
|
||||
offset = 2 if settings['Table Type'] == 'GPT' else 1
|
||||
|
||||
# Add pairs
|
||||
for dest_num, part in enumerate(source_parts):
|
||||
dest_num += offset + 1
|
||||
bp_dest = pathlib.Path(
|
||||
f'{state.destination.path}{dest_sep}{dest_num}',
|
||||
)
|
||||
state.add_block_pair(part, bp_dest)
|
||||
|
||||
# Add to settings file
|
||||
source_num = re.sub(r'^.*?(\d+)$', r'\1', part.path.name)
|
||||
settings['Partition Mapping'].append([source_num, dest_num])
|
||||
|
||||
# Save settings
|
||||
state.save_settings(settings)
|
||||
|
||||
# Done
|
||||
return source_parts
|
||||
|
||||
|
||||
def add_image_block_pairs(state) -> list[hw_disk.Disk]:
|
||||
"""Add device to image file block pairs."""
|
||||
source_parts = menus.select_disk_parts(state.mode, state.source)
|
||||
for part in source_parts:
|
||||
state.add_block_pair(part, state.destination)
|
||||
|
||||
# Done
|
||||
return source_parts
|
||||
|
||||
|
||||
def build_block_pair_report(block_pairs, settings) -> list:
|
||||
"""Build block pair report, returns list."""
|
||||
report = []
|
||||
notes = []
|
||||
if block_pairs:
|
||||
report.append(ansi.color_string('Block Pairs', 'GREEN'))
|
||||
else:
|
||||
# Bail early
|
||||
return report
|
||||
|
||||
# Show block pair mapping
|
||||
if settings and settings['Create Boot Partition']:
|
||||
if settings['Table Type'] == 'GPT':
|
||||
report.append(f'{" —— ":<9} --> EFI System Partition')
|
||||
report.append(f'{" —— ":<9} --> Microsoft Reserved Partition')
|
||||
elif settings['Table Type'] == 'MBR':
|
||||
report.append(f'{" —— ":<9} --> System Reserved')
|
||||
for pair in block_pairs:
|
||||
report.append(f'{pair.source.name:<9} --> {pair.destination.name}')
|
||||
|
||||
# Show resume messages as necessary
|
||||
if settings:
|
||||
if not settings['First Run']:
|
||||
notes.append(
|
||||
ansi.color_string(
|
||||
['NOTE:', 'Clone settings loaded from previous run.'],
|
||||
['BLUE', None],
|
||||
),
|
||||
)
|
||||
if settings['Needs Format'] and settings['Table Type']:
|
||||
msg = f'Destination will be formatted using {settings["Table Type"]}'
|
||||
notes.append(
|
||||
ansi.color_string(
|
||||
['NOTE:', msg],
|
||||
['BLUE', None],
|
||||
),
|
||||
)
|
||||
if any(pair.get_rescued_size() > 0 for pair in block_pairs):
|
||||
notes.append(
|
||||
ansi.color_string(
|
||||
['NOTE:', 'Resume data loaded from map file(s).'],
|
||||
['BLUE', None],
|
||||
),
|
||||
)
|
||||
|
||||
# Add notes to report
|
||||
if notes:
|
||||
report.append(' ')
|
||||
report.extend(notes)
|
||||
|
||||
# Done
|
||||
return report
|
||||
|
||||
|
||||
def build_sfdisk_partition_line(table_type, dev_path, size, details) -> str:
|
||||
"""Build sfdisk partition line using passed details, returns str."""
|
||||
line = f'{dev_path} : size={size}'
|
||||
dest_type = ''
|
||||
source_filesystem = str(details.get('fstype', '')).upper()
|
||||
source_table_type = ''
|
||||
source_type = details.get('parttype', '')
|
||||
|
||||
# Set dest type
|
||||
if re.match(r'^0x\w+$', source_type):
|
||||
# Source is a MBR type
|
||||
source_table_type = 'MBR'
|
||||
if table_type == 'MBR':
|
||||
dest_type = source_type.replace('0x', '').lower()
|
||||
elif re.match(r'^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$', source_type):
|
||||
# Source is a GPT type
|
||||
source_table_type = 'GPT'
|
||||
if table_type == 'GPT':
|
||||
dest_type = source_type.upper()
|
||||
if not dest_type:
|
||||
# Assuming changing table types, set based on FS
|
||||
if source_filesystem in cfg.ddrescue.PARTITION_TYPES.get(table_type, {}):
|
||||
dest_type = cfg.ddrescue.PARTITION_TYPES[table_type][source_filesystem]
|
||||
line += f', type={dest_type}'
|
||||
|
||||
# Safety Check
|
||||
if not dest_type:
|
||||
cli.print_error(f'Failed to determine partition type for: {dev_path}')
|
||||
raise std.GenericAbort()
|
||||
|
||||
# Add extra details
|
||||
if details.get('partlabel', ''):
|
||||
line += f', name="{details["partlabel"]}"'
|
||||
if details.get('partuuid', '') and source_table_type == table_type:
|
||||
# Only add UUID if source/dest table types match
|
||||
line += f', uuid={details["partuuid"].upper()}'
|
||||
|
||||
# Done
|
||||
return line
|
||||
|
||||
|
||||
def get_partition_separator(name) -> str:
|
||||
"""Get partition separator based on device name, returns str."""
|
||||
separator = ''
|
||||
if re.search(r'(loop|mmc|nvme)', name, re.IGNORECASE):
|
||||
separator = 'p'
|
||||
|
||||
return separator
|
||||
|
||||
|
||||
def get_table_type(disk_path) -> str:
|
||||
"""Get disk partition table type, returns str.
|
||||
|
||||
NOTE: If resulting table type is not GPT or MBR
|
||||
then an exception is raised.
|
||||
"""
|
||||
disk_path = str(disk_path)
|
||||
table_type = None
|
||||
|
||||
# Linux
|
||||
if std.PLATFORM == 'Linux':
|
||||
cmd = f'lsblk --json --output=pttype --nodeps {disk_path}'.split()
|
||||
json_data = exe.get_json_from_command(cmd)
|
||||
table_type = json_data['blockdevices'][0].get('pttype', '').upper()
|
||||
table_type = table_type.replace('DOS', 'MBR')
|
||||
|
||||
# macOS
|
||||
if std.PLATFORM == 'Darwin':
|
||||
cmd = ['diskutil', 'list', '-plist', disk_path]
|
||||
proc = exe.run_program(cmd, check=False, encoding=None, errors=None)
|
||||
try:
|
||||
plist_data = plistlib.loads(proc.stdout)
|
||||
except (TypeError, ValueError):
|
||||
# Invalid / corrupt plist data? return empty dict to avoid crash
|
||||
pass
|
||||
else:
|
||||
disk_details = plist_data.get('AllDisksAndPartitions', [{}])[0]
|
||||
table_type = disk_details['Content']
|
||||
table_type = table_type.replace('FDisk_partition_scheme', 'MBR')
|
||||
table_type = table_type.replace('GUID_partition_scheme', 'GPT')
|
||||
|
||||
# Check type
|
||||
if table_type not in ('GPT', 'MBR'):
|
||||
cli.print_error(f'Unsupported partition table type: {table_type}')
|
||||
raise std.GenericAbort()
|
||||
|
||||
# Done
|
||||
return table_type
|
||||
|
||||
|
||||
def prep_destination(
|
||||
state,
|
||||
source_parts: list[hw_disk.Disk],
|
||||
dry_run: bool = True,
|
||||
) -> None:
|
||||
"""Prep destination as necessary."""
|
||||
# TODO: Split into Linux and macOS
|
||||
# logical sector size is not easily found under macOS
|
||||
# It might be easier to rewrite this section using macOS tools
|
||||
dest_prefix = str(state.destination.path)
|
||||
dest_prefix += get_partition_separator(state.destination.path.name)
|
||||
esp_type = 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B'
|
||||
msr_type = 'E3C9E316-0B5C-4DB8-817D-F92DF00215AE'
|
||||
part_num = 0
|
||||
sfdisk_script = []
|
||||
settings = state.load_settings()
|
||||
|
||||
# Bail early
|
||||
if not settings['Needs Format']:
|
||||
return
|
||||
|
||||
# Add partition table settings
|
||||
if settings['Table Type'] == 'GPT':
|
||||
sfdisk_script.append('label: gpt')
|
||||
else:
|
||||
sfdisk_script.append('label: dos')
|
||||
sfdisk_script.append('unit: sectors')
|
||||
sfdisk_script.append('')
|
||||
|
||||
# Add boot partition if requested
|
||||
if settings['Create Boot Partition']:
|
||||
if settings['Table Type'] == 'GPT':
|
||||
part_num += 1
|
||||
sfdisk_script.append(
|
||||
build_sfdisk_partition_line(
|
||||
table_type='GPT',
|
||||
dev_path=f'{dest_prefix}{part_num}',
|
||||
size='260MiB',
|
||||
details={'parttype': esp_type, 'partlabel': 'EFI System'},
|
||||
),
|
||||
)
|
||||
part_num += 1
|
||||
sfdisk_script.append(
|
||||
build_sfdisk_partition_line(
|
||||
table_type=settings['Table Type'],
|
||||
dev_path=f'{dest_prefix}{part_num}',
|
||||
size='16MiB',
|
||||
details={'parttype': msr_type, 'partlabel': 'Microsoft Reserved'},
|
||||
),
|
||||
)
|
||||
elif settings['Table Type'] == 'MBR':
|
||||
part_num += 1
|
||||
sfdisk_script.append(
|
||||
build_sfdisk_partition_line(
|
||||
table_type='MBR',
|
||||
dev_path=f'{dest_prefix}{part_num}',
|
||||
size='100MiB',
|
||||
details={'parttype': '0x7', 'partlabel': 'System Reserved'},
|
||||
),
|
||||
)
|
||||
|
||||
# Add selected partition(s)
|
||||
for part in source_parts:
|
||||
num_sectors = part.size / state.destination.log_sec
|
||||
num_sectors = math.ceil(num_sectors)
|
||||
part_num += 1
|
||||
sfdisk_script.append(
|
||||
build_sfdisk_partition_line(
|
||||
table_type=settings['Table Type'],
|
||||
dev_path=f'{dest_prefix}{part_num}',
|
||||
size=num_sectors,
|
||||
details=part.raw_details,
|
||||
),
|
||||
)
|
||||
|
||||
# Save sfdisk script
|
||||
script_path = (
|
||||
f'{state.working_dir}/'
|
||||
f'sfdisk_{state.destination.path.name}.script'
|
||||
)
|
||||
with open(script_path, 'w', encoding='utf-8') as _f:
|
||||
_f.write('\n'.join(sfdisk_script))
|
||||
|
||||
# Skip real format for dry runs
|
||||
if dry_run:
|
||||
LOG.info('Dry run, refusing to format destination')
|
||||
return
|
||||
|
||||
# Format disk
|
||||
LOG.warning('Formatting destination: %s', state.destination.path)
|
||||
with open(script_path, 'r', encoding='utf-8') as _f:
|
||||
proc = exe.run_program(
|
||||
cmd=['sudo', 'sfdisk', state.destination.path],
|
||||
stdin=_f,
|
||||
check=False,
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
cli.print_error('Error(s) encoundtered while formatting destination')
|
||||
raise std.GenericAbort()
|
||||
|
||||
# Update settings
|
||||
settings['Needs Format'] = False
|
||||
state.save_settings(settings)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,745 +0,0 @@
|
|||
"""WizardKit: ddrescue TUI"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import argparse
|
||||
import atexit
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
|
||||
from random import randint
|
||||
|
||||
import pytz
|
||||
|
||||
from wk import cfg, exe, io, log, std
|
||||
from wk.cfg.ddrescue import DDRESCUE_SPECIFIC_PASS_SETTINGS
|
||||
from wk.clone import menus
|
||||
from wk.clone.state import State, mark_non_recovered_as_non_tried
|
||||
from wk.hw import disk as hw_disk
|
||||
from wk.hw.smart import (
|
||||
check_attributes,
|
||||
smart_status_ok,
|
||||
update_smart_details,
|
||||
)
|
||||
from wk.ui import ansi, cli
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
DETECT_DRIVES_NOTICE = '''
|
||||
This option will force the drive controllers to rescan for devices.
|
||||
The method used is not 100% reliable and may cause issues. If you see
|
||||
any script errors or crashes after running this option then please
|
||||
restart the computer and try again.
|
||||
'''
|
||||
DDRESCUE_OUTPUT_HEIGHT = 14
|
||||
INITIAL_SKIP_MIN = 64 * 1024 # This is ddrescue's minimum accepted value
|
||||
PLATFORM = std.PLATFORM
|
||||
TIMEZONE = pytz.timezone(cfg.main.LINUX_TIME_ZONE)
|
||||
|
||||
|
||||
# Functions
|
||||
def argparse_helper() -> dict[str, None|bool|str]:
|
||||
"""Helper function to setup and return args, returns dict.
|
||||
|
||||
NOTE: A dict is used to match the legacy code.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='ddrescue-tui',
|
||||
description=f'{cfg.main.KIT_NAME_FULL}: ddrescue TUI',
|
||||
)
|
||||
parser.add_argument('mode', choices=('clone', 'image'), nargs='?')
|
||||
parser.add_argument('source', nargs='?')
|
||||
parser.add_argument('destination', nargs='?')
|
||||
parser.add_argument(
|
||||
'-s', '--dry-run', action='store_true',
|
||||
help='Print commands to be used instead of running them',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--force-local-map', action='store_true',
|
||||
help='Skip mounting shares and save map to local drive',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--start-fresh', action='store_true',
|
||||
help='Ignore previous runs and start new recovery',
|
||||
)
|
||||
args = parser.parse_args()
|
||||
legacy_args = {
|
||||
'clone': args.mode == 'clone',
|
||||
'image': args.mode == 'image',
|
||||
'<source>': args.source,
|
||||
'<destination>': args.destination,
|
||||
'--dry-run': args.dry_run,
|
||||
'--force-local-map': args.force_local_map,
|
||||
'--start-fresh': args.start_fresh,
|
||||
}
|
||||
return legacy_args
|
||||
|
||||
def build_ddrescue_cmd(block_pair, pass_name, settings_menu) -> list[str]:
|
||||
"""Build ddrescue cmd using passed details, returns list."""
|
||||
cmd = ['sudo', 'ddrescue']
|
||||
if (block_pair.destination.is_block_device()
|
||||
or block_pair.destination.is_char_device()):
|
||||
cmd.append('--force')
|
||||
cmd.extend(DDRESCUE_SPECIFIC_PASS_SETTINGS.get(pass_name, []))
|
||||
|
||||
# Fix domain size based on starting position
|
||||
domain_size = block_pair.size
|
||||
if settings_menu.options['--input-position']['Selected']:
|
||||
settings_menu.options['--reverse']['Selected'] = False
|
||||
input_position = std.string_to_bytes(
|
||||
settings_menu.options['--input-position']['Value'],
|
||||
)
|
||||
domain_size -= input_position
|
||||
cmd.append(f'--size={domain_size}')
|
||||
|
||||
# Determine skip sizes
|
||||
if settings_menu.options['--skip-size']['Selected']:
|
||||
skip_sizes = settings_menu.options['--skip-size']['Value'].split(',')
|
||||
skip_sizes = [float(s) for s in skip_sizes]
|
||||
initial_skip = max(INITIAL_SKIP_MIN, int(block_pair.size * skip_sizes[0]))
|
||||
max_skip = min(int(block_pair.size * skip_sizes[1]), domain_size)
|
||||
max_skip = max(INITIAL_SKIP_MIN, max_skip)
|
||||
cmd.append(f'--skip-size={initial_skip},{max_skip}')
|
||||
cmd.extend(get_ddrescue_settings(settings_menu))
|
||||
|
||||
# Add source physical sector size (if possible)
|
||||
cmd.append(f'--sector-size={block_pair.sector_size}')
|
||||
|
||||
# Generate test map if needed
|
||||
if '--test-mode' in cmd:
|
||||
cmd.remove('--test-mode')
|
||||
if not block_pair.test_map:
|
||||
block_pair.test_map = block_pair.map_path.with_stem(
|
||||
f'{block_pair.map_path.stem}-testing'
|
||||
)
|
||||
generate_test_map(map_path=block_pair.test_map, size=domain_size)
|
||||
cmd.append(f'--test-mode={block_pair.test_map}')
|
||||
|
||||
# Add block pair and map file
|
||||
if PLATFORM == 'Darwin':
|
||||
# Use Raw disks if possible
|
||||
for dev in (block_pair.source, block_pair.destination):
|
||||
raw_dev = pathlib.Path(dev.with_name(f'r{dev.name}'))
|
||||
if raw_dev.exists():
|
||||
cmd.append(raw_dev)
|
||||
else:
|
||||
cmd.append(dev)
|
||||
else:
|
||||
cmd.append(block_pair.source)
|
||||
cmd.append(block_pair.destination)
|
||||
cmd.append(block_pair.map_path)
|
||||
|
||||
# Done
|
||||
LOG.debug('ddrescue cmd: %s', cmd)
|
||||
return cmd
|
||||
|
||||
|
||||
def check_destination_health(destination) -> str:
|
||||
"""Check destination health, returns str."""
|
||||
result = ''
|
||||
|
||||
# Bail early
|
||||
if not isinstance(destination, hw_disk.Disk):
|
||||
# Return empty string
|
||||
return result
|
||||
|
||||
# Check for critical errors
|
||||
if not smart_status_ok(destination):
|
||||
result = 'Critical error(s) detected for: {destination.path}'
|
||||
|
||||
# Check for minor errors
|
||||
if not check_attributes(destination, only_blocking=False):
|
||||
result = f'Attribute error(s) detected for: {destination.path}'
|
||||
|
||||
# Done
|
||||
return result
|
||||
|
||||
|
||||
def generate_test_map(map_path: pathlib.Path, size: int) -> None:
|
||||
"""Generate test map with roughly 20% of the space marked as bad."""
|
||||
chunk = 2*1024**2
|
||||
output = [
|
||||
'# Mapfile. Created by WizardKit',
|
||||
'0x0 ? 1',
|
||||
]
|
||||
position = 0
|
||||
|
||||
# Generate "holes"
|
||||
steps, remainder = divmod(size, chunk)
|
||||
for _ in range(steps):
|
||||
bad = randint(1, 5) % 5 == 0
|
||||
output.append(f'{hex(position)} {hex(chunk)} {"-" if bad else "+"}')
|
||||
position += chunk
|
||||
if remainder:
|
||||
output.append(f'{hex(position)} {hex(remainder)} +')
|
||||
|
||||
# Save map
|
||||
map_path.write_text('\n'.join(output), encoding='utf-8')
|
||||
|
||||
|
||||
def get_ddrescue_settings(settings_menu) -> list:
|
||||
"""Get ddrescue settings from menu selections, returns list."""
|
||||
settings = []
|
||||
|
||||
# Check menu selections
|
||||
for name, details in settings_menu.options.items():
|
||||
if name == '--skip-size':
|
||||
continue
|
||||
if details['Selected']:
|
||||
if 'Value' in details:
|
||||
settings.append(f'{name}={details["Value"]}')
|
||||
else:
|
||||
settings.append(name)
|
||||
|
||||
# Done
|
||||
return settings
|
||||
|
||||
|
||||
def finalize_recovery(state: State, dry_run: bool = True) -> None:
|
||||
"""Show recovery finalization options and run selected functions."""
|
||||
options = (
|
||||
'Relocate Backup GPT',
|
||||
'Zero-fill Gaps',
|
||||
'Zero-fill Extra Space',
|
||||
)
|
||||
|
||||
# Get destination size
|
||||
dest_size = -1
|
||||
if hasattr(state.destination, 'size'):
|
||||
# hw_disk.Disk
|
||||
dest_size = state.destination.size
|
||||
if hasattr(state.destination, 'stat'):
|
||||
# pathlib.Path
|
||||
dest_size = state.destination.stat().st_size
|
||||
|
||||
# Run checks to disable item(s)
|
||||
whole_disk = bool(
|
||||
len(state.block_pairs) == 1
|
||||
and not state.source.parent
|
||||
)
|
||||
disable_gpt_option = not bool(
|
||||
## Breakdown of below tests:
|
||||
## Only offer this option when cloning a whole, non-child device
|
||||
## where the source is using a GUID_Partition_Table
|
||||
## and the source is smaller than the destination
|
||||
whole_disk
|
||||
and str(state.source.raw_details.get('pttype', 'Unknown')).lower() == 'gpt'
|
||||
and state.source.size < dest_size
|
||||
)
|
||||
|
||||
# Build menu
|
||||
menu = cli.Menu(title=ansi.color_string('ddrescue TUI: Finalization', 'GREEN'))
|
||||
menu.separator = ' '
|
||||
menu.add_action('Start')
|
||||
menu.add_action('Quit')
|
||||
for name in options:
|
||||
details = {'Selected': True}
|
||||
if 'GPT' in name and disable_gpt_option:
|
||||
details['Disabled'] = True
|
||||
details['Selected'] = False
|
||||
if state.mode == 'Image':
|
||||
details['Disabled'] = True
|
||||
details['Selected'] = False
|
||||
menu.add_option(name, details)
|
||||
|
||||
# Show menu
|
||||
selection = menu.advanced_select()
|
||||
if 'Quit' in selection:
|
||||
return
|
||||
|
||||
# Run functions
|
||||
if menu.options['Zero-fill Gaps']['Selected']:
|
||||
zero_fill_gaps(
|
||||
state,
|
||||
dest_size=dest_size,
|
||||
dry_run=dry_run,
|
||||
extend_to_end=menu.options['Zero-fill Extra Space']['Selected'],
|
||||
)
|
||||
if menu.options['Relocate Backup GPT']['Selected']:
|
||||
# NOTE: This needs to be run last to avoid corrupting/erasing the backup GPT
|
||||
relocate_backup_gpt(state, dry_run=dry_run)
|
||||
|
||||
|
||||
def is_missing_source_or_destination(state) -> bool:
|
||||
"""Check if source or destination dissapeared, returns bool."""
|
||||
missing = False
|
||||
items = {
|
||||
'Source': state.source,
|
||||
'Destination': state.destination,
|
||||
}
|
||||
|
||||
# Check items
|
||||
for name, item in items.items():
|
||||
if not item:
|
||||
continue
|
||||
if hasattr(item, 'path'):
|
||||
if not item.path.exists():
|
||||
missing = True
|
||||
cli.print_error(f'{name} disappeared')
|
||||
elif hasattr(item, 'exists'):
|
||||
if not item.exists():
|
||||
missing = True
|
||||
cli.print_error(f'{name} disappeared')
|
||||
else:
|
||||
LOG.error('Unknown %s type: %s', name, item)
|
||||
|
||||
# Update top panes
|
||||
state.update_top_panes()
|
||||
|
||||
# Done
|
||||
return missing
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main function for ddrescue TUI."""
|
||||
try:
|
||||
args = argparse_helper()
|
||||
except SystemExit:
|
||||
print('')
|
||||
cli.pause('Press Enter to exit...')
|
||||
raise
|
||||
|
||||
# Log setup
|
||||
log_path = log.format_log_path(log_name='main', sub_dir='ddrescue-TUI')
|
||||
log.update_log_path(
|
||||
dest_dir=log_path.parent,
|
||||
dest_name=log_path.stem,
|
||||
keep_history=False,
|
||||
timestamp=False,
|
||||
)
|
||||
LOG.info('ddrescue-tui Start')
|
||||
|
||||
# Check if running inside tmux
|
||||
if 'TMUX' not in os.environ:
|
||||
LOG.error('tmux session not found')
|
||||
raise RuntimeError('tmux session not found')
|
||||
|
||||
# Init
|
||||
state = State(log_dir=log_path.parent)
|
||||
try:
|
||||
state.init_recovery(args)
|
||||
except (FileNotFoundError, std.GenericAbort):
|
||||
is_missing_source_or_destination(state)
|
||||
cli.abort()
|
||||
|
||||
# Show menu
|
||||
main_menu = menus.main()
|
||||
settings_menu = menus.settings(state.mode)
|
||||
while True:
|
||||
selection = main_menu.advanced_select()
|
||||
|
||||
# Change settings
|
||||
if 'Change settings' in selection[0]:
|
||||
while True:
|
||||
selection = settings_menu.settings_select()
|
||||
if 'Load Preset' in selection:
|
||||
# Rebuild settings menu using preset
|
||||
settings_menu = menus.settings(state.mode, silent=False)
|
||||
else:
|
||||
break
|
||||
|
||||
# Detect drives
|
||||
if 'Detect drives' in selection[0]:
|
||||
cli.clear_screen()
|
||||
cli.print_warning(DETECT_DRIVES_NOTICE)
|
||||
if cli.ask('Are you sure you proceed?'):
|
||||
cli.print_standard('Forcing controllers to rescan for devices...')
|
||||
cmd = 'echo "- - -" | sudo tee /sys/class/scsi_host/host*/scan'
|
||||
exe.run_program([cmd], check=False, shell=True)
|
||||
if source_or_destination_changed(state):
|
||||
cli.abort()
|
||||
|
||||
# Start recovery
|
||||
if 'Start' in selection:
|
||||
cli.clear_screen()
|
||||
run_recovery(state, main_menu, settings_menu, dry_run=args['--dry-run'])
|
||||
|
||||
# Quit
|
||||
if 'Quit' in selection:
|
||||
total_percent = state.get_percent_recovered()
|
||||
|
||||
# Confirm exit if recovery is less than 100%
|
||||
if total_percent < 100:
|
||||
cli.print_warning('Recovery is less than 100%')
|
||||
if not cli.ask('Are you sure you want to quit?'):
|
||||
continue
|
||||
|
||||
finalize_recovery(state, dry_run=args['--dry-run'])
|
||||
break
|
||||
|
||||
# Save results to log
|
||||
LOG.info('')
|
||||
for line in state.generate_report():
|
||||
LOG.info(' %s', ansi.strip_colors(line))
|
||||
|
||||
|
||||
def relocate_backup_gpt(state: State, dry_run: bool = True) -> None:
|
||||
"""Relocate backup GPT on the destination if applicable and approved."""
|
||||
cmd = ['sudo', 'sfdisk', '--relocate', 'gpt-bak-std', state.destination.path]
|
||||
state.destination.update_details(skip_children=False)
|
||||
|
||||
# Safety checks
|
||||
## Breakdown of below tests:
|
||||
## Only offer this option when cloning a whole, non-child device
|
||||
## where the source is smaller than the destination
|
||||
## and both the source and destination are using a GUID_Partition_Table
|
||||
if not (
|
||||
len(state.block_pairs) == 1
|
||||
and str(state.destination.raw_details.get('pttype', 'Unknown')).lower() == 'gpt'
|
||||
and state.source.size < state.destination.size
|
||||
and not state.source.parent
|
||||
and str(state.source.raw_details.get('pttype', 'Unknown')).lower() == 'gpt'
|
||||
):
|
||||
LOG.warning('Refusing to attempt a backup GPT relocation.')
|
||||
return
|
||||
|
||||
# Dry run
|
||||
if dry_run:
|
||||
cli.print_standard(f'Dry-run: Relocate GPT with command: {cmd}')
|
||||
return
|
||||
|
||||
# Relocate GPT data
|
||||
proc = exe.run_program(cmd, check=False)
|
||||
if proc.returncode:
|
||||
cli.print_error('ERROR: Failed to relocate backup GPT.')
|
||||
LOG.error('sfdisk result: %s, %s', proc.stdout, proc.stderr)
|
||||
|
||||
|
||||
def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None:
|
||||
"""Run ddrescue using passed settings."""
|
||||
cmd = build_ddrescue_cmd(block_pair, pass_name, settings)
|
||||
poweroff_source_after_idle = True
|
||||
state.update_progress_pane('Active')
|
||||
state.ui.clear_current_pane()
|
||||
state.ui.clear_on_resize = True
|
||||
warning_message = ''
|
||||
|
||||
def _poweroff_source_drive(idle_minutes) -> None:
|
||||
"""Power off source drive after a while."""
|
||||
source_dev = state.source.path
|
||||
|
||||
# Bail early
|
||||
if PLATFORM == 'Darwin':
|
||||
return
|
||||
|
||||
# Sleep
|
||||
for i in range(1, idle_minutes*60, 1):
|
||||
if not poweroff_source_after_idle:
|
||||
# Countdown canceled, exit without powering-down drives
|
||||
return
|
||||
if i % 60 == 0:
|
||||
cli.print_warning(
|
||||
f'Powering off source in {int((idle_minutes*60-i)/60)} minutes...',
|
||||
)
|
||||
std.sleep(1)
|
||||
|
||||
# Power off drive
|
||||
cmd = ['sudo', 'hdparm', '-Y', source_dev]
|
||||
proc = exe.run_program(cmd, check=False)
|
||||
if proc.returncode:
|
||||
cli.print_error(f'Failed to poweroff source {source_dev}')
|
||||
else:
|
||||
cli.print_warning(f'Powered off source {source_dev}')
|
||||
cli.print_standard(
|
||||
'Press Enter to return to main menu...', end='', flush=True,
|
||||
)
|
||||
|
||||
def _update_smart_panes() -> None:
|
||||
"""Update SMART panes every 30 seconds."""
|
||||
now = datetime.datetime.now(tz=TIMEZONE).strftime('%Y-%m-%d %H:%M %Z')
|
||||
for dev_str in ('source', 'destination'):
|
||||
dev = getattr(state, dev_str)
|
||||
|
||||
# Safety check
|
||||
if not hasattr(dev, 'attributes'):
|
||||
continue
|
||||
|
||||
# Update SMART data
|
||||
out_path = f'{state.log_dir}/smart_{dev_str}.out'
|
||||
update_smart_details(dev)
|
||||
with open(out_path, 'w', encoding='utf-8') as _f:
|
||||
_f.write(
|
||||
ansi.color_string(
|
||||
['SMART Attributes', f'Updated: {now}\n'],
|
||||
['BLUE', 'YELLOW'],
|
||||
sep='\t\t',
|
||||
),
|
||||
)
|
||||
_f.write('\n'.join(dev.generate_report(header=False)))
|
||||
|
||||
# Dry run
|
||||
if dry_run:
|
||||
LOG.info('ddrescue cmd: %s', cmd)
|
||||
return
|
||||
|
||||
# Start ddrescue and ddrescueview (if enabled)
|
||||
proc = exe.popen_program(cmd)
|
||||
if (
|
||||
block_pair.view_map
|
||||
and (not block_pair.view_proc or block_pair.view_proc.poll() is not None)
|
||||
):
|
||||
block_pair.view_proc = exe.popen_program(
|
||||
['ddrescueview', '-r', '5s', block_pair.map_path],
|
||||
pipe=True,
|
||||
)
|
||||
|
||||
# ddrescue loop
|
||||
_i = 0
|
||||
while True:
|
||||
if _i % 30 == 0:
|
||||
# Update SMART pane
|
||||
_update_smart_panes()
|
||||
|
||||
# Check destination
|
||||
warning_message = check_destination_health(state.destination)
|
||||
if warning_message:
|
||||
# Error detected on destination, stop recovery
|
||||
proc.terminate()
|
||||
cli.print_error(warning_message)
|
||||
break
|
||||
_i += 1
|
||||
|
||||
# Update progress
|
||||
block_pair.update_progress(pass_name)
|
||||
state.update_progress_pane('Active')
|
||||
|
||||
# Check if complete
|
||||
try:
|
||||
proc.wait(timeout=1)
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
# Wait a bit to let ddrescue exit safely
|
||||
LOG.warning('ddrescue stopped by user')
|
||||
warning_message = 'Aborted'
|
||||
std.sleep(2)
|
||||
proc.terminate()
|
||||
break
|
||||
except subprocess.TimeoutExpired:
|
||||
# Continue to next loop to update panes
|
||||
pass
|
||||
else:
|
||||
# Done
|
||||
std.sleep(1)
|
||||
break
|
||||
|
||||
# Update progress
|
||||
# NOTE: Using 'Active' here to avoid flickering between block pairs
|
||||
block_pair.update_progress(pass_name)
|
||||
state.update_progress_pane('Active')
|
||||
state.ui.clear_on_resize = False
|
||||
|
||||
# Check result
|
||||
if proc.poll():
|
||||
# True if return code is non-zero (poll() returns None if still running)
|
||||
poweroff_thread = exe.start_thread(
|
||||
_poweroff_source_drive,
|
||||
[cfg.ddrescue.DRIVE_POWEROFF_TIMEOUT],
|
||||
)
|
||||
warning_message = 'Error(s) encountered, see message above'
|
||||
state.update_top_panes()
|
||||
if warning_message:
|
||||
cli.print_standard(' ')
|
||||
cli.print_standard(' ')
|
||||
cli.print_error('DDRESCUE PROCESS HALTED')
|
||||
cli.print_standard(' ')
|
||||
cli.print_warning(warning_message)
|
||||
|
||||
# Needs attention?
|
||||
if str(proc.poll()) != '0':
|
||||
state.update_progress_pane('NEEDS ATTENTION')
|
||||
cli.pause('Press Enter to return to main menu...')
|
||||
|
||||
# Stop source poweroff countdown
|
||||
cli.print_standard('Stopping device poweroff countdown...', flush=True)
|
||||
poweroff_source_after_idle = False
|
||||
poweroff_thread.join() # type: ignore[reportUnboundVariable]
|
||||
|
||||
# Done
|
||||
raise std.GenericAbort()
|
||||
|
||||
|
||||
def run_recovery(state: State, main_menu, settings_menu, dry_run=True) -> None:
|
||||
"""Run recovery passes."""
|
||||
atexit.register(state.save_debug_reports)
|
||||
attempted_recovery = False
|
||||
auto_continue = False
|
||||
|
||||
# Bail early
|
||||
if is_missing_source_or_destination(state):
|
||||
cli.print_standard('')
|
||||
cli.pause('Press Enter to return to main menu...')
|
||||
return
|
||||
if source_or_destination_changed(state):
|
||||
cli.print_standard('')
|
||||
cli.abort()
|
||||
|
||||
# Get settings
|
||||
for name, details in main_menu.toggles.items():
|
||||
if 'Auto continue' in name and details['Selected']:
|
||||
auto_continue = True
|
||||
if 'Retry' in name and details['Selected']:
|
||||
details['Selected'] = False
|
||||
state.retry_all_passes()
|
||||
|
||||
# Start SMART/Journal
|
||||
state.ui.add_info_pane(
|
||||
percent=50,
|
||||
update_layout=False,
|
||||
watch_file=f'{state.log_dir}/smart_source.out',
|
||||
)
|
||||
if hasattr(state.destination, 'attributes'):
|
||||
state.ui.add_info_pane(
|
||||
percent=50,
|
||||
update_layout=False,
|
||||
watch_file=f'{state.log_dir}/smart_destination.out',
|
||||
)
|
||||
if PLATFORM == 'Linux':
|
||||
state.ui.add_worker_pane(lines=4, cmd='journal-datarec-monitor')
|
||||
state.ui.set_current_pane_height(DDRESCUE_OUTPUT_HEIGHT)
|
||||
|
||||
# Run pass(es)
|
||||
for pass_name in ('read-skip', 'read-full', 'trim', 'scrape'):
|
||||
abort = False
|
||||
|
||||
# Skip to next pass
|
||||
if state.pass_complete(pass_name):
|
||||
# NOTE: This bypasses auto_continue
|
||||
state.skip_pass(pass_name)
|
||||
continue
|
||||
|
||||
# Run ddrescue
|
||||
for pair in state.block_pairs:
|
||||
if not pair.pass_complete(pass_name):
|
||||
attempted_recovery = True
|
||||
state.mark_started()
|
||||
try:
|
||||
run_ddrescue(state, pair, pass_name, settings_menu, dry_run=dry_run)
|
||||
except (FileNotFoundError, KeyboardInterrupt, std.GenericAbort):
|
||||
is_missing_source_or_destination(state)
|
||||
abort = True
|
||||
break
|
||||
|
||||
# Continue or return to menu
|
||||
all_complete = state.pass_complete(pass_name)
|
||||
all_above_threshold = state.pass_above_threshold(pass_name)
|
||||
if abort or not (all_complete and all_above_threshold and auto_continue):
|
||||
LOG.warning('Recovery halted')
|
||||
break
|
||||
|
||||
# Stop SMART/Journal
|
||||
state.ui.remove_all_info_panes()
|
||||
state.ui.remove_all_worker_panes()
|
||||
state.ui.clear_current_pane_height()
|
||||
|
||||
# Show warning if nothing was done
|
||||
if not attempted_recovery:
|
||||
cli.print_warning('No actions performed')
|
||||
cli.print_standard(' ')
|
||||
cli.pause('Press Enter to return to main menu...')
|
||||
|
||||
# Done
|
||||
state.save_debug_reports()
|
||||
atexit.unregister(state.save_debug_reports)
|
||||
state.update_progress_pane('Idle')
|
||||
|
||||
|
||||
def source_or_destination_changed(state) -> bool:
|
||||
"""Verify the source and destination objects are still valid."""
|
||||
changed = False
|
||||
|
||||
# Compare objects
|
||||
for obj in (state.source, state.destination):
|
||||
if not obj:
|
||||
changed = True
|
||||
elif hasattr(obj, 'exists'):
|
||||
# Assuming dest path
|
||||
changed = changed or not obj.exists()
|
||||
elif isinstance(obj, hw_disk.Disk):
|
||||
compare_dev = hw_disk.Disk(obj.path)
|
||||
for key in ('model', 'serial'):
|
||||
changed = changed or getattr(obj, key) != getattr(compare_dev, key)
|
||||
|
||||
# Update top panes
|
||||
state.update_top_panes()
|
||||
|
||||
# Done
|
||||
if changed:
|
||||
cli.print_error('Source and/or Destination changed')
|
||||
return changed
|
||||
|
||||
|
||||
def zero_fill_gaps(
|
||||
state: State,
|
||||
dest_size: int,
|
||||
dry_run: bool = True,
|
||||
extend_to_end: bool = False,
|
||||
) -> None:
|
||||
"""Zero-fill any gaps on the destination."""
|
||||
#fake_settings_menu = menus.settings(state.mode)
|
||||
full_disk_clone = bool(
|
||||
state.mode == 'Clone'
|
||||
and len(state.block_pairs) == 1
|
||||
and state.source.path == state.block_pairs[0].source
|
||||
)
|
||||
larger_destination = state.source.size < dest_size
|
||||
percent_recovered = state.get_percent_recovered()
|
||||
|
||||
# Bail early
|
||||
if percent_recovered == 100 and not (larger_destination and extend_to_end):
|
||||
return
|
||||
|
||||
for block_pair in state.block_pairs:
|
||||
domain_size = block_pair.size
|
||||
if (full_disk_clone and state.source.size < state.destination.size):
|
||||
domain_size = dest_size
|
||||
larger_destination = True
|
||||
|
||||
# Prep zero-fill map file
|
||||
zero_map_path = block_pair.map_path.with_stem(
|
||||
f'{block_pair.map_path.stem}_zero-fill',
|
||||
)
|
||||
io.copy_file(block_pair.map_path, zero_map_path, overwrite=True)
|
||||
mark_non_recovered_as_non_tried(zero_map_path)
|
||||
if full_disk_clone and larger_destination and extend_to_end:
|
||||
# Extend domain size
|
||||
with open(zero_map_path, 'a', encoding='utf-8') as f:
|
||||
f.write(
|
||||
f'\n{hex(block_pair.size)} '
|
||||
f'{hex(domain_size - block_pair.size)} ?'
|
||||
)
|
||||
|
||||
# Build cmd
|
||||
cmd = [
|
||||
'sudo',
|
||||
'ddrescue',
|
||||
'--force',
|
||||
f'--size={domain_size}',
|
||||
'--binary-prefixes',
|
||||
'--complete-only',
|
||||
'--data-preview=5',
|
||||
'--odirect',
|
||||
'--retry-passes=0',
|
||||
f'--sector-size={block_pair.sector_size}',
|
||||
'-vvvv',
|
||||
'/dev/zero',
|
||||
block_pair.destination,
|
||||
zero_map_path,
|
||||
]
|
||||
|
||||
# Dry run
|
||||
if dry_run:
|
||||
cli.print_standard(f'Zero-fill with command: {cmd}')
|
||||
return
|
||||
|
||||
# Re-run ddrescue to zero-fill gaps
|
||||
proc = exe.run_program(cmd, check=False, pipe=False)
|
||||
if proc.returncode:
|
||||
cli.print_error('ERROR: Failed to zero-fill: {block_pair.destination}')
|
||||
LOG.error('zero-fill error: %s, %s', proc.stdout, proc.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
"""WizardKit: ddrescue TUI - State"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import atexit
|
||||
import logging
|
||||
import pathlib
|
||||
import plistlib
|
||||
import re
|
||||
|
||||
from wk import exe
|
||||
from wk.std import PLATFORM
|
||||
from wk.ui import cli
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Functions
|
||||
def mount_raw_image(path) -> pathlib.Path:
|
||||
"""Mount raw image using OS specific methods, returns pathlib.Path."""
|
||||
loopback_path = None
|
||||
|
||||
if PLATFORM == 'Darwin':
|
||||
loopback_path = mount_raw_image_macos(path)
|
||||
elif PLATFORM == 'Linux':
|
||||
loopback_path = mount_raw_image_linux(path)
|
||||
|
||||
# Check
|
||||
if not loopback_path:
|
||||
cli.print_error(f'Failed to mount image: {path}')
|
||||
|
||||
# Register unmount atexit
|
||||
atexit.register(unmount_loopback_device, loopback_path)
|
||||
|
||||
# Done
|
||||
return loopback_path
|
||||
|
||||
|
||||
def mount_raw_image_linux(path) -> pathlib.Path:
|
||||
"""Mount raw image using losetup, returns pathlib.Path."""
|
||||
loopback_path = None
|
||||
|
||||
# Mount using losetup
|
||||
cmd = [
|
||||
'sudo',
|
||||
'losetup',
|
||||
'--find',
|
||||
'--partscan',
|
||||
'--show',
|
||||
path,
|
||||
]
|
||||
proc = exe.run_program(cmd, check=False)
|
||||
|
||||
# Check result
|
||||
if proc.returncode == 0:
|
||||
loopback_path = proc.stdout.strip()
|
||||
|
||||
# Done
|
||||
return loopback_path
|
||||
|
||||
|
||||
def mount_raw_image_macos(path) -> pathlib.Path:
|
||||
"""Mount raw image using hdiutil, returns pathlib.Path."""
|
||||
loopback_path = None
|
||||
plist_data = {}
|
||||
|
||||
# Mount using hdiutil
|
||||
# plistdata['system-entities'][{}...]
|
||||
cmd = [
|
||||
'hdiutil', 'attach',
|
||||
'-imagekey', 'diskimage-class=CRawDiskImage',
|
||||
'-nomount',
|
||||
'-plist',
|
||||
'-readonly',
|
||||
path,
|
||||
]
|
||||
proc = exe.run_program(cmd, check=False, encoding=None, errors=None)
|
||||
|
||||
# Check result
|
||||
try:
|
||||
plist_data = plistlib.loads(proc.stdout)
|
||||
except plistlib.InvalidFileException:
|
||||
return None
|
||||
for dev in plist_data.get('system-entities', []):
|
||||
dev_path = dev.get('dev-entry', '')
|
||||
if re.match(r'^/dev/disk\d+$', dev_path):
|
||||
loopback_path = dev_path
|
||||
|
||||
# Done
|
||||
return loopback_path
|
||||
|
||||
|
||||
def unmount_loopback_device(path) -> None:
|
||||
"""Unmount loopback device using OS specific methods."""
|
||||
cmd = []
|
||||
|
||||
# Build OS specific cmd
|
||||
if PLATFORM == 'Darwin':
|
||||
cmd = ['hdiutil', 'detach', path]
|
||||
elif PLATFORM == 'Linux':
|
||||
cmd = ['sudo', 'losetup', '--detach', path]
|
||||
|
||||
# Unmount loopback device
|
||||
exe.run_program(cmd, check=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,273 +0,0 @@
|
|||
"""WizardKit: ddrescue TUI - Menus"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
import pathlib
|
||||
|
||||
from wk.cfg.ddrescue import DDRESCUE_SETTINGS
|
||||
from wk.hw.disk import Disk, get_disks
|
||||
from wk.std import GenericAbort, PLATFORM, bytes_to_string
|
||||
from wk.ui import ansi, cli
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
CLONE_SETTINGS = {
|
||||
'Source': None,
|
||||
'Destination': None,
|
||||
'Create Boot Partition': False,
|
||||
'First Run': True,
|
||||
'Needs Format': False,
|
||||
'Table Type': None,
|
||||
'Partition Mapping': [
|
||||
# (5, 1) ## Clone source partition #5 to destination partition #1
|
||||
],
|
||||
}
|
||||
if PLATFORM == 'Darwin':
|
||||
# TODO: Direct I/O needs more testing under macOS
|
||||
DDRESCUE_SETTINGS['Default']['--idirect'] = {'Selected': False, 'Hidden': True}
|
||||
DDRESCUE_SETTINGS['Default']['--odirect'] = {'Selected': False, 'Hidden': True}
|
||||
MENU_ACTIONS = (
|
||||
'Start',
|
||||
f'Change settings {ansi.color_string("(experts only)", "YELLOW")}',
|
||||
f'Detect drives {ansi.color_string("(experts only)", "YELLOW")}',
|
||||
'Quit')
|
||||
MENU_TOGGLES = {
|
||||
'Auto continue (if recovery % over threshold)': True,
|
||||
'Retry (mark non-rescued sectors "non-tried")': False,
|
||||
}
|
||||
SETTING_PRESETS = (
|
||||
'Default',
|
||||
'Fast',
|
||||
'Safe',
|
||||
)
|
||||
|
||||
|
||||
# Functions
|
||||
def main() -> cli.Menu:
|
||||
"""Main menu, returns wk.ui.cli.Menu."""
|
||||
menu = cli.Menu(title=ansi.color_string('ddrescue TUI: Main Menu', 'GREEN'))
|
||||
menu.separator = ' '
|
||||
|
||||
# Add actions, options, etc
|
||||
for action in MENU_ACTIONS:
|
||||
if not (PLATFORM == 'Darwin' and 'Detect drives' in action):
|
||||
menu.add_action(action)
|
||||
for toggle, selected in MENU_TOGGLES.items():
|
||||
menu.add_toggle(toggle, {'Selected': selected})
|
||||
|
||||
# Done
|
||||
return menu
|
||||
|
||||
|
||||
def settings(mode: str, silent: bool = True) -> cli.Menu:
|
||||
"""Settings menu, returns wk.ui.cli.Menu."""
|
||||
title_text = [
|
||||
ansi.color_string('ddrescue TUI: Expert Settings', 'GREEN'),
|
||||
' ',
|
||||
ansi.color_string(
|
||||
['These settings can cause', 'MAJOR DAMAGE', 'to drives'],
|
||||
['YELLOW', 'RED', 'YELLOW'],
|
||||
),
|
||||
'Please read the manual before making changes',
|
||||
]
|
||||
menu = cli.Menu(title='\n'.join(title_text))
|
||||
menu.separator = ' '
|
||||
preset = 'Default'
|
||||
if not silent:
|
||||
# Ask which preset to use
|
||||
cli.print_standard(
|
||||
f'Available ddrescue presets: {" / ".join(SETTING_PRESETS)}'
|
||||
)
|
||||
preset = cli.choice('Please select a preset:', SETTING_PRESETS)
|
||||
|
||||
# Fix selection
|
||||
for _p in SETTING_PRESETS:
|
||||
if _p.startswith(preset):
|
||||
preset = _p
|
||||
|
||||
# Add default settings
|
||||
menu.add_action('Load Preset')
|
||||
menu.add_action('Main Menu')
|
||||
for name, details in DDRESCUE_SETTINGS['Default'].items():
|
||||
menu.add_option(name, details.copy())
|
||||
|
||||
# Update settings using preset
|
||||
if preset != 'Default':
|
||||
for name, details in DDRESCUE_SETTINGS[preset].items():
|
||||
menu.options[name].update(details.copy())
|
||||
|
||||
# Disable direct output when saving to an image
|
||||
if mode == 'Image':
|
||||
menu.options['--odirect']['Disabled'] = True
|
||||
menu.options['--odirect']['Selected'] = False
|
||||
|
||||
# Done
|
||||
return menu
|
||||
|
||||
|
||||
def disks() -> cli.Menu:
|
||||
"""Disk menu, returns wk.ui.cli.Menu()."""
|
||||
cli.print_info('Scanning disks...')
|
||||
available_disks = get_disks()
|
||||
menu = cli.Menu('ddrescue TUI: Disk selection')
|
||||
menu.disabled_str = 'Already selected'
|
||||
menu.separator = ' '
|
||||
menu.add_action('Quit')
|
||||
for disk in available_disks:
|
||||
menu.add_option(
|
||||
name=(
|
||||
f'{str(disk.path):<12} '
|
||||
f'{disk.bus:<5} '
|
||||
f'{bytes_to_string(disk.size, decimals=1, use_binary=False):<8} '
|
||||
f'{disk.model} '
|
||||
f'{disk.serial}'
|
||||
),
|
||||
details={'Object': disk},
|
||||
)
|
||||
|
||||
# Done
|
||||
return menu
|
||||
|
||||
|
||||
def select_disk(prompt_msg: str, menu: cli.Menu) -> Disk:
|
||||
"""Select disk from provided Menu, returns Disk()."""
|
||||
menu.title = ansi.color_string(
|
||||
f'ddrescue TUI: {prompt_msg} Selection', 'GREEN',
|
||||
)
|
||||
|
||||
# Get selection
|
||||
selection = menu.simple_select()
|
||||
if 'Quit' in selection:
|
||||
raise GenericAbort()
|
||||
|
||||
# Disable selected disk's menu entry
|
||||
menu.options[selection[0]]['Disabled'] = True
|
||||
|
||||
# Update details to include child devices
|
||||
selected_disk = selection[-1]['Object']
|
||||
selected_disk.update_details(skip_children=False)
|
||||
|
||||
# Done
|
||||
return selected_disk
|
||||
|
||||
|
||||
def select_disk_parts(prompt_msg, disk) -> list[Disk]:
|
||||
"""Select disk parts from list, returns list of Disk()."""
|
||||
title = ansi.color_string('ddrescue TUI: Partition Selection', 'GREEN')
|
||||
title += f'\n\nDisk: {disk.path} {disk.description}'
|
||||
menu = cli.Menu(title)
|
||||
menu.separator = ' '
|
||||
menu.add_action('All')
|
||||
menu.add_action('None')
|
||||
menu.add_action('Proceed', {'Separator': True})
|
||||
menu.add_action('Quit')
|
||||
object_list = []
|
||||
|
||||
def _select_parts(menu) -> None:
|
||||
"""Loop over selection menu until at least one partition selected."""
|
||||
while True:
|
||||
selection = menu.advanced_select(
|
||||
f'Please select the parts to {prompt_msg.lower()}: ',
|
||||
)
|
||||
if 'All' in selection:
|
||||
for option in menu.options.values():
|
||||
option['Selected'] = True
|
||||
elif 'None' in selection:
|
||||
for option in menu.options.values():
|
||||
option['Selected'] = False
|
||||
elif 'Proceed' in selection:
|
||||
if any(option['Selected'] for option in menu.options.values()):
|
||||
# At least one partition/device selected/device selected
|
||||
break
|
||||
elif 'Quit' in selection:
|
||||
raise GenericAbort()
|
||||
|
||||
# Bail early if running under macOS
|
||||
if PLATFORM == 'Darwin':
|
||||
return [disk]
|
||||
|
||||
# Bail early if child device selected
|
||||
if disk.parent:
|
||||
return [disk]
|
||||
|
||||
# Add parts
|
||||
whole_disk_str = f'{str(disk.path):<14} (Whole device)'
|
||||
for part in disk.children:
|
||||
fstype = part.get('fstype', '')
|
||||
fstype = str(fstype) if fstype else ''
|
||||
size = part["size"]
|
||||
name = (
|
||||
f'{str(part["path"]):<14} '
|
||||
f'{fstype.upper():<5} '
|
||||
f'({bytes_to_string(size, decimals=1, use_binary=True):>6})'
|
||||
)
|
||||
menu.add_option(name, details={'Selected': True, 'pathlib.Path': part['path']})
|
||||
|
||||
# Add whole disk if necessary
|
||||
if not menu.options:
|
||||
menu.add_option(whole_disk_str, {'Selected': True, 'pathlib.Path': disk.path})
|
||||
menu.title += '\n\n'
|
||||
menu.title += ansi.color_string(' No partitions detected.', 'YELLOW')
|
||||
|
||||
# Get selection
|
||||
_select_parts(menu)
|
||||
|
||||
# Build list of Disk() object_list
|
||||
for option in menu.options.values():
|
||||
if option['Selected']:
|
||||
object_list.append(option['pathlib.Path'])
|
||||
|
||||
# Check if whole disk selected
|
||||
if len(object_list) == len(disk.children):
|
||||
# NOTE: This is not true if the disk has no partitions
|
||||
msg = f'Preserve partition table and unused space in {prompt_msg.lower()}?'
|
||||
if cli.ask(msg):
|
||||
# Replace part list with whole disk obj
|
||||
object_list = [disk.path]
|
||||
|
||||
# Convert object_list to Disk() objects
|
||||
cli.print_standard(' ')
|
||||
cli.print_info('Getting disk/partition details...')
|
||||
object_list = [Disk(path) for path in object_list]
|
||||
|
||||
# Done
|
||||
return object_list
|
||||
|
||||
|
||||
def select_path(prompt_msg) -> pathlib.Path:
|
||||
"""Select path, returns pathlib.Path."""
|
||||
invalid = False
|
||||
menu = cli.Menu(
|
||||
title=ansi.color_string(f'ddrescue TUI: {prompt_msg} Path Selection', 'GREEN'),
|
||||
)
|
||||
menu.separator = ' '
|
||||
menu.add_action('Quit')
|
||||
menu.add_option('Current directory')
|
||||
menu.add_option('Enter manually')
|
||||
path = pathlib.Path.cwd()
|
||||
|
||||
# Make selection
|
||||
selection = menu.simple_select()
|
||||
if 'Current directory' in selection:
|
||||
pass
|
||||
elif 'Enter manually' in selection:
|
||||
path = pathlib.Path(cli.input_text('Please enter path: '))
|
||||
elif 'Quit' in selection:
|
||||
raise GenericAbort()
|
||||
|
||||
# Check
|
||||
try:
|
||||
path = path.resolve()
|
||||
except TypeError:
|
||||
invalid = True
|
||||
if invalid or not path.is_dir():
|
||||
cli.print_error(f'Invalid path: {path}')
|
||||
raise GenericAbort()
|
||||
|
||||
# Done
|
||||
return path
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
"""WizardKit: Debug Functions"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import inspect
|
||||
import logging
|
||||
import lzma
|
||||
import os
|
||||
import pathlib
|
||||
import pickle
|
||||
import platform
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
|
||||
from wk.cfg.net import CRASH_SERVER
|
||||
from wk.log import get_root_logger_path
|
||||
|
||||
# Classes
|
||||
class Debug():
|
||||
"""Object used when dumping debug data."""
|
||||
def method(self) -> None:
|
||||
"""Dummy method used to identify functions vs data."""
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
DEBUG_CLASS = Debug()
|
||||
METHOD_TYPE = type(DEBUG_CLASS.method)
|
||||
|
||||
|
||||
# Functions
|
||||
def generate_debug_report() -> str:
|
||||
"""Generate debug report, returns str."""
|
||||
platform_function_list = (
|
||||
'architecture',
|
||||
'machine',
|
||||
'platform',
|
||||
'python_version',
|
||||
)
|
||||
report = []
|
||||
|
||||
# Logging data
|
||||
try:
|
||||
log_path = get_root_logger_path()
|
||||
except RuntimeError:
|
||||
# Assuming logging wasn't started
|
||||
pass
|
||||
else:
|
||||
report.append('------ Start Log -------')
|
||||
report.append('')
|
||||
with open(log_path, 'r', encoding='utf-8') as log_file:
|
||||
report.extend(log_file.read().splitlines())
|
||||
report.append('')
|
||||
report.append('------- End Log --------')
|
||||
|
||||
# System
|
||||
report.append('--- Start debug info ---')
|
||||
report.append('')
|
||||
report.append('[System]')
|
||||
report.append(f' {"FQDN":<24} {socket.getfqdn()}')
|
||||
for func in platform_function_list:
|
||||
func_name = func.replace('_', ' ').capitalize()
|
||||
func_result = getattr(platform, func)()
|
||||
report.append(f' {func_name:<24} {func_result}')
|
||||
report.append(f' {"Python sys.argv":<24} {sys.argv}')
|
||||
report.append('')
|
||||
|
||||
# Environment
|
||||
report.append('[Environment Variables]')
|
||||
for key, value in sorted(os.environ.items()):
|
||||
report.append(f' {key:<24} {value}')
|
||||
report.append('')
|
||||
|
||||
# Done
|
||||
report.append('---- End debug info ----')
|
||||
return '\n'.join(report)
|
||||
|
||||
|
||||
def generate_object_report(obj: Any, indent: int = 0) -> list[str]:
|
||||
"""Generate debug report for obj, returns list."""
|
||||
report = []
|
||||
attr_list = []
|
||||
|
||||
# Get attribute list
|
||||
if hasattr(obj, '__slots__'):
|
||||
attr_list = list(obj.__slots__)
|
||||
else:
|
||||
attr_list = [name for name in dir(obj) if not name.startswith('_')]
|
||||
|
||||
# Dump object data
|
||||
for name in attr_list:
|
||||
attr = getattr(obj, name)
|
||||
|
||||
# Skip methods
|
||||
if isinstance(attr, METHOD_TYPE):
|
||||
continue
|
||||
|
||||
# Add attribute to report (expanded if necessary)
|
||||
if isinstance(attr, dict):
|
||||
report.append(f'{name}:')
|
||||
for key, value in attr.items():
|
||||
report.append(f'{" "*(indent+1)}{key}: {str(value)}')
|
||||
else:
|
||||
report.append(f'{" "*indent}{name}: {str(attr)}')
|
||||
|
||||
# Done
|
||||
return report
|
||||
|
||||
|
||||
def save_pickles(
|
||||
obj_dict: dict[Any, Any],
|
||||
out_path: pathlib.Path | str | None = None,
|
||||
) -> None:
|
||||
"""Save dict of objects using pickle."""
|
||||
LOG.info('Saving pickles')
|
||||
|
||||
# Set path
|
||||
if not out_path:
|
||||
out_path = get_root_logger_path()
|
||||
out_path = out_path.parent.joinpath('../debug').resolve()
|
||||
|
||||
# Save pickles
|
||||
try:
|
||||
for name, obj in obj_dict.copy().items():
|
||||
if name.startswith('__') or inspect.ismodule(obj):
|
||||
continue
|
||||
with open(f'{out_path}/{name}.pickle', 'wb') as _f:
|
||||
pickle.dump(obj, _f, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
except Exception:
|
||||
LOG.error('Failed to save all the pickles', exc_info=True)
|
||||
|
||||
|
||||
def upload_debug_report(
|
||||
report: str,
|
||||
compress: bool = True,
|
||||
reason: str = 'DEBUG',
|
||||
) -> None:
|
||||
"""Upload debug report to CRASH_SERVER as specified in wk.cfg.main."""
|
||||
LOG.info('Uploading debug report to %s', CRASH_SERVER.get('Name', '?'))
|
||||
headers = CRASH_SERVER.get('Headers', {'X-Requested-With': 'XMLHttpRequest'})
|
||||
if compress:
|
||||
headers['Content-Type'] = 'application/octet-stream'
|
||||
|
||||
# Check if the required server details are available
|
||||
if not all(CRASH_SERVER.get(key, False) for key in ('Name', 'Url', 'User')):
|
||||
msg = 'Server details missing, aborting upload.'
|
||||
print(msg)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
# Set filename (based on the logging config if possible)
|
||||
filename = 'Unknown'
|
||||
try:
|
||||
log_path = get_root_logger_path()
|
||||
except RuntimeError:
|
||||
# Assuming logging wasn't started
|
||||
pass
|
||||
else:
|
||||
# Strip everything but the prefix
|
||||
filename = re.sub(r'^(.*)_(\d{4}-\d{2}-\d{2}.*)', r'\1', log_path.name)
|
||||
filename = f'{filename}_{reason}_{time.strftime("%Y-%m-%d_%H%M%S%z")}.log'
|
||||
LOG.debug('filename: %s', filename)
|
||||
|
||||
# Compress report
|
||||
if compress:
|
||||
filename += '.xz'
|
||||
xz_report = lzma.compress(report.encode('utf8'))
|
||||
|
||||
# Upload report
|
||||
url = f'{CRASH_SERVER["Url"]}/{filename}'
|
||||
response = requests.put(
|
||||
url,
|
||||
auth=(CRASH_SERVER['User'], CRASH_SERVER.get('Pass', '')),
|
||||
data=xz_report if compress else report,
|
||||
headers=headers,
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
# Check response
|
||||
if not response.ok:
|
||||
raise RuntimeError('Failed to upload report')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,325 +0,0 @@
|
|||
"""WizardKit: Execution functions"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from io import IOBase
|
||||
from queue import Queue, Empty
|
||||
from threading import Thread
|
||||
from typing import Any, Callable, Iterable
|
||||
|
||||
import psutil
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Classes
|
||||
class NonBlockingStreamReader():
|
||||
"""Class to allow non-blocking reads from a stream."""
|
||||
# Credits:
|
||||
## https://gist.github.com/EyalAr/7915597
|
||||
## https://stackoverflow.com/a/4896288
|
||||
|
||||
def __init__(self, stream: IOBase):
|
||||
self.stream: IOBase = stream
|
||||
self.queue: Queue = Queue()
|
||||
|
||||
def populate_queue(stream: IOBase, queue: Queue) -> None:
|
||||
"""Collect lines from stream and put them in queue."""
|
||||
while not stream.closed:
|
||||
try:
|
||||
line = stream.read(1)
|
||||
except ValueError:
|
||||
# Assuming the stream was closed
|
||||
line = None
|
||||
if line:
|
||||
queue.put(line)
|
||||
|
||||
self.thread = start_thread(
|
||||
populate_queue,
|
||||
args=(self.stream, self.queue),
|
||||
)
|
||||
|
||||
def stop(self) -> None:
|
||||
"""Stop reading from input stream."""
|
||||
self.stream.close()
|
||||
|
||||
def read(self, timeout: float | int | None = None) -> Any:
|
||||
"""Read from queue if possible, returns item from queue."""
|
||||
try:
|
||||
return self.queue.get(block=timeout is not None, timeout=timeout)
|
||||
except Empty:
|
||||
return None
|
||||
|
||||
def save_to_file(self, proc: subprocess.Popen, out_path: pathlib.Path | str) -> None:
|
||||
"""Continuously save output to file while proc is running."""
|
||||
LOG.debug('Saving process %s output to %s', proc, out_path)
|
||||
while proc.poll() is None:
|
||||
out = b''
|
||||
out_bytes = b''
|
||||
while out is not None:
|
||||
out = self.read(0.1)
|
||||
if out:
|
||||
out_bytes += out
|
||||
with open(out_path, 'a', encoding='utf-8') as _f:
|
||||
_f.write(out_bytes.decode('utf-8', errors='ignore'))
|
||||
|
||||
# Close stream to prevent 100% CPU usage
|
||||
self.stream.close()
|
||||
|
||||
|
||||
# Functions
|
||||
def build_cmd_kwargs(
|
||||
cmd: list[str],
|
||||
minimized: bool = False,
|
||||
pipe: bool = True,
|
||||
shell: bool = False,
|
||||
**kwargs) -> dict[str, Any]:
|
||||
"""Build kwargs for use by subprocess functions, returns dict.
|
||||
|
||||
Specifically subprocess.run() and subprocess.Popen().
|
||||
NOTE: If no encoding specified then UTF-8 will be used.
|
||||
"""
|
||||
LOG.debug(
|
||||
'cmd: %s, minimized: %s, pipe: %s, shell: %s, kwargs: %s',
|
||||
cmd, minimized, pipe, shell, kwargs,
|
||||
)
|
||||
cmd_kwargs = {
|
||||
'args': cmd,
|
||||
'shell': shell,
|
||||
}
|
||||
|
||||
# Strip sudo if appropriate
|
||||
if cmd[0] == 'sudo':
|
||||
if os.name == 'posix' and os.geteuid() == 0:
|
||||
cmd.pop(0)
|
||||
|
||||
# Add additional kwargs if applicable
|
||||
for key in 'check cwd encoding errors stderr stdin stdout'.split():
|
||||
if key in kwargs:
|
||||
cmd_kwargs[key] = kwargs[key]
|
||||
|
||||
# Default to UTF-8 encoding
|
||||
if not ('encoding' in cmd_kwargs or 'errors' in cmd_kwargs):
|
||||
cmd_kwargs['encoding'] = 'utf-8'
|
||||
cmd_kwargs['errors'] = 'ignore'
|
||||
|
||||
# Start minimized
|
||||
if minimized:
|
||||
startupinfo = subprocess.STARTUPINFO() # type: ignore
|
||||
startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW # type: ignore
|
||||
startupinfo.wShowWindow = 6
|
||||
cmd_kwargs['startupinfo'] = startupinfo
|
||||
|
||||
|
||||
# Pipe output
|
||||
if pipe:
|
||||
cmd_kwargs['stderr'] = subprocess.PIPE
|
||||
cmd_kwargs['stdout'] = subprocess.PIPE
|
||||
|
||||
# Done
|
||||
LOG.debug('cmd_kwargs: %s', cmd_kwargs)
|
||||
return cmd_kwargs
|
||||
|
||||
|
||||
def get_json_from_command(
|
||||
cmd: list[str],
|
||||
check: bool = True,
|
||||
encoding: str = 'utf-8',
|
||||
errors: str = 'ignore',
|
||||
) -> dict[Any, Any]:
|
||||
"""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 errors.
|
||||
"""
|
||||
LOG.debug('Loading JSON data from cmd: %s', cmd)
|
||||
json_data = {}
|
||||
|
||||
try:
|
||||
proc = run_program(cmd, check=check, encoding=encoding, errors=errors)
|
||||
json_data = json.loads(proc.stdout)
|
||||
except (subprocess.CalledProcessError, json.decoder.JSONDecodeError):
|
||||
if errors != 'ignore':
|
||||
raise
|
||||
|
||||
return json_data
|
||||
|
||||
|
||||
def get_procs(
|
||||
name: str,
|
||||
exact: bool = True,
|
||||
try_again: bool = True,
|
||||
) -> list[psutil.Process]:
|
||||
"""Get process object(s) based on name, returns list of proc objects."""
|
||||
LOG.debug('name: %s, exact: %s', name, exact)
|
||||
processes = []
|
||||
regex = f'^{name}$' if exact else name
|
||||
|
||||
# Iterate over all processes
|
||||
for proc in psutil.process_iter():
|
||||
if re.search(regex, proc.name(), re.IGNORECASE):
|
||||
processes.append(proc)
|
||||
|
||||
# Try again?
|
||||
if not processes and try_again:
|
||||
time.sleep(1)
|
||||
processes = get_procs(name, exact, try_again=False)
|
||||
|
||||
# Done
|
||||
return processes
|
||||
|
||||
|
||||
def kill_procs(
|
||||
name: str,
|
||||
exact: bool = True,
|
||||
force: bool = False,
|
||||
timeout: float | int = 30,
|
||||
) -> None:
|
||||
"""Kill all processes matching name (case-insensitively).
|
||||
|
||||
NOTE: Under Posix systems this will send SIGINT to allow processes
|
||||
to gracefully exit.
|
||||
|
||||
If force is True then it will wait until timeout specified and then
|
||||
send SIGKILL to any processes still alive.
|
||||
"""
|
||||
LOG.debug(
|
||||
'name: %s, exact: %s, force: %s, timeout: %s',
|
||||
name, exact, force, timeout,
|
||||
)
|
||||
target_procs = get_procs(name, exact=exact)
|
||||
for proc in target_procs:
|
||||
proc.terminate()
|
||||
|
||||
# Force kill if necesary
|
||||
if force:
|
||||
results = psutil.wait_procs(target_procs, timeout=timeout)
|
||||
for proc in results[1]: # Alive processes
|
||||
proc.kill()
|
||||
|
||||
|
||||
def popen_program(
|
||||
cmd: list[str],
|
||||
minimized: bool = False,
|
||||
pipe: bool = False,
|
||||
shell: bool = False,
|
||||
**kwargs,
|
||||
) -> subprocess.Popen:
|
||||
"""Run program and return a subprocess.Popen object."""
|
||||
LOG.debug(
|
||||
'cmd: %s, minimized: %s, pipe: %s, shell: %s',
|
||||
cmd, minimized, pipe, shell,
|
||||
)
|
||||
LOG.debug('kwargs: %s', kwargs)
|
||||
cmd_kwargs = build_cmd_kwargs(
|
||||
cmd,
|
||||
minimized=minimized,
|
||||
pipe=pipe,
|
||||
shell=shell,
|
||||
**kwargs)
|
||||
try:
|
||||
proc = subprocess.Popen(**cmd_kwargs)
|
||||
except FileNotFoundError:
|
||||
LOG.error('Command not found: %s', cmd)
|
||||
raise
|
||||
LOG.debug('proc: %s', proc)
|
||||
|
||||
# Done
|
||||
return proc
|
||||
|
||||
|
||||
def run_program(
|
||||
cmd: list[str],
|
||||
check: bool = True,
|
||||
pipe: bool = True,
|
||||
shell: bool = False,
|
||||
**kwargs,
|
||||
) -> subprocess.CompletedProcess:
|
||||
"""Run program and return a subprocess.CompletedProcess object."""
|
||||
LOG.debug(
|
||||
'cmd: %s, check: %s, pipe: %s, shell: %s',
|
||||
cmd, check, pipe, shell,
|
||||
)
|
||||
LOG.debug('kwargs: %s', kwargs)
|
||||
cmd_kwargs = build_cmd_kwargs(
|
||||
cmd,
|
||||
check=check,
|
||||
pipe=pipe,
|
||||
shell=shell,
|
||||
**kwargs)
|
||||
check = cmd_kwargs.pop('check', True) # Avoids linting warning
|
||||
try:
|
||||
proc = subprocess.run(check=check, **cmd_kwargs)
|
||||
except FileNotFoundError:
|
||||
LOG.error('Command not found: %s', cmd)
|
||||
raise
|
||||
LOG.debug('proc: %s', proc)
|
||||
|
||||
# Done
|
||||
return proc
|
||||
|
||||
|
||||
def start_thread(
|
||||
function: Callable,
|
||||
args: Iterable[Any] | None = None,
|
||||
daemon: bool = True,
|
||||
) -> Thread:
|
||||
"""Run function as thread in background, returns Thread object."""
|
||||
LOG.debug(
|
||||
'Starting background thread for function: %s, args: %s, daemon: %s',
|
||||
function, args, daemon,
|
||||
)
|
||||
args = args if args else []
|
||||
thread = Thread(target=function, args=args, daemon=daemon)
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
|
||||
def stop_process(proc: subprocess.Popen, graceful: bool = True) -> None:
|
||||
"""Stop process.
|
||||
|
||||
NOTES: proc should be a subprocess.Popen obj.
|
||||
If graceful is True then a SIGTERM is sent before SIGKILL.
|
||||
"""
|
||||
|
||||
# Graceful exit
|
||||
if graceful:
|
||||
if os.name == 'posix' and os.geteuid() != 0:
|
||||
run_program(['sudo', 'kill', str(proc.pid)], check=False)
|
||||
else:
|
||||
proc.terminate()
|
||||
time.sleep(2)
|
||||
|
||||
# Force exit
|
||||
if os.name == 'posix' and os.geteuid() != 0:
|
||||
run_program(['sudo', 'kill', '-9', str(proc.pid)], check=False)
|
||||
else:
|
||||
proc.kill()
|
||||
|
||||
|
||||
def wait_for_procs(
|
||||
name: str,
|
||||
exact: bool = True,
|
||||
timeout: float | int | None = None,
|
||||
) -> None:
|
||||
"""Wait for all process matching name."""
|
||||
LOG.debug('name: %s, exact: %s, timeout: %s', name, exact, timeout)
|
||||
target_procs = get_procs(name, exact=exact)
|
||||
procs = psutil.wait_procs(target_procs, timeout=timeout)
|
||||
|
||||
# Raise exception if necessary
|
||||
if procs[1]: # Alive processes
|
||||
raise psutil.TimeoutExpired(name=name, seconds=timeout)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
"""WizardKit: Graph Functions"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
|
||||
from wk.ui import ansi
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
GRAPH_HORIZONTAL = ('▁', '▂', '▃', '▄', '▅', '▆', '▇', '█')
|
||||
GRAPH_VERTICAL = (
|
||||
'▏', '▎', '▍', '▌',
|
||||
'▋', '▊', '▉', '█',
|
||||
'█▏', '█▎', '█▍', '█▌',
|
||||
'█▋', '█▊', '█▉', '██',
|
||||
'██▏', '██▎', '██▍', '██▌',
|
||||
'██▋', '██▊', '██▉', '███',
|
||||
'███▏', '███▎', '███▍', '███▌',
|
||||
'███▋', '███▊', '███▉', '████',
|
||||
)
|
||||
# SCALE_STEPS: These scales allow showing differences between HDDs and SSDs
|
||||
# on the same graph.
|
||||
SCALE_STEPS = {
|
||||
8: [2**(0.56*(x+1))+(16*(x+1)) for x in range(8)],
|
||||
16: [2**(0.56*(x+1))+(16*(x+1)) for x in range(16)],
|
||||
32: [2**(0.56*(x+1)/2)+(16*(x+1)/2) for x in range(32)],
|
||||
}
|
||||
# THRESHOLDS: These are the rate_list (in MB/s) used to color graphs
|
||||
THRESH_FAIL = 65 * 1024**2
|
||||
THRESH_WARN = 135 * 1024**2
|
||||
THRESH_GREAT = 750 * 1024**2
|
||||
|
||||
|
||||
# Functions
|
||||
def generate_horizontal_graph(
|
||||
rate_list: list[float],
|
||||
graph_width: int = 40,
|
||||
oneline: bool = False) -> list[str]:
|
||||
"""Generate horizontal graph from rate_list, returns list."""
|
||||
graph = ['', '', '', '']
|
||||
scale = 8 if oneline else 32
|
||||
|
||||
# Build graph
|
||||
for rate in merge_rates(rate_list, graph_width=graph_width):
|
||||
step = get_graph_step(rate, scale=scale)
|
||||
|
||||
# Set color
|
||||
rate_color = None
|
||||
if rate < THRESH_FAIL:
|
||||
rate_color = 'RED'
|
||||
elif rate < THRESH_WARN:
|
||||
rate_color = 'YELLOW'
|
||||
elif rate > THRESH_GREAT:
|
||||
rate_color = 'GREEN'
|
||||
|
||||
# Build graph
|
||||
full_block = ansi.color_string((GRAPH_HORIZONTAL[-1],), (rate_color,))
|
||||
if step >= 24:
|
||||
graph[0] += ansi.color_string((GRAPH_HORIZONTAL[step-24],), (rate_color,))
|
||||
graph[1] += full_block
|
||||
graph[2] += full_block
|
||||
graph[3] += full_block
|
||||
elif step >= 16:
|
||||
graph[0] += ' '
|
||||
graph[1] += ansi.color_string((GRAPH_HORIZONTAL[step-16],), (rate_color,))
|
||||
graph[2] += full_block
|
||||
graph[3] += full_block
|
||||
elif step >= 8:
|
||||
graph[0] += ' '
|
||||
graph[1] += ' '
|
||||
graph[2] += ansi.color_string((GRAPH_HORIZONTAL[step-8],), (rate_color,))
|
||||
graph[3] += full_block
|
||||
else:
|
||||
graph[0] += ' '
|
||||
graph[1] += ' '
|
||||
graph[2] += ' '
|
||||
graph[3] += ansi.color_string((GRAPH_HORIZONTAL[step],), (rate_color,))
|
||||
|
||||
# Done
|
||||
if oneline:
|
||||
graph = graph[-1:]
|
||||
return graph
|
||||
|
||||
|
||||
def get_graph_step(rate: float, scale: int = 16) -> int:
|
||||
"""Get graph step based on rate and scale, returns int."""
|
||||
rate_in_mb = rate / (1024**2)
|
||||
step = 0
|
||||
|
||||
# Iterate over scale_steps backwards
|
||||
for _r in range(scale-1, -1, -1):
|
||||
if rate_in_mb >= SCALE_STEPS[scale][_r]:
|
||||
step = _r
|
||||
break
|
||||
|
||||
# Done
|
||||
return step
|
||||
|
||||
|
||||
def merge_rates(
|
||||
rates: list[float],
|
||||
graph_width: int = 40,
|
||||
) -> list[int | float]:
|
||||
"""Merge rates to have entries equal to the width, returns list."""
|
||||
merged_rates = []
|
||||
offset = 0
|
||||
slice_width = int(len(rates) / graph_width)
|
||||
|
||||
# Merge rates
|
||||
for _ in range(graph_width):
|
||||
merged_rates.append(sum(rates[offset:offset+slice_width])/slice_width)
|
||||
offset += slice_width
|
||||
|
||||
# Done
|
||||
return merged_rates
|
||||
|
||||
|
||||
def vertical_graph_line(percent: float, rate: float, scale: int = 32) -> str:
|
||||
"""Build colored graph string using thresholds, returns str."""
|
||||
color_bar = None
|
||||
color_rate = None
|
||||
step = get_graph_step(rate, scale=scale)
|
||||
|
||||
# Set colors
|
||||
if rate < THRESH_FAIL:
|
||||
color_bar = 'RED'
|
||||
color_rate = 'YELLOW'
|
||||
elif rate < THRESH_WARN:
|
||||
color_bar = 'YELLOW'
|
||||
color_rate = 'YELLOW'
|
||||
elif rate > THRESH_GREAT:
|
||||
color_bar = 'GREEN'
|
||||
color_rate = 'GREEN'
|
||||
|
||||
# Build string
|
||||
line = ansi.color_string(
|
||||
strings=(
|
||||
f'{percent:5.1f}%',
|
||||
f'{GRAPH_VERTICAL[step]:<4}',
|
||||
f'{rate/(1000**2):6.1f} MB/s',
|
||||
),
|
||||
colors=(
|
||||
None,
|
||||
color_bar,
|
||||
color_rate,
|
||||
),
|
||||
sep=' ',
|
||||
)
|
||||
|
||||
# Done
|
||||
return line
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
"""WizardKit: hw module init"""
|
||||
|
||||
from . import audio
|
||||
from . import benchmark
|
||||
from . import cpu
|
||||
from . import diags
|
||||
from . import disk
|
||||
from . import keyboard
|
||||
from . import network
|
||||
from . import screensavers
|
||||
from . import sensors
|
||||
from . import smart
|
||||
from . import surface_scan
|
||||
from . import system
|
||||
from . import test
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
"""WizardKit: Audio test functions"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
|
||||
from wk.exe import run_program
|
||||
from wk.std import PLATFORM
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Functions
|
||||
def audio_test() -> None:
|
||||
"""Run an OS-specific audio test."""
|
||||
if PLATFORM == 'Linux':
|
||||
audio_test_linux()
|
||||
|
||||
|
||||
def audio_test_linux() -> None:
|
||||
"""Run an audio test using amixer and speaker-test."""
|
||||
LOG.info('Audio Test')
|
||||
|
||||
# Set volume
|
||||
for source in ('Master', 'PCM'):
|
||||
cmd = f'amixer -q set "{source}" 80% unmute'.split()
|
||||
run_program(cmd, check=False)
|
||||
|
||||
# Run audio tests
|
||||
for mode in ('pink', 'wav'):
|
||||
cmd = f'speaker-test -c 2 -l 1 -t {mode}'.split()
|
||||
run_program(cmd, check=False, pipe=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,219 +0,0 @@
|
|||
"""WizardKit: Benchmark test functions"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
|
||||
from subprocess import PIPE, STDOUT
|
||||
|
||||
from wk import graph
|
||||
from wk.cfg.hw import (
|
||||
IO_ALT_TEST_SIZE_FACTOR,
|
||||
IO_BLOCK_SIZE,
|
||||
IO_CHUNK_SIZE,
|
||||
IO_GRAPH_WIDTH,
|
||||
IO_MINIMUM_TEST_SIZE,
|
||||
IO_RATE_REGEX,
|
||||
THRESH_HDD_AVG_HIGH,
|
||||
THRESH_HDD_AVG_LOW,
|
||||
THRESH_HDD_MIN,
|
||||
THRESH_SSD_AVG_HIGH,
|
||||
THRESH_SSD_AVG_LOW,
|
||||
THRESH_SSD_MIN,
|
||||
)
|
||||
from wk.exe import run_program
|
||||
from wk.std import PLATFORM
|
||||
from wk.ui import ansi
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Error Classes
|
||||
class DeviceTooSmallError(RuntimeError):
|
||||
"""Raised when a device is too small to test."""
|
||||
|
||||
|
||||
# Functions
|
||||
def calc_io_dd_values(dev_size, test_mode=False) -> dict[str, int]:
|
||||
"""Calculate I/O benchmark dd values, returns dict.
|
||||
|
||||
Calculations:
|
||||
The minimum dev size is IO_GRAPH_WIDTH * IO_CHUNK_SIZE
|
||||
(e.g. 1.25 GB for a width of 40 and a chunk size of 32MB)
|
||||
|
||||
read_total is the area to be read in bytes
|
||||
If the dev is < IO_MINIMUM_TEST_SIZE then it's the whole dev
|
||||
Else it's the larger of IO_MINIMUM_TEST_SIZE or the alt test size
|
||||
(determined by dev * IO_ALT_TEST_SIZE_FACTOR)
|
||||
|
||||
read_chunks is the number of groups of IO_CHUNK_SIZE in test_obj.dev
|
||||
This number is reduced to a multiple of IO_GRAPH_WIDTH in order
|
||||
to allow for the data to be condensed cleanly
|
||||
|
||||
read_blocks is the chunk size in number of blocks
|
||||
(e.g. 64 if block size is 512KB and chunk size is 32MB
|
||||
|
||||
skip_total is the number of IO_BLOCK_SIZE groups not tested
|
||||
skip_blocks is the number of blocks to skip per IO_CHUNK_SIZE
|
||||
skip_extra_rate is how often to add an additional skip block
|
||||
This is needed to ensure an even testing across the dev
|
||||
This is calculated by using the fractional amount left off
|
||||
of the skip_blocks variable
|
||||
|
||||
test_mode limits the benchmark to IO_MINIMUM_TEST_SIZE (if possible)
|
||||
"""
|
||||
if test_mode and dev_size > IO_MINIMUM_TEST_SIZE:
|
||||
dev_size = IO_MINIMUM_TEST_SIZE
|
||||
read_total = min(IO_MINIMUM_TEST_SIZE, dev_size)
|
||||
read_total = max(read_total, dev_size*IO_ALT_TEST_SIZE_FACTOR)
|
||||
read_chunks = int(read_total // IO_CHUNK_SIZE)
|
||||
read_chunks -= read_chunks % IO_GRAPH_WIDTH
|
||||
if read_chunks < IO_GRAPH_WIDTH:
|
||||
raise DeviceTooSmallError
|
||||
read_blocks = int(IO_CHUNK_SIZE / IO_BLOCK_SIZE)
|
||||
read_total = read_chunks * IO_CHUNK_SIZE
|
||||
skip_total = int((dev_size - read_total) // IO_BLOCK_SIZE)
|
||||
skip_blocks = int((skip_total / read_chunks) // 1)
|
||||
skip_extra_rate = 0
|
||||
try:
|
||||
skip_extra_rate = 1 + int(1 / ((skip_total / read_chunks) % 1))
|
||||
except ZeroDivisionError:
|
||||
# skip_extra_rate == 0 is fine
|
||||
pass
|
||||
|
||||
# Test mode
|
||||
if test_mode:
|
||||
read_chunks_limit = int(read_chunks * 0.1)
|
||||
read_chunks_limit = (read_chunks_limit // IO_GRAPH_WIDTH) * IO_GRAPH_WIDTH
|
||||
read_chunks = max(IO_GRAPH_WIDTH, read_chunks_limit)
|
||||
|
||||
# Done
|
||||
return {
|
||||
'Read Chunks': read_chunks,
|
||||
'Read Blocks': read_blocks,
|
||||
'Skip Blocks': skip_blocks,
|
||||
'Skip Extra': skip_extra_rate,
|
||||
}
|
||||
|
||||
|
||||
def check_io_results(test_obj, rate_list, graph_width) -> None:
|
||||
"""Check I/O restuls and generate report using rate_list."""
|
||||
avg_read = sum(rate_list) / len(rate_list)
|
||||
min_read = min(rate_list)
|
||||
max_read = max(rate_list)
|
||||
if test_obj.dev.ssd:
|
||||
thresh_min = THRESH_SSD_MIN
|
||||
thresh_avg_high = THRESH_SSD_AVG_HIGH
|
||||
thresh_avg_low = THRESH_SSD_AVG_LOW
|
||||
else:
|
||||
thresh_min = THRESH_HDD_MIN
|
||||
thresh_avg_high = THRESH_HDD_AVG_HIGH
|
||||
thresh_avg_low = THRESH_HDD_AVG_LOW
|
||||
|
||||
# Add horizontal graph to report
|
||||
for line in graph.generate_horizontal_graph(rate_list, graph_width):
|
||||
if not ansi.strip_colors(line).strip():
|
||||
# Skip empty lines
|
||||
continue
|
||||
test_obj.report.append(line)
|
||||
|
||||
# Add read rates to report
|
||||
test_obj.report.append(
|
||||
f'Read speeds avg: {avg_read/(1000**2):3.1f}'
|
||||
f' min: {min_read/(1000**2):3.1f}'
|
||||
f' max: {max_read/(1000**2):3.1f}'
|
||||
)
|
||||
|
||||
# Compare against thresholds
|
||||
if min_read <= thresh_min and avg_read <= thresh_avg_high:
|
||||
test_obj.failed = True
|
||||
elif avg_read <= thresh_avg_low:
|
||||
test_obj.failed = True
|
||||
else:
|
||||
test_obj.passed = True
|
||||
|
||||
# Set status
|
||||
if test_obj.failed:
|
||||
test_obj.set_status('Failed')
|
||||
elif test_obj.passed:
|
||||
test_obj.set_status('Passed')
|
||||
else:
|
||||
test_obj.set_status('Unknown')
|
||||
|
||||
|
||||
def run_io_test(test_obj, log_path, test_mode=False) -> None:
|
||||
"""Run I/O benchmark and handle exceptions."""
|
||||
dev_path = test_obj.dev.path
|
||||
if PLATFORM == 'Darwin':
|
||||
# Use "RAW" disks under macOS
|
||||
dev_path = dev_path.with_name(f'r{dev_path.name}')
|
||||
LOG.info('Using %s for better performance', dev_path)
|
||||
offset = 0
|
||||
read_rates = []
|
||||
test_obj.report.append(ansi.color_string('I/O Benchmark', 'BLUE'))
|
||||
|
||||
# Get dd values or bail
|
||||
try:
|
||||
dd_values = calc_io_dd_values(test_obj.dev.size, test_mode=test_mode)
|
||||
except DeviceTooSmallError:
|
||||
test_obj.set_status('N/A')
|
||||
test_obj.report.append(
|
||||
ansi.color_string('Disk too small to test', 'YELLOW'),
|
||||
)
|
||||
return
|
||||
|
||||
# Run dd read tests
|
||||
for _i in range(dd_values['Read Chunks']):
|
||||
_i += 1
|
||||
|
||||
# Build cmd
|
||||
skip = dd_values['Skip Blocks']
|
||||
if dd_values['Skip Extra'] and _i % dd_values['Skip Extra'] == 0:
|
||||
skip += 1
|
||||
cmd = [
|
||||
'sudo', 'dd',
|
||||
f'bs={IO_BLOCK_SIZE}',
|
||||
f'skip={offset+skip}',
|
||||
f'count={dd_values["Read Blocks"]}',
|
||||
f'if={dev_path}',
|
||||
'of=/dev/null',
|
||||
]
|
||||
if PLATFORM == 'Linux':
|
||||
cmd.append('iflag=direct')
|
||||
|
||||
# Run and get read rate
|
||||
try:
|
||||
proc = run_program(
|
||||
cmd,
|
||||
pipe=False,
|
||||
stdout=PIPE,
|
||||
stderr=STDOUT,
|
||||
)
|
||||
except PermissionError as err:
|
||||
# Since we're using sudo we can't kill dd
|
||||
# Assuming this happened during a CTRL+c
|
||||
raise KeyboardInterrupt from err
|
||||
match = IO_RATE_REGEX.search(proc.stdout)
|
||||
if match:
|
||||
read_rates.append(
|
||||
int(match.group('bytes')) / float(match.group('seconds')),
|
||||
)
|
||||
match.group(1)
|
||||
|
||||
# Show progress
|
||||
with open(log_path, 'a', encoding='utf-8') as _f:
|
||||
if _i % 5 == 0:
|
||||
percent = (_i / dd_values['Read Chunks']) * 100
|
||||
_f.write(f' {graph.vertical_graph_line(percent, read_rates[-1])}\n')
|
||||
|
||||
# Update offset
|
||||
offset += dd_values['Read Blocks'] + skip
|
||||
|
||||
# Check results
|
||||
check_io_results(test_obj, read_rates, IO_GRAPH_WIDTH)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
"""WizardKit: CPU test functions"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
from typing import TextIO
|
||||
|
||||
from wk import exe
|
||||
from wk.cfg.hw import CPU_TEMPS
|
||||
from wk.os.mac import set_fans as macos_set_fans
|
||||
from wk.std import PLATFORM
|
||||
from wk.ui import ansi
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
SysbenchType = tuple[subprocess.Popen, TextIO]
|
||||
|
||||
|
||||
# Functions
|
||||
def check_cooling_results(sensors, test_object) -> None:
|
||||
"""Check cooling result via sensor data."""
|
||||
idle_temp = sensors.get_cpu_temp('Idle')
|
||||
cooldown_temp = sensors.get_cpu_temp('Cooldown')
|
||||
max_temp = sensors.get_cpu_temp('Max')
|
||||
test_object.report.append(ansi.color_string('Temps', 'BLUE'))
|
||||
|
||||
# Check temps
|
||||
if max_temp > CPU_TEMPS['Critical']:
|
||||
test_object.failed = True
|
||||
test_object.set_status('Failed')
|
||||
test_object.report.extend([
|
||||
ansi.color_string(
|
||||
f' WARNING: Critical CPU temp of {CPU_TEMPS["Critical"]} exceeded.',
|
||||
'RED',
|
||||
),
|
||||
'',
|
||||
])
|
||||
elif idle_temp >= CPU_TEMPS['Idle High']:
|
||||
test_object.failed = True
|
||||
test_object.set_status('Failed')
|
||||
test_object.report.extend([
|
||||
ansi.color_string(
|
||||
f' WARNING: Max idle temp of {CPU_TEMPS["Idle High"]} exceeded.',
|
||||
'YELLOW',
|
||||
),
|
||||
'',
|
||||
])
|
||||
elif (
|
||||
cooldown_temp <= CPU_TEMPS['Cooling Low Cutoff']
|
||||
or max_temp - cooldown_temp >= CPU_TEMPS['Cooling Delta']
|
||||
):
|
||||
test_object.passed = True
|
||||
test_object.set_status('Passed')
|
||||
else:
|
||||
test_object.passed = False
|
||||
test_object.set_status('Unknown')
|
||||
if cooldown_temp - idle_temp >= CPU_TEMPS['Idle Delta']:
|
||||
test_object.report.extend([
|
||||
ansi.color_string(
|
||||
f' WARNING: Cooldown temp at least {CPU_TEMPS["Idle Delta"]}° over idle.',
|
||||
'YELLOW',
|
||||
),
|
||||
'',
|
||||
])
|
||||
|
||||
# Build report
|
||||
report_labels = ['Idle']
|
||||
average_labels = []
|
||||
if 'Sysbench' in sensors.temp_labels:
|
||||
average_labels.append('Sysbench')
|
||||
report_labels.extend(['Sysbench', 'Cooldown'])
|
||||
if 'Prime95' in sensors.temp_labels:
|
||||
average_labels.append('Prime95')
|
||||
report_labels.append('Prime95')
|
||||
if 'Cooldown' not in report_labels:
|
||||
report_labels.append('Cooldown')
|
||||
if len(sensors.temp_labels.intersection(['Prime95', 'Sysbench'])) < 1:
|
||||
# Include overall max temp if needed
|
||||
report_labels.append('Max')
|
||||
for line in sensors.generate_report(
|
||||
*report_labels, only_cpu=True, include_avg_for=average_labels):
|
||||
test_object.report.append(f' {line}')
|
||||
|
||||
|
||||
def check_mprime_results(test_obj, working_dir) -> None:
|
||||
"""Check mprime log files and update test_obj."""
|
||||
passing_lines = set()
|
||||
warning_lines = set()
|
||||
|
||||
def _read_file(log_name) -> list[str]:
|
||||
"""Read file and split into lines, returns list."""
|
||||
lines = []
|
||||
try:
|
||||
with open(f'{working_dir}/{log_name}', 'r', encoding='utf-8') as _f:
|
||||
lines = _f.readlines()
|
||||
except FileNotFoundError:
|
||||
# File may be missing on older systems
|
||||
lines = []
|
||||
|
||||
return lines
|
||||
|
||||
# results.txt (check if failed)
|
||||
for line in _read_file('results.txt'):
|
||||
line = line.strip()
|
||||
if re.search(r'(error|fail)', line, re.IGNORECASE):
|
||||
warning_lines.add(line)
|
||||
|
||||
# prime.log (check if passed)
|
||||
for line in _read_file('prime.log'):
|
||||
line = line.strip()
|
||||
match = re.search(
|
||||
r'(completed.*(\d+) errors, (\d+) warnings)', line, re.IGNORECASE)
|
||||
if match:
|
||||
if int(match.group(2)) + int(match.group(3)) > 0:
|
||||
# Errors and/or warnings encountered
|
||||
warning_lines.add(match.group(1).capitalize())
|
||||
else:
|
||||
# No errors/warnings
|
||||
passing_lines.add(match.group(1).capitalize())
|
||||
|
||||
# Update status
|
||||
if warning_lines:
|
||||
test_obj.failed = True
|
||||
test_obj.set_status('Failed')
|
||||
elif passing_lines and 'Aborted' not in test_obj.status:
|
||||
test_obj.passed = True
|
||||
test_obj.set_status('Passed')
|
||||
else:
|
||||
test_obj.set_status('Unknown')
|
||||
|
||||
# Update report
|
||||
for line in passing_lines:
|
||||
test_obj.report.append(f' {line}')
|
||||
for line in warning_lines:
|
||||
test_obj.report.append(ansi.color_string(f' {line}', 'YELLOW'))
|
||||
if not (passing_lines or warning_lines):
|
||||
test_obj.report.append(ansi.color_string(' Unknown result', 'YELLOW'))
|
||||
|
||||
|
||||
def start_mprime(working_dir, log_path) -> subprocess.Popen:
|
||||
"""Start mprime and save filtered output to log, returns Popen object."""
|
||||
set_apple_fan_speed('max')
|
||||
proc_mprime = subprocess.Popen(
|
||||
['mprime', '-t'],
|
||||
cwd=working_dir,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
proc_grep = subprocess.Popen(
|
||||
'grep --ignore-case --invert-match --line-buffered stress.txt'.split(),
|
||||
stdin=proc_mprime.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
proc_mprime.stdout.close() # type: ignore[reportOptionalMemberAccess]
|
||||
save_nbsr = exe.NonBlockingStreamReader(
|
||||
proc_grep.stdout, # type: ignore[reportGeneralTypeIssues]
|
||||
)
|
||||
exe.start_thread(
|
||||
save_nbsr.save_to_file,
|
||||
args=(proc_grep, log_path),
|
||||
)
|
||||
|
||||
# Return objects
|
||||
return proc_mprime
|
||||
|
||||
|
||||
def set_apple_fan_speed(speed) -> None:
|
||||
"""Set Apple fan speed."""
|
||||
cmd = None
|
||||
|
||||
# Check
|
||||
if speed not in ('auto', 'max'):
|
||||
raise RuntimeError(f'Invalid speed {speed}')
|
||||
|
||||
# Set cmd
|
||||
if PLATFORM == 'Darwin':
|
||||
try:
|
||||
macos_set_fans(speed)
|
||||
except (RuntimeError, ValueError, subprocess.CalledProcessError) as err:
|
||||
LOG.error('Failed to set fans to %s', speed)
|
||||
LOG.error('Error: %s', err)
|
||||
#ui.print_error(f'Failed to set fans to {speed}')
|
||||
#for line in str(err).splitlines():
|
||||
# ui.print_warning(f' {line.strip()}')
|
||||
elif PLATFORM == 'Linux':
|
||||
cmd = ['apple-fans', speed]
|
||||
exe.run_program(cmd, check=False)
|
||||
|
||||
|
||||
def start_sysbench(log_path) -> SysbenchType:
|
||||
"""Start sysbench, returns tuple with Popen object and file handle."""
|
||||
set_apple_fan_speed('max')
|
||||
cmd = [
|
||||
'sysbench',
|
||||
f'--threads={exe.psutil.cpu_count()}',
|
||||
'--cpu-max-prime=1000000000',
|
||||
'cpu',
|
||||
'run',
|
||||
]
|
||||
|
||||
# Start sysbench
|
||||
filehandle = open(
|
||||
log_path, 'a', encoding='utf-8',
|
||||
)
|
||||
proc = exe.popen_program(cmd, stdout=filehandle)
|
||||
|
||||
# Done
|
||||
return (proc, filehandle)
|
||||
|
||||
|
||||
def stop_mprime(proc_mprime) -> None:
|
||||
"""Stop mprime gracefully, then forcefully as needed."""
|
||||
proc_mprime.terminate()
|
||||
try:
|
||||
proc_mprime.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc_mprime.kill()
|
||||
set_apple_fan_speed('auto')
|
||||
|
||||
|
||||
def stop_sysbench(proc_sysbench, filehandle_sysbench) -> None:
|
||||
"""Stop sysbench."""
|
||||
proc_sysbench.terminate()
|
||||
try:
|
||||
proc_sysbench.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc_sysbench.kill()
|
||||
filehandle_sysbench.flush()
|
||||
filehandle_sysbench.close()
|
||||
set_apple_fan_speed('auto')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
@ -1,983 +0,0 @@
|
|||
"""WizardKit: Hardware diagnostics"""
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import argparse
|
||||
import atexit
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
|
||||
from wk import cfg, debug, exe, log, std
|
||||
from wk.cfg.hw import STATUS_COLORS
|
||||
from wk.hw import benchmark as hw_benchmark
|
||||
from wk.hw import cpu as hw_cpu
|
||||
from wk.hw import disk as hw_disk
|
||||
from wk.hw import sensors as hw_sensors
|
||||
from wk.hw import smart as hw_smart
|
||||
from wk.hw import surface_scan as hw_surface_scan
|
||||
from wk.hw import system as hw_system
|
||||
from wk.hw.audio import audio_test
|
||||
from wk.hw.keyboard import keyboard_test
|
||||
from wk.hw.network import network_test
|
||||
from wk.hw.screensavers import screensaver
|
||||
from wk.hw.test import Test, TestGroup
|
||||
|
||||
from wk.ui import ansi, cli, tui
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
TEST_GROUPS = {
|
||||
# Also used to build the menu options
|
||||
## NOTE: This needs to be above MENU_SETS
|
||||
'CPU (Sysbench)': 'cpu_test_sysbench',
|
||||
'CPU (Prime95)': 'cpu_test_mprime',
|
||||
'CPU (Cooling)': 'cpu_test_cooling',
|
||||
'Disk Attributes': 'disk_attribute_check',
|
||||
'Disk Self-Test': 'disk_self_test',
|
||||
'Disk Surface Scan': 'disk_surface_scan',
|
||||
'Disk I/O Benchmark': 'disk_io_benchmark',
|
||||
}
|
||||
MENU_ACTIONS = (
|
||||
'Audio Test',
|
||||
'Keyboard Test',
|
||||
'Network Test',
|
||||
'Clock Sync',
|
||||
'Start',
|
||||
'Quit')
|
||||
MENU_ACTIONS_SECRET = (
|
||||
'Matrix',
|
||||
'Tubes',
|
||||
)
|
||||
MENU_OPTIONS_QUICK = ('Disk Attributes',)
|
||||
MENU_SETS = {
|
||||
'Full Diagnostic': (*TEST_GROUPS,),
|
||||
'CPU Diagnostic': (*[group for group in TEST_GROUPS if group.startswith('CPU')],),
|
||||
'Disk Diagnostic': (
|
||||
'Disk Attributes',
|
||||
'Disk Self-Test',
|
||||
'Disk Surface Scan',
|
||||
'Disk I/O Benchmark',
|
||||
),
|
||||
'Disk Diagnostic (Quick)': ('Disk Attributes',),
|
||||
}
|
||||
MENU_TOGGLES = (
|
||||
'Skip USB Benchmarks',
|
||||
)
|
||||
PLATFORM = std.PLATFORM
|
||||
|
||||
# Classes
|
||||
class State():
|
||||
"""Object for tracking hardware diagnostic data."""
|
||||
def __init__(self, test_mode=False):
|
||||
self.disks: list[hw_disk.Disk] = []
|
||||
self.log_dir: pathlib.Path | None = None
|
||||
self.progress_file: pathlib.Path | None = None
|
||||
self.sensors: hw_sensors.Sensors = hw_sensors.Sensors()
|
||||
self.system: hw_system.System | None = None
|
||||
self.test_groups: list[TestGroup] = []
|
||||
self.title_text: str = ansi.color_string('Hardware Diagnostics', 'GREEN')
|
||||
if test_mode:
|
||||
self.title_text += ansi.color_string(' (Test Mode)', 'YELLOW')
|
||||
self.ui: tui.TUI = tui.TUI(f'{self.title_text}\nMain Menu')
|
||||
|
||||
def abort_testing(self) -> None:
|
||||
"""Set unfinished tests as aborted and cleanup panes."""
|
||||
for group in self.test_groups:
|
||||
for test in group.test_objects:
|
||||
if test.status in ('Pending', 'Working'):
|
||||
test.set_status('Aborted')
|
||||
|
||||
# Cleanup panes
|
||||
self.reset_layout()
|
||||
|
||||
def disk_safety_checks(self) -> None:
|
||||
"""Check for mid-run SMART failures and failed test(s)."""
|
||||
for dev in self.disks:
|
||||
disk_smart_status_check(dev, mid_run=True)
|
||||
for test in dev.tests:
|
||||
if test.failed:
|
||||
# Skip acceptable failure states
|
||||
if 'Attributes' in test.name:
|
||||
continue
|
||||
if 'Self-Test' in test.name and 'TimedOut' in test.status:
|
||||
continue
|
||||
# Disable remaining tests
|
||||
dev.disable_disk_tests()
|
||||
break
|
||||
|
||||
def init_diags(self, menu) -> None:
|
||||
"""Initialize diagnostic pass."""
|
||||
|
||||
# Reset objects
|
||||
self.disks.clear()
|
||||
self.sensors = hw_sensors.Sensors()
|
||||
self.test_groups.clear()
|
||||
|
||||
# Set log
|
||||
self.log_dir = log.format_log_path(
|
||||
log_name='main',
|
||||
sub_dir='Hardware-Diagnostics',
|
||||
)
|
||||
log.update_log_path(
|
||||
dest_dir=self.log_dir.parent,
|
||||
dest_name=self.log_dir.stem,
|
||||
keep_history=False,
|
||||
timestamp=False,
|
||||
)
|
||||
self.log_dir = self.log_dir.parent
|
||||
cli.clear_screen()
|
||||
cli.print_info('Initializing...')
|
||||
|
||||
# Progress Pane
|
||||
self.progress_file = pathlib.Path(f'{self.log_dir}/progress.out')
|
||||
self.update_progress_file()
|
||||
self.ui.set_progress_file(self.progress_file)
|
||||
|
||||
# Add HW Objects
|
||||
self.system = hw_system.System()
|
||||
self.disks = hw_disk.get_disks(skip_kits=True)
|
||||
for disk in self.disks:
|
||||
hw_smart.enable_smart(disk)
|
||||
hw_smart.update_smart_details(disk)
|
||||
|
||||
# Add test objects
|
||||
for name, details in menu.options.items():
|
||||
if not details['Selected']:
|
||||
# Only add selected options
|
||||
continue
|
||||
|
||||
if 'CPU' in name:
|
||||
self.system.tests.append(
|
||||
Test(dev=self.system, label=name[5:-1], name=name),
|
||||
)
|
||||
|
||||
if 'Disk' in name:
|
||||
test_group = TestGroup(
|
||||
name=name, function=globals()[TEST_GROUPS[name]],
|
||||
)
|
||||
for disk in self.disks:
|
||||
test_obj = Test(dev=disk, label=disk.path.name, name=name)
|
||||
disk.tests.append(test_obj)
|
||||
test_group.test_objects.append(test_obj)
|
||||
self.test_groups.append(test_group)
|
||||
|
||||
# Group CPU tests
|
||||
if self.system.tests:
|
||||
self.test_groups.insert(
|
||||
0,
|
||||
TestGroup(
|
||||
name='CPU & Cooling',
|
||||
function=run_cpu_tests,
|
||||
test_objects=self.system.tests,
|
||||
),
|
||||
)
|
||||
|
||||
def reset_layout(self) -> None:
|
||||
"""Reset layout to avoid flickering."""
|
||||
self.ui.clear_current_pane_height()
|
||||
self.ui.remove_all_info_panes()
|
||||
self.ui.remove_all_worker_panes()
|
||||
|
||||
def save_debug_reports(self) -> None:
|
||||
"""Save debug reports to disk."""
|
||||
LOG.info('Saving debug reports')
|
||||
debug_dir = pathlib.Path(f'{self.log_dir}/debug')
|
||||
if not debug_dir.exists():
|
||||
debug_dir.mkdir()
|
||||
|
||||
# State (self)
|
||||
debug.save_pickles({'state': self}, debug_dir)
|
||||
with open(f'{debug_dir}/state.report', 'a', encoding='utf-8') as _f:
|
||||
_f.write('\n'.join(debug.generate_object_report(self)))
|
||||
|
||||
# Disks
|
||||
for disk in self.disks:
|
||||
with open(
|
||||
f'{debug_dir}/disk_{disk.path.name}.report', 'a',
|
||||
encoding='utf-8') as _f:
|
||||
_f.write('\n'.join(debug.generate_object_report(disk)))
|
||||
_f.write('\n\n[Tests]')
|
||||
for test in disk.tests:
|
||||
_f.write(f'\n{test.name}:\n')
|
||||
_f.write('\n'.join(debug.generate_object_report(test, indent=1)))
|
||||
|
||||
# SMC
|
||||
if os.path.exists('/.wk-live-macos'):
|
||||
data = []
|
||||
try:
|
||||
proc = exe.run_program(['smc', '-f'])
|
||||
data.extend(proc.stdout.splitlines())
|
||||
data.append('----')
|
||||
proc = exe.run_program(['smc', '-l'])
|
||||
data.extend(proc.stdout.splitlines())
|
||||
except Exception:
|
||||
LOG.error('Error(s) encountered while exporting SMC data')
|
||||
data = [line.strip() for line in data]
|
||||
with open(f'{debug_dir}/smc.data', 'a', encoding='utf-8') as _f:
|
||||
_f.write('\n'.join(data))
|
||||
|
||||
# System
|
||||
with open(f'{debug_dir}/system.report', 'a', encoding='utf-8') as _f:
|
||||
_f.write('\n'.join(debug.generate_object_report(self.system)))
|
||||
_f.write('\n\n[Tests]')
|
||||
for test in self.system.tests:
|
||||
_f.write(f'\n{test.name}:\n')
|
||||
_f.write('\n'.join(debug.generate_object_report(test, indent=1)))
|
||||
|
||||
def update_progress_file(self) -> None:
|
||||
"""Update progress file."""
|
||||
report = []
|
||||
|
||||
for group in self.test_groups:
|
||||
report.append(ansi.color_string(group.name, 'BLUE'))
|
||||
for test in group.test_objects:
|
||||
report.append(ansi.color_string(
|
||||
[test.label, f'{test.status:>{self.ui.side_width-len(test.label)}}'],
|
||||
[None, STATUS_COLORS.get(test.status, None)],
|
||||
sep='',
|
||||
))
|
||||
|
||||
# Add spacer
|
||||
report.append(' ')
|
||||
|
||||
# Write to progress file
|
||||
self.progress_file.write_text('\n'.join(report), encoding='utf-8')
|
||||
|
||||
def update_title_text(self, text) -> None:
|
||||
"""Update top pane with text."""
|
||||
self.ui.set_title(self.title_text, text)
|
||||
|
||||
|
||||
# Functions
|
||||
def argparse_helper() -> dict[str, bool]:
|
||||
"""Helper function to setup and return args, returns dict.
|
||||
|
||||
NOTE: A dict is used to match the legacy code.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='hw-diags',
|
||||
description=f'{cfg.main.KIT_NAME_FULL}: Hardware Diagnostics',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--cli', action='store_true',
|
||||
help='Force CLI mode',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-q', '--quick', action='store_true',
|
||||
help='Skip menu and perform a quick check',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-t', '--test-mode', action='store_true',
|
||||
help='Run diags in test mode',
|
||||
)
|
||||
args = parser.parse_args()
|
||||
legacy_args = {
|
||||
'--cli': args.cli,
|
||||
'--quick': args.quick,
|
||||
'--test-mode': args.test_mode,
|
||||
}
|
||||
return legacy_args
|
||||
|
||||
|
||||
def build_menu(cli_mode=False, quick_mode=False) -> cli.Menu:
|
||||
"""Build main menu, returns wk.ui.cli.Menu."""
|
||||
menu = cli.Menu(title='')
|
||||
|
||||
# Add actions, options, etc
|
||||
for action in MENU_ACTIONS:
|
||||
menu.add_action(action)
|
||||
for action in MENU_ACTIONS_SECRET:
|
||||
menu.add_action(action, {'Hidden': True})
|
||||
for option in TEST_GROUPS:
|
||||
menu.add_option(option, {'Selected': True})
|
||||
for toggle in MENU_TOGGLES:
|
||||
menu.add_toggle(toggle, {'Selected': True})
|
||||
for name, targets in MENU_SETS.items():
|
||||
menu.add_set(name, {'Targets': targets})
|
||||
menu.actions['Start']['Separator'] = True
|
||||
|
||||
# Update default selections for quick mode if necessary
|
||||
if quick_mode:
|
||||
for name, details in menu.options.items():
|
||||
# Only select quick option(s)
|
||||
details['Selected'] = name in MENU_OPTIONS_QUICK
|
||||
|
||||
# Skip CPU tests for TestStations
|
||||
if os.path.exists(cfg.hw.TESTSTATION_FILE):
|
||||
menu.options['CPU (Sysbench)']['Selected'] = False
|
||||
menu.options['CPU (Prime95)']['Selected'] = False
|
||||
menu.options['CPU (Cooling)']['Selected'] = False
|
||||
|
||||
# Add CLI actions if necessary
|
||||
if cli_mode or 'DISPLAY' not in os.environ:
|
||||
menu.add_action('Reboot')
|
||||
menu.add_action('Power Off')
|
||||
|
||||
# Compatibility checks
|
||||
if PLATFORM != 'Linux':
|
||||
for name in ('Audio Test', 'Keyboard Test'):
|
||||
menu.actions[name]['Disabled'] = True
|
||||
if PLATFORM not in ('Darwin', 'Linux'):
|
||||
for name in ('Matrix', 'Network Test', 'Tubes'):
|
||||
menu.actions[name]['Disabled'] = True
|
||||
|
||||
# Live macOS actions
|
||||
if os.path.exists('/.wk-live-macos'):
|
||||
menu.actions['Clock Sync']['Separator'] = True
|
||||
else:
|
||||
menu.actions['Clock Sync']['Disabled'] = True
|
||||
menu.actions['Clock Sync']['Hidden'] = True
|
||||
|
||||
# Done
|
||||
return menu
|
||||
|
||||
|
||||
def cpu_tests_init(state: State) -> None:
|
||||
"""Initialize CPU tests."""
|
||||
sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out')
|
||||
state.update_title_text(state.system.cpu_description)
|
||||
|
||||
# Start monitor
|
||||
if PLATFORM == 'Darwin':
|
||||
state.ui.add_info_pane(
|
||||
percent=80, cmd='./hw-sensors', update_layout=False,
|
||||
)
|
||||
elif PLATFORM == 'Linux':
|
||||
state.ui.add_info_pane(
|
||||
percent=80,
|
||||
watch_file=pathlib.Path(f'{state.log_dir}/sensors.out'),
|
||||
update_layout=False,
|
||||
)
|
||||
state.sensors.start_background_monitor(sensors_out)
|
||||
state.ui.set_current_pane_height(3)
|
||||
|
||||
# Save idle temps
|
||||
cli.print_standard('Saving idle temps...')
|
||||
state.sensors.save_average_temps(temp_label='Idle', seconds=5, save_history=False)
|
||||
|
||||
|
||||
def cpu_tests_end(state: State) -> None:
|
||||
"""End CPU tests."""
|
||||
# Cleanup
|
||||
state.sensors.clear_temps(next_label='Done')
|
||||
state.sensors.stop_background_monitor()
|
||||
state.ui.clear_current_pane_height()
|
||||
state.ui.remove_all_info_panes()
|
||||
state.ui.remove_all_worker_panes()
|
||||
|
||||
|
||||
def cpu_test_cooling(state: State, test_object, test_mode=False) -> None:
|
||||
"""CPU cooling test via sensor data assessment."""
|
||||
_ = test_mode
|
||||
LOG.info('CPU Test (Cooling)')
|
||||
|
||||
# Bail early
|
||||
if test_object.disabled:
|
||||
return
|
||||
|
||||
hw_cpu.check_cooling_results(state.sensors, test_object)
|
||||
state.update_progress_file()
|
||||
|
||||
|
||||
def cpu_test_mprime(state: State, test_object, test_mode=False) -> None:
|
||||
"""CPU stress test using mprime."""
|
||||
LOG.info('CPU Test (Prime95)')
|
||||
aborted = False
|
||||
log_path = pathlib.Path(f'{state.log_dir}/prime.log')
|
||||
sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out')
|
||||
test_minutes = cfg.hw.CPU_TEST_MINUTES
|
||||
if test_mode:
|
||||
test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT
|
||||
|
||||
# Bail early
|
||||
if test_object.disabled:
|
||||
return
|
||||
if state.sensors.cpu_reached_critical_temp():
|
||||
test_object.set_status('Denied')
|
||||
test_object.disabled = True
|
||||
return
|
||||
|
||||
# Prep
|
||||
test_object.set_status('Working')
|
||||
state.update_progress_file()
|
||||
state.ui.clear_current_pane()
|
||||
cli.print_info('Running stress test')
|
||||
print('')
|
||||
|
||||
# Start sensors monitor
|
||||
state.sensors.clear_temps(next_label='Prime95')
|
||||
state.sensors.stop_background_monitor()
|
||||
state.sensors.start_background_monitor(
|
||||
sensors_out,
|
||||
alt_max='Prime95',
|
||||
thermal_action=('killall', '-INT', 'mprime'),
|
||||
)
|
||||
|
||||
# Run Prime95
|
||||
hw_cpu.set_apple_fan_speed('max')
|
||||
proc = hw_cpu.start_mprime(state.log_dir, log_path)
|
||||
state.ui.add_worker_pane(lines=10, watch_cmd='tail', watch_file=log_path)
|
||||
try:
|
||||
print_countdown(proc=proc, seconds=test_minutes*60)
|
||||
except KeyboardInterrupt:
|
||||
aborted = True
|
||||
|
||||
# Stop Prime95
|
||||
hw_cpu.stop_mprime(proc)
|
||||
|
||||
# Get cooldown temp
|
||||
if 'Cooldown' in state.sensors.temp_labels:
|
||||
# Give Prime95 time to save the results
|
||||
std.sleep(1)
|
||||
state.sensors.clear_temps(next_label='Cooldown')
|
||||
else:
|
||||
# Save cooldown temp
|
||||
state.ui.clear_current_pane()
|
||||
cli.print_standard('Letting CPU cooldown...')
|
||||
std.sleep(5)
|
||||
cli.print_standard('Saving cooldown temps...')
|
||||
state.sensors.save_average_temps(temp_label='Cooldown', seconds=5)
|
||||
|
||||
# Check Prime95 results
|
||||
test_object.report.append(ansi.color_string('Prime95', 'BLUE'))
|
||||
hw_cpu.check_mprime_results(test_obj=test_object, working_dir=state.log_dir)
|
||||
|
||||
# Update progress
|
||||
if state.sensors.cpu_reached_critical_temp() or aborted:
|
||||
test_object.set_status('Aborted')
|
||||
state.update_progress_file()
|
||||
|
||||
# Done
|
||||
state.ui.remove_all_worker_panes()
|
||||
if aborted:
|
||||
cpu_tests_end(state)
|
||||
raise std.GenericAbort('Aborted')
|
||||
|
||||
|
||||
def cpu_test_sysbench(state: State, test_object, test_mode=False) -> None:
|
||||
"""CPU stress test using Sysbench."""
|
||||
LOG.info('CPU Test (Sysbench)')
|
||||
aborted = False
|
||||
log_path = pathlib.Path(f'{state.log_dir}/sysbench.log')
|
||||
sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out')
|
||||
test_minutes = cfg.hw.CPU_TEST_MINUTES
|
||||
if test_mode:
|
||||
test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT
|
||||
|
||||
# Bail early
|
||||
if test_object.disabled:
|
||||
return
|
||||
|
||||
# Prep
|
||||
test_object.set_status('Working')
|
||||
state.update_progress_file()
|
||||
state.ui.clear_current_pane()
|
||||
cli.print_info('Running stress test')
|
||||
print('')
|
||||
|
||||
# Start sensors monitor
|
||||
state.sensors.clear_temps(next_label='Sysbench')
|
||||
state.sensors.stop_background_monitor()
|
||||
state.sensors.start_background_monitor(
|
||||
sensors_out,
|
||||
alt_max='Sysbench',
|
||||
thermal_action=('killall', '-INT', 'sysbench'),
|
||||
)
|
||||
|
||||
# Run sysbench
|
||||
state.ui.add_worker_pane(lines=10, watch_cmd='tail', watch_file=log_path)
|
||||
proc, filehandle = hw_cpu.start_sysbench(log_path=log_path)
|
||||
try:
|
||||
print_countdown(proc=proc, seconds=test_minutes*60)
|
||||
except AttributeError:
|
||||
# Assuming the sysbench process wasn't found and proc was set to None
|
||||
LOG.error('Failed to find sysbench process', exc_info=True)
|
||||
except KeyboardInterrupt:
|
||||
aborted = True
|
||||
hw_cpu.stop_sysbench(proc, filehandle)
|
||||
|
||||
# Get cooldown temp
|
||||
if 'Cooldown' in state.sensors.temp_labels:
|
||||
state.sensors.clear_temps(next_label='Cooldown')
|
||||
else:
|
||||
state.ui.clear_current_pane()
|
||||
cli.print_standard('Letting CPU cooldown...')
|
||||
std.sleep(5)
|
||||
cli.print_standard('Saving cooldown temps...')
|
||||
state.sensors.save_average_temps(temp_label='Cooldown', seconds=5)
|
||||
|
||||
# Update progress
|
||||
test_object.report.append(ansi.color_string('Sysbench', 'BLUE'))
|
||||
if aborted:
|
||||
test_object.set_status('Aborted')
|
||||
test_object.report.append(ansi.color_string(' Aborted.', 'YELLOW'))
|
||||
state.update_progress_file()
|
||||
elif state.sensors.cpu_reached_critical_temp():
|
||||
test_object.set_status('Aborted')
|
||||
test_object.report.append(
|
||||
ansi.color_string(' Aborted due to temps.', 'YELLOW'),
|
||||
)
|
||||
elif proc.returncode not in (-15, -2, 0):
|
||||
# NOTE: Return codes:
|
||||
# 0 == Completed w/out issue
|
||||
# -2 == Stopped with INT signal
|
||||
# -15 == Stopped with TERM signal
|
||||
test_object.set_status('Failed')
|
||||
test_object.report.append(f' Failed with return code: {proc.returncode}')
|
||||
else:
|
||||
test_object.set_status('Passed')
|
||||
test_object.report.append(' Completed without issue.')
|
||||
state.update_progress_file()
|
||||
|
||||
# Done
|
||||
state.ui.remove_all_worker_panes()
|
||||
if aborted:
|
||||
cpu_tests_end(state)
|
||||
raise std.GenericAbort('Aborted')
|
||||
|
||||
|
||||
def disk_attribute_check(state: State, test_objects, test_mode=False) -> None:
|
||||
"""Disk attribute check."""
|
||||
_ = test_mode
|
||||
LOG.info('Disk Attribute Check')
|
||||
for test in test_objects:
|
||||
disk_smart_status_check(test.dev, mid_run=False)
|
||||
if not test.dev.attributes:
|
||||
# No NVMe/SMART data
|
||||
test.set_status('N/A')
|
||||
continue
|
||||
|
||||
# Done
|
||||
state.update_progress_file()
|
||||
|
||||
|
||||
def disk_io_benchmark(
|
||||
state, test_objects, skip_usb=True, test_mode=False) -> None:
|
||||
"""Disk I/O benchmark using dd."""
|
||||
LOG.info('Disk I/O Benchmark (dd)')
|
||||
aborted = False
|
||||
|
||||
# Run benchmarks
|
||||
state.update_title_text(
|
||||
f'Disk I/O Benchmark{"s" if len(test_objects) > 1 else ""}',
|
||||
)
|
||||
state.ui.set_current_pane_height(10)
|
||||
for test in test_objects:
|
||||
if test.disabled:
|
||||
# Skip
|
||||
continue
|
||||
|
||||
# Skip USB devices if requested
|
||||
if skip_usb and test.dev.bus == 'USB':
|
||||
test.set_status('Skipped')
|
||||
continue
|
||||
|
||||
# Start benchmark
|
||||
state.ui.clear_current_pane()
|
||||
cli.print_report(test.dev.generate_report())
|
||||
test.set_status('Working')
|
||||
test_log = f'{state.log_dir}/{test.dev.path.name}_benchmark.out'
|
||||
state.ui.remove_all_worker_panes()
|
||||
state.ui.add_worker_pane(
|
||||
percent=50,
|
||||
update_layout=False,
|
||||
watch_cmd='tail',
|
||||
watch_file=test_log,
|
||||
)
|
||||
state.update_progress_file()
|
||||
try:
|
||||
hw_benchmark.run_io_test(test, test_log, test_mode=test_mode)
|
||||
except KeyboardInterrupt:
|
||||
aborted = True
|
||||
except (subprocess.CalledProcessError, TypeError, ValueError) as err:
|
||||
# Something went wrong
|
||||
LOG.error('%s', err)
|
||||
test.set_status('ERROR')
|
||||
test.report.append(ansi.color_string(' Unknown Error', 'RED'))
|
||||
|
||||
# Mark test(s) aborted if necessary
|
||||
if aborted:
|
||||
test.set_status('Aborted')
|
||||
test.report.append(ansi.color_string(' Aborted', 'YELLOW'))
|
||||
break
|
||||
|
||||
# Update progress after each test
|
||||
state.update_progress_file()
|
||||
|
||||
# Cleanup
|
||||
state.update_progress_file()
|
||||
state.ui.clear_current_pane_height()
|
||||
state.ui.remove_all_worker_panes()
|
||||
|
||||
# Done
|
||||
if aborted:
|
||||
raise std.GenericAbort('Aborted')
|
||||
|
||||
|
||||
def disk_self_test(state: State, test_objects, test_mode=False) -> None:
|
||||
"""Disk self-test if available."""
|
||||
_ = test_mode
|
||||
LOG.info('Disk Self-Test(s)')
|
||||
aborted = False
|
||||
threads = []
|
||||
|
||||
# Run self-tests
|
||||
state.update_title_text(
|
||||
f'Disk self-test{"s" if len(test_objects) > 1 else ""}',
|
||||
)
|
||||
cli.print_info(f'Starting self-test{"s" if len(test_objects) > 1 else ""}')
|
||||
show_failed_attributes(state)
|
||||
for test in reversed(test_objects):
|
||||
if test.disabled:
|
||||
# Skip
|
||||
continue
|
||||
|
||||
# Start thread
|
||||
test.set_status('Working')
|
||||
test_log = f'{state.log_dir}/{test.dev.path.name}_selftest.log'
|
||||
threads.append(exe.start_thread(hw_smart.run_self_test, args=(test, test_log)))
|
||||
|
||||
# Show progress
|
||||
if threads[-1].is_alive():
|
||||
state.ui.add_worker_pane(lines=4, watch_file=test_log)
|
||||
|
||||
# Wait for all tests to complete
|
||||
state.update_progress_file()
|
||||
try:
|
||||
while True:
|
||||
if any(t.is_alive() for t in threads):
|
||||
std.sleep(1)
|
||||
else:
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
aborted = True
|
||||
for test in test_objects:
|
||||
hw_smart.abort_self_test(test.dev)
|
||||
std.sleep(0.5)
|
||||
hw_smart.build_self_test_report(test, aborted=True)
|
||||
|
||||
# Cleanup
|
||||
state.update_progress_file()
|
||||
state.ui.remove_all_worker_panes()
|
||||
|
||||
# Done
|
||||
if aborted:
|
||||
raise std.GenericAbort('Aborted')
|
||||
|
||||
|
||||
def disk_smart_status_check(dev, mid_run=True) -> None:
|
||||
"""Check SMART status."""
|
||||
msg = None
|
||||
color = None
|
||||
disable_tests = False
|
||||
|
||||
# Bail if dev is missing
|
||||
if not dev.present:
|
||||
dev.disable_disk_tests()
|
||||
return
|
||||
|
||||
# Check SMART status and attributes
|
||||
if not hw_smart.smart_status_ok(dev):
|
||||
msg = 'Critical SMART error detected'
|
||||
color = 'RED'
|
||||
disable_tests = True
|
||||
elif not hw_smart.check_attributes(dev, only_blocking=False):
|
||||
# Non-blocking errors
|
||||
msg = 'SMART attribute failure(s) detected'
|
||||
color = 'YELLOW'
|
||||
|
||||
# Log errors if detected
|
||||
if msg and not dev.contains_note(msg):
|
||||
msg = f'{msg}{" during diagnostics" if mid_run else ""}'
|
||||
LOG.warning(msg)
|
||||
dev.add_note(msg, color)
|
||||
|
||||
# Set Disk Attributes test result
|
||||
for test in dev.tests:
|
||||
if test.name == 'Disk Attributes':
|
||||
test.failed = bool(test.failed or msg)
|
||||
test.passed = not test.failed
|
||||
if test.failed:
|
||||
test.set_status('Failed')
|
||||
elif 'N/A' not in test.status:
|
||||
test.set_status('Passed')
|
||||
|
||||
# Disable further testing if needed
|
||||
if disable_tests:
|
||||
dev.disable_disk_tests()
|
||||
|
||||
|
||||
def disk_surface_scan(state: State, test_objects, test_mode=False) -> None:
|
||||
"""Read-only disk surface scan using badblocks."""
|
||||
LOG.info('Disk Surface Scan (badblocks)')
|
||||
aborted = False
|
||||
threads = []
|
||||
|
||||
# Update panes
|
||||
state.update_title_text(
|
||||
f'Disk Surface Scan{"s" if len(test_objects) > 1 else ""}',
|
||||
)
|
||||
cli.print_info(
|
||||
f'Starting disk surface scan{"s" if len(test_objects) > 1 else ""}',
|
||||
)
|
||||
show_failed_attributes(state)
|
||||
|
||||
# Run surface scans
|
||||
for test in reversed([test for test in test_objects if not test.disabled]):
|
||||
|
||||
# Start thread
|
||||
test_log = f'{state.log_dir}/{test.dev.path.name}_badblocks.log'
|
||||
threads.append(exe.start_thread(
|
||||
hw_surface_scan.run_scan, args=(test, test_log, test_mode),
|
||||
))
|
||||
|
||||
# Show progress
|
||||
if threads[-1].is_alive():
|
||||
state.ui.add_worker_pane(lines=5, watch_cmd='tail', watch_file=test_log)
|
||||
|
||||
# Wait for all tests to complete
|
||||
try:
|
||||
while True:
|
||||
if any(t.is_alive() for t in threads):
|
||||
state.update_progress_file()
|
||||
std.sleep(5)
|
||||
else:
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
aborted = True
|
||||
std.sleep(0.5)
|
||||
# Handle aborts
|
||||
for test in test_objects:
|
||||
if not (test.disabled or test.passed or test.failed):
|
||||
test.set_status('Aborted')
|
||||
test.report.append(ansi.color_string(' Aborted', 'YELLOW'))
|
||||
|
||||
# Cleanup
|
||||
state.update_progress_file()
|
||||
state.ui.remove_all_worker_panes()
|
||||
|
||||
# Done
|
||||
if aborted:
|
||||
raise std.GenericAbort('Aborted')
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main function for hardware diagnostics."""
|
||||
try:
|
||||
args = argparse_helper()
|
||||
except SystemExit:
|
||||
print('')
|
||||
cli.pause('Press Enter to exit...')
|
||||
raise
|
||||
log.update_log_path(dest_name='Hardware-Diagnostics', timestamp=True)
|
||||
|
||||
# Safety check
|
||||
if 'TMUX' not in os.environ:
|
||||
LOG.error('tmux session not found')
|
||||
raise RuntimeError('tmux session not found')
|
||||
|
||||
# Init
|
||||
menu = build_menu(cli_mode=args['--cli'], quick_mode=args['--quick'])
|
||||
state = State(test_mode=args['--test-mode'])
|
||||
|
||||
# Quick Mode
|
||||
if args['--quick']:
|
||||
run_diags(state, menu, quick_mode=True, test_mode=args['--test-mode'])
|
||||
return
|
||||
|
||||
# Show menu
|
||||
while True:
|
||||
action = None
|
||||
selection = menu.advanced_select()
|
||||
|
||||
# Set action
|
||||
if 'Audio Test' in selection:
|
||||
action = audio_test
|
||||
elif 'Keyboard Test' in selection:
|
||||
action = keyboard_test
|
||||
elif 'Network Test' in selection:
|
||||
action = network_test
|
||||
elif 'Clock Sync' in selection:
|
||||
action = sync_clock
|
||||
|
||||
# Run simple test
|
||||
if action:
|
||||
state.update_title_text(selection[0])
|
||||
try:
|
||||
action()
|
||||
except KeyboardInterrupt:
|
||||
cli.print_warning('Aborted.')
|
||||
cli.print_standard('')
|
||||
cli.pause('Press Enter to return to main menu...')
|
||||
if 'Clock Sync' in selection:
|
||||
state.ui.update_clock()
|
||||
|
||||
# Secrets
|
||||
if 'Matrix' in selection:
|
||||
screensaver('matrix')
|
||||
elif 'Tubes' in selection:
|
||||
# Tubes ≈≈ Pipes?
|
||||
screensaver('pipes')
|
||||
|
||||
# Quit
|
||||
if 'Reboot' in selection:
|
||||
cmd = ['/usr/local/bin/wk-power-command', 'reboot']
|
||||
exe.run_program(cmd, check=False)
|
||||
elif 'Power Off' in selection:
|
||||
cmd = ['/usr/local/bin/wk-power-command', 'poweroff']
|
||||
exe.run_program(cmd, check=False)
|
||||
elif 'Quit' in selection:
|
||||
break
|
||||
|
||||
# Start diagnostics
|
||||
if 'Start' in selection:
|
||||
run_diags(state, menu, quick_mode=False, test_mode=args['--test-mode'])
|
||||
|
||||
# Reset top pane
|
||||
state.update_title_text('Main Menu')
|
||||
|
||||
|
||||
def print_countdown(proc, seconds) -> None:
|
||||
"""Print countdown to screen while proc is alive."""
|
||||
seconds = int(seconds)
|
||||
for i in range(seconds):
|
||||
sec_left = (seconds - i) % 60
|
||||
min_left = int((seconds - i) / 60)
|
||||
|
||||
out_str = '\r '
|
||||
if min_left:
|
||||
out_str += f'{min_left} minute{"s" if min_left != 1 else ""}, '
|
||||
out_str += f'{sec_left} second{"s" if sec_left != 1 else ""}'
|
||||
out_str += ' remaining'
|
||||
|
||||
print(f'{out_str:<42}', end='', flush=True)
|
||||
try:
|
||||
proc.wait(1)
|
||||
except subprocess.TimeoutExpired:
|
||||
# proc still going, continue
|
||||
pass
|
||||
if ((hasattr(proc, 'poll') and proc.poll() is not None)
|
||||
or (hasattr(proc, 'is_running') and not proc.is_running())):
|
||||
# proc exited, stop countdown
|
||||
break
|
||||
|
||||
# Done
|
||||
print('')
|
||||
|
||||
def run_cpu_tests(state: State, test_objects, test_mode=False) -> None:
|
||||
"""Run selected CPU test(s)."""
|
||||
state.update_progress_file()
|
||||
cpu_tests_init(state)
|
||||
for obj in test_objects:
|
||||
func = globals()[TEST_GROUPS[obj.name]]
|
||||
func(state, obj, test_mode=test_mode)
|
||||
cpu_tests_end(state)
|
||||
state.update_progress_file()
|
||||
|
||||
|
||||
def run_diags(state: State, menu, quick_mode=False, test_mode=False) -> None:
|
||||
"""Run selected diagnostics."""
|
||||
aborted = False
|
||||
atexit.register(state.save_debug_reports)
|
||||
state.init_diags(menu)
|
||||
|
||||
# Just return if no tests were selected
|
||||
if not state.test_groups:
|
||||
cli.print_warning('No tests selected?')
|
||||
cli.pause()
|
||||
return
|
||||
|
||||
# Run tests
|
||||
for group in state.test_groups:
|
||||
|
||||
# Run test(s)
|
||||
function = group.function
|
||||
args = [group.test_objects]
|
||||
if group.name == 'Disk I/O Benchmark':
|
||||
args.append(menu.toggles['Skip USB Benchmarks']['Selected'])
|
||||
state.ui.clear_current_pane()
|
||||
try:
|
||||
function(state, *args, test_mode=test_mode)
|
||||
except (KeyboardInterrupt, std.GenericAbort):
|
||||
aborted = True
|
||||
state.abort_testing()
|
||||
state.update_progress_file()
|
||||
state.reset_layout()
|
||||
break
|
||||
else:
|
||||
# Run safety checks after disk tests
|
||||
if group.name.startswith('Disk'):
|
||||
state.disk_safety_checks()
|
||||
|
||||
# Handle aborts
|
||||
if aborted:
|
||||
for group in state.test_groups:
|
||||
for test in group.test_objects:
|
||||
if test.status == 'Pending':
|
||||
test.set_status('Aborted')
|
||||
|
||||
# Show results
|
||||
show_results(state)
|
||||
|
||||
# Done
|
||||
state.save_debug_reports()
|
||||
atexit.unregister(state.save_debug_reports)
|
||||
if quick_mode:
|
||||
cli.pause('Press Enter to exit...')
|
||||
else:
|
||||
cli.pause('Press Enter to return to main menu...')
|
||||
|
||||
|
||||
def show_failed_attributes(state: State) -> None:
|
||||
"""Show failed attributes for all disks."""
|
||||
for dev in state.disks:
|
||||
cli.print_colored([dev.name, dev.description], ['CYAN', None])
|
||||
cli.print_report(
|
||||
hw_smart.generate_attribute_report(dev, only_failed=True),
|
||||
)
|
||||
cli.print_standard('')
|
||||
|
||||
|
||||
def show_results(state: State) -> None:
|
||||
"""Show test results by device."""
|
||||
std.sleep(0.5)
|
||||
state.ui.clear_current_pane()
|
||||
state.update_title_text('Results')
|
||||
|
||||
# CPU Tests
|
||||
cpu_tests_enabled = [
|
||||
group.name for group in state.test_groups if 'CPU' in group.name
|
||||
]
|
||||
if cpu_tests_enabled:
|
||||
cli.print_success('CPU:')
|
||||
cli.print_report(state.system.generate_report())
|
||||
cli.print_standard(' ')
|
||||
|
||||
# Disk Tests
|
||||
disk_tests_enabled = [
|
||||
group.name for group in state.test_groups if 'Disk' in group.name
|
||||
]
|
||||
if disk_tests_enabled:
|
||||
cli.print_success(f'Disk{"s" if len(state.disks) > 1 else ""}:')
|
||||
for disk in state.disks:
|
||||
cli.print_report(disk.generate_report())
|
||||
cli.print_standard(' ')
|
||||
if not state.disks:
|
||||
cli.print_warning('No devices')
|
||||
cli.print_standard(' ')
|
||||
|
||||
|
||||
def sync_clock() -> None:
|
||||
"""Sync clock under macOS using sntp."""
|
||||
cmd = ['sudo', 'sntp', '-Ss', 'us.pool.ntp.org']
|
||||
proc = exe.run_program(cmd, check=False)
|
||||
if proc.returncode:
|
||||
# Assuming we're running under an older version of macOS
|
||||
cmd[2] = '-s'
|
||||
exe.run_program(cmd, check=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||