Compare commits

..

No commits in common. "master" and "v1.8.0" have entirely different histories.

84 changed files with 1933 additions and 2809 deletions

View file

@ -55,8 +55,8 @@ if __name__ == '__main__':
print_success('\nDone.') print_success('\nDone.')
pause("Press Enter to exit...") pause("Press Enter to exit...")
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -1,81 +0,0 @@
#!/bin/env python3
#
## Convert saved WiFi connections for NetworkManager
import os
import re
import sys
import uuid
KNOWN_NETWORKS = '/root/known_networks'
TEMPLATE = '''[connection]
id={ssid}
uuid={uuid}
type=wifi
permissions=user:{user}:;
[wifi]
mac-address-blacklist=
mode=infrastructure
ssid={ssid}
[wifi-security]
auth-alg=open
key-mgmt=wpa-psk
psk={password}
[ipv4]
dns-search=
method=auto
[ipv6]
addr-gen-mode=stable-privacy
dns-search=
method=auto
'''
def get_user_name():
"""Get user name, returns str."""
user = None
# Get running user
if 'SUDO_USER' in os.environ:
user = os.environ.get('SUDO_USER')
else:
user = os.environ.get('USER')
# Check if user manually specified
for a in sys.argv:
a = a.strip().lower()
if a.startswith('--user='):
user = a.replace('--user=', '')
return user
if __name__ == '__main__':
known_networks = {}
#try:
with open('/root/known_networks', 'r') as f:
for line in f.readlines():
r = re.search(r"^'(.*)':\s+'(.*)'", line.strip())
if r:
known_networks[r.group(1)] = r.group(2)
for ssid, password in known_networks.items():
out_path = '{}/{}.nmconnection'.format(
'/etc/NetworkManager/system-connections',
ssid,
)
if not os.path.exists(out_path):
with open(out_path, 'w') as f:
f.write(TEMPLATE.format(
user=get_user_name(),
ssid=ssid,
password=password,
uuid=uuid.uuid4(),
))
os.chmod(out_path, 0o600)
#except:
# # Meh
# pass
# vim: sts=2 sw=2 ts=2

View file

