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