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