xref: /illumos-gate/usr/src/cmd/tip/tip.c (revision 7c478bd9)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 /*
6  * Copyright (c) 1983 Regents of the University of California.
7  * All rights reserved.  The Berkeley software License Agreement
8  * specifies the terms and conditions for redistribution.
9  */
10 
11 #ident	"%Z%%M%	%I%	%E% SMI"	/* from UCB 5.4 4/3/86 */
12 
13 /*
14  * tip - UNIX link to other systems
15  *  tip [-v] [-speed] system-name
16  * or
17  *  cu phone-number [-s speed] [-l line] [-a acu]
18  */
19 #include "tip.h"
20 #include <sys/wait.h>
21 #include <locale.h>
22 
23 /*
24  * Baud rate mapping table
25  */
26 int bauds[] = {
27 	0, 50, 75, 110, 134, 150, 200, 300, 600,
28 	1200, 1800, 2400, 4800, 9600, 19200, 38400,
29 	57600, 76800, 115200, 153600, 230400, 307200, 460800, -1
30 };
31 
32 void	intprompt();
33 void	timeout();
34 void	deadkid();
35 void	cleanup();
36 char	*sname();
37 char	PNbuf[256];			/* This limits the size of a number */
38 int	noparity = 0;
39 
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 	(void) setlocale(LC_CTYPE, "");
89 
90 	if (system == NOSTR)
91 		goto notnumber;
92 	for (p = system; *p; p++)
93 		if (isalpha(*p))
94 			goto notnumber;
95 	/*
96 	 * System name is really a phone number...
97 	 * Copy the number then stomp on the original (in case the number
98 	 *	is private, we don't want 'ps' or 'w' to find it).
99 	 */
100 	if (strlen(system) > sizeof (PNbuf) - 1) {
101 		fprintf(stderr, "tip: phone number too long (max = %d bytes)\n",
102 		    sizeof (PNbuf) - 1);
103 		exit(1);
104 	}
105 	strncpy(PNbuf, system, sizeof (PNbuf) - 1);
106 	for (p = system; *p; p++)
107 		*p = '\0';
108 	PN = PNbuf;
109 	sprintf(sbuf, "tip%d", BR);
110 	system = sbuf;
111 
112 notnumber:
113 	signal(SIGINT, cleanup);
114 	signal(SIGQUIT, cleanup);
115 	signal(SIGHUP, cleanup);
116 	signal(SIGTERM, cleanup);
117 
118 	if ((i = hunt(system)) == 0) {
119 		printf("all ports busy\n");
120 		exit(3);
121 	}
122 	if (i == -1) {
123 		printf("link down\n");
124 		delock(uucplock);
125 		exit(3);
126 	}
127 	setbuf(stdout, NULL);
128 	loginit();
129 
130 	/*
131 	 * Now that we have the logfile and the ACU open
132 	 *  return to the real uid and gid.  These things will
133 	 *  be closed on exit.  The saved-setuid uid and gid
134 	 *  allows us to get the original setuid permissions back
135 	 *  for removing the uucp lock.
136 	 */
137 	userperm();
138 
139 	/*
140 	 * Kludge, there's no easy way to get the initialization
141 	 *   in the right order, so force it here.
142 	 * Do the open here, before we change back to real uid.
143 	 * We will check whether the open succeeded later, when
144 	 * (and if) we actually go to use the file.
145 	 */
146 	if ((PH = getenv("PHONES")) == NOSTR) {
147 		myperm();
148 		PH = "/etc/phones";
149 	}
150 	phfd = fopen(PH, "r");
151 
152 	userperm();
153 
154 	vinit();				/* init variables */
155 	setparity("none");			/* set the parity table */
156 	if ((i = speed(number(value(BAUDRATE)))) == NULL) {
157 		printf("tip: bad baud rate %d\n", number(value(BAUDRATE)));
158 		myperm();
159 		delock(uucplock);
160 		exit(3);
161 	}
162 
163 
164 	/*
165 	 * Hardwired connections require the
166 	 *  line speed set before they make any transmissions
167 	 *  (this is particularly true of things like a DF03-AC)
168 	 */
169 	if (HW)
170 		ttysetup(i);
171 	if (p = connect()) {
172 		printf("\07%s\n[EOT]\n", p);
173 		myperm();
174 		delock(uucplock);
175 		exit(1);
176 	}
177 
178 	/*
179 	 * Always setup the tty again here in case hardware flow
180 	 *  control was selected, which can only be set after the
181 	 *  connection is made, or in case this is not a hardwired
182 	 *  modem (rare these days) that likewise can only be setup
183 	 *  after the connection is made.
184 	 */
185 	ttysetup(i);
186 cucommon:
187 	/*
188 	 * From here down the code is shared with
189 	 * the "cu" version of tip.
190 	 */
191 
192 	ioctl(0, TCGETS, (char *)&defarg);
193 	arg = defarg;
194 	/* turn off input processing */
195 	arg.c_lflag &= ~(ICANON|ISIG|ECHO|IEXTEN);
196 	arg.c_cc[VMIN] = 1;
197 	arg.c_cc[VTIME] = 0;
198 	arg.c_iflag &= ~(INPCK|IXON|IXOFF|ICRNL);
199 	arg.c_oflag = 0;		/* turn off all output processing */
200 	/* handle tandem mode in case was set in remote file */
201 	if (boolean(value(TAND)))
202 		tandem("on");
203 	else
204 		tandem("off");
205 	raw();
206 
207 	pipe(fildes); pipe(repdes);
208 	signal(SIGALRM, timeout);
209 
210 	/*
211 	 * Everything's set up now:
212 	 *	connection established (hardwired or dialup)
213 	 *	line conditioned (baud rate, mode, etc.)
214 	 *	internal data structures (variables)
215 	 * so, fork one process for local side and one for remote.
216 	 */
217 	if (CM != NOSTR) {
218 		sleep(2);	/* let line settle */
219 		parwrite(FD, CM, strlen(CM));
220 	}
221 	printf(cumode ? "Connected\r\n" : "\07connected\r\n");
222 	signal(SIGCHLD, deadkid);
223 	if (pid = fork())
224 		tipin();
225 	else
226 		tipout();
227 	/*NOTREACHED*/
228 }
229 
230 void
231 deadkid()
232 {
233 
234 	if (pid >= 0 && waitpid(pid, NULL, WNOHANG) == pid)
235 		abort("Connection Closed");
236 }
237 
238 void
239 cleanup()
240 {
241 
242 	if (uid != getuid()) {
243 		myperm();
244 	}
245 	delock(uucplock);
246 	exit(0);
247 }
248 
249 /*
250  * put the controlling keyboard into raw mode
251  */
252 raw()
253 {
254 
255 	ioctl(0, TCSETSF, (char *)&arg);
256 }
257 
258 
259 /*
260  * return keyboard to normal mode
261  */
262 unraw()
263 {
264 
265 	ioctl(0, TCSETSF, (char *)&defarg);
266 }
267 
268 /*
269  * switch to using invoking user's permissions
270  */
271 userperm()
272 {
273 
274 	setegid(gid);
275 	seteuid(uid);
276 }
277 
278 /*
279  * switch to using my special (setuid) permissions
280  */
281 myperm()
282 {
283 
284 	setegid(egid);
285 	seteuid(euid);
286 }
287 
288 static	sigjmp_buf promptbuf;
289 
290 /*
291  * Print string ``s'', then read a string
292  *  in from the terminal.  Handles signals & allows use of
293  *  normal erase and kill characters.
294  */
295 prompt(s, p, len)
296 	char *s;
297 	register char *p;
298 	size_t len;
299 {
300 	register char *b = p;
301 	register int c;
302 	void (*ointr)(), (*oquit)();
303 
304 	stoprompt = 0;
305 	ointr = signal(SIGINT, intprompt);
306 	oquit = signal(SIGQUIT, SIG_IGN);
307 	unraw();
308 	printf("%s", s);
309 	if (sigsetjmp(promptbuf, 1) == 0)
310 		while (p < b + len - 1 &&
311 		    ((c = getchar()) != EOF) && (c != '\n'))
312 			*p++ = c;
313 	*p = '\0';
314 
315 	raw();
316 	signal(SIGINT, ointr);
317 	signal(SIGQUIT, oquit);
318 	return (stoprompt || p == b);
319 }
320 
321 /*
322  * Interrupt service routine during prompting
323  */
324 void
325 intprompt()
326 {
327 
328 	signal(SIGINT, SIG_IGN);
329 	signal(SIGQUIT, SIG_IGN);
330 	stoprompt = 1;
331 	printf("\r\n");
332 	siglongjmp(promptbuf, 1);
333 }
334 
335 /*
336  * ****TIPIN   TIPIN****
337  */
338 tipin()
339 {
340 	unsigned char gch, c;
341 	int bol = 1;
342 
343 	/*
344 	 * Kinda klugey here...
345 	 *   check for scripting being turned on from the .tiprc file,
346 	 *   but be careful about just using setscript(), as we may
347 	 *   send a SIGEMT before tipout has a chance to set up catching
348 	 *   it; so wait a second, then setscript()
349 	 */
350 	if (boolean(value(SCRIPT))) {
351 		sleep(1);
352 		setscript();
353 	}
354 
355 	for (;;) {
356 		gch = getchar()&0377;
357 		if ((gch == character(value(ESCAPE))) && bol) {
358 			if (!(gch = escape()))
359 				continue;
360 		} else if (!cumode && gch == character(value(RAISECHAR))) {
361 			boolean(value(RAISE)) = !boolean(value(RAISE));
362 			continue;
363 		} else if (gch == '\r') {
364 			bol = 1;
365 			parwrite(FD, &gch, 1);
366 			if (boolean(value(HALFDUPLEX)))
367 				printf("\r\n");
368 			continue;
369 		} else if (!cumode && gch == character(value(FORCE)))
370 			gch = getchar()&0377;
371 		bol = any(gch, value(EOL));
372 		if (boolean(value(RAISE)) && islower(gch))
373 			gch = toupper(gch);
374 		c = gch;
375 		parwrite(FD, &gch, 1);
376 		if (boolean(value(HALFDUPLEX)))
377 			putchar(c);
378 	}
379 }
380 
381 /*
382  * Escape handler --
383  *  called on recognition of ``escapec'' at the beginning of a line
384  */
385 escape()
386 {
387 	register unsigned char gch;
388 	register esctable_t *p;
389 	char c = character(value(ESCAPE));
390 	extern esctable_t etable[];
391 
392 	gch = (getchar()&0377);
393 	for (p = etable; p->e_char; p++)
394 		if (p->e_char == gch) {
395 			if ((p->e_flags&PRIV) && uid)
396 				continue;
397 			printf("%s", ctrl(c));
398 			(*p->e_func)(gch);
399 			return (0);
400 		}
401 	/* ESCAPE ESCAPE forces ESCAPE */
402 	if (c != gch)
403 		parwrite(FD, &c, 1);
404 	return (gch);
405 }
406 
407 speed(n)
408 	int n;
409 {
410 	register int *p;
411 
412 	for (p = bauds; *p != -1;  p++)
413 		if (*p == n)
414 			return (p - bauds);
415 	return (NULL);
416 }
417 
418 any(c, p)
419 	register char c, *p;
420 {
421 	while (p && *p)
422 		if (*p++ == c)
423 			return (1);
424 	return (0);
425 }
426 
427 char *
428 interp(s)
429 	register char *s;
430 {
431 	static char buf[256];
432 	register char *p = buf, c, *q;
433 
434 	while (c = *s++) {
435 		for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
436 			if (*q++ == c) {
437 				*p++ = '\\'; *p++ = *q;
438 				goto next;
439 			}
440 		if (c < 040) {
441 			*p++ = '^'; *p++ = c + 'A'-1;
442 		} else if (c == 0177) {
443 			*p++ = '^'; *p++ = '?';
444 		} else
445 			*p++ = c;
446 	next:
447 		;
448 	}
449 	*p = '\0';
450 	return (buf);
451 }
452 
453 char *
454 ctrl(c)
455 	char c;
456 {
457 	static char s[3];
458 
459 	if (c < 040 || c == 0177) {
460 		s[0] = '^';
461 		s[1] = c == 0177 ? '?' : c+'A'-1;
462 		s[2] = '\0';
463 	} else {
464 		s[0] = c;
465 		s[1] = '\0';
466 	}
467 	return (s);
468 }
469 
470 /*
471  * Help command
472  */
473 help(c)
474 	char c;
475 {
476 	register esctable_t *p;
477 	extern esctable_t etable[];
478 
479 	printf("%c\r\n", c);
480 	for (p = etable; p->e_char; p++) {
481 		if ((p->e_flags&PRIV) && uid)
482 			continue;
483 		printf("%2s", ctrl(character(value(ESCAPE))));
484 		printf("%-2s %c   %s\r\n", ctrl(p->e_char),
485 			p->e_flags&EXP ? '*': ' ', p->e_help);
486 	}
487 }
488 
489 /*
490  * Set up the "remote" tty's state
491  */
492 ttysetup(speed)
493 	int speed;
494 {
495 	struct termios buf;
496 	char *loc;
497 
498 	ioctl(FD, TCGETS, (char *)&buf);
499 	buf.c_cflag &= (CREAD|HUPCL|CLOCAL|CRTSCTS|CRTSXOFF);
500 	buf.c_cflag |= CS8;
501 	cfsetospeed(&buf, speed);
502 	if (boolean(value(HARDWAREFLOW))) {
503 		int i = TIOCM_CAR;
504 
505 		/*
506 		 * Only set hardware flow control if carrier is up,
507 		 * because some devices require both CD and RTS to
508 		 * be up before sending.
509 		 */
510 		ioctl(FD, TIOCMGET, &i);
511 		if (i & TIOCM_CAR)
512 			buf.c_cflag |= (CRTSCTS|CRTSXOFF);
513 	}
514 
515 	/*
516 	 * Careful to only penalize the 8-bit users here on the
517 	 * incoming tty port.  The default 7-bit users will
518 	 * still get the parity bit from the other side's login
519 	 * process (which happens to be the default for sun tip
520 	 * configurations).
521 	 */
522 	loc = setlocale(LC_CTYPE, NULL);
523 	if (noparity && loc != 0 && strcmp(loc, "C") != 0)
524 		buf.c_iflag = 0;
525 	else
526 		buf.c_iflag = ISTRIP;
527 	buf.c_oflag = 0;
528 	buf.c_lflag = 0;
529 	buf.c_cc[VMIN] = 1;
530 	buf.c_cc[VTIME] = 0;
531 	ioctl(FD, TCSETSF, (char *)&buf);
532 }
533 
534 /*
535  * Return "simple" name from a file name,
536  * strip leading directories.
537  */
538 char *
539 sname(s)
540 	register char *s;
541 {
542 	register char *p = s;
543 
544 	while (*s)
545 		if (*s++ == '/')
546 			p = s;
547 	return (p);
548 }
549 
550 static char partab[0400];
551 
552 /*
553  * Do a write to the remote machine with the correct parity.
554  * We are doing 8 bit wide output, so we just generate a character
555  * with the right parity and output it.
556  */
557 parwrite(fd, buf, n)
558 	int fd;
559 	unsigned char *buf;
560 	register int n;
561 {
562 	register int i;
563 	register unsigned char *bp;
564 	extern int errno;
565 
566 	bp = buf;
567 	for (i = 0; i < n; i++) {
568 		*bp = partab[(*bp)&0377];
569 		bp++;
570 	}
571 	if (write(fd, buf, n) < 0) {
572 		if (errno == EIO || errno == ENXIO)
573 			abort("Lost carrier.");
574 		/* this is questionable */
575 		perror("write");
576 	}
577 }
578 
579 /*
580  * Build a parity table with appropriate high-order bit.
581  */
582 setparity(defparity)
583 	char *defparity;
584 {
585 	register int i;
586 	char *parity;
587 	extern char evenpartab[];
588 
589 	if (value(PARITY) == NOSTR)
590 		value(PARITY) = defparity;
591 	parity = value(PARITY);
592 	for (i = 0; i < 0400; i++)
593 		partab[i] = evenpartab[i];
594 	if (equal(parity, "even"))
595 		;
596 	else if (equal(parity, "odd")) {
597 		for (i = 0; i < 0400; i++)
598 			partab[i] ^= 0200;	/* reverse bit 7 */
599 	} else if (equal(parity, "none")) {
600 		/* Do nothing so we can pass thru 8-bit chars */
601 		noparity = 1;
602 		for (i = 0; i < 0400; i++)
603 			partab[i] = i;
604 	} else if (equal(parity, "zero")) {
605 		for (i = 0; i < 0400; i++)
606 			partab[i] &= ~0200;	/* turn off bit 7 */
607 	} else if (equal(parity, "one")) {
608 		for (i = 0; i < 0400; i++)
609 			partab[i] |= 0200;	/* turn on bit 7 */
610 	} else {
611 		fprintf(stderr, "%s: unknown parity value\n", PA);
612 		fflush(stderr);
613 	}
614 }
615