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