xref: /illumos-gate/usr/src/cmd/mdb/common/modules/nsmb/nsmb.c (revision 56f33205c9ed776c3c909e07d52e94610a675740)
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