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