/*
 *	Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *	This will be free software, but only when it is finished.
 */
/*
 *	Lots of modifications (new guts, more or less..) by
 *	Matti Aarnio <mea@nic.funet.fi>  (copyright) 1992-1995
 */


#include "hostenv.h"
#include <stdio.h>
#include <sys/param.h>
#include "scheduler.h"
#include "mail.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/file.h>
#include "zmsignal.h"
/* #include <stdlib.h> */
#include <unistd.h>

#include "prototypes.h"

extern int forkrate_limit;

static int  scheduler_nofiles = -1; /* Will be filled below */
static int  runcommand __((char **, struct vertex *, struct web *, struct web*));
static void stashprocess __((int, int, int, struct web*, struct web*, struct vertex *));
static void reclaim __((int, int));
static void waitandclose __((int));
static void readfrom __((int));


#ifdef  HAVE_WAITPID
# include <sys/wait.h>
#else
# ifdef HAVE_WAIT3
#  include <sys/wait.h> /* Has BSD wait3() */
# else
#  ifdef HAVE_SYS_WAIT_H /* POSIX.1 compatible */
#   include <sys/wait.h>
#  else /* Not POSIX.1 compatible, lets fake it.. */
extern int wait();
#  endif
# endif
#endif

#ifndef WEXITSTATUS
# define WEXITSTATUS(s) (((s) >> 8) & 0377)
#endif
#ifndef WSIGNALSTATUS
# define WSIGNALSTATUS(s) ((s) & 0177)
#endif

struct procinfo *cpids = NULL;

#define	MAXFILESPERTRANSPORT	1000

int	numkids = 0;

extern char *getzenv();
extern int errno;


/* Send "#idle\n" -string to the child.. */

void
idle_child(proc)
struct procinfo *proc;
{
	int len, rc;
	/* we are NOT to be called while there is something left in the cmdbuf[] ! */
	if (proc->cmdlen != 0) {
	  fprintf(stderr,"idle_child(proc->cmdbuf=\"%s\") -> abort()!\n",proc->cmdbuf);
	  fflush(stderr);
	  /* abort(); */ /* Hmm.. ??? */
	  return;
	}
	strcpy(proc->cmdbuf,"#idle\n");
	len = strlen(proc->cmdbuf);
	rc = write(proc->tofd, proc->cmdbuf, len);
	if (rc != len) {
	  if (rc < 0 && (errno != EAGAIN && errno != EINTR &&
			 errno != EWOULDBLOCK)) {
	    /* Some real failure :-( */
	    pipes_shutdown_child(proc->tofd);
	    proc->tofd = -1;
	    proc->vertex->proc = NULL;
	    proc->thread->proc = NULL;
	    proc->thread       = NULL;
	    proc->cmdlen = 0;
	    return;
	  }
	}
	proc->cmdlen = 0;
}

int
flush_child(proc)
struct procinfo *proc;
{
	int rc, len;

	if (proc->tofd < 0) {
	  if (proc->vertex)
	    proc->vertex->proc = NULL;
	  if (proc->thread) {
	    proc->thread->proc = NULL;
	    proc->thread       = NULL;
	  }
	  proc->cmdlen = 0;
	  return 0;
	}

	if (proc->cmdlen != 0 && proc->tofd >= 0) {
	  /* We have some leftovers from previous feed..
	     .. feed them now.  */
	  rc = write(proc->tofd, proc->cmdbuf, proc->cmdlen);
	  if (rc != proc->cmdlen) {
	    if (rc < 0 && (errno != EAGAIN && errno != EINTR &&
			   errno != EWOULDBLOCK)) {
	      /* Some real failure :-( */
	      pipes_shutdown_child(proc->tofd);
	      proc->tofd = -1;
	      if (proc->vertex)
		proc->vertex->proc = NULL;
	      if (proc->thread) {
		proc->thread->proc = NULL;
		proc->thread       = NULL;
	      }
	      proc->cmdlen = 0;
	      return -1;
	    }
	    if (rc < 0) rc = 0;
	    if (rc > 0)
	      strcpy(proc->cmdbuf, proc->cmdbuf+rc);
	    proc->cmdlen -= rc;
	  } else {
	    proc->cmdlen = 0; /* Clean the pending info.. */
	  }
	  proc->feedtime = now;
	  return proc->cmdlen; /* We return latter.. */
	}
	return 0;
}