@ -1,149 +1,671 @@
#!/bin/env python3 #!/usr/bin/env bash
# #
# pylint: disable=no-name-in-module,wildcard-import,wrong-import-position ## Wizard Kit: UFD Build Tool
# vim: sts=2 sw=2 ts=2 #
"""Wizard Kit: UFD build tool""" # Based on a template by BASH3 Boilerplate v2.3.0
# http://bash3boilerplate.sh/#authors
#
# The MIT License (MIT)
# Copyright (c) 2013 Kevin van Zonneveld and contributors
# You are not obligated to bundle the LICENSE file with your b3bp projects as long
# as you leave these references intact in the header comments of your source files.
import os # Exit on error. Append "|| true" if you expect an error.
import sys set -o errexit
# Exit on error inside any functions or subshells.
set -o errtrace
# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR
set -o nounset
# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip`
set -o pipefail
# Turn on traces, useful while debugging but commented out by default
# set -o xtrace
# Init if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
sys.path.append(os.path.dirname(os.path.realpath(__file__))) __i_am_main_script="0" # false
from docopt import docopt
from functions.common import *
from functions.ufd import *
from settings.ufd import *
init_global_vars(silent=True)
# Main section if [[ "${__usage+x}" ]]; then
if __name__ == '__main__': if [[ "${BASH_SOURCE[1]}" = "${0}" ]]; then
# pylint: disable=invalid-name __i_am_main_script="1" # true
# Set log fi
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 __b3bp_external_usage="true"
print_success(KIT_NAME_FULL) __b3bp_tmp_source_idx=1
print_standard('UFD Build Tool') fi
print_standard(' ') else
__i_am_main_script="1" # true
[[ "${__usage+x}" ]] && unset -v __usage
[[ "${__helptext+x}" ]] && unset -v __helptext
fi
# Check if running as root # Set magic variables for current file, directory, os, etc.
if not running_as_root(): __dir="$(cd "$(dirname "${BASH_SOURCE[${__b3bp_tmp_source_idx:-0}]}")" && pwd)"
print_error('ERROR: This script is meant to be run as root.') __file="${__dir}/$(basename "${BASH_SOURCE[${__b3bp_tmp_source_idx:-0}]}")"
abort(False) __base="$(basename "${__file}" .sh)"
__wd="$(pwd)"
__usage_example="Usage: sudo $(basename "${0}") --ufd-device [device] --linux-iso [path] --main-kit [path] --winpe-iso [path]"
__all_args=""
for a in "${@}"; do
if [[ "${a:0:1}" == "-" ]]; then
__all_args="${__all_args} ${a}"
else
__all_args="${__all_args} \"${a}\""
fi
done
# 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: # Define the environment variables (and their defaults) that this script depends on
# Verify selections LOG_LEVEL="${LOG_LEVEL:-6}" # 7 = debug -> 0 = emergency
ufd_dev = verify_ufd(args['--ufd-device']) NO_COLOR="${NO_COLOR:-}" # true = disable color. otherwise autodetected
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 ### Functions
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 function __b3bp_log () {
if args['--update']: local log_level="${1}"
try_and_print( shift
indent=2,
message='Removing Linux...',
function=remove_arch,
)
# Copy sources # shellcheck disable=SC2034
print_standard(' ') local color_debug="\x1b[35m"
print_info('Copy Sources') # shellcheck disable=SC2034
for s_label, s_path in sources.items(): local color_info="\x1b[32m"
try_and_print( # shellcheck disable=SC2034
indent=2, local color_notice="\x1b[34m"
message='Copying {}...'.format(s_label), # shellcheck disable=SC2034
function=copy_source, local color_warning="\x1b[33m"
source=s_path, # shellcheck disable=SC2034
items=ITEMS[s_label], local color_error="\x1b[31m"
overwrite=True, # shellcheck disable=SC2034
) local color_critical="\x1b[1;31m"
# shellcheck disable=SC2034
local color_alert="\x1b[1;33;41m"
# shellcheck disable=SC2034
local color_emergency="\x1b[1;4;5;33;41m"
# Update boot entries local colorvar="color_${log_level}"
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) local color="${!colorvar:-${color_error}}"
try_and_print( local color_reset="\x1b[0m"
indent=2,
message='Syslinux (partition)...',
function=install_syslinux_to_partition,
partition=find_first_partition(ufd_dev),
)
# Unmount UFD if [[ "${NO_COLOR:-}" = "true" ]] || ( [[ "${TERM:-}" != *"256color"* ]] && [[ "${TERM:-}" != "xterm"* ]] && [[ "${TERM:-}" != "screen"* ]] ) || [[ ! -t 2 ]]; then
try_and_print( if [[ "${NO_COLOR:-}" != "false" ]]; then
indent=2, # Don't use colors on pipes or non-recognized terminals
message='Unmounting UFD...', color=""; color_reset=""
function=unmount, fi
mount_point='/mnt/UFD', fi
)
# Install syslinux (to device) # all remaining arguments are to be printed
try_and_print( local log_line=""
indent=2,
message='Syslinux (device)...',
function=install_syslinux_to_dev,
ufd_dev=ufd_dev,
use_mbr=args['--use-mbr'],
)
# Hide items while IFS=$'\n' read -r log_line; do
print_standard(' ') echo -e "$(date -u +"%Y-%m-%d %H:%M:%S UTC") ${color}$(printf "[%9s]" "${log_level}")${color_reset} ${log_line}" 1>&2
print_info('Final Touches') done <<< "${@:-}"
try_and_print( }
indent=2,
message='Hiding items...',
function=hide_items,
ufd_dev=ufd_dev,
items=ITEMS_HIDDEN,
)
# Done function emergency () { __b3bp_log emergency "${@}"; exit 1; }
if not args['--force']: function alert () { [[ "${LOG_LEVEL:-0}" -ge 1 ]] && __b3bp_log alert "${@}"; true; }
print_standard('\nDone.') function critical () { [[ "${LOG_LEVEL:-0}" -ge 2 ]] && __b3bp_log critical "${@}"; true; }
pause('Press Enter to exit...') function error () { [[ "${LOG_LEVEL:-0}" -ge 3 ]] && __b3bp_log error "${@}"; true; }
exit_script() function warning () { [[ "${LOG_LEVEL:-0}" -ge 4 ]] && __b3bp_log warning "${@}"; true; }
except SystemExit as sys_exit: function notice () { [[ "${LOG_LEVEL:-0}" -ge 5 ]] && __b3bp_log notice "${@}"; true; }
exit_script(sys_exit.code) function info () { [[ "${LOG_LEVEL:-0}" -ge 6 ]] && __b3bp_log info "${@}"; true; }
except: # pylint: disable=bare-except function debug () { [[ "${LOG_LEVEL:-0}" -ge 7 ]] && __b3bp_log debug "${@}"; true; }
major_exception()
function help () {
echo "" 1>&2
echo " ${*}" 1>&2
echo "" 1>&2
echo " ${__usage:-No usage available}" 1>&2
echo "" 1>&2
if [[ "${__helptext:-}" ]]; then
echo " ${__helptext}" 1>&2
echo "" 1>&2
fi
exit 1
}
### Parse commandline options
##############################################################################
# Commandline options. This defines the usage page, and is used to parse cli
# opts & defaults from. The parsing is unforgiving so be precise in your syntax
# - A short option must be preset for every long option; but every short option
# need not have a long option
# - `--` is respected as the separator between options and arguments
# - We do not bash-expand defaults, so setting '~/app' as a default will not resolve to ${HOME}.
# you can use bash variables to work around this (so use ${HOME} instead)
# shellcheck disable=SC2015
[[ "${__usage+x}" ]] || read -r -d '' __usage <<-'EOF' || true # exits non-zero when EOF encountered
OPTIONS:
-u --ufd-device [arg] Device to which the kit will be applied
-l --linux-iso [arg] Path to the Linux ISO
-e --extra-dir [arg] Path to the Extra folder (optional)
-m --main-kit [arg] Path to the Main Kit (optional)
-w --winpe-iso [arg] Path to the WinPE ISO (optional)
-h --help This page
ADVANCED:
-d --debug Enable debug mode
-v --verbose Enable verbose mode
-M --use-mbr Use real MBR instead of GPT w/ Protective MBR
-F --force Bypass all confirmation messages. USE WITH EXTREME CAUTION!
EOF
# shellcheck disable=SC2015
[[ "${__helptext+x}" ]] || read -r -d '' __helptext <<-'EOF' || true # exits non-zero when EOF encountered
Paths can be relative to the current working directory or absolute
EOF
# Translate usage string -> getopts arguments, and set $arg_<flag> defaults
while read -r __b3bp_tmp_line; do
if [[ "${__b3bp_tmp_line}" =~ ^- ]]; then
# fetch single character version of option string
__b3bp_tmp_opt="${__b3bp_tmp_line%% *}"
__b3bp_tmp_opt="${__b3bp_tmp_opt:1}"
# fetch long version if present
__b3bp_tmp_long_opt=""
if [[ "${__b3bp_tmp_line}" = *"--"* ]]; then
__b3bp_tmp_long_opt="${__b3bp_tmp_line#*--}"
__b3bp_tmp_long_opt="${__b3bp_tmp_long_opt%% *}"
fi
# map opt long name to+from opt short name
printf -v "__b3bp_tmp_opt_long2short_${__b3bp_tmp_long_opt//-/_}" '%s' "${__b3bp_tmp_opt}"
printf -v "__b3bp_tmp_opt_short2long_${__b3bp_tmp_opt}" '%s' "${__b3bp_tmp_long_opt//-/_}"
# check if option takes an argument
if [[ "${__b3bp_tmp_line}" =~ \[.*\] ]]; then
__b3bp_tmp_opt="${__b3bp_tmp_opt}:" # add : if opt has arg
__b3bp_tmp_init="" # it has an arg. init with ""
printf -v "__b3bp_tmp_has_arg_${__b3bp_tmp_opt:0:1}" '%s' "1"
elif [[ "${__b3bp_tmp_line}" =~ \{.*\} ]]; then
__b3bp_tmp_opt="${__b3bp_tmp_opt}:" # add : if opt has arg
__b3bp_tmp_init="" # it has an arg. init with ""
# remember that this option requires an argument
printf -v "__b3bp_tmp_has_arg_${__b3bp_tmp_opt:0:1}" '%s' "2"
else
__b3bp_tmp_init="0" # it's a flag. init with 0
printf -v "__b3bp_tmp_has_arg_${__b3bp_tmp_opt:0:1}" '%s' "0"
fi
__b3bp_tmp_opts="${__b3bp_tmp_opts:-}${__b3bp_tmp_opt}"
fi
[[ "${__b3bp_tmp_opt:-}" ]] || continue
if [[ "${__b3bp_tmp_line}" =~ (^|\.\ *)Default= ]]; then
# ignore default value if option does not have an argument
__b3bp_tmp_varname="__b3bp_tmp_has_arg_${__b3bp_tmp_opt:0:1}"
if [[ "${!__b3bp_tmp_varname}" != "0" ]]; then
__b3bp_tmp_init="${__b3bp_tmp_line##*Default=}"
__b3bp_tmp_re='^"(.*)"$'
if [[ "${__b3bp_tmp_init}" =~ ${__b3bp_tmp_re} ]]; then
__b3bp_tmp_init="${BASH_REMATCH[1]}"
else
__b3bp_tmp_re="^'(.*)'$"
if [[ "${__b3bp_tmp_init}" =~ ${__b3bp_tmp_re} ]]; then
__b3bp_tmp_init="${BASH_REMATCH[1]}"
fi
fi
fi
fi
if [[ "${__b3bp_tmp_line}" =~ (^|\.\ *)Required\. ]]; then
# remember that this option requires an argument
printf -v "__b3bp_tmp_has_arg_${__b3bp_tmp_opt:0:1}" '%s' "2"
fi
printf -v "arg_${__b3bp_tmp_opt:0:1}" '%s' "${__b3bp_tmp_init}"
done <<< "${__usage:-}"
# run getopts only if options were specified in __usage
if [[ "${__b3bp_tmp_opts:-}" ]]; then
# Allow long options like --this
__b3bp_tmp_opts="${__b3bp_tmp_opts}-:"
# Reset in case getopts has been used previously in the shell.
OPTIND=1
# start parsing command line
set +o nounset # unexpected arguments will cause unbound variables
# to be dereferenced
# Overwrite $arg_<flag> defaults with the actual CLI options
while getopts "${__b3bp_tmp_opts}" __b3bp_tmp_opt; do
[[ "${__b3bp_tmp_opt}" = "?" ]] && help "Invalid use of script: ${*} "
if [[ "${__b3bp_tmp_opt}" = "-" ]]; then
# OPTARG is long-option-name or long-option=value
if [[ "${OPTARG}" =~ .*=.* ]]; then
# --key=value format
__b3bp_tmp_long_opt=${OPTARG/=*/}
# Set opt to the short option corresponding to the long option
__b3bp_tmp_varname="__b3bp_tmp_opt_long2short_${__b3bp_tmp_long_opt//-/_}"
printf -v "__b3bp_tmp_opt" '%s' "${!__b3bp_tmp_varname}"
OPTARG=${OPTARG#*=}
else
# --key value format
# Map long name to short version of option
__b3bp_tmp_varname="__b3bp_tmp_opt_long2short_${OPTARG//-/_}"
printf -v "__b3bp_tmp_opt" '%s' "${!__b3bp_tmp_varname}"
# Only assign OPTARG if option takes an argument
__b3bp_tmp_varname="__b3bp_tmp_has_arg_${__b3bp_tmp_opt}"
printf -v "OPTARG" '%s' "${@:OPTIND:${!__b3bp_tmp_varname}}"
# shift over the argument if argument is expected
((OPTIND+=__b3bp_tmp_has_arg_${__b3bp_tmp_opt}))
fi
# we have set opt/OPTARG to the short value and the argument as OPTARG if it exists
fi
__b3bp_tmp_varname="arg_${__b3bp_tmp_opt:0:1}"
__b3bp_tmp_default="${!__b3bp_tmp_varname}"
__b3bp_tmp_value="${OPTARG}"
if [[ -z "${OPTARG}" ]] && [[ "${__b3bp_tmp_default}" = "0" ]]; then
__b3bp_tmp_value="1"
fi
printf -v "${__b3bp_tmp_varname}" '%s' "${__b3bp_tmp_value}"
debug "cli arg ${__b3bp_tmp_varname} = (${__b3bp_tmp_default}) -> ${!__b3bp_tmp_varname}"
done
set -o nounset # no more unbound variable references expected
shift $((OPTIND-1))
if [[ "${1:-}" = "--" ]] ; then
shift
fi
fi
### Automatic validation of required option arguments
##############################################################################
for __b3bp_tmp_varname in ${!__b3bp_tmp_has_arg_*}; do
# validate only options which required an argument
[[ "${!__b3bp_tmp_varname}" = "2" ]] || continue
__b3bp_tmp_opt_short="${__b3bp_tmp_varname##*_}"
__b3bp_tmp_varname="arg_${__b3bp_tmp_opt_short}"
[[ "${!__b3bp_tmp_varname}" ]] && continue
__b3bp_tmp_varname="__b3bp_tmp_opt_short2long_${__b3bp_tmp_opt_short}"
printf -v "__b3bp_tmp_opt_long" '%s' "${!__b3bp_tmp_varname}"
[[ "${__b3bp_tmp_opt_long:-}" ]] && __b3bp_tmp_opt_long=" (--${__b3bp_tmp_opt_long//_/-})"
help "Option -${__b3bp_tmp_opt_short}${__b3bp_tmp_opt_long:-} requires an argument"
done
### Cleanup Environment variables
##############################################################################
for __tmp_varname in ${!__b3bp_tmp_*}; do
unset -v "${__tmp_varname}"
done
unset -v __tmp_varname
### Externally supplied __usage. Nothing else to do here
##############################################################################
if [[ "${__b3bp_external_usage:-}" = "true" ]]; then
unset -v __b3bp_external_usage
return
fi
### Signal trapping and backtracing
##############################################################################
function __b3bp_cleanup_before_exit () {
if [[ "$EUID" -eq 0 ]]; then
for d in Dest Linux WinPE; do
if [[ -d "/mnt/${d}" ]]; then
umount "/mnt/${d}" || true
rmdir "/mnt/${d}" || true
fi
done
fi
if [[ "${?}" != "0" ]]; then
info "Sources unmounted"
fi
if [[ ${arg_F:-} == 0 && "${SILENT:-False}" == "False" ]]; then
read -r -p "Press Enter to exit... " ignored_var 2>&1
fi
}
trap __b3bp_cleanup_before_exit EXIT
# requires `set -o errtrace`
__b3bp_err_report() {
local error_code
error_code=${?}
error "Error in ${__file} in function ${1} on line ${2}"
exit ${error_code}
}
# Uncomment the following line for always providing an error backtrace
trap '__b3bp_err_report "${FUNCNAME:-.}" ${LINENO}' ERR
### Command-line argument switches (like -d for debugmode, -h for showing helppage)
##############################################################################
# debug mode
if [[ "${arg_d:?}" = "1" ]]; then
set -o xtrace
LOG_LEVEL="7"
# Enable error backtracing
trap '__b3bp_err_report "${FUNCNAME:-.}" ${LINENO}' ERR
fi
# verbose mode
if [[ "${arg_v:?}" = "1" ]]; then
set -o verbose
fi
### Validation. Error out if the things required for your script are not present
##############################################################################
if [[ "${arg_F:?}" == 1 ]]; then
SILENT="True"
else
SILENT="False"
fi
if [[ "${arg_M:?}" == 1 ]]; then
USE_MBR="True"
else
USE_MBR="False"
fi
if [[ "${arg_h:?}" == 1 ]]; then
help "${__usage_example}"
else
# Print warning line
[[ "${arg_u:-}" ]] || echo " -u or --ufd-device is required"
[[ "${arg_l:-}" ]] || echo " -l or --linux-iso is required"
# Bail if necessary
[[ "${arg_u:-}" ]] || help "${__usage_example}"
[[ "${arg_l:-}" ]] || help "${__usage_example}"
fi
[[ "${LOG_LEVEL:-}" ]] || emergency "Cannot continue without LOG_LEVEL. "
### More functions
##############################################################################
function abort () {
local abort_message="Aborted"
[[ "${1:-}" ]] && abort_message="${1}" || true
error "${abort_message}"
#echo -e "${YELLOW}${abort_message}${CLEAR}"
exit 1
}
function ask() {
if [[ "${SILENT}" == "True" ]]; then
echo -e "${1:-} Yes ${BLUE}(Silent)${CLEAR}"
return 0
fi
while :; do
read -p "${1:-} [Y/N] " -r answer
if echo "$answer" | egrep -iq '^(y|yes|sure)$'; then
return 0
elif echo "$answer" | egrep -iq '^(n|no|nope)$'; then
return 1
fi
done
}
### Runtime
##############################################################################
# VARIABLES
DEST_DEV="${arg_u}"
DEST_PAR="${DEST_DEV}1"
LOG_FILE="$(getent passwd "$SUDO_USER" | cut -d: -f6)/Logs/build-ufd_${DEST_DEV##*/}_$(date +%Y-%m-%d_%H%M_%z).log"
MAIN_PY="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/settings/main.py"
RSYNC_ARGS="-hrtuvS --modify-window=1 --progress"
MAIN_KIT="$(realpath "${arg_m:-}" 2>/dev/null || true)"
LINUX_ISO="$(realpath "${arg_l:-}" 2>/dev/null || true)"
WINPE_ISO="$(realpath "${arg_w:-}" 2>/dev/null || true)"
EXTRA_DIR="$(realpath "${arg_e:-}" 2>/dev/null || true)"
mkdir -p "$(dirname "$LOG_FILE")"
chown "$SUDO_USER:$SUDO_USER" -R "$(dirname "$LOG_FILE")"
# COLORS
CLEAR="\e[0m"
RED="\e[31m"
GREEN="\e[32m"
YELLOW="\e[33m"
BLUE="\e[34m"
# Load main.py settings
if [ ! -f "${MAIN_PY}" ]; then
echo -e "${RED}ERROR${CLEAR}: ${MAIN_PY} not found."
abort
fi
while read line; do
if echo "${line}" | egrep -q "^\w+='"; then
line="$(echo "${line}" | sed -r 's/[\r\n]+//')"
eval "${line}"
fi
done < "${MAIN_PY}"
if [ -z ${KIT_NAME_FULL+x} ]; then
# KIT_NAME_FULL is not set, assume main.py missing or malformatted
echo -e "${RED}ERROR${CLEAR}: failed to load settings from ${MAIN_PY}"
abort
fi
ISO_LABEL="${KIT_NAME_SHORT}_LINUX"
UFD_LABEL="${KIT_NAME_SHORT}_UFD"
# Check if root
if [[ "$EUID" -ne 0 ]]; then
echo -e "${RED}ERROR${CLEAR}: This script must be run as root."
abort
fi
# Check if in tmux
if ! tmux list-session 2>/dev/null | grep -q "build-ufd"; then
# Reload in tmux
eval tmux new-session -s "build-ufd" "${0:-}" ${__all_args}
SILENT="True" # avoid two "Press Enter to exit..." prompts
exit 0
fi
# Header
echo -e "${GREEN}${KIT_NAME_FULL}${CLEAR}: UFD Build Tool"
echo ""
# Verify sources
[[ -b "${DEST_DEV}" ]] || abort "${DEST_DEV} is not a valid device."
[[ -e "${LINUX_ISO}" ]] || abort "Linux ISO not found."
if [[ ! -z "${arg_m:-}" ]]; then
[[ -d "${MAIN_KIT}/.bin" ]] || abort "Invalid Main Kit, ${MAIN_KIT}/.bin not found."
fi
if [[ ! -z "${arg_w:-}" ]]; then
[[ -e "${WINPE_ISO}" ]] || abort "WinPE ISO not found."
fi
if [[ ! -z "${arg_e:-}" ]]; then
[[ -d "${EXTRA_DIR}" ]] || abort "Extra Dir not found."
fi
# Print Info
echo -e "${BLUE}Sources${CLEAR}" | tee -a "${LOG_FILE}"
echo "Main Kit: ${MAIN_KIT}" | tee -a "${LOG_FILE}"
echo "Linux ISO: ${LINUX_ISO}" | tee -a "${LOG_FILE}"
echo "WinPE ISO: ${WINPE_ISO}" | tee -a "${LOG_FILE}"
echo "Extra Dir: ${EXTRA_DIR:-(Not Specified)}" | tee -a "${LOG_FILE}"
echo "" | tee -a "${LOG_FILE}"
echo -e "${BLUE}Destination${CLEAR}" | tee -a "${LOG_FILE}"
lsblk -n -o NAME,LABEL,SIZE,MODEL,SERIAL "${DEST_DEV}" | tee -a "${LOG_FILE}"
if [[ "${USE_MBR}" == "True" ]]; then
echo -e "${YELLOW}Formatting using legacy MBR${CLEAR}" | tee -a "${LOG_FILE}"
fi
echo "" | tee -a "${LOG_FILE}"
# Ask before starting job
echo ""
if ask "Is the above information correct?"; then
echo ""
echo -e "${YELLOW}SAFETY CHECK${CLEAR}"
echo "All data will be DELETED from the disk and partition(s) listed above."
echo -e "This is irreversible and will lead to ${RED}DATA LOSS.${CLEAR}"
if ! ask "Asking again to confirm, is this correct?"; then
abort
fi
else
abort
fi
# Start Build
echo "" | tee -a "${LOG_FILE}"
echo -e "${GREEN}Building Kit${CLEAR}" | tee -a "${LOG_FILE}"
touch "${LOG_FILE}"
tmux split-window -dl 10 tail -f "${LOG_FILE}"
# Zero beginning of device
dd bs=4M count=16 if=/dev/zero of="${DEST_DEV}" >> "${LOG_FILE}" 2>&1
# Format
echo "Formatting drive..." | tee -a "${LOG_FILE}"
if [[ "${USE_MBR}" == "True" ]]; then
parted "${DEST_DEV}" --script -- mklabel msdos mkpart primary fat32 4MiB -1s >> "${LOG_FILE}" 2>&1
parted "${DEST_DEV}" set 1 boot on >> "${LOG_FILE}" 2>&1
else
parted "${DEST_DEV}" --script -- mklabel gpt mkpart primary fat32 4MiB -4MiB >> "${LOG_FILE}" 2>&1
parted "${DEST_DEV}" set 1 legacy_boot on >> "${LOG_FILE}" 2>&1
#parted "${DEST_DEV}" disk_set pmbr_boot on >> "${LOG_FILE}" 2>&1
# pmbr_boot breaks detection on some UEFI MOBOs
fi
mkfs.vfat -F 32 -n "${UFD_LABEL}" "${DEST_PAR}" >> "${LOG_FILE}" 2>&1
# Mount sources and dest
echo "Mounting sources and destination..." | tee -a "${LOG_FILE}"
mkdir /mnt/{Dest,Linux,WinPE} -p >> "${LOG_FILE}" 2>&1
mount ${DEST_PAR} /mnt/Dest >> "${LOG_FILE}" 2>&1
mount "${LINUX_ISO}" /mnt/Linux -r >> "${LOG_FILE}" 2>&1
if [[ ! -z "${arg_w:-}" ]]; then
mount "${WINPE_ISO}" /mnt/WinPE -r >> "${LOG_FILE}" 2>&1
fi
# Find WinPE source
w_boot="$(find /mnt/WinPE -iwholename "/mnt/WinPE/Boot")"
w_boot_bcd="$(find /mnt/WinPE -iwholename "/mnt/WinPE/Boot/BCD")"
w_boot_sdi="$(find /mnt/WinPE -iwholename "/mnt/WinPE/Boot/boot.sdi")"
w_bootmgr="$(find /mnt/WinPE -iwholename "/mnt/WinPE/bootmgr")"
w_bootmgr_efi="$(find /mnt/WinPE -iwholename "/mnt/WinPE/bootmgr.efi")"
w_efi_boot="$(find /mnt/WinPE -iwholename "/mnt/WinPE/EFI/Boot")"
w_efi_microsoft="$(find /mnt/WinPE -iwholename "/mnt/WinPE/EFI/Microsoft")"
w_en_us="$(find /mnt/WinPE -iwholename "/mnt/WinPE/en-us")"
w_sources="$(find /mnt/WinPE -iwholename "/mnt/WinPE/sources")"
# Copy files
echo "Copying Linux files..." | tee -a "${LOG_FILE}"
rsync ${RSYNC_ARGS} /mnt/Linux/* /mnt/Dest/ >> "${LOG_FILE}" 2>&1
sed -i "s/${ISO_LABEL}/${UFD_LABEL}/" /mnt/Dest/EFI/boot/refind.conf
sed -i "s/${ISO_LABEL}/${UFD_LABEL}/" /mnt/Dest/arch/boot/syslinux/*cfg
echo "Copying WinPE files..." | tee -a "${LOG_FILE}"
if [[ ! -z "${arg_w:-}" ]]; then
if [[ ! -z "${w_bootmgr:-}" ]]; then
rsync ${RSYNC_ARGS} "${w_bootmgr}" /mnt/Dest/ >> "${LOG_FILE}" 2>&1
fi
if [[ ! -z "${w_bootmgr_efi:-}" ]]; then
rsync ${RSYNC_ARGS} "${w_bootmgr_efi}" /mnt/Dest/ >> "${LOG_FILE}" 2>&1
fi
if [[ ! -z "${w_en_us:-}" ]]; then
rsync ${RSYNC_ARGS} "${w_en_us}" /mnt/Dest/ >> "${LOG_FILE}" 2>&1
fi
if [[ ! -z "${w_boot:-}" ]]; then
rsync ${RSYNC_ARGS} "${w_boot}"/* /mnt/Dest/Boot/ >> "${LOG_FILE}" 2>&1
fi
if [[ ! -z "${w_efi_boot:-}" ]]; then
rsync ${RSYNC_ARGS} "${w_efi_boot}"/* /mnt/Dest/EFI/Microsoft/ >> "${LOG_FILE}" 2>&1
fi
if [[ ! -z "${w_efi_microsoft:-}" ]]; then
rsync ${RSYNC_ARGS} "${w_efi_microsoft}"/* /mnt/Dest/EFI/Microsoft/ >> "${LOG_FILE}" 2>&1
fi
if [[ ! -z "${w_boot_bcd:-}" ]]; then
rsync ${RSYNC_ARGS} "${w_boot_bcd}" /mnt/Dest/sources/ >> "${LOG_FILE}" 2>&1
fi
if [[ ! -z "${w_boot_sdi:-}" ]]; then
rsync ${RSYNC_ARGS} "${w_boot_sdi}" /mnt/Dest/sources/ >> "${LOG_FILE}" 2>&1
fi
if [[ ! -z "${w_bootmgr:-}" ]]; then
rsync ${RSYNC_ARGS} "${w_bootmgr}" /mnt/Dest/sources/ >> "${LOG_FILE}" 2>&1
fi
if [[ ! -z "${w_sources:-}" ]]; then
rsync ${RSYNC_ARGS} "${w_sources}"/* /mnt/Dest/sources/ >> "${LOG_FILE}" 2>&1
fi
# Uncomment boot entries
sed -i "s/#UFD-WINPE#//" /mnt/Dest/EFI/boot/refind.conf
sed -i "s/#UFD-WINPE#//" /mnt/Dest/arch/boot/syslinux/*cfg
fi
echo "Copying Main Kit..." | tee -a "${LOG_FILE}"
if [[ ! -z "${arg_m:-}" ]]; then
rsync ${RSYNC_ARGS} \
"${MAIN_KIT}/" \
"/mnt/Dest/${KIT_NAME_FULL}/" >> "${LOG_FILE}" 2>&1
fi
if [[ ! -z "${EXTRA_DIR:-}" ]]; then
echo "Copying Extra files..." | tee -a "${LOG_FILE}"
rsync ${RSYNC_ARGS} \
"${EXTRA_DIR}"/ \
/mnt/Dest/ >> "${LOG_FILE}" 2>&1
fi
# Install syslinux
echo "Copying Syslinux files..." | tee -a "${LOG_FILE}"
rsync ${RSYNC_ARGS} /usr/lib/syslinux/bios/*.c32 /mnt/Dest/arch/boot/syslinux/ >> "${LOG_FILE}" 2>&1
syslinux --install -d /arch/boot/syslinux/ ${DEST_PAR} >> "${LOG_FILE}" 2>&1
echo "Unmounting destination..." | tee -a "${LOG_FILE}"
umount /mnt/Dest >> "${LOG_FILE}" 2>&1
rmdir /mnt/Dest >> "${LOG_FILE}" 2>&1
sync
echo "Installing Syslinux MBR..." | tee -a "${LOG_FILE}"
if [[ "${USE_MBR}" == "True" ]]; then
dd bs=440 count=1 if=/usr/lib/syslinux/bios/mbr.bin of=${DEST_DEV} >> "${LOG_FILE}" 2>&1
else
dd bs=440 count=1 if=/usr/lib/syslinux/bios/gptmbr.bin of=${DEST_DEV} >> "${LOG_FILE}" 2>&1
fi
sync
# Cleanup
echo "Hiding boot files..." | tee -a "${LOG_FILE}"
echo "drive s: file=\"${DEST_PAR}\"" > /root/.mtoolsrc
echo 'mtools_skip_check=1' >> /root/.mtoolsrc
for item in arch Boot bootmgr{,.efi} EFI en-us images isolinux sources "${KIT_NAME_FULL}"/{.bin,.cbin}; do
yes | mattrib +h "S:/${item}" >> "${LOG_FILE}" 2>&1 || true
done
sync
# Unmount Sources
echo "Unmounting sources..." | tee -a "${LOG_FILE}"
for d in Linux WinPE; do
umount "/mnt/${d}" >> "${LOG_FILE}" 2>&1 || true
rmdir "/mnt/${d}" >> "${LOG_FILE}" 2>&1 || true
done
# Close progress pane
pkill -f "tail.*${LOG_FILE}"
# Done
echo "" | tee -a "${LOG_FILE}"
echo "Done." | tee -a "${LOG_FILE}"
echo ""
exit 0

View file

@ -35,8 +35,8 @@ if __name__ == '__main__':
print_standard('\nDone.') print_standard('\nDone.')
pause("Press Enter to exit...") pause("Press Enter to exit...")
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -38,7 +38,7 @@ if __name__ == '__main__':
if repair: if repair:
cs = 'Scheduled' cs = 'Scheduled'
else: else:
cs = 'No issues' cs = 'CS'
message = 'CHKDSK ({SYSTEMDRIVE})...'.format(**global_vars['Env']) message = 'CHKDSK ({SYSTEMDRIVE})...'.format(**global_vars['Env'])
try_and_print(message=message, function=run_chkdsk, try_and_print(message=message, function=run_chkdsk,
cs=cs, other_results=other_results, repair=repair) cs=cs, other_results=other_results, repair=repair)
@ -49,8 +49,8 @@ if __name__ == '__main__':
print_success('Done.') print_success('Done.')
pause("Press Enter to exit...") pause("Press Enter to exit...")
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

30
.bin/Scripts/connect-to-network Executable file
View file

@ -0,0 +1,30 @@
#!/bin/python3
#
## Wizard Kit: Network connection tool
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.network import *
init_global_vars()
if __name__ == '__main__':
try:
# Prep
clear_screen()
# Connect
connect_to_network()
# Done
print_standard('\nDone.')
#pause("Press Enter to exit...")
exit_script()
except SystemExit:
pass
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -2,10 +2,42 @@
# #
## Wizard Kit: ddrescue TUI Launcher ## Wizard Kit: ddrescue TUI Launcher
source launch-in-tmux
SESSION_NAME="ddrescue-tui" SESSION_NAME="ddrescue-tui"
WINDOW_NAME="ddrescue TUI" WINDOW_NAME="ddrescue TUI"
TMUX_CMD="ddrescue-tui-menu" MENU="ddrescue-tui-menu"
function ask() {
while :; do
read -p "$1 " -r answer
if echo "$answer" | egrep -iq '^(y|yes|sure)$'; then
return 0
elif echo "$answer" | egrep -iq '^(n|no|nope)$'; then
return 1
fi
done
}
die () {
echo "$0:" "$@" >&2
exit 1
}
# Check for running session
if tmux list-session | grep -q "$SESSION_NAME"; then
echo "WARNING: tmux session $SESSION_NAME already exists."
echo ""
if ask "Kill current 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
fi
fi
# Start session
tmux new-session -s "$SESSION_NAME" -n "$WINDOW_NAME" "$MENU" $*
launch_in_tmux "$@"

View file

@ -45,7 +45,6 @@ if __name__ == '__main__':
# Done # Done
print_standard('\nDone.') print_standard('\nDone.')
pause("Press Enter to exit...") pause("Press Enter to exit...")
tmux_switch_client()
exit_script() exit_script()
except GenericAbort: except GenericAbort:
abort() abort()
@ -55,9 +54,8 @@ if __name__ == '__main__':
msg = str(ge) msg = str(ge)
print_error(msg) print_error(msg)
abort() abort()
except SystemExit as sys_exit: except SystemExit:
tmux_switch_client() pass
exit_script(sys_exit.code)
except: except:
major_exception() major_exception()

View file

@ -149,14 +149,11 @@ def save_debug_reports(state, global_vars):
f.write('{}\n'.format(line)) f.write('{}\n'.format(line))
def upload_logdir(global_vars, reason='Crash'): def upload_logdir(global_vars):
"""Upload compressed LogDir to CRASH_SERVER.""" """Upload compressed LogDir to CRASH_SERVER."""
source = global_vars['LogDir'] source = global_vars['LogDir']
source = source[source.rfind('/')+1:] source = source[source.rfind('/')+1:]
dest = 'HW-Diags_{reason}_{Date-Time}.txz'.format( dest = '{}.txz'.format(source)
reason=reason,
**global_vars,
)
data = None data = None
# Compress LogDir # Compress LogDir
@ -169,7 +166,7 @@ def upload_logdir(global_vars, reason='Crash'):
data = f.read() data = f.read()
# Upload data # Upload data
url = '{}/{}'.format(CRASH_SERVER['Url'], dest) url = '{}/Crash_{}.txz'.format(CRASH_SERVER['Url'], source)
r = requests.put( r = requests.put(
url, url,
data=data, data=data,

View file

@ -50,8 +50,8 @@ if __name__ == '__main__':
print_success('Done.') print_success('Done.')
pause("Press Enter to exit...") pause("Press Enter to exit...")
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -32,8 +32,8 @@ def archive_all_users():
user_path = os.path.join(users_root, user_name) user_path = os.path.join(users_root, user_name)
appdata_local = os.path.join(user_path, r'AppData\Local') appdata_local = os.path.join(user_path, r'AppData\Local')
appdata_roaming = os.path.join(user_path, r'AppData\Roaming') appdata_roaming = os.path.join(user_path, r'AppData\Roaming')
valid_user = valid_user and os.path.exists(appdata_local) valid_user &= os.path.exists(appdata_local)
valid_user = valid_user and os.path.exists(appdata_roaming) valid_user &= os.path.exists(appdata_roaming)
if valid_user: if valid_user:
user_envs.append({ user_envs.append({
'USERNAME': user_name, 'USERNAME': user_name,
@ -325,6 +325,7 @@ def install_adblock(indent=8, width=32, just_firefox=False):
if just_firefox and browser_data[browser]['base'] != 'mozilla': if just_firefox and browser_data[browser]['base'] != 'mozilla':
continue continue
exe_path = browser_data[browser].get('exe_path', None) exe_path = browser_data[browser].get('exe_path', None)
function=run_program
if not exe_path: if not exe_path:
if browser_data[browser]['profiles']: if browser_data[browser]['profiles']:
print_standard( print_standard(
@ -374,6 +375,7 @@ def install_adblock(indent=8, width=32, just_firefox=False):
elif browser_data[browser]['base'] == 'ie': elif browser_data[browser]['base'] == 'ie':
urls.append(IE_GALLERY) urls.append(IE_GALLERY)
function=popen_program
# By using check=False we're skipping any return codes so # By using check=False we're skipping any return codes so
# it should only fail if the program can't be run # it should only fail if the program can't be run
@ -382,16 +384,10 @@ def install_adblock(indent=8, width=32, just_firefox=False):
# installation status. # installation status.
try_and_print(message='{}...'.format(browser), try_and_print(message='{}...'.format(browser),
indent=indent, width=width, indent=indent, width=width,
cs='Started', function=popen_program, cs='Done', function=function,
cmd=[exe_path, *urls], check=False) cmd=[exe_path, *urls], check=False)
def is_installed(browser_name):
"""Checks if browser is installed based on exe_path, returns bool."""
browser_name = browser_name.replace(' Chromium', '')
return bool(browser_data.get(browser_name, {}).get('exe_path', False))
def list_homepages(indent=8, width=32): def list_homepages(indent=8, width=32):
"""List current homepages for reference.""" """List current homepages for reference."""
browser_list = [k for k, v in sorted(browser_data.items()) if v['exe_path']] browser_list = [k for k, v in sorted(browser_data.items()) if v['exe_path']]
@ -423,12 +419,6 @@ def list_homepages(indent=8, width=32):
indent=' '*indent, width=width, name=name, page=page)) indent=' '*indent, width=width, name=name, page=page))
def profile_present(browser_name):
"""Checks if a profile was detected for browser, returns bool."""
browser_name = browser_name.replace(' Chromium', '')
return bool(browser_data.get(browser_name, {}).get('profiles', False))
def reset_browsers(indent=8, width=32): def reset_browsers(indent=8, width=32):
"""Reset all detected browsers to safe defaults.""" """Reset all detected browsers to safe defaults."""
browser_list = [k for k, v in sorted(browser_data.items()) if v['profiles']] browser_list = [k for k, v in sorted(browser_data.items()) if v['profiles']]
@ -447,21 +437,14 @@ def reset_browsers(indent=8, width=32):
other_results=other_results, profile=profile) other_results=other_results, profile=profile)
def scan_for_browsers(just_firefox=False, silent=False): def scan_for_browsers(just_firefox=False):
"""Scan system for any supported browsers.""" """Scan system for any supported browsers."""
for name, details in sorted(SUPPORTED_BROWSERS.items()): for name, details in sorted(SUPPORTED_BROWSERS.items()):
if just_firefox and details['base'] != 'mozilla': if just_firefox and details['base'] != 'mozilla':
continue continue
if silent: try_and_print(message='{}...'.format(name),
try: function=get_browser_details, cs='Detected',
get_browser_details(name) other_results=other_results, name=name)
except Exception:
# Ignore errors in silent mode
pass
else:
try_and_print(message='{}...'.format(name),
function=get_browser_details, cs='Detected',
other_results=other_results, name=name)
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -1,9 +1,7 @@
'''Wizard Kit: Functions - Cleanup''' # Wizard Kit: Functions - Cleanup
# pylint: disable=no-name-in-module,wildcard-import
# vim: sts=2 sw=2 ts=2 from functions.common import *
from functions.setup import *
from settings.cleanup import *
def cleanup_adwcleaner(): def cleanup_adwcleaner():
"""Move AdwCleaner folders into the ClientDir.""" """Move AdwCleaner folders into the ClientDir."""
@ -77,7 +75,8 @@ def cleanup_desktop():
desktop_path = r'{USERPROFILE}\Desktop'.format(**global_vars['Env']) desktop_path = r'{USERPROFILE}\Desktop'.format(**global_vars['Env'])
for entry in os.scandir(desktop_path): for entry in os.scandir(desktop_path):
if DESKTOP_ITEMS.search(entry.name): # JRT, RKill, Shortcut cleaner
if re.search(r'^(JRT|RKill|sc-cleaner)', entry.name, re.IGNORECASE):
dest_name = r'{}\{}'.format(dest_folder, entry.name) dest_name = r'{}\{}'.format(dest_folder, entry.name)
dest_name = non_clobber_rename(dest_name) dest_name = non_clobber_rename(dest_name)
shutil.move(entry.path, dest_name) shutil.move(entry.path, dest_name)
@ -131,14 +130,7 @@ def delete_registry_value(hive, key, value):
winreg.DeleteValue(k, value) winreg.DeleteValue(k, value)
def restore_default_uac():
"""Restores default UAC settings via the registry."""
if global_vars['OS']['Version'] == '10':
write_registry_settings(UAC_DEFAULTS_WIN10, all_users=True)
else:
# Haven't checked Win8 settings, only applying minimum set
write_registry_settings(UAC_DEFAULTS_WIN7, all_users=True)
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.")
# vim: sts=2 sw=2 ts=2

View file

@ -64,15 +64,12 @@ class GenericRepair(Exception):
class MultipleInstallationsError(Exception): class MultipleInstallationsError(Exception):
pass pass
class NoProfilesError(Exception):
pass
class Not4KAlignedError(Exception):
pass
class NotInstalledError(Exception): class NotInstalledError(Exception):
pass pass
class NoProfilesError(Exception):
pass
class OSInstalledLegacyError(Exception): class OSInstalledLegacyError(Exception):
pass pass
@ -91,21 +88,14 @@ class SecureBootNotAvailError(Exception):
class SecureBootUnknownError(Exception): class SecureBootUnknownError(Exception):
pass pass
class WindowsOutdatedError(Exception):
pass
class WindowsUnsupportedError(Exception):
pass
# General functions # General functions
def abort(show_prompt=True): def abort():
"""Abort script.""" """Abort script."""
print_warning('Aborted.') print_warning('Aborted.')
if show_prompt: sleep(1)
sleep(1) pause(prompt='Press Enter to exit... ')
pause(prompt='Press Enter to exit... ') exit_script()
exit_script(1)
def ask(prompt='Kotaero!'): def ask(prompt='Kotaero!'):
@ -173,22 +163,18 @@ def clear_screen():
def convert_to_bytes(size): def convert_to_bytes(size):
"""Convert human-readable size str to bytes and return an int.""" """Convert human-readable size str to bytes and return an int."""
size = str(size) size = str(size)
tmp = re.search(r'(\d+\.?\d*)\s+([PTGMKB])B?', size.upper()) tmp = re.search(r'(\d+\.?\d*)\s+([KMGT]B)', size.upper())
if tmp: if tmp:
size = float(tmp.group(1)) size = float(tmp.group(1))
units = tmp.group(2) units = tmp.group(2)
if units == 'P': if units == 'TB':
size *= 1024 ** 5 size *= 1099511627776
if units == 'T': elif units == 'GB':
size *= 1024 ** 4 size *= 1073741824
elif units == 'G': elif units == 'MB':
size *= 1024 ** 3 size *= 1048576
elif units == 'M': elif units == 'KB':
size *= 1024 ** 2 size *= 1024
elif units == 'K':
size *= 1024 ** 1
elif units == 'B':
size *= 1024 ** 0
size = int(size) size = int(size)
else: else:
return -1 return -1
@ -307,24 +293,20 @@ def human_readable_size(size, decimals=0):
return '{size:>{width}} b'.format(size='???', width=width) return '{size:>{width}} b'.format(size='???', width=width)
# Convert to sensible units # Convert to sensible units
if size >= 1024 ** 5: if size >= 1099511627776:
size /= 1024 ** 5 size /= 1099511627776
units = 'PB' units = 'Tb'
elif size >= 1024 ** 4: elif size >= 1073741824:
size /= 1024 ** 4 size /= 1073741824
units = 'TB' units = 'Gb'
elif size >= 1024 ** 3: elif size >= 1048576:
size /= 1024 ** 3 size /= 1048576
units = 'GB' units = 'Mb'
elif size >= 1024 ** 2: elif size >= 1024:
size /= 1024 ** 2 size /= 1024
units = 'MB' units = 'Kb'
elif size >= 1024 ** 1:
size /= 1024 ** 1
units = 'KB'
else: else:
size /= 1024 ** 0 units = ' b'
units = ' B'
# Return # Return
return '{size:>{width}.{decimals}f} {units}'.format( return '{size:>{width}.{decimals}f} {units}'.format(
@ -439,8 +421,6 @@ def non_clobber_rename(full_path):
def pause(prompt='Press Enter to continue... '): def pause(prompt='Press Enter to continue... '):
"""Simple pause implementation.""" """Simple pause implementation."""
if prompt[-1] != ' ':
prompt += ' '
input(prompt) input(prompt)
@ -899,19 +879,25 @@ def make_tmp_dirs():
def set_common_vars(): def set_common_vars():
"""Set common variables.""" """Set common variables."""
global_vars['Date'] = time.strftime("%Y-%m-%d") global_vars['Date'] = time.strftime("%Y-%m-%d")
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z") global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
global_vars['Env'] = os.environ.copy() global_vars['Env'] = os.environ.copy()
global_vars['ArchivePassword'] = ARCHIVE_PASSWORD global_vars['ArchivePassword'] = ARCHIVE_PASSWORD
global_vars['BinDir'] = r'{BaseDir}\.bin'.format(**global_vars) global_vars['BinDir'] = r'{BaseDir}\.bin'.format(
global_vars['CBinDir'] = r'{BaseDir}\.cbin'.format(**global_vars) **global_vars)
global_vars['ClientDir'] = r'{SYSTEMDRIVE}\{prefix}'.format( global_vars['CBinDir'] = r'{BaseDir}\.cbin'.format(
prefix=KIT_NAME_SHORT, **global_vars['Env']) **global_vars)
global_vars['BackupDir'] = r'{ClientDir}\Backups'.format(**global_vars) global_vars['ClientDir'] = r'{SYSTEMDRIVE}\{prefix}'.format(
global_vars['LogDir'] = r'{ClientDir}\Logs\{Date}'.format(**global_vars) prefix=KIT_NAME_SHORT, **global_vars['Env'])
global_vars['QuarantineDir'] = r'{ClientDir}\Quarantine'.format(**global_vars) global_vars['BackupDir'] = r'{ClientDir}\Backups'.format(
global_vars['TmpDir'] = r'{BinDir}\tmp'.format(**global_vars) **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(): def set_linux_vars():
@ -919,12 +905,12 @@ def set_linux_vars():
These assume we're running under a WK-Linux build.""" These assume we're running under a WK-Linux build."""
result = run_program(['mktemp', '-d']) result = run_program(['mktemp', '-d'])
global_vars['TmpDir'] = result.stdout.decode().strip() global_vars['TmpDir'] = result.stdout.decode().strip()
global_vars['Date'] = time.strftime("%Y-%m-%d") global_vars['Date'] = time.strftime("%Y-%m-%d")
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z") global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
global_vars['Env'] = os.environ.copy() global_vars['Env'] = os.environ.copy()
global_vars['BinDir'] = '/usr/local/bin' global_vars['BinDir'] = '/usr/local/bin'
global_vars['LogDir'] = '{}/Logs'.format(global_vars['Env']['HOME']) global_vars['LogDir'] = global_vars['TmpDir']
global_vars['Tools'] = { global_vars['Tools'] = {
'wimlib-imagex': 'wimlib-imagex', 'wimlib-imagex': 'wimlib-imagex',
'SevenZip': '7z', 'SevenZip': '7z',
@ -933,13 +919,10 @@ def set_linux_vars():
def set_log_file(log_name): def set_log_file(log_name):
"""Sets global var LogFile and creates path as needed.""" """Sets global var LogFile and creates path as needed."""
if psutil.LINUX: folder_path = '{}{}{}'.format(
folder_path = global_vars['LogDir'] global_vars['LogDir'],
else: os.sep,
folder_path = '{}{}{}'.format( KIT_NAME_FULL)
global_vars['LogDir'],
os.sep,
KIT_NAME_FULL)
log_file = '{}{}{}'.format( log_file = '{}{}{}'.format(
folder_path, folder_path,
os.sep, os.sep,

View file

@ -111,7 +111,7 @@ def find_core_storage_volumes(device_path=None):
# Check log for found volumes # Check log for found volumes
cs_vols = {} cs_vols = {}
with open(log_path, 'r', encoding='utf-8', errors='ignore') as f: with open(log_path, 'r') as f:
for line in f.readlines(): for line in f.readlines():
r = re.match( r = re.match(
r'^.*echo "([^"]+)" . dmsetup create test(\d)$', r'^.*echo "([^"]+)" . dmsetup create test(\d)$',
@ -151,16 +151,12 @@ def is_valid_wim_file(item):
def get_mounted_volumes(): def get_mounted_volumes():
"""Get mounted volumes, returns dict.""" """Get mounted volumes, returns dict."""
cmd = [ cmd = [
'findmnt', 'findmnt', '-J', '-b', '-i',
'--list', '-t', (
'--json',
'--bytes',
'--invert',
'--types', (
'autofs,binfmt_misc,bpf,cgroup,cgroup2,configfs,debugfs,devpts,' 'autofs,binfmt_misc,bpf,cgroup,cgroup2,configfs,debugfs,devpts,'
'devtmpfs,hugetlbfs,mqueue,proc,pstore,securityfs,sysfs,tmpfs' 'devtmpfs,hugetlbfs,mqueue,proc,pstore,securityfs,sysfs,tmpfs'
), ),
'--output', 'SOURCE,TARGET,FSTYPE,LABEL,SIZE,AVAIL,USED'] '-o', 'SOURCE,TARGET,FSTYPE,LABEL,SIZE,AVAIL,USED']
json_data = get_json_from_command(cmd) json_data = get_json_from_command(cmd)
mounted_volumes = [] mounted_volumes = []
for item in json_data.get('filesystems', []): for item in json_data.get('filesystems', []):
@ -199,8 +195,6 @@ def mount_volumes(
volumes.update({child['name']: child}) volumes.update({child['name']: child})
for grandchild in child.get('children', []): for grandchild in child.get('children', []):
volumes.update({grandchild['name']: grandchild}) volumes.update({grandchild['name']: grandchild})
for great_grandchild in grandchild.get('children', []):
volumes.update({great_grandchild['name']: great_grandchild})
# Get list of mounted volumes # Get list of mounted volumes
mounted_volumes = get_mounted_volumes() mounted_volumes = get_mounted_volumes()
@ -218,7 +212,7 @@ def mount_volumes(
report[vol_path] = vol_data report[vol_path] = vol_data
elif 'children' in vol_data: elif 'children' in vol_data:
# Skip LVM/RAID partitions (the real volume is mounted separately) # Skip LVM/RAID partitions (the real volume is mounted separately)
vol_data['show_data']['data'] = vol_data.get('fstype', 'Unknown') vol_data['show_data']['data'] = vol_data.get('fstype', 'UNKNOWN')
if vol_data.get('label', None): if vol_data.get('label', None):
vol_data['show_data']['data'] += ' "{}"'.format(vol_data['label']) vol_data['show_data']['data'] += ' "{}"'.format(vol_data['label'])
vol_data['show_data']['info'] = True vol_data['show_data']['info'] = True
@ -243,23 +237,17 @@ def mount_volumes(
if vol_data['show_data']['data'] == 'Failed to mount': if vol_data['show_data']['data'] == 'Failed to mount':
vol_data['mount_point'] = None vol_data['mount_point'] = None
else: else:
fstype = vol_data.get('fstype', 'Unknown FS')
size_used = human_readable_size( size_used = human_readable_size(
mounted_volumes[vol_path]['used'], mounted_volumes[vol_path]['used'])
decimals=1,
)
size_avail = human_readable_size( size_avail = human_readable_size(
mounted_volumes[vol_path]['avail'], mounted_volumes[vol_path]['avail'])
decimals=1,
)
vol_data['size_avail'] = size_avail vol_data['size_avail'] = size_avail
vol_data['size_used'] = size_used vol_data['size_used'] = size_used
vol_data['mount_point'] = mounted_volumes[vol_path]['target'] vol_data['mount_point'] = mounted_volumes[vol_path]['target']
vol_data['show_data']['data'] = 'Mounted on {}'.format( vol_data['show_data']['data'] = 'Mounted on {}'.format(
mounted_volumes[vol_path]['target']) mounted_volumes[vol_path]['target'])
vol_data['show_data']['data'] = '{:40} ({}, {} used, {} free)'.format( vol_data['show_data']['data'] = '{:40} ({} used, {} free)'.format(
vol_data['show_data']['data'], vol_data['show_data']['data'],
fstype,
size_used, size_used,
size_avail) size_avail)
@ -293,14 +281,6 @@ def mount_backup_shares(read_write=False):
def mount_network_share(server, read_write=False): def mount_network_share(server, read_write=False):
"""Mount a network share defined by server.""" """Mount a network share defined by server."""
uid = '1000'
# Get UID
cmd = ['id', '--user', 'tech']
result = run_program(cmd, check=False, encoding='utf-8', errors='ignore')
if result.stdout.strip().isnumeric():
uid = result.stdout.strip()
if read_write: if read_write:
username = server['RW-User'] username = server['RW-User']
password = server['RW-Pass'] password = server['RW-Pass']
@ -316,35 +296,18 @@ def mount_network_share(server, read_write=False):
error = r'Failed to mount \\{Name}\{Share} ({IP})'.format(**server) error = r'Failed to mount \\{Name}\{Share} ({IP})'.format(**server)
success = 'Mounted {Name}'.format(**server) success = 'Mounted {Name}'.format(**server)
elif psutil.LINUX: elif psutil.LINUX:
# Make mountpoint
cmd = [ cmd = [
'sudo', 'mkdir', '-p', 'sudo', 'mkdir', '-p',
'/Backups/{Name}'.format(**server)] '/Backups/{Name}'.format(**server)]
run_program(cmd) run_program(cmd)
# Set mount options
cmd_options = [
# Assuming GID matches UID
'gid={}'.format(uid),
'uid={}'.format(uid),
]
cmd_options.append('rw' if read_write else 'ro')
cmd_options.append('username={}'.format(username))
if password:
cmd_options.append('password={}'.format(password))
else:
# Skip password check
cmd_options.append('guest')
# Set mount command
cmd = [ cmd = [
'sudo', 'mount', 'sudo', 'mount',
'//{IP}/{Share}'.format(**server).replace('\\', '/'), '//{IP}/{Share}'.format(**server),
'/Backups/{Name}'.format(**server), '/Backups/{Name}'.format(**server),
'-o', ','.join(cmd_options), '-o', '{}username={},password={}'.format(
] '' if read_write else 'ro,',
username,
# Set result messages password)]
warning = 'Failed to mount /Backups/{Name}, {IP} unreachable.'.format( warning = 'Failed to mount /Backups/{Name}, {IP} unreachable.'.format(
**server) **server)
error = 'Failed to mount /Backups/{Name}'.format(**server) error = 'Failed to mount /Backups/{Name}'.format(**server)

View file

@ -1,25 +1,25 @@
# pylint: disable=no-name-in-module,too-many-lines,wildcard-import # Wizard Kit: Functions - ddrescue-tui
# vim: sts=2 sw=2 ts=2
'''Wizard Kit: Functions - ddrescue-tui'''
import datetime import datetime
import pathlib import pathlib
import psutil
import pytz
import re import re
import signal
import stat import stat
import time import time
from operator import itemgetter
import pytz from collections import OrderedDict
from functions.data import * from functions.data import *
from functions.hw_diags import * from functions.hw_diags import *
from functions.json import * from functions.json import *
from functions.tmux import * from functions.tmux import *
from operator import itemgetter
from settings.ddrescue import * from settings.ddrescue import *
# Clases # Clases
class BaseObj(): class BaseObj():
# pylint: disable=missing-docstring
"""Base object used by DevObj, DirObj, and ImageObj.""" """Base object used by DevObj, DirObj, and ImageObj."""
def __init__(self, path): def __init__(self, path):
self.type = 'base' self.type = 'base'
@ -44,7 +44,6 @@ class BaseObj():
class BlockPair(): class BlockPair():
# pylint: disable=too-many-instance-attributes
"""Object to track data and methods together for source and dest.""" """Object to track data and methods together for source and dest."""
def __init__(self, mode, source, dest): def __init__(self, mode, source, dest):
self.mode = mode self.mode = mode
@ -61,10 +60,9 @@ class BlockPair():
if self.mode == 'clone': if self.mode == 'clone':
# Cloning # Cloning
self.dest_path = dest.path self.dest_path = dest.path
self.map_path = '{cwd}/Clone_{prefix}.map'.format( self.map_path = '{pwd}/Clone_{prefix}.map'.format(
cwd=os.path.realpath(os.getcwd()), pwd=os.path.realpath(global_vars['Env']['PWD']),
prefix=source.prefix, prefix=source.prefix)
)
else: else:
# Imaging # Imaging
self.dest_path = '{path}/{prefix}.dd'.format( self.dest_path = '{path}/{prefix}.dd'.format(
@ -107,19 +105,19 @@ class BlockPair():
def load_map_data(self): def load_map_data(self):
"""Load data from map file and set progress.""" """Load data from map file and set progress."""
map_data = read_map_file(self.map_path) map_data = read_map_file(self.map_path)
self.rescued = map_data.get('rescued', 0) self.rescued_percent = map_data['rescued']
self.rescued_percent = (self.rescued / self.size) * 100 self.rescued = (self.rescued_percent * self.size) / 100
if map_data['full recovery']: if map_data['full recovery']:
self.pass_done = [True, True, True] self.pass_done = [True, True, True]
self.rescued = self.size self.rescued = self.size
self.status = ['Skipped', 'Skipped', 'Skipped'] self.status = ['Skipped', 'Skipped', 'Skipped']
elif map_data.get('non-tried', 0) > 0: elif map_data['non-tried'] > 0:
# Initial pass incomplete # Initial pass incomplete
pass pass
elif map_data.get('non-trimmed', 0) > 0: elif map_data['non-trimmed'] > 0:
self.pass_done = [True, False, False] self.pass_done = [True, False, False]
self.status = ['Skipped', 'Pending', 'Pending'] self.status = ['Skipped', 'Pending', 'Pending']
elif map_data.get('non-scraped', 0) > 0: elif map_data['non-scraped'] > 0:
self.pass_done = [True, True, False] self.pass_done = [True, True, False]
self.status = ['Skipped', 'Skipped', 'Pending'] self.status = ['Skipped', 'Skipped', 'Pending']
else: else:
@ -147,15 +145,14 @@ class BlockPair():
"""Update progress using map file.""" """Update progress using map file."""
if os.path.exists(self.map_path): if os.path.exists(self.map_path):
map_data = read_map_file(self.map_path) map_data = read_map_file(self.map_path)
self.rescued = map_data.get('rescued', 0) self.rescued_percent = map_data.get('rescued', 0)
self.rescued_percent = (self.rescued / self.size) * 100 self.rescued = (self.rescued_percent * self.size) / 100
self.status[pass_num] = get_formatted_status( self.status[pass_num] = get_formatted_status(
label='Pass {}'.format(pass_num+1), label='Pass {}'.format(pass_num+1),
data=(self.rescued/self.size)*100) data=(self.rescued/self.size)*100)
class DevObj(BaseObj): class DevObj(BaseObj):
# pylint: disable=too-many-instance-attributes
"""Block device object.""" """Block device object."""
def self_check(self): def self_check(self):
"""Verify that self.path points to a block device.""" """Verify that self.path points to a block device."""
@ -189,7 +186,6 @@ class DevObj(BaseObj):
self.update_filename_prefix() self.update_filename_prefix()
def update_filename_prefix(self): def update_filename_prefix(self):
# pylint: disable=attribute-defined-outside-init
"""Set filename prefix based on details.""" """Set filename prefix based on details."""
self.prefix = '{m_size}_{model}'.format( self.prefix = '{m_size}_{model}'.format(
m_size=self.model_size, m_size=self.model_size,
@ -209,7 +205,6 @@ class DevObj(BaseObj):
class DirObj(BaseObj): class DirObj(BaseObj):
"""Directory object."""
def self_check(self): def self_check(self):
"""Verify that self.path points to a directory.""" """Verify that self.path points to a directory."""
if not pathlib.Path(self.path).is_dir(): if not pathlib.Path(self.path).is_dir():
@ -227,7 +222,6 @@ class DirObj(BaseObj):
class ImageObj(BaseObj): class ImageObj(BaseObj):
"""Image file object."""
def self_check(self): def self_check(self):
"""Verify that self.path points to a file.""" """Verify that self.path points to a file."""
if not pathlib.Path(self.path).is_file(): if not pathlib.Path(self.path).is_file():
@ -249,11 +243,10 @@ class ImageObj(BaseObj):
self.report = get_device_report(self.loop_dev) self.report = get_device_report(self.loop_dev)
self.report = self.report.replace( self.report = self.report.replace(
self.loop_dev[self.loop_dev.rfind('/')+1:], '(Img)') self.loop_dev[self.loop_dev.rfind('/')+1:], '(Img)')
run_program(['sudo', 'losetup', '--detach', self.loop_dev], check=False) run_program(['losetup', '--detach', self.loop_dev], check=False)
class RecoveryState(): class RecoveryState():
# pylint: disable=too-many-instance-attributes
"""Object to track BlockPair objects and overall state.""" """Object to track BlockPair objects and overall state."""
def __init__(self, mode, source, dest): def __init__(self, mode, source, dest):
self.mode = mode.lower() self.mode = mode.lower()
@ -277,8 +270,6 @@ class RecoveryState():
if mode not in ('clone', 'image'): if mode not in ('clone', 'image'):
raise GenericError('Unsupported mode') raise GenericError('Unsupported mode')
self.get_smart_source() self.get_smart_source()
self.set_working_dir()
os.makedirs(global_vars['LogDir'], exist_ok=True)
def add_block_pair(self, source, dest): def add_block_pair(self, source, dest):
"""Run safety checks and append new BlockPair to internal list.""" """Run safety checks and append new BlockPair to internal list."""
@ -323,134 +314,20 @@ class RecoveryState():
# Safety checks passed # Safety checks passed
self.block_pairs.append(BlockPair(self.mode, source, dest)) self.block_pairs.append(BlockPair(self.mode, source, dest))
def build_outer_panes(self):
"""Build top and side panes."""
clear_screen()
# Top
self.panes['Source'] = tmux_split_window(
behind=True, vertical=True, lines=2,
text='{BLUE}Source{CLEAR}'.format(**COLORS))
# Started
self.panes['Started'] = tmux_split_window(
lines=SIDE_PANE_WIDTH, target_pane=self.panes['Source'],
text='{BLUE}Started{CLEAR}\n{s}'.format(
s=time.strftime("%Y-%m-%d %H:%M %Z"),
**COLORS))
# Destination
self.panes['Destination'] = tmux_split_window(
percent=50, target_pane=self.panes['Source'],
text='{BLUE}Destination{CLEAR}'.format(**COLORS))
# Progress
update_sidepane(self)
self.panes['Progress'] = tmux_split_window(
lines=SIDE_PANE_WIDTH, watch=self.progress_out)
def current_pass_done(self): def current_pass_done(self):
"""Checks if pass is done for all block-pairs, returns bool.""" """Checks if pass is done for all block-pairs, returns bool."""
done = True done = True
for b_pair in self.block_pairs: for bp in self.block_pairs:
done = done and b_pair.pass_done[self.current_pass] done &= bp.pass_done[self.current_pass]
return done return done
def current_pass_min(self): def current_pass_min(self):
"""Gets minimum pass rescued percentage, returns float.""" """Gets minimum pass rescued percentage, returns float."""
min_percent = 100 min_percent = 100
for b_pair in self.block_pairs: for bp in self.block_pairs:
min_percent = min(min_percent, b_pair.rescued_percent) min_percent = min(min_percent, bp.rescued_percent)
return min_percent return min_percent
def fix_tmux_panes(self, forced=False):
# pylint: disable=too-many-branches,too-many-locals
"""Fix pane sizes if the winodw has been resized."""
needs_fixed = False
# Check layout
for pane, pane_data in TMUX_LAYOUT.items():
if not pane_data.get('Check'):
# Not concerned with the size of this pane
continue
# Get target
target = None
if pane != 'Current':
if pane not in self.panes:
# Skip missing panes
continue
else:
target = self.panes[pane]
# Check pane size
size_x, size_y = tmux_get_pane_size(pane_id=target)
if pane_data.get('x', False) and pane_data['x'] != size_x:
needs_fixed = True
if pane_data.get('y', False) and pane_data['y'] != size_y:
needs_fixed = True
# Bail?
if not needs_fixed and not forced:
return
# Remove Destination pane (temporarily)
tmux_kill_pane(self.panes['Destination'])
# Update layout
for pane, pane_data in TMUX_LAYOUT.items():
# Get target
target = None
if pane != 'Current':
if pane not in self.panes:
# Skip missing panes
continue
else:
target = self.panes[pane]
# Resize pane
tmux_resize_pane(pane_id=target, **pane_data)
# Calc Source/Destination pane sizes
width, height = tmux_get_pane_size()
width = int(width / 2) - 1
# Update Source string
source_str = self.source.name
if len(source_str) > width:
source_str = '{}...'.format(source_str[:width-3])
# Update Destination string
dest_str = self.dest.name
if len(dest_str) > width:
if self.mode == 'clone':
dest_str = '{}...'.format(dest_str[:width-3])
else:
dest_str = '...{}'.format(dest_str[-width+3:])
# Rebuild Source/Destination panes
tmux_update_pane(
pane_id=self.panes['Source'],
text='{BLUE}Source{CLEAR}\n{s}'.format(
s=source_str, **COLORS))
self.panes['Destination'] = tmux_split_window(
percent=50, target_pane=self.panes['Source'],
text='{BLUE}Destination{CLEAR}\n{s}'.format(
s=dest_str, **COLORS))
if 'SMART' in self.panes:
# Calc SMART/ddrescue/Journal panes sizes
ratio = [12, 22, 4]
width, height = tmux_get_pane_size(pane_id=self.panes['Progress'])
height -= 2
total = sum(ratio)
p_ratio = [int((x/total) * height) for x in ratio]
p_ratio[1] = height - p_ratio[0] - p_ratio[2]
# Resize SMART/Journal panes
tmux_resize_pane(self.panes['SMART'], y=ratio[0])
tmux_resize_pane(y=ratio[1])
tmux_resize_pane(self.panes['Journal'], y=ratio[2])
def get_smart_source(self): def get_smart_source(self):
"""Get source for SMART dispay.""" """Get source for SMART dispay."""
disk_path = self.source.path disk_path = self.source.path
@ -462,15 +339,18 @@ class RecoveryState():
def retry_all_passes(self): def retry_all_passes(self):
"""Mark all passes as pending for all block-pairs.""" """Mark all passes as pending for all block-pairs."""
self.finished = False self.finished = False
for b_pair in self.block_pairs: for bp in self.block_pairs:
b_pair.pass_done = [False, False, False] bp.pass_done = [False, False, False]
b_pair.status = ['Pending', 'Pending', 'Pending'] bp.status = ['Pending', 'Pending', 'Pending']
b_pair.fix_status_strings() bp.fix_status_strings()
self.set_pass_num() self.set_pass_num()
def self_checks(self): def self_checks(self):
"""Run self-checks and update state values.""" """Run self-checks and update state values."""
cmd = ['findmnt', '--json', '--target', os.getcwd()] cmd = ['findmnt', '--json', '--target', os.getcwd()]
map_allowed_fstypes = RECOMMENDED_FSTYPES.copy()
map_allowed_fstypes.extend(['cifs', 'ext2', 'vfat'])
map_allowed_fstypes.sort()
json_data = get_json_from_command(cmd) json_data = get_json_from_command(cmd)
# Abort if json_data is empty # Abort if json_data is empty
@ -481,24 +361,23 @@ class RecoveryState():
# Avoid saving map to non-persistent filesystem # Avoid saving map to non-persistent filesystem
fstype = json_data.get( fstype = json_data.get(
'filesystems', [{}])[0].get( 'filesystems', [{}])[0].get(
'fstype', 'unknown') 'fstype', 'unknown')
if fstype not in RECOMMENDED_MAP_FSTYPES: if fstype not in map_allowed_fstypes:
print_error( print_error(
"Map isn't being saved to a recommended filesystem ({})".format( "Map isn't being saved to a recommended filesystem ({})".format(
fstype.upper())) fstype.upper()))
print_info('Recommended types are: {}'.format( print_info('Recommended types are: {}'.format(
' / '.join(RECOMMENDED_MAP_FSTYPES).upper())) ' / '.join(map_allowed_fstypes).upper()))
print_standard(' ') print_standard(' ')
if not ask('Proceed anyways? (Strongly discouraged)'): if not ask('Proceed anyways? (Strongly discouraged)'):
raise GenericAbort() raise GenericAbort()
# Run BlockPair self checks and get total size # Run BlockPair self checks and get total size
self.total_size = 0 self.total_size = 0
for b_pair in self.block_pairs: for bp in self.block_pairs:
b_pair.self_check() bp.self_check()
if b_pair.resumed: self.resumed |= bp.resumed
self.resumed = True self.total_size += bp.size
self.total_size += b_pair.size
def set_pass_num(self): def set_pass_num(self):
"""Set current pass based on all block-pair's progress.""" """Set current pass based on all block-pair's progress."""
@ -506,8 +385,8 @@ class RecoveryState():
for pass_num in (2, 1, 0): for pass_num in (2, 1, 0):
# Iterate backwards through passes # Iterate backwards through passes
pass_done = True pass_done = True
for b_pair in self.block_pairs: for bp in self.block_pairs:
pass_done = pass_done and b_pair.pass_done[pass_num] pass_done &= bp.pass_done[pass_num]
if pass_done: if pass_done:
# All block-pairs reported being done # All block-pairs reported being done
# Set to next pass, unless we're on the last pass (2) # Set to next pass, unless we're on the last pass (2)
@ -525,34 +404,6 @@ class RecoveryState():
elif self.current_pass == 2: elif self.current_pass == 2:
self.current_pass_str = '3 "Scraping bad areas"' self.current_pass_str = '3 "Scraping bad areas"'
def set_working_dir(self):
# pylint: disable=no-self-use
"""Set working dir to MAP_DIR if possible.
NOTE: This is to help ensure the map file
is saved to non-volatile storage."""
map_dir = '{}/{}'.format(MAP_DIR, global_vars['Date-Time'])
# Mount backup shares
mount_backup_shares(read_write=True)
# Get MAP_DIR filesystem type
# NOTE: If the backup share fails to mount then this will
# likely be the type of /
cmd = [
'findmnt',
'--noheadings',
'--target', MAP_DIR,
'--output', 'FSTYPE',
]
result = run_program(cmd, check=False, encoding='utf-8', errors='ingnore')
map_dir_type = result.stdout.strip().lower()
# Change working dir if map_dir_type is acceptable
if map_dir_type in RECOMMENDED_MAP_FSTYPES:
os.makedirs(map_dir, exist_ok=True)
os.chdir(map_dir)
def update_etoc(self): def update_etoc(self):
"""Search ddrescue output for the current EToC, returns str.""" """Search ddrescue output for the current EToC, returns str."""
now = datetime.datetime.now(tz=self.timezone) now = datetime.datetime.now(tz=self.timezone)
@ -562,7 +413,7 @@ class RecoveryState():
# Just set to N/A (NOTE: this overrules the refresh rate below) # Just set to N/A (NOTE: this overrules the refresh rate below)
self.etoc = 'N/A' self.etoc = 'N/A'
return return
if 'In Progress' not in self.status: elif 'In Progress' not in self.status:
# Don't update when EToC is hidden # Don't update when EToC is hidden
return return
if now.second % ETOC_REFRESH_RATE != 0: if now.second % ETOC_REFRESH_RATE != 0:
@ -576,14 +427,13 @@ class RecoveryState():
# Capture main tmux pane # Capture main tmux pane
try: try:
text = tmux_capture_pane() text = tmux_capture_pane()
except Exception: # pylint: disable=broad-except except Exception:
# Ignore # Ignore
pass pass
# Search for EToC delta # Search for EToC delta
matches = re.findall(r'remaining time:.*$', text, re.MULTILINE) matches = re.findall(r'remaining time:.*$', text, re.MULTILINE)
if matches: if matches:
# pylint: disable=invalid-name
r = REGEX_REMAINING_TIME.search(matches[-1]) r = REGEX_REMAINING_TIME.search(matches[-1])
if r.group('na'): if r.group('na'):
self.etoc = 'N/A' self.etoc = 'N/A'
@ -600,7 +450,7 @@ class RecoveryState():
minutes=int(minutes), minutes=int(minutes),
seconds=int(seconds), seconds=int(seconds),
) )
except Exception: # pylint: disable=broad-except except Exception:
# Ignore and leave as raw string # Ignore and leave as raw string
pass pass
@ -610,16 +460,15 @@ class RecoveryState():
now = datetime.datetime.now(tz=self.timezone) now = datetime.datetime.now(tz=self.timezone)
_etoc = now + etoc_delta _etoc = now + etoc_delta
self.etoc = _etoc.strftime('%Y-%m-%d %H:%M %Z') self.etoc = _etoc.strftime('%Y-%m-%d %H:%M %Z')
except Exception: # pylint: disable=broad-except except Exception:
# Ignore and leave as current string # Ignore and leave as current string
pass pass
def update_progress(self): def update_progress(self):
# pylint: disable=attribute-defined-outside-init
"""Update overall progress using block_pairs.""" """Update overall progress using block_pairs."""
self.rescued = 0 self.rescued = 0
for b_pair in self.block_pairs: for bp in self.block_pairs:
self.rescued += b_pair.rescued self.rescued += bp.rescued
self.rescued_percent = (self.rescued / self.total_size) * 100 self.rescued_percent = (self.rescued / self.total_size) * 100
self.status_percent = get_formatted_status( self.status_percent = get_formatted_status(
label='Recovered:', data=self.rescued_percent) label='Recovered:', data=self.rescued_percent)
@ -628,6 +477,26 @@ class RecoveryState():
# Functions # Functions
def build_outer_panes(state):
"""Build top and side panes."""
state.panes['Source'] = tmux_split_window(
behind=True, vertical=True, lines=2,
text='{BLUE}Source{CLEAR}'.format(**COLORS))
state.panes['Started'] = tmux_split_window(
lines=SIDE_PANE_WIDTH, target_pane=state.panes['Source'],
text='{BLUE}Started{CLEAR}\n{s}'.format(
s=time.strftime("%Y-%m-%d %H:%M %Z"),
**COLORS))
state.panes['Destination'] = tmux_split_window(
percent=50, target_pane=state.panes['Source'],
text='{BLUE}Destination{CLEAR}'.format(**COLORS))
# Side pane
update_sidepane(state)
state.panes['Progress'] = tmux_split_window(
lines=SIDE_PANE_WIDTH, watch=state.progress_out)
def create_path_obj(path): def create_path_obj(path):
"""Create Dev, Dir, or Image obj based on path given.""" """Create Dev, Dir, or Image obj based on path given."""
obj = None obj = None
@ -645,16 +514,101 @@ def create_path_obj(path):
def double_confirm_clone(): def double_confirm_clone():
"""Display warning and get 2nd confirmation, returns bool.""" """Display warning and get 2nd confirmation, returns bool."""
print_standard('\nSAFETY CHECK') print_standard('\nSAFETY CHECK')
print_warning( print_warning('All data will be DELETED from the '
'All data will be DELETED from the ' 'destination device and partition(s) listed above.')
'destination device and partition(s) listed above.' print_warning('This is irreversible and will lead '
) 'to {CLEAR}{RED}DATA LOSS.'.format(**COLORS))
print_warning(
'This is irreversible and will lead to {CLEAR}{RED}DATA LOSS.'.format(
**COLORS))
return ask('Asking again to confirm, is this correct?') return ask('Asking again to confirm, is this correct?')
def fix_tmux_panes(state, forced=False):
"""Fix pane sizes if the winodw has been resized."""
needs_fixed = False
# Check layout
for k, v in TMUX_LAYOUT.items():
if not v.get('Check'):
# Not concerned with the size of this pane
continue
# Get target
target = None
if k != 'Current':
if k not in state.panes:
# Skip missing panes
continue
else:
target = state.panes[k]
# Check pane size
x, y = tmux_get_pane_size(pane_id=target)
if v.get('x', False) and v['x'] != x:
needs_fixed = True
if v.get('y', False) and v['y'] != y:
needs_fixed = True
# Bail?
if not needs_fixed and not forced:
return
# Remove Destination pane (temporarily)
tmux_kill_pane(state.panes['Destination'])
# Update layout
for k, v in TMUX_LAYOUT.items():
# Get target
target = None
if k != 'Current':
if k not in state.panes:
# Skip missing panes
continue
else:
target = state.panes[k]
# Resize pane
tmux_resize_pane(pane_id=target, **v)
# Calc Source/Destination pane sizes
width, height = tmux_get_pane_size()
width = int(width / 2) - 1
# Update Source string
source_str = state.source.name
if len(source_str) > width:
source_str = '{}...'.format(source_str[:width-3])
# Update Destination string
dest_str = state.dest.name
if len(dest_str) > width:
if state.mode == 'clone':
dest_str = '{}...'.format(dest_str[:width-3])
else:
dest_str = '...{}'.format(dest_str[-width+3:])
# Rebuild Source/Destination panes
tmux_update_pane(
pane_id=state.panes['Source'],
text='{BLUE}Source{CLEAR}\n{s}'.format(
s=source_str, **COLORS))
state.panes['Destination'] = tmux_split_window(
percent=50, target_pane=state.panes['Source'],
text='{BLUE}Destination{CLEAR}\n{s}'.format(
s=dest_str, **COLORS))
if 'SMART' in state.panes:
# Calc SMART/ddrescue/Journal panes sizes
ratio = [12, 22, 4]
width, height = tmux_get_pane_size(pane_id=state.panes['Progress'])
height -= 2
total = sum(ratio)
p_ratio = [int((x/total) * height) for x in ratio]
p_ratio[1] = height - p_ratio[0] - p_ratio[2]
# Resize SMART/Journal panes
tmux_resize_pane(state.panes['SMART'], y=ratio[0])
tmux_resize_pane(y=ratio[1])
tmux_resize_pane(state.panes['Journal'], y=ratio[2])
def get_device_details(dev_path): def get_device_details(dev_path):
"""Get device details via lsblk, returns JSON dict.""" """Get device details via lsblk, returns JSON dict."""
cmd = ['lsblk', '--json', '--output-all', '--paths', dev_path] cmd = ['lsblk', '--json', '--output-all', '--paths', dev_path]
@ -723,22 +677,22 @@ def get_dir_report(dir_path):
output.append('{BLUE}{label:<{width}}{line}{CLEAR}'.format( output.append('{BLUE}{label:<{width}}{line}{CLEAR}'.format(
label='PATH', label='PATH',
width=width, width=width,
line=line.replace('\n', ''), line=line.replace('\n',''),
**COLORS)) **COLORS))
else: else:
output.append('{path:<{width}}{line}'.format( output.append('{path:<{width}}{line}'.format(
path=dir_path, path=dir_path,
width=width, width=width,
line=line.replace('\n', ''))) line=line.replace('\n','')))
# Done # Done
return '\n'.join(output) return '\n'.join(output)
def get_size_in_bytes(size): def get_size_in_bytes(s):
"""Convert size string from lsblk string to bytes, returns int.""" """Convert size string from lsblk string to bytes, returns int."""
size = re.sub(r'(\d+\.?\d*)\s*([KMGTB])B?', r'\1 \2B', size, re.IGNORECASE) s = re.sub(r'(\d+\.?\d*)\s*([KMGTB])B?', r'\1 \2B', s, re.IGNORECASE)
return convert_to_bytes(size) return convert_to_bytes(s)
def get_formatted_status(label, data): def get_formatted_status(label, data):
@ -746,15 +700,13 @@ def get_formatted_status(label, data):
data_width = SIDE_PANE_WIDTH - len(label) data_width = SIDE_PANE_WIDTH - len(label)
try: try:
data_str = '{data:>{data_width}.2f} %'.format( data_str = '{data:>{data_width}.2f} %'.format(
data=data, data=data,
data_width=data_width-2, data_width=data_width-2)
)
except ValueError: except ValueError:
# Assuming non-numeric data # Assuming non-numeric data
data_str = '{data:>{data_width}}'.format( data_str = '{data:>{data_width}}'.format(
data=data, data=data,
data_width=data_width, data_width=data_width)
)
status = '{label}{s_color}{data_str}{CLEAR}'.format( status = '{label}{s_color}{data_str}{CLEAR}'.format(
label=label, label=label,
s_color=get_status_color(data), s_color=get_status_color(data),
@ -763,19 +715,19 @@ def get_formatted_status(label, data):
return status return status
def get_status_color(status, t_success=99, t_warn=90): def get_status_color(s, t_success=99, t_warn=90):
"""Get color based on status, returns str.""" """Get color based on status, returns str."""
color = COLORS['CLEAR'] color = COLORS['CLEAR']
p_recovered = -1 p_recovered = -1
try: try:
p_recovered = float(status) p_recovered = float(s)
except ValueError: except ValueError:
# Status is either in lists below or will default to red # Status is either in lists below or will default to red
pass pass
if status == 'Pending' or str(status)[-2:] in (' b', 'Kb', 'Mb', 'Gb', 'Tb'): if s in ('Pending',) or str(s)[-2:] in (' b', 'Kb', 'Mb', 'Gb', 'Tb'):
color = COLORS['CLEAR'] color = COLORS['CLEAR']
elif status in ('Skipped', 'Unknown'): elif s in ('Skipped', 'Unknown'):
color = COLORS['YELLOW'] color = COLORS['YELLOW']
elif p_recovered >= t_success: elif p_recovered >= t_success:
color = COLORS['GREEN'] color = COLORS['GREEN']
@ -790,9 +742,9 @@ def is_writable_dir(dir_obj):
"""Check if we have read-write-execute permissions, returns bool.""" """Check if we have read-write-execute permissions, returns bool."""
is_ok = True is_ok = True
path_st_mode = os.stat(dir_obj.path).st_mode path_st_mode = os.stat(dir_obj.path).st_mode
is_ok = is_ok and path_st_mode & stat.S_IRUSR is_ok == is_ok and path_st_mode & stat.S_IRUSR
is_ok = is_ok and path_st_mode & stat.S_IWUSR is_ok == is_ok and path_st_mode & stat.S_IWUSR
is_ok = is_ok and path_st_mode & stat.S_IXUSR is_ok == is_ok and path_st_mode & stat.S_IXUSR
return is_ok return is_ok
@ -802,7 +754,6 @@ def is_writable_filesystem(dir_obj):
def menu_ddrescue(source_path, dest_path, run_mode): def menu_ddrescue(source_path, dest_path, run_mode):
# pylint: disable=too-many-branches
"""ddrescue menu.""" """ddrescue menu."""
source = None source = None
dest = None dest = None
@ -846,17 +797,17 @@ def menu_ddrescue(source_path, dest_path, run_mode):
raise GenericAbort() raise GenericAbort()
# Main menu # Main menu
state.build_outer_panes() clear_screen()
state.fix_tmux_panes(forced=True) build_outer_panes(state)
fix_tmux_panes(state, forced=True)
menu_main(state) menu_main(state)
# Done # Done
tmux_kill_all_panes() run_program(['tmux', 'kill-window'])
exit_script() exit_script()
def menu_main(state): def menu_main(state):
# pylint: disable=too-many-branches,too-many-statements
"""Main menu is used to set ddrescue settings.""" """Main menu is used to set ddrescue settings."""
checkmark = '*' checkmark = '*'
if 'DISPLAY' in global_vars['Env']: if 'DISPLAY' in global_vars['Env']:
@ -867,15 +818,16 @@ def menu_main(state):
# Build menu # Build menu
main_options = [ main_options = [
{'Base Name': 'Auto continue (if recovery % over threshold)', {'Base Name': 'Auto continue (if recovery % over threshold)',
'Enabled': True}, 'Enabled': True},
{'Base Name': 'Retry (mark non-rescued sectors "non-tried")', {'Base Name': 'Retry (mark non-rescued sectors "non-tried")',
'Enabled': False}, 'Enabled': False},
{'Base Name': 'Reverse direction', 'Enabled': False}, {'Base Name': 'Reverse direction', 'Enabled': False},
] ]
actions = [ actions = [
{'Name': 'Start', 'Letter': 'S'}, {'Name': 'Start', 'Letter': 'S'},
{'Name': 'Change settings {YELLOW}(experts only){CLEAR}'.format(**COLORS), {'Name': 'Change settings {YELLOW}(experts only){CLEAR}'.format(
'Letter': 'C'}, **COLORS),
'Letter': 'C'},
{'Name': 'Quit', 'Letter': 'Q', 'CRLF': True}, {'Name': 'Quit', 'Letter': 'Q', 'CRLF': True},
] ]
@ -906,13 +858,13 @@ def menu_main(state):
elif selection == 'S': elif selection == 'S':
# Set settings for pass # Set settings for pass
pass_settings = [] pass_settings = []
for option, option_data in state.settings.items(): for k, v in state.settings.items():
if not option_data['Enabled']: if not v['Enabled']:
continue continue
if 'Value' in option_data: if 'Value' in v:
pass_settings.append('{}={}'.format(option, option_data['Value'])) pass_settings.append('{}={}'.format(k, v['Value']))
else: else:
pass_settings.append(option) pass_settings.append(k)
for opt in main_options: for opt in main_options:
if 'Auto' in opt['Base Name']: if 'Auto' in opt['Base Name']:
auto_run = opt['Enabled'] auto_run = opt['Enabled']
@ -935,7 +887,7 @@ def menu_main(state):
state.current_pass_min() < AUTO_PASS_1_THRESHOLD): state.current_pass_min() < AUTO_PASS_1_THRESHOLD):
auto_run = False auto_run = False
elif (state.current_pass == 1 and elif (state.current_pass == 1 and
state.current_pass_min() < AUTO_PASS_2_THRESHOLD): state.current_pass_min() < AUTO_PASS_2_THRESHOLD):
auto_run = False auto_run = False
else: else:
auto_run = False auto_run = False
@ -964,15 +916,13 @@ def menu_settings(state):
# Build menu # Build menu
settings = [] settings = []
for option, option_data in sorted(state.settings.items()): for k, v in sorted(state.settings.items()):
if not option_data.get('Hidden', False): if not v.get('Hidden', False):
settings.append({'Base Name': option, 'Flag': option}) settings.append({'Base Name': k, 'Flag': k})
actions = [{'Name': 'Main Menu', 'Letter': 'M'}] actions = [{'Name': 'Main Menu', 'Letter': 'M'}]
# Show menu # Show menu
while True: while True:
# pylint: disable=invalid-name
# TODO: Clean up and/or replace with new menu-select function
for s in settings: for s in settings:
s['Name'] = '{}{}{}'.format( s['Name'] = '{}{}{}'.format(
s['Base Name'], s['Base Name'],
@ -1009,27 +959,25 @@ def menu_settings(state):
def read_map_file(map_path): def read_map_file(map_path):
"""Read map file with ddrescuelog and return data as dict.""" """Read map file with ddrescuelog and return data as dict."""
cmd = [ map_data = {'full recovery': False}
'ddrescuelog',
'--binary-prefixes',
'--show-status',
map_path,
]
map_data = {'full recovery': False, 'pass completed': False}
try: try:
result = run_program(cmd, encoding='utf-8', errors='ignore') result = run_program(['ddrescuelog', '-t', map_path])
except CalledProcessError: except CalledProcessError:
# (Grossly) assuming map_data hasn't been saved yet, return empty dict # (Grossly) assuming map_data hasn't been saved yet, return empty dict
return map_data return map_data
# Parse output # Parse output
for line in result.stdout.splitlines(): for line in result.stdout.decode().splitlines():
line = line.strip() m = re.match(
_r = REGEX_DDRESCUE_LOG.search(line) r'^\s*(?P<key>\S+):.*\(\s*(?P<value>\d+\.?\d*)%.*', line.strip())
if _r: if m:
map_data[_r.group('key')] = convert_to_bytes('{size} {unit}B'.format( try:
**_r.groupdict())) map_data[m.group('key')] = float(m.group('value'))
map_data['pass completed'] = 'current status: finished' in line except ValueError:
raise GenericError('Failed to read map data')
m = re.match(r'.*current status:\s+(?P<status>.*)', line.strip())
if m:
map_data['pass completed'] = bool(m.group('status') == 'finished')
# Check if 100% done # Check if 100% done
try: try:
@ -1043,7 +991,6 @@ def read_map_file(map_path):
def run_ddrescue(state, pass_settings): def run_ddrescue(state, pass_settings):
# pylint: disable=too-many-branches,too-many-statements
"""Run ddrescue pass.""" """Run ddrescue pass."""
return_code = -1 return_code = -1
aborted = False aborted = False
@ -1058,8 +1005,8 @@ def run_ddrescue(state, pass_settings):
# Create SMART monitor pane # Create SMART monitor pane
state.smart_out = '{}/smart_{}.out'.format( state.smart_out = '{}/smart_{}.out'.format(
global_vars['TmpDir'], state.smart_source.name) global_vars['TmpDir'], state.smart_source.name)
with open(state.smart_out, 'w') as _f: with open(state.smart_out, 'w') as f:
_f.write('Initializing...') f.write('Initializing...')
state.panes['SMART'] = tmux_split_window( state.panes['SMART'] = tmux_split_window(
behind=True, lines=12, vertical=True, watch=state.smart_out) behind=True, lines=12, vertical=True, watch=state.smart_out)
@ -1069,19 +1016,19 @@ def run_ddrescue(state, pass_settings):
command=['sudo', 'journalctl', '-f']) command=['sudo', 'journalctl', '-f'])
# Fix layout # Fix layout
state.fix_tmux_panes(forced=True) fix_tmux_panes(state, forced=True)
# Run pass for each block-pair # Run pass for each block-pair
for b_pair in state.block_pairs: for bp in state.block_pairs:
if b_pair.pass_done[state.current_pass]: if bp.pass_done[state.current_pass]:
# Skip to next block-pair # Skip to next block-pair
continue continue
update_sidepane(state) update_sidepane(state)
# Set ddrescue cmd # Set ddrescue cmd
cmd = [ cmd = [
'sudo', 'ddrescue', *pass_settings, 'ddrescue', *pass_settings,
b_pair.source_path, b_pair.dest_path, b_pair.map_path] bp.source_path, bp.dest_path, bp.map_path]
if state.mode == 'clone': if state.mode == 'clone':
cmd.append('--force') cmd.append('--force')
if state.current_pass == 0: if state.current_pass == 0:
@ -1096,36 +1043,36 @@ def run_ddrescue(state, pass_settings):
# Start ddrescue # Start ddrescue
try: try:
clear_screen() clear_screen()
print_info('Current dev: {}'.format(b_pair.source_path)) print_info('Current dev: {}'.format(bp.source_path))
ddrescue_proc = popen_program(cmd) ddrescue_proc = popen_program(cmd)
i = 0 i = 0
while True: while True:
# Update SMART display (every 30 seconds) # Update SMART display (every 30 seconds)
if i % 30 == 0: if i % 30 == 0:
state.smart_source.get_smart_details() state.smart_source.get_smart_details()
with open(state.smart_out, 'w') as _f: with open(state.smart_out, 'w') as f:
report = state.smart_source.generate_attribute_report( report = state.smart_source.generate_attribute_report(
timestamp=True) timestamp=True)
for line in report: for line in report:
_f.write('{}\n'.format(line)) f.write('{}\n'.format(line))
i += 1 i += 1
# Update progress # Update progress
b_pair.update_progress(state.current_pass) bp.update_progress(state.current_pass)
update_sidepane(state) update_sidepane(state)
# Fix panes # Fix panes
state.fix_tmux_panes() fix_tmux_panes(state)
# Check if ddrescue has finished # Check if ddrescue has finished
try: try:
ddrescue_proc.wait(timeout=1) ddrescue_proc.wait(timeout=1)
sleep(2) sleep(2)
b_pair.update_progress(state.current_pass) bp.update_progress(state.current_pass)
update_sidepane(state) update_sidepane(state)
break break
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
# Catch to update smart/b_pair/sidepane # Catch to update smart/bp/sidepane
pass pass
except KeyboardInterrupt: except KeyboardInterrupt:
@ -1134,7 +1081,7 @@ def run_ddrescue(state, pass_settings):
ddrescue_proc.wait(timeout=10) ddrescue_proc.wait(timeout=10)
# Update progress/sidepane again # Update progress/sidepane again
b_pair.update_progress(state.current_pass) bp.update_progress(state.current_pass)
update_sidepane(state) update_sidepane(state)
# Was ddrescue aborted? # Was ddrescue aborted?
@ -1156,7 +1103,7 @@ def run_ddrescue(state, pass_settings):
break break
else: else:
# Mark pass finished # Mark pass finished
b_pair.finish_pass(state.current_pass) bp.finish_pass(state.current_pass)
update_sidepane(state) update_sidepane(state)
# Done # Done
@ -1172,8 +1119,6 @@ def run_ddrescue(state, pass_settings):
def select_parts(source_device): def select_parts(source_device):
# pylint: disable=too-many-branches
# TODO: Clean up and/or replace with new menu-select function
"""Select partition(s) or whole device, returns list of DevObj()s.""" """Select partition(s) or whole device, returns list of DevObj()s."""
selected_parts = [] selected_parts = []
children = source_device.details.get('children', []) children = source_device.details.get('children', [])
@ -1235,26 +1180,24 @@ def select_parts(source_device):
raise GenericAbort() raise GenericAbort()
# Build list of selected parts # Build list of selected parts
for _d in dev_options: for d in dev_options:
if _d['Selected']: if d['Selected']:
_d['Dev'].model = source_device.model d['Dev'].model = source_device.model
_d['Dev'].model_size = source_device.model_size d['Dev'].model_size = source_device.model_size
_d['Dev'].update_filename_prefix() d['Dev'].update_filename_prefix()
selected_parts.append(_d['Dev']) selected_parts.append(d['Dev'])
return selected_parts return selected_parts
def select_path(skip_device=None): def select_path(skip_device=None):
# pylint: disable=too-many-branches,too-many-locals
# TODO: Clean up and/or replace with new menu-select function
"""Optionally mount local dev and select path, returns DirObj.""" """Optionally mount local dev and select path, returns DirObj."""
work_dir = os.path.realpath(global_vars['Env']['PWD']) wd = os.path.realpath(global_vars['Env']['PWD'])
selected_path = None selected_path = None
# Build menu # Build menu
path_options = [ path_options = [
{'Name': 'Current directory: {}'.format(work_dir), 'Path': work_dir}, {'Name': 'Current directory: {}'.format(wd), 'Path': wd},
{'Name': 'Local device', 'Path': None}, {'Name': 'Local device', 'Path': None},
{'Name': 'Enter manually', 'Path': None}] {'Name': 'Enter manually', 'Path': None}]
actions = [{'Name': 'Quit', 'Letter': 'Q'}] actions = [{'Name': 'Quit', 'Letter': 'Q'}]
@ -1269,9 +1212,9 @@ def select_path(skip_device=None):
raise GenericAbort() raise GenericAbort()
elif selection.isnumeric(): elif selection.isnumeric():
index = int(selection) - 1 index = int(selection) - 1
if path_options[index]['Path'] == work_dir: if path_options[index]['Path'] == wd:
# Current directory # Current directory
selected_path = DirObj(work_dir) selected_path = DirObj(wd)
elif path_options[index]['Name'] == 'Local device': elif path_options[index]['Name'] == 'Local device':
# Local device # Local device
@ -1287,15 +1230,15 @@ def select_path(skip_device=None):
# Select volume # Select volume
vol_options = [] vol_options = []
for _k, _v in sorted(report.items()): for k, v in sorted(report.items()):
disabled = _v['show_data']['data'] == 'Failed to mount' disabled = v['show_data']['data'] == 'Failed to mount'
if disabled: if disabled:
name = '{name} (Failed to mount)'.format(**_v) name = '{name} (Failed to mount)'.format(**v)
else: else:
name = '{name} (mounted on "{mount_point}")'.format(**_v) name = '{name} (mounted on "{mount_point}")'.format(**v)
vol_options.append({ vol_options.append({
'Name': name, 'Name': name,
'Path': _v['mount_point'], 'Path': v['mount_point'],
'Disabled': disabled}) 'Disabled': disabled})
selection = menu_select( selection = menu_select(
title='Please select a volume', title='Please select a volume',
@ -1370,17 +1313,15 @@ def select_device(description='device', skip_device=None):
action_entries=actions, action_entries=actions,
disabled_label='ALREADY SELECTED') disabled_label='ALREADY SELECTED')
if selection == 'Q':
raise GenericAbort()
if selection.isnumeric(): if selection.isnumeric():
return dev_options[int(selection)-1]['Dev'] return dev_options[int(selection)-1]['Dev']
elif selection == 'Q':
raise GenericAbort()
def setup_loopback_device(source_path): def setup_loopback_device(source_path):
"""Setup loopback device for source_path, returns dev_path as str.""" """Setup loopback device for source_path, returns dev_path as str."""
cmd = ( cmd = (
'sudo',
'losetup', 'losetup',
'--find', '--find',
'--partscan', '--partscan',
@ -1414,7 +1355,6 @@ def show_selection_details(state):
def show_usage(script_name): def show_usage(script_name):
"""Show usage."""
print_info('Usage:') print_info('Usage:')
print_standard(USAGE.format(script_name=script_name)) print_standard(USAGE.format(script_name=script_name))
pause() pause()
@ -1438,14 +1378,14 @@ def update_sidepane(state):
output.append('─────────────────────') output.append('─────────────────────')
# Source(s) progress # Source(s) progress
for b_pair in state.block_pairs: for bp in state.block_pairs:
if state.source.is_image(): if state.source.is_image():
output.append('{BLUE}Image File{CLEAR}'.format(**COLORS)) output.append('{BLUE}Image File{CLEAR}'.format(**COLORS))
else: else:
output.append('{BLUE}{source}{CLEAR}'.format( output.append('{BLUE}{source}{CLEAR}'.format(
source=b_pair.source_path, source=bp.source_path,
**COLORS)) **COLORS))
output.extend(b_pair.status) output.extend(bp.status)
output.append(' ') output.append(' ')
# EToC # EToC
@ -1464,9 +1404,11 @@ def update_sidepane(state):
# Add line-endings # Add line-endings
output = ['{}\n'.format(line) for line in output] output = ['{}\n'.format(line) for line in output]
with open(state.progress_out, 'w') as _f: with open(state.progress_out, 'w') as f:
_f.writelines(output) f.writelines(output)
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.")
# vim: sts=2 sw=2 ts=2

View file

@ -36,7 +36,6 @@ class CpuObj():
self.tests = OrderedDict() self.tests = OrderedDict()
self.get_details() self.get_details()
self.name = self.lscpu.get('Model name', 'Unknown CPU') self.name = self.lscpu.get('Model name', 'Unknown CPU')
self.description = self.name
def get_details(self): def get_details(self):
"""Get CPU details from lscpu.""" """Get CPU details from lscpu."""
@ -52,22 +51,12 @@ class CpuObj():
continue continue
self.lscpu[_field] = _data self.lscpu[_field] = _data
# Get RAM details as well
ram_details = get_ram_details()
self.ram_total = human_readable_size(ram_details.pop('Total', 0)).strip()
self.ram_dimms = [
'{}x {}'.format(v, k) for k, v in sorted(ram_details.items())]
def generate_cpu_report(self): def generate_cpu_report(self):
"""Generate CPU report with data from all tests.""" """Generate CPU report with data from all tests."""
report = [] report = []
report.append('{BLUE}Device{CLEAR}'.format(**COLORS)) report.append('{BLUE}Device{CLEAR}'.format(**COLORS))
report.append(' {}'.format(self.name)) report.append(' {}'.format(self.name))
# Include RAM details
report.append('{BLUE}RAM{CLEAR}'.format(**COLORS))
report.append(' {} ({})'.format(self.ram_total, ', '.join(self.ram_dimms)))
# Tests # Tests
for test in self.tests.values(): for test in self.tests.values():
report.extend(test.report) report.extend(test.report)
@ -95,15 +84,7 @@ class DiskObj():
self.get_size() self.get_size()
# Try enabling SMART # Try enabling SMART
run_program( run_program(['sudo', 'smartctl', '--smart=on', self.path], check=False)
cmd=[
'sudo',
'smartctl',
'--tolerance=permissive',
'--smart=on',
self.path,
],
check=False)
# Get NVMe/SMART data and set description # Get NVMe/SMART data and set description
self.get_smart_details() self.get_smart_details()
@ -197,8 +178,8 @@ class DiskObj():
disk_ok = False disk_ok = False
# Disable override if necessary # Disable override if necessary
if ATTRIBUTES[attr_type][k].get('Critical', False): self.override_disabled |= ATTRIBUTES[attr_type][k].get(
self.override_disabled = True 'Critical', False)
# SMART overall assessment # SMART overall assessment
## NOTE: Only fail drives if the overall value exists and reports failed ## NOTE: Only fail drives if the overall value exists and reports failed
@ -231,12 +212,11 @@ class DiskObj():
# Done # Done
return test_running return test_running
def disable_test(self, name, status, test_failed=False): def disable_test(self, name, status):
"""Disable test by name and update status.""" """Disable test by name and update status."""
if name in self.tests: if name in self.tests:
self.tests[name].update_status(status) self.tests[name].update_status(status)
self.tests[name].disabled = True self.tests[name].disabled = True
self.tests[name].failed = test_failed
def generate_attribute_report( def generate_attribute_report(
self, description=False, timestamp=False): self, description=False, timestamp=False):
@ -319,11 +299,6 @@ class DiskObj():
attr_type=self.attr_type, **COLORS)) attr_type=self.attr_type, **COLORS))
report.extend(sorted(self.nvme_smart_notes.keys())) report.extend(sorted(self.nvme_smart_notes.keys()))
# 4K alignment check
if not self.is_4k_aligned():
report.append('{YELLOW}Warning{CLEAR}'.format(**COLORS))
report.append(' One or more partitions are not 4K aligned')
# Tests # Tests
for test in self.tests.values(): for test in self.tests.values():
report.extend(test.report) report.extend(test.report)
@ -374,15 +349,8 @@ class DiskObj():
def get_smart_details(self): def get_smart_details(self):
"""Get data from smartctl.""" """Get data from smartctl."""
cmd = [ cmd = ['sudo', 'smartctl', '--all', '--json', self.path]
'sudo', self.smartctl = get_json_from_command(cmd)
'smartctl',
'--tolerance=verypermissive',
'--all',
'--json',
self.path,
]
self.smartctl = get_json_from_command(cmd, check=False)
# Check for attributes # Check for attributes
if KEY_NVME in self.smartctl: if KEY_NVME in self.smartctl:
@ -427,26 +395,6 @@ class DiskObj():
'self_test', {}).get( 'self_test', {}).get(
k, {}) k, {})
def is_4k_aligned(self):
"""Check if partitions are 4K aligned, returns bool."""
cmd = [
'sudo',
'sfdisk',
'--json',
self.path,
]
aligned = True
# Get partition details
json_data = get_json_from_command(cmd)
# Check partitions
for part in json_data.get('partitiontable', {}).get('partitions', []):
aligned = aligned and part.get('start', -1) % 4096 == 0
# Done
return aligned
def safety_check(self, silent=False): def safety_check(self, silent=False):
"""Run safety checks and disable tests if necessary.""" """Run safety checks and disable tests if necessary."""
test_running = False test_running = False
@ -491,6 +439,7 @@ class DiskObj():
disk_ok = OVERRIDES_FORCED or ask('Run tests on this device anyway?') disk_ok = OVERRIDES_FORCED or ask('Run tests on this device anyway?')
print_standard(' ') print_standard(' ')
# Disable tests if necessary (statuses won't be overwritten) # Disable tests if necessary (statuses won't be overwritten)
if test_running: if test_running:
if not silent: if not silent:
@ -499,7 +448,7 @@ class DiskObj():
for t in ['badblocks', 'I/O Benchmark']: for t in ['badblocks', 'I/O Benchmark']:
self.disable_test(t, 'Denied') self.disable_test(t, 'Denied')
elif not disk_ok: elif not disk_ok:
self.disable_test('NVMe / SMART', 'NS', test_failed=True) self.disable_test('NVMe / SMART', 'NS')
for t in ['badblocks', 'I/O Benchmark']: for t in ['badblocks', 'I/O Benchmark']:
self.disable_test(t, 'Denied') self.disable_test(t, 'Denied')
@ -507,7 +456,6 @@ class DiskObj():
class State(): class State():
"""Object to track device objects and overall state.""" """Object to track device objects and overall state."""
def __init__(self): def __init__(self):
self.args = None
self.cpu = None self.cpu = None
self.disks = [] self.disks = []
self.panes = {} self.panes = {}
@ -535,83 +483,6 @@ class State():
}, },
}) })
def build_outer_panes(self):
"""Build top and side panes."""
clear_screen()
# Top
self.panes['Top'] = tmux_split_window(
behind=True, lines=2, vertical=True,
text=TOP_PANE_TEXT)
# Started
self.panes['Started'] = tmux_split_window(
lines=SIDE_PANE_WIDTH, target_pane=self.panes['Top'],
text='{BLUE}Started{CLEAR}\n{s}'.format(
s=time.strftime("%Y-%m-%d %H:%M %Z"),
**COLORS))
# Progress
self.panes['Progress'] = tmux_split_window(
lines=SIDE_PANE_WIDTH,
watch=self.progress_out)
def fix_tmux_panes(self):
"""Fix pane sizes if the window has been resized."""
needs_fixed = False
# Bail?
if not self.panes:
return
# Check layout
for k, v in self.tmux_layout.items():
if not v.get('Check'):
# Not concerned with the size of this pane
continue
# Get target
target = None
if k != 'Current':
if k not in self.panes:
# Skip missing panes
continue
else:
target = self.panes[k]
# Check pane size
x, y = tmux_get_pane_size(pane_id=target)
if v.get('x', False) and v['x'] != x:
needs_fixed = True
if v.get('y', False) and v['y'] != y:
needs_fixed = True
# Bail?
if not needs_fixed:
return
# Update layout
for k, v in self.tmux_layout.items():
# Get target
target = None
if k != 'Current':
if k not in self.panes:
# Skip missing panes
continue
else:
target = self.panes[k]
# Resize pane
tmux_resize_pane(pane_id=target, **v)
def fix_tmux_panes_loop(self):
while True:
try:
self.fix_tmux_panes()
sleep(1)
except RuntimeError:
# Assuming layout definitions changes mid-run, ignoring
pass
def init(self): def init(self):
"""Remove test objects, set log, and add devices.""" """Remove test objects, set log, and add devices."""
self.disks = [] self.disks = []
@ -619,18 +490,14 @@ class State():
v['Objects'] = [] v['Objects'] = []
# Update LogDir # Update LogDir
if self.quick_mode: if not self.quick_mode:
global_vars['LogDir'] = '{}/Logs/{}'.format(
global_vars['Env']['HOME'],
time.strftime('%Y-%m-%d_%H%M_%z'))
else:
global_vars['LogDir'] = '{}/Logs/{}_{}'.format( global_vars['LogDir'] = '{}/Logs/{}_{}'.format(
global_vars['Env']['HOME'], global_vars['Env']['HOME'],
get_ticket_number(), get_ticket_number(),
time.strftime('%Y-%m-%d_%H%M_%z')) time.strftime('%Y-%m-%d_%H%M_%z'))
os.makedirs(global_vars['LogDir'], exist_ok=True) os.makedirs(global_vars['LogDir'], exist_ok=True)
global_vars['LogFile'] = '{}/Hardware Diagnostics.log'.format( global_vars['LogFile'] = '{}/Hardware Diagnostics.log'.format(
global_vars['LogDir']) global_vars['LogDir'])
self.progress_out = '{}/progress.out'.format(global_vars['LogDir']) self.progress_out = '{}/progress.out'.format(global_vars['LogDir'])
# Add CPU # Add CPU
@ -659,13 +526,7 @@ class State():
# Start tmux thread # Start tmux thread
self.tmux_layout = TMUX_LAYOUT.copy() self.tmux_layout = TMUX_LAYOUT.copy()
start_thread(self.fix_tmux_panes_loop) start_thread(fix_tmux_panes_loop, args=[self])
def set_top_pane_text(self, text):
"""Set top pane text using TOP_PANE_TEXT and provided text."""
tmux_update_pane(
self.panes['Top'],
text='{}\n{}'.format(TOP_PANE_TEXT, text))
class TestObj(): class TestObj():
@ -700,6 +561,28 @@ class TestObj():
# Functions # Functions
def build_outer_panes(state):
"""Build top and side panes."""
clear_screen()
# Top
state.panes['Top'] = tmux_split_window(
behind=True, lines=2, vertical=True,
text=TOP_PANE_TEXT)
# Started
state.panes['Started'] = tmux_split_window(
lines=SIDE_PANE_WIDTH, target_pane=state.panes['Top'],
text='{BLUE}Started{CLEAR}\n{s}'.format(
s=time.strftime("%Y-%m-%d %H:%M %Z"),
**COLORS))
# Progress
state.panes['Progress'] = tmux_split_window(
lines=SIDE_PANE_WIDTH,
watch=state.progress_out)
def build_status_string(label, status, info_label=False): def build_status_string(label, status, info_label=False):
"""Build status string with appropriate colors.""" """Build status string with appropriate colors."""
status_color = COLORS['CLEAR'] status_color = COLORS['CLEAR']
@ -716,6 +599,64 @@ def build_status_string(label, status, info_label=False):
**COLORS) **COLORS)
def fix_tmux_panes_loop(state):
while True:
try:
fix_tmux_panes(state)
sleep(1)
except RuntimeError:
# Assuming layout definitions changes mid-run, ignoring
pass
def fix_tmux_panes(state):
"""Fix pane sizes if the window has been resized."""
needs_fixed = False
# Bail?
if not state.panes:
return
# Check layout
for k, v in state.tmux_layout.items():
if not v.get('Check'):
# Not concerned with the size of this pane
continue
# Get target
target = None
if k != 'Current':
if k not in state.panes:
# Skip missing panes
continue
else:
target = state.panes[k]
# Check pane size
x, y = tmux_get_pane_size(pane_id=target)
if v.get('x', False) and v['x'] != x:
needs_fixed = True
if v.get('y', False) and v['y'] != y:
needs_fixed = True
# Bail?
if not needs_fixed:
return
# Update layout
for k, v in state.tmux_layout.items():
# Get target
target = None
if k != 'Current':
if k not in state.panes:
# Skip missing panes
continue
else:
target = state.panes[k]
# Resize pane
tmux_resize_pane(pane_id=target, **v)
def generate_horizontal_graph(rates, oneline=False): def generate_horizontal_graph(rates, oneline=False):
"""Generate horizontal graph from rates, returns list.""" """Generate horizontal graph from rates, returns list."""
graph = ['', '', '', ''] graph = ['', '', '', '']
@ -775,44 +716,6 @@ def get_graph_step(rate, scale=16):
return step return step
def get_ram_details():
"""Get RAM details via dmidecode, returns dict."""
cmd = ['sudo', 'dmidecode', '--type', 'memory']
manufacturer = 'UNKNOWN'
ram_details = {'Total': 0}
size = 0
# Get DMI data
result = run_program(cmd, encoding='utf-8', errors='ignore')
dmi_data = result.stdout.splitlines()
# Parse data
for line in dmi_data:
line = line.strip()
if line == 'Memory Device':
# Reset vars
manufacturer = 'UNKNOWN'
size = 0
elif line.startswith('Size:'):
size = convert_to_bytes(line.replace('Size: ', ''))
elif line.startswith('Manufacturer:'):
manufacturer = line.replace('Manufacturer: ', '')
if size > 0:
# Add RAM to list if slot populated
ram_str = '{} {}'.format(
human_readable_size(size).strip(),
manufacturer,
)
ram_details['Total'] += size
if ram_str in ram_details:
ram_details[ram_str] += 1
else:
ram_details[ram_str] = 1
# Done
return ram_details
def get_read_rate(s): def get_read_rate(s):
"""Get read rate in bytes/s from dd progress output.""" """Get read rate in bytes/s from dd progress output."""
real_rate = None real_rate = None
@ -825,7 +728,6 @@ def get_read_rate(s):
def menu_diags(state, args): def menu_diags(state, args):
"""Main menu to select and run HW tests.""" """Main menu to select and run HW tests."""
args = [a.lower() for a in args] args = [a.lower() for a in args]
state.args = args
checkmark = '*' checkmark = '*'
if 'DISPLAY' in global_vars['Env']: if 'DISPLAY' in global_vars['Env']:
checkmark = '' checkmark = ''
@ -871,7 +773,7 @@ def menu_diags(state, args):
# If so, verify no other tests are enabled and set quick_mode # If so, verify no other tests are enabled and set quick_mode
state.quick_mode = True state.quick_mode = True
for opt in main_options[3:4] + main_options[5:]: for opt in main_options[3:4] + main_options[5:]:
state.quick_mode = state.quick_mode and not opt['Enabled'] state.quick_mode &= not opt['Enabled']
else: else:
state.quick_mode = False state.quick_mode = False
@ -967,7 +869,10 @@ def run_badblocks_test(state, test):
update_progress_pane(state) update_progress_pane(state)
# Update tmux layout # Update tmux layout
state.set_top_pane_text(dev.description) tmux_update_pane(
state.panes['Top'],
text='{}\n{}'.format(
TOP_PANE_TEXT, dev.description))
# Create monitor pane # Create monitor pane
test.badblocks_out = '{}/badblocks_{}.out'.format( test.badblocks_out = '{}/badblocks_{}.out'.format(
@ -1050,11 +955,10 @@ def run_hw_tests(state):
"""Run enabled hardware tests.""" """Run enabled hardware tests."""
print_standard('Scanning devices...') print_standard('Scanning devices...')
state.init() state.init()
tests_enabled = False
# Build Panes # Build Panes
update_progress_pane(state) update_progress_pane(state)
state.build_outer_panes() build_outer_panes(state)
# Show selected tests and create TestObj()s # Show selected tests and create TestObj()s
print_info('Selected Tests:') print_info('Selected Tests:')
@ -1066,8 +970,6 @@ def run_hw_tests(state):
COLORS['CLEAR'], COLORS['CLEAR'],
QUICK_LABEL if state.quick_mode and 'NVMe' in k else '')) QUICK_LABEL if state.quick_mode and 'NVMe' in k else ''))
if v['Enabled']: if v['Enabled']:
tests_enabled = True
# Create TestObj and track under both CpuObj/DiskObj and State # Create TestObj and track under both CpuObj/DiskObj and State
if k in TESTS_CPU: if k in TESTS_CPU:
test_obj = TestObj( test_obj = TestObj(
@ -1081,16 +983,10 @@ def run_hw_tests(state):
v['Objects'].append(test_obj) v['Objects'].append(test_obj)
print_standard('') print_standard('')
# Bail if no tests selected
if not tests_enabled:
tmux_kill_pane(*state.panes.values())
return
# Run disk safety checks (if necessary) # Run disk safety checks (if necessary)
_disk_tests_enabled = False _disk_tests_enabled = False
for k in TESTS_DISK: for k in TESTS_DISK:
if state.tests[k]['Enabled']: _disk_tests_enabled |= state.tests[k]['Enabled']
_disk_tests_enabled = True
if _disk_tests_enabled: if _disk_tests_enabled:
for disk in state.disks: for disk in state.disks:
try: try:
@ -1128,7 +1024,7 @@ def run_hw_tests(state):
# Rebuild panes # Rebuild panes
update_progress_pane(state) update_progress_pane(state)
state.build_outer_panes() build_outer_panes(state)
# Mark unfinished tests as aborted # Mark unfinished tests as aborted
for k, v in state.tests.items(): for k, v in state.tests.items():
@ -1140,24 +1036,8 @@ def run_hw_tests(state):
# Update side pane # Update side pane
update_progress_pane(state) update_progress_pane(state)
# Show results
show_results(state)
# Upload for review
if (ENABLED_UPLOAD_DATA
and DEBUG_MODE
and ask('Upload results for review?')):
try_and_print(
message='Saving debug reports...',
function=save_debug_reports,
state=state, global_vars=global_vars)
try_and_print(
message='Uploading Data...',
function=upload_logdir,
global_vars=global_vars,
reason='Review')
# Done # Done
show_results(state)
sleep(1) sleep(1)
if state.quick_mode: if state.quick_mode:
pause('Press Enter to exit... ') pause('Press Enter to exit... ')
@ -1184,7 +1064,10 @@ def run_io_benchmark(state, test):
update_progress_pane(state) update_progress_pane(state)
# Update tmux layout # Update tmux layout
state.set_top_pane_text(dev.description) tmux_update_pane(
state.panes['Top'],
text='{}\n{}'.format(
TOP_PANE_TEXT, dev.description))
state.tmux_layout['Current'] = {'y': 15, 'Check': True} state.tmux_layout['Current'] = {'y': 15, 'Check': True}
# Create monitor pane # Create monitor pane
@ -1343,7 +1226,9 @@ def run_mprime_test(state, test):
test.thermal_abort = False test.thermal_abort = False
# Update tmux layout # Update tmux layout
state.set_top_pane_text(dev.name) tmux_update_pane(
state.panes['Top'],
text='{}\n{}'.format(TOP_PANE_TEXT, dev.name))
# Start live sensor monitor # Start live sensor monitor
test.sensors_out = '{}/sensors.out'.format(global_vars['TmpDir']) test.sensors_out = '{}/sensors.out'.format(global_vars['TmpDir'])
@ -1506,7 +1391,7 @@ def run_mprime_test(state, test):
# Add temps to report # Add temps to report
test.report.append('{BLUE}Temps{CLEAR}'.format(**COLORS)) test.report.append('{BLUE}Temps{CLEAR}'.format(**COLORS))
for line in generate_sensor_report( for line in generate_sensor_report(
test.sensor_data, 'Idle', 'Max', 'Cooldown', cpu_only=True): test.sensor_data, 'Idle', 'Max', 'Cooldown', core_only=True):
test.report.append(' {}'.format(line)) test.report.append(' {}'.format(line))
# Add abort message(s) # Add abort message(s)
@ -1556,7 +1441,10 @@ def run_nvme_smart_tests(state, test, update_mode=False):
update_progress_pane(state) update_progress_pane(state)
# Update tmux layout # Update tmux layout
state.set_top_pane_text(dev.description) tmux_update_pane(
state.panes['Top'],
text='{}\n{}'.format(
TOP_PANE_TEXT, dev.description))
# SMART short self-test # SMART short self-test
if dev.smart_attributes and not (state.quick_mode or update_mode): if dev.smart_attributes and not (state.quick_mode or update_mode):
@ -1626,13 +1514,7 @@ def run_smart_short_test(state, test):
# Start short test # Start short test
print_standard('Running self-test...') print_standard('Running self-test...')
cmd = [ cmd = ['sudo', 'smartctl', '--test=short', dev.path]
'sudo',
'smartctl',
'--tolerance=normal',
'--test=short',
dev.path,
]
run_program(cmd, check=False) run_program(cmd, check=False)
# Monitor progress # Monitor progress
@ -1701,13 +1583,14 @@ def show_report(report, log_report=False):
def show_results(state): def show_results(state):
"""Show results for all tests.""" """Show results for all tests."""
clear_screen() clear_screen()
state.set_top_pane_text('Results') tmux_update_pane(
state.panes['Top'],
text='{}\nResults'.format(TOP_PANE_TEXT))
# CPU tests # CPU tests
_enabled = False _enabled = False
for k in TESTS_CPU: for k in TESTS_CPU:
if state.tests[k]['Enabled']: _enabled |= state.tests[k]['Enabled']
_enabled = True
if _enabled: if _enabled:
print_success('CPU:'.format(k)) print_success('CPU:'.format(k))
show_report(state.cpu.generate_cpu_report(), log_report=True) show_report(state.cpu.generate_cpu_report(), log_report=True)
@ -1716,8 +1599,7 @@ def show_results(state):
# Disk tests # Disk tests
_enabled = False _enabled = False
for k in TESTS_DISK: for k in TESTS_DISK:
if state.tests[k]['Enabled']: _enabled |= state.tests[k]['Enabled']
_enabled = True
if _enabled: if _enabled:
print_success('Disk{}:'.format( print_success('Disk{}:'.format(
'' if len(state.disks) == 1 else 's')) '' if len(state.disks) == 1 else 's'))

View file

@ -95,7 +95,7 @@ def get_installed_antivirus():
out = out.stdout.decode().strip() out = out.stdout.decode().strip()
state = out.split('=')[1] state = out.split('=')[1]
state = hex(int(state)) state = hex(int(state))
if str(state)[3:5] not in ['10', '11']: if str(state)[3:5] != '10':
programs.append('[Disabled] {}'.format(prod)) programs.append('[Disabled] {}'.format(prod))
else: else:
programs.append(prod) programs.append(prod)
@ -446,19 +446,16 @@ def show_os_name():
def show_temp_files_size(): def show_temp_files_size():
"""Show total size of temp files identified by BleachBit.""" """Show total size of temp files identified by BleachBit."""
size_str = None size = None
total = 0
with open(r'{LogDir}\Tools\BleachBit.log'.format(**global_vars), 'r') as f: with open(r'{LogDir}\Tools\BleachBit.log'.format(**global_vars), 'r') as f:
for line in f.readlines(): for line in f.readlines():
if re.search(r'^Disk space (to be |)recovered:', line, re.IGNORECASE): if re.search(r'^disk space to be recovered:', line, re.IGNORECASE):
size = re.sub(r'.*: ', '', line.strip()) size = re.sub(r'.*: ', '', line.strip())
size = re.sub(r'(\w)iB$', r' \1b', size) size = re.sub(r'(\w)iB$', r' \1b', size)
total += convert_to_bytes(size) if size is None:
size_str = human_readable_size(total, decimals=1) print_warning(size, timestamp=False)
if size_str is None:
print_warning('UNKNOWN', timestamp=False)
else: else:
print_standard(size_str, timestamp=False) print_standard(size, timestamp=False)
def show_user_data_summary(indent=8, width=32): def show_user_data_summary(indent=8, width=32):

View file

@ -4,7 +4,7 @@ import json
from functions.common import * from functions.common import *
def get_json_from_command(cmd, check=True, ignore_errors=True): def get_json_from_command(cmd, ignore_errors=True):
"""Capture JSON content from cmd output, returns dict. """Capture JSON content from cmd output, returns dict.
If the data can't be decoded then either an exception is raised If the data can't be decoded then either an exception is raised
@ -17,7 +17,7 @@ def get_json_from_command(cmd, check=True, ignore_errors=True):
errors = 'ignore' errors = 'ignore'
try: try:
result = run_program(cmd, check=check, encoding='utf-8', errors=errors) result = run_program(cmd, encoding='utf-8', errors=errors)
json_data = json.loads(result.stdout) json_data = json.loads(result.stdout)
except (subprocess.CalledProcessError, json.decoder.JSONDecodeError): except (subprocess.CalledProcessError, json.decoder.JSONDecodeError):
if not ignore_errors: if not ignore_errors:

View file

@ -15,6 +15,27 @@ REGEX_VALID_IP = re.compile(
re.IGNORECASE) re.IGNORECASE)
def connect_to_network():
"""Connect to network if not already connected."""
net_ifs = psutil.net_if_addrs()
net_ifs = [i[:2] for i in net_ifs.keys()]
# Bail if currently connected
if is_connected():
return
# WiFi
if 'wl' in net_ifs:
cmd = [
'nmcli', 'dev', 'wifi',
'connect', WIFI_SSID,
'password', WIFI_PASSWORD]
try_and_print(
message = 'Connecting to {}...'.format(WIFI_SSID),
function = run_program,
cmd = cmd)
def is_connected(): def is_connected():
"""Check for a valid private IP.""" """Check for a valid private IP."""
devs = psutil.net_if_addrs() devs = psutil.net_if_addrs()

View file

@ -1,6 +1,4 @@
'''Wizard Kit: Functions - Sensors''' # Wizard Kit: Functions - Sensors
# pylint: disable=no-name-in-module,wildcard-import
# vim: sts=2 sw=2 ts=2
import json import json
import re import re
@ -11,7 +9,7 @@ from settings.sensors import *
# Error Classes # Error Classes
class ThermalLimitReachedError(Exception): class ThermalLimitReachedError(Exception):
'''Thermal limit reached error.''' pass
def clear_temps(sensor_data): def clear_temps(sensor_data):
@ -22,30 +20,28 @@ def clear_temps(sensor_data):
_data['Temps'] = [] _data['Temps'] = []
def fix_sensor_str(_s): def fix_sensor_str(s):
"""Cleanup string and return str.""" """Cleanup string and return str."""
_s = re.sub(r'^(\w+)-(\w+)-(\w+)', r'\1 (\2 \3)', _s, re.IGNORECASE) s = re.sub(r'^(\w+)-(\w+)-(\w+)', r'\1 (\2 \3)', s, re.IGNORECASE)
_s = _s.title() s = s.title()
_s = _s.replace('Coretemp', 'CPUTemp') s = s.replace('Coretemp', 'CoreTemp')
_s = _s.replace('Acpi', 'ACPI') s = s.replace('Acpi', 'ACPI')
_s = _s.replace('ACPItz', 'ACPI TZ') s = s.replace('ACPItz', 'ACPI TZ')
_s = _s.replace('Isa ', 'ISA ') s = s.replace('Isa ', 'ISA ')
_s = _s.replace('Pci ', 'PCI ') s = s.replace('Id ', 'ID ')
_s = _s.replace('Id ', 'ID ') s = re.sub(r'(\D+)(\d+)', r'\1 \2', s, re.IGNORECASE)
_s = re.sub(r'(\D+)(\d+)', r'\1 \2', _s, re.IGNORECASE) s = s.replace(' ', ' ')
_s = re.sub(r'^K (\d+)Temp', r'AMD K\1 Temps', _s, re.IGNORECASE) return s
_s = re.sub(r'T(ctl|die)', r'CPU (T\1)', _s, re.IGNORECASE)
return _s
def generate_sensor_report( def generate_sensor_report(
sensor_data, *temp_labels, sensor_data, *temp_labels,
colors=True, cpu_only=False): colors=True, core_only=False):
"""Generate report based on temp_labels, returns list if str.""" """Generate report based on temp_labels, returns list if str."""
report = [] report = []
for _section, _adapters in sorted(sensor_data.items()): for _section, _adapters in sorted(sensor_data.items()):
# CPU temps then Other temps # CoreTemps then Other temps
if cpu_only and 'CPU' not in _section: if core_only and 'Core' not in _section:
continue continue
for _adapter, _sources in sorted(_adapters.items()): for _adapter, _sources in sorted(_adapters.items()):
# Adapter # Adapter
@ -60,7 +56,7 @@ def generate_sensor_report(
': ' if _label != 'Current' else '', ': ' if _label != 'Current' else '',
get_temp_str(_data.get(_label, '???'), colors=colors)) get_temp_str(_data.get(_label, '???'), colors=colors))
report.append(_line) report.append(_line)
if not cpu_only: if not core_only:
report.append(' ') report.append(' ')
# Handle empty reports (i.e. no sensors detected) # Handle empty reports (i.e. no sensors detected)
@ -95,15 +91,15 @@ def get_colored_temp_str(temp):
else: else:
color = COLORS['CLEAR'] color = COLORS['CLEAR']
return '{color}{prefix}{temp:2.0f}°C{CLEAR}'.format( return '{color}{prefix}{temp:2.0f}°C{CLEAR}'.format(
color=color, color = color,
prefix='-' if temp < 0 else '', prefix = '-' if temp < 0 else '',
temp=temp, temp = temp,
**COLORS) **COLORS)
def get_raw_sensor_data(): def get_raw_sensor_data():
"""Read sensor data and return dict.""" """Read sensor data and return dict."""
json_data = {} data = {}
cmd = ['sensors', '-j'] cmd = ['sensors', '-j']
# Get raw data # Get raw data
@ -126,8 +122,8 @@ def get_raw_sensor_data():
try: try:
json_data = json.loads('\n'.join(raw_data)) json_data = json.loads('\n'.join(raw_data))
except json.JSONDecodeError: except json.JSONDecodeError:
# Still broken, just return the empty dict # Still broken, just set to empty dict
pass json_data = {}
# Done # Done
return json_data return json_data
@ -136,10 +132,10 @@ def get_raw_sensor_data():
def get_sensor_data(): def get_sensor_data():
"""Parse raw sensor data and return new dict.""" """Parse raw sensor data and return new dict."""
json_data = get_raw_sensor_data() json_data = get_raw_sensor_data()
sensor_data = {'CPUTemps': {}, 'Other': {}} sensor_data = {'CoreTemps': {}, 'Other': {}}
for _adapter, _sources in json_data.items(): for _adapter, _sources in json_data.items():
if is_cpu_adapter(_adapter): if 'coretemp' in _adapter:
_section = 'CPUTemps' _section = 'CoreTemps'
else: else:
_section = 'Other' _section = 'Other'
sensor_data[_section][_adapter] = {} sensor_data[_section][_adapter] = {}
@ -161,8 +157,8 @@ def get_sensor_data():
} }
# Remove empty sections # Remove empty sections
for _k, _v in sensor_data.items(): for k, v in sensor_data.items():
_v = {_k2: _v2 for _k2, _v2 in _v.items() if _v2} v = {k2: v2 for k2, v2 in v.items() if v2}
# Done # Done
return sensor_data return sensor_data
@ -182,20 +178,14 @@ def get_temp_str(temp, colors=True):
temp) temp)
def is_cpu_adapter(adapter):
"""Checks if adapter is a known CPU adapter, returns bool."""
is_cpu = re.search(r'(core|k\d+)temp', adapter, re.IGNORECASE)
return bool(is_cpu)
def monitor_sensors(monitor_pane, monitor_file): def monitor_sensors(monitor_pane, monitor_file):
"""Continually update sensor data and report to screen.""" """Continually update sensor data and report to screen."""
sensor_data = get_sensor_data() sensor_data = get_sensor_data()
while True: while True:
update_sensor_data(sensor_data) update_sensor_data(sensor_data)
with open(monitor_file, 'w') as _f: with open(monitor_file, 'w') as f:
report = generate_sensor_report(sensor_data, 'Current', 'Max') report = generate_sensor_report(sensor_data, 'Current', 'Max')
_f.write('\n'.join(report)) f.write('\n'.join(report))
sleep(1) sleep(1)
if monitor_pane and not tmux_poll_pane(monitor_pane): if monitor_pane and not tmux_poll_pane(monitor_pane):
break break
@ -206,7 +196,7 @@ def save_average_temp(sensor_data, temp_label, seconds=10):
clear_temps(sensor_data) clear_temps(sensor_data)
# Get temps # Get temps
for _i in range(seconds): # pylint: disable=unused-variable for i in range(seconds):
update_sensor_data(sensor_data) update_sensor_data(sensor_data)
sleep(1) sleep(1)
@ -229,15 +219,24 @@ def update_sensor_data(sensor_data, thermal_limit=None):
_data['Current'] = _temp _data['Current'] = _temp
_data['Max'] = max(_temp, _data['Max']) _data['Max'] = max(_temp, _data['Max'])
_data['Temps'].append(_temp) _data['Temps'].append(_temp)
except Exception: # pylint: disable=broad-except except Exception:
# Dumb workound for Dell sensors with changing source names # Dumb workound for Dell sensors with changing source names
pass pass
# Check if thermal limit reached # Check if thermal limit reached
if thermal_limit and _section == 'CPUTemps': if thermal_limit and _section == 'CoreTemps':
if max(_data['Current'], _data['Max']) >= thermal_limit: if max(_data['Current'], _data['Max']) >= thermal_limit:
raise ThermalLimitReachedError('CPU temps reached limit') raise ThermalLimitReachedError('CoreTemps reached limit')
def join_columns(column1, column2, width=55):
return '{:<{}}{}'.format(
column1,
55+len(column1)-len(REGEX_COLORS.sub('', column1)),
column2)
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.")
# vim: sts=2 sw=2 ts=2

View file

@ -1,10 +1,7 @@
# Wizard Kit: Functions - Setup # Wizard Kit: Functions - Setup
from functions.browsers import *
from functions.json import *
from functions.update import * from functions.update import *
from settings.setup import * from settings.setup import *
from settings.sources import *
# Configuration # Configuration
@ -66,13 +63,9 @@ def config_explorer_system():
write_registry_settings(SETTINGS_EXPLORER_SYSTEM, all_users=True) write_registry_settings(SETTINGS_EXPLORER_SYSTEM, all_users=True)
def config_explorer_user(setup_mode='All'): def config_explorer_user():
"""Configure Windows Explorer for current user per setup_mode.""" """Configure Windows Explorer for current user."""
settings_explorer_user = { write_registry_settings(SETTINGS_EXPLORER_USER, all_users=False)
k: v for k, v in SETTINGS_EXPLORER_USER.items()
if setup_mode not in v.get('Invalid modes', [])
}
write_registry_settings(settings_explorer_user, all_users=False)
def config_windows_updates(): def config_windows_updates():
@ -80,9 +73,19 @@ def config_windows_updates():
write_registry_settings(SETTINGS_WINDOWS_UPDATES, all_users=True) write_registry_settings(SETTINGS_WINDOWS_UPDATES, all_users=True)
def disable_windows_telemetry():
"""Disable Windows 10 telemetry settings with O&O ShutUp10."""
extract_item('ShutUp10', silent=True)
cmd = [
r'{BinDir}\ShutUp10\OOSU10.exe'.format(**global_vars),
r'{BinDir}\ShutUp10\1201.cfg'.format(**global_vars),
'/quiet']
run_program(cmd)
def update_clock(): def update_clock():
"""Set Timezone and sync clock.""" """Set Timezone and sync clock."""
run_program(['tzutil', '/s', WINDOWS_TIME_ZONE], check=False) run_program(['tzutil' ,'/s', WINDOWS_TIME_ZONE], check=False)
run_program(['net', 'stop', 'w32ime'], check=False) run_program(['net', 'stop', 'w32ime'], check=False)
run_program( run_program(
['w32tm', '/config', '/syncfromflags:manual', ['w32tm', '/config', '/syncfromflags:manual',
@ -114,39 +117,6 @@ def write_registry_settings(settings, all_users=False):
# Installations # Installations
def find_current_software():
"""Find currently installed software, returns list."""
ninite_extras_path = r'{BaseDir}\Installers\Extras'.format(**global_vars)
installers = []
# Browsers
scan_for_browsers(silent=True)
for browser in ('Google Chrome', 'Mozilla Firefox', 'Opera Chromium'):
if is_installed(browser):
installers.append(
r'{}\Web Browsers\{}.exe'.format(ninite_extras_path, browser))
# TODO: Add more sections
return installers
def find_missing_software():
"""Find missing software based on dirs/files present, returns list."""
ninite_extras_path = r'{BaseDir}\Installers\Extras'.format(**global_vars)
installers = []
# Browsers
scan_for_browsers(silent=True)
for browser in ('Google Chrome', 'Mozilla Firefox', 'Opera Chromium'):
if profile_present(browser):
installers.append(
r'{}\Web Browsers\{}.exe'.format(ninite_extras_path, browser))
# TODO: Add more sections
return installers
def install_adobe_reader(): def install_adobe_reader():
"""Install Adobe Reader.""" """Install Adobe Reader."""
cmd = [ cmd = [
@ -199,115 +169,29 @@ def install_firefox_extensions():
run_program(cmd) run_program(cmd)
def install_libreoffice( def install_ninite_bundle(mse=False, libreoffice=False):
quickstart=True, register_mso_types=True,
use_mso_formats=False, vcredist=False):
"""Install LibreOffice using specified settings."""
cmd = [
'msiexec', '/passive', '/norestart',
'/i', r'{}\Installers\Extras\Office\LibreOffice.msi'.format(
global_vars['BaseDir']),
'REBOOTYESNO=No',
'ISCHECKFORPRODUCTUPDATES=0',
'QUICKSTART={}'.format(1 if quickstart else 0),
'UI_LANGS=en_US',
'VC_REDIST={}'.format(1 if vcredist else 0),
]
if register_mso_types:
cmd.append('REGISTER_ALL_MSO_TYPES=1')
else:
cmd.append('REGISTER_NO_MSO_TYPES=1')
xcu_dir = r'{APPDATA}\LibreOffice\4\user'.format(**global_vars['Env'])
xcu_file = r'{}\registrymodifications.xcu'.format(xcu_dir)
# Set default save format
if use_mso_formats and not os.path.exists(xcu_file):
os.makedirs(xcu_dir, exist_ok=True)
with open(xcu_file, 'w', encoding='utf-8', newline='\n') as f:
f.write(LIBREOFFICE_XCU_DATA)
# Install LibreOffice
run_program(cmd, check=True)
def install_ninite_bundle(
# pylint: disable=too-many-arguments,too-many-branches
base=True,
browsers_only=False,
libreoffice=False,
missing=False,
mse=False,
standard=True,
):
"""Run Ninite installer(s), returns list of Popen objects.""" """Run Ninite installer(s), returns list of Popen objects."""
popen_objects = [] popen_objects = []
if browsers_only: if global_vars['OS']['Version'] in ('8', '8.1', '10'):
# This option is deprecated # Modern selection
installer_path = r'{BaseDir}\Installers\Extras\Web Browsers'.format( popen_objects.append(
**global_vars) popen_program(r'{BaseDir}\Installers\Extras\Bundles\Modern.exe'.format(
scan_for_browsers(silent=True) **global_vars)))
for browser in ('Google Chrome', 'Mozilla Firefox', 'Opera Chromium'): else:
if is_installed(browser): # Legacy selection
cmd = r'{}\{}.exe'.format(installer_path, browser) if mse:
popen_objects.append(popen_program(cmd)) cmd = r'{BaseDir}\Installers\Extras\Security'.format(**global_vars)
cmd += r'\Microsoft Security Essentials.exe'
# Bail popen_objects.append(popen_program(cmd))
return popen_objects popen_objects.append(
popen_program(r'{BaseDir}\Installers\Extras\Bundles\Legacy.exe'.format(
# Main selections **global_vars)))
main_selections = []
if base:
main_selections.append('base')
if standard:
if global_vars['OS']['Version'] in ('8', '8.1', '10'):
main_selections.append('standard')
else:
main_selections.append('standard7')
if main_selections:
# Only run if base and/or standard are enabled
cmd = r'{}\Installers\Extras\Bundles\{}.exe'.format(
global_vars['BaseDir'],
'-'.join(main_selections),
)
popen_objects.append(popen_program([cmd]))
# Extra selections
extra_selections = {}
for cmd in find_current_software():
extra_selections[cmd] = True
if missing:
for cmd in find_missing_software():
extra_selections[cmd] = True
# Remove overlapping selections
regex = []
for n_name, n_group in NINITE_REGEX.items():
if n_name in main_selections:
regex.extend(n_group)
regex = '({})'.format('|'.join(regex))
extra_selections = {
cmd: True for cmd in extra_selections
if not re.search(regex, cmd, re.IGNORECASE)
}
# Start extra selections
for cmd in extra_selections:
popen_objects.append(popen_program([cmd]))
# Microsoft Security Essentials
if mse:
cmd = r'{}\Installers\Extras\Security\{}'.format(
global_vars['BaseDir'],
'Microsoft Security Essentials.exe',
)
popen_objects.append(popen_program([cmd]))
# LibreOffice # LibreOffice
if libreoffice: if libreoffice:
cmd = r'{}\Installers\Extras\Office\{}'.format( cmd = r'{BaseDir}\Installers\Extras\Office'.format(**global_vars)
global_vars['BaseDir'], cmd += r'\LibreOffice.exe'
'LibreOffice.exe', popen_objects.append(popen_program(cmd))
)
popen_objects.append(popen_program([cmd]))
# Done # Done
return popen_objects return popen_objects
@ -334,10 +218,6 @@ def open_device_manager():
popen_program(['mmc', 'devmgmt.msc']) popen_program(['mmc', 'devmgmt.msc'])
def open_speedtest():
popen_program(['start', '', 'https://fast.com'], shell=True)
def open_windows_activation(): def open_windows_activation():
popen_program(['slui']) popen_program(['slui'])

View file

@ -6,35 +6,6 @@ from functions.common import *
from settings.sw_diags import * from settings.sw_diags import *
def check_4k_alignment(show_alert=False):
"""Check that all partitions are 4K aligned."""
aligned = True
cmd = ['WMIC', 'partition', 'get', 'StartingOffset']
offsets = []
# Get offsets
result = run_program(cmd, encoding='utf-8', errors='ignore', check=False)
offsets = result.stdout.splitlines()
# Check offsets
for off in offsets:
off = off.strip()
if not off.isnumeric():
# Skip
continue
try:
aligned = aligned and int(off) % 4096 == 0
except ValueError:
# Ignore, this check is low priority
pass
# Show alert
if show_alert:
show_alert_box('One or more partitions are not 4K aligned')
raise Not4KAlignedError
def check_connection(): def check_connection():
"""Check if the system is online and optionally abort the script.""" """Check if the system is online and optionally abort the script."""
while True: while True:
@ -48,37 +19,6 @@ def check_connection():
abort() abort()
def check_os_support_status():
"""Check if current OS is supported."""
msg = ''
outdated = False
unsupported = False
# Check OS version/notes
os_info = global_vars['OS'].copy()
if os_info['Notes'] == 'unsupported':
msg = 'The installed version of Windows is no longer supported'
unsupported = True
elif os_info['Notes'] == 'preview build':
msg = 'Preview builds are not officially supported'
unsupported = True
elif os_info['Version'] == '10' and os_info['Notes'] == 'outdated':
msg = 'The installed version of Windows is outdated'
outdated = True
if 'Preview' not in msg:
msg += '\n\nPlease consider upgrading before continuing setup.'
# Show alert
if outdated or unsupported:
show_alert_box(msg)
# Raise exception if necessary
if outdated:
raise WindowsOutdatedError
if unsupported:
raise WindowsUnsupportedError
def check_secure_boot_status(show_alert=False): def check_secure_boot_status(show_alert=False):
"""Checks UEFI Secure Boot status via PowerShell.""" """Checks UEFI Secure Boot status via PowerShell."""
boot_mode = get_boot_mode() boot_mode = get_boot_mode()
@ -141,6 +81,33 @@ def get_boot_mode():
return type_str return type_str
def os_is_unsupported(show_alert=False):
"""Checks if the current OS is unsupported, returns bool."""
msg = ''
unsupported = False
# Check OS version/notes
os_info = global_vars['OS'].copy()
if os_info['Notes'] == 'unsupported':
msg = 'The installed version of Windows is no longer supported'
unsupported = True
elif os_info['Notes'] == 'preview build':
msg = 'Preview builds are not officially supported'
unsupported = True
elif os_info['Version'] == '10' and os_info['Notes'] == 'outdated':
msg = 'The installed version of Windows is outdated'
unsupported = True
if 'Preview' not in msg:
msg += '\n\nPlease consider upgrading before continuing setup.'
# Show alert
if unsupported and show_alert:
show_alert_box(msg)
# Done
return unsupported
def run_autoruns(): def run_autoruns():
"""Run AutoRuns in the background with VirusTotal checks enabled.""" """Run AutoRuns in the background with VirusTotal checks enabled."""
extract_item('Autoruns', filter='autoruns*', silent=True) extract_item('Autoruns', filter='autoruns*', silent=True)
@ -230,10 +197,8 @@ def run_rkill():
shutil.move(item.path, dest) shutil.move(item.path, dest)
def show_alert_box(message, title=None): def show_alert_box(message, title='Wizard Kit Warning'):
"""Show Windows alert box with message.""" """Show Windows alert box with message."""
if not title:
title = '{} Warning'.format(KIT_NAME_FULL)
message_box = ctypes.windll.user32.MessageBoxW message_box = ctypes.windll.user32.MessageBoxW
message_box(None, message, title, 0x00001030) message_box(None, message, title, 0x00001030)

View file

@ -141,18 +141,6 @@ def tmux_split_window(
return result.stdout.decode().strip() return result.stdout.decode().strip()
def tmux_switch_client(target_session=None):
"""Switch to target tmux session, or previous if none specified."""
cmd = ['tmux', 'switch-client']
if target_session:
cmd.extend(['-t', target_session])
else:
# Switch to previous instead
cmd.append('-p')
run_program(cmd, check=False)
def tmux_update_pane( def tmux_update_pane(
pane_id, command=None, working_dir=None, pane_id, command=None, working_dir=None,
text=None, watch=None, watch_cmd='cat'): text=None, watch=None, watch_cmd='cat'):

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

@ -615,22 +615,6 @@ def update_adobe_reader_dc():
dest, 'Adobe Reader DC.exe', SOURCE_URLS['Adobe Reader DC']) dest, 'Adobe Reader DC.exe', SOURCE_URLS['Adobe Reader DC'])
def update_libreoffice():
# Prep
dest = r'{}\Installers\Extras\Office'.format(
global_vars['BaseDir'])
# Remove existing installer
try:
os.remove(r'{}\LibreOffice.msi'.format(dest))
except FileNotFoundError:
pass
# Download
download_generic(
dest, 'LibreOffice.msi', SOURCE_URLS['LibreOffice'])
def update_macs_fan_control(): def update_macs_fan_control():
# Prep # Prep
dest = r'{}\Installers'.format( dest = r'{}\Installers'.format(

View file

@ -1,143 +0,0 @@
# Wizard Kit: Functions - Windows updates
from functions.common import *
# Functions
def delete_folder(folder_path):
"""Near-useless wrapper for shutil.rmtree."""
shutil.rmtree(folder_path)
def disable_service(service_name):
"""Set service startup to disabled."""
run_program(['sc', 'config', service_name, 'start=', 'disabled'])
# Verify service was disabled
start_type = get_service_start_type(service_name)
if not start_type.lower().startswith('disabled'):
raise GenericError('Failed to disable service {}'.format(service_name))
def disable_windows_updates():
"""Disable windows updates and clear SoftwareDistribution folder."""
indent=2
width=52
update_folders = [
r'{WINDIR}\SoftwareDistribution'.format(**global_vars['Env']),
r'{SYSTEMDRIVE}\$WINDOWS.~BT'.format(**global_vars['Env']),
]
for service in ('wuauserv', 'bits'):
# Stop service
result = try_and_print(
'Stopping service {}...'.format(service),
indent=indent, width=width,
function=stop_service, service_name=service)
if not result['CS']:
result = try_and_print(
'Stopping service {}...'.format(service),
indent=indent, width=width,
function=stop_service, service_name=service)
if not result['CS']:
raise GenericError('Service {} could not be stopped.'.format(service))
# Disable service
result = try_and_print(
'Disabling service {}...'.format(service),
indent=indent, width=width,
function=disable_service, service_name=service)
if not result['CS']:
result = try_and_print(
'Disabling service {}...'.format(service),
indent=indent, width=width,
function=disable_service, service_name=service)
if not result['CS']:
raise GenericError('Service {} could not be disabled.'.format(service))
# Delete update folders
for folder_path in update_folders:
if os.path.exists(folder_path):
result = try_and_print(
'Deleting folder {}...'.format(folder_path),
indent=indent, width=width,
function=delete_folder, folder_path=folder_path)
if not result['CS']:
raise GenericError('Failed to remove folder {}'.format(folder_path))
def enable_service(service_name, start_type='auto'):
"""Enable service by setting start type."""
run_program(['sc', 'config', service_name, 'start=', start_type])
def enable_windows_updates(silent=False):
"""Enable windows updates"""
indent=2
width=52
for service in ('bits', 'wuauserv'):
# Enable service
start_type = 'auto'
if service == 'wuauserv':
start_type = 'demand'
if silent:
try:
enable_service(service, start_type=start_type)
except Exception:
# Try again
enable_service(service, start_type=start_type)
else:
result = try_and_print(
'Enabling service {}...'.format(service),
indent=indent, width=width,
function=enable_service, service_name=service, start_type=start_type)
if not result['CS']:
result = try_and_print(
'Enabling service {}...'.format(service),
indent=indent, width=width,
function=enable_service, service_name=service, start_type=start_type)
if not result['CS']:
raise GenericError('Service {} could not be enabled.'.format(service))
def get_service_status(service_name):
"""Get service status using psutil, returns str."""
status = 'Unknown'
try:
service = psutil.win_service_get(service_name)
status = service.status()
except psutil.NoSuchProcess:
# Ignore and return 'Unknown' below
pass
return status
def get_service_start_type(service_name):
"""Get service startup type using psutil, returns str."""
start_type = 'Unknown'
try:
service = psutil.win_service_get(service_name)
start_type = service.start_type()
except psutil.NoSuchProcess:
# Ignore and return 'Unknown' below
pass
return start_type
def stop_service(service_name):
"""Stop service."""
run_program(['net', 'stop', service_name], check=False)
# Verify service was stopped
status = get_service_status(service_name)
if not status.lower().startswith('stopped'):
raise GenericError('Failed to stop service {}'.format(service_name))
if __name__ == '__main__':
print("This file is not meant to be called directly.")
# vim: sts=2 sw=2 ts=2

View file

@ -1,11 +1,46 @@
#!/bin/bash #!/bin/bash
# #
## Wizard Kit: HW Diagnostics Launcher ## Wizard Kit: HW Diagnostics - Menu Launcher
source launch-in-tmux
SESSION_NAME="hw-diags" SESSION_NAME="hw-diags"
WINDOW_NAME="Hardware Diagnostics" WINDOW_NAME="Hardware Diagnostics"
TMUX_CMD="hw-diags-menu" MENU="hw-diags-menu"
function ask() {
while :; do
read -p "$1 [Y/N] " -r answer
if echo "$answer" | egrep -iq '^(y|yes|sure)$'; then
return 0
elif echo "$answer" | egrep -iq '^(n|no|nope)$'; then
return 1
fi
done
}
die () {
echo "$0:" "$@" >&2
exit 1
}
# Check for running session
if tmux list-session | grep -q "$SESSION_NAME"; then
echo "WARNING: tmux session $SESSION_NAME already exists."
echo ""
if ask "Connect to current session?"; then
# Do nothing, the command below will attach/connect
echo ""
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
fi
fi
# Start session
tmux new-session -A -s "$SESSION_NAME" -n "$WINDOW_NAME" "$MENU" $*
launch_in_tmux "$@"

View file

@ -34,8 +34,8 @@ if __name__ == '__main__':
#print_standard('\nDone.') #print_standard('\nDone.')
#pause("Press Enter to exit...") #pause("Press Enter to exit...")
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -22,9 +22,9 @@ if __name__ == '__main__':
print_standard(' ') print_standard(' ')
sleep(1) sleep(1)
pause('Press Enter to exit...') pause('Press Enter to exit...')
except SystemExit as sys_exit: except SystemExit:
tmux_switch_client() # Normal exit
exit_script(sys_exit.code) pass
except: except:
# Cleanup # Cleanup
tmux_kill_all_panes() tmux_kill_all_panes()
@ -50,7 +50,7 @@ if __name__ == '__main__':
global_vars=global_vars) global_vars=global_vars)
# Done # Done
sleep(1) sleep(10)
pause('Press Enter to exit...') pause('Press Enter to exit...')
exit_script(1) exit_script(1)
@ -60,7 +60,6 @@ if __name__ == '__main__':
# Done # Done
tmux_kill_all_panes() tmux_kill_all_panes()
tmux_switch_client()
exit_script() exit_script()
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2

View file

@ -40,8 +40,8 @@ if __name__ == '__main__':
print_standard('\nDone.') print_standard('\nDone.')
#pause("Press Enter to exit...") #pause("Press Enter to exit...")
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -3,9 +3,9 @@
BLUE='\033[34m' BLUE='\033[34m'
CLEAR='\033[0m' CLEAR='\033[0m'
IFS=$'\n'
# List devices # List devices
IFS=$'\n'
for line in $(lsblk -do NAME,TRAN,SIZE,VENDOR,MODEL,SERIAL); do for line in $(lsblk -do NAME,TRAN,SIZE,VENDOR,MODEL,SERIAL); do
if [[ "${line:0:4}" == "NAME" ]]; then if [[ "${line:0:4}" == "NAME" ]]; then
echo -e "${BLUE}${line}${CLEAR}" echo -e "${BLUE}${line}${CLEAR}"
@ -15,18 +15,6 @@ for line in $(lsblk -do NAME,TRAN,SIZE,VENDOR,MODEL,SERIAL); do
done done
echo "" 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 # List partitions
for line in $(lsblk -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT); do for line in $(lsblk -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT); do
if [[ "${line:0:4}" == "NAME" ]]; then if [[ "${line:0:4}" == "NAME" ]]; then

View file

@ -28,8 +28,8 @@ if __name__ == '__main__':
run_program(cmd, check=False) run_program(cmd, check=False)
monitor_sensors(monitor_pane, monitor_file) monitor_sensors(monitor_pane, monitor_file)
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -25,6 +25,7 @@ if __name__ == '__main__':
'UnsupportedOSError': 'Unsupported OS', 'UnsupportedOSError': 'Unsupported OS',
}} }}
answer_extensions = ask('Install Extensions?') answer_extensions = ask('Install Extensions?')
answer_adobe_reader = ask('Install Adobe Reader?')
answer_vcr = ask('Install Visual C++ Runtimes?') answer_vcr = ask('Install Visual C++ Runtimes?')
answer_ninite = ask('Install Ninite Bundle?') answer_ninite = ask('Install Ninite Bundle?')
if answer_ninite and global_vars['OS']['Version'] in ['7']: if answer_ninite and global_vars['OS']['Version'] in ['7']:
@ -34,6 +35,9 @@ if __name__ == '__main__':
answer_mse = False answer_mse = False
print_info('Installing Programs') print_info('Installing Programs')
if answer_adobe_reader:
try_and_print(message='Adobe Reader DC...',
function=install_adobe_reader, other_results=other_results)
if answer_vcr: if answer_vcr:
install_vcredists() install_vcredists()
if answer_ninite: if answer_ninite:
@ -55,8 +59,8 @@ if __name__ == '__main__':
other_results=other_results) other_results=other_results)
print_standard('\nDone.') print_standard('\nDone.')
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -27,8 +27,8 @@ if __name__ == '__main__':
print_standard('\nDone.') print_standard('\nDone.')
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -1,64 +0,0 @@
#!/bin/bash
#
## Wizard Kit: TMUX Launcher
function ask() {
while :; do
read -p "$1 [Y/N] " -r answer
if echo "$answer" | grep -Eiq '^(y|yes|sure)$'; then
return 0
elif echo "$answer" | grep -Eiq '^(n|no|nope)$'; then
return 1
fi
done
}
die () {
echo "$0:" "$@" >&2
exit 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)"
# Check for running session
if tmux list-session | grep -q "$SESSION_NAME"; then
echo "WARNING: tmux session $SESSION_NAME already exists."
echo ""
if ask "Connect to current session?"; then
if [[ -n "${TMUX:-}" ]]; then
# Running inside TMUX, switch to session
tmux switch-client -t "$SESSION_NAME"
else
# Running outside TMUX, attach to session
tmux attach-session -t "$SESSION_NAME"
fi
exit 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
fi
fi
# Start/Rename session
if [[ -n "${TMUX:-}" ]]; then
# Running inside TMUX, rename session/window and open the menu
tmux rename-session "$SESSION_NAME"
tmux rename-window "$WINDOW_NAME"
"$TMUX_CMD" "$@"
tmux rename-session "${SESSION_NAME}_DONE"
tmux rename-window "${WINDOW_NAME}_DONE"
else
# Running outside TMUX, start/attach to session
tmux new-session -s "$SESSION_NAME" -n "$WINDOW_NAME" "$TMUX_CMD" "$@"
fi
}

View file

@ -30,8 +30,8 @@ if __name__ == '__main__':
pause("Press Enter to exit...") pause("Press Enter to exit...")
popen_program(['nohup', 'thunar', '/media'], pipe=True) popen_program(['nohup', 'thunar', '/media'], pipe=True)
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -16,6 +16,9 @@ if __name__ == '__main__':
# Prep # Prep
clear_screen() clear_screen()
# Connect
connect_to_network()
# Mount # Mount
if is_connected(): if is_connected():
mount_backup_shares(read_write=True) mount_backup_shares(read_write=True)
@ -27,8 +30,8 @@ if __name__ == '__main__':
print_standard('\nDone.') print_standard('\nDone.')
#pause("Press Enter to exit...") #pause("Press Enter to exit...")
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -76,8 +76,8 @@ if __name__ == '__main__':
print_standard('\nDone.') print_standard('\nDone.')
#pause("Press Enter to exit...") #pause("Press Enter to exit...")
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -0,0 +1,163 @@
# Wizard Kit: New system setup
import os
import sys
# Init
os.chdir(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(os.getcwd())
from functions.activation import *
from functions.browsers import *
from functions.cleanup import *
from functions.info import *
from functions.product_keys import *
from functions.setup import *
from functions.sw_diags import *
init_global_vars()
os.system('title {}: New System Setup'.format(KIT_NAME_FULL))
set_log_file('New System Setup.log')
if __name__ == '__main__':
other_results = {
'Error': {
'BIOSKeyNotFoundError': 'BIOS key not found',
'CalledProcessError': 'Unknown Error',
'FileNotFoundError': 'File not found',
'GenericError': 'Unknown Error',
'SecureBootDisabledError': 'Disabled',
},
'Warning': {
'GenericRepair': 'Repaired',
'NoProfilesError': 'No profiles found',
'NotInstalledError': 'Not installed',
'OSInstalledLegacyError': 'OS installed Legacy',
'SecureBootNotAvailError': 'Not available',
'SecureBootUnknownError': 'Unknown',
'UnsupportedOSError': 'Unsupported OS',
}}
try:
stay_awake()
clear_screen()
# Check installed OS
if os_is_unsupported(show_alert=False):
print_warning('OS version not supported by this script')
if not ask('Continue anyway? (NOT RECOMMENDED)'):
abort()
# Install Adobe Reader?
answer_adobe_reader = ask('Install Adobe Reader?')
# Install LibreOffice?
answer_libreoffice = ask('Install LibreOffice?')
# Install MSE?
if global_vars['OS']['Version'] == '7':
answer_mse = ask('Install MSE?')
else:
answer_mse = False
# Install software
print_info('Installing Programs')
install_vcredists()
if answer_adobe_reader:
try_and_print(message='Adobe Reader DC...',
function=install_adobe_reader, other_results=other_results)
result = try_and_print(
message='Ninite bundle...',
function=install_ninite_bundle, cs='Started',
mse=answer_mse, libreoffice=answer_libreoffice,
other_results=other_results)
for proc in result['Out']:
# Wait for all processes to finish
proc.wait()
# Scan for supported browsers
print_info('Scanning for browsers')
scan_for_browsers()
# Install extensions
print_info('Installing Extensions')
try_and_print(message='Classic Shell skin...',
function=install_classicstart_skin,
other_results=other_results)
try_and_print(message='Google Chrome extensions...',
function=install_chrome_extensions)
try_and_print(message='Mozilla Firefox extensions...',
function=install_firefox_extensions,
other_results=other_results)
# Configure software
print_info('Configuring programs')
install_adblock()
if global_vars['OS']['Version'] == '10':
try_and_print(message='ClassicStart...',
function=config_classicstart, cs='Done')
try_and_print(message='Explorer (user)...',
function=config_explorer_user, cs='Done')
# Configure system
print_info('Configuring system')
if global_vars['OS']['Version'] == '10':
try_and_print(message='Explorer (system)...',
function=config_explorer_system, cs='Done')
try_and_print(message='Disabling telemetry...',
function=disable_windows_telemetry, cs='Done')
try_and_print(message='Windows Updates...',
function=config_windows_updates, cs='Done')
try_and_print(message='Updating Clock...',
function=update_clock, cs='Done')
# Restart Explorer
try_and_print(message='Restarting Explorer...',
function=restart_explorer, cs='Done')
# Summary
print_info('Summary')
try_and_print(message='Operating System:',
function=show_os_name, ns='Unknown', silent_function=False)
try_and_print(message='Activation:',
function=show_os_activation, ns='Unknown', silent_function=False)
if (not windows_is_activated()
and global_vars['OS']['Version'] in ('8', '8.1', '10')):
try_and_print(message='BIOS Activation:',
function=activate_with_bios,
other_results=other_results)
try_and_print(message='Secure Boot Status:',
function=check_secure_boot_status, other_results=other_results)
try_and_print(message='Installed RAM:',
function=show_installed_ram, ns='Unknown', silent_function=False)
show_free_space()
try_and_print(message='Installed Antivirus:',
function=get_installed_antivirus, ns='Unknown',
other_results=other_results, print_return=True)
# Play audio, show devices, open Windows updates, and open Activation
try_and_print(message='Opening Device Manager...',
function=open_device_manager, cs='Started')
try_and_print(message='Opening HWiNFO (Sensors)...',
function=run_hwinfo_sensors, cs='Started', other_results=other_results)
try_and_print(message='Opening Windows Updates...',
function=open_windows_updates, cs='Started')
if not windows_is_activated():
try_and_print(message='Opening Windows Activation...',
function=open_windows_activation, cs='Started')
sleep(3)
try_and_print(message='Running XMPlay...',
function=run_xmplay, cs='Started', other_results=other_results)
try:
check_secure_boot_status(show_alert=True)
except:
# Only trying to open alert message boxes
pass
# Done
print_standard('\nDone.')
pause('Press Enter to exit...')
exit_script()
except SystemExit:
pass
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -31,8 +31,8 @@ if __name__ == '__main__':
pause('Press Enter to reboot...') pause('Press Enter to reboot...')
reboot() reboot()
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -31,8 +31,8 @@ if __name__ == '__main__':
pause('Press Enter to reboot...') pause('Press Enter to reboot...')
reboot() reboot()
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -1,37 +0,0 @@
'''Wizard Kit: Settings - Cleanup'''
# vim: sts=2 sw=2 ts=2
import re
# Regex
DESKTOP_ITEMS = re.compile(
r'^(JRT|RKill|sc-cleaner)',
re.IGNORECASE,
)
# Registry
UAC_DEFAULTS_WIN7 = {
r'Software\Microsoft\Windows\CurrentVersion\Policies\System': {
'DWORD Items': {
'ConsentPromptBehaviorAdmin': 5,
'EnableLUA': 1,
'PromptOnSecureDesktop': 1,
},
},
}
UAC_DEFAULTS_WIN10 = {
r'Software\Microsoft\Windows\CurrentVersion\Policies\System': {
'DWORD Items': {
'ConsentPromptBehaviorAdmin': 5,
'ConsentPromptBehaviorUser': 3,
'EnableInstallerDetection': 1,
'EnableLUA': 1,
'EnableVirtualization': 1,
'PromptOnSecureDesktop': 1,
},
},
}
if __name__ == '__main__':
print("This file is not meant to be called directly.")

View file

@ -5,9 +5,7 @@ import re
from collections import OrderedDict from collections import OrderedDict
# General # General
MAP_DIR = '/Backups/ddrescue-tui'
RECOMMENDED_FSTYPES = ['ext3', 'ext4', 'xfs'] RECOMMENDED_FSTYPES = ['ext3', 'ext4', 'xfs']
RECOMMENDED_MAP_FSTYPES = ['cifs', 'ext2', 'ext3', 'ext4', 'vfat', 'xfs']
USAGE = """ {script_name} clone [source [destination]] USAGE = """ {script_name} clone [source [destination]]
{script_name} image [source [destination]] {script_name} image [source [destination]]
(e.g. {script_name} clone /dev/sda /dev/sdb) (e.g. {script_name} clone /dev/sda /dev/sdb)
@ -38,12 +36,6 @@ DDRESCUE_SETTINGS = {
'-vvvv': {'Enabled': True, 'Hidden': True, }, '-vvvv': {'Enabled': True, 'Hidden': True, },
} }
ETOC_REFRESH_RATE = 30 # in seconds ETOC_REFRESH_RATE = 30 # in seconds
REGEX_DDRESCUE_LOG = re.compile(
r'^\s*(?P<key>\S+):\s+'
r'(?P<size>\d+)\s+'
r'(?P<unit>[PTGMKB])i?B?',
re.IGNORECASE,
)
REGEX_REMAINING_TIME = re.compile( REGEX_REMAINING_TIME = re.compile(
r'remaining time:' r'remaining time:'
r'\s*((?P<days>\d+)d)?' r'\s*((?P<days>\d+)d)?'

View file

@ -1,20 +1,35 @@
'''Wizard Kit: Settings - Launchers''' # Wizard Kit: Settings - Launchers
# pylint: disable=line-too-long
# vim: sts=2 sw=2 ts=2
LAUNCHERS = { LAUNCHERS = {
r'(Root)': { r'(Root)': {
'Activate Windows': {
'L_TYPE': 'PyScript',
'L_PATH': 'Scripts',
'L_ITEM': 'activate.py',
'L_ELEV': 'True',
},
'New System Setup': {
'L_TYPE': 'PyScript',
'L_PATH': 'Scripts',
'L_ITEM': 'new_system_setup.py',
'L_ELEV': 'True',
},
'System Checklist': {
'L_TYPE': 'PyScript',
'L_PATH': 'Scripts',
'L_ITEM': 'system_checklist.py',
'L_ELEV': 'True',
},
'System Diagnostics': { 'System Diagnostics': {
'L_TYPE': 'PyScript', 'L_TYPE': 'PyScript',
'L_PATH': 'Scripts', 'L_PATH': 'Scripts',
'L_ITEM': 'system_diagnostics.py', 'L_ITEM': 'system_diagnostics.py',
'L_ELEV': 'True', 'L_ELEV': 'True',
}, },
'System Setup': { 'User Checklist': {
'L_TYPE': 'PyScript', 'L_TYPE': 'PyScript',
'L_PATH': 'Scripts', 'L_PATH': 'Scripts',
'L_ITEM': 'system_setup.py', 'L_ITEM': 'user_checklist.py',
'L_ELEV': 'True',
}, },
}, },
r'Data Recovery': { r'Data Recovery': {
@ -40,7 +55,6 @@ LAUNCHERS = {
}, },
}, },
r'Data Transfers': { r'Data Transfers': {
# pylint: disable=bad-continuation
'FastCopy (as ADMIN)': { 'FastCopy (as ADMIN)': {
'L_TYPE': 'Executable', 'L_TYPE': 'Executable',
'L_PATH': 'FastCopy', 'L_PATH': 'FastCopy',
@ -243,7 +257,7 @@ LAUNCHERS = {
'L_TYPE': 'Executable', 'L_TYPE': 'Executable',
'L_PATH': 'erunt', 'L_PATH': 'erunt',
'L_ITEM': 'ERUNT.EXE', 'L_ITEM': 'ERUNT.EXE',
'L_ARGS': r'%client_dir%\Backups\Registry\%iso_date% sysreg curuser otherusers', 'L_ARGS': '%client_dir%\Backups\Registry\%iso_date% sysreg curuser otherusers',
'L_ELEV': 'True', 'L_ELEV': 'True',
'Extra Code': [ 'Extra Code': [
r'call "%bin%\Scripts\init_client_dir.cmd" /Logs', r'call "%bin%\Scripts\init_client_dir.cmd" /Logs',
@ -273,13 +287,13 @@ LAUNCHERS = {
r'Drivers': { r'Drivers': {
'Intel RST (Current Release)': { 'Intel RST (Current Release)': {
'L_TYPE': 'Executable', 'L_TYPE': 'Executable',
'L_PATH': r'_Drivers\Intel RST', 'L_PATH': '_Drivers\Intel RST',
'L_ITEM': 'SetupRST_17.2.exe', 'L_ITEM': 'SetupRST_17.2.exe',
'L_7ZIP': 'SetupRST_17.2.exe', 'L_7ZIP': 'SetupRST_17.2.exe',
}, },
'Intel RST (Previous Releases)': { 'Intel RST (Previous Releases)': {
'L_TYPE': 'Folder', 'L_TYPE': 'Folder',
'L_PATH': r'_Drivers\Intel RST', 'L_PATH': '_Drivers\Intel RST',
'L_ITEM': '.', 'L_ITEM': '.',
'L_NCMD': 'True', 'L_NCMD': 'True',
}, },
@ -295,7 +309,7 @@ LAUNCHERS = {
}, },
'Snappy Driver Installer Origin': { 'Snappy Driver Installer Origin': {
'L_TYPE': 'Executable', 'L_TYPE': 'Executable',
'L_PATH': r'_Drivers\SDIO', 'L_PATH': '_Drivers\SDIO',
'L_ITEM': 'SDIO.exe', 'L_ITEM': 'SDIO.exe',
}, },
}, },
@ -421,12 +435,6 @@ LAUNCHERS = {
}, },
}, },
r'Misc': { r'Misc': {
'Activate Windows': {
'L_TYPE': 'PyScript',
'L_PATH': 'Scripts',
'L_ITEM': 'activate.py',
'L_ELEV': 'True',
},
'Cleanup CBS Temp Files': { 'Cleanup CBS Temp Files': {
'L_TYPE': 'PyScript', 'L_TYPE': 'PyScript',
'L_PATH': 'Scripts', 'L_PATH': 'Scripts',
@ -444,20 +452,6 @@ LAUNCHERS = {
'L_PATH': 'ConEmu', 'L_PATH': 'ConEmu',
'L_ITEM': 'ConEmu.exe', 'L_ITEM': 'ConEmu.exe',
}, },
'Disable Windows Updates': {
'L_TYPE': 'PyScript',
'L_PATH': 'Scripts',
'L_ITEM': 'windows_updates.py',
'L_ARGS': '--disable',
'L_ELEV': 'True',
},
'Enable Windows Updates': {
'L_TYPE': 'PyScript',
'L_PATH': 'Scripts',
'L_ITEM': 'windows_updates.py',
'L_ARGS': '--enable',
'L_ELEV': 'True',
},
'Enter SafeMode': { 'Enter SafeMode': {
'L_TYPE': 'PyScript', 'L_TYPE': 'PyScript',
'L_PATH': 'Scripts', 'L_PATH': 'Scripts',
@ -497,7 +491,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.7z"', 'L_ARGS': '"%bin%\XMPlay\music.7z"',
}, },
}, },
r'Repairs': { r'Repairs': {
@ -557,7 +551,7 @@ LAUNCHERS = {
'L_TYPE': 'Executable', 'L_TYPE': 'Executable',
'L_PATH': 'RKill', 'L_PATH': 'RKill',
'L_ITEM': 'RKill.exe', 'L_ITEM': 'RKill.exe',
'L_ARGS': r'-s -l %log_dir%\Tools\RKill.log', 'L_ARGS': '-s -l %log_dir%\Tools\RKill.log',
'L_ELEV': 'True', 'L_ELEV': 'True',
'Extra Code': [ 'Extra Code': [
r'call "%bin%\Scripts\init_client_dir.cmd" /Logs', r'call "%bin%\Scripts\init_client_dir.cmd" /Logs',
@ -600,3 +594,5 @@ LAUNCHERS = {
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.")
# vim: sts=2 sw=2 ts=2

View file

@ -21,6 +21,9 @@ QUICKBOOKS_SERVER_IP='10.0.0.10'
# Time Zones # Time Zones
LINUX_TIME_ZONE='America/Denver' # See 'timedatectl list-timezones' for valid values LINUX_TIME_ZONE='America/Denver' # See 'timedatectl list-timezones' for valid values
WINDOWS_TIME_ZONE='Mountain Standard Time' # See 'tzutil /l' for valid values WINDOWS_TIME_ZONE='Mountain Standard Time' # See 'tzutil /l' for valid values
# WiFi
WIFI_SSID='SomeWiFi'
WIFI_PASSWORD='Abracadabra'
# SERVER VARIABLES # SERVER VARIABLES
## NOTE: Windows can only use one user per server. This means that if ## NOTE: Windows can only use one user per server. This means that if

View file

@ -1,19 +1,13 @@
'''Wizard Kit: Settings - Setup''' # Wizard Kit: Settings - Setup
# pylint: disable=bad-continuation,line-too-long
# vim: sts=2 sw=2 ts=2
import os import os
try: import winreg
import winreg
HKU = winreg.HKEY_USERS
HKCR = winreg.HKEY_CLASSES_ROOT
HKCU = winreg.HKEY_CURRENT_USER
HKLM = winreg.HKEY_LOCAL_MACHINE
except ImportError:
if os.name != 'posix':
raise
# General # General
HKU = winreg.HKEY_USERS
HKCR = winreg.HKEY_CLASSES_ROOT
HKCU = winreg.HKEY_CURRENT_USER
HKLM = winreg.HKEY_LOCAL_MACHINE
OTHER_RESULTS = { OTHER_RESULTS = {
'Error': { 'Error': {
'CalledProcessError': 'Unknown Error', 'CalledProcessError': 'Unknown Error',
@ -98,15 +92,6 @@ SETTINGS_EXPLORER_SYSTEM = {
}, },
} }
SETTINGS_EXPLORER_USER = { SETTINGS_EXPLORER_USER = {
# Desktop theme
r'Software\Microsoft\Windows\CurrentVersion\Themes\Personalize': {
'Invalid modes': ['Cur'],
'DWORD Items': {
# <= v1809 default
'AppsUseLightTheme': 1,
'SystemUsesLightTheme': 0,
},
},
# Disable features # Disable features
r'Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager': { r'Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager': {
'DWORD Items': { 'DWORD Items': {
@ -119,41 +104,21 @@ SETTINGS_EXPLORER_USER = {
}, },
# File Explorer # File Explorer
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced': { r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced': {
'Invalid modes': ['Cur'],
'DWORD Items': { 'DWORD Items': {
# Change default Explorer view to "Computer" # Change default Explorer view to "Computer"
'LaunchTo': 1, 'LaunchTo': 1,
}, },
}, },
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced': {
# Dup path so it Will be applied to all modes
'DWORD Items': {
# Launch Folder Windows in a Separate Process
'SeparateProcess': 1,
},
},
# Hide People bar # Hide People bar
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced\People': { r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced\People': {
'Invalid modes': ['Cur'],
'DWORD Items': {'PeopleBand': 0}, 'DWORD Items': {'PeopleBand': 0},
}, },
# Hide Search button / box # Hide Search button / box
r'Software\Microsoft\Windows\CurrentVersion\Search': { r'Software\Microsoft\Windows\CurrentVersion\Search': {
'Invalid modes': ['Cur'],
'DWORD Items': {'SearchboxTaskbarMode': 0}, 'DWORD Items': {'SearchboxTaskbarMode': 0},
}, },
} }
# LibreOffice
LIBREOFFICE_XCU_DATA = '''<?xml version="1.0" encoding="UTF-8"?>
<oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item oor:path="/org.openoffice.Setup/Office/Factories/org.openoffice.Setup:Factory['com.sun.star.presentation.PresentationDocument']"><prop oor:name="ooSetupFactoryDefaultFilter" oor:op="fuse"><value>Impress MS PowerPoint 2007 XML</value></prop></item>
<item oor:path="/org.openoffice.Setup/Office/Factories/org.openoffice.Setup:Factory['com.sun.star.sheet.SpreadsheetDocument']"><prop oor:name="ooSetupFactoryDefaultFilter" oor:op="fuse"><value>Calc MS Excel 2007 XML</value></prop></item>
<item oor:path="/org.openoffice.Setup/Office/Factories/org.openoffice.Setup:Factory['com.sun.star.text.TextDocument']"><prop oor:name="ooSetupFactoryDefaultFilter" oor:op="fuse"><value>MS Word 2007 XML</value></prop></item>
<item oor:path="/org.openoffice.Office.Common/Save/Document"><prop oor:name="WarnAlienFormat" oor:op="fuse"><value>false</value></prop></item>
</oor:items>
'''
# Visual C++ Runtimes # Visual C++ Runtimes
VCR_REDISTS = [ VCR_REDISTS = [
{'Name': 'Visual C++ 2010 x32...', {'Name': 'Visual C++ 2010 x32...',
@ -192,3 +157,5 @@ SETTINGS_WINDOWS_UPDATES = {
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.")
# vim: sts=2 sw=2 ts=2

View file

@ -1,6 +1,4 @@
'''Wizard Kit: Settings - Sources''' # Wizard Kit: Settings - Sources
# pylint: disable=line-too-long
# vim: sts=2 sw=2 ts=2 tw=0
SOURCE_URLS = { SOURCE_URLS = {
'Adobe Reader DC': 'http://ardownload.adobe.com/pub/adobe/reader/win/AcrobatDC/1901020098/AcroRdrDC1901020098_en_US.exe', 'Adobe Reader DC': 'http://ardownload.adobe.com/pub/adobe/reader/win/AcrobatDC/1901020098/AcroRdrDC1901020098_en_US.exe',
@ -17,7 +15,7 @@ SOURCE_URLS = {
'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.935.x86.en-US.zip', 'Everything32': 'https://www.voidtools.com/Everything-1.4.1.935.x86.en-US.zip',
'Everything64': 'https://www.voidtools.com/Everything-1.4.1.935.x64.en-US.zip', 'Everything64': 'https://www.voidtools.com/Everything-1.4.1.935.x64.en-US.zip',
'FastCopy': 'https://fastcopy.jp/archive/FastCopy380_installer.exe', 'FastCopy': 'http://ftp.vector.co.jp/71/31/2323/FastCopy363_installer.exe',
'Firefox uBO': 'https://addons.mozilla.org/firefox/downloads/file/1709472/ublock_origin-1.18.6-an+fx.xpi', 'Firefox uBO': 'https://addons.mozilla.org/firefox/downloads/file/1709472/ublock_origin-1.18.6-an+fx.xpi',
'HitmanPro32': 'https://dl.surfright.nl/HitmanPro.exe', 'HitmanPro32': 'https://dl.surfright.nl/HitmanPro.exe',
'HitmanPro64': 'https://dl.surfright.nl/HitmanPro_x64.exe', 'HitmanPro64': 'https://dl.surfright.nl/HitmanPro_x64.exe',
@ -25,7 +23,6 @@ SOURCE_URLS = {
'Intel SSD Toolbox': r'https://downloadmirror.intel.com/28593/eng/Intel%20SSD%20Toolbox%20-%20v3.5.9.exe', 'Intel SSD Toolbox': r'https://downloadmirror.intel.com/28593/eng/Intel%20SSD%20Toolbox%20-%20v3.5.9.exe',
'IOBit_Uninstaller': r'https://portableapps.com/redirect/?a=IObitUninstallerPortable&s=s&d=pa&f=IObitUninstallerPortable_7.5.0.7.paf.exe', 'IOBit_Uninstaller': r'https://portableapps.com/redirect/?a=IObitUninstallerPortable&s=s&d=pa&f=IObitUninstallerPortable_7.5.0.7.paf.exe',
'KVRT': 'http://devbuilds.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe', 'KVRT': 'http://devbuilds.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe',
'LibreOffice': 'https://download.documentfoundation.org/libreoffice/stable/6.2.4/win/x86_64/LibreOffice_6.2.4_Win_x64.msi',
'Macs Fan Control': 'https://www.crystalidea.com/downloads/macsfancontrol_setup.exe', 'Macs Fan Control': 'https://www.crystalidea.com/downloads/macsfancontrol_setup.exe',
'NirCmd32': 'https://www.nirsoft.net/utils/nircmd.zip', 'NirCmd32': 'https://www.nirsoft.net/utils/nircmd.zip',
'NirCmd64': 'https://www.nirsoft.net/utils/nircmd-x64.zip', 'NirCmd64': 'https://www.nirsoft.net/utils/nircmd-x64.zip',
@ -40,8 +37,8 @@ SOURCE_URLS = {
'SDIO Torrent': 'http://snappy-driver-installer.org/downloads/SDIO_Update.torrent', 'SDIO Torrent': 'http://snappy-driver-installer.org/downloads/SDIO_Update.torrent',
'TDSSKiller': 'https://media.kaspersky.com/utilities/VirusUtilities/EN/tdsskiller.exe', 'TDSSKiller': 'https://media.kaspersky.com/utilities/VirusUtilities/EN/tdsskiller.exe',
'TestDisk': 'https://www.cgsecurity.org/testdisk-7.1-WIP.win.zip', 'TestDisk': 'https://www.cgsecurity.org/testdisk-7.1-WIP.win.zip',
'wimlib32': 'https://wimlib.net/downloads/wimlib-1.13.1-windows-i686-bin.zip', 'wimlib32': 'https://wimlib.net/downloads/wimlib-1.13.0-windows-i686-bin.zip',
'wimlib64': 'https://wimlib.net/downloads/wimlib-1.13.1-windows-x86_64-bin.zip', 'wimlib64': 'https://wimlib.net/downloads/wimlib-1.13.0-windows-x86_64-bin.zip',
'Winapp2': 'https://github.com/MoscaDotTo/Winapp2/archive/master.zip', 'Winapp2': 'https://github.com/MoscaDotTo/Winapp2/archive/master.zip',
'WizTree': 'https://antibody-software.com/files/wiztree_3_28_portable.zip', 'WizTree': 'https://antibody-software.com/files/wiztree_3_28_portable.zip',
'XMPlay 7z': 'https://support.xmplay.com/files/16/xmp-7z.zip?v=800962', 'XMPlay 7z': 'https://support.xmplay.com/files/16/xmp-7z.zip?v=800962',
@ -69,18 +66,10 @@ VCREDIST_SOURCES = {
'64': 'https://aka.ms/vs/15/release/vc_redist.x64.exe', '64': 'https://aka.ms/vs/15/release/vc_redist.x64.exe',
}, },
} }
NINITE_REGEX = {
'base': ['7-Zip', 'VLC'],
'standard': ['Google Chrome', 'Mozilla Firefox', 'SumatraPDF'],
'standard7': ['Google Chrome', 'Mozilla Firefox', 'SumatraPDF'],
}
NINITE_SOURCES = { NINITE_SOURCES = {
'Bundles': { 'Bundles': {
'base.exe': '.net4.7.2-7zip-vlc', 'Legacy.exe': '.net4.7.2-7zip-chrome-firefox-vlc',
'base-standard.exe': '.net4.7.2-7zip-chrome-classicstart-firefox-sumatrapdf-vlc', 'Modern.exe': '.net4.7.2-7zip-chrome-classicstart-firefox-vlc',
'base-standard7.exe': '.net4.7.2-7zip-chrome-firefox-sumatrapdf-vlc',
'standard.exe': 'chrome-classicstart-firefox-sumatrapdf',
'standard7.exe': 'chrome-firefox-sumatrapdf',
}, },
'Audio-Video': { 'Audio-Video': {
'AIMP.exe': 'aimp', 'AIMP.exe': 'aimp',
@ -227,3 +216,5 @@ WINDOWS_UPDATE_SOURCES = {
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.")
# vim: sts=2 sw=2 ts=2 tw=0

View file

@ -1,118 +0,0 @@
'''Wizard Kit: Settings - UFD'''
# pylint: disable=C0326,E0611
# vim: sts=2 sw=2 ts=2
from collections import OrderedDict
from settings.main import KIT_NAME_FULL,KIT_NAME_SHORT
# General
DOCSTRING = '''WizardKit: Build UFD
Usage:
build-ufd [options] --ufd-device PATH --linux PATH
[--linux-minimal PATH]
[--main-kit PATH]
[--winpe PATH]
[--extra-dir PATH]
build-ufd (-h | --help)
Options:
-d PATH, --linux-dgpu PATH
-e PATH, --extra-dir PATH
-k PATH, --main-kit PATH
-l PATH, --linux PATH
-m PATH, --linux-minimal PATH
-u PATH, --ufd-device PATH
-w PATH, --winpe PATH
-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
'''
ISO_LABEL = '{}_LINUX'.format(KIT_NAME_SHORT)
UFD_LABEL = '{}_UFD'.format(KIT_NAME_SHORT)
UFD_SOURCES = OrderedDict({
'Linux': {'Arg': '--linux', 'Type': 'ISO'},
'Linux (dGPU)': {'Arg': '--linux-dgpu', 'Type': 'ISO'},
'Linux (Minimal)': {'Arg': '--linux-minimal', 'Type': 'ISO'},
'WinPE': {'Arg': '--winpe', 'Type': 'ISO'},
'Main Kit': {'Arg': '--main-kit', 'Type': 'KIT'},
'Extra Dir': {'Arg': '--extra-dir', 'Type': 'DIR'},
})
# Definitions: Boot entries
BOOT_ENTRIES = {
# Path to check: Comment to remove
'/arch_minimal': 'UFD-MINIMAL',
'/dgpu': 'UFD-DGPU',
'/sources/boot.wim': 'UFD-WINPE',
}
BOOT_FILES = {
# Directory: extension
'/arch/boot/syslinux': 'cfg',
'/EFI/boot': 'conf',
}
# Definitions: Sources and Destinations
## NOTES: Paths are relative to the root of the ISO/UFD
## Sources use rsync's trailing slash syntax
ITEMS = {
'Extra Dir': (
('/', '/'),
),
'Linux': (
('/arch', '/'),
('/isolinux', '/'),
('/EFI/boot', '/EFI/'),
('/EFI/memtest86', '/EFI/'),
),
'Linux (dGPU)': (
('/arch/boot/x86_64/archiso.img', '/dgpu/'),
('/arch/boot/x86_64/vmlinuz', '/dgpu/'),
('/arch/pkglist.x86_64.txt', '/dgpu/'),
('/arch/x86_64', '/dgpu/'),
),
'Linux (Minimal)': (
('/arch/boot/x86_64/archiso.img', '/arch_minimal/'),
('/arch/boot/x86_64/vmlinuz', '/arch_minimal/'),
('/arch/pkglist.x86_64.txt', '/arch_minimal/'),
('/arch/x86_64', '/arch_minimal/'),
),
'Main Kit': (
('/', '/{}/'.format(KIT_NAME_FULL)),
),
'WinPE': (
('/bootmgr', '/'),
('/bootmgr.efi', '/'),
('/en_us', '/'),
('/Boot/', '/boot/'),
('/EFI/Boot/', '/EFI/Microsoft/'),
('/EFI/Microsoft/', '/EFI/Microsoft/'),
('/Boot/BCD', '/sources/'),
('/Boot/boot.sdi', '/sources/'),
('/bootmgr', '/sources/'),
('/sources/boot.wim', '/sources/'),
),
}
ITEMS_HIDDEN = (
# Linux (all versions)
'arch',
'arch_minimal',
'dgpu',
'EFI',
'isolinux',
# Main Kit
'{}/.bin'.format(KIT_NAME_FULL),
'{}/.cbin'.format(KIT_NAME_FULL),
# WinPE
'boot',
'bootmgr',
'bootmgr.efi',
'en-us',
'images',
'sources',
)
if __name__ == '__main__':
print("This file is not meant to be called directly.")

View file

@ -1,10 +1,8 @@
'''Wizard Kit: Settings - Windows Builds''' # Wizard Kit: Settings - Windows Builds
# pylint: disable=bad-continuation,bad-whitespace
# vim: sts=2 sw=2 ts=2
## NOTE: Data from here: https://en.wikipedia.org/wiki/Windows_10_version_history
WINDOWS_BUILDS = { WINDOWS_BUILDS = {
# Build, Version, Release, Codename, Marketing Name, Notes # Build, Version, Release, Codename, Marketing Name, Notes
'6000': ('Vista', 'RTM', 'Longhorn', None, 'unsupported'),
'6000': ('Vista', 'RTM', 'Longhorn', None, 'unsupported'), '6000': ('Vista', 'RTM', 'Longhorn', None, 'unsupported'),
'6001': ('Vista', 'SP1', 'Longhorn', None, 'unsupported'), '6001': ('Vista', 'SP1', 'Longhorn', None, 'unsupported'),
'6002': ('Vista', 'SP2', 'Longhorn', None, 'unsupported'), '6002': ('Vista', 'SP2', 'Longhorn', None, 'unsupported'),
@ -204,22 +202,15 @@ WINDOWS_BUILDS = {
'18356': ('10', None, '19H1', None, 'preview build'), '18356': ('10', None, '19H1', None, 'preview build'),
'18358': ('10', None, '19H1', None, 'preview build'), '18358': ('10', None, '19H1', None, 'preview build'),
'18361': ('10', None, '19H1', None, 'preview build'), '18361': ('10', None, '19H1', None, 'preview build'),
'18362': ('10', 'v1903', '19H1', 'May 2019 Update', None),
'18836': ('10', None, '20H1', None, 'preview build'), '18836': ('10', None, '20H1', None, 'preview build'),
'18841': ('10', None, '20H1', None, 'preview build'), '18841': ('10', None, '20H1', None, 'preview build'),
'18845': ('10', None, '20H1', None, 'preview build'), '18845': ('10', None, '20H1', None, 'preview build'),
'18850': ('10', None, '20H1', None, 'preview build'), '18850': ('10', None, '20H1', None, 'preview build'),
'18855': ('10', None, '20H1', None, 'preview build'), '18855': ('10', None, '20H1', None, 'preview build'),
'18860': ('10', None, '20H1', None, 'preview build'),
'18865': ('10', None, '20H1', None, 'preview build'),
'18875': ('10', None, '20H1', None, 'preview build'),
'18885': ('10', None, '20H1', None, 'preview build'),
'18890': ('10', None, '20H1', None, 'preview build'),
'18894': ('10', None, '20H1', None, 'preview build'),
'18895': ('10', None, '20H1', None, 'preview build'),
'18898': ('10', None, '20H1', None, 'preview build'),
} }
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.")
# vim: sts=2 sw=2 ts=2

View file

@ -32,8 +32,8 @@ if __name__ == '__main__':
print_standard('\nDone.') print_standard('\nDone.')
pause('Press Enter to exit...') pause('Press Enter to exit...')
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -0,0 +1,133 @@
# Wizard Kit: System Checklist
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.activation import *
from functions.cleanup import *
from functions.info import *
from functions.product_keys import *
from functions.setup import *
from functions.sw_diags import *
init_global_vars()
os.system('title {}: System Checklist Tool'.format(KIT_NAME_FULL))
set_log_file('System Checklist.log')
if __name__ == '__main__':
try:
stay_awake()
clear_screen()
print_info('{}: System Checklist Tool\n'.format(KIT_NAME_FULL))
ticket_number = get_ticket_number()
other_results = {
'Error': {
'BIOSKeyNotFoundError': 'BIOS key not found',
'CalledProcessError': 'Unknown Error',
'FileNotFoundError': 'File not found',
'GenericError': 'Unknown Error',
'SecureBootDisabledError': 'Disabled',
},
'Warning': {
'OSInstalledLegacyError': 'OS installed Legacy',
'SecureBootNotAvailError': 'Not available',
'SecureBootUnknownError': 'Unknown',
}}
if ENABLED_TICKET_NUMBERS:
print_info('Starting System Checklist for Ticket #{}\n'.format(
ticket_number))
# Configure
print_info('Configure')
if global_vars['OS']['Version'] == '10':
try_and_print(message='Explorer...',
function=config_explorer_system, cs='Done')
try_and_print(message='Windows Updates...',
function=config_windows_updates, cs='Done')
try_and_print(message='Updating Clock...',
function=update_clock, cs='Done')
# Restart Explorer
try_and_print(message='Restarting Explorer...',
function=restart_explorer, cs='Done')
# Cleanup
print_info('Cleanup')
try_and_print(message='AdwCleaner...',
function=cleanup_adwcleaner, cs='Done', other_results=other_results)
try_and_print(message='Desktop...',
function=cleanup_desktop, cs='Done')
try_and_print(message='{}...'.format(KIT_NAME_FULL),
function=delete_empty_folders, cs='Done',
folder_path=global_vars['ClientDir'])
# Export system info
print_info('Backup System Information')
try_and_print(message='AIDA64 reports...',
function=run_aida64, cs='Done', other_results=other_results)
try_and_print(message='File listing...',
function=backup_file_list, cs='Done', other_results=other_results)
try_and_print(message='Power plans...',
function=backup_power_plans, cs='Done')
try_and_print(message='Product Keys...', other_results=other_results,
function=run_produkey, cs='Done')
try_and_print(message='Registry...',
function=backup_registry, cs='Done', other_results=other_results)
# User data
print_info('User Data')
show_user_data_summary()
# Summary
print_info('Summary')
try_and_print(message='Operating System:',
function=show_os_name, ns='Unknown', silent_function=False)
try_and_print(message='Activation:',
function=show_os_activation, ns='Unknown', silent_function=False)
if (not windows_is_activated()
and global_vars['OS']['Version'] in ('8', '8.1', '10')):
try_and_print(message='BIOS Activation:',
function=activate_with_bios,
other_results=other_results)
try_and_print(message='Secure Boot Status:',
function=check_secure_boot_status, other_results=other_results)
try_and_print(message='Installed RAM:',
function=show_installed_ram, ns='Unknown', silent_function=False)
show_free_space()
try_and_print(message='Installed Antivirus:',
function=get_installed_antivirus, ns='Unknown',
other_results=other_results, print_return=True)
try_and_print(message='Installed Office:',
function=get_installed_office, ns='Unknown',
other_results=other_results, print_return=True)
# Play audio, show devices, open Windows updates, and open Activation
try_and_print(message='Opening Device Manager...',
function=open_device_manager, cs='Started')
try_and_print(message='Opening HWiNFO (Sensors)...',
function=run_hwinfo_sensors, cs='Started', other_results=other_results)
try_and_print(message='Opening Windows Updates...',
function=open_windows_updates, cs='Started')
if not windows_is_activated():
try_and_print(message='Opening Windows Activation...',
function=open_windows_activation, cs='Started')
sleep(3)
try_and_print(message='Running XMPlay...',
function=run_xmplay, cs='Started', other_results=other_results)
try:
check_secure_boot_status(show_alert=True)
except:
# Only trying to open alert message boxes
pass
# Done
print_standard('\nDone.')
pause('Press Enter exit...')
exit_script()
except SystemExit:
pass
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -165,8 +165,8 @@ if __name__ == '__main__':
print_standard('\nDone.') print_standard('\nDone.')
pause('Press Enter to exit...') pause('Press Enter to exit...')
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -1,355 +0,0 @@
'''Wizard Kit: System Setup'''
# pylint: disable=wildcard-import,wrong-import-position
# vim: sts=2 sw=2 ts=2
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from collections import OrderedDict
from functions.activation import *
from functions.browsers import *
from functions.cleanup import *
from functions.info import *
from functions.product_keys import *
from functions.setup import *
from functions.sw_diags import *
from functions.windows_updates import *
init_global_vars()
os.system('title {}: System Setup'.format(KIT_NAME_FULL))
set_log_file('System Setup.log')
# STATIC VARIABLES
# pylint: disable=bad-whitespace,line-too-long
OTHER_RESULTS = {
'Error': {
'BIOSKeyNotFoundError': 'BIOS KEY NOT FOUND',
'CalledProcessError': 'UNKNOWN ERROR',
'FileNotFoundError': 'FILE NOT FOUND',
'GenericError': 'UNKNOWN ERROR',
'Not4KAlignedError': 'FALSE',
'SecureBootDisabledError': 'DISABLED',
'WindowsUnsupportedError': 'UNSUPPORTED',
},
'Warning': {
'GenericRepair': 'REPAIRED',
'NoProfilesError': 'NO PROFILES FOUND',
'NotInstalledError': 'NOT INSTALLED',
'OSInstalledLegacyError': 'OS INSTALLED LEGACY',
'SecureBootNotAvailError': 'NOT AVAILABLE',
'SecureBootUnknownError': 'UNKNOWN',
'UnsupportedOSError': 'UNSUPPORTED OS',
'WindowsOutdatedError': 'OUTDATED',
},
}
SETUP_ACTIONS = OrderedDict({
# Install software
'Installing Programs': {'Info': True},
'VCR': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': install_vcredists, 'Just run': True,},
'LibreOffice': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': install_libreoffice,
'If answer': 'LibreOffice', 'KWArgs': {'quickstart': False, 'register_mso_types': True, 'use_mso_formats': False, 'vcredist': False},
},
'Ninite bundle': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': install_ninite_bundle, 'KWArgs': {'cs': 'STARTED'},},
# Browsers
'Scanning for browsers': {'Info': True},
'Scan': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': scan_for_browsers, 'Just run': True, 'KWArgs': {'skip_ie': True},},
'Backing up browsers': {'Info': True},
'Backup browsers': {'New': False, 'Dat': True, 'Cur': True, 'HW': False, 'Function': backup_browsers, 'Just run': True,},
# Install extensions
'Installing Extensions': {'Info': True},
'Classic Shell skin': {'New': True, 'Dat': True, 'Cur': False, 'HW': False, 'Function': install_classicstart_skin, 'Win10 only': True,},
'Chrome extensions': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': install_chrome_extensions,},
'Firefox extensions': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': install_firefox_extensions,},
# Configure software'
'Configuring Programs': {'Info': True},
'Browser add-ons': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': install_adblock, 'Just run': True,
'Pause': 'Please enable uBlock Origin for all browsers',
},
'Classic Start': {'New': True, 'Dat': True, 'Cur': False, 'HW': False, 'Function': config_classicstart, 'Win10 only': True,},
'Config Windows Updates': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': config_windows_updates, 'Win10 only': True,},
'Enable Windows Updates': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': enable_windows_updates, 'KWArgs': {'silent': True},},
'Explorer (system)': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': config_explorer_system, 'Win10 only': True,},
'Explorer (user)': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': config_explorer_user, 'Win10 only': True,},
'Restart Explorer': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': restart_explorer,},
'Restore default UAC': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': restore_default_uac,},
'Update Clock': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': update_clock,},
# Cleanup
'Cleaning up': {'Info': True},
'AdwCleaner': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': cleanup_adwcleaner,},
'Desktop': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': cleanup_desktop,},
'KIT_NAME_FULL': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': delete_empty_folders,},
# System Info
'Exporting system info': {'Info': True},
'AIDA64 Report': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': run_aida64,},
'File listing': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': backup_file_list,},
'Power plans': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': backup_power_plans,},
'Product Keys': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': run_produkey,},
'Registry': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': backup_registry,},
# Show Summary
'Summary': {'Info': True},
'Operating System': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': show_os_name, 'KWArgs': {'ns': 'UNKNOWN', 'silent_function': False},},
'Activation': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': show_os_activation, 'KWArgs': {'ns': 'UNKNOWN', 'silent_function': False},},
'BIOS Activation': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': activate_with_bios, 'If not activated': True,},
'Secure Boot': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': check_secure_boot_status, 'KWArgs': {'show_alert': False},},
'Installed RAM': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': show_installed_ram, 'KWArgs': {'ns': 'UNKNOWN', 'silent_function': False},},
'Temp size': {'New': False, 'Dat': False, 'Cur': True, 'HW': False, 'Function': show_temp_files_size, 'KWArgs': {'ns': 'UNKNOWN', 'silent_function': False},},
'Show free space': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': show_free_space, 'Just run': True,},
'Installed AV': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': get_installed_antivirus, 'KWArgs': {'ns': 'UNKNOWN', 'print_return': True},},
'Installed Office': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': get_installed_office, 'KWArgs': {'ns': 'UNKNOWN', 'print_return': True},},
'Partitions 4K aligned': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': check_4k_alignment, 'KWArgs': {'cs': 'TRUE', 'ns': 'FALSE'},},
# Open things
'Opening Programs': {'Info': True},
'Device Manager': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': open_device_manager, 'KWArgs': {'cs': 'STARTED'},},
'HWiNFO sensors': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': run_hwinfo_sensors, 'KWArgs': {'cs': 'STARTED'},},
'Speed test': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': open_speedtest, 'KWArgs': {'cs': 'STARTED'},},
'Windows Updates': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': open_windows_updates, 'KWArgs': {'cs': 'STARTED'},},
'Windows Activation': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Function': open_windows_activation, 'If not activated': True, 'KWArgs': {'cs': 'STARTED'},},
'Sleep': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': sleep, 'Just run': True, 'KWArgs': {'seconds': 3},},
'XMPlay': {'New': True, 'Dat': True, 'Cur': True, 'HW': True, 'Function': run_xmplay, 'KWArgs': {'cs': 'STARTED'},},
})
SETUP_ACTION_KEYS = (
'Function',
'If not activated',
'Info',
'Just run',
'KWArgs',
'Pause',
)
SETUP_QUESTIONS = {
# AV
'MSE': {'New': None, 'Dat': None, 'Cur': None, 'HW': False, 'Ninite': True},
# LibreOffice
'LibreOffice': {'New': None, 'Dat': None, 'Cur': None, 'HW': False, 'Ninite': True},
# Ninite
'Base': {'New': True, 'Dat': True, 'Cur': True, 'HW': False, 'Ninite': True},
'Missing': {'New': False, 'Dat': True, 'Cur': False, 'HW': False, 'Ninite': True},
'Standard': {'New': True, 'Dat': True, 'Cur': False, 'HW': False, 'Ninite': True},
}
# pylint: enable=bad-whitespace,line-too-long
# Functions
def check_os_and_abort():
"""Check OS and prompt to abort if not supported."""
result = try_and_print(
message='OS support status...',
function=check_os_support_status,
cs='GOOD',
)
if not result['CS'] and 'Unsupported' in result['Error']:
print_warning('OS version not supported by this script')
if not ask('Continue anyway? (NOT RECOMMENDED)'):
abort()
def get_actions(setup_mode, answers):
"""Get actions to perform based on setup_mode, returns OrderedDict."""
actions = OrderedDict({})
for _key, _val in SETUP_ACTIONS.items():
_action = {}
_if_answer = _val.get('If answer', False)
_win10_only = _val.get('Win10 only', False)
# Set enabled status
_enabled = _val.get(setup_mode, False)
if _if_answer:
_enabled = _enabled and answers[_if_answer]
if _win10_only:
_enabled = _enabled and global_vars['OS']['Version'] == '10'
_action['Enabled'] = _enabled
# Set other keys
for _sub_key in SETUP_ACTION_KEYS:
_action[_sub_key] = _val.get(_sub_key, None)
# Fix KWArgs
if _action.get('KWArgs', {}) is None:
_action['KWArgs'] = {}
# Handle "special" actions
if _key == 'KIT_NAME_FULL':
# Cleanup WK folders
_key = KIT_NAME_FULL
_action['KWArgs'] = {'folder_path': global_vars['ClientDir']}
elif _key == 'Ninite bundle':
# Add install_ninite_bundle() kwargs
_action['KWArgs'].update({
kw.lower(): kv for kw, kv in answers.items()
if SETUP_QUESTIONS.get(kw, {}).get('Ninite', False)
})
elif _key == 'Explorer (user)':
# Explorer settings (user)
_action['KWArgs'] = {'setup_mode': setup_mode}
# Add to dict
actions[_key] = _action
return actions
def get_answers(setup_mode):
"""Get setup answers based on setup_mode and user input, returns dict."""
answers = {k: v.get(setup_mode, False) for k, v in SETUP_QUESTIONS.items()}
# Answer setup questions as needed
if answers['MSE'] is None and global_vars['OS']['Version'] == '7':
answers.update(get_av_selection())
if answers['LibreOffice'] is None:
answers['LibreOffice'] = ask('Install LibreOffice?')
return answers
def get_av_selection():
"""Get AV selection."""
av_answers = {
'MSE': False,
}
av_options = [
{
'Name': 'Microsoft Security Essentials',
'Disabled': global_vars['OS']['Version'] not in ['7'],
},
]
actions = [
{'Name': 'None', 'Letter': 'N'},
{'Name': 'Quit', 'Letter': 'Q'},
]
# Show menu
selection = menu_select(
'Please select an option to install',
main_entries=av_options,
action_entries=actions)
if selection.isnumeric():
index = int(selection) - 1
if 'Microsoft' in av_options[index]['Name']:
av_answers['MSE'] = True
elif selection == 'Q':
abort()
return av_answers
def get_mode():
"""Get mode via menu_select, returns str."""
setup_mode = None
mode_options = [
{'Name': 'New', 'Display Name': 'New / Clean install (no data)'},
{'Name': 'Dat', 'Display Name': 'Clean install with data migration'},
{'Name': 'Cur', 'Display Name': 'Original OS (post-repair or overinstall)'},
{'Name': 'HW', 'Display Name': 'Hardware service (i.e. no software work)'},
]
actions = [
{'Name': 'Quit', 'Letter': 'Q'},
]
# Get selection
selection = menu_select(
'Please select a setup mode',
main_entries=mode_options,
action_entries=actions)
if selection.isnumeric():
index = int(selection) - 1
setup_mode = mode_options[index]['Name']
elif selection == 'Q':
abort()
return setup_mode
def main():
"""Main function."""
stay_awake()
clear_screen()
# Check installed OS
check_os_and_abort()
# Get setup mode
setup_mode = get_mode()
# Get answers to setup questions
answers = get_answers(setup_mode)
# Get actions to perform
actions = get_actions(setup_mode, answers)
# Perform actions
for action, values in actions.items():
kwargs = values.get('KWArgs', {})
# Print info lines
if values.get('Info', False):
print_info(action)
continue
# Print disabled actions
if not values.get('Enabled', False):
show_data(
message='{}...'.format(action),
data='DISABLED',
warning=True,
)
continue
# Check Windows activation if requested
if values.get('If not activated', False) and windows_is_activated():
# Skip
continue
# Run function
if values.get('Just run', False):
values['Function'](**kwargs)
else:
result = try_and_print(
message='{}...'.format(action),
function=values['Function'],
other_results=OTHER_RESULTS,
**kwargs)
# Wait for Ninite proc(s)
if action == 'Ninite bundle':
print_standard('Waiting for installations to finish...')
try:
for proc in result['Out']:
proc.wait()
except KeyboardInterrupt:
pass
# Pause
if values.get('Pause', False):
print_standard(values['Pause'])
pause()
# Show alert box for SecureBoot issues
try:
check_secure_boot_status(show_alert=True)
except Exception: # pylint: disable=broad-except
# Ignoring exceptions since we just want to show the popup
pass
# Done
pause('Press Enter to exit... ')
if __name__ == '__main__':
try:
main()
exit_script()
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except: # pylint: disable=bare-except
major_exception()

View file

@ -21,8 +21,8 @@ if __name__ == '__main__':
# Done # Done
print_standard('\nDone.') print_standard('\nDone.')
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -57,7 +57,6 @@ if __name__ == '__main__':
# Installers # Installers
print_info(' Installers') print_info(' Installers')
try_and_print(message='Adobe Reader DC...', function=update_adobe_reader_dc, other_results=other_results, width=40) try_and_print(message='Adobe Reader DC...', function=update_adobe_reader_dc, other_results=other_results, width=40)
try_and_print(message='LibreOffice...', function=update_libreoffice, other_results=other_results, width=40)
try_and_print(message='Macs Fan Control...', function=update_macs_fan_control, other_results=other_results, width=40) try_and_print(message='Macs Fan Control...', function=update_macs_fan_control, other_results=other_results, width=40)
try_and_print(message='MS Office...', function=update_office, other_results=other_results, width=40) try_and_print(message='MS Office...', function=update_office, other_results=other_results, width=40)
try_and_print(message='Visual C++ Runtimes...', function=update_vcredists, other_results=other_results, width=40) try_and_print(message='Visual C++ Runtimes...', function=update_vcredists, other_results=other_results, width=40)
@ -135,8 +134,8 @@ if __name__ == '__main__':
print_standard('\nDone.') print_standard('\nDone.')
pause("Press Enter to exit...") pause("Press Enter to exit...")
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -0,0 +1,90 @@
# Wizard Kit: User Checklist
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.browsers import *
from functions.cleanup import *
from functions.setup import *
init_global_vars()
os.system('title {}: User Checklist Tool'.format(KIT_NAME_FULL))
set_log_file('User Checklist ({USERNAME}).log'.format(**global_vars['Env']))
if __name__ == '__main__':
try:
stay_awake()
clear_screen()
print_info('{}: User Checklist\n'.format(KIT_NAME_FULL))
other_results = {
'Warning': {
'NotInstalledError': 'Not installed',
'NoProfilesError': 'No profiles found',
}}
answer_config_browsers = ask('Install adblock?')
if answer_config_browsers:
answer_reset_browsers = ask(
'Reset browsers to safe defaults first?')
if global_vars['OS']['Version'] == '10':
answer_config_classicshell = ask('Configure ClassicShell?')
answer_config_explorer_user = ask('Configure Explorer?')
# Cleanup
print_info('Cleanup')
try_and_print(message='Desktop...',
function=cleanup_desktop, cs='Done')
# Scan for supported browsers
print_info('Scanning for browsers')
scan_for_browsers()
# Homepages
print_info('Current homepages')
list_homepages()
# Backup
print_info('Backing up browsers')
backup_browsers()
# Reset
if answer_config_browsers and answer_reset_browsers:
print_info('Resetting browsers')
reset_browsers()
# Configure
print_info('Configuring programs')
if answer_config_browsers:
install_adblock()
if global_vars['OS']['Version'] == '10':
if answer_config_classicshell:
try_and_print(message='ClassicStart...',
function=config_classicstart, cs='Done')
if answer_config_explorer_user:
try_and_print(message='Explorer...',
function=config_explorer_user, cs='Done')
if (not answer_config_browsers
and not answer_config_classicshell
and not answer_config_explorer_user):
print_warning(' Skipped')
else:
if not answer_config_browsers:
print_warning(' Skipped')
# Restart Explorer
try_and_print(message='Restarting Explorer...',
function=restart_explorer, cs='Done')
# Run speedtest
popen_program(['start', '', 'https://fast.com'], shell=True)
# Done
print_standard('\nDone.')
pause('Press Enter to exit...')
exit_script()
except SystemExit:
pass
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -59,8 +59,8 @@ if __name__ == '__main__':
print_standard('\nDone.') print_standard('\nDone.')
pause("Press Enter to exit...") pause("Press Enter to exit...")
exit_script() exit_script()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

View file

@ -1,46 +0,0 @@
# Wizard Kit: Windows updates
import os
import sys
# Init
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from functions.windows_updates import *
init_global_vars()
os.system('title {}: Windows Updates Tool'.format(KIT_NAME_FULL))
set_log_file('Windows Updates Tool.log')
if __name__ == '__main__':
try:
clear_screen()
print_info('{}: Windows Updates Tool\n'.format(KIT_NAME_FULL))
# Check args
if '--disable' in sys.argv:
disable_windows_updates()
elif '--enable' in sys.argv:
enable_windows_updates()
else:
print_error('Bad mode.')
abort()
# Done
exit_script()
except GenericError as err:
# Failed to complete request, show error(s) and prompt tech
print_standard(' ')
for line in str(err).splitlines():
print_warning(line)
print_standard(' ')
print_error('Error(s) encountered, see above.')
print_standard(' ')
if '--disable' in sys.argv:
print_standard('Please reboot and try again.')
pause('Press Enter to exit... ')
exit_script(1)
except SystemExit as sys_exit:
exit_script(sys_exit.code)
except:
major_exception()
# vim: sts=2 sw=2 ts=2

View file

@ -15,8 +15,8 @@ set_log_file('WinPE.log')
if __name__ == '__main__': if __name__ == '__main__':
try: try:
menu_root() menu_root()
except SystemExit as sys_exit: except SystemExit:
exit_script(sys_exit.code) pass
except: except:
major_exception() major_exception()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

View file

@ -19,7 +19,6 @@ menuentry "MemTest86" {
icon /EFI/boot/icons/wk_memtest.png icon /EFI/boot/icons/wk_memtest.png
loader /EFI/memtest86/memtestx64.efi loader /EFI/memtest86/memtestx64.efi
} }
menuentry "Linux" { menuentry "Linux" {
icon /EFI/boot/icons/wk_arch.png icon /EFI/boot/icons/wk_arch.png
loader /arch/boot/x86_64/vmlinuz loader /arch/boot/x86_64/vmlinuz
@ -27,32 +26,15 @@ menuentry "Linux" {
initrd /arch/boot/amd_ucode.img initrd /arch/boot/amd_ucode.img
initrd /arch/boot/x86_64/archiso.img initrd /arch/boot/x86_64/archiso.img
options "archisobasedir=arch archisolabel=%ARCHISO_LABEL% copytoram loglevel=3" options "archisobasedir=arch archisolabel=%ARCHISO_LABEL% copytoram loglevel=3"
submenuentry "Linux (CLI)" { submenuentry "Linux (i3)" {
add_options "nox" add_options "i3"
}
submenuentry "Linux (CLI)" {
add_options "loglevel=4 nomodeset nox"
} }
#UFD-MINIMAL#submenuentry "Linux (Minimal)" {
#UFD-MINIMAL# loader /arch_minimal/vmlinuz
#UFD-MINIMAL# initrd
#UFD-MINIMAL# initrd /arch/boot/intel_ucode.img
#UFD-MINIMAL# initrd /arch/boot/amd_ucode.img
#UFD-MINIMAL# initrd /arch_minimal/archiso.img
#UFD-MINIMAL# options
#UFD-MINIMAL# options "archisobasedir=arch_minimal archisolabel=%ARCHISO_LABEL% copytoram loglevel=3"
#UFD-MINIMAL#}
} }
#UFD-WINPE#menuentry "WindowsPE" { #UFD-WINPE#menuentry "WindowsPE" {
#UFD-WINPE# ostype windows #UFD-WINPE# ostype windows
#UFD-WINPE# icon /EFI/boot/icons/wk_win.png #UFD-WINPE# icon /EFI/boot/icons/wk_win.png
#UFD-WINPE# loader /EFI/microsoft/bootx64.efi #UFD-WINPE# loader /EFI/microsoft/bootx64.efi
#UFD-WINPE#} #UFD-WINPE#}
#UFD-DGPU#menuentry "Mac dGPU Disable Tool" {
#UFD-DGPU# icon /EFI/boot/icons/dgpu.png
#UFD-DGPU# loader /dgpu/vmlinuz
#UFD-DGPU# initrd /arch/boot/intel_ucode.img
#UFD-DGPU# initrd /arch/boot/amd_ucode.img
#UFD-DGPU# initrd /dgpu/archiso.img
#UFD-DGPU# options "archisobasedir=dgpu archisolabel=%ARCHISO_LABEL% nomodeset"
#UFD-DGPU#}

View file

@ -1,2 +1,9 @@
Welcome to the ______ Welcome to the ______
Some common commands:
% hw-diags
% hw-info
% mount-all-volumes
% mount-backup-shares
% connect-to-network

View file

@ -34,5 +34,5 @@ alias srsz='sudo rsync -avhzPS --stats --exclude-from="$HOME/.rsync_exclusions"'
alias testdisk='sudo testdisk' alias testdisk='sudo testdisk'
alias umount='sudo umount' alias umount='sudo umount'
alias unmount='sudo umount' alias unmount='sudo umount'
alias wkclone='ddrescue-tui clone' alias wkclone='sudo ddrescue-tui clone'
alias wkimage='ddrescue-tui image' alias wkimage='sudo ddrescue-tui image'

View file

@ -1,5 +1,5 @@
set -g status off set -g status off
set -g pane-active-border-style fg=white set -g pane-active-border-fg white
# Window names # Window names
set -g set-titles on set -g set-titles on

View file

@ -1,26 +1,23 @@
## .update_network ##
#!/bin/env bash #!/bin/env bash
# #
## Setup network and update hostname ## Connect to network and update hostname
# Wait for WiFi # Connect
echo -n "Waiting for network... " connect-to-network
sleep 3s sleep 2s
echo "Done"
# Set hostname
echo -n "Updating hostname... "
IP="$(ip a show scope global \ IP="$(ip a show scope global \
| grep inet \ | grep inet \
| head -1 \ | head -1 \
| sed -r 's#.*inet ([0-9]+.[0-9]+.[0-9]+.[0-9]+.)/.*#\1#')" | sed -r 's#.*inet ([0-9]+.[0-9]+.[0-9]+.[0-9]+.)/.*#\1#')"
if [[ "${IP:+x}" ]]; then HOSTNAME="$(dig +noall +answer +short -x "$IP" \
NEW_HOSTNAME="$(dig +noall +answer +short -x "$IP" \ | grep -v ';' \
| grep -v ';' \ | head -1 \
| head -1 \ | sed 's/\.$//')"
| sed 's/\.$//')"
fi # Set hostname
if [[ "${NEW_HOSTNAME:+x}" ]]; then if [[ "${HOSTNAME:+x}" ]]; then
sudo hostnamectl set-hostname "${NEW_HOSTNAME}" sudo hostnamectl set-hostname "${HOSTNAME}"
fi fi
echo "Done"

View file

@ -8,6 +8,17 @@ LINUX boot/x86_64/vmlinuz
INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img
APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=3 APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=3
LABEL wk_linux_i3
TEXT HELP
A live Linux environment (i3)
* HW diagnostics, file-based backups, data recovery, etc
ENDTEXT
MENU LABEL Linux (i3)
LINUX boot/x86_64/vmlinuz
INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img
APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram loglevel=3 i3
SYSAPPEND 3
LABEL wk_linux_cli LABEL wk_linux_cli
TEXT HELP TEXT HELP
A live Linux environment (CLI) A live Linux environment (CLI)
@ -16,17 +27,5 @@ ENDTEXT
MENU LABEL Linux (CLI) MENU LABEL Linux (CLI)
LINUX boot/x86_64/vmlinuz LINUX boot/x86_64/vmlinuz
INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img INITRD boot/intel_ucode.img,boot/amd_ucode.img,boot/x86_64/archiso.img
APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram nox APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram nox nomodeset
SYSAPPEND 3 SYSAPPEND 3
#UFD-MINIMAL#LABEL wk_linux_minimal
#UFD-MINIMAL#TEXT HELP
#UFD-MINIMAL#A live Linux environment (Minimal)
#UFD-MINIMAL# * HW diagnostics, file-based backups, data recovery, etc
#UFD-MINIMAL#ENDTEXT
#UFD-MINIMAL#MENU LABEL Linux (Minimal)
#UFD-MINIMAL#LINUX ../arch_minimal/vmlinuz
#UFD-MINIMAL#INITRD boot/intel_ucode.img,boot/amd_ucode.img,../arch_minimal/archiso.img
#UFD-MINIMAL#APPEND archisobasedir=arch_minimal archisolabel=%ARCHISO_LABEL% copytoram loglevel=3
#UFD-MINIMAL#SYSAPPEND 3

View file

@ -73,7 +73,7 @@ bindsym $mod+f exec "thunar ~"
bindsym $mod+i exec "hardinfo" bindsym $mod+i exec "hardinfo"
bindsym $mod+m exec "urxvt -title 'Mount All Volumes' -e mount-all-volumes gui" bindsym $mod+m exec "urxvt -title 'Mount All Volumes' -e mount-all-volumes gui"
bindsym $mod+s exec "urxvt -title 'Hardware Diagnostics' -e hw-diags --quick" bindsym $mod+s exec "urxvt -title 'Hardware Diagnostics' -e hw-diags --quick"
bindsym $mod+t exec "urxvt" bindsym $mod+t exec "urxvt -e zsh -c 'tmux new-session -A -t general; zsh'"
bindsym $mod+v exec "urxvt -title 'Hardware Sensors' -e watch -c -n1 -t hw-sensors" bindsym $mod+v exec "urxvt -title 'Hardware Sensors' -e watch -c -n1 -t hw-sensors"
bindsym $mod+w exec "firefox" bindsym $mod+w exec "firefox"
@ -320,4 +320,4 @@ bar {
height 26 height 26
} }
exec urxvt -title "Initializing..." -e /home/tech/.update_x exec --no-startup-id /home/tech/.update_x

View file

@ -1,5 +1,3 @@
#openbox-autostart #openbox-autostart
/usr/bin/urxvt -title "Initializing..." -e "$HOME/.update_x" $HOME/.update_x &
"$HOME/.start_desktop_apps" &

View file

@ -329,7 +329,7 @@
</keybind> </keybind>
<keybind key="W-t"> <keybind key="W-t">
<action name="Execute"> <action name="Execute">
<command>urxvt</command> <command>urxvt -e zsh -c 'tmux new-session -A -t general; zsh'</command>
</action> </action>
</keybind> </keybind>
<keybind key="W-v"> <keybind key="W-v">

View file

@ -7,7 +7,5 @@ rofi.color-normal: argb:d02d3036, #d8d8d8, argb:d02d3036, #2d3036, #d64937
rofi.color-active: argb:d0222222, #d64937, argb:d0222222, #d64937, #d8d8d8 rofi.color-active: argb:d0222222, #d64937, argb:d0222222, #d64937, #d8d8d8
rofi.color-urgent: argb:d0888888, #d8d8d8, argb:d0888888, #888888, #d64937 rofi.color-urgent: argb:d0888888, #d8d8d8, argb:d0888888, #888888, #d64937
rofi.font: Noto Sans 12
rofi.separator-style: solid rofi.separator-style: solid
rofi.hide-scrollbar: true rofi.hide-scrollbar: true

View file

@ -1,6 +0,0 @@
[Unit]
Description=Conky config update service
[Service]
Type=oneshot
ExecStart=%h/.update_conky

View file

@ -1,10 +0,0 @@
[Unit]
Description=Conky config update timer
[Timer]
OnBootSec=5s
OnUnitActiveSec=30s
Unit=update-conky.service
[Install]
WantedBy=timers.target

View file

@ -1,24 +0,0 @@
#!/bin/env bash
#
## Start desktop apps based on WM
# Start common apps
feh --bg-fill "$HOME/.wallpaper"
compton --backend xrender --xrender-sync --xrender-sync-fence &
sleep 1s
x0vncserver -display :0 -passwordfile $HOME/.vnc/passwd -AlwaysShared &
conky &
nm-applet &
volumeicon &
# Start WM specific apps
if fgrep -q "i3" /proc/cmdline; then
# i3
i3-msg restart
else
# openbox
openbox --restart
tint2 &
cbatticon --hide-notification &
fi

View file

@ -1,24 +1,18 @@
#!/bin/bash #!/bin/bash
IF_LIST=($(ip l \ IF_LIST=($(ip l | egrep '^[0-9]+:\s+(eth|en|wl)' | sed -r 's/^[0-9]+:\s+(\w+):.*/\1/' | sort))
| egrep '^[0-9]+:\s+(eth|en|wl)' \
| sed -r 's/^[0-9]+:\s+(\w+):.*/\1/' \
| sort))
# Reset conkyrc to default
rm "${HOME}/.conkyrc"
cp "${HOME}/.conkyrc_base" "${HOME}/.conkyrc"
# Add interfaces to conkyrc # Add interfaces to conkyrc
for i in "${IF_LIST[@]}"; do if fgrep '#Network' $HOME/.conkyrc; then
if [[ "${i:0:1}" == "e" ]]; then for i in "${IF_LIST[@]}"; do
sed -i -r "s/#Network/Wired:\${alignr}\${addr $i}\n#Network/" $HOME/.conkyrc if [[ "${i:0:1}" == "e" ]]; then
else sed -i -r "s/#Network/Wired:\${alignr}\${addr $i}\n#Network/" $HOME/.conkyrc
sed -i -r "s/#Network/Wireless:\${alignr}\${addr $i}\n#Network/" $HOME/.conkyrc else
fi sed -i -r "s/#Network/Wireless:\${alignr}\${addr $i}\n#Network/" $HOME/.conkyrc
done fi
done
# Remove '#Network' line to prevent duplicating lines if this script is re-run # Remove '#Network' line to prevent duplicating lines if this script is re-run
sed -i -r "s/#Network//" $HOME/.conkyrc sed -i -r "s/#Network//" $HOME/.conkyrc
fi
# vim: sts=2 sw=2 ts=2

