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
ExitWindowsThread(LPVOID Param)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
ExitWindowsWorker(UINT uFlags,DWORD dwReserved,BOOL bCalledFromThread)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
ExitWindowsEx(UINT uFlags,DWORD dwReserved)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
EndTask(HWND hWnd,BOOL fShutDown,BOOL fForce)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