/*--------------------------------------------------------------------
 * FILE:
 *     main.c
 *
 * NOTE:
 *     This file is composed of the main function of pglb.
 *
 * Portions Copyright (c) 2003-2006, Atsushi Mitani
 *--------------------------------------------------------------------
 */
/*
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby
 * granted, provided that the above copyright notice appear in all
 * copies and that both that copyright notice and this permission
 * notice appear in supporting documentation, and that the name of the
 * author not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission. The author makes no representations about the
 * suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
*/
#include "postgres.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <sys/param.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/file.h>
#include <arpa/inet.h>

#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif

#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif

#include "replicate_com.h"
#include "pglb.h"



#define IPC_NMAXSEM (32)
/*--------------------------------------
 * GLOBAL VARIABLE DECLARATION
 *--------------------------------------
 */
/* for replicate_com.h */
ConfDataType * ConfData_Top = (ConfDataType *)NULL;
ConfDataType * ConfData_End = (ConfDataType *)NULL;
int SynSemID = -1;
int MapTableShmid = -1;
int LifeCheckStartShmid = -1;
char * LifeCheckStartFlag = NULL;
int LifeCheckTimeOut = 10;
FILE * StatusFp = (FILE *)NULL;
FILE * LogFp = (FILE *)NULL;
char * PGRStatusFileName = NULL;
char * PGRLogFileName = NULL;
int Log_Print = 0;
int Debug_Print = 0;

int Recv_Port_Number = 0;
int Recovery_Port_Number = 0;
uint16_t LifeCheck_Port_Number = 0;
int Use_Connection_Pool = 0;
int Max_Pool = 1;
int Connection_Life_Time = 0;
int Max_DB_Server = 0;
int MaxBackends = 0;
ClusterTbl * Cluster_Tbl = (ClusterTbl *)NULL;
int ClusterNum = 0;
int ClusterShmid = 0;
int ClusterSemid = 0;
ChildTbl * Child_Tbl = (ChildTbl *)NULL;
int ChildShmid = 0;
char * PGR_Data_Path = NULL;
char * PGR_Write_Path = NULL;
char * Backend_Socket_Dir = NULL;
FrontSocket Frontend_FD;
ClusterTbl * CurrentCluster = NULL;

extern char *optarg;

/*--------------------------------------
 * PROTOTYPE DECLARATION
 *--------------------------------------
 */
int init_pglb(char * path);
static void pglb_exit(int signal_args);
static void load_balance_main(void);
static void daemonize(void);
static void write_pid_file(void);
static void stop_pglb(void);
static int is_exist_pid_file(void);
static ClusterTbl * scan_cluster_by_pid(pid_t pid);
static void usage(void);
static void close_child(int signal_args);

void PGRrecreate_child(int signal_args);

/*--------------------------------------------------------------------
 * SYMBOL
 *    init_pglb()
 * NOTES
 *    Reading of the setup file
 *    and the initialization of the memory area.
 * ARGS
 *    char * path: path of the setup file (I)
 * RETURN
 *    OK: STATUS_OK
 *    NG: STATUS_ERROR
 *--------------------------------------------------------------------
 */
