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