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