int
init_pglb(char * path)
{
	char * func = "init_pglb()";

	ConfDataType * conf;
	ClusterTbl cluster_tbl[MAX_DB_SERVER];
	int size = 0;
	int rec_no = 0;
	int i;
	int max_connect = 0;
	union semun sem_arg;
	char fname[256];

	/*
	 * read configuration file
	 */
	if (path == NULL)
	{
		path = ".";
	}
	if (PGR_Get_Conf_Data(path,PGLB_CONF_FILE) != STATUS_OK)
	{
		show_error("%s:PGR_Get_Conf_Data failed",func);
		return STATUS_ERROR;
	}
	
	/* cluster db status file open */
	if (PGRStatusFileName == NULL)
	{
		snprintf(fname,sizeof(fname),"%s/%s",PGR_Write_Path,PGLB_STATUS_FILE);
	}
	else
	{
		memcpy(fname,PGRStatusFileName,sizeof(fname));
	}
	StatusFp = fopen(fname, "a");
	if (StatusFp == NULL)
	{
		show_error("%s:open() %s file failed. (%s)",
				   func,fname, strerror(errno));
		exit(1);
	}
	/* pglb log file open */
	if (Log_Print)
	{
		if (PGRLogFileName == NULL)
		{
			snprintf(fname,sizeof(fname),"%s/%s",PGR_Write_Path,PGLB_LOG_FILE);
		}
		else
		{
			memcpy(fname,PGRLogFileName,sizeof(fname));
		}
		LogFp = fopen(fname,"a");
		if (LogFp == NULL)
		{
			show_error("%s:fopen failed: (%s)",func,strerror(errno));
			exit(1);
		}
#ifdef PRINT_DEBUG
		show_debug("%s:%s open ok",func,fname);
#endif			
	}

	Backend_Socket_Dir = malloc(128);
	if (Backend_Socket_Dir == NULL)
	{
		show_error("%s:malloc() failed. (%s)",func,strerror(errno));
		return STATUS_ERROR;
	}
	memset(Backend_Socket_Dir,0,128);
	/* set initiarize data */
	strcpy(Backend_Socket_Dir,"/tmp");
	Max_Pool = 1;
	Connection_Life_Time = 0;
	Use_Connection_Pool = 0;

	conf = ConfData_Top;
	while (conf != (ConfDataType *)NULL) 
	{
		/* get cluster db servers name */
		if (!strcmp(conf->table,CLUSTER_SERVER_TAG))
		{
			rec_no = conf->rec_no;
			if (!strcmp(conf->key,HOST_NAME_TAG))
			{
				memcpy(cluster_tbl[rec_no].hostName,conf->value,sizeof(cluster_tbl[rec_no].hostName));
				conf = (ConfDataType*)conf->next;
				continue;
			}
			if (!strcmp(conf->key,PORT_TAG))
			{
				cluster_tbl[rec_no].port = atoi(conf->value);
				conf = (ConfDataType*)conf->next;
				continue;
			}
			if (!strcmp(conf->key,MAX_CONNECT_TAG))
			{
				cluster_tbl[rec_no].max_connect = atoi(conf->value);
				conf = (ConfDataType*)conf->next;
				continue;
			}
		}
		else
		{
			/* get port number for receive querys */
			if (!strcmp(conf->key,RECV_PORT_TAG))
			{
				Recv_Port_Number = atoi(conf->value);
				conf = (ConfDataType*)conf->next;
				continue;
			}
			/* get port number for recovery session */
			if (!strcmp(conf->key,RECOVERY_PORT_TAG))
			{
				Recovery_Port_Number = atoi(conf->value);
				conf = (ConfDataType*)conf->next;
				continue;
			}
			/*
			if (!STRCMP(conf->key,LIFECHECK_PORT_TAG))
			{
				LifeCheck_Port_Number = atoi(conf->value);
				conf = (ConfDataType*)conf->next;
				continue;
			}
			if (!STRCMP(conf->key,STATUS_LOG_FILE_TAG))
			{
				PGRStatusFileName = strdup(conf->value);
				conf = (ConfDataType*)conf->next;
				continue;
			}
			if (!STRCMP(conf->key,ERROR_LOG_FILE_TAG))
			{
				PGRLogFileName = strdup(conf->value);
				conf = (ConfDataType*)conf->next;
				continue;
			}
			*/
			if (!strcmp(conf->key,MAX_CLUSTER_TAG))
			{
				Max_DB_Server = atoi(conf->value);
				conf = (ConfDataType*)conf->next;
				continue;
			}
			if (!strcmp(conf->key,USE_CONNECTION_POOL_TAG))
			{
				if (!strcmp(conf->value,"yes"))
				{
					Use_Connection_Pool = 1;
				}
				conf = (ConfDataType*)conf->next;
				continue;
			}
			if (!strcmp(conf->key,MAX_POOL_TAG))
			{
				Max_Pool = atoi(conf->value);
				if (Max_Pool < 0)
					Max_Pool = 1;
				conf = (ConfDataType*)conf->next;
				continue;
			}
			if (!strcmp(conf->key,CONNECTION_LIFE_TIME))
			{
				Connection_Life_Time = atoi(conf->value);
				if (Connection_Life_Time < 0)
					Connection_Life_Time = 0;
				conf = (ConfDataType*)conf->next;
				continue;
			}
			if (!strcmp(conf->key,BACKEND_SOCKET_DIR_TAG))
			{
				strncpy(Backend_Socket_Dir,conf->value,128);
				conf = (ConfDataType*)conf->next;
				continue;
			}
		}
		conf = (ConfDataType*)conf->next;
	}
	if (Max_DB_Server <= 0)
	{
		show_error("%s:Max_DB_Server is wrong value. %s/%s file should be broken",func, path, PGLB_CONF_FILE);
		exit(1);
	}
	/* shared memory allocation for cluster table */
	size = sizeof(ClusterTbl) * Max_DB_Server;

	ClusterShmid = shmget(IPC_PRIVATE,size,IPC_CREAT | IPC_EXCL | 0600);
	if (ClusterShmid < 0)
	{
		show_error("%s:ClusterShm shmget() failed. (%s)", func,strerror(errno));
		return STATUS_ERROR;
	}
	Cluster_Tbl = (ClusterTbl *)shmat(ClusterShmid,0,0);
	if (Cluster_Tbl == (ClusterTbl *)-1)
	{
		show_error("%s:shmat() failed. (%s)", func,strerror(errno));
		return STATUS_ERROR;
	}
	if ((ClusterSemid = semget(IPC_PRIVATE,MAX_DB_SERVER+1,IPC_CREAT | IPC_EXCL | 0600)) < 0)
	{
		show_error("%s:semget() failed. (%s)",func,strerror(errno));
		return STATUS_ERROR;
	}
	for ( i = 0 ; i <= MAX_DB_SERVER ; i ++)
	{
		semctl(ClusterSemid, i, GETVAL, sem_arg);
		sem_arg.val = 1;
		semctl(ClusterSemid, i, SETVAL, sem_arg);
	}
	ClusterNum = 0;
	/* set cluster db server name into cluster db server table */
	for ( i = 0 ; i < Max_DB_Server ; i ++)
	{
		(Cluster_Tbl + i)->rec_no = i;
	}
	(Cluster_Tbl + i)->useFlag = TBL_END;
	max_connect = 0;
	for ( i = 0 ; i <= rec_no ; i ++)
	{
		cluster_tbl[i].use_num = 0;
		cluster_tbl[i].rate = 0;
		if (cluster_tbl[i].max_connect < 0)
		{
			cluster_tbl[i].max_connect = 0;
		}
		if (max_connect < cluster_tbl[i].max_connect)
		{
			max_connect = cluster_tbl[i].max_connect;
		}
		PGRadd_cluster_tbl(&cluster_tbl[i]);
	}

	/* shared memory allocation for children table */
	size = sizeof(ChildTbl) * (Max_DB_Server + 1) * max_connect * Max_Pool;
#ifdef PRINT_DEBUG
	show_debug("%s:Child_Tbl size is[%d]",func,size);
#endif			

	ChildShmid = shmget(IPC_PRIVATE,size,IPC_CREAT | IPC_EXCL | 0600);
	if (ChildShmid < 0)
	{
		show_error("%s:ChildShm shmget() failed. (%s)",func, strerror(errno));
		return STATUS_ERROR;
	}
	Child_Tbl = (ChildTbl *)shmat(ChildShmid,0,0);
	if (Child_Tbl == (ChildTbl *)-1)
	{
		show_error("%s:shmat() failed. (%s)", func,strerror(errno));
		return STATUS_ERROR;
	}
	memset(Child_Tbl, 0, size);
	(Child_Tbl + ( Max_DB_Server * max_connect * Max_Pool) -1)->useFlag = TBL_END;

	PGR_Free_Conf_Data();

	return STATUS_OK;
}

