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