|
|
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
Service.c
Abstract:
License Logging Service - Common routines for all service.
Author:
Arthur Hanson (arth) Dec 07, 1994
Environment:
Revision History:
--*/ #include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <tchar.h>
#pragma warning (push)
#pragma warning (disable : 4201) //avoid W4 from sdk\inc\shellapi.h
#include <shellapi.h>
#pragma warning (pop)
#include "service.h"
#include "debug.h"
// internal variables
static SERVICE_STATUS ssStatus; // current status of the service
SERVICE_STATUS_HANDLE sshStatusHandle = 0; static DWORD dwErr = 0; BOOL bDebug = FALSE; TCHAR szErr[256];
// internal function prototypes
VOID WINAPI ServiceCtrl(DWORD dwCtrlCode); VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv); VOID CmdInstallService(); VOID CmdRemoveService(); VOID CmdDebugService(int argc, char **argv); BOOL WINAPI ControlHandler ( DWORD dwCtrlType ); LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
extern BOOL g_fRunning;
/////////////////////////////////////////////////////////////////////////
VOID __cdecl main( int argc, char **argv ) /*++
Routine Description:
Main routine to setup the exception handlers and initialize everything before spawning threads to listen to LPC and RPC port requests.
main() either performs the command line task, or calls StartServiceCtrlDispatcher to register the main service thread. When the this call returns, the service has stopped, so exit.
Arguments:
argc - number of command line arguments argv - array of command line arguments
Return Values:
None.
--*/ { SERVICE_TABLE_ENTRY dispatchTable[] = { { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION) ServiceMain }, { NULL, NULL } };
if ( (argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')) ) { if ( _stricmp( "install", argv[1]+1 ) == 0 ) { CmdInstallService(); } else if ( _stricmp( "remove", argv[1]+1 ) == 0 ) { CmdRemoveService(); } else if ( _stricmp( "debug", argv[1]+1 ) == 0 ) { bDebug = TRUE; CmdDebugService(argc, argv); } else { goto dispatch; }
exit(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: #ifdef DEBUG
// this is just to be friendly
printf( "%s -install to install the service\n", SZAPPNAME ); printf( "%s -remove to remove the service\n", SZAPPNAME ); printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME ); printf( "\nStartServiceCtrlDispatcher being called.\n" ); printf( "This may take several seconds. Please wait.\n" ); #endif
if (!StartServiceCtrlDispatcher(dispatchTable)) dprintf(TEXT("LLS TRACE: StartServiceCtrlDispatcher failed\n"));
} // main
/////////////////////////////////////////////////////////////////////////
VOID WINAPI ServiceMain( DWORD dwArgc, LPTSTR *lpszArgv ) /*++
Routine Description:
Performs the service initialization and then calls the ServiceStart() routine to perform majority of the work.
Arguments:
dwArgc - number of command line arguments ***UNUSED*** lpszArgv - array of command line arguments ***UNUSED***
Return Values:
None.
--*/ {
// register our service control handler:
//
sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), ServiceCtrl);
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
NSERVICEWAITHINT)) // 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; } // ServiceMain
/////////////////////////////////////////////////////////////////////////
VOID WINAPI ServiceCtrl( DWORD dwCtrlCode ) /*++
Routine Description:
Called by the SCM whenever ControlService() is called on this service.
Arguments:
dwCtrlCode - type of control requested
Return Values:
None.
--*/ { DWORD dwState = SERVICE_RUNNING;
// Handle the requested control code.
//
switch(dwCtrlCode) { // Stop the service.
//
case SERVICE_CONTROL_STOP: dwState = SERVICE_STOP_PENDING; ssStatus.dwCurrentState = SERVICE_STOP_PENDING; break;
// Update the service status.
//
case SERVICE_CONTROL_INTERROGATE: break;
// invalid control code
//
default: break;
}
ReportStatusToSCMgr(dwState, NO_ERROR, 0);
if ( SERVICE_CONTROL_STOP == dwCtrlCode ) { ServiceStop(); } } // ServiceCtrl
/////////////////////////////////////////////////////////////////////////
BOOL ReportStatusToSCMgr( DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint ) /*++
Routine Description:
Sets the current status of the service and reports it to the SCM.
Arguments:
dwCurrentState - the state of the service dwWin32ExitCode - error code to report dwWaitHint - worst case estimate to next checkpoint
Return Values:
None.
--*/ { static DWORD dwCheckPoint = 1; BOOL fResult = TRUE;
if ((g_fRunning) && (SERVICE_STOPPED != dwCurrentState) && (SERVICE_STOP_PENDING != dwCurrentState)) { return TRUE; }
if (sshStatusHandle == 0) { return FALSE; }
ssStatus.dwControlsAccepted = 0; 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 (FALSE == (fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) { dprintf(TEXT("LLS TRACE: SetServiceStatus failed\n")); } } return fResult; } // ReportStatusToSCMgr
/////////////////////////////////////////////////////////////////////////
//
// The following code handles service installation and removal
//
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
VOID CmdInstallService() /*++
Routine Description:
Installs the service.
Arguments:
None.
Return Values:
None.
--*/ { SC_HANDLE schService; SC_HANDLE schSCManager;
TCHAR szPath[512];
if (0 == GetModuleFileName( NULL, szPath, sizeof(szPath) / sizeof(szPath[0] ))) { _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256)); return; }
schSCManager = OpenSCManager( NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
); if ( schSCManager ) { schService = CreateService( schSCManager, // SCManager database
TEXT(SZSERVICENAME), // name of service
TEXT(SZSERVICEDISPLAYNAME), // name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_DEMAND_START, // start type
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)); }
CloseServiceHandle(schSCManager); } else _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); } // CmdInstallService
/////////////////////////////////////////////////////////////////////////
VOID CmdRemoveService() /*++
Routine Description:
Stops and removes the service.
Arguments:
None.
Return Values:
None.
--*/ { SC_HANDLE schService; SC_HANDLE schSCManager;
schSCManager = OpenSCManager( NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
); if ( schSCManager ) { schService = OpenService(schSCManager, TEXT(SZSERVICENAME), SERVICE_ALL_ACCESS);
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) );
}
// now remove the service
if( DeleteService(schService) ) _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) ); else _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
CloseServiceHandle(schService); } else _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
CloseServiceHandle(schSCManager); } else _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
} // CmdRemoveService
/////////////////////////////////////////////////////////////////////////
//
// Routines for running the service as a console app
//
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
VOID CmdDebugService( int argc, char ** argv ) /*++
Routine Description:
Runs the service as a console application
Arguments:
argc - number of command line arguments ***UNUSED*** argv - array of command line arguments ***UNUSED***
Return Values:
None.
--*/ { UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv);
_tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
SetConsoleCtrlHandler( ControlHandler, TRUE );
// assumption: argv and argc unused
ServiceStart( 0, NULL ); } // CmdDebugService
/////////////////////////////////////////////////////////////////////////
BOOL WINAPI ControlHandler ( DWORD dwCtrlType ) /*++
Routine Description:
Handle console control events.
Arguments:
dwCtrlType - type of control event lpszMsg - text for message
Return Values:
True - handled False - unhandled
--*/ {
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;
} // ControlHandler
/////////////////////////////////////////////////////////////////////////
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ) /*++
Routine Description:
Copies last error message text to string.
Arguments:
lpszBuf - destination buffer dwSize - size of buffer
Return Values:
destination buffer
--*/ { 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 ) ) { ASSERT(NULL != lpszBuf); lpszBuf[0] = TEXT('\0'); } else { lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
_stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() ); }
if ( lpszTemp ) LocalFree((HLOCAL) lpszTemp );
return lpszBuf; } // GetLastErrorText
|