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