1 /* 2 * Copyright (c) 2005 Tyan Computer Corp. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * Redistribution of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * Redistribution in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * Neither the name of Sun Microsystems, Inc. or the names of 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * This software is provided "AS IS," without a warranty of any kind. 20 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, 21 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A 22 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. 23 * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE 24 * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING 25 * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL 26 * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, 27 * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR 28 * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF 29 * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, 30 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <sys/poll.h> 36 #include <fcntl.h> 37 #include <unistd.h> 38 #include <errno.h> 39 #include <stdlib.h> 40 #include <stdio.h> 41 #include <string.h> 42 #include <sys/socket.h> 43 #include <netinet/in.h> 44 #include <arpa/inet.h> 45 #include <netdb.h> 46 #include <signal.h> 47 48 #include <sys/select.h> 49 #include <sys/time.h> 50 #include <sys/ioctl.h> 51 52 #if defined(HAVE_CONFIG_H) 53 # include <config.h> 54 #endif 55 56 #if defined(HAVE_TERMIOS_H) 57 # include <termios.h> 58 #elif defined (HAVE_SYS_TERMIOS_H) 59 # include <sys/termios.h> 60 #endif 61 62 #include <ipmitool/log.h> 63 #include <ipmitool/helper.h> 64 #include <ipmitool/ipmi.h> 65 #include <ipmitool/ipmi_intf.h> 66 #include <ipmitool/ipmi_tsol.h> 67 #include <ipmitool/ipmi_strings.h> 68 #include <ipmitool/bswap.h> 69 70 static struct timeval _start_keepalive; 71 static struct termios _saved_tio; 72 static struct winsize _saved_winsize; 73 static int _in_raw_mode = 0; 74 static int _altterm = 0; 75 76 extern int verbose; 77 78 static int 79 ipmi_tsol_command(struct ipmi_intf *intf, char *recvip, int port, 80 unsigned char cmd) 81 { 82 struct ipmi_rs *rsp; 83 struct ipmi_rq req; 84 unsigned char data[6]; 85 unsigned ip1, ip2, ip3, ip4; 86 87 if (sscanf(recvip, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) != 4) { 88 lprintf(LOG_ERR, "Invalid IP address: %s", recvip); 89 return (-1); 90 } 91 memset(&req, 0, sizeof(struct ipmi_rq)); 92 req.msg.netfn = IPMI_NETFN_TSOL; 93 req.msg.cmd = cmd; 94 req.msg.data_len = 6; 95 req.msg.data = data; 96 97 memset(data, 0, sizeof(data)); 98 data[0] = ip1; 99 data[1] = ip2; 100 data[2] = ip3; 101 data[3] = ip4; 102 data[4] = (port & 0xff00) >> 8; 103 data[5] = (port & 0xff); 104 105 rsp = intf->sendrecv(intf, &req); 106 if (rsp == NULL) { 107 lprintf(LOG_ERR, "Unable to perform TSOL command"); 108 return (-1); 109 } 110 if (rsp->ccode > 0) { 111 lprintf(LOG_ERR, "Unable to perform TSOL command: %s", 112 val2str(rsp->ccode, completion_code_vals)); 113 return (-1); 114 } 115 return 0; 116 } 117 118 static int 119 ipmi_tsol_start(struct ipmi_intf *intf, char *recvip, int port) 120 { 121 return ipmi_tsol_command(intf, recvip, port, IPMI_TSOL_CMD_START); 122 } 123 124 static int 125 ipmi_tsol_stop(struct ipmi_intf *intf, char *recvip, int port) 126 { 127 return ipmi_tsol_command(intf, recvip, port, IPMI_TSOL_CMD_STOP); 128 } 129 130 static int 131 ipmi_tsol_send_keystroke(struct ipmi_intf *intf, char *buff, int length) 132 { 133 struct ipmi_rs *rsp; 134 struct ipmi_rq req; 135 unsigned char data[16]; 136 static unsigned char keyseq = 0; 137 138 memset(&req, 0, sizeof(struct ipmi_rq)); 139 req.msg.netfn = IPMI_NETFN_TSOL; 140 req.msg.cmd = IPMI_TSOL_CMD_SENDKEY; 141 req.msg.data_len = length + 2; 142 req.msg.data = data; 143 144 memset(data, 0, sizeof(data)); 145 data[0] = length + 1; 146 memcpy(data + 1, buff, length); 147 data[length + 1] = keyseq++; 148 149 rsp = intf->sendrecv(intf, &req); 150 if (verbose) { 151 if (rsp == NULL) { 152 lprintf(LOG_ERR, "Unable to send keystroke"); 153 return -1; 154 } 155 if (rsp->ccode > 0) { 156 lprintf(LOG_ERR, "Unable to send keystroke: %s", 157 val2str(rsp->ccode, completion_code_vals)); 158 return -1; 159 } 160 } 161 return length; 162 } 163 164 static int 165 tsol_keepalive(struct ipmi_intf *intf) 166 { 167 struct timeval end; 168 gettimeofday(&end, 0); 169 if (end.tv_sec - _start_keepalive.tv_sec <= 30) { 170 return 0; 171 } 172 intf->keepalive(intf); 173 gettimeofday(&_start_keepalive, 0); 174 return 0; 175 } 176 177 static void 178 print_escape_seq(struct ipmi_intf *intf) 179 { 180 lprintf(LOG_NOTICE, 181 " %c. - terminate connection\n" 182 " %c^Z - suspend ipmitool\n" 183 " %c^X - suspend ipmitool, but don't restore tty on restart\n" 184 " %c? - this message\n" 185 " %c%c - send the escape character by typing it twice\n" 186 " (Note that escapes are only recognized immediately after newline.)", 187 intf->session->sol_escape_char, 188 intf->session->sol_escape_char, 189 intf->session->sol_escape_char, 190 intf->session->sol_escape_char, 191 intf->session->sol_escape_char, 192 intf->session->sol_escape_char); 193 } 194 195 static int 196 leave_raw_mode(void) 197 { 198 if (!_in_raw_mode) { 199 return -1; 200 } else if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) { 201 lperror(LOG_ERR, "tcsetattr(stdin)"); 202 } else if (tcsetattr(fileno(stdout), TCSADRAIN, &_saved_tio) == -1) { 203 lperror(LOG_ERR, "tcsetattr(stdout)"); 204 } else { 205 _in_raw_mode = 0; 206 } 207 return 0; 208 } 209 210 static int 211 enter_raw_mode(void) 212 { 213 struct termios tio; 214 if (tcgetattr(fileno(stdout), &_saved_tio) < 0) { 215 lperror(LOG_ERR, "tcgetattr failed"); 216 return -1; 217 } 218 219 tio = _saved_tio; 220 if (_altterm) { 221 tio.c_iflag &= (ISTRIP | IGNBRK); 222 tio.c_cflag &= ~(CSIZE | PARENB | IXON | IXOFF | IXANY); 223 tio.c_cflag |= (CS8 |CREAD) | (IXON|IXOFF|IXANY); 224 tio.c_lflag &= 0; 225 tio.c_cc[VMIN] = 1; 226 tio.c_cc[VTIME] = 0; 227 } else { 228 tio.c_iflag |= IGNPAR; 229 tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); 230 tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | IEXTEN); 231 tio.c_oflag &= ~OPOST; 232 tio.c_cc[VMIN] = 1; 233 tio.c_cc[VTIME] = 0; 234 } 235 236 if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) { 237 lperror(LOG_ERR, "tcsetattr(stdin)"); 238 } else if (tcsetattr(fileno(stdout), TCSADRAIN, &tio) < 0) { 239 lperror(LOG_ERR, "tcsetattr(stdout)"); 240 } else { 241 _in_raw_mode = 1; 242 } 243 return 0; 244 } 245 246 static void 247 suspend_self(int restore_tty) 248 { 249 leave_raw_mode(); 250 kill(getpid(), SIGTSTP); 251 if (restore_tty) { 252 enter_raw_mode(); 253 } 254 } 255 256 static int 257 do_inbuf_actions(struct ipmi_intf *intf, char *in_buff, int len) 258 { 259 static int in_esc = 0; 260 static int last_was_cr = 1; 261 int i; 262 263 for(i = 0; i < len ;) { 264 if (!in_esc) { 265 if (last_was_cr && 266 (in_buff[i] == intf->session->sol_escape_char)) { 267 in_esc = 1; 268 memmove(in_buff, in_buff + 1, len - i - 1); 269 len--; 270 continue; 271 } 272 } 273 if (in_esc) { 274 if (in_buff[i] == intf->session->sol_escape_char) { 275 in_esc = 0; 276 i++; 277 continue; 278 } 279 280 switch (in_buff[i]) { 281 case '.': 282 printf("%c. [terminated ipmitool]\n", 283 intf->session->sol_escape_char); 284 return -1; 285 case 'Z' - 64: 286 printf("%c^Z [suspend ipmitool]\n", 287 intf->session->sol_escape_char); 288 /* Restore tty back to raw */ 289 suspend_self(1); 290 break; 291 case 'X' - 64: 292 printf("%c^X [suspend ipmitool]\n", 293 intf->session->sol_escape_char); 294 /* Don't restore to raw mode */ 295 suspend_self(0); 296 break; 297 case '?': 298 printf("%c? [ipmitool help]\n", 299 intf->session->sol_escape_char); 300 print_escape_seq(intf); 301 break; 302 } 303 304 memmove(in_buff, (in_buff + 1), (len - i - 1)); 305 len--; 306 in_esc = 0; 307 continue; 308 } 309 last_was_cr = (in_buff[i] == '\r' || in_buff[i] == '\n'); 310 i++; 311 } 312 return len; 313 } 314 315 316 static void 317 do_terminal_cleanup(void) 318 { 319 if (_saved_winsize.ws_row > 0 && _saved_winsize.ws_col > 0) { 320 ioctl(fileno(stdout), TIOCSWINSZ, &_saved_winsize); 321 } 322 leave_raw_mode(); 323 if (errno) { 324 lprintf(LOG_ERR, "Exiting due to error %d -> %s", 325 errno, strerror(errno)); 326 } 327 } 328 329 static void 330 set_terminal_size(int rows, int cols) 331 { 332 struct winsize winsize; 333 if (rows <= 0 || cols <= 0) { 334 return; 335 } 336 /* save initial winsize */ 337 ioctl(fileno(stdout), TIOCGWINSZ, &_saved_winsize); 338 /* set new winsize */ 339 winsize.ws_row = rows; 340 winsize.ws_col = cols; 341 ioctl(fileno(stdout), TIOCSWINSZ, &winsize); 342 } 343 344 static void 345 print_tsol_usage(void) 346 { 347 struct winsize winsize; 348 lprintf(LOG_NOTICE, 349 "Usage: tsol [recvip] [port=NUM] [ro|rw] [rows=NUM] [cols=NUM] [altterm]"); 350 lprintf(LOG_NOTICE, 351 " recvip Receiver IP Address [default=local]"); 352 lprintf(LOG_NOTICE, 353 " port=NUM Receiver UDP Port [default=%d]", 354 IPMI_TSOL_DEF_PORT); 355 lprintf(LOG_NOTICE, 356 " ro|rw Set Read-Only or Read-Write [default=rw]"); 357 ioctl(fileno(stdout), TIOCGWINSZ, &winsize); 358 lprintf(LOG_NOTICE, 359 " rows=NUM Set terminal rows [default=%d]", 360 winsize.ws_row); 361 lprintf(LOG_NOTICE, 362 " cols=NUM Set terminal columns [default=%d]", 363 winsize.ws_col); 364 lprintf(LOG_NOTICE, 365 " altterm Alternate terminal setup [default=off]"); 366 } 367 368 int 369 ipmi_tsol_main(struct ipmi_intf *intf, int argc, char **argv) 370 { 371 struct pollfd fds_wait[3], fds_data_wait[3], *fds; 372 struct sockaddr_in sin, myaddr, *sa_in; 373 socklen_t mylen; 374 char *recvip = NULL; 375 char in_buff[IPMI_BUF_SIZE]; 376 char out_buff[IPMI_BUF_SIZE * 8]; 377 char buff[IPMI_BUF_SIZE + 4]; 378 int fd_socket, result, i; 379 int out_buff_fill, in_buff_fill; 380 int ip1, ip2, ip3, ip4; 381 int read_only = 0, rows = 0, cols = 0; 382 int port = IPMI_TSOL_DEF_PORT; 383 384 if (strlen(intf->name) < 3 || strncmp(intf->name, "lan", 3) != 0) { 385 lprintf(LOG_ERR, "Error: Tyan SOL is only available over lan interface"); 386 return (-1); 387 } 388 389 for (i = 0; i<argc; i++) { 390 if (sscanf(argv[i], "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) == 4) { 391 /* not free'd ...*/ 392 /* recvip = strdup(argv[i]); */ 393 recvip = argv[i]; 394 } else if (sscanf(argv[i], "port=%d", &ip1) == 1) { 395 port = ip1; 396 } else if (sscanf(argv[i], "rows=%d", &ip1) == 1) { 397 rows = ip1; 398 } else if (sscanf(argv[i], "cols=%d", &ip1) == 1) { 399 cols = ip1; 400 } else if (strlen(argv[i]) == 2 401 && strncmp(argv[i], "ro", 2) == 0) { 402 read_only = 1; 403 } else if (strlen(argv[i]) == 2 404 && strncmp(argv[i], "rw", 2) == 0) { 405 read_only = 0; 406 } else if (strlen(argv[i]) == 7 407 && strncmp(argv[i], "altterm", 7) == 0) { 408 _altterm = 1; 409 } else if (strlen(argv[i]) == 4 410 && strncmp(argv[i], "help", 4) == 0) { 411 print_tsol_usage(); 412 return 0; 413 } else { 414 print_tsol_usage(); 415 return 0; 416 } 417 } 418 419 /* create udp socket to receive the packet */ 420 memset(&sin, 0, sizeof(sin)); 421 sin.sin_family = AF_INET; 422 sin.sin_port = htons(port); 423 424 sa_in = (struct sockaddr_in *)&intf->session->addr; 425 result = inet_pton(AF_INET, (const char *)intf->session->hostname, 426 &sa_in->sin_addr); 427 428 if (result <= 0) { 429 struct hostent *host = gethostbyname((const char *)intf->session->hostname); 430 if (host == NULL ) { 431 lprintf(LOG_ERR, "Address lookup for %s failed", 432 intf->session->hostname); 433 return -1; 434 } 435 if (host->h_addrtype != AF_INET) { 436 lprintf(LOG_ERR, 437 "Address lookup for %s failed. Got %s, expected IPv4 address.", 438 intf->session->hostname, 439 (host->h_addrtype == AF_INET6) ? "IPv6" : "Unknown"); 440 return (-1); 441 } 442 sa_in->sin_family = host->h_addrtype; 443 memcpy(&sa_in->sin_addr, host->h_addr, host->h_length); 444 } 445 446 fd_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 447 if (fd_socket < 0) { 448 lprintf(LOG_ERR, "Can't open port %d", port); 449 return -1; 450 } 451 if (bind(fd_socket, (struct sockaddr *)&sin, sizeof(sin)) == (-1)) { 452 lprintf(LOG_ERR, "Failed to bind socket."); 453 close(fd_socket); 454 return -1; 455 } 456 457 /* 458 * retrieve local IP address if not supplied on command line 459 */ 460 if (recvip == NULL) { 461 /* must connect first */ 462 result = intf->open(intf); 463 if (result < 0) { 464 close(fd_socket); 465 return -1; 466 } 467 468 mylen = sizeof(myaddr); 469 if (getsockname(intf->fd, (struct sockaddr *)&myaddr, &mylen) < 0) { 470 lperror(LOG_ERR, "getsockname failed"); 471 close(fd_socket); 472 return -1; 473 } 474 475 recvip = inet_ntoa(myaddr.sin_addr); 476 if (recvip == NULL) { 477 lprintf(LOG_ERR, "Unable to find local IP address"); 478 close(fd_socket); 479 return -1; 480 } 481 } 482 483 printf("[Starting %sSOL with receiving address %s:%d]\n", 484 read_only ? "Read-only " : "", recvip, port); 485 486 set_terminal_size(rows, cols); 487 enter_raw_mode(); 488 489 /* 490 * talk to smdc to start Console redirect - IP address and port as parameter 491 * ipmitool -I lan -H 192.168.168.227 -U Administrator raw 0x30 0x06 0xC0 0xA8 0xA8 0x78 0x1A 0x0A 492 */ 493 result = ipmi_tsol_start(intf, recvip, port); 494 if (result < 0) { 495 lprintf(LOG_ERR, "Error starting SOL"); 496 close(fd_socket); 497 return (-1); 498 } 499 500 printf("[SOL Session operational. Use %c? for help]\n", 501 intf->session->sol_escape_char); 502 503 gettimeofday(&_start_keepalive, 0); 504 505 fds_wait[0].fd = fd_socket; 506 fds_wait[0].events = POLLIN; 507 fds_wait[0].revents = 0; 508 fds_wait[1].fd = fileno(stdin); 509 fds_wait[1].events = POLLIN; 510 fds_wait[1].revents = 0; 511 fds_wait[2].fd = -1; 512 fds_wait[2].events = 0; 513 fds_wait[2].revents = 0; 514 515 fds_data_wait[0].fd = fd_socket; 516 fds_data_wait[0].events = POLLIN | POLLOUT; 517 fds_data_wait[0].revents = 0; 518 fds_data_wait[1].fd = fileno(stdin); 519 fds_data_wait[1].events = POLLIN; 520 fds_data_wait[1].revents = 0; 521 fds_data_wait[2].fd = fileno(stdout); 522 fds_data_wait[2].events = POLLOUT; 523 fds_data_wait[2].revents = 0; 524 525 out_buff_fill = 0; 526 in_buff_fill = 0; 527 fds = fds_wait; 528 for (;;) { 529 result = poll(fds, 3, 15 * 1000); 530 if (result < 0) { 531 break; 532 } 533 534 /* send keepalive packet */ 535 tsol_keepalive(intf); 536 537 if ((fds[0].revents & POLLIN) && (sizeof(out_buff) > out_buff_fill)) { 538 socklen_t sin_len = sizeof(sin); 539 int buff_size = sizeof(buff); 540 if ((sizeof(out_buff) - out_buff_fill + 4) < buff_size) { 541 buff_size = (sizeof(out_buff) - out_buff_fill) + 4; 542 if ((buff_size - 4) <= 0) { 543 buff_size = 0; 544 } 545 } 546 result = recvfrom(fd_socket, buff, 547 buff_size, 0, 548 (struct sockaddr *)&sin, &sin_len); 549 /* read the data from udp socket, 550 * skip some bytes in the head 551 */ 552 if ((result - 4) > 0) { 553 int length = result - 4; 554 memcpy(out_buff + out_buff_fill, buff + 4, length); 555 out_buff_fill += length; 556 } 557 } 558 if ((fds[1].revents & POLLIN) && (sizeof(in_buff) > in_buff_fill)) { 559 /* Read from keyboard */ 560 result = read(fileno(stdin), in_buff + in_buff_fill, 561 sizeof(in_buff) - in_buff_fill); 562 if (result > 0) { 563 int bytes; 564 bytes = do_inbuf_actions(intf, 565 in_buff + in_buff_fill, result); 566 if (bytes < 0) { 567 result = ipmi_tsol_stop(intf, recvip, port); 568 do_terminal_cleanup(); 569 return result; 570 } 571 if (read_only) { 572 bytes = 0; 573 } 574 in_buff_fill += bytes; 575 } 576 } 577 if ((fds[2].revents & POLLOUT) && out_buff_fill) { 578 /* To screen */ 579 result = write(fileno(stdout), out_buff, out_buff_fill); 580 if (result > 0) { 581 out_buff_fill -= result; 582 if (out_buff_fill) { 583 memmove(out_buff, out_buff + result, out_buff_fill); 584 } 585 } 586 } 587 if ((fds[0].revents & POLLOUT) && in_buff_fill) { 588 /* 589 * translate key and send that to SMDC using IPMI 590 * ipmitool -I lan -H 192.168.168.227 -U Administrator raw 0x30 0x03 0x04 0x1B 0x5B 0x43 591 */ 592 result = ipmi_tsol_send_keystroke(intf, 593 in_buff, __min(in_buff_fill, 14)); 594 if (result > 0) { 595 gettimeofday(&_start_keepalive, 0); 596 in_buff_fill -= result; 597 if (in_buff_fill) { 598 memmove(in_buff, in_buff + result, in_buff_fill); 599 } 600 } 601 } 602 fds = (in_buff_fill || out_buff_fill )? 603 fds_data_wait : fds_wait; 604 } 605 return 0; 606 } 607