/***************************************************************************
                LogFileCleaner.cpp
                -----------------------------------------
    begin      : 2011/11/09
    copyright  : (C) 2005 SolutionBox Inc.
    author     : Service 1 Team
    email      : svc1@solbox.com

    CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
    Redistribution and use in source and binary forms, with or with out
    modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include "LogFileCleaner.h"
#include "DaemonConfigs.h"
#include "Logger.h"
#include "String.h"
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>

#define SECONDS_CORRESPOND_TO_ONE_DAY  86400

//#define __HJ_TEST__

#define	ACCESS_LOG_PRE_NAME	"access_log_"
#define	ERROR_LOG_PRE_NAME	"error_log_"

using namespace std;

CLogFileCleaner::CLogFileCleaner() : CLogFileProcessThread( "CLogFileCleaner" )
{
}

CLogFileCleaner::~CLogFileCleaner()
{
}

bool CLogFileCleaner::DoWork()
{
	/// 로그 보존기간 설정값 가지고 오기.
	unsigned int logPeriod = CDeamonConfig::GetInstance()->GetAppLogPeriod();

	std::vector< std::string > vecSearchingDirList = CDeamonConfig::GetInstance()->GetSearchingDirList();
	std::vector< std::string >::iterator it;

	for (it=vecSearchingDirList.begin(); it<vecSearchingDirList.end(); it++)
	{
		std::string szSearchingDir = *it;

		// return은 bool type이지만.. 여러장비에서 구동하기위해 return값을 무시하기로한다.
		UnlinkFiles( szSearchingDir, logPeriod );
	}

	return true;
}

bool CLogFileCleaner::UnlinkFiles( std::string path, unsigned int period )
{

	LOG( LDBG, "Unlink Request dir is [%s].", path.c_str() );

	/// 디렉토리 open.
	DIR* pDir = opendir( path.c_str() ); 

	if( pDir == NULL )
	{
		//_LOG( LINF, "can't unlink files because opendir(%s) is failed.", path.c_str() );
		_LOG( LINF, "[UNLINK] directory [%s] not exist.", path.c_str() );
		
		return false;
	}

	string fullname;
	struct dirent* pDirEnt;

	/// 디렉토리 내의 파일들을 검색.
	while( ( pDirEnt = readdir( pDir ) ) != NULL )
	{
		if( pDirEnt->d_name[0] == '.' )
		{
			continue;
		}

		/// 파일의 전체 경로 만들기.
		fullname = path + "/" + string( pDirEnt->d_name );

		if( IsDirectory( fullname ) == true )
		{
			/// 디렉토리이면 재귀 호출.
			UnlinkFiles( fullname, period );
		}
		else
		{
			/// 로그 파일이 아니면 skip.
			if( IsLogFile( fullname ) == false )
			{
				LOG( LDEV, "%s is not log file.", fullname.c_str() );
				continue;
			}

			/// 파일이면 CInode 정보를 가지고 온다.
			CInode inode;
			if( inode.LoadInfo( fullname ) == false )
			{
				/// 파일 정보를 가지고 오지 못 하면 skip.
				LOG( LWAR, "inode info loading is failed. file=%s", fullname.c_str() );
				continue;
			}

			/// 로그 파일 보존기간이 경과 했으면 삭제.
			if( IsPassedPeriod( inode, period ) == true )
			{
				LOG( LDBG, "%s is deleted.", fullname.c_str() );
				Unlink( inode.GetFilename() );	
			}
		}
	}

	/// 디렉토리 close.
	closedir( pDir );

	return true;
}

bool CLogFileCleaner::IsPassedPeriod( CInode& inode, unsigned int period )
{
	

	/// period는 일 단위이기 때문에 초 단위로 환산한다.
	time_t periodBySecond = period * SECONDS_CORRESPOND_TO_ONE_DAY;

#ifdef	__HJ_TEST__
	/// 테스트를 위해서 periodBySecond을 5초로 한다.
	periodBySecond = 5;
#endif


    time_t current = time( NULL );;

	LOG( LDEV, "current = %jd, inode.GetATime() = %jd, periodBySecond = %jd, current - inode.GetATime() = %jd",
				current, inode.GetATime(), periodBySecond, current - inode.GetATime() );

	if( current < inode.GetATime() )
	{
		LOG( LWAR, "current timestamp is less than the create time of %s.", inode.GetFilename().c_str() );
		
		return false;
	}

	/// 보존 기간 확인.
	if( ( current - inode.GetATime() ) < periodBySecond  )
	{
		/// 보존 기간이 경과 되지 않았으면 
		LOG( LDEV, "%s is not passed period(%jd sec).", inode.GetFilename().c_str(), periodBySecond );
		
		return false;
	}
        
	
	return true;
}

/// 파일의 확장자가 log이면 true 반환, 그렇지 않으면 false 반환.
bool CLogFileCleaner::IsLogFile( std::string filename )
{
	int n1 = filename.find(ACCESS_LOG_PRE_NAME);
	int n2 = filename.find(ERROR_LOG_PRE_NAME);
	LOG( LDBG, "%s, %d, %d ", filename.c_str(), n1, n2 );

	//!! 아파치 로그인지를 확인한다.
	//if( filename.find(ACCESS_LOG_PRE_NAME) >= 0 || filename.find(ERROR_LOG_PRE_NAME) >=0 )
	if( n1 >= 0 || n2 >=0 )
	{
		LOG( LDBG, "%s is apache log file.", filename.c_str() );
		return true;
	}

	/// '.'을 구분자로한 토큰을 벡터에 넣는다.
	vector<string> tokens = CString::Tokenize( filename, "." );

	/// 벡터의 크기가 1이면 '.'이 없다는 말이다.  즉, 로그 파일이 아니다.
	if( tokens.size() == 1 )
	{
		
		return false;
	}

	/// 파일 이름에서 확장자를 가지고 온다.
	string extension = tokens[ tokens.size() - 1 ];

	/// 확장자 비교.
	if( extension.compare( "log" ) != 0 )
	{
	/// 확장자가 'log'가 아님.
		
		return false;
	}

	

	return true;
}
