conn->headers can be NULL causing segfault. Closes SF Bug #990439.
/*
* The contents of this file are subject to the AOLserver Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://aolserver.com/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is AOLserver Code and related documentation
* distributed by AOL.
*
* The Initial Developer of the Original Code is America Online,
* Inc. Portions created by AOL are Copyright (C) 1999 America Online,
* Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU General Public License (the "GPL"), in which case the
* provisions of GPL are applicable instead of those above. If you wish
* to allow use of your version of this file only under the terms of the
* GPL and not to allow others to use your version of this file under the
* License, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the GPL.
* If you do not delete the provisions above, a recipient may use your
* version of this file under either the License or the GPL.
*/
/*
* nslog.c --
*
* This file implements the access log using NCSA Common Log format.
*/
static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nslog/nslog.c,v 1.15 2004/07/13 20:59:05 dossy Exp $, compiled: " __DATE__ " " __TIME__;
#include "ns.h"
#include <sys/stat.h> /* mkdir */
#include <ctype.h> /* isspace */
#define LOG_COMBINED 1
#define LOG_FMTTIME 2
#define LOG_REQTIME 4
/*
* Exported variables
*/
int Ns_ModuleVersion = 1; /* Needed for AOLserver */
typedef struct {
char *module;
Ns_Mutex lock;
int fd;
char *file;
char *rollfmt;
int flags;
int maxbackup;
int maxlines;
int curlines;
int suppressquery;
Ns_DString buffer;
char **extheaders;
} Log;
/*
* Local functions defined in this file
*/
static Ns_Callback LogRollCallback;
static Ns_Callback LogCloseCallback;
static Ns_TraceProc LogTrace;
static int LogFlush(Log *logPtr, Ns_DString *dsPtr);
static int LogOpen(Log *logPtr);
static int LogRoll(Log *logPtr);
static int LogClose(Log *logPtr);
static void LogConfigExtHeaders(Log *logPtr, char *path);
static Ns_ArgProc LogArg;
static Tcl_CmdProc LogCmd;
static Ns_TclInterpInitProc AddCmds;
/*
* Static variables defined in this file
*/
/*
*----------------------------------------------------------------------
*
* Ns_ModuleInit --
*
* Module initialization routine.
*
* Results:
* NS_OK.
*
* Side effects:
* Log file is opened, trace routine is registered, and, if
* configured, log file roll signal and scheduled procedures
* are registered.
*
*----------------------------------------------------------------------
*/
int
Ns_ModuleInit(char *server, char *module)
{
char *path;
int opt, hour;
Log *logPtr;
static int first = 1;
/*
* Register the info callbacks just once.
*/
if (first) {
Ns_RegisterProcInfo((void *)LogRollCallback, "logroll", LogArg);
Ns_RegisterProcInfo((void *)LogCloseCallback, "logclose", LogArg);
first = 0;
}
/*
* Initialize the log buffer.
*/
logPtr = ns_calloc(1, sizeof(Log));
logPtr->fd = -1;
logPtr->module = module;
Ns_MutexInit(&logPtr->lock);
Ns_MutexSetName2(&logPtr->lock, "nslog", server);
Ns_DStringInit(&logPtr->buffer);
/*
* Determine the log file name which, if not
* absolute, is expected to exist in the module
* specific directory. The module directory is
* created if necessary.
*/
path = Ns_ConfigGetPath(server, module, NULL);
logPtr->file = Ns_ConfigGetValue(path, "file");
if (logPtr->file == NULL) {
logPtr->file = "access.log";
}
if (Ns_PathIsAbsolute(logPtr->file) == NS_FALSE) {
Ns_DString ds;
Ns_DStringInit(&ds);
Ns_ModulePath(&ds, server, module, NULL, NULL);
if (mkdir(ds.string, 0755) != 0 && errno != EEXIST
&& errno != EISDIR) {
Ns_Log(Error, "nslog: mkdir(%s) failed: %s",
ds.string, strerror(errno));
Ns_DStringFree(&ds);
return NS_ERROR;
}
Ns_DStringTrunc(&ds, 0);
Ns_ModulePath(&ds, server, module, logPtr->file, NULL);
logPtr->file = Ns_DStringExport(&ds);
}
/*
* Get parameters from configuration file
*/
logPtr->rollfmt = Ns_ConfigGetValue(path, "rollfmt");
if (!Ns_ConfigGetInt(path, "maxbuffer", &logPtr->maxlines)) {
logPtr->maxlines = 0;
}
if (!Ns_ConfigGetInt(path, "maxbackup", &logPtr->maxbackup) || logPtr->maxbackup < 1) {
logPtr->maxbackup = 100;
}
if (!Ns_ConfigGetBool(path, "formattedTime", &opt)) {
opt = 1;
}
if (opt) {
logPtr->flags |= LOG_FMTTIME;
}
if (!Ns_ConfigGetBool(path, "logcombined", &opt)) {
opt = 1;
}
if (opt) {
logPtr->flags |= LOG_COMBINED;
}
if (!Ns_ConfigGetBool(path, "logreqtime", &opt)) {
opt = 0;
}
if (opt) {
logPtr->flags |= LOG_REQTIME;
}
if (!Ns_ConfigGetBool(path, "suppressquery", &logPtr->suppressquery)) {
logPtr->suppressquery = 0;
}
/*
* Schedule various log roll and shutdown options.
*/
if (!Ns_ConfigGetInt(path, "rollhour", &hour) ||
hour < 0 || hour > 23) {
hour = 0;
}
if (!Ns_ConfigGetBool(path, "rolllog", &opt)) {
opt = 1;
}
if (opt) {
Ns_ScheduleDaily((Ns_SchedProc *) LogRollCallback, logPtr, 0, hour, 0, NULL);
}
if (!Ns_ConfigGetBool(path, "rollonsignal", &opt)) {
opt = 0;;
}
if (opt) {
Ns_RegisterAtSignal(LogRollCallback, logPtr);
}
LogConfigExtHeaders(logPtr, path);
/*
* Open the log and register the trace.
*/
if (LogOpen(logPtr) != NS_OK) {
return NS_ERROR;
}
Ns_RegisterServerTrace(server, LogTrace, logPtr);
Ns_RegisterAtShutdown(LogCloseCallback, logPtr);
Ns_TclInitInterps(server, AddCmds, logPtr);
return NS_OK;
}
/*
*----------------------------------------------------------------------
*
* LogTrace --
*
* Trace routine for appending the log with the current connection
* results.
*
* Results:
* None.
*
* Side effects:
* Entry is appended to the open log.
*
*----------------------------------------------------------------------
*/
static void
LogTrace(void *arg, Ns_Conn *conn)
{
Ns_DString ds;
register char *p;
int quote, n, status, i;
char buf[100];
Log *logPtr = arg;
Ns_Time now, diff;
/*
* Compute the request's elapsed time.
*/
if (logPtr->flags & LOG_REQTIME) {
Ns_GetTime(&now);
Ns_DiffTime(&now, Ns_ConnStartTime(conn), &diff);
}
Ns_DStringInit(&ds);
/*
* Append the peer address and auth user (if any).
* Watch for users comming from proxy servers.
*/
if (conn->headers && (p = Ns_SetIGet(conn->headers, "X-Forwarded-For"))) {
Ns_DStringAppend(&ds, p);
} else {
Ns_DStringAppend(&ds, Ns_ConnPeer(conn));
}
if (conn->authUser == NULL) {
Ns_DStringAppend(&ds, " - - ");
} else {
p = conn->authUser;
quote = 0;
while (*p != '\0') {
if (isspace((unsigned char) *p)) {
quote = 1;
break;
}
++p;
}
if (quote) {
Ns_DStringVarAppend(&ds, " - \"", conn->authUser, "\" ", NULL);
} else {
Ns_DStringVarAppend(&ds, " - ", conn->authUser, " ", NULL);
}
}
/*
* Append a common log format time stamp including GMT offset.
*/
if (!(logPtr->flags & LOG_FMTTIME)) {
sprintf(buf, "[%ld]", (long) time(NULL));
} else {
Ns_LogTime(buf);
}
Ns_DStringAppend(&ds, buf);
/*
* Append the request line.
*/
if (conn->request && conn->request->line) {
if (logPtr->suppressquery) {
/*
* Don't display query data.
* NB: Side-effect is that the real URI is returned, so places
* where trailing slash returns "index.html" logs as "index.html".
*/
Ns_DStringVarAppend(&ds, " \"", conn->request->url, "\" ", NULL);
} else {
Ns_DStringVarAppend(&ds, " \"", conn->request->line, "\" ", NULL);
}
} else {
Ns_DStringAppend(&ds, " \"\" ");
}
/*
* Construct and append the HTTP status code and bytes sent.
*/
n = Ns_ConnResponseStatus(conn);
sprintf(buf, "%d %u ", n ? n : 200, Ns_ConnContentSent(conn));
Ns_DStringAppend(&ds, buf);
if ((logPtr->flags & LOG_COMBINED)) {
/*
* Append the referer and user-agent headers (if any).
*/
Ns_DStringAppend(&ds, "\"");
if ((p = Ns_SetIGet(conn->headers, "referer"))) {
Ns_DStringAppend(&ds, p);
}
Ns_DStringAppend(&ds, "\" \"");
if ((p = Ns_SetIGet(conn->headers, "user-agent"))) {
Ns_DStringAppend(&ds, p);
}
Ns_DStringAppend(&ds, "\"");
}
/*
* Append the request's elapsed time (if enabled).
*/
if (logPtr->flags & LOG_REQTIME) {
sprintf(buf, " %d.%06ld", (int)diff.sec, diff.usec);
Ns_DStringAppend(&ds, buf);
}
/*
* Append the extended headers (if any).
*/
for (i=0; logPtr->extheaders[i] != NULL; i++) {
if ((p = Ns_SetIGet(conn->headers, logPtr->extheaders[i]))) {
Ns_DStringAppend(&ds, " \"");
Ns_DStringAppend(&ds, p);
Ns_DStringAppend(&ds, "\"");
} else {
Ns_DStringAppend(&ds, " -");
}
}
/*
* Append the trailing newline and buffer and/or flush the line.
*/
status = NS_OK;
Ns_DStringAppend(&ds, "\n");
Ns_MutexLock(&logPtr->lock);
if (logPtr->maxlines <= 0) {
status = LogFlush(logPtr, &ds);
} else {
Ns_DStringNAppend(&logPtr->buffer, ds.string, ds.length);
if (++logPtr->curlines > logPtr->maxlines) {
status = LogFlush(logPtr, &logPtr->buffer);
logPtr->curlines = 0;
}
}
Ns_MutexUnlock(&logPtr->lock);
Ns_DStringFree(&ds);
if (status != NS_OK) {
Ns_Log(Error, "nslog: failed to flush log: %s", strerror(errno));
}
}
/*
*----------------------------------------------------------------------
*
* LogCmd --
*
* Implement the ns_accesslog command.
*
* Results:
* Standard Tcl result.
*
* Side effects:
* Depends on command.
*
*----------------------------------------------------------------------
*/
static int
AddCmds(Tcl_Interp *interp, void *arg)
{
Tcl_CreateCommand(interp, "ns_accesslog", LogCmd, arg, NULL);
return TCL_OK;
}
static int
LogCmd(ClientData arg, Tcl_Interp *interp, int argc, CONST char **argv)
{
char *rollfile;
int status;
Log *logPtr = arg;
if (argc < 2) {
Tcl_AppendResult(interp, "wrong # args: should be: \"",
argv[0], " command ?arg?\"", NULL);
return TCL_ERROR;
}
if (STREQ(argv[1], "file")) {
Tcl_SetResult(interp, logPtr->file, TCL_STATIC);
} else if (STREQ(argv[1], "roll")) {
if (argc != 2 && argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be: \"",
argv[0], " ", argv[1], " ?file?\"", NULL);
return TCL_ERROR;
}
Ns_MutexLock(&logPtr->lock);
if (argc == 2) {
status = LogRoll(logPtr);
} else {
rollfile = (char*)argv[2];
status = NS_OK;
if (access(rollfile, F_OK) == 0) {
status = Ns_RollFile(rollfile, logPtr->maxbackup);
}
if (status == NS_OK) {
if (rename(logPtr->file, rollfile) != 0) {
status = NS_ERROR;
} else {
LogFlush(logPtr, &logPtr->buffer);
status = LogOpen(logPtr);
}
}
}
Ns_MutexUnlock(&logPtr->lock);
if (status != NS_OK) {
Tcl_AppendResult(interp, "could not roll \"", logPtr->file,
"\": ", Tcl_PosixError(interp), NULL);
return TCL_ERROR;
}
} else {
Tcl_AppendResult(interp, "unknown command \"", argv[1],
"\": should be file or roll", NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* LogArg --
*
* Copy log file as argument for callback query.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
LogArg(Tcl_DString *dsPtr, void *arg)
{
Log *logPtr = arg;
Tcl_DStringAppendElement(dsPtr, logPtr->file);
}
/*
*----------------------------------------------------------------------
*
* LogOpen --
*
* Open the access log, closing previous log if opeopen
*
* Results:
* NS_OK or NS_ERROR.
*
* Side effects:
* Log re-opened.
*
*----------------------------------------------------------------------
*/
static int
LogOpen(Log *logPtr)
{
int fd;
fd = open(logPtr->file, O_APPEND|O_WRONLY|O_CREAT, 0644);
if (fd < 0) {
Ns_Log(Error, "nslog: error '%s' opening '%s'",
strerror(errno),logPtr->file);
return NS_ERROR;
}
if (logPtr->fd >= 0) {
close(logPtr->fd);
}
logPtr->fd = fd;
Ns_Log(Notice, "nslog: opened '%s'", logPtr->file);
return NS_OK;
}
/*
*----------------------------------------------------------------------
*
* LogClose --
*
* Flush and/or close the log.
*
* Results:
* NS_TRUE or NS_FALSE if log was closed.
*
* Side effects:
* Buffer entries, if any, are flushed.
*
*----------------------------------------------------------------------
*/
static int
LogClose(Log *logPtr)
{
int status;
status = NS_OK;
if (logPtr->fd >= 0) {
Ns_Log(Notice, "nslog: closing '%s'", logPtr->file);
status = LogFlush(logPtr, &logPtr->buffer);
close(logPtr->fd);
logPtr->fd = -1;
Ns_DStringFree(&logPtr->buffer);
}
return status;
}
/*
*----------------------------------------------------------------------
*
* LogFlush --
*
* Flush a log buffer to the open log file. Note: The mutex
* is assumed held during call.
*
* Results:
* None.
*
* Side effects:
* Will disable the log on error.
*
*----------------------------------------------------------------------
*/
static int
LogFlush(Log *logPtr, Ns_DString *dsPtr)
{
if (dsPtr->length > 0) {
if (logPtr->fd >= 0 &&
write(logPtr->fd, dsPtr->string, (size_t)dsPtr->length) != dsPtr->length) {
Ns_Log(Error, "nslog: "
"logging disabled: write() failed: '%s'", strerror(errno));
close(logPtr->fd);
logPtr->fd = -1;
}
Ns_DStringTrunc(dsPtr, 0);
}
if (logPtr->fd < 0) {
return NS_ERROR;
}
return NS_OK;
}
/*
*----------------------------------------------------------------------
*
* LogRoll --
*
* Roll and re-open the access log. This procedure is scheduled
* and/or registered at signal catching.
*
* Results:
* None.
*
* Side effects:
* Files are rolled to new names.
*
*----------------------------------------------------------------------
*/
static int
LogRoll(Log *logPtr)
{
int status;
status = NS_OK;
(void) LogClose(logPtr);
if (access(logPtr->file, F_OK) == 0) {
if (logPtr->rollfmt == NULL) {
status = Ns_RollFile(logPtr->file, logPtr->maxbackup);
} else {
Ns_DString ds;
time_t now;
struct tm *ptm;
char timeBuf[512];
now = time(NULL);
ptm = ns_localtime(&now);
strftime(timeBuf, 512, logPtr->rollfmt, ptm);
Ns_DStringInit(&ds);
Ns_DStringVarAppend(&ds, logPtr->file, ".", timeBuf, NULL);
if (access(ds.string, F_OK) == 0) {
status = Ns_RollFile(ds.string, logPtr->maxbackup);
} else if (errno != ENOENT) {
Ns_Log(Error, "nslog: access(%s, F_OK) failed: '%s'",
ds.string, strerror(errno));
status = NS_ERROR;
}
if (status == NS_OK && rename(logPtr->file, ds.string) != 0) {
Ns_Log(Error, "nslog: rename(%s, %s) failed: '%s'",
logPtr->file, ds.string, strerror(errno));
status = NS_ERROR;
}
Ns_DStringFree(&ds);
if (status == NS_OK) {
status = Ns_PurgeFiles(logPtr->file, logPtr->maxbackup);
}
}
}
status = LogOpen(logPtr);
return status;
}
/*
*----------------------------------------------------------------------
*
* LogCloseCallback, LogRollCallback -
*
* Close or roll the log.
*
* Results:
* None.
*
* Side effects:
* See LogClose and LogRoll.
*
*----------------------------------------------------------------------
*/
static void
LogCallback(int (proc)(Log *), void *arg, char *desc)
{
int status;
Log *logPtr = arg;
Ns_MutexLock(&logPtr->lock);
status = (*proc)(logPtr);
Ns_MutexUnlock(&logPtr->lock);
if (status != NS_OK) {
Ns_Log(Error, "nslog: failed: %s '%s': '%s'",
desc, logPtr->file, strerror(errno));
}
}
static void
LogCloseCallback(void *arg)
{
LogCallback(LogClose, arg, "close");
}
static void
LogRollCallback(void *arg)
{
LogCallback(LogRoll, arg, "roll");
}
/*
*----------------------------------------------------------------------
*
* LogConfigExtHeaders
*
* Parse the ExtendedHeaders configuration.
*
* Results:
* None.
*
* Side effects:
* Sets logPtr->extheaders.
*
*----------------------------------------------------------------------
*/
static void
LogConfigExtHeaders(Log *logPtr, char *path)
{
char *config = Ns_ConfigGetValue(path, "extendedheaders");
char *p;
int i;
if (config == NULL) {
logPtr->extheaders = ns_calloc(1, sizeof *logPtr->extheaders);
logPtr->extheaders[0] = NULL;
return;
}
config = ns_strdup(config);
/* Figure out how many extended headers there are. */
for (i = 1, p = config; *p; p++) {
if (*p == ',') {
i++;
}
}
/* Initialize the extended header pointers. */
logPtr->extheaders = ns_calloc((size_t)(i + 1), sizeof *logPtr->extheaders);
logPtr->extheaders[0] = config;
for (i = 1, p = config; *p; p++) {
if (*p == ',') {
*p = '\000';
logPtr->extheaders[i++] = p + 1;
}
}
logPtr->extheaders[i] = NULL;
}
|
Back to SourceForge.net Powered by ViewCVS 1.0-dev |