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
print_str(uintptr_t addr)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
smb_co_walk_init(mdb_walk_state_t * wsp,int level)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
smb_vc_walk_init(mdb_walk_state_t * wsp)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
smb_ss_walk_init(mdb_walk_state_t * wsp)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
smb_co_walk_step(mdb_walk_state_t * wsp)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
smb_ss_cb(uintptr_t addr,const void * data,void * arg)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 *
vcstate_str(smb_co_cbdata_t * cbd,int stval)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
smb_vc_cb(uintptr_t addr,const void * data,void * arg)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
smb_vc_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
smb_vc_help(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
rqlist_walk_init(mdb_walk_state_t * wsp)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
rqlist_walk_step(mdb_walk_state_t * wsp)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
rqlist_cb(uintptr_t addr,const void * data,void * arg)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
rqlist_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
pwtree_walk_init(mdb_walk_state_t * wsp)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
pwtree_walk_step(mdb_walk_state_t * wsp)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
pwtree_cb(uintptr_t addr,const void * data,void * arg)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
pwtree_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
pwtree_help(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 *
_mdb_init(void)616 _mdb_init(void)
617 {
618 return (&modinfo);
619 }
620