void
feed_child(proc)
struct procinfo *proc;
{
	struct vertex *vtx;
	int len, rc;
	char cmdbuf[500];

	if (proc->thread == NULL) {
	  return; /* Might be called without next process.. */
	}
	if (proc->pid <= 0 || proc->tofd < 0) {
	  return; /* No process../No write channel.. */
	}
	if (proc->fed) {
	  /* DON'T RE-FEED! */
	  if (verbose)
	    printf(" ... no refeeding\n");
	  return;
	}

	vtx = proc->vertex;

	if (proc->thg->withhost)
	  sprintf(cmdbuf, "%s\n", vtx->cfp->mid);
	else
	  sprintf(cmdbuf, "%s\t%s\n", vtx->cfp->mid, proc->ho->name);

	len = strlen(cmdbuf);

	if (len >= (sizeof(proc->cmdbuf) - proc->cmdlen)) {
	  /* Does not fit there, flush it! */
	  rc = flush_child(proc);
	  if (len >= (sizeof(proc->cmdbuf) - proc->cmdlen)) {
	    /* STILL does not fit there, come back latter.. */
	    return;
	  }
	}
	/* Ok, it does fit in, copy it there.. */
	strcpy(proc->cmdbuf+proc->cmdlen,cmdbuf);
	proc->cmdlen += len;

	if (verbose) {
	  printf("feed: tofd=%d, fed=%d, chan=%s, proc=0x%p, vtx=0x%p, ",
		 proc->tofd,proc->fed,proc->ch->name, proc, vtx);
	  fflush(stdout);
	}

	vtx->proc = proc;    /* Flag that it is in processing */
	vtx->ce_pending = 0; /* and clear the pending.. */
	
	if (proc->hungry) --hungry_childs;
	/* It was fed (to buffer), clear this flag.. */
	proc->hungry = 0;
	proc->fed = 1;

	if (verbose)
	  printf("len=%d buf=%s", len, cmdbuf);

	proc->feedtime = now;
	if (vtx)
	  vtx->attempts += 1; /* We may get it closed above.. */
}

/*
 * start_child() -- build argv[], and do other inits for fork()ing
 *                  and execve()ing a new transport program for us.
 */

int
start_child(vhead, chwp, howp)
	struct vertex *vhead;
	struct web *chwp, *howp;
{
	char	*av[30], *s, *os, *cp, *ocp, buf[MAXPATHLEN];
	int	i;
	static time_t prev_time = 0;
	static int startcnt = 0; /* How many childs per second (time_t tick..) ? */
	time_t this_time;


	if (verbose)
	  printf("transport(vhead,chan=%s,host=%s)\n",
		 chwp->name,howp->name);

	++startcnt;
	this_time = mytime(NULL);
	if (this_time != prev_time) {
	  startcnt = 0;
	  prev_time = this_time;
	} else if (startcnt > forkrate_limit) {
	  if (verbose)
	    printf(" ... too many forks per second!\n");
	  return 0;
	}

	if (vhead->thgrp->ce.argv == NULL) {
	  fprintf(stderr, "No command defined for %s/%s!\n",
		  chwp->name, howp->name);
	  return 0;
	}
	/*
	 * Replace the $host and $channel strings in the command line.
	 */
	os = buf;
	for (i = 0; vhead->thgrp->ce.argv[i] != NULL; ++i) {
	  if (strcmp(vhead->thgrp->ce.argv[i], replhost) == 0) {
	    av[i] = howp->name;
	  } else if (strcmp(vhead->thgrp->ce.argv[i], replchannel) == 0) {
	    av[i] = chwp->name;
	  } else if (strchr(vhead->thgrp->ce.argv[i], '$') != NULL) {
	    s = os;
	    for (cp = vhead->thgrp->ce.argv[i]; *cp != '\0'; ++cp) {
	      if (*cp == '$' && *(cp+1) == '{') {
		cp += 2;
		ocp = cp;
		while (*cp != '\0' && *cp != '}')
		  ++cp;
		if (*cp == '}') {
		  *cp = '\0';
		  if (strcmp(ocp,"host")==0)
		    strcpy(s,howp->name);
		  else if (strcmp(ocp,"channel")==0)
		    strcpy(s,chwp->name);
		  else
		    strcpy(s, getzenv(ocp));
		  s += strlen(s);
		  *cp = '}';
		} else
		  --cp;
	      } else
		*s++ = *cp;
	    }
	    *s = '\0';
	    av[i] = os;
	    os = s + 1;
	  } else
	    av[i] = vhead->thgrp->ce.argv[i];
	}
	av[i] = NULL;

