/ * Copyright 1999-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License");. * You may not use this file except in compliance with the License * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDitions of any Kind, Either Express or Implied. * See The license for the specificirate governing permissions and * limiteds under the license. * /
/ * * Http_log.c: dealing with the logs and errors * * Rob McCool * * /
#include "apr.h" #include "apr_general.h" / * for signal stuff * / # include "APR_Strings.h" #include "apr_rrno.h" #include "APR_THREAD_PROC.H" #include "APR_LIB.H" # Include "APR_SIGNAL.H"
#define apr_want_stdio # define apr_want_strfunc # include "APR_WANT.H"
#if APR_HAVE_STDARG_H # include
#define core_private
#include "ap_config.h" #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_main.h" #include "util_time.h" # INCLUDE "ap_mpm.h"
Typedef struct {char * t_name; int t_val;}
APR_HOOK_STRUCT (APR_HOOK_LINK (ERROR_LOG))
INT AP_DECLARE_DATA AP_DEFAULT_LOGLEVEL = Default_Loglevel;
#ifdef Have_syslog
static const TRANS facilities [] = {{ "auth", LOG_AUTH}, # ifdef LOG_AUTHPRIV { "authpriv", LOG_AUTHPRIV}, # endif # ifdef LOG_CRON { "cron", LOG_CRON}, # endif # ifdef LOG_DAEMON { "daemon", LOG_DAEMON}, # endif # ifdef LOG_FTP { "ftp", LOG_FTP}, # endif # ifdef LOG_KERN { "kern", LOG_KERN}, # endif # ifdef LOG_LPR { "lpr", LOG_LPR}, # endif # ifdef LOG_MAIL { "mail ", LOG_MAIL}, # endif # ifdef LOG_NEWS {" news ", LOG_NEWS}, # endif # ifdef LOG_SYSLOG {" syslog ", LOG_SYSLOG}, # endif # ifdef LOG_USER {" user ", LOG_USER}, # endif # ifdef LOG_UUCP { "uucp", LOG_UUCP}, # endif # ifdef LOG_LOCAL0 { "local0", LOG_LOCAL0}, # endif # ifdef LOG_LOCAL1 { "local1", LOG_LOCAL1}, # endif # ifdef LOG_LOCAL2 { "local2", LOG_LOCAL2}, # endif # ifdef LOG_LOCAL3 { "local3", LOG_LOCAL3}, # endif # ifdef LOG_LOCAL4 { "local4", LOG_LOCAL4}, # endif # ifdef LOG_LOCAL5 { "local5", LOG_LOCAL5}, # endif # ifdef LOG_LOCAL6 { "local6", LOG_LOCAL6}, # endif #ifdef log_local7 {"local7", log_local7}, # Endif {Null, -1},}; # Endifstatic constrars [] = {{"Emerg", APLOG_EMERG}, {"Alert", APLOG_ALERT}, {"crit", aplog_crit}, {"error", aplog_err}, {"warn ", APLOG_WARNING}, {" notice ", aplog_notice}, {" info ", aplog_info}, {" debug ", aplog_debug}, {null, -1},};
Static apr_file_t * stderr_log = null;
AP_DECLARE (VOID) AP_OPEN_STDERR_LOG (APR_POOL_T * P) {APR_FILE_OPEN_STDERR (& stderr_log, p);}
AP_DECLARE (apr_status_t) ap_replace_stderr_log (apr_pool_t * p, const char * fname) {apr_file_t * stderr_file; apr_status_t rc; char * filename = ap_server_root_relative (p, fname); if (! Filename) {ap_log_error (APLOG_MARK, APLOG_STARTUP | APLOG_CRIT, APR_EBADPATH, NULL, "Invalid -E error log file% s", fname); return APR_EBADPATH;!} if ((rc = apr_file_open (& stderr_file, filename, APR_APPEND | APR_READ | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, p)) = APR_SUCCESS) {ap_log_error (APLOG_MARK, APLOG_STARTUP, RC, NULL, "% s: Could Not Orror Log File% s.", AP_SERVER_ARGV0, FNAME); RETURN RC;} IF ((RC = APR_FILE_OPEN_STDERR (& stderr_log, p)) == APR_SUCCESS { APR_FILE_FLUSH (stderr_log); if ((RC = APR_FILE_DUP2 (stderr_log, stderr_file, p)) == APR_SUCCESS {Apr_file_close (stderr_file);}} if (! Rc = APR_SUCCESS) {ap_log_error (APLOG_MARK, APLOG_CRIT, rc, NULL, "unable to replace stderr with error_log");} return rc;} static void log_child_errfn (apr_pool_t * pool, apr_status_t Err, Const char * description) {ap_log_error (aplog_mark, aplog_err, err, null, "% s", description);}
Static int log_child (APR_POOL_T * P, Const Char * ProgName, APR_FILE_T ** FPIN) {/ * Child Process Code for 'ErrorLog "| ..."; * May Want A Common Framework for this, Since I Expect It Will * be common for other foo-loggers to want this sort of thing ... * / apr_status_t rc; apr_procattr_t * procattr; apr_proc_t * procnew; if (((rc = apr_procattr_create (& procattr, p)) == APR_SUCCESS) && ((rc = apr_procattr_io_set (procattr, APR_FULL_BLOCK, APR_NO_PIPE, APR_NO_PIPE)) == APR_SUCCESS) && ((rc = apr_procattr_error_check_set (procattr, 1)) == APR_SUCCESS) && ((rc = apr_procattr_child_errfn_set (procattr, log_child_errfn)) == APR_SUCCESS)) { Char ** args; const char * pname;
apr_tokenize_to_argv (progname, & args, p); pname = apr_pstrdup (p, args [0]); procnew = (apr_proc_t *) apr_pcalloc (p, sizeof (* procnew)); rc = apr_proc_create (procnew, pname, (const char * Const *) Args, NULL, Procattr, P);
IF (rc == APR_SUCCESS) {APR_POOL_NOTE_SUBPROCESS (P, Procnew, Apr_kill_AFTER_TIMEOUT); (* fpin) = procnew-> in;}}
Return rc;}
Static int open_error_log (Server_Rec * S, APR_POOL_T * P) {const char * fname; int RC;
IF (* S-> Error_FNAME == '|') {APR_FILE_T * DUMMY = NULL;
/ * This Starts a new process ... * / rc = log_child (p, s-> error_fname 1, & demmy); if (rc! = APR_SUCCESS) {AP_LOG_ERROR (APLOG_MARK, APLOG_STARTUP, RC, NULL, "COULDN ' Start ErrorLog Process "); Return Done;} S-> Error_Log = Dummy;}
#ifdef Have_syslog else if (! StrncaseCMP (S-> Error_FName, "Syslog", 6)) {IF ((FNAME = Strchr (S-> Error_FName, ':'))))) {Const Trans * FAC;
FNAME ; for (FAC = Facilities; Fac-> T_Name; FAC ) {if (! StrcaseCMP (FAC-> T_NAME)) {OpenLog (ap_server_argv0, log_ndlay | log_cons | log_pid, FAC-> T_VAL); S-> Error_log = NULL; RETURN OK;}}} else {OpenLog (ap_server_argv0, log_ndlay | log_cons | log_pid, log_local7);
s-> error_log = NULL;} #endif else {fname = ap_server_root_relative (p, s-> error_fname); if (! fname) {ap_log_error (APLOG_MARK, APLOG_STARTUP, APR_EBADPATH, NULL, "% s: Invalid error log path% s . ", ap_server_argv0, s-> error_fname); return DONE;!} if ((rc = apr_file_open (& s-> error_log, fname, APR_APPEND | APR_READ | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, p)) = APR_SUCCESS) {ap_log_error (APLOG_MARK , APLOG_STARTUP, RC, NULL, "% S: Could Not Open Error Log File% s.", AP_SERVER_ARGV0, FNAME); Return Done;}} Return OK;
int ap_open_logs (apr_pool_t * pconf, apr_pool_t * p / * plog * /, apr_pool_t * ptemp, server_rec * s_main) {apr_status_t rc = APR_SUCCESS; server_rec * virt, * q; int replace_stderr; apr_file_t * errfile = NULL;
IF (open_error_log (s_main, p)! = ok) {Return Done;}
replace_stderr = 1; if (s_main-> error_log) {/ * replace stderr with this new log * / apr_file_flush (s_main-> error_log); if ((rc = apr_file_open_stderr (& errfile, p)) == APR_SUCCESS) {rc = apr_file_dup2 (errfile, s_main-> error_log, p);} if (! rc = APR_SUCCESS) {ap_log_error (APLOG_MARK, APLOG_CRIT, rc, s_main, "unable to replace stderr with error_log");} else {replace_stderr = 0;}} / * Note That stderr may still need to be replaced with something * because it points to the old error log, or back to the TTY * of the submitter. * xxx: this is bs - / dev / null is non-portable * / if (Replace_stderr &&freopen ("/ dev / null", "w", stderr) == NULL) {AP_LOG_ERROR (APLOG_MARK, APLOG_CRIT, Errno, S_Main, "Unable to replace stderr with / dev / null);} for (Virt = S_MAIN-> Next; Virt; Virt = Virt-> Next) {if (Virt-> Error_FNAME) { For (q = s_main; q! = Virt; q = q-> next) {if (q-> error_fname! = Null && strcmp (q-> error_fname, virt-> error_fname) == 0) {Break;}}
IF (q == Virt) {if (Open_ERROR_LOG (Virt, P)! = OK) {Return Done;}}} else {virt-> error_log = q-> error_log;}} else {virt-> error_log = s_main-> Error_log;}}} Return OK;
AP_DECLARE (void) ap_error_log2stderr (server_rec * s) {apr_file_t * errfile = NULL; apr_file_open_stderr (& errfile, s-> process-> pool); if (! S-> error_log = NULL) {apr_file_dup2 (s-> error_log, errfile, S-> Process-> pool);}}
static void log_error_core (const char * file, int line, int level, apr_status_t status, const server_rec * s, const request_rec * r, apr_pool_t * pool, const char * fmt, va_list args) {char errstr [MAX_STRING_LEN]; # ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED Char scratch [MAX_STRING_LEN]; # endif APR_SIZE_T LEN, Errstrlen; Apr_File_t * logf = null; const char * refere; int level_and_mask = level & aplog_levelmask;
IF (s == null) {/ * * if we are doing stderr logging (startup), don't log messages That area * Above the default server log level unlessN * notice * / if ((Level_and_mask) ! = APLOG_NOTICE) && (level_and_mask> ap_default_loglevel)) {RETURN;}
Logf = stderr_log;} else if (s-> error_log) {/ * * if we are doing normal logging, don't log message, don't log Messages That Are * Above the server log level unlessN notice * / if (( Level_and_mask! = aplog_notice) && (level_and_mask> s-> loglevel)) {Return;}
Logf = S-> Error_log;} #ifdef TPF Else IF (TPF_CHILD) {/ * * i we are doing normal logging, don't log messages That Are * Above the Server log level unless it is a startup / shutdown notice * / IF ((Level_And_Mask! = APLOG_NOTICE) && (Level_and_mask> S-> Loglevel)) {Return;} logf = stderr;} #ENDIF / * TPF * / ELSE {/ * * if We are doing syslog logging, Don't log Messages That Are * Above The Server Log Level (Include) * / if (Level_and_mask> S-> Loglevel) {Return;}}
IF (Logf & APLOG_STARTUP)! = aplog_startup) {Errstr [0] = '['; ap_recent_ctime (errstr 1, APR_TIME_NOW ()); Errstr [1 APR_CTIME_LEN - 1] = ']'; Errstr [1 APR_CTIME_LEN] = '; len = 1 APR_CTIME_LEN 1;} else {len = 0;}
IF ((Level & APLOG_STARTUP)! = APLOG_STARTUP) {LEN = APR_SNPRINTF (Errstr LEN, MAX_STRING_LEN - LEN, "[% s]", priorities [level_and_mask] .t_name);}
#ifndef tpf if (file && level_and_mask == aplog_debug) {#if Defined (_SD_POSIX) || Defined (Win32) Char TMP [256]; char * E = Strrchr (file, '/'); # ifdef Win32 IF (! e) {E = Strrchr (file, '//');} #ENDIF
/ * In OSD / POSIX, The Compiler Returns for __File__ * a string Like: __file __ = "* POSIX (/usr/include/stdio.h)" * (It Even Returns An Absolute Path for Sources In * The Current Directory). Here We try to strip this * Down to the basename. * / If (e! = Null && e [1]! = '/ 0') {APR_SnPrintf (TMP, SIZEOF (TMP), "% S", & E [1 ]); E = & TMP [Strlen (TMP) -1]; if (* e == ')') {* e = '/ 0';} file = TMP;} #ENDIF / * _ osd_posix * / len = APR_SNPRINTF (Errstr LEN, MAX_STRING_LEN - LEN, "% s (% d):", ",} #ENDIF / * TPF * /
IF (R && r-> connection) {/ * xxx: Todo: Add a method of selecting WHETHER logged client * addresses are in dotted quad or resolved form ... dotted * quad is the most moture, Which is why i'm Implementing it * first. -djg * / len = APR_SNPRINTF (Errstr LEN, MAX_STRING_LEN - LEN, "[Client% S]", R-> Connection-> Remote_ip);}} (status! = 0) {IF status
errstrlen = len; #ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED if (apr_vsnprintf (scratch, MAX_STRING_LEN - len, fmt, args)) {len = ap_escape_errorlog_item (errstr len, scratch, MAX_STRING_LEN - len);} #else len = apr_vsnprintf (errstr len , MAX_STRING_LEN - len, fmt, args); # endifif (r && (referer = apr_table_get (r-> headers_in, "Referer")) # ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED && ap_escape_errorlog_item (scratch, referer, MAX_STRING_LEN - len) #endif) {len = APR_SNPRINTF (Errstr LEN, MAX_STRING_LEN - LEN, ", Referr:% S", # isef ap_unsafe_error_log_unescaped scratch # else refere # Endif);}
/ * NULL if we are logging to syslog * / if (logf) {/ * Truncate for the terminator (as apr_snprintf does) * / if (len> MAX_STRING_LEN - sizeof (APR_EOL_STR)) {len = MAX_STRING_LEN - sizeof (APR_EOL_STR); } STRCPY (Errstr LEN, APR_EOL_STR); APR_FILE_PUTS (Errstr, Logf); APR_FILE_FLUSH (LOGF);} #ifdef Have_syslog else {syslog (Level_and_mask, "% s", errstr);} #ENDIF
AP_RUN_ERROR_LOG (File, Line, Level, Status, S, R, Pool, Errstr Errstrlen);
AP_DECLARE (VOID) AP_LOG_ERROR (Const Char * file, int line, int level, APR_STATUS_T STATUS, Const Server_Rec * S, Const Char * fmt, ...) {va_list args;
Va_start (args, fmt); log_error_core (file, line, level, status, s, null, null, fmt, args); VA_END (ARGS);
AP_DECLARE (void) ap_log_perror (const char * file, int line, int level, apr_status_t status, apr_pool_t * p, const char * fmt, ...) {va_list args; va_start (args, fmt); log_error_core (file, line, Level, Status, NULL, NULL, P, FMT, ARGS; VA_END (ARGS);
AP_DECLARE (VOID) AP_LOG_RERROR (Const Char * file, int line, int level, APR_STATUS_T STATUS, Const Request_Rec * R, Const Char * fmt, ...) {VA_LIST ARGS;
Va_start (args, fmt); log_error_core (file, line, level, status, r-> server, r, null, fmt, args);
/ * * IF APLOG_TOCLIENT is set, * AND the error level is 'warning' or more severe, * AND there is not already error text associated with this request, * THEN make the message text available to ErrorDocument and * other error processors. * / VA_END (ARGS); VA_START (ARGS, FMT); IF (Level & APLOG_TOCLIENT) && ((Level & APLOG_LEVELMASK) <= aplog_warning) && (APR_TABLE_GET (R-> Notes, "Error-Notes") == NULL ))) {APR_TABLE_SETN (R-> Notes, "Error-Notes", AP_ESCAPE_HTML (R-> Pool, APVSPRINTF (R-> Pool, FMT, Args));} VA_END (ARGS);
AP_DECLARE (void) ap_log_pid (apr_pool_t * p, const char * filename) {apr_file_t * pid_file = NULL; apr_finfo_t finfo; static pid_t saved_pid = -1; pid_t mypid; apr_status_t rv; const char * fname;
IF (! filename) {return;}
fname = ap_server_root_relative (p, filename); if (! fname) {ap_log_error (APLOG_MARK, APLOG_STARTUP | APLOG_CRIT, APR_EBADPATH, NULL, "Invalid PID file path% s, ignoring.", filename); return;} mypid = getpid () ; if (mypid = saved_pid && apr_stat (& finfo, fname, APR_FINFO_MTIME, p) == APR_SUCCESS!) {/ * AP_SIG_GRACEFUL and HUP call this on each restart * Only warn on first time through for this pid * * XXX:.. Could just write first time through too, although * that may screw up scripts written to do something * based on the last modification time of the pid file. * / ap_log_perror (APLOG_MARK, APLOG_WARNING, 0, p, apr_psprintf (p, "pid file% S Overwritten - Unclean "" Shutdown of Previous Apache Run? ", fname);
if ((rv = apr_file_open (& pid_file, fname, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_UREAD | APR_UWRITE | APR_GREAD |! APR_WREAD, p)) = APR_SUCCESS) {ap_log_error (APLOG_MARK, APLOG_ERR, rv, NULL, "could not create% s" , fname); AP_LOG_ERROR (APLOG_MARK, APLOG_ERR, 0, NULL, "% S: Could Not Log Pid to File% S", AP_SERVER_ARGV0, FNAME); EXIT (1);} APR_FILE_PRINTF (PID_FILE, "% LD" APR_EOL_STR, LONG) MyPID); APR_FILE_CLOSE (PID_FILE); Saved_PID = MyPID;
AP_DECLARE (apr_status_t) ap_read_pid (apr_pool_t * p, const char * filename, pid_t * mypid) {const apr_size_t BUFFER_SIZE = sizeof (long) * 3 2; / * see apr_ltoa * / apr_file_t * pid_file = NULL; apr_status_t rv; const char * fname; char * buf, * endptr; APR_SIZE_T BYTES_READ; if (! filename) {Return Apr_egeneral;
fname = ap_server_root_relative (p, filename); if (! fname) {ap_log_error (APLOG_MARK, APLOG_STARTUP | APLOG_CRIT, APR_EBADPATH, NULL, "Invalid PID file path% s, ignoring.", filename); return APR_EGENERAL;}
RV = APR_FILE_OPEN (& PID_FILE, FNAME, APR_READ, APR_OS_DEFAULT, P); IF (RV! = APR_SUCCESS) {Return RV;}
/ * ENSURE NULL-TERMINATION, SO That Strtol Doesn't Go Crazy. * / BUF = APR_Palloc (p, buffer_size); buf [buffer_size - 1] = '/ 0';
RV = APR_FILE_READ_FULL (PID_FILE, BUF, BUFFER_SIZE - 1, & BYTES_READ); if (RV! = APR_SUCCESS && RV! = APR_EOF) {Return RV;}
/ * If We Fill The Buffer, We're Probably Reading a Corrupt Pid File. * To be nice, let's also ensure the first char is a digit. * / If (bytes_read == buffer_size - 1 ||! APR_ISDIGIT (* BUF )) {Return apr_egeneral;}
* mypid = strtol (buf, & endptr, 10);
APR_FILE_CLOSE (PID_FILE); RETURN APR_SUCCESS;}
AP_DECLARE (VOID) AP_LOG_ASSERT (const char * szfile, int nline) {char Time_str [APR_CTIME_LEN];
APR_CTIME (time_str, APR_TIME_NOW ()); ap_log_error (aplog_mark, aplog_crit, 0, null, "[% s] file% s, line% d, assertion /"% s / "failed", time_str, szfile, nline, szexp) ; #if Defined (Win32) debugbreak (); # else / * unix assert does an abort leading to a core dump * / abort (); # Endif} / * piped log support * /
#ifdef AP_HAVE_RELIABLE_PIPED_LOGS / * Forward Declaration * / static void piped_log_maintence (int REAson, Void * Data, Apr_Wait_t Status);
Static int piped_log_spawn (piped_log * pl) {int RC = 0; APR_PROCATTR_T * Procattr; APR_PROC_T * Procnew = NULL; APR_STATUS_T STATUS;
if (((status = apr_procattr_create (& procattr, pl-> p))! = APR_SUCCESS) || ((status = apr_procattr_child_in_set (procattr, ap_piped_log_read_fd (pl), ap_piped_log_write_fd (pl)))! = APR_SUCCESS) || ((status !! = apr_procattr_child_errfn_set (procattr, log_child_errfn)) = APR_SUCCESS) || ((status = apr_procattr_error_check_set (procattr, 1)) = APR_SUCCESS)) {char buf [120]; / * Something bad happened, give up and go away *. / AP_LOG_ERROR (APLOG_MARK, APLOG_STARTUP, 0, NULL, "PIPED_LOG_SPAWN: UNABLE TO SETUP CHILD Process '% S':% S", PL-> Program, APR_STRERROR (STATUS, BUF, SIZEOF (BUF)); RC = -1 Else {char ** args; const char * pname;
apr_tokenize_to_argv (pl-> program, & args, pl-> p); pname = apr_pstrdup (pl-> p, args [0]); procnew = apr_pcalloc (pl-> p, sizeof (apr_proc_t)); status = apr_proc_create (procnew , pname, (const char * const *) args, NULL, procattr, pl-> p); if (status == APR_SUCCESS) {pl-> pid = procnew; ap_piped_log_write_fd (pl) = procnew-> in; apr_proc_other_child_register (procnew , piped_log_maintenance, pl, ap_piped_log_write_fd (pl), pl-> p);} else {char buf [120];. / * Something bad happened, give up and go away * / ap_log_error (APLOG_MARK, APLOG_STARTUP, 0, NULL, " Unable to Start Pired Log Program '% S':% S ", PL-> Program, APR_STRERROR (Status, BUF, SIZEOF (BUF))); RC = -1;}}
Return rc;}
Static void piped_log_maintence (int Reason, void * data, Apr_Wait_t status) {piped_log * pl = data; APR_STATUS_T Stats; int mpm_state;
switch (reason) {case APR_OC_REASON_DEATH: case APR_OC_REASON_LOST: pl-> pid = NULL; / * in case we do not get it going again, this * tells other logic not to try to kill it * / apr_proc_other_child_unregister (pl); stats = ap_mpm_query (AP_MPMQ_MPM_STATE, & mpm_state); if (! stats = APR_SUCCESS) {ap_log_error (APLOG_MARK, APLOG_STARTUP, 0, NULL, "can not query MPM state; not restarting" "piped log program '% s'", pl-> program);!} else if (mpm_state = AP_MPMQ_STOPPING) {ap_log_error (APLOG_MARK, APLOG_STARTUP, 0, NULL, "piped log program '% s' failed unexpectedly", pl-> program); if ((stats = piped_log_spawn (pl) )! = APR_SUCCESS {/ * What Can We do? This Could Be The Error Log We're Having * Problems Opening Up ... * / Char BUF [120]; AP_LOG_ERROR (APLOG_MARK, APLOG_STARTUP, 0, NULL, "PIPED_LOG_MAINTENANCE: UNABLE TO RESPAWN '% S':% S", PL-> Program, APR_STRERROR (stats, buf, sizeof (buf));} } Break; Case Apr_oc_reason_unwritable: / * We Should Not Kill Off The Pipe here
CASE APR_OC_REASON_RESTART: IF (PL-> PID! = null) {APR_PROC_KILL (PL-> PID, SIGTERM); PL-> PID = NULL;} Break;
Case Apr_oc_reason_unregister: Break;}}
Static APR_STATUS_T PIPED_LOG_CLANUP_FOR_EXEC (Void * Data) {piped_log * pl = data;
APR_FILE_CLOSE (AP_PIPED_LOG_READ_FD (PL)); APR_FILE_CLOSE (AP_PIPED_LOG_WRITE_FD (PL)); Return Apr_suCcess;}
Static apr_status_t piped_log_cleanup (void * data) {piped_log * pl = data;
IF (PL-> PID! = NULL) {APR_PROC_KILL (PL-> PID, SIGTERM);} Return Piped_log_cleanup_for_exec (data);}
AP_DECLARE (PIPED_LOG *) AP_OPEN_PIPED_LOG (APR_POOL_T * P, Const Char * Program) {piped_log * pl;
pl = apr_palloc (p, sizeof (* pl)); pl-> p = p; pl-> program = apr_pstrdup (p, program); pl-> pid = NULL; if (apr_file_pipe_create (& ap_piped_log_read_fd (pl), & ap_piped_log_write_fd ( ! pl), p) = APR_SUCCESS) {return NULL;} apr_pool_cleanup_register (p, pl, piped_log_cleanup, piped_log_cleanup_for_exec); if (piped_log_spawn (pl) == -1) {int save_errno = errno; apr_pool_cleanup_kill (p, pl, piped_log_cleanup) APR_FILE_CLOSE (AP_PIPED_LOG_READ_FD (PL)); APR_FILE_CLOSE (AP_PIPED_LOG_WRITE_FD (PL)); errno = save_errno; return null;} Return PL;}
#ELSE / *! AP_HAVE_RELIABLE_PIPED_LOGS * /
Static apr_status_t piped_log_cleanup (void * data) {piped_log * pl = data;
APR_FILE_CLOSE (AP_PIPED_LOG_WRITE_FD (PL)); Return Apr_suCcess;}
AP_DECLARE (PIPED_LOG *) AP_OPEN_PIPED_LOG (APR_POOL_T * P, Const Char * Program) {piped_log * pl; APR_FILE_T * DUMMY = NULL; int RC;
rc = log_child (p, program, & dummy); if (! rc = APR_SUCCESS) {ap_log_error (APLOG_MARK, APLOG_STARTUP, rc, NULL, "Could not start piped log process"); return NULL;} pl = apr_palloc (p, SIZEOF (* PL)); PL-> P = P; AP_PIPED_LOG_READ_FD (PL) = NULL; AP_PIPED_LOG_WRITE_FD (PL) = Dummy; APR_POOL_LOG_CLANUP, PIPED_LOG_CLEANUP);
Return Pl;}
#ENDIF
AP_DECLARE (VOID) AP_CLOSE_PIPED_LOG (PIPED_LOG * PL) {APR_POOL_CLEANUP_RUN (PL-> P, PL, PIPED_LOG_CLANUP);
AP_IMPLEMENT_HOOK_VOID (error_log, (const char * file, int line, int level, apr_status_t status, const server_rec * s, const request_rec * r, apr_pool_t * pool, const char * errstr), (file, line, level, status, s, R, pool, errstr))