Index: /branches/eam_branches/ipp-20111122/Ohana/src/dvosplit/src/dvosplit.c
===================================================================
--- /branches/eam_branches/ipp-20111122/Ohana/src/dvosplit/src/dvosplit.c	(revision 33203)
+++ /branches/eam_branches/ipp-20111122/Ohana/src/dvosplit/src/dvosplit.c	(revision 33204)
@@ -1,4 +1,5 @@
 # include "dvosplit.h"
 
+// XXX : go to this : dvosplit (catdir) (outlevel) [-region Rmin Rmax Dmin Dmax]
 // dvosplit (outlevel) [-region Rmin Rmax Dmin Dmax]
 int main (int argc, char **argv) {
@@ -14,4 +15,7 @@
   ConfigInit (&argc, argv);
   args (argc, argv);
+
+  // XXX : OUTCAT = strcreate (argv[1]);
+  // XXX : OUT_DEPTH = atoi (argv[2]);
 
   OUT_DEPTH = atoi (argv[1]);
@@ -29,4 +33,5 @@
     // if (current level >  out level) skip: cannot currently merge catalogs
     // if (current level == out level) skip: no action is needed
+    // XXX if outdir != indir, copy the tables across
     if (skylist[0].regions[i][0].depth >= OUT_DEPTH) continue;
 
@@ -51,4 +56,5 @@
     // change sky.regions[i].depth for these regions
     outlist = SkyListByPatch (sky, OUT_DEPTH, skylist[0].regions[i]);
+    // XXX update the filenames for outlist given the new outdir
 
     outcatalogs = open_output_catalogs (outlist);
Index: /branches/eam_branches/ipp-20111122/Ohana/src/libautocode/def/SkyRegion.d
===================================================================
--- /branches/eam_branches/ipp-20111122/Ohana/src/libautocode/def/SkyRegion.d	(revision 33203)
+++ /branches/eam_branches/ipp-20111122/Ohana/src/libautocode/def/SkyRegion.d	(revision 33204)
@@ -8,10 +8,23 @@
 FIELD   Dmin,	   D_MIN,	   float, 
 FIELD   Dmax,	   D_MAX,	   float, 
-FIELD   childS,	   CHILD_S,	   int,           sequence number in full table of first child
-FIELD   childE,	   CHILD_E,	   int,           sequence number in full table of last child + 1
-FIELD   parent,	   PARENT,	   int,           sequence number in full table of parent
-FIELD   index,     INDEX,          int,           sequence number in full table of this entry
-FIELD   depth,	   DEPTH,	   char,          depth of this entry
-FIELD   child,	   CHILD,	   char,          does this entry have children?
-FIELD   table,	   TABLE,	   char,          does this entry have a table?
-FIELD   name,      NAME,           char[21],      name / filename
+FIELD   childS,	   CHILD_S,	   int,            sequence number in full table of first child
+FIELD   childE,	   CHILD_E,	   int,            sequence number in full table of last child + 1
+FIELD   parent,	   PARENT,	   int,            sequence number in full table of parent
+FIELD   index,     INDEX,          int,            sequence number in full table of this entry
+FIELD   depth,	   DEPTH,	   char,           depth of this entry
+FIELD   child,	   CHILD,	   char,           does this entry have children?
+FIELD   table,	   TABLE,	   char,           does this entry have a table?
+FIELD   name,      NAME,           char[19],       name / filename
+FIELD   hostID,    HOST_ID,        unsigned short, host ID where data is stored
+
+# note : 2012.02.05 : stole 2 bytes from 'name' to use for host ID (no
+#        DBs were created with more than 17 bytes in the name).  The
+#        hostID points at an entry in the host table (text file).
+#        There is no safety on this, but it is recoverable (by
+#        examining the directories) if misplaced.
+# 
+#        Note that SkyTables from databases generated in the past may
+#        have garbage characters here.  To ensure catch this
+#        condition, we add a header keyword to SkyTables in which the
+#        hostID has been set; if that is not present, the SkyTable
+#        loading software should init these bytes to 0 (== unassigned).
Index: /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/AssignSkyToHost.c
===================================================================
--- /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/AssignSkyToHost.c	(revision 33204)
+++ /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/AssignSkyToHost.c	(revision 33204)
@@ -0,0 +1,30 @@
+# include "photdbc.h"
+# define DEBUG 1
+
+int AssignSkyToHost (SkyList *skylist, HostTable *table) {
+
+  int i;
+  long A, B;
+
+  // init rnd seed
+  A = time(NULL);
+  for (B = 0; A == time(NULL); B++);
+  srand48(B);
+
+  for (i = 0; i < skylist->Nregions; i++) {
+    
+    // assign or re-assign? if a region is already assigned, do we keep it?
+
+    // XXX for the moment, do not reassign tables 
+    // XXX we may want to run this program multiple times
+    if (skylist->regions[i]->hostID) {
+      continue;
+    }
+
+    // assign all regions to one of the Nhost hosts
+    int N = MAX(table->Nhosts - 1, table->Nhosts * drand48());
+    skylist->regions[i]->hostID = table->hosts[N].hostID;
+  }    
+
+  return (TRUE);
+}
Index: /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/CopyToHostLocation.c
===================================================================
--- /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/CopyToHostLocation.c	(revision 33204)
+++ /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/CopyToHostLocation.c	(revision 33204)
@@ -0,0 +1,95 @@
+# include <openssl/md5.h>
+# include <unistd.h>
+# include "photdbc.h"
+# define DEBUG 1
+
+int CostToHostLocation (SkyList *skylist, HostTable *table) {
+
+  int i;
+  uid_t uid;
+  gid_t gid;
+  struct stat filestat;
+  char srcname[1024];
+  char tgtname[1024];
+    
+  uid = getuid();
+  gid = getgid();
+
+  for (i = 0; i < skylist->Nregions; i++) {
+    
+    // assign or re-assign? if a region is already assigned, do we keep it?
+
+    // unassigned
+    if (!skylist->regions[i]->hostID) continue;
+    if (skylist->regions[i]->hostID < 0) continue;
+
+    short index = table->index[skylist->regions[i]->hostID];
+    HostInfo *host = &table->hosts[index];
+
+    // move from CATDIR/name to table->hosts[].pathname/name
+    sprintf (srcname, "%s/%s", CATDIR, skylist->regions[i]->name);
+    sprintf (tgtname, "%s/%s", host->pathname, skylist->regions[i]->name);
+
+    // does srcname exist & can it be read?
+
+    // check permission to read file 
+    int status = stat (srcname, &filestat);
+    if (!status) continue;  // file does not exist (check error stats for other issues?)
+    
+    // can we read the file
+    if (uid == filestat.st_uid) {
+      if (filestat.st_mode & S_IRUSR) goto valid;
+    }
+    if (gid == filestat.st_gid) {
+      if (filestat.st_mode & S_IRGRP) goto valid;
+    }
+    if (filestat.st_mode & S_IROTH) goto valid;
+    
+    fprintf (stderr, "cannot read file %s, skipping\n", srcname);
+    continue;
+
+  valid:
+    // since we have to open and read the whole file anyway, 
+    // perhaps we should just write the output file?
+
+    // read srcname, write to tgtname, get MD5 sum for srcname as we go:
+
+    // open the src file
+    FILE *fInput = fopen (srcname, "r");
+    int fdInput = fileno (fInput);
+
+    // open the tgt file
+    FILE *fOutput = fopen (srcname, "w");
+    int fdOutput = fileno (fInput);
+
+    int n;
+    MD5_CTX c;
+    char buf[512];
+    ssize_t nbytes;
+
+    MD5_Init(&c);
+    for (; (nbytes = read (fd, buf, 512)) > 0;) {
+      MD5_Update(&c, buf, nbytes);
+      nbytes = write (fd, buf, 512);
+    }
+    MD5_Final(out, &c);
+
+    for (n = 0; n < MD5_DIGEST_LENGTH; n++) {
+      fprintf (stderr, "%02x", out[n]);
+    }
+    fclose (f);
+    flush (f);
+
+    // need to re-open and re-read tgtname to check md5sum
+
+}
+    
+
+
+    // assign all regions to one of the Nhost hosts
+    int N = MAX(table->Nhosts - 1, table->Nhosts * drand48());
+    skylist->regions[i]->hostID = table->hosts[N].hostID;
+  }    
+
+  return (TRUE);
+}
Index: /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/HostTableLoad.c
===================================================================
--- /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/HostTableLoad.c	(revision 33203)
+++ /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/HostTableLoad.c	(revision 33204)
@@ -1,3 +1,4 @@
 # include "photdbc.h"
