1247 lines
47 KiB
C++
1247 lines
47 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: QTSServer.cpp
|
|
|
|
Contains: Implements object defined in QTSServer.h
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
#ifndef __Win32__
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#endif
|
|
#include <errno.h>
|
|
|
|
#ifndef __Win32__
|
|
#include <unistd.h>
|
|
#include <grp.h>
|
|
#include <pwd.h>
|
|
#endif
|
|
|
|
#include "QTSServer.h"
|
|
|
|
#include "OSMemory.h"
|
|
#include "OSArrayObjectDeleter.h"
|
|
#include "SocketUtils.h"
|
|
#include "TCPListenerSocket.h"
|
|
#include "Task.h"
|
|
|
|
#include "QTSS_Private.h"
|
|
#include "QTSSCallbacks.h"
|
|
#include "QTSSModuleUtils.h"
|
|
|
|
//Compile time modules
|
|
#include "QTSSErrorLogModule.h"
|
|
#include "QTSSFileModule.h"
|
|
#include "QTSSAccessLogModule.h"
|
|
#include "QTSSFlowControlModule.h"
|
|
#include "QTSSReflectorModule.h"
|
|
#ifdef PROXYSERVER
|
|
#include "QTSSProxyModule.h"
|
|
#endif
|
|
#include "QTSSRelayModule.h"
|
|
#include "QTSSPosixFileSysModule.h"
|
|
#include "QTSSAdminModule.h"
|
|
#include "QTSSAccessModule.h"
|
|
#include "QTSSMP3StreamingModule.h"
|
|
#if __MacOSX__
|
|
#include "QTSSDSAuthModule.h"
|
|
#endif
|
|
#if MEMORY_DEBUGGING
|
|
#include "QTSSWebDebugModule.h"
|
|
#endif
|
|
|
|
|
|
|
|
#include "RTSPRequestInterface.h"
|
|
#include "RTSPSessionInterface.h"
|
|
#include "RTPSessionInterface.h"
|
|
#include "RTSPSession.h"
|
|
#include "RTPStream.h"
|
|
#include "RTCPTask.h"
|
|
#include "QTSSFile.h"
|
|
|
|
#include "RTPStream3GPP.h"
|
|
#include "RTSPRequest3GPP.h"
|
|
|
|
// CLASS DEFINITIONS
|
|
|
|
class RTSPListenerSocket : public TCPListenerSocket
|
|
{
|
|
public:
|
|
|
|
RTSPListenerSocket() {}
|
|
virtual ~RTSPListenerSocket() {}
|
|
|
|
//sole job of this object is to implement this function
|
|
virtual Task* GetSessionTask(TCPSocket** outSocket);
|
|
|
|
//check whether the Listener should be idling
|
|
Bool16 OverMaxConnections(UInt32 buffer);
|
|
|
|
};
|
|
|
|
class RTPSocketPool : public UDPSocketPool
|
|
{
|
|
public:
|
|
|
|
// Pool of UDP sockets for use by the RTP server
|
|
|
|
RTPSocketPool() {}
|
|
~RTPSocketPool() {}
|
|
|
|
virtual UDPSocketPair* ConstructUDPSocketPair();
|
|
virtual void DestructUDPSocketPair(UDPSocketPair* inPair);
|
|
|
|
virtual void SetUDPSocketOptions(UDPSocketPair* inPair);
|
|
};
|
|
|
|
|
|
|
|
char* QTSServer::sPortPrefString = "rtsp_port";
|
|
QTSS_Callbacks QTSServer::sCallbacks;
|
|
XMLPrefsParser* QTSServer::sPrefsSource = NULL;
|
|
PrefsSource* QTSServer::sMessagesSource = NULL;
|
|
|
|
|
|
QTSServer::~QTSServer()
|
|
{
|
|
//
|
|
// Grab the server mutex. This is to make sure all gets & set values on this
|
|
// object complete before we start deleting stuff
|
|
OSMutexLocker serverlocker(this->GetServerObjectMutex());
|
|
|
|
//
|
|
// Grab the prefs mutex. This is to make sure we can't reread prefs
|
|
// WHILE shutting down, which would cause some weirdness for QTSS API
|
|
// (some modules could get QTSS_RereadPrefs_Role after QTSS_Shutdown, which would be bad)
|
|
OSMutexLocker locker(this->GetPrefs()->GetMutex());
|
|
|
|
QTSS_ModuleState theModuleState;
|
|
theModuleState.curRole = QTSS_Shutdown_Role;
|
|
theModuleState.curTask = NULL;
|
|
OSThread::SetMainThreadData(&theModuleState);
|
|
|
|
for (UInt32 x = 0; x < QTSServerInterface::GetNumModulesInRole(QTSSModule::kShutdownRole); x++)
|
|
(void)QTSServerInterface::GetModule(QTSSModule::kShutdownRole, x)->CallDispatch(QTSS_Shutdown_Role, NULL);
|
|
|
|
OSThread::SetMainThreadData(NULL);
|
|
}
|
|
|
|
Bool16 QTSServer::Initialize(XMLPrefsParser* inPrefsSource, PrefsSource* inMessagesSource, UInt16 inPortOverride, Bool16 createListeners)
|
|
{
|
|
static const UInt32 kRTPSessionMapSize = 577;
|
|
fServerState = qtssFatalErrorState;
|
|
sPrefsSource = inPrefsSource;
|
|
sMessagesSource = inMessagesSource;
|
|
this->InitCallbacks();
|
|
|
|
//
|
|
// DICTIONARY INITIALIZATION
|
|
|
|
QTSSModule::Initialize();
|
|
QTSServerPrefs::Initialize();
|
|
QTSSMessages::Initialize();
|
|
RTSPRequestInterface::Initialize();
|
|
RTSPSessionInterface::Initialize();
|
|
RTPSessionInterface::Initialize();
|
|
RTPStream::Initialize();
|
|
RTSPSession::Initialize();
|
|
QTSSFile::Initialize();
|
|
QTSSUserProfile::Initialize();
|
|
|
|
RTSPRequest3GPP::Initialize();
|
|
RTPStream3GPP::Initialize();
|
|
RTPSession3GPP::Initialize();
|
|
RTSPSession3GPP::Initialize();
|
|
|
|
//
|
|
// STUB SERVER INITIALIZATION
|
|
//
|
|
// Construct stub versions of the prefs and messages dictionaries. We need
|
|
// both of these to initialize the server, but they have to be stubs because
|
|
// their QTSSDictionaryMaps will presumably be modified when modules get loaded.
|
|
|
|
fSrvrPrefs = new QTSServerPrefs(inPrefsSource, false); // First time, don't write changes to the prefs file
|
|
fSrvrMessages = new QTSSMessages(inMessagesSource);
|
|
QTSSModuleUtils::Initialize(fSrvrMessages, this, QTSServerInterface::GetErrorLogStream());
|
|
|
|
//
|
|
// SETUP ASSERT BEHAVIOR
|
|
//
|
|
// Depending on the server preference, we will either break when we hit an
|
|
// assert, or log the assert to the error log
|
|
if (!fSrvrPrefs->ShouldServerBreakOnAssert())
|
|
SetAssertLogger(this->GetErrorLogStream());// the error log stream is our assert logger
|
|
|
|
//
|
|
// CREATE GLOBAL OBJECTS
|
|
fSocketPool = new RTPSocketPool();
|
|
fRTPMap = new OSRefTable(kRTPSessionMapSize);
|
|
|
|
//
|
|
// Load ERROR LOG module only. This is good in case there is a startup error.
|
|
|
|
QTSSModule* theLoggingModule = new QTSSModule("QTSSErrorLogModule");
|
|
(void)theLoggingModule->SetupModule(&sCallbacks, &QTSSErrorLogModule_Main);
|
|
(void)AddModule(theLoggingModule);
|
|
this->BuildModuleRoleArrays();
|
|
|
|
//
|
|
// DEFAULT IP ADDRESS & DNS NAME
|
|
if (!this->SetDefaultIPAddr())
|
|
return false;
|
|
|
|
//
|
|
// STARTUP TIME - record it
|
|
fStartupTime_UnixMilli = OS::Milliseconds();
|
|
fGMTOffset = OS::GetGMTOffset();
|
|
|
|
//
|
|
// BEGIN LISTENING
|
|
if (createListeners)
|
|
{
|
|
if ( !this->CreateListeners(false, fSrvrPrefs, inPortOverride) )
|
|
QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgSomePortsFailed, 0);
|
|
}
|
|
|
|
if ( fNumListeners == 0 )
|
|
{ if (createListeners)
|
|
QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgNoPortsSucceeded, 0);
|
|
return false;
|
|
}
|
|
|
|
|
|
fServerState = qtssStartingUpState;
|
|
return true;
|
|
}
|
|
|
|
void QTSServer::InitModules(QTSS_ServerState inEndState)
|
|
{
|
|
//
|
|
// LOAD AND INITIALIZE ALL MODULES
|
|
|
|
// temporarily set the verbosity on missing prefs when starting up to debug level
|
|
// This keeps all the pref messages being written to the config file from being logged.
|
|
// don't exit until the verbosity level is reset back to the initial prefs.
|
|
|
|
LoadModules(fSrvrPrefs);
|
|
LoadCompiledInModules();
|
|
this->BuildModuleRoleArrays();
|
|
|
|
fSrvrPrefs->SetErrorLogVerbosity(qtssWarningVerbosity); // turn off info messages while initializing compiled in modules.
|
|
//
|
|
// CREATE MODULE OBJECTS AND READ IN MODULE PREFS
|
|
|
|
// Finish setting up modules. Create our final prefs & messages objects,
|
|
// register all global dictionaries, and invoke the modules in their Init roles.
|
|
fStubSrvrPrefs = fSrvrPrefs;
|
|
fStubSrvrMessages = fSrvrMessages;
|
|
|
|
fSrvrPrefs = new QTSServerPrefs(sPrefsSource, true); // Now write changes to the prefs file. First time, we don't because the error messages won't get printed.
|
|
QTSS_ErrorVerbosity serverLevel = fSrvrPrefs->GetErrorLogVerbosity(); // get the real prefs verbosity and save it.
|
|
fSrvrPrefs->SetErrorLogVerbosity(qtssWarningVerbosity); // turn off info messages while loading dynamic modules
|
|
|
|
|
|
fSrvrMessages = new QTSSMessages(sMessagesSource);
|
|
QTSSModuleUtils::Initialize(fSrvrMessages, this, QTSServerInterface::GetErrorLogStream());
|
|
|
|
this->SetVal(qtssSvrMessages, &fSrvrMessages, sizeof(fSrvrMessages));
|
|
this->SetVal(qtssSvrPreferences, &fSrvrPrefs, sizeof(fSrvrPrefs));
|
|
|
|
//
|
|
// ADD REREAD PREFERENCES SERVICE
|
|
(void)QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kServiceDictIndex)->
|
|
AddAttribute(QTSS_REREAD_PREFS_SERVICE, (QTSS_AttrFunctionPtr)QTSServer::RereadPrefsService, qtssAttrDataTypeUnknown, qtssAttrModeRead);
|
|
|
|
//
|
|
// INVOKE INITIALIZE ROLE
|
|
this->DoInitRole();
|
|
|
|
if (fServerState != qtssFatalErrorState)
|
|
fServerState = inEndState; // Server is done starting up!
|
|
|
|
|
|
fSrvrPrefs->SetErrorLogVerbosity(serverLevel); // reset the server's verbosity back to the original prefs level.
|
|
}
|
|
|
|
void QTSServer::StartTasks()
|
|
{
|
|
fRTCPTask = new RTCPTask();
|
|
fStatsTask = new RTPStatsUpdaterTask();
|
|
|
|
//
|
|
// Start listening
|
|
for (UInt32 x = 0; x < fNumListeners; x++)
|
|
fListeners[x]->RequestEvent(EV_RE);
|
|
}
|
|
|
|
Bool16 QTSServer::SetDefaultIPAddr()
|
|
{
|
|
//check to make sure there is an available ip interface
|
|
if (SocketUtils::GetNumIPAddrs() == 0)
|
|
{
|
|
QTSSModuleUtils::LogError(qtssFatalVerbosity, qtssMsgNotConfiguredForIP, 0);
|
|
return false;
|
|
}
|
|
|
|
//find out what our default IP addr is & dns name
|
|
UInt32 theNumAddrs = 0;
|
|
UInt32* theIPAddrs = this->GetRTSPIPAddrs(fSrvrPrefs, &theNumAddrs);
|
|
if (theNumAddrs == 1)
|
|
fDefaultIPAddr = SocketUtils::GetIPAddr(0);
|
|
else
|
|
fDefaultIPAddr = theIPAddrs[0];
|
|
delete [] theIPAddrs;
|
|
|
|
for (UInt32 ipAddrIter = 0; ipAddrIter < SocketUtils::GetNumIPAddrs(); ipAddrIter++)
|
|
{
|
|
if (SocketUtils::GetIPAddr(ipAddrIter) == fDefaultIPAddr)
|
|
{
|
|
this->SetVal(qtssSvrDefaultDNSName, SocketUtils::GetDNSNameStr(ipAddrIter));
|
|
Assert(this->GetValue(qtssSvrDefaultDNSName)->Ptr != NULL);
|
|
this->SetVal(qtssSvrDefaultIPAddrStr, SocketUtils::GetIPAddrStr(ipAddrIter));
|
|
Assert(this->GetValue(qtssSvrDefaultDNSName)->Ptr != NULL);
|
|
break;
|
|
}
|
|
}
|
|
if (this->GetValue(qtssSvrDefaultDNSName)->Ptr == NULL)
|
|
{
|
|
//If we've gotten here, what has probably happened is the IP address (explicitly
|
|
//entered as a preference) doesn't exist
|
|
QTSSModuleUtils::LogError(qtssFatalVerbosity, qtssMsgDefaultRTSPAddrUnavail, 0);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
Bool16 QTSServer::CreateListeners(Bool16 startListeningNow, QTSServerPrefs* inPrefs, UInt16 inPortOverride)
|
|
{
|
|
struct PortTracking
|
|
{
|
|
PortTracking() : fPort(0), fIPAddr(0), fNeedsCreating(true) {}
|
|
|
|
UInt16 fPort;
|
|
UInt32 fIPAddr;
|
|
Bool16 fNeedsCreating;
|
|
};
|
|
|
|
PortTracking* thePortTrackers = NULL;
|
|
UInt32 theTotalPortTrackers = 0;
|
|
|
|
// Get the IP addresses from the pref
|
|
UInt32 theNumAddrs = 0;
|
|
UInt32* theIPAddrs = this->GetRTSPIPAddrs(inPrefs, &theNumAddrs);
|
|
UInt32 index = 0;
|
|
|
|
if ( inPortOverride != 0)
|
|
{
|
|
theTotalPortTrackers = theNumAddrs; // one port tracking struct for each IP addr
|
|
thePortTrackers = NEW PortTracking[theTotalPortTrackers];
|
|
for (index = 0; index < theNumAddrs; index++)
|
|
{
|
|
thePortTrackers[index].fPort = inPortOverride;
|
|
thePortTrackers[index].fIPAddr = theIPAddrs[index];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UInt32 theNumPorts = 0;
|
|
UInt16* thePorts = GetRTSPPorts(inPrefs, &theNumPorts);
|
|
theTotalPortTrackers = theNumAddrs * theNumPorts;
|
|
thePortTrackers = NEW PortTracking[theTotalPortTrackers];
|
|
|
|
UInt32 currentIndex = 0;
|
|
|
|
for (index = 0; index < theNumAddrs; index++)
|
|
{
|
|
for (UInt32 portIndex = 0; portIndex < theNumPorts; portIndex++)
|
|
{
|
|
currentIndex = (theNumPorts * index) + portIndex;
|
|
|
|
thePortTrackers[currentIndex].fPort = thePorts[portIndex];
|
|
thePortTrackers[currentIndex].fIPAddr = theIPAddrs[index];
|
|
}
|
|
}
|
|
|
|
delete [] thePorts;
|
|
}
|
|
|
|
delete [] theIPAddrs;
|
|
//
|
|
// Now figure out which of these ports we are *already* listening on.
|
|
// If we already are listening on that port, just move the pointer to the
|
|
// listener over to the new array
|
|
TCPListenerSocket** newListenerArray = NEW TCPListenerSocket*[theTotalPortTrackers];
|
|
UInt32 curPortIndex = 0;
|
|
|
|
for (UInt32 count = 0; count < theTotalPortTrackers; count++)
|
|
{
|
|
for (UInt32 count2 = 0; count2 < fNumListeners; count2++)
|
|
{
|
|
if ((fListeners[count2]->GetLocalPort() == thePortTrackers[count].fPort) &&
|
|
(fListeners[count2]->GetLocalAddr() == thePortTrackers[count].fIPAddr))
|
|
{
|
|
thePortTrackers[count].fNeedsCreating = false;
|
|
newListenerArray[curPortIndex++] = fListeners[count2];
|
|
Assert(curPortIndex <= theTotalPortTrackers);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create any new listeners we need
|
|
for (UInt32 count3 = 0; count3 < theTotalPortTrackers; count3++)
|
|
{
|
|
if (thePortTrackers[count3].fNeedsCreating)
|
|
{
|
|
newListenerArray[curPortIndex] = NEW RTSPListenerSocket();
|
|
QTSS_Error err = newListenerArray[curPortIndex]->Initialize(thePortTrackers[count3].fIPAddr, thePortTrackers[count3].fPort);
|
|
|
|
char thePortStr[20];
|
|
qtss_sprintf(thePortStr, "%hu", thePortTrackers[count3].fPort);
|
|
|
|
//
|
|
// If there was an error creating this listener, destroy it and log an error
|
|
if ((startListeningNow) && (err != QTSS_NoErr))
|
|
delete newListenerArray[curPortIndex];
|
|
|
|
if (err == EADDRINUSE)
|
|
QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssListenPortInUse, 0, thePortStr);
|
|
else if (err == EACCES)
|
|
QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssListenPortAccessDenied, 0, thePortStr);
|
|
else if (err != QTSS_NoErr)
|
|
QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssListenPortError, 0, thePortStr);
|
|
else
|
|
{
|
|
//
|
|
// This listener was successfully created.
|
|
if (startListeningNow)
|
|
newListenerArray[curPortIndex]->RequestEvent(EV_RE);
|
|
curPortIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Kill any listeners that we no longer need
|
|
for (UInt32 count4 = 0; count4 < fNumListeners; count4++)
|
|
{
|
|
Bool16 deleteThisOne = true;
|
|
|
|
for (UInt32 count5 = 0; count5 < curPortIndex; count5++)
|
|
{
|
|
if (newListenerArray[count5] == fListeners[count4])
|
|
deleteThisOne = false;
|
|
}
|
|
|
|
if (deleteThisOne)
|
|
fListeners[count4]->Signal(Task::kKillEvent);
|
|
}
|
|
|
|
//
|
|
// Finally, make our server attributes and fListener privy to the new...
|
|
fListeners = newListenerArray;
|
|
fNumListeners = curPortIndex;
|
|
UInt32 portIndex = 0;
|
|
|
|
for (UInt32 count6 = 0; count6 < fNumListeners; count6++)
|
|
{
|
|
if (fListeners[count6]->GetLocalAddr() != INADDR_LOOPBACK)
|
|
{
|
|
UInt16 thePort = fListeners[count6]->GetLocalPort();
|
|
(void)this->SetValue(qtssSvrRTSPPorts, portIndex, &thePort, sizeof(thePort), QTSSDictionary::kDontObeyReadOnly);
|
|
portIndex++;
|
|
}
|
|
}
|
|
this->SetNumValues(qtssSvrRTSPPorts, portIndex);
|
|
|
|
delete [] thePortTrackers;
|
|
return (fNumListeners > 0);
|
|
}
|
|
|
|
UInt32* QTSServer::GetRTSPIPAddrs(QTSServerPrefs* inPrefs, UInt32* outNumAddrsPtr)
|
|
{
|
|
UInt32 numAddrs = inPrefs->GetNumValues(qtssPrefsRTSPIPAddr);
|
|
UInt32* theIPAddrArray = NULL;
|
|
|
|
if (numAddrs == 0)
|
|
{
|
|
*outNumAddrsPtr = 1;
|
|
theIPAddrArray = NEW UInt32[1];
|
|
theIPAddrArray[0] = INADDR_ANY;
|
|
}
|
|
else
|
|
{
|
|
theIPAddrArray = NEW UInt32[numAddrs + 1];
|
|
UInt32 arrIndex = 0;
|
|
|
|
for (UInt32 theIndex = 0; theIndex < numAddrs; theIndex++)
|
|
{
|
|
// Get the ip addr out of the prefs dictionary
|
|
QTSS_Error theErr = QTSS_NoErr;
|
|
|
|
char* theIPAddrStr = NULL;
|
|
theErr = inPrefs->GetValueAsString(qtssPrefsRTSPIPAddr, theIndex, &theIPAddrStr);
|
|
if (theErr != QTSS_NoErr)
|
|
{
|
|
delete [] theIPAddrStr;
|
|
break;
|
|
}
|
|
|
|
|
|
UInt32 theIPAddr = 0;
|
|
if (theIPAddrStr != NULL)
|
|
{
|
|
theIPAddr = SocketUtils::ConvertStringToAddr(theIPAddrStr);
|
|
delete [] theIPAddrStr;
|
|
|
|
if (theIPAddr != 0)
|
|
theIPAddrArray[arrIndex++] = theIPAddr;
|
|
}
|
|
}
|
|
|
|
if ((numAddrs == 1) && (arrIndex == 0))
|
|
theIPAddrArray[arrIndex++] = INADDR_ANY;
|
|
else
|
|
theIPAddrArray[arrIndex++] = INADDR_LOOPBACK;
|
|
|
|
*outNumAddrsPtr = arrIndex;
|
|
}
|
|
|
|
return theIPAddrArray;
|
|
}
|
|
|
|
UInt16* QTSServer::GetRTSPPorts(QTSServerPrefs* inPrefs, UInt32* outNumPortsPtr)
|
|
{
|
|
*outNumPortsPtr = inPrefs->GetNumValues(qtssPrefsRTSPPorts);
|
|
|
|
if (*outNumPortsPtr == 0)
|
|
return NULL;
|
|
|
|
UInt16* thePortArray = NEW UInt16[*outNumPortsPtr];
|
|
|
|
for (UInt32 theIndex = 0; theIndex < *outNumPortsPtr; theIndex++)
|
|
{
|
|
// Get the ip addr out of the prefs dictionary
|
|
UInt32 theLen = sizeof(UInt16);
|
|
QTSS_Error theErr = QTSS_NoErr;
|
|
theErr = inPrefs->GetValue(qtssPrefsRTSPPorts, theIndex, &thePortArray[theIndex], &theLen);
|
|
Assert(theErr == QTSS_NoErr);
|
|
}
|
|
|
|
return thePortArray;
|
|
}
|
|
|
|
Bool16 QTSServer::SetupUDPSockets()
|
|
{
|
|
//function finds all IP addresses on this machine, and binds 1 RTP / RTCP
|
|
//socket pair to a port pair on each address.
|
|
|
|
UInt32 theNumAllocatedPairs = 0;
|
|
for (UInt32 theNumPairs = 0; theNumPairs < SocketUtils::GetNumIPAddrs(); theNumPairs++)
|
|
{
|
|
UDPSocketPair* thePair = fSocketPool->CreateUDPSocketPair(SocketUtils::GetIPAddr(theNumPairs), 0);
|
|
if (thePair != NULL)
|
|
{
|
|
theNumAllocatedPairs++;
|
|
thePair->GetSocketA()->RequestEvent(EV_RE);
|
|
thePair->GetSocketB()->RequestEvent(EV_RE);
|
|
}
|
|
}
|
|
//only return an error if we couldn't allocate ANY pairs of sockets
|
|
if (theNumAllocatedPairs == 0)
|
|
{
|
|
fServerState = qtssFatalErrorState; // also set the state to fatal error
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Bool16 QTSServer::SwitchPersonality()
|
|
{
|
|
#ifndef __Win32__ //not supported
|
|
OSCharArrayDeleter runGroupName(fSrvrPrefs->GetRunGroupName());
|
|
OSCharArrayDeleter runUserName(fSrvrPrefs->GetRunUserName());
|
|
|
|
int groupID = 0;
|
|
|
|
if (::strlen(runGroupName.GetObject()) > 0)
|
|
{
|
|
struct group* gr = ::getgrnam(runGroupName.GetObject());
|
|
if (gr == NULL || ::setgid(gr->gr_gid) == -1)
|
|
{
|
|
char buffer[kErrorStrSize];
|
|
|
|
QTSSModuleUtils::LogError(qtssFatalVerbosity, qtssMsgCannotSetRunGroup, 0,
|
|
runGroupName.GetObject(), qtss_strerror(OSThread::GetErrno(), buffer, sizeof(buffer)));
|
|
return false;
|
|
}
|
|
groupID = gr->gr_gid;
|
|
}
|
|
|
|
if (::strlen(runUserName.GetObject()) > 0)
|
|
{
|
|
struct passwd* pw = ::getpwnam(runUserName.GetObject());
|
|
|
|
#if __MacOSX__
|
|
if (pw != NULL && groupID != 0) //call initgroups before doing a setuid
|
|
(void) initgroups(runUserName.GetObject(),groupID);
|
|
#endif
|
|
|
|
if (pw == NULL || ::setuid(pw->pw_uid) == -1)
|
|
{
|
|
QTSSModuleUtils::LogError(qtssFatalVerbosity, qtssMsgCannotSetRunUser, 0,
|
|
runUserName.GetObject(), strerror(OSThread::GetErrno()));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
void QTSServer::LoadCompiledInModules()
|
|
{
|
|
#ifndef DSS_DYNAMIC_MODULES_ONLY
|
|
// MODULE DEVELOPERS SHOULD ADD THE FOLLOWING THREE LINES OF CODE TO THIS
|
|
// FUNCTION IF THEIR MODULE IS BEING COMPILED INTO THE SERVER.
|
|
//
|
|
// QTSSModule* myModule = new QTSSModule("__MODULE_NAME__");
|
|
// (void)myModule->Initialize(&sCallbacks, &__MODULE_MAIN_ROUTINE__);
|
|
// (void)AddModule(myModule);
|
|
//
|
|
// The following modules are all compiled into the server.
|
|
|
|
QTSSModule* theFileModule = new QTSSModule("QTSSFileModule");
|
|
(void)theFileModule->SetupModule(&sCallbacks, &QTSSFileModule_Main);
|
|
(void)AddModule(theFileModule);
|
|
|
|
QTSSModule* theReflectorModule = new QTSSModule("QTSSReflectorModule");
|
|
(void)theReflectorModule->SetupModule(&sCallbacks, &QTSSReflectorModule_Main);
|
|
(void)AddModule(theReflectorModule);
|
|
|
|
QTSSModule* theRelayModule = new QTSSModule("QTSSRelayModule");
|
|
(void)theRelayModule->SetupModule(&sCallbacks, &QTSSRelayModule_Main);
|
|
(void)AddModule(theRelayModule);
|
|
|
|
QTSSModule* theAccessLog = new QTSSModule("QTSSAccessLogModule");
|
|
(void)theAccessLog->SetupModule(&sCallbacks, &QTSSAccessLogModule_Main);
|
|
(void)AddModule(theAccessLog);
|
|
|
|
QTSSModule* theFlowControl = new QTSSModule("QTSSFlowControlModule");
|
|
(void)theFlowControl->SetupModule(&sCallbacks, &QTSSFlowControlModule_Main);
|
|
(void)AddModule(theFlowControl);
|
|
|
|
QTSSModule* theFileSysModule = new QTSSModule("QTSSPosixFileSysModule");
|
|
(void)theFileSysModule->SetupModule(&sCallbacks, &QTSSPosixFileSysModule_Main);
|
|
(void)AddModule(theFileSysModule);
|
|
|
|
QTSSModule* theAdminModule = new QTSSModule("QTSSAdminModule");
|
|
(void)theAdminModule->SetupModule(&sCallbacks, &QTSSAdminModule_Main);
|
|
(void)AddModule(theAdminModule);
|
|
|
|
QTSSModule* theMP3StreamingModule = new QTSSModule("QTSSMP3StreamingModule");
|
|
(void)theMP3StreamingModule->SetupModule(&sCallbacks, &QTSSMP3StreamingModule_Main);
|
|
(void)AddModule(theMP3StreamingModule);
|
|
|
|
#if MEMORY_DEBUGGING
|
|
QTSSModule* theWebDebug = new QTSSModule("QTSSWebDebugModule");
|
|
(void)theWebDebug->SetupModule(&sCallbacks, &QTSSWebDebugModule_Main);
|
|
(void)AddModule(theWebDebug);
|
|
#endif
|
|
|
|
#if __MacOSX__
|
|
QTSSModule* theQTSSDSAuthModule = new QTSSModule("QTSSDSAuthModule");
|
|
(void)theQTSSDSAuthModule->SetupModule(&sCallbacks, &QTSSDSAuthModule_Main);
|
|
(void)AddModule(theQTSSDSAuthModule);
|
|
#endif
|
|
|
|
QTSSModule* theQTACCESSmodule = new QTSSModule("QTSSAccessModule");
|
|
(void)theQTACCESSmodule->SetupModule(&sCallbacks, &QTSSAccessModule_Main);
|
|
(void)AddModule(theQTACCESSmodule);
|
|
|
|
|
|
#endif //DSS_DYNAMIC_MODULES_ONLY
|
|
|
|
#ifdef PROXYSERVER
|
|
QTSSModule* theProxyModule = new QTSSModule("QTSSProxyModule");
|
|
(void)theProxyModule->SetupModule(&sCallbacks, &QTSSProxyModule_Main);
|
|
(void)AddModule(theProxyModule);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTSServer::InitCallbacks()
|
|
{
|
|
sCallbacks.addr[kNewCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_New;
|
|
sCallbacks.addr[kDeleteCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Delete;
|
|
sCallbacks.addr[kMillisecondsCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Milliseconds;
|
|
sCallbacks.addr[kConvertToUnixTimeCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_ConvertToUnixTime;
|
|
|
|
sCallbacks.addr[kAddRoleCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_AddRole;
|
|
sCallbacks.addr[kCreateObjectTypeCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_CreateObjectType;
|
|
sCallbacks.addr[kAddAttributeCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_AddAttribute;
|
|
sCallbacks.addr[kIDForTagCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_IDForAttr;
|
|
sCallbacks.addr[kGetAttributePtrByIDCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_GetValuePtr;
|
|
sCallbacks.addr[kGetAttributeByIDCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_GetValue;
|
|
sCallbacks.addr[kSetAttributeByIDCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_SetValue;
|
|
sCallbacks.addr[kCreateObjectValueCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_CreateObject;
|
|
sCallbacks.addr[kGetNumValuesCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_GetNumValues;
|
|
|
|
sCallbacks.addr[kWriteCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Write;
|
|
sCallbacks.addr[kWriteVCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_WriteV;
|
|
sCallbacks.addr[kFlushCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Flush;
|
|
sCallbacks.addr[kReadCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Read;
|
|
sCallbacks.addr[kSeekCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Seek;
|
|
sCallbacks.addr[kAdviseCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Advise;
|
|
|
|
sCallbacks.addr[kAddServiceCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_AddService;
|
|
sCallbacks.addr[kIDForServiceCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_IDForService;
|
|
sCallbacks.addr[kDoServiceCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_DoService;
|
|
|
|
sCallbacks.addr[kSendRTSPHeadersCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_SendRTSPHeaders;
|
|
sCallbacks.addr[kAppendRTSPHeadersCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_AppendRTSPHeader;
|
|
sCallbacks.addr[kSendStandardRTSPCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_SendStandardRTSPResponse;
|
|
|
|
sCallbacks.addr[kAddRTPStreamCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_AddRTPStream;
|
|
sCallbacks.addr[kPlayCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Play;
|
|
sCallbacks.addr[kPauseCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Pause;
|
|
sCallbacks.addr[kTeardownCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Teardown;
|
|
sCallbacks.addr[kRefreshTimeOutCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_RefreshTimeOut;
|
|
|
|
sCallbacks.addr[kRequestEventCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_RequestEvent;
|
|
sCallbacks.addr[kSetIdleTimerCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_SetIdleTimer;
|
|
sCallbacks.addr[kSignalStreamCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_SignalStream;
|
|
|
|
sCallbacks.addr[kOpenFileObjectCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_OpenFileObject;
|
|
sCallbacks.addr[kCloseFileObjectCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_CloseFileObject;
|
|
|
|
sCallbacks.addr[kCreateSocketStreamCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_CreateStreamFromSocket;
|
|
sCallbacks.addr[kDestroySocketStreamCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_DestroySocketStream;
|
|
|
|
sCallbacks.addr[kAddStaticAttributeCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_AddStaticAttribute;
|
|
sCallbacks.addr[kAddInstanceAttributeCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_AddInstanceAttribute;
|
|
sCallbacks.addr[kRemoveInstanceAttributeCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_RemoveInstanceAttribute;
|
|
|
|
sCallbacks.addr[kGetAttrInfoByIndexCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_GetAttrInfoByIndex;
|
|
sCallbacks.addr[kGetAttrInfoByNameCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_GetAttrInfoByName;
|
|
sCallbacks.addr[kGetAttrInfoByIDCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_GetAttrInfoByID;
|
|
sCallbacks.addr[kGetNumAttributesCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_GetNumAttributes;
|
|
|
|
|
|
sCallbacks.addr[kGetValueAsStringCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_GetValueAsString;
|
|
sCallbacks.addr[kTypeToTypeStringCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_TypeToTypeString;
|
|
sCallbacks.addr[kTypeStringToTypeCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_TypeStringToType;
|
|
sCallbacks.addr[kStringToValueCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_StringToValue;
|
|
sCallbacks.addr[kValueToStringCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_ValueToString;
|
|
|
|
sCallbacks.addr[kRemoveValueCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_RemoveValue;
|
|
|
|
sCallbacks.addr[kRequestGlobalLockCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_RequestLockedCallback;
|
|
sCallbacks.addr[kIsGlobalLockedCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_IsGlobalLocked;
|
|
sCallbacks.addr[kUnlockGlobalLock] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_UnlockGlobalLock;
|
|
|
|
sCallbacks.addr[kAuthenticateCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Authenticate;
|
|
sCallbacks.addr[kAuthorizeCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_Authorize;
|
|
|
|
sCallbacks.addr[kLockObjectCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_LockObject;
|
|
sCallbacks.addr[kUnlockObjectCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_UnlockObject;
|
|
sCallbacks.addr[kSetAttributePtrCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_SetValuePtr;
|
|
|
|
sCallbacks.addr[kSetIntervalRoleTimerCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_SetIdleRoleTimer;
|
|
|
|
sCallbacks.addr[kLockStdLibCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_LockStdLib;
|
|
sCallbacks.addr[kUnlockStdLibCallback] = (QTSS_CallbackProcPtr)QTSSCallbacks::QTSS_UnlockStdLib;
|
|
}
|
|
|
|
void QTSServer::LoadModules(QTSServerPrefs* inPrefs)
|
|
{
|
|
// Fetch the name of the module directory and open it.
|
|
OSCharArrayDeleter theModDirName(inPrefs->GetModuleDirectory());
|
|
|
|
#ifdef __Win32__
|
|
// NT doesn't seem to have support for the POSIX directory parsing APIs.
|
|
OSCharArrayDeleter theLargeModDirName(NEW char[::strlen(theModDirName.GetObject()) + 3]);
|
|
::strcpy(theLargeModDirName.GetObject(), theModDirName.GetObject());
|
|
::strcat(theLargeModDirName.GetObject(), "\\*");
|
|
|
|
WIN32_FIND_DATA theFindData;
|
|
HANDLE theSearchHandle = ::FindFirstFile(theLargeModDirName.GetObject(), &theFindData);
|
|
|
|
if (theSearchHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgNoModuleFolder, 0);
|
|
return;
|
|
}
|
|
|
|
while (theSearchHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
this->CreateModule(theModDirName.GetObject(), theFindData.cFileName);
|
|
|
|
if (!::FindNextFile(theSearchHandle, &theFindData))
|
|
{
|
|
::FindClose(theSearchHandle);
|
|
theSearchHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
#else
|
|
|
|
// POSIX version
|
|
// opendir mallocs memory for DIR* so call closedir to free the allocated memory
|
|
DIR* theDir = ::opendir(theModDirName.GetObject());
|
|
if (theDir == NULL)
|
|
{
|
|
QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgNoModuleFolder, 0);
|
|
return;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
// Iterate over each file in the directory, attempting to construct
|
|
// a module object from that file.
|
|
|
|
struct dirent* theFile = ::readdir(theDir);
|
|
if (theFile == NULL)
|
|
break;
|
|
|
|
this->CreateModule(theModDirName.GetObject(), theFile->d_name);
|
|
}
|
|
|
|
(void)::closedir(theDir);
|
|
|
|
#endif
|
|
}
|
|
|
|
void QTSServer::CreateModule(char* inModuleFolderPath, char* inModuleName)
|
|
{
|
|
// Ignore these silly directory names
|
|
|
|
if (::strcmp(inModuleName, ".") == 0)
|
|
return;
|
|
if (::strcmp(inModuleName, "..") == 0)
|
|
return;
|
|
if (::strlen(inModuleName) == 0)
|
|
return;
|
|
if (*inModuleName == '.')
|
|
return; // Fix 2572248. Do not attempt to load '.' files as modules at all
|
|
|
|
//
|
|
// Construct a full path to this module
|
|
UInt32 totPathLen = ::strlen(inModuleFolderPath) + ::strlen(inModuleName);
|
|
OSCharArrayDeleter theModPath(NEW char[totPathLen + 4]);
|
|
::strcpy(theModPath.GetObject(), inModuleFolderPath);
|
|
::strcat(theModPath.GetObject(), kPathDelimiterString);
|
|
::strcat(theModPath.GetObject(), inModuleName);
|
|
|
|
//
|
|
// Construct a QTSSModule object, and attempt to initialize the module
|
|
QTSSModule* theNewModule = NEW QTSSModule(inModuleName, theModPath.GetObject());
|
|
QTSS_Error theErr = theNewModule->SetupModule(&sCallbacks);
|
|
|
|
if (theErr != QTSS_NoErr)
|
|
{
|
|
QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgBadModule, theErr,
|
|
inModuleName);
|
|
delete theNewModule;
|
|
}
|
|
//
|
|
// If the module was successfully initialized, add it to our module queue
|
|
else if (!this->AddModule(theNewModule))
|
|
{
|
|
QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgRegFailed, theErr,
|
|
inModuleName);
|
|
delete theNewModule;
|
|
}
|
|
}
|
|
|
|
Bool16 QTSServer::AddModule(QTSSModule* inModule)
|
|
{
|
|
Assert(inModule->IsInitialized());
|
|
|
|
// Prepare to invoke the module's Register role. Setup the Register param block
|
|
QTSS_ModuleState theModuleState;
|
|
|
|
theModuleState.curModule = inModule;
|
|
theModuleState.curRole = QTSS_Register_Role;
|
|
theModuleState.curTask = NULL;
|
|
OSThread::SetMainThreadData(&theModuleState);
|
|
|
|
// Currently we do nothing with the module name
|
|
QTSS_RoleParams theRegParams;
|
|
theRegParams.regParams.outModuleName[0] = 0;
|
|
|
|
// If the module returns an error from the QTSS_Register role, don't put it anywhere
|
|
if (inModule->CallDispatch(QTSS_Register_Role, &theRegParams) != QTSS_NoErr)
|
|
return false;
|
|
|
|
OSThread::SetMainThreadData(NULL);
|
|
|
|
//
|
|
// Update the module name to reflect what was returned from the register role
|
|
theRegParams.regParams.outModuleName[QTSS_MAX_MODULE_NAME_LENGTH - 1] = 0;
|
|
if (theRegParams.regParams.outModuleName[0] != 0)
|
|
inModule->SetValue(qtssModName, 0, theRegParams.regParams.outModuleName, ::strlen(theRegParams.regParams.outModuleName), false);
|
|
|
|
//
|
|
// Give the module object a prefs dictionary. Instance attributes are allowed for these objects.
|
|
QTSSPrefs* thePrefs = NEW QTSSPrefs( sPrefsSource, inModule->GetValue(qtssModName), QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kModulePrefsDictIndex), true);
|
|
thePrefs->RereadPreferences();
|
|
inModule->SetPrefsDict(thePrefs);
|
|
|
|
//
|
|
// Add this module to the array of module (dictionaries)
|
|
UInt32 theNumModules = this->GetNumValues(qtssSvrModuleObjects);
|
|
QTSS_Error theErr = this->SetValue(qtssSvrModuleObjects, theNumModules, &inModule, sizeof(QTSSModule*), QTSSDictionary::kDontObeyReadOnly);
|
|
Assert(theErr == QTSS_NoErr);
|
|
|
|
//
|
|
// Add this module to the module queue
|
|
sModuleQueue.EnQueue(inModule->GetQueueElem());
|
|
|
|
return true;
|
|
}
|
|
|
|
void QTSServer::BuildModuleRoleArrays()
|
|
{
|
|
OSQueueIter theIter(&sModuleQueue);
|
|
QTSSModule* theModule = NULL;
|
|
|
|
// Make sure these variables are cleaned out in case they've already been inited.
|
|
|
|
DestroyModuleRoleArrays();
|
|
|
|
// Loop through all the roles of all the modules, recording the number of
|
|
// modules in each role, and also recording which modules are doing what.
|
|
|
|
for (UInt32 x = 0; x < QTSSModule::kNumRoles; x++)
|
|
{
|
|
sNumModulesInRole[x] = 0;
|
|
for (theIter.Reset(); !theIter.IsDone(); theIter.Next())
|
|
{
|
|
theModule = (QTSSModule*)theIter.GetCurrent()->GetEnclosingObject();
|
|
if (theModule->RunsInRole(x))
|
|
sNumModulesInRole[x] += 1;
|
|
}
|
|
|
|
if (sNumModulesInRole[x] > 0)
|
|
{
|
|
UInt32 moduleIndex = 0;
|
|
sModuleArray[x] = new QTSSModule*[sNumModulesInRole[x] + 1];
|
|
for (theIter.Reset(); !theIter.IsDone(); theIter.Next())
|
|
{
|
|
theModule = (QTSSModule*)theIter.GetCurrent()->GetEnclosingObject();
|
|
if (theModule->RunsInRole(x))
|
|
{
|
|
sModuleArray[x][moduleIndex] = theModule;
|
|
moduleIndex++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void QTSServer::DestroyModuleRoleArrays()
|
|
{
|
|
for (UInt32 x = 0; x < QTSSModule::kNumRoles; x++)
|
|
{
|
|
sNumModulesInRole[x] = 0;
|
|
if (sModuleArray[x] != NULL)
|
|
delete [] sModuleArray[x];
|
|
sModuleArray[x] = NULL;
|
|
}
|
|
}
|
|
|
|
void QTSServer::DoInitRole()
|
|
{
|
|
QTSS_RoleParams theInitParams;
|
|
theInitParams.initParams.inServer = this;
|
|
theInitParams.initParams.inPrefs = fSrvrPrefs;
|
|
theInitParams.initParams.inMessages = fSrvrMessages;
|
|
theInitParams.initParams.inErrorLogStream = &sErrorLogStream;
|
|
|
|
QTSS_ModuleState theModuleState;
|
|
theModuleState.curRole = QTSS_Initialize_Role;
|
|
theModuleState.curTask = NULL;
|
|
OSThread::SetMainThreadData(&theModuleState);
|
|
|
|
//
|
|
// Add the OPTIONS method as the one method the server handles by default (it handles
|
|
// it internally). Modules that handle other RTSP methods will add
|
|
QTSS_RTSPMethod theOptionsMethod = qtssOptionsMethod;
|
|
(void)this->SetValue(qtssSvrHandledMethods, 0, &theOptionsMethod, sizeof(theOptionsMethod));
|
|
|
|
|
|
// For now just disable the SetParameter to be compatible with Real. It should really be removed only for clients that have problems with their SetParameter implementations like (Real Players).
|
|
// At the moment it isn't necesary to add the option.
|
|
// QTSS_RTSPMethod theSetParameterMethod = qtssSetParameterMethod;
|
|
// (void)this->SetValue(qtssSvrHandledMethods, 0, &theSetParameterMethod, sizeof(theSetParameterMethod));
|
|
|
|
for (UInt32 x = 0; x < QTSServerInterface::GetNumModulesInRole(QTSSModule::kInitializeRole); x++)
|
|
{
|
|
QTSSModule* theModule = QTSServerInterface::GetModule(QTSSModule::kInitializeRole, x);
|
|
theInitParams.initParams.inModule = theModule;
|
|
theModuleState.curModule = theModule;
|
|
QTSS_Error theErr = theModule->CallDispatch(QTSS_Initialize_Role, &theInitParams);
|
|
|
|
if (theErr != QTSS_NoErr)
|
|
{
|
|
// If the module reports an error when initializing itself,
|
|
// delete the module and pretend it was never there.
|
|
QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgInitFailed, theErr,
|
|
theModule->GetValue(qtssModName)->Ptr);
|
|
|
|
sModuleQueue.Remove(theModule->GetQueueElem());
|
|
delete theModule;
|
|
}
|
|
}
|
|
this->SetupPublicHeader();
|
|
|
|
OSThread::SetMainThreadData(NULL);
|
|
}
|
|
|
|
void QTSServer::SetupPublicHeader()
|
|
{
|
|
//
|
|
// After the Init role, all the modules have reported the methods that they handle.
|
|
// So, we can prune this attribute for duplicates, and construct a string to use in the
|
|
// Public: header of the OPTIONS response
|
|
QTSS_RTSPMethod* theMethod = NULL;
|
|
UInt32 theLen = 0;
|
|
|
|
Bool16 theUniqueMethods[qtssNumMethods + 1];
|
|
::memset(theUniqueMethods, 0, sizeof(theUniqueMethods));
|
|
|
|
for (UInt32 y = 0; this->GetValuePtr(qtssSvrHandledMethods, y, (void**)&theMethod, &theLen) == QTSS_NoErr; y++)
|
|
theUniqueMethods[*theMethod] = true;
|
|
|
|
// Rewrite the qtssSvrHandledMethods, eliminating any duplicates that modules may have introduced
|
|
UInt32 uniqueMethodCount = 0;
|
|
for (QTSS_RTSPMethod z = 0; z < qtssNumMethods; z++)
|
|
{
|
|
if (theUniqueMethods[z])
|
|
this->SetValue(qtssSvrHandledMethods, uniqueMethodCount++, &z, sizeof(QTSS_RTSPMethod));
|
|
}
|
|
this->SetNumValues(qtssSvrHandledMethods, uniqueMethodCount);
|
|
|
|
// Format a text string for the Public: header
|
|
ResizeableStringFormatter theFormatter(NULL, 0);
|
|
|
|
for (UInt32 a = 0; this->GetValuePtr(qtssSvrHandledMethods, a, (void**)&theMethod, &theLen) == QTSS_NoErr; a++)
|
|
{
|
|
sPublicHeaderFormatter.Put(RTSPProtocol::GetMethodString(*theMethod));
|
|
sPublicHeaderFormatter.Put(", ");
|
|
}
|
|
sPublicHeaderStr.Ptr = sPublicHeaderFormatter.GetBufPtr();
|
|
sPublicHeaderStr.Len = sPublicHeaderFormatter.GetBytesWritten() - 2; //trunc the last ", "
|
|
}
|
|
|
|
|
|
Task* RTSPListenerSocket::GetSessionTask(TCPSocket** outSocket)
|
|
{
|
|
Assert(outSocket != NULL);
|
|
|
|
// when the server is behing a round robin DNS, the client needs to knwo the IP address ot the server
|
|
// so that it can direct the "POST" half of the connection to the same machine when tunnelling RTSP thru HTTP
|
|
Bool16 doReportHTTPConnectionAddress = QTSServerInterface::GetServer()->GetPrefs()->GetDoReportHTTPConnectionAddress();
|
|
|
|
RTSPSession* theTask = NEW RTSPSession(doReportHTTPConnectionAddress);
|
|
*outSocket = theTask->GetSocket(); // out socket is not attached to a unix socket yet.
|
|
|
|
if (this->OverMaxConnections(0))
|
|
this->SlowDown();
|
|
else
|
|
this->RunNormal();
|
|
|
|
return theTask;
|
|
}
|
|
|
|
|
|
Bool16 RTSPListenerSocket::OverMaxConnections(UInt32 buffer)
|
|
{
|
|
QTSServerInterface* theServer = QTSServerInterface::GetServer();
|
|
SInt32 maxConns = theServer->GetPrefs()->GetMaxConnections();
|
|
Bool16 overLimit = false;
|
|
|
|
if (maxConns > -1) // limit connections
|
|
{
|
|
maxConns += buffer;
|
|
if ( (theServer->GetNumRTPSessions() > (UInt32) maxConns)
|
|
||
|
|
( theServer->GetNumRTSPSessions() + theServer->GetNumRTSPHTTPSessions() > (UInt32) maxConns )
|
|
)
|
|
{
|
|
overLimit = true;
|
|
}
|
|
}
|
|
return overLimit;
|
|
|
|
}
|
|
|
|
|
|
UDPSocketPair* RTPSocketPool::ConstructUDPSocketPair()
|
|
{
|
|
Task* theTask = ((QTSServer*)QTSServerInterface::GetServer())->fRTCPTask;
|
|
|
|
//construct a pair of UDP sockets, the lower one for RTP data (outgoing only, no demuxer
|
|
//necessary), and one for RTCP data (incoming, so definitely need a demuxer).
|
|
//These are nonblocking sockets that DON'T receive events (we are going to poll for data)
|
|
// They do receive events - we don't poll from them anymore
|
|
return NEW
|
|
UDPSocketPair( NEW UDPSocket(theTask, Socket::kNonBlockingSocketType),
|
|
NEW UDPSocket(theTask, UDPSocket::kWantsDemuxer | Socket::kNonBlockingSocketType));
|
|
}
|
|
|
|
void RTPSocketPool::DestructUDPSocketPair(UDPSocketPair* inPair)
|
|
{
|
|
delete inPair->GetSocketA();
|
|
delete inPair->GetSocketB();
|
|
delete inPair;
|
|
}
|
|
|
|
void RTPSocketPool::SetUDPSocketOptions(UDPSocketPair* inPair)
|
|
{
|
|
// Apparently the socket buffer size matters even though this is UDP and being
|
|
// used for sending... on UNIX typically the socket buffer size doesn't matter because the
|
|
// packet goes right down to the driver. On Win32 and linux, unless this is really big, we get packet loss.
|
|
inPair->GetSocketA()->SetSocketBufSize(256 * 1024);
|
|
|
|
//
|
|
// Always set the Rcv buf size for the RTCP sockets. This is important because the
|
|
// server is going to be getting many many acks.
|
|
UInt32 theRcvBufSize = QTSServerInterface::GetServer()->GetPrefs()->GetRTCPSocketRcvBufSizeinK();
|
|
|
|
//
|
|
// In case the rcv buf size is too big for the system, retry, dividing the requested size by 2.
|
|
// Until it works, or until some minimum value is reached.
|
|
OS_Error theErr = EINVAL;
|
|
while ((theErr != OS_NoErr) && (theRcvBufSize > 32))
|
|
{
|
|
theErr = inPair->GetSocketB()->SetSocketRcvBufSize(theRcvBufSize * 1024);
|
|
if (theErr != OS_NoErr)
|
|
theRcvBufSize >>= 1;
|
|
}
|
|
|
|
//
|
|
// Report an error if we couldn't set the socket buffer size the user requested
|
|
if (theRcvBufSize != QTSServerInterface::GetServer()->GetPrefs()->GetRTCPSocketRcvBufSizeinK())
|
|
{
|
|
char theRcvBufSizeStr[20];
|
|
qtss_sprintf(theRcvBufSizeStr, "%"_U32BITARG_"", theRcvBufSize);
|
|
//
|
|
// For now, do not log an error, though we should enable this in the future.
|
|
//QTSSModuleUtils::LogError(qtssWarningVerbosity, qtssMsgSockBufSizesTooLarge, theRcvBufSizeStr);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
QTSS_Error QTSServer::RereadPrefsService(QTSS_ServiceFunctionArgsPtr /*inArgs*/)
|
|
{
|
|
//
|
|
// This function can only be called safely when the server is completely running.
|
|
// Ensuring this is a bit complicated because of preemption. Here's how it's done...
|
|
|
|
QTSServerInterface* theServer = QTSServerInterface::GetServer();
|
|
|
|
// This is to make sure this function isn't being called before the server is
|
|
// completely started up.
|
|
if ((theServer == NULL) || (theServer->GetServerState() != qtssRunningState))
|
|
return QTSS_OutOfState;
|
|
|
|
// Because the server must have started up, and because this object always stays
|
|
// around (until the process dies), we can now safely get this object.
|
|
QTSServerPrefs* thePrefs = theServer->GetPrefs();
|
|
|
|
// Grab the prefs mutex. We want to make sure that calls to RereadPrefsService
|
|
// are serialized. This also prevents the server from shutting down while in
|
|
// this function, because the QTSServer destructor grabs this mutex as well.
|
|
OSMutexLocker locker(thePrefs->GetMutex());
|
|
|
|
// Finally, check the server state again. The state may have changed
|
|
// to qtssShuttingDownState or qtssFatalErrorState in this time, though
|
|
// at this point we have the prefs mutex, so we are guarenteed that the
|
|
// server can't actually shut down anymore
|
|
if (theServer->GetServerState() != qtssRunningState)
|
|
return QTSS_OutOfState;
|
|
|
|
// Ok, we're ready to reread preferences now.
|
|
|
|
//
|
|
// Reread preferences
|
|
sPrefsSource->Parse();
|
|
thePrefs->RereadServerPreferences(true);
|
|
|
|
{
|
|
//
|
|
// Update listeners, ports, and IP addrs.
|
|
OSMutexLocker locker(theServer->GetServerObjectMutex());
|
|
(void)((QTSServer*)theServer)->SetDefaultIPAddr();
|
|
(void)((QTSServer*)theServer)->CreateListeners(true, thePrefs, 0);
|
|
}
|
|
|
|
// Delete all the streams
|
|
QTSSModule** theModule = NULL;
|
|
UInt32 theLen = 0;
|
|
|
|
for (int y = 0; QTSServerInterface::GetServer()->GetValuePtr(qtssSvrModuleObjects, y, (void**)&theModule, &theLen) == QTSS_NoErr; y++)
|
|
{
|
|
Assert(theModule != NULL);
|
|
Assert(theLen == sizeof(QTSSModule*));
|
|
|
|
(*theModule)->GetPrefsDict()->RereadPreferences();
|
|
|
|
#if DEBUG
|
|
theModule = NULL;
|
|
theLen = 0;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Go through each module's prefs object and have those reread as well
|
|
|
|
//
|
|
// Now that we are done rereading the prefs, invoke all modules in the RereadPrefs
|
|
// role so they can update their internal prefs caches.
|
|
for (UInt32 x = 0; x < QTSServerInterface::GetNumModulesInRole(QTSSModule::kRereadPrefsRole); x++)
|
|
{
|
|
QTSSModule* theModule = QTSServerInterface::GetModule(QTSSModule::kRereadPrefsRole, x);
|
|
(void)theModule->CallDispatch(QTSS_RereadPrefs_Role, NULL);
|
|
}
|
|
return QTSS_NoErr;
|
|
}
|
|
|
|
|