/*--------------------------------------------------------------------
 * SYMBOL
 *    pglb_exit()
 * NOTES
 *    Closing of pglb process
 * ARGS
 *    int signal_args: signal number (I)
 * RETURN
 *    none
 *--------------------------------------------------------------------
 */
static void
pglb_exit(int signal_args)
{
	char fname[256];
	int rtn;
	
	Child_Tbl->useFlag = TBL_END;
	signal(SIGCHLD,SIG_IGN);
	signal(signal_args,SIG_IGN);
	kill (0,signal_args);
	while (wait(NULL) > 0 )
		;

	if (ClusterShmid > 0)
	{
		rtn = shmdt((char *)Cluster_Tbl);
		shmctl(ClusterShmid,IPC_RMID,(struct shmid_ds *)NULL);
		ClusterShmid = 0;
		Cluster_Tbl = NULL;
	}
	if (ChildShmid > 0)
	{
		rtn = shmdt((char *)Child_Tbl);
		shmctl(ChildShmid,IPC_RMID,(struct shmid_ds *)NULL);
		ChildShmid = 0;
		Child_Tbl = NULL;
	}
	if (ClusterSemid > 0)
	{
		semctl(ClusterSemid, 0, IPC_RMID);
	}
	snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path, PGLB_PID_FILE);
	unlink(fname);
	
	if (StatusFp != NULL)
	{
		fflush(StatusFp);
		fclose(StatusFp);
	}
	if (LogFp != NULL)
	{
		fflush(LogFp);
		fclose(LogFp);
	}
	if (Frontend_FD.unix_fd != 0)
	{
		close(Frontend_FD.unix_fd);
		snprintf(fname, sizeof(fname), "%s/.s.PGSQL.%d", Backend_Socket_Dir,Recv_Port_Number);
		unlink(fname);
	}
	if (Frontend_FD.inet_fd != 0)
	{
		close(Frontend_FD.inet_fd);
	}
	/*
	PGRsyn_quit();
	*/
	snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path, PGLB_PID_FILE);
	unlink(fname);

	exit(0);
}

/*--------------------------------------------------------------------
 * SYMBOL
 *    load_balance_main()
 * NOTES
 *    This is a main module of load balance function
 * ARGS
 *    void
 * RETURN
 *    none
 *--------------------------------------------------------------------
 */
static void
load_balance_main(void)
{
	char * func = "load_balance_main()";
	int status;
	int rtn;
	int count = 0;

	Frontend_FD.unix_fd = PGRcreate_unix_domain_socket(Backend_Socket_Dir, Recv_Port_Number);
	if (Frontend_FD.unix_fd < 0)
	{
		show_error("%s:PGRcreate_unix_domain_socket failed",func);
		pglb_exit(SIGTERM);
	}
	Frontend_FD.inet_fd = PGRcreate_recv_socket("", Recv_Port_Number);
	if (Frontend_FD.inet_fd < 0)
	{
		show_error("%s:PGRcreate_recv_socket failed",func);
		pglb_exit(SIGTERM);
	}
	if (Use_Connection_Pool)
	{
		signal(SIGCHLD,PGRrecreate_child);
		rtn = PGRpre_fork_children(Cluster_Tbl);
		if (rtn != STATUS_OK)
		{
			show_error("%s:PGRpre_fork_children failed",func);
			pglb_exit(SIGTERM);
		}
	}
	
	for (;;)
	{
		fd_set	  rmask;
		struct timeval timeout;

		timeout.tv_sec = 60;
		timeout.tv_usec = 0;

		/*
		 * Wait for something to happen.
		 */
		FD_ZERO(&rmask);
		FD_SET(Frontend_FD.unix_fd,&rmask);
		if(Frontend_FD.inet_fd)
			FD_SET(Frontend_FD.inet_fd,&rmask);
		rtn = select(Max(Frontend_FD.unix_fd, Frontend_FD.inet_fd) + 1, &rmask, (fd_set *)NULL, (fd_set *)NULL, &timeout);
		if( rtn > 0)
		{
		  if(PGRis_cluster_alive() == STATUS_ERROR) {
		    show_error("%s:all clusters were dead.",func);				    PGRreturn_no_connection_error();        
		    count=0;
		  }
		  else 
		    {
			if (Use_Connection_Pool)
			{
				status = PGRload_balance_with_pool();
			}
			else
			{
				status = PGRload_balance();
			}
			if (status != STATUS_OK)
			{
				show_error("%s:load balance process failed",func);
				if ( count > PGLB_CONNECT_RETRY_TIME)
				{
					show_error("%s:no cluster available",func);
					PGRreturn_connection_full_error();
					count = 0;
				}
				count ++;
			}
			else
			{
				count = 0;
			}
		    }
		}
	}
}

