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