// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
//
//  Copyright (c) 2002, OpenBeOS
//
//  This software is part of the OpenBeOS distribution and is covered 
//  by the OpenBeOS license.
//
//
//  File:        shutdown.cpp
//  Author:      Francois Revol (mmu_man@users.sf.net)
//  Description: shuts down the system, either halting or rebooting.
//
//  Notes:
//  This program behaves identically as the BeOS R5 version, with these 
//  added arguments:
//  
//  -a alert user while counting down
//  -c cancels any running shutdown
//
//  There seem to be a race condition (sometimes crashes on quit with 
//  "the looper must be locked"), if you find it, tell me,
//  tried 3 hours to find it.
//  Some code from Shard's Archiver from BeBits (was BSD/MIT too :).
//
//  Compile with: LDFLAGS="-lbe" make
//
// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

#include <Application.h>
#include <Bitmap.h>
#include <Button.h>
#include <Messenger.h>
#include <Mime.h>
#include <OS.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <String.h>
#include <StringView.h>
#include <unistd.h>
#include <View.h>
#include <Window.h>

// timer should be handled better (with system_time())
#define PULSE_RATE 100000
#define PULSE_MODULO 10

//from bdb-ing /bin/shutdown:
#define B_SYSTEM_SHUTDOWN 0x12d
#define B_SYSTEM_REBOOT 0x12e

const char *roster_sig = "application/x-vnd.Be-ROST";

extern "C" void _kshutdown_(long arg);
#define SHUTDOWN_HALT 0
#define SHUTDOWN_REBOOT 1

