Merge branch 'project-overhaul' into dev

* Linux side is done
This commit is contained in:
2Shirt 2020-01-08 15:47:07 -07:00
commit b101809e42
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
297 changed files with 9316 additions and 2436 deletions

View file

@ -1,63 +0,0 @@
# Wizard Kit: Activate Windows using various methods
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.activation import *
init_global_vars()
os.system('title {}: Windows Activation Tool'.format(KIT_NAME_FULL))
if __name__ == '__main__':
try:
stay_awake()
clear_screen()
print_info('{}: Windows Activation Tool\n'.format(KIT_NAME_FULL))
# Bail early if already activated
if windows_is_activated():
print_info('This system is already activated')
sleep(5)
exit_script()
other_results = {
'Error': {
'BIOSKeyNotFoundError': 'BIOS key not found.',
}}
# Determine activation method
activation_methods = [
{'Name': 'Activate with BIOS key', 'Function': activate_with_bios},
]
if global_vars['OS']['Version'] not in ('8', '8.1', '10'):
activation_methods[0]['Disabled'] = True
actions = [
{'Name': 'Quit', 'Letter': 'Q'},
]
while True:
selection = menu_select(
'{}: Windows Activation Menu'.format(KIT_NAME_FULL),
main_entries=activation_methods, action_entries=actions)
if (selection.isnumeric()):
result = try_and_print(
message = activation_methods[int(selection)-1]['Name'],
function = activation_methods[int(selection)-1]['Function'],
other_results=other_results)
if result['CS']:
break
else:
sleep(2)
elif selection == 'Q':
exit_script()
# Done
print_success('\nDone.')
pause("Press Enter to exit...")
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -1,149 +0,0 @@
#!/bin/env python3
#
# pylint: disable=no-name-in-module,wildcard-import,wrong-import-position
# vim: sts=2 sw=2 ts=2
"""Wizard Kit: UFD build tool"""
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from docopt import docopt
from functions.common import *
from functions.ufd import *
from settings.ufd import *
init_global_vars(silent=True)
# Main section
if __name__ == '__main__':
# pylint: disable=invalid-name
# Set log
try:
global_vars['LogDir'] = '{}/Logs'.format(
get_user_home(get_user_name()))
set_log_file('Build UFD ({Date-Time}).log'.format(**global_vars))
except: # pylint: disable=bare-except
major_exception()
# Header
print_success(KIT_NAME_FULL)
print_standard('UFD Build Tool')
print_standard(' ')
# Check if running as root
if not running_as_root():
print_error('ERROR: This script is meant to be run as root.')
abort(False)
# Docopt
try:
args = docopt(DOCSTRING)
except SystemExit as sys_exit:
# Catch docopt exits
exit_script(sys_exit.code)
except: # pylint: disable=bare-except
major_exception()
try:
# Verify selections
ufd_dev = verify_ufd(args['--ufd-device'])
sources = verify_sources(args, UFD_SOURCES)
show_selections(args, sources, ufd_dev, UFD_SOURCES)
if not args['--force']:
confirm_selections(args)
# Prep UFD
if not args['--update']:
print_info('Prep UFD')
prep_device(ufd_dev, UFD_LABEL, use_mbr=args['--use-mbr'])
# Mount UFD
try_and_print(
indent=2,
message='Mounting UFD...',
function=mount,
mount_source=find_first_partition(ufd_dev),
mount_point='/mnt/UFD',
read_write=True,
)
# Remove Arch folder
if args['--update']:
try_and_print(
indent=2,
message='Removing Linux...',
function=remove_arch,
)
# Copy sources
print_standard(' ')
print_info('Copy Sources')
for s_label, s_path in sources.items():
try_and_print(
indent=2,
message='Copying {}...'.format(s_label),
function=copy_source,
source=s_path,
items=ITEMS[s_label],
overwrite=True,
)
# Update boot entries
print_standard(' ')
print_info('Boot Setup')
try_and_print(
indent=2,
message='Updating boot entries...',
function=update_boot_entries,
boot_entries=BOOT_ENTRIES,
boot_files=BOOT_FILES,
iso_label=ISO_LABEL,
ufd_label=UFD_LABEL,
)
# Install syslinux (to partition)
try_and_print(
indent=2,
message='Syslinux (partition)...',
function=install_syslinux_to_partition,
partition=find_first_partition(ufd_dev),
)
# Unmount UFD
try_and_print(
indent=2,
message='Unmounting UFD...',
function=unmount,
mount_point='/mnt/UFD',
)
# Install syslinux (to device)
try_and_print(
indent=2,
message='Syslinux (device)...',
function=install_syslinux_to_dev,
ufd_dev=ufd_dev,
use_mbr=args['--use-mbr'],
)
# Hide items
print_standard(' ')
print_info('Final Touches')
try_and_print(
indent=2,
message='Hiding items...',
function=hide_items,
ufd_dev=ufd_dev,
items=ITEMS_HIDDEN,
)
# Done
if not args['--force']:
print_standard('\nDone.')
pause('Press Enter to exit...')
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except: # pylint: disable=bare-except
major_exception()

View file

@ -1,43 +0,0 @@
# Wizard Kit: Backup CBS Logs and prep CBS temp data for deletion
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.cleanup import *
from functions.data import *
init_global_vars()
os.system('title {}: CBS Cleanup'.format(KIT_NAME_FULL))
set_log_file('CBS Cleanup.log')
if __name__ == '__main__':
try:
# Prep
stay_awake()
clear_screen()
folder_path = r'{}\Backups'.format(KIT_NAME_SHORT)
dest = select_destination(folder_path=folder_path,
prompt='Which disk are we using for temp data and backup?')
# Show details
print_info('{}: CBS Cleanup Tool\n'.format(KIT_NAME_FULL))
show_data('Backup / Temp path:', dest)
print_standard('\n')
if (not ask('Proceed with CBS cleanup?')):
abort()
# Run Cleanup
try_and_print(message='Running cleanup...', function=cleanup_cbs,
cs='Done', dest_folder=dest)
# Done
print_standard('\nDone.')
pause("Press Enter to exit...")
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -1,11 +0,0 @@
#!/bin/bash
#
## Wizard Kit: ddrescue TUI Launcher
source launch-in-tmux
SESSION_NAME="ddrescue-tui"
WINDOW_NAME="ddrescue TUI"
TMUX_CMD="ddrescue-tui-menu"
launch_in_tmux "$@"

View file

