/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* 61haes_r714 src/43haes/usr/sbin/cluster/clstat/clstat.c 1.49.1.10      */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* Restricted Materials of IBM                                            */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 1990,2013              */
/* All Rights Reserved                                                    */
/*                                                                        */
/* US Government Users Restricted Rights - Use, duplication or            */
/* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
static char sccsid[] = "@(#)42  1.49.1.10  src/43haes/usr/sbin/cluster/clstat/clstat.c, hacmp.clstat, 61haes_r714 7/2/13 19:39:20";
/*
 *   COMPONENT_NAME: CLSTAT
 *
 *   FUNCTIONS: TTYmain
 *              HTMLmain
 *		auto_stat
 *		cl_dump_nodemap
 *		compareNetifs
 *		compareNodemap
 *		copyNodemap
 *		find_cluster
 *		get_clstr_rspns
 *		get_cluster_info
 *		get_state
 *		get_substate
 *              HTML_state_color
 *              HTML_substate_color
 *		inter_act_stat
 *		make_filename
 *		fmt_date
 *		resetClustermap
 *		resetNodemap
 *		resetGroupmap
 *		show_commands
 *		sig_handler
 *		term_dumb
 *		usage
 *
 *   ORIGINS: 27
 *
 *
 *   (C) COPYRIGHT International Business Machines Corp. 1990,1994
 *   All Rights Reserved
 *   Licensed Materials - Property of IBM
 *   US Government Users Restricted Rights - Use, duplication or
 *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <memory.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rpc/rpc.h>
#include <time.h>
#include <signal.h>
#ifndef __LINUX__
#include <curses.h>
#include <term.h>
#include <nl_types.h>
#endif
#include <locale.h>

#include "clinfo.h"
#include "utilities_msg.h"
#include "clstat.h"

/* ILS */
#include <langinfo.h>
#include <wchar.h>
#define MSGSTR(Num, Str) catgets((nl_catd)get_catd("utilities.cat"), CLSTAT_MSG_SET, Num, Str)

#ifndef lint
#endif


/* Global variables used in clstat */

char           *progname;

int ONCE=0;
int INTERACT=0;
int TEST=0;
int FORCE=0;
int CLUSTERED=0;
int CLUSTERNAMED=0;
int helpOptionSpecified=0;
static int ALLSERVICES=0;
int wait_time;
int rflag;
int  nbr_nodes;
int  nbr_groups;
int  redisplay = 0;
/* this is set in response to a signal from clinfo */
int  clinfo_signal = 0;

/* Curses terminal data */
  
char  *TERM;
char  *CR = "", *UP = "", *CE = "", *CL = "";
char   tx[1024];
static char stx[1024];
char  *stx_ptr = stx;

/* The global HTML/CGI mode boolean value.
 * 1 = HTML/CGI mode
 * 0 = ASCII mode
 */
char HTMLmode = 0;

/* External decl */
char *tgetstr();

/* Forward decl */
int compareNetifs();
int compareNodemap();
void copyNodemap();
void resetClustermap();
void resetNodemap();
void resetGroupmap();
char *resourceStateStr(struct cl_group *, int);
char *resourceStateColor(struct cl_group *, int);
void javaGroupInfo(struct cl_group *, struct cl_node *, int);
char *nodeArrayStr(struct cl_node*, int *, int);
char *resourcePolicyStr(int);
caddr_t odmget(char *, char *, int *);
static nl_catd get_catd(char *);

/* routine to setup signal handler with clinfo */
static int register_with_clinfo(const int, const struct cl_cluster *);
static void clstat_sleep(const int);


void 
cl_dump_nodemap(struct cl_node *nm, int num_nodes)
{
  int i,j;
  const char *ip_string_ptr = NULL;
  char ip_string[MAXIPLEN+1];

 printf(MSGSTR(CLSTAT_DUMP_MAP, "\n\nDumping nodemap for cluster %d\n\n"), nm->cln_clusterid);

  for (i=0; i<num_nodes; i++)
  {
      printf(MSGSTR(CLSTAT_NODE_INFO, "Info for Node %s (%d)\n"), nm[i].cln_nodename, nm[i].cln_nodeid);
      printf(MSGSTR(CLSTAT_MSG_3, "    State = %d\n"), 
             nm[i].cln_state);
      printf(MSGSTR(CLSTAT_MSG_4, "    Number of interfaces = %d\n"), nm[i].cln_nif);
      printf(MSGSTR(CLSTAT_MSG_5,"    Interfaces: %s\n"),nm[i].cln_nodename);

      for (j=0; j<nm[i].cln_nif; j++)
      {
          printf(MSGSTR(CLSTAT_MSG_6, "        Id = %d\n"), 
                 nm[i].cln_if[j].cli_interfaceid);
          printf(MSGSTR(CLSTAT_MSG_7, "        state = %d\n)"), 
                 nm[i].cln_if[j].cli_state);
          printf(MSGSTR(CLSTAT_MSG_8, "        name = %s\n"), 
                 nm[i].cln_if[j].cli_name);
          bzero(ip_string, sizeof(ip_string));
          switch(((struct sockaddr*)&nm[i].cln_if[j].cli_addr6)->sa_family)
          {
          case AF_INET:
        	  ip_string_ptr = inet_ntop(AF_INET, 
          			&((struct sockaddr_in*)&nm[i].cln_if[j].cli_addr6)->sin_addr,
          			ip_string,
          			sizeof(ip_string));
          	break;
          case AF_INET6:
        	  ip_string_ptr = inet_ntop(AF_INET6, 
          			&((struct sockaddr_in6*)&nm[i].cln_if[j].cli_addr6)->sin6_addr,
          			ip_string,
          			sizeof(ip_string));
          	break;
          }
/*	  ip_string = inet_ntoa (nm[i].cln_if[j].cli_addr.sin_addr.s_addr); */
          printf(MSGSTR(CLSTAT_MSG_9, "        addr =  %s\n"), 
                 (ip_string_ptr == NULL) ? 
               MSGSTR(CLSTAT_CONVERT_ERR, "Unable to convert to ASCII") : ip_string);
          printf("        role = %d\n", nm[i].cln_if[j].cli_role);
          printf("        node = %d\n", nm[i].cln_if[j].cli_nodeid);
          printf("        active node = %d\n", nm[i].cln_if[j].cli_active_nodeid);
      }
  }
}



/*
 * Name:     clstat_sleep
 *
 * Description: This routine replaces the use of sleep()
 *
 * Arguments: time to sleep in seconds
 *
 * Return:  Exits on error
 *
 */
static void 
clstat_sleep(const int time)
{
    struct timeval tout;
    tout.tv_sec = time;
    tout.tv_usec = 0;
    if (select (0, NULL, NULL, NULL, &tout) == -1)
    {
      if (EINTR != errno)
      {
        exit (-1);       /* exits */
      }
    }
    return;
}

/*
 * Name:     register_with_clinfo
 *
 * Description: This routine registers or unregisters for event 
 *		notifications from cinfo.
 *            This is called when the cluster has gone stable.
 *
 * Arguments: action - either 0 to unregister or 1 to register
 * 		cluster struct - used to send the cluster id to clinfo
 *
 * Return:    return code from cl_registereventnotify as long as its
 *            0, otherwise this routine exits
 *
 */
static int
register_with_clinfo(const int action, const struct cl_cluster *clstr_buf)
{
    struct cli_enr_req_t en_req;  /*  Event notification request  */
    int ret_code;

    memset(&en_req, 0, sizeof(struct cli_enr_req_t));
    en_req.event_id = CL_ALL;
    en_req.cluster_id = clstr_buf->clc_clusterid;
    en_req.net_id = -1;
    en_req.signal_id = SIGUSR1;

    if (action) { 	/* register */
        /* register the signal hander  */
        (void) sigset(SIGUSR1, sig_handler);  
    
#ifdef DEBUG
        fprintf(stderr, "register_with_clinfo: registering with clinfo\n");
#endif
        /* contact clinfo to subscribe for events */
        if(ret_code = cl_registereventnotify(1, &en_req)!=CLE_OK )
        {
            fprintf(stderr, "clstat: cl_registereventnotify failed with error %d.",
		ret_code);
            exit(1);
        }

    } else {	/* unregister */
#ifdef DEBUG
        fprintf(stderr, "register_with_clinfo: UN registering with clinfo\n");
#endif
        if ( (ret_code = cl_unregistereventnotify(1,&en_req))!=CLE_OK)
        {
            fprintf(stderr, "clstat: cl_unregistereventnotify failed with error %d.",
		ret_code);
            exit(1);
        }
        sigignore(SIGUSR1); /* reset handler for now */
        clinfo_signal=0;
    }



    return(ret_code);
}

/*
 * Name:     ifs_is_shared
 *
 * Description: This routine is used to avoid displaying shread interfaces
 *            twice in the clstat display(s). We cannot simply rely on the
 *            interface role, since service labels on aliasing networks can
 *            have role == service yet have an entry for each node
 *	      like a shread label.
 *
 * Arguments: the global node map and number of nodes
 *            the particular interface to test for
 *
 * Return:    0 - interface is not shared
 *            1 - interface is on more than one node and is therefore shared
 *
 */
int 
ifs_is_shared(struct cl_node *nodemap, int num_nodes, struct cl_netif *cln_if)
{
  int i,j;
  int found = 0;

  for (i=0; i<num_nodes; i++)
  {
    for(j=0; j<nodemap[i].cln_nif; j++)
    {
	if (!strcmp(nodemap[i].cln_if[j].cli_name, cln_if->cli_name) &&
		nodemap[i].cln_if[j].cli_interfaceid == cln_if->cli_interfaceid)
	{
	    /* interface is the same. If it has been found already on another
	     * node, our work is done, otherwise just memeber it found and 
	     * continue
	     */
	    if (found) return(1);
	    found = 1;
	}
    }
  }
  return(0);
}

/*
 * Program:   clstat.c - HA6000 cluster status monitor
 *
 * Name:     main
 *
 * Description: This is the main function of clstat.  It will parse and check
 *            all the options and data input at start-up.  If clstat is being
 *            run interactively, it will branch, calling inter_act_stat.  In
 *            automatic mode, auto_stat will be called.
 *
 * Arguments: int argc   - argument count
 *            char **argv - input arguments and options
 *
 * Return:    int return - 0 success, ERROR (-1) unsuccessful
 *
 * Global Data Affected: NONE
 */

