xref: /haiku/src/servers/registrar/ShutdownProcess.cpp (revision d374a27286b8a52974a97dba0d5966ea026a665d)
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_TRANSLATE_CONTEXT
52 #define B_TRANSLATE_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: %ld\n",
743 				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: %ld, team: %ld\n", phase, team));
778 
779 			BAutolock _(fWorkerLock);
780 
781 			if (phase == INVALID_PHASE || phase != fCurrentPhase)
782 				return;
783 
784 			// post the event
785 			_PushEvent(TIMEOUT_EVENT, team, phase);
786 
787 			break;
788 		}
789 
790 		case MSG_KILL_APPLICATION:
791 		{
792 			team_id team;
793 			if (message->FindInt32("team", &team) != B_OK)
794 				break;
795 
796 			// post the event
797 			_PushEvent(KILL_APP_EVENT, team, fCurrentPhase);
798 			break;
799 		}
800 
801 		case MSG_CANCEL_SHUTDOWN:
802 		{
803 			// post the event
804 			_PushEvent(ABORT_EVENT, -1, fCurrentPhase);
805 			break;
806 		}
807 
808 		case MSG_REBOOT_SYSTEM:
809 		{
810 			// post the event
811 			_PushEvent(REBOOT_SYSTEM_EVENT, -1, INVALID_PHASE);
812 			break;
813 		}
814 
815 		case MSG_DONE:
816 		{
817 			// notify the registrar that we're done
818 			be_app->PostMessage(B_REG_SHUTDOWN_FINISHED, be_app);
819 			break;
820 		}
821 
822 		case B_REG_TEAM_DEBUGGER_ALERT:
823 		{
824 			bool stopShutdown;
825 			if (message->FindBool("stop shutdown", &stopShutdown) == B_OK
826 				&& stopShutdown) {
827 				// post abort event to the worker
828 				_PushEvent(ABORT_EVENT, -1, fCurrentPhase);
829 				break;
830 			}
831 
832 			bool open;
833 			team_id team;
834 			if (message->FindInt32("team", &team) != B_OK
835 				|| message->FindBool("open", &open) != B_OK)
836 				break;
837 
838 			BAutolock _(fWorkerLock);
839 			if (open) {
840 				PRINT(("B_REG_TEAM_DEBUGGER_ALERT: insert %ld\n", team));
841 				fDebuggedTeams.insert(team);
842 			} else {
843 				PRINT(("B_REG_TEAM_DEBUGGER_ALERT: remove %ld\n", team));
844 				fDebuggedTeams.erase(team);
845 				_PushEvent(DEBUG_EVENT, -1, fCurrentPhase);
846 			}
847 			break;
848 		}
849 
850 		default:
851 			BLooper::MessageReceived(message);
852 			break;
853 	}
854 }
855 
856 
857 void
858 ShutdownProcess::SendReply(BMessage* request, status_t error)
859 {
860 	if (error == B_OK) {
861 		BMessage reply(B_REG_SUCCESS);
862 		request->SendReply(&reply);
863 	} else {
864 		BMessage reply(B_REG_ERROR);
865 		reply.AddInt32("error", error);
866 		request->SendReply(&reply);
867 	}
868 }
869 
870 
871 void
872 ShutdownProcess::_SendReply(status_t error)
873 {
874 	if (!fRequestReplySent) {
875 		SendReply(fRequest, error);
876 		fRequestReplySent = true;
877 	}
878 }
879 
880 
881 void
882 ShutdownProcess::_SetPhase(int32 phase)
883 {
884 	BAutolock _(fWorkerLock);
885 
886 	if (phase == fCurrentPhase)
887 		return;
888 
889 	fCurrentPhase = phase;
890 
891 	// remove the timeout event scheduled for the previous phase
892 	fEventQueue->RemoveEvent(fTimeoutEvent);
893 }
894 
895 
896 void
897 ShutdownProcess::_ScheduleTimeoutEvent(bigtime_t timeout, team_id team)
898 {
899 	BAutolock _(fWorkerLock);
900 
901 	// remove the timeout event
902 	fEventQueue->RemoveEvent(fTimeoutEvent);
903 
904 	// set the event's phase, team and time
905 	fTimeoutEvent->SetPhase(fCurrentPhase);
906 	fTimeoutEvent->SetTeam(team);
907 	fTimeoutEvent->SetTime(system_time() + timeout);
908 
909 	// add the event
910 	fEventQueue->AddEvent(fTimeoutEvent);
911 }
912 
913 
914 void
915 ShutdownProcess::_SetShowShutdownWindow(bool show)
916 {
917 	if (fHasGUI) {
918 		BAutolock _(fWindow);
919 
920 		if (show == fWindow->IsHidden()) {
921 			if (show)
922 				fWindow->Show();
923 			else
924 				fWindow->Hide();
925 		}
926 	}
927 }
928 
929 
930 void
931 ShutdownProcess::_InitShutdownWindow()
932 {
933 	// prepare the window
934 	if (fHasGUI) {
935 		fWindow = new(nothrow) ShutdownWindow;
936 		if (fWindow != NULL) {
937 			status_t error = fWindow->Init(BMessenger(this));
938 			if (error != B_OK) {
939 				delete fWindow;
940 				fWindow = NULL;
941 			}
942 		}
943 
944 		// add the applications
945 		if (fWindow) {
946 			BAutolock _(fWorkerLock);
947 			_AddShutdownWindowApps(fUserApps);
948 			_AddShutdownWindowApps(fSystemApps);
949 		} else {
950 			WARNING(("ShutdownProcess::Init(): Failed to create or init "
951 				"shutdown window."));
952 
953 			fHasGUI = false;
954 		}
955 	}
956 }
957 
958 
959 void
960 ShutdownProcess::_AddShutdownWindowApps(AppInfoList& infos)
961 {
962 	if (!fHasGUI)
963 		return;
964 
965 	for (AppInfoList::Iterator it = infos.It(); it.IsValid(); ++it) {
966 		RosterAppInfo* info = *it;
967 
968 		// init an app file info
969 		BFile file;
970 		status_t error = file.SetTo(&info->ref, B_READ_ONLY);
971 		if (error != B_OK) {
972 			WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to "
973 				"open file for app %s: %s\n", info->signature,
974 				strerror(error)));
975 			continue;
976 		}
977 
978 		BAppFileInfo appFileInfo;
979 		error = appFileInfo.SetTo(&file);
980 		if (error != B_OK) {
981 			WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to "
982 				"init app file info for app %s: %s\n", info->signature,
983 				strerror(error)));
984 		}
985 
986 		// get the application icons
987 #ifdef __HAIKU__
988 		color_space format = B_RGBA32;
989 #else
990 		color_space format = B_CMAP8;
991 #endif
992 
993 		// mini icon
994 		BBitmap* miniIcon = new(nothrow) BBitmap(BRect(0, 0, 15, 15), format);
995 		if (miniIcon != NULL) {
996 			error = miniIcon->InitCheck();
997 			if (error == B_OK)
998 				error = appFileInfo.GetTrackerIcon(miniIcon, B_MINI_ICON);
999 			if (error != B_OK) {
1000 				delete miniIcon;
1001 				miniIcon = NULL;
1002 			}
1003 		}
1004 
1005 		// mini icon
1006 		BBitmap* largeIcon = new(nothrow) BBitmap(BRect(0, 0, 31, 31), format);
1007 		if (largeIcon != NULL) {
1008 			error = largeIcon->InitCheck();
1009 			if (error == B_OK)
1010 				error = appFileInfo.GetTrackerIcon(largeIcon, B_LARGE_ICON);
1011 			if (error != B_OK) {
1012 				delete largeIcon;
1013 				largeIcon = NULL;
1014 			}
1015 		}
1016 
1017 		// add the app
1018 		error = fWindow->AddApp(info->team, miniIcon, largeIcon);
1019 		if (error != B_OK) {
1020 			WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to "
1021 				"add app to the shutdown window: %s\n", strerror(error)));
1022 		}
1023 	}
1024 }
1025 
1026 
1027 void
1028 ShutdownProcess::_RemoveShutdownWindowApp(team_id team)
1029 {
1030 	if (fHasGUI) {
1031 		BAutolock _(fWindow);
1032 
1033 		fWindow->RemoveApp(team);
1034 	}
1035 }
1036 
1037 
1038 void
1039 ShutdownProcess::_SetShutdownWindowCurrentApp(team_id team)
1040 {
1041 	if (fHasGUI) {
1042 		BAutolock _(fWindow);
1043 
1044 		fWindow->SetCurrentApp(team);
1045 	}
1046 }
1047 
1048 
1049 void
1050 ShutdownProcess::_SetShutdownWindowText(const char* text)
1051 {
1052 	if (fHasGUI) {
1053 		BAutolock _(fWindow);
1054 
1055 		fWindow->SetText(text);
1056 	}
1057 }
1058 
1059 
1060 void
1061 ShutdownProcess::_SetShutdownWindowCancelButtonEnabled(bool enabled)
1062 {
1063 	if (fHasGUI) {
1064 		BAutolock _(fWindow);
1065 
1066 		fWindow->SetCancelShutdownButtonEnabled(enabled);
1067 	}
1068 }
1069 
1070 
1071 void
1072 ShutdownProcess::_SetShutdownWindowKillButtonEnabled(bool enabled)
1073 {
1074 	if (fHasGUI) {
1075 		BAutolock _(fWindow);
1076 
1077 		fWindow->SetKillAppButtonEnabled(enabled);
1078 	}
1079 }
1080 
1081 
1082 void
1083 ShutdownProcess::_SetShutdownWindowWaitForShutdown()
1084 {
1085 	if (fHasGUI) {
1086 		BAutolock _(fWindow);
1087 
1088 		fWindow->SetWaitForShutdown();
1089 	}
1090 }
1091 
1092 
1093 void
1094 ShutdownProcess::_SetShutdownWindowWaitForAbortedOK()
1095 {
1096 	if (fHasGUI) {
1097 		BAutolock _(fWindow);
1098 
1099 		fWindow->SetWaitForAbortedOK();
1100 	}
1101 }
1102 
1103 
1104 void
1105 ShutdownProcess::_NegativeQuitRequestReply(thread_id thread)
1106 {
1107 	BAutolock _(fWorkerLock);
1108 
1109 	// Note: team ID == team main thread ID under Haiku. When testing under R5
1110 	// using the team ID in case of an ABORT_EVENT won't work correctly. But
1111 	// this is done only for system apps.
1112 	_PushEvent(ABORT_EVENT, thread, fCurrentPhase);
1113 }
1114 
1115 
1116 void
1117 ShutdownProcess::_PrepareShutdownMessage(BMessage& message) const
1118 {
1119 	message.what = B_QUIT_REQUESTED;
1120 	message.AddBool("_shutdown_", true);
1121 
1122 	BMessage::Private(message).SetReply(BMessenger(fQuitRequestReplyHandler));
1123 }
1124 
1125 
1126 status_t
1127 ShutdownProcess::_ShutDown()
1128 {
1129 	PRINT(("Invoking _kern_shutdown(%d)\n", fReboot));
1130 	RETURN_ERROR(_kern_shutdown(fReboot));
1131 }
1132 
1133 
1134 status_t
1135 ShutdownProcess::_PushEvent(uint32 eventType, team_id team, int32 phase)
1136 {
1137 	InternalEvent* event = new(nothrow) InternalEvent(eventType, team, phase);
1138 	if (!event) {
1139 		ERROR(("ShutdownProcess::_PushEvent(): Failed to create event!\n"));
1140 
1141 		return B_NO_MEMORY;
1142 	}
1143 
1144 	BAutolock _(fWorkerLock);
1145 
1146 	fInternalEvents->Add(event);
1147 	release_sem(fInternalEventSemaphore);
1148 
1149 	return B_OK;
1150 }
1151 
1152 
1153 status_t
1154 ShutdownProcess::_GetNextEvent(uint32& eventType, thread_id& team, int32& phase,
1155 	bool block)
1156 {
1157 	while (true) {
1158 		// acquire the semaphore
1159 		if (block) {
1160 			status_t error;
1161 			do {
1162 				error = acquire_sem(fInternalEventSemaphore);
1163 			} while (error == B_INTERRUPTED);
1164 
1165 			if (error != B_OK)
1166 				return error;
1167 		} else {
1168 			status_t error = acquire_sem_etc(fInternalEventSemaphore, 1,
1169 				B_RELATIVE_TIMEOUT, 0);
1170 			if (error != B_OK) {
1171 				eventType = NO_EVENT;
1172 				return B_OK;
1173 			}
1174 		}
1175 
1176 		// get the event
1177 		BAutolock _(fWorkerLock);
1178 
1179 		InternalEvent* event = fInternalEvents->Head();
1180 		fInternalEvents->Remove(event);
1181 
1182 		eventType = event->Type();
1183 		team = event->Team();
1184 		phase = event->Phase();
1185 
1186 		delete event;
1187 
1188 		// if the event is an obsolete timeout event, we drop it right here
1189 		if (eventType == TIMEOUT_EVENT && phase != fCurrentPhase)
1190 			continue;
1191 
1192 		break;
1193 	}
1194 
1195 	// notify the window, if an app has been removed
1196 	if (eventType == APP_QUIT_EVENT)
1197 		_RemoveShutdownWindowApp(team);
1198 
1199 	return B_OK;
1200 }
1201 
1202 
1203 status_t
1204 ShutdownProcess::_WorkerEntry(void* data)
1205 {
1206 	return ((ShutdownProcess*)data)->_Worker();
1207 }
1208 
1209 
1210 status_t
1211 ShutdownProcess::_Worker()
1212 {
1213 	try {
1214 		_WorkerDoShutdown();
1215 		fShutdownError = B_OK;
1216 	} catch (status_t error) {
1217 		PRINT(("ShutdownProcess::_Worker(): error while shutting down: %s\n",
1218 			strerror(error)));
1219 
1220 		fShutdownError = error;
1221 	}
1222 
1223 	// this can happen only, if the shutdown process failed or was aborted:
1224 	// notify the looper
1225 	_SetPhase(DONE_PHASE);
1226 	PostMessage(MSG_DONE);
1227 
1228 	return B_OK;
1229 }
1230 
1231 
1232 void
1233 ShutdownProcess::_WorkerDoShutdown()
1234 {
1235 	PRINT(("ShutdownProcess::_WorkerDoShutdown()\n"));
1236 
1237 	// If we are here, the shutdown process has been initiated successfully,
1238 	// that is, if an asynchronous BRoster::Shutdown() was requested, we
1239 	// notify the caller at this point.
1240 	bool synchronous;
1241 	if (fRequest->FindBool("synchronous", &synchronous) == B_OK && !synchronous)
1242 		_SendReply(B_OK);
1243 
1244 	// ask the user to confirm the shutdown, if desired
1245 	bool askUser;
1246 	if (fHasGUI && fRequest->FindBool("confirm", &askUser) == B_OK && askUser) {
1247 		const char* restart = B_TRANSLATE("Restart");
1248 		const char* shutdown = B_TRANSLATE("Shut down");
1249 		BString title = B_TRANSLATE("%action%?");
1250 		title.ReplaceFirst("%action%", fReboot ? restart : shutdown);
1251 		const char* text = fReboot
1252 			? B_TRANSLATE("Do you really want to restart the system?")
1253 			: B_TRANSLATE("Do you really want to shut down the system?");
1254 		const char* defaultText = fReboot ? restart : shutdown;
1255 		const char* otherText = fReboot ? shutdown : restart;
1256 		BAlert* alert = new BAlert(title.String(), text,
1257 			B_TRANSLATE("Cancel"), otherText, defaultText,
1258 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1259 		alert->SetShortcut(0, B_ESCAPE);
1260 		// We want the alert to behave more like a regular window...
1261 		alert->SetFeel(B_NORMAL_WINDOW_FEEL);
1262 		// ...but not quit. Minimizing the alert would prevent the user from
1263 		// finding it again, since registrar does not have an entry in the
1264 		// Deskbar.
1265 		alert->SetFlags(alert->Flags() | B_NOT_MINIMIZABLE);
1266 		alert->SetWorkspaces(B_ALL_WORKSPACES);
1267 		int32 result = alert->Go();
1268 
1269 		if (result == 1) {
1270 			// Toggle shutdown method
1271 			fReboot = !fReboot;
1272 		} else if (result < 1)
1273 			throw_error(B_SHUTDOWN_CANCELLED);
1274 	}
1275 
1276 	// tell TRoster not to accept new applications anymore
1277 	fRoster->SetShuttingDown(true);
1278 
1279 	fWorkerLock.Lock();
1280 
1281 	// get a list of all applications to shut down and sort them
1282 	status_t status = fRoster->GetShutdownApps(fUserApps, fSystemApps,
1283 		fBackgroundApps, fVitalSystemApps);
1284 	if (status  != B_OK) {
1285 		fWorkerLock.Unlock();
1286 		fRoster->RemoveWatcher(this);
1287 		fRoster->SetShuttingDown(false);
1288 		return;
1289 	}
1290 
1291 	fUserApps.Sort(&inverse_compare_by_registration_time);
1292 	fSystemApps.Sort(&inverse_compare_by_registration_time);
1293 
1294 	fWorkerLock.Unlock();
1295 
1296 	// make the shutdown window ready and show it
1297 	_InitShutdownWindow();
1298 	_SetShutdownWindowCurrentApp(-1);
1299 	_SetShutdownWindowText(B_TRANSLATE("Tidying things up a bit."));
1300 	_SetShutdownWindowCancelButtonEnabled(true);
1301 	_SetShutdownWindowKillButtonEnabled(false);
1302 	_SetShowShutdownWindow(true);
1303 
1304 	// sync
1305 	sync();
1306 
1307 	// phase 1: terminate the user apps
1308 	_SetPhase(USER_APP_TERMINATION_PHASE);
1309 	_QuitApps(fUserApps, false);
1310 	_WaitForDebuggedTeams();
1311 
1312 	// phase 2: terminate the system apps
1313 	_SetPhase(SYSTEM_APP_TERMINATION_PHASE);
1314 	_QuitApps(fSystemApps, true);
1315 	_WaitForDebuggedTeams();
1316 
1317 	// phase 3: terminate the background apps
1318 	_SetPhase(BACKGROUND_APP_TERMINATION_PHASE);
1319 	_QuitBackgroundApps();
1320 	_WaitForDebuggedTeams();
1321 
1322 	// phase 4: terminate the other processes
1323 	_SetPhase(OTHER_PROCESSES_TERMINATION_PHASE);
1324 	_QuitNonApps();
1325 	_ScheduleTimeoutEvent(kBackgroundAppQuitTimeout, -1);
1326 	_WaitForBackgroundApps();
1327 	_KillBackgroundApps();
1328 	_WaitForDebuggedTeams();
1329 
1330 	// we're through: do the shutdown
1331 	_SetPhase(DONE_PHASE);
1332 	if (fReboot)
1333 		_SetShutdownWindowText(B_TRANSLATE("Restarting" B_UTF8_ELLIPSIS));
1334 	else
1335 		_SetShutdownWindowText(B_TRANSLATE("Shutting down" B_UTF8_ELLIPSIS));
1336 	_ShutDown();
1337 	_SetShutdownWindowWaitForShutdown();
1338 
1339 	PRINT(("  _kern_shutdown() failed\n"));
1340 
1341 	// shutdown failed: This can happen for power off mode -- reboot should
1342 	// always work.
1343 	if (fHasGUI) {
1344 		// wait for the reboot event
1345 		uint32 event;
1346 		do {
1347 			team_id team;
1348 			int32 phase;
1349 			status = _GetNextEvent(event, team, phase, true);
1350 			if (status != B_OK)
1351 				break;
1352 		} while (event != REBOOT_SYSTEM_EVENT);
1353 
1354 		_kern_shutdown(true);
1355 	}
1356 
1357 	// either there's no GUI or reboot failed: we enter the kernel debugger
1358 	// instead
1359 #ifdef __HAIKU__
1360 // TODO: Introduce the syscall.
1361 //	while (true) {
1362 //		_kern_kernel_debugger("The system is shut down. It's now safe to turn "
1363 //			"off the computer.");
1364 //	}
1365 #endif
1366 }
1367 
1368 
1369 bool
1370 ShutdownProcess::_WaitForApp(team_id team, AppInfoList* list, bool systemApps)
1371 {
1372 	uint32 event;
1373 	do {
1374 		team_id eventTeam;
1375 		int32 phase;
1376 		status_t error = _GetNextEvent(event, eventTeam, phase, true);
1377 		if (error != B_OK)
1378 			throw_error(error);
1379 
1380 		if (event == APP_QUIT_EVENT && eventTeam == team)
1381 			return true;
1382 
1383 		if (event == TIMEOUT_EVENT && eventTeam == team)
1384 			return false;
1385 
1386 		if (event == ABORT_EVENT) {
1387 			if (eventTeam == -1) {
1388 				// The user canceled the shutdown process by pressing the
1389 				// Cancel button.
1390 				throw_error(B_SHUTDOWN_CANCELLED);
1391 			}
1392 			if (systemApps) {
1393 				// If the app requests aborting the shutdown, we don't need
1394 				// to wait any longer. It has processed the request and
1395 				// won't quit by itself. We ignore this for system apps.
1396 				if (eventTeam == team)
1397 					return false;
1398 			} else {
1399 				// The app returned false in QuitRequested().
1400 				PRINT(("ShutdownProcess::_WaitForApp(): shutdown cancelled "
1401 					"by team %ld (-1 => user)\n", eventTeam));
1402 
1403 				_DisplayAbortingApp(team);
1404 				throw_error(B_SHUTDOWN_CANCELLED);
1405 			}
1406 		}
1407 
1408 		BAutolock _(fWorkerLock);
1409 		if (list != NULL && !list->InfoFor(team))
1410 			return true;
1411 	} while (event != NO_EVENT);
1412 
1413 	return false;
1414 }
1415 
1416 
1417 void
1418 ShutdownProcess::_QuitApps(AppInfoList& list, bool systemApps)
1419 {
1420 	PRINT(("ShutdownProcess::_QuitApps(%s)\n",
1421 		(systemApps ? "system" : "user")));
1422 
1423 	if (systemApps) {
1424 		_SetShutdownWindowCancelButtonEnabled(false);
1425 
1426 		// check one last time for abort events
1427 		uint32 event;
1428 		do {
1429 			team_id team;
1430 			int32 phase;
1431 			status_t error = _GetNextEvent(event, team, phase, false);
1432 			if (error != B_OK)
1433 				throw_error(error);
1434 
1435 			if (event == ABORT_EVENT) {
1436 				PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by "
1437 					"team %ld (-1 => user)\n", team));
1438 
1439 				_DisplayAbortingApp(team);
1440 				throw_error(B_SHUTDOWN_CANCELLED);
1441 			}
1442 
1443 		} while (event != NO_EVENT);
1444 	}
1445 
1446 	// prepare the shutdown message
1447 	BMessage message;
1448 	_PrepareShutdownMessage(message);
1449 
1450 	// now iterate through the list of apps
1451 	while (true) {
1452 		// eat events
1453 		uint32 event;
1454 		do {
1455 			team_id team;
1456 			int32 phase;
1457 			status_t error = _GetNextEvent(event, team, phase, false);
1458 			if (error != B_OK)
1459 				throw_error(error);
1460 
1461 			if (!systemApps && event == ABORT_EVENT) {
1462 				PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by "
1463 					"team %ld (-1 => user)\n", team));
1464 
1465 				_DisplayAbortingApp(team);
1466 				throw_error(B_SHUTDOWN_CANCELLED);
1467 			}
1468 
1469 		} while (event != NO_EVENT);
1470 
1471 		// get the first app to quit
1472 		team_id team = -1;
1473 		port_id port = -1;
1474 		char appName[B_FILE_NAME_LENGTH];
1475 		{
1476 			BAutolock _(fWorkerLock);
1477 			while (!list.IsEmpty()) {
1478 				RosterAppInfo* info = *list.It();
1479 				team = info->team;
1480 				port = info->port;
1481 				strcpy(appName, info->ref.name);
1482 
1483 				if (info->IsRunning())
1484 					break;
1485 				list.RemoveInfo(info);
1486 				delete info;
1487 			}
1488 		}
1489 
1490 		if (team < 0) {
1491 			PRINT(("ShutdownProcess::_QuitApps() done\n"));
1492 			return;
1493 		}
1494 
1495 		// set window text
1496 		BString buffer = B_TRANSLATE("Asking \"%appName%\" to quit.");
1497 		buffer.ReplaceFirst("%appName%", appName);
1498 		_SetShutdownWindowText(buffer.String());
1499 		_SetShutdownWindowCurrentApp(team);
1500 
1501 		// send the shutdown message to the app
1502 		PRINT(("  sending team %ld (port: %ld) a shutdown message\n", team,
1503 			port));
1504 		SingleMessagingTargetSet target(port, B_PREFERRED_TOKEN);
1505 		MessageDeliverer::Default()->DeliverMessage(&message, target);
1506 
1507 		// schedule a timeout event
1508 		_ScheduleTimeoutEvent(kAppQuitTimeout, team);
1509 
1510 		// wait for the app to die or for the timeout to occur
1511 		bool appGone = _WaitForApp(team, &list, systemApps);
1512 		if (appGone) {
1513 			// fine: the app finished in an orderly manner
1514 		} else {
1515 			// the app is either blocking on a model alert or blocks for another
1516 			// reason
1517 			if (!systemApps)
1518 				_QuitBlockingApp(list, team, appName, true);
1519 			else {
1520 				// This is a system app: remove it from the list
1521 				BAutolock _(fWorkerLock);
1522 
1523 				if (RosterAppInfo* info = list.InfoFor(team)) {
1524 					list.RemoveInfo(info);
1525 					delete info;
1526 				}
1527 			}
1528 		}
1529 	}
1530 }
1531 
1532 
1533 void
1534 ShutdownProcess::_QuitBackgroundApps()
1535 {
1536 	PRINT(("ShutdownProcess::_QuitBackgroundApps()\n"));
1537 
1538 	_SetShutdownWindowText(
1539 		B_TRANSLATE("Asking background applications to quit."));
1540 
1541 	// prepare the shutdown message
1542 	BMessage message;
1543 	_PrepareShutdownMessage(message);
1544 
1545 	// send shutdown messages to user apps
1546 	BAutolock _(fWorkerLock);
1547 
1548 	AppInfoListMessagingTargetSet targetSet(fBackgroundApps);
1549 
1550 	if (targetSet.HasNext()) {
1551 		PRINT(("  sending shutdown message to %ld apps\n",
1552 			fBackgroundApps.CountInfos()));
1553 
1554 		status_t error = MessageDeliverer::Default()->DeliverMessage(
1555 			&message, targetSet);
1556 		if (error != B_OK) {
1557 			WARNING(("_QuitBackgroundApps::_Worker(): Failed to deliver "
1558 				"shutdown message to all applications: %s\n",
1559 				strerror(error)));
1560 		}
1561 	}
1562 
1563 	PRINT(("ShutdownProcess::_QuitBackgroundApps() done\n"));
1564 }
1565 
1566 
1567 void
1568 ShutdownProcess::_WaitForBackgroundApps()
1569 {
1570 	PRINT(("ShutdownProcess::_WaitForBackgroundApps()\n"));
1571 
1572 	// wait for user apps
1573 	bool moreApps = true;
1574 	while (moreApps) {
1575 		{
1576 			BAutolock _(fWorkerLock);
1577 			moreApps = !fBackgroundApps.IsEmpty();
1578 		}
1579 
1580 		if (moreApps) {
1581 			uint32 event;
1582 			team_id team;
1583 			int32 phase;
1584 			status_t error = _GetNextEvent(event, team, phase, true);
1585 			if (error != B_OK)
1586 				throw_error(error);
1587 
1588 			if (event == ABORT_EVENT) {
1589 				// ignore: it's too late to abort the shutdown
1590 			}
1591 
1592 			if (event == TIMEOUT_EVENT)
1593 				return;
1594 		}
1595 	}
1596 
1597 	PRINT(("ShutdownProcess::_WaitForBackgroundApps() done\n"));
1598 }
1599 
1600 
1601 void
1602 ShutdownProcess::_KillBackgroundApps()
1603 {
1604 	PRINT(("ShutdownProcess::_KillBackgroundApps()\n"));
1605 
1606 	while (true) {
1607 		// eat events (we need to be responsive for an abort event)
1608 		uint32 event;
1609 		do {
1610 			team_id team;
1611 			int32 phase;
1612 			status_t error = _GetNextEvent(event, team, phase, false);
1613 			if (error != B_OK)
1614 				throw_error(error);
1615 
1616 		} while (event != NO_EVENT);
1617 
1618 		// get the first team to kill
1619 		team_id team = -1;
1620 		char appName[B_FILE_NAME_LENGTH];
1621 		AppInfoList& list = fBackgroundApps;
1622 		{
1623 			BAutolock _(fWorkerLock);
1624 
1625 			if (!list.IsEmpty()) {
1626 				RosterAppInfo* info = *list.It();
1627 				team = info->team;
1628 				strcpy(appName, info->ref.name);
1629 			}
1630 		}
1631 
1632 
1633 		if (team < 0) {
1634 			PRINT(("ShutdownProcess::_KillBackgroundApps() done\n"));
1635 			return;
1636 		}
1637 
1638 		// the app is either blocking on a model alert or blocks for another
1639 		// reason
1640 		_QuitBlockingApp(list, team, appName, false);
1641 	}
1642 }
1643 
1644 
1645 void
1646 ShutdownProcess::_QuitNonApps()
1647 {
1648 	PRINT(("ShutdownProcess::_QuitNonApps()\n"));
1649 
1650 	_SetShutdownWindowText(B_TRANSLATE("Asking other processes to quit."));
1651 
1652 	// iterate through the remaining teams and send them the TERM signal
1653 	int32 cookie = 0;
1654 	team_info teamInfo;
1655 	while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
1656 		if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) {
1657 			PRINT(("  sending team %ld TERM signal\n", teamInfo.team));
1658 
1659 			#ifdef __HAIKU__
1660 				// Note: team ID == team main thread ID under Haiku
1661 				send_signal(teamInfo.team, SIGTERM);
1662 			#else
1663 				// We don't want to do this when testing under R5, since it
1664 				// would kill all teams besides our app server and registrar.
1665 			#endif
1666 		}
1667 	}
1668 
1669 	// give them a bit of time to terminate
1670 	// TODO: Instead of just waiting we could periodically check whether the
1671 	// processes are already gone to shorten the process.
1672 	snooze(kNonAppQuitTimeout);
1673 
1674 	// iterate through the remaining teams and kill them
1675 	cookie = 0;
1676 	while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
1677 		if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) {
1678 			PRINT(("  killing team %ld\n", teamInfo.team));
1679 
1680 			#ifdef __HAIKU__
1681 				kill_team(teamInfo.team);
1682 			#else
1683 				// We don't want to do this when testing under R5, since it
1684 				// would kill all teams besides our app server and registrar.
1685 			#endif
1686 		}
1687 	}
1688 
1689 	PRINT(("ShutdownProcess::_QuitNonApps() done\n"));
1690 }
1691 
1692 
1693 void
1694 ShutdownProcess::_QuitBlockingApp(AppInfoList& list, team_id team,
1695 	const char* appName, bool cancelAllowed)
1696 {
1697 	bool debugged = false;
1698 	bool modal = false;
1699 	{
1700 		BAutolock _(fWorkerLock);
1701 		if (fDebuggedTeams.find(team) != fDebuggedTeams.end())
1702 			debugged = true;
1703 	}
1704 	if (!debugged)
1705 		modal = BPrivate::is_app_showing_modal_window(team);
1706 
1707 	if (modal) {
1708 		// app blocks on a modal window
1709 		BString buffer = B_TRANSLATE("The application \"%appName%\" might be "
1710 			"blocked on a modal panel.");
1711 		buffer.ReplaceFirst("%appName%", appName);
1712 		_SetShutdownWindowText(buffer.String());
1713 		_SetShutdownWindowCurrentApp(team);
1714 		_SetShutdownWindowKillButtonEnabled(true);
1715 	}
1716 
1717 	if (modal || debugged) {
1718 		// wait for something to happen
1719 		bool appGone = false;
1720 		while (true) {
1721 			uint32 event;
1722 			team_id eventTeam;
1723 			int32 phase;
1724 			status_t error = _GetNextEvent(event, eventTeam, phase, true);
1725 			if (error != B_OK)
1726 				throw_error(error);
1727 
1728 			if ((event == APP_QUIT_EVENT) && eventTeam == team) {
1729 				appGone = true;
1730 				break;
1731 			}
1732 
1733 			if (event == KILL_APP_EVENT && eventTeam == team)
1734 				break;
1735 
1736 			if (event == ABORT_EVENT) {
1737 				if (cancelAllowed || debugged) {
1738 					PRINT(("ShutdownProcess::_QuitBlockingApp(): shutdown "
1739 						"cancelled by team %ld (-1 => user)\n", eventTeam));
1740 
1741 					if (!debugged)
1742 						_DisplayAbortingApp(eventTeam);
1743 					throw_error(B_SHUTDOWN_CANCELLED);
1744 				}
1745 
1746 				// If the app requests aborting the shutdown, we don't need
1747 				// to wait any longer. It has processed the request and
1748 				// won't quit by itself. We'll have to kill it.
1749 				if (eventTeam == team)
1750 					break;
1751 			}
1752 		}
1753 
1754 		_SetShutdownWindowKillButtonEnabled(false);
1755 
1756 		if (appGone)
1757 			return;
1758 	}
1759 
1760 	// kill the app
1761 	PRINT(("  killing team %ld\n", team));
1762 
1763 	kill_team(team);
1764 
1765 	// remove the app (the roster will note eventually and send us
1766 	// a notification, but we want to be sure)
1767 	{
1768 		BAutolock _(fWorkerLock);
1769 
1770 		if (RosterAppInfo* info = list.InfoFor(team)) {
1771 			list.RemoveInfo(info);
1772 			delete info;
1773 		}
1774 	}
1775 }
1776 
1777 
1778 void
1779 ShutdownProcess::_DisplayAbortingApp(team_id team)
1780 {
1781 	// find the app that cancelled the shutdown
1782 	char appName[B_FILE_NAME_LENGTH];
1783 	bool foundApp = false;
1784 	{
1785 		BAutolock _(fWorkerLock);
1786 
1787 		RosterAppInfo* info = fUserApps.InfoFor(team);
1788 		if (!info)
1789 			info = fSystemApps.InfoFor(team);
1790 		if (!info)
1791 			fBackgroundApps.InfoFor(team);
1792 
1793 		if (info) {
1794 			foundApp = true;
1795 			strcpy(appName, info->ref.name);
1796 		}
1797 	}
1798 
1799 	if (!foundApp) {
1800 		PRINT(("ShutdownProcess::_DisplayAbortingApp(): Didn't find the app "
1801 			"that has cancelled the shutdown.\n"));
1802 		return;
1803 	}
1804 
1805 	// compose the text to be displayed
1806 	BString buffer = B_TRANSLATE("Application \"%appName%\" has aborted the "
1807 		"shutdown process.");
1808 	buffer.ReplaceFirst("%appName%", appName);
1809 
1810 	// set up the window
1811 	_SetShutdownWindowCurrentApp(team);
1812 	_SetShutdownWindowText(buffer.String());
1813 	_SetShutdownWindowWaitForAbortedOK();
1814 
1815 	// schedule the timeout event
1816 	_SetPhase(ABORTED_PHASE);
1817 	_ScheduleTimeoutEvent(kDisplayAbortingAppTimeout);
1818 
1819 	// wait for the timeout or the user to press the cancel button
1820 	while (true) {
1821 		uint32 event;
1822 		team_id eventTeam;
1823 		int32 phase;
1824 		status_t error = _GetNextEvent(event, eventTeam, phase, true);
1825 		if (error != B_OK)
1826 			break;
1827 
1828 		// stop waiting when the timeout occurs
1829 		if (event == TIMEOUT_EVENT)
1830 			break;
1831 
1832 		// stop waiting when the user hit the cancel button
1833 		if (event == ABORT_EVENT && phase == ABORTED_PHASE && eventTeam < 0)
1834 			break;
1835 	}
1836 }
1837 
1838 
1839 /*!	Waits until the debugged team list is empty, ie. when there is no one
1840 	left to debug.
1841 */
1842 void
1843 ShutdownProcess::_WaitForDebuggedTeams()
1844 {
1845 	PRINT(("ShutdownProcess::_WaitForDebuggedTeams()\n"));
1846 	{
1847 		BAutolock _(fWorkerLock);
1848 		if (fDebuggedTeams.empty())
1849 			return;
1850 	}
1851 
1852 	PRINT(("  not empty!\n"));
1853 
1854 	// wait for something to happen
1855 	while (true) {
1856 		uint32 event;
1857 		team_id eventTeam;
1858 		int32 phase;
1859 		status_t error = _GetNextEvent(event, eventTeam, phase, true);
1860 		if (error != B_OK)
1861 			throw_error(error);
1862 
1863 		if (event == ABORT_EVENT)
1864 			throw_error(B_SHUTDOWN_CANCELLED);
1865 
1866 		BAutolock _(fWorkerLock);
1867 		if (fDebuggedTeams.empty()) {
1868 			PRINT(("  out empty"));
1869 			return;
1870 		}
1871 	}
1872 }
1873 
1874