/*
 *
 *   Copyright (c) International Business Machines  Corp., 2002
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/*
 * NAME
 *      diotest_routines.c
 *
 * DESCRIPTION
 *	Functions that are used in diotest programs.
 *	fillbuf(), bufcmp(), filecmp()
 *	forkchldrn(), waitchldrn(), killchldrn()
 *
 * History
 *	04/10/2002	Narasimha Sharoff
 *
 * RESTRICTIONS
 *	None
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/uio.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>

#include "diotest_routines.h"

/* **** Routines for buffer actions, comparisions **** */

/*
 * fillbuf: Fill buffer of given size with given character value
 * vfillbuf: Fill the vector array
*/
void fillbuf(char *buf, int count, char value)
{
	while (count > 0) {
		strncpy(buf, &value, 1);
		buf++;
		count = count - 1;
	}
}

void vfillbuf(struct iovec *iv, int vcnt, char value)
{
	int i;

	for (i = 0; i < vcnt; iv++, i++) {
		fillbuf(iv->iov_base, iv->iov_len, (char)value);
	}
}

/*
 * bufcmp: Compare two buffers
 * vbufcmp: Compare two buffers of two io arrays
*/
int bufcmp(char *b1, char *b2, int bsize)
{
	int i;

	for (i = 0; i < bsize; i++) {
		if (strncmp(&b1[i], &b2[i], 1)) {
			fprintf(stderr,
				"bufcmp: offset %d: Expected: 0x%x, got 0x%x\n",
				i, b1[i], b2[i]);
			return (-1);
		}
	}
	return (0);
}

int vbufcmp(struct iovec *iv1, struct iovec *iv2, int vcnt)
{
	int i;

	for (i = 0; i < vcnt; iv1++, iv2++, i++) {
		if (bufcmp(iv1->iov_base, iv2->iov_base, iv1->iov_len) < 0) {
			fprintf(stderr, "Vector: %d, iv1base=%s, iv2base=%s\n",
				i, (char *)iv1->iov_base,
				(char *)iv2->iov_base);
			return (-1);
		}
	}
	return 0;
}

/*
 * compare_files: Compares two files
*/
int filecmp(char *f1, char *f2)
{
	int i;
	int fd1, fd2;
	int ret1, ret2 = 0;
	char buf1[BUFSIZ], buf2[BUFSIZ];

	/* Open the file for read */
	if ((fd1 = open(f1, O_RDONLY)) == -1) {
		fprintf(stderr, "compare_files: open failed %s: %s",
			f1, strerror(errno));
		return (-1);
	}
	if ((fd2 = open(f2, O_RDONLY)) == -1) {
		fprintf(stderr, "compare_files: open failed %s: %s",
			f2, strerror(errno));
		close(fd1);
		return (-1);
	}

	/* Compare the files */
	while ((ret1 = read(fd1, buf1, BUFSIZ)) > 0) {
		ret2 = read(fd2, buf2, BUFSIZ);
		if (ret1 != ret2) {
			fprintf(stderr, "compare_file: file length mistmatch:");
			fprintf(stderr, "read: %d from %s, %d from %s",
				ret1, f1, ret2, f2);
			close(fd1);
			close(fd2);
			return (-1);
		}
		for (i = 0; i < ret1; i++) {
			if (strncmp(&buf1[i], &buf2[i], 1)) {
				fprintf(stderr, "compare_file: char mismatch:");
				fprintf(stderr, " %s offset %d: 0x%02x %c  ",
					f1, i, buf1[i],
					isprint(buf1[i]) ? buf1[1] : '.');
				fprintf(stderr, " %s offset %d: 0x%02x %c\n",
					f2, i, buf2[i],
					isprint(buf2[i]) ? buf2[i] : '.');
				close(fd1);
				close(fd2);
				return (-1);
			}
		}
	}
	close(fd1);
	close(fd2);
	return 0;
}

/* **** Routines to create, wait and destroy child processes **** */

/*
 * forkchldrn: fork the given number of children and set the function
 *		that child should execute.
*/
int forkchldrn(int **pidlst, int numchld, int action, int (*chldfunc) ())
{
	int i, cpid;

	if ((*pidlst = ((int *)malloc(sizeof(int) * numchld))) == 0) {
		fprintf(stderr, "forkchldrn: calloc failed for pidlst: %s\n",
			strerror(errno));
		return (-1);
	}
	for (i = 0; i < numchld; i++) {
		if ((cpid = fork()) < 0) {
			fprintf(stderr,
				"forkchldrn: fork child %d failed, %s\n", i,
				strerror(errno));
			killchldrn(pidlst, i, SIGTERM);
			return (-1);
		}
		if (cpid == 0)
			exit((*chldfunc) (i, action));
		else
			*(*pidlst + i) = cpid;
	}
	return 0;
}

/*
 * killchldrn: signal the children listed in pidlst with the given signal
 *
*/
int killchldrn(int **pidlst, int numchld, int sig)
{
	int i, cpid, errflag = 0;

	for (i = 0; i < numchld; i++) {
		cpid = *(*pidlst + i);
		if (cpid > 0) {
			if (kill(cpid, sig) < 0) {
				fprintf(stderr,
					"killchldrn: kill %d failed, %s\n",
					cpid, strerror(errno));
				errflag--;
			}
		}
	}
	return (errflag);
}

/*
 * waitchldrn: wait for child process listed in pidlst to finish.
*/
int waitchldrn(int **pidlst, int numchld)
{
	int i, cpid, ret, errflag = 0;
	int status;

	for (i = 0; i < numchld; i++) {
		cpid = *(*pidlst + i);
		if (cpid == 0)
			continue;
		if ((ret = waitpid(cpid, &status, 0)) != cpid) {
			fprintf(stderr,
				"waitchldrn: wait failed for child %d, pid %d: %s\n",
				i, cpid, strerror(errno));
			errflag--;
		}
		if (status)
			errflag = -1;
	}
	return (errflag);
}