reactos/base/services/umpnpmgr/install.c

378 lines
11 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, &regType, (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;
}
/* 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);
HeapFree(GetProcessHeap(), 0, Params);
}
}
return 0;
}