/*
 *========================================================================
 * $Id: connect.c 87 2004-09-28 17:31:03Z rgb $
 *
 * See copyright in copyright.h and the accompanying file COPYING
 *========================================================================
 */

#include <wulfware/libwulf.h>


/*
 *========================================================================
 * connect_to_all hosts() runs through all hosts in the hostlist and
 * establishes a connection to them if possible.  As a general rule,
 * this will be called only once at the very beginning of wulfstat or
 * any other program and the system will still have an ordinary tty
 * to write output to.  Because there can be a LOT of hosts, and each
 * dead host can take ten seconds or so to time out, we announce each
 * connection attempt.
 *
 * This (obviously) must be run as a thread, so it requires a single
 * argument (which must be a struct as there are two arguments
 * required by the routine).
 *========================================================================
 */
void connect_to_all_hosts(Connectlist *cl)
{

 List *hostlist;
 Host *hostptr;
 ListElement *element;

 if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
   fprintf(OUTFP,"D_CONNECT_HOSTS: Starting NEW (llist) connect_to_all_hosts().  Use -v %d to focus.\n",D_CONNECT_HOSTS);
   fprintf(OUTFP,"D_CONNECT_HOSTS: Hostlist = %0x, connect_delay = %d\n",
     cl->hostlist,cl->connect_delay);
 }

 hostlist = cl->hostlist;
 /* Loop forever */
 while(-1){
   element = hostlist->head;
   while(element != NULL){
     hostptr = element->data;
     /*
      * Only try to connect if we're not already connected.
      */
     if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
       fprintf(OUTFP,"D_CONNECT_HOSTS: Checking %s: connection status %d\n",hostptr->hostname,hostptr->connected);
     }
     /*
      * For hostptr->connected:
      * 0 is not connected.  1 is connected but not initialized.  2 is
      * connected and initialized.  We only connect if not connected.
      */
     if(hostptr->connected == 0){
       if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
         fprintf(OUTFP,"D_CONNECT_HOSTS: Trying to connect %s by pointer\n",hostptr->hostname);
	 fprintf(OUTFP,"D_CONNECT_HOSTS: hostip = %-16s inetaddress = %08x port = %4d\n",
	          hostptr->hostip,hostptr->inetaddress,hostptr->port);
       }
       hostptr->connected = connect_to_host_by_ptr(hostptr);
       if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
         fprintf(OUTFP,"D_CONNECT_HOSTS: %s connect status = %d\n",hostptr->hostname,hostptr->connected);
       }
       if(hostptr->connected){
         if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
           fprintf(OUTFP,"D_CONNECT_HOSTS: initializing values for %s\n",hostptr->hostname);
         }
         init_values(hostptr);
	 if(hostptr->connected == 2){
           if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
             fprintf(OUTFP,"D_CONNECT_HOSTS: Got valid connection to %s\n",hostptr->hostname);
           }
	 } else {
           if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
             fprintf(OUTFP,"D_CONNECT_HOSTS: Connection to %s did not initialize, status = %d\n",hostptr->hostname,hostptr->connected);
           }
	 }
       } else {
         if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
           fprintf(OUTFP,"D_CONNECT_HOSTS: xmlsysd on %s not responding.  Marking host down.\n",hostptr->hostname);
	 }
       }
     }
     element = element->next;
   }
   sleep(cl->connect_delay);	/* recheck every connect_delay seconds */
 }

}


/*
 *========================================================================
 * Each host is saved in a linked list.  This takes a pointer
 * to the host struct and attempts to establish a connection
 * with that (presumed) host.
 *========================================================================
 */
int connect_to_host_by_ptr(Host *hostptr)
{

 /* loop index */
 int j;

 /* socket variables */
 int host_len,result;
 struct sockaddr_in host;
 struct linger linger;

 if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
   fprintf(OUTFP,"D_CONNECT_HOSTS: Starting connect_to_host_by_ptr().  Use -v %d to focus.\n",D_CONNECT_HOSTS);
   fprintf(OUTFP,"D_CONNECT_HOSTS: Trying to connect to %s at %x:%d by pointer\n",hostptr->hostname,hostptr->inetaddress,hostptr->port);
 }

 /*
  * If this is null, it means we couldn't resolve the name.  Mark the
  * system DOWN in all displays.  We'll do connected by a -1, not a 1,
  * to differentiate it from hosts that resolve but don't have a
  * connection YET!
  */
 if(hostptr->inetaddress == 0){
   hostptr->client_fd = -1;	/* No socket to read from */
   return -1;
 }

 host_len = sizeof(host);

 /* 
  * Create an AF_INET (tcp) socket, bidirectional:
  *    a) zero the address struct.
  *    b) set the address family (AF_INET or AF_INET6)
  *    c) set the internet address to be connected to
  *    d) set the port address to be connected to
  *    e) finally, create the SOCK_STREAM socket for each host
  */
 bzero( (char*)&host,host_len); 
 host.sin_family = AF_INET;
 host.sin_addr.s_addr = hostptr->inetaddress;
 host.sin_port = htons(hostptr->port);
 hostptr->client_fd = socket(AF_INET,SOCK_STREAM,0);

 /* 
  * Try to connect to this host.  Only up to three times and
  * then quit.  This prevents startup hangs.  We can (and will)
  * try again later if we don't snag a connection now.
  */
 for(j=0;j<3;j++){
   if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
     fprintf(OUTFP,"D_CONNECT_HOSTS: client_fd = %d\n",hostptr->client_fd);     
   }
   result = connect(hostptr->client_fd,(struct sockaddr*) &host,host_len);
   if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
     fprintf(OUTFP,"D_CONNECT_HOSTS: connect result = %d\n",result);
   }
   if (result == 0) break;
 }

 if( hostptr->client_fd && result == 0) {
   if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
     fprintf(OUTFP,"D_CONNECT_HOSTS: Got a connection to %s on client_fd = %d\n",hostptr->hostname,hostptr->client_fd);
   }
   return(1);
 } else {
   if((verbose == D_ALL) || (verbose == D_CONNECT_HOSTS)){
     fprintf(OUTFP,"Failed to connect to %s at this time.\n",hostptr->hostname);
   }
   return(0);
 }

}

