119806Sdist /* 235464Sbostic * Copyright (c) 1983 The Regents of the University of California. 335464Sbostic * All rights reserved. 435464Sbostic * 542770Sbostic * %sccs.include.redist.c% 619806Sdist */ 719806Sdist 813279Ssam #ifndef lint 919806Sdist char copyright[] = 1035464Sbostic "@(#) Copyright (c) 1983 The Regents of the University of California.\n\ 1119806Sdist All rights reserved.\n"; 1235464Sbostic #endif /* not lint */ 135136Ssam 1419806Sdist #ifndef lint 15*46261Storek static char sccsid[] = "@(#)tip.c 5.15 (Berkeley) 02/04/91"; 1635464Sbostic #endif /* not lint */ 1719806Sdist 183696Sroot /* 195136Ssam * tip - UNIX link to other systems 203696Sroot * tip [-v] [-speed] system-name 215136Ssam * or 225136Ssam * cu phone-number [-s speed] [-l line] [-a acu] 233696Sroot */ 243696Sroot #include "tip.h" 2537857Sbostic #include "pathnames.h" 263696Sroot 273696Sroot /* 283696Sroot * Baud rate mapping table 293696Sroot */ 303696Sroot int bauds[] = { 313696Sroot 0, 50, 75, 110, 134, 150, 200, 300, 600, 323696Sroot 1200, 1800, 2400, 4800, 9600, 19200, -1 333696Sroot }; 343696Sroot 354004Ssam int disc = OTTYDISC; /* tip normally runs this way */ 3639254Sbostic void intprompt(); 3739254Sbostic void timeout(); 3839254Sbostic void cleanup(); 395257Sshannon char *sname(); 4025550Sdonn char PNbuf[256]; /* This limits the size of a number */ 413696Sroot 423696Sroot main(argc, argv) 434962Ssam char *argv[]; 443696Sroot { 453696Sroot char *system = NOSTR; 463696Sroot register int i; 475257Sshannon register char *p; 485257Sshannon char sbuf[12]; 493696Sroot 5030458Skarels gid = getgid(); 5130458Skarels egid = getegid(); 5230458Skarels uid = getuid(); 5330458Skarels euid = geteuid(); 545257Sshannon if (equal(sname(argv[0]), "cu")) { 5525905Skarels cumode = 1; 565136Ssam cumain(argc, argv); 575136Ssam goto cucommon; 585136Ssam } 595136Ssam 603696Sroot if (argc > 4) { 613696Sroot fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n"); 623696Sroot exit(1); 633696Sroot } 643696Sroot if (!isatty(0)) { 653696Sroot fprintf(stderr, "tip: must be interactive\n"); 663696Sroot exit(1); 673696Sroot } 685257Sshannon 695257Sshannon for (; argc > 1; argv++, argc--) { 705257Sshannon if (argv[1][0] != '-') 715257Sshannon system = argv[1]; 725257Sshannon else switch (argv[1][1]) { 735257Sshannon 745257Sshannon case 'v': 755257Sshannon vflag++; 765257Sshannon break; 775257Sshannon 785257Sshannon case '0': case '1': case '2': case '3': case '4': 795257Sshannon case '5': case '6': case '7': case '8': case '9': 805257Sshannon BR = atoi(&argv[1][1]); 815257Sshannon break; 825257Sshannon 835257Sshannon default: 845257Sshannon fprintf(stderr, "tip: %s, unknown option\n", argv[1]); 855257Sshannon break; 865257Sshannon } 875257Sshannon } 885257Sshannon 897593Sshannon if (system == NOSTR) 907593Sshannon goto notnumber; 9127004Sdonn if (isalpha(*system)) 9227004Sdonn goto notnumber; 9325550Sdonn /* 9425550Sdonn * System name is really a phone number... 9525550Sdonn * Copy the number then stomp on the original (in case the number 9625550Sdonn * is private, we don't want 'ps' or 'w' to find it). 9725550Sdonn */ 9825550Sdonn if (strlen(system) > sizeof PNbuf - 1) { 9925550Sdonn fprintf(stderr, "tip: phone number too long (max = %d bytes)\n", 10025550Sdonn sizeof PNbuf - 1); 10125550Sdonn exit(1); 10225550Sdonn } 10325550Sdonn strncpy( PNbuf, system, sizeof PNbuf - 1 ); 10425550Sdonn for (p = system; *p; p++) 10525550Sdonn *p = '\0'; 10625550Sdonn PN = PNbuf; 10732513Sbostic (void)sprintf(sbuf, "tip%d", BR); 10832513Sbostic system = sbuf; 1095257Sshannon 1105257Sshannon notnumber: 11140695Sbostic (void)signal(SIGINT, cleanup); 11240695Sbostic (void)signal(SIGQUIT, cleanup); 11340695Sbostic (void)signal(SIGHUP, cleanup); 11440695Sbostic (void)signal(SIGTERM, cleanup); 1153898Ssam 1163696Sroot if ((i = hunt(system)) == 0) { 1173696Sroot printf("all ports busy\n"); 1183696Sroot exit(3); 1193696Sroot } 1203696Sroot if (i == -1) { 1213696Sroot printf("link down\n"); 12235460Sbostic (void)uu_unlock(uucplock); 1233696Sroot exit(3); 1243696Sroot } 1253719Ssam setbuf(stdout, NULL); 1263696Sroot loginit(); 1275257Sshannon 1283696Sroot /* 1293696Sroot * Kludge, their's no easy way to get the initialization 1303696Sroot * in the right order, so force it here 1313696Sroot */ 1323696Sroot if ((PH = getenv("PHONES")) == NOSTR) 13337857Sbostic PH = _PATH_PHONES; 1343696Sroot vinit(); /* init variables */ 13513430Ssam setparity("even"); /* set the parity table */ 1364004Ssam if ((i = speed(number(value(BAUDRATE)))) == NULL) { 1373696Sroot printf("tip: bad baud rate %d\n", number(value(BAUDRATE))); 13835460Sbostic (void)uu_unlock(uucplock); 1393696Sroot exit(3); 1403696Sroot } 1413898Ssam 1424004Ssam /* 14325905Skarels * Now that we have the logfile and the ACU open 14425905Skarels * return to the real uid and gid. These things will 14525905Skarels * be closed on exit. Swap real and effective uid's 14625905Skarels * so we can get the original permissions back 14725905Skarels * for removing the uucp lock. 14825905Skarels */ 14930458Skarels user_uid(); 15025905Skarels 15125905Skarels /* 1524004Ssam * Hardwired connections require the 1534004Ssam * line speed set before they make any transmissions 1544004Ssam * (this is particularly true of things like a DF03-AC) 1554004Ssam */ 1564004Ssam if (HW) 1574004Ssam ttysetup(i); 1583898Ssam if (p = connect()) { 1593898Ssam printf("\07%s\n[EOT]\n", p); 16030458Skarels daemon_uid(); 16135460Sbostic (void)uu_unlock(uucplock); 1623898Ssam exit(1); 1633898Ssam } 1644004Ssam if (!HW) 1654004Ssam ttysetup(i); 1665136Ssam cucommon: 1673696Sroot /* 1685136Ssam * From here down the code is shared with 1695136Ssam * the "cu" version of tip. 1703696Sroot */ 17125905Skarels 1724004Ssam ioctl(0, TIOCGETP, (char *)&defarg); 1734004Ssam ioctl(0, TIOCGETC, (char *)&defchars); 17412478Sroot ioctl(0, TIOCGLTC, (char *)&deflchars); 1754004Ssam ioctl(0, TIOCGETD, (char *)&odisc); 1763696Sroot arg = defarg; 1773696Sroot arg.sg_flags = ANYP | CBREAK; 1783696Sroot tchars = defchars; 1793696Sroot tchars.t_intrc = tchars.t_quitc = -1; 18012478Sroot ltchars = deflchars; 18112478Sroot ltchars.t_suspc = ltchars.t_dsuspc = ltchars.t_flushc 18212478Sroot = ltchars.t_lnextc = -1; 1833696Sroot raw(); 1844004Ssam 1853696Sroot pipe(fildes); pipe(repdes); 18640695Sbostic (void)signal(SIGALRM, timeout); 1873898Ssam 1883898Ssam /* 1893898Ssam * Everything's set up now: 19017236Shelge * connection established (hardwired or dialup) 1913898Ssam * line conditioned (baud rate, mode, etc.) 1923898Ssam * internal data structures (variables) 1933898Ssam * so, fork one process for local side and one for remote. 1943898Ssam */ 1955136Ssam printf(cumode ? "Connected\r\n" : "\07connected\r\n"); 1963696Sroot if (pid = fork()) 1973696Sroot tipin(); 1983696Sroot else 1993696Sroot tipout(); 2003696Sroot /*NOTREACHED*/ 2013696Sroot } 2023696Sroot 20339254Sbostic void 2043696Sroot cleanup() 2053696Sroot { 20613279Ssam 20730458Skarels daemon_uid(); 20835460Sbostic (void)uu_unlock(uucplock); 2093898Ssam if (odisc) 2103898Ssam ioctl(0, TIOCSETD, (char *)&odisc); 2113696Sroot exit(0); 2123696Sroot } 2133696Sroot 2143696Sroot /* 21530458Skarels * Muck with user ID's. We are setuid to the owner of the lock 21630458Skarels * directory when we start. user_uid() reverses real and effective 21730458Skarels * ID's after startup, to run with the user's permissions. 21830458Skarels * daemon_uid() switches back to the privileged uid for unlocking. 21930458Skarels * Finally, to avoid running a shell with the wrong real uid, 22030458Skarels * shell_uid() sets real and effective uid's to the user's real ID. 22130458Skarels */ 22230458Skarels static int uidswapped; 22330458Skarels 22430458Skarels user_uid() 22530458Skarels { 22630458Skarels if (uidswapped == 0) { 22730458Skarels setregid(egid, gid); 22830458Skarels setreuid(euid, uid); 22930458Skarels uidswapped = 1; 23030458Skarels } 23130458Skarels } 23230458Skarels 23330458Skarels daemon_uid() 23430458Skarels { 23530458Skarels 23630458Skarels if (uidswapped) { 23730458Skarels setreuid(uid, euid); 23830458Skarels setregid(gid, egid); 23930458Skarels uidswapped = 0; 24030458Skarels } 24130458Skarels } 24230458Skarels 24330458Skarels shell_uid() 24430458Skarels { 24530458Skarels 24630458Skarels setreuid(uid, uid); 24730458Skarels setregid(gid, gid); 24830458Skarels } 24930458Skarels 25030458Skarels /* 2513696Sroot * put the controlling keyboard into raw mode 2523696Sroot */ 2533696Sroot raw() 2543696Sroot { 25513279Ssam 2563696Sroot ioctl(0, TIOCSETP, &arg); 2573696Sroot ioctl(0, TIOCSETC, &tchars); 25812478Sroot ioctl(0, TIOCSLTC, <chars); 2594004Ssam ioctl(0, TIOCSETD, (char *)&disc); 2603696Sroot } 2613696Sroot 2623696Sroot 2633696Sroot /* 2643696Sroot * return keyboard to normal mode 2653696Sroot */ 2663696Sroot unraw() 2673696Sroot { 26813279Ssam 2694004Ssam ioctl(0, TIOCSETD, (char *)&odisc); 2704004Ssam ioctl(0, TIOCSETP, (char *)&defarg); 2714004Ssam ioctl(0, TIOCSETC, (char *)&defchars); 27212478Sroot ioctl(0, TIOCSLTC, (char *)&deflchars); 2733696Sroot } 2743696Sroot 27513279Ssam static jmp_buf promptbuf; 27613279Ssam 2773696Sroot /* 2783696Sroot * Print string ``s'', then read a string 2793696Sroot * in from the terminal. Handles signals & allows use of 2803696Sroot * normal erase and kill characters. 2813696Sroot */ 2823696Sroot prompt(s, p) 2833696Sroot char *s; 2843696Sroot register char *p; 2853696Sroot { 2863696Sroot register char *b = p; 28739254Sbostic sig_t oint, oquit; 2883696Sroot 2893696Sroot stoprompt = 0; 29013279Ssam oint = signal(SIGINT, intprompt); 29140695Sbostic oquit = signal(SIGQUIT, SIG_IGN); 2923696Sroot unraw(); 2933696Sroot printf("%s", s); 29413279Ssam if (setjmp(promptbuf) == 0) 29513279Ssam while ((*p = getchar()) != EOF && *p != '\n') 29613279Ssam p++; 2973696Sroot *p = '\0'; 29813279Ssam 2993696Sroot raw(); 30040695Sbostic (void)signal(SIGINT, oint); 30140695Sbostic (void)signal(SIGQUIT, oquit); 30213279Ssam return (stoprompt || p == b); 3033696Sroot } 3043696Sroot 3053696Sroot /* 3063696Sroot * Interrupt service routine during prompting 3073696Sroot */ 30839254Sbostic void 3093696Sroot intprompt() 3103696Sroot { 31113279Ssam 31240695Sbostic (void)signal(SIGINT, SIG_IGN); 3133696Sroot stoprompt = 1; 3143696Sroot printf("\r\n"); 31513279Ssam longjmp(promptbuf, 1); 3163696Sroot } 3173696Sroot 3183696Sroot /* 3193696Sroot * ****TIPIN TIPIN**** 3203696Sroot */ 3213696Sroot tipin() 3223696Sroot { 3233696Sroot char gch, bol = 1; 3243696Sroot 3253796Ssam /* 3263796Ssam * Kinda klugey here... 3273796Ssam * check for scripting being turned on from the .tiprc file, 3283796Ssam * but be careful about just using setscript(), as we may 3293796Ssam * send a SIGEMT before tipout has a chance to set up catching 3303796Ssam * it; so wait a second, then setscript() 3313796Ssam */ 3323796Ssam if (boolean(value(SCRIPT))) { 3333796Ssam sleep(1); 3343796Ssam setscript(); 3353796Ssam } 3363796Ssam 3373696Sroot while (1) { 3383696Sroot gch = getchar()&0177; 3393696Sroot if ((gch == character(value(ESCAPE))) && bol) { 3403696Sroot if (!(gch = escape())) 3413696Sroot continue; 3425136Ssam } else if (!cumode && gch == character(value(RAISECHAR))) { 3433696Sroot boolean(value(RAISE)) = !boolean(value(RAISE)); 3443696Sroot continue; 3453696Sroot } else if (gch == '\r') { 3463696Sroot bol = 1; 34713139Sralph pwrite(FD, &gch, 1); 34813139Sralph if (boolean(value(HALFDUPLEX))) 34913139Sralph printf("\r\n"); 3503696Sroot continue; 3515136Ssam } else if (!cumode && gch == character(value(FORCE))) 3523696Sroot gch = getchar()&0177; 3533696Sroot bol = any(gch, value(EOL)); 3543696Sroot if (boolean(value(RAISE)) && islower(gch)) 35513139Sralph gch = toupper(gch); 35613139Sralph pwrite(FD, &gch, 1); 35713139Sralph if (boolean(value(HALFDUPLEX))) 35813139Sralph printf("%c", gch); 3593696Sroot } 3603696Sroot } 3613696Sroot 3623696Sroot /* 3633696Sroot * Escape handler -- 3643696Sroot * called on recognition of ``escapec'' at the beginning of a line 3653696Sroot */ 3663696Sroot escape() 3673696Sroot { 3683696Sroot register char gch; 3693696Sroot register esctable_t *p; 3703696Sroot char c = character(value(ESCAPE)); 3713696Sroot extern esctable_t etable[]; 3723696Sroot 3733696Sroot gch = (getchar()&0177); 3743696Sroot for (p = etable; p->e_char; p++) 3753696Sroot if (p->e_char == gch) { 37630458Skarels if ((p->e_flags&PRIV) && uid) 3773696Sroot continue; 3783696Sroot printf("%s", ctrl(c)); 3793696Sroot (*p->e_func)(gch); 38013279Ssam return (0); 3813696Sroot } 3824146Ssam /* ESCAPE ESCAPE forces ESCAPE */ 3834146Ssam if (c != gch) 38413139Sralph pwrite(FD, &c, 1); 38513279Ssam return (gch); 3863696Sroot } 3873696Sroot 3883696Sroot speed(n) 38913279Ssam int n; 3903696Sroot { 3913696Sroot register int *p; 3923696Sroot 3933696Sroot for (p = bauds; *p != -1; p++) 3943696Sroot if (*p == n) 39513279Ssam return (p - bauds); 39613279Ssam return (NULL); 3973696Sroot } 3983696Sroot 3993696Sroot any(c, p) 4003696Sroot register char c, *p; 4013696Sroot { 40213279Ssam while (p && *p) 4033696Sroot if (*p++ == c) 40413279Ssam return (1); 40513279Ssam return (0); 4063696Sroot } 4073696Sroot 4083696Sroot size(s) 4093696Sroot register char *s; 4103696Sroot { 41113279Ssam register int i = 0; 4123696Sroot 41313279Ssam while (s && *s++) 41413279Ssam i++; 41513279Ssam return (i); 4163696Sroot } 4173696Sroot 4183696Sroot char * 4193696Sroot interp(s) 4203696Sroot register char *s; 4213696Sroot { 4223696Sroot static char buf[256]; 4233696Sroot register char *p = buf, c, *q; 4243696Sroot 4253696Sroot while (c = *s++) { 4263696Sroot for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++) 4273696Sroot if (*q++ == c) { 4283696Sroot *p++ = '\\'; *p++ = *q; 4293696Sroot goto next; 4303696Sroot } 4313696Sroot if (c < 040) { 4323696Sroot *p++ = '^'; *p++ = c + 'A'-1; 4333696Sroot } else if (c == 0177) { 4343696Sroot *p++ = '^'; *p++ = '?'; 4353696Sroot } else 4363696Sroot *p++ = c; 4373696Sroot next: 4383696Sroot ; 4393696Sroot } 4403696Sroot *p = '\0'; 44113279Ssam return (buf); 4423696Sroot } 4433696Sroot 4443696Sroot char * 4453696Sroot ctrl(c) 4463696Sroot char c; 4473696Sroot { 4483696Sroot static char s[3]; 4493696Sroot 4503696Sroot if (c < 040 || c == 0177) { 4513696Sroot s[0] = '^'; 4523696Sroot s[1] = c == 0177 ? '?' : c+'A'-1; 4533696Sroot s[2] = '\0'; 4543696Sroot } else { 4553696Sroot s[0] = c; 4563696Sroot s[1] = '\0'; 4573696Sroot } 45813279Ssam return (s); 4593696Sroot } 4603696Sroot 4613696Sroot /* 4623696Sroot * Help command 4633696Sroot */ 4643696Sroot help(c) 4653696Sroot char c; 4663696Sroot { 4673696Sroot register esctable_t *p; 4683696Sroot extern esctable_t etable[]; 4693696Sroot 4703696Sroot printf("%c\r\n", c); 4713696Sroot for (p = etable; p->e_char; p++) { 47230458Skarels if ((p->e_flags&PRIV) && uid) 4733696Sroot continue; 4743696Sroot printf("%2s", ctrl(character(value(ESCAPE)))); 4753696Sroot printf("%-2s %c %s\r\n", ctrl(p->e_char), 4763696Sroot p->e_flags&EXP ? '*': ' ', p->e_help); 4773696Sroot } 4783696Sroot } 4794004Ssam 4804004Ssam /* 4814004Ssam * Set up the "remote" tty's state 4824004Ssam */ 4834004Ssam ttysetup(speed) 48413279Ssam int speed; 4854004Ssam { 4864004Ssam unsigned bits = LDECCTQ; 4874004Ssam 4884004Ssam arg.sg_ispeed = arg.sg_ospeed = speed; 48913279Ssam arg.sg_flags = RAW; 49013139Sralph if (boolean(value(TAND))) 49113279Ssam arg.sg_flags |= TANDEM; 4924004Ssam ioctl(FD, TIOCSETP, (char *)&arg); 4934004Ssam ioctl(FD, TIOCLBIS, (char *)&bits); 4944004Ssam } 4955257Sshannon 4965257Sshannon /* 4975257Sshannon * Return "simple" name from a file name, 4985257Sshannon * strip leading directories. 4995257Sshannon */ 5005257Sshannon char * 5015257Sshannon sname(s) 5025257Sshannon register char *s; 5035257Sshannon { 5045257Sshannon register char *p = s; 5055257Sshannon 5065257Sshannon while (*s) 5075257Sshannon if (*s++ == '/') 5085257Sshannon p = s; 5095257Sshannon return (p); 5105257Sshannon } 51113139Sralph 51213139Sralph static char partab[0200]; 51340240Skarels static int bits8; 51413139Sralph 51513139Sralph /* 51613279Ssam * Do a write to the remote machine with the correct parity. 51713279Ssam * We are doing 8 bit wide output, so we just generate a character 51813139Sralph * with the right parity and output it. 51913139Sralph */ 52013139Sralph pwrite(fd, buf, n) 52113139Sralph int fd; 52213139Sralph char *buf; 52313139Sralph register int n; 52413139Sralph { 52513139Sralph register int i; 52613279Ssam register char *bp; 52715188Ssam extern int errno; 52813139Sralph 52913279Ssam bp = buf; 53040240Skarels if (bits8 == 0) 53140240Skarels for (i = 0; i < n; i++) { 53240240Skarels *bp = partab[(*bp) & 0177]; 53340240Skarels bp++; 53440240Skarels } 53515188Ssam if (write(fd, buf, n) < 0) { 53615188Ssam if (errno == EIO) 537*46261Storek tipabort("Lost carrier."); 53815188Ssam /* this is questionable */ 53915188Ssam perror("write"); 54015188Ssam } 54113139Sralph } 54213139Sralph 54313139Sralph /* 54413279Ssam * Build a parity table with appropriate high-order bit. 54513139Sralph */ 54613430Ssam setparity(defparity) 54713430Ssam char *defparity; 54813139Sralph { 549*46261Storek register int i, flip, clr, set; 55013139Sralph char *parity; 55113279Ssam extern char evenpartab[]; 55213139Sralph 55313139Sralph if (value(PARITY) == NOSTR) 55413430Ssam value(PARITY) = defparity; 55513139Sralph parity = value(PARITY); 55640240Skarels if (equal(parity, "none")) { 55740240Skarels bits8 = 1; 55840240Skarels return; 55913139Sralph } 560*46261Storek bits8 = 0; 561*46261Storek flip = 0; 562*46261Storek clr = 0377; 563*46261Storek set = 0; 564*46261Storek if (equal(parity, "odd")) 565*46261Storek flip = 0200; /* reverse bit 7 */ 566*46261Storek else if (equal(parity, "zero")) 567*46261Storek clr = 0177; /* turn off bit 7 */ 568*46261Storek else if (equal(parity, "one")) 569*46261Storek set = 0200; /* turn on bit 7 */ 570*46261Storek else if (!equal(parity, "even")) { 571*46261Storek (void) fprintf(stderr, "%s: unknown parity value\r\n", parity); 572*46261Storek (void) fflush(stderr); 57313139Sralph } 574*46261Storek for (i = 0; i < 0200; i++) 575*46261Storek partab[i] = evenpartab[i] ^ flip | set & clr; 57613139Sralph } 577