View file

@ -5,8 +5,6 @@
REGEX_XRANDR='^.* ([0-9]+)x([0-9]+)\+[0-9]+\+[0-9]+.* ([0-9]+)mm x ([0-9]+)mm.*$' REGEX_XRANDR='^.* ([0-9]+)x([0-9]+)\+[0-9]+\+[0-9]+.* ([0-9]+)mm x ([0-9]+)mm.*$'
REGEX_URXVT='(URxvt.geometry:\s+).*' REGEX_URXVT='(URxvt.geometry:\s+).*'
echo -n "Getting display details... "
# Get screen data # Get screen data
xrandr_str="$(xrandr | grep mm | head -1)" xrandr_str="$(xrandr | grep mm | head -1)"
width_px="$(echo "${xrandr_str}" | sed -r "s/${REGEX_XRANDR}/\1/")" width_px="$(echo "${xrandr_str}" | sed -r "s/${REGEX_XRANDR}/\1/")"
@ -31,17 +29,13 @@ width_urxvt="$(echo "${width_px} * 112/1280" | bc)"
height_urxvt="$(echo "${height_px} * 33/720" | bc)" height_urxvt="$(echo "${height_px} * 33/720" | bc)"
offset_urxvt="24" offset_urxvt="24"
echo "Done"
# Update settings if necessary # Update settings if necessary
if [[ "${dpi}" -ge 192 ]]; then if [[ "${dpi}" -ge 192 ]]; then
echo -n "Updating settings for HiDPI... "
# Conky # Conky
sed -i 's/minimum_size 180 0/minimum_size 360 0/' "${HOME}/.conkyrc_base" sed -i 's/minimum_size 180 0/minimum_size 360 0/' "${HOME}/.conkyrc"
sed -i 's/maximum_width 180/maximum_width 360/' "${HOME}/.conkyrc_base" sed -i 's/maximum_width 180/maximum_width 360/' "${HOME}/.conkyrc"
sed -i 's/gap_x 20/gap_x 40/' "${HOME}/.conkyrc_base" sed -i 's/gap_x 20/gap_x 40/' "${HOME}/.conkyrc"
sed -i 's/gap_y 24/gap_y 48/' "${HOME}/.conkyrc_base" sed -i 's/gap_y 24/gap_y 48/' "${HOME}/.conkyrc"
# Fonts # Fonts
sed -i 's/!Xft.dpi: 192/Xft.dpi: 192/' "${HOME}/.Xresources" sed -i 's/!Xft.dpi: 192/Xft.dpi: 192/' "${HOME}/.Xresources"
@ -53,9 +47,6 @@ if [[ "${dpi}" -ge 192 ]]; then
# i3 # i3
sed -i -r 's/(height\s+) 26/\1 52/' "${HOME}/.config/i3/config" sed -i -r 's/(height\s+) 26/\1 52/' "${HOME}/.config/i3/config"
# Rofi
sed -i -r 's/Noto Sans 12/Noto Sans 24/' "${HOME}/.config/rofi/config"
# Tint2 # Tint2
sed -i 's/panel_size = 100% 30/panel_size = 100% 60/' \ sed -i 's/panel_size = 100% 30/panel_size = 100% 60/' \
"${HOME}/.config/tint2/tint2rc" "${HOME}/.config/tint2/tint2rc"
@ -70,29 +61,37 @@ if [[ "${dpi}" -ge 192 ]]; then
width_urxvt="$(echo "${width_urxvt} / 2" | bc)" width_urxvt="$(echo "${width_urxvt} / 2" | bc)"
height_urxvt="$(echo "${height_urxvt} / 2" | bc)" height_urxvt="$(echo "${height_urxvt} / 2" | bc)"
offset_urxvt="$(echo "${offset_urxvt} * 2" | bc)" offset_urxvt="$(echo "${offset_urxvt} * 2" | bc)"
# Done
echo "Done"
fi fi
# Update URxvt (Always) # Update URxvt (Always)
urxvt_geometry="${width_urxvt}x${height_urxvt}+${offset_urxvt}+${offset_urxvt}" urxvt_geometry="${width_urxvt}x${height_urxvt}+${offset_urxvt}+${offset_urxvt}"
sed -i -r "s/${REGEX_URXVT}/\1${urxvt_geometry}/" "${HOME}/.Xresources" sed -i -r "s/${REGEX_URXVT}/\1${urxvt_geometry}/" "${HOME}/.Xresources"
# Update conky
echo -n "Updating conky... "
$HOME/.update_conky
echo "Done"
# Update X # Update X
echo -n "Updating X... "
xset s off xset s off
xset -dpms xset -dpms
xrdb -merge $HOME/.Xresources xrdb -merge $HOME/.Xresources
echo "Done"
# Start desktop apps for i3 # Start common desktop apps
feh --bg-fill "$HOME/.wallpaper"
compton --backend xrender --xrender-sync --xrender-sync-fence &
sleep 1s
x0vncserver -display :0 -passwordfile $HOME/.vnc/passwd -AlwaysShared &
conky &
nm-applet &
volumeicon &
# Start WM specific apps
if fgrep -q "i3" /proc/cmdline; then if fgrep -q "i3" /proc/cmdline; then
i3-msg exec $HOME/.start_desktop_apps # i3
i3-msg restart
else
# openbox
openbox --restart
tint2 &
cbatticon --hide-notification &
fi fi
# Prevent Xorg from being killed by .zlogin
touch "/tmp/x_ok"

