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