#include <err.h>
#include <stdbool.h>
-#include "mlmmj-maintd.h"
#include "mlmmj.h"
#include "strgen.h"
#include "chomp.h"
#include "log_oper.h"
#include "utils.h"
+#define log(...) dprintf(logfd, __VA_ARGS__);
+#define opendirat(_dirfd, _fd, _dirent, _path) \
+ do { \
+ _fd = openat(_dirfd, _path, O_DIRECTORY|O_CLOEXEC); \
+ if (_fd == -1 || (_dirent = fdopendir(_fd)) == NULL) { \
+ log(" - Could not open '%s': %s\n", _path, \
+ strerror(errno)); \
+ return (false); \
+ } \
+ } while (0);
+
static void print_help(const char *prg)
{
printf("Usage: %s [-L | -d] /path/to/dir [-F]\n"
return 0;
}
-int delolder(int dfd, const char *dirname, time_t than)
+static bool
+delolder(int dfd, const char *dirname, time_t than, int logfd)
{
DIR *dir;
struct dirent *dp;
struct stat st;
time_t t;
int fd;
+ bool ret = true;
- fd = openat(dfd, dirname, O_DIRECTORY|O_CLOEXEC);
- if (fd == -1) {
- log_error(LOG_ARGS, "Could not open(%s)", dirname);
- return -1;
- }
-
- if((dir = fdopendir(fd)) == NULL) {
- log_error(LOG_ARGS, "Could not fdopendir(%s)", dirname);
- return -1;
- }
-
+ opendirat(dfd, fd, dir, dirname);
while((dp = readdir(dir)) != NULL) {
if(fstatat(fd, dp->d_name, &st, 0) < 0) {
- log_error(LOG_ARGS, "Could not stat(%s/%s)",
- dirname, dp->d_name);
+ log(" - Could not stat(%s/%s): %s\n",
+ dirname, dp->d_name, strerror(errno));
+ ret = false;
continue;
}
if(!S_ISREG(st.st_mode))
continue;
t = time(NULL);
- if(t - st.st_mtime > than)
- unlinkat(fd, dp->d_name, 0);
+ if(t - st.st_mtime > than) {
+ if (unlinkat(fd, dp->d_name, 0) == -1) {
+ log("- Could not remove ('%s/%s'): %s\n",
+ dirname, dp->d_name, strerror(errno));
+ ret = false;
+ }
+ }
}
closedir(dir);
- return 0;
+ return (ret);
}
-int clean_moderation(int dfd)
+static bool
+clean_moderation(int dfd, int logfd)
{
time_t modreqlife;
modreqlife = ctrltimet(dfd, "modreqlife", MODREQLIFE);
- return (delolder(dfd, "moderation", modreqlife));
+ return (delolder(dfd, "moderation", modreqlife, logfd));
}
-int clean_discarded(int dfd)
+static bool
+clean_discarded(int dfd, int logfd)
{
- return (delolder(dfd, "queue/discarded", DISCARDEDLIFE));
+ return (delolder(dfd, "queue/discarded", DISCARDEDLIFE, logfd));
}
-int clean_subconf(int dfd)
+static bool
+clean_subconf(int dfd, int logfd)
{
- return (delolder(dfd, "subconf", CONFIRMLIFE));
+ return (delolder(dfd, "subconf", CONFIRMLIFE, logfd));
}
-int clean_unsubconf(int dfd)
+static bool
+clean_unsubconf(int dfd, int logfd)
{
- return (delolder(dfd, "unsubconf", CONFIRMLIFE));
+ return (delolder(dfd, "unsubconf", CONFIRMLIFE, logfd));
}
-int resend_queue(int dfd, const char *mlmmjsend, const char *listdir)
+static bool
+resend_queue(int dfd, const char *mlmmjsend, const char *listdir, int logfd)
{
DIR *queuedir;
struct dirent *dp;
struct stat st;
int fromfd, tofd, err = 0, qfd;
time_t t, bouncelife;
+ bool ret = true;
- qfd = openat(dfd, "queue", O_DIRECTORY|O_CLOEXEC);
- if (qfd == -1) {
- log_error(LOG_ARGS, "Could not open(queue)");
- return 1;
- }
-
- if((queuedir = fdopendir(qfd)) == NULL) {
- log_error(LOG_ARGS, "Could not fdopendir(queue)");
- return 1;
- }
+ opendirat(dfd, qfd, queuedir, "queue");
while((dp = readdir(queuedir)) != NULL) {
if(fstatat(qfd, dp->d_name, &st, 0) < 0) {
- log_error(LOG_ARGS, "Could not stat(queue/%s)",
- dp->d_name);
+ log(" - Could not stat(queue/%s): %s",
+ dp->d_name, strerror(errno));
+ ret = false;
continue;
}
if(fstatat(qfd, mailname, &st, 0) < 0) {
if(errno == ENOENT) {
*ch = '.';
- unlinkat(qfd, mailname, 0);
+ if (unlinkat(qfd, mailname, 0) == -1) {
+ log(" - Could not remove %s: %s\n",
+ mailname, strerror(errno));
+ ret = false;
+ }
}
}
free(mailname);
bouncelife = ctrltimet(dfd, "bouncelife", BOUNCELIFE);
t = time(NULL);
if(t - st.st_mtime > bouncelife) {
- unlinkat(qfd, dp->d_name, 0);
+ if (unlinkat(qfd, dp->d_name, 0) == -1) {
+ log(" - Could not remove queue/%s: %s\n",
+ dp->d_name, strerror(errno));
+ ret = false;
+ }
free(mailname);
free(from);
free(to);
closedir(queuedir);
- return 0;
+ return (ret);
}
-int resend_requeue(int dfd, const char *mlmmjsend, const char *listdir)
+static bool
+resend_requeue(int dfd, const char *mlmmjsend, const char *listdir, int logfd)
{
DIR *queuedir;
struct dirent *dp;
struct stat st;
time_t t;
bool fromrequeuedir;
+ int fd;
+ bool ret = true;
- int fd = openat(dfd, "requeue", O_DIRECTORY|O_CLOEXEC);
- if (fd == -1 || (queuedir = fdopendir(fd)) == NULL) {
- log_error(LOG_ARGS, "Could not opendir(requeue)");
- return 1;
- }
-
+ opendirat(dfd, fd, queuedir, "requeue");
while((dp = readdir(queuedir)) != NULL) {
if((strcmp(dp->d_name, "..") == 0) ||
(strcmp(dp->d_name, ".") == 0))
continue;
if(fstatat(fd, dp->d_name, &st, 0) < 0) {
- log_error(LOG_ARGS, "Could not stat(%s)",dp->d_name);
+ log(" - Could not stat(requeue/%s): %s\n", dp->d_name,
+ strerror(errno));
continue;
}
/* Remove old empty directories */
t = time(NULL);
if(t - st.st_mtime > (time_t)3600)
- if (unlinkat(fd, dp->d_name, AT_REMOVEDIR) == 0)
+ if (unlinkat(fd, dp->d_name, AT_REMOVEDIR) == 0) {
continue;
+ }
xasprintf(&archivefilename, "archive/%s", dp->d_name);
}
xasprintf(&subfilename, "%s/subscribers", dp->d_name);
if(fstatat(fd, subfilename, &st, 0) < 0) {
- if (fromrequeuedir)
- unlinkat(dfd, archivefilename, 0);
+ if (fromrequeuedir) {
+ if (unlinkat(dfd, archivefilename, 0) == -1) {
+ log(" - Cound not remove %s: %s\n",
+ archivefilename, strerror(errno));
+ ret = false;
+ }
+ }
free(archivefilename);
free(subfilename);
continue;
xasprintf(&subnewname, "%s.resending", subfilename);
- if(renameat(fd, subfilename, fd, subnewname) < 0) {
- log_error(LOG_ARGS, "Could not rename(requeue/%s, requeue/%s)",
- subfilename, subnewname);
+ if(renameat(fd, subfilename, fd, subnewname) == -1) {
+ log(" - Could not rename(requeue/%s, requeue/%s): %s\n",
+ subfilename, subnewname, strerror(errno));
+ ret = false;
free(archivefilename);
free(subfilename);
free(subnewname);
closedir(queuedir);
- return 0;
+ return ret;
}
-int clean_nolongerbouncing(int dfd)
+static bool
+clean_nolongerbouncing(int dfd, int logfd)
{
DIR *bouncedir;
char *filename, *probetimestr, *s;
struct stat st;
int fd;
const char *errstr;
+ bool ret = true;
- fd = openat(dfd, "bounce", O_DIRECTORY|O_CLOEXEC);
- if (fd == -1 || (bouncedir = fdopendir(fd)) == NULL) {
- log_error(LOG_ARGS, "Could not opendir(bounce)");
- return 1;
- }
-
+ opendirat(dfd, fd, bouncedir, "bounce");
while((dp = readdir(bouncedir)) != NULL) {
if((strcmp(dp->d_name, "..") == 0) ||
(strcmp(dp->d_name, ".") == 0))
s = strrchr(filename, '-');
if(s && (strcmp(s, "-probe") == 0)) {
if(fstatat(fd, filename, &st, 0) < 0) {
- log_error(LOG_ARGS, "Could not stat(%s)",
+ log(" - Could not stat(bounce/%s)",
filename);
free(filename);
+ ret = false;
continue;
}
}
t = time(NULL);
if(t - probetime > WAITPROBE) {
- unlinkat(fd, filename, 0);
+ if (unlinkat(fd, filename, 0) == -1) {
+ log(" - Could not remove bounce/%s: "
+ "%s\n", filename,
+ strerror(errno));
+ ret = false;
+ }
/* remove -probe onwards from filename */
*s = '\0';
unlinkat(fd, filename, 0);
closedir(bouncedir);
- return 0;
+ return (ret);
}
-int probe_bouncers(int dfd, const char *mlmmjbounce, const char *listdir)
+static bool
+probe_bouncers(int dfd, const char *mlmmjbounce, const char *listdir, int logfd)
{
DIR *bouncedir;
char *probefile, *s;
struct dirent *dp;
struct stat st;
int fd;
+ bool ret = true;
- fd = openat(dfd, "bounce", O_DIRECTORY|O_CLOEXEC);
- if (fd == -1 || (bouncedir = fdopendir(fd)) == NULL) {
- log_error(LOG_ARGS, "Could not opendif(bounce)");
- return 1;
- }
-
+ opendirat(dfd, fd, bouncedir, "bounce");
while((dp = readdir(bouncedir)) != NULL) {
if((strcmp(dp->d_name, "..") == 0) ||
(strcmp(dp->d_name, ".") == 0))
continue;
if(fstatat(fd, dp->d_name, &st, 0) < 0) {
- log_error(LOG_ARGS, "Could not stat(%s)", dp->d_name);
+ log(" - Could not stat(bounce/%s): %s\n", dp->d_name,
+ strerror(errno));
+ ret = false;
continue;
}
}
closedir(bouncedir);
- return 0;
+ return (ret);
}
-int unsub_bouncers(int dfd, const char *mlmmjunsub, const char *listdir)
+static bool
+unsub_bouncers(int dfd, const char *mlmmjunsub, const char *listdir, int logfd)
{
DIR *bouncedir;
char *probefile, *address, *a, *firstbounce, *bouncedata;
int bfd;
time_t bouncetime, t, bouncelife;
const char *errstr;
+ bool ret = true;
- bfd = openat(dfd, "bounce", O_DIRECTORY|O_CLOEXEC);
- if (bfd == -1 || (bouncedir = fdopendir(bfd)) == NULL) {
- log_error(LOG_ARGS, "Could not opendir(bounce)");
- return 1;
- }
+ opendirat(dfd, bfd, bouncedir, "bounce");
bouncelife = ctrltimet(dfd, "bouncelife", BOUNCELIFE);
continue;
if(fstatat(bfd, dp->d_name, &st, 0) < 0) {
- log_error(LOG_ARGS, "Could not stat(%s)", dp->d_name);
+ log(" - Could not stat(bounce/%s): %s\n",
+ dp->d_name, strerror(errno));
+ ret = false;
continue;
}
*/
int fd = openat(bfd, dp->d_name, O_RDONLY|O_CLOEXEC);
if(fd == -1) {
- log_error(LOG_ARGS, "Could not open bounce/%s", dp->d_name);
+ log(" - Could not open bounce/%s: %s", dp->d_name,
+ strerror(errno));
+ ret = false;
continue;
}
firstbounce = mygetline(fd);
/* End the string at the comment */
a = strchr(firstbounce, '#');
if(a == NULL) {
+ log("- Error parsing the first line of bounce/%s",
+ dp->d_name);
+ ret = false;
free(firstbounce);
continue;
}
chomp(bouncedata);
a = strchr(firstbounce, ':');
if(a == NULL) {
+ log("- Error parsing the first line of bounce/%s",
+ dp->d_name);
+ ret = false;
free(firstbounce);
free(bouncedata);
continue;
bouncetime = strtotimet(a, &errstr);
free(firstbounce);
if (errstr != NULL) {
+ log("- Error parsing the first line of bounce/%s",
+ dp->d_name);
+ ret = false;
free(bouncedata);
continue;
}
}
closedir(bouncedir);
- return 0;
+ return (ret);
}
-int run_digests(int dfd, const char *mlmmjsend, const char *listdir)
+static bool
+run_digests(int dfd, const char *mlmmjsend, const char *listdir, int logfd)
{
char *lasttimestr, *lastindexstr, *lastissuestr;
char *s1, *s2;
long digestmaxmails, lastindex, index, lastissue;
int fd, indexfd;
const char *errstr = NULL;
+ bool ret = false;
if (statctrl(listdir, "noarchive")) {
- return 0;
+ log(" - noarchive tunable: skipping digest\n");
+ return (true);
+ }
+ if (faccessat(dfd, "index", R_OK, 0) == -1) {
+ log(" - No readable index file: no digest\n");
+ return (true);
}
digestinterval = ctrltimet(dfd, "digestinterval", DIGESTINTERVAL);
fd = openat(dfd, "lastdigest", O_RDWR|O_CREAT|O_EXLOCK, S_IRUSR | S_IWUSR);
if (fd < 0) {
- log_error(LOG_ARGS, "Could not open 'lastdigest'");
- return 1;
+ log(" - Could not open or create 'lastdigest': %s\n",
+ strerror(errno));
+ return (false);
}
s1 = mygetline(fd);
lastissue = strtoim(lastissuestr, LONG_MIN, LONG_MAX,
&errstr);
if (errstr != NULL) {
- log_error(LOG_ARGS, "'lastdigests' contains "
- "malformed data: Impossible to parse "
- "lastissue: '%s': %s",
- lastissuestr, errstr);
- return (1);
+ log(" - 'lastdigests' contains malformed "
+ "data: Impossible to parse lastissue: '%s':"
+ " %s", lastissuestr, errstr);
+ close(fd);
+ return (false);
}
} else {
lastissue = 0;
}
lasttime = strtotimet(lasttimestr, &errstr);
if (errstr) {
- log_error(LOG_ARGS, "'lastdigest' invalid lasttimestr "
+ log(" - 'lastdigest' invalid last time string: "
"'%s': %s", lasttimestr, errstr);
- return (1);
+ close(fd);
+ return (false);
}
lastindexstr = s1;
lastindex = strtoim(lastindexstr, LONG_MIN, LONG_MAX, &errstr);
if (errstr != NULL) {
- log_error(LOG_ARGS, "'lastdigests' contains malformed "
- "data: Impossible to parse lastindex: '%s': %s",
+ log("'lastdigests' invalid last index: '%s': %s\n",
lastindexstr, errstr);
- return (1);
+ return (false);
}
} else {
if (s1 && (strlen(s1) > 0)) {
- log_error(LOG_ARGS, "'lastdigest' contains malformed "
- "data");
+ log_error(LOG_ARGS, "'lastdigest' invalid first line");
free(s1);
close(fd);
- return 1;
+ return (false);
}
/* If lastdigest is empty, we start from scratch */
lasttime = 0;
indexfd = openat(dfd, "index", O_RDONLY);
if (indexfd < 0) {
- log_error(LOG_ARGS, "Could not open 'index'");
+ log("Could not open'index': %s", strerror(errno));
free(s1);
close(fd);
- return 1;
+ return (false);
}
s2 = mygetline(indexfd);
close(indexfd);
* list, and therefore we don't need to send a digest */
free(s1);
close(fd);
- return 1;
+ return (true);
}
-
+ chomp(s2);
index = strtoim(s2, 0, LONG_MAX, &errstr);
- if (errstr == NULL) {
- log_error(LOG_ARGS, "Invalid index content '%s'", s2);
+ if (errstr != NULL) {
+ log(" - Invalid index content '%s': %s\n", s2, errstr);
free(s1);
free(s2);
close(fd);
- return 1;
+ return (false);
}
t = time(NULL);
}
if (lseek(fd, 0, SEEK_SET) < 0) {
- log_error(LOG_ARGS, "Could not seek 'lastdigest'");
+ log(" - Could not seek 'lastdigest': %s\n", strerror(errno));
+ goto out;
} else {
if (dprintf(fd, "%ld:%ld:%ld\n", index, (long)t, lastissue) < 0 ) {
- log_error(LOG_ARGS, "Could not write new "
- "'lastdigest'");
+ log("Could not write new 'lastdigest': %s\n",
+ strerror(errno));
+ goto out;
}
}
}
+ ret = true;
+out:
free(s1);
free(s2);
close(fd);
- return 0;
+ return (ret);
}
void do_maintenance(int listfd, const char *listdir, const char *mlmmjsend,
char *random, *logname;
char timenow[64];
struct stat st;
- int maintdlogfd;
+ int logfd;
uid_t uid = getuid();
time_t t;
xasprintf(&logname, "maintdlog-%s", random);
free(random);
- maintdlogfd = openat(listfd, logname, O_WRONLY|O_EXCL|O_CREAT, S_IRUSR|S_IWUSR);
- if(maintdlogfd == -1) {
+ logfd = openat(listfd, logname, O_WRONLY|O_EXCL|O_CREAT, S_IRUSR|S_IWUSR);
+ if(logfd == -1) {
log_error(LOG_ARGS, "Could not open %s", logname);
free(logname);
return;
t = time(NULL);
if(ctime_r(&t, timenow))
- dprintf(maintdlogfd, "Starting maintenance run at %s\n",
+ log("Starting maintenance run at %s\n",
timenow);
- dprintf(maintdlogfd, "clean_moderation\n");
- clean_moderation(listfd);
-
- dprintf(maintdlogfd, "clean_discarded\n");
- clean_discarded(listfd);
-
- dprintf(maintdlogfd, "clean_subconf\n");
- clean_subconf(listfd);
-
- dprintf(maintdlogfd, "clean_unsubconf\n");
- clean_unsubconf(listfd);
-
- dprintf(maintdlogfd, "resend_queue\n");
- resend_queue(listfd, mlmmjsend, listdir);
-
- dprintf(maintdlogfd, "resend_requeue\n");
- resend_requeue(listfd, mlmmjsend, listdir);
-
- dprintf(maintdlogfd, "clean_nolongerbouncing\n");
- clean_nolongerbouncing(listfd);
-
- dprintf(maintdlogfd, "unsub_bouncers\n");
- unsub_bouncers(listfd, mlmmjunsub, listdir);
-
- dprintf(maintdlogfd, "probe_bouncers\n");
- probe_bouncers(listfd, mlmmjbounce, listdir);
-
- dprintf(maintdlogfd, "run_digests\n");
- run_digests(listfd, mlmmjsend, listdir);
-
- close(maintdlogfd);
+ log("clean_moderation\n");
+ if (!clean_moderation(listfd, logfd))
+ log_err("An error occured while cleaning moderation, see %s",
+ MAINTD_LOGFILE);
+
+ log("clean_discarded\n");
+ if (!clean_discarded(listfd, logfd))
+ log_err("An error occured while cleaning discarded mails, see "
+ "%s", MAINTD_LOGFILE);
+
+ log("clean_subconf\n");
+ if (!clean_subconf(listfd, logfd))
+ log_err("An error occured while cleaning subscribtion "
+ "confirmations, mails, see %s", MAINTD_LOGFILE);
+
+ log("clean_unsubconf\n");
+ if (!clean_unsubconf(listfd, logfd))
+ log_err("An error occured while cleaning unsubscribtion "
+ "confirmations, mails, see %s", MAINTD_LOGFILE);
+
+ log("resend_queue\n");
+ if (!resend_queue(listfd, mlmmjsend, listdir, logfd))
+ log_err("An error occured while resending queued mails, see %s",
+ MAINTD_LOGFILE);
+
+ log("resend_requeue\n");
+ if (!resend_requeue(listfd, mlmmjsend, listdir, logfd))
+ log_err("An error occured while resending requeued mails, see "
+ "%s", MAINTD_LOGFILE);
+
+ log("clean_nolongerbouncing\n");
+ if (!clean_nolongerbouncing(listfd, logfd))
+ log_err("An error occured while cleaning no longer bouncing "
+ "traces, see %s", MAINTD_LOGFILE);
+
+ log("unsub_bouncers\n");
+ if (!unsub_bouncers(listfd, mlmmjunsub, listdir, logfd))
+ log_err("An error occured while unsubscribing bouncing emails, "
+ "see %s", MAINTD_LOGFILE);
+
+ log("probe_bouncers\n");
+ if (!probe_bouncers(listfd, mlmmjbounce, listdir, logfd))
+ log_err("An error occured while sending probes for bouncers, "
+ "see %s", MAINTD_LOGFILE);
+
+ log("run_digests\n");
+ if (!run_digests(listfd, mlmmjsend, listdir, logfd))
+ log_err("An error occured while running digests, see %s",
+ MAINTD_LOGFILE);
+
+ close(logfd);
if (renameat(listfd, logname, listfd, MAINTD_LOGFILE) < 0)
log_error(LOG_ARGS, "Could not rename(%s,%s)",
tests_init \
nolongerbouncing \
discarded \
+ digests \
basics
nolongerbouncing_body()
echo "$now" > ${f2}-probe
touch ${f2}
touch ${f2}.lastmsg
- atf_check -s exit:0 -o empty -e ignore $mlmmjmaintd -L lists/ml -F
+ atf_check -s exit:0 -o empty $mlmmjmaintd -L lists/ml -F
atf_check -s exit:1 -o empty -e empty test -f $f1
atf_check -s exit:1 -o empty -e empty test -f $f1-probe
atf_check -s exit:1 -o empty -e empty test -f $f1.lastmsg
f2=lists/ml/queue/discarded/notsoold
atf_check touch -t "197001020000" $f1
atf_check touch $f2
- atf_check -s exit:0 -o empty -e ignore $mlmmjmaintd -L lists/ml -F
+ atf_check -s exit:0 -o empty $mlmmjmaintd -L lists/ml -F
atf_check -s exit:1 -o empty -e empty test -f $f1
atf_check -s exit:0 -o empty -e empty test -f $f2
}
atf_check -s exit:0 -o "inline:mlmmj-maintd version 1.3.0\n" $mlmmjmaintd -V
atf_check -s exit:0 -o "inline:$helptxt" $mlmmjmaintd -h
echo test@mlmmjtest > lists/ml/control/listaddress
- atf_check -s exit:0 -o empty -e ignore $mlmmjmaintd -L lists/ml -F
+ atf_check -s exit:0 $mlmmjmaintd -L lists/ml -F
output="Starting maintenance run at
clean_moderation
unsub_bouncers
probe_bouncers
run_digests
+ - No readable index file: no digest
"
atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
rm lists/ml/*.log
- atf_check -s exit:0 -e ignore $mlmmjmaintd -d lists -F
+ atf_check -s exit:0 $mlmmjmaintd -d lists -F
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+ rmdir lists/ml/queue/discarded
+ atf_check -s exit:0 -e match:"An error occured while cleaning discarded mails, see mlmmj-maintd.lastrun.log" $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+ - Could not open 'queue/discarded': No such file or directory
+clean_subconf
+clean_unsubconf
+resend_queue
+resend_requeue
+clean_nolongerbouncing
+unsub_bouncers
+probe_bouncers
+run_digests
+ - No readable index file: no digest
+"
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+ mkdir lists/ml/queue/discarded
+
+ rm -rf lists/ml/queue
+ atf_check -s exit:0 -e match:"An error occured while cleaning discarded mails, see mlmmj-maintd.lastrun.log" $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+ - Could not open 'queue/discarded': No such file or directory
+clean_subconf
+clean_unsubconf
+resend_queue
+ - Could not open 'queue': No such file or directory
+resend_requeue
+clean_nolongerbouncing
+unsub_bouncers
+probe_bouncers
+run_digests
+ - No readable index file: no digest
+"
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+ mkdir -p lists/ml/queue/discarded
+
+ rm -rf lists/ml/requeue
+ atf_check -s exit:0 -e match:"An error occured while resending requeued mails, see mlmmj-maintd.lastrun.log" $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+clean_subconf
+clean_unsubconf
+resend_queue
+resend_requeue
+ - Could not open 'requeue': No such file or directory
+clean_nolongerbouncing
+unsub_bouncers
+probe_bouncers
+run_digests
+ - No readable index file: no digest
+"
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+ mkdir -p lists/ml/requeue
+
+ rm -rf lists/ml/bounce
+ atf_check -s exit:0 -e match:"An error occured while cleaning no longer bouncing traces, see mlmmj-maintd.lastrun.log" $mlmmjmaintd -L lists/ml -F
+ atf_check -s exit:0 -e match:"An error occured while unsubscribing bouncing emails, see mlmmj-maintd.lastrun.log" $mlmmjmaintd -L lists/ml -F
+ atf_check -s exit:0 -e match:"An error occured while sending probes for bouncers, see mlmmj-maintd.lastrun.log" $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+clean_subconf
+clean_unsubconf
+resend_queue
+resend_requeue
+clean_nolongerbouncing
+ - Could not open 'bounce': No such file or directory
+unsub_bouncers
+ - Could not open 'bounce': No such file or directory
+probe_bouncers
+ - Could not open 'bounce': No such file or directory
+run_digests
+ - No readable index file: no digest
+"
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+ mkdir -p lists/ml/bounce
+
+ rm -rf lists/ml/subconf
+ atf_check -s exit:0 -e match:"An error occured while cleaning subscribtion confirmations, mails, see mlmmj-maintd.lastrun.log" $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+clean_subconf
+ - Could not open 'subconf': No such file or directory
+clean_unsubconf
+resend_queue
+resend_requeue
+clean_nolongerbouncing
+unsub_bouncers
+probe_bouncers
+run_digests
+ - No readable index file: no digest
+"
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+ mkdir -p lists/ml/subconf
+
+ rm -rf lists/ml/unsubconf
+ atf_check -s exit:0 -e match:"An error occured while cleaning unsubscribtion confirmations, mails, see mlmmj-maintd.lastrun.log" $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+clean_subconf
+clean_unsubconf
+ - Could not open 'unsubconf': No such file or directory
+resend_queue
+resend_requeue
+clean_nolongerbouncing
+unsub_bouncers
+probe_bouncers
+run_digests
+ - No readable index file: no digest
+"
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+ mkdir -p lists/ml/unsubconf
+}
+
+digests_body()
+{
+ mlmmjmaintd=$(command -v mlmmj-maintd)
+ mkdir lists
+ init_ml lists/ml
+
+ touch lists/ml/control/noarchive
+ atf_check -s exit:0 $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+clean_subconf
+clean_unsubconf
+resend_queue
+resend_requeue
+clean_nolongerbouncing
+unsub_bouncers
+probe_bouncers
+run_digests
+ - noarchive tunable: skipping digest
+"
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+
+ rm lists/ml/control/noarchive
+ touch lists/ml/index
+ atf_check -s exit:0 $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+clean_subconf
+clean_unsubconf
+resend_queue
+resend_requeue
+clean_nolongerbouncing
+unsub_bouncers
+probe_bouncers
+run_digests
+"
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+
+ chmod -r lists/ml/index
+ atf_check -s exit:0 $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+clean_subconf
+clean_unsubconf
+resend_queue
+resend_requeue
+clean_nolongerbouncing
+unsub_bouncers
+probe_bouncers
+run_digests
+ - No readable index file: no digest
+"
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+ chmod +r lists/ml/index
+ echo "-1" > lists/ml/index
+ atf_check -s exit:0 -e match:"An error occured while running digests, see mlmmj-maintd.lastrun.log" $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+clean_subconf
+clean_unsubconf
+resend_queue
+resend_requeue
+clean_nolongerbouncing
+unsub_bouncers
+probe_bouncers
+run_digests
+ - Invalid index content '-1': too small
+"
+ atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
+ echo "0" > lists/ml/index
+
+ touch lists/ml/lastdigest
+ chmod -w lists/ml/lastdigest
+ atf_check -s exit:0 -e match:"An error occured while running digests, see mlmmj-maintd.lastrun.log" $mlmmjmaintd -L lists/ml -F
+ output="Starting maintenance run at
+
+clean_moderation
+clean_discarded
+clean_subconf
+clean_unsubconf
+resend_queue
+resend_requeue
+clean_nolongerbouncing
+unsub_bouncers
+probe_bouncers
+run_digests
+ - Could not open or create 'lastdigest': Permission denied
+"
atf_check -o "inline:$output" sed -e "s/at .*/at/" lists/ml/mlmmj-maintd.lastrun.log
}