1 /* $OpenBSD: logger.c,v 1.15 2015/12/02 15:13:00 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 (pledge("stdio recvfd", NULL) == -1) 74 fatal("pledge"); 75 76 if (config_init(ps->ps_env) == -1) 77 fatal("failed to initialize configuration"); 78 79 /* Set to current prefork id */ 80 proc_id = p->p_instance; 81 82 /* We use a custom shutdown callback */ 83 p->p_shutdown = logger_shutdown; 84 85 TAILQ_INIT(&log_files); 86 } 87 88 void 89 logger_close(void) 90 { 91 struct log_file *log, *next; 92 93 TAILQ_FOREACH_SAFE(log, &log_files, log_entry, next) { 94 if (log->log_fd != -1) { 95 close(log->log_fd); 96 log->log_fd = -1; 97 } 98 TAILQ_REMOVE(&log_files, log, log_entry); 99 } 100 } 101 102 struct log_file * 103 logger_open_file(const char *name) 104 { 105 struct log_file *log; 106 struct iovec iov[2]; 107 108 if ((log = calloc(1, sizeof(*log))) == NULL) { 109 log_warn("failed to allocate log %s", name); 110 return (NULL); 111 } 112 113 log->log_id = ++last_log_id; 114 (void)strlcpy(log->log_name, name, sizeof(log->log_name)); 115 116 /* The file will be opened by the parent process */ 117 log->log_fd = -1; 118 119 iov[0].iov_base = &log->log_id; 120 iov[0].iov_len = sizeof(log->log_id); 121 iov[1].iov_base = log->log_name; 122 iov[1].iov_len = strlen(log->log_name) + 1; 123 124 if (proc_composev(env->sc_ps, PROC_PARENT, IMSG_LOG_OPEN, 125 iov, 2) != 0) { 126 log_warn("%s: failed to compose IMSG_LOG_OPEN imsg", __func__); 127 goto err; 128 } 129 130 TAILQ_INSERT_TAIL(&log_files, log, log_entry); 131 132 return (log); 133 134 err: 135 free(log); 136 137 return (NULL); 138 } 139 140 int 141 logger_open_fd(struct imsg *imsg) 142 { 143 struct log_file *log; 144 uint32_t id; 145 146 IMSG_SIZE_CHECK(imsg, &id); 147 memcpy(&id, imsg->data, sizeof(id)); 148 149 TAILQ_FOREACH(log, &log_files, log_entry) { 150 if (log->log_id == id) { 151 DPRINTF("%s: received log fd %d, file %s", 152 __func__, imsg->fd, log->log_name); 153 log->log_fd = imsg->fd; 154 return (0); 155 } 156 } 157 158 return (-1); 159 } 160 161 int 162 logger_open_priv(struct imsg *imsg) 163 { 164 char path[PATH_MAX]; 165 char name[NAME_MAX], *p; 166 uint32_t id; 167 size_t len; 168 int fd; 169 170 /* called from the priviled process */ 171 IMSG_SIZE_CHECK(imsg, &id); 172 memcpy(&id, imsg->data, sizeof(id)); 173 p = (char *)imsg->data + sizeof(id); 174 175 if ((size_t)snprintf(name, sizeof(name), "/%s", p) >= sizeof(name)) 176 return (-1); 177 if ((len = strlcpy(path, env->sc_logdir, sizeof(path))) 178 >= sizeof(path)) 179 return (-1); 180 181 p = path + len; 182 len = sizeof(path) - len; 183 184 if (canonicalize_path(name, p, len) == NULL) { 185 log_warnx("invalid log name"); 186 return (-1); 187 } 188 189 if ((fd = open(path, O_WRONLY|O_APPEND|O_CREAT, 0644)) == -1) { 190 log_warn("failed to open %s", path); 191 return (-1); 192 } 193 194 proc_compose_imsg(env->sc_ps, PROC_LOGGER, -1, IMSG_LOG_OPEN, -1, fd, 195 &id, sizeof(id)); 196 197 DPRINTF("%s: opened log file %s, fd %d", __func__, path, fd); 198 199 return (0); 200 } 201 202 int 203 logger_open(struct server *srv, struct server_config *srv_conf, void *arg) 204 { 205 struct log_file *log, *logfile = NULL, *errfile = NULL; 206 207 if (srv_conf->flags & SRVFLAG_SYSLOG) 208 return (0); 209 210 /* disassociate */ 211 srv_conf->logaccess = srv_conf->logerror = NULL; 212 213 TAILQ_FOREACH(log, &log_files, log_entry) { 214 if (strcmp(log->log_name, srv_conf->accesslog) == 0) 215 logfile = log; 216 if (strcmp(log->log_name, srv_conf->errorlog) == 0) 217 errfile = log; 218 } 219 220 if (logfile == NULL) { 221 if ((srv_conf->logaccess = 222 logger_open_file(srv_conf->accesslog)) == NULL) 223 return (-1); 224 } else 225 srv_conf->logaccess = logfile; 226 227 if (errfile == NULL) { 228 if ((srv_conf->logerror = 229 logger_open_file(srv_conf->errorlog)) == NULL) 230 return (-1); 231 } else 232 srv_conf->logerror = errfile; 233 234 return (0); 235 } 236 237 int 238 logger_start(void) 239 { 240 logger_close(); 241 if (server_foreach(logger_open, NULL) == -1) 242 fatalx("failed to open log files"); 243 return (0); 244 } 245 246 int 247 logger_log(struct imsg *imsg) 248 { 249 char *logline; 250 uint32_t id; 251 struct server_config *srv_conf; 252 struct log_file *log; 253 254 IMSG_SIZE_CHECK(imsg, &id); 255 memcpy(&id, imsg->data, sizeof(id)); 256 257 if ((srv_conf = serverconfig_byid(id)) == NULL) 258 fatalx("invalid logging requestr"); 259 260 if (imsg->hdr.type == IMSG_LOG_ACCESS) 261 log = srv_conf->logaccess; 262 else 263 log = srv_conf->logerror; 264 265 if (log == NULL || log->log_fd == -1) { 266 log_warnx("log file %s not opened", log ? log->log_name : ""); 267 return (0); 268 } 269 270 /* XXX get_string() would sanitize the string, but add a malloc */ 271 logline = (char *)imsg->data + sizeof(id); 272 273 /* For debug output */ 274 log_debug("%s", logline); 275 276 if (dprintf(log->log_fd, "%s\n", logline) == -1) { 277 if (logger_start() == -1) 278 return (-1); 279 } 280 281 return (0); 282 } 283 284 int 285 logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 286 { 287 switch (imsg->hdr.type) { 288 case IMSG_CFG_SERVER: 289 config_getserver(env, imsg); 290 break; 291 case IMSG_CFG_DONE: 292 config_getcfg(env, imsg); 293 break; 294 case IMSG_CTL_START: 295 case IMSG_CTL_REOPEN: 296 logger_start(); 297 break; 298 case IMSG_CTL_RESET: 299 config_getreset(env, imsg); 300 break; 301 case IMSG_LOG_OPEN: 302 return (logger_open_fd(imsg)); 303 default: 304 return (-1); 305 } 306 307 return (0); 308 } 309 310 int 311 logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg) 312 { 313 switch (imsg->hdr.type) { 314 case IMSG_LOG_ACCESS: 315 case IMSG_LOG_ERROR: 316 logger_log(imsg); 317 break; 318 default: 319 return (-1); 320 } 321 322 return (0); 323 } 324