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 */ 880