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

   mod_dav_pgsql (DASL) for apache 2.X
   CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
   CopyRight(C) 2006 Netomi Inc. All Rights reserved.
   Author : elenoa lazyfake (elenoa@solutionbox.co.kr)

   $Id: share.c,v 1.8 2007/02/22 08:22:13 elenoa Exp $

   Redistribution and use in source and binary forms, with or with out
   modification, are not permitted in outside of SolutionBox Inc.

 ****************************************************************************/
#include <unistd.h>
#include <ctype.h>
#include <math.h>

#include <httpd.h>
#include <http_config.h>
#include <http_protocol.h>
#include <http_log.h>
#include <http_core.h>      /* for ap_construct_url */
#include <http_request.h>

#include <apr_strings.h>
#include <apr_signal.h>

#include "dav_repos.h"
#include "dbms.h"
#include "util.h"
#include "share_common.h"
#include "share_fhs_stat.h"

/* for fhs */
static struct shm_mgnt fhs_mgnt = {
	NULL, NULL, SHM_FHS, SHM_FHS_LOCK, 0, 0
};

static struct shm_mgnt fhscnt_mgnt = {
	NULL, NULL, SHM_FHSCNT, SHM_FHSCNT_LOCK, 0, 0
};

struct fhs_stat *g_fhs = NULL;
int *g_fhscnt = NULL;

static int maxfhs = 0;

int dav_initialize_shared_fhs_stat(dav_repos_server_conf *dsc, server_rec *s, apr_pool_t *p)
{
	if (!dsc->max_fhs || (maxfhs = atoi(dsc->max_fhs)) <= 0) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "OPENDAV: initialize_shared_memory: "
			"MaxFHS <= 0 (%d)", maxfhs);
		return !OK;
	}

	/* fhs management: calculate shared memory size from max_fhs value */
	fhs_mgnt.size = (sizeof(struct fhs_stat) * maxfhs);
	g_fhs = (struct fhs_stat *)dav_shared_get_new_memory(&fhs_mgnt, s, p, false);
	if (!g_fhs) return !OK;

	fhscnt_mgnt.size = (sizeof(int));
	g_fhscnt = (int *)dav_shared_get_new_memory(&fhscnt_mgnt, s, p, false);
	if (!g_fhscnt) return !OK;

	return OK;
}

void dav_cleanup_shared_fhs_stat()
{
	if (fhs_mgnt.shm) {
		apr_shm_detach(fhs_mgnt.shm);
	}

	if (fhs_mgnt.lock) {
		apr_global_mutex_destroy(fhs_mgnt.lock);
	}
}

int dav_shared_du_is_alive(const char *my_domain)
{
	int i = 0;
	if (!g_fhs) return 0;

	for (i = 0; i < maxfhs; i++)
	{
		if (*((g_fhs + i)->szHostname) == '\0')
			break;
		
		if (!strcmp((g_fhs + i)->szHostname, my_domain))
		{
			int r = 0;
			switch ((g_fhs+i)->nActionCode)
			{			
			case FHS_OK:
			case READ_ONLY:
				r = 1;
				break;
			case DELETED:
			case MANUAL_STOP:
			case SERVICE_STOP:
				r = 0;
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_du_is_alive:: FHS[%s] => Action Code [%d] Service Disabled", 
						(g_fhs + i)->szHostname, (g_fhs + i)->nActionCode);
				break;
			default:
				r = -1;
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_du_is_alive:: FHS[%s] => Action Code [%d] Unknown",
					(g_fhs + i)->szHostname, (g_fhs + i)->nActionCode);
				break;
			}

			return r;
		}
	}

	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_du_is_alive:: Not found alive FHS.[%s]", my_domain);

	return 0;
}


char *dav_shared_du_put_target(apr_pool_t *p, const char *my_domain)
{
	struct fhs_stat *du = NULL;
	char *domain = NULL;

	if (!g_fhs)
		return NULL;

	if (!g_fhscnt)
		return NULL;

	int fhscnt = *g_fhscnt;
	
	if (fhscnt <= 0)
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_du_put_target:: Empty FHS.[%s]", my_domain);
		return NULL;
	}

	// 랜덤 선택
	unsigned int selfhs = 0;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		apr_generate_random_bytes((unsigned char *)&selfhs, sizeof(unsigned int));
		selfhs = selfhs % fhscnt;

		if ((g_fhs + selfhs)->nActionCode != FHS_OK)
			continue;

		du = (g_fhs + selfhs);
		break;
	}

	if (du == NULL)
	{
		int j = 0;
		
		// 램덤으로 fhs 선정되지 않았을 경우 
		// 특정 fhs 부터 전체 스캔, 업로드 가능한 서버로 
		// 처음 발견된 fhs를 업로드 서버로 선정함

		// 2017-03-08 BUG huibong
		// #30515 - 업로드 대상 선정시 1차 랜덤 선택에서 선택이 실패한 경우..
		//        - 2차 순차 검색에서 선정해야 하는데 루프 버그로 선택되지 않는 상황 발생. 이를 수정 처리함.

		
		// 마지막 랜덤 선택한 FHS 가 유효하지 않으므로 다시 random 선택한 후 작업을 진행한다.
		apr_generate_random_bytes((unsigned char *)&selfhs, sizeof(unsigned int));
		selfhs = selfhs % fhscnt;

		for (j = selfhs; j < fhscnt; ++j)
		{
			if ((g_fhs + j)->nActionCode != FHS_OK)
			{
				//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_du_put_target:: 2nd check FHS readonly. [%s]", (g_fhs+j)->szHostname );

				if( selfhs > 0 && (j+1) >= fhscnt )
				{
					// 최초 0번 장비부터 시작하지 않고 - 즉 중간 장비부터 시작해서
					// 마지막까지 검색했는데도 없는 경우...
					// 0번 장비부터 다시 검색하도록 조정 처리.

					j = -1;          // 0번 장비부터 다시 시작하도록 index 조정.
					fhscnt = selfhs; // 루프 초기에 설정한 장비까지 검색 범위 조정.
					selfhs = 0;      // 본 로직이 다시 수행되지 않도록 flag 설정 처리.
			
					//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_du_put_target:: 2nd select index reset" );
				}

				continue;
			}

			du = (g_fhs + j);

			//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_du_put_target:: 2nd select FHS. [%s]", du->szHostname );
			break;
		}

	}

	if (du != NULL)
	{
		domain = apr_pstrdup(p, du->szHostname);
		//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_du_put_target:: writable FHS. [%s]", domain);
	}
	else
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_shared_du_put_target:: No writable FHS. [%s]", my_domain);
	}

	return domain;
}

