xref: /386bsd/usr/src/usr.bin/tip/tip.c (revision a2142627)
1 /*
2  * Copyright (c) 1983 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
37  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 static char sccsid[] = "@(#)tip.c	5.15 (Berkeley) 2/4/91";
42 #endif /* not lint */
43 
44 /*
45  * tip - UNIX link to other systems
46  *  tip [-v] [-speed] system-name
47  * or
48  *  cu phone-number [-s speed] [-l line] [-a acu]
49  */
50 #include "tip.h"
51 #include "pathnames.h"
52 
53 /*
54  * Baud rate mapping table
55  */
56 int bauds[] = {
57 	0, 50, 75, 110, 134, 150, 200, 300, 600,
58 	1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, -1
59 };
60 
61 int	disc = OTTYDISC;		/* tip normally runs this way */
62 void	intprompt();
63 void	timeout();
64 void	cleanup();
65 char	*sname();
66 char	PNbuf[256];			/* This limits the size of a number */
67 
main(argc,argv)68 main(argc, argv)
69 	char *argv[];
70 {
71 	char *system = NOSTR;
72 	register int i;
73 	register char *p;
74 	char sbuf[12];
75 
76 	gid = getgid();
77 	egid = getegid();
78 	uid = getuid();
79 	euid = geteuid();
80 	if (equal(sname(argv[0]), "cu")) {
81 		cumode = 1;
82 		cumain(argc, argv);
83 		goto cucommon;
84 	}
85 
86 	if (argc > 4) {
87 		fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n");
88 		exit(1);
89 	}
90 	if (!isatty(0)) {
91 		fprintf(stderr, "tip: must be interactive\n");
92 		exit(1);
93 	}
94 
95 	for (; argc > 1; argv++, argc--) {
96 		if (argv[1][0] != '-')
97 			system = argv[1];
98 		else switch (argv[1][1]) {
99 
100 		case 'v':
101 			vflag++;
102 			break;
103 
104 		case '0': case '1': case '2': case '3': case '4':
105 		case '5': case '6': case '7': case '8': case '9':
106 			BR = atoi(&argv[1][1]);
107 			break;
108 
109 		default:
110 			fprintf(stderr, "tip: %s, unknown option\n", argv[1]);
111 			break;
112 		}
113 	}
114 
115 	if (system == NOSTR)
116 		goto notnumber;
117 	if (isalpha(*system))
118 		goto notnumber;
119 	/*
120 	 * System name is really a phone number...
121 	 * Copy the number then stomp on the original (in case the number
122 	 *	is private, we don't want 'ps' or 'w' to find it).
123 	 */
124 	if (strlen(system) > sizeof PNbuf - 1) {
125 		fprintf(stderr, "tip: phone number too long (max = %d bytes)\n",
126 			sizeof PNbuf - 1);
127 		exit(1);
128 	}
129 	strncpy( PNbuf, system, sizeof PNbuf - 1 );
130 	for (p = system; *p; p++)
131 		*p = '\0';
132 	PN = PNbuf;
133 	(void)sprintf(sbuf, "tip%d", BR);
134 	system = sbuf;
135 
136 notnumber:
137 	(void)signal(SIGINT, cleanup);
138 	(void)signal(SIGQUIT, cleanup);
139 	(void)signal(SIGHUP, cleanup);
140 	(void)signal(SIGTERM, cleanup);
141 
142 	if ((i = hunt(system)) == 0) {
143 		printf("all ports busy\n");
144 		exit(3);
145 	}
146 	if (i == -1) {
147 		printf("link down\n");
148 		(void)uu_unlock(uucplock);
149 		exit(3);
150 	}
151 	setbuf(stdout, NULL);
152 	loginit();
153 
154 	/*
155 	 * Kludge, their's no easy way to get the initialization
156 	 *   in the right order, so force it here
157 	 */
158 	if ((PH = getenv("PHONES")) == NOSTR)
159 		PH = _PATH_PHONES;
160 	vinit();				/* init variables */
161 	setparity("even");			/* set the parity table */
162 	if ((i = speed(number(value(BAUDRATE)))) == NULL) {
163 		printf("tip: bad baud rate %d\n", number(value(BAUDRATE)));
164 		(void)uu_unlock(uucplock);
165 		exit(3);
166 	}
167 
168 	/*
169 	 * Now that we have the logfile and the ACU open
170 	 *  return to the real uid and gid.  These things will
171 	 *  be closed on exit.  Swap real and effective uid's
172 	 *  so we can get the original permissions back
173 	 *  for removing the uucp lock.
174 	 */
175 	user_uid();
176 
177 	/*
178 	 * Hardwired connections require the
179 	 *  line speed set before they make any transmissions
180 	 *  (this is particularly true of things like a DF03-AC)
181 	 */
182 	if (HW)
183 		ttysetup(i);
184 	if (p = connect()) {
185 		printf("\07%s\n[EOT]\n", p);
186 		daemon_uid();
187 		(void)uu_unlock(uucplock);
188 		exit(1);
189 	}
190 	if (!HW)
191 		ttysetup(i);
192 cucommon:
193 	/*
194 	 * From here down the code is shared with
195 	 * the "cu" version of tip.
196 	 */
197 
198 	ioctl(0, TIOCGETP, (char *)&defarg);
199 	ioctl(0, TIOCGETC, (char *)&defchars);
200 	ioctl(0, TIOCGLTC, (char *)&deflchars);
201 	ioctl(0, TIOCGETD, (char *)&odisc);
202 	arg = defarg;
203 	arg.sg_flags = ANYP | CBREAK;
204 	tchars = defchars;
205 	tchars.t_intrc = tchars.t_quitc = -1;
206 	ltchars = deflchars;
207 	ltchars.t_suspc = ltchars.t_dsuspc = ltchars.t_flushc
208 		= ltchars.t_lnextc = -1;
209 	raw();
210 
211 	pipe(fildes); pipe(repdes);
212 	(void)signal(SIGALRM, timeout);
213 
214 	/*
215 	 * Everything's set up now:
216 	 *	connection established (hardwired or dialup)
217 	 *	line conditioned (baud rate, mode, etc.)
218 	 *	internal data structures (variables)
219 	 * so, fork one process for local side and one for remote.
220 	 */
221 	printf(cumode ? "Connected\r\n" : "\07connected\r\n");
222 	if (pid = fork())
223 		tipin();
224 	else
225 		tipout();
226 	/*NOTREACHED*/
227 }
228 
229 void
cleanup()230 cleanup()
231 {
232 
233 	daemon_uid();
234 	(void)uu_unlock(uucplock);
235 	if (odisc)
236 		ioctl(0, TIOCSETD, (char *)&odisc);
237 	exit(0);
238 }
239 
240 /*
241  * Muck with user ID's.  We are setuid to the owner of the lock
242  * directory when we start.  user_uid() reverses real and effective
243  * ID's after startup, to run with the user's permissions.
244  * daemon_uid() switches back to the privileged uid for unlocking.
245  * Finally, to avoid running a shell with the wrong real uid,
246  * shell_uid() sets real and effective uid's to the user's real ID.
247  */
248 static int uidswapped;
249 
user_uid()250 user_uid()
251 {
252 	if (uidswapped == 0) {
253 		setregid(egid, gid);
254 		setreuid(euid, uid);
255 		uidswapped = 1;
256 	}
257 }
258 
daemon_uid()259 daemon_uid()
260 {
261 
262 	if (uidswapped) {
263 		setreuid(uid, euid);
264 		setregid(gid, egid);
265 		uidswapped = 0;
266 	}
267 }
268 
shell_uid()269 shell_uid()
270 {
271 
272 	setreuid(uid, uid);
273 	setregid(gid, gid);
274 }
275 
276 /*
277  * put the controlling keyboard into raw mode
278  */
raw()279 raw()
280 {
281 
282 	ioctl(0, TIOCSETP, &arg);
283 	ioctl(0, TIOCSETC, &tchars);
284 	ioctl(0, TIOCSLTC, &ltchars);
285 	ioctl(0, TIOCSETD, (char *)&disc);
286 }
287 
288 
289 /*
290  * return keyboard to normal mode
291  */
unraw()292 unraw()
293 {
294 
295 	ioctl(0, TIOCSETD, (char *)&odisc);
296 	ioctl(0, TIOCSETP, (char *)&defarg);
297 	ioctl(0, TIOCSETC, (char *)&defchars);
298 	ioctl(0, TIOCSLTC, (char *)&deflchars);
299 }
300 
301 static	jmp_buf promptbuf;
302 
303 /*
304  * Print string ``s'', then read a string
305  *  in from the terminal.  Handles signals & allows use of
306  *  normal erase and kill characters.
307  */
prompt(s,p)308 prompt(s, p)
309 	char *s;
310 	register char *p;
311 {
312 	register char *b = p;
313 	sig_t oint, oquit;
314 
315 	stoprompt = 0;
316 	oint = signal(SIGINT, intprompt);
317 	oquit = signal(SIGQUIT, SIG_IGN);
318 	unraw();
319 	printf("%s", s);
320 	if (setjmp(promptbuf) == 0)
321 		while ((*p = getchar()) != EOF && *p != '\n')
322 			p++;
323 	*p = '\0';
324 
325 	raw();
326 	(void)signal(SIGINT, oint);
327 	(void)signal(SIGQUIT, oquit);
328 	return (stoprompt || p == b);
329 }
330 
331 /*
332  * Interrupt service routine during prompting
333  */
334 void
intprompt()335 intprompt()
336 {
337 
338 	(void)signal(SIGINT, SIG_IGN);
339 	stoprompt = 1;
340 	printf("\r\n");
341 	longjmp(promptbuf, 1);
342 }
343 
344 /*
345  * ****TIPIN   TIPIN****
346  */
tipin()347 tipin()
348 {
349 	char gch, bol = 1;
350 
351 	/*
352 	 * Kinda klugey here...
353 	 *   check for scripting being turned on from the .tiprc file,
354 	 *   but be careful about just using setscript(), as we may
355 	 *   send a SIGEMT before tipout has a chance to set up catching
356 	 *   it; so wait a second, then setscript()
357 	 */
358 	if (boolean(value(SCRIPT))) {
359 		sleep(1);
360 		setscript();
361 	}
362 
363 	while (1) {
364 		gch = getchar()&0177;
365 		if ((gch == character(value(ESCAPE))) && bol) {
366 			if (!(gch = escape()))
367 				continue;
368 		} else if (!cumode && gch == character(value(RAISECHAR))) {
369 			boolean(value(RAISE)) = !boolean(value(RAISE));
370 			continue;
371 		} else if (gch == '\r') {
372 			bol = 1;
373 			pwrite(FD, &gch, 1);
374 			if (boolean(value(HALFDUPLEX)))
375 				printf("\r\n");
376 			continue;
377 		} else if (!cumode && gch == character(value(FORCE)))
378 			gch = getchar()&0177;
379 		bol = any(gch, value(EOL));
380 		if (boolean(value(RAISE)) && islower(gch))
381 			gch = toupper(gch);
382 		pwrite(FD, &gch, 1);
383 		if (boolean(value(HALFDUPLEX)))
384 			printf("%c", gch);
385 	}
386 }
387 
388 /*
389  * Escape handler --
390  *  called on recognition of ``escapec'' at the beginning of a line
391  */
escape()392 escape()
393 {
394 	register char gch;
395 	register esctable_t *p;
396 	char c = character(value(ESCAPE));
397 	extern esctable_t etable[];
398 
399 	gch = (getchar()&0177);
400 	for (p = etable; p->e_char; p++)
401 		if (p->e_char == gch) {
402 			if ((p->e_flags&PRIV) && uid)
403 				continue;
404 			printf("%s", ctrl(c));
405 			(*p->e_func)(gch);
406 			return (0);
407 		}
408 	/* ESCAPE ESCAPE forces ESCAPE */
409 	if (c != gch)
410 		pwrite(FD, &c, 1);
411 	return (gch);
412 }
413 
speed(n)414 speed(n)
415 	int n;
416 {
417 	register int *p;
418 
419 	for (p = bauds; *p != -1;  p++)
420 		if (*p == n)
421 			return (p - bauds);
422 	return (NULL);
423 }
424 
any(c,p)425 any(c, p)
426 	register char c, *p;
427 {
428 	while (p && *p)
429 		if (*p++ == c)
430 			return (1);
431 	return (0);
432 }
433 
size(s)434 size(s)
435 	register char	*s;
436 {
437 	register int i = 0;
438 
439 	while (s && *s++)
440 		i++;
441 	return (i);
442 }
443 
444 char *
interp(s)445 interp(s)
446 	register char *s;
447 {
448 	static char buf[256];
449 	register char *p = buf, c, *q;
450 
451 	while (c = *s++) {
452 		for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
453 			if (*q++ == c) {
454 				*p++ = '\\'; *p++ = *q;
455 				goto next;
456 			}
457 		if (c < 040) {
458 			*p++ = '^'; *p++ = c + 'A'-1;
459 		} else if (c == 0177) {
460 			*p++ = '^'; *p++ = '?';
461 		} else
462 			*p++ = c;
463 	next:
464 		;
465 	}
466 	*p = '\0';
467 	return (buf);
468 }
469 
470 char *
ctrl(c)471 ctrl(c)
472 	char c;
473 {
474 	static char s[3];
475 
476 	if (c < 040 || c == 0177) {
477 		s[0] = '^';
478 		s[1] = c == 0177 ? '?' : c+'A'-1;
479 		s[2] = '\0';
480 	} else {
481 		s[0] = c;
482 		s[1] = '\0';
483 	}
484 	return (s);
485 }
486 
487 /*
488  * Help command
489  */
help(c)490 help(c)
491 	char c;
492 {
493 	register esctable_t *p;
494 	extern esctable_t etable[];
495 
496 	printf("%c\r\n", c);
497 	for (p = etable; p->e_char; p++) {
498 		if ((p->e_flags&PRIV) && uid)
499 			continue;
500 		printf("%2s", ctrl(character(value(ESCAPE))));
501 		printf("%-2s %c   %s\r\n", ctrl(p->e_char),
502 			p->e_flags&EXP ? '*': ' ', p->e_help);
503 	}
504 }
505 
506 /*
507  * Set up the "remote" tty's state
508  */
ttysetup(speed)509 ttysetup(speed)
510 	int speed;
511 {
512 	unsigned bits = LDECCTQ;
513 
514 	arg.sg_ispeed = arg.sg_ospeed = speed;
515 	arg.sg_flags = RAW;
516 	if (boolean(value(TAND)))
517 		arg.sg_flags |= TANDEM;
518 	ioctl(FD, TIOCSETP, (char *)&arg);
519 	ioctl(FD, TIOCLBIS, (char *)&bits);
520 }
521 
522 /*
523  * Return "simple" name from a file name,
524  * strip leading directories.
525  */
526 char *
sname(s)527 sname(s)
528 	register char *s;
529 {
530 	register char *p = s;
531 
532 	while (*s)
533 		if (*s++ == '/')
534 			p = s;
535 	return (p);
536 }
537 
538 static char partab[0200];
539 static int bits8;
540 
541 /*
542  * Do a write to the remote machine with the correct parity.
543  * We are doing 8 bit wide output, so we just generate a character
544  * with the right parity and output it.
545  */
pwrite(fd,buf,n)546 pwrite(fd, buf, n)
547 	int fd;
548 	char *buf;
549 	register int n;
550 {
551 	register int i;
552 	register char *bp;
553 	extern int errno;
554 
555 	bp = buf;
556 	if (bits8 == 0) {
557 		static char *mbp; static sz;
558 
559 		if (mbp == 0 || n > sz) {
560 			if (mbp)
561 				free(mbp);
562 			mbp = (char *) malloc(n);
563 			sz = n;
564 		}
565 
566 		bp = mbp;
567 		for (i = 0; i < n; i++)
568 			*bp++ = partab[*buf++ & 0177];
569 		bp = mbp;
570 	}
571 	if (write(fd, bp, n) < 0) {
572 		if (errno == EIO)
573 			tipabort("Lost carrier.");
574 		/* this is questionable */
575 		perror("write");
576 	}
577 }
578 
579 /*
580  * Build a parity table with appropriate high-order bit.
581  */
setparity(defparity)582 setparity(defparity)
583 	char *defparity;
584 {
585 	register int i, flip, clr, set;
586 	char *parity;
587 	extern char evenpartab[];
588 
589 	if (value(PARITY) == NOSTR)
590 		value(PARITY) = defparity;
591 	parity = value(PARITY);
592 	if (equal(parity, "none")) {
593 		bits8 = 1;
594 		return;
595 	}
596 	bits8 = 0;
597 	flip = 0;
598 	clr = 0377;
599 	set = 0;
600 	if (equal(parity, "odd"))
601 		flip = 0200;			/* reverse bit 7 */
602 	else if (equal(parity, "zero"))
603 		clr = 0177;			/* turn off bit 7 */
604 	else if (equal(parity, "one"))
605 		set = 0200;			/* turn on bit 7 */
606 	else if (!equal(parity, "even")) {
607 		(void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
608 		(void) fflush(stderr);
609 	}
610 	for (i = 0; i < 0200; i++)
611 		partab[i] = evenpartab[i] ^ flip | set & clr;
612 }
613