I had an emergency. The CPU usage of a certain mail server was raising, and the culprit was clamd.
For some reason, in the last few months, the CPU usage of clamd kept rising, and was now near 70% average of the server's CPU.
Removing the antivirus is, of course, not an option. On the other hand, performance was starting to suffer.
The usual response would be a full retooling of the setup, multiple SMTP servers handling the load against a central storage server, clamav running on each SMTP... but switching to that involves a full reimplementation of the system. Because of the antivirus??? Hell no.
So, I started investigating how I could move clamd to another box, like I did with spamassassin. It was not pretty.
clamav has a protocol defined for connecting to remote servers.
clamav doesn't have a client for it.
clamd-stream-client doesn't seem to work.
So, I thought... let's be original. What do I actually need?
I need to be able to call clamdscan, and have it scan the current folder. Based on its exit status code (0/1/2) the mail is accepted, rejected, temporarily rejected.
Having the same folders structure available to two boxes is trivial. I have NFS, lots of bandwidth and another computer.
Running clamdscan in the second box, scanning those folders is trivial too.
The missing piece is a way to tell the second box's clamd to scan, and get the exit code in the mail server.
Netpipes is software to "make TCP sockets usable from the shell". You can find it at http://web.purplefrog.com/~thoth/netpipes/netpipes.html.
And here's a replacement clamdscan which works the way I wanted it:
#!/bin/dash exit `echo \$PWD | hose 192.168.1.53 9000 --slave `
This version takes the folder you want to scan as an argument:
#!/bin/dash exit `echo \$* | hose 192.168.1.53 9000 --slave `
And here is the "server side". First netclam.sh:
#!/bin/dash -x read args /usr/bin/clamdscan \$args >/dev/null 2>&1 echo \$?
Then the "network code":
faucet 9000 --in --out /usr/bin/netclam.sh
And there you have it. ClamAV moved to another server. With 5 lines of shell code.