BSD 4 release
[unix-history] / usr / src / cmd / cu.c
CommitLineData
31cef89c 1static char *sccsid = "@(#)cu.c 4.1 (Berkeley) 10/1/80";
fb5d50d1
BJ
2#include <stdio.h>
3#include <signal.h>
4#include <sgtty.h>
5/*
6 * cu telno [-t] [-s speed] [-l line] [-a acu]
7 *
8 * -t is for dial-out to terminal.
9 * speeds are: 110, 134, 150, 300, 1200. 300 is default.
10 *
11 * Escape with `~' at beginning of line.
12 * Ordinary diversions are ~<, ~> and ~>>.
13 * Silent output diversions are ~>: and ~>>:.
14 * Terminate output diversion with ~> alone.
15 * Quit is ~. and ~! gives local command or shell.
16 * Also ~$ for canned procedure pumping remote.
17 * ~%put from [to] and ~%take from [to] invoke builtins
18 */
19
20#define CRLF "\r\n"
21#define wrc(ds) write(ds,&c,1)
22
23
24char *devcul = "/dev/cul0";
25char *devcua = "/dev/cua0";
26char *lspeed = "300";
27
28int ln; /* fd for comm line */
29char tkill, terase; /* current input kill & erase */
30int efk; /* process of id of listener */
31char c;
32char oc;
33
34char *connmsg[] = {
35 "",
36 "line busy",
37 "call dropped",
38 "no carrier",
39 "can't fork",
40 "acu access",
41 "tty access",
42 "tty hung",
43 "usage: cu telno [-t] [-s speed] [-l line] [-a acu]"
44};
45
46rdc(ds) {
47
48 ds=read(ds,&c,1);
49 oc = c;
50 c &= 0177;
51 return (ds);
52}
53
54int intr;
55
56sig2()
57{
58 signal(SIGINT, SIG_IGN);
59 intr = 1;
60}
61
62int set14;
63
64xsleep(n)
65{
66 xalarm(n);
67 pause();
68 xalarm(0);
69}
70
71xalarm(n)
72{
73 set14=n;
74 alarm(n);
75}
76
77sig14()
78{
79 signal(SIGALRM, sig14);
80 if (set14) alarm(1);
81}
82
83int dout;
84int nhup;
85int dbflag;
86
87/*
88 * main: get connection, set speed for line.
89 * spawn child to invoke rd to read from line, output to fd 1
90 * main line invokes wr to read tty, write to line
91 */
92main(ac,av)
93char *av[];
94{
95 int fk;
96 int speed;
97 char *telno;
98 struct sgttyb stbuf;
99
100 signal(SIGALRM, sig14);
101 if (ac < 2) {
102 prf(connmsg[8]);
103 exit(8);
104 }
105 for (; ac > 1; av++,ac--) {
106 if (av[1][0] != '-')
107 telno = av[1];
108 else switch(av[1][1]) {
109 case 't':
110 dout = 1;
111 --ac;
112 continue;
113 case 'd':
114 dbflag++;
115 continue;
116 case 's':
117 lspeed = av[2]; ++av; --ac;
118 break;
119 case 'l':
120 devcul = av[2]; ++av; --ac;
121 break;
122 case 'a':
123 devcua = av[2]; ++av; --ac;
124 break;
125 case '0': case '1': case '2': case '3': case '4':
126 case '5': case '6': case '7': case '8': case '9':
127 devcua[strlen(devcua)-1] = av[1][1];
128 devcul[strlen(devcul)-1] = av[1][1];
129 break;
130 default:
131 prf("Bad flag %s", av[1]);
132 break;
133 }
134 }
135 if (!exists(devcua) || !exists(devcul))
136 exit(9);
137 ln = conn(devcul, devcua, telno);
138 if (ln < 0) {
139 prf("Connect failed: %s",connmsg[-ln]);
140 exit(-ln);
141 }
142 switch(atoi(lspeed)) {
143 case 110:
144 speed = B110;break;
145 case 150:
146 speed = B150;break;
147 default:
148 case 300:
149 speed = B300;break;
150 case 1200:
151 speed = B1200;break;
152 }
153 stbuf.sg_ispeed = speed;
154 stbuf.sg_ospeed = speed;
155 stbuf.sg_flags = EVENP|ODDP;
156 if (!dout) {
157 stbuf.sg_flags |= RAW;
158 stbuf.sg_flags &= ~ECHO;
159 }
160 ioctl(ln, TIOCSETP, &stbuf);
161 ioctl(ln, TIOCEXCL, (struct sgttyb *)NULL);
162 ioctl(ln, TIOCHPCL, (struct sgttyb *)NULL);
163 prf("Connected");
164 if (dout)
165 fk = -1;
166 else
167 fk = fork();
168 nhup = (int)signal(SIGINT, SIG_IGN);
169 if (fk == 0) {
170 chwrsig();
171 rd();
172 prf("\007Lost carrier");
173 exit(3);
174 }
175 mode(1);
176 efk = fk;
177 wr();
178 mode(0);
179 kill(fk, SIGKILL);
180 wait((int *)NULL);
181 stbuf.sg_ispeed = 0;
182 stbuf.sg_ospeed = 0;
183 ioctl(ln, TIOCSETP, &stbuf);
184 prf("Disconnected");
185 exit(0);
186}
187
188/*
189 * conn: establish dial-out connection.
190 * Example: fd = conn("/dev/ttyh","/dev/dn1","4500");
191 * Returns descriptor open to tty for reading and writing.
192 * Negative values (-1...-7) denote errors in connmsg.
193 * Uses alarm and fork/wait; requires sig14 handler.
194 * Be sure to disconnect tty when done, via HUPCL or stty 0.
195 */
196
197conn(dev,acu,telno)
198char *dev, *acu, *telno;
199{
200 struct sgttyb stbuf;
201 extern errno;
202 char *p, *q, b[30];
203 int er, fk, dn, dh, t;
204 er=0;
205 fk=(-1);
206 if ((dn=open(acu,1))<0) {
207 er=(errno == 6? 1:5);
208 goto X;
209 }
210 if ((fk=fork()) == (-1)) {
211 er=4;
212 goto X;
213 }
214 if (fk == 0) {
215 open(dev,2);
216 for (;;) pause();
217 }
218 xsleep(2);
219 /*
220 * copy phone #, assure EON
221 */
222 p=b;
223 q=telno;
224 while (*p++=(*q++))
225 ;
226 p--;
227 if (*(p-1)!='<') {
228 /*if (*(p-1)!='-') *p++='-';*/
229 *p++='<';
230 }
231 t=p-b;
232 xalarm(5*t);
233 t=write(dn,b,t);
234 xalarm(0);
235 if (t<0) {
236 er=2;
237 goto X;
238 }
239 /* close(dn) */
240 xalarm(40); /* was 5; sometimes missed carrier */
241 dh = open(dev,2);
242 xalarm(0);
243 if (dh<0) {
244 er=(errno == 4? 3:6);
245 goto X;
246 }
247 ioctl(ln, TIOCGETP, &stbuf);
248 stbuf.sg_flags &= ~ECHO;
249 xalarm(10);
250 ioctl(dh, TIOCSETP, &stbuf);
251 ioctl(dh, TIOCHPCL, (struct sgttyb *)NULL);
252 xalarm(0);
253X:
254 if (er) close(dn);
255 if (fk!=(-1)) {
256 kill(fk, SIGKILL);
257 xalarm(10);
258 while ((t=wait((int *)NULL))!=(-1) && t!=fk);
259 xalarm(0);
260 }
261 return (er? -er:dh);
262}
263
264/*
265 * wr: write to remote: 0 -> line.
266 * ~. terminate
267 * ~<file send file
268 * ~! local login-style shell
269 * ~!cmd execute cmd locally
270 * ~$proc execute proc locally, send output to line
271 * ~%cmd execute builtin cmd (put and take)
272 * ~# send 1-sec break
273 */
274
275wr()
276{
277 int ds,fk,lcl,x;
278 char *p,b[600];
279 for (;;) {
280 p=b;
281 while (rdc(0) == 1) {
282 if (p == b) lcl=(c == '~');
283 if (p == b+1 && b[0] == '~') lcl=(c!='~');
284 /* if (c == 0) oc=c=0177; fake break kludge */
285 if (!lcl) {
286 c = oc;
287 if (wrc(ln) == 0) {
288 prf("line gone"); return;
289 }
290 c &= 0177;
291 }
292 if (lcl) {
293 if (c == 0177) c=tkill;
294 if (c == '\r' || c == '\n') goto A;
295 if (!dout) wrc(0);
296 }
297 *p++=c;
298 if (c == terase) {
299 p=p-2;
300 if (p<b) p=b;
301 }
302 if (c == tkill || c == 0177 || c == '\4' || c == '\r' || c == '\n') p=b;
303 }
304 return;
305A:
306 if (!dout) echo("");
307 *p=0;
308 switch (b[1]) {
309 case '.':
310 case '\004':
311 return;
312 case '#':
313 ioctl(ln, TIOCSBRK, 0);
314 sleep(1);
315 ioctl(ln, TIOCCBRK, 0);
316 continue;
317 case '!':
318 case '$':
319 fk = fork();
320 if (fk == 0) {
321 char *getenv();
322 char *shell = getenv("SHELL");
323 if (shell == 0) shell = "/bin/sh";
324 close(1);
325 dup(b[1] == '$'? ln:2);
326 close(ln);
327 mode(0);
328 if (!nhup) signal(SIGINT, SIG_DFL);
329 if (b[2] == 0) execl(shell,shell,0);
330 /* if (b[2] == 0) execl(shell,"-",0); */
331 else execl(shell,"sh","-c",b+2,0);
332 prf("Can't execute shell");
333 exit(~0);
334 }
335 if (fk!=(-1)) {
336 while (wait(&x)!=fk);
337 }
338 mode(1);
339 if (b[1] == '!') echo("!");
340 else {
341 if (dout) echo("$");
342 }
343 break;
344 case '<':
345 if (b[2] == 0) break;
346 if ((ds=open(b+2,0))<0) {
347 prf("Can't divert %s",b+1);
348 break;
349 }
350 intr=x=0;
351 mode(2);
352 if (!nhup) signal(SIGINT, sig2);
353 while (!intr && rdc(ds) == 1) {
354 if (wrc(ln) == 0) {
355 x=1;
356 break;
357 }
358 }
359 signal(SIGINT, SIG_IGN);
360 close(ds);
361 mode(1);
362 if (x) return;
363 if (dout) echo("<");
364 break;
365 case '>':
366 case ':':
367 {
368 FILE *fp; char tbuff[128]; register char *q;
369 sprintf(tbuff,"/tmp/cu%d",efk);
370 if(NULL==(fp = fopen(tbuff,"w"))) {
371 prf("Can't tell other demon to divert");
372 break;
373 }
374 fprintf(fp,"%s\n",(b[1]=='>'?&b[2]: &b[1] ));
375 if(dbflag) prf("name to be written in temporary:"),prf(&b[2]);
376 fclose(fp);
377 kill(efk,SIGEMT);
378 }
379 break;
380#ifdef SIGTSTP
381#define CTRLZ 26
382 case CTRLZ:
383 mode(0);
384 kill(getpid(), SIGTSTP);
385 mode(1);
386 break;
387#endif
388 case '%':
389 dopercen(&b[2]);
390 break;
391 default:
392 prf("Use `~~' to start line with `~'");
393 }
394 continue;
395 }
396}
397
398dopercen(line)
399register char *line;
400{
401 char *args[10];
402 register narg, f;
403 int rcount;
404 for (narg = 0; narg < 10;) {
405 while(*line == ' ' || *line == '\t')
406 line++;
407 if (*line == '\0')
408 break;
409 args[narg++] = line;
410 while(*line != '\0' && *line != ' ' && *line != '\t')
411 line++;
412 if (*line == '\0')
413 break;
414 *line++ = '\0';
415 }
416 if (equal(args[0], "take")) {
417 if (narg < 2) {
418 prf("usage: ~%%take from [to]");
419 return;
420 }
421 if (narg < 3)
422 args[2] = args[1];
423 wrln("echo '~>:'");
424 wrln(args[2]);
425 wrln(";tee /dev/null <");
426 wrln(args[1]);
427 wrln(";echo '~>'\n");
428 return;
429 } else if (equal(args[0], "put")) {
430 if (narg < 2) {
431 prf("usage: ~%%put from [to]");
432 return;
433 }
434 if (narg < 3)
435 args[2] = args[1];
436 if ((f = open(args[1], 0)) < 0) {
437 prf("cannot open: %s", args[1]);
438 return;
439 }
440 wrln("stty -echo;cat >");
441 wrln(args[2]);
442 wrln(";stty echo\n");
443 xsleep(5);
444 intr = 0;
445 if (!nhup)
446 signal(SIGINT, sig2);
447 mode(2);
448 rcount = 0;
449 while(!intr && rdc(f) == 1) {
450 rcount++;
451 if (c == tkill || c == terase)
452 wrln("\\");
453 if (wrc(ln) != 1) {
454 xsleep(2);
455 if (wrc(ln) != 1) {
456 prf("character missed");
457 intr = 1;
458 break;
459 }
460 }
461 }
462 signal(SIGINT, SIG_IGN);
463 close(f);
464 if (intr) {
465 wrln("\n");
466 prf("stopped after %d bytes", rcount);
467 }
468 wrln("\004");
469 xsleep(5);
470 mode(1);
471 return;
472 }
473 prf("~%%%s unknown\n", args[0]);
474}
475
476equal(s1, s2)
477register char *s1, *s2;
478{
479 while (*s1++ == *s2)
480 if (*s2++ == '\0')
481 return(1);
482 return(0);
483}
484
485wrln(s)
486register char *s;
487{
488 while (*s)
489 write(ln, s++, 1);
490}
491/* chwrsig: Catch orders from wr process
492 * to instigate diversion
493 */
494int whoami;
495chwrsig(){
496 int dodiver();
497 whoami = getpid();
498 signal(SIGEMT,dodiver);
499}
500int ds,slnt;
501int justrung;
502dodiver(){
503 static char dobuff[128], morejunk[256]; register char *cp;
504 FILE *fp;
505 justrung = 1;
506 signal(SIGEMT,dodiver);
507 sprintf(dobuff,"/tmp/cu%d",whoami);
508 fp = fopen(dobuff,"r");
509 if(fp==NULL) prf("Couldn't open temporary");
510 unlink(dobuff);
511 if(dbflag) {
512 prf("Name of temporary:");
513 prf(dobuff);
514 }
515 fgets(dobuff,128,fp); fclose(fp);
516 if(dbflag) {
517 prf("Name of target file:");
518 prf(dobuff);
519 }
520 for(cp = dobuff-1; *++cp; ) /* squash newline */
521 if(*cp=='\n') *cp=0;
522 cp = dobuff;
523 if (*cp=='>') cp++;
524 if (*cp==':') {
525 cp++;
526 if(*cp==0) {
527 slnt ^= 1;
528 return;
529 } else {
530 slnt = 1;
531 }
532 }
533 if (ds >= 0) close(ds);
534 if (*cp==0) {
535 slnt = 0;
536 ds = -1;
537 return;
538 }
539 if (*dobuff!='>' || (ds=open(cp,1))<0) ds=creat(cp,0644);
540 lseek(ds, (long)0, 2);
541 if(ds < 0) prf("Creat failed:"), prf(cp);
542 if (ds<0) prf("Can't divert %s",cp+1);
543}
544
545
546/*
547 * rd: read from remote: line -> 1
548 * catch:
549 * ~>[>][:][file]
550 * stuff from file...
551 * ~> (ends diversion)
552 */
553
554rd()
555{
556 extern int ds,slnt;
557 char *p,*q,b[600];
558 p=b;
559 ds=(-1);
560agin:
561 while (rdc(ln) == 1) {
562 if (!slnt) wrc(1);
563 *p++=c;
564 if (c!='\n') continue;
565 q=p;
566 p=b;
567 if (b[0]!='~' || b[1]!='>') {
568 if (*(q-2) == '\r') {
569 q--;
570 *(q-1)=(*q);
571 }
572 if (ds>=0) write(ds,b,q-b);
573 continue;
574 }
575 if (ds>=0) close(ds);
576 if (slnt) {
577 write(1, b, q - b);
578 write(1, CRLF, sizeof(CRLF));
579 }
580 if (*(q-2) == '\r') q--;
581 *(q-1)=0;
582 slnt=0;
583 q=b+2;
584 if (*q == '>') q++;
585 if (*q == ':') {
586 slnt=1;
587 q++;
588 }
589 if (*q == 0) {
590 ds=(-1);
591 continue;
592 }
593 if (b[2]!='>' || (ds=open(q,1))<0) ds=creat(q,0644);
594 lseek(ds, (long)0, 2);
595 if (ds<0) prf("Can't divert %s",b+1);
596 }
597 if(justrung) {
598 justrung = 0;
599 goto agin;
600 }
601}
602
603struct {char lobyte; char hibyte;};
604mode(f)
605{
606 struct sgttyb stbuf;
607 if (dout) return;
608 ioctl(0, TIOCGETP, &stbuf);
609 tkill = stbuf.sg_kill;
610 terase = stbuf.sg_erase;
611 if (f == 0) {
612 stbuf.sg_flags &= ~RAW;
613 stbuf.sg_flags |= ECHO|CRMOD;
614 }
615 if (f == 1) {
616 stbuf.sg_flags |= RAW;
617 stbuf.sg_flags &= ~(ECHO|CRMOD);
618 }
619 if (f == 2) {
620 stbuf.sg_flags &= ~RAW;
621 stbuf.sg_flags &= ~(ECHO|CRMOD);
622 }
623 ioctl(0, TIOCSETP, &stbuf);
624}
625
626echo(s)
627char *s;
628{
629 char *p;
630 for (p=s;*p;p++);
631 if (p>s) write(0,s,p-s);
632 write(0,CRLF, sizeof(CRLF));
633}
634
635prf(f, s)
636char *f;
637char *s;
638{
639 fprintf(stderr, f, s);
640 fprintf(stderr, CRLF);
641}
642
643exists(devname)
644char *devname;
645{
646 if (access(devname, 0)==0)
647 return(1);
648 prf("%s does not exist", devname);
649 return(0);
650}