xref: /dragonfly/usr.bin/tip/tip.c (revision aeaecd48)
1 /*
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  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  * @(#) Copyright (c) 1983, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)tip.c	8.1 (Berkeley) 6/6/93
35  * $FreeBSD: src/usr.bin/tip/tip/tip.c,v 1.12.2.2 2001/06/02 08:08:24 phk Exp $
36  */
37 
38 /*
39 	Forward declarations
40 */
41 void ttysetup (int speed);
42 
43 /*
44  * tip - UNIX link to other systems
45  *  tip [-v] [-speed] system-name
46  * or
47  *  cu phone-number [-s speed] [-l line] [-a acu]
48  */
49 
50 #include <err.h>
51 #include <errno.h>
52 #include <sys/types.h>
53 #include <libutil.h>
54 #include "tip.h"
55 #include "pathnames.h"
56 
57 static void	intprompt(int);
58 static void	killchild(void);
59 static void	tipdone(int);
60 static char	*sname(char *);
61 char	PNbuf[256];			/* This limits the size of a number */
62 
63 static void usage(void);
64 void setparity(char *);
65 void xpwrite(int, char *, int);
66 char escape(void);
67 void tipin(void);
68 int prompt(char *, char *, size_t);
69 void unraw(void);
70 void shell_uid(void);
71 void daemon_uid(void);
72 void user_uid(void);
73 
74 int
75 main(int argc, char *argv[])
76 {
77 	char *system = NULL;
78 	int i;
79 	char *p;
80 	char sbuf[12];
81 
82 	gid = getgid();
83 	egid = getegid();
84 	uid = getuid();
85 	euid = geteuid();
86 
87 	if (equal(sname(argv[0]), "cu")) {
88 		cumode = 1;
89 		cumain(argc, argv);
90 		goto cucommon;
91 	}
92 
93 	if (argc > 4)
94 		usage();
95 	if (!isatty(0))
96 		errx(1, "must be interactive");
97 
98 	for (; argc > 1; argv++, argc--) {
99 		if (argv[1][0] != '-')
100 			system = argv[1];
101 		else switch (argv[1][1]) {
102 
103 		case 'v':
104 			vflag++;
105 			break;
106 
107 		case '0': case '1': case '2': case '3': case '4':
108 		case '5': case '6': case '7': case '8': case '9':
109 			BR = atoi(&argv[1][1]);
110 			break;
111 
112 		default:
113 			warnx("%s, unknown option", argv[1]);
114 			break;
115 		}
116 	}
117 
118 	if (system == NULL)
119 		goto notnumber;
120 	if (isalpha(*system))
121 		goto notnumber;
122 	/*
123 	 * System name is really a phone number...
124 	 * Copy the number then stomp on the original (in case the number
125 	 *	is private, we don't want 'ps' or 'w' to find it).
126 	 */
127 	if (strlen(system) > sizeof(PNbuf) - 1)
128 		errx(1, "phone number too long (max = %zd bytes)", sizeof PNbuf - 1);
129 	strncpy(PNbuf, system, sizeof(PNbuf) - 1);
130 	for (p = system; *p; p++)
131 		*p = '\0';
132 	PN = PNbuf;
133 	(void)snprintf(sbuf, sizeof(sbuf), "tip%ld", 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 	(void)signal(SIGUSR1, tipdone);
142 
143 	if ((i = hunt(system)) == 0) {
144 		printf("all ports busy\n");
145 		exit(3);
146 	}
147 	if (i == -1) {
148 		printf("link down\n");
149 		(void)uu_unlock(uucplock);
150 		exit(3);
151 	}
152 	setbuf(stdout, NULL);
153 	loginit();
154 
155 	/*
156 	 * Kludge, their's no easy way to get the initialization
157 	 *   in the right order, so force it here
158 	 */
159 	if ((PH = getenv("PHONES")) == NULL)
160 		PH = _PATH_PHONES;
161 	vinit();				/* init variables */
162 	setparity("even");			/* set the parity table */
163 	if ((i = speed(number(value(BAUDRATE)))) == 0) {
164 		printf("tip: bad baud rate %d\n", number(value(BAUDRATE)));
165 		(void)uu_unlock(uucplock);
166 		exit(3);
167 	}
168 
169 	/*
170 	 * Now that we have the logfile and the ACU open
171 	 *  return to the real uid and gid.  These things will
172 	 *  be closed on exit.  Swap real and effective uid's
173 	 *  so we can get the original permissions back
174 	 *  for removing the uucp lock.
175 	 */
176 	user_uid();
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 	tcgetattr (0, &otermios);
200 	ctermios = otermios;
201 	ctermios.c_iflag = (IMAXBEL|IXANY|ISTRIP|IXON|BRKINT);
202 	ctermios.c_lflag = (PENDIN|IEXTEN|ISIG|ECHOCTL|ECHOE|ECHOKE);
203 	ctermios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8);
204 	ctermios.c_cc[VINTR] = 	ctermios.c_cc[VQUIT] = -1;
205 	ctermios.c_cc[VSUSP] = ctermios.c_cc[VDSUSP] = ctermios.c_cc[VDISCARD] =
206 		ctermios.c_cc[VLNEXT] = -1;
207 	raw();
208 
209 	pipe(fildes); pipe(repdes);
210 	(void)signal(SIGALRM, timeoutfunc);
211 
212 	/*
213 	 * Everything's set up now:
214 	 *	connection established (hardwired or dialup)
215 	 *	line conditioned (baud rate, mode, etc.)
216 	 *	internal data structures (variables)
217 	 * so, fork one process for local side and one for remote.
218 	 */
219 	printf(cumode ? "Connected\r\n" : "\07connected\r\n");
220 
221 	if (LI != NULL && tiplink (LI, 0) != 0) {
222 		tipabort ("login failed");
223 	}
224 
225 	if ((pid = fork()))
226 		tipin();
227 	else
228 		tipout();
229 	/*NOTREACHED*/
230 }
231 
232 static void
233 usage(void)
234 {
235 	fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n");
236 	exit(1);
237 }
238 
239 void
240 killchild(void)
241 {
242 	if (pid != 0) {
243 		kill(pid, SIGTERM);
244 		pid = 0;
245 	}
246 }
247 
248 void
249 cleanup(int signo)
250 {
251 
252 	daemon_uid();
253 	(void)uu_unlock(uucplock);
254 	exit(0);
255 }
256 
257 void
258 tipdone(int signo)
259 {
260 	tipabort("Hangup.");
261 }
262 /*
263  * Muck with user ID's.  We are setuid to the owner of the lock
264  * directory when we start.  user_uid() reverses real and effective
265  * ID's after startup, to run with the user's permissions.
266  * daemon_uid() switches back to the privileged uid for unlocking.
267  * Finally, to avoid running a shell with the wrong real uid,
268  * shell_uid() sets real and effective uid's to the user's real ID.
269  */
270 static int uidswapped;
271 
272 void
273 user_uid(void)
274 {
275 	if (uidswapped == 0) {
276 		seteuid(uid);
277 		uidswapped = 1;
278 	}
279 }
280 
281 void
282 daemon_uid(void)
283 {
284 	if (uidswapped) {
285 		seteuid(euid);
286 		uidswapped = 0;
287 	}
288 }
289 
290 void
291 shell_uid(void)
292 {
293 	setegid(gid);
294 	seteuid(uid);
295 }
296 
297 /*
298  * put the controlling keyboard into raw mode
299  */
300 void
301 raw(void)
302 {
303 	tcsetattr (0, TCSANOW, &ctermios);
304 }
305 
306 
307 /*
308  * return keyboard to normal mode
309  */
310 void
311 unraw(void)
312 {
313 	tcsetattr (0, TCSANOW, &otermios);
314 }
315 
316 static	jmp_buf promptbuf;
317 
318 /*
319  * Print string ``s'', then read a string
320  *  in from the terminal.  Handles signals & allows use of
321  *  normal erase and kill characters.
322  */
323 int
324 prompt(char *s, char *p, size_t sz)
325 {
326 	char *b = p;
327 	sig_t oint, oquit;
328 
329 	stoprompt = 0;
330 	oint = signal(SIGINT, intprompt);
331 	oquit = signal(SIGQUIT, SIG_IGN);
332 	unraw();
333 	printf("%s", s);
334 	if (setjmp(promptbuf) == 0)
335 		while ((*p = getchar()) != EOF && *p != '\n' && --sz > 0)
336 			p++;
337 	*p = '\0';
338 
339 	raw();
340 	(void)signal(SIGINT, oint);
341 	(void)signal(SIGQUIT, oquit);
342 	return (stoprompt || p == b);
343 }
344 
345 /*
346  * Interrupt service routine during prompting
347  */
348 void
349 intprompt(int signo)
350 {
351 
352 	(void)signal(SIGINT, SIG_IGN);
353 	stoprompt = 1;
354 	printf("\r\n");
355 	longjmp(promptbuf, 1);
356 }
357 
358 /*
359  * ****TIPIN   TIPIN****
360  */
361 void
362 tipin(void)
363 {
364 	int i;
365 	char gch, bol = 1;
366 
367 	atexit(killchild);
368 
369 	/*
370 	 * Kinda klugey here...
371 	 *   check for scripting being turned on from the .tiprc file,
372 	 *   but be careful about just using setscript(), as we may
373 	 *   send a SIGEMT before tipout has a chance to set up catching
374 	 *   it; so wait a second, then setscript()
375 	 */
376 	if (boolean(value(SCRIPT))) {
377 		sleep(1);
378 		setscript();
379 	}
380 
381 	while (1) {
382 		i = getchar();
383 		if (i == EOF)
384 			break;
385 		gch = i&0177;
386 		if ((gch == character(value(ESCAPE))) && bol) {
387 			if (!(gch = escape()))
388 				continue;
389 		} else if (!cumode && gch == character(value(RAISECHAR))) {
390 			boolean(value(RAISE)) = !boolean(value(RAISE));
391 			continue;
392 		} else if (gch == '\r') {
393 			bol = 1;
394 			xpwrite(FD, &gch, 1);
395 			if (boolean(value(HALFDUPLEX)))
396 				printf("\r\n");
397 			continue;
398 		} else if (!cumode && gch == character(value(FORCE))) {
399 			i = getchar();
400 			if (i == EOF)
401 				break;
402 			gch = i & 0177;
403 		}
404 		bol = any(gch, value(EOL));
405 		if (boolean(value(RAISE)) && islower(gch))
406 			gch = toupper(gch);
407 		xpwrite(FD, &gch, 1);
408 		if (boolean(value(HALFDUPLEX)))
409 			printf("%c", gch);
410 	}
411 }
412 
413 extern esctable_t etable[];
414 
415 /*
416  * Escape handler --
417  *  called on recognition of ``escapec'' at the beginning of a line
418  */
419 char
420 escape(void)
421 {
422 	char gch;
423 	esctable_t *p;
424 	char c = character(value(ESCAPE));
425 	int i;
426 
427 	i = getchar();
428 	if (i == EOF)
429 		return 0;
430 	gch = (i&0177);
431 	for (p = etable; p->e_char; p++)
432 		if (p->e_char == gch) {
433 			if ((p->e_flags&PRIV) && uid)
434 				continue;
435 			printf("%s", ctrl(c));
436 			(*p->e_func)(gch);
437 			return (0);
438 		}
439 	/* ESCAPE ESCAPE forces ESCAPE */
440 	if (c != gch)
441 		xpwrite(FD, &c, 1);
442 	return (gch);
443 }
444 
445 int
446 speed(int n)
447 {
448 	return (n);
449 }
450 
451 int
452 any(char c, char *p)
453 {
454 	while (p && *p)
455 		if (*p++ == c)
456 			return (1);
457 	return (0);
458 }
459 
460 int
461 size(char *s)
462 {
463 	int i = 0;
464 
465 	while (s && *s++)
466 		i++;
467 	return (i);
468 }
469 
470 char *
471 interp(char *s)
472 {
473 	static char buf[256];
474 	char *p = buf, c, *q;
475 
476 	while ((c = *s++)) {
477 		for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
478 			if (*q++ == c) {
479 				*p++ = '\\'; *p++ = *q;
480 				goto next;
481 			}
482 		if (c < 040) {
483 			*p++ = '^'; *p++ = c + 'A'-1;
484 		} else if (c == 0177) {
485 			*p++ = '^'; *p++ = '?';
486 		} else
487 			*p++ = c;
488 	next:
489 		;
490 	}
491 	*p = '\0';
492 	return (buf);
493 }
494 
495 char *
496 ctrl(char c)
497 {
498 	static char s[3];
499 
500 	if (c < 040 || c == 0177) {
501 		s[0] = '^';
502 		s[1] = c == 0177 ? '?' : c+'A'-1;
503 		s[2] = '\0';
504 	} else {
505 		s[0] = c;
506 		s[1] = '\0';
507 	}
508 	return (s);
509 }
510 
511 /*
512  * Help command
513  */
514 void
515 help(int c)
516 {
517 	esctable_t *p;
518 
519 	printf("%c\r\n", c);
520 	for (p = etable; p->e_char; p++) {
521 		if ((p->e_flags&PRIV) && uid)
522 			continue;
523 		printf("%2s", ctrl(character(value(ESCAPE))));
524 		printf("%-2s %c   %s\r\n", ctrl(p->e_char),
525 			(p->e_flags&EXP) ? '*': ' ', p->e_help);
526 	}
527 }
528 
529 /*
530  * Set up the "remote" tty's state
531  */
532 void
533 ttysetup (int speed)
534 {
535 	struct termios termios;
536 	tcgetattr (FD, &termios);
537 	if (boolean(value(TAND)))
538 		termios.c_iflag = IXOFF;
539 	else
540 		termios.c_iflag = 0;
541 	termios.c_lflag = (PENDIN|ECHOKE|ECHOE);
542 	termios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8);
543 	termios.c_ispeed = termios.c_ospeed = speed;
544 	tcsetattr (FD, TCSANOW, &termios);
545 }
546 
547 /*
548  * Return "simple" name from a file name,
549  * strip leading directories.
550  */
551 char *
552 sname(char *s)
553 {
554 	char *p = s;
555 
556 	while (*s)
557 		if (*s++ == '/')
558 			p = s;
559 	return (p);
560 }
561 
562 static char partab[0200];
563 static int bits8;
564 
565 /*
566  * Do a write to the remote machine with the correct parity.
567  * We are doing 8 bit wide output, so we just generate a character
568  * with the right parity and output it.
569  */
570 void
571 xpwrite(int fd, char *buf, int n)
572 {
573 	int i;
574 	char *bp;
575 
576 	bp = buf;
577 	if (bits8 == 0)
578 		for (i = 0; i < n; i++) {
579 			*bp = partab[(*bp) & 0177];
580 			bp++;
581 		}
582 	if (write(fd, buf, n) < 0) {
583 		if (errno == EIO)
584 			tipabort("Lost carrier.");
585 		if (errno == ENODEV)
586 			tipabort("tty not available.");
587 		tipabort("Something wrong...");
588 	}
589 }
590 
591 /*
592  * Build a parity table with appropriate high-order bit.
593  */
594 void
595 setparity(char *defparity)
596 {
597 	int i, flip, clr, set;
598 	char *parity;
599 	extern char evenpartab[];
600 
601 	if (value(PARITY) == NULL)
602 		value(PARITY) = defparity;
603 	parity = value(PARITY);
604 	if (equal(parity, "none")) {
605 		bits8 = 1;
606 		return;
607 	}
608 	bits8 = 0;
609 	flip = 0;
610 	clr = 0377;
611 	set = 0;
612 	if (equal(parity, "odd"))
613 		flip = 0200;			/* reverse bit 7 */
614 	else if (equal(parity, "zero"))
615 		clr = 0177;			/* turn off bit 7 */
616 	else if (equal(parity, "one"))
617 		set = 0200;			/* turn on bit 7 */
618 	else if (!equal(parity, "even")) {
619 		(void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
620 		(void) fflush(stderr);
621 	}
622 	for (i = 0; i < 0200; i++)
623 		partab[i] = (evenpartab[i] ^ flip) | (set & clr);
624 }
625