	/* fork off the appropriate command with the appropriate stdin */
	if (verbose) {
	  printf("$");
	  for (i = 0; av[i] != NULL; ++i)
	    printf(" %s", av[i]);
	  printf("\n");
	}
	return runcommand(av, vhead, chwp, howp);
}

static int runcommand(argv, vhead, chwp, howp)
	char	*argv[];
	struct vertex *vhead;
	struct web *chwp, *howp;
{
	int	i, pid, to[2], from[2], uid, gid;
	char	*cmd;
	static int pipesize = 0;


	uid = vhead->thgrp->ce.uid;
	gid = vhead->thgrp->ce.gid;
	cmd = vhead->thgrp->ce.command;

	if (pipes_create(to,from) < 0) return 0;
	if (pipesize == 0)
	  pipesize = resources_query_pipesize(to[0]);

	if (verbose)
	  fprintf(stderr, "to %d/%d from %d/%d\n",
		  to[0],to[1],from[0],from[1]);

	if ((pid = fork()) == 0) {	/* child */

	  pipes_to_child_fds(to,from);

	  /* keep current stderr for child stderr */
	  /* close all other open filedescriptors */

	  /* ... if detach() did its job, there shouldn't be any! */
	  /* ... no, the 'querysock' is there somewhere!   */
	  if (scheduler_nofiles < 1)
	    scheduler_nofiles = resources_query_nofiles();
	  for (i = 3; i < scheduler_nofiles; ++i)
	    close(i);

	  resources_limit_nofiles(transportmaxnofiles);
	  setgid(gid);	/* Do GID setup while still UID 0..   */
	  setuid(uid);	/* Now discard all excessive powers.. */
	  execv(cmd, argv);
	  fprintf(stderr, "Exec of %s failed!\n", cmd);
	  _exit(1);
	} else if (pid < 0) {	/* fork failed - yell and forget it */
	  close(to[0]); close(to[1]);
	  close(from[0]); close(from[1]);
	  fprintf(stderr, "Fork failed!\n");
	  return 0;
	}

	/* parent */

	pipes_close_parent(to,from);

	/* save from[0] away as a descriptor to watch */
	stashprocess(pid, from[0], to[1], chwp, howp, vhead);
	/* We wait for the child to report "#hungry", then we feed it.. */
	return 1;
}


static void stashprocess(pid, fromfd, tofd, chwp, howp, vhead)
	int pid, fromfd, tofd;
	struct web *chwp, *howp;
	struct vertex *vhead;
{
	int i;
	struct procinfo *proc;

	if (verbose)
	  fprintf(stderr, "stashprocess(%d, %d, %d, %s, %s)\n",
		  pid, fromfd, tofd, chwp ? chwp->name : "nil",
		  howp ? howp->name : "nil");
	if (cpids == NULL) {
	  if (scheduler_nofiles < 1)
	    scheduler_nofiles = resources_query_nofiles();
	  i = scheduler_nofiles;
	  cpids = (struct procinfo *)
	    emalloc((unsigned)(i * sizeof (struct procinfo)));
	  memset(cpids, 0, sizeof(struct procinfo) * i);
	}
	proc = &cpids[fromfd];
	memset(proc,0,sizeof(struct procinfo));
#if 0 /* the memset() does this more efficiently.. */
	proc->next   = NULL;
	proc->cmdlen = 0;
	proc->reaped = 0;
	proc->carryover = NULL;
	proc->hungry = 0;
	proc->fed    = 0;
	proc->overfed = 0;
#endif
	proc->pid    = pid;
	proc->ch     = chwp;
	proc->ho     = howp;
	proc->vertex = vhead;
	proc->thread = vhead->thread;
	proc->thread->proc = proc;
	proc->thg    = vhead->thread->thgrp;
	proc->thg->transporters += 1;
	++numkids;
	if (chwp != NULL) chwp->kids += 1;
	if (howp != NULL) howp->kids += 1;
	proc->tofd   = tofd;
	vhead->proc  = proc;
	mytime(&proc->hungertime); /* Actually it is not yet 'hungry' as
				      per reporting so, but we store the
				      time-stamp anyway */
	fd_nonblockingmode(fromfd);
	if (fromfd != tofd)
	  fd_nonblockingmode(tofd);
}