View file

@ -3,6 +3,5 @@
dbus-update-activation-environment --systemd DISPLAY dbus-update-activation-environment --systemd DISPLAY
eval $(ssh-agent) eval $(ssh-agent)
export SSH_AUTH_SOCK export SSH_AUTH_SOCK
xrdb -merge $HOME/.Xresources
exec openbox-session exec openbox-session

View file

@ -1,7 +1,7 @@
setterm -blank 0 -powerdown 0 2>/dev/null setterm -blank 0 -powerdown 0 2>/dev/null
if [ "$(fgconsole 2>/dev/null)" -eq "1" ]; then if [ "$(fgconsole 2>/dev/null)" -eq "1" ]; then
# Connect to network and update hostname # Connect to network and update hostname
"${HOME}/.update_network" $HOME/.update_network
# Update settings if using i3 # Update settings if using i3
if fgrep -q "i3" /proc/cmdline; then if fgrep -q "i3" /proc/cmdline; then
@ -9,18 +9,23 @@ if [ "$(fgconsole 2>/dev/null)" -eq "1" ]; then
sed -i -r 's/openbox-session/i3/' ~/.xinitrc sed -i -r 's/openbox-session/i3/' ~/.xinitrc
fi fi
# Update Conky
$HOME/.update_conky
# Start X or HW-diags # Start X or HW-diags
if ! fgrep -q "nox" /proc/cmdline; then if ! fgrep -q "nox" /proc/cmdline; then
# Show freeze warning # Kill Xorg after 30 seconds if it doesn't fully initialize
echo "" (sleep 30s; if ! [[ -f "/tmp/x_ok" ]]; then pkill '(Xorg|startx)'; fi) &
echo "NOTE: Not all GPUs/displays are supported."
echo " If the system is frozen on this screen"
echo " please restart and try CLI mode instead"
echo ""
# Start x # Try starting X
echo "Starting X..." startx >/dev/null
startx >/dev/null 2>&1
# Run Hw-Diags CLI if necessary
if ! [[ -f "/tmp/x_ok" ]]; then
echo "There was an issue starting Xorg, starting CLI interface..."
sleep 2s
hw-diags --cli
fi
else else
hw-diags --cli hw-diags --cli
fi fi

