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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <mdb/mdb_modapi.h> 30 #include <mdb/mdb_ks.h> 31 32 #include <sys/types.h> 33 #include <sys/systm.h> 34 #include <sys/door.h> 35 #include <sys/file.h> 36 #include <sys/mount.h> 37 #include <sys/proc.h> 38 #include <sys/procfs.h> 39 #include <sys/proc/prdata.h> 40 #include <sys/stat.h> 41 #include <sys/vfs.h> 42 #include <sys/vnode.h> 43 #include <sys/fs/snode.h> 44 #include <sys/fs/fifonode.h> 45 #include <sys/fs/namenode.h> 46 #include <sys/socket.h> 47 #include <sys/stropts.h> 48 #include <sys/socketvar.h> 49 #include <sys/strsubr.h> 50 #include <sys/un.h> 51 52 int 53 vfs_walk_init(mdb_walk_state_t *wsp) 54 { 55 if (wsp->walk_addr == NULL && 56 mdb_readvar(&wsp->walk_addr, "rootvfs") == -1) { 57 mdb_warn("failed to read 'rootvfs'"); 58 return (WALK_ERR); 59 } 60 61 wsp->walk_data = (void *)wsp->walk_addr; 62 return (WALK_NEXT); 63 } 64 65 int 66 vfs_walk_step(mdb_walk_state_t *wsp) 67 { 68 vfs_t vfs; 69 int status; 70 71 if (mdb_vread(&vfs, sizeof (vfs), wsp->walk_addr) == -1) { 72 mdb_warn("failed to read vfs_t at %p", wsp->walk_addr); 73 return (WALK_DONE); 74 } 75 76 status = wsp->walk_callback(wsp->walk_addr, &vfs, wsp->walk_cbdata); 77 78 if (vfs.vfs_next == wsp->walk_data) 79 return (WALK_DONE); 80 81 wsp->walk_addr = (uintptr_t)vfs.vfs_next; 82 83 return (status); 84 } 85 86 /* 87 * Utility routine to read in a filesystem name given a vfs pointer. If 88 * no vfssw entry for the vfs is available (as is the case with some pseudo- 89 * filesystems), we check against some known problem fs's: doorfs and 90 * portfs. If that fails, we try to guess the filesystem name using 91 * symbol names. fsname should be a buffer of size _ST_FSTYPSZ. 92 */ 93 static int 94 read_fsname(uintptr_t vfsp, char *fsname) 95 { 96 vfs_t vfs; 97 struct vfssw vfssw_entry; 98 GElf_Sym vfssw_sym, test_sym; 99 char testname[MDB_SYM_NAMLEN]; 100 101 if (mdb_vread(&vfs, sizeof (vfs), vfsp) == -1) { 102 mdb_warn("failed to read vfs %p", vfsp); 103 return (-1); 104 } 105 106 if (mdb_lookup_by_name("vfssw", &vfssw_sym) == -1) { 107 mdb_warn("failed to find vfssw"); 108 return (-1); 109 } 110 111 /* 112 * vfssw is an array; we need vfssw[vfs.vfs_fstype]. 113 */ 114 if (mdb_vread(&vfssw_entry, sizeof (vfssw_entry), 115 vfssw_sym.st_value + (sizeof (struct vfssw) * vfs.vfs_fstype)) 116 == -1) { 117 mdb_warn("failed to read vfssw index %d", vfs.vfs_fstype); 118 return (-1); 119 } 120 121 if (vfs.vfs_fstype != 0) { 122 if (mdb_readstr(fsname, _ST_FSTYPSZ, 123 (uintptr_t)vfssw_entry.vsw_name) == -1) { 124 mdb_warn("failed to find fs name %p", 125 vfssw_entry.vsw_name); 126 return (-1); 127 } 128 return (0); 129 } 130 131 /* 132 * Do precise detection for certain filesystem types that we 133 * know do not appear in vfssw[], and that we depend upon in other 134 * parts of the code: doorfs and portfs. 135 */ 136 if (mdb_lookup_by_name("door_vfs", &test_sym) != -1) { 137 if (test_sym.st_value == vfsp) { 138 strcpy(fsname, "doorfs"); 139 return (0); 140 } 141 } 142 if (mdb_lookup_by_name("port_vfs", &test_sym) != -1) { 143 if (test_sym.st_value == vfsp) { 144 strcpy(fsname, "portfs"); 145 return (0); 146 } 147 } 148 149 /* 150 * Heuristic detection for other filesystems that don't have a 151 * vfssw[] entry. These tend to be named <fsname>_vfs, so we do a 152 * lookup_by_addr and see if we find a symbol of that name. 153 */ 154 if (mdb_lookup_by_addr(vfsp, MDB_SYM_EXACT, testname, sizeof (testname), 155 &test_sym) != -1) { 156 if ((strlen(testname) > 4) && 157 (strcmp(testname + strlen(testname) - 4, "_vfs") == 0)) { 158 testname[strlen(testname) - 4] = '\0'; 159 strncpy(fsname, testname, _ST_FSTYPSZ); 160 return (0); 161 } 162 } 163 164 mdb_warn("unknown filesystem type for vfs %p", vfsp); 165 return (-1); 166 } 167 168 /* 169 * Column widths for mount point display in ::fsinfo output. 170 */ 171 #ifdef _LP64 172 #define FSINFO_MNTLEN 48 173 #else 174 #define FSINFO_MNTLEN 56 175 #endif 176 177 /*ARGSUSED*/ 178 int 179 fsinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 180 { 181 vfs_t vfs; 182 int len; 183 int opt_v = 0; 184 char buf[MAXPATHLEN]; 185 char fsname[_ST_FSTYPSZ]; 186 mntopt_t *mntopts; 187 size_t size; 188 int i; 189 int first = 1; 190 char opt[MAX_MNTOPT_STR]; 191 uintptr_t global_zone; 192 193 if (!(flags & DCMD_ADDRSPEC)) { 194 if (mdb_walk_dcmd("vfs", "fsinfo", argc, argv) == -1) { 195 mdb_warn("failed to walk file system list"); 196 return (DCMD_ERR); 197 } 198 return (DCMD_OK); 199 } 200 201 if (mdb_getopts(argc, argv, 202 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc) 203 return (DCMD_USAGE); 204 205 if (DCMD_HDRSPEC(flags)) 206 mdb_printf("%<u>%?s %-15s %s%</u>\n", 207 "VFSP", "FS", "MOUNT"); 208 209 if (mdb_vread(&vfs, sizeof (vfs), addr) == -1) { 210 mdb_warn("failed to read vfs_t %p", addr); 211 return (DCMD_ERR); 212 } 213 214 if ((len = mdb_read_refstr((uintptr_t)vfs.vfs_mntpt, buf, 215 sizeof (buf))) <= 0) 216 strcpy(buf, "??"); 217 218 else if (!opt_v && (len >= FSINFO_MNTLEN)) 219 /* 220 * In normal mode, we truncate the path to keep the output 221 * clean. In -v mode, we just print the full path. 222 */ 223 strcpy(&buf[FSINFO_MNTLEN - 4], "..."); 224 225 if (read_fsname(addr, fsname) == -1) 226 return (DCMD_ERR); 227 228 mdb_printf("%0?p %-15s %s\n", addr, fsname, buf); 229 230 if (!opt_v) 231 return (DCMD_OK); 232 233 /* 234 * Print 'resource' string; this shows what we're mounted upon. 235 */ 236 if (mdb_read_refstr((uintptr_t)vfs.vfs_resource, buf, 237 MAXPATHLEN) <= 0) 238 strcpy(buf, "??"); 239 240 mdb_printf("%?s %s\n", "R:", buf); 241 242 /* 243 * Print mount options array; it sucks to be a mimic, but we copy 244 * the same logic as in mntvnops.c for adding zone= tags, and we 245 * don't bother with the obsolete dev= option. 246 */ 247 size = vfs.vfs_mntopts.mo_count * sizeof (mntopt_t); 248 mntopts = mdb_alloc(size, UM_SLEEP | UM_GC); 249 250 if (mdb_vread(mntopts, size, 251 (uintptr_t)vfs.vfs_mntopts.mo_list) == -1) { 252 mdb_warn("failed to read mntopts %p", vfs.vfs_mntopts.mo_list); 253 return (DCMD_ERR); 254 } 255 256 for (i = 0; i < vfs.vfs_mntopts.mo_count; i++) { 257 if (mntopts[i].mo_flags & MO_SET) { 258 if (mdb_readstr(opt, sizeof (opt), 259 (uintptr_t)mntopts[i].mo_name) == -1) { 260 mdb_warn("failed to read mntopt name %p", 261 mntopts[i].mo_name); 262 return (DCMD_ERR); 263 } 264 if (first) { 265 mdb_printf("%?s ", "O:"); 266 first = 0; 267 } else { 268 mdb_printf(","); 269 } 270 mdb_printf("%s", opt); 271 if (mntopts[i].mo_flags & MO_HASVALUE) { 272 if (mdb_readstr(opt, sizeof (opt), 273 (uintptr_t)mntopts[i].mo_arg) == -1) { 274 mdb_warn("failed to read mntopt " 275 "value %p", mntopts[i].mo_arg); 276 return (DCMD_ERR); 277 } 278 mdb_printf("=%s", opt); 279 } 280 } 281 } 282 283 if (mdb_readvar(&global_zone, "global_zone") == -1) { 284 mdb_warn("failed to locate global_zone"); 285 return (DCMD_ERR); 286 } 287 288 if ((vfs.vfs_zone != NULL) && 289 ((uintptr_t)vfs.vfs_zone != global_zone)) { 290 zone_t z; 291 292 if (mdb_vread(&z, sizeof (z), (uintptr_t)vfs.vfs_zone) == -1) { 293 mdb_warn("failed to read zone"); 294 return (DCMD_ERR); 295 } 296 /* 297 * zone names are much shorter than MAX_MNTOPT_STR 298 */ 299 if (mdb_readstr(opt, sizeof (opt), 300 (uintptr_t)z.zone_name) == -1) { 301 mdb_warn("failed to read zone name"); 302 return (DCMD_ERR); 303 } 304 if (first) { 305 mdb_printf("%?s ", "O:"); 306 } else { 307 mdb_printf(","); 308 } 309 mdb_printf("zone=%s", opt); 310 } 311 return (DCMD_OK); 312 } 313 314 315 #define REALVP_DONE 0 316 #define REALVP_ERR 1 317 #define REALVP_CONTINUE 2 318 319 static int 320 next_realvp(uintptr_t invp, struct vnode *outvn, uintptr_t *outvp) 321 { 322 char fsname[_ST_FSTYPSZ]; 323 324 *outvp = invp; 325 if (mdb_vread(outvn, sizeof (struct vnode), invp) == -1) { 326 mdb_warn("failed to read vnode at %p", invp); 327 return (REALVP_ERR); 328 } 329 330 if (read_fsname((uintptr_t)outvn->v_vfsp, fsname) == -1) 331 return (REALVP_ERR); 332 333 /* 334 * We know how to do 'realvp' for as many filesystems as possible; 335 * for all other filesystems, we assume that the vp we are given 336 * is the realvp. In the kernel, a realvp operation will sometimes 337 * dig through multiple layers. Here, we only fetch the pointer 338 * to the next layer down. This allows dcmds to print out the 339 * various layers. 340 */ 341 if (strcmp(fsname, "fifofs") == 0) { 342 fifonode_t fn; 343 if (mdb_vread(&fn, sizeof (fn), 344 (uintptr_t)outvn->v_data) == -1) { 345 mdb_warn("failed to read fifonode"); 346 return (REALVP_ERR); 347 } 348 *outvp = (uintptr_t)fn.fn_realvp; 349 350 } else if (strcmp(fsname, "namefs") == 0) { 351 struct namenode nn; 352 if (mdb_vread(&nn, sizeof (nn), 353 (uintptr_t)outvn->v_data) == -1) { 354 mdb_warn("failed to read namenode"); 355 return (REALVP_ERR); 356 } 357 *outvp = (uintptr_t)nn.nm_filevp; 358 359 } else if (outvn->v_type == VSOCK && outvn->v_stream != NULL) { 360 struct stdata stream; 361 362 /* 363 * Sockets have a strange and different layering scheme; we 364 * hop over into the sockfs vnode (accessible via the stream 365 * head) if possible. 366 */ 367 if (mdb_vread(&stream, sizeof (stream), 368 (uintptr_t)outvn->v_stream) == -1) { 369 mdb_warn("failed to read stream data"); 370 return (REALVP_ERR); 371 } 372 *outvp = (uintptr_t)stream.sd_vnode; 373 } 374 375 if (*outvp == invp || *outvp == NULL) 376 return (REALVP_DONE); 377 378 return (REALVP_CONTINUE); 379 } 380 381 static void 382 pfiles_print_addr(struct sockaddr *addr) 383 { 384 struct sockaddr_in *s_in; 385 struct sockaddr_un *s_un; 386 struct sockaddr_in6 *s_in6; 387 388 switch (addr->sa_family) { 389 case AF_INET: 390 /*LINTED: alignment*/ 391 s_in = (struct sockaddr_in *)addr; 392 mdb_printf("AF_INET %I %d ", 393 s_in->sin_addr.s_addr, s_in->sin_port); 394 break; 395 396 case AF_INET6: 397 /*LINTED: alignment*/ 398 s_in6 = (struct sockaddr_in6 *)addr; 399 mdb_printf("AF_INET6 %N %d ", 400 &(s_in6->sin6_addr), s_in6->sin6_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