Reload keyring on filesystem events

Subscribe to FS events from keyring directory using Python Watchdog and when a
modification is observed, reload the key cache.

Since we may receive more than one event about a single modification, keep
directory's last modification to recognise 'false positives'.
This commit is contained in:
Piotr F. Mieszkowski 2022-10-15 19:53:55 +02:00 committed by Gitea
parent 386c23f9f8
commit eb0d5a1326
2 changed files with 41 additions and 7 deletions

View File

@ -9,6 +9,7 @@ from aiosmtpd.smtp import Envelope
import asyncio
import email
import time
from watchdog.observers import Observer
# Mail status constants.
#
@ -59,6 +60,13 @@ def _init_controller(keys: kcache.KeyCache, tout: float = 2.5):
return Controller(proxy, hostname=host, port=port, ready_timeout=tout)
def _init_reloader(keyring_dir: str, reloader) -> kcache.KeyringModificationListener:
listener = kcache.KeyringModificationListener(reloader)
observer = Observer()
observer.schedule(listener, keyring_dir, recursive=False)
return observer
def _validate_config():
missing = conf.validate_config()
if missing:
@ -71,10 +79,9 @@ def _full_param_name(tup):
return f"[{tup[0]}]{tup[1]}"
async def _sleep(minutes, heartbeat_func):
async def _sleep(minutes):
while True:
await asyncio.sleep(minutes * 60)
heartbeat_func()
def _main():
@ -82,19 +89,27 @@ def _main():
refresh_min = float(conf.get_item('gpg', 'cache_refresh_minutes', 2))
keys = kcache.KeyCache(conf.get_item('gpg', 'keyhome'))
keyring_path = conf.get_item('gpg', 'keyhome')
keys = kcache.KeyCache(keyring_path)
keys.load()
controller = _init_controller(keys)
reloader = _init_reloader(keyring_path, keys)
LOG.info("Starting the daemon...")
LOG.info(f'Watching keyring directory {keyring_path}...')
reloader.start()
LOG.info('Starting the daemon...')
controller.start()
try:
asyncio.run(_sleep(refresh_min, keys.load))
asyncio.run(_sleep(refresh_min))
except KeyboardInterrupt:
LOG.info("Finishing...")
controller.stop()
finally:
LOG.info('Shutting down keyring watcher and the daemon...')
reloader.stop()
reloader.join()
controller.stop()
LOG.info("Done")

View File

@ -7,6 +7,7 @@ module.
import lacre.text as text
import logging
from os import stat
from watchdog.events import FileSystemEventHandler
import GnuPG
@ -107,3 +108,21 @@ class KeyCache:
MTIME = 8
st = stat(self._keyring_dir)
return st[MTIME]
class KeyringModificationListener(FileSystemEventHandler):
"""A filesystem event listener that triggers key cache reload."""
def __init__(self, cache: KeyCache):
"""Initialise a listener with a callback to be executed upon each change."""
self._cache = cache
def handle(self, event):
"""Reload keys upon FS event."""
LOG.debug(f'Reloading on event {event!r}')
self._cache.reload()
# All methods should do the same: reload the key cache.
# on_created = handle
# on_deleted = handle
on_modified = handle