1 /* 2 * Copyright 2005-2008, Ingo Weinhold, bonefish@users.sf.net. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <new> 7 8 #include <signal.h> 9 #include <unistd.h> 10 11 #include <Alert.h> 12 #include <AppFileInfo.h> 13 #include <AppMisc.h> 14 #include <Autolock.h> 15 #include <Bitmap.h> 16 #include <Button.h> 17 #include <File.h> 18 #include <Message.h> 19 #include <MessagePrivate.h> 20 #include <RegistrarDefs.h> 21 #include <Roster.h> // for B_REQUEST_QUIT 22 #include <Screen.h> 23 #include <TextView.h> 24 #include <View.h> 25 #include <Window.h> 26 27 #include <TokenSpace.h> 28 #include <util/DoublyLinkedList.h> 29 30 #ifdef __HAIKU__ 31 #include <syscalls.h> 32 #endif 33 34 #include "AppInfoListMessagingTargetSet.h" 35 #include "Debug.h" 36 #include "EventQueue.h" 37 #include "MessageDeliverer.h" 38 #include "MessageEvent.h" 39 #include "Registrar.h" 40 #include "RosterAppInfo.h" 41 #include "ShutdownProcess.h" 42 #include "TRoster.h" 43 44 using std::nothrow; 45 using namespace BPrivate; 46 47 // The time span a non-background application has after the quit message has 48 // been delivered (more precisely: has been handed over to the 49 // MessageDeliverer). 50 static const bigtime_t kAppQuitTimeout = 3000000; // 3 s 51 52 // The time span a background application has after the quit message has been 53 // delivered (more precisely: has been handed over to the MessageDeliverer). 54 static const bigtime_t kBackgroundAppQuitTimeout = 3000000; // 3 s 55 56 // The time span non-app processes have after the TERM signal has been send 57 // to them before they get a KILL signal. 58 static const bigtime_t kNonAppQuitTimeout = 500000; // 0.5 s 59 60 // The time span the app that has aborted the shutdown shall be displayed in 61 // the shutdown window before closing it automatically. 62 static const bigtime_t kDisplayAbortingAppTimeout = 3000000; // 3 s 63 64 static const int kStripeWidth = 30; 65 static const int kIconVSpacing = 6; 66 static const int kIconSize = 32; 67 68 // message what fields (must not clobber the registrar's message namespace) 69 enum { 70 MSG_PHASE_TIMED_OUT = 'phto', 71 MSG_DONE = 'done', 72 MSG_KILL_APPLICATION = 'kill', 73 MSG_CANCEL_SHUTDOWN = 'cncl', 74 MSG_REBOOT_SYSTEM = 'lbot', 75 }; 76 77 // internal events 78 enum { 79 NO_EVENT, 80 ABORT_EVENT, 81 TIMEOUT_EVENT, 82 APP_QUIT_EVENT, 83 KILL_APP_EVENT, 84 REBOOT_SYSTEM_EVENT, 85 DEBUG_EVENT 86 }; 87 88 // phases 89 enum { 90 INVALID_PHASE = -1, 91 USER_APP_TERMINATION_PHASE = 0, 92 SYSTEM_APP_TERMINATION_PHASE = 1, 93 BACKGROUND_APP_TERMINATION_PHASE = 2, 94 OTHER_PROCESSES_TERMINATION_PHASE = 3, 95 ABORTED_PHASE = 4, 96 DONE_PHASE = 5, 97 }; 98 99 100 // inverse_compare_by_registration_time 101 static 102 bool 103 inverse_compare_by_registration_time(const RosterAppInfo *info1, 104 const RosterAppInfo *info2) 105 { 106 return (info2->registration_time < info1->registration_time); 107 } 108 109 // throw_error 110 /*! \brief Used to avoid type matching problems when throwing a constant. 111 */ 112 static inline 113 void 114 throw_error(status_t error) 115 { 116 throw error; 117 } 118 119 // TimeoutEvent 120 class ShutdownProcess::TimeoutEvent : public MessageEvent { 121 public: 122 TimeoutEvent(BHandler *target) 123 : MessageEvent(0, target, MSG_PHASE_TIMED_OUT) 124 { 125 SetAutoDelete(false); 126 127 fMessage.AddInt32("phase", INVALID_PHASE); 128 fMessage.AddInt32("team", -1); 129 } 130 131 void SetPhase(int32 phase) 132 { 133 fMessage.ReplaceInt32("phase", phase); 134 } 135 136 void SetTeam(team_id team) 137 { 138 fMessage.ReplaceInt32("team", team); 139 } 140 141 static int32 GetMessagePhase(BMessage *message) 142 { 143 int32 phase; 144 if (message->FindInt32("phase", &phase) != B_OK) 145 phase = INVALID_PHASE; 146 147 return phase; 148 } 149 150 static int32 GetMessageTeam(BMessage *message) 151 { 152 team_id team; 153 if (message->FindInt32("team", &team) != B_OK) 154 team = -1; 155 156 return team; 157 } 158 }; 159 160 161 // InternalEvent 162 class ShutdownProcess::InternalEvent 163 : public DoublyLinkedListLinkImpl<InternalEvent> { 164 public: 165 InternalEvent(uint32 type, team_id team, int32 phase) 166 : fType(type), 167 fTeam(team), 168 fPhase(phase) 169 { 170 } 171 172 uint32 Type() const { return fType; } 173 team_id Team() const { return fTeam; } 174 int32 Phase() const { return fPhase; } 175 176 private: 177 uint32 fType; 178 int32 fTeam; 179 int32 fPhase; 180 }; 181 182 183 // InternalEventList 184 struct ShutdownProcess::InternalEventList : DoublyLinkedList<InternalEvent> { 185 }; 186 187 188 // QuitRequestReplyHandler 189 class ShutdownProcess::QuitRequestReplyHandler : public BHandler { 190 public: 191 QuitRequestReplyHandler(ShutdownProcess *shutdownProcess) 192 : BHandler("shutdown quit reply handler"), 193 fShutdownProcess(shutdownProcess) 194 { 195 } 196 197 virtual void MessageReceived(BMessage *message) 198 { 199 switch (message->what) { 200 case B_REPLY: 201 { 202 bool result; 203 thread_id thread; 204 if (message->FindBool("result", &result) == B_OK 205 && message->FindInt32("thread", &thread) == B_OK) { 206 if (!result) 207 fShutdownProcess->_NegativeQuitRequestReply(thread); 208 } 209 210 break; 211 } 212 213 default: 214 BHandler::MessageReceived(message); 215 break; 216 } 217 } 218 219 private: 220 ShutdownProcess *fShutdownProcess; 221 }; 222 223 224 // ShutdownWindow 225 class ShutdownProcess::ShutdownWindow : public BWindow { 226 public: 227 ShutdownWindow() 228 : BWindow(BRect(0, 0, 200, 100), "Shutdown Status", 229 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 230 B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE | B_NOT_MINIMIZABLE 231 | B_NOT_ZOOMABLE | B_NOT_CLOSABLE, B_ALL_WORKSPACES), 232 fKillAppMessage(NULL), 233 fCurrentApp(-1) 234 { 235 } 236 237 ~ShutdownWindow() 238 { 239 for (int32 i = 0; AppInfo *info = (AppInfo*)fAppInfos.ItemAt(i); i++) { 240 delete info; 241 } 242 } 243 244 virtual bool QuitRequested() 245 { 246 return false; 247 } 248 249 status_t Init(BMessenger target) 250 { 251 // create the views 252 253 // root view 254 fRootView = new(nothrow) TAlertView(BRect(0, 0, 10, 10), "app icons", 255 B_FOLLOW_NONE, 0); 256 if (!fRootView) 257 return B_NO_MEMORY; 258 fRootView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 259 AddChild(fRootView); 260 261 // text view 262 fTextView = new(nothrow) BTextView(BRect(0, 0, 10, 10), "text", 263 BRect(0, 0, 10, 10), B_FOLLOW_NONE); 264 if (!fTextView) 265 return B_NO_MEMORY; 266 fTextView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 267 rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR); 268 fTextView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor); 269 fTextView->MakeEditable(false); 270 fTextView->MakeSelectable(false); 271 fTextView->SetWordWrap(true); 272 fRootView->AddChild(fTextView); 273 274 // kill app button 275 fKillAppButton = new(nothrow) BButton(BRect(0, 0, 10, 10), "kill app", 276 "Kill Application", NULL, B_FOLLOW_NONE); 277 if (!fKillAppButton) 278 return B_NO_MEMORY; 279 fRootView->AddChild(fKillAppButton); 280 281 BMessage *message = new BMessage(MSG_KILL_APPLICATION); 282 if (!message) 283 return B_NO_MEMORY; 284 message->AddInt32("team", -1); 285 fKillAppMessage = message; 286 fKillAppButton->SetMessage(message); 287 fKillAppButton->SetTarget(target); 288 289 // cancel shutdown button 290 fCancelShutdownButton = new(nothrow) BButton(BRect(0, 0, 10, 10), 291 "cancel shutdown", "Cancel Shutdown", NULL, B_FOLLOW_NONE); 292 if (!fCancelShutdownButton) 293 return B_NO_MEMORY; 294 fRootView->AddChild(fCancelShutdownButton); 295 296 message = new BMessage(MSG_CANCEL_SHUTDOWN); 297 if (!message) 298 return B_NO_MEMORY; 299 fCancelShutdownButton->SetMessage(message); 300 fCancelShutdownButton->SetTarget(target); 301 302 // reboot system button 303 fRebootSystemButton = new(nothrow) BButton(BRect(0, 0, 10, 10), 304 "reboot", "Restart System", NULL, B_FOLLOW_NONE); 305 if (!fRebootSystemButton) 306 return B_NO_MEMORY; 307 fRebootSystemButton->Hide(); 308 fRootView->AddChild(fRebootSystemButton); 309 310 message = new BMessage(MSG_REBOOT_SYSTEM); 311 if (!message) 312 return B_NO_MEMORY; 313 fRebootSystemButton->SetMessage(message); 314 fRebootSystemButton->SetTarget(target); 315 316 // aborted OK button 317 fAbortedOKButton = new(nothrow) BButton(BRect(0, 0, 10, 10), 318 "ok", "OK", NULL, B_FOLLOW_NONE); 319 if (!fAbortedOKButton) 320 return B_NO_MEMORY; 321 fAbortedOKButton->Hide(); 322 fRootView->AddChild(fAbortedOKButton); 323 324 message = new BMessage(MSG_CANCEL_SHUTDOWN); 325 if (!message) 326 return B_NO_MEMORY; 327 fAbortedOKButton->SetMessage(message); 328 fAbortedOKButton->SetTarget(target); 329 330 // compute the sizes 331 static const int kHSpacing = 10; 332 static const int kVSpacing = 10; 333 static const int kInnerHSpacing = 5; 334 static const int kInnerVSpacing = 8; 335 336 // buttons 337 fKillAppButton->ResizeToPreferred(); 338 fCancelShutdownButton->ResizeToPreferred(); 339 fRebootSystemButton->MakeDefault(true); 340 fRebootSystemButton->ResizeToPreferred(); 341 fAbortedOKButton->MakeDefault(true); 342 fAbortedOKButton->ResizeToPreferred(); 343 344 BRect rect(fKillAppButton->Frame()); 345 int buttonWidth = rect.IntegerWidth() + 1; 346 int buttonHeight = rect.IntegerHeight() + 1; 347 348 rect = fCancelShutdownButton->Frame(); 349 if (rect.IntegerWidth() >= buttonWidth) 350 buttonWidth = rect.IntegerWidth() + 1; 351 352 int defaultButtonHeight 353 = fRebootSystemButton->Frame().IntegerHeight() + 1; 354 355 // text view 356 fTextView->SetText("two\nlines"); 357 int textHeight = (int)fTextView->TextHeight(0, 1) + 1; 358 359 int rightPartX = kStripeWidth + kIconSize / 2 + 1; 360 int textX = rightPartX + kInnerHSpacing; 361 int textY = kVSpacing; 362 int buttonsY = textY + textHeight + kInnerVSpacing; 363 int nonDefaultButtonsY = buttonsY 364 + (defaultButtonHeight - buttonHeight) / 2; 365 int rightPartWidth = 2 * buttonWidth + kInnerHSpacing; 366 int width = rightPartX + rightPartWidth + kHSpacing; 367 int height = buttonsY + defaultButtonHeight + kVSpacing; 368 369 // now layout the views 370 371 // text view 372 fTextView->MoveTo(textX, textY); 373 fTextView->ResizeTo(rightPartWidth + rightPartX - textX - 1, 374 textHeight - 1); 375 fTextView->SetTextRect(fTextView->Bounds()); 376 377 // buttons 378 fKillAppButton->MoveTo(rightPartX, nonDefaultButtonsY); 379 fKillAppButton->ResizeTo(buttonWidth - 1, buttonHeight - 1); 380 381 fCancelShutdownButton->MoveTo( 382 rightPartX + buttonWidth + kInnerVSpacing - 1, 383 nonDefaultButtonsY); 384 fCancelShutdownButton->ResizeTo(buttonWidth - 1, buttonHeight - 1); 385 386 fRebootSystemButton->MoveTo( 387 (width - fRebootSystemButton->Frame().IntegerWidth()) / 2, 388 buttonsY); 389 390 fAbortedOKButton->MoveTo( 391 (width - fAbortedOKButton->Frame().IntegerWidth()) / 2, 392 buttonsY); 393 394 // set the root view and window size 395 fRootView->ResizeTo(width - 1, height - 1); 396 ResizeTo(width - 1, height - 1); 397 398 // move the window to the same position as BAlerts 399 BScreen screen(this); 400 BRect screenFrame = screen.Frame(); 401 402 MoveTo(screenFrame.left + (screenFrame.Width() - width) / 2.0, 403 screenFrame.top + screenFrame.Height() / 4.0 - ceilf(height / 3.0)); 404 405 return B_OK; 406 } 407 408 status_t AddApp(team_id team, BBitmap *miniIcon, BBitmap *largeIcon) 409 { 410 AppInfo *info = new(nothrow) AppInfo; 411 if (!info) { 412 delete miniIcon; 413 delete largeIcon; 414 return B_NO_MEMORY; 415 } 416 417 info->team = team; 418 info->miniIcon = miniIcon; 419 info->largeIcon = largeIcon; 420 421 if (!fAppInfos.AddItem(info)) { 422 delete info; 423 return B_NO_MEMORY; 424 } 425 426 return B_OK; 427 } 428 429 void RemoveApp(team_id team) 430 { 431 int32 index = _AppInfoIndexOf(team); 432 if (index < 0) 433 return; 434 435 if (team == fCurrentApp) 436 SetCurrentApp(-1); 437 438 AppInfo *info = (AppInfo*)fAppInfos.RemoveItem(index); 439 delete info; 440 } 441 442 void SetCurrentApp(team_id team) 443 { 444 AppInfo *info = (team >= 0 ? _AppInfoFor(team) : NULL); 445 446 fCurrentApp = team; 447 fRootView->SetAppInfo(info); 448 449 fKillAppMessage->ReplaceInt32("team", team); 450 } 451 452 void SetText(const char *text) 453 { 454 fTextView->SetText(text); 455 } 456 457 void SetCancelShutdownButtonEnabled(bool enable) 458 { 459 fCancelShutdownButton->SetEnabled(enable); 460 } 461 462 void SetKillAppButtonEnabled(bool enable) 463 { 464 if (enable != fKillAppButton->IsEnabled()) { 465 fKillAppButton->SetEnabled(enable); 466 467 if (enable) 468 fKillAppButton->Show(); 469 else 470 fKillAppButton->Hide(); 471 } 472 } 473 474 void SetWaitForShutdown() 475 { 476 fKillAppButton->Hide(); 477 fCancelShutdownButton->Hide(); 478 fRebootSystemButton->MakeDefault(true); 479 fRebootSystemButton->Show(); 480 481 SetTitle("System is Shut Down"); 482 fTextView->SetText("It's now safe to turn off the computer."); 483 } 484 485 void SetWaitForAbortedOK() 486 { 487 fKillAppButton->Hide(); 488 fCancelShutdownButton->Hide(); 489 fAbortedOKButton->MakeDefault(true); 490 fAbortedOKButton->Show(); 491 // TODO: Temporary work-around for a Haiku bug. 492 fAbortedOKButton->Invalidate(); 493 494 SetTitle("Shutdown Aborted"); 495 } 496 497 private: 498 struct AppInfo { 499 team_id team; 500 BBitmap *miniIcon; 501 BBitmap *largeIcon; 502 503 ~AppInfo() 504 { 505 delete miniIcon; 506 delete largeIcon; 507 } 508 }; 509 510 int32 _AppInfoIndexOf(team_id team) 511 { 512 if (team < 0) 513 return -1; 514 515 for (int32 i = 0; AppInfo *info = (AppInfo*)fAppInfos.ItemAt(i); i++) { 516 if (info->team == team) 517 return i; 518 } 519 520 return -1; 521 } 522 523 AppInfo *_AppInfoFor(team_id team) 524 { 525 int32 index = _AppInfoIndexOf(team); 526 return (index >= 0 ? (AppInfo*)fAppInfos.ItemAt(index) : NULL); 527 } 528 529 class TAlertView : public BView { 530 public: 531 TAlertView(BRect frame, const char *name, uint32 resizeMask, uint32 flags) 532 : BView(frame, name, resizeMask, flags | B_WILL_DRAW), 533 fAppInfo(NULL) 534 { 535 } 536 537 virtual void Draw(BRect updateRect) 538 { 539 BRect stripeRect = Bounds(); 540 stripeRect.right = kStripeWidth; 541 SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); 542 FillRect(stripeRect); 543 544 if (fAppInfo && fAppInfo->largeIcon) { 545 if (fAppInfo->largeIcon->ColorSpace() == B_RGBA32) { 546 SetDrawingMode(B_OP_ALPHA); 547 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 548 } else 549 SetDrawingMode(B_OP_OVER); 550 551 DrawBitmapAsync(fAppInfo->largeIcon, 552 BPoint(kStripeWidth - kIconSize / 2, kIconVSpacing)); 553 } 554 } 555 556 void SetAppInfo(AppInfo *info) 557 { 558 fAppInfo = info; 559 Invalidate(); 560 } 561 562 private: 563 const AppInfo *fAppInfo; 564 }; 565 566 private: 567 BList fAppInfos; 568 TAlertView *fRootView; 569 BTextView *fTextView; 570 BButton *fKillAppButton; 571 BButton *fCancelShutdownButton; 572 BButton *fRebootSystemButton; 573 BButton *fAbortedOKButton; 574 BMessage *fKillAppMessage; 575 team_id fCurrentApp; 576 }; 577 578 579 // #pragma mark - 580 581 // constructor 582 ShutdownProcess::ShutdownProcess(TRoster *roster, EventQueue *eventQueue) 583 : BLooper("shutdown process"), 584 EventMaskWatcher(BMessenger(this), B_REQUEST_QUIT), 585 fWorkerLock("worker lock"), 586 fRequest(NULL), 587 fRoster(roster), 588 fEventQueue(eventQueue), 589 fTimeoutEvent(NULL), 590 fInternalEvents(NULL), 591 fInternalEventSemaphore(-1), 592 fQuitRequestReplyHandler(NULL), 593 fWorker(-1), 594 fCurrentPhase(INVALID_PHASE), 595 fShutdownError(B_ERROR), 596 fHasGUI(false), 597 fReboot(false), 598 fRequestReplySent(false), 599 fWindow(NULL) 600 { 601 } 602 603 // destructor 604 ShutdownProcess::~ShutdownProcess() 605 { 606 // terminate the GUI 607 if (fHasGUI && fWindow && fWindow->Lock()) 608 fWindow->Quit(); 609 610 // remove and delete the quit request reply handler 611 if (fQuitRequestReplyHandler) { 612 BAutolock _(this); 613 RemoveHandler(fQuitRequestReplyHandler); 614 delete fQuitRequestReplyHandler; 615 } 616 617 // remove and delete the timeout event 618 if (fTimeoutEvent) { 619 fEventQueue->RemoveEvent(fTimeoutEvent); 620 621 delete fTimeoutEvent; 622 } 623 624 // remove the application quit watcher 625 fRoster->RemoveWatcher(this); 626 627 // If an error occurred (e.g. the shutdown process was cancelled), the 628 // roster should accept applications again. 629 if (fShutdownError != B_OK) 630 fRoster->SetShuttingDown(false); 631 632 // delete the internal event semaphore 633 if (fInternalEventSemaphore >= 0) 634 delete_sem(fInternalEventSemaphore); 635 636 // wait for the worker thread to terminate 637 if (fWorker >= 0) { 638 int32 result; 639 wait_for_thread(fWorker, &result); 640 } 641 642 // delete all internal events and the queue 643 if (fInternalEvents) { 644 while (InternalEvent *event = fInternalEvents->First()) { 645 fInternalEvents->Remove(event); 646 delete event; 647 } 648 649 delete fInternalEvents; 650 } 651 652 // send a reply to the request and delete it 653 _SendReply(fShutdownError); 654 delete fRequest; 655 } 656 657 // Init 658 status_t 659 ShutdownProcess::Init(BMessage *request) 660 { 661 PRINT(("ShutdownProcess::Init()\n")); 662 663 // create and add the quit request reply handler 664 fQuitRequestReplyHandler = new(nothrow) QuitRequestReplyHandler(this); 665 if (!fQuitRequestReplyHandler) 666 RETURN_ERROR(B_NO_MEMORY); 667 AddHandler(fQuitRequestReplyHandler); 668 669 // create the timeout event 670 fTimeoutEvent = new(nothrow) TimeoutEvent(this); 671 if (!fTimeoutEvent) 672 RETURN_ERROR(B_NO_MEMORY); 673 674 // create the event list 675 fInternalEvents = new(nothrow) InternalEventList; 676 if (!fInternalEvents) 677 RETURN_ERROR(B_NO_MEMORY); 678 679 // create the event sempahore 680 fInternalEventSemaphore = create_sem(0, "shutdown events"); 681 if (fInternalEventSemaphore < 0) 682 RETURN_ERROR(fInternalEventSemaphore); 683 684 // init the app server connection 685 fHasGUI = (Registrar::App()->InitGUIContext() == B_OK); 686 687 // tell TRoster not to accept new applications anymore 688 fRoster->SetShuttingDown(true); 689 690 // start watching application quits 691 status_t error = fRoster->AddWatcher(this); 692 if (error != B_OK) { 693 fRoster->SetShuttingDown(false); 694 RETURN_ERROR(error); 695 } 696 697 // get a list of all applications to shut down and sort them 698 error = fRoster->GetShutdownApps(fUserApps, fSystemApps, fBackgroundApps, 699 fVitalSystemApps); 700 if (error != B_OK) { 701 fRoster->RemoveWatcher(this); 702 fRoster->SetShuttingDown(false); 703 RETURN_ERROR(error); 704 } 705 706 fUserApps.Sort(&inverse_compare_by_registration_time); 707 fSystemApps.Sort(&inverse_compare_by_registration_time); 708 709 // start the worker thread 710 fWorker = spawn_thread(_WorkerEntry, "shutdown worker", B_NORMAL_PRIORITY, 711 this); 712 if (fWorker < 0) { 713 fRoster->RemoveWatcher(this); 714 fRoster->SetShuttingDown(false); 715 RETURN_ERROR(fWorker); 716 } 717 718 // everything went fine: now we own the request 719 fRequest = request; 720 721 if (fRequest->FindBool("reboot", &fReboot) != B_OK) 722 fReboot = false; 723 724 resume_thread(fWorker); 725 726 PRINT(("ShutdownProcess::Init() done\n")); 727 728 return B_OK; 729 } 730 731 // MessageReceived 732 void 733 ShutdownProcess::MessageReceived(BMessage *message) 734 { 735 switch (message->what) { 736 case B_SOME_APP_QUIT: 737 { 738 // get the team 739 team_id team; 740 if (message->FindInt32("be:team", &team) != B_OK) { 741 // should not happen 742 return; 743 } 744 745 PRINT(("ShutdownProcess::MessageReceived(): B_SOME_APP_QUIT: %ld\n", 746 team)); 747 748 // remove the app info from the respective list 749 int32 phase; 750 RosterAppInfo *info; 751 { 752 BAutolock _(fWorkerLock); 753 754 info = fUserApps.InfoFor(team); 755 if (info) 756 fUserApps.RemoveInfo(info); 757 else if ((info = fSystemApps.InfoFor(team))) 758 fSystemApps.RemoveInfo(info); 759 else if ((info = fBackgroundApps.InfoFor(team))) 760 fBackgroundApps.RemoveInfo(info); 761 else // not found 762 return; 763 764 phase = fCurrentPhase; 765 } 766 767 // post the event 768 _PushEvent(APP_QUIT_EVENT, team, phase); 769 770 delete info; 771 772 break; 773 } 774 775 case MSG_PHASE_TIMED_OUT: 776 { 777 // get the phase the event is intended for 778 int32 phase = TimeoutEvent::GetMessagePhase(message); 779 team_id team = TimeoutEvent::GetMessageTeam(message);; 780 PRINT(("MSG_PHASE_TIMED_OUT: phase: %ld, team: %ld\n", phase, team)); 781 782 BAutolock _(fWorkerLock); 783 784 if (phase == INVALID_PHASE || phase != fCurrentPhase) 785 return; 786 787 // post the event 788 _PushEvent(TIMEOUT_EVENT, team, phase); 789 790 break; 791 } 792 793 case MSG_KILL_APPLICATION: 794 { 795 team_id team; 796 if (message->FindInt32("team", &team) != B_OK) 797 break; 798 799 // post the event 800 _PushEvent(KILL_APP_EVENT, team, fCurrentPhase); 801 break; 802 } 803 804 case MSG_CANCEL_SHUTDOWN: 805 { 806 // post the event 807 _PushEvent(ABORT_EVENT, -1, fCurrentPhase); 808 break; 809 } 810 811 case MSG_REBOOT_SYSTEM: 812 { 813 // post the event 814 _PushEvent(REBOOT_SYSTEM_EVENT, -1, INVALID_PHASE); 815 break; 816 } 817 818 case MSG_DONE: 819 { 820 // notify the registrar that we're done 821 be_app->PostMessage(B_REG_SHUTDOWN_FINISHED, be_app); 822 break; 823 } 824 825 case B_REG_TEAM_DEBUGGER_ALERT: 826 { 827 bool stopShutdown; 828 if (message->FindBool("stop shutdown", &stopShutdown) == B_OK 829 && stopShutdown) { 830 // post abort event to the worker 831 _PushEvent(ABORT_EVENT, -1, fCurrentPhase); 832 break; 833 } 834 835 bool open; 836 team_id team; 837 if (message->FindInt32("team", &team) != B_OK 838 || message->FindBool("open", &open) != B_OK) 839 break; 840 841 BAutolock _(fWorkerLock); 842 if (open) { 843 PRINT(("B_REG_TEAM_DEBUGGER_ALERT: insert %ld\n", team)); 844 fDebuggedTeams.insert(team); 845 } else { 846 PRINT(("B_REG_TEAM_DEBUGGER_ALERT: remove %ld\n", team)); 847 fDebuggedTeams.erase(team); 848 _PushEvent(DEBUG_EVENT, -1, fCurrentPhase); 849 } 850 break; 851 } 852 853 default: 854 BLooper::MessageReceived(message); 855 break; 856 } 857 } 858 859 // SendReply 860 void 861 ShutdownProcess::SendReply(BMessage *request, status_t error) 862 { 863 if (error == B_OK) { 864 BMessage reply(B_REG_SUCCESS); 865 request->SendReply(&reply); 866 } else { 867 BMessage reply(B_REG_ERROR); 868 reply.AddInt32("error", error); 869 request->SendReply(&reply); 870 } 871 } 872 873 // _SendReply 874 void 875 ShutdownProcess::_SendReply(status_t error) 876 { 877 if (!fRequestReplySent) { 878 SendReply(fRequest, error); 879 fRequestReplySent = true; 880 } 881 } 882 883 // _SetPhase 884 void 885 ShutdownProcess::_SetPhase(int32 phase) 886 { 887 BAutolock _(fWorkerLock); 888 889 if (phase == fCurrentPhase) 890 return; 891 892 fCurrentPhase = phase; 893 894 // remove the timeout event scheduled for the previous phase 895 fEventQueue->RemoveEvent(fTimeoutEvent); 896 } 897 898 // _ScheduleTimeoutEvent 899 void 900 ShutdownProcess::_ScheduleTimeoutEvent(bigtime_t timeout, team_id team) 901 { 902 BAutolock _(fWorkerLock); 903 904 // remove the timeout event 905 fEventQueue->RemoveEvent(fTimeoutEvent); 906 907 // set the event's phase, team and time 908 fTimeoutEvent->SetPhase(fCurrentPhase); 909 fTimeoutEvent->SetTeam(team); 910 fTimeoutEvent->SetTime(system_time() + timeout); 911 912 // add the event 913 fEventQueue->AddEvent(fTimeoutEvent); 914 } 915 916 // _SetShowShutdownWindow 917 void 918 ShutdownProcess::_SetShowShutdownWindow(bool show) 919 { 920 if (fHasGUI) { 921 BAutolock _(fWindow); 922 923 if (show == fWindow->IsHidden()) { 924 if (show) 925 fWindow->Show(); 926 else 927 fWindow->Hide(); 928 } 929 } 930 } 931 932 // _InitShutdownWindow 933 void 934 ShutdownProcess::_InitShutdownWindow() 935 { 936 // prepare the window 937 if (fHasGUI) { 938 fWindow = new(nothrow) ShutdownWindow; 939 if (fWindow != NULL) { 940 status_t error = fWindow->Init(BMessenger(this)); 941 if (error != B_OK) { 942 delete fWindow; 943 fWindow = NULL; 944 } 945 } 946 947 // add the applications 948 if (fWindow) { 949 BAutolock _(fWorkerLock); 950 _AddShutdownWindowApps(fUserApps); 951 _AddShutdownWindowApps(fSystemApps); 952 } else { 953 WARNING(("ShutdownProcess::Init(): Failed to create or init " 954 "shutdown window.")); 955 956 fHasGUI = false; 957 } 958 } 959 } 960 961 // _AddShutdownWindowApps 962 void 963 ShutdownProcess::_AddShutdownWindowApps(AppInfoList &infos) 964 { 965 if (!fHasGUI) 966 return; 967 968 for (AppInfoList::Iterator it = infos.It(); it.IsValid(); ++it) { 969 RosterAppInfo *info = *it; 970 971 // init an app file info 972 BFile file; 973 status_t error = file.SetTo(&info->ref, B_READ_ONLY); 974 if (error != B_OK) { 975 WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to " 976 "open file for app %s: %s\n", info->signature, 977 strerror(error))); 978 continue; 979 } 980 981 BAppFileInfo appFileInfo; 982 error = appFileInfo.SetTo(&file); 983 if (error != B_OK) { 984 WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to " 985 "init app file info for app %s: %s\n", info->signature, 986 strerror(error))); 987 } 988 989 // get the application icons 990 #ifdef __HAIKU__ 991 color_space format = B_RGBA32; 992 #else 993 color_space format = B_CMAP8; 994 #endif 995 996 // mini icon 997 BBitmap *miniIcon = new(nothrow) BBitmap(BRect(0, 0, 15, 15), format); 998 if (miniIcon != NULL) { 999 error = miniIcon->InitCheck(); 1000 if (error == B_OK) 1001 error = appFileInfo.GetTrackerIcon(miniIcon, B_MINI_ICON); 1002 if (error != B_OK) { 1003 delete miniIcon; 1004 miniIcon = NULL; 1005 } 1006 } 1007 1008 // mini icon 1009 BBitmap *largeIcon = new(nothrow) BBitmap(BRect(0, 0, 31, 31), format); 1010 if (largeIcon != NULL) { 1011 error = largeIcon->InitCheck(); 1012 if (error == B_OK) 1013 error = appFileInfo.GetTrackerIcon(largeIcon, B_LARGE_ICON); 1014 if (error != B_OK) { 1015 delete largeIcon; 1016 largeIcon = NULL; 1017 } 1018 } 1019 1020 // add the app 1021 error = fWindow->AddApp(info->team, miniIcon, largeIcon); 1022 if (error != B_OK) { 1023 WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to " 1024 "add app to the shutdown window: %s\n", strerror(error))); 1025 } 1026 } 1027 } 1028 1029 // _RemoveShutdownWindowApp 1030 void 1031 ShutdownProcess::_RemoveShutdownWindowApp(team_id team) 1032 { 1033 if (fHasGUI) { 1034 BAutolock _(fWindow); 1035 1036 fWindow->RemoveApp(team); 1037 } 1038 } 1039 1040 // _SetShutdownWindowCurrentApp 1041 void 1042 ShutdownProcess::_SetShutdownWindowCurrentApp(team_id team) 1043 { 1044 if (fHasGUI) { 1045 BAutolock _(fWindow); 1046 1047 fWindow->SetCurrentApp(team); 1048 } 1049 } 1050 1051 // _SetShutdownWindowText 1052 void 1053 ShutdownProcess::_SetShutdownWindowText(const char *text) 1054 { 1055 if (fHasGUI) { 1056 BAutolock _(fWindow); 1057 1058 fWindow->SetText(text); 1059 } 1060 } 1061 1062 // _SetShutdownWindowCancelButtonEnabled 1063 void 1064 ShutdownProcess::_SetShutdownWindowCancelButtonEnabled(bool enabled) 1065 { 1066 if (fHasGUI) { 1067 BAutolock _(fWindow); 1068 1069 fWindow->SetCancelShutdownButtonEnabled(enabled); 1070 } 1071 } 1072 1073 // _SetShutdownWindowKillButtonEnabled 1074 void 1075 ShutdownProcess::_SetShutdownWindowKillButtonEnabled(bool enabled) 1076 { 1077 if (fHasGUI) { 1078 BAutolock _(fWindow); 1079 1080 fWindow->SetKillAppButtonEnabled(enabled); 1081 } 1082 } 1083 1084 // _SetShutdownWindowWaitForShutdown 1085 void 1086 ShutdownProcess::_SetShutdownWindowWaitForShutdown() 1087 { 1088 if (fHasGUI) { 1089 BAutolock _(fWindow); 1090 1091 fWindow->SetWaitForShutdown(); 1092 } 1093 } 1094 1095 // _SetShutdownWindowWaitForAbortedOK 1096 void 1097 ShutdownProcess::_SetShutdownWindowWaitForAbortedOK() 1098 { 1099 if (fHasGUI) { 1100 BAutolock _(fWindow); 1101 1102 fWindow->SetWaitForAbortedOK(); 1103 } 1104 } 1105 1106 // _NegativeQuitRequestReply 1107 void 1108 ShutdownProcess::_NegativeQuitRequestReply(thread_id thread) 1109 { 1110 BAutolock _(fWorkerLock); 1111 1112 // Note: team ID == team main thread ID under Haiku. When testing under R5 1113 // using the team ID in case of an ABORT_EVENT won't work correctly. But 1114 // this is done only for system apps. 1115 _PushEvent(ABORT_EVENT, thread, fCurrentPhase); 1116 } 1117 1118 // _PrepareShutdownMessage 1119 void 1120 ShutdownProcess::_PrepareShutdownMessage(BMessage &message) const 1121 { 1122 message.what = B_QUIT_REQUESTED; 1123 message.AddBool("_shutdown_", true); 1124 1125 BMessage::Private(message).SetReply(BMessenger(fQuitRequestReplyHandler)); 1126 } 1127 1128 // _ShutDown 1129 status_t 1130 ShutdownProcess::_ShutDown() 1131 { 1132 PRINT(("Invoking _kern_shutdown(%d)\n", fReboot)); 1133 1134 #ifdef __HAIKU__ 1135 RETURN_ERROR(_kern_shutdown(fReboot)); 1136 #else 1137 // we can't do anything on R5 1138 return B_ERROR; 1139 #endif 1140 } 1141 1142 1143 // _PushEvent 1144 status_t 1145 ShutdownProcess::_PushEvent(uint32 eventType, team_id team, int32 phase) 1146 { 1147 InternalEvent *event = new(nothrow) InternalEvent(eventType, team, phase); 1148 if (!event) { 1149 ERROR(("ShutdownProcess::_PushEvent(): Failed to create event!\n")); 1150 1151 return B_NO_MEMORY; 1152 } 1153 1154 BAutolock _(fWorkerLock); 1155 1156 fInternalEvents->Add(event); 1157 release_sem(fInternalEventSemaphore); 1158 1159 return B_OK; 1160 } 1161 1162 // _GetNextEvent 1163 status_t 1164 ShutdownProcess::_GetNextEvent(uint32 &eventType, thread_id &team, int32 &phase, 1165 bool block) 1166 { 1167 while (true) { 1168 // acquire the semaphore 1169 if (block) { 1170 status_t error; 1171 do { 1172 error = acquire_sem(fInternalEventSemaphore); 1173 } while (error == B_INTERRUPTED); 1174 1175 if (error != B_OK) 1176 return error; 1177 } else { 1178 status_t error = acquire_sem_etc(fInternalEventSemaphore, 1, 1179 B_RELATIVE_TIMEOUT, 0); 1180 if (error != B_OK) { 1181 eventType = NO_EVENT; 1182 return B_OK; 1183 } 1184 } 1185 1186 // get the event 1187 BAutolock _(fWorkerLock); 1188 1189 InternalEvent *event = fInternalEvents->Head(); 1190 fInternalEvents->Remove(event); 1191 1192 eventType = event->Type(); 1193 team = event->Team(); 1194 phase = event->Phase(); 1195 1196 delete event; 1197 1198 // if the event is an obsolete timeout event, we drop it right here 1199 if (eventType == TIMEOUT_EVENT && phase != fCurrentPhase) 1200 continue; 1201 1202 break; 1203 } 1204 1205 // notify the window, if an app has been removed 1206 if (eventType == APP_QUIT_EVENT) 1207 _RemoveShutdownWindowApp(team); 1208 1209 return B_OK; 1210 } 1211 1212 // _WorkerEntry 1213 status_t 1214 ShutdownProcess::_WorkerEntry(void *data) 1215 { 1216 return ((ShutdownProcess*)data)->_Worker(); 1217 } 1218 1219 // _Worker 1220 status_t 1221 ShutdownProcess::_Worker() 1222 { 1223 try { 1224 _WorkerDoShutdown(); 1225 fShutdownError = B_OK; 1226 } catch (status_t error) { 1227 PRINT(("ShutdownProcess::_Worker(): error while shutting down: %s\n", 1228 strerror(error))); 1229 1230 fShutdownError = error; 1231 } 1232 1233 // this can happen only, if the shutdown process failed or was aborted: 1234 // notify the looper 1235 _SetPhase(DONE_PHASE); 1236 PostMessage(MSG_DONE); 1237 1238 return B_OK; 1239 } 1240 1241 // _WorkerDoShutdown 1242 void 1243 ShutdownProcess::_WorkerDoShutdown() 1244 { 1245 PRINT(("ShutdownProcess::_WorkerDoShutdown()\n")); 1246 1247 // If we are here, the shutdown process has been initiated successfully, 1248 // that is, if an asynchronous BRoster::Shutdown() was requested, we 1249 // notify the caller at this point. 1250 bool synchronous; 1251 if (fRequest->FindBool("synchronous", &synchronous) == B_OK 1252 && !synchronous) { 1253 _SendReply(B_OK); 1254 } 1255 1256 // ask the user to confirm the shutdown, if desired 1257 bool askUser; 1258 if (fHasGUI && fRequest->FindBool("confirm", &askUser) == B_OK && askUser) { 1259 const char *title = (fReboot ? "Restart?" : "Shut Down?"); 1260 const char *text = (fReboot 1261 ? "Do you really want to restart the system?" 1262 : "Do you really want to shut down the system?"); 1263 const char *buttonText = (fReboot ? "Restart" : "Shut Down"); 1264 BAlert *alert = new BAlert(title, text, "Cancel", buttonText, NULL, 1265 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1266 alert->SetShortcut(0, B_ESCAPE); 1267 int32 result = alert->Go(); 1268 1269 if (result != 1) 1270 throw_error(B_SHUTDOWN_CANCELLED); 1271 } 1272 1273 // make the shutdown window ready and show it 1274 _InitShutdownWindow(); 1275 _SetShutdownWindowCurrentApp(-1); 1276 _SetShutdownWindowText("Tidying things up a bit."); 1277 _SetShutdownWindowCancelButtonEnabled(true); 1278 _SetShutdownWindowKillButtonEnabled(false); 1279 _SetShowShutdownWindow(true); 1280 1281 // sync 1282 sync(); 1283 1284 // phase 1: terminate the user apps 1285 _SetPhase(USER_APP_TERMINATION_PHASE); 1286 _QuitApps(fUserApps, false); 1287 _WaitForDebuggedTeams(); 1288 1289 // phase 2: terminate the system apps 1290 _SetPhase(SYSTEM_APP_TERMINATION_PHASE); 1291 _QuitApps(fSystemApps, true); 1292 _WaitForDebuggedTeams(); 1293 1294 // phase 3: terminate the background apps 1295 _SetPhase(BACKGROUND_APP_TERMINATION_PHASE); 1296 _QuitBackgroundApps(); 1297 _WaitForDebuggedTeams(); 1298 1299 // phase 4: terminate the other processes 1300 _SetPhase(OTHER_PROCESSES_TERMINATION_PHASE); 1301 _QuitNonApps(); 1302 _ScheduleTimeoutEvent(kBackgroundAppQuitTimeout, -1); 1303 _WaitForBackgroundApps(); 1304 _KillBackgroundApps(); 1305 _WaitForDebuggedTeams(); 1306 1307 // we're through: do the shutdown 1308 _SetPhase(DONE_PHASE); 1309 if (fReboot) 1310 _SetShutdownWindowText("Restarting" B_UTF8_ELLIPSIS); 1311 else 1312 _SetShutdownWindowText("Shutting down" B_UTF8_ELLIPSIS); 1313 _ShutDown(); 1314 _SetShutdownWindowWaitForShutdown(); 1315 1316 PRINT((" _kern_shutdown() failed\n")); 1317 1318 // shutdown failed: This can happen for power off mode -- reboot should 1319 // always work. 1320 if (fHasGUI) { 1321 // wait for the reboot event 1322 uint32 event; 1323 do { 1324 team_id team; 1325 int32 phase; 1326 status_t error = _GetNextEvent(event, team, phase, true); 1327 if (error != B_OK) 1328 break; 1329 } while (event != REBOOT_SYSTEM_EVENT); 1330 1331 #ifdef __HAIKU__ 1332 _kern_shutdown(true); 1333 #endif 1334 } 1335 1336 // either there's no GUI or reboot failed: we enter the kernel debugger 1337 // instead 1338 #ifdef __HAIKU__ 1339 // TODO: Introduce the syscall. 1340 // while (true) { 1341 // _kern_kernel_debugger("The system is shut down. It's now safe to turn " 1342 // "off the computer."); 1343 // } 1344 #endif 1345 } 1346 1347 1348 bool 1349 ShutdownProcess::_WaitForApp(team_id team, AppInfoList *list, bool systemApps) 1350 { 1351 uint32 event; 1352 do { 1353 team_id eventTeam; 1354 int32 phase; 1355 status_t error = _GetNextEvent(event, eventTeam, phase, true); 1356 if (error != B_OK) 1357 throw_error(error); 1358 1359 if (event == APP_QUIT_EVENT && eventTeam == team) 1360 return true; 1361 1362 if (event == TIMEOUT_EVENT && eventTeam == team) 1363 return false; 1364 1365 if (event == ABORT_EVENT) { 1366 if (systemApps) { 1367 // If the app requests aborting the shutdown, we don't need 1368 // to wait any longer. It has processed the request and 1369 // won't quit by itself. We ignore this for system apps. 1370 if (eventTeam == team) 1371 return false; 1372 } else { 1373 PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled " 1374 "by team %ld (-1 => user)\n", eventTeam)); 1375 1376 _DisplayAbortingApp(team); 1377 throw_error(B_SHUTDOWN_CANCELLED); 1378 } 1379 } 1380 1381 BAutolock _(fWorkerLock); 1382 if (list != NULL && !list->InfoFor(team)) 1383 return true; 1384 } while (event != NO_EVENT); 1385 1386 return false; 1387 } 1388 1389 1390 void 1391 ShutdownProcess::_QuitApps(AppInfoList &list, bool systemApps) 1392 { 1393 PRINT(("ShutdownProcess::_QuitApps(%s)\n", 1394 (systemApps ? "system" : "user"))); 1395 1396 if (systemApps) { 1397 _SetShutdownWindowCancelButtonEnabled(false); 1398 1399 // check one last time for abort events 1400 uint32 event; 1401 do { 1402 team_id team; 1403 int32 phase; 1404 status_t error = _GetNextEvent(event, team, phase, false); 1405 if (error != B_OK) 1406 throw_error(error); 1407 1408 if (event == ABORT_EVENT) { 1409 PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by " 1410 "team %ld (-1 => user)\n", team)); 1411 1412 _DisplayAbortingApp(team); 1413 throw_error(B_SHUTDOWN_CANCELLED); 1414 } 1415 1416 } while (event != NO_EVENT); 1417 } 1418 1419 // prepare the shutdown message 1420 BMessage message; 1421 _PrepareShutdownMessage(message); 1422 1423 // now iterate through the list of apps 1424 while (true) { 1425 // eat events 1426 uint32 event; 1427 do { 1428 team_id team; 1429 int32 phase; 1430 status_t error = _GetNextEvent(event, team, phase, false); 1431 if (error != B_OK) 1432 throw_error(error); 1433 1434 if (!systemApps && event == ABORT_EVENT) { 1435 PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by " 1436 "team %ld (-1 => user)\n", team)); 1437 1438 _DisplayAbortingApp(team); 1439 throw_error(B_SHUTDOWN_CANCELLED); 1440 } 1441 1442 } while (event != NO_EVENT); 1443 1444 // get the first app to quit 1445 team_id team = -1; 1446 port_id port = -1; 1447 char appName[B_FILE_NAME_LENGTH]; 1448 { 1449 BAutolock _(fWorkerLock); 1450 if (!list.IsEmpty()) { 1451 RosterAppInfo *info = *list.It(); 1452 team = info->team; 1453 port = info->port; 1454 strcpy(appName, info->ref.name); 1455 } 1456 } 1457 1458 if (team < 0) { 1459 PRINT(("ShutdownProcess::_QuitApps() done\n")); 1460 return; 1461 } 1462 1463 // set window text 1464 char buffer[1024]; 1465 snprintf(buffer, sizeof(buffer), "Asking \"%s\" to quit.", appName); 1466 _SetShutdownWindowText(buffer); 1467 _SetShutdownWindowCurrentApp(team); 1468 1469 // send the shutdown message to the app 1470 PRINT((" sending team %ld (port: %ld) a shutdown message\n", team, 1471 port)); 1472 SingleMessagingTargetSet target(port, B_PREFERRED_TOKEN); 1473 MessageDeliverer::Default()->DeliverMessage(&message, target); 1474 1475 // schedule a timeout event 1476 _ScheduleTimeoutEvent(kAppQuitTimeout, team); 1477 1478 // wait for the app to die or for the timeout to occur 1479 bool appGone = _WaitForApp(team, &list, systemApps); 1480 if (appGone) { 1481 // fine: the app finished in an orderly manner 1482 } else { 1483 // the app is either blocking on a model alert or blocks for another 1484 // reason 1485 if (!systemApps) 1486 _QuitBlockingApp(list, team, appName, true); 1487 else { 1488 // This is a system app: remove it from the list 1489 BAutolock _(fWorkerLock); 1490 1491 if (RosterAppInfo *info = list.InfoFor(team)) { 1492 list.RemoveInfo(info); 1493 delete info; 1494 } 1495 } 1496 } 1497 } 1498 } 1499 1500 // _QuitBackgroundApps 1501 void 1502 ShutdownProcess::_QuitBackgroundApps() 1503 { 1504 PRINT(("ShutdownProcess::_QuitBackgroundApps()\n")); 1505 1506 _SetShutdownWindowText("Asking background applications to quit."); 1507 1508 // prepare the shutdown message 1509 BMessage message; 1510 _PrepareShutdownMessage(message); 1511 1512 // send shutdown messages to user apps 1513 BAutolock _(fWorkerLock); 1514 1515 AppInfoListMessagingTargetSet targetSet(fBackgroundApps); 1516 1517 if (targetSet.HasNext()) { 1518 PRINT((" sending shutdown message to %ld apps\n", 1519 fBackgroundApps.CountInfos())); 1520 1521 status_t error = MessageDeliverer::Default()->DeliverMessage( 1522 &message, targetSet); 1523 if (error != B_OK) { 1524 WARNING(("_QuitBackgroundApps::_Worker(): Failed to deliver " 1525 "shutdown message to all applications: %s\n", 1526 strerror(error))); 1527 } 1528 } 1529 1530 PRINT(("ShutdownProcess::_QuitBackgroundApps() done\n")); 1531 } 1532 1533 // _WaitForBackgroundApps 1534 void 1535 ShutdownProcess::_WaitForBackgroundApps() 1536 { 1537 PRINT(("ShutdownProcess::_WaitForBackgroundApps()\n")); 1538 1539 // wait for user apps 1540 bool moreApps = true; 1541 while (moreApps) { 1542 { 1543 BAutolock _(fWorkerLock); 1544 moreApps = !fBackgroundApps.IsEmpty(); 1545 } 1546 1547 if (moreApps) { 1548 uint32 event; 1549 team_id team; 1550 int32 phase; 1551 status_t error = _GetNextEvent(event, team, phase, true); 1552 if (error != B_OK) 1553 throw_error(error); 1554 1555 if (event == ABORT_EVENT) { 1556 // ignore: it's too late to abort the shutdown 1557 } 1558 1559 if (event == TIMEOUT_EVENT) 1560 return; 1561 } 1562 } 1563 1564 PRINT(("ShutdownProcess::_WaitForBackgroundApps() done\n")); 1565 } 1566 1567 // _KillBackgroundApps 1568 void 1569 ShutdownProcess::_KillBackgroundApps() 1570 { 1571 PRINT(("ShutdownProcess::_KillBackgroundApps()\n")); 1572 1573 while (true) { 1574 // eat events (we need to be responsive for an abort event) 1575 uint32 event; 1576 do { 1577 team_id team; 1578 int32 phase; 1579 status_t error = _GetNextEvent(event, team, phase, false); 1580 if (error != B_OK) 1581 throw_error(error); 1582 1583 } while (event != NO_EVENT); 1584 1585 // get the first team to kill 1586 team_id team = -1; 1587 char appName[B_FILE_NAME_LENGTH]; 1588 AppInfoList &list = fBackgroundApps; 1589 { 1590 BAutolock _(fWorkerLock); 1591 1592 if (!list.IsEmpty()) { 1593 RosterAppInfo *info = *list.It(); 1594 team = info->team; 1595 strcpy(appName, info->ref.name); 1596 } 1597 } 1598 1599 1600 if (team < 0) { 1601 PRINT(("ShutdownProcess::_KillBackgroundApps() done\n")); 1602 return; 1603 } 1604 1605 // the app is either blocking on a model alert or blocks for another 1606 // reason 1607 _QuitBlockingApp(list, team, appName, false); 1608 } 1609 } 1610 1611 // _QuitNonApps 1612 void 1613 ShutdownProcess::_QuitNonApps() 1614 { 1615 PRINT(("ShutdownProcess::_QuitNonApps()\n")); 1616 1617 _SetShutdownWindowText("Asking other processes to quit."); 1618 1619 // iterate through the remaining teams and send them the TERM signal 1620 int32 cookie = 0; 1621 team_info teamInfo; 1622 while (get_next_team_info(&cookie, &teamInfo) == B_OK) { 1623 if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) { 1624 PRINT((" sending team %ld TERM signal\n", teamInfo.team)); 1625 1626 #ifdef __HAIKU__ 1627 // Note: team ID == team main thread ID under Haiku 1628 send_signal(teamInfo.team, SIGTERM); 1629 #else 1630 // We don't want to do this when testing under R5, since it 1631 // would kill all teams besides our app server and registrar. 1632 #endif 1633 } 1634 } 1635 1636 // give them a bit of time to terminate 1637 // TODO: Instead of just waiting we could periodically check whether the 1638 // processes are already gone to shorten the process. 1639 snooze(kNonAppQuitTimeout); 1640 1641 // iterate through the remaining teams and kill them 1642 cookie = 0; 1643 while (get_next_team_info(&cookie, &teamInfo) == B_OK) { 1644 if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) { 1645 PRINT((" killing team %ld\n", teamInfo.team)); 1646 1647 #ifdef __HAIKU__ 1648 kill_team(teamInfo.team); 1649 #else 1650 // We don't want to do this when testing under R5, since it 1651 // would kill all teams besides our app server and registrar. 1652 #endif 1653 } 1654 } 1655 1656 PRINT(("ShutdownProcess::_QuitNonApps() done\n")); 1657 } 1658 1659 // _QuitBlockingApp 1660 void 1661 ShutdownProcess::_QuitBlockingApp(AppInfoList &list, team_id team, 1662 const char *appName, bool cancelAllowed) 1663 { 1664 bool debugged = false; 1665 bool modal = false; 1666 { 1667 BAutolock _(fWorkerLock); 1668 if (fDebuggedTeams.find(team) != fDebuggedTeams.end()) 1669 debugged = true; 1670 } 1671 if (!debugged) 1672 modal = BPrivate::is_app_showing_modal_window(team); 1673 1674 if (modal) { 1675 // app blocks on a modal window 1676 char buffer[1024]; 1677 snprintf(buffer, sizeof(buffer), "The application \"%s\" might be " 1678 "blocked on a modal panel.", appName); 1679 _SetShutdownWindowText(buffer); 1680 _SetShutdownWindowCurrentApp(team); 1681 _SetShutdownWindowKillButtonEnabled(true); 1682 } 1683 1684 if (modal || debugged) { 1685 // wait for something to happen 1686 bool appGone = false; 1687 while (true) { 1688 uint32 event; 1689 team_id eventTeam; 1690 int32 phase; 1691 status_t error = _GetNextEvent(event, eventTeam, phase, true); 1692 if (error != B_OK) 1693 throw_error(error); 1694 1695 if ((event == APP_QUIT_EVENT) && eventTeam == team) { 1696 appGone = true; 1697 break; 1698 } 1699 1700 if (event == KILL_APP_EVENT && eventTeam == team) 1701 break; 1702 1703 if (event == ABORT_EVENT) { 1704 if (cancelAllowed || debugged) { 1705 PRINT(("ShutdownProcess::_QuitBlockingApp(): shutdown " 1706 "cancelled by team %ld (-1 => user)\n", eventTeam)); 1707 1708 throw_error(B_SHUTDOWN_CANCELLED); 1709 } 1710 1711 // If the app requests aborting the shutdown, we don't need 1712 // to wait any longer. It has processed the request and 1713 // won't quit by itself. We'll have to kill it. 1714 if (eventTeam == team) 1715 break; 1716 } 1717 } 1718 1719 _SetShutdownWindowKillButtonEnabled(false); 1720 1721 if (appGone) 1722 return; 1723 } 1724 1725 // kill the app 1726 PRINT((" killing team %ld\n", team)); 1727 1728 kill_team(team); 1729 1730 // remove the app (the roster will note eventually and send us 1731 // a notification, but we want to be sure) 1732 { 1733 BAutolock _(fWorkerLock); 1734 1735 if (RosterAppInfo *info = list.InfoFor(team)) { 1736 list.RemoveInfo(info); 1737 delete info; 1738 } 1739 } 1740 } 1741 1742 // _DisplayAbortingApp 1743 void 1744 ShutdownProcess::_DisplayAbortingApp(team_id team) 1745 { 1746 // find the app that cancelled the shutdown 1747 char appName[B_FILE_NAME_LENGTH]; 1748 bool foundApp = false; 1749 { 1750 BAutolock _(fWorkerLock); 1751 1752 RosterAppInfo *info = fUserApps.InfoFor(team); 1753 if (!info) 1754 info = fSystemApps.InfoFor(team); 1755 if (!info) 1756 fBackgroundApps.InfoFor(team); 1757 1758 if (info) { 1759 foundApp = true; 1760 strcpy(appName, info->ref.name); 1761 } 1762 } 1763 1764 if (!foundApp) { 1765 PRINT(("ShutdownProcess::_DisplayAbortingApp(): Didn't find the app " 1766 "that has cancelled the shutdown.\n")); 1767 return; 1768 } 1769 1770 // compose the text to be displayed 1771 char buffer[1024]; 1772 snprintf(buffer, sizeof(buffer), "Application \"%s\" has aborted the shutdown " 1773 "process.", appName); 1774 1775 // set up the window 1776 _SetShutdownWindowCurrentApp(team); 1777 _SetShutdownWindowText(buffer); 1778 _SetShutdownWindowWaitForAbortedOK(); 1779 1780 // schedule the timeout event 1781 _SetPhase(ABORTED_PHASE); 1782 _ScheduleTimeoutEvent(kDisplayAbortingAppTimeout); 1783 1784 // wait for the timeout or the user to press the cancel button 1785 while (true) { 1786 uint32 event; 1787 team_id eventTeam; 1788 int32 phase; 1789 status_t error = _GetNextEvent(event, eventTeam, phase, true); 1790 if (error != B_OK) 1791 break; 1792 1793 // stop waiting when the timeout occurs 1794 if (event == TIMEOUT_EVENT) 1795 break; 1796 1797 // stop waiting when the user hit the cancel button 1798 if (event == ABORT_EVENT && phase == ABORTED_PHASE && eventTeam < 0) 1799 break; 1800 1801 // This doesn't give us anything; it will just prevent us to see which 1802 // app was responsible after all... 1803 #if 0 1804 // also stop when the responsible app quit 1805 if ((event == APP_QUIT_EVENT) && eventTeam == team) 1806 break; 1807 #endif 1808 } 1809 } 1810 1811 1812 /*! Waits until the debugged team list is empty, ie. when there is no one 1813 left to debug. 1814 */ 1815 void 1816 ShutdownProcess::_WaitForDebuggedTeams() 1817 { 1818 PRINT(("ShutdownProcess::_WaitForDebuggedTeams()\n")); 1819 { 1820 BAutolock _(fWorkerLock); 1821 if (fDebuggedTeams.empty()) 1822 return; 1823 } 1824 1825 PRINT((" not empty!\n")); 1826 1827 // wait for something to happen 1828 while (true) { 1829 uint32 event; 1830 team_id eventTeam; 1831 int32 phase; 1832 status_t error = _GetNextEvent(event, eventTeam, phase, true); 1833 if (error != B_OK) 1834 throw_error(error); 1835 1836 if (event == ABORT_EVENT) 1837 throw_error(B_SHUTDOWN_CANCELLED); 1838 1839 BAutolock _(fWorkerLock); 1840 if (fDebuggedTeams.empty()) { 1841 PRINT((" out empty")); 1842 return; 1843 } 1844 } 1845 } 1846 1847