+# define DEBUG 1
 
 typedef struct {
@@ -48,4 +49,6 @@
   InitHosts (hosts, Nhosts, NHOSTS);
 
+  int maxID = 0;
+
   int Nline;
   for (Nline = 0; TRUE; Nline ++) {
@@ -69,4 +72,16 @@
     }
 
+    // check the validity of ID (0 < ID < MAX_SHORT)
+
+    if (ID < 1) {
+      fprint (stderr, "invalid host ID %d\n", ID);
+      exit (1);
+    }
+    if (ID > 65535) {
+      fprint (stderr, "invalid host ID %d\n", ID);
+      exit (1);
+    }
+    maxID = MAX(maxID, ID);
+
     hosts[Nhosts].hostID = ID;
     hosts[Nhosts].hostname = strcreate(tmphost);
@@ -85,4 +100,15 @@
   table->hosts = hosts;
 
+  ALLOCATE (table->index, unsigned short, maxID + 1);
+  for (i = 0; i <= maxID; i++) table->index[i] = -1;
+
+  for (i = 0; i < table->Nhosts; i++) {
+    if (table->index[table->hosts[i].hostID] == -1) {
+      fprintf (stderr, "error: duplicate hostID %d\n", table->hosts[i].hostID);
+      exit (1);
+    }
+    table->index[table->hosts[i].hostID] = i;
+  }
+
   return table;
 }
