|
|
// 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-1995 Microsoft Corporation. All Rights Reserved.
//
// MODULE: service.c
//
// PURPOSE: Implements functions required by all services
// and takes into account dumbed-down win95 services
//
// FUNCTIONS:
// main(int argc, char **argv);
// service_ctrl(DWORD dwCtrlCode);
// PSTOREServiceMain(DWORD dwArgc, LPWSTR *lpszArgv);
// WinNTDebugService(int argc, char **argv);
// ControlHandler ( DWORD dwCtrlType );
//
// COMMENTS:
//
// AUTHOR: Craig Link - Microsoft Developer Support
// MODIFIED: Matt Thomlinson
// Scott Field
//
#include <pch.cpp>
#pragma hdrstop
#include <svcs.h>
#include "service.h"
// this event is signalled when the
// service should end
//
HANDLE hServerStopEvent = NULL;
//
// this event is used to allow external code to determine if we are initialized
// and running. Currently, this is only used by the WinNT and Win95 credential
// managers to prevent logon delays when the service is not available.
//
HANDLE hServiceStarted = NULL;
PACL pDaclInitEvent = NULL;
extern DWORD GlobalSecurityMask; extern BOOL g_bAudit;
//
// waitable thread pool handle.
//
HANDLE hRegisteredWait = NULL;
VOID TeardownServer( DWORD dwLastError );
VOID NTAPI TerminationNotify( PVOID Context, BOOLEAN TimerOrWaitFired );
#define RTN_OK 0 // no errors
#define RTN_USAGE 1 // usage error (invalid commandline)
#define RTN_ERROR_INIT 2 // error during service initialization
#define RTN_ERROR_INSTALL 13 // error during -install or -remove
#define RTN_ERROR_INSTALL_SIG 14 // error installing signature(s)
#define RTN_ERROR_INSTALL_START 15 // could not start service during install
#define RTN_ERROR_INSTALL_SHEXT 16 // error installing shell extension
//
// global module handle used to reference resources contained in this module.
//
HINSTANCE g_hInst = NULL;
// internal variables
static SERVICE_STATUS ssStatus; // current status of the service
SERVICE_STATUS_HANDLE sshStatusHandle;
// internal function prototypes
void WINAPI service_ctrl(DWORD dwCtrlCode); void WINAPI PSTOREServiceMain(DWORD dwArgc, LPWSTR *lpszArgv);
DWORD WINAPI Start( LPVOID lpV );
BOOL WINAPI DllMain( HMODULE hInst, DWORD dwReason, LPVOID lpReserved ) {
if( dwReason == DLL_PROCESS_ATTACH ) { g_hInst = hInst; DisableThreadLibraryCalls(hInst); }
return TRUE; }
VOID WINAPI ServiceEntry( DWORD NumArgs, LPWSTR *ArgsArray, PVOID LmsvcsGlobalData, HANDLE SvcRefHandle ) { Start( NULL ); }
DWORD WINAPI Start( LPVOID lpV ) { BOOL fIsNT = FIsWinNT(); int iRet;
//
// surpress dialog boxes generated by missing files, etc.
//
SetErrorMode(SEM_NOOPENFILEERRORBOX);
SERVICE_TABLE_ENTRYW dispatchTable[] = { { SZSERVICENAME, (LPSERVICE_MAIN_FUNCTIONW)PSTOREServiceMain }, { NULL, NULL } };
#ifdef WIN95_LEGACY
if (!fIsNT) goto dispatch95;
#endif // WIN95_LEGACY
// if it doesn't match any of the above parameters
// the service control manager may be starting the service
// so we must call StartServiceCtrlDispatcher
if(!FIsWinNT5()) { if (!StartServiceCtrlDispatcherW(dispatchTable)) AddToMessageLog(L"StartServiceCtrlDispatcher failed."); } else { PSTOREServiceMain( 0, NULL ); }
return RTN_OK;
#ifdef WIN95_LEGACY
dispatch95:
//
// Win95 doesn't support services, except as pseudo-.exe files
//
HMODULE hKernel = GetModuleHandleA("kernel32.dll"); if (NULL == hKernel) { AddToMessageLog(L"RegisterServiceProcess module handle failed"); return RTN_ERROR_INIT; }
// inline typedef: COOL!
typedef DWORD REGISTERSERVICEPROCESS( DWORD dwProcessId, DWORD dwServiceType);
REGISTERSERVICEPROCESS* pfnRegSvcProc = NULL;
// Make sure Win95 Logoff won't stop our .exe
if (NULL == (pfnRegSvcProc = (REGISTERSERVICEPROCESS*)GetProcAddress(hKernel, "RegisterServiceProcess"))) { AddToMessageLog(L"RegisterServiceProcess failed"); return RTN_ERROR_INIT; }
pfnRegSvcProc(GetCurrentProcessId(), TRUE); // register this process ID as a service process
//
// call re-entry point and return with result of it.
//
iRet = ServiceStart(0, 0);
if(iRet != ERROR_SUCCESS) AddToMessageLog(L"ServiceStart error!");
return iRet;
#endif // WIN95_LEGACY
}
//
// FUNCTION: PSTOREServiceMain
//
// 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 PSTOREServiceMain(DWORD dwArgc, LPWSTR * /*lpszArgv*/) { DWORD dwLastError = ERROR_SUCCESS;
// register our service control handler:
//
sshStatusHandle = RegisterServiceCtrlHandlerW( SZSERVICENAME, service_ctrl);
if (!sshStatusHandle) return;
// 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
)) return ;
dwLastError = ServiceStart(0, 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.
//
case SERVICE_CONTROL_STOP:
//
// tell the SCM we are stopping before triggering StopService() code
// to avoid potential race condition during STOP_PENDING -> STOPPED transition
//
ssStatus.dwCurrentState = SERVICE_STOP_PENDING; ReportStatusToSCMgr(ssStatus.dwCurrentState, 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 (dwCurrentState == SERVICE_START_PENDING) ssStatus.dwControlsAccepted = 0; else ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
ssStatus.dwCurrentState = dwCurrentState; if(dwWin32ExitCode == 0) { ssStatus.dwWin32ExitCode = 0; } else { ssStatus.dwServiceSpecificExitCode = dwWin32ExitCode; ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; }
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(L"SetServiceStatus"); }
return fResult; }
//
// FUNCTION: AddToMessageLog(LPWSTR lpszMsg)
//
// PURPOSE: Allows any thread to log an error message
//
// PARAMETERS:
// lpszMsg - text for message
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
VOID AddToMessageLog(LPWSTR lpszMsg) { DWORD dwLastError = GetLastError();
if(FIsWinNT()) {
//
// WinNT: Use event logging to log the error.
//
WCHAR szMsg[512]; HANDLE hEventSource; LPWSTR lpszStrings[2];
hEventSource = RegisterEventSourceW(NULL, SZSERVICENAME);
if(hEventSource == NULL) return;
wsprintfW(szMsg, L"%s error: %lu", SZSERVICENAME, dwLastError); lpszStrings[0] = szMsg; lpszStrings[1] = lpszMsg;
ReportEventW(hEventSource, // handle of event source
EVENTLOG_ERROR_TYPE, // event type
0, // event category
0, // event ID
NULL, // current user's SID
2, // strings in lpszStrings
0, // no bytes of raw data
(LPCWSTR*)lpszStrings, // array of error strings
NULL); // no raw data
(VOID) DeregisterEventSource(hEventSource);
} #ifdef WIN95_LEGACY
else {
//
// Win95: log error to file
//
HANDLE hFile; SYSTEMTIME st; CHAR szMsgOut[512]; DWORD cchMsgOut; DWORD dwBytesWritten;
hFile = CreateFileA( "pstore.log", GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL );
if(hFile == INVALID_HANDLE_VALUE) return;
GetSystemTime( &st );
cchMsgOut = wsprintfA(szMsgOut, "%.2u-%.2u-%.2u %.2u:%.2u:%.2u %ls (rc=%lu)\015\012", st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond, lpszMsg, dwLastError );
//
// seek to EOF
//
SetFilePointer(hFile, 0, NULL, FILE_END);
WriteFile(hFile, szMsgOut, cchMsgOut, &dwBytesWritten, NULL); CloseHandle(hFile); } #endif // WIN95_LEGACY
}
RPC_STATUS RPC_ENTRY PstoreCallback( RPC_IF_HANDLE idIF, PVOID pCtx) { RPC_STATUS Status; PWSTR pBinding = NULL; PWSTR pProtSeq = NULL;
Status = RpcBindingToStringBinding(pCtx, &pBinding);
if(Status != RPC_S_OK) { goto cleanup; }
Status = RpcStringBindingParse(pBinding, NULL, &pProtSeq, NULL, NULL, NULL); if(Status != RPC_S_OK) { goto cleanup; }
// Make sure caller is using local RPC
if(CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, pProtSeq, -1, PSTORE_LOCAL_PROT_SEQ, -1) != CSTR_EQUAL) { Status = ERROR_ACCESS_DENIED; goto cleanup; }
Status = RPC_S_OK;
cleanup:
if(pProtSeq) { RpcStringFree(&pProtSeq); }
if(pBinding) { RpcStringFree(&pBinding); }
return Status; }
//
// FUNCTION: ServiceStart
//
// COMMENTS:
// The service
// stops when hServerStopEvent is signalled
DWORD ServiceStart( HINSTANCE hInstance, int nCmdShow ) { SECURITY_ATTRIBUTES sa;
PSID pEveryoneSid = NULL; SECURITY_DESCRIPTOR sdInitEvent; RPC_STATUS status = 0;
SID_IDENTIFIER_AUTHORITY sia = SECURITY_WORLD_SID_AUTHORITY; DWORD EveryoneSidBuffer[6];
DWORD dwAclSize;
DWORD dwLastError = ERROR_SUCCESS; BOOL fStartedKeyService = FALSE; BOOL bListConstruct = FALSE;
pEveryoneSid = (PSID)EveryoneSidBuffer; InitializeSid(pEveryoneSid, &sia, 1); *(GetSidSubAuthority( pEveryoneSid, 0)) = SECURITY_WORLD_RID;
dwAclSize = sizeof(ACL) + 1 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) + GetLengthSid(pEveryoneSid) ;
pDaclInitEvent = (PACL)SSAlloc(dwAclSize); if(pDaclInitEvent == NULL) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; }
if(!InitializeAcl(pDaclInitEvent, dwAclSize, ACL_REVISION)) { dwLastError = GetLastError(); goto cleanup; }
if(!AddAccessAllowedAce( pDaclInitEvent, ACL_REVISION, SYNCHRONIZE, pEveryoneSid )) { dwLastError = GetLastError(); goto cleanup; }
if(!InitializeSecurityDescriptor( &sdInitEvent, SECURITY_DESCRIPTOR_REVISION )) { dwLastError = GetLastError(); goto cleanup; }
if(!SetSecurityDescriptorDacl( &sdInitEvent, TRUE, pDaclInitEvent, FALSE )) { dwLastError = GetLastError(); goto cleanup; }
sa.lpSecurityDescriptor = &sdInitEvent;
//
// create the event object. The control handler function signals
// this event when it receives the "stop" control code.
// On WinNT, we let security default to local system+admins access.
// On WinNT, the ServiceStop() API is the correct way to cause a service
// to stop, so we let Service Control manager dictate who can do it.
//
// Only on Win95 do we use a named event, in order to support shutting
// down the server cleanly on that platform, since Win95 does not support
// real services.
hServerStopEvent = CreateEventA( NULL, TRUE, // manual reset event
FALSE, // not-signalled
(FIsWinNT() ? NULL : PST_EVENT_STOP) // WinNT: unnamed, Win95 named
);
//
// if event already exists, terminate quietly so that only one instance
// of the service is allowed.
//
if(hServerStopEvent && GetLastError() == ERROR_ALREADY_EXISTS) { CloseHandle(hServerStopEvent); hServerStopEvent = NULL; }
if(hServerStopEvent == NULL) { dwLastError = GetLastError(); goto cleanup; }
sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = FALSE;
hServiceStarted = CreateEventA( &sa, // security attributes for WinNT
TRUE, // manual reset event
FALSE, // not-signalled
PST_EVENT_INIT_NT5 );
if(hServiceStarted == NULL) { dwLastError = GetLastError(); goto cleanup; }
//
// free Dacl on event, since we no longer need it.
//
if(pDaclInitEvent) { SSFree(pDaclInitEvent); pDaclInitEvent = NULL; }
//
// report the status to the service control manager.
// (service start still pending).
//
if (!ReportStatusToSCMgr( SERVICE_START_PENDING, // service state
NO_ERROR, // exit code
3000 // wait hint
)) { dwLastError = GetLastError(); goto cleanup; }
bListConstruct = ListConstruct();
if(!bListConstruct) { dwLastError = GetLastError(); goto cleanup; }
status = RpcServerUseProtseqEpW(PSTORE_LOCAL_PROT_SEQ, //ncalrpc
RPC_C_PROTSEQ_MAX_REQS_DEFAULT, PSTORE_LOCAL_ENDPOINT, //protected_storage
NULL); //Security Descriptor
if(RPC_S_DUPLICATE_ENDPOINT == status) { status = RPC_S_OK; }
if ( status != RPC_S_OK ) { dwLastError = status; goto cleanup; }
status = RpcServerRegisterIfEx(s_IPStoreProv_v1_0_s_ifspec, NULL, NULL, RPC_IF_AUTOLISTEN, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, PstoreCallback);
if ( status != RPC_S_OK ) { dwLastError = status; goto cleanup; }
SetEvent(hServiceStarted); // signal service is ready to take requests
//
// report the status to the service control manager.
//
if (!ReportStatusToSCMgr( SERVICE_RUNNING, // service state
NO_ERROR, // exit code
0 // wait hint
)) { dwLastError = GetLastError(); goto cleanup; }
//
// on WinNT5, ask services.exe to notify us when the service is shutting
// down, and return this thread to the work item queue.
//
if(!RegisterWaitForSingleObject( &hRegisteredWait, hServerStopEvent, // wait handle
TerminationNotify, // callback fcn
NULL, // parameter
INFINITE, // timeout
WT_EXECUTELONGFUNCTION | WT_EXECUTEONLYONCE )) {
hRegisteredWait = NULL; dwLastError = GetLastError(); }
return dwLastError;
cleanup:
TeardownServer( dwLastError );
return dwLastError; }
VOID NTAPI TerminationNotify( PVOID Context, BOOLEAN TimerOrWaitFired ) /*++
Routine Description:
This function gets called by a services worker thread when the termination event gets signaled.
Arguments:
Return Value:
--*/ { //
// per JSchwart:
// safe to unregister during callback.
//
if( hRegisteredWait ) { UnregisterWaitEx( hRegisteredWait, NULL ); hRegisteredWait = NULL; }
TeardownServer( ERROR_SUCCESS ); }
VOID TeardownServer( DWORD dwLastError ) { //
// ignore errors because we are shutting down
//
RpcServerUnregisterIf(s_IPStoreProv_v1_0_s_ifspec, 0, 0);
if(pDaclInitEvent) { SSFree(pDaclInitEvent); pDaclInitEvent = NULL; }
if(hServiceStarted) { ResetEvent(hServiceStarted); CloseHandle(hServiceStarted); hServiceStarted = NULL; }
if(hServerStopEvent) { SetEvent(hServerStopEvent); // make event signalled to release anyone waiting for termination
CloseHandle(hServerStopEvent); hServerStopEvent = NULL; }
ListTeardown();
ReportStatusToSCMgr( SERVICE_STOPPED, dwLastError, 0 );
}
// FUNCTION: ServiceStop
//
// PURPOSE: Stops the service
//
// COMMENTS:
// If a ServiceStop procedure is going to
// take longer than 3 seconds to execute,
// it should spawn a thread to execute the
// stop code, and return. Otherwise, the
// ServiceControlManager will believe that
// the service has stopped responding.
//
VOID ServiceStop() {
if(hServiceStarted) { ResetEvent(hServiceStarted); CloseHandle(hServiceStarted); hServiceStarted = NULL; }
if(hServerStopEvent) PulseEvent(hServerStopEvent); // signal waiting threads and reset to non-signalled
}
/*********************************************************************/ /* MIDL allocate and free */ /*********************************************************************/
void __RPC_FAR * __RPC_API midl_user_allocate(size_t len) { return(SSAlloc(len)); }
void __RPC_API midl_user_free(void __RPC_FAR * ptr) { //
// sfield: zero memory before freeing it.
// do this because RPC allocates alot on our behalf, and we want to
// be as sanitary as possible with respect to not letting anything
// sensitive go to pagefile.
//
ZeroMemory( ptr, SSSize( ptr ) ); SSFree(ptr); }
|