/*
 * bmchealth.c
 *
 * This tool checks the health of the BMC via IPMI.
 * 
 * Author:  Andy Cress  arcress at users.sourceforge.net
 * Copyright (c) 2006 Intel Corporation. 
 * Copyright (c) 2009 Kontron America, Inc.
 *
 * 03/22/06 Andy Cress - created
 * 06/20/06 Andy Cress 0.6 - more vendor strings, add ping_node() stub for now
 * 10/20/06 Andy Cress 1.1 - added -g for guid
 * 01/10/07 Andy Cress 1.4 - added product strings
 * 02/25/07 Andy Cress 2.8 - added more Chassis Status decoding
 */
/*M*
Copyright (c) 2006, Intel Corporation
All rights reserved.

Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are met:

  a.. Redistributions of source code must retain the above copyright notice, 
      this list of conditions and the following disclaimer. 
  b.. Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution. 
  c.. Neither the name of Intel Corporation nor the names of its contributors 
      may be used to endorse or promote products derived from this software 
      without specific prior written permission. 

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *M*/
#ifdef WIN32
#include <windows.h>
#include <stdio.h>
#include "getopt.h"
#elif defined(DOS)
#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include "getopt.h"
#else
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <getopt.h>
#endif
#include <string.h>
#include "ipmicmd.h"
 
#define  SELFTEST_STATUS   0x04
#define  GET_POWER_STATE   0x07
extern int get_BiosVersion(char *str);
extern int GetSDR(int id, int *next, uchar *recdata, int srecdata, int *rlen);
/*
 * Global variables 
 */
static char * progname  = "ihealth";
static char * progver   = "2.72";
static char   fdebug    = 0;
static char   fipmilan  = 0;
static char   fcanonical = 0;
static char   do_hsc    = 0;
static char   do_me     = 0;
static char   do_frusdr = 0;
static char   do_guid   = 0;
static char   do_powerstate  = 1;
static char   do_lanstats    = 0;
static char   set_restore    = 0;
static uchar  restore_policy = 0;
static uchar  bChan     = 0x0e;
static char   fmBMC     = 0;
static char   bdelim    = '=';  /*delimiter to separate name/value pairs*/
static char   bcomma    = ',';  /*comma delimiter, may change if CSV*/

// extern int get_lan_channel(uchar *chan)   /*see ipmicmd.h*/

int get_lan_stats(uchar chan)
{
   uchar idata[2];
   uchar rdata[20];
   int rlen, rv;
   uchar cc;
   ushort *rw;

   /* get BMC LAN Statistics */
   idata[0] = chan;
   idata[1] = 0x00;  /*do not clear stats*/
   rlen = sizeof(rdata);
   rv = ipmi_cmd(GET_LAN_STATS, idata,2, rdata,&rlen, &cc, fdebug); 
   if (fdebug) printf("get_lan_stats: rv = %d, cc = %02x\n",rv,cc);
   if (rv == 0) {
     if (cc == 0) {  /*success, show BMC LAN stats*/
	rw = (ushort *)&rdata[0];
	printf("IPMI LAN channel %d statistics: \n",chan);
	printf(" \tReceived IP Packets      %c %d\n",bdelim,rw[0]);
	printf(" \tRecvd IP Header errors   %c %d\n",bdelim,rw[1]);
	printf(" \tRecvd IP Address errors  %c %d\n",bdelim,rw[2]);
	printf(" \tRecvd IP Fragments       %c %d\n",bdelim,rw[3]);
	printf(" \tTransmitted IP Packets   %c %d\n",bdelim,rw[4]);
	printf(" \tReceived UDP Packets     %c %d\n",bdelim,rw[5]);
	printf(" \tReceived Valid RMCP Pkts %c %d\n",bdelim,rw[6]);
	printf(" \tReceived UDP Proxy Pkts  %c %d\n",bdelim,rw[7]);
	printf(" \tDropped UDP Proxy Pkts   %c %d\n",bdelim,rw[8]);
     } else if (cc == 0xc1) {
	printf("IPMI LAN channel %d statistics: not supported\n",chan);
     }
   }
   return(rv);
}

static int get_selftest_status(uchar *rdata, int rlen)
{
	uchar idata[4];
	uchar ccode;
	int ret;

        ret = ipmi_cmdraw( SELFTEST_STATUS, NETFN_APP,
                        BMC_SA,PUBLIC_BUS,BMC_LUN,
                        idata,0, rdata,&rlen,&ccode, fdebug);
	if (ret == 0 && ccode != 0) ret = ccode;
	return(ret);
}  /*end get_selftest_status()*/

