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