Index: /trunk/Ohana/src/opihi/include/pcontrol.h
===================================================================
--- /trunk/Ohana/src/opihi/include/pcontrol.h	(revision 4704)
+++ /trunk/Ohana/src/opihi/include/pcontrol.h	(revision 4705)
@@ -83,5 +83,6 @@
   int         pid;
   HostStat    stack;
-  Ptime       start;
+  struct timeval lasttry;
+  struct timeval nexttry;
   Ptime       accum;
   Ptime       timer;
@@ -101,4 +102,6 @@
 # define FREE(X) if (X != NULL) { free (X); }
 # define CLOSE(FD) { if (FD) close (FD); FD = 0; }
+# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
+# define ZTIME(A) ((A.tv_sec == 0) && (A.tv_usec == 0))
 
 void InitPcontrol ();
@@ -113,4 +116,5 @@
 Host *FindHostPtr (IDtype HostID, int StackID);
 Host *FindHostStack (IDtype HostID);
+int FindNamedHostStack (char *name);
 Host *PullHost (IDtype HostID, int StackID);
 int FindNamedHost (char *name, int StackID);
Index: /trunk/Ohana/src/opihi/include/psched.h
===================================================================
--- /trunk/Ohana/src/opihi/include/psched.h	(revision 4704)
+++ /trunk/Ohana/src/opihi/include/psched.h	(revision 4705)
@@ -49,5 +49,5 @@
   Macro  *crash;			/* name is 'crash' */
   Macro  *timeout;
-  Macro  *def;
+  Macro  *defexit;
 
   int     NEXIT;
@@ -115,5 +115,4 @@
 
 void InitPsched ();
-
 void InitTasks ();
 Task *NextTask ();
@@ -132,4 +131,6 @@
 void InitTaskTimers ();
 int TaskHash (char *input);
+int RemoveTask (Task *task);
+Task *SetNewTask (Task *task);
 
 int NextJobID ();
Index: /trunk/Ohana/src/opihi/lib.data/IOBufferOps.c
===================================================================
--- /trunk/Ohana/src/opihi/lib.data/IOBufferOps.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/lib.data/IOBufferOps.c	(revision 4705)
@@ -19,4 +19,5 @@
   buffer[0].Nalloc = buffer[0].Nreset;
   REALLOCATE (buffer[0].buffer, char, buffer[0].Nalloc);
+  bzero (buffer[0].buffer, buffer[0].Nalloc);
 
   return (TRUE);
@@ -37,4 +38,5 @@
     REALLOCATE (buffer[0].buffer, char, buffer[0].Nalloc);
     Nfree = buffer[0].Nalloc - buffer[0].Nbuffer;
+    bzero (buffer[0].buffer + buffer[0].Nbuffer, Nfree);
   }
 
Index: /trunk/Ohana/src/opihi/lib.shell/MacroOps.c
===================================================================
--- /trunk/Ohana/src/opihi/lib.shell/MacroOps.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/lib.shell/MacroOps.c	(revision 4705)
@@ -53,5 +53,5 @@
   }
   for (i = 0; i < macro[0].Nlines; i++) {
-    fprintf (stderr, "%s\n", macro[0].line[i]);
+    fprintf (stderr, "  %s\n", macro[0].line[i]);
   }
   return;
Index: /trunk/Ohana/src/opihi/lib.shell/memstr.c
===================================================================
--- /trunk/Ohana/src/opihi/lib.shell/memstr.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/lib.shell/memstr.c	(revision 4705)
@@ -1,4 +1,5 @@
 # include "shell.h"
 
+/* memstr returns a view, not an allocated string : don't free */
 /* returns pointer to start of m2 in m1, or NULL if failure */ 
 char *memstr (char *m1, char *m2, int n) {
@@ -12,2 +13,23 @@
 
 }
+
+/* formatted write statement, with intelligent allocation */
+int write_fmt (int fd, char *format, ...) {
+
+  int Nbyte, status;
+  char tmp, *line;
+  va_list argp;  
+
+  va_start (argp, format);
+  Nbyte = vsnprintf (&tmp, 0, format, argp);
+  va_end (argp);
+
+  va_start (argp, format);
+  ALLOCATE (line, char, Nbyte + 1);
+  vsnprintf (line, Nbyte + 1, format, argp);
+  status = write (fd, line, strlen(line));
+  va_end (argp);
+
+  free (line);
+  return (status);
+}
Index: /trunk/Ohana/src/opihi/pantasks/CheckJobs.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/CheckJobs.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/CheckJobs.c	(revision 4705)
@@ -26,9 +26,10 @@
       case JOB_CRASH:
 	/* run task[0].crash macro, if it exists */
-	/* set the stdout and stderr variables with job.stdout, job.stderr */
-	/* XXX this will break on 0 values in output streams */
+	/* push output buffer data to the stdout and stderr queues */
 	if (VerboseMode()) fprintf (stderr, "job %s (%d) crash\n", job[0].task[0].name, job[0].JobID);
 	PushNamedQueue ("stdout", job[0].stdout.buffer);
 	PushNamedQueue ("stderr", job[0].stderr.buffer);