/* 
 *  Reclaim the process slot -- this process is dead now.
 */
static void reclaim(fromfd, tofd)
	int fromfd, tofd;
{
	struct vertex *vp, *nvp = NULL;
	struct procinfo *proc = &cpids[fromfd];

if (verbose)
  fprintf(stderr,"reclaim(%d,%d) pid=%d, reaped=%d, chan=%s, host=%s\n",
	  fromfd,tofd,proc->pid,proc->reaped,proc->ch->name,proc->ho->name);

	proc->pid = 0;
	proc->reaped = 0;
	if (proc->carryover != NULL) {
	  fprintf(stderr, "%s: HELP! Lost %d bytes: '%s'\n",
		  progname, strlen(proc->carryover), proc->carryover);
	  free(proc->carryover);
	  proc->carryover = NULL;
	}
	if (proc->ch != NULL) {
	  proc->ch->kids -= 1;
	  if (proc->ch->kids == 0 && proc->ch->link == NULL) {
	    unweb(L_CHANNEL, proc->ch);
	    proc->ch = NULL;
	  }
	}
	if (proc->ho != NULL) {
	  proc->ho->kids -= 1;
	  if (proc->ho->kids == 0 && proc->ho->link == NULL) {
	    unweb(L_HOST, proc->ho);
	    proc->ho = NULL;
	  }
	}
	if (tofd >= 0)
	  pipes_shutdown_child(tofd);
	close(fromfd);

	/* Reschedule the vertices that are left
	   (that were not reported on).		*/

	/* ... but only if we were not in IDLE chain! */
	if (proc->thread != NULL) {
	  /* Reschedule them all .. */
	  thread_reschedule(proc->thread,0,-1);
	  /* Bookkeeping about dead transporters */
	  proc->thg->transporters -= 1;
	  --numkids;
	  if (proc->vertex != NULL)
	    proc->vertex->proc = NULL;
	  proc->vertex = NULL;
	  if (proc->thread)
	    proc->thread->proc = NULL;
	  proc->thread = NULL;
	  proc->thg    = NULL;
	} else {
	  /* Maybe we were in idle chain! */
	  if (proc->thg != NULL /* And not killed by  idle_cleanup() */) {
	    struct procinfo *p, **pp;
	    p  =  proc->thg->idleproc;
	    pp = &proc->thg->idleproc;

	    while (p && p != proc) {
		/* Move to the next possible idle process */
		pp = &p->next;
		p = p->next;
	    }
	    if (p == proc) {
	      /* Remove this entry from the chains */
	      *pp = p->next;
	      p = p->next;
	      proc->thg->idlecnt      -= 1;
	      --idleprocs;
	      proc->thg->transporters -= 1;
	      --numkids;
	      /* It may go down to zero.. */
	      if (proc->thg->transporters == 0 && proc->thg->threads == 0)
		delete_threadgroup(proc->thg);
	    } else {
	      /* It is not in idle chain, it has died somehow else.. */
	      if (proc->vertex != NULL)
		proc->vertex->proc = NULL;
	      proc->vertex = NULL;
	      if (proc->thread != NULL)
		proc->thread->proc = NULL;
	      proc->thread = NULL;
	      proc->thg->transporters -= 1;
	      --numkids;
	    }
	  } else {
	    /* We are killed by the idle_cleanup() !   */
	    /* proc->thg == NULL, proc->thread == NULL */
	    /* idle_cleanup() did all decrementing..   */
	  }
	}
}

