/*
 * See the files COPYING and README for copyright information and how to reach 
 * the author.
 *
 *  $Id: poller.c,v 1.1.1.1 2006/01/14 17:06:40 lordjaxom Exp $
 */

#include "poller.h"
#include "pipe.h"
#include "process.h"

cBurnPoller::cBurnPoller(void):
		mCount(0),
		mBuffers(NULL)
{
}

cBurnPoller::~cBurnPoller()
{
	if (mBuffers != NULL) {
		for (int i = 0; i < mCount; ++i)
			free(mBuffers[i]);
		free(mBuffers);
	}
}

void cBurnPoller::Set(cBurnPipes *Pipes)
{
	mCount = 0;
	mPipes = Pipes;
	
	for (cBurnPipe *pipe = mPipes->First(); pipe != NULL; 
		 pipe = mPipes->Next(pipe)) {
		if (*pipe >= mCount)
			mCount = *pipe + 1;
	}

	mBuffers = MALLOC(char*, mCount);
	memset(mBuffers, 0, sizeof(char*) * mCount);
}

bool cBurnPoller::Poll(int TimeoutMs)
{
	struct timeval tv;

	Clear();
	for (cBurnPipe *pipe = mPipes->First(); pipe != NULL; 
		 pipe = mPipes->Next(pipe))
		FD_SET(*pipe, &mReadFds);
	
	tv.tv_sec = TimeoutMs / 1000;
	tv.tv_usec = (TimeoutMs % 1000) * 1000;
	if (select(mCount, &mReadFds, NULL, NULL, &tv) == -1) {
		if (FATALERRNO) {
			esyslog("ERROR[burn]: failure watching processes: %m");
			return false;
		}
		Clear();
	}
	return true;
}

bool cBurnPoller::Read(cBurnProcess *Process)
{
	cBurnPipe *next; // in case pipe gets deleted within loop
	bool result = true;
	for (cBurnPipe *pipe = mPipes->First(); pipe != NULL; pipe = next) {
		int result;
		int used;
		char *ptr;
		
		next = mPipes->Next(pipe); 

		if (!FD_ISSET(*pipe, &mReadFds))
			continue;

		if (mBuffers[*pipe] == NULL) {
			mBuffers[*pipe] = MALLOC(char, BUFSIZ);
			*mBuffers[*pipe] = '\0';
		}
		used = strlen(mBuffers[*pipe]);

		if ((result = read(*pipe, mBuffers[*pipe] + used, 
						   sizeof(char) * (BUFSIZ - used - 1))) == -1) {
			if (FATALERRNO) {
				esyslog("ERROR[burn]: failure reading process: %m");
				result = false;
				break;
			}
			continue;
		}

		if (result == 0) {
			if (!pipe->Exit(true)) {
				esyslog("ERROR[burn]: bad exit code from process, job failed");
				result = false;
				break;
			}
			mPipes->Del(pipe);
			continue;
		}
		used += result;
		mBuffers[*pipe][used] = '\0';

		while ((ptr = memmultichr(mBuffers[*pipe], "\012\015", used)) != NULL) {
			*ptr = '\0';

			Process->PutLine(mBuffers[*pipe]);

			used -= ptr - mBuffers[*pipe] + 1;
			memmove(mBuffers[*pipe], ptr + 1, used + 1);
		}

		if (used == sizeof(char) * (BUFSIZ - 1)) {
			esyslog("ERROR[burn]: line too long while reading subprocess");
			result = false;
		}
	}
	return result;
}
