Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/ChildOps.c
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/ChildOps.c	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/ChildOps.c	(revision 4764)
@@ -0,0 +1,139 @@
+# include "pclient.h"
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stropts.h>
+
+static int Nbad = 0;
+static struct timeval last = {0, 0};
+
+int InitChild () {
+
+  ChildPID         = 0;                /** current child PID **/
+  ChildStatus      = PCLIENT_NONE;     /** current status of child **/
+  ChildExitStatus  = 0;                /** recent exit status of child **/
+  
+  child_stdin_fd[0]  = 0;
+  child_stdin_fd[1]  = 0;
+  child_stdout_fd[0] = 0;
+  child_stdout_fd[1] = 0;
+  child_stderr_fd[0] = 0;
+  child_stderr_fd[1] = 0;
+
+  InitIOBuffer (&child_stdout, 1024);
+  InitIOBuffer (&child_stderr, 1024);
+  return (TRUE);
+}
+
+int CheckChild () {
+
+  int Nread;
+  pid_t ppid;
+  double dtime;
+  struct timeval now;
+
+  /* runaway test - if pcontrol is killed, pclient starts running away.  this test is a bit
+     dangerous: the choice of dtime probably depends on the processor and the value provided to
+     pclient.c:rl_set_keyboard_input_timeout (1000); note that we cannot use getppid == 1 as a test
+     because the parent of pclient is the ssh process on the pclient host, not pcontrol.  in any
+     case, the opihi shell catches if the ssh dies using getppid
+   */
+  gettimeofday (&now, (void *) NULL);
+  dtime = 1e6*DTIME (now, last);
+  if (dtime < 100) {
+    Nbad ++;
+    if (Nbad > 10) {
+      fprintf (stderr, "runaway!\n");
+      exit (2);
+    }
+  }
+  if (dtime > 950) Nbad = 0;
+  last = now;
+
+  CheckChildStatus ();
+  
+  /* read stdout buffer */
+  Nread = ReadtoIOBuffer (&child_stdout, child_stdout_fd[0]);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      fprintf (stderr, "serious IO error\n");
+      exit (2);
+    case -1:  /* no data in pipe */
+      break;
+    case 0:   /* pipe is closed */
+      /** change child state? **/
+      break;
+    default:  /* data in pipe */
+      break;
+  }
+  
+  /* read stderr buffer */
+  Nread = ReadtoIOBuffer (&child_stderr, child_stderr_fd[0]);
+  switch (Nread) {
+    case -2:  /* error in read (programming error?  system level error?) */
+      fprintf (stderr, "serious IO error\n");
+      exit (2);
+    case -1:  /* no data in pipe */
+      break;
+    case 0:   /* pipe is closed */
+      /** change child state? **/
+      break;
+    default:  /* data in pipe */
+      break;
+  }
+  return (TRUE);
+}
+
+void CheckChildStatus () {
+
+  int result, waitstatus;
+
+  if (ChildStatus != PCLIENT_BUSY) return;
+
+  /* check current child status */
+  result = waitpid (ChildPID, &waitstatus, WNOHANG);
+  switch (result) {
+    case -1:  /* error with waitpid */
+      switch (errno) {
+	case ECHILD:
+	  fprintf (stderr, "unknown PID, not a child proc\n");
+	  fprintf (stderr, "did process already exit?  programming error?\n");
+	  ChildStatus = PCLIENT_NONE;
+	  break;
+	case EINVAL:
+	  fprintf (stderr, "error EINVAL (waitpid): programming error\n");
+	  exit (1);
+	case EINTR:
+	  fprintf (stderr, "error EINTR (waitpid): programming error\n");
+	  exit (1);
+	default:
+	  fprintf (stderr, "unknown error for waitpid (%d): programming error\n", errno);
+	  exit (1);
+      }
+      break;
+      
+    case 0:  /* process not exited */
+      ChildStatus = PCLIENT_BUSY;
+      break;
+
+    default:
+      if (result != ChildPID) {
+	fprintf (stderr, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, ChildPID);
+	exit (1);
+      }
+      
+      if (WIFEXITED(waitstatus)) {
+	ChildStatus = PCLIENT_EXIT;
+	ChildExitStatus = WEXITSTATUS(waitstatus);
+      }
+      if (WIFSIGNALED(waitstatus)) {
+	ChildStatus = PCLIENT_CRASH;
+	ChildExitStatus = WTERMSIG(waitstatus);
+      }
+      if (WIFSTOPPED(waitstatus)) {
+	fprintf (stderr, "waitpid returns 'stopped': programming error\n");
+	exit (1);
+      }
+  }
+  return;
+}
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/Makefile
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/Makefile	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/Makefile	(revision 4764)
@@ -0,0 +1,53 @@
+include ../../../Configure
+
+HOME    =       $(ROOT)/src/opihi
+BIN     =       $(HOME)/bin
+LIB     =       $(HOME)/lib
+INC     =       $(HOME)/include
+SDIR    =       $(HOME)/pclient
+#
+DESTBIN =       $(LBIN)
+DESTLIB =       $(LLIB)
+DESTINC =       $(LINC)
+DESTMAN =       $(LMAN)
+DESTHLP =       $(LHLP)/pclient
+#  
+INCS    =       -I$(INC) -I$(LINC) -I$(XINC)
+LFLAGS  =       -L$(LLIB) -L$(LIB)
+LIBS1   =       -lFITS -lsocket -lnsl -lreadline -ltermcap -lohana -lm
+LIBS2   =       -lbasiccmd -lshell -ldata
+LIBS    =       $(LIBS2) $(LIBS1) 
+CFLAGS  =       $(INCS) -DHELPDIR_DEFAULT=$(DESTHLP)
+CCFLAGS =       $(LIBS) 
+
+# mana user commands and support functions ########################
+
+funcs = \
+$(SDIR)/init.$(ARCH).o \
+$(SDIR)/pclient.$(ARCH).o \
+$(SDIR)/ChildOps.$(ARCH).o \
+
+cmds = \
+$(SDIR)/job.$(ARCH).o \
+$(SDIR)/reset.$(ARCH).o \
+$(SDIR)/check.$(ARCH).o \
+$(SDIR)/status.$(ARCH).o \
+$(SDIR)/stdout.$(ARCH).o  \
+$(SDIR)/version.$(ARCH).o
+
+libs = \
+$(DESTLIB)/libbasiccmd.a \
+$(DESTLIB)/libshell.a \
+$(DESTLIB)/libdata.a
+
+pclient: $(BIN)/pclient.$(ARCH)
+
+$(BIN)/pclient.$(ARCH) : $(funcs) $(cmds) $(libs)
+
+install: $(DESTBIN)/pclient help
+
+help: clean-help cmd.basic.help pclient.help
+
+.PHONY: pclient
+
+include ../Makefile.Common
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/check.c
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/check.c	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/check.c	(revision 4764)
@@ -0,0 +1,9 @@
+# include "pclient.h"
+
+int check (int argc, char **argv) {
+
+  /* force a check */
+  CheckChild ();
+  return (TRUE);
+
+}
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/help/job
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/help/job	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/help/job	(revision 4764)
@@ -0,0 +1,1 @@
+0
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/init.c
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/init.c	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/init.c	(revision 4764)
@@ -0,0 +1,29 @@
+# include "opihi.h"
+
+int job	            PROTO((int, char **));
+int reset           PROTO((int, char **));
+int check           PROTO((int, char **));
+int status	    PROTO((int, char **));
+int stdout_pclient  PROTO((int, char **));
+int stderr_pclient  PROTO((int, char **));
+int version         PROTO((int, char **));
+
+static Command cmds[] = {  
+  {"job",       job,      "start job"},
+  {"reset",     reset,    "reset job"},
+  {"check",     check,    "check job"},
+  {"status",    status,   "check job status"},
+  {"stdout",    stdout_pclient,   "get stdout buffer"},
+  {"stderr",    stderr_pclient,   "get stderr buffer"},
+  {"version",   version,      "show version information"},
+}; 
+
+void InitPclient () {
+  
+  int i;
+
+  for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
+    AddCommand (&cmds[i]);
+  }
+
+}
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/job.c
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/job.c	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/job.c	(revision 4764)
@@ -0,0 +1,98 @@
+# include "pclient.h"
+
+int job (int argc, char **argv) {
+
+  int i, pid, status;
+  char **targv;
+
+  if (argc < 2) {
+    fprintf (stderr, "USAGE: job (arg0) (arg1) ... (argN)\n");
+    fprintf (GetOutfile(), "STATUS %d\n", -2);
+    return (FALSE);
+  }
+  
+  if (ChildStatus != PCLIENT_NONE) {
+    fprintf (stderr, "need to clear existing child\n");
+    fprintf (GetOutfile(), "STATUS %d\n", -3);
+    return (FALSE);
+  }
+
+  if (pipe (child_stdin_fd)  < 0) goto pipe_error;
+  if (pipe (child_stdout_fd) < 0) goto pipe_error;
+  if (pipe (child_stderr_fd) < 0) goto pipe_error;
+
+  /* need to define arg list with NULL termination */
+  ALLOCATE (targv, char *, argc);
+  for (i = 1; i < argc; i++) {
+    targv[i-1] = strcreate (argv[i]);
+  }
+  targv[i-1] = 0;
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    /* fprintf (stderr, "starting child process %s...\n", targv[0]); */
+
+    /* close the other ends of the pipes */
+    close (child_stdin_fd[1]);
+    close (child_stdout_fd[0]);
+    close (child_stderr_fd[0]);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (child_stdin_fd[0],  STDIN_FILENO);
+    dup2 (child_stdout_fd[1], STDOUT_FILENO);
+    dup2 (child_stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    /* exec job */ 
+    status = execvp (targv[0], targv); 
+    fprintf (stderr, "error starting child process: %d\n", status);
+    exit (1);
+  }
+
+  /* free temporary arg list */
+  for (i = 0; i < argc - 1; i++) {
+    free (targv[i]);
+  }
+  free (targv);
+
+  /* close the other ends of the pipes */
+  close (child_stdin_fd[0]);
+  close (child_stdout_fd[1]);
+  close (child_stderr_fd[1]);
+
+  /* make the pipes non-blocking */
+  fcntl (child_stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (child_stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (child_stderr_fd[0], F_SETFL, O_NONBLOCK);
+  
+  ChildStatus = PCLIENT_BUSY;
+  ChildPID = pid;
+
+  fprintf (GetOutfile(), "STATUS %d\n", ChildPID);
+  return (TRUE);
+
+pipe_error:
+  perror ("pipe error:");
+  if (child_stdin_fd[0]  != 0) close (child_stdin_fd[0]);
+  if (child_stdin_fd[1]  != 0) close (child_stdin_fd[1]);
+  if (child_stdout_fd[0] != 0) close (child_stdout_fd[0]);
+  if (child_stdout_fd[1] != 0) close (child_stdout_fd[1]);
+  if (child_stderr_fd[0] != 0) close (child_stderr_fd[0]);
+  if (child_stderr_fd[1] != 0) close (child_stderr_fd[1]);
+
+  fprintf (GetOutfile(), "STATUS %d\n", -1);
+  return (FALSE);
+}
+
+/* possible responses:
+
+STATUS -1 - pipe error
+STATUS -2 - syntax error
+STATUS -3 - existing child
+STATUS >0 - success (PID)
+
+*/
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/notes.txt
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/notes.txt	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/notes.txt	(revision 4764)
@@ -0,0 +1,17 @@
+
+pclient special commands:
+
+ job 
+
+ reset
+
+ check
+
+ status
+
+ stdout
+
+ stderr
+
+- communication with pclient should search for the pclient prompt:
+  pclient:
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/outline.txt
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/outline.txt	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/outline.txt	(revision 4764)
@@ -0,0 +1,12 @@
+
+init stage:
+
+  - set child 
+
+readline loop:
+
+  - wait for commands
+
+
+
+  
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/pclient.c
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/pclient.c	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/pclient.c	(revision 4764)
@@ -0,0 +1,55 @@
+# include "pclient.h"
+
+# define opihi_name "PCLIENT"
+# define opihi_prompt "pclient: "
+# define opihi_description "pcontrol client shell\n"
+# define opihi_history ".pclient"
+# define opihi_rcfile ".pcontrolrc"
+
+/* program-dependent initialization */
+void initialize (int argc, char **argv) {
+  
+  auto_break = TRUE;
+
+  /* load the commands used by this implementation */
+  InitBasic ();
+  InitPclient ();
+  InitChild ();
+
+  rl_readline_name = opihi_name;
+  rl_attempted_completion_function = command_completer;
+  rl_event_hook = CheckChild;
+  rl_set_keyboard_input_timeout (1000); 
+  /* 1 ms seems to be the minimum valid number */
+
+  set_str_variable ("HISTORY", opihi_history);
+  set_str_variable ("PROMPT", opihi_prompt);
+  set_str_variable ("RCFILE", opihi_rcfile);
+# ifdef HELPDIR_DEFAULT
+  set_str_variable ("HELPDIR", MACRO_NAME(HELPDIR_DEFAULT));
+# endif
+
+  /* ignore the history file.  to change this, see, eg, mana.c */
+  signal (SIGINT, SIG_IGN);
+  signal (SIGPIPE, gotsignal);
+  signal (SIGTSTP, gotsignal);
+  signal (SIGTTIN, gotsignal);
+  return;
+}
+
+/* standard welcome message */
+void welcome () {
+  fprintf (stderr, "\n");
+  fprintf (stderr, "Welcome to %s - %s\n\n", opihi_name, opihi_description);
+}
+
+/* add program-dependent exit functions here */
+void cleanup () {
+  return;
+}
+
+void gotsignal (int signum) {
+  fprintf (stderr, "got signal : %d\n", signum);
+  return;
+}
+
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/reset.c
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/reset.c	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/reset.c	(revision 4764)
@@ -0,0 +1,75 @@
+# include "pclient.h"
+
+int reset (int argc, char **argv) {
+
+  int i, result, waitstatus;
+  struct timespec request, remain;
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: reset\n");
+    fprintf (GetOutfile(), "STATUS -1\n");
+    return (FALSE);
+  }
+  
+  if (ChildStatus == PCLIENT_NONE) {
+    fprintf (stderr, "no child process, cannot reset\n");
+    fprintf (GetOutfile(), "STATUS 2\n");
+    return (TRUE);
+  }
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  if (ChildStatus == PCLIENT_BUSY) {
+    /* job is still running, send SIGTERM signal to job */
+    kill (ChildPID, SIGTERM);
+    result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    for (i = 0; (i < 500) && (result == 0); i++) {
+      nanosleep (&request, &remain);
+      result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    }
+    if (result) goto reset_job;
+
+    /* job did not exit, send SIGKILL signal to job */
+    kill (ChildPID, SIGKILL);
+    result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    for (i = 0; (i < 500) && (result == 0); i++) {
+      nanosleep (&request, &remain);
+      result = waitpid (ChildPID, &waitstatus, WNOHANG);
+    }
+    if (result) goto reset_job;
+
+    /* total failure, don't reset */
+    fprintf (stderr, "child process %d is hung, cannot reset\n", ChildPID);
+    fprintf (GetOutfile(), "STATUS 0\n");
+    return (FALSE);
+  }
+
+reset_job:
+  /* close out ends of the pipes */
+  close (child_stdin_fd[1]);
+  close (child_stdout_fd[0]);
+  close (child_stderr_fd[0]);
+  child_stdin_fd[1] = 0;
+  child_stdout_fd[0] = 0;
+  child_stderr_fd[0] = 0;
+
+  FlushIOBuffer (&child_stdout);
+  FlushIOBuffer (&child_stderr);
+  ChildStatus = PCLIENT_NONE;
+  ChildPID = 0;
+  ChildExitStatus = 0;
+
+  fprintf (GetOutfile(), "STATUS 1\n");
+  return (TRUE);
+}
+
+/* exit conditions:
+   -1 - usage message / syntax error
+    2 - unknown job
+    0 - process hung
+    1 - successful resetn
+*/
+
+/* the linux kernel timer sticks in a 10ms lag between kill and the harvest */
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/status.c
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/status.c	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/status.c	(revision 4764)
@@ -0,0 +1,24 @@
+# include "pclient.h"
+
+int status (int argc, char **argv) {
+
+  char status_string[64];
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: status\n");
+    return (FALSE);
+  }
+  
+  if (ChildStatus == PCLIENT_NONE)  strcpy (status_string, "NONE");
+  if (ChildStatus == PCLIENT_BUSY)  strcpy (status_string, "BUSY");
+  if (ChildStatus == PCLIENT_EXIT)  strcpy (status_string, "EXIT");
+  if (ChildStatus == PCLIENT_CRASH) strcpy (status_string, "CRASH");
+
+  fprintf (GetOutfile(), "STATUS %s\n", status_string);
+  fprintf (GetOutfile(), "EXITST %d\n", ChildExitStatus);
+  fprintf (GetOutfile(), "STDOUT %d\n", child_stdout.Nbuffer);
+  fprintf (GetOutfile(), "STDERR %d\n", child_stderr.Nbuffer);
+
+  set_str_variable ("JOBSTATUS", status_string);
+  return (TRUE);
+}
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/stdout.c
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/stdout.c	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/stdout.c	(revision 4764)
@@ -0,0 +1,29 @@
+# include "pclient.h"
+
+int stdout_pclient (int argc, char **argv) {
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: stdout\n");
+    fprintf (stdout, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  fwrite (child_stdout.buffer, 1, child_stdout.Nbuffer, stdout);
+  fprintf (stdout, "STATUS %d\n", 0);
+  return (TRUE);
+
+}
+
+int stderr_pclient (int argc, char **argv) {
+
+  if (argc != 1) {
+    fprintf (stderr, "USAGE: stderr\n");
+    fprintf (stdout, "STATUS %d\n", -1);
+    return (FALSE);
+  }
+  
+  fwrite (child_stderr.buffer, 1, child_stderr.Nbuffer, stdout);
+  fprintf (stdout, "STATUS %d\n", 0);
+  return (TRUE);
+
+}
Index: /tags/pclient-0-1/Ohana/src/opihi/pclient/version.c
===================================================================
--- /tags/pclient-0-1/Ohana/src/opihi/pclient/version.c	(revision 4764)
+++ /tags/pclient-0-1/Ohana/src/opihi/pclient/version.c	(revision 4764)
@@ -0,0 +1,14 @@
+# include "pclient.h"
+static char *name = "$Name: not supported by cvs2svn $";
+
+int version (int argc, char **argv) {
+
+  fprintf (stderr, "%s\n", name);
+
+  fprintf (stderr, "%s\n", opihi_version());
+  fprintf (stderr, "%s\n", ohana_version());
+  fprintf (stderr, "%s\n", fits_version());
+
+  fprintf (stderr, "compiled on %s %s\n", __DATE__, __TIME__);
+  return (TRUE);
+}
