#!/bin/bash # # armbianmonitor-daemon # # This script relies on information gathered in armhwinfo. It # calls armhwinfo in query mode and relies then on the exported # variables: HARDWARE ARCH KERNELID MACHINE ID VERSION # # The purpose is to create an environment for rpimonitord so that # relevant hardware informations can be monitored easily. This # script starts uses configuration files in /etc/armbianmonitor/, # collects data sources below /etc/armbianmonitor/datasources/ and # adjusts templates for RPi-Monitor on the fly. Only if the file # /etc/armbianmonitor/start-monitoring exists this script relinks # /etc/rpimonitor/data.conf and starts rpimonitord if not already # running. # # In case the script detects that not all necessary data sources # are available through sysfs it will act as a daemon to collect # data on its own and to write it to the approriate file. As an # example the SoC's temperature: Running on an AXP209 based board # the script will check /sys/class/thermal/thermal_zone0/temp and # if existing link it to /etc/armbianmonitor/datasources/soctemp. # In case it does not exist the script will create a normal file # at this location and writes the thermal value to it using the # sunxi_axp209_temp binary in an endless loop. # # At least the following files/symlinks will be provided below # /etc/armbianmonitor/datasources/ depending on SoC/PMIC in question: # # soctemp (SoC's internal temp in degree Celsius * 1000) # pmictemp (PMIC's internal temp in degree Celsius * 1000) # ac_voltage (DC-IN voltage in V * 1000000) # usb_voltage (USB OTG voltage in V * 1000000) # battery_voltage (battery voltage in V * 1000000) # ac_current (DC-IN current in A * 1000000) # usb_current (USB OTG current in A * 1000000) # battery_current (battery current in A * 1000000) # # When extended debugging has been chosen (using _armbian-monitoring_ # this can be configured. Then /etc/armbianmonitor/start-monitoring # contains DEBUG) this script will also provide a few more files: # # cpustat (cpu_stat,system_stat,user_stat,nice_stat,iowait_stat,irq_stat # collected through /proc/cpustat in daemon mode) # cpu_count (number of active CPU cores) # vcorevoltage (Vcore in V * 1000 based on sysfs or script.bin/.dtb) # # Disk monitoring: For configured disks the following parameters can be # monitored: temperature, S.M.A.R.T. health, load cycle count and CRC # errors indicating connection/cable problems. The config file used is # /etc/armbianmonitoring/disks.conf # # Filesystem monitoring: In /etc/armbianmonitoring/filesystems.conf # mountpoints and trigger values can be defined that will be used to # create the template stuff for these fs at startup of this script. # # The behaviour of this script can be configured through another tool # called armbian-monitoring. The latter will also check for connected # disks, get their name and GUID and let the user choose whether the # disk should be monitored or not. The information has to be stored in # /etc/armbianmonitor/disks.conf relying on the GUIDs of the disks. # define some variables: CheckInterval=7.5 # time in seconds between two checks DiskCheckInterval=60 # time in seconds between disk checks Main() { PreRequisits case ${BOARD_NAME} in Cubieboard|Cubietruck|Orange|"Lamobo R1"|"Lime 2"|Lime|"Banana Pi"|Micro|"Banana Pi"|"Banana Pi Pro") DealWithAXP209 ;; "Banana M2") DealWithNewBoard # DealWithAXP221 ;; "Cubietruck Plus"|"Banana Pi M3"|"pcDuino8 Uno") DealWithNewBoard # DealWithAXP818 ;; Guitar|"Roseapple Pi") DealWithNewBoard # DealWithS500 ;; "Odroid XU4"|"Odroid XU3") DealWithNewBoard # DealWithExynos4 ;; "Odroid C1") DealWithNewBoard # DealWithS805 ;; Clearfog|"Turris Omnia") DealWithNewBoard # DealWithArmada38x ;; "Cubox i4"|"HB i2eX"|"Cubox i2eX"|"HB i1"|"HB i2"|"Wandboard") DealWithNewBoard # DealWithiMX6 ;; "Orange Pi PC"|"Orange Pi Plus"|"Orange Pi 2"|"Orange Pi One"|"Orange Pi Lite") DealWithNewBoard # DealWithH3 ;; Geekbox) DealWithNewBoard # DealWithRK3368 ;; *) # No templates exist now. Combine sane defaults with some guessing DealWithNewBoard ;; esac # Create the Armbian templates CreateTemplates exit 0 # Decide depending on existence of /etc/armbianmonitor/start-monitoring ShouldMonitoringBeStarted # Provide missing data in daemon mode LoopEndlessly } # Main LoopEndlessly() { while true ; do # get VCore read CPUFreq /tmp/VCore # check disk temperature(s). We execute this only every ${DiskCheckInterval} since # it's a bit costly (S.M.A.R.T. queries). TimeNow=$(( $(date "+%s") / ${DiskCheckInterval} )) if [[ ${TimeNow} -gt ${LastDiskCheck} ]]; then # time for a disk check. If ${CheckAllDisks} is FALSE and /dev/sda exists we # only query this device otherwise all available (might be none) CheckDisks # update check timestamp LastDiskCheck=${TimeNow} fi # External temperature from weather stations # TimeNow=$(( $(date "+%s") / ${TempCheckInterval} )) # if [[ ${TimeNow} -gt ${LastTempCheck} ]]; then # read in external temp values from 2 different web sources # ExternalTemp=$(GetExternalTemp) # LastExternalTemp=$(SanitizeValue ${ExternalTemp} ${LastExternalTemp} | tee /tmp/externaltemp) # LastTempCheck=${TimeNow} # fi # cpustat TimeNow=$(( $(date "+%s") / ${CpuStatCheckInterval} )) if [[ ${TimeNow} -gt ${LastCpuStatCheck} ]]; then ProcessStats LastCpuStatCheck=${TimeNow} fi sleep ${CheckInterval} done } # LoopEndlessly CheckDisks() { # To be done based on new /etc/armbianmonitoring/disks.conf config file : } # CheckDisks DealWithAXP209() { # check existence of sysfs nodes if [ -f /sys/devices/virtual/thermal/thermal_zone0/temp ]; then ln -fs /sys/devices/virtual/thermal/thermal_zone0/temp /etc/armbianmonitor/datasources/soctemp else if [ -L /etc/armbianmonitor/datasources/soctemp ]; then rm -f /etc/armbianmonitor/datasources/soctemp fi echo -n 25000 >/etc/armbianmonitor/datasources/soctemp export GetSoCTemp="${ARCH}-${BOARD_NAME}" fi if [ -d /sys/devices/platform/soc@01c00000/1c2ac00.i2c/i2c-0/0-0034 ]; then # mainline kernel and 'axp209 mainline sysfs interface' patch applied ln -fs /sys/power/axp_pmu/ac/voltage /etc/armbianmonitor/datasources/ac_voltage ln -fs /sys/power/axp_pmu/ac/amperage /etc/armbianmonitor/datasources/ac_current ln -fs /sys/power/axp_pmu/vbus/voltage /etc/armbianmonitor/datasources/usb_voltage ln -fs /sys/power/axp_pmu/vbus/amperage /etc/armbianmonitor/datasources/usb_current ln -fs /sys/power/axp_pmu/battery/voltage /etc/armbianmonitor/datasources/battery_voltage ln -fs /sys/power/axp_pmu/battery/amperage /etc/armbianmonitor/datasources/battery_current ln -fs /sys/power/axp_pmu/pmu/temp /etc/armbianmonitor/datasources/pmictemp ln -fs /sys/power/axp_pmu/battery/capacity /etc/armbianmonitor/datasources/battery_percent ln -fs /sys/power/axp_pmu/battery/charging /etc/armbianmonitor/datasources/battery_charging ln -fs /sys/power/axp_pmu/charger/amperage /etc/armbianmonitor/datasources/charger_current ln -fs /sys/power/axp_pmu/battery/connected /etc/armbianmonitor/datasources/battery_connected ln -fs /sys/power/axp_pmu/battery/charge /etc/armbianmonitor/datasources/battery_charge fi if [ -d /sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply ]; then # sunxi 3.4 kernel SysFSPrefix=/sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/axp20-supplyer.28/power_supply ln -fs "${SysFSPrefix}"/ac/voltage_now /etc/armbianmonitor/datasources/ac_voltage ln -fs "${SysFSPrefix}"/ac/current_now /etc/armbianmonitor/datasources/ac_current ln -fs "${SysFSPrefix}"/usb/voltage_now /etc/armbianmonitor/datasources/usb_voltage ln -fs "${SysFSPrefix}"/usb/current_now /etc/armbianmonitor/datasources/usb_current ln -fs "${SysFSPrefix}"/battery/voltage_now /etc/armbianmonitor/datasources/battery_voltage ln -fs "${SysFSPrefix}"/battery/current_now /etc/armbianmonitor/datasources/battery_current ln -fs /sys/devices/platform/sunxi-i2c.0/i2c-0/0-0034/temp1_input /etc/armbianmonitor/datasources/pmictemp ln -fs /sys/class/power_supply/battery/capacity /etc/armbianmonitor/datasources/battery_percent ln -fs /sys/class/power_supply/battery/charging /etc/armbianmonitor/datasources/battery_charging ln -fs /sys/class/power_supply/battery/connected /etc/armbianmonitor/datasources/battery_connected ln -fs /sys/class/power_supply/battery/charge /etc/armbianmonitor/datasources/battery_charge fi # relink template if [ "X${DebugMode}" = "XDEBUG" ]; then ln -sf /etc/armbianmonitor/templates/axp209_template_debug.conf /etc/armbianmonitor/templates/cpu_pmic.conf else ln -sf /etc/armbianmonitor/templates/axp209_template.conf /etc/armbianmonitor/templates/cpu_pmic.conf fi } # DealWithAXP209 DealWithNewBoard() { # check existence of sysfs nodes if [ -f /sys/devices/virtual/thermal/thermal_zone0/temp ]; then ln -fs /sys/devices/virtual/thermal/thermal_zone0/temp /etc/armbianmonitor/datasources/soctemp elif [ -f /sys/devices/virtual/thermal/thermal_zone1/temp ]; then ln -fs /sys/devices/virtual/thermal/thermal_zone1/temp /etc/armbianmonitor/datasources/soctemp fi # relink template if [ "X${DebugMode}" = "XDEBUG" ]; then ln -sf /etc/armbianmonitor/templates/unknown_board_template_debug.conf /etc/armbianmonitor/templates/cpu_pmic.conf else ln -sf /etc/armbianmonitor/templates/unknown_board_template.conf /etc/armbianmonitor/templates/cpu_pmic.conf fi } # DealWithNewBoard CreateTemplates() { # check whether templates we write aren't symlinks to somewhere else for i in armbian.conf uptime.conf version.conf ; do if [ -L /etc/armbianmonitor/templates/${i} ]; then rm /etc/armbianmonitor/templates/${i} touch /etc/armbianmonitor/templates/${i} chmod 711 /etc/armbianmonitor/templates/${i} fi done # check whether our logo is available if [ ! -f /usr/share/rpimonitor/web/img/armbian.png ]; then if [ -L /usr/share/rpimonitor/web/img/armbian.png ]; then rm /usr/share/rpimonitor/web/img/armbian.png fi cp -p /etc/armbianmonitor/templates/armbian.png /usr/share/rpimonitor/web/img/ fi # create main template echo "web.page.icon='img/armbian.png' web.page.menutitle='RPi-Monitor ('+data.hostname+')' web.page.pagetitle='RPi-Monitor ('+data.hostname+')' web.status.1.name=$BOARD_NAME web.statistics.1.name=$BOARD_NAME web.addons.1.name=Addons web.addons.1.addons=about include=/etc/armbianmonitor/templates/version.conf include=/etc/armbianmonitor/templates/uptime.conf include=/etc/armbianmonitor/templates/cpu_pmic.conf include=/etc/rpimonitor/template/memory.conf" >/etc/armbianmonitor/armbian.conf # remove firmware line in version info: grep -v "Firmware" /etc/rpimonitor/template/version.conf | sed 's|line.5|line.4|' >/etc/armbianmonitor/templates/version.conf # uptime template with correct machine name: sed "s/Raspberry Pi/$ID/" < /etc/rpimonitor/template/uptime.conf >/etc/armbianmonitor/templates/uptime.conf # check swap settings. In case we're using swap then add template HowManySwapDevices=$(swapon -s | wc -l) if [[ ${HowManySwapDevices} -gt 1 ]]; then echo "include=/etc/rpimonitor/template/swap.conf" >>/etc/armbianmonitor/armbian.conf fi echo "include=/etc/armbianmonitor/template/filesystems.conf include=/etc/armbianmonitor/template/disks.conf include=/etc/rpimonitor/template/network.conf" >>/etc/armbianmonitor/armbian.conf UpdateFileSystems UpdateDisks } # UpdateFileSystems() { # Generates /etc/armbianmonitor/template/filesystems.conf dynamically # based on the contents of /etc/armbianmonitoring/filesystems.conf # if not existing, create config file with single entry for / if [ ! -f /etc/armbianmonitoring/filesystems.conf ]; then echo '/' >/etc/armbianmonitoring/filesystems.conf chmod 711 /etc/armbianmonitoring/filesystems.conf fi # Update template: } # UpdateFileSystems UpdateDisks() { # Generates /etc/armbianmonitor/template/disks.conf dynamically # based on the contents of /etc/armbianmonitor/disks.conf. The # current mapping between GUIDs and /dev/sd* nodes will be stored # in /etc/armbianmonitor/datasources/disk-by-guid # ensure lookup file is empty: if [ -L /etc/armbianmonitor/datasources/disk-by-guid ]; then rm -f /etc/armbianmonitor/datasources/disk-by-guid fi echo -n "" >/etc/armbianmonitor/datasources/disk-by-guid chmod 711 /etc/armbianmonitor/datasources/disk-by-guid OIFS=${IFS} IFS=: cat /etc/armbianmonitor/disks.conf | while read ; do IFS=: set ${REPLY} GUID="$1" DiskName="$2" SMARTPrefix="$3" TempCommand="$4" CRCAttribute="$5" LCCAttribute="$6" IFS=${OIFS} # try to resolve device node (when not present, then disk's # not mounted -- we then create the entry without device node) DeviceNode="$(ResolveGUID ${GUID})" echo -e "${GUID}\t${DiskName}\t${DeviceNode}" >>/etc/armbianmonitor/datasources/disk-by-guid # create template stuff for every listed disk : done IFS=${OIFS} } # UpdateDisks ResolveGUID() { # function that will be supplied with a GUID and returns the device node, eg. # translating 637B7677-18E7-4C7B-8DF3-CFED96EA55C3 to /dev/sdb # check whether disks are existent ls /sys/block/sd* >/dev/null 2>&1 || return for i in /sys/block/sd* ; do DeviceNode=/dev/${i##*/} GUID=$(gdisk -l ${DeviceNode} | awk -F" " '/^Disk identifier/ {print $4}') if [ "X${GUID}" = "X${1}" ]; then echo -n ${DeviceNode} break fi done } # GetGUIDforDisk PreRequisits() { export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin unset LANG LastDiskCheck=0 # we need the informations gathered from armhwinfo. In case we're not called # from there, get the variables on our own if [ "X${BOARD_NAME}" = "X" ]; then . /etc/init.d/armhwinfo start >/dev/null 2>&1 fi # check/create /etc/armbianmonitor and /etc/armbianmonitor/datasources if [ ! -d /etc/armbianmonitor ]; then mkdir -p -m 755 /etc/armbianmonitor else chmod 755 /etc/armbianmonitor fi if [ ! -d /etc/armbianmonitor/datasources ]; then mkdir -p -m 755 /etc/armbianmonitor/datasources else chmod 755 /etc/armbianmonitor/datasources fi # check whether we should do debug monitoring or normal read DebugMode /dev/null | \ grep -E "^LV._|^LV_|extrem|boot_clock|_freq|^dram_" | \ grep -Ev "cpu_freq|dram_freq" | while read ; do echo "# ${REPLY}" done >/tmp/dvfs-table echo -e '\nGetVCore() {' >>/tmp/dvfs-table # parse /tmp/dvfs-table to get dvfs entries grep "^# LV._freq" /tmp/dvfs-table | sort -r | while read ; do set ${REPLY} CPUFreq=$4 # if [ ${CPUFreq} -eq 0 ]; then # echo -e "if [ \$1 -ge $(( ${CPUFreq} / 1000 )) ]; then\n\techo -n ${VCore}\nel\c" >>/tmp/dvfs-table # break # else # VCore=$(grep -A1 "^# $2" /tmp/dvfs-table | tail -n1 | awk -F" " '{print $4}') # echo -e "if [ \$1 -ge $(( ${CPUFreq} / 1000 )) ]; then\n\techo -n ${VCore}\nel\c" >>/tmp/dvfs-table if [ ${CPUFreq} -ne 0 ]; then VCore=$(grep -A1 "^# $2" /tmp/dvfs-table | tail -n1 | awk -F" " '{print $4}') echo -e "if [ \$1 -le $(( ${CPUFreq} / 1000 )) ]; then\n\techo -n ${VCore}\nel\c" >>/tmp/dvfs-table fi done # VCore=$(grep -A1 "^# LV1_freq" /tmp/dvfs-table | tail -n1 | awk -F" " '{print $4}') echo -e "se\n\techo -n ${VCore}\nfi\n}" >>/tmp/dvfs-table } # ParseDVFSTable ProcessStats() { set $(awk -F" " '/^cpu / {print $2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7"\t"$8}' /tmp/cpustat LastUserStat=${UserStat} LastNiceStat=${NiceStat} LastSystemStat=${SystemStat} LastIdleStat=${IdleStat} LastIOWaitStat=${IOWaitStat} LastIrqStat=${IrqStat} LastSoftIrqStat=${SoftIrqStat} } # ProcessStats GetExternalTemp() { # example function that parses meteo.physik.uni-muenchen.de and mingaweda.de # temperature values for Munich and compares them. When values are out # of bounds then only the other value will be returned otherwise the average ExternalTemp1=$(/usr/bin/links -http.fake-user-agent 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12' -dump "http://www.meteo.physik.uni-muenchen.de/dokuwiki/doku.php?id=wetter:stadt:messung" | awk -F" " '/Lufttemperatur/ {printf ("%0.0f",$4*1000); }') ExternalTemp2=$(/usr/bin/links -http.fake-user-agent 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12' -dump "http://www.mingaweda.de/wetterdaten/" | awk -F" " '/Ausfu:hrliche/ {printf ("%0.0f",$2*1000); }') if [ "X${ExternalTemp2}" = "X" ]; then ExternalTemp2=${ExternalTemp1} elif [ "X${ExternalTemp1}" = "X" ]; then ExternalTemp1=${ExternalTemp2} fi echo $(( ( ${ExternalTemp1} + ${ExternalTemp2} ) / 2 )) } # GetExternalTemp Main