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