@ -1,64 +0,0 @@
#!/bin/python3
#
## Wizard Kit: TUI for ddrescue cloning and imaging
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.ddrescue import *
from functions.hw_diags import *
init_global_vars()
if __name__ == '__main__':
try:
# Prep
clear_screen()
args = list(sys.argv)
run_mode = ''
source_path = None
dest_path = None
# Parse args
try:
script_name = os.path.basename(args.pop(0))
run_mode = str(args.pop(0)).lower()
source_path = args.pop(0)
dest_path = args.pop(0)
except IndexError:
# We'll set the missing paths later
pass
# Show usage
if re.search(r'-+(h|help)', str(sys.argv), re.IGNORECASE):
show_usage(script_name)
exit_script()
# Start cloning/imaging
if run_mode in ('clone', 'image'):
menu_ddrescue(source_path, dest_path, run_mode)
else:
if not re.search(r'^-*(h|help\?)', run_mode, re.IGNORECASE):
print_error('Invalid mode.')
# Done
print_standard('\nDone.')
pause("Press Enter to exit...")
tmux_switch_client()
exit_script()
except GenericAbort:
abort()
except GenericError as ge:
msg = 'Generic Error'
if str(ge):
msg = str(ge)
print_error(msg)
abort()
except SystemExit as sys_exit:
tmux_switch_client()
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -1,954 +0,0 @@
# Wizard Kit: Functions - Common
import os
import psutil
import re
import shutil
import subprocess
import sys
import time
import traceback
try:
import winreg
except ModuleNotFoundError:
if psutil.WINDOWS:
raise
from settings.main import *
from settings.tools import *
from settings.windows_builds import *
from subprocess import CalledProcessError
# Global variables
global_vars = {}
# STATIC VARIABLES
COLORS = {
'CLEAR': '\033[0m',
'RED': '\033[31m',
'ORANGE': '\033[31;1m',
'GREEN': '\033[32m',
'YELLOW': '\033[33m',
'BLUE': '\033[34m',
'PURPLE': '\033[35m',
'CYAN': '\033[36m',
}
try:
HKU = winreg.HKEY_USERS
HKCR = winreg.HKEY_CLASSES_ROOT
HKCU = winreg.HKEY_CURRENT_USER
HKLM = winreg.HKEY_LOCAL_MACHINE
except NameError:
if psutil.WINDOWS:
raise
# Error Classes
class BIOSKeyNotFoundError(Exception):
pass
class BinNotFoundError(Exception):
pass
class GenericAbort(Exception):
pass
class GenericError(Exception):
pass
class GenericRepair(Exception):
pass
class MultipleInstallationsError(Exception):
pass
class NoProfilesError(Exception):
pass
class Not4KAlignedError(Exception):
pass
class NotInstalledError(Exception):
pass
class OSInstalledLegacyError(Exception):
pass
class PathNotFoundError(Exception):
pass
class UnsupportedOSError(Exception):
pass
class SecureBootDisabledError(Exception):
pass
class SecureBootNotAvailError(Exception):
pass
class SecureBootUnknownError(Exception):
pass
class WindowsOutdatedError(Exception):
pass
class WindowsUnsupportedError(Exception):
pass
# General functions
def abort(show_prompt=True):
"""Abort script."""
print_warning('Aborted.')
if show_prompt:
sleep(1)
pause(prompt='Press Enter to exit... ')
exit_script(1)
def ask(prompt='Kotaero!'):
"""Prompt the user with a Y/N question, returns bool."""
answer = None
prompt = '{} [Y/N]: '.format(prompt)
while answer is None:
tmp = input(prompt)
if re.search(r'^y(es|)$', tmp, re.IGNORECASE):
answer = True
elif re.search(r'^n(o|ope|)$', tmp, re.IGNORECASE):
answer = False
message = '{prompt}{answer_text}'.format(
prompt = prompt,
answer_text = 'Yes' if answer else 'No')
print_log(message=message)
return answer
def beep(repeat=1):
"""Play system bell with optional repeat."""
for i in range(repeat):
# Print bell char
print('\a')
sleep(0.5)
def choice(choices, prompt='Kotaero!'):
"""Prompt the user with a choice question, returns str."""
answer = None
choices = [str(c) for c in choices]
choices_short = {c[:1].upper(): c for c in choices}
prompt = '{} [{}]: '.format(prompt, '/'.join(choices))
regex = '^({}|{})$'.format(
'|'.join([c[:1] for c in choices]),
'|'.join(choices))
# Get user's choice
while answer is None:
tmp = input(prompt)
if re.search(regex, tmp, re.IGNORECASE):
answer = tmp
# Log result
message = '{prompt}{answer_text}'.format(
prompt = prompt,
answer_text = 'Yes' if answer else 'No')
print_log(message=message)
# Fix answer formatting to match provided values
answer = choices_short[answer[:1].upper()]
# Done
return answer
def clear_screen():
"""Simple wrapper for cls/clear."""
if psutil.WINDOWS:
os.system('cls')
else:
os.system('clear')
def convert_to_bytes(size):
"""Convert human-readable size str to bytes and return an int."""
size = str(size)
tmp = re.search(r'(\d+\.?\d*)\s+([PTGMKB])B?', size.upper())
if tmp:
size = float(tmp.group(1))
units = tmp.group(2)
if units == 'P':
size *= 1024 ** 5
if units == 'T':
size *= 1024 ** 4
elif units == 'G':
size *= 1024 ** 3
elif units == 'M':
size *= 1024 ** 2
elif units == 'K':
size *= 1024 ** 1
elif units == 'B':
size *= 1024 ** 0
size = int(size)
else:
return -1
return size
def exit_script(return_value=0):
"""Exits the script after some cleanup and opens the log (if set)."""
# Remove dirs (if empty)
for dir in ['BackupDir', 'LogDir', 'TmpDir']:
try:
os.rmdir(global_vars[dir])
except Exception:
pass
# Open Log (if it exists)
log = global_vars.get('LogFile', '')
if log and os.path.exists(log) and psutil.WINDOWS and ENABLED_OPEN_LOGS:
try:
extract_item('NotepadPlusPlus', silent=True)
popen_program(
[global_vars['Tools']['NotepadPlusPlus'],
global_vars['LogFile']])
except Exception:
print_error('ERROR: Failed to extract Notepad++ and open log.')
pause('Press Enter to exit...')
# Kill Caffeine if still running
kill_process('caffeine.exe')
# Exit
sys.exit(return_value)
def extract_item(item, filter='', silent=False):
"""Extract item from .cbin into .bin."""
cmd = [
global_vars['Tools']['SevenZip'], 'x', '-aos', '-bso0', '-bse0',
'-p{ArchivePassword}'.format(**global_vars),
r'-o{BinDir}\{item}'.format(item=item, **global_vars),
r'{CBinDir}\{item}.7z'.format(item=item, **global_vars),
filter]
if not silent:
print_standard('Extracting "{item}"...'.format(item=item))
try:
run_program(cmd)
except FileNotFoundError:
if not silent:
print_warning('WARNING: Archive not found')
except subprocess.CalledProcessError:
if not silent:
print_warning('WARNING: Errors encountered while exctracting data')
def get_process(name=None):
"""Get process by name, returns psutil.Process obj."""
proc = None
if not name:
raise GenericError
for p in psutil.process_iter():
try:
if p.name() == name:
proc = p
except psutil._exceptions.NoSuchProcess:
# Process finished during iteration? Going to ignore
pass
return proc
def get_simple_string(prompt='Enter string'):
"""Get string from user (restricted character set), returns str."""
simple_string = None
while simple_string is None:
_input = input('{}: '.format(prompt))
if re.match(r"^(\w|-| |\.|')+$", _input, re.ASCII):
simple_string = _input.strip()
return simple_string
def get_ticket_number():
"""Get TicketNumber from user, save in LogDir, and return as str."""
if not ENABLED_TICKET_NUMBERS:
return None
ticket_number = None
while ticket_number is None:
_input = input('Enter ticket number: ')
if re.match(r'^([0-9]+([-_]?\w+|))$', _input):
ticket_number = _input
out_file = r'{}\TicketNumber'.format(global_vars['LogDir'])
if not psutil.WINDOWS:
out_file = out_file.replace('\\', '/')
with open(out_file, 'w', encoding='utf-8') as f:
f.write(ticket_number)
return ticket_number
def human_readable_size(size, decimals=0):
"""Convert size from bytes to a human-readable format, returns str."""
# Prep string formatting
width = 3+decimals
if decimals > 0:
width += 1
# Convert size to int
try:
size = int(size)
except ValueError:
size = convert_to_bytes(size)
except TypeError:
size = -1
# Verify we have a valid size
if size < 0:
return '{size:>{width}} b'.format(size='???', width=width)
# Convert to sensible units
if size >= 1024 ** 5:
size /= 1024 ** 5
units = 'PB'
elif size >= 1024 ** 4:
size /= 1024 ** 4
units = 'TB'
elif size >= 1024 ** 3:
size /= 1024 ** 3
units = 'GB'
elif size >= 1024 ** 2:
size /= 1024 ** 2
units = 'MB'
elif size >= 1024 ** 1:
size /= 1024 ** 1
units = 'KB'
else:
size /= 1024 ** 0
units = ' B'
# Return
return '{size:>{width}.{decimals}f} {units}'.format(
size=size, width=width, decimals=decimals, units=units)
def kill_process(name):
"""Kill any running caffeine.exe processes."""
for proc in psutil.process_iter():
if proc.name() == name:
proc.kill()
def major_exception():
"""Display traceback and exit"""
print_error('Major exception')
print_warning(SUPPORT_MESSAGE)
print(traceback.format_exc())
print_log(traceback.format_exc())
try:
upload_crash_details()
except GenericAbort:
# User declined upload
print_warning('Upload: Aborted')
sleep(10)
except GenericError:
# No log file or uploading disabled
sleep(10)
except:
print_error('Upload: NS')
sleep(10)
else:
print_success('Upload: CS')
pause('Press Enter to exit...')
exit_script(1)
def menu_select(
title='[Untitled Menu]',
prompt='Please make a selection', secret_actions=[], secret_exit=False,
main_entries=[], action_entries=[], disabled_label='DISABLED',
spacer=''):
"""Display options in a menu and return selected option as a str."""
# Bail early
if not main_entries and not action_entries:
raise Exception("MenuError: No items given")
# Set title
if 'Title' in global_vars:
title = '{}\n\n{}'.format(global_vars['Title'], title)
# Build menu
menu_splash = '{}\n{}\n'.format(title, spacer)
width = len(str(len(main_entries)))
valid_answers = []
if secret_exit:
valid_answers.append('Q')
if secret_actions:
valid_answers.extend(secret_actions)
# Add main entries
for i in range(len(main_entries)):
entry = main_entries[i]
# Add Spacer
if ('CRLF' in entry):
menu_splash += '{}\n'.format(spacer)
entry_str = '{number:>{width}}: {name}'.format(
number = i+1,
width = width,
name = entry.get('Display Name', entry['Name']))
if entry.get('Disabled', False):
entry_str = '{YELLOW}{entry_str} ({disabled}){CLEAR}'.format(
entry_str = entry_str,
disabled = disabled_label,
**COLORS)
else:
valid_answers.append(str(i+1))
menu_splash += '{}\n'.format(entry_str)
menu_splash += '{}\n'.format(spacer)
# Add action entries
for entry in action_entries:
# Add Spacer
if ('CRLF' in entry):
menu_splash += '{}\n'.format(spacer)
valid_answers.append(entry['Letter'])
menu_splash += '{letter:>{width}}: {name}\n'.format(
letter = entry['Letter'].upper(),
width = len(str(len(action_entries))),
name = entry['Name'])
answer = ''
while (answer.upper() not in valid_answers):
clear_screen()
print(menu_splash)
answer = input('{}: '.format(prompt))
return answer.upper()
def non_clobber_rename(full_path):
"""Append suffix to path, if necessary, to avoid clobbering path"""
new_path = full_path
_i = 1;
while os.path.exists(new_path):
new_path = '{path}_{i}'.format(i=_i, path=full_path)
_i += 1
return new_path
def pause(prompt='Press Enter to continue... '):
"""Simple pause implementation."""
if prompt[-1] != ' ':
prompt += ' '
input(prompt)
def ping(addr='google.com'):
"""Attempt to ping addr."""
cmd = [
'ping',
'-n' if psutil.WINDOWS else '-c',
'2',
addr]
run_program(cmd)
def popen_program(cmd, pipe=False, minimized=False, shell=False, **kwargs):
"""Run program and return a subprocess.Popen object."""
cmd_kwargs = {'args': cmd, 'shell': shell}
for kw in ('encoding', 'errors'):
if kw in kwargs:
cmd_kwargs[kw] = kwargs[kw]
if minimized:
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = 6
cmd_kwargs['startupinfo'] = startupinfo
if pipe:
cmd_kwargs.update({
'stdout': subprocess.PIPE,
'stderr': subprocess.PIPE,
})
if 'cwd' in kwargs:
cmd_kwargs['cwd'] = kwargs['cwd']
return subprocess.Popen(**cmd_kwargs)
def print_error(*args, **kwargs):
"""Prints message to screen in RED."""
print_standard(*args, color=COLORS['RED'], **kwargs)
def print_info(*args, **kwargs):
"""Prints message to screen in BLUE."""
print_standard(*args, color=COLORS['BLUE'], **kwargs)
def print_standard(message='Generic info',
color=None, end='\n', timestamp=True, **kwargs):
"""Prints message to screen and log (if set)."""
display_message = message
if color:
display_message = color + message + COLORS['CLEAR']
# **COLORS is used below to support non-"standard" color printing
print(display_message.format(**COLORS), end=end, **kwargs)
print_log(message, end, timestamp)
def print_success(*args, **kwargs):
"""Prints message to screen in GREEN."""
print_standard(*args, color=COLORS['GREEN'], **kwargs)
def print_warning(*args, **kwargs):
"""Prints message to screen in YELLOW."""
print_standard(*args, color=COLORS['YELLOW'], **kwargs)
def print_log(message='', end='\n', timestamp=True):
"""Writes message to a log if LogFile is set."""
time_str = time.strftime("%Y-%m-%d %H%M%z: ") if timestamp else ''
if 'LogFile' in global_vars and global_vars['LogFile']:
with open(global_vars['LogFile'], 'a', encoding='utf-8') as f:
for line in message.splitlines():
f.write('{timestamp}{line}{end}'.format(
timestamp = time_str,
line = line,
end = end))
def run_program(cmd, check=True, pipe=True, shell=False, **kwargs):
"""Run program and return a subprocess.CompletedProcess object."""
cmd = [c for c in cmd if c]
if shell:
cmd = ' '.join(cmd)
cmd_kwargs = {'args': cmd, 'check': check, 'shell': shell}
for kw in ('encoding', 'errors'):
if kw in kwargs:
cmd_kwargs[kw] = kwargs[kw]
if pipe:
cmd_kwargs.update({
'stdout': subprocess.PIPE,
'stderr': subprocess.PIPE,
})
if 'cwd' in kwargs:
cmd_kwargs['cwd'] = kwargs['cwd']
return subprocess.run(**cmd_kwargs)
def set_title(title='[Some Title]'):
"""Set title.
Used for window title and menu titles."""
global_vars['Title'] = title
os.system('title {}'.format(title))
def show_data(
message='[Some message]', data='[Some data]',
indent=8, width=32,
info=False, warning=False, error=False):
"""Display info with formatting."""
message = '{indent}{message:<{width}}{data}'.format(
indent=' '*indent, width=width, message=message, data=data)
if error:
print_error(message)
elif warning:
print_warning(message)
elif info:
print_info(message)
else:
print_standard(message)
def sleep(seconds=2):
"""Wait for a while."""
time.sleep(seconds)
def stay_awake():
"""Prevent the system from sleeping or hibernating."""
# DISABLED due to VCR2008 dependency
return
# Bail if caffeine is already running
for proc in psutil.process_iter():
if proc.name() == 'caffeine.exe':
return
# Extract and run
extract_item('Caffeine', silent=True)
try:
popen_program([global_vars['Tools']['Caffeine']])
except Exception:
print_error('ERROR: No caffeine available.')
print_warning('Please set the power setting to High Performance.')
def strip_colors(s):
"""Remove all ASCII color escapes from string, returns str."""
for c in COLORS.values():
s = s.replace(c, '')
return s
def get_exception(s):
"""Get exception by name, returns Exception object."""
try:
obj = getattr(sys.modules[__name__], s)
except AttributeError:
# Try builtin classes
obj = getattr(sys.modules['builtins'], s)
return obj
def try_and_print(message='Trying...',
function=None, cs='CS', ns='NS', other_results={},
catch_all=True, print_return=False, silent_function=True,
indent=8, width=32, *args, **kwargs):
"""Run function, print if successful or not, and return dict.
other_results is in the form of
{
'Warning': {'ExceptionClassName': 'Result Message'},
'Error': {'ExceptionClassName': 'Result Message'}
}
The the ExceptionClassNames will be excepted conditions
and the result string will be printed in the correct color.
catch_all=False will re-raise unspecified exceptions."""
err = None
out = None
w_exceptions = other_results.get('Warning', {}).keys()
w_exceptions = tuple(get_exception(e) for e in w_exceptions)
e_exceptions = other_results.get('Error', {}).keys()
e_exceptions = tuple(get_exception(e) for e in e_exceptions)
w_results = other_results.get('Warning', {})
e_results = other_results.get('Error', {})
# Run function and catch errors
print_standard('{indent}{message:<{width}}'.format(
indent=' '*indent, message=message, width=width), end='', flush=True)
try:
out = function(*args, **kwargs)
if print_return:
str_list = out
if isinstance(out, subprocess.CompletedProcess):
str_list = out.stdout.decode().strip().splitlines()
print_standard(str_list[0].strip(), timestamp=False)
for item in str_list[1:]:
print_standard('{indent}{item}'.format(
indent=' '*(indent+width), item=item.strip()))
elif silent_function:
print_success(cs, timestamp=False)
except w_exceptions as e:
_result = w_results.get(e.__class__.__name__, 'Warning')
print_warning(_result, timestamp=False)
err = e
except e_exceptions as e:
_result = e_results.get(e.__class__.__name__, 'Error')
print_error(_result, timestamp=False)
err = e
except Exception:
print_error(ns, timestamp=False)
err = traceback.format_exc()
# Return or raise?
if err and not catch_all:
raise
else:
return {'CS': not bool(err), 'Error': err, 'Out': out}
def upload_crash_details():
"""Upload log and runtime data to the CRASH_SERVER.
Intended for uploading to a public Nextcloud share."""
if not ENABLED_UPLOAD_DATA:
raise GenericError
import requests
if 'LogFile' in global_vars and global_vars['LogFile']:
if ask('Upload crash details to {}?'.format(CRASH_SERVER['Name'])):
with open(global_vars['LogFile']) as f:
data = '{}\n'.format(f.read())
data += '#############################\n'
data += 'Runtime Details:\n\n'
data += 'sys.argv: {}\n\n'.format(sys.argv)
try:
data += generate_global_vars_report()
except Exception:
data += 'global_vars: {}\n'.format(global_vars)
filename = global_vars.get('LogFile', 'Unknown')
filename = re.sub(r'.*(\\|/)', '', filename)
filename += '.txt'
url = '{}/Crash_{}__{}'.format(
CRASH_SERVER['Url'],
global_vars.get('Date-Time', 'Unknown Date-Time'),
filename)
r = requests.put(
url, data=data,
headers={'X-Requested-With': 'XMLHttpRequest'},
auth=(CRASH_SERVER['User'], CRASH_SERVER['Pass']))
# Raise exception if upload NS
if not r.ok:
raise Exception
else:
# User said no
raise GenericAbort
else:
# No LogFile defined (or invalid LogFile)
raise GenericError
def wait_for_process(name, poll_rate=3):
"""Wait for process by name."""
running = True
while running:
sleep(poll_rate)
running = False
for proc in psutil.process_iter():
try:
if re.search(r'^{}'.format(name), proc.name(), re.IGNORECASE):
running = True
except psutil._exceptions.NoSuchProcess:
# Assuming process closed during iteration
pass
sleep(1)
# global_vars functions
def init_global_vars(silent=False):
"""Sets global variables based on system info."""
if not silent:
print_info('Initializing')
if psutil.WINDOWS:
os.system('title Wizard Kit')
if psutil.LINUX:
init_functions = [
['Checking environment...', set_linux_vars],
['Clearing collisions...', clean_env_vars],
]
else:
init_functions = [
['Checking .bin...', find_bin],
['Checking environment...', set_common_vars],
['Checking OS...', check_os],
['Checking tools...', check_tools],
['Creating folders...', make_tmp_dirs],
['Clearing collisions...', clean_env_vars],
]
try:
if silent:
for f in init_functions:
f[1]()
else:
for f in init_functions:
try_and_print(
message=f[0], function=f[1],
cs='Done', ns='Error', catch_all=False)
except:
major_exception()
def check_os():
"""Set OS specific variables."""
tmp = {}
# Query registry
path = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
with winreg.OpenKey(HKLM, path) as key:
for name in ['CurrentBuild', 'CurrentVersion', 'ProductName']:
try:
tmp[name] = winreg.QueryValueEx(key, name)[0]
except FileNotFoundError:
tmp[name] = 'Unknown'
# Handle CurrentBuild collision
if tmp['CurrentBuild'] == '9200':
if tmp['CurrentVersion'] == '6.2':
# Windown 8, set to fake build number
tmp['CurrentBuild'] = '9199'
else:
# Windows 8.1, leave alone
pass
# Check bit depth
tmp['Arch'] = 32
if 'PROGRAMFILES(X86)' in global_vars['Env']:
tmp['Arch'] = 64
# Get Windows build info
build_info = WINDOWS_BUILDS.get(tmp['CurrentBuild'], None)
if build_info is None:
# Not in windows_builds.py
build_info = [
'Unknown',
'Build {}'.format(tmp['CurrentBuild']),
None,
None,
'unrecognized']
else:
build_info = list(build_info)
tmp['Version'] = build_info.pop(0)
tmp['Release'] = build_info.pop(0)
tmp['Codename'] = build_info.pop(0)
tmp['Marketing Name'] = build_info.pop(0)
tmp['Notes'] = build_info.pop(0)
# Set name
tmp['Name'] = tmp['ProductName']
if tmp['Release']:
tmp['Name'] += ' {}'.format(tmp['Release'])
if tmp['Codename']:
tmp['Name'] += ' "{}"'.format(tmp['Codename'])
if tmp['Marketing Name']:
tmp['Name'] += ' / "{}"'.format(tmp['Marketing Name'])
tmp['Name'] = re.sub(r'\s+', ' ', tmp['Name'])
# Set display name
tmp['DisplayName'] = '{} x{}'.format(tmp['Name'], tmp['Arch'])
if tmp['Notes']:
tmp['DisplayName'] += ' ({})'.format(tmp['Notes'])
global_vars['OS'] = tmp
def check_tools():
"""Set tool variables based on OS bit-depth and tool availability."""
if global_vars['OS'].get('Arch', 32) == 64:
global_vars['Tools'] = {
k: v.get('64', v.get('32')) for (k, v) in TOOLS.items()}
else:
global_vars['Tools'] = {k: v.get('32') for (k, v) in TOOLS.items()}
# Fix paths
global_vars['Tools'] = {k: os.path.join(global_vars['BinDir'], v)
for (k, v) in global_vars['Tools'].items()}
def clean_env_vars():
"""Remove conflicting global_vars and env variables.
This fixes an issue where both global_vars and
global_vars['Env'] are expanded at the same time."""
for key in global_vars.keys():
global_vars['Env'].pop(key, None)
def find_bin():
"""Find .bin folder in the cwd or it's parents."""
wd = os.getcwd()
base = None
while base is None:
if os.path.exists('.bin'):
base = os.getcwd()
break
if re.fullmatch(r'\w:\\', os.getcwd()):
break
os.chdir('..')
os.chdir(wd)
if base is None:
raise BinNotFoundError
global_vars['BaseDir'] = base
def generate_global_vars_report():
"""Build readable string from global_vars, returns str."""
report = ['global_vars: {']
for k, v in sorted(global_vars.items()):
if k == 'Env':
continue
if isinstance(v, list):
report.append(' {}: ['.format(str(k)))
for item in v:
report.append(' {}'.format(str(v)))
report.append(' ]')
elif isinstance(v, dict):
report.append(' {}: {{'.format(str(k)))
for item_k, item_v in sorted(v.items()):
report.append(' {:<15} {}'.format(
str(item_k)+':', str(item_v)))
report.append(' }')
else:
report.append(' {:<18}{}'.format(str(k)+':', str(v)))
report.append(' Env:')
for k, v in sorted(global_vars.get('Env', {}).items()):
report.append(' {:<15} {}'.format(
str(k)+':', str(v)))
report.append('}')
return '\n'.join(report)
def make_tmp_dirs():
"""Make temp directories."""
os.makedirs(global_vars['BackupDir'], exist_ok=True)
os.makedirs(global_vars['LogDir'], exist_ok=True)
os.makedirs(r'{}\{}'.format(
global_vars['LogDir'], KIT_NAME_FULL), exist_ok=True)
os.makedirs(r'{}\Tools'.format(global_vars['LogDir']), exist_ok=True)
os.makedirs(global_vars['TmpDir'], exist_ok=True)
def set_common_vars():
"""Set common variables."""
global_vars['Date'] = time.strftime("%Y-%m-%d")
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
global_vars['Env'] = os.environ.copy()
global_vars['ArchivePassword'] = ARCHIVE_PASSWORD
global_vars['BinDir'] = r'{BaseDir}\.bin'.format(**global_vars)
global_vars['CBinDir'] = r'{BaseDir}\.cbin'.format(**global_vars)
global_vars['ClientDir'] = r'{SYSTEMDRIVE}\{prefix}'.format(
prefix=KIT_NAME_SHORT, **global_vars['Env'])
global_vars['BackupDir'] = r'{ClientDir}\Backups'.format(**global_vars)
global_vars['LogDir'] = r'{ClientDir}\Logs\{Date}'.format(**global_vars)
global_vars['QuarantineDir'] = r'{ClientDir}\Quarantine'.format(**global_vars)
global_vars['TmpDir'] = r'{BinDir}\tmp'.format(**global_vars)
def set_linux_vars():
"""Set common variables in a Linux environment.
These assume we're running under a WK-Linux build."""
result = run_program(['mktemp', '-d'])
global_vars['TmpDir'] = result.stdout.decode().strip()
global_vars['Date'] = time.strftime("%Y-%m-%d")
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
global_vars['Env'] = os.environ.copy()
global_vars['BinDir'] = '/usr/local/bin'
global_vars['LogDir'] = '{}/Logs'.format(global_vars['Env']['HOME'])
global_vars['Tools'] = {
'wimlib-imagex': 'wimlib-imagex',
'SevenZip': '7z',
}
def set_log_file(log_name):
"""Sets global var LogFile and creates path as needed."""
if psutil.LINUX:
folder_path = global_vars['LogDir']
else:
folder_path = '{}{}{}'.format(
global_vars['LogDir'],
os.sep,
KIT_NAME_FULL)
log_file = '{}{}{}'.format(
folder_path,
os.sep,
log_name)
os.makedirs(folder_path, exist_ok=True)
global_vars['LogFile'] = log_file
if __name__ == '__main__':
print("This file is not meant to be called directly.")
# vim: sts=2 sw=2 ts=2

