xref: /original-bsd/old/cu/cu.c (revision 447ecad6)
1 static char *sccsid = "@(#)cu.c	4.1 (Berkeley) 10/01/80";
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 
24 char	*devcul	= "/dev/cul0";
25 char	*devcua	= "/dev/cua0";
26 char	*lspeed	= "300";
27 
28 int	ln;	/* fd for comm line */
29 char	tkill, terase;	/* current input kill & erase */
30 int	efk;		/* process of id of listener  */
31 char	c;
32 char	oc;
33 
34 char	*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 
46 rdc(ds) {
47 
48 	ds=read(ds,&c,1);
49 	oc = c;
50 	c &= 0177;
51 	return (ds);
52 }
53 
54 int intr;
55 
56 sig2()
57 {
58 	signal(SIGINT, SIG_IGN);
59 	intr = 1;
60 }
61 
62 int set14;
63 
64 xsleep(n)
65 {
66 	xalarm(n);
67 	pause();
68 	xalarm(0);
69 }
70 
71 xalarm(n)
72 {
73 	set14=n;
74 	alarm(n);
75 }
76 
77 sig14()
78 {
79 	signal(SIGALRM, sig14);
80 	if (set14) alarm(1);
81 }
82 
83 int	dout;
84 int	nhup;
85 int	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  */
92 main(ac,av)
93 char *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 
197 conn(dev,acu,telno)
198 char *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);
253 X:
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 
275 wr()
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;
305 A:
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 
398 dopercen(line)
399 register 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 
476 equal(s1, s2)
477 register char *s1, *s2;
478 {
479 	while (*s1++ == *s2)
480 		if (*s2++ == '\0')
481 			return(1);
482 	return(0);
483 }
484 
485 wrln(s)
486 register char *s;
487 {
488 	while (*s)
489 		write(ln, s++, 1);
490 }
491 /*	chwrsig:  Catch orders from wr process
492  *	to instigate diversion
493  */
494 int whoami;
495 chwrsig(){
496 	int dodiver();
497 	whoami = getpid();
498 	signal(SIGEMT,dodiver);
499 }
500 int ds,slnt;
501 int justrung;
502 dodiver(){
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 
554 rd()
555 {
556 	extern int ds,slnt;
557 	char *p,*q,b[600];
558 	p=b;
559 	ds=(-1);
560 agin:
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 
603 struct {char lobyte; char hibyte;};
604 mode(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 
626 echo(s)
627 char *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 
635 prf(f, s)
636 char *f;
637 char *s;
638 {
639 	fprintf(stderr, f, s);
640 	fprintf(stderr, CRLF);
641 }
642 
643 exists(devname)
644 char *devname;
645 {
646 	if (access(devname, 0)==0)
647 		return(1);
648 	prf("%s does not exist", devname);
649 	return(0);
650 }
651