/***************************************************************************
 *                                                                         *
 *   usvmon v1.04                                                          *
 *   for information see http://www.schlager.bz                            *
 *                                                                         *
 *   Copyright (C) 2004-2005 by Kurt Schlager                              *
 *   software@schlager.bz                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
using namespace std;

#define OPTION_SAFE_TIMEOUT 10

class USVObj
{
	private:
		char* device_;
		int fd_;
	
	public:
		USVObj();
		~USVObj();
		bool start();
		void setDevice(char* device);
		void shutdown();
		bool isOnBattery();
		bool lowBattery();
};

//--------------------------------------------------
bool USVObj::start()
{
	if (device_ == "")
		return false;
		
	fd_ = open(device_, O_RDWR|O_NDELAY);
	if (fd_ < 0)
		return false;

	//ioctl(fd_, TIOCMSET, TIOCM_RTS);
	return true;
}

//--------------------------------------------------
USVObj::USVObj()
{
}

//--------------------------------------------------
USVObj::~USVObj()
{
	close(fd_);
}

//--------------------------------------------------
void USVObj::setDevice(char* device)
{
	device_ = device;
}

//--------------------------------------------------
void USVObj::shutdown()
{
	ioctl(fd_, TIOCMBIS, TIOCM_DTR);
	ioctl(fd_, TIOCMBIS, TIOCM_DTR);
	ioctl(fd_, TIOCMBIS, TIOCM_DTR);
	sleep(5);
}

//--------------------------------------------------
bool USVObj::isOnBattery()
{
	int status = 0;
	ioctl(fd_, TIOCMGET, &status);
	if ((status & TIOCM_CTS) == TIOCM_CTS)
		return true;
	else
		return false;
}

//--------------------------------------------------
bool USVObj::lowBattery()
{
	int status;
	ioctl(fd_, TIOCMGET, &status);
	if ((status & TIOCM_CD) == TIOCM_CD)
		return true;
	else
		return false;
}



//--------------------------------------------------
bool writeToLog(char* message)
{
	FILE* fp = fopen("/var/log/usvmon.log", "a");
	if (fp < 0)
		return false;

	time_t curr;
	tm local;
	time(&curr);
	local=*(localtime(&curr));
	char *now = new char[32];
	strftime(now, 31, "%Y.%m.%d-%T", &local);

	fwrite(now, 1, strlen(now), fp);
	fwrite(" | ", 1, 3, fp);
	fwrite(message, 1, strlen(message), fp);
	fwrite("\n", 1, 1, fp);
	fclose(fp);
	return true;
}



//--------------------------------------------------
void shutdown(USVObj* usv)
{
	writeToLog("Shutting down the system now!");
	usv->shutdown();
	char* args[] = {"/sbin/shutdown", "-h", "now", NULL};
	(void)execvp(args[0], args);
	writeToLog("ERROR - Could not shut down the system!");
	cout << "usvmon: ERROR - Could not shut down the system!" << endl;
	exit(-1);
}



//--------------------------------------------------
void writeACStatusToLog(bool ac_new)
{
	static bool ac_old = true;

	if (ac_new != ac_old)
	{
		if (ac_new == true)
			writeToLog("ac-power restored");
		else
			writeToLog("ups lost ac-power!");
	}
	ac_old = ac_new;
}



//--------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
	int option_safe_counter = OPTION_SAFE_TIMEOUT;

	if (argc < 2 || argc > 3)
	{
		cout << "Error starting usvmon!" << endl << endl
		<< "Usage: usvmon device [-safe]" << endl << endl
		<< "device\tport the ups is connected to (e.g. /dev/ttyS0)" << endl << endl
		<< "-safe \tIf this option is set, the system is shut down" << endl
		<< "      \t5sec after powerfail. If not, the system is shut" << endl
		<< "      \twhen the ups is indicating low battery." << endl << endl
		<< "example: usvmon /dev/ttyS0" << endl << endl;
		return -1;
	}

	bool option_safe = false;
	if (argc == 3)
	{
		if (strcmp(argv[2], "-safe") == 0)
			option_safe = true;
	}

	USVObj* usv = new USVObj();
	usv->setDevice(argv[1]);
	if (usv->start() == false)
	{
		cout << "Error on opening device!" << endl << endl;
		delete usv;
		return -1;
	}

	char* temp = new char[256];
	if (option_safe == true)
		sprintf(temp, "service started with device %s (safe-mode)", argv[1]);
	else
		sprintf(temp, "service started with device %s", argv[1]);
	writeToLog(temp);
	delete[] temp;


	//main loop	
	while(true)
	{
		writeACStatusToLog(true);
		option_safe_counter = OPTION_SAFE_TIMEOUT; 
		sleep(5);
		while(usv->isOnBattery())
		{
			sleep(1);
			if (!usv->isOnBattery())  // filter out voltage fluctuations
			{                         //
				break;                 //
			}                         //
			writeACStatusToLog(false);
			if (option_safe == true)
			{
				option_safe_counter--;
				if (option_safe_counter <= 0)
					shutdown(usv);
			}
			if (usv->lowBattery() == true)
				shutdown(usv);
		}
	}
}