View file

@ -1,53 +0,0 @@
# Wizard Kit: Functions - Network
import os
import shutil
import sys
from functions.common import *
# REGEX
REGEX_VALID_IP = re.compile(
r'(10.\d+.\d+.\d+'
r'|172.(1[6-9]|2\d|3[0-1])'
r'|192.168.\d+.\d+)',
re.IGNORECASE)
def is_connected():
"""Check for a valid private IP."""
devs = psutil.net_if_addrs()
for dev in devs.values():
for family in dev:
if REGEX_VALID_IP.search(family.address):
# Valid IP found
return True
# Else
return False
def show_valid_addresses():
"""Show all valid private IP addresses assigned to the system."""
devs = psutil.net_if_addrs()
for dev, families in sorted(devs.items()):
for family in families:
if REGEX_VALID_IP.search(family.address):
# Valid IP found
show_data(message=dev, data=family.address)
def speedtest():
"""Run a network speedtest using speedtest-cli."""
result = run_program(['speedtest-cli', '--simple'])
output = [line.strip() for line in result.stdout.decode().splitlines()
if line.strip()]
output = [line.split() for line in output]
output = [(a, float(b), c) for a, b, c in output]
return ['{:10}{:6.2f} {}'.format(*line) for line in output]
if __name__ == '__main__':
print("This file is not meant to be called directly.")
# vim: sts=2 sw=2 ts=2

