]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/telnet.c
Import sources from 2011-03-30 iso image
[plan9front.git] / sys / src / cmd / ip / telnet.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "telnet.h"
5
6 int ctl = -1;           /* control fd (for break's) */
7 int consctl = -1;       /* consctl fd */
8
9 int ttypid;             /* pid's if the 2 processes (used to kill them) */
10 int netpid;
11 int interrupted;
12 int localecho;
13 int notkbd;
14
15 static char *srv;
16
17 typedef struct Comm Comm;
18 struct Comm {
19         int returns;
20         int stopped;
21 };
22 Comm *comm;
23
24 int     dodial(char*);
25 void    fromkbd(int);
26 void    fromnet(int);
27 int     menu(Biobuf*,  int);
28 void    notifyf(void*, char*);
29 void    rawoff(void);
30 void    rawon(void);
31 void    telnet(int);
32 char*   system(int, char*);
33 int     echochange(Biobuf*, int);
34 int     termsub(Biobuf*, uchar*, int);
35 int     xlocsub(Biobuf*, uchar*, int);
36 void*   share(ulong);
37
38 static int islikeatty(int);
39
40 void
41 usage(void)
42 {
43         fatal("usage: telnet [-Cdnr] [-s srv] net!host[!service]", 0, 0);
44 }
45
46 void
47 main(int argc, char *argv[])
48 {
49         int returns;
50
51         returns = 1;
52         ARGBEGIN{
53         case 'C':
54                 opt[Echo].noway = 1;
55                 break;
56         case 'd':
57                 debug = 1;
58                 break;
59         case 'n':
60                 notkbd = 1;
61                 break; 
62         case 'r':
63                 returns = 0;
64                 break;
65         case 's':
66                 srv = EARGF(usage());
67                 break;
68         default:
69                 usage();
70         }ARGEND
71
72         if(argc != 1)
73                 usage();
74
75         /* options we need routines for */
76         opt[Echo].change = echochange;
77         opt[Term].sub = termsub;
78         opt[Xloc].sub = xlocsub;
79
80         comm = share(sizeof(comm));
81         comm->returns = returns;
82
83         telnet(dodial(argv[0]));
84 }
85
86 /*
87  *  dial and return a data connection
88  */
89 int
90 dodial(char *dest)
91 {
92         char *name;
93         int data;
94         char devdir[NETPATHLEN];
95
96         name = netmkaddr(dest, "tcp", "telnet");
97         data = dial(name, 0, devdir, 0);
98         if(data < 0)
99                 fatal("%s: %r", name, 0);
100         fprint(2, "connected to %s on %s\n", name, devdir);
101         return data;
102 }
103
104 void
105 post(char *srv, int fd)
106 {
107         int f;
108         char buf[32];
109
110         f = create(srv, OWRITE, 0666);
111         if(f < 0)
112                 sysfatal("create %s: %r", srv);
113         snprint(buf, sizeof buf, "%d", fd);
114         if(write(f, buf, strlen(buf)) != strlen(buf))
115                 sysfatal("write %s: %r", srv);
116         close(f);
117 }
118
119 /*
120  *  two processes pass bytes back and forth between the
121  *  terminal and the network.
122  */
123 void
124 telnet(int net)
125 {
126         int pid;
127         int p[2];
128         char *svc;
129
130         rawoff();
131         svc = nil;
132         if (srv) {
133                 if(pipe(p) < 0)
134                         sysfatal("pipe: %r");
135                 if (srv[0] != '/')
136                         svc = smprint("/srv/%s", srv);
137                 else
138                         svc = srv;
139                 post(svc, p[0]);
140                 close(p[0]);
141                 dup(p[1], 0);
142                 dup(p[1], 1);
143                 /* pipe is now std in & out */
144         }
145         ttypid = getpid();
146         switch(pid = rfork(RFPROC|RFFDG|RFMEM)){
147         case -1:
148                 perror("con");
149                 exits("fork");
150         case 0:
151                 rawoff();
152                 notify(notifyf);
153                 fromnet(net);
154                 if (svc)
155                         remove(svc);
156                 sendnote(ttypid, "die");
157                 exits(0);
158         default:
159                 netpid = pid;
160                 notify(notifyf);
161                 fromkbd(net);
162                 if(notkbd)
163                         for(;;)
164                                 sleep(0);
165                 if (svc)
166                         remove(svc);
167                 sendnote(netpid, "die");
168                 exits(0);
169         }
170 }
171
172 /*
173  *  Read the keyboard and write it to the network.  '^\' gets us into
174  *  the menu.
175  */
176 void
177 fromkbd(int net)
178 {
179         Biobuf ib, ob;
180         int c, likeatty;
181         int eofs;
182
183         Binit(&ib, 0, OREAD);
184         Binit(&ob, net, OWRITE);
185
186         likeatty = islikeatty(0);
187         eofs = 0;
188         for(;;){
189                 c = Bgetc(&ib);
190
191                 /*
192                  *  with raw off, all ^D's get turned into Eof's.
193                  *  change them back.
194                  *  10 in a row implies that the terminal is really gone so
195                  *  just hang up.
196                  */
197                 if(c < 0){
198                         if(notkbd)
199                                 return;
200                         if(eofs++ > 10)
201                                 return;
202                         c = 004;
203                 } else
204                         eofs = 0;
205
206                 /*
207                  *  if not in binary mode, look for the ^\ escape to menu.
208                  *  also turn \n into \r\n
209                  */
210                 if(likeatty || !opt[Binary].local){
211                         if(c == 0034){ /* CTRL \ */
212                                 if(Bflush(&ob) < 0)
213                                         return;
214                                 if(menu(&ib, net) < 0)
215                                         return;
216                                 continue;
217                         }
218                 }
219                 if(!opt[Binary].local){
220                         if(c == '\n'){
221                                 /*
222                                  *  This is a very strange use of the SGA option.
223                                  *  I did this because some systems that don't
224                                  *  announce a willingness to supress-go-ahead
225                                  *  need the \r\n sequence to recognize input.
226                                  *  If someone can explain this to me, please
227                                  *  send me mail. - presotto
228                                  */
229                                 if(opt[SGA].remote){
230                                         c = '\r';
231                                 } else {
232                                         if(Bputc(&ob, '\r') < 0)
233                                                 return;
234                                 }
235                         }
236                 }
237                 if(Bputc(&ob, c) < 0)
238                         return;
239                 if(Bbuffered(&ib) == 0)
240                         if(Bflush(&ob) < 0)
241                                 return;
242         }
243 }
244
245 /*
246  *  Read from the network and write to the screen.  If 'stopped' is set
247  *  spin and don't read.  Filter out spurious carriage returns.
248  */
249 void
250 fromnet(int net)
251 {
252         int c;
253         int crnls = 0, freenl = 0, eofs;
254         Biobuf ib, ob;
255
256         Binit(&ib, net, OREAD);
257         Binit(&ob, 1, OWRITE);
258         eofs = 0;
259         for(;;){
260                 if(Bbuffered(&ib) == 0)
261                         Bflush(&ob);
262                 if(interrupted){
263                         interrupted = 0;
264                         send2(net, Iac, Interrupt);
265                 }
266                 c = Bgetc(&ib);
267                 if(c < 0){
268                         if(eofs++ >= 2)
269                                 return;
270                         continue;
271                 }
272                 eofs = 0;
273                 switch(c){
274                 case '\n':      /* skip nl after string of cr's */
275                         if(!opt[Binary].local && !comm->returns){
276                                 ++crnls;
277                                 if(freenl == 0)
278                                         break;
279                                 freenl = 0;
280                                 continue;
281                         }
282                         break;
283                 case '\r':      /* first cr becomes nl, remainder dropped */
284                         if(!opt[Binary].local && !comm->returns){
285                                 if(crnls++ == 0){
286                                         freenl = 1;
287                                         c = '\n';
288                                         break;
289                                 }
290                                 continue;
291                         }
292                         break;
293                 case 0:         /* remove nulls from crnl string */
294                         if(crnls)
295                                 continue;
296                         break;
297
298                 case Iac:
299                         crnls = 0;
300                         freenl = 0;
301                         c = Bgetc(&ib);
302                         if(c == Iac)
303                                 break;
304                         if(Bflush(&ob) < 0)
305                                 return;
306                         if(control(&ib, c) < 0)
307                                 return;
308                         continue;
309
310                 default:
311                         crnls = 0;
312                         freenl = 0;
313                         break;
314                 }
315                 if(Bputc(&ob, c) < 0)
316                         return;
317         }
318 }
319
320 /*
321  *  turn keyboard raw mode on
322  */
323 void
324 rawon(void)
325 {
326         if(debug)
327                 fprint(2, "rawon\n");
328         if(consctl < 0)
329                 consctl = open("/dev/consctl", OWRITE);
330         if(consctl < 0){
331                 fprint(2, "can't open consctl: %r\n");
332                 return;
333         }
334         write(consctl, "rawon", 5);
335 }
336
337 /*
338  *  turn keyboard raw mode off
339  */
340 void
341 rawoff(void)
342 {
343         if(debug)
344                 fprint(2, "rawoff\n");
345         if(consctl < 0)
346                 consctl = open("/dev/consctl", OWRITE);
347         if(consctl < 0){
348                 fprint(2, "can't open consctl: %r\n");
349                 return;
350         }
351         write(consctl, "rawoff", 6);
352 }
353
354 /*
355  *  control menu
356  */
357 #define STDHELP "\t(b)reak, (i)nterrupt, (q)uit, (r)eturns, (!cmd), (.)continue\n"
358
359 int
360 menu(Biobuf *bp, int net)
361 {
362         char *cp;
363         int done;
364
365         comm->stopped = 1;
366
367         rawoff();
368         fprint(2, ">>> ");
369         for(done = 0; !done; ){
370                 cp = Brdline(bp, '\n');
371                 if(cp == 0){
372                         comm->stopped = 0;
373                         return -1;
374                 }
375                 cp[Blinelen(bp)-1] = 0;
376                 switch(*cp){
377                 case '!':
378                         system(Bfildes(bp), cp+1);
379                         done = 1;
380                         break;
381                 case '.':
382                         done = 1;
383                         break;
384                 case 'q':
385                         comm->stopped = 0;
386                         return -1;
387                 case 'o':
388                         switch(*(cp+1)){
389                         case 'd':
390                                 send3(net, Iac, Do, atoi(cp+2));
391                                 break;
392                         case 'w':
393                                 send3(net, Iac, Will, atoi(cp+2));
394                                 break;
395                         }
396                         break;
397                 case 'r':
398                         comm->returns = !comm->returns;
399                         done = 1;
400                         break;
401                 case 'i':
402                         send2(net, Iac, Interrupt);
403                         break;
404                 case 'b':
405                         send2(net, Iac, Break);
406                         break;
407                 default:
408                         fprint(2, STDHELP);
409                         break;
410                 }
411                 if(!done)
412                         fprint(2, ">>> ");
413         }
414
415         rawon();
416         comm->stopped = 0;
417         return 0;
418 }
419
420 /*
421  *  ignore interrupts
422  */
423 void
424 notifyf(void *a, char *msg)
425 {
426         USED(a);
427         if(strcmp(msg, "interrupt") == 0){
428                 interrupted = 1;
429                 noted(NCONT);
430         }
431         if(strcmp(msg, "hangup") == 0)
432                 noted(NCONT);
433         noted(NDFLT);
434 }
435
436 /*
437  *  run a command with the network connection as standard IO
438  */
439 char *
440 system(int fd, char *cmd)
441 {
442         int pid;
443         int p;
444         static Waitmsg msg;
445
446         if((pid = fork()) == -1){
447                 perror("con");
448                 return "fork failed";
449         }
450         else if(pid == 0){
451                 dup(fd, 0);
452                 close(ctl);
453                 close(fd);
454                 if(*cmd)
455                         execl("/bin/rc", "rc", "-c", cmd, nil);
456                 else
457                         execl("/bin/rc", "rc", nil);
458                 perror("con");
459                 exits("exec");
460         }
461         for(p = waitpid(); p >= 0; p = waitpid()){
462                 if(p == pid)
463                         return msg.msg; 
464         }
465         return "lost child";
466 }
467
468 /*
469  *  suppress local echo if the remote side is doing it
470  */
471 int
472 echochange(Biobuf *bp, int cmd)
473 {
474         USED(bp);
475
476         switch(cmd){
477         case Will:
478                 rawon();
479                 break;
480         case Wont:
481                 rawoff();
482                 break;
483         }
484         return 0;
485 }
486
487 /*
488  *  send terminal type to the other side
489  */
490 int
491 termsub(Biobuf *bp, uchar *sub, int n)
492 {
493         char buf[64];
494         char *term;
495         char *p = buf;
496
497         if(n < 1)
498                 return 0;
499         if(sub[0] == 1){
500                 *p++ = Iac;
501                 *p++ = Sb;
502                 *p++ = opt[Term].code;
503                 *p++ = 0;
504                 term = getenv("TERM");
505                 if(term == 0 || *term == 0)
506                         term = "p9win";
507                 strncpy(p, term, sizeof(buf) - (p - buf) - 2);
508                 buf[sizeof(buf)-2] = 0;
509                 p += strlen(p);
510                 *p++ = Iac;
511                 *p++ = Se;
512                 return iwrite(Bfildes(bp), buf, p-buf);
513         }
514         return 0;
515 }
516
517 /*
518  *  send an x display location to the other side
519  */
520 int
521 xlocsub(Biobuf *bp, uchar *sub, int n)
522 {
523         char buf[64];
524         char *term;
525         char *p = buf;
526
527         if(n < 1)
528                 return 0;
529         if(sub[0] == 1){
530                 *p++ = Iac;
531                 *p++ = Sb;
532                 *p++ = opt[Xloc].code;
533                 *p++ = 0;
534                 term = getenv("XDISP");
535                 if(term == 0 || *term == 0)
536                         term = "unknown";
537                 strncpy(p, term, p - buf - 2);
538                 p += strlen(term);
539                 *p++ = Iac;
540                 *p++ = Se;
541                 return iwrite(Bfildes(bp), buf, p-buf);
542         }
543         return 0;
544 }
545
546 static int
547 islikeatty(int fd)
548 {
549         char buf[64];
550
551         if(fd2path(fd, buf, sizeof buf) != 0)
552                 return 0;
553
554         /* might be /mnt/term/dev/cons */
555         return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
556 }
557
558 /*
559  *  create a shared segment.  Make is start 2 meg higher than the current
560  *  end of process memory.
561  */
562 void*
563 share(ulong len)
564 {
565         uchar *vastart;
566
567         vastart = sbrk(0);
568         if(vastart == (void*)-1)
569                 return 0;
570         vastart += 2*1024*1024;
571
572         if(segattach(0, "shared", vastart, len) == (void*)-1)
573                 return 0;
574
575         return vastart;
576 }