View file

@ -1,2 +0,0 @@
#Put WiFi network info here
#'WiFi SSID': 'WiFi Password'

View file

@ -16,7 +16,6 @@ e2fsprogs
hexedit hexedit
hfsprogs hfsprogs
htop htop
iwd
ldmtool ldmtool
ldns ldns
lha lha
@ -32,9 +31,7 @@ networkmanager
p7zip p7zip
progsreiserfs progsreiserfs
python python
python-docopt
python-psutil python-psutil
python-pytz
python-requests python-requests
reiserfsprogs reiserfsprogs
rfkill rfkill

View file

@ -14,7 +14,6 @@ rp-pppoe
smartmontools smartmontools
speedtouch speedtouch
testdisk testdisk
wpa_actiond
vim-minimal vim-minimal
vpnc vpnc
wvdial wvdial

View file

@ -281,10 +281,6 @@ function update_live_env() {
mkdir -p "$LIVE_DIR/airootfs/usr/share/wallpaper" mkdir -p "$LIVE_DIR/airootfs/usr/share/wallpaper"
cp "$ROOT_DIR/Images/Linux.png" "$LIVE_DIR/airootfs/usr/share/wallpaper/burned.in" cp "$ROOT_DIR/Images/Linux.png" "$LIVE_DIR/airootfs/usr/share/wallpaper/burned.in"
fi fi
# WiFi
cp "$ROOT_DIR/.linux_items/known_networks" "$LIVE_DIR/airootfs/root/known_networks"
echo "add-known-networks --user=$username" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
} }
function update_repo() { function update_repo() {