View file

@ -1,471 +0,0 @@
"""Wizard Kit: Functions - UFD"""
# pylint: disable=broad-except,wildcard-import
# vim: sts=2 sw=2 ts=2
import os
import re
import shutil
import pathlib
from collections import OrderedDict
from functions.common import *
def case_insensitive_search(path, item):
"""Search path for item case insensitively, returns str."""
regex_match = '^{}$'.format(item)
real_path = ''
# Quick check first
if os.path.exists('{}/{}'.format(path, item)):
real_path = '{}{}{}'.format(
path,
'' if path == '/' else '/',
item,
)
# Check all items in dir
for entry in os.scandir(path):
if re.match(regex_match, entry.name, re.IGNORECASE):
real_path = '{}{}{}'.format(
path,
'' if path == '/' else '/',
entry.name,
)
# Done
if not real_path:
raise FileNotFoundError('{}/{}'.format(path, item))
return real_path
def confirm_selections(args):
"""Ask tech to confirm selections, twice if necessary."""
if not ask('Is the above information correct?'):
abort(False)
## Safety check
if not args['--update']:
print_standard(' ')
print_warning('SAFETY CHECK')
print_standard(
'All data will be DELETED from the disk and partition(s) listed above.')
print_standard(
'This is irreversible and will lead to {RED}DATA LOSS.{CLEAR}'.format(
**COLORS))
if not ask('Asking again to confirm, is this correct?'):
abort(False)
print_standard(' ')
def copy_source(source, items, overwrite=False):
"""Copy source items to /mnt/UFD."""
is_image = source.is_file()
# Mount source if necessary
if is_image:
mount(source, '/mnt/Source')
# Copy items
for i_source, i_dest in items:
i_source = '{}{}'.format(
'/mnt/Source' if is_image else source,
i_source,
)
i_dest = '/mnt/UFD{}'.format(i_dest)
try:
recursive_copy(i_source, i_dest, overwrite=overwrite)
except FileNotFoundError:
# Going to assume (hope) that this is fine
pass
# Unmount source if necessary
if is_image:
unmount('/mnt/Source')
def find_first_partition(dev_path):
"""Find path to first partition of dev, returns str."""
cmd = [
'lsblk',
'--list',
'--noheadings',
'--output', 'name',
'--paths',
dev_path,
]
result = run_program(cmd, encoding='utf-8', errors='ignore')
part_path = result.stdout.splitlines()[-1].strip()
return part_path
def find_path(path):
"""Find path case-insensitively, returns pathlib.Path obj."""
path_obj = pathlib.Path(path).resolve()
# Quick check first
if path_obj.exists():
return path_obj
# Fix case
parts = path_obj.relative_to('/').parts
real_path = '/'
for part in parts:
try:
real_path = case_insensitive_search(real_path, part)
except NotADirectoryError:
# Reclassify error
raise FileNotFoundError(path)
# Raise error if path doesn't exist
path_obj = pathlib.Path(real_path)
if not path_obj.exists():
raise FileNotFoundError(path_obj)
# Done
return path_obj
def get_user_home(user):
"""Get path to user's home dir, returns str."""
home_dir = None
cmd = ['getent', 'passwd', user]
result = run_program(cmd, encoding='utf-8', errors='ignore', check=False)
try:
home_dir = result.stdout.split(':')[5]
except Exception:
# Just use HOME from ENV (or '/root' if that fails)
home_dir = os.environ.get('HOME', '/root')
return home_dir
def get_user_name():
"""Get real user name, returns str."""
user = None
if 'SUDO_USER' in os.environ:
user = os.environ.get('SUDO_USER', 'Unknown')
else:
user = os.environ.get('USER', 'Unknown')
return user
def hide_items(ufd_dev, items):
"""Set FAT32 hidden flag for items."""
# pylint: disable=invalid-name
with open('/root/.mtoolsrc', 'w') as f:
f.write('drive U: file="{}"\n'.format(
find_first_partition(ufd_dev)))
f.write('mtools_skip_check=1\n')
# Hide items
for item in items:
cmd = ['yes | mattrib +h "U:/{}"'.format(item)]
run_program(cmd, check=False, shell=True)
def install_syslinux_to_dev(ufd_dev, use_mbr):
"""Install Syslinux to UFD (dev)."""
cmd = [
'dd',
'bs=440',
'count=1',
'if=/usr/lib/syslinux/bios/{}.bin'.format(
'mbr' if use_mbr else 'gptmbr',
),
'of={}'.format(ufd_dev),
]
run_program(cmd)
def install_syslinux_to_partition(partition):
"""Install Syslinux to UFD (partition)."""
cmd = [
'syslinux',
'--install',
'--directory',
'/arch/boot/syslinux/',
partition,
]
run_program(cmd)
def is_valid_path(path_obj, path_type):
"""Verify path_obj is valid by type, returns bool."""
valid_path = False
if path_type == 'DIR':
valid_path = path_obj.is_dir()
elif path_type == 'KIT':
valid_path = path_obj.is_dir() and path_obj.joinpath('.bin').exists()
elif path_type == 'IMG':
valid_path = path_obj.is_file() and path_obj.suffix.lower() == '.img'
elif path_type == 'ISO':
valid_path = path_obj.is_file() and path_obj.suffix.lower() == '.iso'
elif path_type == 'UFD':
valid_path = path_obj.is_block_device()
return valid_path
def mount(mount_source, mount_point, read_write=False):
"""Mount mount_source on mount_point."""
os.makedirs(mount_point, exist_ok=True)
cmd = [
'mount',
mount_source,
mount_point,
'-o',
'rw' if read_write else 'ro',
]
run_program(cmd)
def prep_device(dev_path, label, use_mbr=False, indent=2):
"""Format device in preparation for applying the WizardKit components
This is done is four steps:
1. Zero-out first 64MB (this deletes the partition table and/or bootloader)
2. Create a new partition table (GPT by default, optionally MBR)
3. Set boot flag
4. Format partition (FAT32, 4K aligned)
"""
# Zero-out first 64MB
cmd = 'dd bs=4M count=16 if=/dev/zero of={}'.format(dev_path).split()
try_and_print(
indent=indent,
message='Zeroing first 64MB...',
function=run_program,
cmd=cmd,
)
# Create partition table
cmd = 'parted {} --script -- mklabel {} mkpart primary fat32 4MiB {}'.format(
dev_path,
'msdos' if use_mbr else 'gpt',
'-1s' if use_mbr else '-4MiB',
).split()
try_and_print(
indent=indent,
message='Creating partition table...',
function=run_program,
cmd=cmd,
)
# Set boot flag
cmd = 'parted {} set 1 {} on'.format(
dev_path,
'boot' if use_mbr else 'legacy_boot',
).split()
try_and_print(
indent=indent,
message='Setting boot flag...',
function=run_program,
cmd=cmd,
)
# Format partition
cmd = [
'mkfs.vfat', '-F', '32',
'-n', label,
find_first_partition(dev_path),
]
try_and_print(
indent=indent,
message='Formatting partition...',
function=run_program,
cmd=cmd,
)
def recursive_copy(source, dest, overwrite=False):
"""Copy source to dest recursively.
NOTE: This uses rsync style source/dest syntax.
If the source has a trailing slash then it's contents are copied,
otherwise the source itself is copied.
Examples assuming "ExDir/ExFile.txt" exists:
recursive_copy("ExDir", "Dest/") results in "Dest/ExDir/ExFile.txt"
recursive_copy("ExDir/", "Dest/") results in "Dest/ExFile.txt"
NOTE 2: dest does not use find_path because it might not exist.
"""
copy_contents = source.endswith('/')
source = find_path(source)
dest = pathlib.Path(dest).resolve().joinpath(source.name)
os.makedirs(dest.parent, exist_ok=True)
if source.is_dir():
if copy_contents:
# Trailing slash syntax
for item in os.scandir(source):
recursive_copy(item.path, dest.parent, overwrite=overwrite)
elif not dest.exists():
# No conflict, copying whole tree (no merging needed)
shutil.copytree(source, dest)
elif not dest.is_dir():
# Refusing to replace file with dir
raise FileExistsError('Refusing to replace file: {}'.format(dest))
else:
# Dest exists and is a dir, merge dirs
for item in os.scandir(source):
recursive_copy(item.path, dest, overwrite=overwrite)
elif source.is_file():
if not dest.exists():
# No conflict, copying file
shutil.copy2(source, dest)
elif not dest.is_file():
# Refusing to replace dir with file
raise FileExistsError('Refusing to replace dir: {}'.format(dest))
elif overwrite:
# Dest file exists, deleting and replacing file
os.remove(dest)
shutil.copy2(source, dest)
else:
# Refusing to delete file when overwrite=False
raise FileExistsError('Refusing to delete file: {}'.format(dest))
def remove_arch():
"""Remove arch dir from UFD.
This ensures a clean installation to the UFD and resets the boot files
"""
shutil.rmtree(find_path('/mnt/UFD/arch'))
def running_as_root():
"""Check if running with effective UID of 0, returns bool."""
return os.geteuid() == 0
def show_selections(args, sources, ufd_dev, ufd_sources):
"""Show selections including non-specified options."""
# Sources
print_info('Sources')
for label in ufd_sources.keys():
if label in sources:
print_standard(' {label:<18} {path}'.format(
label=label+':',
path=sources[label],
))
else:
print_standard(' {label:<18} {YELLOW}Not Specified{CLEAR}'.format(
label=label+':',
**COLORS,
))
print_standard(' ')
# Destination
print_info('Destination')
cmd = [
'lsblk', '--nodeps', '--noheadings', '--paths',
'--output', 'NAME,FSTYPE,TRAN,SIZE,VENDOR,MODEL,SERIAL',
ufd_dev,
]
result = run_program(cmd, check=False, encoding='utf-8', errors='ignore')
print_standard(result.stdout.strip())
cmd = [
'lsblk', '--noheadings', '--paths',
'--output', 'NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT',
ufd_dev,
]
result = run_program(cmd, check=False, encoding='utf-8', errors='ignore')
for line in result.stdout.splitlines()[1:]:
print_standard(line)
# Notes
if args['--update']:
print_warning('Updating kit in-place')
elif args['--use-mbr']:
print_warning('Formatting using legacy MBR')
print_standard(' ')
def unmount(mount_point):
"""Unmount mount_point."""
cmd = ['umount', mount_point]
run_program(cmd)
def update_boot_entries(boot_entries, boot_files, iso_label, ufd_label):
"""Update boot files for UFD usage"""
configs = []
# Find config files
for c_path, c_ext in boot_files.items():
c_path = find_path('/mnt/UFD{}'.format(c_path))
for item in os.scandir(c_path):
if item.name.lower().endswith(c_ext.lower()):
configs.append(item.path)
# Update Linux labels
cmd = [
'sed',
'--in-place',
'--regexp-extended',
's/{}/{}/'.format(iso_label, ufd_label),
*configs,
]
run_program(cmd)
# Uncomment extra entries if present
for b_path, b_comment in boot_entries.items():
try:
find_path('/mnt/UFD{}'.format(b_path))
except (FileNotFoundError, NotADirectoryError):
# Entry not found, continue to next entry
continue
# Entry found, update config files
cmd = [
'sed',
'--in-place',
's/#{}#//'.format(b_comment),
*configs,
]
run_program(cmd, check=False)
def verify_sources(args, ufd_sources):
"""Check all sources and abort if necessary, returns dict."""
sources = OrderedDict()
for label, data in ufd_sources.items():
s_path = args[data['Arg']]
if s_path:
try:
s_path_obj = find_path(s_path)
except FileNotFoundError:
print_error('ERROR: {} not found: {}'.format(label, s_path))
abort(False)
if not is_valid_path(s_path_obj, data['Type']):
print_error('ERROR: Invalid {} source: {}'.format(label, s_path))
abort(False)
sources[label] = s_path_obj
return sources
def verify_ufd(dev_path):
"""Check that dev_path is a valid UFD, returns pathlib.Path obj."""
ufd_dev = None
try:
ufd_dev = find_path(dev_path)
except FileNotFoundError:
print_error('ERROR: UFD device not found: {}'.format(dev_path))
abort(False)
if not is_valid_path(ufd_dev, 'UFD'):
print_error('ERROR: Invalid UFD device: {}'.format(ufd_dev))
abort(False)
return ufd_dev
if __name__ == '__main__':
print("This file is not meant to be called directly.")

