1 /*
2 * ipmi_serial_bmc_emu.c
3 *
4 * An emulator for various IPMI BMCs that sit on a serial port
5 *
6 * Author: MontaVista Software, Inc.
7 * Corey Minyard <minyard@mvista.com>
8 * source@mvista.com
9 *
10 * Copyright 2006 MontaVista Software Inc.
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version.
16 *
17 *
18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * You should have received a copy of the GNU General Public License along
30 * with this program; if not, write to the Free Software Foundation, Inc.,
31 * 675 Mass Ave, Cambridge, MA 02139, USA. */
32
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <netinet/tcp.h>
38 #include <netdb.h>
39 #include <errno.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <sys/select.h>
45 #include <readline/readline.h>
46 #include <readline/history.h>
47
48 #define _GNU_SOURCE
49 #include <getopt.h>
50
51 #define IPMB_MAX_MSG_LENGTH 32
52 #define IPMI_MAX_MSG_LENGTH 36
53
54 struct msg {
55 unsigned char data[IPMB_MAX_MSG_LENGTH];
56 unsigned int data_len;
57 struct msg *next;
58 };
59
60 struct msg_info;
61
62 struct codec {
63 char *name;
64 void (*handle_char)(unsigned char ch, struct msg_info *mi);
65 void (*send)(unsigned char *msg, unsigned int msg_len,
66 struct msg_info *mi);
67 void *(*setup)(void);
68 void (*handle_event)(struct msg *emsg, struct msg_info *mi);
69 void (*handle_ipmb)(struct msg *emsg, struct msg_info *mi);
70 };
71
72 struct oem_handler {
73 char *name;
74 int (*handler)(const unsigned char *msg, unsigned int len,
75 struct msg_info *mi,
76 unsigned char *rsp, unsigned int *rsp_len);
77 void (*init)(struct msg_info *mi);
78 };
79
80 #define EVENT_BUFFER_GLOBAL_ENABLE (1 << 2)
81 #define EVENT_LOG_GLOBAL_ENABLE (1 << 3)
82 #define SUPPORTED_GLOBAL_ENABLES (EVENT_BUFFER_GLOBAL_ENABLE | \
83 EVENT_LOG_GLOBAL_ENABLE)
84
85 struct msg_info {
86 /* General info, set by main code, not formatters. */
87 void *info;
88 int sock;
89 struct codec *codec;
90 struct oem_handler *oem;
91
92 /* Queues for events and IPMB messages. */
93 struct msg *ipmb_q, *ipmb_q_tail;
94 struct msg *event_q, *event_q_tail;
95
96 void *oem_info;
97
98 /* Settings */
99 int echo;
100 unsigned char my_ipmb;
101 int debug;
102 int do_attn;
103 unsigned char attn_chars[8];
104 unsigned int attn_chars_len;
105 char global_enables;
106
107 /* Info from the recv message. */
108 unsigned char netfn;
109 unsigned char dest;
110 unsigned char seq;
111 unsigned char rsAddr;
112 unsigned char rqAddr;
113 unsigned char rqLUN;
114 unsigned char rsLUN;
115 unsigned char cmd;
116 };
117
118 static void socket_send(const unsigned char *data, unsigned int len,
119 struct msg_info *mi);
120 static void handle_msg(const unsigned char *msg, unsigned int len,
121 struct msg_info *mi);
122 static void handle_ipmb_msg(const unsigned char *msg, unsigned int len,
123 struct msg_info *mi, struct msg_info *top_mi);
124
125 static unsigned char hex2char[16] = {
126 '0', '1', '2', '3', '4', '5', '6', '7',
127 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
128 };
129
fromhex(unsigned char c)130 static int fromhex(unsigned char c)
131 {
132 if (isdigit(c))
133 return c - '0';
134 else if (isxdigit(c))
135 return tolower(c) - 'a' + 10;
136 else
137 return -EINVAL;
138 }
139
140 static unsigned char
ipmb_checksum(const unsigned char * data,int size)141 ipmb_checksum(const unsigned char *data, int size)
142 {
143 unsigned char csum = 0;
144
145 for (; size > 0; size--, data++)
146 csum += *data;
147
148 return -csum;
149 }
150
151 static int
unformat_ipmb_msg(const unsigned char * msg,unsigned int * pos,unsigned int * len,struct msg_info * mi)152 unformat_ipmb_msg(const unsigned char *msg, unsigned int *pos,
153 unsigned int *len, struct msg_info *mi)
154 {
155 msg += *pos;
156
157 if (*len < 7) {
158 fprintf(stderr, "Message too short\n");
159 return -EINVAL;
160 }
161
162 if (ipmb_checksum(msg, *len) != 0) {
163 fprintf(stderr, "Message checksum failure\n");
164 return -EINVAL;
165 }
166 (*len)--;
167
168 mi->rsAddr = msg[0];
169 mi->netfn = msg[1] >> 2;
170 mi->rsLUN = msg[1] & 3;
171 mi->rqAddr = msg[3];
172 mi->seq = msg[4] >> 2;
173 mi->rqLUN = msg[4] & 3;
174 mi->cmd = msg[5];
175 *pos += 6;
176 *len -= 6;
177
178 return 0;
179 }
180
181 static unsigned int
format_ipmb_rsp(unsigned char * msg,const unsigned char * omsg,unsigned int omsg_len,struct msg_info * mi)182 format_ipmb_rsp(unsigned char *msg, const unsigned char *omsg,
183 unsigned int omsg_len, struct msg_info *mi)
184 {
185 unsigned int msg_len;
186
187 msg[0] = mi->rqAddr;
188 msg[1] = (mi->netfn << 2) | mi->rqLUN;
189 msg[2] = ipmb_checksum(msg, 2);
190 msg[3] = mi->rsAddr;
191 msg[4] = (mi->seq << 2) | mi->rsLUN;
192 msg[5] = mi->cmd;
193 memcpy(msg+6, omsg, omsg_len);
194 msg_len = omsg_len + 6;
195 msg[msg_len] = ipmb_checksum(msg + 3, msg_len - 3);
196 msg_len++;
197
198 return msg_len;
199 }
200
201 static void
queue_ipmb(struct msg * imsg,struct msg_info * mi)202 queue_ipmb(struct msg *imsg, struct msg_info *mi)
203 {
204 imsg->next = NULL;
205 if (mi->ipmb_q_tail)
206 mi->ipmb_q_tail->next = imsg;
207 else
208 mi->ipmb_q = imsg;
209 mi->ipmb_q_tail = imsg;
210 if (mi->do_attn)
211 socket_send(mi->attn_chars, mi->attn_chars_len, mi);
212 }
213
214 static void
queue_event(struct msg * emsg,struct msg_info * mi)215 queue_event(struct msg *emsg, struct msg_info *mi)
216 {
217 emsg->next = NULL;
218 if (mi->event_q_tail)
219 mi->event_q_tail->next = emsg;
220 else
221 mi->event_q = emsg;
222 mi->event_q_tail = emsg;
223 if (mi->do_attn)
224 socket_send(mi->attn_chars, mi->attn_chars_len, mi);
225 }
226
227 /***********************************************************************
228 *
229 * Radisys ASCII codec.
230 *
231 ***********************************************************************/
232
233 #define RA_MAX_CHARS_SIZE (((IPMI_MAX_MSG_LENGTH + 1) * 3) + 4)
234
235 struct ra_data {
236 unsigned char recv_chars[RA_MAX_CHARS_SIZE];
237 unsigned int recv_chars_len;
238 int recv_chars_too_many;
239 };
240
ra_format_msg(const unsigned char * msg,unsigned int msg_len,struct msg_info * mi)241 static void ra_format_msg(const unsigned char *msg, unsigned int msg_len,
242 struct msg_info *mi)
243 {
244 unsigned int i;
245 unsigned int len;
246 unsigned char c[RA_MAX_CHARS_SIZE];
247
248 len = 0;
249 for (i = 0; i < msg_len; i++) {
250 c[len] = hex2char[msg[i] >> 4];
251 len++;
252 c[len] = hex2char[msg[i] & 0xf];
253 len++;
254 }
255 c[len] = 0x0d;
256 len++;
257
258 socket_send(c, len, mi);
259 }
260
261 static void
ra_ipmb_handler(struct msg * imsg,struct msg_info * mi)262 ra_ipmb_handler(struct msg *imsg, struct msg_info *mi)
263 {
264 ra_format_msg(imsg->data, imsg->data_len, mi);
265 free(imsg);
266 }
267
268 /*
269 * Called when the '0x0d' is seen.
270 */
ra_unformat_msg(unsigned char * r,unsigned int len,struct msg_info * mi)271 static int ra_unformat_msg(unsigned char *r, unsigned int len,
272 struct msg_info *mi)
273 {
274 unsigned char o[IPMI_MAX_MSG_LENGTH];
275 unsigned int p = 0;
276 unsigned int i = 0;
277 int rv;
278
279 while (p < len) {
280 rv = fromhex(r[p]);
281 if (rv < 0)
282 return rv;
283 o[i] = rv << 4;
284 p++;
285 if (p >= len)
286 return -EINVAL;
287 rv = fromhex(r[p]);
288 if (rv < 0)
289 return rv;
290 o[i] |= rv;
291 p++;
292 i++;
293 }
294
295 p = 0;
296 rv = unformat_ipmb_msg(o, &p, &i, mi);
297 if (rv)
298 return rv;
299 if ((mi->rsAddr == mi->my_ipmb) || (mi->rsAddr == 1))
300 handle_msg(o + p, i, mi);
301 else
302 handle_ipmb_msg(o + p, i, mi, mi);
303 return 0;
304 }
305
306 static void
ra_handle_char(unsigned char ch,struct msg_info * mi)307 ra_handle_char(unsigned char ch, struct msg_info *mi)
308 {
309 struct ra_data *info = mi->info;
310 unsigned int len = info->recv_chars_len;
311 unsigned char *r;
312 int rv;
313
314 if (ch == 0x0d) {
315 /* End of command, handle it. */
316 if (info->recv_chars_too_many) {
317 /* Input data overrun. */
318 fprintf(stderr, "Data overrun\n");
319 info->recv_chars_too_many = 0;
320 info->recv_chars_len = 0;
321 return;
322 }
323 rv = ra_unformat_msg(info->recv_chars, info->recv_chars_len, mi);
324 info->recv_chars_too_many = 0;
325 info->recv_chars_len = 0;
326 if (rv) {
327 /* Bad input data. */
328 fprintf(stderr, "Bad input data\n");
329 return;
330 }
331 return;
332 }
333
334 if (info->recv_chars_too_many)
335 return;
336
337 r = info->recv_chars;
338
339 if (len >= sizeof(info->recv_chars)) {
340 info->recv_chars_too_many = 1;
341 } else if ((len > 0) && isspace(r[len-1]) && isspace(ch)) {
342 /* Ignore multiple spaces together. */
343 } else {
344 r[len] = ch;
345 info->recv_chars_len++;
346 }
347 }
348
349 static void
ra_send(unsigned char * omsg,unsigned int omsg_len,struct msg_info * mi)350 ra_send(unsigned char *omsg, unsigned int omsg_len, struct msg_info *mi)
351 {
352 unsigned char msg[IPMI_MAX_MSG_LENGTH + 7];
353 unsigned int msg_len;
354
355 msg_len = format_ipmb_rsp(msg, omsg, omsg_len, mi);
356
357 ra_format_msg(msg, msg_len, mi);
358 }
359
360 static void *
ra_setup(void)361 ra_setup(void)
362 {
363 struct ra_data *info;
364
365 info = malloc(sizeof(*info));
366 if (!info)
367 return NULL;
368
369 info->recv_chars_len = 0;
370 info->recv_chars_too_many = 0;
371 return info;
372 }
373
374 /***********************************************************************
375 *
376 * Direct Mode codec.
377 *
378 ***********************************************************************/
379
380 #define DM_START_CHAR 0xA0
381 #define DM_STOP_CHAR 0xA5
382 #define DM_PACKET_HANDSHAKE 0xA6
383 #define DM_DATA_ESCAPE_CHAR 0xAA
384
385 struct dm_data {
386 unsigned char recv_msg[IPMI_MAX_MSG_LENGTH + 4];
387 unsigned int recv_msg_len;
388 int recv_msg_too_many;
389 int in_recv_msg;
390 int in_escape;
391 };
392
393 static void
dm_handle_msg(unsigned char * msg,unsigned int len,struct msg_info * mi)394 dm_handle_msg(unsigned char *msg, unsigned int len, struct msg_info *mi)
395 {
396 int rv;
397 unsigned int pos;
398
399 pos = 0;
400 rv = unformat_ipmb_msg(msg, &pos, &len, mi);
401 if (rv)
402 return;
403 handle_msg(msg + pos, len, mi);
404 }
405
406 static void
dm_handle_char(unsigned char ch,struct msg_info * mi)407 dm_handle_char(unsigned char ch, struct msg_info *mi)
408 {
409 struct dm_data *info = mi->info;
410 unsigned int len = info->recv_msg_len;
411 unsigned char c;
412
413 switch (ch) {
414 case DM_START_CHAR:
415 if (info->in_recv_msg)
416 fprintf(stderr, "Msg started in the middle of another\n");
417 info->in_recv_msg = 1;
418 info->recv_msg_len = 0;
419 info->recv_msg_too_many = 0;
420 info->in_escape = 0;
421 break;
422
423 case DM_STOP_CHAR:
424 if (!info->in_recv_msg)
425 fprintf(stderr, "Empty message\n");
426 else if (info->in_escape) {
427 info->in_recv_msg = 0;
428 fprintf(stderr, "Message ended in escape\n");
429 } else if (info->recv_msg_too_many) {
430 fprintf(stderr, "Message too long\n");
431 info->in_recv_msg = 0;
432 } else {
433 dm_handle_msg(info->recv_msg, info->recv_msg_len, mi);
434 info->in_recv_msg = 0;
435 }
436 info->in_escape = 0;
437
438 c = DM_PACKET_HANDSHAKE;
439 socket_send(&c, 1, mi);
440 break;
441
442 case DM_PACKET_HANDSHAKE:
443 info->in_escape = 0;
444 break;
445
446 case DM_DATA_ESCAPE_CHAR:
447 if (!info->recv_msg_too_many)
448 info->in_escape = 1;
449 break;
450
451 default:
452 if (!info->in_recv_msg)
453 /* Ignore characters outside of messages. */
454 break;
455
456 if (info->in_escape) {
457 info->in_escape = 0;
458 switch (ch) {
459 case 0xB0: ch = DM_START_CHAR; break;
460 case 0xB5: ch = DM_STOP_CHAR; break;
461 case 0xB6: ch = DM_PACKET_HANDSHAKE; break;
462 case 0xBA: ch = DM_DATA_ESCAPE_CHAR; break;
463 case 0x3B: ch = 0x1b; break;
464 default:
465 fprintf(stderr, "Invalid escape char: 0x%x\n", ch);
466 info->recv_msg_too_many = 1;
467 return;
468 }
469 }
470
471 if (!info->recv_msg_too_many) {
472 if (len >= sizeof(info->recv_msg)) {
473 info->recv_msg_too_many = 1;
474 break;
475 }
476
477 info->recv_msg[len] = ch;
478 info->recv_msg_len++;
479 }
480 break;
481 }
482 }
483
484 static void
dm_send(unsigned char * omsg,unsigned int omsg_len,struct msg_info * mi)485 dm_send(unsigned char *omsg, unsigned int omsg_len, struct msg_info *mi)
486 {
487 unsigned int i;
488 unsigned int len = 0;
489 unsigned char c[(IPMI_MAX_MSG_LENGTH + 7) * 2];
490 unsigned char msg[IPMI_MAX_MSG_LENGTH + 7];
491 unsigned int msg_len;
492
493 msg_len = format_ipmb_rsp(msg, omsg, omsg_len, mi);
494
495 c[len++] = 0xA0;
496 for (i = 0; i < msg_len; i++) {
497 switch (msg[i]) {
498 case 0xA0:
499 c[len++] = 0xAA;
500 c[len++] = 0xB0;
501 break;
502
503 case 0xA5:
504 c[len++] = 0xAA;
505 c[len++] = 0xB5;
506 break;
507
508 case 0xA6:
509 c[len++] = 0xAA;
510 c[len++] = 0xB6;
511 break;
512
513 case 0xAA:
514 c[len++] = 0xAA;
515 c[len++] = 0xBA;
516 break;
517
518 case 0x1B:
519 c[len++] = 0xAA;
520 c[len++] = 0x3B;
521 break;
522
523 default:
524 c[len++] = msg[i];
525 }
526
527 }
528 c[len++] = 0xA5;
529
530 socket_send(c, len, mi);
531 }
532
533 static void *
dm_setup(void)534 dm_setup(void)
535 {
536 struct dm_data *info;
537
538 info = malloc(sizeof(*info));
539 if (!info)
540 return NULL;
541 memset(info, 0, sizeof(*info));
542 return info;
543 }
544
545
546 /***********************************************************************
547 *
548 * Terminal Mode codec.
549 *
550 ***********************************************************************/
551
552 #define TM_MAX_CHARS_SIZE (((IPMI_MAX_MSG_LENGTH + 1) * 3) + 4)
553
554 struct tm_data {
555 unsigned char recv_chars[TM_MAX_CHARS_SIZE];
556 unsigned int recv_chars_len;
557 int recv_chars_too_many;
558 };
559
tm_format_msg(const unsigned char * msg,unsigned int msg_len,struct msg_info * mi)560 static void tm_format_msg(const unsigned char *msg, unsigned int msg_len,
561 struct msg_info *mi)
562 {
563 unsigned int i;
564 unsigned int len;
565 unsigned char c[TM_MAX_CHARS_SIZE];
566 unsigned char t;
567
568 len = 0;
569 c[len] = '[';
570 len++;
571
572 t = mi->netfn << 2 | mi->rqLUN;
573 c[len] = hex2char[t >> 4];
574 len++;
575 c[len] = hex2char[t & 0xf];
576 len++;
577
578 /*
579 * Insert the sequence number and bridge bits. Bridge bits
580 * are always zero.
581 */
582 t = mi->seq << 2;
583 c[len] = hex2char[t >> 4];
584 len++;
585 c[len] = hex2char[t & 0xf];
586 len++;
587
588 c[len] = hex2char[mi->cmd >> 4];
589 len++;
590 c[len] = hex2char[mi->cmd & 0xf];
591 len++;
592
593 /* Now the rest of the message. */
594 for (i = 0; ; ) {
595 c[len] = hex2char[msg[i] >> 4];
596 len++;
597 c[len] = hex2char[msg[i] & 0xf];
598 len++;
599 i++;
600 if (i == msg_len)
601 break;
602 c[len] = ' ';
603 len++;
604 }
605 c[len] = ']';
606 len++;
607 c[len] = 0x0a;
608 len++;
609
610 socket_send(c, len, mi);
611 }
612
613 /*
614 * Called when the ']' is seen, the leading '[' is removed, too. We
615 * get this with a leading space and no more than one space between
616 * items.
617 */
tm_unformat_msg(unsigned char * r,unsigned int len,struct msg_info * mi)618 static int tm_unformat_msg(unsigned char *r, unsigned int len,
619 struct msg_info *mi)
620 {
621 unsigned char o[IPMI_MAX_MSG_LENGTH];
622 unsigned int p = 0;
623 unsigned int i = 0;
624 int rv;
625
626 #define SKIP_SPACE if (isspace(r[p])) p++
627 #define ENSURE_MORE if (p >= len) return -EINVAL
628
629 SKIP_SPACE;
630 while (p < len) {
631 if (i >= sizeof(o))
632 return -EFBIG;
633 ENSURE_MORE;
634 rv = fromhex(r[p]);
635 if (rv < 0)
636 return rv;
637 o[i] = rv << 4;
638 p++;
639 ENSURE_MORE;
640 rv = fromhex(r[p]);
641 if (rv < 0)
642 return rv;
643 o[i] |= rv;
644 p++;
645 i++;
646 SKIP_SPACE;
647 }
648
649 if (i < 3)
650 return -EINVAL;
651
652 mi->netfn = o[0] >> 2;
653 mi->rqLUN = o[0] & 3;
654 mi->seq = o[1] >> 2;
655 mi->cmd = o[2];
656 handle_msg(o+3, i-3, mi);
657 return 0;
658 #undef SKIP_SPACE
659 #undef ENSURE_MORE
660 }
661
662 static void
tm_handle_char(unsigned char ch,struct msg_info * mi)663 tm_handle_char(unsigned char ch, struct msg_info *mi)
664 {
665 struct tm_data *info = mi->info;
666 unsigned int len = info->recv_chars_len;
667 unsigned char *r;
668 int rv;
669
670 if (ch == '[') {
671 /*
672 * Start of a command. Note that if a command is
673 * already in progress (len != 0) we abort it.
674 */
675 if (len != 0)
676 fprintf(stderr, "Msg started in the middle of another\n");
677
678 /* Convert the leading '[' to a space, that's innocuous. */
679 info->recv_chars[0] = ' ';
680 info->recv_chars_len = 1;
681 info->recv_chars_too_many = 0;
682 return;
683 }
684
685 if (len == 0)
686 /* Ignore everything outside [ ]. */
687 return;
688
689 if (ch == ']') {
690 /* End of command, handle it. */
691 if (info->recv_chars_too_many) {
692 /* Input data overrun. */
693 fprintf(stderr, "Data overrun\n");
694 info->recv_chars_too_many = 0;
695 info->recv_chars_len = 0;
696 return;
697 }
698 rv = tm_unformat_msg(info->recv_chars, info->recv_chars_len, mi);
699 info->recv_chars_too_many = 0;
700 info->recv_chars_len = 0;
701 if (rv) {
702 /* Bad input data. */
703 fprintf(stderr, "Bad input data\n");
704 return;
705 }
706 return;
707 }
708
709 if (info->recv_chars_too_many)
710 return;
711
712 r = info->recv_chars;
713
714 if (len >= sizeof(info->recv_chars)) {
715 info->recv_chars_too_many = 1;
716 } else if ((len > 0) && isspace(r[len-1]) && isspace(ch)) {
717 /* Ignore multiple spaces together. */
718 } else {
719 r[len] = ch;
720 info->recv_chars_len++;
721 }
722 }
723
724 static void
tm_send(unsigned char * msg,unsigned int msg_len,struct msg_info * mi)725 tm_send(unsigned char *msg, unsigned int msg_len, struct msg_info *mi)
726 {
727 tm_format_msg(msg, msg_len, mi);
728 }
729
730 static void *
tm_setup(void)731 tm_setup(void)
732 {
733 struct tm_data *info;
734
735 info = malloc(sizeof(*info));
736 if (!info)
737 return NULL;
738
739 info->recv_chars_len = 0;
740 info->recv_chars_too_many = 0;
741 return info;
742 }
743
744
745 /***********************************************************************
746 *
747 * codec structure
748 *
749 ***********************************************************************/
750 struct codec codecs[] = {
751 { "TerminalMode",
752 tm_handle_char, tm_send, tm_setup, queue_event, queue_ipmb },
753 { "Direct",
754 dm_handle_char, dm_send, dm_setup, queue_event, queue_ipmb },
755 { "RadisysAscii",
756 ra_handle_char, ra_send, ra_setup, NULL, ra_ipmb_handler },
757 { NULL }
758 };
759
760
761 static void
socket_send(const unsigned char * data,unsigned int len,struct msg_info * mi)762 socket_send(const unsigned char *data, unsigned int len, struct msg_info *mi)
763 {
764 int rv;
765 unsigned int i;
766
767 if (mi->debug > 0) {
768 printf("Sock send:");
769 for (i=0; i<len; i++) {
770 if ((i % 16) == 0)
771 printf("\n ");
772 printf(" %2.2x(%c)", data[i], isprint(data[i]) ? data[i] : ' ');
773 }
774 printf("\n");
775 }
776
777 restart:
778 rv = write(mi->sock, data, len);
779 if (rv < 0) {
780 perror("write");
781 return;
782 } else if (((unsigned int) rv) < len) {
783 len -= rv;
784 data += rv;
785 goto restart;
786 }
787 }
788
789 #define IPMI_APP_NETFN 6
790 #define IPMI_GET_DEV_ID_CMD 0x01
791 #define IPMI_GET_DEVICE_GUID_CMD 0x08
792 #define IPMI_SET_BMC_GLOBAL_ENABLES_CMD 0x2e
793 #define IPMI_GET_BMC_GLOBAL_ENABLES_CMD 0x2f
794 #define IPMI_GET_MSG_FLAGS_CMD 0x31
795 #define IPMI_GET_MSG_CMD 0x33
796 #define IPMI_SEND_MSG_CMD 0x34
797 #define IPMI_READ_EVENT_MSG_CMD 0x35
798 #define IPMI_OEM_NETFN 0x2e
799
800 static unsigned char ipmb_devid_data[] = {
801 0x00, 0x01, 0x00, 0x48, 0x02, 0x9f, 0xaa, 0x01, 0x00, 0x23, 0x00,
802 0x00, 0x11, 0x00, 0x04
803 };
804
805 static void
handle_ipmb_msg(const unsigned char * msg,unsigned int len,struct msg_info * mi,struct msg_info * top_mi)806 handle_ipmb_msg(const unsigned char *msg, unsigned int len,
807 struct msg_info *mi, struct msg_info *top_mi)
808 {
809 struct msg *imsg;
810 unsigned char rsp[IPMI_MAX_MSG_LENGTH];
811 unsigned int rsp_len;
812 unsigned int i;
813
814 imsg = malloc(sizeof(*imsg));
815 if (!imsg)
816 return;
817
818 if (top_mi->debug > 0) {
819 printf("Recv IPMB Msg (%x:%x):", mi->netfn, mi->cmd);
820 for (i=0; i<len; i++) {
821 if ((i % 16) == 0)
822 printf("\n ");
823 printf(" %2.2x(%c)", msg[i], isprint(msg[i]) ? msg[i] : ' ');
824 }
825 printf("\n");
826 }
827
828 if (mi->netfn == IPMI_APP_NETFN) {
829 switch (mi->cmd) {
830 case IPMI_GET_DEV_ID_CMD:
831 rsp[0] = 0;
832 memcpy(rsp+1, ipmb_devid_data, sizeof(ipmb_devid_data));
833 rsp_len = sizeof(ipmb_devid_data) + 1;
834 break;
835 default:
836 goto invalid_msg;
837 }
838 } else
839 goto invalid_msg;
840
841 send_rsp:
842 /* Convert to response. */
843 mi->netfn |= 1;
844 imsg->data_len = format_ipmb_rsp(imsg->data, rsp, rsp_len, mi);
845 top_mi->codec->handle_ipmb(imsg, top_mi);
846 return;
847
848 invalid_msg:
849 rsp[0] = 0xc1;
850 rsp_len = 1;
851 goto send_rsp;
852 }
853
854 static unsigned char devid_data[] = {
855 0x20, 0x01, 0x00, 0x48, 0x02, 0x9f, 0x22, 0x03, 0x00, 0x11, 0x43,
856 0x00, 0x11, 0x00, 0x04
857 };
858
859 static unsigned char guid_data[] = {
860 0x00, 0x01, 0x00, 0x48, 0x02, 0x9f, 0xaa, 0x01,
861 0x00, 0x23, 0x00, 0x00, 0x11, 0x00, 0x04, 0x99
862 };
863
864 static void
handle_msg(const unsigned char * msg,unsigned int len,struct msg_info * mi)865 handle_msg(const unsigned char *msg, unsigned int len, struct msg_info *mi)
866 {
867 unsigned int i;
868 unsigned char rsp[IPMI_MAX_MSG_LENGTH];
869 unsigned int rsp_len;
870 struct msg *m;
871 int rv;
872 struct msg_info nmi;
873 unsigned int p;
874
875 if (mi->debug > 0) {
876 printf("Recv Msg (%x:%x):", mi->netfn, mi->cmd);
877 for (i=0; i<len; i++) {
878 if ((i % 16) == 0)
879 printf("\n ");
880 printf(" %2.2x(%c)", msg[i], isprint(msg[i]) ? msg[i] : ' ');
881 }
882 printf("\n");
883 }
884
885 if (mi->oem) {
886 rv = mi->oem->handler(msg, len, mi, rsp, &rsp_len);
887 if (!rv)
888 goto send_rsp;
889 }
890
891 if (mi->netfn == IPMI_APP_NETFN) {
892 switch (mi->cmd) {
893 case IPMI_GET_DEV_ID_CMD:
894 rsp[0] = 0;
895 memcpy(rsp+1, devid_data, sizeof(devid_data));
896 rsp_len = sizeof(devid_data) + 1;
897 break;
898
899 case IPMI_GET_DEVICE_GUID_CMD:
900 rsp[0] = 0;
901 memcpy(rsp+1, guid_data, sizeof(guid_data));
902 rsp_len = sizeof(guid_data) + 1;
903 break;
904
905 case IPMI_GET_MSG_FLAGS_CMD:
906 rsp[0] = 0;
907 rsp[1] = 0;
908 if (mi->event_q)
909 rsp[1] |= 2;
910 if (mi->ipmb_q)
911 rsp[1] |= 1;
912 rsp_len = 2;
913 break;
914
915 case IPMI_GET_MSG_CMD:
916 if (!mi->ipmb_q) {
917 rsp[0] = 0x80;
918 rsp_len = 1;
919 break;
920 }
921 m = mi->ipmb_q;
922 mi->ipmb_q = m->next;
923 if (!mi->ipmb_q)
924 mi->ipmb_q_tail = NULL;
925
926 rsp[0] = 0;
927 rsp[1] = 0; /* Channel # */
928 /* Note we don't put our slave address in the response, as
929 that is what get smg expects. */
930 memcpy(rsp + 2, m->data + 1, m->data_len - 1);
931 rsp_len = 2 + m->data_len - 1;
932 free(m);
933 break;
934
935 case IPMI_SEND_MSG_CMD:
936 if (msg[0] != 0) {
937 rsp[0] = 0xcc;
938 rsp_len = 1;
939 break;
940 }
941 p = 1;
942 len -= 1;
943 rv = unformat_ipmb_msg(msg, &p, &len, &nmi);
944 if (rv) {
945 rsp[0] = 0xcc;
946 rsp_len = 1;
947 break;
948 }
949 if (nmi.netfn & 1) {
950 /* Ignore responses */
951 rsp[0] = 0;
952 rsp_len = 1;
953 break;
954 }
955 handle_ipmb_msg(msg+p, len, &nmi, mi);
956 rsp[0] = 0;
957 rsp_len = 1;
958 break;
959
960 case IPMI_SET_BMC_GLOBAL_ENABLES_CMD:
961 if (len < 1) {
962 rsp[0] = 0xcc;
963 rsp_len = 1;
964 break;
965 }
966
967 if ((msg[0] & ~SUPPORTED_GLOBAL_ENABLES) != 0) {
968 rsp[0] = 0xcc;
969 rsp_len = 1;
970 break;
971 }
972
973 mi->global_enables = msg[0];
974
975 rsp[0] = 0;
976 rsp_len = 1;
977 break;
978
979 case IPMI_GET_BMC_GLOBAL_ENABLES_CMD:
980 rsp[0] = 0;
981 rsp[1] = mi->global_enables;
982 rsp_len = 2;
983 break;
984
985 case IPMI_READ_EVENT_MSG_CMD:
986 if (!mi->event_q) {
987 rsp[0] = 0x80;
988 rsp_len = 1;
989 break;
990 }
991 m = mi->event_q;
992 mi->event_q = m->next;
993 if (!mi->event_q)
994 mi->event_q_tail = NULL;
995
996 rsp[0] = 0;
997 memcpy(rsp + 1, m->data, m->data_len);
998 rsp_len = 1 + m->data_len;
999 free(m);
1000 break;
1001
1002 default:
1003 goto invalid_msg;
1004 }
1005 } else
1006 goto invalid_msg;
1007
1008 send_rsp:
1009 /* Convert to response. */
1010 mi->netfn |= 1;
1011 mi->codec->send(rsp, rsp_len, mi);
1012 return;
1013
1014 invalid_msg:
1015 rsp[0] = 0xc1;
1016 rsp_len = 1;
1017 goto send_rsp;
1018 }
1019
1020 #define PP_GET_SERIAL_INTF_CMD 0x01
1021 #define PP_SET_SERIAL_INTF_CMD 0x02
1022 static unsigned char pp_oem_chars[] = { 0x00, 0x40, 0x0a };
1023 static int
pp_oem_handler(const unsigned char * msg,unsigned int len,struct msg_info * mi,unsigned char * rsp,unsigned int * rsp_len)1024 pp_oem_handler(const unsigned char *msg, unsigned int len,
1025 struct msg_info *mi,
1026 unsigned char *rsp, unsigned int *rsp_len)
1027 {
1028 if ((len < 3) || (memcmp(msg, pp_oem_chars, 3) != 0))
1029 return -ENOSYS;
1030 msg += 3;
1031 len -= 3;
1032
1033 if (mi->netfn == IPMI_OEM_NETFN) {
1034 switch (mi->cmd) {
1035 case PP_GET_SERIAL_INTF_CMD:
1036 rsp[0] = 0;
1037 memcpy(rsp+1, pp_oem_chars, 3);
1038 rsp[4] = 0;
1039 if (msg[0] == 1)
1040 rsp[4] |= mi->echo;
1041 *rsp_len = 5;
1042 break;
1043
1044 case PP_SET_SERIAL_INTF_CMD:
1045 if (len < 2)
1046 rsp[0] = 0xcc;
1047 else if (msg[0] == 1) {
1048 mi->echo = msg[1] & 1;
1049 rsp[0] = 0;
1050 }
1051 memcpy(rsp+1, pp_oem_chars, 3);
1052 *rsp_len = 4;
1053 break;
1054
1055 default:
1056 return -ENOSYS;
1057 }
1058 } else
1059 return -ENOSYS;
1060
1061 return 0;
1062 }
1063
1064 static void
pp_oem_init(struct msg_info * mi)1065 pp_oem_init(struct msg_info *mi)
1066 {
1067 mi->echo = 1;
1068 }
1069
1070 #define RA_CONTROLLER_OEM_NETFN 0x3e
1071 #define RA_GET_IPMB_ADDR_CMD 0x12
1072 static int
ra_oem_handler(const unsigned char * msg,unsigned int len,struct msg_info * mi,unsigned char * rsp,unsigned int * rsp_len)1073 ra_oem_handler(const unsigned char *msg, unsigned int len,
1074 struct msg_info *mi,
1075 unsigned char *rsp, unsigned int *rsp_len)
1076 {
1077 if (mi->netfn == RA_CONTROLLER_OEM_NETFN) {
1078 switch (mi->cmd) {
1079 case RA_GET_IPMB_ADDR_CMD:
1080 rsp[0] = 0;
1081 rsp[1] = mi->my_ipmb;
1082 *rsp_len = 2;
1083 break;
1084
1085 default:
1086 return -ENOSYS;
1087 }
1088 } else if (mi->netfn == IPMI_APP_NETFN) {
1089 switch (mi->cmd) {
1090 case IPMI_GET_MSG_FLAGS_CMD:
1091 /* No message flag support. */
1092 rsp[0] = 0xc1;
1093 *rsp_len = 1;
1094 break;
1095
1096 default:
1097 return -ENOSYS;
1098 }
1099 } else
1100 return -ENOSYS;
1101
1102 return 0;
1103 }
1104
1105 static void
ra_oem_init(struct msg_info * mi)1106 ra_oem_init(struct msg_info *mi)
1107 {
1108 }
1109
1110 static struct oem_handler oem_handlers[] = {
1111 { "PigeonPoint", pp_oem_handler, pp_oem_init },
1112 { "Radisys", ra_oem_handler, ra_oem_init },
1113 { NULL }
1114 };
1115
1116 static char *
next_tok(char ** str)1117 next_tok(char **str)
1118 {
1119 char *rv;
1120 char *s = *str;
1121
1122 while (isspace(*s))
1123 s++;
1124 rv = s;
1125 while (*s && (!isspace(*s)))
1126 s++;
1127 if (*s) {
1128 *s = '\0';
1129 s++;
1130 }
1131 *str = s;
1132 if (*rv)
1133 return rv;
1134 else
1135 return NULL;
1136 }
1137
1138 static void
exit_handler(char * line,struct msg_info * mi)1139 exit_handler(char *line, struct msg_info *mi)
1140 {
1141 close(mi->sock);
1142 exit(0);
1143 }
1144
1145 static void
inc_debug_handler(char * line,struct msg_info * mi)1146 inc_debug_handler(char *line, struct msg_info *mi)
1147 {
1148 mi->debug++;
1149 }
1150
1151 static void
dec_debug_handler(char * line,struct msg_info * mi)1152 dec_debug_handler(char *line, struct msg_info *mi)
1153 {
1154 if (mi->debug > 0)
1155 mi->debug--;
1156 }
1157
1158 static void
event_handler(char * line,struct msg_info * mi)1159 event_handler(char *line, struct msg_info *mi)
1160 {
1161 struct msg *emsg;
1162 int i, p;
1163 unsigned char *m;
1164
1165 if (!mi->codec->handle_event) {
1166 printf("This codec does not support event messages\n");
1167 return;
1168 }
1169
1170 emsg = malloc(sizeof(*emsg));
1171 if (!emsg) {
1172 printf("Could not allocate event message\n");
1173 return;
1174 }
1175
1176 p = 0;
1177 m = emsg->data;
1178 for (i=0; i<16; i++) {
1179 char *s, *e;
1180 s = next_tok(&line);
1181 if (!s) {
1182 printf("Events need 16 bytes of data\n");
1183 free(emsg);
1184 return;
1185 }
1186 m[p++] = strtoul(s, &e, 16);
1187 if (*e != '\0') {
1188 printf("Byte %d was invalid\n", i+1);
1189 free(emsg);
1190 return;
1191 }
1192 }
1193 emsg->data_len = 16;
1194
1195 mi->codec->handle_event(emsg, mi);
1196 }
1197
1198 static void help_handler(char *line, struct msg_info *mi);
1199
1200 static const char help_help[] = "This command.";
1201 static const char exit_help[] = "Quit the program.";
1202 static const char quit_help[] = "Quit the program.";
1203 static const char event_help[] =
1204 "Put an event into the event queue. Takes 16 bytes of data like:\n"
1205 " event 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0 f1";
1206 static const char inc_debug_help[] = "Increment the debugging flag.";
1207 static const char dec_debug_help[] = "Decrement the debugging flag.";
1208
1209 static struct {
1210 const char *cmd;
1211 void (*handler)(char *line, struct msg_info *mi);
1212 const char *help;
1213 } cmds[] = {
1214 { "help", help_handler, help_help },
1215 { "exit", exit_handler, exit_help },
1216 { "quit", exit_handler, quit_help },
1217 { "event", event_handler, event_help },
1218 { "debug+", inc_debug_handler, inc_debug_help },
1219 { "debug-", dec_debug_handler, dec_debug_help },
1220 { NULL }
1221 };
1222
1223 static void
help_handler(char * line,struct msg_info * mi)1224 help_handler(char *line, struct msg_info *mi)
1225 {
1226 int i;
1227 printf("Valid commands:");
1228 for (i=0; cmds[i].cmd; i++)
1229 printf(" %s - %s\n", cmds[i].cmd, cmds[i].help);
1230 }
1231
1232 static struct msg_info main_mi;
1233
1234 static void
command_string_handler(char * cmdline)1235 command_string_handler(char *cmdline)
1236 {
1237 char *expansion = NULL;
1238 int result;
1239 int i;
1240 char *s, *cmd;
1241
1242 if (cmdline == NULL) {
1243 printf("\n");
1244 exit_handler(NULL, &main_mi);
1245 }
1246
1247 result = history_expand(cmdline, &expansion);
1248 if (result < 0 || result == 2) {
1249 fprintf(stderr, "%s\n", expansion);
1250 } else if (expansion && strlen(expansion)){
1251 add_history(expansion);
1252
1253 s = expansion;
1254 cmd = next_tok(&s);
1255 if (cmd) {
1256 /* Extract the command. */
1257 for (i=0; cmds[i].cmd != NULL; i++) {
1258 if (strcmp(cmd, cmds[i].cmd) == 0)
1259 break;
1260 }
1261 if (cmds[i].cmd) {
1262 cmds[i].handler(s, &main_mi);
1263 } else {
1264 printf("Unknown command: '%s'\n", cmd);
1265 }
1266 }
1267 }
1268 if (expansion)
1269 free(expansion);
1270 }
1271
1272 struct option options[] = {
1273 { "codec", 1, NULL, 'c' },
1274 { "ipmb_addr", 1, NULL, 'a' },
1275 { "oem_setup", 1, NULL, 'o' },
1276 { "debug", 0, NULL, 'd' },
1277 { "attn", 2, NULL, 't' },
1278 { 0 }
1279 };
1280
1281 static char *usage_str =
1282 "%s [options] <server> <port>\n"
1283 " Emulate various IPMI serial port BMCs, primarily for testing the\n"
1284 " IPMI driver.\n"
1285 " Options are:\n"
1286 " -c <codec>, --codec <codec> - Set the codec to use. Valid codecs\n"
1287 " are:\n"
1288 " TerminalMode - Standard terminal mode\n"
1289 " Direct - standard serial direct mode\n"
1290 " RadisysAscii - Radisys defined ASCII\n"
1291 " -a <addr>, --ipmb_addr <addr> - Set the IPMB address for the emulated\n"
1292 " BMC.\n"
1293 " -o <oem>, --oem_setup <oem> - Emulate certain OEM commands:\n"
1294 " PigeonPoint - Emulate echo handling per the PigeonPoint IPMCs,\n"
1295 " primarily for terminal mode.\n"
1296 " Radisys - Emulate the Radisys method for fetching the IPMB address.\n"
1297 " --attn[=<char>[,<char>[,...]]] - Set the attention characters to\n"
1298 " the given value. This is sent whenever something is added to the\n"
1299 " event or receive message queue. It defaults to one BELL character,\n"
1300 " which is 0x07. The specified values are numbers, like 0x07.\n"
1301 " For direct mode using the ASCII escape, this would be 0x1b. For\n"
1302 " direct mode on the Sun CPxxxx, this would be 0xAA,0x47.\n"
1303 " -d, --debug - Increment the debug setting\n"
1304 " This program connects to a remote TCP port, so you need to have a\n"
1305 " terminal server (in raw mode, not telnet mode) to use this program.\n"
1306 " I use ser2net, get that if you need it.\n";
1307 char *cmdname;
1308 static void
usage(void)1309 usage(void)
1310 {
1311 printf(usage_str, cmdname);
1312 exit(1);
1313 }
1314
1315 int
main(int argc,char * argv[])1316 main(int argc, char *argv[])
1317 {
1318 unsigned int i;
1319 struct addrinfo hints, *res0;
1320 struct sockaddr_storage saddr;
1321 struct sockaddr *addr = (struct sockaddr *) &saddr;
1322 size_t addrlen;
1323 struct msg_info *mi = &main_mi;
1324 int rv;
1325 char *s, *e;
1326
1327 cmdname = argv[0];
1328
1329 memset(mi, 0, sizeof(*mi));
1330 mi->my_ipmb = 0x20;
1331 mi->codec = &(codecs[0]);
1332
1333 for (;;) {
1334 int f;
1335 f = getopt_long(argc, argv, "c:o:a:d", options, NULL);
1336 if (f == -1)
1337 break;
1338
1339 switch (f) {
1340 case 'c':
1341 for (i=0; codecs[i].name; i++) {
1342 if (strcmp(codecs[i].name, optarg) == 0)
1343 break;
1344 }
1345 if (codecs[i].name)
1346 mi->codec = &(codecs[i]);
1347 else {
1348 fprintf(stderr, "Invalid codec: %s\n", optarg);
1349 usage();
1350 }
1351 break;
1352
1353 case 'd':
1354 mi->debug++;
1355 break;
1356
1357 case 'a':
1358 mi->my_ipmb = strtoul(optarg, NULL, 0);
1359 break;
1360
1361 case 't':
1362 mi->do_attn = 1;
1363 if (optarg) {
1364 s = optarg;
1365 for (i=0; ; i++) {
1366 if (i >= sizeof(mi->attn_chars)) {
1367 fprintf(stderr, "Too many attention characters\n");
1368 usage();
1369 }
1370 mi->attn_chars[i] = strtoul(s, &e, 0);
1371 mi->attn_chars_len++;
1372 if (*e == '\0')
1373 break;
1374 else if (*e == ',')
1375 s = e + 1;
1376 else {
1377 fprintf(stderr, "Invalid attention characters\n");
1378 usage();
1379 }
1380 }
1381 } else {
1382 mi->attn_chars[0] = 0x07;
1383 mi->attn_chars_len = 1;
1384 }
1385 break;
1386
1387 case 'o':
1388 for (i=0; oem_handlers[i].name != NULL; i++) {
1389 if (strcmp(optarg, oem_handlers[i].name) == 0)
1390 break;
1391 }
1392 if (oem_handlers[i].name) {
1393 mi->oem = &(oem_handlers[i]);
1394 mi->oem->init(mi);
1395 } else {
1396 fprintf(stderr, "Invalid OEM handler '%s'\n", optarg);
1397 usage();
1398 }
1399 break;
1400
1401 default:
1402 fprintf(stderr, "Invalid flag: '%c'\n", optopt);
1403 usage();
1404 }
1405 }
1406
1407 i = optind;
1408 if (i+2 < ((unsigned int) argc)) {
1409 fprintf(stderr, "Host and/or port not supplied\n");
1410 usage();
1411 }
1412
1413 memset(&hints, 0, sizeof(hints));
1414 hints.ai_socktype = SOCK_STREAM;
1415 hints.ai_family = AF_UNSPEC;
1416 rv = getaddrinfo(argv[i], argv[i+1], &hints, &res0);
1417 if (rv) {
1418 perror("getaddrinfo");
1419 usage();
1420 }
1421 /* Only get the first choices */
1422 memcpy(addr, res0->ai_addr, res0->ai_addrlen);
1423 addrlen = res0->ai_addrlen;
1424 freeaddrinfo(res0);
1425
1426 mi->sock = socket(addr->sa_family, SOCK_STREAM, 0);
1427 if (mi->sock < 0) {
1428 perror("socket");
1429 usage();
1430 }
1431
1432 rv = connect(mi->sock, (struct sockaddr *) addr, addrlen);
1433 if (rv < 0) {
1434 perror("connect");
1435 usage();
1436 }
1437
1438 i = 1;
1439 if (setsockopt(mi->sock, IPPROTO_TCP, TCP_NODELAY,
1440 (char *) &i, sizeof(i)) == -1) {
1441 perror("setsockopt TCP_NODELAY");
1442 usage();
1443 }
1444
1445 mi->info = mi->codec->setup();
1446 if (!mi->info) {
1447 fprintf(stderr, "Out of memory\n");
1448 usage();
1449 }
1450
1451 printf("Starting IPMI serial BMC emulator with:\n %s codec"
1452 "\n %s OEM emulation\n",
1453 mi->codec->name, mi->oem ? mi->oem->name : "no");
1454 if (mi->do_attn) {
1455 printf(" attention chars:");
1456 for (i=0; i<mi->attn_chars_len; i++)
1457 printf(" %2.2x", mi->attn_chars[i]);
1458 printf("\n");
1459 }
1460 stifle_history(500);
1461 rl_callback_handler_install("> ", command_string_handler);
1462
1463 for (;;) {
1464 unsigned char buf[128];
1465 int i;
1466 fd_set readfds;
1467 int rv2;
1468
1469 FD_ZERO(&readfds);
1470 FD_SET(0, &readfds);
1471 FD_SET(mi->sock, &readfds);
1472 rv = select(mi->sock+1, &readfds, NULL, NULL, NULL);
1473 if (rv < 0) {
1474 if (errno != EINTR) {
1475 perror("select");
1476 usage();
1477 }
1478 continue;
1479 }
1480
1481 if (FD_ISSET(mi->sock, &readfds)) {
1482 rv = read(mi->sock, buf, sizeof(buf));
1483 if (rv < 0) {
1484 perror("read");
1485 usage();
1486 }
1487
1488 if (mi->debug > 1) {
1489 printf("recv:");
1490 for (i=0; i<rv; i++) {
1491 if ((i % 16) == 0)
1492 printf("\n ");
1493 printf(" %2.2x(%c)", buf[i],
1494 isprint(buf[i]) ? buf[i] : ' ');
1495 }
1496 printf("\n");
1497 }
1498
1499 for (i=0; i<rv; i++) {
1500 /*
1501 * Echo one at a time in case the echo gets turned off
1502 * in the middle of this data.
1503 */
1504 if (mi->echo) {
1505 rv2 = write(mi->sock, buf+i, 1);
1506 if (rv2 < 0) {
1507 perror("write");
1508 usage();
1509 }
1510 }
1511 mi->codec->handle_char(buf[i], mi);
1512 }
1513 }
1514
1515 if (FD_ISSET(0, &readfds))
1516 rl_callback_read_char();
1517 }
1518 }
1519