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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  *
  26  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  27  */
  28 
  29 
  30 #include <sys/mdb_modapi.h>
  31 #include <mdb/mdb_ctf.h>
  32 #include <sys/types.h>
  33 #include <sys/socket.h>
  34 
  35 #include <netsmb/smb_conn.h>
  36 #include <netsmb/smb_rq.h>
  37 #include <netsmb/smb_pass.h>
  38 
  39 #ifdef _KERNEL
  40 #define NSMB_OBJNAME    "nsmb"
  41 #else
  42 #define NSMB_OBJNAME    "libfknsmb.so.1"
  43 #endif
  44 
  45 #define OPT_VERBOSE     0x0001  /* Be [-v]erbose in dcmd's */
  46 #define OPT_RECURSE     0x0002  /* recursive display */
  47 
  48 /*
  49  * We need to read in a private copy
  50  * of every string we want to print out.
  51  */
  52 void
  53 print_str(uintptr_t addr)
  54 {
  55         char buf[32];
  56         int len, mx = sizeof (buf) - 4;
  57 
  58         if ((len = mdb_readstr(buf, sizeof (buf), addr)) <= 0) {
  59                 mdb_printf(" (%p)", addr);
  60         } else {
  61                 if (len > mx)
  62                         strcpy(&buf[mx], "...");
  63                 mdb_printf(" %s", buf);
  64         }
  65 }
  66 
  67 
  68 /*
  69  * Walker for smb_connobj_t structures, including
  70  * smb_vc_t and smb_share_t which "inherit" from it.
  71  * Tricky: Exploit the "inheritance" of smb_connobj_t
  72  * with common functions for walk_init, walk_next.
  73  */
  74 typedef struct smb_co_walk_data {
  75         uintptr_t       pp;
  76         int level;              /* SMBL_SM, SMBL_VC, SMBL_SHARE, ...  */
  77         int size;               /* sizeof (union member) */
  78         union co_u {
  79                 smb_connobj_t   co;     /* copy of the list element */
  80                 smb_vc_t        vc;
  81                 smb_share_t     ss;
  82                 smb_fh_t        fh;
  83         } u;
  84 } smb_co_walk_data_t;
  85 
  86 /*
  87  * Common walk_init for walking structs inherited
  88  * from smb_connobj_t (smb_vc_t, smb_share_t)
  89  */
  90 int
  91 smb_co_walk_init(mdb_walk_state_t *wsp, int level)
  92 {
  93         smb_co_walk_data_t *smbw;
  94         size_t psz;
  95 
  96         if (wsp->walk_addr == NULL)
  97                 return (WALK_ERR);
  98 
  99         smbw = mdb_alloc(sizeof (*smbw), UM_SLEEP | UM_GC);
 100         wsp->walk_data = smbw;
 101 
 102         /*
 103          * Save the parent pointer for later checks, and
 104          * the level so we know which union member it is.
 105          * Also the size of this union member.
 106          */
 107         smbw->pp = wsp->walk_addr;
 108         smbw->level = level;
 109         switch (level) {
 110         case SMBL_SM:
 111                 smbw->size = sizeof (smbw->u.co);
 112                 break;
 113         case SMBL_VC:
 114                 smbw->size = sizeof (smbw->u.vc);
 115                 break;
 116         case SMBL_SHARE:
 117                 smbw->size = sizeof (smbw->u.ss);
 118                 break;
 119         case SMBL_FH:
 120                 smbw->size = sizeof (smbw->u.fh);
 121                 break;
 122         default:
 123                 smbw->size = sizeof (smbw->u);
 124                 break;
 125         }
 126 
 127         /*
 128          * Read in the parent object.  Just need the
 129          * invariant part (smb_connobj_t) so we can
 130          * get the list of children below it.
 131          */
 132         psz = sizeof (smbw->u.co);
 133         if (mdb_vread(&smbw->u.co, psz, smbw->pp) != psz) {
 134                 mdb_warn("cannot read connobj from %p", smbw->pp);
 135                 return (WALK_ERR);
 136         }
 137 
 138         /*
 139          * Finally, setup to walk the list of children.
 140          */
 141         wsp->walk_addr = (uintptr_t)smbw->u.co.co_children.slh_first;
 142 
 143         return (WALK_NEXT);
 144 }
 145 
 146 /*
 147  * Walk the (global) VC list.
 148  */
 149 int
 150 smb_vc_walk_init(mdb_walk_state_t *wsp)
 151 {
 152         GElf_Sym sym;
 153 
 154         if (wsp->walk_addr != NULL) {
 155                 mdb_warn("::walk smb_vc only supports global walks\n");
 156                 return (WALK_ERR);
 157         }
 158 
 159         /* Locate the VC list head. */
 160         if (mdb_lookup_by_obj(NSMB_OBJNAME, "smb_vclist", &sym)) {
 161                 mdb_warn("failed to lookup `smb_vclist'\n");
 162                 return (WALK_ERR);
 163         }
 164         wsp->walk_addr = sym.st_value;
 165 
 166         return (smb_co_walk_init(wsp, SMBL_VC));
 167 }
 168 
 169 /*
 170  * Walk the share list below some VC.
 171  */
 172 int
 173 smb_ss_walk_init(mdb_walk_state_t *wsp)
 174 {
 175 
 176         /*
 177          * Initial walk_addr is address of parent (VC)
 178          */
 179         if (wsp->walk_addr == 0) {
 180                 mdb_warn("::walk smb_ss does not support global walks\n");
 181                 return (WALK_ERR);
 182         }
 183 
 184         return (smb_co_walk_init(wsp, SMBL_SHARE));
 185 }
 186 
 187 /*
 188  * Walk the file hande list below some share.
 189  */
 190 int
 191 smb_fh_walk_init(mdb_walk_state_t *wsp)
 192 {
 193 
 194         /*
 195          * Initial walk_addr is address of parent (share)
 196          */
 197         if (wsp->walk_addr == 0) {
 198                 mdb_warn("::walk smb_fh does not support global walks\n");
 199                 return (WALK_ERR);
 200         }
 201 
 202         return (smb_co_walk_init(wsp, SMBL_FH));
 203 }
 204 
 205 /*
 206  * Common walk_step for walking structs inherited
 207  * from smb_connobj_t (smb_vc_t, smb_share_t)
 208  */
 209 int
 210 smb_co_walk_step(mdb_walk_state_t *wsp)
 211 {
 212         smb_co_walk_data_t *smbw = wsp->walk_data;
 213         int status;
 214 
 215         if (wsp->walk_addr == NULL)
 216                 return (WALK_DONE);
 217 
 218         if (mdb_vread(&smbw->u, smbw->size, wsp->walk_addr)
 219             != smbw->size) {
 220                 mdb_warn("cannot read connobj from %p", wsp->walk_addr);
 221                 return (WALK_ERR);
 222         }
 223 
 224         /* XXX: Sanity check level? parent pointer? */
 225 
 226         status = wsp->walk_callback(wsp->walk_addr, &smbw->u,
 227             wsp->walk_cbdata);
 228 
 229         wsp->walk_addr = (uintptr_t)smbw->u.co.co_next.sle_next;
 230 
 231         return (status);
 232 }
 233 
 234 
 235 /*
 236  * Dcmd (and callback function) to print a summary of
 237  * all VCs, and optionally all shares under each VC.
 238  */
 239 
 240 typedef struct smb_co_cbdata {
 241         int flags;              /* OPT_...  */
 242         int printed_header;
 243         mdb_ctf_id_t ctf_id;
 244 } smb_co_cbdata_t;
 245 
 246 /*
 247  * Call-back function for walking a file handle list.
 248  */
 249 /* ARGSUSED */
 250 int
 251 smb_fh_cb(uintptr_t addr, const void *data, void *arg)
 252 {
 253         const smb_fh_t *fhp = data;
 254         // smb_co_cbdata_t *cbd = arg;
 255 
 256         mdb_inc_indent(2);
 257         mdb_printf(" %-p", addr);
 258         if (fhp->fh_fid2.fid_volatile != 0) {
 259                 mdb_printf("\t0x%llx\n",
 260                     (long long) fhp->fh_fid2.fid_volatile);
 261         } else {
 262                 mdb_printf("\t0x%x\n", fhp->fh_fid1);
 263         }
 264 
 265         mdb_dec_indent(2);
 266 
 267         return (WALK_NEXT);
 268 }
 269 
 270 /*
 271  * Call-back function for walking a share list.
 272  */
 273 int
 274 smb_ss_cb(uintptr_t addr, const void *data, void *arg)
 275 {
 276         const smb_share_t *ssp = data;
 277         smb_co_cbdata_t *cbd = arg;
 278         uint32_t tid;
 279 
 280         tid = ssp->ss2_tree_id;
 281         if (tid == 0)
 282                 tid = ssp->ss_tid;
 283 
 284         mdb_printf(" %-p\t0x%x\t%s\n", addr, tid, ssp->ss_name);
 285 
 286         if (cbd->flags & OPT_RECURSE) {
 287                 mdb_inc_indent(2);
 288                 if (mdb_pwalk("nsmb_fh", smb_fh_cb, cbd, addr) < 0) {
 289                         mdb_warn("failed to walk 'nsmb_fh'");
 290                         /* Don't: return (WALK_ERR); */
 291                 }
 292                 mdb_dec_indent(2);
 293         }
 294 
 295         return (WALK_NEXT);
 296 }
 297 
 298 static const char *
 299 vcstate_str(smb_co_cbdata_t *cbd, int stval)
 300 {
 301         static const char prefix[] = "SMBIOD_ST_";
 302         int prefix_len = sizeof (prefix) - 1;
 303         mdb_ctf_id_t vcst_enum;
 304         const char *cp;
 305 
 306         /* Got this in smb_vc_dcmd. */
 307         vcst_enum = cbd->ctf_id;
 308 
 309         /* Get the name for the enum value. */
 310         if ((cp = mdb_ctf_enum_name(vcst_enum, stval)) == NULL)
 311                 return ("?");
 312 
 313         /* Skip the prefix part. */
 314         if (strncmp(cp, prefix, prefix_len) == 0)
 315                 cp += prefix_len;
 316 
 317         return (cp);
 318 }
 319 
 320 /*
 321  * Call-back function for walking the VC list.
 322  */
 323 int
 324 smb_vc_cb(uintptr_t addr, const void *data, void *arg)
 325 {
 326         const smb_vc_t *vcp = data;
 327         smb_co_cbdata_t *cbd = arg;
 328 
 329         if (cbd->printed_header == 0) {
 330                 cbd->printed_header = 1;
 331                 mdb_printf("// smb_vc_t  uid  server  \tuser\t\tstate\n");
 332         }
 333 
 334         mdb_printf("%-p", addr);
 335         mdb_printf(" %7d", vcp->vc_owner);
 336 
 337         switch (vcp->vc_srvaddr.sa.sa_family) {
 338         case AF_INET:
 339                 mdb_printf(" %I", vcp->vc_srvaddr.sin.sin_addr);
 340                 break;
 341         case AF_INET6:
 342                 mdb_printf(" %N", &vcp->vc_srvaddr.sin6.sin6_addr);
 343                 break;
 344         default:
 345                 mdb_printf(" %15s", "(bad af)");
 346                 break;
 347         }
 348 
 349         if (vcp->vc_username[0] != '\0')
 350                 mdb_printf("\t%s", vcp->vc_username);
 351         else
 352                 mdb_printf("\t%s", "(?)");
 353 
 354         if (vcp->vc_domain[0] != '\0')
 355                 mdb_printf("@%s", vcp->vc_domain);
 356 
 357         mdb_printf("\t%s\n", vcstate_str(cbd, vcp->vc_state));
 358 
 359         if (cbd->flags & OPT_RECURSE) {
 360                 mdb_inc_indent(2);
 361                 if (mdb_pwalk("nsmb_ss", smb_ss_cb, cbd, addr) < 0) {
 362                         mdb_warn("failed to walk 'nsmb_ss'");
 363                         /* Don't: return (WALK_ERR); */
 364                 }
 365                 mdb_dec_indent(2);
 366         }
 367 
 368         return (WALK_NEXT);
 369 }
 370 
 371 int
 372 smb_vc_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 373 {
 374         smb_co_cbdata_t cbd;
 375         smb_vc_t *vcp;
 376         size_t vcsz;
 377 
 378         memset(&cbd, 0, sizeof (cbd));
 379 
 380         if (mdb_getopts(argc, argv,
 381             'r', MDB_OPT_SETBITS, OPT_RECURSE, &cbd.flags,
 382             'v', MDB_OPT_SETBITS, OPT_VERBOSE, &cbd.flags,
 383             NULL) != argc) {
 384                 return (DCMD_USAGE);
 385         }
 386 
 387         if (mdb_ctf_lookup_by_name("enum smbiod_state", &cbd.ctf_id) == -1) {
 388                 mdb_warn("Could not find enum smbiod_state");
 389         }
 390 
 391         if (!(flags & DCMD_ADDRSPEC)) {
 392                 if (mdb_walk("nsmb_vc", smb_vc_cb, &cbd) == -1) {
 393                         mdb_warn("failed to walk 'nsmb_vc'");
 394                         return (DCMD_ERR);
 395                 }
 396                 return (DCMD_OK);
 397         }
 398 
 399         vcsz = sizeof (*vcp);
 400         vcp = mdb_alloc(vcsz, UM_SLEEP | UM_GC);
 401         if (mdb_vread(vcp, vcsz, addr) != vcsz) {
 402                 mdb_warn("cannot read VC from %p", addr);
 403                 return (DCMD_ERR);
 404         }
 405         smb_vc_cb(addr, vcp, &cbd);
 406 
 407         return (DCMD_OK);
 408 }
 409 
 410 void
 411 smb_vc_help(void)
 412 {
 413         mdb_printf("Options:\n"
 414             "  -r           recursive display of share lists\n"
 415             "  -v           be verbose when displaying smb_vc\n");
 416 }
 417 
 418 /*
 419  * Walker for the request list on a VC,
 420  * and dcmd to show a summary.
 421  */
 422 int
 423 rqlist_walk_init(mdb_walk_state_t *wsp)
 424 {
 425         struct smb_rqhead rqh;
 426         uintptr_t addr;
 427 
 428         /*
 429          * Initial walk_addr is the address of the VC.
 430          * Add offsetof(iod_rqlist) to get the rqhead.
 431          */
 432         if (wsp->walk_addr == 0) {
 433                 mdb_warn("::walk smb_ss does not support global walks\n");
 434                 return (WALK_ERR);
 435         }
 436         addr = wsp->walk_addr;
 437         addr += OFFSETOF(smb_vc_t, iod_rqlist);
 438 
 439         if (mdb_vread(&rqh, sizeof (rqh), addr) == -1) {
 440                 mdb_warn("failed to read smb_rqhead at %p", addr);
 441                 return (WALK_ERR);
 442         }
 443         wsp->walk_addr = (uintptr_t)rqh.tqh_first;
 444 
 445         return (WALK_NEXT);
 446 }
 447 
 448 int
 449 rqlist_walk_step(mdb_walk_state_t *wsp)
 450 {
 451         smb_rq_t rq;
 452         int status;
 453 
 454         if (wsp->walk_addr == NULL)
 455                 return (WALK_DONE);
 456 
 457         if (mdb_vread(&rq, sizeof (rq), wsp->walk_addr) == -1) {
 458                 mdb_warn("cannot read smb_rq from %p", wsp->walk_addr);
 459                 return (WALK_ERR);
 460         }
 461 
 462         status = wsp->walk_callback(wsp->walk_addr, &rq,
 463             wsp->walk_cbdata);
 464 
 465         wsp->walk_addr = (uintptr_t)rq.sr_link.tqe_next;
 466 
 467         return (status);
 468 }
 469 
 470 typedef struct rqlist_cbdata {
 471         int printed_header;
 472         int vcflags;
 473         uintptr_t uid;          /* optional filtering by UID */
 474 } rqlist_cbdata_t;
 475 
 476 int
 477 rqlist_cb(uintptr_t addr, const void *data, void *arg)
 478 {
 479         const smb_rq_t *rq = data;
 480         rqlist_cbdata_t *cbd = arg;
 481 
 482         if (cbd->printed_header == 0) {
 483                 cbd->printed_header = 1;
 484                 mdb_printf("// smb_rq_t MID cmd sr_state sr_flags\n");
 485         }
 486 
 487         mdb_printf(" %-p", addr);       /* smb_rq_t */
 488         if ((cbd->vcflags & SMBV_SMB2) != 0) {
 489                 mdb_printf(" x%04llx", (long long)rq->sr2_messageid);
 490                 mdb_printf(" x%02x", rq->sr2_command);
 491         } else {
 492                 mdb_printf(" x%04x", rq->sr_mid);
 493                 mdb_printf(" x%02x", rq->sr_cmd);
 494         }
 495         mdb_printf(" %d", rq->sr_state);
 496         mdb_printf(" x%x", rq->sr_flags);
 497         mdb_printf("\n");
 498 
 499         return (WALK_NEXT);
 500 }
 501 
 502 /*ARGSUSED*/
 503 int
 504 rqlist_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 505 {
 506         rqlist_cbdata_t cbd;
 507         smb_vc_t *vcp;
 508         size_t vcsz;
 509 
 510         memset(&cbd, 0, sizeof (cbd));
 511 
 512         /* Need the VC again to get  */
 513         vcsz = sizeof (*vcp);
 514         vcp = mdb_alloc(vcsz, UM_SLEEP | UM_GC);
 515         if (mdb_vread(vcp, vcsz, addr) != vcsz) {
 516                 mdb_warn("cannot read VC from %p", addr);
 517                 return (DCMD_ERR);
 518         }
 519         cbd.vcflags = vcp->vc_flags;
 520 
 521         /*
 522          * Initial walk_addr is address of parent (VC)
 523          */
 524         if (!(flags & DCMD_ADDRSPEC)) {
 525                 mdb_warn("address required\n");
 526                 return (DCMD_ERR);
 527         }
 528 
 529         if (mdb_pwalk("nsmb_rqlist", rqlist_cb, &cbd, addr) == -1) {
 530                 mdb_warn("failed to walk 'nsmb_rqlist'");
 531                 return (DCMD_ERR);
 532         }
 533 
 534         return (DCMD_OK);
 535 }
 536 
 537 
 538 /*
 539  * AVL walker for the passwords AVL tree,
 540  * and dcmd to show a summary.
 541  */
 542 static int
 543 pwtree_walk_init(mdb_walk_state_t *wsp)
 544 {
 545         GElf_Sym sym;
 546 
 547         if (wsp->walk_addr != NULL) {
 548                 mdb_warn("pwtree walk only supports global walks\n");
 549                 return (WALK_ERR);
 550         }
 551 
 552         if (mdb_lookup_by_obj(NSMB_OBJNAME, "smb_ptd", &sym) == -1) {
 553                 mdb_warn("failed to find symbol 'smb_ptd'");
 554                 return (WALK_ERR);
 555         }
 556 
 557         wsp->walk_addr = (uintptr_t)sym.st_value;
 558 
 559         if (mdb_layered_walk("avl", wsp) == -1) {
 560                 mdb_warn("failed to walk 'avl'\n");
 561                 return (WALK_ERR);
 562         }
 563 
 564         return (WALK_NEXT);
 565 }
 566 
 567 static int
 568 pwtree_walk_step(mdb_walk_state_t *wsp)
 569 {
 570         smb_passid_t    ptnode;
 571 
 572         if (mdb_vread(&ptnode, sizeof (ptnode), wsp->walk_addr) == -1) {
 573                 mdb_warn("failed to read smb_passid_t at %p", wsp->walk_addr);
 574                 return (WALK_ERR);
 575         }
 576 
 577         return (wsp->walk_callback(wsp->walk_addr, &ptnode, wsp->walk_cbdata));
 578 }
 579 
 580 typedef struct pwtree_cbdata {
 581         int printed_header;
 582         uid_t uid;              /* optional filtering by UID */
 583 } pwtree_cbdata_t;
 584 
 585 int
 586 pwtree_cb(uintptr_t addr, const void *data, void *arg)
 587 {
 588         const smb_passid_t *ptn = data;
 589         pwtree_cbdata_t *cbd = arg;
 590 
 591         /* Optional filtering by UID. */
 592         if (cbd->uid != (uid_t)-1 && cbd->uid != ptn->uid) {
 593                 return (WALK_NEXT);
 594         }
 595 
 596         if (cbd->printed_header == 0) {
 597                 cbd->printed_header = 1;
 598                 mdb_printf("// smb_passid_t UID domain user\n");
 599         }
 600 
 601         mdb_printf(" %-p", addr);       /* smb_passid_t */
 602         mdb_printf(" %d", (uintptr_t)ptn->uid);
 603         print_str((uintptr_t)ptn->srvdom);
 604         print_str((uintptr_t)ptn->username);
 605         mdb_printf("\n");
 606 
 607         return (WALK_NEXT);
 608 }
 609 
 610 /*ARGSUSED*/
 611 int
 612 pwtree_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 613 {
 614         pwtree_cbdata_t cbd;
 615         char *uid_str = NULL;
 616         char buf[32];
 617 
 618         memset(&cbd, 0, sizeof (cbd));
 619 
 620         if (mdb_getopts(argc, argv,
 621             'u', MDB_OPT_STR, &uid_str, NULL) != argc) {
 622                 return (DCMD_USAGE);
 623         }
 624         if (uid_str) {
 625                 /*
 626                  * Want the the default radix to be 10 here.
 627                  * If the string has some kind of radix prefix,
 628                  * just use that as-is, otherwise prepend "0t".
 629                  * Cheating on the "not a digit" test, but
 630                  * mdb_strtoull will do a real syntax check.
 631                  */
 632                 if (uid_str[0] == '0' && uid_str[1] > '9') {
 633                         cbd.uid = (uid_t)mdb_strtoull(uid_str);
 634                 } else {
 635                         strcpy(buf, "0t");
 636                         strlcat(buf, uid_str, sizeof (buf));
 637                         cbd.uid = (uid_t)mdb_strtoull(buf);
 638                 }
 639         } else
 640                 cbd.uid = (uid_t)-1;
 641 
 642         if (flags & DCMD_ADDRSPEC) {
 643                 mdb_warn("address not allowed\n");
 644                 return (DCMD_ERR);
 645         }
 646 
 647         if (mdb_pwalk("nsmb_pwtree", pwtree_cb, &cbd, 0) == -1) {
 648                 mdb_warn("failed to walk 'nsmb_pwtree'");
 649                 return (DCMD_ERR);
 650         }
 651 
 652         return (DCMD_OK);
 653 }
 654 
 655 void
 656 pwtree_help(void)
 657 {
 658         mdb_printf("Options:\n"
 659             "  -u uid       show only entries belonging to uid (decimal)\n");
 660 }
 661 
 662 
 663 static const mdb_dcmd_t dcmds[] = {
 664         { "nsmb_vc", "?[-rv]",
 665                 "show smb_vc (or list)",
 666                 smb_vc_dcmd, smb_vc_help },
 667         { "nsmb_rqlist", ":",
 668                 "show smb_rq list on a VC",
 669                 rqlist_dcmd, NULL },
 670         { "nsmb_pwtree", "?[-u uid]",
 671                 "list smb_passid_t (password tree)",
 672                 pwtree_dcmd, pwtree_help },
 673         {NULL}
 674 };
 675 
 676 static const mdb_walker_t walkers[] = {
 677         { "nsmb_vc", "walk nsmb VC list",
 678                 smb_vc_walk_init, smb_co_walk_step, NULL },
 679         { "nsmb_ss", "walk nsmb share list for some VC",
 680                 smb_ss_walk_init, smb_co_walk_step, NULL },
 681         { "nsmb_fh", "walk nsmb share list for some VC",
 682                 smb_fh_walk_init, smb_co_walk_step, NULL },
 683         { "nsmb_rqlist", "walk request list for some VC",
 684                 rqlist_walk_init, rqlist_walk_step, NULL },
 685         { "nsmb_pwtree", "walk passord AVL tree",
 686                 pwtree_walk_init, pwtree_walk_step, NULL },
 687         {NULL}
 688 };
 689 
 690 static const mdb_modinfo_t modinfo = {
 691         MDB_API_VERSION,
 692         dcmds,
 693         walkers
 694 };
 695 
 696 const mdb_modinfo_t *
 697 _mdb_init(void)
 698 {
 699         return (&modinfo);
 700 }