You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
heartbeat/lib/plugins/stonith/ipmilan_command.c

360 lines
8.5 KiB

/*
* This program is largely based on the ipmicmd.c program that's part of OpenIPMI package.
*
* Copyright Intel Corp.
* Yixiong.Zou@intel.com
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h> /* malloc() */
#include <unistd.h> /* getopt() */
#include <string.h> /* strerror() */
#include <netdb.h> /* gethostbyname() */
#include <sys/types.h>
#include <sys/socket.h>
#include <OpenIPMI/selector.h>
#include <OpenIPMI/ipmi_conn.h>
#include <OpenIPMI/ipmi_lan.h>
#include <OpenIPMI/ipmi_smi.h>
#include <OpenIPMI/ipmi_auth.h>
#include <OpenIPMI/ipmi_msgbits.h>
#include <OpenIPMI/ipmi_posix.h>
#include "ipmilan.h"
#include <stonith/stonith.h>
#include <clplumbing/cl_log.h>
#include <pils/plugin.h>
extern const PILPluginImports* PluginImports;
/* #define DUMP_MSG 0 */
#define OPERATION_TIME_OUT 10
os_handler_t *os_hnd;
selector_t *os_sel;
extern os_handler_t ipmi_os_cb_handlers;
typedef enum ipmi_status {
/*
IPMI_CONNECTION_FAILURE,
IPMI_SEND_FAILURE,
IPMI_BAD_REQUEST,
IPMI_REQUEST_FAILED,
IPMI_TIME_OUT,
*/
IPMI_RUNNING = 99,
} ipmi_status_t;
static ipmi_status_t gstatus;
typedef enum chassis_control_request {
POWER_DOWN = 0X00,
POWER_UP = 0X01,
POWER_CYCLE = 0X02,
HARD_RESET = 0X03,
PULSE_DIAGNOSTIC_INTERRUPT = 0X04,
SOFT_SHUTDOWN = 0X05
} chassis_control_request_t;
void dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, char *type);
int rsp_handler(ipmi_con_t *ipmi, ipmi_msg_t *rsp);
void send_ipmi_cmd(ipmi_con_t *con, int request);
void timed_out(selector_t *sel, sel_timer_t *timer, void *data);
void
timed_out(selector_t *sel, sel_timer_t *timer, void *data)
{
PILCallLog(PluginImports->log,PIL_CRIT, "IPMI operation timed out... :(\n");
gstatus = S_TIMEOUT;
}
void
dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, char *type)
{
ipmi_system_interface_addr_t *smi_addr = NULL;
int i;
ipmi_ipmb_addr_t *ipmb_addr = NULL;
if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
smi_addr = (struct ipmi_system_interface_addr *) addr;
fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ",
addr->channel,
msg->netfn,
smi_addr->lun,
msg->cmd);
} else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)
|| (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) {
ipmb_addr = (struct ipmi_ipmb_addr *) addr;
fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ",
addr->channel,
msg->netfn,
ipmb_addr->lun,
msg->cmd);
}
for (i = 0; i < msg->data_len; i++) {
if (((i%16) == 0) && (i != 0)) {
printf("\n ");
}
fprintf(stderr, "%2.2x ", msg->data[i]);
}
fprintf(stderr, "\n");
}
/*
* This function gets called after the response comes back
* from the IPMI device.
*
* Some IPMI device does not return success, 0x00, to the
* remote node when the power-reset was issued.
*
* The host who sent the ipmi cmd might get a 0xc3,
* a timeout instead. This creates problems for
* STONITH operation, where status is critical. :(
*
* Right now I am only checking 0xc3 as the return.
* If your IPMI device returns some wired code after
* reset, you might want to add it in this code block.
*
*/
int
rsp_handler(ipmi_con_t *ipmi, ipmi_msg_t *rsp)
{
/* This code causes segmentation faults in the "rv = " line, and the
* "free(" line seems to be freeing invalid memory */
#if 0
int rv;
int * request;
request = (void *) rsp->data;
rv = rsp->data[0];
/* some IPMI device might not issue 0x00, success, for reset command.
instead, a 0xc3, timeout, is returned. */
if (rv == 0x00 ||
(rv == 0xc3 && *request <= ST_POWEROFF && *request >= ST_GENERIC_RESET ) ) {
gstatus = S_OK;
} else {
gstatus = S_RESETFAIL;
}
free(request);
#else
/* so, until this code can be fixed, we'll just assume it's ok */
gstatus = S_OK;
#endif
return gstatus;
}
void
send_ipmi_cmd(ipmi_con_t *con, int request)
{
ipmi_addr_t addr;
unsigned int addr_len;
ipmi_msg_t msg;
struct ipmi_system_interface_addr *si;
int rv;
ipmi_msg_t msgi;
/* chassis control command request is only 1 byte long */
unsigned char cc_data = POWER_CYCLE;
si = (void *) &addr;
si->lun = 0x00;
si->channel = IPMI_BMC_CHANNEL;
si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
addr_len = sizeof(*si);
msg.netfn = IPMI_CHASSIS_NETFN;
msg.cmd = IPMI_CHASSIS_CONTROL_CMD;
msg.data = &cc_data;
msg.data_len = 1;
switch (request) {
case ST_POWERON:
cc_data = POWER_DOWN;
break;
case ST_POWEROFF:
cc_data = POWER_UP;
break;
case ST_GENERIC_RESET:
cc_data = POWER_CYCLE;
break;
case ST_IPMI_STATUS:
msg.netfn = IPMI_APP_NETFN;
msg.cmd = IPMI_GET_DEVICE_ID_CMD;
msg.data = NULL;
msg.data_len = 0;
break;
default:
gstatus = S_INVAL;
return;
}
msgi.data = (void *) malloc(sizeof(int));
*((unsigned char *)msgi.data) = request;
rv = con->send_command(con, &addr, addr_len, &msg, (ipmi_ll_rsp_handler_t) rsp_handler, (void *) &msgi);
if (rv == -1) {
PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: %x\n", rv);
gstatus = S_ACCESS;
}
return;
}
static void
con_changed_handler(ipmi_con_t *ipmi,
int err,
unsigned int port_num,
int still_connected,
void *cb_data)
{
int * request;
if (err) {
PILCallLog(PluginImports->log,PIL_CRIT, "Unable to setup connection: %x\n", err);
return;
}
request = (int *) cb_data;
send_ipmi_cmd(ipmi, *request);
}
static int
setup_ipmi_conn(struct ipmilanHostInfo * host, int request)
{
int rv;
struct hostent *ent;
struct in_addr lan_addr[2];
int lan_port[2];
int num_addr = 1;
int authtype = 0;
int privilege = 0;
char username[17];
char password[17];
static ipmi_con_t *con;
sel_timer_t * timer;
struct timeval timeout;
os_hnd = ipmi_posix_get_os_handler();
if (!os_hnd) {
PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_smi_setup_con: Unable to allocate os handler");
return 1;
}
rv = sel_alloc_selector(os_hnd, &os_sel);
if (rv) {
PILCallLog(PluginImports->log,PIL_CRIT, "Could not alloctate selector\n");
return rv;
}
ipmi_posix_os_handler_set_sel(os_hnd, os_sel);
rv = ipmi_init(os_hnd);
if (rv) {
PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_init erro: %d ", rv);
return rv;
}
ent = gethostbyname(host->ipaddr);
if (!ent) {
PILCallLog(PluginImports->log,PIL_CRIT, "gethostbyname failed: %s\n", strerror(h_errno));
return 1;
}
memcpy(&lan_addr[0], ent->h_addr_list[0], ent->h_length);
lan_port[0] = host->portnumber;
lan_port[1] = 0;
authtype = host->authtype;
privilege = host->privilege;
memcpy(username, host->username, sizeof(username));
memcpy(password, host->password, sizeof(password));
rv = ipmi_lan_setup_con(lan_addr, lan_port, num_addr,
authtype, privilege,
username, strlen(username),
password, strlen(password),
os_hnd, os_sel,
&con);
if (rv) {
PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_lan_setup_con: %s\n", strerror(rv));
return S_ACCESS;
}
/* in the OpenIPMI 1.3.x implementation the callback function was
* called set_con_change_handler(), now with the OpenIPMI 2.0.x
* implementation it has changed to add_con_change_handler(). Check out:
*
* http://cvs.sourceforge.net/viewcvs.py/openipmi/OpenIPMI/include/OpenIPMI/ipmi_conn.h?r1=1.27&r2=1.28
*/
con->add_con_change_handler(con, con_changed_handler, &request);
gstatus = IPMI_RUNNING;
rv = con->start_con(con);
if (rv) {
PILCallLog(PluginImports->log,PIL_CRIT, "Could not start IPMI connection: %x\n", rv);
gstatus = S_BADCONFIG;
return rv;
}
gettimeofday(&timeout, NULL);
timeout.tv_sec += OPERATION_TIME_OUT;
timeout.tv_usec += 0;
sel_alloc_timer(os_sel, timed_out, NULL, &timer);
sel_start_timer(timer, &timeout);
while (1) {
rv = sel_select(os_sel, NULL, 0, NULL, NULL);
if (gstatus != IPMI_RUNNING) {
break;
}
}
sel_free_timer(timer);
con->close_connection(con);
ipmi_shutdown();
return gstatus;
}
int
do_ipmi_cmd(struct ipmilanHostInfo * host, int request)
{
return setup_ipmi_conn(host, request);
}