1 #include <u.h> 2 #include <libc.h> 3 4 #include "rustream.h" 5 #include "ruttyio.h" 6 #include "rusignal.h" 7 #include "rufilio.h" 8 9 int debug; /* true if debugging */ 10 int ctl = -1; /* control fd (for break's) */ 11 int raw; /* true if raw is on */ 12 int consctl = -1; /* control fd for cons */ 13 int ttypid; /* pid's if the 2 processes (used to kill them) */ 14 int msgfd = -1; /* mesgld file descriptor (for signals to be written to) */ 15 int outfd = 1; /* local output file descriptor */ 16 int cooked; /* non-zero forces cooked mode */ 17 int returns; /* non-zero forces carriage returns not to be filtered out */ 18 int strip; /* strip off parity bits */ 19 char firsterr[2*ERRLEN]; 20 char transerr[2*ERRLEN]; 21 int limited; 22 char *remuser; 23 int verbose; 24 int baud; 25 26 typedef struct Msg Msg; 27 #define MAXMSG (2*8192) 28 29 int dkauth(int); 30 int dodial(char*, char*, char*); 31 void fromkbd(int); 32 void fromnet(int); 33 long iread(int, void*, int); 34 long iwrite(int, void*, int); 35 int menu(int); 36 void msgfromkbd(int); 37 void msgfromnet(int); 38 void msgnotifyf(void*, char*); 39 int msgwrite(int, void*, int); 40 void notifyf(void*, char*); 41 void pass(int, int, int); 42 void rawoff(void); 43 void rawon(void); 44 int readupto(int, char*, int); 45 int sendctl(int, int); 46 int sendctl1(int, int, int); 47 void stdcon(int); 48 char* system(int, char*); 49 void dosystem(int, char*); 50 int wasintr(void); 51 void punt(char*); 52 char* syserr(void); 53 void seterr(char*); 54 55 /* protocols */ 56 void dcon(char*, char*); 57 void device(char*, char*); 58 void mesgdcon(char*, char*); 59 void rlogin(char*, char*); 60 void simple(char*, char*); 61 62 void 63 usage(void) 64 { 65 punt("usage: con [-drCvs] [-l [user]] [-c cmd] net!host[!service]"); 66 } 67 68 void 69 main(int argc, char *argv[]) 70 { 71 char *dest; 72 char *cmd = 0; 73 74 returns = 1; 75 ARGBEGIN{ 76 case 'b': 77 baud = atoi(ARGF()); 78 break; 79 case 'd': 80 debug = 1; 81 break; 82 case 'l': 83 limited = 1; 84 if(argv[1][0] != '-') 85 remuser = ARGF(); 86 break; 87 case 'r': 88 returns = 0; 89 break; 90 case 'C': 91 cooked = 1; 92 break; 93 case 'c': 94 cmd = ARGF(); 95 break; 96 case 'v': 97 verbose = 1; 98 break; 99 case 's': 100 strip = 1; 101 break; 102 default: 103 usage(); 104 }ARGEND 105 106 if(argc != 1){ 107 if(remuser == 0) 108 usage(); 109 dest = remuser; 110 remuser = 0; 111 } else 112 dest = argv[0]; 113 if(*dest == '/') 114 device(dest, cmd); 115 else if(limited){ 116 simple(dest, cmd); /* doesn't return if dialout succeeds */ 117 rlogin(dest, cmd); /* doesn't return if dialout succeeds */ 118 } else { 119 rlogin(dest, cmd); /* doesn't return if dialout succeeds */ 120 mesgdcon(dest, cmd); /* doesn't return if dialout succeeds */ 121 dcon(dest, cmd); /* doesn't return if dialout succeeds */ 122 simple(dest, cmd); /* doesn't return if dialout succeeds */ 123 } 124 punt(firsterr); 125 } 126 127 /* 128 * just dial and use as a byte stream with remote echo 129 */ 130 void 131 simple(char *dest, char *cmd) 132 { 133 int net; 134 135 net = dodial(dest, 0, 0); 136 if(net < 0) 137 return; 138 139 if(cmd) 140 dosystem(net, cmd); 141 142 if(!cooked) 143 rawon(); 144 stdcon(net); 145 exits(0); 146 } 147 148 /* 149 * dial, do UCB authentication, use as a byte stream with local echo 150 * 151 * return if dial failed 152 */ 153 void 154 rlogin(char *dest, char *cmd) 155 { 156 int net, fd; 157 long n; 158 char buf[2*NAMELEN]; 159 char *p; 160 char *localuser; 161 162 /* only useful on TCP */ 163 if(strchr(dest, '!') 164 && (strncmp(dest, "tcp!", 4)!=0 && strncmp(dest, "net!", 4)!=0)) 165 return; 166 167 net = dodial(dest, "tcp", "login"); 168 if(net < 0) 169 return; 170 171 /* 172 * do UCB rlogin authentication 173 */ 174 fd = open("/dev/user", OREAD); 175 n = read(fd, buf, sizeof(buf)); 176 close(fd); 177 buf[n] = 0; 178 localuser = buf; 179 if(remuser == 0){ 180 if(limited) 181 remuser = ":"; 182 else 183 remuser = localuser; 184 } 185 p = getenv("TERM"); 186 if(p == 0) 187 p = "p9"; 188 if(write(net, "", 1)<0 189 || write(net, localuser, strlen(localuser)+1)<0 190 || write(net, remuser, strlen(remuser)+1)<0 191 || write(net, p, strlen(p)+1)<0){ 192 close(net); 193 punt("BSD authentication failed"); 194 } 195 if(read(net, buf, 1) != 1) 196 punt("BSD authentication failed1"); 197 if(buf[0] != 0){ 198 fprint(2, "con: remote error: "); 199 while(read(net, buf, 1) == 1){ 200 write(2, buf, 1); 201 if(buf[0] == '\n') 202 break; 203 } 204 exits("read"); 205 } 206 207 if(cmd) 208 dosystem(net, cmd); 209 210 if(!cooked) 211 rawon(); 212 stdcon(net); 213 exits(0); 214 } 215 216 /* 217 * dial, do DK authentication, use as a byte stream with remote echo 218 * 219 * return if dial failed 220 */ 221 void 222 dcon(char *dest, char *cmd) 223 { 224 int net; 225 226 net = dodial(dest, 0, "dcon"); 227 if(net < 0) 228 return; 229 230 if(dkauth(net) < 0) 231 punt("can't authenticate across datakit"); 232 233 if(cmd) 234 dosystem(net, cmd); 235 236 if(!cooked) 237 rawon(); 238 stdcon(net); 239 exits(0); 240 } 241 242 /* 243 * just open a device and use it as a connection 244 */ 245 void 246 device(char *dest, char *cmd) 247 { 248 int net; 249 char cname[3*NAMELEN]; 250 251 net = open(dest, ORDWR); 252 if(net < 0) 253 punt(syserr()); 254 sprint(cname, "%sctl", dest); 255 ctl = open(cname, ORDWR); 256 if(ctl >= 0 && baud > 0) 257 fprint(ctl, "b%d", baud); 258 259 if(cmd) 260 dosystem(net, cmd); 261 262 if(!cooked) 263 rawon(); 264 stdcon(net); 265 exits(0); 266 } 267 268 /* 269 * ignore interrupts 270 */ 271 void 272 notifyf(void *a, char *msg) 273 { 274 USED(a); 275 276 if(strstr(msg, "closed pipe") 277 || strcmp(msg, "interrupt") == 0 278 || strcmp(msg, "hangup") == 0) 279 noted(NCONT); 280 noted(NDFLT); 281 } 282 283 /* 284 * turn keyboard raw mode on 285 */ 286 void 287 rawon(void) 288 { 289 if(debug) 290 fprint(2, "rawon\n"); 291 if(raw) 292 return; 293 if(consctl < 0) 294 consctl = open("/dev/consctl", OWRITE); 295 if(consctl < 0){ 296 fprint(2, "can't open consctl\n"); 297 return; 298 } 299 write(consctl, "rawon", 5); 300 raw = 1; 301 } 302 303 /* 304 * turn keyboard raw mode off 305 */ 306 void 307 rawoff(void) 308 { 309 if(debug) 310 fprint(2, "rawoff\n"); 311 if(raw == 0) 312 return; 313 if(consctl < 0) 314 consctl = open("/dev/consctl", OWRITE); 315 if(consctl < 0){ 316 fprint(2, "can't open consctl\n"); 317 return; 318 } 319 write(consctl, "rawoff", 6); 320 raw = 0; 321 } 322 323 /* 324 * control menu 325 */ 326 #define STDHELP "\t(b)reak, (q)uit, (i)nterrupt, (r)eturns, (.)continue, (!cmd)\n" 327 328 int 329 menu(int net) 330 { 331 char buf[MAXMSG]; 332 long n; 333 int done; 334 int wasraw = raw; 335 336 if(wasraw) 337 rawoff(); 338 339 fprint(2, ">>> "); 340 for(done = 0; !done; ){ 341 n = read(0, buf, sizeof(buf)-1); 342 if(n <= 0) 343 return -1; 344 buf[n] = 0; 345 switch(buf[0]){ 346 case '!': 347 print(buf); 348 system(net, buf+1); 349 print("!\n"); 350 done = 1; 351 break; 352 case '.': 353 done = 1; 354 break; 355 case 'q': 356 return -1; 357 break; 358 case 'i': 359 buf[0] = 0x1c; 360 if(msgfd <= 0) 361 write(net, buf, 1); 362 else 363 sendctl1(msgfd, M_SIGNAL, SIGQUIT); 364 done = 1; 365 break; 366 case 'b': 367 if(msgfd >= 0) 368 sendctl(msgfd, M_BREAK); 369 else if(ctl >= 0) 370 write(ctl, "k", 1); 371 done = 1; 372 break; 373 case 'r': 374 returns = 1-returns; 375 done = 1; 376 break; 377 default: 378 fprint(2, STDHELP); 379 break; 380 } 381 if(!done) 382 fprint(2, ">>> "); 383 } 384 385 if(wasraw) 386 rawon(); 387 else 388 rawoff(); 389 return 0; 390 } 391 392 /* 393 * the real work. two processes pass bytes back and forth between the 394 * terminal and the network. 395 */ 396 void 397 stdcon(int net) 398 { 399 int netpid; 400 401 ttypid = getpid(); 402 switch(netpid = rfork(RFMEM|RFPROC)){ 403 case -1: 404 perror("con"); 405 exits("fork"); 406 case 0: 407 notify(notifyf); 408 fromnet(net); 409 postnote(PNPROC, ttypid, "kill"); 410 exits(0); 411 default: 412 notify(notifyf); 413 fromkbd(net); 414 postnote(PNPROC, netpid, "kill"); 415 exits(0); 416 } 417 } 418 419 /* 420 * Read the keyboard and write it to the network. '^\' gets us into 421 * the menu. 422 */ 423 void 424 fromkbd(int net) 425 { 426 long n; 427 char buf[MAXMSG]; 428 429 for(;;){ 430 n = read(0, buf, sizeof(buf)); 431 if(n < 0){ 432 if(wasintr()){ 433 if(cooked){ 434 buf[0] = 0x7f; 435 n = 1; 436 } else 437 continue; 438 } else 439 return; 440 } 441 if(n && memchr(buf, 0x1c, n)){ 442 if(menu(net) < 0) 443 return; 444 }else{ 445 if(cooked && n==0){ 446 buf[0] = 0x4; 447 n = 1; 448 } 449 if(iwrite(net, buf, n) != n) 450 return; 451 } 452 } 453 } 454 455 /* 456 * Read from the network and write to the screen. 457 * Filter out spurious carriage returns. 458 */ 459 void 460 fromnet(int net) 461 { 462 long n; 463 char buf[MAXMSG]; 464 char *cp, *ep; 465 466 for(;;){ 467 n = iread(net, buf, sizeof(buf)); 468 if(n < 0) 469 return; 470 if(n == 0) 471 continue; 472 473 if (strip) 474 for (cp=buf; cp<buf+n; cp++) 475 *cp &= 0177; 476 477 if(!returns){ 478 /* convert cr's to null's */ 479 cp = buf; 480 ep = buf + n; 481 while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){ 482 memmove(cp, cp+1, ep-cp-1); 483 ep--; 484 n--; 485 } 486 } 487 488 if(n > 0 && iwrite(outfd, buf, n) != n){ 489 if(outfd == 1) 490 return; 491 outfd = 1; 492 if(iwrite(1, buf, n) != n) 493 return; 494 } 495 } 496 } 497 498 /* 499 * dial and return a data connection 500 */ 501 int 502 dodial(char *dest, char *net, char *service) 503 { 504 char name[3*NAMELEN]; 505 char devdir[3*NAMELEN]; 506 int data; 507 508 devdir[0] = 0; 509 strcpy(name, netmkaddr(dest, net, service)); 510 data = dial(name, 0, devdir, &ctl); 511 if(data < 0){ 512 seterr(name); 513 return -1; 514 } 515 fprint(2, "connected to %s on %s\n", name, devdir); 516 return data; 517 } 518 519 /* 520 * send a note to a process 521 */ 522 /* 523 * datakit authentication 524 */ 525 int 526 dkauth(int net) 527 { 528 char buf[128]; 529 long n; 530 char *p; 531 532 for(;;){ 533 if(read(net, buf, 2)!=2) 534 return -1; 535 if(buf[0]=='O' && buf[1]=='K') 536 break; 537 if(buf[0]!='N' || buf[1]!='O') 538 return -1; 539 print("please login: "); 540 n = read(0, buf, sizeof(buf)-1); 541 if(n<=0) 542 exits("login"); 543 buf[n-1] = ','; 544 rawon(); 545 print("password: "); 546 for(p = &buf[n]; p<&buf[127];p++){ 547 if(read(0, p, 1)!=1) 548 return -1; 549 if(*p=='\r' || *p=='\n'){ 550 *p++ = 0; 551 break; 552 } 553 } 554 n = p-buf; 555 rawoff(); 556 if(write(net, buf, n)!=n) 557 return -1; 558 } 559 return 0; 560 } 561 562 void 563 dosystem(int fd, char *cmd) 564 { 565 char *p; 566 567 p = system(fd, cmd); 568 if(*p){ 569 print("con: %s terminated with %s\n", p); 570 exits(p); 571 } 572 } 573 574 /* 575 * run a command with the network connection as standard IO 576 */ 577 char * 578 system(int fd, char *cmd) 579 { 580 int pid; 581 int p; 582 static Waitmsg msg; 583 int pfd[2]; 584 int n; 585 char buf[4096]; 586 587 if(pipe(pfd) < 0){ 588 perror("pipe"); 589 return "pipe failed"; 590 } 591 outfd = pfd[1]; 592 593 close(consctl); 594 consctl = -1; 595 switch(pid = fork()){ 596 case -1: 597 perror("con"); 598 return "fork failed"; 599 case 0: 600 close(pfd[1]); 601 dup(pfd[0], 0); 602 dup(pfd[0], 1); 603 close(ctl); 604 close(fd); 605 close(pfd[0]); 606 if(*cmd) 607 execl("/bin/rc", "rc", "-c", cmd, 0); 608 else 609 execl("/bin/rc", "rc", 0); 610 perror("con"); 611 exits("exec"); 612 break; 613 default: 614 close(pfd[0]); 615 while((n = read(pfd[1], buf, sizeof(buf))) > 0){ 616 if(msgfd >= 0){ 617 if(msgwrite(fd, buf, n) != n) 618 break; 619 } else { 620 if(write(fd, buf, n) != n) 621 break; 622 } 623 } 624 p = wait(&msg); 625 outfd = 1; 626 close(pfd[1]); 627 if(p < 0 || p != pid) 628 return "lost child"; 629 break; 630 } 631 return msg.msg; 632 } 633 634 int 635 wasintr(void) 636 { 637 return strcmp(syserr(), "interrupted") == 0; 638 } 639 640 void 641 punt(char *msg) 642 { 643 if(*msg == 0) 644 msg = transerr; 645 fprint(2, "con: %s\n", msg); 646 exits(msg); 647 } 648 649 char* 650 syserr(void) 651 { 652 static char err[ERRLEN]; 653 errstr(err); 654 return err; 655 } 656 657 void 658 seterr(char *addr) 659 { 660 char *se = syserr(); 661 662 if(verbose) 663 fprint(2, "'%s' calling %s\n", se, addr); 664 if(firsterr[0] && (strstr(se, "translate") || 665 strstr(se, "file does not exist") || 666 strstr(se, "unknown address") || 667 strstr(se, "directory entry not found"))) 668 return; 669 strcpy(firsterr, se); 670 } 671 672 673 long 674 iread(int f, void *a, int n) 675 { 676 long m; 677 678 for(;;){ 679 m = read(f, a, n); 680 if(m >= 0 || !wasintr()) 681 break; 682 } 683 return m; 684 } 685 686 long 687 iwrite(int f, void *a, int n) 688 { 689 long m; 690 691 m = write(f, a, n); 692 if(m < 0 && wasintr()) 693 return n; 694 return m; 695 } 696 697 /* 698 * The rest is to support the V10 mesgld protocol. 699 */ 700 701 /* 702 * network orderings 703 */ 704 #define get2byte(p) ((p)[0] + ((p)[1]<<8)) 705 #define get4byte(p) ((p)[0] + ((p)[1]<<8) + ((p)[2]<<16) + ((p)[3]<<24)) 706 #define put2byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8) 707 #define put4byte(p, i) ((p)[0]=(i), (p)[1]=(i)>>8, (p)[2]=(i)>>16, (p)[3]=(i)>>24) 708 709 /* 710 * tty parameters 711 */ 712 int sgflags = ECHO; 713 714 /* 715 * a mesgld message 716 */ 717 struct Msg { 718 struct mesg h; 719 char b[MAXMSG]; 720 }; 721 722 /* 723 * connect using the mesgdcon protocol. infinite ugliness to talk to a V10 724 * system. 725 */ 726 void 727 mesgdcon(char *dest, char *cmd) 728 { 729 int net; 730 int netpid; 731 732 net = dodial(dest, "dk", "mesgdcon"); 733 if(net < 0) 734 return; 735 736 if(dkauth(net) < 0) 737 punt("can't authenticate across datakit"); 738 739 if(cmd) 740 dosystem(net, cmd); 741 742 msgfd = net; 743 ttypid = getpid(); 744 switch(netpid = rfork(RFMEM|RFPROC)){ 745 case -1: 746 perror("con"); 747 exits("fork"); 748 case 0: 749 notify(notifyf); 750 msgfromnet(net); 751 postnote(PNPROC, ttypid, "kill"); 752 exits(0); 753 default: 754 notify(msgnotifyf); 755 msgfromkbd(net); 756 postnote(PNPROC, netpid, "kill"); 757 exits(0); 758 } 759 } 760 761 /* 762 * convert certain interrupts into mesgld messages 763 */ 764 void 765 msgnotifyf(void *a, char *msg) 766 { 767 USED(a); 768 769 if(strstr(msg, "closed pipe")) 770 noted(NCONT); 771 if(strcmp(msg, "interrupt") == 0){ 772 sendctl1(msgfd, M_SIGNAL, SIGINT); 773 noted(NCONT); 774 } 775 if(strcmp(msg, "hangup") == 0){ 776 sendctl(msgfd, M_HANGUP); 777 noted(NCONT); 778 } 779 noted(NDFLT); 780 } 781 782 /* 783 * send an empty mesgld message 784 */ 785 int 786 sendctl(int net, int type) 787 { 788 Msg m; 789 790 m.h.type = type; 791 m.h.magic = MSGMAGIC; 792 put2byte(m.h.size, 0); 793 if(iwrite(net, &m, sizeof(struct mesg)) != sizeof(struct mesg)) 794 return -1; 795 return 0; 796 } 797 798 /* 799 * send a one byte mesgld message 800 */ 801 int 802 sendctl1(int net, int type, int parm) 803 { 804 Msg m; 805 806 m.h.type = type; 807 m.h.magic = MSGMAGIC; 808 m.b[0] = parm; 809 put2byte(m.h.size, 1); 810 if(iwrite(net, &m, sizeof(struct mesg)+1) != sizeof(struct mesg)+1) 811 return -1; 812 return 0; 813 } 814 815 /* 816 * read n bytes. return -1 if it fails, 0 otherwise. 817 */ 818 int 819 readupto(int from, char *a, int len) 820 { 821 int n; 822 823 while(len > 0){ 824 n = iread(from, a, len); 825 if(n < 0) 826 return -1; 827 a += n; 828 len -= n; 829 } 830 return 0; 831 } 832 833 /* 834 * Decode a mesgld message from the network 835 */ 836 void 837 msgfromnet(int net) 838 { 839 ulong com; 840 struct stioctl *io; 841 struct sgttyb *sg; 842 struct ttydevb *td; 843 struct tchars *tc; 844 int len; 845 Msg m; 846 847 for(;;){ 848 /* get a complete mesgld message */ 849 if(readupto(net, (char*)&m.h, sizeof(struct mesg)) < 0) 850 break; 851 if(m.h.magic != MSGMAGIC){ 852 fprint(2, "con: bad message magic 0x%ux\n", m.h.magic); 853 break; 854 } 855 len = get2byte(m.h.size); 856 if(len > sizeof(m.b)){ 857 len = sizeof(m.b); 858 fprint(2, "con: mesgld message too long\n"); 859 } 860 if(len && readupto(net, m.b, len) < 0) 861 break; 862 863 /* decode */ 864 switch(m.h.type){ 865 case M_HANGUP: 866 if(debug) 867 fprint(2, "M_HANGUP\n"); 868 return; 869 case M_DATA: 870 if(debug) 871 fprint(2, "M_DATA %d bytes\n", len); 872 if(iwrite(outfd, m.b, len) != len){ 873 if(outfd == 1) 874 return; 875 outfd = 1; 876 if(iwrite(outfd, m.b, len) != len) 877 return; 878 } 879 continue; 880 case M_IOCTL: 881 break; 882 default: 883 /* ignore */ 884 if(debug) 885 fprint(2, "con: unknown message\n"); 886 continue; 887 } 888 889 /* 890 * answer an ioctl 891 */ 892 io = (struct stioctl *)m.b; 893 com = get4byte(io->com); 894 if(debug) 895 fprint(2, "M_IOCTL %d\n", com); 896 switch(com){ 897 case FIOLOOKLD: 898 put4byte(io->data, tty_ld); 899 len = 0; 900 break; 901 case TIOCGETP: 902 sg = (struct sgttyb *)io->data; 903 sg->sg_ispeed = sg->sg_ospeed = B9600; 904 sg->sg_erase = 0010; /* back space */ 905 sg->sg_kill = 0025; /* CNTL U */ 906 put2byte(sg->sg_flags, sgflags); 907 len = sizeof(struct sgttyb); 908 break; 909 case TIOCSETN: 910 case TIOCSETP: 911 sg = (struct sgttyb *)io->data; 912 sgflags = get2byte(sg->sg_flags); 913 if((sgflags&(RAW|CBREAK)) || !(sgflags&ECHO)) 914 rawon(); 915 else 916 rawoff(); 917 len = 0; 918 break; 919 case TIOCGETC: 920 tc = (struct tchars *)io->data; 921 tc->t_intrc = 0177; 922 tc->t_quitc = 0034; 923 tc->t_startc = 0; 924 tc->t_stopc = 0; 925 tc->t_eofc = 0004; 926 tc->t_brkc = 0; 927 len = sizeof(struct tchars); 928 break; 929 case TIOCSETC: 930 len = 0; 931 break; 932 case TIOCGDEV: 933 td = (struct ttydevb *)io->data; 934 td->ispeed = td->ospeed = B9600; 935 put2byte(td->flags, 0); 936 len = sizeof(struct ttydevb); 937 break; 938 case TIOCSDEV: 939 len = 0; 940 break; 941 default: 942 /* 943 * unimplemented 944 */ 945 m.b[len] = 0; 946 if(sendctl(net, M_IOCNAK) < 0) 947 return; 948 continue; 949 } 950 951 /* 952 * acknowledge 953 */ 954 m.h.type = M_IOCACK; 955 m.h.magic = MSGMAGIC; 956 len += 4; 957 put2byte(m.h.size, len); 958 len += sizeof(struct mesg); 959 if(iwrite(net, &m, len) != len) 960 return; 961 } 962 } 963 964 /* 965 * Read the keyboard, convert to mesgld messages, and write it to the network. 966 * '^\' gets us into the menu. 967 */ 968 void 969 msgfromkbd(int net) 970 { 971 long n; 972 char buf[MAXMSG]; 973 974 for(;;){ 975 n = iread(0, buf, sizeof(buf)); 976 if(n < 0) 977 return; 978 if(n && memchr(buf, 0034, n)){ 979 if(menu(net) < 0) 980 return; 981 } else { 982 if(msgwrite(net, buf, n) != n) 983 return; 984 } 985 } 986 } 987 988 int 989 msgwrite(int fd, void *buf, int len) 990 { 991 Msg m; 992 int n; 993 994 n = len; 995 memmove(m.b, buf, n); 996 put2byte(m.h.size, n); 997 m.h.magic = MSGMAGIC; 998 m.h.type = M_DATA; 999 n += sizeof(struct mesg); 1000 if(iwrite(fd, &m, n) != n) 1001 return -1; 1002 1003 put2byte(m.h.size, 0); 1004 m.h.magic = MSGMAGIC; 1005 m.h.type = M_DELIM; 1006 n = sizeof(struct mesg); 1007 if(iwrite(fd, &m, n) != n) 1008 return -1; 1009 1010 return len; 1011 } 1012 1013