Improve logging to allow per- server/location log files. The log
authorreyk <reyk@openbsd.org>
Tue, 5 Aug 2014 15:36:59 +0000 (15:36 +0000)
committerreyk <reyk@openbsd.org>
Tue, 5 Aug 2014 15:36:59 +0000 (15:36 +0000)
files can also be owned by root now: they're opened by the parent and
send to the logger process with fd passing.  This also works with reload.

ok deraadt@

usr.sbin/httpd/config.c
usr.sbin/httpd/httpd.c
usr.sbin/httpd/httpd.conf.5
usr.sbin/httpd/httpd.h
usr.sbin/httpd/logger.c
usr.sbin/httpd/parse.y
usr.sbin/httpd/server.c

index 958a9f0..cbb2973 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.17 2014/08/05 14:35:47 deraadt Exp $     */
+/*     $OpenBSD: config.c,v 1.18 2014/08/05 15:36:59 reyk Exp $        */
 
 /*
  * Copyright (c) 2011 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -60,7 +60,7 @@ config_init(struct httpd *env)
 
                ps->ps_what[PROC_PARENT] = CONFIG_ALL;
                ps->ps_what[PROC_SERVER] = CONFIG_SERVERS|CONFIG_MEDIA;
-               ps->ps_what[PROC_LOGGER] = 0;
+               ps->ps_what[PROC_LOGGER] = CONFIG_SERVERS;
        }
 
        /* Other configuration */
