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