+	/* XXX this will break on 0 values in output streams */
+	/* perhaps define PushNamedQueueBuffer */
 	if (job[0].task[0].crash != NULL) {
 	  exec_loop (job[0].task[0].crash);
@@ -43,5 +44,5 @@
 	PushNamedQueue ("stderr", job[0].stderr.buffer);
 	/* run corresponding task[0].exit macro, if it exists */
-	macro = job[0].task[0].def;
+	macro = job[0].task[0].defexit;
 	for (i = 0; i < job[0].task[0].Nexit; i++) {
 	  if (job[0].exit_status == atoi(job[0].task[0].exit[i][0].name)) {
Index: /trunk/Ohana/src/opihi/pantasks/ControllerOps.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 4705)
@@ -1,15 +1,17 @@
 # include "psched.h"
-# define CONTROLLER_TIMEOUT 20
+/* adding a new host can delay controller up to a second or so */
+# define CONTROLLER_TIMEOUT 200
 # define CONNECT_TIMEOUT 300
 
 /* local static variables to hold the connection to the controller */
-static int status = FALSE;
+static int ControllerStatus = FALSE;
 static int stdin_cntl, stdout_cntl, stderr_cntl;
 static IOBuffer stdout_buffer;
 static IOBuffer stderr_buffer;
+static int ControllerPID = 0;
 
 /* test if the controller is running */
 int CheckControllerStatus () {
-  return (status);
+  return (ControllerStatus);
 }
 
@@ -29,5 +31,5 @@
 int CheckControllerJobStatus (Job *job) {
 
-  int outstate;
+  int outstate, status;
   char cmd[128], status_string[64];
   char *p;
@@ -41,19 +43,19 @@
   switch (status) {
     case CONTROLLER_DOWN:
-      fprintf (stderr, "controller is down\n");
+      if (VerboseMode()) fprintf (stderr, "controller is down\n");
       FreeIOBuffer (&buffer);
       return (FALSE);
 
     case CONTROLLER_HUNG:
-      fprintf (stderr, "controller is not responding\n");
+      if (VerboseMode()) fprintf (stderr, "controller is not responding\n");
       FreeIOBuffer (&buffer);
       return (FALSE);
 
     case CONTROLLER_GOOD:
-      fprintf (stderr, "message received (CheckControllerJobStatus)\n");
+      if (VerboseMode()) fprintf (stderr, "message received (CheckControllerJobStatus)\n");
       break;
 
     default:
-      fprintf (stderr, "unknown status for controller command: programming error\n");  
+      if (VerboseMode()) fprintf (stderr, "unknown status for controller command: programming error\n");  
       exit (1);
   }
@@ -105,8 +107,5 @@
 
   /* send command to get appropriate channel */
-  ALLOCATE (line, char, MAX (1, strlen(cmd) + 15));
-  sprintf (line, "%s %d\n", cmd, pid);
-  status = write (stdin_cntl, line, strlen(line));
-  free (line);
+  status = write_fmt (stdin_cntl, "%s %d\n", cmd, pid);
 
   /* is pipe still open? */
@@ -126,5 +125,5 @@
   if (status == -1) return (CONTROLLER_HUNG);
 
-  fprintf (stderr, "message received (GetJobOutput : %s)\n", cmd);  
+  if (VerboseMode()) fprintf (stderr, "message received (GetJobOutput : %s)\n", cmd);  
   /* drop extra bytes from pcontrol (not pclient:job) */
   buffer[0].Nbuffer = Nstart + Nbytes;
@@ -161,5 +160,5 @@
 
   /* construct the controller command portion */
-  if (!strcmp (job[0].task[0].host, "NONE")) {
+  if (!strcasecmp (job[0].task[0].host, "ANYHOST")) {
     sprintf (cmd, "job");
   } else {
@@ -177,10 +176,7 @@
   }
 
-  fprintf (stderr, "sending command to controller: %s\n", cmd);
   InitIOBuffer (&buffer, 0x100);
   ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
   free (cmd);
-
-  fprintf (stderr, "response from controller: %s\n", buffer.buffer);
 
   /* extract the job PID from the controller response */
@@ -203,9 +199,9 @@
   char *p;
   char **argv, cmd[128];
-  int i, pid;
+  int i, pid, status;
   int stdin_fd[2], stdout_fd[2], stderr_fd[2];
   IOBuffer buffer;
 
-  if (status) return (TRUE);
+  if (ControllerStatus) return (TRUE);
 
   if (VarConfig ("CONTROLLER", "%s", cmd) == NULL) strcpy (cmd, "pcontrol");
@@ -262,6 +258,5 @@
 
   /* send handshake command */
-  sprintf (cmd, "echo CONNECTED\n");
-  status = write (stdin_fd[1], cmd, strlen(cmd));
+  status = write_fmt (stdin_fd[1], "echo CONNECTED\n");
   if ((status == -1) && (errno == EPIPE)) goto pipe_error;
 
@@ -286,4 +281,6 @@
   InitIOBuffer (&stderr_buffer, 0x100);
 
+  ControllerPID = pid;
+  ControllerStatus = TRUE;
   fprintf (stderr, "Connected\n");
   return (TRUE);
@@ -316,13 +313,8 @@
   FlushIOBuffer (buffer);
 
-  fprintf (stderr, "send: %s\n", cmd);
-
-  /* send command to client (adding on \n) */
-  ALLOCATE (line, char, MAX (1, strlen(cmd)));
-  sprintf (line, "%s\n", cmd);
-  status = write (stdin_cntl, line, strlen(line));
-  free (line);
-
-  /* is pipe still open? */
+  if (VerboseMode()) fprintf (stderr, "send: %s\n", cmd);
+
+  /* send command, is pipe still open? */
+  status = write_fmt (stdin_cntl, "%s\n", cmd);
   if ((status == -1) && (errno == EPIPE)) return (CONTROLLER_DOWN);
   
@@ -335,7 +327,16 @@
     if (status == -1) usleep (10000);
   }
-  if (status ==  0) return (CONTROLLER_DOWN);
+  if (status ==  0) {
+    ControllerStatus = FALSE;
+    return (CONTROLLER_DOWN);
+  }
   if (status == -1) return (CONTROLLER_HUNG);
-  /* fprintf (stderr, "buffer.buffer: %s\n", buffer[0].buffer); */
+
+  /* need to strip off the prompt */
+  line = memstr (buffer[0].buffer, response, buffer[0].Nbuffer);
+  if (line != NULL) {
+    buffer[0].Nbuffer = line - buffer[0].buffer;
+    bzero (buffer[0].buffer + buffer[0].Nbuffer, buffer[0].Nalloc - buffer[0].Nbuffer);
+  }
   return (CONTROLLER_GOOD);
 }
@@ -345,5 +346,5 @@
   int Nread;
 
-  if (!status) return (TRUE);
+  if (!ControllerStatus) return (TRUE);
 
   /* read stdout buffer */
@@ -390,5 +391,5 @@
   IOBuffer buffer;
 
-  if (!CheckControllerStatus()) return (TRUE);
+  if (!ControllerStatus) return (TRUE);
 
   sprintf (cmd, "kill %d", job[0].pid);
@@ -403,15 +404,24 @@
 int QuitController () {
 
-  int status;
+  int i, status, waitstatus, result;
   char cmd[128];
   IOBuffer buffer;
 
-  if (!CheckControllerStatus()) return (TRUE);
+  if (!ControllerStatus) return (TRUE);
 
   sprintf (cmd, "quit");
   InitIOBuffer (&buffer, 0x100);
   status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
-  return (TRUE);
-}
-
-/* memstr returns a view, not an allocated string : don't free */
+  ControllerStatus = FALSE;
+  result = waitpid (ControllerPID, &waitstatus, WNOHANG);
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  /* 10 ms is min */
+    result = waitpid (ControllerPID, &waitstatus, WNOHANG);
+  }
+  ControllerPID = 0;
+  close (stdin_cntl);
+  close (stdout_cntl);
+  close (stderr_cntl);
+
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/pantasks/JobOps.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/JobOps.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/JobOps.c	(revision 4705)
@@ -50,11 +50,13 @@
   int i;
 
+  fprintf (stderr, "\n");
   if (Njobs == 0) {
-    fprintf (stderr, "no defined jobs\n");
+    fprintf (stderr, " no defined jobs\n");
     return;
   }
 
+  fprintf (stderr, " Jobs\n");
   for (i = 0; i < Njobs; i++) {
-    fprintf (stderr, "%d: %-15s %5d %20s (%lx)\n", Njobs, jobs[i][0].task[0].name, jobs[i][0].JobID, jobs[i][0].argv[0], (long) jobs[i][0].argv);
+    fprintf (stderr, " %d: %-15s %5d %20s (%lx)\n", Njobs, jobs[i][0].task[0].name, jobs[i][0].JobID, jobs[i][0].argv[0], (long) jobs[i][0].argv);
   }
   return;
@@ -76,7 +78,6 @@
   }
 
