diff --git a/reactos/base/system/winlogon/sas.c b/reactos/base/system/winlogon/sas.c index de1f2345bdb..e908c5a5c97 100644 --- a/reactos/base/system/winlogon/sas.c +++ b/reactos/base/system/winlogon/sas.c @@ -38,6 +38,8 @@ typedef struct tagLOGOFF_SHUTDOWN_DATA PWLSESSION Session; } LOGOFF_SHUTDOWN_DATA, *PLOGOFF_SHUTDOWN_DATA; +static BOOL ExitReactOSInProgress = FALSE; + /* FUNCTIONS ****************************************************************/ static BOOL @@ -499,25 +501,33 @@ LogoffShutdownThread( LPVOID Parameter) { PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA)Parameter; + UINT uFlags; - if (LSData->Session->UserToken != NULL && !ImpersonateLoggedOnUser(LSData->Session->UserToken)) + if (LSData->Session->UserToken != NULL && + !ImpersonateLoggedOnUser(LSData->Session->UserToken)) { ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError()); return 0; } + uFlags = EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) | + ((LSData->Flags & EWX_ACTION_MASK) == EWX_LOGOFF ? EWX_INTERNAL_FLAG_LOGOFF : 0); + + ERR("In LogoffShutdownThread with uFlags == 0x%x; exit_in_progress == %s\n", + uFlags, ExitReactOSInProgress ? "true" : "false"); + + ExitReactOSInProgress = TRUE; + /* Close processes of the interactive user */ - 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)) + if (!ExitWindowsEx(uFlags, 0)) { ERR("Unable to kill user apps, error %lu\n", GetLastError()); RevertToSelf(); return 0; } - /* FIXME: Call ExitWindowsEx() to terminate COM processes */ + /* FIXME: Call ExitWindowsEx() to terminate COM processes only at logoff! */ + ERR("Should terminate COM processes only at logoff!\n"); if (LSData->Session->UserToken) RevertToSelf(); @@ -687,7 +697,7 @@ HandleLogoff( /* Run logoff thread */ hThread = CreateThread(psa, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL); - /* we're done with the SECURITY_DESCRIPTOR */ + /* We're done with the SECURITY_DESCRIPTOR */ DestroyLogoffSecurityAttributes(psa); psa = NULL; @@ -1317,10 +1327,38 @@ SASWindowProc( } } + ERR("In LN_LOGOFF, exit_in_progress == %s\n", + ExitReactOSInProgress ? "true" : "false"); + + /* + * In case a parallel shutdown request is done (while we are + * being to shut down) and it was not done by Winlogon itself, + * then just stop here. + */ +#if 0 +// This code is commented at the moment (even if it's correct) because +// our log-offs do not really work: the shell is restarted, no app is killed +// etc... and as a result you just get explorer opening "My Documents". And +// if you try now a shut down, it won't work because winlogon thinks it is +// still in the middle of a shutdown. +// Maybe we also need to reset ExitReactOSInProgress somewhere else?? + if (ExitReactOSInProgress && (lParam & EWX_INTERNAL_FLAG) == 0) + { + break; + } +#endif /* Now do the shutdown action proper */ DoGenericAction(Session, wlxAction); return 1; } + case LN_LOGOFF_CANCELED: + { + ERR("Logoff canceled!!, before: exit_in_progress == %s, after will be false\n", + ExitReactOSInProgress ? "true" : "false"); + + ExitReactOSInProgress = FALSE; + return 1; + } default: { ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam); diff --git a/reactos/win32ss/user/ntuser/shutdown.c b/reactos/win32ss/user/ntuser/shutdown.c index ffccb0bd3df..5451c22f3a3 100644 --- a/reactos/win32ss/user/ntuser/shutdown.c +++ b/reactos/win32ss/user/ntuser/shutdown.c @@ -228,11 +228,13 @@ UserInitiateShutdown(IN PETHREAD Thread, if (PsGetThreadProcessId(Thread) != gpidLogon) { // FIXME: HACK!! Do more checks!! + ERR("UserInitiateShutdown -- Notify Winlogon for shutdown\n"); UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_LOGOFF, (LPARAM)Flags); return STATUS_PENDING; } // If we reach this point, that means it's Winlogon that triggered the shutdown. + ERR("UserInitiateShutdown -- Winlogon shuts down\n"); /* * FIXME: diff --git a/reactos/win32ss/user/winsrv/usersrv/shutdown.c b/reactos/win32ss/user/winsrv/usersrv/shutdown.c index 3481d4e7901..eb0cd5fb23a 100644 --- a/reactos/win32ss/user/winsrv/usersrv/shutdown.c +++ b/reactos/win32ss/user/winsrv/usersrv/shutdown.c @@ -758,7 +758,7 @@ LoadShutdownSettings(PSID Sid, PSHUTDOWN_SETTINGS ShutdownSettings) RegCloseKey(DesktopKey); } -static NTSTATUS FASTCALL +NTSTATUS FASTCALL InternalExitReactos(DWORD ProcessId, DWORD ThreadId, UINT Flags) { HANDLE CallerThread; @@ -772,7 +772,7 @@ InternalExitReactos(DWORD ProcessId, DWORD ThreadId, UINT Flags) TOKEN_USER *UserInfo; SHUTDOWN_SETTINGS ShutdownSettings; - if (ProcessId != (DWORD_PTR) LogonProcessId) + if (ProcessId != LogonProcessId) { DPRINT1("Internal ExitWindowsEx call not from winlogon\n"); return STATUS_ACCESS_DENIED; @@ -902,11 +902,17 @@ UserExitReactos(PCSR_THREAD CsrThread, UINT Flags) NTSTATUS Status; LUID CallerLuid; - // FIXME: HACK!! + /* + * Check for flags validity + */ + + /* Implicitely add the shutdown flag when we poweroff or reboot */ + if (Flags & (EWX_POWEROFF | EWX_REBOOT)) + Flags |= EWX_SHUTDOWN; /* - * Retrieve the caller's LUID so that we can only shutdown - * processes in the caller's LUID. + * Impersonate and retrieve the caller's LUID so that + * we can only shutdown processes in its context. */ if (!CsrImpersonateClient(NULL)) return STATUS_BAD_IMPERSONATION_LEVEL; @@ -920,27 +926,84 @@ UserExitReactos(PCSR_THREAD CsrThread, UINT Flags) DPRINT1("Caller LUID is: %lx.%lx\n", CallerLuid.HighPart, CallerLuid.LowPart); - /* Notify Win32k and potentially Winlogon of the shutdown */ - Status = NtUserSetInformationThread(CsrThread->ThreadHandle, - UserThreadInitiateShutdown, - &Flags, sizeof(Flags)); - DPRINT1("Win32k says: %lx\n", Status); + /* Shutdown loop */ + while (TRUE) + { + /* Notify Win32k and potentially Winlogon of the shutdown */ + Status = NtUserSetInformationThread(CsrThread->ThreadHandle, + UserThreadInitiateShutdown, + &Flags, sizeof(Flags)); + DPRINT1("Win32k says: %lx\n", Status); + switch (Status) + { + /* We cannot wait here, the caller should start a new thread */ + case STATUS_CANT_WAIT: + DPRINT1("STATUS_CANT_WAIT\n"); + goto Quit; - /* 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) - { - Status = STATUS_SUCCESS; + /* Shutdown is in progress */ + case STATUS_PENDING: + DPRINT1("STATUS_PENDING\n"); + goto Quit; + + /* Abort */ + case STATUS_RETRY: + { + DPRINT1("STATUS_RETRY\n"); + UNIMPLEMENTED; + continue; + } + + default: + { + if (!NT_SUCCESS(Status)) + { + // FIXME: Use some UserSetLastNTError or SetLastNtError + // that we have defined for user32 or win32k usage only... + SetLastError(RtlNtStatusToDosError(Status)); + goto Quit; + } + } + } + + /* All good */ + break; } - else if (0 == Status) + + /* + * OK we can continue. Now magic happens: + * + * Terminate all Win32 processes, stop if we find one kicking + * and screaming it doesn't want to die. + * + * This function calls the ShutdownProcessCallback callback of + * each CSR server for each Win32 process. + */ + Status = CsrShutdownProcesses(&CallerLuid, Flags); + if (!NT_SUCCESS(Status)) { - Status = STATUS_NOT_IMPLEMENTED; + 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 + + /* Tell Win32k and potentially Winlogon that we're done */ + NtUserSetInformationThread(CsrThread->ThreadHandle, + UserThreadEndShutdown, + &Status, sizeof(Status)); DPRINT1("SrvExitWindowsEx returned 0x%08x\n", Status); Quit: + /* We are done */ CsrRevertToSelf(); return Status; } @@ -970,33 +1033,35 @@ CSR_API(SrvExitWindowsEx) PCSR_THREAD CsrThread = CsrGetClientThread(); ULONG Flags = ExitReactosRequest->Flags; - /* - * Check for flags validity - */ - DWORD ProcessId = HandleToUlong(CsrThread->ClientId.UniqueProcess); DWORD ThreadId = HandleToUlong(CsrThread->ClientId.UniqueThread); DPRINT1("SrvExitWindowsEx(ClientId: %lx.%lx, Flags: 0x%x)\n", ProcessId, ThreadId, Flags); - /* Implicitely add the shutdown flag when we poweroff or reboot */ - if (Flags & (EWX_POWEROFF | EWX_REBOOT)) - Flags |= EWX_SHUTDOWN; - // FIXME: Everything else starting after this line is a HAAACK!! - if (0 == (Flags & EWX_INTERNAL_FLAG)) + 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 { - // EWX_INTERNAL_FLAG --> For Winlogon only!! - // FIXME: This is just a big HAAAACK!! - Status = InternalExitReactos(ProcessId, ThreadId, Flags); + Status = UserExitReactos(CsrThread, Flags); } ExitReactosRequest->Success = NT_SUCCESS(Status);