int TTYmain(int argc, char **argv)
{
  extern char    *optarg;
  extern int      optind;
  
  int            c;
  int            ret;
  int	         cluster_number     = -1;
  int            moreHelpFileToRead =  1;
  char           *cluster_name      = (char *)NULL;
  char *         fgetsReturnPtr     = (char *) NULL;
  char *         helpFileName       = "/usr/es/sbin/cluster/etc/clstat_cldump.help";
  char           helpBuffer [2048];
  char           print_filename[40];
  char 		 *p;
  FILE *         helpFile           = (FILE *) NULL;

  if (progname = strrchr(argv[0], '/'))
    progname++;
  else
    progname = argv[0];
 
#ifndef __LINUX__

  (void) setlocale(LC_ALL, "");
  p = setlocale(LC_ALL, 0);

  /* check for "en" or "En", error if not found
   */
  if(!(((*p == 'E' || *p == 'e') && *++p == 'n') || *p == 'C')) {
    printf(MSGSTR(CLSTAT_UNSUPPORTED,
	    "ASCII mode is not supported in languages other than English\n"));
    exit(-1);
  }

  if(TERM = getenv("TERM"))
    {
      tgetent(tx, TERM);
      CR = tgetstr("cr", &stx_ptr);
      UP = tgetstr("up", &stx_ptr);
      CE = tgetstr("ce", &stx_ptr);
      CL = tgetstr("cl", &stx_ptr);
    }
#endif
  
  (void) sigset(SIGCONT, sig_handler);
  (void) sigset(SIGUSR1, sig_handler);  
  

  wait_time = 1;
  rflag = 0;
  
  while ((c = getopt(argc, argv, "oaishc:n:r:")) != EOF)
    {
      switch (c)
        {
        case 'a':
	  break;
        case 'o':
          ONCE = 1;
          break;
        case 'i':
	  INTERACT = 1;
	  break;
	case 's':
	  ALLSERVICES = 1;
	  break;
        case 'c':
          if((strncmp(optarg, "0", strlen(optarg))) == 0)
	    {
	      cluster_number = 0;
	    }
	  else
	    {
              if((cluster_number = atoi(optarg)) == 0)
		{
                  printf(MSGSTR(CLSTAT_NEED_ID, 
                         "\n\nThe cluster ID (#) must be provided\n\n"));
		  usage();
		  exit(-1);
		}
	    }
	  CLUSTERED = 1;
	  break;
        case 'n':
          cluster_name = optarg;
	  CLUSTERNAMED = 1;
	  break;
	case 'r':
          rflag = 1;
          if((wait_time = atoi(optarg)) == 0)
	    {
              printf(MSGSTR(CLSTAT_NEED_WAIT, 
                     "\n\nThe wait time (#) must be provided\n\n"));
	      usage();
	      exit(-1);
	    }
	  if(wait_time > 30)
	    {
              printf(MSGSTR(CLSTAT_REFRESH_RESET, 
                     "\n\nThe refresh time has been reset to 1 second\n\n"));
	      sleep(3);
	      wait_time = 1;
	    }
	  break;
        case 'h':
          helpOptionSpecified=1;

          break;
        case '?':
          printf(MSGSTR(CLSTAT_INVALID_OPT, "Invalid option selected\n\n"));
	  usage();
	  exit(-1);
        }
    }

  if(optind != argc)
    {
      printf(MSGSTR(CLSTAT_EXTRA_ARGS, 
             "\n\nThere are extra arguments provided\n\n"));
      usage();
      exit(-1);
    }

  /*
   *  - if the help option is specified (-h)
   *     - generate the checklist to establish 
   *       proper functionality and exit with 0.
   */ 
  if (helpOptionSpecified == 1)
  {
     /*
      *  - Open the help file
      */
     helpFile = fopen (helpFileName, "r");

     if (helpFile != NULL)
     {
        /*
         * - Read in each line of the help file,
         *   and display to standard out
         * - exit with zero return code
         */
        while (moreHelpFileToRead)
        {
           fgetsReturnPtr = fgets (helpBuffer,
                                   sizeof (helpBuffer),
                                   helpFile);
           if (fgetsReturnPtr != NULL)
           {
              /*
               * Print the line to standard out
               */
              fprintf (stdout, helpBuffer);
           }
           else
           {
              /*
               * Indicate the processing 
               * of the help file is complete
               */
              moreHelpFileToRead = 0;
           }
        }

        /*
         * - Close the help file
         * - Exit with return code 0
         */
        (void) fclose (helpFile);
        exit (0);
     }
     else
     {
        /*
         * - The help file could not be opened
         *   so generate an error message
         * - Exit with return code 1 error
         */
        printf (MSGSTR (CLSTAT_NO_HELP_FILE,
                        "\n\nThe %s help file could not be opened.\n\n"),
                        helpFileName);
        exit (1);
     }
    
  }

  
#ifdef __LINUX__
  /* linux does not support curses - ignore all other options than -o */
  ONCE=1;
#else
  if(INTERACT && CLUSTERED)
    {
      printf(MSGSTR(CLSTAT_ID_MODE, 
             "\n\nCluster ID is only used in non_interactive mode\n\n"));
      usage();
      exit(-1);
    }
  if(INTERACT && CLUSTERNAMED) 
    {
      printf(MSGSTR(CLSTAT_NAME_MODE, 
             "\n\nCluster Name is only used in non_interactive mode\n\n"));
      usage();
      exit(-1);
    }
  if(CLUSTERED && CLUSTERNAMED)
    {
      printf(MSGSTR(CLSTAT_NAME_ID, 
             "\n\nCluster Name can not be used with Cluster ID\n\n"));
      usage();
      exit(-1);
    }
#endif
   
  if((ret=cl_initialize()) != CLE_OK)
    {
      /* No need to issue error message here because cl_initialize does */
      exit(-1);
    }
 
#ifndef __LINUX__
  if(INTERACT)
    inter_act_stat();
  else
#endif
    auto_stat(cluster_number, cluster_name);
  
}


/*
 * Name:  HTMLmain
 *
 * Description:  This is the main function for the web-based interface to
 *               clstat.  It will print out HTML formatted cluster status
 *               information for each cluster found via clinfo.  This function
 *               is used when clstat is called via it's CGI/HTML interface,
 *               "clstat.cgi".  A webserver can be setup to access clstat.cgi
 *               and display the HTML information to a web browser via a CGI
 *               access request.
 *
 * Arguments:  None.
 *
 * Return:     0 success, -1 error
 *
 * Global Data Affected:  HTMLmode
 */
int HTMLmain(void)
{
  int ret;
  struct cl_cluster *cluster_map;
  struct cl_node *node_map;
  struct cl_group *group_map;
  int num_clusters, num_nodes, num_groups;
  char *nav_header, tmp[MAXNAMELEN + 50];
  int i, j, k;
  char time_buf[TIME_BUF_SIZE] = {0};
  time_t the_time;
  int tnode, tif;
  char str_ip[MAXIPLEN];
  const char* str_ip_ptr = NULL;

  
  HTMLmode = 1;

  /* Print out HTTP headers for caching control.  If supported by the
   * web browser, these options should cause the browser not to store
   * the output of this page in it's local cache.  We want to make sure
   * that the client browser always loads the latest version of the
   * web page.
   */
  printf("Expires: 0\r\n"
	 "Pragma: no-cache\r\n"
	 "Cache-Control: no-cache\r\n");

  /* Print the MIME type header.  The double newline is used to tell the
   * web server and web browser where the HTTP headers end and the page
   * content begins.
   */
  printf("Content-type: text/html\r\n\r\n");

  /* Print the top of the HTML page, including the JavaScript required to
   * auto-refresh the page.
   *
   * Explantation of the JavaScript:
   * Set a timer to execute location.reload() after a certain period.
   * This will auto-reload the page from the webserver.
   * There's a bug in Microsoft Internet Explorer that does not
   * correctly reload the page if there is an anchor reference
   * in the URL.  So we have force the page to scroll to the correct
   * point by setting the location.hash value to the current location.hash
   * value, which is the anchor reference.  However if we did this
   * for Netscape Navigator as well, then a bug shows up in Netscape
   * that causes the page to be constantly refreshed every split second.
   * This bug also appears in IE 5.5, so we only execute this for IE
   * less than or greater than 5.5.
   * The refresh interval shall be 30 seconds unless the user has overriden
   * this by setting CLSTAT_CGI_REFRESH.
   */
  if ((nav_header=getenv("CLSTAT_CGI_REFRESH")) != (char *)NULL)
      i = atoi(nav_header);
  else
      i = 30;

  i = i * 1000;	/* browser deals in mirco */
    
  printf("<HTML>\n"
	 "<HEAD>\n"
	 "<TITLE>clstat - HACMP Cluster Status Monitor</TITLE>\n"
	 "</HEAD>\n"
	 "\n"
	 "<SCRIPT LANGUAGE=\"JavaScript\">\n"
	 "timerID = setTimeout(\"location.reload()\", %d);\n"
	 "if (navigator.appName == \"Microsoft Internet Explorer\" && navigator.userAgent.indexOf(\"MSIE 5.5\") == -1) {\n"
	 "  location.hash = location.hash;\n"
	 "}\n"
	 "</SCRIPT>\n"
	 "\n"
	 "<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#0000FF\">\n"
	 "<FONT SIZE=\"-1\" FACE=\"arial,helvetica\">\n"
	 "<CENTER>\n",
	 i
	 );

  if ((ret = cl_initialize()) != CLE_OK) {
    printf(MSGSTR(CLSTAT_HTML_INIT_FAILED,
		  "<B>Could not initialize clinfo connection.</B>\n"));
    printf("</BODY>\n"
	   "</HTML>\n");
    exit(-1);
  }

  if (cl_alloc_clustermap(&cluster_map) == -1) {
    printf(MSGSTR(CLSTAT_HTML_CLUSTER_MAP_ALLOC_ERROR,
		  "<B>Could not allocate memory for cluster map.</B>\n"));
    printf("</BODY>\n"
	   "</HTML>\n");
    exit(-1);
  }

  num_clusters = cl_getclusters(cluster_map);

  if (num_clusters <= 0) {
    printf(MSGSTR(CLSTAT_HTML_CHECK_CLINFO_1,
		  "<B>Failed retrieving cluster information.<BR><BR>\n\
There are a number of possible causes:<BR>\n\
clinfoES or snmpd subsystems are not active.<BR>\n\
snmp is unresponsive.<BR>\n\
snmp is not configured correctly.<BR>\n\
Cluster services are not active on any nodes.<BR><BR>\n\
Refer to the HACMP Administration Guide for more information.<BR></B>\n"));
    printf("</BODY>\n"
	   "</HTML>\n");
    cl_free_clustermap(cluster_map);
    exit(-1);
  }

  /* Allocate enough space to store the navigational link header for
   * all the clusters found.  The additional 50 characters for each cluster
   * name leaves enough room for the enclosing HTML anchor tags and the
   * cluster ID in string form.
   */
  nav_header = (char *)malloc((MAXNAMELEN + 50) * num_clusters);
  if (nav_header == NULL) {
    printf(MSGSTR(CLSTAT_HTML_NAV_ALLOC_ERROR,
		  "<B>Could not allocate memory for navigation.</B>\n"));
    printf("</BODY>\n"
	   "</HTML>\n");
    cl_free_clustermap(cluster_map);
    exit(-1);
  }
  nav_header[0] = '\0';
  for (i = 0; i < num_clusters; i++) {
    sprintf(tmp, "<A HREF=\"#%d\">%s</A>", cluster_map[i].clc_clusterid,
	    cluster_map[i].clc_name);
    strcat(nav_header, tmp);
    /* Print some formatted spacing between cluster names. */
    if (i != num_clusters - 1)
      strcat(nav_header, " | ");
  }

  /* Start the loop through each cluster.  For each cluster, print the cluster
   * status table, then loop through each node in the cluster, and loop
   * through all the interfaces on each node.
   */
  for (i = 0; i < num_clusters; i++) {
    if (cl_alloc_nodemap(&node_map) == -1 ||
        cl_alloc_groupmap(&group_map) == -1) {
      printf(MSGSTR(CLSTAT_HTML_NODE_MAP_ALLOC_ERROR,
		    "<B>Could not allocate memory for node map.</B>\n"));
      printf("</BODY>\n"
	     "</HTML>\n");
      free(nav_header);
      cl_free_clustermap(cluster_map);
      exit(-1);
    }
    num_nodes = cl_getnodemap(cluster_map[i].clc_clusterid, node_map);

    the_time = time(0);
    fmt_date(&the_time, time_buf, TIME_BUF_SIZE);
    printf("<A NAME=\"%d\"></A>\n", cluster_map[i].clc_clusterid);
    printf("%s\n", nav_header);
    printf("<P>\n");
    /* print cluster name, id and time */
    printf("<TABLE CELLSPACING=2 CELLPADDING=2 BORDER=0>\n"
	   "<TR>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">Cluster:</FONT></TD>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s (%d)</B></FONT></TD>\n"
	   "</TR>\n",
	   cluster_map[i].clc_name, cluster_map[i].clc_clusterid);
    printf("<TR>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">Last Update:</FONT></TD>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s</B></FONT></TD>\n"
	   "</TR>\n",
	   time_buf);


    /* cluster state */
    printf("<TR>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">State:</FONT></TD>\n"
	   "<TD><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0><TR><TD BGCOLOR=%s><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s</B></FONT></TD></TR></TABLE></TD>\n"
	   "</TR>\n",
	   HTML_state_color(cluster_map[i].clc_state),
	   get_state(cluster_map[i].clc_state));

    printf("<TR>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">SubState:</FONT></TD>\n"
	   "<TD><TABLE CELLSPACING=0 CELLPADDING=0 BORDER=0><TR><TD BGCOLOR=%s><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>",
	   HTML_substate_color(cluster_map[i].clc_substate));
    /* If the substate is ERROR, have the text blink.  Note that blinking
     * text is only supported in Netscape Navigator.
     */
    if (cluster_map[i].clc_substate == CLSS_ERROR) {
      printf("<BLINK>%s</BLINK>", get_substate(cluster_map[i].clc_substate));
    }
    else {
      printf("%s", get_substate(cluster_map[i].clc_substate));
    }
    printf("</B></FONT></TD></TR></TABLE></TD>\n"
	   "</TR>\n");

    printf("<TR>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">Nodes:</TD>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%d</B></FONT></TD>\n"
	   "</TR>\n"
	   "</TABLE>\n",
	   num_nodes);

    /* print a header */
    printf("<P>\n"
	   "<TABLE CELLSPACING=2 CELLPADDING=2 BORDER=0>\n"
	   "<TR>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">Node</FONT></TD>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">State</FONT></TD>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">Interface</FONT></TD>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">Address</FONT></TD>\n"
	   "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">State</FONT></TD>\n"
	   "</TR>\n");

    /* Loop through each node in the cluster. */
    for (j = 0; j < num_nodes; j++) {
      /* Loop through each interface defined on the current node. */
      for (k = 0; k < node_map[j].cln_nif; k++) {
	if (k == 0) {
	  printf("<TR>\n");
	  /* We conserve webpage space by printing the name and state of the
	   * node on same line as the first interface defined on that node.
	   */
	  printf("<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s</B></FONT></TD>\n"
		 "<TD><TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0><TR><TD BGCOLOR=%s><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s</B></FONT></TD></TR></TABLE></TD>\n",
		 node_map[j].cln_nodename,
		 HTML_state_color(node_map[j].cln_state),
		 get_state(node_map[j].cln_state));
	}
	/* first print all the boot and standby on this node */
	if ((node_map[j].cln_if[k].cli_role != CL_INT_ROLE_BOOT &&
             node_map[j].cln_if[k].cli_role != CL_INT_ROLE_STANDBY))
            continue;

	printf("<TR>\n");
	if (k) {
	  /* We have to print filler table data fields if this is not the first
	   * interface defined on the node.  This takes the place of the
	   * node name and state fields that appear on the first interface
	   * row.
	   */
	  printf("<TD></TD>\n"
		 "<TD></TD>\n");
	}

	bzero(str_ip, sizeof(str_ip));
    switch(((struct sockaddr*)&node_map[j].cln_if[k].cli_addr6)->sa_family)
    {
    case AF_INET:
    	str_ip_ptr = inet_ntop(AF_INET, 
    			&((struct sockaddr_in*)&node_map[j].cln_if[k].cli_addr6)->sin_addr,
    			str_ip,
    			sizeof(str_ip));
    	break;
    case AF_INET6:
    	str_ip_ptr = inet_ntop(AF_INET6, 
    			&((struct sockaddr_in6*)&node_map[j].cln_if[k].cli_addr6)->sin6_addr,
    			str_ip,
    			sizeof(str_ip));
    	break;
    }
 	/* print the interface info - html style */
	printf("<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s (%d)</B></FONT></TD>\n"
	       "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s</B></FONT></TD>\n"
	       "<TD><TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0><TR><TD BGCOLOR=%s><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s</B></FONT></TD></TR></TABLE></TD>\n"
	       "</TR>\n",
	       node_map[j].cln_if[k].cli_name,
	       node_map[j].cln_if[k].cli_interfaceid,
	       str_ip,
	       HTML_state_color(node_map[j].cln_if[k].cli_state),
	       get_state(node_map[j].cln_if[k].cli_state));

      } /* for all interfaces on this node */

      /* we have displayed the information for all the "local" interfaces -
	   search for (and display) any service addresses also on this node */
      for(tnode=0; tnode < num_nodes; tnode++)
      {
          for(tif=0; tif<node_map[tnode].cln_nif; tif++)
          {
              /* only do service - all others were printed above */
              if ((node_map[tnode].cln_if[tif].cli_role == CL_INT_ROLE_BOOT ||
                   node_map[tnode].cln_if[tif].cli_role == CL_INT_ROLE_STANDBY))
                  continue;

               /* is it up (active) on this node ? */
               if (node_map[tnode].cln_if[tif].cli_active_nodeid ==
                                                       node_map[j].cln_nodeid)
               {

                   /* its active on this node, but only display
                      once if its shared. */
                    if (ifs_is_shared(node_map, num_nodes,
                                      &node_map[tnode].cln_if[tif]) &&
                                      tnode != j)
                        continue;

		    printf("<TR>\n");
	  	    printf("<TD></TD>\n"
		 	    "<TD></TD>\n");

	  		bzero(str_ip, sizeof(str_ip));
	  	    switch(((struct sockaddr*)&node_map[tnode].cln_if[tif].cli_addr6)->sa_family)
	  	    {
	  	    case AF_INET:
	  	    	str_ip_ptr = inet_ntop(AF_INET, 
	  	    			&((struct sockaddr_in*)&node_map[tnode].cln_if[tif].cli_addr6)->sin_addr,
	  	    			str_ip,
	  	    			sizeof(str_ip));
	  	    	break;
	  	    case AF_INET6:
	  	    	str_ip_ptr = inet_ntop(AF_INET6, 
	  	    			&((struct sockaddr_in6*)&node_map[tnode].cln_if[tif].cli_addr6)->sin6_addr,
	  	    			str_ip,
	  	    			sizeof(str_ip));
	  	    	break;
	  	    }
 		    /* print the interface info - html style */
		    printf("<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s (%d)</B></FONT></TD>\n"
	       	   	     "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s</B></FONT></TD>\n"
	       	   	     "<TD><TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0><TR><TD BGCOLOR=%s><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s</B></FONT></TD></TR></TABLE></TD>\n"
	       	   	     "</TR>\n",
	       	   	     node_map[tnode].cln_if[tif].cli_name,
	       	   	     node_map[tnode].cln_if[tif].cli_interfaceid,
	       	   	     str_ip,
	   	             HTML_state_color(node_map[tnode].cln_if[tif].cli_state),
               		     get_state(node_map[tnode].cln_if[tif].cli_state));
 
		} /* active on this node */
	    } /* for tif */
      } /* for tnodes */	
    
      /* all interfaces on this node have been displayed, now search for */
      /* any resource groups 					         */
      num_groups = cl_getgroupmap(cluster_map[i].clc_clusterid, group_map);
      if (num_groups > 0)
      {
      	    /* Loop through each group in the cluster. */
            for (k = 0; k < num_groups; k++) 
	    {
	        if (notOfflineGroupState(&group_map[k], 
					 node_map[j].cln_nodeid)) 
	        {
                    javaGroupInfo(&group_map[k],
                                   node_map,
                                   node_map[j].cln_nodeid);
                } /* not offline */

            } /* for all groups */
      } /* if there are groups to display */

      if (j != num_nodes - 1) {         /* add space between nodes */
        printf("<TR><TD HEIGHT=5></TD></TR>\n");

      }
      
    } /* for all nodes in this cluster */
    printf("</TABLE>\n");

    if (i != num_clusters - 1) {
      printf("<P>\n"
	     "<HR WIDTH=60%%>\n"
	     "<P>\n");
    }

  } /* for all clusters */

  printf("</BODY>\n"
	 "</HTML>\n");

  cl_free_groupmap(group_map);
  cl_free_nodemap(node_map);
  cl_free_clustermap(cluster_map);

  exit(0);
}