-  /* we need our own copy of task[0].argv 
-   *  argc is the number of valid args, like the usual command line.
-   *  we allocate one extra element, with value 0 to be passed to execvp
+  /* we need our own copy of task[0].argv argc is the number of valid args, like the usual command line.  we
+   *  allocate one extra element, with value 0 to be passed to execvp
    */
   job[0].argc = task[0].argc;
@@ -87,14 +88,11 @@
   job[0].argv[i] = 0;
 
-  /* other data from the task is needed by the job 
-     we carry a pointer back to the task.  this means we 
-     cannot modify the task once a job is created, or the changes will 
-     be applied to the existing jobs */
+  /* Other data from the task is needed by the job. We carry a pointer back to the task.  Changes to an
+     executing task are applied to the existing jobs (exit macros, poll_period, timeout) */
 
   job[0].task = task;
   
-  /* if we decide we need to be able to dynamically set task qualities
-     (like host, timeouts, etc), the we will need to have matched 
-     entries to these quantites in the job structure */
+  /* if we decide we need to be able to dynamically set task qualities (like host, timeouts, etc), the we will
+     need to have matched entries to these quantites in the job structure */
 
   jobs[Njobs] = job;
@@ -122,5 +120,5 @@
 }
 
-/** are we deleting the active job?? **/
+/* delete the job from the job list & adjust ActiveJob counter */
 int DeleteJob (Job *job) {
 
@@ -140,6 +138,7 @@
 
   FreeJob (jobs[Nm]);
-  for (i = Nm + 1; i < Njobs; i++)
-    jobs[i - 1] = jobs[i];
+  for (i = Nm; i < Njobs - 1; i++) {
+    jobs[i] = jobs[i + 1];
+  }
   Njobs --;
 
Index: /trunk/Ohana/src/opihi/pantasks/Makefile
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/Makefile	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/Makefile	(revision 4705)
@@ -41,4 +41,5 @@
 $(SDIR)/run.$(ARCH).o \
 $(SDIR)/stop.$(ARCH).o \
+$(SDIR)/pulse.$(ARCH).o \
 $(SDIR)/status.$(ARCH).o \
 $(SDIR)/kill.$(ARCH).o \
Index: /trunk/Ohana/src/opihi/pantasks/TaskOps.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/TaskOps.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/TaskOps.c	(revision 4705)
@@ -54,35 +54,41 @@
   int i, valid;
 
+  fprintf (stderr, "\n");
   if (Ntasks == 0) {
-    fprintf (stderr, "no defined tasks\n");
+    fprintf (stderr, " no defined tasks\n");
     return;
   }
 
-  fprintf (stderr, "Task Status\n");
-  fprintf (stderr, "  name            Njobs  command\n");
+  fprintf (stderr, " Task Status\n");
+  fprintf (stderr, "  * Name            Njobs  Command\n");
   for (i = 0; i < Ntasks; i++) {
     valid = CheckTimeRanges (tasks[i][0].ranges, tasks[i][0].Nranges);
+    if (verbose) fprintf (stderr, "\n");
     if (valid) {
-      fprintf (stderr, "+ ");
+      fprintf (stderr, "  + ");
     } else {
-      fprintf (stderr, "- ");
+      fprintf (stderr, "  - ");
     }
     if (tasks[i][0].argv == NULL) {
-      fprintf (stderr, "%-15s %4d   %-20s\n", tasks[i][0].name, tasks[i][0].Njobs, "dynamic");
+      fprintf (stderr, "%-15s %5d  %-20s\n", tasks[i][0].name, tasks[i][0].Njobs, "(dynamic)");
     } else {
-      fprintf (stderr, "%-15s %4d   %-20s\n", tasks[i][0].name, tasks[i][0].Njobs, tasks[i][0].argv[0]);
+      fprintf (stderr, "%-15s %5d  %-20s\n", tasks[i][0].name, tasks[i][0].Njobs, tasks[i][0].argv[0]);
     }
     if (verbose) {
       fprintf (stderr, "    spawn period: %f, polling period: %f, timeout period: %f\n", 
-	       tasks[i][0].poll_period, tasks[i][0].poll_period, tasks[i][0].poll_period);
-      if (tasks[i][0].host != NULL) {
-	if (tasks[i][0].host_required) {
-	  fprintf (stderr, "    host %s (required)\n", tasks[i][0].host);
-	} else {
-	  fprintf (stderr, "    host %s (desired)\n", tasks[i][0].host);
-	}
+	       tasks[i][0].exec_period, tasks[i][0].poll_period, tasks[i][0].timeout_period);
+      if (tasks[i][0].host == NULL) {
+	fprintf (stderr, "    task runs locally\n");
+	continue;
+      }
+      if (!strcasecmp(tasks[i][0].host, "ANYHOST")) {
+	fprintf (stderr, "    task host selected by controller\n");
+	continue;
+      }
+      if (tasks[i][0].host_required) {
+	fprintf (stderr, "    host %s (required)\n", tasks[i][0].host);
       } else {
-	fprintf (stderr, "    task runs locally\n");
-      }      
+	fprintf (stderr, "    host %s (desired)\n", tasks[i][0].host);
+      }
     }
   }
@@ -102,29 +108,44 @@
   }
 
