diff options
Diffstat (limited to 'lib/ipmi_sol.c')
| -rw-r--r-- | lib/ipmi_sol.c | 2098 | 
1 files changed, 2098 insertions, 0 deletions
| diff --git a/lib/ipmi_sol.c b/lib/ipmi_sol.c new file mode 100644 index 0000000..a5b962f --- /dev/null +++ b/lib/ipmi_sol.c @@ -0,0 +1,2098 @@ +/*                                  + * Copyright (c) 2003 Sun Microsystems, Inc.  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 <string.h> +#include <strings.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/select.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> +#include <unistd.h> + +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif + +#if defined(HAVE_TERMIOS_H) +# include <termios.h> +#elif defined (HAVE_SYS_TERMIOS_H) +# include <sys/termios.h> +#endif + +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_sol.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/bswap.h> + + +#define SOL_PARAMETER_SET_IN_PROGRESS           0x00 +#define SOL_PARAMETER_SOL_ENABLE                0x01 +#define SOL_PARAMETER_SOL_AUTHENTICATION        0x02 +#define SOL_PARAMETER_CHARACTER_INTERVAL        0x03 +#define SOL_PARAMETER_SOL_RETRY                 0x04 +#define SOL_PARAMETER_SOL_NON_VOLATILE_BIT_RATE 0x05 +#define SOL_PARAMETER_SOL_VOLATILE_BIT_RATE     0x06 +#define SOL_PARAMETER_SOL_PAYLOAD_CHANNEL       0x07 +#define SOL_PARAMETER_SOL_PAYLOAD_PORT          0x08 + +#define MAX_SOL_RETRY 6 + +const struct valstr sol_parameter_vals[] = { +	{ SOL_PARAMETER_SET_IN_PROGRESS,           "Set In Progress (0)" }, +	{ SOL_PARAMETER_SOL_ENABLE,                "Enable (1)" }, +	{ SOL_PARAMETER_SOL_AUTHENTICATION,        "Authentication (2)" }, +	{ SOL_PARAMETER_CHARACTER_INTERVAL,        "Character Interval (3)" }, +	{ SOL_PARAMETER_SOL_RETRY,                 "Retry (4)" }, +	{ SOL_PARAMETER_SOL_NON_VOLATILE_BIT_RATE, "Nonvolatile Bitrate (5)" }, +	{ SOL_PARAMETER_SOL_VOLATILE_BIT_RATE,     "Volatile Bitrate (6)" }, +	{ SOL_PARAMETER_SOL_PAYLOAD_CHANNEL,       "Payload Channel (7)" }, +	{ SOL_PARAMETER_SOL_PAYLOAD_PORT,          "Payload Port (8)" }, +	{ 0x00, NULL }, +}; + + +static struct timeval _start_keepalive; +static struct termios _saved_tio; +static int            _in_raw_mode = 0; +static int            _disable_keepalive = 0; +static int            _use_sol_for_keepalive = 0; +static int            _keepalive_retries = 0; + +extern int verbose; + +/* + * ipmi_sol_payload_access + */ +int +ipmi_sol_payload_access(struct ipmi_intf * intf, uint8_t channel, +		uint8_t userid, int enable) +{ +	struct ipmi_rq req; +	struct ipmi_rs *rsp; +	int rc = (-1); +	uint8_t data[6]; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_APP; +	req.msg.cmd = IPMI_SET_USER_PAYLOAD_ACCESS; +	req.msg.data = data; +	req.msg.data_len = 6; + +	memset(data, 0, 6); +	/* channel */ +	data[0] = channel & 0xf; +	/* user id */ +	data[1] = userid & 0x3f; +	if (!enable) { +		/* disable */ +		data[1] |= 0x40; +	} +	/* payload 1 is SOL */ +	data[2] = 0x02; +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error %sabling SOL payload for user %d on channel %d", +				enable ? "en" : "dis", userid, channel); +		rc = (-1); +	} else if (rsp->ccode != 0) { +		lprintf(LOG_ERR, "Error %sabling SOL payload for user %d on channel %d: %s", +				enable ? "en" : "dis", userid, channel, +				val2str(rsp->ccode, completion_code_vals)); +		rc = (-1); +	} else { +		rc = 0; +	} +	return rc; +} + +int +ipmi_sol_payload_access_status(struct ipmi_intf * intf, +				uint8_t channel, +				uint8_t userid) +{ +	struct ipmi_rq req; +	struct ipmi_rs *rsp; +	uint8_t data[2]; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn    = IPMI_NETFN_APP; +	req.msg.cmd      = IPMI_GET_USER_PAYLOAD_ACCESS; +	req.msg.data     = data; +	req.msg.data_len = sizeof(data); + +	data[0] = channel & 0xf;	/* channel */ +	data[1] = userid & 0x3f;	/* user id */ +	rsp = intf->sendrecv(intf, &req); + +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error. No valid response received."); +		return -1; +	} + +	switch(rsp->ccode) { +		case 0x00: +			if (rsp->data_len != 4) { +				lprintf(LOG_ERR, "Error parsing SOL payload status for user %d on channel %d", +					userid, channel); +				return -1; +			} + +			printf("User %d on channel %d is %sabled\n", +				userid, channel, (rsp->data[0] & 0x02) ? "en":"dis"); +			return 0; + +		default: +			lprintf(LOG_ERR, "Error getting SOL payload status for user %d on channel %d: %s", +				userid, channel,  +				val2str(rsp->ccode, completion_code_vals)); +			return -1; +	} +} + + +/* + * ipmi_get_sol_info + */ +int +ipmi_get_sol_info( +				  struct ipmi_intf * intf, +				  uint8_t channel, +				  struct sol_config_parameters * params) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	uint8_t data[4]; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn    = IPMI_NETFN_TRANSPORT; +	req.msg.cmd      = IPMI_GET_SOL_CONFIG_PARAMETERS; +	req.msg.data_len = 4; +	req.msg.data     = data; + +	/* +	 * set in progress +	 */ +	memset(data, 0, sizeof(data)); +	data[0] = channel;                       /* channel number     */ +	data[1] = SOL_PARAMETER_SET_IN_PROGRESS; /* parameter selector */ +	data[2] = 0x00;                          /* set selector       */ +	data[3] = 0x00;                          /* block selector     */ + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", +				val2str(data[1], sol_parameter_vals)); +		return (-1); +	} + +	switch (rsp->ccode) { +		case 0x00: +			if (rsp->data_len == 2) { +				params->set_in_progress = rsp->data[1]; +			} else { +				lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " +						"for SOL parameter '%s'", +						rsp->data_len, +						val2str(data[1], sol_parameter_vals)); +			} +			break; +		case 0x80: +			lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", +					val2str(data[1], sol_parameter_vals)); +			break; +		default: +			lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", +				val2str(data[1], sol_parameter_vals), +				val2str(rsp->ccode, completion_code_vals)); +			return (-1); +	} + +	/* +	 * SOL enable +	 */ +	memset(data, 0, sizeof(data)); +	data[0] = channel;                  /* channel number     */ +	data[1] = SOL_PARAMETER_SOL_ENABLE; /* parameter selector */ +	data[2] = 0x00;                     /* set selector       */ +	data[3] = 0x00;                     /* block selector     */ + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", +				val2str(data[1], sol_parameter_vals)); +		return (-1); +	} + +	switch (rsp->ccode) { +		case 0x00: +			if (rsp->data_len == 2) { +				params->enabled = rsp->data[1]; +			} else { +				lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " +						"for SOL parameter '%s'", +						rsp->data_len, +						val2str(data[1], sol_parameter_vals)); +			} +			break; +		case 0x80: +			lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", +					val2str(data[1], sol_parameter_vals)); +			break; +		default: +			lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", +				val2str(data[1], sol_parameter_vals), +				val2str(rsp->ccode, completion_code_vals)); +			return (-1); +	} + +	/* +	 * SOL authentication +	 */ +	memset(data, 0, sizeof(data)); +	data[0] = channel;                          /* channel number     */ +	data[1] = SOL_PARAMETER_SOL_AUTHENTICATION; /* parameter selector */ +	data[2] = 0x00;                             /* set selector       */ +	data[3] = 0x00;                             /* block selector     */ + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", +				val2str(data[1], sol_parameter_vals)); +		return (-1); +	} + +	switch (rsp->ccode) { +		case 0x00: +			if (rsp->data_len == 2) { +				params->force_encryption     = ((rsp->data[1] & 0x80)? 1 : 0); +				params->force_authentication = ((rsp->data[1] & 0x40)? 1 : 0); +				params->privilege_level      = rsp->data[1] & 0x0F; +			} else { +				lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " +						"for SOL parameter '%s'", +						rsp->data_len, +						val2str(data[1], sol_parameter_vals)); +			} +			break; +		case 0x80: +			lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", +					val2str(data[1], sol_parameter_vals)); +			break; +		default: +			lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", +				val2str(data[1], sol_parameter_vals), +				val2str(rsp->ccode, completion_code_vals)); +			return (-1); +	} + +	/* +	 * Character accumulate interval and character send interval +	 */ +	memset(data, 0, sizeof(data)); +	data[0] = channel;                          /* channel number     */ +	data[1] = SOL_PARAMETER_CHARACTER_INTERVAL; /* parameter selector */ +	data[2] = 0x00;                             /* set selector       */ +	data[3] = 0x00;                             /* block selector     */ + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", +				val2str(data[1], sol_parameter_vals)); +		return (-1); +	} + +	switch (rsp->ccode) { +		case 0x00: +			if (rsp->data_len == 3) { +				params->character_accumulate_level = rsp->data[1]; +				params->character_send_threshold   = rsp->data[2]; +			} else { +				lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " +						"for SOL parameter '%s'", +						rsp->data_len, +						val2str(data[1], sol_parameter_vals)); +			} +			break; +		case 0x80: +			lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", +					val2str(data[1], sol_parameter_vals)); +			break; +		default: +			lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", +				val2str(data[1], sol_parameter_vals), +				val2str(rsp->ccode, completion_code_vals)); +			return (-1); +	} + +	/* +	 * SOL retry +	 */ +	memset(data, 0, sizeof(data)); +	data[0] = channel;                 /* channel number     */ +	data[1] = SOL_PARAMETER_SOL_RETRY; /* parameter selector */ +	data[2] = 0x00;                    /* set selector       */ +	data[3] = 0x00;                    /* block selector     */ + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", +				val2str(data[1], sol_parameter_vals)); +		return (-1); +	} + +	switch (rsp->ccode) { +		case 0x00: +			if (rsp->data_len == 3) { +				params->retry_count    = rsp->data[1]; +				params->retry_interval = rsp->data[2]; +			} else { +				lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " +						"for SOL parameter '%s'", +						rsp->data_len, +						val2str(data[1], sol_parameter_vals)); +			} +			break; +		case 0x80: +			lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", +					val2str(data[1], sol_parameter_vals)); +			break; +		default: +			lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", +				val2str(data[1], sol_parameter_vals), +				val2str(rsp->ccode, completion_code_vals)); +			return (-1); +	} + +	/* +	 * SOL non-volatile bit rate +	 */ +	memset(data, 0, sizeof(data)); +	data[0] = channel;                                 /* channel number     */ +	data[1] = SOL_PARAMETER_SOL_NON_VOLATILE_BIT_RATE; /* parameter selector */ +	data[2] = 0x00;                                    /* set selector       */ +	data[3] = 0x00;                                    /* block selector     */ + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", +				val2str(data[1], sol_parameter_vals)); +		return (-1); +	} + +	switch (rsp->ccode) { +		case 0x00: +			if (rsp->data_len == 2) { +				params->non_volatile_bit_rate = rsp->data[1] & 0x0F; +			} else { +				lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " +						"for SOL parameter '%s'", +						rsp->data_len, +						val2str(data[1], sol_parameter_vals)); +			} +			break; +		case 0x80: +			lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", +					val2str(data[1], sol_parameter_vals)); +			break; +		default: +			lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", +				val2str(data[1], sol_parameter_vals), +				val2str(rsp->ccode, completion_code_vals)); +			return (-1); +	} + +	/* +	 * SOL volatile bit rate +	 */ +	memset(data, 0, sizeof(data)); +	data[0] = channel;                             /* channel number     */ +	data[1] = SOL_PARAMETER_SOL_VOLATILE_BIT_RATE; /* parameter selector */ +	data[2] = 0x00;                                /* set selector       */ +	data[3] = 0x00;                                /* block selector     */ + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", +				val2str(data[1], sol_parameter_vals)); +		return (-1); +	} + +	switch (rsp->ccode) { +		case 0x00: +			if (rsp->data_len == 2) { +				params->volatile_bit_rate = rsp->data[1] & 0x0F; +			} else { +				lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " +						"for SOL parameter '%s'", +						rsp->data_len, +						val2str(data[1], sol_parameter_vals)); +			} +			break; +		case 0x80: +			lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported", +					val2str(data[1], sol_parameter_vals)); +			break; +		default: +			lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", +				val2str(data[1], sol_parameter_vals), +				val2str(rsp->ccode, completion_code_vals)); +			return (-1); +	} + +	/* +	 * SOL payload channel +	 */ +	memset(data, 0, sizeof(data)); +	data[0] = channel;                           /* channel number     */ +	data[1] = SOL_PARAMETER_SOL_PAYLOAD_CHANNEL; /* parameter selector */ +	data[2] = 0x00;                              /* set selector       */ +	data[3] = 0x00;                              /* block selector     */ + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", +				val2str(data[1], sol_parameter_vals)); +		return (-1); +	} + +	switch (rsp->ccode) { +		case 0x00: +			if (rsp->data_len == 2) { +				params->payload_channel = rsp->data[1]; +			} else { +				lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " +						"for SOL parameter '%s'", +						rsp->data_len, +						val2str(data[1], sol_parameter_vals)); +			} +			break; +		case 0x80: +			lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported - defaulting to 0x%02x", +					val2str(data[1], sol_parameter_vals), channel); +			params->payload_channel = channel; +			break; +		default: +			lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", +					val2str(data[1], sol_parameter_vals), +					val2str(rsp->ccode, completion_code_vals)); +			return (-1); +	} + +	/* +	 * SOL payload port +	 */ +	memset(data, 0, sizeof(data)); +	data[0] = channel;                        /* channel number     */ +	data[1] = SOL_PARAMETER_SOL_PAYLOAD_PORT; /* parameter selector */ +	data[2] = 0x00;                           /* set selector       */ +	data[3] = 0x00;                           /* block selector     */ + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error: No response requesting SOL parameter '%s'", +				val2str(data[1], sol_parameter_vals)); +		return (-1); +	} + +	switch (rsp->ccode) { +		case 0x00: +			if (rsp->data_len == 3) { +				params->payload_port = (rsp->data[1]) | (rsp->data[2] << 8); +			} else { +				lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " +						"for SOL parameter '%s'", +						rsp->data_len, +						val2str(data[1], sol_parameter_vals)); +			} +			break; +		case 0x80: +			if( intf->session != NULL ) { +				lprintf(LOG_ERR, "Info: SOL parameter '%s' not supported - defaulting to %d", +						val2str(data[1], sol_parameter_vals), intf->session->port); +				params->payload_port = intf->session->port; +			} else { +				lprintf(LOG_ERR, +						"Info: SOL parameter '%s' not supported - can't determine which " +						"payload port to use on NULL session", +						val2str(data[1], sol_parameter_vals)); +						return (-1);                +			} +			break; +		default: +			lprintf(LOG_ERR, "Error requesting SOL parameter '%s': %s", +				val2str(data[1], sol_parameter_vals), +				val2str(rsp->ccode, completion_code_vals)); +			return (-1); +	} + +	return 0; +} + + + +/* + * ipmi_print_sol_info + */ +static int +ipmi_print_sol_info(struct ipmi_intf * intf, uint8_t channel) +{ +	struct sol_config_parameters params = {0}; +	if (ipmi_get_sol_info(intf, channel, ¶ms)) +		return -1; + +	if (csv_output) +	{ +		printf("%s,", +			   val2str(params.set_in_progress & 0x03, +					   ipmi_set_in_progress_vals)); +		printf("%s,", params.enabled?"true": "false"); +		printf("%s,", params.force_encryption?"true":"false"); +		printf("%s,", params.force_encryption?"true":"false"); +		printf("%s,", +			   val2str(params.privilege_level, ipmi_privlvl_vals)); +		printf("%d,", params.character_accumulate_level * 5); +		printf("%d,", params.character_send_threshold); +		printf("%d,", params.retry_count); +		printf("%d,", params.retry_interval * 10); + +		printf("%s,", +			   val2str(params.volatile_bit_rate, ipmi_bit_rate_vals)); + +		printf("%s,", +			   val2str(params.non_volatile_bit_rate, ipmi_bit_rate_vals)); + +		printf("%d,", params.payload_channel); +		printf("%d\n", params.payload_port); +	} +	else +	{ +		printf("Set in progress                 : %s\n", +			   val2str(params.set_in_progress & 0x03, +					   ipmi_set_in_progress_vals)); +		printf("Enabled                         : %s\n", +			   params.enabled?"true": "false"); +		printf("Force Encryption                : %s\n", +			   params.force_encryption?"true":"false"); +		printf("Force Authentication            : %s\n", +			   params.force_authentication?"true":"false"); +		printf("Privilege Level                 : %s\n", +			   val2str(params.privilege_level, ipmi_privlvl_vals)); +		printf("Character Accumulate Level (ms) : %d\n", +			   params.character_accumulate_level * 5); +		printf("Character Send Threshold        : %d\n", +			   params.character_send_threshold); +		printf("Retry Count                     : %d\n", +			   params.retry_count); +		printf("Retry Interval (ms)             : %d\n", +			   params.retry_interval * 10); + +		printf("Volatile Bit Rate (kbps)        : %s\n", +			   val2str(params.volatile_bit_rate, ipmi_bit_rate_vals)); + +		printf("Non-Volatile Bit Rate (kbps)    : %s\n", +			   val2str(params.non_volatile_bit_rate, ipmi_bit_rate_vals)); + +		printf("Payload Channel                 : %d (0x%02x)\n", +			   params.payload_channel, params.payload_channel); +		printf("Payload Port                    : %d\n", +			   params.payload_port); +	} + +	return 0; +} + + + +/* + * Small function to validate that user-supplied SOL + * configuration parameter values we store in uint8_t + * data type falls within valid range.  With minval + * and maxval parameters we can use the same function + * to validate parameters that have different ranges + * of values. + * + * function will return -1 if value is not valid, or + * will return 0 if valid. + */ +int ipmi_sol_set_param_isvalid_uint8_t( const char *strval, +					const char *name, +					int base, +					uint8_t minval, +					uint8_t maxval, +					uint8_t *out_value) +{ +	if (str2uchar(strval, out_value) != 0 || (*out_value < minval) +			|| (*out_value > maxval)) { +		lprintf(LOG_ERR, "Invalid value %s for parameter %s", +			strval, name); +		lprintf(LOG_ERR, "Valid values are %d-%d", minval, maxval); +		return -1; +	} +	return 0; +} + + +/* + * ipmi_sol_set_param + * + * Set the specified Serial Over LAN value to the specified + * value + * + * return 0 on success, + *        -1 on failure + */ +static int +ipmi_sol_set_param(struct ipmi_intf * intf, +		   uint8_t            channel, +		   const char       * param, +		   const char       * value, +		   uint8_t            guarded) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq   req; +	uint8_t          data[4]; +	int              bGuarded = guarded; /* Use set-in-progress indicator? */ + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn    = IPMI_NETFN_TRANSPORT;           /* 0x0c */ +	req.msg.cmd      = IPMI_SET_SOL_CONFIG_PARAMETERS; /* 0x21 */ +	req.msg.data     = data; + +	data[0] = channel; + +	/* +	 * set-in-progress +	 */ +	if (! strcmp(param, "set-in-progress")) +	{ +		bGuarded = 0; /* We _ARE_ the set-in-progress indicator */ +		req.msg.data_len = 3; +		data[1] = SOL_PARAMETER_SET_IN_PROGRESS; + +		if (! strcmp(value, "set-complete")) +			data[2] = 0x00; +		else if (! strcmp(value, "set-in-progress")) +			data[2] = 0x01; +		else if (! strcmp(value, "commit-write")) +			data[2] = 0x02; +		else +		{ +			lprintf(LOG_ERR, "Invalid value %s for parameter %s", +				   value, param); +			lprintf(LOG_ERR, "Valid values are set-complete, set-in-progress " +				   "and commit-write"); +			return -1; +		} +	} + + +	/* +	 * enabled +	 */ +	else if (! strcmp(param, "enabled")) +	{ +		req.msg.data_len = 3; +		data[1] = SOL_PARAMETER_SOL_ENABLE; + +		if (! strcmp(value, "true")) +			data[2] = 0x01; +		else if (! strcmp(value, "false")) +			data[2] = 0x00; +		else +		{ +			lprintf(LOG_ERR, "Invalid value %s for parameter %s", +				   value, param); +			lprintf(LOG_ERR, "Valid values are true and false"); +			return -1; +		} +	} + + +	/* +	 * force-payload-encryption  +	 */ +	else if (! strcmp(param, "force-encryption")) +	{ +		struct sol_config_parameters params; + +		req.msg.data_len = 3; +		data[1] = SOL_PARAMETER_SOL_AUTHENTICATION; + +		if (! strcmp(value, "true")) +			data[2] = 0x80; +		else if (! strcmp(value, "false")) +			data[2] = 0x00; +		else +		{ +			lprintf(LOG_ERR, "Invalid value %s for parameter %s", +				   value, param); +			lprintf(LOG_ERR, "Valid values are true and false"); +			return -1; +		} + + +		/* We need other values to complete the request */ +		if (ipmi_get_sol_info(intf, channel, ¶ms)) +		{ +			lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", +				   param); +			return -1; +		} + +		data[2] |= params.force_authentication? 0x40 : 0x00; +		data[2] |= params.privilege_level; +	} + + +	/* +	 * force-payload-authentication +	 */ +	else if (! strcmp(param, "force-authentication")) +	{ +		struct sol_config_parameters params; + +		req.msg.data_len = 3; +		data[1] = SOL_PARAMETER_SOL_AUTHENTICATION; + +		if (! strcmp(value, "true")) +			data[2] = 0x40; +		else if (! strcmp(value, "false")) +			data[2] = 0x00; +		else +		{ +			lprintf(LOG_ERR, "Invalid value %s for parameter %s", +				   value, param); +			lprintf(LOG_ERR, "Valid values are true and false"); +			return -1; +		} + + +		/* We need other values to complete the request */ +		if (ipmi_get_sol_info(intf, channel, ¶ms)) +		{ +			lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", +				   param); +			return -1; +		} + +		data[2] |= params.force_encryption? 0x80 : 0x00; +		data[2] |= params.privilege_level; +	} + + +	/* +	 * privilege-level +	 */ +	else if (! strcmp(param, "privilege-level")) +	{ +		struct sol_config_parameters params; + +		req.msg.data_len = 3; +		data[1] = SOL_PARAMETER_SOL_AUTHENTICATION; + +		if (! strcmp(value, "user")) +			data[2] = 0x02; +		else if (! strcmp(value, "operator")) +			data[2] = 0x03; +		else if (! strcmp(value, "admin")) +			data[2] = 0x04; +		else if (! strcmp(value, "oem")) +			data[2] = 0x05; +		else +		{ +			lprintf(LOG_ERR, "Invalid value %s for parameter %s", +				   value, param); +			lprintf(LOG_ERR, "Valid values are user, operator, admin, and oem"); +			return -1; +		} + + +		/* We need other values to complete the request */ +		if (ipmi_get_sol_info(intf, channel, ¶ms)) +		{ +			lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", +				   param); +			return -1; +		} + +		data[2] |= params.force_encryption?     0x80 : 0x00; +		data[2] |= params.force_authentication? 0x40 : 0x00; +	} + + +	/* +	 * character-accumulate-level +	 */ +	else if (! strcmp(param, "character-accumulate-level")) +	{ +		struct sol_config_parameters params; + +		req.msg.data_len = 4; +		data[1] = SOL_PARAMETER_CHARACTER_INTERVAL; + +		/* validate user-supplied input */ +		if (ipmi_sol_set_param_isvalid_uint8_t(value, param, 0, 1, 255, &data[2])) +			return -1; + +		/* We need other values to complete the request */ +		if (ipmi_get_sol_info(intf, channel, ¶ms)) +		{ +			lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", +				   param); +			return -1; +		} + +		data[3] = params.character_send_threshold; +	} + + +	/* +	 * character-send-threshold +	 */ +	else if (! strcmp(param, "character-send-threshold")) +	{ +		struct sol_config_parameters params; + +		req.msg.data_len = 4; +		data[1] = SOL_PARAMETER_CHARACTER_INTERVAL; + +		/* validate user-supplied input */ +		if (ipmi_sol_set_param_isvalid_uint8_t(value, param, 0, 0, 255, &data[3])) +			return -1; + +		/* We need other values to complete the request */ +		if (ipmi_get_sol_info(intf, channel, ¶ms)) +		{ +			lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", +				   param); +			return -1; +		} + +		data[2] = params.character_accumulate_level; +	} + + +	/* +	 * retry-count +	 */ +	else if (! strcmp(param, "retry-count")) +	{ +		struct sol_config_parameters params; + +		req.msg.data_len = 4; +		data[1] = SOL_PARAMETER_SOL_RETRY; + +		/* validate user input, 7 is max value */ +		if (ipmi_sol_set_param_isvalid_uint8_t(value, param, 0, 0, 7, &data[2])) +			return -1; + +		/* We need other values to complete the request */ +		if (ipmi_get_sol_info(intf, channel, ¶ms)) +		{ +			lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", +				   param); +			return -1; +		} + +		data[3] = params.retry_interval; +	} + + +	/* +	 * retry-interval +	 */ +	else if (! strcmp(param, "retry-interval")) +	{ +		struct sol_config_parameters params; + +		req.msg.data_len = 4; +		data[1] = SOL_PARAMETER_SOL_RETRY; + +		/* validate user-supplied input */ +		if (ipmi_sol_set_param_isvalid_uint8_t(value, param, 0, 0, 255, &data[3])) +			return -1; + +		/* We need other values to complete the request */ +		if (ipmi_get_sol_info(intf, channel, ¶ms)) +		{ +			lprintf(LOG_ERR, "Error fetching SOL parameters for %s update", +				   param); +			return -1; +		} + +		data[2] = params.retry_count; +	} + + +	/* +	 * non-volatile-bit-rate +	 */ +	else if (! strcmp(param, "non-volatile-bit-rate")) +	{ +		req.msg.data_len = 3; +		data[1] = SOL_PARAMETER_SOL_NON_VOLATILE_BIT_RATE; + +		if (!strcmp(value, "serial")) +		{ +			data[2] = 0x00; +		} +		else if (!strcmp(value, "9.6")) +		{ +			data[2] = 0x06; +		} +		else if (!strcmp(value, "19.2")) +		{ +			data[2] = 0x07; +		} +		else if (!strcmp(value, "38.4")) +		{ +			data[2] = 0x08; +		} +		else if (!strcmp(value, "57.6")) +		{ +			data[2] = 0x09; +		} +		else if (!strcmp(value, "115.2")) +		{ +			data[2] = 0x0A; +		} +		else +		{ +			lprintf(LOG_ERR, "Invalid value \"%s\" for parameter \"%s\"", +				   value, +				   param); +			lprintf(LOG_ERR, "Valid values are serial, 9.6 19.2, 38.4, 57.6 and 115.2"); +			return -1; +		} +	} + + +	/* +	 * volatile-bit-rate +	 */ +	else if (! strcmp(param, "volatile-bit-rate")) +	{ +		req.msg.data_len = 3; +		data[1] = SOL_PARAMETER_SOL_VOLATILE_BIT_RATE; + +		if (!strcmp(value, "serial")) +		{ +			data[2] = 0x00; +		} +		else if (!strcmp(value, "9.6")) +		{ +			data[2] = 0x06; +		} +		else if (!strcmp(value, "19.2")) +		{ +			data[2] = 0x07; +		} +		else if (!strcmp(value, "38.4")) +		{ +			data[2] = 0x08; +		} +		else if (!strcmp(value, "57.6")) +		{ +			data[2] = 0x09; +		} +		else if (!strcmp(value, "115.2")) +		{ +			data[2] = 0x0A; +		} +		else +		{ +			lprintf(LOG_ERR, "Invalid value \"%s\" for parameter \"%s\"", +				   value, +				   param); +			lprintf(LOG_ERR, "Valid values are serial, 9.6 19.2, 38.4, 57.6 and 115.2"); +			return -1; +		} +	} +	else +	{ +		lprintf(LOG_ERR, "Error: invalid SOL parameter %s", param); +		return -1; +	} + + +	/* +	 * Execute the request +	 */ +	if (bGuarded && +		(ipmi_sol_set_param(intf, +				    channel, +				    "set-in-progress", +				    "set-in-progress", +				    bGuarded))) +	{ +		lprintf(LOG_ERR, "Error: set of parameter \"%s\" failed", param); +		return -1; +	} + + +	/* The command proper */ +	rsp = intf->sendrecv(intf, &req); + +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Error setting SOL parameter '%s'", param); +		return -1; +	} + +	if (!(!strncmp(param, "set-in-progress", 15) && !strncmp(value, "commit-write", 12)) && +	    rsp->ccode > 0) { +		switch (rsp->ccode) { +		case 0x80: +			lprintf(LOG_ERR, "Error setting SOL parameter '%s': " +				"Parameter not supported", param); +			break; +		case 0x81: +			lprintf(LOG_ERR, "Error setting SOL parameter '%s': " +				"Attempt to set set-in-progress when not in set-complete state", +				param); +			break; +		case 0x82: +			lprintf(LOG_ERR, "Error setting SOL parameter '%s': " +				"Attempt to write read-only parameter", param); +			break; +		case 0x83: +			lprintf(LOG_ERR, "Error setting SOL parameter '%s': " +				"Attempt to read write-only parameter", param); +			break; +		default: +			lprintf(LOG_ERR, "Error setting SOL parameter '%s' to '%s': %s", +				param, value, val2str(rsp->ccode, completion_code_vals)); +			break; +		} + +		if (bGuarded && +			(ipmi_sol_set_param(intf, +					    channel, +					    "set-in-progress", +					    "set-complete", +					    bGuarded))) +		{ +			lprintf(LOG_ERR, "Error could not set \"set-in-progress\" " +				   "to \"set-complete\""); +		} + +		return -1; +	} + + +	/* +	 * The commit write could very well fail, but that's ok. +	 * It may not be implemented. +	 */ +	if (bGuarded) +		ipmi_sol_set_param(intf, +				   channel, +				   "set-in-progress", +				   "commit-write", +				   bGuarded); + + +	if (bGuarded && + 		ipmi_sol_set_param(intf, +				   channel, +				   "set-in-progress", +				   "set-complete", +				   bGuarded)) +	{ +		lprintf(LOG_ERR, "Error could not set \"set-in-progress\" " +			   "to \"set-complete\""); +		return -1; +	} + +	return 0; +} + + + +void +leave_raw_mode(void) +{ +	if (!_in_raw_mode) +		return; +	if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) +		perror("tcsetattr"); +	else +		_in_raw_mode = 0; +} + + + +void +enter_raw_mode(void) +{ +	struct termios tio; +	if (tcgetattr(fileno(stdin), &tio) == -1) { +		perror("tcgetattr"); +		return; +	} +	_saved_tio = tio; +	tio.c_iflag |= IGNPAR; +	tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); +	tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); +	//	#ifdef IEXTEN +	tio.c_lflag &= ~IEXTEN; +	//	#endif +	tio.c_oflag &= ~OPOST; +	tio.c_cc[VMIN] = 1; +	tio.c_cc[VTIME] = 0; +	if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1) +		perror("tcsetattr"); +	else +		_in_raw_mode = 1; +} + + +static void +sendBreak(struct ipmi_intf * intf) +{ +	struct ipmi_v2_payload  v2_payload; + +	memset(&v2_payload, 0, sizeof(v2_payload)); + +	v2_payload.payload.sol_packet.character_count = 0; +	v2_payload.payload.sol_packet.generate_break  = 1; + +	intf->send_sol(intf, &v2_payload); +} + + + +/* + * suspendSelf + * + * Put ourself in the background + * + * param bRestoreTty specifies whether we will put our self back + *       in raw mode when we resume + */ +static void +suspendSelf(int bRestoreTty) +{ +	leave_raw_mode(); +	kill(getpid(), SIGTSTP); + +	if (bRestoreTty) +		enter_raw_mode(); +} + + + +/* + * printSolEscapeSequences + * + * Send some useful documentation to the user + */ +static void +printSolEscapeSequences(struct ipmi_intf * intf) +{ +	printf( +		   "%c?\n\ +	Supported escape sequences:\n\ +	%c.  - terminate connection\n\ +	%c^Z - suspend ipmitool\n\ +	%c^X - suspend ipmitool, but don't restore tty on restart\n\ +	%cB  - send break\n\ +	%c?  - this message\n\ +	%c%c  - send the escape character by typing it twice\n\ +	(Note that escapes are only recognized immediately after newline.)\n", +		   intf->session->sol_escape_char, +		   intf->session->sol_escape_char, +		   intf->session->sol_escape_char, +		   intf->session->sol_escape_char, +		   intf->session->sol_escape_char, +		   intf->session->sol_escape_char, +		   intf->session->sol_escape_char, +		   intf->session->sol_escape_char); +} + + + +/* + * output + * + * Send the specified data to stdout + */ +static void +output(struct ipmi_rs * rsp) +{ +	/* Add checks to make sure it is actually SOL data, in general I see +	 * outside code mostly trying to guard against this happening, but +	 * some places fail to do so, so I do so here to make sure nothing gets +	 * through.  If non-sol data comes through here, there is probably  +	 * a packet that won't get processed somewhere else, but the alternative +	 * of outputting corrupt data is worse.  Generally I see the get device +	 * id response make it here somehow.  I assume it is a heartbeat and the +	 * other code will retry if it cares about the response and misses it. +	 */ +	if (rsp && +	    (rsp->session.authtype    == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && +	    (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)) +	{ +		int i; + +		for (i = 0; i < rsp->data_len; ++i) +			putc(rsp->data[i], stdout); + +		fflush(stdout); +	} +} + + + +/* + * ipmi_sol_deactivate + */ +static int +ipmi_sol_deactivate(struct ipmi_intf * intf, int instance) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq   req; +	uint8_t          data[6]; + +	if ((instance <= 0) || (instance > 15)) { +		lprintf(LOG_ERR, "Error: Instance must range from 1 to 15"); +		return -1; +	} + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn    = IPMI_NETFN_APP; +	req.msg.cmd      = IPMI_DEACTIVATE_PAYLOAD; +	req.msg.data_len = 6; +	req.msg.data     = data; + +	bzero(data, sizeof(data)); +	data[0] = IPMI_PAYLOAD_TYPE_SOL;  /* payload type      */ +	data[1] = instance;               /* payload instance. */ + +	/* Lots of important data */ +	data[2] = 0; +	data[3] = 0; +	data[4] = 0; +	data[5] = 0; + +	rsp = intf->sendrecv(intf, &req); + +	if (NULL != rsp) { +		switch (rsp->ccode) { +			case 0x00: +				return 0; +			case 0x80: +				lprintf(LOG_ERR, "Info: SOL payload already de-activated"); +				break; +			case 0x81: +				lprintf(LOG_ERR, "Info: SOL payload type disabled"); +				break; +			default: +				lprintf(LOG_ERR, "Error de-activating SOL payload: %s", +					val2str(rsp->ccode, completion_code_vals)); +				break; +		} +	} else { +		lprintf(LOG_ERR, "Error: No response de-activating SOL payload"); +	} + +	return -1; +} + + + +/* + * processSolUserInput + * + * Act on user input into the SOL session.  The only reason this + * is complicated is that we have to process escape sequences. + * + * return   0 on success + *          1 if we should exit + *        < 0 on error (BMC probably closed the session) + */ +static int +processSolUserInput( +					struct ipmi_intf * intf, +					uint8_t    * input, +					uint16_t   buffer_length) +{ +	static int escape_pending = 0; +	static int last_was_cr    = 1; +	struct ipmi_v2_payload v2_payload; +	int  length               = 0; +	int  retval               = 0; +	char ch; +	int  i; + +	memset(&v2_payload, 0, sizeof(v2_payload)); + +	/* +	 * Our first order of business is to check the input for escape +	 * sequences to act on. +	 */ +	for (i = 0; i < buffer_length; ++i) +	{ +		ch = input[i]; + +		if (escape_pending){ +			escape_pending = 0; + +			/* +			 * Process a possible escape sequence. +			 */ +			switch (ch) { +			case '.': +				printf("%c. [terminated ipmitool]\n", +				       intf->session->sol_escape_char); +				retval = 1; +				break; + +			case 'Z' - 64: +				printf("%c^Z [suspend ipmitool]\n", +				       intf->session->sol_escape_char); +				suspendSelf(1); /* Restore tty back to raw */ +				continue; + +			case 'X' - 64: +				printf("%c^Z [suspend ipmitool]\n", +				       intf->session->sol_escape_char); +				suspendSelf(0); /* Don't restore to raw mode */ +				continue; + +			case 'B': +				printf("%cB [send break]\n", +				       intf->session->sol_escape_char); +				sendBreak(intf); +				continue; + +			case '?': +				printSolEscapeSequences(intf); +				continue; + +			default: +				if (ch != intf->session->sol_escape_char) +					v2_payload.payload.sol_packet.data[length++] = +						intf->session->sol_escape_char; +				v2_payload.payload.sol_packet.data[length++] = ch; +			} +		} + +		else +		{ +			if (last_was_cr && (ch == intf->session->sol_escape_char)) { +				escape_pending = 1; +				continue; +			} + +			v2_payload.payload.sol_packet.data[length++] =	ch; +		} + + +		/* +		 * Normal character.  Record whether it was a newline. +		 */ +		last_was_cr = (ch == '\r' || ch == '\n'); +	} + + +	/* +	 * If there is anything left to process we dispatch it to the BMC, +	 * send intf->session->sol_data.max_outbound_payload_size bytes +	 * at a time. +	 */ +	if (length) +	{ +		struct ipmi_rs * rsp = NULL; +		int try = 0; + +		while (try < intf->session->retry) { + +			v2_payload.payload.sol_packet.character_count = length; + +			rsp = intf->send_sol(intf, &v2_payload); + +			if (rsp) +			{ +				break; +			} + +			usleep(5000); +			try++; +		} + +		if (! rsp) +		{ +			lprintf(LOG_ERR, "Error sending SOL data: FAIL"); +			retval = -1; +		} + +		/* If the sequence number is set we know we have new data */ +		if (retval == 0) +			if ((rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && +			    (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)        && +			    (rsp->payload.sol_packet.packet_sequence_number)) +				output(rsp); +	} + +	return retval; +} + +static int +ipmi_sol_keepalive_using_sol(struct ipmi_intf * intf) +{ +	struct ipmi_v2_payload v2_payload; +   struct ipmi_rs * rsp = NULL; +	struct timeval end; + +	int ret = 0; + +	if (_disable_keepalive) +		return 0; + +	gettimeofday(&end, 0); + +	if (end.tv_sec - _start_keepalive.tv_sec > SOL_KEEPALIVE_TIMEOUT) { +	   memset(&v2_payload, 0, sizeof(v2_payload)); + +      v2_payload.payload.sol_packet.character_count = 0; + +      rsp = intf->send_sol(intf, &v2_payload); + +		gettimeofday(&_start_keepalive, 0); +   } +	return ret; +} + +static int +ipmi_sol_keepalive_using_getdeviceid(struct ipmi_intf * intf) +{ +	struct timeval  end; +	static int ret = 0; + +	if (_disable_keepalive) +		return 0; + +	gettimeofday(&end, 0); + +	if (end.tv_sec - _start_keepalive.tv_sec > SOL_KEEPALIVE_TIMEOUT) { +	   ret = intf->keepalive(intf); +	   if ( (ret!=0) && (_keepalive_retries < SOL_KEEPALIVE_RETRIES) ) { +         ret = 0; +         _keepalive_retries++; +	   } +	   else if ((ret==0) && (_keepalive_retries > 0)) +         _keepalive_retries = 0; +		gettimeofday(&_start_keepalive, 0); +   } +	return ret; +} + + + +/* + * ipmi_sol_red_pill + */ +static int +ipmi_sol_red_pill(struct ipmi_intf * intf, int instance) +{ +	char   * buffer; +	int    numRead; +	int    bShouldExit       = 0; +	int    bBmcClosedSession = 0; +	fd_set read_fds; +	struct timeval tv; +	int    retval; +	int    buffer_size = intf->session->sol_data.max_inbound_payload_size; +	int    keepAliveRet = 0; +	int    retrySol = 0; + +	/* Subtract SOL header from max_inbound_payload_size */ +	if (buffer_size > 4) +		buffer_size -= 4; + +	buffer = (char*)malloc(buffer_size); +	if (buffer == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure");  +		return -1; +	} + +	/* Initialize keepalive start time */ +	gettimeofday(&_start_keepalive, 0); + +	enter_raw_mode(); + +	while (! bShouldExit) +	{ +		FD_ZERO(&read_fds); +		FD_SET(0, &read_fds); +		FD_SET(intf->fd, &read_fds); + +		if (!ipmi_oem_active(intf,"i82571spt")) +		{ +			/* Send periodic keepalive packet */ +			if(_use_sol_for_keepalive == 0) +			{ +				keepAliveRet = ipmi_sol_keepalive_using_getdeviceid(intf); +			} +			else +			{ +				keepAliveRet = ipmi_sol_keepalive_using_sol(intf); +			} +		 +			if (keepAliveRet != 0) +			{ +				/* +				 * Retrying the keep Alive before declaring a communication +				 * lost state with the IPMC. Helpful when the payload is +				 * reset and brings down the connection temporarily. Otherwise, +				 * if we send getDevice Id to check the status of IPMC during +				 * this down time when the connection is restarting, SOL will +				 * exit even though the IPMC is available and the session is open. +				 */ +				if (retrySol == MAX_SOL_RETRY) +				{ +					/* no response to Get Device ID keepalive message */ +					bShouldExit = 1; +					continue; +				} +				else +				{ +					retrySol++; +				} +			} +			else +			{ +				/* if the keep Alive is successful reset retries to zero */ +				retrySol = 0; +			} +		} /* !oem="i82571spt" */ +		/* Wait up to half a second */ +		tv.tv_sec =  0; +		tv.tv_usec = 500000; + +		retval = select(intf->fd + 1, &read_fds, NULL, NULL, &tv); + +		if (retval) +		{ +			if (retval == -1) +			{ +				/* ERROR */ +				perror("select"); +				return -1; +			} + + +			/* +			 * Process input from the user +			 */ +			if (FD_ISSET(0, &read_fds)) +	 		{ +				memset(buffer, 0, buffer_size); +				numRead = read(fileno(stdin), +							   buffer, +							   buffer_size); + +				if (numRead > 0) +				{ +					int rc = processSolUserInput(intf, (uint8_t *)buffer, numRead); + +					if (rc) +					{ +						if (rc < 0) +							bShouldExit = bBmcClosedSession = 1; +						else +							bShouldExit = 1; +					} +				} +				else +				{ +					bShouldExit = 1; +				} +			} + + +			/* +			 * Process input from the BMC +			 */ +			else if (FD_ISSET(intf->fd, &read_fds)) +			{ +				struct ipmi_rs * rs =intf->recv_sol(intf); +				if (! rs) +				{ +					bShouldExit = bBmcClosedSession = 1; +				} +				else +				{ +					output(rs); +				} + 			} + + +			/* +			 * ERROR in select +			 */ + 			else +			{ +				lprintf(LOG_ERR, "Error: Select returned with nothing to read"); +				bShouldExit = 1; +			} +		} +	} + +	leave_raw_mode(); + +	if (keepAliveRet != 0) +	{ +		lprintf(LOG_ERR, "Error: No response to keepalive - Terminating session"); +		/* attempt to clean up anyway */ +		ipmi_sol_deactivate(intf, instance); +		exit(1); +	} + +	if (bBmcClosedSession) +	{ +		lprintf(LOG_ERR, "SOL session closed by BMC"); +		exit(1); +	} +	else +		ipmi_sol_deactivate(intf, instance); + +	return 0; +} + + + + +/* + * ipmi_sol_activate + */ +static int +ipmi_sol_activate(struct ipmi_intf * intf, int looptest, int interval, +		int instance) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq   req; +	struct activate_payload_rsp ap_rsp; +	uint8_t    data[6]; +	uint8_t    bSolEncryption     = 1; +	uint8_t    bSolAuthentication = 1; + +	/* +	 * This command is only available over RMCP+ (the lanplus +	 * interface). +	 */ +	if (strncmp(intf->name, "lanplus", 7) != 0) +	{ +		lprintf(LOG_ERR, "Error: This command is only available over the " +			   "lanplus interface"); +		return -1; +	} + +	if ((instance <= 0) || (instance > 15)) { +		lprintf(LOG_ERR, "Error: Instance must range from 1 to 15"); +		return -1; +	} + + +	/* +	 * Setup a callback so that the lanplus processing knows what +	 * to do with packets that come unexpectedly (while waiting for +	 * an ACK, perhaps. +	 */ +	intf->session->sol_data.sol_input_handler = output; + + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn    = IPMI_NETFN_APP; +	req.msg.cmd      = IPMI_ACTIVATE_PAYLOAD; +	req.msg.data_len = 6; +	req.msg.data     = data; + +	data[0] = IPMI_PAYLOAD_TYPE_SOL;  /* payload type     */ +	data[1] = instance;               /* payload instance */ + +	/* Lots of important data.  Most is default */ +	data[2]  = bSolEncryption?     0x80 : 0; +	data[2] |= bSolAuthentication? 0x40 : 0; +	data[2] |= IPMI_SOL_SERIAL_ALERT_MASK_DEFERRED; + +	if (ipmi_oem_active(intf, "intelplus")) { +		data[2] |= IPMI_SOL_BMC_ASSERTS_CTS_MASK_TRUE; +	} else if (ipmi_oem_active(intf, "i82571spt")) { +		/* +		 * A quote from Intel: "Engineering believes the problem +		 * lies within the Auxiliary data being sent with the +		 * 'Activate Payload' command from IPMITool.  IPMITool +		 * sends a C6h which sets some bits having to do with +		 * encryption and some behavior dealing with CTS DCD/DSR. +		 * I recommend that the customer modify this request +		 * to send 08h instead. This is what our internal utility +		 * sends and it works without issue. I will work with +		 * engineering to ensure the settings that IPMITool uses +		 * (C6h) are supported in the future. +		 */ +		data[2] = 0x08; +	} else { +		data[2] |= IPMI_SOL_BMC_ASSERTS_CTS_MASK_FALSE; +	} + +	data[3] = 0x00; /* reserved */ +	data[4] = 0x00; /* reserved */ +	data[5] = 0x00; /* reserved */ + +	rsp = intf->sendrecv(intf, &req); + +	if (NULL != rsp) { +		switch (rsp->ccode) { +			case 0x00:  +				if (rsp->data_len == 12) { +					break; +				} else { +					lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " +						   "in payload activation response", +						   rsp->data_len); +					return -1; +				} +				break; +			case 0x80: +				lprintf(LOG_ERR, "Info: SOL payload already active on another session"); +				return -1; +			case 0x81: +				lprintf(LOG_ERR, "Info: SOL payload disabled"); +				return -1; +			case 0x82: +				lprintf(LOG_ERR, "Info: SOL payload activation limit reached"); +				return -1; +			case 0x83: +				lprintf(LOG_ERR, "Info: cannot activate SOL payload with encryption"); +				return -1; +			case 0x84: +				lprintf(LOG_ERR, "Info: cannot activate SOL payload without encryption"); +				return -1; +			default: +				lprintf(LOG_ERR, "Error activating SOL payload: %s", +					val2str(rsp->ccode, completion_code_vals)); +				return -1; +		} +	} else { +		lprintf(LOG_ERR, "Error: No response activating SOL payload"); +		return -1; +	} + + +	memcpy(&ap_rsp, rsp->data, sizeof(struct activate_payload_rsp)); + +	intf->session->sol_data.max_inbound_payload_size = +		(ap_rsp.inbound_payload_size[1] << 8) | +		ap_rsp.inbound_payload_size[0]; + +	intf->session->sol_data.max_outbound_payload_size = +		(ap_rsp.outbound_payload_size[1] << 8) | +		ap_rsp.outbound_payload_size[0]; + +	intf->session->sol_data.port = +		(ap_rsp.payload_udp_port[1] << 8) | +		ap_rsp.payload_udp_port[0]; + +	intf->session->timeout = 1; + + +	/* NOTE: the spec does allow for SOL traffic to be sent on +	 * a different port.  we do not yet support that feature. */ +	if (intf->session->sol_data.port != intf->session->port) +	{ +		/* try byteswapping port in case BMC sent it incorrectly */ +		uint16_t portswap = BSWAP_16(intf->session->sol_data.port); + +		if (portswap == intf->session->port) { +			intf->session->sol_data.port = portswap; +		} +		else { +			lprintf(LOG_ERR, "Error: BMC requests SOL session on different port"); +			return -1; +		} +	} + +	printf("[SOL Session operational.  Use %c? for help]\n", +	       intf->session->sol_escape_char); + +	if(looptest == 1) +	{ +		ipmi_sol_deactivate(intf, instance); +		usleep(interval*1000); +		return 0; +	} + +	/* +	 * At this point we are good to go with our SOL session.  We +	 * need to listen to +	 * 1) STDIN for user input +	 * 2) The FD for incoming SOL packets +	 */ +	if (ipmi_sol_red_pill(intf, instance)) +	{ +		lprintf(LOG_ERR, "Error in SOL session"); +		return -1; +	} + +	return 0; +} + + + +/* + * print_sol_usage + */ +static void +print_sol_usage(void) +{ +	lprintf(LOG_NOTICE, "SOL Commands: info [<channel number>]"); +	lprintf(LOG_NOTICE, "              set <parameter> <value> [channel]"); +	lprintf(LOG_NOTICE, "              payload <enable|disable|status> [channel] [userid]"); +	lprintf(LOG_NOTICE, "              activate [<usesolkeepalive|nokeepalive>] [instance=<number>]"); +	lprintf(LOG_NOTICE, "              deactivate [instance=<number>]"); +	lprintf(LOG_NOTICE, "              looptest [<loop times> [<loop interval(in ms)> [<instance>]]]"); +} + + + +/* + * print_sol_set_usage + */ +static void +print_sol_set_usage(void) +{ +	lprintf(LOG_NOTICE, "\nSOL set parameters and values: \n"); +  	lprintf(LOG_NOTICE, "  set-in-progress             set-complete | " +		"set-in-progress | commit-write"); +	lprintf(LOG_NOTICE, "  enabled                     true | false"); +	lprintf(LOG_NOTICE, "  force-encryption            true | false"); +	lprintf(LOG_NOTICE, "  force-authentication        true | false"); +	lprintf(LOG_NOTICE, "  privilege-level             user | operator | admin | oem"); +	lprintf(LOG_NOTICE, "  character-accumulate-level  <in 5 ms increments>"); +	lprintf(LOG_NOTICE, "  character-send-threshold    N"); +	lprintf(LOG_NOTICE, "  retry-count                 N"); +	lprintf(LOG_NOTICE, "  retry-interval              <in 10 ms increments>"); +	lprintf(LOG_NOTICE, "  non-volatile-bit-rate       " +		"serial | 9.6 | 19.2 | 38.4 | 57.6 | 115.2"); +	lprintf(LOG_NOTICE, "  volatile-bit-rate           " +		"serial | 9.6 | 19.2 | 38.4 | 57.6 | 115.2"); +	lprintf(LOG_NOTICE, ""); +} + + + +/* ipmi_sol_main */ +int +ipmi_sol_main(struct ipmi_intf * intf, int argc, char ** argv) +{ +	int retval = 0; +	if (!argc || !strncmp(argv[0], "help", 4)) { +		/* Help */ +		print_sol_usage(); +	} else if (!strncmp(argv[0], "info", 4)) { +		/* Info */ +		uint8_t channel; +		if (argc == 1) { +			/* Ask about the current channel */ +			channel = 0x0E; +		} else if (argc == 2) { +			if (is_ipmi_channel_num(argv[1], &channel) != 0) { +				return (-1); +			} +		} else { +			print_sol_usage(); +			return -1; +		} +		retval = ipmi_print_sol_info(intf, channel); +	} else if (!strncmp(argv[0], "payload", 7)) { +		/* Payload enable or disable */ +		uint8_t channel = 0xe; +		uint8_t userid = 1; +		int enable = -1; +		if (argc == 1 || argc > 4) { +			print_sol_usage(); +			return -1; +		} +		if (argc == 1 || argc > 4) { +			print_sol_usage(); +			return -1; +		} +		if (argc >= 3) { +			if (is_ipmi_channel_num(argv[2], &channel) != 0) { +				return (-1); +			} +		} +		if (argc == 4) { +			if (is_ipmi_user_id(argv[3], &userid) != 0) { +				return (-1); +			} +		} +		if (!strncmp(argv[1], "enable", 6)) { +			enable = 1; +		} else if (!strncmp(argv[1], "disable", 7)) { +			enable = 0; +		} else if (!strncmp(argv[1], "status", 6)) { +			return ipmi_sol_payload_access_status(intf, channel, userid); +		} else { +			print_sol_usage(); +			return -1; +		} +		retval = ipmi_sol_payload_access(intf, channel, userid, enable); +	} else if (!strncmp(argv[0], "set", 3)) { +		/* Set a parameter value */ +		uint8_t channel = 0xe; +		uint8_t guard = 1; +		if (argc == 3) { +			channel = 0xe; +		} else if (argc == 4) { +			if (!strncmp(argv[3], "noguard", 7)) { +				guard = 0; +			} else { +				if (is_ipmi_channel_num(argv[3], &channel) != 0) { +					return (-1); +				} +			} +		} else if (argc == 5) { +			if (is_ipmi_channel_num(argv[3], &channel) != 0) { +				return (-1); +			} +			if (!strncmp(argv[4], "noguard", 7)) { +				guard = 0; +			} +		} else { +			print_sol_set_usage(); +			return -1; +		} +		retval = ipmi_sol_set_param(intf, channel, argv[1], argv[2], guard); +	} else if (!strncmp(argv[0], "activate", 8)) { +		/* Activate */ +		int i; +		uint8_t instance = 1; +		for (i = 1; i < argc; i++) { +			if (!strncmp(argv[i], "usesolkeepalive", 15)) { +				_use_sol_for_keepalive = 1; +			} else if (!strncmp(argv[i], "nokeepalive", 11)) { +				_disable_keepalive = 1; +			} else if (!strncmp(argv[i], "instance=", 9)) { +				if (str2uchar(argv[i] + 9, &instance) != 0) { +					lprintf(LOG_ERR, "Given instance '%s' is invalid.", argv[i] + 9); +					print_sol_usage(); +					return -1; +				} +			} else { +				print_sol_usage(); +				return -1; +			} +		} +		retval = ipmi_sol_activate(intf, 0, 0, instance); +	} else if (!strncmp(argv[0], "deactivate", 10)) { +		/* Dectivate */ +		int i; +		uint8_t instance = 1; +		for (i = 1; i < argc; i++) { +			if (!strncmp(argv[i], "instance=", 9)) { +				if (str2uchar(argv[i] + 9, &instance) != 0) { +					lprintf(LOG_ERR, +							"Given instance '%s' is invalid.", +							argv[i] + 9); +					print_sol_usage(); +					return -1; +				} +			} else { +				print_sol_usage(); +				return -1; +			} +		} +		retval = ipmi_sol_deactivate(intf, instance); +	} else if (!strncmp(argv[0], "looptest", 8)) { +		/* SOL loop test: Activate and then Dectivate */ +		int cnt = 200; +		int interval = 100; /* Unit is: ms */ +		uint8_t instance = 1; +		if (argc > 4) { +			print_sol_usage(); +			return -1; +		} +		if (argc != 1) { +			/* at least 2 */ +			if (str2int(argv[1], &cnt) != 0) { +				lprintf(LOG_ERR, "Given cnt '%s' is invalid.", +						argv[1]); +				return (-1); +			} +			if (cnt <= 0) { +				cnt = 200; +			} +		} +		if (argc >= 3) { +			if (str2int(argv[2], &interval) != 0) { +				lprintf(LOG_ERR, "Given interval '%s' is invalid.", +						argv[2]); +				return (-1); +			} +			if (interval < 0) { +				interval = 0; +			} +		} +		if (argc >= 4) { +			if (str2uchar(argv[3], &instance) != 0) { +				lprintf(LOG_ERR, "Given instance '%s' is invalid.", +						argv[3]); +				print_sol_usage(); +				return -1; +			} +		} + +		while (cnt > 0) { +			printf("remain loop test counter: %d\n", cnt); +			retval = ipmi_sol_activate(intf, 1, interval, instance); +			if (retval) { +				printf("SOL looptest failed: %d\n", +						retval); +				break; +			} +			cnt -= 1; +		} +	} else { +		print_sol_usage(); +		retval = -1; +	} +	return retval; +} | 
