vfs: check userland buffers before reading them.
[haiku.git] / src / servers / registrar / ShutdownProcess.cpp
blobc6ae176303ff6c99dfc3ac3400d2626341d2a687
1 /*
2 * Copyright 2005-2008, Ingo Weinhold, bonefish@users.sf.net.
3 * Copyright 2006-2009, Axel Dörfler, axeld@pinc-software.de.
4 * Copyright 2006-2008, Stephan Aßmus.
5 * Copyright 2006, Ryan Leavengood.
7 * Distributed under the terms of the MIT License.
8 */
10 #include "ShutdownProcess.h"
12 #include <new>
13 #include <string.h>
15 #include <signal.h>
16 #include <unistd.h>
18 #include <Alert.h>
19 #include <AppFileInfo.h>
20 #include <AppMisc.h>
21 #include <Autolock.h>
22 #include <Bitmap.h>
23 #include <Button.h>
24 #include <Catalog.h>
25 #include <File.h>
26 #include <Message.h>
27 #include <MessagePrivate.h>
28 #include <RegistrarDefs.h>
29 #include <Roster.h> // for B_REQUEST_QUIT
30 #include <Screen.h>
31 #include <String.h>
32 #include <TextView.h>
33 #include <View.h>
34 #include <Window.h>
36 #include <TokenSpace.h>
37 #include <util/DoublyLinkedList.h>
39 #include <syscalls.h>
41 #include "AppInfoListMessagingTargetSet.h"
42 #include "Debug.h"
43 #include "EventQueue.h"
44 #include "MessageDeliverer.h"
45 #include "MessageEvent.h"
46 #include "Registrar.h"
47 #include "RosterAppInfo.h"
48 #include "TRoster.h"
51 #undef B_TRANSLATION_CONTEXT
52 #define B_TRANSLATION_CONTEXT "ShutdownProcess"
55 using std::nothrow;
56 using namespace BPrivate;
58 // The time span a non-background application has after the quit message has
59 // been delivered (more precisely: has been handed over to the
60 // MessageDeliverer).
61 static const bigtime_t kAppQuitTimeout = 3000000; // 3 s
63 // The time span a background application has after the quit message has been
64 // delivered (more precisely: has been handed over to the MessageDeliverer).
65 static const bigtime_t kBackgroundAppQuitTimeout = 3000000; // 3 s
67 // The time span non-app processes have after the TERM signal has been send
68 // to them before they get a KILL signal.
69 static const bigtime_t kNonAppQuitTimeout = 500000; // 0.5 s
71 // The time span the app that has aborted the shutdown shall be displayed in
72 // the shutdown window before closing it automatically.
73 static const bigtime_t kDisplayAbortingAppTimeout = 3000000; // 3 s
75 static const int kStripeWidth = 30;
76 static const int kIconVSpacing = 6;
77 static const int kIconSize = 32;
79 // message what fields (must not clobber the registrar's message namespace)
80 enum {
81 MSG_PHASE_TIMED_OUT = 'phto',
82 MSG_DONE = 'done',
83 MSG_KILL_APPLICATION = 'kill',
84 MSG_CANCEL_SHUTDOWN = 'cncl',
85 MSG_REBOOT_SYSTEM = 'lbot',
88 // internal events
89 enum {
90 NO_EVENT,
91 ABORT_EVENT,
92 TIMEOUT_EVENT,
93 APP_QUIT_EVENT,
94 KILL_APP_EVENT,
95 REBOOT_SYSTEM_EVENT,
96 DEBUG_EVENT
99 // phases
100 enum {
101 INVALID_PHASE = -1,
102 USER_APP_TERMINATION_PHASE = 0,
103 SYSTEM_APP_TERMINATION_PHASE = 1,
104 BACKGROUND_APP_TERMINATION_PHASE = 2,
105 OTHER_PROCESSES_TERMINATION_PHASE = 3,
106 ABORTED_PHASE = 4,
107 DONE_PHASE = 5,
111 static bool
112 inverse_compare_by_registration_time(const RosterAppInfo* info1,
113 const RosterAppInfo* info2)
115 return (info2->registration_time < info1->registration_time);
119 /*! \brief Used to avoid type matching problems when throwing a constant.
121 static inline
122 void
123 throw_error(status_t error)
125 throw error;
129 class ShutdownProcess::TimeoutEvent : public MessageEvent {
130 public:
131 TimeoutEvent(BHandler* target)
132 : MessageEvent(0, target, MSG_PHASE_TIMED_OUT)
134 SetAutoDelete(false);
136 fMessage.AddInt32("phase", INVALID_PHASE);
137 fMessage.AddInt32("team", -1);
140 void SetPhase(int32 phase)
142 fMessage.ReplaceInt32("phase", phase);
145 void SetTeam(team_id team)
147 fMessage.ReplaceInt32("team", team);
150 static int32 GetMessagePhase(BMessage* message)
152 int32 phase;
153 if (message->FindInt32("phase", &phase) != B_OK)
154 phase = INVALID_PHASE;
156 return phase;
159 static int32 GetMessageTeam(BMessage* message)
161 team_id team;
162 if (message->FindInt32("team", &team) != B_OK)
163 team = -1;
165 return team;
170 class ShutdownProcess::InternalEvent
171 : public DoublyLinkedListLinkImpl<InternalEvent> {
172 public:
173 InternalEvent(uint32 type, team_id team, int32 phase)
175 fType(type),
176 fTeam(team),
177 fPhase(phase)
181 uint32 Type() const { return fType; }
182 team_id Team() const { return fTeam; }
183 int32 Phase() const { return fPhase; }
185 private:
186 uint32 fType;
187 int32 fTeam;
188 int32 fPhase;
192 struct ShutdownProcess::InternalEventList : DoublyLinkedList<InternalEvent> {
196 class ShutdownProcess::QuitRequestReplyHandler : public BHandler {
197 public:
198 QuitRequestReplyHandler(ShutdownProcess* shutdownProcess)
199 : BHandler("shutdown quit reply handler"),
200 fShutdownProcess(shutdownProcess)
204 virtual void MessageReceived(BMessage* message)
206 switch (message->what) {
207 case B_REPLY:
209 bool result;
210 thread_id thread;
211 if (message->FindBool("result", &result) == B_OK
212 && message->FindInt32("thread", &thread) == B_OK) {
213 if (!result)
214 fShutdownProcess->_NegativeQuitRequestReply(thread);
217 break;
220 default:
221 BHandler::MessageReceived(message);
222 break;
226 private:
227 ShutdownProcess *fShutdownProcess;
231 class ShutdownProcess::ShutdownWindow : public BWindow {
232 public:
233 ShutdownWindow()
234 : BWindow(BRect(0, 0, 200, 100), B_TRANSLATE("Shutdown status"),
235 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
236 B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE | B_NOT_MINIMIZABLE
237 | B_NOT_ZOOMABLE | B_NOT_CLOSABLE, B_ALL_WORKSPACES),
238 fKillAppMessage(NULL),
239 fCurrentApp(-1)
243 ~ShutdownWindow()
245 for (int32 i = 0; AppInfo* info = (AppInfo*)fAppInfos.ItemAt(i); i++) {
246 delete info;
250 virtual bool QuitRequested()
252 return false;
255 status_t Init(BMessenger target)
257 // create the views
259 // root view
260 fRootView = new(nothrow) TAlertView(BRect(0, 0, 10, 10), "app icons",
261 B_FOLLOW_NONE, 0);
262 if (!fRootView)
263 return B_NO_MEMORY;
264 fRootView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
265 AddChild(fRootView);
267 // text view
268 fTextView = new(nothrow) BTextView(BRect(0, 0, 10, 10), "text",
269 BRect(0, 0, 10, 10), B_FOLLOW_NONE);
270 if (!fTextView)
271 return B_NO_MEMORY;
272 fTextView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
273 rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
274 fTextView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
275 fTextView->MakeEditable(false);
276 fTextView->MakeSelectable(false);
277 fTextView->SetWordWrap(false);
278 fRootView->AddChild(fTextView);
280 // kill app button
281 fKillAppButton = new(nothrow) BButton(BRect(0, 0, 10, 10), "kill app",
282 B_TRANSLATE("Kill application"), NULL, B_FOLLOW_NONE);
283 if (!fKillAppButton)
284 return B_NO_MEMORY;
285 fRootView->AddChild(fKillAppButton);
287 BMessage* message = new BMessage(MSG_KILL_APPLICATION);
288 if (!message)
289 return B_NO_MEMORY;
290 message->AddInt32("team", -1);
291 fKillAppMessage = message;
292 fKillAppButton->SetMessage(message);
293 fKillAppButton->SetTarget(target);
295 // cancel shutdown button
296 fCancelShutdownButton = new(nothrow) BButton(BRect(0, 0, 10, 10),
297 "cancel shutdown", B_TRANSLATE("Cancel shutdown"), NULL,
298 B_FOLLOW_NONE);
299 if (!fCancelShutdownButton)
300 return B_NO_MEMORY;
301 fRootView->AddChild(fCancelShutdownButton);
303 message = new BMessage(MSG_CANCEL_SHUTDOWN);
304 if (!message)
305 return B_NO_MEMORY;
306 fCancelShutdownButton->SetMessage(message);
307 fCancelShutdownButton->SetTarget(target);
309 // reboot system button
310 fRebootSystemButton = new(nothrow) BButton(BRect(0, 0, 10, 10),
311 "reboot", B_TRANSLATE("Restart system"), NULL, B_FOLLOW_NONE);
312 if (!fRebootSystemButton)
313 return B_NO_MEMORY;
314 fRebootSystemButton->Hide();
315 fRootView->AddChild(fRebootSystemButton);
317 message = new BMessage(MSG_REBOOT_SYSTEM);
318 if (!message)
319 return B_NO_MEMORY;
320 fRebootSystemButton->SetMessage(message);
321 fRebootSystemButton->SetTarget(target);
323 // aborted OK button
324 fAbortedOKButton = new(nothrow) BButton(BRect(0, 0, 10, 10),
325 "ok", B_TRANSLATE("OK"), NULL, B_FOLLOW_NONE);
326 if (!fAbortedOKButton)
327 return B_NO_MEMORY;
328 fAbortedOKButton->Hide();
329 fRootView->AddChild(fAbortedOKButton);
331 message = new BMessage(MSG_CANCEL_SHUTDOWN);
332 if (!message)
333 return B_NO_MEMORY;
334 fAbortedOKButton->SetMessage(message);
335 fAbortedOKButton->SetTarget(target);
337 // compute the sizes
338 static const int kHSpacing = 10;
339 static const int kVSpacing = 10;
340 static const int kInnerHSpacing = 5;
341 static const int kInnerVSpacing = 8;
343 // buttons
344 fKillAppButton->ResizeToPreferred();
345 fCancelShutdownButton->ResizeToPreferred();
346 fRebootSystemButton->MakeDefault(true);
347 fRebootSystemButton->ResizeToPreferred();
348 fAbortedOKButton->MakeDefault(true);
349 fAbortedOKButton->ResizeToPreferred();
351 BRect rect(fKillAppButton->Frame());
352 int buttonWidth = rect.IntegerWidth() + 1;
353 int buttonHeight = rect.IntegerHeight() + 1;
355 rect = fCancelShutdownButton->Frame();
356 if (rect.IntegerWidth() >= buttonWidth)
357 buttonWidth = rect.IntegerWidth() + 1;
359 int defaultButtonHeight
360 = fRebootSystemButton->Frame().IntegerHeight() + 1;
362 // text view
363 fTextView->SetText("two\nlines");
364 int textHeight = (int)fTextView->TextHeight(0, 1) + 1;
366 int rightPartX = kStripeWidth + kIconSize / 2 + 1;
367 int textX = rightPartX + kInnerHSpacing;
368 int textY = kVSpacing;
369 int buttonsY = textY + textHeight + kInnerVSpacing;
370 int nonDefaultButtonsY = buttonsY
371 + (defaultButtonHeight - buttonHeight) / 2;
372 int rightPartWidth = 2 * buttonWidth + kInnerHSpacing;
373 int width = rightPartX + rightPartWidth + kHSpacing;
374 int height = buttonsY + defaultButtonHeight + kVSpacing;
376 // now layout the views
378 // text view
379 fTextView->MoveTo(textX, textY);
380 fTextView->ResizeTo(rightPartWidth + rightPartX - textX - 1,
381 textHeight - 1);
382 fTextView->SetTextRect(fTextView->Bounds());
384 fTextView->SetWordWrap(true);
386 // buttons
387 fKillAppButton->MoveTo(rightPartX, nonDefaultButtonsY);
388 fKillAppButton->ResizeTo(buttonWidth - 1, buttonHeight - 1);
390 fCancelShutdownButton->MoveTo(
391 rightPartX + buttonWidth + kInnerVSpacing - 1,
392 nonDefaultButtonsY);
393 fCancelShutdownButton->ResizeTo(buttonWidth - 1, buttonHeight - 1);
395 fRebootSystemButton->MoveTo(
396 (width - fRebootSystemButton->Frame().IntegerWidth()) / 2,
397 buttonsY);
399 fAbortedOKButton->MoveTo(
400 (width - fAbortedOKButton->Frame().IntegerWidth()) / 2,
401 buttonsY);
403 // set the root view and window size
404 fRootView->ResizeTo(width - 1, height - 1);
405 ResizeTo(width - 1, height - 1);
407 // move the window to the same position as BAlerts
408 BScreen screen(this);
409 BRect screenFrame = screen.Frame();
411 MoveTo(screenFrame.left + (screenFrame.Width() - width) / 2.0,
412 screenFrame.top + screenFrame.Height() / 4.0 - ceilf(height / 3.0));
414 return B_OK;
417 status_t AddApp(team_id team, BBitmap* miniIcon, BBitmap* largeIcon)
419 AppInfo* info = new(nothrow) AppInfo;
420 if (!info) {
421 delete miniIcon;
422 delete largeIcon;
423 return B_NO_MEMORY;
426 info->team = team;
427 info->miniIcon = miniIcon;
428 info->largeIcon = largeIcon;
430 if (!fAppInfos.AddItem(info)) {
431 delete info;
432 return B_NO_MEMORY;
435 return B_OK;
438 void RemoveApp(team_id team)
440 int32 index = _AppInfoIndexOf(team);
441 if (index < 0)
442 return;
444 if (team == fCurrentApp)
445 SetCurrentApp(-1);
447 AppInfo* info = (AppInfo*)fAppInfos.RemoveItem(index);
448 delete info;
451 void SetCurrentApp(team_id team)
453 AppInfo* info = (team >= 0 ? _AppInfoFor(team) : NULL);
455 fCurrentApp = team;
456 fRootView->SetAppInfo(info);
458 fKillAppMessage->ReplaceInt32("team", team);
461 void SetText(const char* text)
463 fTextView->SetText(text);
466 void SetCancelShutdownButtonEnabled(bool enable)
468 fCancelShutdownButton->SetEnabled(enable);
471 void SetKillAppButtonEnabled(bool enable)
473 if (enable != fKillAppButton->IsEnabled()) {
474 fKillAppButton->SetEnabled(enable);
476 if (enable)
477 fKillAppButton->Show();
478 else
479 fKillAppButton->Hide();
483 void SetWaitForShutdown()
485 fKillAppButton->Hide();
486 fCancelShutdownButton->Hide();
487 fRebootSystemButton->MakeDefault(true);
488 fRebootSystemButton->Show();
490 SetTitle(B_TRANSLATE("System is shut down"));
491 fTextView->SetText(
492 B_TRANSLATE("It's now safe to turn off the computer."));
495 void SetWaitForAbortedOK()
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();
504 SetTitle(B_TRANSLATE("Shutdown aborted"));
507 private:
508 struct AppInfo {
509 team_id team;
510 BBitmap *miniIcon;
511 BBitmap *largeIcon;
513 ~AppInfo()
515 delete miniIcon;
516 delete largeIcon;
520 int32 _AppInfoIndexOf(team_id team)
522 if (team < 0)
523 return -1;
525 for (int32 i = 0; AppInfo* info = (AppInfo*)fAppInfos.ItemAt(i); i++) {
526 if (info->team == team)
527 return i;
530 return -1;
533 AppInfo* _AppInfoFor(team_id team)
535 int32 index = _AppInfoIndexOf(team);
536 return (index >= 0 ? (AppInfo*)fAppInfos.ItemAt(index) : NULL);
539 class TAlertView : public BView {
540 public:
541 TAlertView(BRect frame, const char* name, uint32 resizeMask,
542 uint32 flags)
543 : BView(frame, name, resizeMask, flags | B_WILL_DRAW),
544 fAppInfo(NULL)
548 virtual void Draw(BRect updateRect)
550 BRect stripeRect = Bounds();
551 stripeRect.right = kStripeWidth;
552 SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
553 FillRect(stripeRect);
555 if (fAppInfo && fAppInfo->largeIcon) {
556 if (fAppInfo->largeIcon->ColorSpace() == B_RGBA32) {
557 SetDrawingMode(B_OP_ALPHA);
558 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
559 } else
560 SetDrawingMode(B_OP_OVER);
562 DrawBitmapAsync(fAppInfo->largeIcon,
563 BPoint(kStripeWidth - kIconSize / 2, kIconVSpacing));
567 void SetAppInfo(AppInfo* info)
569 fAppInfo = info;
570 Invalidate();
573 private:
574 const AppInfo *fAppInfo;
577 private:
578 BList fAppInfos;
579 TAlertView* fRootView;
580 BTextView* fTextView;
581 BButton* fKillAppButton;
582 BButton* fCancelShutdownButton;
583 BButton* fRebootSystemButton;
584 BButton* fAbortedOKButton;
585 BMessage* fKillAppMessage;
586 team_id fCurrentApp;
590 // #pragma mark -
593 ShutdownProcess::ShutdownProcess(TRoster* roster, EventQueue* eventQueue)
595 BLooper("shutdown process"),
596 EventMaskWatcher(BMessenger(this), B_REQUEST_QUIT),
597 fWorkerLock("worker lock"),
598 fRequest(NULL),
599 fRoster(roster),
600 fEventQueue(eventQueue),
601 fTimeoutEvent(NULL),
602 fInternalEvents(NULL),
603 fInternalEventSemaphore(-1),
604 fQuitRequestReplyHandler(NULL),
605 fWorker(-1),
606 fCurrentPhase(INVALID_PHASE),
607 fShutdownError(B_ERROR),
608 fHasGUI(false),
609 fReboot(false),
610 fRequestReplySent(false),
611 fWindow(NULL)
616 ShutdownProcess::~ShutdownProcess()
618 // terminate the GUI
619 if (fHasGUI && fWindow && fWindow->Lock())
620 fWindow->Quit();
622 // remove and delete the quit request reply handler
623 if (fQuitRequestReplyHandler) {
624 BAutolock _(this);
625 RemoveHandler(fQuitRequestReplyHandler);
626 delete fQuitRequestReplyHandler;
629 // remove and delete the timeout event
630 if (fTimeoutEvent) {
631 fEventQueue->RemoveEvent(fTimeoutEvent);
633 delete fTimeoutEvent;
636 // remove the application quit watcher
637 fRoster->RemoveWatcher(this);
639 // If an error occurred (e.g. the shutdown process was cancelled), the
640 // roster should accept applications again.
641 if (fShutdownError != B_OK)
642 fRoster->SetShuttingDown(false);
644 // delete the internal event semaphore
645 if (fInternalEventSemaphore >= 0)
646 delete_sem(fInternalEventSemaphore);
648 // wait for the worker thread to terminate
649 if (fWorker >= 0) {
650 int32 result;
651 wait_for_thread(fWorker, &result);
654 // delete all internal events and the queue
655 if (fInternalEvents) {
656 while (InternalEvent* event = fInternalEvents->First()) {
657 fInternalEvents->Remove(event);
658 delete event;
661 delete fInternalEvents;
664 // send a reply to the request and delete it
665 _SendReply(fShutdownError);
666 delete fRequest;
670 status_t
671 ShutdownProcess::Init(BMessage* request)
673 PRINT("ShutdownProcess::Init()\n");
675 // create and add the quit request reply handler
676 fQuitRequestReplyHandler = new(nothrow) QuitRequestReplyHandler(this);
677 if (!fQuitRequestReplyHandler)
678 RETURN_ERROR(B_NO_MEMORY);
679 AddHandler(fQuitRequestReplyHandler);
681 // create the timeout event
682 fTimeoutEvent = new(nothrow) TimeoutEvent(this);
683 if (!fTimeoutEvent)
684 RETURN_ERROR(B_NO_MEMORY);
686 // create the event list
687 fInternalEvents = new(nothrow) InternalEventList;
688 if (!fInternalEvents)
689 RETURN_ERROR(B_NO_MEMORY);
691 // create the event sempahore
692 fInternalEventSemaphore = create_sem(0, "shutdown events");
693 if (fInternalEventSemaphore < 0)
694 RETURN_ERROR(fInternalEventSemaphore);
696 // init the app server connection
697 fHasGUI = Registrar::App()->InitGUIContext() == B_OK;
699 // start watching application quits
700 status_t error = fRoster->AddWatcher(this);
701 if (error != B_OK) {
702 fRoster->SetShuttingDown(false);
703 RETURN_ERROR(error);
706 // start the worker thread
707 fWorker = spawn_thread(_WorkerEntry, "shutdown worker",
708 B_NORMAL_PRIORITY + 1, this);
709 if (fWorker < 0) {
710 fRoster->RemoveWatcher(this);
711 fRoster->SetShuttingDown(false);
712 RETURN_ERROR(fWorker);
715 // everything went fine: now we own the request
716 fRequest = request;
718 if (fRequest->FindBool("reboot", &fReboot) != B_OK)
719 fReboot = false;
721 resume_thread(fWorker);
723 PRINT("ShutdownProcess::Init() done\n");
725 return B_OK;
729 void
730 ShutdownProcess::MessageReceived(BMessage* message)
732 switch (message->what) {
733 case B_SOME_APP_QUIT:
735 // get the team
736 team_id team;
737 if (message->FindInt32("be:team", &team) != B_OK) {
738 // should not happen
739 return;
742 PRINT("ShutdownProcess::MessageReceived(): B_SOME_APP_QUIT: %"
743 B_PRId32 "\n", team);
745 // remove the app info from the respective list
746 int32 phase;
747 RosterAppInfo* info;
749 BAutolock _(fWorkerLock);
751 info = fUserApps.InfoFor(team);
752 if (info)
753 fUserApps.RemoveInfo(info);
754 else if ((info = fSystemApps.InfoFor(team)))
755 fSystemApps.RemoveInfo(info);
756 else if ((info = fBackgroundApps.InfoFor(team)))
757 fBackgroundApps.RemoveInfo(info);
758 else // not found
759 return;
761 phase = fCurrentPhase;
764 // post the event
765 _PushEvent(APP_QUIT_EVENT, team, phase);
767 delete info;
769 break;
772 case MSG_PHASE_TIMED_OUT:
774 // get the phase the event is intended for
775 int32 phase = TimeoutEvent::GetMessagePhase(message);
776 team_id team = TimeoutEvent::GetMessageTeam(message);;
777 PRINT("MSG_PHASE_TIMED_OUT: phase: %" B_PRId32 ", team: %" B_PRId32
778 "\n", phase, team);
780 BAutolock _(fWorkerLock);
782 if (phase == INVALID_PHASE || phase != fCurrentPhase)
783 return;
785 // post the event
786 _PushEvent(TIMEOUT_EVENT, team, phase);
788 break;
791 case MSG_KILL_APPLICATION:
793 team_id team;
794 if (message->FindInt32("team", &team) != B_OK)
795 break;
797 // post the event
798 _PushEvent(KILL_APP_EVENT, team, fCurrentPhase);
799 break;
802 case MSG_CANCEL_SHUTDOWN:
804 // post the event
805 _PushEvent(ABORT_EVENT, -1, fCurrentPhase);
806 break;
809 case MSG_REBOOT_SYSTEM:
811 // post the event
812 _PushEvent(REBOOT_SYSTEM_EVENT, -1, INVALID_PHASE);
813 break;
816 case MSG_DONE:
818 // notify the registrar that we're done
819 be_app->PostMessage(B_REG_SHUTDOWN_FINISHED, be_app);
820 break;
823 case B_REG_TEAM_DEBUGGER_ALERT:
825 bool stopShutdown;
826 if (message->FindBool("stop shutdown", &stopShutdown) == B_OK
827 && stopShutdown) {
828 // post abort event to the worker
829 _PushEvent(ABORT_EVENT, -1, fCurrentPhase);
830 break;
833 bool open;
834 team_id team;
835 if (message->FindInt32("team", &team) != B_OK
836 || message->FindBool("open", &open) != B_OK)
837 break;
839 BAutolock _(fWorkerLock);
840 if (open) {
841 PRINT("B_REG_TEAM_DEBUGGER_ALERT: insert %" B_PRId32 "\n",
842 team);
843 fDebuggedTeams.insert(team);
844 } else {
845 PRINT("B_REG_TEAM_DEBUGGER_ALERT: remove %" B_PRId32 "\n",
846 team);
847 fDebuggedTeams.erase(team);
848 _PushEvent(DEBUG_EVENT, -1, fCurrentPhase);
850 break;
853 default:
854 BLooper::MessageReceived(message);
855 break;
860 void
861 ShutdownProcess::SendReply(BMessage* request, status_t error)
863 if (error == B_OK) {
864 BMessage reply(B_REG_SUCCESS);
865 request->SendReply(&reply);
866 } else {
867 BMessage reply(B_REG_ERROR);
868 reply.AddInt32("error", error);
869 request->SendReply(&reply);
874 void
875 ShutdownProcess::_SendReply(status_t error)
877 if (!fRequestReplySent) {
878 SendReply(fRequest, error);
879 fRequestReplySent = true;
884 void
885 ShutdownProcess::_SetPhase(int32 phase)
887 BAutolock _(fWorkerLock);
889 if (phase == fCurrentPhase)
890 return;
892 fCurrentPhase = phase;
894 // remove the timeout event scheduled for the previous phase
895 fEventQueue->RemoveEvent(fTimeoutEvent);
899 void
900 ShutdownProcess::_ScheduleTimeoutEvent(bigtime_t timeout, team_id team)
902 BAutolock _(fWorkerLock);
904 // remove the timeout event
905 fEventQueue->RemoveEvent(fTimeoutEvent);
907 // set the event's phase, team and time
908 fTimeoutEvent->SetPhase(fCurrentPhase);
909 fTimeoutEvent->SetTeam(team);
910 fTimeoutEvent->SetTime(system_time() + timeout);
912 // add the event
913 fEventQueue->AddEvent(fTimeoutEvent);
917 void
918 ShutdownProcess::_SetShowShutdownWindow(bool show)
920 if (fHasGUI) {
921 BAutolock _(fWindow);
923 if (show == fWindow->IsHidden()) {
924 if (show)
925 fWindow->Show();
926 else
927 fWindow->Hide();
933 void
934 ShutdownProcess::_InitShutdownWindow()
936 // prepare the window
937 if (fHasGUI) {
938 fWindow = new(nothrow) ShutdownWindow;
939 if (fWindow != NULL) {
940 status_t error = fWindow->Init(BMessenger(this));
941 if (error != B_OK) {
942 delete fWindow;
943 fWindow = NULL;
947 // add the applications
948 if (fWindow) {
949 BAutolock _(fWorkerLock);
950 _AddShutdownWindowApps(fUserApps);
951 _AddShutdownWindowApps(fSystemApps);
952 } else {
953 WARNING("ShutdownProcess::Init(): Failed to create or init "
954 "shutdown window.");
956 fHasGUI = false;
962 void
963 ShutdownProcess::_AddShutdownWindowApps(AppInfoList& infos)
965 if (!fHasGUI)
966 return;
968 for (AppInfoList::Iterator it = infos.It(); it.IsValid(); ++it) {
969 RosterAppInfo* info = *it;
971 // init an app file info
972 BFile file;
973 status_t error = file.SetTo(&info->ref, B_READ_ONLY);
974 if (error != B_OK) {
975 WARNING("ShutdownProcess::_AddShutdownWindowApps(): Failed to "
976 "open file for app %s: %s\n", info->signature,
977 strerror(error));
978 continue;
981 BAppFileInfo appFileInfo;
982 error = appFileInfo.SetTo(&file);
983 if (error != B_OK) {
984 WARNING("ShutdownProcess::_AddShutdownWindowApps(): Failed to "
985 "init app file info for app %s: %s\n", info->signature,
986 strerror(error));
989 // get the application icons
990 #ifdef __HAIKU__
991 color_space format = B_RGBA32;
992 #else
993 color_space format = B_CMAP8;
994 #endif
996 // mini icon
997 BBitmap* miniIcon = new(nothrow) BBitmap(BRect(0, 0, 15, 15), format);
998 if (miniIcon != NULL) {
999 error = miniIcon->InitCheck();
1000 if (error == B_OK)
1001 error = appFileInfo.GetTrackerIcon(miniIcon, B_MINI_ICON);
1002 if (error != B_OK) {
1003 delete miniIcon;
1004 miniIcon = NULL;
1008 // mini icon
1009 BBitmap* largeIcon = new(nothrow) BBitmap(BRect(0, 0, 31, 31), format);
1010 if (largeIcon != NULL) {
1011 error = largeIcon->InitCheck();
1012 if (error == B_OK)
1013 error = appFileInfo.GetTrackerIcon(largeIcon, B_LARGE_ICON);
1014 if (error != B_OK) {
1015 delete largeIcon;
1016 largeIcon = NULL;
1020 // add the app
1021 error = fWindow->AddApp(info->team, miniIcon, largeIcon);
1022 if (error != B_OK) {
1023 WARNING("ShutdownProcess::_AddShutdownWindowApps(): Failed to "
1024 "add app to the shutdown window: %s\n", strerror(error));
1030 void
1031 ShutdownProcess::_RemoveShutdownWindowApp(team_id team)
1033 if (fHasGUI) {
1034 BAutolock _(fWindow);
1036 fWindow->RemoveApp(team);
1041 void
1042 ShutdownProcess::_SetShutdownWindowCurrentApp(team_id team)
1044 if (fHasGUI) {
1045 BAutolock _(fWindow);
1047 fWindow->SetCurrentApp(team);
1052 void
1053 ShutdownProcess::_SetShutdownWindowText(const char* text)
1055 if (fHasGUI) {
1056 BAutolock _(fWindow);
1058 fWindow->SetText(text);
1063 void
1064 ShutdownProcess::_SetShutdownWindowCancelButtonEnabled(bool enabled)
1066 if (fHasGUI) {
1067 BAutolock _(fWindow);
1069 fWindow->SetCancelShutdownButtonEnabled(enabled);
1074 void
1075 ShutdownProcess::_SetShutdownWindowKillButtonEnabled(bool enabled)
1077 if (fHasGUI) {
1078 BAutolock _(fWindow);
1080 fWindow->SetKillAppButtonEnabled(enabled);
1085 void
1086 ShutdownProcess::_SetShutdownWindowWaitForShutdown()
1088 if (fHasGUI) {
1089 BAutolock _(fWindow);
1091 fWindow->SetWaitForShutdown();
1096 void
1097 ShutdownProcess::_SetShutdownWindowWaitForAbortedOK()
1099 if (fHasGUI) {
1100 BAutolock _(fWindow);
1102 fWindow->SetWaitForAbortedOK();
1107 void
1108 ShutdownProcess::_NegativeQuitRequestReply(thread_id thread)
1110 BAutolock _(fWorkerLock);
1112 // Note: team ID == team main thread ID under Haiku. When testing under R5
1113 // using the team ID in case of an ABORT_EVENT won't work correctly. But
1114 // this is done only for system apps.
1115 _PushEvent(ABORT_EVENT, thread, fCurrentPhase);
1119 void
1120 ShutdownProcess::_PrepareShutdownMessage(BMessage& message) const
1122 message.what = B_QUIT_REQUESTED;
1123 message.AddBool("_shutdown_", true);
1125 BMessage::Private(message).SetReply(BMessenger(fQuitRequestReplyHandler));
1129 status_t
1130 ShutdownProcess::_ShutDown()
1132 PRINT("Invoking _kern_shutdown(%d)\n", fReboot);
1133 RETURN_ERROR(_kern_shutdown(fReboot));
1137 status_t
1138 ShutdownProcess::_PushEvent(uint32 eventType, team_id team, int32 phase)
1140 InternalEvent* event = new(nothrow) InternalEvent(eventType, team, phase);
1141 if (!event) {
1142 ERROR("ShutdownProcess::_PushEvent(): Failed to create event!\n");
1144 return B_NO_MEMORY;
1147 BAutolock _(fWorkerLock);
1149 fInternalEvents->Add(event);
1150 release_sem(fInternalEventSemaphore);
1152 return B_OK;
1156 status_t
1157 ShutdownProcess::_GetNextEvent(uint32& eventType, thread_id& team, int32& phase,
1158 bool block)
1160 while (true) {
1161 // acquire the semaphore
1162 if (block) {
1163 status_t error;
1164 do {
1165 error = acquire_sem(fInternalEventSemaphore);
1166 } while (error == B_INTERRUPTED);
1168 if (error != B_OK)
1169 return error;
1170 } else {
1171 status_t error = acquire_sem_etc(fInternalEventSemaphore, 1,
1172 B_RELATIVE_TIMEOUT, 0);
1173 if (error != B_OK) {
1174 eventType = NO_EVENT;
1175 return B_OK;
1179 // get the event
1180 BAutolock _(fWorkerLock);
1182 InternalEvent* event = fInternalEvents->Head();
1183 fInternalEvents->Remove(event);
1185 eventType = event->Type();
1186 team = event->Team();
1187 phase = event->Phase();
1189 delete event;
1191 // if the event is an obsolete timeout event, we drop it right here
1192 if (eventType == TIMEOUT_EVENT && phase != fCurrentPhase)
1193 continue;
1195 break;
1198 // notify the window, if an app has been removed
1199 if (eventType == APP_QUIT_EVENT)
1200 _RemoveShutdownWindowApp(team);
1202 return B_OK;
1206 status_t
1207 ShutdownProcess::_WorkerEntry(void* data)
1209 return ((ShutdownProcess*)data)->_Worker();
1213 status_t
1214 ShutdownProcess::_Worker()
1216 try {
1217 _WorkerDoShutdown();
1218 fShutdownError = B_OK;
1219 } catch (status_t error) {
1220 PRINT("ShutdownProcess::_Worker(): error while shutting down: %s\n",
1221 strerror(error));
1223 fShutdownError = error;
1226 // this can happen only, if the shutdown process failed or was aborted:
1227 // notify the looper
1228 _SetPhase(DONE_PHASE);
1229 PostMessage(MSG_DONE);
1231 return B_OK;
1235 void
1236 ShutdownProcess::_WorkerDoShutdown()
1238 PRINT("ShutdownProcess::_WorkerDoShutdown()\n");
1240 // If we are here, the shutdown process has been initiated successfully,
1241 // that is, if an asynchronous BRoster::Shutdown() was requested, we
1242 // notify the caller at this point.
1243 bool synchronous;
1244 if (fRequest->FindBool("synchronous", &synchronous) == B_OK && !synchronous)
1245 _SendReply(B_OK);
1247 // ask the user to confirm the shutdown, if desired
1248 bool askUser;
1249 if (fHasGUI && fRequest->FindBool("confirm", &askUser) == B_OK && askUser) {
1250 const char* restart = B_TRANSLATE("Restart");
1251 const char* shutdown = B_TRANSLATE("Shut down");
1252 BString title = B_TRANSLATE("%action%?");
1253 title.ReplaceFirst("%action%", fReboot ? restart : shutdown);
1254 const char* text = fReboot
1255 ? B_TRANSLATE("Do you really want to restart the system?")
1256 : B_TRANSLATE("Do you really want to shut down the system?");
1257 const char* defaultText = fReboot ? restart : shutdown;
1258 const char* otherText = fReboot ? shutdown : restart;
1259 BAlert* alert = new BAlert(title.String(), text,
1260 B_TRANSLATE("Cancel"), otherText, defaultText,
1261 B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1262 // We want the alert to behave more like a regular window...
1263 alert->SetFeel(B_NORMAL_WINDOW_FEEL);
1264 // ...but not quit. Minimizing the alert would prevent the user from
1265 // finding it again, since registrar does not have an entry in the
1266 // Deskbar.
1267 alert->SetFlags(alert->Flags() | B_NOT_MINIMIZABLE | B_CLOSE_ON_ESCAPE);
1268 alert->SetWorkspaces(B_ALL_WORKSPACES);
1269 int32 result = alert->Go();
1271 if (result == 1) {
1272 // Toggle shutdown method
1273 fReboot = !fReboot;
1274 } else if (result < 1)
1275 throw_error(B_SHUTDOWN_CANCELLED);
1278 // tell TRoster not to accept new applications anymore
1279 fRoster->SetShuttingDown(true);
1281 fWorkerLock.Lock();
1283 // get a list of all applications to shut down and sort them
1284 status_t status = fRoster->GetShutdownApps(fUserApps, fSystemApps,
1285 fBackgroundApps, fVitalSystemApps);
1286 if (status != B_OK) {
1287 fWorkerLock.Unlock();
1288 fRoster->RemoveWatcher(this);
1289 fRoster->SetShuttingDown(false);
1290 return;
1293 fUserApps.Sort(&inverse_compare_by_registration_time);
1294 fSystemApps.Sort(&inverse_compare_by_registration_time);
1296 fWorkerLock.Unlock();
1298 // make the shutdown window ready and show it
1299 _InitShutdownWindow();
1300 _SetShutdownWindowCurrentApp(-1);
1301 _SetShutdownWindowText(B_TRANSLATE("Tidying things up a bit."));
1302 _SetShutdownWindowCancelButtonEnabled(true);
1303 _SetShutdownWindowKillButtonEnabled(false);
1304 _SetShowShutdownWindow(true);
1306 // sync
1307 sync();
1309 // phase 1: terminate the user apps
1310 _SetPhase(USER_APP_TERMINATION_PHASE);
1311 _QuitApps(fUserApps, false);
1312 _WaitForDebuggedTeams();
1314 // phase 2: terminate the system apps
1315 _SetPhase(SYSTEM_APP_TERMINATION_PHASE);
1316 _QuitApps(fSystemApps, true);
1317 _WaitForDebuggedTeams();
1319 // phase 3: terminate the background apps
1320 _SetPhase(BACKGROUND_APP_TERMINATION_PHASE);
1321 _QuitBackgroundApps();
1322 _WaitForDebuggedTeams();
1324 // phase 4: terminate the other processes
1325 _SetPhase(OTHER_PROCESSES_TERMINATION_PHASE);
1326 _QuitNonApps();
1327 _ScheduleTimeoutEvent(kBackgroundAppQuitTimeout, -1);
1328 _WaitForBackgroundApps();
1329 _KillBackgroundApps();
1330 _WaitForDebuggedTeams();
1332 // we're through: do the shutdown
1333 _SetPhase(DONE_PHASE);
1334 if (fReboot)
1335 _SetShutdownWindowText(B_TRANSLATE("Restarting" B_UTF8_ELLIPSIS));
1336 else
1337 _SetShutdownWindowText(B_TRANSLATE("Shutting down" B_UTF8_ELLIPSIS));
1338 _ShutDown();
1339 _SetShutdownWindowWaitForShutdown();
1341 PRINT(" _kern_shutdown() failed\n");
1343 // shutdown failed: This can happen for power off mode -- reboot should
1344 // always work.
1345 if (fHasGUI) {
1346 // wait for the reboot event
1347 uint32 event;
1348 do {
1349 team_id team;
1350 int32 phase;
1351 status = _GetNextEvent(event, team, phase, true);
1352 if (status != B_OK)
1353 break;
1354 } while (event != REBOOT_SYSTEM_EVENT);
1356 _kern_shutdown(true);
1359 // either there's no GUI or reboot failed: we enter the kernel debugger
1360 // instead
1361 #ifdef __HAIKU__
1362 // TODO: Introduce the syscall.
1363 // while (true) {
1364 // _kern_kernel_debugger("The system is shut down. It's now safe to turn "
1365 // "off the computer.");
1366 // }
1367 #endif
1371 bool
1372 ShutdownProcess::_WaitForApp(team_id team, AppInfoList* list, bool systemApps)
1374 uint32 event;
1375 do {
1376 team_id eventTeam;
1377 int32 phase;
1378 status_t error = _GetNextEvent(event, eventTeam, phase, true);
1379 if (error != B_OK)
1380 throw_error(error);
1382 if (event == APP_QUIT_EVENT && eventTeam == team)
1383 return true;
1385 if (event == TIMEOUT_EVENT && eventTeam == team)
1386 return false;
1388 if (event == ABORT_EVENT) {
1389 if (eventTeam == -1) {
1390 // The user canceled the shutdown process by pressing the
1391 // Cancel button.
1392 throw_error(B_SHUTDOWN_CANCELLED);
1394 if (systemApps) {
1395 // If the app requests aborting the shutdown, we don't need
1396 // to wait any longer. It has processed the request and
1397 // won't quit by itself. We ignore this for system apps.
1398 if (eventTeam == team)
1399 return false;
1400 } else {
1401 // The app returned false in QuitRequested().
1402 PRINT("ShutdownProcess::_WaitForApp(): shutdown cancelled "
1403 "by team %" B_PRId32 " (-1 => user)\n", eventTeam);
1405 _DisplayAbortingApp(team);
1406 throw_error(B_SHUTDOWN_CANCELLED);
1410 BAutolock _(fWorkerLock);
1411 if (list != NULL && !list->InfoFor(team))
1412 return true;
1413 } while (event != NO_EVENT);
1415 return false;
1419 void
1420 ShutdownProcess::_QuitApps(AppInfoList& list, bool systemApps)
1422 PRINT("ShutdownProcess::_QuitApps(%s)\n",
1423 (systemApps ? "system" : "user"));
1425 if (systemApps) {
1426 _SetShutdownWindowCancelButtonEnabled(false);
1428 // check one last time for abort events
1429 uint32 event;
1430 do {
1431 team_id team;
1432 int32 phase;
1433 status_t error = _GetNextEvent(event, team, phase, false);
1434 if (error != B_OK)
1435 throw_error(error);
1437 if (event == ABORT_EVENT) {
1438 PRINT("ShutdownProcess::_QuitApps(): shutdown cancelled by "
1439 "team %" B_PRId32 " (-1 => user)\n", team);
1441 _DisplayAbortingApp(team);
1442 throw_error(B_SHUTDOWN_CANCELLED);
1445 } while (event != NO_EVENT);
1448 // prepare the shutdown message
1449 BMessage message;
1450 _PrepareShutdownMessage(message);
1452 // now iterate through the list of apps
1453 while (true) {
1454 // eat events
1455 uint32 event;
1456 do {
1457 team_id team;
1458 int32 phase;
1459 status_t error = _GetNextEvent(event, team, phase, false);
1460 if (error != B_OK)
1461 throw_error(error);
1463 if (!systemApps && event == ABORT_EVENT) {
1464 PRINT("ShutdownProcess::_QuitApps(): shutdown cancelled by "
1465 "team %" B_PRId32 " (-1 => user)\n", team);
1467 _DisplayAbortingApp(team);
1468 throw_error(B_SHUTDOWN_CANCELLED);
1471 } while (event != NO_EVENT);
1473 // get the first app to quit
1474 team_id team = -1;
1475 port_id port = -1;
1476 char appName[B_FILE_NAME_LENGTH];
1478 BAutolock _(fWorkerLock);
1479 while (!list.IsEmpty()) {
1480 RosterAppInfo* info = *list.It();
1481 team = info->team;
1482 port = info->port;
1483 strcpy(appName, info->ref.name);
1485 if (info->IsRunning())
1486 break;
1487 list.RemoveInfo(info);
1488 delete info;
1492 if (team < 0) {
1493 PRINT("ShutdownProcess::_QuitApps() done\n");
1494 return;
1497 // set window text
1498 BString buffer = B_TRANSLATE("Asking \"%appName%\" to quit.");
1499 buffer.ReplaceFirst("%appName%", appName);
1500 _SetShutdownWindowText(buffer.String());
1501 _SetShutdownWindowCurrentApp(team);
1503 // send the shutdown message to the app
1504 PRINT(" sending team %" B_PRId32 " (port: %" B_PRId32 ") a shutdown "
1505 "message\n", team, port);
1506 SingleMessagingTargetSet target(port, B_PREFERRED_TOKEN);
1507 MessageDeliverer::Default()->DeliverMessage(&message, target);
1509 // schedule a timeout event
1510 _ScheduleTimeoutEvent(kAppQuitTimeout, team);
1512 // wait for the app to die or for the timeout to occur
1513 bool appGone = _WaitForApp(team, &list, systemApps);
1514 if (appGone) {
1515 // fine: the app finished in an orderly manner
1516 } else {
1517 // the app is either blocking on a model alert or blocks for another
1518 // reason
1519 if (!systemApps)
1520 _QuitBlockingApp(list, team, appName, true);
1521 else {
1522 // This is a system app: remove it from the list
1523 BAutolock _(fWorkerLock);
1525 if (RosterAppInfo* info = list.InfoFor(team)) {
1526 list.RemoveInfo(info);
1527 delete info;
1535 void
1536 ShutdownProcess::_QuitBackgroundApps()
1538 PRINT("ShutdownProcess::_QuitBackgroundApps()\n");
1540 _SetShutdownWindowText(
1541 B_TRANSLATE("Asking background applications to quit."));
1543 // prepare the shutdown message
1544 BMessage message;
1545 _PrepareShutdownMessage(message);
1547 // send shutdown messages to user apps
1548 BAutolock _(fWorkerLock);
1550 AppInfoListMessagingTargetSet targetSet(fBackgroundApps);
1552 if (targetSet.HasNext()) {
1553 PRINT(" sending shutdown message to %" B_PRId32 " apps\n",
1554 fBackgroundApps.CountInfos());
1556 status_t error = MessageDeliverer::Default()->DeliverMessage(
1557 &message, targetSet);
1558 if (error != B_OK) {
1559 WARNING("_QuitBackgroundApps::_Worker(): Failed to deliver "
1560 "shutdown message to all applications: %s\n",
1561 strerror(error));
1565 PRINT("ShutdownProcess::_QuitBackgroundApps() done\n");
1569 void
1570 ShutdownProcess::_WaitForBackgroundApps()
1572 PRINT("ShutdownProcess::_WaitForBackgroundApps()\n");
1574 // wait for user apps
1575 bool moreApps = true;
1576 while (moreApps) {
1578 BAutolock _(fWorkerLock);
1579 moreApps = !fBackgroundApps.IsEmpty();
1582 if (moreApps) {
1583 uint32 event;
1584 team_id team;
1585 int32 phase;
1586 status_t error = _GetNextEvent(event, team, phase, true);
1587 if (error != B_OK)
1588 throw_error(error);
1590 if (event == ABORT_EVENT) {
1591 // ignore: it's too late to abort the shutdown
1594 if (event == TIMEOUT_EVENT)
1595 return;
1599 PRINT("ShutdownProcess::_WaitForBackgroundApps() done\n");
1603 void
1604 ShutdownProcess::_KillBackgroundApps()
1606 PRINT("ShutdownProcess::_KillBackgroundApps()\n");
1608 while (true) {
1609 // eat events (we need to be responsive for an abort event)
1610 uint32 event;
1611 do {
1612 team_id team;
1613 int32 phase;
1614 status_t error = _GetNextEvent(event, team, phase, false);
1615 if (error != B_OK)
1616 throw_error(error);
1618 } while (event != NO_EVENT);
1620 // get the first team to kill
1621 team_id team = -1;
1622 char appName[B_FILE_NAME_LENGTH];
1623 AppInfoList& list = fBackgroundApps;
1625 BAutolock _(fWorkerLock);
1627 if (!list.IsEmpty()) {
1628 RosterAppInfo* info = *list.It();
1629 team = info->team;
1630 strcpy(appName, info->ref.name);
1635 if (team < 0) {
1636 PRINT("ShutdownProcess::_KillBackgroundApps() done\n");
1637 return;
1640 // the app is either blocking on a model alert or blocks for another
1641 // reason
1642 _QuitBlockingApp(list, team, appName, false);
1647 void
1648 ShutdownProcess::_QuitNonApps()
1650 PRINT("ShutdownProcess::_QuitNonApps()\n");
1652 _SetShutdownWindowText(B_TRANSLATE("Asking other processes to quit."));
1654 // iterate through the remaining teams and send them the TERM signal
1655 int32 cookie = 0;
1656 team_info teamInfo;
1657 while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
1658 if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) {
1659 PRINT(" sending team %" B_PRId32 " TERM signal\n", teamInfo.team);
1661 #ifdef __HAIKU__
1662 // Note: team ID == team main thread ID under Haiku
1663 send_signal(teamInfo.team, SIGTERM);
1664 #else
1665 // We don't want to do this when testing under R5, since it
1666 // would kill all teams besides our app server and registrar.
1667 #endif
1671 // give them a bit of time to terminate
1672 // TODO: Instead of just waiting we could periodically check whether the
1673 // processes are already gone to shorten the process.
1674 snooze(kNonAppQuitTimeout);
1676 // iterate through the remaining teams and kill them
1677 cookie = 0;
1678 while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
1679 if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) {
1680 PRINT(" killing team %" B_PRId32 "\n", teamInfo.team);
1682 #ifdef __HAIKU__
1683 kill_team(teamInfo.team);
1684 #else
1685 // We don't want to do this when testing under R5, since it
1686 // would kill all teams besides our app server and registrar.
1687 #endif
1691 PRINT("ShutdownProcess::_QuitNonApps() done\n");
1695 void
1696 ShutdownProcess::_QuitBlockingApp(AppInfoList& list, team_id team,
1697 const char* appName, bool cancelAllowed)
1699 bool debugged = false;
1700 bool modal = false;
1702 BAutolock _(fWorkerLock);
1703 if (fDebuggedTeams.find(team) != fDebuggedTeams.end())
1704 debugged = true;
1706 if (!debugged)
1707 modal = BPrivate::is_app_showing_modal_window(team);
1709 if (modal) {
1710 // app blocks on a modal window
1711 BString buffer = B_TRANSLATE("The application \"%appName%\" might be "
1712 "blocked on a modal panel.");
1713 buffer.ReplaceFirst("%appName%", appName);
1714 _SetShutdownWindowText(buffer.String());
1715 _SetShutdownWindowCurrentApp(team);
1716 _SetShutdownWindowKillButtonEnabled(true);
1719 if (modal || debugged) {
1720 // wait for something to happen
1721 bool appGone = false;
1722 while (true) {
1723 uint32 event;
1724 team_id eventTeam;
1725 int32 phase;
1726 status_t error = _GetNextEvent(event, eventTeam, phase, true);
1727 if (error != B_OK)
1728 throw_error(error);
1730 if ((event == APP_QUIT_EVENT) && eventTeam == team) {
1731 appGone = true;
1732 break;
1735 if (event == KILL_APP_EVENT && eventTeam == team)
1736 break;
1738 if (event == ABORT_EVENT) {
1739 if (cancelAllowed || debugged) {
1740 PRINT("ShutdownProcess::_QuitBlockingApp(): shutdown "
1741 "cancelled by team %" B_PRId32 " (-1 => user)\n",
1742 eventTeam);
1744 if (!debugged)
1745 _DisplayAbortingApp(eventTeam);
1746 throw_error(B_SHUTDOWN_CANCELLED);
1749 // If the app requests aborting the shutdown, we don't need
1750 // to wait any longer. It has processed the request and
1751 // won't quit by itself. We'll have to kill it.
1752 if (eventTeam == team)
1753 break;
1757 _SetShutdownWindowKillButtonEnabled(false);
1759 if (appGone)
1760 return;
1763 // kill the app
1764 PRINT(" killing team %" B_PRId32 "\n", team);
1766 kill_team(team);
1768 // remove the app (the roster will note eventually and send us
1769 // a notification, but we want to be sure)
1771 BAutolock _(fWorkerLock);
1773 if (RosterAppInfo* info = list.InfoFor(team)) {
1774 list.RemoveInfo(info);
1775 delete info;
1781 void
1782 ShutdownProcess::_DisplayAbortingApp(team_id team)
1784 // find the app that cancelled the shutdown
1785 char appName[B_FILE_NAME_LENGTH];
1786 bool foundApp = false;
1788 BAutolock _(fWorkerLock);
1790 RosterAppInfo* info = fUserApps.InfoFor(team);
1791 if (!info)
1792 info = fSystemApps.InfoFor(team);
1793 if (!info)
1794 fBackgroundApps.InfoFor(team);
1796 if (info) {
1797 foundApp = true;
1798 strcpy(appName, info->ref.name);
1802 if (!foundApp) {
1803 PRINT("ShutdownProcess::_DisplayAbortingApp(): Didn't find the app "
1804 "that has cancelled the shutdown.\n");
1805 return;
1808 // compose the text to be displayed
1809 BString buffer = B_TRANSLATE("Application \"%appName%\" has aborted the "
1810 "shutdown process.");
1811 buffer.ReplaceFirst("%appName%", appName);
1813 // set up the window
1814 _SetShutdownWindowCurrentApp(team);
1815 _SetShutdownWindowText(buffer.String());
1816 _SetShutdownWindowWaitForAbortedOK();
1818 // schedule the timeout event
1819 _SetPhase(ABORTED_PHASE);
1820 _ScheduleTimeoutEvent(kDisplayAbortingAppTimeout);
1822 // wait for the timeout or the user to press the cancel button
1823 while (true) {
1824 uint32 event;
1825 team_id eventTeam;
1826 int32 phase;
1827 status_t error = _GetNextEvent(event, eventTeam, phase, true);
1828 if (error != B_OK)
1829 break;
1831 // stop waiting when the timeout occurs
1832 if (event == TIMEOUT_EVENT)
1833 break;
1835 // stop waiting when the user hit the cancel button
1836 if (event == ABORT_EVENT && phase == ABORTED_PHASE && eventTeam < 0)
1837 break;
1842 /*! Waits until the debugged team list is empty, ie. when there is no one
1843 left to debug.
1845 void
1846 ShutdownProcess::_WaitForDebuggedTeams()
1848 PRINT("ShutdownProcess::_WaitForDebuggedTeams()\n");
1850 BAutolock _(fWorkerLock);
1851 if (fDebuggedTeams.empty())
1852 return;
1855 PRINT(" not empty!\n");
1857 // wait for something to happen
1858 while (true) {
1859 uint32 event;
1860 team_id eventTeam;
1861 int32 phase;
1862 status_t error = _GetNextEvent(event, eventTeam, phase, true);
1863 if (error != B_OK)
1864 throw_error(error);
1866 if (event == ABORT_EVENT)
1867 throw_error(B_SHUTDOWN_CANCELLED);
1869 BAutolock _(fWorkerLock);
1870 if (fDebuggedTeams.empty()) {
1871 PRINT(" out empty");
1872 return;