xref: /dragonfly/usr.bin/tip/tip.c (revision 78478697)
1 /*
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  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  * @(#) Copyright (c) 1983, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)tip.c	8.1 (Berkeley) 6/6/93
35  * $FreeBSD: src/usr.bin/tip/tip/tip.c,v 1.12.2.2 2001/06/02 08:08:24 phk Exp $
36  */
37 
38 /*
39 	Forward declarations
40 */
41 void ttysetup (int speed);
42 
43 /*
44  * tip - UNIX link to other systems
45  *  tip [-v] [-speed] system-name
46  * or
47  *  cu phone-number [-s speed] [-l line] [-a acu]
48  */
49 
50 #include <err.h>
51 #include <errno.h>
52 #include <sys/types.h>
53 #include <libutil.h>
54 #include "tipconf.h"
55 #include "tip.h"
56 #include "pathnames.h"
57 
58 /*
59  * Baud rate mapping table
60  */
61 #if !HAVE_TERMIOS
62 CONST int bauds[] = {
63 	0, 50, 75, 110, 134, 150, 200, 300, 600,
64 	1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, -1
65 };
66 #endif
67 
68 #if !HAVE_TERMIOS
69 int	disc = OTTYDISC;		/* tip normally runs this way */
70 #endif
71 
72 static void	intprompt(int);
73 static void	killchild(void);
74 static void	tipdone(int);
75 static char	*sname(char *);
76 char	PNbuf[256];			/* This limits the size of a number */
77 
78 static void usage(void);
79 void setparity(char *);
80 void xpwrite(int, char *, int);
81 char escape(void);
82 void tipin(void);
83 int prompt(char *, char *, size_t);
84 void unraw(void);
85 void shell_uid(void);
86 void daemon_uid(void);
87 void user_uid(void);
88 
89 int
90 main(int argc, char *argv[])
91 {
92 	char *system = NULL;
93 	int i;
94 	char *p;
95 	char sbuf[12];
96 
97 	gid = getgid();
98 	egid = getegid();
99 	uid = getuid();
100 	euid = geteuid();
101 
102 #if INCLUDE_CU_INTERFACE
103 	if (equal(sname(argv[0]), "cu")) {
104 		cumode = 1;
105 		cumain(argc, argv);
106 		goto cucommon;
107 	}
108 #endif /* INCLUDE_CU_INTERFACE */
109 
110 	if (argc > 4)
111 		usage();
112 	if (!isatty(0))
113 		errx(1, "must be interactive");
114 
115 	for (; argc > 1; argv++, argc--) {
116 		if (argv[1][0] != '-')
117 			system = argv[1];
118 		else switch (argv[1][1]) {
119 
120 		case 'v':
121 			vflag++;
122 			break;
123 
124 		case '0': case '1': case '2': case '3': case '4':
125 		case '5': case '6': case '7': case '8': case '9':
126 			BR = atoi(&argv[1][1]);
127 			break;
128 
129 		default:
130 			warnx("%s, unknown option", argv[1]);
131 			break;
132 		}
133 	}
134 
135 	if (system == NULL)
136 		goto notnumber;
137 	if (isalpha(*system))
138 		goto notnumber;
139 	/*
140 	 * System name is really a phone number...
141 	 * Copy the number then stomp on the original (in case the number
142 	 *	is private, we don't want 'ps' or 'w' to find it).
143 	 */
144 	if (strlen(system) > sizeof(PNbuf) - 1)
145 		errx(1, "phone number too long (max = %zd bytes)", sizeof PNbuf - 1);
146 	strncpy(PNbuf, system, sizeof(PNbuf) - 1);
147 	for (p = system; *p; p++)
148 		*p = '\0';
149 	PN = PNbuf;
150 	(void)snprintf(sbuf, sizeof(sbuf), "tip%ld", BR);
151 	system = sbuf;
152 
153 notnumber:
154 	(void)signal(SIGINT, cleanup);
155 	(void)signal(SIGQUIT, cleanup);
156 	(void)signal(SIGHUP, cleanup);
157 	(void)signal(SIGTERM, cleanup);
158 	(void)signal(SIGUSR1, tipdone);
159 
160 	if ((i = hunt(system)) == 0) {
161 		printf("all ports busy\n");
162 		exit(3);
163 	}
164 	if (i == -1) {
165 		printf("link down\n");
166 		(void)uu_unlock(uucplock);
167 		exit(3);
168 	}
169 	setbuf(stdout, NULL);
170 	loginit();
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", number(value(BAUDRATE)));
182 		(void)uu_unlock(uucplock);
183 		exit(3);
184 	}
185 
186 	/*
187 	 * Now that we have the logfile and the ACU open
188 	 *  return to the real uid and gid.  These things will
189 	 *  be closed on exit.  Swap real and effective uid's
190 	 *  so we can get the original permissions back
191 	 *  for removing the uucp lock.
192 	 */
193 	user_uid();
194 
195 	/*
196 	 * Hardwired connections require the
197 	 *  line speed set before they make any transmissions
198 	 *  (this is particularly true of things like a DF03-AC)
199 	 */
200 	if (HW)
201 		ttysetup(i);
202 	if ((p = connect())) {
203 		printf("\07%s\n[EOT]\n", p);
204 		daemon_uid();
205 		(void)uu_unlock(uucplock);
206 		exit(1);
207 	}
208 	if (!HW)
209 		ttysetup(i);
210 cucommon:
211 	/*
212 	 * From here down the code is shared with
213 	 * the "cu" version of tip.
214 	 */
215 
216 #if HAVE_TERMIOS
217 	tcgetattr (0, &otermios);
218 	ctermios = otermios;
219 #ifndef _POSIX_SOURCE
220 	ctermios.c_iflag = (IMAXBEL|IXANY|ISTRIP|IXON|BRKINT);
221 	ctermios.c_lflag = (PENDIN|IEXTEN|ISIG|ECHOCTL|ECHOE|ECHOKE);
222 #else
223 	ctermios.c_iflag = (ISTRIP|IXON|BRKINT);
224 	ctermios.c_lflag = (PENDIN|IEXTEN|ISIG|ECHOE);
225 #endif
226 	ctermios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8);
227 	ctermios.c_cc[VINTR] = 	ctermios.c_cc[VQUIT] = -1;
228 	ctermios.c_cc[VSUSP] = ctermios.c_cc[VDSUSP] = ctermios.c_cc[VDISCARD] =
229 		ctermios.c_cc[VLNEXT] = -1;
230 #else /* HAVE_TERMIOS */
231 	ioctl(0, TIOCGETP, (char *)&defarg);
232 	ioctl(0, TIOCGETC, (char *)&defchars);
233 	ioctl(0, TIOCGLTC, (char *)&deflchars);
234 	ioctl(0, TIOCGETD, (char *)&odisc);
235 	arg = defarg;
236 	arg.sg_flags = ANYP | CBREAK;
237 	tchars = defchars;
238 	tchars.t_intrc = tchars.t_quitc = -1;
239 	ltchars = deflchars;
240 	ltchars.t_suspc = ltchars.t_dsuspc = ltchars.t_flushc
241 		= ltchars.t_lnextc = -1;
242 #endif /* HAVE_TERMIOS */
243 	raw();
244 
245 	pipe(fildes); pipe(repdes);
246 	(void)signal(SIGALRM, timeoutfunc);
247 
248 	/*
249 	 * Everything's set up now:
250 	 *	connection established (hardwired or dialup)
251 	 *	line conditioned (baud rate, mode, etc.)
252 	 *	internal data structures (variables)
253 	 * so, fork one process for local side and one for remote.
254 	 */
255 	printf(cumode ? "Connected\r\n" : "\07connected\r\n");
256 
257 	if (LI != NULL && tiplink (LI, 0) != 0) {
258 		tipabort ("login failed");
259 	}
260 
261 	if ((pid = fork()))
262 		tipin();
263 	else
264 		tipout();
265 	/*NOTREACHED*/
266 }
267 
268 static void
269 usage(void)
270 {
271 	fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n");
272 	exit(1);
273 }
274 
275 void
276 killchild(void)
277 {
278 	if (pid != 0) {
279 		kill(pid, SIGTERM);
280 		pid = 0;
281 	}
282 }
283 
284 void
285 cleanup(int signo)
286 {
287 
288 	daemon_uid();
289 	(void)uu_unlock(uucplock);
290 #if !HAVE_TERMIOS
291 	if (odisc)
292 		ioctl(0, TIOCSETD, (char *)&odisc);
293 #endif
294 	exit(0);
295 }
296 
297 void
298 tipdone(int signo)
299 {
300 	tipabort("Hangup.");
301 }
302 /*
303  * Muck with user ID's.  We are setuid to the owner of the lock
304  * directory when we start.  user_uid() reverses real and effective
305  * ID's after startup, to run with the user's permissions.
306  * daemon_uid() switches back to the privileged uid for unlocking.
307  * Finally, to avoid running a shell with the wrong real uid,
308  * shell_uid() sets real and effective uid's to the user's real ID.
309  */
310 static int uidswapped;
311 
312 void
313 user_uid(void)
314 {
315 	if (uidswapped == 0) {
316 		seteuid(uid);
317 		uidswapped = 1;
318 	}
319 }
320 
321 void
322 daemon_uid(void)
323 {
324 	if (uidswapped) {
325 		seteuid(euid);
326 		uidswapped = 0;
327 	}
328 }
329 
330 void
331 shell_uid(void)
332 {
333 	setegid(gid);
334 	seteuid(uid);
335 }
336 
337 /*
338  * put the controlling keyboard into raw mode
339  */
340 void
341 raw(void)
342 {
343 #if HAVE_TERMIOS
344 	tcsetattr (0, TCSANOW, &ctermios);
345 #else /* HAVE_TERMIOS */
346 
347 	ioctl(0, TIOCSETP, &arg);
348 	ioctl(0, TIOCSETC, &tchars);
349 	ioctl(0, TIOCSLTC, &ltchars);
350 	ioctl(0, TIOCSETD, (char *)&disc);
351 #endif /* HAVE_TERMIOS */
352 }
353 
354 
355 /*
356  * return keyboard to normal mode
357  */
358 void
359 unraw(void)
360 {
361 #if HAVE_TERMIOS
362 	tcsetattr (0, TCSANOW, &otermios);
363 #else /* HAVE_TERMIOS */
364 
365 	ioctl(0, TIOCSETD, (char *)&odisc);
366 	ioctl(0, TIOCSETP, (char *)&defarg);
367 	ioctl(0, TIOCSETC, (char *)&defchars);
368 	ioctl(0, TIOCSLTC, (char *)&deflchars);
369 #endif /* HAVE_TERMIOS */
370 }
371 
372 static	jmp_buf promptbuf;
373 
374 /*
375  * Print string ``s'', then read a string
376  *  in from the terminal.  Handles signals & allows use of
377  *  normal erase and kill characters.
378  */
379 int
380 prompt(char *s, char *p, size_t sz)
381 {
382 	char *b = p;
383 	sig_t oint, oquit;
384 
385 	stoprompt = 0;
386 	oint = signal(SIGINT, intprompt);
387 	oquit = signal(SIGQUIT, SIG_IGN);
388 	unraw();
389 	printf("%s", s);
390 	if (setjmp(promptbuf) == 0)
391 		while ((*p = getchar()) != EOF && *p != '\n' && --sz > 0)
392 			p++;
393 	*p = '\0';
394 
395 	raw();
396 	(void)signal(SIGINT, oint);
397 	(void)signal(SIGQUIT, oquit);
398 	return (stoprompt || p == b);
399 }
400 
401 /*
402  * Interrupt service routine during prompting
403  */
404 void
405 intprompt(int signo)
406 {
407 
408 	(void)signal(SIGINT, SIG_IGN);
409 	stoprompt = 1;
410 	printf("\r\n");
411 	longjmp(promptbuf, 1);
412 }
413 
414 /*
415  * ****TIPIN   TIPIN****
416  */
417 void
418 tipin(void)
419 {
420 	int i;
421 	char gch, bol = 1;
422 
423 	atexit(killchild);
424 
425 	/*
426 	 * Kinda klugey here...
427 	 *   check for scripting being turned on from the .tiprc file,
428 	 *   but be careful about just using setscript(), as we may
429 	 *   send a SIGEMT before tipout has a chance to set up catching
430 	 *   it; so wait a second, then setscript()
431 	 */
432 	if (boolean(value(SCRIPT))) {
433 		sleep(1);
434 		setscript();
435 	}
436 
437 	while (1) {
438 		i = getchar();
439 		if (i == EOF)
440 			break;
441 		gch = i&0177;
442 		if ((gch == character(value(ESCAPE))) && bol) {
443 			if (!(gch = escape()))
444 				continue;
445 		} else if (!cumode && gch == character(value(RAISECHAR))) {
446 			boolean(value(RAISE)) = !boolean(value(RAISE));
447 			continue;
448 		} else if (gch == '\r') {
449 			bol = 1;
450 			xpwrite(FD, &gch, 1);
451 			if (boolean(value(HALFDUPLEX)))
452 				printf("\r\n");
453 			continue;
454 		} else if (!cumode && gch == character(value(FORCE))) {
455 			i = getchar();
456 			if (i == EOF)
457 				break;
458 			gch = i & 0177;
459 		}
460 		bol = any(gch, value(EOL));
461 		if (boolean(value(RAISE)) && islower(gch))
462 			gch = toupper(gch);
463 		xpwrite(FD, &gch, 1);
464 		if (boolean(value(HALFDUPLEX)))
465 			printf("%c", gch);
466 	}
467 }
468 
469 extern esctable_t etable[];
470 
471 /*
472  * Escape handler --
473  *  called on recognition of ``escapec'' at the beginning of a line
474  */
475 char
476 escape(void)
477 {
478 	char gch;
479 	esctable_t *p;
480 	char c = character(value(ESCAPE));
481 	int i;
482 
483 	i = getchar();
484 	if (i == EOF)
485 		return 0;
486 	gch = (i&0177);
487 	for (p = etable; p->e_char; p++)
488 		if (p->e_char == gch) {
489 			if ((p->e_flags&PRIV) && uid)
490 				continue;
491 			printf("%s", ctrl(c));
492 			(*p->e_func)(gch);
493 			return (0);
494 		}
495 	/* ESCAPE ESCAPE forces ESCAPE */
496 	if (c != gch)
497 		xpwrite(FD, &c, 1);
498 	return (gch);
499 }
500 
501 int
502 speed(int n)
503 {
504 #if HAVE_TERMIOS
505 	return (n);
506 #else
507 	CONST int *p;
508 
509 	for (p = bauds; *p != -1;  p++)
510 		if (*p == n)
511 			return (p - bauds);
512 	return (NULL);
513 #endif
514 }
515 
516 int
517 any(char c, char *p)
518 {
519 	while (p && *p)
520 		if (*p++ == c)
521 			return (1);
522 	return (0);
523 }
524 
525 int
526 size(char *s)
527 {
528 	int i = 0;
529 
530 	while (s && *s++)
531 		i++;
532 	return (i);
533 }
534 
535 char *
536 interp(char *s)
537 {
538 	static char buf[256];
539 	char *p = buf, c, *q;
540 
541 	while ((c = *s++)) {
542 		for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
543 			if (*q++ == c) {
544 				*p++ = '\\'; *p++ = *q;
545 				goto next;
546 			}
547 		if (c < 040) {
548 			*p++ = '^'; *p++ = c + 'A'-1;
549 		} else if (c == 0177) {
550 			*p++ = '^'; *p++ = '?';
551 		} else
552 			*p++ = c;
553 	next:
554 		;
555 	}
556 	*p = '\0';
557 	return (buf);
558 }
559 
560 char *
561 ctrl(char c)
562 {
563 	static char s[3];
564 
565 	if (c < 040 || c == 0177) {
566 		s[0] = '^';
567 		s[1] = c == 0177 ? '?' : c+'A'-1;
568 		s[2] = '\0';
569 	} else {
570 		s[0] = c;
571 		s[1] = '\0';
572 	}
573 	return (s);
574 }
575 
576 /*
577  * Help command
578  */
579 void
580 help(int c)
581 {
582 	esctable_t *p;
583 
584 	printf("%c\r\n", c);
585 	for (p = etable; p->e_char; p++) {
586 		if ((p->e_flags&PRIV) && uid)
587 			continue;
588 		printf("%2s", ctrl(character(value(ESCAPE))));
589 		printf("%-2s %c   %s\r\n", ctrl(p->e_char),
590 			p->e_flags&EXP ? '*': ' ', p->e_help);
591 	}
592 }
593 
594 /*
595  * Set up the "remote" tty's state
596  */
597 void
598 ttysetup (int speed)
599 {
600 #if HAVE_TERMIOS
601 	struct termios termios;
602 	tcgetattr (FD, &termios);
603 	if (boolean(value(TAND)))
604 		termios.c_iflag = IXOFF;
605 	else
606 		termios.c_iflag = 0;
607 #ifndef _POSIX_SOURCE
608 	termios.c_lflag = (PENDIN|ECHOKE|ECHOE);
609 #else
610 	termios.c_lflag = (PENDIN|ECHOE);
611 #endif
612 	termios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8);
613 	termios.c_ispeed = termios.c_ospeed = speed;
614 	tcsetattr (FD, TCSANOW, &termios);
615 #else /* HAVE_TERMIOS */
616 	unsigned bits = LDECCTQ;
617 
618 	arg.sg_ispeed = arg.sg_ospeed = speed;
619 	arg.sg_flags = RAW;
620 	if (boolean(value(TAND)))
621 		arg.sg_flags |= TANDEM;
622 	ioctl(FD, TIOCSETP, (char *)&arg);
623 	ioctl(FD, TIOCLBIS, (char *)&bits);
624 #endif /* HAVE_TERMIOS */
625 }
626 
627 /*
628  * Return "simple" name from a file name,
629  * strip leading directories.
630  */
631 char *
632 sname(char *s)
633 {
634 	char *p = s;
635 
636 	while (*s)
637 		if (*s++ == '/')
638 			p = s;
639 	return (p);
640 }
641 
642 static char partab[0200];
643 static int bits8;
644 
645 /*
646  * Do a write to the remote machine with the correct parity.
647  * We are doing 8 bit wide output, so we just generate a character
648  * with the right parity and output it.
649  */
650 void
651 xpwrite(int fd, char *buf, int n)
652 {
653 	int i;
654 	char *bp;
655 
656 	bp = buf;
657 	if (bits8 == 0)
658 		for (i = 0; i < n; i++) {
659 			*bp = partab[(*bp) & 0177];
660 			bp++;
661 		}
662 	if (write(fd, buf, n) < 0) {
663 		if (errno == EIO)
664 			tipabort("Lost carrier.");
665 		if (errno == ENODEV)
666 			tipabort("tty not available.");
667 		tipabort("Something wrong...");
668 	}
669 }
670 
671 /*
672  * Build a parity table with appropriate high-order bit.
673  */
674 void
675 setparity(char *defparity)
676 {
677 	int i, flip, clr, set;
678 	char *parity;
679 	extern char evenpartab[];
680 
681 	if (value(PARITY) == NULL)
682 		value(PARITY) = defparity;
683 	parity = value(PARITY);
684 	if (equal(parity, "none")) {
685 		bits8 = 1;
686 		return;
687 	}
688 	bits8 = 0;
689 	flip = 0;
690 	clr = 0377;
691 	set = 0;
692 	if (equal(parity, "odd"))
693 		flip = 0200;			/* reverse bit 7 */
694 	else if (equal(parity, "zero"))
695 		clr = 0177;			/* turn off bit 7 */
696 	else if (equal(parity, "one"))
697 		set = 0200;			/* turn on bit 7 */
698 	else if (!equal(parity, "even")) {
699 		(void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
700 		(void) fflush(stderr);
701 	}
702 	for (i = 0; i < 0200; i++)
703 		partab[i] = (evenpartab[i] ^ flip) | (set & clr);
704 }
705