-  fprintf (stderr, "macro %s\n", task[0].name);
-
-  fprintf (stderr, "command: ");
+  fprintf (stderr, "\n macro %s\n", task[0].name);
+
+  fprintf (stderr, "\n command: ");
   for (i = 0; i < task[0].argc; i++) {
     fprintf (stderr, "%s ", task[0].argv[i]);
   }
-
-  fprintf (stderr, "host: %s\n", task[0].host);
-  fprintf (stderr, "time periods: exec: %f  poll: %f  timeout: %f\n", 
+  fprintf (stderr, "\n\n");
+
+  if (task[0].host == NULL) {
+    fprintf (stderr, " task runs locally\n");
+    goto periods;
+  }
+  if (!strcasecmp(task[0].host, "ANYHOST")) {
+    fprintf (stderr, " task host selected by controller\n");
+    goto periods;
+  }
+  if (task[0].host_required) {
+    fprintf (stderr, " host %s (required)\n", task[0].host);
+  } else {
+    fprintf (stderr, " host %s (desired)\n", task[0].host);
+  }
+
+periods:
+  fprintf (stderr, " time periods: exec: %f  poll: %f  timeout: %f\n", 
 	   task[0].exec_period, task[0].poll_period, task[0].timeout_period);
 
-  fprintf (stderr, "pre-execute macro\n");
+  fprintf (stderr, "\n pre-execute macro\n");
   ListMacro (task[0].exec);
 
-  fprintf (stderr, "timeout macro\n");
+  fprintf (stderr, "\n timeout macro\n");
   ListMacro (task[0].timeout);
 
-  fprintf (stderr, "crash macro\n");
+  fprintf (stderr, "\n crash macro\n");
   ListMacro (task[0].crash);
 
-  fprintf (stderr, "default exit macro\n");
-  ListMacro (task[0].def);
+  fprintf (stderr, "\n default exit macro\n");
+  ListMacro (task[0].defexit);
 
   for (i = 0; i < task[0].Nexit; i++) {
-    fprintf (stderr, "exit macro (status == %d)\n", atoi(task[0].exit[i][0].name));
+    fprintf (stderr, "\n exit macro (status == %d)\n", atoi(task[0].exit[i][0].name));
     ListMacro (task[0].exit[i]);
   }
@@ -192,4 +213,5 @@
   NewTask[0].crash = NULL;
   NewTask[0].timeout = NULL;
+  NewTask[0].defexit = NULL;
 
   NewTask[0].Nexit = 0;
@@ -211,4 +233,28 @@
 
   return (NewTask);
+}
+
+/* remove the task from the task list */
+int RemoveTask (Task *task) {
+  
+  int i, Nt;
+
+  /* find task in task list */
+  Nt = -1;
+  for (i = 0; i < Ntasks; i++) {
+    if (task == tasks[i]) {
+      Nt = i;
+      break;
+    }
+  }
+  if (Nt == -1) {
+    fprintf (stderr, "programming error: task not found\n");
+    return (FALSE);
+  }
+  for (i = Nt; i < Ntasks - 1; i++) {
+    tasks[i] = tasks[i+1];
+  }
+  Ntasks --;
+  return (TRUE);
 }
 
@@ -263,4 +309,9 @@
   }
   return (TRUE);
+}
+
+Task *SetNewTask (Task *task) {
+  NewTask = task;
+  return (task);
 }
 
Index: /trunk/Ohana/src/opihi/pantasks/controller_check.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/controller_check.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/controller_check.c	(revision 4705)
@@ -21,4 +21,9 @@
   InitIOBuffer (&buffer, 0x100);
   status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  if (VerboseMode()) {
+    fprintf (stderr, "controller command sent\n");  
+    fwrite (buffer.buffer, 1, buffer.Nbuffer, stderr);
+    fprintf (stderr, "\n Nbytes received: %d\n", buffer.Nbuffer);  
+  }
   FreeIOBuffer (&buffer);
 
@@ -34,6 +39,4 @@
 
     case CONTROLLER_GOOD:
-      fprintf (stderr, "controller command sent\n");  
-      fwrite (buffer.buffer, 1, buffer.Nbuffer, stderr);
       return (TRUE);
 
Index: /trunk/Ohana/src/opihi/pantasks/controller_host.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/controller_host.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/controller_host.c	(revision 4705)
@@ -7,6 +7,6 @@
   IOBuffer buffer;
 
