How to Create a Folder Monitoring Program Using clamdscan on macOS

This memorandum describes how to create a small folder monitoring program using clamdscan.
The program consists of the following six components:

  1) fswatch: A file change monitor
  2) ClamavSentry: Shell Script
  3) SentryAlert.scpt: AppleScript
      3-1) When using the "display notification" command...
      3-2) When using the "display dialog" command...
  4) org.clamav.sentry.plist: LaunchAgents plist
  5) SwiftBar: An application that puts the output in the menu bar
  6) ClamavSentry.10m.sh: SwiftBar plugin
      6-1) When using the "launchctl print" command...
      6-2) When using the "pgrep -f" command...

For instructions on how to build ClamAV, please refer to "How to build ClamAV® using the CMake Build System on macOS".

And Links…




1) fswatch: A file change monitor

fswatch is a file change monitor that receives notifications when the contents of the specified files or directories are modified.

1. Download the latest fswatch
cd ~
curl -LO https://github.com/emcrisostomo/fswatch/releases/download/1.21.0/fswatch-1.21.0.tar.gz
tar xzf fswatch-1.21.0.tar.gz

2. Configure fswatch
cd fswatch-1.21.0
./configure --prefix=/usr/local/fswatch --host=aarch64-apple-darwin`uname -r`

3. Compile and make check
make && make check

    "make check" will end up with as below:
============================================================================
Testsuite summary for fswatch 1.21.0
============================================================================
# TOTAL: 10
# PASS:  10
# SKIP:  0
# XFAIL: 0
# FAIL:  0
# XPASS: 0
# ERROR: 0
============================================================================

4. Install fswatch
sudo make install

5. Set search path
export PATH="/usr/local/fswatch/bin:$PATH"

6. Verify fswatch installed properly
which fswatch
/usr/local/fswatch/bin/fswatch

fswatch --version
fswatch 1.21.0
Copyright (C) 2013-2025 Enrico M. Crisostomo.
License GPLv3+: GNU GPL version 3 or later.




2) Create and Place ClamavSentry

ClamavSentry is a Shell Script that performs the following actions:
  ・Monitors the specified folders using fswatch that is a file change monitor.
  ・Notifies clamdscan whenever new files are created.
  ・If clamdscan detects any viruses, it will notify SentryAlert.scpt and sound an alert.

1. Create ClamavSentry
#!/usr/bin/env bash
#
# Description: Monitors the specified folder and passes detected changes to clamdscan.
#              If a virus is detected, it notifies SentryAlert.scpt and sounds an alert.
# Name       : ClamavSentry

# Path to fswatch
FSWATCH="/usr/local/fswatch/bin/fswatch"

# Path to clamdscan
CLAMDSCAN="/usr/local/clamXav/bin/clamdscan"

# Path to SentryAlert.scpt
APPLE_SCRIPT="$HOME/Library/ClamAV/ClamdScan/SentryAlert.scpt"

# Path to log file
LOG_FILE="$HOME/Library/Logs/ClamdScan.log"

# Path to audio file
AUDIO_FILE="$HOME/Library/ClamAV/ClamdScan/alert.aiff"

# Path to Watch directory
WATCH_DIRS=(
  "/Volumes/Data/Downloads/"
  "$HOME/Library/Mail/"
  "$HOME/Library/Mail Downloads/"
)

"$FSWATCH" --event IsFile Created -0 -r "${WATCH_DIRS[@]}" | while IFS= read -r -d '' changed_path; do
  scan_output="$("$CLAMDSCAN" --stdout --infected --no-summary -- "$changed_path" 2>&1 || true)"
  found_lines="$(printf '%s\n' "$scan_output" | grep 'FOUND' || true)"

  if [ -n "$found_lines" ]; then
    /usr/bin/osascript "$APPLE_SCRIPT" "$found_lines" "$LOG_FILE" & /usr/bin/afplay "$AUDIO_FILE"
  fi
done

2. Grant execute permission to ClamavSentry
chmod +x ClamavSentry

3. Place ClamavSentry in $HOME/Library/ClamAV/ ,for example.
$HOME/Library/ClamAV/ClamavSentry




3) Create and Place SentryAlert.scpt

SentryAlert.scpt is an AppleScript that displays "Notification Alert" or "Dialog Alert" and logs the event.

  3-1) When using the "display notification" command...

  1. Create SentryAlert.scpt
on run argv
	if (count of argv) < 2 then return
	
	set foundText to item 1 of argv
	set logFilePath to item 2 of argv
	
	set msg to "Infection Found:" & linefeed & foundText
	display notification msg with title "ClamAV Alert !"
	
	set ts to do shell script "date '+%Y-%m-%d %H:%M:%S'"
	set logBlock to "-------------------------------------- SCAN LOG --------------------------------------" & linefeed & ts & linefeed & msg & linefeed & "--------------------------------------------------------------------------------------" & linefeed & linefeed
	
	do shell script "printf %s " & quoted form of logBlock & " >> " & quoted form of logFilePath
end run

  2. Grant execute permission to SentryAlert.scpt
chmod +x SentryAlert.scpt

  3. Place SentryAlert.scpt in $HOME/Library/ClamAV/ClamdScan/ ,for example.
$HOME/Library/ClamAV/ClamdScan/SentryAlert.scpt


  4. The following "Notification Aleart" will appear with a warning sound and you will get "ClamdScan.log" as below.


"Notification Aleart" and "ClamdScan.log"
Screenshot



  3-2) When using the "display dialog" command...

  1. Create SentryAlert.scpt using "display dialog"
