# 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);
}

/* for use by shutdown: force machines which are up to go down
   wait for a little while for the client thread to take care 
   of them
*/
   
int DownHosts () {

  int i, Nobject, Nwait;
  Stack *stack;
  Host  *host;

  SetCheckPoint (); // ensure we can find the specified host
  stack = GetHostStack (PCONTROL_HOST_IDLE);
  ASSERT (stack != NULL, "stack missing");
  Nobject = stack[0].Nobject;
  for (i = 0; i < Nobject; i++) {
    host = PullStackByLocation (stack, STACK_TOP);
    if (host == NULL) continue;
    host[0].markoff = TRUE;
    PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
  }

  stack = GetHostStack (PCONTROL_HOST_BUSY);
  ASSERT (stack != NULL, "stack missing");
  Nobject = stack[0].Nobject;
  for (i = 0; i < Nobject; i++) {
    host = PullStackByLocation (stack, STACK_TOP);
    if (host == NULL) continue;
    host[0].markoff = TRUE;
    PutHost (host, PCONTROL_HOST_IDLE, STACK_BOTTOM);
  }
  ClearCheckPoint ();

  Nwait = 0;
  stack = GetHostStack (PCONTROL_HOST_IDLE);
  ASSERT (stack != NULL, "stack missing");

  gprint (GP_ERR, "waiting for clients to exit");
  while ((Nwait < 15) && stack[0].Nobject) {
    gprint (GP_ERR, ".");
    usleep (100000); // wait for clients to exit
    Nwait++;
  }
  gprint (GP_ERR, "\n");
  if (stack[0].Nobject) {
    gprint (GP_ERR, "trouble shutting down all pclient instances: %d still alive\n", stack[0].Nobject);
  } else {
    gprint (GP_ERR, "done\n");
  }
  return (TRUE);
}

int StopHost (Host *host) {

  int       status;
  IOBuffer  buffer;

  InitIOBuffer (&buffer, 0x100);
  status = PclientCommand (host, "exit", "Goodbye", &buffer);

  /* check on success of pclient command */
  switch (status) {
    case PCLIENT_DOWN:
      break;

    case PCLIENT_HUNG:
      gprint (GP_ERR, "host %s is not responding\n", host[0].hostname);
      break;

    case PCLIENT_GOOD:
      break;

    default:
      gprint (GP_ERR, "unknown status for pclient command: programming error\n");  
      exit (1);
  }
  HarvestHost (host[0].pid);
  return (TRUE);
}

/* the host is thought to be down; check for child exit status */
int HarvestHost (int pid) {
  
  int i, result, waitstatus;

  gprint (GP_ERR, "harvesting within thread %d\n", pthread_self());
  gprint (GP_ERR, "child process %d is down, wait for exit status\n", pid);
  
  // Loop a few times waiting for child to exit
  for (i = 0; i < 50; i++) {
    result = waitpid (pid, &waitstatus, WNOHANG);
    if ((result == -1) && (errno == ECHILD)) {
      usleep (10000); // wait for child to exit
      continue;
    } else {
      break;
    }
  }
  switch (result) {
    case -1:  /* error with waitpid */
      switch (errno) {
	case ECHILD:
	  gprint (GP_ERR, "unknown PID, not a child proc\n");
	  gprint (GP_ERR, "did process already exit?  programming error?\n");
	  break;
	case EINTR:
	case EINVAL:
	default:
	  perror ("unexpected error");
	  ABORT ("(HarvestHost)");
      }
      break;
      
    case 0:
      gprint (GP_ERR, "child did not exit??");
      abort ();
      /** put back in IDLE state? **/
      break;

    default:
      if (result != pid) {
	gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
	exit (1);
      }
      
      if (WIFEXITED(waitstatus)) {
	if (VerboseMode()) gprint (GP_ERR, "child exited with status %d\n", WEXITSTATUS(waitstatus));
      }
      if (WIFSIGNALED(waitstatus)) {
	if (VerboseMode()) gprint (GP_ERR, "child crashed with status %d\n", WTERMSIG(waitstatus));
      }
      if (WIFSTOPPED(waitstatus)) {
	if (VerboseMode()) gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
	exit (1);
      }
  }
  return (TRUE);
}
