custom message type for VM_INFO
[minix3.git] / commands / telnet / ttn.c
blobba4f0c716ed0f66566d4151bf2d05323ba1568a0
1 /*
2 ttn.c
3 */
5 #include <sys/types.h>
6 #include <sys/ioctl.h>
7 #include <assert.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <termios.h>
11 #include <signal.h>
12 #include <stdarg.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <net/hton.h>
18 #include <net/netlib.h>
19 #include <net/gen/in.h>
20 #include <net/gen/inet.h>
21 #include <net/gen/netdb.h>
22 #include <net/gen/tcp.h>
23 #include <net/gen/tcp_io.h>
24 #include "ttn.h"
26 #if __STDC__
27 #define PROTOTYPE(func,args) func args
28 #else
29 #define PROTOTYPE(func,args) func()
30 #endif
32 static int do_read(int fd, char *buf, unsigned len);
33 static void screen(void);
34 static void keyboard(void);
35 static void send_brk(void);
36 static int process_opt (char *bp, int count);
37 static void do_option (int optsrt);
38 static void dont_option (int optsrt);
39 static void will_option (int optsrt);
40 static void wont_option (int optsrt);
41 static int writeall (int fd, char *buffer, int buf_size);
42 static int sb_termtype (char *sb, int count);
43 static void fatal(char *fmt, ...);
44 static void usage(void);
46 #if DEBUG
47 #define where() (fprintf(stderr, "%s %d:", __FILE__, __LINE__))
48 #endif
50 static char *prog_name;
51 static int tcp_fd;
52 static char *term_env;
53 static int esc_char= '~';
54 static enum { LS_NORM, LS_BOL, LS_ESC } line_state= LS_BOL;
56 int main(int argc, char *argv[])
58 struct hostent *hostent;
59 struct servent *servent;
60 ipaddr_t host;
61 tcpport_t port;
62 int pid, ppid;
63 nwio_tcpconf_t tcpconf;
64 int c, r;
65 nwio_tcpcl_t tcpconnopt;
66 struct termios termios;
67 char *tcp_device, *remote_name, *port_name;
68 char *e_arg;
70 (prog_name=strrchr(argv[0],'/')) ? prog_name++ : (prog_name=argv[0]);
72 e_arg= NULL;
73 while (c= getopt(argc, argv, "?e:"), c != -1)
75 switch(c)
77 case '?': usage();
78 case 'e': e_arg= optarg; break;
79 default:
80 fatal("Optind failed: '%c'", c);
84 if (optind >= argc)
85 usage();
86 remote_name= argv[optind++];
87 if (optind < argc)
88 port_name= argv[optind++];
89 else
90 port_name= NULL;
91 if (optind != argc)
92 usage();
94 if (e_arg)
96 switch(strlen(e_arg))
98 case 0: esc_char= -1; break;
99 case 1: esc_char= e_arg[0]; break;
100 default: fatal("Invalid escape character '%s'", e_arg);
104 hostent= gethostbyname(remote_name);
105 if (!hostent)
106 fatal("Unknown host %s", remote_name);
107 host= *(ipaddr_t *)(hostent->h_addr);
109 if (!port_name)
110 port= htons(TCPPORT_TELNET);
111 else
113 servent= getservbyname (port_name, "tcp");
114 if (!servent)
116 port= htons(strtol(port_name, (char **)0, 0));
117 if (!port)
118 fatal("Unknown port %s", port_name);
120 else
121 port= (tcpport_t)(servent->s_port);
124 fprintf(stderr, "Connecting to %s:%u...\n",
125 inet_ntoa(host), ntohs(port));
127 tcp_device= getenv("TCP_DEVICE");
128 if (tcp_device == NULL)
129 tcp_device= TCP_DEVICE;
130 tcp_fd= open (tcp_device, O_RDWR);
131 if (tcp_fd == -1)
132 fatal("Unable to open %s: %s", tcp_device, strerror(errno));
134 tcpconf.nwtc_flags= NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP;
135 tcpconf.nwtc_remaddr= host;
136 tcpconf.nwtc_remport= port;
138 r= ioctl (tcp_fd, NWIOSTCPCONF, &tcpconf);
139 if (r == -1)
140 fatal("NWIOSTCPCONF failed: %s", strerror(errno));
142 tcpconnopt.nwtcl_flags= 0;
145 r= ioctl (tcp_fd, NWIOTCPCONN, &tcpconnopt);
146 if (r == -1 && errno == EAGAIN)
148 fprintf(stderr, "%s: Got EAGAIN, sleeping(1s)\n",
149 prog_name);
150 sleep(1);
152 } while (r == -1 && errno == EAGAIN);
153 if (r == -1)
154 fatal("Unable to connect: %s", strerror(errno));
155 printf("Connected\n");
156 ppid= getpid();
157 pid= fork();
158 switch(pid)
160 case 0:
161 keyboard();
162 #if DEBUG
163 fprintf(stderr, "killing %d with %d\r\n", ppid, SIGKILL);
164 #endif
165 kill(ppid, SIGKILL);
166 break;
167 case -1:
168 fprintf(stderr, "%s: fork failed: %s\r\n", argv[0],
169 strerror(errno));
170 exit(1);
171 break;
172 default:
173 tcgetattr(0, &termios);
174 screen();
175 #if DEBUG
176 fprintf(stderr, "killing %d with %d\r\n", pid, SIGKILL);
177 #endif
178 kill(pid, SIGKILL);
179 tcsetattr(0, TCSANOW, &termios);
180 break;
182 exit(0);
185 static int do_read(fd, buf, len)
186 int fd;
187 char *buf;
188 unsigned len;
190 nwio_tcpopt_t tcpopt;
191 int count;
193 for (;;)
195 count= read (fd, buf, len);
196 if (count <0)
198 if (errno == EURG || errno == ENOURG)
200 /* Toggle urgent mode. */
201 tcpopt.nwto_flags= errno == EURG ?
202 NWTO_RCV_URG : NWTO_RCV_NOTURG;
203 if (ioctl(tcp_fd, NWIOSTCPOPT, &tcpopt) == -1)
205 return -1;
207 continue;
209 return -1;
211 return count;
215 static void screen()
217 char buffer[1024], *bp, *iacptr;
218 int count, optsize;
220 for (;;)
222 count= do_read (tcp_fd, buffer, sizeof(buffer));
223 #if DEBUG && 0
224 { where(); fprintf(stderr, "read %d bytes\r\n", count); }
225 #endif
226 if (count <0)
228 perror ("read");
229 return;
231 if (!count)
232 return;
233 bp= buffer;
236 iacptr= memchr (bp, IAC, count);
237 if (!iacptr)
239 write(1, bp, count);
240 count= 0;
242 if (iacptr && iacptr>bp)
244 #if DEBUG
245 { where(); fprintf(stderr, "iacptr-bp= %d\r\n", iacptr-bp); }
246 #endif
247 write(1, bp, iacptr-bp);
248 count -= (iacptr-bp);
249 bp= iacptr;
250 continue;
252 if (iacptr)
254 assert (iacptr == bp);
255 optsize= process_opt(bp, count);
256 #if DEBUG && 0
257 { where(); fprintf(stderr, "process_opt(...)= %d\r\n", optsize); }
258 #endif
259 if (optsize<0)
260 return;
261 assert (optsize);
262 bp += optsize;
263 count -= optsize;
265 } while (count);
269 static void keyboard()
271 char c, buffer[1024];
272 int count;
274 for (;;)
276 count= read (0, buffer, 1 /* sizeof(buffer) */);
277 if (count == -1)
278 fatal("Read: %s\r\n", strerror(errno));
279 if (!count)
280 return;
282 if (line_state != LS_NORM)
284 c= buffer[0];
285 if (line_state == LS_BOL)
287 if (c == esc_char)
289 line_state= LS_ESC;
290 continue;
292 line_state= LS_NORM;
294 else if (line_state == LS_ESC)
296 line_state= LS_NORM;
297 if (c == '.')
298 return;
299 if (c == '#')
301 send_brk();
302 continue;
305 /* Not a valid command or a repeat of the
306 * escape char
308 if (c != esc_char)
310 c= esc_char;
311 write(tcp_fd, &c, 1);
315 if (buffer[0] == '\n')
316 write(tcp_fd, "\r", 1);
317 count= write(tcp_fd, buffer, count);
318 if (buffer[0] == '\r')
320 line_state= LS_BOL;
321 write(tcp_fd, "\0", 1);
323 if (count<0)
325 perror("write");
326 fprintf(stderr, "errno= %d\r\n", errno);
327 return;
329 if (!count)
330 return;
334 static void send_brk(void)
336 int r;
337 unsigned char buffer[2];
339 buffer[0]= IAC;
340 buffer[1]= IAC_BRK;
342 r= writeall(tcp_fd, (char *)buffer, 2);
343 if (r == -1)
344 fatal("Error writing to TCP connection: %s", strerror(errno));
347 #define next_char(var) \
348 if (offset<count) { (var) = bp[offset++]; } \
349 else if (do_read(tcp_fd, (char *)&(var), 1) <= 0) \
350 { perror ("read"); return -1; }
352 static int process_opt (char *bp, int count)
354 unsigned char iac, command, optsrt, sb_command;
355 int offset, result; ;
356 #if DEBUG && 0
357 { where(); fprintf(stderr, "process_opt(bp= 0x%x, count= %d)\r\n",
358 bp, count); }
359 #endif
361 offset= 0;
362 assert (count);
363 next_char(iac);
364 assert (iac == IAC);
365 next_char(command);
366 switch(command)
368 case IAC_NOP:
369 break;
370 case IAC_DataMark:
371 /* Ought to flush input queue or something. */
372 break;
373 case IAC_BRK:
374 fprintf(stderr, "got a BRK\r\n");
375 break;
376 case IAC_IP:
377 fprintf(stderr, "got a IP\r\n");
378 break;
379 case IAC_AO:
380 fprintf(stderr, "got a AO\r\n");
381 break;
382 case IAC_AYT:
383 fprintf(stderr, "got a AYT\r\n");
384 break;
385 case IAC_EC:
386 fprintf(stderr, "got a EC\r\n");
387 break;
388 case IAC_EL:
389 fprintf(stderr, "got a EL\r\n");
390 break;
391 case IAC_GA:
392 fprintf(stderr, "got a GA\r\n");
393 break;
394 case IAC_SB:
395 next_char(sb_command);
396 switch (sb_command)
398 case OPT_TERMTYPE:
399 #if DEBUG && 0
400 fprintf(stderr, "got SB TERMINAL-TYPE\r\n");
401 #endif
402 result= sb_termtype(bp+offset, count-offset);
403 if (result<0)
404 return result;
405 else
406 return result+offset;
407 default:
408 fprintf(stderr, "got an unknown SB (skiping)\r\n");
409 for (;;)
411 next_char(iac);
412 if (iac != IAC)
413 continue;
414 next_char(optsrt);
415 if (optsrt == IAC)
416 continue;
417 if (optsrt != IAC_SE)
418 fprintf(stderr, "got IAC %d\r\n", optsrt);
419 break;
422 break;
423 case IAC_WILL:
424 next_char(optsrt);
425 will_option(optsrt);
426 break;
427 case IAC_WONT:
428 next_char(optsrt);
429 wont_option(optsrt);
430 break;
431 case IAC_DO:
432 next_char(optsrt);
433 do_option(optsrt);
434 break;
435 case IAC_DONT:
436 next_char(optsrt);
437 dont_option(optsrt);
438 break;
439 case IAC:
440 fprintf(stderr, "got a IAC\r\n");
441 break;
442 default:
443 fprintf(stderr, "got unknown command (%d)\r\n", command);
445 return offset;
448 static void do_option (int optsrt)
450 unsigned char reply[3];
451 int result;
453 switch (optsrt)
455 case OPT_TERMTYPE:
456 if (WILL_terminal_type)
457 return;
458 if (!WILL_terminal_type_allowed)
460 reply[0]= IAC;
461 reply[1]= IAC_WONT;
462 reply[2]= optsrt;
464 else
466 WILL_terminal_type= TRUE;
467 term_env= getenv("TERM");
468 if (!term_env)
469 term_env= "unknown";
470 reply[0]= IAC;
471 reply[1]= IAC_WILL;
472 reply[2]= optsrt;
474 break;
475 default:
476 #if DEBUG
477 fprintf(stderr, "got a DO (%d)\r\n", optsrt);
478 fprintf(stderr, "WONT (%d)\r\n", optsrt);
479 #endif
480 reply[0]= IAC;
481 reply[1]= IAC_WONT;
482 reply[2]= optsrt;
483 break;
485 result= writeall(tcp_fd, (char *)reply, 3);
486 if (result<0)
487 perror("write");
490 static void will_option (int optsrt)
492 unsigned char reply[3];
493 int result;
495 switch (optsrt)
497 case OPT_ECHO:
498 if (DO_echo)
499 break;
500 if (!DO_echo_allowed)
502 reply[0]= IAC;
503 reply[1]= IAC_DONT;
504 reply[2]= optsrt;
506 else
508 struct termios termios;
510 tcgetattr(0, &termios);
511 termios.c_iflag &= ~(ICRNL|IGNCR|INLCR|IXON|IXOFF);
512 termios.c_oflag &= ~(OPOST);
513 termios.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
514 tcsetattr(0, TCSANOW, &termios);
516 DO_echo= TRUE;
517 reply[0]= IAC;
518 reply[1]= IAC_DO;
519 reply[2]= optsrt;
521 result= writeall(tcp_fd, (char *)reply, 3);
522 if (result<0)
523 perror("write");
524 break;
525 case OPT_SUPP_GA:
526 if (DO_suppress_go_ahead)
527 break;
528 if (!DO_suppress_go_ahead_allowed)
530 reply[0]= IAC;
531 reply[1]= IAC_DONT;
532 reply[2]= optsrt;
534 else
536 DO_suppress_go_ahead= TRUE;
537 reply[0]= IAC;
538 reply[1]= IAC_DO;
539 reply[2]= optsrt;
541 result= writeall(tcp_fd, (char *)reply, 3);
542 if (result<0)
543 perror("write");
544 break;
545 default:
546 #if DEBUG
547 fprintf(stderr, "got a WILL (%d)\r\n", optsrt);
548 fprintf(stderr, "DONT (%d)\r\n", optsrt);
549 #endif
550 reply[0]= IAC;
551 reply[1]= IAC_DONT;
552 reply[2]= optsrt;
553 result= writeall(tcp_fd, (char *)reply, 3);
554 if (result<0)
555 perror("write");
556 break;
560 static int writeall (fd, buffer, buf_size)
561 int fd;
562 char *buffer;
563 int buf_size;
565 int result;
567 while (buf_size)
569 result= write (fd, buffer, buf_size);
570 if (result <= 0)
571 return -1;
572 assert (result <= buf_size);
573 buffer += result;
574 buf_size -= result;
576 return 0;
579 static void dont_option (int optsrt)
581 switch (optsrt)
583 default:
584 #if DEBUG
585 fprintf(stderr, "got a DONT (%d)\r\n", optsrt);
586 #endif
587 break;
591 static void wont_option (int optsrt)
593 switch (optsrt)
595 default:
596 #if DEBUG
597 fprintf(stderr, "got a WONT (%d)\r\n", optsrt);
598 #endif
599 break;
603 static int sb_termtype (char *bp, int count)
605 unsigned char command, iac, optsrt;
606 unsigned char buffer[4];
607 int offset, result;
609 offset= 0;
610 next_char(command);
611 if (command == TERMTYPE_SEND)
613 buffer[0]= IAC;
614 buffer[1]= IAC_SB;
615 buffer[2]= OPT_TERMTYPE;
616 buffer[3]= TERMTYPE_IS;
617 result= writeall(tcp_fd, (char *)buffer,4);
618 if (result<0)
619 return result;
620 count= strlen(term_env);
621 if (!count)
623 term_env= "unknown";
624 count= strlen(term_env);
626 result= writeall(tcp_fd, term_env, count);
627 if (result<0)
628 return result;
629 buffer[0]= IAC;
630 buffer[1]= IAC_SE;
631 result= writeall(tcp_fd, (char *)buffer,2);
632 if (result<0)
633 return result;
636 else
638 #if DEBUG
639 where();
640 #endif
641 fprintf(stderr, "got an unknown command (skipping)\r\n");
643 for (;;)
645 next_char(iac);
646 if (iac != IAC)
647 continue;
648 next_char(optsrt);
649 if (optsrt == IAC)
650 continue;
651 if (optsrt != IAC_SE)
653 #if DEBUG
654 where();
655 #endif
656 fprintf(stderr, "got IAC %d\r\n", optsrt);
658 break;
660 return offset;
663 static void fatal(char *fmt, ...)
665 va_list ap;
667 va_start(ap, fmt);
668 fprintf(stderr, "%s: ", prog_name);
669 vfprintf(stderr, fmt, ap);
670 fprintf(stderr, "\n");
671 va_end(ap);
673 exit(1);
676 static void usage(void)
678 fprintf(stderr, "Usage: %s [-e esc-char] host [port]\r\n",
679 prog_name);
680 exit(1);
684 * $PchId: ttn.c,v 1.5 2002/05/07 12:06:41 philip Exp $