xref: /openbsd-src/usr.sbin/httpd/logger.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
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