View file

@ -1,42 +0,0 @@
#!/bin/python3
#
## Wizard Kit: HW Diagnostics - Audio
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.common import *
init_global_vars()
if __name__ == '__main__':
try:
# Prep
clear_screen()
print_standard('Hardware Diagnostics: Audio\n')
# Set volume
try:
run_program('amixer -q set "Master" 80% unmute'.split())
run_program('amixer -q set "PCM" 90% unmute'.split())
except subprocess.CalledProcessError:
print_error('Failed to set volume')
# Run tests
for mode in ['pink', 'wav']:
run_program(
cmd = 'speaker-test -c 2 -l 1 -t {}'.format(mode).split(),
check = False,
pipe = False)
# Done
#print_standard('\nDone.')
#pause("Press Enter to exit...")
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -1,18 +0,0 @@
#!/bin/bash
#
## Wizard Kit: HW Diagnostics - Benchmarks
function usage {
echo "Usage: ${0} device log-file"
echo " e.g. ${0} /dev/sda /tmp/tmp.XXXXXXX/benchmarks.log"
}
# Bail early
if [ ! -b "${1}" ]; then
usage
exit 1
fi
# Run Benchmarks
echo 3 | sudo tee -a /proc/sys/vm/drop_caches >/dev/null 2>&1
sudo dd bs=4M if="${1}" of=/dev/null status=progress 2>&1 | tee -a "${2}"

View file

@ -1,66 +0,0 @@
#!/bin/python3
#
## Wizard Kit: HW Diagnostics - Menu
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.hw_diags import *
from functions.tmux import *
init_global_vars()
if __name__ == '__main__':
# Show menu
try:
state = State()
menu_diags(state, sys.argv)
except KeyboardInterrupt:
print_standard(' ')
print_warning('Aborted')
print_standard(' ')
sleep(1)
pause('Press Enter to exit...')
except SystemExit as sys_exit:
tmux_switch_client()
exit_script(sys_exit.code)
except:
# Cleanup
tmux_kill_all_panes()
if DEBUG_MODE:
# Custom major exception
print_standard(' ')
print_error('Major exception')
print_warning(SUPPORT_MESSAGE)
print(traceback.format_exc())
print_log(traceback.format_exc())
# Save debug reports and upload data
try_and_print(
message='Saving debug reports...',
function=save_debug_reports,
state=state, global_vars=global_vars)
question = 'Upload crash details to {}?'.format(CRASH_SERVER['Name'])
if ENABLED_UPLOAD_DATA and ask(question):
try_and_print(
message='Uploading Data...',
function=upload_logdir,
global_vars=global_vars)
# Done
sleep(1)
pause('Press Enter to exit...')
exit_script(1)
else:
# "Normal" major exception
major_exception()
# Done
tmux_kill_all_panes()
tmux_switch_client()
exit_script()
# vim: sts=2 sw=2 ts=2

View file

@ -1,48 +0,0 @@
#!/bin/python3
#
## Wizard Kit: HW Diagnostics - Network
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.network import *
def check_connection():
if not is_connected():
# Raise to cause NS in try_and_print()
raise Exception
if __name__ == '__main__':
try:
# Prep
clear_screen()
print_standard('Hardware Diagnostics: Network\n')
# Connect
print_standard('Initializing...')
connect_to_network()
# Tests
try_and_print(
message='Network connection:', function=check_connection, cs='OK')
show_valid_addresses()
try_and_print(message='Internet connection:', function=ping,
addr='8.8.8.8', cs='OK')
try_and_print(message='DNS Resolution:', function=ping, cs='OK')
try_and_print(message='Speedtest:', function=speedtest,
print_return=True)
# Done
print_standard('\nDone.')
#pause("Press Enter to exit...")
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -1,19 +0,0 @@
#!/bin/bash
#
## Wizard Kit: HW Diagnostics - Prime95
function usage {
echo "Usage: $0 log-dir"
echo " e.g. $0 /tmp/tmp.7Mh5f1RhSL9001"
}
# Bail early
if [ ! -d "$1" ]; then
usage
exit 1
fi
# Run Prime95
cd "$1"
mprime -t | grep -iv --line-buffered 'stress.txt' | tee -a "prime.log"

View file

@ -1,39 +0,0 @@
#!/bin/bash
#
BLUE='\033[34m'
CLEAR='\033[0m'
IFS=$'\n'
# List devices
for line in $(lsblk -do NAME,TRAN,SIZE,VENDOR,MODEL,SERIAL); do
if [[ "${line:0:4}" == "NAME" ]]; then
echo -e "${BLUE}${line}${CLEAR}"
else
echo "${line}"
fi
done
echo ""
# List loopback devices
if [[ "$(losetup -l | wc -l)" > 0 ]]; then
for line in $(losetup -lO NAME,PARTSCAN,RO,BACK-FILE); do
if [[ "${line:0:4}" == "NAME" ]]; then
echo -e "${BLUE}${line}${CLEAR}"
else
echo "${line}" | sed -r 's#/dev/(loop[0-9]+)#\1 #'
fi
done
echo ""
fi
# List partitions
for line in $(lsblk -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT); do
if [[ "${line:0:4}" == "NAME" ]]; then
echo -e "${BLUE}${line}${CLEAR}"
else
echo "${line}"
fi
done
echo ""

View file

@ -1,10 +0,0 @@
#!/bin/bash
#
## Wizard Kit: Sensor monitoring tool
WINDOW_NAME="Hardware Sensors"
MONITOR="hw-sensors-monitor"
# Start session
tmux new-session -n "$WINDOW_NAME" "$MONITOR"

View file

@ -1,36 +0,0 @@
#!/bin/python3
#
## Wizard Kit: Sensor monitoring tool
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.sensors import *
from functions.tmux import *
init_global_vars(silent=True)
if __name__ == '__main__':
background = False
try:
if len(sys.argv) > 1 and os.path.exists(sys.argv[1]):
background = True
monitor_file = sys.argv[1]
monitor_pane = None
else:
result = run_program(['mktemp'])
monitor_file = result.stdout.decode().strip()
if not background:
monitor_pane = tmux_split_window(
percent=1, vertical=True, watch=monitor_file)
cmd = ['tmux', 'resize-pane', '-Z', '-t', monitor_pane]
run_program(cmd, check=False)
monitor_sensors(monitor_pane, monitor_file)
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -1,38 +0,0 @@
#!/bin/python3
#
## Wizard Kit: Volume mount tool
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.data import *
init_global_vars()
if __name__ == '__main__':
try:
# Prep
clear_screen()
print_standard('{}: Volume mount tool'.format(KIT_NAME_FULL))
# Mount volumes
report = mount_volumes(all_devices=True)
# Print report
print_info('\nResults')
for vol_name, vol_data in sorted(report.items()):
show_data(indent=4, width=20, **vol_data['show_data'])
# Done
print_standard('\nDone.')
if 'gui' in sys.argv:
pause("Press Enter to exit...")
popen_program(['nohup', 'thunar', '/media'], pipe=True)
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -1,35 +0,0 @@
#!/bin/python3
#
## Wizard Kit: Backup share mount tool
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.data import *
from functions.network import *
init_global_vars()
if __name__ == '__main__':
try:
# Prep
clear_screen()
# Mount
if is_connected():
mount_backup_shares(read_write=True)
else:
# Couldn't connect
print_error('ERROR: No network connectivity.')
# Done
print_standard('\nDone.')
#pause("Press Enter to exit...")
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -1,39 +0,0 @@
# Wizard Kit: Enter SafeMode by editing the BCD
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.safemode import *
init_global_vars()
os.system('title {}: SafeMode Tool'.format(KIT_NAME_FULL))
if __name__ == '__main__':
try:
clear_screen()
print_info('{}: SafeMode Tool\n'.format(KIT_NAME_FULL))
other_results = {
'Error': {'CalledProcessError': 'Unknown Error'},
'Warning': {}}
if not ask('Enable booting to SafeMode (with Networking)?'):
abort()
# Configure SafeMode
try_and_print(message='Set BCD option...',
function=enable_safemode, other_results=other_results)
try_and_print(message='Enable MSI in SafeMode...',
function=enable_safemode_msi, other_results=other_results)
# Done
print_standard('\nDone.')
pause('Press Enter to reboot...')
reboot()
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -1,39 +0,0 @@
# Wizard Kit: Exit SafeMode by editing the BCD
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.safemode import *
init_global_vars()
os.system('title {}: SafeMode Tool'.format(KIT_NAME_FULL))
if __name__ == '__main__':
try:
clear_screen()
print_info('{}: SafeMode Tool\n'.format(KIT_NAME_FULL))
other_results = {
'Error': {'CalledProcessError': 'Unknown Error'},
'Warning': {}}
if not ask('Disable booting to SafeMode?'):
abort()
# Configure SafeMode
try_and_print(message='Remove BCD option...',
function=disable_safemode, other_results=other_results)
try_and_print(message='Disable MSI in SafeMode...',
function=disable_safemode_msi, other_results=other_results)
# Done
print_standard('\nDone.')
pause('Press Enter to reboot...')
reboot()
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -1,40 +0,0 @@
# Wizard Kit: Check, and possibly repair, system file health via SFC
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.repairs import *
init_global_vars()
os.system('title {}: SFC Tool'.format(KIT_NAME_FULL))
set_log_file('SFC Tool.log')
if __name__ == '__main__':
try:
stay_awake()
clear_screen()
print_info('{}: SFC Tool\n'.format(KIT_NAME_FULL))
other_results = {
'Error': {
'CalledProcessError': 'Unknown Error',
},
'Warning': {
'GenericRepair': 'Repaired',
}}
if ask('Run a SFC scan now?'):
try_and_print(message='SFC scan...',
function=run_sfc_scan, other_results=other_results)
else:
abort()
# Done
print_standard('\nDone.')
pause('Press Enter to exit...')
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

