/*
 *========================================================================
 * $Id: init.c 88 2004-09-28 22:49:38Z rgb $
 *
 * See copyright in copyright.h and the accompanying file COPYING
 *========================================================================
 */

#include <wulfware/libwulf.h>

/*
 *========================================================================
 *
 * We have got to get this straightened out.  There are two ways to
 * get to init_host(hostptr).  One is immediately following a connection
 * in the connection loop.  Initialization MUST be done there because
 * is asynchronous; we cannot predict when any given host will be
 * be connected to and it will always need to be initialized once a
 * connection is made.  However, a host ALSO has to be (re)initialized
 * whenever the display changes, as certain structures (e.g. the
 * of CPUs or interfaces) are dynamic and are counted and allocated by
 * the initialization routine.  Because of this, the safe thing to do
 * is free/clear the entire values struct in the host struct so that
 * they can be recounted and reinitialized.
 *
 * init_values(hostptr) thus:
 *
 *  a) clears the hostptr->val struct and the hostptr->pids
 * struct with init_state(hostptr), which ends with a SEND command
 * to the connected xmlsysd.  This is the ONLY SAFE PLACE to call
 * init_state(hostptr).
 *
 * b) Reads the xmlsysd return message generated by the SEND and
 * converts it into a newly created xmldoc and xpath.
 *
 * c) Runs through all the possible state flags and read/parse out
 * the associated values for initialization or update/display.  All
 * the libwulf-based clients programs can operate parsimoniously in
 * terms of memory and network and processing requirements by
 * throttling xmlsysd and processing only tag sets that contain
 * variables actually needed for the current client display.
 *
 * d) Destroys the xmldoc and xpath so we don't leak memory.
 *
 * e) SETS hostptr->connected to 2 (ready to update) after a short
 * delay (per host).  The delay is so that subsequent updates (which can
 * happen "instantly" in a separate thread by chance as soon as the flag
 * is updated) can compute not horribly inaccurate rates with the
 * timestamp deltas in the return messages.
 *========================================================================
 */

void init_values(Host *hostptr)
{

 int i;
 
 struct timespec pause;

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

 /*
  * The very first thing we do is initialize the state of the xmlsysd
  * we have just connected to.
  */
 init_state(hostptr);

 /*
  * Receive the xmlsysd return, process it until we have a working
  * xpath entry for the remaining parsing.
  */
 create_xmlsysd_xpath(hostptr,init_buf);
 if(hostptr->connected == 0) return;

 /*
  * OK, we no longer do this on the basis of "display type".  We do it on the
  * basis of hostptr->dctl.flag for each throttleable xmlsysd tag.  We only
  * init and extract what we need.
  */

 /*
  * <identity> derived values
  */
 if(hostptr->dctl.identity){
   init_identity(hostptr);
 }

 /*
  * <time> derived values
  */
 if(hostptr->dctl.time){
   init_time(hostptr);
 }

 /*
  * <users> derived values
  */
 if(hostptr->dctl.users){
   init_users(hostptr);
 }

 /*
  * <cpuinfo> derived values
  */
 if(hostptr->dctl.cpuinfo){
   init_cpuinfo(hostptr);
 }

 /*
  * <loadavg> derived values (nothing to do)
  */
 if(hostptr->dctl.loadavg){
   init_loadavg(hostptr);
 }

 /*
  * <meminfo> derived values (nothing to do)
  */
 if(hostptr->dctl.meminfo){
   init_meminfo(hostptr);
 }

 /*
  * <net> derived values
  */
 if(hostptr->dctl.net){
   init_net(hostptr);
 }

 /*
  * <stat> derived values
  */
 if(hostptr->dctl.stat){
   init_stat(hostptr);
 }

 /*
  * <sysvipc> derived values (nothing to do)
  */
 if(hostptr->dctl.sysvipc){
   init_sysvipc(hostptr);
 }

 /*
  * <uptime> derived values (nothing to do)
  */
 if(hostptr->dctl.uptime){
   init_uptime(hostptr);
 }

 /*
  * <version> derived values (nothing to do)
  */
 if(hostptr->dctl.version){
   init_version(hostptr);
 }

 /*
  * <pids> derived values (nothing to do)
  */
 if(hostptr->dctl.pids){
   init_pids(hostptr);
 }


 /*
  * The very LAST thing we have to do is free the xpath context and 
  * xml doc for the host.  Otherwise this code will leak memory 
  * sieve-like.  Or worse, die horribly and without explanation.
  * This MUST be done, and completed, before an update_values() call
  * to this host, so it precedes the delay and change in the controlling
  * state flag.
  */
 if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
   fprintf(OUTFP,"D_INIT_VALUES: Freeing context %d\n",(int) hostptr->xp_doc);
 }
 destroy_xmlsysd_xpath(hostptr);

 /*
  * PAUSE 1 msec! and mark the host as initialized.  This prevents
  * collisions with update_hosts(), as it is entirely possible that the
  * connect loop on one thread is initializing a host at the same time
  * that the update loop on the other thread is trying to update it.  One
  * MUST NOT update without initializing as various structs may not even
  * exist yet for cpu and network interfaces (which can vary in number and
  * are dynamically counted and initialized).
  */

 pause.tv_sec=0;
 pause.tv_nsec=1000000;  /* a million nsec = 1 msec */
 nanosleep(&pause, (struct timespec *) NULL);
 /* Cruft:  usleep is deprecated and non-posix.  usleep(1000); */
 hostptr->connected = 2;
 if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
   fprintf(OUTFP,"D_INIT_VALUES: All done, exiting with connected = %d.\n",hostptr->connected);
 }


}