static void waitandclose(fd)
	int	fd;
{
	/* This is called when
	   - fd return 0 (EOF)
	   - fd returns -1, and errno != {EAGAIN|EWOULDBLOCK|EINTR}
	 */
	reclaim(fd, cpids[fd].tofd);
}

#ifdef	HAVE_SELECT

#ifdef _AIX /* The select.h  defines NFDBITS, etc.. */
# include <sys/types.h>
# include <sys/select.h>
#endif


#if	defined(BSD4_3) || defined(sun)
#include <sys/file.h>
#endif
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>

#ifndef	NFDBITS
/*
 * This stuff taken from the 4.3bsd /usr/include/sys/types.h, but on the
 * assumption we are dealing with pre-4.3bsd select().
 */

typedef long	fd_mask;

#ifndef	NBBY
#define	NBBY	8
#endif	/* NBBY */
#define	NFDBITS		((sizeof fd_mask) * NBBY)

/* SunOS 3.x and 4.x>2 BSD already defines this in /usr/include/sys/types.h */
#ifdef	notdef
typedef	struct fd_set { fd_mask	fds_bits[1]; } fd_set;
#endif	/* notdef */

#ifndef	FD_SET
#define	FD_SET(n, p)	((p)->fds_bits[0] |= (1 << (n)))
#define	FD_CLR(n, p)	((p)->fds_bits[0] &= ~(1 << (n)))
#define	FD_ISSET(n, p)	((p)->fds_bits[0] & (1 << (n)))
#define FD_ZERO(p)	bzero((char *)(p), sizeof(*(p)))
#endif	/* !FD_SET */
#endif	/* !NFDBITS */

#ifdef FD_SET
#define _FD_SET(sock,var) FD_SET(sock,&var)
#define _FD_CLR(sock,var) FD_CLR(sock,&var)
#define _FD_ZERO(var) FD_ZERO(&var)
#define _FD_ISSET(i,var) FD_ISSET(i,&var)
#else
#define _FD_SET(sock,var) var |= (1 << sock)
#define _FD_CLR(sock,var) var &= ~(1 << sock)
#define _FD_ZERO(var) var = 0
#define _FD_ISSET(i,var) ((var & (1 << i)) != 0)
#endif

int in_select = 0;

