├── set_hdd_erc.sh ├── ups_report.sh ├── get_hdd_temp.sh ├── save_config.sh ├── save_config_enc.sh ├── zpool_report.sh ├── get-system-temps.pl ├── smart_report.sh └── README.md /set_hdd_erc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # https://www.smartmontools.org/wiki/FAQ#WhatiserrorrecoverycontrolERCandwhyitisimportanttoenableitfortheSATAdisksinRAID 4 | 5 | # ERC timeout values, in tenths of a second. The defaults below are 7 seconds for both reads and writes: 6 | 7 | readsetting=70 8 | writesetting=70 9 | 10 | # We need a list of the SMART-enabled drives on the system. Choose one of these 11 | # three methods to provide the list. Comment out the two unused sections of code. 12 | 13 | # 1. A string constant; just key in the devices you want to report on here: 14 | #drives="da1 da2 da3 da4 da5 da6 da7 da8 ada0" 15 | 16 | # 2. A systcl-based technique suggested on the FreeNAS forum: 17 | #drives=$(for drive in $(sysctl -n kern.disks); do \ 18 | #if [ "$(/usr/local/sbin/smartctl -i /dev/${drive} | grep "SMART support is: Enabled" | awk '{print $3}')" ] 19 | #then printf ${drive}" "; fi done | awk '{for (i=NF; i!=0 ; i--) print $i }') 20 | 21 | # 3. A smartctl-based function: 22 | get_smart_drives() 23 | { 24 | gs_drives=$(/usr/local/sbin/smartctl --scan | grep "dev" | awk '{print $1}' | sed -e 's/\/dev\///' | tr '\n' ' ') 25 | 26 | gs_smartdrives="" 27 | 28 | for gs_drive in $gs_drives; do 29 | gs_smart_flag=$(/usr/local/sbin/smartctl -i /dev/"$gs_drive" | grep "SMART support is: Enabled" | awk '{print $4}') 30 | if [ "$gs_smart_flag" = "Enabled" ]; then 31 | gs_smartdrives=$gs_smartdrives" "${gs_drive} 32 | fi 33 | done 34 | 35 | eval "$1=\$gs_smartdrives" 36 | } 37 | 38 | drives="" 39 | get_smart_drives drives 40 | 41 | # end of method 3. 42 | 43 | set_erc() 44 | { 45 | echo "Drive: /dev/$1" 46 | /usr/local/sbin/smartctl -q silent -l scterc,"${readsetting}","${writesetting}" /dev/"$1" 47 | /usr/local/sbin/smartctl -l scterc /dev/"$1" | grep "SCT\|Write\|Read" 48 | } 49 | 50 | for drive in $drives; do 51 | set_erc "$drive" 52 | done 53 | -------------------------------------------------------------------------------- /ups_report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Send UPS report to designated email address 4 | # Reference: http://networkupstools.org/docs/developer-guide.chunked/apas01.html 5 | 6 | ### Parameters ### 7 | 8 | # Specify your email address here: 9 | email="" 10 | 11 | # Set to a value greater than zero to include all available UPSC 12 | # variables in the report: 13 | senddetail=0 14 | 15 | freenashost=$(hostname -s) 16 | freenashostuc=$(hostname -s | tr '[:lower:]' '[:upper:]') 17 | boundary="===== MIME boundary; FreeNAS server ${freenashost} =====" 18 | logfile="/tmp/ups_report.tmp" 19 | subject="UPS Status Report for ${freenashostuc}" 20 | 21 | ### Set email headers ### 22 | printf "%s\n" "To: ${email} 23 | Subject: ${subject} 24 | Mime-Version: 1.0 25 | Content-Type: multipart/mixed; boundary=\"$boundary\" 26 | 27 | --${boundary} 28 | Content-Type: text/html; charset=\"US-ASCII\" 29 | Content-Transfer-Encoding: 7bit 30 | Content-Disposition: inline 31 |
" >> ${logfile}
32 |
33 | # Get a list of all ups devices installed on the system:
34 |
35 | upslist=$(upsc -l "${freenashost}")
36 |
37 | ### Set email body ###
38 | (
39 | date "+Time: %Y-%m-%d %H:%M:%S"
40 | echo ""
41 | for ups in $upslist; do
42 | ups_type=$(upsc "${ups}" device.type 2> /dev/null | tr '[:lower:]' '[:upper:]')
43 | ups_mfr=$(upsc "${ups}" ups.mfr 2> /dev/null)
44 | ups_model=$(upsc "${ups}" ups.model 2> /dev/null)
45 | ups_serial=$(upsc "${ups}" ups.serial 2> /dev/null)
46 | ups_status=$(upsc "${ups}" ups.status 2> /dev/null)
47 | ups_load=$(upsc "${ups}" ups.load 2> /dev/null)
48 | ups_realpower=$(upsc "${ups}" ups.realpower 2> /dev/null)
49 | ups_realpowernominal=$(upsc "${ups}" ups.realpower.nominal 2> /dev/null)
50 | ups_batterycharge=$(upsc "${ups}" battery.charge 2> /dev/null)
51 | ups_batteryruntime=$(upsc "${ups}" battery.runtime 2> /dev/null)
52 | ups_batteryvoltage=$(upsc "${ups}" battery.voltage 2> /dev/null)
53 | ups_inputvoltage=$(upsc "${ups}" input.voltage 2> /dev/null)
54 | ups_outputvoltage=$(upsc "${ups}" output.voltage 2> /dev/null)
55 | printf "=== %s %s, model %s, serial number %s\n\n" "${ups_mfr}" "${ups_type}" "${ups_model}" "${ups_serial} ==="
56 | echo "Name: ${ups}"
57 | echo "Status: ${ups_status}"
58 | echo "Output Load: ${ups_load}%"
59 | if [ ! -z "${ups_realpower}" ]; then
60 | echo "Real Power: ${ups_realpower}W"
61 | fi
62 | if [ ! -z "${ups_realpowernominal}" ]; then
63 | echo "Real Power: ${ups_realpowernominal}W (nominal)"
64 | fi
65 | if [ ! -z "${ups_inputvoltage}" ]; then
66 | echo "Input Voltage: ${ups_inputvoltage}V"
67 | fi
68 | if [ ! -z "${ups_outputvoltage}" ]; then
69 | echo "Output Voltage: ${ups_outputvoltage}V"
70 | fi
71 | echo "Battery Runtime: ${ups_batteryruntime}s"
72 | echo "Battery Charge: ${ups_batterycharge}%"
73 | echo "Battery Voltage: ${ups_batteryvoltage}V"
74 | echo ""
75 | if [ $senddetail -gt 0 ]; then
76 | echo "=== ALL AVAILABLE UPS VARIABLES ==="
77 | upsc "${ups}"
78 | echo ""
79 | fi
80 | done
81 | ) >> ${logfile}
82 |
83 | printf "%s\n" "
84 | --${boundary}--" >> ${logfile}
85 |
86 | ### Send report ###
87 | if [ -z "${email}" ]; then
88 | echo "No email address specified, information available in ${logfile}"
89 | else
90 | sendmail -t -oi < ${logfile}
91 | rm ${logfile}
92 | fi
93 |
94 |
--------------------------------------------------------------------------------
/get_hdd_temp.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Display current temperature of CPU(s) and all SMART-enabled drives
4 |
5 | # Optionally uses IPMI to report temperatures of the system CPU(s)
6 | #
7 | # If IPMI is disabled (see 'use_ipmi' below) then the script uses
8 | # sysctl to report the CPU temperatures. To use IPMI, you must
9 | # provide the IPMI host, user name, and user password file.
10 |
11 | # Full path to 'smartctl' program:
12 | smartctl=/usr/local/sbin/smartctl
13 |
14 | # IPMI support: set to a postive value to use IPMI for CPU temp
15 | # reporting, set to zero to disable IPMI and use 'sysctl' instead:
16 | use_ipmi=0
17 |
18 | # IP address or DNS-resolvable hostname of IPMI server:
19 | ipmihost=192.168.1.x
20 |
21 | # IPMI username:
22 | ipmiuser=root
23 |
24 | # IPMI password file. This is a file containing the IPMI user's password
25 | # on a single line and should have 0600 permissions:
26 | ipmipwfile=/root/ipmi_password
27 |
28 | # Full path to 'ipmitool' program:
29 | ipmitool=/usr/local/bin/ipmitool
30 |
31 | # We need a list of the SMART-enabled drives on the system. Choose one of these
32 | # three methods to provide the list. Comment out the two unused sections of code.
33 |
34 | # 1. A string constant; just key in the devices you want to report on here:
35 | #drives="da1 da2 da3 da4 da5 da6 da7 da8 ada0"
36 |
37 | # 2. A systcl-based technique suggested on the FreeNAS forum:
38 | #drives=$(for drive in $(sysctl -n kern.disks); do \
39 | #if [ "$(/usr/local/sbin/smartctl -i /dev/${drive} | grep "SMART support is: Enabled" | awk '{print $3}')" ]
40 | #then printf ${drive}" "; fi done | awk '{for (i=NF; i!=0 ; i--) print $i }')
41 |
42 | # 3. A smartctl-based function:
43 |
44 | get_smart_drives()
45 | {
46 | gs_smartdrives=""
47 | gs_drives=$("$smartctl" --scan | awk '{print $1}')
48 |
49 | for gs_drive in $gs_drives; do
50 | gs_smart_flag=$("$smartctl" -i "$gs_drive" | egrep "SMART support is:[[:blank:]]+Enabled" | awk '{print $4}')
51 | if [ "$gs_smart_flag" = "Enabled" ]; then
52 | gs_smartdrives="$gs_smartdrives $gs_drive"
53 | fi
54 | done
55 | echo "$gs_smartdrives"
56 | }
57 |
58 | drives=$(get_smart_drives)
59 |
60 | # end of method 3.
61 |
62 | #############################
63 | # CPU temperatures:
64 | #############################
65 |
66 | if [ "$use_ipmi" -eq 0 ]; then
67 | cpucores=$(sysctl -n hw.ncpu)
68 | printf '=== CPU (%s) ===\n' "$cpucores"
69 | cpucores=$((cpucores - 1))
70 | for core in $(seq 0 $cpucores); do
71 | temp=$(sysctl -n dev.cpu."$core".temperature|sed 's/\..*$//g')
72 | if [ "$temp" -lt 0 ]; then
73 | temp="--n/a--"
74 | else
75 | temp="${temp}C"
76 | fi
77 | printf 'CPU %2.2s: %5s\n' "$core" "$temp"
78 | done
79 | echo ""
80 | else
81 | cpucores=$("$ipmitool" -I lanplus -H "$ipmihost" -U "$ipmiuser" -f "$ipmipwfile" sdr elist all | grep -c -i "cpu.*temp")
82 |
83 | printf '=== CPU (%s) ===\n' "$cpucores"
84 | if [ "$cpucores" -eq 1 ]; then
85 | temp=$("$ipmitool" -I lanplus -H "$ipmihost" -U "$ipmiuser" -f "$ipmipwfile" sdr elist all | grep "CPU Temp" | awk '{print $10}')
86 | if [ "$temp" -lt 0 ]; then
87 | temp="-n/a-"
88 | else
89 | temp="${temp}C"
90 | fi
91 | printf 'CPU %2s: %5s\n' "$core" "$temp"
92 | else
93 | for core in $(seq 1 "$cpucores"); do
94 | temp=$("$ipmitool" -I lanplus -H "$ipmihost" -U "$ipmiuser" -f "$ipmipwfile" sdr elist all | grep "CPU${core} Temp" | awk '{print $10}')
95 | if [ "$temp" -lt 0 ]; then
96 | temp="-n/a-"
97 | else
98 | temp="${temp}C"
99 | fi
100 | printf 'CPU %2s: [%s]\n' "$core" "$temp"
101 | done
102 | fi
103 | echo ""
104 | fi
105 |
106 | #############################
107 | # Drive temperatures:
108 | #############################
109 |
110 | echo "=== DRIVES ==="
111 |
112 | for drive in $drives; do
113 | serial=$("$smartctl" -i "$drive" | grep -i "serial number" | awk '{print $NF}')
114 | capacity=$("$smartctl" -i "$drive" | grep "User Capacity" | awk '{print $5 $6}')
115 | temp=$("$smartctl" -A "$drive" | grep "194 Temperature" | awk '{print $10}')
116 | if [ -z "$temp" ]; then
117 | temp=$("$smartctl" -A "$drive" | grep "190 Temperature_Case" | awk '{print $10}')
118 | fi
119 | if [ -z "$temp" ]; then
120 | temp=$("$smartctl" -A "$drive" | grep "190 Airflow_Temperature" | awk '{print $10}')
121 | fi
122 | if [ -z "$temp" ]; then
123 | temp=$("$smartctl" -A "$drive" | grep "Current Drive Temperature" | awk '{print $4}')
124 | fi
125 | if [ -z "$temp" ]; then
126 | temp="-n/a-"
127 | else
128 | temp="${temp}C"
129 | fi
130 | dfamily=$("$smartctl" -i "$drive" | grep "Model Family" | awk '{print $3, $4, $5, $6, $7}' | sed -e 's/[[:space:]]*$//')
131 | dmodel=$("$smartctl" -i "$drive" | grep "Device Model" | awk '{print $3, $4, $5, $6, $7}' | sed -e 's/[[:space:]]*$//')
132 | if [ -z "$dfamily" ]; then
133 | dinfo="$dmodel"
134 | else
135 | dinfo="$dfamily ($dmodel)"
136 | fi
137 | if [ -z "$dfamily" ]; then
138 | vendor=$("$smartctl" -i "$drive" | grep "Vendor:" | awk '{print $NF}')
139 | product=$("$smartctl" -i "$drive" | grep "Product:" | awk '{print $NF}')
140 | revision=$("$smartctl" -i "$drive" | grep "Revision:" | awk '{print $NF}')
141 | dinfo="$vendor $product $revision"
142 | fi
143 | printf '%6.6s: %5s %-8s %-20.20s %s\n' "$(basename "$drive")" "$temp" "$capacity" "$serial" "$dinfo"
144 | done
145 |
146 |
--------------------------------------------------------------------------------
/save_config.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #####
4 | # Backup the TrueNAS/FreeNAS configuration database and password secret encryption files
5 | #####
6 |
7 | # REQUIRED: Specify the dataset on your system where you want the configuration files copied.
8 | # Don't include the trailing slash.
9 |
10 | # Example: configdir="/mnt/tank/sysadmin/config"
11 |
12 | configdir=""
13 |
14 | # Remove this code once you've defined configdir above... :-)
15 |
16 | if [ -z "${configdir}" ]; then
17 | echo "Edit script and specify the target directory ('configdir') before using $0"
18 | exit 2
19 | fi
20 |
21 | # Optional: Set non-zero 'do_tar' flag to have both files stored in a tarball as typically
22 | # needed when restoring a configuration.
23 | do_tar=1
24 |
25 | # Optional: specify your email address here if you want to receive a notification message.
26 | notifyemail=""
27 |
28 | # Optional: specify the short name of your ESXi host if you are running FreeNAS
29 | # as a VM and you want to back up the ESXi host's configuration
30 | esxihost=""
31 |
32 | # Get the date and version of TrueNAS/FreeNAS:
33 |
34 | rundate=$(date)
35 |
36 | osvers=$(grep -i truenas /etc/version)
37 | if [ -z "${osvers}" ]; then
38 | osvers=$(grep -i freenas /etc/version)
39 | if [ -z "${osvers}" ]; then
40 | osvers="UNKNOWN"
41 | else
42 | osvers="FreeNAS"
43 | fi
44 | else
45 | osvers="TrueNAS"
46 | fi
47 |
48 | # Form a unique, timestamped filename for the backup configuration database and tarball
49 |
50 | P1=$(hostname -s)
51 | P2=$(< /etc/version sed -e 's/)//;s/(//;s/ /-/' | tr -d '\n')
52 | P3=$(date +%Y%m%d%H%M%S)
53 | fnconfigdest_base="$P1"-"$P2"-"$P3"
54 | fnconfigdestdb="${configdir}"/"${fnconfigdest_base}".db
55 | fnconfigtarball="${configdir}"/"${fnconfigdest_base}".tar
56 |
57 | # Copy the source database and password encryption secret key to the destination:
58 |
59 | echo "Backup ${osvers} configuration database file: ${fnconfigdestdb}"
60 |
61 | cp -f /data/pwenc_secret "$configdir"
62 | /usr/local/bin/sqlite3 /data/freenas-v1.db ".backup main '${fnconfigdestdb}'"
63 | l_status=$?
64 |
65 | # Validate the configuration file and create tarball:
66 |
67 | if [ $l_status -eq 0 ]; then
68 | dbstatus=$(sqlite3 "$fnconfigdestdb" "pragma integrity_check;")
69 | printf 'sqlite3 status: [%s]\n' "${dbstatus}"
70 | if [ "${dbstatus}" = "ok" ]; then
71 | l_status=0
72 | if [ $do_tar -ne 0 ]; then
73 | # Save the config DB w/ its original name in the tarball -- makes restoring them easier:
74 | cp -f "${fnconfigdestdb}" "${configdir}"/freenas-v1.db
75 | tar -cvf "${fnconfigtarball}" -C "${configdir}" freenas-v1.db pwenc_secret
76 | l_status=$?
77 | printf 'tar status: [%s]\n' "${l_status}"
78 | fi
79 | else
80 | l_status=1
81 | fi
82 | fi
83 |
84 | if [ $l_status -eq 0 ]; then
85 | echo "Success backing up configuration files to directory ${configdir}"
86 | else
87 | echo "Error backing up configuration files to directory ${configdir}"
88 | fi
89 | l_status=$?
90 |
91 | # Backup the VMware ESXi host configuration:
92 |
93 | if [ -n "${esxihost}" ]; then
94 | esxihostname=$(ssh root@"${esxihost}" hostname)
95 | esxiversion=$(ssh root@"${esxihost}" uname -a | sed -e "s|VMkernel ||;s|$esxihostname ||")
96 | esxiconfig_url=$(ssh root@"${esxihost}" vim-cmd hostsvc/firmware/backup_config | awk '{print $7}' | sed -e "s|*|$esxihostname|")
97 | esxiconfig_date=$(date +%Y%m%d%H%M%S)
98 | esxiconfig_file="${configdir}"/"${esxihost}"-configBundle-"${esxiconfig_date}".tgz
99 |
100 | echo "Downloading $esxiconfig_url to $esxiconfig_file"
101 | wget --no-check-certificate --output-document="${esxiconfig_file}" "${esxiconfig_url}"
102 | fi
103 |
104 | # Send email notification if indicated:
105 |
106 | if [ -n "${notifyemail}" ]; then
107 | freenashostuc=$(hostname -s | tr '[:lower:]' '[:upper:]')
108 | freenashostname=$(hostname)
109 | freenasversion=$(< /etc/version sed -e 's/)//;s/(//;s/ /-/' | tr -d '\n')
110 | boundary="===== MIME boundary; ${osvers} server ${freenashostname} ====="
111 | logfile="/tmp/save_config.tmp"
112 | if [ $l_status -eq 0 ]; then
113 | subject="${osvers} configuration saved on server ${freenashostuc}"
114 | else
115 | subject="${osvers} configuration backup failed on server ${freenashostuc}"
116 | fi
117 |
118 | printf "%s\n" "To: ${notifyemail}
119 | Subject: ${subject}
120 | Mime-Version: 1.0
121 | Content-Type: multipart/mixed; boundary=\"$boundary\"
122 |
123 | --${boundary}
124 | Content-Type: text/html; charset=\"US-ASCII\"
125 | Content-Transfer-Encoding: 7bit
126 | Content-Disposition: inline
127 | " > ${logfile}
128 |
129 | (
130 | if [ $l_status -eq 0 ]; then
131 | echo "Configuration file saved successfully on ${rundate}"
132 | else
133 | echo "Configuration backup failed with status=${l_status} on ${rundate}"
134 | fi
135 | echo ""
136 | echo "--- ${osvers} ---"
137 | echo "Server: ${freenashostname}"
138 | echo "Version: ${freenasversion}"
139 | echo "Files:"
140 | echo " ${fnconfigdestdb}"
141 | echo " ${configdir}/pwenc_secret"
142 | if [ "$do_tar" -ne 0 ]; then
143 | echo " ${fnconfigtarball}"
144 | fi
145 | if [ -n "${esxihost}" ]; then
146 | echo ""
147 | echo "--- ESXi ---"
148 | echo "Server: ${esxihostname}"
149 | echo "Version: ${esxiversion}"
150 | echo "File: ${esxiconfig_file}"
151 | fi
152 | ) >> ${logfile}
153 |
154 | printf "%s\n" "
155 | --${boundary}--" >> ${logfile}
156 |
157 | sendmail -t -oi < ${logfile}
158 | rm ${logfile}
159 | fi
160 |
161 |
162 |
--------------------------------------------------------------------------------
/save_config_enc.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #################################################
4 | # Backup FreeNAS configuration files
5 | #
6 | # Copies the FreeNAS sqlite3 configuration and password secret
7 | # seed files to the location you specify in the 'configdir'
8 | # variable below.
9 | #
10 | # OPTIONAL:
11 | #
12 | # By specifying your email address in the 'email' variable, you may choose to
13 | # have the configuration file emailed to you in an encrypted tarball.
14 | #
15 | #################################################
16 |
17 | rundate=$(date)
18 |
19 | # Optional: specify your email address here if you want to the script to email
20 | # you the configuration file in an encrypted tarball.
21 | #
22 | # Leave the email address blank to simply copy the configuration file to the
23 | # destination you specify with the 'configdir' setting below.
24 | email=""
25 |
26 | # Specify the dataset on your system where you want the configuration files copied.
27 | # Don't include the trailing slash.
28 |
29 | # Example: configdir=/mnt/tank/sysadmin/config
30 | configdir=""
31 |
32 | # OpenSSL encryption passphrase file. Enter the passphrase on the the first line in
33 | # the file. This file should have 0600 permissions.
34 | enc_passphrasefile=/root/config_passphrase
35 |
36 | # FreeNAS hostname:
37 | freenashost=$(hostname -s)
38 |
39 | # FreeBSD version:
40 | fbsd_relver=$(uname -K)
41 |
42 | # MIME boundary
43 | mime_boundary="==>>> MIME boundary; FreeNAS server [${freenashost}] <<<=="
44 |
45 | #################################################
46 | # Append file attachment to current email message
47 | #################################################
48 |
49 | append_file()
50 | {
51 | l_mimetype=""
52 |
53 | if [ -f "$1" ]; then
54 | l_mimetype=$(file --mime-type "$1" | sed 's/.*: //')
55 |
56 | printf '%s\n' "--${mime_boundary}
57 | Content-Type: $l_mimetype
58 | Content-Transfer-Encoding: base64
59 | Content-Disposition: attachment; filename=\"$(basename "$1")\"
60 | "
61 | base64 "$1"
62 | echo
63 | fi
64 | }
65 |
66 | #################################################
67 | # Backup the FreeNAS configuration file
68 | #################################################
69 |
70 | fnconfigdest_version=$(< /etc/version sed -e 's/)//;s/(//;s/ /-/' | tr -d '\n')
71 | fnconfigdest_date=$(date +%Y%m%d%H%M%S)
72 | fnconfigdest_base="$freenashost"-"$fnconfigdest_version"-"$fnconfigdest_date".db
73 | fnconfigdest="$configdir"/"$fnconfigdest_base"
74 | fnconfigtarball=./"$freenashost"-"$fnconfigdest_version"-"$fnconfigdest_date".tar.gz
75 | fnconfigtarballenc=./"$freenashost"-"$fnconfigdest_version"-"$fnconfigdest_date".tar.gz.enc
76 |
77 | echo "Backup configuration database file: $fnconfigdest"
78 |
79 | # Copy the source database and password encryption secret seed file to the destination:
80 |
81 | /usr/local/bin/sqlite3 /data/freenas-v1.db ".backup main '${fnconfigdest}'"
82 | l_status=$?
83 | cp -f /data/pwenc_secret "$configdir"
84 |
85 | if [ -z "$email" ]; then
86 | # No email message requested, show status and exit:
87 | echo "Configuration file copied with status ${l_status}"
88 | exit $l_status
89 | fi
90 |
91 | #########################################################
92 | # Send email message with encrypted config files attached
93 | #########################################################
94 |
95 | fnconfigtarball=./"$freenashost"-"$fnconfigdest_version"-"$fnconfigdest_date".tar.gz
96 | fnconfigtarballenc=./"$freenashost"-"$fnconfigdest_version"-"$fnconfigdest_date".tar.gz.enc
97 |
98 | # Validate the configuration file and create tarball:
99 |
100 | if [ $l_status -eq 0 ]; then
101 | dbstatus=$(sqlite3 "$fnconfigdest" "pragma integrity_check;")
102 | printf 'sqlite3 status: [%s]\n' "$dbstatus"
103 | if [ "$dbstatus" = "ok" ]; then
104 | tar -czvf "$fnconfigtarball" -C "$configdir" "$fnconfigdest_base" pwenc_secret
105 | l_status=$?
106 | printf 'tar status: [%s]\n' "$l_status"
107 | else
108 | l_status=1
109 | fi
110 | if [ $l_status -eq 0 ]; then
111 | if [ "$fbsd_relver" -ge 1200000 ]; then
112 | openssl enc -e -aes-256-cbc -md sha512 -pbkdf2 -iter 128000 -salt -S "$(openssl rand -hex 8)" -pass file:"$enc_passphrasefile" -in "$fnconfigtarball" -out "$fnconfigtarballenc"
113 | else
114 | openssl enc -e -aes-256-cbc -md sha512 -salt -S "$(openssl rand -hex 4)" -pass file:"$enc_passphrasefile" -in "$fnconfigtarball" -out "$fnconfigtarballenc"
115 | fi
116 | l_status=$?
117 | printf 'openssl status: [%s]\n' "$l_status"
118 | fi
119 | fi
120 |
121 | freenashostuc=$(hostname -s | tr '[:lower:]' '[:upper:]')
122 | freenashostname=$(hostname)
123 | freenasversion=$(cat /etc/version)
124 | if [ $l_status -eq 0 ]; then
125 | subject="FreeNAS configuration saved on server ${freenashostuc}"
126 | savestatus="FreeNAS configuration file saved successfully on ${rundate}"
127 | else
128 | subject="FreeNAS configuration backup failed on server ${freenashostuc}"
129 | savestatus="FreeNAS configuration backup failed with status=${l_status} on ${rundate}"
130 | fi
131 | logfile="/tmp/save_config_enc.tmp"
132 | {
133 | printf '%s\n' "From: root
134 | To: ${email}
135 | Subject: ${subject}
136 | Mime-Version: 1.0
137 | Content-Type: multipart/mixed; boundary=\"$mime_boundary\"
138 |
139 | --${mime_boundary}
140 | Content-Type: text/plain; charset=\"US-ASCII\"
141 | Content-Transfer-Encoding: 7bit
142 | Content-Disposition: inline
143 |
144 | ${savestatus}
145 |
146 | Server: ${freenashostname}
147 | Version: ${freenasversion}
148 | File: ${fnconfigdest}
149 | "
150 |
151 | if [ $l_status -eq 0 ]; then
152 | append_file "$fnconfigtarballenc"
153 | fi
154 |
155 | # print last boundary with closing --
156 | printf '%s\n' "--${mime_boundary}--"
157 | } > "$logfile"
158 |
159 | sendmail -t -oi < "$logfile"
160 | rm "$logfile"
161 | rm "$fnconfigtarball"
162 | rm "$fnconfigtarballenc"
163 |
164 |
165 |
166 |
--------------------------------------------------------------------------------
/zpool_report.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | ### Parameters ###
4 |
5 | # Specify your email address here:
6 | email=""
7 |
8 | # zpool output changed from FreeNAS version 11.0 to 11.1, breaking
9 | # our parsing of the scrubErrors and scrubDate variables. Added a
10 | # conditional to test for the FreeNAS version and parse accordingly.
11 | # This changed again with the release of TrueNAS. Ironically, back to
12 | # the way parsing worked with older versions of FreeNAS.
13 | #
14 | # We obtain the FreeBSD version using uname, as suggested by user
15 | # Chris Moore on the FreeBSD forum.
16 | #
17 | # 'uname -K' gives 7-digit OS release and version, e.g.:
18 | #
19 | # FreeBSD 11.0 1100512
20 | # FreeBSD 11.1 1101505
21 | # FreeBSD 12.2 1202000
22 | #
23 | # If a scrub runs longer than 24 hours, we have two additional tokens to parse in the
24 | # zpool status scan line output ("'x' days"):
25 | #
26 | # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
27 | # scan: scrub repaired 0B in 1 days 11:56:46 with 0 errors on Wed Dec 9 06:07:04 2020
28 | #
29 | # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
30 | # scan: scrub repaired 0B in 00:09:11 with 0 errors on Sun Dec 13 17:31:24 2020
31 |
32 | fbsd_relver=$(uname -K)
33 |
34 | freenashost=$(hostname -s | tr '[:lower:]' '[:upper:]')
35 | boundary="===== MIME boundary; FreeNAS server ${freenashost} ====="
36 | logfile="/tmp/zpool_report.tmp"
37 | subject="ZPool Status Report for ${freenashost}"
38 | pools=$(zpool list -H -o name)
39 | usedWarn=75
40 | usedCrit=90
41 | scrubAgeWarn=30
42 | warnSymbol="?"
43 | critSymbol="!"
44 |
45 | ### Set email headers ###
46 | printf "%s\n" "To: ${email}
47 | Subject: ${subject}
48 | Mime-Version: 1.0
49 | Content-Type: multipart/mixed; boundary=\"$boundary\"
50 |
51 | --${boundary}
52 | Content-Type: text/html; charset=\"US-ASCII\"
53 | Content-Transfer-Encoding: 7bit
54 | Content-Disposition: inline
55 | " >> ${logfile}
56 |
57 | ###### summary ######
58 | (
59 | echo "########## ZPool status report summary for all pools on server ${freenashost} ##########"
60 | echo ""
61 | echo "+--------------+--------+------+------+------+----+----+--------+------+-----+"
62 | echo "|Pool Name |Status |Read |Write |Cksum |Used|Frag|Scrub |Scrub |Last |"
63 | echo "| | |Errors|Errors|Errors| | |Repaired|Errors|Scrub|"
64 | echo "| | | | | | | |Bytes | |Age |"
65 | echo "+--------------+--------+------+------+------+----+----+--------+------+-----+"
66 | ) >> ${logfile}
67 |
68 | for pool in $pools; do
69 | if [ "$fbsd_relver" -ge 1101000 ]; then
70 | frag="$(zpool list -H -o frag "$pool")"
71 | else
72 | if [ "${pool}" = "freenas-boot" ] || [ "${pool}" = "boot-pool" ]; then
73 | frag=""
74 | else
75 | frag="$(zpool list -H -o frag "$pool")"
76 | fi
77 | fi
78 |
79 | status="$(zpool list -H -o health "$pool")"
80 | errors="$(zpool status "$pool" | grep -E "(ONLINE|DEGRADED|FAULTED|UNAVAIL|REMOVED)[ \t]+[0-9]+")"
81 | readErrors=0
82 | for err in $(echo "$errors" | awk '{print $3}'); do
83 | if echo "$err" | grep -E -q "[^0-9]+"; then
84 | readErrors=1000
85 | break
86 | fi
87 | readErrors=$((readErrors + err))
88 | done
89 | writeErrors=0
90 | for err in $(echo "$errors" | awk '{print $4}'); do
91 | if echo "$err" | grep -E -q "[^0-9]+"; then
92 | writeErrors=1000
93 | break
94 | fi
95 | writeErrors=$((writeErrors + err))
96 | done
97 | cksumErrors=0
98 | for err in $(echo "$errors" | awk '{print $5}'); do
99 | if echo "$err" | grep -E -q "[^0-9]+"; then
100 | cksumErrors=1000
101 | break
102 | fi
103 | cksumErrors=$((cksumErrors + err))
104 | done
105 | if [ "$readErrors" -gt 999 ]; then readErrors=">1K"; fi
106 | if [ "$writeErrors" -gt 999 ]; then writeErrors=">1K"; fi
107 | if [ "$cksumErrors" -gt 999 ]; then cksumErrors=">1K"; fi
108 | used="$(zpool list -H -p -o capacity "$pool")"
109 | scrubRepBytes="N/A"
110 | scrubErrors="N/A"
111 | scrubAge="N/A"
112 | if [ "$(zpool status "$pool" | grep "scan" | awk '{print $2}')" = "scrub" ]; then
113 | parseLong=0
114 | if [ "$fbsd_relver" -gt 1101000 ] && [ "$fbsd_relver" -lt 1200000 ]; then
115 | parseLong=$((parseLong+1))
116 | fi
117 | if [ "$(zpool status "$pool" | grep "scan" | awk '{print $7}')" = "days" ]; then
118 | parseLong=$((parseLong+1))
119 | fi
120 | scrubRepBytes="$(zpool status "$pool" | grep "scan" | awk '{print $4}')"
121 | if [ $parseLong -gt 0 ]; then
122 | scrubErrors="$(zpool status "$pool" | grep "scan" | awk '{print $10}')"
123 | scrubDate="$(zpool status "$pool" | grep "scan" | awk '{print $17"-"$14"-"$15"_"$16}')"
124 | else
125 | scrubErrors="$(zpool status "$pool" | grep "scan" | awk '{print $8}')"
126 | scrubDate="$(zpool status "$pool" | grep "scan" | awk '{print $15"-"$12"-"$13"_"$14}')"
127 | fi
128 | scrubTS="$(date -j -f "%Y-%b-%e_%H:%M:%S" "$scrubDate" "+%s")"
129 | currentTS="$(date "+%s")"
130 | scrubAge=$((((currentTS - scrubTS) + 43200) / 86400))
131 | fi
132 | if [ "$status" = "FAULTED" ] || [ "$used" -gt "$usedCrit" ]; then
133 | symbol="$critSymbol"
134 | elif [ "$scrubErrors" != "N/A" ] && [ "$scrubErrors" != "0" ]; then
135 | symbol="$critSymbol"
136 | elif [ "$status" != "ONLINE" ] \
137 | || [ "$readErrors" != "0" ] \
138 | || [ "$writeErrors" != "0" ] \
139 | || [ "$cksumErrors" != "0" ] \
140 | || [ "$used" -gt "$usedWarn" ] \
141 | || [ "$(echo "$scrubAge" | awk '{print int($1)}')" -gt "$scrubAgeWarn" ]; then
142 | symbol="$warnSymbol"
143 | elif [ "$scrubRepBytes" != "0" ] && [ "$scrubRepBytes" != "0B" ] && [ "$scrubRepBytes" != "N/A" ]; then
144 | symbol="$warnSymbol"
145 | else
146 | symbol=" "
147 | fi
148 | (
149 | printf "|%-12s %1s|%-8s|%6s|%6s|%6s|%3s%%|%4s|%8s|%6s|%5s|\n" \
150 | "$pool" "$symbol" "$status" "$readErrors" "$writeErrors" "$cksumErrors" \
151 | "$used" "$frag" "$scrubRepBytes" "$scrubErrors" "$scrubAge"
152 | ) >> ${logfile}
153 | done
154 |
155 | (
156 | echo "+--------------+--------+------+------+------+----+----+--------+------+-----+"
157 | ) >> ${logfile}
158 |
159 | ###### for each pool ######
160 | for pool in $pools; do
161 | (
162 | echo ""
163 | echo "########## ZPool status report for ${pool} ##########"
164 | echo ""
165 | zpool status -v "$pool"
166 | ) >> ${logfile}
167 | done
168 |
169 | printf "%s\n" "
170 | --${boundary}--" >> ${logfile}
171 |
172 | ### Send report ###
173 | if [ -z "${email}" ]; then
174 | echo "No email address specified, information available in ${logfile}"
175 | else
176 | sendmail -t -oi < ${logfile}
177 | rm ${logfile}
178 | fi
179 |
--------------------------------------------------------------------------------
/get-system-temps.pl:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/perl
2 | ###############################################################################
3 | #
4 | # get-system-temps.pl
5 | #
6 | # Displays CPU and drive temperatures
7 | #
8 | # Drive information reported includes the device ID, temperature, capacity, type
9 | # (SDD or HDD), serial number, model, and, if available, the model family.
10 | #
11 | # Optionally uses IPMI to report CPU temperatures. Otherwise, these are pulled
12 | # from sysctl. IPMI is more accurate in that it reports the temperature of each
13 | # socketed CPU in the system, even on virtualized instances, whereas the CPU
14 | # temperatures typically aren't available from sysctl in this case.
15 | #
16 | # Requires the smartmontools, available at: https://www.smartmontools.org/
17 | #
18 | # Keith Nash, July 2017
19 | #
20 | ###############################################################################
21 |
22 | use strict;
23 | use warnings;
24 |
25 | # Get system's hostname:
26 |
27 | my $hostname = qx(hostname);
28 | chomp($hostname);
29 |
30 | # Full path to the smartctl program:
31 |
32 | my $smartctl = "/usr/local/sbin/smartctl";
33 |
34 | # IPMI setup:
35 |
36 | # Toggle IPMI support on or off:
37 | # 1 = on: use IPMI
38 | # 0 = off: use sysctl instead of IPMI
39 |
40 | my $useipmi = 0;
41 |
42 | # IPMI username and password file. The password file is a text file with the
43 | # IPMI user's password on the first line. Be sure to set permissions to 0600
44 | # on the password file.
45 | #
46 | # You may not need credentials on some systems. In this case, ignore these
47 | # variables and modify the ipmitool variable below to suit your environment,
48 | # removing the '-I lanplus' and user credential options (-U and -f) as needed.
49 |
50 | my $ipmiuser = "root";
51 | my $ipmipwfile = "/root/ipmi_password";
52 |
53 | # The IPMI host must be either an IP address or a DNS-resolvable hostname. If you
54 | # have multiple systems, leave the variable blank and edit the conditional below
55 | # to specify the IPMI host according to the host on which you are running the script:
56 |
57 | my $ipmihost = "";
58 |
59 | if ($useipmi && $ipmihost eq "")
60 | {
61 | if ($hostname =~ /bandit/)
62 | {
63 | $ipmihost="falcon.ipmi.spearfoot.net"
64 | }
65 | elsif ($hostname =~ /boomer/)
66 | {
67 | $ipmihost="felix.ipmi.spearfoot.net"
68 | }
69 | elsif ($hostname =~ /bacon/)
70 | {
71 | $ipmihost="fritz.ipmi.spearfoot.net"
72 | }
73 | else
74 | {
75 | die "No IPMI host specified!\n"
76 | }
77 | }
78 |
79 | # Full path to ipmitool program, including options and credentials:
80 |
81 | my $ipmitool = "/usr/local/bin/ipmitool -I lanplus -H $ipmihost -U $ipmiuser -f $ipmipwfile";
82 |
83 | main();
84 |
85 | ###############################################################################
86 | #
87 | # main
88 | #
89 | ###############################################################################
90 | sub main
91 | {
92 | printf("==========\n\n");
93 |
94 | if ($useipmi)
95 | {
96 | printf("%s (IPMI host: %s)\n\n",$hostname,$ipmihost);
97 | }
98 | else
99 | {
100 | printf("%s\n\n",$hostname);
101 | }
102 |
103 | display_cpu_temps();
104 | display_drive_info();
105 | }
106 |
107 | ###############################################################################
108 | #
109 | # display_cpu_temps
110 | #
111 | ###############################################################################
112 | sub display_cpu_temps
113 | {
114 | my $temp;
115 | my $cpucores=0;
116 |
117 | if ($useipmi)
118 | {
119 | $cpucores = qx($ipmitool sdr | grep -c -i "cpu.*temp");
120 | }
121 | else
122 | {
123 | $cpucores = qx(sysctl -n hw.ncpu);
124 | }
125 |
126 | printf("=== CPU (%d) ===\n",$cpucores);
127 |
128 | if ($useipmi)
129 | {
130 | if ($cpucores > 1)
131 | {
132 | for (my $core=1; $core <= $cpucores; $core++)
133 | {
134 | $temp=qx($ipmitool sdr | grep -i "CPU$core Temp" | awk '{print \$4}');
135 | chomp($temp);
136 | printf("CPU %2u: %3sC\n",$core,$temp);
137 | }
138 | }
139 | else
140 | {
141 | $temp=qx($ipmitool sdr | grep -i "CPU Temp" | awk '{print \$4}');
142 | chomp($temp);
143 | printf("CPU %2u: %3sC\n",1,$temp);
144 | }
145 | }
146 | else
147 | {
148 | for (my $core=0; $core < $cpucores; $core++)
149 | {
150 | $temp = qx(sysctl -n dev.cpu.$core.temperature);
151 | $temp =~ s/[^\-[:digit:]\.]//g;
152 | chomp($temp);
153 | if ($temp <= 0)
154 | {
155 | printf("CPU %2u: -N/A-\n",$core);
156 | }
157 | else
158 | {
159 | printf("CPU %2u: %3sC\n",$core,$temp);
160 | }
161 | }
162 | }
163 | }
164 |
165 | ###############################################################################
166 | #
167 | # display_drive_info
168 | #
169 | ###############################################################################
170 | sub display_drive_info
171 | {
172 | my $drive_id;
173 | my $drive_model;
174 | my $drive_family;
175 | my $drive_serial;
176 | my $drive_capacity;
177 | my $drive_temp;
178 | my $drive_is_ssd;
179 | my $drive_family_display;
180 |
181 | printf("\n=== Drives ===\n");
182 |
183 | my @smart_drive_list = get_smart_drives();
184 |
185 | foreach my $drive (@smart_drive_list)
186 | {
187 | ($drive_model, $drive_family, $drive_serial, $drive_capacity, $drive_temp, $drive_is_ssd) = get_drive_info($drive);
188 |
189 | if ($drive =~ /\/dev\/(.*)/)
190 | {
191 | $drive_id = $1;
192 | }
193 | else
194 | {
195 | $drive_id = $drive;
196 | }
197 |
198 | if ($drive_family eq "")
199 | {
200 | $drive_family_display = "";
201 | }
202 | else
203 | {
204 | $drive_family_display = "(" . $drive_family . ")";
205 | }
206 |
207 | printf("%6.6s: %3uC [%8.8s %s] %-20.20s %s %s\n",
208 | $drive_id,
209 | $drive_temp,
210 | $drive_capacity,
211 | $drive_is_ssd ? "SSD" : "HDD",
212 | $drive_serial,
213 | $drive_model,
214 | $drive_family_display);
215 | }
216 | }
217 |
218 | ###############################################################################
219 | #
220 | # get_smart_drives
221 | #
222 | ###############################################################################
223 | sub get_smart_drives
224 | {
225 | my @retval = ();
226 | my @drive_list = split(" ", qx($smartctl --scan | awk '{print \$1}'));
227 |
228 | foreach my $drive (@drive_list)
229 | {
230 | my $smart_enabled = qx($smartctl -i $drive | grep "SMART support is: Enabled" | awk '{print \$4}');
231 | chomp($smart_enabled);
232 | if ($smart_enabled eq "Enabled")
233 | {
234 | push @retval, $drive;
235 | }
236 | }
237 |
238 | return @retval;
239 | }
240 |
241 | ###############################################################################
242 | #
243 | # get_drive_info
244 | #
245 | ###############################################################################
246 | sub get_drive_info
247 | {
248 | my $drive = shift;
249 | my $smart_data = qx($smartctl -a $drive);
250 |
251 | my $drive_model = "";
252 | my $drive_family = "";
253 | my $drive_serial = "";
254 | my $drive_capacity = "";
255 | my $drive_temp = 0;
256 | my $drive_is_ssd = 0;
257 |
258 | $drive_temp = get_drive_temp($drive);
259 |
260 | # Serial number
261 | if ($smart_data =~ /^Serial Number:\s*(.*)\s/m)
262 | {
263 | $drive_serial = $1;
264 | }
265 |
266 | # Device model
267 | if ($smart_data =~ /^Device Model:\s*(.*)\s/m)
268 | {
269 | $drive_model = $1;
270 | }
271 |
272 | # Model family
273 | if ($smart_data =~ /^Model Family:\s*(.*)\s/m)
274 | {
275 | $drive_family = $1;
276 | }
277 |
278 | # User capacity
279 | if ($smart_data =~ /^User Capacity:.*\[(.*)\]\s/m)
280 | {
281 | $drive_capacity = $1;
282 | }
283 |
284 | # Determine if drive is a SSD
285 | if ($smart_data =~ /^Rotation Rate:[ ]*Solid State Device/m)
286 | {
287 | $drive_is_ssd = 1;
288 | }
289 | elsif ($smart_data =~ /^[ 0-9]{3} Unknown_SSD_Attribute/m)
290 | {
291 | $drive_is_ssd = 1;
292 | }
293 | elsif ($smart_data =~ /^[ 0-9]{3} Wear_Leveling_Count/m)
294 | {
295 | $drive_is_ssd = 1;
296 | }
297 | elsif ($smart_data =~ /^[ 0-9]{3} Media_Wearout_Indicator/m)
298 | {
299 | $drive_is_ssd = 1;
300 | }
301 | elsif ($drive_family =~ /SSD/)
302 | {
303 | # Model family indicates SSD
304 | $drive_is_ssd = 1;
305 | }
306 |
307 | return ($drive_model, $drive_family, $drive_serial, $drive_capacity, $drive_temp, $drive_is_ssd);
308 | }
309 |
310 | ###############################################################################
311 | #
312 | # get_drive_temp
313 | #
314 | ###############################################################################
315 | sub get_drive_temp
316 | {
317 | my $drive = shift;
318 | my $retval = 0;
319 |
320 | $retval = qx($smartctl -A $drive | grep "194 Temperature" | awk '{print \$10}');
321 |
322 | if (!$retval)
323 | {
324 | $retval = qx($smartctl -A $drive | grep "190 Airflow_Temperature" | awk '{print \$10}');
325 | }
326 |
327 | return $retval;
328 | }
329 |
330 |
331 |
--------------------------------------------------------------------------------
/smart_report.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | ### Parameters ###
4 |
5 | # Specify your email address here:
6 | email=""
7 |
8 | # Full path to 'smartctl' program:
9 | smartctl=/usr/local/sbin/smartctl
10 |
11 | freenashost=$(hostname -s | tr '[:lower:]' '[:upper:]')
12 | boundary="===== MIME boundary; FreeNAS server ${freenashost} ====="
13 | logfile="smart_report.tmp"
14 | subject="SMART Status Report for ${freenashost}"
15 | tempWarn=40
16 | tempCrit=45
17 | sectorsCrit=10
18 | testAgeWarn=1
19 | warnSymbol="?"
20 | critSymbol="!"
21 | Drive_count=0
22 | SATA_count=0
23 | SAS_count=0
24 | Drive_list=""
25 | SATA_list=""
26 | SAS_list=""
27 |
28 | # Get list of SMART-enabled drives
29 | get_smart_drives()
30 | {
31 | gs_drives=$("$smartctl" --scan | awk '{print $1}')
32 | for gs_drive in $gs_drives; do
33 | gs_smart_flag=$("$smartctl" -i "$gs_drive" | grep -E "SMART support is:[[:blank:]]+Enabled" | awk '{print $4}')
34 | if [ "$gs_smart_flag" = "Enabled" ]; then
35 | Drive_list="$Drive_list $gs_drive"
36 | Drive_count=$((Drive_count + 1))
37 | fi
38 | done
39 | }
40 |
41 | # Get list of SATA disks, including older drives that only report an ATA version
42 | get_sata_drives()
43 | {
44 | for drive in $Drive_list; do
45 | lFound=0
46 | gsata_smart_flag=$("$smartctl" -i "$drive" | grep -E "SATA Version is:[[:blank:]]" | awk '{print $4}')
47 | if [ "$gsata_smart_flag" = "SATA" ]; then
48 | lFound=$((lFound + 1))
49 | else
50 | gsata_smart_flag=$("$smartctl" -i "$drive" | grep -E "ATA Version is:[[:blank:]]" | awk '{print $1}')
51 | if [ "$gsata_smart_flag" = "ATA" ]; then
52 | lFound=$((lFound + 1))
53 | fi
54 | fi
55 | if [ $lFound -gt 0 ]; then
56 | SATA_list="$SATA_list $drive"
57 | SATA_count=$((SATA_count + 1))
58 | fi
59 | done
60 | }
61 |
62 | # Get list of SAS disks
63 | get_sas_drives()
64 | {
65 | for drive in $Drive_list; do
66 | gsas_smart_flag=$("$smartctl" -i "$drive" | grep -E "Transport protocol:[[:blank:]]+SAS" | awk '{print $3}')
67 | if [ "$gsas_smart_flag" = "SAS" ]; then
68 | SAS_list="$SAS_list $drive"
69 | SAS_count=$((SAS_count + 1))
70 | fi
71 | done
72 | }
73 |
74 | ### Fetch drive lists ###
75 | get_smart_drives
76 | get_sata_drives
77 | get_sas_drives
78 |
79 | ### Set email headers ###
80 | printf "%s\n" "To: ${email}
81 | Subject: ${subject}
82 | Mime-Version: 1.0
83 | Content-Type: multipart/mixed; boundary=\"$boundary\"
84 |
85 | --${boundary}
86 | Content-Type: text/html; charset=\"US-ASCII\"
87 | Content-Transfer-Encoding: 7bit
88 | Content-Disposition: inline
89 | " > ${logfile}
90 |
91 | if [ $Drive_count -eq 0 ]; then
92 | echo "##### No SMART-enabled disks found on this system #####" >> "$logfile"
93 | fi
94 |
95 | ###### Summary for SATA drives ######
96 | if [ $SATA_count -gt 0 ]; then
97 | (
98 | echo "########## SMART status report summary for all SATA drives on server ${freenashost} ##########"
99 | echo ""
100 | echo "+-------+------------------------+----+------+-----+-----+-------+-------+--------+------+----------+------+-----------+----+"
101 | echo "|Device |Serial |Temp| Power|Start|Spin |ReAlloc|Current|Offline |Seek |Total |High | Command|Last|"
102 | echo "| |Number | | On |Stop |Retry|Sectors|Pending|Uncorrec|Errors|Seeks |Fly | Timeout|Test|"
103 | echo "| | | | Hours|Count|Count| |Sectors|Sectors | | |Writes| Count |Age |"
104 | echo "+-------+------------------------+----+------+-----+-----+-------+-------+--------+------+----------+------+-----------+----+"
105 | ) >> "$logfile"
106 |
107 | ###### Detail information for each SATA drive ######
108 | for drive in $SATA_list; do
109 | (
110 | devid=$(basename "$drive")
111 | lastTestHours=$("$smartctl" -l selftest "$drive" | grep "# 1" | awk '{print $9}')
112 | "$smartctl" -A -i -v 7,hex48 "$drive" | \
113 | awk -v device="$devid" -v tempWarn="$tempWarn" -v tempCrit="$tempCrit" -v sectorsCrit="$sectorsCrit" \
114 | -v testAgeWarn="$testAgeWarn" -v warnSymbol="$warnSymbol" -v critSymbol="$critSymbol" \
115 | -v lastTestHours="$lastTestHours" '
116 | /Serial Number:/{serial=$3}
117 | /190 Airflow_Temperature/{temp=$10}
118 | /190 Temperature_Case/{temp=$10}
119 | /194 Temperature/{temp=$10}
120 | /Power_On_Hours/{split($10,a,"+");sub(/h/,"",a[1]);onHours=a[1];}
121 | /Power_Cycle_Count/{startStop=$10}
122 | /Start_Stop_Count/{startStop=$10}
123 | /Spin_Retry_Count/{spinRetry=$10}
124 | /Reallocated_Sector/{reAlloc=$10}
125 | /Current_Pending_Sector/{pending=$10}
126 | /Offline_Uncorrectable/{offlineUnc=$10}
127 | /Seek_Error_Rate/{seekErrors=("0x" substr($10,3,4));totalSeeks=("0x" substr($10,7))}
128 | /High_Fly_Writes/{hiFlyWr=$10}
129 | /Command_Timeout/{cmdTimeout=$10}
130 | END {
131 | testAge=sprintf("%.0f", (onHours - lastTestHours) / 24);
132 | if (temp > tempCrit || reAlloc > sectorsCrit || pending > sectorsCrit || offlineUnc > sectorsCrit)
133 | device=device " " critSymbol;
134 | else if (temp > tempWarn || reAlloc > 0 || pending > 0 || offlineUnc > 0 || testAge > testAgeWarn)
135 | device=device " " warnSymbol;
136 | seekErrors=sprintf("%d", seekErrors);
137 | totalSeeks=sprintf("%d", totalSeeks);
138 | if (totalSeeks == "0") {
139 | seekErrors="N/A";
140 | totalSeeks="N/A";
141 | }
142 | if (temp > tempWarn || temp > tempCrit) temp=temp"*"
143 | if (reAlloc > 0 || reAlloc > sectorsCrit) reAlloc=reAlloc"*"
144 | if (pending > 0 || pending > sectorsCrit) pending=pending"*"
145 | if (offlineUnc > 0 || offlineUnc > sectorsCrit) offlineUnc=offlineUnc"*"
146 | if (testAge > testAgeWarn) testAge=testAge"*"
147 | if (hiFlyWr == "") hiFlyWr="N/A";
148 | if (cmdTimeout == "") cmdTimeout="N/A";
149 | printf "|%-7s|%-24s|%-4s|%6s|%5s|%5s|%7s|%7s|%8s|%6s|%10s|%6s|%11s|%4s|\n",
150 | device, serial, temp, onHours, startStop, spinRetry, reAlloc, pending, offlineUnc,
151 | seekErrors, totalSeeks, hiFlyWr, cmdTimeout, testAge;
152 | }'
153 | ) >> "$logfile"
154 | done
155 | (
156 | echo "+-------+------------------------+----+------+-----+-----+-------+-------+--------+------+----------+------+-----------+----+"
157 | ) >> "$logfile"
158 | fi
159 |
160 | ###### Summary for SAS drives ######
161 | if [ $SAS_count -gt 0 ]; then
162 | (
163 | if [ $SATA_count -gt 0 ]; then
164 | echo ""
165 | fi
166 |
167 | echo "########## SMART status report summary for all SAS drives on server ${freenashost} ##########"
168 | echo ""
169 | echo "+-------+------------------------+----+------+-----+------+------+------+------+------+------+----+"
170 | echo "|Device |Serial |Temp| Power|Start|Load |Defect|Uncorr|Uncorr|Uncorr|Non |Last|"
171 | echo "| |Number | | On |Stop |Unload|List |Read |Write |Verify|Medium|Test|"
172 | echo "| | | | Hours|Count|Count |Elems |Errors|Errors|Errors|Errors|Age |"
173 | echo "+-------+------------------------+----+------+-----+------+------+------+------+------+------+----+"
174 | ) >> "$logfile"
175 |
176 | ###### Detail information for each SAS drive ######
177 | for drive in $SAS_list; do
178 | (
179 | devid=$(basename "$drive")
180 | lastTestHours=$("$smartctl" -l selftest "$drive" | grep "# 1" | awk '{print $7}')
181 | "$smartctl" -x "$drive" | \
182 | awk -v device="$devid" -v tempWarn="$tempWarn" -v tempCrit="$tempCrit" \
183 | -v warnSymbol="$warnSymbol" -v critSymbol="$critSymbol" \
184 | -v lastTestHours="$lastTestHours" -v testAgeWarn="$testAgeWarn" '
185 | /Serial number:/{serial=$3}
186 | /Current Drive Temperature:/{temp=$4}
187 | /start-stop cycles:/{startStop=$4}
188 | /load-unload cycles:/{loadUnload=$4}
189 | /grown defect list:/{defectList=$6}
190 | /read:/{readErrors=$8}
191 | /write:/{writeErrors=$8}
192 | /verify:/{verifyErrors=$8}
193 | /Non-medium error count:/{nonMediumErrors=$4}
194 | /Accumulated power on time/{split($6,a,":");sub(/h/,"",a[1]);onHours=a[1];}
195 | END {
196 | testAge=sprintf("%.0f", (onHours - lastTestHours) / 24);
197 | if (temp > tempCrit)
198 | device=device " " critSymbol;
199 | else if (temp > tempWarn || testAge > testAgeWarn)
200 | device=device " " warnSymbol;
201 | if (testAge > testAgeWarn) testAge=testAge"*"
202 | if (defectList > 0) defectList=defectList"*"
203 | printf "|%-7s|%-24s| %3s|%6s|%5s|%6s|%6s|%6s|%6s|%6s|%6s|%4s|\n",
204 | device, serial, temp, onHours, startStop, loadUnload, defectList, \
205 | readErrors, writeErrors, verifyErrors, nonMediumErrors,testAge;
206 | }'
207 | ) >> "$logfile"
208 | done
209 | (
210 | echo "+-------+------------------------+----+------+-----+------+------+------+------+------+------+----+"
211 | ) >> "$logfile"
212 | fi
213 |
214 | if [ $SATA_count -gt 0 ] || [ $SAS_count -gt 0 ]; then
215 |
216 | ###### Emit SATA drive information ######
217 | for drive in $SATA_list; do
218 | vendor=$("$smartctl" -i "$drive" | grep "Vendor:" | awk '{print $NF}')
219 | if [ -z "$vendor" ]; then
220 | dfamily=$("$smartctl" -i "$drive" | grep "Model Family" | awk '{print $3, $4, $5, $6, $7}' | sed -e 's/[[:space:]]*$//')
221 | dmodel=$("$smartctl" -i "$drive" | grep "Device Model" | awk '{print $3, $4, $5, $6, $7}' | sed -e 's/[[:space:]]*$//')
222 | if [ -z "$dfamily" ]; then
223 | dinfo=$dmodel
224 | else
225 | dinfo="$dfamily ($dmodel)"
226 | fi
227 | else
228 | product=$("$smartctl" -i "$drive" | grep "Product:" | awk '{print $NF}')
229 | revision=$("$smartctl" -i "$drive" | grep "Revision:" | awk '{print $NF}')
230 | dinfo="$vendor $product $revision"
231 | fi
232 | serial=$("$smartctl" -i "$drive" | grep "Serial Number" | awk '{print $3}')
233 | (
234 | echo ""
235 | echo "########## SATA drive $drive Serial: $serial"
236 | echo "########## ${dinfo}"
237 | "$smartctl" -n never -H -A -l error "$drive"
238 | "$smartctl" -n never -l selftest "$drive" | grep "# 1 \\|Num" | cut -c6-
239 | ) >> "$logfile"
240 | done
241 |
242 | ###### Emit SAS drive information ######
243 | for drive in $SAS_list; do
244 | devid=$(basename "$drive")
245 | brand=$("$smartctl" -i "$drive" | grep "Product" | sed "s/^.* //")
246 | serial=$("$smartctl" -i "$drive" | grep "Serial number" | sed "s/^.* //")
247 | (
248 | echo ""
249 | echo "########## SMART status for SAS drive $drive $serial (${brand}) ##########"
250 | "$smartctl" -n never -H -A -l error "$drive"
251 | "$smartctl" -n never -l selftest "$drive" | grep "# 1 \\|Num" | cut -c6-
252 | ) >> "$logfile"
253 | done
254 | fi
255 |
256 | sed -i '' -e '/smartctl 7.*/d' "$logfile"
257 | sed -i '' -e '/smartctl 6.*/d' "$logfile"
258 | sed -i '' -e '/smartctl 5.*/d' "$logfile"
259 | sed -i '' -e '/smartctl 4.*/d' "$logfile"
260 | sed -i '' -e '/Copyright/d' "$logfile"
261 | sed -i '' -e '/=== START OF READ/d' "$logfile"
262 | sed -i '' -e '/SMART Attributes Data/d' "$logfile"
263 | sed -i '' -e '/Vendor Specific SMART/d' "$logfile"
264 | sed -i '' -e '/SMART Error Log Version/d' "$logfile"
265 |
266 | printf "%s\n" "
267 | --${boundary}--" >> ${logfile}
268 |
269 | ### Send report ###
270 | if [ -z "${email}" ]; then
271 | echo "No email address specified, information available in ${logfile}"
272 | else
273 | sendmail -t -oi < "$logfile"
274 | rm "$logfile"
275 | fi
276 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FreeNAS/TrueNAS Scripts
2 | Handy shell and Perl scripts for use on FreeNAS and TrueNAS servers
3 |
4 | Most of the shell scripts here are my versions of the useful scripts available at the ["Scripts to report SMART, ZPool and UPS status, HDD/CPU T°, HDD identification and backup the config"](https://forums.freenas.org/index.php?threads/scripts-to-report-smart-zpool-and-ups-status-hdd-cpu-t%C2%B0-hdd-identification-and-backup-the-config.27365/) thread on the FreeNAS forum. The original author is FreeNAS forum member BiduleOhm, with others contributing suggestions and code changes. I have modified the syntax and made minor changes in formatting and spacing of the generated reports.
5 |
6 | I used the excellent shell script static analysis tool at https://www.shellcheck.net to insure that all of the code is POSIX-compliant and free of issues. But this doesn't mean you won't find any errors. ☺️
7 |
8 | All of the Perl code is my own contribution.
9 | ***
10 | #### Operating System Compatibility
11 |
12 | Tested under:
13 | * TrueNAS 12.0 (FreeBSD 12.2)
14 | * FreeNAS 11.3 (FreeBSD 11.3-STABLE)
15 | * FreeNAS 11.2 (FreeBSD 11.2-STABLE)
16 |
17 | Earlier versions of FreeNAS were supported, but are no longer tested.
18 |
19 | ***
20 | # smart_report.sh
21 |
22 | Generates and emails you a status report with detailed SMART information about your system's SATA and SAS drives. A hearty thanks to contributor marrobHD for help in adding SAS support.
23 |
24 | You will need to edit the script and enter your email address before using it.
25 |
26 | NOTE: Users of some HBA controllers may need to change the SMARTCTL call, adding a device specifier. (Hat tip to commenter Tuplink for pointing this out).
27 |
28 | Example: for a 3ware controller, edit the script to invoke SMARTCTL like this:
29 |
30 | ```
31 | "${smartctl}" [options] -d 3ware,"${drive}" /dev/twa0
32 | ```
33 | ...instead of...
34 | ```
35 | "${smartctl}" [options] -d /dev/"${drive}"
36 | ```
37 | Refer to the SMARTCTL man page for addtional details, including support for other controller types.
38 | ***
39 | # zpool_report.sh
40 |
41 | Generates and emails you a status report about your system's pools.
42 |
43 | You will need to edit the script and enter your email address before using it.
44 | ***
45 | # ups_report.sh
46 | Generates and emails you a status report about your UPS.
47 |
48 | You will need to edit the script and enter your email address before using it. You may also have the report include all of the available UPSC variables by setting the `senddetail` variable to a value greater than zero.
49 | ***
50 | # save_config.sh
51 |
52 | Saves your TrueNAS/FreeNAS system configuration files to a dataset you specify, by creating a tarball containing the SQLite configuration database (_freenas-v1.db_) and password secret seed encryption file (_pwenc_secret_). The tarball is suitable for use in restoring the configuration on TrueNAS/FreeNAS systems.
53 |
54 | **!!! Security Warning !!!**
55 | > The system configuration and password secret seed encryption file are sensitive information and should be stored on a dataset available only to system administrators!
56 |
57 | The backup database and tarball filenames are formed from the hostname, complete TrueNAS/FreeNAS version, date, and _tar_ or _db_ extension, in this format: _hostname-version-date.extension_. Here are examples from a recent backup on my server named _brutus_:
58 |
59 | ```
60 | brutus-FreeNAS-11.2-U8-06e1172340-20210806114838.tar
61 | brutus-FreeNAS-11.2-U8-06e1172340-20210806114838.db
62 | ```
63 |
64 | Edit the script and set variable `configdir` to specify the configuration directory, a dataset where you want the backup files stored.
65 |
66 | Optional features:
67 | * Specify your email address in variable `notifyemail` to receive notification messages whenever the script executes.
68 | * Specify your ESXi short hostname in variable `esxihost` to backup the ESXi server configuration file. These backup filenames are formed from the hostname and date in this format: _hostname-configBundle-date.tgz_. Here is an example from a recent backup on my ESXi server _frisco_, on which _brutus_ is a guest:
69 |
70 | ```
71 | frisco-configBundle-20210806114840.tgz
72 | ```
73 | Procedure:
74 | * Create backup of _/data/freenas-v1.db_ using the SQLite `.backup main` command with backup target _hostname-version-date.db_ in the configuration directory
75 | * Copy _/data/pwenc_secret_ to the configuration directory
76 | * Check integrity of the backup database with the SQLite `pragma integrity_check;` command
77 | * Copy the validated backup database to _freenas-v1.db_ in the configuration directory
78 | * Add _freenas-v1.db_ and _pwenc_secret_ to tar file _hostname-version-date.tar_ in the configuration directory
79 | * Optionally create ESXi configuration bundle in the configuration directory
80 | * Optionally send an email notification upon completion
81 |
82 | Note that each invocation of the script creates these files in the configuration directory:
83 | * _hostname-version-date.db_ : validated backup of configuration database _/data/freenas-v1.db_
84 | * _hostname-version-date.tar_ : tar file containing the above configuration database along with the password secret seed encryption file _pwenc_secret_.
85 | * _freenas-v1.db_ : copy of the validated backup configuration database above; over-written each time the script is executed
86 | * _pwenc_secret_ : copy of _/data/pwenc_secret_, over-written each time the script is executed
87 | ***
88 | # save_config_enc.sh
89 |
90 | Saves your FreeNAS system configuration and password secret seed files to a dataset you specify, optionally sending you an email message containing these files in an encrypted tarball.
91 |
92 | **!!! Security Warning !!!**
93 | > The system configuration and password secret seed encryption file are sensitive information and should be stored on a dataset available only to system administrators!
94 |
95 | Supports the versions of FreeNAS which use an SQLite-based configuration file: these include FreeNAS 9.x-12.x, and probably earlier versions as well.
96 |
97 | The backup configuration filenames are formed from the hostname, complete FreeNAS version, and date, in this format: _hostname-freenas_version-date.db_. Here is an example from a recent backup on my server named _bandit_:
98 |
99 | ```
100 | bandit-FreeNAS-11.0-RELEASE-a2dc21583-20170710234500.db
101 | ```
102 |
103 | Edit this script and set variable `configdir` to specify the target dataset where you want the backup files copied.
104 |
105 | Optional feature: Specify your email address and create a passphrase file to receive an email message whenever it executes. The script will create an encrypted tarball containing the configuration file and password secret seed files, which it will include with the email message as a MIME-encoded attachment.
106 |
107 | To enable this feature you must:
108 | * Edit the script and specify your email address in variable 'mail'
109 | * Create a passphrase file. By default, the script will look for a passphrase in `/root/config_passphrase`, but you may use any file location you prefer. This is a simple text file with a single line containing the passphrase you wish to use for encrypting/decrypting the configuration tarball. This file should be owned by `root` and you should secure it by setting its permissions to 0600 (owner read/write).
110 |
111 | The attachment filename is formed from the hostname, complete FreeNAS version, and date, in this format: _hostname-freenas_version-date.tar.gz.enc_. Here is an example from a recent backup on my server named _bandit_:
112 |
113 | ```
114 | bandit-FreeNAS-11.0-RELEASE-a2dc21583-20170710234500.tar.gz.enc
115 | ```
116 | The script uses `tar` to store the configuration and password secret seed files in a gzipped tarball, which it encrypts by calling `openssl`, using the passphrase you specified above. For FreeNAS versions prior to 12.x, this is the command used to encrypt the tarball:
117 |
118 | `openssl enc -e -aes-256-cbc -md sha512 -salt -S "$(openssl rand -hex 4)" -pass file:[passphrase_file] -in [tarball] -out [encrypted_tarball]`
119 |
120 | To decrypt the email attachment, use this command on your FreeNAS system:
121 |
122 | `openssl enc -d -aes-256-cbc -md sha512 -pass file:[passphrase_file] -in [encrypted_file] -out [unencrypted_file]`
123 |
124 | For version 12.x of FreeNAS we add the new OpenSSL v1.1.1 options `-pbkdf2` and `-iter` thus:
125 |
126 | `openssl enc -e -aes-256-cbc -md sha512 -pbkdf2 -iter 128000 -salt -S "$(openssl rand -hex 8)" -pass file:[passphrase_file] -in [tarball] -out [encrypted_tarball]`
127 |
128 | To decrypt the email attachment, use this command on your FreeNAS system:
129 |
130 | `openssl enc -d -aes-256-cbc -md sha512 -pbkdf2 -iter 128000 -pass file:[passphrase_file] -in [encrypted_file] -out [unencrypted_file]`
131 |
132 | In the above commands:
133 | * `passphrase_file` is a file containing the same passphrase you configured on your FreeNAS server
134 | * `encrypted_file` is your locally-saved copy of the email attachment
135 | * `unencrypted_file` is the unencrypted contents of the email attachment
136 | ***
137 | # set_hdd_erc.sh
138 |
139 | Sets the Error Recovery Control (aka SCTERC or TLER) read and write values on your system's hard drives. What is this? There is a good discussion in the ["Checking for TLER, ERC, etc. support on a drive"](https://forums.freenas.org/index.php?threads/checking-for-tler-erc-etc-support-on-a-drive.27126/) thread on the FreeNAS forum, and you can find more gory details in [this FAQ](https://www.smartmontools.org/wiki/FAQ#WhatiserrorrecoverycontrolERCandwhyitisimportanttoenableitfortheSATAdisksinRAID) at the [smartmontools.org](https://www.smartmontools.org) website. This key quote from the FAQ sums up why you want to set this up on your FreeNAS servers:
140 |
141 | >"It is best for ERC to be "enabled" when in a RAID array to prevent the recovery time from a disk read or write error from exceeding the RAID implementation's timeout threshold. If a drive times out, the hard disk will need to be manually re-added to the array, requiring a re-build and re-synchronization of the hard disk. Limiting the drives recovery timeout helps for improved error handling in the hardware or software RAID environments."
142 |
143 | By default, the script sets both the read and write timeout value to 7 seconds. You can change either or both of these values to better suit your environment.
144 |
145 | Some hard drives retain these values when powered down, but some do not - including the HGST 7K4000 drives I use in one of my systems. For this reason, I configure my FreeNAS servers to run `set_hdd_src.sh` as a post-init startup script.
146 | ***
147 | # get_hdd_temp.sh
148 |
149 | Displays the current temperature of your system's CPU and drives.
150 |
151 | By default, the script uses `sysctl` to determine the number of CPU cores and report their temperatures. This reports a temperature for each core on systems equipped with modern multi-core CPUs. The optional IPMI support, if enabled, reports a single temperature for each socketed CPU. The latter result is probably more useful for monitoring CPU status.
152 |
153 | To enable IPMI support, edit the script and:
154 | * Set the `use_ipmi` variable to `1`
155 | * Specify the IPMI host's IP address or DNS-resolvable hostname in the `ipmihost` variable.
156 | * Specify the IPMI username in the `ipmiuser` variable.
157 | * Specify the IPMI password file location in the `ipmipwfile` variable. This is a simple text file containing the IPMI user's password on a single line. You should protect this file by setting its permissions to 0600.
158 |
159 | Drive output includes: the device ID, temperature (in Centigrade), capacity, serial number, and drive family/model. Here is sample output from one of my systems equipped with dual CPUs, using the IPMI feature and with serial numbers obfuscated:
160 |
161 | ```
162 | === CPU (2) ===
163 | CPU 1: [35C]
164 | CPU 2: [38C]
165 |
166 | === DRIVES ===
167 | da1: 19C [8.58GB] SN9999999999999999 INTEL SSDSC2BA100G3L
168 | da2: 39C [4.00TB] SN9999999999999999 HGST Deskstar NAS (HGST HDN724040ALE640)
169 | da3: 36C [4.00TB] SN9999999999999999 HGST Deskstar NAS (HGST HDN724040ALE640)
170 | da4: 27C [240GB] SN9999999999999999 Intel 730 and DC S35x0/3610/3700 (INTEL SSDSC2BB240G4)
171 | da5: 27C [2.00TB] SN9999999999999999 Western Digital Green (WDC WD20EARX-00PASB0)
172 | da6: 28C [2.00TB] SN9999999999999999 Western Digital Red (WDC WD20EFRX-68EUZN0)
173 | da7: 19C [8.58GB] SN9999999999999999 INTEL SSDSC2BA100G3L
174 | da8: 31C [6.00TB] SN9999999999999999 Western Digital Black (WDC WD6001FZWX-00A2VA0)
175 | da9: 29C [2.00TB] SN9999999999999999 Western Digital Green (WDC WD20EARX-00PASB0)
176 | da10: 29C [2.00TB] SN9999999999999999 Western Digital Red (WDC WD20EFRX-68EUZN0)
177 | da11: 34C [4.00TB] SN9999999999999999 HGST HDN726040ALE614
178 | da12: 37C [4.00TB] SN9999999999999999 HGST HDN726040ALE614
179 | da13: 37C [4.00TB] SN9999999999999999 Western Digital Re (WDC WD4000FYYZ-01UL1B1)
180 | da14: 38C [4.00TB] SN9999999999999999 Western Digital Re (WDC WD4000FYYZ-01UL1B1)
181 | ```
182 | (Thanks to P. Robar for his helpful suggestions with respect to `sysctl` usage and the `get_smart_drives()` function.)
183 | ***
184 | # get-system-temps.pl
185 |
186 | Displays the current temperature of your system's CPU and drives.
187 |
188 | This is a Perl version of the `get_cpu_temp.sh` script above.
189 |
190 | By default, the script uses `sysctl` to determine the number of CPU cores and report their temperatures. This reports a temperature for each core on systems equipped with modern multi-core CPUs. The optional IPMI support, if enabled, reports a single temperature for each socketed CPU. The latter result is probably more useful for monitoring CPU status.
191 |
192 | To enable IPMI support, edit the script and:
193 | * Set the `$useipmi` variable to `1`
194 | * Specify the IPMI host's IP address or DNS-resolvable hostname in the `$ipmihost` variable.
195 | * Specify the IPMI username in the `$ipmiuser` variable.
196 | * Specify the IPMI password file location in the `$ipmipwfile` variable. This is a simple text file containing the IPMI user's password on a single line. You should protect this file by setting its permissions to 0600.
197 |
198 | Drive output includes: the device ID, temperature (in Centigrade), capacity, drive type (HDD or SDD), serial number, drive model, and (when available) the model family. Here is sample output from one of my systems equipped with dual CPUs, using the IPMI feature and with serial numbers obfuscated:
199 |
200 | ```
201 | ==========
202 |
203 | bandit.spearfoot.net (IPMI host: falcon.ipmi.spearfoot.net)
204 |
205 | === CPU (2) ===
206 | CPU 1: 35C
207 | CPU 2: 39C
208 |
209 | === Drives ===
210 | da1: 20C [ 8.58 GB SSD] SN999999999999999999 INTEL SSDSC2BA100G3L
211 | da2: 37C [ 4.00 TB HDD] SN999999999999999999 HGST HDN724040ALE640 (HGST Deskstar NAS)
212 | da3: 35C [ 4.00 TB HDD] SN999999999999999999 HGST HDN724040ALE640 (HGST Deskstar NAS)
213 | da4: 28C [ 240 GB SSD] SN999999999999999999 INTEL SSDSC2BB240G4 (Intel 730 and DC S35x0/3610/3700 Series SSDs)
214 | da5: 26C [ 2.00 TB HDD] SN999999999999999999 WDC WD20EARX-00PASB0 (Western Digital Green)
215 | da6: 28C [ 2.00 TB HDD] SN999999999999999999 WDC WD20EFRX-68EUZN0 (Western Digital Red)
216 | da7: 19C [ 8.58 GB SSD] SN999999999999999999 INTEL SSDSC2BA100G3L
217 | da8: 31C [ 6.00 TB HDD] SN999999999999999999 WDC WD6001FZWX-00A2VA0 (Western Digital Black)
218 | da9: 29C [ 2.00 TB HDD] SN999999999999999999 WDC WD20EARX-00PASB0 (Western Digital Green)
219 | da10: 28C [ 2.00 TB HDD] SN999999999999999999 WDC WD20EFRX-68EUZN0 (Western Digital Red)
220 | da11: 32C [ 4.00 TB HDD] SN999999999999999999 HGST HDN726040ALE614
221 | da12: 35C [ 4.00 TB HDD] SN999999999999999999 HGST HDN726040ALE614
222 | da13: 36C [ 4.00 TB HDD] SN999999999999999999 WDC WD4000FYYZ-01UL1B1 (Western Digital Re)
223 | da14: 37C [ 4.00 TB HDD] SN999999999999999999 WDC WD4000FYYZ-01UL1B1 (Western Digital Re)
224 | ```
225 |
226 |
227 |
--------------------------------------------------------------------------------