pyClamd
pyClamd provides a Pythonic interface to the ClamAV daemon (clamd), allowing applications to scan files and streams for viruses. Currently at version 0.4.0, it offers a robust way to integrate anti-malware scanning into Python projects, with releases occurring as needed for bug fixes or minor enhancements.
Common errors
-
pyclamd.ClamdError: Could not connect to ClamAV daemon: [Errno 111] Connection refused
cause The ClamAV daemon (clamd) is either not running or not listening on the specified network host and port.fixStart the `clamd` service. Verify the daemon's configuration (`clamd.conf`) for `LocalSocket` or `TCPSocket` settings, and ensure the `pyclamd` client uses matching `host` and `port`. -
pyclamd.ClamdError: Could not connect to ClamAV daemon: [Errno 2] No such file or directory
cause The specified Unix socket path for `clamd` does not exist or is incorrect.fixCheck the `clamd.conf` for the `LocalSocket` path. Ensure `pyclamd` is configured with the correct `unix_socket` argument (e.g., `ClamdUnixSocket(unix_socket='/var/run/clamav/clamd.ctl')`). -
pyclamd.ClamdError: Could not connect to ClamAV daemon: [Errno 13] Permission denied
cause The user running the Python script does not have sufficient permissions to access the ClamAV daemon's Unix socket file.fixGrant read/write permissions for the socket file to the user running the script, or run the script as a user (e.g., `clamav`) that already has access. This often involves checking the permissions of `/var/run/clamav/clamd.ctl` and its parent directory.
Warnings
- gotcha The ClamAV daemon (clamd) must be running and accessible for `pyclamd` to function. `pyclamd` is just a client library and does not start the daemon.
- gotcha Scan results are dictionaries mapping paths/streams to tuples like `('FOUND', 'Virus.Name')` or `('CLEAN', None)`. They are not simple booleans.
- gotcha `ClamdAgnostic` attempts to connect via Unix socket first, then falls back to a network socket. If both are configured but only one is desired, use `ClamdUnixSocket` or `ClamdNetworkSocket` explicitly.
- gotcha When using Unix sockets, the user running the Python script must have read and write permissions to the ClamAV daemon's socket file.
Install
-
pip install pyclamd
Imports
- ClamdAgnostic
from pyclamd import Clamd
from pyclamd import ClamdAgnostic
- ClamdNetworkSocket
from pyclamd import ClamdNetworkSocket
- ClamdUnixSocket
from pyclamd import ClamdUnixSocket
Quickstart
import pyclamd
import os
# Ensure Clamd daemon is running and accessible (e.g., via network or Unix socket)
# For network socket: Host (default '127.0.0.1'), Port (default 3310)
# For Unix socket: Socket path (default '/var/run/clamav/clamd.ctl')
clamd_host = os.environ.get('CLAMD_HOST', '127.0.0.1')
clamd_port = int(os.environ.get('CLAMD_PORT', '3310'))
clamd_socket = os.environ.get('CLAMD_SOCKET', '/var/run/clamav/clamd.ctl') # Default for many Linux systems
try:
# Use ClamdAgnostic for automatic detection (Unix socket preferred if available)
# This will try UnixSocket first, then NetworkSocket
cd = pyclamd.ClamdAgnostic(
unix_socket=clamd_socket,
host=clamd_host,
port=clamd_port
)
# Ping the ClamAV daemon to check connectivity
cd.ping()
print("Successfully connected to ClamAV daemon.")
# EICAR test string (standard antivirus test file)
eicar_string = b"X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*"
# Scan a stream of bytes
result_stream = cd.scan_stream(eicar_string)
print(f"Scan stream result for EICAR: {result_stream}")
# Create a dummy file for file scan
test_file_path = "eicar_test.txt"
with open(test_file_path, "wb") as f:
f.write(eicar_string)
result_file = cd.scan_file(test_file_path)
print(f"Scan file result for '{test_file_path}': {result_file}")
if os.path.exists(test_file_path):
os.remove(test_file_path)
except pyclamd.ClamdError as e:
print(f"ClamAV daemon error: {e}")
print("Please ensure the ClamAV daemon (clamd) is running and accessible.")
print(f" Network: {clamd_host}:{clamd_port}")
print(f" Unix Socket: {clamd_socket}")
except Exception as e:
print(f"An unexpected error occurred: {e}")