diff options
Diffstat (limited to '')
-rw-r--r-- | lib/plugins/stonith/ipmilan_command.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/lib/plugins/stonith/ipmilan_command.c b/lib/plugins/stonith/ipmilan_command.c new file mode 100644 index 0000000..a3de493 --- /dev/null +++ b/lib/plugins/stonith/ipmilan_command.c @@ -0,0 +1,399 @@ +/* + * 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/ipmiif.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 <OpenIPMI/ipmi_debug.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=NULL; +selector_t *os_sel; +static ipmi_con_t *con; +extern os_handler_t ipmi_os_cb_handlers; +static int reset_method; + +static int request_done = 0; +static int op_done = 0; + +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, const char *type); +int rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi); + +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, const 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_msgi_t *rspi) +{ + int rv; + long request; + + /*dump_msg_data(&rspi->msg, &rspi->addr, "response");*/ + request = (long) rspi->data1; + + op_done = 1; + if( !rspi || !(rspi->msg.data) ) { + PILCallLog(PluginImports->log,PIL_CRIT, "No data received\n"); + gstatus = S_RESETFAIL; + return IPMI_MSG_ITEM_NOT_USED; + } + rv = rspi->msg.data[0]; + /* some IPMI device might not issue 0x00, success, for reset command. + instead, a 0xc3, timeout, is returned. */ + if (rv == 0x00) { + gstatus = S_OK; + } else if((rv == 0xc3 || rv == 0xff) && request == ST_GENERIC_RESET) { + PILCallLog(PluginImports->log,PIL_WARN , + "IPMI reset request failed: %x, but we assume that it succeeded\n", rv); + gstatus = S_OK; + } else { + PILCallLog(PluginImports->log,PIL_INFO + , "IPMI request %ld failed: %x\n", request, rv); + gstatus = S_RESETFAIL; + } + return IPMI_MSG_ITEM_NOT_USED; +} + +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_msgi_t *rspi; + /* 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_UP; + break; + + case ST_POWEROFF: + cc_data = POWER_DOWN; + break; + + case ST_GENERIC_RESET: + cc_data = (reset_method ? POWER_CYCLE : HARD_RESET); + break; + + case ST_IPMI_STATUS: + msg.netfn = IPMI_APP_NETFN; + msg.cmd = IPMI_GET_DEVICE_ID_CMD; + msg.data_len = 0; + break; + + default: + gstatus = S_INVAL; + return; + } + + gstatus = S_ACCESS; + rspi = calloc(1, sizeof(ipmi_msgi_t)); + if (NULL == rspi) { + PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: Out of memory\n"); + } else { + rspi->data1 = (void *) (long) request; + rv = con->send_command(con, &addr, addr_len, &msg, rsp_handler, rspi); + if (rv == -1) { + PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: %x\n", rv); + } else { + request_done = 1; + } + } + + 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; + } + + if( !request_done ) { + 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]; + + /*DEBUG_MSG_ENABLE();*/ + + 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 allocate 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)); + + reset_method = host->reset_method; + + 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; + } + +#if OPENIPMI_VERSION_MAJOR < 2 + con->set_con_change_handler(con, con_changed_handler, request); +#else + con->add_con_change_handler(con, con_changed_handler, request); +#endif + + 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; + } + return S_OK; +} + +void +ipmi_leave() +{ + if( con && con->close_connection ) { + con->close_connection(con); + con = NULL; + } + if( os_sel ) { + sel_free_selector(os_sel); + os_sel = NULL; + } +} + +int +do_ipmi_cmd(struct ipmilanHostInfo * host, int request) +{ + int rv; + sel_timer_t * timer; + struct timeval timeout; + + request_done = 0; + op_done = 0; + + if( !os_hnd ) { + rv = setup_ipmi_conn(host, &request); + if( rv ) { + return rv; + } + } else { + send_ipmi_cmd(con, request); + } + + 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 (!op_done) { + rv = sel_select(os_sel, NULL, 0, NULL, NULL); + if (rv == -1) { + break; + } + } + + sel_free_timer(timer); + return gstatus; +} + +#if OPENIPMI_VERSION_MAJOR < 2 +void +posix_vlog(char *format, enum ipmi_log_type_e log_type, va_list ap) +{ +} +#endif |