summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-04 12:15:00 +0200
committerDanilo M. <danix@danix.xyz>2026-08-04 12:55:01 +0200
commit28653ea5ac7472233425ad8f77837dba5bf4e286 (patch)
tree672cb216fcb0dd7228f2297bcd420fb0794ba813
parent74d1da2755ff8e1a47bda676fab7b84e910ef738 (diff)
downloadqtmaildir-28653ea5ac7472233425ad8f77837dba5bf4e286.tar.gz
qtmaildir-28653ea5ac7472233425ad8f77837dba5bf4e286.zip
fix(sync): stop mailsync.sh rotating a log logrotate already owns
Two problems, both visible in the first real run of the moved script. Every line was logged twice. The script writes to the log through tee, and the user's crontab also ended in ">> mailsync.log 2>&1", so both put the same output there. Reproduced: without the caller's redirect one run writes a single RUN START, with it, two. The crontab redirect goes away and the script keeps owning the log, which is what makes it behave the same however it is invoked. A comment in the header now says so, since the obvious cron line is the one that breaks it. The internal size-based rotation is removed outright. /etc/logrotate.d/mailsync already owns this file and keeps seven compressed days, and the two were fighting: the script's "mv $LOGFILE $LOGFILE.1" overwrites whatever logrotate had just placed at .1, which is why that file was 357K and uncompressed while .2 through .5 were properly gzipped. logrotate is better at this in every respect, including recreating the file with the right ownership, so the script should not have a second opinion. Verified against stub mbsync and notmuch in a fake HOME: one RUN START per run, stdout and the log agree, no rotation performed by the script, and a failing mbsync or notmuch still produces its own exit code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
-rwxr-xr-xassets/mailsync.sh17
1 files changed, 10 insertions, 7 deletions
diff --git a/assets/mailsync.sh b/assets/mailsync.sh
index ee918b6..d5f51ee 100755
--- a/assets/mailsync.sh
+++ b/assets/mailsync.sh
@@ -23,6 +23,10 @@
# systemd timer, which wants a log file it can read afterwards, and
# qtmaildir, which runs this as a subprocess and shows what it prints.
# Output therefore goes to BOTH, and the exit status is real.
+#
+# The script owns the log, so the caller must NOT redirect into it as well. A
+# crontab line ending "> mailsync.log 2>&1" writes every line a second time,
+# because tee has already put it there. Just call the script.
# Defensive: don't rely on cron/systemd/whatever invokes this to have
# set these correctly. Explicit beats inferred, especially after the
@@ -33,10 +37,15 @@ export GNUPGHOME="${GNUPGHOME:-$HOME/.gnupg}"
LOCKFILE="/tmp/mbsync.lock"
LOGFILE="$HOME/.local/state/mailsync.log"
-MAX_LOG_BYTES=$((10 * 1024 * 1024)) # rotate past 10MB, see note below
mkdir -p "$(dirname "$LOGFILE")"
+# Rotation is NOT this script's job: /etc/logrotate.d/mailsync owns this file,
+# keeping seven compressed days. An earlier version also rotated by size here,
+# and the two fought: the script's "mv $LOGFILE $LOGFILE.1" overwrote whatever
+# logrotate had just put at .1, losing a day of history and leaving an
+# uncompressed file where a compressed one belonged.
+
exec 200>"$LOCKFILE"
if ! flock -n 200; then
# Both streams again: a caller that skipped because the cron run holds
@@ -47,12 +56,6 @@ if ! flock -n 200; then
exit 1
fi
-# Simple rotation: if the log's gotten big, keep the last run's worth
-# and move the rest aside rather than letting it grow forever.
-if [ -f "$LOGFILE" ] && [ "$(stat -c%s "$LOGFILE" 2>/dev/null || echo 0)" -gt "$MAX_LOG_BYTES" ]; then
- mv "$LOGFILE" "${LOGFILE}.1"
-fi
-
# Statuses are written to files rather than shell variables because the
# block below is piped into tee, which puts it in a subshell: a variable
# assigned in there is gone by the time the parent reads it.