/**
 * Start function
 */
void IpfixCsExporter::performStart()
{
	writeFileHeader();
	addToCurTime(&nextChunkTimeout, maxChunkBufferTime*1000);
	addToCurTime(&nextFileTimeout, maxFileCreationInterval*1000);
	registerTimeout();
}
/**
 * Creates an output file and writes the CS_Ipfix_file_header
 */
void IpfixCsExporter::writeFileHeader()
{
	closeFile();

	currentFileSize = sizeof(CS_IPFIX_MAGIC)+sizeof(Ipfix_basic_flow_sequence_chunk_header);

	time_t timestamp = time(0);
	tm *st = localtime(&timestamp);

	struct stat sta;

	char prefix[512];
	snprintf(prefix, ARRAY_SIZE(prefix), "%s%s%02d%02d%02d-%02d%02d",
			destinationPath.c_str(), filenamePrefix.c_str(), st->tm_year+1900,st->tm_mon+1,st->tm_mday,st->tm_hour,st->tm_min);
	uint32_t i = 1;
	while (i<0xFFFFFFFE) {
		snprintf(currentFilename, ARRAY_SIZE(currentFilename), "%s_%03d", prefix, i);
		errno = 0;
		if (stat(currentFilename,&sta) != 0) {
			if (errno != 2) {
				//check error code
				msg(MSG_ERROR, "IpfixCsExporter: stat() on filename %s returned with error %i (%s)", currentFilename, errno, strerror(errno));
			} else {
				// errno==2 means there is no file present
				break;
			}
		}
		i++;
	}

	snprintf(currentTmpname, ARRAY_SIZE(currentTmpname), "%s/._%s%02d%02d%02d-%02d%02d_%03d.part",
			destinationPath.c_str(), filenamePrefix.c_str(), st->tm_year+1900,st->tm_mon+1,st->tm_mday,st->tm_hour,st->tm_min, i);


	if (i==0xFFFFFFFF) {
		THROWEXCEPTION("failed to determine index for filename postfix (i==0xFFFFFFFF). Something went terribly wrong ....");
	}

	// fix: cs_export is too stupid to read incomplete file. Let's create a temporary file ....
	currentFile = fopen(currentTmpname, "wb");
	if (currentFile == NULL) {
		THROWEXCEPTION("Could not open file for writing. Check permissions.");
	}

	if (fwrite(CS_IPFIX_MAGIC, sizeof(CS_IPFIX_MAGIC), 1, currentFile)==0){
		THROWEXCEPTION("Could not write file header. Check disk space.");
	}

	//new Timeouts:
	addToCurTime(&nextChunkTimeout, maxChunkBufferTime*1000);
	addToCurTime(&nextFileTimeout, maxFileCreationInterval*1000);
}
/**
 * Writes the content of chunkList to the output file
 */
void IpfixCsExporter::writeChunkList()
{
	Ipfix_basic_flow_sequence_chunk_header csChunkHeader;

	csChunkHeader.ipfix_type = htons(0x0008);
	csChunkHeader.chunk_length = htonl(chunkListSize*sizeof(Ipfix_basic_flow)+4);
	csChunkHeader.flow_count = htonl(chunkListSize);

	if (fwrite(&csChunkHeader, sizeof(csChunkHeader), 1, currentFile)==0){
		THROWEXCEPTION("Could not chunk header. Check disk space.");
	}

	msg(MSG_DEBUG, "IpfixCsExporter: writing %u records to disk", chunkListSize);

	while (!chunkList.empty()){
		Ipfix_basic_flow* flow = chunkList.front();
		if (fwrite(flow, sizeof(Ipfix_basic_flow), 1, currentFile)==0){
			THROWEXCEPTION("Could not write basic flow data. Check disk space.");
		}

		chunkList.pop_front();
		chunkListSize--;
		delete flow;
	}
	addToCurTime(&nextChunkTimeout, maxChunkBufferTime*1000);
}
/**
 * registers timeout for function onTimeout in Timer
 * (used to send records which are cached)
 */
