Index: trunk/Ohana/src/opihi/pcontrol/CheckBusyJob.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/CheckBusyJob.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/CheckBusyJob.c	(revision 8296)
@@ -9,4 +9,8 @@
   IOBuffer buffer;
   Host    *host;
+
+  /* we are checking a job which is currently busy.  it has been pulled from the
+     JOB_BUSY stack, and is linked to a host in the HOST_BUSY stack.  
+     XXX need to check on state of HOST on return */
 
   /** must have a valid host : if not? **/
@@ -28,6 +32,5 @@
 
     case PCLIENT_HUNG:
-      PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
-      job[0].state = PCONTROL_JOB_HUNG;
+      PutJobSetState (job, PCONTROL_JOB_BUSY, STACK_BOTTOM, PCONTROL_JOB_HUNG);
       FreeIOBuffer (&buffer);
       return (TRUE);
@@ -78,6 +81,5 @@
 
   /** job has exited : move to DONE stack (host still BUSY) **/
-  PutJob (job, PCONTROL_JOB_DONE, STACK_BOTTOM);
-  job[0].state = outstate;
+  PutJobSetState (job, PCONTROL_JOB_DONE, STACK_BOTTOM, outstate);
   FreeIOBuffer (&buffer);
   return (TRUE);
Index: trunk/Ohana/src/opihi/pcontrol/CheckDoneJob.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/CheckDoneJob.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/CheckDoneJob.c	(revision 8296)
@@ -16,8 +16,8 @@
   }
 
+  /* job's state is either EXIT or CRASH (verify?) */
   host = UnlinkJobAndHost (job);
   PutHost (host, PCONTROL_HOST_DONE, STACK_BOTTOM);
   PutJob (job, job[0].state, STACK_BOTTOM);
-  /* job's state is either EXIT or CRASH (verify?) */
 
   return (TRUE);
Index: trunk/Ohana/src/opihi/pcontrol/CheckIdleHost.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 8296)
@@ -1,4 +1,5 @@
 # include "pcontrol.h"
 
+/* the supplied host is not on a stack: it cannot be taken by the other thread */
 int CheckIdleHost (Host *host) {
 
@@ -7,5 +8,7 @@
   Job *job;
 
+  /* search the JOB_PENDING stack for an appropriate job */
   stack = GetJobStack (PCONTROL_JOB_PENDING);
+  LockStack (stack);
   
   /* look for first NEEDHOST matching this host */
@@ -18,5 +21,12 @@
     }
     if (strcasecmp (job[0].hostname, host[0].hostname)) continue;
-    LinkJobAndHost (job, host);
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
     StartJob (job);
     return (TRUE);
@@ -26,5 +36,4 @@
   for (i = 0; i < stack[0].Nobject; i++) {
     job = (Job *) stack[0].object[i];
-    /*** this currently never runs WANTHOST jobs on any other machines ***/
     if (job[0].mode != PCONTROL_JOB_WANTHOST) continue;
     if (job[0].hostname == NULL) {
@@ -33,5 +42,12 @@
     }
     if (strcasecmp (job[0].hostname, host[0].hostname)) continue;
-    LinkJobAndHost (job, host);
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
     StartJob (job);
     return (TRUE);
@@ -42,9 +58,37 @@
     job = (Job *) stack[0].object[i];
     if (job[0].mode != PCONTROL_JOB_ANYHOST) continue;
-    LinkJobAndHost (job, host);
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
     StartJob (job);
-    return (TRUE);
+   return (TRUE);
   }
-  /* no jobs for host, but back on IDLE stack */
+
+  /* no ANYHOST entry, look for first WANTHOST with old time */
+  /* XXX perhaps I should add this to the conditions for ANYHOST instead of
+     running a separate loop?  ie, WANTHOST && time > X == ANYHOST */
+  for (i = 0; i < stack[0].Nobject; i++) {
+    job = (Job *) stack[0].object[i];
+    if (job[0].mode != PCONTROL_JOB_WANTHOST) continue;
+    // test the job age and skip if too young
+
+    /* we have found an appropriate job; link it to the host and send to StartJob */
+    job[0].host = (struct Host *) host;
+    host[0].job = (struct Job *) job;
+
+    /* take the job off the stack and unlock the stack */
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack);
+    StartJob (job);
+   return (TRUE);
+  }
+  UnlockStack (stack);
+
+  /* no jobs for host, put it 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 8295)
+++ trunk/Ohana/src/opihi/pcontrol/CheckSystem.c	(revision 8296)
@@ -13,4 +13,5 @@
   CheckBusyJobs(0.020);  /* get job status */
   CheckDoneJobs(0.020);  /* harvest job stdout/stderr */
+  CheckKillJobs(0.020);  /* harvest job stdout/stderr */
 
   CheckDoneHosts(0.020); /* reset the host */
@@ -51,12 +52,28 @@
   float dtime;
 
-  stack = GetJobStack (PCONTROL_JOB_BUSY);
-  Nobject = stack[0].Nobject;
-
-  /* always allow at least one test */
-  gettimeofday (&start, (void *) NULL);
-  dtime = 0.0;
-  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
-    job = GetStack (stack, STACK_TOP);
+  /* Loop through objects on the stack, no more than once.  Note that it is not important if the
+     stack size is modified by other threads or is changed by any of the actions performed during
+     this loop: the Nobject value is only used to get a rough number for the number of iterations.
+   */
+
+  hoststack = GetHostStack (PCONTROL_HOST_BUSY);
+  jobstack  = GetJobStack (PCONTROL_JOB_BUSY);
+  Nobject   = jobstack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    /* pull both job and host from their stacks */
+    /* XXX is the subject to the Dangerous Embrace? */
+    LockStack (hoststack);
+    job = PullStackByLocation (jobstack, STACK_TOP);
+    if (job == NULL) {
+      UnlockStack (hoststack);
+      break;
+    }
+    host = RemoveStackByID (hoststack, job[0].host[0].HostID);
+    UnlockStack (hoststack);
+
     CheckBusyJob (job);
     gettimeofday (&stop, (void *) NULL);
@@ -75,4 +92,5 @@
   float dtime;
 
+  /* Loop through objects on the stack, no more than once. see note above */
   stack = GetJobStack (PCONTROL_JOB_DONE);
   Nobject = stack[0].Nobject;
@@ -82,5 +100,6 @@
   dtime = 0.0;
   for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
-    job = GetStack (stack, STACK_TOP);
+    job = PullStackByLocation (stack, STACK_TOP);
+    if (job == NULL) break;
     CheckDoneJob (job);
     gettimeofday (&stop, (void *) NULL);
@@ -91,4 +110,30 @@
 }
 
