/* *************************************************
 * *********** README ******************************
 * *************************************************
 *
 * COMPILE : make
 * RUN : ./locktests -n <number of concurent process> -f <test file> [-P]
 *
 * GOAL : This test tries to stress the fcntl locking functions.  A
 * master process sets a lock on a file region (this is called "byte
 * range locking").  Some slave processes try to perform operations on
 * this region, such as read, write, set a new lock ... The expected
 * results of these operations are known.  If the operation result is
 * the same as the expected one, the test suceeds, else it fails.
 *
 *
 *
 * Slaves are concurent processes or thread.
 * -n <num>  : Number of threads to use (mandatory).
 * -f <file> : Run the test on given test file defined by the -f option (mandatory).
 * -c <num>  : Number of clients to connect before starting the tests.
 *
 * HISTORY : This program was written to stress NFSv4 locks.
 * EXAMPLE : ./locktests -n 50 -f /file/system/to/test
 *
 *
 * Vincent ROQUETA 2005 - vincent.roqueta@ext.bull.net
 * BULL S.A.
 */

#include "locktests.h"

int MAXLEN = 64;
int MAXTEST = 10;
extern int maxClients;
extern int fdServer;

char message[M_SIZE];
int slaveReader;
int masterReader;
int slaveWriter;

/* Which lock will be applied by the master process on test startup */
int LIST_LOCKS[] = { READLOCK, WRITELOCK,
	READLOCK, WRITELOCK,
	READLOCK, WRITELOCK,
	READLOCK, WRITELOCK,
	BYTELOCK_READ, BYTELOCK_WRITE
};

/* The operations the slave processes will try to perform */
int LIST_TESTS[] = { WRONLY, WRONLY,
	RDONLY, RDONLY,
	READLOCK, WRITELOCK,
	WRITELOCK, READLOCK,
	BYTELOCK_READ, BYTELOCK_WRITE
};

/* List of test names */
char *LIST_NAMES_TESTS[] = { "WRITE ON A READ  LOCK",
	"WRITE ON A WRITE LOCK",
	"READ  ON A READ  LOCK",
	"READ  ON A WRITE LOCK",
	"SET A READ  LOCK ON A READ  LOCK",
	"SET A WRITE LOCK ON A WRITE LOCK",
	"SET A WRITE LOCK ON A READ  LOCK",
	"SET A READ  LOCK ON A WRITE LOCK",
	"READ LOCK THE WHOLE FILE BYTE BY BYTE",
	"WRITE LOCK THE WHOLE FILE BYTE BY BYTE"
};

/* List of expected test results, when slaves are processes */
int LIST_RESULTS_PROCESS[] = { SUCCES, SUCCES,
	SUCCES, SUCCES,
	SUCCES, ECHEC,
	ECHEC, ECHEC,
	SUCCES, SUCCES
};

/* List of expected test results, when slaves are threads */
int LIST_RESULTS_THREADS[] = { SUCCES, SUCCES,
	SUCCES, SUCCES,
	SUCCES, SUCCES,
	SUCCES, SUCCES,
	ECHEC, ECHEC
};

int *LIST_RESULTS = NULL;
char *eType = NULL;

