From bf5e3782720c5938cc24b2af5d93eb1b182a12f0 Mon Sep 17 00:00:00 2001 From: Jeremy Ellington Date: Fri, 23 Jul 2004 20:56:17 +0000 Subject: [PATCH] Incremental changes for SOL. Support for breaks (R. Sunshine). Much refactoring and cleanup. More coming soon. --- ipmitool/lib/ipmi_sol.c | 238 ++++++++++++++++--------- ipmitool/src/plugins/lanplus/lanplus.c | 218 ++++++++++++++++------ 2 files changed, 320 insertions(+), 136 deletions(-) diff --git a/ipmitool/lib/ipmi_sol.c b/ipmitool/lib/ipmi_sol.c index 177f000..c734156 100644 --- a/ipmitool/lib/ipmi_sol.c +++ b/ipmitool/lib/ipmi_sol.c @@ -67,8 +67,7 @@ static struct termios _saved_tio; static int _in_raw_mode = 0; - - +static int bShouldExit = 0; extern int verbose; @@ -719,6 +718,28 @@ void enter_raw_mode(void) } +static void sendBreak(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + + memset(&v2_payload, 0, sizeof(v2_payload)); + + v2_payload.payload_length = 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(); @@ -730,21 +751,87 @@ static void suspendSelf(int bRestoreTty) +/* + * printSolEscapeSequences + * + * Send some useful documentation to the user + */ void printSolEscapeSequences() { printf( - "~?\r\n\ + "%c?\r\n\ Supported escape sequences:\r\n\ - ~. - terminate connection\r\n\ - ~^Z - suspend ipmitool\r\n\ - ~^X - suspend ipmitool, but don't restore tty on restart\r\n\ - ~? - this message\r\n\ - ~~ - send the escape character by typing it twice\r\n\ - (Note that escapes are only recognized immediately after newline.)\r\n"); + %c. - terminate connection\r\n\ + %c^Z - suspend ipmitool\r\n\ + %c^X - suspend ipmitool, but don't restore tty on restart\r\n\ + %cb - send break\r\n\ + %c? - this message\r\n\ + %c%c - send the escape character by typing it twice\r\n\ + (Note that escapes are only recognized immediately after newline.)\r\n", + SOL_ESCAPE_CHARACTER, + SOL_ESCAPE_CHARACTER, + SOL_ESCAPE_CHARACTER, + SOL_ESCAPE_CHARACTER, + SOL_ESCAPE_CHARACTER, + SOL_ESCAPE_CHARACTER, + SOL_ESCAPE_CHARACTER); } +/* + * output + * + * Send the specified data to stdout + */ +static void output(char * data, int length) +{ + int i; + for (i = 0; i < length; ++i) + putc(data[i], stdout); + + fflush(stdout); +} + + + +/* + * impi_sol_deactivate + */ +static int ipmi_sol_deactivate(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char data[6]; + + 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] = 1; /* payload instance. Guess! */ + + /* Lots of important data */ + data[2] = 0; + data[3] = 0; + data[4] = 0; + data[5] = 0; + + rsp = intf->sendrecv(intf, &req); + + if (!rsp || rsp->ccode) { + printf("Error:%x Dectivating SOL payload\n", + rsp ? rsp->ccode : 0); + return -1; + } + + return 0; +} + + + /* * processSolUserInput * @@ -767,11 +854,16 @@ static int processSolUserInput(struct ipmi_intf * intf, 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 && (ch != SOL_ESCAPE_CHARACTER)) { if (escape_pending){ escape_pending = 0; @@ -780,10 +872,9 @@ static int processSolUserInput(struct ipmi_intf * intf, */ switch (ch) { case '.': - /* Terminate the connection. */ - /* Should probably flush our buffer first */ - return 1; - continue; + printf("%c. [terminated ipmitool]\r\n", SOL_ESCAPE_CHARACTER); + retval = 1; + break; case 'Z' - 64: printf("%c^Z [suspend ipmitool]\r\n", SOL_ESCAPE_CHARACTER); suspendSelf(1); /* Restore tty back to raw */ @@ -794,6 +885,11 @@ static int processSolUserInput(struct ipmi_intf * intf, suspendSelf(0); /* Don't restore to raw mode */ continue; + case 'b': + printf("%cb [send break]\r\n", SOL_ESCAPE_CHARACTER); + sendBreak(intf); + continue; + case '?': printSolEscapeSequences(); continue; @@ -823,19 +919,28 @@ static int processSolUserInput(struct ipmi_intf * intf, } + /* + * If there is anything left to process we dispatch it to the BMC + */ if (length) { + struct ipmi_rs * rsp; + v2_payload.payload_length = length; + rsp = intf->send_sol(intf, &v2_payload); - //memcpy(v2_payload.payload.sol_packet.data, - // input, - // v2_payload.payload_length); - - if (intf->send_sol(intf, &v2_payload)) + /* This will always fail until we wait for our ACKs */ + if (! rsp) { - printf("Error sending SOL data\n"); - retval = -1; + //printf("Error sending SOL data\n"); + //retval = -1; } + + /* If the sequence number is set we know we have new data */ + //else 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->data, rsp->data_len); } return retval; @@ -882,11 +987,12 @@ static int ipmi_sol_red_pill(struct ipmi_intf * intf) return -1; } + + /* + * Process input from the user + */ if (FD_ISSET(0, &read_fds)) - { - /* - * Received input from the user - */ + { bzero(buffer, sizeof(buffer)); numRead = read(fileno(stdin), buffer, sizeof(buffer)); @@ -894,10 +1000,14 @@ static int ipmi_sol_red_pill(struct ipmi_intf * intf) if (numRead > 0) { int rc = processSolUserInput(intf, buffer, numRead); + if (rc) - bShouldExit = 1; - if (rc < 1) - bBmcClosedSession = 1; + { + if (rc < 0) + bShouldExit = bBmcClosedSession = 1; + else + bShouldExit = 1; + } } else { @@ -905,11 +1015,12 @@ static int ipmi_sol_red_pill(struct ipmi_intf * intf) } } + + /* + * Process input from the BMC + */ else if (FD_ISSET(intf->fd, &read_fds)) { - /* - * Received input from the BMC. - */ int i; struct ipmi_rs * rs =intf->recv_sol(intf); if (! rs) @@ -917,17 +1028,15 @@ static int ipmi_sol_red_pill(struct ipmi_intf * intf) bShouldExit = bBmcClosedSession = 1; } else - { - for (i = 0; i < rs->data_len; ++i) - putc(rs->data[i], stdout); - } - - fflush(stdout); + output(rs->data, rs->data_len); } + - else + /* + * ERROR in select + */ + else { - /* ERROR */ printf("Error: Select returned with nothing to read\n"); bShouldExit = 1; } @@ -941,46 +1050,14 @@ static int ipmi_sol_red_pill(struct ipmi_intf * intf) printf("SOL session closed by BMC\n"); exit(1); } + else + ipmi_sol_deactivate(intf); return 0; } -/* - * impi_sol_deactivate - */ -static int ipmi_sol_deactivate(struct ipmi_intf * intf) -{ - struct ipmi_rs * rsp; - struct ipmi_rq req; - unsigned char data[6]; - - 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] = 1; /* payload instance. Guess! */ - - /* Lots of important data */ - data[2] = 0; - data[3] = 0; - data[4] = 0; - data[5] = 0; - - rsp = intf->sendrecv(intf, &req); - - if (!rsp || rsp->ccode) { - printf("Error:%x Dectivating SOL payload\n", - rsp ? rsp->ccode : 0); - return -1; - } - - return 0; -} @@ -1065,14 +1142,6 @@ static int ipmi_sol_activate(struct ipmi_intf * intf) #endif - printf("max inbound payload size : %d\n", - intf->session->sol_data.max_inbound_payload_size); - printf("max outbound payload size : %d\n", - intf->session->sol_data.max_outbound_payload_size); - printf("SOL port : %d\n", - intf->session->sol_data.port); - - if (intf->session->sol_data.port != intf->session->port) { printf("Error: BMC requests SOL session on different port\n"); @@ -1080,6 +1149,10 @@ static int ipmi_sol_activate(struct ipmi_intf * intf) } + printf("[SOL Session operational. Use %c? for help]\r\n", + SOL_ESCAPE_CHARACTER); + + /* * At this point we are good to go with our SOL session. We * need to listen to @@ -1104,6 +1177,8 @@ void print_sol_usage() { printf("SOL Commands: info []\n"); printf(" set [channel]\n"); + printf(" activate\n"); + printf(" deactivate\n"); } @@ -1140,6 +1215,7 @@ int ipmi_sol_main(struct ipmi_intf * intf, int argc, char ** argv) retval = ipmi_print_sol_info(intf, channel); } + /* * Set a parameter value */ diff --git a/ipmitool/src/plugins/lanplus/lanplus.c b/ipmitool/src/plugins/lanplus/lanplus.c index 4403504..644c997 100644 --- a/ipmitool/src/plugins/lanplus/lanplus.c +++ b/ipmitool/src/plugins/lanplus/lanplus.c @@ -102,8 +102,12 @@ static void read_session_data_v2x(struct ipmi_rs * rsp, int * offset, struct ipm static void read_ipmi_response(struct ipmi_rs * rsp, int * offset); static void read_sol_packet(struct ipmi_rs * rsp, int * offset); static struct ipmi_rs * ipmi_lanplus_recv_sol(struct ipmi_intf * intf); -static int ipmi_lanplus_send_sol(struct ipmi_intf * intf, - struct ipmi_v2_payload * payload); +static struct ipmi_rs * ipmi_lanplus_send_sol(struct ipmi_intf * intf, + struct ipmi_v2_payload * payload); +static int check_sol_packet_for_data(struct ipmi_intf * intf, + struct ipmi_rs *rsp); +static void ack_sol_packet(struct ipmi_intf * intf, + struct ipmi_rs * rsp); struct ipmi_intf ipmi_lanplus_intf = { @@ -605,7 +609,10 @@ ipmi_lan_poll_recv(struct ipmi_intf * intf) } - else if (rsp->session.payloadtype == + /* + * Open Response + */ + else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE) { if (session->v2_data.session_state != @@ -621,7 +628,11 @@ ipmi_lan_poll_recv(struct ipmi_intf * intf) break; } - else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_2) + + /* + * RAKP 2 + */ + else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_2) { if (session->v2_data.session_state != LANPLUS_STATE_RAKP_1_SENT) { @@ -634,6 +645,10 @@ ipmi_lan_poll_recv(struct ipmi_intf * intf) break; } + + /* + * RAKP 4 + */ else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_4) { if (session->v2_data.session_state != LANPLUS_STATE_RAKP_3_SENT) @@ -647,7 +662,11 @@ ipmi_lan_poll_recv(struct ipmi_intf * intf) break; } - else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) + + /* + * SOL + */ + else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) { int payload_start = offset; int extra_data_length; @@ -1833,6 +1852,8 @@ struct ipmi_rs * while (try < IPMI_LAN_RETRY) { + + if (ipmi_lan_send_packet(intf, msg_data, msg_length) < 0) { printf("ipmi_lan_send_cmd failed\n"); return NULL; @@ -1859,13 +1880,38 @@ struct ipmi_rs * if (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) { - // jme : todo. If the payload is not an ACK we need to wait - // for an ack. And if we don't receive an ACK, we need to - // resend. - break; + //if (! payload->payload.sol_packet.packet_sequence_number) + //{ + /* This was just an ACK. We can leave now. No retry. */ + break; + //} + + + //int try_count = 1; + + + // jme : todo. We need to wait for an ack. And if we + // don't receive an ACK, we need to resend. + + /* We need an ACK */ + //for (try_count = 1; try_count < 3; ++try_count) + //{ + // rsp = ipmi_lan_poll_recv(intf); + + /* Sets our last see */ + // handleIncomingSolPacket(rsp); + + // if (acksPacket(rsp, payload)) + // break; + //} + + //break; } + /* + * This call is NOT made for SOL packets + */ rsp = ipmi_lan_poll_recv(intf); if (rsp) break; @@ -1887,9 +1933,11 @@ struct ipmi_rs * * return 0 on success * -1 on error */ -int ipmi_lanplus_send_sol(struct ipmi_intf * intf, - struct ipmi_v2_payload * v2_payload) +struct ipmi_rs * ipmi_lanplus_send_sol(struct ipmi_intf * intf, + struct ipmi_v2_payload * v2_payload) { + struct ipmi_rs * rs; + v2_payload->payload_type = IPMI_PAYLOAD_TYPE_SOL; /* @@ -1907,27 +1955,100 @@ int ipmi_lanplus_send_sol(struct ipmi_intf * intf, v2_payload->payload.sol_packet.accepted_character_count = 0; /* NA */ - ipmi_lanplus_send_payload(intf, v2_payload); + rs = ipmi_lanplus_send_payload(intf, v2_payload); - return 0; + return rs; } /* - * ipmi_lanplus_recv_sol + * check_sol_packet_for_new_data * - * Receive a SOL packet and send an ACK in response. + * Determine whether the SOL packet has already been seen + * and whether the packet has new data for us. * + * This function has the side effect of removing an previously + * seen data, and moving new data to the front. + * + * It also "Remembers" the data so we don't get repeats. + * + * returns the number of new bytes in the SOL packet */ -struct ipmi_rs * ipmi_lanplus_recv_sol(struct ipmi_intf * intf) +static int check_sol_packet_for_new_data(struct ipmi_intf * intf, + struct ipmi_rs *rsp) { - struct ipmi_v2_payload ack; - struct ipmi_rs * rsp = ipmi_lan_poll_recv(intf); - - /* If the SOL packet looks good, ACK it */ - if (rsp && rsp->data_len) + static unsigned char last_received_sequence_number = 0; + static unsigned char last_received_byte_count = 0; + int new_data_size = 0; + + + if (rsp && + (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && + (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)) { + /* Store the data length before we mod it */ + unsigned char unaltered_data_len = rsp->data_len; + + + if (rsp->payload.sol_packet.packet_sequence_number == + last_received_sequence_number) + { + + /* + * This is the same as the last packet, but may include + * extra data + */ + new_data_size = + rsp->data_len - + intf->session->sol_data.last_received_byte_count; + + if (new_data_size > 0) + { + /* We have more data to process */ + memmove(rsp->data, + rsp->data + + rsp->data_len - new_data_size, + new_data_size); + } + + rsp->data_len = new_data_size; + } + + + /* + *Rember the data for next round + */ + if (rsp->payload.sol_packet.packet_sequence_number) + { + last_received_sequence_number = + rsp->payload.sol_packet.packet_sequence_number; + + last_received_byte_count = unaltered_data_len; + } + } + + + return new_data_size; +} + + + +/* + * ack_sol_packet + * + * Provided the specified packet looks reasonable, ACK it. + */ +static void ack_sol_packet(struct ipmi_intf * intf, + struct ipmi_rs * rsp) +{ + if (rsp && + (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && + (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) && + (rsp->payload.sol_packet.packet_sequence_number)) + { + struct ipmi_v2_payload ack; + bzero(&ack, sizeof(struct ipmi_v2_payload)); ack.payload_type = IPMI_PAYLOAD_TYPE_SOL; @@ -1948,45 +2069,33 @@ struct ipmi_rs * ipmi_lanplus_recv_sol(struct ipmi_intf * intf) ipmi_lanplus_send_payload(intf, &ack); } +} - /* This may be an identical packet */ - if (rsp && - (rsp->payload.sol_packet.packet_sequence_number == - intf->session->sol_data.last_received_sequence_number)) - { - /* This is the same as the last packet, but may include - extra data */ - int extra_data_size = - rsp->data_len - - intf->session->sol_data.last_received_byte_count; - if (extra_data_size > 0) - { - /* We have more data to process */ - memmove(rsp->data, - rsp->data + - rsp->data_len - extra_data_size, - extra_data_size); - } - rsp->data_len = extra_data_size; - } - - /* If it's not an ACK, remember that we saw it. */ - if (rsp && - rsp->payload.sol_packet.packet_sequence_number) - { - intf->session->sol_data.last_received_sequence_number = - rsp->payload.sol_packet.packet_sequence_number; - - intf->session->sol_data.last_received_byte_count = - rsp->data_len; - } +/* + * ipmi_lanplus_recv_sol + * + * Receive a SOL packet and send an ACK in response. + * + */ +struct ipmi_rs * ipmi_lanplus_recv_sol(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp = ipmi_lan_poll_recv(intf); + ack_sol_packet(intf, rsp); + + /* + * Remembers the data sent, and alters the data to just + * include the new stuff. + */ + check_sol_packet_for_new_data(intf, rsp); + return rsp; } + /** * ipmi_lanplus_send_ipmi_cmd * @@ -2005,7 +2114,6 @@ struct ipmi_rs * - /* * ipmi_get_auth_capabilities_cmd * @@ -2584,8 +2692,8 @@ int ipmi_lanplus_open(struct ipmi_intf * intf) session->v2_data.console_id = 0x00; session->v2_data.bmc_id = 0x00; session->sol_data.sequence_number = 1; - session->sol_data.last_received_sequence_number = 0; - session->sol_data.last_received_byte_count = 0; + //session->sol_data.last_received_sequence_number = 0; + //session->sol_data.last_received_byte_count = 0; memset(session->v2_data.sik, 0, IPMI_SIK_BUFFER_SIZE); memset(session->v2_data.kg, 0, IPMI_KG_BUFFER_SIZE);