Problem
db_sqlite.connect() builds the cache db path from disk.get_tmpdir() plus a static filename, with no per-user component (db_sqlite.py, get_filename() around lines 315-321). The result is a shared, world-readable path such as /tmp/linuxfabrik-monitoring-plugins-docker-stats.db.
The first process to run a plugin creates that file as its owner with the default umask (0644). Any later run under a different user can read but not write it. The next write (insert, cut, commit) then fails:
OperationalError: attempt to write a readonly database
which lib.base.coe turns into an UNKNOWN/CRIT abort. This bites whenever a plugin is first run manually (e.g. as root, per several plugins' "Requires root or sudo") and later scheduled under the monitoring agent's own user.
Reproduced:
c = sqlite3.connect('db'); c.execute('CREATE TABLE cpu (...)'); c.commit(); c.close()
os.chmod('db', 0o444) # owned by another user
sqlite3.connect('db').execute("INSERT ...") # OperationalError: attempt to write a readonly database
Proposed fix
Embed the user identity into the default filename inside get_filename(), so each user gets its own cache db:
/tmp/linuxfabrik-monitoring-plugins-docker-stats-uid<UID>.db
Per-UID isolation is preferred over making the file world-writable (0666): no shared-state tampering, no symlink/ownership games in a shared /tmp, and trend data still persists across runs for the same runner (UID is stable per user).
Considerations
- Must stay portable:
os.getuid() does not exist on Windows (lib targets py39 and ships Windows plugins). Use a portable identity with a fallback, e.g. getpass.getuser(), sanitized for the filename.
- Apply centrally in
get_filename() so every caller benefits without touching the plugins.
- Stale shared-path db files from older versions can be left to age out of
/tmp or removed by the new code on first run.
Surfaced via Linuxfabrik/monitoring-plugins#1143 (docker-stats), but this affects every plugin using db_sqlite as a cross-run cache.
Problem
db_sqlite.connect()builds the cache db path fromdisk.get_tmpdir()plus a static filename, with no per-user component (db_sqlite.py,get_filename()around lines 315-321). The result is a shared, world-readable path such as/tmp/linuxfabrik-monitoring-plugins-docker-stats.db.The first process to run a plugin creates that file as its owner with the default umask (
0644). Any later run under a different user can read but not write it. The next write (insert,cut,commit) then fails:which
lib.base.coeturns into an UNKNOWN/CRIT abort. This bites whenever a plugin is first run manually (e.g. asroot, per several plugins' "Requires root or sudo") and later scheduled under the monitoring agent's own user.Reproduced:
Proposed fix
Embed the user identity into the default filename inside
get_filename(), so each user gets its own cache db:Per-UID isolation is preferred over making the file world-writable (
0666): no shared-state tampering, no symlink/ownership games in a shared/tmp, and trend data still persists across runs for the same runner (UID is stable per user).Considerations
os.getuid()does not exist on Windows (lib targetspy39and ships Windows plugins). Use a portable identity with a fallback, e.g.getpass.getuser(), sanitized for the filename.get_filename()so every caller benefits without touching the plugins./tmpor removed by the new code on first run.Surfaced via Linuxfabrik/monitoring-plugins#1143 (
docker-stats), but this affects every plugin usingdb_sqliteas a cross-run cache.