[USERSRV] Hard-error improvements 3/7
[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 // SetThreadDesktop(NotifyContext->Desktop);
195 // SwitchDesktop(NotifyContext->Desktop);
196 CallInitCommonControls();
197 NotifyContext->Dlg = CreateDialogParam(UserServerDllInstance,
198 MAKEINTRESOURCE(IDD_END_NOW), NULL,
199 EndNowDlgProc, (LPARAM)NotifyContext);
200 if (NotifyContext->Dlg == NULL)
201 return 0;
202
203 ShowWindow(NotifyContext->Dlg, SW_SHOWNORMAL);
204
205 while (GetMessageW(&Msg, NULL, 0, 0))
206 {
207 if (!IsDialogMessage(NotifyContext->Dlg, &Msg))
208 {
209 TranslateMessage(&Msg);
210 DispatchMessageW(&Msg);
211 }
212 }
213
214 return Msg.wParam;
215 }
216
217 static DWORD WINAPI
218 SendClientShutdown(LPVOID Parameter)
219 {
220 PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT)Parameter;
221 DWORD_PTR Result;
222
223 /* If the shutdown is aborted, just notify the process, there is no need to wait */
224 if ((Context->wParam & (MCS_QUERYENDSESSION | MCS_ENDSESSION)) == 0)
225 {
226 DPRINT("Called WM_CLIENTSHUTDOWN with wParam == 0 ...\n");
227 SendNotifyMessageW(Context->Wnd, WM_CLIENTSHUTDOWN,
228 Context->wParam, Context->lParam);
229 return QUERY_RESULT_CONTINUE;
230 }
231
232 if (SendMessageTimeoutW(Context->Wnd, WM_CLIENTSHUTDOWN,
233 Context->wParam, Context->lParam,
234 SMTO_NORMAL, Context->Timeout, &Result))
235 {
236 DWORD Ret;
237
238 if (Context->wParam & MCS_QUERYENDSESSION)
239 {
240 /* WM_QUERYENDSESSION case */
241 switch (Result)
242 {
243 case MCSR_DONOTSHUTDOWN:
244 Ret = QUERY_RESULT_ABORT;
245 break;
246
247 case MCSR_GOODFORSHUTDOWN:
248 case MCSR_SHUTDOWNFINISHED:
249 default:
250 Ret = QUERY_RESULT_CONTINUE;
251 }
252 }
253 else
254 {
255 /* WM_ENDSESSION case */
256 Ret = QUERY_RESULT_CONTINUE;
257 }
258
259 DPRINT("SendClientShutdown -- Return == %s\n",
260 Ret == QUERY_RESULT_CONTINUE ? "Continue" : "Abort");
261 return Ret;
262 }
263
264 DPRINT1("SendClientShutdown -- Error == %s\n",
265 GetLastError() == 0 ? "Timeout" : "error");
266
267 return (GetLastError() == 0 ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR);
268 }
269
270 static BOOL
271 NotifyTopLevelWindow(HWND Wnd, PNOTIFY_CONTEXT NotifyContext)
272 {
273 MESSAGE_CONTEXT MessageContext;
274 DWORD Now, Passed;
275 DWORD Timeout, WaitStatus;
276 HANDLE MessageThread;
277 HANDLE Threads[2];
278
279 SetForegroundWindow(Wnd);
280
281 Now = GetTickCount();
282 if (NotifyContext->StartTime == 0)
283 NotifyContext->StartTime = Now;
284
285 /*
286 * Note: Passed is computed correctly even when GetTickCount()
287 * wraps due to unsigned arithmetic.
288 */
289 Passed = Now - NotifyContext->StartTime;
290 MessageContext.Wnd = Wnd;
291 MessageContext.Msg = NotifyContext->Msg;
292 MessageContext.wParam = NotifyContext->wParam;
293 MessageContext.lParam = NotifyContext->lParam;
294 MessageContext.Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
295 if (!NotifyContext->ShutdownSettings->AutoEndTasks)
296 {
297 MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout;
298 }
299 if (Passed < MessageContext.Timeout)
300 {
301 MessageContext.Timeout -= Passed;
302 MessageThread = CreateThread(NULL, 0, SendClientShutdown,
303 (LPVOID)&MessageContext, 0, NULL);
304 if (MessageThread == NULL)
305 {
306 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
307 return FALSE;
308 }
309 Timeout = NotifyContext->ShutdownSettings->HungAppTimeout;
310 if (Passed < Timeout)
311 {
312 Timeout -= Passed;
313 WaitStatus = WaitForSingleObjectEx(MessageThread, Timeout, FALSE);
314 }
315 else
316 {
317 WaitStatus = WAIT_TIMEOUT;
318 }
319 if (WAIT_TIMEOUT == WaitStatus)
320 {
321 NotifyContext->WndClient = Wnd;
322 if (NotifyContext->UIThread == NULL && NotifyContext->ShowUI)
323 {
324 NotifyContext->UIThread = CreateThread(NULL, 0,
325 EndNowThreadProc,
326 (LPVOID)NotifyContext,
327 0, NULL);
328 }
329 Threads[0] = MessageThread;
330 Threads[1] = NotifyContext->UIThread;
331 WaitStatus = WaitForMultipleObjectsEx(NotifyContext->UIThread == NULL ?
332 1 : 2,
333 Threads, FALSE, INFINITE,
334 FALSE);
335 if (WaitStatus == WAIT_OBJECT_0)
336 {
337 if (!GetExitCodeThread(MessageThread, &NotifyContext->QueryResult))
338 {
339 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
340 }
341 }
342 else if (WaitStatus == WAIT_OBJECT_0 + 1)
343 {
344 if (!GetExitCodeThread(NotifyContext->UIThread,
345 &NotifyContext->QueryResult))
346 {
347 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
348 }
349 }
350 else
351 {
352 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
353 }
354 if (WaitStatus != WAIT_OBJECT_0)
355 {
356 TerminateThread(MessageThread, QUERY_RESULT_TIMEOUT);
357 }
358 }
359 else if (WaitStatus == WAIT_OBJECT_0)
360 {
361 if (!GetExitCodeThread(MessageThread,
362 &NotifyContext->QueryResult))
363 {
364 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
365 }
366 }
367 else
368 {
369 NotifyContext->QueryResult = QUERY_RESULT_ERROR;
370 }
371 CloseHandle(MessageThread);
372 }
373 else
374 {
375 NotifyContext->QueryResult = QUERY_RESULT_TIMEOUT;
376 }
377
378 DPRINT("NotifyContext->QueryResult == %d\n", NotifyContext->QueryResult);
379 return (NotifyContext->QueryResult == QUERY_RESULT_CONTINUE);
380 }
381
382 static BOOLEAN
383 IsConsoleMode(VOID)
384 {
385 return (BOOLEAN)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE);
386 }
387
388
389 /* TODO: Find an other way to do it. */
390 #if 0
391 VOID FASTCALL
392 ConioConsoleCtrlEventTimeout(DWORD Event, PCSR_PROCESS ProcessData, DWORD Timeout)
393 {
394 HANDLE Thread;
395
396 DPRINT("ConioConsoleCtrlEvent Parent ProcessId = %x\n", ProcessData->ClientId.UniqueProcess);
397
398 if (ProcessData->CtrlDispatcher)
399 {
400 Thread = CreateRemoteThread(ProcessData->ProcessHandle, NULL, 0,
401 (LPTHREAD_START_ROUTINE) ProcessData->CtrlDispatcher,
402 UlongToPtr(Event), 0, NULL);
403 if (Thread == NULL)
404 {
405 DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError());
406 return;
407 }
408 WaitForSingleObject(Thread, Timeout);
409 CloseHandle(Thread);
410 }
411 }
412 #endif
413 /************************************************/
414
415
416 static VOID
417 ThreadShutdownNotify(IN PCSR_THREAD CsrThread,
418 IN ULONG Flags,
419 IN ULONG Flags2,
420 IN PNOTIFY_CONTEXT Context)
421 {
422 HWND TopWnd = NULL;
423
424 EnumThreadWindows(HandleToUlong(CsrThread->ClientId.UniqueThread),
425 FindTopLevelWnd, (LPARAM)&TopWnd);
426 if (TopWnd)
427 {
428 HWND hWndOwner;
429
430 /*** FOR TESTING PURPOSES ONLY!! ***/
431 HWND tmpWnd;
432 tmpWnd = TopWnd;
433 /***********************************/
434
435 while ((hWndOwner = GetWindow(TopWnd, GW_OWNER)) != NULL)
436 {
437 MY_DPRINT("GetWindow(TopWnd, GW_OWNER) not returned NULL...\n");
438 TopWnd = hWndOwner;
439 }
440 if (TopWnd != tmpWnd) MY_DPRINT("(TopWnd = %x) != (tmpWnd = %x)\n", TopWnd, tmpWnd);
441 }
442 if (TopWnd == NULL)
443 return;
444
445 Context->wParam = Flags2;
446 Context->lParam = (0 != (Flags & EWX_CALLER_WINLOGON_LOGOFF) ?
447 ENDSESSION_LOGOFF : 0);
448
449 Context->StartTime = 0;
450 Context->UIThread = NULL;
451 Context->ShowUI = !IsConsoleMode() && (Flags2 & (MCS_QUERYENDSESSION | MCS_ENDSESSION));
452 Context->Dlg = NULL;
453
454 #if 0 // Obviously, switching desktops like that from within WINSRV doesn't work...
455 {
456 BOOL Success;
457 Context->OldDesktop = GetThreadDesktop(GetCurrentThreadId());
458 // Context->Desktop = GetThreadDesktop(HandleToUlong(CsrThread->ClientId.UniqueThread));
459 Context->Desktop = GetThreadDesktop(GetWindowThreadProcessId(TopWnd, NULL));
460 MY_DPRINT("Last error = %d\n", GetLastError());
461 MY_DPRINT("Before switching to desktop 0x%x\n", Context->Desktop);
462 Success = SwitchDesktop(Context->Desktop);
463 MY_DPRINT("After switching to desktop (Success = %s ; last error = %d); going to notify top-level...\n",
464 Success ? "TRUE" : "FALSE", GetLastError());
465 }
466 #endif
467
468 NotifyTopLevelWindow(TopWnd, Context);
469
470 /******************************************************************************/
471 #if 1
472 if (Context->UIThread)
473 {
474 MY_DPRINT("Context->UIThread != NULL\n");
475 if (Context->Dlg)
476 {
477 MY_DPRINT("Sending WM_CLOSE because Dlg is != NULL\n");
478 SendMessageW(Context->Dlg, WM_CLOSE, 0, 0);
479 }
480 else
481 {
482 MY_DPRINT("Terminating UIThread thread with QUERY_RESULT_ERROR\n");
483 TerminateThread(Context->UIThread, QUERY_RESULT_ERROR);
484 }
485 CloseHandle(Context->UIThread);
486 /**/Context->UIThread = NULL;/**/
487 /**/Context->Dlg = NULL;/**/
488 }
489 #endif
490 /******************************************************************************/
491
492 #if 0
493 MY_DPRINT("Switch back to old desktop 0x%x\n", Context->OldDesktop);
494 SwitchDesktop(Context->OldDesktop);
495 MY_DPRINT("Switched back ok\n");
496 #endif
497 }
498
499 static BOOL
500 NotifyProcessForShutdown(PCSR_PROCESS CsrProcess,
501 PSHUTDOWN_SETTINGS ShutdownSettings,
502 UINT Flags)
503 {
504 DWORD QueryResult = QUERY_RESULT_CONTINUE;
505
506 /* In case we make a forced shutdown, just kill the process */
507 if (Flags & EWX_FORCE)
508 return TRUE;
509
510 // TODO: Find an other way whether or not the process has a console.
511 #if 0
512 if (CsrProcess->Console)
513 {
514 ConioConsoleCtrlEventTimeout(CTRL_LOGOFF_EVENT, CsrProcess,
515 ShutdownSettings->WaitToKillAppTimeout);
516 }
517 else
518 #endif
519 {
520 PCSR_PROCESS Process;
521 PCSR_THREAD Thread;
522 PLIST_ENTRY NextEntry;
523
524 NOTIFY_CONTEXT Context;
525 Context.ShutdownSettings = ShutdownSettings;
526 Context.QueryResult = QUERY_RESULT_CONTINUE; // We continue shutdown by default.
527
528 /* Lock the process */
529 CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
530
531 /* Send first the QUERYENDSESSION messages to all the threads of the process */
532 MY_DPRINT2("Sending the QUERYENDSESSION messages...\n");
533
534 NextEntry = CsrProcess->ThreadList.Flink;
535 while (NextEntry != &CsrProcess->ThreadList)
536 {
537 /* Get the current thread entry */
538 Thread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
539
540 /* Move to the next entry */
541 NextEntry = NextEntry->Flink;
542
543 /* If the thread is being terminated, just skip it */
544 if (Thread->Flags & CsrThreadTerminated) continue;
545
546 /* Reference the thread and temporarily unlock the process */
547 CsrReferenceThread(Thread);
548 CsrUnlockProcess(Process);
549
550 Context.QueryResult = QUERY_RESULT_CONTINUE;
551 ThreadShutdownNotify(Thread, Flags,
552 MCS_QUERYENDSESSION,
553 &Context);
554
555 /* Lock the process again and dereference the thread */
556 CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
557 CsrDereferenceThread(Thread);
558
559 // FIXME: Analyze Context.QueryResult !!
560 /**/if (Context.QueryResult == QUERY_RESULT_ABORT) goto Quit;/**/
561 }
562
563 QueryResult = Context.QueryResult;
564 MY_DPRINT2("QueryResult = %s\n",
565 QueryResult == QUERY_RESULT_ABORT ? "Abort" : "Continue");
566
567 /* Now send the ENDSESSION messages to the threads */
568 MY_DPRINT2("Now sending the ENDSESSION messages...\n");
569
570 NextEntry = CsrProcess->ThreadList.Flink;
571 while (NextEntry != &CsrProcess->ThreadList)
572 {
573 /* Get the current thread entry */
574 Thread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
575
576 /* Move to the next entry */
577 NextEntry = NextEntry->Flink;
578
579 /* If the thread is being terminated, just skip it */
580 if (Thread->Flags & CsrThreadTerminated) continue;
581
582 /* Reference the thread and temporarily unlock the process */
583 CsrReferenceThread(Thread);
584 CsrUnlockProcess(Process);
585
586 Context.QueryResult = QUERY_RESULT_CONTINUE;
587 ThreadShutdownNotify(Thread, Flags,
588 (QUERY_RESULT_ABORT != QueryResult) ? MCS_ENDSESSION : 0,
589 &Context);
590
591 /* Lock the process again and dereference the thread */
592 CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
593 CsrDereferenceThread(Thread);
594 }
595
596 Quit:
597 /* Unlock the process */
598 CsrUnlockProcess(Process);
599
600 #if 0
601 if (Context.UIThread)
602 {
603 if (Context.Dlg)
604 {
605 SendMessageW(Context.Dlg, WM_CLOSE, 0, 0);
606 }
607 else
608 {
609 TerminateThread(Context.UIThread, QUERY_RESULT_ERROR);
610 }
611 CloseHandle(Context.UIThread);
612 }
613 #endif
614 }
615
616 /* Kill the process unless we abort shutdown */
617 return (QueryResult != QUERY_RESULT_ABORT);
618 }
619
620 static NTSTATUS FASTCALL
621 UserExitReactOS(PCSR_THREAD CsrThread, UINT Flags)
622 {
623 NTSTATUS Status;
624 LUID CallerLuid;
625
626 DWORD ProcessId = HandleToUlong(CsrThread->ClientId.UniqueProcess);
627 DWORD ThreadId = HandleToUlong(CsrThread->ClientId.UniqueThread);
628
629 DPRINT1("SrvExitWindowsEx(ClientId: %lx.%lx, Flags: 0x%x)\n",
630 ProcessId, ThreadId, Flags);
631
632 /*
633 * Check for flags validity
634 */
635
636 if (Flags & EWX_CALLER_WINLOGON)
637 {
638 /* Only Winlogon can call this */
639 if (ProcessId != LogonProcessId)
640 {
641 DPRINT1("SrvExitWindowsEx call not from Winlogon\n");
642 return STATUS_ACCESS_DENIED;
643 }
644 }
645
646 /* Implicitely add the shutdown flag when we poweroff or reboot */
647 if (Flags & (EWX_POWEROFF | EWX_REBOOT))
648 Flags |= EWX_SHUTDOWN;
649
650 /*
651 * Impersonate and retrieve the caller's LUID so that
652 * we can only shutdown processes in its context.
653 */
654 if (!CsrImpersonateClient(NULL))
655 return STATUS_BAD_IMPERSONATION_LEVEL;
656
657 Status = CsrGetProcessLuid(NULL, &CallerLuid);
658 if (!NT_SUCCESS(Status))
659 {
660 DPRINT1("Unable to get caller LUID, Status = 0x%08x\n", Status);
661 goto Quit;
662 }
663
664 DPRINT("Caller LUID is: %lx.%lx\n", CallerLuid.HighPart, CallerLuid.LowPart);
665
666 /* Shutdown loop */
667 while (TRUE)
668 {
669 /* Notify Win32k and potentially Winlogon of the shutdown */
670 Status = NtUserSetInformationThread(CsrThread->ThreadHandle,
671 UserThreadInitiateShutdown,
672 &Flags, sizeof(Flags));
673 DPRINT("Win32k says: %lx\n", Status);
674 switch (Status)
675 {
676 /* We cannot wait here, the caller should start a new thread */
677 case STATUS_CANT_WAIT:
678 DPRINT1("NtUserSetInformationThread returned STATUS_CANT_WAIT\n");
679 goto Quit;
680
681 /* Shutdown is in progress */
682 case STATUS_PENDING:
683 DPRINT1("NtUserSetInformationThread returned STATUS_PENDING\n");
684 goto Quit;
685
686 /* Abort */
687 case STATUS_RETRY:
688 {
689 DPRINT1("NtUserSetInformationThread returned STATUS_RETRY\n");
690 UNIMPLEMENTED;
691 continue;
692 }
693
694 default:
695 {
696 if (!NT_SUCCESS(Status))
697 {
698 // FIXME: Use some UserSetLastNTError or SetLastNtError
699 // that we have defined for user32 or win32k usage only...
700 SetLastError(RtlNtStatusToDosError(Status));
701 goto Quit;
702 }
703 }
704 }
705
706 /* All good */
707 break;
708 }
709
710 /*
711 * OK we can continue. Now magic happens:
712 *
713 * Terminate all Win32 processes, stop if we find one kicking
714 * and screaming it doesn't want to die.
715 *
716 * This function calls the ShutdownProcessCallback callback of
717 * each CSR server for each Win32 process.
718 */
719 Status = CsrShutdownProcesses(&CallerLuid, Flags);
720 if (!NT_SUCCESS(Status))
721 {
722 DPRINT1("Failed to shutdown processes, Status = 0x%08x\n", Status);
723 }
724
725 // FIXME: If Status == STATUS_CANCELLED, call RecordShutdownReason
726
727 /* Tell Win32k and potentially Winlogon that we're done */
728 NtUserSetInformationThread(CsrThread->ThreadHandle,
729 UserThreadEndShutdown,
730 &Status, sizeof(Status));
731
732 DPRINT("SrvExitWindowsEx returned 0x%08x\n", Status);
733
734 Quit:
735 /* We are done */
736 CsrRevertToSelf();
737 return Status;
738 }
739
740
741 ULONG
742 NTAPI
743 UserClientShutdown(IN PCSR_PROCESS CsrProcess,
744 IN ULONG Flags,
745 IN BOOLEAN FirstPhase)
746 {
747 DPRINT("UserClientShutdown(0x%p, 0x%x, %s) - [0x%x, 0x%x], ShutdownFlags: %lu\n",
748 CsrProcess, Flags, FirstPhase ? "FirstPhase" : "LastPhase",
749 CsrProcess->ClientId.UniqueProcess, CsrProcess->ClientId.UniqueThread,
750 CsrProcess->ShutdownFlags);
751
752 /*
753 * Check for process validity
754 */
755
756 /* Do not kill system processes when a user is logging off */
757 if ((Flags & EWX_SHUTDOWN) == EWX_LOGOFF &&
758 (CsrProcess->ShutdownFlags & (SHUTDOWN_OTHERCONTEXT | SHUTDOWN_SYSTEMCONTEXT)))
759 {
760 DPRINT("Do not kill a system process in a logoff request!\n");
761 return CsrShutdownNonCsrProcess;
762 }
763
764 /* Do not kill Winlogon or CSRSS */
765 if (CsrProcess->ClientId.UniqueProcess == NtCurrentProcess() ||
766 CsrProcess->ClientId.UniqueProcess == UlongToHandle(LogonProcessId))
767 {
768 DPRINT("Not killing %s; CsrProcess->ShutdownFlags = %lu\n",
769 CsrProcess->ClientId.UniqueProcess == NtCurrentProcess() ? "CSRSS" : "Winlogon",
770 CsrProcess->ShutdownFlags);
771
772 return CsrShutdownNonCsrProcess;
773 }
774
775 /* Notify the process for shutdown if needed */
776 if (!NotifyProcessForShutdown(CsrProcess, &ShutdownSettings, Flags))
777 {
778 DPRINT1("Process 0x%x aborted shutdown\n", CsrProcess->ClientId.UniqueProcess);
779 /* Abort shutdown */
780 return CsrShutdownCancelled;
781 }
782
783 /* Terminate this process */
784 #if DBG
785 {
786 WCHAR buffer[MAX_PATH];
787 if (!GetProcessImageFileNameW(CsrProcess->ProcessHandle, buffer, MAX_PATH))
788 {
789 DPRINT1("Terminating process %x\n", CsrProcess->ClientId.UniqueProcess);
790 }
791 else
792 {
793 DPRINT1("Terminating process %x (%S)\n", CsrProcess->ClientId.UniqueProcess, buffer);
794 }
795 }
796 #endif
797 NtTerminateProcess(CsrProcess->ProcessHandle, 0);
798
799 /* We are done */
800 CsrDereferenceProcess(CsrProcess);
801 return CsrShutdownCsrProcess;
802 }
803
804
805 /* PUBLIC SERVER APIS *********************************************************/
806
807 CSR_API(SrvExitWindowsEx)
808 {
809 NTSTATUS Status;
810 PUSER_EXIT_REACTOS ExitReactOSRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactOSRequest;
811
812 Status = UserExitReactOS(CsrGetClientThread(), ExitReactOSRequest->Flags);
813 ExitReactOSRequest->Success = NT_SUCCESS(Status);
814 ExitReactOSRequest->LastError = GetLastError();
815
816 return Status;
817 }
818
819 CSR_API(SrvEndTask)
820 {
821 PUSER_END_TASK EndTaskRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.EndTaskRequest;
822
823 // FIXME: This is HACK-plemented!!
824 DPRINT1("SrvEndTask is HACKPLEMENTED!!\n");
825
826 SendMessageW(EndTaskRequest->WndHandle, WM_CLOSE, 0, 0);
827 // PostMessageW(EndTaskRequest->WndHandle, WM_CLOSE, 0, 0);
828
829 if (IsWindow(EndTaskRequest->WndHandle))
830 {
831 if (EndTaskRequest->Force)
832 {
833 EndTaskRequest->Success = DestroyWindow(EndTaskRequest->WndHandle);
834 EndTaskRequest->LastError = GetLastError();
835 }
836 else
837 {
838 EndTaskRequest->Success = FALSE;
839 }
840 }
841 else
842 {
843 EndTaskRequest->Success = TRUE;
844 EndTaskRequest->LastError = ERROR_SUCCESS;
845 }
846
847 return STATUS_SUCCESS;
848 }
849
850 CSR_API(SrvRecordShutdownReason)
851 {
852 DPRINT1("%s not yet implemented\n", __FUNCTION__);
853 return STATUS_NOT_IMPLEMENTED;
854 }
855
856 /* EOF */