]> granicus.if.org Git - apache/blob - server/log.c
Win32: Whack the fully qualified names that appear in the log when
[apache] / server / log.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /*
60  * http_log.c: Dealing with the logs and errors
61  *
62  * Rob McCool
63  *
64  */
65
66 #include "apr.h"
67 #include "apr_general.h"        /* for signal stuff */
68 #include "apr_strings.h"
69 #include "apr_errno.h"
70 #include "apr_thread_proc.h"
71 #include "apr_lib.h"
72 #include "apr_signal.h"
73
74 #define APR_WANT_STDIO
75 #define APR_WANT_STRFUNC
76 #include "apr_want.h"
77
78 #if APR_HAVE_STDARG_H
79 #include <stdarg.h>
80 #endif
81 #if APR_HAVE_UNISTD_H
82 #include <unistd.h>
83 #endif
84
85 #define CORE_PRIVATE
86
87 #include "ap_config.h"
88 #include "httpd.h"
89 #include "http_config.h"
90 #include "http_core.h"
91 #include "http_log.h"
92 #include "http_main.h"
93 #include "util_time.h"
94
95 typedef struct {
96     char    *t_name;
97     int      t_val;
98 } TRANS;
99
100 APR_HOOK_STRUCT(
101     APR_HOOK_LINK(error_log)
102 )
103
104 int AP_DECLARE_DATA ap_default_loglevel = DEFAULT_LOGLEVEL;
105
106 #ifdef HAVE_SYSLOG
107
108 static const TRANS facilities[] = {
109     {"auth",    LOG_AUTH},
110 #ifdef LOG_AUTHPRIV
111     {"authpriv",LOG_AUTHPRIV},
112 #endif
113 #ifdef LOG_CRON
114     {"cron",    LOG_CRON},
115 #endif
116 #ifdef LOG_DAEMON
117     {"daemon",  LOG_DAEMON},
118 #endif
119 #ifdef LOG_FTP
120     {"ftp", LOG_FTP},
121 #endif
122 #ifdef LOG_KERN
123     {"kern",    LOG_KERN},
124 #endif
125 #ifdef LOG_LPR
126     {"lpr", LOG_LPR},
127 #endif
128 #ifdef LOG_MAIL
129     {"mail",    LOG_MAIL},
130 #endif
131 #ifdef LOG_NEWS
132     {"news",    LOG_NEWS},
133 #endif
134 #ifdef LOG_SYSLOG
135     {"syslog",  LOG_SYSLOG},
136 #endif
137 #ifdef LOG_USER
138     {"user",    LOG_USER},
139 #endif
140 #ifdef LOG_UUCP
141     {"uucp",    LOG_UUCP},
142 #endif
143 #ifdef LOG_LOCAL0
144     {"local0",  LOG_LOCAL0},
145 #endif
146 #ifdef LOG_LOCAL1
147     {"local1",  LOG_LOCAL1},
148 #endif
149 #ifdef LOG_LOCAL2
150     {"local2",  LOG_LOCAL2},
151 #endif
152 #ifdef LOG_LOCAL3
153     {"local3",  LOG_LOCAL3},
154 #endif
155 #ifdef LOG_LOCAL4
156     {"local4",  LOG_LOCAL4},
157 #endif
158 #ifdef LOG_LOCAL5
159     {"local5",  LOG_LOCAL5},
160 #endif
161 #ifdef LOG_LOCAL6
162     {"local6",  LOG_LOCAL6},
163 #endif
164 #ifdef LOG_LOCAL7
165     {"local7",  LOG_LOCAL7},
166 #endif
167     {NULL,      -1},
168 };
169 #endif
170
171 static const TRANS priorities[] = {
172     {"emerg",   APLOG_EMERG},
173     {"alert",   APLOG_ALERT},
174     {"crit",    APLOG_CRIT},
175     {"error",   APLOG_ERR},
176     {"warn",    APLOG_WARNING},
177     {"notice",  APLOG_NOTICE},
178     {"info",    APLOG_INFO},
179     {"debug",   APLOG_DEBUG},
180     {NULL,      -1},
181 };
182
183 static apr_file_t *stderr_log = NULL;
184
185 AP_DECLARE(void) ap_open_stderr_log(apr_pool_t *p)
186 {
187     apr_file_open_stderr(&stderr_log, p);
188 }
189
190 AP_DECLARE(apr_status_t) ap_replace_stderr_log(apr_pool_t *p, 
191                                                const char *fname)
192 {
193     apr_file_t *stderr_file;
194     apr_status_t rc;
195     char *filename = ap_server_root_relative(p, fname);
196     if (!filename) {
197         ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT,
198                      APR_EBADPATH, NULL, "Invalid -E error log file %s",
199                      fname);
200         return APR_EBADPATH;
201     }
202     if ((rc = apr_file_open(&stderr_file, filename,
203                             APR_APPEND | APR_READ | APR_WRITE | APR_CREATE,
204                             APR_OS_DEFAULT, p)) != APR_SUCCESS) {
205         ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL,
206                      "%s: could not open error log file %s.",
207                      ap_server_argv0, fname);
208         return rc;
209     }
210     if ((rc = apr_file_open_stderr(&stderr_log, p)) == APR_SUCCESS) {
211         apr_file_flush(stderr_log);
212         if ((rc = apr_file_dup2(stderr_log, stderr_file, p)) == APR_SUCCESS) {
213             apr_file_close(stderr_file);
214         }
215     }
216     if (rc != APR_SUCCESS) {
217         ap_log_error(APLOG_MARK, APLOG_CRIT, rc, NULL,
218                      "unable to replace stderr with error_log");
219     }
220     return rc;
221 }
222
223 static void log_child_errfn(apr_pool_t *pool, apr_status_t err,
224                             const char *description)
225 {
226     ap_log_error(APLOG_MARK, APLOG_ERR, err, NULL,
227                  "%s", description);
228 }
229
230 static int log_child(apr_pool_t *p, const char *progname,
231                      apr_file_t **fpin)
232 {
233     /* Child process code for 'ErrorLog "|..."';
234      * may want a common framework for this, since I expect it will
235      * be common for other foo-loggers to want this sort of thing...
236      */
237     apr_status_t rc;
238     apr_procattr_t *procattr;
239     apr_proc_t *procnew;
240
241     if (((rc = apr_procattr_create(&procattr, p)) == APR_SUCCESS)
242         && ((rc = apr_procattr_io_set(procattr,
243                                       APR_FULL_BLOCK,
244                                       APR_NO_PIPE,
245                                       APR_NO_PIPE)) == APR_SUCCESS)
246         && ((rc = apr_procattr_error_check_set(procattr, 1)) == APR_SUCCESS)
247         && ((rc = apr_procattr_child_errfn_set(procattr, log_child_errfn)) == APR_SUCCESS)) {
248         char **args;
249         const char *pname;
250
251         apr_tokenize_to_argv(progname, &args, p);
252         pname = apr_pstrdup(p, args[0]);
253         procnew = (apr_proc_t *)apr_pcalloc(p, sizeof(*procnew));
254         rc = apr_proc_create(procnew, pname, (const char * const *)args,
255                              NULL, procattr, p);
256
257         if (rc == APR_SUCCESS) {
258             apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
259             (*fpin) = procnew->in;
260         }
261     }
262
263     return rc;
264 }
265
266 static int open_error_log(server_rec *s, apr_pool_t *p)
267 {
268     const char *fname;
269     int rc;
270
271     if (*s->error_fname == '|') {
272         apr_file_t *dummy = NULL;
273
274         /* This starts a new process... */
275         rc = log_child (p, s->error_fname + 1, &dummy);
276         if (rc != APR_SUCCESS) {
277             ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL,
278                          "Couldn't start ErrorLog process");
279             return DONE;
280         }
281
282         s->error_log = dummy;
283     }
284
285 #ifdef HAVE_SYSLOG
286     else if (!strncasecmp(s->error_fname, "syslog", 6)) {
287         if ((fname = strchr(s->error_fname, ':'))) {
288             const TRANS *fac;
289
290             fname++;
291             for (fac = facilities; fac->t_name; fac++) {
292                 if (!strcasecmp(fname, fac->t_name)) {
293                     openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID,
294                             fac->t_val);
295                     s->error_log = NULL;
296                     return OK;
297                 }
298             }
299         }
300         else {
301             openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID, LOG_LOCAL7);
302         }
303
304         s->error_log = NULL;
305     }
306 #endif
307     else {
308         fname = ap_server_root_relative(p, s->error_fname);
309         if (!fname) {
310             ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EBADPATH, NULL,
311                          "%s: Invalid error log path %s.",
312                          ap_server_argv0, s->error_fname);
313             return DONE;
314         }
315         if ((rc = apr_file_open(&s->error_log, fname,
316                                APR_APPEND | APR_READ | APR_WRITE | APR_CREATE,
317                                APR_OS_DEFAULT, p)) != APR_SUCCESS) {
318             ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL,
319                          "%s: could not open error log file %s.",
320                          ap_server_argv0, fname);
321             return DONE;
322         }
323     }
324
325     return OK;
326 }
327
328 int ap_open_logs(apr_pool_t *pconf, apr_pool_t *p /* plog */, 
329                  apr_pool_t *ptemp, server_rec *s_main)
330 {
331     apr_status_t rc = APR_SUCCESS;
332     server_rec *virt, *q;
333     int replace_stderr;
334     apr_file_t *errfile = NULL;
335
336     if (open_error_log(s_main, p) != OK) {
337         return DONE;
338     }
339
340     replace_stderr = 1;
341     if (s_main->error_log) {
342         /* replace stderr with this new log */
343         apr_file_flush(s_main->error_log);
344         if ((rc = apr_file_open_stderr(&errfile, p)) == APR_SUCCESS) {
345             rc = apr_file_dup2(errfile, s_main->error_log, p);
346         }
347         if (rc != APR_SUCCESS) {
348             ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s_main,
349                          "unable to replace stderr with error_log");
350         }
351         else {
352             replace_stderr = 0;
353         }
354     }
355     /* note that stderr may still need to be replaced with something
356      * because it points to the old error log, or back to the tty
357      * of the submitter.
358      * XXX: This is BS - /dev/null is non-portable
359      */
360     if (replace_stderr && freopen("/dev/null", "w", stderr) == NULL) {
361         ap_log_error(APLOG_MARK, APLOG_CRIT, errno, s_main,
362                      "unable to replace stderr with /dev/null");
363     }
364
365     for (virt = s_main->next; virt; virt = virt->next) {
366         if (virt->error_fname) {
367             for (q=s_main; q != virt; q = q->next) {
368                 if (q->error_fname != NULL
369                     && strcmp(q->error_fname, virt->error_fname) == 0) {
370                     break;
371                 }
372             }
373
374             if (q == virt) {
375                 if (open_error_log(virt, p) != OK) {
376                     return DONE;
377                 }
378             }
379             else {
380                 virt->error_log = q->error_log;
381             }
382         }
383         else {
384             virt->error_log = s_main->error_log;
385         }
386     }
387     return OK;
388 }
389
390 AP_DECLARE(void) ap_error_log2stderr(server_rec *s) {
391     apr_file_t *errfile = NULL;
392
393     apr_file_open_stderr(&errfile, s->process->pool);
394     if (s->error_log != NULL) {
395         apr_file_dup2(s->error_log, errfile, s->process->pool);
396     }
397 }
398
399 static void log_error_core(const char *file, int line, int level,
400                            apr_status_t status, const server_rec *s,
401                            const request_rec *r, apr_pool_t *pool,
402                            const char *fmt, va_list args)
403 {
404     char errstr[MAX_STRING_LEN];
405     apr_size_t len, errstrlen;
406     apr_file_t *logf = NULL;
407     const char *referer;
408     int level_and_mask = level & APLOG_LEVELMASK;
409
410     if (s == NULL) {
411         /*
412          * If we are doing stderr logging (startup), don't log messages that are
413          * above the default server log level unless it is a startup/shutdown
414          * notice
415          */
416         if ((level_and_mask != APLOG_NOTICE)
417             && (level_and_mask > ap_default_loglevel)) {
418             return;
419         }
420
421         logf = stderr_log;
422     }
423     else if (s->error_log) {
424         /*
425          * If we are doing normal logging, don't log messages that are
426          * above the server log level unless it is a startup/shutdown notice
427          */
428         if ((level_and_mask != APLOG_NOTICE)
429             && (level_and_mask > s->loglevel)) {
430             return;
431         }
432
433         logf = s->error_log;
434     }
435 #ifdef TPF
436     else if (tpf_child) {
437         /*
438          * If we are doing normal logging, don't log messages that are
439          * above the server log level unless it is a startup/shutdown notice
440          */
441         if ((level_and_mask != APLOG_NOTICE)
442             && (level_and_mask > s->loglevel)) {
443             return;
444         }
445
446         logf = stderr;
447     }
448 #endif /* TPF */
449     else {
450         /*
451          * If we are doing syslog logging, don't log messages that are
452          * above the server log level (including a startup/shutdown notice)
453          */
454         if (level_and_mask > s->loglevel) {
455             return;
456         }
457     }
458
459     if (logf && ((level & APLOG_STARTUP) != APLOG_STARTUP)) {
460         errstr[0] = '[';
461         ap_recent_ctime(errstr + 1, apr_time_now());
462         errstr[1 + APR_CTIME_LEN - 1] = ']';
463         errstr[1 + APR_CTIME_LEN    ] = ' ';
464         len = 1 + APR_CTIME_LEN + 1;
465     } else {
466         len = 0;
467     }
468
469     if ((level & APLOG_STARTUP) != APLOG_STARTUP) {
470         len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
471                             "[%s] ", priorities[level_and_mask].t_name);
472     }
473
474 #ifndef TPF
475     if (file && level_and_mask == APLOG_DEBUG) {
476 #if defined(_OSD_POSIX) || defined(WIN32)
477         char tmp[256];
478         char *e = strrchr(file, '/');
479 #ifdef WIN32
480         if (!e) {
481             e = strrchr(file, '\\');
482         }
483 #endif
484
485         /* In OSD/POSIX, the compiler returns for __FILE__
486          * a string like: __FILE__="*POSIX(/usr/include/stdio.h)"
487          * (it even returns an absolute path for sources in
488          * the current directory). Here we try to strip this
489          * down to the basename.
490          */
491         if (e != NULL && e[1] != '\0') {
492             apr_snprintf(tmp, sizeof(tmp), "%s", &e[1]);
493             e = &tmp[strlen(tmp)-1];
494             if (*e == ')') {
495                 *e = '\0';
496             }
497             file = tmp;
498         }
499 #endif /*_OSD_POSIX*/
500         len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
501                             "%s(%d): ", file, line);
502     }
503 #endif /* TPF */
504
505     if (r && r->connection) {
506         /* XXX: TODO: add a method of selecting whether logged client
507          * addresses are in dotted quad or resolved form... dotted
508          * quad is the most secure, which is why I'm implementing it
509          * first. -djg
510          */
511         len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
512                             "[client %s] ", r->connection->remote_ip);
513     }
514     if (status != 0) {
515         if (status < APR_OS_START_EAIERR) {
516             len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
517                                 "(%d)", status);
518         }
519         else if (status < APR_OS_START_SYSERR) {
520             len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
521                                 "(EAI %d)", status - APR_OS_START_EAIERR);
522         }
523         else if (status < 100000 + APR_OS_START_SYSERR) {
524             len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
525                                 "(OS %d)", status - APR_OS_START_SYSERR);
526         }
527         else {
528             len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
529                                 "(os 0x%08x)", status - APR_OS_START_SYSERR);
530         }
531         apr_strerror(status, errstr + len, MAX_STRING_LEN - len);
532         len += strlen(errstr + len);
533         if (MAX_STRING_LEN - len > 2) {
534             errstr[len++] = ':';
535             errstr[len++] = ' ';
536             errstr[len] = '\0';
537         }
538     }
539     errstrlen = len;
540     len += apr_vsnprintf(errstr + len, MAX_STRING_LEN - len, fmt, args);
541
542     if (r && (referer = apr_table_get(r->headers_in, "Referer"))) {
543         len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
544                             ", referer: %s", referer);
545     }
546
547     /* NULL if we are logging to syslog */
548     if (logf) {
549         /* Truncate for the terminator (as apr_snprintf does) */
550         if (len > MAX_STRING_LEN - sizeof(APR_EOL_STR)) {
551             len = MAX_STRING_LEN - sizeof(APR_EOL_STR);
552         }
553         strcpy(errstr + len, APR_EOL_STR);
554         apr_file_puts(errstr, logf);
555         apr_file_flush(logf);
556     }
557 #ifdef HAVE_SYSLOG
558     else {
559         syslog(level_and_mask, "%s", errstr);
560     }
561 #endif
562
563     ap_run_error_log(file, line, level, status, s, r, pool, errstr + errstrlen);
564 }
565
566 AP_DECLARE(void) ap_log_error(const char *file, int line, int level,
567                               apr_status_t status, const server_rec *s,
568                               const char *fmt, ...)
569 {
570     va_list args;
571
572     va_start(args, fmt);
573     log_error_core(file, line, level, status, s, NULL, NULL, fmt, args);
574     va_end(args);
575 }
576
577 AP_DECLARE(void) ap_log_perror(const char *file, int line, int level,
578                                apr_status_t status, apr_pool_t *p,
579                                const char *fmt, ...)
580 {
581     va_list args;
582
583     va_start(args, fmt);
584     log_error_core(file, line, level, status, NULL, NULL, p, fmt, args);
585     va_end(args);
586 }
587
588 AP_DECLARE(void) ap_log_rerror(const char *file, int line, int level,
589                                apr_status_t status, const request_rec *r,
590                                const char *fmt, ...)
591 {
592     va_list args;
593
594     va_start(args, fmt);
595     log_error_core(file, line, level, status, r->server, r, NULL, fmt, args);
596
597     /*
598      * IF APLOG_TOCLIENT is set,
599      * AND the error level is 'warning' or more severe,
600      * AND there isn't already error text associated with this request,
601      * THEN make the message text available to ErrorDocument and
602      * other error processors.
603      */
604     va_end(args);
605     va_start(args,fmt);
606     if ((level & APLOG_TOCLIENT)
607         && ((level & APLOG_LEVELMASK) <= APLOG_WARNING)
608         && (apr_table_get(r->notes, "error-notes") == NULL)) {
609         apr_table_setn(r->notes, "error-notes",
610                        ap_escape_html(r->pool, apr_pvsprintf(r->pool, fmt,
611                                                              args)));
612     }
613     va_end(args);
614 }
615
616 AP_DECLARE(void) ap_log_pid(apr_pool_t *p, const char *filename)
617 {
618     apr_file_t *pid_file = NULL;
619     apr_finfo_t finfo;
620     static pid_t saved_pid = -1;
621     pid_t mypid;
622     apr_status_t rv;
623     const char *fname;
624
625     if (!filename) {
626         return;
627     }
628
629     fname = ap_server_root_relative(p, filename);
630     if (!fname) {
631         ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, APR_EBADPATH, 
632                      NULL, "Invalid PID file path %s, ignoring.", filename);
633         return;
634     }
635
636     mypid = getpid();
637     if (mypid != saved_pid
638         && apr_stat(&finfo, fname, APR_FINFO_MTIME, p) == APR_SUCCESS) {
639         /* AP_SIG_GRACEFUL and HUP call this on each restart.
640          * Only warn on first time through for this pid.
641          *
642          * XXX: Could just write first time through too, although
643          *      that may screw up scripts written to do something
644          *      based on the last modification time of the pid file.
645          */
646         ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, p,
647                       apr_psprintf(p, "pid file %s overwritten -- Unclean "
648                                    "shutdown of previous Apache run?",
649                                    fname));
650     }
651
652     if ((rv = apr_file_open(&pid_file, fname,
653                             APR_WRITE | APR_CREATE | APR_TRUNCATE,
654                             APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD, p))
655         != APR_SUCCESS) {
656         ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
657                      "could not create %s", fname);
658         ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
659                      "%s: could not log pid to file %s",
660                      ap_server_argv0, fname);
661         exit(1);
662     }
663     apr_file_printf(pid_file, "%ld" APR_EOL_STR, (long)mypid);
664     apr_file_close(pid_file);
665     saved_pid = mypid;
666 }
667
668 AP_DECLARE(apr_status_t) ap_read_pid(apr_pool_t *p, const char *filename,
669                                      pid_t *mypid)
670 {
671     const apr_size_t BUFFER_SIZE = sizeof(long) * 3 + 2; /* see apr_ltoa */
672     apr_file_t *pid_file = NULL;
673     apr_status_t rv;
674     const char *fname;
675     char *buf, *endptr;
676     apr_size_t bytes_read;
677
678     if (!filename) {
679         return APR_EGENERAL;
680     }
681
682     fname = ap_server_root_relative(p, filename);
683     if (!fname) {
684         ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, APR_EBADPATH, 
685                      NULL, "Invalid PID file path %s, ignoring.", filename);
686         return APR_EGENERAL;
687     }
688
689     rv = apr_file_open(&pid_file, fname, APR_READ, APR_OS_DEFAULT, p);
690     if (rv != APR_SUCCESS) {
691         return rv;
692     }
693
694     /* Ensure null-termination, so that strtol doesn't go crazy. */
695     buf = apr_palloc(p, BUFFER_SIZE);
696     buf[BUFFER_SIZE - 1] = '\0';
697
698     rv = apr_file_read_full(pid_file, buf, BUFFER_SIZE - 1, &bytes_read);
699     if (rv != APR_SUCCESS && rv != APR_EOF) {
700         return rv;
701     }
702
703     /* If we fill the buffer, we're probably reading a corrupt pid file.
704      * To be nice, let's also ensure the first char is a digit. */
705     if (bytes_read == BUFFER_SIZE - 1 || !apr_isdigit(*buf)) {
706         return APR_EGENERAL;
707     }
708
709     *mypid = strtol(buf, &endptr, 10);
710
711     apr_file_close(pid_file);
712     return APR_SUCCESS;
713 }
714
715 AP_DECLARE(void) ap_log_assert(const char *szExp, const char *szFile,
716                                int nLine)
717 {
718     char time_str[APR_CTIME_LEN];
719
720     apr_ctime(time_str, apr_time_now());
721     ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
722                  "[%s] file %s, line %d, assertion \"%s\" failed",
723                  time_str, szFile, nLine, szExp);
724 #if defined(WIN32)
725     DebugBreak();
726 #else
727     /* unix assert does an abort leading to a core dump */
728     abort();
729 #endif
730 }
731
732 /* piped log support */
733
734 #ifdef AP_HAVE_RELIABLE_PIPED_LOGS
735 /* forward declaration */
736 static void piped_log_maintenance(int reason, void *data, apr_wait_t status);
737
738 static int piped_log_spawn(piped_log *pl)
739 {
740     int rc = 0;
741     apr_procattr_t *procattr;
742     apr_proc_t *procnew = NULL;
743     apr_status_t status;
744
745     if (((status = apr_procattr_create(&procattr, pl->p)) != APR_SUCCESS) ||
746         ((status = apr_procattr_child_in_set(procattr,
747                                              ap_piped_log_read_fd(pl),
748                                              ap_piped_log_write_fd(pl)))
749         != APR_SUCCESS) ||
750         ((status = apr_procattr_child_errfn_set(procattr, log_child_errfn))
751          != APR_SUCCESS) ||
752         ((status = apr_procattr_error_check_set(procattr, 1)) != APR_SUCCESS)) {
753         char buf[120];
754         /* Something bad happened, give up and go away. */
755         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
756                      "piped_log_spawn: unable to setup child process '%s': %s",
757                      pl->program, apr_strerror(status, buf, sizeof(buf)));
758         rc = -1;
759     }
760     else {
761         char **args;
762         const char *pname;
763
764         apr_tokenize_to_argv(pl->program, &args, pl->p);
765         pname = apr_pstrdup(pl->p, args[0]);
766         procnew = apr_pcalloc(pl->p, sizeof(apr_proc_t));
767         status = apr_proc_create(procnew, pname, (const char * const *) args,
768                                  NULL, procattr, pl->p);
769
770         if (status == APR_SUCCESS) {
771             pl->pid = procnew;
772             ap_piped_log_write_fd(pl) = procnew->in;
773             apr_proc_other_child_register(procnew, piped_log_maintenance, pl,
774                                           ap_piped_log_write_fd(pl), pl->p);
775         }
776         else {
777             char buf[120];
778             /* Something bad happened, give up and go away. */
779             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
780                          "unable to start piped log program '%s': %s",
781                          pl->program, apr_strerror(status, buf, sizeof(buf)));
782             rc = -1;
783         }
784     }
785
786     return rc;
787 }
788
789
790 static void piped_log_maintenance(int reason, void *data, apr_wait_t status)
791 {
792     piped_log *pl = data;
793     apr_status_t stats;
794
795     switch (reason) {
796     case APR_OC_REASON_DEATH:
797     case APR_OC_REASON_LOST:
798         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
799                      "piped log program '%s' failed unexpectedly",
800                      pl->program);
801         pl->pid = NULL;
802         apr_proc_other_child_unregister(pl);
803         if (pl->program == NULL) {
804             /* during a restart */
805             break;
806         }
807         if ((stats = piped_log_spawn(pl)) != APR_SUCCESS) {
808             /* what can we do?  This could be the error log we're having
809              * problems opening up... */
810             char buf[120];
811             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
812                          "piped_log_maintenance: unable to respawn '%s': %s",
813                          pl->program, apr_strerror(stats, buf, sizeof(buf)));
814         }
815         break;
816
817     case APR_OC_REASON_UNWRITABLE:
818         /* We should not kill off the pipe here, since it may only be full.
819          * If it really is locked, we should kill it off manually. */
820     break;
821
822     case APR_OC_REASON_RESTART:
823         pl->program = NULL;
824         if (pl->pid != NULL) {
825             apr_proc_kill(pl->pid, SIGTERM);
826         }
827         break;
828
829     case APR_OC_REASON_UNREGISTER:
830         break;
831     }
832 }
833
834
835 static apr_status_t piped_log_cleanup_for_exec(void *data)
836 {
837     piped_log *pl = data;
838
839     apr_file_close(ap_piped_log_read_fd(pl));
840     apr_file_close(ap_piped_log_write_fd(pl));
841     return APR_SUCCESS;
842 }
843
844
845 static apr_status_t piped_log_cleanup(void *data)
846 {
847     piped_log *pl = data;
848
849     if (pl->pid != NULL) {
850         apr_proc_kill(pl->pid, SIGTERM);
851     }
852     return piped_log_cleanup_for_exec(data);
853 }
854
855
856 AP_DECLARE(piped_log *) ap_open_piped_log(apr_pool_t *p, const char *program)
857 {
858     piped_log *pl;
859
860     pl = apr_palloc(p, sizeof (*pl));
861     pl->p = p;
862     pl->program = apr_pstrdup(p, program);
863     pl->pid = NULL;
864     if (apr_file_pipe_create(&ap_piped_log_read_fd(pl),
865                              &ap_piped_log_write_fd(pl), p) != APR_SUCCESS) {
866         return NULL;
867     }
868     apr_pool_cleanup_register(p, pl, piped_log_cleanup,
869                               piped_log_cleanup_for_exec);
870     if (piped_log_spawn(pl) == -1) {
871         int save_errno = errno;
872         apr_pool_cleanup_kill(p, pl, piped_log_cleanup);
873         apr_file_close(ap_piped_log_read_fd(pl));
874         apr_file_close(ap_piped_log_write_fd(pl));
875         errno = save_errno;
876         return NULL;
877     }
878     return pl;
879 }
880
881 #else /* !AP_HAVE_RELIABLE_PIPED_LOGS */
882
883 static apr_status_t piped_log_cleanup(void *data)
884 {
885     piped_log *pl = data;
886
887     apr_file_close(ap_piped_log_write_fd(pl));
888     return APR_SUCCESS;
889 }
890
891 AP_DECLARE(piped_log *) ap_open_piped_log(apr_pool_t *p, const char *program)
892 {
893     piped_log *pl;
894     apr_file_t *dummy = NULL;
895     int rc;
896
897     rc = log_child(p, program, &dummy);
898     if (rc != APR_SUCCESS) {
899         ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL,
900                      "Couldn't start piped log process");
901         return NULL;
902     }
903
904     pl = apr_palloc(p, sizeof (*pl));
905     pl->p = p;
906     ap_piped_log_read_fd(pl) = NULL;
907     ap_piped_log_write_fd(pl) = dummy;
908     apr_pool_cleanup_register(p, pl, piped_log_cleanup, piped_log_cleanup);
909
910     return pl;
911 }
912
913 #endif
914
915 AP_DECLARE(void) ap_close_piped_log(piped_log *pl)
916 {
917     apr_pool_cleanup_run(pl->p, pl, piped_log_cleanup);
918 }
919
920 AP_IMPLEMENT_HOOK_VOID(error_log,
921                        (const char *file, int line, int level,
922                         apr_status_t status, const server_rec *s,
923                         const request_rec *r, apr_pool_t *pool,
924                         const char *errstr), (file, line, level,
925                         status, s, r, pool, errstr))
926