summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Faurot <eric@cvs.openbsd.org>2012-01-11 17:46:38 +0000
committerEric Faurot <eric@cvs.openbsd.org>2012-01-11 17:46:38 +0000
commitcd75d18c8956e1c34385a351e7c916d308413e30 (patch)
tree4ea2632e80cffece6d42234d1b21d2e367a693f7
parent8ccc0ce2a9b3da61262dec658a93fc94497ef635 (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.c20
-rw-r--r--usr.sbin/smtpd/queue_fsqueue.c27
-rw-r--r--usr.sbin/smtpd/runner.c20
-rw-r--r--usr.sbin/smtpd/smtpd.c77
-rw-r--r--usr.sbin/smtpd/smtpd.h6
-rw-r--r--usr.sbin/smtpd/util.c84
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