#ifndef __LINUX__
/*
 * Name:      inter_act_stat
 *
 * Description: This function is called if clstat is run interactively.  It
 *            will call the clinfo API to find all accessible clusters and 
 *            display them in a list on the screen.  All action from this
 *            screen is initiated by user input, with get_cluster_info called
 *            for a detailed display of a cluster status.
 *
 * Arguments: NONE
 *
 * Return:    0 complete
 *
 * Global Data Affected: NONE
 */

int inter_act_stat()
{
  int     cluster_cnt;
  int     i;
  int     the_nbr;
  int     loop = 1;
  char    re_spond[80];
  struct cl_cluster *cluster_map;
  char    quit = 'q';

  if (cl_alloc_clustermap(&cluster_map) == -1)
  {
    printf("Could not allocate memory for cluster_map.\n");
    exit(-1);
  }
  resetClustermap(cluster_map);
  while (re_spond[0] != quit)
    {
      memset(re_spond, '\0', sizeof(re_spond));
      redisplay = 0;
      tputs(CL, 1, term_dumb);

      printf(MSGSTR(CLSTAT_STAT_MON, 
             "\n\t\t%s - HACMP Cluster Status Monitor\n"), progname);
      printf("\t\t-------------------------------------\n\n");
      
      cluster_cnt = cl_getclusters(cluster_map);
      
      if(cluster_cnt <= 0)
        printf(MSGSTR(CLSTAT_NO_ACTIVE, 
               "\n\n\t\tTHERE ARE NO CLUSTERS CURRENTLY ACTIVE\n\n\n"));
      else
        {
          printf(MSGSTR(CLSTAT_ACTIVE_NUM, "Number of clusters active: %d\n\n"), 
                 cluster_cnt);
          printf(MSGSTR(CLSTAT_MSG_20, "\t\tID\tName\t\tState\n\n"));

	  for(i=0; i < cluster_cnt; i++)
	    printf("%18d %9s %15s\n", cluster_map[i].clc_clusterid,
		   cluster_map[i].clc_name,
		   get_state(cluster_map[i].clc_state)); 
        }

      printf(MSGSTR(CLSTAT_SELECT_OPT, "\n\n\nSelect an option:\n"));
      printf("\t# - the Cluster ID \t\t\t%c- quit\n\n",'q');
      
      loop = 1;
      while(loop)
        {
	  gets(re_spond);
	  
	  if(re_spond[0] == 'q')
            {
	      loop--;
	      exit(0);
            }
	  
	  if(re_spond[0] == '?')
            {
	      show_commands(0);
	      loop--;
            }
	  
	  else if(re_spond[0] != '\0'  &&  ! redisplay)
            {
	      if(cluster_cnt > 0)
                {
                  the_nbr = atoi(re_spond); 
		  
		  for(i=0; i<cluster_cnt; i++)
                    {
		      if(the_nbr == cluster_map[i].clc_clusterid)
                        {
			  get_cluster_info(cluster_map[i].clc_clusterid);
			  loop--;
			  break;
                        }
                    }

		  if(i == cluster_cnt)
                    {
                      printf(MSGSTR(CLSTAT_VALID_ID, 
                             "\tThe # must be a valid Cluster ID,  (%S)\n"),
			     re_spond);
		      tputs(UP, 1, term_dumb);
		      tputs(UP, 1, term_dumb);
		      tputs(CE, 1, term_dumb);
		      tputs(UP, 1, term_dumb);
		      printf("\n");
                    }
                }  
            }  
	  else 
            loop--;
        }
    }

    cl_free_clustermap(cluster_map);
}
    
#endif

/*
 * Name:      auto_stat
 *
 * Description: This function is called if clstat is being run in automatic
 *            mode.  If there is no cluster ID provided, (the function) 
 *            find_cluster will be called.  When a single cluster ID is
 *            identified, the function get_cluster_info is called until the 
 *            user cancels clstat (with "^C").  If it is called with a cluster
 *            name, it will use cl_getclusterid to find the cluster id.
 *
 * Arguments: int cluster_id 
 *            char* cluster_name
 *
 * Return:    int retrn - 0 success, ERROR (-1) unsuccessful
 *
 * Global Data Affected: NONE
 */

int
auto_stat(int cluster_id, char *cluster_name)
{

int        forever = 1;

if ((char *)NULL != cluster_name)
    {
    cluster_id = cl_getclusterid (cluster_name);
    if (cluster_id < 0)
        {
        printf ("%s: cl_getclusterid (%s): %s\n", progname,
                  cluster_name, cl_errmsg(cluster_id));
	    /* printf("Cluster name %s is not valid.\n", cluster_name); */
        usage();
        exit (-1);
	}
    }

if(cluster_id < 0)
    {
    cluster_id = find_cluster();
    if(cluster_id == ERROR)
        exit(ERROR);
    }

while(forever) 
    {
    get_cluster_info(cluster_id);

    /* Added during 8 node tty update */
    exit(0);
    }

}


/*
 * Name:      sig_handler
 *
 * Description: Catches signal and if no signal pending sets cm_signal.  Signal
 *            are processed in the function do_complete_cycle().
 *
 * Arguments: int sig - interupt signal
 *
 * Return:    NONE
 *
 * Global Data Affected: NONE
 */


void
sig_handler (sig)
int sig;
{

int   progpid;

switch(sig)
    {
    case SIGCONT:
        redisplay++;
#ifdef DEBUG
        fprintf(stderr, "clinfoES: caught SIGCONT signal\n");
#endif
        break;   
    case SIGUSR1:
	/* this flag tells the main loop to refetch data from clinfo */
        clinfo_signal++;
#ifdef DEBUG
        fprintf(stderr, "clinfoES: caught SIGUSR1 signal, count now %d\n", clinfo_signal);
#endif
	break;
    case SIGINT:
	/* Inactive */
        fprintf(stderr, "clstat: caught SIGINT, exiting\n");
        progpid = getpid();
        exit(-1);
        break;   
    case SIGTSTP:
	/* Inactive */
        fprintf(stderr, "clstat: caught SIGTSTP\n");
        break;   
    default:
        fprintf(stderr, "clstat: caught unknown signal %d\n", sig);
        break;
    }
}



/*
 * Name:      find_cluster
 *
 * Description: In atuomatic mode, if the cluster ID is not provided at startup,
 *            this function is called to find the first cluster accessible to
 *            the system.  The function will loop until a cluster is found.  
 *            If the first loop does not find a cluster, a message is displayed
 *            on the screen.  If more than one cluster is found the function 
 *            returns ERROR which will terminate the program.
 *
 * Arguments: NONE
 *
 * Return:    int clid - the cluster ID
 *
 * Global Data Affected: NONE
 */

#define NO_CLID	-2

