[FORMATTING] Remove trailing whitespace. Addendum to 34593d93.
[reactos.git] / win32ss / user / winsrv / usersrv / shutdown.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS User API Server DLL
4 * FILE: win32ss/user/winsrv/usersrv/shutdown.c
5 * PURPOSE: Logout/shutdown
6 * PROGRAMMERS:
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "usersrv.h"
12
13 #include <commctrl.h>
14 #include <psapi.h>
15
16 #include "resource.h"
17
18 #define NDEBUG
19 #include <debug.h>
20
21 /* GLOBALS ********************************************************************/
22
23 // Those flags (that are used for CsrProcess->ShutdownFlags) are named
24 // in accordance to the only public one: SHUTDOWN_NORETRY used for the
25 // SetProcessShutdownParameters API.
26 #if !defined(SHUTDOWN_SYSTEMCONTEXT) && !defined(SHUTDOWN_OTHERCONTEXT)
27 #define SHUTDOWN_SYSTEMCONTEXT CsrShutdownSystem
28 #define SHUTDOWN_OTHERCONTEXT CsrShutdownOther
29 #endif
30
31 // The DPRINTs that need to really be removed as soon as everything works.
32 #define MY_DPRINT DPRINT1
33 #define MY_DPRINT2 DPRINT
34
35 typedef struct tagNOTIFY_CONTEXT
36 {
37 UINT Msg;
38 WPARAM wParam;
39 LPARAM lParam;
40 // HDESK Desktop;
41 // HDESK OldDesktop;
42 DWORD StartTime;
43 DWORD QueryResult;
44 HWND Dlg;
45 DWORD EndNowResult;
46 BOOL ShowUI;
47 HANDLE UIThread;
48 HWND WndClient;
49 PSHUTDOWN_SETTINGS ShutdownSettings;
50 } NOTIFY_CONTEXT, *PNOTIFY_CONTEXT;
51
52 #define QUERY_RESULT_ABORT 0
53 #define QUERY_RESULT_CONTINUE 1
54 #define QUERY_RESULT_TIMEOUT 2
55 #define QUERY_RESULT_ERROR 3
56 #define QUERY_RESULT_FORCE 4
57
58 typedef void (WINAPI *INITCOMMONCONTROLS_PROC)(void);
59
60 typedef struct tagMESSAGE_CONTEXT
61 {
62 HWND Wnd;
63 UINT Msg;
64 WPARAM wParam;
65 LPARAM lParam;
66 DWORD Timeout;
67 } MESSAGE_CONTEXT, *PMESSAGE_CONTEXT;
68
69
70 /* FUNCTIONS ******************************************************************/
71
72 static HMODULE hComCtl32Lib = NULL;
73
74 static VOID
75 CallInitCommonControls(VOID)
76 {
77 static BOOL Initialized = FALSE;
78 INITCOMMONCONTROLS_PROC InitProc;
79
80 if (Initialized) return;
81
82 hComCtl32Lib = LoadLibraryW(L"COMCTL32.DLL");
83 if (hComCtl32Lib == NULL) return;
84
85 InitProc = (INITCOMMONCONTROLS_PROC)GetProcAddress(hComCtl32Lib, "InitCommonControls");
86 if (InitProc == NULL) return;
87
88 (*InitProc)();
89
90 Initialized = TRUE;
91 }
92
93 static VOID FASTCALL
94 UpdateProgressBar(HWND ProgressBar, PNOTIFY_CONTEXT NotifyContext)
95 {
96 DWORD Passed;
97
98 Passed = GetTickCount() - NotifyContext->StartTime;
99 Passed -= NotifyContext->ShutdownSettings->HungAppTimeout;
100 if (NotifyContext->ShutdownSettings->WaitToKillAppTimeout < Passed)
101 {
102 Passed = NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
103 }
104 SendMessageW(ProgressBar, PBM_SETPOS, Passed / 2, 0);
105 }
106
107 static INT_PTR CALLBACK
108 EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam)
109 {
110 INT_PTR Result;
111 PNOTIFY_CONTEXT NotifyContext;
112 HWND ProgressBar;
113 DWORD TitleLength;
114 int Len;
115 LPWSTR Title;
116
117 switch(Msg)
118 {
119 case WM_INITDIALOG:
120 NotifyContext = (PNOTIFY_CONTEXT)lParam;
121 NotifyContext->EndNowResult = QUERY_RESULT_ABORT;
122 SetWindowLongPtrW(Dlg, DWLP_USER, (LONG_PTR)lParam);
123 TitleLength = SendMessageW(NotifyContext->WndClient, WM_GETTEXTLENGTH,
124 0, 0) +
125 GetWindowTextLengthW(Dlg);
126 Title = HeapAlloc(UserServerHeap, 0, (TitleLength + 1) * sizeof(WCHAR));
127 if (Title)
128 {
129 Len = GetWindowTextW(Dlg, Title, TitleLength + 1);
130 SendMessageW(NotifyContext->WndClient, WM_GETTEXT,
131 TitleLength + 1 - Len, (LPARAM)(Title + Len));
132 SetWindowTextW(Dlg, Title);
133 HeapFree(UserServerHeap, 0, Title);
134 }
135 ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS);
136 SendMessageW(ProgressBar, PBM_SETRANGE32, 0,
137 NotifyContext->ShutdownSettings->WaitToKillAppTimeout / 2);
138 UpdateProgressBar(ProgressBar, NotifyContext);
139 SetTimer(Dlg, 0, 200, NULL);
140 Result = FALSE;
141 break;
142
143 case WM_TIMER:
144 NotifyContext = (PNOTIFY_CONTEXT)GetWindowLongPtrW(Dlg, DWLP_USER);
145 ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS);
146 UpdateProgressBar(ProgressBar, NotifyContext);
147 Result = TRUE;
148 break;
149
150 case WM_COMMAND:
151 if (BN_CLICKED == HIWORD(wParam) && IDC_END_NOW == LOWORD(wParam))
152 {
153 NotifyContext = (PNOTIFY_CONTEXT)GetWindowLongPtrW(Dlg, DWLP_USER);
154 NotifyContext->EndNowResult = QUERY_RESULT_FORCE;
155 MY_DPRINT("Closing progress dlg by hand\n");
156 SendMessageW(Dlg, WM_CLOSE, 0, 0);
157 Result = TRUE;
158 }
159 else
160 {
161 Result = FALSE;
162 }
163 break;
164
165 case WM_CLOSE:
166 MY_DPRINT("WM_CLOSE\n");
167 DestroyWindow(Dlg);
168 Result = TRUE;
169 break;
170
171 case WM_DESTROY:
172 MY_DPRINT("WM_DESTROY\n");
173 NotifyContext = (PNOTIFY_CONTEXT)GetWindowLongPtrW(Dlg, DWLP_USER);
174 NotifyContext->Dlg = NULL;
175 KillTimer(Dlg, 0);
176 PostQuitMessage(NotifyContext->EndNowResult);
177 Result = TRUE;
178 break;
179
180 default:
181 Result = FALSE;
182 break;
183 }
184
185 return Result;
186 }
187
188 static DWORD WINAPI
189 EndNowThreadProc(LPVOID Parameter)
190 {
191 PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT)Parameter;
192 MSG Msg;
193
194 #if 0
195 SetThreadDesktop(NotifyContext->Desktop);
196 SwitchDesktop(NotifyContext->Desktop);
197 #else
198 /* For now show the end task dialog in the active desktop */
199 NtUserSetInformationThread(NtCurrentThread(),
200 UserThreadUseActiveDesktop,
201 NULL,
202 0);
203 #endif
204
205 CallInitCommonControls();
206 NotifyContext->Dlg = CreateDialogParam(UserServerDllInstance,
207 MAKEINTRESOURCE(IDD_END_NOW), NULL,
208 EndNowDlgProc, (LPARAM)NotifyContext);
209 if (NotifyContext->Dlg == NULL)
210 return 0;
211
212 ShowWindow(NotifyContext->Dlg, SW_SHOWNORMAL);
213
214 while (GetMessageW(&Msg, NULL, 0, 0))
215 {
216 if (!IsDialogMessage(NotifyContext->Dlg, &Msg))
217 {
218 TranslateMessage(&Msg);
219 DispatchMessageW(&Msg);
220 }
221 }
222
223 return Msg.wParam;
224 }
225
226 static DWORD WINAPI
227 SendClientShutdown(LPVOID Parameter)
228 {
229 PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT)Parameter;
230 DWORD_PTR Result;
231
232 /* If the shutdown is aborted, just notify the process, there is no need to wait */
233 if ((Context->wParam & (MCS_QUERYENDSESSION | MCS_ENDSESSION)) == 0)
234 {
235 DPRINT("Called WM_CLIENTSHUTDOWN with wParam == 0 ...\n");
236 SendNotifyMessageW(Context->Wnd, WM_CLIENTSHUTDOWN,
237 Context->wParam, Context->lParam);
238 return QUERY_RESULT_CONTINUE;
239 }
240
241 if (SendMessageTimeoutW(Context->Wnd, WM_CLIENTSHUTDOWN,
242 Context->wParam, Context->lParam,
243 SMTO_NORMAL, Context->Timeout, &Result))
244 {
245 DWORD Ret;
246
247 if (Context->wParam & MCS_QUERYENDSESSION)
248 {
249 /* WM_QUERYENDSESSION case */
250 switch (Result)
251 {
252 case MCSR_DONOTSHUTDOWN:
253 Ret = QUERY_RESULT_ABORT;
254 break;
255
256 case MCSR_GOODFORSHUTDOWN:
257 case MCSR_SHUTDOWNFINISHED:
258 default:
259 Ret = QUERY_RESULT_CONTINUE;
260 }
261 }
262 else
263 {
264 /* WM_ENDSESSION case */
265 Ret = QUERY_RESULT_CONTINUE;
266 }
267
268 DPRINT("SendClientShutdown -- Return == %s\n",
269 Ret == QUERY_RESULT_CONTINUE ? "Continue" : "Abort");
270 return Ret;
271 }
272
273 DPRINT1("SendClientShutdown -- Error == %s\n",
274 GetLastError() == 0 ? "Timeout" : "error");
275
276 return (GetLastError() == 0 ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR);
277 }
278
279 static BOOL
280 NotifyTopLevelWindow(HWND Wnd, PNOTIFY_CONTEXT NotifyContext)
281 {
282 MESSAGE_CONTEXT MessageContext;
283 DWORD Now, Passed;
284 DWORD Timeout, WaitStatus;
285 HANDLE MessageThread;
286 HANDLE Threads[2];
287
288 SetForegroundWindow(Wnd);
289
290 Now = GetTickCount();
291 if (NotifyContext->StartTime == 0)
292 NotifyContext->StartTime = Now;
293
294 /*
295 * Note: Passed is computed correctly even when GetTickCount()
296 * wraps due to unsigned arithmetic.
297 */
298 Passed = Now - NotifyContext->StartTime;
299 MessageContext.Wnd = Wnd;
300 MessageContext.Msg = NotifyContext->Msg;
301 MessageContext.wParam = NotifyContext->wParam;
302 MessageContext.lParam = NotifyContext->lParam;
303 MessageContext.Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
304 if (!NotifyContext->ShutdownSettings->AutoEndTasks)
305 {
306 MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
307 }
308 if (Passed < MessageContext.Timeout)
309 {
310 MessageContext.Timeout -= Passed;
311 MessageThread = CreateThread(NULL, 0, SendClientShutdown,
312 (LPVOID)&MessageContext, 0, NULL);
313 if (MessageThread == NULL)
314 {
315 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
316 return FALSE;
317 }
318 Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
319 if (Passed < Timeout)
320 {
321 Timeout -= Passed;
322 WaitStatus = WaitForSingleObjectEx(MessageThread, Timeout, FALSE);
323 }
324 else
325 {
326 WaitStatus = WAIT_TIMEOUT;
327 }
328 if (WAIT_TIMEOUT == WaitStatus)
329 {
330 NotifyContext->WndClient = Wnd;
331 if (NotifyContext->UIThread == NULL && NotifyContext->ShowUI)
332 {
333 NotifyContext->UIThread = CreateThread(NULL, 0,
334 EndNowThreadProc,
335 (LPVOID)NotifyContext,
336 0, NULL);
337 }
338 Threads[0] = MessageThread;
339 Threads[1] = NotifyContext->UIThread;
340 WaitStatus = WaitForMultipleObjectsEx(NotifyContext->UIThread == NULL ?
341 1 : 2,
342 Threads, FALSE, INFINITE,
343 FALSE);
344 if (WaitStatus == WAIT_OBJECT_0)
345 {
346 if (!GetExitCodeThread(MessageThread, &NotifyContext->QueryResult))
347 {
348 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
349 }
350 }
351 else if (WaitStatus == WAIT_OBJECT_0 + 1)
352 {
353 if (!GetExitCodeThread(NotifyContext->UIThread,
354 &NotifyContext->QueryResult))
355 {
356 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
357 }
358 }
359 else
360 {
361 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
362 }
363 if (WaitStatus != WAIT_OBJECT_0)
364 {
365 TerminateThread(MessageThread, QUERY_RESULT_TIMEOUT);
366 }
367 }
368 else if (WaitStatus == WAIT_OBJECT_0)
369 {
370 if (!GetExitCodeThread(MessageThread,
371 &NotifyContext->QueryResult))
372 {
373 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
374 }
375 }
376 else
377 {
378 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
379 }
380 CloseHandle(MessageThread);
381 }
382 else
383 {
384 NotifyContext->QueryResult = QUERY_RESULT_TIMEOUT;
385 }
386
387 DPRINT("NotifyContext->QueryResult == %d\n", NotifyContext->QueryResult);
388 return (NotifyContext->QueryResult == QUERY_RESULT_CONTINUE);
389 }
390
391 static BOOLEAN
392 IsConsoleMode(VOID)
393 {
394 return (BOOLEAN)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE);
395 }
396
397 /************************************************/
398
399
400 static BOOL
401 ThreadShutdownNotify(IN PCSR_THREAD CsrThread,
402 IN ULONG Flags,
403 IN ULONG Flags2,
404 IN PNOTIFY_CONTEXT Context)
405 {
406 HWND TopWnd = NULL;
407
408 EnumThreadWindows(HandleToUlong(CsrThread->ClientId.UniqueThread),
409 FindTopLevelWnd, (LPARAM)&TopWnd);
410 if (TopWnd)
411 {
412 HWND hWndOwner;
413
414 /*** FOR TESTING PURPOSES ONLY!! ***/
415 HWND tmpWnd;
416 tmpWnd = TopWnd;
417 /***********************************/
418
419 while ((hWndOwner = GetWindow(TopWnd, GW_OWNER)) != NULL)
420 {
421 MY_DPRINT("GetWindow(TopWnd, GW_OWNER) not returned NULL...\n");
422 TopWnd = hWndOwner;
423 }
424 if (TopWnd != tmpWnd) MY_DPRINT("(TopWnd = %x) != (tmpWnd = %x)\n", TopWnd, tmpWnd);
425 }
426 else
427 {
428 return FALSE;
429 }
430
431 Context->wParam = Flags2;
432 Context->lParam = (0 != (Flags & EWX_CALLER_WINLOGON_LOGOFF) ?
433 ENDSESSION_LOGOFF : 0);
434
435 Context->StartTime = 0;
436 Context->UIThread = NULL;
437 Context->ShowUI = !IsConsoleMode() && (Flags2 & (MCS_QUERYENDSESSION | MCS_ENDSESSION));
438 Context->Dlg = NULL;
439
440 #if 0 // Obviously, switching desktops like that from within WINSRV doesn't work...
441 {
442 BOOL Success;
443 Context->OldDesktop = GetThreadDesktop(GetCurrentThreadId());
444 // Context->Desktop = GetThreadDesktop(HandleToUlong(CsrThread->ClientId.UniqueThread));
445 Context->Desktop = GetThreadDesktop(GetWindowThreadProcessId(TopWnd, NULL));
446 MY_DPRINT("Last error = %d\n", GetLastError());
447 MY_DPRINT("Before switching to desktop 0x%x\n", Context->Desktop);
448 Success = SwitchDesktop(Context->Desktop);
449 MY_DPRINT("After switching to desktop (Success = %s ; last error = %d); going to notify top-level...\n",
450 Success ? "TRUE" : "FALSE", GetLastError());
451 }
452 #endif
453
454 NotifyTopLevelWindow(TopWnd, Context);
455
456 /******************************************************************************/
457 #if 1
458 if (Context->UIThread)
459 {
460 MY_DPRINT("Context->UIThread != NULL\n");
461 if (Context->Dlg)
462 {
463 MY_DPRINT("Sending WM_CLOSE because Dlg is != NULL\n");
464 SendMessageW(Context->Dlg, WM_CLOSE, 0, 0);
465 }
466 else
467 {
468 MY_DPRINT("Terminating UIThread thread with QUERY_RESULT_ERROR\n");
469 TerminateThread(Context->UIThread, QUERY_RESULT_ERROR);
470 }
471 CloseHandle(Context->UIThread);
472 /**/Context->UIThread = NULL;/**/
473 /**/Context->Dlg = NULL;/**/
474 }
475 #endif
476 /******************************************************************************/
477
478 #if 0
479 MY_DPRINT("Switch back to old desktop 0x%x\n", Context->OldDesktop);
480 SwitchDesktop(Context->OldDesktop);
481 MY_DPRINT("Switched back ok\n");
482 #endif
483
484 return TRUE;
485 }
486
487 static ULONG
488 NotifyUserProcessForShutdown(PCSR_PROCESS CsrProcess,
489 PSHUTDOWN_SETTINGS ShutdownSettings,
490 UINT Flags)
491 {
492 DWORD QueryResult = QUERY_RESULT_CONTINUE;
493 PCSR_PROCESS Process;
494 PCSR_THREAD Thread;
495 PLIST_ENTRY NextEntry;
496 NOTIFY_CONTEXT Context;
497 BOOL FoundWindows = FALSE;
498
499 /* In case we make a forced shutdown, just kill the process */
500 if (Flags & EWX_FORCE)
501 return CsrShutdownCsrProcess;
502
503 Context.ShutdownSettings = ShutdownSettings;
504 Context.QueryResult = QUERY_RESULT_CONTINUE; // We continue shutdown by default.
505
506 /* Lock the process */
507 CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
508
509 /* Send first the QUERYENDSESSION messages to all the threads of the process */
510 MY_DPRINT2("Sending the QUERYENDSESSION messages...\n");
511
512 NextEntry = CsrProcess->ThreadList.Flink;
513 while (NextEntry != &CsrProcess->ThreadList)
514 {
515 /* Get the current thread entry */
516 Thread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
517
518 /* Move to the next entry */
519 NextEntry = NextEntry->Flink;
520
521 /* If the thread is being terminated, just skip it */
522 if (Thread->Flags & CsrThreadTerminated) continue;
523
524 /* Reference the thread and temporarily unlock the process */
525 CsrReferenceThread(Thread);
526 CsrUnlockProcess(Process);
527
528 Context.QueryResult = QUERY_RESULT_CONTINUE;
529 if (ThreadShutdownNotify(Thread, Flags, MCS_QUERYENDSESSION, &Context))
530 {
531 FoundWindows = TRUE;
532 }
533
534 /* Lock the process again and dereference the thread */
535 CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
536 CsrDereferenceThread(Thread);
537
538 // FIXME: Analyze Context.QueryResult !!
539 /**/if (Context.QueryResult == QUERY_RESULT_ABORT) goto Quit;/**/
540 }
541
542 if (!FoundWindows)
543 {
544 /* We looped all threads but no top level window was found so we didn't send any message */
545 /* Let the console server run the generic process shutdown handler */
546 CsrUnlockProcess(Process);
547 return CsrShutdownNonCsrProcess;
548 }
549
550 QueryResult = Context.QueryResult;
551 MY_DPRINT2("QueryResult = %s\n",
552 QueryResult == QUERY_RESULT_ABORT ? "Abort" : "Continue");
553
554 /* Now send the ENDSESSION messages to the threads */
555 MY_DPRINT2("Now sending the ENDSESSION messages...\n");
556
557 NextEntry = CsrProcess->ThreadList.Flink;
558 while (NextEntry != &CsrProcess->ThreadList)
559 {
560 /* Get the current thread entry */
561 Thread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
562
563 /* Move to the next entry */
564 NextEntry = NextEntry->Flink;
565
566 /* If the thread is being terminated, just skip it */
567 if (Thread->Flags & CsrThreadTerminated) continue;
568
569 /* Reference the thread and temporarily unlock the process */
570 CsrReferenceThread(Thread);
571 CsrUnlockProcess(Process);
572
573 Context.QueryResult = QUERY_RESULT_CONTINUE;
574 ThreadShutdownNotify(Thread, Flags,
575 (QUERY_RESULT_ABORT != QueryResult) ? MCS_ENDSESSION : 0,
576 &Context);
577
578 /* Lock the process again and dereference the thread */
579 CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
580 CsrDereferenceThread(Thread);
581 }
582
583 Quit:
584 /* Unlock the process */
585 CsrUnlockProcess(Process);
586
587 #if 0
588 if (Context.UIThread)
589 {
590 if (Context.Dlg)
591 {
592 SendMessageW(Context.Dlg, WM_CLOSE, 0, 0);
593 }
594 else
595 {
596 TerminateThread(Context.UIThread, QUERY_RESULT_ERROR);
597 }
598 CloseHandle(Context.UIThread);
599 }
600 #endif
601
602 /* Kill the process unless we abort shutdown */
603 if (QueryResult == QUERY_RESULT_ABORT)
604 return CsrShutdownCancelled;
605
606 return CsrShutdownCsrProcess;
607 }
608
609 static NTSTATUS FASTCALL
610 UserExitReactOS(PCSR_THREAD CsrThread, UINT Flags)
611 {
612 NTSTATUS Status;
613 LUID CallerLuid;
614
615 DWORD ProcessId = HandleToUlong(CsrThread->ClientId.UniqueProcess);
616 DWORD ThreadId = HandleToUlong(CsrThread->ClientId.UniqueThread);
617
618 DPRINT1("SrvExitWindowsEx(ClientId: %lx.%lx, Flags: 0x%x)\n",
619 ProcessId, ThreadId, Flags);
620
621 /*
622 * Check for flags validity
623 */
624
625 if (Flags & EWX_CALLER_WINLOGON)
626 {
627 /* Only Winlogon can call this */
628 if (ProcessId != LogonProcessId)
629 {
630 DPRINT1("SrvExitWindowsEx call not from Winlogon\n");
631 return STATUS_ACCESS_DENIED;
632 }
633 }
634
635 /* Implicitely add the shutdown flag when we poweroff or reboot */
636 if (Flags & (EWX_POWEROFF | EWX_REBOOT))
637 Flags |= EWX_SHUTDOWN;
638
639 /*
640 * Impersonate and retrieve the caller's LUID so that
641 * we can only shutdown processes in its context.
642 */
643 if (!CsrImpersonateClient(NULL))
644 return STATUS_BAD_IMPERSONATION_LEVEL;
645
646 Status = CsrGetProcessLuid(NULL, &CallerLuid);
647 if (!NT_SUCCESS(Status))
648 {
649 DPRINT1("Unable to get caller LUID, Status = 0x%08x\n", Status);
650 goto Quit;
651 }
652
653 DPRINT("Caller LUID is: %lx.%lx\n", CallerLuid.HighPart, CallerLuid.LowPart);
654
655 /* Shutdown loop */
656 while (TRUE)
657 {
658 /* Notify Win32k and potentially Winlogon of the shutdown */
659 Status = NtUserSetInformationThread(CsrThread->ThreadHandle,
660 UserThreadInitiateShutdown,
661 &Flags, sizeof(Flags));
662 DPRINT("Win32k says: %lx\n", Status);
663 switch (Status)
664 {
665 /* We cannot wait here, the caller should start a new thread */
666 case STATUS_CANT_WAIT:
667 DPRINT1("NtUserSetInformationThread returned STATUS_CANT_WAIT\n");
668 goto Quit;
669
670 /* Shutdown is in progress */
671 case STATUS_PENDING:
672 DPRINT1("NtUserSetInformationThread returned STATUS_PENDING\n");
673 goto Quit;
674
675 /* Abort */
676 case STATUS_RETRY:
677 {
678 DPRINT1("NtUserSetInformationThread returned STATUS_RETRY\n");
679 UNIMPLEMENTED;
680 continue;
681 }
682
683 default:
684 {
685 if (!NT_SUCCESS(Status))
686 {
687 // FIXME: Use some UserSetLastNTError or SetLastNtError
688 // that we have defined for user32 or win32k usage only...
689 SetLastError(RtlNtStatusToDosError(Status));
690 goto Quit;
691 }
692 }
693 }
694
695 /* All good */
696 break;
697 }
698
699 /*
700 * OK we can continue. Now magic happens:
701 *
702 * Terminate all Win32 processes, stop if we find one kicking
703 * and screaming it doesn't want to die.
704 *
705 * This function calls the ShutdownProcessCallback callback of
706 * each CSR server for each Win32 process.
707 */
708 Status = CsrShutdownProcesses(&CallerLuid, Flags);
709 if (!NT_SUCCESS(Status))
710 {
711 DPRINT1("Failed to shutdown processes, Status = 0x%08x\n", Status);
712 }
713
714 // FIXME: If Status == STATUS_CANCELLED, call RecordShutdownReason
715
716 /* Tell Win32k and potentially Winlogon that we're done */
717 NtUserSetInformationThread(CsrThread->ThreadHandle,
718 UserThreadEndShutdown,
719 &Status, sizeof(Status));
720
721 DPRINT("SrvExitWindowsEx returned 0x%08x\n", Status);
722
723 Quit:
724 /* We are done */
725 CsrRevertToSelf();
726 return Status;
727 }
728
729
730 ULONG
731 NTAPI
732 UserClientShutdown(IN PCSR_PROCESS CsrProcess,
733 IN ULONG Flags,
734 IN BOOLEAN FirstPhase)
735 {
736 ULONG result;
737
738 DPRINT("UserClientShutdown(0x%p, 0x%x, %s) - [0x%x, 0x%x], ShutdownFlags: %lu\n",
739 CsrProcess, Flags, FirstPhase ? "FirstPhase" : "LastPhase",
740 CsrProcess->ClientId.UniqueProcess, CsrProcess->ClientId.UniqueThread,
741 CsrProcess->ShutdownFlags);
742
743 /*
744 * Check for process validity
745 */
746
747 /* Do not kill system processes when a user is logging off */
748 if ((Flags & EWX_SHUTDOWN) == EWX_LOGOFF &&
749 (CsrProcess->ShutdownFlags & (SHUTDOWN_OTHERCONTEXT | SHUTDOWN_SYSTEMCONTEXT)))
750 {
751 DPRINT("Do not kill a system process in a logoff request!\n");
752 return CsrShutdownNonCsrProcess;
753 }
754
755 /* Do not kill Winlogon */
756 if (CsrProcess->ClientId.UniqueProcess == UlongToHandle(LogonProcessId))
757 {
758 DPRINT("Not killing Winlogon; CsrProcess->ShutdownFlags = %lu\n",
759 CsrProcess->ShutdownFlags);
760
761 /* Returning CsrShutdownCsrProcess means that we handled this process by doing nothing */
762 /* This will mark winlogon as processed so consrv won't be notified again for it */
763 CsrDereferenceProcess(CsrProcess);
764 return CsrShutdownCsrProcess;
765 }
766
767 /* Notify the process for shutdown if needed */
768 result = NotifyUserProcessForShutdown(CsrProcess, &ShutdownSettings, Flags);
769 if (result == CsrShutdownCancelled || result == CsrShutdownNonCsrProcess)
770 {
771 if (result == CsrShutdownCancelled)
772 DPRINT1("Process 0x%x aborted shutdown\n", CsrProcess->ClientId.UniqueProcess);
773 return result;
774 }
775
776 /* Terminate this process */
777 #if DBG
778 {
779 WCHAR buffer[MAX_PATH];
780 if (!GetProcessImageFileNameW(CsrProcess->ProcessHandle, buffer, MAX_PATH))
781 {
782 DPRINT1("Terminating process %x\n", CsrProcess->ClientId.UniqueProcess);
783 }
784 else
785 {
786 DPRINT1("Terminating process %x (%S)\n", CsrProcess->ClientId.UniqueProcess, buffer);
787 }
788 }
789 #endif
790 NtTerminateProcess(CsrProcess->ProcessHandle, 0);
791
792 WaitForSingleObject(CsrProcess->ProcessHandle, ShutdownSettings.ProcessTerminateTimeout);
793
794 /* We are done */
795 CsrDereferenceProcess(CsrProcess);
796 return CsrShutdownCsrProcess;
797 }
798
799
800 /* PUBLIC SERVER APIS *********************************************************/
801
802 /* API_NUMBER: UserpExitWindowsEx */
803 CSR_API(SrvExitWindowsEx)
804 {
805 NTSTATUS Status;
806 PUSER_EXIT_REACTOS ExitReactOSRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactOSRequest;
807
808 Status = NtUserSetInformationThread(NtCurrentThread(),
809 UserThreadUseActiveDesktop,
810 NULL,
811 0);
812 if (!NT_SUCCESS(Status))
813 {
814 DPRINT1("Failed to set thread desktop!\n");
815 return Status;
816 }
817
818 Status = UserExitReactOS(CsrGetClientThread(), ExitReactOSRequest->Flags);
819 ExitReactOSRequest->Success = NT_SUCCESS(Status);
820 ExitReactOSRequest->LastError = GetLastError();
821
822 NtUserSetInformationThread(NtCurrentThread(), UserThreadRestoreDesktop, NULL, 0);
823
824 return Status;
825 }
826
827 /* API_NUMBER: UserpEndTask */
828 CSR_API(SrvEndTask)
829 {
830 PUSER_END_TASK EndTaskRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.EndTaskRequest;
831 NTSTATUS Status;
832
833 // FIXME: This is HACK-plemented!!
834 DPRINT1("SrvEndTask is HACKPLEMENTED!!\n");
835
836 Status = NtUserSetInformationThread(NtCurrentThread(),
837 UserThreadUseActiveDesktop,
838 NULL,
839 0);
840 if (!NT_SUCCESS(Status))
841 {
842 DPRINT1("Failed to set thread desktop!\n");
843 return Status;
844 }
845
846 SendMessageW(EndTaskRequest->WndHandle, WM_CLOSE, 0, 0);
847 // PostMessageW(EndTaskRequest->WndHandle, WM_CLOSE, 0, 0);
848
849 if (IsWindow(EndTaskRequest->WndHandle))
850 {
851 if (EndTaskRequest->Force)
852 {
853 EndTaskRequest->Success = DestroyWindow(EndTaskRequest->WndHandle);
854 EndTaskRequest->LastError = GetLastError();
855 }
856 else
857 {
858 EndTaskRequest->Success = FALSE;
859 }
860 }
861 else
862 {
863 EndTaskRequest->Success = TRUE;
864 EndTaskRequest->LastError = ERROR_SUCCESS;
865 }
866
867 NtUserSetInformationThread(NtCurrentThread(), UserThreadRestoreDesktop, NULL, 0);
868
869 return STATUS_SUCCESS;
870 }
871
872 /* API_NUMBER: UserpRecordShutdownReason */
873 CSR_API(SrvRecordShutdownReason)
874 {
875 DPRINT1("%s not yet implemented\n", __FUNCTION__);
876 return STATUS_NOT_IMPLEMENTED;
877 }
878
879 /* EOF */