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