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