| |
| |
| #include "service.h" |
| #include "tlog.h" |
| #include "tconvert.h" |
| |
| #include "tfilepath.h" |
| |
| #ifdef _MSC_VER |
| #pragma warning(disable : 4996) |
| #endif |
| |
| #ifdef _WIN32 |
| #include <windows.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <process.h> |
| #include <tchar.h> |
| #else |
| #include <string.h> |
| #include <errno.h> |
| #endif |
| |
| #define SZDEPENDENCIES "" |
| |
| #ifdef _WIN32 |
| |
| |
| |
| static LPTSTR GetLastErrorText(LPTSTR lpszBuf, DWORD dwSize) { |
| DWORD dwRet; |
| LPTSTR lpszTemp = NULL; |
| |
| dwRet = FormatMessage( |
| FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | |
| FORMAT_MESSAGE_ARGUMENT_ARRAY, |
| NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&lpszTemp, 0, NULL); |
| |
| |
| if (!dwRet || ((long)dwSize < (long)dwRet + 14)) |
| lpszBuf[0] = TEXT('\0'); |
| else { |
| lpszTemp[lstrlen(lpszTemp) - 2] = |
| TEXT('\0'); |
| _stprintf(lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError()); |
| } |
| |
| if (lpszTemp) LocalFree((HLOCAL)lpszTemp); |
| |
| return lpszBuf; |
| } |
| |
| #endif // _WIN32 |
| |
| |
| |
| std::string getLastErrorText() { |
| std::string errText; |
| #ifdef _WIN32 |
| char errBuff[256]; |
| errText = GetLastErrorText(errBuff, sizeof(errBuff)); |
| #else |
| errText = strerror(errno); |
| #endif |
| |
| return errText; |
| } |
| |
| |
| |
| |
| |
| |
| class TService::Imp { |
| public: |
| Imp() {} |
| |
| #ifdef _WIN32 |
| static void WINAPI serviceMain(DWORD dwArgc, LPTSTR *lpszArgv); |
| static void WINAPI serviceCtrl(DWORD dwCtrlCode); |
| |
| static BOOL WINAPI controlHandler(DWORD dwCtrlType); |
| |
| static bool reportStatusToSCMgr(long currentState, long win32ExitCode, |
| long waitHint); |
| |
| #endif |
| |
| std::string m_name; |
| std::string m_displayName; |
| static bool m_console; |
| |
| #ifdef _WIN32 |
| static SERVICE_STATUS_HANDLE m_hService; |
| static SERVICE_STATUS m_ssStatus; |
| static DWORD m_dwErr; |
| |
| #endif |
| }; |
| |
| #ifdef _WIN32 |
| SERVICE_STATUS_HANDLE TService::Imp::m_hService = 0; |
| SERVICE_STATUS TService::Imp::m_ssStatus; |
| DWORD TService::Imp::m_dwErr = 0; |
| #endif |
| |
| bool TService::Imp::m_console = false; |
| |
| |
| |
| TService::TService(const std::string &name, const std::string &displayName) |
| : m_imp(new Imp) { |
| m_imp->m_name = name; |
| m_imp->m_displayName = displayName; |
| m_instance = this; |
| } |
| |
| |
| |
| TService::~TService() {} |
| |
| |
| |
| TService *TService::instance() { return m_instance; } |
| |
| |
| |
| TService *TService::m_instance = 0; |
| |
| |
| |
| void TService::setStatus(Status status, long exitCode, long waitHint) { |
| #ifdef _WIN32 |
| if (!isRunningAsConsoleApp()) |
| TService::Imp::reportStatusToSCMgr(status, exitCode, waitHint); |
| else { |
| if (status == Stopped) exit(1); |
| } |
| #else |
| if (status == Stopped) exit(1); |
| #endif |
| } |
| |
| |
| |
| std::string TService::getName() const { return m_imp->m_name; } |
| |
| |
| |
| std::string TService::getDisplayName() const { return m_imp->m_displayName; } |
| |
| |
| |
| #ifdef _WIN32 |
| |
| void WINAPI TService::Imp::serviceCtrl(DWORD dwCtrlCode) { |
| |
| |
| switch (dwCtrlCode) { |
| |
| |
| |
| |
| |
| |
| |
| case SERVICE_CONTROL_STOP: |
| case SERVICE_CONTROL_SHUTDOWN: { |
| TService::Imp::reportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, |
| 3000 ); |
| TService::instance()->onStop(); |
| |
| TService::Imp::reportStatusToSCMgr(SERVICE_STOPPED, NO_ERROR, 3000); |
| return; |
| } |
| |
| |
| |
| case SERVICE_CONTROL_INTERROGATE: |
| break; |
| |
| |
| |
| default: |
| break; |
| } |
| |
| reportStatusToSCMgr(m_ssStatus.dwCurrentState, NO_ERROR, 0); |
| } |
| |
| |
| |
| void WINAPI TService::Imp::serviceMain(DWORD dwArgc, LPTSTR *lpszArgv) { |
| |
| |
| m_hService = RegisterServiceCtrlHandler( |
| TService::instance()->getName().c_str(), TService::Imp::serviceCtrl); |
| |
| if (m_hService == 0) goto cleanup; |
| |
| |
| |
| m_ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; |
| m_ssStatus.dwServiceSpecificExitCode = 0; |
| |
| |
| |
| if (!reportStatusToSCMgr(SERVICE_START_PENDING, |
| NO_ERROR, |
| 3000)) |
| goto cleanup; |
| |
| |
| |
| if (!reportStatusToSCMgr(SERVICE_RUNNING, |
| NO_ERROR, |
| 0)) |
| goto cleanup; |
| |
| TService::instance()->onStart(dwArgc, lpszArgv); |
| |
| cleanup: |
| |
| |
| |
| if (m_hService) reportStatusToSCMgr(SERVICE_STOPPED, m_dwErr, 0); |
| |
| return; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| BOOL WINAPI TService::Imp::controlHandler(DWORD dwCtrlType) { |
| switch (dwCtrlType) { |
| case CTRL_CLOSE_EVENT: |
| case CTRL_LOGOFF_EVENT: |
| case CTRL_SHUTDOWN_EVENT: |
| |
| case CTRL_BREAK_EVENT: |
| case CTRL_C_EVENT: |
| _tprintf(TEXT("Stopping %s.\n"), |
| TService::instance()->getDisplayName().c_str()); |
| |
| TService::instance()->onStop(); |
| return TRUE; |
| break; |
| } |
| |
| return FALSE; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool TService::Imp::reportStatusToSCMgr(long currentState, long win32ExitCode, |
| long waitHint) { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static DWORD dwCheckPoint = 1; |
| BOOL fResult = true; |
| |
| if (!m_console) |
| |
| { |
| if (currentState == SERVICE_START_PENDING) |
| m_ssStatus.dwControlsAccepted = 0; |
| else |
| m_ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | |
| SERVICE_ACCEPT_PAUSE_CONTINUE | |
| SERVICE_ACCEPT_SHUTDOWN; |
| ; |
| |
| m_ssStatus.dwCurrentState = currentState; |
| m_ssStatus.dwWin32ExitCode = win32ExitCode; |
| m_ssStatus.dwWaitHint = waitHint; |
| |
| if ((currentState == SERVICE_RUNNING) || (currentState == SERVICE_STOPPED)) |
| m_ssStatus.dwCheckPoint = 0; |
| else |
| m_ssStatus.dwCheckPoint = dwCheckPoint++; |
| |
| |
| |
| if (!(fResult = SetServiceStatus(m_hService, &m_ssStatus))) { |
| TService::addToMessageLog(QString("Failed to set the service status")); |
| } |
| } |
| return !!fResult; |
| } |
| |
| #endif |
| |
| |
| |
| void TService::run(int argc, char *argv[], bool console) { |
| m_imp->m_console = console; |
| |
| |
| |
| |
| |
| |
| |
| #ifdef _WIN32 |
| if (console) { |
| _tprintf(TEXT("Starting %s.\n"), |
| TService::instance()->getDisplayName().c_str()); |
| |
| SetConsoleCtrlHandler(TService::Imp::controlHandler, TRUE); |
| TService::instance()->onStart(argc, argv); |
| } else { |
| SERVICE_TABLE_ENTRY dispatchTable[2]; |
| |
| std::string name = TService::instance()->getName().c_str(); |
| |
| dispatchTable[0].lpServiceName = (char *)name.c_str(); |
| dispatchTable[0].lpServiceProc = |
| (LPSERVICE_MAIN_FUNCTION)TService::Imp::serviceMain; |
| |
| dispatchTable[1].lpServiceName = NULL; |
| dispatchTable[1].lpServiceProc = NULL; |
| |
| if (!StartServiceCtrlDispatcher(dispatchTable)) |
| TService::addToMessageLog(QString(TEXT("Service start failed."))); |
| } |
| #else |
| TService::instance()->onStart(argc, argv); |
| #endif |
| } |
| |
| |
| |
| void TService::start(const std::string &name) {} |
| |
| |
| |
| void TService::stop(const std::string &name) {} |
| |
| |
| |
| bool TService::isRunningAsConsoleApp() const { return m_imp->m_console; } |
| |
| |
| |
| void TService::install(const std::string &name, const std::string &displayName, |
| const TFilePath &appPath) { |
| #ifdef _WIN32 |
| SC_HANDLE schService; |
| SC_HANDLE schSCManager; |
| |
| schSCManager = OpenSCManager(NULL, |
| NULL, |
| SC_MANAGER_ALL_ACCESS); |
| |
| if (schSCManager) { |
| schService = CreateService( |
| schSCManager, |
| name.c_str(), |
| displayName.c_str(), |
| SERVICE_ALL_ACCESS, |
| SERVICE_WIN32_OWN_PROCESS, |
| SERVICE_DEMAND_START, |
| SERVICE_ERROR_NORMAL, |
| ::to_string(appPath.getWideString()).c_str(), |
| NULL, |
| NULL, |
| TEXT(SZDEPENDENCIES), |
| NULL, |
| NULL); |
| |
| if (schService) { |
| _tprintf(TEXT("%s installed.\n"), displayName.c_str()); |
| CloseServiceHandle(schService); |
| } else { |
| _tprintf(TEXT("CreateService failed - %s\n"), getLastErrorText().c_str()); |
| } |
| |
| CloseServiceHandle(schSCManager); |
| } else |
| _tprintf(TEXT("OpenSCManager failed - %s\n"), getLastErrorText().c_str()); |
| #endif |
| } |
| |
| |
| |
| void TService::remove(const std::string &name) { |
| #ifdef _WIN32 |
| std::string displayName = name; |
| |
| SC_HANDLE schService; |
| SC_HANDLE schSCManager; |
| |
| schSCManager = OpenSCManager(NULL, |
| NULL, |
| SC_MANAGER_ALL_ACCESS); |
| |
| if (schSCManager) { |
| schService = OpenService(schSCManager, name.c_str(), SERVICE_ALL_ACCESS); |
| |
| if (schService) { |
| SERVICE_STATUS ssStatus; |
| |
| |
| if (ControlService(schService, SERVICE_CONTROL_STOP, &ssStatus)) { |
| _tprintf(TEXT("Stopping %s."), displayName.c_str()); |
| Sleep(1000); |
| |
| while (QueryServiceStatus(schService, &ssStatus)) { |
| if (ssStatus.dwCurrentState == SERVICE_STOP_PENDING) { |
| _tprintf(TEXT(".")); |
| Sleep(1000); |
| } else |
| break; |
| } |
| |
| if (ssStatus.dwCurrentState == SERVICE_STOPPED) |
| _tprintf(TEXT("\n%s stopped.\n"), displayName.c_str()); |
| else |
| _tprintf(TEXT("\n%s failed to stop.\n"), displayName.c_str()); |
| } |
| |
| |
| if (DeleteService(schService)) |
| _tprintf(TEXT("%s removed.\n"), displayName.c_str()); |
| else |
| _tprintf(TEXT("DeleteService failed - %s\n"), |
| getLastErrorText().c_str()); |
| |
| CloseServiceHandle(schService); |
| } else |
| _tprintf(TEXT("OpenService failed - %s\n"), getLastErrorText().c_str()); |
| |
| CloseServiceHandle(schSCManager); |
| } else |
| _tprintf(TEXT("OpenSCManager failed - %s\n"), getLastErrorText().c_str()); |
| #endif |
| } |
| |
| |
| |
| void TService::addToMessageLog(const QString &msg) { |
| addToMessageLog(msg.toStdString()); |
| } |
| |
| void TService::addToMessageLog(const std::string &msg) { |
| #ifdef _WIN32 |
| TCHAR szMsg[256]; |
| HANDLE hEventSource; |
| LPCTSTR lpszStrings[2]; |
| |
| if (!TService::Imp::m_console) { |
| TService::Imp::m_dwErr = GetLastError(); |
| |
| |
| |
| hEventSource = |
| RegisterEventSource(NULL, TService::instance()->getName().c_str()); |
| |
| _stprintf(szMsg, TEXT("%s error: %d"), |
| TService::instance()->getName().c_str(), TService::Imp::m_dwErr); |
| lpszStrings[0] = szMsg; |
| lpszStrings[1] = msg.c_str(); |
| |
| if (hEventSource != NULL) { |
| ReportEvent(hEventSource, |
| EVENTLOG_ERROR_TYPE, |
| 0, |
| 0, |
| NULL, |
| 2, |
| 0, |
| lpszStrings, |
| NULL); |
| |
| (VOID) DeregisterEventSource(hEventSource); |
| } |
| } else { |
| std::cout << msg.c_str(); |
| } |
| #else |
| if (!TService::Imp::m_console) { |
| QString str(msg.c_str()); |
| TSysLog::error(str); |
| } else { |
| std::cout << msg.c_str(); |
| } |
| #endif |
| } |
| |