-  if (argc != 2) {
-    fprintf (stderr, "USAGE: controller host (hostname)\n");
+  if (argc != 3) {
+    fprintf (stderr, "USAGE: controller host (command) (hostname)\n");
     return (FALSE);
   }
@@ -15,7 +15,8 @@
   StartController ();
 
-  sprintf (command, "host %s", argv[1]);
+  sprintf (command, "host %s %s", argv[1], argv[2]);
   InitIOBuffer (&buffer, 0x100);
   status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  fwrite (buffer.buffer, 1, buffer.Nbuffer, stderr);
   FreeIOBuffer (&buffer);
 
@@ -31,7 +32,4 @@
 
     case CONTROLLER_GOOD:
-      fprintf (stderr, "controller command sent\n");  
-      fwrite (buffer.buffer, 1, buffer.Nbuffer, stderr);
-      fprintf (stderr, "\n Nbytes received: %d\n", buffer.Nbuffer);  
       return (TRUE);
 
Index: /trunk/Ohana/src/opihi/pantasks/controller_status.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/controller_status.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/controller_status.c	(revision 4705)
@@ -22,5 +22,4 @@
   InitIOBuffer (&buffer, 0x100);
   status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
-  FreeIOBuffer (&buffer);
 
   /* check on success of controller command */
@@ -28,14 +27,15 @@
     case CONTROLLER_DOWN:
       fprintf (stderr, "controller is down\n");
+      FreeIOBuffer (&buffer);
       return (FALSE);
 
     case CONTROLLER_HUNG:
       fprintf (stderr, "controller is not responding\n");
+      FreeIOBuffer (&buffer);
       return (FALSE);
 
     case CONTROLLER_GOOD:
-      fprintf (stderr, "controller command sent\n");  
       fwrite (buffer.buffer, 1, buffer.Nbuffer, stderr);
-      fprintf (stderr, "\n Nbytes received: %d\n", buffer.Nbuffer);  
+      FreeIOBuffer (&buffer);
       return (TRUE);
 
Index: /trunk/Ohana/src/opihi/pantasks/init.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/init.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/init.c	(revision 4705)
@@ -11,4 +11,5 @@
 int run             PROTO((int, char **));
 int stop            PROTO((int, char **));
+int pulse           PROTO((int, char **));
 int status_sys      PROTO((int, char **));
 int kill_job        PROTO((int, char **));
@@ -28,4 +29,5 @@
   {"run",        run,          "run the scheduler"},
   {"stop",       stop,         "stop the scheduler"},
+  {"pulse",      pulse,        "set the scheduler update period"},
   {"status",     status_sys,   "get system status"},
   {"kill",       kill_job,     "kill job"},
Index: /trunk/Ohana/src/opihi/pantasks/psched.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/psched.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/psched.c	(revision 4705)
@@ -39,5 +39,5 @@
   rl_attempted_completion_function = command_completer;
   rl_event_hook = NULL;
-  rl_set_keyboard_input_timeout (1000000); 
+  rl_set_keyboard_input_timeout (100000); 
 
   set_str_variable ("HISTORY", opihi_history);
Index: /trunk/Ohana/src/opihi/pantasks/pulse.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/pulse.c	(revision 4705)
+++ /trunk/Ohana/src/opihi/pantasks/pulse.c	(revision 4705)
@@ -0,0 +1,16 @@
+# include "psched.h"
+
+int pulse (int argc, char **argv) {
+
+  int Nusec;
+
+  if (argc != 2) {
+    fprintf (stderr, "USAGE: pulse (microseconds)\n");
+    return (FALSE);
+  }
+
+  Nusec = atoi (argv[1]);
+  rl_set_keyboard_input_timeout (Nusec); 
+
+  return (TRUE);
+}
Index: /trunk/Ohana/src/opihi/pantasks/run.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/run.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/run.c	(revision 4705)
@@ -10,5 +10,4 @@
   InitTaskTimers ();
   rl_event_hook = CheckSystem;
-  rl_set_keyboard_input_timeout (1000000); 
 
   return (TRUE);
Index: /trunk/Ohana/src/opihi/pantasks/status.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/status.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/status.c	(revision 4705)
@@ -3,8 +3,14 @@
 int status_sys (int argc, char **argv) {
 
+  fprintf (stderr, "\n");
   if (rl_event_hook == NULL) {
-    fprintf (stderr, "scheduler is stopped\n");
+    fprintf (stderr, " Scheduler is stopped\n");
   } else {
-    fprintf (stderr, "scheduler is running\n");
+    fprintf (stderr, " Scheduler is running\n");
+  }
+  if (CheckControllerStatus ()) {
+    fprintf (stderr, " Controller is running\n");
+  } else {
+    fprintf (stderr, " Controller is stopped\n");
   }
   ListTasks (FALSE);
Index: /trunk/Ohana/src/opihi/pantasks/stop.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/stop.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/stop.c	(revision 4705)
@@ -9,5 +9,4 @@
 
   rl_event_hook = NULL;
-  rl_set_keyboard_input_timeout (1000000); 
 
   return (TRUE);
Index: /trunk/Ohana/src/opihi/pantasks/task.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/task.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/task.c	(revision 4705)
@@ -36,11 +36,15 @@
   if (task == NULL) { /**** new task ****/
     task = CreateTask (argv[1]);
+  } else {
+    RemoveTask (task);
+    SetNewTask (task);
   }
-  /* temporary task is saved statically 
-     add to list after definition is complete */
+  /* While a task is being defined, it is removed from the task list.  The new task is added to the task list
+     when the definition process is complete.  
+     XXX If an outstanding job has a task deleted, it will not be able to complete... */
 
   /* read in task from appropriate source (keyboard or list) until end */
 
-  /* allowed tokens: command, host, stderr, periods, end */
+  /* allowed tokens: command, host, stderr, periods, trange, nmax, task.exit, task.exec, end */
 
   ThisList = Nlists;
Index: /trunk/Ohana/src/opihi/pantasks/task_command.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/task_command.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/task_command.c	(revision 4705)
@@ -8,5 +8,5 @@
   if (argc < 2) {
     fprintf (stderr, "USAGE: command <command> <arg>. ..\n");
-    fprintf (stderr, "  (define command machine for this task (or 'none'))\n");
+    fprintf (stderr, "  (define command for this task)\n");
     return (FALSE);
   }
Index: /trunk/Ohana/src/opihi/pantasks/task_host.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/task_host.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/task_host.c	(revision 4705)
@@ -17,5 +17,5 @@
     fprintf (stderr, "  -required flags indicates controller must use this host\n");
     fprintf (stderr, "  value of 'local' for host indicates process not using controller\n");
-    fprintf (stderr, "  value of 'none' for host indicates controller may assign at will\n");
+    fprintf (stderr, "  value of 'anyhost' for host indicates controller may assign at will\n");
     return (FALSE);
   }
@@ -23,6 +23,9 @@
   task = GetNewTask ();
   if (task == NULL) {
-    fprintf (stderr, "ERROR: not defining or running a task\n");
-    return (FALSE);
+    task = GetActiveTask ();
+    if (task == NULL) {
+      fprintf (stderr, "ERROR: not defining or running a task\n");
+      return (FALSE);
+    }
   }
   task[0].host_required = RequiredHost;
@@ -36,2 +39,4 @@
   return (TRUE);
 }
+
+/* apparently, local is the default! */
Index: /trunk/Ohana/src/opihi/pantasks/task_macros.c
===================================================================
--- /trunk/Ohana/src/opihi/pantasks/task_macros.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pantasks/task_macros.c	(revision 4705)
@@ -62,10 +62,10 @@
   }
   if (!strcmp (argv[0], "task.exit") && !strcmp (argv[1], "default")) {
-    if (task[0].def != NULL) {
-      FreeMacro (task[0].def);
-      free (task[0].def);
+    if (task[0].defexit != NULL) {
+      FreeMacro (task[0].defexit);
+      free (task[0].defexit);
     }
-    ALLOCATE (task[0].def, Macro, 1);
-    macro = task[0].def;
+    ALLOCATE (task[0].defexit, Macro, 1);
+    macro = task[0].defexit;
     macro[0].name = strcreate ("default");
     goto found;
Index: /trunk/Ohana/src/opihi/pcontrol/CheckIdleHost.c
===================================================================
--- /trunk/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 4705)
@@ -45,4 +45,6 @@
     return (TRUE);
   }
+  /* no jobs for host, but back on IDLE stack */
+  PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
   return (TRUE);
 }
