xref: /haiku/src/servers/registrar/ShutdownProcess.cpp (revision 7d6915b4d08ffe728cd38af02843d5e98ddfe0db)
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->SetViewColor(ui_color(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->SetViewColor(ui_color(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),
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 MSG_PHASE_TIMED_OUT:
773 		{
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);
779 
780 			BAutolock _(fWorkerLock);
781 
782 			if (phase == INVALID_PHASE || phase != fCurrentPhase)
783 				return;
784 
785 			// post the event
786 			_PushEvent(TIMEOUT_EVENT, team, phase);
787 
788 			break;
789 		}
790 
791 		case MSG_KILL_APPLICATION:
792 		{
793 			team_id team;
794 			if (message->FindInt32("team", &team) != B_OK)
795 				break;
796 
797 			// post the event
798 			_PushEvent(KILL_APP_EVENT, team, fCurrentPhase);
799 			break;
800 		}
801 
802 		case MSG_CANCEL_SHUTDOWN:
803 		{
804 			// post the event
805 			_PushEvent(ABORT_EVENT, -1, fCurrentPhase);
806 			break;
807 		}
808 
809 		case MSG_REBOOT_SYSTEM:
810 		{
811 			// post the event
812 			_PushEvent(REBOOT_SYSTEM_EVENT, -1, INVALID_PHASE);
813 			break;
814 		}
815 
816 		case MSG_DONE:
817 		{
818 			// notify the registrar that we're done
819 			be_app->PostMessage(B_REG_SHUTDOWN_FINISHED, be_app);
820 			break;
821 		}
822 
823 		case B_REG_TEAM_DEBUGGER_ALERT:
824 		{
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;
831 			}
832 
833 			bool open;
834 			team_id team;
835 			if (message->FindInt32("team", &team) != B_OK
836 				|| message->FindBool("open", &open) != B_OK)
837 				break;
838 
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);
849 			}
850 			break;
851 		}
852 
853 		default:
854 			BLooper::MessageReceived(message);
855 			break;
856 	}
857 }
858 
859 
860 void
861 ShutdownProcess::SendReply(BMessage* request, status_t error)
862 {
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);
870 	}
871 }
872 
873 
874 void
875 ShutdownProcess::_SendReply(status_t error)
876 {
877 	if (!fRequestReplySent) {
878 		SendReply(fRequest, error);
879 		fRequestReplySent = true;
880 	}
881 }
882 
883 
884 void
885 ShutdownProcess::_SetPhase(int32 phase)
886 {
887 	BAutolock _(fWorkerLock);
888 
889 	if (phase == fCurrentPhase)
890 		return;
891 
892 	fCurrentPhase = phase;
893 
894 	// remove the timeout event scheduled for the previous phase
895 	fEventQueue->RemoveEvent(fTimeoutEvent);
896 }
897 
898 
899 void
900 ShutdownProcess::_ScheduleTimeoutEvent(bigtime_t timeout, team_id team)
901 {
902 	BAutolock _(fWorkerLock);
903 
904 	// remove the timeout event
905 	fEventQueue->RemoveEvent(fTimeoutEvent);
906 
907 	// set the event's phase, team and time
908 	fTimeoutEvent->SetPhase(fCurrentPhase);
909 	fTimeoutEvent->SetTeam(team);
910 	fTimeoutEvent->SetTime(system_time() + timeout);
911 
912 	// add the event
913 	fEventQueue->AddEvent(fTimeoutEvent);
914 }
915 
916 
917 void
918 ShutdownProcess::_SetShowShutdownWindow(bool show)
919 {
920 	if (fHasGUI) {
921 		BAutolock _(fWindow);
922 
923 		if (show == fWindow->IsHidden()) {
924 			if (show)
925 				fWindow->Show();
926 			else
927 				fWindow->Hide();
928 		}
929 	}
930 }
931 
932 
933 void
934 ShutdownProcess::_InitShutdownWindow()
935 {
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;
944 			}
945 		}
946 
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.");
955 
956 			fHasGUI = false;
957 		}
958 	}
959 }
960 
961 
962 void
963 ShutdownProcess::_AddShutdownWindowApps(AppInfoList& infos)
964 {
965 	if (!fHasGUI)
966 		return;
967 
968 	for (AppInfoList::Iterator it = infos.It(); it.IsValid(); ++it) {
969 		RosterAppInfo* info = *it;
970 
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;
979 		}
980 
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));
987 		}
988 
989 		// get the application icons
990 #ifdef __HAIKU__
991 		color_space format = B_RGBA32;
992 #else
993 		color_space format = B_CMAP8;
994 #endif
995 
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;
1005 			}
1006 		}
1007 
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;
1017 			}
1018 		}
1019 
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));
1025 		}
1026 	}
1027 }
1028 
1029 
1030 void
1031 ShutdownProcess::_RemoveShutdownWindowApp(team_id team)
1032 {
1033 	if (fHasGUI) {
1034 		BAutolock _(fWindow);
1035 
1036 		fWindow->RemoveApp(team);
1037 	}
1038 }
1039 
1040 
1041 void
1042 ShutdownProcess::_SetShutdownWindowCurrentApp(team_id team)
1043 {
1044 	if (fHasGUI) {
1045 		BAutolock _(fWindow);
1046 
1047 		fWindow->SetCurrentApp(team);
1048 	}
1049 }
1050 
1051 
1052 void
1053 ShutdownProcess::_SetShutdownWindowText(const char* text)
1054 {
1055 	if (fHasGUI) {
1056 		BAutolock _(fWindow);
1057 
1058 		fWindow->SetText(text);
1059 	}
1060 }
1061 
1062 
1063 void
1064 ShutdownProcess::_SetShutdownWindowCancelButtonEnabled(bool enabled)
1065 {
1066 	if (fHasGUI) {
1067 		BAutolock _(fWindow);
1068 
1069 		fWindow->SetCancelShutdownButtonEnabled(enabled);
1070 	}
1071 }
1072 
1073 
1074 void
1075 ShutdownProcess::_SetShutdownWindowKillButtonEnabled(bool enabled)
1076 {
1077 	if (fHasGUI) {
1078 		BAutolock _(fWindow);
1079 
1080 		fWindow->SetKillAppButtonEnabled(enabled);
1081 	}
1082 }
1083 
1084 
1085 void
1086 ShutdownProcess::_SetShutdownWindowWaitForShutdown()
1087 {
1088 	if (fHasGUI) {
1089 		BAutolock _(fWindow);
1090 
1091 		fWindow->SetWaitForShutdown();
1092 	}
1093 }
1094 
1095 
1096 void
1097 ShutdownProcess::_SetShutdownWindowWaitForAbortedOK()
1098 {
1099 	if (fHasGUI) {
1100 		BAutolock _(fWindow);
1101 
1102 		fWindow->SetWaitForAbortedOK();
1103 	}
1104 }
1105 
1106 
1107 void
1108 ShutdownProcess::_NegativeQuitRequestReply(thread_id thread)
1109 {
1110 	BAutolock _(fWorkerLock);
1111 
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);
1116 }
1117 
1118 
1119 void
1120 ShutdownProcess::_PrepareShutdownMessage(BMessage& message) const
1121 {
1122 	message.what = B_QUIT_REQUESTED;
1123 	message.AddBool("_shutdown_", true);
1124 
1125 	BMessage::Private(message).SetReply(BMessenger(fQuitRequestReplyHandler));
1126 }
1127 
1128 
1129 status_t
1130 ShutdownProcess::_ShutDown()
1131 {
1132 	PRINT("Invoking _kern_shutdown(%d)\n", fReboot);
1133 	RETURN_ERROR(_kern_shutdown(fReboot));
1134 }
1135 
1136 
1137 status_t
1138 ShutdownProcess::_PushEvent(uint32 eventType, team_id team, int32 phase)
1139 {
1140 	InternalEvent* event = new(nothrow) InternalEvent(eventType, team, phase);
1141 	if (!event) {
1142 		ERROR("ShutdownProcess::_PushEvent(): Failed to create event!\n");
1143 
1144 		return B_NO_MEMORY;
1145 	}
1146 
1147 	BAutolock _(fWorkerLock);
1148 
1149 	fInternalEvents->Add(event);
1150 	release_sem(fInternalEventSemaphore);
1151 
1152 	return B_OK;
1153 }
1154 
1155 
1156 status_t
1157 ShutdownProcess::_GetNextEvent(uint32& eventType, thread_id& team, int32& phase,
1158 	bool block)
1159 {
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);
1167 
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;
1176 			}
1177 		}
1178 
1179 		// get the event
1180 		BAutolock _(fWorkerLock);
1181 
1182 		InternalEvent* event = fInternalEvents->Head();
1183 		fInternalEvents->Remove(event);
1184 
1185 		eventType = event->Type();
1186 		team = event->Team();
1187 		phase = event->Phase();
1188 
1189 		delete event;
1190 
1191 		// if the event is an obsolete timeout event, we drop it right here
1192 		if (eventType == TIMEOUT_EVENT && phase != fCurrentPhase)
1193 			continue;
1194 
1195 		break;
1196 	}
1197 
1198 	// notify the window, if an app has been removed
1199 	if (eventType == APP_QUIT_EVENT)
1200 		_RemoveShutdownWindowApp(team);
1201 
1202 	return B_OK;
1203 }
1204 
1205 
1206 status_t
1207 ShutdownProcess::_WorkerEntry(void* data)
1208 {
1209 	return ((ShutdownProcess*)data)->_Worker();
1210 }
1211 
1212 
1213 status_t
1214 ShutdownProcess::_Worker()
1215 {
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));
1222 
1223 		fShutdownError = error;
1224 	}
1225 
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);
1230 
1231 	return B_OK;
1232 }
1233 
1234 
1235 void
1236 ShutdownProcess::_WorkerDoShutdown()
1237 {
1238 	PRINT("ShutdownProcess::_WorkerDoShutdown()\n");
1239 
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);
1246 
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();
1270 
1271 		if (result == 1) {
1272 			// Toggle shutdown method
1273 			fReboot = !fReboot;
1274 		} else if (result < 1)
1275 			throw_error(B_SHUTDOWN_CANCELLED);
1276 	}
1277 
1278 	// tell TRoster not to accept new applications anymore
1279 	fRoster->SetShuttingDown(true);
1280 
1281 	fWorkerLock.Lock();
1282 
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;
1291 	}
1292 
1293 	fUserApps.Sort(&inverse_compare_by_registration_time);
1294 	fSystemApps.Sort(&inverse_compare_by_registration_time);
1295 
1296 	fWorkerLock.Unlock();
1297 
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);
1305 
1306 	// sync
1307 	sync();
1308 
1309 	// phase 1: terminate the user apps
1310 	_SetPhase(USER_APP_TERMINATION_PHASE);
1311 	_QuitApps(fUserApps, false);
1312 	_WaitForDebuggedTeams();
1313 
1314 	// phase 2: terminate the system apps
1315 	_SetPhase(SYSTEM_APP_TERMINATION_PHASE);
1316 	_QuitApps(fSystemApps, true);
1317 	_WaitForDebuggedTeams();
1318 
1319 	// phase 3: terminate the background apps
1320 	_SetPhase(BACKGROUND_APP_TERMINATION_PHASE);
1321 	_QuitBackgroundApps();
1322 	_WaitForDebuggedTeams();
1323 
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();
1331 
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();
1340 
1341 	PRINT("  _kern_shutdown() failed\n");
1342 
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);
1355 
1356 		_kern_shutdown(true);
1357 	}
1358 
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
1368 }
1369 
1370 
1371 bool
1372 ShutdownProcess::_WaitForApp(team_id team, AppInfoList* list, bool systemApps)
1373 {
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);
1381 
1382 		if (event == APP_QUIT_EVENT && eventTeam == team)
1383 			return true;
1384 
1385 		if (event == TIMEOUT_EVENT && eventTeam == team)
1386 			return false;
1387 
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);
1393 			}
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);
1404 
1405 				_DisplayAbortingApp(team);
1406 				throw_error(B_SHUTDOWN_CANCELLED);
1407 			}
1408 		}
1409 
1410 		BAutolock _(fWorkerLock);
1411 		if (list != NULL && !list->InfoFor(team))
1412 			return true;
1413 	} while (event != NO_EVENT);
1414 
1415 	return false;
1416 }
1417 
1418 
1419 void
1420 ShutdownProcess::_QuitApps(AppInfoList& list, bool systemApps)
1421 {
1422 	PRINT("ShutdownProcess::_QuitApps(%s)\n",
1423 		(systemApps ? "system" : "user"));
1424 
1425 	if (systemApps) {
1426 		_SetShutdownWindowCancelButtonEnabled(false);
1427 
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);
1436 
1437 			if (event == ABORT_EVENT) {
1438 				PRINT("ShutdownProcess::_QuitApps(): shutdown cancelled by "
1439 					"team %" B_PRId32 " (-1 => user)\n", team);
1440 
1441 				_DisplayAbortingApp(team);
1442 				throw_error(B_SHUTDOWN_CANCELLED);
1443 			}
1444 
1445 		} while (event != NO_EVENT);
1446 	}
1447 
1448 	// prepare the shutdown message
1449 	BMessage message;
1450 	_PrepareShutdownMessage(message);
1451 
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);
1462 
1463 			if (!systemApps && event == ABORT_EVENT) {
1464 				PRINT("ShutdownProcess::_QuitApps(): shutdown cancelled by "
1465 					"team %" B_PRId32 " (-1 => user)\n", team);
1466 
1467 				_DisplayAbortingApp(team);
1468 				throw_error(B_SHUTDOWN_CANCELLED);
1469 			}
1470 
1471 		} while (event != NO_EVENT);
1472 
1473 		// get the first app to quit
1474 		team_id team = -1;
1475 		port_id port = -1;
1476 		char appName[B_FILE_NAME_LENGTH];
1477 		{
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);
1484 
1485 				if (info->IsRunning())
1486 					break;
1487 				list.RemoveInfo(info);
1488 				delete info;
1489 			}
1490 		}
1491 
1492 		if (team < 0) {
1493 			PRINT("ShutdownProcess::_QuitApps() done\n");
1494 			return;
1495 		}
1496 
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);
1502 
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);
1508 
1509 		// schedule a timeout event
1510 		_ScheduleTimeoutEvent(kAppQuitTimeout, team);
1511 
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);
1524 
1525 				if (RosterAppInfo* info = list.InfoFor(team)) {
1526 					list.RemoveInfo(info);
1527 					delete info;
1528 				}
1529 			}
1530 		}
1531 	}
1532 }
1533 
1534 
1535 void
1536 ShutdownProcess::_QuitBackgroundApps()
1537 {
1538 	PRINT("ShutdownProcess::_QuitBackgroundApps()\n");
1539 
1540 	_SetShutdownWindowText(
1541 		B_TRANSLATE("Asking background applications to quit."));
1542 
1543 	// prepare the shutdown message
1544 	BMessage message;
1545 	_PrepareShutdownMessage(message);
1546 
1547 	// send shutdown messages to user apps
1548 	BAutolock _(fWorkerLock);
1549 
1550 	AppInfoListMessagingTargetSet targetSet(fBackgroundApps);
1551 
1552 	if (targetSet.HasNext()) {
1553 		PRINT("  sending shutdown message to %" B_PRId32 " apps\n",
1554 			fBackgroundApps.CountInfos());
1555 
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));
1562 		}
1563 	}
1564 
1565 	PRINT("ShutdownProcess::_QuitBackgroundApps() done\n");
1566 }
1567 
1568 
1569 void
1570 ShutdownProcess::_WaitForBackgroundApps()
1571 {
1572 	PRINT("ShutdownProcess::_WaitForBackgroundApps()\n");
1573 
1574 	// wait for user apps
1575 	bool moreApps = true;
1576 	while (moreApps) {
1577 		{
1578 			BAutolock _(fWorkerLock);
1579 			moreApps = !fBackgroundApps.IsEmpty();
1580 		}
1581 
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);
1589 
1590 			if (event == ABORT_EVENT) {
1591 				// ignore: it's too late to abort the shutdown
1592 			}
1593 
1594 			if (event == TIMEOUT_EVENT)
1595 				return;
1596 		}
1597 	}
1598 
1599 	PRINT("ShutdownProcess::_WaitForBackgroundApps() done\n");
1600 }
1601 
1602 
1603 void
1604 ShutdownProcess::_KillBackgroundApps()
1605 {
1606 	PRINT("ShutdownProcess::_KillBackgroundApps()\n");
1607 
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);
1617 
1618 		} while (event != NO_EVENT);
1619 
1620 		// get the first team to kill
1621 		team_id team = -1;
1622 		char appName[B_FILE_NAME_LENGTH];
1623 		AppInfoList& list = fBackgroundApps;
1624 		{
1625 			BAutolock _(fWorkerLock);
1626 
1627 			if (!list.IsEmpty()) {
1628 				RosterAppInfo* info = *list.It();
1629 				team = info->team;
1630 				strcpy(appName, info->ref.name);
1631 			}
1632 		}
1633 
1634 
1635 		if (team < 0) {
1636 			PRINT("ShutdownProcess::_KillBackgroundApps() done\n");
1637 			return;
1638 		}
1639 
1640 		// the app is either blocking on a model alert or blocks for another
1641 		// reason
1642 		_QuitBlockingApp(list, team, appName, false);
1643 	}
1644 }
1645 
1646 
1647 void
1648 ShutdownProcess::_QuitNonApps()
1649 {
1650 	PRINT("ShutdownProcess::_QuitNonApps()\n");
1651 
1652 	_SetShutdownWindowText(B_TRANSLATE("Asking other processes to quit."));
1653 
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);
1660 
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
1668 		}
1669 	}
1670 
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);
1675 
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);
1681 
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
1688 		}
1689 	}
1690 
1691 	PRINT("ShutdownProcess::_QuitNonApps() done\n");
1692 }
1693 
1694 
1695 void
1696 ShutdownProcess::_QuitBlockingApp(AppInfoList& list, team_id team,
1697 	const char* appName, bool cancelAllowed)
1698 {
1699 	bool debugged = false;
1700 	bool modal = false;
1701 	{
1702 		BAutolock _(fWorkerLock);
1703 		if (fDebuggedTeams.find(team) != fDebuggedTeams.end())
1704 			debugged = true;
1705 	}
1706 	if (!debugged)
1707 		modal = BPrivate::is_app_showing_modal_window(team);
1708 
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);
1717 	}
1718 
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);
1729 
1730 			if ((event == APP_QUIT_EVENT) && eventTeam == team) {
1731 				appGone = true;
1732 				break;
1733 			}
1734 
1735 			if (event == KILL_APP_EVENT && eventTeam == team)
1736 				break;
1737 
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);
1743 
1744 					if (!debugged)
1745 						_DisplayAbortingApp(eventTeam);
1746 					throw_error(B_SHUTDOWN_CANCELLED);
1747 				}
1748 
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;
1754 			}
1755 		}
1756 
1757 		_SetShutdownWindowKillButtonEnabled(false);
1758 
1759 		if (appGone)
1760 			return;
1761 	}
1762 
1763 	// kill the app
1764 	PRINT("  killing team %" B_PRId32 "\n", team);
1765 
1766 	kill_team(team);
1767 
1768 	// remove the app (the roster will note eventually and send us
1769 	// a notification, but we want to be sure)
1770 	{
1771 		BAutolock _(fWorkerLock);
1772 
1773 		if (RosterAppInfo* info = list.InfoFor(team)) {
1774 			list.RemoveInfo(info);
1775 			delete info;
1776 		}
1777 	}
1778 }
1779 
1780 
1781 void
1782 ShutdownProcess::_DisplayAbortingApp(team_id team)
1783 {
1784 	// find the app that cancelled the shutdown
1785 	char appName[B_FILE_NAME_LENGTH];
1786 	bool foundApp = false;
1787 	{
1788 		BAutolock _(fWorkerLock);
1789 
1790 		RosterAppInfo* info = fUserApps.InfoFor(team);
1791 		if (!info)
1792 			info = fSystemApps.InfoFor(team);
1793 		if (!info)
1794 			fBackgroundApps.InfoFor(team);
1795 
1796 		if (info) {
1797 			foundApp = true;
1798 			strcpy(appName, info->ref.name);
1799 		}
1800 	}
1801 
1802 	if (!foundApp) {
1803 		PRINT("ShutdownProcess::_DisplayAbortingApp(): Didn't find the app "
1804 			"that has cancelled the shutdown.\n");
1805 		return;
1806 	}
1807 
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);
1812 
1813 	// set up the window
1814 	_SetShutdownWindowCurrentApp(team);
1815 	_SetShutdownWindowText(buffer.String());
1816 	_SetShutdownWindowWaitForAbortedOK();
1817 
1818 	// schedule the timeout event
1819 	_SetPhase(ABORTED_PHASE);
1820 	_ScheduleTimeoutEvent(kDisplayAbortingAppTimeout);
1821 
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;
1830 
1831 		// stop waiting when the timeout occurs
1832 		if (event == TIMEOUT_EVENT)
1833 			break;
1834 
1835 		// stop waiting when the user hit the cancel button
1836 		if (event == ABORT_EVENT && phase == ABORTED_PHASE && eventTeam < 0)
1837 			break;
1838 	}
1839 }
1840 
1841 
1842 /*!	Waits until the debugged team list is empty, ie. when there is no one
1843 	left to debug.
1844 */
1845 void
1846 ShutdownProcess::_WaitForDebuggedTeams()
1847 {
1848 	PRINT("ShutdownProcess::_WaitForDebuggedTeams()\n");
1849 	{
1850 		BAutolock _(fWorkerLock);
1851 		if (fDebuggedTeams.empty())
1852 			return;
1853 	}
1854 
1855 	PRINT("  not empty!\n");
1856 
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);
1865 
1866 		if (event == ABORT_EVENT)
1867 			throw_error(B_SHUTDOWN_CANCELLED);
1868 
1869 		BAutolock _(fWorkerLock);
1870 		if (fDebuggedTeams.empty()) {
1871 			PRINT("  out empty");
1872 			return;
1873 		}
1874 	}
1875 }
1876 
1877