xref: /haiku/src/servers/registrar/ShutdownProcess.cpp (revision 9ecf9d1c1d4888d341a6eac72112c72d1ae3a4cb)
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 		// move the window to the same position as BAlerts
407 		BScreen screen(this);
408 	 	BRect screenFrame = screen.Frame();
409 
410 		MoveTo(screenFrame.left + (screenFrame.Width() - width) / 2.0,
411 			screenFrame.top + screenFrame.Height() / 4.0 - ceilf(height / 3.0));
412 
413 		return B_OK;
414 	}
415 
416 	status_t AddApp(team_id team, BBitmap *miniIcon, BBitmap *largeIcon)
417 	{
418 		AppInfo *info = new(nothrow) AppInfo;
419 		if (!info) {
420 			delete miniIcon;
421 			delete largeIcon;
422 			return B_NO_MEMORY;
423 		}
424 
425 		info->team = team;
426 		info->miniIcon = miniIcon;
427 		info->largeIcon = largeIcon;
428 
429 		if (!fAppInfos.AddItem(info)) {
430 			delete info;
431 			return B_NO_MEMORY;
432 		}
433 
434 		return B_OK;
435 	}
436 
437 	void RemoveApp(team_id team)
438 	{
439 		int32 index = _AppInfoIndexOf(team);
440 		if (index < 0)
441 			return;
442 
443 		if (team == fCurrentApp)
444 			SetCurrentApp(-1);
445 
446 		AppInfo *info = (AppInfo*)fAppInfos.RemoveItem(index);
447 		delete info;
448 	}
449 
450 	void SetCurrentApp(team_id team)
451 	{
452 		AppInfo *info = (team >= 0 ? _AppInfoFor(team) : NULL);
453 
454 		fCurrentApp = team;
455 		fCurrentAppIconView->SetAppInfo(info);
456 
457 		fKillAppMessage->ReplaceInt32("team", team);
458 	}
459 
460 	void SetText(const char *text)
461 	{
462 		fTextView->SetText(text);
463 	}
464 
465 	void SetCancelShutdownButtonEnabled(bool enable)
466 	{
467 		fCancelShutdownButton->SetEnabled(enable);
468 	}
469 
470 	void SetKillAppButtonEnabled(bool enable)
471 	{
472 		if (enable != fKillAppButton->IsEnabled()) {
473 			fKillAppButton->SetEnabled(enable);
474 
475 			if (enable)
476 				fKillAppButton->Show();
477 			else
478 				fKillAppButton->Hide();
479 		}
480 	}
481 
482 	void SetWaitForShutdown()
483 	{
484 		fCurrentAppIconView->Hide();
485 		fKillAppButton->Hide();
486 		fCancelShutdownButton->Hide();
487 		fRebootSystemButton->MakeDefault(true);
488 		fRebootSystemButton->Show();
489 
490 		SetTitle("System is Shut Down");
491 		fTextView->SetText("It's now safe to turn off the computer.");
492 	}
493 
494 	void SetWaitForAbortedOK()
495 	{
496 		fCurrentAppIconView->Hide();
497 		fKillAppButton->Hide();
498 		fCancelShutdownButton->Hide();
499 		fAbortedOKButton->MakeDefault(true);
500 		fAbortedOKButton->Show();
501 		// TODO: Temporary work-around for a Haiku bug.
502 		fAbortedOKButton->Invalidate();
503 
504 		SetTitle("Shutdown Aborted");
505 	}
506 
507 private:
508 	struct AppInfo {
509 		team_id		team;
510 		BBitmap		*miniIcon;
511 		BBitmap		*largeIcon;
512 
513 		~AppInfo()
514 		{
515 			delete miniIcon;
516 			delete largeIcon;
517 		}
518 	};
519 
520 	int32 _AppInfoIndexOf(team_id team)
521 	{
522 		if (team < 0)
523 			return -1;
524 
525 		for (int32 i = 0; AppInfo *info = (AppInfo*)fAppInfos.ItemAt(i); i++) {
526 			if (info->team == team)
527 				return i;
528 		}
529 
530 		return -1;
531 	}
532 
533 	AppInfo *_AppInfoFor(team_id team)
534 	{
535 		int32 index = _AppInfoIndexOf(team);
536 		return (index >= 0 ? (AppInfo*)fAppInfos.ItemAt(index) : NULL);
537 	}
538 
539 	class CurrentAppIconView : public BView {
540 	  public:
541 		CurrentAppIconView()
542 			: BView(BRect(0, 0, 31,  31), "current app icon", B_FOLLOW_NONE,
543 				B_WILL_DRAW),
544 			  fAppInfo(NULL)
545 		{
546 			SetViewColor(B_TRANSPARENT_32_BIT);
547 			fBackground = ui_color(B_PANEL_BACKGROUND_COLOR);
548 		}
549 
550 		virtual void Draw(BRect updateRect)
551 		{
552 			SetDrawingMode(B_OP_COPY);
553 			SetLowColor(fBackground);
554 			FillRect(Bounds(), B_SOLID_LOW);
555 
556 			if (fAppInfo && fAppInfo->largeIcon) {
557 				SetDrawingMode(B_OP_OVER);
558 				DrawBitmap(fAppInfo->largeIcon, BPoint(0, 0));
559 			}
560 		}
561 
562 		void SetAppInfo(AppInfo *info)
563 		{
564 			fAppInfo = info;
565 			Invalidate();
566 		}
567 
568 	  private:
569 		const AppInfo	*fAppInfo;
570 		rgb_color		fBackground;
571 	};
572 
573 private:
574 	BList				fAppInfos;
575 	CurrentAppIconView	*fCurrentAppIconView;
576 	BTextView			*fTextView;
577 	BButton				*fKillAppButton;
578 	BButton				*fCancelShutdownButton;
579 	BButton				*fRebootSystemButton;
580 	BButton				*fAbortedOKButton;
581 	BMessage			*fKillAppMessage;
582 	team_id				fCurrentApp;
583 };
584 
585 
586 // #pragma mark -
587 
588 // constructor
589 ShutdownProcess::ShutdownProcess(TRoster *roster, EventQueue *eventQueue)
590 	: BLooper("shutdown process"),
591 	  EventMaskWatcher(BMessenger(this), B_REQUEST_QUIT),
592 	  fWorkerLock("worker lock"),
593 	  fRequest(NULL),
594 	  fRoster(roster),
595 	  fEventQueue(eventQueue),
596 	  fTimeoutEvent(NULL),
597 	  fInternalEvents(NULL),
598 	  fInternalEventSemaphore(-1),
599 	  fQuitRequestReplyHandler(NULL),
600 	  fWorker(-1),
601 	  fCurrentPhase(INVALID_PHASE),
602 	  fShutdownError(B_ERROR),
603 	  fHasGUI(false),
604 	  fReboot(false),
605 	  fRequestReplySent(false),
606 	  fWindow(NULL)
607 {
608 }
609 
610 // destructor
611 ShutdownProcess::~ShutdownProcess()
612 {
613 	// terminate the GUI
614 	if (fHasGUI && fWindow && fWindow->Lock())
615 		fWindow->Quit();
616 
617 	// remove and delete the quit request reply handler
618 	if (fQuitRequestReplyHandler) {
619 		BAutolock _(this);
620 		RemoveHandler(fQuitRequestReplyHandler);
621 		delete fQuitRequestReplyHandler;
622 	}
623 
624 	// remove and delete the timeout event
625 	if (fTimeoutEvent) {
626 		fEventQueue->RemoveEvent(fTimeoutEvent);
627 
628 		delete fTimeoutEvent;
629 	}
630 
631 	// remove the application quit watcher
632 	fRoster->RemoveWatcher(this);
633 
634 	// If an error occurred (e.g. the shutdown process was cancelled), the
635 	// roster should accept applications again.
636 	if (fShutdownError != B_OK)
637 		fRoster->SetShuttingDown(false);
638 
639 	// delete the internal event semaphore
640 	if (fInternalEventSemaphore >= 0)
641 		delete_sem(fInternalEventSemaphore);
642 
643 	// wait for the worker thread to terminate
644 	if (fWorker >= 0) {
645 		int32 result;
646 		wait_for_thread(fWorker, &result);
647 	}
648 
649 	// delete all internal events and the queue
650 	if (fInternalEvents) {
651 		while (InternalEvent *event = fInternalEvents->First()) {
652 			fInternalEvents->Remove(event);
653 			delete event;
654 		}
655 
656 		delete fInternalEvents;
657 	}
658 
659 	// send a reply to the request and delete it
660 	_SendReply(fShutdownError);
661 	delete fRequest;
662 }
663 
664 // Init
665 status_t
666 ShutdownProcess::Init(BMessage *request)
667 {
668 	PRINT(("ShutdownProcess::Init()\n"));
669 
670 	// create and add the quit request reply handler
671 	fQuitRequestReplyHandler = new(nothrow) QuitRequestReplyHandler(this);
672 	if (!fQuitRequestReplyHandler)
673 		RETURN_ERROR(B_NO_MEMORY);
674 	AddHandler(fQuitRequestReplyHandler);
675 
676 	// create the timeout event
677 	fTimeoutEvent = new(nothrow) TimeoutEvent(this);
678 	if (!fTimeoutEvent)
679 		RETURN_ERROR(B_NO_MEMORY);
680 
681 	// create the event list
682 	fInternalEvents = new(nothrow) InternalEventList;
683 	if (!fInternalEvents)
684 		RETURN_ERROR(B_NO_MEMORY);
685 
686 	// create the event sempahore
687 	fInternalEventSemaphore = create_sem(0, "shutdown events");
688 	if (fInternalEventSemaphore < 0)
689 		RETURN_ERROR(fInternalEventSemaphore);
690 
691 	// init the app server connection
692 	fHasGUI = (Registrar::App()->InitGUIContext() == B_OK);
693 
694 	// tell TRoster not to accept new applications anymore
695 	fRoster->SetShuttingDown(true);
696 
697 	// start watching application quits
698 	status_t error = fRoster->AddWatcher(this);
699 	if (error != B_OK) {
700 		fRoster->SetShuttingDown(false);
701 		RETURN_ERROR(error);
702 	}
703 
704 	// get a list of all applications to shut down and sort them
705 	error = fRoster->GetShutdownApps(fUserApps, fSystemApps, fBackgroundApps,
706 		fVitalSystemApps);
707 	if (error != B_OK) {
708 		fRoster->RemoveWatcher(this);
709 		fRoster->SetShuttingDown(false);
710 		RETURN_ERROR(error);
711 	}
712 
713 	fUserApps.Sort(&inverse_compare_by_registration_time);
714 	fSystemApps.Sort(&inverse_compare_by_registration_time);
715 
716 	// start the worker thread
717 	fWorker = spawn_thread(_WorkerEntry, "shutdown worker", B_NORMAL_PRIORITY,
718 		this);
719 	if (fWorker < 0) {
720 		fRoster->RemoveWatcher(this);
721 		fRoster->SetShuttingDown(false);
722 		RETURN_ERROR(fWorker);
723 	}
724 
725 	// everything went fine: now we own the request
726 	fRequest = request;
727 
728 	if (fRequest->FindBool("reboot", &fReboot) != B_OK)
729 		fReboot = false;
730 
731 	resume_thread(fWorker);
732 
733 	PRINT(("ShutdownProcess::Init() done\n"));
734 
735 	return B_OK;
736 }
737 
738 // MessageReceived
739 void
740 ShutdownProcess::MessageReceived(BMessage *message)
741 {
742 	switch (message->what) {
743 		case B_SOME_APP_QUIT:
744 		{
745 			// get the team
746 			team_id team;
747 			if (message->FindInt32("be:team", &team) != B_OK) {
748 				// should not happen
749 				return;
750 			}
751 
752 			PRINT(("ShutdownProcess::MessageReceived(): B_SOME_APP_QUIT: %ld\n",
753 				team));
754 
755 			// remove the app info from the respective list
756 			int32 phase;
757 			RosterAppInfo *info;
758 			{
759 				BAutolock _(fWorkerLock);
760 
761 				info = fUserApps.InfoFor(team);
762 				if (info)
763 					fUserApps.RemoveInfo(info);
764 				else if ((info = fSystemApps.InfoFor(team)))
765 					fSystemApps.RemoveInfo(info);
766 				else if ((info = fBackgroundApps.InfoFor(team)))
767 					fBackgroundApps.RemoveInfo(info);
768 				else	// not found
769 					return;
770 
771 				phase = fCurrentPhase;
772 			}
773 
774 			// post the event
775 			_PushEvent(APP_QUIT_EVENT, team, phase);
776 
777 			delete info;
778 
779 			break;
780 		}
781 
782 		case MSG_PHASE_TIMED_OUT:
783 		{
784 			// get the phase the event is intended for
785 			int32 phase = TimeoutEvent::GetMessagePhase(message);
786 			team_id team = TimeoutEvent::GetMessageTeam(message);;
787 PRINT(("MSG_PHASE_TIMED_OUT: phase: %ld, team: %ld\n", phase, team));
788 
789 			BAutolock _(fWorkerLock);
790 
791 			if (phase == INVALID_PHASE || phase != fCurrentPhase)
792 				return;
793 
794 			// post the event
795 			_PushEvent(TIMEOUT_EVENT, team, phase);
796 
797 			break;
798 		}
799 
800 		case MSG_KILL_APPLICATION:
801 		{
802 			team_id team;
803 			if (message->FindInt32("team", &team) != B_OK)
804 				break;
805 
806 			BAutolock _(fWorkerLock);
807 
808 			// post the event
809 			_PushEvent(KILL_APP_EVENT, team, fCurrentPhase);
810 
811 			break;
812 		}
813 
814 		case MSG_CANCEL_SHUTDOWN:
815 		{
816 			BAutolock _(fWorkerLock);
817 
818 			// post the event
819 			_PushEvent(ABORT_EVENT, -1, fCurrentPhase);
820 
821 			break;
822 		}
823 
824 		case MSG_REBOOT_SYSTEM:
825 		{
826 			// post the event
827 			_PushEvent(REBOOT_SYSTEM_EVENT, -1, INVALID_PHASE);
828 
829 			break;
830 		}
831 
832 		case MSG_DONE:
833 		{
834 			// notify the registrar that we're done
835 			be_app->PostMessage(B_REG_SHUTDOWN_FINISHED, be_app);
836 
837 			break;
838 		}
839 
840 		default:
841 			BLooper::MessageReceived(message);
842 			break;
843 	}
844 }
845 
846 // SendReply
847 void
848 ShutdownProcess::SendReply(BMessage *request, status_t error)
849 {
850 	if (error == B_OK) {
851 		BMessage reply(B_REG_SUCCESS);
852 		request->SendReply(&reply);
853 	} else {
854 		BMessage reply(B_REG_ERROR);
855 		reply.AddInt32("error", error);
856 		request->SendReply(&reply);
857 	}
858 }
859 
860 // _SendReply
861 void
862 ShutdownProcess::_SendReply(status_t error)
863 {
864 	if (!fRequestReplySent) {
865 		SendReply(fRequest, error);
866 		fRequestReplySent = true;
867 	}
868 }
869 
870 // _SetPhase
871 void
872 ShutdownProcess::_SetPhase(int32 phase)
873 {
874 	BAutolock _(fWorkerLock);
875 
876 	if (phase == fCurrentPhase)
877 		return;
878 
879 	fCurrentPhase = phase;
880 
881 	// remove the timeout event scheduled for the previous phase
882 	fEventQueue->RemoveEvent(fTimeoutEvent);
883 }
884 
885 // _ScheduleTimeoutEvent
886 void
887 ShutdownProcess::_ScheduleTimeoutEvent(bigtime_t timeout, team_id team)
888 {
889 	BAutolock _(fWorkerLock);
890 
891 	// remove the timeout event
892 	fEventQueue->RemoveEvent(fTimeoutEvent);
893 
894 	// set the event's phase, team and time
895 	fTimeoutEvent->SetPhase(fCurrentPhase);
896 	fTimeoutEvent->SetTeam(team);
897 	fTimeoutEvent->SetTime(system_time() + timeout);
898 
899 	// add the event
900 	fEventQueue->AddEvent(fTimeoutEvent);
901 }
902 
903 // _SetShowShutdownWindow
904 void
905 ShutdownProcess::_SetShowShutdownWindow(bool show)
906 {
907 	if (fHasGUI) {
908 		BAutolock _(fWindow);
909 
910 		if (show == fWindow->IsHidden()) {
911 			if (show)
912 				fWindow->Show();
913 			else
914 				fWindow->Hide();
915 		}
916 	}
917 }
918 
919 // _InitShutdownWindow
920 void
921 ShutdownProcess::_InitShutdownWindow()
922 {
923 	// prepare the window
924 	if (fHasGUI) {
925 		fWindow = new(nothrow) ShutdownWindow;
926 		if (fWindow != NULL) {
927 			status_t error = fWindow->Init(BMessenger(this));
928 			if (error != B_OK) {
929 				delete fWindow;
930 				fWindow = NULL;
931 			}
932 		}
933 
934 		// add the applications
935 		if (fWindow) {
936 			BAutolock _(fWorkerLock);
937 			_AddShutdownWindowApps(fUserApps);
938 			_AddShutdownWindowApps(fSystemApps);
939 		} else {
940 			WARNING(("ShutdownProcess::Init(): Failed to create or init "
941 				"shutdown window."));
942 
943 			fHasGUI = false;
944 		}
945 	}
946 }
947 
948 // _AddShutdownWindowApps
949 void
950 ShutdownProcess::_AddShutdownWindowApps(AppInfoList &infos)
951 {
952 	if (!fHasGUI)
953 		return;
954 
955 	for (AppInfoList::Iterator it = infos.It(); it.IsValid(); ++it) {
956 		RosterAppInfo *info = *it;
957 
958 		// init an app file info
959 		BFile file;
960 		status_t error = file.SetTo(&info->ref, B_READ_ONLY);
961 		if (error != B_OK) {
962 			WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to "
963 				"open file for app %s: %s\n", info->signature,
964 				strerror(error)));
965 			continue;
966 		}
967 
968 		BAppFileInfo appFileInfo;
969 		error = appFileInfo.SetTo(&file);
970 		if (error != B_OK) {
971 			WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to "
972 				"init app file info for app %s: %s\n", info->signature,
973 				strerror(error)));
974 		}
975 
976 		// get the application icons
977 
978 		// mini icon
979 		BBitmap *miniIcon = new(nothrow) BBitmap(BRect(0, 0, 15, 15), B_CMAP8);
980 		if (miniIcon != NULL) {
981 			error = miniIcon->InitCheck();
982 			if (error == B_OK)
983 				error = appFileInfo.GetTrackerIcon(miniIcon, B_MINI_ICON);
984 			if (error != B_OK) {
985 				delete miniIcon;
986 				miniIcon = NULL;
987 			}
988 		}
989 
990 		// mini icon
991 		BBitmap *largeIcon = new(nothrow) BBitmap(BRect(0, 0, 31, 31), B_CMAP8);
992 		if (largeIcon != NULL) {
993 			error = largeIcon->InitCheck();
994 			if (error == B_OK)
995 				error = appFileInfo.GetTrackerIcon(largeIcon, B_LARGE_ICON);
996 			if (error != B_OK) {
997 				delete largeIcon;
998 				largeIcon = NULL;
999 			}
1000 		}
1001 
1002 		// add the app
1003 		error = fWindow->AddApp(info->team, miniIcon, largeIcon);
1004 		if (error != B_OK) {
1005 			WARNING(("ShutdownProcess::_AddShutdownWindowApps(): Failed to "
1006 				"add app to the shutdown window: %s\n", strerror(error)));
1007 		}
1008 	}
1009 }
1010 
1011 // _RemoveShutdownWindowApp
1012 void
1013 ShutdownProcess::_RemoveShutdownWindowApp(team_id team)
1014 {
1015 	if (fHasGUI) {
1016 		BAutolock _(fWindow);
1017 
1018 		fWindow->RemoveApp(team);
1019 	}
1020 }
1021 
1022 // _SetShutdownWindowCurrentApp
1023 void
1024 ShutdownProcess::_SetShutdownWindowCurrentApp(team_id team)
1025 {
1026 	if (fHasGUI) {
1027 		BAutolock _(fWindow);
1028 
1029 		fWindow->SetCurrentApp(team);
1030 	}
1031 }
1032 
1033 // _SetShutdownWindowText
1034 void
1035 ShutdownProcess::_SetShutdownWindowText(const char *text)
1036 {
1037 	if (fHasGUI) {
1038 		BAutolock _(fWindow);
1039 
1040 		fWindow->SetText(text);
1041 	}
1042 }
1043 
1044 // _SetShutdownWindowCancelButtonEnabled
1045 void
1046 ShutdownProcess::_SetShutdownWindowCancelButtonEnabled(bool enabled)
1047 {
1048 	if (fHasGUI) {
1049 		BAutolock _(fWindow);
1050 
1051 		fWindow->SetCancelShutdownButtonEnabled(enabled);
1052 	}
1053 }
1054 
1055 // _SetShutdownWindowKillButtonEnabled
1056 void
1057 ShutdownProcess::_SetShutdownWindowKillButtonEnabled(bool enabled)
1058 {
1059 	if (fHasGUI) {
1060 		BAutolock _(fWindow);
1061 
1062 		fWindow->SetKillAppButtonEnabled(enabled);
1063 	}
1064 }
1065 
1066 // _SetShutdownWindowWaitForShutdown
1067 void
1068 ShutdownProcess::_SetShutdownWindowWaitForShutdown()
1069 {
1070 	if (fHasGUI) {
1071 		BAutolock _(fWindow);
1072 
1073 		fWindow->SetWaitForShutdown();
1074 	}
1075 }
1076 
1077 // _SetShutdownWindowWaitForAbortedOK
1078 void
1079 ShutdownProcess::_SetShutdownWindowWaitForAbortedOK()
1080 {
1081 	if (fHasGUI) {
1082 		BAutolock _(fWindow);
1083 
1084 		fWindow->SetWaitForAbortedOK();
1085 	}
1086 }
1087 
1088 // _NegativeQuitRequestReply
1089 void
1090 ShutdownProcess::_NegativeQuitRequestReply(thread_id thread)
1091 {
1092 	BAutolock _(fWorkerLock);
1093 
1094 	// Note: team ID == team main thread ID under Haiku. When testing under R5
1095 	// using the team ID in case of an ABORT_EVENT won't work correctly. But
1096 	// this is done only for system apps.
1097 	_PushEvent(ABORT_EVENT, thread, fCurrentPhase);
1098 }
1099 
1100 // _PrepareShutdownMessage
1101 void
1102 ShutdownProcess::_PrepareShutdownMessage(BMessage &message) const
1103 {
1104 	message.what = B_QUIT_REQUESTED;
1105 	message.AddBool("_shutdown_", true);
1106 
1107 	BMessage::Private(message).SetReply(BMessenger(fQuitRequestReplyHandler));
1108 }
1109 
1110 // _ShutDown
1111 status_t
1112 ShutdownProcess::_ShutDown()
1113 {
1114 	PRINT(("Invoking _kern_shutdown(%d)\n", fReboot));
1115 
1116 	#ifdef __HAIKU__
1117 		RETURN_ERROR(_kern_shutdown(fReboot));
1118 	#else
1119 		// we can't do anything on R5
1120 		return B_ERROR;
1121 	#endif
1122 }
1123 
1124 
1125 // _PushEvent
1126 status_t
1127 ShutdownProcess::_PushEvent(uint32 eventType, team_id team, int32 phase)
1128 {
1129 	InternalEvent *event = new(nothrow) InternalEvent(eventType, team, phase);
1130 	if (!event) {
1131 		ERROR(("ShutdownProcess::_PushEvent(): Failed to create event!\n"));
1132 
1133 		return B_NO_MEMORY;
1134 	}
1135 
1136 	BAutolock _(fWorkerLock);
1137 
1138 	fInternalEvents->Add(event);
1139 	release_sem(fInternalEventSemaphore);
1140 
1141 	return B_OK;
1142 }
1143 
1144 // _GetNextEvent
1145 status_t
1146 ShutdownProcess::_GetNextEvent(uint32 &eventType, thread_id &team, int32 &phase,
1147 	bool block)
1148 {
1149 	while (true) {
1150 		// acquire the semaphore
1151 		if (block) {
1152 			status_t error;
1153 			do {
1154 				error = acquire_sem(fInternalEventSemaphore);
1155 			} while (error == B_INTERRUPTED);
1156 
1157 			if (error != B_OK)
1158 				return error;
1159 
1160 		} else {
1161 			status_t error = acquire_sem_etc(fInternalEventSemaphore, 1,
1162 				B_RELATIVE_TIMEOUT, 0);
1163 			if (error != B_OK) {
1164 				eventType = NO_EVENT;
1165 				return B_OK;
1166 			}
1167 		}
1168 
1169 		// get the event
1170 		BAutolock _(fWorkerLock);
1171 
1172 		InternalEvent *event = fInternalEvents->Head();
1173 		fInternalEvents->Remove(event);
1174 
1175 		eventType = event->Type();
1176 		team = event->Team();
1177 		phase = event->Phase();
1178 
1179 		delete event;
1180 
1181 		// if the event is an obsolete timeout event, we drop it right here
1182 		if (eventType == TIMEOUT_EVENT && phase != fCurrentPhase)
1183 			continue;
1184 
1185 		break;
1186 	}
1187 
1188 	// notify the window, if an app has been removed
1189 	if (eventType == APP_QUIT_EVENT)
1190 		_RemoveShutdownWindowApp(team);
1191 
1192 	return B_OK;
1193 }
1194 
1195 // _WorkerEntry
1196 status_t
1197 ShutdownProcess::_WorkerEntry(void *data)
1198 {
1199 	return ((ShutdownProcess*)data)->_Worker();
1200 }
1201 
1202 // _Worker
1203 status_t
1204 ShutdownProcess::_Worker()
1205 {
1206 	try {
1207 		_WorkerDoShutdown();
1208 		fShutdownError = B_OK;
1209 	} catch (status_t error) {
1210 		PRINT(("ShutdownProcess::_Worker(): error while shutting down: %s\n",
1211 			strerror(error)));
1212 
1213 		fShutdownError = error;
1214 	}
1215 
1216 	// this can happen only, if the shutdown process failed or was aborted:
1217 	// notify the looper
1218 	_SetPhase(DONE_PHASE);
1219 	PostMessage(MSG_DONE);
1220 
1221 	return B_OK;
1222 }
1223 
1224 // _WorkerDoShutdown
1225 void
1226 ShutdownProcess::_WorkerDoShutdown()
1227 {
1228 	PRINT(("ShutdownProcess::_WorkerDoShutdown()\n"));
1229 
1230 	// If we are here, the shutdown process has been initiated successfully,
1231 	// that is, if an asynchronous BRoster::Shutdown() was requested, we
1232 	// notify the caller at this point.
1233 	bool synchronous;
1234 	if (fRequest->FindBool("synchronous", &synchronous) == B_OK
1235 		&& !synchronous) {
1236 		_SendReply(B_OK);
1237 	}
1238 
1239 	// ask the user to confirm the shutdown, if desired
1240 	bool askUser;
1241 	if (fHasGUI && fRequest->FindBool("confirm", &askUser) == B_OK && askUser) {
1242 		const char *title = (fReboot ? "Restart?" : "Shut Down?");
1243 		const char *text = (fReboot
1244 			? "Do you really want to restart the system?"
1245 			: "Do you really want to shut down the system?");
1246 		const char *buttonText = (fReboot ? "Restart" : "Shut Down");
1247 		BAlert *alert = new BAlert(title, text, "Cancel", buttonText, NULL,
1248 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1249 		int32 result = alert->Go();
1250 
1251 		if (result != 1)
1252 			throw_error(B_SHUTDOWN_CANCELLED);
1253 	}
1254 
1255 	// make the shutdown window ready and show it
1256 	_InitShutdownWindow();
1257 	_SetShutdownWindowCurrentApp(-1);
1258 	_SetShutdownWindowText("Tidying things up a bit.");
1259 	_SetShutdownWindowCancelButtonEnabled(true);
1260 	_SetShutdownWindowKillButtonEnabled(false);
1261 	_SetShowShutdownWindow(true);
1262 
1263 	// sync
1264 	sync();
1265 
1266 	// phase 1: terminate the user apps
1267 	_SetPhase(USER_APP_TERMINATION_PHASE);
1268 	_QuitApps(fUserApps, false);
1269 
1270 	// phase 2: terminate the system apps
1271 	_SetPhase(SYSTEM_APP_TERMINATION_PHASE);
1272 	_QuitApps(fSystemApps, true);
1273 
1274 	// phase 3: terminate the background apps
1275 	_SetPhase(BACKGROUND_APP_TERMINATION_PHASE);
1276 // TODO: _QuitNonApps() and _QuitBackgroundApps() are called in reverse?
1277 // and don't match the phase
1278 	_QuitNonApps();
1279 
1280 	// phase 4: terminate the other processes
1281 	_SetPhase(OTHER_PROCESSES_TERMINATION_PHASE);
1282 	_QuitBackgroundApps();
1283 	_ScheduleTimeoutEvent(kBackgroundAppQuitTimeout, -1);
1284 	_WaitForBackgroundApps();
1285 	_KillBackgroundApps();
1286 
1287 	// we're through: do the shutdown
1288 	_SetPhase(DONE_PHASE);
1289 	_ShutDown();
1290 	_SetShutdownWindowWaitForShutdown();
1291 
1292 	PRINT(("  _kern_shutdown() failed\n"));
1293 
1294 	// shutdown failed: This can happen for power off mode -- reboot should
1295 	// always work.
1296 	if (fHasGUI) {
1297 		// wait for the reboot event
1298 		uint32 event;
1299 		do {
1300 			team_id team;
1301 			int32 phase;
1302 			status_t error = _GetNextEvent(event, team, phase, true);
1303 			if (error != B_OK)
1304 				break;
1305 		} while (event != REBOOT_SYSTEM_EVENT);
1306 
1307 		#ifdef __HAIKU__
1308 		_kern_shutdown(true);
1309 		#endif
1310 	}
1311 
1312 	// either there's no GUI or reboot failed: we enter the kernel debugger
1313 	// instead
1314 	#ifdef __HAIKU__
1315 // TODO: Introduce the syscall.
1316 //	while (true) {
1317 //		_kern_kernel_debugger("The system is shut down. It's now safe to turn "
1318 //			"off the computer.");
1319 //	}
1320 	#endif
1321 }
1322 
1323 // _QuitApps
1324 void
1325 ShutdownProcess::_QuitApps(AppInfoList &list, bool disableCancel)
1326 {
1327 	PRINT(("ShutdownProcess::_QuitApps(%s)\n",
1328 		(disableCancel ? "system" : "user")));
1329 
1330 	if (disableCancel) {
1331 		_SetShutdownWindowCancelButtonEnabled(false);
1332 
1333 		// check one last time for abort events
1334 		uint32 event;
1335 		do {
1336 			team_id team;
1337 			int32 phase;
1338 			status_t error = _GetNextEvent(event, team, phase, false);
1339 			if (error != B_OK)
1340 				throw_error(error);
1341 
1342 			if (event == ABORT_EVENT) {
1343 				PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by "
1344 					"team %ld (-1 => user)\n", team));
1345 
1346 				_DisplayAbortingApp(team);
1347 				throw_error(B_SHUTDOWN_CANCELLED);
1348 			}
1349 
1350 		} while (event != NO_EVENT);
1351 	}
1352 
1353 	// prepare the shutdown message
1354 	BMessage message;
1355 	_PrepareShutdownMessage(message);
1356 
1357 	// now iterate through the list of apps
1358 	while (true) {
1359 		// eat events
1360 		uint32 event;
1361 		do {
1362 			team_id team;
1363 			int32 phase;
1364 			status_t error = _GetNextEvent(event, team, phase, false);
1365 			if (error != B_OK)
1366 				throw_error(error);
1367 
1368 			if (!disableCancel && event == ABORT_EVENT) {
1369 				PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled by "
1370 					"team %ld (-1 => user)\n", team));
1371 
1372 				_DisplayAbortingApp(team);
1373 				throw_error(B_SHUTDOWN_CANCELLED);
1374 			}
1375 
1376 		} while (event != NO_EVENT);
1377 
1378 		// get the first app to quit
1379 		team_id team = -1;
1380 		port_id port = -1;
1381 		char appName[B_FILE_NAME_LENGTH];
1382 		{
1383 			BAutolock _(fWorkerLock);
1384 			if (!list.IsEmpty()) {
1385 				RosterAppInfo *info = *list.It();
1386 				team = info->team;
1387 				port = info->port;
1388 				strcpy(appName, info->ref.name);
1389 			}
1390 		}
1391 
1392 		if (team < 0) {
1393 			PRINT(("ShutdownProcess::_QuitApps() done\n"));
1394 			return;
1395 		}
1396 
1397 		// set window text
1398 		char buffer[1024];
1399 		snprintf(buffer, sizeof(buffer), "Asking \"%s\" to quit.", appName);
1400 		_SetShutdownWindowText(buffer);
1401 		_SetShutdownWindowCurrentApp(team);
1402 
1403 		// send the shutdown message to the app
1404 		PRINT(("  sending team %ld (port: %ld) a shutdown message\n", team,
1405 			port));
1406 		SingleMessagingTargetSet target(port, B_PREFERRED_TOKEN);
1407 		MessageDeliverer::Default()->DeliverMessage(&message, target);
1408 
1409 		// schedule a timeout event
1410 		_ScheduleTimeoutEvent(kAppQuitTimeout, team);
1411 
1412 		// wait for the app to die or for the timeout to occur
1413 		bool appGone = false;
1414 		do {
1415 			team_id eventTeam;
1416 			int32 phase;
1417 			status_t error = _GetNextEvent(event, eventTeam, phase, true);
1418 			if (error != B_OK)
1419 				throw_error(error);
1420 
1421 			if ((event == APP_QUIT_EVENT)
1422 				&& eventTeam == team) {
1423 				appGone = true;
1424 			}
1425 
1426 			if (event == TIMEOUT_EVENT && eventTeam == team)
1427 				break;
1428 
1429 			if (event == ABORT_EVENT) {
1430 				if (disableCancel) {
1431 					// If the app requests aborting the shutdown, we don't need
1432 					// to wait any longer. It has processed the request and
1433 					// won't quit by itself. We'll have to kill it.
1434 					if (eventTeam == team)
1435 						break;
1436 				} else {
1437 					PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled "
1438 						"by team %ld (-1 => user)\n", eventTeam));
1439 
1440 					_DisplayAbortingApp(team);
1441 					throw_error(B_SHUTDOWN_CANCELLED);
1442 				}
1443 			}
1444 
1445 			BAutolock _(fWorkerLock);
1446 			if (!list.InfoFor(team))
1447 				break;
1448 
1449 		} while (event != NO_EVENT);
1450 
1451 		if (appGone) {
1452 			// fine: the app finished in an orderly manner
1453 		} else {
1454 			// the app is either blocking on a model alert or blocks for another
1455 			// reason
1456 			_QuitBlockingApp(list, team, appName, !disableCancel);
1457 		}
1458 	}
1459 }
1460 
1461 // _QuitBackgroundApps
1462 void
1463 ShutdownProcess::_QuitBackgroundApps()
1464 {
1465 	PRINT(("ShutdownProcess::_QuitBackgroundApps()\n"));
1466 
1467 	_SetShutdownWindowText("Asking background applications to quit.");
1468 
1469 	// prepare the shutdown message
1470 	BMessage message;
1471 	_PrepareShutdownMessage(message);
1472 
1473 	// send shutdown messages to user apps
1474 	BAutolock _(fWorkerLock);
1475 
1476 	AppInfoListMessagingTargetSet targetSet(fBackgroundApps);
1477 
1478 	if (targetSet.HasNext()) {
1479 		PRINT(("  sending shutdown message to %ld apps\n",
1480 			fBackgroundApps.CountInfos()));
1481 
1482 		status_t error = MessageDeliverer::Default()->DeliverMessage(
1483 			&message, targetSet);
1484 		if (error != B_OK) {
1485 			WARNING(("_QuitBackgroundApps::_Worker(): Failed to deliver "
1486 				"shutdown message to all applications: %s\n",
1487 				strerror(error)));
1488 		}
1489 	}
1490 
1491 	PRINT(("ShutdownProcess::_QuitBackgroundApps() done\n"));
1492 }
1493 
1494 // _WaitForBackgroundApps
1495 void
1496 ShutdownProcess::_WaitForBackgroundApps()
1497 {
1498 	PRINT(("ShutdownProcess::_WaitForBackgroundApps()\n"));
1499 
1500 	// wait for user apps
1501 	bool moreApps = true;
1502 	while (moreApps) {
1503 		{
1504 			BAutolock _(fWorkerLock);
1505 			moreApps = !fBackgroundApps.IsEmpty();
1506 		}
1507 
1508 		if (moreApps) {
1509 			uint32 event;
1510 			team_id team;
1511 			int32 phase;
1512 			status_t error = _GetNextEvent(event, team, phase, true);
1513 			if (error != B_OK)
1514 				throw_error(error);
1515 
1516 			if (event == ABORT_EVENT) {
1517 				// ignore: it's too late to abort the shutdown
1518 			}
1519 
1520 			if (event == TIMEOUT_EVENT)
1521 				return;
1522 		}
1523 	}
1524 
1525 	PRINT(("ShutdownProcess::_WaitForBackgroundApps() done\n"));
1526 }
1527 
1528 // _KillBackgroundApps
1529 void
1530 ShutdownProcess::_KillBackgroundApps()
1531 {
1532 	PRINT(("ShutdownProcess::_KillBackgroundApps()\n"));
1533 
1534 	while (true) {
1535 		// eat events (we need to be responsive for an abort event)
1536 		uint32 event;
1537 		do {
1538 			team_id team;
1539 			int32 phase;
1540 			status_t error = _GetNextEvent(event, team, phase, false);
1541 			if (error != B_OK)
1542 				throw_error(error);
1543 
1544 		} while (event != NO_EVENT);
1545 
1546 		// get the first team to kill
1547 		team_id team = -1;
1548 		char appName[B_FILE_NAME_LENGTH];
1549 		AppInfoList &list = fBackgroundApps;
1550 		{
1551 			BAutolock _(fWorkerLock);
1552 
1553 			if (!list.IsEmpty()) {
1554 				RosterAppInfo *info = *list.It();
1555 				team = info->team;
1556 				strcpy(appName, info->ref.name);
1557 			}
1558 		}
1559 
1560 
1561 		if (team < 0) {
1562 			PRINT(("ShutdownProcess::_KillBackgroundApps() done\n"));
1563 			return;
1564 		}
1565 
1566 		// the app is either blocking on a model alert or blocks for another
1567 		// reason
1568 		_QuitBlockingApp(list, team, appName, false);
1569 	}
1570 }
1571 
1572 // _QuitNonApps
1573 void
1574 ShutdownProcess::_QuitNonApps()
1575 {
1576 	PRINT(("ShutdownProcess::_QuitNonApps()\n"));
1577 
1578 	_SetShutdownWindowText("Asking other processes to quit.");
1579 
1580 	// iterate through the remaining teams and send them the HUP signal
1581 	int32 cookie = 0;
1582 	team_info teamInfo;
1583 	while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
1584 		if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) {
1585 			PRINT(("  sending team %ld HUP signal\n", teamInfo.team));
1586 
1587 			#ifdef __HAIKU__
1588 				// Note: team ID == team main thread ID under Haiku
1589 				send_signal(teamInfo.team, SIGHUP);
1590 			#else
1591 				// We don't want to do this when testing under R5, since it
1592 				// would kill all teams besides our app server and registrar.
1593 			#endif
1594 		}
1595 	}
1596 
1597 	// give them a bit of time to terminate
1598 	// TODO: Instead of just waiting we could periodically check whether the
1599 	// processes are already gone to shorten the process.
1600 	snooze(kNonAppQuitTimeout);
1601 
1602 	// iterate through the remaining teams and kill them
1603 	cookie = 0;
1604 	while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
1605 		if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) {
1606 			PRINT(("  killing team %ld\n", teamInfo.team));
1607 
1608 			#ifdef __HAIKU__
1609 				kill_team(teamInfo.team);
1610 			#else
1611 				// We don't want to do this when testing under R5, since it
1612 				// would kill all teams besides our app server and registrar.
1613 			#endif
1614 		}
1615 	}
1616 
1617 	PRINT(("ShutdownProcess::_QuitNonApps() done\n"));
1618 }
1619 
1620 // _QuitBlockingApp
1621 void
1622 ShutdownProcess::_QuitBlockingApp(AppInfoList &list, team_id team,
1623 	const char *appName, bool cancelAllowed)
1624 {
1625 	if (BPrivate::is_app_showing_modal_window(team)) {
1626 		// app blocks on a modal window
1627 		char buffer[1024];
1628 		snprintf(buffer, sizeof(buffer), "The application \"%s\" might be "
1629 			"blocked on a modal panel.", appName);
1630 		_SetShutdownWindowText(buffer);
1631 		_SetShutdownWindowCurrentApp(team);
1632 		_SetShutdownWindowKillButtonEnabled(true);
1633 
1634 		// wait for something to happen
1635 		bool appGone = false;
1636 		while (true) {
1637 			uint32 event;
1638 			team_id eventTeam;
1639 			int32 phase;
1640 			status_t error = _GetNextEvent(event, eventTeam, phase, true);
1641 			if (error != B_OK)
1642 				throw_error(error);
1643 
1644 			if ((event == APP_QUIT_EVENT) && eventTeam == team) {
1645 				appGone = true;
1646 				break;
1647 			}
1648 
1649 			if (event == KILL_APP_EVENT && eventTeam == team)
1650 				break;
1651 
1652 			if (event == ABORT_EVENT) {
1653 				if (cancelAllowed) {
1654 					PRINT(("ShutdownProcess::_QuitBlockingApp(): shutdown "
1655 						"cancelled by team %ld (-1 => user)\n", eventTeam));
1656 
1657 					throw_error(B_SHUTDOWN_CANCELLED);
1658 				}
1659 
1660 				// If the app requests aborting the shutdown, we don't need
1661 				// to wait any longer. It has processed the request and
1662 				// won't quit by itself. We'll have to kill it.
1663 				if (eventTeam == team)
1664 					break;
1665 			}
1666 		}
1667 
1668 		_SetShutdownWindowKillButtonEnabled(false);
1669 
1670 		if (appGone)
1671 			return;
1672 	}
1673 
1674 	// kill the app
1675 	PRINT(("  killing team %ld\n", team));
1676 
1677 	kill_team(team);
1678 
1679 	// remove the app (the roster will note eventually and send us
1680 	// a notification, but we want to be sure)
1681 	{
1682 		BAutolock _(fWorkerLock);
1683 
1684 		if (RosterAppInfo *info = list.InfoFor(team)) {
1685 			list.RemoveInfo(info);
1686 			delete info;
1687 		}
1688 	}
1689 }
1690 
1691 // _DisplayAbortingApp
1692 void
1693 ShutdownProcess::_DisplayAbortingApp(team_id team)
1694 {
1695 	// find the app that cancelled the shutdown
1696 	char appName[B_FILE_NAME_LENGTH];
1697 	bool foundApp = false;
1698 	{
1699 		BAutolock _(fWorkerLock);
1700 
1701 		RosterAppInfo *info = fUserApps.InfoFor(team);
1702 		if (!info)
1703 			info = fSystemApps.InfoFor(team);
1704 		if (!info)
1705 			fBackgroundApps.InfoFor(team);
1706 
1707 		if (info) {
1708 			foundApp = true;
1709 			strcpy(appName, info->ref.name);
1710 		}
1711 	}
1712 
1713 	if (!foundApp) {
1714 		PRINT(("ShutdownProcess::_DisplayAbortingApp(): Didn't find the app "
1715 			"that has cancelled the shutdown.\n"));
1716 		return;
1717 	}
1718 
1719 	// compose the text to be displayed
1720 	char buffer[1024];
1721 	snprintf(buffer, sizeof(buffer), "Application \"%s\" has aborted the shutdown "
1722 		"process.", appName);
1723 
1724 	// set up the window
1725 	_SetShutdownWindowCurrentApp(team);
1726 	_SetShutdownWindowText(buffer);
1727 	_SetShutdownWindowWaitForAbortedOK();
1728 
1729 	// schedule the timeout event
1730 	_SetPhase(ABORTED_PHASE);
1731 	_ScheduleTimeoutEvent(kDisplayAbortingAppTimeout);
1732 
1733 	// wait for the timeout or the user to press the cancel button
1734 	while (true) {
1735 		uint32 event;
1736 		team_id eventTeam;
1737 		int32 phase;
1738 		status_t error = _GetNextEvent(event, eventTeam, phase, true);
1739 		if (error != B_OK)
1740 			break;
1741 
1742 		// stop waiting when the timeout occurs
1743 		if (event == TIMEOUT_EVENT)
1744 			break;
1745 
1746 		// stop waiting when the user hit the cancel button
1747 		if (event == ABORT_EVENT && phase == ABORTED_PHASE && eventTeam < 0)
1748 			break;
1749 
1750 // This doesn't give us anything; it will just prevent us to see which
1751 // app was responsible after all...
1752 #if 0
1753 		// also stop when the responsible app quit
1754 		if ((event == APP_QUIT_EVENT) && eventTeam == team)
1755 			break;
1756 #endif
1757 	}
1758 }
1759 
1760