#include "Main.h" #include "ProcessRename.h" #include "ServiceConfig.h" #include "Logger.h" #include "Worker.h" #include "Util.h" #include #include #include #include #include #include #include #include #include #include // Accept ´ë±â Count ¼ö #define DEFAULT_ACCEPT_WAIT_COUNT 50 // Àü¿ª °´Ã¼ Á¤º¸ // ¸í·É ¼ö½Å, Data ¼Û¼ö½ÅÀ» ´ã´çÇÒ TCP Listen Socket int g_listenSocket = -1; // »ç¿ë¹æ¹ý Ç¥½Ã void PrintUsage() { fprintf(stderr, "\n"); fprintf(stderr, "Usage: " PROG_NAME " [-h] [-v] [-c {file}] \n"); fprintf(stderr, "Options: \n"); fprintf(stderr, " -h Display help information \n"); fprintf(stderr, " -v Display version \n"); fprintf(stderr, " -c {file} Use {file} as config file \n"); fprintf(stderr, "\n"); fprintf(stderr, PROG_NAME " is Solbox Cloud Storage module that service - service content sync and check .\n\n"); return; } // ¹öÀü Á¤º¸ Ç¥½Ã void PrintVersion() { fprintf(stderr, "\n"); fprintf(stderr, PROG_NAME " version: " PROG_VERSION "\n\n"); return; } int main( int argc, char * argv[] ) { // ¼³Á¤ ÆÄÀÏÀ» ÀúÀåÇÒ º¯¼ö. std::string strConfigFileName = DEFAULT_CONFIG_FILE; // linux ¿¡¼­ ÇÁ·Î¼¼½º¸¦ Titile º¯°æÀ» Áö¿øÀ» À§Çؼ­ argv ¸Þ¸ð¸®¿¡ ÀúÀå argv = save_ps_display_args(argc, argv); // Àü´Þ ¹ÞÀº ¿É¼Ç ¿©ºÎ È®ÀÎ ¹× ó¸® if( argc >= 2) { int opt; while((opt = getopt(argc, argv, "hvc:")) != -1) // ¿É¼Ç ³¡±îÁö ÆÄ½Ìó¸® { switch(opt) { case 'h': case '?': // Á¤ÀǵÇÁö ¾ÊÀº ¹®ÀÚ°¡ ³ªÅ¸³¯ °æ¿ì getopt ¿¡ ÀÚµ¿¹Ýȯ. µµ¿ò¸» Ç¥½Ã ó¸®. PrintUsage(); return EXIT_SUCCESS; case 'v': PrintVersion(); return EXIT_SUCCESS; case 'c': strConfigFileName = optarg; fprintf(stderr, PROG_NAME " start with user define conf[%s]\n", strConfigFileName.c_str()); break; default: // º» Á¶°ÇÀýÀº getopt Ư¼º»ó µ¿ÀÛÇÏÁö ¾ÊÁö¸¸...¾÷¹« Flow ÀÌÇØ(?)¸¦ À§ÇØ À¯ÁöÇÑ´Ù. fprintf(stderr, "Unkonwn options[%c] used. program terminated.\n", opt); return EXIT_FAILURE; } } } // ÇÁ·Î±×·¥ Áߺ¹ ½ÇÇà üũ if( IsCurrentProcessRun() == true ) { fprintf(stderr, "[warning] Process[" PROG_NAME "] is already running...\n"); return EXIT_FAILURE; } // conf ·Îµù ¹× °Ë»ç std::string szErrorMessage; if( CServiceConfig::Init( PROG_NAME, strConfigFileName, szErrorMessage ) == false) { fprintf(stderr, "[ERR] %s\n\n", szErrorMessage.c_str()); return EXIT_FAILURE; } // Log °´Ã¼ »ý¼º ¹× ÃʱâÈ­ if (CLogger::Init(PROG_NAME, CServiceConfig::GetInstance()->GetLogPath(), LINF) == false) { fprintf(stderr, "[ERR] Log module initialize failed.[%s]\n\n", CServiceConfig::GetInstance()->GetLogPath()); return EXIT_FAILURE; } // Daemonize... // - nochdir: true(ÀÛ¾÷ µð·ºÅ丮 º¯°æ ¾ÈÇÔ) // - noclose: false (Ç¥ÁØ ÀÔÃâ·Â, ¿¡·¯¸¦ /dev/null ·Î ¸®µð·ºÆ® ó¸®) if( daemon(1, 0) == -1) { fprintf(stderr, "[ERR] Process daemonize failed.[%s]\n\n", strerror(errno) ); ReadyToExit(); return EXIT_FAILURE; } // Signal 󸮱⠼³Á¤ SetSignalMain(); // Process ±âµ¿ °ü·Ã Á¤º¸ ·Î±ë std::vector vecTemp; std::vector< std::string >::const_iterator it; _LOG( LINF, "***********************************************************"); _LOG( LINF, " %s Start. Version: %s", PROG_NAME, PROG_VERSION); _LOG( LINF, "***********************************************************"); _LOG( LINF, "Config : %s", strConfigFileName.c_str()); _LOG( LINF, "Log : %s/%s", CServiceConfig::GetInstance()->GetLogPath(), PROG_NAME); _LOG( LINF, "Log level : %d", CServiceConfig::GetInstance()->GetLogLevel()); _LOG( LINF, "RC ID : %s", CServiceConfig::GetInstance()->GetRcId()); _LOG( LINF, "RCDB : %s %u %s %s", CServiceConfig::GetInstance()->GetRcdbIp() , CServiceConfig::GetInstance()->GetRcdbPort() , CServiceConfig::GetInstance()->GetRcdbName() , CServiceConfig::GetInstance()->GetRcdbAcct()); _LOG( LINF, "Skip keyword" ); vecTemp = CServiceConfig::GetInstance()->GetSkipWord(); for( it = vecTemp.begin(); it != vecTemp.end(); it++ ) { _LOG( LINF, " : %s", it->c_str() ); } _LOG( LINF, "Operation tcp port : %u", CServiceConfig::GetInstance()->GetOperationPort() ); _LOG( LINF, "DB pool count : %u", CServiceConfig::GetInstance()->GetDbPoolCount() ); _LOG( LINF, "Work pool count : %u", CServiceConfig::GetInstance()->GetWorkPoolCount() ); _LOG( LINF, "Safety factor : %u (sec)", CServiceConfig::GetInstance()->GetSafetyFactor() ); _LOG( LINF, "Once Size : %u (day)", CServiceConfig::GetInstance()->GetOnceSize() ); _LOG( LINF, "Check" ); _LOG( LINF, " small : %u (sec)", CServiceConfig::GetInstance()->GetChkSmall() ); _LOG( LINF, " middle : %u (sec)", CServiceConfig::GetInstance()->GetChkMiddle() ); _LOG( LINF, " once : %u (hour)", CServiceConfig::GetInstance()->GetChkOnce() ); _LOG( LINF, "Diff command : %s", CServiceConfig::GetInstance()->GetDiffCommand() ); _LOG( LINF, "***********************************************************"); // Log Level Àç¼³Á¤. -> conf ¼³Á¤´ë·Î º¯°æ ó¸®. #ifdef _DEBUG_ CLogger::GetInstance()->SetLogLevel(LDBG); #else CLogger::GetInstance()->SetLogLevel(CServiceConfig::GetInstance()->GetLogLevel()); #endif // Command ¹× Data ¼Û¼ö½Å ó¸® °ü·Ã Listen Socket »ý¼º if( MakeListenSocket( g_listenSocket ) == false ) { LOG( LERR, "Listen socket create failed. => process exit." ); return EXIT_FAILURE; } // Worker Process »ý¼º if( MakeProcessWorker( g_listenSocket ) == false ) { LOG( LERR, "Worker process make failed. => process exit."); ReadyToExit(); return EXIT_FAILURE; } // Process Rename set_ps_display(PROG_NAME": Main [monitor worker process]", false); // Main Process : ±×³É ´ë±â while( 1 ) { // Main Process ´Â ÇÒ ÀÏÀÌ ¾ø´Ù. // => Worker ÇÁ·Î¼¼½º Á¾·á½Ã SIGCHLD ½ÅÈ£·Î ÀÎÇØ ½Åȣ󸮱⿡¼­ Worker Process À缺¼º ¼öÇàÇÔ. // => µû¶ó¼­ ±×³É ½Ã±×³Î ´ë±â pause(); } // ´ÙÀ½ÀÇ ÄÚµå´Â Daemon À¸·Î µ¿ÀÛÇϱ⠶§¹®¿¡ ¼öÇàµÇÁö ¾Ê´Â´Ù. ReadyToExit(); return EXIT_SUCCESS; } // Command, Data ¼ö½ÅÀ» À§ÇÑ TCP Listen socket »ý¼º bool MakeListenSocket( int & listenSocket ) { listenSocket = socket( AF_INET, SOCK_STREAM, 0 ); if( listenSocket == -1 ) { int errorNum = errno; LOG( LERR, "Listen socket create failed.[%d][%s]", errorNum, strerror( errorNum ) ); return false; } struct sockaddr_in listenSockAddr; socklen_t listenSockLen = 0; memset( &listenSockAddr, 0x00, sizeof( listenSockAddr ) ); listenSockAddr.sin_family = AF_INET; listenSockAddr.sin_port = htons( CServiceConfig::GetInstance()->GetOperationPort() ); listenSockAddr.sin_addr.s_addr = htonl( INADDR_ANY ); listenSockLen = sizeof( listenSockAddr ); int result = 0; // Socket Port Reuse Option Set int opt = 1; result = setsockopt( listenSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof( opt ) ); if( result != 0 ) { int errorNum = errno; LOG( LERR, "Listen socket option[SO_REUSEADDR] set failed. [%d][%s]", errorNum, strerror( errorNum ) ); return false; } // Keep Alive Option set opt = 1; result = setsockopt( listenSocket, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) ); if( result != 0 ) { int errorNum = errno; LOG( LERR, "Listen socket option[SO_KEEPALIVE] set failed. [%d][%s]", errorNum, strerror( errorNum ) ); return false; } // Socket Bind result = bind( listenSocket, ( struct sockaddr * )&listenSockAddr, listenSockLen ); if( result != 0 ) { int errorNum = errno; LOG( LERR, "Listen socket bind failed.[%d][%s]", errorNum, strerror( errorNum ) ); return false; } // Socket Listen result = listen( listenSocket, DEFAULT_ACCEPT_WAIT_COUNT ); if( result != 0 ) { int errorNum = errno; LOG( LERR, "Listen socket listen failed.[%d][%s]", errorNum, strerror( errorNum ) ); return false; } return true; } // Worker Process »ý¼º(fork) ó¸® ÇÔ¼ö // @return »ý¼º ¼º°ø½Ã true, ½ÇÆÐ½Ã¿¡´Â false ¸¦ ¹Ýȯ. bool MakeProcessWorker( int listenSocket ) { // Worker ÇÁ·Î¼¼½º fork pid_t processId = fork(); if( processId < 0 ) // fork fail { int errorNum = errno; LOG( LERR, "Worker process create failed. [%d][%s]", errorNum, strerror(errorNum) ); return false; } else if( processId == 0 ) // Child Process => Worker Process { // Worker Process Main ÇÔ¼ö È£Ãâ ¹× Á¾·áó¸®. int iResult; iResult = WorkerMain( listenSocket ); ReadyToExit(); // WorkerMain() ÇÔ¼ö return °ªÀ¸·Î Á¾·á󸮵ǵµ·Ï ÇÑ´Ù. exit( iResult ); } else { // Parent Process => Logging _LOG(LINF, "Worker[%d] create success." , processId); // Àá½Ã ´ë±â solusleep(100000); } return true; } // std »ó¿¡ trim ÇÔ¼ö°¡ ¾ø¾î¼­ Á÷Á¢ ±¸Çö ¾Æ´Ï¸é boost/algorithm/string.hpp »óÀÇ boost::trim ÇÔ¼ö »ç¿ë // @return void void Trim( std::string & str) { if( str.length() == 0 ) return; // ¹®ÀÚ¿­ µÚÀÇ °ø¹é, TAB, CR µîÀÇ ¹®ÀÚ Á¦°Åó¸®. std::string::size_type pos = str.find_last_not_of(" \a\b\f\n\r\t\v"); if( pos != std::string::npos ) str.erase(pos + 1); // ¹®ÀÚ¿­ ¾ÕÀÇ °ø¹é, TAB, CR µîÀÇ ¹®ÀÚ Á¦°Åó¸®. pos = str.find_first_not_of(" \a\b\f\n\r\t\v"); if( pos != std::string::npos ) str.erase(0, pos); } // ÇöÀç Process°¡ ±âµ¿ÁßÀÎÁö ¿©ºÎ¸¦ ÆÇ´ÜÇϱâ À§ÇÑ ÇÔ¼ö( ÇÁ·Î¼¼½º Áߺ¹ ½ÇÇà üũ) // @return ÀÌ¹Ì ÇØ´ç ÇÁ·Î¼¼½º°¡ ±âµ¿ ÁßÀÎ °æ¿ì true ¹Ýȯ, ±×·¸Áö ¾ÊÀ¸¸é false ¹Ýȯ. bool IsCurrentProcessRun() { char tempBuffer[512]; FILE * fd = NULL; bool bRun = false; snprintf( tempBuffer, sizeof(tempBuffer), "pgrep -x %s | sort", PROG_NAME ); fd = popen( tempBuffer, "r" ); if( fd == NULL ) { // ¿À·ù ¹ß»ý½Ã true ¹ÝȯÇÏ¿© ÇÁ·Î¼¼½º ½ÇÇà ¹æÁöó¸® fprintf(stderr, "[ERR] Process duplication check failed.[popen error][%s]\n", strerror(errno)); return true; } else { memset( tempBuffer, 0x00, sizeof(tempBuffer) ); while( fgets( tempBuffer, sizeof(tempBuffer)-1, fd) != NULL ) { string tempPid( tempBuffer ); Trim(tempPid); if( atoi( tempPid.c_str() ) != getpid() ) { fprintf(stderr, "[info] Process duplication found. pid[%d]\n", atoi(tempPid.c_str())); bRun = true; break; } } pclose( fd ); return bRun; } } // ÇÁ·Î¼¼½º Á¾·á Àü ó¸®ÇÒ Á¾·á °ü·Ã °¢ ÀÛ¾÷À» Àϰý·Î ó¸®Çϱâ À§ÇÑ ÇÔ¼ö. void ReadyToExit() { if( g_listenSocket != -1 ) { close( g_listenSocket ); g_listenSocket = -1; } // Logger °´Ã¼ Á¾·á ó¸®. CLogger::Exit(); return; } // Main Process °¡ Á¾·á Signal À» Àü´Þ¹ÞÀº °æ¿ì À̸¦ ó¸®Çϱâ À§ÇÑ ÇÔ¼ö. // @param nSignalNumber [in] ¹ß»ýÇÑ ½Ã±×³Î Number // @return void static void SignalMainTerminate(int nSignalNumber) { // Signal Number ¿¡ µû¸¥ ·Î±ëó¸®. if( nSignalNumber == SIGTERM ) { _LOG( LINF, "Main[%d] process exit by user signal[SIGTERM]. Good Bye..", getpid() ); } else { _LOG( LWAR, "Main[%d] process exit by abnormal signal[%d]. Good Bye..", getpid(), nSignalNumber ); } // KILL - Child kill(0, SIGTERM); ReadyToExit(); solusleep(500000); exit( EXIT_SUCCESS ); } // Main Process ¿¡¼­ ÀÚ½Ä ÇÁ·Î¼¼½ºÀÎ Worker Process °¡ Á¾·áµÈ °æ¿ì ½Ã±×³Î 󸮸¦ À§ÇÑ ÇÔ¼ö // @param nSignalNumber [in] ¹ß»ýÇÑ ½Ã±×³Î Number // @return void static void SignalWorkerDead(int nSignalNumber) { pid_t deadPid; int nDeadStatus; while( (deadPid = waitpid(-1, &nDeadStatus, WNOHANG)) > 0 ) { // Worker ÇÁ·Î¼¼½º°¡ Signal ¿¡ ÀÇÇØ Á¾·áµÇ¾ú´Â°¡? if( WIFSIGNALED( nDeadStatus ) ) { _LOG( LWAR, "Worker[%d] killed by signal[%d].", deadPid, WTERMSIG(nDeadStatus)); } else { _LOG( LWAR, "Worker[%d] killed. Not signal.", deadPid); // Worker ÇÁ·Î¼¼½º°¡ EXIT_FAILURE ¹Ýȯ ( ÃʱâÈ­ ½ÇÆÐ½Ã ) // ÇØ´ç ³»¿ªÀ» È­¸é ¹× ·Î±× »ó¿¡ Ãâ·ÂÇϰí // Worker ÇÁ·Î¼¼½º¸¦ Àç»ý¼º ó¸®ÇÏÁö ¾Ê´Â´Ù. if( WIFEXITED( nDeadStatus ) ) { if( WEXITSTATUS( nDeadStatus ) == EXIT_FAILURE ) { _LOG( LERR, "Worker[%d] initilaize failed.", deadPid); _LOG( LERR, "Main[%d] abnormally exit by worker initialize failed. Good Bye..", getpid()); ReadyToExit(); solusleep(500000); exit( EXIT_SUCCESS ); } } } // Worker Process Àç»ý¼º ó¸®. if( MakeProcessWorker( g_listenSocket ) == false ) { // Worker Process À缺¼º ½ÇÆÐ½Ã // ·Î±ë ó¸® ÈÄ Main ÇÁ·Î¼¼½º Á¾·á ½Ã±×³Î ¹ß»ý _LOG( LERR, "Worker process recreate failed. => Main process exit."); raise( SIGTERM ); } else { _LOG( LWAR, "Worker process recreate success."); } } // ¿À·ù ¹ß»ý½Ã ÇØ´ç ³»¿ª ·Î±ë if( deadPid < 0 ) { int errorNum = errno; LOG( LERR, "Main process error: SIG_CHLD receive but waitpid return error[%d][%s]", errorNum, strerror(errorNum)); } return; } /// @brief Main ÇÁ·Î¼¼½º signal ó¸® ¼³Á¤À» À§ÇÑ ÇÔ¼ö /// @return void void SetSignalMain() { sigset_t set; struct sigaction act; memset(&act, 0x00, sizeof(act)); sigfillset( &set ); sigprocmask( SIG_SETMASK, &set, NULL ); sigfillset( &act.sa_mask ); // ¹«½Ã ó¸® signal act.sa_handler = SIG_IGN; sigaction( SIGPIPE, &act, NULL); /* desciptor ¿À·ù ¹ß»ý½Ã Process°¡ Á×´Â °ÍÀº ¹æÁöÇϱâ À§ÇÏ¿© ¼³Á¤ */ sigaction( SIGHUP, &act, NULL); /* process¸¦ ±âµ¿½ÃŲ °ü¸®ÀÚÀÇ ·Î±×¾Æ¿ô½Ã, ¶Ç´Â config reload µîÀÇ Ã³¸® signal*/ sigaction( SIGINT, &act, NULL); /* ^C ۸¦ ´©¸¥ °æ¿ì ¹Þ´Â ½ÅÈ£ => demon À¸·Î ±âµ¿µÇ±â ¶«½Ã ÀÌ ½ÅÈ£ ¸ø¹ÞÀ½ */ sigaction( SIGQUIT, &act, NULL); /* Űº¸µå¿¡ ÀÇÇÑ Abort ½ÅÈ£ ó¸® => ? */ // Child Process ÀÎ Worker ÇÁ·Î¼¼½º Á¾·á¿¡ ´ëÇÑ Ã³¸®±â ¼³Á¤. act.sa_handler = SignalWorkerDead; sigaction( SIGCHLD, &act, NULL); // »ç¿ëÀÚ Á¾·á ¶Ç´Â ¿¡·¯ °ü·Ã ½ÅÈ£ ó¸®. act.sa_handler = SignalMainTerminate; sigaction( SIGTERM, &act, NULL); /* kill -TERM ¿¡ ÀÇÇÑ ÇÁ·Î¼¼½º Á¾·á½Ã */ sigemptyset(&set); /* ½ÅÈ£ 󸮱â ó¸® ¼³Á¤ À§ÇÑ ºí·Ï ÇØÁ¦ */ sigprocmask(SIG_SETMASK, &set, NULL); }