|
|
/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
srventry.c
Abstract:
This module contains the main entry for the User-mode Plug-and-Play Service. It also contains the service control handler and service status update routines.
Author:
Paula Tomlinson (paulat) 6-8-1995
Environment:
User-mode only.
Revision History:
8-June-1995 paulat
Creation and initial implementation.
--*/
//
// includes
//
#include "precomp.h"
#pragma hdrstop
#include "umpnpi.h"
#include <svcsp.h>
//
// private prototypes
//
DWORD PnPControlHandlerEx( IN DWORD dwControl, IN DWORD dwEventType, IN LPVOID lpEventData, IN LPVOID lpContext );
VOID PnPServiceStatusUpdate( SERVICE_STATUS_HANDLE hSvcHandle, DWORD dwState, DWORD dwCheckPoint, DWORD dwExitCode );
RPC_STATUS CALLBACK PnPRpcIfCallback( RPC_IF_HANDLE* Interface, void* Context );
//
// global data
//
PSVCS_GLOBAL_DATA PnPGlobalData = NULL; HANDLE PnPGlobalSvcRefHandle = NULL; DWORD CurrentServiceState = SERVICE_START_PENDING; SERVICE_STATUS_HANDLE hSvcHandle = 0;
VOID SvcEntry_PlugPlay( DWORD argc, LPWSTR argv[], PSVCS_GLOBAL_DATA SvcsGlobalData, HANDLE SvcRefHandle ) /*++
Routine Description:
This is the main routine for the User-mode Plug-and-Play Service. It registers itself as an RPC server and notifies the Service Controller of the PNP service control entry point.
Arguments:
argc, argv - Command-line arguments, not used.
SvcsGlobalData - Global data for services running in services.exe that contains function entry points and pipe name for establishing an RPC server interface for this service.
SvcRefHandle - Service reference handle, not used.
Return Value:
None.
Note:
None.
--*/ { RPC_STATUS RpcStatus; HANDLE hThread; DWORD ThreadID;
UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv);
//
// Save the global data and service reference handle in global variables
//
PnPGlobalSvcRefHandle = SvcRefHandle; PnPGlobalData = SvcsGlobalData;
//
// Register our service ctrl handler
//
if ((hSvcHandle = RegisterServiceCtrlHandlerEx(L"PlugPlay", (LPHANDLER_FUNCTION_EX)PnPControlHandlerEx, NULL)) == 0) {
KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "UMPNPMGR: RegisterServiceCtrlHandlerEx failed, error = %d\n", GetLastError())); return; }
//
// Notify Service Controller that we're alive
//
PnPServiceStatusUpdate(hSvcHandle, SERVICE_START_PENDING, 1, 0);
//
// Create the Plug and Play security object, used to determine client access
// to the PlugPlay server APIs. Note that since the security object is used
// by the PNP RPC interface security callback routine, it must be created
// before the PNP RPC interface can be registered, below.
//
if (!CreatePlugPlaySecurityObject()) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "UMPNPMGR: CreatePlugPlayManagerSecurityDescriptor failed!\n")); return; }
//
// Notify Service Controller that we're alive
//
PnPServiceStatusUpdate(hSvcHandle, SERVICE_START_PENDING, 2, 0);
//
// Register the PNP RPC interface, and specify a security callback routine
// for the interface. The callback will be called for all methods in the
// interface, before RPC has marshalled any data to the stubs. This allows
// us to reject calls before RPC has allocated any memory from our process,
// preventing possible DOS attacks by unauthorized clients.
//
// Few things to note about how we do this...
//
// First, NOTE that we previously used the RPC start/stop server routines
// provided by SVCS_GLOBAL_DATA StartRpcServer/StopRpcServer, however those
// did not allow for a security callback routine to be registered for the
// interface (RpcServerRegisterIf). Instead, we now register and unregister
// the PNP RPC interface directly ourself, using RpcServerRegisterIfEx.
//
// Also NOTE that technically, we should also register the named pipe
// endpoint and protocol sequence that our CFGMGR32 client uses to access
// this interface ("ntsvcs", "ncacn_np") with the RPC runtime -- BUT because
// we know that our server resides in the services.exe process along with
// the SCM, and that the same endopint and protocol is also used by the SCM,
// we know that it has already been registered for the process long before
// our service is started, and will exist after our service is stopped.
//
// And also NOTE that technically, we should also make sure that the RPC
// runtime is listening within the process when we register our interface,
// and that it remains listening until we have unregistered out interface --
// BUT because we're in services.exe, RPC should already be listening in the
// process for the SCM before and after our service needs it to be (see
// above). We don't really need to start RPC listening ourselves either,
// but there's no harm in registering our interface as "auto-listen", so
// we'll do that anyways.
//
// EXTRA NOTE -- This is really just a safeguard replacement for the
// refcounting for that would ordinarily have been done by the
// SVCS_GLOBAL_DATA StartRpcServer, StopRpcServer routines that the SCM
// and other servers in this process use to register their interfaces.
// These routines refcount the need to listen in the process by counting
// the number of interfaces in the process that have been registered by
// those routines. Since we are now registering the PNP interface ourself
// (outside these routines), no refcounting is done for our interface. By
// registering our interface as "auto-listen", We can make sure that the
// RPC runtime is listening when we register our interface, and that it
// remains listening until it is unregistered (regardless of the listening
// state that is started and stopped on behalf of the other servers in
// this process).
//
// ... Basically, because we share a process with the SCM, the only work we
// really need to do ourselves is register our own interface. If we ever
// move the PlugPlay service outside of the services.exe process, we will
// need to do everything else mentioned above ourselves, as well.
//
//
// Even though we will register our interface as "auto-listen", verify that
// this process is already listening via a previous call to RpcServerListen
// (note that other "auto-listen" interfaces don't count). This tells us
// that the endpoint has already been registered, and RPC is already
// listening, on behalf of some other server.
//
ASSERT(RpcMgmtIsServerListening(NULL) == RPC_S_OK);
//
// Register the PNP RPC interface.
//
RpcStatus = RpcServerRegisterIfEx( pnp_ServerIfHandle, NULL, NULL, RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH, RPC_C_LISTEN_MAX_CALLS_DEFAULT, PnPRpcIfCallback);
if (RpcStatus != RPC_S_OK) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "UMPNPMGR: RpcServerRegisterIfEx failed with RpcStatus = %d\n", RpcStatus)); return; }
//
// Notify Service Controller that we're alive
//
PnPServiceStatusUpdate(hSvcHandle, SERVICE_START_PENDING, 3, 0);
//
// Initialize pnp manager
//
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)InitializePnPManager, NULL, 0, &ThreadID);
if (hThread != NULL) { CloseHandle(hThread); }
//
// Notify Service Controller that we're now running
//
PnPServiceStatusUpdate(hSvcHandle, SERVICE_RUNNING, 0, 0);
//
// Service initialization is complete.
//
return;
} // SvcEntry_PlugPlay
DWORD PnPControlHandlerEx( IN DWORD dwControl, IN DWORD dwEventType, IN LPVOID lpEventData, IN LPVOID lpContext ) /*++
Routine Description:
This is the service control handler of the Plug-and-Play service.
Arguments:
dwControl - The requested control code.
dwEventType - The type of event that has occurred.
lpEventData - Additional device information, if required.
lpContext - User-defined data, not used.
Return Value:
Returns NO_ERROR if sucessful, otherwise returns an error code describing the problem.
--*/ { RPC_STATUS RpcStatus;
UNREFERENCED_PARAMETER(lpContext);
switch (dwControl) {
case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: //
// If we aren't already in the middle of a stop, then
// stop the PNP service now and perform the necessary cleanup.
//
if (CurrentServiceState != SERVICE_STOPPED && CurrentServiceState != SERVICE_STOP_PENDING) {
//
// Notify Service Controller that we're stopping
//
PnPServiceStatusUpdate(hSvcHandle, SERVICE_STOP_PENDING, 1, 0);
//
// Unregister the RPC server interface registered by our service
// entry point, do not wait for outstanding calls to complete
// before unregistering the interface.
//
RpcStatus = RpcServerUnregisterIf( pnp_ServerIfHandle, NULL, 0);
if (RpcStatus != RPC_S_OK) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "UMPNPMGR: RpcServerUnregisterIf failed with RpcStatus = %d\n", RpcStatus)); }
//
// Destroy the Plug and Play security object
//
DestroyPlugPlaySecurityObject();
//
// Notify Service Controller that we've now stopped
//
PnPServiceStatusUpdate(hSvcHandle, SERVICE_STOPPED, 0, 0); } break;
case SERVICE_CONTROL_INTERROGATE: //
// Request to immediately notify Service Controller of
// current status
//
PnPServiceStatusUpdate(hSvcHandle, CurrentServiceState, 0, 0); break;
case SERVICE_CONTROL_SESSIONCHANGE: //
// Session change notification.
//
SessionNotificationHandler(dwEventType, (PWTSSESSION_NOTIFICATION)lpEventData); break;
default: //
// No special handling for any other service controls.
//
break; }
return NO_ERROR;
} // PnPControlHandlerEx
VOID PnPServiceStatusUpdate( SERVICE_STATUS_HANDLE hSvcHandle, DWORD dwState, DWORD dwCheckPoint, DWORD dwExitCode ) /*++
Routine Description:
This routine notifies the Service Controller of the current status of the Plug-and-Play service.
Arguments:
hSvcHandle - Supplies the service status handle for the Plug-and-Play service.
dwState - Specifies the current state of the service to report.
dwCheckPoint - Specifies an intermediate checkpoint for operations during which the state is pending.
dwExitCode - Specifies a service specific error code.
Return Value:
None.
Note:
This routine also updates the set of controls accepted by the service.
The PlugPlay service currently accepts the following controls when the service is running:
SERVICE_CONTROL_SHUTDOWN - the system is shutting down.
SERVICE_CONTROL_SESSIONCHANGE - the state of some remote or console session has changed.
--*/ { SERVICE_STATUS SvcStatus;
SvcStatus.dwServiceType = SERVICE_WIN32; SvcStatus.dwCurrentState = CurrentServiceState = dwState; SvcStatus.dwCheckPoint = dwCheckPoint;
if (dwState == SERVICE_RUNNING) { SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_SESSIONCHANGE; } else { SvcStatus.dwControlsAccepted = 0; }
if ((dwState == SERVICE_START_PENDING) || (dwState == SERVICE_STOP_PENDING)) { SvcStatus.dwWaitHint = 45000; // 45 seconds
} else { SvcStatus.dwWaitHint = 0; }
if (dwExitCode != 0) { SvcStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; SvcStatus.dwServiceSpecificExitCode = dwExitCode; } else { SvcStatus.dwWin32ExitCode = NO_ERROR; SvcStatus.dwServiceSpecificExitCode = 0; }
SetServiceStatus(hSvcHandle, &SvcStatus);
return;
} // PnPServiceStatusUpdate
RPC_STATUS CALLBACK PnPRpcIfCallback( RPC_IF_HANDLE* Interface, void* Context )
/*++
Routine Description:
RPC interface callback function for authenticating clients of the Plug and Play RPC server.
Arguments:
Interface - Supplies the UUID and version of the interface.
Context - Supplies a server binding handle representing the client
Return Value:
RPC_S_OK if an interface method can be called, RPC_S_ACCESS_DENIED if the interface method should not be called.
--*/
{ handle_t hBinding; RPC_STATUS RpcStatus = RPC_S_OK;
UNREFERENCED_PARAMETER(Interface);
//
// The Context supplied to the interface callback routine is an RPC binding
// handle.
//
hBinding = (handle_t)Context;
//
// Make sure that the provided RPC binding handle is not NULL.
//
// The RPC interface routines sometimes get called directly directly by the
// SCM and other internal routines, using a NULL binding handle. This
// security callback routine should only get called in the context of an RPC
// call, so the supplied binding handle should never be NULL.
//
ASSERT(hBinding != NULL);
//
// Verify client basic "read" access for all APIs.
//
if (!VerifyClientAccess(hBinding, PLUGPLAY_READ)) { RpcStatus = RPC_S_ACCESS_DENIED; goto Clean0; }
Clean0:
return RpcStatus;
} // PnPRpcIfCallback
|