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