diff --git a/reactos/include/reactos/winlogon.h b/reactos/include/reactos/winlogon.h index f5fc4609076..4c00b674e81 100644 --- a/reactos/include/reactos/winlogon.h +++ b/reactos/include/reactos/winlogon.h @@ -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 */ diff --git a/reactos/lib/user32/misc/exit.c b/reactos/lib/user32/misc/exit.c index fe38e55d783..e8a0ed96c4f 100644 --- a/reactos/lib/user32/misc/exit.c +++ b/reactos/lib/user32/misc/exit.c @@ -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 diff --git a/reactos/subsys/csrss/win32csr/conio.c b/reactos/subsys/csrss/win32csr/conio.c index b2cc24f4817..1d1ebff3e62 100644 --- a/reactos/subsys/csrss/win32csr/conio.c +++ b/reactos/subsys/csrss/win32csr/conio.c @@ -56,7 +56,7 @@ ConioConsoleFromProcessData(PCSRSS_PROCESS_DATA ProcessData, PCSRSS_CONSOLE *Con } VOID FASTCALL -ConioConsoleCtrlEvent(DWORD Event, PCSRSS_PROCESS_DATA ProcessData) +ConioConsoleCtrlEventTimeout(DWORD Event, PCSRSS_PROCESS_DATA ProcessData, DWORD Timeout) { HANDLE Thread; @@ -73,10 +73,17 @@ ConioConsoleCtrlEvent(DWORD Event, PCSRSS_PROCESS_DATA ProcessData) 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)++] diff --git a/reactos/subsys/csrss/win32csr/dllmain.c b/reactos/subsys/csrss/win32csr/dllmain.c index b4fb3f78858..d42d3d5b1c0 100644 --- a/reactos/subsys/csrss/win32csr/dllmain.c +++ b/reactos/subsys/csrss/win32csr/dllmain.c @@ -143,11 +143,18 @@ Win32CsrUnlockObject(Object_t *Object) 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) { diff --git a/reactos/subsys/csrss/win32csr/exitros.c b/reactos/subsys/csrss/win32csr/exitros.c index e8e5e80c36c..b172447062a 100644 --- a/reactos/subsys/csrss/win32csr/exitros.c +++ b/reactos/subsys/csrss/win32csr/exitros.c @@ -9,6 +9,8 @@ /* INCLUDES ******************************************************************/ #include "w32csr.h" +#include +#include "resource.h" #define NDEBUG #include @@ -52,7 +54,8 @@ CSR_API(CsrSetLogonNotifyWindow) 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.LogonNotifyWindow, &WindowCreator)) @@ -75,32 +78,871 @@ CSR_API(CsrSetLogonNotifyWindow) 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) { - Request->Status = STATUS_SUCCESS; + Status = STATUS_SUCCESS; } - else if (0 == Request->Status) + else if (0 == Status) { - Request->Status = STATUS_NOT_IMPLEMENTED; + Status = STATUS_NOT_IMPLEMENTED; + } + + return Status; +} + +CSR_API(CsrExitReactos) +{ + Request->Header.u1.s1.TotalLength = sizeof(CSR_API_MESSAGE); + Request->Header.u1.s1.DataLength = sizeof(CSR_API_MESSAGE) - + sizeof(PORT_MESSAGE); + + if (0 == (Request->Data.ExitReactosRequest.Flags & EWX_INTERNAL_FLAG)) + { + Request->Status = UserExitReactos((DWORD) Request->Header.ClientId.UniqueProcess, + Request->Data.ExitReactosRequest.Flags); + } + else + { + Request->Status = InternalExitReactos((DWORD) Request->Header.ClientId.UniqueProcess, + (DWORD) Request->Header.ClientId.UniqueThread, + Request->Data.ExitReactosRequest.Flags); } return Request->Status; diff --git a/reactos/subsys/csrss/win32csr/resource.h b/reactos/subsys/csrss/win32csr/resource.h new file mode 100755 index 00000000000..ed15b50169b --- /dev/null +++ b/reactos/subsys/csrss/win32csr/resource.h @@ -0,0 +1,20 @@ +/* $Id$ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Win32 subsystem + * FILE: subsys/csrss/win32csr/resource.h + * PURPOSE: Resource #defines + */ + +#ifndef WIN32CSR_RESOURCE_H_INCLUDED +#define WIN32CSR_RESOURCE_H_INCLUDED + +#define IDD_END_NOW 10 + +#define IDC_STATIC 100 +#define IDC_PROGRESS 101 +#define IDC_END_NOW 102 + +#endif /* WIN32CSR_RESOURCE_H_INCLUDED */ + +/* EOF */ diff --git a/reactos/subsys/csrss/win32csr/win32csr.rc b/reactos/subsys/csrss/win32csr/win32csr.rc index aab4057983a..aabf87bbe53 100644 --- a/reactos/subsys/csrss/win32csr/win32csr.rc +++ b/reactos/subsys/csrss/win32csr/win32csr.rc @@ -1,3 +1,6 @@ +#include +#include "resource.h" + #define REACTOS_VERSION_DLL #define REACTOS_STR_FILE_DESCRIPTION "CSRSS subsystem usermode code\0" #define REACTOS_STR_INTERNAL_NAME "win32csr\0" @@ -5,3 +8,32 @@ #include 1 ICON DISCARDABLE res/terminal.ico + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +IDD_END_NOW DIALOG DISCARDABLE 0, 0, 200, 95 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "End Program - " +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Ending Program... Please wait",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "If you choose to end the program immediately, you will lose any unsaved data. To end the program now, click End Now.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&End Now",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOG DISCARDABLE 0, 0, 192, 122 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "End Program - " +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Cancel",IDCANCEL,142,98,43,17 + PUSHBUTTON "&End Now",IDC_END_NOW,78,98,43,17 + LTEXT "This program is not responding",IDC_STATIC,7,7,178,8 + LTEXT "To return to ReactOS and check the status of the program, click Cancel", + IDC_STATIC,7,26,178,16 + LTEXT "If you choose to end the program immediately, you will loose any unsaved data. To end the program now, click End Now", + IDC_STATIC,7,53,178,26 +END diff --git a/reactos/subsys/csrss/win32csr/win32csr.xml b/reactos/subsys/csrss/win32csr/win32csr.xml index bd24f5bf998..f7c19a14728 100644 --- a/reactos/subsys/csrss/win32csr/win32csr.xml +++ b/reactos/subsys/csrss/win32csr/win32csr.xml @@ -4,6 +4,7 @@ include + 0x0500 ntdll kernel32 user32 diff --git a/reactos/subsys/system/winlogon/sas.c b/reactos/subsys/system/winlogon/sas.c index 24dedb5a69f..7c3b64645fa 100644 --- a/reactos/subsys/system/winlogon/sas.c +++ b/reactos/subsys/system/winlogon/sas.c @@ -14,6 +14,9 @@ #define NDEBUG #include +#define WINLOGON_SAS_CLASS L"SAS window class" +#define WINLOGON_SAS_TITLE L"SAS" + #define HK_CTRL_ALT_DEL 0 #define HK_CTRL_SHIFT_ESC 1 @@ -72,15 +75,50 @@ DestroySAS(PWLSESSION Session, HWND hwndSAS) return TRUE; } -#define EWX_ACTION_MASK 0x0b +#define EWX_ACTION_MASK 0xffffffeb +#define EWX_FLAGS_MASK 0x00000014 + +typedef struct tagLOGOFF_SHUTDOWN_DATA +{ + UINT Flags; + PWLSESSION Session; +} LOGOFF_SHUTDOWN_DATA, *PLOGOFF_SHUTDOWN_DATA; + +static DWORD WINAPI +LogoffShutdownThread(LPVOID Parameter) +{ + PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA) Parameter; + + if (! ImpersonateLoggedOnUser(LSData->Session->UserToken)) + { + DPRINT1("ImpersonateLoggedOnUser failed with error %d\n", GetLastError()); + return 0; + } + if (! ExitWindowsEx(EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) + | (EWX_LOGOFF == (LSData->Flags & EWX_ACTION_MASK) ? EWX_INTERNAL_FLAG_LOGOFF : 0), + 0)) + { + DPRINT1("Unable to kill user apps, error %d\n", GetLastError()); + RevertToSelf(); + return 0; + } + RevertToSelf(); + + HeapFree(GetProcessHeap(), 0, LSData); + + return 1; +} + static LRESULT -HandleExitWindows(DWORD RequestingProcessId, UINT Flags) +HandleExitWindows(PWLSESSION Session, DWORD RequestingProcessId, UINT Flags) { UINT Action; HANDLE Process; HANDLE Token; + HANDLE Thread; BOOL CheckResult; PPRIVILEGE_SET PrivSet; + PLOGOFF_SHUTDOWN_DATA LSData; /* Check parameters */ Action = Flags & EWX_ACTION_MASK; @@ -139,7 +177,22 @@ HandleExitWindows(DWORD RequestingProcessId, UINT Flags) } } - /* FIXME actually start logoff/shutdown now */ + LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA)); + if (NULL == LSData) + { + DPRINT1("Failed to allocate mem for thread data\n"); + return STATUS_NO_MEMORY; + } + LSData->Flags = Flags; + LSData->Session = Session; + Thread = CreateThread(NULL, 0, LogoffShutdownThread, (LPVOID) LSData, 0, NULL); + if (NULL == Thread) + { + DPRINT1("Unable to create shutdown thread, error %d\n", GetLastError()); + HeapFree(GetProcessHeap(), 0, LSData); + return STATUS_UNSUCCESSFUL; + } + CloseHandle(Thread); return 1; } @@ -178,7 +231,7 @@ SASProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } case PM_WINLOGON_EXITWINDOWS: { - return HandleExitWindows((DWORD) wParam, (UINT) lParam); + return HandleExitWindows(Session, (DWORD) wParam, (UINT) lParam); } case WM_DESTROY: { diff --git a/reactos/subsys/system/winlogon/winlogon.c b/reactos/subsys/system/winlogon/winlogon.c index 2cb92bdde5f..fb541a9ab44 100644 --- a/reactos/subsys/system/winlogon/winlogon.c +++ b/reactos/subsys/system/winlogon/winlogon.c @@ -405,7 +405,6 @@ DoLogonUser (PWCHAR Name, STARTUPINFO StartupInfo; WCHAR CommandLine[MAX_PATH]; WCHAR CurrentDirectory[MAX_PATH]; - HANDLE hToken; PROFILEINFOW ProfileInfo; BOOL Result; LPVOID lpEnvironment = NULL; @@ -416,7 +415,7 @@ DoLogonUser (PWCHAR Name, Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, - &hToken); + &WLSession->UserToken); if (!Result) { DbgPrint ("WL: LogonUserW() failed\n"); @@ -434,24 +433,24 @@ DoLogonUser (PWCHAR Name, ProfileInfo.lpPolicyPath = NULL; ProfileInfo.hProfile = NULL; - if (!LoadUserProfileW (hToken, + if (!LoadUserProfileW (WLSession->UserToken, &ProfileInfo)) { DbgPrint ("WL: LoadUserProfileW() failed\n"); - CloseHandle (hToken); + CloseHandle (WLSession->UserToken); RtlDestroyEnvironment (lpEnvironment); return FALSE; } if (!CreateEnvironmentBlock (&lpEnvironment, - hToken, + WLSession->UserToken, TRUE)) { DbgPrint ("WL: CreateEnvironmentBlock() failed\n"); return FALSE; } - if (ImpersonateLoggedOnUser(hToken)) + if (ImpersonateLoggedOnUser(WLSession->UserToken)) { UpdatePerUserSystemParameters(0, TRUE); RevertToSelf(); @@ -467,7 +466,7 @@ DoLogonUser (PWCHAR Name, StartupInfo.cbReserved2 = 0; StartupInfo.lpReserved2 = 0; - Result = CreateProcessAsUserW (hToken, + Result = CreateProcessAsUserW (WLSession->UserToken, NULL, GetUserInit (CommandLine), NULL, @@ -481,14 +480,14 @@ DoLogonUser (PWCHAR Name, if (!Result) { DbgPrint ("WL: Failed to execute user shell %s\n", CommandLine); - if (ImpersonateLoggedOnUser(hToken)) + if (ImpersonateLoggedOnUser(WLSession->UserToken)) { UpdatePerUserSystemParameters(0, FALSE); RevertToSelf(); } - UnloadUserProfile (hToken, + UnloadUserProfile (WLSession->UserToken, ProfileInfo.hProfile); - CloseHandle (hToken); + CloseHandle (WLSession->UserToken); DestroyEnvironmentBlock (lpEnvironment); return FALSE; } @@ -511,17 +510,17 @@ DoLogonUser (PWCHAR Name, CloseHandle (ProcessInformation.hProcess); CloseHandle (ProcessInformation.hThread); - if (ImpersonateLoggedOnUser(hToken)) + if (ImpersonateLoggedOnUser(WLSession->UserToken)) { UpdatePerUserSystemParameters(0, FALSE); RevertToSelf(); } /* Unload user profile */ - UnloadUserProfile (hToken, + UnloadUserProfile (WLSession->UserToken, ProfileInfo.hProfile); - CloseHandle (hToken); + CloseHandle (WLSession->UserToken); RtlDestroyEnvironment (lpEnvironment); diff --git a/reactos/subsys/system/winlogon/winlogon.h b/reactos/subsys/system/winlogon/winlogon.h index 9413761b9f2..d8422cf520f 100644 --- a/reactos/subsys/system/winlogon/winlogon.h +++ b/reactos/subsys/system/winlogon/winlogon.h @@ -140,6 +140,7 @@ typedef struct _WLSESSION HDESK WinlogonDesktop; HDESK ScreenSaverDesktop; LUID LogonId; + HANDLE UserToken; } WLSESSION, *PWLSESSION; extern HINSTANCE hAppInstance; diff --git a/reactos/subsys/system/winlogon/wlx.c b/reactos/subsys/system/winlogon/wlx.c index 6d8df8fec71..f9961657c54 100644 --- a/reactos/subsys/system/winlogon/wlx.c +++ b/reactos/subsys/system/winlogon/wlx.c @@ -18,6 +18,8 @@ #define Unimplemented DbgPrint("WL: %S() at %S:%i unimplemented!\n", __FUNCTION__, __FILE__, __LINE__) +#define WINLOGON_DESKTOP L"Winlogon" + /* * @implemented */