int
find_cluster()
{

int               clid = NO_CLID;
int               number_clstrs;
int               first = 1;
struct cl_cluster *cluster_map;
FILE* fp;
char *returnval=NULL;
char check_main_mib[512]="grep \"^VACM_VIEW *defaultView *internet.*- *included *-\" /etc/snmpdv3.conf 2>/dev/null";
char check_sub_mib[512]="grep \"^VACM_VIEW *defaultView *1.3.6.1.4.1.2.3.1.2.1.5 *- *included *-\" /etc/snmpdv3.conf 2>/dev/null";
char output_array[512];
char search_string[15]="VACM_VIEW";

    if (cl_alloc_clustermap(&cluster_map) == -1)
    {
      printf("Could not allocate memory for cluster_map.\n");
      exit(-1);
    }

    while(clid == NO_CLID)
    {
        number_clstrs = cl_getclusters(cluster_map);

	if (number_clstrs < 0)
	{
            printf(MSGSTR(CLSTAT_CHECK_CLINFO_1, "Failed retrieving cluster information.\n\n\
There are a number of possible causes:\n\
clinfoES or snmpd subsystems are not active.\n\
snmp is unresponsive.\n\
snmp is not configured correctly.\n\
Cluster services are not active on any nodes.\n\n\
Refer to the HACMP Administration Guide for more information.\n"));

#ifdef SNMP_CONF_CHECK
            output_array[0]=0;
            fp = popen(check_main_mib,"r");
            if(fp == NULL) {
                clid = ERROR;
                return(ERROR);
            }
            else {
                returnval = fgets(output_array,512,fp);
                if (returnval != NULL) {
                    /* Entry found so return */
                    pclose(fp);
                    clid = ERROR;
                    return(ERROR);
                }
                    pclose(fp);
                    fp = popen(check_sub_mib,"r");
                    if(fp == NULL) {
                        clid = ERROR;
                        return(ERROR);
                    }
                    else {
                        returnval = NULL;
                        output_array[0]=0;
                        returnval = fgets(output_array,512,fp);
                        if (returnval != NULL) {
                            pclose(fp);
                            clid = ERROR;
                            return(ERROR);
                        }
                            printf(MSGSTR(CLSTAT_README_MSG, "\nInternet MIB tree not enabled. Please refer the PowerHA \nreadme file for more information on how to enable it.\n"));
                            pclose(fp);
                     }
             }
#endif

	    clid = ERROR;
	    return(ERROR);
	}
	
        switch(number_clstrs)
            {
            case 0:
                if(first)
                {
                    first--;
#ifndef __LINUX__
                    tputs(CL, 1, term_dumb);
#endif
                    printf(MSGSTR(CLSTAT_STAT_MON, 
                       "\t\t%s - HACMP Cluster Status Monitor\n"), progname);
                    printf("\t\t-------------------------------------\n");
                    printf(MSGSTR(CLSTAT_NO_ACTIVE, 
                           "\n\n\n\t\t\tTHERE ARE NO ACTIVE CLUSTERS\n\n\n"));
                    printf(MSGSTR(CLSTAT_SEARCH, 
                           "\n\t\tTHE PROGRAM WILL CONTINUE SEARCHING FOR ONE\n\n"));
                }
		break;
	    case 1:
		clid = cluster_map[0].clc_clusterid;
		break;
	    default:
                printf(MSGSTR(CLSTAT_MORE_ACCESS, 
                               "\n\nThere is more than one cluster accessible.\n"));
                printf(MSGSTR(CLSTAT_PROVIDE1, 
                       "Either provide the cluster to look for, or run\nthe program interactively (or try again).\n\n"));
		usage();
		clid = ERROR;
		return(ERROR);
		break;
	    }
    }
    cl_free_clustermap(cluster_map);
    return(clid);
}


/*
 * Name:      get_cluster_info
 *
 * Description: This function performs the loop that calls the clinfo APIs
 *            to obtain cluster information and display on the screen.  If 
 *            clstat is in automatic mode, the function will wait the specified
 *            time in each loop.  In interactive mode the function will call 
 *            get_clstr_rspns (function) to determine the user input and process
 *            that.  
 *
 *            Changed by SAM, 7-25-94, for Fallwell.  clstat now functions
 *            the same in interactive mode and auto mode except that when 
 *            you quit, you get the interactive response instead of exiting
 *            clstat.
 *
 * Arguments: int clstr_ID - ID of cluster being monitored
 *
 * Return:    0            - complete
 *
 * Global Data Affected: NONE
 */


int get_cluster_info(int clstr_ID)

