v1.8.1
Main Kit
* Added Win10 v1903 support
* System Setup script
* Replaces Install SW Bundle, New System Setup, System Checklists, and User Checklists
* Actions performed are based on the mode selected and answers given
* Improved LibreOffice installations
* Installed from a local source
* Registers all MS Office types†
* Sets default save-as options unless a config file is already present†
* † Depending on setup mode selected
* Improved performance when exporting system info
* NOTE: Event logs are no longer saved
* New 4K alignment check
* New Win10 1903 theme handling
* If updating Explorer (user) settings the Light theme is disabled
* Set to the <= 1809 default to match Classic Start theme
* Fixed issue #102
* Various other bug fixes
Linux
* Auto-connects to known WiFi networks
* ddrescue-tui (AKA WKClone and WKImage)
* Improved recovery state detection
* NOTE: percentages may be off by 0.01% due to the rounding method used
* HW-Diagnostics
* Added option to upload results for review
* Added RAM info to CPU results screen
* Added warning if one or more partitions are not 4K aligned
* Improved AMD CPU support (k8, k10, etc)
* Improved SMART detection and reporting
* New build-ufd script
* Now written in Python
* Only Linux (Full) is required, all other sources optional
* Boot options are enabled based on files present on the UFD
* Can update in place without reformatting
* Fixed issues #101-#108, & #113
* Various other bug fixes
This commit is contained in:
commit
df5a3081b9
73 changed files with 2665 additions and 1826 deletions
|
|
@ -55,8 +55,8 @@ if __name__ == '__main__':
|
|||
print_success('\nDone.')
|
||||
pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
81
.bin/Scripts/add-known-networks
Normal file
81
.bin/Scripts/add-known-networks
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#!/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
|
||||
|
|
@ -1,671 +1,149 @@
|
|||
#!/usr/bin/env bash
|
||||
#!/bin/env python3
|
||||
#
|
||||
## 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.
|
||||
|
||||
# Exit on error. Append "|| true" if you expect an error.
|
||||
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
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
|
||||
__i_am_main_script="0" # false
|
||||
|
||||
if [[ "${__usage+x}" ]]; then
|
||||
if [[ "${BASH_SOURCE[1]}" = "${0}" ]]; then
|
||||
__i_am_main_script="1" # true
|
||||
fi
|
||||
|
||||
__b3bp_external_usage="true"
|
||||
__b3bp_tmp_source_idx=1
|
||||
fi
|
||||
else
|
||||
__i_am_main_script="1" # true
|
||||
[[ "${__usage+x}" ]] && unset -v __usage
|
||||
[[ "${__helptext+x}" ]] && unset -v __helptext
|
||||
fi
|
||||
|
||||
# Set magic variables for current file, directory, os, etc.
|
||||
__dir="$(cd "$(dirname "${BASH_SOURCE[${__b3bp_tmp_source_idx:-0}]}")" && pwd)"
|
||||
__file="${__dir}/$(basename "${BASH_SOURCE[${__b3bp_tmp_source_idx:-0}]}")"
|
||||
__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
|
||||
|
||||
|
||||
# Define the environment variables (and their defaults) that this script depends on
|
||||
LOG_LEVEL="${LOG_LEVEL:-6}" # 7 = debug -> 0 = emergency
|
||||
NO_COLOR="${NO_COLOR:-}" # true = disable color. otherwise autodetected
|
||||
|
||||
|
||||
### Functions
|
||||
##############################################################################
|
||||
|
||||
function __b3bp_log () {
|
||||
local log_level="${1}"
|
||||
shift
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
local color_debug="\x1b[35m"
|
||||
# shellcheck disable=SC2034
|
||||
local color_info="\x1b[32m"
|
||||
# shellcheck disable=SC2034
|
||||
local color_notice="\x1b[34m"
|
||||
# shellcheck disable=SC2034
|
||||
local color_warning="\x1b[33m"
|
||||
# shellcheck disable=SC2034
|
||||
local color_error="\x1b[31m"
|
||||
# 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"
|
||||
|
||||
local colorvar="color_${log_level}"
|
||||
|
||||
local color="${!colorvar:-${color_error}}"
|
||||
local color_reset="\x1b[0m"
|
||||
|
||||
if [[ "${NO_COLOR:-}" = "true" ]] || ( [[ "${TERM:-}" != *"256color"* ]] && [[ "${TERM:-}" != "xterm"* ]] && [[ "${TERM:-}" != "screen"* ]] ) || [[ ! -t 2 ]]; then
|
||||
if [[ "${NO_COLOR:-}" != "false" ]]; then
|
||||
# Don't use colors on pipes or non-recognized terminals
|
||||
color=""; color_reset=""
|
||||
fi
|
||||
fi
|
||||
|
||||
# all remaining arguments are to be printed
|
||||
local log_line=""
|
||||
|
||||
while IFS=$'\n' read -r log_line; do
|
||||
echo -e "$(date -u +"%Y-%m-%d %H:%M:%S UTC") ${color}$(printf "[%9s]" "${log_level}")${color_reset} ${log_line}" 1>&2
|
||||
done <<< "${@:-}"
|
||||
}
|
||||
|
||||
function emergency () { __b3bp_log emergency "${@}"; exit 1; }
|
||||
function alert () { [[ "${LOG_LEVEL:-0}" -ge 1 ]] && __b3bp_log alert "${@}"; true; }
|
||||
function critical () { [[ "${LOG_LEVEL:-0}" -ge 2 ]] && __b3bp_log critical "${@}"; true; }
|
||||
function error () { [[ "${LOG_LEVEL:-0}" -ge 3 ]] && __b3bp_log error "${@}"; true; }
|
||||
function warning () { [[ "${LOG_LEVEL:-0}" -ge 4 ]] && __b3bp_log warning "${@}"; true; }
|
||||
function notice () { [[ "${LOG_LEVEL:-0}" -ge 5 ]] && __b3bp_log notice "${@}"; true; }
|
||||
function info () { [[ "${LOG_LEVEL:-0}" -ge 6 ]] && __b3bp_log info "${@}"; true; }
|
||||
function debug () { [[ "${LOG_LEVEL:-0}" -ge 7 ]] && __b3bp_log debug "${@}"; true; }
|
||||
|
||||
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
|
||||
# pylint: disable=no-name-in-module,wildcard-import,wrong-import-position
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
"""Wizard Kit: UFD build tool"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Init
|
||||
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||
from docopt import docopt
|
||||
from functions.common import *
|
||||
from functions.ufd import *
|
||||
from settings.ufd import *
|
||||
init_global_vars(silent=True)
|
||||
|
||||
# Main section
|
||||
if __name__ == '__main__':
|
||||
# pylint: disable=invalid-name
|
||||
# Set log
|
||||
try:
|
||||
global_vars['LogDir'] = '{}/Logs'.format(
|
||||
get_user_home(get_user_name()))
|
||||
set_log_file('Build UFD ({Date-Time}).log'.format(**global_vars))
|
||||
except: # pylint: disable=bare-except
|
||||
major_exception()
|
||||
|
||||
# Header
|
||||
print_success(KIT_NAME_FULL)
|
||||
print_standard('UFD Build Tool')
|
||||
print_standard(' ')
|
||||
|
||||
# Check if running as root
|
||||
if not running_as_root():
|
||||
print_error('ERROR: This script is meant to be run as root.')
|
||||
abort(False)
|
||||
|
||||
# Docopt
|
||||
try:
|
||||
args = docopt(DOCSTRING)
|
||||
except SystemExit as sys_exit:
|
||||
# Catch docopt exits
|
||||
exit_script(sys_exit.code)
|
||||
except: # pylint: disable=bare-except
|
||||
major_exception()
|
||||
|
||||
try:
|
||||
# Verify selections
|
||||
ufd_dev = verify_ufd(args['--ufd-device'])
|
||||
sources = verify_sources(args, UFD_SOURCES)
|
||||
show_selections(args, sources, ufd_dev, UFD_SOURCES)
|
||||
if not args['--force']:
|
||||
confirm_selections(args)
|
||||
|
||||
# Prep UFD
|
||||
if not args['--update']:
|
||||
print_info('Prep UFD')
|
||||
prep_device(ufd_dev, UFD_LABEL, use_mbr=args['--use-mbr'])
|
||||
|
||||
# Mount UFD
|
||||
try_and_print(
|
||||
indent=2,
|
||||
message='Mounting UFD...',
|
||||
function=mount,
|
||||
mount_source=find_first_partition(ufd_dev),
|
||||
mount_point='/mnt/UFD',
|
||||
read_write=True,
|
||||
)
|
||||
|
||||
# Remove Arch folder
|
||||
if args['--update']:
|
||||
try_and_print(
|
||||
indent=2,
|
||||
message='Removing Linux...',
|
||||
function=remove_arch,
|
||||
)
|
||||
|
||||
# Copy sources
|
||||
print_standard(' ')
|
||||
print_info('Copy Sources')
|
||||
for s_label, s_path in sources.items():
|
||||
try_and_print(
|
||||
indent=2,
|
||||
message='Copying {}...'.format(s_label),
|
||||
function=copy_source,
|
||||
source=s_path,
|
||||
items=ITEMS[s_label],
|
||||
overwrite=True,
|
||||
)
|
||||
|
||||
# Update boot entries
|
||||
print_standard(' ')
|
||||
print_info('Boot Setup')
|
||||
try_and_print(
|
||||
indent=2,
|
||||
message='Updating boot entries...',
|
||||
function=update_boot_entries,
|
||||
boot_entries=BOOT_ENTRIES,
|
||||
boot_files=BOOT_FILES,
|
||||
iso_label=ISO_LABEL,
|
||||
ufd_label=UFD_LABEL,
|
||||
)
|
||||
|
||||
# Install syslinux (to partition)
|
||||
try_and_print(
|
||||
indent=2,
|
||||
message='Syslinux (partition)...',
|
||||
function=install_syslinux_to_partition,
|
||||
partition=find_first_partition(ufd_dev),
|
||||
)
|
||||
|
||||
# Unmount UFD
|
||||
try_and_print(
|
||||
indent=2,
|
||||
message='Unmounting UFD...',
|
||||
function=unmount,
|
||||
mount_point='/mnt/UFD',
|
||||
)
|
||||
|
||||
# Install syslinux (to device)
|
||||
try_and_print(
|
||||
indent=2,
|
||||
message='Syslinux (device)...',
|
||||
function=install_syslinux_to_dev,
|
||||
ufd_dev=ufd_dev,
|
||||
use_mbr=args['--use-mbr'],
|
||||
)
|
||||
|
||||
# Hide items
|
||||
print_standard(' ')
|
||||
print_info('Final Touches')
|
||||
try_and_print(
|
||||
indent=2,
|
||||
message='Hiding items...',
|
||||
function=hide_items,
|
||||
ufd_dev=ufd_dev,
|
||||
items=ITEMS_HIDDEN,
|
||||
)
|
||||
|
||||
# Done
|
||||
if not args['--force']:
|
||||
print_standard('\nDone.')
|
||||
pause('Press Enter to exit...')
|
||||
exit_script()
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except: # pylint: disable=bare-except
|
||||
major_exception()
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ if __name__ == '__main__':
|
|||
print_standard('\nDone.')
|
||||
pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ if __name__ == '__main__':
|
|||
if repair:
|
||||
cs = 'Scheduled'
|
||||
else:
|
||||
cs = 'CS'
|
||||
cs = 'No issues'
|
||||
message = 'CHKDSK ({SYSTEMDRIVE})...'.format(**global_vars['Env'])
|
||||
try_and_print(message=message, function=run_chkdsk,
|
||||
cs=cs, other_results=other_results, repair=repair)
|
||||
|
|
@ -49,8 +49,8 @@ if __name__ == '__main__':
|
|||
print_success('Done.')
|
||||
pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
#!/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
|
||||
|
|
@ -54,8 +54,8 @@ if __name__ == '__main__':
|
|||
msg = str(ge)
|
||||
print_error(msg)
|
||||
abort()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -149,11 +149,14 @@ def save_debug_reports(state, global_vars):
|
|||
f.write('{}\n'.format(line))
|
||||
|
||||
|
||||
def upload_logdir(global_vars):
|
||||
def upload_logdir(global_vars, reason='Crash'):
|
||||
"""Upload compressed LogDir to CRASH_SERVER."""
|
||||
source = global_vars['LogDir']
|
||||
source = source[source.rfind('/')+1:]
|
||||
dest = '{}.txz'.format(source)
|
||||
dest = 'HW-Diags_{reason}_{Date-Time}.txz'.format(
|
||||
reason=reason,
|
||||
**global_vars,
|
||||
)
|
||||
data = None
|
||||
|
||||
# Compress LogDir
|
||||
|
|
@ -166,7 +169,7 @@ def upload_logdir(global_vars):
|
|||
data = f.read()
|
||||
|
||||
# Upload data
|
||||
url = '{}/Crash_{}.txz'.format(CRASH_SERVER['Url'], source)
|
||||
url = '{}/{}'.format(CRASH_SERVER['Url'], dest)
|
||||
r = requests.put(
|
||||
url,
|
||||
data=data,
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ if __name__ == '__main__':
|
|||
print_success('Done.')
|
||||
pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ def archive_all_users():
|
|||
user_path = os.path.join(users_root, user_name)
|
||||
appdata_local = os.path.join(user_path, r'AppData\Local')
|
||||
appdata_roaming = os.path.join(user_path, r'AppData\Roaming')
|
||||
valid_user &= os.path.exists(appdata_local)
|
||||
valid_user &= os.path.exists(appdata_roaming)
|
||||
valid_user = valid_user and os.path.exists(appdata_local)
|
||||
valid_user = valid_user and os.path.exists(appdata_roaming)
|
||||
if valid_user:
|
||||
user_envs.append({
|
||||
'USERNAME': user_name,
|
||||
|
|
@ -325,7 +325,6 @@ def install_adblock(indent=8, width=32, just_firefox=False):
|
|||
if just_firefox and browser_data[browser]['base'] != 'mozilla':
|
||||
continue
|
||||
exe_path = browser_data[browser].get('exe_path', None)
|
||||
function=run_program
|
||||
if not exe_path:
|
||||
if browser_data[browser]['profiles']:
|
||||
print_standard(
|
||||
|
|
@ -375,7 +374,6 @@ def install_adblock(indent=8, width=32, just_firefox=False):
|
|||
|
||||
elif browser_data[browser]['base'] == 'ie':
|
||||
urls.append(IE_GALLERY)
|
||||
function=popen_program
|
||||
|
||||
# By using check=False we're skipping any return codes so
|
||||
# it should only fail if the program can't be run
|
||||
|
|
@ -384,10 +382,16 @@ def install_adblock(indent=8, width=32, just_firefox=False):
|
|||
# installation status.
|
||||
try_and_print(message='{}...'.format(browser),
|
||||
indent=indent, width=width,
|
||||
cs='Done', function=function,
|
||||
cs='Started', function=popen_program,
|
||||
cmd=[exe_path, *urls], check=False)
|
||||
|
||||
|
||||
def is_installed(browser_name):
|
||||
"""Checks if browser is installed based on exe_path, returns bool."""
|
||||
browser_name = browser_name.replace(' Chromium', '')
|
||||
return bool(browser_data.get(browser_name, {}).get('exe_path', False))
|
||||
|
||||
|
||||
def list_homepages(indent=8, width=32):
|
||||
"""List current homepages for reference."""
|
||||
browser_list = [k for k, v in sorted(browser_data.items()) if v['exe_path']]
|
||||
|
|
@ -419,6 +423,12 @@ def list_homepages(indent=8, width=32):
|
|||
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):
|
||||
"""Reset all detected browsers to safe defaults."""
|
||||
browser_list = [k for k, v in sorted(browser_data.items()) if v['profiles']]
|
||||
|
|
@ -437,14 +447,21 @@ def reset_browsers(indent=8, width=32):
|
|||
other_results=other_results, profile=profile)
|
||||
|
||||
|
||||
def scan_for_browsers(just_firefox=False):
|
||||
def scan_for_browsers(just_firefox=False, silent=False):
|
||||
"""Scan system for any supported browsers."""
|
||||
for name, details in sorted(SUPPORTED_BROWSERS.items()):
|
||||
if just_firefox and details['base'] != 'mozilla':
|
||||
continue
|
||||
try_and_print(message='{}...'.format(name),
|
||||
function=get_browser_details, cs='Detected',
|
||||
other_results=other_results, name=name)
|
||||
if silent:
|
||||
try:
|
||||
get_browser_details(name)
|
||||
except Exception:
|
||||
# Ignore errors in silent mode
|
||||
pass
|
||||
else:
|
||||
try_and_print(message='{}...'.format(name),
|
||||
function=get_browser_details, cs='Detected',
|
||||
other_results=other_results, name=name)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
# Wizard Kit: Functions - Cleanup
|
||||
|
||||
from functions.common import *
|
||||
'''Wizard Kit: Functions - Cleanup'''
|
||||
# pylint: disable=no-name-in-module,wildcard-import
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
from functions.setup import *
|
||||
from settings.cleanup import *
|
||||
|
||||
def cleanup_adwcleaner():
|
||||
"""Move AdwCleaner folders into the ClientDir."""
|
||||
|
|
@ -75,8 +77,7 @@ def cleanup_desktop():
|
|||
|
||||
desktop_path = r'{USERPROFILE}\Desktop'.format(**global_vars['Env'])
|
||||
for entry in os.scandir(desktop_path):
|
||||
# JRT, RKill, Shortcut cleaner
|
||||
if re.search(r'^(JRT|RKill|sc-cleaner)', entry.name, re.IGNORECASE):
|
||||
if DESKTOP_ITEMS.search(entry.name):
|
||||
dest_name = r'{}\{}'.format(dest_folder, entry.name)
|
||||
dest_name = non_clobber_rename(dest_name)
|
||||
shutil.move(entry.path, dest_name)
|
||||
|
|
@ -130,7 +131,14 @@ def delete_registry_value(hive, key, 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__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
|
|
|||
|
|
@ -64,10 +64,13 @@ class GenericRepair(Exception):
|
|||
class MultipleInstallationsError(Exception):
|
||||
pass
|
||||
|
||||
class NotInstalledError(Exception):
|
||||
class NoProfilesError(Exception):
|
||||
pass
|
||||
|
||||
class NoProfilesError(Exception):
|
||||
class Not4KAlignedError(Exception):
|
||||
pass
|
||||
|
||||
class NotInstalledError(Exception):
|
||||
pass
|
||||
|
||||
class OSInstalledLegacyError(Exception):
|
||||
|
|
@ -88,14 +91,21 @@ class SecureBootNotAvailError(Exception):
|
|||
class SecureBootUnknownError(Exception):
|
||||
pass
|
||||
|
||||
class WindowsOutdatedError(Exception):
|
||||
pass
|
||||
|
||||
class WindowsUnsupportedError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# General functions
|
||||
def abort():
|
||||
def abort(show_prompt=True):
|
||||
"""Abort script."""
|
||||
print_warning('Aborted.')
|
||||
sleep(1)
|
||||
pause(prompt='Press Enter to exit... ')
|
||||
exit_script()
|
||||
if show_prompt:
|
||||
sleep(1)
|
||||
pause(prompt='Press Enter to exit... ')
|
||||
exit_script(1)
|
||||
|
||||
|
||||
def ask(prompt='Kotaero!'):
|
||||
|
|
@ -163,18 +173,22 @@ def clear_screen():
|
|||
def convert_to_bytes(size):
|
||||
"""Convert human-readable size str to bytes and return an int."""
|
||||
size = str(size)
|
||||
tmp = re.search(r'(\d+\.?\d*)\s+([KMGT]B)', size.upper())
|
||||
tmp = re.search(r'(\d+\.?\d*)\s+([PTGMKB])B?', size.upper())
|
||||
if tmp:
|
||||
size = float(tmp.group(1))
|
||||
units = tmp.group(2)
|
||||
if units == 'TB':
|
||||
size *= 1099511627776
|
||||
elif units == 'GB':
|
||||
size *= 1073741824
|
||||
elif units == 'MB':
|
||||
size *= 1048576
|
||||
elif units == 'KB':
|
||||
size *= 1024
|
||||
if units == 'P':
|
||||
size *= 1024 ** 5
|
||||
if units == 'T':
|
||||
size *= 1024 ** 4
|
||||
elif units == 'G':
|
||||
size *= 1024 ** 3
|
||||
elif units == 'M':
|
||||
size *= 1024 ** 2
|
||||
elif units == 'K':
|
||||
size *= 1024 ** 1
|
||||
elif units == 'B':
|
||||
size *= 1024 ** 0
|
||||
size = int(size)
|
||||
else:
|
||||
return -1
|
||||
|
|
@ -293,20 +307,24 @@ def human_readable_size(size, decimals=0):
|
|||
return '{size:>{width}} b'.format(size='???', width=width)
|
||||
|
||||
# Convert to sensible units
|
||||
if size >= 1099511627776:
|
||||
size /= 1099511627776
|
||||
units = 'Tb'
|
||||
elif size >= 1073741824:
|
||||
size /= 1073741824
|
||||
units = 'Gb'
|
||||
elif size >= 1048576:
|
||||
size /= 1048576
|
||||
units = 'Mb'
|
||||
elif size >= 1024:
|
||||
size /= 1024
|
||||
units = 'Kb'
|
||||
if size >= 1024 ** 5:
|
||||
size /= 1024 ** 5
|
||||
units = 'PB'
|
||||
elif size >= 1024 ** 4:
|
||||
size /= 1024 ** 4
|
||||
units = 'TB'
|
||||
elif size >= 1024 ** 3:
|
||||
size /= 1024 ** 3
|
||||
units = 'GB'
|
||||
elif size >= 1024 ** 2:
|
||||
size /= 1024 ** 2
|
||||
units = 'MB'
|
||||
elif size >= 1024 ** 1:
|
||||
size /= 1024 ** 1
|
||||
units = 'KB'
|
||||
else:
|
||||
units = ' b'
|
||||
size /= 1024 ** 0
|
||||
units = ' B'
|
||||
|
||||
# Return
|
||||
return '{size:>{width}.{decimals}f} {units}'.format(
|
||||
|
|
@ -421,6 +439,8 @@ def non_clobber_rename(full_path):
|
|||
|
||||
def pause(prompt='Press Enter to continue... '):
|
||||
"""Simple pause implementation."""
|
||||
if prompt[-1] != ' ':
|
||||
prompt += ' '
|
||||
input(prompt)
|
||||
|
||||
|
||||
|
|
@ -879,25 +899,19 @@ def make_tmp_dirs():
|
|||
|
||||
def set_common_vars():
|
||||
"""Set common variables."""
|
||||
global_vars['Date'] = time.strftime("%Y-%m-%d")
|
||||
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
|
||||
global_vars['Env'] = os.environ.copy()
|
||||
global_vars['Date'] = time.strftime("%Y-%m-%d")
|
||||
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
|
||||
global_vars['Env'] = os.environ.copy()
|
||||
|
||||
global_vars['ArchivePassword'] = ARCHIVE_PASSWORD
|
||||
global_vars['BinDir'] = r'{BaseDir}\.bin'.format(
|
||||
**global_vars)
|
||||
global_vars['CBinDir'] = r'{BaseDir}\.cbin'.format(
|
||||
**global_vars)
|
||||
global_vars['ClientDir'] = r'{SYSTEMDRIVE}\{prefix}'.format(
|
||||
prefix=KIT_NAME_SHORT, **global_vars['Env'])
|
||||
global_vars['BackupDir'] = r'{ClientDir}\Backups'.format(
|
||||
**global_vars)
|
||||
global_vars['LogDir'] = r'{ClientDir}\Logs\{Date}'.format(
|
||||
**global_vars)
|
||||
global_vars['QuarantineDir'] = r'{ClientDir}\Quarantine'.format(
|
||||
**global_vars)
|
||||
global_vars['TmpDir'] = r'{BinDir}\tmp'.format(
|
||||
**global_vars)
|
||||
global_vars['BinDir'] = r'{BaseDir}\.bin'.format(**global_vars)
|
||||
global_vars['CBinDir'] = r'{BaseDir}\.cbin'.format(**global_vars)
|
||||
global_vars['ClientDir'] = r'{SYSTEMDRIVE}\{prefix}'.format(
|
||||
prefix=KIT_NAME_SHORT, **global_vars['Env'])
|
||||
global_vars['BackupDir'] = r'{ClientDir}\Backups'.format(**global_vars)
|
||||
global_vars['LogDir'] = r'{ClientDir}\Logs\{Date}'.format(**global_vars)
|
||||
global_vars['QuarantineDir'] = r'{ClientDir}\Quarantine'.format(**global_vars)
|
||||
global_vars['TmpDir'] = r'{BinDir}\tmp'.format(**global_vars)
|
||||
|
||||
|
||||
def set_linux_vars():
|
||||
|
|
@ -905,12 +919,12 @@ def set_linux_vars():
|
|||
|
||||
These assume we're running under a WK-Linux build."""
|
||||
result = run_program(['mktemp', '-d'])
|
||||
global_vars['TmpDir'] = result.stdout.decode().strip()
|
||||
global_vars['Date'] = time.strftime("%Y-%m-%d")
|
||||
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
|
||||
global_vars['Env'] = os.environ.copy()
|
||||
global_vars['BinDir'] = '/usr/local/bin'
|
||||
global_vars['LogDir'] = global_vars['TmpDir']
|
||||
global_vars['TmpDir'] = result.stdout.decode().strip()
|
||||
global_vars['Date'] = time.strftime("%Y-%m-%d")
|
||||
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
|
||||
global_vars['Env'] = os.environ.copy()
|
||||
global_vars['BinDir'] = '/usr/local/bin'
|
||||
global_vars['LogDir'] = '{}/Logs'.format(global_vars['Env']['HOME'])
|
||||
global_vars['Tools'] = {
|
||||
'wimlib-imagex': 'wimlib-imagex',
|
||||
'SevenZip': '7z',
|
||||
|
|
@ -919,10 +933,13 @@ def set_linux_vars():
|
|||
|
||||
def set_log_file(log_name):
|
||||
"""Sets global var LogFile and creates path as needed."""
|
||||
folder_path = '{}{}{}'.format(
|
||||
global_vars['LogDir'],
|
||||
os.sep,
|
||||
KIT_NAME_FULL)
|
||||
if psutil.LINUX:
|
||||
folder_path = global_vars['LogDir']
|
||||
else:
|
||||
folder_path = '{}{}{}'.format(
|
||||
global_vars['LogDir'],
|
||||
os.sep,
|
||||
KIT_NAME_FULL)
|
||||
log_file = '{}{}{}'.format(
|
||||
folder_path,
|
||||
os.sep,
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ def find_core_storage_volumes(device_path=None):
|
|||
|
||||
# Check log for found volumes
|
||||
cs_vols = {}
|
||||
with open(log_path, 'r') as f:
|
||||
with open(log_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
for line in f.readlines():
|
||||
r = re.match(
|
||||
r'^.*echo "([^"]+)" . dmsetup create test(\d)$',
|
||||
|
|
@ -151,12 +151,16 @@ def is_valid_wim_file(item):
|
|||
def get_mounted_volumes():
|
||||
"""Get mounted volumes, returns dict."""
|
||||
cmd = [
|
||||
'findmnt', '-J', '-b', '-i',
|
||||
'-t', (
|
||||
'findmnt',
|
||||
'--list',
|
||||
'--json',
|
||||
'--bytes',
|
||||
'--invert',
|
||||
'--types', (
|
||||
'autofs,binfmt_misc,bpf,cgroup,cgroup2,configfs,debugfs,devpts,'
|
||||
'devtmpfs,hugetlbfs,mqueue,proc,pstore,securityfs,sysfs,tmpfs'
|
||||
),
|
||||
'-o', 'SOURCE,TARGET,FSTYPE,LABEL,SIZE,AVAIL,USED']
|
||||
'--output', 'SOURCE,TARGET,FSTYPE,LABEL,SIZE,AVAIL,USED']
|
||||
json_data = get_json_from_command(cmd)
|
||||
mounted_volumes = []
|
||||
for item in json_data.get('filesystems', []):
|
||||
|
|
@ -195,6 +199,8 @@ def mount_volumes(
|
|||
volumes.update({child['name']: child})
|
||||
for grandchild in child.get('children', []):
|
||||
volumes.update({grandchild['name']: grandchild})
|
||||
for great_grandchild in grandchild.get('children', []):
|
||||
volumes.update({great_grandchild['name']: great_grandchild})
|
||||
|
||||
# Get list of mounted volumes
|
||||
mounted_volumes = get_mounted_volumes()
|
||||
|
|
@ -212,7 +218,7 @@ def mount_volumes(
|
|||
report[vol_path] = vol_data
|
||||
elif 'children' in vol_data:
|
||||
# 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):
|
||||
vol_data['show_data']['data'] += ' "{}"'.format(vol_data['label'])
|
||||
vol_data['show_data']['info'] = True
|
||||
|
|
@ -237,17 +243,23 @@ def mount_volumes(
|
|||
if vol_data['show_data']['data'] == 'Failed to mount':
|
||||
vol_data['mount_point'] = None
|
||||
else:
|
||||
fstype = vol_data.get('fstype', 'Unknown FS')
|
||||
size_used = human_readable_size(
|
||||
mounted_volumes[vol_path]['used'])
|
||||
mounted_volumes[vol_path]['used'],
|
||||
decimals=1,
|
||||
)
|
||||
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_used'] = size_used
|
||||
vol_data['mount_point'] = mounted_volumes[vol_path]['target']
|
||||
vol_data['show_data']['data'] = 'Mounted on {}'.format(
|
||||
mounted_volumes[vol_path]['target'])
|
||||
vol_data['show_data']['data'] = '{:40} ({} used, {} free)'.format(
|
||||
vol_data['show_data']['data'] = '{:40} ({}, {} used, {} free)'.format(
|
||||
vol_data['show_data']['data'],
|
||||
fstype,
|
||||
size_used,
|
||||
size_avail)
|
||||
|
||||
|
|
@ -281,6 +293,14 @@ def mount_backup_shares(read_write=False):
|
|||
|
||||
def mount_network_share(server, read_write=False):
|
||||
"""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:
|
||||
username = server['RW-User']
|
||||
password = server['RW-Pass']
|
||||
|
|
@ -296,18 +316,35 @@ def mount_network_share(server, read_write=False):
|
|||
error = r'Failed to mount \\{Name}\{Share} ({IP})'.format(**server)
|
||||
success = 'Mounted {Name}'.format(**server)
|
||||
elif psutil.LINUX:
|
||||
# Make mountpoint
|
||||
cmd = [
|
||||
'sudo', 'mkdir', '-p',
|
||||
'/Backups/{Name}'.format(**server)]
|
||||
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 = [
|
||||
'sudo', 'mount',
|
||||
'//{IP}/{Share}'.format(**server),
|
||||
'//{IP}/{Share}'.format(**server).replace('\\', '/'),
|
||||
'/Backups/{Name}'.format(**server),
|
||||
'-o', '{}username={},password={}'.format(
|
||||
'' if read_write else 'ro,',
|
||||
username,
|
||||
password)]
|
||||
'-o', ','.join(cmd_options),
|
||||
]
|
||||
|
||||
# Set result messages
|
||||
warning = 'Failed to mount /Backups/{Name}, {IP} unreachable.'.format(
|
||||
**server)
|
||||
error = 'Failed to mount /Backups/{Name}'.format(**server)
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
# Wizard Kit: Functions - ddrescue-tui
|
||||
# pylint: disable=no-name-in-module,too-many-lines,wildcard-import
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
'''Wizard Kit: Functions - ddrescue-tui'''
|
||||
|
||||
import datetime
|
||||
import pathlib
|
||||
import psutil
|
||||
import pytz
|
||||
import re
|
||||
import signal
|
||||
import stat
|
||||
import time
|
||||
from operator import itemgetter
|
||||
|
||||
from collections import OrderedDict
|
||||
import pytz
|
||||
from functions.data import *
|
||||
from functions.hw_diags import *
|
||||
from functions.json import *
|
||||
from functions.tmux import *
|
||||
from operator import itemgetter
|
||||
from settings.ddrescue import *
|
||||
|
||||
|
||||
# Clases
|
||||
class BaseObj():
|
||||
# pylint: disable=missing-docstring
|
||||
"""Base object used by DevObj, DirObj, and ImageObj."""
|
||||
def __init__(self, path):
|
||||
self.type = 'base'
|
||||
|
|
@ -44,6 +44,7 @@ class BaseObj():
|
|||
|
||||
|
||||
class BlockPair():
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
"""Object to track data and methods together for source and dest."""
|
||||
def __init__(self, mode, source, dest):
|
||||
self.mode = mode
|
||||
|
|
@ -60,9 +61,10 @@ class BlockPair():
|
|||
if self.mode == 'clone':
|
||||
# Cloning
|
||||
self.dest_path = dest.path
|
||||
self.map_path = '{pwd}/Clone_{prefix}.map'.format(
|
||||
pwd=os.path.realpath(global_vars['Env']['PWD']),
|
||||
prefix=source.prefix)
|
||||
self.map_path = '{cwd}/Clone_{prefix}.map'.format(
|
||||
cwd=os.path.realpath(os.getcwd()),
|
||||
prefix=source.prefix,
|
||||
)
|
||||
else:
|
||||
# Imaging
|
||||
self.dest_path = '{path}/{prefix}.dd'.format(
|
||||
|
|
@ -105,19 +107,19 @@ class BlockPair():
|
|||
def load_map_data(self):
|
||||
"""Load data from map file and set progress."""
|
||||
map_data = read_map_file(self.map_path)
|
||||
self.rescued_percent = map_data['rescued']
|
||||
self.rescued = (self.rescued_percent * self.size) / 100
|
||||
self.rescued = map_data.get('rescued', 0)
|
||||
self.rescued_percent = (self.rescued / self.size) * 100
|
||||
if map_data['full recovery']:
|
||||
self.pass_done = [True, True, True]
|
||||
self.rescued = self.size
|
||||
self.status = ['Skipped', 'Skipped', 'Skipped']
|
||||
elif map_data['non-tried'] > 0:
|
||||
elif map_data.get('non-tried', 0) > 0:
|
||||
# Initial pass incomplete
|
||||
pass
|
||||
elif map_data['non-trimmed'] > 0:
|
||||
elif map_data.get('non-trimmed', 0) > 0:
|
||||
self.pass_done = [True, False, False]
|
||||
self.status = ['Skipped', 'Pending', 'Pending']
|
||||
elif map_data['non-scraped'] > 0:
|
||||
elif map_data.get('non-scraped', 0) > 0:
|
||||
self.pass_done = [True, True, False]
|
||||
self.status = ['Skipped', 'Skipped', 'Pending']
|
||||
else:
|
||||
|
|
@ -145,14 +147,15 @@ class BlockPair():
|
|||
"""Update progress using map file."""
|
||||
if os.path.exists(self.map_path):
|
||||
map_data = read_map_file(self.map_path)
|
||||
self.rescued_percent = map_data.get('rescued', 0)
|
||||
self.rescued = (self.rescued_percent * self.size) / 100
|
||||
self.rescued = map_data.get('rescued', 0)
|
||||
self.rescued_percent = (self.rescued / self.size) * 100
|
||||
self.status[pass_num] = get_formatted_status(
|
||||
label='Pass {}'.format(pass_num+1),
|
||||
data=(self.rescued/self.size)*100)
|
||||
|
||||
|
||||
class DevObj(BaseObj):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
"""Block device object."""
|
||||
def self_check(self):
|
||||
"""Verify that self.path points to a block device."""
|
||||
|
|
@ -186,6 +189,7 @@ class DevObj(BaseObj):
|
|||
self.update_filename_prefix()
|
||||
|
||||
def update_filename_prefix(self):
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
"""Set filename prefix based on details."""
|
||||
self.prefix = '{m_size}_{model}'.format(
|
||||
m_size=self.model_size,
|
||||
|
|
@ -205,6 +209,7 @@ class DevObj(BaseObj):
|
|||
|
||||
|
||||
class DirObj(BaseObj):
|
||||
"""Directory object."""
|
||||
def self_check(self):
|
||||
"""Verify that self.path points to a directory."""
|
||||
if not pathlib.Path(self.path).is_dir():
|
||||
|
|
@ -222,6 +227,7 @@ class DirObj(BaseObj):
|
|||
|
||||
|
||||
class ImageObj(BaseObj):
|
||||
"""Image file object."""
|
||||
def self_check(self):
|
||||
"""Verify that self.path points to a file."""
|
||||
if not pathlib.Path(self.path).is_file():
|
||||
|
|
@ -243,10 +249,11 @@ class ImageObj(BaseObj):
|
|||
self.report = get_device_report(self.loop_dev)
|
||||
self.report = self.report.replace(
|
||||
self.loop_dev[self.loop_dev.rfind('/')+1:], '(Img)')
|
||||
run_program(['losetup', '--detach', self.loop_dev], check=False)
|
||||
run_program(['sudo', 'losetup', '--detach', self.loop_dev], check=False)
|
||||
|
||||
|
||||
class RecoveryState():
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
"""Object to track BlockPair objects and overall state."""
|
||||
def __init__(self, mode, source, dest):
|
||||
self.mode = mode.lower()
|
||||
|
|
@ -270,6 +277,7 @@ class RecoveryState():
|
|||
if mode not in ('clone', 'image'):
|
||||
raise GenericError('Unsupported mode')
|
||||
self.get_smart_source()
|
||||
self.set_working_dir()
|
||||
|
||||
def add_block_pair(self, source, dest):
|
||||
"""Run safety checks and append new BlockPair to internal list."""
|
||||
|
|
@ -314,20 +322,134 @@ class RecoveryState():
|
|||
# Safety checks passed
|
||||
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):
|
||||
"""Checks if pass is done for all block-pairs, returns bool."""
|
||||
done = True
|
||||
for bp in self.block_pairs:
|
||||
done &= bp.pass_done[self.current_pass]
|
||||
for b_pair in self.block_pairs:
|
||||
done = done and b_pair.pass_done[self.current_pass]
|
||||
return done
|
||||
|
||||
def current_pass_min(self):
|
||||
"""Gets minimum pass rescued percentage, returns float."""
|
||||
min_percent = 100
|
||||
for bp in self.block_pairs:
|
||||
min_percent = min(min_percent, bp.rescued_percent)
|
||||
for b_pair in self.block_pairs:
|
||||
min_percent = min(min_percent, b_pair.rescued_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):
|
||||
"""Get source for SMART dispay."""
|
||||
disk_path = self.source.path
|
||||
|
|
@ -339,18 +461,15 @@ class RecoveryState():
|
|||
def retry_all_passes(self):
|
||||
"""Mark all passes as pending for all block-pairs."""
|
||||
self.finished = False
|
||||
for bp in self.block_pairs:
|
||||
bp.pass_done = [False, False, False]
|
||||
bp.status = ['Pending', 'Pending', 'Pending']
|
||||
bp.fix_status_strings()
|
||||
for b_pair in self.block_pairs:
|
||||
b_pair.pass_done = [False, False, False]
|
||||
b_pair.status = ['Pending', 'Pending', 'Pending']
|
||||
b_pair.fix_status_strings()
|
||||
self.set_pass_num()
|
||||
|
||||
def self_checks(self):
|
||||
"""Run self-checks and update state values."""
|
||||
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)
|
||||
|
||||
# Abort if json_data is empty
|
||||
|
|
@ -361,23 +480,24 @@ class RecoveryState():
|
|||
# Avoid saving map to non-persistent filesystem
|
||||
fstype = json_data.get(
|
||||
'filesystems', [{}])[0].get(
|
||||
'fstype', 'unknown')
|
||||
if fstype not in map_allowed_fstypes:
|
||||
'fstype', 'unknown')
|
||||
if fstype not in RECOMMENDED_MAP_FSTYPES:
|
||||
print_error(
|
||||
"Map isn't being saved to a recommended filesystem ({})".format(
|
||||
fstype.upper()))
|
||||
print_info('Recommended types are: {}'.format(
|
||||
' / '.join(map_allowed_fstypes).upper()))
|
||||
' / '.join(RECOMMENDED_MAP_FSTYPES).upper()))
|
||||
print_standard(' ')
|
||||
if not ask('Proceed anyways? (Strongly discouraged)'):
|
||||
raise GenericAbort()
|
||||
|
||||
# Run BlockPair self checks and get total size
|
||||
self.total_size = 0
|
||||
for bp in self.block_pairs:
|
||||
bp.self_check()
|
||||
self.resumed |= bp.resumed
|
||||
self.total_size += bp.size
|
||||
for b_pair in self.block_pairs:
|
||||
b_pair.self_check()
|
||||
if b_pair.resumed:
|
||||
self.resumed = True
|
||||
self.total_size += b_pair.size
|
||||
|
||||
def set_pass_num(self):
|
||||
"""Set current pass based on all block-pair's progress."""
|
||||
|
|
@ -385,8 +505,8 @@ class RecoveryState():
|
|||
for pass_num in (2, 1, 0):
|
||||
# Iterate backwards through passes
|
||||
pass_done = True
|
||||
for bp in self.block_pairs:
|
||||
pass_done &= bp.pass_done[pass_num]
|
||||
for b_pair in self.block_pairs:
|
||||
pass_done = pass_done and b_pair.pass_done[pass_num]
|
||||
if pass_done:
|
||||
# All block-pairs reported being done
|
||||
# Set to next pass, unless we're on the last pass (2)
|
||||
|
|
@ -404,6 +524,34 @@ class RecoveryState():
|
|||
elif self.current_pass == 2:
|
||||
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):
|
||||
"""Search ddrescue output for the current EToC, returns str."""
|
||||
now = datetime.datetime.now(tz=self.timezone)
|
||||
|
|
@ -413,7 +561,7 @@ class RecoveryState():
|
|||
# Just set to N/A (NOTE: this overrules the refresh rate below)
|
||||
self.etoc = 'N/A'
|
||||
return
|
||||
elif 'In Progress' not in self.status:
|
||||
if 'In Progress' not in self.status:
|
||||
# Don't update when EToC is hidden
|
||||
return
|
||||
if now.second % ETOC_REFRESH_RATE != 0:
|
||||
|
|
@ -427,13 +575,14 @@ class RecoveryState():
|
|||
# Capture main tmux pane
|
||||
try:
|
||||
text = tmux_capture_pane()
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# Ignore
|
||||
pass
|
||||
|
||||
# Search for EToC delta
|
||||
matches = re.findall(r'remaining time:.*$', text, re.MULTILINE)
|
||||
if matches:
|
||||
# pylint: disable=invalid-name
|
||||
r = REGEX_REMAINING_TIME.search(matches[-1])
|
||||
if r.group('na'):
|
||||
self.etoc = 'N/A'
|
||||
|
|
@ -450,7 +599,7 @@ class RecoveryState():
|
|||
minutes=int(minutes),
|
||||
seconds=int(seconds),
|
||||
)
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# Ignore and leave as raw string
|
||||
pass
|
||||
|
||||
|
|
@ -460,15 +609,16 @@ class RecoveryState():
|
|||
now = datetime.datetime.now(tz=self.timezone)
|
||||
_etoc = now + etoc_delta
|
||||
self.etoc = _etoc.strftime('%Y-%m-%d %H:%M %Z')
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# Ignore and leave as current string
|
||||
pass
|
||||
|
||||
def update_progress(self):
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
"""Update overall progress using block_pairs."""
|
||||
self.rescued = 0
|
||||
for bp in self.block_pairs:
|
||||
self.rescued += bp.rescued
|
||||
for b_pair in self.block_pairs:
|
||||
self.rescued += b_pair.rescued
|
||||
self.rescued_percent = (self.rescued / self.total_size) * 100
|
||||
self.status_percent = get_formatted_status(
|
||||
label='Recovered:', data=self.rescued_percent)
|
||||
|
|
@ -477,26 +627,6 @@ class RecoveryState():
|
|||
|
||||
|
||||
# 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):
|
||||
"""Create Dev, Dir, or Image obj based on path given."""
|
||||
obj = None
|
||||
|
|
@ -514,101 +644,16 @@ def create_path_obj(path):
|
|||
def double_confirm_clone():
|
||||
"""Display warning and get 2nd confirmation, returns bool."""
|
||||
print_standard('\nSAFETY CHECK')
|
||||
print_warning('All data will be DELETED from the '
|
||||
'destination device and partition(s) listed above.')
|
||||
print_warning('This is irreversible and will lead '
|
||||
'to {CLEAR}{RED}DATA LOSS.'.format(**COLORS))
|
||||
print_warning(
|
||||
'All data will be DELETED from the '
|
||||
'destination device and partition(s) listed above.'
|
||||
)
|
||||
print_warning(
|
||||
'This is irreversible and will lead to {CLEAR}{RED}DATA LOSS.'.format(
|
||||
**COLORS))
|
||||
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):
|
||||
"""Get device details via lsblk, returns JSON dict."""
|
||||
cmd = ['lsblk', '--json', '--output-all', '--paths', dev_path]
|
||||
|
|
@ -677,22 +722,22 @@ def get_dir_report(dir_path):
|
|||
output.append('{BLUE}{label:<{width}}{line}{CLEAR}'.format(
|
||||
label='PATH',
|
||||
width=width,
|
||||
line=line.replace('\n',''),
|
||||
line=line.replace('\n', ''),
|
||||
**COLORS))
|
||||
else:
|
||||
output.append('{path:<{width}}{line}'.format(
|
||||
path=dir_path,
|
||||
width=width,
|
||||
line=line.replace('\n','')))
|
||||
line=line.replace('\n', '')))
|
||||
|
||||
# Done
|
||||
return '\n'.join(output)
|
||||
|
||||
|
||||
def get_size_in_bytes(s):
|
||||
def get_size_in_bytes(size):
|
||||
"""Convert size string from lsblk string to bytes, returns int."""
|
||||
s = re.sub(r'(\d+\.?\d*)\s*([KMGTB])B?', r'\1 \2B', s, re.IGNORECASE)
|
||||
return convert_to_bytes(s)
|
||||
size = re.sub(r'(\d+\.?\d*)\s*([KMGTB])B?', r'\1 \2B', size, re.IGNORECASE)
|
||||
return convert_to_bytes(size)
|
||||
|
||||
|
||||
def get_formatted_status(label, data):
|
||||
|
|
@ -700,13 +745,15 @@ def get_formatted_status(label, data):
|
|||
data_width = SIDE_PANE_WIDTH - len(label)
|
||||
try:
|
||||
data_str = '{data:>{data_width}.2f} %'.format(
|
||||
data=data,
|
||||
data_width=data_width-2)
|
||||
data=data,
|
||||
data_width=data_width-2,
|
||||
)
|
||||
except ValueError:
|
||||
# Assuming non-numeric data
|
||||
data_str = '{data:>{data_width}}'.format(
|
||||
data=data,
|
||||
data_width=data_width)
|
||||
data=data,
|
||||
data_width=data_width,
|
||||
)
|
||||
status = '{label}{s_color}{data_str}{CLEAR}'.format(
|
||||
label=label,
|
||||
s_color=get_status_color(data),
|
||||
|
|
@ -715,19 +762,19 @@ def get_formatted_status(label, data):
|
|||
return status
|
||||
|
||||
|
||||
def get_status_color(s, t_success=99, t_warn=90):
|
||||
def get_status_color(status, t_success=99, t_warn=90):
|
||||
"""Get color based on status, returns str."""
|
||||
color = COLORS['CLEAR']
|
||||
p_recovered = -1
|
||||
try:
|
||||
p_recovered = float(s)
|
||||
p_recovered = float(status)
|
||||
except ValueError:
|
||||
# Status is either in lists below or will default to red
|
||||
pass
|
||||
|
||||
if s in ('Pending',) or str(s)[-2:] in (' b', 'Kb', 'Mb', 'Gb', 'Tb'):
|
||||
if status == 'Pending' or str(status)[-2:] in (' b', 'Kb', 'Mb', 'Gb', 'Tb'):
|
||||
color = COLORS['CLEAR']
|
||||
elif s in ('Skipped', 'Unknown'):
|
||||
elif status in ('Skipped', 'Unknown'):
|
||||
color = COLORS['YELLOW']
|
||||
elif p_recovered >= t_success:
|
||||
color = COLORS['GREEN']
|
||||
|
|
@ -742,9 +789,9 @@ def is_writable_dir(dir_obj):
|
|||
"""Check if we have read-write-execute permissions, returns bool."""
|
||||
is_ok = True
|
||||
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_IWUSR
|
||||
is_ok == is_ok and path_st_mode & stat.S_IXUSR
|
||||
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_IXUSR
|
||||
return is_ok
|
||||
|
||||
|
||||
|
|
@ -754,6 +801,7 @@ def is_writable_filesystem(dir_obj):
|
|||
|
||||
|
||||
def menu_ddrescue(source_path, dest_path, run_mode):
|
||||
# pylint: disable=too-many-branches
|
||||
"""ddrescue menu."""
|
||||
source = None
|
||||
dest = None
|
||||
|
|
@ -797,9 +845,8 @@ def menu_ddrescue(source_path, dest_path, run_mode):
|
|||
raise GenericAbort()
|
||||
|
||||
# Main menu
|
||||
clear_screen()
|
||||
build_outer_panes(state)
|
||||
fix_tmux_panes(state, forced=True)
|
||||
state.build_outer_panes()
|
||||
state.fix_tmux_panes(forced=True)
|
||||
menu_main(state)
|
||||
|
||||
# Done
|
||||
|
|
@ -808,6 +855,7 @@ def menu_ddrescue(source_path, dest_path, run_mode):
|
|||
|
||||
|
||||
def menu_main(state):
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
"""Main menu is used to set ddrescue settings."""
|
||||
checkmark = '*'
|
||||
if 'DISPLAY' in global_vars['Env']:
|
||||
|
|
@ -818,16 +866,15 @@ def menu_main(state):
|
|||
# Build menu
|
||||
main_options = [
|
||||
{'Base Name': 'Auto continue (if recovery % over threshold)',
|
||||
'Enabled': True},
|
||||
'Enabled': True},
|
||||
{'Base Name': 'Retry (mark non-rescued sectors "non-tried")',
|
||||
'Enabled': False},
|
||||
'Enabled': False},
|
||||
{'Base Name': 'Reverse direction', 'Enabled': False},
|
||||
]
|
||||
actions = [
|
||||
{'Name': 'Start', 'Letter': 'S'},
|
||||
{'Name': 'Change settings {YELLOW}(experts only){CLEAR}'.format(
|
||||
**COLORS),
|
||||
'Letter': 'C'},
|
||||
{'Name': 'Change settings {YELLOW}(experts only){CLEAR}'.format(**COLORS),
|
||||
'Letter': 'C'},
|
||||
{'Name': 'Quit', 'Letter': 'Q', 'CRLF': True},
|
||||
]
|
||||
|
||||
|
|
@ -858,13 +905,13 @@ def menu_main(state):
|
|||
elif selection == 'S':
|
||||
# Set settings for pass
|
||||
pass_settings = []
|
||||
for k, v in state.settings.items():
|
||||
if not v['Enabled']:
|
||||
for option, option_data in state.settings.items():
|
||||
if not option_data['Enabled']:
|
||||
continue
|
||||
if 'Value' in v:
|
||||
pass_settings.append('{}={}'.format(k, v['Value']))
|
||||
if 'Value' in option_data:
|
||||
pass_settings.append('{}={}'.format(option, option_data['Value']))
|
||||
else:
|
||||
pass_settings.append(k)
|
||||
pass_settings.append(option)
|
||||
for opt in main_options:
|
||||
if 'Auto' in opt['Base Name']:
|
||||
auto_run = opt['Enabled']
|
||||
|
|
@ -887,7 +934,7 @@ def menu_main(state):
|
|||
state.current_pass_min() < AUTO_PASS_1_THRESHOLD):
|
||||
auto_run = False
|
||||
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
|
||||
else:
|
||||
auto_run = False
|
||||
|
|
@ -916,13 +963,15 @@ def menu_settings(state):
|
|||
|
||||
# Build menu
|
||||
settings = []
|
||||
for k, v in sorted(state.settings.items()):
|
||||
if not v.get('Hidden', False):
|
||||
settings.append({'Base Name': k, 'Flag': k})
|
||||
for option, option_data in sorted(state.settings.items()):
|
||||
if not option_data.get('Hidden', False):
|
||||
settings.append({'Base Name': option, 'Flag': option})
|
||||
actions = [{'Name': 'Main Menu', 'Letter': 'M'}]
|
||||
|
||||
# Show menu
|
||||
while True:
|
||||
# pylint: disable=invalid-name
|
||||
# TODO: Clean up and/or replace with new menu-select function
|
||||
for s in settings:
|
||||
s['Name'] = '{}{}{}'.format(
|
||||
s['Base Name'],
|
||||
|
|
@ -959,25 +1008,27 @@ def menu_settings(state):
|
|||
|
||||
def read_map_file(map_path):
|
||||
"""Read map file with ddrescuelog and return data as dict."""
|
||||
map_data = {'full recovery': False}
|
||||
cmd = [
|
||||
'ddrescuelog',
|
||||
'--binary-prefixes',
|
||||
'--show-status',
|
||||
map_path,
|
||||
]
|
||||
map_data = {'full recovery': False, 'pass completed': False}
|
||||
try:
|
||||
result = run_program(['ddrescuelog', '-t', map_path])
|
||||
result = run_program(cmd, encoding='utf-8', errors='ignore')
|
||||
except CalledProcessError:
|
||||
# (Grossly) assuming map_data hasn't been saved yet, return empty dict
|
||||
return map_data
|
||||
|
||||
# Parse output
|
||||
for line in result.stdout.decode().splitlines():
|
||||
m = re.match(
|
||||
r'^\s*(?P<key>\S+):.*\(\s*(?P<value>\d+\.?\d*)%.*', line.strip())
|
||||
if m:
|
||||
try:
|
||||
map_data[m.group('key')] = float(m.group('value'))
|
||||
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')
|
||||
for line in result.stdout.splitlines():
|
||||
line = line.strip()
|
||||
_r = REGEX_DDRESCUE_LOG.search(line)
|
||||
if _r:
|
||||
map_data[_r.group('key')] = convert_to_bytes('{size} {unit}B'.format(
|
||||
**_r.groupdict()))
|
||||
map_data['pass completed'] = 'current status: finished' in line
|
||||
|
||||
# Check if 100% done
|
||||
try:
|
||||
|
|
@ -991,6 +1042,7 @@ def read_map_file(map_path):
|
|||
|
||||
|
||||
def run_ddrescue(state, pass_settings):
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
"""Run ddrescue pass."""
|
||||
return_code = -1
|
||||
aborted = False
|
||||
|
|
@ -1005,8 +1057,8 @@ def run_ddrescue(state, pass_settings):
|
|||
# Create SMART monitor pane
|
||||
state.smart_out = '{}/smart_{}.out'.format(
|
||||
global_vars['TmpDir'], state.smart_source.name)
|
||||
with open(state.smart_out, 'w') as f:
|
||||
f.write('Initializing...')
|
||||
with open(state.smart_out, 'w') as _f:
|
||||
_f.write('Initializing...')
|
||||
state.panes['SMART'] = tmux_split_window(
|
||||
behind=True, lines=12, vertical=True, watch=state.smart_out)
|
||||
|
||||
|
|
@ -1016,19 +1068,19 @@ def run_ddrescue(state, pass_settings):
|
|||
command=['sudo', 'journalctl', '-f'])
|
||||
|
||||
# Fix layout
|
||||
fix_tmux_panes(state, forced=True)
|
||||
state.fix_tmux_panes(forced=True)
|
||||
|
||||
# Run pass for each block-pair
|
||||
for bp in state.block_pairs:
|
||||
if bp.pass_done[state.current_pass]:
|
||||
for b_pair in state.block_pairs:
|
||||
if b_pair.pass_done[state.current_pass]:
|
||||
# Skip to next block-pair
|
||||
continue
|
||||
update_sidepane(state)
|
||||
|
||||
# Set ddrescue cmd
|
||||
cmd = [
|
||||
'ddrescue', *pass_settings,
|
||||
bp.source_path, bp.dest_path, bp.map_path]
|
||||
'sudo', 'ddrescue', *pass_settings,
|
||||
b_pair.source_path, b_pair.dest_path, b_pair.map_path]
|
||||
if state.mode == 'clone':
|
||||
cmd.append('--force')
|
||||
if state.current_pass == 0:
|
||||
|
|
@ -1043,36 +1095,36 @@ def run_ddrescue(state, pass_settings):
|
|||
# Start ddrescue
|
||||
try:
|
||||
clear_screen()
|
||||
print_info('Current dev: {}'.format(bp.source_path))
|
||||
print_info('Current dev: {}'.format(b_pair.source_path))
|
||||
ddrescue_proc = popen_program(cmd)
|
||||
i = 0
|
||||
while True:
|
||||
# Update SMART display (every 30 seconds)
|
||||
if i % 30 == 0:
|
||||
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(
|
||||
timestamp=True)
|
||||
timestamp=True)
|
||||
for line in report:
|
||||
f.write('{}\n'.format(line))
|
||||
_f.write('{}\n'.format(line))
|
||||
i += 1
|
||||
|
||||
# Update progress
|
||||
bp.update_progress(state.current_pass)
|
||||
b_pair.update_progress(state.current_pass)
|
||||
update_sidepane(state)
|
||||
|
||||
# Fix panes
|
||||
fix_tmux_panes(state)
|
||||
state.fix_tmux_panes()
|
||||
|
||||
# Check if ddrescue has finished
|
||||
try:
|
||||
ddrescue_proc.wait(timeout=1)
|
||||
sleep(2)
|
||||
bp.update_progress(state.current_pass)
|
||||
b_pair.update_progress(state.current_pass)
|
||||
update_sidepane(state)
|
||||
break
|
||||
except subprocess.TimeoutExpired:
|
||||
# Catch to update smart/bp/sidepane
|
||||
# Catch to update smart/b_pair/sidepane
|
||||
pass
|
||||
|
||||
except KeyboardInterrupt:
|
||||
|
|
@ -1081,7 +1133,7 @@ def run_ddrescue(state, pass_settings):
|
|||
ddrescue_proc.wait(timeout=10)
|
||||
|
||||
# Update progress/sidepane again
|
||||
bp.update_progress(state.current_pass)
|
||||
b_pair.update_progress(state.current_pass)
|
||||
update_sidepane(state)
|
||||
|
||||
# Was ddrescue aborted?
|
||||
|
|
@ -1103,7 +1155,7 @@ def run_ddrescue(state, pass_settings):
|
|||
break
|
||||
else:
|
||||
# Mark pass finished
|
||||
bp.finish_pass(state.current_pass)
|
||||
b_pair.finish_pass(state.current_pass)
|
||||
update_sidepane(state)
|
||||
|
||||
# Done
|
||||
|
|
@ -1119,6 +1171,8 @@ def run_ddrescue(state, pass_settings):
|
|||
|
||||
|
||||
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."""
|
||||
selected_parts = []
|
||||
children = source_device.details.get('children', [])
|
||||
|
|
@ -1180,24 +1234,26 @@ def select_parts(source_device):
|
|||
raise GenericAbort()
|
||||
|
||||
# Build list of selected parts
|
||||
for d in dev_options:
|
||||
if d['Selected']:
|
||||
d['Dev'].model = source_device.model
|
||||
d['Dev'].model_size = source_device.model_size
|
||||
d['Dev'].update_filename_prefix()
|
||||
selected_parts.append(d['Dev'])
|
||||
for _d in dev_options:
|
||||
if _d['Selected']:
|
||||
_d['Dev'].model = source_device.model
|
||||
_d['Dev'].model_size = source_device.model_size
|
||||
_d['Dev'].update_filename_prefix()
|
||||
selected_parts.append(_d['Dev'])
|
||||
|
||||
return selected_parts
|
||||
|
||||
|
||||
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."""
|
||||
wd = os.path.realpath(global_vars['Env']['PWD'])
|
||||
work_dir = os.path.realpath(global_vars['Env']['PWD'])
|
||||
selected_path = None
|
||||
|
||||
# Build menu
|
||||
path_options = [
|
||||
{'Name': 'Current directory: {}'.format(wd), 'Path': wd},
|
||||
{'Name': 'Current directory: {}'.format(work_dir), 'Path': work_dir},
|
||||
{'Name': 'Local device', 'Path': None},
|
||||
{'Name': 'Enter manually', 'Path': None}]
|
||||
actions = [{'Name': 'Quit', 'Letter': 'Q'}]
|
||||
|
|
@ -1212,9 +1268,9 @@ def select_path(skip_device=None):
|
|||
raise GenericAbort()
|
||||
elif selection.isnumeric():
|
||||
index = int(selection) - 1
|
||||
if path_options[index]['Path'] == wd:
|
||||
if path_options[index]['Path'] == work_dir:
|
||||
# Current directory
|
||||
selected_path = DirObj(wd)
|
||||
selected_path = DirObj(work_dir)
|
||||
|
||||
elif path_options[index]['Name'] == 'Local device':
|
||||
# Local device
|
||||
|
|
@ -1230,15 +1286,15 @@ def select_path(skip_device=None):
|
|||
|
||||
# Select volume
|
||||
vol_options = []
|
||||
for k, v in sorted(report.items()):
|
||||
disabled = v['show_data']['data'] == 'Failed to mount'
|
||||
for _k, _v in sorted(report.items()):
|
||||
disabled = _v['show_data']['data'] == 'Failed to mount'
|
||||
if disabled:
|
||||
name = '{name} (Failed to mount)'.format(**v)
|
||||
name = '{name} (Failed to mount)'.format(**_v)
|
||||
else:
|
||||
name = '{name} (mounted on "{mount_point}")'.format(**v)
|
||||
name = '{name} (mounted on "{mount_point}")'.format(**_v)
|
||||
vol_options.append({
|
||||
'Name': name,
|
||||
'Path': v['mount_point'],
|
||||
'Path': _v['mount_point'],
|
||||
'Disabled': disabled})
|
||||
selection = menu_select(
|
||||
title='Please select a volume',
|
||||
|
|
@ -1313,15 +1369,17 @@ def select_device(description='device', skip_device=None):
|
|||
action_entries=actions,
|
||||
disabled_label='ALREADY SELECTED')
|
||||
|
||||
if selection == 'Q':
|
||||
raise GenericAbort()
|
||||
|
||||
if selection.isnumeric():
|
||||
return dev_options[int(selection)-1]['Dev']
|
||||
elif selection == 'Q':
|
||||
raise GenericAbort()
|
||||
|
||||
|
||||
def setup_loopback_device(source_path):
|
||||
"""Setup loopback device for source_path, returns dev_path as str."""
|
||||
cmd = (
|
||||
'sudo',
|
||||
'losetup',
|
||||
'--find',
|
||||
'--partscan',
|
||||
|
|
@ -1355,6 +1413,7 @@ def show_selection_details(state):
|
|||
|
||||
|
||||
def show_usage(script_name):
|
||||
"""Show usage."""
|
||||
print_info('Usage:')
|
||||
print_standard(USAGE.format(script_name=script_name))
|
||||
pause()
|
||||
|
|
@ -1378,14 +1437,14 @@ def update_sidepane(state):
|
|||
output.append('─────────────────────')
|
||||
|
||||
# Source(s) progress
|
||||
for bp in state.block_pairs:
|
||||
for b_pair in state.block_pairs:
|
||||
if state.source.is_image():
|
||||
output.append('{BLUE}Image File{CLEAR}'.format(**COLORS))
|
||||
else:
|
||||
output.append('{BLUE}{source}{CLEAR}'.format(
|
||||
source=bp.source_path,
|
||||
source=b_pair.source_path,
|
||||
**COLORS))
|
||||
output.extend(bp.status)
|
||||
output.extend(b_pair.status)
|
||||
output.append(' ')
|
||||
|
||||
# EToC
|
||||
|
|
@ -1404,11 +1463,9 @@ def update_sidepane(state):
|
|||
# Add line-endings
|
||||
output = ['{}\n'.format(line) for line in output]
|
||||
|
||||
with open(state.progress_out, 'w') as f:
|
||||
f.writelines(output)
|
||||
with open(state.progress_out, 'w') as _f:
|
||||
_f.writelines(output)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ class CpuObj():
|
|||
self.tests = OrderedDict()
|
||||
self.get_details()
|
||||
self.name = self.lscpu.get('Model name', 'Unknown CPU')
|
||||
self.description = self.name
|
||||
|
||||
def get_details(self):
|
||||
"""Get CPU details from lscpu."""
|
||||
|
|
@ -57,6 +58,13 @@ class CpuObj():
|
|||
report.append('{BLUE}Device{CLEAR}'.format(**COLORS))
|
||||
report.append(' {}'.format(self.name))
|
||||
|
||||
# Include RAM details
|
||||
ram_details = get_ram_details()
|
||||
ram_total = human_readable_size(ram_details.pop('Total', 0)).strip()
|
||||
ram_dimms = ['{}x {}'.format(v, k) for k, v in sorted(ram_details.items())]
|
||||
report.append('{BLUE}RAM{CLEAR}'.format(**COLORS))
|
||||
report.append(' {} ({})'.format(ram_total, ', '.join(ram_dimms)))
|
||||
|
||||
# Tests
|
||||
for test in self.tests.values():
|
||||
report.extend(test.report)
|
||||
|
|
@ -84,7 +92,15 @@ class DiskObj():
|
|||
self.get_size()
|
||||
|
||||
# Try enabling SMART
|
||||
run_program(['sudo', 'smartctl', '--smart=on', self.path], check=False)
|
||||
run_program(
|
||||
cmd=[
|
||||
'sudo',
|
||||
'smartctl',
|
||||
'--tolerance=permissive',
|
||||
'--smart=on',
|
||||
self.path,
|
||||
],
|
||||
check=False)
|
||||
|
||||
# Get NVMe/SMART data and set description
|
||||
self.get_smart_details()
|
||||
|
|
@ -178,8 +194,8 @@ class DiskObj():
|
|||
disk_ok = False
|
||||
|
||||
# Disable override if necessary
|
||||
self.override_disabled |= ATTRIBUTES[attr_type][k].get(
|
||||
'Critical', False)
|
||||
if ATTRIBUTES[attr_type][k].get('Critical', False):
|
||||
self.override_disabled = True
|
||||
|
||||
# SMART overall assessment
|
||||
## NOTE: Only fail drives if the overall value exists and reports failed
|
||||
|
|
@ -212,11 +228,12 @@ class DiskObj():
|
|||
# Done
|
||||
return test_running
|
||||
|
||||
def disable_test(self, name, status):
|
||||
def disable_test(self, name, status, test_failed=False):
|
||||
"""Disable test by name and update status."""
|
||||
if name in self.tests:
|
||||
self.tests[name].update_status(status)
|
||||
self.tests[name].disabled = True
|
||||
self.tests[name].failed = test_failed
|
||||
|
||||
def generate_attribute_report(
|
||||
self, description=False, timestamp=False):
|
||||
|
|
@ -299,6 +316,11 @@ class DiskObj():
|
|||
attr_type=self.attr_type, **COLORS))
|
||||
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
|
||||
for test in self.tests.values():
|
||||
report.extend(test.report)
|
||||
|
|
@ -349,8 +371,15 @@ class DiskObj():
|
|||
|
||||
def get_smart_details(self):
|
||||
"""Get data from smartctl."""
|
||||
cmd = ['sudo', 'smartctl', '--all', '--json', self.path]
|
||||
self.smartctl = get_json_from_command(cmd)
|
||||
cmd = [
|
||||
'sudo',
|
||||
'smartctl',
|
||||
'--tolerance=verypermissive',
|
||||
'--all',
|
||||
'--json',
|
||||
self.path,
|
||||
]
|
||||
self.smartctl = get_json_from_command(cmd, check=False)
|
||||
|
||||
# Check for attributes
|
||||
if KEY_NVME in self.smartctl:
|
||||
|
|
@ -395,6 +424,26 @@ class DiskObj():
|
|||
'self_test', {}).get(
|
||||
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):
|
||||
"""Run safety checks and disable tests if necessary."""
|
||||
test_running = False
|
||||
|
|
@ -439,7 +488,6 @@ class DiskObj():
|
|||
disk_ok = OVERRIDES_FORCED or ask('Run tests on this device anyway?')
|
||||
print_standard(' ')
|
||||
|
||||
|
||||
# Disable tests if necessary (statuses won't be overwritten)
|
||||
if test_running:
|
||||
if not silent:
|
||||
|
|
@ -448,7 +496,7 @@ class DiskObj():
|
|||
for t in ['badblocks', 'I/O Benchmark']:
|
||||
self.disable_test(t, 'Denied')
|
||||
elif not disk_ok:
|
||||
self.disable_test('NVMe / SMART', 'NS')
|
||||
self.disable_test('NVMe / SMART', 'NS', test_failed=True)
|
||||
for t in ['badblocks', 'I/O Benchmark']:
|
||||
self.disable_test(t, 'Denied')
|
||||
|
||||
|
|
@ -456,6 +504,7 @@ class DiskObj():
|
|||
class State():
|
||||
"""Object to track device objects and overall state."""
|
||||
def __init__(self):
|
||||
self.args = None
|
||||
self.cpu = None
|
||||
self.disks = []
|
||||
self.panes = {}
|
||||
|
|
@ -483,6 +532,83 @@ 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):
|
||||
"""Remove test objects, set log, and add devices."""
|
||||
self.disks = []
|
||||
|
|
@ -490,14 +616,18 @@ class State():
|
|||
v['Objects'] = []
|
||||
|
||||
# Update LogDir
|
||||
if not self.quick_mode:
|
||||
if 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['Env']['HOME'],
|
||||
get_ticket_number(),
|
||||
time.strftime('%Y-%m-%d_%H%M_%z'))
|
||||
os.makedirs(global_vars['LogDir'], exist_ok=True)
|
||||
global_vars['LogFile'] = '{}/Hardware Diagnostics.log'.format(
|
||||
global_vars['LogDir'])
|
||||
os.makedirs(global_vars['LogDir'], exist_ok=True)
|
||||
global_vars['LogFile'] = '{}/Hardware Diagnostics.log'.format(
|
||||
global_vars['LogDir'])
|
||||
self.progress_out = '{}/progress.out'.format(global_vars['LogDir'])
|
||||
|
||||
# Add CPU
|
||||
|
|
@ -526,7 +656,13 @@ class State():
|
|||
|
||||
# Start tmux thread
|
||||
self.tmux_layout = TMUX_LAYOUT.copy()
|
||||
start_thread(fix_tmux_panes_loop, args=[self])
|
||||
start_thread(self.fix_tmux_panes_loop)
|
||||
|
||||
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():
|
||||
|
|
@ -561,28 +697,6 @@ class TestObj():
|
|||
|
||||
|
||||
# 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):
|
||||
"""Build status string with appropriate colors."""
|
||||
status_color = COLORS['CLEAR']
|
||||
|
|
@ -599,64 +713,6 @@ def build_status_string(label, status, info_label=False):
|
|||
**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):
|
||||
"""Generate horizontal graph from rates, returns list."""
|
||||
graph = ['', '', '', '']
|
||||
|
|
@ -716,6 +772,44 @@ def get_graph_step(rate, scale=16):
|
|||
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):
|
||||
"""Get read rate in bytes/s from dd progress output."""
|
||||
real_rate = None
|
||||
|
|
@ -728,6 +822,7 @@ def get_read_rate(s):
|
|||
def menu_diags(state, args):
|
||||
"""Main menu to select and run HW tests."""
|
||||
args = [a.lower() for a in args]
|
||||
state.args = args
|
||||
checkmark = '*'
|
||||
if 'DISPLAY' in global_vars['Env']:
|
||||
checkmark = '✓'
|
||||
|
|
@ -773,7 +868,7 @@ def menu_diags(state, args):
|
|||
# If so, verify no other tests are enabled and set quick_mode
|
||||
state.quick_mode = True
|
||||
for opt in main_options[3:4] + main_options[5:]:
|
||||
state.quick_mode &= not opt['Enabled']
|
||||
state.quick_mode = state.quick_mode and not opt['Enabled']
|
||||
else:
|
||||
state.quick_mode = False
|
||||
|
||||
|
|
@ -869,10 +964,7 @@ def run_badblocks_test(state, test):
|
|||
update_progress_pane(state)
|
||||
|
||||
# Update tmux layout
|
||||
tmux_update_pane(
|
||||
state.panes['Top'],
|
||||
text='{}\n{}'.format(
|
||||
TOP_PANE_TEXT, dev.description))
|
||||
state.set_top_pane_text(dev.description)
|
||||
|
||||
# Create monitor pane
|
||||
test.badblocks_out = '{}/badblocks_{}.out'.format(
|
||||
|
|
@ -955,10 +1047,11 @@ def run_hw_tests(state):
|
|||
"""Run enabled hardware tests."""
|
||||
print_standard('Scanning devices...')
|
||||
state.init()
|
||||
tests_enabled = False
|
||||
|
||||
# Build Panes
|
||||
update_progress_pane(state)
|
||||
build_outer_panes(state)
|
||||
state.build_outer_panes()
|
||||
|
||||
# Show selected tests and create TestObj()s
|
||||
print_info('Selected Tests:')
|
||||
|
|
@ -970,6 +1063,8 @@ def run_hw_tests(state):
|
|||
COLORS['CLEAR'],
|
||||
QUICK_LABEL if state.quick_mode and 'NVMe' in k else ''))
|
||||
if v['Enabled']:
|
||||
tests_enabled = True
|
||||
|
||||
# Create TestObj and track under both CpuObj/DiskObj and State
|
||||
if k in TESTS_CPU:
|
||||
test_obj = TestObj(
|
||||
|
|
@ -983,10 +1078,16 @@ def run_hw_tests(state):
|
|||
v['Objects'].append(test_obj)
|
||||
print_standard('')
|
||||
|
||||
# Bail if no tests selected
|
||||
if not tests_enabled:
|
||||
tmux_kill_pane(*state.panes.values())
|
||||
return
|
||||
|
||||
# Run disk safety checks (if necessary)
|
||||
_disk_tests_enabled = False
|
||||
for k in TESTS_DISK:
|
||||
_disk_tests_enabled |= state.tests[k]['Enabled']
|
||||
if state.tests[k]['Enabled']:
|
||||
_disk_tests_enabled = True
|
||||
if _disk_tests_enabled:
|
||||
for disk in state.disks:
|
||||
try:
|
||||
|
|
@ -1024,7 +1125,7 @@ def run_hw_tests(state):
|
|||
|
||||
# Rebuild panes
|
||||
update_progress_pane(state)
|
||||
build_outer_panes(state)
|
||||
state.build_outer_panes()
|
||||
|
||||
# Mark unfinished tests as aborted
|
||||
for k, v in state.tests.items():
|
||||
|
|
@ -1036,8 +1137,22 @@ def run_hw_tests(state):
|
|||
# Update side pane
|
||||
update_progress_pane(state)
|
||||
|
||||
# Done
|
||||
# Show results
|
||||
show_results(state)
|
||||
|
||||
# Upload for review
|
||||
if ENABLED_UPLOAD_DATA 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
|
||||
sleep(1)
|
||||
if state.quick_mode:
|
||||
pause('Press Enter to exit... ')
|
||||
|
|
@ -1064,10 +1179,7 @@ def run_io_benchmark(state, test):
|
|||
update_progress_pane(state)
|
||||
|
||||
# Update tmux layout
|
||||
tmux_update_pane(
|
||||
state.panes['Top'],
|
||||
text='{}\n{}'.format(
|
||||
TOP_PANE_TEXT, dev.description))
|
||||
state.set_top_pane_text(dev.description)
|
||||
state.tmux_layout['Current'] = {'y': 15, 'Check': True}
|
||||
|
||||
# Create monitor pane
|
||||
|
|
@ -1226,9 +1338,7 @@ def run_mprime_test(state, test):
|
|||
test.thermal_abort = False
|
||||
|
||||
# Update tmux layout
|
||||
tmux_update_pane(
|
||||
state.panes['Top'],
|
||||
text='{}\n{}'.format(TOP_PANE_TEXT, dev.name))
|
||||
state.set_top_pane_text(dev.name)
|
||||
|
||||
# Start live sensor monitor
|
||||
test.sensors_out = '{}/sensors.out'.format(global_vars['TmpDir'])
|
||||
|
|
@ -1391,7 +1501,7 @@ def run_mprime_test(state, test):
|
|||
# Add temps to report
|
||||
test.report.append('{BLUE}Temps{CLEAR}'.format(**COLORS))
|
||||
for line in generate_sensor_report(
|
||||
test.sensor_data, 'Idle', 'Max', 'Cooldown', core_only=True):
|
||||
test.sensor_data, 'Idle', 'Max', 'Cooldown', cpu_only=True):
|
||||
test.report.append(' {}'.format(line))
|
||||
|
||||
# Add abort message(s)
|
||||
|
|
@ -1441,10 +1551,7 @@ def run_nvme_smart_tests(state, test, update_mode=False):
|
|||
update_progress_pane(state)
|
||||
|
||||
# Update tmux layout
|
||||
tmux_update_pane(
|
||||
state.panes['Top'],
|
||||
text='{}\n{}'.format(
|
||||
TOP_PANE_TEXT, dev.description))
|
||||
state.set_top_pane_text(dev.description)
|
||||
|
||||
# SMART short self-test
|
||||
if dev.smart_attributes and not (state.quick_mode or update_mode):
|
||||
|
|
@ -1514,7 +1621,13 @@ def run_smart_short_test(state, test):
|
|||
|
||||
# Start short test
|
||||
print_standard('Running self-test...')
|
||||
cmd = ['sudo', 'smartctl', '--test=short', dev.path]
|
||||
cmd = [
|
||||
'sudo',
|
||||
'smartctl',
|
||||
'--tolerance=normal',
|
||||
'--test=short',
|
||||
dev.path,
|
||||
]
|
||||
run_program(cmd, check=False)
|
||||
|
||||
# Monitor progress
|
||||
|
|
@ -1583,14 +1696,13 @@ def show_report(report, log_report=False):
|
|||
def show_results(state):
|
||||
"""Show results for all tests."""
|
||||
clear_screen()
|
||||
tmux_update_pane(
|
||||
state.panes['Top'],
|
||||
text='{}\nResults'.format(TOP_PANE_TEXT))
|
||||
state.set_top_pane_text('Results')
|
||||
|
||||
# CPU tests
|
||||
_enabled = False
|
||||
for k in TESTS_CPU:
|
||||
_enabled |= state.tests[k]['Enabled']
|
||||
if state.tests[k]['Enabled']:
|
||||
_enabled = True
|
||||
if _enabled:
|
||||
print_success('CPU:'.format(k))
|
||||
show_report(state.cpu.generate_cpu_report(), log_report=True)
|
||||
|
|
@ -1599,7 +1711,8 @@ def show_results(state):
|
|||
# Disk tests
|
||||
_enabled = False
|
||||
for k in TESTS_DISK:
|
||||
_enabled |= state.tests[k]['Enabled']
|
||||
if state.tests[k]['Enabled']:
|
||||
_enabled = True
|
||||
if _enabled:
|
||||
print_success('Disk{}:'.format(
|
||||
'' if len(state.disks) == 1 else 's'))
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ def get_installed_antivirus():
|
|||
out = out.stdout.decode().strip()
|
||||
state = out.split('=')[1]
|
||||
state = hex(int(state))
|
||||
if str(state)[3:5] != '10':
|
||||
if str(state)[3:5] not in ['10', '11']:
|
||||
programs.append('[Disabled] {}'.format(prod))
|
||||
else:
|
||||
programs.append(prod)
|
||||
|
|
@ -446,16 +446,19 @@ def show_os_name():
|
|||
|
||||
def show_temp_files_size():
|
||||
"""Show total size of temp files identified by BleachBit."""
|
||||
size = None
|
||||
size_str = None
|
||||
total = 0
|
||||
with open(r'{LogDir}\Tools\BleachBit.log'.format(**global_vars), 'r') as f:
|
||||
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'(\w)iB$', r' \1b', size)
|
||||
if size is None:
|
||||
print_warning(size, timestamp=False)
|
||||
total += convert_to_bytes(size)
|
||||
size_str = human_readable_size(total, decimals=1)
|
||||
if size_str is None:
|
||||
print_warning('UNKNOWN', timestamp=False)
|
||||
else:
|
||||
print_standard(size, timestamp=False)
|
||||
print_standard(size_str, timestamp=False)
|
||||
|
||||
|
||||
def show_user_data_summary(indent=8, width=32):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import json
|
|||
|
||||
from functions.common import *
|
||||
|
||||
def get_json_from_command(cmd, ignore_errors=True):
|
||||
def get_json_from_command(cmd, check=True, ignore_errors=True):
|
||||
"""Capture JSON content from cmd output, returns dict.
|
||||
|
||||
If the data can't be decoded then either an exception is raised
|
||||
|
|
@ -17,7 +17,7 @@ def get_json_from_command(cmd, ignore_errors=True):
|
|||
errors = 'ignore'
|
||||
|
||||
try:
|
||||
result = run_program(cmd, encoding='utf-8', errors=errors)
|
||||
result = run_program(cmd, check=check, encoding='utf-8', errors=errors)
|
||||
json_data = json.loads(result.stdout)
|
||||
except (subprocess.CalledProcessError, json.decoder.JSONDecodeError):
|
||||
if not ignore_errors:
|
||||
|
|
|
|||
|
|
@ -15,27 +15,6 @@ REGEX_VALID_IP = re.compile(
|
|||
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():
|
||||
"""Check for a valid private IP."""
|
||||
devs = psutil.net_if_addrs()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
# 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 re
|
||||
|
|
@ -9,7 +11,7 @@ from settings.sensors import *
|
|||
|
||||
# Error Classes
|
||||
class ThermalLimitReachedError(Exception):
|
||||
pass
|
||||
'''Thermal limit reached error.'''
|
||||
|
||||
|
||||
def clear_temps(sensor_data):
|
||||
|
|
@ -20,28 +22,30 @@ def clear_temps(sensor_data):
|
|||
_data['Temps'] = []
|
||||
|
||||
|
||||
def fix_sensor_str(s):
|
||||
def fix_sensor_str(_s):
|
||||
"""Cleanup string and return str."""
|
||||
s = re.sub(r'^(\w+)-(\w+)-(\w+)', r'\1 (\2 \3)', s, re.IGNORECASE)
|
||||
s = s.title()
|
||||
s = s.replace('Coretemp', 'CoreTemp')
|
||||
s = s.replace('Acpi', 'ACPI')
|
||||
s = s.replace('ACPItz', 'ACPI TZ')
|
||||
s = s.replace('Isa ', 'ISA ')
|
||||
s = s.replace('Id ', 'ID ')
|
||||
s = re.sub(r'(\D+)(\d+)', r'\1 \2', s, re.IGNORECASE)
|
||||
s = s.replace(' ', ' ')
|
||||
return s
|
||||
_s = re.sub(r'^(\w+)-(\w+)-(\w+)', r'\1 (\2 \3)', _s, re.IGNORECASE)
|
||||
_s = _s.title()
|
||||
_s = _s.replace('Coretemp', 'CPUTemp')
|
||||
_s = _s.replace('Acpi', 'ACPI')
|
||||
_s = _s.replace('ACPItz', 'ACPI TZ')
|
||||
_s = _s.replace('Isa ', 'ISA ')
|
||||
_s = _s.replace('Pci ', 'PCI ')
|
||||
_s = _s.replace('Id ', 'ID ')
|
||||
_s = re.sub(r'(\D+)(\d+)', r'\1 \2', _s, re.IGNORECASE)
|
||||
_s = re.sub(r'^K (\d+)Temp', r'AMD K\1 Temps', _s, re.IGNORECASE)
|
||||
_s = re.sub(r'T(ctl|die)', r'CPU (T\1)', _s, re.IGNORECASE)
|
||||
return _s
|
||||
|
||||
|
||||
def generate_sensor_report(
|
||||
sensor_data, *temp_labels,
|
||||
colors=True, core_only=False):
|
||||
colors=True, cpu_only=False):
|
||||
"""Generate report based on temp_labels, returns list if str."""
|
||||
report = []
|
||||
for _section, _adapters in sorted(sensor_data.items()):
|
||||
# CoreTemps then Other temps
|
||||
if core_only and 'Core' not in _section:
|
||||
# CPU temps then Other temps
|
||||
if cpu_only and 'CPU' not in _section:
|
||||
continue
|
||||
for _adapter, _sources in sorted(_adapters.items()):
|
||||
# Adapter
|
||||
|
|
@ -56,7 +60,7 @@ def generate_sensor_report(
|
|||
': ' if _label != 'Current' else '',
|
||||
get_temp_str(_data.get(_label, '???'), colors=colors))
|
||||
report.append(_line)
|
||||
if not core_only:
|
||||
if not cpu_only:
|
||||
report.append(' ')
|
||||
|
||||
# Handle empty reports (i.e. no sensors detected)
|
||||
|
|
@ -91,17 +95,17 @@ def get_colored_temp_str(temp):
|
|||
else:
|
||||
color = COLORS['CLEAR']
|
||||
return '{color}{prefix}{temp:2.0f}°C{CLEAR}'.format(
|
||||
color = color,
|
||||
prefix = '-' if temp < 0 else '',
|
||||
temp = temp,
|
||||
color=color,
|
||||
prefix='-' if temp < 0 else '',
|
||||
temp=temp,
|
||||
**COLORS)
|
||||
|
||||
|
||||
def get_raw_sensor_data():
|
||||
"""Read sensor data and return dict."""
|
||||
data = {}
|
||||
json_data = {}
|
||||
cmd = ['sensors', '-j']
|
||||
|
||||
|
||||
# Get raw data
|
||||
try:
|
||||
result = run_program(cmd)
|
||||
|
|
@ -122,8 +126,8 @@ def get_raw_sensor_data():
|
|||
try:
|
||||
json_data = json.loads('\n'.join(raw_data))
|
||||
except json.JSONDecodeError:
|
||||
# Still broken, just set to empty dict
|
||||
json_data = {}
|
||||
# Still broken, just return the empty dict
|
||||
pass
|
||||
|
||||
# Done
|
||||
return json_data
|
||||
|
|
@ -132,10 +136,10 @@ def get_raw_sensor_data():
|
|||
def get_sensor_data():
|
||||
"""Parse raw sensor data and return new dict."""
|
||||
json_data = get_raw_sensor_data()
|
||||
sensor_data = {'CoreTemps': {}, 'Other': {}}
|
||||
sensor_data = {'CPUTemps': {}, 'Other': {}}
|
||||
for _adapter, _sources in json_data.items():
|
||||
if 'coretemp' in _adapter:
|
||||
_section = 'CoreTemps'
|
||||
if is_cpu_adapter(_adapter):
|
||||
_section = 'CPUTemps'
|
||||
else:
|
||||
_section = 'Other'
|
||||
sensor_data[_section][_adapter] = {}
|
||||
|
|
@ -157,8 +161,8 @@ def get_sensor_data():
|
|||
}
|
||||
|
||||
# Remove empty sections
|
||||
for k, v in sensor_data.items():
|
||||
v = {k2: v2 for k2, v2 in v.items() if v2}
|
||||
for _k, _v in sensor_data.items():
|
||||
_v = {_k2: _v2 for _k2, _v2 in _v.items() if _v2}
|
||||
|
||||
# Done
|
||||
return sensor_data
|
||||
|
|
@ -178,14 +182,20 @@ def get_temp_str(temp, colors=True):
|
|||
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):
|
||||
"""Continually update sensor data and report to screen."""
|
||||
sensor_data = get_sensor_data()
|
||||
while True:
|
||||
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')
|
||||
f.write('\n'.join(report))
|
||||
_f.write('\n'.join(report))
|
||||
sleep(1)
|
||||
if monitor_pane and not tmux_poll_pane(monitor_pane):
|
||||
break
|
||||
|
|
@ -196,7 +206,7 @@ def save_average_temp(sensor_data, temp_label, seconds=10):
|
|||
clear_temps(sensor_data)
|
||||
|
||||
# Get temps
|
||||
for i in range(seconds):
|
||||
for _i in range(seconds): # pylint: disable=unused-variable
|
||||
update_sensor_data(sensor_data)
|
||||
sleep(1)
|
||||
|
||||
|
|
@ -219,24 +229,15 @@ def update_sensor_data(sensor_data, thermal_limit=None):
|
|||
_data['Current'] = _temp
|
||||
_data['Max'] = max(_temp, _data['Max'])
|
||||
_data['Temps'].append(_temp)
|
||||
except Exception:
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# Dumb workound for Dell sensors with changing source names
|
||||
pass
|
||||
|
||||
# Check if thermal limit reached
|
||||
if thermal_limit and _section == 'CoreTemps':
|
||||
if thermal_limit and _section == 'CPUTemps':
|
||||
if max(_data['Current'], _data['Max']) >= thermal_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)
|
||||
raise ThermalLimitReachedError('CPU temps reached limit')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
# Wizard Kit: Functions - Setup
|
||||
|
||||
from functions.browsers import *
|
||||
from functions.json import *
|
||||
from functions.update import *
|
||||
from settings.setup import *
|
||||
from settings.sources import *
|
||||
|
||||
|
||||
# Configuration
|
||||
|
|
@ -63,9 +66,13 @@ def config_explorer_system():
|
|||
write_registry_settings(SETTINGS_EXPLORER_SYSTEM, all_users=True)
|
||||
|
||||
|
||||
def config_explorer_user():
|
||||
"""Configure Windows Explorer for current user."""
|
||||
write_registry_settings(SETTINGS_EXPLORER_USER, all_users=False)
|
||||
def config_explorer_user(setup_mode='All'):
|
||||
"""Configure Windows Explorer for current user per setup_mode."""
|
||||
settings_explorer_user = {
|
||||
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():
|
||||
|
|
@ -73,19 +80,9 @@ def config_windows_updates():
|
|||
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():
|
||||
"""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(
|
||||
['w32tm', '/config', '/syncfromflags:manual',
|
||||
|
|
@ -117,6 +114,39 @@ def write_registry_settings(settings, all_users=False):
|
|||
|
||||
|
||||
# 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():
|
||||
"""Install Adobe Reader."""
|
||||
cmd = [
|
||||
|
|
@ -169,29 +199,115 @@ def install_firefox_extensions():
|
|||
run_program(cmd)
|
||||
|
||||
|
||||
def install_ninite_bundle(mse=False, libreoffice=False):
|
||||
def install_libreoffice(
|
||||
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."""
|
||||
popen_objects = []
|
||||
if global_vars['OS']['Version'] in ('8', '8.1', '10'):
|
||||
# Modern selection
|
||||
popen_objects.append(
|
||||
popen_program(r'{BaseDir}\Installers\Extras\Bundles\Modern.exe'.format(
|
||||
**global_vars)))
|
||||
else:
|
||||
# Legacy selection
|
||||
if mse:
|
||||
cmd = r'{BaseDir}\Installers\Extras\Security'.format(**global_vars)
|
||||
cmd += r'\Microsoft Security Essentials.exe'
|
||||
popen_objects.append(popen_program(cmd))
|
||||
popen_objects.append(
|
||||
popen_program(r'{BaseDir}\Installers\Extras\Bundles\Legacy.exe'.format(
|
||||
**global_vars)))
|
||||
if browsers_only:
|
||||
# This option is deprecated
|
||||
installer_path = r'{BaseDir}\Installers\Extras\Web Browsers'.format(
|
||||
**global_vars)
|
||||
scan_for_browsers(silent=True)
|
||||
for browser in ('Google Chrome', 'Mozilla Firefox', 'Opera Chromium'):
|
||||
if is_installed(browser):
|
||||
cmd = r'{}\{}.exe'.format(installer_path, browser)
|
||||
popen_objects.append(popen_program(cmd))
|
||||
|
||||
# Bail
|
||||
return popen_objects
|
||||
|
||||
# Main selections
|
||||
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
|
||||
if libreoffice:
|
||||
cmd = r'{BaseDir}\Installers\Extras\Office'.format(**global_vars)
|
||||
cmd += r'\LibreOffice.exe'
|
||||
popen_objects.append(popen_program(cmd))
|
||||
cmd = r'{}\Installers\Extras\Office\{}'.format(
|
||||
global_vars['BaseDir'],
|
||||
'LibreOffice.exe',
|
||||
)
|
||||
popen_objects.append(popen_program([cmd]))
|
||||
|
||||
# Done
|
||||
return popen_objects
|
||||
|
|
@ -218,6 +334,10 @@ def open_device_manager():
|
|||
popen_program(['mmc', 'devmgmt.msc'])
|
||||
|
||||
|
||||
def open_speedtest():
|
||||
popen_program(['start', '', 'https://fast.com'], shell=True)
|
||||
|
||||
|
||||
def open_windows_activation():
|
||||
popen_program(['slui'])
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,35 @@ from functions.common 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():
|
||||
"""Check if the system is online and optionally abort the script."""
|
||||
while True:
|
||||
|
|
@ -19,6 +48,37 @@ def check_connection():
|
|||
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):
|
||||
"""Checks UEFI Secure Boot status via PowerShell."""
|
||||
boot_mode = get_boot_mode()
|
||||
|
|
@ -81,33 +141,6 @@ def get_boot_mode():
|
|||
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():
|
||||
"""Run AutoRuns in the background with VirusTotal checks enabled."""
|
||||
extract_item('Autoruns', filter='autoruns*', silent=True)
|
||||
|
|
@ -197,8 +230,10 @@ def run_rkill():
|
|||
shutil.move(item.path, dest)
|
||||
|
||||
|
||||
def show_alert_box(message, title='Wizard Kit Warning'):
|
||||
def show_alert_box(message, title=None):
|
||||
"""Show Windows alert box with message."""
|
||||
if not title:
|
||||
title = '{} Warning'.format(KIT_NAME_FULL)
|
||||
message_box = ctypes.windll.user32.MessageBoxW
|
||||
message_box(None, message, title, 0x00001030)
|
||||
|
||||
|
|
|
|||
471
.bin/Scripts/functions/ufd.py
Normal file
471
.bin/Scripts/functions/ufd.py
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
"""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.")
|
||||
|
|
@ -615,6 +615,22 @@ def update_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():
|
||||
# Prep
|
||||
dest = r'{}\Installers'.format(
|
||||
|
|
|
|||
143
.bin/Scripts/functions/windows_updates.py
Normal file
143
.bin/Scripts/functions/windows_updates.py
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
# 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
|
||||
|
|
@ -34,8 +34,8 @@ if __name__ == '__main__':
|
|||
#print_standard('\nDone.')
|
||||
#pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,8 @@ if __name__ == '__main__':
|
|||
print_standard(' ')
|
||||
sleep(1)
|
||||
pause('Press Enter to exit...')
|
||||
except SystemExit:
|
||||
# Normal exit
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
# Cleanup
|
||||
tmux_kill_all_panes()
|
||||
|
|
@ -50,7 +49,7 @@ if __name__ == '__main__':
|
|||
global_vars=global_vars)
|
||||
|
||||
# Done
|
||||
sleep(10)
|
||||
sleep(1)
|
||||
pause('Press Enter to exit...')
|
||||
exit_script(1)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ if __name__ == '__main__':
|
|||
print_standard('\nDone.')
|
||||
#pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
BLUE='\033[34m'
|
||||
CLEAR='\033[0m'
|
||||
IFS=$'\n'
|
||||
|
||||
# List devices
|
||||
IFS=$'\n'
|
||||
for line in $(lsblk -do NAME,TRAN,SIZE,VENDOR,MODEL,SERIAL); do
|
||||
if [[ "${line:0:4}" == "NAME" ]]; then
|
||||
echo -e "${BLUE}${line}${CLEAR}"
|
||||
|
|
@ -15,6 +15,18 @@ for line in $(lsblk -do NAME,TRAN,SIZE,VENDOR,MODEL,SERIAL); do
|
|||
done
|
||||
echo ""
|
||||
|
||||
# List loopback devices
|
||||
if [[ "$(losetup -l | wc -l)" > 0 ]]; then
|
||||
for line in $(losetup -lO NAME,PARTSCAN,RO,BACK-FILE); do
|
||||
if [[ "${line:0:4}" == "NAME" ]]; then
|
||||
echo -e "${BLUE}${line}${CLEAR}"
|
||||
else
|
||||
echo "${line}" | sed -r 's#/dev/(loop[0-9]+)#\1 #'
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# List partitions
|
||||
for line in $(lsblk -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT); do
|
||||
if [[ "${line:0:4}" == "NAME" ]]; then
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ if __name__ == '__main__':
|
|||
run_program(cmd, check=False)
|
||||
monitor_sensors(monitor_pane, monitor_file)
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ if __name__ == '__main__':
|
|||
'UnsupportedOSError': 'Unsupported OS',
|
||||
}}
|
||||
answer_extensions = ask('Install Extensions?')
|
||||
answer_adobe_reader = ask('Install Adobe Reader?')
|
||||
answer_vcr = ask('Install Visual C++ Runtimes?')
|
||||
answer_ninite = ask('Install Ninite Bundle?')
|
||||
if answer_ninite and global_vars['OS']['Version'] in ['7']:
|
||||
|
|
@ -35,9 +34,6 @@ if __name__ == '__main__':
|
|||
answer_mse = False
|
||||
|
||||
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:
|
||||
install_vcredists()
|
||||
if answer_ninite:
|
||||
|
|
@ -59,8 +55,8 @@ if __name__ == '__main__':
|
|||
other_results=other_results)
|
||||
print_standard('\nDone.')
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ if __name__ == '__main__':
|
|||
|
||||
print_standard('\nDone.')
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ if __name__ == '__main__':
|
|||
pause("Press Enter to exit...")
|
||||
popen_program(['nohup', 'thunar', '/media'], pipe=True)
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@ if __name__ == '__main__':
|
|||
# Prep
|
||||
clear_screen()
|
||||
|
||||
# Connect
|
||||
connect_to_network()
|
||||
|
||||
# Mount
|
||||
if is_connected():
|
||||
mount_backup_shares(read_write=True)
|
||||
|
|
@ -30,8 +27,8 @@ if __name__ == '__main__':
|
|||
print_standard('\nDone.')
|
||||
#pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -76,8 +76,8 @@ if __name__ == '__main__':
|
|||
print_standard('\nDone.')
|
||||
#pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,163 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -31,8 +31,8 @@ if __name__ == '__main__':
|
|||
pause('Press Enter to reboot...')
|
||||
reboot()
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ if __name__ == '__main__':
|
|||
pause('Press Enter to reboot...')
|
||||
reboot()
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
37
.bin/Scripts/settings/cleanup.py
Normal file
37
.bin/Scripts/settings/cleanup.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
'''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.")
|
||||
|
|
@ -5,7 +5,9 @@ import re
|
|||
from collections import OrderedDict
|
||||
|
||||
# General
|
||||
MAP_DIR = '/Backups/ddrescue-tui'
|
||||
RECOMMENDED_FSTYPES = ['ext3', 'ext4', 'xfs']
|
||||
RECOMMENDED_MAP_FSTYPES = ['cifs', 'ext2', 'ext3', 'ext4', 'vfat', 'xfs']
|
||||
USAGE = """ {script_name} clone [source [destination]]
|
||||
{script_name} image [source [destination]]
|
||||
(e.g. {script_name} clone /dev/sda /dev/sdb)
|
||||
|
|
@ -36,6 +38,12 @@ DDRESCUE_SETTINGS = {
|
|||
'-vvvv': {'Enabled': True, 'Hidden': True, },
|
||||
}
|
||||
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(
|
||||
r'remaining time:'
|
||||
r'\s*((?P<days>\d+)d)?'
|
||||
|
|
|
|||
|
|
@ -1,35 +1,20 @@
|
|||
# Wizard Kit: Settings - Launchers
|
||||
'''Wizard Kit: Settings - Launchers'''
|
||||
# pylint: disable=line-too-long
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
LAUNCHERS = {
|
||||
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': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
'L_ITEM': 'system_diagnostics.py',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'User Checklist': {
|
||||
'System Setup': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
'L_ITEM': 'user_checklist.py',
|
||||
'L_ITEM': 'system_setup.py',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
},
|
||||
r'Data Recovery': {
|
||||
|
|
@ -55,6 +40,7 @@ LAUNCHERS = {
|
|||
},
|
||||
},
|
||||
r'Data Transfers': {
|
||||
# pylint: disable=bad-continuation
|
||||
'FastCopy (as ADMIN)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'FastCopy',
|
||||
|
|
@ -257,7 +243,7 @@ LAUNCHERS = {
|
|||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'erunt',
|
||||
'L_ITEM': 'ERUNT.EXE',
|
||||
'L_ARGS': '%client_dir%\Backups\Registry\%iso_date% sysreg curuser otherusers',
|
||||
'L_ARGS': r'%client_dir%\Backups\Registry\%iso_date% sysreg curuser otherusers',
|
||||
'L_ELEV': 'True',
|
||||
'Extra Code': [
|
||||
r'call "%bin%\Scripts\init_client_dir.cmd" /Logs',
|
||||
|
|
@ -287,13 +273,13 @@ LAUNCHERS = {
|
|||
r'Drivers': {
|
||||
'Intel RST (Current Release)': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': '_Drivers\Intel RST',
|
||||
'L_PATH': r'_Drivers\Intel RST',
|
||||
'L_ITEM': 'SetupRST_17.2.exe',
|
||||
'L_7ZIP': 'SetupRST_17.2.exe',
|
||||
},
|
||||
'Intel RST (Previous Releases)': {
|
||||
'L_TYPE': 'Folder',
|
||||
'L_PATH': '_Drivers\Intel RST',
|
||||
'L_PATH': r'_Drivers\Intel RST',
|
||||
'L_ITEM': '.',
|
||||
'L_NCMD': 'True',
|
||||
},
|
||||
|
|
@ -309,7 +295,7 @@ LAUNCHERS = {
|
|||
},
|
||||
'Snappy Driver Installer Origin': {
|
||||
'L_TYPE': 'Executable',
|
||||
'L_PATH': '_Drivers\SDIO',
|
||||
'L_PATH': r'_Drivers\SDIO',
|
||||
'L_ITEM': 'SDIO.exe',
|
||||
},
|
||||
},
|
||||
|
|
@ -435,6 +421,12 @@ LAUNCHERS = {
|
|||
},
|
||||
},
|
||||
r'Misc': {
|
||||
'Activate Windows': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
'L_ITEM': 'activate.py',
|
||||
'L_ELEV': 'True',
|
||||
},
|
||||
'Cleanup CBS Temp Files': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
|
|
@ -452,6 +444,20 @@ LAUNCHERS = {
|
|||
'L_PATH': 'ConEmu',
|
||||
'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': {
|
||||
'L_TYPE': 'PyScript',
|
||||
'L_PATH': 'Scripts',
|
||||
|
|
@ -491,7 +497,7 @@ LAUNCHERS = {
|
|||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'XMPlay',
|
||||
'L_ITEM': 'xmplay.exe',
|
||||
'L_ARGS': '"%bin%\XMPlay\music.7z"',
|
||||
'L_ARGS': r'"%bin%\XMPlay\music.7z"',
|
||||
},
|
||||
},
|
||||
r'Repairs': {
|
||||
|
|
@ -551,7 +557,7 @@ LAUNCHERS = {
|
|||
'L_TYPE': 'Executable',
|
||||
'L_PATH': 'RKill',
|
||||
'L_ITEM': 'RKill.exe',
|
||||
'L_ARGS': '-s -l %log_dir%\Tools\RKill.log',
|
||||
'L_ARGS': r'-s -l %log_dir%\Tools\RKill.log',
|
||||
'L_ELEV': 'True',
|
||||
'Extra Code': [
|
||||
r'call "%bin%\Scripts\init_client_dir.cmd" /Logs',
|
||||
|
|
@ -594,5 +600,3 @@ LAUNCHERS = {
|
|||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@ QUICKBOOKS_SERVER_IP='10.0.0.10'
|
|||
# Time Zones
|
||||
LINUX_TIME_ZONE='America/Denver' # See 'timedatectl list-timezones' for valid values
|
||||
WINDOWS_TIME_ZONE='Mountain Standard Time' # See 'tzutil /l' for valid values
|
||||
# WiFi
|
||||
WIFI_SSID='SomeWiFi'
|
||||
WIFI_PASSWORD='Abracadabra'
|
||||
|
||||
# SERVER VARIABLES
|
||||
## NOTE: Windows can only use one user per server. This means that if
|
||||
|
|
|
|||
|
|
@ -1,13 +1,19 @@
|
|||
# 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 winreg
|
||||
try:
|
||||
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
|
||||
HKU = winreg.HKEY_USERS
|
||||
HKCR = winreg.HKEY_CLASSES_ROOT
|
||||
HKCU = winreg.HKEY_CURRENT_USER
|
||||
HKLM = winreg.HKEY_LOCAL_MACHINE
|
||||
OTHER_RESULTS = {
|
||||
'Error': {
|
||||
'CalledProcessError': 'Unknown Error',
|
||||
|
|
@ -92,6 +98,15 @@ SETTINGS_EXPLORER_SYSTEM = {
|
|||
},
|
||||
}
|
||||
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
|
||||
r'Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager': {
|
||||
'DWORD Items': {
|
||||
|
|
@ -104,21 +119,41 @@ SETTINGS_EXPLORER_USER = {
|
|||
},
|
||||
# File Explorer
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced': {
|
||||
'Invalid modes': ['Cur'],
|
||||
'DWORD Items': {
|
||||
# Change default Explorer view to "Computer"
|
||||
'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
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced\People': {
|
||||
'Invalid modes': ['Cur'],
|
||||
'DWORD Items': {'PeopleBand': 0},
|
||||
},
|
||||
# Hide Search button / box
|
||||
r'Software\Microsoft\Windows\CurrentVersion\Search': {
|
||||
'Invalid modes': ['Cur'],
|
||||
'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
|
||||
VCR_REDISTS = [
|
||||
{'Name': 'Visual C++ 2010 x32...',
|
||||
|
|
@ -157,5 +192,3 @@ SETTINGS_WINDOWS_UPDATES = {
|
|||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
# Wizard Kit: Settings - Sources
|
||||
'''Wizard Kit: Settings - Sources'''
|
||||
# pylint: disable=line-too-long
|
||||
# vim: sts=2 sw=2 ts=2 tw=0
|
||||
|
||||
SOURCE_URLS = {
|
||||
'Adobe Reader DC': 'http://ardownload.adobe.com/pub/adobe/reader/win/AcrobatDC/1901020098/AcroRdrDC1901020098_en_US.exe',
|
||||
|
|
@ -15,7 +17,7 @@ SOURCE_URLS = {
|
|||
'ERUNT': 'http://www.aumha.org/downloads/erunt.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',
|
||||
'FastCopy': 'http://ftp.vector.co.jp/71/31/2323/FastCopy363_installer.exe',
|
||||
'FastCopy': 'https://fastcopy.jp/archive/FastCopy380_installer.exe',
|
||||
'Firefox uBO': 'https://addons.mozilla.org/firefox/downloads/file/1709472/ublock_origin-1.18.6-an+fx.xpi',
|
||||
'HitmanPro32': 'https://dl.surfright.nl/HitmanPro.exe',
|
||||
'HitmanPro64': 'https://dl.surfright.nl/HitmanPro_x64.exe',
|
||||
|
|
@ -23,6 +25,7 @@ SOURCE_URLS = {
|
|||
'Intel SSD Toolbox': r'https://downloadmirror.intel.com/28593/eng/Intel%20SSD%20Toolbox%20-%20v3.5.9.exe',
|
||||
'IOBit_Uninstaller': r'https://portableapps.com/redirect/?a=IObitUninstallerPortable&s=s&d=pa&f=IObitUninstallerPortable_7.5.0.7.paf.exe',
|
||||
'KVRT': 'http://devbuilds.kaspersky-labs.com/devbuilds/KVRT/latest/full/KVRT.exe',
|
||||
'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',
|
||||
'NirCmd32': 'https://www.nirsoft.net/utils/nircmd.zip',
|
||||
'NirCmd64': 'https://www.nirsoft.net/utils/nircmd-x64.zip',
|
||||
|
|
@ -37,8 +40,8 @@ SOURCE_URLS = {
|
|||
'SDIO Torrent': 'http://snappy-driver-installer.org/downloads/SDIO_Update.torrent',
|
||||
'TDSSKiller': 'https://media.kaspersky.com/utilities/VirusUtilities/EN/tdsskiller.exe',
|
||||
'TestDisk': 'https://www.cgsecurity.org/testdisk-7.1-WIP.win.zip',
|
||||
'wimlib32': 'https://wimlib.net/downloads/wimlib-1.13.0-windows-i686-bin.zip',
|
||||
'wimlib64': 'https://wimlib.net/downloads/wimlib-1.13.0-windows-x86_64-bin.zip',
|
||||
'wimlib32': 'https://wimlib.net/downloads/wimlib-1.13.1-windows-i686-bin.zip',
|
||||
'wimlib64': 'https://wimlib.net/downloads/wimlib-1.13.1-windows-x86_64-bin.zip',
|
||||
'Winapp2': 'https://github.com/MoscaDotTo/Winapp2/archive/master.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',
|
||||
|
|
@ -66,10 +69,18 @@ VCREDIST_SOURCES = {
|
|||
'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 = {
|
||||
'Bundles': {
|
||||
'Legacy.exe': '.net4.7.2-7zip-chrome-firefox-vlc',
|
||||
'Modern.exe': '.net4.7.2-7zip-chrome-classicstart-firefox-vlc',
|
||||
'base.exe': '.net4.7.2-7zip-vlc',
|
||||
'base-standard.exe': '.net4.7.2-7zip-chrome-classicstart-firefox-sumatrapdf-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': {
|
||||
'AIMP.exe': 'aimp',
|
||||
|
|
@ -216,5 +227,3 @@ WINDOWS_UPDATE_SOURCES = {
|
|||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
# vim: sts=2 sw=2 ts=2 tw=0
|
||||
|
|
|
|||
118
.bin/Scripts/settings/ufd.py
Normal file
118
.bin/Scripts/settings/ufd.py
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
'''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.")
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
# 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 = {
|
||||
# Build, Version, Release, Codename, Marketing Name, Notes
|
||||
'6000': ('Vista', 'RTM', 'Longhorn', None, 'unsupported'),
|
||||
'6000': ('Vista', 'RTM', 'Longhorn', None, 'unsupported'),
|
||||
'6001': ('Vista', 'SP1', 'Longhorn', None, 'unsupported'),
|
||||
'6002': ('Vista', 'SP2', 'Longhorn', None, 'unsupported'),
|
||||
|
|
@ -202,15 +204,22 @@ WINDOWS_BUILDS = {
|
|||
'18356': ('10', None, '19H1', None, 'preview build'),
|
||||
'18358': ('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'),
|
||||
'18841': ('10', None, '20H1', None, 'preview build'),
|
||||
'18845': ('10', None, '20H1', None, 'preview build'),
|
||||
'18850': ('10', None, '20H1', None, 'preview build'),
|
||||
'18855': ('10', None, '20H1', None, 'preview build'),
|
||||
'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__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ if __name__ == '__main__':
|
|||
print_standard('\nDone.')
|
||||
pause('Press Enter to exit...')
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,133 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -165,8 +165,8 @@ if __name__ == '__main__':
|
|||
print_standard('\nDone.')
|
||||
pause('Press Enter to exit...')
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
354
.bin/Scripts/system_setup.py
Normal file
354
.bin/Scripts/system_setup.py
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
'''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, 'Fab': True, 'Cur': True, 'HW': False, 'Function': install_vcredists, 'Just run': True,},
|
||||
'LibreOffice': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': install_libreoffice,
|
||||
'If answer': 'LibreOffice', 'KWArgs': {'quickstart': False, 'register_mso_types': True, 'use_mso_formats': True, 'vcredist': False},
|
||||
},
|
||||
'Ninite bundle': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': install_ninite_bundle, 'KWArgs': {'cs': 'STARTED'},},
|
||||
|
||||
# Browsers
|
||||
'Scanning for browsers': {'Info': True},
|
||||
'Scan': {'New': True, 'Fab': 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, 'Fab': True, 'Cur': True, 'HW': False, 'Function': backup_browsers, 'Just run': True,},
|
||||
|
||||
# Install extensions
|
||||
'Installing Extensions': {'Info': True},
|
||||
'Classic Shell skin': {'New': True, 'Fab': True, 'Cur': False, 'HW': False, 'Function': install_classicstart_skin, 'Win10 only': True,},
|
||||
'Chrome extensions': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': install_chrome_extensions,},
|
||||
'Firefox extensions': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': install_firefox_extensions,},
|
||||
|
||||
# Configure software'
|
||||
'Configuring Programs': {'Info': True},
|
||||
'Browser add-ons': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': install_adblock, 'Just run': True,
|
||||
'Pause': 'Please enable uBlock Origin for all browsers',
|
||||
},
|
||||
'Classic Start': {'New': True, 'Fab': True, 'Cur': False, 'HW': False, 'Function': config_classicstart, 'Win10 only': True,},
|
||||
'Config Windows Updates': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': config_windows_updates, 'Win10 only': True,},
|
||||
'Enable Windows Updates': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': enable_windows_updates, 'KWArgs': {'silent': True},},
|
||||
'Explorer (system)': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': config_explorer_system, 'Win10 only': True,},
|
||||
'Explorer (user)': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': config_explorer_user, 'Win10 only': True,},
|
||||
'Restart Explorer': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': restart_explorer,},
|
||||
'Update Clock': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': update_clock,},
|
||||
|
||||
# Cleanup
|
||||
'Cleaning up': {'Info': True},
|
||||
'AdwCleaner': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': cleanup_adwcleaner,},
|
||||
'Desktop': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': cleanup_desktop,},
|
||||
'KIT_NAME_FULL': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': delete_empty_folders,},
|
||||
|
||||
# System Info
|
||||
'Exporting system info': {'Info': True},
|
||||
'AIDA64 Report': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': run_aida64,},
|
||||
'File listing': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': backup_file_list,},
|
||||
'Power plans': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': backup_power_plans,},
|
||||
'Product Keys': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': run_produkey,},
|
||||
'Registry': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': backup_registry,},
|
||||
|
||||
# Show Summary
|
||||
'Summary': {'Info': True},
|
||||
'Operating System': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': show_os_name, 'KWArgs': {'ns': 'UNKNOWN', 'silent_function': False},},
|
||||
'Activation': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': show_os_activation, 'KWArgs': {'ns': 'UNKNOWN', 'silent_function': False},},
|
||||
'BIOS Activation': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': activate_with_bios, 'If not activated': True,},
|
||||
'Secure Boot': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': check_secure_boot_status, 'KWArgs': {'show_alert': False},},
|
||||
'Installed RAM': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': show_installed_ram, 'KWArgs': {'ns': 'UNKNOWN', 'silent_function': False},},
|
||||
'Temp size': {'New': False, 'Fab': False, 'Cur': True, 'HW': False, 'Function': show_temp_files_size, 'KWArgs': {'ns': 'UNKNOWN', 'silent_function': False},},
|
||||
'Show free space': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': show_free_space, 'Just run': True,},
|
||||
'Installed AV': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': get_installed_antivirus, 'KWArgs': {'ns': 'UNKNOWN', 'print_return': True},},
|
||||
'Installed Office': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': get_installed_office, 'KWArgs': {'ns': 'UNKNOWN', 'print_return': True},},
|
||||
'Partitions 4K aligned': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': check_4k_alignment, 'KWArgs': {'cs': 'TRUE', 'ns': 'FALSE'},},
|
||||
|
||||
# Open things
|
||||
'Opening Programs': {'Info': True},
|
||||
'Device Manager': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': open_device_manager, 'KWArgs': {'cs': 'STARTED'},},
|
||||
'HWiNFO sensors': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': run_hwinfo_sensors, 'KWArgs': {'cs': 'STARTED'},},
|
||||
'Speed test': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': open_speedtest, 'KWArgs': {'cs': 'STARTED'},},
|
||||
'Windows Updates': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': open_windows_updates, 'KWArgs': {'cs': 'STARTED'},},
|
||||
'Windows Activation': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': open_windows_activation, 'If not activated': True, 'KWArgs': {'cs': 'STARTED'},},
|
||||
'Sleep': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': sleep, 'Just run': True, 'KWArgs': {'seconds': 3},},
|
||||
'XMPlay': {'New': True, 'Fab': 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, 'Fab': None, 'Cur': None, 'HW': False, 'Ninite': True},
|
||||
|
||||
# LibreOffice
|
||||
'LibreOffice': {'New': None, 'Fab': None, 'Cur': None, 'HW': False, 'Ninite': True},
|
||||
|
||||
# Ninite
|
||||
'Base': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Ninite': True},
|
||||
'Missing': {'New': False, 'Fab': True, 'Cur': False, 'HW': False, 'Ninite': True},
|
||||
'Standard': {'New': True, 'Fab': 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': 'Data', '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()
|
||||
|
|
@ -21,8 +21,8 @@ if __name__ == '__main__':
|
|||
# Done
|
||||
print_standard('\nDone.')
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ if __name__ == '__main__':
|
|||
# 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='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='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)
|
||||
|
|
@ -134,8 +135,8 @@ if __name__ == '__main__':
|
|||
print_standard('\nDone.')
|
||||
pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -59,8 +59,8 @@ if __name__ == '__main__':
|
|||
print_standard('\nDone.')
|
||||
pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
46
.bin/Scripts/windows_updates.py
Normal file
46
.bin/Scripts/windows_updates.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# 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
|
||||
|
|
@ -15,8 +15,8 @@ set_log_file('WinPE.log')
|
|||
if __name__ == '__main__':
|
||||
try:
|
||||
menu_root()
|
||||
except SystemExit:
|
||||
pass
|
||||
except SystemExit as sys_exit:
|
||||
exit_script(sys_exit.code)
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
|
|||
BIN
.linux_items/include/EFI/boot/icons/dgpu.png
Normal file
BIN
.linux_items/include/EFI/boot/icons/dgpu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3 KiB |
|
|
@ -19,6 +19,7 @@ menuentry "MemTest86" {
|
|||
icon /EFI/boot/icons/wk_memtest.png
|
||||
loader /EFI/memtest86/memtestx64.efi
|
||||
}
|
||||
|
||||
menuentry "Linux" {
|
||||
icon /EFI/boot/icons/wk_arch.png
|
||||
loader /arch/boot/x86_64/vmlinuz
|
||||
|
|
@ -26,15 +27,32 @@ menuentry "Linux" {
|
|||
initrd /arch/boot/amd_ucode.img
|
||||
initrd /arch/boot/x86_64/archiso.img
|
||||
options "archisobasedir=arch archisolabel=%ARCHISO_LABEL% copytoram loglevel=3"
|
||||
submenuentry "Linux (i3)" {
|
||||
add_options "i3"
|
||||
}
|
||||
submenuentry "Linux (CLI)" {
|
||||
add_options "loglevel=4 nomodeset nox"
|
||||
add_options "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# ostype windows
|
||||
#UFD-WINPE# icon /EFI/boot/icons/wk_win.png
|
||||
#UFD-WINPE# loader /EFI/microsoft/bootx64.efi
|
||||
#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#}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,5 +34,5 @@ alias srsz='sudo rsync -avhzPS --stats --exclude-from="$HOME/.rsync_exclusions"'
|
|||
alias testdisk='sudo testdisk'
|
||||
alias umount='sudo umount'
|
||||
alias unmount='sudo umount'
|
||||
alias wkclone='sudo ddrescue-tui clone'
|
||||
alias wkimage='sudo ddrescue-tui image'
|
||||
alias wkclone='ddrescue-tui clone'
|
||||
alias wkimage='ddrescue-tui image'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
set -g status off
|
||||
set -g pane-active-border-fg white
|
||||
set -g pane-active-border-style fg=white
|
||||
|
||||
# Window names
|
||||
set -g set-titles on
|
||||
|
|
|
|||
|
|
@ -1,23 +1,22 @@
|
|||
## .update_network ##
|
||||
#!/bin/env bash
|
||||
#
|
||||
## Connect to network and update hostname
|
||||
## Setup network and update hostname
|
||||
|
||||
# Connect
|
||||
connect-to-network
|
||||
sleep 2s
|
||||
# Wait for WiFi
|
||||
sleep 1s
|
||||
|
||||
# Set hostname
|
||||
IP="$(ip a show scope global \
|
||||
| grep inet \
|
||||
| head -1 \
|
||||
| sed -r 's#.*inet ([0-9]+.[0-9]+.[0-9]+.[0-9]+.)/.*#\1#')"
|
||||
HOSTNAME="$(dig +noall +answer +short -x "$IP" \
|
||||
| grep -v ';' \
|
||||
| head -1 \
|
||||
| sed 's/\.$//')"
|
||||
|
||||
# Set hostname
|
||||
if [[ "${HOSTNAME:+x}" ]]; then
|
||||
sudo hostnamectl set-hostname "${HOSTNAME}"
|
||||
if [[ "${IP:+x}" ]]; then
|
||||
NEW_HOSTNAME="$(dig +noall +answer +short -x "$IP" \
|
||||
| grep -v ';' \
|
||||
| head -1 \
|
||||
| sed 's/\.$//')"
|
||||
fi
|
||||
if [[ "${NEW_HOSTNAME:+x}" ]]; then
|
||||
sudo hostnamectl set-hostname "${NEW_HOSTNAME}"
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -8,17 +8,6 @@ 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
|
||||
|
||||
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
|
||||
TEXT HELP
|
||||
A live Linux environment (CLI)
|
||||
|
|
@ -27,5 +16,17 @@ ENDTEXT
|
|||
MENU LABEL Linux (CLI)
|
||||
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 nox nomodeset
|
||||
APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram nox
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -7,5 +7,7 @@ rofi.color-normal: argb:d02d3036, #d8d8d8, argb:d02d3036, #2d3036, #d64937
|
|||
rofi.color-active: argb:d0222222, #d64937, argb:d0222222, #d64937, #d8d8d8
|
||||
rofi.color-urgent: argb:d0888888, #d8d8d8, argb:d0888888, #888888, #d64937
|
||||
|
||||
rofi.font: Noto Sans 12
|
||||
|
||||
rofi.separator-style: solid
|
||||
rofi.hide-scrollbar: true
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
../update-conky.timer
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
[Unit]
|
||||
Description=Conky config update service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=%h/.update_conky
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
[Unit]
|
||||
Description=Conky config update timer
|
||||
|
||||
[Timer]
|
||||
OnBootSec=5s
|
||||
OnUnitActiveSec=30s
|
||||
Unit=update-conky.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
|
|
@ -1,18 +1,24 @@
|
|||
#!/bin/bash
|
||||
|
||||
IF_LIST=($(ip l | egrep '^[0-9]+:\s+(eth|en|wl)' | sed -r 's/^[0-9]+:\s+(\w+):.*/\1/' | sort))
|
||||
IF_LIST=($(ip l \
|
||||
| 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
|
||||
if fgrep '#Network' $HOME/.conkyrc; then
|
||||
for i in "${IF_LIST[@]}"; do
|
||||
if [[ "${i:0:1}" == "e" ]]; then
|
||||
sed -i -r "s/#Network/Wired:\${alignr}\${addr $i}\n#Network/" $HOME/.conkyrc
|
||||
else
|
||||
sed -i -r "s/#Network/Wireless:\${alignr}\${addr $i}\n#Network/" $HOME/.conkyrc
|
||||
fi
|
||||
done
|
||||
for i in "${IF_LIST[@]}"; do
|
||||
if [[ "${i:0:1}" == "e" ]]; then
|
||||
sed -i -r "s/#Network/Wired:\${alignr}\${addr $i}\n#Network/" $HOME/.conkyrc
|
||||
else
|
||||
sed -i -r "s/#Network/Wireless:\${alignr}\${addr $i}\n#Network/" $HOME/.conkyrc
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove '#Network' line to prevent duplicating lines if this script is re-run
|
||||
sed -i -r "s/#Network//" $HOME/.conkyrc
|
||||
fi
|
||||
# Remove '#Network' line to prevent duplicating lines if this script is re-run
|
||||
sed -i -r "s/#Network//" $HOME/.conkyrc
|
||||
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@ offset_urxvt="24"
|
|||
# Update settings if necessary
|
||||
if [[ "${dpi}" -ge 192 ]]; then
|
||||
# Conky
|
||||
sed -i 's/minimum_size 180 0/minimum_size 360 0/' "${HOME}/.conkyrc"
|
||||
sed -i 's/maximum_width 180/maximum_width 360/' "${HOME}/.conkyrc"
|
||||
sed -i 's/gap_x 20/gap_x 40/' "${HOME}/.conkyrc"
|
||||
sed -i 's/gap_y 24/gap_y 48/' "${HOME}/.conkyrc"
|
||||
sed -i 's/minimum_size 180 0/minimum_size 360 0/' "${HOME}/.conkyrc_base"
|
||||
sed -i 's/maximum_width 180/maximum_width 360/' "${HOME}/.conkyrc_base"
|
||||
sed -i 's/gap_x 20/gap_x 40/' "${HOME}/.conkyrc_base"
|
||||
sed -i 's/gap_y 24/gap_y 48/' "${HOME}/.conkyrc_base"
|
||||
|
||||
# Fonts
|
||||
sed -i 's/!Xft.dpi: 192/Xft.dpi: 192/' "${HOME}/.Xresources"
|
||||
|
|
@ -47,6 +47,9 @@ if [[ "${dpi}" -ge 192 ]]; then
|
|||
# i3
|
||||
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
|
||||
sed -i 's/panel_size = 100% 30/panel_size = 100% 60/' \
|
||||
"${HOME}/.config/tint2/tint2rc"
|
||||
|
|
@ -67,6 +70,9 @@ fi
|
|||
urxvt_geometry="${width_urxvt}x${height_urxvt}+${offset_urxvt}+${offset_urxvt}"
|
||||
sed -i -r "s/${REGEX_URXVT}/\1${urxvt_geometry}/" "${HOME}/.Xresources"
|
||||
|
||||
# Update conky
|
||||
$HOME/.update_conky
|
||||
|
||||
# Update X
|
||||
xset s off
|
||||
xset -dpms
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ if [ "$(fgconsole 2>/dev/null)" -eq "1" ]; then
|
|||
sed -i -r 's/openbox-session/i3/' ~/.xinitrc
|
||||
fi
|
||||
|
||||
# Update Conky
|
||||
$HOME/.update_conky
|
||||
|
||||
# Start X or HW-diags
|
||||
if ! fgrep -q "nox" /proc/cmdline; then
|
||||
# Kill Xorg after 30 seconds if it doesn't fully initialize
|
||||
|
|
|
|||
2
.linux_items/known_networks
Normal file
2
.linux_items/known_networks
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#Put WiFi network info here
|
||||
#'WiFi SSID': 'WiFi Password'
|
||||
|
|
@ -16,6 +16,7 @@ e2fsprogs
|
|||
hexedit
|
||||
hfsprogs
|
||||
htop
|
||||
iwd
|
||||
ldmtool
|
||||
ldns
|
||||
lha
|
||||
|
|
@ -31,7 +32,9 @@ networkmanager
|
|||
p7zip
|
||||
progsreiserfs
|
||||
python
|
||||
python-docopt
|
||||
python-psutil
|
||||
python-pytz
|
||||
python-requests
|
||||
reiserfsprogs
|
||||
rfkill
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ rp-pppoe
|
|||
smartmontools
|
||||
speedtouch
|
||||
testdisk
|
||||
wpa_actiond
|
||||
vim-minimal
|
||||
vpnc
|
||||
wvdial
|
||||
|
|
|
|||
|
|
@ -281,6 +281,10 @@ function update_live_env() {
|
|||
mkdir -p "$LIVE_DIR/airootfs/usr/share/wallpaper"
|
||||
cp "$ROOT_DIR/Images/Linux.png" "$LIVE_DIR/airootfs/usr/share/wallpaper/burned.in"
|
||||
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() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue