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

#define PGCONN_STRING   "host=%s port=%d dbname=%s user=%s password=%s"
static char hexchars[] = "0123456789ABCDEF";

void chk_rtime_update_serv_status()
{
	servinfo_t *i, *servinfo_head;
    time_t now, past;

	g_shm_t *g_shm_ptr;
	void *shared_memory = (void *)0;
    char pgsql_conn_str[256];
    char pgsql_query[128];
    int connRes;
    int hcterm = 0;
    int toRead = 0;
    int sendSMSTerm;
	//int wget_waiting_cnt = 0;
	
	int time_cnt = 0;
	int shmid;
    int first_time = 0;
    //unsigned int g_CSMErrorCount = 0;

    /* Load conf file */
    hcterm = GetProfileInt("PARAM", "HEALTH_CHECK_TERM", 5);
    sendSMSTerm = (SENDSMSTERM * 60) / hcterm;
    toRead = GetProfileInt("PARAM", "TIME_OUT_READ", 5);

    /* get the global SHM start pointer if system V shm's value is dirty */
    shmid = shmget((key_t)6677, sizeof(g_shm_t), 0666|IPC_CREAT);
    if (shmid == -1)
    {
        WriteSHLog_safe("system V shmget failed\n");
        exit(EXIT_FAILURE);
    }
    shared_memory = shmat(shmid, (void *)0, 0);
    if (shared_memory == (void *)-1)
    {
        WriteSHLog_safe("system V shmat failed\n");
        exit(EXIT_FAILURE);
    }
    g_shm_ptr = (g_shm_t *)shared_memory;

    servinfo_head = malloc(sizeof(servinfo_t));
    past = now = time(NULL);

    //while (g_CSMErrorCount < ALLOWED_MAX_ERROR_COUNT)
    while (1)
    {
		if (g_shm_ptr->is_dirty == 1 || first_time == 0)
		{
			/***************** store SHM data to Heap *********************/
			servinfo_t *start_servinfo, *servinfo_ptr, *temp_servinfo;
			int shm_size = 0;

			Semop(check_semid, &waitop, 1);
            shm_size = get_sizeof_shm();
            /* get the global SHM start pointer */
            start_servinfo = servinfo_ptr = get_shm_start_pointer();
            servinfo_head = realloc(servinfo_head, shm_size+sizeof(servinfo_t));
			memset(servinfo_head, 0, shm_size+sizeof(servinfo_t));
            temp_servinfo = (servinfo_t *)servinfo_head;
            while (strcmp(servinfo_ptr->m_pszServName, END_OF_SHM))
            {
                if (servinfo_ptr->m_pszServName[0] == '\0')
                    break;
                /* tmporary servinfo_t */
				if (servinfo_ptr->m_Status == SERV_DEAD)
					servinfo_ptr->m_Status = SERV_ALIVE;
				servinfo_ptr->m_DeadCount = 0;
                memcpy(temp_servinfo, servinfo_ptr, sizeof(servinfo_t));
                temp_servinfo++;
                servinfo_ptr++;
                //setproctitle("child(check): first section running");
            }
            strncpy(temp_servinfo->m_pszServName, END_OF_SHM, 11);
            munmap((void *)start_servinfo, shm_size);
			Semop(check_semid, &postop, 1);
			/*****************************************************************/
	        /* update list from SHM */
			g_shm_ptr->is_dirty = 0;
            first_time = 1;
		}
        for (i = servinfo_head; strcmp(i->m_pszServName, END_OF_SHM); i++)
        {
			if (i->m_pszServName[0] == '\0')
				break;

			signal(SIGCHLD, SIG_DFL);
            connRes = 0;

			// Check for monitor_yn
			if (!strncmp(i->m_monitorYN, "N", 2))
				continue;

            if (i->m_ServType == 0) // RC
            {
				connRes = -1;
				pid_t pid;
				char *param[512];
                int k, ret, param_num, status = 0;

                if ((time_cnt % 4) != 0)
                    continue;

                param_num = rcacctFind(param, i->m_szIpString);

#ifdef __DEBUG__

                WriteSHLog_safe("RETURN RCCHECK QUERY : %s %s %s %s %s %s %s\n", "./rccheck"
						, DEFAULT_LOGFILE_PATH, i->m_szIpString, param[1], param[2], param[3], param[4]);

#endif

				//char rccheck_exec[1024];
                //sprintf(rccheck_exec, "%s %s %s %s %s %s %s\n", "./rccheck", DEFAULT_LOGFILE_PATH, param[0], param[1], param[2], param[3], param[4]);
				if ((pid = fork()) == 0) {
                        execl(DEFAULT_RC_CHECK_BIN,
                                DEFAULT_RC_CHECK_BIN,
                                DEFAULT_LOGFILE_PATH,
								i->m_szIpString,
                                param[1],
                                param[2],
                                param[3],
                                param[4], NULL);
                }
                ret = waitpid(pid, &status, 0);
                if (ret != pid) {
#ifdef __SETLOG__
					WriteSHLog_safe("!Check RC '%s' not checked (at first time, ignore it)\n", param[1]);
#endif
                } else {
                	if (status != 0) {
                    	connRes = -1;
                    } else {
                        connRes = 1;
                    }
                }
#ifdef __DEBUG__
                fprintf(stderr, "rccheck result %d: %d (%s)\n", pid, status, strerror(errno));
#endif
				//int retval=0;
                //if ((retval = system(rccheck_exec)) != 0)
                //    connRes = -1;
                //else
                //    connRes = 1;
 
				for (k = 0; k < param_num; k++)
					free(param[k]);
            }
            else if (i->m_ServType == 4)    // DATABASE
            {
				if ((time_cnt % 2) != 0)
	                continue;

				PGconn *conn;
				PGresult *result;
				connRes = -1;
				memset(pgsql_conn_str, 0, 1024);
			    snprintf(pgsql_conn_str, 1024, PGCONN_STRING, i->m_szIpString, i->m_sPort, "localdb", "syshost", "alfkzmf!?{}");

                conn = PQconnectdb(pgsql_conn_str);
                if (PQstatus(conn) == CONNECTION_BAD)
                {
#ifdef __SETLOG__
					WriteSHLog_safe("pgSQL Connection failed(%s), %s\n", PQerrorMessage(conn), i->m_szIpString);
#endif
                    connRes = -1;
                }
                else
                {
                    snprintf(pgsql_query, 128, "select now()");
                    result = PQexec(conn, pgsql_query);
                    if (!result)
                    {
#ifdef __SETLOG__
						WriteSHLog_safe("PQexec command failed, no error code(%s)\n", i->m_szIpString);
#endif
                        connRes = -1;
                    }
                    else
					{
                        connRes = 1;
						PQclear(result);
					}
                }
				// close postgreSQL
			    PQfinish(conn);
            }
            else if (i->m_ServType == 9)    // timed
            {
                if (timedCheck(i->m_szIpString, i->m_sPort, toRead) < 0)
                {
#ifdef __SETLOG__
					WriteSHLog_safe("--- Time Server seems dead ---\n");
#endif
                    connRes = -1;
                }
                else
                {
#ifdef __DEBUG__
                    fprintf(stderr, "--- Time Server is Alive!! ---\n");
#endif
                    connRes = 1;
                }
            }
            else if (i->m_ServType == 1) // STAT
            {
				// CHG 2014-10-29 huibong
				// - STAT check not used ( CCDB change and New SMS server apply)
				// - STAT check code disable

				/*
				char buf[1024];
				int result;
				connRes = -1;
                if ((time_cnt % 4) != 0)
                    continue;
				
				FILE *fp;
                connRes = -1;
                //sprintf(buf, "wget -T 2 -t 1 -q \"http://www.ktsh.co.kr:8080/stat-status/index.html?rcid=%s\" -O /root/%s", i->m_szIpString, i->m_szIpString);
                sprintf(buf, "wget -T 10 -t 2 -q \"http://www.ktsh.co.kr:8080/stat-status/index.html?rcid=%s\" -O /root/%s", i->m_szIpString, i->m_szIpString);
#ifdef __DEBUG__
                fprintf(stderr, "[WGET] %s\n", buf);
#endif
                result = system(buf);
				if (result == 0)	// wget success
				{
	                sprintf(buf, "/root/%s", i->m_szIpString);

					if ((fp = fopen(buf, "r")))
			        {
				        memset(buf, 0, 4);
					    fread(buf, 1, 3, fp);
#ifdef __DEBUG__
	                    fprintf(stderr, "%s\n", buf);
#endif
		                if (buf[0] == 'O') {
							wget_waiting_cnt = 0;
			                connRes = 1;
				        }
	                    else
		                {
#ifdef __SETLOG__
							WriteSHLog_safe("!STAT ERR MSG : %c %c %c\n", buf[0], buf[1], buf[2]);
#endif
	                    }
						fclose(fp);
			        }
					else
	                {
#ifdef __SETLOG__
						WriteSHLog_safe("!FILE OPEN ERR: %s : %s\n", buf, strerror(errno));
#endif
			        }
				}
				else	// wget fail : guess : timeout or remote server down
				{
					char msgBuffer[128];
					// Send to Web : reason : check remote server status
					wget_waiting_cnt++;
					
					WriteSHLog_safe("connRes: -1, seems to DEAD SQL_RELAY(211.39.132.217),Dead count : %d\n", wget_waiting_cnt);
					snprintf(msgBuffer, 128, "[SQL_RELAY] seems to DEAD Server(211.39.132.217)");

					if ((wget_waiting_cnt < 200) && ((wget_waiting_cnt % 40) == 0)) {
						SendToWebServ(msgBuffer, "SQLRELAY");
					}
					continue;
				}
				*/


            }
            else  // other Servers included RTS(FHS) or CACHE
            {
                signal(SIGALRM, HandleTimeOut);
                alarm(toRead);
                connRes = socket_open_with_timeout(toRead, i);
                alarm(0);
            }
			
			//setproctitle("child(check): second section running");
            processing_server_status(connRes, sendSMSTerm, i);
			//setproctitle("child(check): third section running");
        }
		time_cnt++;
#ifdef __DEBUG__
        fprintf(stderr , "report_thread_server_check_main@@@@@@@@@@@@@@\n");
#endif
        now = time(0);

        while((past + hcterm) > now)
        {
			SafeProcessRunSpinWait();
            now = time(0);
        }
        past = now;
		usleep(100);
    }
	if (shmdt(shared_memory) == -1)
    {
        WriteSHLog_safe("system V shmdt failed\n");
        exit(EXIT_FAILURE);
    }
    free(servinfo_head);
}

