xref: /haiku/src/servers/registrar/ShutdownProcess.cpp (revision 204dee708a999d5a71d0cb9497650ee7cef85d0a)
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: %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 		// We want the alert to behave more like a regular window...
1260 		alert->SetFeel(B_NORMAL_WINDOW_FEEL);
1261 		// ...but not quit. Minimizing the alert would prevent the user from
1262 		// finding it again, since registrar does not have an entry in the
1263 		// Deskbar.
1264 		alert->SetFlags(alert->Flags() | B_NOT_MINIMIZABLE | B_CLOSE_ON_ESCAPE);
1265 		alert->SetWorkspaces(B_ALL_WORKSPACES);
1266 		int32 result = alert->Go();
1267 
1268 		if (result == 1) {
1269 			// Toggle shutdown method
1270 			fReboot = !fReboot;
1271 		} else if (result < 1)
1272 			throw_error(B_SHUTDOWN_CANCELLED);
1273 	}
1274 
1275 	// tell TRoster not to accept new applications anymore
1276 	fRoster->SetShuttingDown(true);
1277 
1278 	fWorkerLock.Lock();
1279 
1280 	// get a list of all applications to shut down and sort them
1281 	status_t status = fRoster->GetShutdownApps(fUserApps, fSystemApps,
1282 		fBackgroundApps, fVitalSystemApps);
1283 	if (status  != B_OK) {
1284 		fWorkerLock.Unlock();
1285 		fRoster->RemoveWatcher(this);
1286 		fRoster->SetShuttingDown(false);
1287 		return;
1288 	}
1289 
1290 	fUserApps.Sort(&inverse_compare_by_registration_time);
1291 	fSystemApps.Sort(&inverse_compare_by_registration_time);
1292 
1293 	fWorkerLock.Unlock();
1294 
1295 	// make the shutdown window ready and show it
1296 	_InitShutdownWindow();
1297 	_SetShutdownWindowCurrentApp(-1);
1298 	_SetShutdownWindowText(B_TRANSLATE("Tidying things up a bit."));
1299 	_SetShutdownWindowCancelButtonEnabled(true);
1300 	_SetShutdownWindowKillButtonEnabled(false);
1301 	_SetShowShutdownWindow(true);
1302 
1303 	// sync
1304 	sync();
1305 
1306 	// phase 1: terminate the user apps
1307 	_SetPhase(USER_APP_TERMINATION_PHASE);
1308 	_QuitApps(fUserApps, false);
1309 	_WaitForDebuggedTeams();
1310 
1311 	// phase 2: terminate the system apps
1312 	_SetPhase(SYSTEM_APP_TERMINATION_PHASE);
1313 	_QuitApps(fSystemApps, true);
1314 	_WaitForDebuggedTeams();
1315 
1316 	// phase 3: terminate the background apps
1317 	_SetPhase(BACKGROUND_APP_TERMINATION_PHASE);
1318 	_QuitBackgroundApps();
1319 	_WaitForDebuggedTeams();
1320 
1321 	// phase 4: terminate the other processes
1322 	_SetPhase(OTHER_PROCESSES_TERMINATION_PHASE);
1323 	_QuitNonApps();
1324 	_ScheduleTimeoutEvent(kBackgroundAppQuitTimeout, -1);
1325 	_WaitForBackgroundApps();
1326 	_KillBackgroundApps();
1327 	_WaitForDebuggedTeams();
1328 
1329 	// we're through: do the shutdown
1330 	_SetPhase(DONE_PHASE);
1331 	if (fReboot)
1332 		_SetShutdownWindowText(B_TRANSLATE("Restarting" B_UTF8_ELLIPSIS));
1333 	else
1334 		_SetShutdownWindowText(B_TRANSLATE("Shutting down" B_UTF8_ELLIPSIS));
1335 	_ShutDown();
1336 	_SetShutdownWindowWaitForShutdown();
1337 
1338 	PRINT("  _kern_shutdown() failed\n");
1339 
1340 	// shutdown failed: This can happen for power off mode -- reboot should
1341 	// always work.
1342 	if (fHasGUI) {
1343 		// wait for the reboot event
1344 		uint32 event;
1345 		do {
1346 			team_id team;
1347 			int32 phase;
1348 			status = _GetNextEvent(event, team, phase, true);
1349 			if (status != B_OK)
1350 				break;
1351 		} while (event != REBOOT_SYSTEM_EVENT);
1352 
1353 		_kern_shutdown(true);
1354 	}
1355 
1356 	// either there's no GUI or reboot failed: we enter the kernel debugger
1357 	// instead
1358 #ifdef __HAIKU__
1359 // TODO: Introduce the syscall.
1360 //	while (true) {
1361 //		_kern_kernel_debugger("The system is shut down. It's now safe to turn "
1362 //			"off the computer.");
1363 //	}
1364 #endif
1365 }
1366 
1367 
1368 bool
1369 ShutdownProcess::_WaitForApp(team_id team, AppInfoList* list, bool systemApps)
1370 {
1371 	uint32 event;
1372 	do {
1373 		team_id eventTeam;
1374 		int32 phase;
1375 		status_t error = _GetNextEvent(event, eventTeam, phase, true);
1376 		if (error != B_OK)
1377 			throw_error(error);
1378 
1379 		if (event == APP_QUIT_EVENT && eventTeam == team)
1380 			return true;
1381 
1382 		if (event == TIMEOUT_EVENT && eventTeam == team)
1383 			return false;
1384 
1385 		if (event == ABORT_EVENT) {
1386 			if (eventTeam == -1) {
1387 				// The user canceled the shutdown process by pressing the
1388 				// Cancel button.
1389 				throw_error(B_SHUTDOWN_CANCELLED);
1390 			}
1391 			if (systemApps) {
1392 				// If the app requests aborting the shutdown, we don't need
1393 				// to wait any longer. It has processed the request and
1394 				// won't quit by itself. We ignore this for system apps.
1395 				if (eventTeam == team)
1396 					return false;
1397 			} else {
1398 				// The app returned false in QuitRequested().
1399 				PRINT("ShutdownProcess::_WaitForApp(): shutdown cancelled "
1400 					"by team %ld (-1 => user)\n", eventTeam);
1401 
1402 				_DisplayAbortingApp(team);
1403 				throw_error(B_SHUTDOWN_CANCELLED);
1404 			}
1405 		}
1406 
1407 		BAutolock _(fWorkerLock);
1408 		if (list != NULL && !list->InfoFor(team))
1409 			return true;
1410 	} while (event != NO_EVENT);
1411 
1412 	return false;
1413 }
1414 
1415 
1416 void
1417 ShutdownProcess::_QuitApps(AppInfoList& list, bool systemApps)
1418 {
1419 	PRINT("ShutdownProcess::_QuitApps(%s)\n",
1420 		(systemApps ? "system" : "user"));
1421 
1422 	if (systemApps) {
1423 		_SetShutdownWindowCancelButtonEnabled(false);
1424 
1425 		// check one last time for abort events
1426 		uint32 event;
1427 		do {
1428 			team_id team;
1429 			int32 phase;
1430 			status_t error = _GetNextEvent(event, team, phase, false);
1431 			if (error != B_OK)
1432 				throw_error(error);
1433 
1434 			if (event == ABORT_EVENT) {
1435 				PRINT("ShutdownProcess::_QuitApps(): shutdown cancelled by "
1436 					"team %ld (-1 => user)\n", team);
1437 
1438 				_DisplayAbortingApp(team);
1439 				throw_error(B_SHUTDOWN_CANCELLED);
1440 			}
1441 
1442 		} while (event != NO_EVENT);
1443 	}
1444 
1445 	// prepare the shutdown message
1446 	BMessage message;
1447 	_PrepareShutdownMessage(message);
1448 
1449 	// now iterate through the list of apps
1450 	while (true) {
1451 		// eat events
1452 		uint32 event;
1453 		do {
1454 			team_id team;
1455 			int32 phase;
1456 			status_t error = _GetNextEvent(event, team, phase, false);
1457 			if (error != B_OK)
1458 				throw_error(error);
1459 
1460 			if (!systemApps && event == ABORT_EVENT) {
1461 				PRINT("ShutdownProcess::_QuitApps(): shutdown cancelled by "
1462 					"team %ld (-1 => user)\n", team);
1463 
1464 				_DisplayAbortingApp(team);
1465 				throw_error(B_SHUTDOWN_CANCELLED);
1466 			}
1467 
1468 		} while (event != NO_EVENT);
1469 
1470 		// get the first app to quit
1471 		team_id team = -1;
1472 		port_id port = -1;
1473 		char appName[B_FILE_NAME_LENGTH];
1474 		{
1475 			BAutolock _(fWorkerLock);
1476 			while (!list.IsEmpty()) {
1477 				RosterAppInfo* info = *list.It();
1478 				team = info->team;
1479 				port = info->port;
1480 				strcpy(appName, info->ref.name);
1481 
1482 				if (info->IsRunning())
1483 					break;
1484 				list.RemoveInfo(info);
1485 				delete info;
1486 			}
1487 		}
1488 
1489 		if (team < 0) {
1490 			PRINT("ShutdownProcess::_QuitApps() done\n");
1491 			return;
1492 		}
1493 
1494 		// set window text
1495 		BString buffer = B_TRANSLATE("Asking \"%appName%\" to quit.");
1496 		buffer.ReplaceFirst("%appName%", appName);
1497 		_SetShutdownWindowText(buffer.String());
1498 		_SetShutdownWindowCurrentApp(team);
1499 
1500 		// send the shutdown message to the app
1501 		PRINT("  sending team %ld (port: %ld) a shutdown message\n", team,
1502 			port);
1503 		SingleMessagingTargetSet target(port, B_PREFERRED_TOKEN);
1504 		MessageDeliverer::Default()->DeliverMessage(&message, target);
1505 
1506 		// schedule a timeout event
1507 		_ScheduleTimeoutEvent(kAppQuitTimeout, team);
1508 
1509 		// wait for the app to die or for the timeout to occur
1510 		bool appGone = _WaitForApp(team, &list, systemApps);
1511 		if (appGone) {
1512 			// fine: the app finished in an orderly manner
1513 		} else {
1514 			// the app is either blocking on a model alert or blocks for another
1515 			// reason
1516 			if (!systemApps)
1517 				_QuitBlockingApp(list, team, appName, true);
1518 			else {
1519 				// This is a system app: remove it from the list
1520 				BAutolock _(fWorkerLock);
1521 
1522 				if (RosterAppInfo* info = list.InfoFor(team)) {
1523 					list.RemoveInfo(info);
1524 					delete info;
1525 				}
1526 			}
1527 		}
1528 	}
1529 }
1530 
1531 
1532 void
1533 ShutdownProcess::_QuitBackgroundApps()
1534 {
1535 	PRINT("ShutdownProcess::_QuitBackgroundApps()\n");
1536 
1537 	_SetShutdownWindowText(
1538 		B_TRANSLATE("Asking background applications to quit."));
1539 
1540 	// prepare the shutdown message
1541 	BMessage message;
1542 	_PrepareShutdownMessage(message);
1543 
1544 	// send shutdown messages to user apps
1545 	BAutolock _(fWorkerLock);
1546 
1547 	AppInfoListMessagingTargetSet targetSet(fBackgroundApps);
1548 
1549 	if (targetSet.HasNext()) {
1550 		PRINT("  sending shutdown message to %ld apps\n",
1551 			fBackgroundApps.CountInfos());
1552 
1553 		status_t error = MessageDeliverer::Default()->DeliverMessage(
1554 			&message, targetSet);
1555 		if (error != B_OK) {
1556 			WARNING("_QuitBackgroundApps::_Worker(): Failed to deliver "
1557 				"shutdown message to all applications: %s\n",
1558 				strerror(error));
1559 		}
1560 	}
1561 
1562 	PRINT("ShutdownProcess::_QuitBackgroundApps() done\n");
1563 }
1564 
1565 
1566 void
1567 ShutdownProcess::_WaitForBackgroundApps()
1568 {
1569 	PRINT("ShutdownProcess::_WaitForBackgroundApps()\n");
1570 
1571 	// wait for user apps
1572 	bool moreApps = true;
1573 	while (moreApps) {
1574 		{
1575 			BAutolock _(fWorkerLock);
1576 			moreApps = !fBackgroundApps.IsEmpty();
1577 		}
1578 
1579 		if (moreApps) {
1580 			uint32 event;
1581 			team_id team;
1582 			int32 phase;
1583 			status_t error = _GetNextEvent(event, team, phase, true);
1584 			if (error != B_OK)
1585 				throw_error(error);
1586 
1587 			if (event == ABORT_EVENT) {
1588 				// ignore: it's too late to abort the shutdown
1589 			}
1590 
1591 			if (event == TIMEOUT_EVENT)
1592 				return;
1593 		}
1594 	}
1595 
1596 	PRINT("ShutdownProcess::_WaitForBackgroundApps() done\n");
1597 }
1598 
1599 
1600 void
1601 ShutdownProcess::_KillBackgroundApps()
1602 {
1603 	PRINT("ShutdownProcess::_KillBackgroundApps()\n");
1604 
1605 	while (true) {
1606 		// eat events (we need to be responsive for an abort event)
1607 		uint32 event;
1608 		do {
1609 			team_id team;
1610 			int32 phase;
1611 			status_t error = _GetNextEvent(event, team, phase, false);
1612 			if (error != B_OK)
1613 				throw_error(error);
1614 
1615 		} while (event != NO_EVENT);
1616 
1617 		// get the first team to kill
1618 		team_id team = -1;
1619 		char appName[B_FILE_NAME_LENGTH];
1620 		AppInfoList& list = fBackgroundApps;
1621 		{
1622 			BAutolock _(fWorkerLock);
1623 
1624 			if (!list.IsEmpty()) {
1625 				RosterAppInfo* info = *list.It();
1626 				team = info->team;
1627 				strcpy(appName, info->ref.name);
1628 			}
1629 		}
1630 
1631 
1632 		if (team < 0) {
1633 			PRINT("ShutdownProcess::_KillBackgroundApps() done\n");
1634 			return;
1635 		}
1636 
1637 		// the app is either blocking on a model alert or blocks for another
1638 		// reason
1639 		_QuitBlockingApp(list, team, appName, false);
1640 	}
1641 }
1642 
1643 
1644 void
1645 ShutdownProcess::_QuitNonApps()
1646 {
1647 	PRINT("ShutdownProcess::_QuitNonApps()\n");
1648 
1649 	_SetShutdownWindowText(B_TRANSLATE("Asking other processes to quit."));
1650 
1651 	// iterate through the remaining teams and send them the TERM signal
1652 	int32 cookie = 0;
1653 	team_info teamInfo;
1654 	while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
1655 		if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) {
1656 			PRINT("  sending team %ld TERM signal\n", teamInfo.team);
1657 
1658 			#ifdef __HAIKU__
1659 				// Note: team ID == team main thread ID under Haiku
1660 				send_signal(teamInfo.team, SIGTERM);
1661 			#else
1662 				// We don't want to do this when testing under R5, since it
1663 				// would kill all teams besides our app server and registrar.
1664 			#endif
1665 		}
1666 	}
1667 
1668 	// give them a bit of time to terminate
1669 	// TODO: Instead of just waiting we could periodically check whether the
1670 	// processes are already gone to shorten the process.
1671 	snooze(kNonAppQuitTimeout);
1672 
1673 	// iterate through the remaining teams and kill them
1674 	cookie = 0;
1675 	while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
1676 		if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) {
1677 			PRINT("  killing team %ld\n", teamInfo.team);
1678 
1679 			#ifdef __HAIKU__
1680 				kill_team(teamInfo.team);
1681 			#else
1682 				// We don't want to do this when testing under R5, since it
1683 				// would kill all teams besides our app server and registrar.
1684 			#endif
1685 		}
1686 	}
1687 
1688 	PRINT("ShutdownProcess::_QuitNonApps() done\n");
1689 }
1690 
1691 
1692 void
1693 ShutdownProcess::_QuitBlockingApp(AppInfoList& list, team_id team,
1694 	const char* appName, bool cancelAllowed)
1695 {
1696 	bool debugged = false;
1697 	bool modal = false;
1698 	{
1699 		BAutolock _(fWorkerLock);
1700 		if (fDebuggedTeams.find(team) != fDebuggedTeams.end())
1701 			debugged = true;
1702 	}
1703 	if (!debugged)
1704 		modal = BPrivate::is_app_showing_modal_window(team);
1705 
1706 	if (modal) {
1707 		// app blocks on a modal window
1708 		BString buffer = B_TRANSLATE("The application \"%appName%\" might be "
1709 			"blocked on a modal panel.");
1710 		buffer.ReplaceFirst("%appName%", appName);
1711 		_SetShutdownWindowText(buffer.String());
1712 		_SetShutdownWindowCurrentApp(team);
1713 		_SetShutdownWindowKillButtonEnabled(true);
1714 	}
1715 
1716 	if (modal || debugged) {
1717 		// wait for something to happen
1718 		bool appGone = false;
1719 		while (true) {
1720 			uint32 event;
1721 			team_id eventTeam;
1722 			int32 phase;
1723 			status_t error = _GetNextEvent(event, eventTeam, phase, true);
1724 			if (error != B_OK)
1725 				throw_error(error);
1726 
1727 			if ((event == APP_QUIT_EVENT) && eventTeam == team) {
1728 				appGone = true;
1729 				break;
1730 			}
1731 
1732 			if (event == KILL_APP_EVENT && eventTeam == team)
1733 				break;
1734 
1735 			if (event == ABORT_EVENT) {
1736 				if (cancelAllowed || debugged) {
1737 					PRINT("ShutdownProcess::_QuitBlockingApp(): shutdown "
1738 						"cancelled by team %ld (-1 => user)\n", eventTeam);
1739 
1740 					if (!debugged)
1741 						_DisplayAbortingApp(eventTeam);
1742 					throw_error(B_SHUTDOWN_CANCELLED);
1743 				}
1744 
1745 				// If the app requests aborting the shutdown, we don't need
1746 				// to wait any longer. It has processed the request and
1747 				// won't quit by itself. We'll have to kill it.
1748 				if (eventTeam == team)
1749 					break;
1750 			}
1751 		}
1752 
1753 		_SetShutdownWindowKillButtonEnabled(false);
1754 
1755 		if (appGone)
1756 			return;
1757 	}
1758 
1759 	// kill the app
1760 	PRINT("  killing team %ld\n", team);
1761 
1762 	kill_team(team);
1763 
1764 	// remove the app (the roster will note eventually and send us
1765 	// a notification, but we want to be sure)
1766 	{
1767 		BAutolock _(fWorkerLock);
1768 
1769 		if (RosterAppInfo* info = list.InfoFor(team)) {
1770 			list.RemoveInfo(info);
1771 			delete info;
1772 		}
1773 	}
1774 }
1775 
1776 
1777 void
1778 ShutdownProcess::_DisplayAbortingApp(team_id team)
1779 {
1780 	// find the app that cancelled the shutdown
1781 	char appName[B_FILE_NAME_LENGTH];
1782 	bool foundApp = false;
1783 	{
1784 		BAutolock _(fWorkerLock);
1785 
1786 		RosterAppInfo* info = fUserApps.InfoFor(team);
1787 		if (!info)
1788 			info = fSystemApps.InfoFor(team);
1789 		if (!info)
1790 			fBackgroundApps.InfoFor(team);
1791 
1792 		if (info) {
1793 			foundApp = true;
1794 			strcpy(appName, info->ref.name);
1795 		}
1796 	}
1797 
1798 	if (!foundApp) {
1799 		PRINT("ShutdownProcess::_DisplayAbortingApp(): Didn't find the app "
1800 			"that has cancelled the shutdown.\n");
1801 		return;
1802 	}
1803 
1804 	// compose the text to be displayed
1805 	BString buffer = B_TRANSLATE("Application \"%appName%\" has aborted the "
1806 		"shutdown process.");
1807 	buffer.ReplaceFirst("%appName%", appName);
1808 
1809 	// set up the window
1810 	_SetShutdownWindowCurrentApp(team);
1811 	_SetShutdownWindowText(buffer.String());
1812 	_SetShutdownWindowWaitForAbortedOK();
1813 
1814 	// schedule the timeout event
1815 	_SetPhase(ABORTED_PHASE);
1816 	_ScheduleTimeoutEvent(kDisplayAbortingAppTimeout);
1817 
1818 	// wait for the timeout or the user to press the cancel button
1819 	while (true) {
1820 		uint32 event;
1821 		team_id eventTeam;
1822 		int32 phase;
1823 		status_t error = _GetNextEvent(event, eventTeam, phase, true);
1824 		if (error != B_OK)
1825 			break;
1826 
1827 		// stop waiting when the timeout occurs
1828 		if (event == TIMEOUT_EVENT)
1829 			break;
1830 
1831 		// stop waiting when the user hit the cancel button
1832 		if (event == ABORT_EVENT && phase == ABORTED_PHASE && eventTeam < 0)
1833 			break;
1834 	}
1835 }
1836 
1837 
1838 /*!	Waits until the debugged team list is empty, ie. when there is no one
1839 	left to debug.
1840 */
1841 void
1842 ShutdownProcess::_WaitForDebuggedTeams()
1843 {
1844 	PRINT("ShutdownProcess::_WaitForDebuggedTeams()\n");
1845 	{
1846 		BAutolock _(fWorkerLock);
1847 		if (fDebuggedTeams.empty())
1848 			return;
1849 	}
1850 
1851 	PRINT("  not empty!\n");
1852 
1853 	// wait for something to happen
1854 	while (true) {
1855 		uint32 event;
1856 		team_id eventTeam;
1857 		int32 phase;
1858 		status_t error = _GetNextEvent(event, eventTeam, phase, true);
1859 		if (error != B_OK)
1860 			throw_error(error);
1861 
1862 		if (event == ABORT_EVENT)
1863 			throw_error(B_SHUTDOWN_CANCELLED);
1864 
1865 		BAutolock _(fWorkerLock);
1866 		if (fDebuggedTeams.empty()) {
1867 			PRINT("  out empty");
1868 			return;
1869 		}
1870 	}
1871 }
1872 
1873