48
.gitignore vendored
View file

@ -1,41 +1,7 @@
**/__pycache__/*
*.bak
*.exe
*.swp
.bin/7-Zip/
.bin/AIDA64/
.bin/BleachBit/
.bin/ClassicStartSkin/
.bin/ConEmu/
.bin/Erunt/
.bin/Everything/
.bin/FastCopy/
.bin/HWiNFO/HWiNFO*.ini
.bin/NotepadPlusPlus/
.bin/ProcessKiller/
.bin/ProduKey/
.bin/Python/
.bin/Tmp/
.bin/XMPlay/
.bin/_Drivers/SDIO/
.cbin/*.7z
.cbin/AIDA64/
.cbin/Autoruns/
.cbin/BleachBit-Portable/
.cbin/BlueScreenView/
.cbin/Caffeine/
.cbin/Du/
.cbin/Everything/
.cbin/FirefoxExtensions/
.cbin/IObitUninstallerPortable/
.cbin/ProduKey/
.cbin/TestDisk/
.cbin/TreeSizeFree-Portable/
.cbin/XMPlay/
.cbin/XYplorerFree/
.cbin/_Drivers/
.cbin/_Office/
.cbin/_vcredists/
.cbin/wimlib/
BUILD*/
OUT*/
**/__pycache__
**/*.7z
**/*.bak
**/*.exe
**/*.swp
setup/BUILD*
setup/OUT*

View file

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View file

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

View file

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

6
scripts/README.md Normal file
View file

@ -0,0 +1,6 @@
## pylint ##
These scripts use two spaces per indent instead of the default four. As such you will need to update your pylintrc file or run like this:
`pylint --indent-after-paren=2 --indent-string=' ' wk`

31
scripts/activate.py Normal file
View file

@ -0,0 +1,31 @@
"""Wizard Kit: Activate Windows using a BIOS key"""
# vim: sts=2 sw=2 ts=2
import wk
def main():
"""Attempt to activate Windows and show result."""
title = f'{wk.cfg.main.KIT_NAME_FULL}: Activation Tool'
try_print = wk.std.TryAndPrint()
wk.std.clear_screen()
wk.std.set_title(title)
wk.std.print_info(title)
print('')
# Attempt activation
try_print.run('Attempting activation...', wk.os.win.activate_with_bios)
# Done
print('')
print('Done.')
wk.std.pause('Press Enter to exit...')
if __name__ == '__main__':
try:
main()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

14
scripts/build-ufd Executable file
View file

@ -0,0 +1,14 @@
#!/usr/bin/env python3
"""Wizard Kit: Build UFD Tool"""
# vim: sts=2 sw=2 ts=2
import wk
if __name__ == '__main__':
try:
wk.kit.ufd.build_ufd()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

49
scripts/check_disk.py Normal file
View file

@ -0,0 +1,49 @@
"""Wizard Kit: Check or repair the %SYSTEMDRIVE% filesystem via CHKDSK"""
# vim: sts=2 sw=2 ts=2
import os
import wk
def main():
"""Run or schedule CHKDSK and show result."""
title = f'{wk.cfg.main.KIT_NAME_FULL}: Check Disk Tool'
menu = wk.std.Menu(title=title)
try_print = wk.std.TryAndPrint()
wk.std.clear_screen()
wk.std.set_title(title)
print('')
# Add menu entries
menu.add_option('Offline scan')
menu.add_option('Online scan')
# Show menu and make selection
selection = menu.simple_select()
# Run or schedule scan
if 'Offline' in selection[0]:
function = wk.os.win.run_chkdsk_offline
msg_good = 'Scheduled'
else:
function = wk.os.win.run_chkdsk_online
msg_good = 'No issues detected'
try_print.run(
message=f'CHKDSK ({os.environ.get("SYSTEMDRIVE")})...',
function=function,
msg_good=msg_good,
)
# Done
print('')
print('Done.')
wk.std.pause('Press Enter to exit...')
if __name__ == '__main__':
try:
main()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

21
scripts/ddrescue-tui Executable file
View file

@ -0,0 +1,21 @@
#!/bin/bash
#
## Wizard Kit: ddrescue TUI Launcher
# Check if running under Linux
os_name="$(uname -s)"
if [[ "$os_name" == "Darwin" ]]; then
os_name="macOS"
fi
if [[ "$os_name" != "Linux" ]]; then
echo "This script is not supported under $os_name." 1>&2
exit 1
fi
source ./launch-in-tmux
SESSION_NAME="ddrescue-tui"
WINDOW_NAME="ddrescue TUI"
TMUX_CMD="./ddrescue-tui.py"
launch_in_tmux "$@"

14
scripts/ddrescue-tui.py Executable file
View file

@ -0,0 +1,14 @@
#!/usr/bin/env python3
"""Wizard Kit: ddrescue TUI"""
# vim: sts=2 sw=2 ts=2
import wk
if __name__ == '__main__':
try:
wk.hw.ddrescue.main()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

View file

@ -2,10 +2,10 @@
#
## Wizard Kit: HW Diagnostics Launcher
source launch-in-tmux
source ./launch-in-tmux
SESSION_NAME="hw-diags"
WINDOW_NAME="Hardware Diagnostics"
TMUX_CMD="hw-diags-menu"
TMUX_CMD="./hw-diags.py"
launch_in_tmux "$@"

14
scripts/hw-diags.py Executable file
View file

@ -0,0 +1,14 @@
#!/usr/bin/env python3
"""Wizard Kit: Hardware Diagnostics"""
# vim: sts=2 sw=2 ts=2
import wk
if __name__ == '__main__':
try:
wk.hw.diags.main()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

49
scripts/hw-drive-info Executable file
View file

@ -0,0 +1,49 @@
#!/bin/bash
#
BLUE='\033[34m'
CLEAR='\033[0m'
IFS=$'\n'
# Check if running under Linux
os_name="$(uname -s)"
if [[ "$os_name" == "Darwin" ]]; then
os_name="macOS"
fi
if [[ "$os_name" != "Linux" ]]; then
echo "This script is not supported under $os_name." 1>&2
exit 1
fi
# List devices
for line in $(lsblk -do NAME,TRAN,SIZE,VENDOR,MODEL,SERIAL); do
if [[ "${line:0:4}" == "NAME" ]]; then
echo -e "${BLUE}${line}${CLEAR}"
else
echo "${line}"
fi
done
echo ""
# List loopback devices
if [[ "$(losetup -l | wc -l)" > 0 ]]; then
for line in $(losetup -lO NAME,PARTSCAN,RO,BACK-FILE); do
if [[ "${line:0:4}" == "NAME" ]]; then
echo -e "${BLUE}${line}${CLEAR}"
else
echo "${line}" | sed -r 's#/dev/(loop[0-9]+)#\1 #'
fi
done
echo ""
fi
# List partitions
for line in $(lsblk -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT); do
if [[ "${line:0:4}" == "NAME" ]]; then
echo -e "${BLUE}${line}${CLEAR}"
else
echo "${line}"
fi
done
echo ""

View file

@ -9,22 +9,32 @@ YELLOW="\e[33m"
BLUE="\e[34m"
function print_in_columns() {
string="$1"
label="$(echo "$string" | sed -r 's/^\s*(.*:).*/\1/')"
value="$(echo "$string" | sed -r 's/^\s*.*:\s*(.*)/\1/')"
printf ' %-18s%s\n' "$label" "$value"
string="$1"
label="$(echo "$string" | sed -r 's/^\s*(.*:).*/\1/')"
value="$(echo "$string" | sed -r 's/^\s*.*:\s*(.*)/\1/')"
printf ' %-18s%s\n' "$label" "$value"
}
function print_dmi_value() {
name="$1"
file="/sys/devices/virtual/dmi/id/$2"
value="UNKNOWN"
if [[ -e "$file" ]]; then
value="$(cat "$file")"
fi
print_in_columns "$name: $value"
name="$1"
file="/sys/devices/virtual/dmi/id/$2"
value="UNKNOWN"
if [[ -e "$file" ]]; then
value="$(cat "$file")"
fi
print_in_columns "$name: $value"
}
# Check if running under Linux
os_name="$(uname -s)"
if [[ "$os_name" == "Darwin" ]]; then
os_name="macOS"
fi
if [[ "$os_name" != "Linux" ]]; then
echo "This script is not supported under $os_name." 1>&2
exit 1
fi
# System
echo -e "${BLUE}System Information${CLEAR}"
print_dmi_value "Vendor" "sys_vendor"
@ -50,58 +60,58 @@ echo ""
# Processor
echo -e "${BLUE}Processor${CLEAR}"
lscpu | grep -E '^(Arch|CPU.s.|Core|Thread|Model name|Virt)' \
| sed -r 's/\(s\)(.*:)/s\1 /' \
| sed -r 's/CPUs: /Threads:/' \
| sed -r 's/^(.*:) / \1/'
| sed -r 's/\(s\)(.*:)/s\1 /' \
| sed -r 's/CPUs: /Threads:/' \
| sed -r 's/^(.*:) / \1/'
echo ""
# Memory
echo -e "${BLUE}Memory${CLEAR}"
first_device="True"
while read -r line; do
if [[ "$line" == "Memory Device" ]]; then
if [[ "$first_device" == "True" ]]; then
first_device="False"
else
# Add space between devices
echo ""
fi
if [[ "$line" == "Memory Device" ]]; then
if [[ "$first_device" == "True" ]]; then
first_device="False"
else
print_in_columns "$line"
# Add space between devices
echo ""
fi
else
print_in_columns "$line"
fi
done <<< $(sudo dmidecode -t memory \
| grep -E '^(Memory Device|\s+(Type|Size|Speed|Manuf.*|Locator|Part Number):)')
| grep -E '^(Memory Device|\s+(Type|Size|Speed|Manuf.*|Locator|Part Number):)')
echo ""
# Graphics
echo -e "${BLUE}Graphics${CLEAR}"
lspci | grep 'VGA' | sed -r 's/^.*:/ Device: /' \
| sed 's/Intel Corporation/Intel/' \
| sed 's/Generation Core Processor Family/Gen/' \
| sed 's/Integrated Graphics Controller.*/iGPU/'
glxinfo 2>/dev/null | grep 'OpenGL renderer' | sed -r 's/^.*:/ OpenGL Renderer: /' \
| sed 's/Mesa DRI //'
lspci | grep 'VGA' | sed -r 's/^.*:/ Device: /' \
| sed 's/Intel Corporation/Intel/' \
| sed 's/Generation Core Processor Family/Gen/' \
| sed 's/Integrated Graphics Controller.*/iGPU/'
glxinfo 2>/dev/null | grep 'OpenGL renderer' | sed -r 's/^.*:/ OpenGL Renderer: /' \
| sed 's/Mesa DRI //'
echo ""
# Audio
echo -e "${BLUE}Audio${CLEAR}"
while read -r line; do
if [[ "$line" =~ .*no.soundcards.found.* ]]; then
echo " No soundcards found"
else
print_in_columns "$line"
fi
if [[ "$line" = .*no.soundcards.found.* ]]; then
echo " No soundcards found"
else
print_in_columns "$line"
fi
done <<< $(aplay -l 2>&1 | grep -Ei '(^card|no soundcards found)' | sed -r 's/.*\[(.*)\].*\[(.*)\].*/\1: \2/')
echo ""
# Network
echo -e "${BLUE}Network${CLEAR}"
lspci | grep -Ei '(ethernet|network|wireless|wifi)' \
| sed -r 's/.*: (.*)$/ \1/'
| sed -r 's/.*: (.*)$/ \1/'
echo ""
# Drives
echo -e "${BLUE}Drives${CLEAR}"
hw-drive-info | sed 's/^/ /'
hw-drive-info | sed 's/^/ /'
echo ""