{
int               result, i, j, update, refresh, numStars, status;
struct cl_cluster clstr_buf, HOLD_cluster;
struct cl_node    *nodemap;
struct cl_node    *HOLD_NODES;
struct cl_group   *groupmap;
struct cl_group   *HOLD_GROUPS;
int               hold_ID;
time_t            start_time, done_time;
char              BodyTxt[65536];  /* Enough to hold 2 clusters w 8nodes/8 intfcs. */
char              StatusTxt[256];
char              HeaderTxt[1024];
#ifndef __LINUX__
WINDOW            *winBody, *winStatusLine, *winHeader;
#endif
char              command[1], tmp[256], *pc;
int               lineCount = 0;
int               segCount = 0;
int               sizeWinBody = 0;
char       	  time_buf[TIME_BUF_SIZE] = {0}, state_buf[STATE_BUF_SIZE] = {0};
char              temp[256];
int 		  tnode, tif;
char			  str_ip[MAXIPLEN+1];
/* this flag tells the main loop to sample clinfo for updates */
int 		  check_for_updates = 5;

#define WIN_HEADER_SIZE 10  /* Size of cluster status window */

    if (cl_alloc_nodemap(&nodemap) == -1)
    {
      printf("Could not allocate memory for nodemap.\n");
      exit(-1);
    }
    resetNodemap(nodemap);

/* #define DUMP_AND_EXIT   */

#ifdef DUMP_AND_EXIT
    nbr_nodes = cl_getnodemap(clstr_ID, nodemap);
    cl_dump_nodemap(nodemap, nbr_nodes);
    exit(0);
#endif 

    if (cl_alloc_nodemap(&HOLD_NODES) == -1)
    {
      printf("Could not allocate memory for HOLD_NODES.\n");
      exit(-1);
    }
    resetNodemap(HOLD_NODES);

    if (cl_alloc_groupmap(&groupmap) == -1)
    {
      printf("Could not allocate memory for groupmap.\n");
      exit(-1);
    }
    resetGroupmap(groupmap);
    if (cl_alloc_groupmap(&HOLD_GROUPS) == -1)
    {
      printf("Could not allocate memory for HOLD_GROUPS.\n");
      exit(-1);
    }
    resetGroupmap(HOLD_GROUPS);

    bzero(&HOLD_cluster, sizeof(struct cl_cluster));
    done_time = time(0);

    /* This is virtually the main loop of clstat. */

    if (!ONCE)
    {
#ifndef __LINUX__
	initscr();   /* Initialize curses */

        if (cur_term->Lines < 24 || cur_term->Columns < 80)
        {
            /* This keeps the tty from wrapping and looking unreadable */
            printf(MSGSTR(CLSTAT_DISPLAY_SIZE, 
                   "Display must be at least 24 lines by 80 columns\n"));
	    endwin();
	    exit (0);
	}

        /* Set up the curses windows.  winBody holds all cluster info except
         * command status line.  Leave 2 lines at bottom of display for this line. 
         * winStatusLine holds the b-back, f-forward, q-quit status line.
         * winHeader holds the cluster status info.
         */

        winHeader = newwin (WIN_HEADER_SIZE, 0, 0, 0);
        sizeWinBody = cur_term->Lines - 2 - WIN_HEADER_SIZE;
        winBody = newwin (sizeWinBody, 0, WIN_HEADER_SIZE, 0);
        winStatusLine = newwin (1, 0, cur_term->Lines - 1, 0);
    
        if ( winBody == NULL || winHeader == NULL || winStatusLine == NULL )
        {
            printf(MSGSTR(CLSTAT_MEM_PROB, 
	       "Memory problems.  Couldn't create curses window.\n"));
	    endwin();
	    exit (0);
        }

        /* Figure out how to nicely draw the status bar */
        numStars = (cur_term->Columns - 32) / 2;
#else
	numStars = 32;
#endif
        memset (StatusTxt, '\0', sizeof(StatusTxt) );
        for (i = 0; i < numStars; i++)
        {
	    strcat(StatusTxt, "*");
        }
        sprintf(temp, " %c/forward, %c/back, %c/refresh, %c/quit \0",'f','b','r','q');
        strcat(StatusTxt, temp);
        for (i = 0; i < numStars; i++)
        {
            strcat(StatusTxt, "*");
        }

#ifndef __LINUX__
        mvwaddstr(winStatusLine, 0, 0, StatusTxt);
        nodelay(winStatusLine, TRUE);
        noecho();
        cbreak();
#endif
    }

    command[0] = '\0';

    while (0 != strcmp(command, "q"))
    {
	struct cli_en_msg_t  en_msg;
	int last_update;

        start_time = time(0);

	/* check for events available from clinfo */
   	if (clinfo_signal) {
#ifdef DEBUG
            fprintf(stderr, "main loop: signal count %d\n", clinfo_signal);
#endif
	    /* read events even though we dont actually do anything with it */
 	    while (clinfo_signal--) {
		int rc = cl_getevent(&en_msg);
#ifdef DEBUG
        	fprintf(stderr, "main loop: cl_getevent returned %d\n", rc);
#endif
	    }
	    /* unregister for event notifications */
	    register_with_clinfo(0, &clstr_buf);

	    /* make sure we sample a few more time before going back to */
	    /* signal mode */
	    check_for_updates = 5;
	}

#ifdef DEBUG
        fprintf(stderr, "main loop: check_for_updates is %d\n",check_for_updates);
#endif
	if (check_for_updates) {
            if ((result = cl_getcluster(clstr_ID, &clstr_buf)) != CLE_OK)
            {
                if(INTERACT)
	        {
#ifndef __LINUX__
	            endwin();
#endif
                    return(10);
	        }
                clstr_buf.clc_clusterid = clstr_ID;
                strcpy(clstr_buf.clc_name, HOLD_cluster.clc_name);
                clstr_buf.clc_state = CLS_DOWN;
	        resetNodemap(nodemap);
                nbr_nodes = 0;
	        resetGroupmap(groupmap);
                nbr_groups = 0;
            }
            else
            {
                nbr_nodes = cl_getnodemap(clstr_ID, nodemap);
                if(nbr_nodes < 1 || nbr_nodes > CL_MAXNODES)
                {
                    if(INTERACT)
		    {
#ifndef __LINUX__
		        endwin();
#endif
                        return(10);
		    }
                    nbr_nodes = 0;
                    memset(clstr_buf.clc_primary, '\0', CL_MAXNAMELEN);
                    clstr_buf.clc_state = CLS_DOWN;
		    resetNodemap(nodemap);
                }
                nbr_groups = cl_getgroupmap(clstr_ID, groupmap);
                if(nbr_groups < 1 || nbr_groups > CL_MAXGROUPS)
                {
                    nbr_groups = 0;
		    resetGroupmap(groupmap);
	    	
                }
            }
        
            update=0;
            refresh=0;
    
    
            if (bcmp(&HOLD_cluster, &clstr_buf, sizeof(struct cl_cluster)))
            {
                bcopy(&clstr_buf, &HOLD_cluster, sizeof(struct cl_cluster));
                update = 1;
            }
            else if (0 != compareGroupmap(HOLD_GROUPS, groupmap))
	    {
                HOLD_GROUPS = groupmap;
	        update = 1;
	    }
            else if (0 != compareNodemap(HOLD_NODES, nodemap))
	    {
	        copyNodemap(nodemap, HOLD_NODES);
	        update = 1;
	    }
            else
	    {
	        for (i = 0; i<nbr_nodes; i++)
	        {
	            /* Check for diffs in the interfaces for each node as well */
	            if (0 != compareNetifs(HOLD_NODES[i].cln_if, nodemap[i].cln_if))
		    {
		        copyNodemap(nodemap, HOLD_NODES);
		        update = 1;
		        break;
		    }
	        }
	    }
    
  	    /* if update is not set there were no changes */
	    /* if state is up and stable at this point then we want to stop */
	    /* polling for changes and wait on a signal from clinfo */
	    if (update == 0) 	
	    {
	        check_for_updates--;
		if (check_for_updates == 0) {
#ifdef DEBUG
		fprintf(stderr, "check_for_updates 0, if stable then register\n");
#endif
	        if (clstr_buf.clc_state == CLS_UP &&
		    clstr_buf.clc_substate == CLSS_STABLE)
	        {
		    /* register with clinfo and reset flags which cause polling */
		    register_with_clinfo(1, &clstr_buf);
		    check_for_updates = 0;
		    last_update = 0;
	        }
		/* no changes but cluster unstable - poll a bit longer */
		else check_for_updates = 5;
}
	    }
    
        } else {	/* check_for_updates = 0 */
            update=0;
            redisplay=0;

	    if (last_update++ > 15) {
	    	/* every so often we want to resample cluster state just */
		/* to make sure clinfo is accessable */
#ifdef DEBUG
        	fprintf(stderr, "main loop: last_update fired\n");
#endif
	    	last_update = 0;
            	if ((result = cl_getcluster(clstr_ID, &clstr_buf)) == CLE_OK)
            	{
		    if (clstr_buf.clc_substate != CLSS_STABLE)
		    	check_for_updates = 5;
                }
		else check_for_updates = 5;
		/* setting check_for_updates will cause the entire config */
		/* to be checked next time around */
    	    }
	}
    
        if(redisplay)
        {
            update=1;
            redisplay=0;
        }

        if ( 0 == strcmp(command, "f") || 
	     0 == strcmp(command, "b") ||
	     0 == strcmp(command, "r") )
        {
    	    update = 1;
        }

        if(rflag)
        {
            update = 1;
        }

        if(ONCE || update)
        {
#ifndef __LINUX__
	    if (!ONCE)
	    {
	        werase(winHeader);
	        werase(winBody);
	    }
#endif

	    memset(HeaderTxt, '\0', sizeof(HeaderTxt) ); 
	    memset(BodyTxt, '\0', sizeof(BodyTxt) );

	    lineCount = 0;
	    sprintf(HeaderTxt, "\n"); 
    
            sprintf(tmp, MSGSTR(CLSTAT_STAT_MON,
                    "\t\t%s - HACMP Cluster Status Monitor\n"),
		    progname);
	    strncat(HeaderTxt, tmp, sizeof(tmp) );
            sprintf(tmp,"\t\t-------------------------------------\n");
	    strncat(HeaderTxt, tmp, sizeof(tmp) );
            (void) fmt_date(&start_time, time_buf, TIME_BUF_SIZE);
            sprintf(tmp, "\nCluster: %s\t(%d)\n%s\n", clstr_buf.clc_name,
		    clstr_buf.clc_clusterid, time_buf);
	    strncat(HeaderTxt, tmp, sizeof(tmp) );
            sprintf(tmp, MSGSTR(CLSTAT_MSG_42, "\t\tState: %s\t\tNodes: %d\n"),
                    get_state(clstr_buf.clc_state), nbr_nodes); 
	    strncat(HeaderTxt, tmp, sizeof(tmp) );
	    sprintf(tmp, MSGSTR(CLSTAT_MSG_43, "\t\tSubState: %s\n\n"), 
                    get_substate(clstr_buf.clc_substate));
	    strncat(HeaderTxt, tmp, sizeof(tmp) );
    
            if(clstr_buf.clc_state == CLS_DOWN || clstr_buf.clc_state == CLS_UP)
            {
                for(i=0; i < nbr_nodes; i++)
                {
		    sprintf(tmp, MSGSTR(CLSTAT_MSG_44, "\tNode: %s\t\tState: %s\n"), 
                            nodemap[i].cln_nodename,
			    get_state(nodemap[i].cln_state));       

		    strncat(BodyTxt, tmp, sizeof(tmp) );
		    lineCount++;
		    
		    for(j=0; j<nodemap[i].cln_nif; j++)
                    {
			/* only display boot and standby here (service will
			   be displayed below)	*/
			if (
			  (nodemap[i].cln_if[j].cli_role != CL_INT_ROLE_BOOT &&
			   nodemap[i].cln_if[j].cli_role != CL_INT_ROLE_STANDBY))
			   continue; 

                        sprintf(tmp, MSGSTR(CLSTAT_MSG_45,
                                "\t   Interface: %s (%d)    "),
				nodemap[i].cln_if[j].cli_name, 
				nodemap[i].cln_if[j].cli_interfaceid);
			strncat(BodyTxt, tmp, sizeof(tmp) );
    
                        if(strlen(nodemap[i].cln_if[j].cli_name) < 10)
			{
			    sprintf(tmp, "\t");
			    strncat(BodyTxt, tmp, sizeof(tmp) );
			}
            bzero(str_ip, sizeof(str_ip));
            switch(((struct sockaddr*)&nodemap[i].cln_if[j].cli_addr6)->sa_family)
            {
            case AF_INET:
            	inet_ntop(AF_INET, 
            			&((struct sockaddr_in*)&nodemap[i].cln_if[j].cli_addr6)->sin_addr,
            			str_ip,
            			sizeof(str_ip));
            	break;
            case AF_INET6:
            	inet_ntop(AF_INET6, 
            			&((struct sockaddr_in6*)&nodemap[i].cln_if[j].cli_addr6)->sin6_addr,
            			str_ip,
            			sizeof(str_ip));
            	break;
            }
                       
            sprintf(tmp, MSGSTR(CLSTAT_MSG_46, "\tAddress: %s\n"), str_ip);
			strncat(BodyTxt, tmp, sizeof(tmp) );
			lineCount++;
    
                        sprintf(tmp, MSGSTR(CLSTAT_MSG_47,
                               "\t\t\t\t\t\tState:   %s\n"), 
				get_state(nodemap[i].cln_if[j].cli_state));
			strncat(BodyTxt, tmp, sizeof(tmp) );
			lineCount++;
		    }

		    /* 
		     * We have displayed the all the boot interfaces, now look
		     * for any service labels which may be up on this node.
		     * Note that we look at all services on all nodes since
		     * there could be a takeover label currently on this node.
		     */
                    for(tnode=0; tnode < nbr_nodes; tnode++)
                    {
		        for(tif=0; tif<nodemap[tnode].cln_nif; tif++)
                        {
			  /* only do service - all others were printed above */
			  if (
			  (nodemap[tnode].cln_if[tif].cli_role == CL_INT_ROLE_BOOT ||
			   nodemap[tnode].cln_if[tif].cli_role == CL_INT_ROLE_STANDBY))
			       continue;

			    /* if the service is shared it will show up in the
				interface list for all nodes whereas if its 
				just plain service it will only be in the ifs 
				list for one node  - the trick is to just 
				display it once if its shared */
		            if ((ALLSERVICES && nodemap[tnode].cln_if[tif].cli_nodeid == nodemap[i].cln_nodeid) 
				|| (nodemap[tnode].cln_if[tif].cli_active_nodeid == nodemap[i].cln_nodeid))
			    {

				/* its active on this node or inactive shold 
				   also be displayed, but only display
				   once if its shared. We cant rely on the
				   role of service/shared unfortunately because
				   ipat via aliasing treats both equally - 
				   instead we will look for it on the local
				   node and skip displaying it here */
			       if (ifs_is_shared(nodemap, nbr_nodes,
					&nodemap[tnode].cln_if[tif]) && 
				  tnode != i) 
					continue;

                         	sprintf(tmp, MSGSTR(CLSTAT_MSG_45,
                                    "\t   Interface: %s (%d)    "),
				    nodemap[tnode].cln_if[tif].cli_name,
				    nodemap[tnode].cln_if[tif].cli_interfaceid);
			        strncat(BodyTxt, tmp, sizeof(tmp) );
    
                                if(strlen(nodemap[tnode].cln_if[tif].cli_name) < 10)
			        {
			            sprintf(tmp, "\t");
			            strncat(BodyTxt, tmp, sizeof(tmp) );
			        }
                    bzero(str_ip, sizeof(str_ip));
                    switch(((struct sockaddr*)&nodemap[tnode].cln_if[tif].cli_addr6)->sa_family)
                    {
                    case AF_INET:
                    	inet_ntop(AF_INET, 
                    			&((struct sockaddr_in*)&nodemap[tnode].cln_if[tif].cli_addr6)->sin_addr,
                    			str_ip,
                    			sizeof(str_ip));
                    	break;
                    case AF_INET6:
                    	inet_ntop(AF_INET6, 
                    			&((struct sockaddr_in6*)&nodemap[tnode].cln_if[tif].cli_addr6)->sin6_addr,
                    			str_ip,
                    			sizeof(str_ip));
                    	break;
                    }
                    sprintf(tmp, MSGSTR(CLSTAT_MSG_46, "\tAddress: %s\n"), str_ip);
			        strncat(BodyTxt, tmp, sizeof(tmp) );
			        lineCount++;
    
                                sprintf(tmp, MSGSTR(CLSTAT_MSG_47,
                                   "\t\t\t\t\t\tState:   %s\n"), 
				    get_state(nodemap[tnode].cln_if[tif].cli_state));
			        strncat(BodyTxt, tmp, sizeof(tmp) );
			        lineCount++;
			    }
                        }
		    } /* for all nodes */
	

		    /* 
		     * We have displayed the all the interfaces, now show
		     * the performance data
		     */
#ifdef SUPPORTS_PERF_DATA
                    sprintf(tmp, /* MSGSTR(CLSTAT_MSG_45, */
                                "\t   CPU (idle) (%d)",
				nodemap[i].cln_glidle);
		    strncat(BodyTxt, tmp, sizeof(tmp) );
                    sprintf(tmp, /* MSGSTR(CLSTAT_MSG_45, */
                                "\t\t\tMemory (free) (%d)\n",
				nodemap[i].cln_real_mem_free);
		    strncat(BodyTxt, tmp, sizeof(tmp) );
		    lineCount++;
#endif

		    /* 
		     * We have displayed the node and all its interfaces.
		     * Now look through the list of resource groups for this
		     * node - if the state is anything other than offline,
		     * display the group and state.
 		     */
		    for(j=0; j<nbr_groups; j++)
                    {
			if (notOfflineGroupState(&groupmap[j], nodemap[i].cln_nodeid)) 
			{
                            sprintf(tmp, /* MSGSTR(CLSTAT_MSG_45, */
                                "\t   Resource Group: %s",
				groupmap[j].clg_name);
			    strncat(BodyTxt, tmp, sizeof(tmp) );
    
                            sprintf(tmp, /* MSGSTR(CLSTAT_MSG_46, */
				"\t\t\tState:\t%s\n",
				resourceStateStr(&groupmap[j], nodemap[i].cln_nodeid));

			    strncat(BodyTxt, tmp, sizeof(tmp) );
			    lineCount++;
			} /* not offline */
		    } /* for all groups */
    
		    strcat(BodyTxt, "\n");
		    lineCount++;
		}
	    }
    
	    if (ONCE)
            {
                /* "once" mode - print the status to stdout and exit */
                printf("%s",HeaderTxt);
                printf("%s",BodyTxt);
                cl_free_nodemap (nodemap);
                cl_free_nodemap (HOLD_NODES);
                cl_free_groupmap (groupmap);
                cl_free_groupmap (HOLD_GROUPS);
                exit(0);
	    }

#ifndef __LINUX__
	    /* Update curses Window */
    
	    /* Is there enough room to display all nodes? */
	    if (lineCount > sizeWinBody)
	    {
		if (0 == strcmp(command, "b") )
		{
		    refresh = 1;
		    if (segCount > 0)
		    {
		        segCount--;
		    }
		}
		else if (0 == strcmp(command, "f") )
		{
		    refresh = 1;
		    if (lineCount > segCount * sizeWinBody)
		    {
		        segCount++;
		    }
		}
		else if (0 == strcmp(command, "r") )
		{
		    refresh = 1;
		}
	    }
    
	    if (segCount > 0)
	    {
	        pc = BodyTxt;
		i = 0;
		while (pc != NULL)
		{
		    pc = strchr(pc, '\n');
		    i++;
		    pc++;
		    if (i >= segCount * sizeWinBody)
		    {
		        break;
		    }
		}
		if (refresh)
		{
		    wclear(winBody);
		    touchwin(winBody);
		}
        if ((char *)NULL != pc) {
		    mvwaddstr(winBody, 0, 0, pc);
        }
	    }
	    else 
	    {
		if (refresh)
		{
		    wclear(winBody);
		    touchwin(winBody);
		}
		mvwaddstr(winBody, 0, 0, BodyTxt);
    	    }
    
	    wclear(winHeader);
	    touchwin(winHeader);
	    mvwaddstr(winHeader, 0, 0, HeaderTxt);

	    wrefresh(winHeader);
	    wrefresh(winBody);

	    if (winStatusLine != NULL)
	    {
		wrefresh(winStatusLine);
		command[0] = '\0';
	    }

            if(rflag)
            {
                clstat_sleep(wait_time);
            }

	}
        else
        {
            clstat_sleep(wait_time);
        }

	if (winStatusLine != NULL)
	{
	    sprintf(command, "%c", wgetch(winStatusLine) ); 
	}
    
    }
    cl_free_nodemap (nodemap);
    cl_free_nodemap (HOLD_NODES);
    cl_free_groupmap (groupmap);
    cl_free_groupmap (HOLD_GROUPS);

    if (winStatusLine != NULL)
    {
        wclear(winStatusLine);
        touchwin(winStatusLine);
	wrefresh(winStatusLine);
    }

    endwin();
#else
	}
    }
