ClamXav differential update

November 29, 2009

ClamXavClamXav is a free virus checker for Mac OS X. It uses the tried, tested and very popular ClamAV open source antivirus engine as a back end.

The default install of ClamXav does not enable the automatic virus definition update. When a user enables those automatic updates, a ‘good old cron job’ is created for that user. Three minor concerns with that method are:

  1. Each users on the system can enable the automatic updates leading to multiple redundant checks for new virus definitions
  2. The users do not have write access to ClamXav directories and are not able to create a temporary directory required for differential updates. Fortunately ClamXav fall back to a standard update where the entire virus definition file is downloaded.
  3. All the users who enable the automatic updates will get e-mails containing an error message like this one :
    clamxav ERROR: chdir_tmp: Can't create directory ./clamav-97e66bd7fbb

Only the _clamav user has write access to his directories. I’ve found several workarounds for this by googling but most of them were either not secure like setting the _clamav user’s directories world wide writable, either not elegant like putting the cron job in the root’s crontab…

The only elegant workaround I’ve found so far is still requiring some manual configuration but at least it uses Apple’s vision of daemon and recurrent tasks; it uses launchd. I won’t explain launchd here but Apple website or AFP548 website are very good start to understand its philosophy.

We will configure a launchd daemon to start the virus definition update every day at 21:30 as _clamav user. Launchd is configured via property list files (.plist) placed in appropriate directories. In this case we want the daemon to be started as long as the system is started (i.e. regardless if a user is logged or not). We will place our plist file in /Library/LaunchDaemons/.

 1. <?xml version="1.0" encoding="UTF-8"?>
 2. <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 3. <plist version="1.0">
 4.   <dict>
 5.     <key>Label</key>
 6.     <string>com.clamxav.freshclam</string>
 7.     <key>UserName</key>
 8.     <string>_clamav</string>
 9.     <key>LowPriorityIO</key>
10.     <true/>
11.     <key>Nice</key>
12.     <integer>1</integer>
13.     <key>Program</key>
14.     <string>/usr/local/clamXav/bin/freshclam</string>
15.     <key>ProgramArguments</key>
16.     <array>
17.       <string>/usr/local/clamXav/bin/freshclam</string>
18.       <string>--quiet</string>
19.       <string>--log=/usr/local/clamXav/share/clamav/freshclam.log</string>
20.     </array>
21.     <key>StartCalendarInterval</key>
22.     <dict>
23.       <key>Hour</key>
24.       <integer>21</integer>
25.       <key>Minute</key>
26.       <integer>30</integer>
27.     </dict>
28.   </dict>
29. </plist>

Lines 5-6 : define the daemon’s name
Lines 7-8 : define the user the daemon will run as
Lines 9-12 : reduce the update process priority as it is not required to run fast
Lines 13-20 : define the daemon’s executable and its parameters
Lines 21- 27 : schedule when the daemon must run

That plist file must be placed in /Library/LaunchDaemons/. It will be automatically loaded after each reboot but you can manually load it with launchctl if you don’t want to restart your computer.

$ sudo launchctl load /Library/LaunchDaemons/com.clamxav.freshclam.plist
$ sudo launchctl list | grep com.clamxav.freshclam
-	0	com.clamxav.freshclam

Now you can check in the log file (/usr/local/clamXav/share/clamav/freshclam.log) the error message has disappeared and differential update is now working fine…

ClamAV update process started at Sat Nov 28 21:30:09 2009
main.cvd is up to date (version: 51, sigs: 545035, f-level: 42, builder: sven)
Downloading daily-10091.cdiff [100%]
daily.cld updated (version: 10091, sigs: 115838, f-level: 44, builder: ccordes)
Database updated (660873 signatures) from database.clamav.net (IP: 193.1.193.64)
Clamd successfully notified about the update.

Thanks for leaving a comment if you found this useful