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