mirror of
https://github.com/reactos/reactos.git
synced 2024-12-31 19:42:51 +00:00
ffc96d26ec
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.
392 lines
12 KiB
C
392 lines
12 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/install.c
|
|
* PURPOSE: Device installer
|
|
* 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 ******************************************************************/
|
|
|
|
HANDLE hUserToken = NULL;
|
|
HANDLE hInstallEvent = NULL;
|
|
HANDLE hNoPendingInstalls = NULL;
|
|
|
|
/* Device-install event list */
|
|
HANDLE hDeviceInstallListMutex;
|
|
LIST_ENTRY DeviceInstallListHead;
|
|
HANDLE hDeviceInstallListNotEmpty;
|
|
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
static BOOL
|
|
InstallDevice(PCWSTR DeviceInstance, BOOL ShowWizard)
|
|
{
|
|
BOOL DeviceInstalled = FALSE;
|
|
DWORD BytesWritten;
|
|
DWORD Value;
|
|
HANDLE hInstallEvent;
|
|
HANDLE hPipe = INVALID_HANDLE_VALUE;
|
|
LPVOID Environment = NULL;
|
|
PROCESS_INFORMATION ProcessInfo;
|
|
STARTUPINFOW StartupInfo;
|
|
UUID RandomUuid;
|
|
HKEY DeviceKey;
|
|
|
|
/* The following lengths are constant (see below), they cannot overflow */
|
|
WCHAR CommandLine[116];
|
|
WCHAR InstallEventName[73];
|
|
WCHAR PipeName[74];
|
|
WCHAR UuidString[39];
|
|
|
|
DPRINT("InstallDevice(%S, %d)\n", DeviceInstance, ShowWizard);
|
|
|
|
ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
|
|
|
|
if (RegOpenKeyExW(hEnumKey,
|
|
DeviceInstance,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&DeviceKey) == ERROR_SUCCESS)
|
|
{
|
|
if (RegQueryValueExW(DeviceKey,
|
|
L"Class",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL) == ERROR_SUCCESS)
|
|
{
|
|
DPRINT("No need to install: %S\n", DeviceInstance);
|
|
RegCloseKey(DeviceKey);
|
|
return TRUE;
|
|
}
|
|
|
|
BytesWritten = sizeof(DWORD);
|
|
if (RegQueryValueExW(DeviceKey,
|
|
L"ConfigFlags",
|
|
NULL,
|
|
NULL,
|
|
(PBYTE)&Value,
|
|
&BytesWritten) == ERROR_SUCCESS)
|
|
{
|
|
if (Value & CONFIGFLAG_FAILEDINSTALL)
|
|
{
|
|
DPRINT("No need to install: %S\n", DeviceInstance);
|
|
RegCloseKey(DeviceKey);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(DeviceKey);
|
|
}
|
|
|
|
DPRINT1("Installing: %S\n", DeviceInstance);
|
|
|
|
/* Create a random UUID for the named pipe & event*/
|
|
UuidCreate(&RandomUuid);
|
|
swprintf(UuidString, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
|
|
RandomUuid.Data1, RandomUuid.Data2, RandomUuid.Data3,
|
|
RandomUuid.Data4[0], RandomUuid.Data4[1], RandomUuid.Data4[2],
|
|
RandomUuid.Data4[3], RandomUuid.Data4[4], RandomUuid.Data4[5],
|
|
RandomUuid.Data4[6], RandomUuid.Data4[7]);
|
|
|
|
/* Create the event */
|
|
wcscpy(InstallEventName, L"Global\\PNP_Device_Install_Event_0.");
|
|
wcscat(InstallEventName, UuidString);
|
|
hInstallEvent = CreateEventW(NULL, TRUE, FALSE, InstallEventName);
|
|
if (!hInstallEvent)
|
|
{
|
|
DPRINT1("CreateEventW('%ls') failed with error %lu\n", InstallEventName, GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Create the named pipe */
|
|
wcscpy(PipeName, L"\\\\.\\pipe\\PNP_Device_Install_Pipe_0.");
|
|
wcscat(PipeName, UuidString);
|
|
hPipe = CreateNamedPipeW(PipeName, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 512, 512, 0, NULL);
|
|
if (hPipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
DPRINT1("CreateNamedPipeW failed with error %u\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Launch rundll32 to call ClientSideInstallW */
|
|
wcscpy(CommandLine, L"rundll32.exe newdev.dll,ClientSideInstall ");
|
|
wcscat(CommandLine, PipeName);
|
|
|
|
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
|
StartupInfo.cb = sizeof(StartupInfo);
|
|
|
|
if (hUserToken)
|
|
{
|
|
/* newdev has to run under the environment of the current user */
|
|
if (!CreateEnvironmentBlock(&Environment, hUserToken, FALSE))
|
|
{
|
|
DPRINT1("CreateEnvironmentBlock failed with error %d\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!CreateProcessAsUserW(hUserToken, NULL, CommandLine, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, Environment, NULL, &StartupInfo, &ProcessInfo))
|
|
{
|
|
DPRINT1("CreateProcessAsUserW failed with error %u\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* FIXME: This is probably not correct, I guess newdev should never be run with SYSTEM privileges.
|
|
|
|
Still, we currently do that in 2nd stage setup and probably Console mode as well, so allow it here.
|
|
(ShowWizard is only set to FALSE for these two modes) */
|
|
ASSERT(!ShowWizard);
|
|
|
|
if (!CreateProcessW(NULL, CommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInfo))
|
|
{
|
|
DPRINT1("CreateProcessW failed with error %u\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Wait for the function to connect to our pipe */
|
|
if (!ConnectNamedPipe(hPipe, NULL))
|
|
{
|
|
if (GetLastError() != ERROR_PIPE_CONNECTED)
|
|
{
|
|
DPRINT1("ConnectNamedPipe failed with error %u\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Pass the data. The following output is partly compatible to Windows XP SP2 (researched using a modified newdev.dll to log this stuff) */
|
|
Value = sizeof(InstallEventName);
|
|
WriteFile(hPipe, &Value, sizeof(Value), &BytesWritten, NULL);
|
|
WriteFile(hPipe, InstallEventName, Value, &BytesWritten, NULL);
|
|
|
|
/* I couldn't figure out what the following value means under WinXP. It's usually 0 in my tests, but was also 5 once.
|
|
Therefore the following line is entirely ReactOS-specific. We use the value here to pass the ShowWizard variable. */
|
|
WriteFile(hPipe, &ShowWizard, sizeof(ShowWizard), &BytesWritten, NULL);
|
|
|
|
Value = (wcslen(DeviceInstance) + 1) * sizeof(WCHAR);
|
|
WriteFile(hPipe, &Value, sizeof(Value), &BytesWritten, NULL);
|
|
WriteFile(hPipe, DeviceInstance, Value, &BytesWritten, NULL);
|
|
|
|
/* Wait for newdev.dll to finish processing */
|
|
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
|
|
|
|
/* If the event got signalled, this is success */
|
|
DeviceInstalled = WaitForSingleObject(hInstallEvent, 0) == WAIT_OBJECT_0;
|
|
|
|
cleanup:
|
|
if (hInstallEvent)
|
|
CloseHandle(hInstallEvent);
|
|
|
|
if (hPipe != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hPipe);
|
|
|
|
if (Environment)
|
|
DestroyEnvironmentBlock(Environment);
|
|
|
|
if (ProcessInfo.hProcess)
|
|
CloseHandle(ProcessInfo.hProcess);
|
|
|
|
if (ProcessInfo.hThread)
|
|
CloseHandle(ProcessInfo.hThread);
|
|
|
|
if (!DeviceInstalled)
|
|
{
|
|
DPRINT1("InstallDevice failed for DeviceInstance '%ws'\n", DeviceInstance);
|
|
}
|
|
|
|
return DeviceInstalled;
|
|
}
|
|
|
|
|
|
static LONG
|
|
ReadRegSzKey(
|
|
IN HKEY hKey,
|
|
IN LPCWSTR pszKey,
|
|
OUT LPWSTR* pValue)
|
|
{
|
|
LONG rc;
|
|
DWORD dwType;
|
|
DWORD cbData = 0;
|
|
LPWSTR Value;
|
|
|
|
if (!pValue)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
*pValue = NULL;
|
|
rc = RegQueryValueExW(hKey, pszKey, NULL, &dwType, NULL, &cbData);
|
|
if (rc != ERROR_SUCCESS)
|
|
return rc;
|
|
if (dwType != REG_SZ)
|
|
return ERROR_FILE_NOT_FOUND;
|
|
Value = HeapAlloc(GetProcessHeap(), 0, cbData + sizeof(WCHAR));
|
|
if (!Value)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
rc = RegQueryValueExW(hKey, pszKey, NULL, NULL, (LPBYTE)Value, &cbData);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, Value);
|
|
return rc;
|
|
}
|
|
/* NULL-terminate the string */
|
|
Value[cbData / sizeof(WCHAR)] = '\0';
|
|
|
|
*pValue = Value;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SetupIsActive(VOID)
|
|
{
|
|
HKEY hKey = NULL;
|
|
DWORD regType, active, size;
|
|
LONG rc;
|
|
BOOL ret = FALSE;
|
|
|
|
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\Setup", 0, KEY_QUERY_VALUE, &hKey);
|
|
if (rc != ERROR_SUCCESS)
|
|
goto cleanup;
|
|
|
|
size = sizeof(DWORD);
|
|
rc = RegQueryValueExW(hKey, L"SystemSetupInProgress", NULL, ®Type, (LPBYTE)&active, &size);
|
|
if (rc != ERROR_SUCCESS)
|
|
goto cleanup;
|
|
if (regType != REG_DWORD || size != sizeof(DWORD))
|
|
goto cleanup;
|
|
|
|
ret = (active != 0);
|
|
|
|
cleanup:
|
|
if (hKey != NULL)
|
|
RegCloseKey(hKey);
|
|
|
|
DPRINT("System setup in progress? %S\n", ret ? L"YES" : L"NO");
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static BOOL
|
|
IsConsoleBoot(VOID)
|
|
{
|
|
HKEY ControlKey = NULL;
|
|
LPWSTR SystemStartOptions = NULL;
|
|
LPWSTR CurrentOption, NextOption; /* Pointers into SystemStartOptions */
|
|
BOOL ConsoleBoot = FALSE;
|
|
LONG rc;
|
|
|
|
rc = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&ControlKey);
|
|
|
|
rc = ReadRegSzKey(ControlKey, L"SystemStartOptions", &SystemStartOptions);
|
|
if (rc != ERROR_SUCCESS)
|
|
goto cleanup;
|
|
|
|
/* Check for CONSOLE switch in SystemStartOptions */
|
|
CurrentOption = SystemStartOptions;
|
|
while (CurrentOption)
|
|
{
|
|
NextOption = wcschr(CurrentOption, L' ');
|
|
if (NextOption)
|
|
*NextOption = L'\0';
|
|
if (_wcsicmp(CurrentOption, L"CONSOLE") == 0)
|
|
{
|
|
DPRINT("Found %S. Switching to console boot\n", CurrentOption);
|
|
ConsoleBoot = TRUE;
|
|
goto cleanup;
|
|
}
|
|
CurrentOption = NextOption ? NextOption + 1 : NULL;
|
|
}
|
|
|
|
cleanup:
|
|
if (ControlKey != NULL)
|
|
RegCloseKey(ControlKey);
|
|
HeapFree(GetProcessHeap(), 0, SystemStartOptions);
|
|
return ConsoleBoot;
|
|
}
|
|
|
|
|
|
FORCEINLINE
|
|
BOOL
|
|
IsUISuppressionAllowed(VOID)
|
|
{
|
|
/* Display the newdev.dll wizard UI only if it's allowed */
|
|
return (g_IsUISuppressed || GetSuppressNewUIValue());
|
|
}
|
|
|
|
|
|
/* Loop to install all queued devices installations */
|
|
DWORD
|
|
WINAPI
|
|
DeviceInstallThread(LPVOID lpParameter)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
DeviceInstallParams* Params;
|
|
BOOL showWizard;
|
|
|
|
UNREFERENCED_PARAMETER(lpParameter);
|
|
|
|
WaitForSingleObject(hInstallEvent, INFINITE);
|
|
|
|
showWizard = !SetupIsActive() && !IsConsoleBoot();
|
|
|
|
while (TRUE)
|
|
{
|
|
/* Dequeue the next oldest device-install event */
|
|
WaitForSingleObject(hDeviceInstallListMutex, INFINITE);
|
|
ListEntry = (IsListEmpty(&DeviceInstallListHead)
|
|
? NULL : RemoveHeadList(&DeviceInstallListHead));
|
|
ReleaseMutex(hDeviceInstallListMutex);
|
|
|
|
if (ListEntry == NULL)
|
|
{
|
|
SetEvent(hNoPendingInstalls);
|
|
WaitForSingleObject(hDeviceInstallListNotEmpty, INFINITE);
|
|
}
|
|
else
|
|
{
|
|
ResetEvent(hNoPendingInstalls);
|
|
Params = CONTAINING_RECORD(ListEntry, DeviceInstallParams, ListEntry);
|
|
InstallDevice(Params->DeviceIds, showWizard && !IsUISuppressionAllowed());
|
|
HeapFree(GetProcessHeap(), 0, Params);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|