/* * * @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); }