@@ -269,6 +269,22 @@ config_getserver_config(struct httpd *env, struct server *srv,
                f = SRVFLAG_SSL;
                srv_conf->flags |= srv->srv_conf.flags & f;
 
+               f = SRVFLAG_ACCESS_LOG;
+               if ((srv_conf->flags & f) == 0) {
+                       srv_conf->flags |= srv->srv_conf.flags & f;
+                       (void)strlcpy(srv_conf->accesslog,
+                           srv->srv_conf.accesslog,
+                           sizeof(srv_conf->accesslog));
+               }
+
+               f = SRVFLAG_ERROR_LOG;
+               if ((srv_conf->flags & f) == 0) {
+                       srv_conf->flags |= srv->srv_conf.flags & f;
+                       (void)strlcpy(srv_conf->errorlog,
+                           srv->srv_conf.errorlog,
+                           sizeof(srv_conf->errorlog));
+               }
+
                DPRINTF("%s: %s %d location \"%s\", "
                    "parent \"%s\", flags: %s",
                    __func__, ps->ps_title[privsep_process], ps->ps_instance,
index 4064abe..220f537 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: httpd.c,v 1.16 2014/08/05 09:24:21 jsg Exp $  */
+/*     $OpenBSD: httpd.c,v 1.17 2014/08/05 15:36:59 reyk Exp $ */
 
 /*
  * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
@@ -226,6 +226,8 @@ main(int argc, char *argv[])
        ps->ps_instances[PROC_SERVER] = env->sc_prefork_server;
        ps->ps_ninstances = env->sc_prefork_server;
 
+       if (env->sc_chroot == NULL)
+               env->sc_chroot = ps->ps_pw->pw_dir;
        for (proc = 0; proc < nitems(procs); proc++)
                procs[proc].p_chroot = env->sc_chroot;
 
@@ -434,6 +436,10 @@ parent_dispatch_logger(int fd, struct privsep_proc *p, struct imsg *imsg)
        case IMSG_CFG_DONE:
                parent_configure_done(env);
                break;
+       case IMSG_LOG_OPEN:
+               if (logger_open_priv(imsg) == -1)
+                       fatalx("failed to open log file");
+               break;
        default:
                return (-1);
        }
index fa805ed..a0930d1 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: httpd.conf.5,v 1.22 2014/08/05 09:24:21 jsg Exp $
+.\"    $OpenBSD: httpd.conf.5,v 1.23 2014/08/05 15:36:59 reyk Exp $
 .\"
 .\" Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
 .\"
@@ -152,7 +152,19 @@ except
 .Ic listen on
 and
 .Ic location .
-.It Ic log style Op Ar style
+.It Ic log access Ar name
+Set the
+.Ar name
+of the access log file relative to the log directory.
+If not specified, it defaults to
+.Pa access.log .
+.It Ic log error Ar name
+Set the
+.Ar name
+of the error log file relative to the log directory.
+If not specified, it defaults to
+.Pa error.log .
+.It Ic log style Ar style
 Set the logging style.
 The
 .Ar style
index d3a6ca8..2692150 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: httpd.h,v 1.43 2014/08/05 09:24:21 jsg Exp $  */
+/*     $OpenBSD: httpd.h,v 1.44 2014/08/05 15:36:59 reyk Exp $ */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -35,8 +35,9 @@
 #define HTTPD_DOCROOT          "/htdocs"
 #define HTTPD_INDEX            "index.html"
 #define HTTPD_FCGI_SOCKET      "/run/slowcgi.sock"
-#define HTTPD_ACCESS_LOG       "/logs/access.log"
-#define HTTPD_ERROR_LOG                "/logs/error.log"
+#define HTTPD_LOGROOT          "/logs"
+#define HTTPD_ACCESS_LOG       "access.log"
+#define HTTPD_ERROR_LOG                "error.log"
 #define HTTPD_SSL_KEY          "/conf/server.key"
 #define HTTPD_SSL_CERT         "/conf/server.crt"
 #define FD_RESERVE             5
@@ -186,7 +187,8 @@ enum imsg_type {
        IMSG_CFG_MEDIA,
        IMSG_CFG_DONE,
        IMSG_LOG_ACCESS,
-       IMSG_LOG_ERROR
+       IMSG_LOG_ERROR,
+       IMSG_LOG_OPEN
 };
 
 enum privsep_procid {
@@ -312,11 +314,13 @@ SPLAY_HEAD(client_tree, client);
 #define SRVFLAG_SYSLOG         0x0800
 #define SRVFLAG_NO_SYSLOG      0x1000
 #define SRVFLAG_SSL            0x2000
+#define SRVFLAG_ACCESS_LOG     0x4000
+#define SRVFLAG_ERROR_LOG      0x8000
 
 #define SRVFLAG_BITS                                                   \
        "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX"           \
        "\05ROOT\06LOCATION\07FCGI\10NO_FCGI\11LOG\12NO_LOG\13SOCKET"   \
-       "\14SYSLOG\15NO_SYSLOG"
+       "\14SYSLOG\15NO_SYSLOG\16SSL\17ACCESS_LOG\20ERROR_LOG"
 
 #define TCPFLAG_NODELAY                0x01
 #define TCPFLAG_NNODELAY       0x02
@@ -338,6 +342,14 @@ enum log_format {
        LOG_FORMAT_CONNECTION
 };
 
+struct log_file {
+       char                    log_name[NAME_MAX];
+       int                     log_fd;
+       u_int32_t               log_id;
+       TAILQ_ENTRY(log_file)   log_entry;
+};
+TAILQ_HEAD(log_files, log_file) log_files;
+
 struct server_config {
        u_int32_t                id;
        char                     name[MAXHOSTNAMELEN];
@@ -345,11 +357,13 @@ struct server_config {
        char                     index[NAME_MAX];
        char                     root[MAXPATHLEN];
        char                     socket[MAXPATHLEN];
+       char                     accesslog[NAME_MAX];
+       char                     errorlog[NAME_MAX];
 
        in_port_t                port;
        struct sockaddr_storage  ss;
-       int                      prefixlen;
        struct timeval           timeout;
+       int                      prefixlen;
 
        u_int16_t                flags;
        u_int8_t                 tcpflags;
@@ -359,6 +373,8 @@ struct server_config {
        u_int8_t                 tcpipminttl;
 
        enum log_format          logformat;
+       struct log_file         *logaccess;
+       struct log_file         *logerror;
 
        TAILQ_ENTRY(server_config) entry;
 };
@@ -439,10 +455,8 @@ void        server_write(struct bufferevent *, void *);
 void    server_read(struct bufferevent *, void *);
 void    server_error(struct bufferevent *, short, void *);
 void    server_log(struct client *, const char *);
-void    server_log_access(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
-void    server_log_error(const char *, ...)
-           __attribute__((__format__ (printf, 1, 2)));
+void    server_sendlog(struct server_config *, int, const char *, ...)
+           __attribute__((__format__ (printf, 3, 4)));
 void    server_close(struct client *, const char *);
 void    server_dump(struct client *, const void *, size_t);
 int     server_client_cmp(struct client *, struct client *);
@@ -456,6 +470,10 @@ int         server_bufferevent_write(struct client *, void *, size_t);
 void    server_inflight_dec(struct client *, const char *);
 struct server *
         server_byaddr(struct sockaddr *, in_port_t);
+struct server_config *
+        serverconfig_byid(u_int32_t);
+int     server_foreach(int (*)(struct server *,
+           struct server_config *, void *), void *);
 
 SPLAY_PROTOTYPE(client_tree, client, clt_nodes, server_client_cmp);
 
@@ -585,5 +603,6 @@ int  config_getmedia(struct httpd *, struct imsg *);
 
 /* logger.c */
 pid_t   logger(struct privsep *, struct privsep_proc *);
+int     logger_open_priv(struct imsg *);
 
 #endif /* _HTTPD_H */
index 1b727fd..5c9a4d7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: logger.c,v 1.2 2014/08/04 15:57:25 reyk Exp $ */
+/*     $OpenBSD: logger.c,v 1.3 2014/08/05 15:36:59 reyk Exp $ */
 
 /*
  * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
@@ -18,6 +18,8 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
 
 #include <net/if.h>
 
@@ -36,14 +38,16 @@ int          logger_dispatch_server(int, struct privsep_proc *,
                    struct imsg *);
 void            logger_shutdown(void);
 void            logger_close(void);
+struct log_file *logger_open_file(const char *);
+int             logger_open_fd(struct imsg *);
+int             logger_open(struct server *, struct server_config *, void *);
 void            logger_init(struct privsep *, struct privsep_proc *p, void *);
 int             logger_start(void);
 int             logger_log(struct imsg *);
 
 static struct httpd            *env = NULL;
 int                             proc_id;
-int                             log_fd = -1;
-int                             error_fd = -1;
+static u_int32_t                last_log_id = 0;
 
 static struct privsep_proc procs[] = {
        { "parent",     PROC_PARENT,    logger_dispatch_parent },
@@ -64,19 +68,6 @@ logger_shutdown(void)
        config_purge(env, CONFIG_ALL);
 }
 
-void
-logger_close(void)
-{
-       if (log_fd != -1) {
-               close(log_fd);
-               log_fd = -1;
-       }
-       if (error_fd != -1) {
-               close(error_fd);
-               error_fd = -1;
-       }
-}
-
 void
 logger_init(struct privsep *ps, struct privsep_proc *p, void *arg)
 {
@@ -88,43 +79,188 @@ logger_init(struct privsep *ps, struct privsep_proc *p, void *arg)
 
        /* We use a custom shutdown callback */
        p->p_shutdown = logger_shutdown;
+
+       TAILQ_INIT(&log_files);
+}
+
+void
+logger_close(void)
+{
+       struct log_file *log, *next;
+
+       TAILQ_FOREACH_SAFE(log, &log_files, log_entry, next) {
+               if (log->log_fd != -1) {
+                       close(log->log_fd);
+                       log->log_fd = -1;
+               }
+               TAILQ_REMOVE(&log_files, log, log_entry);
+       }
+}
+
+struct log_file *
+logger_open_file(const char *name)
+{
+       struct log_file *log;
+       struct iovec     iov[2];
+
+       if ((log = calloc(1, sizeof(*log))) == NULL) {
+               log_warn("failed to allocate log %s", name);
+               return (NULL);
+       }
+
+       log->log_id = ++last_log_id;
+       (void)strlcpy(log->log_name, name, sizeof(log->log_name));      
+
+       /* The file will be opened by the parent process */
+       log->log_fd = -1;
+
+       iov[0].iov_base = &log->log_id;
+       iov[0].iov_len = sizeof(log->log_id);
+       iov[1].iov_base = log->log_name;
+       iov[1].iov_len = strlen(log->log_name) + 1;
+
+       proc_composev_imsg(env->sc_ps, PROC_PARENT, -1, IMSG_LOG_OPEN, -1,
+           iov, 2);
+
+       TAILQ_INSERT_TAIL(&log_files, log, log_entry);
+
+       return (log);
 }
 
 int
-logger_start(void)
+logger_open_fd(struct imsg *imsg)
 {
-       logger_close();
-       if ((log_fd = open(HTTPD_ACCESS_LOG,
-           O_WRONLY|O_APPEND|O_CREAT, 0644)) == -1) {
-               log_warn("failed to open %s", HTTPD_ACCESS_LOG);
+       struct log_file         *log;
+       u_int32_t                id;
+
+       IMSG_SIZE_CHECK(imsg, &id);
+       memcpy(&id, imsg->data, sizeof(id));
+
+       TAILQ_FOREACH(log, &log_files, log_entry) {
+               if (log->log_id == id) {
+                       DPRINTF("%s: received log fd %d, file %s",
+                           __func__, imsg->fd, log->log_name);
+                       log->log_fd = imsg->fd;
+                       return (0);
+               }
+       }
+
+       return (-1);
+}
+
+int
+logger_open_priv(struct imsg *imsg)
+{
+       char                     path[MAXPATHLEN];
+       char                     name[NAME_MAX], *p;
+       u_int32_t                id;
+       size_t                   len;
+       int                      fd;
+
+       /* called from the priviled process */
+       IMSG_SIZE_CHECK(imsg, &id);
+       memcpy(&id, imsg->data, sizeof(id));
+       p = (char *)imsg->data + sizeof(id);
+
+       if ((size_t)snprintf(name, sizeof(name), "/%s", p) >= sizeof(name))
+               return (-1);
+       if ((len = (size_t)snprintf(path, sizeof(path), "%s%s",
+           env->sc_chroot, HTTPD_LOGROOT)) >= sizeof(path))
+               return (-1);
+
+       p = path + len;
+       len = sizeof(path) - len;
+
+       if (canonicalize_path(name, p, len) == NULL) {
+               log_warnx("invalid log name");
                return (-1);
        }
-       if ((error_fd = open(HTTPD_ERROR_LOG,
-           O_WRONLY|O_APPEND|O_CREAT, 0644)) == -1) {
-               log_warn("failed to open %s", HTTPD_ERROR_LOG);
+
+       if ((fd = open(path, O_WRONLY|O_APPEND|O_CREAT, 0644)) == -1) {
+               log_warn("failed to open %s", path);
                return (-1);
        }
+
+       proc_compose_imsg(env->sc_ps, PROC_LOGGER, -1, IMSG_LOG_OPEN, fd,
+           &id, sizeof(id));
+
+       DPRINTF("%s: opened log file %s, fd %d", __func__, path, fd);
+
+       return (0);
+}
+
+int
+logger_open(struct server *srv, struct server_config *srv_conf, void *arg)
+{
+       struct log_file *log, *logfile = NULL, *errfile = NULL;
+
+       /* disassociate */
+       srv_conf->logaccess = srv_conf->logerror = NULL;
+
+       TAILQ_FOREACH(log, &log_files, log_entry) {
+               if (strcmp(log->log_name, srv_conf->accesslog) == 0)
+                       logfile = log;
+               if (strcmp(log->log_name, srv_conf->errorlog) == 0)
+                       errfile = log;
+       }
+
+       if (logfile == NULL) {
+               if ((srv_conf->logaccess =
+                   logger_open_file(srv_conf->accesslog)) == NULL)
+                       return (-1);
+       } else
+               srv_conf->logaccess = logfile;
+
+       if (errfile == NULL) {
+               if ((srv_conf->logerror =
+                   logger_open_file(srv_conf->errorlog)) == NULL)
+                       return (-1);
+       } else
+               srv_conf->logerror = errfile;
+
+       return (0);
+}
+
+int
+logger_start(void)
+{
+       logger_close();
+       if (server_foreach(logger_open, NULL) == -1)
+               fatalx("failed to open log files");
        return (0);
 }
 
 int
 logger_log(struct imsg *imsg)
 {
-       char    *logline;
-       int      fd;
+       char                    *logline;
+       u_int32_t                id;
+       struct server_config    *srv_conf;
+       struct log_file         *log;
+
+       IMSG_SIZE_CHECK(imsg, &id);
+       memcpy(&id, imsg->data, sizeof(id));
+
+       if ((srv_conf = serverconfig_byid(id)) == NULL)
+               fatalx("invalid logging requestr");
 
        if (imsg->hdr.type == IMSG_LOG_ACCESS)
-               fd = log_fd;
+               log = srv_conf->logaccess;
        else
-               fd = error_fd;
+               log = srv_conf->logerror;
+
+       if (log == NULL || log->log_fd == -1) {
+               log_warnx("log file %s not opened", log->log_name);
+               return (0);
+       }
 
        /* XXX get_string() would sanitize the string, but add a malloc */
-       logline = imsg->data;
+       logline = (char *)imsg->data + sizeof(id);
 
        /* For debug output */
        log_debug("%s", logline);
 
-       if (dprintf(fd, "%s\n", logline) == -1) {
+       if (dprintf(log->log_fd, "%s\n", logline) == -1) {
                if (logger_start() == -1)
                        return (-1);
        }
@@ -136,15 +272,21 @@ int
 logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
        switch (imsg->hdr.type) {
+       case IMSG_CFG_SERVER:
+               config_getserver(env, imsg);
+               break;
        case IMSG_CFG_DONE:
                config_getcfg(env, imsg);
                break;
        case IMSG_CTL_START:
        case IMSG_CTL_REOPEN:
-               return (logger_start());
+               logger_start();
+               break;
        case IMSG_CTL_RESET:
                config_getreset(env, imsg);
                break;
+       case IMSG_LOG_OPEN:
+               return (logger_open_fd(imsg));
        default:
                return (-1);
        }
@@ -157,9 +299,9 @@ logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
        switch (imsg->hdr.type) {
        case IMSG_LOG_ACCESS:
-               return (logger_log(imsg));
        case IMSG_LOG_ERROR:
-               return (logger_log(imsg));
+               logger_log(imsg);
+               break;
        default:
                return (-1);
        }
index 8562f81..6f8b093 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.24 2014/08/05 09:24:21 jsg Exp $  */
+/*     $OpenBSD: parse.y,v 1.25 2014/08/05 15:36:59 reyk Exp $ */
 
 /*
  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -124,9 +124,9 @@ typedef struct {
 
 %}
 
-%token AUTO CHROOT COMMON COMBINED CONNECTION DIRECTORY FCGI FILE INDEX LISTEN
-%token LOCATION LOG NO ON PORT PREFORK ROOT SERVER SOCKET SSL STYLE SYSLOG
-%token TYPES
+%token ACCESS AUTO CHROOT COMMON COMBINED CONNECTION DIRECTORY ERR FCGI
+%token INDEX LISTEN LOCATION LOG NO ON PORT PREFORK ROOT SERVER SOCKET SSL
+%token STYLE SYSLOG TYPES
 %token ERROR INCLUDE
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
@@ -221,6 +221,10 @@ server             : SERVER STRING         {
                            sizeof(s->srv_conf.root));
                        strlcpy(s->srv_conf.index, HTTPD_INDEX,
                            sizeof(s->srv_conf.index));
+                       strlcpy(s->srv_conf.accesslog, HTTPD_ACCESS_LOG,
+                           sizeof(s->srv_conf.accesslog));
+                       strlcpy(s->srv_conf.errorlog, HTTPD_ERROR_LOG,
+                           sizeof(s->srv_conf.errorlog));
                        s->srv_conf.id = ++last_server_id;
                        s->srv_conf.timeout.tv_sec = SERVER_TIMEOUT;
                        s->srv_conf.flags |= SRVFLAG_LOG;
@@ -468,6 +472,28 @@ logflags   : STYLE logstyle
                        srv->srv_conf.flags &= ~SRVFLAG_SYSLOG;
                        srv->srv_conf.flags |= SRVFLAG_NO_SYSLOG;
                }
+               | ACCESS STRING         {
+                       if (strlcpy(srv->srv_conf.accesslog, $2,
+                           sizeof(srv->srv_conf.accesslog)) >=
+                           sizeof(srv->srv_conf.accesslog)) {
+                               yyerror("access log name too long");
+                               free($2);
+                               YYERROR;
+                       }
+                       free($2);
+                       srv->srv_conf.flags |= SRVFLAG_ACCESS_LOG;
+               }
+               | ERR STRING            {
+                       if (strlcpy(srv->srv_conf.errorlog, $2,
+                           sizeof(srv->srv_conf.errorlog)) >=
+                           sizeof(srv->srv_conf.errorlog)) {
+                               yyerror("error log name too long");
+                               free($2);
+                               YYERROR;
+                       }
+                       free($2);
+                       srv->srv_conf.flags |= SRVFLAG_ERROR_LOG;
+               }
                ;
 
 logstyle       : COMMON                {
@@ -614,14 +640,15 @@ lookup(char *s)
 {
        /* this has to be sorted always */
        static const struct keywords keywords[] = {
+               { "access",             ACCESS },
                { "auto",               AUTO },
                { "chroot",             CHROOT },
                { "combined",           COMBINED },
                { "common",             COMMON },
                { "connection",         CONNECTION },
                { "directory",          DIRECTORY },
+               { "error",              ERR },
                { "fastcgi",            FCGI },
-               { "file",               FILE },
                { "include",            INCLUDE },
                { "index",              INDEX },
                { "listen",             LISTEN },
index c8b134b..7f675e1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server.c,v 1.28 2014/08/04 18:12:15 reyk Exp $        */
+/*     $OpenBSD: server.c,v 1.29 2014/08/05 15:36:59 reyk Exp $        */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -22,6 +22,7 @@
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <sys/uio.h>
 #include <sys/tree.h>
 #include <sys/hash.h>
 
@@ -36,6 +37,7 @@
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
+#include <syslog.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <err.h>
@@ -246,6 +248,43 @@ server_byaddr(struct sockaddr *addr, in_port_t port)
        return (NULL);
 }
 
+struct server_config *
+serverconfig_byid(u_int32_t id)
+{
+       struct server           *srv;
+       struct server_config    *srv_conf;
+
+       TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+               if (srv->srv_conf.id == id)
+                       return (&srv->srv_conf);
+               TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
+                       if (srv_conf->id == id)
+                               return (srv_conf);
+               }
+       }
+
+       return (NULL);
+}
+
+int
+server_foreach(int (*srv_cb)(struct server *,
+    struct server_config *, void *), void *arg)
+{
+       struct server           *srv;
+       struct server_config    *srv_conf;
+
+       TAILQ_FOREACH(srv, env->sc_servers, srv_entry) {
+               if ((srv_cb)(srv, &srv->srv_conf, arg) == -1)
+                       return (-1);
+               TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) {
+                       if ((srv_cb)(srv, srv_conf, arg) == -1)
+                               return (-1);
+               }
+       }
+
+       return (0);
+}
+
 int
 server_socket_af(struct sockaddr_storage *ss, in_port_t port)
 {
@@ -819,31 +858,21 @@ server_inflight_dec(struct client *clt, const char *why)
 }
 
 void
-server_log_access(const char *emsg, ...)
+server_sendlog(struct server_config *srv_conf, int cmd, const char *emsg, ...)
 {
-       va_list  ap;
-       char    *msg;
-       int      ret;
+       va_list          ap;
+       char            *msg;
+       int              ret;
+       struct iovec     iov[2];
 
-       va_start(ap, emsg);
-       ret = vasprintf(&msg, emsg, ap);
-       va_end(ap);
-       if (ret == -1) {
-               log_warn("%s: vasprintf", __func__);
+       if (srv_conf->flags & SRVFLAG_SYSLOG) {
+               if (cmd == IMSG_LOG_ACCESS)
+                       vlog(LOG_INFO, emsg, ap);
+               else
+                       vlog(LOG_DEBUG, emsg, ap);
                return;
        }
 
-       proc_compose_imsg(env->sc_ps, PROC_LOGGER, -1,
-           IMSG_LOG_ACCESS, -1, msg, strlen(msg) + 1);
-}
-
-void
-server_log_error(const char *emsg, ...)
-{
-       va_list  ap;
-       char    *msg;
-       int      ret;
-
        va_start(ap, emsg);
        ret = vasprintf(&msg, emsg, ap);
        va_end(ap);
@@ -852,8 +881,12 @@ server_log_error(const char *emsg, ...)
                return;
        }
 
-       proc_compose_imsg(env->sc_ps, PROC_LOGGER, -1,
-           IMSG_LOG_ERROR, -1, msg, strlen(msg) + 1);
+       iov[0].iov_base = &srv_conf->id;
+       iov[0].iov_len = sizeof(srv_conf->id);
+       iov[1].iov_base = msg;
+       iov[1].iov_len = strlen(msg) + 1;
+
+       proc_composev_imsg(env->sc_ps, PROC_LOGGER, -1, cmd, -1, iov, 2);
 }
 
 void
@@ -862,36 +895,28 @@ server_log(struct client *clt, const char *msg)
        char                     ibuf[MAXHOSTNAMELEN], obuf[MAXHOSTNAMELEN];
        struct server_config    *srv_conf = clt->clt_srv_conf;
        char                    *ptr = NULL;
-       void                    (*log_infocb)(const char *, ...);
-       void                    (*log_debugcb)(const char *, ...);
+       int                      debug_cmd = -1;
        extern int               verbose;
 
-       if (srv_conf->flags & SRVFLAG_SYSLOG) {
-               log_infocb = log_info;
-               log_debugcb = log_debug;
-       } else {
-               log_infocb = server_log_access;
-               log_debugcb = server_log_error;
-       }
-
        switch (srv_conf->logformat) {
        case LOG_FORMAT_CONNECTION:
-               log_debugcb = server_log_error;
+               debug_cmd = IMSG_LOG_ACCESS;
                break;
        default:
-               if (verbose <= 1)
-                       log_debugcb = NULL;
+               if (verbose > 1)
+                       debug_cmd = IMSG_LOG_ERROR;
                if (EVBUFFER_LENGTH(clt->clt_log)) {
                        while ((ptr =
                            evbuffer_readline(clt->clt_log)) != NULL) {
-                               (log_infocb)("%s", ptr);
+                               server_sendlog(srv_conf,
+                                   IMSG_LOG_ACCESS, "%s", ptr);
                                free(ptr);
                        }
                }
                break;
        }
 
-       if (log_debugcb != NULL && msg != NULL) {
+       if (debug_cmd != -1 && msg != NULL) {
                memset(&ibuf, 0, sizeof(ibuf));
                memset(&obuf, 0, sizeof(obuf));
                (void)print_host(&clt->clt_ss, ibuf, sizeof(ibuf));
@@ -899,7 +924,7 @@ server_log(struct client *clt, const char *msg)
                if (EVBUFFER_LENGTH(clt->clt_log) &&
                    evbuffer_add_printf(clt->clt_log, "\n") != -1)
                        ptr = evbuffer_readline(clt->clt_log);
-               (log_debugcb)("server %s, "
+               server_sendlog(srv_conf, debug_cmd, "server %s, "
                    "client %d (%d active), %s:%u -> %s, "
                    "%s%s%s", srv_conf->name, clt->clt_id, server_clients,
                    ibuf, ntohs(clt->clt_port), obuf, msg,