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