diff options
author | Jacek Masiulaniec <jacekm@cvs.openbsd.org> | 2009-12-13 22:02:56 +0000 |
---|---|---|
committer | Jacek Masiulaniec <jacekm@cvs.openbsd.org> | 2009-12-13 22:02:56 +0000 |
commit | a667cad6a34b7efa8b53891858ff5c77800b7813 (patch) | |
tree | 133d5ec6b4b43d4bf170a366fe97fea488aea75c /usr.sbin | |
parent | fcdbb6f81071cffc3aed63cc4af0907fcd70ebef (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.c | 71 | ||||
-rw-r--r-- | usr.sbin/smtpd/enqueue.c | 5 | ||||
-rw-r--r-- | usr.sbin/smtpd/lka.c | 8 | ||||
-rw-r--r-- | usr.sbin/smtpd/queue.c | 9 | ||||
-rw-r--r-- | usr.sbin/smtpd/runner.c | 5 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtp.c | 252 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtp_session.c | 19 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpctl.c | 5 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.c | 29 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 11 | ||||
-rw-r--r-- | usr.sbin/smtpd/util.c | 32 |
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); +} |