GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/httpd/logger.c Lines: 16 116 13.8 %
Date: 2017-11-07 Branches: 5 71 7.0 %

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