static int get_last_selftest(uchar *val, int vlen)
{
   uchar idata[4]; 
   uchar rdata[16]; 
   int rlen;
   uchar ccode;
   int ret;

   if (val == NULL) return(ERR_BAD_PARAM);
   idata[0] = 0;  /*0=first, 1=next*/
   memset(rdata,0xFF,2);  /*initial value = end-of-list*/
   rlen = sizeof(rdata);
   ret = ipmi_cmdraw( 0x16, 0x30, BMC_SA,PUBLIC_BUS,BMC_LUN,
                        idata,1, rdata,&rlen,&ccode, fdebug);
   if (ret == 0 && ccode != 0) ret = ccode;
   if (ret == 0) {
      if (rlen <= 0) ret = LAN_ERR_BADLENGTH; 
      else {
        if (rlen > vlen) rlen = vlen;  /*truncate if too long*/
        memcpy(val,rdata,rlen);
      }
   }
   return(ret);
}

static int get_chassis_status(uchar *rdata, int rlen)
{
	uchar idata[4];
	uchar ccode;
	int ret;

        ret = ipmi_cmdraw( CHASSIS_STATUS, NETFN_CHAS,
                        BMC_SA,PUBLIC_BUS,BMC_LUN,
                        idata,0, rdata,&rlen,&ccode, fdebug);
	if (ret == 0 && ccode != 0) ret = ccode;
	return(ret);
}  /*end chassis_status()*/

static char *chs_string(uchar state, uchar b2, uchar b3)
{
   static char chs_strbuf[80];
   char *pstr;
   pstr = &chs_strbuf[0];
   if (state & 0x01) strcpy(pstr,"on");
   else strcpy(pstr,"off");
   if (state & 0x02) strcat(pstr,", overload");
   if (state & 0x04) strcat(pstr,", interlock");
   if (state & 0x08) strcat(pstr,", fault");
   if (state & 0x10) strcat(pstr,", control error");
   if (state & 0x20) strcat(pstr,", restore=last_state");
   else if (state & 0x40) strcat(pstr,", restore=turn_on");
   else strcat(pstr,", restore=stay_off");
   strcat(pstr,", last_pwr=");
   if (b2 & 0x10) strcat(pstr,"IPMI ");
   if (b2 & 0x08) strcat(pstr,"fault ");
   if (b2 & 0x04) strcat(pstr,"interlock ");
   if (b2 & 0x02) strcat(pstr,"overload ");
   if (b2 & 0x01) strcat(pstr,"ACfailed");
   pstr = &chs_strbuf[0];
   return(pstr);
}

static int get_power_state(uchar *rdata, int rlen)
{
	uchar idata[4];
	uchar ccode;
	int ret;

        ret = ipmi_cmdraw( GET_POWER_STATE, NETFN_APP,
                        BMC_SA,PUBLIC_BUS,BMC_LUN,
                        idata,0, rdata,&rlen,&ccode, fdebug);
	if (ret == 0 && ccode != 0) ret = ccode;
	return(ret);
}  /*end get_power_state()*/

static char *pwr_string(uchar pstate)
{
   char *pstr;
   switch(pstate) {
      case 0x00: pstr = "S0: working"; break; 
      case 0x01: pstr = "S1: clock stopped, context ok"; break; 
      case 0x02: pstr = "S2: clock stopped, context lost"; break; 
      case 0x03: pstr = "S3: suspend-to-RAM"; break; 
      case 0x04: pstr = "S4: suspend-to-Disk"; break; 
      case 0x05: pstr = "S5: soft off"; break; 
      case 0x06: pstr = "S4/S5: soft off, either S4 or S5"; break; 
      case 0x07: pstr = "G3: mechanical off"; break; 
      case 0x08: pstr = "S1-S3: sleeping"; break; 
      case 0x09: pstr = "S1-S4: sleeping"; break; 
      case 0x0A: pstr = "S5/o: soft off by override"; break; 
      case 0x20: pstr = "legacy on"; break; 
      case 0x21: pstr = "legacy soft-off"; break; 
      case 0x2a:  /* not initialized or device lost track of state */
      default:   pstr = "unknown"; break;
   }
   return(pstr);
}

