diff options
Diffstat (limited to 'logger.c')
-rw-r--r-- | logger.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/logger.c b/logger.c new file mode 100644 index 0000000..665db75 --- /dev/null +++ b/logger.c @@ -0,0 +1,310 @@ +/* $OpenBSD: logger.c,v 1.5 2014/08/06 12:56:58 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/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/uio.h> + +#include <net/if.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <event.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[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 ((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; + 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); +} |