/*
 *	Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *	This will be free software, but only when it is finished.
 */

#include <stdio.h>
#include <sys/param.h>
#include "hostenv.h"
#include "scheduler.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/file.h>
#include NDIR_H
/*
 * FD_SETSIZE should be set in either <sys/param.h> or
 * <sys/types.h> if we have USE_SELECT
 */
#ifndef FD_SETSIZE
#define	FD_SETSIZE	32
#endif	/* !FD_SETSIZE */

#define TRANSPORTMAXNOFILES 32 /* Number of files a transporter may
				  need open -- or any of its children.. */

#ifdef	USE_UNIONWAIT
#include <sys/wait.h>
#endif
#ifdef	USE_WAITPID
#include <sys/wait.h>
#endif

struct procinfo *cpids = NULL;


#define	MAXFILESPERTRANSPORT	1000

int	numkids = 0;

extern int resources_query_pipesize();
extern char *getzenv();
extern char *progname;
extern char *replchannel;
extern char *replhost;
extern char *vtx_to_dir();
extern char *strchr();
extern int cistrcmp(), ranny();
extern int epipe();
extern int errno;
extern int querysocket;
extern int subsubdirs;
extern int verbose;
extern time_t qipcretry, time();
extern void dotransport();
extern void qprint(), readfrom();
extern void runcommand();
extern void stashprocess();
extern void unweb(), reschedule(), fifoschedule();
extern void update();


/*
 * Collect up all the marked control file names (unmarking them in the process)
 * and send them to the command given by the parameter. Said command must be
 * forked off, and the appropriate structures set up to deal with the stdout
 * of that command.
 */

static int t_n;
static char **t_files;

int
tscan(spl)
	struct spblk *spl;
{
	struct ctlfile *cfp = (struct ctlfile *)spl->data;
	char *dir, path[MAXPATHLEN], buf[MAXPATHLEN];

	/* assert cfp != NULL */
	if (cfp->mark == V_NONE || t_n >= MAXFILESPERTRANSPORT)
	  return -1;
	/*
	 * XX: Note that any old filename will do for the control file,
	 * since all names are links to the same file anyway.
	 * However, we need to get the filename right, based on
	 * our favorite filename generation technique.
	 */
	if (subsubdirs)	/* channel/host */
	  dir = vtx_to_dir(cfp->head, buf);
	else		/* channel */
	  dir = cfp->head->orig[L_CHANNEL]->name;
	sprintf(path, "%s/%lu", dir, cfp->id);
	t_files[t_n++] = strsave(path);
	cfp->mark = V_NONE;
	return 0;
}

void
transport(vhead, channel, host)
	struct vertex *vhead;
	char *channel, *host;
{
	int i;
	char *files[MAXFILESPERTRANSPORT];	/* XX */

	/* generate the file names we care about */
	t_n = 0;
	t_files = files;
	sp_scan(tscan, (struct spblk *)NULL, spt_mesh[L_CTLFILE]);
	dotransport(files, t_n, vhead, channel, host);
	for (i = 0; i < t_n; ++i)
		free(files[i]);
}