@@ -90,9 +116,11 @@
 int CheckHostsAndPaths (HostTable *table) {
 
+  int i;
+  int stdio[3];
   char command[64];
   char shell[64];
 
   strcpy (command, "ssh");
-  strcpy (shell, "pclient");
+  strcpy (shell, "csh");
 
   for (i = 0; i < table->Nhosts; i++) {
@@ -101,10 +129,26 @@
     if (DEBUG) fprintf (stderr, "starting host within thread\n");
 
-    pid = rconnect (command, host[0].hostname, shell, stdio);
+    pid = rconnect (command, table->hosts[i].hostname, shell, stdio);
     if (!pid) {     
       /** failure to start: extend retry period **/
       if (DEBUG) fprintf (stderr, "failure to start %s\n", table->hosts[i].hostname);
+      exit (1);
     }
+
+    // XXX check anything else?
+
+    // close the connection
+    close(stdio[0]);
+    close(stdio[1]);
+    close(stdio[2]);
+
+    status = check_dir_access (table->hosts[i].pathname, DEBUG);
+    if (!status) {
+      fprintf (stderr, "failed to find / create target path\n");
+      exit (2);
+    }
+
+    fprintf (stderr, "success : %s, %s\n", table->hosts[i].hostname, table->hosts[i].pathname);
   }
-
+  return (TRUE);
 }
