aboutsummaryrefslogtreecommitdiff
path: root/httpd/logger.c
diff options
context:
space:
mode:
Diffstat (limited to 'httpd/logger.c')
-rw-r--r--httpd/logger.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/httpd/logger.c b/httpd/logger.c
new file mode 100644
index 0000000..b4e4404
--- /dev/null
+++ b/httpd/logger.c
@@ -0,0 +1,312 @@
+/* $OpenBSD: logger.c,v 1.11 2015/02/08 00:00:59 reyk Exp $ */
+
+/*
+ * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h> /* nitems */
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <imsg.h>
+
+#include "httpd.h"
+
+int logger_dispatch_parent(int, struct privsep_proc *,
+ struct imsg *);
+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;
+static u_int32_t last_log_id = 0;
+
+static struct privsep_proc procs[] = {
+ { "parent", PROC_PARENT, logger_dispatch_parent },
+ { "server", PROC_SERVER, logger_dispatch_server }
+};
+
+pid_t
+logger(struct privsep *ps, struct privsep_proc *p)
+{
+ env = ps->ps_env;
+ return (proc_run(ps, p, procs, nitems(procs), logger_init, NULL));
+}
+
+void
+logger_shutdown(void)
+{
+ logger_close();
+ config_purge(env, CONFIG_ALL);
+}
+
+void
+logger_init(struct privsep *ps, struct privsep_proc *p, void *arg)
+{
+ if (config_init(ps->ps_env) == -1)
+ fatal("failed to initialize configuration");
+
+ /* Set to current prefork id */
+ proc_id = p->p_instance;
+
+ /* 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_open_fd(struct imsg *imsg)
+{
+ 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[PATH_MAX];
+ 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 = strlcpy(path, env->sc_logdir, sizeof(path)))
+ >= 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 ((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;
+
+ if (srv_conf->flags & SRVFLAG_SYSLOG)
+ return (0);
+
+ /* 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;
+ 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)
+ log = srv_conf->logaccess;
+ else
+ log = srv_conf->logerror;
+
+ if (log == NULL || log->log_fd == -1) {
+ log_warnx("log file %s not opened", log ? log->log_name : "");
+ return (0);
+ }
+
+ /* XXX get_string() would sanitize the string, but add a malloc */
+ logline = (char *)imsg->data + sizeof(id);
+
+ /* For debug output */
+ log_debug("%s", logline);
+
+ if (dprintf(log->log_fd, "%s\n", logline) == -1) {
+ if (logger_start() == -1)
+ return (-1);
+ }
+
+ return (0);
+}
+
+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:
+ logger_start();
+ break;
+ case IMSG_CTL_RESET:
+ config_getreset(env, imsg);
+ break;
+ case IMSG_LOG_OPEN:
+ return (logger_open_fd(imsg));
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
+{
+ switch (imsg->hdr.type) {
+ case IMSG_LOG_ACCESS:
+ case IMSG_LOG_ERROR:
+ logger_log(imsg);
+ break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}