#ifdef PING_OK
extern int ping_bmc(char *node, char fdebug);

static int ping_node(char *node)
{
   int rv = 0;
   /* verify that the BMC LAN channel is configured & active */
   /* send rmcp_ping to node's BMC */
   rv = ping_bmc(node,fdebug);
   return(rv);
}
#endif

#define MIN_SDR_SZ  8
static int get_frusdr_version(char *pver, int sver)
{
   ushort recid;
   int recnext;
   int ret, sz, i, len;
   uchar sdr[MAX_BUFFER_SIZE];
   char verstr[30];
   char fIntel;
   int verlen;

   recid = 0;
   verstr[0] = 0;
   verlen = 0;
   while (recid != 0xffff) 
   {
      memset(sdr,0,sizeof(sdr)); 
      ret = GetSDR(recid,&recnext,sdr,sizeof(sdr),&sz);
      if (fdebug)
         printf("GetSDR[%04x]: ret = %x, next=%x\n",recid,ret,recnext);
      if (ret != 0) {
         if (ret > 0) {  /* ret is a completion code error */
            if (fdebug)
                printf("%04x GetSDR error 0x%02x %s, rlen=%d\n",recid,ret,
			decode_cc((ushort)0,(uchar)ret),sz);
         } else printf("%04x GetSDR error %d, rlen = %d\n", recid,ret,sz);
         if (sz < MIN_SDR_SZ) {  /* don't have recnext, so abort */
              break;
         } /* else fall through & continue */
      } else { /*got SDR */
	 len = sdr[4] + 5;
	 if (sdr[3] == 0xC0) {  /* OEM SDR */
	     /* check for Intel mfg id  */
	     if ((sdr[5] == 0x57) && (sdr[6] == 0x01) && (sdr[7] == 0x00))
		fIntel = 1;
	     else fIntel = 0;
	     if (sdr[8] == 0x53) {  /*Intel OEM subtype, ASCII 'S' */
		verlen = 0;
		for (i = 8; i < len; i++) {
		   if (sdr[i] == 0) break;
		   if (i >= sizeof(verstr)) break;
                   verstr[verlen++] = sdr[i];
            	}
                verstr[verlen] = 0;  /*stringify*/
		/* continue on past SDR File, get SDR Package version */
		// break;
	     }
         }  /*endif OEM SDR*/
      }
      if (recnext == recid) recid = 0xffff; /*break;*/
      else recid = recnext;
   }
   if (verlen > sver) verlen = sver;
   if (fdebug) 
	printf("get_frusdr_version: verstr=%s, verlen=%d\n",verstr,verlen);
   strncpy(pver,verstr,verlen);
   return(ret);
}

static int get_hsc_devid(uchar *rdata, int rlen)
{
	uchar ccode;
	int ret;

        ret = ipmi_cmdraw( 0x01, /*(GET_DEVICEID & 0x00ff)*/ 
			NETFN_APP, 0xC0,PUBLIC_BUS,BMC_LUN,
                        NULL,0, rdata,&rlen,&ccode, fdebug);
	if (ret == 0 && ccode != 0) ret = ccode;
	return(ret);
}  /*end get_hsc_devid*/

static int get_chan_auth(uchar chan, uchar *rdata, int rlen)
{
	uchar idata[4];
	uchar ccode;
	int ret;

        idata[0] = chan; /*0x0e = this channel*/
        idata[1] = 0x02; /*user level*/
        ret = ipmi_cmdraw( 0x38, /*(GET_CHAN_AUTH & 0x00ff)*/ 
			NETFN_APP, BMC_SA, PUBLIC_BUS,BMC_LUN,
                        idata,2, rdata,&rlen,&ccode, fdebug);
	if (ret == 0 && ccode != 0) ret = ccode;
	return(ret);
}  /*end get_chan_auth*/

void show_chan_auth(char *tag, uchar *rec, int srec)
{
   char pstr[40];
   pstr[0] = 0;
   if (rec[1] & 0x01) strcat(pstr,"None ");
   if (rec[1] & 0x02) strcat(pstr,"MD2 ");
   if (rec[1] & 0x04) strcat(pstr,"MD5 ");
   if (rec[1] & 0x10) strcat(pstr,"Straight_Passwd ");
   if (rec[1] & 0x20) strcat(pstr,"OEM ");
   printf("Chan %d AuthTypes %c %s\n",rec[0],bdelim,pstr);
   printf("Chan %d Status    %c %02x, OEM ID %02x%02x%02x OEM Aux %02x\n",
          rec[0],bdelim,rec[2],rec[4],rec[5],rec[6],rec[7]);
}

