]> granicus.if.org Git - apache/blob - server/log.c
Description of terms used to describe modules in the new (not yet
[apache] / server / log.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 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
67 #define CORE_PRIVATE
68 #include "apr.h"  /* for apr_signal */
69 #include "ap_config.h"
70 #include "apr_strings.h"
71 #include "apr_lib.h"
72 #include "apr_portable.h"
73 #include "httpd.h"
74 #include "http_config.h"
75 #include "http_core.h"
76 #include "http_log.h"
77 #include "http_main.h"
78
79 #ifdef APR_HAVE_STDARG_H
80 #include <stdarg.h>
81 #endif
82 #ifdef HAVE_STRINGS_H
83 #include <strings.h>
84 #endif
85 #ifdef HAVE_UNISTD_H
86 #include <unistd.h>
87 #endif
88
89 typedef struct {
90         char    *t_name;
91         int     t_val;
92 } TRANS;
93
94 #ifdef HAVE_SYSLOG
95
96 static const TRANS facilities[] = {
97     {"auth",    LOG_AUTH},
98 #ifdef LOG_AUTHPRIV
99     {"authpriv",LOG_AUTHPRIV},
100 #endif
101 #ifdef LOG_CRON
102     {"cron",    LOG_CRON},
103 #endif
104 #ifdef LOG_DAEMON
105     {"daemon",  LOG_DAEMON},
106 #endif
107 #ifdef LOG_FTP
108     {"ftp",     LOG_FTP},
109 #endif
110 #ifdef LOG_KERN
111     {"kern",    LOG_KERN},
112 #endif
113 #ifdef LOG_LPR
114     {"lpr",     LOG_LPR},
115 #endif
116 #ifdef LOG_MAIL
117     {"mail",    LOG_MAIL},
118 #endif
119 #ifdef LOG_NEWS
120     {"news",    LOG_NEWS},
121 #endif
122 #ifdef LOG_SYSLOG
123     {"syslog",  LOG_SYSLOG},
124 #endif
125 #ifdef LOG_USER
126     {"user",    LOG_USER},
127 #endif
128 #ifdef LOG_UUCP
129     {"uucp",    LOG_UUCP},
130 #endif
131 #ifdef LOG_LOCAL0
132     {"local0",  LOG_LOCAL0},
133 #endif
134 #ifdef LOG_LOCAL1
135     {"local1",  LOG_LOCAL1},
136 #endif
137 #ifdef LOG_LOCAL2
138     {"local2",  LOG_LOCAL2},
139 #endif
140 #ifdef LOG_LOCAL3
141     {"local3",  LOG_LOCAL3},
142 #endif
143 #ifdef LOG_LOCAL4
144     {"local4",  LOG_LOCAL4},
145 #endif
146 #ifdef LOG_LOCAL5
147     {"local5",  LOG_LOCAL5},
148 #endif
149 #ifdef LOG_LOCAL6
150     {"local6",  LOG_LOCAL6},
151 #endif
152 #ifdef LOG_LOCAL7
153     {"local7",  LOG_LOCAL7},
154 #endif
155     {NULL,              -1},
156 };
157 #endif
158
159 static const TRANS priorities[] = {
160     {"emerg",   APLOG_EMERG},
161     {"alert",   APLOG_ALERT},
162     {"crit",    APLOG_CRIT},
163     {"error",   APLOG_ERR},
164     {"warn",    APLOG_WARNING},
165     {"notice",  APLOG_NOTICE},
166     {"info",    APLOG_INFO},
167     {"debug",   APLOG_DEBUG},
168     {NULL,      -1},
169 };
170
171 static int log_child(apr_pool_t *p, const char *progname,
172                      apr_file_t **fpin)
173 {
174     /* Child process code for 'ErrorLog "|..."';
175      * may want a common framework for this, since I expect it will
176      * be common for other foo-loggers to want this sort of thing...
177      */
178     int rc = -1;
179     apr_procattr_t *procattr;
180     apr_proc_t *procnew;
181
182 #ifdef SIGHUP
183     /* No concept of a child process on Win32 */
184     apr_signal(SIGHUP, SIG_IGN);
185 #endif /* ndef SIGHUP */
186
187     if ((apr_createprocattr_init(&procattr, p) != APR_SUCCESS) ||
188         (apr_setprocattr_io(procattr,
189                            APR_FULL_BLOCK,
190                            APR_NO_PIPE,
191                            APR_NO_PIPE) != APR_SUCCESS)) {
192         /* Something bad happened, give up and go away. */
193         rc = -1;
194     }
195     else {
196         char **args;
197         const char *pname;
198         
199         apr_tokenize_to_argv(progname, &args, p);
200         pname = apr_pstrdup(p, args[0]);
201         procnew = (apr_proc_t *) apr_palloc(p, sizeof(*procnew));
202         rc = apr_create_process(procnew, pname, args, NULL, procattr, p);
203     
204         if (rc == APR_SUCCESS) {
205             apr_note_subprocess(p, procnew, kill_after_timeout);
206             (*fpin) = procnew->in;
207         }
208     }
209
210     return(rc);
211 }
212
213 static void open_error_log(server_rec *s, apr_pool_t *p)
214 {
215     const char *fname;
216     int rc;
217
218     if (*s->error_fname == '|') {
219         apr_file_t *dummy = NULL;
220
221         /* This starts a new process... */
222         rc = log_child (p, s->error_fname+1, &dummy);
223         if (rc != APR_SUCCESS) {
224             perror("ap_spawn_child");
225             ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
226                          "Couldn't fork child for ErrorLog process");
227                          exit(1);
228         }
229
230         s->error_log = dummy;
231     }
232
233 #ifdef HAVE_SYSLOG
234     else if (!strncasecmp(s->error_fname, "syslog", 6)) {
235         if ((fname = strchr(s->error_fname, ':'))) {
236             const TRANS *fac;
237
238             fname++;
239             for (fac = facilities; fac->t_name; fac++) {
240                 if (!strcasecmp(fname, fac->t_name)) {
241                     openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID,
242                             fac->t_val);
243                     s->error_log = NULL;
244                     return;
245                 }
246             }
247         }
248         else
249             openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID, LOG_LOCAL7);
250
251         s->error_log = NULL;
252     }
253 #endif
254     else {
255         fname = ap_server_root_relative(p, s->error_fname);
256         /*  Change to AP funcs. */
257         if (apr_open(&s->error_log, fname, APR_APPEND | 
258                     APR_READ | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, p) != APR_SUCCESS) {
259             perror("fopen");
260             ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
261                          "%s: could not open error log file %s.",
262                          ap_server_argv0, fname);
263             exit(1);
264         }
265     }
266 }
267
268 void ap_open_logs(server_rec *s_main, apr_pool_t *p)
269 {
270     apr_status_t rc = APR_SUCCESS;
271     server_rec *virt, *q;
272     int replace_stderr;
273     apr_file_t *errfile = NULL;
274
275     open_error_log(s_main, p);
276
277     replace_stderr = 1;
278     if (s_main->error_log) {
279         /* replace stderr with this new log */
280         apr_flush(s_main->error_log);
281         apr_open_stderr(&errfile, p);        
282         if ((rc = apr_dupfile(&errfile, s_main->error_log, NULL)) != APR_SUCCESS) {
283             ap_log_error(APLOG_MARK, APLOG_CRIT, rc, s_main,
284                          "unable to replace stderr with error_log");
285         } else {
286             replace_stderr = 0;
287         }
288     }
289     /* note that stderr may still need to be replaced with something
290      * because it points to the old error log, or back to the tty
291      * of the submitter.
292      */
293     if (replace_stderr && freopen("/dev/null", "w", stderr) == NULL) {
294         ap_log_error(APLOG_MARK, APLOG_CRIT, errno, s_main,
295             "unable to replace stderr with /dev/null");
296     }
297
298     for (virt = s_main->next; virt; virt = virt->next) {
299         if (virt->error_fname) {
300             for (q=s_main; q != virt; q = q->next)
301                 if (q->error_fname != NULL &&
302                     strcmp(q->error_fname, virt->error_fname) == 0)
303                     break;
304             if (q == virt)
305                 open_error_log(virt, p);
306             else 
307                 virt->error_log = q->error_log;
308         }
309         else
310             virt->error_log = s_main->error_log;
311     }
312 }
313
314 API_EXPORT(void) ap_error_log2stderr(server_rec *s) {
315     apr_file_t *errfile = NULL;
316
317     apr_open_stderr(&errfile, s->process->pool);        
318     if (s->error_log != NULL) {
319         apr_dupfile(&(s->error_log), errfile, s->process->pool);
320     }
321 }
322
323 static void log_error_core(const char *file, int line, int level, 
324                            apr_status_t status, const server_rec *s, 
325                            const request_rec *r, apr_pool_t *pool,
326                            const char *fmt, va_list args)
327 {
328     char errstr[MAX_STRING_LEN];
329     size_t len;
330     apr_file_t *logf = NULL;
331
332     if (s == NULL) {
333         /*
334          * If we are doing stderr logging (startup), don't log messages that are
335          * above the default server log level unless it is a startup/shutdown
336          * notice
337          */
338         if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) &&
339             ((level & APLOG_LEVELMASK) > DEFAULT_LOGLEVEL))
340             return;
341         apr_open_stderr(&logf, NULL);
342     }
343     else if (s->error_log) {
344         /*
345          * If we are doing normal logging, don't log messages that are
346          * above the server log level unless it is a startup/shutdown notice
347          */
348         if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) && 
349             ((level & APLOG_LEVELMASK) > s->loglevel))
350             return;
351         logf = s->error_log;
352     }
353 #ifdef TPF
354     else if (tpf_child) {
355     /*
356      * If we are doing normal logging, don't log messages that are
357      * above the server log level unless it is a startup/shutdown notice
358      */
359     if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) &&
360         ((level & APLOG_LEVELMASK) > s->loglevel))
361         return;
362     logf = stderr;
363     }
364 #endif /* TPF */
365     else {
366         /*
367          * If we are doing syslog logging, don't log messages that are
368          * above the server log level (including a startup/shutdown notice)
369          */
370         if ((level & APLOG_LEVELMASK) > s->loglevel)
371             return;
372         logf = NULL;
373     }
374
375     if (logf && ((level & APLOG_STARTUP) != APLOG_STARTUP)) {
376         errstr[0] = '[';
377         apr_ctime(errstr + 1, apr_now());
378         errstr[1 + APR_CTIME_LEN - 1] = ']';
379         errstr[1 + APR_CTIME_LEN    ] = ' ';
380         len = 1 + APR_CTIME_LEN + 1;
381     } else {
382         len = 0;
383     }
384
385     if ((level & APLOG_STARTUP) != APLOG_STARTUP) {
386         len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
387                 "[%s] ", priorities[level & APLOG_LEVELMASK].t_name);
388     }
389 #ifndef TPF
390     if (file && (level & APLOG_LEVELMASK) == APLOG_DEBUG) {
391 #ifdef _OSD_POSIX
392         char tmp[256];
393         char *e = strrchr(file, '/');
394
395         /* In OSD/POSIX, the compiler returns for __FILE__
396          * a string like: __FILE__="*POSIX(/usr/include/stdio.h)"
397          * (it even returns an absolute path for sources in
398          * the current directory). Here we try to strip this
399          * down to the basename.
400          */
401         if (e != NULL && e[1] != '\0') {
402             apr_snprintf(tmp, sizeof(tmp), "%s", &e[1]);
403             e = &tmp[strlen(tmp)-1];
404             if (*e == ')')
405                 *e = '\0';
406             file = tmp;
407         }
408 #endif /*_OSD_POSIX*/
409         len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
410                 "%s(%d): ", file, line);
411     }
412 #endif /* TPF */
413     if (r && r->connection) {
414         /* XXX: TODO: add a method of selecting whether logged client
415          * addresses are in dotted quad or resolved form... dotted
416          * quad is the most secure, which is why I'm implementing it
417          * first. -djg
418          */
419         len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
420                 "[client %s] ", r->connection->remote_ip);
421     }
422     if (!(level & APLOG_NOERRNO)
423         && (status != 0)) {
424         char buf[120];
425         len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
426                 "(%d)%s: ", status, apr_strerror(status, buf, sizeof(buf)));
427     }
428
429     len += apr_vsnprintf(errstr + len, MAX_STRING_LEN - len, fmt, args);
430
431     /* NULL if we are logging to syslog */
432     if (logf) {
433         /* Truncate for the terminator (as apr_snprintf does) */
434         if (len > MAX_STRING_LEN - sizeof(APR_EOL_STR))
435             len = MAX_STRING_LEN - sizeof(APR_EOL_STR);
436         strcpy(errstr + len, APR_EOL_STR);
437         apr_puts(errstr, logf);
438         apr_flush(logf);
439     }
440 #ifdef HAVE_SYSLOG
441     else {
442         syslog(level & APLOG_LEVELMASK, "%s", errstr);
443     }
444 #endif
445 }
446     
447 API_EXPORT(void) ap_log_error(const char *file, int line, int level,
448                               apr_status_t status, const server_rec *s, 
449                               const char *fmt, ...)
450 {
451     va_list args;
452
453     va_start(args, fmt);
454     log_error_core(file, line, level, status, s, NULL, NULL, fmt, args);
455     va_end(args);
456 }
457
458 API_EXPORT(void) ap_log_perror(const char *file, int line, int level,
459                               apr_status_t status, apr_pool_t *p, 
460                               const char *fmt, ...)
461 {
462     va_list args;
463
464     va_start(args, fmt);
465     log_error_core(file, line, level, status, NULL, NULL, p, fmt, args);
466     va_end(args);
467 }
468
469 API_EXPORT(void) ap_log_rerror(const char *file, int line, int level,
470                                apr_status_t status, const request_rec *r, 
471                                const char *fmt, ...)
472 {
473     va_list args;
474
475     va_start(args, fmt);
476     log_error_core(file, line, level, status, r->server, r, NULL, fmt, args);
477     /*
478      * IF the error level is 'warning' or more severe,
479      * AND there isn't already error text associated with this request,
480      * THEN make the message text available to ErrorDocument and
481      * other error processors.  This can be disabled by stuffing
482      * something, even an empty string, into the "error-notes" cell
483      * before calling this routine.
484      */
485     va_end(args);
486     va_start(args,fmt); 
487     if (((level & APLOG_LEVELMASK) <= APLOG_WARNING)
488         && (apr_table_get(r->notes, "error-notes") == NULL)) {
489         apr_table_setn(r->notes, "error-notes",
490                       ap_escape_html(r->pool, apr_pvsprintf(r->pool, fmt, 
491                       args)));
492     }
493     va_end(args);
494 }
495
496 void ap_log_pid(apr_pool_t *p, const char *fname)
497 {
498     apr_file_t *pid_file = NULL;
499     apr_finfo_t finfo;
500     static pid_t saved_pid = -1;
501     pid_t mypid;
502     apr_status_t rv;
503
504     if (!fname) 
505         return;
506
507     fname = ap_server_root_relative(p, fname);
508     mypid = getpid();
509     if (mypid != saved_pid && apr_stat(&finfo, fname, p) == APR_SUCCESS) {
510       /* WINCH and HUP call this on each restart.
511        * Only warn on first time through for this pid.
512        *
513        * XXX: Could just write first time through too, although
514        *      that may screw up scripts written to do something
515        *      based on the last modification time of the pid file.
516        */
517         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, NULL,
518                      apr_psprintf(p,
519                                  "pid file %s overwritten -- Unclean shutdown of previous Apache run?",
520                      fname)
521                                );
522     }
523
524     if ((rv = apr_open(&pid_file, fname, APR_WRITE | APR_CREATE | APR_TRUNCATE,
525                       APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD, p)) != APR_SUCCESS) {
526         ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, 
527                      "could not create %s", fname);
528         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, NULL, 
529                      "%s: could not log pid to file %s",
530                      ap_server_argv0, fname);
531         exit(1);
532     }
533     apr_fprintf(pid_file, "%ld" APR_EOL_STR, (long)mypid);
534     apr_close(pid_file);
535     saved_pid = mypid;
536 }
537
538 API_EXPORT(void) ap_log_assert(const char *szExp, const char *szFile, int nLine)
539 {
540     char time_str[APR_CTIME_LEN];
541
542     apr_ctime(time_str, apr_now());
543     ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
544                  "[%s] file %s, line %d, assertion \"%s\" failed",
545                  time_str, szFile, nLine, szExp);
546 #ifndef WIN32
547     /* unix assert does an abort leading to a core dump */
548     abort();
549 #else
550     exit(1);
551 #endif
552 }
553
554 /* piped log support */
555
556 #ifdef HAVE_RELIABLE_PIPED_LOGS
557 /* forward declaration */
558 static void piped_log_maintenance(int reason, void *data, apr_wait_t status);
559
560 static int piped_log_spawn(piped_log *pl)
561 {
562     int rc;
563     apr_procattr_t *procattr;
564     apr_proc_t *procnew;
565     apr_status_t status;
566
567 #ifdef SIGHUP
568     apr_signal(SIGHUP, SIG_IGN);
569 #endif
570     if (((status = apr_createprocattr_init(&procattr, pl->p)) != APR_SUCCESS) ||
571         ((status = apr_setprocattr_childin(procattr, ap_piped_log_read_fd(pl), 
572                                 ap_piped_log_write_fd(pl)))  != APR_SUCCESS)) {
573         char buf[120];
574         /* Something bad happened, give up and go away. */
575         ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
576             "piped_log_spawn: unable to setup child process '%s': %s",
577             pl->program, apr_strerror(status, buf, sizeof(buf)));
578         rc = -1;
579     }
580     else {
581         char **args;
582         const char *pname;
583
584         apr_tokenize_to_argv(pl->program, &args, pl->p);
585         pname = apr_pstrdup(pl->p, args[0]);
586         procnew = (apr_proc_t *) apr_palloc(pl->p, sizeof(*procnew));
587         rc = apr_create_process(procnew, pname, args, NULL, procattr, pl->p);
588     
589         if (rc == APR_SUCCESS) {            
590             /* pjr - This no longer happens inside the child, */
591             /*   I am assuming that if apr_create_process was  */
592             /*   successful that the child is running.        */
593             RAISE_SIGSTOP(PIPED_LOG_SPAWN); 
594             pl->pid = procnew;
595             ap_piped_log_write_fd(pl) = procnew->in;
596             apr_register_other_child(procnew, piped_log_maintenance, pl, 
597                                     ap_piped_log_write_fd(pl), pl->p);
598         }
599     }
600     
601     return 0;
602 }
603
604
605 static void piped_log_maintenance(int reason, void *data, apr_wait_t status)
606 {
607     piped_log *pl = data;
608     apr_status_t stats;
609
610     switch (reason) {
611     case APR_OC_REASON_DEATH:
612         pl->pid = NULL;
613         apr_unregister_other_child(pl);
614         if (pl->program == NULL) {
615             /* during a restart */
616             break;
617         }
618         break;
619     case APR_OC_REASON_LOST:
620         pl->pid = NULL;
621         apr_unregister_other_child(pl);
622         if (pl->program == NULL) {
623             /* during a restart */
624             break;
625         }
626         if ((stats = piped_log_spawn(pl)) != APR_SUCCESS) {
627             /* what can we do?  This could be the error log we're having
628              * problems opening up... */
629             char buf[120];
630             ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
631                 "piped_log_maintenance: unable to respawn '%s': %s",
632                 pl->program, apr_strerror(stats, buf, sizeof(buf)));
633         }
634         break;
635     
636     case APR_OC_REASON_UNWRITABLE:
637         if (pl->pid != NULL) {
638             apr_kill(pl->pid, SIGTERM);
639         }
640         break;
641     
642     case APR_OC_REASON_RESTART:
643         pl->program = NULL;
644         if (pl->pid != NULL) {
645             apr_kill(pl->pid, SIGTERM);
646         }
647         break;
648
649     case APR_OC_REASON_UNREGISTER:
650         break;
651     }
652 }
653
654
655 static apr_status_t piped_log_cleanup(void *data)
656 {
657     piped_log *pl = data;
658
659     if (pl->pid != NULL) {
660         apr_kill(pl->pid, SIGTERM);
661     }
662     apr_unregister_other_child(pl);
663     apr_close(ap_piped_log_read_fd(pl));
664     apr_close(ap_piped_log_write_fd(pl));
665     return APR_SUCCESS;
666 }
667
668
669 static apr_status_t piped_log_cleanup_for_exec(void *data)
670 {
671     piped_log *pl = data;
672
673     apr_close(ap_piped_log_read_fd(pl));
674     apr_close(ap_piped_log_write_fd(pl));
675     return APR_SUCCESS;
676 }
677
678 API_EXPORT(piped_log *) ap_open_piped_log(apr_pool_t *p, const char *program)
679 {
680     piped_log *pl;
681
682     pl = apr_palloc(p, sizeof (*pl));
683     pl->p = p;
684     pl->program = apr_pstrdup(p, program);
685     pl->pid = NULL;
686     if (apr_create_pipe(&ap_piped_log_read_fd(pl), &ap_piped_log_write_fd(pl), p) != APR_SUCCESS) {
687         return NULL;
688     }
689     apr_register_cleanup(p, pl, piped_log_cleanup, piped_log_cleanup_for_exec);
690     if (piped_log_spawn(pl) == -1) {
691         int save_errno = errno;
692         apr_kill_cleanup(p, pl, piped_log_cleanup);
693         apr_close(ap_piped_log_read_fd(pl));
694         apr_close(ap_piped_log_write_fd(pl));
695         errno = save_errno;
696         return NULL;
697     }
698     return pl;
699 }
700
701 #else
702
703 static apr_status_t piped_log_cleanup(void *data)
704 {
705     piped_log *pl = data;
706
707     apr_close(ap_piped_log_write_fd(pl));
708     return APR_SUCCESS;
709 }
710
711 API_EXPORT(piped_log *) ap_open_piped_log(apr_pool_t *p, const char *program)
712 {
713     piped_log *pl;
714     apr_file_t *dummy = NULL;
715     int rc;
716
717     rc = log_child(p, program, &dummy);
718     if (rc != APR_SUCCESS) {
719         perror("ap_spawn_child");
720         ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
721                      "Couldn't fork child for piped log process");
722         exit (1);
723     }
724
725     pl = apr_palloc(p, sizeof (*pl));
726     pl->p = p;
727     ap_piped_log_read_fd(pl) = NULL;
728     ap_piped_log_write_fd(pl) = dummy;
729     apr_register_cleanup(p, pl, piped_log_cleanup, piped_log_cleanup);
730
731     return pl;
732 }
733
734 #endif
735
736 API_EXPORT(void) ap_close_piped_log(piped_log *pl)
737 {
738     apr_run_cleanup(pl->p, pl, piped_log_cleanup);
739 }
740