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