void send_command_to_host_by_ptr(Host *hostptr, char *cmd)
{

 int i,old_length,new_length,send_length;
 char *sndbuf;

 if((verbose == D_ALL) || (verbose == D_SENDCMD)){
   fprintf(OUTFP,"D_SENDCMD: Starting D_SENDCMD.  Use -v %d to focus.\n",D_SENDCMD);
   
 }

 if(hostptr->client_fd < 1) {
   return;	/* Don't try to send to a nonexistent socket */
 }

 /*
  * Terminate the commands with LF.  This is the default
  * terminator for all command strings, read with readline().
  * Commands in commands[] do NOT have terminators, so
  * any command string received here will almost certainly
  * require termination.  We arbitrarily put a 64K limit
  * on command length (hoping that this fits in TCP send
  * buffer default).
  */
 old_length = strlen(cmd);
 if( cmd[old_length-1] != (char)10 ) {
   new_length = old_length + 2;	/* to accomodate terminating LF and NULL */
   send_length = old_length + 1;	/* But don't send the NULL */
 } else {
   new_length = old_length + 1;	/* already ends in LF NULL */
   send_length = old_length;	/* But don't send the NULL */
 }

 /*
  * This allocates exactly enough space for:
  *   (string)(LF)(0)
  */
 sndbuf = (char*) malloc((size_t) new_length*sizeof(char));
 if((verbose == D_ALL) || (verbose == D_SENDCMD)){
   fprintf(OUTFP,"D_SENDCMD: Sending command %s of (unpadded) length %d\n",cmd,old_length);
   
 }

 if( old_length == new_length) {
   /* command is already terminated with a \n */
   if((verbose == D_ALL) || (verbose == D_SENDCMD)){
     fprintf(OUTFP,"D_SENDCMD: Command %s already LF terminated.\n",cmd);
     
   }
   strncpy(sndbuf,cmd,new_length);
 } else {
   if((verbose == D_ALL) || (verbose == D_SENDCMD)){
     fprintf(OUTFP,"D_SENDCMD: Adding LF to command %s\n",cmd);
     
   }
   strncpy(sndbuf,cmd,new_length);
   sndbuf[new_length-2] = (char)10;	/* Overwrites the terminating NULL */
   sndbuf[new_length-1] = (char)0;	/* Terminates with  NULL (not sent) */
 }
 if((verbose == D_ALL) || (verbose == D_SENDCMD)){
   fprintf(OUTFP,"D_SENDCMD: Sending command = %s of length %d to %d\n",sndbuf,send_length,hostptr->client_fd);
   
 }

 /*
  * This routine isn't QUITE trivial, as we don't want it to block
  * if the daemon for any reason isn't responsive.  So we use the
  * send command with the MSG_DONTWAIT flag.  If the operation
  * would block (in spite of the connection having been established
  * and affirmed a few microseconds ago) then for the moment we just
  * move on.  Note that len < 1024, so it is certainly smaller than
  * any default TCP send buffer.
  *
  * Our action on any error is to just close the socket and continue.
  * The program will reopen the socket again later when maybe it will
  * work.
  */
 if(send(hostptr->client_fd,sndbuf,send_length,MSG_DONTWAIT) == -1) {
   close(hostptr->client_fd);
   hostptr->client_fd = 0;
   hostptr->connected = 0;
 }

 if((verbose == D_ALL) || (verbose == D_SENDCMD)){
   fprintf(OUTFP,"D_SENDCMD: command sent!\n");
   
 }

 free(sndbuf);

}

void send_command_to_all_hosts(char *cmd)
{

 Host *hostptr;
 ListElement *element;

 if((verbose == D_ALL) || (verbose == D_SENDCMD)){
   fprintf(OUTFP,"D_SENDCMD: Starting send_command_to_all.  Use -v %d to focus.\n",D_SENDCMD);
   
 }

 element = hostlist->head;
 while(element != NULL){
   hostptr = element->data;
   if(hostptr->client_fd && hostptr->connected){
     if((verbose == D_ALL) || (verbose == D_SENDCMD)){
       fprintf(OUTFP,"D_SENDCMD: Sending command %s to fd %d\n",cmd,hostptr->client_fd);
       
     }
     send_command_to_host_by_ptr(hostptr,cmd);

   }
   element = element->next;
 }

}

/*
 * The following two are placeholders for routines that will
 * shut down all host connections gracefully, if possible,
 * or shut down a selected host connection by pointer.
 */
void disconnect_from_hosts_by_ptr(Host *hostptr)
{

}

void disconnect_from_all_hosts(List *hostlist)
{

}
