#include "unp.h"
#include "unpipc.h"

#define PGCONN_STRING   "host=%s port=%s dbname=%s user=%s password=%s"
#define	DEBUG_MODE	1
#define RELEASE_MODE 0

pthread_mutex_t	vrcd_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t	report_lock = PTHREAD_MUTEX_INITIALIZER;
//const char a_ServType[NumOfServerType][MaxSizeOfServerType] = {"WWW", "RTS", "RC", "CCDB", "RCTS", "GTS", "RCDB", "GMS", "TIMED", "STAT"};
const char a_ServType[NumOfServerType][MaxSizeOfServerType] = {"RC", "STAT", "RTS", "RCTS", "RCDB", "WWW", "CCDB", "GTS", "GMS", "TIMED"};
FILE *shLogFile = NULL;
char logString[1024];

int main(int argc, char **argv)
{
	void    sig_int(int);
	void    sig_int_report(int);
	void	sig_chld(int);
	void    vrcd_thread_make(int);
	void    report_thread_make();

	/* declare Server List Structure */
	//servinfo_t *g_servinfo;
    servinfo_t *start_servinfo;

	MYSQL	mysql;
	MYSQL_RES *res;
	MYSQL_ROW row;

	FILE *shLogFile;
	PGconn *conn;
    PGresult *result;

	int		i;
	int		fd;
	int		pid, pid2, pid3;
	int		status=0, status1=0;
	int		mysql_port, fields;
	long	numRows;

	char	mysql_db[8];
	char	mysql_user[16];
	char	mysql_pass[16];

	char	HostName[512];
	char	sh_port[16];
	char	pg_addr[30];
	char	pg_port[10];
	char	pg_name[30];
	char	pg_acct[30];
	char	pg_pass[30];
    char	pgsql_query[1024];

	/* damonlizing... */
	//makedaemon(DEBUG_MODE);
	makedaemon(RELEASE_MODE);
	setproctitle("Parents(guard): initialize");

	/* sh log file open */
	shLogFile = fopen(makeLogString(), "a");
	if (!shLogFile)
	{
		fprintf(stderr, "Cannot open SH Log file!!\n");
		exit(EXIT_FAILURE);
	}

	/* initialize mysqlDB */
	mysql_init(&mysql);

	/* Load conf file */
	SetIniName(CFGFILE);

	GetProfile("PGSQL", "DATABASE_ADDR", pg_addr, "");
	GetProfile("PGSQL", "DATABASE_PORT", pg_port, "");
	GetProfile("PGSQL", "DATABASE_NAME", pg_name, "");
	GetProfile("PGSQL", "DATABASE_ACCT", pg_acct, "");
	GetProfile("PGSQL", "DATABASE_PW", pg_pass, "");

	GetProfile("MYSQL", "DATABASE_NAME", mysql_db, "CCDB");
	GetProfile("MYSQL", "DATABASE_ACCT", mysql_user, "root");
	GetProfile("MYSQL", "DATABASE_PW", mysql_pass, "alfkzmf!?{}");
	mysql_port = GetProfileInt("MYSQL", "DATABASE_PORT", 3306);

	snprintf(logString, 1024, "[PG]DATABASE_ADDR -->%s", pg_addr);
	WriteSHLog(logString, shLogFile);
	snprintf(logString, 1024, "[PG]DATABASE_PORT -->%s", pg_port);
	WriteSHLog(logString, shLogFile);
	snprintf(logString, 1024, "[PG]DATABASE_NAME -->%s", pg_name);
	WriteSHLog(logString, shLogFile);
	snprintf(logString, 1024, "[PG]DATABASE_ACCT -->%s", pg_acct);
	WriteSHLog(logString, shLogFile);
	snprintf(logString, 1024, "[PG]DATABASE_PW -->%s\n", pg_pass);
	WriteSHLog(logString, shLogFile);
	snprintf(logString, 1024, "[MY]DATABASE_NAME -->%s", mysql_db);
	WriteSHLog(logString, shLogFile);
	snprintf(logString, 1024, "[MY]DATABASE_ACCT -->%s", mysql_user);
	WriteSHLog(logString, shLogFile);
	snprintf(logString, 1024, "[MY]DATABASE_PW -->%s", mysql_pass);
	WriteSHLog(logString, shLogFile);
	snprintf(logString, 1024, "[MY}DATABASE_PORT -->%d", mysql_port);
	WriteSHLog(logString, shLogFile);
	/* connect mysqlDB */
	if (!mysql_real_connect(&mysql, NULL, mysql_user, mysql_pass, mysql_db, mysql_port, (char *)NULL, 0))
	{
		fprintf(stderr, "%s\n", mysql_error(&mysql));
		snprintf(logString, 1024, "%s", mysql_error(&mysql));
		WriteSHLog(logString, shLogFile);
		exit(EXIT_FAILURE);
	}
	snprintf(logString, 1024, "Query :: %s\n", MYQUERY);
	WriteSHLog(logString, shLogFile);

	/* start transaction */
	mysql_autocommit(&mysql, 0);
	if (mysql_query(&mysql, MYQUERY))
	{
		fprintf(stderr, "[error]%s\n", mysql_error(&mysql));
		snprintf(logString, 1024, "[error]%s", mysql_error(&mysql));
		WriteSHLog(logString, shLogFile);
		mysql_rollback(&mysql);
	}
	else
	{
		fprintf(stderr, "[MySQL]Server List Loading Success!!\n");
		snprintf(logString, 1024, "%s", "[MySQL]Server List Loading Success!!");
		WriteSHLog(logString, shLogFile);
		mysql_commit(&mysql);
	}
	res = mysql_store_result(&mysql);
    fields = mysql_num_fields(res);
	numRows = mysql_num_rows(res);
#ifdef __SETLOG__
	snprintf(logString, 1024, "numRows --> %ld, Sizeof structure servinfo_t : %d", numRows, sizeof(servinfo_t));
	WriteSHLog(logString, shLogFile);
#endif

	/* connect postgreSQL */
	snprintf(pgsql_query, 1024, PGCONN_STRING, pg_addr, pg_port, pg_name, pg_acct, pg_pass);
	conn = PQconnectdb(pgsql_query);
	//conn = PQconnectdb("host=211.38.137.34 port=5432 dbname=opendisk user=opendisk password=os1234!");
	
	if (PQstatus(conn) == CONNECTION_BAD)
	{
		fprintf(stderr, "pgSQL Connection failed, %s\n", PQerrorMessage(conn));
		snprintf(logString, 1024, "pgSQL Connection failed, %s", PQerrorMessage(conn));
		WriteSHLog(logString, shLogFile);
		if (conn != NULL)
			PQfinish(conn);
		exit(EXIT_FAILURE);
	}
	else
	{
		fprintf(stderr, "pgSQL Connected OK!\n");
		WriteSHLog("pgSQL Connected OK!", shLogFile);
	}
	result = PQexec(conn, "select now() ");
	if (!result)
	{
		fprintf(stderr, "PQexec command failed, no error code\n");
		WriteSHLog("PQexec command failed, no error code", shLogFile);
	}
	else
	{
		switch (PQresultStatus(result))
		{
			case PGRES_COMMAND_OK:
				snprintf(logString, 1024, "Command executed OK, %s rows affected", PQcmdTuples(result));
				WriteSHLog(logString, shLogFile);
				break;
			case PGRES_TUPLES_OK:
				snprintf(logString, 1024, "Query may have returned data. %d rows found", PQntuples(result));
				WriteSHLog(logString, shLogFile);
				break;
			default:
				snprintf(logString, 1024, "Command failed with code %s, error message %s", PQresStatus(PQresultStatus(result)), PQresultErrorMessage(result));
				WriteSHLog(logString, shLogFile);
				break;
		}
		PQclear(result);
	}
	
	//close postgreSQL
	PQfinish(conn);
	/***************************************************************/
	/********************* create shared memory ********************/
	/***************************************************************/
	if (numRows == 0)
		numRows = 1;

	shm_unlink(SHM_NAME);
	fd = Shm_open(SHM_NAME, O_RDWR | O_CREAT | O_EXCL | O_TRUNC, FILE_MODE);
	Ftruncate(fd, (sizeof(servinfo_t)*(numRows+1)));
	g_servinfo = Mmap(NULL, (sizeof(servinfo_t)*(numRows+1)), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	memset(g_servinfo, 0, sizeof(servinfo_t)*numRows);
	//Close(fd);
	/***************************************************************/
	/***************************************************************/
	/***************************************************************/
	int cnt = 1;
	start_servinfo = g_servinfo;
	while((row = mysql_fetch_row(res)))
	{
		addServerToList(g_servinfo, row);
		//g_servinfo->next = Mmap(NULL, (sizeof(servinfo_t)), PROT_READ | PROT_WRITE, MAP_SHARED, fd, (sizeof(servinfo_t))*cnt);
		//memset(g_servinfo->next, 0, sizeof(servinfo_t));
		g_servinfo++;
		cnt++;
	}

	strncpy(g_servinfo->m_pszServName, END_OF_SHM, 11);
	g_servinfo = start_servinfo;	
	Close(fd);
	
	/****************************************************************/

	/********************** create Semaphore mutex ********************/
	sem_unlink(SEM_NAME);
	sem_mutex = Sem_open(SEM_NAME, O_RDWR | O_CREAT | O_EXCL, FILE_MODE, 1);
	Sem_close(sem_mutex);
	/******************************************************************/
	
	/* close mysqlDB */
	mysql_free_result(res);
	mysql_close(&mysql);

	fflush(shLogFile);

	Signal(SIGCHLD, sig_chld);

	/* start fork to service */	
	if ( (pid = fork()) < 0 )
	{
		fprintf(stderr, "Cannot Fork(VRCD)\n");
		exit(EXIT_FAILURE);
	}
	else if ( pid == 0 )
	{
		int vrcd_stat;
		fprintf(stderr, "Start VRCD Child Process!!\n");
		setproctitle("child(vrcd): initialize");
		
		/* To do VRCD */
		GetProfile("PARAM", "HOSTNAME", HostName, "localhost");
		GetProfile("PARAM", "VRCD_PORT", sh_port, "22200");
		vrcd_threads = GetProfileInt("PARAM", "VRCD_THREAD_NUM", 4);
	    vrcdfd = Tcp_listen(HostName, sh_port, &vrcd_addrlen);
		vrcd_tptr = Calloc(vrcd_threads, sizeof(Thread));

		sem_mutex = Sem_open(SEM_NAME, 0);
		for (i = 0; i < vrcd_threads; i++)
			vrcd_thread_make(i);					// only main thread returns		

		setproctitle("child(vrcd): running");

		for (i = 0; i < vrcd_threads; i++)
		{
			Pthread_join(vrcd_tptr[i].thread_tid, (void *)&vrcd_stat);
			snprintf(logString, 1024, "vrcd thread join : %d", vrcd_stat);
			WriteSHLog(logString, shLogFile);
		}	
		//for ( ; ; )
		//	pause();
	}
	else
	{
		if ( (pid2=fork()) < 0 )
		{
			fprintf(stderr, "Cannot Fork(REPORT)\n");
			exit(EXIT_FAILURE);
		}
		else if (pid2 == 0)
		{
			//int report_stat;
			fprintf(stderr, "Start REPORT(update,send) Child Process!!\n");
			setproctitle("child(update,send): initialize");

			/* To do REPORT */
			report_threads = 2;
			report_tptr = Calloc(report_threads, sizeof(Thread));

			sem_mutex = Sem_open(SEM_NAME, 0);
			/* make thread(servList update thread(0), servList send thread(1)) */
			report_thread_make(2);			
			setproctitle("child(update,send): running");

			if ( (pid3=fork()) < 0 )
            {
                fprintf(stderr, "Cannot Fork(REPORT)\n");
                exit(EXIT_FAILURE);
            }
            else if (pid3 == 0)
            {
                // Checking Real Time Update Server Status
				fprintf(stderr, "Start REPORT Child(check) Process!!\n");
	            setproctitle("child(check): initialize");
                chk_rtime_update_serv_status();
            }
            else
            {
				wait(&status);	
            }
		}
		else
		{
			setproctitle("Parents(guard): waiting");		
			wait(&status1);
			/* to remove a mapping from the address space of the process */
			//munmap((void *)g_servinfo, get_sizeof_shm());
		}
		/* when  all processes have finished */
		/* to remove a mapping from the address space of the process */
        //munmap((void *)g_servinfo, get_sizeof_shm());	
	}
	
	/* close sh log file */
	fflush(shLogFile);
    if (shLogFile)
	    fclose(shLogFile);
	
	exit(EXIT_SUCCESS);
}

void addServerToList(servinfo_t *serv, MYSQL_ROW row)
{
	int j;
	strncpy(serv->m_pszServName, (char *)row[0], MAX_SRV_NAME);
	strncpy(serv->m_szIpString, (char *)row[1], MAX_IP_STRING);
	serv->m_sPort = atoi(row[2]);
	
	/* Server Type String to Integer */
	for (j=0; j < NumOfServerType; j++)
	{
		if(!strcmp((char *)row[3], a_ServType[j]))
			serv->m_ServType = j;
	}
	serv->m_StatChgTime = time(NULL);
	serv->m_Status = SERV_ALIVE;
	serv->m_DeadCount = 0;
	strncpy(serv->m_monitorYN, (char *)row[4], 2);
	serv->next = NULL;
}

void makedaemon(int mode) 
{ 
#ifdef SIGTTOU
	signal(SIGTTOU, SIG_IGN);
#endif
#ifdef SIGTTIN
	signal(SIGTTIN, SIG_IGN);
#endif
#ifdef SIGTSTP
	signal(SIGTSTP, SIG_IGN);
#endif
#ifdef SIGINT
	signal(SIGINT, SIG_IGN);
#endif
#ifdef SIGSTP
	signal(SIGSTP, SIG_IGN);
#endif

	pid_t pid; 
	if ((pid = fork()) < 0) 
		exit(0); 

	else if (pid != 0) 
		exit(0); 

	if (mode == 0) 
	{ 
		close(0); 
		close(1); 
	} 
	setsid(); 
}

/*
#define DISPLAY_SIGNAL_ACTION_ERROR( rv) LFile << "!sigaction : " << strerror(errno) << EL;  return rv
static void HandleCoreDump(int signum)
{
	::chdir(DEFAULT_LOGFILE_PATH);
	::signal(signum, SIG_DFL);
	::kill(getpid(), SIGKILL);
}

static void HandleChildHang(int signum)
{
	int status = 1;
	LogToFile LFile(g_logPath, g_logPrefix, 0);

	fprintf(stderr, "Signum: %d", signum);
	//cerr << "Signum:" << signum << EL;
	LFile << "!Worker Process " << (int)getpid() << " is dead. " << EL;
	while (waitpid(-1, &status, WNOHANG) > 0) g_pWProcManager->dcrNoProc();
}

static int SetSigHandle(int Option)
{
	struct sigaction sa;
	LogToFile LFile(g_logPath, g_logPrefix, 0);

	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;

#if defined(SA_ONESHOT)
	sa.sa_flags = SA_ONESHOT;
#elif defined(SA_RESETHAND)
	sa.sa_flags = SA_RESETHAND;
#endif
	if(Option == 1)
	{
		sa.sa_handler = HandleChildHang;
		if(sigaction(SIGCHLD, &sa, NULL) < 0)
		{
			DISPLAY_SIGNAL_ACTION_ERROR(-1);
		}
	}
	else
	{
		sa.sa_handler = HandleCoreDump;
	}

	if(sigaction(SIGSEGV, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-2);
	}
#ifdef SIGBUS
	if(sigaction(SIGBUS, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-3);
	}
#endif
#ifdef SIGABORT
	if(sigaction(SIGABORT, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-4);
	}
#endif
#ifdef SIGABRT
	if(sigaction(SIGABRT, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-5);
	}
#endif
#ifdef SIGILL
	if(sigaction(SIGILL, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-6);
	}
#endif
	if(sigaction(SIGHUP, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-7);
	}
	if(sigaction(SIGUSR1, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-8);
	}

#ifdef SIGINT
	if(sigaction(SIGINT, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-9);
	}
#endif
#ifdef SIGXCPU
	sa.sa_handler = SIG_DFL;
	if(sigaction(SIGXCPU, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-10);
	}
#endif
#ifdef SIGXPSZ
	sa.sa_handler = SIG_DFL;
	if(sigaction(SIGSEGV, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-11);
	}
#endif
#ifdef SIGPIPE
	sa.sa_handler = SIG_IGN;
	if(sigaction(SIGPIPE, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-12);
	}
#endif
#ifdef SIGSYS
	sa.sa_handler = SIG_IGN;
	if(sigaction(SIGPIPE, &sa, NULL) < 0)
	{
		DISPLAY_SIGNAL_ACTION_ERROR(-13);
	}
#endif
	return 1;
}
*/
void sig_chld(int signo)
{
	pid_t pid;
	int	  stat;

	while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
		fprintf(stderr, "child %d terminated\n", pid);
	return;
}

void sig_int(int signo)
{
	int		i;

	for (i = 0; i < vrcd_threads; i++)
		fprintf(stderr, "vrcd thread %d, %ld connections\n", i, vrcd_tptr[i].thread_count);
//	exit(0);
}

void sig_int_report(int signo)
{
	int     i;

	for (i = 0; i < report_threads; i++)
		fprintf(stderr, "report thread %d, %ld connections\n", i, report_tptr[i].thread_count);
//	exit(0);
}