Index: /trunk/Ohana/src/opihi/pcontrol/CheckSystem.c
===================================================================
--- /trunk/Ohana/src/opihi/pcontrol/CheckSystem.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pcontrol/CheckSystem.c	(revision 4705)
@@ -93,4 +93,8 @@
   Stack *stack;
   Host  *host;
+  struct timeval now;
+  float delta;
+
+  gettimeofday (&now, (void *) NULL);
 
   stack = GetHostStack (PCONTROL_HOST_DOWN);
@@ -99,5 +103,10 @@
   for (i = 0; i < Nobject; i++) {
     host = GetStack (stack, STACK_TOP);
-    StartHost (host);
+    delta = DTIME (host[0].nexttry, now);
+    if (delta > 0) {
+      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+    } else {
+      StartHost (host);
+    }
   }
   return (TRUE);
Index: /trunk/Ohana/src/opihi/pcontrol/GetJobOutput.c
===================================================================
--- /trunk/Ohana/src/opihi/pcontrol/GetJobOutput.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pcontrol/GetJobOutput.c	(revision 4705)
@@ -14,8 +14,5 @@
 
   /* send cmd (stdout / stderr) */
-  ALLOCATE (line, char, MAX (1, strlen(cmd) + 1));
-  sprintf (line, "%s\n", cmd);
-  status = write (host[0].stdin, line, strlen(line));
-  free (line);
+  status = write_fmt (host[0].stdin, "%s\n", cmd);
 
   /* is pipe still open? */
Index: /trunk/Ohana/src/opihi/pcontrol/HostOps.c
===================================================================
--- /trunk/Ohana/src/opihi/pcontrol/HostOps.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pcontrol/HostOps.c	(revision 4705)
@@ -57,4 +57,26 @@
 }
 
+int FindNamedHostStack (char *name) {
+
+  int N;
+
+  N = FindNamedHost (name, PCONTROL_HOST_IDLE);
+  if (N > 0) return (PCONTROL_HOST_IDLE);
+
+  N = FindNamedHost (name, PCONTROL_HOST_DOWN);
+  if (N > 0) return (PCONTROL_HOST_DOWN);
+
+  N = FindNamedHost (name, PCONTROL_HOST_DONE);
+  if (N > 0) return (PCONTROL_HOST_DONE);
+
+  N = FindNamedHost (name, PCONTROL_HOST_BUSY);
+  if (N > 0) return (PCONTROL_HOST_BUSY);
+
+  N = FindNamedHost (name, PCONTROL_HOST_OFF);
+  if (N > 0) return (PCONTROL_HOST_OFF);
+
+  return (-1);
+}
+
 int PutHost (Host *host, int StackID, int where) {
 
@@ -165,4 +187,9 @@
   host[0].HostID   = NextHostID();
 
+  host[0].lasttry.tv_sec = 0;
+  host[0].lasttry.tv_usec = 0;
+  host[0].nexttry.tv_sec = 0;
+  host[0].nexttry.tv_usec = 0;
+
   host[0].markoff  = FALSE;
   host[0].job      = NULL;
Index: /trunk/Ohana/src/opihi/pcontrol/JobOps.c
===================================================================
--- /trunk/Ohana/src/opihi/pcontrol/JobOps.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pcontrol/JobOps.c	(revision 4705)
@@ -161,4 +161,5 @@
 
   PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
+  fprintf (stderr, "added new job\n");
   return (job[0].JobID);
 }
Index: /trunk/Ohana/src/opihi/pcontrol/StartHost.c
===================================================================
--- /trunk/Ohana/src/opihi/pcontrol/StartHost.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pcontrol/StartHost.c	(revision 4705)
@@ -1,3 +1,4 @@
 # include "pcontrol.h"
