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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <sys/mdb_modapi.h> 31 #include <sys/types.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 } smb_co_cbdata_t; 214 215 /* 216 * Call-back function for walking a share list. 217 */ 218 int 219 smb_ss_cb(uintptr_t addr, const void *data, void *arg) 220 { 221 const smb_share_t *ssp = data; 222 smb_co_cbdata_t *cbd = arg; 223 224 mdb_printf(" %-p", addr); 225 print_str((uintptr_t)ssp->ss_name); 226 mdb_printf("\n"); 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 /* 238 * Call-back function for walking the VC list. 239 */ 240 int 241 smb_vc_cb(uintptr_t addr, const void *data, void *arg) 242 { 243 const smb_vc_t *vcp = data; 244 smb_co_cbdata_t *cbd = arg; 245 246 if (cbd->printed_header == 0) { 247 cbd->printed_header = 1; 248 mdb_printf("// smb_vc_t uid server user\n"); 249 } 250 251 mdb_printf("%-p", addr); 252 mdb_printf(" %d", vcp->vc_uid); 253 print_str((uintptr_t)vcp->vc_srvname); 254 print_str((uintptr_t)vcp->vc_username); 255 mdb_printf("\n"); 256 257 if (cbd->flags & OPT_RECURSE) { 258 mdb_inc_indent(2); 259 if (mdb_pwalk("nsmb_ss", smb_ss_cb, cbd, addr) < 0) { 260 mdb_warn("failed to walk 'nsmb_ss'"); 261 /* Don't: return (WALK_ERR); */ 262 } 263 mdb_dec_indent(2); 264 } 265 266 return (WALK_NEXT); 267 } 268 269 int 270 smb_vc_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 271 { 272 smb_co_cbdata_t cbd; 273 smb_vc_t *vcp; 274 size_t vcsz; 275 276 memset(&cbd, 0, sizeof (cbd)); 277 278 if (mdb_getopts(argc, argv, 279 'r', MDB_OPT_SETBITS, OPT_RECURSE, &cbd.flags, 280 'v', MDB_OPT_SETBITS, OPT_VERBOSE, &cbd.flags, 281 NULL) != argc) { 282 return (DCMD_USAGE); 283 } 284 285 if (!(flags & DCMD_ADDRSPEC)) { 286 if (mdb_walk("nsmb_vc", smb_vc_cb, &cbd) == -1) { 287 mdb_warn("failed to walk 'nsmb_vc'"); 288 return (DCMD_ERR); 289 } 290 return (DCMD_OK); 291 } 292 293 vcsz = sizeof (*vcp); 294 vcp = mdb_alloc(vcsz, UM_SLEEP | UM_GC); 295 if (mdb_vread(vcp, vcsz, addr) != vcsz) { 296 mdb_warn("cannot read VC from %p", addr); 297 return (DCMD_ERR); 298 } 299 smb_vc_cb(addr, vcp, &cbd); 300 301 return (DCMD_OK); 302 } 303 304 void 305 smb_vc_help(void) 306 { 307 mdb_printf("Options:\n" 308 " -r recursive display of share lists\n" 309 " -v be verbose when displaying smb_vc\n"); 310 } 311 312 /* 313 * Walker for the request list on a VC, 314 * and dcmd to show a summary. 315 */ 316 int 317 rqlist_walk_init(mdb_walk_state_t *wsp) 318 { 319 struct smb_rqhead rqh; 320 uintptr_t addr; 321 322 /* 323 * Initial walk_addr is the address of the VC. 324 * Add offsetof(iod_rqlist) to get the rqhead. 325 */ 326 if (wsp->walk_addr == 0) { 327 mdb_warn("::walk smb_ss does not support global walks\n"); 328 return (WALK_ERR); 329 } 330 addr = wsp->walk_addr; 331 addr += OFFSETOF(smb_vc_t, iod_rqlist); 332 333 if (mdb_vread(&rqh, sizeof (rqh), addr) == -1) { 334 mdb_warn("failed to read smb_rqhead at %p", addr); 335 return (WALK_ERR); 336 } 337 wsp->walk_addr = (uintptr_t)rqh.tqh_first; 338 339 return (WALK_NEXT); 340 } 341 342 int 343 rqlist_walk_step(mdb_walk_state_t *wsp) 344 { 345 smb_rq_t rq; 346 int status; 347 348 if (wsp->walk_addr == NULL) 349 return (WALK_DONE); 350 351 if (mdb_vread(&rq, sizeof (rq), wsp->walk_addr) == -1) { 352 mdb_warn("cannot read smb_rq from %p", wsp->walk_addr); 353 return (WALK_ERR); 354 } 355 356 status = wsp->walk_callback(wsp->walk_addr, &rq, 357 wsp->walk_cbdata); 358 359 wsp->walk_addr = (uintptr_t)rq.sr_link.tqe_next; 360 361 return (status); 362 } 363 364 typedef struct rqlist_cbdata { 365 int printed_header; 366 uintptr_t uid; /* optional filtering by UID */ 367 } rqlist_cbdata_t; 368 369 int 370 rqlist_cb(uintptr_t addr, const void *data, void *arg) 371 { 372 const smb_rq_t *rq = data; 373 rqlist_cbdata_t *cbd = arg; 374 375 if (cbd->printed_header == 0) { 376 cbd->printed_header = 1; 377 mdb_printf("// smb_rq_t MID cmd sr_state sr_flags\n"); 378 } 379 380 mdb_printf(" %-p", addr); /* smb_rq_t */ 381 mdb_printf(" x%04x", rq->sr_mid); 382 mdb_printf(" x%02x", rq->sr_cmd); 383 mdb_printf(" %d", rq->sr_state); 384 mdb_printf(" x%x", rq->sr_flags); 385 mdb_printf("\n"); 386 387 return (WALK_NEXT); 388 } 389 390 /*ARGSUSED*/ 391 int 392 rqlist_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 393 { 394 rqlist_cbdata_t cbd; 395 396 memset(&cbd, 0, sizeof (cbd)); 397 398 /* 399 * Initial walk_addr is address of parent (VC) 400 */ 401 if (!(flags & DCMD_ADDRSPEC)) { 402 mdb_warn("address required\n"); 403 return (DCMD_ERR); 404 } 405 406 if (mdb_pwalk("nsmb_rqlist", rqlist_cb, &cbd, addr) == -1) { 407 mdb_warn("failed to walk 'nsmb_rqlist'"); 408 return (DCMD_ERR); 409 } 410 411 return (DCMD_OK); 412 } 413 414 415 /* 416 * AVL walker for the passwords AVL tree, 417 * and dcmd to show a summary. 418 */ 419 static int 420 pwtree_walk_init(mdb_walk_state_t *wsp) 421 { 422 GElf_Sym sym; 423 424 if (wsp->walk_addr != NULL) { 425 mdb_warn("pwtree walk only supports global walks\n"); 426 return (WALK_ERR); 427 } 428 429 if (mdb_lookup_by_obj("nsmb", "smb_ptd", &sym) == -1) { 430 mdb_warn("failed to find symbol 'smb_ptd'"); 431 return (WALK_ERR); 432 } 433 434 wsp->walk_addr = (uintptr_t)sym.st_value; 435 436 if (mdb_layered_walk("avl", wsp) == -1) { 437 mdb_warn("failed to walk 'avl'\n"); 438 return (WALK_ERR); 439 } 440 441 return (WALK_NEXT); 442 } 443 444 static int 445 pwtree_walk_step(mdb_walk_state_t *wsp) 446 { 447 smb_passid_t ptnode; 448 449 if (mdb_vread(&ptnode, sizeof (ptnode), wsp->walk_addr) == -1) { 450 mdb_warn("failed to read smb_passid_t at %p", wsp->walk_addr); 451 return (WALK_ERR); 452 } 453 454 return (wsp->walk_callback(wsp->walk_addr, &ptnode, wsp->walk_cbdata)); 455 } 456 457 typedef struct pwtree_cbdata { 458 int printed_header; 459 uid_t uid; /* optional filtering by UID */ 460 } pwtree_cbdata_t; 461 462 int 463 pwtree_cb(uintptr_t addr, const void *data, void *arg) 464 { 465 const smb_passid_t *ptn = data; 466 pwtree_cbdata_t *cbd = arg; 467 468 /* Optional filtering by UID. */ 469 if (cbd->uid != (uid_t)-1 && cbd->uid != ptn->uid) { 470 return (WALK_NEXT); 471 } 472 473 if (cbd->printed_header == 0) { 474 cbd->printed_header = 1; 475 mdb_printf("// smb_passid_t UID domain user\n"); 476 } 477 478 mdb_printf(" %-p", addr); /* smb_passid_t */ 479 mdb_printf(" %d", (uintptr_t)ptn->uid); 480 print_str((uintptr_t)ptn->srvdom); 481 print_str((uintptr_t)ptn->username); 482 mdb_printf("\n"); 483 484 return (WALK_NEXT); 485 } 486 487 /*ARGSUSED*/ 488 int 489 pwtree_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 490 { 491 pwtree_cbdata_t cbd; 492 char *uid_str = NULL; 493 char buf[32]; 494 495 memset(&cbd, 0, sizeof (cbd)); 496 497 if (mdb_getopts(argc, argv, 498 'u', MDB_OPT_STR, &uid_str, NULL) != argc) { 499 return (DCMD_USAGE); 500 } 501 if (uid_str) { 502 /* 503 * Want the the default radix to be 10 here. 504 * If the string has some kind of radix prefix, 505 * just use that as-is, otherwise prepend "0t". 506 * Cheating on the "not a digit" test, but 507 * mdb_strtoull will do a real syntax check. 508 */ 509 if (uid_str[0] == '0' && uid_str[1] > '9') { 510 cbd.uid = (uid_t)mdb_strtoull(uid_str); 511 } else { 512 strcpy(buf, "0t"); 513 strlcat(buf, uid_str, sizeof (buf)); 514 cbd.uid = (uid_t)mdb_strtoull(buf); 515 } 516 } else 517 cbd.uid = (uid_t)-1; 518 519 if (flags & DCMD_ADDRSPEC) { 520 mdb_warn("address not allowed\n"); 521 return (DCMD_ERR); 522 } 523 524 if (mdb_pwalk("nsmb_pwtree", pwtree_cb, &cbd, 0) == -1) { 525 mdb_warn("failed to walk 'nsmb_pwtree'"); 526 return (DCMD_ERR); 527 } 528 529 return (DCMD_OK); 530 } 531 532 void 533 pwtree_help(void) 534 { 535 mdb_printf("Options:\n" 536 " -u uid show only entries belonging to uid (decimal)\n"); 537 } 538 539 540 static const mdb_dcmd_t dcmds[] = { 541 { "nsmb_vc", "?[-rv]", 542 "show smb_vc (or list)", 543 smb_vc_dcmd, smb_vc_help }, 544 { "nsmb_rqlist", ":", 545 "show smb_rq list on a VC", 546 rqlist_dcmd, NULL }, 547 { "nsmb_pwtree", "?[-u uid]", 548 "list smb_passid_t (password tree)", 549 pwtree_dcmd, pwtree_help }, 550 {NULL} 551 }; 552 553 static const mdb_walker_t walkers[] = { 554 { "nsmb_vc", "walk nsmb VC list", 555 smb_vc_walk_init, smb_co_walk_step, NULL }, 556 { "nsmb_ss", "walk nsmb share list for some VC", 557 smb_ss_walk_init, smb_co_walk_step, NULL }, 558 { "nsmb_rqlist", "walk request list for some VC", 559 rqlist_walk_init, rqlist_walk_step, NULL }, 560 { "nsmb_pwtree", "walk passord AVL tree", 561 pwtree_walk_init, pwtree_walk_step, NULL }, 562 {NULL} 563 }; 564 565 static const mdb_modinfo_t modinfo = { 566 MDB_API_VERSION, 567 dcmds, 568 walkers 569 }; 570 571 const mdb_modinfo_t * 572 _mdb_init(void) 573 { 574 return (&modinfo); 575 } 576