693 lines
24 KiB
C++
693 lines
24 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: main.cpp
|
||
|
|
||
|
Contains: main function to drive streaming server.
|
||
|
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include <errno.h>
|
||
|
|
||
|
#include "RunServer.h"
|
||
|
#include "SafeStdLib.h"
|
||
|
#include "OS.h"
|
||
|
#include "OSMemory.h"
|
||
|
#include "OSThread.h"
|
||
|
#include "Socket.h"
|
||
|
#include "SocketUtils.h"
|
||
|
#include "ev.h"
|
||
|
#include "OSArrayObjectDeleter.h"
|
||
|
#include "Task.h"
|
||
|
#include "IdleTask.h"
|
||
|
#include "TimeoutTask.h"
|
||
|
#include "DateTranslator.h"
|
||
|
#include "QTSSRollingLog.h"
|
||
|
|
||
|
|
||
|
#ifndef __Win32__
|
||
|
#include <sys/types.h>
|
||
|
#include <unistd.h>
|
||
|
#endif
|
||
|
#include "QTSServerInterface.h"
|
||
|
#include "QTSServer.h"
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <sys/stat.h>
|
||
|
|
||
|
QTSServer* sServer = NULL;
|
||
|
int sStatusUpdateInterval = 0;
|
||
|
Bool16 sHasPID = false;
|
||
|
UInt64 sLastStatusPackets = 0;
|
||
|
UInt64 sLastDebugPackets = 0;
|
||
|
SInt64 sLastDebugTotalQuality = 0;
|
||
|
#ifdef __sgi__
|
||
|
#include <sched.h>
|
||
|
#endif
|
||
|
|
||
|
QTSS_ServerState StartServer(XMLPrefsParser* inPrefsSource, PrefsSource* inMessagesSource, UInt16 inPortOverride, int statsUpdateInterval, QTSS_ServerState inInitialState, Bool16 inDontFork, UInt32 debugLevel, UInt32 debugOptions)
|
||
|
{
|
||
|
//Mark when we are done starting up. If auto-restart is enabled, we want to make sure
|
||
|
//to always exit with a status of 0 if we encountered a problem WHILE STARTING UP. This
|
||
|
//will prevent infinite-auto-restart-loop type problems
|
||
|
Bool16 doneStartingUp = false;
|
||
|
QTSS_ServerState theServerState = qtssStartingUpState;
|
||
|
|
||
|
sStatusUpdateInterval = statsUpdateInterval;
|
||
|
|
||
|
//Initialize utility classes
|
||
|
OS::Initialize();
|
||
|
OSThread::Initialize();
|
||
|
|
||
|
Socket::Initialize();
|
||
|
SocketUtils::Initialize(!inDontFork);
|
||
|
|
||
|
#if !MACOSXEVENTQUEUE
|
||
|
::select_startevents();//initialize the select() implementation of the event queue
|
||
|
#endif
|
||
|
|
||
|
//start the server
|
||
|
QTSSDictionaryMap::Initialize();
|
||
|
QTSServerInterface::Initialize();// this must be called before constructing the server object
|
||
|
sServer = NEW QTSServer();
|
||
|
sServer->SetDebugLevel(debugLevel);
|
||
|
sServer->SetDebugOptions(debugOptions);
|
||
|
|
||
|
// re-parse config file
|
||
|
inPrefsSource->Parse();
|
||
|
|
||
|
Bool16 createListeners = true;
|
||
|
if (qtssShuttingDownState == inInitialState)
|
||
|
createListeners = false;
|
||
|
|
||
|
sServer->Initialize(inPrefsSource, inMessagesSource, inPortOverride,createListeners);
|
||
|
|
||
|
if (inInitialState == qtssShuttingDownState)
|
||
|
{
|
||
|
sServer->InitModules(inInitialState);
|
||
|
return inInitialState;
|
||
|
}
|
||
|
|
||
|
OSCharArrayDeleter runGroupName(sServer->GetPrefs()->GetRunGroupName());
|
||
|
OSCharArrayDeleter runUserName(sServer->GetPrefs()->GetRunUserName());
|
||
|
OSThread::SetPersonality(runUserName.GetObject(), runGroupName.GetObject());
|
||
|
|
||
|
if (sServer->GetServerState() != qtssFatalErrorState)
|
||
|
{
|
||
|
UInt32 numShortTaskThreads = 0;
|
||
|
UInt32 numBlockingThreads = 0;
|
||
|
UInt32 numThreads = 0;
|
||
|
UInt32 numProcessors = 0;
|
||
|
|
||
|
if (OS::ThreadSafe())
|
||
|
{
|
||
|
numShortTaskThreads = sServer->GetPrefs()->GetNumThreads(); // whatever the prefs say
|
||
|
if (numShortTaskThreads == 0) {
|
||
|
numProcessors = OS::GetNumProcessors();
|
||
|
// 1 worker thread per processor, up to 2 threads.
|
||
|
// Note: Limiting the number of worker threads to 2 on a MacOS X system with > 2 cores
|
||
|
// results in better performance on those systems, as of MacOS X 10.5. Future
|
||
|
// improvements should make this limit unnecessary.
|
||
|
if (numProcessors > 2)
|
||
|
numShortTaskThreads = 2;
|
||
|
else
|
||
|
numShortTaskThreads = numProcessors;
|
||
|
}
|
||
|
|
||
|
numBlockingThreads = sServer->GetPrefs()->GetNumBlockingThreads(); // whatever the prefs say
|
||
|
if (numBlockingThreads == 0)
|
||
|
numBlockingThreads = 1;
|
||
|
|
||
|
}
|
||
|
if (numShortTaskThreads == 0)
|
||
|
numShortTaskThreads = 1;
|
||
|
|
||
|
if (numBlockingThreads == 0)
|
||
|
numBlockingThreads = 1;
|
||
|
|
||
|
numThreads = numShortTaskThreads + numBlockingThreads;
|
||
|
//qtss_printf("Add threads shortask=%lu blocking=%lu\n",numShortTaskThreads, numBlockingThreads);
|
||
|
TaskThreadPool::SetNumShortTaskThreads(numShortTaskThreads);
|
||
|
TaskThreadPool::SetNumBlockingTaskThreads(numBlockingThreads);
|
||
|
TaskThreadPool::AddThreads(numThreads);
|
||
|
sServer->InitNumThreads(numThreads);
|
||
|
|
||
|
#if DEBUG
|
||
|
qtss_printf("Number of task threads: %"_U32BITARG_"\n",numThreads);
|
||
|
#endif
|
||
|
|
||
|
// Start up the server's global tasks, and start listening
|
||
|
TimeoutTask::Initialize(); // The TimeoutTask mechanism is task based,
|
||
|
// we therefore must do this after adding task threads
|
||
|
// this be done before starting the sockets and server tasks
|
||
|
}
|
||
|
|
||
|
//Make sure to do this stuff last. Because these are all the threads that
|
||
|
//do work in the server, this ensures that no work can go on while the server
|
||
|
//is in the process of staring up
|
||
|
if (sServer->GetServerState() != qtssFatalErrorState)
|
||
|
{
|
||
|
IdleTask::Initialize();
|
||
|
Socket::StartThread();
|
||
|
OSThread::Sleep(1000);
|
||
|
|
||
|
//
|
||
|
// On Win32, in order to call modwatch the Socket EventQueue thread must be
|
||
|
// created first. Modules call modwatch from their initializer, and we don't
|
||
|
// want to prevent them from doing that, so module initialization is separated
|
||
|
// out from other initialization, and we start the Socket EventQueue thread first.
|
||
|
// The server is still prevented from doing anything as of yet, because there
|
||
|
// aren't any TaskThreads yet.
|
||
|
sServer->InitModules(inInitialState);
|
||
|
sServer->StartTasks();
|
||
|
sServer->SetupUDPSockets(); // udp sockets are set up after the rtcp task is instantiated
|
||
|
theServerState = sServer->GetServerState();
|
||
|
}
|
||
|
|
||
|
if (theServerState != qtssFatalErrorState)
|
||
|
{
|
||
|
CleanPid(true);
|
||
|
WritePid(!inDontFork);
|
||
|
|
||
|
doneStartingUp = true;
|
||
|
qtss_printf("Streaming Server done starting up\n");
|
||
|
OSMemory::SetMemoryError(ENOMEM);
|
||
|
}
|
||
|
|
||
|
|
||
|
// SWITCH TO RUN USER AND GROUP ID
|
||
|
if (!sServer->SwitchPersonality())
|
||
|
theServerState = qtssFatalErrorState;
|
||
|
|
||
|
//
|
||
|
// Tell the caller whether the server started up or not
|
||
|
return theServerState;
|
||
|
}
|
||
|
|
||
|
void WritePid(Bool16 forked)
|
||
|
{
|
||
|
#ifndef __Win32__
|
||
|
// WRITE PID TO FILE
|
||
|
OSCharArrayDeleter thePidFileName(sServer->GetPrefs()->GetPidFilePath());
|
||
|
FILE *thePidFile = fopen(thePidFileName, "w");
|
||
|
if(thePidFile)
|
||
|
{
|
||
|
if (!forked)
|
||
|
fprintf(thePidFile,"%d\n",getpid()); // write own pid
|
||
|
else
|
||
|
{
|
||
|
fprintf(thePidFile,"%d\n",getppid()); // write parent pid
|
||
|
fprintf(thePidFile,"%d\n",getpid()); // and our own pid in the next line
|
||
|
}
|
||
|
fclose(thePidFile);
|
||
|
sHasPID = true;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void CleanPid(Bool16 force)
|
||
|
{
|
||
|
#ifndef __Win32__
|
||
|
if (sHasPID || force)
|
||
|
{
|
||
|
OSCharArrayDeleter thePidFileName(sServer->GetPrefs()->GetPidFilePath());
|
||
|
unlink(thePidFileName);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
void LogStatus(QTSS_ServerState theServerState)
|
||
|
{
|
||
|
static QTSS_ServerState lastServerState = 0;
|
||
|
static char *sPLISTHeader[] =
|
||
|
{ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
||
|
#if __MacOSX__
|
||
|
"<!DOCTYPE plist SYSTEM \"file://localhost/System/Library/DTDs/PropertyList.dtd\">",
|
||
|
#else
|
||
|
"<!ENTITY % plistObject \"(array | data | date | dict | real | integer | string | true | false )\">",
|
||
|
"<!ELEMENT plist %plistObject;>",
|
||
|
"<!ATTLIST plist version CDATA \"0.9\">",
|
||
|
"",
|
||
|
"<!-- Collections -->",
|
||
|
"<!ELEMENT array (%plistObject;)*>",
|
||
|
"<!ELEMENT dict (key, %plistObject;)*>",
|
||
|
"<!ELEMENT key (#PCDATA)>",
|
||
|
"",
|
||
|
"<!--- Primitive types -->",
|
||
|
"<!ELEMENT string (#PCDATA)>",
|
||
|
"<!ELEMENT data (#PCDATA)> <!-- Contents interpreted as Base-64 encoded -->",
|
||
|
"<!ELEMENT date (#PCDATA)> <!-- Contents should conform to a subset of ISO 8601 (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'. Smaller units may be omitted with a loss of precision) -->",
|
||
|
"",
|
||
|
"<!-- Numerical primitives -->",
|
||
|
"<!ELEMENT true EMPTY> <!-- Boolean constant true -->",
|
||
|
"<!ELEMENT false EMPTY> <!-- Boolean constant false -->",
|
||
|
"<!ELEMENT real (#PCDATA)> <!-- Contents should represent a floating point number matching (\"+\" | \"-\")? d+ (\".\"d*)? (\"E\" (\"+\" | \"-\") d+)? where d is a digit 0-9. -->",
|
||
|
"<!ELEMENT integer (#PCDATA)> <!-- Contents should represent a (possibly signed) integer number in base 10 -->",
|
||
|
"]>",
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static int numHeaderLines = sizeof(sPLISTHeader) / sizeof(char*);
|
||
|
|
||
|
static char* sPlistStart = "<plist version=\"0.9\">";
|
||
|
static char* sPlistEnd = "</plist>";
|
||
|
static char* sDictStart = "<dict>";
|
||
|
static char* sDictEnd = "</dict>";
|
||
|
|
||
|
static char* sKey = " <key>%s</key>\n";
|
||
|
static char* sValue = " <string>%s</string>\n";
|
||
|
|
||
|
static char *sAttributes[] =
|
||
|
{
|
||
|
"qtssSvrServerName",
|
||
|
"qtssSvrServerVersion",
|
||
|
"qtssSvrServerBuild",
|
||
|
"qtssSvrServerPlatform",
|
||
|
"qtssSvrRTSPServerComment",
|
||
|
"qtssSvrServerBuildDate",
|
||
|
"qtssSvrStartupTime",
|
||
|
"qtssSvrCurrentTimeMilliseconds",
|
||
|
"qtssSvrCPULoadPercent",
|
||
|
"qtssSvrState",
|
||
|
"qtssRTPSvrCurConn",
|
||
|
"qtssRTSPCurrentSessionCount",
|
||
|
"qtssRTSPHTTPCurrentSessionCount",
|
||
|
"qtssRTPSvrCurBandwidth",
|
||
|
"qtssRTPSvrCurPackets",
|
||
|
"qtssRTPSvrTotalConn",
|
||
|
"qtssRTPSvrTotalBytes",
|
||
|
"qtssMP3SvrCurConn",
|
||
|
"qtssMP3SvrTotalConn",
|
||
|
"qtssMP3SvrCurBandwidth",
|
||
|
"qtssMP3SvrTotalBytes"
|
||
|
};
|
||
|
static int numAttributes = sizeof(sAttributes) / sizeof(char*);
|
||
|
|
||
|
static StrPtrLen statsFileNameStr("server_status");
|
||
|
|
||
|
if (false == sServer->GetPrefs()->ServerStatFileEnabled())
|
||
|
return;
|
||
|
|
||
|
UInt32 interval = sServer->GetPrefs()->GetStatFileIntervalSec();
|
||
|
if (interval == 0 || (OS::UnixTime_Secs() % interval) > 0 )
|
||
|
return;
|
||
|
|
||
|
// If the total number of RTSP sessions is 0 then we
|
||
|
// might not need to update the "server_status" file.
|
||
|
char* thePrefStr = NULL;
|
||
|
// We start lastRTSPSessionCount off with an impossible value so that
|
||
|
// we force the "server_status" file to be written at least once.
|
||
|
static int lastRTSPSessionCount = -1;
|
||
|
// Get the RTSP session count from the server.
|
||
|
(void)QTSS_GetValueAsString(sServer, qtssRTSPCurrentSessionCount, 0, &thePrefStr);
|
||
|
int currentRTSPSessionCount = ::atoi(thePrefStr);
|
||
|
delete [] thePrefStr; thePrefStr = NULL;
|
||
|
if (currentRTSPSessionCount == 0 && currentRTSPSessionCount == lastRTSPSessionCount)
|
||
|
{
|
||
|
// we don't need to update the "server_status" file except the
|
||
|
// first time we are in the idle state.
|
||
|
if (theServerState == qtssIdleState && lastServerState == qtssIdleState)
|
||
|
{
|
||
|
lastRTSPSessionCount = currentRTSPSessionCount;
|
||
|
lastServerState = theServerState;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// save the RTSP session count for the next time we execute.
|
||
|
lastRTSPSessionCount = currentRTSPSessionCount;
|
||
|
}
|
||
|
|
||
|
StrPtrLenDel pathStr(sServer->GetPrefs()->GetErrorLogDir());
|
||
|
StrPtrLenDel fileNameStr(sServer->GetPrefs()->GetStatsMonitorFileName());
|
||
|
ResizeableStringFormatter pathBuffer(NULL,0);
|
||
|
pathBuffer.PutFilePath(&pathStr,&fileNameStr);
|
||
|
pathBuffer.PutTerminator();
|
||
|
|
||
|
char* filePath = pathBuffer.GetBufPtr();
|
||
|
FILE* statusFile = ::fopen(filePath, "w");
|
||
|
char* theAttributeValue = NULL;
|
||
|
int i;
|
||
|
|
||
|
if (statusFile != NULL)
|
||
|
{
|
||
|
::chmod(filePath, 0640);
|
||
|
for ( i = 0; i < numHeaderLines; i++)
|
||
|
{
|
||
|
qtss_fprintf(statusFile, "%s\n",sPLISTHeader[i]);
|
||
|
}
|
||
|
|
||
|
qtss_fprintf(statusFile, "%s\n", sPlistStart);
|
||
|
qtss_fprintf(statusFile, "%s\n", sDictStart);
|
||
|
|
||
|
// show each element value
|
||
|
for ( i = 0; i < numAttributes; i++)
|
||
|
{
|
||
|
(void)QTSS_GetValueAsString(sServer, QTSSModuleUtils::GetAttrID(sServer,sAttributes[i]), 0, &theAttributeValue);
|
||
|
if (theAttributeValue != NULL)
|
||
|
{
|
||
|
qtss_fprintf(statusFile, sKey, sAttributes[i]);
|
||
|
qtss_fprintf(statusFile, sValue, theAttributeValue);
|
||
|
delete [] theAttributeValue;
|
||
|
theAttributeValue = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qtss_fprintf(statusFile, "%s\n", sDictEnd);
|
||
|
qtss_fprintf(statusFile, "%s\n\n", sPlistEnd);
|
||
|
|
||
|
::fclose(statusFile);
|
||
|
}
|
||
|
lastServerState = theServerState;
|
||
|
}
|
||
|
|
||
|
void print_status(FILE* file, FILE* console, char* format, char* theStr)
|
||
|
{
|
||
|
if (file) qtss_fprintf(file, format, theStr);
|
||
|
if (console) qtss_fprintf(console, format, theStr);
|
||
|
|
||
|
}
|
||
|
|
||
|
void DebugLevel_1(FILE* statusFile, FILE* stdOut, Bool16 printHeader )
|
||
|
{
|
||
|
char* thePrefStr = NULL;
|
||
|
static char numStr[12] ="";
|
||
|
static char dateStr[25] ="";
|
||
|
UInt32 theLen = 0;
|
||
|
|
||
|
if ( printHeader )
|
||
|
{
|
||
|
|
||
|
print_status(statusFile,stdOut,"%s", " RTP-Conns RTSP-Conns HTTP-Conns kBits/Sec Pkts/Sec RTP-Playing AvgDelay CurMaxDelay MaxDelay AvgQuality NumThinned Time\n");
|
||
|
|
||
|
}
|
||
|
|
||
|
(void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurConn, 0, &thePrefStr);
|
||
|
print_status(statusFile, stdOut,"%11s", thePrefStr);
|
||
|
|
||
|
delete [] thePrefStr; thePrefStr = NULL;
|
||
|
|
||
|
(void)QTSS_GetValueAsString(sServer, qtssRTSPCurrentSessionCount, 0, &thePrefStr);
|
||
|
print_status(statusFile, stdOut,"%11s", thePrefStr);
|
||
|
delete [] thePrefStr; thePrefStr = NULL;
|
||
|
|
||
|
(void)QTSS_GetValueAsString(sServer, qtssRTSPHTTPCurrentSessionCount, 0, &thePrefStr);
|
||
|
print_status(statusFile, stdOut,"%11s", thePrefStr);
|
||
|
delete [] thePrefStr; thePrefStr = NULL;
|
||
|
|
||
|
UInt32 curBandwidth = 0;
|
||
|
theLen = sizeof(curBandwidth);
|
||
|
(void)QTSS_GetValue(sServer, qtssRTPSvrCurBandwidth, 0, &curBandwidth, &theLen);
|
||
|
qtss_snprintf(numStr, 11, "%"_U32BITARG_"", curBandwidth/1024);
|
||
|
print_status(statusFile, stdOut,"%11s", numStr);
|
||
|
|
||
|
(void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurPackets, 0, &thePrefStr);
|
||
|
print_status(statusFile, stdOut,"%11s", thePrefStr);
|
||
|
delete [] thePrefStr; thePrefStr = NULL;
|
||
|
|
||
|
|
||
|
UInt32 currentPlaying = sServer->GetNumRTPPlayingSessions();
|
||
|
qtss_snprintf( numStr, sizeof(numStr) -1, "%"_U32BITARG_"", currentPlaying);
|
||
|
print_status(statusFile, stdOut,"%14s", numStr);
|
||
|
|
||
|
|
||
|
//is the server keeping up with the streams?
|
||
|
//what quality are the streams?
|
||
|
SInt64 totalRTPPaackets = sServer->GetTotalRTPPackets();
|
||
|
SInt64 deltaPackets = totalRTPPaackets - sLastDebugPackets;
|
||
|
sLastDebugPackets = totalRTPPaackets;
|
||
|
|
||
|
SInt64 totalQuality = sServer->GetTotalQuality();
|
||
|
SInt64 deltaQuality = totalQuality - sLastDebugTotalQuality;
|
||
|
sLastDebugTotalQuality = totalQuality;
|
||
|
|
||
|
SInt64 currentMaxLate = sServer->GetCurrentMaxLate();
|
||
|
SInt64 totalLate = sServer->GetTotalLate();
|
||
|
|
||
|
sServer->ClearTotalLate();
|
||
|
sServer->ClearCurrentMaxLate();
|
||
|
sServer->ClearTotalQuality();
|
||
|
|
||
|
::qtss_snprintf(numStr, sizeof(numStr) -1, "%s", "0");
|
||
|
if (deltaPackets > 0)
|
||
|
qtss_snprintf(numStr, sizeof(numStr) -1, "%"_S32BITARG_"", (SInt32) ((SInt64)totalLate / (SInt64) deltaPackets ));
|
||
|
print_status(statusFile, stdOut,"%11s", numStr);
|
||
|
|
||
|
qtss_snprintf(numStr,sizeof(numStr) -1, "%"_S32BITARG_"", (SInt32) currentMaxLate);
|
||
|
print_status(statusFile, stdOut,"%11s", numStr);
|
||
|
|
||
|
qtss_snprintf(numStr,sizeof(numStr) -1, "%"_S32BITARG_"", (SInt32) sServer->GetMaxLate() );
|
||
|
print_status(statusFile, stdOut,"%11s", numStr);
|
||
|
|
||
|
::qtss_snprintf(numStr, sizeof(numStr) -1, "%s", "0");
|
||
|
if (deltaPackets > 0)
|
||
|
qtss_snprintf(numStr, sizeof(numStr) -1, "%"_S32BITARG_"", (SInt32) ((SInt64) deltaQuality / (SInt64) deltaPackets));
|
||
|
print_status(statusFile, stdOut,"%11s", numStr);
|
||
|
|
||
|
qtss_snprintf(numStr,sizeof(numStr) -1, "%"_S32BITARG_"", (SInt32) sServer->GetNumThinned() );
|
||
|
print_status(statusFile, stdOut,"%11s", numStr);
|
||
|
|
||
|
|
||
|
|
||
|
char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
|
||
|
(void) QTSSRollingLog::FormatDate(theDateBuffer, false);
|
||
|
|
||
|
qtss_snprintf(dateStr,sizeof(dateStr) -1, "%s", theDateBuffer );
|
||
|
print_status(statusFile, stdOut,"%24s\n", dateStr);
|
||
|
}
|
||
|
|
||
|
FILE* LogDebugEnabled()
|
||
|
{
|
||
|
|
||
|
if (DebugLogOn(sServer))
|
||
|
{
|
||
|
static StrPtrLen statsFileNameStr("server_debug_status");
|
||
|
|
||
|
StrPtrLenDel pathStr(sServer->GetPrefs()->GetErrorLogDir());
|
||
|
ResizeableStringFormatter pathBuffer(NULL,0);
|
||
|
pathBuffer.PutFilePath(&pathStr,&statsFileNameStr);
|
||
|
pathBuffer.PutTerminator();
|
||
|
|
||
|
char* filePath = pathBuffer.GetBufPtr();
|
||
|
return ::fopen(filePath, "a");
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
FILE* DisplayDebugEnabled()
|
||
|
{
|
||
|
return ( DebugDisplayOn(sServer) ) ? stdout : NULL ;
|
||
|
}
|
||
|
|
||
|
|
||
|
void DebugStatus(UInt32 debugLevel, Bool16 printHeader)
|
||
|
{
|
||
|
|
||
|
FILE* statusFile = LogDebugEnabled();
|
||
|
FILE* stdOut = DisplayDebugEnabled();
|
||
|
|
||
|
if (debugLevel > 0)
|
||
|
DebugLevel_1(statusFile, stdOut, printHeader);
|
||
|
|
||
|
if (statusFile)
|
||
|
::fclose(statusFile);
|
||
|
}
|
||
|
|
||
|
void FormattedTotalBytesBuffer(char *outBuffer, int outBufferLen, UInt64 totalBytes)
|
||
|
{
|
||
|
Float32 displayBytes = 0.0;
|
||
|
char sizeStr[] = "B";
|
||
|
char* format = NULL;
|
||
|
|
||
|
if (totalBytes > 1073741824 ) //GBytes
|
||
|
{ displayBytes = (Float32) ( (Float64) (SInt64) totalBytes / (Float64) (SInt64) 1073741824 );
|
||
|
sizeStr[0] = 'G';
|
||
|
format = "%.4f%s ";
|
||
|
}
|
||
|
else if (totalBytes > 1048576 ) //MBytes
|
||
|
{ displayBytes = (Float32) (SInt32) totalBytes / (Float32) (SInt32) 1048576;
|
||
|
sizeStr[0] = 'M';
|
||
|
format = "%.3f%s ";
|
||
|
}
|
||
|
else if (totalBytes > 1024 ) //KBytes
|
||
|
{ displayBytes = (Float32) (SInt32) totalBytes / (Float32) (SInt32) 1024;
|
||
|
sizeStr[0] = 'K';
|
||
|
format = "%.2f%s ";
|
||
|
}
|
||
|
else
|
||
|
{ displayBytes = (Float32) (SInt32) totalBytes; //Bytes
|
||
|
sizeStr[0] = 'B';
|
||
|
format = "%4.0f%s ";
|
||
|
}
|
||
|
|
||
|
outBuffer[outBufferLen -1] = 0;
|
||
|
qtss_snprintf(outBuffer, outBufferLen -1, format , displayBytes, sizeStr);
|
||
|
}
|
||
|
|
||
|
void PrintStatus(Bool16 printHeader)
|
||
|
{
|
||
|
char* thePrefStr = NULL;
|
||
|
UInt32 theLen = 0;
|
||
|
|
||
|
if ( printHeader )
|
||
|
{
|
||
|
qtss_printf(" RTP-Conns RTSP-Conns HTTP-Conns kBits/Sec Pkts/Sec TotConn TotBytes TotPktsLost Time\n");
|
||
|
}
|
||
|
|
||
|
(void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurConn, 0, &thePrefStr);
|
||
|
qtss_printf( "%11s", thePrefStr);
|
||
|
delete [] thePrefStr; thePrefStr = NULL;
|
||
|
|
||
|
(void)QTSS_GetValueAsString(sServer, qtssRTSPCurrentSessionCount, 0, &thePrefStr);
|
||
|
qtss_printf( "%11s", thePrefStr);
|
||
|
delete [] thePrefStr; thePrefStr = NULL;
|
||
|
|
||
|
(void)QTSS_GetValueAsString(sServer, qtssRTSPHTTPCurrentSessionCount, 0, &thePrefStr);
|
||
|
qtss_printf( "%11s", thePrefStr);
|
||
|
delete [] thePrefStr; thePrefStr = NULL;
|
||
|
|
||
|
UInt32 curBandwidth = 0;
|
||
|
theLen = sizeof(curBandwidth);
|
||
|
(void)QTSS_GetValue(sServer, qtssRTPSvrCurBandwidth, 0, &curBandwidth, &theLen);
|
||
|
qtss_printf("%11"_U32BITARG_, curBandwidth/1024);
|
||
|
|
||
|
(void)QTSS_GetValueAsString(sServer, qtssRTPSvrCurPackets, 0, &thePrefStr);
|
||
|
qtss_printf( "%11s", thePrefStr);
|
||
|
delete [] thePrefStr; thePrefStr = NULL;
|
||
|
|
||
|
(void)QTSS_GetValueAsString(sServer, qtssRTPSvrTotalConn, 0, &thePrefStr);
|
||
|
qtss_printf( "%11s", thePrefStr);
|
||
|
delete [] thePrefStr; thePrefStr = NULL;
|
||
|
|
||
|
UInt64 totalBytes = sServer->GetTotalRTPBytes();
|
||
|
char displayBuff[32] = "";
|
||
|
FormattedTotalBytesBuffer(displayBuff, sizeof(displayBuff),totalBytes);
|
||
|
qtss_printf( "%17s", displayBuff);
|
||
|
|
||
|
qtss_printf( "%11"_64BITARG_"u", sServer->GetTotalRTPPacketsLost());
|
||
|
|
||
|
char theDateBuffer[QTSSRollingLog::kMaxDateBufferSizeInBytes];
|
||
|
(void) QTSSRollingLog::FormatDate(theDateBuffer, false);
|
||
|
qtss_printf( "%25s",theDateBuffer);
|
||
|
|
||
|
qtss_printf( "\n");
|
||
|
|
||
|
}
|
||
|
|
||
|
Bool16 PrintHeader(UInt32 loopCount)
|
||
|
{
|
||
|
return ( (loopCount % (sStatusUpdateInterval * 10) ) == 0 ) ? true : false;
|
||
|
}
|
||
|
|
||
|
Bool16 PrintLine(UInt32 loopCount)
|
||
|
{
|
||
|
return ( (loopCount % sStatusUpdateInterval) == 0 ) ? true : false;
|
||
|
}
|
||
|
|
||
|
|
||
|
void RunServer()
|
||
|
{
|
||
|
Bool16 restartServer = false;
|
||
|
UInt32 loopCount = 0;
|
||
|
UInt32 debugLevel = 0;
|
||
|
Bool16 printHeader = false;
|
||
|
Bool16 printStatus = false;
|
||
|
|
||
|
|
||
|
//just wait until someone stops the server or a fatal error occurs.
|
||
|
QTSS_ServerState theServerState = sServer->GetServerState();
|
||
|
while ((theServerState != qtssShuttingDownState) &&
|
||
|
(theServerState != qtssFatalErrorState))
|
||
|
{
|
||
|
#ifdef __sgi__
|
||
|
OSThread::Sleep(999);
|
||
|
#else
|
||
|
OSThread::Sleep(1000);
|
||
|
#endif
|
||
|
|
||
|
LogStatus(theServerState);
|
||
|
|
||
|
if (sStatusUpdateInterval)
|
||
|
{
|
||
|
debugLevel = sServer->GetDebugLevel();
|
||
|
printHeader = PrintHeader(loopCount);
|
||
|
printStatus = PrintLine(loopCount);
|
||
|
|
||
|
if (printStatus)
|
||
|
{
|
||
|
if (DebugOn(sServer) ) // debug level display or logging is on
|
||
|
DebugStatus(debugLevel, printHeader);
|
||
|
|
||
|
if (!DebugDisplayOn(sServer))
|
||
|
PrintStatus(printHeader); // default status output
|
||
|
}
|
||
|
|
||
|
|
||
|
loopCount++;
|
||
|
|
||
|
}
|
||
|
|
||
|
if ((sServer->SigIntSet()) || (sServer->SigTermSet()))
|
||
|
{
|
||
|
//
|
||
|
// start the shutdown process
|
||
|
theServerState = qtssShuttingDownState;
|
||
|
(void)QTSS_SetValue(QTSServerInterface::GetServer(), qtssSvrState, 0, &theServerState, sizeof(theServerState));
|
||
|
|
||
|
if (sServer->SigIntSet())
|
||
|
restartServer = true;
|
||
|
}
|
||
|
|
||
|
theServerState = sServer->GetServerState();
|
||
|
if (theServerState == qtssIdleState)
|
||
|
sServer->KillAllRTPSessions();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Kill all the sessions and wait for them to die,
|
||
|
// but don't wait more than 5 seconds
|
||
|
sServer->KillAllRTPSessions();
|
||
|
for (UInt32 shutdownWaitCount = 0; (sServer->GetNumRTPSessions() > 0) && (shutdownWaitCount < 5); shutdownWaitCount++)
|
||
|
OSThread::Sleep(1000);
|
||
|
|
||
|
//Now, make sure that the server can't do any work
|
||
|
TaskThreadPool::RemoveThreads();
|
||
|
|
||
|
//now that the server is definitely stopped, it is safe to initate
|
||
|
//the shutdown process
|
||
|
delete sServer;
|
||
|
|
||
|
CleanPid(false);
|
||
|
//ok, we're ready to exit. If we're quitting because of some fatal error
|
||
|
//while running the server, make sure to let the parent process know by
|
||
|
//exiting with a nonzero status. Otherwise, exit with a 0 status
|
||
|
if (theServerState == qtssFatalErrorState || restartServer)
|
||
|
::exit (-2);//-2 signals parent process to restart server
|
||
|
}
|