Compare commits

..

18 commits
dev-pe ... dev

Author SHA1 Message Date
875166b683
Replace WMIC sections
Addresses issue #227
2024-11-23 15:27:17 -08:00
179748c469
Add Windows 11 24H2 to version list 2024-11-22 07:27:44 -08:00
ffcee1156a
Remove stale docopt files 2024-09-30 01:52:07 -07:00
edd944a325
Hide extra partitions when building UFDs
Addresses issue #226
2024-09-28 23:22:31 -07:00
75119c15ad
Fix bug in ddrescue-tui argument parsing 2024-09-11 18:11:12 -07:00
e6db63c8b0
Fix typo 2024-09-09 21:43:28 -07:00
e388b77639
Add OSFMount 2024-09-09 21:40:19 -07:00
bbdef56df2
Add OSFMount 2024-09-09 21:05:36 -07:00
6a5a944ea0
Refactor notepad replacement in WinPE
The registry method has proved problematic.  This DOSKEY method seems
less error prone since it's evaluated at runtime.
2024-09-09 20:12:54 -07:00
3621914312
Remove temporary WinPE WIM file 2024-09-08 13:28:31 -07:00
0ef9412995
Switch back to ConEmu in WinPE 2024-09-08 03:53:48 -07:00
0335797a5d
Update WinPE sections 2024-09-08 03:04:33 -07:00
2a07aebff3
Move macOS icons into a tar archive 2024-09-08 00:13:47 -07:00
244d917c73
Drop remaining docopt references 2024-09-04 01:14:34 -07:00
13b8dc8696
Replace remaining docopt sections 2024-09-04 00:53:42 -07:00
58576cbdb4
Use FAT32 for all UFD partitions
This breaks macOS images but those are deprecated at this point.
2024-09-04 00:24:03 -07:00
a3a7b25512
Fix case_insensitive_search 2024-09-04 00:16:54 -07:00
50033f42f6
Move to argparse for ddrescue-tui and hw-diags 2024-09-03 01:12:00 -07:00
155 changed files with 3507 additions and 13798 deletions

7
LICENSE.txt Normal file
View file

@ -0,0 +1,7 @@
Copyright (c) 2023 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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

BIN
images/ConEmu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 KiB

BIN
images/Linux.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
images/Pxelinux.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

BIN
images/Syslinux.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 KiB

After

Width:  |  Height:  |  Size: 168 KiB

BIN
images/WizardHat.xcf Normal file

Binary file not shown.

122
images/logo.svg Normal file
View file

@ -0,0 +1,122 @@
<?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>

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 756 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

BIN
images/macOS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 98 KiB

View file