#define BMC  1
#define HSC  2

 /* For a list of all IANA enterprise mfg vendor numbers, 
  * see http://www.iana.org/assignments/enterprise-numbers 
  * Product numbers are different for each mfg vendor.  */
#define N_MFG  36
struct { int val; char *pstr; } mfgs[N_MFG] = {
    0,   " ",
    0x000002, "IBM",
    0x00000B, "HP",
    0x0000BA, "Toshiba",
    0x000074, "Hitachi",
    0x00018F, "Hitachi",
    0x000175, "Tatung", 
    0x000F85, "Aelita Software", /*3973. HP DL140*/
    0x0028B2, "Avocent", 
    0x002B5E, "OSA",
    0x0035AE, "Raritan",   /*13742.*/
    0x0051EE, "AMI",   
          94, "Nokia-Siemens",
         107, "Bull",
        4337, "Radisys",
        5593, "Magnum Technologies",
        6569, "Inventec",
        9237, "Newisys",
       11129, "Google",
       12634, "PICMG",
       16394, "Pigeon Point",
       20569, "Inventec ESC",
       24673, "ServerEngines",
       47488, "WinBond",
    VENDOR_DELL, "Dell",    /*0x0002A2*/
    VENDOR_KONTRON,    "Kontron",     /*=0x003A98, 15000.*/
    VENDOR_SUPERMICRO, "SuperMicro",  /*=0x002A7C used in AOC-SIMSO*/
    VENDOR_FUJITSU,    "Fujitsu-Siemens", /* 0x002880, 10368. */
    VENDOR_PEPPERCON,  "Peppercon",  /* 0x0028C5, 10437. now w Raritan*/
    VENDOR_MICROSOFT,  "Microsoft",  /*=0x000137, 311.*/
    VENDOR_NEC,  "NEC",     /*=0x000077*/
    VENDOR_NSC,  "NSC",     /*=0x000322*/
    VENDOR_LMC,  "LMC",     /*=0x000878 with SuperMicro*/
    VENDOR_TYAN, "Tyan",    /*=0x0019FD*/
    VENDOR_SUN,  "Sun",  
    VENDOR_INTEL, "Intel"   /*=0x000157*/
};

char * get_mfg_str(uchar *rgmfg, int *pmfg)
{
      char *mfgstr = "";
      int mfg;
      int i;
      mfg  = rgmfg[0] + (rgmfg[1] << 8) + (rgmfg[2] << 16);
      if (pmfg != NULL) *pmfg = mfg;  /*vend_id*/
      for (i = 0; i < N_MFG; i++) {
	 if (mfgs[i].val == mfg) {
	    mfgstr = mfgs[i].pstr; 
	    break; 
	 }
      }
      if (i >= N_MFG) mfgstr = mfgs[0].pstr;
      return(mfgstr);
}

