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