Send proper messages/events to processes during logoff and kill them if they dont self-destruct. Work in progress, not complete yet. Committed so others can work on it too. Modified: trunk/reactos/include/reactos/winlogon.h Modified: trunk/reactos/lib/user32/misc/exit.c Modified: trunk/reactos/subsys/csrss/win32csr/conio.c Modified: trunk/reactos/subsys/csrss/win32csr/dllmain.c Modified: trunk/reactos/subsys/csrss/win32csr/exitros.c Added: trunk/reactos/subsys/csrss/win32csr/resource.h Modified: trunk/reactos/subsys/csrss/win32csr/win32csr.rc Modified: trunk/reactos/subsys/csrss/win32csr/win32csr.xml Modified: trunk/reactos/subsys/system/winlogon/sas.c Modified: trunk/reactos/subsys/system/winlogon/winlogon.c Modified: trunk/reactos/subsys/system/winlogon/winlogon.h Modified: trunk/reactos/subsys/system/winlogon/wlx.c _____
Modified: trunk/reactos/include/reactos/winlogon.h --- trunk/reactos/include/reactos/winlogon.h 2005-12-01 22:24:00 UTC (rev 19814) +++ trunk/reactos/include/reactos/winlogon.h 2005-12-01 22:29:57 UTC (rev 19815) @@ -10,15 +10,12 @@
#ifndef REACTOS_WINLOGON_H_INCLUDED #define REACTOS_WINLOGON_H_INCLUDED
-#define WINLOGON_DESKTOP L"Winlogon" -#define WINLOGON_SAS_CLASS L"SAS window class" -#define WINLOGON_SAS_TITLE L"SAS" - #define PM_WINLOGON_EXITWINDOWS WM_APP
-#define EWX_INTERNAL_FLAG 0x10000 +#define EWX_INTERNAL_FLAG 0x10000 #define EWX_INTERNAL_KILL_USER_APPS (EWX_INTERNAL_FLAG | 0x100) #define EWX_INTERNAL_KILL_ALL_APPS (EWX_INTERNAL_FLAG | 0x200) +#define EWX_INTERNAL_FLAG_LOGOFF 0x1000
#endif /* REACTOS_WINLOGON_H_INCLUDED */
_____
Modified: trunk/reactos/lib/user32/misc/exit.c --- trunk/reactos/lib/user32/misc/exit.c 2005-12-01 22:24:00 UTC (rev 19814) +++ trunk/reactos/lib/user32/misc/exit.c 2005-12-01 22:29:57 UTC (rev 19815) @@ -13,11 +13,9 @@
* Sequence of events: * * - App (usually explorer) calls ExitWindowsEx() - * - ExitWindowsEx() sends a message to CSRSS (note: investigation shows it - * doesn't transfer to kernel mode) + * - ExitWindowsEx() sends a message to CSRSS * - CSRSS impersonates the caller and sends a message to a hidden WinLogon window - * - WinLogon sends a SAS event to the GINA, asking for permission (e.g. if the - * required rights are granted) to proceed + * - WinLogon checks if the caller has the required privileges * - WinLogon enters pending log-out state * - WinLogon impersonates the interactive user and calls ExitWindowsEx() again, * passing some special internal flags @@ -28,7 +26,7 @@ * CSRSS will put up a dialog box asking if the process should be terminated. * Using the registry key HKCU\Control Panel\Desktop\AutoEndTask you can * specify that the dialog box shouldn't be shown and CSRSS should just - * terminates the thread. If the the WM_ENDSESSION message is processed + * terminate the thread. If the the WM_ENDSESSION message is processed * but the thread doesn't terminate within the timeout specified by * HKCU\Control Panel\Desktop\WaitToKillAppTimeout CSRSS will terminate * the thread. When all the top-level windows have been destroyed CSRSS @@ -53,15 +51,13 @@ * dialog boxes or kill threads/processes. Same for console processes, * using the CTRL_SHUTDOWN_EVENT. The Service Control Manager is one of * these console processes and has a special timeout value WaitToKillServiceTimeout. - * - WinLogon calls ADVAPI32.InitiateSystemShutdown() - * - ADVAPI32.InitiateSystemShutdown*() issues a "InitiateSystemShutdown" request - * to the SM (SMSS API # 1) + * - WinLogon issues a "InitiateSystemShutdown" request to the SM (SMSS API # 1) * - the SM propagates the shutdown request to every environment subsystem it * started since bootstrap time (still active ones, of course) * - each environment subsystem, on shutdown request, releases every resource * it aquired during its life (processes, memory etc), then dies * - when every environment subsystem has gone to bed, the SM actually initiates - * to shutdown the kernel and executive by calling NtShutdownSystem. + * the kernel and executive shutdown by calling NtShutdownSystem. */ /* * @implemented _____
Modified: trunk/reactos/subsys/csrss/win32csr/conio.c --- trunk/reactos/subsys/csrss/win32csr/conio.c 2005-12-01 22:24:00 UTC (rev 19814) +++ trunk/reactos/subsys/csrss/win32csr/conio.c 2005-12-01 22:29:57 UTC (rev 19815) @@ -56,7 +56,7 @@
}
VOID FASTCALL -ConioConsoleCtrlEvent(DWORD Event, PCSRSS_PROCESS_DATA ProcessData) +ConioConsoleCtrlEventTimeout(DWORD Event, PCSRSS_PROCESS_DATA ProcessData, DWORD Timeout) { HANDLE Thread;
@@ -73,10 +73,17 @@ DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError()); return; } + WaitForSingleObject(Thread, Timeout); CloseHandle(Thread); } }
+VOID FASTCALL +ConioConsoleCtrlEvent(DWORD Event, PCSRSS_PROCESS_DATA ProcessData) +{ + ConioConsoleCtrlEventTimeout(Event, ProcessData, INFINITE); +} + #define GET_CELL_BUFFER(b,o)\ (b)->Buffer[(o)++]
_____
Modified: trunk/reactos/subsys/csrss/win32csr/dllmain.c --- trunk/reactos/subsys/csrss/win32csr/dllmain.c 2005-12-01 22:24:00 UTC (rev 19814) +++ trunk/reactos/subsys/csrss/win32csr/dllmain.c 2005-12-01 22:29:57 UTC (rev 19815) @@ -143,11 +143,18 @@
NTSTATUS FASTCALL Win32CsrReleaseObject(PCSRSS_PROCESS_DATA ProcessData, - HANDLE Object) + HANDLE Object) { return (CsrExports.CsrReleaseObjectProc)(ProcessData, Object); }
+NTSTATUS FASTCALL +Win32CsrEnumProcesses(CSRSS_ENUM_PROCESS_PROC EnumProc, + PVOID Context) +{ + return (CsrExports.CsrEnumProcessesProc)(EnumProc, Context); +} + static BOOL STDCALL Win32CsrInitComplete(void) { _____
Modified: trunk/reactos/subsys/csrss/win32csr/exitros.c --- trunk/reactos/subsys/csrss/win32csr/exitros.c 2005-12-01 22:24:00 UTC (rev 19814) +++ trunk/reactos/subsys/csrss/win32csr/exitros.c 2005-12-01 22:29:57 UTC (rev 19815) @@ -9,6 +9,8 @@
/* INCLUDES ******************************************************************/
#include "w32csr.h" +#include <sddl.h> +#include "resource.h"
#define NDEBUG #include <debug.h> @@ -52,7 +54,8 @@ DWORD WindowCreator;
Request->Header.u1.s1.TotalLength = sizeof(CSR_API_MESSAGE); - Request->Header.u1.s1.DataLength = sizeof(CSR_API_MESSAGE) - sizeof(PORT_MESSAGE); + Request->Header.u1.s1.DataLength = sizeof(CSR_API_MESSAGE) - + sizeof(PORT_MESSAGE);
if (0 == GetWindowThreadProcessId(Request->Data.SetLogonNotifyWindowRequest.Logon NotifyWindow, &WindowCreator)) @@ -75,34 +78,873 @@ return Request->Status; }
-CSR_API(CsrExitReactos) +typedef struct tagSHUTDOWN_SETTINGS { - Request->Header.u1.s1.TotalLength = sizeof(CSR_API_MESSAGE); - Request->Header.u1.s1.DataLength = sizeof(CSR_API_MESSAGE) - sizeof(PORT_MESSAGE); + BOOL AutoEndTasks; + DWORD HungAppTimeout; + DWORD WaitToKillAppTimeout; +} SHUTDOWN_SETTINGS, *PSHUTDOWN_SETTINGS;
+#define DEFAULT_AUTO_END_TASKS FALSE +#define DEFAULT_HUNG_APP_TIMEOUT 5000 +#define DEFAULT_WAIT_TO_KILL_APP_TIMEOUT 20000 + +typedef struct tagNOTIFY_CONTEXT +{ + DWORD ProcessId; + UINT Msg; + WPARAM wParam; + LPARAM lParam; + HDESK Desktop; + DWORD StartTime; + DWORD QueryResult; + HWND Dlg; + DWORD EndNowResult; + BOOL ShowUI; + HANDLE UIThread; + HWND WndClient; + PSHUTDOWN_SETTINGS ShutdownSettings; + LPTHREAD_START_ROUTINE SendMessageProc; +} NOTIFY_CONTEXT, *PNOTIFY_CONTEXT; + +#define QUERY_RESULT_ABORT 0 +#define QUERY_RESULT_CONTINUE 1 +#define QUERY_RESULT_TIMEOUT 2 +#define QUERY_RESULT_ERROR 3 +#define QUERY_RESULT_FORCE 4 + +static void FASTCALL +UpdateProgressBar(HWND ProgressBar, PNOTIFY_CONTEXT NotifyContext) +{ + DWORD Passed; + + Passed = GetTickCount() - NotifyContext->StartTime; + Passed -= NotifyContext->ShutdownSettings->HungAppTimeout; + if (NotifyContext->ShutdownSettings->WaitToKillAppTimeout < Passed) + { + Passed = NotifyContext->ShutdownSettings->WaitToKillAppTimeout; + } + SendMessageW(ProgressBar, PBM_SETPOS, Passed / 2, 0); +} + +static INT_PTR CALLBACK +EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + INT_PTR Result; + PNOTIFY_CONTEXT NotifyContext; + HWND ProgressBar; + DWORD TitleLength; + int Len; + LPWSTR Title; + + switch(Msg) + { + case WM_INITDIALOG: + NotifyContext = (PNOTIFY_CONTEXT) lParam; + NotifyContext->EndNowResult = QUERY_RESULT_ABORT; + SetWindowLongPtrW(Dlg, DWLP_USER, (LONG_PTR) lParam); + TitleLength = SendMessageW(NotifyContext->WndClient, WM_GETTEXTLENGTH, + 0, 0) + + GetWindowTextLengthW(Dlg); + Title = HeapAlloc(Win32CsrApiHeap, 0, (TitleLength + 1) * sizeof(WCHAR)); + if (NULL != Title) + { + Len = GetWindowTextW(Dlg, Title, TitleLength + 1); + SendMessageW(NotifyContext->WndClient, WM_GETTEXT, + TitleLength + 1 - Len, (LPARAM) (Title + Len)); + SetWindowTextW(Dlg, Title); + HeapFree(Win32CsrApiHeap, 0, Title); + } + ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS); + SendMessageW(ProgressBar, PBM_SETRANGE32, 0, + NotifyContext->ShutdownSettings->WaitToKillAppTimeout / 2); + UpdateProgressBar(ProgressBar, NotifyContext); + SetTimer(Dlg, 0, 200, NULL); + Result = FALSE; + break; + + case WM_TIMER: + NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER); + ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS); + UpdateProgressBar(ProgressBar, NotifyContext); + Result = TRUE; + break; + + case WM_COMMAND: + if (BN_CLICKED == HIWORD(wParam) && IDC_END_NOW == LOWORD(wParam)) + { + NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER); + NotifyContext->EndNowResult = QUERY_RESULT_FORCE; + SendMessageW(Dlg, WM_CLOSE, 0, 0); + Result = TRUE; + } + else + { + Result = FALSE; + } + break; + + case WM_CLOSE: + DestroyWindow(Dlg); + Result = TRUE; + break; + + case WM_DESTROY: + NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER); + NotifyContext->Dlg = NULL; + KillTimer(Dlg, 0); + PostQuitMessage(NotifyContext->EndNowResult); + Result = TRUE; + break; + + default: + Result = FALSE; + break; + } + + return Result; +} + +typedef void (STDCALL *INITCOMMONCONTROLS_PROC)(void); + +static void FASTCALL +CallInitCommonControls() +{ + static BOOL Initialized = FALSE; + HMODULE Lib; + INITCOMMONCONTROLS_PROC InitProc; + + if (Initialized) + { + return; + } + + Lib = LoadLibraryW(L"COMCTL32.DLL"); + if (NULL == Lib) + { + return; + } + InitProc = (INITCOMMONCONTROLS_PROC) GetProcAddress(Lib, "InitCommonControls"); + if (NULL == InitProc) + { + return; + } + + (*InitProc)(); + + Initialized = TRUE; +} + +static DWORD WINAPI +EndNowThreadProc(LPVOID Parameter) +{ + PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT) Parameter; + MSG Msg; + + SetThreadDesktop(NotifyContext->Desktop); + SwitchDesktop(NotifyContext->Desktop); + CallInitCommonControls(); + NotifyContext->Dlg = CreateDialogParam(Win32CsrDllHandle, + MAKEINTRESOURCE(IDD_END_NOW), NULL, + EndNowDlgProc, (LPARAM) NotifyContext); + if (NULL == NotifyContext->Dlg) + { + return 0; + } + ShowWindow(NotifyContext->Dlg, SW_SHOWNORMAL); + + while (GetMessageW(&Msg, NULL, 0, 0)) + { + if (! IsDialogMessage(NotifyContext->Dlg, &Msg)) + { + TranslateMessage(&Msg); + DispatchMessageW(&Msg); + } + } + + return Msg.wParam; +} + +typedef struct tagMESSAGE_CONTEXT +{ + HWND Wnd; + UINT Msg; + WPARAM wParam; + LPARAM lParam; + DWORD Timeout; +} MESSAGE_CONTEXT, *PMESSAGE_CONTEXT; + +static DWORD WINAPI +SendQueryEndSession(LPVOID Parameter) +{ + PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT) Parameter; + LRESULT Result; + + if (SendMessageTimeoutW(Context->Wnd, WM_QUERYENDSESSION, Context->wParam, + Context->lParam, SMTO_NORMAL, Context->Timeout, + &Result)) + { + return Result ? QUERY_RESULT_CONTINUE : QUERY_RESULT_ABORT; + } + + return 0 == GetLastError() ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR; +} + +static DWORD WINAPI +SendEndSession(LPVOID Parameter) +{ + PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT) Parameter; + LRESULT Result; + + if (Context->wParam) + { + if (SendMessageTimeoutW(Context->Wnd, WM_ENDSESSION, Context->wParam, + Context->lParam, SMTO_NORMAL, Context->Timeout, + &Result)) + { + return QUERY_RESULT_CONTINUE; + } + return 0 == GetLastError() ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR; + } + else + { + SendMessage(Context->Wnd, WM_ENDSESSION, Context->wParam, + Context->lParam); + return QUERY_RESULT_CONTINUE; + } +} + +static BOOL CALLBACK +NotifyTopLevelEnum(HWND Wnd, LPARAM lParam) +{ + PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT) lParam; + MESSAGE_CONTEXT MessageContext; + DWORD Now, Passed; + DWORD Timeout, WaitStatus; + DWORD ProcessId; + HANDLE MessageThread; + HANDLE Threads[2]; + + if (0 == GetWindowThreadProcessId(Wnd, &ProcessId)) + { + NotifyContext->QueryResult = QUERY_RESULT_ERROR; + return FALSE; + } + + if (ProcessId == NotifyContext->ProcessId) + { + 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->QueryResult = QUERY_RESULT_ERROR; + return FALSE; + } + Timeout = NotifyContext->ShutdownSettings->HungAppTimeout; + if (Passed < Timeout) + { + 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 + { + NotifyContext->QueryResult = QUERY_RESULT_ERROR; + } + 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_ERROR; + } + CloseHandle(MessageThread); + } + else + { + NotifyContext->QueryResult = QUERY_RESULT_TIMEOUT; + } + } + + return QUERY_RESULT_CONTINUE == NotifyContext->QueryResult; +} + +static BOOL CALLBACK +NotifyDesktopEnum(LPWSTR DesktopName, LPARAM lParam) +{ + PNOTIFY_CONTEXT Context = (PNOTIFY_CONTEXT) lParam; + + Context->Desktop = OpenDesktopW(DesktopName, 0, FALSE, + DESKTOP_ENUMERATE | DESKTOP_SWITCHDESKTOP); + if (NULL == Context->Desktop) + { + DPRINT1("OpenDesktop failed with error %d\n", GetLastError()); + Context->QueryResult = QUERY_RESULT_ERROR; + return FALSE; + } + + EnumDesktopWindows(Context->Desktop, NotifyTopLevelEnum, lParam); + + CloseDesktop(Context->Desktop); + + return QUERY_RESULT_CONTINUE == Context->QueryResult; +} + +static BOOL FASTCALL +NotifyTopLevelWindows(PNOTIFY_CONTEXT Context) +{ + HWINSTA WindowStation; + + WindowStation = GetProcessWindowStation(); + if (NULL == WindowStation) + { + DPRINT1("GetProcessWindowStation failed with error %d\n", GetLastError()); + return TRUE; + } + + EnumDesktopsW(WindowStation, NotifyDesktopEnum, (LPARAM) Context); + + return TRUE; +} + +static BOOL FASTCALL +NotifyAndTerminateProcess(PCSRSS_PROCESS_DATA ProcessData, + PSHUTDOWN_SETTINGS ShutdownSettings, + UINT Flags) +{ + NOTIFY_CONTEXT Context; + HANDLE Process; + DWORD QueryResult = QUERY_RESULT_CONTINUE; + + Context.QueryResult = QUERY_RESULT_CONTINUE; + + if (0 == (Flags & EWX_FORCE)) + { + if (NULL != ProcessData->Console) + { + ConioConsoleCtrlEventTimeout(CTRL_LOGOFF_EVENT, ProcessData, + ShutdownSettings->WaitToKillAppTimeout); + } + else + { + Context.ProcessId = (DWORD) ProcessData->ProcessId; + Context.wParam = 0; + Context.lParam = (0 != (Flags & EWX_INTERNAL_FLAG_LOGOFF) ? + ENDSESSION_LOGOFF : 0); + Context.StartTime = 0; + Context.UIThread = NULL; + Context.ShowUI = DtbgIsDesktopVisible(); + Context.Dlg = NULL; + Context.ShutdownSettings = ShutdownSettings; + Context.SendMessageProc = SendQueryEndSession; + + NotifyTopLevelWindows(&Context); + + Context.wParam = (QUERY_RESULT_ABORT != Context.QueryResult); + Context.lParam = (0 != (Flags & EWX_INTERNAL_FLAG_LOGOFF) ? + ENDSESSION_LOGOFF : 0); + Context.SendMessageProc = SendEndSession; + Context.ShowUI = DtbgIsDesktopVisible() && + (QUERY_RESULT_ABORT != Context.QueryResult); + QueryResult = Context.QueryResult; + Context.QueryResult = QUERY_RESULT_CONTINUE; + + NotifyTopLevelWindows(&Context); + + if (NULL != Context.UIThread) + { + if (NULL != Context.Dlg) + { + SendMessageW(Context.Dlg, WM_CLOSE, 0, 0); + } + else + { + TerminateThread(Context.UIThread, QUERY_RESULT_ERROR); + } + CloseHandle(Context.UIThread); + } + } + + if (QUERY_RESULT_ABORT == QueryResult) + { + return FALSE; + } + } + + /* Terminate this process */ + Process = OpenProcess(PROCESS_TERMINATE, FALSE, + (DWORD) ProcessData->ProcessId); + if (NULL == Process) + { + DPRINT1("Unable to open process %d, error %d\n", ProcessData->ProcessId, + GetLastError()); + return TRUE; + } + TerminateProcess(Process, 0); + CloseHandle(Process); + + return TRUE; +} + +typedef struct tagPROCESS_ENUM_CONTEXT +{ + UINT ProcessCount; + PCSRSS_PROCESS_DATA *ProcessData; + TOKEN_ORIGIN TokenOrigin; + DWORD ShellProcess; + DWORD CsrssProcess; +} PROCESS_ENUM_CONTEXT, *PPROCESS_ENUM_CONTEXT; + +static NTSTATUS STDCALL +ExitReactosProcessEnum(PCSRSS_PROCESS_DATA ProcessData, PVOID Data) +{ + HANDLE Process; + HANDLE Token; + TOKEN_ORIGIN Origin; + DWORD ReturnLength; + PPROCESS_ENUM_CONTEXT Context = (PPROCESS_ENUM_CONTEXT) Data; + PCSRSS_PROCESS_DATA *NewData; + + /* Do not kill winlogon or csrss */ + if ((DWORD) ProcessData->ProcessId == Context->CsrssProcess || + ProcessData->ProcessId == LogonProcess) + { + return STATUS_SUCCESS; + } + + /* Get the login session of this process */ + Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, + (DWORD) ProcessData->ProcessId); + if (NULL == Process) + { + DPRINT1("Unable to open process %d, error %d\n", ProcessData->ProcessId, + GetLastError()); + return STATUS_UNSUCCESSFUL; + } + + if (! OpenProcessToken(Process, TOKEN_QUERY, &Token)) + { + DPRINT1("Unable to open token for process %d, error %d\n", + ProcessData->ProcessId, GetLastError()); + CloseHandle(Process); + return STATUS_UNSUCCESSFUL; + } + CloseHandle(Process); + + if (! GetTokenInformation(Token, TokenOrigin, &Origin, + sizeof(TOKEN_ORIGIN), &ReturnLength)) + { + DPRINT1("GetTokenInformation failed for process %d with error %d\n", + ProcessData->ProcessId, GetLastError()); + CloseHandle(Token); + return STATUS_UNSUCCESSFUL; + } + CloseHandle(Token); + + /* This process will be killed if it's in the correct logon session */ + if (RtlEqualLuid(&(Context->TokenOrigin.OriginatingLogonSession), + &(Origin.OriginatingLogonSession))) + { + /* Kill the shell process last */ + if ((DWORD) ProcessData->ProcessId == Context->ShellProcess) + { + ProcessData->ShutdownLevel = 0; + } + NewData = HeapAlloc(Win32CsrApiHeap, 0, (Context->ProcessCount + 1) + * sizeof(PCSRSS_PROCESS_DATA)); + if (NULL == NewData) + { + return STATUS_NO_MEMORY; + } + if (0 != Context->ProcessCount) + { + memcpy(NewData, Context->ProcessData, + Context->ProcessCount * sizeof(PCSRSS_PROCESS_DATA)); + HeapFree(Win32CsrApiHeap, 0, Context->ProcessData); + } + Context->ProcessData = NewData; + Context->ProcessData[Context->ProcessCount] = ProcessData; + Context->ProcessCount++; + } + + return STATUS_SUCCESS; +} + +static int +ProcessDataCompare(const void *Elem1, const void *Elem2) +{ + const PCSRSS_PROCESS_DATA *ProcessData1 = (PCSRSS_PROCESS_DATA *) Elem1; + const PCSRSS_PROCESS_DATA *ProcessData2 = (PCSRSS_PROCESS_DATA *) Elem2; + + if ((*ProcessData1)->ShutdownLevel < (*ProcessData2)->ShutdownLevel) + { + return +1; + } + else if ((*ProcessData2)->ShutdownLevel < (*ProcessData1)->ShutdownLevel) + { + return -1; + } + else if ((*ProcessData1)->ProcessId < (*ProcessData2)->ProcessId) + { + return +1; + } + else if ((*ProcessData2)->ProcessId < (*ProcessData1)->ProcessId) + { + return -1; + } + + return 0; +} + +static DWORD FASTCALL +GetShutdownSetting(HKEY DesktopKey, LPCWSTR ValueName, DWORD DefaultValue) +{ + BYTE ValueBuffer[16]; + LONG ErrCode; + DWORD Type; + DWORD ValueSize; + UNICODE_STRING StringValue; + ULONG Value; + + ValueSize = sizeof(ValueBuffer); + ErrCode = RegQueryValueExW(DesktopKey, ValueName, NULL, &Type, ValueBuffer, + &ValueSize); + if (ERROR_SUCCESS != ErrCode) + { + DPRINT("GetShutdownSetting for %S failed with error code %ld\n", + ValueName, ErrCode); + return DefaultValue; + } + + if (REG_SZ == Type) + { + RtlInitUnicodeString(&StringValue, (LPCWSTR) ValueBuffer); + if (! NT_SUCCESS(RtlUnicodeStringToInteger(&StringValue, 10, &Value))) + { + DPRINT1("Unable to convert value %S for setting %S\n", + StringValue.Buffer, ValueName); + return DefaultValue; + } + return (DWORD) Value; + } + else if (REG_DWORD == Type) + { + return *((DWORD *) ValueBuffer); + } + + DPRINT1("Unexpected registry type %d for setting %S\n", Type, ValueName); + return DefaultValue; +} + +static void FASTCALL +LoadShutdownSettings(PSID Sid, PSHUTDOWN_SETTINGS ShutdownSettings) +{ + static WCHAR Subkey[] = L"\Control Panel\Desktop"; + LPWSTR StringSid; + WCHAR InitialKeyName[128]; + LPWSTR KeyName; + HKEY DesktopKey; + LONG ErrCode; + + ShutdownSettings->AutoEndTasks = DEFAULT_AUTO_END_TASKS; + ShutdownSettings->HungAppTimeout = DEFAULT_HUNG_APP_TIMEOUT; + ShutdownSettings->WaitToKillAppTimeout = DEFAULT_WAIT_TO_KILL_APP_TIMEOUT; + + if (! ConvertSidToStringSidW(Sid, &StringSid)) + { + DPRINT1("ConvertSidToStringSid failed with error %d, using default shutdown settings\n", + GetLastError()); + return; + } + if (wcslen(StringSid) + wcslen(Subkey) + 1 <= + sizeof(InitialKeyName) / sizeof(WCHAR)) + { + KeyName = InitialKeyName; + } + else + { + KeyName = HeapAlloc(Win32CsrApiHeap, 0, + (wcslen(StringSid) + wcslen(Subkey) + 1) * + sizeof(WCHAR)); + if (NULL == KeyName) + { + DPRINT1("Failed to allocate memory, using default shutdown settings\n"); + LocalFree(StringSid); + return; + } + } + wcscat(wcscpy(KeyName, StringSid), Subkey); + LocalFree(StringSid); + + ErrCode = RegOpenKeyExW(HKEY_USERS, KeyName, 0, KEY_QUERY_VALUE, &DesktopKey); + if (KeyName != InitialKeyName) + { + HeapFree(Win32CsrApiHeap, 0, KeyName); + } + if (ERROR_SUCCESS != ErrCode) + { + DPRINT1("RegOpenKeyEx failed with error %ld, using default shutdown settings\n", ErrCode); + return; + } + + ShutdownSettings->AutoEndTasks = (BOOL) GetShutdownSetting(DesktopKey, L"AutoEndTasks", + (DWORD) DEFAULT_AUTO_END_TASKS); + ShutdownSettings->HungAppTimeout = GetShutdownSetting(DesktopKey, + L"HungAppTimeout", + DEFAULT_HUNG_APP_TIMEOUT); + ShutdownSettings->WaitToKillAppTimeout = GetShutdownSetting(DesktopKey, + L"WaitToKillAppTimeout", + DEFAULT_WAIT_TO_KILL_APP_TIMEOUT); + + RegCloseKey(DesktopKey); +} + +static NTSTATUS FASTCALL +InternalExitReactos(DWORD ProcessId, DWORD ThreadId, UINT Flags) +{ + HANDLE CallerThread; + HANDLE CallerToken; + NTSTATUS Status; + PROCESS_ENUM_CONTEXT Context; + DWORD ReturnLength; + HWND ShellWnd; + UINT ProcessIndex; + char FixedUserInfo[64]; + TOKEN_USER *UserInfo; + SHUTDOWN_SETTINGS ShutdownSettings; + + if (ProcessId != (DWORD) LogonProcess) + { + DPRINT1("Internal ExitWindowsEx call not from winlogon\n"); + return STATUS_ACCESS_DENIED; + } + + 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(Win32CsrApiHeap, 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(Win32CsrApiHeap, 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(Win32CsrApiHeap, 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(Win32CsrApiHeap, 0, Context.ProcessData); + } + return Status; + } + + qsort(Context.ProcessData, Context.ProcessCount, sizeof(PCSRSS_PROCESS_DATA), + 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(Win32CsrApiHeap, 0, Context.ProcessData); + } + + return Status; +} + +static NTSTATUS FASTCALL +UserExitReactos(DWORD UserProcessId, UINT Flags) +{ + NTSTATUS Status; + if (NULL == LogonNotifyWindow) { DPRINT1("No LogonNotifyWindow registered\n"); - Request->Status = STATUS_NOT_FOUND; - return Request->Status; + return STATUS_NOT_FOUND; }
/* FIXME Inside 2000 says we should impersonate the caller here */ - Request->Status = SendMessageW(LogonNotifyWindow, PM_WINLOGON_EXITWINDOWS, - (WPARAM) Request->Header.ClientId.UniqueProcess, - (LPARAM) Request->Data.ExitReactosRequest.Flags); - /* If the message isn't handled, the return value is 0, so 0 doesn't indicate success. - Success is indicated by a 1 return value, if anything besides 0 or 1 it's a - NTSTATUS value */ - if (1 == Request->Status) + Status = SendMessageW(LogonNotifyWindow, PM_WINLOGON_EXITWINDOWS, + (WPARAM) UserProcessId, + (LPARAM) Flags); + /* If the message isn't handled, the return value is 0, so 0 doesn't indicate + success. Success is indicated by a 1 return value, if anything besides 0 + or 1 it's a NTSTATUS value */ + if (1 == Status) { [truncated at 1000 lines; 346 more skipped]