diff options
Diffstat (limited to 'lib/ipmi_sunoem.c')
| -rw-r--r-- | lib/ipmi_sunoem.c | 2434 | 
1 files changed, 2434 insertions, 0 deletions
| diff --git a/lib/ipmi_sunoem.c b/lib/ipmi_sunoem.c new file mode 100644 index 0000000..16a6090 --- /dev/null +++ b/lib/ipmi_sunoem.c @@ -0,0 +1,2434 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <time.h> +#include <unistd.h> +#include <signal.h> +#include <ctype.h> +#include <sys/time.h> +#include <limits.h> +#include <fcntl.h> + +#include <termios.h> + +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi_sel.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/ipmi_channel.h> +#include <ipmitool/ipmi_sunoem.h> +#include <ipmitool/ipmi_raw.h> + +static const struct valstr sunoem_led_type_vals[] = { +	{ 0, "OK2RM" }, +	{ 1, "SERVICE" }, +	{ 2, "ACT" }, +	{ 3, "LOCATE" }, +	{ 0xFF, NULL }, +}; + +static const struct valstr sunoem_led_mode_vals[] = { +	{ 0, "OFF" }, +	{ 1, "ON" }, +	{ 2, "STANDBY" }, +	{ 3, "SLOW" }, +	{ 4, "FAST" }, +	{ 0xFF, NULL }, +}; + +static const struct valstr sunoem_led_mode_optvals[] = { +	{ 0, "STEADY_OFF" }, +	{ 1, "STEADY_ON" }, +	{ 2, "STANDBY_BLINK" }, +	{ 3, "SLOW_BLINK" }, +	{ 4, "FAST_BLINK" }, +	{ 0xFF, NULL }, +}; + +#define SUNOEM_SUCCESS 1 + +#define IPMI_SUNOEM_GETFILE_VERSION {3,2,0,0} +#define IPMI_SUNOEM_GETBEHAVIOR_VERSION {3,2,0,0} + +/* + * PRINT_NORMAL: print out the LED value as normal + * PRINT_ERROR: print out "na" for the LED value + */ +typedef enum +{ +	PRINT_NORMAL = 0, PRINT_ERROR +} print_status_t; + +int ret_get = 0; +int ret_set = 0; + +static void +ipmi_sunoem_usage(void) +{ +	lprintf(LOG_NOTICE, "Usage: sunoem <command> [option...]"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, "Commands:"); +	lprintf(LOG_NOTICE, " - cli [<command string> ...]"); +	lprintf(LOG_NOTICE, "      Execute SP CLI commands."); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - led get [<sensor_id>] [ledtype]"); +	lprintf(LOG_NOTICE, "      - Read status of LED found in Generic Device Locator."); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - led set <sensor_id> <led_mode> [led_type]"); +	lprintf(LOG_NOTICE, "      - Set mode of LED found in Generic Device Locator."); +	lprintf(LOG_NOTICE, "      - You can pass 'all' as the <senso_rid> to change the LED mode of all sensors."); +	lprintf(LOG_NOTICE, "      - Use 'sdr list generic' command to get list of Generic"); +	lprintf(LOG_NOTICE, "      - Devices that are controllable LEDs."); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, "      - Required SIS LED Mode:"); +	lprintf(LOG_NOTICE, "          OFF          Off"); +	lprintf(LOG_NOTICE, "          ON           Steady On"); +	lprintf(LOG_NOTICE, "          STANDBY      100ms on 2900ms off blink rate"); +	lprintf(LOG_NOTICE, "          SLOW         1HZ blink rate"); +	lprintf(LOG_NOTICE, "          FAST         4HZ blink rate"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, "      - Optional SIS LED Type:"); +	lprintf(LOG_NOTICE, "          OK2RM        OK to Remove"); +	lprintf(LOG_NOTICE, "          SERVICE      Service Required"); +	lprintf(LOG_NOTICE, "          ACT          Activity"); +	lprintf(LOG_NOTICE, "          LOCATE       Locate"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - nacname <ipmi_nac_name>"); +	lprintf(LOG_NOTICE, "      - Returns the full nac name"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - ping NUMBER <q>"); +	lprintf(LOG_NOTICE, "      - Send and Receive NUMBER (64 Byte) packets."); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, "      - q - Quiet. Displays output at start and end"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - getval <target_name>"); +	lprintf(LOG_NOTICE, "      - Returns the ILOM property value"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - setval <property name> <property value> <timeout>"); +	lprintf(LOG_NOTICE, "      - Sets the ILOM property value"); +	lprintf(LOG_NOTICE, "      - If timeout is not specified, the default is 5 sec."); +	lprintf(LOG_NOTICE, "      - NOTE: must be executed locally on host, not remotely over LAN!"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - sshkey del <user_id>"); +	lprintf(LOG_NOTICE, "      - Delete ssh key for user id from authorized_keys,"); +	lprintf(LOG_NOTICE, "      - view users with 'user list' command."); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - sshkey set <user_id> <id_rsa.pub>"); +	lprintf(LOG_NOTICE, "      - Set ssh key for a userid into authorized_keys,"); +	lprintf(LOG_NOTICE, "      - view users with 'user list' command."); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - version"); +	lprintf(LOG_NOTICE, "      - Display the software version"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - nacname <ipmi_nac_name>"); +	lprintf(LOG_NOTICE, "      - Returns the full nac name"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - getfile <file_string_id> <destination_file_name>"); +	lprintf(LOG_NOTICE, "      - Copy file <file_string_id> to <destination_file_name>"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, "      - File string ids:"); +	lprintf(LOG_NOTICE, "          SSH_PUBKEYS"); +	lprintf(LOG_NOTICE, "          DIAG_PASSED"); +	lprintf(LOG_NOTICE, "          DIAG_FAILED"); +	lprintf(LOG_NOTICE, "          DIAG_END_TIME"); +	lprintf(LOG_NOTICE, "          DIAG_INVENTORY"); +	lprintf(LOG_NOTICE, "          DIAG_TEST_LOG"); +	lprintf(LOG_NOTICE, "          DIAG_START_TIME"); +	lprintf(LOG_NOTICE, "          DIAG_UEFI_LOG"); +	lprintf(LOG_NOTICE, "          DIAG_TEST_LOG"); +	lprintf(LOG_NOTICE, "          DIAG_LAST_LOG"); +	lprintf(LOG_NOTICE, "          DIAG_LAST_CMD"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, " - getbehavior <behavior_string_id>"); +	lprintf(LOG_NOTICE, "      - Test if ILOM behavior is enabled"); +	lprintf(LOG_NOTICE, ""); +	lprintf(LOG_NOTICE, "      - Behavior string ids:"); +	lprintf(LOG_NOTICE, "          SUPPORTS_SIGNED_PACKAGES"); +	lprintf(LOG_NOTICE, "          REQUIRES_SIGNED_PACKAGES"); +	lprintf(LOG_NOTICE, ""); +} + +#define SUNOEM_FAN_MODE_AUTO    0x00 +#define SUNOEM_FAN_MODE_MANUAL  0x01 + +static void +__sdr_list_empty(struct sdr_record_list * head) +{ +	struct sdr_record_list * e, *f; +	for (e = head; e != NULL; e = f) { +		f = e->next; +		free(e); +	} +	head = NULL; +} + +/* + *  led_print + *  Print out the led name and the state, if stat is PRINT_NORMAL. + *  Otherwise, print out the led name and "na". + *  The state parameter is not referenced if stat is not PRINT_NORMAL. + */ +static void +led_print(const char * name, print_status_t stat, uint8_t state) +{ +	const char *theValue; + +	if (stat == PRINT_NORMAL) { +		theValue = val2str(state, sunoem_led_mode_vals); +	} else { +		theValue = "na"; +	} + +	if (csv_output) { +		printf("%s,%s\n", name, theValue); +	} else { +		printf("%-16s | %s\n", name, theValue); +	} +} + +#define CC_NORMAL                  0x00 +#define CC_PARAM_OUT_OF_RANGE      0xc9 +#define CC_DEST_UNAVAILABLE        0xd3 +#define CC_UNSPECIFIED_ERR         0xff +#define CC_INSUFFICIENT_PRIVILEGE  0xd4 +#define CC_INV_CMD                 0xc1 +#define CC_INV_DATA_FIELD          0xcc + +/* + * sunoem_led_get(....) + * + * OUTPUT: + *   SUNOEM_EC_INVALID_ARG         if dev is NULL, + *   SUNOEM_EC_BMC_NOT_RESPONDING  if no reply is obtained from BMC, + *   SUNOEM_EC_BMC_CCODE_NONZERO   if completion code is nonzero, + *   SUNOEM_EC_SUCCESS             otherwise. + */ +static sunoem_ec_t +sunoem_led_get(struct ipmi_intf * intf,	struct sdr_record_generic_locator * dev, +		int ledtype, struct ipmi_rs **loc_rsp) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	uint8_t rqdata[7]; +	int rqdata_len; + +	if (dev == NULL) { +		*loc_rsp = NULL; +		return (SUNOEM_EC_INVALID_ARG); +	} + +	rqdata[0] = dev->dev_slave_addr; +	if (ledtype == 0xFF) +		rqdata[1] = dev->oem; +	else +		rqdata[1] = ledtype; + +	rqdata[2] = dev->dev_access_addr; +	rqdata[3] = dev->oem; +	rqdata[4] = dev->entity.id; +	rqdata[5] = dev->entity.instance; +	rqdata[6] = 0; +	rqdata_len = 7; + +	req.msg.netfn = IPMI_NETFN_SUNOEM; +	req.msg.cmd = IPMI_SUNOEM_LED_GET; +	req.msg.lun = dev->lun; +	req.msg.data = rqdata; +	req.msg.data_len = rqdata_len; + +	rsp = intf->sendrecv(intf, &req); +	/* +	 * Just return NULL if there was +	 * an error. +	 */ +	if (rsp == NULL) { +		*loc_rsp = NULL; +		return (SUNOEM_EC_BMC_NOT_RESPONDING); +	} else if (rsp->ccode > 0) { +		*loc_rsp = rsp; +		return (SUNOEM_EC_BMC_CCODE_NONZERO); +	} else { +		*loc_rsp = rsp; +		return (SUNOEM_EC_SUCCESS); +	} +} + +static struct ipmi_rs * +sunoem_led_set(struct ipmi_intf * intf, struct sdr_record_generic_locator * dev, +		int ledtype, int ledmode) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	uint8_t rqdata[9]; +	int rqdata_len; + +	if (dev == NULL) +		return NULL; + +	rqdata[0] = dev->dev_slave_addr; + +	if (ledtype == 0xFF) +		rqdata[1] = dev->oem; +	else +		rqdata[1] = ledtype; + +	rqdata[2] = dev->dev_access_addr; +	rqdata[3] = dev->oem; +	rqdata[4] = ledmode; +	rqdata[5] = dev->entity.id; +	rqdata[6] = dev->entity.instance; +	rqdata[7] = 0; +	rqdata[8] = 0; +	rqdata_len = 9; + +	req.msg.netfn = IPMI_NETFN_SUNOEM; +	req.msg.cmd = IPMI_SUNOEM_LED_SET; +	req.msg.lun = dev->lun; +	req.msg.data = rqdata; +	req.msg.data_len = rqdata_len; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Sun OEM Set LED command failed."); +		return NULL; +	} else if (rsp->ccode > 0) { +		lprintf(LOG_ERR, "Sun OEM Set LED command failed: %s", +				val2str(rsp->ccode, completion_code_vals)); +		return NULL; +	} + +	return (rsp); +} + +static void +sunoem_led_get_byentity(struct ipmi_intf * intf, uint8_t entity_id, +		uint8_t entity_inst, int ledtype) +{ +	struct ipmi_rs * rsp; +	struct sdr_record_list *elist, *e; +	struct entity_id entity; +	sunoem_ec_t res; + +	if (entity_id == 0) +		return; + +	/* lookup sdrs with this entity */ +	memset(&entity, 0, sizeof(struct entity_id)); +	entity.id = entity_id; +	entity.instance = entity_inst; + +	elist = ipmi_sdr_find_sdr_byentity(intf, &entity); + +	if (elist == NULL) +		ret_get = -1; + +	/* for each generic sensor get its led state */ +	for (e = elist; e != NULL; e = e->next) { +		if (e->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) +			continue; + +		res = sunoem_led_get(intf, e->record.genloc, ledtype, &rsp); + +		if (res == SUNOEM_EC_SUCCESS && rsp && rsp->data_len == 1) { +			led_print((const char *) e->record.genloc->id_string, PRINT_NORMAL, +					rsp->data[0]); +		} else { +			led_print((const char *) e->record.genloc->id_string, PRINT_ERROR, +					0); +			if (res != SUNOEM_EC_BMC_CCODE_NONZERO|| !rsp +			|| rsp->ccode != CC_DEST_UNAVAILABLE) { +				ret_get = -1; +			} +		} +	} +	__sdr_list_empty(elist); +} + +static void +sunoem_led_set_byentity(struct ipmi_intf * intf, uint8_t entity_id, +		uint8_t entity_inst, int ledtype, int ledmode) +{ +	struct ipmi_rs * rsp; +	struct sdr_record_list *elist, *e; +	struct entity_id entity; + +	if (entity_id == 0) +		return; + +	/* lookup sdrs with this entity */ +	memset(&entity, 0, sizeof(struct entity_id)); +	entity.id = entity_id; +	entity.instance = entity_inst; + +	elist = ipmi_sdr_find_sdr_byentity(intf, &entity); + +	if (elist == NULL) +		ret_set = -1; + +	/* for each generic sensor set its led state */ +	for (e = elist; e != NULL; e = e->next) { + +		if (e->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) +			continue; + +		rsp = sunoem_led_set(intf, e->record.genloc, ledtype, ledmode); +		if (rsp && rsp->data_len == 0) { +			led_print((const char *) e->record.genloc->id_string, PRINT_NORMAL, +					ledmode); +		} else if (rsp == NULL) { +			ret_set = -1; +		} +	} + +	__sdr_list_empty(elist); +} + +/* + * IPMI Request Data: 5 bytes + * + * [byte 0]  devAddr     Value from the "Device Slave Address" field in + *                       LED's Generic Device Locator record in the SDR + * [byte 1]  led         LED Type: OK2RM, ACT, LOCATE, SERVICE + * [byte 2]  ctrlrAddr   Controller address; value from the "Device + *                       Access Address" field, 0x20 if the LED is local + * [byte 3]  hwInfo      The OEM field from the SDR record + * [byte 4]  force       1 = directly access the device + *                       0 = go thru its controller + *                       Ignored if LED is local + * + * The format below is for Sun Blade Modular systems only + * [byte 4]  entityID    The entityID field from the SDR record + * [byte 5]  entityIns   The entityIns field from the SDR record + * [byte 6]  force       1 = directly access the device + *                       0 = go thru its controller + *                       Ignored if LED is local + */ +static int +ipmi_sunoem_led_get(struct ipmi_intf * intf, int argc, char ** argv) +{ +	struct ipmi_rs * rsp; +	struct sdr_record_list *sdr; +	struct sdr_record_list *alist, *a; +	struct sdr_record_entity_assoc *assoc; +	int ledtype = 0xFF; +	int i; +	sunoem_ec_t res; + +	/* +	 * sunoem led/sbled get <id> [type] +	 */ + +	if (argc < 1 || strncmp(argv[0], "help", 4) == 0) { +		ipmi_sunoem_usage(); +		return (0); +	} + +	if (argc > 1) { +		ledtype = str2val(argv[1], sunoem_led_type_vals); +		if (ledtype == 0xFF) +			lprintf(LOG_ERR, +					"Unknow ledtype, will use data from the SDR oem field"); +	} + +	if (strncasecmp(argv[0], "all", 3) == 0) { +		/* do all generic sensors */ +		alist = ipmi_sdr_find_sdr_bytype(intf, +		SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR); + +		if (alist == NULL) +			return (-1); + +		for (a = alist; a != NULL; a = a->next) { +			if (a->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) +				continue; +			if (a->record.genloc->entity.logical) +				continue; + +			res = sunoem_led_get(intf, a->record.genloc, ledtype, &rsp); + +			if (res == SUNOEM_EC_SUCCESS && rsp && rsp->data_len == 1) { +				led_print((const char *) a->record.genloc->id_string, +						PRINT_NORMAL, rsp->data[0]); +			} else { +				led_print((const char *) a->record.genloc->id_string, +						PRINT_ERROR, 0); +				if (res != SUNOEM_EC_BMC_CCODE_NONZERO|| !rsp || +				rsp->ccode != CC_DEST_UNAVAILABLE) { +					ret_get = -1; +				} +			} +		} +		__sdr_list_empty(alist); + +		if (ret_get == -1) +			return (-1); + +		return (0); +	} + +	/* look up generic device locator record in SDR */ +	sdr = ipmi_sdr_find_sdr_byid(intf, argv[0]); + +	if (sdr == NULL) { +		lprintf(LOG_ERR, "No Sensor Data Record found for %s", argv[0]); +		return (-1); +	} + +	if (sdr->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) { +		lprintf(LOG_ERR, "Invalid SDR type %d", sdr->type); +		return (-1); +	} + +	if (!sdr->record.genloc->entity.logical) { +		/* +		 * handle physical entity +		 */ + +		res = sunoem_led_get(intf, sdr->record.genloc, ledtype, &rsp); + +		if (res == SUNOEM_EC_SUCCESS && rsp && rsp->data_len == 1) { +			led_print((const char *) sdr->record.genloc->id_string, +					PRINT_NORMAL, rsp->data[0]); + +		} else { +			led_print((const char *) sdr->record.genloc->id_string, PRINT_ERROR, +					0); +			if (res != SUNOEM_EC_BMC_CCODE_NONZERO|| !rsp +			|| rsp->ccode != CC_DEST_UNAVAILABLE) { +				ret_get = -1; +			} +		} + +		if (ret_get == -1) +			return (-1); + +		return (0); +	} + +	/* +	 * handle logical entity for LED grouping +	 */ + +	lprintf(LOG_INFO, "LED %s is logical device", argv[0]); + +	/* get entity assoc records */ +	alist = ipmi_sdr_find_sdr_bytype(intf, SDR_RECORD_TYPE_ENTITY_ASSOC); + +	if (alist == NULL) +		return (-1); + +	for (a = alist; a != NULL; a = a->next) { +		if (a->type != SDR_RECORD_TYPE_ENTITY_ASSOC) +			continue; +		assoc = a->record.entassoc; +		if (assoc == NULL) +			continue; + +		/* check that the entity id/instance matches our generic record */ +		if (assoc->entity.id != sdr->record.genloc->entity.id +				|| assoc->entity.instance +						!= sdr->record.genloc->entity.instance) +			continue; + +		if (assoc->flags.isrange) { +			/* +			 * handle ranged entity associations +			 * +			 * the test for non-zero entity id is handled in +			 * sunoem_led_get_byentity() +			 */ + +			/* first range set - id 1 and 2 must be equal */ +			if (assoc->entity_id_1 == assoc->entity_id_2) +				for (i = assoc->entity_inst_1; i <= assoc->entity_inst_2; i++) +					sunoem_led_get_byentity(intf, assoc->entity_id_1, i, +							ledtype); + +			/* second range set - id 3 and 4 must be equal */ +			if (assoc->entity_id_3 == assoc->entity_id_4) +				for (i = assoc->entity_inst_3; i <= assoc->entity_inst_4; i++) +					sunoem_led_get_byentity(intf, assoc->entity_id_3, i, +							ledtype); +		} else { +			/* +			 * handle entity list +			 */ +			sunoem_led_get_byentity(intf, assoc->entity_id_1, +					assoc->entity_inst_1, ledtype); +			sunoem_led_get_byentity(intf, assoc->entity_id_2, +					assoc->entity_inst_2, ledtype); +			sunoem_led_get_byentity(intf, assoc->entity_id_3, +					assoc->entity_inst_3, ledtype); +			sunoem_led_get_byentity(intf, assoc->entity_id_4, +					assoc->entity_inst_4, ledtype); +		} +	} + +	__sdr_list_empty(alist); + +	if (ret_get == -1) +		return (-1); + +	return (0); +} + +/* + * IPMI Request Data: 7 bytes + * + * [byte 0]  devAddr     Value from the "Device Slave Address" field in + *                       LED's Generic Device Locator record in the SDR + * [byte 1]  led         LED Type: OK2RM, ACT, LOCATE, SERVICE + * [byte 2]  ctrlrAddr   Controller address; value from the "Device + *                       Access Address" field, 0x20 if the LED is local + * [byte 3]  hwInfo      The OEM field from the SDR record + * [byte 4]  mode        LED Mode: OFF, ON, STANDBY, SLOW, FAST + * [byte 5]  force       TRUE - directly access the device + *                       FALSE - go thru its controller + *                       Ignored if LED is local + * [byte 6]  role        Used by BMC for authorization purposes + * + * The format below is for Sun Blade Modular systems only + * [byte 5]  entityID    The entityID field from the SDR record + * [byte 6]  entityIns   The entityIns field from the SDR record + * [byte 7]  force       TRUE - directly access the device + *                       FALSE - go thru its controller + *                       Ignored if LED is local + * [byte 8]  role        Used by BMC for authorization purposes + * + * + * IPMI Response Data: 1 byte + * + * [byte 0]  mode     LED Mode: OFF, ON, STANDBY, SLOW, FAST + */ + +static int +ipmi_sunoem_led_set(struct ipmi_intf * intf, int argc, char ** argv) +{ +	struct ipmi_rs * rsp; +	struct sdr_record_list *sdr; +	struct sdr_record_list *alist, *a; +	struct sdr_record_entity_assoc *assoc; +	int ledmode; +	int ledtype = 0xFF; +	int i; + +	/* +	 * sunoem led/sbled set <id> <mode> [type] +	 */ + +	if (argc < 2 || strncmp(argv[0], "help", 4) == 0) { +		ipmi_sunoem_usage(); +		return (0); +	} + +	ledmode = str2val(argv[1], sunoem_led_mode_vals); +	if (ledmode == 0xFF) { +		ledmode = str2val(argv[1], sunoem_led_mode_optvals); +		if (ledmode == 0xFF) { +			lprintf(LOG_NOTICE, "Invalid LED Mode: %s", argv[1]); +			return (-1); +		} +	} + +	if (argc > 3) { +		ledtype = str2val(argv[2], sunoem_led_type_vals); +		if (ledtype == 0xFF) +			lprintf(LOG_ERR, +					"Unknow ledtype, will use data from the SDR oem field"); +	} + +	if (strncasecmp(argv[0], "all", 3) == 0) { +		/* do all generic sensors */ +		alist = ipmi_sdr_find_sdr_bytype(intf, +		SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR); + +		if (alist == NULL) +			return (-1); + +		for (a = alist; a != NULL; a = a->next) { +			if (a->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) +				continue; +			if (a->record.genloc->entity.logical) +				continue; +			rsp = sunoem_led_set(intf, a->record.genloc, ledtype, ledmode); +			if (rsp && rsp->ccode == 0) +				led_print((const char *) a->record.genloc->id_string, +						PRINT_NORMAL, ledmode); +			else +				ret_set = -1; +		} +		__sdr_list_empty(alist); + +		if (ret_set == -1) +			return (-1); + +		return (0); +	} + +	/* look up generic device locator records in SDR */ +	sdr = ipmi_sdr_find_sdr_byid(intf, argv[0]); + +	if (sdr == NULL) { +		lprintf(LOG_ERR, "No Sensor Data Record found for %s", argv[0]); +		return (-1); +	} + +	if (sdr->type != SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR) { +		lprintf(LOG_ERR, "Invalid SDR type %d", sdr->type); +		return (-1); +	} + +	if (!sdr->record.genloc->entity.logical) { +		/* +		 * handle physical entity +		 */ +		rsp = sunoem_led_set(intf, sdr->record.genloc, ledtype, ledmode); +		if (rsp && rsp->ccode == 0) +			led_print(argv[0], PRINT_NORMAL, ledmode); +		else +			return (-1); + +		return (0); +	} + +	/* +	 * handle logical entity for LED grouping +	 */ + +	lprintf(LOG_INFO, "LED %s is logical device", argv[0]); + +	/* get entity assoc records */ +	alist = ipmi_sdr_find_sdr_bytype(intf, SDR_RECORD_TYPE_ENTITY_ASSOC); + +	if (alist == NULL) +		return (-1); + +	for (a = alist; a != NULL; a = a->next) { +		if (a->type != SDR_RECORD_TYPE_ENTITY_ASSOC) +			continue; +		assoc = a->record.entassoc; +		if (assoc == NULL) +			continue; + +		/* check that the entity id/instance matches our generic record */ +		if (assoc->entity.id != sdr->record.genloc->entity.id +				|| assoc->entity.instance +						!= sdr->record.genloc->entity.instance) +			continue; + +		if (assoc->flags.isrange) { +			/* +			 * handle ranged entity associations +			 * +			 * the test for non-zero entity id is handled in +			 * sunoem_led_get_byentity() +			 */ + +			/* first range set - id 1 and 2 must be equal */ +			if (assoc->entity_id_1 == assoc->entity_id_2) +				for (i = assoc->entity_inst_1; i <= assoc->entity_inst_2; i++) +					sunoem_led_set_byentity(intf, assoc->entity_id_1, i, +							ledtype, ledmode); + +			/* second range set - id 3 and 4 must be equal */ +			if (assoc->entity_id_3 == assoc->entity_id_4) +				for (i = assoc->entity_inst_3; i <= assoc->entity_inst_4; i++) +					sunoem_led_set_byentity(intf, assoc->entity_id_3, i, +							ledtype, ledmode); +		} else { +			/* +			 * handle entity list +			 */ +			sunoem_led_set_byentity(intf, assoc->entity_id_1, +					assoc->entity_inst_1, ledtype, ledmode); +			sunoem_led_set_byentity(intf, assoc->entity_id_2, +					assoc->entity_inst_2, ledtype, ledmode); +			sunoem_led_set_byentity(intf, assoc->entity_id_3, +					assoc->entity_inst_3, ledtype, ledmode); +			sunoem_led_set_byentity(intf, assoc->entity_id_4, +					assoc->entity_inst_4, ledtype, ledmode); +		} +	} + +	__sdr_list_empty(alist); + +	if (ret_set == -1) +		return (-1); + +	return (0); +} + +static int +ipmi_sunoem_sshkey_del(struct ipmi_intf * intf, uint8_t uid) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; + +	memset(&req, 0, sizeof(struct ipmi_rq)); +	req.msg.netfn = IPMI_NETFN_SUNOEM; +	req.msg.cmd = IPMI_SUNOEM_DEL_SSH_KEY; +	req.msg.data = &uid; +	req.msg.data_len = 1; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Unable to delete ssh key for UID %d", uid); +		return (-1); +	} else if (rsp->ccode > 0) { +		lprintf(LOG_ERR, "Unable to delete ssh key for UID %d: %s", uid, +				val2str(rsp->ccode, completion_code_vals)); +		return (-1); +	} + +	printf("Deleted SSH key for user id %d\n", uid); +	return (0); +} + +#define SSHKEY_BLOCK_SIZE	64 +static int +ipmi_sunoem_sshkey_set(struct ipmi_intf * intf, uint8_t uid, char * ifile) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	FILE * fp; +	int count = 0; +	uint8_t wbuf[SSHKEY_BLOCK_SIZE + 3]; +	int32_t i_size = 0; +	int32_t r = 0; +	int32_t size = 0; + +	if (ifile == NULL) { +		lprintf(LOG_ERR, "Invalid or misisng input filename."); +		return (-1); +	} + +	fp = ipmi_open_file_read(ifile); +	if (fp == NULL) { +		lprintf(LOG_ERR, "Unable to open file '%s' for reading.", ifile); +		return (-1); +	} + +	memset(&req, 0, sizeof(struct ipmi_rq)); +	req.msg.netfn = IPMI_NETFN_SUNOEM; +	req.msg.cmd = IPMI_SUNOEM_SET_SSH_KEY; +	req.msg.data = wbuf; + +	if (fseek(fp, 0, SEEK_END) == (-1)) { +		lprintf(LOG_ERR, "Failed to seek in file '%s'.", ifile); +		if (fp != NULL) +			fclose(fp); + +		return (-1); +	} + +	size = (int32_t) ftell(fp); +	if (size < 0) { +		lprintf(LOG_ERR, "Failed to seek in file '%s'.", ifile); +		if (fp != NULL) +			fclose(fp); + +		return (-1); +	} else if (size == 0) { +		lprintf(LOG_ERR, "File '%s' is empty.", ifile); +		if (fp != NULL) +			fclose(fp); + +		return (-1); +	} + +	if (fseek(fp, 0, SEEK_SET) == (-1)) { +		lprintf(LOG_ERR, "Failed to seek in file '%s'.", ifile); +		if (fp != NULL) +			fclose(fp); + +		return (-1); +	} + +	printf("Setting SSH key for user id %d...", uid); + +	for (r = 0; r < size; r += i_size) { +		i_size = size - r; +		if (i_size > SSHKEY_BLOCK_SIZE) +			i_size = SSHKEY_BLOCK_SIZE; + +		memset(wbuf, 0, SSHKEY_BLOCK_SIZE); +		fseek(fp, r, SEEK_SET); +		count = fread(wbuf + 3, 1, i_size, fp); +		if (count != i_size) { +			printf("failed\n"); +			lprintf(LOG_ERR, "Unable to read %ld bytes from file '%s'.", i_size, +					ifile); +			if (fp != NULL) +				fclose(fp); + +			return (-1); +		} + +		printf("."); +		fflush(stdout); + +		wbuf[0] = uid; +		if ((r + SSHKEY_BLOCK_SIZE) >= size) +			wbuf[1] = 0xff; +		else { +			if ((r / SSHKEY_BLOCK_SIZE) > UINT8_MAX) { +				printf("failed\n"); +				lprintf(LOG_ERR, "Unable to pack byte %ld from file '%s'.", r, +						ifile); +				if (fp != NULL) +					fclose(fp); + +				return (-1); +			} +			wbuf[1] = (uint8_t) (r / SSHKEY_BLOCK_SIZE); +		} + +		wbuf[2] = (uint8_t) i_size; + +		req.msg.data_len = i_size + 3; + +		rsp = intf->sendrecv(intf, &req); +		if (rsp == NULL) { +			printf("failed\n"); +			lprintf(LOG_ERR, "Unable to set ssh key for UID %d.", uid); +			if (fp != NULL) +				fclose(fp); + +			return (-1); +		} /* if (rsp == NULL) */ +		if (rsp->ccode != 0) { +			printf("failed\n"); +			lprintf(LOG_ERR, "Unable to set ssh key for UID %d, %s.", uid, +					val2str(rsp->ccode, completion_code_vals)); +			if (fp != NULL) +				fclose(fp); + +			return (-1); +		} /* if (rsp->ccode != 0) */ +	} + +	printf("done\n"); + +	fclose(fp); +	return (0); +} + +/* + * This structure is used in both the request to and response from the BMC. + */ +#define SUNOEM_CLI_LEGACY_VERSION       1 +#define SUNOEM_CLI_SEQNUM_VERSION       2 +#define SUNOEM_CLI_VERSION       SUNOEM_CLI_SEQNUM_VERSION +#define SUNOEM_CLI_HEADER        8 /* command + spare + handle */ +#define SUNOEM_CLI_BUF_SIZE      (80 - SUNOEM_CLI_HEADER) /* Total 80 bytes */ +#define SUNOEM_CLI_MSG_SIZE(msg) (SUNOEM_CLI_HEADER + strlen((msg).buf) + 1) + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ +	/* +	 * Set version to SUNOEM_CLI_VERSION. +	 */ +	uint8_t version; +	/* +	 * The command in a request, or in a response indicates an error if +	 * non-zero. +	 */ +	uint8_t command_response; +	uint8_t seqnum; +	uint8_t spare; +	/* +	 * Opaque 4-byte handle, supplied in the response to an OPEN request, +	 * and used in all subsequent POLL and CLOSE requests. +	 */ +	uint8_t handle[4]; +	/* +	 * The client data in a request, or the server data in a response. Must +	 * by null terminated, i.e., it must be at least one byte, but can be +	 * smaller if there's less data. +	 */ +	char buf[SUNOEM_CLI_BUF_SIZE]; +}__attribute__((packed)) sunoem_cli_msg_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * Command codes for the command_request field in each request. + */ +#define SUNOEM_CLI_CMD_OPEN   0 /* Open a new connection */ +#define SUNOEM_CLI_CMD_FORCE  1 /* Close any existing connection, then open */ +#define SUNOEM_CLI_CMD_CLOSE  2 /* Close the current connection */ +#define SUNOEM_CLI_CMD_POLL   3 /* Poll for new data to/from the server */ +#define SUNOEM_CLI_CMD_EOF    4 /* Poll, client is out of data */ + +#define SUNOEM_CLI_MAX_RETRY  3 /* Maximum number of retries */ + +#define SUNOEM_CLI_INVALID_VER_ERR "Invalid version" +#define SUNOEM_CLI_BUSY_ERR        "Busy" + +typedef enum +{ +	C_CTL_B = 0x02, /* same as left arrow */ +	C_CTL_C = 0x03, +	C_CTL_D = 0x04, +	C_CTL_F = 0x06, /* same as right arrow */ +	C_CTL_N = 0x0E, /* same as down arrow */ +	C_CTL_P = 0x10, /* same as up arrow */ +	C_DEL = 0x7f +} canon_char_t; + +static int +sunoem_cli_unbufmode_start(FILE *f, struct termios *orig_ts) +{ +	struct termios ts; +	int rc; + +	if ((rc = tcgetattr(fileno(f), &ts))) { +		return (rc); +	} +	*orig_ts = ts; +	ts.c_lflag &= ~(ICANON | ECHO | ISIG); +	ts.c_cc[VMIN] = 1; +	if ((rc = tcsetattr(fileno(f), TCSAFLUSH, &ts))) { +		return (rc); +	} + +	return (0); +} + +static int +sunoem_cli_unbufmode_stop(FILE *f, struct termios *ts) +{ +	int rc; + +	if ((rc = tcsetattr(fileno(f), TCSAFLUSH, ts))) { +		return (rc); +	} + +	return (0); +} + +static int +ipmi_sunoem_cli(struct ipmi_intf * intf, int argc, char *argv[]) +{ +	struct ipmi_rs *rsp; +	struct ipmi_rq req; +	sunoem_cli_msg_t cli_req; +	sunoem_cli_msg_t *cli_rsp; +	int arg_num = 0; +	int arg_pos = 0; +	time_t wait_time = 0; +	int retries; +	static uint8_t SunOemCliActingVersion = SUNOEM_CLI_VERSION; + +	unsigned short first_char = 0; /*first char on the line*/ +	struct termios orig_ts; +	int error = 0; + +	time_t now = 0; +	int delay = 0; + +	/* Prepare to open an SP shell session */ +	memset(&cli_req, 0, sizeof(cli_req)); +	cli_req.version = SunOemCliActingVersion; +	cli_req.command_response = SUNOEM_CLI_CMD_OPEN; +	if (argc > 0 && strcmp(argv[0], "force") == 0) { +		cli_req.command_response = SUNOEM_CLI_CMD_FORCE; +		argc--; +		argv++; +	} +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_SUNOEM; +	req.msg.cmd = IPMI_SUNOEM_CLI; +	req.msg.data = (uint8_t *) &cli_req; +	req.msg.data_len = SUNOEM_CLI_HEADER + 1; +	retries = 0; +	while (1) { +		cli_req.version = SunOemCliActingVersion; +		rsp = intf->sendrecv(intf, &req); +		if (rsp == NULL) { +			lprintf(LOG_ERR, "Sun OEM cli command failed"); +			return (-1); +		} +		cli_rsp = (sunoem_cli_msg_t *) rsp->data; +		if ((cli_rsp->command_response != 0) || (rsp->ccode != 0)) { +			if (strncmp(cli_rsp->buf, SUNOEM_CLI_INVALID_VER_ERR, +					sizeof(SUNOEM_CLI_INVALID_VER_ERR) - 1) == 0 +					|| strncmp(&(cli_rsp->buf[1]), SUNOEM_CLI_INVALID_VER_ERR, +							sizeof(SUNOEM_CLI_INVALID_VER_ERR) - 1) == 0) { +				if (SunOemCliActingVersion == SUNOEM_CLI_VERSION) { +					/* Server doesn't support version SUNOEM_CLI_VERSION +					 Fall back to legacy version, and try again*/ +					SunOemCliActingVersion = SUNOEM_CLI_LEGACY_VERSION; +					continue; +				} +				/* Server doesn't support legacy version either */ +				lprintf(LOG_ERR, "Failed to connect: %s", cli_rsp->buf); +				return (-1); +			} else if (strncmp(cli_rsp->buf, SUNOEM_CLI_BUSY_ERR, +					sizeof(SUNOEM_CLI_BUSY_ERR) - 1) == 0) { +				if (retries++ < SUNOEM_CLI_MAX_RETRY) { +					lprintf(LOG_INFO, "Failed to connect: %s, retrying", +							cli_rsp->buf); +					sleep(2); +					continue; +				} +				lprintf(LOG_ERR, "Failed to connect: %s", cli_rsp->buf); +				return (-1); +			} else { +				lprintf(LOG_ERR, "Failed to connect: %s", cli_rsp->buf); +				return (-1); +			} +		} +		break; +	} +	if (SunOemCliActingVersion == SUNOEM_CLI_SEQNUM_VERSION) { +		/* +		 * Bit 1 of seqnum is used as an alternating sequence number +		 * to allow a server that supports it to detect when a retry is being sent from the host IPMI driver. +		 * Typically when this occurs, the server's last response message would have been dropped. +		 * Once the server detects this condition, it will know that it should retry sending the response. +		 */ +		cli_req.seqnum ^= 0x1; +	} +	printf("Connected. Use ^D to exit.\n"); +	fflush(NULL); + +	/* +	 * Remember the handle provided in the response, and issue a +	 * series of "poll" commands to send and get data +	 */ +	memcpy(cli_req.handle, cli_rsp->handle, 4); +	cli_req.command_response = SUNOEM_CLI_CMD_POLL; +	/* +	 * If no arguments make input unbuffered and so interactive +	 */ +	if (argc == 0) { +		if (sunoem_cli_unbufmode_start(stdin, &orig_ts)) { +			lprintf(LOG_ERR, "Failed to set interactive mode: %s", +					strerror(errno)); +			return (-1); +		} +	} +	while (rsp->ccode == 0 && cli_rsp->command_response == 0) { +		int rc = 0; +		int count = 0; +		cli_req.buf[0] = '\0'; +		if (argc == 0) { +			/* +			 * Accept input from stdin. Use select so we don't hang if +			 * there's no input to read. Select timeout is 500 msec. +			 */ +			struct timeval tv = { 0, 500000 }; /* 500 msec */ +			fd_set rfds; +			FD_ZERO(&rfds); +			FD_SET(0, &rfds); +			rc = select(1, &rfds, NULL, NULL, &tv); +			if (rc < 0) { +				/* Select returned an error so close and exit */ +				printf("Broken pipe\n"); +				cli_req.command_response = SUNOEM_CLI_CMD_CLOSE; +			} else if (rc > 0) { +				/* Read data from stdin */ +				count = read(0, cli_req.buf, 1 /* sizeof (cli_req.buf) - 1 */); +				/* +				 * If select said there was data but there was nothing to +				 * read. This implies user hit ^D. +				 * Also handle ^D input when pressed as first char at a new line. +				 */ +				if (count <= 0 || (first_char && cli_req.buf[0] == C_CTL_D)) { +					cli_req.command_response = SUNOEM_CLI_CMD_EOF; +					count = 0; +				} +				first_char = cli_req.buf[0] == '\n' || cli_req.buf[0] == '\r'; +			} +		} else { +			/* +			 * Get data from command line arguments +			 */ +			now = time(NULL); +			if (now < wait_time) { +				/* Do nothing; we're waiting */ +			} else if (arg_num >= argc) { +				/* Last arg was sent. Set EOF */ +				cli_req.command_response = SUNOEM_CLI_CMD_EOF; +			} else if (strncmp(argv[arg_num], "@wait=", 6) == 0) { +				/* This is a wait command */ +				char *s = &argv[arg_num][6]; +				delay = 0; +				if (*s != '\0') { +					if (str2int(s, &delay)) { +						delay = 0; +					} +					if (delay < 0) { +						delay = 0; +					} +				} +				wait_time = now + delay; +				arg_num++; +			} else { +				/* +				 * Take data from args. It may be that the argument is larger +				 * than the request buffer can hold. So pull off BUF_SIZE +				 * number of characters at a time. When we've consumed the +				 * entire arg, append a newline and advance to the next arg. +				 */ +				int i; +				char *s = argv[arg_num]; +				for (i = arg_pos; +						s[i] != '\0' && count < (SUNOEM_CLI_BUF_SIZE - 2); +						i++, count++) { +					cli_req.buf[count] = s[i]; +				} +				if (s[i] == '\0') { +					/* Reached end of the arg string, so append a newline */ +					cli_req.buf[count++] = '\n'; +					/* Reset pos to 0 and advance to the next arg next time */ +					arg_pos = 0; +					arg_num++; +				} else { +					/* +					 * Otherwise, there's still more characters in the arg +					 * to send, so remember where we left off +					 */ +					arg_pos = i; +				} +			} +		} +		/* +		 * Now send the clients's data (if any) and get data back from the +		 * server. Loop while the server is giving us data until we suck +		 * it dry. +		 */ +		do { +			cli_req.buf[count++] = '\0'; /* Terminate the string */ +			memset(&req, 0, sizeof(req)); +			req.msg.netfn = IPMI_NETFN_SUNOEM; +			req.msg.cmd = 0x19; +			req.msg.data = (uint8_t *) &cli_req; +			req.msg.data_len = SUNOEM_CLI_HEADER + count; +			for (retries = 0; retries <= SUNOEM_CLI_MAX_RETRY; retries++) { +				rsp = intf->sendrecv(intf, &req); +				if (rsp == NULL) { +					lprintf(LOG_ERR, "Communication error."); +					error = 1; +					goto cleanup; +				} +				if (rsp->ccode == IPMI_CC_TIMEOUT) { /* Retry if timed out. */ +					if (retries == SUNOEM_CLI_MAX_RETRY) { /* If it's the last retry. */ +						lprintf(LOG_ERR, "Excessive timeout."); +						error = 1; +						goto cleanup; +					} +					continue; +				} +				break; +			} /* for (retries = 0; retries <= SUNOEM_CLI_MAX_RETRY; retries++) */ + +			if (SunOemCliActingVersion == SUNOEM_CLI_SEQNUM_VERSION) { +				cli_req.seqnum ^= 0x1; /* Toggle sequence number after request is sent */ +			} + +			cli_rsp = (sunoem_cli_msg_t *) rsp->data; +			/* Make sure response string is null terminated */ +			cli_rsp->buf[sizeof(cli_rsp->buf) - 1] = '\0'; +			printf("%s", cli_rsp->buf); +			fflush(NULL); /* Flush partial lines to stdout */ +			count = 0; /* Don't re-send the client's data */ +			if (cli_req.command_response == SUNOEM_CLI_CMD_EOF +					&& cli_rsp->command_response != 0 && rsp->ccode == 0) { +				cli_rsp->command_response = 1; +			} +		} while (cli_rsp->command_response == 0 && cli_rsp->buf[0] != '\0'); +	} + +cleanup: +	/* Restore original input mode if cli was running interactively */ +	if (argc == 0) { +		if (sunoem_cli_unbufmode_stop(stdin, &orig_ts)) { +			lprintf(LOG_ERR, "Failed to restore interactive mode: %s", +					strerror(errno)); +			return (-1); +		} +	} + +	return ((error == 0 && cli_rsp->command_response == SUNOEM_SUCCESS) ? 0 : -1); +} +#define ECHO_DATA_SIZE 64 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ +	uint16_t seq_num; +	unsigned char data[ECHO_DATA_SIZE]; +}__attribute__((packed)) sunoem_echo_msg_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * Send and receive X packets to the BMC. Each packet has a + * payload size of (sunoem_echo_msg_t) bytes. Each packet is tagged with a + * sequence number + */ +static int +ipmi_sunoem_echo(struct ipmi_intf * intf, int argc, char *argv[]) +{ +	struct ipmi_rs *rsp; +	struct ipmi_rq req; +	sunoem_echo_msg_t echo_req; +	sunoem_echo_msg_t *echo_rsp; +	struct timeval start_time; +	struct timeval end_time; + +	int rc = 0; +	int received = 0; +	int transmitted = 0; +	int quiet_mode = 0; + +	uint16_t num, i, j; +	uint32_t total_time, resp_time, min_time, max_time; + +	if (argc < 1) { +		return (1); +	} + +	if (argc == 2) { +		if (*(argv[1]) == 'q') { +			quiet_mode = 1; +		} else { +			lprintf(LOG_ERR, "Unknown option '%s' given.", argv[1]); +			return (-1); +		} +	} else if (argc > 2) { +		lprintf(LOG_ERR, +				"Too many parameters given. See help for more information."); +		return (-1); +	} +	/* The number of packets to send/receive */ +	if (str2ushort(argv[0], &num) != 0) { +		lprintf(LOG_ERR, +				"Given number of packets is either invalid or out of range."); +		return (-1); +	} + +	/* Fill in data packet */ +	for (i = 0; i < ECHO_DATA_SIZE; i++) { +		if (i > UINT8_MAX) +			break; + +		echo_req.data[i] = (uint8_t) i; +	} + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_SUNOEM; +	req.msg.cmd = IPMI_SUNOEM_ECHO; +	req.msg.data = (uint8_t *) &echo_req; +	req.msg.data_len = sizeof(sunoem_echo_msg_t); +	echo_req.seq_num = i; +	min_time = INT_MAX; +	max_time = 0; +	total_time = 0; +	for (i = 0; i < num; i++) { +		echo_req.seq_num = i; +		transmitted++; +		gettimeofday(&start_time, NULL); +		rsp = intf->sendrecv(intf, &req); +		gettimeofday(&end_time, NULL); +		resp_time = ((end_time.tv_sec - start_time.tv_sec) * 1000) +				+ ((end_time.tv_usec - start_time.tv_usec) / 1000); +		if ((rsp == NULL) || (rsp->ccode != 0)) { +			lprintf(LOG_ERR, "Sun OEM echo command failed. Seq # %d", +					echo_req.seq_num); +			rc = (-2); +			break; +		} +		echo_rsp = (sunoem_echo_msg_t *) rsp->data; + +		/* Test if sequence # is valid */ +		if (echo_rsp->seq_num != echo_req.seq_num) { +			printf("Invalid Seq # Expecting %d Received %d\n", echo_req.seq_num, +					echo_rsp->seq_num); +			rc = (-2); +			break; +		} + +		/* Test if response length is valid */ +		if (rsp->session.msglen == req.msg.data_len) { +			printf("Invalid payload size for seq # %d. " +					"Expecting %d Received %d\n", echo_rsp->seq_num, +					req.msg.data_len, rsp->session.msglen); +			rc = (-2); +			break; +		} + +		/* Test if the data is valid */ +		for (j = 0; j < ECHO_DATA_SIZE; j++) { +			if (echo_rsp->data[j] != j) { +				printf("Corrupt data packet. Seq # %d Offset %d\n", +						echo_rsp->seq_num, j); +				break; +			} +		} /* for (j = 0; j < ECHO_DATA_SIZE; j++) */ + +		/* If the for loop terminated early - data is corrupt */ +		if (j != ECHO_DATA_SIZE) { +			rc = (-2); +			break; +		} + +		/* cumalative time */ +		total_time += resp_time; + +		/* min time */ +		if (resp_time < min_time) { +			min_time = resp_time; +		} + +		/* max time */ +		if (resp_time > max_time) { +			max_time = resp_time; +		} + +		received++; +		if (!quiet_mode) { +			printf("Receive %u Bytes - Seq. # %d time=%d ms\n", +					sizeof(sunoem_echo_msg_t), echo_rsp->seq_num, resp_time); +		} +	} /* for (i = 0; i < num; i++) */ +	printf("%d packets transmitted, %d packets received\n", transmitted, +			received); +	if (received) { +		printf("round-trip min/avg/max = %d/%d/%d ms\n", min_time, +				total_time / received, max_time); +	} + +	return (rc); +} /* ipmi_sunoem_echo(...) */ + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ +	unsigned char oem_record_ver_num; +	unsigned char major; +	unsigned char minor; +	unsigned char update; +	unsigned char micro; +	char nano[10]; +	char revision[10]; +	char version[40]; +	/* +	 * When adding new fields (using the spare bytes), +	 * add it immediately after the spare field to +	 * ensure backward compatability. +	 * +	 * e.g.   char version[40]; +	 *        unsigned char spare[11]; +	 *        int new_item; +	 *    } sunoem_version_response_t; +	 */ +	unsigned char spare[15]; +}__attribute__((packed)) sunoem_version_response_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +typedef struct +{ +	unsigned char major; +	unsigned char minor; +	unsigned char update; +	unsigned char micro; +} supported_version_t; + +static int +ipmi_sunoem_getversion(struct ipmi_intf * intf, +		sunoem_version_response_t **version_rsp) +{ +	struct ipmi_rs *rsp; +	struct ipmi_rq req; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_SUNOEM; +	req.msg.cmd = IPMI_SUNOEM_VERSION; +	req.msg.data = NULL; +	req.msg.data_len = 0; +	rsp = intf->sendrecv(intf, &req); + +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Sun OEM Get SP Version Failed."); +		return (-1); +	} +	if (rsp->ccode != 0) { +		lprintf(LOG_ERR, "Sun OEM Get SP Version Failed: %d", rsp->ccode); +		return (-1); +	} + +	*version_rsp = (sunoem_version_response_t *) rsp->data; + +	return (0); +} + +static void +ipmi_sunoem_print_required_version(const supported_version_t* supp_ver) +{ +	lprintf(LOG_ERR, "Command is not supported by this version of ILOM," +			" required at least: %d.%d.%d.%d", supp_ver->major, supp_ver->minor, +			supp_ver->update, supp_ver->micro); +} + +/* + * Function checks current version result against required version. + * Returns: + *  - negative value if current ILOM version is smaller than required or + *    in case of error + *  - positive value if current ILOM version is greater than required + *  - 0 if there is an exact ILOM version match + */ +static int +ipmi_sunoem_checkversion(struct ipmi_intf * intf, supported_version_t* supp_ver) +{ +	sunoem_version_response_t *version_rsp; +	int i = 1; + +	if (ipmi_sunoem_getversion(intf, &version_rsp)) { +		lprintf(LOG_ERR, "Unable to get ILOM version"); +		return (-1); +	} + +	if (version_rsp->major < supp_ver->major) return (-i); +	if (version_rsp->major > supp_ver->major) return (i); +	/*version_rsp->major == supp_ver->major*/ +	++i; + +	if (version_rsp->minor < supp_ver->minor) return (-i); +	if (version_rsp->minor > supp_ver->minor) return (i); +	/*version_rsp->minor == supp_ver->minor*/ +	++i; + +	if (version_rsp->update < supp_ver->update) return (-i); +	if (version_rsp->update > supp_ver->update) return (i); +	/*version_rsp->update == supp_ver->update*/ +	++i; + +	if (version_rsp->micro < supp_ver->micro) return (-i); +	if (version_rsp->micro > supp_ver->micro) return (i); +	/*version_rsp->micro == supp_ver->micro*/ + +	return (0); +} + +/* + * Extract the SP version data including + * - major # + * - minor # + * - update # + * - micro # + * - nano # + * - Revision/Build # + */ +static int +ipmi_sunoem_version(struct ipmi_intf * intf) +{ +	sunoem_version_response_t *version_rsp; +	int rc = ipmi_sunoem_getversion(intf, &version_rsp); + +	if (!rc) { +		printf("Version: %s\n", version_rsp->version); +	} + +	return (rc); +} + +/* + * IPMI Max string length is 16 bytes + * define in usr/src/common/include/ami/IPMI_SDRRecord.h + */ +#define MAX_ID_STR_LEN  16 +#define MAX_SUNOEM_NAC_SIZE 64 +#define LUAPI_MAX_OBJ_PATH_LEN 256 +#define LUAPI_MAX_OBJ_VAL_LEN 1024 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ +	unsigned char seq_num; +	char nac_name[MAX_SUNOEM_NAC_SIZE]; +}__attribute__((packed)) sunoem_nacname_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * Retrieve the full NAC name of the IPMI target. + * + * The returned nac name may be larger than the payload size. + * In which case, it make take several request/payload to retrieve + * the entire full path name + * + * The initial seq_num is set to 0. If the return seq_num is incremented, + * only the 1st 72 bytes of the nac name is returned and the caller + * needs to get the next set of string data. + * If the returned seq_num is identical to the input seq_num, all data + * has been returned. + */ +static int +ipmi_sunoem_nacname(struct ipmi_intf * intf, int argc, char *argv[]) +{ +	struct ipmi_rs *rsp; +	struct ipmi_rq req; +	sunoem_nacname_t nacname_req; +	sunoem_nacname_t *nacname_rsp; +	char full_nac_name[LUAPI_MAX_OBJ_PATH_LEN]; + +	if (argc < 1) { +		return (1); +	} + +	if (strlen(argv[0]) > MAX_ID_STR_LEN) { +		lprintf(LOG_ERR, +				"Sun OEM nacname command failed: Max size on IPMI name"); +		return (-1); +	} + +	nacname_req.seq_num = 0; +	strcpy(nacname_req.nac_name, argv[0]); + +	full_nac_name[0] = '\0'; +	while (1) { +		memset(&req, 0, sizeof(req)); +		req.msg.netfn = IPMI_NETFN_SUNOEM; +		req.msg.cmd = IPMI_SUNOEM_NACNAME; +		req.msg.data = (uint8_t *) &nacname_req; +		req.msg.data_len = sizeof(sunoem_nacname_t); +		rsp = intf->sendrecv(intf, &req); + +		if (rsp == NULL) { +			lprintf(LOG_ERR, "Sun OEM nacname command failed."); +			return (-1); +		} +		if (rsp->ccode != 0) { +			lprintf(LOG_ERR, "Sun OEM nacname command failed: %d", rsp->ccode); +			return (-1); +		} + +		nacname_rsp = (sunoem_nacname_t *) rsp->data; +		strncat(full_nac_name, nacname_rsp->nac_name, MAX_SUNOEM_NAC_SIZE); + +		/* +		 * break out of the loop if there is no more data +		 * In most cases, if not all, the NAC name fits into a +		 * single payload +		 */ +		if (nacname_req.seq_num == nacname_rsp->seq_num) { +			break; +		} + +		/* Get the next seq of string bytes */ +		nacname_req.seq_num = nacname_rsp->seq_num; + +		/* Check if we exceeded the size of the full nac name */ +		if ((nacname_req.seq_num * MAX_SUNOEM_NAC_SIZE) > LUAPI_MAX_OBJ_PATH_LEN) { +			lprintf(LOG_ERR, +					"Sun OEM nacname command failed: invalid path length"); +			return (-1); +		} +	} + +	printf("NAC Name: %s\n", full_nac_name); +	return (0); +} + +/* Constants used by ipmi_sunoem_getval */ +#define MAX_SUNOEM_VAL_PAYLOAD 79 +#define MAX_SUNOEM_VAL_COMPACT_PAYLOAD 56 + +/* + * SUNOEM GET/SET LUAPI Commands + * + * SUNOEM_REQ_VAL - Request LUAPI Property Value + * SUNOEM_GET_VAL - Return the value from  SUNOEM_REQ_VAL + * SUNOEM_SET_VAL - Set the LUAPI Property value + * SUNOEM_GET_STATUS - Return the Status from SUNOEM_SET_VAL + */ +#define SUNOEM_REQ_VAL 1 +#define SUNOEM_GET_VAL 2 +#define SUNOEM_SET_VAL 3 +#define SUNOEM_GET_STATUS 4 + +/* Status Code */ +#define SUNOEM_REQ_RECV 1 +#define SUNOEM_REQ_FAILED 2 +#define SUNOEM_DATA_READY 3 +#define SUNOEM_DATA_NOT_READY 4 +#define SUNOEM_DATA_NOT_FOUND 5 +#define GETVAL_MAX_RETRIES 5 + +/* Parameter type Codes */ +#define SUNOEM_LUAPI_TARGET 0 +#define SUNOEM_LUAPI_VALUE  1 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ +	unsigned char cmd_code; +	unsigned char luapi_value[MAX_SUNOEM_VAL_PAYLOAD]; +}__attribute__((packed)) sunoem_getval_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * REQUEST PAYLOAD + * + * cmd_code - SUNOEM GET/SET LUAPI Cmds - see above + * param_type: 0: luapi_data contains the luapi property name + *             1: luapi_data contains the luapi value + * luapi_data: Either luapi property name or value + * tid: Transaction ID. If 0. This is the initial request for the + *      param_type. If tid > 0, this luapi_data string is a concatenation + *      of the previous request. Handle cases where the LUAPI target name + *      or value is > MAX_SUNOEM_VAL_COMPACT_PAYLOAD + * eof: If non zero, this is the last payload for the request + */ +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ +	unsigned char cmd_code; +	unsigned char param_type; +	unsigned char tid; +	unsigned char eof; +	char luapi_data[MAX_SUNOEM_VAL_COMPACT_PAYLOAD]; +}__attribute__((packed)) sunoem_setval_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * RESPONSE PAYLOAD + * + * status_code - see above for code definitions + * tid - transaction ID - assigned ny the ILOM stack + */ +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ +	unsigned char status_code; +	unsigned char tid; +}__attribute__((packed)) sunoem_setval_resp_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +/* + * Return the ILOM target property value + */ +static int +ipmi_sunoem_getval(struct ipmi_intf * intf, int argc, char *argv[]) +{ +	struct ipmi_rs *rsp; +	struct ipmi_rq req; +	sunoem_getval_t getval_req; +	sunoem_getval_t *getval_rsp; +	int i; + +	const char* sp_path = "/SP"; +	supported_version_t supp_ver = { 3, 2, 0, 0 }; + +	if (argc < 1) { +		return (1); +	} + +	if (strlen(argv[0]) > MAX_SUNOEM_VAL_PAYLOAD) { +		lprintf(LOG_ERR, +				"Sun OEM get value command failed: Max size on IPMI name"); +		return (-1); +	} + +	if ((ipmi_sunoem_checkversion(intf, &supp_ver) < 0) +			&& (!strncmp(argv[0], sp_path, strlen(sp_path)))) { +		argv[0][1] = 'X'; /*replace SP by X to gain access to hidden properties*/ +		memmove(&argv[0][2], &argv[0][3], strlen(argv[0]) - 2); +	} + +	/* +	 * Setup the initial request to fetch the data. +	 * Upon function return, the next cmd (SUNOEM_GET_VAL) +	 * can be requested. +	 */ +	memset(&getval_req, 0, sizeof(getval_req)); +	strncpy((char*) getval_req.luapi_value, argv[0], MAX_SUNOEM_VAL_PAYLOAD); +	getval_req.cmd_code = SUNOEM_REQ_VAL; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_SUNOEM; +	req.msg.cmd = IPMI_SUNOEM_GETVAL; +	req.msg.data = (uint8_t *) &getval_req; +	req.msg.data_len = sizeof(sunoem_getval_t); +	rsp = intf->sendrecv(intf, &req); + +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Sun OEM getval1 command failed."); +		return (-1); +	} +	if (rsp->ccode != 0) { +		lprintf(LOG_ERR, "Sun OEM getval1 command failed: %d", rsp->ccode); +		return (-1); +	} + +	/* +	 * Fetch the data value - if it is not ready, +	 * retry the request up to GETVAL_MAX_RETRIES +	 */ +	for (i = 0; i < GETVAL_MAX_RETRIES; i++) { +		memset(&req, 0, sizeof(req)); +		req.msg.netfn = IPMI_NETFN_SUNOEM; +		req.msg.cmd = IPMI_SUNOEM_GETVAL; +		getval_req.cmd_code = SUNOEM_GET_VAL; +		req.msg.data = (uint8_t *) &getval_req; +		req.msg.data_len = sizeof(sunoem_getval_t); +		rsp = intf->sendrecv(intf, &req); + +		if (rsp == NULL) { +			lprintf(LOG_ERR, "Sun OEM getval2 command failed."); +			return (-1); +		} + +		if (rsp->ccode != 0) { +			lprintf(LOG_ERR, "Sun OEM getval2 command failed: %d", rsp->ccode); +			return (-1); +		} + +		getval_rsp = (sunoem_getval_t *) rsp->data; + +		if (getval_rsp->cmd_code == SUNOEM_DATA_READY) { +			printf("Target Value: %s\n", getval_rsp->luapi_value); +			return (0); +		} else if (getval_rsp->cmd_code == SUNOEM_DATA_NOT_FOUND) { +			lprintf(LOG_ERR, "Target: %s not found", getval_req.luapi_value); +			return (-1); +		} + +		sleep(1); +	} + +	lprintf(LOG_ERR, "Unable to retrieve target value."); +	return (-1); +} + +static int +send_luapi_prop_name(struct ipmi_intf * intf, int len, char *prop_name, +		unsigned char *tid_num) +{ +	int i = 0; +	struct ipmi_rs *rsp; +	struct ipmi_rq req; +	sunoem_setval_t setval_req; +	sunoem_setval_resp_t *setval_rsp; + +	*tid_num = 0; +	while (i < len) { +		/* +		 * Setup the request, +		 * Upon function return, the next cmd (SUNOEM_SET_VAL) +		 * can be requested. +		 */ +		memset(&req, 0, sizeof(req)); +		memset(&setval_req, 0, sizeof(sunoem_setval_t)); +		req.msg.netfn = IPMI_NETFN_SUNOEM; +		req.msg.cmd = IPMI_SUNOEM_SETVAL; +		setval_req.cmd_code = SUNOEM_SET_VAL; +		setval_req.param_type = SUNOEM_LUAPI_TARGET; +		setval_req.tid = *tid_num; +		setval_req.eof = 0; +		/* +		 * If the property name is > payload, only copy +		 * the payload size and increment the string offset (i) +		 * for the next payload +		 */ +		if (strlen(&(prop_name[i])) > MAX_SUNOEM_VAL_COMPACT_PAYLOAD) { +			strncpy(setval_req.luapi_data, &(prop_name[i]), +			MAX_SUNOEM_VAL_COMPACT_PAYLOAD); +		} else { +			strncpy(setval_req.luapi_data, &(prop_name[i]), +					strlen(&(prop_name[i]))); +		} +		req.msg.data = (uint8_t *) &setval_req; +		req.msg.data_len = sizeof(sunoem_setval_t); +		rsp = intf->sendrecv(intf, &req); + +		if (rsp == NULL) { +			lprintf(LOG_ERR, "Sun OEM setval prop name: response is NULL"); +			return (-1); +		} + +		if (rsp->ccode != 0) { +			lprintf(LOG_ERR, "Sun OEM setval prop name: request failed: %d", +					rsp->ccode); +			return (-1); +		} + +		setval_rsp = (sunoem_setval_resp_t *) rsp->data; + +		/* +		 * If the return code is other than data received, the +		 * request failed +		 */ +		if (setval_rsp->status_code != SUNOEM_REQ_RECV) { +			lprintf(LOG_ERR, +					"Sun OEM setval prop name: invalid status code: %d", +					setval_rsp->status_code); +			return (-1); +		} +		/* Use the tid returned by ILOM */ +		*tid_num = setval_rsp->tid; +		/* Increment the string offset */ +		i += MAX_SUNOEM_VAL_COMPACT_PAYLOAD; +	} + +	return (0); +} + +static int +send_luapi_prop_value(struct ipmi_intf * intf, int len,	char *prop_value, +		unsigned char tid_num) +{ +	int i = 0; +	struct ipmi_rs *rsp; +	struct ipmi_rq req; +	sunoem_setval_t setval_req; +	sunoem_setval_resp_t *setval_rsp; + +	while (i < len) { +		/* +		 * Setup the request, +		 * Upon function return, the next cmd (SUNOEM_GET_VAL) +		 * can be requested. +		 */ +		memset(&req, 0, sizeof(req)); +		memset(&setval_req, 0, sizeof(sunoem_setval_t)); +		req.msg.netfn = IPMI_NETFN_SUNOEM; +		req.msg.cmd = IPMI_SUNOEM_SETVAL; +		setval_req.cmd_code = SUNOEM_SET_VAL; +		setval_req.param_type = SUNOEM_LUAPI_VALUE; +		setval_req.tid = tid_num; +		/* +		 * If the property name is > payload, only copy the +		 * the payload size and increment the string offset +		 * for the next payload +		 */ +		if (strlen(&(prop_value[i])) > MAX_SUNOEM_VAL_COMPACT_PAYLOAD) { +			strncpy(setval_req.luapi_data, &(prop_value[i]), +			MAX_SUNOEM_VAL_COMPACT_PAYLOAD); +		} else { +			/* Captured the entire string, mark this as the last payload */ +			strncpy(setval_req.luapi_data, &(prop_value[i]), +					strlen(&(prop_value[i]))); +			setval_req.eof = 1; +		} +		req.msg.data = (uint8_t *) &setval_req; +		req.msg.data_len = sizeof(sunoem_setval_t); +		rsp = intf->sendrecv(intf, &req); + +		if (rsp == NULL) { +			lprintf(LOG_ERR, "Sun OEM setval prop value: response is NULL"); +			return (-1); +		} + +		if (rsp->ccode != 0) { +			lprintf(LOG_ERR, "Sun OEM setval prop value: request failed: %d", +					rsp->ccode); +			return (-1); +		} + +		setval_rsp = (sunoem_setval_resp_t *) rsp->data; + +		/* +		 * If the return code is other than data received, the +		 * request failed +		 */ +		if (setval_rsp->status_code != SUNOEM_REQ_RECV) { +			lprintf(LOG_ERR, +					"Sun OEM setval prop value: invalid status code: %d", +					setval_rsp->status_code); +			return (-1); +		} + +		/* Increment the string offset */ +		i += MAX_SUNOEM_VAL_COMPACT_PAYLOAD; +	} +	return (0); +} + +static int +ipmi_sunoem_setval(struct ipmi_intf * intf, int argc, char *argv[]) +{ +	struct ipmi_rs *rsp; +	struct ipmi_rq req; +	sunoem_setval_t setval_req; +	sunoem_setval_resp_t *setval_rsp; +	int prop_len; +	int value_len; +	int i; +	unsigned char tid_num; +	int retries; + +	prop_len = strlen(argv[0]); +	value_len = strlen(argv[1]); +	if (prop_len > LUAPI_MAX_OBJ_PATH_LEN) { +		lprintf(LOG_ERR, +				"Sun OEM set value command failed: Max size on property name"); +		return (-1); +	} +	if (value_len > LUAPI_MAX_OBJ_VAL_LEN) { +		lprintf(LOG_ERR, +				"Sun OEM set value command failed: Max size on property value"); +		return (-1); +	} + +	/* Test if there is a timeout specified */ +	if (argc == 3) { +		if ((str2int(argv[2], &retries) != 0) || retries < 0) { +			lprintf(LOG_ERR, +					"Invalid input given or out of range for time-out parameter."); +			return (-1); +		} +	} else { +		retries = GETVAL_MAX_RETRIES; +	} + +	/* Send the property name 1st */ +	if (send_luapi_prop_name(intf, prop_len, argv[0], &tid_num) != 0) { +		/* return if there is an error */ +		return (-1); +	} + +	if (send_luapi_prop_value(intf, value_len, argv[1], tid_num) != 0) { +		/* return if there is an error */ +		return (-1); +	} + +	/* +	 * Get The status of the command. +	 * if it is not ready, retry the request up to +	 * GETVAL_MAX_RETRIES +	 */ +	for (i = 0; i < retries; i++) { +		memset(&req, 0, sizeof(req)); +		req.msg.netfn = IPMI_NETFN_SUNOEM; +		req.msg.cmd = IPMI_SUNOEM_SETVAL; +		setval_req.cmd_code = SUNOEM_GET_STATUS; +		setval_req.tid = tid_num; +		req.msg.data = (uint8_t *) &setval_req; +		req.msg.data_len = sizeof(sunoem_setval_t); +		rsp = intf->sendrecv(intf, &req); + +		if (rsp == NULL) { +			lprintf(LOG_ERR, "Sun OEM setval command failed."); +			return (-1); +		} + +		if (rsp->ccode != 0) { +			lprintf(LOG_ERR, "Sun OEM setval command failed: %d", rsp->ccode); +			return (-1); +		} + +		setval_rsp = (sunoem_setval_resp_t *) rsp->data; + +		if (setval_rsp->status_code == SUNOEM_DATA_READY) { +			printf("Sun OEM setval command successful.\n"); +			return (0); +		} else if (setval_rsp->status_code != SUNOEM_DATA_NOT_READY) { +			lprintf(LOG_ERR, "Sun OEM setval command failed."); +			return (-1); +		} + +		sleep(1); +	} +	/* If we reached here, retries exceeded */ +	lprintf(LOG_ERR, "Sun OEM setval command failed: Command Timed Out"); + +	return (-1); +} + +#define MAX_FILE_DATA_SIZE            1024 +#define MAX_FILEID_LEN                16 +#define CORE_TUNNEL_SUBCMD_GET_FILE   11 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ +	unsigned char cmd_code; +	unsigned char file_id[MAX_FILEID_LEN]; +	unsigned int block_num; +}__attribute__((packed)) getfile_req_t; + +typedef struct +{ +	unsigned int block_num; +	unsigned int data_size; +	unsigned char eof; +	unsigned char data[MAX_FILE_DATA_SIZE]; +}__attribute__((packed)) getfile_rsp_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +static int +ipmi_sunoem_getfile(struct ipmi_intf * intf, int argc, char *argv[]) +{ +	struct ipmi_rs *rsp; +	struct ipmi_rq req; +	getfile_req_t getfile_req; +	getfile_rsp_t *getfile_rsp; +	int block_num = 0; +	int nbo_blk_num; /* Network Byte Order Block Num */ +	FILE *fp; +	unsigned data_size; +	supported_version_t supp_ver = IPMI_SUNOEM_GETFILE_VERSION; + +	if (argc < 1) { +		return (-1); +	} + +	/*check if command is supported by this version of ilom*/ +	if (ipmi_sunoem_checkversion(intf, &supp_ver) < 0) { +		ipmi_sunoem_print_required_version(&supp_ver); +		return (-1); +	} + +	/* +	 * File ID is < MAX_FILEID_LEN +	 * Save 1 byte for null Terminated string +	 */ +	if (strlen(argv[0]) >= MAX_FILE_DATA_SIZE) { +		lprintf(LOG_ERR, "File ID >= %d characters", MAX_FILEID_LEN); +		return (-1); +	} + +	memset(&getfile_req, 0, sizeof(getfile_req)); +	strncpy((char*) getfile_req.file_id, argv[0], MAX_FILEID_LEN - 1); + +	/* Create the destination file */ +	fp = ipmi_open_file_write(argv[1]); +	if (fp == NULL) { +		lprintf(LOG_ERR, "Unable to open file: %s", argv[1]); +		return (-1); +	} + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_SUNOEM; +	req.msg.cmd = IPMI_SUNOEM_CORE_TUNNEL; +	req.msg.data = (uint8_t *) &getfile_req; +	req.msg.data_len = sizeof(getfile_req_t); +	getfile_req.cmd_code = CORE_TUNNEL_SUBCMD_GET_FILE; + +	do { + +		nbo_blk_num = htonl(block_num); +		/* Block Num must be in network byte order */ +		memcpy(&(getfile_req.block_num), &nbo_blk_num, +				sizeof(getfile_req.block_num)); + +		rsp = intf->sendrecv(intf, &req); + +		if (rsp == NULL) { +			lprintf(LOG_ERR, "Sun OEM getfile command failed."); +			fclose(fp); +			return (-1); +		} +		if (rsp->ccode != 0) { +			lprintf(LOG_ERR, "Sun OEM getfile command failed: %d", rsp->ccode); +			fclose(fp); +			return (-1); +		} + +		getfile_rsp = (getfile_rsp_t *) rsp->data; + +		memcpy(&data_size, &(getfile_rsp->data_size), +				sizeof(getfile_rsp->data_size)); +		data_size = ntohl(data_size); + +		if (data_size > MAX_FILE_DATA_SIZE) { +			lprintf(LOG_ERR, "Sun OEM getfile invalid data size: %d", +					data_size); +			fclose(fp); +			return (-1); +		} + +		/* Check if Block Num matches */ +		if (memcmp(&(getfile_req.block_num), &(getfile_rsp->block_num), +				sizeof(getfile_req.block_num)) != 0) { +			lprintf(LOG_ERR, "Sun OEM getfile Incorrect Block Num Returned"); +			lprintf(LOG_ERR, "Expecting: %x Received: %x", +					getfile_req.block_num, getfile_rsp->block_num); +			fclose(fp); +			return (-1); +		} + +		if (fwrite(getfile_rsp->data, 1, data_size, fp) != data_size) { +			lprintf(LOG_ERR, "Sun OEM getfile write failed: %d", rsp->ccode); +			fclose(fp); +			return (-1); +		} + +		block_num++; +	} while (getfile_rsp->eof == 0); + +	fclose(fp); + +	return (0); +} + +/* + * Query BMC for capability/behavior. + */ + +#define CORE_TUNNEL_SUBCMD_GET_BEHAVIOR   15 +#define SUNOEM_BEHAVIORID_SIZE            32 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(push, 1) +#endif +typedef struct +{ +	unsigned char cmd_code; +	unsigned char behavior_id[SUNOEM_BEHAVIORID_SIZE]; +}__attribute__((packed)) getbehavior_req_t; + +typedef struct +{ +	unsigned char enabled; +}__attribute__((packed)) getbehavior_rsp_t; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(pop) +#endif + +static int +ipmi_sunoem_getbehavior(struct ipmi_intf * intf, int argc, char *argv[]) +{ +	struct ipmi_rq req; +	struct ipmi_rs *rsp; +	getbehavior_req_t getbehavior_req; +	getbehavior_rsp_t *getbehavior_rsp; +	supported_version_t supp_ver = IPMI_SUNOEM_GETBEHAVIOR_VERSION; + +	if (argc < 1) { +		return (-1); +	} + +	/*check if command is supported by this version of ilom*/ +	if (ipmi_sunoem_checkversion(intf, &supp_ver) < 0) { +		ipmi_sunoem_print_required_version(&supp_ver); +		return (-1); +	} + +	/* +	 * Behavior ID is < SUNOEM_BEHAVIORID_SIZE. +	 * Save 1 byte for null terminated string +	 */ +	if (strlen(argv[0]) >= SUNOEM_BEHAVIORID_SIZE) { +		lprintf(LOG_ERR, "Behavior ID >= %d characters", +		SUNOEM_BEHAVIORID_SIZE); +		return (-1); +	} + +	memset(&getbehavior_req, 0, sizeof(getbehavior_req)); +	strncpy(getbehavior_req.behavior_id, argv[0], SUNOEM_BEHAVIORID_SIZE - 1); + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_SUNOEM; +	req.msg.cmd = IPMI_SUNOEM_CORE_TUNNEL; +	req.msg.data = (uint8_t *) &getbehavior_req; +	req.msg.data_len = sizeof(getbehavior_req_t); +	getbehavior_req.cmd_code = CORE_TUNNEL_SUBCMD_GET_BEHAVIOR; + +	rsp = intf->sendrecv(intf, &req); + +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Sun OEM getbehavior command failed."); +		return (-1); +	} + +	if (rsp->ccode != 0) { +		lprintf(LOG_ERR, "Sun OEM getbehavior command failed: %d", rsp->ccode); +		return (-1); +	} + +	getbehavior_rsp = (getbehavior_rsp_t *) rsp->data; +	printf("ILOM behavior %s %s enabled\n", getbehavior_req.behavior_id, +			getbehavior_rsp->enabled ? "is" : "is not"); + +	return (0); +} + +int +ipmi_sunoem_main(struct ipmi_intf * intf, int argc, char ** argv) +{ +	int rc = 0; + +	if (argc == 0 || strcmp(argv[0], "help") == 0) { +		ipmi_sunoem_usage(); +		return (0); +	} /* if (argc == 0 || strcmp(argv[0], "help") == 0) */ + +	if (strcmp(argv[0], "cli") == 0) { +		rc = ipmi_sunoem_cli(intf, argc - 1, &argv[1]); +	} else if ((strcmp(argv[0], "led") == 0) || (strcmp(argv[0], "sbled") == 0)) { +		if (argc < 2) { +			ipmi_sunoem_usage(); +			return (-1); +		} + +		if (strcmp(argv[1], "get") == 0) { +			if (argc < 3) { +				char * arg[] = { "all" }; +				rc = ipmi_sunoem_led_get(intf, 1, arg); +			} else { +				rc = ipmi_sunoem_led_get(intf, argc - 2, &(argv[2])); +			} +		} else if (strcmp(argv[1], "set") == 0) { +			if (argc < 4) { +				ipmi_sunoem_usage(); +				return (-1); +			} +			rc = ipmi_sunoem_led_set(intf, argc - 2, &(argv[2])); +		} else { +			ipmi_sunoem_usage(); +			return (-1); +		} +	} else if (strcmp(argv[0], "sshkey") == 0) { +		uint8_t uid = 0; +		if (argc < 3) { +			ipmi_sunoem_usage(); +			return (-1); +		} +		rc = str2uchar(argv[2], &uid); +		if (rc == 0) { +			/* conversion should be OK. */ +		} else if (rc == 2) { +			lprintf(LOG_NOTICE, "Invalid interval given."); +			return (-1); +		} else { +			/* defaults to rc = 3 */ +			lprintf(LOG_NOTICE, "Given interval is too big."); +			return (-1); +		} + +		if (strcmp(argv[1], "del") == 0) { +			/* number of arguments, three, is already checked at this point */ +			rc = ipmi_sunoem_sshkey_del(intf, uid); +		} else if (strcmp(argv[1], "set") == 0) { +			if (argc < 4) { +				ipmi_sunoem_usage(); +				return (-1); +			} +			rc = ipmi_sunoem_sshkey_set(intf, uid, argv[3]); +		} else { +			ipmi_sunoem_usage(); +			return (-1); +		} +	} else if (strcmp(argv[0], "ping") == 0) { +		if (argc < 2) { +			ipmi_sunoem_usage(); +			return (-1); +		} +		rc = ipmi_sunoem_echo(intf, argc - 1, &(argv[1])); +	} else if (strcmp(argv[0], "version") == 0) { +		rc = ipmi_sunoem_version(intf); +	} else if (strcmp(argv[0], "nacname") == 0) { +		if (argc < 2) { +			ipmi_sunoem_usage(); +			return (-1); +		} +		rc = ipmi_sunoem_nacname(intf, argc - 1, &(argv[1])); +	} else if (strcmp(argv[0], "getval") == 0) { +		if (argc < 2) { +			ipmi_sunoem_usage(); +			return (-1); +		} +		rc = ipmi_sunoem_getval(intf, argc - 1, &(argv[1])); +	} else if (strcmp(argv[0], "setval") == 0) { +		if (argc < 3) { +			ipmi_sunoem_usage(); +			return (-1); +		} +		rc = ipmi_sunoem_setval(intf, argc - 1, &(argv[1])); +	} else if (strcmp(argv[0], "getfile") == 0) { +		if (argc < 3) { +			ipmi_sunoem_usage(); +			return (-1); +		} +		rc = ipmi_sunoem_getfile(intf, argc - 1, &(argv[1])); +	} else if (strcmp(argv[0], "getbehavior") == 0) { +		if (argc < 2) { +			ipmi_sunoem_usage(); +			return (-1); +		} +		rc = ipmi_sunoem_getbehavior(intf, argc - 1, &(argv[1])); +	} else { +		lprintf(LOG_ERR, "Invalid sunoem command: %s", argv[0]); +		return (-1); +	} /* if (strcmp(argv[0], "cli") == 0) */ + +	return (rc); +} | 
