summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorJacek Masiulaniec <jacekm@cvs.openbsd.org>2009-12-13 22:02:56 +0000
committerJacek Masiulaniec <jacekm@cvs.openbsd.org>2009-12-13 22:02:56 +0000
commita667cad6a34b7efa8b53891858ff5c77800b7813 (patch)
tree133d5ec6b4b43d4bf170a366fe97fea488aea75c /usr.sbin
parentfcdbb6f81071cffc3aed63cc4af0907fcd70ebef (diff)
Use safe fd limits in smtp, lka, queue, and control. Removes a
possibility for fd-starvation fatal when under heavy load.
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/smtpd/control.c71
-rw-r--r--usr.sbin/smtpd/enqueue.c5
-rw-r--r--usr.sbin/smtpd/lka.c8
-rw-r--r--usr.sbin/smtpd/queue.c9
-rw-r--r--usr.sbin/smtpd/runner.c5
-rw-r--r--usr.sbin/smtpd/smtp.c252
-rw-r--r--usr.sbin/smtpd/smtp_session.c19
-rw-r--r--usr.sbin/smtpd/smtpctl.c5
-rw-r--r--usr.sbin/smtpd/smtpd.c29
-rw-r--r--usr.sbin/smtpd/smtpd.h11
-rw-r--r--usr.sbin/smtpd/util.c32
11 files changed, 240 insertions, 206 deletions
diff --git a/usr.sbin/smtpd/control.c b/usr.sbin/smtpd/control.c
index cb6cc775450..1aaa40f32e5 100644
--- a/usr.sbin/smtpd/control.c
+++ b/usr.sbin/smtpd/control.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: control.c,v 1.42 2009/12/02 19:10:02 mk Exp $ */
+/* $OpenBSD: control.c,v 1.43 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -47,11 +47,11 @@ struct {
__dead void control_shutdown(void);
int control_init(void);
-int control_listen(struct smtpd *);
+void control_listen(struct smtpd *);
void control_cleanup(void);
void control_accept(int, short, void *);
struct ctl_conn *control_connbyfd(int);
-void control_close(int);
+void control_close(struct smtpd *, int);
void control_sig_handler(int, short, void *);
void control_dispatch_ext(int, short, void *);
void control_dispatch_lka(int, short, void *);
@@ -185,19 +185,22 @@ control_shutdown(void)
_exit(0);
}
-int
+void
control_listen(struct smtpd *env)
{
- if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
- log_warn("control_listen: listen");
- return (-1);
- }
+ int avail = availdesc();
- event_set(&control_state.ev, control_state.fd, EV_READ | EV_PERSIST,
+ if (listen(control_state.fd, CONTROL_BACKLOG) == -1)
+ fatal("control_listen");
+ avail--;
+
+ event_set(&control_state.ev, control_state.fd, EV_READ|EV_PERSIST,
control_accept, env);
event_add(&control_state.ev, NULL);
- return (0);
+ /* guarantee 2 fds to each accepted client */
+ if ((env->sc_maxconn = avail / 2) < 1)
+ fatalx("control_listen: fd starvation");
}
void
@@ -217,21 +220,16 @@ control_accept(int listenfd, short event, void *arg)
struct smtpd *env = arg;
len = sizeof(sun);
- if ((connfd = accept(listenfd,
- (struct sockaddr *)&sun, &len)) == -1) {
- if (errno != EWOULDBLOCK && errno != EINTR)
- log_warn("control_accept: accept");
- return;
+ if ((connfd = accept(listenfd, (struct sockaddr *)&sun, &len)) == -1) {
+ if (errno == EINTR || errno == ECONNABORTED)
+ return;
+ fatal("control_accept: accept");
}
session_socket_blockmode(connfd, BM_NONBLOCK);
- if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
- close(connfd);
- log_warn("control_accept");
- return;
- }
-
+ if ((c = calloc(1, sizeof(*c))) == NULL)
+ fatal(NULL);
imsg_init(&c->iev.ibuf, connfd);
c->iev.handler = control_dispatch_ext;
c->iev.events = EV_READ;
@@ -239,8 +237,15 @@ control_accept(int listenfd, short event, void *arg)
event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
c->iev.handler, env);
event_add(&c->iev.ev, NULL);
-
TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
+
+ env->stats->control.sessions++;
+ env->stats->control.sessions_active++;
+
+ if (env->stats->control.sessions_active >= env->sc_maxconn) {
+ log_warnx("ctl client limit hit, disabling new connections");
+ event_del(&control_state.ev);
+ }
}
struct ctl_conn *
@@ -256,7 +261,7 @@ control_connbyfd(int fd)
}
void
-control_close(int fd)
+control_close(struct smtpd *env, int fd)
{
struct ctl_conn *c;
@@ -264,13 +269,19 @@ control_close(int fd)
log_warn("control_close: fd %d: not found", fd);
return;
}
-
- msgbuf_clear(&c->iev.ibuf.w);
TAILQ_REMOVE(&ctl_conns, c, entry);
-
event_del(&c->iev.ev);
- close(c->iev.ibuf.fd);
+ imsg_clear(&c->iev.ibuf);
+ close(fd);
free(c);
+
+ env->stats->control.sessions_active--;
+
+ if (!event_pending(&control_state.ev, EV_READ, NULL) &&
+ env->stats->control.sessions_active < env->sc_maxconn) {
+ log_warnx("re-enabling ctl connections");
+ event_add(&control_state.ev, NULL);
+ }
}
/* ARGSUSED */
@@ -294,21 +305,21 @@ control_dispatch_ext(int fd, short event, void *arg)
if (event & EV_READ) {
if ((n = imsg_read(&c->iev.ibuf)) == -1 || n == 0) {
- control_close(fd);
+ control_close(env, fd);
return;
}
}
if (event & EV_WRITE) {
if (msgbuf_write(&c->iev.ibuf.w) < 0) {
- control_close(fd);
+ control_close(env, fd);
return;
}
}
for (;;) {
if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
- control_close(fd);
+ control_close(env, fd);
return;
}
diff --git a/usr.sbin/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c
index 251a2346c1f..c1a29d63b60 100644
--- a/usr.sbin/smtpd/enqueue.c
+++ b/usr.sbin/smtpd/enqueue.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: enqueue.c,v 1.29 2009/12/12 14:03:59 jacekm Exp $ */
+/* $OpenBSD: enqueue.c,v 1.30 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2005 Henning Brauer <henning@bulabula.org>
@@ -188,7 +188,8 @@ enqueue(int argc, char *argv[])
signal(SIGALRM, sighdlr);
alarm(300);
- msg.fd = open_connection();
+ if ((msg.fd = open_connection()) == -1)
+ errx(1, "server too busy");
/* init session */
pcb = client_init(msg.fd, open("/dev/null", O_RDONLY), "localhost",
diff --git a/usr.sbin/smtpd/lka.c b/usr.sbin/smtpd/lka.c
index a5d1099403b..e32c77dc342 100644
--- a/usr.sbin/smtpd/lka.c
+++ b/usr.sbin/smtpd/lka.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka.c,v 1.94 2009/11/13 11:27:51 jacekm Exp $ */
+/* $OpenBSD: lka.c,v 1.95 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -665,6 +665,12 @@ lka(struct smtpd *env)
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
+ /*
+ * lka opens all kinds of files and sockets, so bump the limit further.
+ * XXX: need to analyse the exact hard limit.
+ */
+ fdlimit(getdtablesize() * 2);
+
config_pipes(env, peers, nitems(peers));
config_peers(env, peers, nitems(peers));
diff --git a/usr.sbin/smtpd/queue.c b/usr.sbin/smtpd/queue.c
index c9074ea3b6e..d43164ac4c4 100644
--- a/usr.sbin/smtpd/queue.c
+++ b/usr.sbin/smtpd/queue.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: queue.c,v 1.73 2009/11/08 21:40:05 gilles Exp $ */
+/* $OpenBSD: queue.c,v 1.74 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -579,6 +579,13 @@ queue(struct smtpd *env)
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
+ /*
+ * queue opens fds for four purposes: smtp, mta, mda, and bounces.
+ * Therefore, double the fdlimit the second time to achieve a 4x
+ * increase relative to default.
+ */
+ fdlimit(getdtablesize() * 2);
+
config_pipes(env, peers, nitems(peers));
config_peers(env, peers, nitems(peers));
diff --git a/usr.sbin/smtpd/runner.c b/usr.sbin/smtpd/runner.c
index 594d7d458da..e68cd5d9176 100644
--- a/usr.sbin/smtpd/runner.c
+++ b/usr.sbin/smtpd/runner.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: runner.c,v 1.72 2009/11/08 23:08:56 gilles Exp $ */
+/* $OpenBSD: runner.c,v 1.73 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -548,6 +548,9 @@ runner(struct smtpd *env)
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
+ /* see fdlimit()-related comment in queue.c */
+ fdlimit(getdtablesize() * 2);
+
config_pipes(env, peers, nitems(peers));
config_peers(env, peers, nitems(peers));
diff --git a/usr.sbin/smtpd/smtp.c b/usr.sbin/smtpd/smtp.c
index 14465f9d355..4a944a20208 100644
--- a/usr.sbin/smtpd/smtp.c
+++ b/usr.sbin/smtpd/smtp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp.c,v 1.64 2009/11/08 21:40:05 gilles Exp $ */
+/* $OpenBSD: smtp.c,v 1.65 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -25,6 +25,7 @@
#include <sys/socket.h>
#include <ctype.h>
+#include <errno.h>
#include <event.h>
#include <netdb.h>
#include <pwd.h>
@@ -48,9 +49,9 @@ void smtp_dispatch_runner(int, short, void *);
void smtp_setup_events(struct smtpd *);
void smtp_disable_events(struct smtpd *);
void smtp_pause(struct smtpd *);
-void smtp_resume(struct smtpd *);
+int smtp_enqueue(struct smtpd *, uid_t *);
void smtp_accept(int, short, void *);
-void session_auth_pickup(struct session *, char *, size_t);
+struct session *smtp_new(struct listener *);
struct session *session_lookup(struct smtpd *, u_int64_t);
void
@@ -507,66 +508,11 @@ smtp_dispatch_control(int sig, short event, void *p)
break;
switch (imsg.hdr.type) {
- case IMSG_SMTP_ENQUEUE: {
- static struct listener l;
- struct addrinfo hints, *res;
- struct session *s;
- uid_t euid;
- int fd[2];
-
- bzero(&l, sizeof(l));
- l.env = env;
-
- memcpy(&euid, imsg.data, sizeof(euid));
-
- if (env->stats->smtp.sessions_active >=
- env->sc_maxconn) {
- log_warnx("denying local connection, too many"
- " sessions active");
- imsg_compose_event(iev, IMSG_SMTP_ENQUEUE,
- imsg.hdr.peerid, 0, -1, NULL, 0);
- break;
- }
-
- if (socketpair(
- AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd) == -1)
- fatal("socketpair");
-
- if ((s = calloc(1, sizeof(*s))) == NULL)
- fatal(NULL);
-
- s->s_id = generate_uid();
- s->s_fd = fd[0];
- s->s_env = env;
- s->s_l = &l;
- s->s_msg.flags |= F_MESSAGE_ENQUEUED;
- (void)strlcpy(s->s_msg.tag, s->s_l->tag, sizeof(s->s_msg.tag));
-
- bzero(&hints, sizeof(hints));
- hints.ai_family = PF_UNSPEC;
- hints.ai_flags = AI_NUMERICHOST;
-
- if (getaddrinfo("::1", NULL, &hints, &res) != 0)
- fatal("getaddrinfo");
-
- memcpy(&s->s_ss, res->ai_addr, res->ai_addrlen);
-
- env->stats->smtp.sessions++;
- env->stats->smtp.sessions_active++;
-
- bsnprintf(s->s_hostname, sizeof(s->s_hostname),
- "%d@localhost", euid);
- strlcpy(s->s_msg.session_hostname, s->s_hostname,
- sizeof(s->s_msg.session_hostname));
-
- SPLAY_INSERT(sessiontree, &s->s_env->sc_sessions, s);
-
- session_init(s->s_l, s);
-
+ case IMSG_SMTP_ENQUEUE:
imsg_compose_event(iev, IMSG_SMTP_ENQUEUE,
- imsg.hdr.peerid, 0, fd[1], NULL, 0);
+ imsg.hdr.peerid, 0, smtp_enqueue(env, imsg.data),
+ NULL, 0);
break;
- }
case IMSG_SMTP_PAUSE:
smtp_pause(env);
break;
@@ -618,62 +564,11 @@ smtp_dispatch_runner(int sig, short event, void *p)
break;
switch (imsg.hdr.type) {
- case IMSG_SMTP_ENQUEUE: {
- static struct listener l;
- struct addrinfo hints, *res;
- struct session *s;
- int fd[2];
-
- bzero(&l, sizeof(l));
- l.env = env;
-
- if (env->stats->smtp.sessions_active >=
- env->sc_maxconn) {
- log_warnx("denying internal connection, too many"
- " sessions active");
- imsg_compose_event(iev, IMSG_SMTP_ENQUEUE, 0, 0, -1,
- imsg.data, sizeof(struct message));
- break;
- }
-
- if (socketpair(
- AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd) == -1)
- fatal("socketpair");
-
- if ((s = calloc(1, sizeof(*s))) == NULL)
- fatal(NULL);
-
- s->s_id = generate_uid();
- s->s_fd = fd[0];
- s->s_env = env;
- s->s_l = &l;
- s->s_msg.flags |= F_MESSAGE_ENQUEUED|F_MESSAGE_BOUNCE;
-
- bzero(&hints, sizeof(hints));
- hints.ai_family = PF_UNSPEC;
- hints.ai_flags = AI_NUMERICHOST;
-
- if (getaddrinfo("::1", NULL, &hints, &res) != 0)
- fatal("getaddrinfo");
-
- memcpy(&s->s_ss, res->ai_addr, res->ai_addrlen);
-
- env->stats->smtp.sessions++;
- env->stats->smtp.sessions_active++;
-
- strlcpy(s->s_hostname, "localhost",
- sizeof(s->s_hostname));
- strlcpy(s->s_msg.session_hostname, s->s_hostname,
- sizeof(s->s_msg.session_hostname));
-
- SPLAY_INSERT(sessiontree, &s->s_env->sc_sessions, s);
-
- session_init(s->s_l, s);
-
- imsg_compose_event(iev, IMSG_SMTP_ENQUEUE, 0, 0, fd[1],
- imsg.data, sizeof(struct message));
+ case IMSG_SMTP_ENQUEUE:
+ imsg_compose_event(iev, IMSG_SMTP_ENQUEUE, 0, 0,
+ smtp_enqueue(env, NULL), imsg.data,
+ sizeof(struct message));
break;
- }
default:
log_warnx("smtp_dispatch_runner: got imsg %d",
imsg.hdr.type);
@@ -764,6 +659,7 @@ void
smtp_setup_events(struct smtpd *env)
{
struct listener *l;
+ int avail = availdesc();
TAILQ_FOREACH(l, env->sc_listeners, entry) {
log_debug("smtp_setup_events: listen on %s port %d flags 0x%01x"
@@ -774,10 +670,17 @@ smtp_setup_events(struct smtpd *env)
if (listen(l->fd, SMTPD_BACKLOG) == -1)
fatal("listen");
l->env = env;
- event_set(&l->ev, l->fd, EV_READ, smtp_accept, l);
+ event_set(&l->ev, l->fd, EV_READ|EV_PERSIST, smtp_accept, l);
event_add(&l->ev, NULL);
ssl_setup(env, l);
+ avail--;
}
+
+ /* guarantee 2 fds to each accepted client */
+ if ((env->sc_maxconn = avail / 2) < 1)
+ fatalx("smtp_setup_events: fd starvation");
+
+ log_debug("smtp: will accept at most %d clients", env->sc_maxconn);
}
void
@@ -794,6 +697,7 @@ smtp_disable_events(struct smtpd *env)
}
free(env->sc_listeners);
env->sc_listeners = NULL;
+ env->sc_maxconn = 0;
}
void
@@ -820,45 +724,115 @@ smtp_resume(struct smtpd *env)
event_add(&l->ev, NULL);
}
+int
+smtp_enqueue(struct smtpd *env, uid_t *euid)
+{
+ static struct listener local, *l;
+ static struct sockaddr_storage sa;
+ struct session *s;
+ int fd[2];
+
+ if (l == NULL) {
+ struct addrinfo hints, *res;
+
+ l = &local;
+ strlcpy(l->tag, "local", sizeof(l->tag));
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if (getaddrinfo("::1", NULL, &hints, &res))
+ fatal("getaddrinfo");
+ memcpy(&sa, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ }
+ l->env = env;
+
+ /*
+ * Some enqueue requests buffered in IMSG may still arrive even after
+ * call to smtp_pause() because enqueue listener is not a real socket
+ * and thus cannot be paused properly.
+ */
+ if (env->sc_opts & SMTPD_SMTP_PAUSED)
+ return (-1);
+
+ if ((s = smtp_new(l)) == NULL)
+ return (-1);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd))
+ fatal("socketpair");
+
+ s->s_fd = fd[0];
+ s->s_ss = sa;
+ s->s_msg.flags |= F_MESSAGE_ENQUEUED;
+
+ if (euid)
+ bsnprintf(s->s_hostname, sizeof(s->s_hostname), "%d@localhost",
+ *euid);
+ else {
+ strlcpy(s->s_hostname, "localhost", sizeof(s->s_hostname));
+ s->s_msg.flags |= F_MESSAGE_BOUNCE;
+ }
+
+ strlcpy(s->s_msg.session_hostname, s->s_hostname,
+ sizeof(s->s_msg.session_hostname));
+
+ session_init(l, s);
+
+ return (fd[1]);
+}
+
void
smtp_accept(int fd, short event, void *p)
{
- int s_fd;
- struct sockaddr_storage ss;
struct listener *l = p;
struct session *s;
socklen_t len;
- log_debug("smtp_accept: incoming client on listener: %p", l);
- len = sizeof(struct sockaddr_storage);
- if ((s_fd = accept(l->fd, (struct sockaddr *)&ss, &len)) == -1) {
- event_del(&l->ev);
+ if ((s = smtp_new(l)) == NULL)
return;
- }
- log_debug("smtp_accept: accepted client on listener: %p", l);
- if ((s = calloc(1, sizeof(*s))) == NULL)
- fatal(NULL);
len = sizeof(s->s_ss);
+ if ((s->s_fd = accept(fd, (struct sockaddr *)&s->s_ss, &len)) == -1) {
+ if (errno == EINTR || errno == ECONNABORTED)
+ return;
+ fatal("smtp_accept");
+ }
- s->s_id = generate_uid();
- s->s_fd = s_fd;
- s->s_env = l->env;
- s->s_l = l;
+ dns_query_ptr(l->env, &s->s_ss, s->s_id);
+}
- (void)memcpy(&s->s_ss, &ss, sizeof(s->s_ss));
- event_add(&l->ev, NULL);
+struct session *
+smtp_new(struct listener *l)
+{
+ struct smtpd *env = l->env;
+ struct session *s;
+
+ log_debug("smtp_new: incoming client on listener: %p", l);
- s->s_env->stats->smtp.sessions++;
- s->s_env->stats->smtp.sessions_active++;
+ if (env->sc_opts & SMTPD_SMTP_PAUSED)
+ fatalx("smtp_new: unexpected client");
- if (s->s_env->stats->smtp.sessions_active == s->s_env->sc_maxconn)
- event_del(&l->ev);
+ if (env->stats->smtp.sessions_active >= env->sc_maxconn) {
+ log_warnx("client limit hit, disabling incoming connections");
+ smtp_pause(env);
+ return (NULL);
+ }
+
+ if ((s = calloc(1, sizeof(*s))) == NULL)
+ fatal(NULL);
+ s->s_id = generate_uid();
+ s->s_env = env;
+ s->s_l = l;
+ strlcpy(s->s_msg.tag, l->tag, sizeof(s->s_msg.tag));
+ SPLAY_INSERT(sessiontree, &env->sc_sessions, s);
- dns_query_ptr(l->env, &s->s_ss, s->s_id);
+ env->stats->smtp.sessions++;
+ env->stats->smtp.sessions_active++;
- SPLAY_INSERT(sessiontree, &s->s_env->sc_sessions, s);
+ return (s);
}
/*
diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c
index 03b5a4aa4a5..ca5810b4600 100644
--- a/usr.sbin/smtpd/smtp_session.c
+++ b/usr.sbin/smtpd/smtp_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp_session.c,v 1.126 2009/11/16 10:38:11 jacekm Exp $ */
+/* $OpenBSD: smtp_session.c,v 1.127 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -418,7 +418,6 @@ session_rfc5321_mail_handler(struct session *s, char *args)
s->s_msg.id = s->s_id;
s->s_msg.session_id = s->s_id;
s->s_msg.session_ss = s->s_ss;
- (void)strlcpy(s->s_msg.tag, s->s_l->tag, sizeof(s->s_msg.tag));
log_debug("session_rfc5321_mail_handler: sending notification to mfa");
@@ -945,6 +944,8 @@ session_error(struct bufferevent *bev, short event, void *p)
void
session_destroy(struct session *s)
{
+ size_t resume;
+
log_debug("session_destroy: killing client: %p", s);
if (s->s_flags & F_WRITEONLY)
@@ -967,14 +968,12 @@ session_destroy(struct session *s)
fatal("session_destroy: close");
s->s_env->stats->smtp.sessions_active--;
- if (s->s_env->stats->smtp.sessions_active < s->s_env->sc_maxconn &&
- !(s->s_msg.flags & F_MESSAGE_ENQUEUED)) {
- /*
- * if our session_destroy occurs because of a configuration
- * reload, our listener no longer exist and s->s_l is NULL.
- */
- if (s->s_l != NULL)
- event_add(&s->s_l->ev, NULL);
+
+ /* resume when session count decreases to 95% */
+ resume = s->s_env->sc_maxconn * 95 / 100;
+ if (s->s_env->stats->smtp.sessions_active == resume) {
+ log_warnx("re-enabling incoming connections");
+ smtp_resume(s->s_env);
}
SPLAY_REMOVE(sessiontree, &s->s_env->sc_sessions, s);
diff --git a/usr.sbin/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c
index d3794fdaeb0..89446dba2e1 100644
--- a/usr.sbin/smtpd/smtpctl.c
+++ b/usr.sbin/smtpd/smtpctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpctl.c,v 1.41 2009/12/13 21:48:21 jacekm Exp $ */
+/* $OpenBSD: smtpctl.c,v 1.42 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -273,6 +273,9 @@ show_stats_output(struct imsg *imsg)
stats = imsg->data;
+ printf("control.sessions=%zd\n", stats->control.sessions);
+ printf("control.sessions_active=%zd\n", stats->control.sessions_active);
+
printf("mda.errors.write_system=%zd\n", stats->mda.write_error);
printf("mta.sessions=%zd\n", stats->mta.sessions);
printf("mta.sessions.active=%zd\n", stats->mta.sessions_active);
diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c
index 6097538a0b3..071db08fd75 100644
--- a/usr.sbin/smtpd/smtpd.c
+++ b/usr.sbin/smtpd/smtpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.c,v 1.89 2009/11/14 18:48:05 chl Exp $ */
+/* $OpenBSD: smtpd.c,v 1.90 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -26,7 +26,6 @@
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/uio.h>
-#include <sys/resource.h>
#include <sys/mman.h>
#include <err.h>
@@ -867,7 +866,6 @@ main(int argc, char *argv[])
struct event ev_sigchld;
struct event ev_sighup;
struct timeval tv;
- struct rlimit rl;
struct peer peers[] = {
{ PROC_CONTROL, parent_dispatch_control },
{ PROC_LKA, parent_dispatch_lka },
@@ -949,22 +947,6 @@ main(int argc, char *argv[])
env.stats->parent.start = time(NULL);
- if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
- fatal("smtpd: failed to get resource limit");
-
- log_debug("smtpd: max open files %lld", rl.rlim_max);
-
- /*
- * Allow the maximum number of open file descriptors for this
- * login class (which should be the class "daemon" by default).
- */
- rl.rlim_cur = rl.rlim_max;
- if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
- fatal("smtpd: failed to set resource limit");
-
- env.sc_maxconn = (rl.rlim_cur / 4) * 3;
- log_debug("smtpd: will accept at most %d clients", env.sc_maxconn);
-
fork_peers(&env);
event_init();
@@ -996,6 +978,15 @@ fork_peers(struct smtpd *env)
{
SPLAY_INIT(&env->children);
+ /*
+ * Each process has fd soft limit doubled. This is done with smtp,
+ * mta and mda in mind, because all of them require 2 fds per one
+ * session. Additionally, processes such as queue may bump it even
+ * further as in the worst case scenarios they could hold many more
+ * fds open.
+ */
+ fdlimit(getdtablesize() * 2);
+
env->sc_instances[PROC_CONTROL] = 1;
env->sc_instances[PROC_LKA] = 1;
env->sc_instances[PROC_MDA] = 1;
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index 43830fb1492..9d97d541903 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.160 2009/12/12 10:33:11 jacekm Exp $ */
+/* $OpenBSD: smtpd.h,v 1.161 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -673,6 +673,11 @@ struct s_mda {
size_t write_error;
};
+struct s_control {
+ size_t sessions;
+ size_t sessions_active;
+};
+
struct stats {
struct s_parent parent;
struct s_queue queue;
@@ -680,6 +685,7 @@ struct stats {
struct s_session mta;
struct s_mda mda;
struct s_session smtp;
+ struct s_control control;
};
struct sched {
@@ -937,6 +943,7 @@ SPLAY_PROTOTYPE(batchtree, batch, b_nodes, batch_cmp);
/* smtp.c */
pid_t smtp(struct smtpd *);
+void smtp_resume(struct smtpd *);
/* smtp_session.c */
void session_init(struct listener *, struct session *);
@@ -1012,3 +1019,5 @@ char *message_get_errormsg(struct message *);
void sa_set_port(struct sockaddr *, int);
struct path *path_dup(struct path *);
u_int64_t generate_uid(void);
+void fdlimit(int);
+int availdesc(void);
diff --git a/usr.sbin/smtpd/util.c b/usr.sbin/smtpd/util.c
index a7af7b6dbd8..29c040c3e63 100644
--- a/usr.sbin/smtpd/util.c
+++ b/usr.sbin/smtpd/util.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: util.c,v 1.29 2009/11/08 21:40:05 gilles Exp $ */
+/* $OpenBSD: util.c,v 1.30 2009/12/13 22:02:55 jacekm Exp $ */
/*
* Copyright (c) 2000,2001 Markus Friedl. All rights reserved.
@@ -24,6 +24,7 @@
#include <sys/tree.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/resource.h>
#include <err.h>
#include <ctype.h>
@@ -449,3 +450,32 @@ generate_uid(void)
return (id);
}
+
+void
+fdlimit(int n)
+{
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
+ fatal("fdlimit: getrlimit");
+ if ((u_int)n > rl.rlim_max) {
+ log_warnx("fdlimit: need %d, limit %llu", n, rl.rlim_max);
+ fatalx("fdlimit: hard limit reached");
+ }
+ rl.rlim_cur = n;
+ if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
+ fatal("fdlimit: getrlimit");
+}
+
+int
+availdesc(void)
+{
+ int avail;
+
+ avail = getdtablesize();
+ avail -= 3; /* stdin, stdout, stderr */
+ avail -= PROC_COUNT; /* imsg channels */
+ avail -= 5; /* safety buffer */
+
+ return (avail);
+}