46
scripts/hw-sensors Executable file
View file

@ -0,0 +1,46 @@
#!/usr/bin/env python3
"""Wizard Kit: Hardware Sensors"""
# vim: sts=2 sw=2 ts=2
import platform
import wk
def main():
"""Show sensor data on screen."""
sensors = wk.hw.sensors.Sensors()
if platform.system() == 'Darwin':
wk.std.clear_screen()
while True:
print('\033[100A', end='')
sensors.update_sensor_data()
wk.std.print_report(sensors.generate_report('Current', 'Max'))
wk.std.sleep(1)
elif platform.system() == 'Linux':
proc = wk.exe.run_program(cmd=['mktemp'])
sensors.start_background_monitor(
out_path=proc.stdout.strip(),
exit_on_thermal_limit=False,
temp_labels=('Current', 'Max'),
)
watch_cmd = [
'watch',
'--color',
'--exec',
'--no-title',
'--interval', '1',
'cat',
proc.stdout.strip(),
]
wk.exe.run_program(watch_cmd, check=False, pipe=False)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
pass
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

View file

@ -13,16 +13,16 @@ function ask() {
done
}
die () {
function err () {
echo "$0:" "$@" >&2
exit 1
return 1
}
function launch_in_tmux() {
# Check for required vars
[[ -n "${SESSION_NAME:-}" ]] || die "Required variable missing (SESSION_NAME)"
[[ -n "${WINDOW_NAME:-}" ]] || die "Required variable missing (WINDOW_NAME)"
[[ -n "${TMUX_CMD:-}" ]] || die "Required variable missing (TMUX_CMD)"
[[ -n "${SESSION_NAME:-}" ]] || return $(err "Required variable missing (SESSION_NAME)")
[[ -n "${WINDOW_NAME:-}" ]] || return $(err "Required variable missing (WINDOW_NAME)")
[[ -n "${TMUX_CMD:-}" ]] || return $(err "Required variable missing (TMUX_CMD)")
# Check for running session
if tmux list-session | grep -q "$SESSION_NAME"; then
@ -32,31 +32,35 @@ function launch_in_tmux() {
if [[ -n "${TMUX:-}" ]]; then
# Running inside TMUX, switch to session
tmux switch-client -t "$SESSION_NAME"
if ! jobs %% >/dev/null 2>&1; then
# No running jobs, try exiting abandoned tmux session
exit 0
fi
else
# Running outside TMUX, attach to session
tmux attach-session -t "$SESSION_NAME"
fi
exit 0
return 0
elif ask "Kill current session and start new session?"; then
tmux kill-session -t "$SESSION_NAME" || \
die "Failed to kill session: $SESSION_NAME"
else
echo "Aborted."
echo ""
echo -n "Press Enter to exit... "
read -r
exit 0
return 1
fi
fi
# Start/Rename session
# Start session
if [[ -n "${TMUX:-}" ]]; then
# Running inside TMUX, rename session/window and open the menu
# Running inside TMUX, save current session/window names
ORIGINAL_SESSION_NAME="$(tmux display-message -p '#S')"
ORIGINAL_WINDOW_NAME="$(tmux display-message -p '#W')"
tmux rename-session "$SESSION_NAME"
tmux rename-window "$WINDOW_NAME"
"$TMUX_CMD" "$@"
tmux rename-session "${SESSION_NAME}_DONE"
tmux rename-window "${WINDOW_NAME}_DONE"
# Restore previous session/window names
tmux rename-session "${ORIGINAL_SESSION_NAME}"
tmux rename-window "${ORIGINAL_WINDOW_NAME}"
else
# Running outside TMUX, start/attach to session
tmux new-session -s "$SESSION_NAME" -n "$WINDOW_NAME" "$TMUX_CMD" "$@"

33
scripts/mount-all-volumes Executable file
View file

@ -0,0 +1,33 @@
#!/usr/bin/env python3
"""Wizard Kit: Mount all volumes"""
# vim: sts=2 sw=2 ts=2
import wk
# Functions
def main():
"""Mount all volumes and show results."""
wk.std.print_standard(f'{wk.cfg.main.KIT_NAME_FULL}: Volume mount tool')
wk.std.print_standard(' ')
# Mount volumes and get report
wk.std.print_standard('Mounting volumes...')
report = wk.os.linux.mount_volumes()
# Show results
wk.std.print_info('Results')
wk.std.print_report(report, indent=2)
if __name__ == '__main__':
if wk.std.PLATFORM != 'Linux':
os_name = wk.std.PLATFORM.replace('Darwin', 'macOS')
wk.std.print_error(f'This script is not supported under {os_name}.')
wk.std.abort()
try:
main()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

30
scripts/mount-backup-shares Executable file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env python3
"""Wizard Kit: Mount Backup Shares"""
# pylint: disable=invalid-name
# vim: sts=2 sw=2 ts=2
import wk
# Functions
def main():
"""Attempt to mount backup shares and print report."""
wk.std.print_info('Mounting Backup Shares')
report = wk.net.mount_backup_shares()
for line in report:
color = 'GREEN'
line = f' {line}'
if 'Failed' in line:
color = 'RED'
elif 'Already' in line:
color = 'YELLOW'
print(wk.std.color_string(line, color))
if __name__ == '__main__':
try:
main()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

37
scripts/safemode_enter.py Normal file
View file

@ -0,0 +1,37 @@
"""Wizard Kit: Enter SafeMode by editing the BCD"""
# vim: sts=2 sw=2 ts=2
import wk
def main():
"""Prompt user to enter safe mode."""
title = f'{wk.cfg.main.KIT_NAME_FULL}: SafeMode Tool'
try_print = wk.std.TryAndPrint()
wk.std.clear_screen()
wk.std.set_title(title)
wk.std.print_info(title)
print('')
# Ask
if not wk.std.ask('Enable booting to SafeMode (with Networking)?'):
wk.std.abort()
print('')
# Configure SafeMode
try_print.run('Set BCD option...', wk.os.win.enable_safemode)
try_print.run('Enable MSI in SafeMode...', wk.os.win.enable_safemode_msi)
# Done
print('Done.')
wk.std.pause('Press Enter to reboot...')
wk.exe.run_program('shutdown -r -t 3'.split(), check=False)
if __name__ == '__main__':
try:
main()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

37
scripts/safemode_exit.py Normal file
View file

@ -0,0 +1,37 @@
"""Wizard Kit: Exit SafeMode by editing the BCD"""
# vim: sts=2 sw=2 ts=2
import wk
def main():
"""Prompt user to exit safe mode."""
title = f'{wk.cfg.main.KIT_NAME_FULL}: SafeMode Tool'
try_print = wk.std.TryAndPrint()
wk.std.clear_screen()
wk.std.set_title(title)
wk.std.print_info(title)
print('')
# Ask
if not wk.std.ask('Disable booting to SafeMode?'):
wk.std.abort()
print('')
# Configure SafeMode
try_print.run('Remove BCD option...', wk.os.win.disable_safemode)
try_print.run('Disable MSI in SafeMode...', wk.os.win.disable_safemode_msi)
# Done
print('Done.')
wk.std.pause('Press Enter to reboot...')
wk.exe.run_program('shutdown -r -t 3'.split(), check=False)
if __name__ == '__main__':
try:
main()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

35
scripts/sfc_scan.py Normal file
View file

@ -0,0 +1,35 @@
"""Wizard Kit: Check, and possibly repair, system file health via SFC"""
# vim: sts=2 sw=2 ts=2
import wk
def main():
"""Run SFC and report result."""
title = f'{wk.cfg.main.KIT_NAME_FULL}: SFC Tool'
try_print = wk.std.TryAndPrint()
wk.std.clear_screen()
wk.std.set_title(title)
wk.std.print_info(title)
print('')
# Ask
if not wk.std.ask('Run a SFC scan now?'):
wk.std.abort()
print('')
# Run
try_print.run('SFC scan...', wk.os.win.run_sfc_scan)
# Done
print('Done')
wk.std.pause('Press Enter to exit...')
if __name__ == '__main__':
try:
main()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

28
scripts/unmount-backup-shares Executable file
View file

@ -0,0 +1,28 @@
#!/usr/bin/env python3
"""Wizard Kit: Unmount Backup Shares"""
# pylint: disable=invalid-name
# vim: sts=2 sw=2 ts=2
import wk
# Functions
def main():
"""Attempt to mount backup shares and print report."""
wk.std.print_info('Unmounting Backup Shares')
report = wk.net.unmount_backup_shares()
for line in report:
color = 'GREEN'
line = f' {line}'
if 'Not mounted' in line:
color = 'YELLOW'
print(wk.std.color_string(line, color))
if __name__ == '__main__':
try:
main()
except SystemExit:
raise
except: #pylint: disable=bare-except
wk.std.major_exception()

11
scripts/watch-mac Executable file
View file

@ -0,0 +1,11 @@
#!/bin/zsh
#
## watch-like utility
WATCH_FILE="${1}"
while :; do
echo -n "\e[100A"
cat "${WATCH_FILE}"
sleep 1s
done

View file

@ -0,0 +1,434 @@
# Wizard Kit: Functions - Common
import os
import psutil
import re
import shutil
import subprocess
import sys
import time
import traceback
try:
import winreg
except ModuleNotFoundError:
if psutil.WINDOWS:
raise
from settings.main import *
from settings.tools import *
from settings.windows_builds import *
from subprocess import CalledProcessError
# Global variables
global_vars = {}
# STATIC VARIABLES
COLORS = {
'CLEAR': '\033[0m',
'RED': '\033[31m',
'ORANGE': '\033[31;1m',
'GREEN': '\033[32m',
'YELLOW': '\033[33m',
'BLUE': '\033[34m',
'PURPLE': '\033[35m',
'CYAN': '\033[36m',
}
try:
HKU = winreg.HKEY_USERS
HKCR = winreg.HKEY_CLASSES_ROOT
HKCU = winreg.HKEY_CURRENT_USER
HKLM = winreg.HKEY_LOCAL_MACHINE
except NameError:
if psutil.WINDOWS:
raise
# Error Classes
class BIOSKeyNotFoundError(Exception):
pass
class BinNotFoundError(Exception):
pass
class GenericAbort(Exception):
pass
class GenericError(Exception):
pass
class GenericRepair(Exception):
pass
class MultipleInstallationsError(Exception):
pass
class NoProfilesError(Exception):
pass
class Not4KAlignedError(Exception):
pass
class NotInstalledError(Exception):
pass
class OSInstalledLegacyError(Exception):
pass
class PathNotFoundError(Exception):
pass
class UnsupportedOSError(Exception):
pass
class SecureBootDisabledError(Exception):
pass
class SecureBootNotAvailError(Exception):
pass
class SecureBootUnknownError(Exception):
pass
class WindowsOutdatedError(Exception):
pass
class WindowsUnsupportedError(Exception):
pass
# General functions
def exit_script(return_value=0):
"""Exits the script after some cleanup and opens the log (if set)."""
# Remove dirs (if empty)
for dir in ['BackupDir', 'LogDir', 'TmpDir']:
try:
os.rmdir(global_vars[dir])
except Exception:
pass
# Open Log (if it exists)
log = global_vars.get('LogFile', '')
if log and os.path.exists(log) and psutil.WINDOWS and ENABLED_OPEN_LOGS:
try:
extract_item('NotepadPlusPlus', silent=True)
popen_program(
[global_vars['Tools']['NotepadPlusPlus'],
global_vars['LogFile']])
except Exception:
print_error('ERROR: Failed to extract Notepad++ and open log.')
pause('Press Enter to exit...')
# Kill Caffeine if still running
kill_process('caffeine.exe')
# Exit
sys.exit(return_value)
def extract_item(item, filter='', silent=False):
"""Extract item from .cbin into .bin."""
cmd = [
global_vars['Tools']['SevenZip'], 'x', '-aos', '-bso0', '-bse0',
'-p{ArchivePassword}'.format(**global_vars),
r'-o{BinDir}\{item}'.format(item=item, **global_vars),
r'{CBinDir}\{item}.7z'.format(item=item, **global_vars),
filter]
if not silent:
print_standard('Extracting "{item}"...'.format(item=item))
try:
run_program(cmd)
except FileNotFoundError:
if not silent:
print_warning('WARNING: Archive not found')
except subprocess.CalledProcessError:
if not silent:
print_warning('WARNING: Errors encountered while exctracting data')
def get_process(name=None):
"""Get process by name, returns psutil.Process obj."""
proc = None
if not name:
raise GenericError
for p in psutil.process_iter():
try:
if p.name() == name:
proc = p
except psutil._exceptions.NoSuchProcess:
# Process finished during iteration? Going to ignore
pass
return proc
def get_ticket_number():
"""Get TicketNumber from user, save in LogDir, and return as str."""
if not ENABLED_TICKET_NUMBERS:
return None
ticket_number = None
while ticket_number is None:
_input = input('Enter ticket number: ')
if re.match(r'^([0-9]+([-_]?\w+|))$', _input):
ticket_number = _input
out_file = r'{}\TicketNumber'.format(global_vars['LogDir'])
if not psutil.WINDOWS:
out_file = out_file.replace('\\', '/')
with open(out_file, 'w', encoding='utf-8') as f:
f.write(ticket_number)
return ticket_number
def kill_process(name):
"""Kill any running caffeine.exe processes."""
for proc in psutil.process_iter():
if proc.name() == name:
proc.kill()
def stay_awake():
"""Prevent the system from sleeping or hibernating."""
# DISABLED due to VCR2008 dependency
return
# Bail if caffeine is already running
for proc in psutil.process_iter():
if proc.name() == 'caffeine.exe':
return
# Extract and run
extract_item('Caffeine', silent=True)
try:
popen_program([global_vars['Tools']['Caffeine']])
except Exception:
print_error('ERROR: No caffeine available.')
print_warning('Please set the power setting to High Performance.')
def wait_for_process(name, poll_rate=3):
"""Wait for process by name."""
running = True
while running:
sleep(poll_rate)
running = False
for proc in psutil.process_iter():
try:
if re.search(r'^{}'.format(name), proc.name(), re.IGNORECASE):
running = True
except psutil._exceptions.NoSuchProcess:
# Assuming process closed during iteration
pass
sleep(1)
# global_vars functions
def init_global_vars(silent=False):
"""Sets global variables based on system info."""
if not silent:
print_info('Initializing')
if psutil.WINDOWS:
os.system('title Wizard Kit')
if psutil.LINUX:
init_functions = [
['Checking environment...', set_linux_vars],
['Clearing collisions...', clean_env_vars],
]
else:
init_functions = [
['Checking .bin...', find_bin],
['Checking environment...', set_common_vars],
['Checking OS...', check_os],
['Checking tools...', check_tools],
['Creating folders...', make_tmp_dirs],
['Clearing collisions...', clean_env_vars],
]
try:
if silent:
for f in init_functions:
f[1]()
else:
for f in init_functions:
try_and_print(
message=f[0], function=f[1],
cs='Done', ns='Error', catch_all=False)
except:
major_exception()
def check_os():
"""Set OS specific variables."""
tmp = {}
# Query registry
path = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
with winreg.OpenKey(HKLM, path) as key:
for name in ['CurrentBuild', 'CurrentVersion', 'ProductName']:
try:
tmp[name] = winreg.QueryValueEx(key, name)[0]
except FileNotFoundError:
tmp[name] = 'Unknown'
# Handle CurrentBuild collision
if tmp['CurrentBuild'] == '9200':
if tmp['CurrentVersion'] == '6.2':
# Windown 8, set to fake build number
tmp['CurrentBuild'] = '9199'
else:
# Windows 8.1, leave alone
pass
# Check bit depth
tmp['Arch'] = 32
if 'PROGRAMFILES(X86)' in global_vars['Env']:
tmp['Arch'] = 64
# Get Windows build info
build_info = WINDOWS_BUILDS.get(tmp['CurrentBuild'], None)
if build_info is None:
# Not in windows_builds.py
build_info = [
'Unknown',
'Build {}'.format(tmp['CurrentBuild']),
None,
None,
'unrecognized']
else:
build_info = list(build_info)
tmp['Version'] = build_info.pop(0)
tmp['Release'] = build_info.pop(0)
tmp['Codename'] = build_info.pop(0)
tmp['Marketing Name'] = build_info.pop(0)
tmp['Notes'] = build_info.pop(0)
# Set name
tmp['Name'] = tmp['ProductName']
if tmp['Release']:
tmp['Name'] += ' {}'.format(tmp['Release'])
if tmp['Codename']:
tmp['Name'] += ' "{}"'.format(tmp['Codename'])
if tmp['Marketing Name']:
tmp['Name'] += ' / "{}"'.format(tmp['Marketing Name'])
tmp['Name'] = re.sub(r'\s+', ' ', tmp['Name'])
# Set display name
tmp['DisplayName'] = '{} x{}'.format(tmp['Name'], tmp['Arch'])
if tmp['Notes']:
tmp['DisplayName'] += ' ({})'.format(tmp['Notes'])
global_vars['OS'] = tmp
def check_tools():
"""Set tool variables based on OS bit-depth and tool availability."""
if global_vars['OS'].get('Arch', 32) == 64:
global_vars['Tools'] = {
k: v.get('64', v.get('32')) for (k, v) in TOOLS.items()}
else:
global_vars['Tools'] = {k: v.get('32') for (k, v) in TOOLS.items()}
# Fix paths
global_vars['Tools'] = {k: os.path.join(global_vars['BinDir'], v)
for (k, v) in global_vars['Tools'].items()}
def clean_env_vars():
"""Remove conflicting global_vars and env variables.
This fixes an issue where both global_vars and
global_vars['Env'] are expanded at the same time."""
for key in global_vars.keys():
global_vars['Env'].pop(key, None)
def find_bin():
"""Find .bin folder in the cwd or it's parents."""
wd = os.getcwd()
base = None
while base is None:
if os.path.exists('.bin'):
base = os.getcwd()
break
if re.fullmatch(r'\w:\\', os.getcwd()):
break
os.chdir('..')
os.chdir(wd)
if base is None:
raise BinNotFoundError
global_vars['BaseDir'] = base
def generate_global_vars_report():
"""Build readable string from global_vars, returns str."""
report = ['global_vars: {']
for k, v in sorted(global_vars.items()):
if k == 'Env':
continue
if isinstance(v, list):
report.append(' {}: ['.format(str(k)))
for item in v:
report.append(' {}'.format(str(v)))
report.append(' ]')
elif isinstance(v, dict):
report.append(' {}: {{'.format(str(k)))
for item_k, item_v in sorted(v.items()):
report.append(' {:<15} {}'.format(
str(item_k)+':', str(item_v)))
report.append(' }')
else:
report.append(' {:<18}{}'.format(str(k)+':', str(v)))
report.append(' Env:')
for k, v in sorted(global_vars.get('Env', {}).items()):
report.append(' {:<15} {}'.format(
str(k)+':', str(v)))
report.append('}')
return '\n'.join(report)
def make_tmp_dirs():
"""Make temp directories."""
os.makedirs(global_vars['BackupDir'], exist_ok=True)
os.makedirs(global_vars['LogDir'], exist_ok=True)
os.makedirs(r'{}\{}'.format(
global_vars['LogDir'], KIT_NAME_FULL), exist_ok=True)
os.makedirs(r'{}\Tools'.format(global_vars['LogDir']), exist_ok=True)
os.makedirs(global_vars['TmpDir'], exist_ok=True)
def set_common_vars():
"""Set common variables."""
global_vars['Date'] = time.strftime("%Y-%m-%d")
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
global_vars['Env'] = os.environ.copy()
global_vars['ArchivePassword'] = ARCHIVE_PASSWORD
global_vars['BinDir'] = r'{BaseDir}\.bin'.format(**global_vars)
global_vars['CBinDir'] = r'{BaseDir}\.cbin'.format(**global_vars)
global_vars['ClientDir'] = r'{SYSTEMDRIVE}\{prefix}'.format(
prefix=KIT_NAME_SHORT, **global_vars['Env'])
global_vars['BackupDir'] = r'{ClientDir}\Backups'.format(**global_vars)
global_vars['LogDir'] = r'{ClientDir}\Logs\{Date}'.format(**global_vars)
global_vars['QuarantineDir'] = r'{ClientDir}\Quarantine'.format(**global_vars)
global_vars['TmpDir'] = r'{BinDir}\tmp'.format(**global_vars)
def set_linux_vars():
"""Set common variables in a Linux environment.
These assume we're running under a WK-Linux build."""
result = run_program(['mktemp', '-d'])
global_vars['TmpDir'] = result.stdout.decode().strip()
global_vars['Date'] = time.strftime("%Y-%m-%d")
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
global_vars['Env'] = os.environ.copy()
global_vars['BinDir'] = '/usr/local/bin'
global_vars['LogDir'] = '{}/Logs'.format(global_vars['Env']['HOME'])
global_vars['Tools'] = {
'wimlib-imagex': 'wimlib-imagex',
'SevenZip': '7z',
}
if __name__ == '__main__':
print("This file is not meant to be called directly.")
# vim: sts=2 sw=2 ts=2

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