tip.c revision 1.23 1 /* $NetBSD: tip.c,v 1.23 2000/10/11 14:46:19 is Exp $ */
2
3 /*
4 * Copyright (c) 1983, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)tip.c 8.1 (Berkeley) 6/6/93";
45 #endif
46 __RCSID("$NetBSD: tip.c,v 1.23 2000/10/11 14:46:19 is Exp $");
47 #endif /* not lint */
48
49 /*
50 * tip - UNIX link to other systems
51 * tip [-v] [-speed] system-name
52 * or
53 * cu phone-number [-s speed] [-l line] [-a acu]
54 */
55 #include "tip.h"
56 #include "pathnames.h"
57
58 /*
59 * Baud rate mapping table
60 */
61 int rates[] = {
62 0, 50, 75, 110, 134, 150, 200, 300, 600,
63 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, -1
64 };
65
66 int escape __P((void));
67 int main __P((int, char **));
68 void intprompt __P((int));
69 void tipin __P((void));
70
71 char PNbuf[256]; /* This limits the size of a number */
72
73 int
74 main(argc, argv)
75 int argc;
76 char *argv[];
77 {
78 char *system = NULL;
79 int i;
80 char *p;
81 char sbuf[12];
82 int fcarg;
83
84 gid = getgid();
85 egid = getegid();
86 uid = getuid();
87 euid = geteuid();
88 if (equal(basename(argv[0]), "cu")) {
89 cumode = 1;
90 cumain(argc, argv);
91 goto cucommon;
92 }
93
94 if (argc > 4) {
95 fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n");
96 exit(1);
97 }
98 if (!isatty(0)) {
99 fprintf(stderr, "tip: must be interactive\n");
100 exit(1);
101 }
102
103 for (; argc > 1; argv++, argc--) {
104 if (argv[1][0] != '-')
105 system = argv[1];
106 else switch (argv[1][1]) {
107
108 case 'v':
109 vflag++;
110 break;
111
112 case '0': case '1': case '2': case '3': case '4':
113 case '5': case '6': case '7': case '8': case '9':
114 BR = atoi(&argv[1][1]);
115 break;
116
117 default:
118 fprintf(stderr, "tip: %s, unknown option\n", argv[1]);
119 break;
120 }
121 }
122
123 if (system == NULL)
124 goto notnumber;
125 if (isalpha((unsigned char)*system))
126 goto notnumber;
127 /*
128 * System name is really a phone number...
129 * Copy the number then stomp on the original (in case the number
130 * is private, we don't want 'ps' or 'w' to find it).
131 */
132 if (strlen(system) > sizeof PNbuf - 1) {
133 fprintf(stderr, "tip: phone number too long (max = %d bytes)\n",
134 (int)sizeof(PNbuf) - 1);
135 exit(1);
136 }
137 strncpy(PNbuf, system, sizeof PNbuf - 1);
138 PNbuf[sizeof PNbuf - 1] = '\0';
139 for (p = system; *p; p++)
140 *p = '\0';
141 PN = PNbuf;
142 (void)snprintf(sbuf, sizeof sbuf, "tip%d", (int)BR);
143 system = sbuf;
144
145 notnumber:
146 (void)signal(SIGINT, cleanup);
147 (void)signal(SIGQUIT, cleanup);
148 (void)signal(SIGHUP, cleanup);
149 (void)signal(SIGTERM, cleanup);
150
151 if ((i = hunt(system)) == 0) {
152 printf("all ports busy\n");
153 exit(3);
154 }
155 if (i == -1) {
156 printf("link down\n");
157 (void)uu_unlock(uucplock);
158 exit(3);
159 }
160 setbuf(stdout, NULL);
161 loginit();
162
163 /*
164 * Now that we have the logfile and the ACU open
165 * return to the real uid and gid. These things will
166 * be closed on exit. Swap real and effective uid's
167 * so we can get the original permissions back
168 * for removing the uucp lock.
169 */
170 user_uid();
171
172 /*
173 * Kludge, their's no easy way to get the initialization
174 * in the right order, so force it here
175 */
176 if ((PH = getenv("PHONES")) == NULL)
177 PH = _PATH_PHONES;
178 vinit(); /* init variables */
179 setparity("even"); /* set the parity table */
180 if ((i = speed(number(value(BAUDRATE)))) == 0) {
181 printf("tip: bad baud rate %d\n", (int)number(value(BAUDRATE)));
182 daemon_uid();
183 (void)uu_unlock(uucplock);
184 exit(3);
185 }
186
187 /*
188 * Hardwired connections require the
189 * line speed set before they make any transmissions
190 * (this is particularly true of things like a DF03-AC)
191 */
192 if (HW)
193 ttysetup(i);
194 if ((p = connect()) != NULL) {
195 printf("\07%s\n[EOT]\n", p);
196 daemon_uid();
197 (void)uu_unlock(uucplock);
198 exit(1);
199 }
200 if (!HW)
201 ttysetup(i);
202
203 /*
204 * Direct connections with no carrier require using O_NONBLOCK on
205 * open, but we don't want to keep O_NONBLOCK after open because it
206 * will cause busy waits.
207 */
208 if (DC &&
209 ((fcarg = fcntl(FD, F_GETFL, 0)) < 0 ||
210 fcntl(FD, F_SETFL, fcarg & ~O_NONBLOCK) < 0)) {
211 printf("tip: can't clear O_NONBLOCK: %s", strerror (errno));
212 daemon_uid();
213 (void)uu_unlock(uucplock);
214 exit(1);
215 }
216
217 cucommon:
218 /*
219 * From here down the code is shared with
220 * the "cu" version of tip.
221 */
222
223 tcgetattr(0, &defterm);
224 term = defterm;
225 term.c_lflag &= ~(ICANON|IEXTEN|ECHO);
226 term.c_iflag &= ~(INPCK|ICRNL);
227 term.c_oflag &= ~OPOST;
228 term.c_cc[VMIN] = 1;
229 term.c_cc[VTIME] = 0;
230 defchars = term;
231 term.c_cc[VINTR] = term.c_cc[VQUIT] = term.c_cc[VSUSP] =
232 term.c_cc[VDSUSP] = term.c_cc[VDISCARD] =
233 term.c_cc[VLNEXT] = _POSIX_VDISABLE;
234 raw();
235
236 pipe(fildes); pipe(repdes);
237 (void)signal(SIGALRM, alrmtimeout);
238
239 /*
240 * Everything's set up now:
241 * connection established (hardwired or dialup)
242 * line conditioned (baud rate, mode, etc.)
243 * internal data structures (variables)
244 * so, fork one process for local side and one for remote.
245 */
246 printf("%s", cumode ? "Connected\r\n" : "\07connected\r\n");
247 switch (pid = fork()) {
248 default:
249 tipin();
250 break;
251 case 0:
252 tipout();
253 break;
254 case -1:
255 err(1, "can't fork");
256 }
257 /*NOTREACHED*/
258 exit(0); /* XXX: pacify gcc */
259 }
260
261 void
262 cleanup(dummy)
263 int dummy;
264 {
265
266 daemon_uid();
267 (void)uu_unlock(uucplock);
268 if (odisc)
269 ioctl(0, TIOCSETD, (char *)&odisc);
270 exit(0);
271 }
272
273 /*
274 * Muck with user ID's. We are setuid to the owner of the lock
275 * directory when we start. user_uid() reverses real and effective
276 * ID's after startup, to run with the user's permissions.
277 * daemon_uid() switches back to the privileged uid for unlocking.
278 * Finally, to avoid running a shell with the wrong real uid,
279 * shell_uid() sets real and effective uid's to the user's real ID.
280 */
281 static int uidswapped;
282
283 void
284 user_uid()
285 {
286
287 if (uidswapped == 0) {
288 seteuid(uid);
289 uidswapped = 1;
290 }
291 }
292
293 void
294 daemon_uid()
295 {
296
297 if (uidswapped) {
298 seteuid(euid);
299 uidswapped = 0;
300 }
301 }
302
303 void
304 shell_uid()
305 {
306
307 seteuid(uid);
308 }
309
310 /*
311 * put the controlling keyboard into raw mode
312 */
313 void
314 raw()
315 {
316
317 tcsetattr(0, TCSADRAIN, &term);
318 }
319
320
321 /*
322 * return keyboard to normal mode
323 */
324 void
325 unraw()
326 {
327
328 tcsetattr(0, TCSADRAIN, &defterm);
329 }
330
331 static jmp_buf promptbuf;
332
333 /*
334 * Print string ``s'', then read a string
335 * in from the terminal. Handles signals & allows use of
336 * normal erase and kill characters.
337 */
338 int
339 prompt(s, p, l)
340 char *s;
341 char *p;
342 size_t l;
343 {
344 int c;
345 char *b = p;
346 sig_t oint, oquit;
347
348 #if __GNUC__ /* XXX: pacify gcc */
349 (void)&p;
350 #endif
351
352 stoprompt = 0;
353 oint = signal(SIGINT, intprompt);
354 oquit = signal(SIGQUIT, SIG_IGN);
355 unraw();
356 printf("%s", s);
357 if (setjmp(promptbuf) == 0)
358 while ((c = getchar()) != -1 && (*p = c) != '\n' &&
359 b + l > p)
360 p++;
361 *p = '\0';
362
363 raw();
364 (void)signal(SIGINT, oint);
365 (void)signal(SIGQUIT, oquit);
366 return (stoprompt || p == b);
367 }
368
369 /*
370 * Interrupt service routine during prompting
371 */
372 void
373 intprompt(dummy)
374 int dummy;
375 {
376
377 (void)signal(SIGINT, SIG_IGN);
378 stoprompt = 1;
379 printf("\r\n");
380 longjmp(promptbuf, 1);
381 }
382
383 /*
384 * ****TIPIN TIPIN****
385 */
386 void
387 tipin()
388 {
389 char gch, bol = 1;
390
391 /*
392 * Kinda klugey here...
393 * check for scripting being turned on from the .tiprc file,
394 * but be careful about just using setscript(), as we may
395 * send a SIGEMT before tipout has a chance to set up catching
396 * it; so wait a second, then setscript()
397 */
398 if (boolean(value(SCRIPT))) {
399 sleep(1);
400 setscript();
401 }
402
403 while (1) {
404 gch = getchar()&STRIP_PAR;
405 if ((gch == character(value(ESCAPE))) && bol) {
406 if (!(gch = escape()))
407 continue;
408 } else if (!cumode &&
409 gch && gch == character(value(RAISECHAR))) {
410 setboolean(value(RAISE), !boolean(value(RAISE)));
411 continue;
412 } else if (gch == '\r') {
413 bol = 1;
414 xpwrite(FD, &gch, 1);
415 if (boolean(value(HALFDUPLEX)))
416 printf("\r\n");
417 continue;
418 } else if (!cumode && gch && gch == character(value(FORCE)))
419 gch = getchar()&STRIP_PAR;
420 bol = any(gch, value(EOL));
421 if (boolean(value(RAISE)) && islower((unsigned char)gch))
422 gch = toupper(gch);
423 xpwrite(FD, &gch, 1);
424 if (boolean(value(HALFDUPLEX)))
425 printf("%c", gch);
426 }
427 }
428
429 /*
430 * Escape handler --
431 * called on recognition of ``escapec'' at the beginning of a line
432 */
433 int
434 escape()
435 {
436 char gch;
437 esctable_t *p;
438 char c = character(value(ESCAPE));
439
440 gch = (getchar()&STRIP_PAR);
441 for (p = etable; p->e_char; p++)
442 if (p->e_char == gch) {
443 if ((p->e_flags&PRIV) && uid)
444 continue;
445 printf("%s", ctrl(c));
446 (*p->e_func)(gch);
447 return (0);
448 }
449 /* ESCAPE ESCAPE forces ESCAPE */
450 if (c != gch)
451 xpwrite(FD, &c, 1);
452 return (gch);
453 }
454
455 int
456 speed(n)
457 int n;
458 {
459 int *p;
460
461 for (p = rates; *p != -1; p++)
462 if (*p == n)
463 return n;
464 return 0;
465 }
466
467 int
468 any(c, p)
469 char c, *p;
470 {
471
472 while (p && *p)
473 if (*p++ == c)
474 return (1);
475 return (0);
476 }
477
478 char *
479 interp(s)
480 char *s;
481 {
482 static char buf[256];
483 char *p = buf, c, *q;
484
485 while ((c = *s++) != 0 && buf + sizeof buf - p > 2) {
486 for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
487 if (*q++ == c) {
488 *p++ = '\\'; *p++ = *q;
489 goto next;
490 }
491 if (c < 040) {
492 *p++ = '^'; *p++ = c + 'A'-1;
493 } else if (c == 0177) {
494 *p++ = '^'; *p++ = '?';
495 } else
496 *p++ = c;
497 next:
498 ;
499 }
500 *p = '\0';
501 return (buf);
502 }
503
504 char *
505 ctrl(c)
506 char c;
507 {
508 static char s[3];
509
510 if (c < 040 || c == 0177) {
511 s[0] = '^';
512 s[1] = c == 0177 ? '?' : c+'A'-1;
513 s[2] = '\0';
514 } else {
515 s[0] = c;
516 s[1] = '\0';
517 }
518 return (s);
519 }
520
521 /*
522 * Help command
523 */
524 void
525 help(c)
526 char c;
527 {
528 esctable_t *p;
529
530 printf("%c\r\n", c);
531 for (p = etable; p->e_char; p++) {
532 if ((p->e_flags&PRIV) && uid)
533 continue;
534 printf("%2s", ctrl(character(value(ESCAPE))));
535 printf("%-2s %c %s\r\n", ctrl(p->e_char),
536 p->e_flags&EXP ? '*': ' ', p->e_help);
537 }
538 }
539
540 /*
541 * Set up the "remote" tty's state
542 */
543 void
544 ttysetup(speed)
545 int speed;
546 {
547 struct termios cntrl;
548
549 tcgetattr(FD, &cntrl);
550 cfsetospeed(&cntrl, speed);
551 cfsetispeed(&cntrl, speed);
552 cntrl.c_cflag &= ~(CSIZE|PARENB);
553 cntrl.c_cflag |= CS8;
554 if (DC)
555 cntrl.c_cflag |= CLOCAL;
556 cntrl.c_iflag &= ~(ISTRIP|ICRNL);
557 cntrl.c_oflag &= ~OPOST;
558 cntrl.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
559 cntrl.c_cc[VMIN] = 1;
560 cntrl.c_cc[VTIME] = 0;
561 if (boolean(value(TAND)))
562 cntrl.c_iflag |= IXOFF;
563 tcsetattr(FD, TCSAFLUSH, &cntrl);
564 }
565
566 static char partab[0200];
567
568 /*
569 * Do a write to the remote machine with the correct parity.
570 * We are doing 8 bit wide output, so we just generate a character
571 * with the right parity and output it.
572 */
573 void
574 xpwrite(fd, buf, n)
575 int fd;
576 char *buf;
577 int n;
578 {
579 int i;
580 char *bp;
581
582 bp = buf;
583 if (bits8 == 0)
584 for (i = 0; i < n; i++) {
585 *bp = partab[(*bp) & 0177];
586 bp++;
587 }
588 if (write(fd, buf, n) < 0) {
589 if (errno == EIO)
590 tipabort("Lost carrier.");
591 /* this is questionable */
592 perror("write");
593 }
594 }
595
596 /*
597 * Build a parity table with appropriate high-order bit.
598 */
599 void
600 setparity(defparity)
601 char *defparity;
602 {
603 int i, flip, clr, set;
604 char *parity;
605
606 if (value(PARITY) == NULL || (value(PARITY))[0] == '\0')
607 value(PARITY) = defparity;
608 parity = value(PARITY);
609 if (equal(parity, "none")) {
610 bits8 = 1;
611 return;
612 }
613 bits8 = 0;
614 flip = 0;
615 clr = 0377;
616 set = 0;
617 if (equal(parity, "odd"))
618 flip = 0200; /* reverse bit 7 */
619 else if (equal(parity, "zero"))
620 clr = 0177; /* turn off bit 7 */
621 else if (equal(parity, "one"))
622 set = 0200; /* turn on bit 7 */
623 else if (!equal(parity, "even")) {
624 (void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
625 (void) fflush(stderr);
626 }
627 for (i = 0; i < 0200; i++)
628 partab[i] = ((evenpartab[i] ^ flip) | set) & clr;
629 }
630