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