1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1991, 1999 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 /* 27 * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 28 */ 29 30 #include <sys/types.h> 31 #include <sys/errno.h> 32 #include <setjmp.h> 33 #include <sys/tiuser.h> 34 #include <string.h> 35 36 #include <rpc/types.h> 37 #include <rpc/xdr.h> 38 #include <rpc/auth.h> 39 #include <rpc/clnt.h> 40 #include <rpc/rpc_msg.h> 41 #include <rpc/pmap_prot.h> 42 #include "snoop.h" 43 44 /* 45 * Number of bytes to display from a string (address, netid, etc.). 46 */ 47 #define MAXSTRINGLEN 64 48 49 extern char *dlc_header; 50 extern jmp_buf xdr_err; 51 52 static void interpret_pmap_2(int, int, int, int, int, char *, int); 53 static void interpret_pmap_4(int, int, int, int, int, char *, int); 54 static void stash_callit(ulong_t, int, int, int, int); 55 56 void 57 interpret_pmap(flags, type, xid, vers, proc, data, len) 58 int flags, type, xid, vers, proc; 59 char *data; 60 int len; 61 { 62 switch (vers) { 63 case 2: interpret_pmap_2(flags, type, xid, vers, proc, data, len); 64 break; 65 66 /* Version 3 is a subset of version 4 */ 67 case 3: 68 case 4: interpret_pmap_4(flags, type, xid, vers, proc, data, len); 69 break; 70 } 71 } 72 73 void show_pmap(); 74 char *sum_pmaplist(); 75 void show_pmaplist(); 76 77 static char *procnames_short_2[] = { 78 "Null", /* 0 */ 79 "SET", /* 1 */ 80 "UNSET", /* 2 */ 81 "GETPORT", /* 3 */ 82 "DUMP", /* 4 */ 83 "CALLIT", /* 5 */ 84 }; 85 86 static char *procnames_long_2[] = { 87 "Null procedure", /* 0 */ 88 "Set port", /* 1 */ 89 "Unset port", /* 2 */ 90 "Get port number", /* 3 */ 91 "Dump the mappings", /* 4 */ 92 "Indirect call", /* 5 */ 93 }; 94 95 #define MAXPROC_2 5 96 97 void 98 interpret_pmap_2(flags, type, xid, vers, proc, data, len) 99 int flags, type, xid, vers, proc; 100 char *data; 101 int len; 102 { 103 char *line; 104 unsigned port, proto; 105 unsigned iprog, ivers, iproc, ilen; 106 extern int pi_frame; 107 struct cache_struct *x, *find_callit(); 108 int trailer_done = 0; 109 110 if (proc < 0 || proc > MAXPROC_2) 111 return; 112 113 if (proc == PMAPPROC_CALLIT) { 114 if (type == CALL) { 115 iprog = getxdr_u_long(); 116 ivers = getxdr_u_long(); 117 iproc = getxdr_u_long(); 118 stash_callit(xid, pi_frame, iprog, ivers, iproc); 119 } else { 120 x = find_callit(xid); 121 } 122 } 123 124 if (flags & F_SUM) { 125 if (setjmp(xdr_err)) { 126 return; 127 } 128 129 line = get_sum_line(); 130 131 if (type == CALL) { 132 (void) sprintf(line, "PORTMAP C %s", 133 procnames_short_2[proc]); 134 line += strlen(line); 135 switch (proc) { 136 case PMAPPROC_GETPORT: 137 iprog = getxdr_u_long(); 138 ivers = getxdr_u_long(); 139 proto = getxdr_u_long(); 140 (void) sprintf(line, 141 " prog=%d (%s) vers=%d proto=%s", 142 iprog, nameof_prog(iprog), 143 ivers, 144 getproto(proto)); 145 break; 146 case PMAPPROC_CALLIT: 147 (void) sprintf(line, 148 " prog=%s vers=%d proc=%d", 149 nameof_prog(iprog), 150 ivers, iproc); 151 if (flags & F_ALLSUM) { 152 (void) getxdr_u_long(); /* length */ 153 data += 16; /* prog+ver+proc+len */ 154 len -= 16; 155 protoprint(flags, type, xid, 156 iprog, ivers, iproc, 157 data, len); 158 } 159 break; 160 default: 161 break; 162 } 163 check_retransmit(line, xid); 164 } else { 165 (void) sprintf(line, "PORTMAP R %s ", 166 procnames_short_2[proc]); 167 line += strlen(line); 168 switch (proc) { 169 case PMAPPROC_GETPORT: 170 port = getxdr_u_long(); 171 (void) sprintf(line, "port=%d", port); 172 break; 173 case PMAPPROC_DUMP: 174 (void) sprintf(line, "%s", sum_pmaplist()); 175 break; 176 case PMAPPROC_CALLIT: 177 port = getxdr_u_long(); 178 ilen = getxdr_u_long(); 179 (void) sprintf(line, "port=%d len=%d", 180 port, ilen); 181 if (flags & F_ALLSUM && x != NULL) { 182 data += 8; /* port+len */ 183 len -= 8; 184 protoprint(flags, type, xid, 185 x->xid_prog, 186 x->xid_vers, 187 x->xid_proc, 188 data, len); 189 } 190 break; 191 default: 192 break; 193 } 194 } 195 } 196 197 if (flags & F_DTAIL) { 198 show_header("PMAP: ", "Portmapper", len); 199 show_space(); 200 if (setjmp(xdr_err)) { 201 return; 202 } 203 (void) sprintf(get_line(0, 0), 204 "Proc = %d (%s)", 205 proc, procnames_long_2[proc]); 206 if (type == CALL) { 207 switch (proc) { 208 case PMAPPROC_NULL: 209 case PMAPPROC_SET: 210 case PMAPPROC_UNSET: 211 break; 212 case PMAPPROC_GETPORT: 213 iprog = getxdr_u_long(); 214 (void) sprintf(get_line(0, 0), 215 "Program = %d (%s)", 216 iprog, nameof_prog(iprog)); 217 (void) showxdr_u_long("Version = %d"); 218 proto = getxdr_u_long(); 219 (void) sprintf(get_line(0, 0), 220 "Protocol = %d (%s)", 221 proto, getproto(proto)); 222 break; 223 case PMAPPROC_DUMP: 224 break; 225 case PMAPPROC_CALLIT: 226 (void) sprintf(get_line(0, 0), 227 "Program = %d (%s)", 228 iprog, nameof_prog(iprog)); 229 (void) sprintf(get_line(0, 0), 230 "Version = %d", ivers); 231 (void) sprintf(get_line(0, 0), 232 "Proc = %d", iproc); 233 (void) showxdr_u_long("Callit data = %d bytes"); 234 show_trailer(); 235 trailer_done = 1; 236 data += 16; /* prog+ver+proc+len */ 237 len -= 16; 238 protoprint(flags, type, xid, 239 iprog, ivers, iproc, 240 data, len); 241 break; 242 } 243 } else { 244 switch (proc) { 245 case PMAPPROC_NULL: 246 case PMAPPROC_SET: 247 case PMAPPROC_UNSET: 248 break; 249 case PMAPPROC_GETPORT: 250 (void) showxdr_u_long("Port = %d"); 251 break; 252 case PMAPPROC_DUMP: 253 show_pmaplist(); 254 break; 255 case PMAPPROC_CALLIT: 256 (void) showxdr_u_long("Port = %d"); 257 (void) showxdr_u_long("Length = %d bytes"); 258 show_trailer(); 259 trailer_done = 1; 260 if (x != NULL) { 261 protoprint(flags, type, xid, 262 x->xid_prog, 263 x->xid_vers, 264 x->xid_proc, 265 data, len); 266 } 267 break; 268 } 269 } 270 if (!trailer_done) 271 show_trailer(); 272 } 273 } 274 275 char * 276 sum_pmaplist() 277 { 278 int maps = 0; 279 static char buff[16]; 280 281 if (setjmp(xdr_err)) { 282 (void) sprintf(buff, "%d+ map(s) found", maps); 283 return (buff); 284 } 285 286 while (getxdr_u_long()) { 287 (void) getxdr_u_long(); /* program */ 288 (void) getxdr_u_long(); /* version */ 289 (void) getxdr_u_long(); /* protocol */ 290 (void) getxdr_u_long(); /* port */ 291 maps++; 292 } 293 294 (void) sprintf(buff, "%d map(s) found", maps); 295 return (buff); 296 } 297 298 void 299 show_pmaplist() 300 { 301 unsigned prog, vers, proto, port; 302 int maps = 0; 303 304 if (setjmp(xdr_err)) { 305 (void) sprintf(get_line(0, 0), 306 " %d+ maps. (Frame is incomplete)", 307 maps); 308 return; 309 } 310 311 (void) sprintf(get_line(0, 0), " Program Version Protocol Port"); 312 313 while (getxdr_u_long()) { 314 prog = getxdr_u_long(); 315 vers = getxdr_u_long(); 316 proto = getxdr_u_long(); 317 port = getxdr_u_long(); 318 (void) sprintf(get_line(0, 0), 319 "%8d%8d%9d%7d %s", 320 prog, vers, proto, port, nameof_prog(prog)); 321 maps++; 322 } 323 324 (void) sprintf(get_line(0, 0), " %d maps", maps); 325 } 326 327 /* 328 * ****************************************** 329 */ 330 char *sum_rpcblist(); 331 void show_rpcblist(); 332 char *sum_rpcb_entry_list(); 333 void show_rpcb_entry_list(); 334 335 static char *procnames_short_4[] = { 336 /* 337 * version 3 and 4 procs 338 */ 339 "Null", /* 0 */ 340 "SET", /* 1 */ 341 "UNSET", /* 2 */ 342 "GETADDR", /* 3 */ 343 "DUMP", /* 4 */ 344 "BCAST", /* 5 */ 345 "GETTIME", /* 6 */ 346 "UADDR2TADDR", /* 7 */ 347 "TADDR2UADDR", /* 8 */ 348 /* 349 * version 4 procs only 350 */ 351 "GETVERSADDR", /* 9 */ 352 "INDIRECT", /* 10 */ 353 "GETADDRLIST", /* 11 */ 354 "GETSTAT", /* 12 */ 355 }; 356 357 static char *procnames_long_4[] = { 358 /* 359 * version 3 and 4 procs 360 */ 361 "Null procedure", /* 0 */ 362 "Set address", /* 1 */ 363 "Unset address", /* 2 */ 364 "Get address", /* 3 */ 365 "Dump the mappings", /* 4 */ 366 "Broadcast call (no error)", /* 5 */ 367 "Get the time", /* 6 */ 368 "Universal to transport address", /* 7 */ 369 "Transport to universal address", /* 8 */ 370 /* 371 * version 4 procs only 372 */ 373 "Get address of specific version", /* 9 */ 374 "Indirect call (return error)", /* 10 */ 375 "Return addresses of prog/vers", /* 11 */ 376 "Get statistics", /* 12 */ 377 }; 378 379 #define MAXPROC_3 8 380 #define MAXPROC_4 12 381 #define RPCBPROC_NULL 0 382 383 void 384 interpret_pmap_4(flags, type, xid, vers, proc, data, len) 385 int flags, type, xid, vers, proc; 386 char *data; 387 int len; 388 { 389 char *line; 390 unsigned prog, ver; 391 char buff1[MAXSTRINGLEN + 1]; 392 int iprog, ivers, iproc, ilen; 393 extern int pi_frame; 394 struct cache_struct *x, *find_callit(); 395 int trailer_done = 0; 396 397 if (proc < 0 || proc > MAXPROC_4 || (vers == 3 && proc > MAXPROC_3)) 398 return; 399 400 if (proc == RPCBPROC_BCAST || proc == RPCBPROC_INDIRECT) { 401 if (type == CALL) { 402 iprog = getxdr_u_long(); 403 ivers = getxdr_u_long(); 404 iproc = getxdr_u_long(); 405 stash_callit(xid, pi_frame, 406 iprog, ivers, iproc); 407 } else { 408 x = find_callit(xid); 409 } 410 } 411 412 if (flags & F_SUM) { 413 if (setjmp(xdr_err)) { 414 return; 415 } 416 417 line = get_sum_line(); 418 419 if (type == CALL) { 420 (void) sprintf(line, 421 "RPCBIND C %s", 422 procnames_short_4[proc]); 423 line += strlen(line); 424 switch (proc) { 425 case RPCBPROC_SET: 426 case RPCBPROC_UNSET: 427 case RPCBPROC_GETADDR: 428 case RPCBPROC_GETVERSADDR: 429 case RPCBPROC_GETADDRLIST: 430 prog = getxdr_u_long(); 431 ver = getxdr_u_long(); 432 (void) sprintf(line, 433 " prog=%d (%s) vers=%d", 434 prog, nameof_prog(prog), 435 ver); 436 break; 437 case RPCBPROC_BCAST: 438 case RPCBPROC_INDIRECT: 439 (void) sprintf(line, 440 " prog=%s vers=%d proc=%d", 441 nameof_prog(iprog), 442 ivers, iproc); 443 if (flags & F_ALLSUM) { 444 (void) getxdr_u_long(); /* length */ 445 data += 16; /* prog+ver+proc+len */ 446 len -= 16; 447 protoprint(flags, type, xid, 448 iprog, ivers, iproc, 449 data, len); 450 } 451 break; 452 default: 453 break; 454 } 455 456 check_retransmit(line, xid); 457 } else { 458 int pos; 459 460 (void) sprintf(line, "RPCBIND R %s ", 461 procnames_short_4[proc]); 462 line += strlen(line); 463 switch (proc) { 464 case RPCBPROC_GETADDR: 465 case RPCBPROC_TADDR2UADDR: 466 case RPCBPROC_GETVERSADDR: 467 (void) getxdr_string(buff1, MAXSTRINGLEN); 468 (void) sprintf(line, 469 " Uaddr=%s", 470 buff1); 471 break; 472 case RPCBPROC_BCAST: 473 case RPCBPROC_INDIRECT: 474 pos = getxdr_pos(); 475 (void) getxdr_string(buff1, MAXSTRINGLEN); 476 ilen = getxdr_u_long(); 477 (void) sprintf(line, "Uaddr=%s len=%d", 478 buff1, ilen); 479 if (flags & F_ALLSUM && x != NULL) { 480 pos = getxdr_pos() - pos; 481 data += pos; /* uaddr+len */ 482 len -= pos; 483 protoprint(flags, type, xid, 484 x->xid_prog, 485 x->xid_vers, 486 x->xid_proc, 487 data, len); 488 } 489 break; 490 case RPCBPROC_DUMP: 491 (void) sprintf(line, "%s", 492 sum_rpcblist()); 493 break; 494 case RPCBPROC_GETTIME: 495 { 496 time_t sec = getxdr_long(); 497 struct tm *tmp = gmtime(&sec); 498 (void) strftime(line, MAXLINE, 499 "%d-%h-%y %T GMT", tmp); 500 } 501 break; 502 case RPCBPROC_GETADDRLIST: 503 (void) sprintf(line, "%s", 504 sum_rpcb_entry_list()); 505 break; 506 default: 507 break; 508 } 509 } 510 } 511 512 if (flags & F_DTAIL) { 513 show_header("RPCB: ", "RPC Bind", len); 514 show_space(); 515 if (setjmp(xdr_err)) { 516 return; 517 } 518 (void) sprintf(get_line(0, 0), 519 "Proc = %d (%s)", 520 proc, procnames_long_4[proc]); 521 if (type == CALL) { 522 switch (proc) { 523 case RPCBPROC_NULL: 524 break; 525 case RPCBPROC_SET: 526 case RPCBPROC_UNSET: 527 case RPCBPROC_GETADDR: 528 case RPCBPROC_GETVERSADDR: 529 case RPCBPROC_GETADDRLIST: 530 (void) showxdr_u_long("Program = %d"); 531 (void) showxdr_u_long("Version = %d"); 532 (void) showxdr_string(64, "Netid = %s"); 533 break; 534 case RPCBPROC_DUMP: 535 break; 536 case RPCBPROC_BCAST: 537 case RPCBPROC_INDIRECT: 538 (void) sprintf(get_line(0, 0), 539 "Program = %d (%s)", 540 iprog, nameof_prog(iprog)); 541 (void) sprintf(get_line(0, 0), 542 "Version = %d", ivers); 543 (void) sprintf(get_line(0, 0), 544 "Proc = %d", iproc); 545 (void) showxdr_u_long( 546 "Callit data = %d bytes"); 547 show_trailer(); 548 trailer_done = 1; 549 data += 16; /* prog+ver+proc+len */ 550 len -= 16; 551 protoprint(flags, type, xid, 552 iprog, ivers, iproc, 553 data, len); 554 break; 555 case RPCBPROC_GETTIME: 556 break; 557 case RPCBPROC_UADDR2TADDR: 558 case RPCBPROC_TADDR2UADDR: 559 break; 560 } 561 } else { 562 switch (proc) { 563 case RPCBPROC_NULL: 564 case RPCBPROC_SET: 565 case RPCBPROC_UNSET: 566 break; 567 case RPCBPROC_GETADDR: 568 case RPCBPROC_TADDR2UADDR: 569 case RPCBPROC_GETVERSADDR: 570 (void) showxdr_string(64, "Uaddr = %s"); 571 break; 572 case RPCBPROC_DUMP: 573 show_rpcblist(); 574 break; 575 case RPCBPROC_BCAST: 576 case RPCBPROC_INDIRECT: 577 (void) showxdr_string(64, "Uaddr = %s"); 578 (void) showxdr_u_long("Length = %d bytes"); 579 show_trailer(); 580 trailer_done = 1; 581 if (x != NULL) { 582 protoprint(flags, type, xid, 583 x->xid_prog, 584 x->xid_vers, 585 x->xid_proc, 586 data, len); 587 } 588 break; 589 case RPCBPROC_GETTIME: 590 { 591 int pos = getxdr_pos(); 592 time_t sec = getxdr_long(); 593 struct tm *tmp = gmtime(&sec); 594 (void) strftime(get_line(pos, 595 getxdr_pos()), MAXLINE, 596 "Time = %d-%h-%y %T GMT", tmp); 597 } 598 break; 599 case RPCBPROC_UADDR2TADDR: 600 break; 601 case RPCBPROC_GETADDRLIST: 602 show_rpcb_entry_list(); 603 break; 604 } 605 } 606 if (!trailer_done) 607 show_trailer(); 608 } 609 } 610 611 char * 612 sum_rpcblist() 613 { 614 int maps = 0; 615 static char buff[MAXSTRINGLEN + 1]; 616 617 if (setjmp(xdr_err)) { 618 (void) sprintf(buff, "%d+ map(s) found", maps); 619 return (buff); 620 } 621 622 while (getxdr_u_long()) { 623 (void) getxdr_u_long(); /* program */ 624 (void) getxdr_u_long(); /* version */ 625 (void) getxdr_string(buff, MAXSTRINGLEN); /* netid */ 626 (void) getxdr_string(buff, MAXSTRINGLEN); /* uaddr */ 627 (void) getxdr_string(buff, MAXSTRINGLEN); /* owner */ 628 maps++; 629 } 630 631 (void) sprintf(buff, "%d map(s) found", maps); 632 return (buff); 633 } 634 635 void 636 show_rpcblist() 637 { 638 unsigned prog, vers; 639 char netid[MAXSTRINGLEN + 1], uaddr[MAXSTRINGLEN + 1]; 640 char owner[MAXSTRINGLEN + 1]; 641 int maps = 0; 642 643 if (setjmp(xdr_err)) { 644 (void) sprintf(get_line(0, 0), 645 " %d+ maps. (Frame is incomplete)", 646 maps); 647 return; 648 } 649 650 show_space(); 651 (void) sprintf(get_line(0, 0), 652 " Program Vers Netid Uaddr Owner"); 653 654 while (getxdr_u_long()) { 655 prog = getxdr_u_long(); 656 vers = getxdr_u_long(); 657 (void) getxdr_string(netid, MAXSTRINGLEN); 658 (void) getxdr_string(uaddr, MAXSTRINGLEN); 659 (void) getxdr_string(owner, MAXSTRINGLEN); 660 (void) sprintf(get_line(0, 0), 661 "%8d%5d %-12s %-18s %-10s (%s)", 662 prog, vers, 663 netid, uaddr, owner, 664 nameof_prog(prog)); 665 maps++; 666 } 667 668 (void) sprintf(get_line(0, 0), " (%d maps)", maps); 669 } 670 671 char * 672 sum_rpcb_entry_list() 673 { 674 int maps = 0; 675 static char buff[MAXSTRINGLEN + 1]; 676 677 if (setjmp(xdr_err)) { 678 (void) sprintf(buff, "%d+ map(s) found", maps); 679 return (buff); 680 } 681 682 while (getxdr_u_long()) { 683 (void) getxdr_string(buff, MAXSTRINGLEN); /* maddr */ 684 (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_netid */ 685 (void) getxdr_u_long(); /* nc_semantics */ 686 (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_protofmly */ 687 (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_proto */ 688 maps++; 689 } 690 691 (void) sprintf(buff, "%d map(s) found", maps); 692 return (buff); 693 } 694 695 char *semantics_strs[] = {"", "CLTS", "COTS", "COTS-ORD", "RAW"}; 696 697 void 698 show_rpcb_entry_list() 699 { 700 char maddr[MAXSTRINGLEN + 1], netid[MAXSTRINGLEN + 1]; 701 char protofmly[MAXSTRINGLEN + 1], proto[MAXSTRINGLEN + 1]; 702 unsigned sem; 703 int maps = 0; 704 705 if (setjmp(xdr_err)) { 706 (void) sprintf(get_line(0, 0), 707 " %d+ maps. (Frame is incomplete)", 708 maps); 709 return; 710 } 711 712 show_space(); 713 (void) sprintf(get_line(0, 0), 714 " Maddr Netid Semantics Protofmly Proto"); 715 716 while (getxdr_u_long()) { 717 (void) getxdr_string(maddr, MAXSTRINGLEN); 718 (void) getxdr_string(netid, MAXSTRINGLEN); 719 sem = getxdr_u_long(); 720 (void) getxdr_string(protofmly, MAXSTRINGLEN); 721 (void) getxdr_string(proto, MAXSTRINGLEN); 722 (void) sprintf(get_line(0, 0), 723 "%-12s %-12s %-8s %-8s %-8s", 724 maddr, netid, 725 semantics_strs[sem], 726 protofmly, proto); 727 maps++; 728 } 729 730 (void) sprintf(get_line(0, 0), " (%d maps)", maps); 731 } 732 733 #define CXID_CACHE_SIZE 16 734 struct cache_struct cxid_cache[CXID_CACHE_SIZE]; 735 struct cache_struct *cxcpfirst = &cxid_cache[0]; 736 struct cache_struct *cxcp = &cxid_cache[0]; 737 struct cache_struct *cxcplast = &cxid_cache[CXID_CACHE_SIZE - 1]; 738 739 struct cache_struct * 740 find_callit(xid) 741 ulong_t xid; 742 { 743 struct cache_struct *x; 744 745 for (x = cxcp; x >= cxcpfirst; x--) 746 if (x->xid_num == xid) 747 return (x); 748 for (x = cxcplast; x > cxcp; x--) 749 if (x->xid_num == xid) 750 return (x); 751 return (NULL); 752 } 753 754 static void 755 stash_callit(xid, frame, prog, vers, proc) 756 ulong_t xid; 757 int frame, prog, vers, proc; 758 { 759 struct cache_struct *x; 760 761 x = find_callit(xid); 762 if (x == NULL) { 763 x = cxcp++; 764 if (cxcp > cxcplast) 765 cxcp = cxcpfirst; 766 x->xid_num = xid; 767 x->xid_frame = frame; 768 } 769 x->xid_prog = prog; 770 x->xid_vers = vers; 771 x->xid_proc = proc; 772 } 773