on run argv
	if (count of argv) < 2 then return
	
	set foundText to item 1 of argv
	set logFilePath to item 2 of argv
	
	set msg to "Infection Found:" & linefeed & foundText
	set iconPath to POSIX file "/Users/xxx/Library/ClamAV/ClamdScan/alert.png"
		
	display dialog msg with title "                                            ClamAV Alert !" with icon file iconPath buttons "OK" default button "OK"
	
	set ts to do shell script "date '+%Y-%m-%d %H:%M:%S'"
	set logBlock to "-------------------------------------- SCAN LOG --------------------------------------" & linefeed & ts & linefeed & msg & linefeed & "--------------------------------------------------------------------------------------" & linefeed & linefeed
	
	do shell script "printf %s " & quoted form of logBlock & " >> " & quoted form of logFilePath
	
	set savedDelimiters to AppleScript's text item delimiters
	set AppleScript's text item delimiters to "/"
	set parts to text items of foundText
	
	set folderParts to items 1 thru -2 of parts
	set AppleScript's text item delimiters to "/"
	set folderPath to folderParts as text
	set folderPath to folderPath & "/"
	
	set AppleScript's text item delimiters to savedDelimiters
	
	tell application "Finder"
		open folderPath as POSIX file
		activate
	end tell
end run

  2. Grant execute permission to SentryAlert.scpt
chmod +x SentryAlert.scpt

  3. Place SentryAlert.scpt in $HOME/Library/ClamAV/ClamdScan/ ,for example.
$HOME/Library/ClamAV/ClamdScan/SentryAlert.scpt


  4. The following "Dialog" will appear with a warning sound and you will get "ClamdScan.log" as below.
      Clicking the "OK" button will open a Finder window displaying the directory containing the infected files.


"Dialog" and "ClamdScan.log"
Screenshot





4) Create and Place org.clamav.sentry.plist

org.clamav.sentry.plist is a plist file for LaunchAgents that starts ClamavSentry using launchctl at login.


1. Create org.clamav.sentry.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>org.clamav.sentry</string>
	<key>Disabled</key>
	<false/>
	<key>KeepAlive</key>
	<dict>
		<key>PathState</key> 
		<dict>
		<key>/private/tmp/clamd.socket</key>
		<true/> 
		</dict>
	</dict>
	<key>ProgramArguments</key>
	<array>
		<string>/Users/xxx/Library/ClamAV/ClamavSentry</string>
	</array>
</dict>
</plist>

2. Place SentryAlert.scpt in $HOME/Library/LaunchAgents/.
$HOME/LaunchAgents/org.clamav.sentry.plist




5) Install SwiftBar

SwiftBar is an application that puts the output in the menu bar.

1. Download the latest SwiftBar
cd ~
curl -LO https://github.com/swiftbar/SwiftBar/releases/download/v2.0.1/SwiftBar.v2.0.1.b536.zip
unzip SwiftBar.v2.0.1.b536.zip -x "__MACOSX/*"

2. Place SwiftBar.app in /Applications/Utilities/ ,for example.
mv SwiftBar.app /Applications/Utilities/




6) Create and Place ClamavSentry.10m.sh

ClamavSentry.10m.sh is a SwiftBar plugin that displays the processes of ClamavSentry in the menu bar using SwiftBar.

1. Create ClamavSentry.10m.sh

  6-1) When using the "launchctl print" command...
#!/usr/bin/env bash
#
# Description      : Monitor the processes of ClamAvSentry and display them in the menu bar.
# Name             : ClamavSentry
# Refresh interval : 10 minutes
# File extension   : sh
#                    {ClamavSentry}.{10m}.{sh}
#
# SwiftBar Settings:
# <swiftbar.refreshOnOpen>true</swiftbar.refreshOnOpen>

LABEL="org.clamav.sentry"
FONT_SIZE=18
FONT_COLOR_ALIVE="#e4e4e4"
FONT_COLOR_DEAD="#fe0000"

if launchctl print "gui/$UID/$LABEL" >/dev/null 2>&1; then
  echo "ρ| size=$FONT_SIZE color=$FONT_COLOR_ALIVE"
else
  echo "χ| size=$FONT_SIZE color=$FONT_COLOR_DEAD"
fi


  6-2) When using the "pgrep -f" command...
#!/usr/bin/env bash
#
# Description      : Monitor the processes of ClamAvSentry and display them in the menu bar.
# Name             : ClamavSentry
# Refresh interval : 10 minutes
# File extension   : sh
#                    {ClamavSentry}.{10m}.{sh}
#
# SwiftBar Settings:
# <swiftbar.refreshOnOpen>true</swiftbar.refreshOnOpen>

PROCESS_NAME="ClamAvSentry"
FONT_SIZE=18
FONT_COLOR_ALIVE="#e4e4e4"
FONT_COLOR_DEAD="#fe0000"

if pgrep -f "$PROCESS_NAME" >/dev/null 2>&1; then
  echo "ρ| size=$FONT_SIZE color=$FONT_COLOR_ALIVE"
else
  echo "χ| size=$FONT_SIZE color=$FONT_COLOR_DEAD"
fi

2. Place ClamavSentry.10m.sh in $HOME/Library/Application Support/SwiftBar/plugins/.
$HOME/Library/Application Support/SwiftBar/plugins/ClamavSentry.10m.sh





"Menu bar Icons that shows the processes of ClamavSentry"

Screenshot
Links…

mail