#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/ns.h>
#include <9pm/thread.h>
#include <9pm/threadimpl.h>

/*
 * Make sure that file descriptors 0, 1, and 2 have valid c->sysfd
 * equivalents, so that we can use them if we exec.  We use system
 * pipes and extra copying processes; it's not exact but it's as close
 * as you're going to get.  It means that this is not appropriate when
 * we depend on knowing when the exec'ed process is actually reading
 * (for example, a rio window behaves differently when the /dev/cons
 * it provides is being read from or not; thus we can't easily provide rio).
 *
 * On Windows, we can only specify three file descriptors:
 * stdin, stdout, and stderr.  For consistency, we silently 
 * close all but the first three file descriptors, on all platforms.
 */
static void
copyproc(void *a)
{
	int from, to;
	struct {int from; int to; Channel *c;} *s;
	char buf[8192];
	int i, n;

	_threadgetproc()->nowait = 1;
	s = a;
	from = s->from;
	to = s->to;
	rfork(RFFDG);
	sendul(s->c, 0);
	for(i=0; i<3+6; i++)
		if(i!=from && i!=to)
			close(i);
	while((n = read(from, buf, sizeof buf)) > 0)
		write(to, buf, n);
}

static void
bugger(Fgrp *f)
{
	int i;

	/* make sure first three fds are not viewed as available by pipe() */
	for(i=0; i<3; i++)
		if(f->fd[i] == nil)
			f->fd[i] = (Chan*)~0;
}

static void
unbugger(Fgrp *f)
{
	int i;

	for(i=0; i<3; i++)
		if(f->fd[i] == (Chan*)~0)
			f->fd[i] = nil;
}

int
_threadfdcoherence(void)
{
	int i, closefd, copyfd, fd[2], m, ret;
	Chan *c;
	struct {int from; int to; Channel *c;} s;
	Proc *p;
	Fgrp *f;

	rfork(RFFDG);
	/*
	 * We now have our own file descriptor table, so holding
	 * locks should be unnecessary: no one will modify it under us.
	 * (Holding locks is counterproductive, since close et al. will
	 * try to acquire them.)
	 */
	p = getproc();
	f = p->fgrp;
	m = f->maxfd;
	if(m==0)
		return 0;
	assert(m>=3);	/* mchan grows by chunks of DELTAFD >= 3 (currently 20) */
	for(i=3; i<m; i++)
		close(i);

	ret = 0;
	s.c = chancreate(sizeof(ulong), 0);
	for(i=0; i<3; i++){
		c = f->fd[i];
		if(c == nil)
			continue;
		if(issysfd(c))
			continue;
		bugger(f);
		if(pipe(fd) < 0){
			ret = -1;
			unbugger(f);
			continue;
		}
		unbugger(f);
		if(i==0){
			s.from = i;
			s.to = fd[1];
			closefd = fd[1];
			copyfd = fd[0];
		}else{
			s.from = fd[0];
			s.to = i;
			closefd = fd[0];
			copyfd = fd[1];
		}
		proccreate(copyproc, &s, 10240);
		recvul(s.c);
		close(closefd);
		dup(copyfd, i);
		close(copyfd);
	}
	chanfree(s.c);
	return ret;
}