+int CheckKillJobs (float MaxDelay) {
+
+  struct timeval start, stop;
+  int i, Nobject;
+  Stack *stack;
+  Job   *job;
+  float dtime;
+
+  /* Loop through objects on the stack, no more than once. see note above */
+  stack = GetJobStack (PCONTROL_JOB_KILL);
+  Nobject = stack[0].Nobject;
+
+  /* always allow at least one test */
+  gettimeofday (&start, (void *) NULL);
+  dtime = 0.0;
+  for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
+    job = PullStackByLocation (stack, STACK_TOP);
+    if (job == NULL) break;
+    KillJob (job);
+    gettimeofday (&stop, (void *) NULL);
+    dtime = DTIME (stop, start);
+  }
+  if (0 && (Nobject > 0)) gprint (GP_ERR, "checked %d of %d jobs\n", i, Nobject);
+  return (TRUE);
+}
+
 int CheckDoneHosts (float MaxDelay) {
 
@@ -99,4 +144,5 @@
   float dtime;
 
+  /* Loop through objects on the stack, no more than once. see note above */
   stack = GetHostStack (PCONTROL_HOST_DONE);
   Nobject = stack[0].Nobject;
@@ -106,5 +152,6 @@
   dtime = 0.0;
   for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
-    host = GetStack (stack, STACK_TOP);
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
     CheckDoneHost (host);
     gettimeofday (&stop, (void *) NULL);
@@ -123,4 +170,5 @@
   float dtime;
 
+  /* Loop through objects on the stack, no more than once. see note above */
   stack = GetHostStack (PCONTROL_HOST_DOWN);
   Nobject = stack[0].Nobject;
@@ -130,5 +178,6 @@
   dtime = 0.0;
   for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
-    host = GetStack (stack, STACK_TOP);
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
     dtime = DTIME (host[0].nexttry, start);
     if (dtime > 0) {
@@ -156,4 +205,5 @@
   if (!stack[0].Nobject) return (TRUE);
 
+  /* Loop through objects on the stack, no more than once. see note above */
   stack = GetHostStack (PCONTROL_HOST_IDLE);
   Nobject = stack[0].Nobject;
@@ -163,5 +213,6 @@
   dtime = 0.0;
   for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
-    host = GetStack (stack, STACK_TOP);
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
     CheckIdleHost (host);
     gettimeofday (&stop, (void *) NULL);
@@ -181,4 +232,5 @@
   float dtime;
 
+  /* Loop through objects on the stack, no more than once. see note above */
   stack = GetHostStack (PCONTROL_HOST_IDLE);
   Nobject = stack[0].Nobject;
@@ -188,5 +240,6 @@
   dtime = 0.0;
   for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
-    host = GetStack (stack, STACK_TOP);
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
     CheckHost (host);
     gettimeofday (&stop, (void *) NULL);
@@ -195,4 +248,5 @@
   if (0) gprint (GP_ERR, "checked %d idle hosts\n", i);
 
+  /* Loop through objects on the stack, no more than once. see note above */
   stack = GetHostStack (PCONTROL_HOST_BUSY);
   Nobject = stack[0].Nobject;
@@ -200,5 +254,6 @@
   dtime = 0.0;
   for (i = 0; (i < Nobject) && (dtime < MaxDelay); i++) {
-    host = GetStack (stack, STACK_TOP);
+    host = PullStackByLocation (stack, STACK_TOP);
+    if (host == NULL) break;
     CheckHost (host);
     gettimeofday (&stop, (void *) NULL);
Index: trunk/Ohana/src/opihi/pcontrol/HostOps.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/HostOps.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/HostOps.c	(revision 8296)
@@ -12,5 +12,5 @@
   HostPool_Done = InitStack ();
   HostPool_Down = InitStack ();
-  HostPool_Off = InitStack ();
+  HostPool_Off  = InitStack ();
 }
 
@@ -35,48 +35,14 @@
 }
 
-Host *FindHostStack (IDtype HostID) {
-
-  Host *host;
-
-  host = FindHostPtr (HostID, PCONTROL_HOST_IDLE);
-  if (host != NULL) return (host);
-
-  host = FindHostPtr (HostID, PCONTROL_HOST_DOWN);
-  if (host != NULL) return (host);
-
-  host = FindHostPtr (HostID, PCONTROL_HOST_DONE);
-  if (host != NULL) return (host);
-
-  host = FindHostPtr (HostID, PCONTROL_HOST_BUSY);
-  if (host != NULL) return (host);
-
-  host = FindHostPtr (HostID, PCONTROL_HOST_OFF);
-  if (host != NULL) return (host);
-
-  return (NULL);
-}
-
-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);
-}
-
+Stack *GetHostStackByName (char *name) {
+  if (!strcasecmp (name, "idle")) return (HostPool_Idle);
+  if (!strcasecmp (name, "down")) return (HostPool_Down);
+  if (!strcasecmp (name, "done")) return (HostPool_Done);
+  if (!strcasecmp (name, "busy")) return (HostPool_Busy);
+  if (!strcasecmp (name, "off"))  return (HostPool_Off);
+  return (NULL);
+}
+
+/* add host to position in stack */
 int PutHost (Host *host, int StackID, int where) {
 
@@ -88,90 +54,175 @@
 
   host[0].stack = StackID;
-  stat = PutStack (stack, where, host);
-  /* status (0, NULL); */
+  stat = PushStack (stack, where, host, host[0].HostID, host[0].hostname);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
   return (stat);
 }
   