Index: /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/rconnect.c
===================================================================
--- /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/rconnect.c	(revision 33204)
+++ /branches/eam_branches/ipp-20111122/Ohana/src/photdbc/src/rconnect.c	(revision 33204)
@@ -0,0 +1,148 @@
+# include "pcontrol.h"
+
+/* connection can take a while, allow up to 2 sec */
+# define CONNECT_TIMEOUT 5000
+
+/* connect to host, start the shell: ssh hostname pclient -> command hostname shell
+   stdio is an array of file descriptors (stdio[3])
+*/
+
+int rconnect (char *command, char *hostname, char *shell, int *stdio) {
+
+  int i = 0, stdin_fd[2], stdout_fd[2], stderr_fd[2], status;
+  int result, waitstatus;
+  pid_t pid;
+  char *p;
+  char **argv;
+  IOBuffer buffer;
+  struct timespec request, remain;
+
+  ASSERT (command != NULL, "command is NULL");
+  ASSERT (hostname != NULL, "hostname is NULL");
+  ASSERT (shell != NULL, "shell is NULL");
+  ASSERT (stdio != NULL, "stdio is NULL");
+
+  bzero (stdin_fd,  2*sizeof(int));
+  bzero (stdout_fd, 2*sizeof(int));
+  bzero (stderr_fd, 2*sizeof(int));
+
+  if (pipe (stdin_fd)  < 0) goto pipe_error;
+  if (pipe (stdout_fd) < 0) goto pipe_error;
+  if (pipe (stderr_fd) < 0) goto pipe_error;
+
+  ALLOCATE (argv, char *, 4);
+  argv[0] = command;
+  argv[1] = hostname;
+  argv[2] = shell;
+  argv[3] = 0;
+
+  pid = fork ();
+  if (!pid) { /* must be child process */
+    if (VerboseMode()) gprint (GP_ERR, "starting remote connection to %s...", hostname);
+
+    /* close the other ends of the pipes */
+    close (stdin_fd[1]);
+    close (stdout_fd[0]);
+    close (stderr_fd[0]);
+
+    /* tie our ends of the pipes to stdin, stdout, stderr */
+    dup2 (stdin_fd[0],  STDIN_FILENO);
+    dup2 (stdout_fd[1], STDOUT_FILENO);
+    dup2 (stderr_fd[1], STDERR_FILENO);
+
+    /* set all three unblocking */
+    setvbuf (stdin,  (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stdout, (char *) NULL, _IONBF, BUFSIZ);
+    setvbuf (stderr, (char *) NULL, _IONBF, BUFSIZ);
+
+    status = execvp (argv[0], argv);
+    gprint (GP_ERR, "error starting remote shell process\n");
+    pcontrol_exit (60);
+  }
+  free (argv);
+
+  /* close the other ends of the pipes */
+  close (stdin_fd[0]);  stdin_fd[0]  = 0;
+  close (stdout_fd[1]); stdout_fd[1] = 0;
+  close (stderr_fd[1]); stderr_fd[1] = 0;
+
+  /* make the pipes non-blocking */
+  fcntl (stdin_fd[1],  F_SETFL, O_NONBLOCK);
+  fcntl (stdout_fd[0], F_SETFL, O_NONBLOCK);
+  fcntl (stderr_fd[0], F_SETFL, O_NONBLOCK);
+
+  /* perform handshake with pclient to verify alive & running */
+  /** this handshake is similar to PclientCommand, but has important differences **/
+  InitIOBuffer (&buffer, 0x100);
+
+  /* send handshake command */
+  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 */
+  p = NULL;
+  status = -1;
+  for (i = 0; (i < CONNECT_TIMEOUT) && (status != 0) && (p == NULL); i++) {
+    status = ReadtoIOBuffer (&buffer, stdout_fd[0]);
+    p = memstr (buffer.buffer, "CONNECTED", buffer.Nbuffer);
+    usleep (10000); // wait for client to be connected
+  }
+  if (status == 0) goto connect_error;
+  if (status == -1) goto connect_error;
+  if (VerboseMode()) gprint (GP_ERR, "%d cycles to connect\n", i);
+  FreeIOBuffer (&buffer);
+
+  if (VerboseMode()) gprint (GP_ERR, "Connected\n");
+
+  stdio[0] = stdin_fd[1];
+  stdio[1] = stdout_fd[0];
+  stdio[2] = stderr_fd[0];
+
+  return (pid);
+
+pipe_error:
+  perror ("pipe error:");
+  goto close_pipes;
+
+connect_error:
+  if (VerboseMode()) gprint (GP_ERR, "error while connecting, status: %d, ncycles: %d\n", status, i);
+
+  /* avoid blocking on waitpid, test every 100 usec, up to 50 msec */
+  request.tv_sec = 0;
+  request.tv_nsec = 100000;
+
+  /* harvest the child process: kill & wait (< 100 ms) for exit */
+  kill (pid, SIGKILL);
+  result = waitpid (pid, &waitstatus, WNOHANG);
+  for (i = 0; (i < 50) && (result == 0); i++) {
+    nanosleep (&request, &remain);
+    result = waitpid (pid, &waitstatus, WNOHANG);
+  }
+
+  if ((result == -1) && (errno != ECHILD)) {
+    gprint (GP_ERR, "unexpected error from waitpid (%d): programming error\n", errno);
+    pcontrol_exit (61);
+  }
+  if (result == 0) {
+    if (VerboseMode()) gprint (GP_ERR, "child did not exit (rconnect)??");
+  }
+  if (result > 0) {
+    if (result != pid) {
+      gprint (GP_ERR, "waitpid error: mis-matched PID (%d vs %d).  programming error\n", result, pid);
+      pcontrol_exit (62);
+    }
+    if (WIFSTOPPED(waitstatus)) {
+      gprint (GP_ERR, "waitpid returns 'stopped': programming error\n");
+      pcontrol_exit (63);
+    }
+  }
+
+close_pipes:
+  if (stdin_fd[0]  != 0) close (stdin_fd[0]);
+  if (stdin_fd[1]  != 0) close (stdin_fd[1]);
+  if (stdout_fd[0] != 0) close (stdout_fd[0]);
+  if (stdout_fd[1] != 0) close (stdout_fd[1]);
+  if (stderr_fd[0] != 0) close (stderr_fd[0]);
+  if (stderr_fd[1] != 0) close (stderr_fd[1]);
+  return (FALSE);
+}
