armbian-build/scripts/armbianmonitor/armbianmonitor
2016-06-24 13:13:37 +02:00

808 lines
37 KiB
Bash

#!/bin/bash
#
# armbianmonitor
#
# This script serves different purposes based on how it is called:
#
# - toggle boot verbosity (works)
# - monitoring mode: continually print monitoring info (WiP)
# - uploading /var/log/armhwinfo.log to online pastebin service
#
# Without arguments called it should present a simple user
# interface that guides through:
#
# - installation of RPi-Monitor if not already installed by user
# - active basic or more verbose monitoring mode
# - provides monitoring parameters for connected disks
#
# The second part is WiP and all the user interaction part
# still completely missing.
#
# This script is used to configure armbianmonitor behaviour.
# It will ask the user whether to activate monitoring or not,
# whether to enable debug monitoring and also how to deal with
# connected disks. In fact it walks through the list of available
# disks, checks them, tries to patch hddtemp.db if necessary
# and provides a proposal for /etc/armbianmonitor/disks.conf
# when a new disk is found.
#
# In case monitoring should be activated the following file
# will be created: /etc/armbianmonitor/start-monitoring. If
# debug output has been chosen, then DEBUG will be written to
# the file.
#
# The script will install smartmontools/gdisk if not already
# installed and patches smartmontools' update-smart-drivedb
# script if necessary. For disks the 'device model' will be
# shown but internally we rely always on the GUID. This is the
# key for entry in /etc/armbianmonitor/disks.conf
#
# When the script exits and the user activated monitoring it
# recommends doing a restart since on the next reboot the
# setup-armbian-monitoring-environment script will configure
# monitoring sources and decides based on the existence and
# contents of /etc/armbianmonitor/start-monitoring whether
# rpimonitord should be started or not.
#
# The format of /etc/armbianmonitor/disks.conf is as follows:
#
# ${GUID}:${Name}:${smartctl prefix}:${temp call}:${CRC}:${LCC}
#
# Two examples:
#
# A57BF307-7D82-4783-BD1D-B346CA8C195B:WD Green::199:193 # WD HDD on SATA
# F8D372DC-63DB-494B-B802-87DC47FAD4E1:Samsung EVO:sat::199: # SSD in USB enclosure
#
# - GUID is the GUID as determined by gdisk
# - 'Name': The name as it will later be shown in RPi-Monitor, defaults to
# the 'device model' read out through smartctl but can be changed to
# be more significant (beware that this string must contain colons!)
# - "smartctl prefix" can be empty or should be the the necessary prefix for
# USB disks, eg. '-d sat' or '-d usbjmicron' and so on -- please have a
# look at https://www.smartmontools.org/wiki/Supported_USB-Devices
# - "temp call" when being omitted indicates that hddtemp should be used.
# Otherwise it should contain the complete command line ('DISK' will be
# dynamically replaced by the device node when the actual monitoring
# happens), for example:
# /sbin/hdparm -C DISK | egrep -q "standby|sleeping" || /usr/sbin/smartctl -d sat -a DISK | awk -F" " '/Temperature_Cel/ {printf $10}'
# - 'CRC attribute': The decimal value of the S.M.A.R.T. attribute that
# is used to store the count of checksum errors between disk and host
# controller (might be omitted if the drive doesn't support it)
# - 'LCC attribute': The decimal value of the S.M.A.R.T. attribute that
# should contain the load cycle counter value (might be omitted
# if the drive doesn't support it)
#
# TODO:
#
# - develop main functionality ;) asking the user regarding monitoring
# - deal with 'SMART overall-health self-assessment test result:'
# - write documentation
#
############################################################################
Main() {
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# check if stdout is a terminal...
if test -t 1; then
# see if it supports colors...
ncolors=$(tput colors)
if test -n "$ncolors" && test $ncolors -ge 8; then
BOLD="$(tput bold)"
NC='\033[0m' # No Color
LGREEN='\033[1;32m'
LRED='\e[0;91m'
fi
fi
[ $# -eq 0 ] && (DisplayUsage ; exit 0)
ParseOptions "$@"
exit 0
PreRequisits
# check whether user runs rpimonitord on his own or we activated it
if [ -f /etc/armbianmonitor/start-monitoring ]; then
# we should already provide monitoring, check whether DEBUG
# is also set
ArmbianMonitoring=TRUE
read DebugMode </etc/armbianmonitor/start-monitoring 2>/dev/null
fi
# check whether rpimonitord is running and compare with ${ArmbianMonitoring}
# In case the user chose to run rpimonitord on his own, we skip the config
# part and only output disk info
:
# check available disk devices
CheckDisks
} # Main
ParseOptions() {
while getopts 'hHbBuUrRmMdDc:C:' c ; do
case ${c} in
H)
# display full help test
# export FullUsage=TRUE
DisplayUsage
exit 0
;;
h)
# display short help
DisplayUsage
exit 0
;;
b|B)
# toggle boot verbosity
if [ -f /boot/.force-verbose ]; then
rm /boot/.force-verbose
echo -e "Verbose kernel messages have been disabled. Please reboot for changes to take effect."
else
date "+%s" >/boot/.force-verbose
chmod 666 /boot/.force-verbose
echo -e "Verbose kernel messages have been enabled. Please reboot for changes to take effect."
fi
exit 0
;;
m|M)
# monitoring mode
echo -e "Stop monitoring using [ctrl]-[c]"
MonitorMode ${OPTARG}
exit 0
;;
u|U)
# Upload /var/log/armhwinfo.log to be of help in support forum.
fping sprunge.us | grep -q alive || \
(echo "Network/firewall problem detected. Please fix this or upload /var/log/armhwinfo.log manually." >&2 ; exit 1)
which curl >/dev/null 2>&1 || apt-get -f -qq -y install curl
echo -e "/var/log/armhwinfo.log has been uploaded to \c"
# we obfuscate IPv4 addresses somehow but not too much, MAC addresses have to remain
# in clear since otherwise the log becomes worthless due to randomly generated
# addresses here and there that might conflict
CollectSupportInfo \
| sed -E 's/([0-9]{1,3}\.)([0-9]{1,3}\.)([0-9]{1,3}\.)([0-9]{1,3})/XXX.XXX.\3\4/g' \
| curl -F 'sprunge=<-' http://sprunge.us
echo -e "Please post the URL in the Armbian forum where you've been asked for."
exit 0
;;
r|R)
# Installs RPi-Monitor and patches config on sun8i
fping armbian.com | grep -q alive || \
(echo "Network/firewall problem detected. Please fix this prior to installing RPi-Monitor." >&2 ; exit 1)
InstallRPiMonitor
case $(awk '/Hardware/ {print $3}' </proc/cpuinfo) in
sun8i)
PatchRPiMonitor_for_sun8i
;;
esac
echo -e "\nNow you're able to enjoy RPi-Monitor at http://$((ifconfig -a) | sed -n '/inet addr/s/.*addr.\([^ ]*\) .*/\1/p' | head -1):8888"
exit 0
;;
d|D)
fping sprunge.us | grep -q alive || \
(echo "Network/firewall problem detected. Please fix this prior to installing RPi-Monitor." >&2 ; exit 1)
DebugOutput="$(mktemp /tmp/${0##*/}.XXXXXX)"
trap "rm \"${DebugOutput}\" ; exit 0" 0 1 2 3 15
set -x
exec 2>"${DebugOutput}"
PreRequisits >/dev/null 2>&1
CheckDisks
which curl >/dev/null 2>&1 || apt-get -f -qq -y install curl
echo -e "\nDebug output has been collected at the following URL: \c"
(cat "${DebugOutput}"; echo -e "\n\n\ngdisk.txt contents:\n" ; cat "${MyTempDir}/gdisk.txt" ;\
echo -e "\n\n\nsmartctl.txt contents:\n" ; cat "${MyTempDir}/smartctl.txt") \
| curl -F 'sprunge=<-' http://sprunge.us
echo -e "Please post the URL in the Armbian forum where you've been asked for."
exit 0
;;
c|C)
# check card mode
CheckCard "${OPTARG}"
exit 0
;;
esac
done
} # ParseOptions
DisplayUsage() {
echo -e "Usage: ${BOLD}${0##*/} [-h] [-b] [-c \$path] [-d] [-m] [-r] [-u]${NC}\n"
echo -e "############################################################################"
if [ ${FullUsage} ]; then
echo -e "\nDetailed Description:"
grep "^#" "$0" | grep -v "^#\!/bin/bash" | sed 's/^#//'
fi
echo -e "\n Use ${BOLD}armbianmonitor${NC} for the following tasks:\n"
echo -e " armbianmonitor ${BOLD}-b${NC} switches between verbose and normal boot"
echo -e " armbianmonitor ${BOLD}-c /path/to/test${NC} performs disk health/performance tests"
echo -e " armbianmonitor ${BOLD}-d${NC} tries to upload debug disk info to improve armbianmonitor"
echo -e " armbianmonitor ${BOLD}-m${NC} provides simple CLI monitoring"
echo -e " armbianmonitor ${BOLD}-r${NC} tries to install RPi-Monitor"
echo -e " armbianmonitor ${BOLD}-u${NC} tries to upload armhwinfo.log for support purposes\n"
echo -e "############################################################################\n"
} # DisplayUsage
MonitorMode() {
# $1 is the time in seconds to pause between two prints, defaults to 5 seconds
# This functions prints out endlessly:
# - time/date
# - average 1m load
# - detailed CPU statistics
# - Soc temperature if available
# - PMIC temperature if available
# TODO: Format output nicely
LastUserStat=0
LastNiceStat=0
LastSystemStat=0
LastIdleStat=0
LastIOWaitStat=0
LastIrqStat=0
LastSoftIrqStat=0
LastCpuStatCheck=0
Sensors="/etc/armbianmonitor/datasources/"
echo -e "Time CPU load %cpu %sys %usr %nice %io %irq\c"
[ -f "${Sensors}/soctemp" ] && echo -e " CPU\c"
[ -f "${Sensors}/pmictemp" ] && echo -e " PMIC\c"
while true ; do
LoadAvg=$(cut -f1 -d" " </proc/loadavg)
CpuFreq=$(awk '{printf ("%0.0f",$1/1000); }' </sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq)
echo -e "\n$(date "+%H:%M:%S"): $(printf "%4s" ${CpuFreq})MHz $(printf "%5s" ${LoadAvg}) $(ProcessStats)\c"
if [ -f "${Sensors}/soctemp" ]; then
read SocTemp <"${Sensors}/soctemp"
if [ ${SocTemp} -ge 1000 ]; then
SocTemp=$(awk '{printf ("%0.1f",$1/1000); }' <"${Sensors}/soctemp")
fi
echo -e " $(printf "%4s" ${SocTemp})°C\c"
fi
[ -f "${Sensors}/pmictemp" ] && \
(PMICTemp=$(awk '{printf ("%0.1f",$1/1000); }' <"${Sensors}/pmictemp") ; echo -e " $(printf "%4s" ${PMICTemp})°C\c")
sleep ${1:-5}
done
} # MonitorMode
ProcessStats() {
if [ -f /tmp/cpustat ]; then
# RPi-Monitor/Armbianmonitor already running and providing processed values
set $(awk -F" " '{print $1"\t"$2"\t"$3"\t"$4"\t"$5"\t"$6}' </tmp/cpustat)
CPULoad=$1
SystemLoad=$2
UserLoad=$3
NiceLoad=$4
IOWaitLoad=$5
IrqCombinedLoad=$6
else
set $(awk -F" " '/^cpu / {print $2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7"\t"$8}' </proc/stat)
UserStat=$1
NiceStat=$2
SystemStat=$3
IdleStat=$4
IOWaitStat=$5
IrqStat=$6
SoftIrqStat=$7
UserDiff=$(( ${UserStat} - ${LastUserStat} ))
NiceDiff=$(( ${NiceStat} - ${LastNiceStat} ))
SystemDiff=$(( ${SystemStat} - ${LastSystemStat} ))
IdleDiff=$(( ${IdleStat} - ${LastIdleStat} ))
IOWaitDiff=$(( ${IOWaitStat} - ${LastIOWaitStat} ))
IrqDiff=$(( ${IrqStat} - ${LastIrqStat} ))
SoftIrqDiff=$(( ${SoftIrqStat} - ${LastSoftIrqStat} ))
Total=$(( ${UserDiff} + ${NiceDiff} + ${SystemDiff} + ${IdleDiff} + ${IOWaitDiff} + ${IrqDiff} + ${SoftIrqDiff} ))
CPULoad=$(( ( ${Total} - ${IdleDiff} ) * 100 / ${Total} ))
UserLoad=$(( ${UserDiff} *100 / ${Total} ))
SystemLoad=$(( ${SystemDiff} *100 / ${Total} ))
NiceLoad=$(( ${NiceDiff} *100 / ${Total} ))
IOWaitLoad=$(( ${IOWaitDiff} *100 / ${Total} ))
IrqCombinedLoad=$(( ( ${IrqDiff} + ${SoftIrqDiff} ) *100 / ${Total} ))
LastUserStat=${UserStat}
LastNiceStat=${NiceStat}
LastSystemStat=${SystemStat}
LastIdleStat=${IdleStat}
LastIOWaitStat=${IOWaitStat}
LastIrqStat=${IrqStat}
LastSoftIrqStat=${SoftIrqStat}
fi
echo -e "$(printf "%3s" ${CPULoad})%$(printf "%4s" ${SystemLoad})%$(printf "%4s" ${UserLoad})%$(printf "%4s" ${NiceLoad})%$(printf "%4s" ${IOWaitLoad})%$(printf "%4s" ${IrqCombinedLoad})%"
} # ProcessStats
CheckDisks() {
# This function walks through all block devices whose name starts with sd* and
# then gets the name hddtemp expects, the model name from smartctl, looks whether
# the drive only lists one temperature value and patches hddtemp.db if necessary
# and also tries to get CRC and LCC S.M.A.R.T. attributes to provide the user
# with the necessary config file contents for /etc/armbianmonitor/disks.conf:
ls /sys/block/sd* >/dev/null 2>&1 || exit 0
for i in /sys/block/sd* ; do
DeviceNode=/dev/${i##*/}
# get GUID/UUID for disk and check whether a partition table is existent. If
# not GUID will always be random
gdisk -l ${DeviceNode} >"${MyTempDir}/gdisk.txt"
GUID=$(awk -F" " '/^Disk identifier/ {print $4}' <"${MyTempDir}/gdisk.txt")
CountOfUnavailablePartitionTables=$(grep ': not present' "${MyTempDir}/gdisk.txt" | wc -l)
if [ ${CountOfUnavailablePartitionTables} -eq 4 ]; then
echo -e "\nSkipping ${DeviceNode} due to missing partition table. Use parted to create one."
break
else
echo -e "\nExamining ${DeviceNode} with GUID ${GUID}\c"
fi
# get name hddtemp needs
HddtempName="$(hddtemp --debug ${DeviceNode} | awk -F": " '/^Model: / {print $2}' | \
cut -c-40 | sed 's/^[ \t]*//;s/[ \t]*$//')"
# store smartctl output in temporary file
smartctl -q noserial -s on -a ${DeviceNode} >"${MyTempDir}/smartctl.txt" 2>&1
DeviceModel="$(awk -F": " '/^Device Model/ {print $2}' <"${MyTempDir}/smartctl.txt" | \
sed 's/^[ \t]*//;s/[ \t]*$//')"
if [ "X${DeviceModel}" = "X" ]; then
# Reading S.M.A.R.T. failed, we try autodetect mode iterating through all
# known smartctl modes (-d auto|sat|usbcypress|usbjmicron|usbprolific|usbsunplus)
SMARTPrefix="$(CheckSMARTModes ${DeviceNode} 2>/dev/null)"
if [ "X${SMARTPrefix}" = "X" ]; then
# we can't query the disk. Time to give up
echo -e "\nUnable to query the disk through S.M.A.R.T.\nPlease investigate manually using smartctl\n"
break
fi
fi
# user feedback
if [ "X${SMARTPrefix}" = "X" ]; then
echo -e " \n(accessible through S.M.A.R.T.)"
else
echo -e " \n(can be queried with \"-d ${SMARTPrefix}\" through S.M.A.R.T.)"
fi
# check for CRC and LCC attributes
CRCAttribute=$(awk -F" " '/CRC_Error_Count/ {print $1}' <"${MyTempDir}/smartctl.txt")
LCCAttribute=$(grep -i "load.cycle" "${MyTempDir}/smartctl.txt" | awk -F" " '{print $1}')
# check whether /etc/hddtemp.db should be patched
grep -q "${HddtempName}" /etc/hddtemp.db
if [ $? -ne 0 ]; then
# No entry into hddtemp database, we've a look whether there's a 'temperature'
# attribute available (we take the 1st we find) and if that's the case we use this
DiskTemp=$(awk -F" " '/Temperature/ {print $1}' <"${MyTempDir}/smartctl.txt" | head -n1)
if [[ ${DiskTemp} -gt 0 ]]; then
echo -e "\"${HddtempName}\" ${DiskTemp} C \"${DeviceModel}\"" >>/etc/hddtemp.db
echo -e "\nAdded disk \"${DeviceModel}\"/\"${HddtempName}\" to /etc/hddtemp.db using S.M.A.R.T. attribute ${DiskTemp}\nbased on the following available thermal values:"
grep "Temperature" "${MyTempDir}/smartctl.txt"
# check hddtemp result
HddtempResult=$(hddtemp -n ${DeviceNode} | grep -v 'not available' | awk -F" " '{print $1}')
if [ "X${HddtempResult}" != "X${DeviceNode}:" ]; then
# hddtemp isn't able to query the disk
HddtempStatus="does not work. Please check with smartctl and adjust config accordingly"
echo -e "\nhddtemp output: $(hddtemp ${DeviceNode})"
echo -e "\nIt seems we can not rely on hddtemp to query this disk. Please try smartctl instead\n"
else
HddtempStatus="will work"
echo -e "\nhddtemp output: ${HddtempResult})"
echo -e "\nIn case this seems not to be correct please adjust /etc/hddtemp.db manually\n"
fi
else
HddtempStatus="does not work. Please check with smartctl and adjust config accordingly"
fi
else
HddtempStatus="will work"
fi
# check for firmware updates
FirmwareUpdate="$(grep "^http" "${MyTempDir}/smartctl.txt")"
# Check whether the disk (based on GUID) is already configured in our config file
# /etc/armbianmonitor/disks.conf or not
grep -q "^${GUID}:" /etc/armbianmonitor/disks.conf >/dev/null 2>/dev/null
case $? in
0)
# already listed, we provide just infos:
echo -e "Disk is already configured by the following monitoring config:\n$(grep "^${GUID}:" /etc/armbianmonitor/disks.conf)\n"
;;
*)
# new disk, we recommend an entry for /etc/armbianmonitor/disks.conf
echo -e "Disk not configured for monitoring. We were able to extract the following \ninformation:\n GUID: ${GUID}"
if [ "X${SMARTPrefix}" != "X" ]; then
echo -e " QueryMode: -d ${SMARTPrefix}"
fi
echo -e " hddtemp: ${HddtempStatus}\n CRC attribute: ${CRCAttribute}\n LCC Attribute: ${LCCAttribute}"
case ${HddtempStatus} in
"will work")
echo -e "If you want to monitor the disk please add to /etc/armbianmonitor/disks.conf:\n${GUID}:${DeviceModel}:${SMARTPrefix}::${CRCAttribute}:${LCCAttribute}"
;;
*)
echo -e "Proposal for /etc/armbianmonitor/disks.conf:\n${GUID}:${DeviceModel}:${SMARTPrefix}:FIXME:${CRCAttribute}:${LCCAttribute}"
echo -e "You have to figure out how to query the disk for its thermal sensor."
echo -e "Please check the output of \"hddtemp --debug ${DeviceNode}\" and smartctl\n"
;;
esac
;;
esac
if [ "X${FirmwareUpdate}" != "X" ]; then
echo -e "\nWARNING: A firmware update seems to be available:\n${FirmwareUpdate}\n"
fi
done
} # CheckDisks
CheckSMARTModes() {
# This function tries to access USB disks through S.M.A.R.T. and returns the necessary
# '-d' call as well as fills in ${MyTempDir}/smartctl.txt
for i in auto sat usbcypress usbjmicron usbprolific usbsunplus ; do
# user feedback
# echo -n "." >/dev/tty
# query disk using the specific protocol
echo -n "" >"${MyTempDir}/smartctl.txt"
smartctl -q noserial -s on -d ${i} -a ${1} >"${MyTempDir}/smartctl.txt" 2>/dev/null
DeviceModel="$(awk -F": " '/^Device Model/ {print $2}' <"${MyTempDir}/smartctl.txt" | \
sed 's/^[ \t]*//;s/[ \t]*$//')"
if [ "X${DeviceModel}" != "X" ]; then
echo ${i}
break
fi
done
} # CheckSMARTModes
PreRequisits() {
# Ensure that we're running as root since otherwise querying SATA/USB disks won't work
if [ "$(id -u)" != "0" ]; then
echo "This script must be run as root" >&2
exit 1
fi
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
unset LANG
DISTROCODE=$(lsb_release -s -c)
# check whether gdisk/smartctl are available and up to date
echo -e "Check whether necessary software is available\c"
which gdisk >/dev/null 2>&1 || (echo -e " Installing gdisk\c" ; apt-get -f -qq -y install gdisk)
which smartctl >/dev/null 2>&1 || (echo -e " Installing smartmontools\c" ; apt-get -f -qq -y install smartmontools)
echo -e " [done]\nUpdating smartmontools' drivedb\c"
/usr/sbin/update-smart-drivedb >/dev/null 2>&1
if [ $? -ne 0 -a "X${DISTROCODE}" = "Xwheezy" ]; then
sed -i "/^SRCEXPR/{s#=.*#='http://sourceforge.net/p/smartmontools/code/HEAD/tree/\$location/smartmontools/drivedb.h?format=raw'#}" /usr/sbin/update-smart-drivedb
/usr/sbin/update-smart-drivedb
fi
echo -e " [done]"
CreateTempDir
} # PreRequisits
CreateTempDir() {
# create a safe temporary dir
MyTempDir=$(mktemp -d /tmp/${0##*/}.XXXXXX)
if [ ! -d "${MyTempDir}" ]; then
MyTempDir=/tmp/${0##*/}.$RANDOM.$RANDOM.$RANDOM.$$
(umask 066 && mkdir ${MyTempDir}) || (echo "Failed to create temp dir. Aborting" >&2 ; exit 1)
fi
chmod 711 "${MyTempDir}"
trap "rm -rf \"${MyTempDir}\" ; exit 0" 0 1 2 3 15
for file in smartctl.txt gdisk.txt ; do
touch "${MyTempDir}/${file}"
chmod 644 "${MyTempDir}/${file}"
done
} #CreateTempFiles
InstallRPiMonitor() {
# Installs rpimonitord based on the official instructions from
# http://rpi-experiences.blogspot.fr/p/rpi-monitor-installation.html
if [ "$(id -u)" != "0" ]; then
echo "Installing RPi-Monitor requires root privileges, try sudo please. Exiting" >&2
exit 1
fi
echo -e "Installing RPi-Monitor. This can take up to 5 minutes. Be patient please\c"
apt-get -qq -y update
apt-get -f -qq -y install rpimonitor
/usr/share/rpimonitor/scripts/updatePackagesStatus.pl &
} # InstallRPiMonitor
PatchRPiMonitor_for_sun8i() {
echo -e "\nNow patching RPi-Monitor to deal correctly with H3"
cd / && echo "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
BAKBQCAQCAQCgUAgEAgEwjPhPzou6T0AoAAA" | base64 --decode | tar xzf -
which systemctl >/dev/null 2>&1
case $? in
0)
# Jessie
systemctl enable rpimonitor-helper >/dev/null 2>&1
systemctl start rpimonitor-helper >/dev/null 2>&1
systemctl restart rpimonitor >/dev/null 2>&1
;;
*)
# Wheezy|Trusty
insserv rpimonitor-helper >/dev/null 2>&1 || update-rc.d rpimonitor-helper defaults 90 10 >/dev/null 2>&1
cd /tmp && nohup /usr/local/sbin/rpimonitor-helper.sh & >/dev/null 2>&1
/etc/init.d/rpimonitor stop >/dev/null 2>&1
/etc/init.d/rpimonitor start >/dev/null 2>&1
;;
esac
} # PatchRPiMonitor_for_sun8i
CollectSupportInfo() {
cat /var/log/armhwinfo.log
[ -z $SUDO_USER ] || echo -e "\n### Group membership of $(groups $SUDO_USER)"
echo -e "\n### Installed packages:\n\n$(dpkg -l | egrep "armbian| linux-")"
KernelVersion=$(awk -F" " '{print $3}' < /proc/version)
case ${KernelVersion} in
3.*)
[[ -e /boot/script.bin ]] && echo -e "\n### fex settings: $(ls -la /boot/script.bin)\n\n$(bin2fex /boot/script.bin 2>/dev/null)"
;;
esac
echo -e "\n### dmesg now:\n\n$(dmesg | tail -n 250)"
ls /tmp/armbianmonitor_checks_* >/dev/null 2>&1 || return
for file in /tmp/armbianmonitor_checks_* ; do
echo -e "\n### \c"
ls "${file}" | cut -f1 -d.
echo
cat "${file}"
done
} # CollectSupportInfo
CheckCard() {
if [ "$(id -u)" = "0" ]; then
echo "Checking disks is not permitted as root or through sudo. Exiting" >&2
exit 1
fi
if [ ! -d "$1" ]; then
echo "\"$1\" does not exist or is no directory. Exiting" >&2
exit 1
fi
TargetDir="$1"
# check requirements
which f3write >/dev/null 2>&1 || MissingTools=" f3"
which iozone >/dev/null 2>&1 || MissingTools="${MissingTools} iozone3"
if [ "X${MissingTools}" != "X" ]; then
echo "Some tools are missing, please do an \"sudo apt-get -f -y install${MissingTools}\" before and try again" >&2
exit 1
fi
# check provided path
Device="$(GetDevice "$1")"
set ${Device}
DeviceName=$1
FileSystem=$2
echo "${DeviceName}" | grep -q "mmcblk0" || echo -e "\n${BOLD}WARNING:${NC} It seems you're not testing the SD card but instead ${DeviceName} (${FileSystem})\n"
TestDir="$(mktemp -d "${TargetDir}/cardtest.XXXXXX" || exit 1)"
date "+%s" >"${TestDir}/.starttime" || exit 1
trap "rm -rf \"${TestDir}\" ; exit 0" 0 1 2 3 15
LogFile="$(mktemp /tmp/armbianmonitor_checks_${DeviceName##*/}_${FileSystem}.XXXXXX)"
# start actual test, create a small file for some space reserve
fallocate -l 32M "${TestDir}/empty.32m" 2>/dev/null || dd if=/dev/zero of="${TestDir}/empty.32m" bs=1M count=32 status=noxfer >/dev/null 2>&1
ShowWarning=false
# Start writing
echo -e "Starting to fill ${DeviceName} with test patterns, please be patient this might take a very long time"
f3write "${TestDir}" | tee "${LogFile}"
touch "${TestDir}/.starttime" || ShowDeviceWarning
rm "${TestDir}/empty.32m"
# Start verify
echo -e "\nNow verifying the written data:"
echo "" >>"${LogFile}"
f3read "${TestDir}" | tee -a "${LogFile}"
touch "${TestDir}/.starttime" || ShowDeviceWarning
rm "${TestDir}/"*.h2w
echo -e "\nStarting iozone tests. Be patient, this can take a very long time to complete:"
echo "" >>"${LogFile}"
cd "${TestDir}"
iozone -e -I -a -s 100M -r 4k -r 512k -r 16M -i 0 -i 1 -i 2 | tee -a "${LogFile}"
touch "${TestDir}/.starttime" || ShowDeviceWarning
echo -e "\n${BOLD}The results from testing ${DeviceName} (${FileSystem}):${NC}"
egrep "Average|Data" "${LogFile}" | sort -r
echo " random random"
echo -e "reclen write rewrite read reread read write\c"
awk -F"102400 " '/102400/ {print $2}' <"${LogFile}"
# check health
echo -e "\n${BOLD}Health summary: \c"
egrep -q "Read-only|Input/output error" "${LogFile}" && (echo -e "${LRED}${BOLD}${DeviceName} failed${NC}" ; exit 0)
grep -q "Data LOST: 0.00 Byte" "${LogFile}" && echo -e "${LGREEN}OK" || \
(echo -e "${LRED}${BOLD}${DeviceName} failed. Replace it as soon as possible!" ; \
grep -A3 "^Data LOST" "${LogFile}")
# check performance
RandomSpeed=$(awk -F" " '/102400 4/ {print $7"\t"$8}' <"${LogFile}")
if [ "X${RandomSpeed}" != "X" ]; then
# Only continue when we're able to read out iozone results
set ${RandomSpeed}
RandomReadSpead=$1
RandomWriteSpead=$2
ReadSpeed=$(awk -F" " '/Average reading speed/ {print $4"\t"$5}' <"${LogFile}")
set ${ReadSpeed}
if [ "X$2" = "XMB/s" ]; then
RawReadSpead=$(echo "$1 * 1000" | bc -s | cut -f1 -d.)
else
RawReadSpead$(echo "$1" | cut -f1 -d.)
fi
echo -e "\n${NC}${BOLD}Performance summary:${NC}\nSequential reading speed:$(printf "%6s" $1) $2 \c"
[ ${RawReadSpead} -le 2500 ] && Exclamation="${LRED}${BOLD}way " || Exclamation=""
[ ${RawReadSpead} -le 5000 ] && Exclamation="${Exclamation}${BOLD}too "
[ ${RawReadSpead} -le 7500 ] && echo -e "(${Exclamation}low${NC})\c"
echo "${Exclamation}" | grep -q "too" && ShowWarning=true
echo -e "\n 4K random reading speed:$(printf "%6s" ${RandomReadSpead}) KB/s \c"
[ ${RandomReadSpead} -le 700 ] && Exclamation="${LRED}${BOLD}way " || Exclamation=""
[ ${RandomReadSpead} -le 1400 ] && Exclamation="${Exclamation}${BOLD}too "
[ ${RandomReadSpead} -le 2500 ] && echo -e "(${Exclamation}low${NC})\c"
echo "${Exclamation}" | grep -q "too" && ShowWarning=true
WriteSpeed=$(awk -F" " '/Average writing speed/ {print $4"\t"$5}' <"${LogFile}")
set ${WriteSpeed}
if [ "X$2" = "XMB/s" ]; then
RawWriteSpeed=$(echo "$1 * 1000" | bc -s | cut -f1 -d.)
else
RawWriteSpeed=$(echo "$1" | cut -f1 -d.)
fi
echo -e "\nSequential writing speed:$(printf "%6s" $1) $2 \c"
[ ${RawWriteSpeed} -le 2500 ] && Exclamation="${LRED}${BOLD}way " || Exclamation=""
[ ${RawWriteSpeed} -le 4000 ] && Exclamation="${Exclamation}${BOLD}too "
[ ${RawWriteSpeed} -le 6000 ] && echo -e "(${Exclamation}low${NC})\c"
echo "${Exclamation}" | grep -q "too" && ShowWarning=true
echo -e "\n 4K random writing speed:$(printf "%6s" ${RandomWriteSpead}) KB/s \c"
[ ${RandomWriteSpead} -le 400 ] && Exclamation="${LRED}${BOLD}way " || Exclamation=""
[ ${RandomWriteSpead} -le 750 ] && Exclamation="${Exclamation}${BOLD}too "
[ ${RandomWriteSpead} -lt 1000 ] && echo -e "(${Exclamation}low${NC})\c"
echo "${Exclamation}" | grep -q "too" && ShowWarning=true
if [ "X${ShowWarning}" = "Xtrue" ]; then
echo -e "\n\n${BOLD}The device you tested seems to perform too slow to be used with Armbian."
echo -e "This applies especially to desktop images where slow storage is responsible"
echo -e "for sluggish behaviour. If you want to have fun with your device do NOT use"
echo -e "this media to put the OS image or the user homedirs on.${NC}\c"
fi
echo -e "\n\nTo interpret the results above correctly or search for better storage
alternatives please refer to http://oss.digirati.com.br/f3/ and also
http://www.jeffgeerling.com/blogs/jeff-geerling/raspberry-pi-microsd-card
and http://thewirecutter.com/reviews/best-microsd-card/"
fi
} # CheckCard
ShowDeviceWarning() {
echo -e "\n${LRED}${BOLD}Test stopped, read-only filesystem\n\n${NC}${LRED}$(dmesg | grep 'I/O error')"
echo -e "\n${BOLD}Please be careful using this media since it seems it's already broken. Exiting test.\n${NC}"
exit 0
} # ShowDeviceWarning
GetDevice() {
TestPath=$(findmnt "$1" | awk -F" " '/\/dev\// {print $2"\t"$3}')
if [[ -z ${TestPath} && -n "${1%/*}" ]]; then
GetDevice "${1%/*}"
elif [[ -z ${TestPath} && -z "${1%/*}" ]]; then
findmnt / | awk -F" " '/\/dev\// {print $2"\t"$3}'
else
echo "${TestPath}"
fi
} # GetDevice
Main "$@"