1 /*
2 * This program is largely based on the ipmicmd.c program that's part of OpenIPMI package.
3 *
4 * Copyright Intel Corp.
5 * Yixiong.Zou@intel.com
6 *
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22 #include <stdio.h>
23
24 #include <stdlib.h> /* malloc() */
25 #include <unistd.h> /* getopt() */
26 #include <string.h> /* strerror() */
27 #include <netdb.h> /* gethostbyname() */
28 #include <sys/types.h>
29 #include <sys/socket.h>
30
31 #include <OpenIPMI/ipmiif.h>
32 #include <OpenIPMI/selector.h>
33 #include <OpenIPMI/ipmi_conn.h>
34 #include <OpenIPMI/ipmi_lan.h>
35 #include <OpenIPMI/ipmi_smi.h>
36 #include <OpenIPMI/ipmi_auth.h>
37 #include <OpenIPMI/ipmi_msgbits.h>
38 #include <OpenIPMI/ipmi_posix.h>
39 #include <OpenIPMI/ipmi_debug.h>
40
41 #include "ipmilan.h"
42 #include <stonith/stonith.h>
43 #include <clplumbing/cl_log.h>
44
45 #include <pils/plugin.h>
46 extern const PILPluginImports* PluginImports;
47
48 /* #define DUMP_MSG 0 */
49 #define OPERATION_TIME_OUT 10
50 typedef struct selector_s selector_t;
51
52 os_handler_t *os_hnd=NULL;
53 selector_t *os_sel;
54 static ipmi_con_t *con;
55 extern os_handler_t ipmi_os_cb_handlers;
56 static int reset_method;
57
58 static int request_done = 0;
59 static int op_done = 0;
60
61 typedef enum ipmi_status {
62 /*
63 IPMI_CONNECTION_FAILURE,
64 IPMI_SEND_FAILURE,
65 IPMI_BAD_REQUEST,
66 IPMI_REQUEST_FAILED,
67 IPMI_TIME_OUT,
68 */
69 IPMI_RUNNING = 99,
70 } ipmi_status_t;
71
72 static ipmi_status_t gstatus;
73
74 typedef enum chassis_control_request {
75 POWER_DOWN = 0X00,
76 POWER_UP = 0X01,
77 POWER_CYCLE = 0X02,
78 HARD_RESET = 0X03,
79 PULSE_DIAGNOSTIC_INTERRUPT = 0X04,
80 SOFT_SHUTDOWN = 0X05
81 } chassis_control_request_t;
82
83 void dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, const char *type);
84 int rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi);
85
86 void send_ipmi_cmd(ipmi_con_t *con, int request);
87
88 void timed_out(selector_t *sel, sel_timer_t *timer, void *data);
89
90 void
timed_out(selector_t * sel,sel_timer_t * timer,void * data)91 timed_out(selector_t *sel, sel_timer_t *timer, void *data)
92 {
93 PILCallLog(PluginImports->log,PIL_CRIT, "IPMI operation timed out... :(\n");
94 gstatus = S_TIMEOUT;
95 }
96
97 void
dump_msg_data(ipmi_msg_t * msg,ipmi_addr_t * addr,const char * type)98 dump_msg_data(ipmi_msg_t *msg, ipmi_addr_t *addr, const char *type)
99 {
100 ipmi_system_interface_addr_t *smi_addr = NULL;
101 int i;
102 ipmi_ipmb_addr_t *ipmb_addr = NULL;
103
104 if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
105 smi_addr = (struct ipmi_system_interface_addr *) addr;
106
107 fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ",
108 addr->channel,
109 msg->netfn,
110 smi_addr->lun,
111 msg->cmd);
112 } else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)
113 || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) {
114 ipmb_addr = (struct ipmi_ipmb_addr *) addr;
115
116 fprintf(stderr, "%2.2x %2.2x %2.2x %2.2x ",
117 addr->channel,
118 msg->netfn,
119 ipmb_addr->lun,
120 msg->cmd);
121 }
122
123 for (i = 0; i < msg->data_len; i++) {
124 if (((i%16) == 0) && (i != 0)) {
125 printf("\n ");
126 }
127 fprintf(stderr, "%2.2x ", msg->data[i]);
128 }
129 fprintf(stderr, "\n");
130 }
131
132 /*
133 * This function gets called after the response comes back
134 * from the IPMI device.
135 *
136 * Some IPMI device does not return success, 0x00, to the
137 * remote node when the power-reset was issued.
138 *
139 * The host who sent the ipmi cmd might get a 0xc3,
140 * a timeout instead. This creates problems for
141 * STONITH operation, where status is critical. :(
142 *
143 * Right now I am only checking 0xc3 as the return.
144 * If your IPMI device returns some wired code after
145 * reset, you might want to add it in this code block.
146 *
147 */
148
149 int
rsp_handler(ipmi_con_t * ipmi,ipmi_msgi_t * rspi)150 rsp_handler(ipmi_con_t *ipmi, ipmi_msgi_t *rspi)
151 {
152 int rv;
153 long request;
154
155 /*dump_msg_data(&rspi->msg, &rspi->addr, "response");*/
156 request = (long) rspi->data1;
157
158 op_done = 1;
159 if( !rspi || !(rspi->msg.data) ) {
160 PILCallLog(PluginImports->log,PIL_CRIT, "No data received\n");
161 gstatus = S_RESETFAIL;
162 return IPMI_MSG_ITEM_NOT_USED;
163 }
164 rv = rspi->msg.data[0];
165 /* some IPMI device might not issue 0x00, success, for reset command.
166 instead, a 0xc3, timeout, is returned. */
167 if (rv == 0x00) {
168 gstatus = S_OK;
169 } else if((rv == 0xc3 || rv == 0xff) && request == ST_GENERIC_RESET) {
170 PILCallLog(PluginImports->log,PIL_WARN ,
171 "IPMI reset request failed: %x, but we assume that it succeeded\n", rv);
172 gstatus = S_OK;
173 } else {
174 PILCallLog(PluginImports->log,PIL_INFO
175 , "IPMI request %ld failed: %x\n", request, rv);
176 gstatus = S_RESETFAIL;
177 }
178 return IPMI_MSG_ITEM_NOT_USED;
179 }
180
181 void
send_ipmi_cmd(ipmi_con_t * con,int request)182 send_ipmi_cmd(ipmi_con_t *con, int request)
183 {
184 ipmi_addr_t addr;
185 unsigned int addr_len;
186 ipmi_msg_t msg;
187 struct ipmi_system_interface_addr *si;
188 int rv;
189 ipmi_msgi_t *rspi;
190 /* chassis control command request is only 1 byte long */
191 unsigned char cc_data = POWER_CYCLE;
192
193 si = (void *) &addr;
194 si->lun = 0x00;
195 si->channel = IPMI_BMC_CHANNEL;
196 si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
197 addr_len = sizeof(*si);
198
199 msg.netfn = IPMI_CHASSIS_NETFN;
200 msg.cmd = IPMI_CHASSIS_CONTROL_CMD;
201 msg.data = &cc_data;
202 msg.data_len = 1;
203
204 switch (request) {
205 case ST_POWERON:
206 cc_data = POWER_UP;
207 break;
208
209 case ST_POWEROFF:
210 cc_data = POWER_DOWN;
211 break;
212
213 case ST_GENERIC_RESET:
214 cc_data = (reset_method ? POWER_CYCLE : HARD_RESET);
215 break;
216
217 case ST_IPMI_STATUS:
218 msg.netfn = IPMI_APP_NETFN;
219 msg.cmd = IPMI_GET_DEVICE_ID_CMD;
220 msg.data_len = 0;
221 break;
222
223 default:
224 gstatus = S_INVAL;
225 return;
226 }
227
228 gstatus = S_ACCESS;
229 rspi = calloc(1, sizeof(ipmi_msgi_t));
230 if (NULL == rspi) {
231 PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: Out of memory\n");
232 } else {
233 rspi->data1 = (void *) (long) request;
234 rv = con->send_command(con, &addr, addr_len, &msg, rsp_handler, rspi);
235 if (rv == -1) {
236 PILCallLog(PluginImports->log,PIL_CRIT, "Error sending IPMI command: %x\n", rv);
237 } else {
238 request_done = 1;
239 }
240 }
241
242 return;
243 }
244
245 static void
con_changed_handler(ipmi_con_t * ipmi,int err,unsigned int port_num,int still_connected,void * cb_data)246 con_changed_handler(ipmi_con_t *ipmi, int err, unsigned int port_num,
247 int still_connected, void *cb_data)
248 {
249 int * request;
250
251 if (err) {
252 PILCallLog(PluginImports->log,PIL_CRIT, "Unable to setup connection: %x\n", err);
253 return;
254 }
255
256 if( !request_done ) {
257 request = (int *) cb_data;
258 send_ipmi_cmd(ipmi, *request);
259 }
260 }
261
262 static int
setup_ipmi_conn(struct ipmilanHostInfo * host,int * request)263 setup_ipmi_conn(struct ipmilanHostInfo * host, int *request)
264 {
265 int rv;
266
267 struct hostent *ent;
268 struct in_addr lan_addr[2];
269 int lan_port[2];
270 int num_addr = 1;
271 int authtype = 0;
272 int privilege = 0;
273 char username[17];
274 char password[17];
275
276 /*DEBUG_MSG_ENABLE();*/
277
278 os_hnd = ipmi_posix_get_os_handler();
279 if (!os_hnd) {
280 PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_smi_setup_con: Unable to allocate os handler");
281 return 1;
282 }
283
284 rv = sel_alloc_selector(os_hnd, &os_sel);
285 if (rv) {
286 PILCallLog(PluginImports->log,PIL_CRIT, "Could not allocate selector\n");
287 return rv;
288 }
289
290 ipmi_posix_os_handler_set_sel(os_hnd, os_sel);
291
292 rv = ipmi_init(os_hnd);
293 if (rv) {
294 PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_init erro: %d ", rv);
295 return rv;
296 }
297
298 ent = gethostbyname(host->ipaddr);
299 if (!ent) {
300 PILCallLog(PluginImports->log,PIL_CRIT, "gethostbyname failed: %s\n", strerror(h_errno));
301 return 1;
302 }
303
304 memcpy(&lan_addr[0], ent->h_addr_list[0], ent->h_length);
305 lan_port[0] = host->portnumber;
306 lan_port[1] = 0;
307
308 authtype = host->authtype;
309 privilege = host->privilege;
310
311 memcpy(username, host->username, sizeof(username));
312 memcpy(password, host->password, sizeof(password));
313
314 reset_method = host->reset_method;
315
316 rv = ipmi_lan_setup_con(lan_addr, lan_port, num_addr,
317 authtype, privilege,
318 username, strlen(username),
319 password, strlen(password),
320 os_hnd, os_sel,
321 &con);
322
323 if (rv) {
324 PILCallLog(PluginImports->log,PIL_CRIT, "ipmi_lan_setup_con: %s\n", strerror(rv));
325 return S_ACCESS;
326 }
327
328 #if OPENIPMI_VERSION_MAJOR < 2
329 con->set_con_change_handler(con, con_changed_handler, request);
330 #else
331 con->add_con_change_handler(con, con_changed_handler, request);
332 #endif
333
334 gstatus = IPMI_RUNNING;
335
336 rv = con->start_con(con);
337 if (rv) {
338 PILCallLog(PluginImports->log,PIL_CRIT, "Could not start IPMI connection: %x\n", rv);
339 gstatus = S_BADCONFIG;
340 return rv;
341 }
342 return S_OK;
343 }
344
345 void
ipmi_leave()346 ipmi_leave()
347 {
348 if( con && con->close_connection ) {
349 con->close_connection(con);
350 con = NULL;
351 }
352 if( os_sel ) {
353 sel_free_selector(os_sel);
354 os_sel = NULL;
355 }
356 }
357
358 int
do_ipmi_cmd(struct ipmilanHostInfo * host,int request)359 do_ipmi_cmd(struct ipmilanHostInfo * host, int request)
360 {
361 int rv;
362 sel_timer_t * timer;
363 struct timeval timeout;
364
365 request_done = 0;
366 op_done = 0;
367
368 if( !os_hnd ) {
369 rv = setup_ipmi_conn(host, &request);
370 if( rv ) {
371 return rv;
372 }
373 } else {
374 send_ipmi_cmd(con, request);
375 }
376
377 gettimeofday(&timeout, NULL);
378 timeout.tv_sec += OPERATION_TIME_OUT;
379 timeout.tv_usec += 0;
380
381 sel_alloc_timer(os_sel, timed_out, NULL, &timer);
382 sel_start_timer(timer, &timeout);
383
384 while (!op_done) {
385 rv = sel_select(os_sel, NULL, 0, NULL, NULL);
386 if (rv == -1) {
387 break;
388 }
389 }
390
391 sel_free_timer(timer);
392 return gstatus;
393 }
394
395 #if OPENIPMI_VERSION_MAJOR < 2
396 void
posix_vlog(char * format,enum ipmi_log_type_e log_type,va_list ap)397 posix_vlog(char *format, enum ipmi_log_type_e log_type, va_list ap)
398 {
399 }
400 #endif
401