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