You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
536 lines
15 KiB
536 lines
15 KiB
/*++
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|