]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.bin/tip/tip/tip.c
MFC r357269:
[FreeBSD/stable/10.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 /*
329  * give up exclusive tty access
330  */
331 void
332 unexcl()
333 {
334         ioctl(FD, TIOCNXCL, 0);
335 }
336
337 static  jmp_buf promptbuf;
338
339 /*
340  * Print string ``s'', then read a string
341  *  in from the terminal.  Handles signals & allows use of
342  *  normal erase and kill characters.
343  */
344 int
345 prompt(char *s, char *p, size_t sz)
346 {
347         int c;
348         char *b = p;
349         sig_t oint, oquit;
350
351         stoprompt = 0;
352         oint = signal(SIGINT, intprompt);
353         oquit = signal(SIGQUIT, SIG_IGN);
354         unraw();
355         printf("%s", s);
356         if (setjmp(promptbuf) == 0)
357                 while ((c = getchar()) != EOF && (*p = c) != '\n' && --sz > 0)
358                         p++;
359         *p = '\0';
360
361         raw();
362         (void)signal(SIGINT, oint);
363         (void)signal(SIGQUIT, oquit);
364         return (stoprompt || p == b);
365 }
366
367 /*
368  * Interrupt service routine during prompting
369  */
370 /*ARGSUSED*/
371 static void
372 intprompt(int signo)
373 {
374         (void)signal(SIGINT, SIG_IGN);
375         stoprompt = 1;
376         printf("\r\n");
377         longjmp(promptbuf, 1);
378 }
379
380 /*
381  * ****TIPIN   TIPIN****
382  */
383 static void
384 tipin(void)
385 {
386         int bol = 1;
387         int gch;
388         char ch;
389
390         /*
391          * Kinda klugey here...
392          *   check for scripting being turned on from the .tiprc file,
393          *   but be careful about just using setscript(), as we may
394          *   send a SIGEMT before tipout has a chance to set up catching
395          *   it; so wait a second, then setscript()
396          */
397         if (boolean(value(SCRIPT))) {
398                 sleep(1);
399                 setscript();
400         }
401
402         while (1) {
403                 gch = getchar()&STRIP_PAR;
404                 /* XXX does not check for EOF */
405                 if ((gch == character(value(ESCAPE))) && bol) {
406                         if (!noesc) {
407                                 if (!(gch = escape()))
408                                         continue;
409                         }
410                 } else if (!cumode && gch == character(value(RAISECHAR))) {
411                         setboolean(value(RAISE), !boolean(value(RAISE)));
412                         continue;
413                 } else if (gch == '\r') {
414                         bol = 1;
415                         ch = gch;
416                         parwrite(FD, &ch, 1);
417                         if (boolean(value(HALFDUPLEX)))
418                                 printf("\r\n");
419                         continue;
420                 } else if (!cumode && gch == character(value(FORCE))) {
421                         gch = getchar()&STRIP_PAR;
422                 }
423                 bol = any(gch, value(EOL));
424                 if (boolean(value(RAISE)) && islower(gch))
425                         gch = toupper(gch);
426                 ch = gch;
427                 parwrite(FD, &ch, 1);
428                 if (boolean(value(HALFDUPLEX)))
429                         printf("%c", ch);
430         }
431 }
432
433 extern esctable_t etable[];
434
435 /*
436  * Escape handler --
437  *  called on recognition of ``escapec'' at the beginning of a line
438  */
439 static int
440 escape(void)
441 {
442         int gch;
443         esctable_t *p;
444         char c = character(value(ESCAPE));
445
446         gch = (getchar()&STRIP_PAR);
447         /* XXX does not check for EOF */
448         for (p = etable; p->e_char; p++)
449                 if (p->e_char == gch) {
450                         if ((p->e_flags&PRIV) && uid)
451                                 continue;
452                         printf("%s", ctrl(c));
453                         (*p->e_func)(gch);
454                         return (0);
455                 }
456         /* ESCAPE ESCAPE forces ESCAPE */
457         if (c != gch)
458                 parwrite(FD, &c, 1);
459         return (gch);
460 }
461
462 int
463 any(int cc, char *p)
464 {
465         char c = cc;
466         while (p && *p)
467                 if (*p++ == c)
468                         return (1);
469         return (0);
470 }
471
472 size_t
473 size(char *s)
474 {
475         size_t i = 0;
476
477         while (s && *s++)
478                 i++;
479         return (i);
480 }
481
482 char *
483 interp(char *s)
484 {
485         static char buf[256];
486         char *p = buf, c, *q;
487
488         while ((c = *s++)) {
489                 for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
490                         if (*q++ == c) {
491                                 *p++ = '\\'; *p++ = *q;
492                                 goto next;
493                         }
494                 if (c < 040) {
495                         *p++ = '^'; *p++ = c + 'A'-1;
496                 } else if (c == 0177) {
497                         *p++ = '^'; *p++ = '?';
498                 } else
499                         *p++ = c;
500         next:
501                 ;
502         }
503         *p = '\0';
504         return (buf);
505 }
506
507 char *
508 ctrl(char c)
509 {
510         static char s[3];
511
512         if (c < 040 || c == 0177) {
513                 s[0] = '^';
514                 s[1] = c == 0177 ? '?' : c+'A'-1;
515                 s[2] = '\0';
516         } else {
517                 s[0] = c;
518                 s[1] = '\0';
519         }
520         return (s);
521 }
522
523 /*
524  * Help command
525  */
526 void
527 help(int c)
528 {
529         esctable_t *p;
530
531         printf("%c\r\n", c);
532         for (p = etable; p->e_char; p++) {
533                 if ((p->e_flags&PRIV) && uid)
534                         continue;
535                 printf("%2s", ctrl(character(value(ESCAPE))));
536                 printf("%-2s %c   %s\r\n", ctrl(p->e_char),
537                         p->e_flags&EXP ? '*': ' ', p->e_help);
538         }
539 }
540
541 /*
542  * Set up the "remote" tty's state
543  */
544 int
545 ttysetup(int speed)
546 {
547         struct termios  cntrl;
548
549         if (tcgetattr(FD, &cntrl))
550                 return (-1);
551         cfsetspeed(&cntrl, speed);
552         cntrl.c_cflag &= ~(CSIZE|PARENB);
553         cntrl.c_cflag |= CS8;
554         if (boolean(value(DC)))
555                 cntrl.c_cflag |= CLOCAL;
556         if (boolean(value(HARDWAREFLOW)))
557                 cntrl.c_cflag |= CRTSCTS;
558         cntrl.c_iflag &= ~(ISTRIP|ICRNL);
559         cntrl.c_oflag &= ~OPOST;
560         cntrl.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
561         cntrl.c_cc[VMIN] = 1;
562         cntrl.c_cc[VTIME] = 0;
563         if (boolean(value(TAND)))
564                 cntrl.c_iflag |= IXOFF;
565         return (tcsetattr(FD, TCSAFLUSH, &cntrl));
566 }
567
568 static char partab[0200];
569
570 /*
571  * Do a write to the remote machine with the correct parity.
572  * We are doing 8 bit wide output, so we just generate a character
573  * with the right parity and output it.
574  */
575 void
576 parwrite(int fd, char *buf, size_t n)
577 {
578         size_t i;
579         char *bp;
580
581         bp = buf;
582         if (bits8 == 0)
583                 for (i = 0; i < n; i++) {
584                         *bp = partab[(*bp) & 0177];
585                         bp++;
586                 }
587         if (write(fd, buf, n) < 0) {
588                 if (errno == EIO || errno == ENXIO)
589                         tipabort("Lost carrier.");
590                 /* this is questionable */
591                 perror("write");
592         }
593 }
594
595 /*
596  * Build a parity table with appropriate high-order bit.
597  */
598 void
599 setparity(char *defparity)
600 {
601         int i, flip, clr, set;
602         char *parity;
603         extern const unsigned char evenpartab[];
604
605         if (value(PARITY) == NOSTR)
606                 value(PARITY) = defparity;
607         parity = value(PARITY);
608         if (equal(parity, "none")) {
609                 bits8 = 1;
610                 return;
611         }
612         bits8 = 0;
613         flip = 0;
614         clr = 0377;
615         set = 0;
616         if (equal(parity, "odd"))
617                 flip = 0200;                    /* reverse bit 7 */
618         else if (equal(parity, "zero"))
619                 clr = 0177;                     /* turn off bit 7 */
620         else if (equal(parity, "one"))
621                 set = 0200;                     /* turn on bit 7 */
622         else if (!equal(parity, "even")) {
623                 (void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
624                 (void) fflush(stderr);
625         }
626         for (i = 0; i < 0200; i++)
627                 partab[i] = ((evenpartab[i] ^ flip) | set) & clr;
628 }