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