int increaseDeadCount(unsigned int _ServType, char *servname)
{
    int fFound = 0;
    int retCount = 0;

    servinfo_t *i, *servinfo;
	servinfo = get_shm_start_pointer();

	//setproctitle("child(check): increaseDeadCount section running");
    for (i = servinfo; strcmp(i->m_pszServName, END_OF_SHM); i++)
    {
		if (i->m_pszServName[0] == '\0')
	        break;
        if ((i->m_ServType == _ServType) && !strcmp(i->m_pszServName, servname)) {
#ifdef __SETLOG__ 
            //WriteSHLog_safe("SERVER IP[1] : %s\n", i->m_szIpString);
#endif
            fFound = 1;
            i->m_DeadCount++;
#ifdef __SETLOG__
            //WriteSHLog_safe("[%s]i->m_DeadCount = %d\n", i->m_pszServName, i->m_DeadCount);
#endif
            break;
        }
    }
    retCount = i->m_DeadCount;
	munmap((void *)servinfo, get_sizeof_shm());
    return (fFound)?retCount:-1;
}

int socket_open_with_timeout(int toRead, servinfo_t *servinfo)
{
    struct sockaddr_in serveraddr;
    struct timeval tv;
    int len;
    int twsfd;

    tv.tv_sec = toRead;
    tv.tv_usec = 0;

    if ( (twsfd = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
    {
        perror("error");
        close(twsfd);
        return -1;
    }

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(servinfo->m_szIpString);
    serveraddr.sin_port = htons(servinfo->m_sPort);

    setsockopt(twsfd, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(tv));
    setsockopt(twsfd, SOL_SOCKET, SO_SNDTIMEO, (struct timeval *)&tv, sizeof(tv));

    len = sizeof(serveraddr);

    if (connect(twsfd, (struct sockaddr *)&serveraddr, len) < 0)
    {
        if (errno == EINTR)
        {
            close(twsfd);
            return -1;
        }
        close(twsfd);
        return -1;
    }

    else
    {
        //WriteSHLog_safe("Connect Success\n");
        close(twsfd);
        return 1;
    }
    close(twsfd);
    return 1;
}

int timedCheck(char *ip, int port, int toRead)
{
    int res = 0;
    int nRead = -1;
    int nWrite = -1;
    int sSocket = -1;
    char szRcvBuf[32];
    time_sync_t ctime;
    time_sync_t *recv_time = NULL;
    struct sockaddr_in serv_addr;

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    serv_addr.sin_addr.s_addr = inet_addr(ip);
    ctime.sync_info = htonl(AuthPrime);
    sSocket = socket(AF_INET, SOCK_STREAM, 0);

#ifdef __DEBUG__
    fprintf(stderr, "------ Check Time Server [port] : %d, [ip] : %s\n", port, ip);
#endif
    signal(SIGALRM, HandleTimeOut);
    alarm(toRead);

    res = connect(sSocket, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (res < 0) {
#ifdef __DEBUG__
        fprintf(stderr, "connect :: %s\n", strerror(errno));
#endif		
        return -1;
    }
    alarm(0);

    nWrite = send(sSocket, (char *)&ctime, sizeof(ctime), 0);
    if (nWrite < 0) {
#ifdef __DEBUG__
        fprintf(stderr, "send :: %s\n", strerror(errno));
#endif
        shutdown(sSocket, SHUT_RDWR);
        close(sSocket);
        return -2;
    }

    nRead = recv(sSocket, szRcvBuf, sizeof(szRcvBuf), 0);
    if (nRead < 0) {
#ifdef __DEBUG__
        fprintf(stderr, "recv :: %s\n", strerror(errno));
#endif
        shutdown(sSocket, SHUT_RDWR);
        close(sSocket);
        return -3;
    }
    shutdown(sSocket, SHUT_RDWR);
    close(sSocket);
    recv_time = (time_sync_t *)&szRcvBuf;

#ifdef __DEBUG__
    fprintf(stderr, "TIME = %ld\n", ntohl(recv_time->sync_info));
#endif
    return 0;
}

#define MaxSizeOfBuffer 1024*4
void processing_server_status(int connRes, int sendSMSTerm, servinfo_t *i)
{
    struct tm *t;
    time_t timer;
    int nDeadCount;
    char szMsgBuffer[MaxSizeOfBuffer];

    timer = time(NULL);
    t = localtime(&timer);

    if (connRes < 0)
    {
		/* modified 2008_08_05 */
		nDeadCount = ++(i->m_DeadCount);

#ifdef __SETLOG__
		WriteSHLog_safe("[DEAD] connRes[%d] mServType[%d] m_ServName[%s] Dead Count[%d]\n"
				, connRes, i->m_ServType, i->m_pszServName, nDeadCount);
#endif

		if ((nDeadCount >= MAX_ALLOWED_DEAD_COUNT) && ((int)nDeadCount < (sendSMSTerm * (MAXSENDSMSCOUNT)))) {
            i->m_Status = SERV_DEAD;
			Semop(check_semid, &waitop, 1);
            UpdateStatus(i->m_ServType, SERV_DEAD, i->m_pszServName);
            snprintf(szMsgBuffer, MaxSizeOfBuffer, "[ %s ] DEAD :: %s ",  i->m_pszServName, timeToString(t));

            if (((int)nDeadCount < (sendSMSTerm * (MAXSENDSMSCOUNT))) &&
                    (nDeadCount == MAX_ALLOWED_DEAD_COUNT || (nDeadCount % sendSMSTerm) == 0))
            {
                SendToWebServ(szMsgBuffer, i->m_pszServName);
            }
			Semop(check_semid, &postop, 1);
        }
    }
    else
    {
        /* To Cleaning Up DEAD COUNT */
        if (i->m_Status == SERV_DEAD) 
		{
            i->m_Status = SERV_ALIVE;

#ifdef __SETLOG__
		WriteSHLog_safe("[BACK] connRes[%d] mServType[%d] m_ServName[%s]\n", connRes, i->m_ServType, i->m_pszServName);
#endif

		snprintf(szMsgBuffer, MaxSizeOfBuffer, "[ %s ] IS BACK :: %s ",  i->m_pszServName, timeToString(t));

			Semop(check_semid, &waitop, 1);
			UpdateStatus(i->m_ServType, SERV_ALIVE, i->m_pszServName);
            
			SendToWebServ(szMsgBuffer, i->m_pszServName);
			Semop(check_semid, &postop, 1);
        }
		i->m_DeadCount = 0;
    }
}

void UpdateStatus(unsigned int servtype, unsigned int status, char *servname)
{
    servinfo_t *i;
	servinfo_t *servinfo;

	servinfo = get_shm_start_pointer();
	//setproctitle("child(check): UpdateStatus section running");
    for (i = servinfo; strcmp(i->m_pszServName, END_OF_SHM); i++)
    {
		if (i == NULL || i->m_pszServName[0] == '\0')
	        break;
        if (!strcmp(i->m_pszServName, servname) && i->m_ServType == servtype)
        {
            i->m_Status = status;
			if (status == SERV_ALIVE)
				i->m_DeadCount = 0;
            break;
        }
	}
	munmap((void *)servinfo, get_sizeof_shm());
}

int rcacctFind(char **param, char *ipString)
{
	FILE *acct_ifp = NULL;
	char delim = ':';
	char buff[1024];
	int num = 0;
	int tnum = 0;

	acct_ifp = fopen(RCACCT_CONF_PATH, "r");
	if (acct_ifp == NULL)
	{
		fprintf(stderr, "cannot open rcacct file!\n");
		return 0;
	}

	while(fgets(buff, 1024, acct_ifp) != NULL)
	{
		char *temp[512];
		//extraction information
		tnum = devide(temp, buff, delim);
		
		if (!strcmp(ipString, temp[0]))
		{
			int i;
			for (i = 0; i < tnum; i++)
	            free(temp[i]);
					
			num = devide(param, buff, delim);
			break;
		}
		else
		{
			int j;
			for (j = 0; j < tnum; j++)
				free(temp[j]);
		}
	}
	if (acct_ifp)
		fclose(acct_ifp);
	return num;
}

//#define SMSServAddr           "211.39.132.250"
//#define SMSServerHost         "cc-gms.ktsh.co.kr"
#define SMSServAddr           "222.122.175.23"
#define SMSServerHost         "cs-gms.ktcdn.co.kr"
#define SMSServPort           "80"
#define MaxSizeOfTRxBuffer    1024

void SendToWebServ(const char *pszMesg, char *servName)
{
    char *PostDataEncode(const char *);

    char *strURI;
    char szSndBuffer[MaxSizeOfTRxBuffer];
    char szRcvBuffer[MaxSizeOfTRxBuffer];
	char str_replace[128];
	//char logString[1024];
	//char *rc_id_serv[128];
	
    //int fd, n, k, param_num;
    int fd, n;

    if (pszMesg == NULL)
    {
		WriteSHLog_safe("SendToWeb : Invalid Agument.\n");
    }
	else
	{
		WriteSHLog_safe("SendToWeb [%s][%s]\n", servName, pszMesg);
	}

    bzero(szSndBuffer, MaxSizeOfTRxBuffer);
    bzero(szRcvBuffer, MaxSizeOfTRxBuffer);

    strURI = PostDataEncode(pszMesg);

	// Extract RCID from m_pszServName
	//param_num = devide(rc_id_serv, servName, '_');

	/*
    snprintf(szSndBuffer, MaxSizeOfTRxBuffer, "GET /sms/send_sms.html?id=0udisk&pw=alfkzmf!?{}&msg=%s HTTP/1.0\n"\
    snprintf(szSndBuffer, MaxSizeOfTRxBuffer, "GET /sms/aaa.html?id=ADMIN&pw=roolu&msg=[test:roolu]%s HTTP/1.0\n"\
	*/

	if (!strcmp(servName, "SQLRELAY"))
		strncpy(str_replace, "ADMIN", 128);
	else
	{
		//strncpy(str_replace, rc_id_serv[0], 128);
		strncpy(str_replace, "ALL", 128);
	}

    snprintf(szSndBuffer, MaxSizeOfTRxBuffer, "GET /sms/send_sms.html?id=%s&msg=%s HTTP/1.0\n"\
            "Content-Type: text/plain\n"\
            "User-Agent: SeanZilla/1.0\n"\
            "Host: %s\n"\
            "Connection: close\n"\
            "Cache-Control: no-cache\n\n"
            , str_replace, strURI, SMSServerHost);

	WriteSHLog_safe("[SMS message] group[%s] message[%s]\n", str_replace, pszMesg);
	
    /* tcp connection, send, receive */
    fd = Tcp_connect(SMSServAddr, SMSServPort);
    /* send sndMessage to SMS WEB SERV */
    Send(fd, szSndBuffer, strlen(szSndBuffer), 0);
	usleep(100);

    /* receive result from new sh_statusd */
    if ( (n = Recv(fd, szRcvBuffer, strlen(szRcvBuffer), 0)) == 0)
    {
#if 0
		WriteSHLog_safe("[SMS] server : %s\n", strURI);
#endif
    }
    free(strURI);


	/*
	for (k = 0; k < param_num; k++)
	    free(rc_id_serv[k]);	
	*/

    Close(fd);
}

char *PostDataEncode(const char *pszCommand)
{
    int i = 0;
    char tmpBuffer[MaxSizeOfTRxBuffer];
    static char *strReturn;

    if(pszCommand == NULL)
    {
        return NULL;
    }

    while(*pszCommand != '\0')
    {
        if(*pszCommand == ' ')
        {
            tmpBuffer[i++] = '+';
            //*pszCommand++;
            pszCommand++;
        }
        else if((*pszCommand < '0' && *pszCommand != '-' && *pszCommand != '.' && *pszCommand !='%') ||
                (*pszCommand < 'A' && *pszCommand > '9' && *pszCommand !='%') ||
                (*pszCommand > 'Z' && *pszCommand < 'a' && *pszCommand != '_' && *pszCommand !='%') ||
                (*pszCommand > 'z' && *pszCommand !='%'))
        {
            tmpBuffer[i++] = '%';
            tmpBuffer[i++] = hexchars[(unsigned char)*pszCommand >> 4];
            tmpBuffer[i++] = hexchars[(unsigned char)*pszCommand & 15];
            //*pszCommand++;
            pszCommand++;
        }
        else if(*pszCommand == '%')
        {
            tmpBuffer[i++] = '%';
            tmpBuffer[i++] = '%';
            //*pszCommand++;
            pszCommand++;
        }
        else
        {
            tmpBuffer[i++] = *pszCommand++;
        }
    }
    tmpBuffer[i] = '\0';

    // allocate heap
    //strReturn = (char *)malloc(strlen(tmpBuffer));
    //memset(strReturn, 0, strlen(tmpBuffer));
    //strncpy(strReturn, tmpBuffer, strlen(tmpBuffer)+1);
   
	// huibong
	// memory overflow code modify
	strReturn = (char *)malloc(strlen(tmpBuffer)+1);
    memset(strReturn, 0x00, strlen(tmpBuffer)+1);
    strncpy(strReturn, tmpBuffer, strlen(tmpBuffer));
   
	return strReturn;
}

void SafeProcessRunSpinWait(void)
{
	struct timeval tv;
	tv.tv_sec = 1;
	tv.tv_usec = 0;
	select(0, NULL, NULL, NULL, &tv);
}

void HandleTimeOut(int signum)
{
	fprintf(stderr, "HandleTimeOut exceeded\n");
	WriteSHLog_safe("HandleTimeOut exceeded\n");
    return;
}
