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