Merge branch 'new-linux-scripts' into linux
* Finished Python rewrite, ready for testing * (I think)
This commit is contained in:
commit
70e1655efe
76 changed files with 8103 additions and 7433 deletions
|
|
@ -1,13 +1,13 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## HW diagnostics - Prime95
|
||||
## Wizard Kit: Apple fan speed tool
|
||||
|
||||
SMCPATH="/sys/devices/platform/applesmc.768"
|
||||
SET_MAX="True"
|
||||
|
||||
function usage {
|
||||
echo "Usage: $0 auto|max"
|
||||
echo " e.g. $0 max"
|
||||
echo "Usage: $(basename "$0") auto|max"
|
||||
echo " e.g. $(basename "$0") max"
|
||||
}
|
||||
|
||||
# Set mode
|
||||
35
.bin/Scripts/borrowed/sensors-README.md
Normal file
35
.bin/Scripts/borrowed/sensors-README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
sensors.py
|
||||
==========
|
||||
python bindings using ctypes for libsensors3 of the [lm-sensors project](https://github.com/groeck/lm-sensors). The code was written against libsensors 3.3.4.
|
||||
|
||||
For documentation of the low level API see [sensors.h](https://github.com/groeck/lm-sensors/blob/master/lib/sensors.h). For an example of the high level API see [example.py](example.py).
|
||||
|
||||
For a GUI application that displays the sensor readings and is based on this library, take a look at [sensors-unity](https://launchpad.net/sensors-unity).
|
||||
|
||||
Features
|
||||
--------
|
||||
* Full access to low level libsensors3 API
|
||||
* High level iterator API
|
||||
* unicode handling
|
||||
* Python2 and Python3 compatible
|
||||
|
||||
Licensing
|
||||
---------
|
||||
LGPLv2 (same as libsensors3)
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
As Python does not support call by reference for primitive types some of the libsensors API had to be adapted:
|
||||
|
||||
```python
|
||||
# nr is changed by refrence in the C API
|
||||
chip_name, nr = sensors.get_detected_chips(None, nr)
|
||||
|
||||
# returns the value. throws on error
|
||||
val = sensors.get_value(chip, subfeature_nr)
|
||||
```
|
||||
|
||||
Missing Features (pull requests are welcome):
|
||||
* `sensors_subfeature_type` enum
|
||||
* `sensors_get_subfeature`
|
||||
* Error handlers
|
||||
236
.bin/Scripts/borrowed/sensors.py
Normal file
236
.bin/Scripts/borrowed/sensors.py
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
"""
|
||||
@package sensors.py
|
||||
Python Bindings for libsensors3
|
||||
|
||||
use the documentation of libsensors for the low level API.
|
||||
see example.py for high level API usage.
|
||||
|
||||
@author: Pavel Rojtberg (http://www.rojtberg.net)
|
||||
@see: https://github.com/paroj/sensors.py
|
||||
@copyright: LGPLv2 (same as libsensors) <http://opensource.org/licenses/LGPL-2.1>
|
||||
"""
|
||||
|
||||
from ctypes import *
|
||||
import ctypes.util
|
||||
|
||||
_libc = cdll.LoadLibrary(ctypes.util.find_library("c"))
|
||||
# see https://github.com/paroj/sensors.py/issues/1
|
||||
_libc.free.argtypes = [c_void_p]
|
||||
|
||||
_hdl = cdll.LoadLibrary(ctypes.util.find_library("sensors"))
|
||||
|
||||
version = c_char_p.in_dll(_hdl, "libsensors_version").value.decode("ascii")
|
||||
|
||||
class bus_id(Structure):
|
||||
_fields_ = [("type", c_short),
|
||||
("nr", c_short)]
|
||||
|
||||
class chip_name(Structure):
|
||||
_fields_ = [("prefix", c_char_p),
|
||||
("bus", bus_id),
|
||||
("addr", c_int),
|
||||
("path", c_char_p)]
|
||||
|
||||
class feature(Structure):
|
||||
_fields_ = [("name", c_char_p),
|
||||
("number", c_int),
|
||||
("type", c_int)]
|
||||
|
||||
# sensors_feature_type
|
||||
IN = 0x00
|
||||
FAN = 0x01
|
||||
TEMP = 0x02
|
||||
POWER = 0x03
|
||||
ENERGY = 0x04
|
||||
CURR = 0x05
|
||||
HUMIDITY = 0x06
|
||||
MAX_MAIN = 0x7
|
||||
VID = 0x10
|
||||
INTRUSION = 0x11
|
||||
MAX_OTHER = 0x12
|
||||
BEEP_ENABLE = 0x18
|
||||
|
||||
class subfeature(Structure):
|
||||
_fields_ = [("name", c_char_p),
|
||||
("number", c_int),
|
||||
("type", c_int),
|
||||
("mapping", c_int),
|
||||
("flags", c_uint)]
|
||||
|
||||
_hdl.sensors_get_detected_chips.restype = POINTER(chip_name)
|
||||
_hdl.sensors_get_features.restype = POINTER(feature)
|
||||
_hdl.sensors_get_all_subfeatures.restype = POINTER(subfeature)
|
||||
_hdl.sensors_get_label.restype = c_void_p # return pointer instead of str so we can free it
|
||||
_hdl.sensors_get_adapter_name.restype = c_char_p # docs do not say whether to free this or not
|
||||
_hdl.sensors_strerror.restype = c_char_p
|
||||
|
||||
### RAW API ###
|
||||
MODE_R = 1
|
||||
MODE_W = 2
|
||||
COMPUTE_MAPPING = 4
|
||||
|
||||
def init(cfg_file = None):
|
||||
file = _libc.fopen(cfg_file.encode("utf-8"), "r") if cfg_file is not None else None
|
||||
|
||||
if _hdl.sensors_init(file) != 0:
|
||||
raise Exception("sensors_init failed")
|
||||
|
||||
if file is not None:
|
||||
_libc.fclose(file)
|
||||
|
||||
def cleanup():
|
||||
_hdl.sensors_cleanup()
|
||||
|
||||
def parse_chip_name(orig_name):
|
||||
ret = chip_name()
|
||||
err= _hdl.sensors_parse_chip_name(orig_name.encode("utf-8"), byref(ret))
|
||||
|
||||
if err < 0:
|
||||
raise Exception(strerror(err))
|
||||
|
||||
return ret
|
||||
|
||||
def strerror(errnum):
|
||||
return _hdl.sensors_strerror(errnum).decode("utf-8")
|
||||
|
||||
def free_chip_name(chip):
|
||||
_hdl.sensors_free_chip_name(byref(chip))
|
||||
|
||||
def get_detected_chips(match, nr):
|
||||
"""
|
||||
@return: (chip, next nr to query)
|
||||
"""
|
||||
_nr = c_int(nr)
|
||||
|
||||
if match is not None:
|
||||
match = byref(match)
|
||||
|
||||
chip = _hdl.sensors_get_detected_chips(match, byref(_nr))
|
||||
chip = chip.contents if bool(chip) else None
|
||||
return chip, _nr.value
|
||||
|
||||
def chip_snprintf_name(chip, buffer_size=200):
|
||||
"""
|
||||
@param buffer_size defaults to the size used in the sensors utility
|
||||
"""
|
||||
ret = create_string_buffer(buffer_size)
|
||||
err = _hdl.sensors_snprintf_chip_name(ret, buffer_size, byref(chip))
|
||||
|
||||
if err < 0:
|
||||
raise Exception(strerror(err))
|
||||
|
||||
return ret.value.decode("utf-8")
|
||||
|
||||
def do_chip_sets(chip):
|
||||
"""
|
||||
@attention this function was not tested
|
||||
"""
|
||||
err = _hdl.sensors_do_chip_sets(byref(chip))
|
||||
if err < 0:
|
||||
raise Exception(strerror(err))
|
||||
|
||||
def get_adapter_name(bus):
|
||||
return _hdl.sensors_get_adapter_name(byref(bus)).decode("utf-8")
|
||||
|
||||
def get_features(chip, nr):
|
||||
"""
|
||||
@return: (feature, next nr to query)
|
||||
"""
|
||||
_nr = c_int(nr)
|
||||
feature = _hdl.sensors_get_features(byref(chip), byref(_nr))
|
||||
feature = feature.contents if bool(feature) else None
|
||||
return feature, _nr.value
|
||||
|
||||
def get_label(chip, feature):
|
||||
ptr = _hdl.sensors_get_label(byref(chip), byref(feature))
|
||||
val = cast(ptr, c_char_p).value.decode("utf-8")
|
||||
_libc.free(ptr)
|
||||
return val
|
||||
|
||||
def get_all_subfeatures(chip, feature, nr):
|
||||
"""
|
||||
@return: (subfeature, next nr to query)
|
||||
"""
|
||||
_nr = c_int(nr)
|
||||
subfeature = _hdl.sensors_get_all_subfeatures(byref(chip), byref(feature), byref(_nr))
|
||||
subfeature = subfeature.contents if bool(subfeature) else None
|
||||
return subfeature, _nr.value
|
||||
|
||||
def get_value(chip, subfeature_nr):
|
||||
val = c_double()
|
||||
err = _hdl.sensors_get_value(byref(chip), subfeature_nr, byref(val))
|
||||
if err < 0:
|
||||
raise Exception(strerror(err))
|
||||
return val.value
|
||||
|
||||
def set_value(chip, subfeature_nr, value):
|
||||
"""
|
||||
@attention this function was not tested
|
||||
"""
|
||||
val = c_double(value)
|
||||
err = _hdl.sensors_set_value(byref(chip), subfeature_nr, byref(val))
|
||||
if err < 0:
|
||||
raise Exception(strerror(err))
|
||||
|
||||
### Convenience API ###
|
||||
class ChipIterator:
|
||||
def __init__(self, match = None):
|
||||
self.match = parse_chip_name(match) if match is not None else None
|
||||
self.nr = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
chip, self.nr = get_detected_chips(self.match, self.nr)
|
||||
|
||||
if chip is None:
|
||||
raise StopIteration
|
||||
|
||||
return chip
|
||||
|
||||
def __del__(self):
|
||||
if self.match is not None:
|
||||
free_chip_name(self.match)
|
||||
|
||||
def next(self): # python2 compability
|
||||
return self.__next__()
|
||||
|
||||
class FeatureIterator:
|
||||
def __init__(self, chip):
|
||||
self.chip = chip
|
||||
self.nr = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
feature, self.nr = get_features(self.chip, self.nr)
|
||||
|
||||
if feature is None:
|
||||
raise StopIteration
|
||||
|
||||
return feature
|
||||
|
||||
def next(self): # python2 compability
|
||||
return self.__next__()
|
||||
|
||||
class SubFeatureIterator:
|
||||
def __init__(self, chip, feature):
|
||||
self.chip = chip
|
||||
self.feature = feature
|
||||
self.nr = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
subfeature, self.nr = get_all_subfeatures(self.chip, self.feature, self.nr)
|
||||
|
||||
if subfeature is None:
|
||||
raise StopIteration
|
||||
|
||||
return subfeature
|
||||
|
||||
def next(self): # python2 compability
|
||||
return self.__next__()
|
||||
30
.bin/Scripts/connect-to-network
Executable file
30
.bin/Scripts/connect-to-network
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## Wizard Kit: Network connection tool
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Init
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
sys.path.append(os.getcwd())
|
||||
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()
|
||||
|
||||
|
|
@ -8,7 +8,11 @@ import subprocess
|
|||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import winreg
|
||||
try:
|
||||
import winreg
|
||||
except ModuleNotFoundError:
|
||||
if psutil.WINDOWS:
|
||||
raise
|
||||
|
||||
from subprocess import CalledProcessError
|
||||
|
||||
|
|
@ -26,9 +30,13 @@ COLORS = {
|
|||
'YELLOW': '\033[33m',
|
||||
'BLUE': '\033[34m'
|
||||
}
|
||||
HKU = winreg.HKEY_USERS
|
||||
HKCU = winreg.HKEY_CURRENT_USER
|
||||
HKLM = winreg.HKEY_LOCAL_MACHINE
|
||||
try:
|
||||
HKU = winreg.HKEY_USERS
|
||||
HKCU = winreg.HKEY_CURRENT_USER
|
||||
HKLM = winreg.HKEY_LOCAL_MACHINE
|
||||
except NameError:
|
||||
if psutil.WINDOWS:
|
||||
raise
|
||||
|
||||
# Error Classes
|
||||
class BIOSKeyNotFoundError(Exception):
|
||||
|
|
@ -86,8 +94,11 @@ def ask(prompt='Kotaero!'):
|
|||
return answer
|
||||
|
||||
def clear_screen():
|
||||
"""Simple wrapper for cls."""
|
||||
os.system('cls')
|
||||
"""Simple wrapper for cls/clear."""
|
||||
if psutil.WINDOWS:
|
||||
os.system('cls')
|
||||
else:
|
||||
os.system('clear')
|
||||
|
||||
def convert_to_bytes(size):
|
||||
"""Convert human-readable size str to bytes and return an int."""
|
||||
|
|
@ -121,7 +132,7 @@ def exit_script(return_value=0):
|
|||
|
||||
# Open Log (if it exists)
|
||||
log = global_vars.get('LogFile', '')
|
||||
if log and os.path.exists(log):
|
||||
if log and os.path.exists(log) and psutil.WINDOWS:
|
||||
try:
|
||||
extract_item('NotepadPlusPlus', silent=True)
|
||||
popen_program(
|
||||
|
|
@ -160,8 +171,10 @@ def get_ticket_number():
|
|||
_input = input('Enter ticket number: ')
|
||||
if re.match(r'^([0-9]+([-_]?\w+|))$', _input):
|
||||
ticket_number = _input
|
||||
with open(r'{}\TicketNumber'.format(global_vars['LogDir']), 'w',
|
||||
encoding='utf-8') as f:
|
||||
out_file = r'{}\TicketNumber'.format(global_vars['LogDir'])
|
||||
if not psutil.WINDOWS:
|
||||
out_file = out_file.replace('\\', '/')
|
||||
with open(out_file, 'w', encoding='utf-8') as f:
|
||||
f.write(ticket_number)
|
||||
return ticket_number
|
||||
|
||||
|
|
@ -220,7 +233,8 @@ def major_exception():
|
|||
|
||||
def menu_select(title='~ Untitled Menu ~',
|
||||
prompt='Please make a selection', secret_exit=False,
|
||||
main_entries=[], action_entries=[], disabled_label='DISABLED'):
|
||||
main_entries=[], action_entries=[], disabled_label='DISABLED',
|
||||
spacer=''):
|
||||
"""Display options in a menu and return selected option as a str."""
|
||||
# Bail early
|
||||
if not main_entries and not action_entries:
|
||||
|
|
@ -231,7 +245,7 @@ def menu_select(title='~ Untitled Menu ~',
|
|||
title = '{}\n\n{}'.format(global_vars['Title'], title)
|
||||
|
||||
# Build menu
|
||||
menu_splash = '{}\n\n'.format(title)
|
||||
menu_splash = '{}\n{}\n'.format(title, spacer)
|
||||
width = len(str(len(main_entries)))
|
||||
valid_answers = []
|
||||
if (secret_exit):
|
||||
|
|
@ -242,7 +256,7 @@ def menu_select(title='~ Untitled Menu ~',
|
|||
entry = main_entries[i]
|
||||
# Add Spacer
|
||||
if ('CRLF' in entry):
|
||||
menu_splash += '\n'
|
||||
menu_splash += '{}\n'.format(spacer)
|
||||
entry_str = '{number:>{width}}: {name}'.format(
|
||||
number = i+1,
|
||||
width = width,
|
||||
|
|
@ -255,13 +269,13 @@ def menu_select(title='~ Untitled Menu ~',
|
|||
else:
|
||||
valid_answers.append(str(i+1))
|
||||
menu_splash += '{}\n'.format(entry_str)
|
||||
menu_splash += '\n'
|
||||
menu_splash += '{}\n'.format(spacer)
|
||||
|
||||
# Add action entries
|
||||
for entry in action_entries:
|
||||
# Add Spacer
|
||||
if ('CRLF' in entry):
|
||||
menu_splash += '\n'
|
||||
menu_splash += '{}\n'.format(spacer)
|
||||
valid_answers.append(entry['Letter'])
|
||||
menu_splash += '{letter:>{width}}: {name}\n'.format(
|
||||
letter = entry['Letter'].upper(),
|
||||
|
|
@ -272,7 +286,7 @@ def menu_select(title='~ Untitled Menu ~',
|
|||
answer = ''
|
||||
|
||||
while (answer.upper() not in valid_answers):
|
||||
os.system('cls')
|
||||
clear_screen()
|
||||
print(menu_splash)
|
||||
answer = input('{}: '.format(prompt))
|
||||
|
||||
|
|
@ -294,7 +308,11 @@ def pause(prompt='Press Enter to continue... '):
|
|||
|
||||
def ping(addr='google.com'):
|
||||
"""Attempt to ping addr."""
|
||||
cmd = ['ping', '-n', '2', addr]
|
||||
cmd = [
|
||||
'ping',
|
||||
'-n' if psutil.WINDOWS else '-c',
|
||||
'2',
|
||||
addr]
|
||||
run_program(cmd)
|
||||
|
||||
def popen_program(cmd, pipe=False, minimized=False, shell=False, **kwargs):
|
||||
|
|
@ -341,7 +359,7 @@ def print_warning(*args, **kwargs):
|
|||
|
||||
def print_log(message='', end='\n', timestamp=True):
|
||||
time_str = time.strftime("%Y-%m-%d %H%M%z: ") if timestamp else ''
|
||||
if 'LogFile' in global_vars and global_vars['LogFile'] is not None:
|
||||
if 'LogFile' in global_vars and global_vars['LogFile']:
|
||||
with open(global_vars['LogFile'], 'a', encoding='utf-8') as f:
|
||||
for line in message.splitlines():
|
||||
f.write('{timestamp}{line}{end}'.format(
|
||||
|
|
@ -442,10 +460,13 @@ def try_and_print(message='Trying...',
|
|||
try:
|
||||
out = function(*args, **kwargs)
|
||||
if print_return:
|
||||
print_standard(out[0], timestamp=False)
|
||||
for item in out[1:]:
|
||||
str_list = out
|
||||
if isinstance(out, subprocess.CompletedProcess):
|
||||
str_list = out.stdout.decode().strip().splitlines()
|
||||
print_standard(str_list[0].strip(), timestamp=False)
|
||||
for item in str_list[1:]:
|
||||
print_standard('{indent}{item}'.format(
|
||||
indent=' '*(indent+width), item=item))
|
||||
indent=' '*(indent+width), item=item.strip()))
|
||||
elif silent_function:
|
||||
print_success(cs, timestamp=False)
|
||||
except w_exceptions as e:
|
||||
|
|
@ -534,15 +555,22 @@ def wait_for_process(name, poll_rate=3):
|
|||
def init_global_vars():
|
||||
"""Sets global variables based on system info."""
|
||||
print_info('Initializing')
|
||||
os.system('title Wizard Kit')
|
||||
init_functions = [
|
||||
['Checking .bin...', find_bin],
|
||||
['Checking environment...', set_common_vars],
|
||||
['Checking OS...', check_os],
|
||||
['Checking tools...', check_tools],
|
||||
['Creating folders...', make_tmp_dirs],
|
||||
['Clearing collisions...', clean_env_vars],
|
||||
]
|
||||
if psutil.WINDOWS:
|
||||
os.system('title Wizard Kit')
|
||||
if psutil.LINUX:
|
||||
init_functions = [
|
||||
['Checking environment...', set_linux_vars],
|
||||
['Clearing collisions...', clean_env_vars],
|
||||
]
|
||||
else:
|
||||
init_functions = [
|
||||
['Checking .bin...', find_bin],
|
||||
['Checking environment...', set_common_vars],
|
||||
['Checking OS...', check_os],
|
||||
['Checking tools...', check_tools],
|
||||
['Creating folders...', make_tmp_dirs],
|
||||
['Clearing collisions...', clean_env_vars],
|
||||
]
|
||||
try:
|
||||
for f in init_functions:
|
||||
try_and_print(
|
||||
|
|
@ -713,5 +741,14 @@ def set_common_vars():
|
|||
global_vars['TmpDir'] = r'{BinDir}\tmp'.format(
|
||||
**global_vars)
|
||||
|
||||
def set_linux_vars():
|
||||
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']
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Wizard Kit: Functions - Data
|
||||
|
||||
import ctypes
|
||||
import json
|
||||
|
||||
from operator import itemgetter
|
||||
|
||||
|
|
@ -157,38 +158,143 @@ def is_valid_wim_file(item):
|
|||
print_log('WARNING: Image "{}" damaged.'.format(item.name))
|
||||
return valid
|
||||
|
||||
def get_mounted_data():
|
||||
"""Get mounted volumes, returns dict."""
|
||||
cmd = [
|
||||
'findmnt', '-J', '-b', '-i',
|
||||
't', (
|
||||
'autofs,binfmt_misc,cgroup,cgroup2,configfs,debugfs,devpts,devtmpfs,'
|
||||
'hugetlbfs,mqueue,proc,pstore,securityfs,sysfs,tmpfs'
|
||||
),
|
||||
'-o', 'SOURCE,TARGET,FSTYPE,LABEL,SIZE,AVAIL,USED']
|
||||
result = run_program(cmd)
|
||||
json_data = json.loads(result.stdout.decode())
|
||||
mounted_data = []
|
||||
for item in json_data.get('filesystems', []):
|
||||
mounted_data.append(item)
|
||||
mounted_data.extend(item.get('children', []))
|
||||
return {item['source']: item for item in mounted_data}
|
||||
|
||||
def mount_all_volumes():
|
||||
"""Mount all attached devices with recognized filesystems."""
|
||||
report = []
|
||||
|
||||
# Get list of block devices
|
||||
cmd = ['lsblk', '-J', '-o', 'NAME,FSTYPE,LABEL,UUID,PARTTYPE,TYPE,SIZE']
|
||||
result = run_program(cmd)
|
||||
json_data = json.loads(result.stdout.decode())
|
||||
devs = json_data.get('blockdevices', [])
|
||||
|
||||
# Get list of mounted devices
|
||||
mounted_data = get_mounted_data()
|
||||
mounted_list = [m['source'] for m in mounted_data.values()]
|
||||
|
||||
# Loop over devices
|
||||
for dev in devs:
|
||||
dev_path = '/dev/{}'.format(dev['name'])
|
||||
if re.search(r'^(loop|sr)', dev['name'], re.IGNORECASE):
|
||||
# Skip loopback devices and optical media
|
||||
report.append([dev_path, 'Skipped'])
|
||||
continue
|
||||
for child in dev.get('children', []):
|
||||
child_path = '/dev/{}'.format(child['name'])
|
||||
if child_path in mounted_list:
|
||||
report.append([child_path, 'Already Mounted'])
|
||||
else:
|
||||
try:
|
||||
run_program(['udevil', 'mount', '-o', 'ro', child_path])
|
||||
report.append([child_path, 'CS'])
|
||||
except subprocess.CalledProcessError:
|
||||
report.append([child_path, 'NS'])
|
||||
|
||||
# Update list of mounted devices
|
||||
mounted_data = get_mounted_data()
|
||||
mounted_list = [m['source'] for m in mounted_data.values()]
|
||||
|
||||
# Update report lines for show_data()
|
||||
for line in report:
|
||||
_path = line[0]
|
||||
_result = line[1]
|
||||
info = {'message': '{}:'.format(_path)}
|
||||
if _path in mounted_list:
|
||||
info['data'] = 'Mounted on {}'.format(
|
||||
mounted_data[_path]['target'])
|
||||
info['data'] = '{:40} ({} used, {} free)'.format(
|
||||
info['data'],
|
||||
human_readable_size(mounted_data[_path]['used']),
|
||||
human_readable_size(mounted_data[_path]['avail']))
|
||||
if _result == 'Already Mounted':
|
||||
info['warning'] = True
|
||||
elif _result == 'Skipped':
|
||||
info['data'] = 'Skipped'
|
||||
info['warning'] = True
|
||||
else:
|
||||
info['data'] = 'Failed to mount'
|
||||
info['error'] = True
|
||||
line.append(info)
|
||||
return report
|
||||
|
||||
def mount_backup_shares():
|
||||
"""Mount the backup shares unless labeled as already mounted."""
|
||||
if psutil.LINUX:
|
||||
mounted_data = get_mounted_data()
|
||||
for server in BACKUP_SERVERS:
|
||||
# Blindly skip if we mounted earlier
|
||||
if psutil.LINUX:
|
||||
# Update mounted status
|
||||
source = '//{IP}/{Share}'.format(**server)
|
||||
dest = '/Backups/{Name}'.format(**server)
|
||||
mounted_str = '(Already) Mounted {}'.format(dest)
|
||||
data = mounted_data.get(source, {})
|
||||
if dest == data.get('target', ''):
|
||||
server['Mounted'] = True
|
||||
elif psutil.WINDOWS:
|
||||
mounted_str = '(Already) Mounted {Name}'.format(**server)
|
||||
if server['Mounted']:
|
||||
print_warning(mounted_str)
|
||||
continue
|
||||
|
||||
mount_network_share(server)
|
||||
|
||||
def mount_network_share(server):
|
||||
"""Mount a network share defined by server."""
|
||||
if psutil.WINDOWS:
|
||||
cmd = r'net use \\{IP}\{Share} /user:{User} {Pass}'.format(**server)
|
||||
cmd = cmd.split(' ')
|
||||
warning = r'Failed to mount \\{Name}\{Share}, {IP} unreachable.'.format(
|
||||
**server)
|
||||
error = r'Failed to mount \\{Name}\{Share} ({IP})'.format(**server)
|
||||
success = 'Mounted {Name}'.format(**server)
|
||||
elif psutil.LINUX:
|
||||
cmd = [
|
||||
'sudo', 'mkdir', '-p',
|
||||
'/Backups/{Name}'.format(**server)]
|
||||
run_program(cmd)
|
||||
cmd = [
|
||||
'sudo', 'mount',
|
||||
'//{IP}/{Share}'.format(**server),
|
||||
'/Backups/{Name}'.format(**server),
|
||||
'-o', 'username={User},password={Pass}'.format(**server)]
|
||||
warning = 'Failed to mount /Backups/{Name}, {IP} unreachable.'.format(
|
||||
**server)
|
||||
error = 'Failed to mount /Backups/{Name}'.format(**server)
|
||||
success = 'Mounted /Backups/{Name}'.format(**server)
|
||||
|
||||
# Test connection
|
||||
try:
|
||||
ping(server['IP'])
|
||||
except subprocess.CalledProcessError:
|
||||
print_error(
|
||||
r'Failed to mount \\{Name}\{Share}, {IP} unreachable.'.format(
|
||||
**server))
|
||||
print_warning(warning)
|
||||
sleep(1)
|
||||
return False
|
||||
|
||||
# Mount
|
||||
cmd = r'net use \\{IP}\{Share} /user:{User} {Pass}'.format(**server)
|
||||
cmd = cmd.split(' ')
|
||||
try:
|
||||
run_program(cmd)
|
||||
except Exception:
|
||||
print_warning(r'Failed to mount \\{Name}\{Share} ({IP})'.format(
|
||||
**server))
|
||||
print_error(error)
|
||||
sleep(1)
|
||||
else:
|
||||
print_info('Mounted {Name}'.format(**server))
|
||||
print_info(success)
|
||||
server['Mounted'] = True
|
||||
|
||||
def run_fast_copy(items, dest):
|
||||
|
|
|
|||
643
.bin/Scripts/functions/hw_diags.py
Normal file
643
.bin/Scripts/functions/hw_diags.py
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
# Wizard Kit: Functions - HW Diagnostics
|
||||
|
||||
import json
|
||||
|
||||
from functions.common import *
|
||||
|
||||
# STATIC VARIABLES
|
||||
ATTRIBUTES = {
|
||||
'NVMe': {
|
||||
'critical_warning': {'Error': 1},
|
||||
'media_errors': {'Error': 1},
|
||||
'power_on_hours': {'Warning': 12000, 'Error': 18000, 'Ignore': True},
|
||||
'unsafe_shutdowns': {'Warning': 1},
|
||||
},
|
||||
'SMART': {
|
||||
5: {'Error': 1},
|
||||
9: {'Warning': 12000, 'Error': 18000, 'Ignore': True},
|
||||
10: {'Warning': 1},
|
||||
184: {'Error': 1},
|
||||
187: {'Warning': 1},
|
||||
188: {'Warning': 1},
|
||||
197: {'Error': 1},
|
||||
198: {'Error': 1},
|
||||
201: {'Warning': 1},
|
||||
},
|
||||
}
|
||||
TESTS = {
|
||||
'Prime95': {
|
||||
'Enabled': False,
|
||||
'Status': 'Pending',
|
||||
},
|
||||
'NVMe/SMART': {
|
||||
'Enabled': False,
|
||||
'Quick': False,
|
||||
'Status': {},
|
||||
},
|
||||
'badblocks': {
|
||||
'Enabled': False,
|
||||
'Results': {},
|
||||
'Status': {},
|
||||
},
|
||||
}
|
||||
|
||||
def get_smart_details(dev):
|
||||
cmd = 'sudo smartctl --all --json /dev/{}'.format(dev).split()
|
||||
result = run_program(cmd, check=False)
|
||||
try:
|
||||
return json.loads(result.stdout.decode())
|
||||
except Exception:
|
||||
# Let other sections deal with the missing data
|
||||
return {}
|
||||
|
||||
def get_status_color(s):
|
||||
color = COLORS['CLEAR']
|
||||
if s in ['Denied', 'NS', 'OVERRIDE', 'Unknown']:
|
||||
color = COLORS['RED']
|
||||
elif s in ['Aborted', 'Working', 'Skipped']:
|
||||
color = COLORS['YELLOW']
|
||||
elif s in ['CS']:
|
||||
color = COLORS['GREEN']
|
||||
return color
|
||||
|
||||
def menu_diags(*args):
|
||||
diag_modes = [
|
||||
{'Name': 'All tests',
|
||||
'Tests': ['Prime95', 'NVMe/SMART', 'badblocks']},
|
||||
{'Name': 'Prime95',
|
||||
'Tests': ['Prime95']},
|
||||
{'Name': 'NVMe/SMART & badblocks',
|
||||
'Tests': ['NVMe/SMART', 'badblocks']},
|
||||
{'Name': 'NVMe/SMART',
|
||||
'Tests': ['NVMe/SMART']},
|
||||
{'Name': 'badblocks',
|
||||
'Tests': ['badblocks']},
|
||||
{'Name': 'Quick drive test',
|
||||
'Tests': ['Quick', 'NVMe/SMART']},
|
||||
]
|
||||
actions = [
|
||||
{'Letter': 'A', 'Name': 'Audio test'},
|
||||
{'Letter': 'N', 'Name': 'Network test'},
|
||||
{'Letter': 'M', 'Name': 'Screen Saver - Matrix', 'CRLF': True},
|
||||
{'Letter': 'P', 'Name': 'Screen Saver - Pipes'},
|
||||
{'Letter': 'Q', 'Name': 'Quit', 'CRLF': True},
|
||||
]
|
||||
|
||||
# Quick disk check
|
||||
if 'quick' in args:
|
||||
run_tests(['Quick', 'NVMe/SMART'])
|
||||
exit_script()
|
||||
|
||||
# Show menu
|
||||
while True:
|
||||
selection = menu_select(
|
||||
title = 'Hardware Diagnostics: Menu',
|
||||
main_entries = diag_modes,
|
||||
action_entries = actions,
|
||||
spacer = '──────────────────────────')
|
||||
if selection.isnumeric():
|
||||
if diag_modes[int(selection)-1]['Name'] != 'Quick drive test':
|
||||
# Save log for non-quick tests
|
||||
ticket_number = get_ticket_number()
|
||||
global_vars['LogDir'] = '{}/Tickets/{}'.format(
|
||||
global_vars['Env']['HOME'],
|
||||
ticket_number)
|
||||
os.makedirs(global_vars['LogDir'], exist_ok=True)
|
||||
global_vars['LogFile'] = '{}/Hardware Diagnostics.log'.format(
|
||||
global_vars['LogDir'])
|
||||
run_tests(diag_modes[int(selection)-1]['Tests'])
|
||||
elif selection == 'A':
|
||||
run_program(['hw-diags-audio'], check=False, pipe=False)
|
||||
sleep(1)
|
||||
elif selection == 'N':
|
||||
run_program(['hw-diags-network'], check=False, pipe=False)
|
||||
sleep(1)
|
||||
elif selection == 'M':
|
||||
run_program(['cmatrix', '-abs'], check=False, pipe=False)
|
||||
elif selection == 'P':
|
||||
run_program(
|
||||
'pipes -t 0 -t 1 -t 2 -t 3 -p 5 -R -r 4000'.split(),
|
||||
check=False, pipe=False)
|
||||
elif selection == 'Q':
|
||||
break
|
||||
|
||||
def run_badblocks():
|
||||
aborted = False
|
||||
clear_screen()
|
||||
print_log('\nStart badblocks test(s)\n')
|
||||
progress_file = '{}/badblocks_progress.out'.format(global_vars['LogDir'])
|
||||
update_progress()
|
||||
|
||||
# Set Window layout and start test
|
||||
run_program('tmux split-window -dhl 15 watch -c -n1 -t cat {}'.format(
|
||||
TESTS['Progress Out']).split())
|
||||
|
||||
# Show disk details
|
||||
for name, dev in sorted(TESTS['badblocks']['Devices'].items()):
|
||||
show_disk_details(dev)
|
||||
print_standard(' ')
|
||||
update_progress()
|
||||
|
||||
# Run
|
||||
print_standard('Running badblock test(s):')
|
||||
for name, dev in sorted(TESTS['badblocks']['Devices'].items()):
|
||||
cur_status = TESTS['badblocks']['Status'][name]
|
||||
nvme_smart_status = TESTS['NVMe/SMART']['Status'].get(name, None)
|
||||
if cur_status == 'Denied':
|
||||
# Skip denied disks
|
||||
continue
|
||||
if nvme_smart_status == 'NS':
|
||||
TESTS['badblocks']['Status'][name] = 'Skipped'
|
||||
else:
|
||||
# Not testing SMART, SMART CS, or SMART OVERRIDE
|
||||
print_standard(' /dev/{:11} '.format(name+'...'), end='', flush=True)
|
||||
run_program('tmux split-window -dl 10 {} {} {}'.format(
|
||||
'hw-diags-badblocks',
|
||||
'/dev/{}'.format(name),
|
||||
progress_file).split())
|
||||
wait_for_process('badblocks')
|
||||
print_standard('Done', timestamp=False)
|
||||
|
||||
# Check results
|
||||
with open(progress_file, 'r') as f:
|
||||
text = f.read()
|
||||
TESTS['badblocks']['Results'][name] = text
|
||||
r = re.search(r'Pass completed.*0/0/0 errors', text)
|
||||
if r:
|
||||
TESTS['badblocks']['Status'][name] = 'CS'
|
||||
else:
|
||||
TESTS['badblocks']['Status'][name] = 'NS'
|
||||
|
||||
# Remove temp file
|
||||
os.remove(progress_file)
|
||||
update_progress()
|
||||
|
||||
# Done
|
||||
run_program('tmux kill-pane -a'.split(), check=False)
|
||||
pass
|
||||
|
||||
def run_mprime():
|
||||
aborted = False
|
||||
clear_screen()
|
||||
print_log('\nStart Prime95 test')
|
||||
TESTS['Prime95']['Status'] = 'Working'
|
||||
update_progress()
|
||||
|
||||
# Set Window layout and start test
|
||||
run_program('tmux split-window -dl 10 -c {wd} {cmd} {wd}'.format(
|
||||
wd=global_vars['TmpDir'], cmd='hw-diags-prime95').split())
|
||||
run_program('tmux split-window -dhl 15 watch -c -n1 -t cat {}'.format(
|
||||
TESTS['Progress Out']).split())
|
||||
run_program('tmux split-window -bd watch -c -n1 -t hw-sensors'.split())
|
||||
run_program('tmux resize-pane -y 3'.split())
|
||||
|
||||
# Start test
|
||||
run_program(['apple-fans', 'max'])
|
||||
print_standard('Running Prime95 for {} minutes'.format(MPRIME_LIMIT))
|
||||
print_warning('If running too hot, press CTL+c to abort the test')
|
||||
try:
|
||||
sleep(int(MPRIME_LIMIT)*60)
|
||||
except KeyboardInterrupt:
|
||||
# Catch CTL+C
|
||||
aborted = True
|
||||
|
||||
# Save "final" temps
|
||||
run_program(
|
||||
cmd = 'hw-sensors >> "{}/Final Temps.out"'.format(
|
||||
global_vars['LogDir']).split(),
|
||||
check = False,
|
||||
pipe = False,
|
||||
shell = True)
|
||||
run_program(
|
||||
cmd = 'hw-sensors --nocolor >> "{}/Final Temps.log"'.format(
|
||||
global_vars['LogDir']).split(),
|
||||
check = False,
|
||||
pipe = False,
|
||||
shell = True)
|
||||
|
||||
# Stop test
|
||||
run_program('killall -s INT mprime'.split(), check=False)
|
||||
run_program(['apple-fans', 'auto'])
|
||||
|
||||
# Move logs to Ticket folder
|
||||
for item in os.scandir(global_vars['TmpDir']):
|
||||
try:
|
||||
shutil.move(item.path, global_vars['LogDir'])
|
||||
except Exception:
|
||||
print_error('ERROR: Failed to move "{}" to "{}"'.format(
|
||||
item.path,
|
||||
global_vars['LogDir']))
|
||||
|
||||
# Check logs
|
||||
TESTS['Prime95']['NS'] = False
|
||||
TESTS['Prime95']['CS'] = False
|
||||
log = '{}/results.txt'.format(global_vars['LogDir'])
|
||||
if os.path.exists(log):
|
||||
with open(log, 'r') as f:
|
||||
text = f.read()
|
||||
TESTS['Prime95']['results.txt'] = text
|
||||
r = re.search(r'(error|fail)', text)
|
||||
TESTS['Prime95']['NS'] = bool(r)
|
||||
log = '{}/prime.log'.format(global_vars['LogDir'])
|
||||
if os.path.exists(log):
|
||||
with open(log, 'r') as f:
|
||||
text = f.read()
|
||||
TESTS['Prime95']['prime.log'] = text
|
||||
r = re.search(r'completed.*0 errors, 0 warnings', text)
|
||||
TESTS['Prime95']['CS'] = bool(r)
|
||||
|
||||
# Update status
|
||||
if aborted:
|
||||
TESTS['Prime95']['Status'] = 'Aborted'
|
||||
print_warning('\nAborted.')
|
||||
update_progress()
|
||||
if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled']:
|
||||
if not ask('Proceed to next test?'):
|
||||
run_program('tmux kill-pane -a'.split())
|
||||
raise GenericError
|
||||
else:
|
||||
if TESTS['Prime95']['NS']:
|
||||
TESTS['Prime95']['Status'] = 'NS'
|
||||
elif TESTS['Prime95']['CS']:
|
||||
TESTS['Prime95']['Status'] = 'CS'
|
||||
else:
|
||||
TESTS['Prime95']['Status'] = 'Unknown'
|
||||
update_progress()
|
||||
|
||||
# Done
|
||||
run_program('tmux kill-pane -a'.split())
|
||||
|
||||
def run_nvme_smart():
|
||||
aborted = False
|
||||
clear_screen()
|
||||
print_log('\nStart NVMe/SMART test(s)\n')
|
||||
progress_file = '{}/selftest_progress.out'.format(global_vars['LogDir'])
|
||||
update_progress()
|
||||
|
||||
# Set Window layout and start test
|
||||
run_program('tmux split-window -dl 3 watch -c -n1 -t cat {}'.format(
|
||||
progress_file).split())
|
||||
run_program('tmux split-window -dhl 15 watch -c -n1 -t cat {}'.format(
|
||||
TESTS['Progress Out']).split())
|
||||
|
||||
# Show disk details
|
||||
for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()):
|
||||
show_disk_details(dev)
|
||||
print_standard(' ')
|
||||
update_progress()
|
||||
|
||||
# Run
|
||||
for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()):
|
||||
cur_status = TESTS['NVMe/SMART']['Status'][name]
|
||||
if cur_status == 'OVERRIDE':
|
||||
# Skipping test per user request
|
||||
continue
|
||||
if TESTS['NVMe/SMART']['Quick'] or dev.get('NVMe Disk', False):
|
||||
# Skip SMART self-tests for quick checks and NVMe disks
|
||||
if dev['Quick Health OK']:
|
||||
TESTS['NVMe/SMART']['Status'][name] = 'CS'
|
||||
else:
|
||||
TESTS['NVMe/SMART']['Status'][name] = 'NS'
|
||||
elif not dev['Quick Health OK']:
|
||||
# SMART overall == Failed or attributes bad, avoid self-test
|
||||
TESTS['NVMe/SMART']['Status'][name] = 'NS'
|
||||
else:
|
||||
# Start SMART short self-test
|
||||
test_length = dev['smartctl'].get(
|
||||
'ata_smart_data', {}).get(
|
||||
'self_test', {}).get(
|
||||
'polling_minutes', {}).get(
|
||||
'short', 5)
|
||||
test_length = int(test_length) + 5
|
||||
TESTS['NVMe/SMART']['Status'][name] = 'Working'
|
||||
update_progress()
|
||||
print_standard('Running SMART short self-test(s):')
|
||||
print_standard(
|
||||
' /dev/{:8}({} minutes)... '.format(name, test_length),
|
||||
end='', flush=True)
|
||||
run_program(
|
||||
'sudo smartctl -t short /dev/{}'.format(name).split(),
|
||||
check=False)
|
||||
|
||||
# Wait and show progress (in 10 second increments)
|
||||
for iteration in range(int(test_length*60/10)):
|
||||
# Update SMART data
|
||||
dev['smartctl'] = get_smart_details(name)
|
||||
|
||||
# Check if test is complete
|
||||
if iteration >= 6:
|
||||
done = dev['smartctl'].get(
|
||||
'ata_smart_data', {}).get(
|
||||
'self_test', {}).get(
|
||||
'status', {}).get(
|
||||
'passed', False)
|
||||
if done:
|
||||
break
|
||||
|
||||
# Update progress_file
|
||||
with open(progress_file, 'w') as f:
|
||||
f.write('SMART self-test status:\n {}'.format(
|
||||
dev['smartctl'].get(
|
||||
'ata_smart_data', {}).get(
|
||||
'self_test', {}).get(
|
||||
'status', {}).get(
|
||||
'string', 'unknown')))
|
||||
sleep(10)
|
||||
os.remove(progress_file)
|
||||
|
||||
# Check result
|
||||
test_passed = dev['smartctl'].get(
|
||||
'ata_smart_data', {}).get(
|
||||
'self_test', {}).get(
|
||||
'status', {}).get(
|
||||
'passed', False)
|
||||
if test_passed:
|
||||
TESTS['NVMe/SMART']['Status'][name] = 'CS'
|
||||
else:
|
||||
TESTS['NVMe/SMART']['Status'][name] = 'NS'
|
||||
update_progress()
|
||||
print_standard('Done', timestamp=False)
|
||||
|
||||
# Done
|
||||
run_program('tmux kill-pane -a'.split(), check=False)
|
||||
|
||||
def run_tests(tests):
|
||||
print_log('Starting Hardware Diagnostics')
|
||||
print_log('\nRunning tests: {}'.format(', '.join(tests)))
|
||||
# Enable selected tests
|
||||
for t in ['Prime95', 'NVMe/SMART', 'badblocks']:
|
||||
TESTS[t]['Enabled'] = t in tests
|
||||
TESTS['NVMe/SMART']['Quick'] = 'Quick' in tests
|
||||
|
||||
# Initialize
|
||||
if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled']:
|
||||
scan_disks()
|
||||
update_progress()
|
||||
|
||||
# Run
|
||||
mprime_aborted = False
|
||||
if TESTS['Prime95']['Enabled']:
|
||||
try:
|
||||
run_mprime()
|
||||
except GenericError:
|
||||
mprime_aborted = True
|
||||
if not mprime_aborted:
|
||||
if TESTS['NVMe/SMART']['Enabled']:
|
||||
run_nvme_smart()
|
||||
if TESTS['badblocks']['Enabled']:
|
||||
run_badblocks()
|
||||
|
||||
# Show results
|
||||
show_results()
|
||||
|
||||
def scan_disks():
|
||||
clear_screen()
|
||||
|
||||
# Get eligible disk list
|
||||
result = run_program(['lsblk', '-J', '-O'])
|
||||
json_data = json.loads(result.stdout.decode())
|
||||
devs = {}
|
||||
for d in json_data.get('blockdevices', []):
|
||||
if d['type'] == 'disk' and d['hotplug'] == '0':
|
||||
devs[d['name']] = {'lsblk': d}
|
||||
TESTS['NVMe/SMART']['Status'][d['name']] = 'Pending'
|
||||
TESTS['badblocks']['Status'][d['name']] = 'Pending'
|
||||
|
||||
for dev, data in devs.items():
|
||||
# Get SMART attributes
|
||||
run_program(
|
||||
cmd = 'sudo smartctl -s on /dev/{}'.format(dev).split(),
|
||||
check = False)
|
||||
data['smartctl'] = get_smart_details(dev)
|
||||
|
||||
# Get NVMe attributes
|
||||
if data['lsblk']['tran'] == 'nvme':
|
||||
cmd = 'sudo nvme smart-log /dev/{} -o json'.format(dev).split()
|
||||
result = run_program(cmd, check=False)
|
||||
try:
|
||||
data['nvme-cli'] = json.loads(result.stdout.decode())
|
||||
except Exception:
|
||||
# Let other sections deal with the missing data
|
||||
data['nvme-cli'] = {}
|
||||
data['NVMe Disk'] = True
|
||||
|
||||
# Set "Quick Health OK" value
|
||||
## NOTE: If False then require override for badblocks test
|
||||
wanted_smart_list = [
|
||||
'ata_smart_attributes',
|
||||
'ata_smart_data',
|
||||
'smart_status',
|
||||
]
|
||||
if data.get('NVMe Disk', False):
|
||||
crit_warn = data['nvme-cli'].get('critical_warning', 1)
|
||||
data['Quick Health OK'] = True if crit_warn == 0 else False
|
||||
elif set(wanted_smart_list).issubset(data['smartctl'].keys()):
|
||||
data['SMART Pass'] = data['smartctl'].get('smart_status', {}).get(
|
||||
'passed', False)
|
||||
data['Quick Health OK'] = data['SMART Pass']
|
||||
data['SMART Support'] = True
|
||||
else:
|
||||
data['Quick Health OK'] = False
|
||||
data['SMART Support'] = False
|
||||
|
||||
# Ask for manual overrides if necessary
|
||||
if not data['Quick Health OK'] and TESTS['badblocks']['Enabled']:
|
||||
show_disk_details(data)
|
||||
print_warning("WARNING: Health can't be confirmed for: {}".format(
|
||||
'/dev/{}'.format(dev)))
|
||||
dev_name = data['lsblk']['name']
|
||||
print_standard(' ')
|
||||
if ask('Run badblocks for this device anyway?'):
|
||||
TESTS['NVMe/SMART']['Status'][dev_name] = 'OVERRIDE'
|
||||
else:
|
||||
TESTS['NVMe/SMART']['Status'][dev_name] = 'NS'
|
||||
TESTS['badblocks']['Status'][dev_name] = 'Denied'
|
||||
print_standard(' ') # In case there's more than one "OVERRIDE" disk
|
||||
|
||||
TESTS['NVMe/SMART']['Devices'] = devs
|
||||
TESTS['badblocks']['Devices'] = devs
|
||||
|
||||
def show_disk_details(dev):
|
||||
dev_name = dev['lsblk']['name']
|
||||
# Device description
|
||||
print_info('Device: /dev/{}'.format(dev['lsblk']['name']))
|
||||
for key in ['model', 'size', 'serial']:
|
||||
print_standard(' {:8}{}'.format(key, dev['lsblk'].get(key, 'Unknown')))
|
||||
if dev['lsblk'].get('tran', 'Unknown') == 'nvme':
|
||||
print_standard(' {:8}{}'.format('type', 'NVMe'))
|
||||
else:
|
||||
print_standard(' {:8}{}'.format(
|
||||
'type',
|
||||
dev['lsblk'].get('tran', 'Unknown').upper()))
|
||||
|
||||
# Warnings
|
||||
if dev.get('NVMe Disk', False):
|
||||
if dev['Quick Health OK']:
|
||||
print_warning('WARNING: NVMe support is still experimental')
|
||||
else:
|
||||
print_error('ERROR: NVMe disk is reporting critical warnings')
|
||||
elif not dev['SMART Support']:
|
||||
print_error('ERROR: Unable to retrieve SMART data')
|
||||
elif not dev['SMART Pass']:
|
||||
print_error('ERROR: SMART overall-health assessment result: FAILED')
|
||||
|
||||
# Attributes
|
||||
if dev.get('NVMe Disk', False):
|
||||
print_info('Attributes:')
|
||||
for attrib, threshold in sorted(ATTRIBUTES['NVMe'].items()):
|
||||
if attrib in dev['nvme-cli']:
|
||||
print_standard(
|
||||
' {:37}'.format(attrib.replace('_', ' ').title()),
|
||||
end='', flush=True)
|
||||
raw_num = dev['nvme-cli'][attrib]
|
||||
raw_str = str(raw_num)
|
||||
if (threshold.get('Error', False) and
|
||||
raw_num >= threshold.get('Error', -1)):
|
||||
print_error(raw_str, timestamp=False)
|
||||
if not threshold.get('Ignore', False):
|
||||
dev['Quick Health OK'] = False
|
||||
TESTS['NVMe/SMART']['Status'][dev_name] = 'NS'
|
||||
elif (threshold.get('Warning', False) and
|
||||
raw_num >= threshold.get('Warning', -1)):
|
||||
print_warning(raw_str, timestamp=False)
|
||||
else:
|
||||
print_success(raw_str, timestamp=False)
|
||||
elif dev['smartctl'].get('ata_smart_attributes', None):
|
||||
# SMART attributes
|
||||
print_info('Attributes:')
|
||||
s_table = dev['smartctl'].get('ata_smart_attributes', {}).get(
|
||||
'table', {})
|
||||
s_table = {a.get('id', 'Unknown'): a for a in s_table}
|
||||
for attrib, threshold in sorted(ATTRIBUTES['SMART'].items()):
|
||||
if attrib in s_table:
|
||||
print_standard(
|
||||
' {:>3} {:32}'.format(
|
||||
attrib,
|
||||
s_table[attrib]['name']).replace('_', ' ').title(),
|
||||
end='', flush=True)
|
||||
raw_str = s_table[attrib]['raw']['string']
|
||||
raw_num = re.sub(r'^(\d+).*$', r'\1', raw_str)
|
||||
try:
|
||||
raw_num = float(raw_num)
|
||||
except ValueError:
|
||||
# Not sure about this one, print raw_str without color?
|
||||
print_standard(raw_str, timestamp=False)
|
||||
continue
|
||||
if (threshold.get('Error', False) and
|
||||
raw_num >= threshold.get('Error', -1)):
|
||||
print_error(raw_str, timestamp=False)
|
||||
if not threshold.get('Ignore', False):
|
||||
dev['Quick Health OK'] = False
|
||||
TESTS['NVMe/SMART']['Status'][dev_name] = 'NS'
|
||||
elif (threshold.get('Warning', False) and
|
||||
raw_num >= threshold.get('Warning', -1)):
|
||||
print_warning(raw_str, timestamp=False)
|
||||
else:
|
||||
print_success(raw_str, timestamp=False)
|
||||
|
||||
def show_results():
|
||||
clear_screen()
|
||||
print_standard('Hardware Diagnostic Results')
|
||||
update_progress()
|
||||
|
||||
# Set Window layout and show progress
|
||||
run_program('tmux split-window -dhl 15 watch -c -n1 -t cat {}'.format(
|
||||
TESTS['Progress Out']).split())
|
||||
|
||||
# Prime95
|
||||
if TESTS['Prime95']['Enabled']:
|
||||
print_success('\nPrime95:')
|
||||
for log, regex in [
|
||||
['results.txt', r'(error|fail)'],
|
||||
['prime.log', r'completed.*0 errors, 0 warnings']]:
|
||||
if log in TESTS['Prime95']:
|
||||
print_info('Log: {}'.format(log))
|
||||
lines = [line.strip() for line
|
||||
in TESTS['Prime95'][log].splitlines()
|
||||
if re.search(regex, line, re.IGNORECASE)]
|
||||
for line in lines[-4:]:
|
||||
line = re.sub(r'^.*Worker #\d.*Torture Test (.*)', r'\1',
|
||||
line, re.IGNORECASE)
|
||||
if TESTS['Prime95'].get('NS', False):
|
||||
print_error(' {}'.format(line))
|
||||
else:
|
||||
print_standard(' {}'.format(line))
|
||||
print_info('Final temps')
|
||||
print_log(' See Final Temps.log')
|
||||
with open('{}/Final Temps.out'.format(global_vars['LogDir']), 'r') as f:
|
||||
for line in f.readlines():
|
||||
if re.search(r'^\s*$', line.strip()):
|
||||
# Stop after coretemps (which should be first)
|
||||
break
|
||||
print(' {}'.format(line.strip()))
|
||||
print_standard(' ')
|
||||
|
||||
# NVMe/SMART / badblocks
|
||||
if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled']:
|
||||
print_success('Disks:')
|
||||
for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()):
|
||||
show_disk_details(dev)
|
||||
bb_status = TESTS['badblocks']['Status'].get(name, None)
|
||||
if (TESTS['badblocks']['Enabled']
|
||||
and bb_status not in ['Denied', 'OVERRIDE', 'Skipped']):
|
||||
print_info('badblocks:')
|
||||
result = TESTS['badblocks']['Results'].get(name, '')
|
||||
for line in result.splitlines():
|
||||
if re.search(r'Pass completed', line, re.IGNORECASE):
|
||||
line = re.sub(
|
||||
r'Pass completed,?\s+', r'',
|
||||
line.strip(), re.IGNORECASE)
|
||||
if TESTS['badblocks']['Status'][name] == 'CS':
|
||||
print_standard(' {}'.format(line))
|
||||
else:
|
||||
print_error(' {}'.format(line))
|
||||
print_standard(' ')
|
||||
|
||||
# Done
|
||||
pause('Press Enter to return to main menu... ')
|
||||
run_program('tmux kill-pane -a'.split())
|
||||
|
||||
def update_progress():
|
||||
if 'Progress Out' not in TESTS:
|
||||
TESTS['Progress Out'] = '{}/progress.out'.format(global_vars['LogDir'])
|
||||
output = []
|
||||
output.append('{BLUE}HW Diagnostics{CLEAR}'.format(**COLORS))
|
||||
output.append('───────────────')
|
||||
if TESTS['Prime95']['Enabled']:
|
||||
output.append(' ')
|
||||
output.append('{BLUE}Prime95{s_color}{status:>8}{CLEAR}'.format(
|
||||
s_color = get_status_color(TESTS['Prime95']['Status']),
|
||||
status = TESTS['Prime95']['Status'],
|
||||
**COLORS))
|
||||
if TESTS['NVMe/SMART']['Enabled']:
|
||||
output.append(' ')
|
||||
output.append('{BLUE}NVMe / SMART{CLEAR}'.format(**COLORS))
|
||||
if TESTS['NVMe/SMART']['Quick']:
|
||||
output.append('{YELLOW} (Quick Check){CLEAR}'.format(**COLORS))
|
||||
for dev, status in sorted(TESTS['NVMe/SMART']['Status'].items()):
|
||||
output.append('{dev}{s_color}{status:>{pad}}{CLEAR}'.format(
|
||||
dev = dev,
|
||||
pad = 15-len(dev),
|
||||
s_color = get_status_color(status),
|
||||
status = status,
|
||||
**COLORS))
|
||||
if TESTS['badblocks']['Enabled']:
|
||||
output.append(' ')
|
||||
output.append('{BLUE}badblocks{CLEAR}'.format(**COLORS))
|
||||
for dev, status in sorted(TESTS['badblocks']['Status'].items()):
|
||||
output.append('{dev}{s_color}{status:>{pad}}{CLEAR}'.format(
|
||||
dev = dev,
|
||||
pad = 15-len(dev),
|
||||
s_color = get_status_color(status),
|
||||
status = status,
|
||||
**COLORS))
|
||||
|
||||
# Add line-endings
|
||||
output = ['{}\n'.format(line) for line in output]
|
||||
|
||||
with open(TESTS['Progress Out'], 'w') as f:
|
||||
f.writelines(output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
81
.bin/Scripts/functions/network.py
Normal file
81
.bin/Scripts/functions/network.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## Wizard Kit: Functions - Network
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Init
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
sys.path.append(os.getcwd())
|
||||
from functions.common import *
|
||||
|
||||
# REGEX
|
||||
REGEX_VALID_IP = re.compile(
|
||||
r'(10.\d+.\d+.\d+'
|
||||
r'|172.(1[6-9]|2\d|3[0-1])'
|
||||
r'|192.168.\d+.\d+)',
|
||||
re.IGNORECASE)
|
||||
|
||||
def 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
|
||||
|
||||
# LAN
|
||||
if 'en' in net_ifs:
|
||||
# Reload the tg3/broadcom driver (known fix for some Dell systems)
|
||||
try_and_print(message='Reloading drivers...', function=reload_tg3)
|
||||
|
||||
# WiFi
|
||||
if not is_connected() and '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()
|
||||
for dev in devs.values():
|
||||
for family in dev:
|
||||
if REGEX_VALID_IP.search(family.address):
|
||||
# Valid IP found
|
||||
return True
|
||||
# Else
|
||||
return False
|
||||
|
||||
def show_valid_addresses():
|
||||
devs = psutil.net_if_addrs()
|
||||
for dev, families in sorted(devs.items()):
|
||||
for family in families:
|
||||
if REGEX_VALID_IP.search(family.address):
|
||||
# Valid IP found
|
||||
show_data(message=dev, data=family.address)
|
||||
|
||||
def speedtest():
|
||||
result = run_program(['speedtest-cli', '--simple'])
|
||||
output = [line.strip() for line in result.stdout.decode().splitlines()
|
||||
if line.strip()]
|
||||
output = [line.split() for line in output]
|
||||
output = [(a, float(b), c) for a, b, c in output]
|
||||
return ['{:10}{:6.2f} {}'.format(*line) for line in output]
|
||||
|
||||
def reload_tg3():
|
||||
"""Reload tg3 module as a workaround for some Dell systems."""
|
||||
run_program(['sudo', 'modprobe', '-r', 'tg3'])
|
||||
run_program(['sudo', 'modprobe', 'broadcom'])
|
||||
run_program(['sudo', 'modprobe', 'tg3'])
|
||||
sleep(5)
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("This file is not meant to be called directly.")
|
||||
|
||||
43
.bin/Scripts/hw-diags
Executable file
43
.bin/Scripts/hw-diags
Executable file
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## Wizard Kit: HW Diagnostics - Menu Launcher
|
||||
|
||||
SESSION_NAME="hw-diags"
|
||||
WINDOW_NAME="Hardware Diagnostics"
|
||||
MENU="hw-diags-menu"
|
||||
|
||||
function ask() {
|
||||
while :; do
|
||||
read -p "$1 " -r answer
|
||||
if echo "$answer" | egrep -iq '^(y|yes|sure)$'; then
|
||||
return 0
|
||||
elif echo "$answer" | egrep -iq '^(n|no|nope)$'; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
die () {
|
||||
echo "$0:" "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check for running session
|
||||
if tmux list-session | grep -q "$SESSION_NAME"; then
|
||||
echo "WARNING: hw-diags tmux session already exists."
|
||||
echo ""
|
||||
if ask "Kill current session?"; then
|
||||
tmux kill-session -t "$SESSION_NAME" || \
|
||||
die "Failed to kill session: $SESSION_NAME"
|
||||
else
|
||||
echo "Aborted."
|
||||
echo ""
|
||||
echo -n "Press Enter to exit... "
|
||||
read -r
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start session
|
||||
tmux new-session -s "$SESSION_NAME" -n "$WINDOW_NAME" "$MENU" $*
|
||||
|
||||
42
.bin/Scripts/hw-diags-audio
Executable file
42
.bin/Scripts/hw-diags-audio
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## Wizard Kit: HW Diagnostics - Audio
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Init
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
sys.path.append(os.getcwd())
|
||||
from functions.common import *
|
||||
init_global_vars()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
# Prep
|
||||
clear_screen()
|
||||
print_standard('Hardware Diagnostics: Audio\n')
|
||||
|
||||
# Set volume
|
||||
try:
|
||||
run_program('amixer -q set "Master" 80% unmute'.split())
|
||||
run_program('amixer -q set "PCM" 90% unmute'.split())
|
||||
except subprocess.CalledProcessError:
|
||||
print_error('Failed to set volume')
|
||||
|
||||
# Run tests
|
||||
for mode in ['pink', 'wav']:
|
||||
run_program(
|
||||
cmd = 'speaker-test -c 2 -l 1 -t {}'.format(mode).split(),
|
||||
check = False,
|
||||
pipe = False)
|
||||
|
||||
# Done
|
||||
#print_standard('\nDone.')
|
||||
#pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
18
.bin/Scripts/hw-diags-badblocks
Executable file
18
.bin/Scripts/hw-diags-badblocks
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## Wizard Kit: HW Diagnostics - badblocks
|
||||
|
||||
function usage {
|
||||
echo "Usage: $0 device log-file"
|
||||
echo " e.g. $0 /dev/sda /tmp/tmp.XXXXXXX/badblocks.log"
|
||||
}
|
||||
|
||||
# Bail early
|
||||
if [ ! -b "$1" ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run Badblocks
|
||||
sudo badblocks -sv -e 1 "$1" 2>&1 | tee -a "$2"
|
||||
|
||||
30
.bin/Scripts/hw-diags-menu
Executable file
30
.bin/Scripts/hw-diags-menu
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## Wizard Kit: HW Diagnostics - Menu
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Init
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
sys.path.append(os.getcwd())
|
||||
from functions.hw_diags import *
|
||||
init_global_vars()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
# Prep
|
||||
clear_screen()
|
||||
|
||||
# Show menu
|
||||
menu_diags(*sys.argv)
|
||||
|
||||
# Done
|
||||
#print_standard('\nDone.')
|
||||
#pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
46
.bin/Scripts/hw-diags-network
Executable file
46
.bin/Scripts/hw-diags-network
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## Wizard Kit: HW Diagnostics - Network
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Init
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
sys.path.append(os.getcwd())
|
||||
from functions.network import *
|
||||
|
||||
def check_connection():
|
||||
if not is_connected():
|
||||
# Raise to cause NS in try_and_print()
|
||||
raise Exception
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
# Prep
|
||||
clear_screen()
|
||||
print_standard('Hardware Diagnostics: Network\n')
|
||||
|
||||
# Connect
|
||||
print_standard('Initializing...')
|
||||
connect_to_network()
|
||||
|
||||
# Tests
|
||||
try_and_print(
|
||||
message='Network connection:', function=check_connection, cs='OK')
|
||||
show_valid_addresses()
|
||||
try_and_print(message='Internet connection:', function=ping,
|
||||
addr='8.8.8.8', cs='OK')
|
||||
try_and_print(message='DNS Resolution:', function=ping, cs='OK')
|
||||
try_and_print(message='Speedtest:', function=speedtest,
|
||||
print_return=True)
|
||||
|
||||
# Done
|
||||
print_standard('\nDone.')
|
||||
#pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## HW diagnostics - Prime95
|
||||
## Wizard Kit: HW Diagnostics - Prime95
|
||||
|
||||
function usage {
|
||||
echo "Usage: $0 log-dir"
|
||||
|
|
@ -15,3 +15,4 @@ fi
|
|||
|
||||
# Run Prime95
|
||||
mprime -t | grep -iv --line-buffered 'stress.txt' | tee -a "$1/prime.log"
|
||||
|
||||
164
.bin/Scripts/hw-sensors
Executable file
164
.bin/Scripts/hw-sensors
Executable file
|
|
@ -0,0 +1,164 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## Wizard Kit: Sensor monitoring tool
|
||||
|
||||
import itertools
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
# Init
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
sys.path.append(os.getcwd())
|
||||
from functions.common import *
|
||||
from borrowed import sensors
|
||||
|
||||
# STATIC VARIABLES
|
||||
COLORS = {
|
||||
'CLEAR': '\033[0m',
|
||||
'RED': '\033[31m',
|
||||
'GREEN': '\033[32m',
|
||||
'YELLOW': '\033[33m',
|
||||
'ORANGE': '\033[31;1m',
|
||||
'BLUE': '\033[34m'
|
||||
}
|
||||
TEMP_LIMITS = {
|
||||
'GREEN': 60,
|
||||
'YELLOW': 70,
|
||||
'ORANGE': 80,
|
||||
'RED': 90,
|
||||
}
|
||||
|
||||
# REGEX
|
||||
REGEX_COLORS = re.compile(r'\033\[\d+;?1?m')
|
||||
|
||||
def color_temp(temp):
|
||||
try:
|
||||
temp = float(temp)
|
||||
except ValueError:
|
||||
return '{YELLOW}{temp}{CLEAR}'.format(temp=temp, **COLORS)
|
||||
if temp > TEMP_LIMITS['RED']:
|
||||
color = COLORS['RED']
|
||||
elif temp > TEMP_LIMITS['ORANGE']:
|
||||
color = COLORS['ORANGE']
|
||||
elif temp > TEMP_LIMITS['YELLOW']:
|
||||
color = COLORS['YELLOW']
|
||||
elif temp > TEMP_LIMITS['GREEN']:
|
||||
color = COLORS['GREEN']
|
||||
elif temp > 0:
|
||||
color = COLORS['BLUE']
|
||||
else:
|
||||
color = COLORS['CLEAR']
|
||||
return '{color}{prefix}{temp:2.0f}°C{CLEAR}'.format(
|
||||
color = color,
|
||||
prefix = '+' if temp>0 else '-',
|
||||
temp = temp,
|
||||
**COLORS)
|
||||
|
||||
def get_feature_string(chip, feature):
|
||||
sfs = list(sensors.SubFeatureIterator(chip, feature)) # get a list of all subfeatures
|
||||
label = sensors.get_label(chip, feature)
|
||||
skipname = len(feature.name)+1 # skip common prefix
|
||||
data = {}
|
||||
|
||||
if feature.type == sensors.feature.INTRUSION:
|
||||
vals = [sensors.get_value(chip, sf.number) for sf in sfs]
|
||||
# short path for INTRUSION to demonstrate type usage
|
||||
status = "alarm" if int(vals[0]) == 1 else "normal"
|
||||
print_standard(' {:18} {}'.format(label, status))
|
||||
return
|
||||
|
||||
for sf in sfs:
|
||||
name = sf.name[skipname:].decode("utf-8").strip()
|
||||
val = sensors.get_value(chip, sf.number)
|
||||
if 'alarm' in name:
|
||||
# Skip
|
||||
continue
|
||||
if '--nocolor' in sys.argv:
|
||||
try:
|
||||
temp = float(val)
|
||||
except ValueError:
|
||||
data[name] = ' {}°C'.format(val)
|
||||
else:
|
||||
data[name] = '{}{:2.0f}°C'.format(
|
||||
'+' if temp>0 else '-',
|
||||
temp)
|
||||
else:
|
||||
data[name] = color_temp(val)
|
||||
|
||||
main_temp = data.pop('input', None)
|
||||
if main_temp:
|
||||
list_data = []
|
||||
for item in ['max', 'crit']:
|
||||
if item in data:
|
||||
list_data.append('{}: {}'.format(item, data.pop(item)))
|
||||
list_data.extend(
|
||||
['{}: {}'.format(k, v) for k, v in sorted(data.items())])
|
||||
data_str = '{:18} {} ({})'.format(
|
||||
label, main_temp, ', '.join(list_data))
|
||||
else:
|
||||
list_data.extend(sorted(data.items()))
|
||||
list_data = ['{}: {}'.format(item[0], item[1]) for item in list_data]
|
||||
data_str = '{:18} {}'.format(label, ', '.join(list_data))
|
||||
return data_str
|
||||
|
||||
def join_columns(column1, column2, width=55):
|
||||
return '{:<{}}{}'.format(
|
||||
column1,
|
||||
55+len(column1)-len(REGEX_COLORS.sub('', column1)),
|
||||
column2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
# Prep
|
||||
sensors.init()
|
||||
|
||||
# Get sensor data
|
||||
chip_temps = {}
|
||||
for chip in sensors.ChipIterator():
|
||||
chip_name = '{} ({})'.format(
|
||||
sensors.chip_snprintf_name(chip),
|
||||
sensors.get_adapter_name(chip.bus))
|
||||
chip_temps[chip_name] = [chip_name]
|
||||
for feature in sensors.FeatureIterator(chip):
|
||||
chip_temps[chip_name].append(get_feature_string(chip, feature))
|
||||
chip_temps[chip_name].append('')
|
||||
|
||||
# Sort chips
|
||||
sensor_temps = []
|
||||
for chip in [k for k in sorted(chip_temps.keys()) if 'coretemp' in k]:
|
||||
sensor_temps.extend(chip_temps[chip])
|
||||
for chip in sorted(chip_temps.keys()):
|
||||
if 'coretemp' not in chip:
|
||||
sensor_temps.extend(chip_temps[chip])
|
||||
|
||||
# Wrap columns as needed
|
||||
screen_size = shutil.get_terminal_size()
|
||||
rows = screen_size.lines - 1
|
||||
if len(sensor_temps) > rows and screen_size.columns > 55*2:
|
||||
sensor_temps = list(itertools.zip_longest(
|
||||
sensor_temps[:rows], sensor_temps[rows:], fillvalue=''))
|
||||
sensor_temps = [join_columns(a, b) for a, b in sensor_temps]
|
||||
|
||||
# Print data
|
||||
if sensor_temps:
|
||||
for line in sensor_temps:
|
||||
print_standard(line)
|
||||
else:
|
||||
if '--nocolor' in sys.argv:
|
||||
print_standard('WARNING: No sensors found')
|
||||
print_standard('\nPlease monitor temps manually')
|
||||
else:
|
||||
print_warning('WARNING: No sensors found')
|
||||
print_standard('\nPlease monitor temps manually')
|
||||
|
||||
# Done
|
||||
sensors.cleanup()
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
sensors.cleanup()
|
||||
pass
|
||||
except:
|
||||
sensors.cleanup()
|
||||
major_exception()
|
||||
|
||||
38
.bin/Scripts/mount-all-volumes
Executable file
38
.bin/Scripts/mount-all-volumes
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## Wizard Kit: Volume mount tool
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Init
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
sys.path.append(os.getcwd())
|
||||
from functions.data import *
|
||||
init_global_vars()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
# Prep
|
||||
clear_screen()
|
||||
print_standard('{}: Volume mount tool'.format(KIT_NAME_FULL))
|
||||
|
||||
# Mount volumes
|
||||
report = mount_all_volumes()
|
||||
|
||||
# Print report
|
||||
print_info('\nResults')
|
||||
for line in report:
|
||||
show_data(indent=4, width=16, **line[-1])
|
||||
|
||||
# Done
|
||||
print_standard('\nDone.')
|
||||
if 'gui' in sys.argv:
|
||||
pause("Press Enter to exit...")
|
||||
popen_program(['nohup', 'thunar', '/media'])
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
38
.bin/Scripts/mount-backup-shares
Executable file
38
.bin/Scripts/mount-backup-shares
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## Wizard Kit: Backup share mount tool
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Init
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
sys.path.append(os.getcwd())
|
||||
from functions.data import *
|
||||
from functions.network import *
|
||||
init_global_vars()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
# Prep
|
||||
clear_screen()
|
||||
|
||||
# Connect
|
||||
connect_to_network()
|
||||
|
||||
# Mount
|
||||
if is_connected():
|
||||
mount_backup_shares()
|
||||
else:
|
||||
# Couldn't connect
|
||||
print_error('ERROR: No network connectivity.')
|
||||
|
||||
# Done
|
||||
print_standard('\nDone.')
|
||||
#pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
81
.bin/Scripts/msword-search
Executable file
81
.bin/Scripts/msword-search
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
#!/bin/python3
|
||||
#
|
||||
## Wizard Kit: MS Word content search tool
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# STATIC VARIABLES
|
||||
SCANDIR = os.getcwd()
|
||||
USAGE = '''Usage: {script} <search-terms>...
|
||||
e.g. {script} "Book Title" "Keyword" "etc"
|
||||
|
||||
This script will search all doc/docx files below the current directory for
|
||||
the search-terms provided (case-insensitive).'''.format(script=__file__)
|
||||
|
||||
# Init
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
sys.path.append(os.getcwd())
|
||||
from functions.network import *
|
||||
init_global_vars()
|
||||
|
||||
REGEX_DOC_FILES = re.compile(r'\.docx?$', re.IGNORECASE)
|
||||
|
||||
def scan_for_docs(path):
|
||||
for entry in os.scandir(path):
|
||||
if entry.is_dir(follow_symlinks=False):
|
||||
yield from scantree(entry.path)
|
||||
elif entry.is_file and REGEX_DOC_FILES.search(entry.name):
|
||||
yield entry
|
||||
|
||||
def scan_file(file_path, search):
|
||||
match = False
|
||||
try:
|
||||
if entry.name.lower().endswith('.docx'):
|
||||
result = run_program(['unzip', '-p', entry.path])
|
||||
else:
|
||||
# Assuming .doc
|
||||
result = run_program(['antiword', entry.path])
|
||||
out = result.stdout.decode()
|
||||
match = re.search(search, out, re.IGNORECASE)
|
||||
except Exception:
|
||||
# Ignore errors since files may be corrupted
|
||||
pass
|
||||
|
||||
return entry.path if match else None
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
# Prep
|
||||
clear_screen()
|
||||
terms = [re.sub(r'\s+', r'\s*', t) for t in sys.argv[1:]]
|
||||
search = '({})'.format('|'.join(terms))
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
# Print usage
|
||||
print_standard(USAGE)
|
||||
else:
|
||||
matches = []
|
||||
for entry in scan_for_docs(SCANDIR):
|
||||
matches.append(scan_file(entry.path, search))
|
||||
# Strip None values (i.e. non-matching entries)
|
||||
matches = [m for m in matches if m]
|
||||
if matches:
|
||||
print_success('Found {} {}:'.format(
|
||||
len(matches),
|
||||
'Matches' if len(matches) > 1 else 'Match'))
|
||||
for match in matches:
|
||||
print_standard(match)
|
||||
else:
|
||||
print_error('No matches found.')
|
||||
|
||||
# Done
|
||||
print_standard('\nDone.')
|
||||
#pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
major_exception()
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## Remount volume read-write
|
||||
## Wizard Kit: Volume remount tool
|
||||
|
||||
if ! mount | grep -q "$1"; then
|
||||
echo "ERROR: Can't remount $1"
|
||||
|
|
@ -10,6 +10,7 @@ fi
|
|||
|
||||
DEVICE=$(mount | grep "$1" | cut -d' ' -f1)
|
||||
|
||||
# Remount read-write
|
||||
echo "Remounting: $DEVICE"
|
||||
udevil umount $DEVICE
|
||||
if udevil mount $DEVICE; then
|
||||
3
.linux_items/include/live/airootfs/etc/hosts
Normal file
3
.linux_items/include/live/airootfs/etc/hosts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
127.0.0.1 localhost.localdomain localhost
|
||||
::1 localhost.localdomain localhost
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
Welcome to the [32mWK Linux Toolbox[0m
|
||||
Welcome to the [32m______[0m
|
||||
|
||||
Some common commands:
|
||||
[34m%[0m hw-diags
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
# the top and down respectively.
|
||||
# The width can be negative. In this case the actual width is the
|
||||
# screen width minus the width defined in within the geometry option.
|
||||
geometry = "300x5-30+20"
|
||||
geometry = "300x5-220+40"
|
||||
|
||||
# Show how many messages are currently hidden (because of geometry).
|
||||
indicate_hidden = yes
|
||||
|
|
|
|||
|
|
@ -71,10 +71,10 @@ bindsym $mod+r exec "rofi -combi-modi window,drun,run -show combi -modi combi"
|
|||
bindsym $mod+d exec "urxvt -title 'Hardware Diagnostics' -e hw-diags"
|
||||
bindsym $mod+f exec "thunar ~"
|
||||
bindsym $mod+i exec "hardinfo"
|
||||
bindsym $mod+m exec "urxvt -title 'Mount All Volumes' -e mount-all-volumes"
|
||||
bindsym $mod+s exec "urxvt -title 'Hardware Diagnostics' -e hw-diags foh"
|
||||
bindsym $mod+m exec "urxvt -title 'Mount All Volumes' -e mount-all-volumes gui"
|
||||
bindsym $mod+s exec "urxvt -title 'Hardware Diagnostics' -e hw-diags quick"
|
||||
bindsym $mod+t exec "urxvt"
|
||||
bindsym $mod+v exec "urxvt -title 'Hardware Sensors' -e hw-diags-sensors"
|
||||
bindsym $mod+v exec "urxvt -title 'Hardware Sensors' -e watch -c -n1 -t hw-sensors"
|
||||
bindsym $mod+w exec "firefox"
|
||||
|
||||
focus_follows_mouse no
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@
|
|||
</keybind>
|
||||
<keybind key="W-m">
|
||||
<action name="Execute">
|
||||
<command>urxvt -title "Mount all Volumes" -e mount-all-volumes-foh</command>
|
||||
<command>urxvt -title "Mount all Volumes" -e mount-all-volumes gui</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-r">
|
||||
|
|
@ -324,7 +324,7 @@
|
|||
</keybind>
|
||||
<keybind key="W-s">
|
||||
<action name="Execute">
|
||||
<command>urxvt -title "Hardware Diagnostics" -e hw-diags foh</command>
|
||||
<command>urxvt -title "Hardware Diagnostics" -e hw-diags quick</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-t">
|
||||
|
|
@ -334,7 +334,7 @@
|
|||
</keybind>
|
||||
<keybind key="W-v">
|
||||
<action name="Execute">
|
||||
<command>urxvt -title "Hardware Sensors" -e hw-diags-sensors</command>
|
||||
<command>urxvt -title "Hardware Sensors" -e watch -c -n1 -t hw-sensors</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-w">
|
||||
|
|
|
|||
|
|
@ -1,2 +1,7 @@
|
|||
set -g status off
|
||||
set -g pane-active-border-fg white
|
||||
|
||||
# Window names
|
||||
set -g set-titles on
|
||||
set -g set-titles-string '#W'
|
||||
setw -g automatic-rename
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
IF_LIST=($(ip l | egrep '^[0-9]+:\s+(eth|en|wl)' | sed -r 's/^[0-9]+:\s+(\w+):.*/\1/' | sort))
|
||||
|
||||
|
||||
# Add interfaces to conkyrc
|
||||
for i in "${IF_LIST[@]}"; do
|
||||
if [[ "${i:0:1}" == "e" ]]; then
|
||||
sed -i -r "s/#Network/Wired:\${alignr}\${addr $i}\n#Network/" ~/.conkyrc
|
||||
|
|
@ -10,3 +10,7 @@ for i in "${IF_LIST[@]}"; do
|
|||
sed -i -r "s/#Network/Wireless:\${alignr}\${addr $i}\n#Network/" ~/.conkyrc
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove '#Network' line to prevent duplicating lines if this script is re-run
|
||||
sed -i -r "s/#Network//" ~/.conkyrc
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ nm-applet &
|
|||
cbatticon &
|
||||
pasystray &
|
||||
connect-to-network &
|
||||
(sleep 5s && killall dunst) &
|
||||
$HOME/.urxvt_default_res &
|
||||
$HOME/.update_wallpaper &
|
||||
$HOME/.update_conky &
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## Get connected to a network
|
||||
|
||||
# 1. Checks if already online; skips if so
|
||||
# 2. If no wired devices are present then reload kernel modules
|
||||
# 3. If wireless devices are present, and we're still offline, then connect to WiFi
|
||||
|
||||
die () {
|
||||
echo "$0:" "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
function test_connection() {
|
||||
# Check for a valid private IP
|
||||
if ip a | grep -Eq '10.[0-9]+.[0-9]+.[0-9]+'; then
|
||||
return 0 # Class A
|
||||
elif ip a | grep -Eq '172.(1[6-9]|2[0-9]|3[0-1]).[0-9]+.[0-9]+'; then
|
||||
return 0 # Class B
|
||||
elif ip a | grep -Eq '192.168.[0-9]+.[0-9]+'; then
|
||||
return 0 # Class C
|
||||
else
|
||||
return 1 # Invalid private IP
|
||||
fi
|
||||
}
|
||||
|
||||
# Load settings
|
||||
if [[ -f "/run/archiso/bootmnt/arch/wifi.conf" ]]; then
|
||||
source "/run/archiso/bootmnt/arch/wifi.conf" || \
|
||||
die "ERROR: failed to load wifi.conf (from /run/archiso/bootmnt/arch/)"
|
||||
else
|
||||
source "/usr/local/bin/wifi.conf" || \
|
||||
die "ERROR: failed to load wifi.conf (from /usr/local/bin/)"
|
||||
fi
|
||||
|
||||
# Init
|
||||
WIFI_SSID="${WIFI_SSID}"
|
||||
WIFI_PASSWORD="${WIFI_PASSWORD}"
|
||||
|
||||
# Connect to network
|
||||
if ! test_connection; then
|
||||
# LAN
|
||||
if ! ip l | grep -Eq '[0-9]+: +en'; then
|
||||
## Reload the tg3/broadcom driver (known fix for some Dell systems)
|
||||
echo "No wired network adapters found; reloading drivers..."
|
||||
sudo modprobe -r tg3
|
||||
sudo modprobe broadcom
|
||||
sudo modprobe tg3
|
||||
sleep 5s
|
||||
fi
|
||||
|
||||
# WiFi
|
||||
if ip l | grep -Eq '[0-9]+: +wl'; then
|
||||
## Skip if we're already connected (i.e. the code above worked)
|
||||
if ! test_connection; then
|
||||
echo "Attempting to connect to ${WIFI_SSID}..."
|
||||
nmcli dev wifi connect "${WIFI_SSID}" password "${WIFI_PASSWORD}"
|
||||
sleep 5s
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Done
|
||||
if test_connection; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WK HW diagnostics - Launcher
|
||||
|
||||
MODE="$1"
|
||||
SHOW_MENU="True"
|
||||
if [[ "$MODE" =~ ^(all|cpu|drives|foh|smart|badblocks)$ ]]; then
|
||||
SHOW_MENU="False"
|
||||
fi
|
||||
|
||||
SHOW_POWEROPTIONS="False"
|
||||
if [[ "$MODE" =~ ^cli$ ]]; then
|
||||
SHOW_POWEROPTIONS="True"
|
||||
fi
|
||||
|
||||
function pause() {
|
||||
# Pause to review output
|
||||
echo ""
|
||||
echo "$1"
|
||||
read
|
||||
}
|
||||
|
||||
function menu() {
|
||||
while :; do
|
||||
if [[ "$SHOW_MENU" == "False" ]]; then
|
||||
break
|
||||
else
|
||||
clear
|
||||
echo "Hardware Diagnostics"
|
||||
echo "────────────────────"
|
||||
echo "0: Quick drive test"
|
||||
echo "1: All tests"
|
||||
echo "2: Prime95"
|
||||
echo "3: SMART & badblocks"
|
||||
echo "4: SMART"
|
||||
echo "5: badblocks"
|
||||
echo "────────────────────"
|
||||
#if [[ -n $DISPLAY ]] && [[ $(getconf LONG_BIT) -eq "64" ]]; then
|
||||
# echo "6: Graphics Test - FurMark"
|
||||
# echo "7: Graphics Test - Piano"
|
||||
# echo "8: Graphics Test - Volplosion"
|
||||
#fi
|
||||
echo "A: Speaker Test"
|
||||
if [[ -n $DISPLAY ]]; then
|
||||
echo "K: Keyboard Test"
|
||||
fi
|
||||
echo "N: Network Test"
|
||||
echo "────────────────────"
|
||||
echo "M: Screen Saver - Matrix"
|
||||
echo "P: Screen Saver - Pipes"
|
||||
echo "────────────────────"
|
||||
echo "Q: Quit"
|
||||
if [[ "$SHOW_POWEROPTIONS" =~ ^True$ ]]; then
|
||||
echo "R: Reboot"
|
||||
echo "S: Shutdown"
|
||||
fi
|
||||
echo ""
|
||||
read -r -p "Please make a selection: " MODE
|
||||
|
||||
# Check input
|
||||
case $MODE in
|
||||
0)
|
||||
MODE=foh
|
||||
break;;
|
||||
1)
|
||||
MODE=all
|
||||
break;;
|
||||
2)
|
||||
MODE=cpu
|
||||
break;;
|
||||
3)
|
||||
MODE=drives
|
||||
break;;
|
||||
4)
|
||||
MODE=smart
|
||||
break;;
|
||||
5)
|
||||
MODE=badblocks
|
||||
break;;
|
||||
#6)
|
||||
# if [[ -n $DISPLAY ]] && [[ $(getconf LONG_BIT) -eq "64" ]]; then
|
||||
# gputest /fullscreen /test=fur
|
||||
# fi
|
||||
# ;;
|
||||
#7)
|
||||
# if [[ -n $DISPLAY ]] && [[ $(getconf LONG_BIT) -eq "64" ]]; then
|
||||
# gputest /fullscreen /test=pixmark_piano
|
||||
# fi
|
||||
# ;;
|
||||
#8)
|
||||
# if [[ -n $DISPLAY ]] && [[ $(getconf LONG_BIT) -eq "64" ]]; then
|
||||
# gputest /fullscreen /test=pixmark_volplosion
|
||||
# fi
|
||||
# ;;
|
||||
a|A|audio|Audio)
|
||||
clear
|
||||
hw-diags-audio
|
||||
pause "Press Enter to return to menu...";;
|
||||
k|K|keyboard|Keyboard)
|
||||
if [[ -n $DISPLAY ]]; then
|
||||
xev
|
||||
fi
|
||||
;;
|
||||
m|M)
|
||||
cmatrix -abs
|
||||
reset
|
||||
clear;;
|
||||
n|N)
|
||||
clear
|
||||
hw-diags-network
|
||||
pause "Press Enter to return to menu...";;
|
||||
p|P)
|
||||
pipes -t 0 -t 1 -t 2 -t 3 -p 5 -R -r 4000
|
||||
reset
|
||||
clear;;
|
||||
q|Q|quit|Quit)
|
||||
exit 0;;
|
||||
r|R)
|
||||
sudo reboot;;
|
||||
s|S)
|
||||
sudo poweroff;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Main Loop
|
||||
while :; do
|
||||
menu
|
||||
tmux new-session -s 'hw-session' -n 'hw-window' "hw-diags-inner $MODE"
|
||||
if [[ "$SHOW_MENU" == "False" ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WK HW diagnostics - Audio (Stereo)
|
||||
|
||||
# Unmute and set volume
|
||||
amixer -q set "Master" 80% unmute
|
||||
amixer -q set "PCM" 90% unmute
|
||||
|
||||
speaker-test -c 2 -l 1 -t pink
|
||||
# speaker-test -c 2 -l 1 -t sine
|
||||
speaker-test -c 2 -l 1 -t wav
|
||||
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WK HW diagnostics - badblocks
|
||||
|
||||
function usage {
|
||||
echo "Usage: $0 log-dir device"
|
||||
echo " e.g. $0 /tmp/tmp.7Mh5f1RhSL9001 /dev/sda"
|
||||
}
|
||||
|
||||
# Bail early
|
||||
if [ ! -d "$1" ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -b "$2" ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run Badblocks
|
||||
sudo badblocks -sv -e 1 "$2"
|
||||
tmux capture-pane
|
||||
tmux save-buffer "$1/bb_tmp.out"
|
||||
grep -Ev '^$' "$1/bb_tmp.out" > "$1/${2##*/}_badblocks.log"
|
||||
rm "$1/bb_tmp.out"
|
||||
|
|
@ -1,518 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WK HW Diagnostics - Main script
|
||||
|
||||
die () {
|
||||
echo "$0:" "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Load settings
|
||||
if [[ -f "/run/archiso/bootmnt/arch/arch.conf" ]]; then
|
||||
source "/run/archiso/bootmnt/arch/arch.conf" || \
|
||||
die "ERROR: failed to load arch.conf (from /run/archiso/bootmnt/arch/)"
|
||||
else
|
||||
source "/usr/local/bin/arch.conf" || \
|
||||
die "ERROR: failed to load arch.conf (from /usr/local/bin/)"
|
||||
fi
|
||||
|
||||
# Get TICKET
|
||||
## Inital SKIP_UPLOAD value loaded from arch.conf
|
||||
SKIP_UPLOAD="${SKIP_UPLOAD}"
|
||||
TICKET=""
|
||||
while [[ "$TICKET" == "" ]]; do
|
||||
if [[ "$1" == "foh" ]]; then
|
||||
TICKET="foh-consult"
|
||||
SKIP_UPLOAD="True"
|
||||
else
|
||||
echo -n "Please enter the Service Order #: "
|
||||
read -r _ticket
|
||||
if echo "$_ticket" | grep -Eq '^[1-9]+\S*$'; then
|
||||
TICKET="$_ticket"
|
||||
elif echo "$_ticket" | grep -Eq '^0'; then
|
||||
SKIP_UPLOAD="True"
|
||||
TICKET="$_ticket"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Init
|
||||
## Tautologies left to show which settings are coming from arch.conf
|
||||
DIAG_DATE="$(date "+%F_%H%M")"
|
||||
DIAG_SERVER_AVAIL="False"
|
||||
DIAG_SERVER="${DIAG_SERVER}"
|
||||
DIAG_SHARE="${DIAG_SHARE}"
|
||||
DIAG_DEST="${DIAG_SHARE}/${TICKET}"
|
||||
DIAG_UPLOAD_NAME="HW-Diagnostics_${DIAG_DATE}"
|
||||
DIAG_USER="${DIAG_USER}"
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
ERRORS="False"
|
||||
TEST_CPU="False"
|
||||
TEST_CPU_LENGTH="${TEST_CPU_LENGTH}"
|
||||
TEST_OVER="False"
|
||||
TEST_SMART="False"
|
||||
TEST_BADBLOCKS="False"
|
||||
SKIP_SHORT_TEST="False"
|
||||
LOG="$TMP_DIR/hw-diags.log"
|
||||
OUT="$TMP_DIR/hw-diags.out"
|
||||
|
||||
# Get list of drives to test (excluding any ARCH drives)
|
||||
|
||||
## Some code borrowed from stackoverflow.com/a/10020397
|
||||
ARCH_DRIVES=($(ls -l /dev/disk/by-label | grep -iE 'ARCH.*[hs]d[a-z]' | sed -r 's#.*/([hs]d[a-z])[0-9]+#\1#' | sort | uniq))
|
||||
DRIVES=($(inxi -Dxx -c 0 | grep -E "ID-[0-9]+" | sed -r 's#.*/dev/([hsv]d[a-z]|nvme[0-9]n[0-9]).*#\1#' | sort))
|
||||
for d in "${ARCH_DRIVES[@]}"; do
|
||||
DRIVES=(${DRIVES[@]//*$d*})
|
||||
done
|
||||
|
||||
# Handle testing runs
|
||||
if [[ "$SKIP_UPLOAD" != "True" ]]; then
|
||||
# Connect to network
|
||||
connect-to-network
|
||||
|
||||
# Test connection to DIAG_SERVER
|
||||
if ip a | grep -Eq '(10.[0-9]+|172.(1[6-9]|2[0-9]|3[0-1])|192.168).[0-9]+.[0-9]+' && \
|
||||
ping -c 1 -q $DIAG_SERVER >/dev/null 2>&1; then
|
||||
DIAG_SERVER_AVAIL="True"
|
||||
ssh-add
|
||||
ssh $DIAG_USER@$DIAG_SERVER mkdir -p "$DIAG_DEST"
|
||||
ssh $DIAG_USER@$DIAG_SERVER chmod 755 "$DIAG_DEST"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Setup Env
|
||||
mkdir "$TMP_DIR" -p 2>/dev/null
|
||||
pushd "$TMP_DIR" >/dev/null
|
||||
touch "$OUT"
|
||||
rm local.txt results.txt 2>/dev/null
|
||||
|
||||
# Functions
|
||||
CLEAR="\e[0m"
|
||||
RED="\e[31m"
|
||||
GREEN="\e[32m"
|
||||
YELLOW="\e[33m"
|
||||
BLUE="\e[34m"
|
||||
function update_progress {
|
||||
echo "HW Diagnostics" > "$LOG"
|
||||
echo "${BLUE}HW Diagnostics${CLEAR}" > "$OUT"
|
||||
echo "───────────────" >> "$LOG"
|
||||
echo "───────────────" >> "$OUT"
|
||||
|
||||
if [[ "$TEST_CPU" == "True" ]]; then
|
||||
echo "" >> "$LOG"
|
||||
echo "" >> "$OUT"
|
||||
if [[ "$cpu_result" == "CS" ]]; then
|
||||
echo "Prime95 CS" >> "$LOG"
|
||||
echo "${BLUE}Prime95${CLEAR} ${GREEN}CS${CLEAR}" >> "$OUT"
|
||||
elif [[ "$cpu_result" == "Working" ]]; then
|
||||
echo "Prime95 Working" >> "$LOG"
|
||||
echo "${BLUE}Prime95${CLEAR} ${YELLOW}Working${CLEAR}" >> "$OUT"
|
||||
elif [[ "$cpu_result" == "Unknown" ]]; then
|
||||
echo "Prime95 Unknown" >> "$LOG"
|
||||
echo "${BLUE}Prime95${CLEAR} ${YELLOW}Unknown${CLEAR}" >> "$OUT"
|
||||
else
|
||||
echo "Prime95 NS" >> "$LOG"
|
||||
echo "${BLUE}Prime95${CLEAR} ${RED}NS${CLEAR}" >> "$OUT"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$TEST_SMART" == "True" ]]; then
|
||||
echo "" >> "$LOG"
|
||||
echo "" >> "$OUT"
|
||||
if [[ "$SKIP_SHORT_TEST" == "True" ]]; then
|
||||
echo "SMART (Quick)" >> "$LOG"
|
||||
echo "${BLUE}SMART${CLEAR} ${YELLOW}(Quick)${CLEAR}" >> "$OUT"
|
||||
else
|
||||
echo "SMART" >> "$LOG"
|
||||
echo "${BLUE}SMART${CLEAR}" >> "$OUT"
|
||||
fi
|
||||
for d in "${DRIVES[@]}"; do
|
||||
d_tmp="${d##*/}_smart_result"
|
||||
eval "d_tmp=\$$d_tmp"
|
||||
if [[ "$d_tmp" == "CS" ]]; then
|
||||
echo "${d##*/} CS" >> "$LOG"
|
||||
echo "${d##*/} ${GREEN}CS${CLEAR}" >> "$OUT"
|
||||
elif [[ "$d_tmp" == "Working" ]]; then
|
||||
echo "${d##*/} Working" >> "$LOG"
|
||||
echo "${d##*/} ${YELLOW}Working${CLEAR}" >> "$OUT"
|
||||
elif [[ "$d_tmp" == "Unknown" ]]; then
|
||||
echo "${d##*/} Unknown" >> "$LOG"
|
||||
echo "${d##*/} ${YELLOW}Unknown${CLEAR}" >> "$OUT"
|
||||
elif [[ "$d_tmp" == "NS" ]]; then
|
||||
echo "${d##*/} NS" >> "$LOG"
|
||||
echo "${d##*/} ${RED}NS${CLEAR}" >> "$OUT"
|
||||
else
|
||||
echo "${d##*/}" >> "$LOG"
|
||||
echo "${d##*/}" >> "$OUT"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "$TEST_BADBLOCKS" == "True" ]]; then
|
||||
echo "" >> "$LOG"
|
||||
echo "" >> "$OUT"
|
||||
echo "Badblocks" >> "$LOG"
|
||||
echo "${BLUE}Badblocks${CLEAR}" >> "$OUT"
|
||||
for d in "${DRIVES[@]}"; do
|
||||
d_tmp="${d##*/}_badblocks_result"
|
||||
eval "d_tmp=\$$d_tmp"
|
||||
if [[ "$d_tmp" == "CS" ]]; then
|
||||
echo "${d##*/} CS" >> "$LOG"
|
||||
echo "${d##*/} ${GREEN}CS${CLEAR}" >> "$OUT"
|
||||
elif [[ "$d_tmp" == "Working" ]]; then
|
||||
echo "${d##*/} Working" >> "$LOG"
|
||||
echo "${d##*/} ${YELLOW}Working${CLEAR}" >> "$OUT"
|
||||
elif [[ "$d_tmp" == "Skipped" ]]; then
|
||||
echo "${d##*/} Skipped" >> "$LOG"
|
||||
echo "${d##*/} ${RED}Skipped${CLEAR}" >> "$OUT"
|
||||
elif [[ "$d_tmp" == "NS" ]]; then
|
||||
echo "${d##*/} NS" >> "$LOG"
|
||||
echo "${d##*/} ${RED}NS${CLEAR}" >> "$OUT"
|
||||
else
|
||||
echo "${d##*/}" >> "$LOG"
|
||||
echo "${d##*/}" >> "$OUT"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "$TEST_OVER" == "True" ]]; then
|
||||
echo "" >> "$LOG"
|
||||
echo "" >> "$OUT"
|
||||
echo "───────────────" >> "$LOG"
|
||||
echo "───────────────" >> "$OUT"
|
||||
|
||||
if [[ "$ERRORS" == "True" ]]; then
|
||||
echo "HW: Error(s)" >> "$LOG"
|
||||
echo "${RED}HW: Error(s)${CLEAR}" >> "$OUT"
|
||||
else
|
||||
echo "HW: Passed" >> "$LOG"
|
||||
echo "${GREEN}HW: Passed${CLEAR}" >> "$OUT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Update Server
|
||||
if [[ "$DIAG_SERVER_AVAIL" == "True" ]]; then
|
||||
rsync -aqz --chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r "$TMP_DIR/" $DIAG_USER@$DIAG_SERVER:"$DIAG_DEST/${DIAG_UPLOAD_NAME}/" --exclude '*.out'
|
||||
fi
|
||||
}
|
||||
|
||||
# Select Tests
|
||||
case "$1" in
|
||||
all)
|
||||
TEST_CPU="True"
|
||||
TEST_SMART="True"
|
||||
TEST_BADBLOCKS="True"
|
||||
;;
|
||||
cpu)
|
||||
TEST_CPU="True"
|
||||
TEST_SMART="False"
|
||||
TEST_BADBLOCKS="False"
|
||||
;;
|
||||
drives)
|
||||
TEST_CPU="False"
|
||||
TEST_SMART="True"
|
||||
TEST_BADBLOCKS="True"
|
||||
;;
|
||||
foh)
|
||||
TEST_CPU="False"
|
||||
TEST_SMART="True"
|
||||
TEST_BADBLOCKS="False"
|
||||
SKIP_SHORT_TEST="True"
|
||||
;;
|
||||
smart)
|
||||
TEST_CPU="False"
|
||||
TEST_SMART="True"
|
||||
TEST_BADBLOCKS="False"
|
||||
;;
|
||||
badblocks)
|
||||
TEST_CPU="False"
|
||||
TEST_SMART="False"
|
||||
TEST_BADBLOCKS="True"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$TEST_CPU" == "False" ]] && \
|
||||
[[ "$TEST_SMART" == "False" ]] && \
|
||||
[[ "$TEST_BADBLOCKS" == "False" ]]; then
|
||||
echo -e "${YELLOW}Aborting HW diagnostics${CLEAR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Configure display
|
||||
tmux split-window -d -h -l 16 "hw-diags-progress $OUT"
|
||||
update_progress
|
||||
|
||||
# CPU
|
||||
if [[ "$TEST_CPU" == "True" ]]; then
|
||||
clear
|
||||
CPU_ERRORS="False"
|
||||
cpu_result="Working"
|
||||
update_progress
|
||||
apple_fans max
|
||||
(sleep ${TEST_CPU_LENGTH}m && killall -s INT "mprime" >>/dev/null 2>&1) &
|
||||
(sleep ${TEST_CPU_LENGTH}m && killall "hw-diags-sensors" >>/dev/null 2>&1) &
|
||||
(sleep ${TEST_CPU_LENGTH}m && apple_fans auto >>/dev/null 2>&1) &
|
||||
tmux split-window -d -v -l 10 "hw-diags-prime95 $TMP_DIR"
|
||||
hw-diags-sensors "$TMP_DIR" 2>/dev/null
|
||||
sleep 1s
|
||||
# tmux kill-pane -t 1
|
||||
if [[ -f "results.txt" ]]; then
|
||||
mv -nv results.txt "prime-results.txt"
|
||||
if grep -q -iE '(error|fail)' "prime-results.txt"; then
|
||||
cpu_result="NS"
|
||||
CPU_ERRORS="True"
|
||||
else
|
||||
cpu_result="CS"
|
||||
fi
|
||||
elif [[ -f "prime.log" ]]; then
|
||||
if grep -i 'completed' "prime.log" | grep -q -iv '0 errors, 0 warnings'; then
|
||||
cpu_result="NS"
|
||||
CPU_ERRORS="True"
|
||||
else
|
||||
cpu_result="CS"
|
||||
fi
|
||||
else
|
||||
CPU_ERRORS="True"
|
||||
cpu_result="Unknown"
|
||||
fi
|
||||
|
||||
update_progress
|
||||
|
||||
if [[ "$CPU_ERRORS" == "True" ]]; then
|
||||
ERRORS="True"
|
||||
fi
|
||||
fi
|
||||
|
||||
# SMART
|
||||
if [[ "$TEST_SMART" == "True" ]]; then
|
||||
clear
|
||||
echo "Checking SMART status..."
|
||||
for d in "${DRIVES[@]}"; do
|
||||
SMART_ERRORS="False"
|
||||
tmp_device="${d##*/}"
|
||||
eval "${tmp_device}_smart_result=Working"
|
||||
inxi -Dxx | grep "/dev/${tmp_device}" | sed -r "s#.*/dev/${tmp_device} (.*)# \1#" > "${tmp_device}_report.out"
|
||||
inxi -Dxxc 0 | grep "/dev/${tmp_device}" | sed -r "s#.*/dev/${tmp_device} (.*)# \1#" > "${tmp_device}_report.log"
|
||||
update_progress
|
||||
|
||||
# Attempt to enable SMART reporting
|
||||
if sudo smartctl -s on "/dev/${tmp_device}" | grep -q 'device lacks SMART capability'; then
|
||||
SMART_ERRORS="True"
|
||||
eval "${tmp_device}_smart_result=Unknown"
|
||||
echo " ${RED}ERROR: device lacks SMART capability${CLEAR}" >> "${tmp_device}_report.out"
|
||||
echo " ERROR: device lacks SMART capability" >> "${tmp_device}_report.log"
|
||||
sleep 1s
|
||||
fi
|
||||
|
||||
# Save current SMART values
|
||||
sudo smartctl --all "/dev/${tmp_device}" >> "${tmp_device}-smart.log"
|
||||
sudo smartctl -l error "/dev/${tmp_device}" >> "${tmp_device}-smart-err.log"
|
||||
|
||||
# Check specific SMART results
|
||||
sudo smartctl -A "/dev/${tmp_device}" | grep -E '^\s*(5|9|184|197|198)\s' >> "${tmp_device}-smart-attributes.log"
|
||||
|
||||
# 5 - Reallocated Sectors
|
||||
if grep -qE '^\s*5\s' "${tmp_device}-smart-attributes.log"; then
|
||||
line="$(grep -E '^\s*5\s' "${tmp_device}-smart-attributes.log")"
|
||||
value=$(echo "$line" | sed -r 's/.*\s([0-9]+).*/\1/')
|
||||
echo " Reallocated Sectors: $value" >> "${tmp_device}_report.log"
|
||||
if [[ "$value" -gt 0 ]]; then
|
||||
SMART_ERRORS="True"
|
||||
echo " ${RED}Reallocated Sectors: $value${CLEAR}" >> "${tmp_device}_report.out"
|
||||
else
|
||||
echo " ${GREEN}Reallocated Sectors: $value${CLEAR}" >> "${tmp_device}_report.out"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 9 - Power-on Hours (Warn, but don't prevent badblock scan)
|
||||
if grep -qE '^\s*9\s' "${tmp_device}-smart-attributes.log"; then
|
||||
line="$(grep -E '^\s*9\s' "${tmp_device}-smart-attributes.log")"
|
||||
value=$(echo "$line" | sed -r 's/.*\s([0-9]+).*/\1/')
|
||||
echo " Power-on Hours: $value" >> "${tmp_device}_report.log"
|
||||
if [[ "$value" -gt 18000 ]]; then
|
||||
#SMART_ERRORS="True"
|
||||
echo " ${RED}Power-on Hours: $value (VERY OLD)${CLEAR}" >> "${tmp_device}_report.out"
|
||||
elif [[ "$value" -gt 12000 ]]; then
|
||||
echo " ${YELLOW}Power-on Hours: $value${CLEAR}" >> "${tmp_device}_report.out"
|
||||
else
|
||||
echo " ${GREEN}Power-on Hours: $value${CLEAR}" >> "${tmp_device}_report.out"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 184 - End-to-End Errors
|
||||
if grep -qE '^\s*184\s' "${tmp_device}-smart-attributes.log"; then
|
||||
line="$(grep -E '^\s*184\s' "${tmp_device}-smart-attributes.log")"
|
||||
value=$(echo "$line" | sed -r 's/.*\s([0-9]+).*/\1/')
|
||||
echo " End-to-End Errors: $value" >> "${tmp_device}_report.log"
|
||||
if [[ "$value" -gt 0 ]]; then
|
||||
SMART_ERRORS="True"
|
||||
echo " ${RED}End-to-End Errors: $value${CLEAR}" >> "${tmp_device}_report.out"
|
||||
else
|
||||
echo " ${GREEN}End-to-End Errors: $value${CLEAR}" >> "${tmp_device}_report.out"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 197 - Current Pending Sectors
|
||||
if grep -qE '^\s*197\s' "${tmp_device}-smart-attributes.log"; then
|
||||
line="$(grep -E '^\s*197\s' "${tmp_device}-smart-attributes.log")"
|
||||
value=$(echo "$line" | sed -r 's/.*\s([0-9]+).*/\1/')
|
||||
echo " Current Pending Sectors: $value" >> "${tmp_device}_report.log"
|
||||
if [[ "$value" -gt 0 ]]; then
|
||||
SMART_ERRORS="True"
|
||||
echo " ${RED}Current Pending Sectors: $value${CLEAR}" >> "${tmp_device}_report.out"
|
||||
else
|
||||
echo " ${GREEN}Current Pending Sectors: $value${CLEAR}" >> "${tmp_device}_report.out"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 198 - Offline Uncorrectable
|
||||
if grep -qE '^\s*198\s' "${tmp_device}-smart-attributes.log"; then
|
||||
line="$(grep -E '^\s*198\s' "${tmp_device}-smart-attributes.log")"
|
||||
value=$(echo "$line" | sed -r 's/.*\s([0-9]+).*/\1/')
|
||||
echo " Offline Uncorrectable: $value" >> "${tmp_device}_report.log"
|
||||
if [[ "$value" -gt 0 ]]; then
|
||||
SMART_ERRORS="True"
|
||||
echo " ${RED}Offline Uncorrectable: $value${CLEAR}" >> "${tmp_device}_report.out"
|
||||
else
|
||||
echo " ${GREEN}Offline Uncorrectable: $value${CLEAR}" >> "${tmp_device}_report.out"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$SMART_ERRORS" == "False" ]] && [[ "$SKIP_SHORT_TEST" == "False" ]]; then
|
||||
if sudo smartctl -c "/dev/${tmp_device}" >>/dev/null 2>&1; then
|
||||
# Determine short-test polling time
|
||||
wait_time=$(sudo smartctl -c "/dev/${tmp_device}" | grep -i 'polling time' | head -1 | sed -r 's/.*\( *([0-9]+)\).*/\1/')
|
||||
wait_time=$(( wait_time + 5))
|
||||
|
||||
# Run short self-test
|
||||
echo " Running SMART short self-test ($wait_time minutes)..."
|
||||
sudo smartctl -t short "/dev/${tmp_device}" >/dev/null
|
||||
sleep ${wait_time}m
|
||||
sudo smartctl -l selftest "/dev/${tmp_device}" >> "${tmp_device}-smart-tests.log"
|
||||
if grep '^#' "${tmp_device}-smart-tests.log" | head -1 | grep -iq 'completed without error'; then
|
||||
echo " ${GREEN}Self-test: passed${CLEAR}" >> "${tmp_device}_report.out"
|
||||
echo " Self-test: passed" >> "${tmp_device}_report.log"
|
||||
else
|
||||
echo " ${RED}Self-test: failed${CLEAR}" >> "${tmp_device}_report.out"
|
||||
echo " Self-test: failed" >> "${tmp_device}_report.log"
|
||||
SMART_ERRORS="True"
|
||||
fi
|
||||
else
|
||||
echo " ${RED}ERROR: Unable to run SMART self-test.${CLEAR}" >> "${tmp_device}_report.out"
|
||||
echo " ERROR: Unable to run SMART self-test." >> "${tmp_device}_report.log"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$SMART_ERRORS" == "False" ]]; then
|
||||
eval "${tmp_device}_smart_result=CS"
|
||||
else
|
||||
ERRORS="True"
|
||||
tmp_if="${tmp_device}_smart_result"
|
||||
eval "tmp_if=\$$tmp_if"
|
||||
if [[ "$tmp_if" != "Unknown" ]]; then
|
||||
eval "${tmp_device}_smart_result=NS"
|
||||
fi
|
||||
fi
|
||||
|
||||
update_progress
|
||||
done
|
||||
fi
|
||||
|
||||
# Badblocks
|
||||
if [[ "$TEST_BADBLOCKS" == "True" ]]; then
|
||||
clear
|
||||
for d in "${DRIVES[@]}"; do
|
||||
# Get SMART results
|
||||
tmp_device="${d##*/}"
|
||||
d_smart="${tmp_device}_smart_result"
|
||||
eval "d_smart=\$$d_smart"
|
||||
|
||||
# Check SMART results
|
||||
if [[ "$d_smart" == "NS" ]]; then
|
||||
echo -e "${RED}Skipping drive: $tmp_device${CLEAR}"
|
||||
eval "${tmp_device}_badblocks_result=Skipped"
|
||||
else
|
||||
eval "${tmp_device}_badblocks_result=Working"
|
||||
update_progress
|
||||
echo "Testing drive: ${tmp_device}"
|
||||
|
||||
# Split and run
|
||||
tmux split-window -v -l 7 "hw-diags-badblocks $TMP_DIR /dev/${tmp_device}"
|
||||
|
||||
# Wait until done
|
||||
sleep 2s
|
||||
while pgrep -G 0 -U 0 -f "badblocks.*${tmp_device}" >/dev/null 2>&1; do
|
||||
sleep 1s;
|
||||
done
|
||||
sleep 2s
|
||||
|
||||
# Check log
|
||||
if grep -Eiq 'Pass completed.*0/0/0 errors' "${tmp_device}_badblocks.log"; then
|
||||
eval "${tmp_device}_badblocks_result=CS"
|
||||
else
|
||||
eval "${tmp_device}_badblocks_result=NS"
|
||||
fi
|
||||
update_progress
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Result Screen
|
||||
TEST_OVER="True"
|
||||
update_progress
|
||||
clear
|
||||
echo "─── RESULTS ───"
|
||||
if [[ "$TEST_CPU" == "True" ]]; then
|
||||
echo -e "${BLUE}CPU:${CLEAR}"
|
||||
if [[ -f "results.txt" ]]; then
|
||||
echo "results.txt"
|
||||
if grep -q -iE '(error|fail)' "prime-results.txt"; then
|
||||
echo -e "${RED}$(grep -q -iE '(error|fail)' "prime-results.txt" | sed -r 's/^/ /' | tail -4)${CLEAR}"
|
||||
else
|
||||
sed -r 's/^/ /' "prime-results.txt" 2>/dev/null | tail -4
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
if [[ -f "prime.log" ]]; then
|
||||
echo "prime.log"
|
||||
if grep -i 'completed' "prime.log" | grep -q -iv '0 errors, 0 warnings'; then
|
||||
echo -e "${RED}$(grep -i 'completed' "prime.log" | grep -iv '0 errors, 0 warnings' | sed -r 's/^/ /' | tail -4)${CLEAR}"
|
||||
else
|
||||
grep -i 'completed' "prime.log" | grep -i '0 errors, 0 warnings' | sed -r 's/^.*(Worker #[0-9]+).*(Torture.*)/ \1 \2/' | tail -4
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [[ "$TEST_SMART" == "True" ]] || \
|
||||
[[ "$TEST_BADBLOCKS" == "True" ]]; then
|
||||
for d in "${DRIVES[@]}"; do
|
||||
echo -e "${BLUE}Drive $d:${CLEAR}"
|
||||
if [[ -f "${d##*/}_report.out" ]]; then
|
||||
echo -e "$(cat "${d##*/}_report.out" 2>/dev/null)"
|
||||
fi
|
||||
if [[ -f "${d##*/}_badblocks.log" ]]; then
|
||||
grep 'Pass completed, ' "${d##*/}_badblocks.log" 2>/dev/null | sed -r 's/^Pass completed, / /' 2>/dev/null
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
fi
|
||||
update_progress
|
||||
|
||||
# System info dump
|
||||
sudo inxi -CDdGlMmNopRsc 0 | grep -Ev '(/dev/ram|No RAID devices|Display Server|multisession)' > "system_info.txt"
|
||||
|
||||
# Cleanup
|
||||
mkdir "$HOME/Tickets/$TICKET" -p 2>/dev/null
|
||||
rsync -aS --chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r "$TMP_DIR/" "$HOME/Tickets/$TICKET/"
|
||||
popd >/dev/null
|
||||
cd "$HOME/Tickets" && tar czf "${DIAG_UPLOAD_NAME}.tgz" "$TICKET"
|
||||
|
||||
# Update Server
|
||||
if [[ "$DIAG_SERVER_AVAIL" == "True" ]]; then
|
||||
rsync -aqz --chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r "${DIAG_UPLOAD_NAME}.tgz" $DIAG_USER@$DIAG_SERVER:"$DIAG_DEST/"
|
||||
fi
|
||||
|
||||
# End
|
||||
echo -n "Press Enter to exit..."
|
||||
read -r
|
||||
killall hw-diags-progress >>/dev/null 2>&1
|
||||
exit 0
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WK HW diagnostics - Network
|
||||
|
||||
function test_connection() {
|
||||
cmd="a"
|
||||
if [[ -e "/sys/class/net/$1" ]]; then
|
||||
cmd="a show $1"
|
||||
fi
|
||||
if ip $cmd | grep -Eq '(10.[0-9]+|172.(1[6-9]|2[0-9]|3[0-1]).[0-9]+|192.168).[0-9]+.[0-9]+'; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
CLEAR="\e[0m"
|
||||
RED="\e[31m"
|
||||
GREEN="\e[32m"
|
||||
YELLOW="\e[33m"
|
||||
BLUE="\e[34m"
|
||||
|
||||
# Header
|
||||
echo "WK HW Diagnostics - Network"
|
||||
echo ""
|
||||
|
||||
# Start Wifi if necessary
|
||||
echo "Initializing..."
|
||||
connect-to-network >/dev/null 2>&1
|
||||
|
||||
# Check network connection
|
||||
echo -n "Network connection: "
|
||||
if test_connection; then
|
||||
echo -e "${GREEN}OK${CLEAR}"
|
||||
else
|
||||
echo -e "${RED}No access${CLEAR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check IP addresses
|
||||
for d in /sys/class/net/*; do
|
||||
device="$(basename $d)"
|
||||
if [ "$device" != "lo" ]; then
|
||||
if test_connection $device; then
|
||||
ip="$(ip a show $device | egrep 'inet [0-9]' | sed -r 's#.*inet (.*?/[0-9]+).*#\1#')"
|
||||
echo "$device: $ip" | awk '{printf " %-16s %s\n", $1, $2}'
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Check internet connection
|
||||
echo -n "Internet connection: "
|
||||
if ping -c 2 -q 8.8.8.8 >/dev/null 2>&1; then
|
||||
echo -e "${GREEN}OK${CLEAR}"
|
||||
else
|
||||
echo -e "${RED}No access${CLEAR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check DNS
|
||||
echo -n "DNS Resolution: "
|
||||
if ping -c 2 -q google.com >/dev/null 2>&1; then
|
||||
echo -e "${GREEN}OK${CLEAR}"
|
||||
else
|
||||
echo -e "${RED}Unable to resolve google.com${CLEAR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check speed
|
||||
echo "Speedtest:"
|
||||
speedtest-cli --simple | awk '{printf " %-16s %6.2f %s\n", $1, $2, $3}'
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WK HW diagnostics - Progress
|
||||
|
||||
# Loop forever
|
||||
while :; do
|
||||
clear
|
||||
echo -e "$(cat "$1")"
|
||||
sleep 1s
|
||||
done
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## WK HW diagnostics - Sensors
|
||||
|
||||
LOG_DIR="$1"
|
||||
|
||||
function usage {
|
||||
echo "Usage: $0 log-dir"
|
||||
echo " e.g. $0 /tmp/tmp.7Mh5f1RhSL9001"
|
||||
}
|
||||
|
||||
# Create directory if necessary
|
||||
if [ ! -d "$LOG_DIR" ]; then
|
||||
LOG_DIR="$(mktemp -d)"
|
||||
fi
|
||||
|
||||
# Run Sensor loop
|
||||
if sensors >/dev/null 2>&1; then
|
||||
while :; do
|
||||
sensors -A | grep -E -i -v '(N/A|RPM|\d+\s+V\s+|^\s*$)' > "$LOG_DIR/sensors.out" 2>/dev/null
|
||||
|
||||
# Colorize
|
||||
# Blue: All temps (superseeded by other colors below)
|
||||
sed -i -r 's#(\+[0-9]+\.[0-9].C)#\\e[34m\1\\e[0m#g' "$LOG_DIR/sensors.out" >/dev/null 2>&1
|
||||
# Green >= 60* C
|
||||
sed -i -r 's#(\+6[0-9]\.[0-9].C)#\\e[32m\1\\e[0m#g' "$LOG_DIR/sensors.out" >/dev/null 2>&1
|
||||
# Yellow >= 70* C
|
||||
sed -i -r 's#(\+7[0-9]\.[0-9].C)#\\e[33m\1\\e[0m#g' "$LOG_DIR/sensors.out" >/dev/null 2>&1
|
||||
# Orange >= 80* C
|
||||
sed -i -r 's#(\+(8[0-9]|9[0-4])\.[0-9].C)#\\e[31\;1m\1\\e[0m#g' "$LOG_DIR/sensors.out" >/dev/null 2>&1
|
||||
# Red >= 95* C
|
||||
sed -i -r 's#(\+(9[5-9]|1[0-9][0-9])\.[0-9].C)#\\e[31m\1\\e[0m#g' "$LOG_DIR/sensors.out" >/dev/null 2>&1
|
||||
|
||||
# Output data
|
||||
clear
|
||||
echo -e "$(cat "$LOG_DIR/sensors.out")"
|
||||
sleep 1s
|
||||
done
|
||||
else
|
||||
echo -e "\e[33mNo sensors found!\nPlease monitor temperatures manually\e[0m"
|
||||
sleep 1h
|
||||
fi
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## Mount all volumes read-only
|
||||
|
||||
# Mount all volumes
|
||||
echo "Mounting all volumes"
|
||||
regex="/dev/((h|s)d[a-z]|md)[0-9]+"
|
||||
for volume in $(inxi -Dopxx | grep -E "$regex" | sed -r "s#.*($regex).*#\1#" | sort); do
|
||||
if grep -q "$volume" /proc/mounts; then
|
||||
if ! mount | grep "/run/archiso/bootmnt" | grep -q "$volume"; then
|
||||
# Show what's already mounted except the WK_ARCH boot device
|
||||
echo "$volume: (Already) mounted $(mount | grep "$volume" | sed -r 's/^\S+ (on.*) type .*/\1/') ($(df -h "$volume" | tail -1 | awk '{print $3, $4}' | sed -r 's/(K|M|G|T|) (.*[0-9])(K|M|G|T|)$/ \1b used, \2 \3b free/'))"
|
||||
fi
|
||||
else
|
||||
if udevil mount -o ro $volume >/dev/null 2>&1; then
|
||||
echo "$volume: Mounted $(mount | grep "$volume" | sed -r 's/^\S+ (on.*) type .*/\1/') ($(df -h "$volume" | tail -1 | awk '{print $3, $4}' | sed -r 's/(K|M|G|T|) (.*[0-9])(K|M|G|T|)$/ \1b used, \2 \3b free/'))"
|
||||
else
|
||||
echo "$volume: Failed to mount"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Open folder?
|
||||
if echo "$0" | grep -iq foh; then
|
||||
thunar /media
|
||||
fi
|
||||
|
||||
echo "Done."
|
||||
|
|
@ -1 +0,0 @@
|
|||
mount-all-volumes
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
## Mount NAS backup shares
|
||||
|
||||
die () {
|
||||
echo "$0:" "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Load settings
|
||||
if [[ -f "/run/archiso/bootmnt/arch/arch.conf" ]]; then
|
||||
source "/run/archiso/bootmnt/arch/arch.conf" || \
|
||||
die "ERROR: failed to load arch.conf (from /run/archiso/bootmnt/arch/)"
|
||||
else
|
||||
source "/usr/local/bin/arch.conf" || \
|
||||
die "ERROR: failed to load arch.conf (from /usr/local/bin/)"
|
||||
fi
|
||||
|
||||
# Connect to a network
|
||||
connect-to-network
|
||||
|
||||
# Mount loop
|
||||
echo "Mounting NAS backup shares"
|
||||
for x in {1..4}; do
|
||||
_skip="False"
|
||||
|
||||
# Load Backup share info
|
||||
eval "declare -a _backup=(\${BACKUP_$x[@]})"
|
||||
_name="${_backup[0]}"
|
||||
_ip="${_backup[1]}"
|
||||
_share="${_backup[2]}"
|
||||
_user="${_backup[3]}"
|
||||
_pass="${_backup[4]}"
|
||||
|
||||
# Check backup share info
|
||||
if echo "$_name" | grep -Eq '^\s*$'; then
|
||||
_skip="True";
|
||||
fi
|
||||
if echo "$_ip" | grep -Eq '^\s*$'; then
|
||||
_skip="True";
|
||||
fi
|
||||
if echo "$_share" | grep -Eq '^\s*$'; then
|
||||
_skip="True";
|
||||
fi
|
||||
if echo "$_user" | grep -Eq '^\s*$'; then
|
||||
_skip="True";
|
||||
fi
|
||||
if echo "$_pass" | grep -Eq '^\s*$'; then
|
||||
_skip="True";
|
||||
fi
|
||||
|
||||
# Mount
|
||||
if [[ "$_skip" == "False" ]]; then
|
||||
sudo mkdir "/Backups/$_name" -p
|
||||
if mountpoint -q "/Backups/$_name"; then
|
||||
echo "$_name: (Already) mounted at /Backups/$_name ($(df -h "/Backups/$_name" | tail -1 | awk '{print $4}' | sed -r 's/([KMGT])/ \1b/') free)"
|
||||
else
|
||||
if sudo mount "//$_ip/$_share" "/Backups/$_name" -o username=$_user,password=$_pass 2>/dev/null; then
|
||||
echo "$_name: Mounted at /Backups/$_name ($(df -h "/Backups/$_name" | tail -1 | awk '{print $4}' | sed -r 's/([KMGT])/ \1b/') free)"
|
||||
else
|
||||
rmdir "/Backups/$_name" -p 2>/dev/null
|
||||
echo "$_name: Failed to mount"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
TMP_FILE="$(mktemp)"
|
||||
|
||||
IFS=$'\n'
|
||||
for s in $*; do
|
||||
REGEX="$s"
|
||||
REGEX=$(echo "$REGEX" | sed -r 's/\s+/\\s\*/g')
|
||||
|
||||
# Word Doc
|
||||
for d in *doc; do
|
||||
if antiword "$d" | grep -iqsP "($REGEX)"; then
|
||||
echo "Possible match: $d"
|
||||
echo "$d" >> "$TMP_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
# Word Docx
|
||||
for d in *docx; do
|
||||
if unzip -p "$d" word/document.xml | grep -iqsP "($REGEX)"; then
|
||||
echo "Possible match: $d"
|
||||
echo "$d" >> "$TMP_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
done
|
||||
|
||||
# Cleanup results
|
||||
if [[ -s "$TMP_FILE" ]]; then
|
||||
sort -u "$TMP_FILE" >> "$HOME/msword-matches.txt"
|
||||
fi
|
||||
rm "$TMP_FILE"
|
||||
|
||||
# Done
|
||||
if [[ -s "$HOME/msword-matches.txt" ]]; then
|
||||
echo "Found $(wc -l "$HOME/msword-matches.txt") possible matches"
|
||||
echo "The results have been saved to $HOME"
|
||||
fi
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
# Wizard Kit: Settings - WiFi
|
||||
|
||||
|
|
@ -5,9 +5,11 @@ hfsprogs
|
|||
i3-gaps
|
||||
i3lock-fancy-git
|
||||
mprime-bin
|
||||
nvme-cli
|
||||
openbox-patched
|
||||
papirus-icon-theme
|
||||
pasystray
|
||||
smartmontools-svn
|
||||
testdisk-wip
|
||||
ttf-font-awesome
|
||||
wd719x-firmware
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ archiso
|
|||
attr
|
||||
base-devel
|
||||
curl
|
||||
dos2unix
|
||||
git
|
||||
libewf
|
||||
openssh
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ mupdf
|
|||
ncdu
|
||||
network-manager-applet
|
||||
networkmanager
|
||||
nvme-cli
|
||||
oblogout
|
||||
openbox-patched
|
||||
p7zip
|
||||
|
|
@ -54,12 +55,16 @@ pasystray
|
|||
pavucontrol
|
||||
progsreiserfs
|
||||
pulseaudio
|
||||
python
|
||||
python-psutil
|
||||
python-requests
|
||||
qemu-guest-agent
|
||||
reiserfsprogs
|
||||
rfkill
|
||||
rng-tools
|
||||
rofi
|
||||
rxvt-unicode
|
||||
smartmontools-svn
|
||||
speedtest-cli
|
||||
spice-vdagent
|
||||
terminus-font
|
||||
|
|
@ -75,6 +80,7 @@ udevil
|
|||
udisks2
|
||||
ufw
|
||||
unzip
|
||||
util-linux
|
||||
veracrypt
|
||||
vim
|
||||
virtualbox-guest-modules-arch
|
||||
|
|
|
|||
18
Build Linux
18
Build Linux
|
|
@ -66,10 +66,7 @@ function load_settings() {
|
|||
## Answer: https://stackoverflow.com/a/13864829
|
||||
## Answer by: https://stackoverflow.com/users/1633643/lionel
|
||||
## Answer edit: https://stackoverflow.com/users/-1/community
|
||||
if [ -z ${KIT_NAME_FULL+x} ]; then
|
||||
# KIT_NAME_FULL is unset
|
||||
: # pass
|
||||
else
|
||||
if [ ! -z ${KIT_NAME_FULL+x} ]; then
|
||||
# KIT_NAME_FULL is set
|
||||
return 0 # Skip loading settings from main.py
|
||||
fi
|
||||
|
|
@ -77,6 +74,7 @@ function load_settings() {
|
|||
# Copy settings
|
||||
if [[ ! -e "$BUILD_DIR/main.py" ]] || ask "Overwrite main.py?"; then
|
||||
cp -bv "$ROOT_DIR/.bin/Scripts/settings/main.py" "$BUILD_DIR/main.py"
|
||||
unix2dos "$BUILD_DIR/main.py"
|
||||
fi
|
||||
|
||||
# Edit settings
|
||||
|
|
@ -98,6 +96,8 @@ function copy_live_env() {
|
|||
|
||||
# Add items
|
||||
rsync -aI "$ROOT_DIR/.linux_items/include/live/" "$LIVE_DIR/"
|
||||
mkdir -p "$LIVE_DIR/airootfs/usr/local/bin"
|
||||
rsync -aI "$ROOT_DIR/.bin/Scripts/" "$LIVE_DIR/airootfs/usr/local/bin/"
|
||||
|
||||
# Remove items
|
||||
rm "$LIVE_DIR/airootfs/etc/systemd/scripts/choose-mirror"
|
||||
|
|
@ -140,6 +140,7 @@ function update_live_env() {
|
|||
echo "[custom]" >> "$LIVE_DIR/pacman.conf"
|
||||
echo "SigLevel = Optional TrustAll" >> "$LIVE_DIR/pacman.conf"
|
||||
echo "Server = file://$REPO_DIR" >> "$LIVE_DIR/pacman.conf"
|
||||
echo "" >> "$LIVE_DIR/pacman.conf"
|
||||
|
||||
# Mirrors
|
||||
sed -i -r 's/^(.*mirrorlist.*)$/#NOPE#\1/' "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
||||
|
|
@ -147,7 +148,7 @@ function update_live_env() {
|
|||
echo "sed -i 's/#Server/Server/g' /etc/pacman.d/mirrorlist" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
||||
|
||||
# MOTD
|
||||
sed -i "s/WK/$KIT_NAME_SHORT/" "$LIVE_DIR/airootfs/etc/motd"
|
||||
sed -i "s/_+/$KIT_NAME_FULL Linux Environment/" "$LIVE_DIR/airootfs/etc/motd"
|
||||
|
||||
# Oh My ZSH
|
||||
git clone --depth=1 git://github.com/robbyrussell/oh-my-zsh.git "$SKEL_DIR/.oh-my-zsh"
|
||||
|
|
@ -156,7 +157,8 @@ function update_live_env() {
|
|||
|
||||
# Openbox theme
|
||||
git clone --depth=1 git@github.com:addy-dclxvi/Openbox-Theme-Collections.git "$TEMP_DIR/ob-themes"
|
||||
cp -a "$TEMP_DIR/ob-themes/Triste-Orange" "$LIVE_DIR/airootfs/usr/share/themes/Triste-Orange"
|
||||
mkdir -p "$LIVE_DIR/airootfs/usr/share/themes"
|
||||
cp -a "$TEMP_DIR/ob-themes/Triste-Orange" "$LIVE_DIR/airootfs/usr/share/themes/"
|
||||
|
||||
# Services
|
||||
sed -i -r 's/^(.*pacman-init.*)$/#NOPE#\1/' "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
||||
|
|
@ -196,10 +198,6 @@ 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"
|
||||
|
||||
# WiFi
|
||||
echo "WIFI_SSID='$WIFI_SSID'" >> "$LIVE_DIR/airootfs/usr/local/bin/wifi.conf"
|
||||
echo "WIFI_PASSWORD='$WIFI_PASSWORD'" >> "$LIVE_DIR/airootfs/usr/local/bin/wifi.conf"
|
||||
}
|
||||
|
||||
function update_repo() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue