//******************************************** // AE.cpp // ------------------------------------------- // Main program and function implementations // of the AE (Anti-pollution Engine) // // Author: Leiwen Deng // Date: 12/10/2005 //******************************************** #include "AE.h" #include "Sketch/IDS.h" SYS_AE_PARAMETER sys_AE; // global variable of system parameters const char * log_tags[] = { "NONE", "TCP_HIT", "TCP_MISS", "TCP_REFRESH_HIT", "TCP_REF_FAIL_HIT", "TCP_REFRESH_MISS", "TCP_CLIENT_REFRESH_MISS", "TCP_IMS_HIT", "TCP_SWAPFAIL_MISS", "TCP_NEGATIVE_HIT", "TCP_MEM_HIT", "TCP_DENIED", "TCP_OFFLINE_HIT", #if LOG_TCP_REDIRECTS "TCP_REDIRECT", #endif "UDP_HIT", "UDP_MISS", "UDP_DENIED", "UDP_INVALID", "UDP_MISS_NOFETCH", "ICP_QUERY", "LOG_TYPE_MAX" }; // tags that interpret the "result_code" of SQUID access information // Read configurations from the configuration file of the AE static int ReadConfiguration( void ); // Read the next parameter in the configuration file static char ReadParameter( FILE * fp, const char * parameter_name, const char * format_string, void * parameter_ptr ); // The main program of AE int main( void ) { const char * log_file = LOG_FILE_NAME; if( ( sys_AE.log_file = fopen( log_file, "w" ) ) == NULL ) { fprintf ( stderr, "Error opening file \"%s\" for writing\n", log_file ); exit( 1 ); } ParseConfiguration(); const char * statistic_file = STATISTIC_FILE_NAME; if( sys_AE.statistic_enable && ( sys_AE.statistic_file = fopen( statistic_file, "w" ) ) == NULL ) Die( "Error opening file \"%s\" for writing\n", statistic_file ); Install_SKETCH_Handler(); Install_AEM_Handler(); signal( SIGTERM, Sig_Term ); time_t current_time = time( NULL ); char * s_time = asctime( localtime( ¤t_time ) ); s_time[ strlen( s_time ) - 1 ] = 0; LogPrintf( "%s: AE is running...\n", s_time ); LogFlush(); // Turn off buffering for both input and output pipes setvbuf( stdin, NULL, _IONBF, 0 ); setvbuf( stdout, NULL, _IONBF, 0 ); while( ! feof( stdin ) ) { char buffer[MAX_VALUE_LENGTH]; IPC_TLV tlv; tlv.value = buffer; // Clear the FLAG_AE_BUSY bit sys_AE.flag &= ~FLAG_AE_BUSY; // Check the pending processes triggered by signals CheckPendingProcess(); // Read TLV from "stdin" in blocking mode char error_code = ReceiveTLV( &tlv, stdin ); // Set the FLAG_AE_BUSY bit sys_AE.flag |= FLAG_AE_BUSY; if ( error_code == 0 ) { switch( tlv.type ) { case TLV_ACCESS_INFO: ACCESS_INFO access_info = *(ACCESS_INFO *)buffer; if ( sys_AE.statistic_enable ) Statistic( access_info ); PCSA_Process( access_info ); Append_SKETCH_Record( access_info ); LogPrintf( "%d.%03d %d %s %s %d %s %d %d %d\n", access_info.current_time_sec, access_info.current_time_usec / 1000, access_info.duration, inet_ntoa( access_info.client_address ), log_tags[access_info.result_code], access_info.received_bytes, MD5_Hex( access_info.md5key ), access_info.refcount, access_info.lastref, access_info.flag & AFLAG_CACHED ); break; case TLV_RELEASE_ENTRY: ENTRY_INFO entry_info = *(ENTRY_INFO *)buffer; const char * result; if ( sys_AE.PCSA_enable && sys_AE.pcsa_main.DeleteEntry( entry_info.md5key ) ) LogPrintf( "Release entry, key=<%s>\n", MD5_Hex( entry_info.md5key ) ); break; default: LogPrintf( "Type error: type %d\n", tlv.type ); } } else { LogPrintf( "Corrupt TLV\n" ); } LogFlush(); } LogPrintf( "AE exits due to the close of input pipe\n" ); if( sys_AE.statistic_enable ) fclose( sys_AE.statistic_file ); fclose( sys_AE.log_file ); return 0; } //******************************************** // errorcode = ReceiveTLV( tlv, fp ) // ------------------------------------------- // Receive a TLV from an input stream // points to the space to store the TLV // specifies the stream // Return 0 on success and return a nonzero // code if an error occurs //******************************************** char ReceiveTLV( IPC_TLV * tlv, FILE * fp ) { if ( fread( tlv, sizeof( short ) * 2, 1, fp ) != 1 ) return 1; if ( fread( tlv->value, tlv->length, 1, fp ) != 1 ) return 2; return 0; } //******************************************** // errorcode = SendTLV( tlv, fp ) // ------------------------------------------- // Send a TLV to an output stream // specifies the TLV // specifies the stream // Return 0 on success and return a nonzero // code if an error occurs //******************************************** char SendTLV( const IPC_TLV * tlv, FILE * fp ) { struct{ short type; short length; char value[MAX_VALUE_LENGTH]; }tlv_buffer; tlv_buffer.type = tlv->type; tlv_buffer.length = tlv->length; assert( tlv_buffer.length <= MAX_VALUE_LENGTH ); memcpy( tlv_buffer.value, tlv->value, tlv_buffer.length ); if ( fwrite( &tlv_buffer, sizeof( short ) * 2 + tlv_buffer.length, 1, fp ) != 1 ) { LogPrintf( "SendTLV: IPC pipe full. (TLV type %d)\n", tlv_buffer.type ); return 1; } return 0; } //******************************************** // Quit() // ------------------------------------------- // Quit the AE module //******************************************** void Quit( void ) { if ( sys_AE.SKETCH_enable ) { fclose( sys_AE.sketch_input ); // Send terminal signal to SKETCH process kill( sys_AE.sketch_pid, SIGTERM ); // Wait SKETCH process to exit waitpid( sys_AE.sketch_pid, NULL, 0 ); } time_t current_time = time( NULL ); char * s_time = asctime( localtime( ¤t_time ) ); s_time[ strlen( s_time ) - 1 ] = 0; LogPrintf( "%s: AE exits normally\n", s_time ); if( sys_AE.statistic_enable ) fclose( sys_AE.statistic_file ); fclose( sys_AE.log_file ); exit( 0 ); } //******************************************** // character = Hex_4bit( value ) // ------------------------------------------- // Convert the (0--15) to its corresponding // hexadecimal character ('0','1',...'E','F') //******************************************** static char Hex_4bit( char value ) { value &= 15; if ( value <= 9 ) value += '0'; else value += ( 'A' - 10 ); return value; } //******************************************** // SYS_AE_PARAMETER() // ------------------------------------------- // The constructor //******************************************** SYS_AE_PARAMETER::SYS_AE_PARAMETER( void ) { log_file = NULL; log_updated = false; sketch_input = NULL; flag = 0; } //******************************************** // PCSA_Process( access_info ) // ------------------------------------------- // Update PCSA records according to the // input access information // specifies the access // information //******************************************** void PCSA_Process( const ACCESS_INFO &access_info ) { if ( ! sys_AE.PCSA_enable ) return; if ( ! ( access_info.flag & AFLAG_CACHED ) ) // The corresponding entry is not in the SQUID cache, ignore it return; // ************* Begin ************* // For test // PCSA_RECORD * current_entry = sys_AE.pcsa_main.ReferenceEntry( access_info.md5key, random(), access_info.refcount ); // ************* End *************** PCSA_RECORD * current_entry = sys_AE.pcsa_main.ReferenceEntry( access_info.md5key, access_info.client_address.s_addr, access_info.refcount ); if ( current_entry ) // Entry is found in the records { if ( sys_AE.pcsa_main.Positive( current_entry ) ) // This is a positive entry, delete and block it { sys_AE.pcsa_main.DeleteEntry( access_info.md5key ); SendBlockEntryRequest( access_info.md5key ); } } } //******************************************** // Append_SKETCH_Record( access_info ) // ------------------------------------------- // Append a record to the SKETCH input file // based on the input access information // specifies the access // information //******************************************** void Append_SKETCH_Record( const ACCESS_INFO &access_info ) { if ( ! sys_AE.SKETCH_enable ) return; if ( ! ( access_info.flag & AFLAG_CACHED ) ) // The corresponding entry is not in the SQUID cache, ignore it return; record_t record; record.src_ip = access_info.client_address.s_addr; record.dst_ip = record.file = record.size = 0; if ( fwrite( &record, sizeof( record_t ), 1, sys_AE.sketch_input ) != 1 ) { LogPrintf( "%d.%03d Fail to append record to SKETCH input file\n", access_info.current_time_sec, access_info.current_time_usec / 1000 ); LogFlush(); } } //******************************************** // Sig_Term( sig_num ) // ------------------------------------------- // The signal handler for SIGTERM //******************************************** void Sig_Term( int sig_num ) { if ( ! ( sys_AE.flag & FLAG_AE_BUSY ) ) Quit(); else sys_AE.flag |= FLAG_QUIT; } //******************************************** // Sig_User1( sig_num ) // ------------------------------------------- // The signal handler for SIGUSR1. // SIGUSR1 is sent by the SKETCH module when // it has finished processing an input file. // On receiving this signal, the AE processes // SKETCH's feedback by reading its output file. //******************************************** void Sig_User1( int sig_num ) { // Re-set the signal handler, for the next time signal( SIGUSR1, Sig_User1 ); static int lock = 0; if ( lock ) return; lock ++; if ( ! ( sys_AE.flag & FLAG_AE_BUSY ) ) Process_SKETCH_Feedback(); else sys_AE.flag |= FLAG_SKETCH_FEEDBACK; lock --; } //******************************************** // Sig_User2( sig_num ) // ------------------------------------------- // The signal handler for SIGUSR2. // SIGUSR2 is sent by the AEM tool to perform // some monitoring operations. //******************************************** void Sig_User2( int sig_num ) { // Re-set the signal handler, for the next time signal( SIGUSR2, Sig_User2 ); static int lock = 0; if ( lock ) return; lock ++; if ( ! ( sys_AE.flag & FLAG_AE_BUSY ) ) Process_AEM_operation(); else sys_AE.flag |= FLAG_PROCESS_AEM; lock --; } //******************************************** // Sig_Alarm( sig_num ) // ------------------------------------------- // The signal handler for SIGALRM. // SIGALRM is sent by AE itself periodically // to trigger the data processing of the SKETCH // module //******************************************** void Sig_Alarm( int sig_num ) { // Re-set the signal handler, for the next time signal( SIGALRM, Sig_Alarm ); // Set the timer for the next period alarm( sys_AE.SKETCH_interval ); if ( ! ( sys_AE.flag & FLAG_AE_BUSY ) ) Run_SKETCH(); else sys_AE.flag |= FLAG_RUN_SKETCH; } //******************************************** // Process_AEM_operation() // ------------------------------------------- // Process the operation specified by the AEM // tool. // The AEM issues commands of operations by // sending the signal SIGUSR2. The operation // parameters are passed through shared memory. //******************************************** void Process_AEM_operation( void ) { switch ( sys_AE.shm_aem->operation ) { case AEM_SHOW_TABLE: if ( sys_AE.PCSA_enable ) sys_AE.pcsa_main.DumpTable( SHOW_TABLE ); break; case AEM_SHOW_TABLE_STRUCTURE: if ( sys_AE.PCSA_enable ) sys_AE.pcsa_main.DumpTable( SHOW_TABLE_STRUCTURE ); break; } } //******************************************** // Install_AEM_Handler() // ------------------------------------------- // Install the handler for the AEM tool. // This includes the following two steps: // 1. Allocate and attach to the shared // memory // 2. Install the handler for SIGUSR2 //******************************************** void Install_AEM_Handler( void ) { int shmid; key_t key = SHM_AEM_KEY; if ( ( shmid = shmget( key, sizeof( SHM_AEM ), IPC_CREAT | 0666) ) < 0 ) { Die( "Error: fail to create the shared memory for AEM handler\n" ); } if ( ( sys_AE.shm_aem = (SHM_AEM *)shmat( shmid, NULL, 0) ) == (SHM_AEM *)-1 ) { Die( "Error: fail to attach to the shared memory for AEM handler\n" ); } signal( SIGUSR2, Sig_User2 ); } //******************************************** // errorcode = SendBlockEntryRequest( md5key ) // ------------------------------------------- // Send a block-entry request to the AEI. // specifies the key of the entry // to block // Return 0 on success and return a nonzero // code if an error occurs //******************************************** char SendBlockEntryRequest( const MD5_KEY &md5key ) { ENTRY_INFO entry_info; memcpy( entry_info.md5key, md5key.key, MD5_DIGEST_CHARS ); IPC_TLV tlv; tlv.type = TLV_BLOCK_ENTRY; tlv.length = sizeof( ENTRY_INFO ); tlv.value = (void *)&entry_info; if ( SendTLV( &tlv, stdout ) != 0 ) return 1; return 0; } //******************************************** // errorcode = SendBlockClientRequest( client_ip ) // ------------------------------------------- // Send a block-client request to the AEI. // specifies the ip address of the // client to block // Return 0 on success and return a nonzero // code if an error occurs //******************************************** char SendBlockClientRequest( uint32 client_ip ) { CLIENT_INFO client_info; client_info.ip.s_addr = client_ip; IPC_TLV tlv; tlv.type = TLV_BLOCK_CLIENT; tlv.length = sizeof( CLIENT_INFO ); tlv.value = (void *)&client_info; if ( SendTLV( &tlv, stdout ) != 0 ) return 1; return 0; } //******************************************** // Install_SKETCH_Handler() // ------------------------------------------- // Install the handler for the SKETCH module. // This includes the following steps: // 1. Spawn the child process for the SKETCH // module // 2. Open the input file of SKETCH module // 3. Install the handler for SIGALRM and // start the timer // 4. Install the handler for SIGUSR1, which // is used to process the feedback from // the SKETCH module //******************************************** void Install_SKETCH_Handler( void ) { if ( ! sys_AE.SKETCH_enable ) return; if ( ( sys_AE.sketch_pid = fork() ) < 0 ) // fork fails { Die( "Fatal error: cannot create child process for SKETCH\n" ); } else if ( sys_AE.sketch_pid == 0 ) // In the child { // Get rid of "stdin" and "stdout" freopen( "/dev/null", "r", stdin ); freopen( "/dev/null", "w", stdout ); // Redirect "stderr" const char * sketch_log_file = SKETCH_LOG_FILE_NAME; freopen( sketch_log_file, "w", stderr ); const char * sketch_program = SKETCH_PROGRAM; const char * input = SKETCH_INPUT_FILE_NAME; const char * output = SKETCH_OUTPUT_FILE_NAME; char delta[20]; char gamma[20]; sprintf( delta, "%lf", sys_AE.SKETCH_delta ); sprintf( gamma, "%lf", sys_AE.SKETCH_gamma ); // Start the SKETCH program execl( sketch_program, sketch_program, delta, input, output, gamma, NULL ); Die( "Fatal error: fail to run SKETCH program \"%s\"\n", sketch_program ); } const char * sketch_temp_file = SKETCH_TEMP_FILE_NAME; if( ( sys_AE.sketch_input = fopen( sketch_temp_file, "wb" ) ) == NULL ) { Die( "Error opening file \"%s\" for writing\n", sketch_temp_file ); } signal( SIGALRM, Sig_Alarm ); alarm( sys_AE.SKETCH_interval ); signal( SIGUSR1, Sig_User1 ); } //******************************************** // Run_SKETCH() // ------------------------------------------- // Trigger the data processing of the SKETCH // module //******************************************** void Run_SKETCH( void ) { fclose( sys_AE.sketch_input ); const char * sketch_temp_file = SKETCH_TEMP_FILE_NAME; const char * sketch_input_file = SKETCH_INPUT_FILE_NAME; rename( sketch_temp_file, sketch_input_file ); sys_AE.sketch_input = fopen( sketch_temp_file, "wb" ); // Send the signal to SKETCH process kill( sys_AE.sketch_pid, SIGUSR1 ); LogPrintf( "%d Send running signal to SKETCH process\n", time( NULL ) ); LogFlush(); } //******************************************** // CheckPendingProcess() // ------------------------------------------- // Check the pending processes triggered by // signals //******************************************** void CheckPendingProcess( void ) { if ( sys_AE.flag & FLAG_RUN_SKETCH ) { // Clear the FLAG_RUN_SKETCH bit sys_AE.flag &= ~FLAG_RUN_SKETCH; // Run the SKETCH detection algorithm Run_SKETCH(); } if ( sys_AE.flag & FLAG_PROCESS_AEM ) { // Clear the FLAG_PROCESS_AEM bit sys_AE.flag &= ~FLAG_PROCESS_AEM; // Process AE monitor operation Process_AEM_operation(); } if ( sys_AE.flag & FLAG_SKETCH_FEEDBACK ) { // Clear the FLAG_SKETCH_FEEDBACK bit sys_AE.flag &= ~FLAG_SKETCH_FEEDBACK; // Process SKETCH feedback information Process_SKETCH_Feedback(); } if ( sys_AE.flag & FLAG_QUIT ) { // Quit the AE module Quit(); } } //******************************************** // Process_SKETCH_Feedback() // ------------------------------------------- // Process the feedback from the SKETCH module. // This is accomplished by reading the output // file of the SKETCH module and sending // a block-client request to AEI for each // entry specified in the output file. //******************************************** void Process_SKETCH_Feedback( void ) { LogPrintf( "%d SKETCH feedback received\n", time( NULL ) ); FILE * sketch_output_fp; const char * sketch_output_file = SKETCH_OUTPUT_FILE_NAME; if( ( sketch_output_fp = fopen( sketch_output_file, "r" ) ) == NULL ) { LogPrintf( "Error opening file \"%s\" for reading\n", sketch_output_file ); } else { char buffer[200]; int count = 0; while ( fgets( buffer, 200, sketch_output_fp ) ) { uint32 client_ip; if ( sscanf( buffer, "%u", &client_ip ) == 1 ) { SendBlockClientRequest( client_ip ); count ++; } } fclose( sketch_output_fp ); if ( count > 0 ) LogPrintf( "\t%d block-client request sent\n", count ); } LogFlush(); } //******************************************** // Die( format, ... ) // ------------------------------------------- // Print a formatted message to the log file // and then terminate the program //******************************************** void Die( const char * format, ... ) { va_list param_list; va_start( param_list, format ); vfprintf( sys_AE.log_file, format, param_list ); exit( 1 ); } //******************************************** // LogPrintf( format, ... ) // ------------------------------------------- // Print a formatted message to the log file //******************************************** void LogPrintf( const char * format, ... ) { va_list param_list; va_start( param_list, format ); vfprintf( sys_AE.log_file, format, param_list ); sys_AE.log_updated = true; } //******************************************** // LogFlush() // ------------------------------------------- // Flush the log file if necessary //******************************************** void LogFlush( void ) { if ( sys_AE.log_updated ) { fflush( sys_AE.log_file ); sys_AE.log_updated = false; } } //******************************************** // ParseConfiguration() // ------------------------------------------- // Parse the configuration file of the AE //******************************************** void ParseConfiguration( void ) { int code; if ( ( code = ReadConfiguration() ) != 0 ) { if ( code == 1 ) { const char * config_file = CONFIG_FILE_NAME; Die( "Error opening configurtion file \"%s\" for reading\n", config_file ); } else { Die( "Error in configurtion file, errorcode: %d\n", code ); } } } //******************************************** // errorcode = ReadConfiguration() // ------------------------------------------- // Read configurations from the configuration // file of the AE. // Return 0 on success and return a nonzero // code if an error occurs //******************************************** static int ReadConfiguration( void ) { FILE * fp; const char * config_file = CONFIG_FILE_NAME; if( ( fp = fopen( config_file, "r" ) ) == NULL ) return 1; int temp; if ( ReadParameter( fp, "PCSA_enable", "%d", &temp ) != 0 ) { fclose( fp ); return 2; } sys_AE.PCSA_enable = ( temp ? true : false ); if ( ReadParameter( fp, "PCSA_refcount_threshold", "%d", &sys_AE.pcsa_main.refcount_threshold ) != 0 ) { fclose( fp ); return 3; } if ( ReadParameter( fp, "PCSA_m_bit", "%d", &sys_AE.pcsa_main.m_bit ) != 0 ) { fclose( fp ); return 4; } if ( ReadParameter( fp, "PCSA_max_ac_set_size", "%d", &sys_AE.pcsa_main.max_ac_set_size ) != 0 ) { fclose( fp ); return 10; } if ( ReadParameter( fp, "PCSA_positive_threshold", "%lf", &sys_AE.pcsa_main.positive_threshold ) != 0 ) { fclose( fp ); return 5; } if ( ReadParameter( fp, "SKETCH_enable", "%d", &temp ) != 0 ) { fclose( fp ); return 6; } sys_AE.SKETCH_enable = ( temp ? true : false ); if ( ReadParameter( fp, "SKETCH_delta", "%lf", &sys_AE.SKETCH_delta ) != 0 ) { fclose( fp ); return 7; } if ( ReadParameter( fp, "SKETCH_gamma", "%lf", &sys_AE.SKETCH_gamma ) != 0 ) { fclose( fp ); return 8; } if ( ReadParameter( fp, "SKETCH_interval", "%d", &sys_AE.SKETCH_interval ) != 0 ) { fclose( fp ); return 9; } if ( ReadParameter( fp, "statistic_enable", "%d", &temp ) != 0 ) { fclose( fp ); return 11; } sys_AE.statistic_enable = ( temp ? true : false ); fclose( fp ); // Print out configurations to the log file LogPrintf( "[Configuration Parameters]\nPCSA_enable: %d\nPCSA_refcount_threshold: %d\nPCSA_m_bit: %d\nPCSA_max_ac_set_size: %d\n"\ "PCSA_positive_threshold: %g\nSKETCH_enable: %d\nSKETCH_delta: %g\nSKETCH_gamma: %g\nSKETCH_interval: %d\nstatistic_enable: %d\n\n", sys_AE.PCSA_enable, sys_AE.pcsa_main.refcount_threshold, sys_AE.pcsa_main.m_bit, sys_AE.pcsa_main.max_ac_set_size, sys_AE.pcsa_main.positive_threshold, sys_AE.SKETCH_enable, sys_AE.SKETCH_delta, sys_AE.SKETCH_gamma, sys_AE.SKETCH_interval, sys_AE.statistic_enable ); return 0; } //******************************************** // errorcode = ReadParameter( fp, parameter_name, format_string, parameter_ptr ) // ------------------------------------------- // Read the next parameter in the configuration // file. // points to the configuration file // specifies the name of the // next parameter expected // specifies the data type of // the parameter // points to the space to // store the parameter // Return 0 on success and return a nonzero // code if an error occurs //******************************************** static char ReadParameter( FILE * fp, const char * parameter_name, const char * format_string, void * parameter_ptr ) { char buffer[MAX_CONFIG_LINE_WIDTH + 1]; char match = 0; while ( ! match && fgets( buffer, MAX_CONFIG_LINE_WIDTH, fp ) ) { if ( buffer[0] != '#' ) // Not a comment line { int str_len = strlen( parameter_name ); if ( strncmp( buffer, parameter_name, str_len ) == 0 ) { match = 1; sscanf( buffer + str_len + 1, format_string, parameter_ptr ); } } } if ( ! match ) return 1; return 0; } //******************************************** // Statistic( access_info ) // ------------------------------------------- // Perform statistics based on //******************************************** #define TCP_HIT 1 #define TCP_MEM_HIT 10 #define TCP_MISS 2 #define STATISTIC_INTERVAL 30 void Statistic( const ACCESS_INFO &access_info ) { static int hit = 0, miss = 0, good_hit = 0, good_miss = 0; static time_t timestamp = access_info.current_time_sec; static time_t start_time = access_info.current_time_sec; if ( access_info.result_code == TCP_HIT || access_info.result_code == TCP_MEM_HIT ) { hit ++; if ( ! ( access_info.flag & AFLAG_POLLUTION ) ) good_hit ++; } else if ( access_info.result_code == TCP_MISS ) { miss ++; if ( ! ( access_info.flag & AFLAG_POLLUTION ) ) good_miss ++; } if ( access_info.current_time_sec >= timestamp + STATISTIC_INTERVAL ) { int bad_hit = hit - good_hit; int bad_miss = miss - good_miss; int total = hit + miss; if ( total == 0 ) total = 1; int good_total = good_hit + good_miss; if ( good_total == 0 ) good_total = 1; int bad_total = bad_hit + bad_miss; if ( bad_total == 0 ) bad_total = 1; fprintf( sys_AE.statistic_file, "%d %d %d %.1f %d %d %.1f %d %d %.1f\n", timestamp - start_time, hit, miss, (double)hit * 100 / total, good_hit, good_miss, (double)good_hit * 100 / good_total, bad_hit, bad_miss, (double)bad_hit * 100 / bad_total ); fflush( sys_AE.statistic_file ); hit = miss = good_hit = good_miss = 0; timestamp = access_info.current_time_sec; } }