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