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