/*--------------------------------------------------------------------------- THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved. MODULE: service.c PURPOSE: Implements functions required by all Windows NT services FUNCTIONS: main(int argc, char **argv); service_ctrl(DWORD dwCtrlCode); service_main(DWORD dwArgc, LPTSTR *lpszArgv); CmdInstallService(); CmdRemoveService(); CmdStartService(); CmdDebugService(int argc, char **argv); ControlHandler ( DWORD dwCtrlType ); GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ); ---------------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H #include "config.h" #elif defined(_MSC_VER) #include "config-msvc.h" #endif #include #include #include #include #include #include "service.h" // internal variables SERVICE_STATUS ssStatus; // current status of the service SERVICE_STATUS_HANDLE sshStatusHandle; DWORD dwErr = 0; BOOL bDebug = FALSE; TCHAR szErr[256]; // internal function prototypes VOID WINAPI service_ctrl(DWORD dwCtrlCode); VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv); int CmdInstallService(); int CmdRemoveService(); int CmdStartService(); VOID CmdDebugService(int argc, char **argv); BOOL WINAPI ControlHandler ( DWORD dwCtrlType ); LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ); // // FUNCTION: main // // PURPOSE: entrypoint for service // // PARAMETERS: // argc - number of command line arguments // argv - array of command line arguments // // RETURN VALUE: // none // // COMMENTS: // main() either performs the command line task, or // call StartServiceCtrlDispatcher to register the // main service thread. When the this call returns, // the service has stopped, so exit. // int __cdecl main(int argc, char **argv) { SERVICE_TABLE_ENTRY dispatchTable[] = { { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main}, { NULL, NULL} }; if ( (argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')) ) { if ( _stricmp( "install", argv[1]+1 ) == 0 ) { return CmdInstallService(); } else if ( _stricmp( "remove", argv[1]+1 ) == 0 ) { return CmdRemoveService(); } else if ( _stricmp( "start", argv[1]+1 ) == 0) { return CmdStartService(); } else if ( _stricmp( "debug", argv[1]+1 ) == 0 ) { bDebug = TRUE; CmdDebugService(argc, argv); } else { goto dispatch; } return 0; } // if it doesn't match any of the above parameters // the service control manager may be starting the service // so we must call StartServiceCtrlDispatcher dispatch: // this is just to be friendly printf( "%s -install to install the service\n", SZAPPNAME ); printf( "%s -start to start the service\n", SZAPPNAME ); printf( "%s -remove to remove the service\n", SZAPPNAME ); printf( "%s -debug to run as a console app for debugging\n", SZAPPNAME ); printf( "\nStartServiceCtrlDispatcher being called.\n" ); printf( "This may take several seconds. Please wait.\n" ); if (!StartServiceCtrlDispatcher(dispatchTable)) AddToMessageLog(MSG_FLAGS_ERROR, TEXT("StartServiceCtrlDispatcher failed.")); return 0; } // // FUNCTION: service_main // // PURPOSE: To perform actual initialization of the service // // PARAMETERS: // dwArgc - number of command line arguments // lpszArgv - array of command line arguments // // RETURN VALUE: // none // // COMMENTS: // This routine performs the service initialization and then calls // the user defined ServiceStart() routine to perform majority // of the work. // void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv) { // register our service control handler: // sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl); if (!sshStatusHandle) goto cleanup; // SERVICE_STATUS members that don't change in example // ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ssStatus.dwServiceSpecificExitCode = 0; // report the status to the service control manager. // if (!ReportStatusToSCMgr( SERVICE_START_PENDING, // service state NO_ERROR, // exit code 3000)) // wait hint goto cleanup; ServiceStart( dwArgc, lpszArgv ); cleanup: // try to report the stopped status to the service control manager. // if (sshStatusHandle) (VOID)ReportStatusToSCMgr( SERVICE_STOPPED, dwErr, 0); return; } // // FUNCTION: service_ctrl // // PURPOSE: This function is called by the SCM whenever // ControlService() is called on this service. // // PARAMETERS: // dwCtrlCode - type of control requested // // RETURN VALUE: // none // // COMMENTS: // VOID WINAPI service_ctrl(DWORD dwCtrlCode) { // Handle the requested control code. // switch (dwCtrlCode) { // Stop the service. // // SERVICE_STOP_PENDING should be reported before // setting the Stop Event - hServerStopEvent - in // ServiceStop(). This avoids a race condition // which may result in a 1053 - The Service did not respond... // error. case SERVICE_CONTROL_STOP: ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0); ServiceStop(); return; // Update the service status. // case SERVICE_CONTROL_INTERROGATE: break; // invalid control code // default: break; } ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0); } // // FUNCTION: ReportStatusToSCMgr() // // PURPOSE: Sets the current status of the service and // reports it to the Service Control Manager // // PARAMETERS: // dwCurrentState - the state of the service // dwWin32ExitCode - error code to report // dwWaitHint - worst case estimate to next checkpoint // // RETURN VALUE: // TRUE - success // FALSE - failure // // COMMENTS: // BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) { static DWORD dwCheckPoint = 1; BOOL fResult = TRUE; if ( !bDebug ) // when debugging we don't report to the SCM { if (dwCurrentState == SERVICE_START_PENDING) ssStatus.dwControlsAccepted = 0; else ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; ssStatus.dwCurrentState = dwCurrentState; ssStatus.dwWin32ExitCode = dwWin32ExitCode; ssStatus.dwWaitHint = dwWaitHint; if ( ( dwCurrentState == SERVICE_RUNNING ) || ( dwCurrentState == SERVICE_STOPPED ) ) ssStatus.dwCheckPoint = 0; else ssStatus.dwCheckPoint = dwCheckPoint++; // Report the status of the service to the service control manager. // if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) { AddToMessageLog(MSG_FLAGS_ERROR, TEXT("SetServiceStatus")); } } return fResult; } // // FUNCTION: AddToMessageLog(LPTSTR lpszMsg) // // PURPOSE: Allows any thread to log an error message // // PARAMETERS: // lpszMsg - text for message // // RETURN VALUE: // none // // COMMENTS: // void AddToMessageLog(DWORD flags, LPTSTR lpszMsg) { TCHAR szMsg [(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100 ]; HANDLE hEventSource; LPCSTR lpszStrings[2]; if ( !bDebug ) { if (flags & MSG_FLAGS_SYS_CODE) dwErr = GetLastError(); else dwErr = 0; // Use event logging to log the error. // hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME)); _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), (int)dwErr); lpszStrings[0] = szMsg; lpszStrings[1] = lpszMsg; if (hEventSource != NULL) { ReportEvent(hEventSource, // handle of event source // event type (flags & MSG_FLAGS_ERROR) ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE, 0, // event category 0, // event ID NULL, // current user's SID 2, // strings in lpszStrings 0, // no bytes of raw data lpszStrings, // array of error strings NULL); // no raw data (VOID) DeregisterEventSource(hEventSource); } } } void ResetError (void) { dwErr = 0; } /////////////////////////////////////////////////////////////////// // // The following code handles service installation and removal // // // FUNCTION: CmdInstallService() // // PURPOSE: Installs the service // // PARAMETERS: // none // // RETURN VALUE: // 0 if success // // COMMENTS: // int CmdInstallService() { SC_HANDLE schService; SC_HANDLE schSCManager; TCHAR szPath[512]; int ret = 0; if ( GetModuleFileName( NULL, szPath+1, 510 ) == 0 ) { _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256)); return 1; } szPath[0] = '\"'; strcat(szPath, "\""); schSCManager = OpenSCManager( NULL, // machine (NULL == local) NULL, // database (NULL == default) SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE // access required ); if ( schSCManager ) { schService = CreateService( schSCManager, // SCManager database TEXT(SZSERVICENAME), // name of service TEXT(SZSERVICEDISPLAYNAME), // name to display SERVICE_QUERY_STATUS, // desired access SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_DEMAND_START, // start type -- alternative: SERVICE_AUTO_START SERVICE_ERROR_NORMAL, // error control type szPath, // service's binary NULL, // no load ordering group NULL, // no tag identifier TEXT(SZDEPENDENCIES), // dependencies NULL, // LocalSystem account NULL); // no password if ( schService ) { _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) ); CloseServiceHandle(schService); } else { _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256)); ret = 1; } CloseServiceHandle(schSCManager); } else { _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); ret = 1; } return ret; } // // FUNCTION: CmdStartService() // // PURPOSE: Start the service // // PARAMETERS: // none // // RETURN VALUE: // 0 if success // // COMMENTS: int CmdStartService() { int ret = 0; SC_HANDLE schSCManager; SC_HANDLE schService; // Open a handle to the SC Manager database. schSCManager = OpenSCManager( NULL, // local machine NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS); // full access rights if (NULL == schSCManager) { _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); ret = 1; } schService = OpenService( schSCManager, // SCM database SZSERVICENAME, // service name SERVICE_ALL_ACCESS); if (schService == NULL) { _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256)); ret = 1; } if (!StartService( schService, // handle to service 0, // number of arguments NULL) ) // no arguments { _tprintf(TEXT("StartService failed - %s\n"), GetLastErrorText(szErr,256)); ret = 1; } else { _tprintf(TEXT("Service Started\n")); ret = 0; } CloseServiceHandle(schService); CloseServiceHandle(schSCManager); return ret; } // // FUNCTION: CmdRemoveService() // // PURPOSE: Stops and removes the service // // PARAMETERS: // none // // RETURN VALUE: // 0 if success // // COMMENTS: // int CmdRemoveService() { SC_HANDLE schService; SC_HANDLE schSCManager; int ret = 0; schSCManager = OpenSCManager( NULL, // machine (NULL == local) NULL, // database (NULL == default) SC_MANAGER_CONNECT // access required ); if ( schSCManager ) { schService = OpenService(schSCManager, TEXT(SZSERVICENAME), DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS); if (schService) { // try to stop the service if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) ) { _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME)); 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"), TEXT(SZSERVICEDISPLAYNAME) ); else { _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) ); ret = 1; } } // now remove the service if ( DeleteService(schService) ) _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) ); else { _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256)); ret = 1; } CloseServiceHandle(schService); } else { _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256)); ret = 1; } CloseServiceHandle(schSCManager); } else { _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); ret = 1; } return ret; } /////////////////////////////////////////////////////////////////// // // The following code is for running the service as a console app // // // FUNCTION: CmdDebugService(int argc, char ** argv) // // PURPOSE: Runs the service as a console application // // PARAMETERS: // argc - number of command line arguments // argv - array of command line arguments // // RETURN VALUE: // none // // COMMENTS: // void CmdDebugService(int argc, char ** argv) { DWORD dwArgc; LPTSTR *lpszArgv; #ifdef UNICODE lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) ); if (NULL == lpszArgv) { // CommandLineToArvW failed!! _tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n")); return; } #else dwArgc = (DWORD) argc; lpszArgv = argv; #endif _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME)); SetConsoleCtrlHandler( ControlHandler, TRUE ); ServiceStart( dwArgc, lpszArgv ); #ifdef UNICODE // Must free memory allocated for arguments GlobalFree(lpszArgv); #endif // UNICODE } // // FUNCTION: ControlHandler ( DWORD dwCtrlType ) // // PURPOSE: Handled console control events // // PARAMETERS: // dwCtrlType - type of control event // // RETURN VALUE: // True - handled // False - unhandled // // COMMENTS: // BOOL WINAPI ControlHandler ( DWORD dwCtrlType ) { switch ( dwCtrlType ) { case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME)); ServiceStop(); return TRUE; break; } return FALSE; } // // FUNCTION: GetLastErrorText // // PURPOSE: copies error message text to string // // PARAMETERS: // lpszBuf - destination buffer // dwSize - size of buffer // // RETURN VALUE: // destination buffer // // COMMENTS: // 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 ); // supplied buffer is not long enough if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) ) lpszBuf[0] = TEXT('\0'); else { lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, (int)GetLastError() ); } if ( lpszTemp ) LocalFree((HLOCAL) lpszTemp ); return lpszBuf; }