]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/con/con.c
fix filetype detecton by suffix so that multiple dots dont confuse it. (thanks kvik)
[plan9front.git] / sys / src / cmd / con / con.c
1 #include <u.h>
2 #include <libc.h>
3
4 int debug;              /* true if debugging */
5 int ctl = -1;           /* control fd (for break's) */
6 int raw;                /* true if raw is on */
7 int consctl = -1;       /* control fd for cons */
8 int ttypid;             /* pid's if the 2 processes (used to kill them) */
9 int outfd = 1;          /* local output file descriptor */
10 int cooked;             /* non-zero forces cooked mode */
11 int returns;            /* non-zero forces carriage returns not to be filtered out */
12 int crtonl;                     /* non-zero forces carriage returns to be converted to nls coming from net */
13 int     strip;          /* strip off parity bits */
14 char firsterr[2*ERRMAX];
15 char transerr[2*ERRMAX];
16 int limited;
17 char *remuser;          /* for BSD rlogin authentication */
18 int verbose;
19 int baud;
20 int notkbd;
21 int nltocr;             /* translate kbd nl to cr  and vice versa */
22
23 static char *srv;
24
25 #define MAXMSG (2*8192)
26
27 int     dodial(char*, char*, char*);
28 void    fromkbd(int);
29 void    fromnet(int);
30 long    iread(int, void*, int);
31 long    iwrite(int, void*, int);
32 int     menu(int);
33 void    notifyf(void*, char*);
34 void    pass(int, int, int);
35 void    rawoff(void);
36 void    rawon(void);
37 void    stdcon(int);
38 char*   system(int, char*);
39 void    dosystem(int, char*);
40 int     wasintr(void);
41 void    punt(char*);
42 char*   syserr(void);
43 void    seterr(char*);
44
45 /* protocols */
46 void    device(char*, char*);
47 void    rlogin(char*, char*);
48 void    simple(char*, char*);
49
50 void
51 usage(void)
52 {
53         punt("usage: con [-CdnrRsTv] [-b baud] [-l [user]] [-c cmd] [-S svc] "
54                 "net!host[!service]");
55 }
56
57 void
58 main(int argc, char *argv[])
59 {
60         char *dest;
61         char *cmd = 0;
62
63         returns = 1;
64         ARGBEGIN{
65         case 'b':
66                 baud = atoi(EARGF(usage()));
67                 break;
68         case 'C':
69                 cooked = 1;
70                 break;
71         case 'c':
72                 cmd = EARGF(usage());
73                 break;
74         case 'd':
75                 debug = 1;
76                 break;
77         case 'l':
78                 limited = 1;
79                 if(argv[1] != nil && argv[1][0] != '-')
80                         remuser = EARGF(usage());
81                 break;
82         case 'n':
83                 notkbd = 1;
84                 break;
85         case 'r':
86                 returns = 0;
87                 break;
88         case 's':
89                 strip = 1;
90                 break;
91         case 'S':
92                 srv = EARGF(usage());
93                 break;
94         case 'R':
95                 nltocr = 1;
96                 break;
97         case 'T':
98                 crtonl = 1;
99                 break;
100         case 'v':
101                 verbose = 1;
102                 break;
103         default:
104                 usage();
105         }ARGEND
106
107         if(argc != 1){
108                 if(remuser == 0)
109                         usage();
110                 dest = remuser;
111                 remuser = 0;
112         } else
113                 dest = argv[0];
114         if(*dest == '/' && strchr(dest, '!') == 0)
115                 device(dest, cmd);
116         else if(limited){
117                 simple(dest, cmd);      /* doesn't return if dialout succeeds */
118                 rlogin(dest, cmd);      /* doesn't return if dialout succeeds */
119         } else {
120                 rlogin(dest, cmd);      /* doesn't return if dialout succeeds */
121                 simple(dest, cmd);      /* doesn't return if dialout succeeds */
122         }
123         punt(firsterr);
124 }
125
126 /*
127  *  just dial and use as a byte stream with remote echo
128  */
129 void
130 simple(char *dest, char *cmd)
131 {
132         int net;
133
134         net = dodial(dest, 0, 0);
135         if(net < 0)
136                 return;
137
138         if(cmd)
139                 dosystem(net, cmd);
140
141         if(!cooked)
142                 rawon();
143         stdcon(net);
144         exits(0);
145 }
146
147 /*
148  *  dial, do UCB authentication, use as a byte stream with local echo
149  *
150  *  return if dial failed
151  */
152 void
153 rlogin(char *dest, char *cmd)
154 {
155         int net;
156         char buf[128];
157         char *p;
158         char *localuser;
159
160         /* only useful on TCP */
161         if(strchr(dest, '!')
162         && (strncmp(dest, "tcp!", 4)!=0 && strncmp(dest, "net!", 4)!=0))
163                 return;
164
165         net = dodial(dest, "tcp", "login");
166         if(net < 0)
167                 return;
168
169         /*
170          *  do UCB rlogin authentication
171          */
172         localuser = getuser();
173         if(remuser == 0){
174                 if(limited)
175                         remuser = ":";
176                 else
177                         remuser = localuser;
178         }
179         p = getenv("TERM");
180         if(p == 0)
181                 p = "p9";
182         if(write(net, "", 1)<0
183         || write(net, localuser, strlen(localuser)+1)<0
184         || write(net, remuser, strlen(remuser)+1)<0
185         || write(net, p, strlen(p)+1)<0){
186                 close(net);
187                 punt("BSD authentication failed");
188         }
189         if(read(net, buf, 1) != 1)
190                 punt("BSD authentication failed1");
191         if(buf[0] != 0){
192                 fprint(2, "con: remote error: ");
193                 while(read(net, buf, 1) == 1){
194                         write(2, buf, 1);
195                         if(buf[0] == '\n')
196                                 break;
197                 }
198                 exits("read");
199         }
200
201         if(cmd)
202                 dosystem(net, cmd);
203
204         if(!cooked)
205                 rawon();
206         nltocr = 1;
207         stdcon(net);
208         exits(0);
209 }
210
211 /*
212  *  just open a device and use it as a connection
213  */
214 void
215 device(char *dest, char *cmd)
216 {
217         int net;
218         char cname[128];
219
220         net = open(dest, ORDWR);
221         if(net < 0) {
222                 fprint(2, "con: cannot open %s: %r\n", dest);
223                 exits("open");
224         }
225         snprint(cname, sizeof cname, "%sctl", dest);
226         ctl = open(cname, ORDWR);
227         if (baud > 0) {
228                 if(ctl >= 0){
229                         /* set speed and use fifos if available */
230                         fprint(ctl, "b%d i1", baud);
231                 }
232                 else
233                         fprint(2, "con: cannot open %s: %r\n", cname);
234         }
235
236         if(cmd)
237                 dosystem(net, cmd);
238
239         if(!cooked)
240                 rawon();
241         stdcon(net);
242         exits(0);
243 }
244
245 /*
246  *  ignore interrupts
247  */
248 void
249 notifyf(void *a, char *msg)
250 {
251         USED(a);
252
253         if(strstr(msg, "yankee"))
254                 noted(NDFLT);
255         if(strstr(msg, "closed pipe")
256         || strcmp(msg, "interrupt") == 0
257         || strcmp(msg, "hangup") == 0)
258                 noted(NCONT);
259         noted(NDFLT);
260 }
261
262 /*
263  *  turn keyboard raw mode on
264  */
265 void
266 rawon(void)
267 {
268         if(debug)
269                 fprint(2, "rawon\n");
270         if(raw)
271                 return;
272         if(consctl < 0)
273                 consctl = open("/dev/consctl", OWRITE);
274         if(consctl < 0){
275 //              fprint(2, "can't open consctl\n");
276                 return;
277         }
278         write(consctl, "rawon", 5);
279         raw = 1;
280 }
281
282 /*
283  *  turn keyboard raw mode off
284  */
285 void
286 rawoff(void)
287 {
288         if(debug)
289                 fprint(2, "rawoff\n");
290         if(raw == 0)
291                 return;
292         if(consctl < 0)
293                 consctl = open("/dev/consctl", OWRITE);
294         if(consctl < 0){
295 //              fprint(2, "can't open consctl\n");
296                 return;
297         }
298         write(consctl, "rawoff", 6);
299         raw = 0;
300 }
301
302 /*
303  *  control menu
304  */
305 #define STDHELP "\t(b)reak, (q)uit, (i)nterrupt, toggle printing (r)eturns, (.)continue, (!cmd)\n"
306
307 int
308 menu(int net)
309 {
310         char buf[MAXMSG];
311         long n;
312         int done;
313         int wasraw = raw;
314
315         if(wasraw)
316                 rawoff();
317
318         fprint(2, ">>> ");
319         for(done = 0; !done; ){
320                 n = read(0, buf, sizeof(buf)-1);
321                 if(n <= 0)
322                         return -1;
323                 buf[n] = 0;
324                 switch(buf[0]){
325                 case '!':
326                         print(buf);
327                         system(net, buf+1);
328                         print("!\n");
329                         done = 1;
330                         break;
331                 case '.':
332                         done = 1;
333                         break;
334                 case 'q':
335                         return -1;
336                 case 'i':
337                         buf[0] = 0x1c;
338                         write(net, buf, 1);
339                         done = 1;
340                         break;
341                 case 'b':
342                         if(ctl >= 0)
343                                 write(ctl, "k", 1);
344                         done = 1;
345                         break;
346                 case 'r':
347                         returns = 1-returns;
348                         done = 1;
349                         break;
350                 default:
351                         fprint(2, STDHELP);
352                         break;
353                 }
354                 if(!done)
355                         fprint(2, ">>> ");
356         }
357
358         if(wasraw)
359                 rawon();
360         else
361                 rawoff();
362         return 0;
363 }
364
365 void
366 post(char *srv, int fd)
367 {
368         int f;
369         char buf[32];
370
371         f = create(srv, OWRITE /* |ORCLOSE */ , 0666);
372         if(f < 0)
373                 sysfatal("create %s: %r", srv);
374         snprint(buf, sizeof buf, "%d", fd);
375         if(write(f, buf, strlen(buf)) != strlen(buf))
376                 sysfatal("write %s: %r", srv);
377         close(f);
378 }
379
380 /*
381  *  the real work.  two processes pass bytes back and forth between the
382  *  terminal and the network.
383  */
384 void
385 stdcon(int net)
386 {
387         int netpid;
388         int p[2];
389         char *svc;
390
391         svc = nil;
392         if (srv) {
393                 if(pipe(p) < 0)
394                         sysfatal("pipe: %r");
395                 if (srv[0] != '/')
396                         svc = smprint("/srv/%s", srv);
397                 else
398                         svc = srv;
399                 post(svc, p[0]);
400                 close(p[0]);
401                 dup(p[1], 0);
402                 dup(p[1], 1);
403                 /* pipe is now std in & out */
404         }
405         ttypid = getpid();
406         switch(netpid = rfork(RFMEM|RFPROC)){
407         case -1:
408                 perror("con");
409                 exits("fork");
410         case 0:
411                 notify(notifyf);
412                 fromnet(net);
413                 if (svc)
414                         remove(svc);
415                 postnote(PNPROC, ttypid, "die yankee dog");
416                 exits(0);
417         default:
418                 notify(notifyf);
419                 fromkbd(net);
420                 if (svc)
421                         remove(svc);
422                 if(notkbd)
423                         for(;;)
424                                 sleep(0);
425                 postnote(PNPROC, netpid, "die yankee dog");
426                 exits(0);
427         }
428 }
429
430 /*
431  *  Read the keyboard and write it to the network.  '^\' gets us into
432  *  the menu.
433  */
434 void
435 fromkbd(int net)
436 {
437         long n;
438         char buf[MAXMSG];
439         char *p, *ep;
440         int eofs;
441
442         eofs = 0;
443         for(;;){
444                 n = read(0, buf, sizeof(buf));
445                 if(n < 0){
446                         if(wasintr()){
447                                 if(!raw){
448                                         buf[0] = 0x7f;
449                                         n = 1;
450                                 } else
451                                         continue;
452                         } else
453                                 return;
454                 }
455                 if(n == 0){
456                         if(++eofs > 32)
457                                 return;
458                 } else
459                         eofs = 0;
460                 if(n && memchr(buf, 0x1c, n)){
461                         if(menu(net) < 0)
462                                 return;
463                 }else{
464                         if(!raw && n==0){
465                                 buf[0] = 0x4;
466                                 n = 1;
467                         }
468                         if(nltocr){
469                                 ep = buf+n;
470                                 for(p = buf; p < ep; p++)
471                                         switch(*p){
472                                         case '\r':
473                                                 *p = '\n';
474                                                 break;
475                                         case '\n':
476                                                 *p = '\r';
477                                                 break;
478                                         }
479                         }
480                         if(iwrite(net, buf, n) != n)
481                                 return;
482                 }
483         }
484 }
485
486 /*
487  *  Read from the network and write to the screen.
488  *  Filter out spurious carriage returns.
489  */
490 void
491 fromnet(int net)
492 {
493         long n;
494         char buf[MAXMSG];
495         char *cp, *ep;
496
497         for(;;){
498                 n = iread(net, buf, sizeof(buf));
499                 if(n < 0)
500                         return;
501                 if(n == 0)
502                         continue;
503
504                 if (strip)
505                         for (cp=buf; cp<buf+n; cp++)
506                                 *cp &= 0177;
507
508                 if(crtonl) {
509                         /* convert cr's to nl's */
510                         for (cp = buf; cp < buf + n; cp++)
511                                 if (*cp == '\r')
512                                         *cp = '\n';
513                 }
514                 else if(!returns){
515                         /* convert cr's to null's */
516                         cp = buf;
517                         ep = buf + n;
518                         while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){
519                                 memmove(cp, cp+1, ep-cp-1);
520                                 ep--;
521                                 n--;
522                         }
523                 }
524
525                 if(n > 0 && iwrite(outfd, buf, n) != n){
526                         if(outfd == 1)
527                                 return;
528                         outfd = 1;
529                         if(iwrite(1, buf, n) != n)
530                                 return;
531                 }
532         }
533 }
534
535 /*
536  *  dial and return a data connection
537  */
538 int
539 dodial(char *dest, char *net, char *service)
540 {
541         char name[128];
542         char devdir[128];
543         int data;
544
545         devdir[0] = 0;
546         strcpy(name, netmkaddr(dest, net, service));
547         data = dial(name, 0, devdir, &ctl);
548         if(data < 0){
549                 seterr(name);
550                 return -1;
551         }
552         fprint(2, "connected to %s on %s\n", name, devdir);
553         return data;
554 }
555
556 void
557 dosystem(int fd, char *cmd)
558 {
559         char *p;
560
561         p = system(fd, cmd);
562         if(p){
563                 print("con: %s terminated with %s\n", cmd, p);
564                 exits(p);
565         }
566 }
567
568 /*
569  *  run a command with the network connection as standard IO
570  */
571 char *
572 system(int fd, char *cmd)
573 {
574         int pid;
575         int p;
576         static Waitmsg msg;
577         int pfd[2];
578         int n;
579         char buf[4096];
580
581         if(pipe(pfd) < 0){
582                 perror("pipe");
583                 return "pipe failed";
584         }
585         outfd = pfd[1];
586
587         close(consctl);
588         consctl = -1;
589         switch(pid = fork()){
590         case -1:
591                 perror("con");
592                 return "fork failed";
593         case 0:
594                 close(pfd[1]);
595                 dup(pfd[0], 0);
596                 dup(fd, 1);
597                 close(ctl);
598                 close(fd);
599                 close(pfd[0]);
600                 if(*cmd)
601                         execl("/bin/rc", "rc", "-c", cmd, nil);
602                 else
603                         execl("/bin/rc", "rc", nil);
604                 perror("con");
605                 exits("exec");
606                 break;
607         default:
608                 close(pfd[0]);
609                 while((n = read(pfd[1], buf, sizeof(buf))) > 0)
610                         if(write(fd, buf, n) != n)
611                                 break;
612                 p = waitpid();
613                 outfd = 1;
614                 close(pfd[1]);
615                 if(p < 0 || p != pid)
616                         return "lost child";
617                 break;
618         }
619         return msg.msg;
620 }
621
622 int
623 wasintr(void)
624 {
625         return strcmp(syserr(), "interrupted") == 0;
626 }
627
628 void
629 punt(char *msg)
630 {
631         if(*msg == 0)
632                 msg = transerr;
633         fprint(2, "con: %s\n", msg);
634         exits(msg);
635 }
636
637 char*
638 syserr(void)
639 {
640         static char err[ERRMAX];
641         errstr(err, sizeof err);
642         return err;
643 }
644
645 void
646 seterr(char *addr)
647 {
648         char *se = syserr();
649
650         if(verbose)
651                 fprint(2, "'%s' calling %s\n", se, addr);
652         if(firsterr[0] && (strstr(se, "translate") ||
653          strstr(se, "file does not exist") ||
654          strstr(se, "unknown address") ||
655          strstr(se, "directory entry not found")))
656                 return;
657         strcpy(firsterr, se);
658 }
659
660
661 long
662 iread(int f, void *a, int n)
663 {
664         long m;
665
666         for(;;){
667                 m = read(f, a, n);
668                 if(m >= 0 || !wasintr())
669                         break;
670         }
671         return m;
672 }
673
674 long
675 iwrite(int f, void *a, int n)
676 {
677         long m;
678
679         m = write(f, a, n);
680         if(m < 0 && wasintr())
681                 return n;
682         return m;
683 }