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