ClamdConfig

1. Overview

The ClamdConfig component is a configuration model and persistence utility for managing ClamAV daemon settings (typically stored in clamd.conf). It provides a structured interface to:

  • Load and parse configuration files while preserving original formatting
  • Read and update key/value options (including boolean yes/no values)
  • Manage on-access scan include paths (OnAccessIncludePath)
  • Persist updates back to disk while retaining unmodified lines and comments
  • Create backups before applying changes

This module is designed for UI-driven configuration workflows (e.g., Settings pages) where maintaining the user’s original file structure and comments is important.


2. Data Model

Internally, the configuration is represented using two complementary structures:

  • rawLines – an ordered list of parsed lines (including comments and unknown entries) that preserves file structure
  • options – a key/value map containing the effective non-commented configuration values

Each entry in rawLines is represented by a Line structure containing:

  • text – original line string as read from file
  • key – parsed directive key (if detected)
  • value – parsed value (single token value)
  • commented – whether the line was originally commented out with #

This dual representation enables accurate round-trip save behavior while still supporting fast lookups and updates.


3. Configuration Loading

3.1 Parsing Strategy

The configuration is loaded via:

bool load(const std::string& path)

Load behavior:

  • Clears both rawLines and options to ensure a fresh model
  • Reads the file line-by-line
  • Detects comment prefix (#) and records commented=true
  • Parses the remaining content using a token-based approach:
    • key and value are extracted using istringstream (iss >> key >> value)
    • Only the first two tokens are captured (multi-token values are not expanded)
  • For non-commented parsed directives, updates options[key] = value
  • Always appends a Line record to rawLines to preserve full file content

If the file cannot be opened, load() returns false.


4. Persistence and Round-trip Save

4.1 Standard Save

Saving is performed via:

bool save(const std::string& path)

Save strategy:

  • Iterates through rawLines in original order
  • If a line has a parsed key and that key exists in options, the output line is rewritten using the current value
  • If the original line was commented, the # prefix is preserved
  • Lines not associated with tracked keys are written exactly as originally read (ln.text)

This approach preserves:

  • Unknown directives
  • Non-directive lines
  • Whitespace patterns embedded in unmodified lines
  • Commented-out directive lines (while still allowing their values to be updated if tracked)

If the output file cannot be opened, save() returns false.


4.2 Backup and Safe Save

ClamdConfig provides a backup mechanism:

bool backupFile(const std::string& path) const

Backup behavior:

  • If path exists, a copy is created at <path>.bak
  • Existing backups are overwritten
  • Exceptions are caught and reported to stderr (localized message)

A combined safe-save entry point is provided:

bool saveWithBackup(const std::string& path)

This performs:

  1. Backup creation (backupFile())
  2. Save execution (save())

If the backup step fails, persistence is aborted and an error is logged.


5. Option Accessors

5.1 Generic Options

The module exposes generic set/get operations:

  • void setOption(const std::string& key, const std::string& value)
  • std::string getOption(const std::string& key) const

Options are stored in the options map and represent the effective (non-commented) configuration values. Missing keys return an empty string.


5.2 Boolean Options

Boolean helpers provide yes/no semantics:

  • bool getOptionBool(const std::string& key) const
  • void setOptionBool(const std::string& key, bool enabled)

Normalization is handled by isYes(), which lowercases the value and treats the following as true:

  • yes
  • true
  • 1

All other values are treated as false.


6. On-access Include Path Management

6.1 Enumerating Paths

On-access monitored folders are represented by repeated directives:

OnAccessIncludePath <path>

The method:

std::vector<std::string> getOnAccessPaths() const

collects all non-commented occurrences of OnAccessIncludePath from rawLines.


6.2 Adding a Path

Adding a path is performed via:

void addOnAccessPath(const std::string& path)

Behavior:

  • Creates a new Line record with key OnAccessIncludePath and value set to the provided path
  • Appends it to rawLines (preserving existing entries)
  • Also sets options["OnAccessIncludePath"] = path

Note: because OnAccessIncludePath is a multi-occurrence directive, storing it in options as a single value implies that the map will hold only the last added instance. The authoritative multi-value representation is the rawLines list.


6.3 Removing a Path

Removal is performed via:

void removeOnAccessPath(const std::string& path)

This erases any line from rawLines where:

  • ln.key == "OnAccessIncludePath" and
  • ln.value == path

This operation updates the serialized output but does not remove any corresponding entry from options, because options is not a complete representation of multi-occurrence directives.


7. Advanced Scan Options Extraction

7.1 Enumerating yes/no Scan Options

The method:

std::map<std::string, bool> getScanOptions() const

extracts configuration directives that use explicit yes or no values. For each line:

  • If the line has a non-empty key and its value is exactly yes or no, it is included
  • The resulting boolean is true only if the line is not commented and the value is affirmative

This provides a convenient UI-ready model for rendering advanced boolean toggles without hardcoding option names.


7.2 Updating a Scan Option

Scan option updates are performed via:

void setScanOption(const std::string& key, bool enabled)

Behavior:

  • Updates options[key] to yes or no
  • Iterates over rawLines and rewrites matching key lines:
    • ln.value set to yes/no
    • ln.text rewritten, preserving comment prefix if ln.commented is true

This ensures round-trip consistency between the in-memory options map and the serialized file representation.


8. Security and Operational Considerations

  • Round-trip preservation: the design favors preserving original file structure, comments, and unknown directives.
  • Tokenization limitation: only two tokens (key and a single-token value) are parsed; multi-token values may require extension if needed by specific directives.
  • Multi-occurrence directives: directives like OnAccessIncludePath are represented fully in rawLines; the options map stores only the last seen/assigned value for such keys.
  • Backup strategy: backups are created with a simple .bak suffix; upstream installers may extend this to timestamped backups if required.
  • Privilege separation: ClamdConfig itself performs file I/O; privileged installation (e.g., writing to system clamd.conf paths) should be handled by higher-level modules (e.g., Settings page using pkexec).

In summary, ClamdConfig acts as a practical configuration round-tripping layer for ClamAV, enabling UI-driven edits of clamd.conf while preserving formatting and minimizing the risk of destructive rewrites.