1 /* 2 * X.29 server 3 * 4 * Frank Pronk (...!ubc-vision!pronk) 5 * April, September 1984 6 * 7 * Laboratory for Computational Vision 8 * University of British Columbia 9 * Copyright (c) 10 */ 11 12 #include <sys/param.h> 13 #include <sys/socket.h> 14 #include <sys/stat.h> 15 #include <sys/wait.h> 16 17 #include <netccitt/x25.h> 18 19 #include <errno.h> 20 #include <netdb.h> 21 #include <signal.h> 22 #include <sys/ioctl.h> 23 #include <sys/termios.h> 24 #include <paths.h> 25 26 #include "../h/x29.h" 27 28 #define BUFSIZ 1024 29 #define MAXARGS 10 /* maximum size of server argument list */ 30 31 #define X25NET 0 /* no ITI parameters */ 32 #define CCITT1978 1 /* 1978 CCITT standard parameter set */ 33 #define CCITT1980 2 /* 1980 CCITT standard parameter set */ 34 35 36 char pibuf[BUFSIZ], fibuf[BUFSIZ]; 37 int pty, net; 38 extern char **environ; 39 extern int errno; 40 char line[MAXPATHLEN]; 41 char console[] = "/dev/console"; 42 short packet_size; 43 short debug; 44 char *tracefn; /* trace file name */ 45 char *server; 46 short send_banner; 47 struct termios pt, old_pt; 48 struct sockaddr_x25 sock; 49 50 int reapchild(); 51 struct net *lookup (); 52 53 char ccitt1978_prof[] = { /* initial profile */ 54 Q_BIT, X29_SET_AND_READ_PARMS, 55 X29_ECHO_CODE, 1, /* echo on */ 56 X29_FORWARDING_SIGNAL_CODE, 126, /* forward on all cntl */ 57 X29_IDLE_TIMER_CODE, 0, /* off */ 58 X29_AUX_DEV_CONTROL_CODE, 0, /* off */ 59 X29_RECEIVE_NET_MSGS_CODE, 1, /* xmit network msgs */ 60 X29_BREAK_PROCEDURE_CODE, 21, 61 X29_PADDING_CODE, 0, /* off */ 62 X29_LINE_FOLDING_CODE, 0, /* off */ 63 X29_TRANSMISSION_SPEED_CODE, 0, 64 X29_XON_XOFF_CODE, 1, /* enable XON/XOFF */ 65 }; 66 67 char ccitt1980_prof[] = { /* initial profile */ 68 Q_BIT, X29_SET_AND_READ_PARMS, 69 X29_ECHO_CODE, 1, /* echo on */ 70 X29_FORWARDING_SIGNAL_CODE, 126, /* forward on all cntl */ 71 X29_IDLE_TIMER_CODE, 0, /* off */ 72 X29_AUX_DEV_CONTROL_CODE, 0, /* off */ 73 X29_RECEIVE_NET_MSGS_CODE, 1, /* xmit network msgs */ 74 X29_BREAK_PROCEDURE_CODE, 21, 75 X29_PADDING_CODE, 0, /* off */ 76 X29_LINE_FOLDING_CODE, 0, /* off */ 77 X29_TRANSMISSION_SPEED_CODE, 0, 78 X29_XON_XOFF_CODE, 1, /* enable XON/XOFF */ 79 80 X29_LF_AFTER_CR, 4, /* lf after cr from terminal */ 81 X29_EDITING, 1, /* on */ 82 X29_CHARACTER_DELETE, CERASE, 83 X29_LINE_DELETE, CKILL, 84 X29_LINE_DISPLAY, CRPRNT, 85 }; 86 87 char datapac_prof[] = { /* Canadian X.25 network */ 88 Q_BIT, X29_SET_AND_READ_PARMS, 89 X29_ECHO_CODE, 1, /* echo on */ 90 X29_FORWARDING_SIGNAL_CODE, 126, /* forward on all cntl */ 91 X29_IDLE_TIMER_CODE, 0, /* off */ 92 X29_AUX_DEV_CONTROL_CODE, 0, /* off */ 93 X29_RECEIVE_NET_MSGS_CODE, 1, /* xmit network msgs */ 94 X29_BREAK_PROCEDURE_CODE, 21, 95 X29_PADDING_CODE, 0, /* off */ 96 X29_LINE_FOLDING_CODE, 0, /* off */ 97 X29_TRANSMISSION_SPEED_CODE, 0, 98 X29_XON_XOFF_CODE, 1, /* enable XON/XOFF */ 99 100 X29_LF_AFTER_CR, 4, /* lf after cr from terminal */ 101 X29_EDITING, 1, /* on */ 102 X29_CHARACTER_DELETE, CERASE, 103 X29_LINE_DELETE, CKILL, 104 X29_LINE_DISPLAY, CRPRNT, 105 106 /* 107 * This rubbish can be removed when Datapac 108 * adopts the 1980 standard parameter set. 109 */ 110 111 0, 0, /* national parameter marker */ 112 123, 0, /* parity off */ 113 }; 114 115 struct net { 116 char *n_name; /* generic name */ 117 short n_type; /* see defines above */ 118 char *n_profile; /* initial profile */ 119 short n_proflen; /* length of n_profile */ 120 } *netp, nets[] = { 121 "x.25", X25NET, 0, 0, 122 "1978", CCITT1978, ccitt1978_prof, sizeof(ccitt1978_prof), 123 "ccitt1978", CCITT1978, ccitt1978_prof, sizeof(ccitt1978_prof), 124 "1980", CCITT1980, ccitt1980_prof, sizeof(ccitt1980_prof), 125 "ccitt1980", CCITT1980, ccitt1980_prof, sizeof(ccitt1980_prof), 126 "datapac", CCITT1980, datapac_prof, sizeof(datapac_prof), 127 0, 0, 0, 0 128 }; 129 130 main(argc, argv) 131 register char **argv; 132 { 133 register int s, pid; 134 register char *p; 135 136 /* 137 * If this host doesn't support X.25, give up. 138 */ 139 s = socket(AF_CCITT, SOCK_STREAM, 0); 140 if (s < 0 && errno == EPROTONOSUPPORT) 141 fatal(2, "X.25 is not supported on this machine"); 142 close(s); 143 netp = lookup ("ccitt1978"); 144 sock.x25_family = AF_CCITT; 145 sock.x25_len = sizeof(sock); 146 sock.x25_opts.op_flags = X25_MQBIT; 147 sock.x25_udata[0] = ITI_CALL; 148 sock.x25_udlen = 4; 149 150 for (argv++; argc > 1; argc--, argv++) 151 if (**argv == '-') 152 for (p = *argv+1; *p; p++) 153 switch (*p) { 154 case 'b': 155 send_banner++; 156 break; 157 158 case 'c': 159 if (argc > 1) { 160 argc--; argv++; 161 if ((netp = lookup (*argv)) == 0) 162 fatal(1, "Unknown network type"); 163 } 164 break; 165 166 case 'p': 167 if (argc > 1) { 168 argc--; argv++; 169 strcpy (sock.x25_udata, *argv); 170 } 171 break; 172 173 case 'r': 174 sock.x25_opts.op_flags |= X25_REVERSE_CHARGE; 175 break; 176 177 case 'd': 178 debug++; 179 break; 180 181 case 't': 182 if (argc > 1) { 183 argc--; argv++; 184 tracefn = *argv; 185 } 186 else fatal(1, "missing trace file"); 187 break; 188 189 default: 190 fatal (1, "usage: x29d -b -c nettype -p protocol -r -t trace_file server"); 191 } 192 else 193 server = *argv; 194 if (server == 0) 195 fatal (1, "no server specified"); 196 if (debug == 0) 197 daemon(0, 0); 198 199 while ((s = socket(AF_CCITT, SOCK_STREAM, 0)) < 0) 200 sleep(60); 201 while (bind(s, (caddr_t)&sock, sizeof (sock)) < 0) 202 sleep(60); 203 signal(SIGCHLD, reapchild); 204 listen(s, 5); 205 206 for (;;) { 207 struct sockaddr_x25 from; 208 int fromlen = sizeof (from); 209 210 if ((net = accept(s, (caddr_t)&from, &fromlen)) < 0) { 211 if (errno != EINTR) 212 sleep (60); 213 continue; 214 } 215 while ((pid = fork()) < 0) 216 sleep(60); 217 if (pid == 0) { 218 signal(SIGCHLD, SIG_DFL); 219 doit(&from); 220 } 221 close(net); 222 } 223 /*NOTREACHED*/ 224 } 225 226 struct net * 227 lookup (name) 228 char *name; 229 { 230 register struct net *np; 231 232 for (np = nets; np->n_name; np++) 233 if (strcmp (np->n_name, name) == 0) 234 return (np); 235 return (0); 236 } 237 238 reapchild() 239 { 240 union wait status; 241 242 while (wait3(&status, WNOHANG, 0) > 0) 243 ; 244 } 245 246 char *envinit[] = { "TERM=ccitt", 0 }; 247 int cleanup(); 248 struct termios term; 249 250 /* 251 * Get a pty, scan input lines. 252 */ 253 doit(who) 254 struct sockaddr_x25 *who; 255 { 256 register char *cp; 257 int i, p, t; 258 259 packet_size = 1 << who->x25_opts.op_psize; 260 i = forkpty(&pty, line, &term, 0); 261 if (i > 0) 262 x29d(); 263 if (i < 0) 264 fatalperror("fork", errno); 265 environ = envinit; 266 call_server (who); 267 /*NOTREACHED*/ 268 } 269 270 call_server (who) 271 struct sockaddr_x25 *who; 272 { 273 register struct hostent *hp = 0; 274 register char *p, **ap; 275 char *args[MAXARGS]; 276 struct stat st; 277 struct hostent *getx25hostbyaddr(); 278 int ccitt = 0; 279 280 p = server; 281 while (*p && *p != ' ' && *p != '\t') /* split program from args */ 282 p++; 283 if (*p) 284 *p++ = '\0'; 285 ap = args; 286 while (*p) { 287 while (*p == ' ' || *p == '\t') 288 p++; 289 if (ap < &args[MAXARGS-2]) 290 *ap++ = p; 291 if (strcmp(p, "-ccitt") == 0) 292 ccitt = 1; 293 while (*p && *p != ' ' && *p != '\t') 294 p++; 295 if (*p) 296 *p++ = '\0'; 297 } 298 if (stat (server, &st) < 0) 299 fatalperror (server, errno); 300 /* 301 * For security: if running as root, switch to user 302 * and group id of server. This prevents privately 303 * maintainted or bogus servers from getting super- 304 * user permissions. 305 */ 306 if (getuid() == 0) { 307 setgid (st.st_gid); 308 setuid (st.st_uid); 309 } 310 if (hp = getx25hostbyaddr (who->x25_addr)) 311 *ap++ = hp->h_name; 312 else 313 *ap++ = (char *)who->x25_addr; 314 /* 315 * If the -ccitt flag was given, add another argument 316 * to tell login if charging is being reversed or not. 317 */ 318 if (ccitt) 319 *ap++ = (who->x25_opts.op_flags & X25_REVERSE_CHARGE) ? "y" : "n"; 320 *ap = 0; 321 execv (server, args); 322 fatalperror (server, errno); 323 /*NOTREACHED*/ 324 } 325 326 fatal(f, msg) 327 int f; 328 char *msg; 329 { 330 register char *p; 331 char buf[BUFSIZ], *index(); 332 333 p = buf; 334 if (f == net) 335 *p++ = 0; 336 strcpy(p, "x29d: "); 337 strcat(p, msg); 338 strcat(p, "\n"); 339 (void) write(f, p, (index(p, '\n')-p)+1); 340 exit(1); 341 } 342 343 fatalperror(msg, err) 344 char *msg; 345 { 346 char buf[BUFSIZ]; 347 extern char *sys_errlist[]; 348 349 strcpy(buf, msg); 350 strcat(buf, ": "); 351 strcat(buf, sys_errlist[err]); 352 fatal(net, buf); 353 } 354 355 /* 356 * Main loop. Select from pty and network, and 357 * hand data to iti receiver. 358 */ 359 x29d() 360 { 361 register int pcc, fcc, cc; 362 register char *fbp; 363 int pgrp, x25_interrupt(), on = 1; 364 char hostname[32]; 365 366 ioctl(net, FIONBIO, (char *)&on); 367 ioctl(pty, FIONBIO, (char *)&on); 368 /*ioctl(pty, TIOCREMECHO, (char *)&on); /* enable special pty mode */ 369 /* new equivalent is no processing in pty, no echo, but let 370 user set modes and have either remote end do line mode processing 371 or do it in daemon */ 372 ioctl(pty, TIOCEXT, (char *)&on); 373 ioctl(pty, TIOCPKT, (char *)&on); 374 ioctl(pty, TIOCGETA, (char *)&pt); 375 signal(SIGPIPE, SIG_IGN); /* why not cleanup? --kwl */ 376 signal(SIGTSTP, SIG_IGN); 377 signal(SIGCHLD, cleanup); 378 signal(SIGHUP, cleanup); 379 380 signal(SIGTTOU, SIG_IGN); 381 signal(SIGURG, x25_interrupt); /* for out-of-band data */ 382 383 if (netp->n_proflen) 384 (void) write(net, netp->n_profile, netp->n_proflen); 385 386 /* 387 * Show banner that getty never gave. 388 */ 389 if (send_banner) { 390 gethostname(hostname, sizeof (hostname)); 391 #ifdef BSD4_3 392 strcpy(pibuf+1, "\r\n\r\n4.3 BSD UNIX ("); 393 #else 394 strcpy(pibuf+1, "\r\n\r\n4.2 BSD UNIX ("); 395 #endif 396 strcat(pibuf+1, hostname); 397 strcat(pibuf+1, ")\r\n\r\n"); 398 pcc = strlen(pibuf+1) + 1; 399 } else 400 pcc = 0; 401 402 fcc = 0; 403 for (;;) { 404 int ibits, obits; 405 406 ibits = obits = 0; 407 /* 408 * Never look for input if there's still 409 * stuff in the corresponding output buffer 410 */ 411 if (fcc >= 0) /* net connection alive? */ 412 if (fcc && pcc >= 0) /* output pending? */ 413 obits |= (1 << pty); 414 else 415 if (pcc >= 0) /* pty still alive? */ 416 ibits |= (1 << net); 417 if (pcc >= 0) /* pty connection alive? */ 418 if (pcc && fcc >= 0) /* output pending? */ 419 obits |= (1 << net); 420 else 421 if (fcc >= 0) /* net still alive? */ 422 ibits |= (1 << pty); 423 if (ibits == 0 && obits == 0) 424 break; 425 (void) select(16, &ibits, &obits, (int *)0, 0); 426 if (ibits == 0 && obits == 0) { 427 sleep(5); 428 continue; 429 } 430 431 /* 432 * Something to read from the network... 433 */ 434 if (fcc == 0 && (ibits & (1 << net))) { 435 fcc = read(net, fibuf, BUFSIZ); 436 fbp = fibuf+1; 437 if (fcc < 0 && errno == EWOULDBLOCK) 438 fcc = 0; 439 else if (fcc <= 0) 440 fcc = -1; 441 else { 442 if (tracefn) 443 x29d_trace("netread", fibuf, fcc); 444 if (fibuf[0] & Q_BIT) { 445 x29_qbit(fcc); 446 fcc = 0; 447 } else 448 fcc--; 449 } 450 } 451 452 /* 453 * Something to read from the pty... 454 */ 455 if (ibits & (1 << pty)) { 456 pcc = read(pty, pibuf, packet_size+1); 457 if (pcc < 0 && errno == EWOULDBLOCK) 458 pcc = 0; 459 else if (pcc <= 0) 460 pcc = -1; 461 else if (pibuf[0] != 0) { /* non-data packet */ 462 if (pibuf[0] & TIOCPKT_IOCTL) { 463 if (--pcc > sizeof(pt)) 464 pcc = sizeof(pt); 465 old_pt = pt; 466 bcopy(pibuf + 1, (char *)&pt, pcc); 467 pcc = set_x29_parameters(); 468 } else 469 pcc = 0; 470 } else /* data packet */ 471 pibuf[0] = 0; 472 } 473 474 if ((obits & (1<<net)) && pcc > 0) 475 if ((cc = write(net, pibuf, pcc)) == pcc) { 476 if (tracefn) 477 x29d_trace("netwrite", pibuf, pcc); 478 pcc = 0; 479 } else { 480 extern char *sys_errlist[]; 481 482 if (tracefn) 483 x29d_trace("netwrite", 484 sys_errlist[errno], 485 strlen(sys_errlist[errno])); 486 487 } 488 489 if ((obits & (1 << pty)) && fcc > 0) { 490 cc = ptywrite(fbp, fcc); 491 if (cc > 0) { 492 fcc -= cc; 493 fbp += cc; 494 } 495 } 496 } 497 cleanup(); 498 } 499 500 501 /* 502 * Send interrupt to process on other side of pty. 503 * If it is in raw mode, just write NULL; 504 * otherwise, write intr char. 505 */ 506 507 x25_interrupt() 508 { 509 struct termios tt; 510 int zero = 0; 511 512 signal(SIGURG, x25_interrupt); 513 tcgetattr(pty, &tt); 514 if (tt.c_lflag & ISIG) { 515 tcsetattr(pty, TCSAFLUSH, &tt); 516 (void) write(pty, &tt.c_cc[VINTR], 1); 517 } else 518 (void) write(pty, "\0", 1); 519 } 520 521 cleanup() 522 { 523 char *p; 524 525 p = line + sizeof(_PATH_DEV) - 1; 526 if (logout(p)) 527 logwtmp(p, "", ""); 528 (void)chmod(line, 0666); 529 (void)chown(line, 0, 0); 530 *p = 'p'; 531 (void)chmod(line, 0666); 532 (void)chown(line, 0, 0); 533 shutdown(net, 2); 534 exit(1); 535 } 536 537 /* 538 * Map unix tty modes and special characters 539 * into x29 parameters. 540 */ 541 542 set_x29_parameters() 543 { 544 register char *p; 545 int f; 546 char *lim = p + sizeof (pt); 547 548 if (netp->n_type == X25NET) 549 return (0); 550 if ((old_pt.c_lflag & ICANON) != (pt.c_lflag & ICANON)) { 551 f = pt.c_lflag & ICANON; 552 ioctl(pty, TIOCEXT, &f); 553 /* this precipitates more junk of the same 554 * sort that caused our call here, but we can't 555 * turn it off since something may be going on in our progeny. 556 * 557 * Instead, we'll check the next time around to see if nothing 558 * has changed, and skip informing the network. 559 */ 560 } 561 if (bcmp((char *)&pt, (char *)&old_pt, sizeof (pt)) == 0) 562 return; 563 p = pibuf; 564 *p++ = Q_BIT; 565 *p++ = X29_SET_PARMS; 566 /* *p++ = X29_ESCAPE_TO_CMD_CODE; *p++ = (f & (RAW|CBREAK)) == 0;*/ 567 *p++ = X29_ESCAPE_TO_CMD_CODE; *p++ = (pt.c_lflag & ICANON) != 0; 568 569 *p++ = X29_ECHO_CODE; *p++ = (pt.c_lflag & ECHO) != 0; 570 *p++ = X29_FORWARDING_SIGNAL_CODE; 571 *p++ = (pt.c_lflag & ISIG) ? 0 : 126; 572 573 /* 574 * The value of 10 (0.5 seconds) for the idle timer when 575 * in raw or cbreak mode is a compromise value. For good 576 * interactive response this value should be as low as 577 * possible; for reasonable efficiency with file transfers 578 * this value should be at fairly high. This number should 579 * be changed to suit local requirements. 580 */ 581 582 /**p++ = X29_IDLE_TIMER_CODE; *p++ = (f & (RAW|CBREAK)) ? 10 : 0;*/ 583 *p++ = X29_IDLE_TIMER_CODE; *p++ = (pt.c_lflag & ICANON) ? 0 : 10; 584 585 /**p++ = X29_AUX_DEV_CONTROL_CODE;*p++ = (f & TANDEM) != 0;*/ 586 *p++ = X29_AUX_DEV_CONTROL_CODE;*p++ = (pt.c_iflag & IXOFF) != 0; 587 *p++ = X29_XON_XOFF_CODE; *p++ = (pt.c_iflag & IXON) != 0; 588 if (netp->n_type == CCITT1980) { 589 *p++ = X29_LF_AFTER_CR; 590 /* *p++ = (f & (RAW|CBREAK) || (f & ECHO) == 0) ? 0 : 4; */ 591 *p++ = ((pt.c_lflag & (ICANON | ECHO)) != (ICANON | ECHO)) ? 592 0 : 4; 593 594 *p++ = X29_EDITING; *p++ = (pt.c_lflag & ICANON) != 0; 595 #define ctlchar(x) \ 596 (0 == (pt.c_lflag & ICANON) || pt.c_cc[x] == _POSIX_VDISABLE) ? 0 : pt.c_cc[x] 597 *p++ = X29_CHARACTER_DELETE; *p++ = ctlchar(VERASE); 598 *p++ = X29_LINE_DELETE; *p++ = ctlchar(VKILL); 599 *p++ = X29_LINE_DISPLAY; *p++ = ctlchar(VREPRINT); 600 } 601 #undef ctlchar 602 return (p - pibuf); 603 } 604 605 /* Have to be careful writing to pty. The pad will forward control 606 * characters without necessarily sending an interrupt so if ISIG and 607 * ICANNON are set, must inspect line for quit or interrupt or suspend. 608 */ 609 ptywrite(buf, n) 610 char *buf; 611 int n; 612 { 613 register char *cp, *lim; 614 char *last; 615 #define is_ctl(x) (pt.c_cc[x] == *(cc_t *)cp) 616 617 if ((pt.c_lflag & EXTPROC) && (pt.c_lflag & ISIG)) { 618 for (cp = buf, lim = buf + n; cp < lim; cp ++) { 619 if (is_ctl(VLNEXT)) 620 { cp++; continue; } 621 if (is_ctl(VSUSP) || is_ctl(VDSUSP) || 622 is_ctl(VINTR) || is_ctl(VQUIT)) { 623 int onoff = 0; 624 tcflag_t old_echo = pt.c_lflag & ECHO; 625 626 ioctl(pty, TIOCPKT, (char *)&onoff); 627 ioctl(pty, FIONBIO, (char *)&onoff); 628 ioctl(pty, TIOCEXT, (char *)&onoff); 629 if (old_echo) { 630 pt.c_lflag &= ~ECHO; 631 ioctl(pty, TIOCSETA, (char *)&pt); 632 } 633 n = write(pty, buf, n); 634 onoff = 1; 635 if (old_echo) { 636 pt.c_lflag |= ECHO; 637 ioctl(pty, TIOCSETA, (char *)&pt); 638 } 639 ioctl(pty, TIOCEXT, (char *)&onoff); 640 ioctl(pty, FIONBIO, (char *)&onoff); 641 ioctl(pty, TIOCPKT, (char *)&onoff); 642 return (n); 643 } 644 } 645 } 646 return write(pty, buf, n); 647 } 648 649 /* 650 * Process Q BIT (control) packets from the net. 651 * The only message that we are interested in are 652 * those indicating output is being discarded. 653 */ 654 655 x29_qbit(n) 656 { 657 register char *p; 658 659 switch (fibuf[1]) { 660 case X29_SET_PARMS: 661 case X29_SET_AND_READ_PARMS: 662 case X29_PARAMETER_INDICATION: 663 case X29_INDICATION_OF_BREAK: 664 for (p = &fibuf[2]; p < fibuf+n; p++) { 665 if (*p == X29_TRANSMISSION_SPEED_CODE) { 666 static char speeds[] = { 667 B110, B0, B300, B1200, B600, 668 B0, B0, B0, B0, B0, B0, B0, 669 B2400, B4800, B9600, EXTA }; 670 671 if (*++p >= 0 && *p < sizeof(speeds)) { 672 cfsetspeed(&pt, speeds[*p]); 673 tcsetattr(pty, TCSANOW, &pt); 674 } 675 } else if (*p == X29_DISCARD_OUTPUT_CODE && *++p != 0) { 676 char message[4]; 677 678 /* 679 * Always re-enable normal output 680 */ 681 message[0] = Q_BIT; 682 message[1] = X29_SET_PARMS; 683 message[2] = X29_DISCARD_OUTPUT_CODE; 684 message[3] = 0; 685 (void) write(net, message, sizeof(message)); 686 if (tracefn) 687 x29d_trace("netwrite", message, 4); 688 } 689 } 690 return; 691 692 default: { 693 register char *p2; 694 char buf[BUFSIZ*4]; 695 static int fd; 696 697 /* 698 * Bad news - we received an x29 error message or 699 * some other unknown packet. Dump the contents 700 * of the packet on the console. 701 */ 702 p = buf; 703 for (p2 = "x29d: unknown q-bit packet: "; *p++ = *p2++; ); 704 for (p2 = fibuf+1; p2 < fibuf+n; p2++) 705 if (*p2 >= ' ' && *p2 < 0177) 706 *p++ = *p2; 707 else { 708 *p++ = '\\'; 709 *p++ = ((*p2 & 0300) >> 6) + '0'; 710 *p++ = ((*p2 & 070) >> 3) + '0'; 711 *p++ = (*p2 & 07) + '0'; 712 } 713 *p++ = '\n'; 714 if (fd <= 0) 715 fd = open(console, 1); 716 (void) write(fd, buf, p-buf); 717 } 718 } 719 } 720 721 x29d_trace(s, bp, n) 722 char *s, *bp; 723 { 724 static int fd; 725 char buf[BUFSIZ*4]; 726 register char *p1, *p2; 727 728 for (p1 = buf; *s; *p1++ = *s++); 729 *p1++ = ':'; 730 *p1++ = ' '; 731 for (p2=bp; p2 < bp+n; p2++) 732 if (*p2 >= ' ' && *p2 < 0177) 733 *p1++ = *p2; 734 else { 735 *p1++ = '\\'; 736 *p1++ = ((*p2 & 0300) >> 6) + '0'; 737 *p1++ = ((*p2 & 070) >> 3) + '0'; 738 *p1++ = (*p2 & 07) + '0'; 739 } 740 *p1++ = '\n'; 741 if (fd <= 0) 742 fd = creat(tracefn, 0666); 743 (void) write(fd, buf, p1-buf); 744 } 745