xref: /openbmc/ipmitool/lib/ipmi_sunoem.c (revision 531569ec)
1 /*
2  * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * Redistribution of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * Redistribution in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * Neither the name of Sun Microsystems, Inc. or the names of
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * This software is provided "AS IS," without a warranty of any kind.
20  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
23  * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
24  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
25  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
26  * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
27  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
28  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
29  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  */
32 
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/time.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <errno.h>
43 #include <time.h>
44 #include <unistd.h>
45 #include <signal.h>
46 #include <ctype.h>
47 #include <sys/time.h>
48 #include <limits.h>
49 #include <fcntl.h>
50 
51 #include <termios.h>
52 
53 #include <ipmitool/ipmi.h>
54 #include <ipmitool/ipmi_intf.h>
55 #include <ipmitool/helper.h>
56 #include <ipmitool/log.h>
57 #include <ipmitool/ipmi_sel.h>
58 #include <ipmitool/ipmi_sdr.h>
59 #include <ipmitool/ipmi_strings.h>
60 #include <ipmitool/ipmi_channel.h>
61 #include <ipmitool/ipmi_sunoem.h>
62 #include <ipmitool/ipmi_raw.h>
63 
64 static const struct valstr sunoem_led_type_vals[] = {
65 	{ 0, "OK2RM" },
66 	{ 1, "SERVICE" },
67 	{ 2, "ACT" },
68 	{ 3, "LOCATE" },
69 	{ 0xFF, NULL },
70 };
71 
72 static const struct valstr sunoem_led_mode_vals[] = {
73 	{ 0, "OFF" },
74 	{ 1, "ON" },
75 	{ 2, "STANDBY" },
76 	{ 3, "SLOW" },
77 	{ 4, "FAST" },
78 	{ 0xFF, NULL },
79 };
80 
81 static const struct valstr sunoem_led_mode_optvals[] = {
82 	{ 0, "STEADY_OFF" },
83 	{ 1, "STEADY_ON" },
84 	{ 2, "STANDBY_BLINK" },
85 	{ 3, "SLOW_BLINK" },
86 	{ 4, "FAST_BLINK" },
87 	{ 0xFF, NULL },
88 };
89 
90 #define SUNOEM_SUCCESS 1
91 
92 #define IPMI_SUNOEM_GETFILE_VERSION {3,2,0,0}
93 #define IPMI_SUNOEM_GETBEHAVIOR_VERSION {3,2,0,0}
94 
95 /*
96  * PRINT_NORMAL: print out the LED value as normal
97  * PRINT_ERROR: print out "na" for the LED value
98  */
99 typedef enum
100 {
101 	PRINT_NORMAL = 0, PRINT_ERROR
102 } print_status_t;
103 
104 int ret_get = 0;
105 int ret_set = 0;
106 
107 static void
108 ipmi_sunoem_usage(void)
109 {
110 	lprintf(LOG_NOTICE, "Usage: sunoem <command> [option...]");
111 	lprintf(LOG_NOTICE, "");
112 	lprintf(LOG_NOTICE, "Commands:");
113 	lprintf(LOG_NOTICE, " - cli [<command string> ...]");
114 	lprintf(LOG_NOTICE, "      Execute SP CLI commands.");
115 	lprintf(LOG_NOTICE, "");
116 	lprintf(LOG_NOTICE, " - led get [<sensor_id>] [ledtype]");
117 	lprintf(LOG_NOTICE, "      - Read status of LED found in Generic Device Locator.");
118 	lprintf(LOG_NOTICE, "");
119 	lprintf(LOG_NOTICE, " - led set <sensor_id> <led_mode> [led_type]");
120 	lprintf(LOG_NOTICE, "      - Set mode of LED found in Generic Device Locator.");
121 	lprintf(LOG_NOTICE, "      - You can pass 'all' as the <senso_rid> to change the LED mode of all sensors.");
122 	lprintf(LOG_NOTICE, "      - Use 'sdr list generic' command to get list of Generic");
123 	lprintf(LOG_NOTICE, "      - Devices that are controllable LEDs.");
124 	lprintf(LOG_NOTICE, "");
125 	lprintf(LOG_NOTICE, "      - Required SIS LED Mode:");
126 	lprintf(LOG_NOTICE, "          OFF          Off");
127 	lprintf(LOG_NOTICE, "          ON           Steady On");
128 	lprintf(LOG_NOTICE, "          STANDBY      100ms on 2900ms off blink rate");
129 	lprintf(LOG_NOTICE, "          SLOW         1HZ blink rate");
130 	lprintf(LOG_NOTICE, "          FAST         4HZ blink rate");
131 	lprintf(LOG_NOTICE, "");
132 	lprintf(LOG_NOTICE, "      - Optional SIS LED Type:");
133 	lprintf(LOG_NOTICE, "          OK2RM        OK to Remove");
134 	lprintf(LOG_NOTICE, "          SERVICE      Service Required");
135 	lprintf(LOG_NOTICE, "          ACT          Activity");
136 	lprintf(LOG_NOTICE, "          LOCATE       Locate");
137 	lprintf(LOG_NOTICE, "");
138 	lprintf(LOG_NOTICE, " - nacname <ipmi_nac_name>");
139 	lprintf(LOG_NOTICE, "      - Returns the full nac name");
140 	lprintf(LOG_NOTICE, "");
141 	lprintf(LOG_NOTICE, " - ping NUMBER <q>");
142 	lprintf(LOG_NOTICE, "      - Send and Receive NUMBER (64 Byte) packets.");
143 	lprintf(LOG_NOTICE, "");
144 	lprintf(LOG_NOTICE, "      - q - Quiet. Displays output at start and end");
145 	lprintf(LOG_NOTICE, "");
146 	lprintf(LOG_NOTICE, " - getval <target_name>");
147 	lprintf(LOG_NOTICE, "      - Returns the ILOM property value");
148 	lprintf(LOG_NOTICE, "");
149 	lprintf(LOG_NOTICE, " - setval <property name> <property value> <timeout>");
150 	lprintf(LOG_NOTICE, "      - Sets the ILOM property value");
151 	lprintf(LOG_NOTICE, "      - If timeout is not specified, the default is 5 sec.");
152 	lprintf(LOG_NOTICE, "      - NOTE: must be executed locally on host, not remotely over LAN!");
153 	lprintf(LOG_NOTICE, "");
154 	lprintf(LOG_NOTICE, " - sshkey del <user_id>");
155 	lprintf(LOG_NOTICE, "      - Delete ssh key for user id from authorized_keys,");
156 	lprintf(LOG_NOTICE, "      - view users with 'user list' command.");
157 	lprintf(LOG_NOTICE, "");
158 	lprintf(LOG_NOTICE, " - sshkey set <user_id> <id_rsa.pub>");
159 	lprintf(LOG_NOTICE, "      - Set ssh key for a userid into authorized_keys,");
160 	lprintf(LOG_NOTICE, "      - view users with 'user list' command.");
161 	lprintf(LOG_NOTICE, "");
162 	lprintf(LOG_NOTICE, " - version");
163 	lprintf(LOG_NOTICE, "      - Display the software version");
164 	lprintf(LOG_NOTICE, "");
165 	lprintf(LOG_NOTICE, " - nacname <ipmi_nac_name>");
166 	lprintf(LOG_NOTICE, "      - Returns the full nac name");
167 	lprintf(LOG_NOTICE, "");
168 	lprintf(LOG_NOTICE, " - getfile <file_string_id> <destination_file_name>");
169 	lprintf(LOG_NOTICE, "      - Copy file <file_string_id> to <destination_file_name>");
170 	lprintf(LOG_NOTICE, "");
171 	lprintf(LOG_NOTICE, "      - File string ids:");
172 	lprintf(LOG_NOTICE, "          SSH_PUBKEYS");
173 	lprintf(LOG_NOTICE, "          DIAG_PASSED");
174 	lprintf(LOG_NOTICE, "          DIAG_FAILED");
175 	lprintf(LOG_NOTICE, "          DIAG_END_TIME");
176 	lprintf(LOG_NOTICE, "          DIAG_INVENTORY");
177 	lprintf(LOG_NOTICE, "          DIAG_TEST_LOG");
178 	lprintf(LOG_NOTICE, "          DIAG_START_TIME");
179 	lprintf(LOG_NOTICE, "          DIAG_UEFI_LOG");
180 	lprintf(LOG_NOTICE, "          DIAG_TEST_LOG");
181 	lprintf(LOG_NOTICE, "          DIAG_LAST_LOG");
182 	lprintf(LOG_NOTICE, "          DIAG_LAST_CMD");
183 	lprintf(LOG_NOTICE, "");
184 	lprintf(LOG_NOTICE, " - getbehavior <behavior_string_id>");
185 	lprintf(LOG_NOTICE, "      - Test if ILOM behavior is enabled");
186 	lprintf(LOG_NOTICE, "");
187 	lprintf(LOG_NOTICE, "      - Behavior string ids:");
188 	lprintf(LOG_NOTICE, "          SUPPORTS_SIGNED_PACKAGES");
189 	lprintf(LOG_NOTICE, "          REQUIRES_SIGNED_PACKAGES");
190 	lprintf(LOG_NOTICE, "");
191 }
192 
193 #define SUNOEM_FAN_MODE_AUTO    0x00
194 #define SUNOEM_FAN_MODE_MANUAL  0x01
195 
196 static void
197 __sdr_list_empty(struct sdr_record_list * head)
198 {
199 	struct sdr_record_list * e, *f;
200 	for (e = head; e != NULL; e = f) {
201 		f = e->next;
202 		free(e);
203 	}
204 	head = NULL;
205 }
206 
207 /*
208  *  led_print
209  *  Print out the led name and the state, if stat is PRINT_NORMAL.
210  *  Otherwise, print out the led name and "na".
211  *  The state parameter is not referenced if stat is not PRINT_NORMAL.
212  */
213 static void
214 led_print(const char * name, print_status_t stat, uint8_t state)
215 {
216 	const char *theValue;
217 
218 	if (stat == PRINT_NORMAL) {
219 		theValue = val2str(state, sunoem_led_mode_vals);
220 	} else {
221 		theValue = "na";
222 	}
223 
224 	if (csv_output) {
225 		printf("%s,%s\n", name, theValue);
226 	} else {
227 		printf("%-16s | %s\n", name, theValue);
228 	}
229 }
230 
231 #define CC_NORMAL                  0x00
232 #define CC_PARAM_OUT_OF_RANGE      0xc9
233 #define CC_DEST_UNAVAILABLE        0xd3
234 #define CC_UNSPECIFIED_ERR         0xff
235 #define CC_INSUFFICIENT_PRIVILEGE  0xd4
236 #define CC_INV_CMD                 0xc1
237 #define CC_INV_DATA_FIELD          0xcc
238 
239 /*
240  * sunoem_led_get(....)
241  *
242  * OUTPUT:
243  *   SUNOEM_EC_INVALID_ARG         if dev is NULL,
244  *   SUNOEM_EC_BMC_NOT_RESPONDING  if no reply is obtained from BMC,
245  *   SUNOEM_EC_BMC_CCODE_NONZERO   if completion code is nonzero,
246  *   SUNOEM_EC_SUCCESS             otherwise.
247  */
248 static sunoem_ec_t
249 sunoem_led_get(struct ipmi_intf * intf,	struct sdr_record_generic_locator * dev,
250 		int ledtype, struct ipmi_rs **loc_rsp)
251 {
252 	struct ipmi_rs * rsp;
253 	struct ipmi_rq req;
254 	uint8_t rqdata[7];
255 	int rqdata_len;
256 
257 	if (dev == NULL) {
258 		*loc_rsp = NULL;
259 		return (SUNOEM_EC_INVALID_ARG);
260 	}
261 
262 	rqdata[0] = dev->dev_slave_addr;
263 	if (ledtype == 0xFF)
264 		rqdata[1] = dev->oem;
265 	else
266 		rqdata[1] = ledtype;
267 
268 	rqdata[2] = dev->dev_access_addr;
269 	rqdata[3] = dev->oem;
270 	rqdata[4] = dev->entity.id;
271 	rqdata[5] = dev->entity.instance;
272 	rqdata[6] = 0;
273 	rqdata_len = 7;
274 
275 	req.msg.netfn = IPMI_NETFN_SUNOEM;
276 	req.msg.cmd = IPMI_SUNOEM_LED_GET;
277 	req.msg.lun = dev->lun;
278 	req.msg.data = rqdata;
279 	req.msg.data_len = rqdata_len;
280 
281 	rsp = intf->sendrecv(intf, &req);
282 	/*
283 	 * Just return NULL if there was
284 	 * an error.
285 	 */
286 	if (rsp == NULL) {
287 		*loc_rsp = NULL;
288 		return (SUNOEM_EC_BMC_NOT_RESPONDING);
289 	} else if (rsp->ccode > 0) {
290 		*loc_rsp = rsp;
291 		return (SUNOEM_EC_BMC_CCODE_NONZERO);
292 	} else {
293 		*loc_rsp = rsp;
294 		return (SUNOEM_EC_SUCCESS);
295 	}
296 }
297 
298 static struct ipmi_rs *
299 sunoem_led_set(struct ipmi_intf * intf, struct sdr_record_generic_locator * dev,
300 		int ledtype, int ledmode)
301 {
302 	struct ipmi_rs * rsp;
303 	struct ipmi_rq req;
304 	uint8_t rqdata[9];
305 	int rqdata_len;
306 
307 	if (dev == NULL)
308 		return NULL;
309 
310 	rqdata[0] = dev->dev_slave_addr;
311 
312 	if (ledtype == 0xFF)
313 		rqdata[1] = dev->oem;
314 	else
315 		rqdata[1] = ledtype;
316 
317 	rqdata[2] = dev->dev_access_addr;
318 	rqdata[3] = dev->oem;
319 	rqdata[4] = ledmode;
320 	rqdata[5] = dev->entity.id;
321 	rqdata[6] = dev->entity.instance;
322 	rqdata[7] = 0;
323 	rqdata[8] = 0;
324 	rqdata_len = 9;
325 
326 	req.msg.netfn = IPMI_NETFN_SUNOEM;
327 	req.msg.cmd = IPMI_SUNOEM_LED_SET;
328 	req.msg.lun = dev->lun;
329 	req.msg.data = rqdata;
330 	req.msg.data_len = rqdata_len;
331 
332 	rsp = intf->sendrecv(intf, &req);
333 	if (rsp == NULL) {
334 		lprintf(LOG_ERR, "Sun OEM Set LED command failed.");
335 		return NULL;
336 	} else if (rsp->ccode > 0) {
337 		lprintf(LOG_ERR, "Sun OEM Set LED command failed: %s",
338 				val2str(rsp->ccode, completion_code_vals));
339 		return NULL;
340 	}
341 
342 	return (rsp);
343 }
344 
345 static void
346 sunoem_led_get_byentity(struct ipmi_intf * intf, uint8_t entity_id,
347 		uint8_t entity_inst, int ledtype)
348 {
349 	struct ipmi_rs * rsp;
350 	struct sdr_record_list *elist, *e;
351 	struct entity_id entity;
352 	sunoem_ec_t res;
353 
354 	if (entity_id == 0)
355 		return;
356 
357 	/* lookup sdrs with this entity */
358 	memset(&entity, 0, sizeof(struct entity_id));
359 	entity.id = entity_id;
360 	entity.instance = entity_inst;
361 
362 	elist = ipmi_sdr_find_sdr_byentity(intf, &entity);
363 
364 	if (elist == NULL)
365 		ret_get = -1;
366 
367 	/* for each generic sensor get its led state */
368 	for (e = elist; e != NULL; e = e->next) {
369 		if (e->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR)
370 			continue;
371 
372 		res = sunoem_led_get(intf, e->record.genloc, ledtype, &rsp);
373 
374 		if (res == SUNOEM_EC_SUCCESS && rsp && rsp->data_len == 1) {
375 			led_print((const char *) e->record.genloc->id_string, PRINT_NORMAL,
376 					rsp->data[0]);
377 		} else {
378 			led_print((const char *) e->record.genloc->id_string, PRINT_ERROR,
379 					0);
380 			if (res != SUNOEM_EC_BMC_CCODE_NONZERO|| !rsp
381 			|| rsp->ccode != CC_DEST_UNAVAILABLE) {
382 				ret_get = -1;
383 			}
384 		}
385 	}
386 	__sdr_list_empty(elist);
387 }
388 
389 static void
390 sunoem_led_set_byentity(struct ipmi_intf * intf, uint8_t entity_id,
391 		uint8_t entity_inst, int ledtype, int ledmode)
392 {
393 	struct ipmi_rs * rsp;
394 	struct sdr_record_list *elist, *e;
395 	struct entity_id entity;
396 
397 	if (entity_id == 0)
398 		return;
399 
400 	/* lookup sdrs with this entity */
401 	memset(&entity, 0, sizeof(struct entity_id));
402 	entity.id = entity_id;
403 	entity.instance = entity_inst;
404 
405 	elist = ipmi_sdr_find_sdr_byentity(intf, &entity);
406 
407 	if (elist == NULL)
408 		ret_set = -1;
409 
410 	/* for each generic sensor set its led state */
411 	for (e = elist; e != NULL; e = e->next) {
412 
413 		if (e->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR)
414 			continue;
415 
416 		rsp = sunoem_led_set(intf, e->record.genloc, ledtype, ledmode);
417 		if (rsp && rsp->data_len == 0) {
418 			led_print((const char *) e->record.genloc->id_string, PRINT_NORMAL,
419 					ledmode);
420 		} else if (rsp == NULL) {
421 			ret_set = -1;
422 		}
423 	}
424 
425 	__sdr_list_empty(elist);
426 }
427 
428 /*
429  * IPMI Request Data: 5 bytes
430  *
431  * [byte 0]  devAddr     Value from the "Device Slave Address" field in
432  *                       LED's Generic Device Locator record in the SDR
433  * [byte 1]  led         LED Type: OK2RM, ACT, LOCATE, SERVICE
434  * [byte 2]  ctrlrAddr   Controller address; value from the "Device
435  *                       Access Address" field, 0x20 if the LED is local
436  * [byte 3]  hwInfo      The OEM field from the SDR record
437  * [byte 4]  force       1 = directly access the device
438  *                       0 = go thru its controller
439  *                       Ignored if LED is local
440  *
441  * The format below is for Sun Blade Modular systems only
442  * [byte 4]  entityID    The entityID field from the SDR record
443  * [byte 5]  entityIns   The entityIns field from the SDR record
444  * [byte 6]  force       1 = directly access the device
445  *                       0 = go thru its controller
446  *                       Ignored if LED is local
447  */
448 static int
449 ipmi_sunoem_led_get(struct ipmi_intf * intf, int argc, char ** argv)
450 {
451 	struct ipmi_rs * rsp;
452 	struct sdr_record_list *sdr;
453 	struct sdr_record_list *alist, *a;
454 	struct sdr_record_entity_assoc *assoc;
455 	int ledtype = 0xFF;
456 	int i;
457 	sunoem_ec_t res;
458 
459 	/*
460 	 * sunoem led/sbled get <id> [type]
461 	 */
462 
463 	if (argc < 1 || strncmp(argv[0], "help", 4) == 0) {
464 		ipmi_sunoem_usage();
465 		return (0);
466 	}
467 
468 	if (argc > 1) {
469 		ledtype = str2val(argv[1], sunoem_led_type_vals);
470 		if (ledtype == 0xFF)
471 			lprintf(LOG_ERR,
472 					"Unknow ledtype, will use data from the SDR oem field");
473 	}
474 
475 	if (strncasecmp(argv[0], "all", 3) == 0) {
476 		/* do all generic sensors */
477 		alist = ipmi_sdr_find_sdr_bytype(intf,
478 		SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR);
479 
480 		if (alist == NULL)
481 			return (-1);
482 
483 		for (a = alist; a != NULL; a = a->next) {
484 			if (a->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR)
485 				continue;
486 			if (a->record.genloc->entity.logical)
487 				continue;
488 
489 			res = sunoem_led_get(intf, a->record.genloc, ledtype, &rsp);
490 
491 			if (res == SUNOEM_EC_SUCCESS && rsp && rsp->data_len == 1) {
492 				led_print((const char *) a->record.genloc->id_string,
493 						PRINT_NORMAL, rsp->data[0]);
494 			} else {
495 				led_print((const char *) a->record.genloc->id_string,
496 						PRINT_ERROR, 0);
497 				if (res != SUNOEM_EC_BMC_CCODE_NONZERO|| !rsp ||
498 				rsp->ccode != CC_DEST_UNAVAILABLE) {
499 					ret_get = -1;
500 				}
501 			}
502 		}
503 		__sdr_list_empty(alist);
504 
505 		if (ret_get == -1)
506 			return (-1);
507 
508 		return (0);
509 	}
510 
511 	/* look up generic device locator record in SDR */
512 	sdr = ipmi_sdr_find_sdr_byid(intf, argv[0]);
513 
514 	if (sdr == NULL) {
515 		lprintf(LOG_ERR, "No Sensor Data Record found for %s", argv[0]);
516 		return (-1);
517 	}
518 
519 	if (sdr->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) {
520 		lprintf(LOG_ERR, "Invalid SDR type %d", sdr->type);
521 		return (-1);
522 	}
523 
524 	if (!sdr->record.genloc->entity.logical) {
525 		/*
526 		 * handle physical entity
527 		 */
528 
529 		res = sunoem_led_get(intf, sdr->record.genloc, ledtype, &rsp);
530 
531 		if (res == SUNOEM_EC_SUCCESS && rsp && rsp->data_len == 1) {
532 			led_print((const char *) sdr->record.genloc->id_string,
533 					PRINT_NORMAL, rsp->data[0]);
534 
535 		} else {
536 			led_print((const char *) sdr->record.genloc->id_string, PRINT_ERROR,
537 					0);
538 			if (res != SUNOEM_EC_BMC_CCODE_NONZERO|| !rsp
539 			|| rsp->ccode != CC_DEST_UNAVAILABLE) {
540 				ret_get = -1;
541 			}
542 		}
543 
544 		if (ret_get == -1)
545 			return (-1);
546 
547 		return (0);
548 	}
549 
550 	/*
551 	 * handle logical entity for LED grouping
552 	 */
553 
554 	lprintf(LOG_INFO, "LED %s is logical device", argv[0]);
555 
556 	/* get entity assoc records */
557 	alist = ipmi_sdr_find_sdr_bytype(intf, SDR_RECORD_TYPE_ENTITY_ASSOC);
558 
559 	if (alist == NULL)
560 		return (-1);
561 
562 	for (a = alist; a != NULL; a = a->next) {
563 		if (a->type != SDR_RECORD_TYPE_ENTITY_ASSOC)
564 			continue;
565 		assoc = a->record.entassoc;
566 		if (assoc == NULL)
567 			continue;
568 
569 		/* check that the entity id/instance matches our generic record */
570 		if (assoc->entity.id != sdr->record.genloc->entity.id
571 				|| assoc->entity.instance
572 						!= sdr->record.genloc->entity.instance)
573 			continue;
574 
575 		if (assoc->flags.isrange) {
576 			/*
577 			 * handle ranged entity associations
578 			 *
579 			 * the test for non-zero entity id is handled in
580 			 * sunoem_led_get_byentity()
581 			 */
582 
583 			/* first range set - id 1 and 2 must be equal */
584 			if (assoc->entity_id_1 == assoc->entity_id_2)
585 				for (i = assoc->entity_inst_1; i <= assoc->entity_inst_2; i++)
586 					sunoem_led_get_byentity(intf, assoc->entity_id_1, i,
587 							ledtype);
588 
589 			/* second range set - id 3 and 4 must be equal */
590 			if (assoc->entity_id_3 == assoc->entity_id_4)
591 				for (i = assoc->entity_inst_3; i <= assoc->entity_inst_4; i++)
592 					sunoem_led_get_byentity(intf, assoc->entity_id_3, i,
593 							ledtype);
594 		} else {
595 			/*
596 			 * handle entity list
597 			 */
598 			sunoem_led_get_byentity(intf, assoc->entity_id_1,
599 					assoc->entity_inst_1, ledtype);
600 			sunoem_led_get_byentity(intf, assoc->entity_id_2,
601 					assoc->entity_inst_2, ledtype);
602 			sunoem_led_get_byentity(intf, assoc->entity_id_3,
603 					assoc->entity_inst_3, ledtype);
604 			sunoem_led_get_byentity(intf, assoc->entity_id_4,
605 					assoc->entity_inst_4, ledtype);
606 		}
607 	}
608 
609 	__sdr_list_empty(alist);
610 
611 	if (ret_get == -1)
612 		return (-1);
613 
614 	return (0);
615 }
616 
617 /*
618  * IPMI Request Data: 7 bytes
619  *
620  * [byte 0]  devAddr     Value from the "Device Slave Address" field in
621  *                       LED's Generic Device Locator record in the SDR
622  * [byte 1]  led         LED Type: OK2RM, ACT, LOCATE, SERVICE
623  * [byte 2]  ctrlrAddr   Controller address; value from the "Device
624  *                       Access Address" field, 0x20 if the LED is local
625  * [byte 3]  hwInfo      The OEM field from the SDR record
626  * [byte 4]  mode        LED Mode: OFF, ON, STANDBY, SLOW, FAST
627  * [byte 5]  force       TRUE - directly access the device
628  *                       FALSE - go thru its controller
629  *                       Ignored if LED is local
630  * [byte 6]  role        Used by BMC for authorization purposes
631  *
632  * The format below is for Sun Blade Modular systems only
633  * [byte 5]  entityID    The entityID field from the SDR record
634  * [byte 6]  entityIns   The entityIns field from the SDR record
635  * [byte 7]  force       TRUE - directly access the device
636  *                       FALSE - go thru its controller
637  *                       Ignored if LED is local
638  * [byte 8]  role        Used by BMC for authorization purposes
639  *
640  *
641  * IPMI Response Data: 1 byte
642  *
643  * [byte 0]  mode     LED Mode: OFF, ON, STANDBY, SLOW, FAST
644  */
645 
646 static int
647 ipmi_sunoem_led_set(struct ipmi_intf * intf, int argc, char ** argv)
648 {
649 	struct ipmi_rs * rsp;
650 	struct sdr_record_list *sdr;
651 	struct sdr_record_list *alist, *a;
652 	struct sdr_record_entity_assoc *assoc;
653 	int ledmode;
654 	int ledtype = 0xFF;
655 	int i;
656 
657 	/*
658 	 * sunoem led/sbled set <id> <mode> [type]
659 	 */
660 
661 	if (argc < 2 || strncmp(argv[0], "help", 4) == 0) {
662 		ipmi_sunoem_usage();
663 		return (0);
664 	}
665 
666 	ledmode = str2val(argv[1], sunoem_led_mode_vals);
667 	if (ledmode == 0xFF) {
668 		ledmode = str2val(argv[1], sunoem_led_mode_optvals);
669 		if (ledmode == 0xFF) {
670 			lprintf(LOG_NOTICE, "Invalid LED Mode: %s", argv[1]);
671 			return (-1);
672 		}
673 	}
674 
675 	if (argc > 3) {
676 		ledtype = str2val(argv[2], sunoem_led_type_vals);
677 		if (ledtype == 0xFF)
678 			lprintf(LOG_ERR,
679 					"Unknow ledtype, will use data from the SDR oem field");
680 	}
681 
682 	if (strncasecmp(argv[0], "all", 3) == 0) {
683 		/* do all generic sensors */
684 		alist = ipmi_sdr_find_sdr_bytype(intf,
685 		SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR);
686 
687 		if (alist == NULL)
688 			return (-1);
689 
690 		for (a = alist; a != NULL; a = a->next) {
691 			if (a->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR)
692 				continue;
693 			if (a->record.genloc->entity.logical)
694 				continue;
695 			rsp = sunoem_led_set(intf, a->record.genloc, ledtype, ledmode);
696 			if (rsp && rsp->ccode == 0)
697 				led_print((const char *) a->record.genloc->id_string,
698 						PRINT_NORMAL, ledmode);
699 			else
700 				ret_set = -1;
701 		}
702 		__sdr_list_empty(alist);
703 
704 		if (ret_set == -1)
705 			return (-1);
706 
707 		return (0);
708 	}
709 
710 	/* look up generic device locator records in SDR */
711 	sdr = ipmi_sdr_find_sdr_byid(intf, argv[0]);
712 
713 	if (sdr == NULL) {
714 		lprintf(LOG_ERR, "No Sensor Data Record found for %s", argv[0]);
715 		return (-1);
716 	}
717 
718 	if (sdr->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) {
719 		lprintf(LOG_ERR, "Invalid SDR type %d", sdr->type);
720 		return (-1);
721 	}
722 
723 	if (!sdr->record.genloc->entity.logical) {
724 		/*
725 		 * handle physical entity
726 		 */
727 		rsp = sunoem_led_set(intf, sdr->record.genloc, ledtype, ledmode);
728 		if (rsp && rsp->ccode == 0)
729 			led_print(argv[0], PRINT_NORMAL, ledmode);
730 		else
731 			return (-1);
732 
733 		return (0);
734 	}
735 
736 	/*
737 	 * handle logical entity for LED grouping
738 	 */
739 
740 	lprintf(LOG_INFO, "LED %s is logical device", argv[0]);
741 
742 	/* get entity assoc records */
743 	alist = ipmi_sdr_find_sdr_bytype(intf, SDR_RECORD_TYPE_ENTITY_ASSOC);
744 
745 	if (alist == NULL)
746 		return (-1);
747 
748 	for (a = alist; a != NULL; a = a->next) {
749 		if (a->type != SDR_RECORD_TYPE_ENTITY_ASSOC)
750 			continue;
751 		assoc = a->record.entassoc;
752 		if (assoc == NULL)
753 			continue;
754 
755 		/* check that the entity id/instance matches our generic record */
756 		if (assoc->entity.id != sdr->record.genloc->entity.id
757 				|| assoc->entity.instance
758 						!= sdr->record.genloc->entity.instance)
759 			continue;
760 
761 		if (assoc->flags.isrange) {
762 			/*
763 			 * handle ranged entity associations
764 			 *
765 			 * the test for non-zero entity id is handled in
766 			 * sunoem_led_get_byentity()
767 			 */
768 
769 			/* first range set - id 1 and 2 must be equal */
770 			if (assoc->entity_id_1 == assoc->entity_id_2)
771 				for (i = assoc->entity_inst_1; i <= assoc->entity_inst_2; i++)
772 					sunoem_led_set_byentity(intf, assoc->entity_id_1, i,
773 							ledtype, ledmode);
774 
775 			/* second range set - id 3 and 4 must be equal */
776 			if (assoc->entity_id_3 == assoc->entity_id_4)
777 				for (i = assoc->entity_inst_3; i <= assoc->entity_inst_4; i++)
778 					sunoem_led_set_byentity(intf, assoc->entity_id_3, i,
779 							ledtype, ledmode);
780 		} else {
781 			/*
782 			 * handle entity list
783 			 */
784 			sunoem_led_set_byentity(intf, assoc->entity_id_1,
785 					assoc->entity_inst_1, ledtype, ledmode);
786 			sunoem_led_set_byentity(intf, assoc->entity_id_2,
787 					assoc->entity_inst_2, ledtype, ledmode);
788 			sunoem_led_set_byentity(intf, assoc->entity_id_3,
789 					assoc->entity_inst_3, ledtype, ledmode);
790 			sunoem_led_set_byentity(intf, assoc->entity_id_4,
791 					assoc->entity_inst_4, ledtype, ledmode);
792 		}
793 	}
794 
795 	__sdr_list_empty(alist);
796 
797 	if (ret_set == -1)
798 		return (-1);
799 
800 	return (0);
801 }
802 
803 static int
804 ipmi_sunoem_sshkey_del(struct ipmi_intf * intf, uint8_t uid)
805 {
806 	struct ipmi_rs * rsp;
807 	struct ipmi_rq req;
808 
809 	memset(&req, 0, sizeof(struct ipmi_rq));
810 	req.msg.netfn = IPMI_NETFN_SUNOEM;
811 	req.msg.cmd = IPMI_SUNOEM_DEL_SSH_KEY;
812 	req.msg.data = &uid;
813 	req.msg.data_len = 1;
814 
815 	rsp = intf->sendrecv(intf, &req);
816 	if (rsp == NULL) {
817 		lprintf(LOG_ERR, "Unable to delete ssh key for UID %d", uid);
818 		return (-1);
819 	} else if (rsp->ccode > 0) {
820 		lprintf(LOG_ERR, "Unable to delete ssh key for UID %d: %s", uid,
821 				val2str(rsp->ccode, completion_code_vals));
822 		return (-1);
823 	}
824 
825 	printf("Deleted SSH key for user id %d\n", uid);
826 	return (0);
827 }
828 
829 #define SSHKEY_BLOCK_SIZE	64
830 static int
831 ipmi_sunoem_sshkey_set(struct ipmi_intf * intf, uint8_t uid, char * ifile)
832 {
833 	struct ipmi_rs * rsp;
834 	struct ipmi_rq req;
835 	FILE * fp;
836 	int count = 0;
837 	uint8_t wbuf[SSHKEY_BLOCK_SIZE + 3];
838 	int32_t i_size = 0;
839 	int32_t r = 0;
840 	int32_t size = 0;
841 
842 	if (ifile == NULL) {
843 		lprintf(LOG_ERR, "Invalid or misisng input filename.");
844 		return (-1);
845 	}
846 
847 	fp = ipmi_open_file_read(ifile);
848 	if (fp == NULL) {
849 		lprintf(LOG_ERR, "Unable to open file '%s' for reading.", ifile);
850 		return (-1);
851 	}
852 
853 	memset(&req, 0, sizeof(struct ipmi_rq));
854 	req.msg.netfn = IPMI_NETFN_SUNOEM;
855 	req.msg.cmd = IPMI_SUNOEM_SET_SSH_KEY;
856 	req.msg.data = wbuf;
857 
858 	if (fseek(fp, 0, SEEK_END) == (-1)) {
859 		lprintf(LOG_ERR, "Failed to seek in file '%s'.", ifile);
860 		if (fp != NULL)
861 			fclose(fp);
862 
863 		return (-1);
864 	}
865 
866 	size = (int32_t) ftell(fp);
867 	if (size < 0) {
868 		lprintf(LOG_ERR, "Failed to seek in file '%s'.", ifile);
869 		if (fp != NULL)
870 			fclose(fp);
871 
872 		return (-1);
873 	} else if (size == 0) {
874 		lprintf(LOG_ERR, "File '%s' is empty.", ifile);
875 		if (fp != NULL)
876 			fclose(fp);
877 
878 		return (-1);
879 	}
880 
881 	if (fseek(fp, 0, SEEK_SET) == (-1)) {
882 		lprintf(LOG_ERR, "Failed to seek in file '%s'.", ifile);
883 		if (fp != NULL)
884 			fclose(fp);
885 
886 		return (-1);
887 	}
888 
889 	printf("Setting SSH key for user id %d...", uid);
890 
891 	for (r = 0; r < size; r += i_size) {
892 		i_size = size - r;
893 		if (i_size > SSHKEY_BLOCK_SIZE)
894 			i_size = SSHKEY_BLOCK_SIZE;
895 
896 		memset(wbuf, 0, SSHKEY_BLOCK_SIZE);
897 		fseek(fp, r, SEEK_SET);
898 		count = fread(wbuf + 3, 1, i_size, fp);
899 		if (count != i_size) {
900 			printf("failed\n");
901 			lprintf(LOG_ERR, "Unable to read %ld bytes from file '%s'.", i_size,
902 					ifile);
903 			if (fp != NULL)
904 				fclose(fp);
905 
906 			return (-1);
907 		}
908 
909 		printf(".");
910 		fflush(stdout);
911 
912 		wbuf[0] = uid;
913 		if ((r + SSHKEY_BLOCK_SIZE) >= size)
914 			wbuf[1] = 0xff;
915 		else {
916 			if ((r / SSHKEY_BLOCK_SIZE) > UINT8_MAX) {
917 				printf("failed\n");
918 				lprintf(LOG_ERR, "Unable to pack byte %ld from file '%s'.", r,
919 						ifile);
920 				if (fp != NULL)
921 					fclose(fp);
922 
923 				return (-1);
924 			}
925 			wbuf[1] = (uint8_t) (r / SSHKEY_BLOCK_SIZE);
926 		}
927 
928 		wbuf[2] = (uint8_t) i_size;
929 
930 		req.msg.data_len = i_size + 3;
931 
932 		rsp = intf->sendrecv(intf, &req);
933 		if (rsp == NULL) {
934 			printf("failed\n");
935 			lprintf(LOG_ERR, "Unable to set ssh key for UID %d.", uid);
936 			if (fp != NULL)
937 				fclose(fp);
938 
939 			return (-1);
940 		} /* if (rsp == NULL) */
941 		if (rsp->ccode != 0) {
942 			printf("failed\n");
943 			lprintf(LOG_ERR, "Unable to set ssh key for UID %d, %s.", uid,
944 					val2str(rsp->ccode, completion_code_vals));
945 			if (fp != NULL)
946 				fclose(fp);
947 
948 			return (-1);
949 		} /* if (rsp->ccode != 0) */
950 	}
951 
952 	printf("done\n");
953 
954 	fclose(fp);
955 	return (0);
956 }
957 
958 /*
959  * This structure is used in both the request to and response from the BMC.
960  */
961 #define SUNOEM_CLI_LEGACY_VERSION       1
962 #define SUNOEM_CLI_SEQNUM_VERSION       2
963 #define SUNOEM_CLI_VERSION       SUNOEM_CLI_SEQNUM_VERSION
964 #define SUNOEM_CLI_HEADER        8 /* command + spare + handle */
965 #define SUNOEM_CLI_BUF_SIZE      (80 - SUNOEM_CLI_HEADER) /* Total 80 bytes */
966 #define SUNOEM_CLI_MSG_SIZE(msg) (SUNOEM_CLI_HEADER + strlen((msg).buf) + 1)
967 
968 #ifdef HAVE_PRAGMA_PACK
969 #pragma pack(push, 1)
970 #endif
971 typedef struct
972 {
973 	/*
974 	 * Set version to SUNOEM_CLI_VERSION.
975 	 */
976 	uint8_t version;
977 	/*
978 	 * The command in a request, or in a response indicates an error if
979 	 * non-zero.
980 	 */
981 	uint8_t command_response;
982 	uint8_t seqnum;
983 	uint8_t spare;
984 	/*
985 	 * Opaque 4-byte handle, supplied in the response to an OPEN request,
986 	 * and used in all subsequent POLL and CLOSE requests.
987 	 */
988 	uint8_t handle[4];
989 	/*
990 	 * The client data in a request, or the server data in a response. Must
991 	 * by null terminated, i.e., it must be at least one byte, but can be
992 	 * smaller if there's less data.
993 	 */
994 	char buf[SUNOEM_CLI_BUF_SIZE];
995 }__attribute__((packed)) sunoem_cli_msg_t;
996 #ifdef HAVE_PRAGMA_PACK
997 #pragma pack(pop)
998 #endif
999 
1000 /*
1001  * Command codes for the command_request field in each request.
1002  */
1003 #define SUNOEM_CLI_CMD_OPEN   0 /* Open a new connection */
1004 #define SUNOEM_CLI_CMD_FORCE  1 /* Close any existing connection, then open */
1005 #define SUNOEM_CLI_CMD_CLOSE  2 /* Close the current connection */
1006 #define SUNOEM_CLI_CMD_POLL   3 /* Poll for new data to/from the server */
1007 #define SUNOEM_CLI_CMD_EOF    4 /* Poll, client is out of data */
1008 
1009 #define SUNOEM_CLI_MAX_RETRY  3 /* Maximum number of retries */
1010 
1011 #define SUNOEM_CLI_INVALID_VER_ERR "Invalid version"
1012 #define SUNOEM_CLI_BUSY_ERR        "Busy"
1013 
1014 typedef enum
1015 {
1016 	C_CTL_B = 0x02, /* same as left arrow */
1017 	C_CTL_C = 0x03,
1018 	C_CTL_D = 0x04,
1019 	C_CTL_F = 0x06, /* same as right arrow */
1020 	C_CTL_N = 0x0E, /* same as down arrow */
1021 	C_CTL_P = 0x10, /* same as up arrow */
1022 	C_DEL = 0x7f
1023 } canon_char_t;
1024 
1025 static int
1026 sunoem_cli_unbufmode_start(FILE *f, struct termios *orig_ts)
1027 {
1028 	struct termios ts;
1029 	int rc;
1030 
1031 	if ((rc = tcgetattr(fileno(f), &ts))) {
1032 		return (rc);
1033 	}
1034 	*orig_ts = ts;
1035 	ts.c_lflag &= ~(ICANON | ECHO | ISIG);
1036 	ts.c_cc[VMIN] = 1;
1037 	if ((rc = tcsetattr(fileno(f), TCSAFLUSH, &ts))) {
1038 		return (rc);
1039 	}
1040 
1041 	return (0);
1042 }
1043 
1044 static int
1045 sunoem_cli_unbufmode_stop(FILE *f, struct termios *ts)
1046 {
1047 	int rc;
1048 
1049 	if ((rc = tcsetattr(fileno(f), TCSAFLUSH, ts))) {
1050 		return (rc);
1051 	}
1052 
1053 	return (0);
1054 }
1055 
1056 static int
1057 ipmi_sunoem_cli(struct ipmi_intf * intf, int argc, char *argv[])
1058 {
1059 	struct ipmi_rs *rsp;
1060 	struct ipmi_rq req;
1061 	sunoem_cli_msg_t cli_req;
1062 	sunoem_cli_msg_t *cli_rsp;
1063 	int arg_num = 0;
1064 	int arg_pos = 0;
1065 	time_t wait_time = 0;
1066 	int retries;
1067 	static uint8_t SunOemCliActingVersion = SUNOEM_CLI_VERSION;
1068 
1069 	unsigned short first_char = 0; /*first char on the line*/
1070 	struct termios orig_ts;
1071 	int error = 0;
1072 
1073 	time_t now = 0;
1074 	int delay = 0;
1075 
1076 	/* Prepare to open an SP shell session */
1077 	memset(&cli_req, 0, sizeof(cli_req));
1078 	cli_req.version = SunOemCliActingVersion;
1079 	cli_req.command_response = SUNOEM_CLI_CMD_OPEN;
1080 	if (argc > 0 && strcmp(argv[0], "force") == 0) {
1081 		cli_req.command_response = SUNOEM_CLI_CMD_FORCE;
1082 		argc--;
1083 		argv++;
1084 	}
1085 	memset(&req, 0, sizeof(req));
1086 	req.msg.netfn = IPMI_NETFN_SUNOEM;
1087 	req.msg.cmd = IPMI_SUNOEM_CLI;
1088 	req.msg.data = (uint8_t *) &cli_req;
1089 	req.msg.data_len = SUNOEM_CLI_HEADER + 1;
1090 	retries = 0;
1091 	while (1) {
1092 		cli_req.version = SunOemCliActingVersion;
1093 		rsp = intf->sendrecv(intf, &req);
1094 		if (rsp == NULL) {
1095 			lprintf(LOG_ERR, "Sun OEM cli command failed");
1096 			return (-1);
1097 		}
1098 		cli_rsp = (sunoem_cli_msg_t *) rsp->data;
1099 		if ((cli_rsp->command_response != 0) || (rsp->ccode != 0)) {
1100 			if (strncmp(cli_rsp->buf, SUNOEM_CLI_INVALID_VER_ERR,
1101 					sizeof(SUNOEM_CLI_INVALID_VER_ERR) - 1) == 0
1102 					|| strncmp(&(cli_rsp->buf[1]), SUNOEM_CLI_INVALID_VER_ERR,
1103 							sizeof(SUNOEM_CLI_INVALID_VER_ERR) - 1) == 0) {
1104 				if (SunOemCliActingVersion == SUNOEM_CLI_VERSION) {
1105 					/* Server doesn't support version SUNOEM_CLI_VERSION
1106 					 Fall back to legacy version, and try again*/
1107 					SunOemCliActingVersion = SUNOEM_CLI_LEGACY_VERSION;
1108 					continue;
1109 				}
1110 				/* Server doesn't support legacy version either */
1111 				lprintf(LOG_ERR, "Failed to connect: %s", cli_rsp->buf);
1112 				return (-1);
1113 			} else if (strncmp(cli_rsp->buf, SUNOEM_CLI_BUSY_ERR,
1114 					sizeof(SUNOEM_CLI_BUSY_ERR) - 1) == 0) {
1115 				if (retries++ < SUNOEM_CLI_MAX_RETRY) {
1116 					lprintf(LOG_INFO, "Failed to connect: %s, retrying",
1117 							cli_rsp->buf);
1118 					sleep(2);
1119 					continue;
1120 				}
1121 				lprintf(LOG_ERR, "Failed to connect: %s", cli_rsp->buf);
1122 				return (-1);
1123 			} else {
1124 				lprintf(LOG_ERR, "Failed to connect: %s", cli_rsp->buf);
1125 				return (-1);
1126 			}
1127 		}
1128 		break;
1129 	}
1130 	if (SunOemCliActingVersion == SUNOEM_CLI_SEQNUM_VERSION) {
1131 		/*
1132 		 * Bit 1 of seqnum is used as an alternating sequence number
1133 		 * to allow a server that supports it to detect when a retry is being sent from the host IPMI driver.
1134 		 * Typically when this occurs, the server's last response message would have been dropped.
1135 		 * Once the server detects this condition, it will know that it should retry sending the response.
1136 		 */
1137 		cli_req.seqnum ^= 0x1;
1138 	}
1139 	printf("Connected. Use ^D to exit.\n");
1140 	fflush(NULL);
1141 
1142 	/*
1143 	 * Remember the handle provided in the response, and issue a
1144 	 * series of "poll" commands to send and get data
1145 	 */
1146 	memcpy(cli_req.handle, cli_rsp->handle, 4);
1147 	cli_req.command_response = SUNOEM_CLI_CMD_POLL;
1148 	/*
1149 	 * If no arguments make input unbuffered and so interactive
1150 	 */
1151 	if (argc == 0) {
1152 		if (sunoem_cli_unbufmode_start(stdin, &orig_ts)) {
1153 			lprintf(LOG_ERR, "Failed to set interactive mode: %s",
1154 					strerror(errno));
1155 			return (-1);
1156 		}
1157 	}
1158 	while (rsp->ccode == 0 && cli_rsp->command_response == 0) {
1159 		int rc = 0;
1160 		int count = 0;
1161 		cli_req.buf[0] = '\0';
1162 		if (argc == 0) {
1163 			/*
1164 			 * Accept input from stdin. Use select so we don't hang if
1165 			 * there's no input to read. Select timeout is 500 msec.
1166 			 */
1167 			struct timeval tv = { 0, 500000 }; /* 500 msec */
1168 			fd_set rfds;
1169 			FD_ZERO(&rfds);
1170 			FD_SET(0, &rfds);
1171 			rc = select(1, &rfds, NULL, NULL, &tv);
1172 			if (rc < 0) {
1173 				/* Select returned an error so close and exit */
1174 				printf("Broken pipe\n");
1175 				cli_req.command_response = SUNOEM_CLI_CMD_CLOSE;
1176 			} else if (rc > 0) {
1177 				/* Read data from stdin */
1178 				count = read(0, cli_req.buf, 1 /* sizeof (cli_req.buf) - 1 */);
1179 				/*
1180 				 * If select said there was data but there was nothing to
1181 				 * read. This implies user hit ^D.
1182 				 * Also handle ^D input when pressed as first char at a new line.
1183 				 */
1184 				if (count <= 0 || (first_char && cli_req.buf[0] == C_CTL_D)) {
1185 					cli_req.command_response = SUNOEM_CLI_CMD_EOF;
1186 					count = 0;
1187 				}
1188 				first_char = cli_req.buf[0] == '\n' || cli_req.buf[0] == '\r';
1189 			}
1190 		} else {
1191 			/*
1192 			 * Get data from command line arguments
1193 			 */
1194 			now = time(NULL);
1195 			if (now < wait_time) {
1196 				/* Do nothing; we're waiting */
1197 			} else if (arg_num >= argc) {
1198 				/* Last arg was sent. Set EOF */
1199 				cli_req.command_response = SUNOEM_CLI_CMD_EOF;
1200 			} else if (strncmp(argv[arg_num], "@wait=", 6) == 0) {
1201 				/* This is a wait command */
1202 				char *s = &argv[arg_num][6];
1203 				delay = 0;
1204 				if (*s != '\0') {
1205 					if (str2int(s, &delay)) {
1206 						delay = 0;
1207 					}
1208 					if (delay < 0) {
1209 						delay = 0;
1210 					}
1211 				}
1212 				wait_time = now + delay;
1213 				arg_num++;
1214 			} else {
1215 				/*
1216 				 * Take data from args. It may be that the argument is larger
1217 				 * than the request buffer can hold. So pull off BUF_SIZE
1218 				 * number of characters at a time. When we've consumed the
1219 				 * entire arg, append a newline and advance to the next arg.
1220 				 */
1221 				int i;
1222 				char *s = argv[arg_num];
1223 				for (i = arg_pos;
1224 						s[i] != '\0' && count < (SUNOEM_CLI_BUF_SIZE - 2);
1225 						i++, count++) {
1226 					cli_req.buf[count] = s[i];
1227 				}
1228 				if (s[i] == '\0') {
1229 					/* Reached end of the arg string, so append a newline */
1230 					cli_req.buf[count++] = '\n';
1231 					/* Reset pos to 0 and advance to the next arg next time */
1232 					arg_pos = 0;
1233 					arg_num++;
1234 				} else {
1235 					/*
1236 					 * Otherwise, there's still more characters in the arg
1237 					 * to send, so remember where we left off
1238 					 */
1239 					arg_pos = i;
1240 				}
1241 			}
1242 		}
1243 		/*
1244 		 * Now send the clients's data (if any) and get data back from the
1245 		 * server. Loop while the server is giving us data until we suck
1246 		 * it dry.
1247 		 */
1248 		do {
1249 			cli_req.buf[count++] = '\0'; /* Terminate the string */
1250 			memset(&req, 0, sizeof(req));
1251 			req.msg.netfn = IPMI_NETFN_SUNOEM;
1252 			req.msg.cmd = 0x19;
1253 			req.msg.data = (uint8_t *) &cli_req;
1254 			req.msg.data_len = SUNOEM_CLI_HEADER + count;
1255 			for (retries = 0; retries <= SUNOEM_CLI_MAX_RETRY; retries++) {
1256 				rsp = intf->sendrecv(intf, &req);
1257 				if (rsp == NULL) {
1258 					lprintf(LOG_ERR, "Communication error.");
1259 					error = 1;
1260 					goto cleanup;
1261 				}
1262 				if (rsp->ccode == IPMI_CC_TIMEOUT) { /* Retry if timed out. */
1263 					if (retries == SUNOEM_CLI_MAX_RETRY) { /* If it's the last retry. */
1264 						lprintf(LOG_ERR, "Excessive timeout.");
1265 						error = 1;
1266 						goto cleanup;
1267 					}
1268 					continue;
1269 				}
1270 				break;
1271 			} /* for (retries = 0; retries <= SUNOEM_CLI_MAX_RETRY; retries++) */
1272 
1273 			if (SunOemCliActingVersion == SUNOEM_CLI_SEQNUM_VERSION) {
1274 				cli_req.seqnum ^= 0x1; /* Toggle sequence number after request is sent */
1275 			}
1276 
1277 			cli_rsp = (sunoem_cli_msg_t *) rsp->data;
1278 			/* Make sure response string is null terminated */
1279 			cli_rsp->buf[sizeof(cli_rsp->buf) - 1] = '\0';
1280 			printf("%s", cli_rsp->buf);
1281 			fflush(NULL); /* Flush partial lines to stdout */
1282 			count = 0; /* Don't re-send the client's data */
1283 			if (cli_req.command_response == SUNOEM_CLI_CMD_EOF
1284 					&& cli_rsp->command_response != 0 && rsp->ccode == 0) {
1285 				cli_rsp->command_response = 1;
1286 			}
1287 		} while (cli_rsp->command_response == 0 && cli_rsp->buf[0] != '\0');
1288 	}
1289 
1290 cleanup:
1291 	/* Restore original input mode if cli was running interactively */
1292 	if (argc == 0) {
1293 		if (sunoem_cli_unbufmode_stop(stdin, &orig_ts)) {
1294 			lprintf(LOG_ERR, "Failed to restore interactive mode: %s",
1295 					strerror(errno));
1296 			return (-1);
1297 		}
1298 	}
1299 
1300 	return ((error == 0 && cli_rsp->command_response == SUNOEM_SUCCESS) ? 0 : -1);
1301 }
1302 #define ECHO_DATA_SIZE 64
1303 
1304 #ifdef HAVE_PRAGMA_PACK
1305 #pragma pack(push, 1)
1306 #endif
1307 typedef struct
1308 {
1309 	uint16_t seq_num;
1310 	unsigned char data[ECHO_DATA_SIZE];
1311 }__attribute__((packed)) sunoem_echo_msg_t;
1312 #ifdef HAVE_PRAGMA_PACK
1313 #pragma pack(pop)
1314 #endif
1315 
1316 /*
1317  * Send and receive X packets to the BMC. Each packet has a
1318  * payload size of (sunoem_echo_msg_t) bytes. Each packet is tagged with a
1319  * sequence number
1320  */
1321 static int
1322 ipmi_sunoem_echo(struct ipmi_intf * intf, int argc, char *argv[])
1323 {
1324 	struct ipmi_rs *rsp;
1325 	struct ipmi_rq req;
1326 	sunoem_echo_msg_t echo_req;
1327 	sunoem_echo_msg_t *echo_rsp;
1328 	struct timeval start_time;
1329 	struct timeval end_time;
1330 
1331 	int rc = 0;
1332 	int received = 0;
1333 	int transmitted = 0;
1334 	int quiet_mode = 0;
1335 
1336 	uint16_t num, i, j;
1337 	uint32_t total_time, resp_time, min_time, max_time;
1338 
1339 	if (argc < 1) {
1340 		return (1);
1341 	}
1342 
1343 	if (argc == 2) {
1344 		if (*(argv[1]) == 'q') {
1345 			quiet_mode = 1;
1346 		} else {
1347 			lprintf(LOG_ERR, "Unknown option '%s' given.", argv[1]);
1348 			return (-1);
1349 		}
1350 	} else if (argc > 2) {
1351 		lprintf(LOG_ERR,
1352 				"Too many parameters given. See help for more information.");
1353 		return (-1);
1354 	}
1355 	/* The number of packets to send/receive */
1356 	if (str2ushort(argv[0], &num) != 0) {
1357 		lprintf(LOG_ERR,
1358 				"Given number of packets is either invalid or out of range.");
1359 		return (-1);
1360 	}
1361 
1362 	/* Fill in data packet */
1363 	for (i = 0; i < ECHO_DATA_SIZE; i++) {
1364 		echo_req.data[i] = (uint8_t) i;
1365 	}
1366 
1367 	memset(&req, 0, sizeof(req));
1368 	req.msg.netfn = IPMI_NETFN_SUNOEM;
1369 	req.msg.cmd = IPMI_SUNOEM_ECHO;
1370 	req.msg.data = (uint8_t *) &echo_req;
1371 	req.msg.data_len = sizeof(sunoem_echo_msg_t);
1372 	echo_req.seq_num = i;
1373 	min_time = INT_MAX;
1374 	max_time = 0;
1375 	total_time = 0;
1376 	for (i = 0; i < num; i++) {
1377 		echo_req.seq_num = i;
1378 		transmitted++;
1379 		gettimeofday(&start_time, NULL);
1380 		rsp = intf->sendrecv(intf, &req);
1381 		gettimeofday(&end_time, NULL);
1382 		resp_time = ((end_time.tv_sec - start_time.tv_sec) * 1000)
1383 				+ ((end_time.tv_usec - start_time.tv_usec) / 1000);
1384 		if ((rsp == NULL) || (rsp->ccode != 0)) {
1385 			lprintf(LOG_ERR, "Sun OEM echo command failed. Seq # %d",
1386 					echo_req.seq_num);
1387 			rc = (-2);
1388 			break;
1389 		}
1390 		echo_rsp = (sunoem_echo_msg_t *) rsp->data;
1391 
1392 		/* Test if sequence # is valid */
1393 		if (echo_rsp->seq_num != echo_req.seq_num) {
1394 			printf("Invalid Seq # Expecting %d Received %d\n", echo_req.seq_num,
1395 					echo_rsp->seq_num);
1396 			rc = (-2);
1397 			break;
1398 		}
1399 
1400 		/* Test if response length is valid */
1401 		if (rsp->session.msglen == req.msg.data_len) {
1402 			printf("Invalid payload size for seq # %d. "
1403 					"Expecting %d Received %d\n", echo_rsp->seq_num,
1404 					req.msg.data_len, rsp->session.msglen);
1405 			rc = (-2);
1406 			break;
1407 		}
1408 
1409 		/* Test if the data is valid */
1410 		for (j = 0; j < ECHO_DATA_SIZE; j++) {
1411 			if (echo_rsp->data[j] != j) {
1412 				printf("Corrupt data packet. Seq # %d Offset %d\n",
1413 						echo_rsp->seq_num, j);
1414 				break;
1415 			}
1416 		} /* for (j = 0; j < ECHO_DATA_SIZE; j++) */
1417 
1418 		/* If the for loop terminated early - data is corrupt */
1419 		if (j != ECHO_DATA_SIZE) {
1420 			rc = (-2);
1421 			break;
1422 		}
1423 
1424 		/* cumalative time */
1425 		total_time += resp_time;
1426 
1427 		/* min time */
1428 		if (resp_time < min_time) {
1429 			min_time = resp_time;
1430 		}
1431 
1432 		/* max time */
1433 		if (resp_time > max_time) {
1434 			max_time = resp_time;
1435 		}
1436 
1437 		received++;
1438 		if (!quiet_mode) {
1439 			printf("Receive %u Bytes - Seq. # %d time=%d ms\n",
1440 					sizeof(sunoem_echo_msg_t), echo_rsp->seq_num, resp_time);
1441 		}
1442 	} /* for (i = 0; i < num; i++) */
1443 	printf("%d packets transmitted, %d packets received\n", transmitted,
1444 			received);
1445 	if (received) {
1446 		printf("round-trip min/avg/max = %d/%d/%d ms\n", min_time,
1447 				total_time / received, max_time);
1448 	}
1449 
1450 	return (rc);
1451 } /* ipmi_sunoem_echo(...) */
1452 
1453 #ifdef HAVE_PRAGMA_PACK
1454 #pragma pack(push, 1)
1455 #endif
1456 typedef struct
1457 {
1458 	unsigned char oem_record_ver_num;
1459 	unsigned char major;
1460 	unsigned char minor;
1461 	unsigned char update;
1462 	unsigned char micro;
1463 	char nano[10];
1464 	char revision[10];
1465 	char version[40];
1466 	/*
1467 	 * When adding new fields (using the spare bytes),
1468 	 * add it immediately after the spare field to
1469 	 * ensure backward compatability.
1470 	 *
1471 	 * e.g.   char version[40];
1472 	 *        unsigned char spare[11];
1473 	 *        int new_item;
1474 	 *    } sunoem_version_response_t;
1475 	 */
1476 	unsigned char spare[15];
1477 }__attribute__((packed)) sunoem_version_response_t;
1478 #ifdef HAVE_PRAGMA_PACK
1479 #pragma pack(pop)
1480 #endif
1481 
1482 typedef struct
1483 {
1484 	unsigned char major;
1485 	unsigned char minor;
1486 	unsigned char update;
1487 	unsigned char micro;
1488 } supported_version_t;
1489 
1490 static int
1491 ipmi_sunoem_getversion(struct ipmi_intf * intf,
1492 		sunoem_version_response_t **version_rsp)
1493 {
1494 	struct ipmi_rs *rsp;
1495 	struct ipmi_rq req;
1496 
1497 	memset(&req, 0, sizeof(req));
1498 	req.msg.netfn = IPMI_NETFN_SUNOEM;
1499 	req.msg.cmd = IPMI_SUNOEM_VERSION;
1500 	req.msg.data = NULL;
1501 	req.msg.data_len = 0;
1502 	rsp = intf->sendrecv(intf, &req);
1503 
1504 	if (rsp == NULL) {
1505 		lprintf(LOG_ERR, "Sun OEM Get SP Version Failed.");
1506 		return (-1);
1507 	}
1508 	if (rsp->ccode != 0) {
1509 		lprintf(LOG_ERR, "Sun OEM Get SP Version Failed: %d", rsp->ccode);
1510 		return (-1);
1511 	}
1512 
1513 	*version_rsp = (sunoem_version_response_t *) rsp->data;
1514 
1515 	return (0);
1516 }
1517 
1518 static void
1519 ipmi_sunoem_print_required_version(const supported_version_t* supp_ver)
1520 {
1521 	lprintf(LOG_ERR, "Command is not supported by this version of ILOM,"
1522 			" required at least: %d.%d.%d.%d", supp_ver->major, supp_ver->minor,
1523 			supp_ver->update, supp_ver->micro);
1524 }
1525 
1526 /*
1527  * Function checks current version result against required version.
1528  * Returns:
1529  *  - negative value if current ILOM version is smaller than required or
1530  *    in case of error
1531  *  - positive value if current ILOM version is greater than required
1532  *  - 0 if there is an exact ILOM version match
1533  */
1534 static int
1535 ipmi_sunoem_checkversion(struct ipmi_intf * intf, supported_version_t* supp_ver)
1536 {
1537 	sunoem_version_response_t *version_rsp;
1538 	int i = 1;
1539 
1540 	if (ipmi_sunoem_getversion(intf, &version_rsp)) {
1541 		lprintf(LOG_ERR, "Unable to get ILOM version");
1542 		return (-1);
1543 	}
1544 
1545 	if (version_rsp->major < supp_ver->major) return (-i);
1546 	if (version_rsp->major > supp_ver->major) return (i);
1547 	/*version_rsp->major == supp_ver->major*/
1548 	++i;
1549 
1550 	if (version_rsp->minor < supp_ver->minor) return (-i);
1551 	if (version_rsp->minor > supp_ver->minor) return (i);
1552 	/*version_rsp->minor == supp_ver->minor*/
1553 	++i;
1554 
1555 	if (version_rsp->update < supp_ver->update) return (-i);
1556 	if (version_rsp->update > supp_ver->update) return (i);
1557 	/*version_rsp->update == supp_ver->update*/
1558 	++i;
1559 
1560 	if (version_rsp->micro < supp_ver->micro) return (-i);
1561 	if (version_rsp->micro > supp_ver->micro) return (i);
1562 	/*version_rsp->micro == supp_ver->micro*/
1563 
1564 	return (0);
1565 }
1566 
1567 /*
1568  * Extract the SP version data including
1569  * - major #
1570  * - minor #
1571  * - update #
1572  * - micro #
1573  * - nano #
1574  * - Revision/Build #
1575  */
1576 static int
1577 ipmi_sunoem_version(struct ipmi_intf * intf)
1578 {
1579 	sunoem_version_response_t *version_rsp;
1580 	int rc = ipmi_sunoem_getversion(intf, &version_rsp);
1581 
1582 	if (!rc) {
1583 		printf("Version: %s\n", version_rsp->version);
1584 	}
1585 
1586 	return (rc);
1587 }
1588 
1589 /*
1590  * IPMI Max string length is 16 bytes
1591  * define in usr/src/common/include/ami/IPMI_SDRRecord.h
1592  */
1593 #define MAX_ID_STR_LEN  16
1594 #define MAX_SUNOEM_NAC_SIZE 64
1595 #define LUAPI_MAX_OBJ_PATH_LEN 256
1596 #define LUAPI_MAX_OBJ_VAL_LEN 1024
1597 
1598 #ifdef HAVE_PRAGMA_PACK
1599 #pragma pack(push, 1)
1600 #endif
1601 typedef struct
1602 {
1603 	unsigned char seq_num;
1604 	char nac_name[MAX_SUNOEM_NAC_SIZE];
1605 }__attribute__((packed)) sunoem_nacname_t;
1606 #ifdef HAVE_PRAGMA_PACK
1607 #pragma pack(pop)
1608 #endif
1609 
1610 /*
1611  * Retrieve the full NAC name of the IPMI target.
1612  *
1613  * The returned nac name may be larger than the payload size.
1614  * In which case, it make take several request/payload to retrieve
1615  * the entire full path name
1616  *
1617  * The initial seq_num is set to 0. If the return seq_num is incremented,
1618  * only the 1st 72 bytes of the nac name is returned and the caller
1619  * needs to get the next set of string data.
1620  * If the returned seq_num is identical to the input seq_num, all data
1621  * has been returned.
1622  */
1623 static int
1624 ipmi_sunoem_nacname(struct ipmi_intf * intf, int argc, char *argv[])
1625 {
1626 	struct ipmi_rs *rsp;
1627 	struct ipmi_rq req;
1628 	sunoem_nacname_t nacname_req;
1629 	sunoem_nacname_t *nacname_rsp;
1630 	char full_nac_name[LUAPI_MAX_OBJ_PATH_LEN];
1631 
1632 	if (argc < 1) {
1633 		return (1);
1634 	}
1635 
1636 	if (strlen(argv[0]) > MAX_ID_STR_LEN) {
1637 		lprintf(LOG_ERR,
1638 				"Sun OEM nacname command failed: Max size on IPMI name");
1639 		return (-1);
1640 	}
1641 
1642 	nacname_req.seq_num = 0;
1643 	strcpy(nacname_req.nac_name, argv[0]);
1644 
1645 	full_nac_name[0] = '\0';
1646 	while (1) {
1647 		memset(&req, 0, sizeof(req));
1648 		req.msg.netfn = IPMI_NETFN_SUNOEM;
1649 		req.msg.cmd = IPMI_SUNOEM_NACNAME;
1650 		req.msg.data = (uint8_t *) &nacname_req;
1651 		req.msg.data_len = sizeof(sunoem_nacname_t);
1652 		rsp = intf->sendrecv(intf, &req);
1653 
1654 		if (rsp == NULL) {
1655 			lprintf(LOG_ERR, "Sun OEM nacname command failed.");
1656 			return (-1);
1657 		}
1658 		if (rsp->ccode != 0) {
1659 			lprintf(LOG_ERR, "Sun OEM nacname command failed: %d", rsp->ccode);
1660 			return (-1);
1661 		}
1662 
1663 		nacname_rsp = (sunoem_nacname_t *) rsp->data;
1664 		strncat(full_nac_name, nacname_rsp->nac_name, MAX_SUNOEM_NAC_SIZE);
1665 
1666 		/*
1667 		 * break out of the loop if there is no more data
1668 		 * In most cases, if not all, the NAC name fits into a
1669 		 * single payload
1670 		 */
1671 		if (nacname_req.seq_num == nacname_rsp->seq_num) {
1672 			break;
1673 		}
1674 
1675 		/* Get the next seq of string bytes */
1676 		nacname_req.seq_num = nacname_rsp->seq_num;
1677 
1678 		/* Check if we exceeded the size of the full nac name */
1679 		if ((nacname_req.seq_num * MAX_SUNOEM_NAC_SIZE) > LUAPI_MAX_OBJ_PATH_LEN) {
1680 			lprintf(LOG_ERR,
1681 					"Sun OEM nacname command failed: invalid path length");
1682 			return (-1);
1683 		}
1684 	}
1685 
1686 	printf("NAC Name: %s\n", full_nac_name);
1687 	return (0);
1688 }
1689 
1690 /* Constants used by ipmi_sunoem_getval */
1691 #define MAX_SUNOEM_VAL_PAYLOAD 79
1692 #define MAX_SUNOEM_VAL_COMPACT_PAYLOAD 56
1693 
1694 /*
1695  * SUNOEM GET/SET LUAPI Commands
1696  *
1697  * SUNOEM_REQ_VAL - Request LUAPI Property Value
1698  * SUNOEM_GET_VAL - Return the value from  SUNOEM_REQ_VAL
1699  * SUNOEM_SET_VAL - Set the LUAPI Property value
1700  * SUNOEM_GET_STATUS - Return the Status from SUNOEM_SET_VAL
1701  */
1702 #define SUNOEM_REQ_VAL 1
1703 #define SUNOEM_GET_VAL 2
1704 #define SUNOEM_SET_VAL 3
1705 #define SUNOEM_GET_STATUS 4
1706 
1707 /* Status Code */
1708 #define SUNOEM_REQ_RECV 1
1709 #define SUNOEM_REQ_FAILED 2
1710 #define SUNOEM_DATA_READY 3
1711 #define SUNOEM_DATA_NOT_READY 4
1712 #define SUNOEM_DATA_NOT_FOUND 5
1713 #define GETVAL_MAX_RETRIES 5
1714 
1715 /* Parameter type Codes */
1716 #define SUNOEM_LUAPI_TARGET 0
1717 #define SUNOEM_LUAPI_VALUE  1
1718 
1719 #ifdef HAVE_PRAGMA_PACK
1720 #pragma pack(push, 1)
1721 #endif
1722 typedef struct
1723 {
1724 	unsigned char cmd_code;
1725 	unsigned char luapi_value[MAX_SUNOEM_VAL_PAYLOAD];
1726 }__attribute__((packed)) sunoem_getval_t;
1727 #ifdef HAVE_PRAGMA_PACK
1728 #pragma pack(pop)
1729 #endif
1730 
1731 /*
1732  * REQUEST PAYLOAD
1733  *
1734  * cmd_code - SUNOEM GET/SET LUAPI Cmds - see above
1735  * param_type: 0: luapi_data contains the luapi property name
1736  *             1: luapi_data contains the luapi value
1737  * luapi_data: Either luapi property name or value
1738  * tid: Transaction ID. If 0. This is the initial request for the
1739  *      param_type. If tid > 0, this luapi_data string is a concatenation
1740  *      of the previous request. Handle cases where the LUAPI target name
1741  *      or value is > MAX_SUNOEM_VAL_COMPACT_PAYLOAD
1742  * eof: If non zero, this is the last payload for the request
1743  */
1744 #ifdef HAVE_PRAGMA_PACK
1745 #pragma pack(push, 1)
1746 #endif
1747 typedef struct
1748 {
1749 	unsigned char cmd_code;
1750 	unsigned char param_type;
1751 	unsigned char tid;
1752 	unsigned char eof;
1753 	char luapi_data[MAX_SUNOEM_VAL_COMPACT_PAYLOAD];
1754 }__attribute__((packed)) sunoem_setval_t;
1755 #ifdef HAVE_PRAGMA_PACK
1756 #pragma pack(pop)
1757 #endif
1758 
1759 /*
1760  * RESPONSE PAYLOAD
1761  *
1762  * status_code - see above for code definitions
1763  * tid - transaction ID - assigned ny the ILOM stack
1764  */
1765 #ifdef HAVE_PRAGMA_PACK
1766 #pragma pack(push, 1)
1767 #endif
1768 typedef struct
1769 {
1770 	unsigned char status_code;
1771 	unsigned char tid;
1772 }__attribute__((packed)) sunoem_setval_resp_t;
1773 #ifdef HAVE_PRAGMA_PACK
1774 #pragma pack(pop)
1775 #endif
1776 
1777 /*
1778  * Return the ILOM target property value
1779  */
1780 static int
1781 ipmi_sunoem_getval(struct ipmi_intf * intf, int argc, char *argv[])
1782 {
1783 	struct ipmi_rs *rsp;
1784 	struct ipmi_rq req;
1785 	sunoem_getval_t getval_req;
1786 	sunoem_getval_t *getval_rsp;
1787 	int i;
1788 
1789 	const char* sp_path = "/SP";
1790 	supported_version_t supp_ver = { 3, 2, 0, 0 };
1791 
1792 	if (argc < 1) {
1793 		return (1);
1794 	}
1795 
1796 	if (strlen(argv[0]) > MAX_SUNOEM_VAL_PAYLOAD) {
1797 		lprintf(LOG_ERR,
1798 				"Sun OEM get value command failed: Max size on IPMI name");
1799 		return (-1);
1800 	}
1801 
1802 	if ((ipmi_sunoem_checkversion(intf, &supp_ver) < 0)
1803 			&& (!strncmp(argv[0], sp_path, strlen(sp_path)))) {
1804 		argv[0][1] = 'X'; /*replace SP by X to gain access to hidden properties*/
1805 		memmove(&argv[0][2], &argv[0][3], strlen(argv[0]) - 2);
1806 	}
1807 
1808 	/*
1809 	 * Setup the initial request to fetch the data.
1810 	 * Upon function return, the next cmd (SUNOEM_GET_VAL)
1811 	 * can be requested.
1812 	 */
1813 	memset(&getval_req, 0, sizeof(getval_req));
1814 	strncpy((char*) getval_req.luapi_value, argv[0], MAX_SUNOEM_VAL_PAYLOAD);
1815 	getval_req.cmd_code = SUNOEM_REQ_VAL;
1816 
1817 	memset(&req, 0, sizeof(req));
1818 	req.msg.netfn = IPMI_NETFN_SUNOEM;
1819 	req.msg.cmd = IPMI_SUNOEM_GETVAL;
1820 	req.msg.data = (uint8_t *) &getval_req;
1821 	req.msg.data_len = sizeof(sunoem_getval_t);
1822 	rsp = intf->sendrecv(intf, &req);
1823 
1824 	if (rsp == NULL) {
1825 		lprintf(LOG_ERR, "Sun OEM getval1 command failed.");
1826 		return (-1);
1827 	}
1828 	if (rsp->ccode != 0) {
1829 		lprintf(LOG_ERR, "Sun OEM getval1 command failed: %d", rsp->ccode);
1830 		return (-1);
1831 	}
1832 
1833 	/*
1834 	 * Fetch the data value - if it is not ready,
1835 	 * retry the request up to GETVAL_MAX_RETRIES
1836 	 */
1837 	for (i = 0; i < GETVAL_MAX_RETRIES; i++) {
1838 		memset(&req, 0, sizeof(req));
1839 		req.msg.netfn = IPMI_NETFN_SUNOEM;
1840 		req.msg.cmd = IPMI_SUNOEM_GETVAL;
1841 		getval_req.cmd_code = SUNOEM_GET_VAL;
1842 		req.msg.data = (uint8_t *) &getval_req;
1843 		req.msg.data_len = sizeof(sunoem_getval_t);
1844 		rsp = intf->sendrecv(intf, &req);
1845 
1846 		if (rsp == NULL) {
1847 			lprintf(LOG_ERR, "Sun OEM getval2 command failed.");
1848 			return (-1);
1849 		}
1850 
1851 		if (rsp->ccode != 0) {
1852 			lprintf(LOG_ERR, "Sun OEM getval2 command failed: %d", rsp->ccode);
1853 			return (-1);
1854 		}
1855 
1856 		getval_rsp = (sunoem_getval_t *) rsp->data;
1857 
1858 		if (getval_rsp->cmd_code == SUNOEM_DATA_READY) {
1859 			printf("Target Value: %s\n", getval_rsp->luapi_value);
1860 			return (0);
1861 		} else if (getval_rsp->cmd_code == SUNOEM_DATA_NOT_FOUND) {
1862 			lprintf(LOG_ERR, "Target: %s not found", getval_req.luapi_value);
1863 			return (-1);
1864 		}
1865 
1866 		sleep(1);
1867 	}
1868 
1869 	lprintf(LOG_ERR, "Unable to retrieve target value.");
1870 	return (-1);
1871 }
1872 
1873 static int
1874 send_luapi_prop_name(struct ipmi_intf * intf, int len, char *prop_name,
1875 		unsigned char *tid_num)
1876 {
1877 	int i = 0;
1878 	struct ipmi_rs *rsp;
1879 	struct ipmi_rq req;
1880 	sunoem_setval_t setval_req;
1881 	sunoem_setval_resp_t *setval_rsp;
1882 
1883 	*tid_num = 0;
1884 	while (i < len) {
1885 		/*
1886 		 * Setup the request,
1887 		 * Upon function return, the next cmd (SUNOEM_SET_VAL)
1888 		 * can be requested.
1889 		 */
1890 		memset(&req, 0, sizeof(req));
1891 		memset(&setval_req, 0, sizeof(sunoem_setval_t));
1892 		req.msg.netfn = IPMI_NETFN_SUNOEM;
1893 		req.msg.cmd = IPMI_SUNOEM_SETVAL;
1894 		setval_req.cmd_code = SUNOEM_SET_VAL;
1895 		setval_req.param_type = SUNOEM_LUAPI_TARGET;
1896 		setval_req.tid = *tid_num;
1897 		setval_req.eof = 0;
1898 		/*
1899 		 * If the property name is > payload, only copy
1900 		 * the payload size and increment the string offset (i)
1901 		 * for the next payload
1902 		 */
1903 		if (strlen(&(prop_name[i])) > MAX_SUNOEM_VAL_COMPACT_PAYLOAD) {
1904 			strncpy(setval_req.luapi_data, &(prop_name[i]),
1905 			MAX_SUNOEM_VAL_COMPACT_PAYLOAD);
1906 		} else {
1907 			strncpy(setval_req.luapi_data, &(prop_name[i]),
1908 					strlen(&(prop_name[i])));
1909 		}
1910 		req.msg.data = (uint8_t *) &setval_req;
1911 		req.msg.data_len = sizeof(sunoem_setval_t);
1912 		rsp = intf->sendrecv(intf, &req);
1913 
1914 		if (rsp == NULL) {
1915 			lprintf(LOG_ERR, "Sun OEM setval prop name: response is NULL");
1916 			return (-1);
1917 		}
1918 
1919 		if (rsp->ccode != 0) {
1920 			lprintf(LOG_ERR, "Sun OEM setval prop name: request failed: %d",
1921 					rsp->ccode);
1922 			return (-1);
1923 		}
1924 
1925 		setval_rsp = (sunoem_setval_resp_t *) rsp->data;
1926 
1927 		/*
1928 		 * If the return code is other than data received, the
1929 		 * request failed
1930 		 */
1931 		if (setval_rsp->status_code != SUNOEM_REQ_RECV) {
1932 			lprintf(LOG_ERR,
1933 					"Sun OEM setval prop name: invalid status code: %d",
1934 					setval_rsp->status_code);
1935 			return (-1);
1936 		}
1937 		/* Use the tid returned by ILOM */
1938 		*tid_num = setval_rsp->tid;
1939 		/* Increment the string offset */
1940 		i += MAX_SUNOEM_VAL_COMPACT_PAYLOAD;
1941 	}
1942 
1943 	return (0);
1944 }
1945 
1946 static int
1947 send_luapi_prop_value(struct ipmi_intf * intf, int len,	char *prop_value,
1948 		unsigned char tid_num)
1949 {
1950 	int i = 0;
1951 	struct ipmi_rs *rsp;
1952 	struct ipmi_rq req;
1953 	sunoem_setval_t setval_req;
1954 	sunoem_setval_resp_t *setval_rsp;
1955 
1956 	while (i < len) {
1957 		/*
1958 		 * Setup the request,
1959 		 * Upon function return, the next cmd (SUNOEM_GET_VAL)
1960 		 * can be requested.
1961 		 */
1962 		memset(&req, 0, sizeof(req));
1963 		memset(&setval_req, 0, sizeof(sunoem_setval_t));
1964 		req.msg.netfn = IPMI_NETFN_SUNOEM;
1965 		req.msg.cmd = IPMI_SUNOEM_SETVAL;
1966 		setval_req.cmd_code = SUNOEM_SET_VAL;
1967 		setval_req.param_type = SUNOEM_LUAPI_VALUE;
1968 		setval_req.tid = tid_num;
1969 		/*
1970 		 * If the property name is > payload, only copy the
1971 		 * the payload size and increment the string offset
1972 		 * for the next payload
1973 		 */
1974 		if (strlen(&(prop_value[i])) > MAX_SUNOEM_VAL_COMPACT_PAYLOAD) {
1975 			strncpy(setval_req.luapi_data, &(prop_value[i]),
1976 			MAX_SUNOEM_VAL_COMPACT_PAYLOAD);
1977 		} else {
1978 			/* Captured the entire string, mark this as the last payload */
1979 			strncpy(setval_req.luapi_data, &(prop_value[i]),
1980 					strlen(&(prop_value[i])));
1981 			setval_req.eof = 1;
1982 		}
1983 		req.msg.data = (uint8_t *) &setval_req;
1984 		req.msg.data_len = sizeof(sunoem_setval_t);
1985 		rsp = intf->sendrecv(intf, &req);
1986 
1987 		if (rsp == NULL) {
1988 			lprintf(LOG_ERR, "Sun OEM setval prop value: response is NULL");
1989 			return (-1);
1990 		}
1991 
1992 		if (rsp->ccode != 0) {
1993 			lprintf(LOG_ERR, "Sun OEM setval prop value: request failed: %d",
1994 					rsp->ccode);
1995 			return (-1);
1996 		}
1997 
1998 		setval_rsp = (sunoem_setval_resp_t *) rsp->data;
1999 
2000 		/*
2001 		 * If the return code is other than data received, the
2002 		 * request failed
2003 		 */
2004 		if (setval_rsp->status_code != SUNOEM_REQ_RECV) {
2005 			lprintf(LOG_ERR,
2006 					"Sun OEM setval prop value: invalid status code: %d",
2007 					setval_rsp->status_code);
2008 			return (-1);
2009 		}
2010 
2011 		/* Increment the string offset */
2012 		i += MAX_SUNOEM_VAL_COMPACT_PAYLOAD;
2013 	}
2014 	return (0);
2015 }
2016 
2017 static int
2018 ipmi_sunoem_setval(struct ipmi_intf * intf, int argc, char *argv[])
2019 {
2020 	struct ipmi_rs *rsp;
2021 	struct ipmi_rq req;
2022 	sunoem_setval_t setval_req;
2023 	sunoem_setval_resp_t *setval_rsp;
2024 	int prop_len;
2025 	int value_len;
2026 	int i;
2027 	unsigned char tid_num;
2028 	int retries;
2029 
2030 	prop_len = strlen(argv[0]);
2031 	value_len = strlen(argv[1]);
2032 	if (prop_len > LUAPI_MAX_OBJ_PATH_LEN) {
2033 		lprintf(LOG_ERR,
2034 				"Sun OEM set value command failed: Max size on property name");
2035 		return (-1);
2036 	}
2037 	if (value_len > LUAPI_MAX_OBJ_VAL_LEN) {
2038 		lprintf(LOG_ERR,
2039 				"Sun OEM set value command failed: Max size on property value");
2040 		return (-1);
2041 	}
2042 
2043 	/* Test if there is a timeout specified */
2044 	if (argc == 3) {
2045 		if ((str2int(argv[2], &retries) != 0) || retries < 0) {
2046 			lprintf(LOG_ERR,
2047 					"Invalid input given or out of range for time-out parameter.");
2048 			return (-1);
2049 		}
2050 	} else {
2051 		retries = GETVAL_MAX_RETRIES;
2052 	}
2053 
2054 	/* Send the property name 1st */
2055 	if (send_luapi_prop_name(intf, prop_len, argv[0], &tid_num) != 0) {
2056 		/* return if there is an error */
2057 		return (-1);
2058 	}
2059 
2060 	if (send_luapi_prop_value(intf, value_len, argv[1], tid_num) != 0) {
2061 		/* return if there is an error */
2062 		return (-1);
2063 	}
2064 
2065 	/*
2066 	 * Get The status of the command.
2067 	 * if it is not ready, retry the request up to
2068 	 * GETVAL_MAX_RETRIES
2069 	 */
2070 	for (i = 0; i < retries; i++) {
2071 		memset(&req, 0, sizeof(req));
2072 		req.msg.netfn = IPMI_NETFN_SUNOEM;
2073 		req.msg.cmd = IPMI_SUNOEM_SETVAL;
2074 		setval_req.cmd_code = SUNOEM_GET_STATUS;
2075 		setval_req.tid = tid_num;
2076 		req.msg.data = (uint8_t *) &setval_req;
2077 		req.msg.data_len = sizeof(sunoem_setval_t);
2078 		rsp = intf->sendrecv(intf, &req);
2079 
2080 		if (rsp == NULL) {
2081 			lprintf(LOG_ERR, "Sun OEM setval command failed.");
2082 			return (-1);
2083 		}
2084 
2085 		if (rsp->ccode != 0) {
2086 			lprintf(LOG_ERR, "Sun OEM setval command failed: %d", rsp->ccode);
2087 			return (-1);
2088 		}
2089 
2090 		setval_rsp = (sunoem_setval_resp_t *) rsp->data;
2091 
2092 		if (setval_rsp->status_code == SUNOEM_DATA_READY) {
2093 			printf("Sun OEM setval command successful.\n");
2094 			return (0);
2095 		} else if (setval_rsp->status_code != SUNOEM_DATA_NOT_READY) {
2096 			lprintf(LOG_ERR, "Sun OEM setval command failed.");
2097 			return (-1);
2098 		}
2099 
2100 		sleep(1);
2101 	}
2102 	/* If we reached here, retries exceeded */
2103 	lprintf(LOG_ERR, "Sun OEM setval command failed: Command Timed Out");
2104 
2105 	return (-1);
2106 }
2107 
2108 #define MAX_FILE_DATA_SIZE            1024
2109 #define MAX_FILEID_LEN                16
2110 #define CORE_TUNNEL_SUBCMD_GET_FILE   11
2111 
2112 #ifdef HAVE_PRAGMA_PACK
2113 #pragma pack(push, 1)
2114 #endif
2115 typedef struct
2116 {
2117 	unsigned char cmd_code;
2118 	unsigned char file_id[MAX_FILEID_LEN];
2119 	unsigned int block_num;
2120 }__attribute__((packed)) getfile_req_t;
2121 
2122 typedef struct
2123 {
2124 	unsigned int block_num;
2125 	unsigned int data_size;
2126 	unsigned char eof;
2127 	unsigned char data[MAX_FILE_DATA_SIZE];
2128 }__attribute__((packed)) getfile_rsp_t;
2129 #ifdef HAVE_PRAGMA_PACK
2130 #pragma pack(pop)
2131 #endif
2132 
2133 static int
2134 ipmi_sunoem_getfile(struct ipmi_intf * intf, int argc, char *argv[])
2135 {
2136 	struct ipmi_rs *rsp;
2137 	struct ipmi_rq req;
2138 	getfile_req_t getfile_req;
2139 	getfile_rsp_t *getfile_rsp;
2140 	int block_num = 0;
2141 	int nbo_blk_num; /* Network Byte Order Block Num */
2142 	FILE *fp;
2143 	unsigned data_size;
2144 	supported_version_t supp_ver = IPMI_SUNOEM_GETFILE_VERSION;
2145 
2146 	if (argc < 1) {
2147 		return (-1);
2148 	}
2149 
2150 	/*check if command is supported by this version of ilom*/
2151 	if (ipmi_sunoem_checkversion(intf, &supp_ver) < 0) {
2152 		ipmi_sunoem_print_required_version(&supp_ver);
2153 		return (-1);
2154 	}
2155 
2156 	/*
2157 	 * File ID is < MAX_FILEID_LEN
2158 	 * Save 1 byte for null Terminated string
2159 	 */
2160 	if (strlen(argv[0]) >= MAX_FILE_DATA_SIZE) {
2161 		lprintf(LOG_ERR, "File ID >= %d characters", MAX_FILEID_LEN);
2162 		return (-1);
2163 	}
2164 
2165 	memset(&getfile_req, 0, sizeof(getfile_req));
2166 	strncpy((char*) getfile_req.file_id, argv[0], MAX_FILEID_LEN - 1);
2167 
2168 	/* Create the destination file */
2169 	fp = ipmi_open_file_write(argv[1]);
2170 	if (fp == NULL) {
2171 		lprintf(LOG_ERR, "Unable to open file: %s", argv[1]);
2172 		return (-1);
2173 	}
2174 
2175 	memset(&req, 0, sizeof(req));
2176 	req.msg.netfn = IPMI_NETFN_SUNOEM;
2177 	req.msg.cmd = IPMI_SUNOEM_CORE_TUNNEL;
2178 	req.msg.data = (uint8_t *) &getfile_req;
2179 	req.msg.data_len = sizeof(getfile_req_t);
2180 	getfile_req.cmd_code = CORE_TUNNEL_SUBCMD_GET_FILE;
2181 
2182 	do {
2183 
2184 		nbo_blk_num = htonl(block_num);
2185 		/* Block Num must be in network byte order */
2186 		memcpy(&(getfile_req.block_num), &nbo_blk_num,
2187 				sizeof(getfile_req.block_num));
2188 
2189 		rsp = intf->sendrecv(intf, &req);
2190 
2191 		if (rsp == NULL) {
2192 			lprintf(LOG_ERR, "Sun OEM getfile command failed.");
2193 			fclose(fp);
2194 			return (-1);
2195 		}
2196 		if (rsp->ccode != 0) {
2197 			lprintf(LOG_ERR, "Sun OEM getfile command failed: %d", rsp->ccode);
2198 			fclose(fp);
2199 			return (-1);
2200 		}
2201 
2202 		getfile_rsp = (getfile_rsp_t *) rsp->data;
2203 
2204 		memcpy(&data_size, &(getfile_rsp->data_size),
2205 				sizeof(getfile_rsp->data_size));
2206 		data_size = ntohl(data_size);
2207 
2208 		if (data_size > MAX_FILE_DATA_SIZE) {
2209 			lprintf(LOG_ERR, "Sun OEM getfile invalid data size: %d",
2210 					data_size);
2211 			fclose(fp);
2212 			return (-1);
2213 		}
2214 
2215 		/* Check if Block Num matches */
2216 		if (memcmp(&(getfile_req.block_num), &(getfile_rsp->block_num),
2217 				sizeof(getfile_req.block_num)) != 0) {
2218 			lprintf(LOG_ERR, "Sun OEM getfile Incorrect Block Num Returned");
2219 			lprintf(LOG_ERR, "Expecting: %x Received: %x",
2220 					getfile_req.block_num, getfile_rsp->block_num);
2221 			fclose(fp);
2222 			return (-1);
2223 		}
2224 
2225 		if (fwrite(getfile_rsp->data, 1, data_size, fp) != data_size) {
2226 			lprintf(LOG_ERR, "Sun OEM getfile write failed: %d", rsp->ccode);
2227 			fclose(fp);
2228 			return (-1);
2229 		}
2230 
2231 		block_num++;
2232 	} while (getfile_rsp->eof == 0);
2233 
2234 	fclose(fp);
2235 
2236 	return (0);
2237 }
2238 
2239 /*
2240  * Query BMC for capability/behavior.
2241  */
2242 
2243 #define CORE_TUNNEL_SUBCMD_GET_BEHAVIOR   15
2244 #define SUNOEM_BEHAVIORID_SIZE            32
2245 
2246 #ifdef HAVE_PRAGMA_PACK
2247 #pragma pack(push, 1)
2248 #endif
2249 typedef struct
2250 {
2251 	unsigned char cmd_code;
2252 	unsigned char behavior_id[SUNOEM_BEHAVIORID_SIZE];
2253 }__attribute__((packed)) getbehavior_req_t;
2254 
2255 typedef struct
2256 {
2257 	unsigned char enabled;
2258 }__attribute__((packed)) getbehavior_rsp_t;
2259 #ifdef HAVE_PRAGMA_PACK
2260 #pragma pack(pop)
2261 #endif
2262 
2263 static int
2264 ipmi_sunoem_getbehavior(struct ipmi_intf * intf, int argc, char *argv[])
2265 {
2266 	struct ipmi_rq req;
2267 	struct ipmi_rs *rsp;
2268 	getbehavior_req_t getbehavior_req;
2269 	getbehavior_rsp_t *getbehavior_rsp;
2270 	supported_version_t supp_ver = IPMI_SUNOEM_GETBEHAVIOR_VERSION;
2271 
2272 	if (argc < 1) {
2273 		return (-1);
2274 	}
2275 
2276 	/*check if command is supported by this version of ilom*/
2277 	if (ipmi_sunoem_checkversion(intf, &supp_ver) < 0) {
2278 		ipmi_sunoem_print_required_version(&supp_ver);
2279 		return (-1);
2280 	}
2281 
2282 	/*
2283 	 * Behavior ID is < SUNOEM_BEHAVIORID_SIZE.
2284 	 * Save 1 byte for null terminated string
2285 	 */
2286 	if (strlen(argv[0]) >= SUNOEM_BEHAVIORID_SIZE) {
2287 		lprintf(LOG_ERR, "Behavior ID >= %d characters",
2288 		SUNOEM_BEHAVIORID_SIZE);
2289 		return (-1);
2290 	}
2291 
2292 	memset(&getbehavior_req, 0, sizeof(getbehavior_req));
2293 	strncpy(getbehavior_req.behavior_id, argv[0], SUNOEM_BEHAVIORID_SIZE - 1);
2294 
2295 	memset(&req, 0, sizeof(req));
2296 	req.msg.netfn = IPMI_NETFN_SUNOEM;
2297 	req.msg.cmd = IPMI_SUNOEM_CORE_TUNNEL;
2298 	req.msg.data = (uint8_t *) &getbehavior_req;
2299 	req.msg.data_len = sizeof(getbehavior_req_t);
2300 	getbehavior_req.cmd_code = CORE_TUNNEL_SUBCMD_GET_BEHAVIOR;
2301 
2302 	rsp = intf->sendrecv(intf, &req);
2303 
2304 	if (rsp == NULL) {
2305 		lprintf(LOG_ERR, "Sun OEM getbehavior command failed.");
2306 		return (-1);
2307 	}
2308 
2309 	if (rsp->ccode != 0) {
2310 		lprintf(LOG_ERR, "Sun OEM getbehavior command failed: %d", rsp->ccode);
2311 		return (-1);
2312 	}
2313 
2314 	getbehavior_rsp = (getbehavior_rsp_t *) rsp->data;
2315 	printf("ILOM behavior %s %s enabled\n", getbehavior_req.behavior_id,
2316 			getbehavior_rsp->enabled ? "is" : "is not");
2317 
2318 	return (0);
2319 }
2320 
2321 int
2322 ipmi_sunoem_main(struct ipmi_intf * intf, int argc, char ** argv)
2323 {
2324 	int rc = 0;
2325 
2326 	if (argc == 0 || strcmp(argv[0], "help") == 0) {
2327 		ipmi_sunoem_usage();
2328 		return (0);
2329 	} /* if (argc == 0 || strcmp(argv[0], "help") == 0) */
2330 
2331 	if (strcmp(argv[0], "cli") == 0) {
2332 		rc = ipmi_sunoem_cli(intf, argc - 1, &argv[1]);
2333 	} else if ((strcmp(argv[0], "led") == 0) || (strcmp(argv[0], "sbled") == 0)) {
2334 		if (argc < 2) {
2335 			ipmi_sunoem_usage();
2336 			return (-1);
2337 		}
2338 
2339 		if (strcmp(argv[1], "get") == 0) {
2340 			if (argc < 3) {
2341 				char * arg[] = { "all" };
2342 				rc = ipmi_sunoem_led_get(intf, 1, arg);
2343 			} else {
2344 				rc = ipmi_sunoem_led_get(intf, argc - 2, &(argv[2]));
2345 			}
2346 		} else if (strcmp(argv[1], "set") == 0) {
2347 			if (argc < 4) {
2348 				ipmi_sunoem_usage();
2349 				return (-1);
2350 			}
2351 			rc = ipmi_sunoem_led_set(intf, argc - 2, &(argv[2]));
2352 		} else {
2353 			ipmi_sunoem_usage();
2354 			return (-1);
2355 		}
2356 	} else if (strcmp(argv[0], "sshkey") == 0) {
2357 		uint8_t uid = 0;
2358 		if (argc < 3) {
2359 			ipmi_sunoem_usage();
2360 			return (-1);
2361 		}
2362 		rc = str2uchar(argv[2], &uid);
2363 		if (rc == 0) {
2364 			/* conversion should be OK. */
2365 		} else if (rc == 2) {
2366 			lprintf(LOG_NOTICE, "Invalid interval given.");
2367 			return (-1);
2368 		} else {
2369 			/* defaults to rc = 3 */
2370 			lprintf(LOG_NOTICE, "Given interval is too big.");
2371 			return (-1);
2372 		}
2373 
2374 		if (strcmp(argv[1], "del") == 0) {
2375 			/* number of arguments, three, is already checked at this point */
2376 			rc = ipmi_sunoem_sshkey_del(intf, uid);
2377 		} else if (strcmp(argv[1], "set") == 0) {
2378 			if (argc < 4) {
2379 				ipmi_sunoem_usage();
2380 				return (-1);
2381 			}
2382 			rc = ipmi_sunoem_sshkey_set(intf, uid, argv[3]);
2383 		} else {
2384 			ipmi_sunoem_usage();
2385 			return (-1);
2386 		}
2387 	} else if (strcmp(argv[0], "ping") == 0) {
2388 		if (argc < 2) {
2389 			ipmi_sunoem_usage();
2390 			return (-1);
2391 		}
2392 		rc = ipmi_sunoem_echo(intf, argc - 1, &(argv[1]));
2393 	} else if (strcmp(argv[0], "version") == 0) {
2394 		rc = ipmi_sunoem_version(intf);
2395 	} else if (strcmp(argv[0], "nacname") == 0) {
2396 		if (argc < 2) {
2397 			ipmi_sunoem_usage();
2398 			return (-1);
2399 		}
2400 		rc = ipmi_sunoem_nacname(intf, argc - 1, &(argv[1]));
2401 	} else if (strcmp(argv[0], "getval") == 0) {
2402 		if (argc < 2) {
2403 			ipmi_sunoem_usage();
2404 			return (-1);
2405 		}
2406 		rc = ipmi_sunoem_getval(intf, argc - 1, &(argv[1]));
2407 	} else if (strcmp(argv[0], "setval") == 0) {
2408 		if (argc < 3) {
2409 			ipmi_sunoem_usage();
2410 			return (-1);
2411 		}
2412 		rc = ipmi_sunoem_setval(intf, argc - 1, &(argv[1]));
2413 	} else if (strcmp(argv[0], "getfile") == 0) {
2414 		if (argc < 3) {
2415 			ipmi_sunoem_usage();
2416 			return (-1);
2417 		}
2418 		rc = ipmi_sunoem_getfile(intf, argc - 1, &(argv[1]));
2419 	} else if (strcmp(argv[0], "getbehavior") == 0) {
2420 		if (argc < 2) {
2421 			ipmi_sunoem_usage();
2422 			return (-1);
2423 		}
2424 		rc = ipmi_sunoem_getbehavior(intf, argc - 1, &(argv[1]));
2425 	} else {
2426 		lprintf(LOG_ERR, "Invalid sunoem command: %s", argv[0]);
2427 		return (-1);
2428 	} /* if (strcmp(argv[0], "cli") == 0) */
2429 
2430 	return (rc);
2431 }
2432