/* cc -Wall -o do do.c -lutil */

/* This is to be used in a distributed parallel scrabble program,
   where the program does one level of lookahead; its parameters
   are read from stdin, and its results are printed to stdout.

   It'll be wrapped in a C procedure so that the application never
   sees all this behind-the-scenes stuff, and just does the equivalent
   of a popen()  Something like this:

   int calculate(int i, char *s) {
     int rc;
     create_process(command, &inputfd, &outputfd);
     outf = fdopen(outputfd, "w"); inf = fdopen(inputfd, "r");
     fprintf(outf, "%d\n%s\n", i, s);
     fscan(inf, "%d\n", &rc);
     return rc;
  }

  noting that the command isn't just a program name like "scrabhelper",
  it could be a call to ssh such as
  "ssh gtoal@hive0001.honeybeegardens.net scrabhelper"
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pwd.h>
#include <pty.h>
#include <sys/wait.h>

int master, slave, child, subchild, lb, l;
struct termios tt;
struct winsize win;
   
#define done() exit (((subchild ? close (master) : tcsetattr (0, TCSAFLUSH, &tt)), EXIT_SUCCESS))

static void fail (char *who) {
   perror (who);
   (void) kill (0, SIGTERM);
   done ();
}

static char *home (void) {
   struct passwd *p;
   return (((p = getpwuid (getuid ())) != NULL) ? p->pw_dir : NULL);
}

void finish (int arg) {
   union wait status;
   register int pid;
   while ((pid = wait3 ((int *) &status, WNOHANG, 0)) > 0) { if (pid == child) done (); }
}

int call(char *program) {
   struct termios rtt;
   int cc;
   (void) home ();

   /* getmaster */
   (void) tcgetattr (0, &tt);
   (void) ioctl (0, TIOCGWINSZ, (char *) &win);
   if (openpty (&master, &slave, NULL, &tt, &win) < 0) fail ("openpty");

   /* fixtty */
   rtt = tt;
   cfmakeraw (&rtt);
   rtt.c_lflag &= ~ECHO;
   (void) tcsetattr (0, TCSAFLUSH, &rtt);

   (void) signal (SIGCHLD, &finish);
   child = fork ();
   if (child == 0) {
      subchild = child = fork ();
      if (child == 0) {
	/* getslave */
	(void) setsid ();
	(void) ioctl (slave, TIOCSCTTY, 0);
	/* doshell */
	(void) close (master);
	(void) dup2 (slave, 0); (void) dup2 (slave, 1); (void) dup2 (slave, 2);
	(void) close (slave);
        system(program);
        return (EXIT_SUCCESS);
      } else if (child > 0) {
	/* dooutput */
	time_t tvec, time ();
	char obuf[BUFSIZ], *ctime ();
	(void) close (0);
	tvec = time ((time_t *) NULL);	/* unused for now. Needed for games with move time limit */
	(void) tvec;
	for (;;) {
	  if ((cc = read (master, obuf, sizeof (obuf))) <= 0) break;
	  (void) write (1, obuf, cc);
	}
	done ();
      }
   } else if (child > 0) {
     /* doinput */
      char ibuf[BUFSIZ];
      while ((cc = read (0, ibuf, BUFSIZ)) > 0) (void) write (master, ibuf, cc);
      done ();
   } 
   fail ("fork");
   return (EXIT_FAILURE);
}

int main (int argc, char **argv) {
  int rc;
  rc = call("/bin/bash -i");
  //rc = call("/usr/local/bin/ecce /dev/null");
  //rc = call("bash -c '/usr/local/bin/ecce /dev/null'; exit 0");
  fflush(stderr);
  sleep(1); /* without this call to sleep, we don't see the printf below???! */
  fprintf(stderr, "Exited %d.\n", rc);
  fflush(stderr);
  exit (rc);
  return (rc);
}