summaryrefslogtreecommitdiff
path: root/util/ihealth.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/ihealth.c')
-rw-r--r--util/ihealth.c1153
1 files changed, 1153 insertions, 0 deletions
diff --git a/util/ihealth.c b/util/ihealth.c
new file mode 100644
index 0000000..6f6f0e1
--- /dev/null
+++ b/util/ihealth.c
@@ -0,0 +1,1153 @@
+/*
+ * ihealth.c (was 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 <stdlib.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>
+#if defined(HPUX)
+/* getopt is defined in stdio.h */
+#elif defined(MACOS)
+/* getopt is defined in unistd.h */
+#include <unistd.h>
+#else
+#include <getopt.h>
+#endif
+#endif
+#include <string.h>
+#include "ipmicmd.h"
+#include "oem_intel.h"
+
+#define SELFTEST_STATUS 0x04
+#define GET_POWER_STATE 0x07
+extern int get_BiosVersion(char *str);
+extern int get_SystemGuid(uchar *guid);
+extern int GetSDR(int id, int *next, uchar *recdata, int srecdata, int *rlen);
+extern int get_device_guid(char *pbuf, int *sz); /*subs.c*/
+extern int oem_supermicro_get_health(char *pstr, int sz); /*oem_supermicro.c*/
+extern int oem_supermicro_get_firmware_str(char *pstr, int sz); /*oem_supermicro.c*/
+
+/*
+ * Global variables
+ */
+static char * progname = "ihealth";
+static char * progver = "2.93";
+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 do_session = 0;
+static char do_systeminfo = 0;
+static char set_restore = 0;
+static char set_name = 0;
+static char set_os = 0;
+static char set_os2 = 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*/
+static char lan_ch_restrict = 0;
+static int kcs_loops = 0;
+static int vend_id = 0;
+static int prod_id = 0;
+static char *pname = NULL;
+static char *pos = NULL;
+static char *pos2 = NULL;
+static uchar g_bus = PUBLIC_BUS;
+static uchar g_sa = BMC_SA;
+static uchar g_lun = BMC_LUN;
+static uchar g_addrtype = ADDR_SMI;
+
+
+int oem_get_health(char *pstr, int sz)
+{
+ int rv;
+ switch(vend_id) {
+ case VENDOR_PEPPERCON:
+ case VENDOR_SUPERMICRO:
+ rv = oem_supermicro_get_health(pstr, sz);
+ break;
+ case VENDOR_SUPERMICROX:
+ rv = oem_supermicro_get_firmware_str(pstr,sz);
+ break;
+ default:
+ rv = LAN_ERR_NOTSUPPORT;
+ break;
+ }
+ if (fdebug) printf("oem_get_health rv = %d\n",rv);
+ return rv;
+}
+
+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);
+}
+
+int get_session_info(uchar idx, int hnd, uchar *rdata, int *len)
+{
+ uchar idata[5];
+ int ilen, rlen, rv;
+ uchar cc;
+
+ ilen = 1;
+ idata[0] = idx;
+ if (idx == 0xFE) {
+ idata[1] = (uchar)hnd;
+ ilen = 2;
+ } else if (idx == 0xFF) {
+ idata[1] = (uchar)(hnd & 0x000000FF);
+ idata[2] = (uchar)((hnd & 0x0000FF00) >> 8);
+ idata[3] = (uchar)((hnd & 0x00FF0000) >> 16);
+ idata[4] = (uchar)((hnd & 0xFF000000) >> 24);
+ ilen = 5;
+ }
+ rlen = *len;
+ *len = 0;
+ rv = ipmi_cmdraw(CMD_GET_SESSION_INFO,NETFN_APP,
+ g_sa, g_bus, g_lun,
+ idata,ilen, rdata,&rlen,&cc, fdebug);
+ if (fdebug) printf("get_lan_stats: rv = %d, cc = %02x\n",rv,cc);
+ if (rv == 0) *len = rlen;
+ if ((rv == 0) && (cc != 0)) rv = cc;
+ return(rv);
+}
+
+static char *sesstype_str(uchar c)
+{
+ uchar b;
+ char *s;
+ b = ((c & 0xf0) >> 4);
+ switch(b) {
+ case 0: s = "IPMIv1.5"; break;
+ case 1: s = "IPMIv2/RMCP+"; break;
+ default: s = "Other"; break;
+ }
+ return s;
+}
+
+static void show_session_info(uchar idx, uchar *sinfo,int len)
+{
+ int i;
+ char lan_type = 1;
+ if (fdebug) {
+ printf("Raw Session Info[%d]: ",idx);
+ for (i = 0; i < len; i++) printf("%02x ",sinfo[i]);
+ printf("\n");
+ }
+
+ printf("Session Info[%d]:\n",idx);
+ printf("\tSession Handle %c %d\n",bdelim,sinfo[0]);
+ printf("\tSession Slot Count %c %d\n",bdelim,(sinfo[1] & 0x3f));
+ printf("\tActive Sessions %c %d\n",bdelim,(sinfo[2] & 0x3f));
+ if (len <= 3) return;
+ printf("\tUser ID %c %d\n",bdelim,(sinfo[3] & 0x3f));
+ printf("\tPrivilege Level %c %d\n",bdelim,(sinfo[4] & 0x0f));
+ printf("\tSession Type %c %s\n",bdelim,sesstype_str(sinfo[5]));
+ printf("\tChannel Number %c %d\n",bdelim,(sinfo[5] & 0x0f));
+ if (len <= 6) return;
+ if (lan_type) {
+ printf("\tConsole IP %c %d.%d.%d.%d\n",bdelim,
+ sinfo[6],sinfo[7],sinfo[8],sinfo[9]);
+ printf("\tConsole MAC %c %02x:%02x:%02x:%02x:%02x:%02x\n",bdelim,
+ sinfo[10], sinfo[11], sinfo[12],
+ sinfo[13], sinfo[14], sinfo[15]);
+ printf("\tConsole Port %c %d\n",bdelim,
+ sinfo[16]+ (sinfo[17] << 8));
+ }
+}
+
+int get_session_info_all(void)
+{
+ int rv, len, nslots, i;
+ uchar sinfo[24];
+ nslots = 1;
+ for (i = 1; i <= nslots; i++) {
+ len = sizeof(sinfo);
+ rv = get_session_info(i,0,sinfo,&len);
+ if (fdebug) printf("get_session_info(%d): rv = %d\n",i,rv);
+ if (rv != 0) {
+ if ((rv == 0xCB) || (rv == 0xCC)) {
+ if (len >= 3) show_session_info(i,sinfo,len);
+ if (i > 1) rv = 0; /*no such idx, end */
+ }
+ break;
+ }
+ nslots = (sinfo[1] & 0x3F);
+ show_session_info(i,sinfo,len);
+ }
+ 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,
+ g_sa, g_bus, g_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, g_sa, g_bus, g_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 *rsz)
+{
+ uchar idata[4];
+ uchar ccode;
+ int rlen;
+ int ret;
+
+ rlen = *rsz;
+ ret = ipmi_cmdraw( CHASSIS_STATUS, NETFN_CHAS,
+ g_sa, g_bus, g_lun,
+ idata,0, rdata,&rlen,&ccode, fdebug);
+ if (ret == 0 && ccode != 0) ret = ccode;
+ if (ret == 0) *rsz = rlen;
+ return(ret);
+} /*end chassis_status()*/
+
+static void show_chs_status(uchar *sbuf, int slen)
+{
+ char chs_strbuf[80];
+ char *pstr;
+ uchar state, b2, b3, b4;
+
+ pstr = &chs_strbuf[0];
+ state = sbuf[0] & 0x7f;
+ b2 = sbuf[1];
+ b3 = sbuf[2];
+ sprintf(pstr,"%s",(state & 0x01) ? "on" : "off");
+ printf("Chassis Status %c %02x %02x %02x %02x (%s, see below)\n",
+ bdelim,state,sbuf[1],sbuf[2],sbuf[3],pstr);
+ sprintf(pstr,"\tchassis_power %c ",bdelim);
+ if (state & 0x01) strcat(pstr,"on");
+ else strcat(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");
+ printf("%s\n",pstr);
+
+ sprintf(pstr,"\tpwr_restore_policy %c ",bdelim);
+ if (state & 0x20) strcat(pstr,"last_state");
+ else if (state & 0x40) strcat(pstr,"turn_on");
+ else strcat(pstr,"stay_off");
+ printf("%s\n",pstr);
+
+ if (b2 != 0) {
+ sprintf(pstr,"\tlast_power_event %c ",bdelim);
+ 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");
+ printf("%s\n",pstr);
+ }
+ printf("\tchassis_intrusion %c %s\n", bdelim,
+ (b3 & 0x01) ? "active":"inactive");
+ printf("\tfront_panel_lockout %c %s\n", bdelim,
+ (b3 & 0x02) ? "active":"inactive");
+ printf("\tdrive_fault %c %s\n", bdelim,
+ (b3 & 0x04) ? "true":"false");
+ printf("\tcooling_fan_fault %c %s\n", bdelim,
+ (b3 & 0x08) ? "true":"false");
+ if (slen > 3) {
+ b4 = sbuf[3];
+ if (b4 & 0x80) {
+ printf("\tFP sleep_button_disable %c allowed, button %s\n",bdelim,
+ (b4 & 0x08) ? "disabled":"enabled");
+ }
+ if (b4 & 0x40) {
+ printf("\tFP diag_button_disable %c allowed, button %s\n",bdelim,
+ (b4 & 0x04) ? "disabled":"enabled");
+ }
+ if (b4 & 0x20) {
+ printf("\tFP reset_button_disable %c allowed, button %s\n",bdelim,
+ (b4 & 0x02) ? "disabled":"enabled");
+ }
+ if (b4 & 0x10) {
+ printf("\tFP power_button_disable %c allowed, button %s\n",bdelim,
+ (b4 & 0x01) ? "disabled":"enabled");
+ }
+ }
+ return;
+}
+
+static int get_power_state(uchar *rdata, int rlen)
+{
+ uchar idata[4];
+ uchar ccode;
+ int ret;
+
+ ret = ipmi_cmdraw( GET_POWER_STATE, NETFN_APP,
+ g_sa, g_bus, g_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 = (ushort)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; /*priv level = user*/
+ ret = ipmi_cmdraw( 0x38, NETFN_APP, /*CMD_GET_CHAN_AUTH_CAP*/
+ g_sa, g_bus, g_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);
+ if (do_hsc) /*only show this if extra output*/
+ 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
+
+#ifdef MOVED
+/* moved to subs.c*/
+#define N_MFG 41
+struct { int val; char *pstr; } mfgs[N_MFG] = { };
+char * get_iana_str(int mfg);
+#endif
+
+char * get_mfg_str(uchar *rgmfg, int *pmfg)
+{
+ char *mfgstr = "";
+ int mfg;
+ mfg = rgmfg[0] + (rgmfg[1] << 8) + (rgmfg[2] << 16);
+ if (pmfg != NULL) *pmfg = mfg; /*vend_id*/
+ mfgstr = get_iana_str(mfg);
+ return(mfgstr);
+}
+
+/* int get_system_info(uchar parm, char *pbuf, int *szbuf); *see subs.c*/
+/* int set_system_info(uchar parm, uchar *pbuf, int *szbuf); *see subs.c*/
+
+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 = "";
+ char prodoem[40];
+ char extraver[32];
+ int i, j, k, l, rv;
+
+ ipmi_maj = devrec[4] & 0x0f;
+ ipmi_min = devrec[4] >> 4;
+ prod = devrec[9] + (devrec[10] << 8);
+ mfgstr = get_mfg_str(&devrec[6],&mfg);
+ vend_id = mfg;
+ prod_id = prod;
+ extraver[0] = 0;
+ 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_SUN: /*=0x00002a*/
+ if (prod == 0x4701) prodstr = "(X4140)";
+ 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_CISCO: /*=0x00168b*/
+ if (prod == 0x0005) prodstr = "(UCS C200)";
+ else prodstr = "";
+ if (fipmilan) lan_ch_restrict = 1; /*fw bug, gets 0xC1 on ipmilan*/
+ break;
+ case VENDOR_INTEL: /*=0x000157*/
+ if (do_hsc && (dtype == BMC)) /*if HSC option, also show extra*/
+ sprintf(extraver," (Boot %x.%x PIA %x.%x)", /*BMC extra*/
+ devrec[11],devrec[12],devrec[13],devrec[14]);
+ 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 0x002B: prodstr = "(S5000VSA)"; break;
+ case 0x002D: prodstr = "(MFSYS25)"; break; /*ClearBay*/
+ case 0x003E: prodstr = "(S5520UR)"; /*CG2100 or NSN2U*/
+ do_me = 1; kcs_loops = URNLOOPS;
+ bChan = 1; break;
+ case 0x0040: prodstr = "(QSSC-S4R)"; /*Stoutland*/
+ do_me = 1; kcs_loops = URNLOOPS;
+ 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;
+ }
+ if (is_romley(mfg,prod)) {
+ intel_romley_desc(mfg,prod,&prodstr);
+ snprintf(prodoem,sizeof(prodoem),"(%s)",prodstr);
+ prodstr = prodoem;
+ do_me = 1; kcs_loops = URNLOOPS;
+ do_hsc = 1; /*the HSC is embedded, so not the same*/
+ sprintf(extraver,".%d (Boot %x.%x)", /*BMC extra*/
+ (devrec[13] + (devrec[14] << 8)),devrec[11],devrec[12]);
+ }
+ break;
+ case VENDOR_KONTRON: /*=0x003A98=15000.*/
+ i = devrec[11] + (devrec[12] << 8);
+ j = devrec[13] + (devrec[14] << 8);
+ k = 0; l = 0;
+ { /* get Kontron firmware version with OEM cmd */
+ int rlen;
+ uchar idata[4];
+ uchar rdata[16];
+ uchar cc;
+ rlen = sizeof(rdata);
+ idata[0] = 0;
+ idata[1] = 0;
+ idata[2] = 1;
+ rv = ipmi_cmdraw(0x2f, 0x2c, g_sa, g_bus, g_lun,
+ idata,3,rdata,&rlen,&cc,fdebug);
+ if (rv == 0 && cc == 0) {
+ k = rdata[1];
+ l = rdata[2];
+ }
+ }
+ sprintf(extraver,".%02d.%02d (FW %x.%x)",i,j,k,l);
+ switch(prod) { /* show product names for some */
+ case 0x1590: prodstr = "(KTC5520)"; break;
+ default: prodstr = ""; break;
+ }
+ break;
+ case VENDOR_PEPPERCON: /*=0x0028c5 Peppercon/Raritan */
+ if (prod == 0x0004) prodstr = "(AOC-IPMI20)"; /*SuperMicro*/
+ else if (prod == 0x0007) prodstr = "(RMM2)"; /*Intel RMM2*/
+ break;
+ case VENDOR_HP: /*=0x00000B*/
+ switch(prod) { /* show product names for some */
+ case 0x2000: prodstr = "(Proliant ML/DL)"; break; /*DL380*/
+ case 0x2020: prodstr = "(Proliant BL)"; break;
+ default: if ((prod & 0xff00) == 0x8300)
+ prodstr = "(Proliant SL)";
+ else prodstr = "";
+ break;
+ }
+ do_powerstate = 0; /*HP does not support get_power_state cmd*/
+ break;
+ case VENDOR_DELL: /*=0x0002A2*/
+ switch(prod) { /* show product names for some */
+ case 0x0100: prodstr = "(PE R610)"; break;
+ default: prodstr = ""; break;
+ }
+ break;
+ case VENDOR_MAGNUM: /* =5593. used by SuperMicro*/
+ switch(prod) { /* show product names for some */
+ case 6: prodstr = "(X8DTL)"; break;
+ default: prodstr = ""; break;
+ }
+ break;
+ case VENDOR_SUPERMICRO: /* =10876. used by SuperMicro*/
+ case VENDOR_SUPERMICROX: /* =47488. used by Winbond/SuperMicro*/
+ switch(prod) { /* decode some SuperMicro product ids */
+ case 4: prodstr = "(X7DBR)"; break;
+ case 6: prodstr = "(X8DTL)"; break;
+ case 1037: prodstr = "(X8SIE)"; break;
+ case 1541: prodstr = "(X8SIL)"; break;
+ case 1547: prodstr = "(X8SIA)"; break; /*0x060b*/
+ case 1549: prodstr = "(X8DTU)"; break;
+ case 1551: prodstr = "(X8DTN)"; break;
+ case 1562: prodstr = "(X8SIU-F)"; break; /*0x061a*/
+ case 1572: prodstr = "(X9SCM)"; break; /*or X9SCL*/
+ case 1576: prodstr = "(X9DRi)"; break;
+ case 1585: prodstr = "(X9SCA)"; break;
+ case 1603: prodstr = "(X9SPU)"; break; /*0x0643*/
+ case 1643: prodstr = "(X9SRL)"; break; /*0x066b*/
+ case 1797: prodstr = "(X9DR7)"; break; /*0x0705*/
+ case 43025: prodstr = "(H8DGU)"; break;
+ case 43707: prodstr = "(X8DTH)"; break;
+ default: prodstr = ""; break;
+ }
+ if (!fipmilan) lan_ch_restrict = 1; /*fw bug, gets 0xd4 locally*/
+ break;
+ case VENDOR_QUANTA: /*=7244.*/
+ switch(prod) { /* show product names for some */
+ case 21401: prodstr = "(S99Q)"; break;
+ default: prodstr = ""; break;
+ }
+ break;
+ default:
+ prodstr = "";
+ break;
+ } /*end switch(prod)*/
+ if (kcs_loops != 0) set_max_kcs_loops(kcs_loops);
+ } /*end-else BMC*/
+
+ printf("%s manufacturer %c %06x (%s)%c product %c %04x %s\n",
+ tag, bdelim,mfg,mfgstr,bcomma,bdelim,prod,prodstr);
+ { /* BMC version */
+ printf("%s version %c %x.%02x%s%c IPMI v%d.%d\n", tag,bdelim,
+ devrec[2],devrec[3],extraver,bcomma,ipmi_maj,ipmi_min);
+ }
+ /* 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);
+}
+
+char *decode_selftest(int stat)
+{
+ uchar *s;
+ uchar b;
+ if (stat == 0x0055) s = "(OK)";
+ else {
+ s = "(Error)";
+ if ((stat & 0x00ff) == 0x0057) {
+ b = ((stat & 0xff00) >> 8);
+ if (b & 0x80) s = "(No SEL Access)";
+ if (b & 0x40) s = "(No SDR Access)";
+ if (b & 0x20) s = "(No FRU Access)";
+ if (b & 0x10) s = "(IPMB Error)";
+ if (b & 0x08) s = "(SDR Empty)";
+ if (b & 0x02) s = "(BootCode Corrupt)";
+ if (b & 0x01) s = "(OpCode Corrupt)";
+ }
+ }
+ return(s);
+}
+
+#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;
+ char selfstr[36];
+ char *s;
+ char *s1;
+ int i, sresp;
+ uint n;
+ int rlen, len;
+ uchar idata[4];
+ uchar rdata[16];
+
+ printf("%s ver %s\n", progname,progver);
+
+ while ( (c = getopt( argc, argv,"cfghiln:o:p:q:sT:V:J:YEF:P:N:R:U:Z:x?")) != EOF )
+ switch(c) {
+ case 'c': fcanonical = 1;
+ bdelim = BDELIM; break; /* canonical output */
+ case 'f': do_frusdr = 1; break; /* check the FRUSDR too */
+ case 'g': do_guid = 1; break; /* get the System GUID also */
+ case 'h': do_hsc = 1; break; /* check the HSC too */
+ case 'i': do_systeminfo = 1; break; /* get system info too */
+ case 'l': do_lanstats = 1; break; /* get the LAN stats too */
+ case 'm': /* specific IPMB MC, 3-byte address, e.g. "409600" */
+ g_bus = htoi(&optarg[0]); /*bus/channel*/
+ g_sa = htoi(&optarg[2]); /*device slave address*/
+ g_lun = htoi(&optarg[4]); /*LUN*/
+ g_addrtype = ADDR_IPMB;
+ if (optarg[6] == 's') {
+ g_addrtype = ADDR_SMI; s1 = "SMI";
+ } else { g_addrtype = ADDR_IPMB; s1 = "IPMB"; }
+ ipmi_set_mc(g_bus,g_sa,g_lun,g_addrtype);
+ printf("Use MC at %s bus=%x sa=%x lun=%x\n",
+ s1,g_bus,g_sa,g_lun);
+ break;
+ case 'n': set_name = 1; /* set the system name*/
+ pname = optarg;
+ break;
+ case 'o': set_os = 1; /* set the Operating System*/
+ pos = optarg;
+ break;
+ case 'q': set_os2 = 1; /* set the Operating System*/
+ pos2 = optarg;
+ break;
+ case 'p': set_restore = 1; /* set the restore policy */
+ restore_policy = atob(optarg);
+ if (restore_policy > 2) restore_policy = 1;
+ break;
+ case 's': do_session = 1; break; /* get session info too */
+ case 'x': fdebug = 1; break; /* debug messages */
+ 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 [-cfghilnopsx -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(" -i get System Info also: Name and OS\n");
+ printf(" -l get the IPMI LAN statistics also\n");
+ printf(" -n set System Name to this string \n");
+ printf(" -o set Operating System to this string\n");
+ printf(" -p1 set restore policy: 0=off, 1=last, 2=on\n");
+ printf(" -s get the IPMI Session info 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,16);
+ }
+
+ 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) {
+ uchar m,n;
+ m = (rdata[3] & 0xf0) >> 4;
+ n = (rdata[3] & 0x0f);
+ printf("ME Firmware Ver %c %02x.%02x.%02x.%02x%02x\n",bdelim,
+ rdata[2],m,n,rdata[12],rdata[13]);
+ /* If rdata[2] has 0x80 bit on, ME is in update/recovery mode.
+ That can be cleared by removing input power. */
+ } else
+ if (fdebug) printf("GetDeviceID(ME) error ret = %d\n",ret);
+ }
+
+ if (do_hsc) {
+ if (fmBMC) printf("No HSC present\n");
+ else {
+ /* Get HSC status */
+ if (is_romley(vend_id, prod_id)) {
+ uchar maj, min;
+ ret = get_hsbp_version_intel(&maj, &min);
+ if (fdebug) printf("get_hsbp_version_intel ret = %d\n",ret);
+ if (ret == 0)
+ printf("HSC version %c %d.%02d\n", bdelim,maj,min);
+ } else {
+ ret = get_hsc_devid(&devrec[0],sizeof(devrec));
+ if (fdebug) printf("get_hsc_devid ret = %d\n",ret);
+ if (ret == 0) /* only if HSC is detected */
+ show_devid_all(HSC,devrec,14);
+ }
+ }
+ }
+
+ i = get_driver_type();
+ printf("IPMI driver type %c %d (%s)\n",bdelim,i,show_driver_type(i));
+
+ 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);
+ s = decode_selftest(selfstatus);
+ 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);
+ }
+
+ ret = oem_get_health(&selfstr[0],sizeof(selfstr));
+ if (ret == 0) {
+ printf("%s\n",selfstr);
+ }
+
+ rlen = 4;
+ ret = get_chassis_status(selfbuf,&rlen);
+ if (ret != 0) {
+ printf("Cannot do get_chassis_status, ret = %d\n",ret);
+ goto health_end;
+ } else {
+ show_chs_status(selfbuf,rlen);
+ }
+
+ if (vend_id == VENDOR_INTEL) {
+ int pwr_delay = 0;
+ ret = get_power_restore_delay_intel(&pwr_delay);
+ if (fdebug) printf("get_power_restore_delay_intel ret = %d\n",ret);
+ if (ret == 0) {
+ printf("PowerRestoreDelay %c %d seconds\n",bdelim,pwr_delay);
+ } else ret = 0;
+ }
+
+ if (do_guid) {
+ sresp = sizeof(devrec);
+ ret = ipmi_cmd(GET_SYSTEM_GUID,NULL,0,devrec,&sresp,&cc,fdebug);
+ if (ret != 0) {
+ if (!is_remote()) { /* get UUID from SMBIOS */
+ cc = 0; sresp = 16;
+ ret = get_SystemGuid(devrec);
+ } else {
+ cc = 0;
+ sresp = sizeof(devrec);
+ ret = get_device_guid(devrec,&sresp);
+ }
+ }
+ 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) /* do not get first lan chan if set above */
+ ret = get_lan_channel(1,&bChan);
+
+ if (fmBMC == 0) {
+ ret = GetPowerOnHours(&n);
+ if (ret == 0)
+ printf("Power On Hours %c %d hours (%d days)\n",bdelim,n,(n/24));
+ if (ret == 0xC1) ret = 0; /* not supporting poweron hours is ok. */
+
+ printf("BMC LAN Channels %c ",bdelim);
+ for (i = 1; i<= 16; ) {
+ c = get_lan_channel(i,&cc);
+ if (c != 0) break;
+ printf("%d ",cc);
+ i = cc+1;
+ }
+ printf("\n");
+
+ if (lan_ch_restrict) ; /*skip if vendor fw bug*/
+ else {
+ 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_systeminfo) {
+ char infostr[64];
+ len = sizeof(infostr);
+ ret = get_system_info(1,infostr,&len); /*Firmware Version*/
+ len = sizeof(infostr);
+ ret = get_system_info(2,infostr,&len);
+ if (ret == 0) {
+ printf("System Name %c %s\n",bdelim,infostr);
+ len = sizeof(infostr);
+ ret = get_system_info(3,infostr,&len);
+ if (ret == 0) printf("Pri Operating System %c %s\n",bdelim,infostr);
+ len = sizeof(infostr);
+ ret = get_system_info(4,infostr,&len);
+ if (ret == 0) printf("Sec Operating System %c %s\n",bdelim,infostr);
+ } else {
+ if (ret == 0xC1) /*only supported on later IPMI 2.0 firmware */
+ printf("GetSystemInfo not supported on this platform\n");
+ }
+ }
+
+ 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);
+ }
+
+ if (do_session) {
+ i = get_session_info_all();
+ if (i != 0) printf("get_session_info error %d, %s\n",i,decode_rv(i));
+ }
+
+#ifdef PING_OK
+ {
+ char *node;
+ /* Currently some problems with this:
+ * works first time, but locks up BMC LAN on subsequent attempts.
+ */
+ node = get_nodename();
+ ret = ping_node(node);
+ printf("ping_node(%s): ret = %d\n",node,ret);
+ }
+#endif
+
+ if (set_name) {
+ len = (int)strlen(pname);
+ ret = set_system_info(2,pname,len);
+ printf("Set System Name to '%s', ret = %d\n",pname,ret);
+ if (ret == 0xC1) /*only supported on later IPMI 2.0 firmware */
+ printf("SetSystemInfo not supported on this platform\n");
+ }
+
+ if (set_os) {
+ len = (int)strlen(pos);
+ ret = set_system_info(3,pos,len);
+ printf("Set Pri Operating System to '%s', ret = %d\n",pos,ret);
+ if (ret == 0xC1) /*only supported on later IPMI 2.0 firmware */
+ printf("SetSystemInfo not supported on this platform\n");
+ }
+
+ if (set_os2) {
+ len = (int)strlen(pos2);
+ ret = set_system_info(4,pos2,len);
+ printf("Set Sec Operating System to '%s', ret = %d\n",pos2,ret);
+ if (ret == 0xC1) /*only supported on later IPMI 2.0 firmware */
+ printf("SetSystemInfo not supported on this platform\n");
+ }
+
+ 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, g_sa, g_bus, g_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 */