/*--------------------------------------------------------------------
 * SYMBOL
 *    daemonize()
 * NOTES
 *    Daemonize this process
 * ARGS
 *    void
 * RETURN
 *    none
 *--------------------------------------------------------------------
 */
static void 
daemonize(void)
{
	char *  func = "daemonize()";
	int		i;
	pid_t		pid;

	pid = fork();
	if (pid == (pid_t) -1)
	{
		show_error("%s:fork() failed. (%s)",func, strerror(errno));
		exit(1);
		return;					/* not reached */
	}
	else if (pid > 0)
	{			/* parent */
		exit(0);
	}

#ifdef HAVE_SETSID
	if (setsid() < 0)
	{
		show_error("%s:setsid() failed. (%s)", func,strerror(errno));
		exit(1);
	}
#endif

	i = open("/dev/null", O_RDWR);
	dup2(i, 0);
	dup2(i, 1);
	dup2(i, 2);
	close(i);
}


/*--------------------------------------------------------------------
 * SYMBOL
 *    write_pid_file()
 * NOTES
 *    The process ID is written in the file.
 *    This process ID is used when finish pglb.
 * ARGS
 *    void
 * RETURN
 *    none
 *--------------------------------------------------------------------
 */
static void 
write_pid_file(void)
{
	char * func = "write_pid_file()";
	FILE *fd;
	char fname[256];
	char pidbuf[128];

	snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path, PGLB_PID_FILE);
	fd = fopen(fname, "w");
	if (!fd)
	{
		show_error("%s:open() %s file failed. (%s)",
				   func,fname, strerror(errno));
		exit(1);
	}
	snprintf(pidbuf, sizeof(pidbuf), "%d", getpid());
	fwrite(pidbuf, strlen(pidbuf), 1, fd);
	if (fclose(fd))
	{
		show_error("%s:fwrite() %s file failed. (%s)",
				   func,fname, strerror(errno));
		exit(1);
	}
}


/*--------------------------------------------------------------------
 * SYMBOL
 *    stop_pglb()
 * NOTES
 *    Stop the pglb process
 * ARGS
 *    void
 * RETURN
 *    none
 *--------------------------------------------------------------------
 */
static void 
stop_pglb(void)
{
	char * func = "stop_pglb()";
	FILE *fd;
	char fname[256];
	char pidbuf[128];
	pid_t pid;

	if (PGR_Write_Path == NULL)
	{
		PGR_Write_Path = ".";
	}
	snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path, PGLB_PID_FILE);
	fd = fopen(fname, "r");
	if (!fd)
	{
		show_error("%s:open() %s file failed. (%s)",
				   func,fname, strerror(errno));
		exit(1);
	}
	memset(pidbuf,0,sizeof(pidbuf));
	fread(pidbuf, sizeof(pidbuf), 1, fd);
	fclose(fd);
	pid = atoi(pidbuf);
	if (kill (pid,SIGTERM) == -1)
	{
		show_error("%s:could not stop pid: %d (%s)",func,pid,strerror(errno));
		exit(1);
	}
}


/*--------------------------------------------------------------------
 * SYMBOL
 *    is_exist_pid_file()
 * NOTES
 *    Check existence of pid file.
 * ARGS
 *    void
 * RETURN
 *    1: the pid file is exist
 *    0: the pid file is not exist
 *--------------------------------------------------------------------
 */
static int
is_exist_pid_file(void)
{
	char fname[256];
	struct stat buf;

	snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path, PGLB_PID_FILE);
	if (stat(fname,&buf) == 0)
	{
		/* pid file is exist */
		return 1;
	}
	else
	{
		/* pid file is not exist */
		return 0;
	}
}


