1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS user32.dll 4 * FILE: win32ss/user/user32/misc/exit.c 5 * PURPOSE: Shutdown related functions 6 * PROGRAMMER: Eric Kohl 7 */ 8 9 #include <user32.h> 10 11 /* 12 * Sequence of events: 13 * 14 * - App (usually explorer) calls ExitWindowsEx() 15 * - ExitWindowsEx() sends a message to CSRSS 16 * - CSRSS impersonates the caller and sends a message to a hidden Winlogon window 17 * - Winlogon checks if the caller has the required privileges 18 * - Winlogon enters pending log-out state 19 * - Winlogon impersonates the interactive user and calls ExitWindowsEx() again, 20 * passing some special internal flags 21 * - CSRSS loops over all processes of the interactive user (sorted by their 22 * SetProcessShutdownParameters() level), sending WM_QUERYENDSESSION and 23 * WM_ENDSESSION messages to its top-level windows. If the messages aren't 24 * processed within the timeout period (registry key HKCU\Control Panel\Desktop\HungAppTimeout) 25 * CSRSS will put up a dialog box asking if the process should be terminated. 26 * Using the registry key HKCU\Control Panel\Desktop\AutoEndTask you can 27 * specify that the dialog box shouldn't be shown and CSRSS should just 28 * terminate the thread. If the the WM_ENDSESSION message is processed 29 * but the thread doesn't terminate within the timeout specified by 30 * HKCU\Control Panel\Desktop\WaitToKillAppTimeout CSRSS will terminate 31 * the thread. When all the top-level windows have been destroyed CSRSS 32 * will terminate the process. 33 * If the process is a console process, CSRSS will send a CTRL_LOGOFF_EVENT 34 * to the console control handler on logoff. No event is sent on shutdown. 35 * If the handler doesn't respond in time the same activities as for GUI 36 * apps (i.e. display dialog box etc) take place. This also happens if 37 * the handler returns TRUE. 38 * - This ends the processing for the first ExitWindowsEx() call from Winlogon. 39 * Execution continues in Winlogon, which calls ExitWindowsEx() again to 40 * terminate COM processes in the interactive user's session. 41 * - Winlogon stops impersonating the interactive user (whose processes are 42 * all dead by now). and enters log-out state 43 * - If the ExitWindowsEx() request was for a logoff, Winlogon sends a SAS 44 * event (to display the "press ctrl+alt+del") to the GINA. Winlogon then 45 * waits for the GINA to send a SAS event to login. 46 * - If the ExitWindowsEx() request was for shutdown/restart, Winlogon calls 47 * ExitWindowsEx() again in the system process context. 48 * - CSRSS goes through the motions of sending WM_QUERYENDSESSION/WM_ENDSESSION 49 * to GUI processes running in the system process context but won't display 50 * dialog boxes or kill threads/processes. Same for console processes, 51 * using the CTRL_SHUTDOWN_EVENT. The Service Control Manager is one of 52 * these console processes and has a special timeout value WaitToKillServiceTimeout. 53 * - After CSRSS has finished its pass notifying processes that system is shutting down, 54 * Winlogon finishes the shutdown process by calling the executive subsystem 55 * function NtShutdownSystem. 56 */ 57 58 typedef struct 59 { 60 UINT uFlags; 61 DWORD dwReserved; 62 } EXIT_REACTOS_DATA, *PEXIT_REACTOS_DATA; 63 64 static BOOL 65 ExitWindowsWorker(UINT uFlags, 66 DWORD dwReserved, 67 BOOL bCalledFromThread); 68 69 static DWORD 70 WINAPI 71 ExitWindowsThread(LPVOID Param) 72 { 73 DWORD dwExitCode; 74 PEXIT_REACTOS_DATA ExitData = (PEXIT_REACTOS_DATA)Param; 75 76 /* Do the exit asynchronously */ 77 if (ExitWindowsWorker(ExitData->uFlags, ExitData->dwReserved, TRUE)) 78 dwExitCode = ERROR_SUCCESS; 79 else 80 dwExitCode = GetLastError(); 81 82 ExitThread(dwExitCode); 83 return ERROR_SUCCESS; 84 } 85 86 static BOOL 87 ExitWindowsWorker(UINT uFlags, 88 DWORD dwReserved, 89 BOOL bCalledFromThread) 90 { 91 EXIT_REACTOS_DATA ExitData; 92 HANDLE hExitThread; 93 DWORD ExitCode; 94 MSG msg; 95 96 USER_API_MESSAGE ApiMessage; 97 PUSER_EXIT_REACTOS ExitReactOSRequest = &ApiMessage.Data.ExitReactOSRequest; 98 99 /* 100 * 1- FIXME: Call NtUserCallOneParam(uFlags, ONEPARAM_ROUTINE_PREPAREFORLOGOFF); 101 * If success we can continue, otherwise we must fail. 102 */ 103 104 /* 105 * 2- Send the Exit request to CSRSS (and to Win32k indirectly). 106 * We can shutdown synchronously or asynchronously. 107 */ 108 109 // ExitReactOSRequest->LastError = ERROR_SUCCESS; 110 ExitReactOSRequest->Flags = uFlags; 111 112 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, 113 NULL, 114 CSR_CREATE_API_NUMBER(USERSRV_SERVERDLL_INDEX, UserpExitWindowsEx), 115 sizeof(*ExitReactOSRequest)); 116 117 /* Set the last error accordingly */ 118 if (NT_SUCCESS(ApiMessage.Status) || ApiMessage.Status == STATUS_CANT_WAIT) 119 { 120 if (ExitReactOSRequest->LastError != ERROR_SUCCESS) 121 UserSetLastError(ExitReactOSRequest->LastError); 122 } 123 else 124 { 125 UserSetLastNTError(ApiMessage.Status); 126 ExitReactOSRequest->Success = FALSE; 127 } 128 129 /* 130 * In case CSR call succeeded and we did a synchronous exit 131 * (STATUS_CANT_WAIT is considered as a non-success status), 132 * return the real state of the operation now. 133 */ 134 if (NT_SUCCESS(ApiMessage.Status)) 135 return ExitReactOSRequest->Success; 136 137 /* 138 * In case something failed: we have a non-success status and: 139 * - either we were doing a synchronous exit (Status != STATUS_CANT_WAIT), or 140 * - we were doing an asynchronous exit because we were called recursively via 141 * another thread but we failed to exit, 142 * then bail out immediately, otherwise we would enter an infinite loop of exit requests. 143 * 144 * On the contrary if we need to do an asynchronous exit (Status == STATUS_CANT_WAIT 145 * and not called recursively via another thread), then continue and do the exit. 146 */ 147 if (ApiMessage.Status != STATUS_CANT_WAIT || bCalledFromThread) 148 { 149 UserSetLastNTError(ApiMessage.Status); 150 return FALSE; 151 } 152 153 /* 154 * 3- Win32k wants us to perform an asynchronous exit. Run the request in a thread. 155 * (ApiMessage.Status == STATUS_CANT_WAIT and not already called from a thread) 156 */ 157 ExitData.uFlags = uFlags; 158 ExitData.dwReserved = dwReserved; 159 hExitThread = CreateThread(NULL, 0, ExitWindowsThread, &ExitData, 0, NULL); 160 if (hExitThread == NULL) 161 return FALSE; 162 163 /* Pump and discard any input events sent to the app(s) */ 164 while (MsgWaitForMultipleObjectsEx(1, &hExitThread, INFINITE, QS_ALLINPUT, 0)) 165 { 166 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 167 DispatchMessageW(&msg); 168 } 169 170 /* Finally, return to caller */ 171 if (!GetExitCodeThread(hExitThread, &ExitCode)) 172 ExitCode = GetLastError(); 173 174 CloseHandle(hExitThread); 175 176 if (ExitCode != ERROR_SUCCESS) 177 UserSetLastError(ExitCode); 178 179 return (ExitCode == ERROR_SUCCESS); 180 } 181 182 /* 183 * @implemented 184 */ 185 BOOL WINAPI 186 ExitWindowsEx(UINT uFlags, 187 DWORD dwReserved) 188 { 189 /* 190 * FIXME: 191 * 1- Calling the Exit worker must be done under certain conditions. 192 * We may also need to warn the user if there are other people logged 193 * on this computer (see http://pve.proxmox.com/wiki/Windows_2003_guest_best_practices ) 194 * 2- Call SrvRecordShutdownReason. 195 */ 196 197 return ExitWindowsWorker(uFlags, dwReserved, FALSE); 198 199 /* FIXME: Call SrvRecordShutdownReason if we failed */ 200 } 201 202 /* 203 * @implemented 204 */ 205 BOOL 206 WINAPI 207 EndTask(HWND hWnd, 208 BOOL fShutDown, 209 BOOL fForce) 210 { 211 USER_API_MESSAGE ApiMessage; 212 PUSER_END_TASK EndTaskRequest = &ApiMessage.Data.EndTaskRequest; 213 214 UNREFERENCED_PARAMETER(fShutDown); 215 216 // EndTaskRequest->LastError = ERROR_SUCCESS; 217 EndTaskRequest->WndHandle = hWnd; 218 EndTaskRequest->Force = fForce; 219 220 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, 221 NULL, 222 CSR_CREATE_API_NUMBER(USERSRV_SERVERDLL_INDEX, UserpEndTask), 223 sizeof(*EndTaskRequest)); 224 if (!NT_SUCCESS(ApiMessage.Status)) 225 { 226 UserSetLastNTError(ApiMessage.Status); 227 return FALSE; 228 } 229 230 if (EndTaskRequest->LastError != ERROR_SUCCESS) 231 UserSetLastError(EndTaskRequest->LastError); 232 233 return EndTaskRequest->Success; 234 } 235 236 /* EOF */ 237