f8d5329652ae7e1ebfb52a1ba2e9f64269d3bee6
[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 BOOL CALLBACK
417 FindTopLevelWnd(IN HWND hWnd,
418 IN LPARAM lParam)
419 {
420 if (GetWindow(hWnd, GW_OWNER) == NULL)
421 {
422 *(HWND*)lParam = hWnd;
423 return FALSE;
424 }
425 return TRUE;
426 }
427
428 static VOID
429 ThreadShutdownNotify(IN PCSR_THREAD CsrThread,
430 IN ULONG Flags,
431 IN ULONG Flags2,
432 IN PNOTIFY_CONTEXT Context)
433 {
434 HWND TopWnd = NULL;
435
436 EnumThreadWindows(HandleToUlong(CsrThread->ClientId.UniqueThread),
437 FindTopLevelWnd, (LPARAM)&TopWnd);
438 if (TopWnd)
439 {
440 HWND hWndOwner;
441
442 /*** FOR TESTING PURPOSES ONLY!! ***/
443 HWND tmpWnd;
444 tmpWnd = TopWnd;
445 /***********************************/
446
447 while ((hWndOwner = GetWindow(TopWnd, GW_OWNER)) != NULL)
448 {
449 MY_DPRINT("GetWindow(TopWnd, GW_OWNER) not returned NULL...\n");
450 TopWnd = hWndOwner;
451 }
452 if (TopWnd != tmpWnd) MY_DPRINT("(TopWnd = %x) != (tmpWnd = %x)\n", TopWnd, tmpWnd);
453 }
454 if (TopWnd == NULL)
455 return;
456
457 Context->wParam = Flags2;
458 Context->lParam = (0 != (Flags & EWX_CALLER_WINLOGON_LOGOFF) ?
459 ENDSESSION_LOGOFF : 0);
460
461 Context->StartTime = 0;
462 Context->UIThread = NULL;
463 Context->ShowUI = !IsConsoleMode() && (Flags2 & (MCS_QUERYENDSESSION | MCS_ENDSESSION));
464 Context->Dlg = NULL;
465
466 #if 0 // Obviously, switching desktops like that from within WINSRV doesn't work...
467 {
468 BOOL Success;
469 Context->OldDesktop = GetThreadDesktop(GetCurrentThreadId());
470 // Context->Desktop = GetThreadDesktop(HandleToUlong(CsrThread->ClientId.UniqueThread));
471 Context->Desktop = GetThreadDesktop(GetWindowThreadProcessId(TopWnd, NULL));
472 MY_DPRINT("Last error = %d\n", GetLastError());
473 MY_DPRINT("Before switching to desktop 0x%x\n", Context->Desktop);
474 Success = SwitchDesktop(Context->Desktop);
475 MY_DPRINT("After switching to desktop (Success = %s ; last error = %d); going to notify top-level...\n",
476 Success ? "TRUE" : "FALSE", GetLastError());
477 }
478 #endif
479
480 NotifyTopLevelWindow(TopWnd, Context);
481
482 /******************************************************************************/
483 #if 1
484 if (Context->UIThread)
485 {
486 MY_DPRINT("Context->UIThread != NULL\n");
487 if (Context->Dlg)
488 {
489 MY_DPRINT("Sending WM_CLOSE because Dlg is != NULL\n");
490 SendMessageW(Context->Dlg, WM_CLOSE, 0, 0);
491 }
492 else
493 {
494 MY_DPRINT("Terminating UIThread thread with QUERY_RESULT_ERROR\n");
495 TerminateThread(Context->UIThread, QUERY_RESULT_ERROR);
496 }
497 CloseHandle(Context->UIThread);
498 /**/Context->UIThread = NULL;/**/
499 /**/Context->Dlg = NULL;/**/
500 }
501 #endif
502 /******************************************************************************/
503
504 #if 0
505 MY_DPRINT("Switch back to old desktop 0x%x\n", Context->OldDesktop);
506 SwitchDesktop(Context->OldDesktop);
507 MY_DPRINT("Switched back ok\n");
508 #endif
509 }
510
511 static BOOL
512 NotifyProcessForShutdown(PCSR_PROCESS CsrProcess,
513 PSHUTDOWN_SETTINGS ShutdownSettings,
514 UINT Flags)
515 {
516 DWORD QueryResult = QUERY_RESULT_CONTINUE;
517
518 /* In case we make a forced shutdown, just kill the process */
519 if (Flags & EWX_FORCE)
520 return TRUE;
521
522 // TODO: Find an other way whether or not the process has a console.
523 #if 0
524 if (CsrProcess->Console)
525 {
526 ConioConsoleCtrlEventTimeout(CTRL_LOGOFF_EVENT, CsrProcess,
527 ShutdownSettings->WaitToKillAppTimeout);
528 }
529 else
530 #endif
531 {
532 PCSR_PROCESS Process;
533 PCSR_THREAD Thread;
534 PLIST_ENTRY NextEntry;
535
536 NOTIFY_CONTEXT Context;
537 Context.ShutdownSettings = ShutdownSettings;
538 Context.QueryResult = QUERY_RESULT_CONTINUE; // We continue shutdown by default.
539
540 /* Lock the process */
541 CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
542
543 /* Send first the QUERYENDSESSION messages to all the threads of the process */
544 MY_DPRINT2("Sending the QUERYENDSESSION messages...\n");
545
546 NextEntry = CsrProcess->ThreadList.Flink;
547 while (NextEntry != &CsrProcess->ThreadList)
548 {
549 /* Get the current thread entry */
550 Thread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
551
552 /* Move to the next entry */
553 NextEntry = NextEntry->Flink;
554
555 /* If the thread is being terminated, just skip it */
556 if (Thread->Flags & CsrThreadTerminated) continue;
557
558 /* Reference the thread and temporarily unlock the process */
559 CsrReferenceThread(Thread);
560 CsrUnlockProcess(Process);
561
562 Context.QueryResult = QUERY_RESULT_CONTINUE;
563 ThreadShutdownNotify(Thread, Flags,
564 MCS_QUERYENDSESSION,
565 &Context);
566
567 /* Lock the process again and dereference the thread */
568 CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
569 CsrDereferenceThread(Thread);
570
571 // FIXME: Analyze Context.QueryResult !!
572 /**/if (Context.QueryResult == QUERY_RESULT_ABORT) goto Quit;/**/
573 }
574
575 QueryResult = Context.QueryResult;
576 MY_DPRINT2("QueryResult = %s\n",
577 QueryResult == QUERY_RESULT_ABORT ? "Abort" : "Continue");
578
579 /* Now send the ENDSESSION messages to the threads */
580 MY_DPRINT2("Now sending the ENDSESSION messages...\n");
581
582 NextEntry = CsrProcess->ThreadList.Flink;
583 while (NextEntry != &CsrProcess->ThreadList)
584 {
585 /* Get the current thread entry */
586 Thread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
587
588 /* Move to the next entry */
589 NextEntry = NextEntry->Flink;
590
591 /* If the thread is being terminated, just skip it */
592 if (Thread->Flags & CsrThreadTerminated) continue;
593
594 /* Reference the thread and temporarily unlock the process */
595 CsrReferenceThread(Thread);
596 CsrUnlockProcess(Process);
597
598 Context.QueryResult = QUERY_RESULT_CONTINUE;
599 ThreadShutdownNotify(Thread, Flags,
600 (QUERY_RESULT_ABORT != QueryResult) ? MCS_ENDSESSION : 0,
601 &Context);
602
603 /* Lock the process again and dereference the thread */
604 CsrLockProcessByClientId(CsrProcess->ClientId.UniqueProcess, &Process);
605 CsrDereferenceThread(Thread);
606 }
607
608 Quit:
609 /* Unlock the process */
610 CsrUnlockProcess(Process);
611
612 #if 0
613 if (Context.UIThread)
614 {
615 if (Context.Dlg)
616 {
617 SendMessageW(Context.Dlg, WM_CLOSE, 0, 0);
618 }
619 else
620 {
621 TerminateThread(Context.UIThread, QUERY_RESULT_ERROR);
622 }
623 CloseHandle(Context.UIThread);
624 }
625 #endif
626 }
627
628 /* Kill the process unless we abort shutdown */
629 return (QueryResult != QUERY_RESULT_ABORT);
630 }
631
632 static NTSTATUS FASTCALL
633 UserExitReactOS(PCSR_THREAD CsrThread, UINT Flags)
634 {
635 NTSTATUS Status;
636 LUID CallerLuid;
637
638 DWORD ProcessId = HandleToUlong(CsrThread->ClientId.UniqueProcess);
639 DWORD ThreadId = HandleToUlong(CsrThread->ClientId.UniqueThread);
640
641 DPRINT1("SrvExitWindowsEx(ClientId: %lx.%lx, Flags: 0x%x)\n",
642 ProcessId, ThreadId, Flags);
643
644 /*
645 * Check for flags validity
646 */
647
648 if (Flags & EWX_CALLER_WINLOGON)
649 {
650 /* Only Winlogon can call this */
651 if (ProcessId != LogonProcessId)
652 {
653 DPRINT1("SrvExitWindowsEx call not from Winlogon\n");
654 return STATUS_ACCESS_DENIED;
655 }
656 }
657
658 /* Implicitely add the shutdown flag when we poweroff or reboot */
659 if (Flags & (EWX_POWEROFF | EWX_REBOOT))
660 Flags |= EWX_SHUTDOWN;
661
662 /*
663 * Impersonate and retrieve the caller's LUID so that
664 * we can only shutdown processes in its context.
665 */
666 if (!CsrImpersonateClient(NULL))
667 return STATUS_BAD_IMPERSONATION_LEVEL;
668
669 Status = CsrGetProcessLuid(NULL, &CallerLuid);
670 if (!NT_SUCCESS(Status))
671 {
672 DPRINT1("Unable to get caller LUID, Status = 0x%08x\n", Status);
673 goto Quit;
674 }
675
676 DPRINT("Caller LUID is: %lx.%lx\n", CallerLuid.HighPart, CallerLuid.LowPart);
677
678 /* Shutdown loop */
679 while (TRUE)
680 {
681 /* Notify Win32k and potentially Winlogon of the shutdown */
682 Status = NtUserSetInformationThread(CsrThread->ThreadHandle,
683 UserThreadInitiateShutdown,
684 &Flags, sizeof(Flags));
685 DPRINT("Win32k says: %lx\n", Status);
686 switch (Status)
687 {
688 /* We cannot wait here, the caller should start a new thread */
689 case STATUS_CANT_WAIT:
690 DPRINT1("NtUserSetInformationThread returned STATUS_CANT_WAIT\n");
691 goto Quit;
692
693 /* Shutdown is in progress */
694 case STATUS_PENDING:
695 DPRINT1("NtUserSetInformationThread returned STATUS_PENDING\n");
696 goto Quit;
697
698 /* Abort */
699 case STATUS_RETRY:
700 {
701 DPRINT1("NtUserSetInformationThread returned STATUS_RETRY\n");
702 UNIMPLEMENTED;
703 continue;
704 }
705
706 default:
707 {
708 if (!NT_SUCCESS(Status))
709 {
710 // FIXME: Use some UserSetLastNTError or SetLastNtError
711 // that we have defined for user32 or win32k usage only...
712 SetLastError(RtlNtStatusToDosError(Status));
713 goto Quit;
714 }
715 }
716 }
717
718 /* All good */
719 break;
720 }
721
722 /*
723 * OK we can continue. Now magic happens:
724 *
725 * Terminate all Win32 processes, stop if we find one kicking
726 * and screaming it doesn't want to die.
727 *
728 * This function calls the ShutdownProcessCallback callback of
729 * each CSR server for each Win32 process.
730 */
731 Status = CsrShutdownProcesses(&CallerLuid, Flags);
732 if (!NT_SUCCESS(Status))
733 {
734 DPRINT1("Failed to shutdown processes, Status = 0x%08x\n", Status);
735 }
736
737 // FIXME: If Status == STATUS_CANCELLED, call RecordShutdownReason
738
739 /* Tell Win32k and potentially Winlogon that we're done */
740 NtUserSetInformationThread(CsrThread->ThreadHandle,
741 UserThreadEndShutdown,
742 &Status, sizeof(Status));
743
744 DPRINT("SrvExitWindowsEx returned 0x%08x\n", Status);
745
746 Quit:
747 /* We are done */
748 CsrRevertToSelf();
749 return Status;
750 }
751
752
753 ULONG
754 NTAPI
755 UserClientShutdown(IN PCSR_PROCESS CsrProcess,
756 IN ULONG Flags,
757 IN BOOLEAN FirstPhase)
758 {
759 DPRINT("UserClientShutdown(0x%p, 0x%x, %s) - [0x%x, 0x%x], ShutdownFlags: %lu\n",
760 CsrProcess, Flags, FirstPhase ? "FirstPhase" : "LastPhase",
761 CsrProcess->ClientId.UniqueProcess, CsrProcess->ClientId.UniqueThread,
762 CsrProcess->ShutdownFlags);
763
764 /*
765 * Check for process validity
766 */
767
768 /* Do not kill system processes when a user is logging off */
769 if ((Flags & EWX_SHUTDOWN) == EWX_LOGOFF &&
770 (CsrProcess->ShutdownFlags & (SHUTDOWN_OTHERCONTEXT | SHUTDOWN_SYSTEMCONTEXT)))
771 {
772 DPRINT("Do not kill a system process in a logoff request!\n");
773 return CsrShutdownNonCsrProcess;
774 }
775
776 /* Do not kill Winlogon or CSRSS */
777 if (CsrProcess->ClientId.UniqueProcess == NtCurrentProcess() ||
778 CsrProcess->ClientId.UniqueProcess == UlongToHandle(LogonProcessId))
779 {
780 DPRINT("Not killing %s; CsrProcess->ShutdownFlags = %lu\n",
781 CsrProcess->ClientId.UniqueProcess == NtCurrentProcess() ? "CSRSS" : "Winlogon",
782 CsrProcess->ShutdownFlags);
783
784 return CsrShutdownNonCsrProcess;
785 }
786
787 /* Notify the process for shutdown if needed */
788 if (!NotifyProcessForShutdown(CsrProcess, &ShutdownSettings, Flags))
789 {
790 DPRINT1("Process 0x%x aborted shutdown\n", CsrProcess->ClientId.UniqueProcess);
791 /* Abort shutdown */
792 return CsrShutdownCancelled;
793 }
794
795 /* Terminate this process */
796 #if DBG
797 {
798 WCHAR buffer[MAX_PATH];
799 if (!GetProcessImageFileNameW(CsrProcess->ProcessHandle, buffer, MAX_PATH))
800 {
801 DPRINT1("Terminating process %x\n", CsrProcess->ClientId.UniqueProcess);
802 }
803 else
804 {
805 DPRINT1("Terminating process %x (%S)\n", CsrProcess->ClientId.UniqueProcess, buffer);
806 }
807 }
808 #endif
809 NtTerminateProcess(CsrProcess->ProcessHandle, 0);
810
811 /* We are done */
812 CsrDereferenceProcess(CsrProcess);
813 return CsrShutdownCsrProcess;
814 }
815
816
817 /* PUBLIC SERVER APIS *********************************************************/
818
819 CSR_API(SrvExitWindowsEx)
820 {
821 NTSTATUS Status;
822 PUSER_EXIT_REACTOS ExitReactOSRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactOSRequest;
823
824 Status = UserExitReactOS(CsrGetClientThread(), ExitReactOSRequest->Flags);
825 ExitReactOSRequest->Success = NT_SUCCESS(Status);
826 ExitReactOSRequest->LastError = GetLastError();
827
828 return Status;
829 }
830
831 CSR_API(SrvEndTask)
832 {
833 PUSER_END_TASK EndTaskRequest = &((PUSER_API_MESSAGE)ApiMessage)->Data.EndTaskRequest;
834
835 // FIXME: This is HACK-plemented!!
836 DPRINT1("SrvEndTask is HACKPLEMENTED!!\n");
837
838 SendMessageW(EndTaskRequest->WndHandle, WM_CLOSE, 0, 0);
839 // PostMessageW(EndTaskRequest->WndHandle, WM_CLOSE, 0, 0);
840
841 if (IsWindow(EndTaskRequest->WndHandle))
842 {
843 if (EndTaskRequest->Force)
844 {
845 EndTaskRequest->Success = DestroyWindow(EndTaskRequest->WndHandle);
846 EndTaskRequest->LastError = GetLastError();
847 }
848 else
849 {
850 EndTaskRequest->Success = FALSE;
851 }
852 }
853 else
854 {
855 EndTaskRequest->Success = TRUE;
856 EndTaskRequest->LastError = ERROR_SUCCESS;
857 }
858
859 return STATUS_SUCCESS;
860 }
861
862 CSR_API(SrvRecordShutdownReason)
863 {
864 DPRINT1("%s not yet implemented\n", __FUNCTION__);
865 return STATUS_NOT_IMPLEMENTED;
866 }
867
868 /* EOF */