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