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