void IpfixNetflowExporter::registerTimeout()
{
	if (timeoutRegistered) return;

	addToCurTime(&nextTimeout, recordCacheTimeout);
	timer->addTimeout(this, nextTimeout, NULL);
	timeoutRegistered = true;
}
/**
 * Registers timeout for function onTimeout in Timer
 * to compute the criterias for every interval
 */
void P2PDetector::registerTimeout()
{
	if (timeoutRegistered) return;

	addToCurTime(&nextTimeout, intLength*1000);
	timer->addTimeout(this, nextTimeout, NULL);
	timeoutRegistered = true;
}
void IpfixCsExporter::onTimeout(void* dataPtr)
{
	timeoutRegistered = false;
	struct timeval now;
	gettimeofday(&now, 0);
	//check if this is one of the desired timeouts
	if (nextFileTimeout.tv_sec <= now.tv_sec) {
		//close File, add new one
		writeChunkList();
		writeFileHeader();
		addToCurTime(&nextChunkTimeout, maxChunkBufferTime*1000);
		addToCurTime(&nextFileTimeout, maxFileCreationInterval*1000);
	} else if (nextChunkTimeout.tv_sec <= now.tv_sec) {
		writeChunkList();
		addToCurTime(&nextChunkTimeout, maxChunkBufferTime*1000);
	}

	registerTimeout();
}
/**
 * sends records to the network
 * @param forcesend to send all records regardless how many were cached
 */
void IpfixNetflowExporter::sendRecords(bool forcesend)
{
	if (recordCache.size() == 0) return;

	// we have a maximum of 30 data records in one packet
	if ((recordCache.size() >= 30) || forcesend) {
		sendPacket();
	}
	// set next timeout
	addToCurTime(&nextTimeout, recordCacheTimeout);
}
/**
 * thread which regularly scans hashtable for expired buckets/flows
 */
void BaseAggregator::exporterThread()
{
    struct timeval inttimer;
    gettimeofday(&inttimer, 0);
    //struct timeval difftime;
    //REQUIRE(timeval_subtract(&difftime, &stoptime, &starttime) == 0);


    /*timespec req;
    req.tv_sec = pollInterval / 1000;
    req.tv_nsec = (pollInterval % 1000) * 1000;*/


    registerCurrentThread();

    msg(MSG_INFO, "Polling aggregator each %u msec", pollInterval);
    while (!exitFlag) {
        addToCurTime(&inttimer, pollInterval);

        struct timeval curtime;
        gettimeofday(&curtime, 0);

        struct timeval difftime;
        if (timeval_subtract(&difftime, &inttimer, &curtime)!=1) {
            // restart nanosleep with the remaining sleep time
            // if we got interrupted by a signal
            struct timespec ts;
            TIMEVAL_TO_TIMESPEC(&difftime, &ts);
            while (nanosleep(&ts, &ts) == -1 && errno == EINTR);
        }

        gettimeofday(&curtime, 0);
        msg(MSG_VDEBUG,"Aggregator: starting Export");
        for (size_t i = 0; i < rules->count; i++) {
            rules->rule[i]->hashtable->expireFlows();
        }
        struct timeval endtime;
        gettimeofday(&endtime, 0);
        timeval_subtract(&difftime, &endtime, &curtime);

        msg (MSG_VDEBUG,"Aggregator: export took %.03f secs", (float)difftime.tv_usec/1000000+difftime.tv_sec);
    }

    if (getShutdownProperly()) {
        for (size_t i = 0; i < rules->count; i++) {
            rules->rule[i]->hashtable->expireFlows(true);
        }
    }

    unregisterCurrentThread();
}
void DelayedDeleter::addCfgNode(CfgNode* node)
{
	struct timespec deadline;

#if defined(DEBUG)
	mutex.lock();
#endif

	addToCurTime(&deadline, wait_in_ms);


	timer.addTimeout(this, deadline, (void*)node);

#if defined(DEBUG)
	list.push_back(node);
	mutex.unlock();
#endif
}