/*
 * This is NOT QUITE cruft.  We will call this (I suppose) after
 * resetting dctl's to correspond to a new display in wulfstat or
 * related display based clients.  In most (maybe all) cases it may
 * prove easier to just init_values(hostptr) at the end of the
 * same loop that is resetting all the dctl's...
 */
void init_host_values(List *hostlist)
{

 Host *hostptr;
 ListElement *element;

 /*
  * walk the list and call init_values() if the host is connected.
  * This will clear all old values and reset dctl's to current and
  * initialize all variables (some for computing rates later).
  */
 if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
   fprintf(OUTFP,"D_INIT_VALUES: Starting init_host_values().  Use -v %d to focus.\n",D_INIT_VALUES);
 }
 element = hostlist->head;
 while(element != NULL){
   hostptr = element->data;
   if(hostptr->client_fd && hostptr->connected){
     if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
       fprintf(OUTFP,"D_INIT_VALUES: About to init_values() for host %s.\n",hostptr->hostname);
     }
     init_values(hostptr);
   }
   element = element->next;
 }

}

/*
 * Note clear what these are here for.  I suppose they can be used
 * to initialize the connected xmlsysd so that it knows about the
 * current user list or pid list.
 */
void send_userlist(Host *hostptr,List *userlistptr)
{

 int i;

 char *user;
 ListElement *element;
 static char command[K];

 element = userlistptr->head;
 while(element != NULL){
   user = element->data;
   snprintf(command,K,"adduser %s",user);
   send_command_to_host_by_ptr(hostptr,command);
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"Sending %s to xmlsysd on %s\n",command,hostptr->hostname);
     
   }
   element = element->next;
 }

}

void send_tasklist(Host *hostptr,List *tasklistptr)
{

 int i;

 char *task;
 ListElement *element;
 static char command[K];

 element = tasklistptr->head;
 while(element != NULL){
   task = element->data;
   snprintf(command,K,"addtask %s",task);
   send_command_to_host_by_ptr(hostptr,command);
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"Sending %s to xmlsysd on %s\n",command,hostptr->hostname);
     
   }
   element = element->next;
 }

}

/*
 *==================================================================
 * This routine sets the state of xmlsysd on the remote host to
 * match the state in the hostptr->dctl.flags.  This begs the question
 * of how the flags state gets set in the first place. It can't
 * happen until the host struct is allocated, and it has to be
 * done by the toplevel application, as only it knows what the
 * state is supposed to be.
 *==================================================================
 */