@ -45,7 +45,6 @@ BASE_MENUS = {
MenuEntry('CHKDSK', 'auto_chkdsk'), MenuEntry('CHKDSK', 'auto_chkdsk'),
MenuEntry('DISM RestoreHealth', 'auto_dism'), MenuEntry('DISM RestoreHealth', 'auto_dism'),
MenuEntry('SFC Scan', 'auto_sfc'), MenuEntry('SFC Scan', 'auto_sfc'),
MenuEntry('Fix File Associations', 'auto_fix_file_associations'),
MenuEntry('Clear Proxy Settings', 'auto_reset_proxy'), MenuEntry('Clear Proxy Settings', 'auto_reset_proxy'),
MenuEntry('Disable Pending Renames', 'auto_disable_pending_renames'), MenuEntry('Disable Pending Renames', 'auto_disable_pending_renames'),
MenuEntry('Registry Repairs', 'auto_repair_registry'), MenuEntry('Registry Repairs', 'auto_repair_registry'),
@ -53,23 +52,15 @@ BASE_MENUS = {
MenuEntry('Reset Windows Policies', 'auto_reset_windows_policies'), MenuEntry('Reset Windows Policies', 'auto_reset_windows_policies'),
), ),
'Malware Cleanup': ( 'Malware Cleanup': (
MenuEntry('Disable Defender Scans', 'auto_disable_defender', selected=False),
MenuEntry('BleachBit', 'auto_bleachbit'), MenuEntry('BleachBit', 'auto_bleachbit'),
MenuEntry('HitmanPro', 'auto_hitmanpro'), MenuEntry('HitmanPro', 'auto_hitmanpro'),
MenuEntry('KVRT', 'auto_kvrt'), MenuEntry('KVRT', 'auto_kvrt'),
MenuEntry('EmsisoftCmd', 'auto_emsisoft_cmd_run', selected=False), MenuEntry('Windows Defender', 'auto_microsoft_defender'),
MenuEntry(REBOOT_STR, 'auto_reboot', selected=False),
MenuEntry('EmsisoftCmd (Uninstall)', 'auto_emsisoft_cmd_uninstall', selected=False),
MenuEntry('Enable Defender Scans', 'auto_enable_defender', selected=False),
MenuEntry('Windows Defender', 'auto_microsoft_defender', selected=False),
MenuEntry('Remove Custom Power Plan', 'auto_remove_power_plan'), MenuEntry('Remove Custom Power Plan', 'auto_remove_power_plan'),
MenuEntry(REBOOT_STR, 'auto_reboot'),
), ),
'Manual Steps': ( 'Manual Steps': (
MenuEntry('Malwarebytes (Install)', 'auto_mbam_install'),
MenuEntry(REBOOT_STR, 'auto_reboot'),
MenuEntry('AdwCleaner', 'auto_adwcleaner'), MenuEntry('AdwCleaner', 'auto_adwcleaner'),
MenuEntry('Malwarebytes (Run)', 'auto_mbam_run'),
MenuEntry('Malwarebytes (Uninstall)', 'auto_mbam_uninstall'),
MenuEntry('Bulk Crap Uninstaller', 'auto_bcuninstaller'), MenuEntry('Bulk Crap Uninstaller', 'auto_bcuninstaller'),
MenuEntry('Enable Windows Updates', 'auto_windows_updates_enable'), MenuEntry('Enable Windows Updates', 'auto_windows_updates_enable'),
), ),
@ -90,112 +81,6 @@ BASE_MENUS = {
} }
PRESETS = { PRESETS = {
'Default': { # Will be expanded at runtime using BASE_MENUS 'Default': { # Will be expanded at runtime using BASE_MENUS
'Options': (
'Run AVRemover (once)',
'Run RKill',
'Sync Clock',
),
},
'Data Transfer (to a clean Windows install)': {
'Backup Settings': (
'Enable RegBack',
'Enable System Restore',
'Set System Restore Size',
'Reset Power Plans',
'Set Custom Power Plan',
),
'Malware Cleanup': (
'Disable Defender Scans',
'BleachBit',
'HitmanPro',
'KVRT',
'Enable Defender Scans',
'Remove Custom Power Plan',
),
'Manual Steps': (
'Malwarebytes (Install)',
REBOOT_STR,
'AdwCleaner',
'Malwarebytes (Run)',
'Malwarebytes (Uninstall)',
'Enable Windows Updates',
),
'Options': (
'Sync Clock',
),
},
'Full Malware/Virus Cleanup': {
'Backup Settings': (
'Enable RegBack',
'Enable System Restore',
'Set System Restore Size',
'Create System Restore',
'Backup Browsers',
'Backup Power Plans',
'Reset Power Plans',
'Set Custom Power Plan',
'Backup Registry',
),
'Windows Repairs': (
'Disable Windows Updates',
'Reset Windows Updates',
REBOOT_STR,
'CHKDSK',
'DISM RestoreHealth',
'SFC Scan',
'Fix File Associations',
'Clear Proxy Settings',
'Disable Pending Renames',
'Registry Repairs',
'Reset UAC',
'Reset Windows Policies',
),
'Malware Cleanup': (
'Disable Defender Scans',
'BleachBit',
'HitmanPro',
'KVRT',
'EmsisoftCmd',
REBOOT_STR,
'EmsisoftCmd (Uninstall)',
'Enable Defender Scans',
'Windows Defender',
'Remove Custom Power Plan',
),
'Manual Steps': (
'Malwarebytes (Install)',
REBOOT_STR,
'AdwCleaner',
'Malwarebytes (Run)',
'Malwarebytes (Uninstall)',
'Bulk Crap Uninstaller',
'Enable Windows Updates',
),
'Options': (
'Run AVRemover (once)',
'Run RKill',
'Sync Clock',
),
},
'Minimal Repairs': {
'Backup Settings': (
'Enable RegBack',
'Enable System Restore',
'Set System Restore Size',
'Create System Restore',
'Backup Browsers',
'Backup Power Plans',
'Backup Registry',
),
'Windows Repairs': (
'CHKDSK',
'DISM RestoreHealth',
'SFC Scan',
'Fix File Associations',
'Clear Proxy Settings',
'Disable Pending Renames',
'Registry Repairs',
),
'Options': ( 'Options': (
'Run RKill', 'Run RKill',
'Sync Clock', 'Sync Clock',

View file

@ -34,24 +34,17 @@ BASE_MENUS = {
), ),
'Install Software': ( 'Install Software': (
MenuEntry('Winget', 'auto_install_winget'), MenuEntry('Winget', 'auto_install_winget'),
MenuEntry('ESET NOD32 AV', 'auto_install_eset_nod32_av', selected=False),
MenuEntry('ESET NOD32 AV (MSP)', 'auto_install_eset_nod32_av_msp', selected=False),
MenuEntry('Firefox', 'auto_install_firefox'), MenuEntry('Firefox', 'auto_install_firefox'),
MenuEntry('LibreOffice', 'auto_install_libreoffice', selected=False), MenuEntry('LibreOffice', 'auto_install_libreoffice', selected=False),
MenuEntry('Open-Shell', 'auto_install_open_shell', selected=False), MenuEntry('Open Shell', 'auto_install_open_shell'),
MenuEntry('Software Bundle', 'auto_install_software_bundle'), MenuEntry('Software Bundle', 'auto_install_software_bundle'),
MenuEntry('Software Upgrades', 'auto_install_software_upgrades', selected=False), MenuEntry('Software Upgrades', 'auto_install_software_upgrades'),
MenuEntry('Visual C++ Runtimes', 'auto_install_vcredists'), MenuEntry('Visual C++ Runtimes', 'auto_install_vcredists'),
), ),
'Configure System': ( 'Configure System': (
MenuEntry('Apply ITS Settings', 'auto_apply_its_settings', selected=False), MenuEntry('Open Shell', 'auto_config_open_shell'),
MenuEntry('Open-Shell', 'auto_config_open_shell', selected=False),
MenuEntry('Disable Chrome Notifications', 'auto_disable_chrome_notifications', selected=False),
MenuEntry('Disable Fast Startup', 'auto_disable_fast_startup', selected=False),
MenuEntry('Disable Password Expiration', 'auto_disable_password_expiration'), MenuEntry('Disable Password Expiration', 'auto_disable_password_expiration'),
MenuEntry('Disable Telemetry', 'auto_shutup_10'),
MenuEntry('Enable BSoD MiniDumps', 'auto_enable_bsod_minidumps'), MenuEntry('Enable BSoD MiniDumps', 'auto_enable_bsod_minidumps'),
MenuEntry('Enable Hibernation', 'auto_enable_hibernation', selected=False),
MenuEntry('Enable RegBack', 'auto_enable_regback'), MenuEntry('Enable RegBack', 'auto_enable_regback'),
MenuEntry('Enable System Restore', 'auto_system_restore_enable'), MenuEntry('Enable System Restore', 'auto_system_restore_enable'),
MenuEntry('Set System Restore Size', 'auto_system_restore_set_size'), MenuEntry('Set System Restore Size', 'auto_system_restore_set_size'),
@ -63,7 +56,7 @@ BASE_MENUS = {
MenuEntry('Create System Restore', 'auto_system_restore_create'), MenuEntry('Create System Restore', 'auto_system_restore_create'),
), ),
'System Information': ( 'System Information': (
MenuEntry('AIDA64 Report', 'auto_export_aida64_report', selected=False), MenuEntry('AIDA64 Report', 'auto_export_aida64_report'),
MenuEntry('Backup Registry', 'auto_backup_registry'), MenuEntry('Backup Registry', 'auto_backup_registry'),
), ),
'System Summary': ( 'System Summary': (
@ -77,9 +70,9 @@ BASE_MENUS = {
), ),
'Run Programs': ( 'Run Programs': (
MenuEntry('Device Manager', 'auto_open_device_manager'), MenuEntry('Device Manager', 'auto_open_device_manager'),
MenuEntry('HWiNFO Sensors', 'auto_open_hwinfo_sensors'),
MenuEntry('Microsoft Store Updates', 'auto_open_microsoft_store_updates'), MenuEntry('Microsoft Store Updates', 'auto_open_microsoft_store_updates'),
MenuEntry('Snappy Driver Installer', 'auto_open_snappy_driver_installer_origin'), MenuEntry('Snappy Driver Installer', 'auto_open_snappy_driver_installer_origin'),
MenuEntry('Webcam Tests', 'auto_open_mic_and_webcam_tests'),
MenuEntry('Windows Activation', 'auto_open_windows_activation'), MenuEntry('Windows Activation', 'auto_open_windows_activation'),
MenuEntry('Windows Updates', 'auto_open_windows_updates'), MenuEntry('Windows Updates', 'auto_open_windows_updates'),
MenuEntry('XMPlay', 'auto_open_xmplay'), MenuEntry('XMPlay', 'auto_open_xmplay'),
@ -96,7 +89,7 @@ PRESETS = {
'Additional User': { 'Additional User': {
'Configure System': ( 'Configure System': (
'Configure Browsers', 'Configure Browsers',
'Open-Shell', 'Open Shell',
'uBlock Origin', 'uBlock Origin',
'Enable BSoD MiniDumps', 'Enable BSoD MiniDumps',
'Enable RegBack', 'Enable RegBack',
@ -143,6 +136,7 @@ PRESETS = {
), ),
'Run Programs': ( 'Run Programs': (
'Device Manager', 'Device Manager',
'HWiNFO Sensors',
'XMPlay', 'XMPlay',
), ),
}, },

13
scripts/check_av.ps1 Normal file
View file

@ -0,0 +1,13 @@
# 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

View file

@ -0,0 +1,13 @@
# 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

View file

@ -2,19 +2,10 @@
"""WizardKit: ddrescue TUI""" """WizardKit: ddrescue TUI"""
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
from docopt import docopt
import wk import wk
if __name__ == '__main__': if __name__ == '__main__':
try:
docopt(wk.clone.ddrescue.DOCSTRING)
except SystemExit:
print('')
wk.ui.cli.pause('Press Enter to exit...')
raise
try: try:
wk.clone.ddrescue.main() wk.clone.ddrescue.main()
except SystemExit: except SystemExit:

View file

@ -0,0 +1,13 @@
# 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

View file

@ -1,79 +0,0 @@
# Export Bitlocker info
# Init
$REPORT = ""
$SKIPPED_PROPERTIES = @(
"CapacityGB",
"MountPoint",
"KeyProtector"
)
# Functions
function Convert-BytesToString ($bytes) {
If ($bytes -gt 1PB) {
return ("{0:0.#} PB" -f ($bytes / 1PB) )
} ElseIf ($bytes -gt 1TB) {
return ("{0:0.#} TB" -f ($bytes / 1TB) )
} ElseIf ($bytes -gt 1GB) {
return ("{0:0.#} GB" -f ($bytes / 1GB) )
} ElseIf ($bytes -gt 1MB) {
return ("{0:0.#} MB" -f ($bytes / 1MB) )
} ElseIf ($bytes -gt 1KB) {
return ("{0:0.#} KB" -f ($bytes / 1KB) )
} Else {
return ("{0} B" -f $bytes)
}
}
# Build report
$system_drive = $env:SystemDrive
if ($system_drive -eq "X:") {
# Assuming we're running in WinPE
$system_drive = "C:"
}
Get-BitlockerVolume -MountPoint $system_drive | ForEach-Object {
$bitlocker_volume = $_
$REPORT += ("`n`nDrive {0}`n---`n" -f $bitlocker_volume.MountPoint)
# Size info
$volume = Get-Volume -DriveLetter $bitlocker_volume.MountPoint[0]
$total = Convert-BytesToString ($volume.Size)
$used = Convert-BytesToString ($volume.Size - $volume.SizeRemaining)
if ($volume.Size -gt 0) {
$REPORT += ("Size: {0} ({1} used)`n" -f $total, $used)
} else {
$REPORT += "Size: Unknown`n"
}
# Volume info
$bitlocker_volume |
Get-Member -MemberType Property |
Where-Object {! $SKIPPED_PROPERTIES.Contains($_.Name)} |
ForEach-Object {
$name = $_.Name
if ($bitlocker_volume.$name -ne $null) {
$REPORT += ("{0}: {1}`n" -f $name, $bitlocker_volume.$name)
}
}
# Key info
$bitlocker_volume.KeyProtector |
ForEach-Object {
$key = $_
$REPORT += "Key Slot:`n"
$key |
Get-Member -MemberType Property |
ForEach-Object {
$name = $_.Name
if ($key.$name -ne $null -and $key.$name -ne "") {
$REPORT += ("... {0}: {1}`n" -f $name, $key.$name)
}
}
}
}
# Show report
Write-Host $REPORT.Trim()
# vim: sts=2 sw=2 ts=2

View file

@ -2,19 +2,10 @@
"""WizardKit: Hardware Diagnostics""" """WizardKit: Hardware Diagnostics"""
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
from docopt import docopt
import wk import wk
if __name__ == '__main__': if __name__ == '__main__':
try:
docopt(wk.hw.diags.DOCSTRING)
except SystemExit:
print('')
wk.ui.cli.pause('Press Enter to exit...')
raise
try: try:
wk.hw.diags.main() wk.hw.diags.main()
except SystemExit: except SystemExit:

View file

@ -1,4 +0,0 @@
#!/bin/zsh
#
sudo hw-info | less -S --use-color

View file

@ -56,7 +56,6 @@ for %%f in (%*) do (
if defined _backups mkdir "%client_dir%\Backups">nul 2>&1 if defined _backups mkdir "%client_dir%\Backups">nul 2>&1
if defined _logs ( if defined _logs (
mkdir "%log_dir%\%KIT_NAME_FULL%">nul 2>&1 mkdir "%log_dir%\%KIT_NAME_FULL%">nul 2>&1
mkdir "%log_dir%\d7II">nul 2>&1
mkdir "%log_dir%\Tools">nul 2>&1) mkdir "%log_dir%\Tools">nul 2>&1)
if defined _office mkdir "%client_dir%\Office">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 _quarantine mkdir "%client_dir%\Quarantine">nul 2>&1

View file

@ -1,15 +1,78 @@
"""WizardKit: Launch Snappy Driver Installer Origin""" """WizardKit: Launch Snappy Driver Installer Origin"""
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import wk 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 # 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__': if __name__ == '__main__':
wk.ui.cli.set_title( wk.ui.cli.set_title(
f'{wk.cfg.main.KIT_NAME_FULL}: Snappy Driver Installer Origin Launcher', f'{wk.cfg.main.KIT_NAME_FULL}: Snappy Driver Installer Origin Launcher',
) )
log_dir = wk.log.format_log_path(tool=True).parent log_dir = wk.log.format_log_path(tool=True).parent
USE_NETWORK = False
# Windows 11 workaround # Windows 11 workaround
if wk.os.win.OS_VERSION == 11: if wk.os.win.OS_VERSION == 11:
@ -19,6 +82,18 @@ if __name__ == '__main__':
if any([wk.os.win.get_service_status(s) != 'stopped' for s in appid_services]): if any([wk.os.win.get_service_status(s) != 'stopped' for s in appid_services]):
raise wk.std.GenericWarning('Failed to stop AppID services') raise wk.std.GenericWarning('Failed to stop AppID services')
sdio_path = wk.kit.tools.get_sdio_path(interactive=True) # Try to mount server
cmd = [sdio_path, '-log_dir', log_dir] try:
wk.exe.popen_program(cmd, cwd=sdio_path.parent) 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)

View file

@ -1,9 +0,0 @@
"""WizardKit: Launch Stress Tests Tool"""
# vim: sts=2 sw=2 ts=2
import wk
wk.kit.tools.run_tool('CoreTemp', 'CoreTemp', popen=True)
wk.kit.tools.run_tool('FurMark2', 'FurMark_GUI', popen=True)
wk.kit.tools.run_tool('Prime95', 'prime95', popen=True)
wk.kit.tools.run_tool('RUST', 'RUST', popen=True)

View file

@ -1,9 +0,0 @@
"""WizardKit: Launch Stress Tests Tool"""
# vim: sts=2 sw=2 ts=2
import wk
wk.kit.tools.run_tool('CoreTemp', 'CoreTemp', popen=True)
wk.kit.tools.run_tool('FurMark', 'FurMark', popen=True)
wk.kit.tools.run_tool('Prime95', 'prime95', popen=True)
wk.kit.tools.run_tool('RUST', 'RUST', popen=True)

View file

@ -11,13 +11,9 @@
"wk/ui/__init__.py" = ["F401"] "wk/ui/__init__.py" = ["F401"]
# Long lines # Long lines
"auto_setup.py" = ["E501"] "wk/borrowed/acpi.py" = ["E501", "F841"]
"wk/cfg/ddrescue.py" = ["E501"] "wk/cfg/ddrescue.py" = ["E501"]
"wk/cfg/hw.py" = ["E501"] "wk/cfg/hw.py" = ["E501"]
"wk/cfg/launchers.py" = ["E501"] "wk/cfg/launchers.py" = ["E501"]
"wk/cfg/setup.py" = ["E501"] "wk/cfg/setup.py" = ["E501"]
"wk/cfg/sources.py" = ["E501"] "wk/cfg/sources.py" = ["E501"]
# Misc
"set_lp8550_slope.py" = ["E501", "E741"]
"wk/borrowed/acpi.py" = ["E501", "F841"]

View file

@ -1,103 +0,0 @@
#!/usr/bin/env python3
###
# Dependencies: Linux, python3, python3-smbus
# Usage: `sudo ./set_lp8550_slope.py`
#
# Default slope is 0b101=200ms, can be adjusted below
# Tested with Ubuntu 20.04 on A1466 2013
# All original EEPROM registers value:
# - A0: 0b01111111
# - A1: 0b10110101
# - A2: 0b10111111
# - A3: 0b01111011
# - A4: 0b00101000
# - A5: 0b11001111
# - A6: 0b01100100
# - A7: 0b00101101
###
import time
import smbus2
config = {
'smbus_address': 0, # SMBus address, can use `i2cdetect -l` to find it
'device_address': 0x2C, # LP8550 7 bits address
'slope': (1, 0, 1) # The new slope to set, MSB first, see lp8550 datasheet 8.6.2.3 SLOPE
}
lp8550_regs = {
'EEPROMControl': 0x72,
'EEPROMAddress1': 0xA1 # default value for this register is 0b10110101 on original 820-3437
}
def bitstolist(val, regsize=8):
return [(val >> i) & 0b1 for i in range(0, regsize)]
def listtobits(l, regsize=8):
val = 0
for i in range(0, regsize):
val += l[i] << i
return val
def read_reg(bus, dev_address, reg_name):
return bitstolist(bus.read_byte_data(dev_address, lp8550_regs[reg_name]))
def write_reg(bus, dev_address, reg_name, reg):
print(f'Writing { listtobits(reg)} to {dev_address}, {lp8550_regs[reg_name]}')
bus.write_byte_data(dev_address, lp8550_regs[reg_name], listtobits(reg))
def get_slope(bus, dev_address):
return read_reg(bus, dev_address, 'EEPROMAddress1')[0:3][::-1]
def set_slope(bus, dev_address, slope):
print('Original slope: ', get_slope(bus, dev_address))
EEPROMAddress1 = read_reg(bus, dev_address, 'EEPROMAddress1')
EEPROMAddress1[0:3] = slope[::-1]
write_reg(bus, dev_address, 'EEPROMAddress1', EEPROMAddress1)
print('New slope: ', get_slope(bus, dev_address))
def write_nvm(bus, dev_address):
print('Writing to non-volatile memory')
EEPROMControl = [0]*8
EEPROMControl[2] = 1 # EE_INIT=1
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
EEPROMControl[1] = 1 # EE_PROG=1
EEPROMControl[2] = 0 # EE_INIT=0
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
time.sleep(0.2) # wait 200ms
EEPROMControl[1] = 0 # EE_PROG=0
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
def load_nvm(bus, dev_address):
print('Loading from non-volatile memory')
EEPROMControl = [0]*8
EEPROMControl[2] = 1 # EE_INIT=1
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
EEPROMControl[0] = 1 # EE_READ=1
EEPROMControl[2] = 0 # EE_INIT=0
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
time.sleep(0.2) # wait 200ms
EEPROMControl[0] = 0 # EE_READ=0
write_reg(bus, dev_address, 'EEPROMControl', EEPROMControl)
def main():
bus = smbus2.SMBus(config['smbus_address'])
set_slope(bus, config['device_address'], config['slope'])
write_nvm(bus, config['device_address'])
load_nvm(bus, config['device_address'])
print('Slope in non-volatile memory: ', get_slope(bus,config['device_address']))
if __name__ == "__main__":
main()

View file

@ -25,53 +25,6 @@ if PLATFORM not in ('macOS', 'Linux'):
# Functions # Functions
def compress(reason):
dest = pathlib.Path(f'~/{reason}_{NOW.strftime("%Y-%m-%dT%H%M%S%z")}.txz')
dest = dest.expanduser().resolve()
cmd = ['tar', 'caf', dest.name, LOG_DIR.name]
wk.exe.run_program(cmd, check=False)
return dest
def compress_live_macos(reason):
"""Compress log_dir using the RAM disk."""
dest = pathlib.Path(
f'/Volumes/RAM_Disk/{reason}_{NOW.strftime("%Y-%m-%dT%H%M%S%z")}.txz',
)
tmp_file = dest.with_suffix('.tar')
cmd = ['tar', '-cf', tmp_file.name, LOG_DIR.name]
wk.exe.run_program(cmd, check=False)
cmd = ['xz', '-z', tmp_file.name]
wk.exe.run_program(cmd, check=False)
tmp_file = tmp_file.with_suffix('.tar.xz')
tmp_file.rename(dest.name)
return dest
def compress_and_upload(reason='Testing'):
"""Upload compressed log_dir to the crash server."""
server = wk.cfg.net.CRASH_SERVER
upload_args = {
'headers': server['Headers'],
'auth': (server['User'], server['Pass']),
}
# Compress LOG_DIR (relative to parent dir)
if os.path.exists('/.wk-live-macos'):
dest = compress_live_macos(reason)
else:
dest = compress(reason)
upload_args['data'] = dest.read_bytes()
# Upload compressed data
url = f'{server["Url"]}/{dest.name}'
result = requests.put(url, **upload_args)
# Check result
if not result.ok:
raise wk.std.GenericError('Failed to upload logs')
def main() -> None: def main() -> None:
"""Upload logs for review.""" """Upload logs for review."""
lines = [] lines = []
@ -89,7 +42,7 @@ def main() -> None:
# Get reason note # Get reason note
while True: while True:
text = wk.ui.cli.input_text('> ', allow_empty=True) text = wk.ui.cli.input_text('> ')
if not text: if not text:
lines.append('') lines.append('')
break break
@ -98,11 +51,9 @@ def main() -> None:
_f.write('\n'.join(lines)) _f.write('\n'.join(lines))
# Compress and upload logs # Compress and upload logs
os.chdir(LOG_DIR.parent)
print(os.getcwd())
result = try_and_print.run( result = try_and_print.run(
message='Uploading logs...', message='Uploading logs...',
function=compress_and_upload, function=upload_log_dir,
reason='Review', reason='Review',
) )
if not result['Failed']: if not result['Failed']:

View file

@ -1,13 +1,13 @@
"""WizardKit: wk module init""" """WizardKit: wk module init"""
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import platform
from sys import stderr, version_info from sys import stderr, version_info
from . import cfg from . import cfg
from . import clone from . import clone
from . import debug from . import debug
from . import exe from . import exe
from . import graph
from . import hw from . import hw
from . import io from . import io
from . import kit from . import kit
@ -19,10 +19,6 @@ from . import setup
from . import std from . import std
from . import ui from . import ui
if platform.system() != 'Windows':
from wk import graph
from wk import osticket
# Check env # Check env
if version_info < (3, 10): if version_info < (3, 10):

View file

@ -1,36 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIGTzCCBDegAwIBAgIBfDANBgkqhkiG9w0BAQsFADCBsDELMAkGA1UEBhMCVVMx
DzANBgNVBAgTBk9yZWdvbjERMA8GA1UEBxMIUG9ydGxhbmQxHTAbBgNVBAoTFDEy
MDEgQ29tcHV0ZXIgUmVwYWlyMSMwIQYDVQQLExoxMjAxIENlcnRpZmljYXRlIEF1
dGhvcml0eTEVMBMGA1UEAxMMMTIwMSBSb290IENBMSIwIAYJKoZIhvcNAQkBFhNt
YW5hZ2VtZW50QDEyMDEuY29tMB4XDTE4MDgyMDA2MDEwMFoXDTM4MDgyMDA2MDEw
MFowgbAxCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZPcmVnb24xETAPBgNVBAcTCFBv
cnRsYW5kMR0wGwYDVQQKExQxMjAxIENvbXB1dGVyIFJlcGFpcjEjMCEGA1UECxMa
MTIwMSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFTATBgNVBAMTDDEyMDEgUm9vdCBD
QTEiMCAGCSqGSIb3DQEJARYTbWFuYWdlbWVudEAxMjAxLmNvbTCCAiIwDQYJKoZI
hvcNAQEBBQADggIPADCCAgoCggIBANGYohJk5/CC/p14R7EpnhdEUF7Wvlnt8yuF
dtuyStlIGkLxPMlj9hQfoLDplQqlKBefTaI3WwrI/Hndso+jStLKgtRWRdyNB34K
AWqT04zXYGicdi3fqaMhEC4SPyX1tRXU2e9kjtIJ21AZx2F40NUjfOMKLVymZgXm
gkG1oA/BSzE8vIidrd/lJPwo0u+EYFa87y+9SHS93Ze1AVoTVqUzSMkjqt+6YIzJ
4XBD7UBvps0Mnd18HMUlXHFXusUL1K921W3wDVcMlNIIA8MJjQk+aVS/1EGSn+81
C+r40x64lYkyh0ZUAHkVXUC/BUfa0SKx1Nfa4mSvtyPnUCb7Dir8MkTDKgopGCok
KmW+VvE2H8AEPCbcctFmhdip19laYxzyDhZ5wiQN6AOg64cWvDf6/uT9hyPvxkj1
ps5vWElryzawTE7h1BI8liMqwsG1Y7cc6D0PABxPsp4iR8pde0oZtpLnEvejRodo
zz3BGvZjq+pHtRMjL+yiDtdAL+K+7/e7gNCQBIGsphahWIOf7TczWVgMNclTNxl3
WZjKkOEs7j+prRTDvffV6H32+Tk5TwgMsfvnY4a37CkJ0L0d1JhWj9wO+gESfg3W
8yvt3hfcj3NOUMJWhJstqlIeX8dj7vVcMhjNvYJxabJmJgk+DNlHe55fXDGJ1CLO
E0EbRTyBAgMBAAGjcjBwMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFM+hXjFx
6BldZFBQW1Pn/Yp3vbw+MAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAAcw
HgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOC
AgEALWcnu3auMSnSSF/kOiLvJ4RAnHZebGYNcUWM14u1K1/XtTB7AFzQIHX7BcDH
m/z4UEyhl9EdR5Bgf2Szuk+8+LyGqcdAdbPoK+bmcwwL8lufDnlIYBThKIBfU2Xw
vw41972B+HH5r1TZXve1EdJaLyImbxmq5s41oH7djGC+sowtyGuVqP7RBguXBGiJ
At1yfdPWVaxLmE8QFknkIvpgTmELpxasTfvgnQBenA3Ts0Z2hwN4796hLbRzGsb8
4hKWAfQDP0klzXKRRyVeAueXxj/FcNZilYxv15MqMc4qrUiW0hXHluQM1yceNjNZ
SE4Igi1Ap71L4PpgkHIDfZD908UexGGkql+p4EWrpnXUYWTa0sHg1bFKQntgpyFg
86Ug0Q7ZNhImENzeigZknL0ceIdaNUCs7UPrkqpUSJR2yujp1JC3tX1LgKZw8B3J
fQx/8h3zzNuz5dVtr1wUJaUD0nGhMIRBEXb2t4jupEISSTN1pkHPcbNzhAQXjVUA
CJxnnz3jmyGsNCoQf7NWfaN6RSRTWehsC6m7JvPvoU2EZoQkSlNOv4xZuFpEx0u7
MFDtC1cSGPH7YwYXPVc45xAMC6Ni8mvq93oT89XZNHIqE8/T8aPHLwYFgu1b1r/A
L8oMEnG5s8tG3n0DcFoOYsaIzVeP0r7B7e3zKui6DQLuu9E=
-----END CERTIFICATE-----

View file

@ -5,6 +5,7 @@ from . import hw
from . import launchers from . import launchers
from . import log from . import log
from . import main from . import main
from . import music
from . import net from . import net
from . import repairs from . import repairs
from . import setup from . import setup

View file

@ -6,7 +6,6 @@
TMUX_SIDE_WIDTH = 21 TMUX_SIDE_WIDTH = 21
TMUX_LAYOUT = { TMUX_LAYOUT = {
'Source': {'height': 2, 'Check': True}, 'Source': {'height': 2, 'Check': True},
'Ticket': {'height': 2, 'Check': True},
'Started': {'width': TMUX_SIDE_WIDTH, 'Check': True}, 'Started': {'width': TMUX_SIDE_WIDTH, 'Check': True},
'Progress': {'width': TMUX_SIDE_WIDTH, 'Check': True}, 'Progress': {'width': TMUX_SIDE_WIDTH, 'Check': True},
} }

View file

@ -14,14 +14,11 @@ ATTRIBUTE_COLORS = (
# NOTE: Force 4K read block size for disks >= 3TB # NOTE: Force 4K read block size for disks >= 3TB
BADBLOCKS_EXTRA_LARGE_DISK = 15 * 1024**4 BADBLOCKS_EXTRA_LARGE_DISK = 15 * 1024**4
BADBLOCKS_LARGE_DISK = 3 * 1024**4 BADBLOCKS_LARGE_DISK = 3 * 1024**4
BADBLOCKS_MAX_ERRORS = 1
BADBLOCKS_REGEX = re.compile( BADBLOCKS_REGEX = re.compile(
r'^Pass completed, (\d+) bad blocks found. .(\d+)/(\d+)/(\d+) errors', r'^Pass completed, (\d+) bad blocks found. .(\d+)/(\d+)/(\d+) errors',
re.IGNORECASE, re.IGNORECASE,
) )
BADBLOCKS_RESULTS_REGEX = re.compile( BADBLOCKS_RESULTS_REGEX = re.compile(r'^(.*?)\x08.*\x08(.*)')
r'^(Checking for bad blocks .read-only test.: ).*\x08+(done|\s+).*?(\x08+)?'
)
BADBLOCKS_SKIP_REGEX = re.compile(r'^(Checking|\[)', re.IGNORECASE) BADBLOCKS_SKIP_REGEX = re.compile(r'^(Checking|\[)', re.IGNORECASE)
CPU_TEMPS = { CPU_TEMPS = {
'Cooling Delta': 25, 'Cooling Delta': 25,
@ -39,7 +36,6 @@ IO_MINIMUM_TEST_SIZE = 10 * 1024**3
IO_RATE_REGEX = re.compile( IO_RATE_REGEX = re.compile(
r'(?P<bytes>\d+) bytes.* (?P<seconds>\S+) s(?:,|ecs )', r'(?P<bytes>\d+) bytes.* (?P<seconds>\S+) s(?:,|ecs )',
) )
IO_SMALL_DISK = 450 * 1000**3
KEY_NVME = 'nvme_smart_health_information_log' KEY_NVME = 'nvme_smart_health_information_log'
KEY_SMART = 'ata_smart_attributes' KEY_SMART = 'ata_smart_attributes'
KNOWN_DISK_ATTRIBUTES = { KNOWN_DISK_ATTRIBUTES = {
@ -86,18 +82,9 @@ NVME_WARNING_KEYS = (
'reliability_degraded', 'reliability_degraded',
'volatile_memory_backup_failed', 'volatile_memory_backup_failed',
) )
REGEX_BLOCK_GRAPH = re.compile(r'(▁|▂|▃|▄|▅|▆|▇|█)')
REGEX_POWER_ON_TIME = re.compile( REGEX_POWER_ON_TIME = re.compile(
r'^(\d+)([Hh].*|\s+\(\d+\s+\d+\s+\d+\).*)' r'^(\d+)([Hh].*|\s+\(\d+\s+\d+\s+\d+\).*)'
) )
REGEX_SMART_ATTRIBUTES = re.compile(
r'^\s*(?P<decimal>\d+) / (?P<hex>\w\w): (?P<data>.*)$',
)
REGEX_VOLUME = re.compile(
r'^(?P<dev>.*?) '
r'(?P<result>(APFS|CoreStorage) container|Failed to mount|Mounted on|\S+$)'
r'($| (?P<path>.*) \((?P<details>.*)\))'
)
SMART_SELF_TEST_START_TIMEOUT_IN_SECONDS = 120 SMART_SELF_TEST_START_TIMEOUT_IN_SECONDS = 120
SMC_IDS = { SMC_IDS = {
# Sources: https://github.com/beltex/SMCKit/blob/master/SMCKit/SMC.swift # Sources: https://github.com/beltex/SMCKit/blob/master/SMCKit/SMC.swift
@ -147,16 +134,15 @@ SMC_IDS = {
'TS0C': {'CPU Temp': False, 'Source': 'CPU B DIMM Exit Ambient'}, 'TS0C': {'CPU Temp': False, 'Source': 'CPU B DIMM Exit Ambient'},
} }
STATUS_COLORS = { STATUS_COLORS = {
'Done': 'CYAN', 'Passed': 'GREEN',
'Passed': 'GREEN', 'Aborted': 'YELLOW',
'Aborted': 'YELLOW', 'N/A': 'YELLOW',
'N/A': 'YELLOW', 'Skipped': 'YELLOW',
'Skipped': 'YELLOW', 'Unknown': 'YELLOW',
'Unknown': 'YELLOW', 'Working': 'YELLOW',
'Working': 'YELLOW', 'Denied': 'RED',
'Denied': 'RED', 'ERROR': 'RED',
'ERROR': 'RED', 'Failed': 'RED',
'Failed': 'RED',
'TimedOut': 'RED', 'TimedOut': 'RED',
} }
TEMP_COLORS = { TEMP_COLORS = {
@ -179,9 +165,8 @@ THRESH_SSD_MIN = 90 * 1024**2
THRESH_SSD_AVG_HIGH = 135 * 1024**2 THRESH_SSD_AVG_HIGH = 135 * 1024**2
THRESH_SSD_AVG_LOW = 100 * 1024**2 THRESH_SSD_AVG_LOW = 100 * 1024**2
# VOLUME THRESHOLDS in percent # VOLUME THRESHOLDS in percent
VOLUME_WARNING_THRESHOLD = 70 VOLUME_WARNING_THRESHOLD = 70
VOLUME_FAILURE_THRESHOLD = 85 VOLUME_FAILURE_THRESHOLD = 85
VOLUME_SIZE_THRESHOLD = 30 # In GB
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -133,15 +133,6 @@ LAUNCHERS = {
'L_ARGS': '-nodb', 'L_ARGS': '-nodb',
'L_ELEV': 'True', 'L_ELEV': 'True',
}, },
"Fab's Autobackup Pro": {
'L_TYPE': 'Executable',
'L_PATH': 'AutoBackupPro',
'L_ITEM': 'autobackup6pro.exe',
'Extra Code': [
r'call "%bin%\Scripts\init_client_dir.cmd"',
r'reg add HKCU\Software\1201-WizardKit /v FabLastRun /t REG_SZ /d %iso_date% /f >nul 2>&1',
],
},
'FastCopy (as ADMIN)': { 'FastCopy (as ADMIN)': {
'L_TYPE': 'Executable', 'L_TYPE': 'Executable',
'L_PATH': 'FastCopy', 'L_PATH': 'FastCopy',
@ -242,76 +233,40 @@ LAUNCHERS = {
'L_PATH': 'FurMark', 'L_PATH': 'FurMark',
'L_ITEM': 'FurMark.exe', '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++': { 'Notepad++': {
'L_TYPE': 'Executable', 'L_TYPE': 'Executable',
'L_PATH': 'notepadplusplus', 'L_PATH': 'notepadplusplus',
'L_ITEM': 'notepadplusplus.exe', 'L_ITEM': 'notepadplusplus.exe',
}, },
'Prime95': {
'L_TYPE': 'Executable',
'L_PATH': 'Prime95',
'L_ITEM': 'prime95.exe',
},
'PuTTY': { 'PuTTY': {
'L_TYPE': 'Executable', 'L_TYPE': 'Executable',
'L_PATH': 'PuTTY', 'L_PATH': 'PuTTY',
'L_ITEM': 'PUTTY.EXE', 'L_ITEM': 'PUTTY.EXE',
}, },
'ShutUp10': {
'L_TYPE': 'Executable',
'L_PATH': 'ShutUp10',
'L_ITEM': 'OOSU10.exe',
},
'ShutUp10 (1201 Minimal Selection)': {
'L_TYPE': 'Executable',
'L_PATH': 'ShutUp10',
'L_ITEM': 'OOSU10.exe',
'L_ARGS': '1201.cfg',
},
'Stress Tests (Classic)': {
'L_TYPE': 'PyScript',
'L_PATH': 'Scripts',
'L_ITEM': 'launch_stress_tests_classic.py',
'L_ELEV': 'True',
},
'Stress Tests': {
'L_TYPE': 'PyScript',
'L_PATH': 'Scripts',
'L_ITEM': 'launch_stress_tests.py',
'L_ELEV': 'True',
},
'Windows Repair AIO': {
'L_TYPE': 'Executable',
'L_PATH': 'WinRepairAIO',
'L_ITEM': 'Repair_Windows.exe',
'L_ELEV': 'True',
'Extra Code': [
r'copy /y "%bin%\WinRepairAIO\__empty.ini" "%bin%\WinRepairAIO\settings.ini"',
],
},
'Windows Repair AIO (Fix Associations)': {
'L_TYPE': 'Executable',
'L_PATH': 'WinRepairAIO',
'L_ITEM': 'Repair_Windows.exe',
'L_ELEV': 'True',
'Extra Code': [
r'copy /y "%bin%\WinRepairAIO\__associations.ini" "%bin%\WinRepairAIO\settings.ini"',
],
},
'Windows Repair AIO (Fix Permissions)': {
'L_TYPE': 'Executable',
'L_PATH': 'WinRepairAIO',
'L_ITEM': 'Repair_Windows.exe',
'L_ELEV': 'True',
'Extra Code': [
r'copy /y "%bin%\WinRepairAIO\__permissions.ini" "%bin%\WinRepairAIO\settings.ini"',
],
},
'WinSCP': {
'L_TYPE': 'Executable',
'L_PATH': 'WinSCP',
'L_ITEM': 'WinSCP.exe',
},
'WizTree': { 'WizTree': {
'L_TYPE': 'Executable', 'L_TYPE': 'Executable',
'L_PATH': 'WizTree', 'L_PATH': 'WizTree',
@ -322,7 +277,7 @@ LAUNCHERS = {
'L_TYPE': 'Executable', 'L_TYPE': 'Executable',
'L_PATH': 'XMPlay', 'L_PATH': 'XMPlay',
'L_ITEM': 'xmplay.exe', 'L_ITEM': 'xmplay.exe',
'L_ARGS': r'"%bin%\XMPlay\music.m3u"', 'L_ARGS': r'"%bin%\XMPlay\music.7z"',
}, },
}, },
} }

View file

@ -8,21 +8,21 @@ NOTE: Non-standard formating is used for BASH/BATCH/PYTHON compatibility
# Features # Features
ENABLED_OPEN_LOGS=False ENABLED_OPEN_LOGS=False
ENABLED_TICKET_NUMBERS=False ENABLED_TICKET_NUMBERS=False
ENABLED_UPLOAD_DATA=True ENABLED_UPLOAD_DATA=False
# Main Kit # Main Kit
ARCHIVE_PASSWORD='Sorted1201' ARCHIVE_PASSWORD='Abracadabra'
KIT_NAME_FULL='1201-WizardKit' KIT_NAME_FULL='WizardKit'
KIT_NAME_SHORT='1201' KIT_NAME_SHORT='WK'
SUPPORT_MESSAGE='Please let support know by opening an issue on Gitea' SUPPORT_MESSAGE='Please let 2Shirt know by opening an issue on Gitea'
# Text Formatting # Text Formatting
INDENT=4 INDENT=4
WIDTH=32 WIDTH=32
# Live Linux # Live Linux
ROOT_PASSWORD='1201 loves computers!' ROOT_PASSWORD='Abracadabra'
TECH_PASSWORD='Sorted1201' TECH_PASSWORD='Abracadabra'
# Time Zones # Time Zones
## See 'timedatectl list-timezones' for valid Linux values ## See 'timedatectl list-timezones' for valid Linux values

76
scripts/wk/cfg/music.py Normal file
View file

@ -0,0 +1,76 @@
"""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

View file

@ -4,41 +4,38 @@
# Servers # Servers
BACKUP_SERVERS = { BACKUP_SERVERS = {
'Anaconda': { #'Server One': {
'Address': 'anaconda.1201.com', # 'Address': '10.0.0.10',
'Share': 'Backups', # 'Share': 'Backups',
'RO-User': 'cx', # 'RO-User': 'restore',
'RO-Pass': 'cx', # 'RO-Pass': 'Abracadabra',
'RW-User': 'backup', # 'RW-User': 'backup',
'RW-Pass': '1201 loves computers!', # 'RW-Pass': 'Abracadabra',
}, # },
#'Server Two': {
# 'Address': 'servertwo.example.com',
# 'Share': 'Backups',
# 'RO-User': 'restore',
# 'RO-Pass': 'Abracadabra',
# 'RW-User': 'backup',
# 'RW-Pass': 'Abracadabra',
# },
} }
BENCHMARK_SERVER = {
'Name': 'Nextcloud',
'Short Url': 'https://1201.ddns.net/nextcloud/f/14775',
'Url': 'https://1201.ddns.net/nextcloud/public.php/webdav/Benchmarks',
'User': 'LHdPTofNBQNGBbX',
'Pass': '',
}
CRASH_SERVER = { CRASH_SERVER = {
'Name': 'Nextcloud', #'Name': 'CrashServer',
'Url': 'https://1201.ddns.net/nextcloud/public.php/webdav/Issues', #'Url': '',
'User': 'fnyzeK64rKxcEL4', #'User': '',
'Pass': '', #'Pass': '',
'Headers': {'X-Requested-With': 'XMLHttpRequest'}, #'Headers': {'X-Requested-With': 'XMLHttpRequest'},
} }
SDIO_SERVER = { SDIO_SERVER = {
'Address': 'anaconda.1201.com', 'Address': '',
'Share': 'Snappy', 'Share': '',
'Path': '\\', 'Path': '',
'RO-User': 'cx', 'RO-User': '',
'RO-Pass': 'cx', 'RO-Pass': '',
} }
# Misc
IMGUR_CLIENT_ID='3d1ee1d38707b85'
if __name__ == '__main__': if __name__ == '__main__':
print("This file is not meant to be called directly.") print("This file is not meant to be called directly.")

View file

@ -1,17 +0,0 @@
"""Wizard Kit: Config - osTicket"""
# vim: sts=2 sw=2 ts=2
SQL = {
'DB': 'osticket',
'Host': 'osticket.1201.com',
'Port': 3306,
'User': 'wizardkit',
'Pass': 'U9bJnF9eamVkfsVw',
}
STAFF = {
'ID': '23',
'Name': 'Wizard Kit',
}
if __name__ == '__main__':
print("This file is not meant to be called directly.")

View file

@ -1,7 +1,7 @@
"""WizardKit: Config - Repairs""" """WizardKit: Config - Repairs"""
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
from wk.cfg.main import KIT_NAME_FULL, KIT_NAME_SHORT from wk.cfg.main import KIT_NAME_FULL
AUTO_REPAIR_DELAY_IN_SECONDS = 3 AUTO_REPAIR_DELAY_IN_SECONDS = 3
AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs' AUTO_REPAIR_KEY = fr'Software\{KIT_NAME_FULL}\Auto Repairs'
@ -59,7 +59,7 @@ BLEACH_BIT_CLEANERS = (
'windows_explorer.run', 'windows_explorer.run',
'windows_explorer.thumbnails', 'windows_explorer.thumbnails',
) )
CUSTOM_POWER_PLAN_NAME = f'{KIT_NAME_SHORT} Power Plan' CUSTOM_POWER_PLAN_NAME = f'{KIT_NAME_FULL} Power Plan'
CUSTOM_POWER_PLAN_DESC = 'Customized for the best experience.' CUSTOM_POWER_PLAN_DESC = 'Customized for the best experience.'
POWER_PLANS = { POWER_PLANS = {
'Balanced': '381b4222-f694-41f0-9685-ff5bb260df2e', 'Balanced': '381b4222-f694-41f0-9685-ff5bb260df2e',

View file

@ -9,9 +9,10 @@ BROWSER_PATHS = {
'Microsoft Edge': 'Microsoft/Edge/Application/msedge.exe', 'Microsoft Edge': 'Microsoft/Edge/Application/msedge.exe',
'Opera': 'Opera/launcher.exe', 'Opera': 'Opera/launcher.exe',
} }
FAB_TIMEFRAME = 14 # If it's been at least this many days don't prompt for an AV scan
DISABLED_ENTRIES_WINDOWS_11 = { DISABLED_ENTRIES_WINDOWS_11 = {
# Group Name: Option Name # Group Name: Option Name
'Install Software': 'Open Shell',
'Configure System': 'Open Shell',
} }
LIBREOFFICE_XCU_DATA = '''<?xml version="1.0" encoding="UTF-8"?> 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"> <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">
@ -28,20 +29,13 @@ REG_CHROME_UBLOCK_ORIGIN = {
) )
}, },
} }
REG_ESET_NOD32_SETTINGS = { REG_WINDOWS_BSOD_MINIDUMPS = {
'HKCU': {
r'Software\ESET\ESET Security\CurrentVersion\gui\UI_CONFIG': (
('FullScreenMode', 0, 'DWORD'),
('ShowAlertStatus', 0x40000000, 'DWORD'),
('ShowDesktopAlert', 0, 'DWORD'),
('ShowSplash', 0, 'DWORD'),
),
},
'HKLM': { 'HKLM': {
r'Software\ESET\ESET Security\CurrentVersion\Config\Settings\LiveGrid': ( # Enable small memory dumps
('LiveGridFeedbackEnabled', 1, 'DWORD'), r'SYSTEM\CurrentControlSet\Control\CrashControl': (
), ('CrashDumpEnabled', 3, 'DWORD'),
}, )
}
} }
REG_WINDOWS_EXPLORER = { REG_WINDOWS_EXPLORER = {
'HKLM': { 'HKLM': {
@ -64,12 +58,6 @@ REG_WINDOWS_EXPLORER = {
r'Software\Policies\Microsoft\Windows\DataCollection': ( r'Software\Policies\Microsoft\Windows\DataCollection': (
('AllowTelemetry', 0, 'DWORD'), ('AllowTelemetry', 0, 'DWORD'),
), ),
# Disable Timeline
r'Software\Policies\Microsoft\Windows\System': (
('EnableActivityFeed', 0, 'DWORD'),
('PublishUserActivities', 0, 'DWORD'),
('UploadUserActivities', 0, 'DWORD'),
),
# Disable floating Bing search widget # Disable floating Bing search widget
r'Software\Policies\Microsoft\Edge': ( r'Software\Policies\Microsoft\Edge': (
('WebWidgetAllowed', 0, 'DWORD'), ('WebWidgetAllowed', 0, 'DWORD'),
@ -105,7 +93,6 @@ REG_WINDOWS_EXPLORER = {
), ),
# Disable search highlights # Disable search highlights
r'Software\Microsoft\Windows\CurrentVersion\Feeds\DSB': ( r'Software\Microsoft\Windows\CurrentVersion\Feeds\DSB': (
('OpenOnHover', 0, 'DWORD'),
('ShowDynamicContent', 0, 'DWORD'), ('ShowDynamicContent', 0, 'DWORD'),
), ),
# File Explorer # File Explorer
@ -120,7 +107,7 @@ REG_WINDOWS_EXPLORER = {
), ),
# Hide Search button / box # Hide Search button / box
r'Software\Microsoft\Windows\CurrentVersion\Search': ( r'Software\Microsoft\Windows\CurrentVersion\Search': (
('SearchboxTaskbarMode', 2, 'DWORD'), ('SearchboxTaskbarMode', 1, 'DWORD'),
), ),
# Disable search highlights from opening on hover # Disable search highlights from opening on hover
r'Software\Microsoft\Windows\CurrentVersion\SearchSettings': ( r'Software\Microsoft\Windows\CurrentVersion\SearchSettings': (

View file

@ -9,45 +9,46 @@ DOWNLOAD_FREQUENCY = 7
# Sources # Sources
SOURCES = { SOURCES = {
# Main # 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', 'AVRemover64': 'https://download.eset.com/com/eset/tools/installers/av_remover/latest/avremover_nt64_enu.exe',
'AdwCleaner': 'https://adwcleaner.malwarebytes.com/adwcleaner?channel=release', 'AdwCleaner': 'https://downloads.malwarebytes.com/file/adwcleaner',
'Autologon32': 'http://live.sysinternals.com/Autologon.exe',
'Autologon64': 'http://live.sysinternals.com/Autologon64.exe', 'Autologon64': 'http://live.sysinternals.com/Autologon64.exe',
'CoreTemp64': 'https://www.alcpu.com/CoreTemp/CoreTemp64.zip', 'Firefox32': 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win&lang=en-US',
'ESET_NOD32_AV64': 'https://download.eset.com/com/eset/apps/home/eav/windows/latest/eav_nt64.exe',
'EmsisoftCmd64': 'https://dl.emsisoft.com/EmsisoftCommandlineScanner64.exe',
'Firefox64': 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&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', 'HitmanPro64': 'https://dl.surfright.nl/HitmanPro_x64.exe',
'KVRT': 'https://devbuilds.s.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe', 'KVRT': 'https://devbuilds.s.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe',
'MBAM': 'https://downloads.malwarebytes.com/file/mb-windows',
'RKill': 'https://download.bleepingcomputer.com/grinler/rkill.exe', 'RKill': 'https://download.bleepingcomputer.com/grinler/rkill.exe',
'RegDelNull': 'https://live.sysinternals.com/RegDelNull.exe',
'RegDelNull64': 'https://live.sysinternals.com/RegDelNull64.exe', 'RegDelNull64': 'https://live.sysinternals.com/RegDelNull64.exe',
# Build Kit # Build Kit
'AIDA64': 'https://download.aida64.com/aida64engineer720.zip', 'AIDA64': 'https://download.aida64.com/aida64engineer692.zip',
'Adobe Reader DC': 'https://ardownload2.adobe.com/pub/adobe/acrobat/win/AcrobatDC/2400120615/AcroRdrDCx642400120615_en_US.exe', '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', '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', 'Autoruns64': 'http://live.sysinternals.com/Autoruns64.exe',
'BleachBit': 'https://download.bleachbit.org/BleachBit-4.4.2-portable.zip', 'BleachBit': 'https://download.bleachbit.org/BleachBit-4.4.2-portable.zip',
'BCUninstaller': 'https://github.com/Klocman/Bulk-Crap-Uninstaller/releases/download/v5.7/BCUninstaller_5.7_portable.zip', 'BlueScreenView32': 'http://www.nirsoft.net/utils/bluescreenview.zip',
'BlueScreenView64': 'http://www.nirsoft.net/utils/bluescreenview-x64.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', 'DDU': 'https://www.wagnardsoft.com/DDU/download/DDU%20v18.0.6.8.exe',
'ERUNT': 'http://www.aumha.org/downloads/erunt.zip', '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', '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', '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', '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', 'FurMark': 'https://geeks3d.com/dl/get/728',
'LibreOffice64': 'https://download.documentfoundation.org/libreoffice/stable/7.6.6/win/x86_64/LibreOffice_7.6.6_Win_x86-64.msi', 'HWiNFO': 'https://www.sac.sk/download/utildiag/hwi_764.zip',
'Linux Reader': 'https://www.diskinternals.com/download/Linux_Reader.exe', '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', 'Macs Fan Control': 'https://www.crystalidea.com/downloads/macsfancontrol_setup.exe',
'Neutron': 'http://keir.net/download/neutron.zip', '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', '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', 'OpenShell': 'https://github.com/Open-Shell/Open-Shell-Menu/releases/download/v4.4.191/OpenShellSetup_4_4_191.exe',
'Prime95': 'https://www.mersenne.org/download/software/v30/30.19/p95v3019b13.win64.zip',
'PuTTY': 'https://the.earth.li/~sgtatham/putty/latest/w32/putty.zip', 'PuTTY': 'https://the.earth.li/~sgtatham/putty/latest/w32/putty.zip',
'SDIO Torrent': 'https://www.glenn.delahoy.com/downloads/sdio/SDIO_Update.torrent', 'SDIO Torrent': 'https://www.glenn.delahoy.com/downloads/sdio/SDIO_Update.torrent',
'ShutUp10': 'https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe',
'Windows Repair AIO': 'http://www.tweaking.com/files/setups/tweaking.com_windows_repair_aio.zip',
'WinSCP': 'https://sourceforge.net/projects/winscp/files/WinSCP/5.19.5/WinSCP-5.19.5-Portable.zip/download',
'WizTree': 'https://diskanalyzer.com/files/wiztree_4_15_portable.zip', 'WizTree': 'https://diskanalyzer.com/files/wiztree_4_15_portable.zip',
'XMPlay': 'https://support.xmplay.com/files/20/xmplay385.zip?v=47090', '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 7z': 'https://support.xmplay.com/files/16/xmp-7z.zip?v=800962',

View file

@ -8,7 +8,6 @@ from wk.cfg.main import KIT_NAME_FULL
SOURCES = { SOURCES = {
'Linux': {'Arg': '--linux', 'Type': 'ISO'}, 'Linux': {'Arg': '--linux', 'Type': 'ISO'},
'WinPE': {'Arg': '--winpe', 'Type': 'ISO'}, 'WinPE': {'Arg': '--winpe', 'Type': 'ISO'},
'ESET SysRescue': {'Arg': '--eset', 'Type': 'IMG'},
'Main Kit': {'Arg': '--main-kit', 'Type': 'KIT'}, 'Main Kit': {'Arg': '--main-kit', 'Type': 'KIT'},
'Extra Dir': {'Arg': '--extra-dir', 'Type': 'DIR'}, 'Extra Dir': {'Arg': '--extra-dir', 'Type': 'DIR'},
} }
@ -16,44 +15,23 @@ SOURCES = {
# Definitions: Boot entries # Definitions: Boot entries
BOOT_ENTRIES = { BOOT_ENTRIES = {
# Path to check: Comment to remove # Path to check: Comment to remove
'/arch_minimal': 'UFD-MINIMAL',
'/casper': 'UFD-ESET',
'/sources/boot.wim': 'UFD-WINPE', '/sources/boot.wim': 'UFD-WINPE',
} }
BOOT_FILES = { BOOT_FILES = {
# Directory: extension # Directory: extension
'/syslinux': 'cfg', '/syslinux': 'cfg',
'/boot/grub': 'cfg',
'/EFI/boot': 'conf', '/EFI/boot': 'conf',
} }
IMAGE_BOOT_ENTRIES = { IMAGE_BOOT_ENTRIES = {
'El Capitan': 'UFD-MACOS-10.11', 'El Capitan': 'UFD-MACOS-10.11',
'High Sierra': 'UFD-MACOS-10.13', 'High Sierra': 'UFD-MACOS-10.13',
'Catalina': 'UFD-MACOS-10.15', 'Catalina': 'UFD-MACOS-10.15',
'3S132': 'UFD-ASD-3S132',
'3S138': 'UFD-ASD-3S138',
'3S140': 'UFD-ASD-3S140',
'3S142A': 'UFD-ASD-3S142A',
'3S144': 'UFD-ASD-3S144',
'3S145A': 'UFD-ASD-3S145A',
'3S146': 'UFD-ASD-3S146',
'3S147': 'UFD-ASD-3S147',
'3S148': 'UFD-ASD-3S148',
'3S150': 'UFD-ASD-3S150',
'3S155': 'UFD-ASD-3S155',
'3S156': 'UFD-ASD-3S156',
'3S162': 'UFD-ASD-3S162',
} }
# Definitions: Sources and Destinations # Definitions: Sources and Destinations
## NOTES: Paths are relative to the root of the ISO/UFD ## NOTES: Paths are relative to the root of the ISO/UFD
## Sources use rsync's trailing slash syntax ## Sources use rsync's trailing slash syntax
ITEMS = { ITEMS = {
'ESET SysRescue': (
('/boot/grub/', '/boot/grub/'),
('/casper/', '/casper/'),
('/EFI/boot/', '/EFI/ESET/'),
),
'Extra Dir': ( 'Extra Dir': (
('/', '/'), ('/', '/'),
), ),
@ -64,14 +42,15 @@ ITEMS = {
('/', f'/{KIT_NAME_FULL}/'), ('/', f'/{KIT_NAME_FULL}/'),
), ),
'WinPE': ( 'WinPE': (
('/BOOTMGR', '/'), ('/bootmgr', '/'),
('/bootmgr.efi', '/'), ('/bootmgr.efi', '/'),
('/boot/', '/boot/'), ('/en_us', '/'),
('/efi/boot/', '/EFI/Microsoft/'), ('/Boot/', '/boot/'),
('/efi/microsoft/', '/EFI/Microsoft/'), ('/EFI/Boot/', '/EFI/Microsoft/'),
('/boot/bcd', '/sources/'), ('/EFI/Microsoft/', '/EFI/Microsoft/'),
('/boot/boot.sdi', '/sources/'), ('/Boot/BCD', '/sources/'),
('/BOOTMGR', '/sources/'), ('/Boot/boot.sdi', '/sources/'),
('/bootmgr', '/sources/'),
('/sources/boot.wim', '/sources/'), ('/sources/boot.wim', '/sources/'),
), ),
} }
@ -95,8 +74,6 @@ ITEMS_FROM_LIVE = {
), ),
} }
ITEMS_HIDDEN = ( ITEMS_HIDDEN = (
# ESET
'casper',
# Linux (all versions) # Linux (all versions)
'arch', 'arch',
'EFI', 'EFI',
@ -106,8 +83,9 @@ ITEMS_HIDDEN = (
f'{KIT_NAME_FULL}/.cbin', f'{KIT_NAME_FULL}/.cbin',
# WinPE # WinPE
'boot', 'boot',
'BOOTMGR', 'bootmgr',
'bootmgr.efi', 'bootmgr.efi',
'en-us',
'images', 'images',
'sources', 'sources',
) )

View file

@ -39,4 +39,5 @@ WINDOWS_BUILDS = {
'10.0.22000': '21H2', '10.0.22000': '21H2',
'10.0.22621': '22H2', '10.0.22621': '22H2',
'10.0.22631': '23H2', '10.0.22631': '23H2',
'10.0.26100': '24H2',
} }

View file

@ -41,7 +41,6 @@ class BlockPair():
self.map_data: dict[str, bool | int] = {} self.map_data: dict[str, bool | int] = {}
self.map_path: pathlib.Path = pathlib.Path() self.map_path: pathlib.Path = pathlib.Path()
self.size: int = source_dev.size self.size: int = source_dev.size
self.stats = {}
self.status: dict[str, float | int | str] = { self.status: dict[str, float | int | str] = {
'read-skip': 'Pending', 'read-skip': 'Pending',
'read-full': 'Pending', 'read-full': 'Pending',
@ -191,25 +190,6 @@ class BlockPair():
# This should never be reached # This should never be reached
return False return False
def reset_progress(self):
"""Reset progress to start fresh recovery."""
self.map_data = {}
self.status = {
'read': 'Pending',
'trim': 'Pending',
'scrape': 'Pending',
}
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 safety_check(self) -> None: def safety_check(self) -> None:
"""Run safety check and abort if necessary.""" """Run safety check and abort if necessary."""
# TODO: Expand section to support non-Linux systems # TODO: Expand section to support non-Linux systems
@ -269,7 +249,6 @@ def add_clone_block_pairs(state) -> list[hw_disk.Disk]:
source_sep = get_partition_separator(state.source.path.name) source_sep = get_partition_separator(state.source.path.name)
dest_sep = get_partition_separator(state.destination.path.name) dest_sep = get_partition_separator(state.destination.path.name)
settings = {} settings = {}
source_parts = []
# Clone settings # Clone settings
settings = state.load_settings(discard_unused_settings=True) settings = state.load_settings(discard_unused_settings=True)

View file

@ -1,6 +1,7 @@
"""WizardKit: ddrescue TUI""" """WizardKit: ddrescue TUI"""
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import argparse
import atexit import atexit
import datetime import datetime
import logging import logging
@ -8,13 +9,10 @@ import os
import pathlib import pathlib
import subprocess import subprocess
from typing import Any
from random import randint from random import randint
import pytz import pytz
from docopt import docopt
from wk import cfg, exe, io, log, std from wk import cfg, exe, io, log, std
from wk.cfg.ddrescue import DDRESCUE_SPECIFIC_PASS_SETTINGS from wk.cfg.ddrescue import DDRESCUE_SPECIFIC_PASS_SETTINGS
from wk.clone import menus from wk.clone import menus
@ -25,24 +23,11 @@ from wk.hw.smart import (
smart_status_ok, smart_status_ok,
update_smart_details, update_smart_details,
) )
from wk.ui import ansi, cli, tmux from wk.ui import ansi, cli
# STATIC VARIABLES # STATIC VARIABLES
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
DOCSTRING = f'''{cfg.main.KIT_NAME_FULL}: ddrescue TUI
Usage:
ddrescue-tui
ddrescue-tui [options] (clone|image) [<source> [<destination>]]
ddrescue-tui (-h | --help)
Options:
-h --help Show this page
-s --dry-run Print commands to be used instead of running them
--force-local-map Skip mounting shares and save map to local drive
--start-fresh Ignore previous runs and start new recovery
'''
DETECT_DRIVES_NOTICE = ''' DETECT_DRIVES_NOTICE = '''
This option will force the drive controllers to rescan for devices. 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 The method used is not 100% reliable and may cause issues. If you see
@ -56,6 +41,42 @@ TIMEZONE = pytz.timezone(cfg.main.LINUX_TIME_ZONE)
# Functions # 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]: def build_ddrescue_cmd(block_pair, pass_name, settings_menu) -> list[str]:
"""Build ddrescue cmd using passed details, returns list.""" """Build ddrescue cmd using passed details, returns list."""
cmd = ['sudo', 'ddrescue'] cmd = ['sudo', 'ddrescue']
@ -130,25 +151,13 @@ def check_destination_health(destination) -> str:
result = 'Critical error(s) detected for: {destination.path}' result = 'Critical error(s) detected for: {destination.path}'
# Check for minor errors # Check for minor errors
if not check_attributes(destination, only_blocking=True): if not check_attributes(destination, only_blocking=False):
result = f'Attribute error(s) detected for: {destination.path}' result = f'Attribute error(s) detected for: {destination.path}'
# Done # Done
return result return result
def detect_drives(state):
"""Detect connected drives and check source/dest selections."""
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()
def generate_test_map(map_path: pathlib.Path, size: int) -> None: def generate_test_map(map_path: pathlib.Path, size: int) -> None:
"""Generate test map with roughly 20% of the space marked as bad.""" """Generate test map with roughly 20% of the space marked as bad."""
chunk = 2*1024**2 chunk = 2*1024**2
@ -189,28 +198,9 @@ def get_ddrescue_settings(settings_menu) -> list:
return settings return settings
def get_stats(output: str | None = None) -> dict[str, Any]:
"""Get stats from ddrescue output, returns dict."""
output = tmux.capture_pane()
stats = {}
temp = []
for line in output[output.find('ipos:'):].splitlines():
temp.extend(line.split(','))
for line in temp:
line = line.strip()
try:
key, value = line.split(':')
stats[key] = value.strip()
except ValueError:
# ignore
pass
return stats
def finalize_recovery(state: State, dry_run: bool = True) -> None: def finalize_recovery(state: State, dry_run: bool = True) -> None:
"""Show recovery finalization options and run selected functions.""" """Show recovery finalization options and run selected functions."""
options = ( options = (
'Post results to osTicket',
'Relocate Backup GPT', 'Relocate Backup GPT',
'Zero-fill Gaps', 'Zero-fill Gaps',
'Zero-fill Extra Space', 'Zero-fill Extra Space',
@ -253,9 +243,6 @@ def finalize_recovery(state: State, dry_run: bool = True) -> None:
if state.mode == 'Image': if state.mode == 'Image':
details['Disabled'] = True details['Disabled'] = True
details['Selected'] = False details['Selected'] = False
if 'osTicket' in name:
# This is last to override the Image section above
details['Selected'] = not state.ost.disabled
menu.add_option(name, details) menu.add_option(name, details)
# Show menu # Show menu
@ -275,10 +262,6 @@ def finalize_recovery(state: State, dry_run: bool = True) -> None:
# NOTE: This needs to be run last to avoid corrupting/erasing the backup GPT # NOTE: This needs to be run last to avoid corrupting/erasing the backup GPT
relocate_backup_gpt(state, dry_run=dry_run) relocate_backup_gpt(state, dry_run=dry_run)
# osTicket
if menu.options['Post results to osTicket']['Selected']:
state.post_to_osticket()
def is_missing_source_or_destination(state) -> bool: def is_missing_source_or_destination(state) -> bool:
"""Check if source or destination dissapeared, returns bool.""" """Check if source or destination dissapeared, returns bool."""
@ -312,7 +295,12 @@ def is_missing_source_or_destination(state) -> bool:
def main() -> None: def main() -> None:
"""Main function for ddrescue TUI.""" """Main function for ddrescue TUI."""
args = docopt(DOCSTRING) try:
args = argparse_helper()
except SystemExit:
print('')
cli.pause('Press Enter to exit...')
raise
# Log setup # Log setup
log_path = log.format_log_path(log_name='main', sub_dir='ddrescue-TUI') log_path = log.format_log_path(log_name='main', sub_dir='ddrescue-TUI')
@ -331,9 +319,6 @@ def main() -> None:
# Init # Init
state = State(log_dir=log_path.parent) state = State(log_dir=log_path.parent)
if not args['--force-local-map']:
state.ost.select_ticket()
state.update_top_panes()
try: try:
state.init_recovery(args) state.init_recovery(args)
except (FileNotFoundError, std.GenericAbort): except (FileNotFoundError, std.GenericAbort):
@ -341,7 +326,7 @@ def main() -> None:
cli.abort() cli.abort()
# Show menu # Show menu
main_menu = menus.main(ost_disabled=state.ost.disabled) main_menu = menus.main()
settings_menu = menus.settings(state.mode) settings_menu = menus.settings(state.mode)
while True: while True:
selection = main_menu.advanced_select() selection = main_menu.advanced_select()
@ -358,18 +343,14 @@ def main() -> None:
# Detect drives # Detect drives
if 'Detect drives' in selection[0]: if 'Detect drives' in selection[0]:
detect_drives(state) cli.clear_screen()
cli.print_warning(DETECT_DRIVES_NOTICE)
# Tech note if cli.ask('Are you sure you proceed?'):
if 'tech note' in selection[0]: cli.print_standard('Forcing controllers to rescan for devices...')
note_lines = state.ost.add_note( cmd = 'echo "- - -" | sudo tee /sys/class/scsi_host/host*/scan'
'Please enter any additional information about this recovery', exe.run_program([cmd], check=False, shell=True)
) if source_or_destination_changed(state):
state.update_top_panes(note_lines=note_lines) cli.abort()
# Start over
if 'Fresh start' in selection[0]:
state.fresh_start()
# Start recovery # Start recovery
if 'Start' in selection: if 'Start' in selection:
@ -425,9 +406,6 @@ def relocate_backup_gpt(state: State, dry_run: bool = True) -> None:
if proc.returncode: if proc.returncode:
cli.print_error('ERROR: Failed to relocate backup GPT.') cli.print_error('ERROR: Failed to relocate backup GPT.')
LOG.error('sfdisk result: %s, %s', proc.stdout, proc.stderr) LOG.error('sfdisk result: %s, %s', proc.stdout, proc.stderr)
state.notes.add('WARNING: Failed to relocated backup GPT')
else:
state.notes.add('NOTE: Relocated backup GPT to the end of the disk.')
def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None: def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None:
@ -497,10 +475,6 @@ def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None:
LOG.info('ddrescue cmd: %s', cmd) LOG.info('ddrescue cmd: %s', cmd)
return return
# Stats
if pass_name not in block_pair.stats:
block_pair.stats[pass_name] = {}
# Start ddrescue and ddrescueview (if enabled) # Start ddrescue and ddrescueview (if enabled)
proc = exe.popen_program(cmd) proc = exe.popen_program(cmd)
if ( if (
@ -529,7 +503,6 @@ def run_ddrescue(state, block_pair, pass_name, settings, dry_run=True) -> None:
_i += 1 _i += 1
# Update progress # Update progress
block_pair.stats[pass_name].update(get_stats())
block_pair.update_progress(pass_name) block_pair.update_progress(pass_name)
state.update_progress_pane('Active') state.update_progress_pane('Active')
@ -763,17 +736,9 @@ def zero_fill_gaps(
# Re-run ddrescue to zero-fill gaps # Re-run ddrescue to zero-fill gaps
proc = exe.run_program(cmd, check=False, pipe=False) proc = exe.run_program(cmd, check=False, pipe=False)
LOG.info('Zero-fill result: %s', proc)
if proc.returncode: if proc.returncode:
cli.print_error('ERROR: Failed to zero-fill: {block_pair.destination}') cli.print_error('ERROR: Failed to zero-fill: {block_pair.destination}')
LOG.error('zero-fill error: %s, %s', proc.stdout, proc.stderr) LOG.error('zero-fill error: %s, %s', proc.stdout, proc.stderr)
state.notes.add('WARNING: Failed to zero-fill destination')
else:
msg = (
'NOTE: Zero-filled gaps'
f'{" and extra space on destination." if extend_to_end else "."}'
)
state.notes.add(msg)
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -29,10 +29,8 @@ if PLATFORM == 'Darwin':
DDRESCUE_SETTINGS['Default']['--odirect'] = {'Selected': False, 'Hidden': True} DDRESCUE_SETTINGS['Default']['--odirect'] = {'Selected': False, 'Hidden': True}
MENU_ACTIONS = ( MENU_ACTIONS = (
'Start', 'Start',
'Add tech note',
f'Change settings {ansi.color_string("(experts only)", "YELLOW")}', f'Change settings {ansi.color_string("(experts only)", "YELLOW")}',
f'Detect drives {ansi.color_string("(experts only)", "YELLOW")}', f'Detect drives {ansi.color_string("(experts only)", "YELLOW")}',
f'Fresh start {ansi.color_string("(experts only)", "YELLOW")}',
'Quit') 'Quit')
MENU_TOGGLES = { MENU_TOGGLES = {
'Auto continue (if recovery % over threshold)': True, 'Auto continue (if recovery % over threshold)': True,
@ -46,7 +44,7 @@ SETTING_PRESETS = (
# Functions # Functions
def main(ost_disabled: bool) -> cli.Menu: def main() -> cli.Menu:
"""Main menu, returns wk.ui.cli.Menu.""" """Main menu, returns wk.ui.cli.Menu."""
menu = cli.Menu(title=ansi.color_string('ddrescue TUI: Main Menu', 'GREEN')) menu = cli.Menu(title=ansi.color_string('ddrescue TUI: Main Menu', 'GREEN'))
menu.separator = ' ' menu.separator = ' '
@ -58,15 +56,6 @@ def main(ost_disabled: bool) -> cli.Menu:
for toggle, selected in MENU_TOGGLES.items(): for toggle, selected in MENU_TOGGLES.items():
menu.add_toggle(toggle, {'Selected': selected}) menu.add_toggle(toggle, {'Selected': selected})
# osTicket actions
if ost_disabled:
menu.actions['Add tech note']['Disabled'] = True
menu.actions['Add tech note']['Hidden'] = True
# TODO: Remove this ugly call
menu.actions[MENU_ACTIONS[2]]['Separator'] = True
else:
menu.actions['Add tech note']['Separator'] = True
# Done # Done
return menu return menu

View file

@ -15,7 +15,7 @@ from typing import Any
import psutil import psutil
import pytz import pytz
from wk import cfg, debug, exe, io, net, osticket, std from wk import cfg, debug, exe, io, net, std
from wk.clone import menus from wk.clone import menus
from wk.clone.block_pair import ( from wk.clone.block_pair import (
BlockPair, BlockPair,
@ -81,10 +81,8 @@ class State():
self.block_pairs: list[BlockPair] = [] self.block_pairs: list[BlockPair] = []
self.destination: hw_disk.Disk | pathlib.Path = pathlib.Path('/dev/null') self.destination: hw_disk.Disk | pathlib.Path = pathlib.Path('/dev/null')
self.log_dir: pathlib.Path = log_dir self.log_dir: pathlib.Path = log_dir
self.ost = osticket.osTicket()
self.progress_out: pathlib.Path = self.log_dir.joinpath('progress.out') self.progress_out: pathlib.Path = self.log_dir.joinpath('progress.out')
self.mode: str = '?' self.mode: str = '?'
self.notes: set = set()
self.source: hw_disk.Disk | None = None self.source: hw_disk.Disk | None = None
self.working_dir: pathlib.Path | None = None self.working_dir: pathlib.Path | None = None
self.ui: tui.TUI = tui.TUI('Source') self.ui: tui.TUI = tui.TUI('Source')
@ -147,7 +145,7 @@ class State():
errors_detected = True errors_detected = True
# Check for minor errors # Check for minor errors
if not check_attributes(self.destination, only_blocking=True): if not check_attributes(self.destination, only_blocking=False):
cli.print_warning( cli.print_warning(
f'Attribute error(s) detected for: {self.destination.path}', f'Attribute error(s) detected for: {self.destination.path}',
) )
@ -252,45 +250,18 @@ class State():
if not cli.ask(prompt_msg): if not cli.ask(prompt_msg):
raise std.GenericAbort() raise std.GenericAbort()
def fresh_start(self) -> None:
"""Clean working dir and reset progress."""
cli.print_error(
'This will reset all progress and create new map file(s).',
)
cli.print_warning(
"Please only proceed if you understand what you're doing!",
)
cli.print_warning(
'NOTE: This will keep the current partition selection(s).',
)
if not cli.ask('Continue?'):
return
# Clean and reset
clean_working_dir(self.working_dir)
for block_pair in self.block_pairs:
block_pair.reset_progress()
self.update_progress_pane('Idle')
def generate_report(self) -> list[str]: def generate_report(self) -> list[str]:
"""Generate report of overall and per block_pair results, returns list.""" """Generate report of overall and per block_pair results, returns list."""
report = [] report = []
stats_str = (
'... pass: {pass_name} -- '
'non-trimmed: {non-trimmed}, '
'non-scraped: {non-scraped}, '
'bad-sectors: {bad-sector}, '
'slow reads: {slow reads}, '
'run time: {run time}'
)
# Header # Header
report.append(f'{self.mode.title()} Results:') report.append(f'{self.mode.title()} Results:')
report.append(f'... Source: {self.source.description}') report.append(' ')
report.append(f'Source: {self.source.description}')
if self.mode == 'Clone': if self.mode == 'Clone':
report.append(f'... Destination: {self.destination.description}') report.append(f'Destination: {self.destination.description}')
else: else:
report.append(f'... Destination: {self.destination}/') report.append(f'Destination: {self.destination}/')
# Overall # Overall
report.append(' ') report.append(' ')
@ -303,17 +274,8 @@ class State():
report.append(f'Overall rescued: {percent}, error size: {error_size_str}') report.append(f'Overall rescued: {percent}, error size: {error_size_str}')
# Block-Pairs # Block-Pairs
if len(self.block_pairs) == 1: if len(self.block_pairs) > 1:
stats = self.block_pairs[0].stats report.append(' ')
if stats:
try:
for _name, _stats in stats.items():
report.append(stats_str.format(pass_name=_name, **_stats))
except KeyError:
# Ignore and omit stats
pass
else:
# Two or more block_pairs
for pair in self.block_pairs: for pair in self.block_pairs:
error_size = pair.get_error_size() error_size = pair.get_error_size()
error_size_str = std.bytes_to_string(error_size, decimals=2) error_size_str = std.bytes_to_string(error_size, decimals=2)
@ -322,26 +284,11 @@ class State():
pair_size = std.bytes_to_string(pair.size, decimals=2) pair_size = std.bytes_to_string(pair.size, decimals=2)
percent = pair.get_percent_recovered() percent = pair.get_percent_recovered()
percent = format_status_string(percent, width=0) percent = format_status_string(percent, width=0)
report.append(' ')
report.append( report.append(
f'{pair.source.name} ({pair_size}) ' f'{pair.source.name} ({pair_size}) '
f'rescued: {percent}, ' f'rescued: {percent}, '
f'error size: {error_size_str}' f'error size: {error_size_str}'
) )
stats = pair.stats
if not stats:
continue
try:
for _name, _stats in stats.items():
report.append(stats_str.format(pass_name=_name, **_stats))
except KeyError:
# Ignore and omit stats
pass
# Notes
if self.notes:
report.append(' ')
report.extend(sorted(self.notes))
# Done # Done
return report return report
@ -369,7 +316,7 @@ class State():
"""Get total size of all block_pairs in bytes, returns int.""" """Get total size of all block_pairs in bytes, returns int."""
return sum(pair.size for pair in self.block_pairs) return sum(pair.size for pair in self.block_pairs)
def init_recovery(self, docopt_args: dict[str, Any]) -> None: def init_recovery(self, cli_args: dict[str, Any]) -> None:
"""Select source/dest and set env.""" """Select source/dest and set env."""
cli.clear_screen() cli.clear_screen()
disk_menu = menus.disks() disk_menu = menus.disks()
@ -377,10 +324,10 @@ class State():
self.ui.set_progress_file(str(self.progress_out)) self.ui.set_progress_file(str(self.progress_out))
# Set mode # Set mode
self.mode = set_mode(docopt_args) self.mode = set_mode(cli_args)
# Select source # Select source
self.source = select_disk_obj('source', disk_menu, docopt_args['<source>']) self.source = select_disk_obj('source', disk_menu, cli_args['<source>'])
self.update_top_panes() self.update_top_panes()
if self.source.trim: if self.source.trim:
cli.print_warning('Source device supports TRIM') cli.print_warning('Source device supports TRIM')
@ -393,12 +340,12 @@ class State():
self.destination = select_disk_obj( self.destination = select_disk_obj(
'destination', 'destination',
disk_menu, disk_menu,
docopt_args['<destination>'], cli_args['<destination>'],
) )
self.ui.add_title_pane('Destination', self.destination.name) self.ui.add_title_pane('Destination', self.destination.name)
elif self.mode == 'Image': elif self.mode == 'Image':
if docopt_args['<destination>']: if cli_args['<destination>']:
self.destination = pathlib.Path(docopt_args['<destination>']).resolve() self.destination = pathlib.Path(cli_args['<destination>']).resolve()
else: else:
self.destination = menus.select_path('Destination') self.destination = menus.select_path('Destination')
self.destination.mkdir(parents=True, exist_ok=True) self.destination.mkdir(parents=True, exist_ok=True)
@ -411,17 +358,10 @@ class State():
self.destination.update_details(skip_children=False) self.destination.update_details(skip_children=False)
# Confirmation #1 # Confirmation #1
advanced_selection = False self.confirm_selections(
try: prompt_msg='Are these selections correct?',
self.confirm_selections( source_parts=source_parts,
prompt_msg='Are these selections correct? (use "No" for advanced selection)', )
source_parts=source_parts,
)
except std.GenericAbort:
if cli.ask('Proceed to advanced partition selection?'):
advanced_selection = True
else:
raise
# Update panes # Update panes
self.update_progress_pane('Idle') self.update_progress_pane('Idle')
@ -430,34 +370,18 @@ class State():
self.working_dir = get_working_dir( self.working_dir = get_working_dir(
self.mode, self.mode,
self.destination, self.destination,
force_local=docopt_args['--force-local-map'], force_local=cli_args['--force-local-map'],
ticket_id=self.ost.ticket_id,
ticket_name=self.ost.ticket_name,
) )
# Start fresh if requested # Start fresh if requested
if docopt_args['--start-fresh']: if cli_args['--start-fresh']:
clean_working_dir(self.working_dir) clean_working_dir(self.working_dir)
# Add block pairs # Add block pairs
if advanced_selection: if self.mode == 'Clone':
if self.mode == 'Clone': source_parts = add_clone_block_pairs(self)
source_parts = add_clone_block_pairs(self)
else:
source_parts = add_image_block_pairs(self)
else: else:
if self.mode == 'Clone': source_parts = add_image_block_pairs(self)
source_parts.append(hw_disk.Disk(self.source.path))
self.add_block_pair(
hw_disk.Disk(self.source.path),
pathlib.Path(self.destination.path),
)
if self.mode == 'Image':
source_parts.append(hw_disk.Disk(self.source.path))
self.add_block_pair(
hw_disk.Disk(self.source.path),
self.destination,
)
# Update SMART data # Update SMART data
## TODO: Verify if needed ## TODO: Verify if needed
@ -472,8 +396,7 @@ class State():
# Confirmation #2 # Confirmation #2
self.update_progress_pane('Idle') self.update_progress_pane('Idle')
if advanced_selection: self.confirm_selections('Start recovery?', source_parts)
self.confirm_selections('Start recovery?', source_parts)
# Unmount source and/or destination under macOS # Unmount source and/or destination under macOS
if PLATFORM == 'Darwin': if PLATFORM == 'Darwin':
@ -489,10 +412,10 @@ class State():
# Prep destination # Prep destination
if self.mode == 'Clone': if self.mode == 'Clone':
prep_destination(self, source_parts, dry_run=docopt_args['--dry-run']) prep_destination(self, source_parts, dry_run=cli_args['--dry-run'])
# Safety Checks #2 # Safety Checks #2
if not docopt_args['--dry-run']: if not cli_args['--dry-run']:
for pair in self.block_pairs: for pair in self.block_pairs:
pair.safety_check() pair.safety_check()
@ -573,29 +496,6 @@ class State():
"""Check if all block_pairs completed pass_name, returns bool.""" """Check if all block_pairs completed pass_name, returns bool."""
return all(p.pass_complete(pass_name) for p in self.block_pairs) return all(p.pass_complete(pass_name) for p in self.block_pairs)
def post_to_osticket(self):
"""Post results to osTicket."""
color = 'Diags'
percent = self.get_percent_recovered()
report = self.generate_report()
report[0] = f'ddrescue-tui {report[0]}'
if percent < 90:
color = 'Diags FAIL'
elif percent < 95:
color = 'Normal'
# Init osTicket if necessary
if not self.ost.ticket_id:
self.ost.init()
self.ost.select_ticket()
# Bail if user changed their mind
if not self.ost.ticket_id:
return
# Post report
self.ost.post_response('\n'.join(report), color=color)
def retry_all_passes(self) -> None: def retry_all_passes(self) -> None:
"""Prep block_pairs for a retry recovery attempt.""" """Prep block_pairs for a retry recovery attempt."""
LOG.warning('Updating block_pairs for retry') LOG.warning('Updating block_pairs for retry')
@ -712,10 +612,8 @@ class State():
# Write to progress file # Write to progress file
self.progress_out.write_text('\n'.join(report), encoding='utf-8', errors='ignore') self.progress_out.write_text('\n'.join(report), encoding='utf-8', errors='ignore')
def update_top_panes(self, note_lines: list | None = None) -> None: def update_top_panes(self) -> None:
"""(Re)create top source/destination panes.""" """(Re)create top source/destination panes."""
if not note_lines:
note_lines = []
source_exists = True source_exists = True
source_str = '' source_str = ''
dest_exists = True dest_exists = True
@ -783,29 +681,6 @@ class State():
dest_str, dest_str,
) )
# Bail if ticket not selected
if not self.ost:
return
# Ticket Details
self.ui.reset_subtitle_pane()
if self.ost.ticket_id and not self.ui.layout['Subtitle']['Panes']:
self.ui.add_subtitle_pane(
ansi.color_string(
[f'#{self.ost.ticket_id}', str(self.ost.ticket_name)],
[None, 'CYAN'],
),
str(self.ost.ticket_subject),
)
# Tech note
note_lines = self.ost.note.replace('...', '').splitlines()
if note_lines:
self.ui.add_subtitle_pane(
ansi.color_string('Tech Note', 'YELLOW'),
' | '.join(note_lines),
)
# Functions # Functions
def build_directory_report(path: pathlib.Path) -> list[str]: def build_directory_report(path: pathlib.Path) -> list[str]:
@ -926,7 +801,7 @@ def build_object_report(obj) -> list[str]:
return report return report
def clean_working_dir(working_dir, confirm=False) -> None: def clean_working_dir(working_dir) -> None:
"""Clean working directory to ensure a fresh recovery session. """Clean working directory to ensure a fresh recovery session.
NOTE: Data from previous sessions will be preserved NOTE: Data from previous sessions will be preserved
@ -939,9 +814,6 @@ def clean_working_dir(working_dir, confirm=False) -> None:
# Move settings, maps, etc to backup_dir # Move settings, maps, etc to backup_dir
for entry in os.scandir(working_dir): for entry in os.scandir(working_dir):
if entry.name.endswith(('.dd', '.json', '.map')): if entry.name.endswith(('.dd', '.json', '.map')):
if confirm and entry.name.endswith('.json'):
# Keep JSON settings if using the menu option
continue
new_path = f'{backup_dir}/{entry.name}' new_path = f'{backup_dir}/{entry.name}'
new_path = io.non_clobber_path(new_path) new_path = io.non_clobber_path(new_path)
shutil.move(entry.path, new_path) shutil.move(entry.path, new_path)
@ -1081,22 +953,11 @@ def get_percent_color(percent) -> str:
return color return color
def get_working_dir( def get_working_dir(mode, destination, force_local=False) -> pathlib.Path:
mode,
destination,
force_local=False,
ticket_id=None,
ticket_name=None,
) -> pathlib.Path:
"""Get working directory using mode and destination, returns path.""" """Get working directory using mode and destination, returns path."""
ticket_id = cli.get_ticket_id()
working_dir = None working_dir = None
# Set ticket ID
if ticket_id is None:
now = datetime.datetime.now(tz=TIMEZONE)
ticket_id = now.strftime('%Y-%m-%dT%H%M_%Z')
ticket_id = ticket_id.replace(' ', '_')
# Use preferred path if possible # Use preferred path if possible
if mode == 'Image': if mode == 'Image':
try: try:
@ -1127,12 +988,6 @@ def get_working_dir(
if mode == 'Clone': if mode == 'Clone':
working_dir = working_dir.joinpath(ticket_id) working_dir = working_dir.joinpath(ticket_id)
# Append ticket name if set
if ticket_name:
working_dir = working_dir.with_name(
f'{ticket_id}_{ticket_name.replace(" ", "-")}',
)
# Create directory # Create directory
working_dir.mkdir(parents=True, exist_ok=True) working_dir.mkdir(parents=True, exist_ok=True)
os.chdir(working_dir) os.chdir(working_dir)
@ -1196,12 +1051,12 @@ def select_disk_obj(label:str, disk_menu: cli.Menu, disk_path: str) -> hw_disk.D
raise std.GenericAbort() raise std.GenericAbort()
def set_mode(docopt_args) -> str: def set_mode(cli_args) -> str:
"""Set mode from docopt_args or user selection, returns str.""" """Set mode from cli_args or user selection, returns str."""
if docopt_args['clone']: if cli_args['clone']:
return 'Clone' return 'Clone'
if docopt_args['image']: if cli_args['image']:
return 'Image' return 'Image'
# Ask user if necessary # Ask user if necessary

View file

@ -81,7 +81,6 @@ def build_cmd_kwargs(
cmd: list[str], cmd: list[str],
minimized: bool = False, minimized: bool = False,
pipe: bool = True, pipe: bool = True,
priority: bool = False,
shell: bool = False, shell: bool = False,
**kwargs) -> dict[str, Any]: **kwargs) -> dict[str, Any]:
"""Build kwargs for use by subprocess functions, returns dict. """Build kwargs for use by subprocess functions, returns dict.
@ -120,9 +119,6 @@ def build_cmd_kwargs(
startupinfo.wShowWindow = 6 startupinfo.wShowWindow = 6
cmd_kwargs['startupinfo'] = startupinfo cmd_kwargs['startupinfo'] = startupinfo
# High priority
if priority:
cmd_kwargs['creationflags'] = subprocess.HIGH_PRIORITY_CLASS
# Pipe output # Pipe output
if pipe: if pipe:
@ -272,18 +268,6 @@ def run_program(
return proc return proc
def set_proc_priority(name, priority, exact=True):
"""Set process priority by name.
NOTE: priority currently can be only set to NORMAL or HIGH.
"""
if priority not in ('NORMAL', 'HIGH'):
raise RuntimeError('Invalid process priority specified.')
for proc in get_procs(name, exact=exact):
proc.nice(psutil.HIGH_PRIORITY_CLASS)
def start_thread( def start_thread(
function: Callable, function: Callable,
args: Iterable[Any] | None = None, args: Iterable[Any] | None = None,

View file

@ -1,25 +1,13 @@
"""WizardKit: Graph Functions""" """WizardKit: Graph Functions"""
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import base64
import json
import logging import logging
import pathlib
import time
from matplotlib import pyplot
import requests
from wk.cfg.net import BENCHMARK_SERVER, IMGUR_CLIENT_ID
from wk.ui import ansi from wk.ui import ansi
# STATIC VARIABLES # STATIC VARIABLES
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
GRAPH_FIGURE_SIZE = (
9.167, # 660px at 72dpi
4.167, # 300px at 72dpi
)
GRAPH_HORIZONTAL = ('', '', '', '', '', '', '', '') GRAPH_HORIZONTAL = ('', '', '', '', '', '', '', '')
GRAPH_VERTICAL = ( GRAPH_VERTICAL = (
'', '', '', '', '', '', '', '',
@ -45,137 +33,6 @@ THRESH_GREAT = 750 * 1024**2
# Functions # Functions
def reduce_cpu_temp_list(list_of_temps, factor=5, start=30) -> list[float]:
"""Reduce temperature list by averaging adjacent values.
NOTE: This only averages values after the amount defined by start.
NOTE 2: If the last group is less than the factor it is simply dropped.
"""
new_list = list_of_temps[:start]
list_of_temps = list_of_temps[start:]
while list_of_temps:
group = list_of_temps[:factor]
list_of_temps = list_of_temps[factor:]
if len(group) < factor:
continue
new_list.append(sum(group)/factor)
return new_list
def export_cpu_graph(cpu_description, log_dir, sensor_history):
"""Exports PNG graph using matplotlib."""
lines = {}
offset_labels = []
out_path = pathlib.Path(f'{log_dir}/cpu_tests.png')
run_averages = {}
# Safety check
if not sensor_history:
raise RuntimeError('No sensor_data available.')
# Prep
offset = 0
for run_label, sensor_data in sensor_history:
all_run_temps = []
offset_labels.append((offset, run_label))
run_length = 0
for adapter in sensor_data.get('CPUTemps', {}).values():
for source, data in adapter.items():
y_values = data['Temps']
if run_label in ('Sysbench', 'Prime95'):
y_values = reduce_cpu_temp_list(y_values)
all_run_temps.extend(y_values)
run_length = max(run_length, len(y_values))
if source not in lines:
lines[source] = []
lines[source].extend(y_values)
try:
run_averages[run_label] = {
'Start': offset,
'End': offset+run_length,
'Temp': sum(all_run_temps) / len(all_run_temps),
}
except ZeroDivisionError:
# Ignore
pass
offset += run_length
# Build graph
_, ax = pyplot.subplots(
dpi=72,
figsize=list(x*2 for x in GRAPH_FIGURE_SIZE),
layout='constrained',
)
ax.set_title(cpu_description)
ax.set_xticks([])
for label, data in lines.items():
ax.plot(data, label=label)
#prev_label = 'Idle' # Always skip Idle
prev_label = ''
for offset, label in offset_labels:
color = 'grey'
if label == prev_label:
continue
if label == 'Sysbench':
color = 'orange'
if label == 'Prime95':
color = 'red'
if label == 'Cooldown':
color = 'blue'
label = ''
ax.axvline(x=offset, color=color, label=label, linestyle='--')
#ax.axvline(x=offset, color=color)
prev_label = label
for run_label, data in run_averages.items():
if run_label not in ('Prime95', 'Sysbench'):
continue
ax.axhline(
y = data['Temp'],
color = 'orange' if run_label == 'Sysbench' else 'red',
label = f'{run_label} (Avg)',
linestyle = ':',
)
ax.legend()
pyplot.savefig(out_path)
# Done
return out_path
def export_io_graph(disk, log_dir, read_rates):
"""Exports PNG graph using matplotlib."""
# Safety check
if not read_rates:
raise RuntimeError(f'No read rates for {disk.path}')
# Prep
max_rate = max(read_rates) / (1024**2)
max_rate = max(800, max_rate)
out_path = pathlib.Path(f'{log_dir}/{disk.path.name}_iobenchmark.png')
plot_data = ([], [])
# Prep data for graph
for i, rate in enumerate(read_rates):
plot_data[0].append((i+1) / len(read_rates) * 100) # Step
plot_data[1].append(int(rate / (1024**2))) # Data
# Build graph
_, ax = pyplot.subplots(
dpi=72,
figsize=GRAPH_FIGURE_SIZE,
layout='constrained',
)
ax.set_title('I/O Benchmark')
ax.plot(*plot_data, label=disk.description.replace('_', ' '))
ax.legend()
pyplot.savefig(out_path)
# Done
return out_path
def generate_horizontal_graph( def generate_horizontal_graph(
rate_list: list[float], rate_list: list[float],
graph_width: int = 40, graph_width: int = 40,
@ -259,76 +116,6 @@ def merge_rates(
return merged_rates return merged_rates
def upload_to_imgur(image_path):
"""Upload image to Imgur and return image url as str."""
image_data = None
image_link = None
# Bail early
if not image_path:
raise RuntimeError(f'Invalid image path: {image_path}')
# Read image file and convert to base64 then convert to str
with open(image_path, 'rb') as _f:
image_data = base64.b64encode(_f.read()).decode()
# POST image
url = 'https://api.imgur.com/3/image'
boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW'
payload = (
f'--{boundary}\r\nContent-Disposition: form-data; '
f'name="image"\r\n\r\n{image_data}\r\n--{boundary}--'
)
headers = {
'content-type': f'multipart/form-data; boundary={boundary}',
'Authorization': f'Client-ID {IMGUR_CLIENT_ID}',
}
response = requests.request(
'POST',
url,
data=payload,
headers=headers,
timeout=60,
)
# Return image link
if response.ok:
json_data = json.loads(response.text)
image_link = json_data['data']['link']
return image_link
def upload_to_nextcloud(image_path, ticket_number, dev_name):
"""Upload image to Nextcloud server and return folder url as str."""
image_data = None
ticket_range = f'{ticket_number[:3]}00-{ticket_number[:3]}99'
# Bail early
if not image_path:
raise RuntimeError(f'Invalid image path: {image_path}')
# Read image file and convert to base64
with open(image_path, 'rb') as _f:
image_data = _f.read()
# PUT image
url = (
f'{BENCHMARK_SERVER["Url"]}/'
f'{ticket_range}/{ticket_number}_iobenchmark'
f'_{dev_name}_{time.strftime("%Y-%m-%d_%H%M_%z")}.png'
)
requests.put(
url,
data=image_data,
auth=(BENCHMARK_SERVER['User'], BENCHMARK_SERVER['Pass']),
headers={'X-Requested-With': 'XMLHttpRequest'},
timeout=60,
)
# Return folder link
return BENCHMARK_SERVER['Short Url']
def vertical_graph_line(percent: float, rate: float, scale: int = 32) -> str: def vertical_graph_line(percent: float, rate: float, scale: int = 32) -> str:
"""Build colored graph string using thresholds, returns str.""" """Build colored graph string using thresholds, returns str."""
color_bar = None color_bar = None

View file

@ -7,11 +7,9 @@ from . import diags
from . import disk from . import disk
from . import keyboard from . import keyboard
from . import network from . import network
from . import osticket
from . import screensavers from . import screensavers
from . import sensors from . import sensors
from . import smart from . import smart
from . import surface_scan from . import surface_scan
from . import system from . import system
from . import test from . import test
from . import volumes

View file

@ -2,10 +2,10 @@
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import logging import logging
import platform
from subprocess import PIPE, STDOUT from subprocess import PIPE, STDOUT
from wk import graph
from wk.cfg.hw import ( from wk.cfg.hw import (
IO_ALT_TEST_SIZE_FACTOR, IO_ALT_TEST_SIZE_FACTOR,
IO_BLOCK_SIZE, IO_BLOCK_SIZE,
@ -24,9 +24,6 @@ from wk.exe import run_program
from wk.std import PLATFORM from wk.std import PLATFORM
from wk.ui import ansi from wk.ui import ansi
if platform.system() != 'Windows':
from wk import graph
# STATIC VARIABLES # STATIC VARIABLES
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -100,7 +97,7 @@ def calc_io_dd_values(dev_size, test_mode=False) -> dict[str, int]:
} }
def check_io_results(state, test_obj, rate_list, graph_width) -> None: def check_io_results(test_obj, rate_list, graph_width) -> None:
"""Check I/O restuls and generate report using rate_list.""" """Check I/O restuls and generate report using rate_list."""
avg_read = sum(rate_list) / len(rate_list) avg_read = sum(rate_list) / len(rate_list)
min_read = min(rate_list) min_read = min(rate_list)
@ -144,36 +141,8 @@ def check_io_results(state, test_obj, rate_list, graph_width) -> None:
else: else:
test_obj.set_status('Unknown') test_obj.set_status('Unknown')
# Export and upload graphs
export_and_upload_graphs(state, test_obj, rate_list)
def run_io_test(test_obj, log_path, test_mode=False) -> None:
def export_and_upload_graphs(state, test_obj, rate_list):
"""Export and upload graphs."""
image_path = None
try:
image_path = graph.export_io_graph(test_obj.dev, state.log_dir, rate_list)
except RuntimeError as err:
# Failed to export PNG, skip uploads below
LOG.error('Failed to export graph: %s', err)
test_obj.report.append('Failed to export graph')
return
# Upload PNG
if not state.ost.disabled and state.ost.ticket_id:
try:
imgur_url = graph.upload_to_imgur(image_path)
nextcloud_url = graph.upload_to_nextcloud(
image_path, state.ost.ticket_id, test_obj.dev.path.name)
test_obj.report.append(f'Imgur: {imgur_url}')
test_obj.report.append(f'Nextcloud: {nextcloud_url}')
except Exception as err:
LOG.error('%s', err)
LOG.error('Failed to upload graph')
test_obj.report.append('Failed to upload graph')
def run_io_test(state, test_obj, log_path, test_mode=False) -> None:
"""Run I/O benchmark and handle exceptions.""" """Run I/O benchmark and handle exceptions."""
dev_path = test_obj.dev.path dev_path = test_obj.dev.path
if PLATFORM == 'Darwin': if PLATFORM == 'Darwin':
@ -242,7 +211,7 @@ def run_io_test(state, test_obj, log_path, test_mode=False) -> None:
offset += dd_values['Read Blocks'] + skip offset += dd_values['Read Blocks'] + skip
# Check results # Check results
check_io_results(state, test_obj, read_rates, IO_GRAPH_WIDTH) check_io_results(test_obj, read_rates, IO_GRAPH_WIDTH)

View file

@ -2,7 +2,6 @@
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import logging import logging
import platform
import re import re
import subprocess import subprocess
@ -14,13 +13,6 @@ from wk.os.mac import set_fans as macos_set_fans
from wk.std import PLATFORM from wk.std import PLATFORM
from wk.ui import ansi from wk.ui import ansi
if platform.system() != 'Windows':
from wk.graph import (
export_cpu_graph,
upload_to_imgur,
upload_to_nextcloud,
)
# STATIC VARIABLES # STATIC VARIABLES
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -28,9 +20,8 @@ SysbenchType = tuple[subprocess.Popen, TextIO]
# Functions # Functions
def check_cooling_results(state, test_object) -> None: def check_cooling_results(sensors, test_object) -> None:
"""Check cooling result via sensor data.""" """Check cooling result via sensor data."""
sensors = state.sensors
idle_temp = sensors.get_cpu_temp('Idle') idle_temp = sensors.get_cpu_temp('Idle')
cooldown_temp = sensors.get_cpu_temp('Cooldown') cooldown_temp = sensors.get_cpu_temp('Cooldown')
max_temp = sensors.get_cpu_temp('Max') max_temp = sensors.get_cpu_temp('Max')
@ -93,37 +84,6 @@ def check_cooling_results(state, test_object) -> None:
*report_labels, only_cpu=True, include_avg_for=average_labels): *report_labels, only_cpu=True, include_avg_for=average_labels):
test_object.report.append(f' {line}') test_object.report.append(f' {line}')
# Export and upload graph
export_and_upload_graphs(state, test_object)
def export_and_upload_graphs(state, test_object):
"""Export and upload graphs."""
image_path = None
try:
image_path = export_cpu_graph(
cpu_description = state.system.cpu_description,
log_dir = state.log_dir,
sensor_history = state.sensors.history,
)
except RuntimeError as err:
# Failed to export PNG, skip uploads below
LOG.error('Failed to export graph: %s', err)
test_object.report.append('Failed to export graph')
return
# Upload PNG
if not state.ost.disabled and state.ost.ticket_id:
try:
imgur_url = upload_to_imgur(image_path)
nextcloud_url = upload_to_nextcloud(image_path, state.ost.ticket_id, 'cpu')
test_object.report.append(f'Imgur: {imgur_url}')
test_object.report.append(f'Nextcloud: {nextcloud_url}')
except Exception as err:
LOG.error('%s', err)
LOG.error('Failed to upload graph')
test_object.report.append('Failed to upload graph')
def check_mprime_results(test_obj, working_dir) -> None: def check_mprime_results(test_obj, working_dir) -> None:
"""Check mprime log files and update test_obj.""" """Check mprime log files and update test_obj."""

View file

@ -1,26 +1,22 @@
"""WizardKit: Hardware diagnostics""" """WizardKit: Hardware diagnostics"""
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import argparse
import atexit import atexit
import logging import logging
import os import os
import pathlib import pathlib
import platform
import subprocess import subprocess
from docopt import docopt from wk import cfg, debug, exe, log, std
from wk.cfg.hw import STATUS_COLORS
from wk import cfg, debug, exe, log, osticket, std
from wk.cfg.hw import CPU_TEST_MINUTES, STATUS_COLORS
from wk.hw import benchmark as hw_benchmark from wk.hw import benchmark as hw_benchmark
from wk.hw import cpu as hw_cpu from wk.hw import cpu as hw_cpu
from wk.hw import disk as hw_disk from wk.hw import disk as hw_disk
from wk.hw import osticket as hw_osticket
from wk.hw import sensors as hw_sensors from wk.hw import sensors as hw_sensors
from wk.hw import smart as hw_smart from wk.hw import smart as hw_smart
from wk.hw import surface_scan as hw_surface_scan from wk.hw import surface_scan as hw_surface_scan
from wk.hw import system as hw_system from wk.hw import system as hw_system
from wk.hw import volumes as hw_volumes
from wk.hw.audio import audio_test from wk.hw.audio import audio_test
from wk.hw.keyboard import keyboard_test from wk.hw.keyboard import keyboard_test
from wk.hw.network import network_test from wk.hw.network import network_test
@ -31,27 +27,10 @@ from wk.ui import ansi, cli, tui
# STATIC VARIABLES # STATIC VARIABLES
DOCSTRING = f'''{cfg.main.KIT_NAME_FULL}: Hardware Diagnostics
Usage:
hw-diags [options]
hw-diags (-h | --help)
Options:
-c --cli Force CLI mode
-h --help Show this page
-q --quick Skip menu and perform a quick check
-t --test-mode Run diags in test mode
'''
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
IO_SIZE_SKIP_NAME = (
'Skip USB Benchmarks '
f'(< {std.bytes_to_string(cfg.hw.IO_SMALL_DISK, use_binary=False)})'
)
TEST_GROUPS = { TEST_GROUPS = {
# Also used to build the menu options # Also used to build the menu options
## NOTE: This needs to be above MENU_SETS ## NOTE: This needs to be above MENU_SETS
'System Info': 'post_system_info',
'CPU (Sysbench)': 'cpu_test_sysbench', 'CPU (Sysbench)': 'cpu_test_sysbench',
'CPU (Prime95)': 'cpu_test_mprime', 'CPU (Prime95)': 'cpu_test_mprime',
'CPU (Cooling)': 'cpu_test_cooling', 'CPU (Cooling)': 'cpu_test_cooling',
@ -59,7 +38,6 @@ TEST_GROUPS = {
'Disk Self-Test': 'disk_self_test', 'Disk Self-Test': 'disk_self_test',
'Disk Surface Scan': 'disk_surface_scan', 'Disk Surface Scan': 'disk_surface_scan',
'Disk I/O Benchmark': 'disk_io_benchmark', 'Disk I/O Benchmark': 'disk_io_benchmark',
'Disk Utilization': 'disk_volume_utilization',
} }
MENU_ACTIONS = ( MENU_ACTIONS = (
'Audio Test', 'Audio Test',
@ -71,13 +49,7 @@ MENU_ACTIONS = (
MENU_ACTIONS_SECRET = ( MENU_ACTIONS_SECRET = (
'Matrix', 'Matrix',
'Tubes', 'Tubes',
'?Secrets',
) )
MENU_OPTIONS_SECRET = {
'Ignore SMART errors': False,
'Override CPU Testing Time (minutes)': CPU_TEST_MINUTES,
'Override Surface Scan Error Limit': 1,
}
MENU_OPTIONS_QUICK = ('Disk Attributes',) MENU_OPTIONS_QUICK = ('Disk Attributes',)
MENU_SETS = { MENU_SETS = {
'Full Diagnostic': (*TEST_GROUPS,), 'Full Diagnostic': (*TEST_GROUPS,),
@ -87,16 +59,12 @@ MENU_SETS = {
'Disk Self-Test', 'Disk Self-Test',
'Disk Surface Scan', 'Disk Surface Scan',
'Disk I/O Benchmark', 'Disk I/O Benchmark',
'Disk Utilization',
), ),
'Disk Diagnostic (Quick)': ('Disk Attributes', 'Disk Utilization'), 'Disk Diagnostic (Quick)': ('Disk Attributes',),
} }
MENU_TOGGLES = ( MENU_TOGGLES = (
'osTicket Integration', 'Skip USB Benchmarks',
'osTicket Tech Note',
IO_SIZE_SKIP_NAME,
) )
NUM_DISK_TESTS = len([s for s in TEST_GROUPS if s.startswith('Disk')])
PLATFORM = std.PLATFORM PLATFORM = std.PLATFORM
# Classes # Classes
@ -105,9 +73,7 @@ class State():
def __init__(self, test_mode=False): def __init__(self, test_mode=False):
self.disks: list[hw_disk.Disk] = [] self.disks: list[hw_disk.Disk] = []
self.log_dir: pathlib.Path | None = None self.log_dir: pathlib.Path | None = None
self.ost = osticket.osTicket()
self.progress_file: pathlib.Path | None = None self.progress_file: pathlib.Path | None = None
self.secret_menu = build_secret_menu()
self.sensors: hw_sensors.Sensors = hw_sensors.Sensors() self.sensors: hw_sensors.Sensors = hw_sensors.Sensors()
self.system: hw_system.System | None = None self.system: hw_system.System | None = None
self.test_groups: list[TestGroup] = [] self.test_groups: list[TestGroup] = []
@ -148,11 +114,6 @@ class State():
self.disks.clear() self.disks.clear()
self.sensors = hw_sensors.Sensors() self.sensors = hw_sensors.Sensors()
self.test_groups.clear() self.test_groups.clear()
self.ui.remove_all_subtitle_panes()
# osTicket
self.ost.init()
self.ost.disabled = not menu.toggles['osTicket Integration']['Selected']
# Set log # Set log
self.log_dir = log.format_log_path( self.log_dir = log.format_log_path(
@ -187,17 +148,6 @@ class State():
# Only add selected options # Only add selected options
continue continue
if 'System' in name:
test = Test(dev=self.system, label=name, name=name)
self.test_groups.insert(
0,
TestGroup(
name=name,
function=globals()[TEST_GROUPS[name]],
test_objects=[test],
)
)
if 'CPU' in name: if 'CPU' in name:
self.system.tests.append( self.system.tests.append(
Test(dev=self.system, label=name[5:-1], name=name), Test(dev=self.system, label=name[5:-1], name=name),
@ -215,11 +165,8 @@ class State():
# Group CPU tests # Group CPU tests
if self.system.tests: if self.system.tests:
index = 0
if self.test_groups and self.test_groups[0].name.startswith('System'):
index = 1
self.test_groups.insert( self.test_groups.insert(
index, 0,
TestGroup( TestGroup(
name='CPU & Cooling', name='CPU & Cooling',
function=run_cpu_tests, function=run_cpu_tests,
@ -255,16 +202,6 @@ class State():
for test in disk.tests: for test in disk.tests:
_f.write(f'\n{test.name}:\n') _f.write(f'\n{test.name}:\n')
_f.write('\n'.join(debug.generate_object_report(test, indent=1))) _f.write('\n'.join(debug.generate_object_report(test, indent=1)))
if platform.system() == 'Darwin':
cmd = [(
f'sudo gpt -r show "{disk.path}"'
f' >> {debug_dir}/gpt_{disk.path.name}.info'
)]
exe.run_program(cmd, check=False, shell=True)
# osTicket
with open(f'{debug_dir}/osTicket.report', 'a', encoding='utf-8') as _f:
_f.write('\n'.join(debug.generate_object_report(self.ost)))
# SMC # SMC
if os.path.exists('/.wk-live-macos'): if os.path.exists('/.wk-live-macos'):
@ -314,6 +251,36 @@ class State():
# Functions # 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: def build_menu(cli_mode=False, quick_mode=False) -> cli.Menu:
"""Build main menu, returns wk.ui.cli.Menu.""" """Build main menu, returns wk.ui.cli.Menu."""
menu = cli.Menu(title='') menu = cli.Menu(title='')
@ -331,9 +298,6 @@ def build_menu(cli_mode=False, quick_mode=False) -> cli.Menu:
menu.add_set(name, {'Targets': targets}) menu.add_set(name, {'Targets': targets})
menu.actions['Start']['Separator'] = True menu.actions['Start']['Separator'] = True
# osTicket
menu.toggles['osTicket Tech Note']['Selected'] = False
# Update default selections for quick mode if necessary # Update default selections for quick mode if necessary
if quick_mode: if quick_mode:
for name, details in menu.options.items(): for name, details in menu.options.items():
@ -345,7 +309,6 @@ def build_menu(cli_mode=False, quick_mode=False) -> cli.Menu:
menu.options['CPU (Sysbench)']['Selected'] = False menu.options['CPU (Sysbench)']['Selected'] = False
menu.options['CPU (Prime95)']['Selected'] = False menu.options['CPU (Prime95)']['Selected'] = False
menu.options['CPU (Cooling)']['Selected'] = False menu.options['CPU (Cooling)']['Selected'] = False
menu.options['System Info']['Selected'] = False
# Add CLI actions if necessary # Add CLI actions if necessary
if cli_mode or 'DISPLAY' not in os.environ: if cli_mode or 'DISPLAY' not in os.environ:
@ -371,24 +334,6 @@ def build_menu(cli_mode=False, quick_mode=False) -> cli.Menu:
return menu return menu
def build_secret_menu() -> cli.Menu:
title_text = [
ansi.color_string(('Expert Settings', "(It's a secret to everyone!)"), ('ORANGE', None)),
' ',
ansi.color_string(
[' !!', 'These settings can cause', 'MAJOR DAMAGE', 'to systems'],
[None, 'YELLOW', 'RED', 'YELLOW'],
),
' !! Please read the manual before making changes',
' ',
]
menu = cli.Menu(title='\n'.join(title_text))
for option, value in MENU_OPTIONS_SECRET.items():
menu.add_option(option, {'Selected': False, 'Value': value})
menu.add_action('Main Menu')
return menu
def cpu_tests_init(state: State) -> None: def cpu_tests_init(state: State) -> None:
"""Initialize CPU tests.""" """Initialize CPU tests."""
sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out') sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out')
@ -432,7 +377,7 @@ def cpu_test_cooling(state: State, test_object, test_mode=False) -> None:
if test_object.disabled: if test_object.disabled:
return return
hw_cpu.check_cooling_results(state, test_object) hw_cpu.check_cooling_results(state.sensors, test_object)
state.update_progress_file() state.update_progress_file()
@ -445,11 +390,6 @@ def cpu_test_mprime(state: State, test_object, test_mode=False) -> None:
test_minutes = cfg.hw.CPU_TEST_MINUTES test_minutes = cfg.hw.CPU_TEST_MINUTES
if test_mode: if test_mode:
test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT
if state.secret_menu.options.get(
'Override CPU Testing Time (minutes)')['Selected']:
test_minutes = int(
state.secret_menu.options.get('Override CPU Testing Time (minutes)')['Value'],
)
# Bail early # Bail early
if test_object.disabled: if test_object.disabled:
@ -485,7 +425,6 @@ def cpu_test_mprime(state: State, test_object, test_mode=False) -> None:
aborted = True aborted = True
# Stop Prime95 # Stop Prime95
state.sensors.save_average_temps(temp_label='Cooldown', seconds=5)
hw_cpu.stop_mprime(proc) hw_cpu.stop_mprime(proc)
# Get cooldown temp # Get cooldown temp
@ -526,11 +465,6 @@ def cpu_test_sysbench(state: State, test_object, test_mode=False) -> None:
test_minutes = cfg.hw.CPU_TEST_MINUTES test_minutes = cfg.hw.CPU_TEST_MINUTES
if test_mode: if test_mode:
test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT
if state.secret_menu.options.get(
'Override CPU Testing Time (minutes)')['Selected']:
test_minutes = int(
state.secret_menu.options.get('Override CPU Testing Time (minutes)')['Value'],
)
# Bail early # Bail early
if test_object.disabled: if test_object.disabled:
@ -562,7 +496,6 @@ def cpu_test_sysbench(state: State, test_object, test_mode=False) -> None:
LOG.error('Failed to find sysbench process', exc_info=True) LOG.error('Failed to find sysbench process', exc_info=True)
except KeyboardInterrupt: except KeyboardInterrupt:
aborted = True aborted = True
state.sensors.save_average_temps(temp_label='Cooldown', seconds=5)
hw_cpu.stop_sysbench(proc, filehandle) hw_cpu.stop_sysbench(proc, filehandle)
# Get cooldown temp # Get cooldown temp
@ -591,11 +524,9 @@ def cpu_test_sysbench(state: State, test_object, test_mode=False) -> None:
# 0 == Completed w/out issue # 0 == Completed w/out issue
# -2 == Stopped with INT signal # -2 == Stopped with INT signal
# -15 == Stopped with TERM signal # -15 == Stopped with TERM signal
test_object.failed = True
test_object.set_status('Failed') test_object.set_status('Failed')
test_object.report.append(f' Failed with return code: {proc.returncode}') test_object.report.append(f' Failed with return code: {proc.returncode}')
else: else:
test_object.passed = True
test_object.set_status('Passed') test_object.set_status('Passed')
test_object.report.append(' Completed without issue.') test_object.report.append(' Completed without issue.')
state.update_progress_file() state.update_progress_file()
@ -634,18 +565,13 @@ def disk_io_benchmark(
) )
state.ui.set_current_pane_height(10) state.ui.set_current_pane_height(10)
for test in test_objects: for test in test_objects:
if ( if test.disabled:
skip_usb # Skip
and test.dev.bus == 'USB'
and test.dev.size < cfg.hw.IO_SMALL_DISK
):
test.set_status('Skipped')
test.disabled = True
continue continue
# Start benchmark # Skip USB devices if requested
for test in test_objects: if skip_usb and test.dev.bus == 'USB':
if test.disabled: test.set_status('Skipped')
continue continue
# Start benchmark # Start benchmark
@ -662,7 +588,7 @@ def disk_io_benchmark(
) )
state.update_progress_file() state.update_progress_file()
try: try:
hw_benchmark.run_io_test(state, test, test_log, test_mode=test_mode) hw_benchmark.run_io_test(test, test_log, test_mode=test_mode)
except KeyboardInterrupt: except KeyboardInterrupt:
aborted = True aborted = True
except (subprocess.CalledProcessError, TypeError, ValueError) as err: except (subprocess.CalledProcessError, TypeError, ValueError) as err:
@ -787,12 +713,7 @@ def disk_surface_scan(state: State, test_objects, test_mode=False) -> None:
"""Read-only disk surface scan using badblocks.""" """Read-only disk surface scan using badblocks."""
LOG.info('Disk Surface Scan (badblocks)') LOG.info('Disk Surface Scan (badblocks)')
aborted = False aborted = False
max_errors = 1
threads = [] threads = []
if state.secret_menu.options.get(
'Override Surface Scan Error Limit')['Selected']:
max_errors = state.secret_menu.options.get(
'Override Surface Scan Error Limit')['Value']
# Update panes # Update panes
state.update_title_text( state.update_title_text(
@ -809,7 +730,7 @@ def disk_surface_scan(state: State, test_objects, test_mode=False) -> None:
# Start thread # Start thread
test_log = f'{state.log_dir}/{test.dev.path.name}_badblocks.log' test_log = f'{state.log_dir}/{test.dev.path.name}_badblocks.log'
threads.append(exe.start_thread( threads.append(exe.start_thread(
hw_surface_scan.run_scan, args=(test, test_log, test_mode, max_errors), hw_surface_scan.run_scan, args=(test, test_log, test_mode),
)) ))
# Show progress # Show progress
@ -842,20 +763,14 @@ def disk_surface_scan(state: State, test_objects, test_mode=False) -> None:
raise std.GenericAbort('Aborted') raise std.GenericAbort('Aborted')
def disk_volume_utilization(state, test_objects, test_mode=False) -> None:
"""Check disk for full volumes."""
_ = test_mode
LOG.info('Disk Utilization')
for test in test_objects:
hw_volumes.check_volume_utilization(test)
# Done
state.update_progress_file()
def main() -> None: def main() -> None:
"""Main function for hardware diagnostics.""" """Main function for hardware diagnostics."""
args = docopt(DOCSTRING) try:
args = argparse_helper()
except SystemExit:
print('')
cli.pause('Press Enter to exit...')
raise
log.update_log_path(dest_name='Hardware-Diagnostics', timestamp=True) log.update_log_path(dest_name='Hardware-Diagnostics', timestamp=True)
# Safety check # Safety check
@ -869,7 +784,6 @@ def main() -> None:
# Quick Mode # Quick Mode
if args['--quick']: if args['--quick']:
menu.toggles['osTicket Integration']['Selected'] = False
run_diags(state, menu, quick_mode=True, test_mode=args['--test-mode']) run_diags(state, menu, quick_mode=True, test_mode=args['--test-mode'])
return return
@ -901,11 +815,9 @@ def main() -> None:
state.ui.update_clock() state.ui.update_clock()
# Secrets # Secrets
if '?Secrets' in selection:
state.secret_menu.settings_select()
if 'Matrix' in selection: if 'Matrix' in selection:
screensaver('matrix') screensaver('matrix')
if 'Tubes' in selection: elif 'Tubes' in selection:
# Tubes ≈≈ Pipes? # Tubes ≈≈ Pipes?
screensaver('pipes') screensaver('pipes')
@ -927,33 +839,6 @@ def main() -> None:
state.update_title_text('Main Menu') state.update_title_text('Main Menu')
def post_system_info(state, test_objects, test_mode=False) -> None:
"""Post system info to osTicket."""
_ = test_mode
# Bail early
if state.ost.disabled:
return
# Build report
report = state.system.generate_full_report()
if state.disks:
report.append('\n[Disks]')
for disk in state.disks:
report.append(f'... {disk.description}')
# Post to osTicket
state.ost.post_response('\n'.join(report))
# Update test object(s)
for test_object in test_objects:
test_object.passed = True
test_object.set_status('Done')
# Done
state.update_progress_file()
def print_countdown(proc, seconds) -> None: def print_countdown(proc, seconds) -> None:
"""Print countdown to screen while proc is alive.""" """Print countdown to screen while proc is alive."""
seconds = int(seconds) seconds = int(seconds)
@ -991,22 +876,8 @@ def run_cpu_tests(state: State, test_objects, test_mode=False) -> None:
cpu_tests_end(state) cpu_tests_end(state)
state.update_progress_file() state.update_progress_file()
# Post results to osTicket
if not state.ost.disabled:
failed = any(test.failed for test in state.system.tests)
cli.print_info('Posting results to osTicket...')
state.ost.post_response(
hw_osticket.build_report(state.system, 'CPU'),
color='Diags FAIL' if failed else 'Diags',
)
def run_diags(state: State, menu, quick_mode=False, test_mode=False) -> None:
def run_diags(
state: State,
menu: cli.Menu,
quick_mode: bool = False,
test_mode: bool = False,
) -> None:
"""Run selected diagnostics.""" """Run selected diagnostics."""
aborted = False aborted = False
atexit.register(state.save_debug_reports) atexit.register(state.save_debug_reports)
@ -1018,32 +889,6 @@ def run_diags(
cli.pause() cli.pause()
return return
# osTicket
if not state.ost.disabled:
# Select Ticket
state.ost.select_ticket()
# Update top_text
if state.ost.ticket_id:
state.ui.add_subtitle_pane(
cli.color_string(
[f'#{state.ost.ticket_id}', str(state.ost.ticket_name)],
[None, 'CYAN'],
),
str(state.ost.ticket_subject),
)
# Add note
if (state.ost.ticket_id
and menu.toggles['osTicket Tech Note']['Selected']):
note_lines = state.ost.add_note()
if note_lines:
state.ui.add_subtitle_pane(
cli.color_string('Tech Note', 'YELLOW'),
' | '.join(note_lines),
)
# Run tests # Run tests
for group in state.test_groups: for group in state.test_groups:
@ -1051,7 +896,7 @@ def run_diags(
function = group.function function = group.function
args = [group.test_objects] args = [group.test_objects]
if group.name == 'Disk I/O Benchmark': if group.name == 'Disk I/O Benchmark':
args.append(menu.toggles[IO_SIZE_SKIP_NAME]['Selected']) args.append(menu.toggles['Skip USB Benchmarks']['Selected'])
state.ui.clear_current_pane() state.ui.clear_current_pane()
try: try:
function(state, *args, test_mode=test_mode) function(state, *args, test_mode=test_mode)
@ -1073,17 +918,10 @@ def run_diags(
if test.status == 'Pending': if test.status == 'Pending':
test.set_status('Aborted') test.set_status('Aborted')
# Post disk results
hw_osticket.post_disk_results(state, NUM_DISK_TESTS)
# Show results # Show results
show_results(state) show_results(state)
# Update checkboxes
hw_osticket.update_checkboxes(state, NUM_DISK_TESTS)
# Done # Done
state.ui.remove_all_subtitle_panes()
state.save_debug_reports() state.save_debug_reports()
atexit.unregister(state.save_debug_reports) atexit.unregister(state.save_debug_reports)
if quick_mode: if quick_mode:
@ -1114,7 +952,7 @@ def show_results(state: State) -> None:
] ]
if cpu_tests_enabled: if cpu_tests_enabled:
cli.print_success('CPU:') cli.print_success('CPU:')
cli.print_report(state.system.generate_cpu_ram_report()) cli.print_report(state.system.generate_report())
cli.print_standard(' ') cli.print_standard(' ')
# Disk Tests # Disk Tests

View file

@ -1,216 +0,0 @@
"""WizardKit: osTicket hardware diagnostic functions"""
# vim: sts=2 sw=2 ts=2
import logging
import re
from wk import osticket
from wk.cfg.hw import (
REGEX_BLOCK_GRAPH,
REGEX_SMART_ATTRIBUTES,
)
from wk.hw import smart as hw_smart
from wk.ui import cli
# STATIC VARIABLES
LOG = logging.getLogger(__name__)
# Functions
def build_report(dev, dev_type, num_disk_tests=None):
"""Build report for posting to osTicket, returns str."""
report = []
# Combined result
if dev_type == 'CPU' or len(dev.tests) == num_disk_tests:
# Build list of failed tests (if any)
failed_tests = [t.name for t in dev.tests if t.failed]
failed_tests = [name.replace('Disk ', '') for name in failed_tests]
if len(failed_tests) > 2:
failed_tests = f'{", ".join(failed_tests[:-1])}, & {failed_tests[-1]}'
else:
failed_tests = ' & '.join(failed_tests)
# Get overall result
result = 'UNKNOWN'
if any(t.failed for t in dev.tests):
result = 'FAILED'
elif all(t.passed for t in dev.tests):
result = 'PASSED'
# Add to report
report.append(
f'{dev_type} hardware diagnostic tests: {result}'
f'{" ("+failed_tests+")" if failed_tests else ""}'
)
report.append('')
# Description
if hasattr(dev, 'cpu_description'):
report.append(dev.cpu_description)
else:
report.append(dev.description)
if hasattr(dev, 'ram_total'):
if len(dev.ram_dimms) == 1 and 'justTotalRAM' in dev.ram_dimms[0]:
report.append(f'{dev.ram_total} (Total - no DIMM info available)')
else:
report.append(f'{dev.ram_total} ({", ".join(dev.ram_dimms)})')
if hasattr(dev, 'serial') and dev.serial:
report.append(f'Serial Number: {dev.serial}')
report.append('')
# Notes
if hasattr(dev, 'notes') and dev.notes:
report.append('Notes')
report.extend([f'... {note}' for note in dev.notes])
report.append('')
# Tests
for test in dev.tests:
report.append(f'{test.name} ({test.status})')
# Report
if test.name == 'Disk Attributes' and dev.attributes:
report.extend(
convert_report(
hw_smart.generate_attribute_report(dev),
start_index=0,
),
)
else:
report.extend(convert_report(test.report, start_index=1))
# I/O graph upload report
report.extend(getattr(test, 'upload_report', []))
# Spacer
report.append('')
# Remove last line if empty
if not report[-1].strip():
report.pop(-1)
# Done
return cli.strip_colors('\n'.join(report))
def convert_report(original_report, start_index):
"""Convert report to an osTicket compatible type, returns list."""
report = []
# Convert report
for line in original_report[start_index:]:
# Remove colors and leading spaces
line = cli.strip_colors(line)
line = re.sub(r'^\s+', '', line)
# Disk I/O Benchmark
if REGEX_BLOCK_GRAPH.search(line):
line = REGEX_BLOCK_GRAPH.sub('', line)
line = line.strip()
# SMART attributes
match = REGEX_SMART_ATTRIBUTES.search(line)
if match:
# Switch decimal and hex labels
_dec = f'{match.group("decimal"):>3}'
_dec = osticket.pad_with_dots(_dec)
_hex = match.group('hex')
_data = match.group('data')
line = f'{_hex}/{_dec}: {_data}'
line = line.replace('failed', 'FAILED')
# Skip empty lines
if not line.strip():
continue
# Fix inner spacing
for spacing in re.findall(r'\s\s+', line):
new_padding = osticket.pad_with_dots(spacing)
new_padding += ' '
line = line.replace(spacing, new_padding)
# Indent line
line = f'... {line}'
# Add to (converted) report
report.append(line)
# Done
return report
def post_disk_results(state, num_disk_tests):
"""Post disk test results for all disks."""
disk_tests = []
for group in state.test_groups:
if group.name.startswith('Disk'):
disk_tests.extend(group.test_objects)
# Bail if no disk tests were run
if not disk_tests or state.ost.disabled:
return
# Post disk results
cli.print_info('Posting results to osTicket...')
for disk in state.disks:
state.ost.post_response(
build_report(disk, 'Disk', num_disk_tests),
color='Diags FAIL' if any(t.failed for t in disk.tests) else 'Diags',
)
def update_checkboxes(state, num_disk_tests):
"""Update osTicket checkboxes after confirmation."""
cpu_tests = []
disk_tests = []
num_disk_tests_run = len(state.test_groups)
# Build list of tests
for group in state.test_groups:
if group.name.startswith('CPU'):
cpu_tests.extend(group.test_objects)
num_disk_tests_run -= 1
elif group.name.startswith('Disk'):
disk_tests.extend(group.test_objects)
elif group.name.startswith('System'):
num_disk_tests_run -= 1
# Bail if osTicket integration disabled
if state.ost.disabled:
return
# Bail if values not confirmed
if not cli.ask('Update osTicket checkboxes using the data above?'):
return
# CPU max temp and pass/fail
if cpu_tests:
state.ost.set_cpu_max_temp(state.sensors.get_cpu_temp('Max'))
if any(t.failed for t in cpu_tests):
state.ost.set_flag_failed('CPU')
elif all(t.passed for t in cpu_tests):
state.ost.set_flag_passed('CPU')
# Check results for all disks
if state.disks:
all_disks_passed = True
for disk in state.disks:
if any(t.failed for t in disk.tests):
# Mark failed disk in osTicket and stop checking results
all_disks_passed = False
state.ost.set_flag_failed('Disk')
break
if not all(t.passed for t in disk.tests):
all_disks_passed = False
break
# All disks passed
if all_disks_passed and num_disk_tests_run == num_disk_tests:
# Only mark as passed if a full disk diagnostic passed
state.ost.set_flag_passed('Disk')
if __name__ == '__main__':
print("This file is not meant to be called directly.")

View file

@ -10,7 +10,7 @@ from wk.cfg.hw import (
BADBLOCKS_LARGE_DISK, BADBLOCKS_LARGE_DISK,
BADBLOCKS_REGEX, BADBLOCKS_REGEX,
BADBLOCKS_RESULTS_REGEX, BADBLOCKS_RESULTS_REGEX,
#BADBLOCKS_SKIP_REGEX, BADBLOCKS_SKIP_REGEX,
TEST_MODE_BADBLOCKS_LIMIT, TEST_MODE_BADBLOCKS_LIMIT,
) )
from wk.exe import run_program from wk.exe import run_program
@ -25,52 +25,34 @@ LOG = logging.getLogger(__name__)
# Functions # Functions
def check_surface_scan_results(test_obj, log_path) -> None: def check_surface_scan_results(test_obj, log_path) -> None:
"""Check results and set test status.""" """Check results and set test status."""
report = []
report_color = None
# Read result
with open(log_path, 'r', encoding='utf-8') as _f: with open(log_path, 'r', encoding='utf-8') as _f:
for line in _f.readlines(): for line in _f.readlines():
line = ansi.strip_colors(line.strip()) line = ansi.strip_colors(line.strip())
if not line: if not line or BADBLOCKS_SKIP_REGEX.match(line):
# Skip # Skip
continue continue
# Clean line by removing backspaces/etc # Clean line by removing backspaces/etc
match = BADBLOCKS_RESULTS_REGEX.match(line) line = BADBLOCKS_RESULTS_REGEX.sub(r'\1 \2', line)
if match:
if match.group(2) == ' done':
line = BADBLOCKS_RESULTS_REGEX.sub(r'\1done', line)
else:
line = BADBLOCKS_RESULTS_REGEX.sub(r'\1\2', line)
line = line.replace(r'\x08', '')
line = line.strip()
# Add to report # Add to report
report.append(line)
match = BADBLOCKS_REGEX.search(line) match = BADBLOCKS_REGEX.search(line)
if match: if match:
if all(s == '0' for s in match.groups()): if all(s == '0' for s in match.groups()):
test_obj.passed = True test_obj.passed = True
test_obj.report.append(f' {line}')
test_obj.set_status('Passed') test_obj.set_status('Passed')
else: else:
test_obj.failed = True test_obj.failed = True
test_obj.report.append(f' {ansi.color_string(line, "YELLOW")}')
test_obj.set_status('Failed') test_obj.set_status('Failed')
else:
# Set report color and save to test_obj test_obj.report.append(f' {ansi.color_string(line, "YELLOW")}')
if test_obj.failed:
report_color = 'RED'
elif not test_obj.passed:
report_color = 'YELLOW'
for line in report:
test_obj.report.append(f' {ansi.color_string(line, report_color)}')
# Handle undefined result status
if not (test_obj.passed or test_obj.failed): if not (test_obj.passed or test_obj.failed):
test_obj.set_status('Unknown') test_obj.set_status('Unknown')
def run_scan(test_obj, log_path, test_mode=False, max_errors=1) -> None: def run_scan(test_obj, log_path, test_mode=False) -> None:
"""Run surface scan and handle exceptions.""" """Run surface scan and handle exceptions."""
block_size = '1024' block_size = '1024'
dev = test_obj.dev dev = test_obj.dev
@ -89,17 +71,8 @@ def run_scan(test_obj, log_path, test_mode=False, max_errors=1) -> None:
or dev.size >= BADBLOCKS_LARGE_DISK): or dev.size >= BADBLOCKS_LARGE_DISK):
block_size = '4096' block_size = '4096'
# Max errors
if int(max_errors) <= 0:
max_errors = ''
else:
max_errors = f'-e{max_errors}'
# Start scan # Start scan
cmd = ['sudo', 'badblocks', '-sv', '-b', block_size] cmd = ['sudo', 'badblocks', '-sv', '-b', block_size, '-e', '1', dev_path]
if max_errors:
cmd.append(max_errors)
cmd.append(dev_path)
if test_mode: if test_mode:
# Only test a limited scope instead of the whole device # Only test a limited scope instead of the whole device
cmd.append(TEST_MODE_BADBLOCKS_LIMIT) cmd.append(TEST_MODE_BADBLOCKS_LIMIT)

View file

@ -2,7 +2,6 @@
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import logging import logging
import os
import plistlib import plistlib
import re import re
@ -18,7 +17,6 @@ from wk.ui import ansi
# STATIC VARIABLES # STATIC VARIABLES
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
EVERYMAC_URL = 'https://everymac.com/ultimate-mac-lookup/?search_keywords='
@dataclass(slots=True) @dataclass(slots=True)
@ -35,7 +33,7 @@ class System:
self.set_cpu_description() self.set_cpu_description()
self.get_ram_details() self.get_ram_details()
def generate_cpu_ram_report(self) -> list[str]: def generate_report(self) -> list[str]:
"""Generate CPU & RAM report, returns list.""" """Generate CPU & RAM report, returns list."""
report = [] report = []
report.append(ansi.color_string('Device', 'BLUE')) report.append(ansi.color_string('Device', 'BLUE'))
@ -51,38 +49,6 @@ class System:
return report return report
def generate_full_report(self) -> list[str]:
"""Generate full report, returns list."""
report = ['[System]']
report.extend([f'... {line}' for line in self.get_system_info()])
report.append('\n[Motherboard]')
report.extend([f'... {line}' for line in self.get_mobo_info()])
report.append('\n[BIOS]')
report.extend([f'... {line}' for line in self.get_bios_info()])
report.append('\n[CPU]')
report.append(f'... {self.cpu_description}')
report.append('\n[RAM]')
report.append(f'... {self.ram_total} ({", ".join(self.ram_dimms)})')
report.append('\n[GPU]')
report.extend([f'... {line}' for line in self.get_gpu_info()])
return report
def get_bios_info(self) -> list[str]:
"""Get BIOS details, returns list."""
report = []
# Bail early
if PLATFORM != 'Linux':
# Only Linux is supported ATM
return report
# Get details
report.append(f'Version: {get_dmi_info_linux("bios_version")}')
report.append(f'Released: {get_dmi_info_linux("bios_date")}')
# Done
return report
def get_cpu_details(self) -> None: def get_cpu_details(self) -> None:
"""Get CPU details using OS specific methods.""" """Get CPU details using OS specific methods."""
cmd = ['lscpu', '--json'] cmd = ['lscpu', '--json']
@ -102,78 +68,6 @@ class System:
continue continue
self.raw_details[_field] = _data self.raw_details[_field] = _data
def get_gpu_info(self) -> list[str]:
"""Get GPU details, returns list."""
report = []
# Bail early
if PLATFORM != 'Linux':
# Only Linux is supported ATM
return report
# Get PCI details
proc = run_program(['lspci'])
for line in proc.stdout.splitlines():
if 'VGA' not in line:
continue
line = re.sub('^.*:', '', line)
line = re.sub('Integrated Graphics Controller.*', 'iGPU', line)
line = line.replace('Advanced Micro Devices, Inc.', 'AMD')
line = line.replace('Intel Corporation', 'Intel')
line = line.replace('Generation Core Processor Family', 'Gen')
report.append(f'{line.strip()}')
# Get GLX info
if 'DISPLAY' in os.environ or 'WAYLAND_DISPLAY' in os.environ:
proc = run_program(['glxinfo'])
for line in proc.stdout.splitlines():
if 'OpenGL renderer' in line:
line = re.sub('^.*:', '', line)
report.append(line.strip())
break
# Done
return report
def get_mobo_info(self) -> list[str]:
"""Get motherboard details, returns list."""
report = []
# Bail early
if PLATFORM != 'Linux':
# Only Linux is supported ATM
return report
# Get details
report.append(f'Vendor: {get_dmi_info_linux("board_vendor")}')
report.append(f'Name: {get_dmi_info_linux("board_name")}')
report.append(f'Version: {get_dmi_info_linux("board_version")}')
report.append(f'Serial: {get_dmi_info_linux("board_serial")}')
# Done
return report
def get_system_info(self) -> list[str]:
"""Get system details, returns list."""
report = []
# Bail early
if PLATFORM != 'Linux':
# Only Linux is supported ATM
return report
# Get details
vendor = get_dmi_info_linux("sys_vendor")
serial = get_dmi_info_linux("product_serial")
report.append(f'Vendor: {vendor}')
report.append(f'Name: {get_dmi_info_linux("product_name")}')
report.append(f'Serial: {serial}')
if 'apple' in vendor.lower():
report.append(f'{EVERYMAC_URL}{serial}')
# Done
return report
def get_ram_details(self) -> None: def get_ram_details(self) -> None:
"""Get RAM details using OS specific methods.""" """Get RAM details using OS specific methods."""
if PLATFORM == 'Darwin': if PLATFORM == 'Darwin':
@ -211,16 +105,6 @@ class System:
self.cpu_description = re.sub(r'\s+', ' ', proc.stdout.strip()) self.cpu_description = re.sub(r'\s+', ' ', proc.stdout.strip())
def get_dmi_info_linux(value) -> str:
"""Get DMI info, returns str."""
dmi_path = '/sys/devices/virtual/dmi/id'
cmd = ['sudo', 'cat', f'{dmi_path}/{value}']
proc = run_program(cmd, check=False)
if proc.returncode:
return '[???]'
return proc.stdout.strip()
def get_ram_list_linux() -> list[list]: def get_ram_list_linux() -> list[list]:
"""Get RAM list using dmidecode.""" """Get RAM list using dmidecode."""
cmd = ['sudo', 'dmidecode', '--type', 'memory'] cmd = ['sudo', 'dmidecode', '--type', 'memory']

View file

@ -1,123 +0,0 @@
"""WizardKit: Volume functions"""
# vim: sts=2 sw=2 ts=2
import logging
import re
from wk import os as wk_os
from wk.cfg.hw import (
VOLUME_FAILURE_THRESHOLD,
VOLUME_WARNING_THRESHOLD,
VOLUME_SIZE_THRESHOLD,
)
from wk.std import PLATFORM, bytes_to_string
from wk.ui.ansi import color_string
# STATIC VARIABLES
LOG = logging.getLogger(__name__)
# Functions
def add_dev_line(test_obj, details) -> None:
"""Add device line to test report."""
if details is test_obj.dev:
details = details.raw_details
filesystem = details['fstype']
report_line = re.sub(r"^/dev/", " ", details['name'])
label = details['label']
if label:
label = f', "{color_string(label, "CYAN")}"'
report_line += f' ({filesystem}{label if label else ""})'
# Bail early
if not filesystem:
# Skip devices without a filesystem
return
# Get sizes
used = -1
percent_used = -1
size = details['size']
if PLATFORM == 'Darwin':
size = int(details.get('TotalSize', -1))
free = int(details.get('FreeSpace', 0))
used = size - free
elif PLATFORM == 'Linux':
free = details.get('fsavail', 0)
used = details.get('fsused', -1)
if free is None:
free = 0
if used is None:
used = -1
percent_used = (used / size) * 100
# Report Bitlocker
if filesystem == 'BitLocker':
test_obj.report.append(f'{report_line} {bytes_to_string(size)}')
# Handle unsupported devices
if not details['mountpoint']:
# Under Linux the volume needs to be mounted to get used space
used = -1
# Check for failures
if (used > 0
and percent_used >= VOLUME_FAILURE_THRESHOLD
and size >= VOLUME_SIZE_THRESHOLD * 1024**3):
test_obj.failed = True
# Build and color size_line if needed
color = None
if test_obj.failed:
color = 'RED'
elif percent_used >= VOLUME_WARNING_THRESHOLD:
color = 'YELLOW'
size_line = f'{bytes_to_string(size)}'
if used > 0:
size_line += f' ({bytes_to_string(used)} used, {percent_used:0.0f}% full)'
size_line = color_string(size_line, str(color))
# Done
test_obj.report.append(f'{report_line} {size_line}')
def check_volume_utilization(test_obj) -> None:
"""Check volume utilization using OS specific methods."""
dev = test_obj.dev
# Mount all volumes (read only if possible)
mount_all_volumes(test_obj.dev)
dev.update_details(skip_children=False)
# Build report
test_obj.report.append(color_string('Disk Utilization', 'BLUE'))
for _d in (dev, *dev.children):
add_dev_line(test_obj, _d)
# Update test object
if test_obj.failed:
test_obj.dev.add_note('Full volume(s) detected', color='YELLOW')
test_obj.passed = False
test_obj.set_status('Failed')
else:
test_obj.passed = True
test_obj.set_status('Passed')
def mount_all_volumes(dev) -> None:
"""Mount all volumes for dev using."""
if PLATFORM == 'Darwin':
# NOTE: Disabled due to a bug they should already be mounted by macOS
#wk_os.mac.mount_disk(device_path=dev.path)
pass
elif PLATFORM == 'Linux':
wk_os.linux.mount_volumes(
device_path=dev.path,
read_write=False,
scan_corestorage=not any(t.failed for t in dev.tests),
)
if __name__ == '__main__':
print("This file is not meant to be called directly.")

View file

@ -43,7 +43,7 @@ def case_insensitive_search(
path = pathlib.Path(path).resolve() path = pathlib.Path(path).resolve()
given_path = path.joinpath(item) given_path = path.joinpath(item)
real_path = None real_path = None
regex = fr'^{item}' regex = fr'^{item}$'
# Quick check # Quick check
if given_path.exists(): if given_path.exists():

View file

@ -11,6 +11,7 @@ import re
from wk.cfg.launchers import LAUNCHERS from wk.cfg.launchers import LAUNCHERS
from wk.cfg.main import ARCHIVE_PASSWORD, KIT_NAME_FULL from wk.cfg.main import ARCHIVE_PASSWORD, KIT_NAME_FULL
from wk.cfg.music import MUSIC_MOD, MUSIC_SNES, MUSIC_SNES_BAD
from wk.cfg.sources import SOURCES from wk.cfg.sources import SOURCES
from wk.exe import popen_program, run_program, wait_for_procs from wk.exe import popen_program, run_program, wait_for_procs
from wk.io import copy_file, delete_item, recursive_copy, rename_item from wk.io import copy_file, delete_item, recursive_copy, rename_item
@ -122,16 +123,9 @@ def download_aida64() -> None:
def download_autoruns() -> None: def download_autoruns() -> None:
"""Download Autoruns.""" """Download Autoruns."""
out_path = BIN_DIR.joinpath(f'Sysinternals/Autoruns64.exe') for item in ('Autoruns32', 'Autoruns64'):
download_file(out_path, SOURCES['Autoruns64']) out_path = BIN_DIR.joinpath(f'Sysinternals/{item}.exe')
download_file(out_path, SOURCES[item])
def download_bcuninstaller() -> None:
"""Download Bulk Crap Uninstaller."""
archive = download_to_temp('BCU.zip', SOURCES['BCUninstaller'])
extract_to_bin(archive, 'BCUninstaller')
delete_from_temp('BCU.zip')
delete_item(BIN_DIR.joinpath('BCUninstaller/win-x86'))
def download_bleachbit() -> None: def download_bleachbit() -> None:
@ -151,6 +145,9 @@ def download_bleachbit() -> None:
def download_bluescreenview() -> None: def download_bluescreenview() -> None:
"""Download BlueScreenView.""" """Download BlueScreenView."""
archive_32 = download_to_temp(
'bluescreenview32.zip', SOURCES['BlueScreenView32'],
)
archive_64 = download_to_temp( archive_64 = download_to_temp(
'bluescreenview64.zip', SOURCES['BlueScreenView64'], 'bluescreenview64.zip', SOURCES['BlueScreenView64'],
) )
@ -160,21 +157,11 @@ def download_bluescreenview() -> None:
out_path.joinpath('BlueScreenView.exe'), out_path.joinpath('BlueScreenView.exe'),
out_path.joinpath('BlueScreenView64.exe'), out_path.joinpath('BlueScreenView64.exe'),
) )
extract_archive(archive_32, out_path)
delete_from_temp('bluescreenview32.zip')
delete_from_temp('bluescreenview64.zip') delete_from_temp('bluescreenview64.zip')
def download_coretemp():
"""Download Core Temp."""
archive_64 = download_to_temp('coretemp64.zip', SOURCES['CoreTemp64'])
out_path = BIN_DIR.joinpath('CoreTemp')
extract_archive(archive_64, out_path, 'Core Temp.exe')
rename_item(
out_path.joinpath('Core Temp.exe'),
out_path.joinpath('CoreTemp64.exe'),
)
delete_from_temp('coretemp64.zip')
def download_ddu() -> None: def download_ddu() -> None:
"""Download Display Driver Uninstaller.""" """Download Display Driver Uninstaller."""
archive = download_to_temp('DDU.exe', SOURCES['DDU']) archive = download_to_temp('DDU.exe', SOURCES['DDU'])
@ -191,6 +178,13 @@ def download_ddu() -> None:
delete_from_temp('DDU.exe') delete_from_temp('DDU.exe')
def download_bcuninstaller() -> None:
"""Download Bulk Crap Uninstaller."""
archive = download_to_temp('BCU.zip', SOURCES['BCUninstaller'])
extract_to_bin(archive, 'BCUninstaller')
delete_from_temp('BCU.zip')
def download_erunt() -> None: def download_erunt() -> None:
"""Download ERUNT.""" """Download ERUNT."""
archive = download_to_temp('erunt.zip', SOURCES['ERUNT']) archive = download_to_temp('erunt.zip', SOURCES['ERUNT'])
@ -200,6 +194,7 @@ def download_erunt() -> None:
def download_everything() -> None: def download_everything() -> None:
"""Download Everything.""" """Download Everything."""
archive_32 = download_to_temp('everything32.zip', SOURCES['Everything32'])
archive_64 = download_to_temp('everything64.zip', SOURCES['Everything64']) archive_64 = download_to_temp('everything64.zip', SOURCES['Everything64'])
out_path = BIN_DIR.joinpath('Everything') out_path = BIN_DIR.joinpath('Everything')
extract_archive(archive_64, out_path, 'Everything.exe') extract_archive(archive_64, out_path, 'Everything.exe')
@ -207,6 +202,8 @@ def download_everything() -> None:
out_path.joinpath('Everything.exe'), out_path.joinpath('Everything.exe'),
out_path.joinpath('Everything64.exe'), out_path.joinpath('Everything64.exe'),
) )
extract_archive(archive_32, out_path)
delete_from_temp('everything32.zip')
delete_from_temp('everything64.zip') delete_from_temp('everything64.zip')
@ -214,11 +211,14 @@ def download_fastcopy() -> None:
"""Download FastCopy.""" """Download FastCopy."""
installer = download_to_temp('FastCopyInstaller.exe', SOURCES['FastCopy']) installer = download_to_temp('FastCopyInstaller.exe', SOURCES['FastCopy'])
out_path = BIN_DIR.joinpath('FastCopy') out_path = BIN_DIR.joinpath('FastCopy')
run_program([installer, '/NOSUBDIR', f'/DIR={out_path}', '/EXTRACT64']) tmp_path = TMP_DIR.joinpath('FastCopy64')
run_program([installer, '/NOSUBDIR', f'/DIR={out_path}', '/EXTRACT32'])
run_program([installer, '/NOSUBDIR', f'/DIR={tmp_path}', '/EXTRACT64'])
rename_item( rename_item(
out_path.joinpath('FastCopy.exe'), tmp_path.joinpath('FastCopy.exe'),
out_path.joinpath('FastCopy64.exe'), out_path.joinpath('FastCopy64.exe'),
) )
delete_from_temp('FastCopy64')
delete_from_temp('FastCopyInstaller.exe') delete_from_temp('FastCopyInstaller.exe')
delete_item(BIN_DIR.joinpath('FastCopy/setup.exe')) delete_item(BIN_DIR.joinpath('FastCopy/setup.exe'))
@ -243,23 +243,11 @@ def download_furmark() -> None:
delete_from_temp('FurMarkInstall') delete_from_temp('FurMarkInstall')
def download_libreoffice() -> None: def download_hwinfo() -> None:
"""Download LibreOffice.""" """Download HWiNFO."""
out_path = INSTALLERS_DIR.joinpath(f'LibreOffice64.msi') archive = download_to_temp('HWiNFO.zip', SOURCES['HWiNFO'])
download_file( extract_to_bin(archive, 'HWiNFO')
out_path, delete_from_temp('HWiNFO.zip')
SOURCES[f'LibreOffice64'],
referer='https://www.libreoffice.org/download/download-libreoffice/',
)
ui.sleep(1)
def download_linux_reader():
"""Download Linux Reader."""
installer = download_to_temp('LinuxReader.exe', SOURCES['Linux Reader'])
out_path = BIN_DIR.joinpath('LinuxReader')
extract_archive(installer, out_path, '-x!*PLUGINSDIR', '-x!Uninstall*')
delete_from_temp('LinuxReader.exe')
def download_macs_fan_control() -> None: def download_macs_fan_control() -> None:
@ -268,6 +256,18 @@ def download_macs_fan_control() -> None:
download_file(out_path, SOURCES['Macs Fan Control']) download_file(out_path, SOURCES['Macs Fan Control'])
def download_libreoffice() -> None:
"""Download LibreOffice."""
for arch in 32, 64:
out_path = INSTALLERS_DIR.joinpath(f'LibreOffice{arch}.msi')
download_file(
out_path,
SOURCES[f'LibreOffice{arch}'],
referer='https://www.libreoffice.org/download/download-libreoffice/',
)
ui.sleep(1)
def download_neutron() -> None: def download_neutron() -> None:
"""Download Neutron.""" """Download Neutron."""
archive = download_to_temp('neutron.zip', SOURCES['Neutron']) archive = download_to_temp('neutron.zip', SOURCES['Neutron'])
@ -295,13 +295,6 @@ def download_openshell() -> None:
download_file(out_path, SOURCES[name[:-4]]) download_file(out_path, SOURCES[name[:-4]])
def download_prime95():
"""Download Prime95."""
archive = download_to_temp('prime95.zip', SOURCES['Prime95'])
extract_to_bin(archive, 'Prime95')
delete_from_temp('prime95.zip')
def download_putty() -> None: def download_putty() -> None:
"""Download PuTTY.""" """Download PuTTY."""
archive = download_to_temp('putty.zip', SOURCES['PuTTY']) archive = download_to_temp('putty.zip', SOURCES['PuTTY'])
@ -309,12 +302,6 @@ def download_putty() -> None:
delete_from_temp('putty.zip') delete_from_temp('putty.zip')
def download_shutup10():
"""Download O&O ShutUp10."""
out_path = BIN_DIR.joinpath('ShutUp10/OOSU10.exe')
download_file(out_path, SOURCES['ShutUp10'])
def download_snappy_driver_installer_origin() -> None: def download_snappy_driver_installer_origin() -> None:
"""Download Snappy Driver Installer Origin.""" """Download Snappy Driver Installer Origin."""
archive = download_to_temp('aria2.zip', SOURCES['Aria2']) archive = download_to_temp('aria2.zip', SOURCES['Aria2'])
@ -385,29 +372,6 @@ def download_snappy_driver_installer_origin() -> None:
delete_from_temp('fake.7z') delete_from_temp('fake.7z')
def download_windows_repair_aio():
"""Download Windows Repair AIO."""
archive = download_to_temp('winrepairaio.zip', SOURCES['Windows Repair AIO'])
out_path = BIN_DIR.joinpath('WinRepairAIO')
tmp_path = TMP_DIR.joinpath('WinRepairAIO')
extract_archive(archive, tmp_path)
for item in tmp_path.joinpath('Tweaking.com - Windows Repair').iterdir():
try:
rename_item(item, out_path.joinpath(item.name))
except FileExistsError:
# Ignore and use our defaults
pass
delete_from_temp('WinRepairAIO')
delete_from_temp('winrepairaio.zip')
def download_winscp():
"""Download WinSCP."""
archive = download_to_temp('winscp.zip', SOURCES['WinSCP'])
extract_to_bin(archive, 'WinSCP')
delete_from_temp('winscp.zip')
def download_wiztree() -> None: def download_wiztree() -> None:
"""Download WizTree.""" """Download WizTree."""
archive = download_to_temp('wiztree.zip', SOURCES['WizTree']) archive = download_to_temp('wiztree.zip', SOURCES['WizTree'])
@ -443,6 +407,57 @@ def download_xmplay() -> None:
delete_from_temp('xmp-rar.zip') delete_from_temp('xmp-rar.zip')
delete_from_temp('Innocuous.zip') delete_from_temp('Innocuous.zip')
def download_xmplay_music() -> None:
"""Download XMPlay Music."""
music_tmp = TMP_DIR.joinpath('music')
music_tmp.mkdir(exist_ok=True)
current_dir = os.getcwd()
os.chdir(music_tmp)
url_mod = 'https://api.modarchive.org/downloads.php'
url_rsn = 'http://snesmusic.org/v2/download.php'
# Download music
for song_id, song_name in MUSIC_MOD:
download_file(
music_tmp.joinpath(f'MOD/{song_name}'),
f'{url_mod}?moduleid={song_id}#{song_name}',
)
for game in MUSIC_SNES:
download_file(
music_tmp.joinpath(f'SNES/{game}.rsn'),
f'{url_rsn}?spcNow={game}',
)
# Extract SNES archives
for item in music_tmp.joinpath('SNES').iterdir():
cmd = [
SEVEN_ZIP,
'x', item, f'-oSNES\\{item.stem}',
'-bso0', '-bse0', '-bsp0',
]
run_program(cmd)
delete_item(item)
# Remove bad songs
for game, globs in MUSIC_SNES_BAD.items():
for glob in globs:
for item in music_tmp.joinpath(f'SNES/{game}').glob(glob):
delete_item(item)
# Compress music
cmd = [
SEVEN_ZIP,
'a', '-t7z', '-mx=9',
'-bso0', '-bse0', '-bsp0',
BIN_DIR.joinpath('XMPlay/music.7z'),
'MOD', 'SNES',
]
run_program(cmd)
os.chdir(current_dir)
# Cleanup
delete_from_temp('music')
# "Main" Function # "Main" Function
def build_kit() -> None: def build_kit() -> None:
@ -467,27 +482,23 @@ def build_kit() -> None:
try_print.run('Autoruns...', download_autoruns) try_print.run('Autoruns...', download_autoruns)
try_print.run('BleachBit...', download_bleachbit) try_print.run('BleachBit...', download_bleachbit)
try_print.run('BlueScreenView...', download_bluescreenview) try_print.run('BlueScreenView...', download_bluescreenview)
try_print.run('CoreTemp...', download_coretemp)
try_print.run('ERUNT...', download_erunt) try_print.run('ERUNT...', download_erunt)
try_print.run('BulkCrapUninstaller...', download_bcuninstaller) try_print.run('BulkCrapUninstaller...', download_bcuninstaller)
try_print.run('DDU...', download_ddu) try_print.run('DDU...', download_ddu)
try_print.run('Everything...', download_everything) try_print.run('Everything...', download_everything)
try_print.run('FastCopy...', download_fastcopy) try_print.run('FastCopy...', download_fastcopy)
try_print.run('FurMark...', download_furmark) try_print.run('FurMark...', download_furmark)
try_print.run('HWiNFO...', download_hwinfo)
try_print.run('LibreOffice...', download_libreoffice) try_print.run('LibreOffice...', download_libreoffice)
try_print.run('Linux Reader...', download_linux_reader)
try_print.run('Macs Fan Control...', download_macs_fan_control) try_print.run('Macs Fan Control...', download_macs_fan_control)
try_print.run('Neutron...', download_neutron) try_print.run('Neutron...', download_neutron)
try_print.run('Notepad++...', download_notepad_plus_plus) try_print.run('Notepad++...', download_notepad_plus_plus)
try_print.run('O&O ShutUp10...', download_shutup10)
try_print.run('OpenShell...', download_openshell) try_print.run('OpenShell...', download_openshell)
try_print.run('Prime95...', download_prime95)
try_print.run('PuTTY...', download_putty) try_print.run('PuTTY...', download_putty)
try_print.run('WinSCP...', download_winscp) try_print.run('Snappy Driver Installer...', download_snappy_driver_installer_origin)
try_print.run('Windows Repair AIO...', download_windows_repair_aio)
try_print.run('WizTree...', download_wiztree) try_print.run('WizTree...', download_wiztree)
try_print.run('XMPlay...', download_xmplay) try_print.run('XMPlay...', download_xmplay)
try_print.run('Snappy Driver Installer...', download_snappy_driver_installer_origin) try_print.run('XMPlay Music...', download_xmplay_music)
# Pause # Pause
print('', flush=True) print('', flush=True)

View file

@ -6,18 +6,14 @@ import pathlib
import platform import platform
from datetime import datetime, timedelta from datetime import datetime, timedelta
from subprocess import CalledProcessError, CompletedProcess, Popen from subprocess import CompletedProcess, Popen
import requests import requests
from wk.cfg.main import ARCHIVE_PASSWORD from wk.cfg.main import ARCHIVE_PASSWORD
from wk.cfg.net import SDIO_SERVER
from wk.cfg.sources import DOWNLOAD_FREQUENCY, SOURCES from wk.cfg.sources import DOWNLOAD_FREQUENCY, SOURCES
from wk.exe import popen_program, run_program from wk.exe import popen_program, run_program
from wk.io import get_path_obj
from wk.net import mount_network_share
from wk.std import GenericError, sleep from wk.std import GenericError, sleep
from wk.ui.cli import abort, ask, print_error
# STATIC VARIABLES # STATIC VARIABLES
@ -29,14 +25,6 @@ HEADERS = {
'Gecko/20100101 Firefox/97.0' 'Gecko/20100101 Firefox/97.0'
), ),
} }
MOUNT_EXCEPTIONS = (RuntimeError, CalledProcessError)
SDIO_REMOTE_PATH = get_path_obj(
(
fr'\\{SDIO_SERVER["Address"]}\{SDIO_SERVER["Share"]}\{SDIO_SERVER["Path"]}'
fr'\SDIO{"64" if ARCH == "64" else ""}.exe'
),
resolve=False,
)
# "GLOBAL" VARIABLES # "GLOBAL" VARIABLES
@ -215,52 +203,6 @@ def get_tool_path(folder, name, check=True, suffix=None) -> pathlib.Path:
return tool_path return tool_path
def get_sdio_path(interactive: bool) -> pathlib.Path:
"""Try to mount SDIO server."""
use_network = False
sdio_path = get_tool_path("SDIO", "SDIO")
def _mount_server() -> CompletedProcess:
if interactive:
print('Connecting to server... (Press CTRL+c to use local copy)')
return mount_network_share(SDIO_SERVER, read_write=False)
def _try_again(error_msg: str) -> bool:
"""Ask to try again or quit."""
print_error(error_msg)
if ask(' Try again?'):
return True
if not ask(' Use local version?'):
abort()
return False
# Bail early
if not SDIO_SERVER['Address']:
return sdio_path
# Main loop
while True:
try:
proc = _mount_server()
except KeyboardInterrupt:
break
except MOUNT_EXCEPTIONS as err:
if not (interactive and _try_again(f' ERROR: {err}')):
break
else:
if proc.returncode == 0:
# Network copy available
use_network = True
break
# Failed to mount
if not (interactive and _try_again(' Failed to mount server')):
break
# Done
if use_network:
sdio_path = SDIO_REMOTE_PATH
return sdio_path
def run_tool( def run_tool(
folder, name, *run_args, folder, name, *run_args,
cbin=False, cwd=False, download=False, popen=False, cbin=False, cwd=False, download=False, popen=False,

View file

@ -1,6 +1,7 @@
"""WizardKit: UFD Functions""" """WizardKit: UFD Functions"""
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import argparse
import logging import logging
import math import math
import os import os
@ -9,8 +10,6 @@ import re
import shutil import shutil
from subprocess import CalledProcessError from subprocess import CalledProcessError
from docopt import docopt
from wk import io, log from wk import io, log
from wk.cfg.main import KIT_NAME_FULL, KIT_NAME_SHORT from wk.cfg.main import KIT_NAME_FULL, KIT_NAME_SHORT
from wk.cfg.ufd import ( from wk.cfg.ufd import (
@ -29,32 +28,6 @@ from wk.ui import cli as ui
# STATIC VARIABLES # STATIC VARIABLES
DOCSTRING = '''WizardKit: Build UFD
Usage:
build-ufd [options] --ufd-device PATH
[--linux PATH]
[--main-kit PATH]
[--winpe PATH]
[--eset PATH]
[--extra-dir PATH]
[EXTRA_IMAGES...]
build-ufd (-h | --help)
Options:
-e PATH, --extra-dir PATH
-k PATH, --main-kit PATH
-l PATH, --linux PATH
-s PATH, --eset PATH
-u PATH, --ufd-device PATH
-w PATH, --winpe PATH
-d --debug Enable debug mode
-h --help Show this page
-M --use-mbr Use real MBR instead of GPT w/ Protective MBR
-F --force Bypass all confirmation messages. USE WITH EXTREME CAUTION!
-U --update Don't format device, just update
'''
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
EXTRA_IMAGES_LIST = '/mnt/UFD/arch/extra_images.list' EXTRA_IMAGES_LIST = '/mnt/UFD/arch/extra_images.list'
MIB = 1024 ** 2 MIB = 1024 ** 2
@ -63,6 +36,54 @@ UFD_LABEL = f'{KIT_NAME_SHORT}_UFD'
# Functions # 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='build-ufd',
description=f'{KIT_NAME_FULL}: Build UFD',
)
parser.add_argument('-u', '--ufd-device', required=True)
parser.add_argument('-l', '--linux', required=False)
parser.add_argument('-e', '--extra-dir', required=False)
parser.add_argument('-k', '--main-kit', required=False)
parser.add_argument('-w', '--winpe', required=False)
parser.add_argument(
'-d', '--debug', action='store_true',
help='Enable debug mode',
)
parser.add_argument(
'-M', '--use-mbr', action='store_true',
help='Use real MBR instead of GPT w/ Protective MBR',
)
parser.add_argument(
'-F', '--force', action='store_true',
help='Bypass all confirmation messages. USE WITH EXTREME CAUTION!',
)
parser.add_argument(
'-U', '--update', action='store_true',
help="Don't format device, just update",
)
parser.add_argument(
'EXTRA_IMAGES', nargs='*',
)
args = parser.parse_args()
legacy_args = {
'--debug': args.debug,
'--extra-dir': args.extra_dir,
'--force': args.force,
'--linux': args.linux,
'--main-kit': args.main_kit,
'--ufd-device': args.ufd_device,
'--update': args.update,
'--use-mbr': args.use_mbr,
'--winpe': args.winpe,
'EXTRA_IMAGES': args.EXTRA_IMAGES,
}
return legacy_args
def apply_image(part_path, image_path, hide_macos_boot=True) -> None: def apply_image(part_path, image_path, hide_macos_boot=True) -> None:
"""Apply raw image to dev_path using dd.""" """Apply raw image to dev_path using dd."""
cmd = [ cmd = [
@ -95,7 +116,12 @@ def apply_image(part_path, image_path, hide_macos_boot=True) -> None:
def build_ufd() -> None: def build_ufd() -> None:
"""Build UFD using selected sources.""" """Build UFD using selected sources."""
args = docopt(DOCSTRING) try:
args = argparse_helper()
except SystemExit:
print('')
ui.pause('Press Enter to exit...')
raise
if args['--debug']: if args['--debug']:
log.enable_debug_mode() log.enable_debug_mode()
if args['--update'] and args['EXTRA_IMAGES']: if args['--update'] and args['EXTRA_IMAGES']:
@ -149,6 +175,13 @@ def build_ufd() -> None:
dev_path=ufd_dev, dev_path=ufd_dev,
label=UFD_LABEL, label=UFD_LABEL,
) )
try_print.run(
message='Hiding extra partition(s)...',
function=hide_extra_partitions,
dev_path=ufd_dev,
num_parts=len(extra_images),
use_mbr=args['--use-mbr'],
)
ufd_dev_first_partition = find_first_partition(ufd_dev) ufd_dev_first_partition = find_first_partition(ufd_dev)
# Mount UFD # Mount UFD
@ -352,7 +385,7 @@ def create_table(dev_path, use_mbr=False, images=None) -> None:
for part, real in zip(part_sizes, images): for part, real in zip(part_sizes, images):
end = start + real end = start + real
cmd.append( cmd.append(
f'mkpart primary {"fat32" if start==MIB else "hfs+"} {start}B {end-1}B', f'mkpart primary "fat32" {start}B {end-1}B',
) )
start += part start += part
@ -438,6 +471,17 @@ def hide_items(ufd_dev_first_partition, items) -> None:
run_program(cmd, shell=True, check=False) run_program(cmd, shell=True, check=False)
def hide_extra_partitions(dev_path, num_parts, use_mbr) -> None:
if use_mbr:
# Bail early
return
for part_id in range(num_parts):
part_id += 2 # Extra partitions start at 2
cmd = ['sfdisk', '--part-attrs', dev_path, str(part_id), 'RequiredPartition,62,63']
run_program(cmd, check=False)
def install_syslinux_to_dev(ufd_dev, use_mbr) -> None: def install_syslinux_to_dev(ufd_dev, use_mbr) -> None:
"""Install Syslinux to UFD (dev).""" """Install Syslinux to UFD (dev)."""
cmd = [ cmd = [
@ -571,10 +615,7 @@ def update_boot_entries(ufd_dev, images=None) -> None:
'sed', 'sed',
'--in-place', '--in-place',
'--regexp-extended', '--regexp-extended',
( f's/___+/{uuids[0]}/',
f's/___+/{uuids[0]}/; '
f's#by-label/(eSysRescueLiveCD|{ISO_LABEL}|{UFD_LABEL})#by-uuid/{uuids[0]}#'
),
*configs, *configs,
] ]
run_program(cmd) run_program(cmd)

View file

@ -21,7 +21,7 @@ if os.name == 'nt':
) )
else: else:
# Example: "/home/tech/Logs" # Example: "/home/tech/Logs"
DEFAULT_LOG_DIR = pathlib.Path('~/Logs').expanduser().resolve() DEFAULT_LOG_DIR = f'{os.path.expanduser("~")}/Logs'
DEFAULT_LOG_NAME = cfg.main.KIT_NAME_FULL DEFAULT_LOG_NAME = cfg.main.KIT_NAME_FULL

View file

@ -88,15 +88,11 @@ def mount_backup_shares(read_write: bool = False) -> list[str]:
continue continue
# Mount share # Mount share
try: proc = mount_network_share(details, mount_point, read_write=read_write)
proc = mount_network_share(details, mount_point, read_write=read_write) if proc.returncode:
if proc.returncode:
report.append(f'Failed to Mount {mount_str}')
else:
report.append(f'Mounted {mount_str}')
except RuntimeError:
# Assuming we're not connected to a network?
report.append(f'Failed to Mount {mount_str}') report.append(f'Failed to Mount {mount_str}')
else:
report.append(f'Mounted {mount_str}')
# Done # Done
return report return report

View file

@ -2,13 +2,9 @@
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import logging import logging
import plistlib
import re import re
from wk import std
from wk.exe import run_program from wk.exe import run_program
from wk.hw.disk import Disk
from wk.ui import cli
# STATIC VARIABLES # STATIC VARIABLES
@ -36,189 +32,6 @@ def decode_smc_bytes(text) -> int:
return result return result
def get_apfs_volumes(device_path):
"""Get APFS volumes contained in device_path, returns list."""
volumes = []
containers = []
# Get APFS details
cmd = ['diskutil', 'apfs', 'list', '-plist']
proc = 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
LOG.error('Failed to get diskutil apfs list for %s', device_path)
# Find container(s) relating to device_path
for container in plist_data['Containers']:
if container['DesignatedPhysicalStore'] == device_path:
containers.append(container)
# Add volumes
for container in containers:
for volume in container['Volumes']:
mount_volume(volume['DeviceIdentifier'])
volumes.append(Disk(f'/dev/{volume["DeviceIdentifier"]}'))
# Done
return volumes
def get_core_storage_volumes(device_path):
"""Get CoreStorage volumes contained in device_path, returns list."""
disks = []
volumes = []
# Get coreStorage details
cmd = ['diskutil', 'corestorage', 'list', '-plist']
proc = 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
LOG.error('failed to get diskutil corestorage list for %s', device_path)
# Find related virtual disks
for l_vg in plist_data['CoreStorageLogicalVolumeGroups']:
related = False
# Compare parent physical volumes against device_path
for p_v in l_vg['CoreStoragePhysicalVolumes']:
uuid = p_v['CoreStorageUUID']
cmd = ['diskutil', 'coreStorage', 'info', '-plist', uuid]
proc = run_program(cmd, check=False, encoding=None, errors=None)
try:
plist_data = plistlib.loads(proc.stdout)
except (TypeError, ValueError):
LOG.error('failed to get diskutil corestorage info for %s', uuid)
continue
if plist_data['DeviceIdentifier'] == device_path:
related = True
break
# Move on if no related p_v was found
if not related:
continue
# Add logical disks to list
for l_vf in l_vg['CoreStorageLogicalVolumeFamilies']:
for l_v in l_vf['CoreStorageLogicalVolumes']:
uuid = l_v['CoreStorageUUID']
cmd = ['diskutil', 'coreStorage', 'info', '-plist', uuid]
proc = run_program(cmd, check=False, encoding=None, errors=None)
try:
plist_data = plistlib.loads(proc.stdout)
except (TypeError, ValueError):
LOG.error('failed to get diskutil corestorage info for %s', uuid)
continue
disks.append(plist_data['DeviceIdentifier'])
# Get volumes from logical disks
for disk in disks:
cmd = ['diskutil', 'list', '-plist', f'/dev/{disk}']
proc = run_program(cmd, check=False, encoding=None, errors=None)
try:
plist_data = plistlib.loads(proc.stdout)
disk_data = plist_data['AllDisksAndPartitions'][0]
except (TypeError, ValueError):
LOG.error('Failed to get diskutil list for %s', disk)
continue
if 'Partitions' in disk_data:
for part in disk_data['Partitions']:
mount_volume(part['DeviceIdentifier'])
volumes.append(Disk(f'/dev/{part["DeviceIdentifier"]}'))
elif 'VolumeName' in disk_data:
mount_volume(disk_data['DeviceIdentifier'])
volumes.append(Disk(f'/dev/{disk_data["DeviceIdentifier"]}'))
# Done
return volumes
def mount_volume(volume_path, read_only=True):
"""Try to mount a volume, returns subprocess.CompletedProcess."""
cmd = ['sudo', 'diskutil', 'mount']
if read_only:
cmd.append('readOnly')
cmd.append(volume_path)
proc = run_program(cmd, check=False)
# Done
return proc
def mount_disk(device_path=None):
"""Mount all detected volumes, returns list.
NOTE: If device_path is specified then only volumes
under that path will be mounted.
"""
report = []
volumes = []
# Get device details
cmd = [
'diskutil',
'list',
'-plist',
]
if device_path:
cmd.append(device_path)
proc = 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
LOG.error('Failed to get diskutil list for %s', device_path)
return report
# Mount and add volumes
for part in plist_data['AllDisksAndPartitions'][0]['Partitions']:
proc = mount_volume(part['DeviceIdentifier'])
# Add volume
volumes.append(Disk(f'/dev/{part["DeviceIdentifier"]}'))
# Add sub-volumes
if proc.returncode:
if part['Content'] == 'Apple_CoreStorage':
volumes.extend(get_core_storage_volumes(part['DeviceIdentifier']))
elif part['Content'] == 'Apple_APFS':
volumes.extend(get_apfs_volumes(part['DeviceIdentifier']))
# Build report from volume list
for vol in volumes:
result = f'{vol.details["name"]:<20}'
# Containers
if vol.details['Content'] in ('Apple_APFS', 'Apple_CoreStorage'):
result += f'{vol.details["Content"].replace("Apple_", "")} container'
report.append(cli.color_string(result, 'BLUE'))
continue
# Unknown partitions
if not vol.details['mountpoint']:
result += 'Failed to mount'
report.append(cli.color_string(result, 'RED'))
continue
# Volumes
result += f'{"Mounted on "+vol.details.get("mountpoint", "?"):<40}'
size = vol.details.get('TotalSize', -1)
free = vol.details.get('FreeSpace', -1)
used = size - free
result = (
f'{result} ({vol.details.get("fstype", "Unknown FS")+",":<5} '
f'{std.bytes_to_string(used, decimals=1):>9} used, '
f'{std.bytes_to_string(free, decimals=1):>9} free) '
)
report.append(result)
# Done
return report
def set_fans(mode) -> None: def set_fans(mode) -> None:
"""Set fans to auto or max.""" """Set fans to auto or max."""
if mode == 'auto': if mode == 'auto':

View file

@ -6,7 +6,6 @@ import logging
import os import os
import pathlib import pathlib
import platform import platform
import re
from contextlib import suppress from contextlib import suppress
from typing import Any from typing import Any
@ -28,7 +27,6 @@ from wk.cfg.windows_builds import (
) )
from wk.exe import get_json_from_command, run_program, wait_for_procs from wk.exe import get_json_from_command, run_program, wait_for_procs
from wk.kit.tools import find_kit_dir from wk.kit.tools import find_kit_dir
from wk.osticket import osTicket
from wk.std import ( from wk.std import (
GenericError, GenericError,
GenericWarning, GenericWarning,
@ -75,9 +73,6 @@ KNOWN_HIVE_NAMES = {
RAM_OK = 5.5 * 1024**3 # ~6 GiB assuming a bit of shared memory RAM_OK = 5.5 * 1024**3 # ~6 GiB assuming a bit of shared memory
RAM_WARNING = 3.5 * 1024**3 # ~4 GiB assuming a bit of shared memory RAM_WARNING = 3.5 * 1024**3 # ~4 GiB assuming a bit of shared memory
REG_MSISERVER = r'HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot\Network\MSIServer' REG_MSISERVER = r'HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot\Network\MSIServer'
REGEX_4K_ALIGNMENT = re.compile(
r'^(?P<description>.*?)\s+(?P<size>\d+)\s+(?P<offset>\d+)',
)
SLMGR = pathlib.Path(f'{os.environ.get("SYSTEMROOT")}/System32/slmgr.vbs') SLMGR = pathlib.Path(f'{os.environ.get("SYSTEMROOT")}/System32/slmgr.vbs')
SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE') SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE')
@ -170,29 +165,23 @@ def set_timezone(zone) -> None:
# Info Functions # Info Functions
def check_4k_alignment(show_alert=False) -> list[str]: def check_4k_alignment(show_alert=False) -> list[str]:
"""Check if all partitions are 4K aligned, returns list.""" """Check if all partitions are 4K aligned, returns list."""
cmd = ['WMIC', 'partition', 'get', 'Caption,Size,StartingOffset'] script_path = find_kit_dir('Scripts').joinpath('check_partition_alignment.ps1')
cmd = ['PowerShell', '-ExecutionPolicy', 'Bypass', '-File', script_path]
json_data = get_json_from_command(cmd)
report = [] report = []
show_alert = False show_alert = False
# Check offsets # Check offsets
proc = run_program(cmd) for part in json_data:
for line in proc.stdout.splitlines(): if part['StartingOffset'] % 4096 != 0:
line = line.strip() report.append(
if not line or not line.startswith('Disk'): ansi.color_string(
continue f'{part["Name"]}'
match = REGEX_4K_ALIGNMENT.match(line) f' ({bytes_to_string(part["Size"], decimals=1)})'
if not match: ,
LOG.error('Failed to parse partition info for: %s', line) 'RED'
continue )
if int(match.group('offset')) % 4096 != 0: )
report.append(
ansi.color_string(
f'{match.group("description")}'
f' ({bytes_to_string(match.group("size"), decimals=1)})'
,
'RED'
)
)
# Show alert # Show alert
if show_alert: if show_alert:
@ -204,109 +193,74 @@ def check_4k_alignment(show_alert=False) -> list[str]:
0, 0,
ansi.color_string('One or more partitions not 4K aligned', 'YELLOW'), ansi.color_string('One or more partitions not 4K aligned', 'YELLOW'),
) )
report.sort()
return report return report
def defender_is_disabled():
"""Check if Windows Defender is enabled, returns bool."""
reg_key = r'Software\Microsoft\Windows Defender'
disabled = reg_read_value('HKLM', reg_key, 'DisableAntiSpyware')
disabled = disabled or reg_read_value('HKLM', reg_key, 'DisableAntiVirus')
return bool(disabled)
def export_bitlocker_info() -> None: def export_bitlocker_info() -> None:
"""Get Bitlocker info and save to either the base directory of the kit or osTicket.""" """Get Bitlocker info and save to the base directory of the kit."""
script_path = find_kit_dir('Scripts').joinpath('export_bitlocker.ps1') commands = [
cmd = ['PowerShell', '-ExecutionPolicy', 'Bypass', '-File', script_path] ['manage-bde', '-status', SYSTEMDRIVE],
file_name = '' ['manage-bde', '-protectors', '-get', SYSTEMDRIVE],
output = []
# Include OS info
if os.environ.get('SYSTEMDRIVE', 'C:').upper() == 'X:':
# Assuming this is true
output.append('[Check run under Win10XPE]')
else:
output.append(get_os_name())
output.append('')
# Get Bitlocker info
proc = run_program(cmd, check=False)
if proc.returncode or not proc.stdout.strip():
output.append('Error: Failed to export Bitlocker info\n')
output.extend(proc.stdout.splitlines())
# Show info
print('\n'.join(output), '\n\n')
# Save to osTicket
ost = osTicket()
ost.init()
ost.select_ticket()
if not ost.disabled:
ost.post_response('\n'.join(output))
result = 'OK'
if ost.disabled or ost.errors:
result = 'Unknown'
print(
ansi.color_string(
['\nPost info to osTicket... ', result],
[None, 'GREEN' if result == 'OK' else 'YELLOW'],
)
)
# Save to file
if ost.ticket_name:
file_name = f'{ost.ticket_id}_{ost.ticket_name.replace(" ", "-")}'
if not file_name:
file_name = ui.input_text(prompt_msg='Enter filename: ', allow_empty=False)
file_path = pathlib.Path(f'../../../Bitlocker_{file_name}.txt').resolve()
with open(file_path, 'a', encoding='utf-8') as _f:
_f.write('\n'.join(output))
# Done
ui.pause('\nPress Enter to exit...')
def get_installed_antivirus() -> list[str]:
"""Get list of installed antivirus programs, returns list."""
cmd = [
'WMIC', r'/namespace:\\root\SecurityCenter2',
'path', 'AntivirusProduct',
'get', 'displayName', '/value',
] ]
products = []
report = []
# Get list of products # Get filename
proc = run_program(cmd) file_name = ui.input_text(prompt_msg='Enter filename')
for line in proc.stdout.splitlines(): file_path = pathlib.Path(f'../../Bitlocker_{file_name}.txt').resolve()
line = line.strip()
if '=' in line: # Save info
products.append(line.split('=')[1]) with open(file_path, 'a', encoding='utf-8') as _f:
for cmd in commands:
proc = run_program(cmd, check=False)
_f.write(f'{proc.stdout}\n\n')
def get_installed_antivirus() -> dict[str, dict]:
"""Get installed antivirus products and their status, returns dict."""
script_path = find_kit_dir('Scripts').joinpath('check_av.ps1')
cmd = ['PowerShell', '-ExecutionPolicy', 'Bypass', '-File', script_path]
json_data = get_json_from_command(cmd)
products = {}
# Check state and build dict
for p in json_data:
name = p['displayName']
state = p['productState']
enabled = ((state>>8) & 0x11) in (0x10, 0x11) # middle two hex digits
outdated = (state & 0x11) != 0x00 # last two hex digits
products[name] = {
'Enabled': enabled,
'Outdated': outdated,
'State': state,
}
return products
def list_installed_antivirus() -> list[str]:
"""Get list of installed antivirus programs, returns list."""
products = get_installed_antivirus()
products_active = []
products_inactive = []
# Check product(s) status # Check product(s) status
for product in sorted(products): for name, details in products.items():
cmd = [ if details['Enabled']:
'WMIC', r'/namespace:\\root\SecurityCenter2', if details['Outdated']:
'path', 'AntivirusProduct', products_active.append(ansi.color_string(f'{name} [OUTDATED]', 'YELLOW'))
'where', f'displayName="{product}"', else:
'get', 'productState', '/value', products_active.append(name)
]
proc = run_program(cmd)
state = proc.stdout.split('=')[1]
state = hex(int(state))
if str(state)[3:5] not in ['10', '11']:
report.append(ansi.color_string(f'[Disabled] {product}', 'YELLOW'))
else: else:
report.append(product) # Disabled
products_inactive.append(ansi.color_string(f'[Disabled] {name}', 'YELLOW'))
# Final check # Final check
if not report: if not (products_active or products_inactive):
report.append(ansi.color_string('No products detected', 'RED')) products_inactive.append(ansi.color_string('No products detected', 'RED'))
# Done # Done
return report products_active.sort()
products_inactive.sort()
return products_active + products_inactive
def get_installed_ram(as_list=False, raise_exceptions=False) -> list | str: def get_installed_ram(as_list=False, raise_exceptions=False) -> list | str:

View file

@ -1,431 +0,0 @@
"""WizardKit: osTicket Functions"""
# vim: sts=2 sw=2 ts=2
import atexit
import logging
import pathlib
import time
import mariadb
from wk.cfg.hw import TESTSTATION_FILE
from wk.cfg.osticket import SQL, STAFF
from wk.ui import ansi, cli
# STATIC_VARIABLES
LOG = logging.getLogger(__name__)
FLAG_CODES = {
'Pass': 1,
'Fail': 2,
}
FLAG_CPU = 'zTemps'
FLAG_DISK = 'zHDTune'
FLAG_MAX_TEMP = 'zMaxTemp'
RESPONSE_COLOR_CODES = {
'Normal': '0',
'Contact': '1',
'Diags': '2',
'Diags FAIL': '3',
'Money': '4',
}
TABLE_RESPONSE = 'ost_ticket_response'
TABLE_TICKET = 'ost_ticket'
# Classes
class osTicket():
"""Class to track osTicket data and functions."""
def __init__(self):
self.db_connection = None
self.db_cursor = None
self.disabled: bool = False
self.errors: bool = False
self.note: str = ''
self.ticket_id: int | None = None
self.ticket_name: str | None = None
self.ticket_subject: str | None = None
# Ensure connection is closed atexit
atexit.register(self._disconnect)
def _connect(self, silent=True):
"""Establish connection to osTicket."""
if self.disabled:
return
# Connect to database
try:
self.db_connection = mariadb.connect(
host=SQL['Host'],
port=SQL['Port'],
database=SQL['DB'],
user=SQL['User'],
password=SQL['Pass'],
connect_timeout=5,
)
self.db_cursor = self.db_connection.cursor()
except mariadb.Error:
# Assuming network issue or bad creds
pass
# Raise exception if necessary
if self.db_cursor is None:
LOG.error('Failed to connect to osTicket database')
if silent:
# Don't raise exceptions, just disable ost
self.disabled = True
self.errors = True
else:
raise RuntimeError('Failed to connect to osTicket database')
def _disconnect(self):
"""Close osTicket connection."""
for db_obj in (self.db_cursor, self.db_connection):
if db_obj:
try:
db_obj.close()
except mariadb.Error:
# Ignore errors since vars will be reset below
pass
# Reset db objects
self.db_cursor = None
self.db_connection = None
def _get_flag(self, flag_name):
"""Get flag for self.ticket_id from osTicket, returns str."""
flag_value = None
self._verify_ticket_id()
# Build SQL cmd
sql_cmd = (
f"SELECT `{flag_name}` FROM `{SQL['DB']}`.`{TABLE_TICKET}` "
f"WHERE `{TABLE_TICKET}`.`ticket_id` = {self.ticket_id};"
)
# Run SQL cmd
try:
self.db_cursor.execute(sql_cmd)
for s in self.db_cursor:
flag_value = s[0]
except mariadb.Error as err_msg:
cli.print_error(err_msg)
self.errors = True
# Done
return str(flag_value)
def _get_ticket_field(self, ticket_id, field_name):
"""Get field for ticket_id from osTicket, returns str."""
field_data = None
# Build SQL cmd
sql_cmd = (
f"SELECT {field_name} FROM `{SQL['DB']}`.`{TABLE_TICKET}` "
f"WHERE `{TABLE_TICKET}`.`ticket_id` = {ticket_id};"
)
# Lookup data
# NOTE: If multiple entries are found it will return the last
try:
self.db_cursor.execute(sql_cmd)
for result in self.db_cursor:
field_data = result[0]
except mariadb.Error as err_msg:
# Show error and return None
cli.print_error(err_msg)
# Done
return field_data
def _set_flag(self, flag_name, flag_value):
"""Set flag_name to flag_value for ticket_id in osTicket.
NOTE: This will overwrite any existing value.
"""
self._verify_ticket_id()
sql_cmd = (
f"UPDATE `{SQL['DB']}`.`{TABLE_TICKET}` "
f"SET `{flag_name}` = '{flag_value}' "
f"WHERE `{TABLE_TICKET}`.`ticket_id` = {self.ticket_id};"
)
# Run SQL cmd
try:
self.db_cursor.execute(sql_cmd)
except mariadb.Error as err_msg:
cli.print_error(err_msg)
self.errors = True
def _verify_ticket_id(self):
"""Verify that ticket_id has been set."""
if not self.ticket_id:
LOG.error('Ticket ID not set')
raise RuntimeError('Ticket ID not set')
def add_note(self, prompt: str = 'Add note') -> list[str]:
"""Add note to be included in osTicket replies."""
lines = []
if not prompt:
prompt = 'Please enter any additional information for this ticket'
# Instructions
cli.print_standard(prompt)
cli.print_info(' (End note with an empty line)')
cli.print_standard(' ')
# Get note
while True:
text = cli.input_text('> ', allow_empty=True)
if not text:
break
lines.append(text.strip())
# Save note
if lines:
self.note = lines[0]
for line in lines[1:]:
self.note += f'\n...{line}'
else:
self.note = ''
# Done
return lines
def init(self):
"""Revert to defaults."""
self._disconnect()
self.disabled = False
self.errors = False
self.note = None
self.ticket_id = None
self.ticket_name = None
def post_response(self, response, color='Normal'):
"""Post a reply to a ticket in osTicket."""
cur_date_time = time.strftime('%Y-%m-%d %H:%M:%S')
lines = []
test_station = get_test_station_name()
self._connect(silent=True)
self._verify_ticket_id()
# Bail if disabled
if self.disabled:
return
# Format response
if test_station:
lines.append(f'[Test-Station: {test_station}]')
lines.append(f'[Report for ticket #{self.ticket_id} {self.ticket_name}]')
if self.note:
lines.append(f'[Note] {self.note}\n')
lines.append(str(response))
response = '\n'.join(lines)
response = ansi.strip_colors(response)
response = response.replace("`", "'").replace("'", "\\'")
# Build SQL cmd
sql_cmd = (
f"INSERT INTO `{SQL['DB']}`.`{TABLE_RESPONSE}` "
f"(ticket_id, staff_id, staff_name, response, created, code) "
f"VALUES ("
f" '{self.ticket_id}',"
f" '{STAFF['ID']}',"
f" '{STAFF['Name']}',"
f" '{response}',"
f" '{cur_date_time}',"
f" '{RESPONSE_COLOR_CODES.get(color, 'Normal')}'"
f");"
)
# Run SQL cmd
try:
self.db_cursor.execute(sql_cmd)
except mariadb.Error:
self.errors = True
# Update ticket last repsonse field
sql_cmd = (
f"UPDATE `{SQL['DB']}`.`{TABLE_TICKET}`"
f"SET `lastresponse` = '{cur_date_time}' "
f"WHERE `{TABLE_TICKET}`.`ticket_id` = {self.ticket_id};"
)
try:
self.db_cursor.execute(sql_cmd)
except mariadb.Error:
self.errors = True
# Done
self._disconnect()
def select_ticket(self):
"""Set ticket number and name from osTicket DB."""
cli.print_standard('Connecting to osTicket...')
# Bail if disabled
if self.disabled:
return
# Connect
while True:
try:
self._connect(silent=False)
except (mariadb.Error, RuntimeError):
cli.print_warning('Failed to connect to osTicket')
if not cli.ask('Try again?'):
cli.print_standard('Integration disabled for this session')
self.disabled = True
return
else:
# Connection successful
break
# Main loop
while self.ticket_id is None:
cli.print_standard(' ')
_id = cli.input_text(
'Enter ticket number (or leave blank to disable): ', allow_empty=True,
)
_id = _id.strip()
# Nothing entered
if not _id:
print(' ')
if cli.ask('Disable osTicket integration for this session?'):
self.disabled = True
break
# Invalid ID entered
if not _id.isnumeric():
continue
# Valid ID entered, lookup name
_name = self._get_ticket_field(_id, 'name')
# Invalid ticket selected
if _name is None:
cli.print_error(f'Ticket #{_id} not found')
continue
# Valid ticket selected, lookup subject
_subject = self._get_ticket_field(_id, 'subject')
# Verify selection
cli.print_colored(
['You have selected ticket', f'#{_id}', _name],
[None, 'BLUE', None],
)
cli.print_colored(f' {_subject}', 'CYAN')
cli.print_standard(' ')
if cli.ask('Is this correct?'):
self.ticket_id = _id
self.ticket_name = _name
self.ticket_subject = _subject
# Done
self._disconnect()
def set_cpu_max_temp(self, temp):
"""Set CPU max temp in osTicket for ticket_id.
NOTE: This will not replace a higher temp value.
"""
LOG.info('Setting max CPU temp to %s', temp)
self._connect(silent=True)
# Bail if disabled
if self.disabled:
return
# Compare to current temp
current_temp = self._get_flag(FLAG_MAX_TEMP)
if str(current_temp).isnumeric() and int(current_temp) > temp:
cli.print_warning('Not replacing higher temp in osTicket')
self._disconnect()
return
# Update temp
self._set_flag(FLAG_MAX_TEMP, int(temp))
# Done
self._disconnect()
def set_flag_failed(self, flag_name):
"""Set flag as failed in osTicket for ticket_id."""
LOG.warning('Setting osTicket %s checkbox to FAILED', flag_name)
real_flag_name = FLAG_CPU if flag_name == 'CPU' else FLAG_DISK
self._connect(silent=True)
# Bail if disabled
if self.disabled:
return
# Set flag
self._set_flag(real_flag_name, FLAG_CODES['Fail'])
# Done
self._disconnect()
def set_flag_passed(self, flag_name):
"""Set flag as passed in osTicket for ticket_id.
NOTE: This will not overwrite a failed status.
"""
real_flag_name = FLAG_CPU if flag_name == 'CPU' else FLAG_DISK
self._connect(silent=True)
# Bail if disabled
if self.disabled:
return
# Bail if flag checkbox set as FAILED
if self._get_flag(real_flag_name) == str(FLAG_CODES['Fail']):
cli.print_warning(
f'Not replacing osTicket {flag_name} checkbox FAILED value',
)
self._disconnect()
return
# Current value != to FAILED, updating checkbox
LOG.info('Setting osTicket %s checkbox to PASSED', flag_name)
self._set_flag(real_flag_name, FLAG_CODES['Pass'])
# Done
self._disconnect()
# Functions
def get_test_station_name():
"""Get test station name, returns str."""
hostname_file = pathlib.Path(TESTSTATION_FILE)
# Bail early
if not hostname_file.exists():
return ''
if not hostname_file.read_text(encoding='utf-8'):
return ''
# Done
return hostname_file.read_text(encoding='utf-8').splitlines()[0]
def pad_with_dots(text, pad_right=False):
"""Replace space padding with dots, returns str.
NOTE: This is a dumb hack to better align text in osTicket.
"""
text = str(text)
text = text.replace(' ', '..')
if '.' in text:
if pad_right:
text = f'{text}.'
else:
text = f'.{text}'
# Done
return text
if __name__ == '__main__':
print("This file is not meant to be called directly.")

View file

@ -30,7 +30,6 @@ from wk.exe import (
get_procs, get_procs,
run_program, run_program,
popen_program, popen_program,
set_proc_priority,
wait_for_procs, wait_for_procs,
) )
from wk.io import ( from wk.io import (
@ -40,22 +39,14 @@ from wk.io import (
non_clobber_path, non_clobber_path,
rename_item, rename_item,
) )
from wk.kit.tools import ( from wk.kit.tools import (download_tool, get_tool_path, run_tool)
download_tool,
extract_archive,
get_tool_path,
run_tool,
)
from wk.log import ( from wk.log import (
format_log_path, format_log_path,
get_root_logger_path, get_root_logger_path,
update_log_path, update_log_path,
) )
from wk.os.win import ( from wk.os.win import (
ARCH,
OS_VERSION, OS_VERSION,
defender_is_disabled,
show_alert_box,
get_timezone, get_timezone,
set_timezone, set_timezone,
reg_delete_value, reg_delete_value,
@ -94,16 +85,7 @@ PROGRAMFILES_32 = os.environ.get(
'PROGRAMFILES', r'C:\Program Files (x86)', 'PROGRAMFILES', r'C:\Program Files (x86)',
), ),
) )
MBAM_EXE_PATH = 'Malwarebytes/Anti-Malware/mbam.exe'
MBAM_PRESERVE_MARKER = 'Preserve-MBAM.marker'
MBAM_UNINSTALL_KEY = (
r'Software\Microsoft\Windows\CurrentVersion\Uninstall'
r'\{35065F43-4BB2-439A-BFF7-0F1014F2E0CD}_is1'
)
MS_ANTIVIRUS_ENABLED = 0x1000
PROGRAMDATA = os.environ.get('{ALLUSERSPROFILE}', r'C:\ProgramData')
SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE', 'C:') SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE', 'C:')
EMSISOFT_INSTALL_PATH = get_path_obj(f'{SYSTEMDRIVE}/EmsisoftCmd')
WHITELIST = '\n'.join(( WHITELIST = '\n'.join((
str(CONEMU_EXE), str(CONEMU_EXE),
fr'{PROGRAMFILES_32}\TeamViewer\TeamViewer.exe', fr'{PROGRAMFILES_32}\TeamViewer\TeamViewer.exe',
@ -189,17 +171,22 @@ def build_menus(base_menus, title, presets) -> dict[str, ui.Menu]:
return menus return menus
def end_session(menus: dict[str, ui.Menu]) -> None: def update_scheduled_task() -> None:
"""End Auto Repairs session.""" """Create (or update) scheduled task to start repairs."""
# Add last run marker cmd = [
reg_set_value( 'schtasks', '/create', '/f',
'HKCU', '/sc', 'ONLOGON',
fr'Software\{KIT_NAME_FULL}', '/tn', f'{KIT_NAME_FULL}-AutoRepairs',
'AutoRepairsLastRun', '/rl', 'HIGHEST',
time.strftime('%Y-%m-%d'), '/tr', fr'C:\Windows\System32\cmd.exe "/C {sys.executable} {sys.argv[0]}"',
'SZ', ]
) if IN_CONEMU:
cmd[-1] = f'{CONEMU_EXE} -run {sys.executable} {sys.argv[0]}'
run_program(cmd)
def end_session() -> None:
"""End Auto Repairs session."""
# Remove logon task # Remove logon task
cmd = [ cmd = [
'schtasks', '/delete', '/f', 'schtasks', '/delete', '/f',
@ -231,18 +218,10 @@ def end_session(menus: dict[str, ui.Menu]) -> None:
except FileNotFoundError: except FileNotFoundError:
LOG.error('Ending repair session but session not started.') LOG.error('Ending repair session but session not started.')
try: try:
reg_delete_value('HKCU', AUTO_REPAIR_KEY, 'Kill Explorer') cmd = ['reg', 'delete', fr'HKCU\{AUTO_REPAIR_KEY}', '/f']
except FileNotFoundError: run_program(cmd)
# Ignore if not set except CalledProcessError:
pass LOG.error('Failed to remove Auto Repairs session settings')
for group in menus:
try:
cmd = ['reg', 'delete', fr'HKCU\{AUTO_REPAIR_KEY}\{group}', '/f']
run_program(cmd)
except CalledProcessError:
LOG.error(
'Failed to remove Auto Repairs session settings for group %s', group,
)
def get_entry_settings(group, name) -> dict[str, Any]: def get_entry_settings(group, name) -> dict[str, Any]:
@ -269,15 +248,11 @@ def init(menus, presets) -> None:
session_started = is_session_started() session_started = is_session_started()
# Check if autologon is needed # Check if autologon is needed
if not session_started: if not session_started and is_autologon_enabled():
if is_autologon_enabled(): LOG.warning('Skipping Autologon to preserve current settings.')
LOG.warning('Skipping Autologon to preserve current settings.') menus['Options'].options['Use Autologon']['Disabled'] = True
menus['Options'].options['Use Autologon']['Disabled'] = True menus['Options'].options['Use Autologon']['Selected'] = False
menus['Options'].options['Use Autologon']['Selected'] = False reg_set_value('HKCU', AUTO_REPAIR_KEY, 'Use Autologon', 0, 'DWORD')
reg_set_value('HKCU', AUTO_REPAIR_KEY, 'Use Autologon', 0, 'DWORD')
else:
# Deselect Autologon by default
menus['Options'].options['Use Autologon']['Selected'] = False
save_selection_settings(menus) save_selection_settings(menus)
# Start new session # Start new session
@ -299,7 +274,6 @@ def init_run(options) -> None:
update_scheduled_task() update_scheduled_task()
if options['Kill Explorer']['Selected']: if options['Kill Explorer']['Selected']:
atexit.register(start_explorer) atexit.register(start_explorer)
reg_set_value('HKCU', AUTO_REPAIR_KEY, 'Kill Explorer', 1, 'DWORD')
TRY_PRINT.run('Killing Explorer...', kill_explorer, msg_good='DONE') TRY_PRINT.run('Killing Explorer...', kill_explorer, msg_good='DONE')
if options['Use Autologon']['Selected'] and not is_autologon_enabled(): if options['Use Autologon']['Selected'] and not is_autologon_enabled():
TRY_PRINT.run( TRY_PRINT.run(
@ -344,6 +318,7 @@ def init_session(options) -> None:
download=True, msg_good='DONE', download=True, msg_good='DONE',
) )
print('') print('')
reboot(30)
def is_autologon_enabled() -> bool: def is_autologon_enabled() -> bool:
@ -440,7 +415,7 @@ def run_auto_repairs(base_menus, presets) -> None:
show_main_menu(base_menus, menus, presets, title) show_main_menu(base_menus, menus, presets, title)
except SystemExit: except SystemExit:
if ui.ask('End session?'): if ui.ask('End session?'):
end_session(menus) end_session()
raise raise
# Start or resume repairs # Start or resume repairs
@ -465,7 +440,7 @@ def run_auto_repairs(base_menus, presets) -> None:
ui.abort() ui.abort()
# Done # Done
end_session(menus) end_session()
ui.print_info('Done') ui.print_info('Done')
ui.pause('Press Enter to exit...') ui.pause('Press Enter to exit...')
@ -635,20 +610,6 @@ def update_main_menu(menus) -> None:
menus['Main'].options[name]['Display Name'] = display_name menus['Main'].options[name]['Display Name'] = display_name
def update_scheduled_task():
"""Create (or update) scheduled task to start repairs."""
cmd = [
'schtasks', '/create', '/f',
'/sc', 'ONLOGON',
'/tn', f'{KIT_NAME_FULL}-AutoRepairs',
'/rl', 'HIGHEST',
'/tr', fr'C:\Windows\System32\cmd.exe "/C {sys.executable} {sys.argv[0]}"',
]
if IN_CONEMU:
cmd[-1] = f'{CONEMU_EXE} -run {sys.executable} {sys.argv[0]}'
run_program(cmd)
# Auto Repairs: Wrapper Functions # Auto Repairs: Wrapper Functions
def auto_adwcleaner(group, name) -> None: def auto_adwcleaner(group, name) -> None:
"""Run AdwCleaner scan. """Run AdwCleaner scan.
@ -720,28 +681,6 @@ def auto_chkdsk(group, name) -> None:
reboot() reboot()
def auto_disable_defender(group, name):
"""Open Windows Defender Threat Settings."""
result = TRY_PRINT.run(
'Open Windows Defender settings...',
open_defender_settings,
disable=True,
msg_good='Done',
)
save_settings(group, name, result=result)
def auto_enable_defender(group, name):
"""Open Windows Defender Threat Settings."""
result = TRY_PRINT.run(
'Open Windows Defender settings...',
open_defender_settings,
enable=True,
msg_good='Done',
)
save_settings(group, name, result=result)
def auto_disable_pending_renames(group, name) -> None: def auto_disable_pending_renames(group, name) -> None:
"""Disable pending renames.""" """Disable pending renames."""
result = TRY_PRINT.run( result = TRY_PRINT.run(
@ -775,21 +714,6 @@ def auto_dism(group, name) -> None:
reboot() reboot()
def auto_emsisoft_cmd_run(group, name):
"""Run EmisoftCmd."""
TRY_PRINT.run('EmsisoftCmd (Install)...', install_emsisoft_cmd)
TRY_PRINT.run('EmsisoftCmd (Update)...', update_emsisoft_cmd)
result = TRY_PRINT.run('EmsisoftCmd (Scan)...', run_emsisoft_cmd_scan)
TRY_PRINT.run('EmsisoftCmd (Uninstall Service)...', delete_emsisoft_cmd_service)
save_settings(group, name, result=result)
def auto_emsisoft_cmd_uninstall(group, name):
"""Uninstall EmsisoftCmd."""
result = TRY_PRINT.run('EmsisoftCmd (Uninstall)...', uninstall_emsisoft_cmd)
save_settings(group, name, result=result)
def auto_enable_regback(group, name) -> None: def auto_enable_regback(group, name) -> None:
"""Enable RegBack.""" """Enable RegBack."""
result = TRY_PRINT.run( result = TRY_PRINT.run(
@ -800,14 +724,6 @@ def auto_enable_regback(group, name) -> None:
save_settings(group, name, result=result) save_settings(group, name, result=result)
def auto_fix_file_associations(group, name):
"""Run Fix File Associations scan."""
result = TRY_PRINT.run(
'Fix File Associations...', fix_file_associations, msg_good='DONE',
)
save_settings(group, name, result=result)
def auto_hitmanpro(group, name) -> None: def auto_hitmanpro(group, name) -> None:
"""Run HitmanPro scan.""" """Run HitmanPro scan."""
result = TRY_PRINT.run('HitmanPro...', run_hitmanpro, msg_good='DONE') result = TRY_PRINT.run('HitmanPro...', run_hitmanpro, msg_good='DONE')
@ -820,29 +736,6 @@ def auto_kvrt(group, name) -> None:
save_settings(group, name, result=result) save_settings(group, name, result=result)
def auto_mbam_install(group, name):
"""Install Malwarebytes."""
result = TRY_PRINT.run('Malwarebytes (Install)...', install_mbam)
save_settings(group, name, result=result)
def auto_mbam_run(group, name):
"""Run Malwarebytes.
save_settings() is called first since MBAM may kill this script.
"""
save_settings(group, name, done=True, failed=False, message='DONE')
result = TRY_PRINT.run('Malwarebytes (Run)...', run_mbam, msg_good='DONE')
ui.pause('Press Enter to continue...')
save_settings(group, name, result=result)
def auto_mbam_uninstall(group, name):
"""Uninstall Malwarebytes."""
result = TRY_PRINT.run('Malwarebytes (Uninstall)...', uninstall_mbam)
save_settings(group, name, result=result)
def auto_microsoft_defender(group, name) -> None: def auto_microsoft_defender(group, name) -> None:
"""Run Microsoft Defender scan.""" """Run Microsoft Defender scan."""
result = TRY_PRINT.run( result = TRY_PRINT.run(
@ -862,7 +755,6 @@ def auto_remove_power_plan(group, name) -> None:
"""Remove custom power plan and set to Balanced.""" """Remove custom power plan and set to Balanced."""
result = TRY_PRINT.run( result = TRY_PRINT.run(
'Remove Custom Power Plan...', remove_custom_power_plan, 'Remove Custom Power Plan...', remove_custom_power_plan,
high_performance=True,
) )
save_settings(group, name, result=result) save_settings(group, name, result=result)
@ -1091,61 +983,11 @@ def backup_registry() -> None:
run_tool('ERUNT', 'ERUNT', backup_path, 'sysreg', 'curuser', 'otherusers') run_tool('ERUNT', 'ERUNT', backup_path, 'sysreg', 'curuser', 'otherusers')
def delete_emsisoft_cmd_service():
"""Delete EmsisoftCmd service."""
try:
stop_service('epp')
except GenericError:
pass
# Delete service
run_program(['sc', 'delete', 'epp'], check=False)
def delete_registry_null_keys() -> None: def delete_registry_null_keys() -> None:
"""Delete registry keys with embedded null characters.""" """Delete registry keys with embedded null characters."""
run_tool('RegDelNull', 'RegDelNull', '-s', '-y', download=True) run_tool('RegDelNull', 'RegDelNull', '-s', '-y', download=True)
def fix_file_associations():
"""Fix file associations via registry edits."""
winaio_path = get_tool_path('WinRepairAIO', 'Repair_Windows').parent
winaio_path = winaio_path.joinpath(
f'Files/regfiles/file_associations/{OS_VERSION}',
)
for item in winaio_path.iterdir():
if item.suffix.lower() != '.reg':
continue
cmd = ['reg', 'import', str(item), f'/reg:{ARCH}']
run_program(cmd, check=False)
def install_emsisoft_cmd():
"""Install EmsisoftCmd."""
download_tool('EmsisoftCmd', 'EmsisoftCmd')
installer = get_tool_path('EmsisoftCmd', 'EmsisoftCmd')
extract_archive(installer, EMSISOFT_INSTALL_PATH, '-aos')
def install_mbam():
"""Install Malwarebytes."""
marker = set_local_storage_path('.', MBAM_PRESERVE_MARKER)
marker.unlink(missing_ok=True)
# Check for current installation
for path in ('ProgramW6432', 'PROGRAMFILES', 'PROGRAMFILES(X86)'):
if os.path.exists(f'{os.environ.get(path, "")}/{MBAM_EXE_PATH}'):
LOG.info('Previous Malwarebytes installation detected.')
marker.touch()
break
# Install / Upgrade
proc = run_tool(
'MBAM', 'MBAM', '/VERYSILENT', '/NORESTART', download=True, popen=True,
)
proc.wait()
def log_kvrt_results(log_path, report_path) -> None: def log_kvrt_results(log_path, report_path) -> None:
"""Parse KVRT report and log results in plain text.""" """Parse KVRT report and log results in plain text."""
log_text = '' log_text = ''
@ -1216,41 +1058,6 @@ def run_bleachbit(cleaners, preview=True) -> None:
) )
def run_emsisoft_cmd_scan():
"""Run EmsisoftCmd scan."""
log_path = format_log_path(
log_name='EmsisoftCmd', timestamp=True, tool=True,
)
log_path.parent.mkdir(parents=True, exist_ok=True)
quarantine_path = set_quarantine_path('EmsisoftCmd')
quarantine_path.mkdir(parents=True, exist_ok=True)
whitelist_path = log_path.with_suffix('.wl')
# Create whitelist
whitelist_path.write_text(WHITELIST, encoding='utf-8')
# Run Scan
cmd = [
f'{EMSISOFT_INSTALL_PATH}/a2cmd.exe',
fr'/files={PROGRAMDATA},{SYSTEMDRIVE}\Users',
'/archive', '/memory', '/ntfs', '/pup', '/traces',
f'/log={log_path}',
f'/quarantine={quarantine_path}',
f'/whitelist={whitelist_path}',
]
if IN_CONEMU:
cmd.extend(['-new_console:nb', '-new_console:s33V'])
run_program(cmd, check=False, pipe=False)
sleep(5)
set_proc_priority('a2cmd.exe', 'HIGH')
wait_for_procs('a2cmd.exe')
else:
proc = popen_program(cmd, priority=True)
sleep(5)
set_proc_priority('a2cmd.exe', 'HIGH')
proc.wait()
def run_bcuninstaller() -> None: def run_bcuninstaller() -> None:
"""Run BCUninstaller.""" """Run BCUninstaller."""
run_tool('BCUninstaller', 'BCUninstaller') run_tool('BCUninstaller', 'BCUninstaller')
@ -1262,11 +1069,7 @@ def run_hitmanpro() -> None:
log_path = log_path.with_suffix('.xml') log_path = log_path.with_suffix('.xml')
log_path.parent.mkdir(parents=True, exist_ok=True) log_path.parent.mkdir(parents=True, exist_ok=True)
cmd_args = ['/scanonly', f'/log={log_path}'] cmd_args = ['/scanonly', f'/log={log_path}']
proc = run_tool( run_tool('HitmanPro', 'HitmanPro', *cmd_args, download=True)
'HitmanPro', 'HitmanPro', *cmd_args,
download=True, popen=True, priority=True,
)
proc.wait()
def run_kvrt() -> None: def run_kvrt() -> None:
@ -1300,41 +1103,16 @@ def run_kvrt() -> None:
_f.write(f'"{kvrt_path}" {" ".join(cmd_args)}\n') _f.write(f'"{kvrt_path}" {" ".join(cmd_args)}\n')
cmd = ('cmd', '/c', tmp_file, '-new_console:nb', '-new_console:s33V') cmd = ('cmd', '/c', tmp_file, '-new_console:nb', '-new_console:s33V')
run_program(cmd, check=False) run_program(cmd, check=False)
sleep(5) sleep(1)
set_proc_priority('KVRT', 'HIGH', exact=False)
wait_for_procs('KVRT.exe') wait_for_procs('KVRT.exe')
log_kvrt_results(log_path, report_path) log_kvrt_results(log_path, report_path)
return return
# Run in background # Run in background
proc = run_tool( run_tool('KVRT', 'KVRT', *cmd_args, download=True)
'KVRT', 'KVRT', *cmd_args,
download=True, popen=True, priority=True,
)
sleep(5)
set_proc_priority('KVRT', 'HIGH', exact=False)
proc.wait()
log_kvrt_results(log_path, report_path) log_kvrt_results(log_path, report_path)
def run_mbam():
"""Run Malwarebytes."""
exe_path = None
# Get EXE path
for path in ('ProgramW6432', 'PROGRAMFILES'):
test_path = get_path_obj(f'{os.environ.get(path, "")}/{MBAM_EXE_PATH}')
if test_path.exists():
exe_path = str(test_path)
# Bail if MBAM not found
if not exe_path:
raise FileNotFoundError('MBAM not found')
# Run
run_program(exe_path, check=False)
def run_microsoft_defender(full=True) -> None: def run_microsoft_defender(full=True) -> None:
"""Run Microsoft Defender scan.""" """Run Microsoft Defender scan."""
reg_key = r'Software\Microsoft\Windows Defender' reg_key = r'Software\Microsoft\Windows Defender'
@ -1351,7 +1129,8 @@ def run_microsoft_defender(full=True) -> None:
# Get MS Defender status # Get MS Defender status
## NOTE: disabled may be set to an int instead of bool ## NOTE: disabled may be set to an int instead of bool
## This is fine because we're just checking if it's enabled. ## This is fine because we're just checking if it's enabled.
disabled = defender_is_disabled() disabled = bool(reg_read_value('HKLM', reg_key, 'DisableAntiSpyware'))
disabled = disabled or reg_read_value('HKLM', reg_key, 'DisableAntiVirus')
try: try:
passive_mode = reg_read_value('HKLM', reg_key, 'PassiveMode') == 2 passive_mode = reg_read_value('HKLM', reg_key, 'PassiveMode') == 2
except FileNotFoundError: except FileNotFoundError:
@ -1393,38 +1172,6 @@ def run_rkill() -> None:
run_tool('RKill', 'RKill', *cmd_args, download=True) run_tool('RKill', 'RKill', *cmd_args, download=True)
def uninstall_emsisoft_cmd():
"""Uninstall EmsisoftCmd."""
delete_folder(EMSISOFT_INSTALL_PATH, force=True, ignore_errors=True)
def uninstall_mbam():
"""Uninstall Malwarebytes."""
marker = set_local_storage_path('.', MBAM_PRESERVE_MARKER)
if marker.exists():
marker.unlink()
raise GenericWarning('Leaving existing MBAM installation in place.')
# Uninstall
install_path = reg_read_value('HKLM', MBAM_UNINSTALL_KEY, 'InstallLocation')
cmd = [
fr'{install_path}\mbuns.exe', '/Uninstall', '/VERYSILENT', '/NORESTART',
]
run_program(cmd)
def update_emsisoft_cmd():
"""Update EmsisoftCmd."""
cmd = [f'{EMSISOFT_INSTALL_PATH}/a2cmd.exe', '/update']
if IN_CONEMU:
cmd.extend(['-new_console:nb', '-new_console:s33V'])
run_program(cmd, check=False, pipe=False)
sleep(1)
wait_for_procs('a2cmd.exe')
else:
run_program(cmd, check=False)
# OS Built-in Functions # OS Built-in Functions
def create_custom_power_plan(enable_sleep=True, keep_display_on=False) -> None: def create_custom_power_plan(enable_sleep=True, keep_display_on=False) -> None:
"""Create new power plan and set as active.""" """Create new power plan and set as active."""
@ -1546,63 +1293,6 @@ def kill_explorer() -> None:
run_program(cmd, check=False) run_program(cmd, check=False)
def open_defender_settings(disable=False, enable=False):
"""Open Windows Defender Threat Settings."""
enabled = None
# Check Registry if Defender is disabled
if defender_is_disabled():
raise GenericError('Defender is disabled.')
# Check WMIC if Defender is active
cmd = [
'WMIC', r'/namespace:\\root\SecurityCenter2',
'path', 'AntivirusProduct',
'where', 'displayName="Windows Defender"',
'get', 'productState', '/value',
]
try:
proc = run_program(cmd)
status = proc.stdout.split('=')[1]
enabled = bool(int(status) & MS_ANTIVIRUS_ENABLED)
except Exception:
# Unknown result, just show the prompt
pass
# Set prompt message
message = 'Please adjust Windows Defender settings as appropriate.'
if disable:
if enabled is False:
# Already disabled, just bail
return
message = 'Please disable realtime Windows Defender scanning.'
elif enable:
if enabled:
# Already enabled, just bail
return
message = 'Please enable realtime Windows Defender scanning.'
message += '\nPress OK to continue repairs.'
# Check Kill Explorer setting
cmd = ['start', '', 'windowsdefender://threatsettings']
kill_explorer_proc = False
try:
kill_explorer_proc = reg_read_value(
'HKCU', AUTO_REPAIR_KEY, 'Kill Explorer',
)
except FileNotFoundError:
# Ignore if not set
pass
if kill_explorer_proc:
# Explorer is needed to open the settings window, relaunch for now
start_explorer()
sleep(2)
run_program(cmd, check=False, shell=True)
show_alert_box(message=message, title='Windows Defender: Threat Settings')
if kill_explorer_proc:
kill_explorer()
def reboot(timeout=10) -> None: def reboot(timeout=10) -> None:
"""Reboot the system.""" """Reboot the system."""
atexit.unregister(start_explorer) atexit.unregister(start_explorer)
@ -1804,11 +1494,6 @@ def run_sfc_scan() -> None:
raise OSError raise OSError
def run_uninstallview() -> None:
"""Run UninstallView."""
run_tool('UninstallView', 'UninstallView')
def set_system_restore_size(size=8) -> None: def set_system_restore_size(size=8) -> None:
"""Set System Restore size.""" """Set System Restore size."""
cmd = [ cmd = [

View file

@ -2,13 +2,11 @@
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import configparser import configparser
from datetime import datetime, timedelta
import logging import logging
import json import json
import os import os
import re import re
import sys import sys
import webbrowser
from typing import Any from typing import Any
@ -16,23 +14,21 @@ from wk.cfg.main import KIT_NAME_FULL
from wk.cfg.setup import ( from wk.cfg.setup import (
BROWSER_PATHS, BROWSER_PATHS,
DISABLED_ENTRIES_WINDOWS_11, DISABLED_ENTRIES_WINDOWS_11,
FAB_TIMEFRAME,
LIBREOFFICE_XCU_DATA, LIBREOFFICE_XCU_DATA,
REG_CHROME_UBLOCK_ORIGIN, REG_CHROME_UBLOCK_ORIGIN,
REG_ESET_NOD32_SETTINGS,
REG_OPEN_SHELL_LOW_POWER_IDLE,
REG_OPEN_SHELL_SETTINGS,
REG_WINDOWS_EXPLORER, REG_WINDOWS_EXPLORER,
REG_OPEN_SHELL_SETTINGS,
REG_OPEN_SHELL_LOW_POWER_IDLE,
REG_WINDOWS_BSOD_MINIDUMPS,
UBLOCK_ORIGIN_URLS, UBLOCK_ORIGIN_URLS,
) )
from wk.exe import get_procs, kill_procs, run_program, popen_program from wk.exe import kill_procs, run_program, popen_program
from wk.io import case_insensitive_path, get_path_obj from wk.io import case_insensitive_path, get_path_obj
from wk.kit.tools import ( from wk.kit.tools import (
ARCH, ARCH,
extract_archive, extract_archive,
extract_tool, extract_tool,
find_kit_dir, find_kit_dir,
get_sdio_path,
get_tool_path, get_tool_path,
run_tool, run_tool,
) )
@ -41,7 +37,6 @@ from wk.os.win import (
OS_VERSION, OS_VERSION,
activate_with_bios, activate_with_bios,
check_4k_alignment, check_4k_alignment,
get_installed_antivirus,
get_installed_ram, get_installed_ram,
get_os_activation, get_os_activation,
get_os_name, get_os_name,
@ -50,7 +45,7 @@ from wk.os.win import (
get_volume_usage, get_volume_usage,
is_activated, is_activated,
is_secure_boot_enabled, is_secure_boot_enabled,
reg_read_value, list_installed_antivirus,
reg_set_value, reg_set_value,
reg_write_settings, reg_write_settings,
stop_service, stop_service,
@ -166,44 +161,6 @@ def build_menus(base_menus, title, presets) -> dict[str, ui.Menu]:
return menus return menus
def check_if_auto_repairs_are_needed():
"""Check if Auto Repairs are needed based on last Fab run."""
try:
last_run = reg_read_value(
'HKCU', fr'Software\{KIT_NAME_FULL}', 'FabLastRun',
)
except FileNotFoundError:
# Assuming it isn't needed
return
fab_last_run = datetime.strptime(last_run, '%Y-%m-%d')
# Check if Auto Repairs was run after Fab
auto_repairs_last_run = None
try:
last_run = reg_read_value(
'HKCU', fr'Software\{KIT_NAME_FULL}', 'AutoRepairsLastRun',
)
auto_repairs_last_run = datetime.strptime(last_run, '%Y-%m-%d')
except FileNotFoundError:
# Assuming it wasn't
pass
# Check date(s) and prompt tech if necessary
if datetime.now() - fab_last_run < timedelta(days=FAB_TIMEFRAME):
if auto_repairs_last_run and auto_repairs_last_run >= fab_last_run:
# Auto Repairs shouldn't be needed
return
ui.print_warning('Fab was recently run, Auto Repairs may be needed.')
selection = ui.choice(
'Continue with Auto Setup, run Auto Repairs, or Quit?', ['C', 'R', 'Q'],
)
if selection == 'Q':
ui.abort()
if selection == 'R':
auto_repairs_script = find_kit_dir('Scripts').joinpath('auto_repairs.py')
os.execv(sys.executable, ['python', auto_repairs_script])
def check_os_and_set_menu_title(title) -> str: def check_os_and_set_menu_title(title) -> str:
"""Check OS version and update title for menus, returns str.""" """Check OS version and update title for menus, returns str."""
color = None color = None
@ -232,9 +189,6 @@ def check_os_and_set_menu_title(title) -> str:
def load_preset(menus, presets, title, enable_menu_exit=True) -> None: def load_preset(menus, presets, title, enable_menu_exit=True) -> None:
"""Load menu settings from preset and ask selection question(s).""" """Load menu settings from preset and ask selection question(s)."""
msp = False
# Menu exit entry
if not enable_menu_exit: if not enable_menu_exit:
MENU_PRESETS.actions['Main Menu'].update({'Disabled':True, 'Hidden':True}) MENU_PRESETS.actions['Main Menu'].update({'Disabled':True, 'Hidden':True})
@ -259,36 +213,8 @@ def load_preset(menus, presets, title, enable_menu_exit=True) -> None:
ui.clear_screen() ui.clear_screen()
ui.print_standard(f'{title}') ui.print_standard(f'{title}')
print('') print('')
if selection[0] == 'Default': if selection[0] == 'Default' and ui.ask('Install LibreOffice?'):
# OpenShell menus['Install Software'].options['LibreOffice']['Selected'] = True
if OS_VERSION != 11 and ui.ask('Install OpenShell?'):
menus['Install Software'].options['Open-Shell']['Selected'] = True
menus['Configure System'].options['Open-Shell']['Selected'] = True
# LibreOffice
if ui.ask('Install LibreOffice?'):
menus['Install Software'].options['LibreOffice']['Selected'] = True
# Hiberboot & Hibernation
print('')
msg = 'Disable Fast Startup and enable Hibernation? (Recommended for SSDs)'
if ui.ask(msg):
for option in ('Disable Fast Startup', 'Enable Hibernation'):
menus['Configure System'].options[option]['Selected'] = True
# Apply ITS settings?
msp = ui.ask('Is this an ITS system?')
if msp:
option = 'Apply ITS Settings'
menus['Configure System'].options[option]['Selected'] = True
# ESET NOD32 AV
print('')
if msp or ui.ask('Install ESET NOD32 AV?'):
option = 'ESET NOD32 AV'
if msp or ui.ask(' For VIP?'):
option = f'{option} (MSP)'
menus['Install Software'].options[option]['Selected'] = True
# Re-enable Main Menu action if disabled # Re-enable Main Menu action if disabled
MENU_PRESETS.actions['Main Menu'].update({'Disabled':False, 'Hidden':False}) MENU_PRESETS.actions['Main Menu'].update({'Disabled':False, 'Hidden':False})
@ -313,9 +239,6 @@ def run_auto_setup(base_menus, presets) -> None:
# Check OS and update title for menus # Check OS and update title for menus
title = check_os_and_set_menu_title(title) title = check_os_and_set_menu_title(title)
# Check if AV scan needs to be run
check_if_auto_repairs_are_needed()
# Generate menus # Generate menus
menus = build_menus(base_menus, title, presets) menus = build_menus(base_menus, title, presets)
@ -449,11 +372,6 @@ def auto_enable_regback() -> None:
) )
def auto_apply_its_settings():
"""Apply ITS settings."""
TRY_PRINT.run('Apply ITS settings...', apply_its_settings)
def auto_system_restore_enable() -> None: def auto_system_restore_enable() -> None:
"""Enable System Restore.""" """Enable System Restore."""
cmd = [ cmd = [
@ -487,6 +405,7 @@ def auto_activate_windows() -> None:
def auto_config_browsers() -> None: def auto_config_browsers() -> None:
"""Configure Browsers.""" """Configure Browsers."""
prompt = ' Press Enter to continue...' prompt = ' Press Enter to continue...'
TRY_PRINT.run('Chrome Notifications...', disable_chrome_notifications)
TRY_PRINT.run( TRY_PRINT.run(
'uBlock Origin...', enable_ublock_origin, msg_good='STARTED', 'uBlock Origin...', enable_ublock_origin, msg_good='STARTED',
) )
@ -510,49 +429,16 @@ def auto_config_open_shell() -> None:
TRY_PRINT.run('Open Shell...', config_open_shell) TRY_PRINT.run('Open Shell...', config_open_shell)
def auto_disable_chrome_notifications():
"""Disable Chrome notifications.
NOTE: This can cause Chrome Sync to be re-authenticated."""
TRY_PRINT.run('Disable Chrome Notifications...', disable_chrome_notifications)
def auto_disable_fast_startup():
"""Disable fast startup (i.e. Hiberboot)."""
TRY_PRINT.run(
'Disable Fast Startup...', reg_set_value, 'HKLM',
r'SYSTEM\CurrentControlSet\Control\Session Manager\Power',
'HiberbootEnabled', 0, 'DWORD',
)
def auto_disable_password_expiration() -> None: def auto_disable_password_expiration() -> None:
"""Disable password expiration for all users.""" """Disable password expiration for all users."""
TRY_PRINT.run('Disable password expiration...', disable_password_expiration) TRY_PRINT.run('Disable password expiration...', disable_password_expiration)
def auto_enable_hibernation():
"""Enable Hibernation."""
TRY_PRINT.run(
'Enable Hibernation...', run_program, ['powercfg', '/hibernate', 'on'],
)
def auto_export_aida64_report() -> None: def auto_export_aida64_report() -> None:
"""Export AIDA64 reports.""" """Export AIDA64 reports."""
TRY_PRINT.run('AIDA64 Report...', export_aida64_report) TRY_PRINT.run('AIDA64 Report...', export_aida64_report)
def auto_install_eset_nod32_av():
"""Install ESET NOD32 AV."""
TRY_PRINT.run('ESET NOD32...', install_eset_nod32_av)
def auto_install_eset_nod32_av_msp():
"""Install ESET NOD32 AV with MSP settings."""
TRY_PRINT.run('ESET NOD32 (MSP)...', install_eset_nod32_av, msp=True)
def auto_install_firefox() -> None: def auto_install_firefox() -> None:
"""Install Firefox.""" """Install Firefox."""
TRY_PRINT.run('Firefox...', install_firefox) TRY_PRINT.run('Firefox...', install_firefox)
@ -564,10 +450,7 @@ def auto_install_libreoffice() -> None:
NOTE: It is assumed that auto_install_vcredists() will be run NOTE: It is assumed that auto_install_vcredists() will be run
before this function to satisfy the vcredist=False usage. before this function to satisfy the vcredist=False usage.
""" """
TRY_PRINT.run( TRY_PRINT.run('LibreOffice...', install_libreoffice, vcredist=False)
'LibreOffice...', install_libreoffice,
use_mso_formats=True, vcredist=False,
)
def auto_install_open_shell() -> None: def auto_install_open_shell() -> None:
@ -600,9 +483,9 @@ def auto_open_device_manager() -> None:
TRY_PRINT.run('Device Manager...', open_device_manager) TRY_PRINT.run('Device Manager...', open_device_manager)
def auto_open_mic_and_webcam_tests(): def auto_open_hwinfo_sensors() -> None:
"""Open Webcam Tests.""" """Open HWiNFO Sensors."""
TRY_PRINT.run('Webcam Tests...', open_mic_and_webcam_tests) TRY_PRINT.run('HWiNFO Sensors...', open_hwinfo_sensors)
def auto_open_microsoft_store_updates() -> None: def auto_open_microsoft_store_updates() -> None:
@ -638,7 +521,7 @@ def auto_show_4k_alignment_check() -> None:
def auto_show_installed_antivirus() -> None: def auto_show_installed_antivirus() -> None:
"""Display installed antivirus.""" """Display installed antivirus."""
TRY_PRINT.run('Virus Protection...', get_installed_antivirus) TRY_PRINT.run('Virus Protection...', list_installed_antivirus)
def auto_show_installed_ram() -> None: def auto_show_installed_ram() -> None:
@ -670,11 +553,6 @@ def auto_show_storage_status() -> None:
TRY_PRINT.run('Storage Status...', get_storage_status) TRY_PRINT.run('Storage Status...', get_storage_status)
def auto_shutup_10():
"""Disable Windows telemetry using O&O ShutUp 10."""
TRY_PRINT.run('Disable Telemetry...', run_shutup_10)
def auto_windows_temp_fix() -> None: def auto_windows_temp_fix() -> None:
"""Restore default ACLs for Windows\\Temp.""" """Restore default ACLs for Windows\\Temp."""
TRY_PRINT.run(r'Windows\Temp fix...', fix_windows_temp) TRY_PRINT.run(r'Windows\Temp fix...', fix_windows_temp)
@ -685,12 +563,7 @@ def config_explorer() -> None:
"""Configure Windows Explorer and restart the process.""" """Configure Windows Explorer and restart the process."""
reg_write_settings(REG_WINDOWS_EXPLORER) reg_write_settings(REG_WINDOWS_EXPLORER)
kill_procs('explorer.exe', force=True) kill_procs('explorer.exe', force=True)
popen_program([fr'{SYSTEMDRIVE}\Windows\explorer.exe']) popen_program(['explorer.exe'])
sleep(3)
for _ in range(30):
if get_procs('explorer.exe'):
break
sleep(1)
def config_open_shell() -> None: def config_open_shell() -> None:
@ -757,14 +630,14 @@ def disable_chrome_notifications() -> None:
def disable_password_expiration() -> None: def disable_password_expiration() -> None:
"""Disable password expiration for all users.""" """Disable password expiration for all users."""
cmd = ['wmic', 'UserAccount', 'set', 'PasswordExpires=False'] script_path = find_kit_dir('Scripts').joinpath('disable_password_expiration.ps1')
cmd = ['PowerShell', '-ExecutionPolicy', 'Bypass', '-File', script_path]
run_program(cmd) run_program(cmd)
def enable_bsod_minidumps() -> None: def enable_bsod_minidumps() -> None:
"""Enable saving minidumps during BSoDs.""" """Enable saving minidumps during BSoDs."""
cmd = ['wmic', 'RECOVEROS', 'set', 'DebugInfoType', '=', '3'] reg_write_settings(REG_WINDOWS_BSOD_MINIDUMPS)
run_program(cmd)
def enable_ublock_origin() -> None: def enable_ublock_origin() -> None:
@ -809,20 +682,6 @@ def fix_windows_temp() -> None:
# Install Functions # Install Functions
def install_eset_nod32_av(msp=False):
"""Install ESET NOD32 Antivirus."""
config_file = f'eset-config{"-msp" if msp else ""}.xml'
reg_write_settings(REG_ESET_NOD32_SETTINGS)
run_tool(
'ESET', 'ESET_NOD32_AV',
'--silent', '--accepteula',
'--msi-property', 'PRODUCTTYPE=eav',
'PRODUCT_LANG=1033', 'PRODUCT_LANG_CODE=en-US',
f'ADMINCFG="{config_file}"',
cwd=True, download=True,
)
def install_firefox() -> None: def install_firefox() -> None:
"""Install Firefox. """Install Firefox.
@ -979,11 +838,6 @@ def uninstall_firefox() -> None:
# Misc Functions # Misc Functions
def apply_its_settings():
"""Apply ITS settings."""
create_custom_power_plan(enable_sleep=False)
def check_secure_boot_status() -> None: def check_secure_boot_status() -> None:
"""Check Secure Boot status.""" """Check Secure Boot status."""
is_secure_boot_enabled(raise_exceptions=True, show_alert=True) is_secure_boot_enabled(raise_exceptions=True, show_alert=True)
@ -1071,11 +925,20 @@ def open_device_manager() -> None:
popen_program(['mmc', 'devmgmt.msc']) popen_program(['mmc', 'devmgmt.msc'])
def open_mic_and_webcam_tests(): def open_hwinfo_sensors() -> None:
"""Open Webcam Tests.""" """Open HWiNFO sensors."""
webbrowser.open('https://mictests.com/') hwinfo_path = get_tool_path('HWiNFO', 'HWiNFO')
sleep(1) base_config = hwinfo_path.with_name('general.ini')
webbrowser.open('http://webcamtests.com/')
# Write new config to disk
with open(hwinfo_path.with_suffix('.ini'), 'w', encoding='utf-8') as _f:
_f.write(
f'{base_config.read_text(encoding="utf-8")}\n'
'SensorsOnly=1\nSummaryOnly=0\n'
)
# Open HWiNFO
run_tool('HWiNFO', 'HWiNFO', popen=True)
def open_microsoft_store_updates() -> None: def open_microsoft_store_updates() -> None:
@ -1091,10 +954,7 @@ def open_snappy_driver_installer_origin() -> None:
stop_service(svc) stop_service(svc)
if any([get_service_status(s) != 'stopped' for s in appid_services]): if any([get_service_status(s) != 'stopped' for s in appid_services]):
raise GenericWarning('Failed to stop AppID services') raise GenericWarning('Failed to stop AppID services')
sdio_path = get_sdio_path(interactive=False) run_tool('SDIO', 'SDIO', cwd=True, pipe=True, popen=True)
log_dir = format_log_path(tool=True).parent
cmd = [sdio_path, '-log_dir', log_dir]
popen_program(cmd, cwd=sdio_path.parent, pipe=True)
def open_windows_activation() -> None: def open_windows_activation() -> None:
@ -1110,12 +970,7 @@ def open_windows_updates() -> None:
def open_xmplay() -> None: def open_xmplay() -> None:
"""Open XMPlay.""" """Open XMPlay."""
sleep(2) sleep(2)
run_tool('XMPlay', 'XMPlay', 'music.m3u', cwd=True, popen=True) run_tool('XMPlay', 'XMPlay', 'music.7z', cwd=True, popen=True)
def run_shutup_10():
"""Disable Windows telemetry using O&O ShutUp 10."""
run_tool('ShutUp10', 'OOSU10', '1201.cfg', '/quiet', cwd=True)
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -67,7 +67,7 @@ def fix_layout(
# Calculate constraints # Calculate constraints
avail_horizontal, avail_vertical = get_window_size() avail_horizontal, avail_vertical = get_window_size()
avail_vertical -= layout['Current'].get('height', 0) avail_vertical -= layout['Current'].get('height', 0)
for group in ('Title', 'Subtitle', 'Info'): for group in ('Title', 'Info'):
if not layout[group]['Panes']: if not layout[group]['Panes']:
continue continue
avail_vertical -= layout[group].get('height', 0) + 1 avail_vertical -= layout[group].get('height', 0) + 1
@ -97,7 +97,7 @@ def fix_layout(
) )
for group, data in layout.items(): for group, data in layout.items():
num_panes = len(data['Panes']) num_panes = len(data['Panes'])
if num_panes < 2 or group not in ('Title', 'Subtitle', 'Info'): if num_panes < 2 or group not in ('Title', 'Info'):
continue continue
pane_width, remainder = divmod(avail_horizontal - (num_panes-1), num_panes) pane_width, remainder = divmod(avail_horizontal - (num_panes-1), num_panes)
for pane_id in data['Panes']: for pane_id in data['Panes']:

View file

@ -19,7 +19,6 @@ TMUX_SIDE_WIDTH = 21
TMUX_TITLE_HEIGHT = 2 TMUX_TITLE_HEIGHT = 2
TMUX_LAYOUT = { # NOTE: This needs to be in order from top to bottom TMUX_LAYOUT = { # NOTE: This needs to be in order from top to bottom
'Title': {'Panes': [], 'height': TMUX_TITLE_HEIGHT}, 'Title': {'Panes': [], 'height': TMUX_TITLE_HEIGHT},
'Subtitle': {'Panes': [], 'height': TMUX_TITLE_HEIGHT},
'Info': {'Panes': []}, 'Info': {'Panes': []},
'Current': {'Panes': [environ.get('TMUX_PANE', None)]}, 'Current': {'Panes': [environ.get('TMUX_PANE', None)]},
'Workers': {'Panes': []}, 'Workers': {'Panes': []},
@ -116,29 +115,6 @@ class TUI():
# Add pane # Add pane
self.layout['Title']['Panes'].append(tmux.split_window(**tmux_args)) self.layout['Title']['Panes'].append(tmux.split_window(**tmux_args))
def add_subtitle_pane(self, line1: str, line2: str) -> None:
"""Add pane to subtitle row."""
lines = [line1, line2]
tmux_args = {
'behind': True,
'lines': TMUX_TITLE_HEIGHT,
'target_id': None,
'text': '\n'.join(lines),
'vertical': True,
}
if self.layout['Subtitle']['Panes']:
tmux_args.update({
'behind': False,
'percent': 50,
'target_id': self.layout['Subtitle']['Panes'][-1],
'text': '\n'.join(lines),
'vertical': False,
})
tmux_args.pop('lines')
# Add pane
self.layout['Subtitle']['Panes'].append(tmux.split_window(**tmux_args))
def add_worker_pane( def add_worker_pane(
self, self,
lines: int | None = None, lines: int | None = None,
@ -245,12 +221,6 @@ class TUI():
self.layout['Info']['Panes'].clear() self.layout['Info']['Panes'].clear()
tmux.kill_pane(*panes) tmux.kill_pane(*panes)
def remove_all_subtitle_panes(self) -> None:
"""Remove all subtitle panes and update layout."""
panes = self.layout['Subtitle']['Panes'].copy()
self.layout['Subtitle']['Panes'].clear()
tmux.kill_pane(*panes)
def remove_all_worker_panes(self) -> None: def remove_all_worker_panes(self) -> None:
"""Remove all worker panes and update layout.""" """Remove all worker panes and update layout."""
self.layout['Workers'].pop('height', None) self.layout['Workers'].pop('height', None)
@ -272,13 +242,6 @@ class TUI():
self.layout['Title']['Panes'] = panes[:1] self.layout['Title']['Panes'] = panes[:1]
self.set_title(line1, line2, colors) self.set_title(line1, line2, colors)
def reset_subtitle_pane(self) -> None:
"""Remove all extra subtitle panes and update layout."""
panes = self.layout['Subtitle']['Panes'].copy()
if len(panes) > 1:
tmux.kill_pane(*panes[1:])
self.layout['Subtitle']['Panes'] = panes[:1]
def set_current_pane_height(self, height: int) -> None: def set_current_pane_height(self, height: int) -> None:
"""Set current pane height and update layout.""" """Set current pane height and update layout."""
self.layout['Current']['height'] = height self.layout['Current']['height'] = height
@ -360,7 +323,7 @@ def fix_layout(layout, forced: bool = False) -> None:
pass pass
# Update "group" panes widths # Update "group" panes widths
for group in ('Title', 'Subtitle', 'Info'): for group in ('Title', 'Info'):
num_panes = len(layout[group]['Panes']) num_panes = len(layout[group]['Panes'])
if num_panes <= 1: if num_panes <= 1:
continue continue

View file

@ -84,7 +84,7 @@ function copy_live_env() {
rsync -aI --exclude="macOS-boot-icon.tar" "$ROOT_DIR/setup/ufd/" "$PROFILE_DIR/airootfs/usr/share/WizardKit/" rsync -aI --exclude="macOS-boot-icon.tar" "$ROOT_DIR/setup/ufd/" "$PROFILE_DIR/airootfs/usr/share/WizardKit/"
tar xaf "$ROOT_DIR/setup/ufd/macOS-boot-icon.tar" -C "$PROFILE_DIR/airootfs/usr/share/WizardKit" tar xaf "$ROOT_DIR/setup/ufd/macOS-boot-icon.tar" -C "$PROFILE_DIR/airootfs/usr/share/WizardKit"
cp "$ROOT_DIR/images/rEFInd.png" "$PROFILE_DIR/airootfs/usr/share/WizardKit/EFI/Boot/rEFInd.png" cp "$ROOT_DIR/images/rEFInd.png" "$PROFILE_DIR/airootfs/usr/share/WizardKit/EFI/Boot/rEFInd.png"
cp "$ROOT_DIR/images/Syslinux.jpg" "$PROFILE_DIR/airootfs/usr/share/WizardKit/syslinux/syslinux.jpg" cp "$ROOT_DIR/images/Syslinux.png" "$PROFILE_DIR/airootfs/usr/share/WizardKit/syslinux/syslinux.png"
echo "Copying Memtest86+ files..." echo "Copying Memtest86+ files..."
rsync -aI "/boot/memtest86+/memtest.bin" "$PROFILE_DIR/airootfs/usr/share/WizardKit/syslinux/" rsync -aI "/boot/memtest86+/memtest.bin" "$PROFILE_DIR/airootfs/usr/share/WizardKit/syslinux/"
@ -128,7 +128,8 @@ function update_live_env() {
label="${KIT_NAME_SHORT}_LINUX" label="${KIT_NAME_SHORT}_LINUX"
# Boot config # Boot config
cp "$ROOT_DIR/images/Syslinux.jpg" "$PROFILE_DIR/syslinux/splash.jpg" cp "$ROOT_DIR/images/Syslinux.png" "$PROFILE_DIR/syslinux/splash.png"
sed -i -r "s/___+/${KIT_NAME_FULL}/" "$PROFILE_DIR/airootfs/usr/share/WizardKit/syslinux/syslinux.cfg"
# MOTD # MOTD
sed -i -r "s/KIT_NAME_SHORT/$KIT_NAME_SHORT/" "$PROFILE_DIR/profiledef.sh" sed -i -r "s/KIT_NAME_SHORT/$KIT_NAME_SHORT/" "$PROFILE_DIR/profiledef.sh"
@ -196,7 +197,7 @@ function update_live_env() {
# Wallpaper # Wallpaper
mkdir -p "$PROFILE_DIR/airootfs/usr/share/wallpaper" mkdir -p "$PROFILE_DIR/airootfs/usr/share/wallpaper"
cp "$ROOT_DIR/images/Linux.jpg" "$PROFILE_DIR/airootfs/usr/share/wallpaper/burned.in" cp "$ROOT_DIR/images/Linux.png" "$PROFILE_DIR/airootfs/usr/share/wallpaper/burned.in"
# udevil # udevil
mkdir -p "$PROFILE_DIR/airootfs/media" mkdir -p "$PROFILE_DIR/airootfs/media"

View file

@ -195,10 +195,10 @@ ln -s /Volumes/RAM_Disk/Logs "${WK_PATH}/var/root/Logs"
cp -a "${LINUX_DIR}/profile_base/airootfs/etc/skel/.tmux.conf" "${WK_PATH}/etc/tmux.conf" cp -a "${LINUX_DIR}/profile_base/airootfs/etc/skel/.tmux.conf" "${WK_PATH}/etc/tmux.conf"
rsync -aS /usr/bin/{env,killall} "${WK_PATH}/usr/bin"/ rsync -aS /usr/bin/{env,killall} "${WK_PATH}/usr/bin"/
rsync -aS "${MACOS_DIR}/live-macos-startup" "${ROOT_DIR}/scripts/" "${WK_PATH}/usr/local/bin"/ rsync -aS "${MACOS_DIR}/live-macos-startup" "${ROOT_DIR}/scripts/" "${WK_PATH}/usr/local/bin"/
if [[ -f "${ROOT_DIR}/images/macOS-${OS_VERSION:0:5}.jpg" ]]; then if [[ -f "${ROOT_DIR}/images/macOS-${OS_VERSION:0:5}.png" ]]; then
cp -a "${ROOT_DIR}/images/macOS-${OS_VERSION:0:5}.jpg" "${WK_PATH}/usr/local/wallpaper.jpg" cp -a "${ROOT_DIR}/images/macOS-${OS_VERSION:0:5}.png" "${WK_PATH}/usr/local/wallpaper.png"
else else
cp -a "${ROOT_DIR}/images/macOS.jpg" "${WK_PATH}/usr/local/wallpaper.jpg" cp -a "${ROOT_DIR}/images/macOS.png" "${WK_PATH}/usr/local/wallpaper.png"
fi fi
# Unbless # Unbless

View file

@ -30,7 +30,7 @@ rem robocopy /e windows/cbin %OUT_DIR%\.cbin
mkdir %OUT_DIR%\.cbin mkdir %OUT_DIR%\.cbin
copy ..\LICENSE.txt %OUT_DIR%\LICENSE.txt copy ..\LICENSE.txt %OUT_DIR%\LICENSE.txt
copy ..\README.md %OUT_DIR%\README.md copy ..\README.md %OUT_DIR%\README.md
copy ..\images\ConEmu.jpg %OUT_DIR%\.bin\ConEmu\ copy ..\images\ConEmu.png %OUT_DIR%\.bin\ConEmu\
attrib +h %OUT_DIR%\.bin >nul 2>&1 attrib +h %OUT_DIR%\.bin >nul 2>&1
attrib +h %OUT_DIR%\.cbin >nul 2>&1 attrib +h %OUT_DIR%\.cbin >nul 2>&1

View file

@ -1,7 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAgEAhF5u/wn04dSDI1mg6HlbDhmM3orJtu2kZdOTBd+/kvCxdC4h+AuwRSWjbIhGi9s8R3iq64gC6TJ2dX4skJ5fMeYqHMZieGgfXderRiPgMURCDWONLZCWyowBun5qGaXkYQo5VbLUNGXua9+RMXaxoJSCOhxq/6CzdleD6pkNNgL5a1ZarD3wLCNRsS50OmyGytXsYTClAY+F9lTd4VXjKuZpjqGfhA5Xz09Rad3Me2Tsd07cdO3LxDNlr969Q/hEwfZ/g+ePaIO0Z/zFHIO4J6Mnt8POTU1fM99tgqUQHWZRP2A+9OYx7nApdA4IFWVUhNfsVkrgcgYdCLD8U16Cdfwm1i1RiBhlYBfzA+EDjD6cbegaaMCKhsCZMZKQB4LyJTDgjvCrk1Q7dE0Nc1Nq8qD/BMbZkIKQcQH8xrX1hONayUMdOPnpDpi6IP2NIYFTbdJD70JQ8ru+gIDRQJR6g0AwnjMoJYNRKgAtWlcbKQ8YQW/FNREtyUhh8tquCyFbofGUiJxmTbWki7u7VJxRLNSnp+NQhPNT4FUbWy1vPrJi0l3MxuXdG3nZ0Brnggn72tnGcAUOmRTPubNlkhFStqcCM+tAVTHeLwHobdMewKQMWGPt6UXLtEJINqW9nCh1SAOA3cjsZ3ugXLRQQc82rEgTfTp2RtuN+NSf/vgoDbk= 1201 PuTTY Key #Put SSH keys here
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGZqPA7/T3zw8YipngM6vKpxX+IMpwqm2jad+NcJWxC0Hh09oVUalgbO/eWRloQlNgxHTosdIkSp++xnyeEHI3vL0pbko/c0OJbs6vEsHp8ljzt61HKKsscVADCI/ogGwHkPIHENgbiXkBPutEBLYFPtMcJQw6GBIkagW+dTyqEw/8c1cDmTq7AM74tHqgrpYjJy35Nne0K+zGIeqzTJdtg3fkRCN9JH24c3VbYoeIpMlgfxxgRG44DC/o0BRoMr3wWiS/sTrutlluB03vaQWe+4o8p789wq+fDqKhBTisH96RQ1Y1D5eZcf0DngBJtnCLWPG+z6YP0DFW5DYeQRoYNneRkR10RUqN0Eufok/UMNnxWcW+TztTxO5JSucXt0MYpLsWO1U7B22MR1W7oVlbznUrVENSQGRqyOS4PdqmI4faF0vEKis68S7NaXg88zwBr612HwFBianXvvxhkyEKv8ECbeXSvHcIajepdzbGLwaussDQiNv8vyX/8o3K7Si/ktdgIK4UzfaMq7G0vX1KEvuKAfMPYn4z0zuc/ahxTTfvHqfO+cnYgH8Pnzi1DGziRveeKIFT4OKOP5C3wHntsfv1CStK/LXhH3j4PNRuet0Fm5+jUJ22DQqlXtFiwFz4E+Bt+67exhm4Out4rAUqCzjDW5+mw0WU2P7u01cT+Q== admin@anaconda.1201.com
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDaJkYcxi7eSpRE7i0n2N2q4CHebPqxYc5eLQ2mDbrLpAIRUSdVjqe8RiVLJ5LKejkGMnWbaiDZ59cLWJhDTfqZFCRexVQkA9v8MVHUWINzxqfJFOnz7dKXP0PmggDbuKMIUkY65KtOLC2Cnqs3epmysQxrSf33W3IqPwL2XjJQXRbFLd4UG1bSZtBAAF3C/i7hBzb2iQPIpr1v1rCrW6gVhrgyoN7ZTfMqcKHnTPsUn95Qtyp6NUYKh4ctrPmTcTHFfwmgQiAYQ6jMcH8bNBf1XDmJuEIwaILYVzzrWOXlx84rqaEUdeRbvd4S/G9FcRt9eO3o+jD4hupdyvIDCttP3ct6yeWQVotreZE/5OVwfxr0BNXPBmTiyF2c6cRrMlXpQMoRQyjnD5Eua0VEUJzj2ows5APGmciJudDw4rXoRLOZRX/nDuXqgNJJ6q+MgRx3hor/etbV2mXcmSMdoI8DuybFzHuEb9GRwYhLxzDdDe+zmevx0rl01W8EJImqgrTwMblADbM4VHpC974d5w2s9uoXJg1NY4UVkSjJdK0De0kyGh8hK2pqTqCBh1OznB43Yla5PwM+05JIMySVRuivvZ1+tBh8ZURzIbFFIrdgNiZIooI4hA/m8qz4CTWIxKWstXR08eOufIbjV+R6VJjEssvLmgYw93rUJ4WbQNbaiw== twoshirt@anaconda.1201.com
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDKjMOQl/re3sTBUFeNEYOt2DsZLEeQiFfzu1tFlMUGcLnpjpA/Zhp0qpBBVhFS9j8p7s1838v+HS5dN1yLhWjjdM7EbzIwczJTe83D52GPwOisVWyL6cdB++bynl6LQRGe/dZu19uO1nX3sVU8gime4KLMasihCWw4XATQC1fFB6CnTu62ps3jO1n1MnjsX4odjC/Und5xD39trGU5aO4l17Zns+OBCLwsERofvkb02XZaqmcAXie+/o3jS43dANJXhtsqk0dRiNONccsnshFP90ujR8mPQU1h15fppwI/mRJ4GwNkA7XMcHghj/KqOIptSf0w8bBBQ7RTJATG0nPw45BJHu1bHZEbECpHBbM6TIIKgYVm7cJs54kf0yN66UWzDOfzqjL8Mo/Gmu/bHKhCg+gEQg4xFKbBtB8adMCrj8q9JZ2x8a9l9wcUKpSN8DM4SWUP4F4uXe0cAHhrMIAOfCFcX1lBsilNxidE5fZUGW3WJJ3+WUkeWnfifEBDZpSV0BCWjHKz1R/tQeJfBS8sjjGSheIJZBxLYc+6IUdL77Zjy2byzQ7m4PfaMDI3CxXhkhEeC4AnCn6/quRLFuChrvRYLC9UDrrmMMB4xo4SGsWKnM83orD1UbtbsqfJaHLBGF3WgII77uDvmUnwZJmaHCYM4N97lkoqNgC4VAEd4Q== twoshirt@dinraal.2shirt.work
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDa9lUItn9ooNu2FrOUMHytoQlAtH0x+tcszmt0ZNZN6DU6H87bFtZcB3gaanU4Kr3mIaJBUSjgbafJHiiYXvbzt+Gx7iQxAsXQkOfzArKHu9WV5OIHBATtONI3pokF3crlHJVR7hjY1lvP07c+F/Oygt0Hjd2RcslxPjqSVBRTl6z8Yu7r/BbkNiZRJfuJW/hNg+tGYRFHM6uy0qZ3fOwUmQ+MqCXm3V9K0A/e8dNyHEkW2v4CuAW8QP7gjA4nT/jrkJAA98firwSJdMgpZIFSuzf48D/AhDGuE55W5m/ItpMjVO7oDvqrEygyMO1ugxrIVqj7EM3HtArOtsMDueyCcTd5tWs/e80Y7/E4iB4bQQGo9ifA9oPqywAfETqvO0VUtjO9cMt7/Xnhc9qLxwKstr9UX9ORFm8hiaq9jVPkbwpmBIdfBpk4YwbY/1ObRQmOgsIuq5otPMrw17ymrJCagxk74BAOiMoUte5fhBezMl0ZlsBA9yWw63PHchWCyXQ0o195A3e04Api2FYx2er3mA28vhj8WkXwsuF1C2nypztn0GlmVoDKK5Z//slefLWAYq+V7AyGW3x2zYi4WpBiyM+FmG4ABVw5alxJjkvsoC1Ho7kKjQAdARhy+HsveSYLPPaSsnFIexpTjPlNlSquZG2rXxWLquo40CMMbRdJbw== twoshirt@farosh
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCh+EGFqMfJmfOQvfqfv/ilDL80krdZNdBvJ8xFWYMRy8z4z1Ih7BlCfkGObyxKLIYJgkPPhN3xLWWpN78ME7YeeUF1Ly84oIJABvK9CuZPDcUwDhpLoBli/qxoF/YyLufqtECKaQpDWt+iB1522XcVUerRFMGX3brE+b49gmXSuCC0lPijqcWV9KZVNKj9EStgY5b4DTjINOoiHIHBFsov37wt/+qJn7th+EDqcPndIcTQ31VbcGqEWtfNQWPGVmH898Q2XLvCbN+seBK9ZmttcXzWlS70qUr4xHrZCnegwMnBKPQ0sVo7NtC5DQHrN4I4NyvY0fr/o16nIphfaNZgy/CDo1WauBjJlbQ9MRcFVSPhL3LeyiBtBwC6Nr/qR+6Xjf/BV0ybw8MjXukZxKfW9xJsL631Xrk1/oYJnNKL5IyhIsVFRtfwcB4YfPEZbrdBxrse0Ypchgvpl5srTQfEL5ZTf/ozWCUST2Bf+dDMYrdqudg5zhPU6prneaEzlBxiLqGwBId9bzIqqHD+fE4a90oOX/WpZW0PFApnXGkPahaTPHGOAMfM+DmXdr2e5emdRZp+wgZU1EXwUzKVwE4fnj6HhuUlAt18LBZ/WLHMDGryuKemW0z9X9CUhzgkAmzgRCJ09ZXLYcwuyLwv9HnMQUa6RlWK69s3T/m+Y8fzKQ== twoshirt@mipha.2shirt.work
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC7MHLxoozeHOmFpjIGaXi8m0LuHZb4eUjX6x50XUlP81oVHbl4iAqw/N4r4j+Lg+lga7xcTeBMNWsa/tGxYkAfF8TP8M0S8XEMOR04r9iA4V6wQ8nGhv99y24NZMJPfiseNjDEwoPYPP5+zpCNfvxeMXxz3EhLFwzSp1dz+f1tBcDd8OtxDAGPn1cIM4Ms9gmIv3/HxOinW4IHxEbsZUWtKGPumsuIYwObfBYZPK0WtK23LPYqtReLYbzQmsf5SPpJAx20mwtZm8Y+LBXAKuxWEKzlF4l/ubqjTfMff5t+8FPEyCgRwm8VcMqxA1b04Pkcuq08AuooJuLKHOJ+aSyDWGXrSf3RlUAx8GafYOpZrcMEhQ0Q1a0XqZNTUXrf13qVPBqnKV8asOXrYIP56Z3zp8qoHasT8t5PbYrwvtV7weW/X61cQf/nn1B/BJbJr/rrOOxdqRCsH44OlElrhjwHsuzQbMc4U4wwoazlMmfNKsKUaoBmLPDTfmMBwnCuBrngNY9JsSB778fqwxsyLSfDTcIDW2kGuQ4P/4u5cKNw/q8Zo15Ghw2327kyTK9e4EGPCpBk+s5832FWS0uJLV269R5aZ5dYzYU13djfVOw2ypZTdr55nTlIH/31iSmJS0PBKyRLa2a7YxErt37W3f2U1eUt+j91LeaP5cMKHkWAsw== twoshirt@naydra.2shirt.work

View file

@ -1,3 +1,3 @@
# Put known WiFi networks in the form: # Put known WiFi networks in the form:
1201computersXI:::wifipassword #SSID-5G:::Password
1201computersXI-5g:::wifipassword #SSID-2G:::hunter2

View file

@ -1,12 +1,14 @@
aic94xx-firmware
ddrescueview-bin ddrescueview-bin
hardinfo-gtk3
hfsprogs hfsprogs
iwgtk iwgtk
memtest86-efi memtest86-efi
mprime-bin mprime-bin
opensuperclone-git
pipes.sh pipes.sh
python-mariadb-connector
python-smbus2
smartmontools-svn smartmontools-svn
ttf-font-awesome-4 ttf-font-awesome-4
udevil udevil
wd719x-firmware
wimboot-bin wimboot-bin

View file

@ -1,5 +1,4 @@
adobe-source-han-sans-otc-fonts aic94xx-firmware
adobe-source-sans-fonts
alsa-utils alsa-utils
amd-ucode amd-ucode
antiword antiword
@ -9,9 +8,12 @@ arch-install-scripts
base base
bc bc
bind bind
bluez
bluez-utils
bolt bolt
btrfs-progs btrfs-progs
cbatticon cbatticon
chntpw
cmatrix cmatrix
colordiff colordiff
conky conky
@ -23,7 +25,6 @@ ddrescueview-bin
device-mapper device-mapper
diffutils diffutils
dkms dkms
dmde
dmidecode dmidecode
dmraid dmraid
dos2unix dos2unix
@ -38,12 +39,16 @@ f2fs-tools
fatresize fatresize
feh feh
ffmpeg ffmpeg
firefox
foot-terminfo foot-terminfo
gnome-keyring gnome-keyring
gnu-netcat gnu-netcat
gparted gparted
gpicview
gptfdisk gptfdisk
grub grub
gsmartcontrol
hardinfo-gtk3
hexedit hexedit
hfsprogs hfsprogs
htop htop
@ -65,14 +70,12 @@ libxft
linux linux
linux-firmware linux-firmware
linux-firmware-marvell linux-firmware-marvell
linux-headers
lm_sensors lm_sensors
lsscsi lsscsi
lvm2 lvm2
lzip lzip
man-db man-db
man-pages man-pages
mariadb-clients
mdadm mdadm
mediainfo mediainfo
memtest86-efi memtest86-efi
@ -89,35 +92,31 @@ nano
nbd nbd
ncdu ncdu
ndisc6 ndisc6
netsurf
nfs-utils nfs-utils
nmap nmap
noto-fonts
noto-fonts-cjk
ntfs-3g ntfs-3g
numlockx numlockx
nvme-cli nvme-cli
open-iscsi open-iscsi
openbox openbox
openssh openssh
opensuperclone-git
otf-font-awesome-4 otf-font-awesome-4
p7zip p7zip
papirus-icon-theme papirus-icon-theme
parted parted
pcmanfm
perl perl
picom picom
pipes.sh pipes.sh
pv pv
python python
python-docopt
python-mariadb-connector
python-matplotlib
python-packaging
python-prompt_toolkit python-prompt_toolkit
python-psutil python-psutil
python-pytz python-pytz
python-requests python-requests
qemu-guest-agent qemu-guest-agent
ranger
refind refind
reiserfsprogs reiserfsprogs
rfkill rfkill
@ -127,10 +126,10 @@ rxvt-unicode
rxvt-unicode-terminfo rxvt-unicode-terminfo
sdparm sdparm
smartmontools-svn smartmontools-svn
smbclient
sof-firmware sof-firmware
speedtest-cli speedtest-cli
spice-vdagent spice-vdagent
squashfs-tools
st st
sudo sudo
sysbench sysbench
@ -141,6 +140,7 @@ systemd-sysvcompat
terminus-font terminus-font
testdisk testdisk
texinfo texinfo
thunar
tigervnc tigervnc
tint2 tint2
tk tk
@ -161,9 +161,12 @@ usb_modeswitch
usbmuxd usbmuxd
usbutils usbutils
util-linux util-linux
veracrypt
vim vim
virtualbox-guest-utils virtualbox-guest-utils
volumeicon volumeicon
wd719x-firmware
wezterm-terminfo
which which
wimboot-bin wimboot-bin
wimlib wimlib
@ -172,6 +175,7 @@ xarchiver
xf86-input-libinput xf86-input-libinput
xf86-video-amdgpu xf86-video-amdgpu
xf86-video-fbdev xf86-video-fbdev
xf86-video-nouveau
xf86-video-qxl xf86-video-qxl
xf86-video-vesa xf86-video-vesa
xfsprogs xfsprogs

View file

@ -1,27 +0,0 @@
pkgname=dmde
pkgver=4.0.6.806
pkgrel=1
pkgdesc="DMDE is a powerful tool for data searching, editing, and recovery on disks"
arch=('x86_64')
url="https://dmde.com/"
license=('custom')
makedepends=(unzip)
replaces=($pkgname)
source=("dmde-mine.ini")
source_x86_64=("https://dmde.com/download/dmde-4-0-6-806-lin64-gui.zip")
sha512sums=('1f11bee45672f507c4401031811273e8d6b103358bd0b2bec1cd6ecca1782ec23c0af3f81c23c5fcedd4380169fdea0eab60909ce59e17053029608ba324586a')
sha512sums_x86_64=('b2d20d13ced8780baa46a435c772af16b7b16b38c7daf65d2edc55f3560bb2ab417390ffd55cea6f424d4237ff911baa5547a45a9c9e279689fb1fd8fb82fc99')
package() {
cd $srcdir
mkdir -p ./usr/share/dmde/ $pkgdir/usr/bin/ $pkgdir/usr/share/applications/ $pkgdir/usr/share/pixmaps/
unzip ./dmde-4-0-6-806-lin64-gui.zip -d $srcdir/usr/share/dmde/
cp $srcdir/dmde-mine.ini $srcdir/usr/share/dmde/dmde.ini
cp -r usr $pkgdir
chmod +x $pkgdir/usr/share/dmde/dmde
chmod +x $pkgdir/usr/share/dmde/dmde-su
ln -sr /usr/share/dmde/dmde $pkgdir/usr/bin/dmde
ln -sr /usr/share/dmde/dmde-su $pkgdir/usr/bin/dmde-su
}

View file

@ -1,482 +0,0 @@
;
;dmde.ini - DMDE for Linux Initialization File
;
setupsign=5e440968
lickey=
activatekey[0]=
portablekey=
activatedev=
lngfile=\en
;interface language file
loadlngfiles=%appres%/locals/*.lng
;load language files
loadcptables=%appres%/locals/*.tbl
;load code page tables for Unicode-Char,Translit,Upcase conversions
editortemplates=%appres%/template.txt
;editor template file(s) - wildcards are possible
[CONSOLE]
autosizemaxy=
autosizemaxx=
[GUI]
mainwndpos=
;x,y,cx,cy
dialogfontsize=
;dialogfont=
;scale percents:
dialogwidth=100
dialogheight=100
;editorfont=
editorfontsize=
;editorfontbold=0
;previewfiles=*.bmp;*.gif;*.ico;*.jfif;*.jpe;*.jpeg;*.jpg;*.png;*.tif;*.tiff
[COMMON]
mainwndsplit=-1
;usecodepage=1252
;manually select used code page (CP)
;oemcodepage=850
;manually select source OEM CP for DOS names
applylngcodepage=1
;=1: use codepages from the lng file
translitenable=1
;=1: transliterate symbols outside current code page
viewtranslit=0
;=1: transliterate symbols on the screen
rtlapply=0
;=1: use rtl ui for right-to-left languages
rtldismiss=0
;=1: turn off rtl string mirroring if terminal already supports it
shellopen=xdg-open
stephints=0
;=1: activate step-by-step hints
;=0: deactivate step-by-step hints
popuphints=
;=1: activate popup hints (mouse over)
;=0: deactivate popup hints (mouse over)
kilobyte=1000
;kilobyte=1024
displayCHS=0
editorhexcolsnum=16
;editorhexcolsnum=N - maximum columns number for hex editor
;editorhexcolsnum=0: auto
editorhexgroupsize=8
startup=p
;startup=p - open physical drives at startup
;startupimage=filename - open disk image
;startupdev=devicestring - open device at startup
;startupraid=raid.ini - load RAID from "raid.ini" at startup
;startupcopy=copy.ini - load "Copy sectors" from "copy.ini" at startup
readonly=0
;=1 - globally disable writing to devices
devopen_editmode=0
;=1 - default edit mode when open using dialog box
devload_editmode=0
;=1 - default edit mode when load from log-file
devapply_ioparamsopt=0
;=1 - option to change i/o params when applying changes
devapply_editmodeopt=0
;=1 - option to set edit mode when applying changes
partapply_ioparamsopt=1
;=1 - option to change i/o params when applying partitioning changes
partapply_editmodeopt=1
;=1 - option to set edit mode when applying partitioning changes
queryclosedevice=1
;query when closing device
showpartitions=1
;show partitions when open device
;editmode=1
;enable disk editor edit mode by default
;Redefine default device enumeration:
;Linux device enumeration by FindFile:
;enumdevs0=0,"/dev/fd*",0,"",""
;enumdevs1=0,"/dev/sd?",0,"",""
;enumdevs2=0,"/dev/hd?",0,"",""
;enumdevs3=0,"/dev/ub?",0,"",""
;enumdevs4=0,"/dev/md*",0,"",""
;enumdevs5=0,"/dev/scd*",0,"",""
;include /dev/disks/by-id & /dev/disks/path enumeration:
;enumdevs_byid=1
;devusbdetectex=
ckeventtick=1
;ckeventtick=0 check event every time (slow down operations)
;ckeventtick=1 check event every 55ms only
mousehandler=1
;mousehandler=0 - no mouse
;mousehandler=1 - use mouse
inet=2
;1:read hosts file and access DNS servers
;2:use getaddrinfo function for internet connections
[IO]
;deviopopupontry=3
;additional tries num. before displaying i/o dialog box when errors are auto ignored
deviopopupdelay=2000
;additional ms delay before displaying i/o dialog box when errors are auto ignored
queryioerrors=1
querydrivenotready=1
retries=1
;retries=N
; read/write retries number for sector with errors (N=0..999)
; if N=0 read block only once (the rest of the block after error is zeroed)
seekretries=1
;seekretries=N - read/write auto retries number if sector not found (N=0..64)
deviojump=0
;deviojump=N
;jump over N sectors after I/O error
deviojumpreturn=0
;deviojumpreturn=1 - reverse read after jump
;deviojumpreturn=0 - do not reverse read
devioskipfiller=0x50494B53
;fill skipped sectors with hex values
deviobadfiller=0x20444142
;deviobadfiller=0x20444142 - fill bad sectors with hex values
;deviobadfiller= - do not fill bads
buffer=131072
;buffer=N - disk data transfer block size (N=4096..1048576)
dblbuffer=2097152
;dblbuffer=N -
;(N=4096..16777216)
diskcache=16777216
;diskcache=N - disk cache size
;(N=4096..33554432)
;diskmaxmodsize=209715200
;max size for pending modifications
devreset_chkserial=1
;check device serial number when resetting handle
devreset_trynewdevs=1
;devreset_trynewdevs=0 ;never try different device names
;devreset_trynewdevs=1 ;try different names when needed and serial can be checked
;devreset_trynewdevs=2 ;always try different names (not recommended)
devlistresetonerror=0
;devlistresetonerror=1 ;Update the list of devices on a device i/o error
vhd_writeenabled=0
;vhd_writeenabled=1 ;enable partial write support for virtual disk image files (vhd/vmdk/etc.)
[DR]
maxrecoverdepth=1024
maxfilerenames=16
maxdirrenames=16
splitfilesize=0
[FullScan]
showvolumesnum=2048
[FAT]
fatdirentryaccept=1
;FAT directory entry validation level:
;fatdirentryaccept=0 - accept reliable entries within reliable sectors only
;fatdirentryaccept=1 - accept any entries within reliable sectors
;fatdirentryaccept=2 - accept reliable entries within any sector
;fatdirentryaccept=3 - accept any entry within any sector
fatinvdirtotree=0
;fatinvdirtotree=0 - add FAT invalid directories to file panel only
;fatinvdirtotree=1 - insert FAT invalid directories into tree also
fatfoundtoroot=1
;fatfoundtoroot=0 - add found root subdirs to a common directory list
;fatfoundtoroot=1 - insert found root subdirectories into $Root
volumeseachblock=4194304
;blocksize for FAT/NTFS volume search
fatmaxvolumes=1024
[NTFSSearch]
ntfsmaxvolumes=1024
ntfsmaxprocstarts=1024
ntfsmaxmftruns=0xffffff
ntfsmaxprocmftruns=32768
ntfsmaxindxrecs=0
;ntfsthoroughsearch=0 - may skip small inserted MFT fragments
;ntfsthoroughsearch=1 - more thorough NTFS Search
ntfsthoroughsearch=1
;ntfsmftshift=0 - process sector aligned MFT records only
;ntfsmftshift=N - process MFT shifted by multiple of N bytes (shifted, traced, etc.)
ntfsmftshift=1
;ntfsrestrunlen=N - small MFT runs (N or less records) to be partially processed only
ntfsrestrunlen=4
[IFACE]
filecachesize=32768
;max. number of cached file panel items
maxtreechilditems=2048
;max child items number displayed in tree (GUI)
charclustermap=]xR./=><<!x|[
;console UI:
dlgframe=1
dlglistframe=0
;console chars:
charraidbtns=+,30,x,^,v,*
charmarks=<>x*\u25BC\x20\x20
charbtnshadow=\x20\x20\x20
charwframe=\u250C\u2500\u2510\u2502\u2502\u2514\u2500\u2518
charframe=\u250C\u2500\u2510\u2502\u2502\u2514\u2500\u2518
charvscroll=\u25B2\u25BC\u2592\u25A0
charhscroll=<>\u2592\u25A0
chartree=\u251C\u2514
charprogress=\u2588\u2592
; remove "#" to use color scheme
editorcolors#=\ ;color scheme
1F,\ ;0 Default
1B,\ ;1 Caption
0F,\ ;2 Focused
70,\ ;3 Selected block
0F,\ ;4 Selected object
0E,\ ;5 Modified
1A,\ ;6 Read Error
1A,\ ;7 Title
1C,\ ;8 Invalid Value
03,\ ;9 selected input
07,\ ;10 grayed
1B,\ ;11 selected not focused
1A,\ ;12 Caption Raid Disk #1
17,\ ;13 Caption Raid Disk #2
1C ;14 Invalid Input
editorcolors#=\ ;gray scheme
8F,\ ;0 Default
8B,\ ;1 Caption
0F,\ ;2 Focused
70,\ ;3 Selected block
0F,\ ;4 Selected object
0E,\ ;5 Modified
8A,\ ;6 Read Error
8A,\ ;7 Title
8C,\ ;8 Invalid Value
03,\ ;9 selected input
07,\ ;10 grayed
8B,\ ;11 selected not focused
81,\ ;12 Caption Raid Disk #1
82,\ ;13 Caption Raid Disk #2
8C ;14 Invalid Input
editorcolors#=\ ;blue scheme
1B,\ ;0 Default
1E,\ ;1 Caption
3F,\ ;2 Focused
30,\ ;3 Selected block
0F,\ ;4 Selected object
0E,\ ;5 Modified
1A,\ ;6 Read Error
1A,\ ;7 Title
1C,\ ;8 Invalid Value
3F,\ ;9 selected input
07,\ ;10 grayed
1F,\ ;11 selected not focused
1A,\ ;12 Caption Raid Disk #1
17,\ ;13 Caption Raid Disk #2
1C ;14 Invalid Input
;console text attributes:
; xxxx:
; x... - text background color for shortcut
; .x.. - text color for shortcut
; ..x. - text background color
; ...x - text color
; 0=Black 1=Blue 2=Green 3=Cyan 4=Red 5=Magenta 6=Brown 7=ltGray
; 8=dkGray 9=ltBlue A=ltGreen B=ltCyan C=ltRed D=ltMagenta E=Yellow F=White
; remove "#" to use color scheme
colors#=\ ;color scheme
3E30,\ ;menubox
3E30,\ ;menubar
0E0E,\ ;highlighted menu item
3838,\ ;disabled menu item
2E20,\ ;button
2828,\ ;disabled button
2E2F,\ ;focused button
2E2B,\ ;default button
0030,\ ;list item
002F,\ ;focused list item
003F,\ ;selected list item
003E,\ ;marked list item
002E,\ ;marked focused list item
002F,\ ;active dialog caption
002F,\ ;not active dialog caption
001F,\ ;input
003F,\ ;input selection
0078,\ ;disabled input
00FE,\ ;modified input
0013,\ ;scrollbar
3E30,\ ;cluster
3F3F,\ ;cluster highlighted
7878,\ ;cluster disabled
001F,\ ;active window frame
0017,\ ;not active window frame
001F,\ ;active window title
0017,\ ;not active window title
001E,\ ;column title
1E1F,\ ;item in window
002F,\ ;focused item in window
001A,\ ;selected item in window
001E,\ ;marked item in window
003E,\ ;focused marked item in window
3E30,\ ;status line
7E70,\ ;dialog box text
3F3B,\ ;dialog column header
1F1E,\ ;window column header
0071,\ ;group/tab frame color
1E1F,\ ;window button
1818,\ ;window disabled button
2E2F,\ ;window focused button
1E1A,\ ;window default button
1E1F,\ ;window cluster
1E1F,\ ;window cluster highlighted
1818 ;window cluster disabled
colors#=\ ;gray scheme
7470,\ ;menubox
F4F0,\ ;menubar
040F,\ ;highlighted menu item
7878,\ ;disabled menu item
F4F0,\ ;button
F7F7,\ ;disabled button
F4F0,\ ;focused button
F4F0,\ ;default button
008F,\ ;list item
000F,\ ;focused list item
008B,\ ;selected list item
008E,\ ;marked list item
000E,\ ;marked focused list item
00F0,\ ;active dialog caption
00F8,\ ;not active dialog caption
008F,\ ;input
0080,\ ;input selection
0078,\ ;disabled input
008E,\ ;modified input
008F,\ ;scrollbar
7470,\ ;cluster
7470,\ ;cluster highlighted
7878,\ ;cluster disabled
008F,\ ;active window frame
008F,\ ;not active window frame
008F,\ ;active window title
008F,\ ;not active window title
008B,\ ;column title
8B8F,\ ;item in window
000F,\ ;focused item in window
008B,\ ;selected item in window
008E,\ ;marked item in window
000E,\ ;focused marked item in window
0070,\ ;status line
7470,\ ;dialog box text
8F8B,\ ;dialog column header
8F8B,\ ;window column header
0070,\ ;group/tab frame color
8E8F,\ ;window button
8787,\ ;window disabled button
0E0F,\ ;window focused button
8E8B,\ ;window default button
8E8F,\ ;window cluster
8E8F,\ ;window cluster highlighted
8787 ;window cluster disabled
colors#=\ ;blue scheme
3F30,\ ;menubox
3F30,\ ;menubar
0F0F,\ ;highlighted menu item
3838,\ ;disabled menu item
7470,\ ;button
7878,\ ;disabled button
3F3F,\ ;focused button
3F3F,\ ;default button
001B,\ ;list item
003F,\ ;focused list item
001F,\ ;selected list item
001E,\ ;marked list item
003E,\ ;marked focused list item
003F,\ ;active dialog caption
003F,\ ;not active dialog caption
001F,\ ;input
003F,\ ;input selection
0078,\ ;disabled input
00FE,\ ;modified input
001F,\ ;scrollbar
7470,\ ;cluster
7470,\ ;cluster highlighted
7878,\ ;cluster disabled
001B,\ ;active window frame
001B,\ ;not active window frame
001F,\ ;active window title
001B,\ ;not active window title
001E,\ ;column title
1E1B,\ ;item in window
003F,\ ;focused item in window
001F,\ ;selected item in window
001E,\ ;marked item in window
003E,\ ;focused marked item in window
0030,\ ;status line
7470,\ ;dialog box text
1B1E,\ ;dialog column header
1F1E,\ ;window column header
0073,\ ;group/tab frame color
1E1B,\ ;window button
1818,\ ;window disabled button
3F3F,\ ;window focused button
1E1F,\ ;window default button
1E1B,\ ;window cluster
1E1B,\ ;window cluster highlighted
1818 ;window cluster disabled

View file

@ -47,7 +47,7 @@ prepare() {
patch --directory="$_sourcedir" <"$srcdir/$(basename ${file})" patch --directory="$_sourcedir" <"$srcdir/$(basename ${file})"
fi fi
done done
sed 's/Liberation Mono/Hack/;s/float alpha = 0.8;/float alpha = 0.55;/' "$_sourcedir/config.def.h" > "$_sourcedir/config.h" sed 's/Liberation Mono/Hack/;s/float alpha = 0.8;/float alpha = 0.85;/' "$_sourcedir/config.def.h" > "$_sourcedir/config.h"
# This package provides a mechanism to provide a custom config.h. Multiple # This package provides a mechanism to provide a custom config.h. Multiple
# configuration states are determined by the presence of two files in # configuration states are determined by the presence of two files in

View file

@ -15,18 +15,21 @@ syslinux
tigervnc tigervnc
wpa_supplicant wpa_supplicant
# hardinfo-gtk3 / opensuperclone-git
cmake
# iwgtk # iwgtk
gtk4 gtk4
meson meson
qrencode qrencode
scdoc scdoc
# python-mariadb-connector
mariadb-libs
# smartmontools-svn # smartmontools-svn
subversion subversion
# udevil # udevil
gettext gettext
intltool intltool
# wd719x-firmware
lha

View file

@ -1,3 +0,0 @@
0.0 0 0
0
LOCAL

View file

@ -1,36 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIGTzCCBDegAwIBAgIBfDANBgkqhkiG9w0BAQsFADCBsDELMAkGA1UEBhMCVVMx
DzANBgNVBAgTBk9yZWdvbjERMA8GA1UEBxMIUG9ydGxhbmQxHTAbBgNVBAoTFDEy
MDEgQ29tcHV0ZXIgUmVwYWlyMSMwIQYDVQQLExoxMjAxIENlcnRpZmljYXRlIEF1
dGhvcml0eTEVMBMGA1UEAxMMMTIwMSBSb290IENBMSIwIAYJKoZIhvcNAQkBFhNt
YW5hZ2VtZW50QDEyMDEuY29tMB4XDTE4MDgyMDA2MDEwMFoXDTM4MDgyMDA2MDEw
MFowgbAxCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZPcmVnb24xETAPBgNVBAcTCFBv
cnRsYW5kMR0wGwYDVQQKExQxMjAxIENvbXB1dGVyIFJlcGFpcjEjMCEGA1UECxMa
MTIwMSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFTATBgNVBAMTDDEyMDEgUm9vdCBD
QTEiMCAGCSqGSIb3DQEJARYTbWFuYWdlbWVudEAxMjAxLmNvbTCCAiIwDQYJKoZI
hvcNAQEBBQADggIPADCCAgoCggIBANGYohJk5/CC/p14R7EpnhdEUF7Wvlnt8yuF
dtuyStlIGkLxPMlj9hQfoLDplQqlKBefTaI3WwrI/Hndso+jStLKgtRWRdyNB34K
AWqT04zXYGicdi3fqaMhEC4SPyX1tRXU2e9kjtIJ21AZx2F40NUjfOMKLVymZgXm
gkG1oA/BSzE8vIidrd/lJPwo0u+EYFa87y+9SHS93Ze1AVoTVqUzSMkjqt+6YIzJ
4XBD7UBvps0Mnd18HMUlXHFXusUL1K921W3wDVcMlNIIA8MJjQk+aVS/1EGSn+81
C+r40x64lYkyh0ZUAHkVXUC/BUfa0SKx1Nfa4mSvtyPnUCb7Dir8MkTDKgopGCok
KmW+VvE2H8AEPCbcctFmhdip19laYxzyDhZ5wiQN6AOg64cWvDf6/uT9hyPvxkj1
ps5vWElryzawTE7h1BI8liMqwsG1Y7cc6D0PABxPsp4iR8pde0oZtpLnEvejRodo
zz3BGvZjq+pHtRMjL+yiDtdAL+K+7/e7gNCQBIGsphahWIOf7TczWVgMNclTNxl3
WZjKkOEs7j+prRTDvffV6H32+Tk5TwgMsfvnY4a37CkJ0L0d1JhWj9wO+gESfg3W
8yvt3hfcj3NOUMJWhJstqlIeX8dj7vVcMhjNvYJxabJmJgk+DNlHe55fXDGJ1CLO
E0EbRTyBAgMBAAGjcjBwMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFM+hXjFx
6BldZFBQW1Pn/Yp3vbw+MAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAAcw
HgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOC
AgEALWcnu3auMSnSSF/kOiLvJ4RAnHZebGYNcUWM14u1K1/XtTB7AFzQIHX7BcDH
m/z4UEyhl9EdR5Bgf2Szuk+8+LyGqcdAdbPoK+bmcwwL8lufDnlIYBThKIBfU2Xw
vw41972B+HH5r1TZXve1EdJaLyImbxmq5s41oH7djGC+sowtyGuVqP7RBguXBGiJ
At1yfdPWVaxLmE8QFknkIvpgTmELpxasTfvgnQBenA3Ts0Z2hwN4796hLbRzGsb8
4hKWAfQDP0klzXKRRyVeAueXxj/FcNZilYxv15MqMc4qrUiW0hXHluQM1yceNjNZ
SE4Igi1Ap71L4PpgkHIDfZD908UexGGkql+p4EWrpnXUYWTa0sHg1bFKQntgpyFg
86Ug0Q7ZNhImENzeigZknL0ceIdaNUCs7UPrkqpUSJR2yujp1JC3tX1LgKZw8B3J
fQx/8h3zzNuz5dVtr1wUJaUD0nGhMIRBEXb2t4jupEISSTN1pkHPcbNzhAQXjVUA
CJxnnz3jmyGsNCoQf7NWfaN6RSRTWehsC6m7JvPvoU2EZoQkSlNOv4xZuFpEx0u7
MFDtC1cSGPH7YwYXPVc45xAMC6Ni8mvq93oT89XZNHIqE8/T8aPHLwYFgu1b1r/A
L8oMEnG5s8tG3n0DcFoOYsaIzVeP0r7B7e3zKui6DQLuu9E=
-----END CERTIFICATE-----

View file

@ -30,7 +30,6 @@ alias rmdirs='find -depth -mindepth 1 -type d -exec rmdir "{}" --ignore-fail-on-
alias rs='rsync -avhPS --stats --exclude-from="$HOME/.rsync_exclusions"' alias rs='rsync -avhPS --stats --exclude-from="$HOME/.rsync_exclusions"'
alias rsz='rsync -avhzPS --stats --exclude-from="$HOME/.rsync_exclusions"' alias rsz='rsync -avhzPS --stats --exclude-from="$HOME/.rsync_exclusions"'
alias sdu='sudo du -sch --apparent-size' alias sdu='sudo du -sch --apparent-size'
alias set_lp8550_slope='sudo set_lp8550_slope.py'
alias srmdirs='sudo find -depth -mindepth 1 -type d -exec rmdir "{}" --ignore-fail-on-non-empty \;' alias srmdirs='sudo find -depth -mindepth 1 -type d -exec rmdir "{}" --ignore-fail-on-non-empty \;'
alias srs='sudo rsync -avhPS --stats --exclude-from="$HOME/.rsync_exclusions"' alias srs='sudo rsync -avhPS --stats --exclude-from="$HOME/.rsync_exclusions"'
alias srsz='sudo rsync -avhzPS --stats --exclude-from="$HOME/.rsync_exclusions"' alias srsz='sudo rsync -avhzPS --stats --exclude-from="$HOME/.rsync_exclusions"'

View file

@ -1,4 +0,0 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by fontconfig.
# For information about cache directory tags, see:
# http://www.brynosaurus.com/cachedir/

Some files were not shown because too many files have changed in this diff Show more