Files
reactos/base/system/userinit/userinit.c
Carl J. Bialorucki 2263c9ca0f [USERINIT] Start shell from profile path
When the shell is assigned to cmd.exe, open to the logged in user's profile path. This appears to match the behavior of Windows Server 2003 userinit. It is also a prerequisite for ReactOS Server Core.
2026-06-08 10:11:57 -05:00

751 lines
22 KiB
C

/*
* ReactOS applications
* Copyright (C) 2001, 2002 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 Userinit Logon Application
* FILE: base/system/userinit/userinit.c
* PROGRAMMERS: Thomas Weidenmueller (w3seek@users.sourceforge.net)
* Hervé Poussineau (hpoussin@reactos.org)
*/
#include "userinit.h"
#include <userenv.h>
#define CMP_MAGIC 0x01234567
/* GLOBALS ******************************************************************/
HINSTANCE hInstance;
/* FUNCTIONS ****************************************************************/
LONG
ReadRegSzKey(
IN HKEY hKey,
IN LPCWSTR pszKey,
OUT LPWSTR *pValue)
{
LONG rc;
DWORD dwType;
DWORD cbData = 0;
LPWSTR Value;
rc = RegQueryValueExW(hKey, pszKey, NULL, &dwType, NULL, &cbData);
if (rc != ERROR_SUCCESS)
{
WARN("RegQueryValueEx(%s) failed with error %lu\n", debugstr_w(pszKey), rc);
return rc;
}
if (dwType != REG_SZ)
{
WARN("Wrong registry data type (%u vs %u)\n", dwType, REG_SZ);
return ERROR_FILE_NOT_FOUND;
}
Value = (WCHAR*) HeapAlloc(GetProcessHeap(), 0, cbData + sizeof(WCHAR));
if (!Value)
{
WARN("No memory\n");
return ERROR_NOT_ENOUGH_MEMORY;
}
rc = RegQueryValueExW(hKey, pszKey, NULL, NULL, (LPBYTE)Value, &cbData);
if (rc != ERROR_SUCCESS)
{
WARN("RegQueryValueEx(%s) failed with error %lu\n", debugstr_w(pszKey), rc);
HeapFree(GetProcessHeap(), 0, Value);
return rc;
}
/* NULL-terminate the string */
Value[cbData / sizeof(WCHAR)] = L'\0';
*pValue = Value;
return ERROR_SUCCESS;
}
static BOOL
IsConsoleShell(VOID)
{
HKEY ControlKey = NULL;
LPWSTR SystemStartOptions = NULL;
LPWSTR CurrentOption, NextOption; /* Pointers into SystemStartOptions */
LONG rc;
BOOL ret = FALSE;
rc = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
REGSTR_PATH_CURRENT_CONTROL_SET,
0,
KEY_QUERY_VALUE,
&ControlKey);
if (rc != ERROR_SUCCESS)
{
WARN("RegOpenKeyEx() failed with error %lu\n", rc);
goto cleanup;
}
rc = ReadRegSzKey(ControlKey, L"SystemStartOptions", &SystemStartOptions);
if (rc != ERROR_SUCCESS)
{
WARN("ReadRegSzKey() failed with error %lu\n", rc);
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)
{
TRACE("Found 'CONSOLE' boot option\n");
ret = TRUE;
goto cleanup;
}
CurrentOption = NextOption ? NextOption + 1 : NULL;
}
cleanup:
if (ControlKey != NULL)
RegCloseKey(ControlKey);
HeapFree(GetProcessHeap(), 0, SystemStartOptions);
TRACE("IsConsoleShell() returning %u\n", ret);
return ret;
}
static BOOL
GetShell(
OUT WCHAR *CommandLine, /* must be at least MAX_PATH long */
IN HKEY hRootKey)
{
HKEY hKey;
DWORD Type, Size;
WCHAR Shell[MAX_PATH];
BOOL ConsoleShell = IsConsoleShell();
LONG rc;
rc = RegOpenKeyExW(hRootKey, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
0, KEY_QUERY_VALUE, &hKey);
if (rc != ERROR_SUCCESS)
{
WARN("RegOpenKeyEx() failed with error %lu\n", rc);
return FALSE;
}
Size = sizeof(Shell);
rc = RegQueryValueExW(hKey,
ConsoleShell ? L"ConsoleShell" : L"Shell",
NULL,
&Type,
(LPBYTE)Shell,
&Size);
RegCloseKey(hKey);
if (rc != ERROR_SUCCESS)
{
WARN("RegQueryValueEx() failed with error %lu\n", rc);
return FALSE;
}
if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
{
TRACE("Found command line %s\n", debugstr_w(Shell));
wcscpy(CommandLine, Shell);
return TRUE;
}
else
{
WARN("Wrong type %lu (expected %u or %u)\n", Type, REG_SZ, REG_EXPAND_SZ);
return FALSE;
}
}
static BOOL
StartProcess(
_In_ PCWSTR CommandLine,
_In_opt_ PVOID pEnvironment)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
WCHAR ExpandedCmdLine[MAX_PATH], ProfilePath[MAX_PATH];
HANDLE hToken;
PWCHAR WorkingDir = NULL;
ExpandEnvironmentStringsW(CommandLine, ExpandedCmdLine, ARRAYSIZE(ExpandedCmdLine));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
ZeroMemory(&pi, sizeof(pi));
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
DWORD PathSize = _countof(ProfilePath);
if (GetUserProfileDirectoryW(hToken, ProfilePath, &PathSize))
WorkingDir = ProfilePath;
CloseHandle(hToken);
}
if (!CreateProcessW(NULL,
ExpandedCmdLine,
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
pEnvironment,
WorkingDir,
&si,
&pi))
{
WARN("CreateProcessW() failed with error %lu\n", GetLastError());
return FALSE;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return TRUE;
}
static BOOL
StartShell(
_In_opt_ PVOID pEnvironment)
{
DWORD Type, Size;
DWORD Value = 0;
LONG rc;
HKEY hKey;
WCHAR Shell[MAX_PATH];
WCHAR szMsg[RC_STRING_MAX_SIZE];
/* Safe Mode shell run */
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
0, KEY_QUERY_VALUE, &hKey);
if (rc == ERROR_SUCCESS)
{
Size = sizeof(Value);
rc = RegQueryValueExW(hKey, L"UseAlternateShell", NULL,
&Type, (LPBYTE)&Value, &Size);
RegCloseKey(hKey);
if (rc == ERROR_SUCCESS)
{
if (Type == REG_DWORD)
{
if (Value)
{
/* Safe Mode Alternate Shell required */
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot",
0, KEY_READ, &hKey);
if (rc == ERROR_SUCCESS)
{
Size = sizeof(Shell);
rc = RegQueryValueExW(hKey, L"AlternateShell", NULL,
&Type, (LPBYTE)Shell, &Size);
RegCloseKey(hKey);
if (rc == ERROR_SUCCESS)
{
if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
{
TRACE("Key located - %s\n", debugstr_w(Shell));
/* Try to run alternate shell */
if (StartProcess(Shell, pEnvironment))
{
TRACE("Alternate shell started (Safe Mode)\n");
return TRUE;
}
}
else
{
WARN("Wrong type %lu (expected %u or %u)\n",
Type, REG_SZ, REG_EXPAND_SZ);
}
}
else
{
WARN("Alternate shell in Safe Mode required but not specified.\n");
}
}
}
}
else
{
WARN("Wrong type %lu (expected %u)\n", Type, REG_DWORD);
}
}
}
/* Try to run shell in user key */
if (GetShell(Shell, HKEY_CURRENT_USER) && StartProcess(Shell, pEnvironment))
{
TRACE("Started shell from HKEY_CURRENT_USER\n");
return TRUE;
}
/* Try to run shell in local machine key */
if (GetShell(Shell, HKEY_LOCAL_MACHINE) && StartProcess(Shell, pEnvironment))
{
TRACE("Started shell from HKEY_LOCAL_MACHINE\n");
return TRUE;
}
/* Try default shell */
if (IsConsoleShell())
{
*Shell = UNICODE_NULL;
if (GetSystemDirectoryW(Shell, ARRAYSIZE(Shell) - 8))
StringCchCatW(Shell, ARRAYSIZE(Shell), L"\\");
StringCchCatW(Shell, ARRAYSIZE(Shell), L"cmd.exe");
}
else
{
*Shell = UNICODE_NULL;
if (GetSystemWindowsDirectoryW(Shell, ARRAYSIZE(Shell) - 13))
StringCchCatW(Shell, ARRAYSIZE(Shell), L"\\");
StringCchCatW(Shell, ARRAYSIZE(Shell), L"explorer.exe");
}
if (StartProcess(Shell, pEnvironment))
return TRUE;
/* We failed, display an error message and quit */
ERR("Failed to start default shell '%s'\n", debugstr_w(Shell));
LoadStringW(GetModuleHandle(NULL), IDS_SHELL_FAIL, szMsg, ARRAYSIZE(szMsg));
MessageBoxW(NULL, szMsg, NULL, MB_OK);
return FALSE;
}
const WCHAR g_RegColorNames[][32] = {
L"Scrollbar", /* 00 = COLOR_SCROLLBAR */
L"Background", /* 01 = COLOR_DESKTOP */
L"ActiveTitle", /* 02 = COLOR_ACTIVECAPTION */
L"InactiveTitle", /* 03 = COLOR_INACTIVECAPTION */
L"Menu", /* 04 = COLOR_MENU */
L"Window", /* 05 = COLOR_WINDOW */
L"WindowFrame", /* 06 = COLOR_WINDOWFRAME */
L"MenuText", /* 07 = COLOR_MENUTEXT */
L"WindowText", /* 08 = COLOR_WINDOWTEXT */
L"TitleText", /* 09 = COLOR_CAPTIONTEXT */
L"ActiveBorder", /* 10 = COLOR_ACTIVEBORDER */
L"InactiveBorder", /* 11 = COLOR_INACTIVEBORDER */
L"AppWorkSpace", /* 12 = COLOR_APPWORKSPACE */
L"Hilight", /* 13 = COLOR_HIGHLIGHT */
L"HilightText", /* 14 = COLOR_HIGHLIGHTTEXT */
L"ButtonFace", /* 15 = COLOR_BTNFACE */
L"ButtonShadow", /* 16 = COLOR_BTNSHADOW */
L"GrayText", /* 17 = COLOR_GRAYTEXT */
L"ButtonText", /* 18 = COLOR_BTNTEXT */
L"InactiveTitleText", /* 19 = COLOR_INACTIVECAPTIONTEXT */
L"ButtonHilight", /* 20 = COLOR_BTNHIGHLIGHT */
L"ButtonDkShadow", /* 21 = COLOR_3DDKSHADOW */
L"ButtonLight", /* 22 = COLOR_3DLIGHT */
L"InfoText", /* 23 = COLOR_INFOTEXT */
L"InfoWindow", /* 24 = COLOR_INFOBK */
L"ButtonAlternateFace", /* 25 = COLOR_ALTERNATEBTNFACE */
L"HotTrackingColor", /* 26 = COLOR_HOTLIGHT */
L"GradientActiveTitle", /* 27 = COLOR_GRADIENTACTIVECAPTION */
L"GradientInactiveTitle", /* 28 = COLOR_GRADIENTINACTIVECAPTION */
L"MenuHilight", /* 29 = COLOR_MENUHILIGHT */
L"MenuBar" /* 30 = COLOR_MENUBAR */
};
static COLORREF
StrToColorref(
IN LPWSTR lpszCol)
{
BYTE rgb[3];
rgb[0] = (BYTE)wcstoul(lpszCol, &lpszCol, 10);
rgb[1] = (BYTE)wcstoul(lpszCol, &lpszCol, 10);
rgb[2] = (BYTE)wcstoul(lpszCol, &lpszCol, 10);
return RGB(rgb[0], rgb[1], rgb[2]);
}
static VOID
SetUserSysColors(VOID)
{
HKEY hKey;
INT i;
WCHAR szColor[25];
DWORD Type, Size;
COLORREF crColor;
LONG rc;
rc = RegOpenKeyExW(HKEY_CURRENT_USER, REGSTR_PATH_COLORS,
0, KEY_QUERY_VALUE, &hKey);
if (rc != ERROR_SUCCESS)
{
WARN("RegOpenKeyEx() failed with error %lu\n", rc);
return;
}
for (i = 0; i < ARRAYSIZE(g_RegColorNames); i++)
{
Size = sizeof(szColor);
rc = RegQueryValueExW(hKey, g_RegColorNames[i], NULL, &Type,
(LPBYTE)szColor, &Size);
if (rc == ERROR_SUCCESS && Type == REG_SZ)
{
crColor = StrToColorref(szColor);
SetSysColors(1, &i, &crColor);
}
else
{
WARN("RegQueryValueEx(%s) failed with error %lu\n",
debugstr_w(g_RegColorNames[i]), rc);
}
}
RegCloseKey(hKey);
}
static VOID
SetUserWallpaper(VOID)
{
HKEY hKey;
DWORD Type, Size;
WCHAR szWallpaper[MAX_PATH + 1];
LONG rc;
rc = RegOpenKeyExW(HKEY_CURRENT_USER, REGSTR_PATH_DESKTOP,
0, KEY_QUERY_VALUE, &hKey);
if (rc != ERROR_SUCCESS)
{
WARN("RegOpenKeyEx() failed with error %lu\n", rc);
return;
}
Size = sizeof(szWallpaper);
rc = RegQueryValueExW(hKey,
L"Wallpaper",
NULL,
&Type,
(LPBYTE)szWallpaper,
&Size);
RegCloseKey(hKey);
if (rc == ERROR_SUCCESS && Type == REG_SZ)
{
ExpandEnvironmentStringsW(szWallpaper, szWallpaper, ARRAYSIZE(szWallpaper));
TRACE("Using wallpaper %s\n", debugstr_w(szWallpaper));
/* Load and change the wallpaper */
SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, szWallpaper, SPIF_SENDCHANGE);
}
else
{
/* Remove the wallpaper */
TRACE("No wallpaper set in registry (error %lu)\n", rc);
SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDCHANGE);
}
}
static VOID
SetUserSettings(VOID)
{
UpdatePerUserSystemParameters(1, TRUE);
SetUserSysColors();
SetUserWallpaper();
}
typedef DWORD (WINAPI *PCMP_REPORT_LOGON)(DWORD, DWORD);
static VOID
NotifyLogon(VOID)
{
HINSTANCE hModule;
PCMP_REPORT_LOGON CMP_Report_LogOn;
hModule = LoadLibraryW(L"setupapi.dll");
if (!hModule)
{
WARN("LoadLibrary() failed with error %lu\n", GetLastError());
return;
}
CMP_Report_LogOn = (PCMP_REPORT_LOGON)GetProcAddress(hModule, "CMP_Report_LogOn");
if (CMP_Report_LogOn)
CMP_Report_LogOn(CMP_MAGIC, GetCurrentProcessId());
else
WARN("GetProcAddress() failed\n");
FreeLibrary(hModule);
}
/*
* Expands the path for the ReactOS Installer "reactos.exe".
* See also base/setup/welcome/welcome.c!ExpandInstallerPath()
*/
BOOL
ExpandInstallerPath(
IN LPCWSTR lpInstallerName,
OUT LPWSTR lpInstallerPath,
IN SIZE_T PathSize)
{
SYSTEM_INFO SystemInfo;
SIZE_T cchInstallerNameLen;
PWSTR ptr;
DWORD dwAttribs;
cchInstallerNameLen = wcslen(lpInstallerName);
if (PathSize < cchInstallerNameLen)
{
/* The buffer is not large enough to contain the installer file name */
*lpInstallerPath = UNICODE_NULL;
return FALSE;
}
/*
* First, try to find the installer using the default drive, under
* the directory whose name corresponds to the currently-running
* CPU architecture.
*/
GetSystemInfo(&SystemInfo);
*lpInstallerPath = UNICODE_NULL;
/* Alternatively one can use SharedUserData->NtSystemRoot */
GetSystemWindowsDirectoryW(lpInstallerPath, PathSize - cchInstallerNameLen - 1);
ptr = wcschr(lpInstallerPath, L'\\');
if (ptr)
*++ptr = UNICODE_NULL;
else
*lpInstallerPath = UNICODE_NULL;
/* Append the corresponding CPU architecture */
switch (SystemInfo.wProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_INTEL:
StringCchCatW(lpInstallerPath, PathSize, L"I386");
break;
case PROCESSOR_ARCHITECTURE_MIPS:
StringCchCatW(lpInstallerPath, PathSize, L"MIPS");
break;
case PROCESSOR_ARCHITECTURE_ALPHA:
StringCchCatW(lpInstallerPath, PathSize, L"ALPHA");
break;
case PROCESSOR_ARCHITECTURE_PPC:
StringCchCatW(lpInstallerPath, PathSize, L"PPC");
break;
case PROCESSOR_ARCHITECTURE_SHX:
StringCchCatW(lpInstallerPath, PathSize, L"SHX");
break;
case PROCESSOR_ARCHITECTURE_ARM:
StringCchCatW(lpInstallerPath, PathSize, L"ARM");
break;
case PROCESSOR_ARCHITECTURE_IA64:
StringCchCatW(lpInstallerPath, PathSize, L"IA64");
break;
case PROCESSOR_ARCHITECTURE_ALPHA64:
StringCchCatW(lpInstallerPath, PathSize, L"ALPHA64");
break;
case PROCESSOR_ARCHITECTURE_AMD64:
StringCchCatW(lpInstallerPath, PathSize, L"AMD64");
break;
// case PROCESSOR_ARCHITECTURE_MSIL: /* .NET CPU-independent code */
case PROCESSOR_ARCHITECTURE_UNKNOWN:
default:
WARN("Unknown processor architecture %lu\n", SystemInfo.wProcessorArchitecture);
SystemInfo.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
break;
}
if (SystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_UNKNOWN)
StringCchCatW(lpInstallerPath, PathSize, L"\\");
StringCchCatW(lpInstallerPath, PathSize, lpInstallerName);
dwAttribs = GetFileAttributesW(lpInstallerPath);
if ((dwAttribs != INVALID_FILE_ATTRIBUTES) &&
!(dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
{
/* We have found the installer */
return TRUE;
}
WARN("Couldn't find the installer '%s', trying alternative.\n", debugstr_w(lpInstallerPath));
/*
* We failed. Try to find the installer from either the current
* ReactOS installation directory, or from our current directory.
*/
*lpInstallerPath = UNICODE_NULL;
/* Alternatively one can use SharedUserData->NtSystemRoot */
if (GetSystemWindowsDirectoryW(lpInstallerPath, PathSize - cchInstallerNameLen - 1))
StringCchCatW(lpInstallerPath, PathSize, L"\\");
StringCchCatW(lpInstallerPath, PathSize, lpInstallerName);
dwAttribs = GetFileAttributesW(lpInstallerPath);
if ((dwAttribs != INVALID_FILE_ATTRIBUTES) &&
!(dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
{
/* We have found the installer */
return TRUE;
}
/* Installer not found */
ERR("Couldn't find the installer '%s'\n", debugstr_w(lpInstallerPath));
*lpInstallerPath = UNICODE_NULL;
return FALSE;
}
static BOOL
StartInstaller(IN LPCWSTR lpInstallerName)
{
WCHAR Installer[MAX_PATH];
WCHAR szMsg[RC_STRING_MAX_SIZE];
if (ExpandInstallerPath(lpInstallerName, Installer, ARRAYSIZE(Installer)))
{
/* We have found the installer */
if (StartProcess(Installer, NULL))
return TRUE;
}
/* We failed, display an error message and quit */
ERR("Failed to start the installer '%s'\n", debugstr_w(Installer));
LoadStringW(GetModuleHandle(NULL), IDS_INSTALLER_FAIL, szMsg, ARRAYSIZE(szMsg));
MessageBoxW(NULL, szMsg, NULL, MB_OK);
return FALSE;
}
/* Used to get the shutdown privilege */
static BOOL
EnablePrivilege(LPCWSTR lpszPrivilegeName, BOOL bEnablePrivilege)
{
BOOL Success;
HANDLE hToken;
TOKEN_PRIVILEGES tp;
Success = OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
&hToken);
if (!Success) return Success;
Success = LookupPrivilegeValueW(NULL,
lpszPrivilegeName,
&tp.Privileges[0].Luid);
if (!Success) goto Quit;
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = (bEnablePrivilege ? SE_PRIVILEGE_ENABLED : 0);
Success = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
Quit:
CloseHandle(hToken);
return Success;
}
int WINAPI
wWinMain(IN HINSTANCE hInst,
IN HINSTANCE hPrevInstance,
IN LPWSTR lpszCmdLine,
IN int nCmdShow)
{
BOOL bIsLiveCD, Success = TRUE;
STATE State;
hInstance = hInst;
bIsLiveCD = IsLiveCD();
Restart:
SetUserSettings();
if (bIsLiveCD)
{
State.NextPage = LOCALEPAGE;
State.Run = SHELL;
}
else
{
State.NextPage = DONE;
State.Run = SHELL;
}
if (State.NextPage != DONE) // && bIsLiveCD
{
RunLiveCD(&State);
}
switch (State.Run)
{
case SHELL:
{
/* In LiveCD mode, create a suitable environment block for the
* shell; otherwise, use the current one (built by WinLogon) */
PVOID pEnvironment = NULL;
if (bIsLiveCD && /* In LiveCD mode we run under the LocalSystem account */
!CreateEnvironmentBlock(&pEnvironment, NULL, TRUE))
{
WARN("CreateEnvironmentBlock() failed, fall back to default (error %lu)\n",
GetLastError());
}
Success = StartShell(pEnvironment);
if (pEnvironment)
DestroyEnvironmentBlock(pEnvironment);
if (Success)
NotifyLogon();
break;
}
case INSTALLER:
Success = StartInstaller(L"reactos.exe");
break;
case REBOOT:
{
EnablePrivilege(SE_SHUTDOWN_NAME, TRUE);
ExitWindowsEx(EWX_REBOOT, 0);
EnablePrivilege(SE_SHUTDOWN_NAME, FALSE);
Success = TRUE;
break;
}
default:
Success = FALSE;
break;
}
/*
* In LiveCD mode, go back to the main menu if we failed
* to either start the shell or the installer.
*/
if (bIsLiveCD && !Success)
goto Restart;
return 0;
}
/* EOF */