mirror of
https://github.com/reactos/reactos.git
synced 2024-09-06 10:52:52 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
Dedicated to the hard work of Joachim Henze! xD This reverts part of commit043a98dd
(see also commitb2aeafca
). Contrary to what I assumed in commit043a98dd
(and was also assumed in the older commitb2aeafca
), we cannot use the singled-linked lists to queue and dequeue the PnP device-install events, because: - the events must be treated from the oldest to the newest ones, for consistency, otherwise this creates problems, as shown by e.g. CORE-16103; - the system singled-linked lists only offer access to the top of the list (like a stack) instead of to both the top and the bottom of the list, as would be required for a queue. Using the SLISTs would mean that only the newest-received events would be treated first, while the oldest (which were the first received) events would be treated last, and this is wrong. Therefore one must use e.g. the standard doubly-linked list. Also, using locked operations (insertion & removal) on the list of device-install events is necessary, because these operations are done concurrently by two different threads: PnpEventThread() and DeviceInstallThread(). Since the interlocked linked list functions are not available in user-mode, we need to use instead locking access through e.g. a mutex.
508 lines
16 KiB
C
508 lines
16 KiB
C
/*
|
|
* ReactOS kernel
|
|
* Copyright (C) 2005 ReactOS Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: base/services/umpnpmgr/umpnpmgr.c
|
|
* PURPOSE: User-mode Plug and Play manager
|
|
* PROGRAMMER: Eric Kohl (eric.kohl@reactos.org)
|
|
* Hervé Poussineau (hpoussin@reactos.org)
|
|
* Colin Finck (colin@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
|
|
/* GLOBALS ******************************************************************/
|
|
|
|
static WCHAR ServiceName[] = L"PlugPlay";
|
|
|
|
static SERVICE_STATUS_HANDLE ServiceStatusHandle;
|
|
static SERVICE_STATUS ServiceStatus;
|
|
|
|
HKEY hEnumKey = NULL;
|
|
HKEY hClassKey = NULL;
|
|
BOOL g_IsUISuppressed = FALSE;
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
static DWORD WINAPI
|
|
PnpEventThread(LPVOID lpParameter)
|
|
{
|
|
PLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData = {0, 0, 0, 0};
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
NTSTATUS Status;
|
|
RPC_STATUS RpcStatus;
|
|
PPLUGPLAY_EVENT_BLOCK PnpEvent, NewPnpEvent;
|
|
ULONG PnpEventSize;
|
|
|
|
UNREFERENCED_PARAMETER(lpParameter);
|
|
|
|
PnpEventSize = 0x1000;
|
|
PnpEvent = HeapAlloc(GetProcessHeap(), 0, PnpEventSize);
|
|
if (PnpEvent == NULL)
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
for (;;)
|
|
{
|
|
DPRINT("Calling NtGetPlugPlayEvent()\n");
|
|
|
|
/* Wait for the next PnP event */
|
|
Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
|
|
|
|
/* Resize the buffer for the PnP event if it's too small */
|
|
if (Status == STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
PnpEventSize += 0x400;
|
|
NewPnpEvent = HeapReAlloc(GetProcessHeap(), 0, PnpEvent, PnpEventSize);
|
|
if (NewPnpEvent == NULL)
|
|
{
|
|
dwRet = ERROR_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
PnpEvent = NewPnpEvent;
|
|
continue;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status);
|
|
break;
|
|
}
|
|
|
|
/* Process the PnP event */
|
|
DPRINT("Received PnP Event\n");
|
|
if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ENUMERATED, &RpcStatus))
|
|
{
|
|
DeviceInstallParams* Params;
|
|
DWORD len;
|
|
DWORD DeviceIdLength;
|
|
|
|
DPRINT("Device enumerated: %S\n", PnpEvent->TargetDevice.DeviceIds);
|
|
|
|
DeviceIdLength = lstrlenW(PnpEvent->TargetDevice.DeviceIds);
|
|
if (DeviceIdLength)
|
|
{
|
|
/* Allocate a new device-install event */
|
|
len = FIELD_OFFSET(DeviceInstallParams, DeviceIds) + (DeviceIdLength + 1) * sizeof(WCHAR);
|
|
Params = HeapAlloc(GetProcessHeap(), 0, len);
|
|
if (Params)
|
|
{
|
|
wcscpy(Params->DeviceIds, PnpEvent->TargetDevice.DeviceIds);
|
|
|
|
/* Queue the event (will be dequeued by DeviceInstallThread) */
|
|
WaitForSingleObject(hDeviceInstallListMutex, INFINITE);
|
|
InsertTailList(&DeviceInstallListHead, &Params->ListEntry);
|
|
ReleaseMutex(hDeviceInstallListMutex);
|
|
|
|
SetEvent(hDeviceInstallListNotEmpty);
|
|
}
|
|
}
|
|
}
|
|
else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ARRIVAL, &RpcStatus))
|
|
{
|
|
// DWORD dwRecipient;
|
|
|
|
DPRINT("Device arrival: %S\n", PnpEvent->TargetDevice.DeviceIds);
|
|
|
|
// dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
|
|
// BroadcastSystemMessageW(BSF_POSTMESSAGE,
|
|
// &dwRecipient,
|
|
// WM_DEVICECHANGE,
|
|
// DBT_DEVNODES_CHANGED,
|
|
// 0);
|
|
SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
|
|
}
|
|
else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_EJECT_VETOED, &RpcStatus))
|
|
{
|
|
DPRINT1("Eject vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds);
|
|
}
|
|
else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_KERNEL_INITIATED_EJECT, &RpcStatus))
|
|
{
|
|
DPRINT1("Kernel initiated eject: %S\n", PnpEvent->TargetDevice.DeviceIds);
|
|
}
|
|
else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SAFE_REMOVAL, &RpcStatus))
|
|
{
|
|
// DWORD dwRecipient;
|
|
|
|
DPRINT1("Safe removal: %S\n", PnpEvent->TargetDevice.DeviceIds);
|
|
|
|
// dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
|
|
// BroadcastSystemMessageW(BSF_POSTMESSAGE,
|
|
// &dwRecipient,
|
|
// WM_DEVICECHANGE,
|
|
// DBT_DEVNODES_CHANGED,
|
|
// 0);
|
|
SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
|
|
}
|
|
else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SURPRISE_REMOVAL, &RpcStatus))
|
|
{
|
|
// DWORD dwRecipient;
|
|
|
|
DPRINT1("Surprise removal: %S\n", PnpEvent->TargetDevice.DeviceIds);
|
|
|
|
// dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
|
|
// BroadcastSystemMessageW(BSF_POSTMESSAGE,
|
|
// &dwRecipient,
|
|
// WM_DEVICECHANGE,
|
|
// DBT_DEVNODES_CHANGED,
|
|
// 0);
|
|
SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
|
|
}
|
|
else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVAL_VETOED, &RpcStatus))
|
|
{
|
|
DPRINT1("Removal vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds);
|
|
}
|
|
else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVE_PENDING, &RpcStatus))
|
|
{
|
|
DPRINT1("Removal pending: %S\n", PnpEvent->TargetDevice.DeviceIds);
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
|
|
PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3,
|
|
PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2],
|
|
PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5],
|
|
PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]);
|
|
}
|
|
|
|
/* Dequeue the current PnP event and signal the next one */
|
|
Status = NtPlugPlayControl(PlugPlayControlUserResponse,
|
|
&ResponseData,
|
|
sizeof(ResponseData));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status);
|
|
break;
|
|
}
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, PnpEvent);
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
static VOID
|
|
UpdateServiceStatus(DWORD dwState)
|
|
{
|
|
ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
|
ServiceStatus.dwCurrentState = dwState;
|
|
ServiceStatus.dwControlsAccepted = 0;
|
|
ServiceStatus.dwWin32ExitCode = 0;
|
|
ServiceStatus.dwServiceSpecificExitCode = 0;
|
|
ServiceStatus.dwCheckPoint = 0;
|
|
|
|
if (dwState == SERVICE_START_PENDING ||
|
|
dwState == SERVICE_STOP_PENDING ||
|
|
dwState == SERVICE_PAUSE_PENDING ||
|
|
dwState == SERVICE_CONTINUE_PENDING)
|
|
ServiceStatus.dwWaitHint = 10000;
|
|
else
|
|
ServiceStatus.dwWaitHint = 0;
|
|
|
|
SetServiceStatus(ServiceStatusHandle,
|
|
&ServiceStatus);
|
|
}
|
|
|
|
|
|
static DWORD WINAPI
|
|
ServiceControlHandler(DWORD dwControl,
|
|
DWORD dwEventType,
|
|
LPVOID lpEventData,
|
|
LPVOID lpContext)
|
|
{
|
|
DPRINT1("ServiceControlHandler() called\n");
|
|
|
|
switch (dwControl)
|
|
{
|
|
case SERVICE_CONTROL_STOP:
|
|
DPRINT1(" SERVICE_CONTROL_STOP received\n");
|
|
/* Stop listening to RPC Messages */
|
|
RpcMgmtStopServerListening(NULL);
|
|
UpdateServiceStatus(SERVICE_STOPPED);
|
|
return ERROR_SUCCESS;
|
|
|
|
case SERVICE_CONTROL_PAUSE:
|
|
DPRINT1(" SERVICE_CONTROL_PAUSE received\n");
|
|
UpdateServiceStatus(SERVICE_PAUSED);
|
|
return ERROR_SUCCESS;
|
|
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
DPRINT1(" SERVICE_CONTROL_CONTINUE received\n");
|
|
UpdateServiceStatus(SERVICE_RUNNING);
|
|
return ERROR_SUCCESS;
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
DPRINT1(" SERVICE_CONTROL_INTERROGATE received\n");
|
|
SetServiceStatus(ServiceStatusHandle,
|
|
&ServiceStatus);
|
|
return ERROR_SUCCESS;
|
|
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
DPRINT1(" SERVICE_CONTROL_SHUTDOWN received\n");
|
|
/* Stop listening to RPC Messages */
|
|
RpcMgmtStopServerListening(NULL);
|
|
UpdateServiceStatus(SERVICE_STOPPED);
|
|
return ERROR_SUCCESS;
|
|
|
|
default :
|
|
DPRINT1(" Control %lu received\n", dwControl);
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
|
|
static DWORD
|
|
GetBooleanRegValue(
|
|
IN HKEY hKey,
|
|
IN PCWSTR lpSubKey,
|
|
IN PCWSTR lpValue,
|
|
OUT PBOOL pValue)
|
|
{
|
|
DWORD dwError, dwType, dwData;
|
|
DWORD cbData = sizeof(dwData);
|
|
HKEY hSubKey = NULL;
|
|
|
|
/* Default value */
|
|
*pValue = FALSE;
|
|
|
|
dwError = RegOpenKeyExW(hKey,
|
|
lpSubKey,
|
|
0,
|
|
KEY_READ,
|
|
&hSubKey);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT("GetBooleanRegValue(): RegOpenKeyExW() has failed to open '%S' key! (Error: %lu)\n",
|
|
lpSubKey, dwError);
|
|
return dwError;
|
|
}
|
|
|
|
dwError = RegQueryValueExW(hSubKey,
|
|
lpValue,
|
|
0,
|
|
&dwType,
|
|
(PBYTE)&dwData,
|
|
&cbData);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT("GetBooleanRegValue(): RegQueryValueExW() has failed to query '%S' value! (Error: %lu)\n",
|
|
lpValue, dwError);
|
|
goto Cleanup;
|
|
}
|
|
if (dwType != REG_DWORD)
|
|
{
|
|
DPRINT("GetBooleanRegValue(): The value is not of REG_DWORD type!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Return the value */
|
|
*pValue = (dwData == 1);
|
|
|
|
Cleanup:
|
|
RegCloseKey(hSubKey);
|
|
|
|
return dwError;
|
|
}
|
|
|
|
BOOL
|
|
GetSuppressNewUIValue(VOID)
|
|
{
|
|
BOOL bSuppressNewHWUI = FALSE;
|
|
|
|
/*
|
|
* Query the SuppressNewHWUI policy registry value. Don't cache it
|
|
* as we want to update our behaviour in consequence.
|
|
*/
|
|
GetBooleanRegValue(HKEY_LOCAL_MACHINE,
|
|
L"Software\\Policies\\Microsoft\\Windows\\DeviceInstall\\Settings",
|
|
L"SuppressNewHWUI",
|
|
&bSuppressNewHWUI);
|
|
if (bSuppressNewHWUI)
|
|
DPRINT("GetSuppressNewUIValue(): newdev.dll's wizard UI won't be shown!\n");
|
|
|
|
return bSuppressNewHWUI;
|
|
}
|
|
|
|
VOID WINAPI
|
|
ServiceMain(DWORD argc, LPTSTR *argv)
|
|
{
|
|
HANDLE hThread;
|
|
DWORD dwThreadId;
|
|
|
|
UNREFERENCED_PARAMETER(argc);
|
|
UNREFERENCED_PARAMETER(argv);
|
|
|
|
DPRINT("ServiceMain() called\n");
|
|
|
|
ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
|
|
ServiceControlHandler,
|
|
NULL);
|
|
if (!ServiceStatusHandle)
|
|
{
|
|
DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
UpdateServiceStatus(SERVICE_START_PENDING);
|
|
|
|
hThread = CreateThread(NULL,
|
|
0,
|
|
PnpEventThread,
|
|
NULL,
|
|
0,
|
|
&dwThreadId);
|
|
if (hThread != NULL)
|
|
CloseHandle(hThread);
|
|
|
|
hThread = CreateThread(NULL,
|
|
0,
|
|
RpcServerThread,
|
|
NULL,
|
|
0,
|
|
&dwThreadId);
|
|
if (hThread != NULL)
|
|
CloseHandle(hThread);
|
|
|
|
hThread = CreateThread(NULL,
|
|
0,
|
|
DeviceInstallThread,
|
|
NULL,
|
|
0,
|
|
&dwThreadId);
|
|
if (hThread != NULL)
|
|
CloseHandle(hThread);
|
|
|
|
UpdateServiceStatus(SERVICE_RUNNING);
|
|
|
|
DPRINT("ServiceMain() done\n");
|
|
}
|
|
|
|
static DWORD
|
|
InitializePnPManager(VOID)
|
|
{
|
|
BOOLEAN OldValue;
|
|
DWORD dwError;
|
|
|
|
DPRINT("UMPNPMGR: InitializePnPManager() started\n");
|
|
|
|
/* We need this privilege for using CreateProcessAsUserW */
|
|
RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &OldValue);
|
|
|
|
hInstallEvent = CreateEventW(NULL, TRUE, SetupIsActive()/*FALSE*/, NULL);
|
|
if (hInstallEvent == NULL)
|
|
{
|
|
dwError = GetLastError();
|
|
DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError);
|
|
return dwError;
|
|
}
|
|
|
|
hNoPendingInstalls = CreateEventW(NULL,
|
|
TRUE,
|
|
FALSE,
|
|
L"Global\\PnP_No_Pending_Install_Events");
|
|
if (hNoPendingInstalls == NULL)
|
|
{
|
|
dwError = GetLastError();
|
|
DPRINT1("Could not create the Pending-Install Event! (Error %lu)\n", dwError);
|
|
return dwError;
|
|
}
|
|
|
|
/*
|
|
* Initialize the device-install event list
|
|
*/
|
|
|
|
hDeviceInstallListNotEmpty = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
if (hDeviceInstallListNotEmpty == NULL)
|
|
{
|
|
dwError = GetLastError();
|
|
DPRINT1("Could not create the List Event! (Error %lu)\n", dwError);
|
|
return dwError;
|
|
}
|
|
|
|
hDeviceInstallListMutex = CreateMutexW(NULL, FALSE, NULL);
|
|
if (hDeviceInstallListMutex == NULL)
|
|
{
|
|
dwError = GetLastError();
|
|
DPRINT1("Could not create the List Mutex! (Error %lu)\n", dwError);
|
|
return dwError;
|
|
}
|
|
InitializeListHead(&DeviceInstallListHead);
|
|
|
|
/* Query the SuppressUI registry value and cache it for our whole lifetime */
|
|
GetBooleanRegValue(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services\\PlugPlay\\Parameters",
|
|
L"SuppressUI",
|
|
&g_IsUISuppressed);
|
|
if (g_IsUISuppressed)
|
|
DPRINT("UMPNPMGR: newdev.dll's wizard UI won't be shown!\n");
|
|
|
|
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Enum",
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hEnumKey);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError);
|
|
return dwError;
|
|
}
|
|
|
|
dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Control\\Class",
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hClassKey);
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError);
|
|
return dwError;
|
|
}
|
|
|
|
DPRINT("UMPNPMGR: InitializePnPManager() done\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
DllMain(HINSTANCE hinstDLL,
|
|
DWORD fdwReason,
|
|
LPVOID lpvReserved)
|
|
{
|
|
switch (fdwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
DisableThreadLibraryCalls(hinstDLL);
|
|
InitializePnPManager();
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* EOF */
|