[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;
} SHUTDOWN_SETTINGS, *PSHUTDOWN_SETTINGS;
SHUTDOWN_SETTINGS ShutdownSettings = {0};
#define DEFAULT_AUTO_END_TASKS FALSE
#define DEFAULT_HUNG_APP_TIMEOUT 5000
#define DEFAULT_WAIT_TO_KILL_APP_TIMEOUT 20000
@ -84,16 +86,7 @@ typedef struct tagPROCESS_ENUM_CONTEXT
/* FUNCTIONS ******************************************************************/
/*
NTSTATUS FASTCALL
Win32CsrEnumProcesses(CSRSS_ENUM_PROCESS_PROC EnumProc,
PVOID Context)
{
return CsrEnumProcesses(EnumProc, Context);
}
*/
static void FASTCALL
static VOID FASTCALL
UpdateProgressBar(HWND ProgressBar, PNOTIFY_CONTEXT NotifyContext)
{
DWORD Passed;
@ -185,20 +178,21 @@ EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
return Result;
}
static void
CallInitCommonControls()
HMODULE hComCtl32Lib = NULL;
static VOID
CallInitCommonControls(VOID)
{
static BOOL Initialized = FALSE;
HMODULE Lib;
INITCOMMONCONTROLS_PROC InitProc;
if (Initialized) return;
Lib = LoadLibraryW(L"COMCTL32.DLL");
if (NULL == Lib) return;
hComCtl32Lib = LoadLibraryW(L"COMCTL32.DLL");
if (hComCtl32Lib == NULL) return;
InitProc = (INITCOMMONCONTROLS_PROC) GetProcAddress(Lib, "InitCommonControls");
if (NULL == InitProc) return;
InitProc = (INITCOMMONCONTROLS_PROC)GetProcAddress(hComCtl32Lib, "InitCommonControls");
if (InitProc == NULL) return;
(*InitProc)();
@ -292,88 +286,74 @@ NotifyTopLevelEnum(HWND Wnd, LPARAM lParam)
return FALSE;
}
if (ProcessId == NotifyContext->ProcessId)
if (ProcessId != NotifyContext->ProcessId)
goto Quit;
Now = GetTickCount();
if (0 == NotifyContext->StartTime)
{
Now = GetTickCount();
if (0 == NotifyContext->StartTime)
NotifyContext->StartTime = Now;
}
/*
* 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
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)
Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
if (Passed < Timeout)
{
MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
Timeout -= Passed;
WaitStatus = WaitForSingleObjectEx(MessageThread, Timeout, FALSE);
}
if (Passed < MessageContext.Timeout)
else
{
MessageContext.Timeout -= Passed;
MessageThread = CreateThread(NULL, 0, NotifyContext->SendMessageProc,
(LPVOID) &MessageContext, 0, NULL);
if (NULL == MessageThread)
WaitStatus = WAIT_TIMEOUT;
}
if (WAIT_TIMEOUT == WaitStatus)
{
NotifyContext->WndClient = Wnd;
if (NULL == NotifyContext->UIThread && NotifyContext->ShowUI)
{
NotifyContext->QueryResult = QUERY_RESULT_ERROR;
return FALSE;
NotifyContext->UIThread = CreateThread(NULL, 0,
EndNowThreadProc,
(LPVOID) NotifyContext,
0, NULL);
}
Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
if (Passed < Timeout)
Threads[0] = MessageThread;
Threads[1] = NotifyContext->UIThread;
WaitStatus = WaitForMultipleObjectsEx(NULL == NotifyContext->UIThread ?
1 : 2,
Threads, FALSE, INFINITE,
FALSE);
if (WAIT_OBJECT_0 == WaitStatus)
{
Timeout -= Passed;
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
if (! GetExitCodeThread(MessageThread, &NotifyContext->QueryResult))
{
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 = QUERY_RESULT_ERROR;
@ -383,15 +363,33 @@ NotifyTopLevelEnum(HWND Wnd, LPARAM lParam)
{
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
{
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
@ -470,7 +468,7 @@ ConioConsoleCtrlEventTimeout(DWORD Event, PCSR_PROCESS ProcessData, DWORD Timeou
#endif
/************************************************/
static BOOL FASTCALL
BOOL FASTCALL
NotifyAndTerminateProcess(PCSR_PROCESS ProcessData,
PSHUTDOWN_SETTINGS ShutdownSettings,
UINT Flags)
@ -629,32 +627,7 @@ ExitReactosProcessEnum(PCSR_PROCESS ProcessData, PVOID Data)
}
#endif
static int
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;
}
#if 0
static DWORD FASTCALL
GetShutdownSettings(HKEY DesktopKey, LPCWSTR ValueName, DWORD DefaultValue)
{
@ -695,7 +668,7 @@ GetShutdownSettings(HKEY DesktopKey, LPCWSTR ValueName, DWORD DefaultValue)
return DefaultValue;
}
static void FASTCALL
static VOID FASTCALL
LoadShutdownSettings(PSID Sid, PSHUTDOWN_SETTINGS ShutdownSettings)
{
static WCHAR Subkey[] = L"\\Control Panel\\Desktop";
@ -761,140 +734,30 @@ LoadShutdownSettings(PSID Sid, PSHUTDOWN_SETTINGS ShutdownSettings)
NTSTATUS FASTCALL
InternalExitReactos(DWORD ProcessId, DWORD ThreadId, UINT Flags)
{
HANDLE CallerThread;
HANDLE CallerToken;
NTSTATUS Status;
PROCESS_ENUM_CONTEXT Context;
DWORD ReturnLength;
// PROCESS_ENUM_CONTEXT Context;
HWND ShellWnd;
UINT ProcessIndex;
char FixedUserInfo[64];
TOKEN_USER *UserInfo;
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);
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);
if (! NT_SUCCESS(Status))
{
DPRINT1("Failed to enumerate registered processes, status 0x%x\n",
Status);
if (NULL != Context.ProcessData)
{
HeapFree(UserServerHeap, 0, Context.ProcessData);
}
return Status;
}
// 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;
// }
qsort(Context.ProcessData, Context.ProcessCount, sizeof(PCSR_PROCESS),
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;
return STATUS_SUCCESS;
}
#endif
static NTSTATUS FASTCALL
UserExitReactos(PCSR_THREAD CsrThread, UINT Flags)
@ -902,10 +765,26 @@ UserExitReactos(PCSR_THREAD CsrThread, UINT Flags)
NTSTATUS Status;
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
*/
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 */
if (Flags & (EWX_POWEROFF | EWX_REBOOT))
Flags |= EWX_SHUTDOWN;
@ -984,14 +863,6 @@ UserExitReactos(PCSR_THREAD CsrThread, UINT Flags)
{
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
@ -1018,7 +889,29 @@ UserClientShutdown(IN PCSR_PROCESS CsrProcess,
DPRINT1("UserClientShutdown(0x%p, 0x%x, %s)\n",
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;
}
@ -1030,41 +923,11 @@ CSR_API(SrvExitWindowsEx)
{
NTSTATUS Status;
PUSER_EXIT_REACTOS ExitReactosRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactosRequest;
PCSR_THREAD CsrThread = CsrGetClientThread();
ULONG Flags = ExitReactosRequest->Flags;
DWORD ProcessId = HandleToUlong(CsrThread->ClientId.UniqueProcess);
DWORD ThreadId = HandleToUlong(CsrThread->ClientId.UniqueThread);
Status = UserExitReactos(CsrGetClientThread(), ExitReactosRequest->Flags);
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;
}
@ -1082,7 +945,7 @@ CSR_API(SrvEndTask)
{
if (EndTaskRequest->Force)
{
EndTaskRequest->Success = DestroyWindow(EndTaskRequest->WndHandle);
EndTaskRequest->Success = DestroyWindow(EndTaskRequest->WndHandle);
EndTaskRequest->LastError = GetLastError();
}
else
@ -1092,8 +955,8 @@ CSR_API(SrvEndTask)
}
else
{
EndTaskRequest->Success = TRUE;
EndTaskRequest->LastError = ERROR_SUCCESS;
EndTaskRequest->Success = TRUE;
}
return STATUS_SUCCESS;