#!/bin/sh

###### Parameters ######
logfile="/tmp/smart_report.tmp"
email="exampleEmail@gmail.com"
subject="Status report for my NAS"
drives="da0 da1 da2 da3 da4 da5 da6 da7 da8 da9 da10 da11 da12 da13 da14 da15"
pools="freenas-boot tank"
tempWarn=40
tempCrit=45
sectorsCrit=10
testAgeWarn=5
usedWarn=85
usedCrit=90
scrubAgeWarn=30
warnSymbol="?"
critSymbol="!"


###### Config Backup Parameters ######
config_logfile="/tmp/config_backup_error.tmp"
tarfile="/tmp/config_backup.tar"
filename="$(date "+FreeNAS_Config_%Y-%m-%d_%H-%M-%S")"
config_subject="Config backup for my NAS"

###### Set email headers ######
(
    echo "To: ${email}"
    echo "Subject: ${subject}"
    echo "Content-Type: text/html"
    echo "MIME-Version: 1.0"
    echo -e "\r\n"
) > "$logfile"


###### Set email body ######
echo "<pre style=\"font-size:14px\">" >> "$logfile"


###### Check config integrity ######
if ! [ "$(sqlite3 /data/freenas-v1.db "pragma integrity_check;")" == "ok" ]; then
	### Send error message ###
    (
        echo "<b>Automatic backup of FreeNAS config failed! The config file is corrupted!</b>"
        echo "<b>You should correct this problem as soon as possible!</b>"
    ) >> "${logfile}"
fi


###### zpool status summary ######
(
    echo "<b>########## ZPool status report summary for all pools ##########</b>"
    echo ""
    echo "+--------------+--------+------+------+------+----+--------+------+-----+"
    echo "|Pool Name     |Status  |Read  |Write |Cksum |Used|Scrub   |Scrub |Last |"
    echo "|              |        |Errors|Errors|Errors|    |Repaired|Errors|Scrub|"
    echo "|              |        |      |      |      |    |Bytes   |      |Age  |"
    echo "+--------------+--------+------+------+------+----+--------+------+-----+"
) >> "$logfile"
for pool in $pools; do
    status="$(zpool list -H -o health "$pool")"
    errors="$(zpool status "$pool" | egrep "(ONLINE|DEGRADED|FAULTED|UNAVAIL|REMOVED)[ \t]+[0-9]+")"
   readErrors=0
   for err in $(echo "$errors" | awk '{print $3}'); do
       if echo "$err" | egrep -q "[^0-9]+"; then
           readErrors=1000
           break
       fi
       readErrors=$((readErrors + err))
   done
   writeErrors=0
   for err in $(echo "$errors" | awk '{print $4}'); do
       if echo "$err" | egrep -q "[^0-9]+"; then
           writeErrors=1000
           break
       fi
       writeErrors=$((writeErrors + err))
   done
   cksumErrors=0
   for err in $(echo "$errors" | awk '{print $5}'); do
       if echo "$err" | egrep -q "[^0-9]+"; then
           cksumErrors=1000
           break
       fi
       cksumErrors=$((cksumErrors + err))
   done
   if [ "$readErrors" -gt 999 ]; then readErrors=">1K"; fi
   if [ "$writeErrors" -gt 999 ]; then writeErrors=">1K"; fi
   if [ "$cksumErrors" -gt 999 ]; then cksumErrors=">1K"; fi
   used="$(zpool list -H -p -o capacity "$pool")"
   scrubRepBytes="N/A"
   scrubErrors="N/A"
   scrubAge="N/A"
   if [ "$(zpool status "$pool" | grep "scan" | awk '{print $2}')" = "scrub" ]; then
       scrubRepBytes="$(zpool status "$pool" | grep "scan" | awk '{print $4}')"
       scrubErrors="$(zpool status "$pool" | grep "scan" | awk '{print $8}')"
       scrubDate="$(zpool status "$pool" | grep "scan" | awk '{print $15"-"$12"-"$13"_"$14}')"
       scrubTS="$(date -j -f "%Y-%b-%e_%H:%M:%S" "$scrubDate" "+%s")"
       currentTS="$(date "+%s")"
       scrubAge=$((((currentTS - scrubTS) + 43200) / 86400))
   fi
   if [ "$status" = "FAULTED" ] \
   || [ "$used" -gt "$usedCrit" ] \
   || ( [ "$scrubErrors" != "N/A" ] && [ "$scrubErrors" != "0" ] )
   then
       symbol="$critSymbol"
   elif [ "$status" != "ONLINE" ] \
   || [ "$readErrors" != "0" ] \
   || [ "$writeErrors" != "0" ] \
   || [ "$cksumErrors" != "0" ] \
   || [ "$used" -gt "$usedWarn" ] \
   || [ "$scrubRepBytes" != "0" ] \
   || [ "$(echo "$scrubAge" | awk '{print int($1)}')" -gt "$scrubAgeWarn" ]
   then
       symbol="$warnSymbol"
   else
       symbol=" "
   fi
   (
       printf "|%-12s %1s|%-8s|%6s|%6s|%6s|%3s%%|%8s|%6s|%5s|\n" \
       "$pool" "$symbol" "$status" "$readErrors" "$writeErrors" "$cksumErrors" \
       "$used" "$scrubRepBytes" "$scrubErrors" "$scrubAge"
   ) >> "$logfile"
done
(
   echo "+--------------+--------+------+------+------+----+--------+------+-----+"
   echo ""
   echo ""
) >> "$logfile"