#endif
}



#ifndef __LINUX__

/*
 * Name:      get_clstr_rspns
 *
 * Description: This function is called in interactive mode (only) when a user
 *            has entered input that must be acted upon.  The function will 
 *            determine whether the input is valid for the existing environment.
 *            The function returns 0 if the input is "r" to return to the cluster
 *            list display, returns the end of looping time for non-interactive
 *            mode ("l time"input), current time otherwise (for immediate update).
 *
 * Arguments: NONE
 *
 * Return:    time_t done_time - the time that non-interactive display will end
 *
 * Global Data Affected: NONE
 */

time_t get_clstr_rspns()
{
  static time_t       done_time;
  int          sleepy_time;
  int          loop = 1;
  char         node_respond[80];

while(loop)
    {

    echo();
    nocbreak();

    printf("> ");
    gets(node_respond);
    tputs(CE, 1, term_dumb);

    if(redisplay)
        node_respond[0] = '\0';

    switch(node_respond[0])
        {
        case 'r':
	    echo();
	    nocbreak();
            return(0);
            break;
        case 'l':
        /* Loop (sleep) for a time */
            if((sleepy_time = atoi(node_respond+1)) == 0)
                {
                printf(MSGSTR(CLSTAT_INVALID_TIME,
                      "\tInvalid time provided for automatic mode\n"));
                break;
                }
            done_time = time(0);
            done_time += sleepy_time;

            printf(MSGSTR(CLSTAT_AUTO,
                   "\t%s is now in automatic mode for %d seconds\n"), progname,

                    sleepy_time);
            tputs(UP, 1, term_dumb);
            tputs(UP, 1, term_dumb);
            tputs(CE, 1, term_dumb);
            tputs(UP, 1, term_dumb);
            printf("\n");
            loop--;
            break;
        case '\0':
            done_time = time(0);
            loop--;
            break;
        case '?':
            show_commands(1);
            done_time = time(0);
            loop--;
            break;
        default:
            printf("\tInvalid clstat command - enter %c to see commands\n",'?');
            break;
        }

    tputs(UP, 1, term_dumb);
    tputs(UP, 1, term_dumb);
    tputs(CE, 1, term_dumb);
 }
  echo();
  nocbreak();

return(done_time);
}

#endif

/*
 * Name:      resourceStateStr
 *
 * Description: Converts the numeric resource state value to a character string 
 * 		for use in screen displays.
 *
 * Arguments: resource group handle and node id
 *
 * Return:    char * state_name - the equivalent character string of the state
 *
 * Global Data Affected: NONE
 */

char *
resourceStateStr(struct cl_group *pGroup, int node_id)
{
    char *state;
    int i;

    for (i = 0; i < pGroup->clg_num_nodes; i++)
    {
        if (pGroup->clg_node_ids[i] == node_id)
	{

    	    switch (pGroup->clg_node_states[i])
      	    {
        	case CL_RGNS_ONLINE:
            	    return("On line");
            	    break;
        	case CL_RGNS_OFFLINE:
            	    return("Off line");
            	    break;
        	case CL_RGNS_ACQUIRING:
            	    return("Acquiring");
            	    break;
        	case CL_RGNS_RELEASING:
            	    return("Releasing");
            	    break;
        	case CL_RGNS_ERROR:
            	    return("Error");
            	    break;
        	case CL_RGNS_ONLINE_SECONDARY_STATE:
            	    return("Online (Secondary)");
            	    break;
        	case CL_RGNS_ONLINE_PEER_STATE:
            	    return("Online (Peer)");
            	    break;
        	case CL_RGNS_ACQUIRING_SECONDARY_STATE:
            	    return("Acquiring (Secondary)");
            	    break;
        	case CL_RGNS_ACQUIRING_PEER_STATE:
            	    return("Acquiring (Peer)");
            	    break;
        	case CL_RGNS_RELEASING_SECONDARY_STATE:
            	    return("Releasing (Secondary)");
            	    break;
        	case CL_RGNS_RELEASING_PEER_STATE:
            	    return("Releasing (Peer)");
            	    break;
        	case CL_RGNS_ERROR_SECONDARY_STATE:
            	    return("Error (Secondary)");
            	    break;
        	case CL_RGNS_TEMP_ERROR_STATE:
            	    return("Temp Error");
            	    break;
        	case CL_RGNS_TEMP_ERROR_SECONDARY_STATE:
            	    return("Temp Error (Secondary)");
            	    break;
                case CL_RGNS_UNMANAGED_STATE:
            	   return("Unmanaged");
            	    break;
        	case CL_RGNS_UNMANAGED_SECONDARY_STATE:
            	    return("Unmanaged(Secondary)");
            	    break;
		case CL_RGNS_OFFLINE_DUE_TO_FALLOVER:
	            return("Offline due to failover");
		    break;
		case CL_RGNS_OFFLINE_DUE_TO_PARENT_OFFLINE:
	            return("Offline due to parent offline");
		    break;
		case CL_RGNS_OFFLINE_DUE_TO_LACK_OF_NODE:
		    return("Offline due to lack of node");
		    break;
		case CL_RGNS_OFFLINE_DUE_TO_TARGET_OFFLINE:
	            return("Offline due to target offline");
	            break;
        	case CL_RGNS_INVALID:
        	default:
            	    return( MSGSTR(CLSTAT_STATE_UNDEFINED, "UNDEFINED"));
            	    break;
	    }
	}
    }
    return(NULL);
}
        

/*
 * Name:      resourceStateColor
 *
 * Description: Converts the numeric resource state value to a "color" string
 * 		for use in HTML output
 *
 * Arguments: resource group handle and node id
 *
 * Return:    char * color - the equivalent color representing the state
 *
 * Global Data Affected: NONE
 */

char *
resourceStateColor(struct cl_group *pGroup, int node_id)
{
    char *color;
    int i;

    for (i = 0; i < pGroup->clg_num_nodes; i++)
    {
        if (pGroup->clg_node_ids[i] == node_id)
	{

    	    switch (pGroup->clg_node_states[i])
      	    {
        	case CL_RGNS_ONLINE:
            	    /* Green */
      		    return("\"#00FF00\"");
            	    break;

        	case CL_RGNS_OFFLINE:
        	case CL_RGNS_ERROR:
        	case CL_RGNS_ERROR_SECONDARY_STATE:
        	case CL_RGNS_INVALID:
        	case CL_RGNS_TEMP_ERROR_STATE:
        	case CL_RGNS_TEMP_ERROR_SECONDARY_STATE:
		    case CL_RGNS_OFFLINE_DUE_TO_FALLOVER:
		    case CL_RGNS_OFFLINE_DUE_TO_PARENT_OFFLINE:
		    case CL_RGNS_OFFLINE_DUE_TO_LACK_OF_NODE:
		    case CL_RGNS_OFFLINE_DUE_TO_TARGET_OFFLINE:
		    case CL_RGNS_OFFLINE_DUE_TO_SOURCE_OFFLINE:
            
            	    /* Red */
      		    return("\"#FF0000\"");
            	    break;

        	case CL_RGNS_ACQUIRING:
        	case CL_RGNS_ACQUIRING_SECONDARY_STATE:
        	case CL_RGNS_ACQUIRING_PEER_STATE:
		    /* Aqua */
      		    return("\"#00FFFF\"");
            	    break;

        	case CL_RGNS_RELEASING:
        	case CL_RGNS_RELEASING_SECONDARY_STATE:
        	case CL_RGNS_RELEASING_PEER_STATE:
            	    /* Yellow */
      		    return("\"#FFFF00\"");
            	    break;

        	case CL_RGNS_ONLINE_SECONDARY_STATE:
        	case CL_RGNS_ONLINE_PEER_STATE:
            	    /* Blue */
      		    return("\"#0000FF\"");
            	    break;

        	default:
            	    return( MSGSTR(CLSTAT_STATE_UNDEFINED, "UNDEFINED"));
            	    break;
	    }
	}
    }
    return(NULL);
}
        

/*
 * Name:      get_state
 *
 * Description: Converts the numeric state value to a character string for use
 *            in screen displays.
 *
 * Arguments: int stat_nbr      - value for state (of node, cluster, interface)
 *
 * Return:    char * state_name - the equivalent character string of the state
 *
 * Global Data Affected: NONE
 */

char *get_state(int stat_nbr)
{
  char *state;

switch(stat_nbr)
    {
    case CLS_INVALID:
        state = MSGSTR(CLSTAT_STATE_INVALID, "INVALID");
        break;
    case CLS_VALID:
        state = MSGSTR(CLSTAT_STATE_VALID, "VALID");
        break;
    case CLS_UP:
        state = MSGSTR(CLSTAT_STATE_UP, "UP");
        break;
    case CLS_DOWN:
        state = MSGSTR(CLSTAT_STATE_DOWN, "DOWN");
        break;
    case CLS_UNKNOWN:
        state = MSGSTR(CLSTAT_STATE_UNKNOWN, "UNKNOWN");
        break;
    case CLS_GRACE:
        state = MSGSTR(CLSTAT_STATE_GRACE, "GRACE");
        break;
    case CLS_JOINING:
        state = MSGSTR(CLSTAT_STATE_JOINING, "JOINING");
        break;
    case CLS_LEAVING:
        state = MSGSTR(CLSTAT_STATE_LEAVING, "LEAVING");
        break;
    case CLS_IN_USE:
        state = MSGSTR(CLSTAT_STATE_IN_USE, "IN_USE");
        break;
    case CLS_PRIMARY:
        state = MSGSTR(CLSTAT_STATE_PRIMARY, "PRIMARY");
        break;
    default:
        state = MSGSTR(CLSTAT_STATE_UNDEFINED, "UNDEFINED");
        break;
    }
return(state);
}
        
/*
 * Name:      get_substate
 *
 * Description: Converts the numeric substate value to a character string for use
 *            in screen displays.
 *
 * Arguments: int stat_nbr      - value for substate (of cluster)
 *
 * Return:    char * state_name - the equivalent character string of the state
 *
 * Global Data Affected: NONE
 */

char *get_substate(int stat_nbr)
{
  char *state;
  
  switch(stat_nbr)
    {
    case CLSS_STABLE:
            state = MSGSTR(CLSTAT_SUBSTATE_STABLE, "STABLE");
      break;
    case CLSS_UNSTABLE:
            state = MSGSTR(CLSTAT_SUBSTATE_UNSTABLE, "UNSTABLE");
      break;
    case CLSS_ERROR:
            state = MSGSTR(CLSTAT_SUBSTATE_ERROR, "ERROR");
      break;
    case CLSS_RECONFIG:
      state = "RECONFIG";
      break;
    case CLSS_UNKNOWN:
            state = MSGSTR(CLSTAT_SUBSTATE_UNKNOWN, "UNKNOWN");
      break;
    default:
            state = MSGSTR(CLSTAT_SUBSTATE_UNDEFINED, "UNDEFINED");
      break;
    }
  return(state);
}   


/*
 * Name:      HTML_state_color
 *
 * Description:  Returns a hex color code based on the numeric state
 *               value.  For use in HTML formatted output.
 *
 * Arguments: int state  : numeric state value
 *
 * Return:    A string containing the hex color value.
 *
 * Global Data Affected: NONE
 *
 */
char *HTML_state_color(int state)
{
  switch(state)
    {
    case CLS_UP:
    case CLS_VALID:
      /* Green */
      return("\"#00FF00\"");

    case CLS_DOWN:
    case CLS_INVALID:
      /* Red */
      return("\"#FF0000\"");

    case CLS_GRACE:
    case CLS_LEAVING:
      /* Yellow */
      return("\"#FFFF00\"");

    case CLS_JOINING:
      /* Aqua */
      return("\"#00FFFF\"");

    case CLS_IN_USE:
    case CLS_PRIMARY:
      /* Magenta */
      return("\"#FF00FF\"");

    case CLS_UNKNOWN:
    default: /* UNDEFINED */
      /* Blue */
      return("\"#0000FF\"");
    }
}


/*
 * Name:      HTML_substate_color
 *
 * Description:  Returns a hex color code based on the numeric substate
 *               value.  For use in HTML formatted output.
 *
 * Arguments: int state  : numeric substate value
 *
 * Return:    A string containing the hex color value.
 *
 * Global Data Affected: NONE
 *
 */
