#include <sys/time.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

/* Creates n subprocesses, each computing a sum [0, x],
   where x is a random number. Results are read through
   pipes */

void *ecalloc(size_t nelem, size_t elsize);

int main(int argc, char *argv[]) {
struct timeval tv;
int (*pipe_fds)[2];
pid_t *pids;
int numprocs, i, j, readyFiles;
int *limits;
long *results;
int hasError = 0;
fd_set theFds, readFds, errorFds;
int maxfd = -1;

if (argc == 1) {
	fprintf(stderr, "provide the number of procs\n");
	return 1;
}
numprocs = atoi(argv[1]);
if (numprocs <= 0) {
	fprintf(stderr, "numprocs must be > 0\n");
	return 1;
}

gettimeofday(&tv, NULL);
srand(tv.tv_usec);

pipe_fds = ecalloc(numprocs, sizeof(int [2]));
pids = ecalloc(numprocs, sizeof(pid_t));
limits = ecalloc(numprocs, sizeof(int));
results = ecalloc(numprocs, sizeof(long));
FD_ZERO(&theFds);

for (i = 0; i < numprocs; i++) {
	if (pipe(pipe_fds[i]) < 0) {
		perror("cannot create pipe pair");
		hasError = 1;
		break;
	}
	while ((limits[i] = rand()) == 0) ;
	if ((pids[i] = fork()) == -1) {
		hasError = 1;
		close(pipe_fds[i][0]);
		close(pipe_fds[i][1]);
		break;
	}
	if (pids[i] > 0) { /* parent */
		close(pipe_fds[i][1]);
		FD_SET(pipe_fds[i][0], &theFds);
		maxfd = (maxfd > pipe_fds[i][0]) ? maxfd : pipe_fds[i][0];
	}
	else { /* Child process */
		for (j = 0; j <= i; j++) {
			close(pipe_fds[j][0]);
		}
		sleep(rand() % 3);
		for (results[i] = 0, j = 0; j < limits[i]; j++)
			results[i] += j;
		if (write(pipe_fds[i][1], &(results[i]), sizeof(long)) != sizeof(long))
			perror("Cannot write to pipe\n");
		close(pipe_fds[i][1]);
		exit(0);
	}
}

/* Parent done with creating children */
if (hasError == 1) {
	printf("Successfully created %d out of %d procs\n", i - 1, numprocs);
	numprocs = i - 1;
}
else
	printf("Successfully created all %d procs\n", numprocs);

i = numprocs;
while (i != 0) {
	memcpy(&readFds, &theFds, sizeof(fd_set));
	if ((readyFiles = select(maxfd + 1, &readFds, NULL, NULL, NULL)) == -1)
		continue;
	j = 0;
	while(readyFiles > 0) {
		if (FD_ISSET(pipe_fds[j][0], &readFds)) {
			if (read(pipe_fds[j][0], &(results[j]), sizeof(long)) != sizeof(long)) {
				perror("could not read");
				fprintf(stderr, "j = %d, fileno = %d\n", j, pipe_fds[j][0]);
			}
			close(pipe_fds[j][0]);
			printf("read from child %d (pid = %ld)\n", j, (long)pids[j]);
			FD_CLR(pipe_fds[j][0], &theFds);
			i--;
			readyFiles--;
			waitpid(pids[j], NULL);
		}
		j++;
	}
}

for (i = 0; i < numprocs; i++) {
	if (results[i] == -1)
		printf("Child %d (pid = %ld) failed to compute/return sum\n",
			i, (long)(pids[i]));
	else
		printf("Child %d (pid = %ld) computed: sum([1..%d]) = %ld\n",
			i, (long)(pids[i]), limits[i], results[i]);
}
return 0;
}


void *ecalloc(size_t nelem, size_t elsize) {

void *res = calloc(nelem, elsize);
if (res == NULL) {
	perror("Cannot allocate memory");
	exit(2);
}
return res;
}

