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