char *HTML_substate_color(int substate)
{
  switch(substate)
    {
    case CLSS_STABLE:
      /* Green */
      return("\"#00FF00\"");

    case CLSS_ERROR:
      /* Red */
      return("\"#FF0000\"");

    case CLSS_UNSTABLE:
    case CLSS_RECONFIG:
      /* Yellow */
      return("\"#FFFF00\"");

    case CLSS_UNKNOWN:
    default: /* UNDEFINED */
      /* Blue */
      return("\"#0000FF\"");
    }
}


/*
 * Name:      fmt_date
 *
 * Description: Generates a string with the current date for use in the screen
 *            displays.  Format depends on current locale.
 *
 * Arguments: time_t * time_nbr - pointer to structure with the current time
 *
 * Return:    char * buf  - buffer in which to store formatted time string
 *            rc - return code from strftime
 *
 * Global Data Affected: NONE
 */

size_t fmt_date(time_t *time_nbr, char *buf, size_t max_size)
{
  size_t rc;
  
  rc = strftime(buf, max_size, nl_langinfo(D_T_FMT), localtime(time_nbr));
  return(rc);
}

/*
 * Name:      make_filename
 *
 * Description: Creates a filename for output file to be used to print screens.
 *            This function is called only if the print options is flagged at
 *            start-up without a file also provided.  The file is named 
 *            according to format: "/tmp/clstr_<date>.<pid>", where the date is
 *            mmddyy format and pid is the process ID.
 *
 * Arguments: NONE
 *
 * Return:    char * the_filename - pointer to filename of created output file
 *
 * Global Data Affected: NONE
 */
char *make_filename()
{
  int               progid;
  char              the_date[5];
  static char       the_filename[60];
  struct tm        *time_ptr;
  time_t            current_time;

  progid = getpid();
  
  current_time = time(0);
  time_ptr = localtime(&current_time);
  
  sprintf(the_date, "%d%d%d", time_ptr->tm_mon + 1, time_ptr->tm_mday, 
	  time_ptr->tm_year);
  sprintf(the_filename, "/tmp/clstat_%s.%d", the_date, progid);
  
  return(the_filename);
}


/*
 * Name:      usage
 *
 * Description: Prints the program usage message.
 *
 * Arguments: NONE
 *
 * Return:    NONE
 *
 * Global Data Affected: NONE
 */

void usage()
{
printf(MSGSTR(CLSTAT_USAGE2,"usage: %s [-c cluster ID | -n cluster_name] [-i] [-r seconds] [-a|-o] [-s] [-h]\n\n"), progname);
printf(MSGSTR(CLSTAT_c_OPT,"  -c cluster ID > run in automatic (non-interactive) mode for the\n                  specified cluster. \n"));
printf(MSGSTR(CLSTAT_n_OPT,"  -n name       > run in automatic (non-interactive) mode for the\n                  specified cluster name. \n"));
printf(MSGSTR(CLSTAT_i_OPT,"  -i            > run in interactive mode\n"));
printf(MSGSTR(CLSTAT_r_OPT,"  -r seconds    > number of seconds between each check of the\n                  cluster status\n"));
printf(MSGSTR(CLSTAT_a_OPT,"  -a            > run ascii version.\n"));
printf(MSGSTR(CLSTAT_o_OPT,"  -o            > run once and exit.\n"));
printf(MSGSTR(CLSTAT_s_OPT,"  -s            > display both up and down service labels.\n"));
printf(MSGSTR(CLSTAT_h_OPT,"  -h            > display help for clstat configuration issues.\n")); 
}


/*
 * Name:      term_dumb
 *
 * Description: Dummy function that is called by tputs for putchar
 *
 * Arguments: NONE
 *
 * Return:    NONE
 *
 * Global Data Affected: NONE
 */

void term_dumb(char c)
{
  putchar(c);
}


/*
 * Name:      show_commands
 *
 * Description: Displays the command options from the screens
 *
 * Arguments: NONE
 *
 * Return:    NONE
 *
 * Global Data Affected: NONE
 */

void show_commands(int detail)
{
  char any_act[60];

#ifndef __LINUX__
tputs(CL, 1, term_dumb);
#endif

printf(MSGSTR(CLSTAT_STAT_MON,"\t\t%s - HACMP Cluster Status Monitor\n"), progname);
printf("\t\t-------------------------------------\n\n");

if(detail)
    {
    printf(MSGSTR(CLSTAT_ALLOWED_DETAIL_CMDS,"Commands allowed from the detailed cluster info screen:\n\n"));
    printf(MSGSTR(CLSTAT_CMD_LIST,"\tr :       return to cluster list screen\n"));
    printf(MSGSTR(CLSTAT_CMD_AUTO,"\tl <#> :   program will go into automatic mode for # seconds\n\t          If the cluster info changes, the display will be \n\t          updated.  Note # must be a numeric value\n"));
    printf(MSGSTR(CLSTAT_CMD_DISP,"\t[ENTER] : the information will be updated immediately\n"));
    }
else
    {
    printf(MSGSTR(CLSTAT_ALLOWED_LIST_CMDS,"Commands allowed from the cluster list screen:\n\n"));
    printf("\t%c :       exit the program\n",'q');
    printf(MSGSTR(CLSTAT_CMD_DETAIL,"\t# :       display the detailed cluster info of the cluster \n\t          with the ID number of #.\n"));
    printf(MSGSTR(CLSTAT_DISP_LIST,"\t[ENTER] : the cluster list will be updated immediately\n"));
    }
  printf(MSGSTR(CLSTAT_PREV_SCRN,"\n\n\nPress [ENTER] to return to the previous screen\n"));
  gets(any_act);
}


int
compareNetifs(pNetif1, pNetif2)
    struct cl_netif *pNetif1, *pNetif2;
{
    int i;

    for (i = 0; i < CL_MAXNETIFS; i++)
    {
        if (pNetif1[i].cli_state != pNetif2[i].cli_state ||
    			(pNetif1[i].cli_addr.sin_addr.s_addr !=
    				pNetif2[i].cli_addr.sin_addr.s_addr) ||
				(memcmp (&(((struct sockaddr_in6 *)&(pNetif1[i].cli_addr6))->sin6_addr),
					&(((struct sockaddr_in6 *)&(pNetif1[i].cli_addr6))->sin6_addr),
					sizeof (struct in6_addr))) ||
				strcmp(pNetif1[i].cli_name, pNetif2[i].cli_name))
	    return 1;
    }
    return 0;
}


int
compareNodemap(pNodemap1, pNodemap2)
    struct cl_node *pNodemap1, *pNodemap2;
{
    int i;

    for (i = 0; i < CL_MAXNODES; i++)
    {
        if (pNodemap1[i].cln_state != pNodemap2[i].cln_state)
	    return 1;
    }
    return 0;
}

/*
 * Name:      compareGroupmap
 *
 * Description: compares the contents of 2 cl_group struct arrays
 *
 * Arguments: pointer to the cl_group(s) to be compared
 *
 * Return:    0 - maps are equal
 *            1 - maps are not equal
 *
 * Global Data Affected: NONE
 */
int
compareGroupmap(struct cl_group *pGroupmap1, struct cl_group *pGroupmap2)
{
    int i;

    for (i = 0; i < CL_MAXGROUPS; i++)
    {
        if (pGroupmap1[i].clg_group_id != pGroupmap2[i].clg_group_id)
	    return 1;
    }
    return 0;
}


/*
 * Name:      copyNodemap
 *
 * Description: Copies a nodemap from source to dest.  This routine assumes 
 * storage has already been allocated for both nodemaps.  copyNodemap differs 
 * from bcopy in that it does not copy the pointer to interfaces, but rather 
 * the interfaces themselves. 
 *
 * Arguments: pointer to the source and destination maps
 *
 * Return:    NONE
 *
 * Global Data Affected: NONE
 */
void
copyNodemap(pSource, pDest)
    struct cl_node *pSource, *pDest;
{
    int i;

    for (i = 0; i < CL_MAXNODES; i++)
    {
        pDest[i].cln_clusterid = pSource[i].cln_clusterid;
        pDest[i].cln_nodeid = pSource[i].cln_nodeid;
        strcpy(pDest[i].cln_nodename, pSource[i].cln_nodename);
        pDest[i].cln_state = pSource[i].cln_state;
        pDest[i].cln_nif = pSource[i].cln_nif;
	bcopy (pSource[i].cln_if,
	       pDest[i].cln_if,
	       sizeof (struct cl_netif) * CL_MAXNETIFS);
    }
}

/*
 * Name:      notOfflineGroupState
 *
 * Description: Given a group pointer and a node id, search all the nodes
 *		in the group for the node id.
 *
 * Arguments: pointer to the group struct and a node id
 *
 * Return:    0 - if the node is not in the group or 
 *		  the node is in the group but the state is offline
 * 	      1 - the node is part of the group and the state is not offline
 *
 */
int
notOfflineGroupState(struct cl_group *pGroup, int node_id)
{
    int i;

    for (i = 0; i < pGroup->clg_num_nodes; i++)
    {
        if (pGroup->clg_node_ids[i] == node_id &&
	    pGroup->clg_node_states[i] > CL_RGNS_INVALID &&
	    pGroup->clg_node_states[i] <=  CL_RGNS_UNMANAGED_SECONDARY_STATE &&
	    pGroup->clg_node_states[i] <= CL_RGNS_OFFLINE_DUE_TO_TARGET_OFFLINE &&
	    pGroup->clg_node_states[i] != CL_RGNS_OFFLINE)
		return(TRUE);
    }
    return(FALSE);
}


/*
 * Name:      resetClustermap
 *
 * Description: "clears" the entries in a cluster map to default values
 *
 * Arguments: pointer to the cl_cluster map
 *
 * Return:    NONE
 *
 * Global Data Affected: NONE
 */
void
resetClustermap(pClustermap)
    struct cl_cluster *pClustermap;
{
    int i;

    for (i = 0; i < CL_MAXCLUSTERS; i++)
    {
        pClustermap[i].clc_clusterid = -1;
        pClustermap[i].clc_state = CLS_INVALID;
        pClustermap[i].clc_substate = CLS_INVALID;
        memset(pClustermap[i].clc_primary, '\0', CL_MAXNAMELEN);
        memset(pClustermap[i].clc_name, '\0', CL_MAXNAMELEN);
        pClustermap[i].clc_number_of_nodes = 0;
        pClustermap[i].clc_number_of_groups = 0;
        pClustermap[i].clc_number_of_networks = 0;
        pClustermap[i].clc_number_of_sites = 0;
    }
}

/*
 * Name:      resetNodemap
 *
 * Description: "clears" the entries in a node map to default values
 *
 * Arguments: pointer to the cl_node map
 *
 * Return:    NONE
 *
 * Global Data Affected: NONE
 */
void
resetNodemap(pNodemap)
    struct cl_node *pNodemap;
{
    int i;

    for (i = 0; i < CL_MAXNODES; i++)
    {
        pNodemap[i].cln_clusterid = -1;
        pNodemap[i].cln_nodeid = -1;
        memset(pNodemap[i].cln_nodename, '\0', CL_MAXNAMELEN);
        pNodemap[i].cln_state = CLS_INVALID;
        pNodemap[i].cln_nif = 0;
	memset (pNodemap[i].cln_if, 0,
	       sizeof (struct cl_netif) * CL_MAXNETIFS);
    }
}

/*
 * Name:      resetGroupmap
 *
 * Description: "clears" the entries in a resource group map to default values
 *
 * Arguments: pointer to the cl_group map
 *
 * Return:    NONE
 *
 * Global Data Affected: NONE
 */
void
resetGroupmap(struct cl_group *groupMap)
{
    int i;

    for (i = 0; i < CL_MAXGROUPS; i++)
    {
        groupMap[i].clg_clusterid = -1;
        groupMap[i].clg_group_id = -1;
        memset(groupMap[i].clg_name, '\0', CL_MAXNAMELEN);
    }
}



/*
 * Name:      javaGroupInfo
 *
 * Description: Shows resource group info along with building a javascript
 * 		which shows additional data
 *
 * Arguments: resource group struct, the node map and a node id
 *
 * Return:    NONE
 *
 * Global Data Affected: NONE
 */