int
mux(timeout)
time_t timeout;
{
	int	i, n, rc, maxf;
	fd_set	rdmask;
	fd_set	wrmask;
	struct timeval tv;

	if (in_select) {
	  fprintf(stderr,"**** recursed into mux()! ***\n");
	  return 0;
	}
	in_select = 1;

	mytime(&now);

	tv.tv_sec = timeout - now; /* Timeout in seconds */
	if (timeout < now)
	  tv.tv_sec = 0;
	tv.tv_usec = 0;

	maxf = -1;
	_FD_ZERO(rdmask);
	_FD_ZERO(wrmask);
	if (cpids != NULL)
	  for (i = 0; i < scheduler_nofiles ; ++i)
	    if (cpids[i].pid != 0) {
	      _FD_SET(i, rdmask);
	      maxf = i;
	      if (cpids[i].cmdlen != 0)
		_FD_SET(i, wrmask);
	    }
	if (querysocket >= 0) {
	  _FD_SET(querysocket, rdmask);
	  if (maxf < querysocket)
	    maxf = querysocket;
	}
	if (maxf < 0)
	  return -1;
	++maxf;
	/*fprintf(stderr, "about to select on %x [%d]\n",
			  mask.fds_bits[0], maxf); */
	if ((n = select(maxf, &rdmask, &wrmask, NULL, &tv)) < 0) {
	  int err = errno;
	  /* fprintf(stderr, "got an interrupt (%d)\n", errno); */
	  in_select = 0;
	  if (errno == EINTR || errno == EAGAIN)
	    return 0;
	  if (errno == EINVAL || errno == EBADF) {
	    fprintf(stderr, "** select() returned errno=%d\n", err);
	    for (i = 0; i < maxf; ++i) {
	      if (_FD_ISSET(i,rdmask) &&
		  fcntl(i,F_GETFL,0) < 0)
		fprintf(stderr,"** Invalid fd on a select() rdmask: %d\n",i);
	      if (_FD_ISSET(i,wrmask) &&
		  fcntl(i,F_GETFL,0) < 0)
		fprintf(stderr,"** Invalid fd on a select() wrmask: %d\n",i);
	    }
	    fflush(stderr);
	    abort();
	  }
	  perror("select() returned unknown error ");
	  fflush(stderr);
	  abort();
	} else if (n == 0) {
	  /* fprintf(stderr, "abnormal 0 return from select!\n"); */
	  /* -- just a timeout -- fast or long */
	  in_select = 0;
	  return 1;
	} else {
	  /*fprintf(stderr, "got %d ready (%x)\n", n, rdmask.fds_bits[0]);*/
	  if (querysocket >= 0 && _FD_ISSET(querysocket, rdmask)) {
	    struct sockaddr_in raddr;
	    int	raddrlen;

	    --n;
	    _FD_CLR(querysocket, rdmask);
	    raddrlen = sizeof raddr;
	    i = accept(querysocket, (struct sockaddr *)&raddr, &raddrlen);
	    if (i < 0) {
	      perror("accept");
	    } else {
	      rc = fork();
	      if (rc == 0) { /* Child! */
		close(querysocket);
		qprint(i);
		execl("/bin/false",NULL);
		_exit(0);
	      }
	      /* if (rc > 0)
		 ++numkids; */
	      close(i);
	    }
	  }
	  if (cpids != NULL) {
	    for (i = 0; i < maxf; ++i) {
	      if (cpids[i].pid != 0 && _FD_ISSET(i, rdmask)) {
		--n;
		_FD_CLR(i, rdmask);
		/*fprintf(stderr,"that is fd %d\n",i);*/
		/* do non-blocking reads from this fd */
		readfrom(i);
		/* Because this loop might take a while ... */
		queryipccheck();
	      }
	    }
	    /* In case we have non-completed 'feeds', try feeding them */
	    for (i = 0; i < scheduler_nofiles; ++i)
	      if (cpids[i].pid > 0
		  && cpids[i].cmdlen != 0
		  && _FD_ISSET(i, wrmask)) {
		flush_child(&cpids[i]);
		/* Because this loop might take a while ... */
		queryipccheck();
	      }
	  }
	}
	/* fprintf(stderr, "return from mux\n"); */
	in_select = 0;
	return 0;
}

void
queryipccheck()
{
	if (querysocket >= 0) {
	  int	n;
	  fd_set	mask;
	  struct timeval tv;

	  tv.tv_sec = 0;
	  tv.tv_usec = 0;

	  _FD_ZERO(mask);
	  _FD_SET(querysocket, mask);

	  n = select(querysocket+1, &mask, NULL, NULL, &tv);
	  if (n > 0 &&
	      _FD_ISSET(querysocket, mask)) {
	    struct sockaddr_in raddr;
	    int raddrlen = sizeof(raddr);

	    n = accept(querysocket, (struct sockaddr *)&raddr, &raddrlen);
	    if (n >= 0) {
	      int pid = fork();
	      if (pid == 0) {
		qprint(n);
		execl("/bin/false",NULL);
		_exit(0);
	      }
	      close(n);
	    }
	  }
	}
}