###### SMART status summary ######
(
    echo "<b>########## SMART status report summary for all drives ##########</b>"
    echo ""
    echo "+------+---------------+----+-----+-----+-----+-------+-------+--------+------+------+------+-------+----+"
    echo "|Device|Serial         |Temp|Power|Start|Spin |ReAlloc|Current|Offline |UDMA  |Seek  |High  |Command|Last|"
    echo "|      |               |    |On   |Stop |Retry|Sectors|Pending|Uncorrec|CRC   |Errors|Fly   |Timeout|Test|"
    echo "|      |               |    |Hours|Count|Count|       |Sectors|Sectors |Errors|      |Writes|Count  |Age |"
    echo "+------+---------------+----+-----+-----+-----+-------+-------+--------+------+------+------+-------+----+"
) >> "$logfile"
for drive in $drives
do
    (
        smartctl -A -i -v 7,hex48 /dev/"$drive" | \
        awk -v device="$drive" -v tempWarn="$tempWarn" -v tempCrit="$tempCrit" -v sectorsCrit="$sectorsCrit" \
        -v testAgeWarn="$testAgeWarn" -v warnSymbol="$warnSymbol" -v critSymbol="$critSymbol" \
        -v lastTestHours="$(smartctl -l selftest /dev/"$drive" | grep "# 1" | awk '{print $9}')" '\
       /Serial Number:/{serial=$3} \
       /Temperature_Celsius/{temp=$10} \
       /Power_On_Hours/{onHours=$10} \
       /Start_Stop_Count/{startStop=$10} \
       /Spin_Retry_Count/{spinRetry=$10} \
       /Reallocated_Sector/{reAlloc=$10} \
       /Current_Pending_Sector/{pending=$10} \
       /Offline_Uncorrectable/{offlineUnc=$10} \
       /UDMA_CRC_Error_Count/{crcErrors=$10} \
       /Seek_Error_Rate/{seekErrors=("0x" substr($10,3,4));totalSeeks=("0x" substr($10,7))} \
       /High_Fly_Writes/{hiFlyWr=$10} \
       /Command_Timeout/{cmdTimeout=$10} \
       END {
           testAge=sprintf("%.0f", (onHours - lastTestHours) / 24);
           if (temp > tempCrit || reAlloc > sectorsCrit || pending > sectorsCrit || offlineUnc > sectorsCrit)
               device=device " " critSymbol;
           else if (temp > tempWarn || reAlloc > 0 || pending > 0 || offlineUnc > 0 || testAge > testAgeWarn)
               device=device " " warnSymbol;
           seekErrors=sprintf("%d", seekErrors);
           totalSeeks=sprintf("%d", totalSeeks);
           if (totalSeeks == "0") {
               seekErrors="N/A";
               totalSeeks="N/A";
           }
           if (hiFlyWr == "") hiFlyWr="N/A";
           if (cmdTimeout == "") cmdTimeout="N/A";
           printf "|%-6s|%-15s| %s |%5s|%5s|%5s|%7s|%7s|%8s|%6s|%6s|%6s|%7s|%4s|\n",
           device, serial, temp, onHours, startStop, spinRetry, reAlloc, pending, offlineUnc, \
           crcErrors, seekErrors, hiFlyWr, cmdTimeout, testAge;
       }'
    ) >> "$logfile"
done
(
    echo "+------+---------------+----+-----+-----+-----+-------+-------+--------+------+------+------+-------+----+"
    echo ""
    echo ""
) >> "$logfile"


###### zpool status for each pool ######
for pool in $pools; do
   (
       echo ""
       echo "<b>########## ZPool status report for ${pool} ##########</b>"
        echo ""
        zpool status -v "$pool"
        echo ""
        echo ""
    ) >> "$logfile"
done


###### SMART status for each drive ######
for drive in $drives
do
    brand="$(smartctl -i /dev/"$drive" | grep "Model Family" | awk '{print $3, $4, $5}')"
    serial="$(smartctl -i /dev/"$drive" | grep "Serial Number" | awk '{print $3}')"
    (
        echo ""
        echo "<b>########## SMART status report for ${drive} drive (${brand}: ${serial}) ##########</b>"
        smartctl -H -A -l error /dev/"$drive"
        smartctl -l selftest /dev/"$drive" | grep "# 1 \|Num" | cut -c6-
        echo ""
        echo ""
    ) >> "$logfile"
done
sed -i '' -e '/smartctl 6.3/d' "$logfile"
sed -i '' -e '/Copyright/d' "$logfile"
sed -i '' -e '/=== START OF READ/d' "$logfile"
sed -i '' -e '/SMART Attributes Data/d' "$logfile"
sed -i '' -e '/Vendor Specific SMART/d' "$logfile"
sed -i '' -e '/SMART Error Log Version/d' "$logfile"


###### End body ######
echo "</pre>" >> "$logfile"


###### Send backup & report ######
### Send config backup ###
if [ "$(sqlite3 /data/freenas-v1.db "pragma integrity_check;")" == "ok" ]; then
    cp /data/freenas-v1.db "/tmp/${filename}.db"
    md5 "/tmp/${filename}.db" > /tmp/config_backup.md5
    sha256 "/tmp/${filename}.db" > /tmp/config_backup.sha256
    cd "/tmp/"; tar -cf "${tarfile}" "./${filename}.db" ./config_backup.md5 ./config_backup.sha256; cd -
    uuencode "${tarfile}" "${filename}.tar" | mail -s "${config_subject}" "${email}"
    rm "/tmp/${filename}.db"
    rm /tmp/config_backup.md5
    rm /tmp/config_backup.sha256
    rm "${tarfile}"
fi
### Send report ###
sendmail -t < "$logfile"
rm "$logfile"
