Diagnose lcf Before Trusting Its Configuration History Check

You will learn what lcf checks and how to test the installed command without changing a configuration file. Along the way you will see how its historical MD5 data is laid out. On this machine, the ucf package is version 3.0043+nmu1. Its installed lcf script fails before it can perform the documented comparison, so the practical result is a safe diagnosis rather than a claimed version match.

Allow about ten minutes. You need a shell, the ucf package, a destination file that is already registered with ucf, and a temporary directory. The checks below only read the destination, /var/lib/ucf/hashfile, and your temporary history file. They do not need sudo.

1. Check the installed command and package

Start by checking which executable will run and which package supplied it:

$ command -v lcf
/usr/bin/lcf
$ dpkg-query -W -f='${Package} ${Version}\n' ucf
ucf 3.0043+nmu1

The installed script reports its own revision as 3.00, while the Debian package has the version above. Keep both facts when reporting a result. The manpage is old documentation, and the package version identifies the local implementation you actually tested.

Checkpoint: make sure command -v points to the executable you intend to test. A different copy earlier in PATH could have different behaviour.

2. Understand the two inputs

The documented invocation has a destination file followed by a directory containing historical MD5 records:

$ lcf [options] DESTINATION_FILE HISTORY_DIRECTORY

The destination is the installed configuration whose history you want to identify. The history directory is expected to contain either a file named after the destination's basename plus .md5sum, or a directory with the same basename plus .md5sum.d. For a destination named /etc/example.conf, the two possible locations are:

HISTORY_DIRECTORY/example.conf.md5sum
HISTORY_DIRECTORY/example.conf.md5sum.d/

The records contain an MD5 sum followed by a label for the historical version. A directory entry named default is also meaningful to the installed script: if no sum matches, it can report default. That is a maintainer's fallback label, not proof that the destination is unchanged.

Do not confuse this history directory with /var/lib/ucf/hashfile. The latter is the state database used to decide whether the destination is registered. The history directory is supplied as an argument.

3. Choose a read-only test target

Use a destination already listed in the state database. This command selects the first registered path from the local file without changing it:

$ awk 'NR == 1 { print $2; exit }' /var/lib/ucf/hashfile
/etc/apt/apt.conf.d/20auto-upgrades

Your first line may name a different file. Confirm that it is readable before continuing:

$ test -r /etc/apt/apt.conf.d/20auto-upgrades && echo readable
readable

Replace the example path in the next commands with the path printed on your system. Do not edit the real destination to make this test work. If the hashfile is missing or unreadable, stop and investigate the package installation instead of using elevated privileges blindly.

4. Build a harmless history fixture

Create a temporary directory and record the destination's current checksum under a made-up label. The command reads the file and writes only below /tmp:

$ history_dir=$(mktemp -d /tmp/lcf-history.XXXXXX)
$ destination=/etc/apt/apt.conf.d/20auto-upgrades
$ printf '%s known-version\n' "$(md5sum "$destination" | awk '{print $1}')" > "$history_dir/20auto-upgrades.md5sum"
$ cat "$history_dir/20auto-upgrades.md5sum"
1c261d6541420797f8b824d65ac5c197 known-version

The checksum in the output will differ if your destination differs. The important shape is one 32-character MD5 value, whitespace, and a label. This fixture does not alter /etc or the ucf registry.

Checkpoint: compare the checksum directly with the destination if you want to verify the fixture:

$ md5sum "$destination"
1c261d6541420797f8b824d65ac5c197  /etc/apt/apt.conf.d/20auto-upgrades

5. Run lcf in dry-run mode

The manpage documents --no-action as a dry run. Use it for the diagnostic invocation:

$ lcf --no-action "$destination" "$history_dir"
basename: missing operand
Try 'basename --help' for more information.

On the installed package, the command exits with status 1 and does not print known-version. Capture the status immediately if you need it in a script:

$ lcf --no-action "$destination" "$history_dir"
basename: missing operand
Try 'basename --help' for more information.
$ printf '%s\n' "$?"
1

This is not a mismatch between the destination and the fixture. The installed shell script tries to derive a basename from an unset variable before it reaches the historical checksum comparison. With set -e enabled, that failed basename command terminates the run.

6. Interpret nearby failure modes

A missing history directory is reported separately and returns status 2:

$ lcf --no-action "$destination" "$history_dir/not-present"
The source dir does not exist. Stopping now.
$ printf '%s\n' "$?"
2

An unregistered destination also cannot be identified. The script looks for the exact destination path in /var/lib/ucf/hashfile and reports that no record exists. That does not mean the file is unmodified; it means lcf has no ucf state for it.

The installed help also lists --src-dir, which can provide the history directory as an option. The local lcf(1) manpage does not document that option, so prefer the two-argument form shown here when writing a reproducible diagnostic. Likewise, --version is not accepted by this executable; use the package query from step 1.

Do not "fix" the failure by editing /usr/bin/lcf, deleting /var/lib/ucf/hashfile, or replacing a configuration file. Those changes can affect package upgrades and user settings. If a package maintainer needs this check in an installation script, report the local failure and use a separately reviewed checksum comparison until the packaged implementation is corrected.

Done means