| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- /***********************************************************************
- **
- /** BEGIN COPYRIGHT BLOCK
- * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
- * Copyright (C) 2005 Red Hat, Inc.
- * All rights reserved.
- * END COPYRIGHT BLOCK **/
- /*
- **
- ** NAME
- ** NTService.cpp
- **
- ** DESCRIPTION
- ** Base class for NT Service app
- **
- ** AUTHOR
- ** Rob Weltman <[email protected]>
- **
- ***********************************************************************/
- /***********************************************************************
- ** Includes
- ***********************************************************************/
- // Removed: 2-8-2005
- //#include "sysplat.h"
- // Added: 2-8-2005
- #include <stdio.h>
- // End Change
- #include <tchar.h>
- #include <time.h>
- #include "NTService.h"
- // Remove: 2-8-2005
- //#include "uniutil.h"
- // End Change
- #include "dssynchmsg.h"
- // static variables
- CNTService* CNTService::m_pThis = NULL;
- CNTService::CNTService(const TCHAR* szServiceName)
- {
- // copy the address of the current object so we can access it from
- // the static member callback functions.
- // WARNING: This limits the application to only one CNTService object.
- m_pThis = this;
-
- // Set the default service name and version
- _tcsncpy(m_szServiceName, szServiceName, sizeof(m_szServiceName)-1);
- m_iMajorVersion = 1;
- m_iMinorVersion = 0;
- m_hEventSource = NULL;
- // set up the initial service status
- m_hServiceStatus = NULL;
- m_Status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- m_Status.dwCurrentState = SERVICE_STOPPED;
- m_Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
- m_Status.dwWin32ExitCode = 0;
- m_Status.dwServiceSpecificExitCode = 0;
- m_Status.dwCheckPoint = 0;
- m_Status.dwWaitHint = 0;
- m_bIsRunning = FALSE;
- }
- CNTService::~CNTService()
- {
- DebugMsg(_T("CNTService::~CNTService()"));
- if (m_hEventSource) {
- ::DeregisterEventSource(m_hEventSource);
- m_hEventSource = NULL;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Default command line argument parsing
- // Returns TRUE if it found an arg it recognised, FALSE if not
- // Note: processing some arguments causes output to stdout to be generated.
- BOOL CNTService::ParseStandardArgs(int argc, char* argv[])
- {
- // See if we have any command line args we recognise
- if (argc <= 1) return FALSE;
- if (_stricmp(argv[1], "-v") == 0) {
- // Spit out version info
- _tprintf(_T("%s Version %d.%d\n"),
- m_szServiceName, m_iMajorVersion, m_iMinorVersion);
- _tprintf(_T("The service is %s installed\n"),
- IsInstalled() ? _T("currently") : _T("not"));
- return TRUE; // say we processed the argument
- } else if (_stricmp(argv[1], "-i") == 0) {
- // Request to install.
- if (IsInstalled()) {
- _tprintf(_T("%s is already installed\n"), m_szServiceName);
- } else {
- // Try and install the copy that's running
- if (Install()) {
- _tprintf(_T("%s installed\n"), m_szServiceName);
- } else {
- _tprintf(_T("%s failed to install. Error %d\n"),
- m_szServiceName, GetLastError());
- }
- }
- return TRUE; // say we processed the argument
- } else if (_stricmp(argv[1], "-u") == 0) {
- // Request to uninstall.
- if (!IsInstalled()) {
- _tprintf(_T("%s is not installed\n"), m_szServiceName);
- } else {
- // Try and remove the copy that's installed
- if (Uninstall()) {
- // Get the executable file path
- TCHAR szFilePath[_MAX_PATH];
- ::GetModuleFileName(NULL, szFilePath, sizeof(szFilePath));
- _tprintf(_T("%s removed. (You must delete the file (%s) yourself.)\n"),
- m_szServiceName, szFilePath);
- } else {
- _tprintf(_T("Could not remove %s. Error %d\n"),
- m_szServiceName, GetLastError());
- }
- }
- return TRUE; // say we processed the argument
-
- }
-
- // Don't recognise the args
- return FALSE;
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Install/uninstall routines
- // Test if the service is currently installed
- BOOL CNTService::IsInstalled()
- {
- BOOL bResult = FALSE;
- // Open the Service Control Manager
- SC_HANDLE hSCM = ::OpenSCManager(NULL, // local machine
- NULL, // ServicesActive database
- SC_MANAGER_ALL_ACCESS); // full access
- if (hSCM) {
- // Try to open the service
- SC_HANDLE hService = ::OpenService(hSCM,
- m_szServiceName,
- SERVICE_QUERY_CONFIG);
- if (hService) {
- bResult = TRUE;
- ::CloseServiceHandle(hService);
- }
- ::CloseServiceHandle(hSCM);
- }
-
- return bResult;
- }
- BOOL CNTService::Install()
- {
- // Open the Service Control Manager
- SC_HANDLE hSCM = ::OpenSCManager(NULL, // local machine
- NULL, // ServicesActive database
- SC_MANAGER_ALL_ACCESS); // full access
- if (!hSCM) return FALSE;
- // Get the executable file path
- TCHAR szFilePath[_MAX_PATH];
- ::GetModuleFileName(NULL, szFilePath, sizeof(szFilePath)/sizeof(*szFilePath));
- // Create the service
- SC_HANDLE hService = ::CreateService(hSCM,
- m_szServiceName,
- m_szServiceName,
- SERVICE_ALL_ACCESS,
- SERVICE_WIN32_OWN_PROCESS,
- SERVICE_DEMAND_START, // start condition
- SERVICE_ERROR_NORMAL,
- szFilePath,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL);
- if (!hService) {
- ::CloseServiceHandle(hSCM);
- return FALSE;
- }
- // make registry entries to support logging messages
- // Add the source name as a subkey under the Application
- // key in the EventLog service portion of the registry.
- TCHAR szKey[256];
- HKEY hKey = NULL;
- _tcscpy(szKey, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"));
- _tcscat(szKey, GetEventName());
- if (::RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey) != ERROR_SUCCESS) {
- ::CloseServiceHandle(hService);
- ::CloseServiceHandle(hSCM);
- return FALSE;
- }
- // Add the Event ID message-file name to the 'EventMessageFile' subkey.
- ::RegSetValueEx(hKey,
- _T("EventMessageFile"),
- 0,
- REG_EXPAND_SZ,
- (CONST BYTE*)szFilePath,
- (_tcslen(szFilePath) + 1)*sizeof(*szFilePath));
- // Set the supported types flags.
- DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
- ::RegSetValueEx(hKey,
- _T("TypesSupported"),
- 0,
- REG_DWORD,
- (CONST BYTE*)&dwData,
- sizeof(DWORD));
- ::RegCloseKey(hKey);
- LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_INSTALLED, m_szServiceName);
- // tidy up
- ::CloseServiceHandle(hService);
- ::CloseServiceHandle(hSCM);
- return TRUE;
- }
- BOOL CNTService::Uninstall()
- {
- // Open the Service Control Manager
- SC_HANDLE hSCM = ::OpenSCManager(NULL, // local machine
- NULL, // ServicesActive database
- SC_MANAGER_ALL_ACCESS); // full access
- if (!hSCM) return FALSE;
- BOOL bResult = FALSE;
- SC_HANDLE hService = ::OpenService(hSCM,
- m_szServiceName,
- DELETE);
- if (hService) {
- // Stop it if it is running
- SERVICE_STATUS serviceStatus;
- BOOL bStop = ControlService( hService, SERVICE_CONTROL_STOP,
- &serviceStatus );
- if (::DeleteService(hService)) {
- LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_REMOVED, m_szServiceName);
- bResult = TRUE;
- } else {
- LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_NOTREMOVED, m_szServiceName);
- }
- ::CloseServiceHandle(hService);
- }
-
- ::CloseServiceHandle(hSCM);
- return bResult;
- }
- ///////////////////////////////////////////////////////////////////////////////////////
- // Logging functions
- // This function makes an entry into the application event log
- void CNTService::LogEvent(WORD wType, DWORD dwID,
- const wchar_t* pszS1,
- const wchar_t* pszS2,
- const wchar_t* pszS3)
- {
- #ifndef _DEBUG
- if ( EVMSG_DEBUG == dwID )
- return;
- #endif
- const wchar_t* ps[3];
- ps[0] = pszS1;
- ps[1] = pszS2;
- ps[2] = pszS3;
- int iStr = 0;
- for (int i = 0; i < 3; i++) {
- if (ps[i] != NULL) iStr++;
- }
-
- // Check the event source has been registered and if
- // not then register it now
- if (!m_hEventSource) {
- TCHAR *name = GetEventName();
- // Modification: 2-8-2005
- // m_hEventSource = ::RegisterEventSourceW(NULL, // local machine
- // GetEventName()); // source name
- m_hEventSource = ::RegisterEventSource(NULL, // local machine
- GetEventName()); // source name
- // End Change
- }
- if (m_hEventSource) {
- ::ReportEventW(m_hEventSource,
- wType,
- 0,
- dwID,
- NULL, // sid
- iStr,
- 0,
- ps,
- NULL);
- }
- }
- // This function makes an entry into the application event log
- void CNTService::LogEvent(WORD wType, DWORD dwID,
- const char* pszS1,
- const char* pszS2,
- const char* pszS3)
- {
- wchar_t *p1 = pszS1 ? StrToUnicode( pszS1 ) : NULL;
- wchar_t *p2 = pszS2 ? StrToUnicode( pszS2 ) : NULL;
- wchar_t *p3 = pszS3 ? StrToUnicode( pszS3 ) : NULL;
- LogEvent( wType, dwID, p1, p2, p3 );
- if ( p1 )
- free( p1 );
- if ( p2 )
- free( p2 );
- if ( p3 )
- free( p3 );
- }
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Service startup and registration
- BOOL CNTService::StartService()
- {
- CNTService* pService = m_pThis;
- SERVICE_TABLE_ENTRY st[] = {
- {m_szServiceName, ServiceMain},
- {NULL, NULL}
- };
- DebugMsg(_T("Calling StartServiceCtrlDispatcher()"));
- // Fails if started from command line, but StartService
- // works any way
- BOOL b = ::StartServiceCtrlDispatcher(st);
- DWORD err = GetLastError();
- DebugMsg(_T("Returned from StartServiceCtrlDispatcher()"));
- return b;
- }
- BOOL CNTService::StartServiceDirect()
- {
- BOOL b = FALSE;
- // Open the Service Control Manager
- SC_HANDLE hSCM = ::OpenSCManager(NULL, // local machine
- NULL, // ServicesActive database
- SC_MANAGER_ALL_ACCESS); // full access
- if (!hSCM) return FALSE;
- SC_HANDLE hService = ::OpenService(hSCM,
- m_szServiceName,
- SERVICE_START);
- if (hService)
- {
- DebugMsg(_T("Calling StartServiceDirect()"));
- b = ::StartService( hService, 0, NULL );
- ::CloseServiceHandle(hService);
- }
- ::CloseServiceHandle(hSCM);
- return b;
- }
- // static member function (callback)
- void CNTService::ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
- {
- // Get a pointer to the C++ object
- CNTService* pService = m_pThis;
-
- pService->DebugMsg(_T("Entering CNTService::ServiceMain()"));
- // Register the control request handler
- pService->m_Status.dwCurrentState = SERVICE_START_PENDING;
- pService->m_hServiceStatus = RegisterServiceCtrlHandler(pService->m_szServiceName,
- Handler);
- if (pService->m_hServiceStatus == NULL) {
- pService->LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_CTRLHANDLERNOTINSTALLED);
- return;
- }
- // Start the initialisation
- if (pService->Initialize()) {
- // Do the real work.
- // When the Run function returns, the service has stopped.
- pService->m_bIsRunning = TRUE;
- pService->m_Status.dwWin32ExitCode = 0;
- pService->m_Status.dwCheckPoint = 0;
- pService->m_Status.dwWaitHint = 0;
- pService->Run();
- }
- // Tell the service manager we are stopped
- pService->SetStatus(SERVICE_STOPPED);
- pService->DebugMsg(_T("Leaving CNTService::ServiceMain()"));
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- // status functions
- void CNTService::SetStatus(DWORD dwState)
- {
- DebugMsg(_T("CNTService::SetStatus(%lu, %lu)"), m_hServiceStatus, dwState);
- m_Status.dwCurrentState = dwState;
- ::SetServiceStatus(m_hServiceStatus, &m_Status);
- }
- ///////////////////////////////////////////////////////////////////////////////////////////
- // Service initialization
- BOOL CNTService::Initialize()
- {
- DebugMsg(_T("Entering CNTService::Initialize()"));
- // Start the initialization
- SetStatus(SERVICE_START_PENDING);
-
- // Perform the actual initialization
- BOOL bResult = OnInit();
-
- // Set final state
- m_Status.dwWin32ExitCode = GetLastError();
- m_Status.dwCheckPoint = 0;
- m_Status.dwWaitHint = 0;
- if (!bResult) {
- LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_FAILEDINIT);
- SetStatus(SERVICE_STOPPED);
- return FALSE;
- }
-
- LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_STARTED);
- SetStatus(SERVICE_RUNNING);
- DebugMsg(_T("Leaving CNTService::Initialize()"));
- return TRUE;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // main function to do the real work of the service
- // This function performs the main work of the service.
- // When this function returns the service has stopped.
- void CNTService::Run()
- {
- DebugMsg(_T("Entering CNTService::Run()"));
- while (m_bIsRunning) {
- DebugMsg(_T("Sleeping..."));
- Sleep(5000);
- }
- // nothing more to do
- DebugMsg(_T("Leaving CNTService::Run()"));
- }
- //////////////////////////////////////////////////////////////////////////////////////
- // Control request handlers
- // static member function (callback) to handle commands from the
- // service control manager
- void CNTService::Handler(DWORD dwOpcode)
- {
- // Get a pointer to the object
- CNTService* pService = m_pThis;
-
- pService->DebugMsg(_T("CNTService::Handler(%lu)"), dwOpcode);
- switch (dwOpcode) {
- case SERVICE_CONTROL_STOP: // 1
- pService->SetStatus(SERVICE_STOP_PENDING);
- pService->OnStop();
- pService->m_bIsRunning = FALSE;
- pService->LogEvent(EVENTLOG_INFORMATION_TYPE, EVMSG_STOPPED);
- if (pService->m_hEventSource) {
- ::DeregisterEventSource(pService->m_hEventSource);
- pService->m_hEventSource = NULL;
- }
- break;
- case SERVICE_CONTROL_PAUSE: // 2
- pService->OnPause();
- break;
- case SERVICE_CONTROL_CONTINUE: // 3
- pService->OnContinue();
- break;
- case SERVICE_CONTROL_INTERROGATE: // 4
- pService->OnInterrogate();
- break;
- case SERVICE_CONTROL_SHUTDOWN: // 5
- pService->OnShutdown();
- break;
- default:
- if (dwOpcode >= SERVICE_CONTROL_USER) {
- if (!pService->OnUserControl(dwOpcode)) {
- pService->LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_BADREQUEST);
- }
- } else {
- pService->LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_BADREQUEST);
- }
- break;
- }
- // Report current status
- pService->DebugMsg(_T("Updating status (%lu, %lu)"),
- pService->m_hServiceStatus,
- pService->m_Status.dwCurrentState);
- ::SetServiceStatus(pService->m_hServiceStatus, &pService->m_Status);
- }
-
- // Called when the service is first initialized
- BOOL CNTService::OnInit()
- {
- DebugMsg(_T("CNTService::OnInit()"));
- return TRUE;
- }
- // Called when the service control manager wants to stop the service
- void CNTService::OnStop()
- {
- DebugMsg(_T("CNTService::OnStop()"));
- }
- // called when the service is interrogated
- void CNTService::OnInterrogate()
- {
- DebugMsg(_T("CNTService::OnInterrogate()"));
- }
- // called when the service is paused
- void CNTService::OnPause()
- {
- DebugMsg(_T("CNTService::OnPause()"));
- }
- // called when the service is continued
- void CNTService::OnContinue()
- {
- DebugMsg(_T("CNTService::OnContinue()"));
- }
- // called when the service is shut down
- void CNTService::OnShutdown()
- {
- DebugMsg(_T("CNTService::OnShutdown()"));
- }
- // called when the service gets a user control message
- BOOL CNTService::OnUserControl(DWORD dwOpcode)
- {
- DebugMsg(_T("CNTService::OnUserControl(%8.8lXH)"), dwOpcode);
- return FALSE; // say not handled
- }
- ////////////////////////////////////////////////////////////////////////////////////////////
- // Debugging support
- void CNTService::DebugMsg(const TCHAR* pszFormat, ...)
- {
- TCHAR buf[1024];
- _stprintf(buf, _T("[%s](%lu): "), m_szServiceName, GetCurrentThreadId());
- va_list arglist;
- va_start(arglist, pszFormat);
- _vstprintf(&buf[_tcslen(buf)], pszFormat, arglist);
- va_end(arglist);
- _tcscat(buf, _T("\n"));
- OutputDebugString(buf);
- }
|