void show_devid_all(int dtype, uchar *devrec, int sdevrec)
{
   uchar ipmi_maj = 0;
   uchar ipmi_min = 0;
   char *tag; 
   int mfg, prod;
   char *mfgstr = "";
   char *prodstr = "";

      ipmi_maj = devrec[4] & 0x0f;
      ipmi_min = devrec[4] >> 4;
      prod = devrec[9] + (devrec[10] << 8);

      mfgstr = get_mfg_str(&devrec[6],&mfg);
      if (dtype == HSC) tag = "HSC";
      else {
	tag = "BMC";
	/* The product ids below only apply to BMCs */
	switch(mfg) {
         case VENDOR_NSC:      /*=0x000322*/
	     fmBMC = 1;
             if (dtype == BMC) tag="mBMC";
             if (prod == 0x4311) prodstr = "(TIGPT1U)"; /*Intel*/
             break;
         case VENDOR_TYAN:      /*=0x0019fd*/
             switch(prod) {     /* show product names for some */
                 case 0x0b41:   prodstr = "(M3289)"; break;
                 case 0x0f98:   prodstr = "(M3291)"; break;
                 case 0x137d:   prodstr = "(S4989)"; break;
                 case 0x13ee:   prodstr = "(S5102)"; break;
                 case 0x14fc:   prodstr = "(S5372)"; break;
                 default:       prodstr = ""; break;
             }
             break;
         case VENDOR_FUJITSU:      /*=0x002880*/
	     if (prod >= 0x200) prodstr = "(iRMC S2)"; 
	     else prodstr = "";
             break;
         case VENDOR_INTEL:     /*=0x000157*/
             switch(prod) {     /* show product names for some */
                 case 0x000C:   prodstr = "(TSRLT2)";    /*SCB2*/
                                bChan = 7;  break;
                 case 0x001B:   prodstr = "(TIGPR2U)";   /*SWV2*/
                                bChan = 7;  break;
                 case 0x0022:   prodstr = "(TIGI2U)"; break;   /*SJR2*/
                 case 0x0026:   prodstr = "(Bridgeport)"; break;
                 case 0x0028:   prodstr = "(S5000PAL)"; break; /*Alcolu*/
                 case 0x0029:   prodstr = "(S5000PSL)"; break; /*StarLake*/
                 case 0x002D:   prodstr = "(MFSYS25)"; break; /*ClearBay*/
                 case 0x003E:   prodstr = "(S5520UR)";   /*CG2100 or NSN2U*/
				do_me = 1;
#ifdef LINUX
	           		set_max_kcs_loops(1000); /*longer KCS timeout*/
#endif
                                bChan = 1;  break;
                 case 0x0100:   prodstr = "(Tiger4)"; break;
                 case 0x0103:   prodstr = "(McCarran)";  /*BladeCenter*/
			do_powerstate = 0;   break; 
                 case 0x0800:   prodstr = "(ZT5504)";    /*ZiaTech*/
			do_powerstate = 0;   break; 
                 case 0x0808:   prodstr = "(MPCBL0001)";   /*ATCA Blade*/
			do_powerstate = 0;   break; 
                 case 0x0841:   prodstr = "(MPCMM0001)";   /*ATCA CMM*/
			do_powerstate = 0;   break; 
                 case 0x0811:   prodstr = "(TIGW1U)";  break; /*S5000PHB*/
                 case 0x4311:   prodstr = "(NSI2U)";     /*SE7520JR23*/
				if (dtype == BMC) tag="mBMC";
				fmBMC = 1; break;
                 default:       prodstr = ""; break;
             }
             break;
         case VENDOR_KONTRON:     /*=0x003A98=15000.*/
             switch(prod) {     /* show product names for some */
                 case 0x1590:   prodstr = "(KTC5520)";  break;
                 default:       prodstr = ""; break;
             }
             break;
         case VENDOR_PEPPERCON:  /*=0x0028c5*/
	     if (prod == 0x0004) prodstr = "(AOC-IPMI20)";
             break;
         case 0x00000B: /*HP, does not support get_power_state cmd*/
	     do_powerstate = 0;
         default:
             prodstr = "";
             break;
	} /*end switch(prod)*/
      } /*end-else BMC*/
      {  /* BMC version */
         printf("%s version      %c %x.%02x%c IPMI v%d.%d\n",
		tag,bdelim,devrec[2],devrec[3],bcomma,ipmi_maj,ipmi_min);
      }
      printf("%s manufacturer %c %06x (%s)%c product %c %04x %s\n",
		tag, bdelim,mfg,mfgstr,bcomma,bdelim,prod,prodstr);
      /* could show product rev, if available (sdevrec > 14) */
      return;
}

int GetPowerOnHours(unsigned int *val)
{
   uchar resp[MAX_BUFFER_SIZE];
   int sresp = MAX_BUFFER_SIZE;
   uchar cc;
   int rc = -1;
   int i;
   unsigned int hrs;

   *val = 0;
   if (fmBMC) return(rc);
   sresp = MAX_BUFFER_SIZE;
   memset(resp,0,6);  /* default response size is 5 */
   rc = ipmi_cmd_mc(GET_POWERON_HOURS, NULL, 0, resp, &sresp, &cc, fdebug);
   if (rc == 0 && cc == 0) {
      /* show the hours (32-bits) */
      hrs = resp[1] | (resp[2] << 8) | (resp[3] << 16) | (resp[4] << 24);
      if (resp[0] == 60) /*normal*/ i = 1;
      else {
                i = 60 / resp[0];
                hrs = hrs / i;
      }
      *val = hrs;
   }
   return(rc);
}