void
queryipcinit()
{
#ifdef	AF_INET
	struct servent *serv;
	struct sockaddr_in sad;
	int on = 1;

	if (querysocket >= 0)
		return;
	mytime(&now);
	if ((serv = getservbyname("mailq", "tcp")) == NULL) {
	  fprintf(stderr, "No 'mailq' tcp service defined!\n");
	  /* try again in 5 minutes or so */
	  qipcretry = now + 300;
	  return;
	}
	qipcretry = now + 5;
	sad.sin_port        = serv->s_port;
	sad.sin_family      = AF_INET;
	sad.sin_addr.s_addr = htonl(INADDR_ANY);
	if ((querysocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	  perror("socket");
	  return;
	}
	setsockopt(querysocket, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on));

	if (bind(querysocket, (struct sockaddr *)&sad, sizeof sad) < 0) {
	  perror("bind:mailq socket");
	  close(querysocket);
	  querysocket = -1;
	  return;
	}
	if (listen(querysocket, 5) < 0) {
	  perror("listen:mailq socket");
	  close(querysocket);
	  querysocket = -1;
	  return;
	}
	qipcretry = 0;
#endif	/* AF_INET */
}
#else	/* !HAVE_SELECT */
int
mux(timeout)
time_t timeout;
{
	int	fd;

	/*
	 * Nice'n easy and simpleminded: grab a random file descriptor,
	 * and sit and read off it until something happens.
	 * Some very complicated mux'ing schemes (with shared pipes'n stuff)
	 * are possible in the absence of async i/o like select() or the
	 * simulation USG supplies, but it ain't worth the hassle.
	 */
	if (cpids != NULL)
	  for (fd = 0; fd < scheduler_nofiles ; ++fd)
	    if (cpids[fd].pid != 0)
	      readfrom(fd);

	mytime(&now);
	if (timeout > now)
	  sleep(1);
	return 1;
}

void queryipccheck()
{
	/* NOTHING AT ALL -- No select(), no querysocket.. */
}

void
queryipcinit()
{
}
#endif	/* HAVE_SELECT */

static void readfrom(fd)
	int fd;
{
	int	n, dontbreak, bufsize = 2048;
	char	*cp, *pcp, *eobuf, *buf;
	struct procinfo *proc = &cpids[fd];

	dontbreak = 0;
	cp = pcp = NULL;

	buf = (char *)emalloc(bufsize);

	if (proc->carryover != NULL) {
	  int carrylen = strlen(proc->carryover);
	  if (carrylen > bufsize) {
	    while (carrylen > bufsize)
	      bufsize += 1024;
	    buf = erealloc(buf,bufsize);
	  }
	  strcpy(buf, proc->carryover);
	  cp = buf+strlen(buf);
	  pcp = buf;
	  free(proc->carryover);
	  proc->carryover = NULL;
	  dontbreak = 1;
	}

	/* Note that if we get an alarm() call, the read will return -1, TG */
	errno = 0;
	while ((n = read(fd, dontbreak ? cp : buf,
			 bufsize - (dontbreak ? (cp - buf) : 0))) > 0) {
	  if (verbose)
	    fprintf(stderr, "read from %d returns %d\n", fd, n);
	  eobuf = (dontbreak ? cp : buf) + n;

	  for (cp = buf, pcp = buf; cp < eobuf; ++cp) {
	    if (*cp == '\n') {
	      *cp = '\0';
	      if (verbose)
		fprintf(stderr, "%d fd=%d processed: %s\n",
			proc->pid,fd, pcp);
	      update(fd,pcp);
	      *cp = '_';
	      pcp = cp + 1;
	      dontbreak = 0;
	    } else
	      dontbreak = 1;
	  }

	  if (dontbreak && cp == buf + bufsize) {
	    if (pcp == buf) {
	      /* XX:
	       * can't happen, this would mean a status report line 
	       * that is rather long...
	       * (oh no! it did happen, it did, it did!...)
	       */
	      bufsize += 1024;
	      pcp = buf = erealloc(buf,bufsize);
	      cp = buf + (bufsize - 1024);
	      *cp = '\0';
	    } else {
	      memcpy(buf, pcp, cp-pcp);
	      cp = buf + (cp-pcp);
	      *cp = '\0';
	      pcp = buf;	/* may be used below */
	    }
	  }
	  if (!dontbreak)
	    break;
	}

	if (verbose) {
	  if (!(errno == EAGAIN || errno == EWOULDBLOCK))
	    fprintf(stderr,
		    "read from %d returns %d, errno=%d\n", fd, n, errno);
	}
	if (n == 0 || (n < 0 && errno != EWOULDBLOCK && errno != EAGAIN &&
		       errno != EINTR)) {
	  /*printf("about to call waitandclose(), n=%d, errno=%d\n",n,errno);*/
	  if (proc->tofd >= 0)
	    pipes_shutdown_child(proc->tofd);
	  proc->tofd = -1;
	  waitandclose(fd);
	}
	/* fprintf(stderr, "n = %d, errno = %d\n", n, errno); */
	/*
	 * if n < 0, then either we got an interrupt or the read would
	 * block (EINTR or EWOULDBLOCK). In both cases we basically just
	 * want to get back to whatever we were doing. We just need to
	 * make darned sure that a newline was the last character we saw,
	 * or else some report may get lost somewhere.
	 */
	if (dontbreak) {
	  if (proc->pid != 0) {
	    proc->carryover = emalloc(cp-pcp+1);
	    memcpy(proc->carryover, pcp, cp-pcp);
	    proc->carryover[cp-pcp] = '\0';
	  } else
	    fprintf(stderr,
		    "HELP! Lost %ld bytes (n=%d/%d, off=%ld): '%s'\n",
		    (long)(cp - pcp), n, errno, (long)(pcp-buf), pcp);
	}
	free(buf);
}

