ipmi_tsol.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  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. #define _DEFAULT_SOURCE
  33. #include <sys/types.h>
  34. #include <sys/stat.h>
  35. #include <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. #include <sys/select.h>
  48. #include <sys/time.h>
  49. #include <sys/ioctl.h>
  50. #if defined(HAVE_CONFIG_H)
  51. # include <config.h>
  52. #endif
  53. #if defined(HAVE_TERMIOS_H)
  54. # include <termios.h>
  55. #elif defined (HAVE_SYS_TERMIOS_H)
  56. # include <sys/termios.h>
  57. #endif
  58. #include <ipmitool/log.h>
  59. #include <ipmitool/helper.h>
  60. #include <ipmitool/ipmi.h>
  61. #include <ipmitool/ipmi_intf.h>
  62. #include <ipmitool/ipmi_tsol.h>
  63. #include <ipmitool/ipmi_strings.h>
  64. #include <ipmitool/bswap.h>
  65. static struct timeval _start_keepalive;
  66. static struct termios _saved_tio;
  67. static struct winsize _saved_winsize;
  68. static int _in_raw_mode = 0;
  69. static int _altterm = 0;
  70. extern int verbose;
  71. static int
  72. ipmi_tsol_command(struct ipmi_intf *intf, char *recvip, int port,
  73. unsigned char cmd)
  74. {
  75. struct ipmi_rs *rsp;
  76. struct ipmi_rq req;
  77. unsigned char data[6];
  78. unsigned ip1, ip2, ip3, ip4;
  79. if (sscanf(recvip, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) != 4) {
  80. lprintf(LOG_ERR, "Invalid IP address: %s", recvip);
  81. return (-1);
  82. }
  83. memset(&req, 0, sizeof(struct ipmi_rq));
  84. req.msg.netfn = IPMI_NETFN_TSOL;
  85. req.msg.cmd = cmd;
  86. req.msg.data_len = 6;
  87. req.msg.data = data;
  88. memset(data, 0, sizeof(data));
  89. data[0] = ip1;
  90. data[1] = ip2;
  91. data[2] = ip3;
  92. data[3] = ip4;
  93. data[4] = (port & 0xff00) >> 8;
  94. data[5] = (port & 0xff);
  95. rsp = intf->sendrecv(intf, &req);
  96. if (rsp == NULL) {
  97. lprintf(LOG_ERR, "Unable to perform TSOL command");
  98. return (-1);
  99. }
  100. if (rsp->ccode > 0) {
  101. lprintf(LOG_ERR, "Unable to perform TSOL command: %s",
  102. val2str(rsp->ccode, completion_code_vals));
  103. return (-1);
  104. }
  105. return 0;
  106. }
  107. static int
  108. ipmi_tsol_start(struct ipmi_intf *intf, char *recvip, int port)
  109. {
  110. return ipmi_tsol_command(intf, recvip, port, IPMI_TSOL_CMD_START);
  111. }
  112. static int
  113. ipmi_tsol_stop(struct ipmi_intf *intf, char *recvip, int port)
  114. {
  115. return ipmi_tsol_command(intf, recvip, port, IPMI_TSOL_CMD_STOP);
  116. }
  117. static int
  118. ipmi_tsol_send_keystroke(struct ipmi_intf *intf, char *buff, int length)
  119. {
  120. struct ipmi_rs *rsp;
  121. struct ipmi_rq req;
  122. unsigned char data[16];
  123. static unsigned char keyseq = 0;
  124. memset(&req, 0, sizeof(struct ipmi_rq));
  125. req.msg.netfn = IPMI_NETFN_TSOL;
  126. req.msg.cmd = IPMI_TSOL_CMD_SENDKEY;
  127. req.msg.data_len = length + 2;
  128. req.msg.data = data;
  129. memset(data, 0, sizeof(data));
  130. data[0] = length + 1;
  131. memcpy(data + 1, buff, length);
  132. data[length + 1] = keyseq++;
  133. rsp = intf->sendrecv(intf, &req);
  134. if (verbose) {
  135. if (rsp == NULL) {
  136. lprintf(LOG_ERR, "Unable to send keystroke");
  137. return -1;
  138. }
  139. if (rsp->ccode > 0) {
  140. lprintf(LOG_ERR, "Unable to send keystroke: %s",
  141. val2str(rsp->ccode, completion_code_vals));
  142. return -1;
  143. }
  144. }
  145. return length;
  146. }
  147. static int
  148. tsol_keepalive(struct ipmi_intf *intf)
  149. {
  150. struct timeval end;
  151. gettimeofday(&end, 0);
  152. if (end.tv_sec - _start_keepalive.tv_sec <= 30) {
  153. return 0;
  154. }
  155. intf->keepalive(intf);
  156. gettimeofday(&_start_keepalive, 0);
  157. return 0;
  158. }
  159. static void
  160. print_escape_seq(struct ipmi_intf *intf)
  161. {
  162. lprintf(LOG_NOTICE,
  163. " %c. - terminate connection\n"
  164. " %c^Z - suspend ipmitool\n"
  165. " %c^X - suspend ipmitool, but don't restore tty on restart\n"
  166. " %c? - this message\n"
  167. " %c%c - send the escape character by typing it twice\n"
  168. " (Note that escapes are only recognized immediately after newline.)",
  169. intf->ssn_params.sol_escape_char,
  170. intf->ssn_params.sol_escape_char,
  171. intf->ssn_params.sol_escape_char,
  172. intf->ssn_params.sol_escape_char,
  173. intf->ssn_params.sol_escape_char,
  174. intf->ssn_params.sol_escape_char);
  175. }
  176. static int
  177. leave_raw_mode(void)
  178. {
  179. if (!_in_raw_mode) {
  180. return -1;
  181. } else if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) {
  182. lperror(LOG_ERR, "tcsetattr(stdin)");
  183. } else if (tcsetattr(fileno(stdout), TCSADRAIN, &_saved_tio) == -1) {
  184. lperror(LOG_ERR, "tcsetattr(stdout)");
  185. } else {
  186. _in_raw_mode = 0;
  187. }
  188. return 0;
  189. }
  190. static int
  191. enter_raw_mode(void)
  192. {
  193. struct termios tio;
  194. if (tcgetattr(fileno(stdout), &_saved_tio) < 0) {
  195. lperror(LOG_ERR, "tcgetattr failed");
  196. return -1;
  197. }
  198. tio = _saved_tio;
  199. if (_altterm) {
  200. tio.c_iflag &= (ISTRIP | IGNBRK);
  201. tio.c_cflag &= ~(CSIZE | PARENB | IXON | IXOFF | IXANY);
  202. tio.c_cflag |= (CS8 |CREAD) | (IXON|IXOFF|IXANY);
  203. tio.c_lflag &= 0;
  204. tio.c_cc[VMIN] = 1;
  205. tio.c_cc[VTIME] = 0;
  206. } else {
  207. tio.c_iflag |= IGNPAR;
  208. tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
  209. tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | IEXTEN);
  210. tio.c_oflag &= ~OPOST;
  211. tio.c_cc[VMIN] = 1;
  212. tio.c_cc[VTIME] = 0;
  213. }
  214. if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) {
  215. lperror(LOG_ERR, "tcsetattr(stdin)");
  216. } else if (tcsetattr(fileno(stdout), TCSADRAIN, &tio) < 0) {
  217. lperror(LOG_ERR, "tcsetattr(stdout)");
  218. } else {
  219. _in_raw_mode = 1;
  220. }
  221. return 0;
  222. }
  223. static void
  224. suspend_self(int restore_tty)
  225. {
  226. leave_raw_mode();
  227. kill(getpid(), SIGTSTP);
  228. if (restore_tty) {
  229. enter_raw_mode();
  230. }
  231. }
  232. static int
  233. do_inbuf_actions(struct ipmi_intf *intf, char *in_buff, int len)
  234. {
  235. static int in_esc = 0;
  236. static int last_was_cr = 1;
  237. int i;
  238. for(i = 0; i < len ;) {
  239. if (!in_esc) {
  240. if (last_was_cr &&
  241. (in_buff[i] == intf->ssn_params.sol_escape_char)) {
  242. in_esc = 1;
  243. memmove(in_buff, in_buff + 1, len - i - 1);
  244. len--;
  245. continue;
  246. }
  247. }
  248. if (in_esc) {
  249. if (in_buff[i] == intf->ssn_params.sol_escape_char) {
  250. in_esc = 0;
  251. i++;
  252. continue;
  253. }
  254. switch (in_buff[i]) {
  255. case '.':
  256. printf("%c. [terminated ipmitool]\n",
  257. intf->ssn_params.sol_escape_char);
  258. return -1;
  259. case 'Z' - 64:
  260. printf("%c^Z [suspend ipmitool]\n",
  261. intf->ssn_params.sol_escape_char);
  262. /* Restore tty back to raw */
  263. suspend_self(1);
  264. break;
  265. case 'X' - 64:
  266. printf("%c^X [suspend ipmitool]\n",
  267. intf->ssn_params.sol_escape_char);
  268. /* Don't restore to raw mode */
  269. suspend_self(0);
  270. break;
  271. case '?':
  272. printf("%c? [ipmitool help]\n",
  273. intf->ssn_params.sol_escape_char);
  274. print_escape_seq(intf);
  275. break;
  276. }
  277. memmove(in_buff, (in_buff + 1), (len - i - 1));
  278. len--;
  279. in_esc = 0;
  280. continue;
  281. }
  282. last_was_cr = (in_buff[i] == '\r' || in_buff[i] == '\n');
  283. i++;
  284. }
  285. return len;
  286. }
  287. static void
  288. do_terminal_cleanup(void)
  289. {
  290. if (_saved_winsize.ws_row > 0 && _saved_winsize.ws_col > 0) {
  291. ioctl(fileno(stdout), TIOCSWINSZ, &_saved_winsize);
  292. }
  293. leave_raw_mode();
  294. if (errno) {
  295. lprintf(LOG_ERR, "Exiting due to error %d -> %s",
  296. errno, strerror(errno));
  297. }
  298. }
  299. static void
  300. set_terminal_size(int rows, int cols)
  301. {
  302. struct winsize winsize;
  303. if (rows <= 0 || cols <= 0) {
  304. return;
  305. }
  306. /* save initial winsize */
  307. ioctl(fileno(stdout), TIOCGWINSZ, &_saved_winsize);
  308. /* set new winsize */
  309. winsize.ws_row = rows;
  310. winsize.ws_col = cols;
  311. ioctl(fileno(stdout), TIOCSWINSZ, &winsize);
  312. }
  313. static void
  314. print_tsol_usage(void)
  315. {
  316. struct winsize winsize;
  317. lprintf(LOG_NOTICE,
  318. "Usage: tsol [recvip] [port=NUM] [ro|rw] [rows=NUM] [cols=NUM] [altterm]");
  319. lprintf(LOG_NOTICE,
  320. " recvip Receiver IP Address [default=local]");
  321. lprintf(LOG_NOTICE,
  322. " port=NUM Receiver UDP Port [default=%d]",
  323. IPMI_TSOL_DEF_PORT);
  324. lprintf(LOG_NOTICE,
  325. " ro|rw Set Read-Only or Read-Write [default=rw]");
  326. ioctl(fileno(stdout), TIOCGWINSZ, &winsize);
  327. lprintf(LOG_NOTICE,
  328. " rows=NUM Set terminal rows [default=%d]",
  329. winsize.ws_row);
  330. lprintf(LOG_NOTICE,
  331. " cols=NUM Set terminal columns [default=%d]",
  332. winsize.ws_col);
  333. lprintf(LOG_NOTICE,
  334. " altterm Alternate terminal setup [default=off]");
  335. }
  336. int
  337. ipmi_tsol_main(struct ipmi_intf *intf, int argc, char **argv)
  338. {
  339. struct pollfd fds_wait[3], fds_data_wait[3], *fds;
  340. struct sockaddr_in sin, myaddr, *sa_in;
  341. socklen_t mylen;
  342. char *recvip = NULL;
  343. char in_buff[IPMI_BUF_SIZE];
  344. char out_buff[IPMI_BUF_SIZE * 8];
  345. char buff[IPMI_BUF_SIZE + 4];
  346. int fd_socket, result, i;
  347. int out_buff_fill, in_buff_fill;
  348. int ip1, ip2, ip3, ip4;
  349. int read_only = 0, rows = 0, cols = 0;
  350. int port = IPMI_TSOL_DEF_PORT;
  351. if (strlen(intf->name) < 3 || strncmp(intf->name, "lan", 3) != 0) {
  352. lprintf(LOG_ERR, "Error: Tyan SOL is only available over lan interface");
  353. return (-1);
  354. }
  355. for (i = 0; i<argc; i++) {
  356. if (sscanf(argv[i], "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) == 4) {
  357. /* not free'd ...*/
  358. /* recvip = strdup(argv[i]); */
  359. recvip = argv[i];
  360. } else if (sscanf(argv[i], "port=%d", &ip1) == 1) {
  361. port = ip1;
  362. } else if (sscanf(argv[i], "rows=%d", &ip1) == 1) {
  363. rows = ip1;
  364. } else if (sscanf(argv[i], "cols=%d", &ip1) == 1) {
  365. cols = ip1;
  366. } else if (strlen(argv[i]) == 2
  367. && strncmp(argv[i], "ro", 2) == 0) {
  368. read_only = 1;
  369. } else if (strlen(argv[i]) == 2
  370. && strncmp(argv[i], "rw", 2) == 0) {
  371. read_only = 0;
  372. } else if (strlen(argv[i]) == 7
  373. && strncmp(argv[i], "altterm", 7) == 0) {
  374. _altterm = 1;
  375. } else if (strlen(argv[i]) == 4
  376. && strncmp(argv[i], "help", 4) == 0) {
  377. print_tsol_usage();
  378. return 0;
  379. } else {
  380. lprintf(LOG_ERR, "Invalid tsol command: '%s'\n",
  381. argv[i]);
  382. print_tsol_usage();
  383. return (-1);
  384. }
  385. }
  386. /* create udp socket to receive the packet */
  387. memset(&sin, 0, sizeof(sin));
  388. sin.sin_family = AF_INET;
  389. sin.sin_port = htons(port);
  390. sa_in = (struct sockaddr_in *)&intf->session->addr;
  391. result = inet_pton(AF_INET, (const char *)intf->ssn_params.hostname,
  392. &sa_in->sin_addr);
  393. if (result <= 0) {
  394. struct hostent *host = gethostbyname((const char *)intf->ssn_params.hostname);
  395. if (host == NULL ) {
  396. lprintf(LOG_ERR, "Address lookup for %s failed",
  397. intf->ssn_params.hostname);
  398. return -1;
  399. }
  400. if (host->h_addrtype != AF_INET) {
  401. lprintf(LOG_ERR,
  402. "Address lookup for %s failed. Got %s, expected IPv4 address.",
  403. intf->ssn_params.hostname,
  404. (host->h_addrtype == AF_INET6) ? "IPv6" : "Unknown");
  405. return (-1);
  406. }
  407. sa_in->sin_family = host->h_addrtype;
  408. memcpy(&sa_in->sin_addr, host->h_addr_list[0], host->h_length);
  409. }
  410. fd_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
  411. if (fd_socket < 0) {
  412. lprintf(LOG_ERR, "Can't open port %d", port);
  413. return -1;
  414. }
  415. if (bind(fd_socket, (struct sockaddr *)&sin, sizeof(sin)) == (-1)) {
  416. lprintf(LOG_ERR, "Failed to bind socket.");
  417. close(fd_socket);
  418. return -1;
  419. }
  420. /*
  421. * retrieve local IP address if not supplied on command line
  422. */
  423. if (recvip == NULL) {
  424. /* must connect first */
  425. result = intf->open(intf);
  426. if (result < 0) {
  427. close(fd_socket);
  428. return -1;
  429. }
  430. mylen = sizeof(myaddr);
  431. if (getsockname(intf->fd, (struct sockaddr *)&myaddr, &mylen) < 0) {
  432. lperror(LOG_ERR, "getsockname failed");
  433. close(fd_socket);
  434. return -1;
  435. }
  436. recvip = inet_ntoa(myaddr.sin_addr);
  437. if (recvip == NULL) {
  438. lprintf(LOG_ERR, "Unable to find local IP address");
  439. close(fd_socket);
  440. return -1;
  441. }
  442. }
  443. printf("[Starting %sSOL with receiving address %s:%d]\n",
  444. read_only ? "Read-only " : "", recvip, port);
  445. set_terminal_size(rows, cols);
  446. enter_raw_mode();
  447. /*
  448. * talk to smdc to start Console redirect - IP address and port as parameter
  449. * ipmitool -I lan -H 192.168.168.227 -U Administrator raw 0x30 0x06 0xC0 0xA8 0xA8 0x78 0x1A 0x0A
  450. */
  451. result = ipmi_tsol_start(intf, recvip, port);
  452. if (result < 0) {
  453. lprintf(LOG_ERR, "Error starting SOL");
  454. close(fd_socket);
  455. return (-1);
  456. }
  457. printf("[SOL Session operational. Use %c? for help]\n",
  458. intf->ssn_params.sol_escape_char);
  459. gettimeofday(&_start_keepalive, 0);
  460. fds_wait[0].fd = fd_socket;
  461. fds_wait[0].events = POLLIN;
  462. fds_wait[0].revents = 0;
  463. fds_wait[1].fd = fileno(stdin);
  464. fds_wait[1].events = POLLIN;
  465. fds_wait[1].revents = 0;
  466. fds_wait[2].fd = -1;
  467. fds_wait[2].events = 0;
  468. fds_wait[2].revents = 0;
  469. fds_data_wait[0].fd = fd_socket;
  470. fds_data_wait[0].events = POLLIN | POLLOUT;
  471. fds_data_wait[0].revents = 0;
  472. fds_data_wait[1].fd = fileno(stdin);
  473. fds_data_wait[1].events = POLLIN;
  474. fds_data_wait[1].revents = 0;
  475. fds_data_wait[2].fd = fileno(stdout);
  476. fds_data_wait[2].events = POLLOUT;
  477. fds_data_wait[2].revents = 0;
  478. out_buff_fill = 0;
  479. in_buff_fill = 0;
  480. fds = fds_wait;
  481. for (;;) {
  482. result = poll(fds, 3, 15 * 1000);
  483. if (result < 0) {
  484. break;
  485. }
  486. /* send keepalive packet */
  487. tsol_keepalive(intf);
  488. if ((fds[0].revents & POLLIN) && (sizeof(out_buff) > out_buff_fill)) {
  489. socklen_t sin_len = sizeof(sin);
  490. int buff_size = sizeof(buff);
  491. if ((sizeof(out_buff) - out_buff_fill + 4) < buff_size) {
  492. buff_size = (sizeof(out_buff) - out_buff_fill) + 4;
  493. if ((buff_size - 4) <= 0) {
  494. buff_size = 0;
  495. }
  496. }
  497. result = recvfrom(fd_socket, buff,
  498. buff_size, 0,
  499. (struct sockaddr *)&sin, &sin_len);
  500. /* read the data from udp socket,
  501. * skip some bytes in the head
  502. */
  503. if ((result - 4) > 0) {
  504. int length = result - 4;
  505. memcpy(out_buff + out_buff_fill, buff + 4, length);
  506. out_buff_fill += length;
  507. }
  508. }
  509. if ((fds[1].revents & POLLIN) && (sizeof(in_buff) > in_buff_fill)) {
  510. /* Read from keyboard */
  511. result = read(fileno(stdin), in_buff + in_buff_fill,
  512. sizeof(in_buff) - in_buff_fill);
  513. if (result > 0) {
  514. int bytes;
  515. bytes = do_inbuf_actions(intf,
  516. in_buff + in_buff_fill, result);
  517. if (bytes < 0) {
  518. result = ipmi_tsol_stop(intf, recvip, port);
  519. do_terminal_cleanup();
  520. return result;
  521. }
  522. if (read_only) {
  523. bytes = 0;
  524. }
  525. in_buff_fill += bytes;
  526. }
  527. }
  528. if ((fds[2].revents & POLLOUT) && out_buff_fill) {
  529. /* To screen */
  530. result = write(fileno(stdout), out_buff, out_buff_fill);
  531. if (result > 0) {
  532. out_buff_fill -= result;
  533. if (out_buff_fill) {
  534. memmove(out_buff, out_buff + result, out_buff_fill);
  535. }
  536. }
  537. }
  538. if ((fds[0].revents & POLLOUT) && in_buff_fill) {
  539. /*
  540. * translate key and send that to SMDC using IPMI
  541. * ipmitool -I lan -H 192.168.168.227 -U Administrator raw 0x30 0x03 0x04 0x1B 0x5B 0x43
  542. */
  543. result = ipmi_tsol_send_keystroke(intf,
  544. in_buff, __min(in_buff_fill, 14));
  545. if (result > 0) {
  546. gettimeofday(&_start_keepalive, 0);
  547. in_buff_fill -= result;
  548. if (in_buff_fill) {
  549. memmove(in_buff, in_buff + result, in_buff_fill);
  550. }
  551. }
  552. }
  553. fds = (in_buff_fill || out_buff_fill )?
  554. fds_data_wait : fds_wait;
  555. }
  556. return 0;
  557. }