void
dotransport(files, n, vhead, channel, host)
	char *files[];
	int n;
	struct vertex *vhead;
	char *channel, *host;
{
	struct spblk *spl;
	struct web *chwp, *howp;
	char	*av[30], *s, *os, *cp, *ocp, buf[MAXPATHLEN];
	int	i;

	if (n == 0)
		return;
	chwp = NULL;
	if (channel != NULL) {
	  spl = sp_lookup(symbol((u_char *)channel), spt_mesh[L_CHANNEL]);
	  if (spl == NULL || (chwp = (struct web *)spl->data) == NULL)
	    return;		/* yes, this can happen */
	}
	howp = NULL;
	if (host != NULL) {
	  spl = sp_lookup(symbol((u_char *)host), spt_mesh[L_HOST]);
	  if (spl == NULL || (howp = (struct web *)spl->data) == NULL)
	    return;		/* yes, this can happen */
	}
	if (vhead->ce.argv == NULL) {
	  if (verbose) {
	    fprintf(stderr, "No command defined for %s/%s!\n",
		    channel, host);
	  }
	  return;
	}
	/*
	 * Replace the $host and $channel strings in the command line.
	 */
	os = buf;
	for (i = 0; vhead->ce.argv[i] != NULL; ++i) {
	  if (cistrcmp(vhead->ce.argv[i], replhost) == 0) {
	    av[i] = host;
	  } else if (cistrcmp(vhead->ce.argv[i], replchannel) == 0) {
	    av[i] = channel;
	  } else if (strchr(vhead->ce.argv[i], '$') != NULL) {
	    s = os;
	    for (cp = vhead->ce.argv[i]; *cp != '\0'; ++cp) {
	      if (*cp == '$' && *(cp+1) == '{') {
		cp += 2;
		ocp = cp;
		while (*cp != '\0' && *cp != '}')
		  ++cp;
		if (*cp == '}') {
		  *cp = '\0';
		  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->ce.argv[i];
	}
	av[i] = NULL;
	/*
	 * randomize the file names if appropriate, this is a simpleminded
	 * but probably effective way of avoiding a single message blocking
	 * some queue.  XX: This should really be expanded to invoke a general
	 * strategy routine/mechanism.
	 */
	if (n > 1) {
	  char *temp;
	  u_int ni;

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

void
runcommand(filearr, nelem, argv, vhead, chwp, howp)
	char	*filearr[];
	int	nelem;
	char	*argv[];
	struct vertex *vhead;
	struct web *chwp, *howp;
{
	int	i, pid, position, to[2], from[2], uid, gid;
	char	*cmd;
	FILE	*fp;
	static int pipesize;


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

	if (nelem <= 0)
	  return;
	if (epipe(to) < 0)
	  return;
	if (epipe(from) < 0) {
	  close(to[0]);
	  close(to[1]);
	  return;
	}
	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 */
	  int topfileno;
	  if (to[0] != 0) {
	    dup2(to[0],0);	/* child stdin */
	    close(to[0]);
	  }
	  if (from[1] != 1) {
	    dup2(from[1],1);	/* child stdout */
	    close(from[1]);
	  }
	  dup2(1,2);
	  /* keep current stderr for child stderr */
	  /* close all other open filedescriptors */
#if	1
	  /* ... if detach() did its job, there shouldn't be any! */
	  /* ... no, the 'querysock' is there somewhere!   */
	  topfileno = resources_query_nofiles();
	  for (i = 3; i < topfileno; ++i)
	    close(i);
#else
	  close(to[1]);
	  close(from[0]);
	  /* we can ignore the stdouts from other children */
#endif
	  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;
	} else {		/* parent */
	  close(to[0]);
	  close(from[1]);
	  ++numkids;
	  if (chwp != NULL) chwp->kids += 1;
	  if (howp != NULL) howp->kids += 1;
	  /* save from[0] away as a descriptor to watch */
	  stashprocess(pid, from[0], chwp, howp, vhead);
	  /* write the list of files into to[1], and close it */
	  if ((fp = fdopen(to[1], "w")) == NULL) {
	    if (verbose)
	      fprintf(stderr, "cannot open fd %d for w.\n",
		      to[1]);
	    close(to[1]);
	    close(from[0]);
	    return;
	  }
	  for (i = position = 0; i < nelem; ++i) {
	    position += strlen(filearr[i]) + 1;
	    if (position + 1 >= pipesize)
	      break;
	    fprintf(fp, "%s\n", filearr[i]);
	  }
	  if (verbose)
	    fprintf(stderr, "wrote %d bytes to fd %d\n",
		    position, fileno(fp));
	  fputc('\n', fp);
	  fclose(fp);	/* This will also close the to[1] fd */
	}
}

void
stashprocess(pid, fd, chwp, howp, vhead)
	int pid, fd;
	struct web *chwp, *howp;
	struct vertex *vhead;
{
	int i;

	if (verbose)
	  fprintf(stderr, "stashprocess(%d, %d, %s, %s)\n",
		  pid, fd, chwp ? chwp->name : "nil",
		  howp ? howp->name : "nil");
	if (cpids == NULL) {
	  i = FD_SETSIZE;
	  cpids = (struct procinfo *)
	    emalloc((unsigned)(i * sizeof (struct procinfo)));
	  while (--i >= 0)
	    cpids[i].pid = 0;
	}
	cpids[fd].pid = pid;
	cpids[fd].ch = chwp;
	cpids[fd].ho = howp;
	cpids[fd].vertices = vhead;
	vhead->proc = &cpids[fd];
	cpids[fd].carryover = NULL;
#ifdef	USE_BSDNODELAY
	/* set up non-blocking I/O */
	i = fcntl(fd, F_GETFL, 0);
#ifdef	FNONBLOCK
	i |= FNONBLOCK;
#else
	i |= FNDELAY;
#endif
	i = fcntl(fd, F_SETFL, i);
#endif	/* USE_BSDNODELAY */
}

void
reclaim(fd)
	int fd;
{
	struct vertex *vp, *nvp;

	cpids[fd].pid = 0;
	if (cpids[fd].carryover != NULL) {
	  fprintf(stderr, "%s: HELP! Lost %d bytes: '%s'\n",
		  progname, strlen(cpids[fd].carryover),
		  cpids[fd].carryover);
	  free(cpids[fd].carryover);
	  cpids[fd].carryover = NULL;
	}
	if (cpids[fd].ch != NULL) {
	  --cpids[fd].ch->kids;
	  if (cpids[fd].ch->fifohead)
	    fifoschedule(cpids[fd].ch);
	  else if (cpids[fd].ch->kids == 0 && cpids[fd].ch->link == NULL) {
	    unweb(L_CHANNEL, cpids[fd].ch);
	    cpids[fd].ch = NULL;
	  }
	}
	if (cpids[fd].ho != NULL) {
	  --cpids[fd].ho->kids;
	  if (cpids[fd].ho->fifohead)
	    fifoschedule(cpids[fd].ho);
	  else if (cpids[fd].ho->kids == 0 && cpids[fd].ho->link == NULL) {
	    unweb(L_HOST, cpids[fd].ho);
	    cpids[fd].ho = NULL;
	  }
	}
	close(fd);

	/* Reschedule the vertices that are left (that were not reported on). */
	nvp = cpids[fd].vertices;
	cpids[fd].vertices = NULL;
	for (vp = nvp; vp != NULL ; vp = nvp) {
	  nvp = vp->nextitem;
	  if (vp->ngroup == 0)
	    abort();
	  reschedule(vp, 0, -1);
	}
}

void
waitandclose(fd)
	int	fd;
{
	int	n, pid;

#if 0
	/* This is a quick and dirty hack (for A/UX I think). BEWARE */
	if (cpids[fd].pid <= 0) {
	  fprintf(stderr,"fd %d: pid is %d ??\n",fd,cpids[fd].pid);
	  return;
	}
	n = waitpid(cpids[fd].pid, (int *)NULL, 0);
	if(n != cpids[fd].pid && (n != -1 || errno != 10)) {
	  fprintf(stderr,"fd %d, pid %d: waitpid says %d, errno %d\n",
		  fd,cpids[fd].pid, n, errno);
	  return;
	}
	--numkids;
	reclaim(fd);
#else
	if (cpids[fd].pid > 0) {
#ifdef	USE_UNIONWAIT
	  while ((pid = wait((union wait *)0)) > 0)
#else	/* !USE_UNIONWAIT */
	  while ((pid = wait((int *)0)) > 0)
#endif	/* USE_UNIONWAIT */
	    {
	      --numkids;
	      if (verbose)
		printf("reaped %d\n", pid);
	      if (pid == cpids[fd].pid) {
		reclaim(fd);
		break;
	      } else {
		for (n = 0; n < FD_SETSIZE; ++n) {
		  if (cpids[n].pid == pid) {
		    cpids[n].pid = -pid;
		    ++numkids;
		    break;
		  }
		}
	      }
	    }
	} else if (cpids[fd].pid < 0) {
	  --numkids;
	  reclaim(fd);
	}
#endif
}

#ifdef	USE_SELECT

#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 */

void
mux(quickcheck)
int quickcheck;
{
	int	i, n, rc, maxf;
	fd_set	mask;
	struct sockaddr_in raddr;
	int	raddrlen;
	struct timeval tv;

	tv.tv_sec = 10000; /* LONG delay.. */
	tv.tv_usec = 0;
	if (quickcheck)
	  tv.tv_sec = 0; /* Check the sockets fast.. */

	maxf = -1;
	FD_ZERO(&mask);
	if (cpids != NULL)
	  for (i = 0; i < FD_SETSIZE ; ++i)
	    if (cpids[i].pid != 0) {
	      FD_SET(i, &mask);
	      maxf = i;
	    }
	if (querysocket >= 0) {
	  FD_SET(querysocket, &mask);
	  if (maxf < querysocket)
	    maxf = querysocket;
	}
	if (maxf < 0)
	  return;
	++maxf;
	/*fprintf(stderr, "about to select on %x [%d]\n",
			  mask.fds_bits[0], maxf); */
	if ((n = select(maxf, &mask, NULL, NULL, &tv)) < 0) {
	  /* fprintf(stderr, "got an interrupt (%d)\n", errno); */
	  if (errno == EINTR)
	    return;
	} else if (n == 0) {
	  /* fprintf(stderr, "abnormal 0 return from select!\n"); */
	  /* -- just a timeout -- fast or long */
	  return;
	} else {
	  /*fprintf(stderr, "got %d ready (%x)\n", n, mask.fds_bits[0]);*/
	  if (querysocket >= 0 && FD_ISSET(querysocket, &mask)) {
	    --n;
	    FD_CLR(querysocket, &mask);
	    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);
		_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, &mask)) {
		--n;
		FD_CLR(i, &mask);
		/*fprintf(stderr,"that is fd %d\n",i);*/
		/* do non-blocking reads from this fd */
		readfrom(i);
	      }
	    }
	}
	/* fprintf(stderr, "return from mux\n"); */
}

void
ipcpause(pollflag)
int pollflag;
{
#ifdef	AF_INET
	int	i, n, rc;
	fd_set	mask;
	struct sockaddr_in raddr;
	int	raddrlen;
	struct timeval timeout;

	if (querysocket < 0 && !pollflag) {
		pause();
		return;
	}
	FD_ZERO(&mask);
	FD_SET(querysocket, &mask);
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	if (!pollflag)
	  timeout.tv_sec = 9999; /* VERY long time..
				    We wait for an interrupt.. */
	if ((n = select(querysocket+1, &mask, NULL, NULL, &timeout)) < 0) {
	  if (errno == EINTR)
	    return;
	} else if (n == 0) {
	  /* fprintf(stderr, "abnormal 0 return from select!\n"); */
	  return;
	} else {
	  raddrlen = sizeof raddr;
	  i = accept(querysocket, (struct sockaddr *)&raddr, &raddrlen);
	  if (i < 0) {
	    perror("querysocket accept");
	  } else {
	    /* Fork for QPRINT */
	    rc = fork();
	    if (rc == 0) { /* Child! */
	      close(querysocket);
	      qprint(i);
	      exit(0);
	    }
	    if (rc > 0)
	      ++numkids;
	    close(i);
	  }
	}
#else	/* !AF_INET */
	if (!pollflag)
	  pause();
#endif	/* AF_INET */
}

void
queryipcinit()
{
#ifdef	AF_INET
	time_t now;
	struct servent *serv;
	struct sockaddr_in sad;

	if (querysocket >= 0)
		return;
	now = time((time_t *)0);
	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 = INADDR_ANY;
	if ((querysocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
	  perror("socket");
	  return;
	}
	if (bind(querysocket, (struct sockaddr *)&sad, sizeof sad) < 0) {
	  perror("bind");
	  close(querysocket);
	  querysocket = -1;
	  return;
	}
	if (listen(querysocket, 5) < 0) {
	  perror("listen");
	  close(querysocket);
	  querysocket = -1;
	  return;
	}
	qipcretry = 0;
#endif	/* AF_INET */
}
#else	/* !HAVE_SELECT */
void
mux(quickcheck)
int quickcheck; /* Is always a quickcheck..  Aurgh the induced load.. */
{
	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 < FD_SETSIZE ; ++fd)
	    if (cpids[fd].pid != 0)
	      readfrom(fd);
}

ipcpause(pollflag)
int pollflag;
{
	if (!pollflag)
		pause();
}

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

void
readfrom(fd)
	int fd;
{
	int	n, dontbreak, bufsize = 2048;
	char	*cp, *pcp, *eobuf, *buf;

	dontbreak = 0;
	cp = pcp = NULL;

	buf = (char *)emalloc(bufsize);

	if (cpids[fd].carryover != NULL) {
	  int carrylen = strlen(cpids[fd].carryover);
	  if (carrylen > bufsize) {
	    while (carrylen > bufsize)
	      bufsize += 1024;
	    buf = erealloc(buf,bufsize);
	  }
	  strcpy(buf, cpids[fd].carryover);
	  cp = buf+strlen(buf);
	  pcp = buf;
	  free(cpids[fd].carryover);
	  cpids[fd].carryover = NULL;
	  dontbreak = 1;
	}
	/* Note that if we get an alarm() call, the read will return -1, TG */
	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 processed: %s\n",
			fd, pcp);
	      update(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);
	    } else {
	      memcpy(buf, pcp, cp-pcp);
	      cp = buf + (cp-pcp);
	      *cp = '\0';
	      pcp = buf;	/* may be used below */
	    }
	  }