-Host *GetHost (int StackID, int where) {
-
-  Host *host;
-  Stack *stack;
-
-  stack = GetHostStack (StackID);
-  if (stack == NULL) return (NULL);
-
-  host = GetStack (stack, where);
-  return (host);
-}
-
-int FindHost (IDtype HostID, int StackID) {
-
-  int i;
-  Host *host;
-  Stack *stack;
-
-  stack = GetHostStack (StackID);
-  if (stack == NULL) return (-2);
-
-  for (i = 0; i < stack[0].Nobject; i++) {
-    host = (Host *) stack[0].object[i];
-    if (host[0].HostID == HostID) {
-      return (i);
-    }
-  }
-  return (-1);
-}
-
-Host *FindHostPtr (IDtype HostID, int StackID) {
-
-  int i;
-  Host *host;
-  Stack *stack;
-
-  stack = GetHostStack (StackID);
-  if (stack == NULL) return (NULL);
-
-  for (i = 0; i < stack[0].Nobject; i++) {
-    host = (Host *) stack[0].object[i];
-    if (host[0].HostID == HostID) {
-      return (host);
-    }
-  }
-  return (NULL);
-}
-
-Host *PullHost (IDtype HostID, int StackID) {
-  
-  int N;
-  Host *host;
-
-  N = FindHost (HostID, StackID);
-  if (N < 0) return (NULL);
-
-  host = GetHost (StackID, N);
-  if (host == NULL) {
-    gprint (GP_ERR, "programming error! host missing from stack\n");
-    exit (1);
-  }
-  return (host);
-}
-
-int FindNamedHost (char *name, int StackID) {
-
-  int i;
-  Host *host;
-  Stack *stack;
-
-  stack = GetHostStack (StackID);
-  if (stack == NULL) return (-2);
-
-  for (i = 0; i < stack[0].Nobject; i++) {
-    host = (Host *) stack[0].object[i];
-    if (!strcasecmp (host[0].hostname, name)) {
-      return (i);
-    }
-  }
-  return (-1);
-}
+/* find the host by ID in the defined host stacks */
+Host *PullHostByID (IDtype HostID, int *StackID) {
+
+  Host *host;
+
+  *StackID = PCONTROL_HOST_IDLE;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DOWN;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DONE;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_BUSY;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_OFF;
+  host = PullHostFromStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = -1;
+  return (NULL);
+}
+
+/* find the host by ID in the defined host stacks */
+Host *PullHostByName (char *name, int *StackID) {
+
+  Host *host;
+
+  *StackID = PCONTROL_HOST_IDLE;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DOWN;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DONE;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_BUSY;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_OFF;
+  host = PullHostFromStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = -1;
+  return (NULL);
+}
+
+Host *PullHostFromStackByID (int StackID, IDtype ID) {
+
+  Host *host;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  host = PullStackByID (stack, ID);
+  return (host);
+}
+
+Host *PullHostFromStackByName (int StackID, char *name) {
+
+  Host *host;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  host = PullStackByName (stack, name);
+  return (host);
+}
+
+/* find the host by ID in the defined host stacks (don't pull off stack) */
+/* the Find functions are not thread-safe: DROP */
+# if (0) 
+Host *FindHostByID (IDtype HostID, int *StackID) {
+
+  Host *host;
+
+  *StackID = PCONTROL_HOST_IDLE;
+  host = FindHostInStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DOWN;
+  host = FindHostInStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DONE;
+  host = FindHostInStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_BUSY;
+  host = FindHostInStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_OFF;
+  host = FindHostInStackByID (*StackID, HostID);
+  if (host != NULL) return (host);
+
+  *StackID = -1;
+  return (NULL);
+}
+
+/* find the host by Name in the defined host stacks (don't pull off stack) */
+Host *FindHostByName (char *name, int *StackID) {
+
+  Host *host;
+
+  *StackID = PCONTROL_HOST_IDLE;
+  host = FindHostInStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DOWN;
+  host = FindHostInStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_DONE;
+  host = FindHostInStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_BUSY;
+  host = FindHostInStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = PCONTROL_HOST_OFF;
+  host = FindHostInStackByName (*StackID, name);
+  if (host != NULL) return (host);
+
+  *StackID = -1;
+  return (NULL);
+}
+
+Host *FindHostInStackByID (int StackID, IDtype ID) {
+
+  Host *host;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  host = FindStackByID (stack, ID);
+  return (host);
+}
+
+Host *FindHostInStackByName (int StackID, char *name) {
+
+  Host *host;
+  Stack *stack;
+
+  stack = GetHostStack (StackID);
+  if (stack == NULL) return (NULL);
+
+  host = FindStackByName (stack, name);
+  return (host);
+}
+# endif
 
 IDtype AddHost (char *hostname) {
Index: trunk/Ohana/src/opihi/pcontrol/IDops.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/IDops.c	(revision 8296)
+++ trunk/Ohana/src/opihi/pcontrol/IDops.c	(revision 8296)
@@ -0,0 +1,42 @@
+# include "pcontrol.h"
+
+static IDtype CurrentJobID  = 0;
+static IDtype CurrentHostID = 0;
+
+/* for now, no persistence : we could use the date/time to seed the upper byte(s) if needed */
+void InitIDs () {
+  CurrentJobID = 0;
+  CurrentHostID = 0;
+}
+
+IDtype NextJobID () {
+
+  IDtype ID;
+
+  ID = CurrentJobID;
+  CurrentJobID ++;
+  return (ID);
+}
+
+/* only used by the User thread */
+IDtype NextHostID () {
+
+  IDtype ID;
+
+  ID = CurrentHostID;
+  CurrentHostID ++;
+  return (ID);
+}
+
+void PrintID (gpDest dest, IDtype ID) {
+
+  unsigned short int word0, word1, word2, word3;
+
+  word0 = 0xffff & ID;
+  word1 = 0xffff & (ID >> 16);
+  word2 = 0xffff & (ID >> 32);
+  word3 = 0xffff & (ID >> 48);
+
+  gprint (dest, "%x.%x.%x.%x", word3, word2, word1, word0);
+}
+
Index: trunk/Ohana/src/opihi/pcontrol/JobOps.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/JobOps.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/JobOps.c	(revision 8296)
@@ -4,4 +4,5 @@
 Stack *JobPool_Busy;
 Stack *JobPool_Done;
+Stack *JobPool_Kill;
 Stack *JobPool_Exit;
 Stack *JobPool_Crash;
@@ -9,8 +10,9 @@
 void InitJobStacks () {
   JobPool_Pending = InitStack ();
-  JobPool_Busy = InitStack ();
-  JobPool_Done = InitStack ();
-  JobPool_Exit = InitStack ();
-  JobPool_Crash = InitStack ();
+  JobPool_Busy    = InitStack ();
+  JobPool_Done    = InitStack ();
+  JobPool_Kill    = InitStack ();
+  JobPool_Exit    = InitStack ();
+  JobPool_Crash   = InitStack ();
 }
 
@@ -23,4 +25,6 @@
     case PCONTROL_JOB_DONE:
       return (JobPool_Done);
+    case PCONTROL_JOB_KILL:
+      return (JobPool_Kill);
     case PCONTROL_JOB_EXIT:
       return (JobPool_Exit);
@@ -37,40 +41,13 @@
 Stack *GetJobStackByName (char *name) {
 
-  if (!strcasecmp (name, "pending")) 
-    return (JobPool_Pending);
-  if (!strcasecmp (name, "busy")) 
-    return (JobPool_Busy);
-  if (!strcasecmp (name, "done")) 
-    return (JobPool_Done);
-  if (!strcasecmp (name, "exit")) 
-    return (JobPool_Exit);
-  if (!strcasecmp (name, "crash")) 
-    return (JobPool_Crash);
+  if (!strcasecmp (name, "pending")) return (JobPool_Pending);
+  if (!strcasecmp (name, "busy"))    return (JobPool_Busy);
+  if (!strcasecmp (name, "done"))    return (JobPool_Done);
+  if (!strcasecmp (name, "exit"))    return (JobPool_Exit);
+  if (!strcasecmp (name, "crash"))   return (JobPool_Crash);
   return (NULL);
 }
 
-Job *FindJobStack (IDtype JobID) {
-
-  Job *job;
-
-  job = FindJobPtr (JobID, PCONTROL_JOB_PENDING);
-  if (job != NULL) return (job);
-
-  job = FindJobPtr (JobID, PCONTROL_JOB_BUSY);
-  if (job != NULL) return (job);
-
-  job = FindJobPtr (JobID, PCONTROL_JOB_EXIT);
-  if (job != NULL) return (job);
-
-  job = FindJobPtr (JobID, PCONTROL_JOB_CRASH);
-  if (job != NULL) return (job);
-
-  job = FindJobPtr (JobID, PCONTROL_JOB_DONE);
-  if (job != NULL) return (job);
-
-  return (NULL);
-}
-
-/* add job to position in stack */
+/* add job to position in stack, use StackID as default state */
 int PutJob (Job *job, int StackID, int where) {
 
@@ -81,14 +58,62 @@
   if (stack == NULL) return (FALSE);
 
-  /* be default, these are both the same - to override, set state after PutJob */
+  /* by default, these are both the same - to override, use PutJobSetState */
   job[0].state = StackID;
   job[0].stack = StackID;
-  stat = PutStack (stack, where, job);
-  /* status (0, NULL); */
+  stat = PushStack (stack, where, job, job[0].JobID, job[0].argv[0]);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
   return (stat);
 }
   
+/* add job to position in stack.  set state to 'state' */
+int PutJobSetState (Job *job, int StackID, int where, int state) {
+
+  int stat;
+  Stack *stack;
+
+  stack = GetJobStack (StackID);
+  if (stack == NULL) return (FALSE);
+
+  /* alternate state specified by user */
+  job[0].state = state;
+  job[0].stack = StackID;
+  stat = PushStack (stack, where, job, job[0].JobID, job[0].argv[0]);
+  // XXX need to handle the error conditions, or we drop the host & leak memory
+  return (stat);
+}
+  
+Job *PullJobByID (IDtype JobID, int *StackID) {
+
+  Job *job;
+
+  *StackID = PCONTROL_JOB_PENDING;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_BUSY;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_DONE;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_KILL;
+  job = PullJobFromStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  return (NULL);
+}
+
 /* remove job from position in stack */
-Job *GetJob (int StackID, int where) {
+Job *PullJobFromStackByID (int StackID, int ID) {
 
   Job *job;
@@ -98,31 +123,40 @@
   if (stack == NULL) return (NULL);
 
-  job = GetStack (stack, where);
+  job = PullStackByID (stack, ID);
   return (job);
 }
   
-/* return stack position of job */
-int FindJob (IDtype JobID, int StackID) {
-
-  int i;
-  Job *job;
-  Stack *stack;
-
-  stack = GetJobStack (StackID);
-  if (stack == NULL) return (-2);
-
-  for (i = 0; i < stack[0].Nobject; i++) {
-    job = (Job *) stack[0].object[i];
-    if (job[0].JobID == JobID) {
-      return (i);
-    }
-  }
-  return (-1);
-}
-
-/* return pointer to job */
-Job *FindJobPtr (IDtype JobID, int StackID) {
-
-  int i;
+/* the Find functions are not thread-safe: DROP */
+# if (0)
+Job *FindJobByID (IDtype JobID, int *StackID) {
+
+  Job *job;
+
+  *StackID = PCONTROL_JOB_PENDING;
+  job = FindJobInStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_BUSY;
+  job = FindJobInStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_EXIT;
+  job = FindJobInStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_CRASH;
+  job = FindJobInStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  *StackID = PCONTROL_JOB_DONE;
+  job = FindJobInStackByID (*StackID, JobID);
+  if (job != NULL) return (job);
+
+  return (NULL);
+}
+
+/* remove job from position in stack */
+Job *FindJobInStackByID (int StackID, int ID) {
+
   Job *job;
   Stack *stack;
@@ -131,32 +165,12 @@
   if (stack == NULL) return (NULL);
 
-  for (i = 0; i < stack[0].Nobject; i++) {
-    job = (Job *) stack[0].object[i];
-    if (job[0].JobID == JobID) {
-      return (job);
-    }
-  }
-  return (NULL);
-}
-
-/* remove job from stack, return pointer */
-Job *PullJob (IDtype JobID, int StackID) {
-  
-  int N;
-  Job *job;
-
-  N = FindJob (JobID, StackID);
-  if (N < 0) return (NULL);
-
-  job = GetJob (StackID, N);
-  if (job == NULL) {
-    gprint (GP_ERR, "programming error! job missing from stack\n");
-    exit (1);
-  }
+  job = FindStackByID (stack, ID);
   return (job);
 }
+# endif
 
 IDtype AddJob (char *hostname, JobMode mode, int timeout, int argc, char **argv) {
 
+  int JobID;
   Job *job;
 
@@ -175,7 +189,8 @@
   InitIOBuffer (&job[0].stderr, 0x1000);
 
+  JobID = job[0].JobID;
   PutJob (job, PCONTROL_JOB_PENDING, STACK_BOTTOM);
   gprint (GP_ERR, "added new job\n");
-  return (job[0].JobID);
+  return (JobID);
 }
 
@@ -212,5 +227,6 @@
   
   /* remove host from correct stack */
-  if (PullHost (host[0].HostID, host[0].stack) == NULL) {
+  XXXX does this step asuume the host is in this stack??
+  if (PullHostFromStackByID (host[0].stack, host[0].HostID) == NULL) {
     gprint (GP_ERR, "programming error: host is not found in current stack\n");
     exit (2);
@@ -218,14 +234,2 @@
   return (host);
 }
-
-void LinkJobAndHost (Job *job, Host *host) {
-
-  job[0].host = (struct Host *) host;
-  host[0].job = (struct Job *) job;
-
-  /* remove job from correct stack */
-  if (PullJob (job[0].JobID, job[0].stack) == NULL) {
-    gprint (GP_ERR, "programming error: job is not found in current stack\n");
-    exit (2);
-  }
-}
Index: trunk/Ohana/src/opihi/pcontrol/KillJob.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/KillJob.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/KillJob.c	(revision 8296)
@@ -26,6 +26,5 @@
 
     case PCLIENT_HUNG:
-      PutJob (job, PCONTROL_JOB_BUSY, STACK_BOTTOM);
-      job[0].state = PCONTROL_JOB_HUNG;
+      PutJobSetState (job, PCONTROL_JOB_BUSY, STACK_BOTTOM, PCONTROL_JOB_HUNG);
       FreeIOBuffer (&buffer);
       return (TRUE);
@@ -61,7 +60,5 @@
       return (FALSE);
     case 1:
-      PutJob (job, PCONTROL_JOB_DONE, STACK_BOTTOM);
-      job[0].state = PCONTROL_JOB_CRASH;
-      /* this overrides the default assigned by PutJob */
+      PutJobSetState (job, PCONTROL_JOB_DONE, STACK_BOTTOM, PCONTROL_JOB_CRASH);
       return (TRUE);
     case 2:
@@ -73,2 +70,4 @@
   exit (1);
 }
+
+/** XXX need to do something appropriate with host? ***/
Index: trunk/Ohana/src/opihi/pcontrol/Makefile
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/Makefile	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/Makefile	(revision 8296)
@@ -38,7 +38,7 @@
 $(SDIR)/GetJobOutput.$(ARCH).o \
 $(SDIR)/HostOps.$(ARCH).o \
-$(SDIR)/JobID.$(ARCH).o \
+$(SDIR)/IDops.$(ARCH).o \
 $(SDIR)/JobOps.$(ARCH).o \
-$(SDIR)/QueueOps.$(ARCH).o \
+$(SDIR)/StackOps.$(ARCH).o \
 $(SDIR)/ResetJob.$(ARCH).o \
 $(SDIR)/StartHost.$(ARCH).o \
@@ -53,4 +53,5 @@
 $(SDIR)/job.$(ARCH).o \
 $(SDIR)/jobstack.$(ARCH).o \
+$(SDIR)/hoststack.$(ARCH).o \
 $(SDIR)/kill.$(ARCH).o \
 $(SDIR)/pulse.$(ARCH).o \
Index: trunk/Ohana/src/opihi/pcontrol/StackOps.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/StackOps.c	(revision 8296)
+++ trunk/Ohana/src/opihi/pcontrol/StackOps.c	(revision 8296)
@@ -0,0 +1,214 @@
+# include "pcontrol.h"
+
+/* these stacks are not super efficient, and should probably be replaced with linked lists, 
+   but I find these easier to get my brain around.
+*/
+
+/* Stacks and thread locks: interacting with the Stacks needs to be thread-safe so that the user may
+ * perform operations which interact with the stacks at the same time that the background loops
+ * check the current status of the jobs and hosts in the different stacks.  The simplest way in
+ * which the stacks are made thread safe is to lock them with a mutex before every interaction
+ */
+
+/* allocate stack, setup with default values, allocate data */
+Stack *InitStack () {
+
+  Stack *stack;
+
+  ALLOCATE (stack, Stack, 1);
+
+  stack[0].Nobject = 0;
+  stack[0].NOBJECT = 50;
+
+  ALLOCATE (stack[0].object, void *, stack[0].NOBJECT);
+  ALLOCATE (stack[0].name,   char *, stack[0].NOBJECT);
+  ALLOCATE (stack[0].id,     int,    stack[0].NOBJECT);
+
+  // we need to use a mutex of type 
+  // stack[0].mutex = PTHREAD_MUTEX_INITIALIZER;
+
+  return (stack);
+}
+
+/* STACK_TOP == 0, STACK_BOTTOM == -1 */
+/* this code correctly handles the negative 'where' and Nobject == 0 */
+
+/* push object on stack at given location */
+int PushStack (Stack *stack, int where, void *object, int id, char *name) {
+
+  int i;
+
+  LockStack (stack);
+
+  if (where < 0) where += stack[0].Nobject + 1;
+  if (where < 0) {
+    UnlockStack (stack);
+    return (FALSE);
+  }
+  if (where > stack[0].Nobject) {
+    UnlockStack (stack);
+    return (FALSE);
+  }
+
+  /* extend stack as needed */
+  if (stack[0].Nobject >= stack[0].NOBJECT) {
+    stack[0].NOBJECT += 100;
+    REALLOCATE (stack[0].object, void *, stack[0].NOBJECT);
+  }
+
+  for (i = stack[0].Nobject; i > where; i--) {
+    stack[0].object[i] = stack[0].object[i-1];
+  }
+  stack[0].object[where] = object;
+  stack[0].name[where]   = name;
+  stack[0].id[where]     = id;
+  stack[0].Nobject ++;
+
+  UnlockStack (stack);
+  return (TRUE);
+}
+
+/* get object from specified point in stack (negative == distance from end) */
+void *PullStackByLocation (Stack *stack, int where) {
+
+  int i;
+  void *object;
+  
+  LockStack (stack);
+
+  if (where < 0) where += stack[0].Nobject;
+  if (where < 0) { 
+    UnlockStack (stack); 
+    return (NULL); 
+  }
+  if (where >= stack[0].Nobject) {
+    UnlockStack (stack); 
+    return (NULL);
+  }
+
+  object = stack[0].object[where];
+  RemoveStackEntry (stack, where);
+  UnlockStack (stack); 
+  return (object);
+}
+
+/* get object from stack which matches name */
+void *PullStackByName (Stack *stack, char *name) {
+
+  int i, j;
+  void *object;
+
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (strcasecmp (stack[0].name[i], name)) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* get object from point in stack (negative == distance from end) */
+void *PullStackByID (Stack *stack, int id) {
+
+  int i, j;
+  void *object;
+  
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* should only be called if you know where is a valid entry */
+int RemoveStackEntry (Stack *stack, int where) {
+
+  if (where < 0) abort();
+  if (where >= stack[0].Nobject) abort();
+  if (stack[0].Nobject < 1) abort();
+
+  /* shift the remaining entries by one */
+  stack[0].Nobject --;
+  for (i = where; i < stack[0].Nobject; i++) {
+    stack[0].object[i] = stack[0].object[i+1];
+    stack[0].name[i]   = stack[0].name[i+1];
+    stack[0].id[i]     = stack[0].id[i+1];
+  }
+  return (TRUE);
+}
+
+/* should only be called if manually lock the stack */
+int RemoveStackByID (Stack *stack, int id) {
+
+  int i, j;
+  void *object;
+  
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    return (object);
+  }
+  return (NULL);
+}
+
+/* the Find operations are not thread-safe */
+# if (0)
+/* get object from stack which matches name */
+void *FindStackByName (Stack *stack, char *name) {
+
+  int i, j;
+  void *object;
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (strcasecmp (stack[0].name[i], name)) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    return (object);
+  }
+  return (NULL);
+}
+
+/* find object in stack, don't remove */
+void *FindStackByID (Stack *stack, int id) {
+
+  int i, j;
+  void *object;
+  
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    return (object);
+  }
+  return (NULL);
+}
+# endif
+
+void LockStack (Stack *stack) {
+  return;
+}
+
+void UnlockStack (Stack *stack) {
+  return;
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: trunk/Ohana/src/opihi/pcontrol/StartHost.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/StartHost.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/StartHost.c	(revision 8296)
@@ -1,4 +1,4 @@
 # include "pcontrol.h"
-# define RETRY_BASE 1.0
+# define RETRY_BASE 10.0
 
 int StartHost (Host *host) {
Index: trunk/Ohana/src/opihi/pcontrol/StopHosts.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/StopHosts.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/StopHosts.c	(revision 8296)
@@ -1,3 +1,19 @@
 # include "pcontrol.h"
+
+void DownHost (Host *host) {
+  CLOSE (host[0].stdin);
+  CLOSE (host[0].stdout);
+  CLOSE (host[0].stderr);
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
+}
+
+void OffHost (Host *host) {
+  CLOSE (host[0].stdin);
+  CLOSE (host[0].stdout);
+  CLOSE (host[0].stderr);
+  host[0].job = NULL;
+  PutHost (host, PCONTROL_HOST_OFF, STACK_BOTTOM);
+}
 
 int DownHosts () {
@@ -8,8 +24,5 @@
 
   stack = GetHostStack (PCONTROL_HOST_IDLE);
-  Nobject = stack[0].Nobject;
-
-  for (i = 0; i < Nobject; i++) {
-    host = GetStack (stack, STACK_TOP);
+  while ((host = PullStackByLocation (stack, STACK_BOTTOM)) != NULL) {
     StopHost (host);
     DownHost (host);
@@ -17,8 +30,5 @@
 
   stack = GetHostStack (PCONTROL_HOST_BUSY);
-  Nobject = stack[0].Nobject;
-
-  for (i = 0; i < Nobject; i++) {
-    host = GetStack (stack, STACK_TOP);
+  while ((host = PullStackByLocation (stack, STACK_BOTTOM)) != NULL) {
     StopHost (host);
     DownHost (host);
@@ -55,20 +65,5 @@
 }
 
-void DownHost (Host *host) {
-  CLOSE (host[0].stdin);
-  CLOSE (host[0].stdout);
-  CLOSE (host[0].stderr);
-  host[0].job = NULL;
-  PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
-}
-
-void OffHost (Host *host) {
-  CLOSE (host[0].stdin);
-  CLOSE (host[0].stdout);
-  CLOSE (host[0].stderr);
-  host[0].job = NULL;
-  PutHost (host, PCONTROL_HOST_OFF, STACK_BOTTOM);
-}
-
+/* the host is thought to be down; check for child exit status */
 int HarvestHost (int pid) {
   
Index: trunk/Ohana/src/opihi/pcontrol/check.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/check.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/check.c	(revision 8296)
@@ -1,6 +1,6 @@
 # include "pcontrol.h"
 
-char jobstate[6][32] = {"PENDING", "BUSY", "EXIT", "CRASH", "HUNG", "DONE"};
-char hoststate[5][32] = {"IDLE", "BUSY", "DOWN", "DONE", "OFF"};
+char jobstate[7][32]  = {"PENDING", "BUSY", "HUNG", "DONE", "KILL", "EXIT", "CRASH"};
+char hoststate[5][32] = {"IDLE",    "BUSY", "DOWN", "DONE", "OFF"};
 
 int check (int argc, char **argv) {
@@ -8,5 +8,5 @@
   Job *job;
   Host *host;
-  int JobID, HostID;
+  int JobID, HostID, StackID;
 
   if (argc != 3) {
@@ -18,13 +18,14 @@
   if (!strcasecmp (argv[1], "JOB")) {
     JobID = atoi (argv[2]);
-    job = FindJobStack (JobID);
+    job = PullJobByID (JobID, &StackID);
     if (job == NULL) {
       gprint (GP_LOG, "job not found\n");
       return (FALSE);
     }
-    gprint (GP_LOG, "STATUS %s\n", jobstate[job[0].stack]);
+    gprint (GP_LOG, "STATUS %s\n", jobstate[StackID]);
     gprint (GP_LOG, "EXITST %d\n", job[0].exit_status);
     gprint (GP_LOG, "STDOUT %d\n", job[0].stdout_size);
     gprint (GP_LOG, "STDERR %d\n", job[0].stderr_size);
+    PutJob (job, StackID, STACK_BOTTOM);
     return (TRUE);
   }
@@ -32,10 +33,11 @@
   if (!strcasecmp (argv[1], "HOST")) {
     HostID = atoi (argv[2]);
-    host = FindHostStack (HostID);
+    host = PullHostByID (HostID, &StackID);
     if (host == NULL) {
       gprint (GP_LOG, "host not found\n");
       return (FALSE);
     }
-    gprint (GP_LOG, "host %s\n", hoststate[host[0].stack]);
+    gprint (GP_LOG, "host %s\n", hoststate[StackID]);
+    PutHost (host, StackID, STACK_BOTTOM);
     return (TRUE);
   }
@@ -44,2 +46,4 @@
   return (FALSE);
 }
+
+XXX how do I handle objects which are in flight??
Index: trunk/Ohana/src/opihi/pcontrol/delete.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/delete.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/delete.c	(revision 8296)
@@ -13,11 +13,11 @@
   /* use a string interp to convert JobIDs to ints ? */
 
-  job = PullJob (JobID, PCONTROL_JOB_PENDING);
+  job = PullJobFromStackByID (PCONTROL_JOB_PENDING, JobID);
   if (job != NULL) goto found;
 
-  job = PullJob (JobID, PCONTROL_JOB_CRASH);
+  job = PullJobFromStackByID (PCONTROL_JOB_CRASH, JobID);
   if (job != NULL) goto found;
 
-  job = PullJob (JobID, PCONTROL_JOB_EXIT);
+  job = PullJobFromStackByID (PCONTROL_JOB_EXIT, JobID);
   if (job != NULL) goto found;
 
Index: trunk/Ohana/src/opihi/pcontrol/host.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/host.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/host.c	(revision 8296)
@@ -4,4 +4,5 @@
 
   int N, Ns;
+  int StackID;
   IDtype HostID;
   Host *host;
@@ -15,68 +16,61 @@
   }
   if (!strcasecmp (argv[1], "ON")) {
-    N = FindNamedHost (argv[2], PCONTROL_HOST_OFF);
-    if (N < 0) {
+    host = PullHostFromStackByName (PCONTROL_HOST_OFF, argv[2]);
+    if (!host) {
       gprint (GP_LOG, "host %s is not OFF\n", argv[2]);
       return (FALSE);
     }
-    host = GetHost (PCONTROL_HOST_OFF, N);
     DownHost (host);
     return (TRUE);
   }
   if (!strcasecmp (argv[1], "RETRY")) {
-    N = FindNamedHost (argv[2], PCONTROL_HOST_DOWN);
-    if (N < 0) {
+    host = PullHostFromStackByName (PCONTROL_HOST_DOWN, argv[2]);
+    if (!host) {
       gprint (GP_LOG, "host %s is not DOWN\n", argv[2]);
       return (FALSE);
     }
-    host = GetHost (PCONTROL_HOST_DOWN, N);
+    /* reset time, place back on DOWN stack */
     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);
+    PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
     return (TRUE);
   }
   if (!strcasecmp (argv[1], "CHECK")) {
-    Ns = FindNamedHostStack (argv[2]);
-    switch (Ns) {
+    host = PullHostByName (argv[2], &StackID);
+    switch (StackID) {
       case PCONTROL_HOST_IDLE:
+	gprint (GP_LOG, "host %s is IDLE\n", argv[2]);
       case PCONTROL_HOST_BUSY:
+	gprint (GP_LOG, "host %s is BUSY\n", argv[2]);
       case PCONTROL_HOST_DONE:
-	N = FindNamedHost (argv[2], Ns);
-	host = GetHost (Ns, N);
-	if (CheckHost (host)) {
-	  if (VerboseMode()) gprint (GP_ERR, "host %s is on\n", host[0].hostname);
-	} 
-	return (TRUE);
+	gprint (GP_LOG, "host %s is DONE\n", argv[2]);
       case PCONTROL_HOST_DOWN:
 	gprint (GP_LOG, "host %s is DOWN\n", argv[2]);
-	return (TRUE);
       case PCONTROL_HOST_OFF:
 	gprint (GP_LOG, "host %s is OFF\n", argv[2]);
-	return (TRUE);
       default:
 	gprint (GP_LOG, "host %s not found\n", argv[2]);
 	return (FALSE);
     }
+    PutHost (host, StackID, STACK_BOTTOM);
     return (FALSE);
   }
   if (!strcasecmp (argv[1], "OFF")) {
-    N = FindNamedHost (argv[2], PCONTROL_HOST_IDLE);
-    if (N >= 0) {
-      host = GetHost (PCONTROL_HOST_IDLE, N);
+    host = PullHostFromStackByName (PCONTROL_HOST_IDLE, argv[2]);
+    if (host) {
       StopHost (host);
       OffHost (host);
       return (TRUE);
     }
-    N = FindNamedHost (argv[2], PCONTROL_HOST_DOWN);
-    if (N >= 0) {
-      host = GetHost (PCONTROL_HOST_DOWN, N);
+    host = PullHostFromStackByName (PCONTROL_HOST_DOWN, argv[2]);
+    if (host) {
       OffHost (host);
       return (TRUE);
     }
-    N = FindNamedHost (argv[2], PCONTROL_HOST_BUSY);
-    if (N >= 0) {
-      host = GetHost (PCONTROL_HOST_BUSY, N);
+    /* XXX the 'markoff' flag is not being used */
+    host = PullHostFromStackByName (PCONTROL_HOST_BUSY, argv[2]);
+    if (host) {
       host[0].markoff  = TRUE;
       PutHost (host, PCONTROL_HOST_BUSY, STACK_BOTTOM);
@@ -87,10 +81,9 @@
   }
   if (!strcasecmp (argv[1], "DELETE")) {
-    N = FindNamedHost (argv[2], PCONTROL_HOST_OFF);
-    if (N < 0) {
+    host = PullHostFromStackByName (PCONTROL_HOST_OFF, argv[2]);
+    if (!host) {
       gprint (GP_LOG, "host %s is not OFF\n", argv[2]);
       return (FALSE);
     }
-    host = GetHost (PCONTROL_HOST_OFF, N);
     DelHost (host);
     return (TRUE);
Index: trunk/Ohana/src/opihi/pcontrol/hoststack.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/hoststack.c	(revision 8296)
+++ trunk/Ohana/src/opihi/pcontrol/hoststack.c	(revision 8296)
@@ -0,0 +1,34 @@
+# include "pcontrol.h"
+
+int hoststack (int argc, char **argv) {
+
+  int i;
+  Stack *stack;
+  Host *host;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: hoststack (hoststack)\n");
+    gprint (GP_ERR, "       (hoststack) : idle, busy, done, down, off\n");
+    return (FALSE);
+  }
+
+  /* select hoststack */
+  stack = GetHostStackByName (argv[1]);
+  if (stack == NULL) {
+    gprint (GP_ERR, "hoststack not found\n");
+    return (FALSE);
+  }
+
+  /* print list */
+  LockStack (stack);
+  gprint (GP_LOG, "Nhosts: %d\n", stack[0].Nobject);
+  for (i = 0; i < stack[0].Nobject; i++) {
+    host = stack[0].object[i];
+    gprint (GP_LOG, "%lld %s\n", host[0].HostID, host[0].hostname);
+  }
+  UnlockStack (stack);
+
+  return (TRUE);
+}
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: trunk/Ohana/src/opihi/pcontrol/init.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/init.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/init.c	(revision 8296)
@@ -4,4 +4,5 @@
 int delete      PROTO((int, char **));
 int host        PROTO((int, char **));
+int hoststack   PROTO((int, char **));
 int job	        PROTO((int, char **));
 int jobstack    PROTO((int, char **));
@@ -17,18 +18,19 @@
 
 static Command cmds[] = {  
-  {"check",     check,     "get job or host status"},
-  {"delete",    delete,    "delete job"},
   {"host",      host,      "add / delete / modify host"},
-  {"job",       job,       "add job"},
-  {"jobstack",  jobstack,  "list jobs for a single stack"},
-  {"kill",      kill_pc,   "kill job"},
-  {"pulse",     pulse,     "set system pulse"},
+  {"hoststack", hoststack, "list hosts for a single stack"},
   {"status",    status,    "get system status"},
   {"stop",      stop,      "stop controller processing"},
   {"run",       run,       "start controller processing"},
+  {"verbose",   verbose,   "set the verbose mode for job"},
+  {"version",   version,   "show version information"},
+  {"pulse",     pulse,     "set system pulse"},
+  {"job",       job,       "add job"},
+  {"jobstack",  jobstack,  "list jobs for a single stack"},
+  {"check",     check,     "get job or host status"},
+  {"delete",    delete,    "delete job"},
+  {"kill",      kill_pc,   "kill job"},
   {"stderr",    stderr_pc, "get stderr buffer for job"},
   {"stdout",    stdout_pc, "get stdout buffer for job"},
-  {"verbose",   verbose,   "set the verbose mode for job"},
-  {"version",     version,      "show version information"},
 }; 
 
@@ -41,7 +43,6 @@
   }
 
-  /* temporary : put this elsewhere? */
+  /* XXX temporary : put this elsewhere? */
   InitJobStacks ();
   InitHostStacks ();
-
 }
Index: trunk/Ohana/src/opihi/pcontrol/job.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/job.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/job.c	(revision 8296)
@@ -6,4 +6,9 @@
   int i, N, Mode, targc, Timeout;
   IDtype JobID;
+
+  if (get_argument (argc, argv, "-host") && get_argument (argc, argv, "+host")) {
+      gprint (GP_ERR, "ERROR: -host and +host are incompatible\n");
+      return (FALSE);
+  }    
 
   Host = NULL;
@@ -16,9 +21,4 @@
   }
   if ((N = get_argument (argc, argv, "+host"))) {
-    if (Mode == PCONTROL_JOB_WANTHOST) {
-      gprint (GP_ERR, "ERROR: -host and +host are incompatible\n");
-      FREE (Host);
-      return (FALSE);
-    }
     remove_argument (N, &argc, argv);
     Host = strcreate (argv[N]);
Index: trunk/Ohana/src/opihi/pcontrol/jobstack.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/jobstack.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/jobstack.c	(revision 8296)
@@ -21,4 +21,5 @@
 
   /* print list */
+  LockStack (stack);
   gprint (GP_LOG, "Njobs: %d\n", stack[0].Nobject);
   for (i = 0; i < stack[0].Nobject; i++) {
@@ -28,4 +29,8 @@
     gprint (GP_LOG, "%s  %s\n", job[0].argv[0], job[0].hostname);
   }
+  UnlockStack (stack);
+
   return (TRUE);
 }
+
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: trunk/Ohana/src/opihi/pcontrol/kill.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/kill.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/kill.c	(revision 8296)
@@ -13,5 +13,5 @@
 
   /* XXX this function should only fail if a process is hung */
-  job = PullJob (JobID, PCONTROL_JOB_BUSY);
+  job = PullJobFromStackByID (PCONTROL_JOB_BUSY, JobID);
   if (job == NULL) {
     gprint (GP_ERR, "job %s not BUSY\n", argv[1]);
@@ -20,7 +20,5 @@
   }
 
-  /* XXX - check on success / failure of kill */
-  KillJob (job);
-
+  PutJob (job, PCONTROL_JOB_KILL, STACK_BOTTOM);
   return (TRUE);
 }
Index: trunk/Ohana/src/opihi/pcontrol/run.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/run.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/run.c	(revision 8296)
@@ -12,2 +12,14 @@
   return (TRUE);
 }
+
+int run_threaded (int argc, char **argv) {
+
+  if (argc != 1) {
+    gprint (GP_ERR, "USAGE: run\n");
+    return (FALSE);
+  }
+
+  // some action
+
+  return (TRUE);
+}
Index: trunk/Ohana/src/opihi/pcontrol/status.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/status.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/status.c	(revision 8296)
@@ -1,6 +1,6 @@
 # include "pcontrol.h"
 
-char jobname[6][32] = {"PENDING", "BUSY", "EXIT", "CRASH", "HUNG", "DONE"};
-char hostname[5][32] = {"IDLE", "BUSY", "DOWN", "DONE", "OFF"};
+char jobname[7][32]  = {"PENDING", "BUSY", "HUNG", "DONE", "KILL", "EXIT", "CRASH"};
+char hostname[5][32] = {"IDLE",    "BUSY", "DOWN", "DONE", "OFF"};
 
 int status (int argc, char **argv) {
@@ -9,4 +9,5 @@
   PrintJobStack (PCONTROL_JOB_BUSY);
   PrintJobStack (PCONTROL_JOB_DONE);
+  PrintJobStack (PCONTROL_JOB_KILL);
   PrintJobStack (PCONTROL_JOB_EXIT);
   PrintJobStack (PCONTROL_JOB_CRASH);
@@ -27,4 +28,6 @@
 
   stack = GetJobStack (Nstack);
+
+  LockStack (stack);
   Nobject = stack[0].Nobject;
   gprint (GP_LOG, "job stack %s:  %d objects\n", jobname[Nstack], Nobject);
@@ -39,4 +42,6 @@
     gprint (GP_LOG, "\n");
   }
+  UnlockStack (stack);
+
   return (TRUE);
 }
@@ -49,4 +54,6 @@
 
   stack = GetHostStack (Nstack);
+
+  LockStack (stack);
   Nobject = stack[0].Nobject;
   gprint (GP_LOG, "host stack %s:  %d objects\n", hostname[Nstack], Nobject);
@@ -58,5 +65,8 @@
     gprint (GP_LOG, "\n");
   }
+  UnlockStack (stack);
+
   return (TRUE);
 }
 
+// Safe with PTHREAD_MUTEX_INITIALIZER lock
Index: trunk/Ohana/src/opihi/pcontrol/stdout.c
===================================================================
--- trunk/Ohana/src/opihi/pcontrol/stdout.c	(revision 8295)
+++ trunk/Ohana/src/opihi/pcontrol/stdout.c	(revision 8296)
@@ -3,5 +3,5 @@
 int stdout_pc (int argc, char **argv) {
 
-  int JobID;
+  int JobID, StackID;
   Job *job;
   IOBuffer *buffer;
@@ -15,24 +15,28 @@
   /* find Job of interest (must be EXIT or CRASH) */
   JobID = atoi (argv[1]);
-  job = FindJobPtr (JobID, PCONTROL_JOB_EXIT);
-  if (job == NULL) {
-    job = FindJobPtr (JobID, PCONTROL_JOB_CRASH);
-    if (job == NULL) goto missing_stdout;
-  }    
 
+  StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stdout;
+
+  StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stdout;
+
+  gprint (GP_ERR, "job not found in EXIT or CRASH\n");
+  gprint (GP_LOG, "STATUS %d\n", -2);
+  return (FALSE);
+
+found_stdout:
   buffer = &job[0].stdout;
   fwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, stdout);
   gprint (GP_LOG, "STATUS %d\n", 0);
+  PutJob (job, StackID, STACK_BOTTOM);
   return (TRUE);
-
-missing_stdout:
-  gprint (GP_ERR, "job not found in EXIT or CRASH\n");
-  gprint (GP_LOG, "STATUS %d\n", -2);
-  return (FALSE);
 }
 
 int stderr_pc (int argc, char **argv) {
 
-  int JobID;
+  int JobID, StackID;
   Job *job;
   IOBuffer *buffer;
@@ -46,18 +50,22 @@
   /* find Job of interest (must be EXIT or CRASH) */
   JobID = atoi (argv[1]);
-  job = FindJobPtr (JobID, PCONTROL_JOB_EXIT);
-  if (job == NULL) {
-    job = FindJobPtr (JobID, PCONTROL_JOB_CRASH);
-    if (job == NULL) goto missing_stderr;
-  }    
 
+  StackID = PCONTROL_JOB_EXIT;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stderr;
+
+  StackID = PCONTROL_JOB_CRASH;
+  job = PullJobFromStackByID (StackID, JobID);
+  if (job != NULL) goto found_stderr;
+
+  gprint (GP_ERR, "job not found in EXIT or CRASH\n");
+  gprint (GP_LOG, "STATUS %d\n", -2);
+  return (FALSE);
+
+found_stderr:
   buffer = &job[0].stderr;
   fwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, stdout);
   gprint (GP_LOG, "STATUS %d\n", 0);
+  PutJob (job, StackID, STACK_BOTTOM);
   return (TRUE);
-
-missing_stderr:
-  gprint (GP_ERR, "job not found in EXIT or CRASH\n");
-  gprint (GP_LOG, "STATUS %d\n", -2);
-  return (FALSE);
 }
