diff options
author | Eric Faurot <eric@cvs.openbsd.org> | 2012-01-11 17:46:38 +0000 |
---|---|---|
committer | Eric Faurot <eric@cvs.openbsd.org> | 2012-01-11 17:46:38 +0000 |
commit | cd75d18c8956e1c34385a351e7c916d308413e30 (patch) | |
tree | 4ea2632e80cffece6d42234d1b21d2e367a693f7 | |
parent | 8ccc0ce2a9b3da61262dec658a93fc94497ef635 (diff) |
Simplify runner/queue by getting rid of Q_PURGE. Instead, let smtpd
periodically clear the purge/ directory. At init time, the fsqueue
backend simply moves the existing incoming/ dir in purge/ to discard
aborted sessions.
ok gilles@ chl@
-rw-r--r-- | usr.sbin/smtpd/queue.c | 20 | ||||
-rw-r--r-- | usr.sbin/smtpd/queue_fsqueue.c | 27 | ||||
-rw-r--r-- | usr.sbin/smtpd/runner.c | 20 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.c | 77 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 6 | ||||
-rw-r--r-- | usr.sbin/smtpd/util.c | 84 |
6 files changed, 168 insertions, 66 deletions
diff --git a/usr.sbin/smtpd/queue.c b/usr.sbin/smtpd/queue.c index ccd6a8acad5..1e7cd1c331d 100644 --- a/usr.sbin/smtpd/queue.c +++ b/usr.sbin/smtpd/queue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: queue.c,v 1.113 2011/11/21 18:57:54 eric Exp $ */ +/* $OpenBSD: queue.c,v 1.114 2012/01/11 17:46:36 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -42,7 +42,6 @@ static void queue_imsg(struct imsgev *, struct imsg *); static void queue_pass_to_runner(struct imsgev *, struct imsg *); static void queue_shutdown(void); static void queue_sig_handler(int, short, void *); -static void queue_purge(enum queue_kind); static void queue_imsg(struct imsgev *iev, struct imsg *imsg) @@ -282,8 +281,6 @@ queue(void) config_pipes(peers, nitems(peers)); config_peers(peers, nitems(peers)); - queue_purge(Q_INCOMING); - if (event_dispatch() < 0) fatal("event_dispatch"); queue_shutdown(); @@ -291,21 +288,6 @@ queue(void) return (0); } -static void -queue_purge(enum queue_kind qkind) -{ - struct qwalk *q; - u_int32_t msgid; - u_int64_t evpid; - - q = qwalk_new(qkind, 0); - while (qwalk(q, &evpid)) { - msgid = evpid_to_msgid(evpid); - queue_message_purge(qkind, msgid); - } - qwalk_close(q); -} - void queue_submit_envelope(struct envelope *ep) { diff --git a/usr.sbin/smtpd/queue_fsqueue.c b/usr.sbin/smtpd/queue_fsqueue.c index 75c3c76a8ee..a94380eb2f3 100644 --- a/usr.sbin/smtpd/queue_fsqueue.c +++ b/usr.sbin/smtpd/queue_fsqueue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: queue_fsqueue.c,v 1.25 2011/12/27 17:13:05 eric Exp $ */ +/* $OpenBSD: queue_fsqueue.c,v 1.26 2012/01/11 17:46:36 eric Exp $ */ /* * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org> @@ -69,7 +69,6 @@ void fsqueue_qwalk_close(void *); #define PATH_INCOMING "/incoming" #define PATH_QUEUE "/queue" -#define PATH_PURGE "/purge" #define PATH_CORRUPT "/corrupt" #define PATH_MESSAGE "/message" @@ -95,9 +94,6 @@ fsqueue_getpath(enum queue_kind kind) case Q_QUEUE: return (PATH_QUEUE); - case Q_PURGE: - return (PATH_PURGE); - case Q_CORRUPT: return (PATH_CORRUPT); @@ -399,11 +395,6 @@ fsqueue_message_delete(enum queue_kind qkind, u_int32_t msgid) msgid & 0xfff, msgid)) fatal("fsqueue_message_delete: snprintf"); } - else if (qkind == Q_PURGE) { - if (! bsnprintf(rootdir, sizeof(rootdir), "%s/%08x", PATH_PURGE, - msgid)) - fatal("fsqueue_message_delete: snprintf"); - } if (! bsnprintf(evpdir, sizeof(evpdir), "%s%s", rootdir, PATH_ENVELOPES)) @@ -456,18 +447,13 @@ static int fsqueue_message_purge(enum queue_kind qkind, u_int32_t msgid) { char rootdir[MAXPATHLEN]; - char purgedir[MAXPATHLEN]; if (! bsnprintf(rootdir, sizeof(rootdir), "%s/%08x", fsqueue_getpath(qkind), msgid)) fatalx("fsqueue_message_purge: snprintf"); - if (! bsnprintf(purgedir, sizeof(purgedir), "%s/%08x", - fsqueue_getpath(Q_PURGE), msgid)) - fatalx("fsqueue_message_purge: snprintf"); - - if (rename(rootdir, purgedir) == -1) - fatal("fsqueue_message_purge: rename"); + if (mvpurge(rootdir, PATH_PURGE) == -1) + fatal("fsqueue_message_purge: mvpurge"); return 1; } @@ -497,11 +483,12 @@ int fsqueue_init(void) { unsigned int n; - char *paths[] = { PATH_INCOMING, PATH_QUEUE, - PATH_PURGE, PATH_CORRUPT }; + char *paths[] = { PATH_INCOMING, PATH_QUEUE, PATH_CORRUPT }; char path[MAXPATHLEN]; int ret; + mvpurge(PATH_SPOOL PATH_INCOMING, PATH_SPOOL PATH_PURGE); + ret = 1; for (n = 0; n < nitems(paths); n++) { strlcpy(path, PATH_SPOOL, sizeof(path)); @@ -626,7 +613,7 @@ fsqueue_qwalk_new(enum queue_kind kind, u_int32_t msgid) if (kind == Q_QUEUE) q->filefn = walk_queue; - if (kind == Q_INCOMING || kind == Q_PURGE) + if (kind == Q_INCOMING) q->filefn = walk_queue_nobucket; q->dirs[q->level] = opendir(q->path); diff --git a/usr.sbin/smtpd/runner.c b/usr.sbin/smtpd/runner.c index 2f5d45fa6a3..391a777ff05 100644 --- a/usr.sbin/smtpd/runner.c +++ b/usr.sbin/smtpd/runner.c @@ -1,4 +1,4 @@ -/* $OpenBSD: runner.c,v 1.126 2011/11/16 19:38:56 eric Exp $ */ +/* $OpenBSD: runner.c,v 1.127 2012/01/11 17:46:36 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -54,7 +54,6 @@ static void runner_disable_events(void); static void runner_timeout(int, short, void *); static int runner_process_envelope(struct ramqueue_envelope *, time_t); static void runner_process_batch(struct ramqueue_envelope *, time_t); -static void runner_purge_run(void); static int runner_check_loop(struct envelope *); static int runner_force_message_to_ramqueue(struct ramqueue *, u_int32_t); @@ -297,8 +296,6 @@ runner_timeout(int fd, short event, void *p) time_t nsched; time_t curtm; - runner_purge_run(); - nsched = 0; rq_evp = ramqueue_first_envelope(rqueue); if (rq_evp) @@ -505,21 +502,6 @@ runner_force_message_to_ramqueue(struct ramqueue *rqueue, u_int32_t msgid) return 1; } -void -runner_purge_run(void) -{ - struct qwalk *q; - u_int32_t msgid; - u_int64_t evpid; - - q = qwalk_new(Q_PURGE, 0); - while (qwalk(q, &evpid)) { - msgid = evpid_to_msgid(evpid); - queue_message_delete(Q_PURGE, msgid); - } - qwalk_close(q); -} - int runner_check_loop(struct envelope *ep) { diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c index 2ea72c43737..91b5e0aeca9 100644 --- a/usr.sbin/smtpd/smtpd.c +++ b/usr.sbin/smtpd/smtpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.c,v 1.143 2011/12/13 23:55:00 gilles Exp $ */ +/* $OpenBSD: smtpd.c,v 1.144 2012/01/11 17:46:36 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -66,6 +66,9 @@ static int offline_add(char *); static void offline_done(void); static int offline_enqueue(char *); +static void purge_task(int, short, void *); + + struct offline { TAILQ_ENTRY(offline) entry; char *path; @@ -79,6 +82,9 @@ TAILQ_HEAD(, offline) offline_q; static struct event offline_ev; static struct timeval offline_timeout; +static pid_t purge_pid; +static struct timeval purge_timeout; +static struct event purge_ev; extern char **environ; void (*imsg_callback)(struct imsgev *, struct imsg *); @@ -337,10 +343,6 @@ parent_sig_handler(int sig, short event, void *p) if (pid <= 0) continue; - child = child_lookup(pid); - if (child == NULL) - fatalx("unexpected SIGCHLD"); - fail = 0; if (WIFSIGNALED(status)) { fail = 1; @@ -355,6 +357,13 @@ parent_sig_handler(int sig, short event, void *p) } else fatalx("unexpected cause of SIGCHLD"); + if (pid == purge_pid) + purge_pid = -1; + + child = child_lookup(pid); + if (child == NULL) + goto skip; + switch (child->type) { case CHILD_DAEMON: die = 1; @@ -389,6 +398,7 @@ parent_sig_handler(int sig, short event, void *p) } child_del(child->pid); + skip: free(cause); } while (pid > 0 || (pid == -1 && errno == EINTR)); @@ -504,6 +514,8 @@ main(int argc, char *argv[]) errx(1, "error in spool directory setup"); if (ckdir(PATH_SPOOL PATH_OFFLINE, 01777, 0, 0, 1) == 0) errx(1, "error in offline directory setup"); + if (ckdir(PATH_SPOOL PATH_PURGE, 0700, env->sc_pw->pw_uid, 0, 1) == 0) + errx(1, "error in purge directory setup"); env->sc_queue = queue_backend_lookup(QT_FS); if (env->sc_queue == NULL) @@ -561,6 +573,12 @@ main(int argc, char *argv[]) offline_timeout.tv_usec = 0; evtimer_add(&offline_ev, &offline_timeout); + purge_pid = -1; + evtimer_set(&purge_ev, purge_task, NULL); + purge_timeout.tv_sec = 10; + purge_timeout.tv_usec = 0; + evtimer_add(&purge_ev, &purge_timeout); + if (event_dispatch() < 0) fatal("event_dispatch"); @@ -687,6 +705,55 @@ imsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid, imsg_event_add(iev); } + +static void +purge_task(int fd, short ev, void *arg) +{ + DIR *d; + struct dirent *de; + int n; + uid_t uid; + gid_t gid; + + if (purge_pid == -1) { + + n = 0; + if ((d = opendir(PATH_SPOOL PATH_PURGE))) { + while ((de = readdir(d)) != NULL) + n++; + closedir(d); + } else + log_warn("purge_task: opendir"); + + if (n > 2) { + log_debug("smtpd: forking purge process"); + switch(purge_pid = fork()) { + case -1: + log_warn("purge_task: fork"); + break; + case 0: + if (chroot(PATH_SPOOL PATH_PURGE) == -1) + fatal("smtpd: chroot"); + if (chdir("/") == -1) + fatal("smtpd: chdir"); + uid = env->sc_pw->pw_uid; + gid = env->sc_pw->pw_gid; + if (setgroups(1, &gid) || + setresgid(gid, gid, gid) || + setresuid(uid, uid, uid)) + fatal("smtpd: cannot drop privileges"); + rmtree(".", 1); + _exit(0); + break; + default: + break; + } + } + } + + evtimer_add(&purge_ev, &purge_timeout); +} + static void forkmda(struct imsgev *iev, u_int32_t id, struct deliver *deliver) diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index b3cde66de37..7391c1802a7 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.270 2012/01/11 17:07:18 eric Exp $ */ +/* $OpenBSD: smtpd.h,v 1.271 2012/01/11 17:46:36 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -65,6 +65,7 @@ #define PATH_SPOOL "/var/spool/smtpd" #define PATH_OFFLINE "/offline" +#define PATH_PURGE "/purge" /* number of MX records to lookup */ #define MAX_MX_COUNT 10 @@ -888,7 +889,6 @@ enum queue_type { enum queue_kind { Q_INCOMING, Q_QUEUE, - Q_PURGE, Q_CORRUPT }; @@ -1220,4 +1220,6 @@ u_int32_t evpid_to_msgid(u_int64_t); u_int64_t msgid_to_evpid(u_int32_t); void log_imsg(int, int, struct imsg*); int ckdir(const char *, mode_t, uid_t, gid_t, int); +int rmtree(char *, int); +int mvpurge(char *, char *); const char *parse_smtp_response(char *, size_t, char **, int *); diff --git a/usr.sbin/smtpd/util.c b/usr.sbin/smtpd/util.c index 820915dc07a..b8e196b6650 100644 --- a/usr.sbin/smtpd/util.c +++ b/usr.sbin/smtpd/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.54 2011/12/18 22:51:29 chl Exp $ */ +/* $OpenBSD: util.c,v 1.55 2012/01/11 17:46:37 eric Exp $ */ /* * Copyright (c) 2000,2001 Markus Friedl. All rights reserved. @@ -33,6 +33,7 @@ #include <errno.h> #include <event.h> #include <fcntl.h> +#include <fts.h> #include <imsg.h> #include <libgen.h> #include <netdb.h> @@ -129,6 +130,87 @@ ckdir(const char *path, mode_t mode, uid_t owner, gid_t group, int create) return ret; } +int +rmtree(char *path, int keepdir) +{ + char *path_argv[2]; + FTS *fts; + FTSENT *e; + int ret, depth; + + path_argv[0] = path; + path_argv[1] = NULL; + ret = 0; + depth = 1; + + if ((fts = fts_open(path_argv, FTS_PHYSICAL, NULL)) == NULL) { + warn("fts_open: %s", path); + return (-1); + } + + while ((e = fts_read(fts)) != NULL) { + if (e->fts_number) { + depth--; + if (keepdir && e->fts_number == 1) + continue; + log_debug("rmdir %s", e->fts_path); + if (rmdir(e->fts_path) == -1) { + warn("rmdir: %s", e->fts_path); + ret = -1; + } + continue; + } + + if (S_ISDIR(e->fts_statp->st_mode)) { + e->fts_number = depth++; + continue; + } + + log_debug("unlink %s", e->fts_path); + if (unlink(e->fts_path) == -1) { + warn("unlink: %s", e->fts_path); + ret = -1; + } + } + + fts_close(fts); + + return (ret); +} + +int +mvpurge(char *from, char *to) +{ + size_t n; + int retry; + const char *sep; + char buf[MAXPATHLEN]; + + if ((n = strlen(to)) == 0) + fatalx("to is empty"); + + sep = (to[n - 1] == '/') ? "" : "/"; + retry = 0; + + again: + snprintf(buf, sizeof buf, "%s%s%u", to, sep, arc4random()); + log_debug("rename %s -> %s", from, buf); + if (rename(from, buf) == -1) { + /* ENOTDIR has actually 2 meanings, and incorrect input + * could lead to an infinite loop. Consider that after + * 20 tries something is hopelessly wrong. + */ + if (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR) { + if ((retry++) >= 20) + return (-1); + goto again; + } + return -1; + } + + return 0; +} + /* Close file, signifying temporary error condition (if any) to the caller. */ int |