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