xref: /openbsd-src/usr.sbin/httpd/logger.c (revision 3cc21533749d6d8c9c57a10ab6a6ad2723eb3a0d)
1*3cc21533Sclaudio /*	$OpenBSD: logger.c,v 1.25 2024/01/17 08:22:40 claudio Exp $	*/
2844c3615Sreyk 
3844c3615Sreyk /*
4844c3615Sreyk  * Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
5844c3615Sreyk  *
6844c3615Sreyk  * Permission to use, copy, modify, and distribute this software for any
7844c3615Sreyk  * purpose with or without fee is hereby granted, provided that the above
8844c3615Sreyk  * copyright notice and this permission notice appear in all copies.
9844c3615Sreyk  *
10844c3615Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11844c3615Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12844c3615Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13844c3615Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14844c3615Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15844c3615Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16844c3615Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17844c3615Sreyk  */
18844c3615Sreyk 
19844c3615Sreyk #include <sys/types.h>
20cf605f40Sreyk #include <sys/queue.h>
21cf605f40Sreyk #include <sys/uio.h>
22844c3615Sreyk 
2386f952e4Sreyk #include <limits.h>
24844c3615Sreyk #include <stdio.h>
25844c3615Sreyk #include <stdlib.h>
26844c3615Sreyk #include <string.h>
27844c3615Sreyk #include <unistd.h>
28844c3615Sreyk #include <fcntl.h>
2986f952e4Sreyk #include <imsg.h>
30844c3615Sreyk 
31844c3615Sreyk #include "httpd.h"
32844c3615Sreyk 
33844c3615Sreyk int		 logger_dispatch_parent(int, struct privsep_proc *,
34844c3615Sreyk 		    struct imsg *);
35844c3615Sreyk int		 logger_dispatch_server(int, struct privsep_proc *,
36844c3615Sreyk 		    struct imsg *);
37844c3615Sreyk void		 logger_shutdown(void);
38844c3615Sreyk void		 logger_close(void);
39cf605f40Sreyk struct log_file *logger_open_file(const char *);
40cf605f40Sreyk int		 logger_open_fd(struct imsg *);
41cf605f40Sreyk int		 logger_open(struct server *, struct server_config *, void *);
42844c3615Sreyk void		 logger_init(struct privsep *, struct privsep_proc *p, void *);
43844c3615Sreyk int		 logger_start(void);
44844c3615Sreyk int		 logger_log(struct imsg *);
45844c3615Sreyk 
464703e0faSreyk static uint32_t		 last_log_id = 0;
47844c3615Sreyk 
48488a4384Sderaadt struct log_files log_files;
49488a4384Sderaadt 
50844c3615Sreyk static struct privsep_proc procs[] = {
51844c3615Sreyk 	{ "parent",	PROC_PARENT,	logger_dispatch_parent },
52844c3615Sreyk 	{ "server",	PROC_SERVER,	logger_dispatch_server }
53844c3615Sreyk };
54844c3615Sreyk 
555811a22aSrzalamena void
logger(struct privsep * ps,struct privsep_proc * p)56844c3615Sreyk logger(struct privsep *ps, struct privsep_proc *p)
57844c3615Sreyk {
585811a22aSrzalamena 	proc_run(ps, p, procs, nitems(procs), logger_init, NULL);
59844c3615Sreyk }
60844c3615Sreyk 
61844c3615Sreyk void
logger_shutdown(void)62844c3615Sreyk logger_shutdown(void)
63844c3615Sreyk {
64844c3615Sreyk 	logger_close();
65781985a7Srzalamena 	config_purge(httpd_env, CONFIG_ALL);
66844c3615Sreyk }
67844c3615Sreyk 
68844c3615Sreyk void
logger_init(struct privsep * ps,struct privsep_proc * p,void * arg)69844c3615Sreyk logger_init(struct privsep *ps, struct privsep_proc *p, void *arg)
70844c3615Sreyk {
711019da98Sflorian 	if (pledge("stdio recvfd", NULL) == -1)
721019da98Sflorian 		fatal("pledge");
731019da98Sflorian 
74844c3615Sreyk 	if (config_init(ps->ps_env) == -1)
75844c3615Sreyk 		fatal("failed to initialize configuration");
76844c3615Sreyk 
77844c3615Sreyk 	/* We use a custom shutdown callback */
78844c3615Sreyk 	p->p_shutdown = logger_shutdown;
79cf605f40Sreyk 
80cf605f40Sreyk 	TAILQ_INIT(&log_files);
81cf605f40Sreyk }
82cf605f40Sreyk 
83cf605f40Sreyk void
logger_close(void)84cf605f40Sreyk logger_close(void)
85cf605f40Sreyk {
86cf605f40Sreyk 	struct log_file	*log, *next;
87cf605f40Sreyk 
88cf605f40Sreyk 	TAILQ_FOREACH_SAFE(log, &log_files, log_entry, next) {
89cf605f40Sreyk 		if (log->log_fd != -1) {
90cf605f40Sreyk 			close(log->log_fd);
91cf605f40Sreyk 			log->log_fd = -1;
92cf605f40Sreyk 		}
93cf605f40Sreyk 		TAILQ_REMOVE(&log_files, log, log_entry);
94944d8663Stb 		free(log);
95cf605f40Sreyk 	}
96cf605f40Sreyk }
97cf605f40Sreyk 
98cf605f40Sreyk struct log_file *
logger_open_file(const char * name)99cf605f40Sreyk logger_open_file(const char *name)
100cf605f40Sreyk {
101cf605f40Sreyk 	struct log_file	*log;
102cf605f40Sreyk 	struct iovec	 iov[2];
103cf605f40Sreyk 
104cf605f40Sreyk 	if ((log = calloc(1, sizeof(*log))) == NULL) {
105cf605f40Sreyk 		log_warn("failed to allocate log %s", name);
106cf605f40Sreyk 		return (NULL);
107cf605f40Sreyk 	}
108cf605f40Sreyk 
109cf605f40Sreyk 	log->log_id = ++last_log_id;
110cf605f40Sreyk 	(void)strlcpy(log->log_name, name, sizeof(log->log_name));
111cf605f40Sreyk 
112cf605f40Sreyk 	/* The file will be opened by the parent process */
113cf605f40Sreyk 	log->log_fd = -1;
114cf605f40Sreyk 
115cf605f40Sreyk 	iov[0].iov_base = &log->log_id;
116cf605f40Sreyk 	iov[0].iov_len = sizeof(log->log_id);
117cf605f40Sreyk 	iov[1].iov_base = log->log_name;
118cf605f40Sreyk 	iov[1].iov_len = strlen(log->log_name) + 1;
119cf605f40Sreyk 
120781985a7Srzalamena 	if (proc_composev(httpd_env->sc_ps, PROC_PARENT, IMSG_LOG_OPEN,
12159ac4ec7Sjsing 	    iov, 2) != 0) {
12259ac4ec7Sjsing 		log_warn("%s: failed to compose IMSG_LOG_OPEN imsg", __func__);
12359ac4ec7Sjsing 		goto err;
12459ac4ec7Sjsing 	}
125cf605f40Sreyk 
126cf605f40Sreyk 	TAILQ_INSERT_TAIL(&log_files, log, log_entry);
127cf605f40Sreyk 
128cf605f40Sreyk 	return (log);
12959ac4ec7Sjsing 
13059ac4ec7Sjsing err:
13159ac4ec7Sjsing 	free(log);
13259ac4ec7Sjsing 
13359ac4ec7Sjsing 	return (NULL);
134cf605f40Sreyk }
135cf605f40Sreyk 
136cf605f40Sreyk int
logger_open_fd(struct imsg * imsg)137cf605f40Sreyk logger_open_fd(struct imsg *imsg)
138cf605f40Sreyk {
139cf605f40Sreyk 	struct log_file		*log;
1404703e0faSreyk 	uint32_t		 id;
141cf605f40Sreyk 
142cf605f40Sreyk 	IMSG_SIZE_CHECK(imsg, &id);
143cf605f40Sreyk 	memcpy(&id, imsg->data, sizeof(id));
144cf605f40Sreyk 
145cf605f40Sreyk 	TAILQ_FOREACH(log, &log_files, log_entry) {
146cf605f40Sreyk 		if (log->log_id == id) {
147*3cc21533Sclaudio 			log->log_fd = imsg_get_fd(imsg);
148cf605f40Sreyk 			DPRINTF("%s: received log fd %d, file %s",
149*3cc21533Sclaudio 			    __func__, log->log_fd, log->log_name);
150cf605f40Sreyk 			return (0);
151cf605f40Sreyk 		}
152cf605f40Sreyk 	}
153cf605f40Sreyk 
154cf605f40Sreyk 	return (-1);
155cf605f40Sreyk }
156cf605f40Sreyk 
157cf605f40Sreyk int
logger_open_priv(struct imsg * imsg)158cf605f40Sreyk logger_open_priv(struct imsg *imsg)
159cf605f40Sreyk {
160b9fc9a72Sderaadt 	char			 path[PATH_MAX];
1618f5bf38cSflorian 	char			 name[PATH_MAX], *p;
1624703e0faSreyk 	uint32_t		 id;
163cf605f40Sreyk 	size_t			 len;
164cf605f40Sreyk 	int			 fd;
165cf605f40Sreyk 
1668f5bf38cSflorian 	/* called from the privileged process */
167cf605f40Sreyk 	IMSG_SIZE_CHECK(imsg, &id);
168cf605f40Sreyk 	memcpy(&id, imsg->data, sizeof(id));
169cf605f40Sreyk 	p = (char *)imsg->data + sizeof(id);
170cf605f40Sreyk 
171cf605f40Sreyk 	if ((size_t)snprintf(name, sizeof(name), "/%s", p) >= sizeof(name))
172cf605f40Sreyk 		return (-1);
173781985a7Srzalamena 	if ((len = strlcpy(path, httpd_env->sc_logdir, sizeof(path)))
174032d2b93Sbeck 	    >= sizeof(path))
175cf605f40Sreyk 		return (-1);
176cf605f40Sreyk 
177cf605f40Sreyk 	p = path + len;
178cf605f40Sreyk 	len = sizeof(path) - len;
179cf605f40Sreyk 
180cf605f40Sreyk 	if (canonicalize_path(name, p, len) == NULL) {
181cf605f40Sreyk 		log_warnx("invalid log name");
182cf605f40Sreyk 		return (-1);
183cf605f40Sreyk 	}
184cf605f40Sreyk 
185cf605f40Sreyk 	if ((fd = open(path, O_WRONLY|O_APPEND|O_CREAT, 0644)) == -1) {
186cf605f40Sreyk 		log_warn("failed to open %s", path);
187cf605f40Sreyk 		return (-1);
188cf605f40Sreyk 	}
189cf605f40Sreyk 
1908ea811cdSreyk 	proc_compose_imsg(httpd_env->sc_ps, PROC_LOGGER, -1,
1918ea811cdSreyk 	    IMSG_LOG_OPEN, -1, fd, &id, sizeof(id));
192cf605f40Sreyk 
193cf605f40Sreyk 	DPRINTF("%s: opened log file %s, fd %d", __func__, path, fd);
194cf605f40Sreyk 
195cf605f40Sreyk 	return (0);
196cf605f40Sreyk }
197cf605f40Sreyk 
198cf605f40Sreyk int
logger_open(struct server * srv,struct server_config * srv_conf,void * arg)199cf605f40Sreyk logger_open(struct server *srv, struct server_config *srv_conf, void *arg)
200cf605f40Sreyk {
201cf605f40Sreyk 	struct log_file	*log, *logfile = NULL, *errfile = NULL;
202cf605f40Sreyk 
20358443515Skn 	if (srv_conf->flags & (SRVFLAG_SYSLOG | SRVFLAG_NO_LOG))
20457bc6ec9Sbeck 		return (0);
20557bc6ec9Sbeck 
206cf605f40Sreyk 	/* disassociate */
207cf605f40Sreyk 	srv_conf->logaccess = srv_conf->logerror = NULL;
208cf605f40Sreyk 
209cf605f40Sreyk 	TAILQ_FOREACH(log, &log_files, log_entry) {
210cf605f40Sreyk 		if (strcmp(log->log_name, srv_conf->accesslog) == 0)
211cf605f40Sreyk 			logfile = log;
212cf605f40Sreyk 		if (strcmp(log->log_name, srv_conf->errorlog) == 0)
213cf605f40Sreyk 			errfile = log;
214cf605f40Sreyk 	}
215cf605f40Sreyk 
216cf605f40Sreyk 	if (logfile == NULL) {
217cf605f40Sreyk 		if ((srv_conf->logaccess =
218cf605f40Sreyk 		    logger_open_file(srv_conf->accesslog)) == NULL)
219cf605f40Sreyk 			return (-1);
220cf605f40Sreyk 	} else
221cf605f40Sreyk 		srv_conf->logaccess = logfile;
222cf605f40Sreyk 
223cf605f40Sreyk 	if (errfile == NULL) {
224cf605f40Sreyk 		if ((srv_conf->logerror =
225cf605f40Sreyk 		    logger_open_file(srv_conf->errorlog)) == NULL)
226cf605f40Sreyk 			return (-1);
227cf605f40Sreyk 	} else
228cf605f40Sreyk 		srv_conf->logerror = errfile;
229cf605f40Sreyk 
230cf605f40Sreyk 	return (0);
231844c3615Sreyk }
232844c3615Sreyk 
233844c3615Sreyk int
logger_start(void)234844c3615Sreyk logger_start(void)
235844c3615Sreyk {
236844c3615Sreyk 	logger_close();
237cf605f40Sreyk 	if (server_foreach(logger_open, NULL) == -1)
238cf605f40Sreyk 		fatalx("failed to open log files");
239844c3615Sreyk 	return (0);
240844c3615Sreyk }
241844c3615Sreyk 
242844c3615Sreyk int
logger_log(struct imsg * imsg)243844c3615Sreyk logger_log(struct imsg *imsg)
244844c3615Sreyk {
245844c3615Sreyk 	char			*logline;
2464703e0faSreyk 	uint32_t		 id;
247cf605f40Sreyk 	struct server_config	*srv_conf;
248cf605f40Sreyk 	struct log_file		*log;
249cf605f40Sreyk 
250cf605f40Sreyk 	IMSG_SIZE_CHECK(imsg, &id);
251cf605f40Sreyk 	memcpy(&id, imsg->data, sizeof(id));
252cf605f40Sreyk 
253cf605f40Sreyk 	if ((srv_conf = serverconfig_byid(id)) == NULL)
254cf605f40Sreyk 		fatalx("invalid logging requestr");
255844c3615Sreyk 
256844c3615Sreyk 	if (imsg->hdr.type == IMSG_LOG_ACCESS)
257cf605f40Sreyk 		log = srv_conf->logaccess;
258844c3615Sreyk 	else
259cf605f40Sreyk 		log = srv_conf->logerror;
260cf605f40Sreyk 
261cf605f40Sreyk 	if (log == NULL || log->log_fd == -1) {
26230d381cfSjsg 		log_warnx("log file %s not opened", log ? log->log_name : "");
263cf605f40Sreyk 		return (0);
264cf605f40Sreyk 	}
265844c3615Sreyk 
266844c3615Sreyk 	/* XXX get_string() would sanitize the string, but add a malloc */
267cf605f40Sreyk 	logline = (char *)imsg->data + sizeof(id);
268844c3615Sreyk 
269844c3615Sreyk 	/* For debug output */
270844c3615Sreyk 	log_debug("%s", logline);
271844c3615Sreyk 
272cf605f40Sreyk 	if (dprintf(log->log_fd, "%s\n", logline) == -1) {
273844c3615Sreyk 		if (logger_start() == -1)
274844c3615Sreyk 			return (-1);
275844c3615Sreyk 	}
276844c3615Sreyk 
277844c3615Sreyk 	return (0);
278844c3615Sreyk }
279844c3615Sreyk 
280844c3615Sreyk int
logger_dispatch_parent(int fd,struct privsep_proc * p,struct imsg * imsg)281844c3615Sreyk logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
282844c3615Sreyk {
283844c3615Sreyk 	switch (imsg->hdr.type) {
284cf605f40Sreyk 	case IMSG_CFG_SERVER:
285781985a7Srzalamena 		config_getserver(httpd_env, imsg);
286cf605f40Sreyk 		break;
287844c3615Sreyk 	case IMSG_CFG_DONE:
288781985a7Srzalamena 		config_getcfg(httpd_env, imsg);
289844c3615Sreyk 		break;
290844c3615Sreyk 	case IMSG_CTL_START:
291844c3615Sreyk 	case IMSG_CTL_REOPEN:
292cf605f40Sreyk 		logger_start();
293cf605f40Sreyk 		break;
294844c3615Sreyk 	case IMSG_CTL_RESET:
295781985a7Srzalamena 		config_getreset(httpd_env, imsg);
296844c3615Sreyk 		break;
297cf605f40Sreyk 	case IMSG_LOG_OPEN:
298cf605f40Sreyk 		return (logger_open_fd(imsg));
299844c3615Sreyk 	default:
300844c3615Sreyk 		return (-1);
301844c3615Sreyk 	}
302844c3615Sreyk 
303844c3615Sreyk 	return (0);
304844c3615Sreyk }
305844c3615Sreyk 
306844c3615Sreyk int
logger_dispatch_server(int fd,struct privsep_proc * p,struct imsg * imsg)307844c3615Sreyk logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
308844c3615Sreyk {
309844c3615Sreyk 	switch (imsg->hdr.type) {
310844c3615Sreyk 	case IMSG_LOG_ACCESS:
311844c3615Sreyk 	case IMSG_LOG_ERROR:
312cf605f40Sreyk 		logger_log(imsg);
313cf605f40Sreyk 		break;
314844c3615Sreyk 	default:
315844c3615Sreyk 		return (-1);
316844c3615Sreyk 	}
317844c3615Sreyk 
318844c3615Sreyk 	return (0);
319844c3615Sreyk }
320