xref: /netbsd-src/usr.bin/tip/tip.c (revision cda4f8f6ee55684e8d311b86c99ea59191e6b74f)
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, 115200, -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 
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 	 * Now that we have the logfile and the ACU open
156 	 *  return to the real uid and gid.  These things will
157 	 *  be closed on exit.  Swap real and effective uid's
158 	 *  so we can get the original permissions back
159 	 *  for removing the uucp lock.
160 	 */
161 	user_uid();
162 
163 	/*
164 	 * Kludge, their's no easy way to get the initialization
165 	 *   in the right order, so force it here
166 	 */
167 	if ((PH = getenv("PHONES")) == NOSTR)
168 		PH = _PATH_PHONES;
169 	vinit();				/* init variables */
170 	setparity("even");			/* set the parity table */
171 	if ((i = speed(number(value(BAUDRATE)))) == NULL) {
172 		printf("tip: bad baud rate %d\n", number(value(BAUDRATE)));
173 		daemon_uid();
174 		(void)uu_unlock(uucplock);
175 		exit(3);
176 	}
177 
178 	/*
179 	 * Hardwired connections require the
180 	 *  line speed set before they make any transmissions
181 	 *  (this is particularly true of things like a DF03-AC)
182 	 */
183 	if (HW)
184 		ttysetup(i);
185 	if (p = connect()) {
186 		printf("\07%s\n[EOT]\n", p);
187 		daemon_uid();
188 		(void)uu_unlock(uucplock);
189 		exit(1);
190 	}
191 	if (!HW)
192 		ttysetup(i);
193 cucommon:
194 	/*
195 	 * From here down the code is shared with
196 	 * the "cu" version of tip.
197 	 */
198 
199 	ioctl(0, TIOCGETP, (char *)&defarg);
200 	ioctl(0, TIOCGETC, (char *)&defchars);
201 	ioctl(0, TIOCGLTC, (char *)&deflchars);
202 	ioctl(0, TIOCGETD, (char *)&odisc);
203 	arg = defarg;
204 	arg.sg_flags = ANYP | CBREAK;
205 	tchars = defchars;
206 	tchars.t_intrc = tchars.t_quitc = -1;
207 	ltchars = deflchars;
208 	ltchars.t_suspc = ltchars.t_dsuspc = ltchars.t_flushc
209 		= ltchars.t_lnextc = -1;
210 	raw();
211 
212 	pipe(fildes); pipe(repdes);
213 	(void)signal(SIGALRM, timeout);
214 
215 	/*
216 	 * Everything's set up now:
217 	 *	connection established (hardwired or dialup)
218 	 *	line conditioned (baud rate, mode, etc.)
219 	 *	internal data structures (variables)
220 	 * so, fork one process for local side and one for remote.
221 	 */
222 	printf(cumode ? "Connected\r\n" : "\07connected\r\n");
223 	if (pid = fork())
224 		tipin();
225 	else
226 		tipout();
227 	/*NOTREACHED*/
228 }
229 
230 void
231 cleanup()
232 {
233 
234 	daemon_uid();
235 	(void)uu_unlock(uucplock);
236 	if (odisc)
237 		ioctl(0, TIOCSETD, (char *)&odisc);
238 	exit(0);
239 }
240 
241 /*
242  * Muck with user ID's.  We are setuid to the owner of the lock
243  * directory when we start.  user_uid() reverses real and effective
244  * ID's after startup, to run with the user's permissions.
245  * daemon_uid() switches back to the privileged uid for unlocking.
246  * Finally, to avoid running a shell with the wrong real uid,
247  * shell_uid() sets real and effective uid's to the user's real ID.
248  */
249 static int uidswapped;
250 
251 user_uid()
252 {
253 	if (uidswapped == 0) {
254 		setregid(egid, gid);
255 		setreuid(euid, uid);
256 		uidswapped = 1;
257 	}
258 }
259 
260 daemon_uid()
261 {
262 
263 	if (uidswapped) {
264 		setreuid(uid, euid);
265 		setregid(gid, egid);
266 		uidswapped = 0;
267 	}
268 }
269 
270 shell_uid()
271 {
272 
273 	setreuid(uid, uid);
274 	setregid(gid, gid);
275 }
276 
277 /*
278  * put the controlling keyboard into raw mode
279  */
280 raw()
281 {
282 
283 	ioctl(0, TIOCSETP, &arg);
284 	ioctl(0, TIOCSETC, &tchars);
285 	ioctl(0, TIOCSLTC, &ltchars);
286 	ioctl(0, TIOCSETD, (char *)&disc);
287 }
288 
289 
290 /*
291  * return keyboard to normal mode
292  */
293 unraw()
294 {
295 
296 	ioctl(0, TIOCSETD, (char *)&odisc);
297 	ioctl(0, TIOCSETP, (char *)&defarg);
298 	ioctl(0, TIOCSETC, (char *)&defchars);
299 	ioctl(0, TIOCSLTC, (char *)&deflchars);
300 }
301 
302 static	jmp_buf promptbuf;
303 
304 /*
305  * Print string ``s'', then read a string
306  *  in from the terminal.  Handles signals & allows use of
307  *  normal erase and kill characters.
308  */
309 prompt(s, p)
310 	char *s;
311 	register char *p;
312 {
313 	register char *b = p;
314 	sig_t oint, oquit;
315 
316 	stoprompt = 0;
317 	oint = signal(SIGINT, intprompt);
318 	oquit = signal(SIGQUIT, SIG_IGN);
319 	unraw();
320 	printf("%s", s);
321 	if (setjmp(promptbuf) == 0)
322 		while ((*p = getchar()) != EOF && *p != '\n')
323 			p++;
324 	*p = '\0';
325 
326 	raw();
327 	(void)signal(SIGINT, oint);
328 	(void)signal(SIGQUIT, oquit);
329 	return (stoprompt || p == b);
330 }
331 
332 /*
333  * Interrupt service routine during prompting
334  */
335 void
336 intprompt()
337 {
338 
339 	(void)signal(SIGINT, SIG_IGN);
340 	stoprompt = 1;
341 	printf("\r\n");
342 	longjmp(promptbuf, 1);
343 }
344 
345 /*
346  * ****TIPIN   TIPIN****
347  */
348 tipin()
349 {
350 	char gch, bol = 1;
351 
352 	/*
353 	 * Kinda klugey here...
354 	 *   check for scripting being turned on from the .tiprc file,
355 	 *   but be careful about just using setscript(), as we may
356 	 *   send a SIGEMT before tipout has a chance to set up catching
357 	 *   it; so wait a second, then setscript()
358 	 */
359 	if (boolean(value(SCRIPT))) {
360 		sleep(1);
361 		setscript();
362 	}
363 
364 	while (1) {
365 		gch = getchar()&0177;
366 		if ((gch == character(value(ESCAPE))) && bol) {
367 			if (!(gch = escape()))
368 				continue;
369 		} else if (!cumode && gch == character(value(RAISECHAR))) {
370 			boolean(value(RAISE)) = !boolean(value(RAISE));
371 			continue;
372 		} else if (gch == '\r') {
373 			bol = 1;
374 			pwrite(FD, &gch, 1);
375 			if (boolean(value(HALFDUPLEX)))
376 				printf("\r\n");
377 			continue;
378 		} else if (!cumode && gch == character(value(FORCE)))
379 			gch = getchar()&0177;
380 		bol = any(gch, value(EOL));
381 		if (boolean(value(RAISE)) && islower(gch))
382 			gch = toupper(gch);
383 		pwrite(FD, &gch, 1);
384 		if (boolean(value(HALFDUPLEX)))
385 			printf("%c", gch);
386 	}
387 }
388 
389 /*
390  * Escape handler --
391  *  called on recognition of ``escapec'' at the beginning of a line
392  */
393 escape()
394 {
395 	register char gch;
396 	register esctable_t *p;
397 	char c = character(value(ESCAPE));
398 	extern esctable_t etable[];
399 
400 	gch = (getchar()&0177);
401 	for (p = etable; p->e_char; p++)
402 		if (p->e_char == gch) {
403 			if ((p->e_flags&PRIV) && uid)
404 				continue;
405 			printf("%s", ctrl(c));
406 			(*p->e_func)(gch);
407 			return (0);
408 		}
409 	/* ESCAPE ESCAPE forces ESCAPE */
410 	if (c != gch)
411 		pwrite(FD, &c, 1);
412 	return (gch);
413 }
414 
415 speed(n)
416 	int n;
417 {
418 	register int *p;
419 
420 	for (p = bauds; *p != -1;  p++)
421 		if (*p == n)
422 			return (p - bauds);
423 	return (NULL);
424 }
425 
426 any(c, p)
427 	register char c, *p;
428 {
429 	while (p && *p)
430 		if (*p++ == c)
431 			return (1);
432 	return (0);
433 }
434 
435 size(s)
436 	register char	*s;
437 {
438 	register int i = 0;
439 
440 	while (s && *s++)
441 		i++;
442 	return (i);
443 }
444 
445 char *
446 interp(s)
447 	register char *s;
448 {
449 	static char buf[256];
450 	register char *p = buf, c, *q;
451 
452 	while (c = *s++) {
453 		for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
454 			if (*q++ == c) {
455 				*p++ = '\\'; *p++ = *q;
456 				goto next;
457 			}
458 		if (c < 040) {
459 			*p++ = '^'; *p++ = c + 'A'-1;
460 		} else if (c == 0177) {
461 			*p++ = '^'; *p++ = '?';
462 		} else
463 			*p++ = c;
464 	next:
465 		;
466 	}
467 	*p = '\0';
468 	return (buf);
469 }
470 
471 char *
472 ctrl(c)
473 	char c;
474 {
475 	static char s[3];
476 
477 	if (c < 040 || c == 0177) {
478 		s[0] = '^';
479 		s[1] = c == 0177 ? '?' : c+'A'-1;
480 		s[2] = '\0';
481 	} else {
482 		s[0] = c;
483 		s[1] = '\0';
484 	}
485 	return (s);
486 }
487 
488 /*
489  * Help command
490  */
491 help(c)
492 	char c;
493 {
494 	register esctable_t *p;
495 	extern esctable_t etable[];
496 
497 	printf("%c\r\n", c);
498 	for (p = etable; p->e_char; p++) {
499 		if ((p->e_flags&PRIV) && uid)
500 			continue;
501 		printf("%2s", ctrl(character(value(ESCAPE))));
502 		printf("%-2s %c   %s\r\n", ctrl(p->e_char),
503 			p->e_flags&EXP ? '*': ' ', p->e_help);
504 	}
505 }
506 
507 /*
508  * Set up the "remote" tty's state
509  */
510 ttysetup(speed)
511 	int speed;
512 {
513 	unsigned bits = LDECCTQ;
514 
515 	arg.sg_ispeed = arg.sg_ospeed = speed;
516 	arg.sg_flags = RAW;
517 	if (boolean(value(TAND)))
518 		arg.sg_flags |= TANDEM;
519 	ioctl(FD, TIOCSETP, (char *)&arg);
520 	ioctl(FD, TIOCLBIS, (char *)&bits);
521 }
522 
523 /*
524  * Return "simple" name from a file name,
525  * strip leading directories.
526  */
527 char *
528 sname(s)
529 	register char *s;
530 {
531 	register char *p = s;
532 
533 	while (*s)
534 		if (*s++ == '/')
535 			p = s;
536 	return (p);
537 }
538 
539 static char partab[0200];
540 static int bits8;
541 
542 /*
543  * Do a write to the remote machine with the correct parity.
544  * We are doing 8 bit wide output, so we just generate a character
545  * with the right parity and output it.
546  */
547 pwrite(fd, buf, n)
548 	int fd;
549 	char *buf;
550 	register int n;
551 {
552 	register int i;
553 	register char *bp;
554 	extern int errno;
555 
556 	bp = buf;
557 	if (bits8 == 0) {
558 		static char *mbp; static sz;
559 
560 		if (mbp == 0 || n > sz) {
561 			if (mbp)
562 				free(mbp);
563 			mbp = (char *) malloc(n);
564 			sz = n;
565 		}
566 
567 		bp = mbp;
568 		for (i = 0; i < n; i++)
569 			*bp++ = partab[*buf++ & 0177];
570 		bp = mbp;
571 	}
572 	if (write(fd, bp, n) < 0) {
573 		if (errno == EIO)
574 			tipabort("Lost carrier.");
575 		/* this is questionable */
576 		perror("write");
577 	}
578 }
579 
580 /*
581  * Build a parity table with appropriate high-order bit.
582  */
583 setparity(defparity)
584 	char *defparity;
585 {
586 	register int i, flip, clr, set;
587 	char *parity;
588 	extern char evenpartab[];
589 
590 	if (value(PARITY) == NOSTR)
591 		value(PARITY) = defparity;
592 	parity = value(PARITY);
593 	if (equal(parity, "none")) {
594 		bits8 = 1;
595 		return;
596 	}
597 	bits8 = 0;
598 	flip = 0;
599 	clr = 0377;
600 	set = 0;
601 	if (equal(parity, "odd"))
602 		flip = 0200;			/* reverse bit 7 */
603 	else if (equal(parity, "zero"))
604 		clr = 0177;			/* turn off bit 7 */
605 	else if (equal(parity, "one"))
606 		set = 0200;			/* turn on bit 7 */
607 	else if (!equal(parity, "even")) {
608 		(void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
609 		(void) fflush(stderr);
610 	}
611 	for (i = 0; i < 0200; i++)
612 		partab[i] = evenpartab[i] ^ flip | set & clr;
613 }
614