mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
eede1b9b7a
CORE-15897 Co-authored-by: Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
386 lines
12 KiB
C
386 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;
|
|
|
|
SLIST_HEADER 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)
|
|
{
|
|
PSLIST_ENTRY ListEntry;
|
|
DeviceInstallParams* Params;
|
|
BOOL showWizard;
|
|
|
|
UNREFERENCED_PARAMETER(lpParameter);
|
|
|
|
WaitForSingleObject(hInstallEvent, INFINITE);
|
|
|
|
showWizard = !SetupIsActive() && !IsConsoleBoot();
|
|
|
|
while (TRUE)
|
|
{
|
|
ListEntry = InterlockedPopEntrySList(&DeviceInstallListHead);
|
|
|
|
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;
|
|
}
|