int TOTAL_RESULT_OK[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

void *slave(void *data);
int (*finish) (int a);

int finishProcess(int a)
{
	exit(a);
}

int (*load) (void);

struct dataPub dp;

/* Functions to access tests/tests names/tests results*/
int testSuiv(int n)
{
	return LIST_TESTS[n];
}

int resAttSuiv(int n)
{
	return LIST_RESULTS[n];
}

char *nomTestSuiv(int n)
{
	return LIST_NAMES_TESTS[n];
}

int lockSuiv(int n)
{
	return LIST_LOCKS[n];
}

/* Verify the test result is the expected one */
int matchResult(int r, int n)
{

	P("r=%d\n", r);
	if (r == LIST_RESULTS[n])
		return 1;
	else
		return 0;
}

/* Increments the number of process which have successfully passed the test */
void counter(int r, int n)
{
	TOTAL_RESULT_OK[n] += matchResult(r, n);
}

/* Special case for test 'lock file byte byte by byte'.
 * We ensure each byte is correctly locked.
 */
void validationResults(int n)
{
	int i, u, l, fsize;
	struct flock request;

	fsize = dp.nclnt * (maxClients + 1);
	TOTAL_RESULT_OK[n] = 0;
	l = FALSE;
	u = TRUE;

	/* If the expected operation result is a success, we will have to increase the number of correct results */
	if (LIST_RESULTS[n]) {
		l = TRUE;
		u = FALSE;
	}

	for (i = 0; i < fsize; i++) {
		request.l_type = F_WRLCK;
		request.l_whence = SEEK_SET;
		request.l_start = i;
		request.l_len = 1;
		fcntl(dp.fd, F_GETLK, &request);
		/* Ensure the lock is correctly set */
		if (request.l_type != F_UNLCK)
			TOTAL_RESULT_OK[n] += l;
		else
			TOTAL_RESULT_OK[n] += u;
	}
}

int initTest(void)
{

	P("Master opens %s\n", dp.fname);
	dp.fd = open(dp.fname, OPENFLAGS, MANDMODES);
	if (dp.fd < 0) {
		perror("lock test : can't open test file :");
		finish(1);
	}
	P("fd=%d\n", dp.fd);
	return 0;
}

struct dataChild *initClientFork(int i)
{
	struct dataPriv *dpr;
	struct dataChild *df;

	/* Initialize private data fields */
	dpr = malloc(sizeof(struct dataPriv));
	df = malloc(sizeof(struct dataChild));
	dpr->whoami = i;
	df->dp = &dp;
	df->dpr = dpr;
	/* Initialize master to client pipe */
	dp.lclnt[i] = malloc(sizeof(int) * 2);
	if (pipe(dp.lclnt[i]) < 0) {
		perror("Impossible to create pipe\n");
		exit(1);
	}
	P("Initialization %d\n", i);
	write(0, ".", 1);
	return df;
}

int initialize(int clnt)
{

	/* Initialize private data fields */
	printf("Init\n");
	dp.nclnt = clnt;
	dp.lclnt = malloc(sizeof(int *) * clnt);
	dp.lthreads = malloc(sizeof(pthread_t) * clnt);

	/* Initialize client to master pipe */
	if (pipe(dp.master) < 0) {
		perror("Master pipe creation error\n");
		exit(1);
	}
	printf("%s initialization\n", eType);
	load();
	initTest();

	return 0;
}

void cleanClient(struct dataChild *df)
{
	int i;
	i = df->dpr->whoami;
	free(dp.lclnt[i]);
	free(df->dpr);
	free(df);
}

void clean(void)
{
	free(dp.lthreads);
	free(dp.lclnt);
}

int loadProcess(void)
{
	int i;
	struct dataChild *df;
	for (i = 0; i < dp.nclnt; i++) {
		df = initClientFork(i);
		if (!fork()) {
			P("Running slave num: %d\n", df->dpr->whoami);
			write(0, ".", 1);
			slave((void *)df);
			cleanClient(df);
			exit(0);
		}
	}
	return 0;
}

void lockWholeFile(struct flock *request)
{
	request->l_whence = SEEK_SET;
	request->l_start = 0;
	/* Lock the whole file */
	request->l_len = 0;
}

void selectTest(int n, struct s_test *test)
{

	test->test = testSuiv(n);
	test->resAtt = resAttSuiv(n);
	test->nom = nomTestSuiv(n);
	test->type = lockSuiv(n);
}

/* Final test report */
int report(int clnt)
{
	int rc = 0;
	int i;
	int totalClients;
	totalClients = clnt * (maxClients + 1);
	printf
	    ("\n%s number : %d - Remote clients: %d local client 1 - Total client %d - Total concurent tests: %d\n",
	     eType, clnt, maxClients, maxClients + 1, totalClients);
	printf("%s number running test successfully :\n", eType);
	for (i = 0; i < MAXTEST; i++) {
		if (TOTAL_RESULT_OK[i] != totalClients)
			rc = 1;

		printf("%d %s of %d successfully ran test : %s\n",
		       TOTAL_RESULT_OK[i], eType, totalClients,
		       LIST_NAMES_TESTS[i]);
    }
    return rc;
}

int serverSendLocal(void)
{
	int i;
	/* Synchronize slave processes */
	/* Configure slaves for test */

	for (i = 0; i < dp.nclnt; i++)
		write(dp.lclnt[i][1], message, M_SIZE);
	return 0;

}

void serverSendNet(void)
{
	writeToAllClients(message);
}

int serverReceiveNet(void)
{
	int i, c;
	for (c = 0; c < maxClients; c++) {
		for (i = 0; i < dp.nclnt; i++) {
			serverReceiveClient(c);
		}
	}
	return 0;
}

int serverReceiveLocal(void)
{
	int i;
	for (i = 0; i < dp.nclnt; i++)
		read(masterReader, message, M_SIZE);
	return 0;
}

int clientReceiveLocal(void)
{
	read(slaveReader, message, M_SIZE);
	return 0;
}

int clientSend(void)
{
	write(slaveWriter, message, M_SIZE);
	return 0;
}

int serverSend(void)
{
	serverSendNet();
	serverSendLocal();
	return 0;
}

int serverReceive(void)
{
	serverReceiveNet();
	serverReceiveLocal();
	return 0;
}

/* binary structure <-> ASCII functions used to ensure data will be correctly used over
 * the network, especially when multiples clients do not use the same hardware architecture.
 */
int serializeTLock(struct s_test *tLock)
{
	memset(message, 0, M_SIZE);
	sprintf(message, "T:%d:%d:%d::", tLock->test, tLock->type,
		tLock->resAtt);
	return 0;
}

void unSerializeTLock(struct s_test *tLock)
{
	sscanf(message, "T:%d:%d:%d::", &(tLock->test), &(tLock->type),
	       &(tLock->resAtt));
	memset(message, 0, M_SIZE);

}

void serializeFLock(struct flock *request)
{
	int len, pid, start;
	memset(message, 0, M_SIZE);
	len = (int)request->l_len;
	pid = (int)request->l_pid;
	start = (int)request->l_start;
	/* Beware to length of integer conversions ... */
	sprintf(message, "L:%hd:%hd:%d:%d:%d::",
		request->l_type, request->l_whence, start, len, pid);
}

void serializeResult(int result)
{
	memset(message, 0, M_SIZE);
	sprintf(message, "R:%d::", result);

}

void unSerializeResult(int *result)
{
	sscanf(message, "R:%d::", result);
}

void unSerializeFLock(struct flock *request)
{
	int len, pid, start;
	sscanf(message, "L:%hd:%hd:%d:%d:%d::",
	       &(request->l_type), &(request->l_whence), &start, &len, &pid);
	request->l_start = (off_t) start;
	request->l_len = (off_t) len;
	request->l_pid = (pid_t) pid;
}

int serverSendLockClient(struct flock *request, int client)
{
	serializeFLock(request);
	return serverSendClient(client);
}

int serverSendLockLocal(struct flock *request, int slave)
{
	serializeFLock(request);
	return write(dp.lclnt[slave][1], message, M_SIZE);
}

int getLockSection(struct flock *request)
{
	memset(message, 0, M_SIZE);
	clientReceiveLocal();
	unSerializeFLock(request);
	return 0;
}

int sendLockTest(struct s_test *tLock)
{
	serializeTLock(tLock);
	serverSend();
	return 0;
}

int getLockTest(struct s_test *tLock)
{
	clientReceiveLocal();
	unSerializeTLock(tLock);
	return 0;
}

int sendResult(int result)
{
	serializeResult(result);
	clientSend();
	return 0;
}

int getResults(int ntest)
{
	int i, c;
	int result = 0;
	/* Add remote test results */
	for (c = 0; c < maxClients; c++) {
		for (i = 0; i < dp.nclnt; i++) {
			serverReceiveClient(c);
			unSerializeResult(&result);
			counter(result, ntest);

		}
	}
	/* Add local test results */
	for (i = 0; i < dp.nclnt; i++) {
		read(masterReader, message, M_SIZE);
		unSerializeResult(&result);
		counter(result, ntest);
	}

	return 0;
}

#ifdef DEBUG
#define P(a,b)  memset(dbg,0,16);sprintf(dbg,a,b);write(0,dbg,16);
#endif

/* In the case of a network use, the master of the client application si only
 * a 'repeater' of information. It resends server-master instructions to its own slaves.
 */
void masterClient(void)
{
	fd_set fdread;
	struct timeval tv;
	int n, i, r, m, start;
#ifdef DEBUG
	char dbg[16];
#endif
	struct flock lock;
	int t;

	masterReader = dp.master[0];
	FD_ZERO(&fdread);
	tv.tv_sec = 50;
	tv.tv_usec = 0;
	n = fdServer > masterReader ? fdServer : masterReader;
	printf("Master Client - fdServer=%d\n", fdServer);
	while (1) {
		/* Add slave and server pipe file descriptors */
		FD_ZERO(&fdread);
		FD_SET(fdServer, &fdread);
		FD_SET(masterReader, &fdread);
		r = select(n + 1, &fdread, NULL, NULL, &tv);
		if (r < 0) {
			perror("select:\n");
			continue;
		}
		if (r == 0) {
			exit(0);
		}

		if (FD_ISSET(fdServer, &fdread)) {
			/* We just have received information from the server.
			 * We repeat it to slaves.
			 */
			i = readFromServer(message);
			t = message[0];
			switch (t) {
			case 'L':
				/* Lock instruction. We need to send a different section to lock to each process */
				unSerializeFLock(&lock);
				start = lock.l_start;
				for (i = 0; i < dp.nclnt; i++) {
					lock.l_start = start + i;
					serializeFLock(&lock);
					write(dp.lclnt[i][1], message, M_SIZE);
				}
				printf("\n");
				continue;
			case 'T':
				/* Test instruction. Ensure server is not sending the END(ish) instruction to end tests */
				/* To be rewritten asap */
				m = atoi(&(message[2]));
				if (m == END)
					break;
				if (m == CLEAN)
					printf("\n");

				serverSendLocal();
				continue;
			}
			break;
		} else {
			/* Else, we read information from slaves and repeat them to the server */
			for (i = 0; i < dp.nclnt; i++) {
				r = read(masterReader, message, M_SIZE);
				r = write(fdServer, message, M_SIZE);
				if (r < 0)
					perror("write : ");

			}
			continue;
		}
	}

	/* Receive the END(ish) instruction */

	/* Repeat it to the slaves */
	printf("Exitting...\n");
	serverSendLocal();

	/* Ok, we can quit */
	printf("Bye :)\n");

}

int master(void)
{
	int i, n, bl;
	int clnt;
	char tmp[MAXLEN], *buf;
#ifdef DEBUG
	char dbg[16];
#endif
	struct flock request;
	struct s_test tLock;
	enum state_t state;
	int offset;
	/* A test sentence written in the file */
	char phraseTest[] =
	    "Ceci est une phrase test ecrite par le maitre dans le fichier";
	bl = -1;
	clnt = dp.nclnt;
	masterReader = dp.master[0];
	state = SELECT;
	/* Start with the first test ;) */
	n = 0;
	printf("\n--------------------------------------\n");
	while (1) {
		switch (state) {
		case SELECT:
			/* Select the test to perform   */
			printf("\n");
			E("Master: SELECT");
			selectTest(n, &tLock);
			state = tLock.type;
			bl = 0;
			if (n < MAXTEST) {
				memset(tmp, 0, MAXLEN);
				sprintf(tmp, "TEST : TRY TO %s:",
					LIST_NAMES_TESTS[n]);
				write(0, tmp, strlen(tmp));
			} else
				state = END;
			P("state=%d\n", state);
			n += 1;
			continue;

		case RDONLY:
		case WRONLY:

		case READLOCK:
			P("Read lock :%d\n", state);
			request.l_type = F_RDLCK;
			state = LOCK;
			continue;

		case WRITELOCK:
			P("Write lock :%d\n", state);
			request.l_type = F_WRLCK;
			state = LOCK;
			continue;

		case LOCK:
			/* Apply the wanted lock */
			E("Master: LOCK");
			write(dp.fd, phraseTest, strlen(phraseTest));
			lockWholeFile(&request);
			if (fcntl(dp.fd, F_SETLK, &request) < 0) {
				perror("Master: can't set lock\n");
				perror("Echec\n");
				exit(0);
			}
			E("Master");
			state = SYNC;
			continue;

		case BYTELOCK_READ:
			bl = 1;
			request.l_type = F_RDLCK;
			state = SYNC;
			continue;

		case BYTELOCK_WRITE:
			bl = 1;
			request.l_type = F_WRLCK;
			state = SYNC;
			continue;

		case BYTELOCK:
			/* The main idea is to lock all the bytes in a file. Each slave process locks one byte.
			 *
			 * We need :
			 * - To create a file of a length equal to the total number of slave processes
			 * - send the exact section to lock to each slave
			 * - ensure locks have been correctly set
			 */

			/* Create a string to record in the test file. Length is exactly the number of sub process */
			P("Master: BYTELOCK: %d\n", state);
			buf = malloc(clnt * (maxClients + 1));
			memset(buf, '*', clnt);
			write(dp.fd, buf, clnt);
			free(buf);

			/* Each slave process re-writes its own field to lock */
			request.l_whence = SEEK_SET;
			request.l_start = 0;
			request.l_len = 1;

			/* Start to send sections to lock to remote process (network clients) */
			for (i = 0; i < maxClients; i++) {
				/* Set the correct byte to lock */
				offset = (i + 1) * clnt;
				request.l_start = (off_t) offset;
				serverSendLockClient(&request, i);
			}

			/* Now send sections to local processes */
			for (i = 0; i < clnt; i++) {
				request.l_start = i;
				serverSendLockLocal(&request, i);
			}
			state = RESULT;
			continue;

		case SYNC:
			sendLockTest(&tLock);
			if (bl) {
				state = BYTELOCK;
				continue;
			}

			if (n < MAXTEST + 1)
				state = RESULT;
			else
				state = END;
			continue;

		case RESULT:
			/* Read results by one */
			getResults(n - 1);
			if (bl)
				validationResults(n - 1);
			state = CLEAN;
			continue;

		case CLEAN:
			/* Ask the clients to stop testing ... */
			tLock.test = CLEAN;
			serializeTLock(&tLock);
			serverSend();
			/* ... and wait for an ack before closing */
			serverReceive();
			/* Ignore message content : that is only an ack */

			/* close and open file */
			close(dp.fd);
			initTest();
			state = SELECT;
			continue;
		case END:
			tLock.test = END;
			serializeTLock(&tLock);
			serverSend();
			sleep(2);
			break;

			printf("(end)\n");
			exit(0);

		}
		break;
	}

	return report(clnt);
}

/* Slave process/thread */

void *slave(void *data)
{
	struct dataChild *df;
	int i, a, result, ftest;
	struct s_test tLock;
	struct flock request;
	char tmp[16];
#ifdef DEBUG
	char dbg[16];
#endif
	char *phraseTest = "L'ecriture a reussi";
	int len;
	enum state_t state;

	result = -1;
	ftest = -1;
	len = strlen(phraseTest);
	df = (struct dataChild *)data;
	i = df->dpr->whoami;
	P("Slave n=%d\n", i);
	slaveReader = dp.lclnt[i][0];
	slaveWriter = dp.master[1];
	state = SYNC;
	errno = 0;
	memset(tmp, 0, 16);
	while (1) {
		switch (state) {
		case SELECT:
		case SYNC:
			getLockTest(&tLock);
			state = tLock.test;
			P("Slave State=%d\n", state);

			continue;
		case RDONLY:
			/* Try to read a file */
			P("TEST READ ONLY %d\n", RDONLY);
			if ((ftest = open(dp.fname, O_RDONLY | O_NONBLOCK)) < 0) {
				result = ECHEC;
				state = RESULT;
				if (dp.verbose)
					perror("Open:");
				continue;
			}
			P("fd=%d\n", ftest);
			a = read(ftest, tmp, 16);
			if (a < 16)
				result = ECHEC;
			else
				result = SUCCES;
			state = RESULT;
			continue;

		case WRONLY:
			/* Try to write a file */
			P("TEST WRITE ONLY %d\n", WRONLY);
			if ((ftest = open(dp.fname, O_WRONLY | O_NONBLOCK)) < 0) {
				result = ECHEC;
				state = RESULT;
				if (dp.verbose)
					perror("Open read only:");
				continue;
			}
			P("fd=%d\n", ftest);
			if (write(ftest, phraseTest, len) < len)
				result = ECHEC;
			else
				result = SUCCES;
			state = RESULT;
			continue;

		case LOCK:

		case READLOCK:
			/* Try to read a read-locked section */
			P("READ LOCK %d\n", F_RDLCK);
			if ((ftest = open(dp.fname, O_RDONLY | O_NONBLOCK)) < 0) {
				result = ECHEC;
				state = RESULT;
				if (dp.verbose)
					perror("Open read-write:");
				continue;
			}

			P("fd=%d\n", ftest);
			/* Lock the whole file */
			request.l_type = F_RDLCK;
			request.l_whence = SEEK_SET;
			request.l_start = 0;
			request.l_len = 0;

			if (fcntl(ftest, F_SETLK, &request) < 0) {
				if (dp.verbose || errno != EAGAIN)
					perror("RDONLY: fcntl");
				result = ECHEC;
			} else
				result = SUCCES;
			state = RESULT;
			continue;

		case WRITELOCK:
			/* Try to write a file */
			P("WRITE LOCK %d\n", F_WRLCK);
			if ((ftest = open(dp.fname, O_WRONLY | O_NONBLOCK)) < 0) {
				result = ECHEC;
				state = RESULT;
				if (dp.verbose)
					perror("\nOpen:");
				continue;
			}
			/* Lock the whole file */
			P("fd=%d\n", ftest);
			request.l_type = F_WRLCK;
			request.l_whence = SEEK_SET;
			request.l_start = 0;
			request.l_len = 0;

			if (fcntl(ftest, F_SETLK, &request) < 0) {
				if (dp.verbose || errno != EAGAIN)
					perror("\nWRONLY: fcntl");
				result = ECHEC;
			} else
				result = SUCCES;
			state = RESULT;
			continue;

		case BYTELOCK_READ:
			P("BYTE LOCK READ: %d\n", state);
			state = BYTELOCK;
			continue;

		case BYTELOCK_WRITE:
			P("BYTE LOCK WRITE: %d\n", state);
			state = BYTELOCK;
			continue;

		case BYTELOCK:
			/* Wait for the exact section to lock. The whole file is sent by the master */
			P("BYTE LOCK %d\n", state);
			getLockSection(&request);
			if ((ftest = open(dp.fname, O_RDWR | O_NONBLOCK)) < 0) {
				result = ECHEC;
				state = RESULT;
				if (dp.verbose)
					perror("\nOpen:");
				continue;
			}

			if (fcntl(ftest, F_SETLK, &request) < 0) {
				if (dp.verbose || errno != EAGAIN)
					perror("\nBTLOCK: fcntl");
				result = ECHEC;
				state = RESULT;
				continue;
			}
			/* Change the character at the given position for an easier verification */
			a = lseek(ftest, request.l_start, SEEK_SET);
			write(ftest, "=", 1);
			result = SUCCES;
			state = RESULT;
			continue;

		case RESULT:
			if (result == SUCCES)
				write(0, "=", 1);
			else
				write(0, "x", 1);
			P("RESULT: %d\n", result);
			sendResult(result);
			state = SYNC;
			continue;

		case CLEAN:
			close(ftest);
			/* Send CLEAN Ack */
			sendResult(result);
			state = SYNC;
			continue;

		case END:
			E("(End)\n");
			finish(0);
			printf("Unknown command\n");
			finish(1);

		}
	}

}

char *nextArg(int argc, char **argv, int *i)
{
	if (((*i) + 1) < argc) {
		(*i) += 1;
		return argv[(*i)];
	}
	return NULL;
}

int usage(void)
{
	printf("locktest -n <number of process> -f <test file> [-T]\n");
	printf("Number of child process must be higher than 1\n");
	exit(0);
	return 0;
}

int main(int argc, char **argv)
{
	int rc = 0;
	int i, nThread = 0;
	char *tmp;
	int type = 0;
	int clients;
	type = PROCESS;
	dp.fname = NULL;
	dp.verbose = 0;
	int server = 1;
	char *host;

	host = NULL;
	clients = 0;

	for (i = 1; i < argc; i++) {

		if (!strcmp("-n", argv[i])) {
			if (!(tmp = nextArg(argc, argv, &i)))
				usage();
			nThread = atoi(tmp);
			continue;
		}

		if (!strcmp("-f", argv[i])) {
			if (!(dp.fname = nextArg(argc, argv, &i)))
				usage();
			continue;
		}
		if (!strcmp("-v", argv[i])) {
			dp.verbose = TRUE;
			continue;
		}
		if (!strcmp("-c", argv[i])) {
			if (!(clients = atoi(nextArg(argc, argv, &i))))
				usage();
			continue;
		}

		if (!strcmp("--server", argv[i])) {
			if (!(host = nextArg(argc, argv, &i)))
				usage();
			server = 0;
			continue;
		}
		printf("Ignoring unknown option: %s\n", argv[i]);
	}

	if (server) {
		if (!(dp.fname && nThread))
			usage();
		if (clients > 0) {
			configureServer(clients);
			setupClients(type, dp.fname, nThread);
		}
	} else {
		configureClient(host);
		dp.fname = malloc(512);
		memset(dp.fname, 0, 512);
		getConfiguration(&type, dp.fname, &nThread);
	}

	if (dp.verbose)
		printf("By process.\n");
	load = loadProcess;
	eType = "process";
	finish = finishProcess;
	LIST_RESULTS = LIST_RESULTS_PROCESS;
	initialize(nThread);
	if (server) {
		rc = master();
	} else {
		masterClient();
		free(dp.fname);
	}
	clean();

	return rc;
}