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 } 621