void init_state(Host *hostptr)
{

 /* Loop indices */
 int i,j,k;

 char *xvalue;
 /* id buffer */
 char idbuf[K];
 /* scratch buffer for attributes */
 char attr_buf[K];

 /* Cpu pointer */
 Cpu *cpuptr;

 /* 
  * scratch: current timestamp and delta (to microsecond resolution) 
  */
 double timestamp, delta_time, delta_value, new_value;
 long seconds,useconds,delta_sec,delta_usec,new_ivalue;
 unsigned long new_uvalue;

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

 /*
  * We call this when we are fundamentally changing the display (for
  * example, when we first start up).  This routine PRESUMES that anything
  * in hostptr aside from the toplevel identity data needs to be cleared.
  * We therefore start by clearing VALUES part of the the Host struct.
  * The only things preserved are hostname, hostip, client_fd, dctl, and
  * connected (connection status), things that are not monitored values.
  * USUALLY we will get here with hostptr->connected equal to 1, and in
  * fact in many cases (possibly all) this is the only safe way to get
  * here as we must have a connection up in order to send to it.  We may
  * need an enumerated list of connected states to make this clear in
  * code.
  */
 clear_values(hostptr);


 /*
  * The host struct is in a precisely determined state (empty).  We now
  * have to put the daemon in a precisely determined state.  First we turn
  * off everything.  There is nothing returned to process from this
  * command.
  */
 send_command_to_host_by_ptr(hostptr,"off all");

 /*
  * EVERYTHING BELOW is selected only on a switched basis.  We only
  * get it if we need it.  Note well that the SAME switched blocks 
  * are required in the associated fill_values() and display_values()
  * routines.  In this way we can make one shell (wulfstat) serve
  * many purposes and switch between functions dynamically.
  */

 /* Turn on identity */
 if(hostptr->dctl.identity){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: hostname = %s  dctl.identity = %d\n",
       hostptr->hostname,hostptr->dctl.identity);
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on identity",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on identity");
 }

 /* Turn on time */
 if(hostptr->dctl.time){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on time",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on time");
 }

 /* Turn on users */
 if(hostptr->dctl.users){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on users",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on users");
 }

 /* Turn on cpuinfo */
 if(hostptr->dctl.cpuinfo){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on cpuinfo",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on cpuinfo");
 }

 /* Turn on loadavg */
 if(hostptr->dctl.loadavg){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on loadavg",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on loadavg");
 }

 /* Turn on meminfo */
 if(hostptr->dctl.meminfo){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on meminfo",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on meminfo");
 }

 /* Turn on net */
 if(hostptr->dctl.net){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on net",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on net");
 }

 /* Turn on stat */
 if(hostptr->dctl.stat){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on stat",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on stat");
 }

 /* Turn on sysvipc */
 if(hostptr->dctl.sysvipc){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on sysvipc",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on sysvipc");
 }

 /* Turn on uptime */
 if(hostptr->dctl.uptime){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on uptime",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on uptime");
 }
 /* Turn on version */
 if(hostptr->dctl.version){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on version",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on version");
 }

 /* Turn on pids */
 if(hostptr->dctl.pids){
   if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
     fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on pids",hostptr->client_fd);
   }
   send_command_to_host_by_ptr(hostptr,"on pids");
   send_userlist(hostptr,userlist);
   send_tasklist(hostptr,tasklist);
   /* Also turn on cmdline, but only if pids is on first */
   if(hostptr->dctl.cmdline){
     if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
       fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n","on cmdline",hostptr->client_fd);
     }
     send_command_to_host_by_ptr(hostptr,"on cmdline");
   }
 }

 /*
  * (re)-initialize xmlsysd by sending it an INIT command.  This is
  * essential for certain things, e.g. cpu or network info, to come
  * through correctly.  This leaves the daemon ready to receive
  * a SEND command from create_xmlsysd_xpath().
  */
 if((verbose == D_ALL) || (verbose == D_INIT_VALUES)){
   fprintf(OUTFP,"D_INIT_VALUES: Sending command %s to fd %d\n",commands[INIT],hostptr->client_fd);
   
 }
 send_command_to_host_by_ptr(hostptr,commands[INIT]);

}
