Compare commits

..

1 Commits

Author SHA1 Message Date
holger krekel
eb1424f944 fixup after testing on nine:
- don't remove large files already after 7 days if they are in the "new/" folder
- report which mailbox is being checked so that "journalctl -u
  chatmail-expire.service" provides sufficient output for checking
- don't trigger expiry or fsreport services during cmdeploy-run but run it from timer only
2025-10-21 21:49:58 +02:00
2 changed files with 18 additions and 75 deletions

View File

@@ -22,30 +22,11 @@ def iter_mailboxes(basedir, maxnum):
print_info(f"no mailboxes found at: {basedir}") print_info(f"no mailboxes found at: {basedir}")
return return
for name in os_listdir_if_exists(basedir)[:maxnum]: for name in os.listdir(basedir)[:maxnum]:
if "@" in name: if "@" in name:
yield MailboxStat(basedir + "/" + name) yield MailboxStat(basedir + "/" + name)
def get_file_entry(path):
"""return a FileEntry or None if the path does not exist or is not a regular file."""
try:
st = os.stat(path)
except FileNotFoundError:
return None
if not S_ISREG(st.st_mode):
return None
return FileEntry(path, st.st_mtime, st.st_size)
def os_listdir_if_exists(path):
"""return a list of names obtained from os.listdir or an empty list if the path does not exist."""
try:
return os.listdir(path)
except FileNotFoundError:
return []
class MailboxStat: class MailboxStat:
last_login = None last_login = None
@@ -59,23 +40,19 @@ class MailboxStat:
# scan all relevant files (without recursion) # scan all relevant files (without recursion)
old_cwd = os.getcwd() old_cwd = os.getcwd()
try: os.chdir(self.basedir)
os.chdir(self.basedir) for name in os.listdir("."):
except FileNotFoundError:
return
for name in os_listdir_if_exists("."):
if name in ("cur", "new", "tmp"): if name in ("cur", "new", "tmp"):
for msg_name in os_listdir_if_exists(name): for msg_name in os.listdir(name):
entry = get_file_entry(f"{name}/{msg_name}") relpath = name + "/" + msg_name
if entry is not None: st = os.stat(relpath)
self.messages.append(entry) self.messages.append(FileEntry(relpath, st.st_mtime, st.st_size))
else: else:
entry = get_file_entry(name) st = os.stat(name)
if entry is not None: if S_ISREG(st.st_mode):
self.extrafiles.append(entry) self.extrafiles.append(FileEntry(name, st.st_mtime, st.st_size))
if name == "password": if name == "password":
self.last_login = entry.mtime self.last_login = st.st_mtime
self.extrafiles.sort(key=lambda x: -x.size) self.extrafiles.sort(key=lambda x: -x.size)
os.chdir(old_cwd) os.chdir(old_cwd)
@@ -103,13 +80,9 @@ class Expiry:
shutil.rmtree(mboxdir) shutil.rmtree(mboxdir)
self.del_mboxes += 1 self.del_mboxes += 1
def remove_file(self, path, mtime=None): def remove_file(self, path):
if self.verbose: if self.verbose:
if mtime is not None: print_info(f"removing {path}")
date = datetime.fromtimestamp(mtime).strftime("%b %d")
print_info(f"removing {date} {path}")
else:
print_info(f"removing {path}")
if not self.dry: if not self.dry:
try: try:
os.unlink(path) os.unlink(path)
@@ -131,27 +104,18 @@ class Expiry:
return return
# all to-be-removed files are relative to the mailbox basedir # all to-be-removed files are relative to the mailbox basedir
try: os.chdir(mbox.basedir)
os.chdir(mbox.basedir)
except FileNotFoundError:
print_info(f"mailbox not found/vanished {mbox.basedir}")
return
mboxname = os.path.basename(mbox.basedir) mboxname = os.path.basename(mbox.basedir)
if self.verbose: if self.verbose:
date = datetime.fromtimestamp(mbox.last_login) if mbox.last_login else None print_info(f"checking for mailbox messages in: {mboxname}")
if date:
print_info(f"checking mailbox {date.strftime('%b %d')} {mboxname}")
else:
print_info(f"checking mailbox (no last_login) {mboxname}")
self.all_files += len(mbox.messages) self.all_files += len(mbox.messages)
for message in mbox.messages: for message in mbox.messages:
if message.mtime < cutoff_mails: if message.mtime < cutoff_mails:
self.remove_file(message.relpath, mtime=message.mtime) self.remove_file(message.relpath)
elif message.size > 200000 and message.mtime < cutoff_large_mails: elif message.size > 200000 and message.mtime < cutoff_large_mails:
# we only remove noticed large files (not unnoticed ones in new/) # we only remove noticed large files (not unnoticed ones in new/)
if message.relpath.startswith("cur/"): if message.relpath.startswith("cur/"):
self.remove_file(message.relpath, mtime=message.mtime) self.remove_file(message.relpath)
else: else:
continue continue
changed = True changed = True

View File

@@ -6,13 +6,7 @@ from pathlib import Path
import pytest import pytest
from chatmaild.expire import ( from chatmaild.expire import FileEntry, MailboxStat, iter_mailboxes
FileEntry,
MailboxStat,
get_file_entry,
iter_mailboxes,
os_listdir_if_exists,
)
from chatmaild.expire import main as expiry_main from chatmaild.expire import main as expiry_main
from chatmaild.fsreport import main as report_main from chatmaild.fsreport import main as report_main
@@ -133,18 +127,3 @@ def test_expiry_cli_old_files(capsys, example_config, mbox1):
pytest.fail(f"failed to remove {path}\n{err}") pytest.fail(f"failed to remove {path}\n{err}")
assert "shouldstay" not in err assert "shouldstay" not in err
def test_get_file_entry(tmp_path):
assert get_file_entry(str(tmp_path.joinpath("123123"))) is None
p = tmp_path.joinpath("x")
p.write_text("hello")
entry = get_file_entry(str(p))
assert entry.size == 5
assert entry.mtime
def test_os_listdir_if_exists(tmp_path):
tmp_path.joinpath("x").write_text("hello")
assert len(os_listdir_if_exists(str(tmp_path))) == 1
assert len(os_listdir_if_exists(str(tmp_path.joinpath("123123")))) == 0