#!/bin/bash ###### ZPool & SMART status report with FreeNAS/TrueNAS config backup ### Original script by joeschmuck, modified by Bidelu0hm, then by melp (me) ### At a minimum, enter email address in user-definable parameter section. Feel free to edit other user parameters as needed. ### If you find any errors, feel free to contact me on the FreeNAS forums (username melp) or email me at jason at jro dot io. ### Version: v1.3 TrueNAS Scale (Jeff Alperin 12-6-21) ### Version v1.4c & v1.4d FreeNAS/TrueNAS (joeschmuck 22 June 2022) ### Changelog: # v1.4d: # - Fixed Scrub In Progress nuisance error when a scrub is in progress. # - Added offsetting Reallocated Sectors for four drives. This should be for testing only. Any drives # -- with a significant number of bad sectors should be replaced, not used to store important data. # - Added Drive Warranty Expiration warning messages and ability to disable the Email Subject line warning. # - Added HDD and SSD individual temperature settings. # -- NOT TESTED ON OTHER THAN U.S. FORMATTED DATE YYYY-MM-DD. # - Changed order of pulling Temperature data from HDD/SSD. # # v1.4c: # - Rewrite to create functions and enable easier editing. # - Added Custom Reports. # - Added disabling the RAW 'smartctl -a' data appened to the end of the email. # - Added sorting drives alphabetically vice the default the OS reports them. # - Added RED warning in Device for any single failure in the summary (deviceRedFlag switch controlled). # - Added some additional SSD definitions. # - Fixed sorting last two SMART Tests, now reports them in proper order. # - Fixed detecting "SMART Support is: Enabled", for white spaces. # - Changed IGNORE DRIVES to a String Format to clean up and simplify programming. # - Added MultiZone_Errors support for up to eight drives. # - Added sectorWarn variable to complement the sectorCrit variable. # - Added ignoreSeekError variable to ignore some of those wild Seek Error Rate values. # - Added ignoreUDMA CRC Errors due to the "Known Problem" # - Fixed md5/sha256 error on TrueNAS Scale (only used during config backups). # - Added selectable config backup periodicity by day vice every run. # - Added Exporting statistical data for trend analysis. # -- Can be setup to email statistics weekly, monthly, or not at all. # -- The -s switch will run Data Collection Only, no email generated. Note: Do Not run two instances at once, the temp files do not survive. # - Fixed the Capacity to remove the brackets "[]", thanks Jeff Alperin. # - Fixed Scrub Age failure due to 1 day or longer repair time, now shows anything >24 hours. # # - Known Problem: One user reported UDMA_CRC_ERRORS is not subtracting correctly, have not been able to personnaly replicate it. # -- This error seems to occur around line #1027 # # v1.4b: # - Added SMART test reamining percentage if Last Test has a SMART Test is in progress. # - Fix for empty SMART fields, typically for unsupported SSD's. # - Added IGNORE SMART Drive so you can ignore specific drives that may cause you wierd readings. # --- Updated so blank SSD table header is removed when you ignore all the drives (just crazy talk). # v1.4a: # - Fixed report errors for if a SCRUB is in progress, now shows estimated completion time. # - Fixed report error for a Canceled SCRUB. # - Fixed FreeBSD/Linux use for SCRUB report (minor oversight). # v1.4: # - Run on CRON JOB using /path/multi_report_v1.4.sh # - Fixed for automatic running between FreeBSD and Linux Debian (aka SCALE) as of this date. # - All SMART Devices will report. # - Added conditional Subject Line (Good/Critical/Warning). # - Added Automatic SSD Support. # --- Some updates may need to be made to fit some of SSD's. Code in the area of about line 530 will # --- need to be adjusted to add new attributes for the desired SSD's fields. # - UDMA_CRC_ERROR Override because once a drive encounters this type of error, it cannot be cleared # --- so you can offset it now vice having an alarm condition for old UDMA_CRC_Errors. # - Added listing NON-SMART Supported Drives. Use only if useful to you, some drives will # --- still output some relevant data, many will not. # v1.3: # - Added scrub duration column # - Fixed for FreeNAS 11.1 (thanks reven!) # - Fixed fields parsed out of zpool status # - Buffered zpool status to reduce calls to script # v1.2: # - Added switch for power-on time format # - Slimmed down table columns # - Fixed some shellcheck errors & other misc stuff # - Added .tar.gz to backup file attached to email # - (Still coming) Better SSD SMART support # v1.1: # - Config backup now attached to report email # - Added option to turn off config backup # - Added option to save backup configs in a specified directory # - Power-on hours in SMART summary table now listed as YY-MM-DD-HH # - Changed filename of config backup to exclude timestamp (just uses datestamp now) # - Config backup and checksum files now zipped (was just .tar before; now .tar.gz) # - Fixed degrees symbol in SMART table (rendered weird for a lot of people); replaced with a * # - Added switch to enable or disable SSDs in SMART table (SSD reporting still needs work) # - Added most recent Extended & Short SMART tests in drive details section (only listed one before, whichever was more recent) # - Reformatted user-definable parameters section # - Added more general comments to code # v1.0: # - Initial release ######### INSTRUCTIONS ON USE OF THIS SCRIPT # # This script will perform two main functions: 1: Generate a report send to email on your drive status, # and 2: Create a copy of your Config File and attach to the same email. # # In order to configure the script properly read over the User Definable Section before making any changes. # Then make changes as indicted by the section instructions. # # To run the program from the command line, use ./program_name.sh # Also you need to make it executable using "chmod +x program_name.sh" # # Enjoy ###### User-definable Parameters # Enter your email address to send the report to. The from address does not need to be changed. ###### Email Address email="YourEmail@Address.com" from="TrueNAS@local.com" ###### REPORT HEADER TITLE ###### HDDreportTitle="Spinning Rust Summary Report" # This is the title of the HDD report, change as you desire. SSDreportTitle="SSD Summary Report" # This is the title of the SSD report, change as you desire. ### CUSTOM REPORT CONFIGURATION ### # By default all items are selected. Change the item to "true" to have it displayed, "false" to not have it displayed. # NOTE: Alarm setpoints are not affected by these settings. # For Hard Drive Section HDD_Device_ID="true" HDD_Serial_Number="true" HDD_Model_Number="true" HDD_Capacity="true" HDD_Rotational_Rate="true" HDD_SMART_Status="true" HDD_Drive_Temp="true" HDD_Power_On_Hours="true" HDD_Start_Stop_Count="true" HDD_Load_Cycle="true" HDD_Spin_Retry="true" HDD_Reallocated_Sectors="true" HDD_Reallocated_Events="true" HDD_Pending_Sectors="true" HDD_Offline_Uncorrectable="true" HDD_UDMA_CRC_Errors="true" HDD_Seek_Error_Rate="true" HDD_MultiZone_Errors="true" HDD_Last_Test_Age="true" HDD_Last_Test_Type="true" # For Solid State Drive Section SSD_Device_ID="true" SSD_Serial_Number="true" SSD_Model_Number="true" SSD_Capacity="true" SSD_SMART_Status="true" SSD_Drive_Temp="true" SSD_Power_On_Hours="true" SSD_Wear_Level="true" SSD_Reallocated_Sectors="true" SSD_Reallocated_Events="true" SSD_Pending_Sectors="true" SSD_Offline_Uncorrectable="true" SSD_UDMA_CRC_Errors="true" SSD_Last_Test_Age="true" SSD_Last_Test_Type="true" ###### Drive Ignore List # Use this section to list any drives to ignore and remove from the report. This is useful for ignoring USB Flash Drives. # We are using a comma delimited file to identify the drive serial numbers. You MUST use the exact and full serial number # smartctl reports, if there is no identical match then it doesn't work properly. # # Format: IGNOREDRIVES="DRIVE_SERIAL_NUMBER,DRIVE_SERIAL_NUMBER" # # Example: IGNOREDRIVES="1GVD84B,1JUMLBD,WD-WXQ1E36C19XC" IGNOREDRIVES="2X1J90CA48799" ###### Drive UDMA_CRC_Error_Count Flags # List each drive by serial number and include the current UDMA_CRC_Error_Count value. # If the count exceeds the count in the table below then a warning message will be generated. # On the Status Report the UDMA CRC Errors block will be YELLOW with a value of "0" for an overridden value, for your indication. # # Format: UCRC1SN = Drive Serial Number reported by SMART, UCRC1CNT = Current UDMA Error Count you want to subtract from this drive. # You may add up to 8 drives with the current code. More drives could be added with a simple modification of the program. # Do not list your drives with a value of "0" or each one will be Yellow. Yikes! # # Example (drive has 2 UDMA CRC Errors): UCRC1SN="S2X1J90CA48799" # UCRC1CNT=2 # Drives "2011E2940534" and "2011E2940536" are hard coded at lines 1057 through 1060. UCRC1SN="WD-WMC4N2578099" UCRC1CNT=1 UCRC2SN="S2X1J90CA48799" UCRC2CNT=2 UCRC3SN="P02618119268" UCRC3CNT=1 UCRC4SN="DRIVE_SERIAL_NUMBER" UCRC4CNT=0 UCRC5SN="DRIVE_SERIAL_NUMBER" UCRC5CNT=0 UCRC6SN="DRIVE_SERIAL_NUMBER" UCRC6CNT=0 UCRC7SN="DRIVE_SERIAL_NUMBER" UCRC7CNT=0 UCRC8SN="DRIVE_SERIAL_NUMBER" UCRC8CNT=0 ###### Multi_Zone_Errors # This identifies drives with Multi_Zone_Errors which may be irritating people. # Using same format as UDMA_CRC_Errors (see above). MULTIZONE1SN="WD-WCC4N0KZDD1L" MULTIZONE1CNT=4 MULTIZONE2SN="WD-WCC4N2VNZ6HH" MULTIZONE2CNT=51 MULTIZONE3SN="S2X1J90CA48799" MULTIZONE3CNT=31 MULTIZONE4SN="DRIVE_SERIAL_NUMBER" MULTIZONE4CNT=0 MULTIZONE5SN="DRIVE_SERIAL_NUMBER" MULTIZONE5CNT=0 MULTIZONE6SN="DRIVE_SERIAL_NUMBER" MULTIZONE6CNT=0 MULTIZONE7SN="DRIVE_SERIAL_NUMBER" MULTIZONE7CNT=0 MULTIZONE8SN="DRIVE_SERIAL_NUMBER" MULTIZONE8CNT=0 ####### Reallocated Sectors Exceptions # This section should only be used for testing, normally you should be replacing drives that have a higher than normal reallocated sector count. BADSECTORS1SN="W3N0B3RP" BADSECTORS1CNT=16 BADSECTORS2SN="DRIVE_SERIAL_NUMBER" BADSECTORS2CNT=0 BADSECTORS3SN="DRIVE_SERIAL_NUMBER" BADSECTORS3CNT=0 BADSECTORS4SN="DRIVE_SERIAL_NUMBER" BADSECTORS4CNT=0 ####### Warranty Expiration Date # This section is used to add warranty expirations for designated drives and to create an alert when they expire. # Format of date is YYYY-MM-DD # To disable this feature, remove the drive on the list below. WARRANTYDRIVESN1="K1JUMLBD" WARRANTYDRIVESN1DATE="2020-09-30" WARRANTYDRIVESN2="K1JRSWLD" WARRANTYDRIVESN2DATE="2020-09-30" WARRANTYDRIVESN3="K1JUMW4D" WARRANTYDRIVESN3DATE="2020-09-30" WARRANTYDRIVESN4="K1GVD84B" WARRANTYDRIVESN4DATE="2020-10-12" ###### zpool status summary table settings usedWarn=90 # Pool used percentage for CRITICAL color to be used. scrubAgeWarn=37 # Maximum age (in days) of last pool scrub before CRITICAL color will be used (30 + 7 days for day of week). Default=37. ###### Report Status Summary Table Settings # These settings will adjust how the scripts acts. # includeSSD="true" # Set to "true" will engage SSD Automatic Detection and Reporting, false = Disable SSD Automatic Detection and Reporting. reportnonSMART="true" # Will force even non-SMART devices to be reported, "false" = normal operation to ignore non-SMART devices. HDDtempWarn=45 # HDD Drive temp (in C) at which WARNING color/message will be used. HDDtempCrit=50 # HDD Drive temp (in C) at which CRITICAL color/message will be used. SSDtempWarn=50 # SSD Drive temp (in C) at which WARNING color/message will be used. SSDtempCrit=65 # SSD Drive temp (in C) at which CRITICAL color/message will be used. sectorsWarn=2 # Number of sectors per drive with errors before WARNING color/message will be used, this value should be less than sectorsCrit. sectorsCrit=10 # Number of sectors per drive with errors before CRITICAL color/message will be used. testAgeWarn=2 # Maximum age (in days) of last SMART test before CRITICAL color/message will be used. powerTimeFormat="h" # Format for power-on hours string, valid options are "ymdh", "ymd", "ym", "y", or "h" (year month day hour). deviceRedFlag="true" # Set "true" to have the Device Column indicate RED for ANY alarm condition. Default is true. ignoreMultiZone="false" # Set to "true" to ignore all MultiZone Errors. Default is false. ignoreSeekError="true" # Set to "true" to ignore all Seek Error Rate/Health errors. Default is true. ignoreUDMA="false" # Set to "true" to ignore all UltraDMA CRC Errors for the summary alarm (Email Header) only, errors will appear in the graphical chart. expDataEnable="true" # Set to "true" will save all drive data into a CSV file defined by "statistical_data_file" below. expDataEmail="true" # Set to "true" to have an attachment of the file emailed to you. Default is true. expDataPurge=730 # Set to the number of day you wish to keep in the data. Data older will be purged. Default is 730 days (2 years). 0=Disable. expDataEmailSend="Mon" # Set to the day of the week the statistical report is emailed. (All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month) disableRAWdata="false" # Set to "true" to remove the 'smartctl -a' data and non-smart data appended to the normal report. Default is false. disableWarranty="true" # Set to "true to disable email Subject line alerts for any expired warranty alert. The email body will still report the alert. ###### Export Data File # This is the loction for your export file to reside. statistical_data_file="/tmp/statisticalsmartdata.csv" ###### FreeNAS config backup settings configBackup="true" # Set to "true" to save config backup (which renders next two options operational); "false" to keep disable config backups. configSendDay="Mon" # Set to the day of the week the statistical report is emailed. (All, Mon, Tue, Wed, Thu, Fri, Sat, Sun, Month) saveBackup="false" # Set to "false" to delete FreeNAS config backup after mail is sent; "true" to keep it in dir below. backupLocation="/tmp/" # Directory in which to store the backup FreeNAS config files. ###### Global table of colors # The colors selected you can change but you will need to look up the proper HEX code for a color. # okColor="#c9ffcc" # Hex code for color to use in SMART Status column if drives pass (default is light green, #c9ffcc). warnColor="#f765d0" # Hex code for WARN color (default is purple, #f765d0). critColor="#ff0000" # Hex code for CRITICAL color (default is red, #ff0000). altColor="#f4f4f4" # Table background alternates row colors between white and this color (default is light gray, #f4f4f4). whtColor="#ffffff" # Hex for White background. ovrdColor="#ffff66" # Hex code for Override Yellow. blueColor="#87ceeb" # Hex code for Sky Blue, used for the SCRUB In Progress background. ###### STOP EDITING THE SCRIPT HERE ###### # If you really feel like editing below this line, you are customizing the code beyond a novice user. ###### Auto-generated Parameters programver="Multi-Report v1.4d (22 June 2022)" softver=$(uname -s) host=$(hostname -s) logfile="/tmp/smart_report_body.tmp" logfile_header="/tmp/smart_report_header.tmp" logfile_warning="/tmp/smart_report_warning_flag.tmp" logfile_critical="/tmp/smart_report_critical_flag.tmp" logfile_warranty="/tmp/smart_report_warranty_flag.tmp" boundary="gc0p4Jq0M2Yt08jU534c0p" ########################## ########################## ### ### ### DEFINE FUNCTIONS ### ### ### ########################## ########################## #################### PURGE EXPORT DATA CSV FILE ####################### purge_exportdata () { ### This routine will purge the "statistical_data_file" of data older then "expDataPurge". # echo "Running Purging Routine" # Delete temp file if it exists if test -e "/tmp/temp_purge_file.csv"; then rm "/tmp/temp_purge_file.csv" # Create the header printf "Date,Time,Device ID,Drive Type,Serial Number,SMART Status,Temp,Power On Hours,Wear Level,Start Stop Count,Load Cycle,Spin Retry,Reallocated Sectors,\ ReAllocated Sector Events,Pending Sectors,Offline Uncorrectable,UDMA CRC Errors,Seek Error Rate,Multi Zone Errors,\n" > "/tmp/temp_purge_file.csv" fi { input="$statistical_data_file" if [ $softver != "Linux" ]; then expireDate=$(date -v -"$expDataPurge"d +%Y/%m/%d) else expireDate=$(date -d "$expDataPurge days ago" +%Y/%m/%d) fi awk -v expireDate="$expireDate" ' BEGIN { FS=OFS="," FPAT = "([^,]+)|(\"[^\"]+\")" count=0 } data=$1 { FS=OFS=" " if (count !=0) if ($1 >= expireDate) { printf ("%s\n", data) >> "/tmp/temp_purge_file.csv" } ++count } END { } ' $input >/dev/null } cp -R "/tmp/temp_purge_file.csv" "$statistical_data_file" } ################## EMAIL EXPORT DATA CVS FILE ######################### email_datafile () { if [ "$expDataEmail" == "true" ]; then Now=$(date +"%a") doit="false" case $expDataEmailSend in All) doit="true" ;; Mon|Tue|Wed|Thu|Fri|Sat|Sun) if [[ "$expDataEmailSend" == "$Now" ]]; then doit="true" fi ;; Month) if [[ $(date +"%d") == "01" ]]; then doit="true" fi ;; *) ;; esac if [[ "$doit" == "true" ]]; then ( # Write MIME section header for file attachment (encoded with base64) echo "--${boundary}" echo "Content-Type: text/csv" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=Statistical_Data.csv" base64 "$statistical_data_file" echo "--${boundary}--" ) >> "$logfile" fi fi } #################### GET SMART HARD DRIVES ############################ get_smartHDD_listings () { ### GET non-SSD listing - MUST support SMART # smartdrives= All Drives that are SMART # smartdrivesSSD = SSD all SMART # nonsmartdrives = Any drive not supporting SMART # Get Hard Drive listing - MUST support SMART # variable smartdrives if [ $softver != "Linux" ]; then smartdrives=$(for drive in $(sysctl -n kern.disks); do if [ "$(smartctl -i /dev/"${drive}" | grep "SMART support is:.\s*Enabled")" ] && ! [ "$(smartctl -i /dev/"${drive}" | grep "Solid State Device")" ]; then targument="$(smartctl -i /dev/"${drive}" | grep "Serial Number:" | awk '{print $3}')"; ### Process Ignore List ### s="0" IFS=',' read -ra ADDR <<< "$IGNOREDRIVES" for i in "${ADDR[@]}"; do if [[ $i == $targument ]]; then s="1" continue fi done if [[ $s == "0" ]]; then printf "%s " "${drive}" fi fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') else smartdrives=$(for drive in $(fdisk -l | grep "Disk /dev/sd" | cut -c 11-13 | tr '\n' ' '); do if [ "$(smartctl -i /dev/"${drive}" | grep "SMART support is:.\s*Enabled")" ] && ! [ "$(smartctl -i /dev/"${drive}" | grep "Solid State Device")" ]; then targument="$(smartctl -i /dev/"${drive}" | grep "Serial Number:" | awk '{print $3}')"; ### Process Ignore List ### s="0" IFS=',' read -ra ADDR <<< "$IGNOREDRIVES" for i in "${ADDR[@]}"; do if [[ $i == $targument ]]; then s="1" continue fi done if [[ $s == "0" ]]; then printf "%s " "${drive}" fi fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') fi } ########################## GET SMART SOLID DISK DRIVES ################################ get_smartSSD_listings () { # Get SSD listing - MUST suport SMART # variable smartdrivesSSD if [ $softver != "Linux" ]; then smartdrivesSSD=$(for drive in $(sysctl -n kern.disks); do if [ "$(smartctl -i /dev/"${drive}" | grep "SMART support is:.\s*Enabled")" ] && [ "$(smartctl -i /dev/"${drive}" | grep "Solid State Device")" ]; then targument="$(smartctl -i /dev/"${drive}" | grep "Serial Number:" | awk '{print $3}')"; ### Process Ignore List ### s="0" IFS=',' read -ra ADDR <<< "$IGNOREDRIVES" for i in "${ADDR[@]}"; do if [[ $i == $targument ]]; then s="1" continue fi done if [[ $s == "0" ]]; then printf "%s " "${drive}" fi fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') else smartdrivesSSD=$(for drive in $(fdisk -l | grep "Disk /dev/sd" | cut -c 11-13 | tr '\n' ' '); do if [ "$(smartctl -i /dev/"${drive}" | grep "SMART support is:.\s*Enabled")" ] && [ "$(smartctl -i /dev/"${drive}" | grep "Solid State Device")" ]; then targument="$(smartctl -i /dev/"${drive}" | grep "Serial Number:" | awk '{print $3}')"; ### Process Ignore List ### s="0" IFS=',' read -ra ADDR <<< "$IGNOREDRIVES" for i in "${ADDR[@]}"; do if [[ $i == $targument ]]; then s="1" continue fi done if [[ $s == "0" ]]; then printf "%s " "${drive}" fi fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') fi } ########################## GET OTHER SMART DEVICES ################################## get_smartOther_listings () { ### Get the non-SSD listing - MUST support SMART # variable nonsmartdrives if [ $softver != "Linux" ]; then nonsmartdrives=$(for drive in $(sysctl -n kern.disks); do if [ ! "$(smartctl -i /dev/"${drive}" | grep "SMART support is:.\s*Enabled")" ]; then targument="$(smartctl -i /dev/"${drive}" | grep "Serial Number:" | awk '{print $3}')"; ### Process Ignore List ### s="0" IFS=',' read -ra ADDR <<< "$IGNOREDRIVES" for i in "${ADDR[@]}"; do if [[ $i == $targument ]]; then s="1" continue fi done if [[ $s == "0" ]]; then printf "%s " "${drive}" fi fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') else nonsmartdrives=$(for drive in $(fdisk -l | grep "Disk /dev/sd" | cut -c 11-13 | tr '\n' ' '); do if [ ! "$(smartctl -i /dev/"${drive}" | grep "SMART support is: Enabled")" ]; then targument="$(smartctl -i /dev/"${drive}" | grep "Serial Number:" | awk '{print $3}')"; ### Process Ignore List ### s="0" IFS=',' read -ra ADDR <<< "$IGNOREDRIVES" for i in "${ADDR[@]}"; do if [[ $i == $targument ]]; then s="1" continue fi done if [[ $s == "0" ]]; then printf "%s " "${drive}" fi fi done | awk '{for (i=NF; i!=0 ; i--) print $i }' | tr ' ' '\n' | sort | tr '\n' ' ') fi } ########################### FORMAT EMAILS STEP 1 ########################## email_preformat () { ###### Email pre-formatting ### Set some of the email headers before conditional headers ( echo "MIME-Version: 1.0" echo "Content-Type: multipart/mixed; boundary=${boundary}" ) > "$logfile" } ########################### CONFIGURATION BACKUP ############################## config_backup () { ###### Config backup (if enabled) tarfile="/tmp/config_backup.tar.gz" filename="$(date "+FreeNAS_Config_%Y-%m-%d")" filename2="Stat_Data" #1 if [ "$configBackup" == "true" ]; then ############################################################################################################ Now=$(date +"%a") doit="false" case $configSendDay in All) doit="true" ;; Mon|Tue|Wed|Thu|Fri|Sat|Sun) if [[ "$configSendDay" == "$Now" ]]; then doit="true" fi ;; Month) if [[ $(date +"%d") == "01" ]]; then doit="true" fi ;; *) ;; esac if [[ "$doit" == "true" ]]; then # Set up file names, etc for later tarfile="/tmp/config_backup.tar.gz" filename="$(date "+FreeNAS_Config_%Y-%m-%d")" filename2="Stat_Data" ### Test config integrity if ! [ "$(sqlite3 /data/freenas-v1.db "pragma integrity_check;")" == "ok" ]; then # Config integrity check failed, set MIME content type to html and print warning ( echo "--${boundary}" echo "Content-Type: text/html" echo "Automatic backup of FreeNAS configuration has failed! The configuration file is corrupted!" echo "You should correct this problem as soon as possible!" echo "
" ) >> "$logfile" else # Config integrity check passed; copy config db, generate checksums, make .tar.gz archive cp /data/freenas-v1.db "/tmp/${filename}.db" if [ $softver != "Linux" ]; then md5 "/tmp/${filename}.db" > /tmp/config_backup.md5 sha256 "/tmp/${filename}.db" > /tmp/config_backup.sha256 else md5sum "/tmp/${filename}.db" > /tmp/config_backup.md5 sha256sum "/tmp/${filename}.db" > /tmp/config_backup.sha256 fi ( cd "/tmp/" || exit; tar -czf "${tarfile}" "./${filename}.db" ./config_backup.md5 ./config_backup.sha256; ) ( # Write MIME section header for file attachment (encoded with base64) echo "--${boundary}" echo "Content-Type: application/tar+gzip" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=${filename}.tar.gz" base64 "$tarfile" # Write MIME section header for html content to come below echo "--${boundary}" echo "Content-Type: text/html" ) >> "$logfile" # If logfile saving is enabled, copy .tar.gz file to specified location before it (and everything else) is removed below if [ "$saveBackup" == "true" ]; then cp "${tarfile}" "${backupLocation}/${filename}.tar.gz" fi rm "/tmp/${filename}.db" rm /tmp/config_backup.md5 rm /tmp/config_backup.sha256 rm "${tarfile}" fi else ( echo "--${boundary}" echo "Content-Type: text/html" ) >> "$logfile" fi else # configBackup = false so this is what to do # Config backup enabled; set up for html-type content ( echo "--${boundary}" echo "Content-Type: text/html" ) >> "$logfile" fi } ################################## GENERATE ZPOOL REPORT ################################## zpool_report () { ###### Report Summary Section (html tables) ### zpool status summary table ( # Write HTML table headers to log file; HTML in an email requires 100% in-line styling (no CSS or