shellfreebsdopnsense

Simplifying file comparison in a shell script


I have this shell running in OPNSense to check any new DHCP releases that don't match with the table and send an alert email. However, this code is very cpu consuming. I might try to put the changes of the given time period, and compare those.

Do you guys any have any better ideas?

#!/usr/local/bin/bash

# Define the location of the static DHCP release file
STATIC_DHCP_FILE="/var/dhcpd/etc/dhcpd.conf"

# Define the location of the new DHCP release file
NEW_DHCP_FILE="/var/log/dhcpd/latest.log"

# Define the subject of the email
SUBJECT="New DHCP Release Detected"

# Define the last line of the file
LAST_LINE=$(tail -n 1 "$NEW_DHCP_FILE")

# Create last line file
touch /tmp/last_match

while true; do
    # Check if the file has changed
    if ! cmp -s /tmp/last_match "$NEW_DHCP_FILE"; then # Use cmp instead of diff
        LAST_LINE=$(tail -n 1 "$NEW_DHCP_FILE")
        DHCP_MODE=$(echo "$LAST_LINE" | awk '{print $9}')
        if [ "$DHCP_MODE" == "DHCPOFFER" ]; then
            IP=$(echo "$LAST_LINE" | awk '{print $11}')
            MAC=$(echo "$LAST_LINE" | awk '{print $13}')
            ETH=$(echo "$LAST_LINE" | awk '{print $15}')
            if  ! grep -qi "$MAC" "$STATIC_DHCP_FILE"; then
        # Run the nmap command and store the output in a variable
        OUTPUT=$(nmap -sP "$IP")
        # Extract the hostname from the output using grep and cut
        HOSTNAME=$(echo "$OUTPUT" | grep -m 1 "Nmap scan report for" | cut -d " " -f 5)
                echo "$LAST_LINE" > /tmp/last_match
                BODY=`printf "A new DHCP release has been detected for an unknown device. The details are as follows:\n\nOn $(date)\nIP Address: $IP\nMAC Address: $MAC\nHostname: $HOSTNAME\n"`
                echo -e "$BODY"
                sleep 3
                python3 /root/sendmail.py -s "$SUBJECT" -a "$BODY"
            fi
        fi
    fi
done


Solution

  • Don't call awk multiple times when only one is needed.

    More importantly, don't busy-wait / poll. Just read new data when it arrives.

    For example:

    static_dhcp_file=/var/dhcpd/etc/dhcpd.conf
    new_dhcp_file=/var/log/dhcpd/latest.log
    
    subject="New DHCP Release Detected"
    
    tail -f -n1 "$new_dhcp_file" |
    while read _ _ _ _ _ _ _ _ mode _ ip _ mac _ eth _; do 
        if [[ $mode = DHCP_OFFER ]] && ! grep -qi $mac "$static_dhcp_file"; then
            host=$(nmap -sP $ip | awk '/Nmap scan report for/ { print $5; exit }')
            body=$(printf "A new DHCP release has been detected for an unknown device. The details are as follows:\n\nOn $(date)\nIP Address: ${ip}\nMAC Address: ${mac}\nHostname: ${host}\n")
            echo "$body"
            sleep 3
            python3 /root/sendmail.py -s "$subject" -a "$body"
        fi
    done
    

    Note that your original code could theoretically miss changes. If more than one new line is added to latest.log during your sleep 3, only the final one will be noticed.