#if defined(USE_BINMKDIR) || defined(USE_BINRMDIR)

/*
 * From Ross Ridge's Xenix port:
 * - A nasty problem occurs with scheduler if rmdir (and mkdir also I think),
 *   is implented as system("/bin/rmdir ...").  When system() calls wait()
 *   it can reap the scheduler's children without it knowing.  I fixed this
 *   problem by writing a replacement system() function for scheduler.
 *
 */

int
system(name)
	char *name;
{
	char *sh;
	int st, r;
	int pid;
	int i;

	pid = fork();
	switch(pid) {
	case -1:
		return -1;
	case 0:
		sh = getenv("SHELL");
		if (sh == NULL) {
		  sh = "/bin/sh";
		}
		execl(sh, sh, "-c", name, NULL);
		_exit(1);
	default:
#ifndef USE_SIGREAPER
		while(1) {
		  r = wait(&st);
		  if (r == -1) {
		    if (errno != EINTR) {
		      return -1;
		      if (errno != EINTR) {
			return -1;
		      }
		    } else if (r == pid) {
		      break;
		    }
		    for(i = 0; i < scheduler_nofiles; i++) {
		      if (cpids[i].pid == r) {
			cpids[i].pid = -r;
			break;
		      }
		    }
		  }

		  if ((st & 0x00ff) == 0) {
		    return st >> 8;
		  }
		  return 1;
		}
#endif
		break;
	   }
}

#endif


#ifdef USE_SIGREAPER
/*
 *	Catch each child-process death, and reap them..
 */
RETSIGTYPE sig_chld(signum)
int signum;
{
	int pid;
	int ok = 0;
	int i;
	int statloc;

	for (;;) {

#ifdef	HAVE_WAITPID
	  pid = waitpid(-1,&statloc,WNOHANG);
#else
#ifdef  HAVE_WAIT3
	  pid = wait3(&statloc,WNOHANG,NULL);
#else
	  pid = wait(&statloc);
#endif
#endif
	  if (pid <= 0) break;

	  if (WEXITSTATUS  (statloc) != 0) ok = 1;
	  if (WSIGNALSTATUS(statloc) != 0) ok = 1;

	  if (verbose)
	    fprintf(stderr,"sig_chld() pid=%d, ok=%d, stat=0x%x\n",
		    pid,ok,statloc);
	  
	  if (ok && cpids != NULL) {
	    /* Only EXIT and SIGxx DEATHS accepted */

	    for (i = scheduler_nofiles-1; i >= 0; --i) {
	      if (cpids[i].pid == pid) {
		cpids[i].pid = -pid; /* Mark it as reaped.. */
		cpids[i].reaped = 1;
		ok = 0;
		break;
	      }
	      if (cpids[i].pid == -pid) {
		printf(" .. already reaped ??\n");
		cpids[i].pid = -pid; /* Mark it as reaped.. */
		cpids[i].reaped = 1;
		ok = 0;
		break;
	      }
	    }
	  }
	}

	/* re-instantiate the signal handler.. */
#ifdef SIGCLD
	SIGNAL_HANDLE(SIGCLD,  sig_chld);
#else
	SIGNAL_HANDLE(SIGCHLD, sig_chld);
#endif
}
#endif /* USE_SIGREAPER */
