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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <mdb/mdb_modapi.h> 29 #include <mdb/mdb_ks.h> 30 31 #include <sys/types.h> 32 #include <sys/systm.h> 33 #include <sys/door.h> 34 #include <sys/file.h> 35 #include <sys/mount.h> 36 #include <sys/proc.h> 37 #include <sys/procfs.h> 38 #include <sys/proc/prdata.h> 39 #include <sys/stat.h> 40 #include <sys/vfs.h> 41 #include <sys/vnode.h> 42 #include <sys/fs/snode.h> 43 #include <sys/fs/fifonode.h> 44 #include <sys/fs/namenode.h> 45 #include <sys/socket.h> 46 #include <sys/stropts.h> 47 #include <sys/socketvar.h> 48 #include <sys/strsubr.h> 49 #include <sys/un.h> 50 51 int 52 vfs_walk_init(mdb_walk_state_t *wsp) 53 { 54 if (wsp->walk_addr == NULL && 55 mdb_readvar(&wsp->walk_addr, "rootvfs") == -1) { 56 mdb_warn("failed to read 'rootvfs'"); 57 return (WALK_ERR); 58 } 59 60 wsp->walk_data = (void *)wsp->walk_addr; 61 return (WALK_NEXT); 62 } 63 64 int 65 vfs_walk_step(mdb_walk_state_t *wsp) 66 { 67 vfs_t vfs; 68 int status; 69 70 if (mdb_vread(&vfs, sizeof (vfs), wsp->walk_addr) == -1) { 71 mdb_warn("failed to read vfs_t at %p", wsp->walk_addr); 72 return (WALK_DONE); 73 } 74 75 status = wsp->walk_callback(wsp->walk_addr, &vfs, wsp->walk_cbdata); 76 77 if (vfs.vfs_next == wsp->walk_data) 78 return (WALK_DONE); 79 80 wsp->walk_addr = (uintptr_t)vfs.vfs_next; 81 82 return (status); 83 } 84 85 /* 86 * Utility routine to read in a filesystem name given a vfs pointer. If 87 * no vfssw entry for the vfs is available (as is the case with some pseudo- 88 * filesystems), we check against some known problem fs's: doorfs and 89 * portfs. If that fails, we try to guess the filesystem name using 90 * symbol names. fsname should be a buffer of size _ST_FSTYPSZ. 91 */ 92 static int 93 read_fsname(uintptr_t vfsp, char *fsname) 94 { 95 vfs_t vfs; 96 struct vfssw vfssw_entry; 97 GElf_Sym vfssw_sym, test_sym; 98 char testname[MDB_SYM_NAMLEN]; 99 100 if (mdb_vread(&vfs, sizeof (vfs), vfsp) == -1) { 101 mdb_warn("failed to read vfs %p", vfsp); 102 return (-1); 103 } 104 105 if (mdb_lookup_by_name("vfssw", &vfssw_sym) == -1) { 106 mdb_warn("failed to find vfssw"); 107 return (-1); 108 } 109 110 /* 111 * vfssw is an array; we need vfssw[vfs.vfs_fstype]. 112 */ 113 if (mdb_vread(&vfssw_entry, sizeof (vfssw_entry), 114 vfssw_sym.st_value + (sizeof (struct vfssw) * vfs.vfs_fstype)) 115 == -1) { 116 mdb_warn("failed to read vfssw index %d", vfs.vfs_fstype); 117 return (-1); 118 } 119 120 if (vfs.vfs_fstype != 0) { 121 if (mdb_readstr(fsname, _ST_FSTYPSZ, 122 (uintptr_t)vfssw_entry.vsw_name) == -1) { 123 mdb_warn("failed to find fs name %p", 124 vfssw_entry.vsw_name); 125 return (-1); 126 } 127 return (0); 128 } 129 130 /* 131 * Do precise detection for certain filesystem types that we 132 * know do not appear in vfssw[], and that we depend upon in other 133 * parts of the code: doorfs and portfs. 134 */ 135 if (mdb_lookup_by_name("door_vfs", &test_sym) != -1) { 136 if (test_sym.st_value == vfsp) { 137 strcpy(fsname, "doorfs"); 138 return (0); 139 } 140 } 141 if (mdb_lookup_by_name("port_vfs", &test_sym) != -1) { 142 if (test_sym.st_value == vfsp) { 143 strcpy(fsname, "portfs"); 144 return (0); 145 } 146 } 147 148 /* 149 * Heuristic detection for other filesystems that don't have a 150 * vfssw[] entry. These tend to be named <fsname>_vfs, so we do a 151 * lookup_by_addr and see if we find a symbol of that name. 152 */ 153 if (mdb_lookup_by_addr(vfsp, MDB_SYM_EXACT, testname, sizeof (testname), 154 &test_sym) != -1) { 155 if ((strlen(testname) > 4) && 156 (strcmp(testname + strlen(testname) - 4, "_vfs") == 0)) { 157 testname[strlen(testname) - 4] = '\0'; 158 strncpy(fsname, testname, _ST_FSTYPSZ); 159 return (0); 160 } 161 } 162 163 mdb_warn("unknown filesystem type for vfs %p", vfsp); 164 return (-1); 165 } 166 167 /* 168 * Column widths for mount point display in ::fsinfo output. 169 */ 170 #ifdef _LP64 171 #define FSINFO_MNTLEN 48 172 #else 173 #define FSINFO_MNTLEN 56 174 #endif 175 176 /*ARGSUSED*/ 177 int 178 fsinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 179 { 180 vfs_t vfs; 181 int len; 182 int opt_v = 0; 183 char buf[MAXPATHLEN]; 184 char fsname[_ST_FSTYPSZ]; 185 mntopt_t *mntopts; 186 size_t size; 187 int i; 188 int first = 1; 189 char opt[MAX_MNTOPT_STR]; 190 uintptr_t global_zone; 191 192 if (!(flags & DCMD_ADDRSPEC)) { 193 if (mdb_walk_dcmd("vfs", "fsinfo", argc, argv) == -1) { 194 mdb_warn("failed to walk file system list"); 195 return (DCMD_ERR); 196 } 197 return (DCMD_OK); 198 } 199 200 if (mdb_getopts(argc, argv, 201 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) 202 return (DCMD_USAGE); 203 204 if (DCMD_HDRSPEC(flags)) 205 mdb_printf("%<u>%?s %-15s %s%</u>\n", 206 "VFSP", "FS", "MOUNT"); 207 208 if (mdb_vread(&vfs, sizeof (vfs), addr) == -1) { 209 mdb_warn("failed to read vfs_t %p", addr); 210 return (DCMD_ERR); 211 } 212 213 if ((len = mdb_read_refstr((uintptr_t)vfs.vfs_mntpt, buf, 214 sizeof (buf))) <= 0) 215 strcpy(buf, "??"); 216 217 else if (!opt_v && (len >= FSINFO_MNTLEN)) 218 /* 219 * In normal mode, we truncate the path to keep the output 220 * clean. In -v mode, we just print the full path. 221 */ 222 strcpy(&buf[FSINFO_MNTLEN - 4], "..."); 223 224 if (read_fsname(addr, fsname) == -1) 225 return (DCMD_ERR); 226 227 mdb_printf("%0?p %-15s %s\n", addr, fsname, buf); 228 229 if (!opt_v) 230 return (DCMD_OK); 231 232 /* 233 * Print 'resource' string; this shows what we're mounted upon. 234 */ 235 if (mdb_read_refstr((uintptr_t)vfs.vfs_resource, buf, 236 MAXPATHLEN) <= 0) 237 strcpy(buf, "??"); 238 239 mdb_printf("%?s %s\n", "R:", buf); 240 241 /* 242 * Print mount options array; it sucks to be a mimic, but we copy 243 * the same logic as in mntvnops.c for adding zone= tags, and we 244 * don't bother with the obsolete dev= option. 245 */ 246 size = vfs.vfs_mntopts.mo_count * sizeof (mntopt_t); 247 mntopts = mdb_alloc(size, UM_SLEEP | UM_GC); 248 249 if (mdb_vread(mntopts, size, 250 (uintptr_t)vfs.vfs_mntopts.mo_list) == -1) { 251 mdb_warn("failed to read mntopts %p", vfs.vfs_mntopts.mo_list); 252 return (DCMD_ERR); 253 } 254 255 for (i = 0; i < vfs.vfs_mntopts.mo_count; i++) { 256 if (mntopts[i].mo_flags & MO_SET) { 257 if (mdb_readstr(opt, sizeof (opt), 258 (uintptr_t)mntopts[i].mo_name) == -1) { 259 mdb_warn("failed to read mntopt name %p", 260 mntopts[i].mo_name); 261 return (DCMD_ERR); 262 } 263 if (first) { 264 mdb_printf("%?s ", "O:"); 265 first = 0; 266 } else { 267 mdb_printf(","); 268 } 269 mdb_printf("%s", opt); 270 if (mntopts[i].mo_flags & MO_HASVALUE) { 271 if (mdb_readstr(opt, sizeof (opt), 272 (uintptr_t)mntopts[i].mo_arg) == -1) { 273 mdb_warn("failed to read mntopt " 274 "value %p", mntopts[i].mo_arg); 275 return (DCMD_ERR); 276 } 277 mdb_printf("=%s", opt); 278 } 279 } 280 } 281 282 if (mdb_readvar(&global_zone, "global_zone") == -1) { 283 mdb_warn("failed to locate global_zone"); 284 return (DCMD_ERR); 285 } 286 287 if ((vfs.vfs_zone != NULL) && 288 ((uintptr_t)vfs.vfs_zone != global_zone)) { 289 zone_t z; 290 291 if (mdb_vread(&z, sizeof (z), (uintptr_t)vfs.vfs_zone) == -1) { 292 mdb_warn("failed to read zone"); 293 return (DCMD_ERR); 294 } 295 /* 296 * zone names are much shorter than MAX_MNTOPT_STR 297 */ 298 if (mdb_readstr(opt, sizeof (opt), 299 (uintptr_t)z.zone_name) == -1) { 300 mdb_warn("failed to read zone name"); 301 return (DCMD_ERR); 302 } 303 if (first) { 304 mdb_printf("%?s ", "O:"); 305 } else { 306 mdb_printf(","); 307 } 308 mdb_printf("zone=%s", opt); 309 } 310 return (DCMD_OK); 311 } 312 313 314 #define REALVP_DONE 0 315 #define REALVP_ERR 1 316 #define REALVP_CONTINUE 2 317 318 static int 319 next_realvp(uintptr_t invp, struct vnode *outvn, uintptr_t *outvp) 320 { 321 char fsname[_ST_FSTYPSZ]; 322 323 *outvp = invp; 324 if (mdb_vread(outvn, sizeof (struct vnode), invp) == -1) { 325 mdb_warn("failed to read vnode at %p", invp); 326 return (REALVP_ERR); 327 } 328 329 if (read_fsname((uintptr_t)outvn->v_vfsp, fsname) == -1) 330 return (REALVP_ERR); 331 332 /* 333 * We know how to do 'realvp' for as many filesystems as possible; 334 * for all other filesystems, we assume that the vp we are given 335 * is the realvp. In the kernel, a realvp operation will sometimes 336 * dig through multiple layers. Here, we only fetch the pointer 337 * to the next layer down. This allows dcmds to print out the 338 * various layers. 339 */ 340 if (strcmp(fsname, "fifofs") == 0) { 341 fifonode_t fn; 342 if (mdb_vread(&fn, sizeof (fn), 343 (uintptr_t)outvn->v_data) == -1) { 344 mdb_warn("failed to read fifonode"); 345 return (REALVP_ERR); 346 } 347 *outvp = (uintptr_t)fn.fn_realvp; 348 349 } else if (strcmp(fsname, "namefs") == 0) { 350 struct namenode nn; 351 if (mdb_vread(&nn, sizeof (nn), 352 (uintptr_t)outvn->v_data) == -1) { 353 mdb_warn("failed to read namenode"); 354 return (REALVP_ERR); 355 } 356 *outvp = (uintptr_t)nn.nm_filevp; 357 358 } else if (outvn->v_type == VSOCK && outvn->v_stream != NULL) { 359 struct stdata stream; 360 361 /* 362 * Sockets have a strange and different layering scheme; we 363 * hop over into the sockfs vnode (accessible via the stream 364 * head) if possible. 365 */ 366 if (mdb_vread(&stream, sizeof (stream), 367 (uintptr_t)outvn->v_stream) == -1) { 368 mdb_warn("failed to read stream data"); 369 return (REALVP_ERR); 370 } 371 *outvp = (uintptr_t)stream.sd_vnode; 372 } 373 374 if (*outvp == invp || *outvp == NULL) 375 return (REALVP_DONE); 376 377 return (REALVP_CONTINUE); 378 } 379 380 static void 381 pfiles_print_addr(struct sockaddr *addr) 382 { 383 struct sockaddr_in *s_in; 384 struct sockaddr_un *s_un; 385 struct sockaddr_in6 *s_in6; 386 in_port_t port; 387 388 switch (addr->sa_family) { 389 case AF_INET: 390 /*LINTED: alignment*/ 391 s_in = (struct sockaddr_in *)addr; 392 mdb_nhconvert(&port, &s_in->sin_port, sizeof (port)); 393 mdb_printf("AF_INET %I %d ", s_in->sin_addr.s_addr, port); 394 break; 395 396 case AF_INET6: 397 /*LINTED: alignment*/ 398 s_in6 = (struct sockaddr_in6 *)addr; 399 mdb_nhconvert(&port, &s_in6->sin6_port, sizeof (port)); 400 mdb_printf("AF_INET6 %N %d ", &(s_in6->sin6_addr), port); 401 break; 402 403 case AF_UNIX: 404 s_un = (struct sockaddr_un *)addr; 405 mdb_printf("AF_UNIX %s ", s_un->sun_path); 406 break; 407 default: 408 mdb_printf("AF_?? (%d) ", addr->sa_family); 409 break; 410 } 411 } 412 413 414 static int 415 pfiles_get_sonode(uintptr_t vp, struct sonode *sonode) 416 { 417 vnode_t v; 418 struct stdata stream; 419 420 if (mdb_vread(&v, sizeof (v), vp) == -1) { 421 mdb_warn("failed to read socket vnode"); 422 return (-1); 423 } 424 425 if (mdb_vread(&stream, sizeof (stream), (uintptr_t)v.v_stream) == -1) { 426 mdb_warn("failed to read stream data"); 427 return (-1); 428 } 429 430 if (mdb_vread(&v, sizeof (v), (uintptr_t)stream.sd_vnode) == -1) { 431 mdb_warn("failed to read stream vnode"); 432 return (-1); 433 } 434 435 if (mdb_vread(sonode, sizeof (struct sonode), 436 (uintptr_t)v.v_data) == -1) { 437 mdb_warn("failed to read sonode"); 438 return (-1); 439 } 440 441 return (0); 442 } 443 444 /* 445 * Do some digging to get a reasonable pathname for this vnode. 'path' 446 * should point at a buffer of MAXPATHLEN in size. 447 */ 448 static int 449 pfiles_dig_pathname(uintptr_t vp, char *path) 450 { 451 vnode_t v; 452 453 bzero(path, MAXPATHLEN); 454 455 if (mdb_vread(&v, sizeof (v), vp) == -1) { 456 mdb_warn("failed to read vnode"); 457 return (-1); 458 } 459 460 if (v.v_path == NULL) { 461 /* 462 * fifo's and doors are special. Some have pathnames, and 463 * some do not. And for these, it is pointless to go off to 464 * mdb_vnode2path, which is very slow. 465 * 466 * Event ports never have a pathname. 467 */ 468 if (v.v_type == VFIFO || v.v_type == VDOOR || v.v_type == VPORT) 469 return (0); 470 471 /* 472 * For sockets, we won't find a path unless we print the path 473 * associated with the accessvp. 474 */ 475 if (v.v_type == VSOCK) { 476 struct sonode sonode; 477 478 if (pfiles_get_sonode(vp, &sonode) == -1) { 479 return (-1); 480 } 481 482 vp = (uintptr_t)sonode.so_accessvp; 483 } 484 } 485 486 487 /* 488 * mdb_vnode2path will print an error for us as needed, but not 489 * finding a pathname is not really an error, so we plow on. 490 */ 491 (void) mdb_vnode2path(vp, path, MAXPATHLEN); 492 493 /* 494 * A common problem is that device pathnames are prefixed with 495 * /dev/../devices/. We just clean those up slightly: 496 * /dev/../devices/<mumble> --> /devices/<mumble> 497 * /dev/pts/../../devices/<mumble> --> /devices/<mumble> 498 */ 499 if (strncmp("/dev/../devices/", path, strlen("/dev/../devices/")) == 0) 500 strcpy(path, path + 7); 501 502 if (strncmp("/dev/pts/../../devices/", path, 503 strlen("/dev/pts/../../devices/")) == 0) 504 strcpy(path, path + 14); 505 506 return (0); 507 } 508 509 const struct fs_type { 510 int type; 511 const char *name; 512 } fs_types[] = { 513 { VNON, "NON" }, 514 { VREG, "REG" }, 515 { VDIR, "DIR" }, 516 { VBLK, "BLK" }, 517 { VCHR, "CHR" }, 518 { VLNK, "LNK" }, 519 { VFIFO, "FIFO" }, 520 { VDOOR, "DOOR" }, 521 { VPROC, "PROC" }, 522 { VSOCK, "SOCK" }, 523 { VPORT, "PORT" }, 524 { VBAD, "BAD" } 525 }; 526 527 #define NUM_FS_TYPES (sizeof (fs_types) / sizeof (struct fs_type)) 528 529 struct pfiles_cbdata { 530 int opt_p; 531 int fd; 532 }; 533 534 static int 535 pfile_callback(uintptr_t addr, const struct file *f, struct pfiles_cbdata *cb) 536 { 537 vnode_t v, layer_vn; 538 int myfd = cb->fd; 539 const char *type; 540 char path[MAXPATHLEN]; 541 uintptr_t top_vnodep, realvpp; 542 char fsname[_ST_FSTYPSZ]; 543 int err, i; 544 545 cb->fd++; 546 547 if (addr == NULL) { 548 return (WALK_NEXT); 549 } 550 551 top_vnodep = realvpp = (uintptr_t)f->f_vnode; 552 553 if (mdb_vread(&v, sizeof (v), realvpp) == -1) { 554 mdb_warn("failed to read vnode"); 555 return (DCMD_ERR); 556 } 557 558 type = "?"; 559 for (i = 0; i <= NUM_FS_TYPES; i++) { 560 if (fs_types[i].type == v.v_type) 561 type = fs_types[i].name; 562 } 563 564 do { 565 uintptr_t next_realvpp; 566 567 err = next_realvp(realvpp, &layer_vn, &next_realvpp); 568 if (next_realvpp != NULL) 569 realvpp = next_realvpp; 570 571 } while (err == REALVP_CONTINUE); 572 573 if (err == REALVP_ERR) { 574 mdb_warn("failed to do realvp() for %p", realvpp); 575 return (DCMD_ERR); 576 } 577 578 if (read_fsname((uintptr_t)layer_vn.v_vfsp, fsname) == -1) 579 return (DCMD_ERR); 580 581 mdb_printf("%4d %4s %?0p ", myfd, type, top_vnodep); 582 583 if (cb->opt_p) { 584 if (pfiles_dig_pathname(top_vnodep, path) == -1) 585 return (DCMD_ERR); 586 587 mdb_printf("%s\n", path); 588 return (DCMD_OK); 589 } 590 591 /* 592 * Sockets generally don't have interesting pathnames; we only 593 * show those in the '-p' view. 594 */ 595 path[0] = '\0'; 596 if (v.v_type != VSOCK) { 597 if (pfiles_dig_pathname(top_vnodep, path) == -1) 598 return (DCMD_ERR); 599 } 600 mdb_printf("%s%s", path, path[0] == '\0' ? "" : " "); 601 602 switch (v.v_type) { 603 case VDOOR: 604 { 605 door_node_t doornode; 606 proc_t pr; 607 608 if (mdb_vread(&doornode, sizeof (doornode), 609 (uintptr_t)layer_vn.v_data) == -1) { 610 mdb_warn("failed to read door_node"); 611 return (DCMD_ERR); 612 } 613 614 if (mdb_vread(&pr, sizeof (pr), 615 (uintptr_t)doornode.door_target) == -1) { 616 mdb_warn("failed to read door server process %p", 617 doornode.door_target); 618 return (DCMD_ERR); 619 } 620 mdb_printf("[door to '%s' (proc=%p)]", pr.p_user.u_comm, 621 doornode.door_target); 622 break; 623 } 624 625 case VSOCK: 626 { 627 struct sonode sonode; 628 629 if (pfiles_get_sonode(realvpp, &sonode) == -1) 630 return (DCMD_ERR); 631 632 /* 633 * If the address is cached in the sonode, use it; otherwise, 634 * we print nothing. 635 */ 636 if (sonode.so_state & SS_LADDR_VALID) { 637 struct sockaddr *laddr = 638 mdb_alloc(sonode.so_laddr_len, UM_SLEEP); 639 if (mdb_vread(laddr, sonode.so_laddr_len, 640 (uintptr_t)sonode.so_laddr_sa) == -1) { 641 mdb_warn("failed to read sonode socket addr"); 642 return (DCMD_ERR); 643 } 644 645 mdb_printf("socket: "); 646 pfiles_print_addr(laddr); 647 } 648 649 if (sonode.so_state & SS_FADDR_VALID) { 650 struct sockaddr *faddr = 651 mdb_alloc(sonode.so_faddr_len, UM_SLEEP); 652 if (mdb_vread(faddr, sonode.so_faddr_len, 653 (uintptr_t)sonode.so_faddr_sa) == -1) { 654 mdb_warn("failed to read sonode remote addr"); 655 return (DCMD_ERR); 656 } 657 658 mdb_printf("remote: "); 659 pfiles_print_addr(faddr); 660 } 661 break; 662 } 663 664 case VPORT: 665 mdb_printf("[event port (port=%p)]", v.v_data); 666 break; 667 668 case VPROC: 669 { 670 prnode_t prnode; 671 prcommon_t prcommon; 672 673 if (mdb_vread(&prnode, sizeof (prnode), 674 (uintptr_t)layer_vn.v_data) == -1) { 675 mdb_warn("failed to read prnode"); 676 return (DCMD_ERR); 677 } 678 679 if (mdb_vread(&prcommon, sizeof (prcommon), 680 (uintptr_t)prnode.pr_common) == -1) { 681 mdb_warn("failed to read prcommon %p", 682 prnode.pr_common); 683 return (DCMD_ERR); 684 } 685 686 mdb_printf("(proc=%p)", prcommon.prc_proc); 687 break; 688 } 689 690 default: 691 break; 692 } 693 694 695 mdb_printf("\n"); 696 697 return (WALK_NEXT); 698 } 699 700 static int 701 file_t_callback(uintptr_t addr, const struct file *f, struct pfiles_cbdata *cb) 702 { 703 int myfd = cb->fd; 704 705 cb->fd++; 706 707 if (addr == NULL) { 708 return (WALK_NEXT); 709 } 710 711 /* 712 * We really need 20 digits to print a 64-bit offset_t, but this 713 * is exceedingly rare, so we cheat and assume a column width of 10 714 * digits, in order to fit everything cleanly into 80 columns. 715 */ 716 mdb_printf("%?0p %4d %8x %?0p %10lld %?0p %4d\n", 717 addr, myfd, f->f_flag, f->f_vnode, f->f_offset, f->f_cred, 718 f->f_count); 719 720 return (WALK_NEXT); 721 } 722 723 int 724 pfiles(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 725 { 726 int opt_f = 0; 727 728 struct pfiles_cbdata cb; 729 730 bzero(&cb, sizeof (cb)); 731 732 if (!(flags & DCMD_ADDRSPEC)) 733 return (DCMD_USAGE); 734 735 if (mdb_getopts(argc, argv, 736 'p', MDB_OPT_SETBITS, TRUE, &cb.opt_p, 737 'f', MDB_OPT_SETBITS, TRUE, &opt_f, NULL) != argc) 738 return (DCMD_USAGE); 739 740 if (opt_f) { 741 mdb_printf("%<u>%?s %4s %8s %?s %10s %?s %4s%</u>\n", "FILE", 742 "FD", "FLAG", "VNODE", "OFFSET", "CRED", "CNT"); 743 if (mdb_pwalk("allfile", (mdb_walk_cb_t)file_t_callback, &cb, 744 addr) == -1) { 745 mdb_warn("failed to walk 'allfile'"); 746 return (DCMD_ERR); 747 } 748 } else { 749 mdb_printf("%<u>%-4s %4s %?s ", "FD", "TYPE", "VNODE"); 750 if (cb.opt_p) 751 mdb_printf("PATH"); 752 else 753 mdb_printf("INFO"); 754 mdb_printf("%</u>\n"); 755 756 if (mdb_pwalk("allfile", (mdb_walk_cb_t)pfile_callback, &cb, 757 addr) == -1) { 758 mdb_warn("failed to walk 'allfile'"); 759 return (DCMD_ERR); 760 } 761 } 762 763 764 return (DCMD_OK); 765 } 766 767 void 768 pfiles_help(void) 769 { 770 mdb_printf( 771 "Given the address of a process, print information about files\n" 772 "which the process has open. By default, this includes decoded\n" 773 "information about the file depending on file and filesystem type\n" 774 "\n" 775 "\t-p\tPathnames; omit decoded information. Only display " 776 "pathnames\n" 777 "\t-f\tfile_t view; show the file_t structure corresponding to " 778 "the fd\n"); 779 } 780