Darwin-Streaming-Server/Server.tproj/win32main.cpp

523 lines
18 KiB
C++
Raw Normal View History

/*
*
* @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: win32main.cpp
Contains: main function to drive streaming server on win32.
*/
#include "getopt.h"
#include "FilePrefsSource.h"
#include "RunServer.h"
#include "QTSServer.h"
#include "QTSSExpirationDate.h"
#include "GenerateXMLPrefs.h"
//
// Data
static FilePrefsSource sPrefsSource(true); // Allow dups
static XMLPrefsParser* sXMLParser = NULL;
static FilePrefsSource sMessagesSource;
static UInt16 sPort = 0; //port can be set on the command line
static int sStatsUpdateInterval = 0;
static SERVICE_STATUS_HANDLE sServiceStatusHandle = 0;
static QTSS_ServerState sInitialState = qtssRunningState;
//
// Functions
static void ReportStatus(DWORD inCurrentState, DWORD inExitCode);
static void InstallService(char* inServiceName);
static void RemoveService(char *inServiceName);
static void RunAsService(char* inServiceName);
void WINAPI ServiceControl(DWORD);
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
int main(int argc, char * argv[])
{
extern char* optarg;
//First thing to do is to read command-line arguments.
int ch;
char* theConfigFilePath = "c:\\Program Files\\Darwin Streaming Server\\streamingserver.cfg";
char* theXMLFilePath = "c:\\Program Files\\Darwin Streaming Server\\streamingserver.xml";
Bool16 notAService = false;
Bool16 theXMLPrefsExist = true;
Bool16 dontFork = false;
#if _DEBUG
char* compileType = "Compile_Flags/_DEBUG; ";
#else
char* compileType = "";
#endif
while ((ch = getopt(argc,argv, "vdxp:o:c:irsS:I")) != EOF) // opt: means requires option
{
switch(ch)
{
case 'v':
qtss_printf("%s/%s ( Build/%s; Platform/%s; %s%s) Built on: %s\n",QTSServerInterface::GetServerName().Ptr,
QTSServerInterface::GetServerVersion().Ptr,
QTSServerInterface::GetServerBuild().Ptr,
QTSServerInterface::GetServerPlatform().Ptr,
compileType,
QTSServerInterface::GetServerComment().Ptr,
QTSServerInterface::GetServerBuildDate().Ptr);
qtss_printf("usage: %s [ -d | -p port | -v | -c /myconfigpath.xml | -o /myconfigpath.conf | -x | -S numseconds | -I | -h ]\n", QTSServerInterface::GetServerName().Ptr);
qtss_printf("-d: Don't run as a Win32 Service\n");
qtss_printf("-p XXX: Specify the default RTSP listening port of the server\n");
qtss_printf("-c c:\\myconfigpath.xml: Specify a config file path\n");
qtss_printf("-o c:\\myconfigpath.conf: Specify a DSS 1.x / 2.x config file path\n");
qtss_printf("-x: Force create new .xml config file from 1.x / 2.x config\n");
qtss_printf("-i: Install the Darwin Streaming Server service\n");
qtss_printf("-r: Remove the Darwin Streaming Server service\n");
qtss_printf("-s: Start the Darwin Streaming Server service\n");
qtss_printf("-S n: Display server stats in the console every \"n\" seconds\n");
qtss_printf("-I: Start the server in the idle state\n");
::exit(0);
case 'd':
notAService = true;
break;
case 'p':
Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
sPort = ::atoi(optarg);
break;
case 'c':
Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
theXMLFilePath = optarg;
break;
case 'S':
Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
sStatsUpdateInterval = ::atoi(optarg);
break;
case 'o':
Assert(optarg != NULL);// this means we didn't declare getopt options correctly or there is a bug in getopt.
theConfigFilePath = optarg;
break;
case 'x':
theXMLPrefsExist = false; // Force us to generate a new XML prefs file
break;
case 'i':
qtss_printf("Installing the Darwin Streaming Server service...\n");
::InstallService("Darwin Streaming Server");
qtss_printf("Starting the Darwin Streaming Server service...\n");
::RunAsService("Darwin Streaming Server");
::exit(0);
break;
case 'r':
qtss_printf("Removing the Darwin Streaming Server service...\n");
::RemoveService("Darwin Streaming Server");
::exit(0);
case 's':
qtss_printf("Starting the Darwin Streaming Server service...\n");
::RunAsService("Darwin Streaming Server");
::exit(0);
case 'I':
sInitialState = qtssIdleState;
break;
default:
break;
}
}
//
// Check expiration date
QTSSExpirationDate::PrintExpirationDate();
if (QTSSExpirationDate::IsSoftwareExpired())
{
qtss_printf("Streaming Server has expired\n");
::exit(0);
}
//
// Create an XML prefs parser object using the specified path
sXMLParser = new XMLPrefsParser(theXMLFilePath);
//
// Check to see if the XML file exists as a directory. If it does,
// just bail because we do not want to overwrite a directory
if (sXMLParser->DoesFileExistAsDirectory())
{
qtss_printf("Directory located at location where streaming server prefs file should be.\n");
::exit(0);
}
if (!sXMLParser->CanWriteFile())
{
qtss_printf("Cannot write to the streaming server prefs file.\n");
::exit(0);
}
// If we aren't forced to create a new XML prefs file, whether
// we do or not depends solely on whether the XML prefs file exists currently.
if (theXMLPrefsExist)
theXMLPrefsExist = sXMLParser->DoesFileExist();
if (!theXMLPrefsExist)
{
//
//Construct a Prefs Source object to get server preferences
int prefsErr = sPrefsSource.InitFromConfigFile(theConfigFilePath);
if ( prefsErr )
qtss_printf("Could not load configuration file at %s.\n Generating a new prefs file at %s\n", theConfigFilePath, theXMLFilePath);
//
// Generate a brand-new XML prefs file out of the old prefs
int xmlGenerateErr = GenerateAllXMLPrefs(&sPrefsSource, sXMLParser);
if (xmlGenerateErr)
{
qtss_printf("Fatal Error: Could not create new prefs file at: %s. (%d)\n", theConfigFilePath, OSThread::GetErrno());
::exit(-1);
}
}
//
// Parse the configs from the XML file
int xmlParseErr = sXMLParser->Parse();
if (xmlParseErr)
{
qtss_printf("Fatal Error: Could not load configuration file at %s. (%d)\n", theXMLFilePath, OSThread::GetErrno());
::exit(-1);
}
//
// Construct a messages source object
sMessagesSource.InitFromConfigFile("qtssmessages.txt");
//
// Start Win32 DLLs
WORD wsVersion = MAKEWORD(1, 1);
WSADATA wsData;
(void)::WSAStartup(wsVersion, &wsData);
if (notAService)
{
// If we're running off the command-line, don't do the service initiation crap.
::StartServer(sXMLParser, &sMessagesSource, sPort, sStatsUpdateInterval, sInitialState, false,0, kRunServerDebug_Off); // No stats update interval for now
::RunServer();
::exit(0);
}
SERVICE_TABLE_ENTRY dispatchTable[] =
{
{ "", ServiceMain },
{ NULL, NULL }
};
//
// In case someone runs the server improperly, print out a friendly message.
qtss_printf("Darwin Streaming Server must either be started from the DOS Console\n");
qtss_printf("using the -d command-line option, or using the Service Control Manager\n\n");
qtss_printf("Waiting for the Service Control Manager to start Darwin Streaming Server...\n");
BOOL theErr = ::StartServiceCtrlDispatcher(dispatchTable);
if (!theErr)
{
qtss_printf("Fatal Error: Couldn't start Service\n");
::exit(-1);
}
return (0);
}
void __stdcall ServiceMain(DWORD /*argc*/, LPTSTR *argv)
{
char* theServerName = argv[0];
sServiceStatusHandle = ::RegisterServiceCtrlHandler( theServerName, &ServiceControl );
if (sServiceStatusHandle == 0)
{
qtss_printf("Failure registering service handler");
return;
}
//
// Report our status
::ReportStatus( SERVICE_START_PENDING, NO_ERROR );
//
// Start & Run the server - no stats update interval for now
if (::StartServer(sXMLParser, &sMessagesSource, sPort, sStatsUpdateInterval, sInitialState, false,0, kRunServerDebug_Off) != qtssFatalErrorState)
{
::ReportStatus( SERVICE_RUNNING, NO_ERROR );
::RunServer(); // This function won't return until the server has died
//
// Ok, server is done...
::ReportStatus( SERVICE_STOPPED, NO_ERROR );
}
else
::ReportStatus( SERVICE_STOPPED, ERROR_BAD_COMMAND ); // I dunno... report some error
}
void WINAPI ServiceControl(DWORD inControlCode)
{
QTSS_ServerState theState;
QTSServerInterface* theServer = QTSServerInterface::GetServer();
DWORD theStatusReport = SERVICE_START_PENDING;
if (theServer != NULL)
theState = theServer->GetServerState();
else
theState = qtssStartingUpState;
switch(inControlCode)
{
// Stop the service.
//
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
{
if (theState == qtssStartingUpState)
break;
//
// Signal the server to shut down.
theState = qtssShuttingDownState;
if (theServer != NULL)
theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
break;
}
case SERVICE_CONTROL_PAUSE:
{
if (theState != qtssRunningState)
break;
//
// Signal the server to refuse new connections.
theState = qtssRefusingConnectionsState;
if (theServer != NULL)
theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
break;
}
case SERVICE_CONTROL_CONTINUE:
{
if (theState != qtssRefusingConnectionsState)
break;
//
// Signal the server to refuse new connections.
theState = qtssRefusingConnectionsState;
if (theServer != NULL)
theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
break;
}
case SERVICE_CONTROL_INTERROGATE:
break; // Just update our status
default:
break;
}
if (theServer != NULL)
{
theState = theServer->GetServerState();
//
// Convert a QTSS state to a Win32 Service state
switch (theState)
{
case qtssStartingUpState: theStatusReport = SERVICE_START_PENDING; break;
case qtssRunningState: theStatusReport = SERVICE_RUNNING; break;
case qtssRefusingConnectionsState: theStatusReport = SERVICE_PAUSED; break;
case qtssFatalErrorState: theStatusReport = SERVICE_STOP_PENDING; break;
case qtssShuttingDownState: theStatusReport = SERVICE_STOP_PENDING; break;
default: theStatusReport = SERVICE_RUNNING; break;
}
}
else
theStatusReport = SERVICE_START_PENDING;
qtss_printf("Reporting status from ServiceControl function\n");
::ReportStatus(theStatusReport, NO_ERROR);
}
void ReportStatus(DWORD inCurrentState, DWORD inExitCode)
{
static Bool16 sFirstTime = 1;
static UInt32 sCheckpoint = 0;
static SERVICE_STATUS sStatus;
if(sFirstTime)
{
sFirstTime = false;
//
// Setup the status structure
sStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
sStatus.dwCurrentState = SERVICE_START_PENDING;
//sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
sStatus.dwWin32ExitCode = 0;
sStatus.dwServiceSpecificExitCode = 0;
sStatus.dwCheckPoint = 0;
sStatus.dwWaitHint = 0;
}
if (sStatus.dwCurrentState == SERVICE_START_PENDING)
sStatus.dwCheckPoint = ++sCheckpoint;
else
sStatus.dwCheckPoint = 0;
sStatus.dwCurrentState = inCurrentState;
sStatus.dwServiceSpecificExitCode = inExitCode;
BOOL theErr = SetServiceStatus(sServiceStatusHandle, &sStatus);
if (theErr == 0)
{
DWORD theerrvalue = ::GetLastError();
}
}
void RunAsService(char* inServiceName)
{
SC_HANDLE theService;
SC_HANDLE theSCManager;
theSCManager = ::OpenSCManager(
NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if (!theSCManager)
return;
theService = ::OpenService(
theSCManager, // SCManager database
inServiceName, // name of service
SERVICE_ALL_ACCESS );
SERVICE_STATUS lpServiceStatus;
if (theService)
{ const SInt32 kNotRunning = 1062;
Bool16 stopped = ::ControlService(theService, SERVICE_CONTROL_STOP, &lpServiceStatus);
if(!stopped && ( (SInt32) ::GetLastError() != kNotRunning) )
qtss_printf("Stopping Service Error: %d\n", ::GetLastError());
Bool16 started = ::StartService(theService, 0, NULL);
if(!started)
qtss_printf("Starting Service Error: %d\n", ::GetLastError());
::CloseServiceHandle(theService);
}
::CloseServiceHandle(theSCManager);
}
void InstallService(char* inServiceName)
{
SC_HANDLE theService;
SC_HANDLE theSCManager;
TCHAR thePath[512];
TCHAR theQuotedPath[522];
BOOL theErr = ::GetModuleFileName( NULL, thePath, 512 );
if (!theErr)
return;
qtss_sprintf(theQuotedPath, "\"%s\"", thePath);
theSCManager = ::OpenSCManager(
NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if (!theSCManager)
{
qtss_printf("Failed to install Darwin Streaming Server Service\n");
return;
}
theService = CreateService(
theSCManager, // SCManager database
inServiceName, // name of service
inServiceName, // name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_AUTO_START, // start type
SERVICE_ERROR_NORMAL, // error control type
theQuotedPath, // service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // dependencies
NULL, // LocalSystem account
NULL); // no password
if (theService)
{
::CloseServiceHandle(theService);
qtss_printf("Installed Darwin Streaming Server Service\n");
}
else
qtss_printf("Failed to install Darwin Streaming Server Service\n");
::CloseServiceHandle(theSCManager);
}
void RemoveService(char *inServiceName)
{
SC_HANDLE theSCManager;
SC_HANDLE theService;
theSCManager = ::OpenSCManager(
NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if (!theSCManager)
{
qtss_printf("Failed to remove Darwin Streaming Server Service\n");
return;
}
theService = ::OpenService(theSCManager, inServiceName, SERVICE_ALL_ACCESS);
if (theService != NULL)
{
Bool16 stopped = ::ControlService(theService, SERVICE_CONTROL_STOP, NULL);
if(!stopped)
qtss_printf("Stopping Service Error: %d\n", ::GetLastError());
(void)::DeleteService(theService);
::CloseServiceHandle(theService);
qtss_printf("Removed Darwin Streaming Server Service\n");
}
else
qtss_printf("Failed to remove Darwin Streaming Server Service\n");
::CloseServiceHandle(theSCManager);
}