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