// globals
const char kIconBits[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00,
0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00,
0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x3f, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x2f,
0xeb, 0x2f, 0x30, 0x2f, 0x00, 0x00, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x2f, 0xeb, 0x2c,
0x2b, 0x2c, 0x2d, 0x30, 0x2f, 0x2f, 0x00, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x2f, 0x2b, 0x2a,
0x2b, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x2f, 0x2b, 0x2b, 0x2a,
0x2b, 0x2b, 0x2b, 0x2d, 0x2d, 0x2f, 0x30, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x2f, 0x2b, 0x2b, 0x3f,
0x2b, 0x2a, 0x2c, 0x2d, 0x2d, 0xeb, 0x30, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x2f, 0x2c, 0x2b, 0x2b,
0x2a, 0x2b, 0x2c, 0x2c, 0x2e, 0x2f, 0x2f, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x2f, 0x2d, 0x2b, 0x2c,
0x2c, 0x2b, 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x2f, 0xeb, 0x2d, 0x2d,
0x2c, 0x2c, 0x2d, 0x2d, 0xeb, 0x2f, 0x2f, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x00, 0xff,
0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x30, 0x2e, 0x2c,
0x2d, 0x2d, 0x2d, 0x2f, 0x2f, 0x2f, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0xff,
0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x00, 0x2f, 0x2f, 0xeb,
0xeb, 0xeb, 0xeb, 0xeb, 0x2f, 0x2f, 0x00, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff,
0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x3f, 0x3f, 0x00, 0x00, 0x2f,
0x2f, 0x30, 0x2f, 0x2f, 0x00, 0x00, 0x3f, 0x3f, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff,
0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x3f, 0x3f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff,
0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x3f,
0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff,
0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00,
0x00, 0x3f, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

const char kElectronBits[] = {
0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
0xff, 0x00, 0x25, 0x24, 0x26, 0x00, 0xff, 0xff,
0x00, 0x25, 0x21, 0x21, 0x22, 0x26, 0x00, 0xff,
0x00, 0x21, 0x3f, 0x22, 0x22, 0x25, 0x00, 0xff,
0x00, 0x25, 0x22, 0x22, 0x24, 0x24, 0x00, 0xff,
0xff, 0x00, 0x23, 0x23, 0x25, 0x00, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

#define EL_POS_COUNT 17
static const int electron1_table[][2] = { 
{3, 8}, {7, 8}, {12, 8}, {17, 10}, 
{22, 12}, {26, 16}, {28, 20}, {28, 24}, 
{26, 26}, {22, 27}, {18, 26}, {14, 25}, 
{10, 24}, {5, 22}, {3, 18}, {1, 14}, 
{1, 10}, {-1, -1} };

static const int electron2_table[][2] = { 
{23, 18}, {21, 22}, {18, 25}, {14, 27},
{10, 27}, {5, 26}, {0, 25}, {1, 18}, 
{4, 15}, {8, 12}, {12, 10}, {16, 8},
{20, 8}, {24, 9}, {28, 11}, {27, 13},
{25, 15}, {-1, -1} };

unsigned long time_to_sleep = 0; // -d
int reboot = 0; // -r


class SView : public BView
{
	public:
							SView(BRect frame);
							~SView();

		void				Draw(BRect updateRect);
		void				Pulse();

		BButton				*fButton;
		BString				*fTitleStr;
		BStringView			*fTitle;

		BBitmap				*fIcon;
		BBitmap				*fElectron;
		int				fElectronPos;

		int				fPulseDivider;

		int32				fWidth;
		int32				fHeight;

		int32				fLeftMargin;
		static const int32	fTopMargin = 8;
		unsigned long		fTimeLeft;
};

class ShutdownWindow : public BWindow
{
	public:
						ShutdownWindow(thread_id id);
						~ShutdownWindow();
		void			MessageReceived(BMessage *msg);
		bool			QuitRequested();
	private:
		SView *fView;
		thread_id fShutdownThread;
};

class ShutdownApp : public BApplication
{
	public:
						ShutdownApp(thread_id id);
						~ShutdownApp();
	private:
		ShutdownWindow *fWindow;
};

SView::SView(BRect frame)
	:BView(frame, "SView", B_FOLLOW_LEFT_RIGHT, B_PULSE_NEEDED | B_WILL_DRAW),
	fButton(NULL),
	fTitleStr(NULL),
	fTitle(NULL),
	fIcon(NULL),
	fElectron(NULL),
	fElectronPos(0),
	fPulseDivider(PULSE_MODULO+1),
	fWidth(0),
	fHeight(0),
	fLeftMargin(B_LARGE_ICON + 8),
	fTimeLeft(time_to_sleep)
{
	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	SetLowColor(ViewColor());
	SetDrawingMode(B_OP_OVER);
	fIcon = new BBitmap(BRect(0, 0, B_LARGE_ICON-1, B_LARGE_ICON-1), B_CMAP8);
	fIcon->SetBits(kIconBits, B_LARGE_ICON*B_LARGE_ICON, 0, B_CMAP8);
	fElectron = new BBitmap(BRect(0, 0, 7, 7), B_CMAP8);
	fElectron->SetBits(kElectronBits, 8*8, 0, B_CMAP8);
	fTitleStr = new BString;
	if (reboot)
		*fTitleStr << "Rebooting in " << (int32)fTimeLeft << " seconds !";
	else
		*fTitleStr << "Shutting down in " << (int32)fTimeLeft << " seconds !";
	fTitle = new BStringView(BRect(55, 10, 260, 35), "", fTitleStr->String());
	fButton = new BButton(BRect(159, 42, 259, 66), "quit", "Cancel", new BMessage('_QRQ'));
	fButton->MakeDefault(true);
	AddChild(fTitle);
	AddChild(fButton);
}

SView::~SView()
{
	if (fButton != NULL)
		delete fButton;

	if (fTitle != NULL)
		delete fTitle;

	if (fIcon != NULL)
		delete fIcon;
}

void
SView::Pulse()
{
	fPulseDivider--;
	if (fPulseDivider <= 0) {
		fPulseDivider = PULSE_MODULO;
		fTimeLeft--;
		fTitleStr->SetTo("");
		if (reboot)
			*fTitleStr << "Rebooting in " << (int32)fTimeLeft << " seconds !";
		else
			*fTitleStr << "Shutting down in " << (int32)fTimeLeft << " seconds !";
		fTitle->SetText(fTitleStr->String());
	}
	fElectronPos += 1;
	fElectronPos %= EL_POS_COUNT;
	Invalidate();
}

void
SView::Draw(BRect updateRect)
{
	BRect bounds = Bounds();

	// left margin background
//	SetHighColor(tint_color(ViewColor(), B_HIGHLIGHT_BACKGROUND_TINT));
	SetHighColor(0xB8, 0xB8, 0xB8); // same color as the registrar shutdown dialog
	FillRect(BRect(0, 0, B_LARGE_ICON, bounds.bottom));
	
	if(fIcon != NULL)
	{
		DrawBitmap(fIcon, BPoint((B_LARGE_ICON / 2), fTopMargin));
	}
	if(fIcon != NULL)
	{
		DrawBitmap(fElectron, BPoint((B_LARGE_ICON / 2) + electron1_table[fElectronPos][0], fTopMargin + electron1_table[fElectronPos][1]));
		DrawBitmap(fElectron, BPoint((B_LARGE_ICON / 2) + electron2_table[fElectronPos][0], fTopMargin + electron2_table[fElectronPos][1]));
	}
	if (Window())
		Window()->Flush();
}

ShutdownWindow::ShutdownWindow(thread_id id)
	:BWindow(BRect( 139, 81, 407, 156), "Shutdown Status", B_TITLED_WINDOW_LOOK, 
				B_NORMAL_WINDOW_FEEL, B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_ASYNCHRONOUS_CONTROLS),
	fView(NULL)
{
	fShutdownThread = id;
	SetPulseRate(PULSE_RATE);
	fView = new SView(Bounds());
	AddChild(fView);
}

ShutdownWindow::~ShutdownWindow()
{
	fView->LockLooper();
	fView->RemoveSelf();
	delete fView;
}

void
ShutdownWindow::MessageReceived(BMessage *msg)
{
	switch(msg->what)
	{
		default:
			BWindow::MessageReceived(msg);
			break;
	}
}

bool
ShutdownWindow::QuitRequested()
{
	SetPulseRate(0);
	kill(fShutdownThread, SIGUSR1);
	return true;
}

ShutdownApp::ShutdownApp(thread_id id)
	:BApplication("application/x-vnd.ShutDown-app")
{
	fWindow = new ShutdownWindow(id);
	fWindow->SetWorkspaces(0xFFFFFFFF);
	fWindow->Show();
}

ShutdownApp::~ShutdownApp()
{
}

int32 app_thread(void *arg)
{
	thread_id tid = (thread_id) arg;
	(new ShutdownApp(tid))->Run();
	delete be_app;
	return B_OK;
}

/***************************/

// we get here when shutdown is cancelled.
// then sleep() returns
void handle_usr1(int sig)
{
	while (0);
}

int main(int argc, char **argv)
{
	int i;
	int quick = 0; // -q
	int alert = 0; // -a
	BMessenger msgr(roster_sig);

	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-q"))
			quick = 1;
		else if (!strcmp(argv[i], "-r"))
			reboot = 1;
		else if (!strcmp(argv[i], "-c")) {
			team_info ti;
			thread_info thi;
			int32 team_cookie = 0;
			int32 thread_cookie = 0;
			team_id myteam;

			get_thread_info(find_thread(NULL), &thi);
			myteam = thi.team;
			while (get_next_team_info(&team_cookie, &ti) == B_OK) {
				if (strstr(ti.args, "shutdown") != NULL && (ti.team != myteam)) {
					thread_cookie = 0;
					while (get_next_thread_info(ti.team, &thread_cookie, &thi) == B_OK) {
						if (!strcmp(thi.name, "shutdown")) {
							kill(thi.thread, SIGUSR1);
						}
					}
				}
			}
			exit (0);
		} else if (!strcmp(argv[i], "-a")) {
			alert = 1;
		} else if (!strcmp(argv[i], "-d") && (i+1 < argc)) {
			i++;
			time_to_sleep = strtol(argv[i], &argv[i], 10);
		} else {
			fprintf(stderr, 
"usage: %s [-r] [-q] [-c] [-a] [-d time]

	-r reboot,
	-q quick shutdown (don't broadcast apps),
	-c cancel a running shutdown,
	-a alerts user while waiting,
	-d delay shutdown by <time> seconds.

", argv[0]);
			exit (1);
		}
	}

	if (time_to_sleep > 0) {
		unsigned int left;
		thread_id BApp_thread = -1;

		signal(SIGUSR1, handle_usr1);

		if (alert) {
			// BApplication::Run() blocks
			BApp_thread = spawn_thread(app_thread, "BApplication", B_NORMAL_PRIORITY, (void *)find_thread(NULL));
			resume_thread(BApp_thread);
		}

		left = sleep((unsigned int)time_to_sleep);
		
		if (alert) {
			status_t err;
//			be_app->LockLooper();
			be_app->PostMessage(B_QUIT_REQUESTED);
//			be_app->UnlockLooper();
			
			wait_for_thread(BApp_thread, &err);
			snooze(100000);
		}
		
		if (left > 0) {
			fprintf(stderr, "Shutdown cancelled.\n");
			exit (0);
		}
	}

	if (quick) {
		if (reboot)
			_kshutdown_(SHUTDOWN_REBOOT);
		else
			_kshutdown_(SHUTDOWN_HALT);
		fprintf(stderr, "Shutdown failed!\n");
		return 2;
	} else {
		status_t err = 1;

		if (reboot)
			err = msgr.SendMessage(B_SYSTEM_REBOOT);
		else
			err = msgr.SendMessage(B_SYSTEM_SHUTDOWN);
		if (err != B_OK) {
			fprintf(stderr, "Failed to send message to roster server: %s\n", strerror(err));
			exit (2);
		}
	}
	return 0;
}

