401 lines
13 KiB
C++
401 lines
13 KiB
C++
|
/*
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_START@
|
||
|
*
|
||
|
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original Code
|
||
|
* as defined in and that are subject to the Apple Public Source License
|
||
|
* Version 2.0 (the 'License'). You may not use this file except in
|
||
|
* compliance with the License. Please obtain a copy of the License at
|
||
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
||
|
* file.
|
||
|
*
|
||
|
* The Original Code and all software distributed under the License are
|
||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||
|
* Please see the License for the specific language governing rights and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
* @APPLE_LICENSE_HEADER_END@
|
||
|
*
|
||
|
*/
|
||
|
/*
|
||
|
File: QTSSErrorLogModule.cpp
|
||
|
|
||
|
Contains: Implementation of object defined in .h file.
|
||
|
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include <string.h>
|
||
|
#include "QTSSErrorLogModule.h"
|
||
|
#include "QTSSMessages.h"
|
||
|
#include "QTSSRollingLog.h"
|
||
|
#include "QTSServerInterface.h"
|
||
|
#include "QTSSExpirationDate.h"
|
||
|
#include "OSMemory.h"
|
||
|
#include "OS.h"
|
||
|
#include "Task.h"
|
||
|
|
||
|
// STATIC FUNCTIONS
|
||
|
|
||
|
// The dispatch function for this module
|
||
|
static QTSS_Error QTSSErrorLogModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock);
|
||
|
|
||
|
// A service routine allowing other modules to roll the log
|
||
|
static QTSS_Error RollErrorLog(QTSS_ServiceFunctionArgsPtr inArgs);
|
||
|
|
||
|
static QTSS_Error Register(QTSS_Register_Params* inParams);
|
||
|
static QTSS_Error Shutdown();
|
||
|
|
||
|
static QTSS_Error LogError(QTSS_RoleParamPtr inParamBlock);
|
||
|
static void CheckErrorLogState();
|
||
|
|
||
|
static QTSS_Error StateChange(QTSS_StateChange_Params* stateChangeParams);
|
||
|
static void WriteStartupMessage();
|
||
|
static void WriteShutdownMessage();
|
||
|
|
||
|
typedef char* LevelMsg;
|
||
|
|
||
|
static LevelMsg sErrorLevel[] = {
|
||
|
"FATAL:",
|
||
|
"WARNING:",
|
||
|
"INFO:",
|
||
|
"ASSERT:",
|
||
|
"DEBUG:"
|
||
|
};
|
||
|
|
||
|
// QTSSERRORLOG CLASS DEFINITION
|
||
|
|
||
|
class QTSSErrorLog : public QTSSRollingLog
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
QTSSErrorLog() : QTSSRollingLog() {this->SetTaskName("QTSSErrorLog");}
|
||
|
virtual ~QTSSErrorLog() {}
|
||
|
|
||
|
virtual char* GetLogName() { return QTSServerInterface::GetServer()->GetPrefs()->GetErrorLogName();}
|
||
|
|
||
|
virtual char* GetLogDir() { return QTSServerInterface::GetServer()->GetPrefs()->GetErrorLogDir();}
|
||
|
|
||
|
virtual UInt32 GetRollIntervalInDays() { return QTSServerInterface::GetServer()->GetPrefs()->GetErrorRollIntervalInDays();}
|
||
|
|
||
|
virtual UInt32 GetMaxLogBytes() { return QTSServerInterface::GetServer()->GetPrefs()->GetMaxErrorLogBytes();}
|
||
|
|
||
|
};
|
||
|
|
||
|
//ERRORLOGCHECKTASK CLASS DEFINITION
|
||
|
|
||
|
class ErrorLogCheckTask : public Task
|
||
|
{
|
||
|
public:
|
||
|
ErrorLogCheckTask() : Task() {this->SetTaskName("ErrorLogCheckTask"); this->Signal(Task::kStartEvent); }
|
||
|
virtual ~ErrorLogCheckTask() {}
|
||
|
|
||
|
private:
|
||
|
virtual SInt64 Run();
|
||
|
};
|
||
|
|
||
|
const UInt32 kMaxLogStringLen = 2172;
|
||
|
|
||
|
// STATIC DATA
|
||
|
|
||
|
static OSMutex* sLogMutex = NULL;//Log module isn't reentrant
|
||
|
static QTSSErrorLog* sErrorLog = NULL;
|
||
|
static char sLastErrorString[kMaxLogStringLen] = "";
|
||
|
static int sDupErrorStringCount = 0;
|
||
|
static Bool16 sStartedUp = false;
|
||
|
static ErrorLogCheckTask* sErrorLogCheckTask = NULL;
|
||
|
|
||
|
|
||
|
|
||
|
// FUNCTION IMPLEMENTATIONS
|
||
|
|
||
|
QTSS_Error QTSSErrorLogModule_Main(void* inPrivateArgs)
|
||
|
{
|
||
|
return _stublibrary_main(inPrivateArgs, QTSSErrorLogModuleDispatch);
|
||
|
}
|
||
|
|
||
|
|
||
|
QTSS_Error QTSSErrorLogModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock)
|
||
|
{
|
||
|
switch (inRole)
|
||
|
{
|
||
|
case QTSS_Register_Role:
|
||
|
return Register(&inParamBlock->regParams);
|
||
|
case QTSS_StateChange_Role:
|
||
|
return StateChange(&inParamBlock->stateChangeParams);
|
||
|
case QTSS_ErrorLog_Role:
|
||
|
return LogError(inParamBlock);
|
||
|
case QTSS_Shutdown_Role:
|
||
|
return Shutdown();
|
||
|
}
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// ROLE METHODS
|
||
|
|
||
|
QTSS_Error Register(QTSS_Register_Params* inParams)
|
||
|
{
|
||
|
sLogMutex = NEW OSMutex();
|
||
|
|
||
|
// Do role & service setup
|
||
|
|
||
|
(void)QTSS_AddRole(QTSS_ErrorLog_Role);
|
||
|
(void)QTSS_AddRole(QTSS_Shutdown_Role);
|
||
|
(void)QTSS_AddRole(QTSS_StateChange_Role);
|
||
|
|
||
|
(void)QTSS_AddService("RollErrorLog", &RollErrorLog);
|
||
|
|
||
|
// Unlike most modules, all initialization for this module happens in
|
||
|
// the register role. This is so that this error log can begin logging
|
||
|
// errors ASAP.
|
||
|
|
||
|
CheckErrorLogState();
|
||
|
WriteStartupMessage();
|
||
|
|
||
|
// Tell the server our name!
|
||
|
static char* sModuleName = "QTSSErrorLogModule";
|
||
|
::strcpy(inParams->outModuleName, sModuleName);
|
||
|
|
||
|
sErrorLogCheckTask = NEW ErrorLogCheckTask();
|
||
|
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
QTSS_Error Shutdown()
|
||
|
{
|
||
|
WriteShutdownMessage();
|
||
|
if (sErrorLogCheckTask != NULL)
|
||
|
{
|
||
|
// sErrorLogCheckTask is a task object, so don't delete it directly
|
||
|
// instead we signal it to kill itself.
|
||
|
sErrorLogCheckTask->Signal(Task::kKillEvent);
|
||
|
sErrorLogCheckTask = NULL;
|
||
|
}
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
QTSS_Error StateChange(QTSS_StateChange_Params* stateChangeParams)
|
||
|
{
|
||
|
if (stateChangeParams->inNewState == qtssIdleState)
|
||
|
{
|
||
|
WriteShutdownMessage();
|
||
|
}
|
||
|
else if (stateChangeParams->inNewState == qtssRunningState)
|
||
|
{
|
||
|
// Always force our preferences to be reread when we change
|
||
|
// the server's state back to the start -- [sfu]
|
||
|
QTSS_ServiceID id;
|
||
|
(void) QTSS_IDForService(QTSS_REREAD_PREFS_SERVICE, &id);
|
||
|
(void) QTSS_DoService(id, NULL);
|
||
|
WriteStartupMessage();
|
||
|
}
|
||
|
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
|
||
|
QTSS_Error LogError(QTSS_RoleParamPtr inParamBlock)
|
||
|
{
|
||
|
Assert(NULL != inParamBlock->errorParams.inBuffer);
|
||
|
if (inParamBlock->errorParams.inBuffer == NULL)
|
||
|
return QTSS_NoErr;
|
||
|
|
||
|
UInt16 verbLvl = (UInt16) inParamBlock->errorParams.inVerbosity;
|
||
|
if (verbLvl >= qtssIllegalVerbosity)
|
||
|
verbLvl = qtssFatalVerbosity;
|
||
|
|
||
|
QTSServerPrefs* thePrefs = QTSServerInterface::GetServer()->GetPrefs();
|
||
|
|
||
|
OSMutexLocker locker(sLogMutex);
|
||
|
if (thePrefs->GetErrorLogVerbosity() >= inParamBlock->errorParams.inVerbosity)
|
||
|
{
|
||
|
size_t inStringLen = ::strlen(inParamBlock->errorParams.inBuffer);
|
||
|
size_t lastStringLen = ::strlen(sLastErrorString);
|
||
|
Bool16 isDuplicate = true;
|
||
|
|
||
|
if (inStringLen > sizeof(sLastErrorString) -1) //truncate to max char buffer subtract \0 terminator
|
||
|
inStringLen = sizeof(sLastErrorString) -1;
|
||
|
|
||
|
if (lastStringLen != inStringLen) //same size?
|
||
|
isDuplicate = false; // different sizes
|
||
|
else if (::strncmp(inParamBlock->errorParams.inBuffer, sLastErrorString, lastStringLen ) != 0 ) //same chars?
|
||
|
isDuplicate = false; //different chars
|
||
|
|
||
|
//is this error message the same as the last one we received?
|
||
|
if ( isDuplicate )
|
||
|
{ //yes? increment count and bail if it's not the first time we've seen this message (otherwise fall thourhg and write it to the log)
|
||
|
sDupErrorStringCount++;
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//we have a new error message, write a "previous line" message before writing the new log entry
|
||
|
if ( sDupErrorStringCount >= 1 )
|
||
|
{
|
||
|
/*** clean this up - lots of duplicate code ***/
|
||
|
|
||
|
//The error logger is the bottleneck for any and all messages printed by the server.
|
||
|
//For debugging purposes, these messages can be printed to stdout as well.
|
||
|
if (thePrefs->IsScreenLoggingEnabled())
|
||
|
qtss_printf("--last message repeated %d times\n", sDupErrorStringCount);
|
||
|
|
||
|
CheckErrorLogState();
|
||
|
|
||
|
if (sErrorLog == NULL)
|
||
|
return QTSS_NoErr;
|
||
|
|
||
|
//timestamp the error
|
||
|
char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
|
||
|
Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false);
|
||
|
//for now, just ignore the error.
|
||
|
if (!result)
|
||
|
theDateBuffer[0] = '\0';
|
||
|
|
||
|
char tempBuffer[kMaxLogStringLen];
|
||
|
qtss_snprintf(tempBuffer,sizeof(tempBuffer), "%s: --last message repeated %d times\n", theDateBuffer, sDupErrorStringCount);
|
||
|
|
||
|
sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll);
|
||
|
|
||
|
sDupErrorStringCount = 0;
|
||
|
}
|
||
|
::strlcpy(sLastErrorString, inParamBlock->errorParams.inBuffer, sizeof(sLastErrorString));
|
||
|
|
||
|
}
|
||
|
|
||
|
//The error logger is the bottleneck for any and all messages printed by the server.
|
||
|
//For debugging purposes, these messages can be printed to stdout as well.
|
||
|
if (thePrefs->IsScreenLoggingEnabled())
|
||
|
qtss_printf("%s %s\n", sErrorLevel[verbLvl], inParamBlock->errorParams.inBuffer);
|
||
|
|
||
|
CheckErrorLogState();
|
||
|
|
||
|
if (sErrorLog == NULL)
|
||
|
return QTSS_NoErr;
|
||
|
|
||
|
//timestamp the error
|
||
|
char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
|
||
|
Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false);
|
||
|
//for now, just ignore the error.
|
||
|
if (!result)
|
||
|
theDateBuffer[0] = '\0';
|
||
|
|
||
|
char tempBuffer[kMaxLogStringLen];
|
||
|
qtss_snprintf(tempBuffer,sizeof(tempBuffer), "%s: %s %s\n", theDateBuffer, sErrorLevel[verbLvl], inParamBlock->errorParams.inBuffer);
|
||
|
tempBuffer[sizeof(tempBuffer)-2] = '\n'; //make sure the entry has a line feed before the \0 terminator
|
||
|
tempBuffer[sizeof(tempBuffer)-1] = '\0'; //make sure it is 0 terminated.
|
||
|
|
||
|
sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll);
|
||
|
}
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CheckErrorLogState()
|
||
|
{
|
||
|
//this function makes sure the logging state is in synch with the preferences.
|
||
|
//extern variable declared in QTSSPreferences.h
|
||
|
|
||
|
QTSServerPrefs* thePrefs = QTSServerInterface::GetServer()->GetPrefs();
|
||
|
|
||
|
//check error log.
|
||
|
if ((NULL == sErrorLog) && (thePrefs->IsErrorLogEnabled()))
|
||
|
{
|
||
|
sErrorLog = NEW QTSSErrorLog();
|
||
|
sErrorLog->EnableLog();
|
||
|
}
|
||
|
|
||
|
if ((NULL != sErrorLog) && (!thePrefs->IsErrorLogEnabled()))
|
||
|
{
|
||
|
sErrorLog->Delete(); //sErrorLog is a task object, so don't delete it directly
|
||
|
sErrorLog = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SERVICE ROUTINES
|
||
|
|
||
|
QTSS_Error RollErrorLog(QTSS_ServiceFunctionArgsPtr /*inArgs*/)
|
||
|
{
|
||
|
OSMutexLocker locker(sLogMutex);
|
||
|
if (sErrorLog != NULL)
|
||
|
sErrorLog->RollLog();
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
void WriteStartupMessage()
|
||
|
{
|
||
|
if (sStartedUp)
|
||
|
return;
|
||
|
|
||
|
sStartedUp = true;
|
||
|
|
||
|
//format a date for the startup time
|
||
|
char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
|
||
|
Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false);
|
||
|
|
||
|
char tempBuffer[kMaxLogStringLen];
|
||
|
if (result)
|
||
|
qtss_snprintf(tempBuffer,sizeof(tempBuffer), "# Streaming STARTUP %s\n", theDateBuffer);
|
||
|
|
||
|
// log startup message to error log as well.
|
||
|
if ((result) && (sErrorLog != NULL))
|
||
|
sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll);
|
||
|
|
||
|
//write the expire date to the log
|
||
|
if ( QTSSExpirationDate::WillSoftwareExpire() && sErrorLog != NULL )
|
||
|
{
|
||
|
QTSSExpirationDate::sPrintExpirationDate(tempBuffer);
|
||
|
sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WriteShutdownMessage()
|
||
|
{
|
||
|
if (!sStartedUp)
|
||
|
return;
|
||
|
|
||
|
sStartedUp = false;
|
||
|
|
||
|
//log shutdown message
|
||
|
//format a date for the shutdown time
|
||
|
char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
|
||
|
Bool16 result = QTSSRollingLog::FormatDate(theDateBuffer, false);
|
||
|
|
||
|
char tempBuffer[kMaxLogStringLen];
|
||
|
if (result)
|
||
|
qtss_snprintf(tempBuffer, sizeof(tempBuffer), "# Streaming SHUTDOWN %s\n", theDateBuffer);
|
||
|
|
||
|
if ( result && sErrorLog != NULL )
|
||
|
sErrorLog->WriteToLog(tempBuffer, kAllowLogToRoll);
|
||
|
}
|
||
|
|
||
|
// This task runs once an hour to check and see if the log needs to roll.
|
||
|
SInt64 ErrorLogCheckTask::Run()
|
||
|
{
|
||
|
static Bool16 firstTime = true;
|
||
|
|
||
|
// don't check the log for rolling the first time we run.
|
||
|
if (firstTime)
|
||
|
{
|
||
|
firstTime = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Bool16 success = false;
|
||
|
|
||
|
if (sErrorLog != NULL && sErrorLog->IsLogEnabled())
|
||
|
success = sErrorLog->CheckRollLog();
|
||
|
Assert(success);
|
||
|
}
|
||
|
// execute this task again in one hour.
|
||
|
return (60*60*1000);
|
||
|
}
|
||
|
|
||
|
|