FreeBSD may be the hottest operating system available, but having hot hardware isn’t so good. Modern drives and CPUs can report their temperature, but it’s not easy to see it.
I’ve produced an example script that will report the temperature of whatever it can, with the idea that it can form the basis of whatever you really need to do. I have a multi-server monitoring system using the same methods.
Getting the CPU core temperature is a simple sysctl variable, but it only appears if you have the coretemp.ko module loaded or compiled into the kernel. But coretemp.ko only works for Intel processors; for AMD you need amdtemp.ko instead.
The script tries to determine the CPU type the best way I know how, and loads the appropriate module if necessary. If it loads the module, it unloads the module at the end to leave things as it found them. You can omit the unload so it’s loaded on first, or put the module permanently in loader.conf if you prefer. But this is only an example, and I prefer to keep my scripts independent of host configuration.
Next you need a way to get the temperature from all your drives. This isn’t built in to FreeBSD but you can use the excellent the excellent smartmontools https://www.smartmontools.org by Bruce Allen and Christian Franke.
This was originally intended to access the SMART reporting on ATA drives, but will now extract information for SAS units too. To smartmontools, and the “smartctl” utility in particular, you can build from ports with:
cd /usr/ports/sysutils/smartmontools
make install clean
You can also install it as a binary package with: “pkg install smartmontools”
The script tries to enumerate the drives and CPUs on the system. ATA drives are in /dev/ and begin “ada”, SCSI and USB drives begin “da”. The trick is to figure out which devices are drives and which are partitions or slices within a drive – I don’t have a perfect method.
SCSI drives that aren’t disks (i.e. tape) start “sa”, and return some really weird stuff when smartctl queries them. I’ve tested the script with standard LTO tape drives, but you’ll probably need to tweak it for other things. (Do let me know).
Figuring out the CPUs is tricky, as discrete CPUs and cores within a single chip appear the same. The script simply goes on the cores, which results in the same temperature being reported for each.
You can override any of the enumeration by simply assigning the appropriate devices to a list, but where’s the fun? Seriously, this example shows how you can enumerate devices and it’s useful when you’re monitoring disparate hosts using the same script.
Finally, there are three loops that read the temperature for each device type into into “temp” and then print it. Do whatever you want – call “shutdown -p now” if you think something’s too hot; autodial the fire brigade or, as I do, send yourself an email.
The Script
#!/bin/sh
# FreeBSD Temperature monitoring example script
# (c) FJL 2024 frank@fjl.co.uk
# Please feel free to use this as an example for a few techniques
# including enumerating devices on a host and extracting temperature
# information.
# Full path to utilities in case run with no PATH set
GREP=/usr/bin/grep SMARTCTL=/usr/local/sbin/smartctl
CUT=/usr/bin/cut
SYSCTL=/sbin/sysctl
# Load the AMD CPU monitoring driver if necessary
if [ ! $($SYSCTL -n dev.cpu.0.temperature 2>/dev/null) ]
then
# Let's try to find out if we have Intel or
# AMD processor and select correct kernel module
if $SYSCTL -n hw.model | $GREP AMD >/dev/null
then
tempmodule=amdtemp
else
tempmodule=coretemp
fi
# Load the CPU temp kernel module
kldload $tempmodule
# Set command to unload it when we're done (optional)
unload="kldunload $tempmodule"
fi
# Enumerate SATA, USB and SAS disks - everything
# in /dev/ starting da or ada, except where there's an s or p in it
disks=$(find /dev -depth 1 \( -name da\* -o -name ada\* \) -and -not -name \*s\* -and -not -name \*p\* | cut -c 6- | sort )
# Enumerate other SCSI devices, starting in sa.
# Normally tape drives. May need tweaking!
scsis=$(find /dev -depth 1 \( -name sa\* \) -and -not -name \*.ctl | cut -c 6- | sort)
# Enumerate the CPUs
cpus="$(seq 0 $(expr $($SYSCTL -n hw.ncpu ) - 1))"
# Print all the ATA disks
for disk in $disks
do
temp=$($SMARTCTL -a /dev/$disk | $GREP Temperature_Celsius | $CUT -w -f 10)
echo "$disk: ${temp}C" done
# Print all the SCSI devices (e.g. tapes)
# NB. This will probably take a lot of fiddling as SCSI units return all sorts to smartctl
# Note the -T verypermissive. see man smartctl for details.
for scsi in $scsis
do
temp=$($SMARTCTL -a -T verypermissive /dev/$scsi | $GREP "Current Drive Temperature" | cut -w -f 4)
echo "$scsi: ${temp}C"
done
# Print all the CPUs
for cpu in $cpus
do
temp=$($SYSCTL -n dev.cpu.$cpu.temperature | $CUT -f 1 -d .)
echo "CPU$cpu: ${temp}C"
done
# Unload the CPU temp kernel module if we loaded it (optional)
$unload
Example Output
ada0: 35C
ada1: 39C
ada2: 39C
ada3: 38C
da0: 24C
da1: 25C
sa0: 33C
CPU0: 48C
CPU1: 48C