+# define RETRY_BASE 1.0
 
 int StartHost (Host *host) {
@@ -6,6 +7,6 @@
   int stdio[3];
   char command[64], shell[64];
-
-  /* pid = rconnect (CONNECT, host[0].hostname, PCLIENT, stdio); */
+  struct timeval now;
+  float delta;
 
   /* perhaps change the name of these config variables... */
@@ -14,9 +15,26 @@
 
   pid = rconnect (command, host[0].hostname, shell, stdio);
-  if (!pid) {     /** failure to start **/
+  if (!pid) {     
+    /** failure to start: extend retry period **/
     if (VerboseMode()) fprintf (stderr, "failure to start %s\n", host[0].hostname);
+    gettimeofday (&now, (void *) NULL);
+    if (ZTIME(host[0].nexttry) || ZTIME(host[0].lasttry)) {
+      /* reset retry period if either is zero */
+      delta = RETRY_BASE;
+    } else {
+      delta = 2*DTIME (host[0].nexttry, host[0].lasttry);
+    }
+    host[0].nexttry.tv_sec  = now.tv_sec  + delta;
+    host[0].nexttry.tv_usec = now.tv_usec;
+    host[0].lasttry.tv_sec  = now.tv_sec;
+    host[0].lasttry.tv_usec = now.tv_usec;
     PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
     return (FALSE);
   }
+  host[0].nexttry.tv_sec  = 0;
+  host[0].nexttry.tv_usec = 0;
+  host[0].lasttry.tv_sec  = 0;
+  host[0].lasttry.tv_usec = 0;
+
   host[0].stdin  = stdio[0];
   host[0].stdout = stdio[1];
Index: /trunk/Ohana/src/opihi/pcontrol/host.c
===================================================================
--- /trunk/Ohana/src/opihi/pcontrol/host.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pcontrol/host.c	(revision 4705)
@@ -3,67 +3,92 @@
 int host (int argc, char **argv) {
 
+  int N, Ns;
   IDtype HostID;
-  int N, Delete, Off, On, Start, Stop, Check;
   Host *host;
 
-  /* this section needs some help: find the specified host in the stacks */
-  Delete = FALSE;
-  if ((N = get_argument (argc, argv, "-delete"))) {
-    remove_argument (N, &argc, argv);
-    Delete = TRUE;
+  if (argc != 3) {
+    fprintf (stdout, "USAGE: host (command) (hostname)\n");
+    return (FALSE);
   }
- 
-  /* this section needs some help: find the specified host in the stacks */
-  Off = FALSE;
-  if ((N = get_argument (argc, argv, "-off"))) {
-    if (Delete) {
-      fprintf (stdout, "-delete and -off incompatible\n");
+
+  if (!strcasecmp (argv[1], "ADD")) {
+    HostID = AddHost (argv[2]);
+    fprintf (stdout, "HostID: %d\n", (int) HostID);
+    return (TRUE);
+  }
+  if (!strcasecmp (argv[1], "ON")) {
+    N = FindNamedHost (argv[2], PCONTROL_HOST_OFF);
+    if (N < 0) {
+      fprintf (stdout, "host %s is not OFF\n", argv[2]);
       return (FALSE);
     }
-    remove_argument (N, &argc, argv);
-    Off = TRUE;
+    host = GetHost (PCONTROL_HOST_OFF, N);
+    DownHost (host);
+    return (TRUE);
   }
- 
-  /* this section needs some help: find the specified host in the stacks */
-  On = FALSE;
-  if ((N = get_argument (argc, argv, "-on"))) {
-    if (Delete || Off) {
-      fprintf (stdout, "only one of -delete, -off, -on\n");
+  if (!strcasecmp (argv[1], "RETRY")) {
+    N = FindNamedHost (argv[2], PCONTROL_HOST_DOWN);
+    if (N < 0) {
+      fprintf (stdout, "host %s is not DOWN\n", argv[2]);
       return (FALSE);
     }
-    remove_argument (N, &argc, argv);
-    On = TRUE;
+    host = GetHost (PCONTROL_HOST_DOWN, N);
+    host[0].nexttry.tv_sec  = 0;
+    host[0].nexttry.tv_usec = 0;
+    host[0].lasttry.tv_sec  = 0;
+    host[0].lasttry.tv_usec = 0;
+    StartHost (host);
+    return (TRUE);
   }
- 
-  /* this section needs some help: find the specified host in the stacks */
-  Start = FALSE;
-  if ((N = get_argument (argc, argv, "-start"))) {
-    remove_argument (N, &argc, argv);
-    Start = TRUE;
-  }
- 
-  /* this section needs some help: find the specified host in the stacks */
-  Check = FALSE;
-  if ((N = get_argument (argc, argv, "-check"))) {
-    remove_argument (N, &argc, argv);
-    Check = TRUE;
-  }
- 
-  /* this section needs some help: find the specified host in the stacks */
-  Stop = FALSE;
-  if ((N = get_argument (argc, argv, "-stop"))) {
-    remove_argument (N, &argc, argv);
-    Stop = TRUE;
-  }
- 
-  if (argc != 2) {
-    fprintf (stdout, "USAGE: host (hostname) [-delete]\n");
+  if (!strcasecmp (argv[1], "CHECK")) {
+    Ns = FindNamedHostStack (argv[2]);
+    switch (Ns) {
+      case PCONTROL_HOST_IDLE:
+      case PCONTROL_HOST_BUSY:
+      case PCONTROL_HOST_DONE:
+	N = FindNamedHost (argv[2], Ns);
+	host = GetHost (Ns, N);
+	CheckHost (host);
+	return (TRUE);
+      case PCONTROL_HOST_DOWN:
+	fprintf (stdout, "host %s is DOWN\n", argv[2]);
+	return (TRUE);
+      case PCONTROL_HOST_OFF:
+	fprintf (stdout, "host %s is OFF\n", argv[2]);
+	return (TRUE);
+      default:
+	fprintf (stdout, "host %s not found\n", argv[2]);
+	return (FALSE);
+    }
     return (FALSE);
   }
-  
-  if (Delete) {
-    N = FindNamedHost (argv[1], PCONTROL_HOST_OFF);
+  if (!strcasecmp (argv[1], "OFF")) {
+    N = FindNamedHost (argv[2], PCONTROL_HOST_IDLE);
+    if (N >= 0) {
+      host = GetHost (PCONTROL_HOST_IDLE, N);
+      StopHost (host);
+      OffHost (host);
+      return (TRUE);
+    }
+    N = FindNamedHost (argv[2], PCONTROL_HOST_DOWN);
+    if (N >= 0) {
+      host = GetHost (PCONTROL_HOST_DOWN, N);
+      OffHost (host);
+      return (TRUE);
+    }
+    N = FindNamedHost (argv[2], PCONTROL_HOST_BUSY);
+    if (N >= 0) {
+      host = GetHost (PCONTROL_HOST_BUSY, N);
+      host[0].markoff  = TRUE;
+      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
+      return (TRUE);
+    }
+    fprintf (stdout, "host %s is not BUSY, IDLE, or DOWN\n", argv[2]);
+    return (FALSE);
+  }
+  if (!strcasecmp (argv[1], "DELETE")) {
+    N = FindNamedHost (argv[2], PCONTROL_HOST_OFF);
     if (N < 0) {
-      fprintf (stdout, "host %s is not OFF\n", argv[1]);
+      fprintf (stdout, "host %s is not OFF\n", argv[2]);
       return (FALSE);
     }
@@ -72,82 +97,7 @@
     return (TRUE);
   }
-
-  if (On) {
-    N = FindNamedHost (argv[1], PCONTROL_HOST_OFF);
-    if (N < 0) {
-      fprintf (stdout, "host %s is not OFF\n", argv[1]);
-      return (FALSE);
-    }
-    host = GetHost (PCONTROL_HOST_OFF, N);
-    DownHost (host);
-    return (TRUE);
-  }
-
-  if (Check) {
-    N = FindNamedHost (argv[1], PCONTROL_HOST_IDLE);
-    if (N >= 0) {
-      host = GetHost (PCONTROL_HOST_IDLE, N);
-      CheckHost (host);
-      return (TRUE);
-    }
-    N = FindNamedHost (argv[1], PCONTROL_HOST_BUSY);
-    if (N >= 0) {
-      host = GetHost (PCONTROL_HOST_BUSY, N);
-      CheckHost (host);
-      return (TRUE);
-    }
-    fprintf (stdout, "host %s is not BUSY or IDLE\n", argv[1]);
-    return (FALSE);
-  }
-
-  if (Start) {
-    N = FindNamedHost (argv[1], PCONTROL_HOST_DOWN);
-    if (N < 0) {
-      fprintf (stdout, "host %s is not DOWN\n", argv[1]);
-      return (FALSE);
-    }
-    host = GetHost (PCONTROL_HOST_DOWN, N);
-    StartHost (host);
-    return (TRUE);
-  }
-  if (Stop) {
-    N = FindNamedHost (argv[1], PCONTROL_HOST_IDLE);
-    if (N < 0) {
-      fprintf (stdout, "host %s is not IDLE\n", argv[1]);
-      return (FALSE);
-    }
-    host = GetHost (PCONTROL_HOST_IDLE, N);
-    StopHost (host);
-    DownHost (host);
-    return (TRUE);
-  }
-
-  if (Off) {
-    N = FindNamedHost (argv[1], PCONTROL_HOST_IDLE);
-    if (N >= 0) {
-      host = GetHost (PCONTROL_HOST_IDLE, N);
-      StopHost (host);
-      OffHost (host);
-      return (TRUE);
-    }
-    N = FindNamedHost (argv[1], PCONTROL_HOST_DOWN);
-    if (N >= 0) {
-      host = GetHost (PCONTROL_HOST_DOWN, N);
-      OffHost (host);
-      return (TRUE);
-    }
-    N = FindNamedHost (argv[1], PCONTROL_HOST_BUSY);
-    if (N >= 0) {
-      host = GetHost (PCONTROL_HOST_BUSY, N);
-      host[0].markoff  = TRUE;
-      PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
-      return (TRUE);
-    }
-    fprintf (stdout, "host %s is not BUSY, IDLE, or DOWN\n", argv[1]);
-    return (FALSE);
-  }
-
-  HostID = AddHost (argv[1]);
-  fprintf (stdout, "HostID: %d\n", (int) HostID);
-  return (TRUE);
+  
+  fprintf (stderr, "unknown host command %s\n", argv[1]);
+  fprintf (stderr, "valid options: xxx\n");
+  return (FALSE);
 }
Index: /trunk/Ohana/src/opihi/pcontrol/pclient.c
===================================================================
--- /trunk/Ohana/src/opihi/pcontrol/pclient.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pcontrol/pclient.c	(revision 4705)
@@ -12,9 +12,5 @@
 
   /* send command to client (adding on \n) */
-  /* fprintf (stderr, "send: %s (%d)\n", command, buffer[0].Nbuffer); */
-  ALLOCATE (line, char, MAX (1, strlen(command) + 1));
-  sprintf (line, "%s\n", command);
-  status = write (host[0].stdin, line, strlen(line));
-  free (line);
+  status = write_fmt (host[0].stdin, "%s\n", command);
 
   /* is pipe still open? */
Index: /trunk/Ohana/src/opihi/pcontrol/rconnect.c
===================================================================
--- /trunk/Ohana/src/opihi/pcontrol/rconnect.c	(revision 4704)
+++ /trunk/Ohana/src/opihi/pcontrol/rconnect.c	(revision 4705)
@@ -10,4 +10,5 @@
 
   int i, stdin_fd[2], stdout_fd[2], stderr_fd[2], status;
+  int result, waitstatus;
   pid_t pid;
   char *p;
@@ -69,7 +70,6 @@
 
   /* send handshake command */
-  sprintf (command, "echo CONNECTED\n");
-  status = write (stdin_fd[1], command, strlen(command));
-  if ((status == -1) && (errno == EPIPE)) goto pipe_error;
+  status = write_fmt (stdin_fd[1], "echo CONNECTED\n");
+  if ((status == -1) && (errno == EPIPE)) goto connect_error;
 
   /* try to get evidence connection is alive - wait upto a few seconds */
@@ -81,7 +81,7 @@
     usleep (20000);
   }
+  if (status == 0) goto connect_error;
+  if (status == -1) goto connect_error;
   if (VerboseMode()) fprintf (stderr, "%d cycles to connect\n", i);
-  if (status == 0) goto pipe_error;
-  if (status == -1) goto io_error;
   FreeIOBuffer (&buffer);
 
@@ -98,7 +98,32 @@
   goto close_pipes;
 
-io_error:
-  if (VerboseMode()) fprintf (stderr, "timeout while connecting\n");
-  goto close_pipes;
+connect_error:
+  if (VerboseMode()) fprintf (stderr, "error while connecting\n");
+
+  /* harvest the child process: kill & wait (< 100 ms) for exit */
+  kill (pid, SIGKILL);
+  result = waitpid (pid, &waitstatus, WNOHANG);
+  for (i = 0; (i < 10) && (result == 0); i++) {
+    usleep (10000);  /* 10 ms is min */
+    result = waitpid (pid, &waitstatus, WNOHANG);
+  }
+
+  if ((result == -1) && (errno != ECHILD)) {
+    fprintf (stderr, "unexpected error from waitpid (%d): programming error\n", errno);
+    exit (1);
+  }
+  if (result == 0) {
+    if (VerboseMode()) fprintf (stderr, "child did not exit??");
+  }
+  if (result > 0) {
+    if (result != pid) {
+      fprintf (stderr, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+      exit (1);
+    }
+    if (WIFSTOPPED(waitstatus)) {
+      fprintf (stderr, "waitpid returns 'stopped': programming error\n");
+      exit (1);
+    }
+  }
 
 close_pipes:
Index: /trunk/Ohana/src/opihi/scripts/psched.pro
===================================================================
--- /trunk/Ohana/src/opihi/scripts/psched.pro	(revision 4705)
+++ /trunk/Ohana/src/opihi/scripts/psched.pro	(revision 4705)
@@ -0,0 +1,40 @@
+
+controller exit true
+controller host add kiawe
+controller host add alala
+# verbose on
+
+task test
+  command partest
+  periods -poll 1.0
+  periods -exec 2.0
+  periods -timeout 10.0
+  nmax 15
+  host anyhost
+
+  # stdout / stderr lines on named queues
+  task.exit 0
+    # echo "task exit 0"
+    queuesize stdout -var Nstdout
+    for i 0 $Nstdout
+      queuepop stdout -var line
+      queuepush results "$line"
+    end
+  end
+
+  task.exit 1
+    # echo "task exit 1"
+    queuesize stdout
+    queuesize stderr
+  end
+
+  task.exit crash
+    echo "crashed job"
+  end
+
+  task.exit timeout
+    output timeout.log
+    echo $stdout
+    output stdout
+  end
+end
Index: /trunk/Ohana/src/opihi/scripts/queuetests
===================================================================
--- /trunk/Ohana/src/opihi/scripts/queuetests	(revision 4705)
+++ /trunk/Ohana/src/opihi/scripts/queuetests	(revision 4705)
@@ -0,0 +1,19 @@
+
+macro test.unique
+  list line -x "cat new.list"
+  for i 0 $line:n
+    push t1 "$line:$i"
+  end
+
+  queuelist
+  queuesize t1
+
+  list line -x "cat copy.list"
+  for i 0 $line:n
+    push t1 "$line:$i" -uniq
+  end
+
+  queuelist
+  queuesize t1
+
+end
