1 /* $OpenBSD: logger.c,v 1.13 2015/08/20 13:00:23 reyk Exp $ */ 2 3 /* 4 * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> /* nitems */ 20 #include <sys/types.h> 21 #include <sys/queue.h> 22 #include <sys/uio.h> 23 24 #include <limits.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 #include <fcntl.h> 30 #include <imsg.h> 31 32 #include "httpd.h" 33 34 int logger_dispatch_parent(int, struct privsep_proc *, 35 struct imsg *); 36 int logger_dispatch_server(int, struct privsep_proc *, 37 struct imsg *); 38 void logger_shutdown(void); 39 void logger_close(void); 40 struct log_file *logger_open_file(const char *); 41 int logger_open_fd(struct imsg *); 42 int logger_open(struct server *, struct server_config *, void *); 43 void logger_init(struct privsep *, struct privsep_proc *p, void *); 44 int logger_start(void); 45 int logger_log(struct imsg *); 46 47 static struct httpd *env = NULL; 48 int proc_id; 49 static uint32_t last_log_id = 0; 50 51 static struct privsep_proc procs[] = { 52 { "parent", PROC_PARENT, logger_dispatch_parent }, 53 { "server", PROC_SERVER, logger_dispatch_server } 54 }; 55 56 pid_t 57 logger(struct privsep *ps, struct privsep_proc *p) 58 { 59 env = ps->ps_env; 60 return (proc_run(ps, p, procs, nitems(procs), logger_init, NULL)); 61 } 62 63 void 64 logger_shutdown(void) 65 { 66 logger_close(); 67 config_purge(env, CONFIG_ALL); 68 } 69 70 void 71 logger_init(struct privsep *ps, struct privsep_proc *p, void *arg) 72 { 73 if (config_init(ps->ps_env) == -1) 74 fatal("failed to initialize configuration"); 75 76 /* Set to current prefork id */ 77 proc_id = p->p_instance; 78 79 /* We use a custom shutdown callback */ 80 p->p_shutdown = logger_shutdown; 81 82 TAILQ_INIT(&log_files); 83 } 84 85 void 86 logger_close(void) 87 { 88 struct log_file *log, *next; 89 90 TAILQ_FOREACH_SAFE(log, &log_files, log_entry, next) { 91 if (log->log_fd != -1) { 92 close(log->log_fd); 93 log->log_fd = -1; 94 } 95 TAILQ_REMOVE(&log_files, log, log_entry); 96 } 97 } 98 99 struct log_file * 100 logger_open_file(const char *name) 101 { 102 struct log_file *log; 103 struct iovec iov[2]; 104 105 if ((log = calloc(1, sizeof(*log))) == NULL) { 106 log_warn("failed to allocate log %s", name); 107 return (NULL); 108 } 109 110 log->log_id = ++last_log_id; 111 (void)strlcpy(log->log_name, name, sizeof(log->log_name)); 112 113 /* The file will be opened by the parent process */ 114 log->log_fd = -1; 115 116 iov[0].iov_base = &log->log_id; 117 iov[0].iov_len = sizeof(log->log_id); 118 iov[1].iov_base = log->log_name; 119 iov[1].iov_len = strlen(log->log_name) + 1; 120 121 if (proc_composev_imsg(env->sc_ps, PROC_PARENT, -1, IMSG_LOG_OPEN, -1, 122 iov, 2) != 0) { 123 log_warn("%s: failed to compose IMSG_LOG_OPEN imsg", __func__); 124 goto err; 125 } 126 127 TAILQ_INSERT_TAIL(&log_files, log, log_entry); 128 129 return (log); 130 131 err: 132 free(log); 133 134 return (NULL); 135 } 136 137 int 138 logger_open_fd(struct imsg *imsg) 139 { 140 struct log_file *log; 141 uint32_t id; 142 143 IMSG_SIZE_CHECK(imsg, &id); 144 memcpy(&id, imsg->data, sizeof(id)); 145 146 TAILQ_FOREACH(log, &log_files, log_entry) { 147 if (log->log_id == id) { 148 DPRINTF("%s: received log fd %d, file %s", 149 __func__, imsg->fd, log->log_name); 150 log->log_fd = imsg->fd; 151 return (0); 152 } 153 } 154 155 return (-1); 156 } 157 158 int 159 logger_open_priv(struct imsg *imsg) 160 { 161 char path[PATH_MAX]; 162 char name[NAME_MAX], *p; 163 uint32_t id; 164 size_t len; 165 int fd; 166 167 /* called from the priviled process */ 168 IMSG_SIZE_CHECK(imsg, &id); 169 memcpy(&id, imsg->data, sizeof(id)); 170 p = (char *)imsg->data + sizeof(id); 171 172 if ((size_t)snprintf(name, sizeof(name), "/%s", p) >= sizeof(name)) 173 return (-1); 174 if ((len = strlcpy(path, env->sc_logdir, sizeof(path))) 175 >= sizeof(path)) 176 return (-1); 177 178 p = path + len; 179 len = sizeof(path) - len; 180 181 if (canonicalize_path(name, p, len) == NULL) { 182 log_warnx("invalid log name"); 183 return (-1); 184 } 185 186 if ((fd = open(path, O_WRONLY|O_APPEND|O_CREAT, 0644)) == -1) { 187 log_warn("failed to open %s", path); 188 return (-1); 189 } 190 191 proc_compose_imsg(env->sc_ps, PROC_LOGGER, -1, IMSG_LOG_OPEN, fd, 192 &id, sizeof(id)); 193 194 DPRINTF("%s: opened log file %s, fd %d", __func__, path, fd); 195 196 return (0); 197 } 198 199 int 200 logger_open(struct server *srv, struct server_config *srv_conf, void *arg) 201 { 202 struct log_file *log, *logfile = NULL, *errfile = NULL; 203 204 if (srv_conf->flags & SRVFLAG_SYSLOG) 205 return (0); 206 207 /* disassociate */ 208 srv_conf->logaccess = srv_conf->logerror = NULL; 209 210 TAILQ_FOREACH(log, &log_files, log_entry) { 211 if (strcmp(log->log_name, srv_conf->accesslog) == 0) 212 logfile = log; 213 if (strcmp(log->log_name, srv_conf->errorlog) == 0) 214 errfile = log; 215 } 216 217 if (logfile == NULL) { 218 if ((srv_conf->logaccess = 219 logger_open_file(srv_conf->accesslog)) == NULL) 220 return (-1); 221 } else 222 srv_conf->logaccess = logfile; 223 224 if (errfile == NULL) { 225 if ((srv_conf->logerror = 226 logger_open_file(srv_conf->errorlog)) == NULL) 227 return (-1); 228 } else 229 srv_conf->logerror = errfile; 230 231 return (0); 232 } 233 234 int 235 logger_start(void) 236 { 237 logger_close(); 238 if (server_foreach(logger_open, NULL) == -1) 239 fatalx("failed to open log files"); 240 return (0); 241 } 242 243 int 244 logger_log(struct imsg *imsg) 245 { 246 char *logline; 247 uint32_t id; 248 struct server_config *srv_conf; 249 struct log_file *log; 250 251 IMSG_SIZE_CHECK(imsg, &id); 252 memcpy(&id, imsg->data, sizeof(id)); 253 254 if ((srv_conf = serverconfig_byid(id)) == NULL) 255 fatalx("invalid logging requestr"); 256 257 if (imsg->hdr.type == IMSG_LOG_ACCESS) 258 log = srv_conf->logaccess; 259 else 260 log = srv_conf->logerror; 261 262 if (log == NULL || log->log_fd == -1) { 263 log_warnx("log file %s not opened", log ? log->log_name : ""); 264 return (0); 265 } 266 267 /* XXX get_string() would sanitize the string, but add a malloc */ 268 logline = (char *)imsg->data + sizeof(id); 269 270 /* For debug output */ 271 log_debug("%s", logline); 272 273 if (dprintf(log->log_fd, "%s\n", logline) == -1) { 274 if (logger_start() == -1) 275 return (-1); 276 } 277 278 return (0); 279 } 280 281 int 282 logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 283 { 284 switch (imsg->hdr.type) { 285 case IMSG_CFG_SERVER: 286 config_getserver(env, imsg); 287 break; 288 case IMSG_CFG_DONE: 289 config_getcfg(env, imsg); 290 break; 291 case IMSG_CTL_START: 292 case IMSG_CTL_REOPEN: 293 logger_start(); 294 break; 295 case IMSG_CTL_RESET: 296 config_getreset(env, imsg); 297 break; 298 case IMSG_LOG_OPEN: 299 return (logger_open_fd(imsg)); 300 default: 301 return (-1); 302 } 303 304 return (0); 305 } 306 307 int 308 logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg) 309 { 310 switch (imsg->hdr.type) { 311 case IMSG_LOG_ACCESS: 312 case IMSG_LOG_ERROR: 313 logger_log(imsg); 314 break; 315 default: 316 return (-1); 317 } 318 319 return (0); 320 } 321