/*
char *startchild_defgb(name,cin,cout)
char *name;
FILE **cin, **cout;
  name: procedure name (either in search path or full path specified)
  cin: pointer to place into which startchild returns the stream pointer to
	opened file which will be child's stdin.  If NULL,
	nothing opened or returned
  cout: same as cin, but for child's stdout

  In parent context, returns NULL if things worked; pointer to (dynamically
	allocated) error string otherwise.
  In child context, we have new process if things worked; a message to stdout
	and an exit w/non-zero status (errno if non-0; 1 else-too bad if
	errno=1!) otherwise.
*/
char startchild_id[]="startchild_defgb version 1.2  27 Jan 99";
/*
  27 Jan 99.  Avoid opening & returning streams if they are given as NULL
	      Test more (hopefully all!) system calls
	      Return a pointer to an error string instead of errno.
		[Begin 1.2]
  25 Jan 99.  Return errno in case of parent error instead of exiting.
	Rename startchild startchild_defgb to reflect this calling sequence
	change.  WJS
		[Begin 1.1]
  15 May 98.  Type startchild so compiler is happy
  26 Apr 97.  Change error string to match JGOFS protocol.  WJS
  14 Mar 97.  Write in 1 printf instead of 2 (got first line but not
	2nd-hope this helps & doesn't eliminate both instead!)
	      Exit w/errno instead of 1 if errno non-zero.  WJS
   3 Jun 96.  Change error string so defgb can recognize it as error.  WJS
  30 May 96.  Mod to child process behavior after failed exec.  WJS
	1) print error info to stdout instead of stderr and
	flush it, thus feeding message to parent
	2) _exit()
		[Original source startchild.c from defexec stuff, I think]
*/

#include <errno.h>
#include <stdio.h>
#include <string.h>
#if SOL || HP
#include <sys/time.h>
#include <sys/resource.h>
#endif

int errno_sav;

void child_error(s,image)
char *s,*image;
{
    /*  "&x" is magic string causing defgb to interpret line as error	*/
    /*  message.  Defined as ERR_PREFIX in defgb.h, but not sure I	*/
    /*  should include that in what is possibly a library subroutine	*/
  printf("&x startchild_defgb %s error in child process\n%s: %s\n",
    s,image,strerror(errno));
  fflush(NULL);
  if (errno == 0) errno = 1;
  _exit(errno);
}

char *parent_error(s)
char *s;
{
  static char *err_string;
  char *err_ptr;
  char *msg = ". errno msg: ";
  int len_s,len_msg;

  err_ptr = (errno > 0) ? strerror(errno) : "[None-errno not set]";
  len_s = strlen(s);
  len_msg = strlen(msg);

  if (  (err_string = (char *)malloc(len_s + len_msg + strlen(err_ptr) + 1))
		    == NULL  						    ) {
    err_string = (errno > 0) ? err_ptr : s;
    errno = errno_sav;
    return err_string;
  }

  strcpy (err_string,s);
  strcpy (err_string+len_s,msg);
  strcpy (err_string+len_s+len_msg,err_ptr);

  errno = errno_sav;
  return err_string;
}

char *startchild_defgb(name,cin,cout)
char *name;
FILE **cin, **cout;
{
  static int pipeto[2],pipefrom[2];
  int i,j,numfds,childpid;
  char *args[20];
#if SOL || HP
  struct rlimit rlp;
#endif

  errno_sav = errno;
  errno = -1;	/* Set to a know value in case functions don't set it	*/

  if (pipe(pipeto) || pipe(pipefrom) <0)
    return parent_error("pipe problem");

  switch (childpid = fork()) {

    case -1:  /*  fork error  */
      return parent_error("Could not fork");

    case 0:   /*  child process */
	/*  Break up input string into argument vector			*/
      i = 0;
      args[i++] = strtok(name," ");
      while (  (args[i]=strtok(NULL," ")) != NULL  )
	i++;
				/*
				    fprintf(stderr,"got here after fork\n");
				    fprintf(stderr,"%s\n",name);
				*/

      if (dup2(pipeto[0],0) < 0) child_error("dup2-pipeto",args[0]);
      if (dup2(pipefrom[1],1) < 0) child_error("dup2-pipefrom",args[0]);

	/*  Close all open files except stdin, stdout, and stderr.  Do	*/
	/*  this by trying to close every legal file descriptor >= 3,	*/
	/*  ignoring BADF, which means that particular descriptor was	*/
	/*  not pointing to an open file (? or is otherwise illegal)	*/
#if SOL || HP
      if (getrlimit(RLIMIT_NOFILE,&rlp) != 0) child_error("getrlimit",args[0]);
      numfds = rlp.rlim_cur;
#else
      numfds = getdtablesize();
#endif
      for (i=3; i<numfds; i++) {
	j = close(i);
	if ( ! ((j==0) || (errno==EBADF)) ) child_error("close",args[0]);
      }
      errno = -1;	/*  Reset to known value			*/

		    /*
		      i = 0;
		      while(args[i])fprintf(stderr,"arg %d = %s\n",i,args[i++]);
		    */

	/*  Overlay forked copy of calling program with program child	*/
	/*  should be running						*/
      execvp(args[0],args);
	/*  Error if we return from execvp				*/
      child_error("execvp",args[0]);

    default:  /*  parent process  */

	/*  Need to deal with pipes.  First close descriptors child is	*/
	/*  using.  Then connect file descriptors we want to use with	*/
	/*  streams sent to this routine, or close the descriptors	*/

	  /*  Close child's descriptors					*/
      if (close(pipeto[0]) != 0)
	return parent_error("Problem closing pipeto[0]");
      if (close(pipefrom[1]) != 0)
	return parent_error("Problem closing pipefrom[1]");
						/*    ptoch = pipeto[1];*/
						/*    pfrch = pipefrom[0];*/

	  /*  Deal with pipe that will write to child's stdin		*/
      if (cin != NULL) {
	if (  (*cin = fdopen(pipeto[1],"w")) == NULL  )
	  return parent_error("Problem connecting pipeto[1] to stream");
	setbuf(*cin,NULL);
      } else {
        if (close(pipeto[1]) != 0)
	  return parent_error("Problem closing pipeto[1]");
      }

	  /*  Deal with pipe that will read from child's stdout		*/
      if (cout != NULL) {
	if (  (*cout = fdopen(pipefrom[0],"r")) == NULL  )
	  return parent_error("Problem connecting pipefrom[0] to stream");
						/*    setbuf(frch,NULL);*/
      } else
	if (close(pipefrom[0]) != 0)
	  return parent_error("Problem closing pipefrom[0]");

      break;
    }

  errno = errno_sav;
  return NULL;
}
