diff --git a/.bin/ConEmu/ConEmu.xml b/.bin/ConEmu/ConEmu.xml index 3a00062b..871995cc 100644 --- a/.bin/ConEmu/ConEmu.xml +++ b/.bin/ConEmu/ConEmu.xml @@ -139,7 +139,7 @@ - + diff --git a/.bin/Scripts/activate.py b/.bin/Scripts/activate.py index f9a9c69d..fa54fa5d 100644 --- a/.bin/Scripts/activate.py +++ b/.bin/Scripts/activate.py @@ -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() diff --git a/.bin/Scripts/add-known-networks b/.bin/Scripts/add-known-networks new file mode 100755 index 00000000..bdfc7a1c --- /dev/null +++ b/.bin/Scripts/add-known-networks @@ -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 diff --git a/.bin/Scripts/build-ufd b/.bin/Scripts/build-ufd index 5f272d7d..17e5972c 100755 --- a/.bin/Scripts/build-ufd +++ b/.bin/Scripts/build-ufd @@ -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_ 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_ 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 + print_info('Prep UFD') + if not args['--update']: + 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() diff --git a/.bin/Scripts/cbs_fix.py b/.bin/Scripts/cbs_fix.py index 36a7906f..167f95aa 100644 --- a/.bin/Scripts/cbs_fix.py +++ b/.bin/Scripts/cbs_fix.py @@ -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() diff --git a/.bin/Scripts/check_disk.py b/.bin/Scripts/check_disk.py index baee7460..fe18650b 100644 --- a/.bin/Scripts/check_disk.py +++ b/.bin/Scripts/check_disk.py @@ -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() diff --git a/.bin/Scripts/connect-to-network b/.bin/Scripts/connect-to-network deleted file mode 100755 index 02500b37..00000000 --- a/.bin/Scripts/connect-to-network +++ /dev/null @@ -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 diff --git a/.bin/Scripts/ddrescue-tui-menu b/.bin/Scripts/ddrescue-tui-menu index a6014d76..f65e24e1 100755 --- a/.bin/Scripts/ddrescue-tui-menu +++ b/.bin/Scripts/ddrescue-tui-menu @@ -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() diff --git a/.bin/Scripts/debug/hw_diags.py b/.bin/Scripts/debug/hw_diags.py index 87a35990..44517fb4 100644 --- a/.bin/Scripts/debug/hw_diags.py +++ b/.bin/Scripts/debug/hw_diags.py @@ -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, diff --git a/.bin/Scripts/dism.py b/.bin/Scripts/dism.py index 2ef3ff25..4de5b788 100644 --- a/.bin/Scripts/dism.py +++ b/.bin/Scripts/dism.py @@ -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() diff --git a/.bin/Scripts/find_user_wallpapers.py b/.bin/Scripts/find_user_wallpapers.py index 3ea182f5..e1e77060 100644 --- a/.bin/Scripts/find_user_wallpapers.py +++ b/.bin/Scripts/find_user_wallpapers.py @@ -73,8 +73,8 @@ if __name__ == '__main__': # Done exit_script() - except SystemExit: - pass + except SystemExit as sys_exit: + exit_script(sys_exit.code) except: major_exception() diff --git a/.bin/Scripts/functions/browsers.py b/.bin/Scripts/functions/browsers.py index 9f51b1e6..e599bbd3 100644 --- a/.bin/Scripts/functions/browsers.py +++ b/.bin/Scripts/functions/browsers.py @@ -9,11 +9,11 @@ from settings.browsers import * browser_data = {} other_results = { 'Error': { - 'MultipleInstallationsError': 'Multiple installations detected', + 'MultipleInstallationsError': 'MULTIPLE INSTALLATIONS DETECTED', }, 'Warning': { - 'NotInstalledError': 'Not installed', - 'NoProfilesError': 'No profiles found', + 'NotInstalledError': 'NOT INSTALLED', + 'NoProfilesError': 'NO PROFILES FOUND', } } @@ -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, @@ -319,13 +319,15 @@ def get_mozilla_profiles(search_path, dev=False): return profiles -def install_adblock(indent=8, width=32, just_firefox=False): +def install_adblock( + indent=8, width=32, just_firefox=False, skip_firefox=False): """Install adblock for all supported browsers.""" for browser in sorted(browser_data): if just_firefox and browser_data[browser]['base'] != 'mozilla': continue + if skip_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 +377,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,7 +385,7 @@ 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) @@ -425,6 +426,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']] @@ -458,7 +465,7 @@ def scan_for_browsers(just_firefox=False, silent=False, skip_ie=False): pass else: try_and_print(message='{}...'.format(name), - function=get_browser_details, cs='Detected', + function=get_browser_details, cs='DETECTED', other_results=other_results, name=name) diff --git a/.bin/Scripts/functions/cleanup.py b/.bin/Scripts/functions/cleanup.py index 7caf19dc..2723994b 100644 --- a/.bin/Scripts/functions/cleanup.py +++ b/.bin/Scripts/functions/cleanup.py @@ -1,49 +1,7 @@ # Wizard Kit: Functions - Cleanup from functions.setup import * - -# STATIC VARIABLES -D7_HKCR_CLEANUP = { - r'batfile\shell\!!RunWithParms': {'Recurse': True}, - r'batfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, - r'cmdfile\shell\!!RunWithParms': {'Recurse': True}, - r'cmdfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, - r'exefile\shell\!!RunWithParms': {'Recurse': True}, - r'exefile\shell\ResourceHacker': {'Recurse': True}, - r'regfile\shell\!!RunWithParms': {'Recurse': True}, - r'regfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, - } -D7_HKCU_CLEANUP = { - r'Software\Malwarebytes': {'Recurse': False}, - } -D7_HKLM_CLEANUP = { - r'Software\Emsisoft': {'Recurse': False}, - } -HKU = winreg.HKEY_USERS -HKCR = winreg.HKEY_CLASSES_ROOT -HKCU = winreg.HKEY_CURRENT_USER -HKLM = winreg.HKEY_LOCAL_MACHINE -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, - }, - }, - } +from settings.cleanup import * def cleanup_adwcleaner(): @@ -195,8 +153,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) diff --git a/.bin/Scripts/functions/common.py b/.bin/Scripts/functions/common.py index 20f0f9f8..bc5ed3f3 100644 --- a/.bin/Scripts/functions/common.py +++ b/.bin/Scripts/functions/common.py @@ -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) @@ -885,25 +905,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(): @@ -911,12 +925,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', @@ -925,10 +939,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, diff --git a/.bin/Scripts/functions/data.py b/.bin/Scripts/functions/data.py index a2e5662c..c359ab6c 100644 --- a/.bin/Scripts/functions/data.py +++ b/.bin/Scripts/functions/data.py @@ -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() @@ -239,9 +245,13 @@ def mount_volumes( 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'] @@ -283,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'] @@ -298,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) diff --git a/.bin/Scripts/functions/ddrescue.py b/.bin/Scripts/functions/ddrescue.py index 545d08e0..b276d52b 100644 --- a/.bin/Scripts/functions/ddrescue.py +++ b/.bin/Scripts/functions/ddrescue.py @@ -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() @@ -259,17 +266,21 @@ class RecoveryState(): self.etoc = '' self.settings = DDRESCUE_SETTINGS.copy() self.finished = False + self.ost = osTicket([], []) self.panes = {} self.progress_out = '{}/progress.out'.format(global_vars['LogDir']) self.rescued = 0 self.resumed = False self.started = False self.status = 'Inactive' + self.ticket_id = None + self.ticket_name = None self.timezone = pytz.timezone(LINUX_TIME_ZONE) self.total_size = 0 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 +325,180 @@ 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 generate_report(self): + """Generate report for osTicket post, returns list.""" + report = [''] + test_station = TEST_STATIONS.get(get_hostname().lower(), '') + + # Header + if test_station: + report[0] += '[Test-Station: {}] '.format(test_station) + report[0] += '[Report for ticket #{} {}]'.format( + self.ticket_id, self.ticket_name) + report.append('ddrescue-tui {} results'.format(self.mode)) + + # Source/Dest + report.append(' ') + report.append('Source: {}'.format(self.source.name)) + report.append('Destination: {}'.format(self.dest.name)) + + # Overall + try: + percent = (self.rescued / self.total_size) * 100 + except ZeroDivisionError: + percent = 0 + report.append(' ') + report.append('Overall rescued: {:0.2f}% error size: {}'.format( + percent, + human_readable_size(self.total_size-self.rescued, decimals=2), + )) + + # Block-Pairs + if len(self.block_pairs) > 1: + report.append(' ') + for b_pair in self.block_pairs: + try: + percent = (b_pair.rescued / b_pair.size) * 100 + except ZeroDivisionError: + percent = 0 + report.append('{} ({}) rescued: {:0.2f}% error size: {}'.format( + b_pair.source.path, + human_readable_size(b_pair.source.size, decimals=1), + percent, + human_readable_size(b_pair.size-b_pair.rescued, decimals=2), + )) + + # Done + return report + def get_smart_source(self): """Get source for SMART dispay.""" disk_path = self.source.path @@ -339,18 +510,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 +529,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 +554,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 +573,47 @@ class RecoveryState(): elif self.current_pass == 2: self.current_pass_str = '3 "Scraping bad areas"' + def set_working_dir(self): + """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']) + + # Get Ticket ID + if not self.ost.disabled: + try: + self.ticket_id, self.ticket_name = self.ost.get_ticket_details() + except TypeError: + # Happens if connection fails and retry is not attempted + pass + if self.ticket_id and self.ticket_name: + map_dir = '{}/{}_{}'.format( + MAP_DIR, + self.ticket_id, + self.ticket_name.replace(' ', '-'), + ) + + # 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 +623,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 +637,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 +661,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 +671,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 +689,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 +706,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 +784,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 +807,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 +824,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 +851,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 +863,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,17 +907,35 @@ 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) + # Post results + if ask('Post results to osTicket?'): + if not state.ticket_id: + # (Re)enable osTicket + state.ost.disabled = False + try: + state.ticket_id, state.ticket_name = state.ost.get_ticket_details() + except TypeError: + # Happens if connection fails and retry is not attempted + pass + + # Post + if state.ticket_id: + state.ost.post_response(state.generate_report(), state.ticket_id) + print_standard('Done') + print_standard(' ') + pause('Press Enter to exit... ') + # Done run_program(['tmux', 'kill-window']) exit_script() 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 +946,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 +985,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 +1014,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 +1043,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 +1088,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\S+):.*\(\s*(?P\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.*)', 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 +1122,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 +1137,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 +1148,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 +1175,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 +1213,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 +1235,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 +1251,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 +1314,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 +1348,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 +1366,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 +1449,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 +1493,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 +1517,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 +1543,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 diff --git a/.bin/Scripts/functions/hw_diags.py b/.bin/Scripts/functions/hw_diags.py index 8a5a441f..ec5b2373 100644 --- a/.bin/Scripts/functions/hw_diags.py +++ b/.bin/Scripts/functions/hw_diags.py @@ -59,6 +59,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) @@ -87,7 +94,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() @@ -181,8 +196,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 @@ -303,6 +318,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) @@ -353,8 +373,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: @@ -399,6 +426,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 @@ -459,6 +506,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.ost = osTicket(TESTS_CPU, TESTS_DISK) @@ -489,6 +537,83 @@ class State(): self.ticket_name = None self.ticket_id = None + 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 = [] @@ -534,7 +659,7 @@ 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.""" @@ -578,28 +703,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'] @@ -616,64 +719,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 = ['', '', '', ''] @@ -733,6 +778,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 @@ -745,6 +828,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 = '✓' @@ -791,7 +875,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[4:5] + main_options[6:]: - state.quick_mode &= not opt['Enabled'] + state.quick_mode = state.quick_mode and not opt['Enabled'] else: state.quick_mode = False @@ -972,12 +1056,13 @@ def run_hw_tests(state): state.init() tests_enabled = False - # Disable osTicket Integration if in quick mode - state.ost.disabled |= state.quick_mode + # Disable osTicket Integration if necessary + if '--quick' in state.args: + state.ost.disabled = True # 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:') @@ -1020,7 +1105,8 @@ def run_hw_tests(state): # 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: @@ -1062,7 +1148,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(): @@ -1092,8 +1178,8 @@ def run_hw_tests(state): # Aborted/Unknown/etc all_disks_passed = False else: - all_disks_passed &= disk.checkbox - disk_failures |= not disk.checkbox + all_disks_passed = all_disks_passed and disk.checkbox + disk_failures = disk_failures or not disk.checkbox # Update checkbox if necessary if disk_failures: @@ -1109,6 +1195,18 @@ def run_hw_tests(state): print_warning('Errors encountered posting results to osTicket.') print_standard(' ') + # 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: @@ -1459,7 +1557,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) @@ -1579,7 +1677,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 @@ -1653,7 +1757,8 @@ def show_results(state): # 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) @@ -1662,7 +1767,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')) diff --git a/.bin/Scripts/functions/info.py b/.bin/Scripts/functions/info.py index 84d92663..b1959090 100644 --- a/.bin/Scripts/functions/info.py +++ b/.bin/Scripts/functions/info.py @@ -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): diff --git a/.bin/Scripts/functions/json.py b/.bin/Scripts/functions/json.py index b4527c0d..49481903 100644 --- a/.bin/Scripts/functions/json.py +++ b/.bin/Scripts/functions/json.py @@ -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: diff --git a/.bin/Scripts/functions/network.py b/.bin/Scripts/functions/network.py index 492ba16f..5b5d4f52 100644 --- a/.bin/Scripts/functions/network.py +++ b/.bin/Scripts/functions/network.py @@ -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() diff --git a/.bin/Scripts/functions/osticket.py b/.bin/Scripts/functions/osticket.py index aed84f92..5ec9529e 100644 --- a/.bin/Scripts/functions/osticket.py +++ b/.bin/Scripts/functions/osticket.py @@ -189,11 +189,14 @@ class osTicket(): def generate_report(self, dev, ticket_id, ticket_name): """Generate device report for osTicket, returns list.""" - report = [] + report = [''] results = self.get_device_overall_results(dev) + test_station = TEST_STATIONS.get(get_hostname().lower(), '') # Header - report.append('[Report for ticket #{} {}]'.format(ticket_id, ticket_name)) + if test_station: + report[0] += '[Test-Station: {}] '.format(test_station) + report[0] += '[Report for ticket #{} {}]'.format(ticket_id, ticket_name) if results['Full Diag']: report.append( '{Dev Type} hardware diagnostic tests: {Status}'.format(**results)) @@ -346,8 +349,15 @@ class osTicket(): results['Status'] += '*' # Enable CoreStorage searches - results['Core'] = (results['Full Diag'] and - results['Passed']+results['N/A']+results['OVERRIDE'] == len(test_list)) + results['Core'] = False + if results['Full Diag']: + num_passed = results['Passed'] + results['N/A'] + results['OVERRIDE'] + if num_passed == len(test_list): + # We ran all disk tests and all results were acceptable + results['Core'] = True + elif results['Failed'] == 1 and dev.tests['I/O Benchmark'].failed: + # We ran all disk tests and only I/O Benchmark failed + results['Core'] = True # Done return results @@ -395,28 +405,13 @@ class osTicket(): self.disconnect() return flag_value - def get_ticket_name(self, ticket_id): - """Lookup ticket and return name as str.""" - name = None - sql_cmd = "SELECT name FROM `{Ticket}`".format(**OSTICKET['Tables']) - sql_cmd += " WHERE `ticket_id` = {}".format(ticket_id) - sql_cmd += ";" - - # Lookup name - # NOTE: If multiple entries are found it will return the last - self.db_cursor.execute(sql_cmd) - for s in self.db_cursor: - name = s[0] - - # Done - return name - def get_ticket_details(self): """Get ticket number and name from osTicket DB, returns tuple.""" ticket_name = None ticket_number = None # Connect + print_standard('Connecting to osTicket...') while True: try: self.connect(silent=False) @@ -434,36 +429,74 @@ class osTicket(): # Main loop while ticket_number is None: print_standard(' ') - _input = input('Enter ticket number (or leave blank to disable): ') - _input = _input.strip() + _ticket_id = input('Enter ticket number (or leave blank to disable): ') + _ticket_id = _ticket_id.strip() # No ticket ID entered - if re.match(r'^\s*$', _input): + if re.match(r'^\s*$', _ticket_id): if ask('Disable osTicket integration for this run?'): self.disabled = True break # Invalid ID entered - if not re.match(r'^(\d+)$', _input): + if not re.match(r'^(\d+)$', _ticket_id): continue - # Valid ID entered, lookup name and verify + # Valid ID entered, lookup name try: - _name = self.get_ticket_name(_input) + _name = self.get_ticket_field(_ticket_id, 'name') except Exception: # Ignore and return None below break - if _name: - print_standard('You have selected ticket #{} {}'.format( - _input, _name)) - if ask('Is this correct?'): - ticket_name = _name - ticket_number = _input + + # Verify ticket exists + if _name is None: + print_error('ERROR: Ticket {} not found.'.format(_ticket_id)) + continue + + # Lookup subject + try: + _subject = self.get_ticket_field(_ticket_id, 'subject') + except Exception: + # Ignore and set to None + _subject = None + + # Verify the selected ticket is correct + print_standard( + 'You have selected ticket {BLUE}#{ticket_id}{CLEAR} {name}'.format( + ticket_id=_ticket_id, + name=_name, + **COLORS)) + print_standard('{CYAN} {subject}{CLEAR}'.format( + subject=_subject, + **COLORS)) + print_standard(' ') + if ask('Is this correct?'): + ticket_name = _name + ticket_number = _ticket_id # Done self.disconnect() return (ticket_number, ticket_name) + def get_ticket_field(self, ticket_id, field): + """Lookup ticket and return field as str.""" + data = None + sql_cmd = "SELECT {field} FROM `{Ticket}`".format( + field=field, + **OSTICKET['Tables']) + sql_cmd += " WHERE `ticket_id` = {}".format(ticket_id) + sql_cmd += ";" + + # Lookup data + # NOTE: If multiple entries are found it will return the last + self.db_cursor.execute(sql_cmd) + for s in self.db_cursor: + data = s[0] + + # Done + return data + def post_device_results(self, dev, ticket_id, ticket_name): """Generate osTicket friendly report and post as response to ticket.""" if not dev.tests: @@ -555,6 +588,13 @@ class osTicket(): # Functions +def get_hostname(): + """Get hostname, returns str.""" + cmd = ['hostnamectl', '--static'] + result = run_program(cmd, check=False, encoding='utf-8', errors='ignore') + return result.stdout.strip() + + def pad_with_dots(s, pad_right=False): """Replace space padding with dots, returns str.""" s = str(s).replace(' ', '..') diff --git a/.bin/Scripts/functions/sensors.py b/.bin/Scripts/functions/sensors.py index 8525deb8..49a7472c 100644 --- a/.bin/Scripts/functions/sensors.py +++ b/.bin/Scripts/functions/sensors.py @@ -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,15 +95,15 @@ 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 @@ -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 diff --git a/.bin/Scripts/functions/setup.py b/.bin/Scripts/functions/setup.py index 7c2062ac..d75a9965 100644 --- a/.bin/Scripts/functions/setup.py +++ b/.bin/Scripts/functions/setup.py @@ -1,6 +1,7 @@ # Wizard Kit: Functions - Setup from functions.browsers import * +from functions.json import * from functions.update import * from settings.setup import * from settings.sources import * @@ -65,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(): @@ -85,6 +90,11 @@ def create_system_restore_point(): run_program(cmd) +def disable_fast_startup(): + """Disable Fast Startup.""" + write_registry_settings(SETTINGS_FAST_STARTUP, all_users=True) + + def disable_windows_telemetry(): """Disable Windows 10 telemetry settings with O&O ShutUp10.""" extract_item('ShutUp10', silent=True) @@ -95,6 +105,12 @@ def disable_windows_telemetry(): run_program(cmd) +def enable_hibernation(): + """Enable hibernation.""" + cmd = ['powercfg', '/hibernate', 'on'] + run_program(cmd) + + def enable_regback(): """Enable RegBack.""" write_registry_settings(SETTINGS_REGBACK, all_users=True) @@ -123,9 +139,10 @@ def enable_system_restore(): '/maxsize=8%'] 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', @@ -157,6 +174,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(skip_ie=True, 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(skip_ie=True, 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 = [ @@ -188,36 +238,26 @@ def install_classicstart_skin(): shutil.copy(source, dest) -def install_eset_nod32_av(scan_pups=True): +def install_eset_nod32_av(msp=False): """Install ESET NOD32 AV with custom config.""" extract_item('ESETConfigs', silent=True) config_file = r'{BinDir}\ESETConfigs\{config_file}.xml'.format( - config_file='eset-config' if scan_pups else 'eset-config-no-pup', + config_file='eset-config-msp' if msp else 'eset-config', **global_vars) - # Apply user configuration - write_registry_settings(SETTINGS_ESET, all_users=False) - - # Download - result = try_and_print(message='Downloading Setup...', cs='Done', - other_results=OTHER_RESULTS, function=download_generic, - out_dir=global_vars['ClientDir'], - out_name='eav_nt64.exe', - source_url=SOURCE_URLS['ESET NOD32 AV']) - installer = r'{ClientDir}\eav_nt64.exe'.format(**global_vars) - if not result['CS']: - raise GenericError('Failed to download ESET NOD32 AV') + # Registry settings + write_registry_settings(SETTINGS_ESET_SYSTEM, all_users=True) + write_registry_settings(SETTINGS_ESET_USER, all_users=False) # Install - cmd = [installer, + cmd = [ + r'{}\Installers\Extras\Security\ESET NOD32 x64.exe'.format( + global_vars['BaseDir']), '--silent', '--accepteula', '--msi-property', 'PRODUCTTYPE=eav', 'PRODUCT_LANG=1033', 'PRODUCT_LANG_CODE=en-US', - 'ADMINCFG="{}"'.format(config_file)] - try_and_print(message='Installing ESET NOD32 AV...', - other_results=OTHER_RESULTS, function=run_program, cmd=cmd) - - # Delete installer - remove_item(installer) + 'ADMINCFG="{}"'.format(config_file), + ] + run_program(cmd) def install_firefox_extensions(): """Install Firefox extensions for all users.""" @@ -240,10 +280,49 @@ def install_firefox_extensions(): run_program(cmd) -def install_ninite_bundle(browsers_only=False, 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 browsers_only: + # This option is deprecated installer_path = r'{BaseDir}\Installers\Extras\Web Browsers'.format( **global_vars) scan_for_browsers(skip_ie=True, silent=True) @@ -251,26 +330,65 @@ def install_ninite_bundle(browsers_only=False, mse=False, libreoffice=False): if is_installed(browser): cmd = r'{}\{}.exe'.format(installer_path, browser) popen_objects.append(popen_program(cmd)) - elif 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))) + + # 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 @@ -293,6 +411,32 @@ def install_vcredists(): # Misc +def drive_is_rotational(drive): + """Check if drive is rotational, returns bool. + + NOTE: This is a quick, naive check with a bias towards rotational.""" + is_rotational = True + + # Get SMART data + extract_item('smartmontools', silent=True) + cmd = [ + global_vars['Tools']['smartctl'], + '--info', + '--json', + drive, + ] + data = get_json_from_command(cmd, check=False) + + # Check rotation rate + try: + is_rotational = int(data.get('rotation_rate', '01189998819991197253')) == 0 + except ValueError: + # Ignore and assume rotational + pass + + return is_rotational + + def open_device_manager(): popen_program(['mmc', 'devmgmt.msc']) @@ -303,6 +447,10 @@ def open_snappy_driver_origin(): popen_program(cmd, cwd=cwd, pipe=True) +def open_speedtest(): + popen_program(['start', '', 'https://fast.com'], shell=True) + + def open_windows_activation(): popen_program(['slui']) diff --git a/.bin/Scripts/functions/sw_diags.py b/.bin/Scripts/functions/sw_diags.py index 3aa2838c..1c5b943f 100644 --- a/.bin/Scripts/functions/sw_diags.py +++ b/.bin/Scripts/functions/sw_diags.py @@ -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) diff --git a/.bin/Scripts/functions/ufd.py b/.bin/Scripts/functions/ufd.py new file mode 100644 index 00000000..e92c8bd8 --- /dev/null +++ b/.bin/Scripts/functions/ufd.py @@ -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/(eSysRescueLiveCD|{})/{}/'.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.") diff --git a/.bin/Scripts/functions/update.py b/.bin/Scripts/functions/update.py index 6ebc52f2..e2ac3f21 100755 --- a/.bin/Scripts/functions/update.py +++ b/.bin/Scripts/functions/update.py @@ -702,6 +702,39 @@ def update_eset_config(): if os.path.exists(include_path): shutil.copytree(include_path, dest) + +def update_eset_nod32(): + # Prep + dest = r'{}\Installers\Extras\Security'.format( + global_vars['BaseDir']) + + # Remove existing installer + try: + os.remove(r'{}\ESET NOD32 x64.exe'.format(dest)) + except FileNotFoundError: + pass + + # Download + download_generic( + dest, 'ESET NOD32 x64.exe', SOURCE_URLS['ESET NOD32 AV']) + + +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( @@ -935,6 +968,42 @@ def update_shutup10(): 'OOSU10.exe', SOURCE_URLS['ShutUp10']) + +def update_smartmontools(): + # Stop running processes + for process in ['smartctl.exe', 'smartctl64.exe']: + kill_process(process) + + # Remove existing folders + remove_from_kit('smartmontools') + + # Download installer + download_to_temp('smartmontools.exe', SOURCE_URLS['smartmontools']) + _installer = r'{}\smartmontools.exe'.format(global_vars['TmpDir']) + + # Extract 64-bit + extract_temp_to_cbin( + _installer, + 'smartmontools', + mode='e', + sz_args=[r'bin64\smartctl.exe'], + ) + shutil.move( + r'{}\smartmontools\smartctl.exe'.format(global_vars['CBinDir']), + r'{}\smartmontools\smartctl64.exe'.format(global_vars['CBinDir'])) + + # Extract 32-bit + extract_temp_to_cbin( + _installer, + 'smartmontools', + mode='e', + sz_args=[r'bin\drivedb.h', r'bin\smartctl.exe'], + ) + + # Cleanup + remove_item(_installer) + + def update_wiztree(): # Stop running processes for process in ['WizTree.exe', 'WizTree64.exe']: diff --git a/.bin/Scripts/hw-diags-audio b/.bin/Scripts/hw-diags-audio index 3d3a5991..e581330f 100755 --- a/.bin/Scripts/hw-diags-audio +++ b/.bin/Scripts/hw-diags-audio @@ -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() diff --git a/.bin/Scripts/hw-diags-menu b/.bin/Scripts/hw-diags-menu index 58bf5280..fc95e04a 100755 --- a/.bin/Scripts/hw-diags-menu +++ b/.bin/Scripts/hw-diags-menu @@ -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() diff --git a/.bin/Scripts/hw-diags-network b/.bin/Scripts/hw-diags-network index 3047e131..138ea67e 100755 --- a/.bin/Scripts/hw-diags-network +++ b/.bin/Scripts/hw-diags-network @@ -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() diff --git a/.bin/Scripts/hw-drive-info b/.bin/Scripts/hw-drive-info index 71a8d388..df1e1748 100755 --- a/.bin/Scripts/hw-drive-info +++ b/.bin/Scripts/hw-drive-info @@ -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 diff --git a/.bin/Scripts/hw-sensors-monitor b/.bin/Scripts/hw-sensors-monitor index c27d786a..ffdbbad3 100755 --- a/.bin/Scripts/hw-sensors-monitor +++ b/.bin/Scripts/hw-sensors-monitor @@ -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() diff --git a/.bin/Scripts/install_eset_nod32_av.py b/.bin/Scripts/install_eset_nod32_av.py index ecdd9da6..7a2509f7 100644 --- a/.bin/Scripts/install_eset_nod32_av.py +++ b/.bin/Scripts/install_eset_nod32_av.py @@ -16,12 +16,12 @@ if __name__ == '__main__': stay_awake() clear_screen() print_info('{}: Install ESET NOD32 AV\n'.format(KIT_NAME_FULL)) - scan_pups = ask('Enable PUP scans in ESET?') - install_eset_nod32_av(scan_pups) + msp = ask('Use MSP settings (ITS/VIP)?') + install_eset_nod32_av(msp=msp) print_standard('\nDone.') exit_script() - except SystemExit: - pass + except SystemExit as sys_exit: + exit_script(sys_exit.code) except: major_exception() diff --git a/.bin/Scripts/install_sw_bundle.py b/.bin/Scripts/install_sw_bundle.py index 25f45638..c69a19dd 100644 --- a/.bin/Scripts/install_sw_bundle.py +++ b/.bin/Scripts/install_sw_bundle.py @@ -64,8 +64,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() diff --git a/.bin/Scripts/install_vcredists.py b/.bin/Scripts/install_vcredists.py index a22cc729..1cd31d95 100644 --- a/.bin/Scripts/install_vcredists.py +++ b/.bin/Scripts/install_vcredists.py @@ -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() diff --git a/.bin/Scripts/mount-all-volumes b/.bin/Scripts/mount-all-volumes index f49439f2..5b34c579 100755 --- a/.bin/Scripts/mount-all-volumes +++ b/.bin/Scripts/mount-all-volumes @@ -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() diff --git a/.bin/Scripts/mount-backup-shares b/.bin/Scripts/mount-backup-shares index 57fbe572..0d8b7fd3 100755 --- a/.bin/Scripts/mount-backup-shares +++ b/.bin/Scripts/mount-backup-shares @@ -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() diff --git a/.bin/Scripts/msword-search b/.bin/Scripts/msword-search index b005c3c1..879e6b8d 100755 --- a/.bin/Scripts/msword-search +++ b/.bin/Scripts/msword-search @@ -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() diff --git a/.bin/Scripts/network_stability_test.py b/.bin/Scripts/network_stability_test.py index a1ca63b3..cb38ac8d 100644 --- a/.bin/Scripts/network_stability_test.py +++ b/.bin/Scripts/network_stability_test.py @@ -38,7 +38,7 @@ 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() diff --git a/.bin/Scripts/new_system_setup.py b/.bin/Scripts/new_system_setup.py deleted file mode 100644 index f3e3e9b2..00000000 --- a/.bin/Scripts/new_system_setup.py +++ /dev/null @@ -1,199 +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=True): - print_warning('OS version not supported by this script') - if not ask('Continue anyway? (NOT RECOMMENDED)'): - abort() - - # Select AV software - # NOTE: Truth tuple is (ESET, ESET_PUPS, MSE) - av_options = [ - {'Name': 'ESET NOD32', - 'Truths': (True, True, False),}, - {'Name': 'ESET NOD32 (no PUP/PUW scans)', - 'Truths': (True, False, False),}, - {'Name': 'Microsoft Security Essentials', - 'Disabled': global_vars['OS']['Version'] not in ['7'], - 'Truths': (False, False, True),}, - ] - actions = [ - {'Name': 'None', 'Letter': 'N'}, - {'Name': 'Quit', 'Letter': 'Q'}, - ] - selection = menu_select( - 'Please select an option to install', - main_entries=av_options, - action_entries=actions) - if selection.isnumeric(): - index = int(selection) - 1 - answer_eset, answer_pups, answer_mse = av_options[index]['Truths'] - elif selection == 'Q': - abort() - else: - answer_eset = False - answer_pups = False - answer_mse = False - - # Install LibreOffice? - answer_libreoffice = ask('Install LibreOffice?') - - # Install software - print_info('Installing Programs') - install_vcredists() - if answer_eset: - install_eset_nod32_av(scan_pups=answer_pups) - result = try_and_print( - message='Ninite bundle...', - function=install_ninite_bundle, cs='Started', - mse=answer_mse, libreoffice=answer_libreoffice, - other_results=other_results) - try: - for proc in result['Out']: - # Wait for all processes to finish - print_standard('Waiting for installations to finish...') - proc.wait() - except KeyboardInterrupt: - pass - - # Scan for supported browsers - print_info('Scanning for browsers') - scan_for_browsers(skip_ie=True) - - # 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') - print_standard(' (if stuck press CTRL+c to cancel this step).') - try: - install_adblock() - except KeyboardInterrupt: - print_warning('Configuration interrupted.') - print_standard('Please confirm all browsers have adblock installed.') - if not ask('Continue to next step?'): - abort() - 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='Enabling RegBack...', - function=enable_regback, cs='Done') - try_and_print(message='Windows Updates...', - function=config_windows_updates, cs='Done') - try_and_print(message='Enabling System Restore...', - function=enable_system_restore, 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 SDI Origin...', - function=open_snappy_driver_origin, cs='Started') - 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 diff --git a/.bin/Scripts/post_d7.py b/.bin/Scripts/post_d7.py deleted file mode 100644 index 2fa8f9f2..00000000 --- a/.bin/Scripts/post_d7.py +++ /dev/null @@ -1,55 +0,0 @@ -# Wizard Kit: Post-d7II items - -import os -import sys - -# Init -os.chdir(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.getcwd()) -from functions.browsers import * -from functions.cleanup import * -from functions.setup import * -init_global_vars() -os.system('title {}: Post-d7II Work'.format(KIT_NAME_FULL)) -set_log_file('Post-d7II Work.log') - -if __name__ == '__main__': - try: - stay_awake() - clear_screen() - print_info('{}: Post-d7II Work\n'.format(KIT_NAME_FULL)) - other_results = { - 'Warning': { - 'NotInstalledError': 'Not installed', - 'NoProfilesError': 'No profiles found', - }} - - # Scan for Firefox browsers - print_info('Scanning for Firefox browsers') - scan_for_browsers(just_firefox=True) - - # Install uBlock Origin - print_info('Installing uBlock Origin') - install_adblock(just_firefox=True) - - # Cleanup - print_info('Cleanup') - try_and_print(message='d7II...', - function=cleanup_d7ii, cs='Done') - try_and_print(message='{}...'.format(KIT_NAME_FULL), - function=delete_empty_folders, cs='Done', - folder_path=global_vars['ClientDir']) - - # 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 diff --git a/.bin/Scripts/reset_browsers.py b/.bin/Scripts/reset_browsers.py index 58f15f42..4130af5a 100644 --- a/.bin/Scripts/reset_browsers.py +++ b/.bin/Scripts/reset_browsers.py @@ -47,7 +47,7 @@ if __name__ == '__main__': # Done exit_script() - except SystemExit: - pass + except SystemExit as sys_exit: + exit_script(sys_exit.code) except: major_exception() diff --git a/.bin/Scripts/safemode_enter.py b/.bin/Scripts/safemode_enter.py index bc6d659d..de9ad119 100644 --- a/.bin/Scripts/safemode_enter.py +++ b/.bin/Scripts/safemode_enter.py @@ -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() diff --git a/.bin/Scripts/safemode_exit.py b/.bin/Scripts/safemode_exit.py index bbbdbcf8..6c47b02d 100644 --- a/.bin/Scripts/safemode_exit.py +++ b/.bin/Scripts/safemode_exit.py @@ -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() diff --git a/.bin/Scripts/settings/cleanup.py b/.bin/Scripts/settings/cleanup.py new file mode 100644 index 00000000..fd4a595b --- /dev/null +++ b/.bin/Scripts/settings/cleanup.py @@ -0,0 +1,66 @@ +# Wizard Kit: Settings - Cleanup + +import re +import psutil +try: + import winreg +except ModuleNotFoundError: + if psutil.WINDOWS: + raise + +# d7II +D7_HKCR_CLEANUP = { + r'batfile\shell\!!RunWithParms': {'Recurse': True}, + r'batfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, + r'cmdfile\shell\!!RunWithParms': {'Recurse': True}, + r'cmdfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, + r'exefile\shell\!!RunWithParms': {'Recurse': True}, + r'exefile\shell\ResourceHacker': {'Recurse': True}, + r'regfile\shell\!!RunWithParms': {'Recurse': True}, + r'regfile\shell\{0001B4FD-9EA3-4D90-A79E-FD14BA3AB01D}': {'Recurse': True}, + } +D7_HKCU_CLEANUP = { + r'Software\Malwarebytes': {'Recurse': False}, + } +D7_HKLM_CLEANUP = { + r'Software\Emsisoft': {'Recurse': False}, + } + +# Regex +DESKTOP_ITEMS = re.compile( + r'^(JRT|RKill|sc-cleaner|Transfer_Log)', + re.IGNORECASE, + ) + +# Registry +HKU = winreg.HKEY_USERS +HKCR = winreg.HKEY_CLASSES_ROOT +HKCU = winreg.HKEY_CURRENT_USER +HKLM = winreg.HKEY_LOCAL_MACHINE +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.") + +# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/settings/ddrescue.py b/.bin/Scripts/settings/ddrescue.py index 675019ca..bc9bef93 100644 --- a/.bin/Scripts/settings/ddrescue.py +++ b/.bin/Scripts/settings/ddrescue.py @@ -5,7 +5,9 @@ import re from collections import OrderedDict # General +MAP_DIR = '/Backups/Anaconda' 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\S+):\s+' + r'(?P\d+)\s+' + r'(?P[PTGMKB])i?B?', + re.IGNORECASE, + ) REGEX_REMAINING_TIME = re.compile( r'remaining time:' r'\s*((?P\d+)d)?' diff --git a/.bin/Scripts/settings/launchers.py b/.bin/Scripts/settings/launchers.py index 76dbd7d9..ca7ea636 100644 --- a/.bin/Scripts/settings/launchers.py +++ b/.bin/Scripts/settings/launchers.py @@ -2,46 +2,17 @@ LAUNCHERS = { r'(Root)': { - 'Activate Windows': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'activate.py', - 'L_ELEV': 'True', - }, 'd7II': { 'L_TYPE': 'Executable', 'L_PATH': 'd7II', 'L_ITEM': 'd7II.exe', }, - 'New System Setup': { + 'System Setup': { 'L_TYPE': 'PyScript', 'L_PATH': 'Scripts', - 'L_ITEM': 'new_system_setup.py', + 'L_ITEM': 'system_setup.py', 'L_ELEV': 'True', }, - 'Post-d7II Work': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'post_d7.py', - 'L_ELEV': 'True', - }, - 'System Checklist': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'system_checklist.py', - 'L_ELEV': 'True', - }, - 'System Checklist (HW)': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'system_checklist_hw.py', - 'L_ELEV': 'True', - }, - 'User Checklist': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'user_checklist.py', - }, }, r'.bin\Scripts\launchers_for_d7': { 'Browser Reset': { @@ -50,20 +21,6 @@ LAUNCHERS = { 'L_ITEM': 'reset_browsers.py', 'L_ARGS': 'd7mode', }, - 'Install SW Bundle': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'install_sw_bundle.py', - 'L_ARGS': 'd7mode', - 'L_ELEV': 'True', - }, - 'System Checklist': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'system_checklist.py', - 'L_ARGS': 'd7mode', - 'L_ELEV': 'True', - }, 'System Diagnostics': { 'L_TYPE': 'PyScript', 'L_PATH': 'Scripts', @@ -71,12 +28,6 @@ LAUNCHERS = { 'L_ARGS': 'd7mode', 'L_ELEV': 'True', }, - 'User Checklist': { - 'L_TYPE': 'PyScript', - 'L_PATH': 'Scripts', - 'L_ITEM': 'user_checklist.py', - 'L_ARGS': 'd7mode', - }, 'Disable Windows Updates': { 'L_TYPE': 'PyScript', 'L_PATH': 'Scripts', @@ -533,6 +484,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', diff --git a/.bin/Scripts/settings/main.py b/.bin/Scripts/settings/main.py index c104a45f..896d9c62 100644 --- a/.bin/Scripts/settings/main.py +++ b/.bin/Scripts/settings/main.py @@ -25,16 +25,13 @@ QUICKBOOKS_SERVER_IP='10.11.1.20' # Time Zones LINUX_TIME_ZONE='America/Los_Angeles' # See 'timedatectl list-timezones' for valid values WINDOWS_TIME_ZONE='Pacific Standard Time' # See 'tzutil /l' for valid values -# WiFi -WIFI_SSID='1201Computers' -WIFI_PASSWORD='justintime!' # SERVER VARIABLES ## NOTE: Windows can only use one user per server. This means that if ## one server serves multiple shares then you have to use the same ## user/password for all of those shares. BACKUP_SERVERS = [ - { 'IP': '10.11.1.20', + { 'IP': '10.120.1.15', 'Name': 'Anaconda', 'Mounted': False, 'Share': 'Backups', @@ -43,18 +40,28 @@ BACKUP_SERVERS = [ 'RW-User': 'backup', 'RW-Pass': '1201 loves computers!', }, + # DISABLED due to issues handling mounting a subdir + #{ 'IP': '10.120.1.23', + # 'Name': 'Server1201', + # 'Mounted': False, + # 'Share': r'Public\TEMP', + # 'User': 'cx', + # 'Pass': '', + # 'RW-User': 'it', + # 'RW-Pass': 'sorted', + #}, ] BENCHMARK_SERVER = { 'Name': 'Nextcloud', - 'Short Url': 'https://1201north.ddns.net:8001/index.php/f/27892', - 'Url': 'https://1201north.ddns.net:8001/public.php/webdav/Benchmarks', - 'User': 'RAE7ajRk25CBnW6', + 'Short Url': 'https://nextcloud.1201.com/index.php/f/108024', + 'Url': 'https://1201.ddns.net:8001/public.php/webdav/Benchmarks', + 'User': 'QAx28ZgmPwCwGHc', 'Pass': '', } CRASH_SERVER = { 'Name': 'Nextcloud', - 'Url': 'https://1201north.ddns.net:8001/public.php/webdav/WizardKit_Issues', - 'User': 'LoQ97J3r6CFGT2T', + 'Url': 'https://1201.ddns.net:8001/public.php/webdav/Issues', + 'User': 'n9Ds76WizNyBw8Y', 'Pass': '', } OFFICE_SERVER = { @@ -78,10 +85,10 @@ QUICKBOOKS_SERVER = { 'RW-Pass': '1201 loves computers!', } WINDOWS_SERVER = { - 'IP': '10.11.1.20', + 'IP': '10.120.1.15', 'Name': 'Anaconda', 'Mounted': False, - 'Share': r'Public\Windows', + 'Share': r'HH-Proliant\Windows', 'User': 'cx', 'Pass': 'cx', 'RW-User': 'backup', diff --git a/.bin/Scripts/settings/osticket.py b/.bin/Scripts/settings/osticket.py index 6360a579..dc30ced3 100644 --- a/.bin/Scripts/settings/osticket.py +++ b/.bin/Scripts/settings/osticket.py @@ -26,6 +26,22 @@ OSTICKET = { 'Ticket': 'ost_ticket', }, } +TEST_STATIONS = { + 'bender': 'Bender', + 'combine': 'Combine', + 'control': 'Control', + 'cortana': 'Cortana', + 'data': 'Data', + 'glados': 'GLaDOS', + 'locutus': 'Locutus', + 'lore': 'Lore', + 'sex-robot': 'Sex-Robot', + 'shodan': 'Shodan', + 'six': 'Six', + 'skynet': 'Skynet', + 'supremo': 'Supremo', + 'unicron': 'Unicron', + } if __name__ == '__main__': print("This file is not meant to be called directly.") diff --git a/.bin/Scripts/settings/setup.py b/.bin/Scripts/settings/setup.py index db30fe19..e3f504cc 100644 --- a/.bin/Scripts/settings/setup.py +++ b/.bin/Scripts/settings/setup.py @@ -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', @@ -63,10 +69,18 @@ SETTINGS_CLASSIC_START = { } # ESET -SETTINGS_ESET = { +SETTINGS_ESET_SYSTEM = { + r'Software\ESET\ESET Security\CurrentVersion\Config\Settings\LiveGrid': { + 'DWORD Items': { + 'LiveGridFeedbackEnabled': 1, + }, + }, + } +SETTINGS_ESET_USER = { r'Software\ESET\ESET Security\CurrentVersion\gui\UI_CONFIG': { 'DWORD Items': { 'FullScreenMode': 0, + 'ShowAlertStatus': 0x40000000, 'ShowDesktopAlert': 0, 'ShowSplash': 0, }, @@ -109,6 +123,7 @@ SETTINGS_EXPLORER_SYSTEM = { SETTINGS_EXPLORER_USER = { # Desktop icons r'Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\ClassicStartMenu': { + 'Invalid modes': ['Cur'], 'DWORD Items': { # This PC '{20D04FE0-3AEA-1069-A2D8-08002B30309D}': 0, @@ -117,6 +132,7 @@ SETTINGS_EXPLORER_USER = { }, }, r'Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel': { + 'Invalid modes': ['Cur'], 'DWORD Items': { # This PC '{20D04FE0-3AEA-1069-A2D8-08002B30309D}': 0, @@ -124,6 +140,15 @@ SETTINGS_EXPLORER_USER = { '{59031a47-3f72-44a7-89c5-5595fe6b30ee}': 0, }, }, + # 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': { @@ -136,25 +161,49 @@ SETTINGS_EXPLORER_USER = { }, # File Explorer r'Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced': { + 'Invalid modes': ['Cur'], 'DWORD Items': { # Replace PowerShell with CMD in Win+X menu 'DontUsePowerShellOnWinX': 1, # Change default Explorer view to "Computer" 'LaunchTo': 1, + }, + }, + r'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced': { + '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': 1}, }, } +# Fast Startup +SETTINGS_FAST_STARTUP = { + # Disable Fast Startup + r'SOFTWARE\Policies\Microsoft\Windows\System': { + 'DWORD Items': {'HiberbootEnabled': 0}, + }, + } + +# LibreOffice +LIBREOFFICE_XCU_DATA = ''' + +Impress MS PowerPoint 2007 XML +Calc MS Excel 2007 XML +MS Word 2007 XML + +''' + # Registry SETTINGS_REGBACK = { # Enable RegBack @@ -201,5 +250,3 @@ SETTINGS_WINDOWS_UPDATES = { if __name__ == '__main__': print("This file is not meant to be called directly.") - -# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/settings/sources.py b/.bin/Scripts/settings/sources.py index 517add65..817441b5 100644 --- a/.bin/Scripts/settings/sources.py +++ b/.bin/Scripts/settings/sources.py @@ -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', @@ -17,7 +19,7 @@ SOURCE_URLS = { 'ESET NOD32 AV': 'https://download.eset.com/com/eset/apps/home/eav/windows/latest/eav_nt64.exe', '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', 'FurMark': 'https://geeks3d.com/dl/get/569', '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', @@ -26,6 +28,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', 'Linux Reader': 'https://www.diskinternals.com/download/Linux_Reader.exe', 'Macs Fan Control': 'https://www.crystalidea.com/downloads/macsfancontrol_setup.exe', 'NirCmd32': 'https://www.nirsoft.net/utils/nircmd.zip', @@ -40,10 +43,11 @@ SOURCE_URLS = { 'SDIO Themes': 'http://snappy-driver-installer.org/downloads/SDIO_Themes.zip', 'SDIO Torrent': 'http://snappy-driver-installer.org/downloads/SDIO_Update.torrent', 'ShutUp10': 'https://dl5.oo-software.com/files/ooshutup10/OOSU10.exe', + 'smartmontools': 'https://700-105252244-gh.circle-artifacts.com/0/builds/smartmontools-win32-setup-7.1-r4914.exe', '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', 'WinAIO Repair': 'http://www.tweaking.com/files/setups/tweaking.com_windows_repair_aio.zip', 'Winapp2': 'https://github.com/MoscaDotTo/Winapp2/archive/master.zip', 'WizTree': 'https://antibody-software.com/files/wiztree_3_28_portable.zip', @@ -72,10 +76,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-sumatrapdf-vlc', - 'Modern.exe': '.net4.7.2-7zip-chrome-classicstart-firefox-sumatrapdf-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', @@ -222,5 +234,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 diff --git a/.bin/Scripts/settings/tools.py b/.bin/Scripts/settings/tools.py index dd3812e1..a2471520 100644 --- a/.bin/Scripts/settings/tools.py +++ b/.bin/Scripts/settings/tools.py @@ -43,6 +43,9 @@ TOOLS = { 'SevenZip': { '32': r'7-Zip\7za.exe', '64': r'7-Zip\7za64.exe'}, + 'smartctl': { + '32': r'smartmontools\smartctl.exe', + '64': r'smartmontools\smartctl64.exe'}, 'TDSSKiller': { '32': r'TDSSKiller\TDSSKiller.exe'}, 'wimlib-imagex': { diff --git a/.bin/Scripts/settings/ufd.py b/.bin/Scripts/settings/ufd.py new file mode 100644 index 00000000..77b7f6df --- /dev/null +++ b/.bin/Scripts/settings/ufd.py @@ -0,0 +1,143 @@ +'''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] + [--eset PATH] + [--hdclone PATH] + [--extra-dir PATH] + build-ufd (-h | --help) + +Options: + -c PATH, --hdclone PATH + -d PATH, --linux-dgpu PATH + -e PATH, --extra-dir PATH + -k PATH, --main-kit PATH + -l PATH, --linux PATH + -m PATH, --linux-minimal PATH + -s PATH, --eset 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'}, + 'ESET SysRescue': {'Arg': '--eset', 'Type': 'IMG'}, + 'HDClone': {'Arg': '--hdclone', 'Type': 'IMG'}, + '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', + '/casper': 'UFD-ESET', + '/dgpu': 'UFD-DGPU', + '/kernel.map': 'UFD-HDCLONE', + '/sources/boot.wim': 'UFD-WINPE', + } +BOOT_FILES = { + # Directory: extension + '/arch/boot/syslinux': 'cfg', + '/boot/grub/': '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 = { + 'ESET SysRescue': ( + ('/boot/grub/', '/boot/grub/'), + ('/casper/', '/casper/'), + ('/EFI/boot/', '/EFI/ESET/'), + ), + 'Extra Dir': ( + ('/', '/'), + ), + 'HDClone': ( + ('/bootenv', '/'), + ('/kernel.map', '/'), + ('/EFI/boot/', '/EFI/HDClone/'), + ('/hdclone.iso', '/sources/'), + ), + '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 = ( + # ESET + 'casper', + # HDClone + 'bootenv', + 'kernel.map', + # 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.") diff --git a/.bin/Scripts/settings/windows_builds.py b/.bin/Scripts/settings/windows_builds.py index db538bf2..f7481294 100644 --- a/.bin/Scripts/settings/windows_builds.py +++ b/.bin/Scripts/settings/windows_builds.py @@ -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 diff --git a/.bin/Scripts/sfc_scan.py b/.bin/Scripts/sfc_scan.py index 884694f5..ec85836a 100644 --- a/.bin/Scripts/sfc_scan.py +++ b/.bin/Scripts/sfc_scan.py @@ -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() diff --git a/.bin/Scripts/system_checklist.py b/.bin/Scripts/system_checklist.py deleted file mode 100644 index 1088cf5d..00000000 --- a/.bin/Scripts/system_checklist.py +++ /dev/null @@ -1,160 +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 * -from functions.windows_updates import * -init_global_vars() -os.system('title {}: System Checklist Tool'.format(KIT_NAME_FULL)) -set_log_file('System Checklist.log') -D7_MODE = 'd7mode' in sys.argv - -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='Disabling telemetry...', - function=disable_windows_telemetry, cs='Done') - try_and_print(message='Enabling RegBack...', - function=enable_regback, cs='Done') - try_and_print(message='Windows Updates...', - function=config_windows_updates, cs='Done') - try_and_print(message='Enabling BSoD mini dumps...', - function=enable_mini_dumps, cs='Done') - try_and_print(message='Enabling System Restore...', - function=enable_system_restore, cs='Done') - try_and_print(message='Create System Restore point...', - function=create_system_restore_point, cs='Done') - try_and_print(message='Enabling Windows Updates...', - function=enable_windows_updates, cs='Done', silent=True) - 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='Emsisoft a2cmd...', - function=cleanup_emsisoft, cs='Done') - try_and_print(message='Registry Backup(s)...', - function=cleanup_regbackups, cs='Done') - try_and_print(message='{}...'.format(KIT_NAME_FULL), - function=delete_empty_folders, cs='Done', - folder_path=global_vars['ClientDir']) - - # Export system info - if not D7_MODE: - 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) - if D7_MODE: - try_and_print(message='Product Keys:', - function=get_product_keys, ns='Unknown', 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 Exception: - # Only trying to open alert message boxes - pass - - # Done - print_standard('\nDone.') - pause('Press Enter exit...') - if D7_MODE: - show_alert_box( - message='Please run the Post-d7II script after ending the session.', - title='{} Notice'.format(KIT_NAME_FULL), - ) - exit_script() - except SystemExit: - pass - except: - major_exception() - -# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/system_checklist_hw.py b/.bin/Scripts/system_checklist_hw.py deleted file mode 100644 index 1edb0e96..00000000 --- a/.bin/Scripts/system_checklist_hw.py +++ /dev/null @@ -1,122 +0,0 @@ -# Wizard Kit: System HW 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 HW Checklist Tool'.format(KIT_NAME_FULL)) -set_log_file('System HW Checklist.log') - -if __name__ == '__main__': - try: - stay_awake() - clear_screen() - print_info('{}: System HW 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='Enabling RegBack...', - function=enable_regback, cs='Done') - try_and_print(message='Enabling BSoD mini dumps...', - function=enable_mini_dumps, cs='Done') - try_and_print(message='Enabling System Restore...', - function=enable_system_restore, cs='Done') - try_and_print(message='Create System Restore point...', - function=create_system_restore_point, cs='Done') - - # 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) - - # Cleanup - print_info('Cleanup') - try_and_print(message='{}...'.format(KIT_NAME_FULL), - function=delete_empty_folders, cs='Done', - folder_path=global_vars['ClientDir']) - - # 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) - 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 Exception: - # 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 diff --git a/.bin/Scripts/system_diagnostics.py b/.bin/Scripts/system_diagnostics.py index 2e717ed5..74e92dd8 100644 --- a/.bin/Scripts/system_diagnostics.py +++ b/.bin/Scripts/system_diagnostics.py @@ -123,21 +123,22 @@ if __name__ == '__main__': result = try_and_print( message='CHKDSK ({SYSTEMDRIVE})...'.format(**global_vars['Env']), function=run_chkdsk, other_results=other_results) - system_ok &= check_result(result, other_results) + system_ok = system_ok and check_result(result, other_results) result = try_and_print(message='SFC scan...', function=run_sfc_scan, other_results=other_results) - system_ok &= check_result(result, other_results) + system_ok = system_ok and check_result(result, other_results) if D7_MODE: result = try_and_print(message='DISM RestoreHealth...', function=run_dism, other_results=other_results, repair=True) if global_vars['OS']['Version'] in ('8', '8.1', '10'): - system_ok &= check_result(result, other_results) + system_ok = system_ok and check_result(result, other_results) else: try_and_print(message='DISM CheckHealth...', function=run_dism, other_results=other_results, repair=False) if D7_MODE: # Archive all browsers for all users + print_standard(' ') archive_all_users() else: # Scan for supported browsers @@ -149,24 +150,25 @@ if __name__ == '__main__': for k, v in sorted(BLEACH_BIT_CLEANERS.items()): try_and_print(message='{}...'.format(k), function=run_bleachbit, - cs='Done', other_results=other_results, + cs='SUCCESS', other_results=other_results, cleaners=v, preview=bool(not D7_MODE)) + print_standard(' ') # Export system info - print_info('Backup System Information') - try_and_print(message='AIDA64 reports...', - function=run_aida64, cs='Done', other_results=other_results) if not D7_MODE: + print_info('Backup System Information') + try_and_print(message='AIDA64 reports...', + function=run_aida64, cs='SUCCESS', other_results=other_results) backup_browsers() - 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...', - function=run_produkey, cs='Done', other_results=other_results) - try_and_print(message='Registry...', - function=backup_registry, cs='Done', other_results=other_results, - overwrite=True) + try_and_print(message='File listing...', + function=backup_file_list, cs='SUCCESS', other_results=other_results) + try_and_print(message='Power plans...', + function=backup_power_plans, cs='SUCCESS') + try_and_print(message='Product Keys...', + function=run_produkey, cs='SUCCESS', other_results=other_results) + try_and_print(message='Registry...', + function=backup_registry, cs='SUCCESS', other_results=other_results, + overwrite=True) # Summary if not D7_MODE: @@ -202,8 +204,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() diff --git a/.bin/Scripts/system_setup.py b/.bin/Scripts/system_setup.py new file mode 100644 index 00000000..9e871f73 --- /dev/null +++ b/.bin/Scripts/system_setup.py @@ -0,0 +1,394 @@ +'''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,}, + 'ESET NOD32 AV': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': install_eset_nod32_av, 'If answer': 'ESET', 'KWArgs': {'msp': False},}, + '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 System Restore': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': enable_system_restore,}, + 'Create System Restore': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': create_system_restore_point,}, + 'Disable Fast Startup': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': disable_fast_startup, 'If answer': 'Fast-Hiber', 'Win10 only': True,}, + 'Disable telemetry': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': disable_windows_telemetry, 'Win10 only': True,}, + 'Enable BSoD mini dumps': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': enable_mini_dumps,}, + 'Enable Hibernation': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': enable_hibernation, 'If answer': 'Fast-Hiber', 'Win10 only': True,}, + 'Enable RegBack': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': enable_regback, '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,}, + 'd7II': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': cleanup_d7ii,}, + 'Desktop': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': cleanup_desktop,}, + 'Emsisoft s2cmd': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': cleanup_emsisoft,}, + 'Registry Backup(s)': {'New': True, 'Fab': True, 'Cur': True, 'HW': False, 'Function': cleanup_regbackups,}, + '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'},}, + 'Snappy': {'New': True, 'Fab': True, 'Cur': True, 'HW': True, 'Function': open_snappy_driver_origin, '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 + 'ESET': {'New': None, 'Fab': None, 'Cur': None, 'HW': False}, + 'MSE': {'New': None, 'Fab': None, 'Cur': None, 'HW': False, 'Ninite': True}, + + # Fast Startup / Hibernation + 'Fast-Hiber': {'New': None, 'Fab': None, 'Cur': None, 'HW': False}, + + # 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['ESET'] is None or answers['MSE'] is None: + answers.update(get_av_selection()) + + if answers['LibreOffice'] is None: + answers['LibreOffice'] = ask('Install LibreOffice?') + + if answers['Fast-Hiber'] is None: + if global_vars['OS']['Version'] == '10': + # SSD check + if drive_is_rotational(global_vars['Env']['SYSTEMDRIVE']): + answers['Fast-Hiber'] = True + else: + print_standard(' ') + print_standard('Disable Fast Startup and enable Hibernation?') + print_standard(' Recommended for SSDs, optional for HDDs') + answers['Fast-Hiber'] = ask(' Proceed?') + else: + answers['Fast-Hiber'] = False + + return answers + + +def get_av_selection(): + """Get AV selection.""" + av_answers = { + 'ESET': False, + 'MSE': False, + } + av_options = [ + {'Name': 'ESET NOD32 AV'}, + { + '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 'ESET' in av_options[index]['Name']: + av_answers['ESET'] = True + av_answers['MSE'] = False + elif 'Microsoft' in av_options[index]['Name']: + av_answers['ESET'] = False + av_answers['MSE'] = True + elif selection == 'Q': + abort() + + # ESET settings + if av_answers['ESET'] and ask(' Use MSP settings (ITS/VIP)?'): + # NOTE: There's probably a better way than updating a static var... + SETUP_ACTIONS['ESET NOD32 AV']['KWArgs']['msp'] = True + + 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': 'Fab', 'Display Name': 'Clean install with data migration'}, + {'Name': 'Cur', 'Display Name': 'Original OS (post-d7II 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() diff --git a/.bin/Scripts/transferred_keys.py b/.bin/Scripts/transferred_keys.py index 6dab114d..216f2046 100644 --- a/.bin/Scripts/transferred_keys.py +++ b/.bin/Scripts/transferred_keys.py @@ -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() diff --git a/.bin/Scripts/update_kit.py b/.bin/Scripts/update_kit.py index 13520d5c..086535a4 100644 --- a/.bin/Scripts/update_kit.py +++ b/.bin/Scripts/update_kit.py @@ -60,6 +60,8 @@ if __name__ == '__main__': 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='ESET Configs...', function=update_eset_config, other_results=other_results, width=40) + try_and_print(message='ESET NOD32...', function=update_eset_nod32, 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) @@ -75,6 +77,7 @@ if __name__ == '__main__': try_and_print(message='Firefox Extensions...', function=update_firefox_ublock_origin, other_results=other_results, width=40) try_and_print(message='PuTTY...', function=update_putty, other_results=other_results, width=40) try_and_print(message='ShutUp10...', function=update_shutup10, other_results=other_results, width=40) + try_and_print(message='smartmontools...', function=update_smartmontools, other_results=other_results, width=40) try_and_print(message='Notepad++...', function=update_notepadplusplus, other_results=other_results, width=40) try_and_print(message='WizTree...', function=update_wiztree, other_results=other_results, width=40) try_and_print(message='XMPlay...', function=update_xmplay, other_results=other_results, width=40) @@ -126,22 +129,12 @@ if __name__ == '__main__': section=section, name=name, options=options, other_results=other_results, width=40) - # Rename "Copy WizardKit.cmd" (if necessary) - source = r'{}\Scripts\Copy WizardKit.cmd'.format(global_vars['BinDir']) - dest = r'{}\Copy {}.cmd'.format(global_vars['BaseDir'], KIT_NAME_FULL) - if os.path.exists(source): - try: - shutil.move(source, dest) - except Exception: - print_error(' Failed to rename "{}.cmd" to "{}.cmd"'.format( - 'Copy WizardKit', KIT_NAME_FULL)) - # Done 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() diff --git a/.bin/Scripts/user_checklist.py b/.bin/Scripts/user_checklist.py deleted file mode 100644 index 30a78730..00000000 --- a/.bin/Scripts/user_checklist.py +++ /dev/null @@ -1,101 +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'])) -D7_MODE = 'd7mode' in sys.argv - -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?') - answer_config_browsers = True - if answer_config_browsers: - if D7_MODE: - # This is handled by another script option in d7ii - answer_reset_browsers = False - else: - answer_reset_browsers = ask( - 'Reset browsers to safe defaults first?') - if global_vars['OS']['Version'] == '10': - answer_config_classicshell = True - answer_config_explorer_user = True - - # 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(skip_ie=True) - - # Homepages - if not D7_MODE: - print_info('Current homepages') - list_homepages() - - # Backup - if not D7_MODE: - # Done during system_diagnostics - 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 - if not D7_MODE: - popen_program(['start', '', 'https://fast.com'], shell=True) - - # Done - if not D7_MODE: - print_standard('\nDone.') - pause('Press Enter to exit...') - exit_script() - except SystemExit: - pass - except: - major_exception() - -# vim: sts=2 sw=2 ts=2 diff --git a/.bin/Scripts/user_data_transfer.py b/.bin/Scripts/user_data_transfer.py index e63e0d7d..60b3c464 100644 --- a/.bin/Scripts/user_data_transfer.py +++ b/.bin/Scripts/user_data_transfer.py @@ -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() diff --git a/.bin/Scripts/windows_updates.py b/.bin/Scripts/windows_updates.py index a9856cd0..9332e706 100644 --- a/.bin/Scripts/windows_updates.py +++ b/.bin/Scripts/windows_updates.py @@ -43,8 +43,8 @@ if __name__ == '__main__': print_standard('Please reboot and try again.') pause('Press Enter to exit... ') exit_script(1) - except SystemExit: - pass + except SystemExit as sys_exit: + exit_script(sys_exit.code) except: major_exception() diff --git a/.bin/Scripts/winpe_root_menu.py b/.bin/Scripts/winpe_root_menu.py index a9555e07..94669609 100644 --- a/.bin/Scripts/winpe_root_menu.py +++ b/.bin/Scripts/winpe_root_menu.py @@ -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() diff --git a/.bin/_Drivers/SDIO/sdi.cfg b/.bin/_Drivers/SDIO/sdi.cfg index d1bd67fb..45b0e7c2 100644 --- a/.bin/_Drivers/SDIO/sdi.cfg +++ b/.bin/_Drivers/SDIO/sdi.cfg @@ -21,4 +21,4 @@ -uplimit:1 -connections:0 --expertmode -norestorepnt -showdrpnames2 -onlyupdates -preservecfg -novirusalerts \ No newline at end of file +-expertmode -showdrpnames2 -onlyupdates -preservecfg -novirusalerts diff --git a/.bin/d7ii/3rd Party Tools/Python_Check.cmd b/.bin/d7ii/3rd Party Tools/Python_Check.cmd new file mode 100644 index 00000000..aa4e7394 --- /dev/null +++ b/.bin/d7ii/3rd Party Tools/Python_Check.cmd @@ -0,0 +1,30 @@ +:: Python compatibility check +@echo off + +setlocal +pushd "%~dp0\..\.." +set "bin=%cd%" +set "python=%bin%\Python\x32\python.exe" + +rem Verify Python can run +"%python%" --version >nul || goto ErrorPythonUnsupported +goto Done + +:ErrorPythonUnsupported +rem The Windows installation lacks Windows update KB2999226 needed to run Python +echo. +echo ERROR: Failed to run Python, try installing Windows update KB2999226. +echo NOTE: That update is from October 2015 so this system is SEVERELY outdated +echo. +echo Please stop Auto Mode, exit this script, install the update, and try again. +echo. +echo Press Enter to exit... +pause>nul +if exist "%bin%\..\Installers\Extras\Windows Updates" ( + start "" "explorer.exe" + start "" "explorer.exe" "%bin%\..\Installers\Extras\Windows Updates" +) + +:Done +popd +endlocal diff --git a/.bin/d7ii/Config/AltText.ini b/.bin/d7ii/Config/AltText.ini index b58b6c23..0ce2c640 100644 --- a/.bin/d7ii/Config/AltText.ini +++ b/.bin/d7ii/Config/AltText.ini @@ -5,7 +5,7 @@ Autoruns (Verify and Log)=Manages Startup Items Google Chrome Software Removal Tool=Remove add-ons, extensions, toolbars, and other software that may interfere with the operation of Google Chrome. VipreRescueScanner (Deep Scan)=Virus scanner (Designed for both the Malware Removal and the Offline Operations tab) VipreRescueScanner (Quick Scan)=Virus scanner (Designed for both the Malware Removal and the Offline Operations tab) -=Install/Upgrade MBAM +=Python Check [ReportDesc] Autoruns=Examined Windows startup items and removed unnecessary entries. Autoruns_Copy=Examined Windows startup items and removed unnecessary entries. @@ -36,4 +36,4 @@ VipreRescueScanner (Quick Scan)=Ran virus scans (Vipre) 22=Repaired the Windows Update services responsible for Windows Update functionality. 38=Performed repair routines to ensure the Winsock is operating properly. 83=Examined internet speed/bandwidth. -=Malwarebytes installed successfully. +=Python compatibility check diff --git a/.bin/d7ii/Config/CustomApps/Disable Windows Updates.cfg b/.bin/d7ii/Config/CustomApps/Disable Windows Updates.cfg index 8b47cecc..6d110bb5 100644 --- a/.bin/d7ii/Config/CustomApps/Disable Windows Updates.cfg +++ b/.bin/d7ii/Config/CustomApps/Disable Windows Updates.cfg @@ -1,5 +1,5 @@ [Config] -LastEditDate=8/30/2018 10:49:46 AM +LastEditDate=5/6/2019 6:09:55 PM PostRunApp= AppParms=.bin\Scripts\launchers_for_d7\Disable Windows Updates.cmd UseFTPServer=0 @@ -9,7 +9,7 @@ AppWait=1 EmailBeforeExecution=0 PriorAlert=0 ServiceWait=0 -AppMsgBox=1 +AppMsgBox=0 AppRandomize=0 SaveConfigAfter=0 MoveSnatchReports=0 diff --git a/.bin/d7ii/Config/CustomApps/Python Check.cfg b/.bin/d7ii/Config/CustomApps/Python Check.cfg new file mode 100644 index 00000000..5b12c7a5 --- /dev/null +++ b/.bin/d7ii/Config/CustomApps/Python Check.cfg @@ -0,0 +1,32 @@ +[Config] +LastEditDate=5/6/2019 6:48:27 PM +PostRunApp= +AppDesc=Python Check +App=Python_Check.cmd +UseFTPServer=0 +AlwaysAttemptDownload=0 +DLafterXdays=5 +AppWait=0 +EmailBeforeExecution=0 +PriorAlert=0 +ServiceWait=0 +AppMsgBox=0 +AppWaitTime=30 +AppRandomize=0 +SaveConfigAfter=0 +MoveSnatchReports=0 +SnatchReportsToMalwareLogs=1 +RunInCMD=0 +SendEnter=0 +RunWithSystemAccess=0 +IsDLInstaller=0 +LogVerbiage=Python compatibility check +32=1 +64=1 +XP=1 +Vista=1 +7=1 +8=1 +Servers=1 +NonDirectURLs=0 +AutoFlag=0 diff --git a/.bin/d7ii/Config/Profiles/Default.cfg b/.bin/d7ii/Config/Profiles/Default.cfg index 161e77e5..ba51d4e8 100644 --- a/.bin/d7ii/Config/Profiles/Default.cfg +++ b/.bin/d7ii/Config/Profiles/Default.cfg @@ -782,6 +782,7 @@ WizardKit System Diagnostics=1 1=1 RKill (Auto)=1 Disable Windows Updates=1 +Python Check=1 [Malware3] ComboFix=0 ComboFix (Uninstall)=0 diff --git a/.bin/d7ii/Config/SortOrder/MalwareBox2.cfg b/.bin/d7ii/Config/SortOrder/MalwareBox2.cfg index 3ef28f76..f26c90a5 100644 --- a/.bin/d7ii/Config/SortOrder/MalwareBox2.cfg +++ b/.bin/d7ii/Config/SortOrder/MalwareBox2.cfg @@ -1 +1 @@ -Disable Windows Updates|98|RKill (Auto)|Kaspersky TDSSKiller (Silent)|WizardKit System Diagnostics|34|Emsisoft a2cmd Deep Scan|HitmanPro|1|98| +Python Check|Disable Windows Updates|98|RKill (Auto)|Kaspersky TDSSKiller (Silent)|WizardKit System Diagnostics|34|Emsisoft a2cmd Deep Scan|HitmanPro|1|98| diff --git a/.bin/d7ii/Config/SortOrder/MalwareBox3.cfg b/.bin/d7ii/Config/SortOrder/MalwareBox3.cfg index 75da8a9d..72196949 100644 --- a/.bin/d7ii/Config/SortOrder/MalwareBox3.cfg +++ b/.bin/d7ii/Config/SortOrder/MalwareBox3.cfg @@ -1 +1 @@ -Malwarebytes Download|Malwarebytes Install|Malwarebytes Scan|Malwarebytes Uninstall|AdwCleaner (Updated)|IObit Uninstaller|Install SW Bundle|WizardKit Browser Reset|WizardKit User Checklist|WizardKit System Checklist|Bitdefender Rootkit Remover| +Malwarebytes Download|Malwarebytes Install|Malwarebytes Scan|Malwarebytes Uninstall|AdwCleaner (Updated)|IObit Uninstaller|Bitdefender Rootkit Remover| diff --git a/.cbin/_include/AIDA64/full.rpf b/.cbin/_include/AIDA64/full.rpf index 28baab65..8d7df2ad 100644 --- a/.cbin/_include/AIDA64/full.rpf +++ b/.cbin/_include/AIDA64/full.rpf @@ -92,7 +92,6 @@ InfoPage="Config;Control Panel" InfoPage="Config;Recycle Bin" InfoPage="Config;System Files" InfoPage="Config;System Folders" -InfoPage="Config;Event Logs" InfoPage="Database;Database Software" InfoPage="Database;BDE Drivers" InfoPage="Database;ODBC Drivers" diff --git a/.cbin/_include/ESETConfigs/eset-config-no-pup.xml b/.cbin/_include/ESETConfigs/eset-config-msp.xml similarity index 90% rename from .cbin/_include/ESETConfigs/eset-config-no-pup.xml rename to .cbin/_include/ESETConfigs/eset-config-msp.xml index ac4e010e..36d01521 100644 --- a/.cbin/_include/ESETConfigs/eset-config-no-pup.xml +++ b/.cbin/_include/ESETConfigs/eset-config-msp.xml @@ -1,6 +1,31 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -11,7 +36,7 @@ - + @@ -125,6 +150,10 @@ + + + + @@ -145,10 +174,6 @@ - - - - @@ -238,6 +263,7 @@ + @@ -255,11 +281,19 @@ - + - - - + + + + + + + + + + + @@ -269,10 +303,10 @@ - - - - + + + + @@ -335,6 +369,9 @@ + + + @@ -394,28 +431,7 @@ - - - - - - - - - - - - - - - - - - - - - - + @@ -471,7 +487,6 @@ - @@ -507,59 +522,23 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -568,24 +547,10 @@ - - - - - - - - - - - - - - @@ -642,7 +607,7 @@ - + @@ -669,28 +634,6 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -729,14 +672,9 @@ - + - - - - - @@ -768,6 +706,7 @@ + @@ -784,14 +723,14 @@ - - - + + + - - - + + + @@ -942,7 +881,7 @@ - + @@ -1462,7 +1401,7 @@ - + @@ -1633,36 +1572,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.cbin/_include/ESETConfigs/eset-config.xml b/.cbin/_include/ESETConfigs/eset-config.xml index 31804d40..772c6a03 100644 --- a/.cbin/_include/ESETConfigs/eset-config.xml +++ b/.cbin/_include/ESETConfigs/eset-config.xml @@ -1,6 +1,31 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -11,7 +36,7 @@ - + @@ -125,6 +150,10 @@ + + + + @@ -145,10 +174,6 @@ - - - - @@ -238,6 +263,7 @@ + @@ -255,11 +281,19 @@ - + - - - + + + + + + + + + + + @@ -269,10 +303,10 @@ - - - - + + + + @@ -335,6 +369,9 @@ + + + @@ -394,28 +431,7 @@ - - - - - - - - - - - - - - - - - - - - - - + @@ -471,7 +487,6 @@ - @@ -507,59 +522,23 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -568,24 +547,10 @@ - - - - - - - - - - - - - - @@ -596,8 +561,8 @@ - - + + @@ -669,28 +634,6 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -732,11 +675,6 @@ - - - - - @@ -768,6 +706,7 @@ + @@ -784,14 +723,14 @@ - - - + + + - - - + + + @@ -942,7 +881,7 @@ - + @@ -1462,7 +1401,7 @@ - + @@ -1633,36 +1572,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.linux_items/include/EFI/boot/icons/1201_mac-dgpu.png b/.linux_items/include/EFI/boot/icons/dgpu.png similarity index 100% rename from .linux_items/include/EFI/boot/icons/1201_mac-dgpu.png rename to .linux_items/include/EFI/boot/icons/dgpu.png diff --git a/.linux_items/include/EFI/boot/refind.conf b/.linux_items/include/EFI/boot/refind.conf index e7dcbfb6..34a638c1 100644 --- a/.linux_items/include/EFI/boot/refind.conf +++ b/.linux_items/include/EFI/boot/refind.conf @@ -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,30 +27,42 @@ 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#menuentry "ESET SysRescue Live" { -#UFD# icon /EFI/boot/icons/1201_eset.png -#UFD# loader /EFI/ESET/grubx64.efi -#UFD#} -#UFD#menuentry "HDClone" { -#UFD# icon /EFI/boot/icons/1201_hdclone.png -#UFD# loader /EFI/HDClone/bootx64.efi -#UFD#} -#UFD#menuentry "Mac dGPU Disable Tool" { -#UFD# icon /EFI/boot/icons/1201_mac-dgpu.png -#UFD# loader /dgpu/boot/x86_64/vmlinuz -#UFD# initrd /dgpu/boot/intel_ucode.img -#UFD# initrd /dgpu/boot/x86_64/archiso.img -#UFD# options "archisobasedir=dgpu archisolabel=1201_UFD nomodeset" -#UFD#} + +#UFD-ESET#menuentry "ESET SysRescue Live" { +#UFD-ESET# icon /EFI/boot/icons/1201_eset.png +#UFD-ESET# loader /EFI/ESET/grubx64.efi +#UFD-ESET#} + +#UFD-HDCLONE#menuentry "HDClone" { +#UFD-HDCLONE# icon /EFI/boot/icons/1201_hdclone.png +#UFD-HDCLONE# loader /EFI/HDClone/bootx64.efi +#UFD-HDCLONE#} + +#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#} + diff --git a/.linux_items/include/airootfs/etc/adjtime b/.linux_items/include/airootfs/etc/adjtime new file mode 100644 index 00000000..df526cf2 --- /dev/null +++ b/.linux_items/include/airootfs/etc/adjtime @@ -0,0 +1,3 @@ +0.0 0 0 +0 +LOCAL diff --git a/.linux_items/include/airootfs/etc/skel/.aliases b/.linux_items/include/airootfs/etc/skel/.aliases index d6486258..b0068be3 100644 --- a/.linux_items/include/airootfs/etc/skel/.aliases +++ b/.linux_items/include/airootfs/etc/skel/.aliases @@ -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' diff --git a/.linux_items/include/airootfs/etc/skel/.tmux.conf b/.linux_items/include/airootfs/etc/skel/.tmux.conf index a3712835..c82d4600 100644 --- a/.linux_items/include/airootfs/etc/skel/.tmux.conf +++ b/.linux_items/include/airootfs/etc/skel/.tmux.conf @@ -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 diff --git a/.linux_items/include/airootfs/etc/skel/.update_network b/.linux_items/include/airootfs/etc/skel/.update_network index e7119a37..fb3ec990 100755 --- a/.linux_items/include/airootfs/etc/skel/.update_network +++ b/.linux_items/include/airootfs/etc/skel/.update_network @@ -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 diff --git a/.linux_items/include/syslinux/wk_head.cfg b/.linux_items/include/syslinux/wk_head.cfg index d2f1ee9f..2d57c98b 100644 --- a/.linux_items/include/syslinux/wk_head.cfg +++ b/.linux_items/include/syslinux/wk_head.cfg @@ -1,7 +1,7 @@ SERIAL 0 38400 UI boot/syslinux/vesamenu.c32 MENU TITLE _______ -MENU BACKGROUND syslinux.png +MENU BACKGROUND syslinux.jpg MENU WIDTH 80 MENU MARGIN 10 diff --git a/.linux_items/include/syslinux/wk_pxe.cfg b/.linux_items/include/syslinux/wk_pxe.cfg index 10112666..20bdf3dd 100644 --- a/.linux_items/include/syslinux/wk_pxe.cfg +++ b/.linux_items/include/syslinux/wk_pxe.cfg @@ -1,5 +1,5 @@ INCLUDE boot/syslinux/wk_head.cfg -MENU BACKGROUND pxelinux.png +MENU BACKGROUND pxelinux.jpg INCLUDE boot/syslinux/wk_pxe_linux.cfg #UFD-WINPE#INCLUDE boot/syslinux/wk_pxe_winpe.cfg diff --git a/.linux_items/include/syslinux/wk_sys.cfg b/.linux_items/include/syslinux/wk_sys.cfg index 442ec2a3..f756e525 100644 --- a/.linux_items/include/syslinux/wk_sys.cfg +++ b/.linux_items/include/syslinux/wk_sys.cfg @@ -2,7 +2,7 @@ INCLUDE boot/syslinux/wk_head.cfg INCLUDE boot/syslinux/wk_sys_linux.cfg #UFD-WINPE#INCLUDE boot/syslinux/wk_sys_winpe.cfg -#UFD#INCLUDE boot/syslinux/1201_hdclone.cfg -#UFD#INCLUDE boot/syslinux/1201_eset.cfg +#UFD-HDCLONE#INCLUDE boot/syslinux/1201_hdclone.cfg +#UFD-ESET#INCLUDE boot/syslinux/1201_eset.cfg #DISABLED_UPSTREAM_BUG#INCLUDE boot/syslinux/wk_hdt.cfg INCLUDE boot/syslinux/wk_tail.cfg diff --git a/.linux_items/include/syslinux/wk_sys_linux.cfg b/.linux_items/include/syslinux/wk_sys_linux.cfg index 9c229249..b6a9370c 100644 --- a/.linux_items/include/syslinux/wk_sys_linux.cfg +++ b/.linux_items/include/syslinux/wk_sys_linux.cfg @@ -8,16 +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_sys_linux_extras -TEXT HELP -Show extra Linux options -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) @@ -26,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 + diff --git a/.linux_items/include_x/airootfs/etc/skel/.Xresources b/.linux_items/include_x/airootfs/etc/skel/.Xresources index 8e303276..d659b735 100755 --- a/.linux_items/include_x/airootfs/etc/skel/.Xresources +++ b/.linux_items/include_x/airootfs/etc/skel/.Xresources @@ -23,7 +23,7 @@ URxvt*externalBorder: 0 !URxvt.colorUL: #87afd7 URxvt.geometry: 92x16 URxvt.internalBorder: 8 -URxvt.shading: 7 +URxvt.shading: 10 URxvt.transparent: true ! Base16 Isotope diff --git a/.linux_items/include_x/airootfs/etc/skel/.config/rofi/config b/.linux_items/include_x/airootfs/etc/skel/.config/rofi/config index 5c27c752..fd8bc24c 100644 --- a/.linux_items/include_x/airootfs/etc/skel/.config/rofi/config +++ b/.linux_items/include_x/airootfs/etc/skel/.config/rofi/config @@ -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 diff --git a/.linux_items/include_x/airootfs/etc/skel/.config/systemd/user/timers.target.wants/update-conky.timer b/.linux_items/include_x/airootfs/etc/skel/.config/systemd/user/timers.target.wants/update-conky.timer new file mode 120000 index 00000000..dc3ece34 --- /dev/null +++ b/.linux_items/include_x/airootfs/etc/skel/.config/systemd/user/timers.target.wants/update-conky.timer @@ -0,0 +1 @@ +../update-conky.timer \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/etc/skel/.config/systemd/user/update-conky.service b/.linux_items/include_x/airootfs/etc/skel/.config/systemd/user/update-conky.service new file mode 100644 index 00000000..d2048e93 --- /dev/null +++ b/.linux_items/include_x/airootfs/etc/skel/.config/systemd/user/update-conky.service @@ -0,0 +1,6 @@ +[Unit] +Description=Conky config update service + +[Service] +Type=oneshot +ExecStart=%h/.update_conky diff --git a/.linux_items/include_x/airootfs/etc/skel/.config/systemd/user/update-conky.timer b/.linux_items/include_x/airootfs/etc/skel/.config/systemd/user/update-conky.timer new file mode 100644 index 00000000..c742a10a --- /dev/null +++ b/.linux_items/include_x/airootfs/etc/skel/.config/systemd/user/update-conky.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Conky config update timer + +[Timer] +OnBootSec=5s +OnUnitActiveSec=30s +Unit=update-conky.service + +[Install] +WantedBy=timers.target diff --git a/.linux_items/include_x/airootfs/etc/skel/.conkyrc b/.linux_items/include_x/airootfs/etc/skel/.conkyrc_base similarity index 100% rename from .linux_items/include_x/airootfs/etc/skel/.conkyrc rename to .linux_items/include_x/airootfs/etc/skel/.conkyrc_base diff --git a/.linux_items/include_x/airootfs/etc/skel/.update_conky b/.linux_items/include_x/airootfs/etc/skel/.update_conky index 79801d8b..0e45e13d 100755 --- a/.linux_items/include_x/airootfs/etc/skel/.update_conky +++ b/.linux_items/include_x/airootfs/etc/skel/.update_conky @@ -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 diff --git a/.linux_items/include_x/airootfs/etc/skel/.update_x b/.linux_items/include_x/airootfs/etc/skel/.update_x index 650d3162..06611081 100755 --- a/.linux_items/include_x/airootfs/etc/skel/.update_x +++ b/.linux_items/include_x/airootfs/etc/skel/.update_x @@ -4,6 +4,7 @@ REGEX_XRANDR='^.* ([0-9]+)x([0-9]+)\+[0-9]+\+[0-9]+.* ([0-9]+)mm x ([0-9]+)mm.*$' REGEX_URXVT='(URxvt.geometry:\s+).*' +TEST_STATION_WALLPAPERS='/usr/share/wallpaper/test-stations' # Get screen data xrandr_str="$(xrandr | grep mm | head -1)" @@ -32,10 +33,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 +48,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,13 +71,29 @@ 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 xrdb -merge $HOME/.Xresources +# Set wallpaper +_hostname="$(hostnamectl --static | sed 's/.1201.com//')" +_ip="$(ip a show scope global \ + | grep inet \ + | head -1 \ + | sed -r 's#.*inet ([0-9]+.[0-9]+.[0-9]+.[0-9]+.)/.*#\1#')" +if [[ -e "$TEST_STATION_WALLPAPERS/${_hostname:+x}" ]]; then + feh --bg-fill "$TEST_STATION_WALLPAPERS/${_hostname:+x}" +elif [[ -e "$TEST_STATION_WALLPAPERS/${_ip:+x}" ]]; then + feh --bg-fill "$TEST_STATION_WALLPAPERS/${_ip:+x}" +else + feh --bg-fill "$HOME/.wallpaper" +fi + # Start common desktop apps -feh --bg-fill "$HOME/.wallpaper" compton --backend xrender --xrender-sync --xrender-sync-fence & sleep 1s x0vncserver -display :0 -passwordfile $HOME/.vnc/passwd -AlwaysShared & diff --git a/.linux_items/include_x/airootfs/etc/skel/.zlogin b/.linux_items/include_x/airootfs/etc/skel/.zlogin index 775ac320..8505b95a 100644 --- a/.linux_items/include_x/airootfs/etc/skel/.zlogin +++ b/.linux_items/include_x/airootfs/etc/skel/.zlogin @@ -12,9 +12,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 diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.30 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.30 new file mode 120000 index 00000000..e7e8c5aa --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.30 @@ -0,0 +1 @@ +bender \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.31 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.31 new file mode 120000 index 00000000..80059e2b --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.31 @@ -0,0 +1 @@ +cortana \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.32 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.32 new file mode 120000 index 00000000..0cf4a743 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.32 @@ -0,0 +1 @@ +glados \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.33 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.33 new file mode 120000 index 00000000..859a8a22 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.33 @@ -0,0 +1 @@ +shodan \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.34 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.34 new file mode 120000 index 00000000..2656ea46 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.34 @@ -0,0 +1 @@ +skynet \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.35 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.35 new file mode 120000 index 00000000..fc6bb73b --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.35 @@ -0,0 +1 @@ +sex-robot \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.36 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.36 new file mode 120000 index 00000000..221efe41 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.36 @@ -0,0 +1 @@ +combine \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.37 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.37 new file mode 120000 index 00000000..6c400f05 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.37 @@ -0,0 +1 @@ +locutus \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.38 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.38 new file mode 120000 index 00000000..64c56a36 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.38 @@ -0,0 +1 @@ +six \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.39 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.39 new file mode 120000 index 00000000..45f6d8e7 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.39 @@ -0,0 +1 @@ +Data \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.40 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.40 new file mode 120000 index 00000000..b69e57d2 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.40 @@ -0,0 +1 @@ +Control \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.41 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.41 new file mode 120000 index 00000000..0e2e8212 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.41 @@ -0,0 +1 @@ +Supremo \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.42 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.42 new file mode 120000 index 00000000..6f65f708 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.42 @@ -0,0 +1 @@ +Unicron \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.43 b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.43 new file mode 120000 index 00000000..ec4183e6 --- /dev/null +++ b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/10.120.1.43 @@ -0,0 +1 @@ +Lore \ No newline at end of file diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Control b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Control new file mode 100644 index 00000000..22a30dd6 Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Control differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Data b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Data new file mode 100644 index 00000000..0f41842a Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Data differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Lore b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Lore new file mode 100644 index 00000000..710f9359 Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Lore differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Supremo b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Supremo new file mode 100644 index 00000000..4006cb32 Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Supremo differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Unicron b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Unicron new file mode 100644 index 00000000..4089bb81 Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/Unicron differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/bender b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/bender new file mode 100644 index 00000000..2b39c767 Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/bender differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/combine b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/combine new file mode 100644 index 00000000..8a8b0b76 Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/combine differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/cortana b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/cortana new file mode 100644 index 00000000..01dfd980 Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/cortana differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/glados b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/glados new file mode 100644 index 00000000..661195b8 Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/glados differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/locutus b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/locutus new file mode 100644 index 00000000..01be685d Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/locutus differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/sex-robot b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/sex-robot new file mode 100644 index 00000000..ed63559d Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/sex-robot differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/shodan b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/shodan new file mode 100644 index 00000000..c781fb2e Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/shodan differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/six b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/six new file mode 100644 index 00000000..a32bf534 Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/six differ diff --git a/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/skynet b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/skynet new file mode 100644 index 00000000..b4d6cd17 Binary files /dev/null and b/.linux_items/include_x/airootfs/usr/share/wallpapers/test-stations/skynet differ diff --git a/.linux_items/known_networks b/.linux_items/known_networks new file mode 100644 index 00000000..08b60f1e --- /dev/null +++ b/.linux_items/known_networks @@ -0,0 +1,4 @@ +#Put WiFi network info here +#'WiFi SSID': 'WiFi Password' +'1201computersXI': 'wifipassword' +'1201computersXI-5g': 'wifipassword' diff --git a/.linux_items/packages/dependencies b/.linux_items/packages/dependencies index 4558e9eb..5600fec3 100644 --- a/.linux_items/packages/dependencies +++ b/.linux_items/packages/dependencies @@ -21,6 +21,7 @@ ntfs-3g openssh p7zip pango +perl-rename progsreiserfs refind-efi rsync diff --git a/.linux_items/packages/live_add b/.linux_items/packages/live_add index 5d28f9f9..5000edcf 100644 --- a/.linux_items/packages/live_add +++ b/.linux_items/packages/live_add @@ -16,6 +16,7 @@ e2fsprogs hexedit hfsprogs htop +iwd ldmtool ldns lha @@ -32,9 +33,11 @@ networkmanager p7zip progsreiserfs python +python-docopt python-gnuplot python-mysql-connector python-psutil +python-pytz python-requests reiserfsprogs rfkill diff --git a/.linux_items/packages/live_remove b/.linux_items/packages/live_remove index 9feb3c02..2e1ffbe7 100644 --- a/.linux_items/packages/live_remove +++ b/.linux_items/packages/live_remove @@ -14,6 +14,7 @@ rp-pppoe smartmontools speedtouch testdisk +wpa_actiond vim-minimal vpnc wvdial diff --git a/Build Kit.cmd b/Build Kit.cmd index 58272a45..982c90c8 100644 --- a/Build Kit.cmd +++ b/Build Kit.cmd @@ -14,7 +14,7 @@ robocopy /e .bin OUT_KIT\.bin robocopy /e .cbin OUT_KIT\.cbin copy LICENSE.txt OUT_KIT\LICENSE.txt copy README.md OUT_KIT\README.md -copy Images\ConEmu.png OUT_KIT\.bin\ConEmu\ +copy Images\ConEmu.jpg OUT_KIT\.bin\ConEmu\ mkdir OUT_KIT\.cbin >nul 2>&1 attrib +h OUT_KIT\.bin >nul 2>&1 attrib +h OUT_KIT\.cbin >nul 2>&1 diff --git a/Build Linux b/Build Linux index 8ed965b5..6f709a77 100755 --- a/Build Linux +++ b/Build Linux @@ -40,7 +40,7 @@ function ask() { fi done } - + function cleanup() { for d in "$TEMP_DIR" "$LIVE_DIR"; do if [[ -d "$d" ]]; then @@ -116,7 +116,7 @@ function copy_live_env() { rmdir "$LIVE_DIR/airootfs/etc/udev" --ignore-fail-on-non-empty rm "$LIVE_DIR/isolinux"/*.cfg rm "$LIVE_DIR/syslinux"/*.cfg "$LIVE_DIR/syslinux"/*.png - + # Add items rsync -aI "$ROOT_DIR/.linux_items/include/" "$LIVE_DIR/" if [[ "${1:-}" != "--minimal" ]]; then @@ -155,17 +155,17 @@ function update_live_env() { # Boot config (legacy) mkdir -p "$LIVE_DIR/arch" - cp "$ROOT_DIR/Images/Pxelinux.png" "$LIVE_DIR/arch/pxelinux.png" - cp "$ROOT_DIR/Images/Syslinux.png" "$LIVE_DIR/arch/syslinux.png" + cp "$ROOT_DIR/Images/Pxelinux.jpg" "$LIVE_DIR/arch/pxelinux.jpg" + cp "$ROOT_DIR/Images/Syslinux.jpg" "$LIVE_DIR/arch/syslinux.jpg" sed -i -r "s/_+/$KIT_NAME_FULL/" "$LIVE_DIR/syslinux/wk_head.cfg" mkdir -p "$TEMP_DIR" 2>/dev/null curl -Lo "$TEMP_DIR/wimboot.zip" "http://git.ipxe.org/releases/wimboot/wimboot-latest.zip" - 7z e "$TEMP_DIR/wimboot.zip" -o"$LIVE_DIR/arch/boot" 'wimboot*/LICENSE.txt' 'wimboot*/README.txt' 'wimboot*/wimboot' + 7z e -aoa "$TEMP_DIR/wimboot.zip" -o"$LIVE_DIR/arch/boot" 'wimboot*/LICENSE.txt' 'wimboot*/README.txt' 'wimboot*/wimboot' # Boot config (UEFI) mkdir -p "$LIVE_DIR/EFI/boot" cp "/usr/share/refind/refind_x64.efi" "$LIVE_DIR/EFI/boot/bootx64.efi" - cp "$ROOT_DIR/Images/rEFInd.png" "$LIVE_DIR/EFI/boot/rEFInd.png" + cp "$ROOT_DIR/Images/rEFInd.jpg" "$LIVE_DIR/EFI/boot/rEFInd.jpg" rsync -aI "/usr/share/refind/drivers_x64/" "$LIVE_DIR/EFI/boot/drivers_x64/" rsync -aI "/usr/share/refind/icons/" "$LIVE_DIR/EFI/boot/icons/" --exclude "/usr/share/refind/icons/svg" sed -i "s/%ARCHISO_LABEL%/${label}/" "$LIVE_DIR/EFI/boot/refind.conf" @@ -177,9 +177,9 @@ function update_live_env() { mkdir -p "$LIVE_DIR/EFI/memtest86/Benchmark" mkdir -p "$TEMP_DIR/memtest86" curl -Lo "$TEMP_DIR/memtest86/memtest86-usb.zip" "https://www.memtest86.com/downloads/memtest86-usb.zip" - 7z e "$TEMP_DIR/memtest86/memtest86-usb.zip" -o"$TEMP_DIR/memtest86" "memtest86-usb.img" - 7z e "$TEMP_DIR/memtest86/memtest86-usb.img" -o"$TEMP_DIR/memtest86" "MemTest86.img" - 7z x "$TEMP_DIR/memtest86/MemTest86.img" -o"$TEMP_DIR/memtest86" + 7z e -aoa "$TEMP_DIR/memtest86/memtest86-usb.zip" -o"$TEMP_DIR/memtest86" "memtest86-usb.img" + 7z e -aoa "$TEMP_DIR/memtest86/memtest86-usb.img" -o"$TEMP_DIR/memtest86" "MemTest86.img" + 7z x -aoa "$TEMP_DIR/memtest86/MemTest86.img" -o"$TEMP_DIR/memtest86" rm "$TEMP_DIR/memtest86/EFI/BOOT/BOOTIA32.efi" mv "$TEMP_DIR/memtest86/EFI/BOOT/BOOTX64.efi" "$LIVE_DIR/EFI/memtest86/memtestx64.efi" mv "$TEMP_DIR/memtest86/EFI/BOOT"/* "$LIVE_DIR/EFI/memtest86"/ @@ -195,7 +195,7 @@ function update_live_env() { # Hostname echo "$hostname" > "$LIVE_DIR/airootfs/etc/hostname" echo "127.0.1.1 $hostname.localdomain $hostname" >> "$LIVE_DIR/airootfs/etc/hosts" - + # Live packages while read -r p; do sed -i "/$p/d" "$LIVE_DIR/packages.x86_64" @@ -215,7 +215,7 @@ function update_live_env() { sed -i -r 's/^(.*mirrorlist.*)$/#NOPE#\1/' "$LIVE_DIR/airootfs/root/customize_airootfs.sh" echo "curl -o '/etc/pacman.d/mirrorlist' '$MIRRORLIST_SOURCE'" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" echo "sed -i 's/#Server/Server/g' /etc/pacman.d/mirrorlist" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" - + # MOTD sed -i -r "s/_+/$KIT_NAME_FULL Linux Environment/" "$LIVE_DIR/airootfs/etc/motd" @@ -223,7 +223,7 @@ function update_live_env() { git clone --depth=1 https://github.com/robbyrussell/oh-my-zsh.git "$SKEL_DIR/.oh-my-zsh" rm -Rf "$SKEL_DIR/.oh-my-zsh/.git" curl -o "$SKEL_DIR/.oh-my-zsh/themes/lean.zsh-theme" https://raw.githubusercontent.com/miekg/lean/master/prompt_lean_setup - + if [[ "${1:-}" != "--minimal" ]]; then # Openbox theme git clone --depth=1 https://github.com/addy-dclxvi/Openbox-Theme-Collections.git "$TEMP_DIR/ob-themes" @@ -238,7 +238,7 @@ function update_live_env() { # Shutdown stall fix echo "sed -i -r 's/^.*(DefaultTimeoutStartSec)=.*$/\1=15s/' /etc/systemd/system.conf" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" echo "sed -i -r 's/^.*(DefaultTimeoutStopSec)=.*$/\1=15s/' /etc/systemd/system.conf" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" - + # SSH mkdir -p "$SKEL_DIR/.ssh" ssh-keygen -b 4096 -C "$username@$hostname" -N "" -f "$SKEL_DIR/.ssh/id_rsa" @@ -247,7 +247,7 @@ function update_live_env() { sed -i -r '/.*PermitRootLogin.*/d' "$LIVE_DIR/airootfs/root/customize_airootfs.sh" echo "sed -i -r '/.*PermitRootLogin.*/d' /etc/ssh/sshd_config" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" cp "$ROOT_DIR/.linux_items/authorized_keys" "$SKEL_DIR/.ssh/authorized_keys" - + # Root user echo "echo 'root:$ROOT_PASSWORD' | chpasswd" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh" @@ -279,8 +279,12 @@ function update_live_env() { # Wallpaper mkdir -p "$LIVE_DIR/airootfs/usr/share/wallpaper" - cp "$ROOT_DIR/Images/Linux.png" "$LIVE_DIR/airootfs/usr/share/wallpaper/burned.in" + cp "$ROOT_DIR/Images/Linux.jpg" "$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() { @@ -335,7 +339,7 @@ function build_iso() { echo "Aborted." exit 1 fi - + # Set permissions echo "Setting permissions..." chown root:root "$LIVE_DIR" -R @@ -350,7 +354,7 @@ function build_iso() { fi done done - + # Build ISO prefix="${KIT_NAME_SHORT}-Linux" label="${KIT_NAME_SHORT}_LINUX" @@ -365,13 +369,44 @@ function build_iso() { chown $REAL_USER:$REAL_USER "$OUT_DIR" -R } +function build_all() { + if [[ "$EUID" -eq 0 ]]; then + echo "This section not meant to be run as root." + echo "Aborted." + exit 1 + fi + + # Prep for build (full) + cleanup + fix_kit_permissions + install_deps + load_settings --edit + update_repo + copy_live_env + update_live_env + # Rerun script as root to start Archiso build process + run_elevated "$(realpath "$0")" --build-iso + # Cleanup + mv -nv "$LIVE_DIR" "${LIVE_DIR}.full" + perl-rename -v "s/(${KIT_NAME_SHORT}-Linux)-(${DATE}.*)/\1-Full-\2/" "$OUT_DIR"/* + + # Prep for build (minimal) + copy_live_env --minimal + update_live_env --minimal + # Rerun script as root to start Archiso build process + run_elevated "$(realpath "$0")" --build-iso + # Cleanup + mv -nv "$LIVE_DIR" "${LIVE_DIR}.minimal" + perl-rename -v "s/(${KIT_NAME_SHORT}-Linux)-(${DATE}.*)/\1-Minimal-\2/" "$OUT_DIR"/* +} + function build_full() { if [[ "$EUID" -eq 0 ]]; then echo "This section not meant to be run as root." echo "Aborted." exit 1 fi - + # Prep for build cleanup fix_kit_permissions @@ -386,6 +421,11 @@ function build_full() { # Check input case ${1:-} in + -a|--build-all) + build_all + echo Done + ;; + -b|--build-full) build_full echo Done @@ -413,7 +453,7 @@ case ${1:-} in build_iso echo Done ;; - + -p|--prep-live-env) load_settings --edit copy_live_env @@ -430,7 +470,8 @@ case ${1:-} in echo "Usage: $(basename "$0") [OPTIONS]" echo "" echo "Options:" - echo " -b --build-full Perform all tasks and build iso" + echo " -a --build-all Perform all tasks to build all isos" + echo " -b --build-full Perform all tasks to build the full iso" echo " -h --help Show usage" echo "" echo "Advanced options:" diff --git a/Images/ConEmu.jpg b/Images/ConEmu.jpg new file mode 100644 index 00000000..2d8ef42b Binary files /dev/null and b/Images/ConEmu.jpg differ diff --git a/Images/ConEmu.png b/Images/ConEmu.png deleted file mode 100644 index c5bdb72e..00000000 Binary files a/Images/ConEmu.png and /dev/null differ diff --git a/Images/Linux.jpg b/Images/Linux.jpg new file mode 100644 index 00000000..92d2778d Binary files /dev/null and b/Images/Linux.jpg differ diff --git a/Images/Linux.png b/Images/Linux.png deleted file mode 100644 index 7fcf0056..00000000 Binary files a/Images/Linux.png and /dev/null differ diff --git a/Images/Pxelinux.jpg b/Images/Pxelinux.jpg new file mode 100644 index 00000000..1247bff9 Binary files /dev/null and b/Images/Pxelinux.jpg differ diff --git a/Images/Pxelinux.png b/Images/Pxelinux.png deleted file mode 100644 index f3a6a6d9..00000000 Binary files a/Images/Pxelinux.png and /dev/null differ diff --git a/Images/Syslinux.jpg b/Images/Syslinux.jpg new file mode 100644 index 00000000..1247bff9 Binary files /dev/null and b/Images/Syslinux.jpg differ diff --git a/Images/Syslinux.png b/Images/Syslinux.png deleted file mode 100644 index de82aa33..00000000 Binary files a/Images/Syslinux.png and /dev/null differ diff --git a/Images/WinPE.jpg b/Images/WinPE.jpg index 5bc27ca2..5ad51d84 100644 Binary files a/Images/WinPE.jpg and b/Images/WinPE.jpg differ diff --git a/Images/rEFInd.png b/Images/rEFInd.png index 7e8e2fe5..d5fb1742 100644 Binary files a/Images/rEFInd.png and b/Images/rEFInd.png differ