#ifndef	USE_BSDNODELAY
	  if (!dontbreak)
	    break;
#endif				/* !USE_BSDNODELAY */
	}
	if (verbose) {
	  fprintf(stderr, "read from %d returns %d, errno=%d\n", fd, n, errno);
	}
	if (n == 0 || (n < 0 && errno != EINTR && errno != EWOULDBLOCK
		       && errno != EAGAIN)) {
	  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 (cpids[fd].pid != 0) {
	    cpids[fd].carryover = emalloc(cp-pcp+1);
	    memcpy(cpids[fd].carryover, pcp, cp-pcp);
	    *(cpids[fd].carryover+(cp-pcp)) = '\0';
	  } else
	    fprintf(stderr,
		    "HELP! Lost %d bytes (n=%d/%d, off=%d): '%s'\n",
		    cp - pcp, n, errno, pcp-buf, pcp);
	}
	free(buf);
}

#if 0
close(fd)
	int fd;
{
	fprintf(stderr, "closing fd %d\n", fd);
	if (fd == querysocket && getppid() == 1)
		abort();
	xclose(fd);
}
#endif


#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:
                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 < FD_SETSIZE; i++) {
		      if (cpids[i].pid == r) {
			cpids[i].pid = -r;
			break;
		      }
		    }
		  }

		  if ((st & 0x00ff) == 0) {
		    return st >> 8;
		  }

		  return 1;
		}
}

#endif