void
javaGroupInfo(struct cl_group *group_map, 
	      struct cl_node  *node_map,
	      int nodeid)
{
    int i, rc=0;

#ifdef CLINFO_ODM_LOOKUP
    char *classname = NULL;
    char crit[128]="";
    char *odmdir = "/etc/es/objrepos";
    char *field = NULL;
    char *group = NULL;
    int count;
    struct HACMPresource *info;
    struct HACMPgroup *group_info;
    CLASS_SYMBOL class;
#endif

    /* begin by printing the info we know - group name and id */
    printf(
	"<TR>\n");
    printf(
	"<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">Resource Group:</FONT></TD>\n");
    printf(
	"<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">\n"
	"<A HREF=\"javascript:{'\n"
	"<HTML><HEAD><TITLE>Resource Group Information</TITLE></HEAD>"
	"<CENTER><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	"<B>Resource Group Information</B></FONT>"
	"<BR><BR><TABLE BORDER=0 CELLPADDING=2 CELLSPACING=0>"
    
	/* table row */
	"<TR><TD ALIGN=left VALIGN=top>"
	"<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	"<B>Name / ID:</B></FONT></TD>"
	"<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	"<B>&nbsp;&nbsp;%s&nbsp;&nbsp(%d)</B>"
	"</FONT></TD></TR>\n",
	group_map->clg_name, group_map->clg_group_id);

#ifdef CLINFO_ODM_LOOKUP
    /* The javascript produced to display extended RG information does
     * not work correctly in IE, Mozilla, Netscape, or Firefox. Also,
     * logic used to determine the information to display is incorrect,
     * because it assumes that only one cluster is being monitored.
     * Because this code is non-essential, I have used preprocessor
     * directives to exclude it from being built. This code can be
     * corrected in a future release.
     */

    /* now we will print the rest of the information - either from the odm */
    /* or from the clinfo data */
    odm_set_path(odmdir);
    sprintf(crit, "id = %d", group_map->clg_group_id);
    classname = "HACMPgroup";
    group_info = (struct HACMPgroup *)odmget(classname, crit, &count);

    if (count > 0) {
        sprintf(crit, "group = %s", group_info->group);
        classname = "HACMPresource";
        count = 0;
        info = (struct HACMPresource *)odmget(classname, crit, &count);

        /* we have the group info from odm and the list of resources - */
	/* start printing */
        /* RG startup policy */
        printf(
	    "<TR><TD ALIGN=left VALIGN=top>"
	    "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>Startup Policy:</B></FONT></TD>"
	    "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>&nbsp;&nbsp;%s&nbsp;&nbsp;</B>"
	    "</FONT></TD></TR>\n",
	    resourcePolicyStr(group_map->clg_startup_policy));

        /* RG Fallover Policy */
        printf(
	    "<TR><TD ALIGN=left VALIGN=top>"
	    "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>Fallover Policy:</B></FONT></TD>"
	    "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>&nbsp;&nbsp;%s&nbsp;&nbsp;</B>"
	    "</FONT></TD></TR>\n",
	    resourcePolicyStr(group_map->clg_fallover_policy));

        /* Fallback Policy */
        printf(
	    "<TR><TD ALIGN=left VALIGN=top>"
	    "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>Fallback Policy:</B></FONT></TD>"
	    "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>&nbsp;&nbsp;%s&nbsp;&nbsp;</B>"
	    "</FONT></TD></TR>\n",
	    resourcePolicyStr(group_map->clg_fallback_policy));

        /* Site Policy */
        printf(
	    "<TR><TD ALIGN=left VALIGN=top>"
	    "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>Site Policy:</B></FONT></TD>"
	    "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>&nbsp;&nbsp;%s&nbsp;&nbsp;</B>"
	    "</FONT></TD></TR>\n",
	    resourcePolicyStr(group_map->clg_site_policy));

        printf(
	    /* table row */
	    "<TR><TD ALIGN=left VALIGN=top>"
	    "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>Node list:</B></FONT></TD>"
	    "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>&nbsp;&nbsp;%s</B>"
	    "</FONT></TD></TR>\n",
	    group_info->nodes);

        for (i = 0; i < count; i++) {
            printf(
	       "<TR><TD ALIGN=left VALIGN=top>"
	       "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	       "<B>%s:</B></FONT></TD>"
	       "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	       "<B>&nbsp;&nbsp;%s</B>"
	       "</FONT></TD></TR>\n",
		    info[i].name, info[i].value);
        }

    } /* we have odm info */

    else {
#endif
	/* if here, we were not able to read from the odm - all we can */
	/* display is the info available from clinfo		       */
        /* RG startup policy */
        printf(
	    "<TR><TD ALIGN=left VALIGN=top>"
	    "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>Startup Policy:</B></FONT></TD>"
	    "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>&nbsp;&nbsp;%s&nbsp;&nbsp;</B>"
	    "</FONT></TD></TR>\n",
	    resourcePolicyStr(group_map->clg_startup_policy));

        /* RG Fallover Policy */
        printf(
	    "<TR><TD ALIGN=left VALIGN=top>"
	    "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>Fallover Policy:</B></FONT></TD>"
	    "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>&nbsp;&nbsp;%s&nbsp;&nbsp;</B>"
	    "</FONT></TD></TR>\n",
	    resourcePolicyStr(group_map->clg_fallover_policy));

        /* Fallback Policy */
        printf(
	    "<TR><TD ALIGN=left VALIGN=top>"
	    "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>Fallback Policy:</B></FONT></TD>"
	    "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>&nbsp;&nbsp;%s&nbsp;&nbsp;</B>"
	    "</FONT></TD></TR>\n",
	    resourcePolicyStr(group_map->clg_fallback_policy));

        /* Site Policy */
        printf(
	    "<TR><TD ALIGN=left VALIGN=top>"
	    "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>Site Policy:</B></FONT></TD>"
	    "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>&nbsp;&nbsp;%s&nbsp;&nbsp;</B>"
	    "</FONT></TD></TR>\n",
	    resourcePolicyStr(group_map->clg_site_policy));

	/* nodes in the group are stored by id - convert the node list */
        printf(
	    /* table row */
	    "<TR><TD ALIGN=left VALIGN=top>"
	    "<FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>Node list:</B></FONT></TD>"
	    "<TD ALIGN=left VALIGN=top><FONT FACE=Arial,Helvetica,sans-serif SIZE=2>"
	    "<B>&nbsp;&nbsp;%s</B>"
	    "</FONT></TD></TR>\n",
	    nodeArrayStr(node_map, 
			 group_map->clg_node_ids, 
			 group_map->clg_num_nodes));
#ifdef CLINFO_ODM_LOOKUP

    }
#endif

    /* end of the javascript table */
    printf(
	    "</TABLE></HTML>';}\">"
	    "<B>%s</B></A></FONT></TD>\n",
	    group_map->clg_name);
	        
    printf(
    	    "<TD><FONT SIZE=\"-1\" FACE=\"arial,helvetica\">State:</FONT></TD>\n");
    printf(
   	    "<TD><TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0><TR><TD BGCOLOR=%s><FONT SIZE=\"-1\" FACE=\"arial,helvetica\"><B>%s</B></FONT></TD></TR></TABLE></TD></TR>\n",
	    resourceStateColor(group_map, nodeid),
	    resourceStateStr(group_map, nodeid));

}

/*
 * Name:      nodeArrayStr
 *
 * Description: Generates a string with the names of all the nodes in the group
 *
 * Arguments: Node map, list of nodes in the group, and number of nodes
 *
 * Return:    char * buf  - buffer in which to store formatted node string
 *
 * Global Data Affected: NONE
 */

char *
nodeArrayStr(struct cl_node *pNodemap, int *group_node_ids, int num_nodes)
{
  int i,j;
  /* use MAXNODES here because CL_MAXNODES maps to a variable value */
  static char ret_str[MAXNODES][CL_MAXNAMELEN];

  memset(ret_str, 0, CL_MAXNODES * CL_MAXNAMELEN);

  for (i=0; i<num_nodes; i++)
  {
    /* find this node in the list of all nodes */
    for (j=0; j<CL_MAXNODES; j++)
    {
        if (group_node_ids[i] == pNodemap[j].cln_nodeid)
        {
            strcat((char *)ret_str, pNodemap[j].cln_nodename);
            strcat((char *)ret_str, " ");
        }
    }
  }

  return((char *)ret_str);
}


/*
 * Name:      resourcePolicyStr
 *
 * Description: Converts the numeric resource policy into a string
 *
 * Arguments: resource group policy
 *
 * Return:    char *  - the equivalent string representing the policy
 *
 * Global Data Affected: NONE
 */

char *
resourcePolicyStr(int policy)
{
    static char str[1024];

    switch (policy) 
    {

        case CL_RGP_INVALID: 
            strcpy(str, MSGSTR(TYPE_UNKNOWN, "Unknown"));
            break;

        case CL_RGP_ONLINE_ON_HOME_NODE:
            strcpy(str, MSGSTR(TYPE_HOME_NODE, "Online On Home Node Only"));
            break;

        case CL_RGP_ONLINE_ONFIRST_AVAILBLE_NODE:
            strcpy(str, MSGSTR(TYPE_FIRST_NODE, "Online On First Available Node"));
            break;

        case CL_RGP_ONLINE_USING_DISTRIBUTION_POLICY:
            strcpy(str, MSGSTR(TYPE_DISTR_NODE, "Online Using Distribution Policy"));
            break;

        case CL_RGP_ONLINE_ALL_NODES:
            strcpy(str, MSGSTR(TYPE_ALL_NODE, "Online On All Available Nodes"));
            break;

        case CL_RGP_FALLOVER_TO_PRIORITY_NODE:
            strcpy(str, MSGSTR(TYPE_NEXT_NODE, "Fallover To Next Priority Node In The List"));
            break;

        case CL_RGP_FALLOVER_USING_DNP:
            strcpy(str, MSGSTR(TYPE_DNP_NODE, "Fallover Using Dynamic Node Priority"));
            break;

        case CL_RGP_BRING_OFFLINE:
            strcpy(str, MSGSTR(TYPE_BO, "Bring Offline (On Error Node Only)"));
            break;

        case CL_RGP_FALLBACK_TO_HIGHER_PRIORITY_NODE:
            strcpy(str, MSGSTR(TYPE_HIGHER, "Fallback To Higher Priority Node In The List"));
            break;

        case CL_RGP_NEVER_FALLBACK:
            strcpy(str, MSGSTR(TYPE_NF, "Never Fallback"));
            break;

        case CL_RGP_PREFER_PRIMARY_SITE:
            strcpy(str, MSGSTR(TYPE_P_SITE, "Prefer Primary Site"));
            break;

        case CL_RGP_ONLINE_ON_EITHER_SITE:
            strcpy(str, MSGSTR(TYPE_EITER_SITE, "Online On Either Site"));
            break;

        case CL_RGP_ONLINE_ON_BOTH_SITES:
            strcpy(str, MSGSTR(TYPE_BOTH_SITE, "Online On Both Sites"));
            break;

        case CL_RGP_IGNORE_SITES:
            strcpy(str, MSGSTR(TYPE_IGNOTE_SITE, "Ignore"));
            break;
    }
    return str;
}

/*
 * Name: 	get_catd
 *
 * Description: return catalog file desciptor for the named catalog
 *
 *
 * Arguments:   catalog name
 *
 * Return:	file descriptor handle or CATD_ERR
 *
 * Global data affectec:        none
 *
 */
static
nl_catd get_catd(char *name)
{

	nl_catd cat_id;

        if (! name || strlen (name) == 0)
#ifdef __LINUX__
                return(0);
#else
                return(CATD_ERR);
#endif

        (void) setlocale(LC_ALL, "");
	cat_id = catopen(name, 0);
        return(cat_id);
        
}


#ifdef CLINFO_ODM_LOOKUP
/*
 * Name:        odmget
 *
 * Description: get information from local node's ODM database
 *
 * Arguments:   classname       specifies the name of the ODM class
 *              criteria        specifies the criteria to get ODM info
 *              count           numbers of ODM structures got
 *
 * Return:      odm info struct
 *
 * Global data affectec:        none
 *
 */
caddr_t
odmget(char *classname, char *criteria, int *countp)
{
        CLASS_SYMBOL class;
        caddr_t odmp;
        struct listinfo listinfo;
        int linkdepth = 1;
        int maxlist = 256;

        caddr_t infop = NULL;
        *countp = 0;

        class = odm_mount_class(classname);
        if(class  == (CLASS_SYMBOL) -1)
        {
                /* extern long odmerrno; */
                char *errmsg;
                odm_err_msg(odmerrno, &errmsg);
                fprintf(stderr, "%s: %s\n", classname, errmsg);
                return(NULL);
        }

        odmp = odm_get_list(class, criteria, &listinfo, maxlist, linkdepth);

        if((int) odmp == -1)
        {
                char *errmsg;
                odm_err_msg(odmerrno, &errmsg);
                fprintf(stderr, "odm_get_list: %s\n", errmsg);
                return(NULL);
        }

        if(listinfo.num > 0)
        {
                infop = malloc(listinfo.num * class->structsize);
                if(infop)
                {
                        bcopy(odmp, infop, listinfo.num * class->structsize);
                        *countp = listinfo.num;
                }
                else
                {
                        perror("odmget: malloc");
                }
        }
        if(odmp) odm_free_list(odmp, &listinfo);
        odm_close_class(class);

        return(infop);
}
#endif

