[WINSRV]: Remove some deprecated code, keep other parts for reusage later on; cleanup SrvExitWindowsEx. If you enable the NotifyAndTerminateProcess call, you get a basic win32 process termination. This is now where the real work starts: do not kill CSRSS nor Winlogon, do not kill processes from other sessions, etc... Deal with console apps; display timeout dialog, and focus on the app that blocks shutdown by switching to the correct desktop. And we MUST NOT use user32 desktop APIs for switching to the different desktops, but we need to directly call win32k!! (for reasons that will appear later on; not implemented at the moment). Giannis, I will need your help for that!

Part 10/X
CORE-8322

svn path=/trunk/; revision=66202
This commit is contained in:
Hermès Bélusca-Maïto 2015-02-08 00:54:22 +00:00
parent 683c14c278
commit 3d3bdc5873

View file

@ -32,6 +32,8 @@ typedef struct tagSHUTDOWN_SETTINGS
DWORD WaitToKillAppTimeout; DWORD WaitToKillAppTimeout;
} SHUTDOWN_SETTINGS, *PSHUTDOWN_SETTINGS; } SHUTDOWN_SETTINGS, *PSHUTDOWN_SETTINGS;
SHUTDOWN_SETTINGS ShutdownSettings = {0};
#define DEFAULT_AUTO_END_TASKS FALSE #define DEFAULT_AUTO_END_TASKS FALSE
#define DEFAULT_HUNG_APP_TIMEOUT 5000 #define DEFAULT_HUNG_APP_TIMEOUT 5000
#define DEFAULT_WAIT_TO_KILL_APP_TIMEOUT 20000 #define DEFAULT_WAIT_TO_KILL_APP_TIMEOUT 20000
@ -84,16 +86,7 @@ typedef struct tagPROCESS_ENUM_CONTEXT
/* FUNCTIONS ******************************************************************/ /* FUNCTIONS ******************************************************************/
/* static VOID FASTCALL
NTSTATUS FASTCALL
Win32CsrEnumProcesses(CSRSS_ENUM_PROCESS_PROC EnumProc,
PVOID Context)
{
return CsrEnumProcesses(EnumProc, Context);
}
*/
static void FASTCALL
UpdateProgressBar(HWND ProgressBar, PNOTIFY_CONTEXT NotifyContext) UpdateProgressBar(HWND ProgressBar, PNOTIFY_CONTEXT NotifyContext)
{ {
DWORD Passed; DWORD Passed;
@ -185,20 +178,21 @@ EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
return Result; return Result;
} }
static void HMODULE hComCtl32Lib = NULL;
CallInitCommonControls()
static VOID
CallInitCommonControls(VOID)
{ {
static BOOL Initialized = FALSE; static BOOL Initialized = FALSE;
HMODULE Lib;
INITCOMMONCONTROLS_PROC InitProc; INITCOMMONCONTROLS_PROC InitProc;
if (Initialized) return; if (Initialized) return;
Lib = LoadLibraryW(L"COMCTL32.DLL"); hComCtl32Lib = LoadLibraryW(L"COMCTL32.DLL");
if (NULL == Lib) return; if (hComCtl32Lib == NULL) return;
InitProc = (INITCOMMONCONTROLS_PROC) GetProcAddress(Lib, "InitCommonControls"); InitProc = (INITCOMMONCONTROLS_PROC)GetProcAddress(hComCtl32Lib, "InitCommonControls");
if (NULL == InitProc) return; if (InitProc == NULL) return;
(*InitProc)(); (*InitProc)();
@ -292,88 +286,74 @@ NotifyTopLevelEnum(HWND Wnd, LPARAM lParam)
return FALSE; return FALSE;
} }
if (ProcessId == NotifyContext->ProcessId) if (ProcessId != NotifyContext->ProcessId)
goto Quit;
Now = GetTickCount();
if (0 == NotifyContext->StartTime)
{ {
Now = GetTickCount(); NotifyContext->StartTime = Now;
if (0 == NotifyContext->StartTime) }
/*
* Note: Passed is computed correctly even when GetTickCount()
* wraps due to unsigned arithmetic.
*/
Passed = Now - NotifyContext->StartTime;
MessageContext.Wnd = Wnd;
MessageContext.Msg = NotifyContext->Msg;
MessageContext.wParam = NotifyContext->wParam;
MessageContext.lParam = NotifyContext->lParam;
MessageContext.Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
if (! NotifyContext->ShutdownSettings->AutoEndTasks)
{
MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
}
if (Passed < MessageContext.Timeout)
{
MessageContext.Timeout -= Passed;
MessageThread = CreateThread(NULL, 0, NotifyContext->SendMessageProc,
(LPVOID) &MessageContext, 0, NULL);
if (NULL == MessageThread)
{ {
NotifyContext->StartTime = Now; NotifyContext->QueryResult = QUERY_RESULT_ERROR;
return FALSE;
} }
/* Note: Passed is computed correctly even when GetTickCount() wraps due Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
to unsigned arithmetic */ if (Passed < Timeout)
Passed = Now - NotifyContext->StartTime;
MessageContext.Wnd = Wnd;
MessageContext.Msg = NotifyContext->Msg;
MessageContext.wParam = NotifyContext->wParam;
MessageContext.lParam = NotifyContext->lParam;
MessageContext.Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
if (! NotifyContext->ShutdownSettings->AutoEndTasks)
{ {
MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout; Timeout -= Passed;
WaitStatus = WaitForSingleObjectEx(MessageThread, Timeout, FALSE);
} }
if (Passed < MessageContext.Timeout) else
{ {
MessageContext.Timeout -= Passed; WaitStatus = WAIT_TIMEOUT;
MessageThread = CreateThread(NULL, 0, NotifyContext->SendMessageProc, }
(LPVOID) &MessageContext, 0, NULL); if (WAIT_TIMEOUT == WaitStatus)
if (NULL == MessageThread) {
NotifyContext->WndClient = Wnd;
if (NULL == NotifyContext->UIThread && NotifyContext->ShowUI)
{ {
NotifyContext->QueryResult = QUERY_RESULT_ERROR; NotifyContext->UIThread = CreateThread(NULL, 0,
return FALSE; EndNowThreadProc,
(LPVOID) NotifyContext,
0, NULL);
} }
Timeout = NotifyContext->ShutdownSettings->HungAppTimeout; Threads[0] = MessageThread;
if (Passed < Timeout) Threads[1] = NotifyContext->UIThread;
WaitStatus = WaitForMultipleObjectsEx(NULL == NotifyContext->UIThread ?
1 : 2,
Threads, FALSE, INFINITE,
FALSE);
if (WAIT_OBJECT_0 == WaitStatus)
{ {
Timeout -= Passed; if (! GetExitCodeThread(MessageThread, &NotifyContext->QueryResult))
WaitStatus = WaitForSingleObjectEx(MessageThread, Timeout, FALSE);
}
else
{
WaitStatus = WAIT_TIMEOUT;
}
if (WAIT_TIMEOUT == WaitStatus)
{
NotifyContext->WndClient = Wnd;
if (NULL == NotifyContext->UIThread && NotifyContext->ShowUI)
{
NotifyContext->UIThread = CreateThread(NULL, 0,
EndNowThreadProc,
(LPVOID) NotifyContext,
0, NULL);
}
Threads[0] = MessageThread;
Threads[1] = NotifyContext->UIThread;
WaitStatus = WaitForMultipleObjectsEx(NULL == NotifyContext->UIThread ?
1 : 2,
Threads, FALSE, INFINITE,
FALSE);
if (WAIT_OBJECT_0 == WaitStatus)
{
if (! GetExitCodeThread(MessageThread, &NotifyContext->QueryResult))
{
NotifyContext->QueryResult = QUERY_RESULT_ERROR;
}
}
else if (WAIT_OBJECT_0 + 1 == WaitStatus)
{
if (! GetExitCodeThread(NotifyContext->UIThread,
&NotifyContext->QueryResult))
{
NotifyContext->QueryResult = QUERY_RESULT_ERROR;
}
}
else
{ {
NotifyContext->QueryResult = QUERY_RESULT_ERROR; NotifyContext->QueryResult = QUERY_RESULT_ERROR;
} }
if (WAIT_OBJECT_0 != WaitStatus)
{
TerminateThread(MessageThread, QUERY_RESULT_TIMEOUT);
}
} }
else if (WAIT_OBJECT_0 == WaitStatus) else if (WAIT_OBJECT_0 + 1 == WaitStatus)
{ {
if (! GetExitCodeThread(MessageThread, if (! GetExitCodeThread(NotifyContext->UIThread,
&NotifyContext->QueryResult)) &NotifyContext->QueryResult))
{ {
NotifyContext->QueryResult = QUERY_RESULT_ERROR; NotifyContext->QueryResult = QUERY_RESULT_ERROR;
@ -383,15 +363,33 @@ NotifyTopLevelEnum(HWND Wnd, LPARAM lParam)
{ {
NotifyContext->QueryResult = QUERY_RESULT_ERROR; NotifyContext->QueryResult = QUERY_RESULT_ERROR;
} }
CloseHandle(MessageThread); if (WAIT_OBJECT_0 != WaitStatus)
{
TerminateThread(MessageThread, QUERY_RESULT_TIMEOUT);
}
}
else if (WAIT_OBJECT_0 == WaitStatus)
{
if (! GetExitCodeThread(MessageThread,
&NotifyContext->QueryResult))
{
NotifyContext->QueryResult = QUERY_RESULT_ERROR;
}
} }
else else
{ {
NotifyContext->QueryResult = QUERY_RESULT_TIMEOUT; NotifyContext->QueryResult = QUERY_RESULT_ERROR;
} }
CloseHandle(MessageThread);
}
else
{
NotifyContext->QueryResult = QUERY_RESULT_TIMEOUT;
} }
return QUERY_RESULT_CONTINUE == NotifyContext->QueryResult; Quit:
DPRINT1("NotifyContext->QueryResult == %d\n", NotifyContext->QueryResult);
return (NotifyContext->QueryResult == QUERY_RESULT_CONTINUE);
} }
static BOOL CALLBACK static BOOL CALLBACK
@ -470,7 +468,7 @@ ConioConsoleCtrlEventTimeout(DWORD Event, PCSR_PROCESS ProcessData, DWORD Timeou
#endif #endif
/************************************************/ /************************************************/
static BOOL FASTCALL BOOL FASTCALL
NotifyAndTerminateProcess(PCSR_PROCESS ProcessData, NotifyAndTerminateProcess(PCSR_PROCESS ProcessData,
PSHUTDOWN_SETTINGS ShutdownSettings, PSHUTDOWN_SETTINGS ShutdownSettings,
UINT Flags) UINT Flags)
@ -629,32 +627,7 @@ ExitReactosProcessEnum(PCSR_PROCESS ProcessData, PVOID Data)
} }
#endif #endif
static int #if 0
ProcessDataCompare(const void *Elem1, const void *Elem2)
{
const PCSR_PROCESS *ProcessData1 = (PCSR_PROCESS *) Elem1;
const PCSR_PROCESS *ProcessData2 = (PCSR_PROCESS *) Elem2;
if ((*ProcessData1)->ShutdownLevel < (*ProcessData2)->ShutdownLevel)
{
return +1;
}
else if ((*ProcessData2)->ShutdownLevel < (*ProcessData1)->ShutdownLevel)
{
return -1;
}
else if ((*ProcessData1)->ClientId.UniqueProcess < (*ProcessData2)->ClientId.UniqueProcess)
{
return +1;
}
else if ((*ProcessData2)->ClientId.UniqueProcess < (*ProcessData1)->ClientId.UniqueProcess)
{
return -1;
}
return 0;
}
static DWORD FASTCALL static DWORD FASTCALL
GetShutdownSettings(HKEY DesktopKey, LPCWSTR ValueName, DWORD DefaultValue) GetShutdownSettings(HKEY DesktopKey, LPCWSTR ValueName, DWORD DefaultValue)
{ {
@ -695,7 +668,7 @@ GetShutdownSettings(HKEY DesktopKey, LPCWSTR ValueName, DWORD DefaultValue)
return DefaultValue; return DefaultValue;
} }
static void FASTCALL static VOID FASTCALL
LoadShutdownSettings(PSID Sid, PSHUTDOWN_SETTINGS ShutdownSettings) LoadShutdownSettings(PSID Sid, PSHUTDOWN_SETTINGS ShutdownSettings)
{ {
static WCHAR Subkey[] = L"\\Control Panel\\Desktop"; static WCHAR Subkey[] = L"\\Control Panel\\Desktop";
@ -761,140 +734,30 @@ LoadShutdownSettings(PSID Sid, PSHUTDOWN_SETTINGS ShutdownSettings)
NTSTATUS FASTCALL NTSTATUS FASTCALL
InternalExitReactos(DWORD ProcessId, DWORD ThreadId, UINT Flags) InternalExitReactos(DWORD ProcessId, DWORD ThreadId, UINT Flags)
{ {
HANDLE CallerThread; // PROCESS_ENUM_CONTEXT Context;
HANDLE CallerToken;
NTSTATUS Status;
PROCESS_ENUM_CONTEXT Context;
DWORD ReturnLength;
HWND ShellWnd; HWND ShellWnd;
UINT ProcessIndex;
char FixedUserInfo[64]; char FixedUserInfo[64];
TOKEN_USER *UserInfo; TOKEN_USER *UserInfo;
SHUTDOWN_SETTINGS ShutdownSettings; SHUTDOWN_SETTINGS ShutdownSettings;
if (ProcessId != LogonProcessId)
{
DPRINT1("Internal ExitWindowsEx call not from winlogon\n");
return STATUS_ACCESS_DENIED;
}
// FIXME: HAAAAACK!!
DPRINT1("FIXME: Need to close all user processes!\n");
return STATUS_SUCCESS;
CallerThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, ThreadId);
if (NULL == CallerThread)
{
DPRINT1("OpenThread failed with error %d\n", GetLastError());
return STATUS_UNSUCCESSFUL;
}
if (! OpenThreadToken(CallerThread, TOKEN_QUERY, FALSE, &CallerToken))
{
DPRINT1("OpenThreadToken failed with error %d\n", GetLastError());
CloseHandle(CallerThread);
return STATUS_UNSUCCESSFUL;
}
CloseHandle(CallerThread);
Context.ProcessCount = 0;
Context.ProcessData = NULL;
if (! GetTokenInformation(CallerToken, TokenOrigin, &Context.TokenOrigin,
sizeof(TOKEN_ORIGIN), &ReturnLength))
{
DPRINT1("GetTokenInformation failed with error %d\n", GetLastError());
CloseHandle(CallerToken);
return STATUS_UNSUCCESSFUL;
}
if (! GetTokenInformation(CallerToken, TokenUser, FixedUserInfo,
sizeof(FixedUserInfo), &ReturnLength))
{
if (sizeof(FixedUserInfo) < ReturnLength)
{
UserInfo = HeapAlloc(UserServerHeap, 0, ReturnLength);
if (NULL == UserInfo)
{
DPRINT1("Unable to allocate %u bytes for user info\n",
(unsigned) ReturnLength);
CloseHandle(CallerToken);
return STATUS_NO_MEMORY;
}
if (! GetTokenInformation(CallerToken, TokenUser, UserInfo,
ReturnLength, &ReturnLength))
{
DPRINT1("GetTokenInformation failed with error %d\n",
GetLastError());
HeapFree(UserServerHeap, 0, UserInfo);
CloseHandle(CallerToken);
return STATUS_UNSUCCESSFUL;
}
}
else
{
DPRINT1("GetTokenInformation failed with error %d\n", GetLastError());
CloseHandle(CallerToken);
return STATUS_UNSUCCESSFUL;
}
}
else
{
UserInfo = (TOKEN_USER *) FixedUserInfo;
}
CloseHandle(CallerToken);
LoadShutdownSettings(UserInfo->User.Sid, &ShutdownSettings); LoadShutdownSettings(UserInfo->User.Sid, &ShutdownSettings);
if (UserInfo != (TOKEN_USER *) FixedUserInfo)
{
HeapFree(UserServerHeap, 0, UserInfo);
}
Context.CsrssProcess = GetCurrentProcessId();
ShellWnd = GetShellWindow();
if (NULL == ShellWnd)
{
DPRINT("No shell present\n");
Context.ShellProcess = 0;
}
else if (0 == GetWindowThreadProcessId(ShellWnd, &Context.ShellProcess))
{
DPRINT1("Can't get process id of shell window\n");
Context.ShellProcess = 0;
}
// Status = Win32CsrEnumProcesses(ExitReactosProcessEnum, &Context); // Context.CsrssProcess = GetCurrentProcessId();
if (! NT_SUCCESS(Status)) // ShellWnd = GetShellWindow();
{ // if (NULL == ShellWnd)
DPRINT1("Failed to enumerate registered processes, status 0x%x\n", // {
Status); // DPRINT("No shell present\n");
if (NULL != Context.ProcessData) // Context.ShellProcess = 0;
{ // }
HeapFree(UserServerHeap, 0, Context.ProcessData); // else if (0 == GetWindowThreadProcessId(ShellWnd, &Context.ShellProcess))
} // {
return Status; // DPRINT1("Can't get process id of shell window\n");
} // Context.ShellProcess = 0;
// }
qsort(Context.ProcessData, Context.ProcessCount, sizeof(PCSR_PROCESS), return STATUS_SUCCESS;
ProcessDataCompare);
/* Terminate processes, stop if we find one kicking and screaming it doesn't
want to die */
Status = STATUS_SUCCESS;
for (ProcessIndex = 0;
ProcessIndex < Context.ProcessCount && NT_SUCCESS(Status);
ProcessIndex++)
{
if (! NotifyAndTerminateProcess(Context.ProcessData[ProcessIndex],
&ShutdownSettings, Flags))
{
Status = STATUS_REQUEST_ABORTED;
}
}
/* Cleanup */
if (NULL != Context.ProcessData)
{
HeapFree(UserServerHeap, 0, Context.ProcessData);
}
return Status;
} }
#endif
static NTSTATUS FASTCALL static NTSTATUS FASTCALL
UserExitReactos(PCSR_THREAD CsrThread, UINT Flags) UserExitReactos(PCSR_THREAD CsrThread, UINT Flags)
@ -902,10 +765,26 @@ UserExitReactos(PCSR_THREAD CsrThread, UINT Flags)
NTSTATUS Status; NTSTATUS Status;
LUID CallerLuid; LUID CallerLuid;
DWORD ProcessId = HandleToUlong(CsrThread->ClientId.UniqueProcess);
DWORD ThreadId = HandleToUlong(CsrThread->ClientId.UniqueThread);
DPRINT1("SrvExitWindowsEx(ClientId: %lx.%lx, Flags: 0x%x)\n",
ProcessId, ThreadId, Flags);
/* /*
* Check for flags validity * Check for flags validity
*/ */
if (Flags & EWX_INTERNAL_FLAG)
{
/* Only Winlogon can call this */
if (ProcessId != LogonProcessId)
{
DPRINT1("SrvExitWindowsEx call not from Winlogon\n");
return STATUS_ACCESS_DENIED;
}
}
/* Implicitely add the shutdown flag when we poweroff or reboot */ /* Implicitely add the shutdown flag when we poweroff or reboot */
if (Flags & (EWX_POWEROFF | EWX_REBOOT)) if (Flags & (EWX_POWEROFF | EWX_REBOOT))
Flags |= EWX_SHUTDOWN; Flags |= EWX_SHUTDOWN;
@ -984,14 +863,6 @@ UserExitReactos(PCSR_THREAD CsrThread, UINT Flags)
{ {
DPRINT1("Failed to shutdown processes, Status = 0x%08x\n", Status); DPRINT1("Failed to shutdown processes, Status = 0x%08x\n", Status);
} }
/*
* FIXME:
* At the moment the callbacks just do nothing so no process will be killed.
* It's not dramatic since:
* 1- We didn't do that before,
* 2- Related to point 1, the hackish InternalExitReactos call will end back
* into Winlogon that will directly call NtShutdownSystem.
*/
// FIXME: If Status == STATUS_CANCELLED, call RecordShutdownReason // FIXME: If Status == STATUS_CANCELLED, call RecordShutdownReason
@ -1018,7 +889,29 @@ UserClientShutdown(IN PCSR_PROCESS CsrProcess,
DPRINT1("UserClientShutdown(0x%p, 0x%x, %s)\n", DPRINT1("UserClientShutdown(0x%p, 0x%x, %s)\n",
CsrProcess, Flags, FirstPhase ? "FirstPhase" : "LastPhase"); CsrProcess, Flags, FirstPhase ? "FirstPhase" : "LastPhase");
UNIMPLEMENTED; /*
* Check for process validity
*/
/* Do not kill WinLogon or CSRSS */
if (CsrProcess->ClientId.UniqueProcess == NtCurrentProcess() ||
CsrProcess->ClientId.UniqueProcess == UlongToHandle(LogonProcessId))
{
DPRINT1("Not killing %s; CsrProcess->ShutdownFlags = %lu\n",
CsrProcess->ClientId.UniqueProcess == NtCurrentProcess() ? "CSRSS" : "Winlogon",
CsrProcess->ShutdownFlags);
return CsrShutdownNonCsrProcess;
}
DPRINT1("Killing process with ShutdownFlags = %lu\n", CsrProcess->ShutdownFlags);
#if 0
if (!NotifyAndTerminateProcess(CsrProcess, &ShutdownSettings, Flags))
{
return CsrShutdownCancelled;
}
#endif
return CsrShutdownNonCsrProcess; return CsrShutdownNonCsrProcess;
} }
@ -1030,41 +923,11 @@ CSR_API(SrvExitWindowsEx)
{ {
NTSTATUS Status; NTSTATUS Status;
PUSER_EXIT_REACTOS ExitReactosRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactosRequest; PUSER_EXIT_REACTOS ExitReactosRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactosRequest;
PCSR_THREAD CsrThread = CsrGetClientThread();
ULONG Flags = ExitReactosRequest->Flags;
DWORD ProcessId = HandleToUlong(CsrThread->ClientId.UniqueProcess); Status = UserExitReactos(CsrGetClientThread(), ExitReactosRequest->Flags);
DWORD ThreadId = HandleToUlong(CsrThread->ClientId.UniqueThread); ExitReactosRequest->Success = NT_SUCCESS(Status);
ExitReactosRequest->LastError = GetLastError();
DPRINT1("SrvExitWindowsEx(ClientId: %lx.%lx, Flags: 0x%x)\n",
ProcessId, ThreadId, Flags);
// FIXME: Everything else starting after this line is a HAAACK!!
if (Flags & EWX_INTERNAL_FLAG)
{
/* Only Winlogon can call this */
if (ProcessId != LogonProcessId)
{
DPRINT1("SrvExitWindowsEx call not from Winlogon\n");
return STATUS_ACCESS_DENIED;
}
// This function is a HACK!!
// EWX_INTERNAL_FLAG --> For Winlogon only!!
// Status = InternalExitReactos(ProcessId, ThreadId, Flags);
// This is this function that we need to call instead!
Status = UserExitReactos(CsrThread, Flags);
}
else
{
Status = UserExitReactos(CsrThread, Flags);
}
ExitReactosRequest->Success = NT_SUCCESS(Status);
return Status; return Status;
} }
@ -1082,7 +945,7 @@ CSR_API(SrvEndTask)
{ {
if (EndTaskRequest->Force) if (EndTaskRequest->Force)
{ {
EndTaskRequest->Success = DestroyWindow(EndTaskRequest->WndHandle); EndTaskRequest->Success = DestroyWindow(EndTaskRequest->WndHandle);
EndTaskRequest->LastError = GetLastError(); EndTaskRequest->LastError = GetLastError();
} }
else else
@ -1092,8 +955,8 @@ CSR_API(SrvEndTask)
} }
else else
{ {
EndTaskRequest->Success = TRUE;
EndTaskRequest->LastError = ERROR_SUCCESS; EndTaskRequest->LastError = ERROR_SUCCESS;
EndTaskRequest->Success = TRUE;
} }
return STATUS_SUCCESS; return STATUS_SUCCESS;