/*--------------------------------------------------------------------
 * SYMBOL
 *    PGRrecreate_child()
 * NOTES
 *    create the child process again which it hunged up
 * ARGS
 *    int signal_args: signal number (expecting the SIGCHLD)
 * RETURN
 *    none
 *--------------------------------------------------------------------
 */
void
PGRrecreate_child(int signal_args)
{
	pid_t pid = 0;
	int status;
	ClusterTbl * cluster_p;

ReWait:

	errno = 0;
#ifdef HAVE_WAITPID
	while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
	{
#else
	while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
	{
#endif
		cluster_p = scan_cluster_by_pid(pid);
		pid = PGRcreate_child(cluster_p);	
	}
	if ((pid < 0) && (errno == EINTR))
		goto ReWait;
}

/*--------------------------------------------------------------------
 * SYMBOL
 *    close_child()
 * NOTES
 *    Hung up child process 
 * ARGS
 *    int signal_args: signal number (expecting the SIGUSR2)
 * RETURN
 *    none
 *--------------------------------------------------------------------
 */
static void
close_child(int signal_args)
{
	char * func = "close_child()";
	ChildTbl * child;
	ClusterTbl * cluster;
	int rec_no = -1;

	if (( Cluster_Tbl == NULL) || (Child_Tbl == NULL))
	{
		show_error("%s:Cluster_Tbl or Child_Tbl is not initialize",func);
		return ;
	}
	cluster = Cluster_Tbl;
	while(cluster->useFlag != TBL_END)
	{
		if (cluster->useFlag == TBL_ERROR_NOTICE) 
		{
			rec_no = cluster->rec_no;
			PGRset_status_on_cluster_tbl(TBL_ERROR,cluster);
			break;
		}
		cluster++;
	}
	if (rec_no < 0)
	{
		return;
	}
	child = Child_Tbl;
	while(child->useFlag != TBL_END)
	{
		if (child->rec_no == rec_no) 
		{
			if (kill (child->pid,SIGTERM) == -1)
			{
				show_error("%s:could not stop pid: %d (%s)",func,child->pid,strerror(errno));
				return;
			}
			PGRchild_wait(signal_args);
			child->useFlag = DATA_FREE;
		}
		child++;
	}
	signal(SIGUSR2, close_child);
}

/*--------------------------------------------------------------------
 * SYMBOL
 *    scan_cluster_by_pid()
 * NOTES
 *    get cluster server record from child process id
 * ARGS
 *    pid_t pid: child process id (I)
 * RETURN
 *    OK: pointer of cluster table
 *    NG: NULL
 *--------------------------------------------------------------------
 */
static ClusterTbl *
scan_cluster_by_pid(pid_t pid)
{
	char * func = "scan_cluster_by_pid()";
	ChildTbl * child_p;
	ClusterTbl * cluster_p;
	int cnt;

	child_p = Child_Tbl;
	if (child_p == NULL)
	{
		show_error("%s:Child Table is not initialize",func);
		return NULL;
	}
	cluster_p = Cluster_Tbl;
	if (cluster_p == NULL)
	{
		show_error("%s:Cluster Table is not initialize",func);
		return NULL;
	}
	
	while (child_p->useFlag != TBL_END)
	{
		if (child_p->pid == pid)
		{
			break;
		}
		child_p++;
	}
	if (child_p->useFlag == TBL_END)
	{
		show_error("%s:pid:%d not found in child table",func,pid);
		return NULL;
	}

	cnt = 0;
	while ((cluster_p->useFlag != TBL_END) && (cnt < ClusterNum))
	{
		if (cluster_p->rec_no == child_p->rec_no)
		{
			return cluster_p;
		}
		cluster_p++;
		cnt ++;
	}
	return NULL;
}

/*--------------------------------------------------------------------
 * SYMBOL
 *    usage()
 * NOTES
 *    show usage of pglb
 * ARGS
 *    void
 * RETURN
 *    none
 *--------------------------------------------------------------------
 */
static void
usage(void)
{
	char * path;

	path = getenv("PGDATA");
	if (path == NULL)
		path = ".";
	fprintf(stderr,"pglb version [%s]\n",PGLB_VERSION);
	fprintf(stderr,"A load balancer for PostgreSQL\n\n");
	fprintf(stderr,"usage: pglb [-D path_of_config_file] [-W path_of_work_files] [-n][-v][-h][stop | restart]\n");
	fprintf(stderr,"    config file default path: %s/%s\n",path, PGLB_CONF_FILE);
	fprintf(stderr,"    -l: print error logs in the log file.\n");
	fprintf(stderr,"    -n: don't run in daemon mode.\n");
	fprintf(stderr,"    -v: debug mode. need '-n' flag\n");
	fprintf(stderr,"    -h: print this help\n");
	fprintf(stderr,"    stop: stop pglb\n");
	fprintf(stderr,"    restart: restart pglb\n");
}

/*--------------------------------------------------------------------
 * SYMBOL
 *    main()
 * NOTES
 *    main module of pglb
 * ARGS
 *    int argc: number of parameter
 *    char ** argv: value of parameter
 * RETURN
 *    none
 *--------------------------------------------------------------------
 */
int
main(int argc, char ** argv)
{
	int opt = 0;
	pid_t pid,pgid;
	char * r_path = NULL;
	char * w_path = NULL;
	int detach = 1;

	signal(SIGHUP, pglb_exit);
	signal(SIGINT, pglb_exit);	
	signal(SIGQUIT, pglb_exit);
	signal(SIGTERM, pglb_exit);
	signal(SIGALRM, SIG_IGN); /* ignored */
	signal(SIGPIPE, SIG_IGN); /* ignored */
	signal(SIGTTIN, SIG_IGN); /* ignored */
	signal(SIGTTOU, SIG_IGN); /* ignored */
	signal(SIGCHLD,PGRchild_wait);
	signal(SIGUSR1, SIG_IGN); /* ignored */
	signal(SIGUSR2, close_child); /* close child process */
	r_path = getenv("PGDATA");
	if (r_path == NULL)
		r_path = ".";

	while ((opt = getopt(argc, argv, "D:W:lvnh")) != -1)
	{
		switch (opt)
		{
			case 'D':
				if (!optarg)
				{
					usage();
					exit(1);
				}
				r_path = optarg;
				break;
			case 'W':
				if (!optarg)
				{
					usage();
					exit(1);
				}
				w_path = optarg;
				break;
			case 'l':
				Log_Print = 1;
				break;
			case 'v':
				Debug_Print = 1;
				break;
			case 'n':
				detach = 0;
				break;
			case 'h':
				usage();
				exit(0);
				break;
			default:
				usage();
				exit(1);
		}
	}
	PGR_Data_Path = r_path;
	if (w_path == NULL)
	{
		PGR_Write_Path = PGR_Data_Path;
	}
	else
	{
		PGR_Write_Path = w_path;
	}

	if (optind == (argc-1) &&
			((!strcmp(argv[optind],"stop")) ||
			(!strcmp(argv[optind],"restart"))))
	{
		stop_pglb();
		if (!strcmp(argv[optind],"stop"))
		{
			exit(0);
		}
	}
	else if (optind == argc)
	{
		if (is_exist_pid_file())
		{
			fprintf(stderr,"pid file %s/%s found. is another pglb running?", PGR_Write_Path, PGLB_PID_FILE);
			exit(1);
		}
	}
	else if (optind < argc)
	{
		usage();
		exit(1);
	}

	if (detach)
	{
		daemonize();
	}
	write_pid_file();
	
	if (init_pglb(PGR_Data_Path) != STATUS_OK)
	{
		exit(0);
	}

	pgid = getpgid((pid_t)0);
	pid = fork();
	if (pid < 0)
	{
		pglb_exit(0);
	}
	if (pid == 0)
	{
		setpgid((pid_t)0,pgid);
		PGRrecovery_main();
		exit(0);
	}
	else
	{
		load_balance_main();
	}
	pglb_exit(0);
	return STATUS_OK;
}