#ifdef METACOMMAND
int i_health(int argc, char **argv)
#else
#ifdef WIN32
int __cdecl
#else
int
#endif
main(int argc, char **argv)
#endif
{
   int ret = 0;
   int c;
   uchar selfbuf[16];
   uchar devrec[30];
   char biosver[80];
   uchar cc;
   int selfstatus;
   uchar pwr_state;
   uchar chs_status;
   char selfstr[30];
   char *s;
   int i, sresp;
   uint n;
   int rlen;
   uchar idata[4];
   uchar rdata[16];

   printf("%s ver %s\n", progname,progver);

   while ( (c = getopt( argc, argv,"cfghp:sT:V:J:YEF:P:N:R:U:Z:x?")) != EOF ) 
      switch(c) {
          case 'x': fdebug = 1;     break;  /* debug messages */
          case 'h': do_hsc = 1;     break;  /* check the HSC too */
          case 'f': do_frusdr = 1;  break;  /* check the FRUSDR too */
          case 'g': do_guid = 1;    break;  /* get the System GUID also */
          case 's': do_lanstats = 1;  break;  /* get the LAN stats too */
          case 'p': set_restore = 1; 		/* set the restore policy */
		    restore_policy = atoi(optarg);
		    if (restore_policy > 2) restore_policy = 1;
		    break; 
          case 'c': fcanonical = 1;
		    bdelim = BDELIM;  break;  /* canonical output */
          case 'N':    /* nodename */
          case 'U':    /* remote username */
          case 'P':    /* remote password */
          case 'R':    /* remote password */
          case 'E':    /* get password from IPMI_PASSWORD environment var */
          case 'F':    /* force driver type */
          case 'T':    /* auth type */
          case 'J':    /* cipher suite */ 
          case 'V':    /* priv level */
          case 'Y':    /* prompt for remote password */
          case 'Z':    /* set local MC address */
                parse_lan_options(c,optarg,fdebug);
                break;
	  default:
                printf("Usage: %s [-cfghsx -N node -U user -P/-R pswd -EFTVY]\n", progname);
                printf(" where -x   show eXtra debug messages\n");
                printf("       -c   canonical output\n");
                printf("       -f   get the FRUSDR version also\n");
                printf("       -g   get the System GUID also\n");
                printf("       -h   check the HotSwap Controller also\n");
		printf("       -p1  set restore policy: 0=off, 1=last, 2=on\n");
                printf("       -s   get the IPMI LAN statistics also\n");
		print_lan_opt_usage();
		ret = ERR_USAGE;
		goto health_end;
      }

   fipmilan = is_remote();
   if (fipmilan && set_restore) 
             parse_lan_options('V',"4",0);  /*if set, request admin priv*/
	
   ret = ipmi_getdeviceid(devrec,16,fdebug);
   if (ret != 0) {
	goto health_end;
   } else {
       show_devid_all(BMC,devrec,12);
   }

   if (!fipmilan) {  /*get local BIOS version*/
	biosver[0] = 0;
	ret = get_BiosVersion(biosver);
	if (ret == 0) printf("BIOS Version     %c %s\n",bdelim,biosver);
   }
   if (do_me) {  /* ME version for Intel S5500 motherboards */
        rlen = sizeof(rdata);
        ret = ipmi_cmdraw((GET_DEVICE_ID & 0xff), NETFN_APP,ME_SA,ME_BUS,0x00,
			  idata,0,rdata,&rlen,&cc,fdebug);
        if (ret == 0 && cc != 0) ret = cc;
	if (ret == 0) printf("ME Firmware Ver  %c %x.%x.%02x%02x\n",bdelim,
				rdata[2],rdata[3],rdata[12],rdata[13]);
   }

   i = get_driver_type();
   printf("IPMI driver type %c %d        (%s)\n",bdelim,i,show_driver_type(i));

   ret = get_chassis_status(selfbuf,4);
   if (ret != 0) {
	printf("Cannot do get_chassis_status, ret = %d\n",ret);
	goto health_end;
   } else {
        chs_status = selfbuf[0] & 0x7f;
	printf("Chassis Status   %c %02x %02x %02x (%s)\n",
		bdelim,chs_status,selfbuf[1],selfbuf[2],
		chs_string(chs_status,selfbuf[1],selfbuf[2]));
   }

   if (do_powerstate) 
   {  /* Some BMCs dont support get_power_state*/
     ret = get_power_state(selfbuf,4);
     if (ret != 0) {
	printf("ipmi_getpowerstate error, ret = %d\n",ret);
	goto health_end;
     } else {
        pwr_state = selfbuf[0] & 0x7f;
	printf("Power State      %c %02x       (%s)\n",
		bdelim,pwr_state,pwr_string(pwr_state));
     }
   }

   ret = get_selftest_status(&selfbuf[0],sizeof(selfbuf));
   if (ret != 0) {
	printf("get_selftest_status error, ret = %x\n",ret);
	goto health_end;
   } else {
        selfstatus = selfbuf[0] + (selfbuf[1] << 8);
        switch(selfstatus) {
           case 0x0055: s = "(OK)";   break;
           default:     s = "(Err)";  break;
        }
        if (fmBMC) {
           sprintf(selfstr,"%s",s);
	} else {
	   ret = get_last_selftest(&selfbuf[0],sizeof(selfbuf));
	   if (fdebug) printf("get_last_selftest ret = %x, %02x%02x\n",
				ret, selfbuf[1],selfbuf[0]);
	   if (ret == 0 && (selfbuf[0] != 0xFF)) {
	      sprintf(selfstr,"%s, last = %02x%02x",s,selfbuf[1],selfbuf[0]);
	   } else sprintf(selfstr,"%s",s);
	   ret = 0;  /*ignore any errors with get_last_selftest*/
	}
	printf("Selftest status  %c %04x     %s\n",bdelim,selfstatus,selfstr);
   }

   if (do_guid) {
      sresp = sizeof(devrec);
      ret = ipmi_cmd(GET_SYSTEM_GUID,NULL,0,devrec,&sresp,&cc,fdebug);
      if (fdebug) printf("system_guid: ret = %d, cc = %x\n",ret,cc);
      if (ret == 0 && cc == 0) {
         printf("System GUID      %c ",bdelim);
         for (i=0; i<16; i++) {
            if ((i == 4) || (i == 6) || (i == 8) || (i == 10)) s = "-";
            else s = "";
            printf("%s%02x",s,devrec[i]);
         }
         printf("\n");
      }
   }

   if (bChan != 7) 
      ret = get_lan_channel(&bChan);

   if (fmBMC == 0) {
      ret = GetPowerOnHours(&n);
      if (ret == 0) 
	 printf("Power On Hours   %c %d hours (%d days)\n",bdelim,n,(n/24));

      ret = get_chan_auth(bChan,&devrec[0],sizeof(devrec));
      if (ret == 0) 
         show_chan_auth("Channel Auth Cap",devrec,8);
      else 
         printf("get_chan_auth error: ret = %x\n",ret);
   }

   if (do_hsc) {
      if (fmBMC) printf("No HSC present\n");
      else {
	 /* Get HSC status */
	 ret = get_hsc_devid(&devrec[0],sizeof(devrec));
	 if (ret == 0) {  /* only if HSC is detected */
            show_devid_all(HSC,devrec,14);
	 }
      }
   }

   if (do_frusdr) {
      ret = get_frusdr_version((char *)&devrec[0],sizeof(devrec));
      if (ret == 0) printf("FRU/SDR Version  %c %s\n",bdelim,devrec);
      else          printf("FRU/SDR Version  %c error %d\n",bdelim,ret);
   }

   if (do_lanstats) {
	ret = get_lan_stats(bChan);
   }

#ifdef PING_OK
   {
     char *node;
     /* Currently some problems with this:
      * works first time, but locks up BMC LAN on subsequent attempts.
      */
     node = get_node();
     ret = ping_node(node);
     printf("ping_node(%s): ret = %d\n",node,ret);
   }
#endif

   if (set_restore) {
	idata[0] = restore_policy; /* 1=last_state, 2=turn_on, 0=stay_off*/
        rlen = sizeof(rdata);
        ret = ipmi_cmdraw(0x06 , NETFN_CHAS, BMC_SA,PUBLIC_BUS,BMC_LUN,
			  idata,1,rdata,&rlen,&cc,fdebug);
        if (ret == 0 && cc != 0) ret = cc;
        printf("set_restore_policy(%x): ret = %d\n",restore_policy,ret);
   }

health_end:
   ipmi_close_();
   // show_outcome(progname,ret);  
   return (ret);
}  /* end main()*/

/* end bmchealth.c */
