1 /* $OpenBSD: http_log.c,v 1.17 2005/02/09 12:13:09 henning Exp $ */
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" must
29 * not be used to endorse or promote products derived from this
30 * software without prior written permission. For written
31 * permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * nor may "Apache" appear in their name, without prior written
35 * permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 *
56 * Portions of this software are based upon public domain software
57 * originally written at the National Center for Supercomputing Applications,
58 * University of Illinois, Urbana-Champaign.
59 */
60
61 /*
62 * http_log.c: Dealing with the logs and errors
63 *
64 * Rob McCool
65 *
66 */
67
68
69 #define CORE_PRIVATE
70 #include "httpd.h"
71 #include "http_conf_globals.h"
72 #include "http_config.h"
73 #include "http_core.h"
74 #include "http_log.h"
75 #include "http_main.h"
76
77 #include <stdarg.h>
78
79 typedef struct {
80 char *t_name;
81 int t_val;
82 } TRANS;
83
84 static const TRANS facilities[] = {
85 {"auth", LOG_AUTH},
86 {"authpriv",LOG_AUTHPRIV},
87 {"cron", LOG_CRON},
88 {"daemon", LOG_DAEMON},
89 {"ftp", LOG_FTP},
90 {"kern", LOG_KERN},
91 {"lpr", LOG_LPR},
92 {"mail", LOG_MAIL},
93 {"news", LOG_NEWS},
94 {"syslog", LOG_SYSLOG},
95 {"user", LOG_USER},
96 {"uucp", LOG_UUCP},
97 {"local0", LOG_LOCAL0},
98 {"local1", LOG_LOCAL1},
99 {"local2", LOG_LOCAL2},
100 {"local3", LOG_LOCAL3},
101 {"local4", LOG_LOCAL4},
102 {"local5", LOG_LOCAL5},
103 {"local6", LOG_LOCAL6},
104 {"local7", LOG_LOCAL7},
105 {NULL, -1},
106 };
107
108 static const TRANS priorities[] = {
109 {"emerg", APLOG_EMERG},
110 {"alert", APLOG_ALERT},
111 {"crit", APLOG_CRIT},
112 {"error", APLOG_ERR},
113 {"warn", APLOG_WARNING},
114 {"notice", APLOG_NOTICE},
115 {"info", APLOG_INFO},
116 {"debug", APLOG_DEBUG},
117 {NULL, -1},
118 };
119
error_log_child(void * cmd,child_info * pinfo)120 static int error_log_child(void *cmd, child_info *pinfo)
121 {
122 /* Child process code for 'ErrorLog "|..."';
123 * may want a common framework for this, since I expect it will
124 * be common for other foo-loggers to want this sort of thing...
125 */
126 int child_pid = 0;
127
128 ap_cleanup_for_exec();
129 /* No concept of a child process on Win32 */
130 signal(SIGHUP, SIG_IGN);
131 execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, (char *)NULL);
132 exit(1);
133 /* NOT REACHED */
134 return(child_pid);
135 }
136
open_error_log(server_rec * s,pool * p)137 static void open_error_log(server_rec *s, pool *p)
138 {
139 char *fname;
140
141 if (*s->error_fname == '|') {
142 FILE *dummy;
143 if (!ap_spawn_child(p, error_log_child, (void *)(s->error_fname+1),
144 kill_after_timeout, &dummy, NULL, NULL)) {
145 perror("ap_spawn_child");
146 fprintf(stderr, "Couldn't fork child for ErrorLog process\n");
147 exit(1);
148 }
149
150 s->error_log = dummy;
151 }
152
153 else if (!strncasecmp(s->error_fname, "syslog", 6)) {
154 if ((fname = strchr(s->error_fname, ':'))) {
155 const TRANS *fac;
156
157 fname++;
158 for (fac = facilities; fac->t_name; fac++) {
159 if (!strcasecmp(fname, fac->t_name)) {
160 openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID,
161 fac->t_val);
162 s->error_log = NULL;
163 return;
164 }
165 }
166 }
167 else
168 openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID, LOG_LOCAL7);
169
170 s->error_log = NULL;
171 }
172 else {
173 fname = ap_server_root_relative(p, s->error_fname);
174 if (!(s->error_log = ap_pfopen(p, fname, "a"))) {
175 perror("fopen");
176 fprintf(stderr, "%s: could not open error log file %s.\n",
177 ap_server_argv0, fname);
178 exit(1);
179 }
180 }
181 }
182
ap_open_logs(server_rec * s_main,pool * p)183 API_EXPORT(void) ap_open_logs(server_rec *s_main, pool *p)
184 {
185 server_rec *virt, *q;
186 int replace_stderr;
187
188
189 open_error_log(s_main, p);
190
191 replace_stderr = 1;
192 if (s_main->error_log) {
193 /* replace stderr with this new log */
194 fflush(stderr);
195 if (dup2(fileno(s_main->error_log), STDERR_FILENO) == -1) {
196 ap_log_error(APLOG_MARK, APLOG_CRIT, s_main,
197 "unable to replace stderr with error_log");
198 } else {
199 replace_stderr = 0;
200 }
201 }
202 /* note that stderr may still need to be replaced with something
203 * because it points to the old error log, or back to the tty
204 * of the submitter.
205 */
206 if (replace_stderr && freopen("/dev/null", "w", stderr) == NULL) {
207 ap_log_error(APLOG_MARK, APLOG_CRIT, s_main,
208 "unable to replace stderr with /dev/null");
209 }
210
211 for (virt = s_main->next; virt; virt = virt->next) {
212 if (virt->error_fname)
213 {
214 for (q=s_main; q != virt; q = q->next)
215 if (q->error_fname != NULL &&
216 strcmp(q->error_fname, virt->error_fname) == 0)
217 break;
218 if (q == virt)
219 open_error_log(virt, p);
220 else
221 virt->error_log = q->error_log;
222 }
223 else
224 virt->error_log = s_main->error_log;
225 }
226 }
227
ap_error_log2stderr(server_rec * s)228 API_EXPORT(void) ap_error_log2stderr(server_rec *s) {
229 if ( s->error_log != NULL
230 && fileno(s->error_log) != STDERR_FILENO)
231 dup2(fileno(s->error_log), STDERR_FILENO);
232 }
233
log_error_core(const char * file,int line,int level,const server_rec * s,const request_rec * r,const char * fmt,va_list args)234 static void log_error_core(const char *file, int line, int level,
235 const server_rec *s, const request_rec *r,
236 const char *fmt, va_list args)
237 {
238 char errstr[MAX_STRING_LEN];
239 char scratch[MAX_STRING_LEN];
240 size_t len;
241 int save_errno = errno;
242 FILE *logf;
243
244 if (s == NULL) {
245 /*
246 * If we are doing stderr logging (startup), don't log messages that are
247 * above the default server log level unless it is a startup/shutdown
248 * notice
249 */
250 if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) &&
251 ((level & APLOG_LEVELMASK) > DEFAULT_LOGLEVEL))
252 return;
253 logf = stderr;
254 }
255 else if (s->error_log) {
256 /*
257 * If we are doing normal logging, don't log messages that are
258 * above the server log level unless it is a startup/shutdown notice
259 */
260 if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) &&
261 ((level & APLOG_LEVELMASK) > s->loglevel))
262 return;
263 logf = s->error_log;
264 }
265 else {
266 /*
267 * If we are doing syslog logging, don't log messages that are
268 * above the server log level (including a startup/shutdown notice)
269 */
270 if ((level & APLOG_LEVELMASK) > s->loglevel)
271 return;
272 logf = NULL;
273 }
274
275 if (logf) {
276 len = snprintf(errstr, sizeof(errstr), "[%s] ", ap_get_time());
277 } else {
278 len = 0;
279 }
280
281 len += snprintf(errstr + len, sizeof(errstr) - len,
282 "[%s] ", priorities[level & APLOG_LEVELMASK].t_name);
283
284 if (file && (level & APLOG_LEVELMASK) == APLOG_DEBUG) {
285 len += snprintf(errstr + len, sizeof(errstr) - len,
286 "%s(%d): ", file, line);
287 }
288 if (r) {
289 /* XXX: TODO: add a method of selecting whether logged client
290 * addresses are in dotted quad or resolved form... dotted
291 * quad is the most secure, which is why I'm implementing it
292 * first. -djg
293 */
294 len += snprintf(errstr + len, sizeof(errstr) - len,
295 "[client %s] ", r->connection->remote_ip);
296 }
297 if (!(level & APLOG_NOERRNO)
298 && (save_errno != 0)
299 ) {
300 len += snprintf(errstr + len, sizeof(errstr) - len,
301 "(%d)%s: ", save_errno, strerror(save_errno));
302 }
303
304 if (ap_vsnprintf(scratch, sizeof(scratch) - len, fmt, args)) {
305 len += ap_escape_errorlog_item(errstr + len, scratch,
306 sizeof(errstr) - len);
307 }
308
309 /* NULL if we are logging to syslog */
310 if (logf) {
311 fputs(errstr, logf);
312 fputc('\n', logf);
313 fflush(logf);
314 }
315 else {
316 syslog(level & APLOG_LEVELMASK, "%s", errstr);
317 }
318 }
319
ap_log_error(const char * file,int line,int level,const server_rec * s,const char * fmt,...)320 API_EXPORT_NONSTD(void) ap_log_error(const char *file, int line, int level,
321 const server_rec *s, const char *fmt, ...)
322 {
323 va_list args;
324
325 va_start(args, fmt);
326 log_error_core(file, line, level, s, NULL, fmt, args);
327 va_end(args);
328 }
329
ap_log_rerror(const char * file,int line,int level,const request_rec * r,const char * fmt,...)330 API_EXPORT_NONSTD(void) ap_log_rerror(const char *file, int line, int level,
331 const request_rec *r, const char *fmt, ...)
332 {
333 va_list args;
334
335 va_start(args, fmt);
336 log_error_core(file, line, level, r->server, r, fmt, args);
337 /*
338 * IF the error level is 'warning' or more severe,
339 * AND there isn't already error text associated with this request,
340 * THEN make the message text available to ErrorDocument and
341 * other error processors. This can be disabled by stuffing
342 * something, even an empty string, into the "error-notes" cell
343 * before calling this routine.
344 */
345 va_end(args);
346 va_start(args,fmt);
347 if (((level & APLOG_LEVELMASK) <= APLOG_WARNING)
348 && (ap_table_get(r->notes, "error-notes") == NULL)) {
349 ap_table_setn(r->notes, "error-notes",
350 ap_escape_html(r->pool, ap_pvsprintf(r->pool, fmt,
351 args)));
352 }
353 va_end(args);
354 }
355
ap_log_pid(pool * p,char * fname)356 API_EXPORT(void) ap_log_pid(pool *p, char *fname)
357 {
358 FILE *pid_file;
359 struct stat finfo;
360 static pid_t saved_pid = -1;
361 pid_t mypid;
362 mode_t u;
363
364 if (!fname)
365 return;
366
367 fname = ap_server_root_relative(p, fname);
368 mypid = getpid();
369 if (!ap_server_chroot_desired() && mypid != saved_pid
370 && stat(fname, &finfo) == 0) {
371 /* USR1 and HUP call this on each restart.
372 * Only warn on first time through for this pid.
373 *
374 * XXX: Could just write first time through too, although
375 * that may screw up scripts written to do something
376 * based on the last modification time of the pid file.
377 */
378 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
379 ap_psprintf(p,
380 "pid file %s overwritten -- Unclean shutdown of previous Apache run?",
381 fname)
382 );
383 }
384
385 u = umask(022);
386 (void) umask(u | 022);
387 if(!(pid_file = fopen(fname, "w"))) {
388 perror("fopen");
389 fprintf(stderr, "%s: could not log pid to file %s\n",
390 ap_server_argv0, fname);
391 exit(1);
392 }
393 (void) umask(u);
394 fprintf(pid_file, "%ld\n", (long)mypid);
395 fclose(pid_file);
396 saved_pid = mypid;
397 }
398
ap_log_error_old(const char * err,server_rec * s)399 API_EXPORT(void) ap_log_error_old(const char *err, server_rec *s)
400 {
401 ap_log_error(APLOG_MARK, APLOG_ERR, s, "%s", err);
402 }
403
ap_log_unixerr(const char * routine,const char * file,const char * msg,server_rec * s)404 API_EXPORT(void) ap_log_unixerr(const char *routine, const char *file,
405 const char *msg, server_rec *s)
406 {
407 ap_log_error(file, 0, APLOG_ERR, s, "%s", msg);
408 }
409
ap_log_printf(const server_rec * s,const char * fmt,...)410 API_EXPORT_NONSTD(void) ap_log_printf(const server_rec *s, const char *fmt, ...)
411 {
412 va_list args;
413
414 va_start(args, fmt);
415 log_error_core(APLOG_MARK, APLOG_ERR, s, NULL, fmt, args);
416 va_end(args);
417 }
418
ap_log_reason(const char * reason,const char * file,request_rec * r)419 API_EXPORT(void) ap_log_reason(const char *reason, const char *file, request_rec *r)
420 {
421 ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
422 "access to %s failed for %s, reason: %s",
423 file,
424 ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME),
425 reason);
426 }
427
ap_log_assert(const char * szExp,const char * szFile,int nLine)428 API_EXPORT(void) ap_log_assert(const char *szExp, const char *szFile, int nLine)
429 {
430 fprintf(stderr, "[%s] file %s, line %d, assertion \"%s\" failed\n",
431 ap_get_time(), szFile, nLine, szExp);
432 /* unix assert does an abort leading to a core dump */
433 abort();
434 }
435
436 /* piped log support */
437
438 /* forward declaration */
439 static void piped_log_maintenance(int reason, void *data, ap_wait_t status);
440
piped_log_spawn(piped_log * pl)441 static int piped_log_spawn(piped_log *pl)
442 {
443 int pid;
444
445 ap_block_alarms();
446 pid = fork();
447 if (pid == 0) {
448 /* XXX: need to check what open fds the logger is actually passed,
449 * XXX: and CGIs for that matter ... cleanup_for_exec *should*
450 * XXX: close all the relevant stuff, but hey, it could be broken. */
451 RAISE_SIGSTOP(PIPED_LOG_SPAWN);
452 /* we're now in the child */
453 close(STDIN_FILENO);
454 dup2(pl->fds[0], STDIN_FILENO);
455
456 ap_cleanup_for_exec();
457 signal(SIGCHLD, SIG_DFL); /* for HPUX */
458 signal(SIGHUP, SIG_IGN);
459 execl(SHELL_PATH, SHELL_PATH, "-c", pl->program, (char *)NULL);
460 fprintf(stderr,
461 "piped_log_spawn: unable to exec %s -c '%s': %s\n",
462 SHELL_PATH, pl->program, strerror (errno));
463 exit(1);
464 }
465 if (pid == -1) {
466 fprintf(stderr,
467 "piped_log_spawn: unable to fork(): %s\n", strerror (errno));
468 ap_unblock_alarms();
469 return -1;
470 }
471 ap_unblock_alarms();
472 pl->pid = pid;
473 ap_register_other_child(pid, piped_log_maintenance, pl, pl->fds[1]);
474 return 0;
475 }
476
477
piped_log_maintenance(int reason,void * data,ap_wait_t status)478 static void piped_log_maintenance(int reason, void *data, ap_wait_t status)
479 {
480 piped_log *pl = data;
481
482 switch (reason) {
483 case OC_REASON_DEATH:
484 case OC_REASON_LOST:
485 pl->pid = -1;
486 ap_unregister_other_child(pl);
487 if (pl->program == NULL) {
488 /* during a restart */
489 break;
490 }
491 if (piped_log_spawn(pl) == -1) {
492 /* what can we do? This could be the error log we're having
493 * problems opening up... */
494 fprintf(stderr,
495 "piped_log_maintenance: unable to respawn '%s': %s\n",
496 pl->program, strerror(errno));
497 }
498 break;
499
500 case OC_REASON_UNWRITABLE:
501 /* We should not kill off the pipe here, since it may only be full.
502 * If it really is locked, we should kill it off manually. */
503 break;
504
505 case OC_REASON_RESTART:
506 pl->program = NULL;
507 if (pl->pid != -1) {
508 kill(pl->pid, SIGTERM);
509 }
510 break;
511
512 case OC_REASON_UNREGISTER:
513 break;
514 }
515 }
516
517
piped_log_cleanup(void * data)518 static void piped_log_cleanup(void *data)
519 {
520 piped_log *pl = data;
521
522 if (pl->pid != -1) {
523 kill(pl->pid, SIGTERM);
524 }
525 ap_unregister_other_child(pl);
526 close(pl->fds[0]);
527 close(pl->fds[1]);
528 }
529
530
piped_log_cleanup_for_exec(void * data)531 static void piped_log_cleanup_for_exec(void *data)
532 {
533 piped_log *pl = data;
534
535 close(pl->fds[0]);
536 close(pl->fds[1]);
537 }
538
piped_log_magic_cleanup(void * data)539 static int piped_log_magic_cleanup(void *data)
540 {
541 piped_log *pl = data;
542
543 /* Yes, I _do_ mean a binary and */
544 return ap_close_fd_on_exec(pl->fds[0]) & ap_close_fd_on_exec(pl->fds[1]);
545 }
546
ap_open_piped_log(pool * p,const char * program)547 API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program)
548 {
549 piped_log *pl;
550
551 pl = ap_palloc(p, sizeof (*pl));
552 pl->p = p;
553 pl->program = ap_pstrdup(p, program);
554 pl->pid = -1;
555 ap_block_alarms ();
556 if (pipe(pl->fds) == -1) {
557 int save_errno = errno;
558 ap_unblock_alarms();
559 errno = save_errno;
560 return NULL;
561 }
562 ap_register_cleanup_ex(p, pl, piped_log_cleanup, piped_log_cleanup_for_exec,
563 piped_log_magic_cleanup);
564 if (piped_log_spawn(pl) == -1) {
565 int save_errno = errno;
566 ap_kill_cleanup(p, pl, piped_log_cleanup);
567 close(pl->fds[0]);
568 close(pl->fds[1]);
569 ap_unblock_alarms();
570 errno = save_errno;
571 return NULL;
572 }
573 ap_unblock_alarms();
574 return pl;
575 }
576
ap_close_piped_log(piped_log * pl)577 API_EXPORT(void) ap_close_piped_log(piped_log *pl)
578 {
579 ap_block_alarms();
580 piped_log_cleanup(pl);
581 ap_kill_cleanup(pl->p, pl, piped_log_cleanup);
582 ap_unblock_alarms();
583 }
584