diff --git a/usr.bin/fstat/fstat.1 b/usr.bin/fstat/fstat.1 --- a/usr.bin/fstat/fstat.1 +++ b/usr.bin/fstat/fstat.1 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd November 19, 2020 +.Dd June 3, 2025 .Dt FSTAT 1 .Os .Sh NAME @@ -33,6 +33,7 @@ .Nd identify active files .Sh SYNOPSIS .Nm +.Op Fl -libxo .Op Fl fmnsv .Op Fl M Ar core .Op Fl N Ar system @@ -52,6 +53,13 @@ .Pp The following options are available: .Bl -tag -width "-N system" +.It Fl -libxo +Generate output via +.Xr libxo 3 +in a selection of different human and machine readable formats. +See +.Xr xo_parse_args 3 +for details on command line arguments. .It Fl f Restrict examination to files open in the same file systems as the named file arguments, or to the file system containing the @@ -327,18 +335,10 @@ .Xr ps 1 , .Xr sockstat 1 , .Xr systat 1 , +.Xr libxo 3 , +.Xr xo_parse_args 3 , .Xr tcp 4 , .Xr unix 4 , .Xr iostat 8 , .Xr pstat 8 , .Xr vmstat 8 -.Sh HISTORY -The -.Nm -command appeared in -.Bx 4.3 tahoe . -.Sh BUGS -Since -.Nm -takes a snapshot of the system, it is only correct for a very short period -of time. diff --git a/usr.bin/fstat/fstat.c b/usr.bin/fstat/fstat.c --- a/usr.bin/fstat/fstat.c +++ b/usr.bin/fstat/fstat.c @@ -45,8 +45,9 @@ #include #include -#include +#include #include +#include #include #include #include @@ -104,11 +105,14 @@ struct passwd *passwd; struct procstat *procstat; int arg, ch, what; - int cnt, i; - + unsigned int cnt, i; arg = 0; what = KERN_PROC_PROC; nlistf = memf = NULL; + /* parse --libxo arguments first */ + argc = xo_parse_args(argc, argv); + if (argc < 0) + xo_errx(1, "libxo argument parsing failed"); while ((ch = getopt(argc, argv, "fmnp:su:vN:M:")) != -1) switch((char)ch) { case 'f': @@ -130,7 +134,7 @@ if (pflg++) usage(); if (!isdigit(*optarg)) { - warnx("-p requires a process id"); + xo_warnx("-p requires a process id"); usage(); } what = KERN_PROC_PID; @@ -143,7 +147,7 @@ if (uflg++) usage(); if (!(passwd = getpwnam(optarg))) - errx(1, "%s: unknown uid", optarg); + xo_errx(1, "%s: unknown uid", optarg); what = KERN_PROC_UID; arg = passwd->pw_uid; break; @@ -176,24 +180,30 @@ else procstat = procstat_open_sysctl(); if (procstat == NULL) - errx(1, "procstat_open()"); + xo_errx(1, "procstat_open_sysctl() failed: %s", strerror(errno)); p = procstat_getprocs(procstat, what, arg, &cnt); if (p == NULL) - errx(1, "procstat_getprocs()"); + xo_errx(1, "procstat_getprocs() failed: %s", strerror(errno)); /* * Print header. */ - if (nflg) - printf("%s", -"USER CMD PID FD DEV INUM MODE SZ|DV R/W"); - else - printf("%s", -"USER CMD PID FD MOUNT INUM MODE SZ|DV R/W"); + xo_open_container("fstat-information"); + if (nflg) { + xo_emit("{T:/%-9s}{T:/%-13s}{T:/%-6s}{T:/%-4s}{T:/%-7s}{T:/%-11s}", + "{T:/%-5s}{T:/%-6s}{T:/%-s}", + "USER", "CMD", "PID", "FD", "DEV", + "INUM", "MODE", "SZ|DV ", "R/W"); + } else { + xo_emit("{T:/%-9s}{T:/%-13s}{T:/%-6s}{T:/%-3s}{T:/%-11s}{T:/%-5s}", + "{T:/%-13s}{T:/%-6s}{T:/%-3s}", + "USER", "CMD", "PID", "FD", "MOUNT", + "INUM", "MODE", "SZ|DV", "R/W"); + } if (checkfile && fsflg == 0) - printf(" NAME\n"); + xo_emit("{T:/ NAME}\n"); else - putchar('\n'); + xo_emit("\n"); /* * Go through the process list. @@ -203,8 +213,10 @@ continue; dofiles(procstat, &p[i]); } + xo_close_container("fstat-information"); procstat_freeprocs(procstat, p); procstat_close(procstat); + xo_finish(); return (0); } @@ -224,8 +236,10 @@ head = procstat_getfiles(procstat, kp, mflg); if (head == NULL) return; - STAILQ_FOREACH(fst, head, next) - print_file_info(procstat, fst, uname, cmd, pid); + xo_open_list("files"); + STAILQ_FOREACH(fst, head, next) + print_file_info(procstat, fst, uname, cmd, pid); + xo_close_list("files"); procstat_freefiles(procstat, head); } @@ -264,70 +278,83 @@ /* * Print entry prefix. */ - printf("%-8.8s %-10s %5d", uname, cmd, pid); - if (fst->fs_uflags & PS_FST_UFLAG_TEXT) - printf(" text"); - else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) - printf(" wd"); - else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) - printf(" root"); - else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) - printf(" tr"); - else if (fst->fs_uflags & PS_FST_UFLAG_MMAP) - printf(" mmap"); - else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) - printf(" jail"); - else if (fst->fs_uflags & PS_FST_UFLAG_CTTY) - printf(" ctty"); - else - printf(" %4d", fst->fs_fd); + xo_open_instance("file"); + xo_emit("{:user/%-8.8s/%s} {:command/%-10s/%s} {:pid/%5d/%d}", + uname, cmd, pid); + if (fst->fs_uflags & PS_FST_UFLAG_TEXT) { + xo_emit(" {:fd/text}"); + } else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) { + xo_emit(" {:fd/wd}"); + } else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) { + xo_emit(" {:fd/root}"); + } else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) { + xo_emit(" {:fd/tr}"); + } else if (fst->fs_uflags & PS_FST_UFLAG_MMAP) { + xo_emit(" {:fd/mmap}"); + } else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) { + xo_emit(" {:fd/jail}"); + } else if (fst->fs_uflags & PS_FST_UFLAG_CTTY) { + xo_emit(" {:fd/ctty}"); + } else { + xo_emit(" {:fd/%4d}", fst->fs_fd); + } /* * Print type-specific data. */ switch (fst->fs_type) { case PS_FST_TYPE_FIFO: + xo_emit("{e:file_type/fifo}"); + print_vnode_info(procstat, fst); + break; case PS_FST_TYPE_VNODE: + xo_emit("{e:file_type/vnode}"); print_vnode_info(procstat, fst); break; case PS_FST_TYPE_SOCKET: + xo_emit("{e:file_type/socket}"); print_socket_info(procstat, fst); break; case PS_FST_TYPE_PIPE: + xo_emit("{e:file_type/pipe}"); print_pipe_info(procstat, fst); break; case PS_FST_TYPE_PTS: + xo_emit("{e:file_type/pts}"); print_pts_info(procstat, fst); break; case PS_FST_TYPE_KQUEUE: - printf(" [kqueue]"); + xo_emit("{e:file_type/[kqueue]}"); break; case PS_FST_TYPE_MQUEUE: - printf(" [mqueue]"); + xo_emit("{e:file_type/[mqueue]}"); break; case PS_FST_TYPE_SHM: + xo_emit("{e:file_type/shm}"); print_shm_info(procstat, fst); break; case PS_FST_TYPE_SEM: + xo_emit("{e:file_type/sem}"); print_sem_info(procstat, fst); break; case PS_FST_TYPE_PROCDESC: - printf(" [procdesc]"); + xo_emit("{e:file_type/[procdesc]}"); break; case PS_FST_TYPE_DEV: break; case PS_FST_TYPE_EVENTFD: - printf(" [eventfd]"); + xo_emit("{e:file_type/[eventfd]}"); break; default: + xo_emit("{e:file_type/unknown}"); if (vflg) - fprintf(stderr, - "unknown file type %d for file %d of pid %d\n", - fst->fs_type, fst->fs_fd, pid); + xo_warnx("unknown file type %d for file %d of pid %d", + fst->fs_type, fst->fs_fd, pid); } if (filename && !fsflg) - printf(" %s", filename); - putchar('\n'); + xo_emit(" {:filename/ %s}", filename); + xo_emit("\n"); + xo_close_instance("file"); } static char * @@ -399,15 +426,19 @@ int error; static int isopen; + xo_open_container("socket"); error = procstat_get_socket_info(procstat, fst, &sock, errbuf); if (error != 0) { - printf("* error"); + xo_emit(" {:socket_error/error}"); + xo_close_container("socket"); return; } if (sock.type > STYPEMAX) - printf("* %s ?%d", sock.dname, sock.type); + xo_emit("* {:socket_domain/%s} {:socket_type_unknown/?%d}", sock.dname, + sock.type); else - printf("* %s %s", sock.dname, stypename[sock.type]); + xo_emit("* {:socket_domain/%s} {:socket_type/%s}", sock.dname, + stypename[sock.type]); /* * protocol specific formatting @@ -425,21 +456,21 @@ if (!isopen) setprotoent(++isopen); if ((pe = getprotobynumber(sock.proto)) != NULL) - printf(" %s", pe->p_name); + xo_emit(" {:protocol_name/%s}", pe->p_name); else - printf(" %d", sock.proto); + xo_emit(" {:protocol_number/%d}", sock.proto); if (sock.so_pcb != 0) - printf(" %lx", (u_long)sock.so_pcb); + xo_emit(" {:pcb_address/%lx}", (u_long)sock.so_pcb); if (!sflg) break; - printf(" %s <-> %s", - addr_to_string(&sock.sa_local, src_addr, sizeof(src_addr)), - addr_to_string(&sock.sa_peer, dst_addr, sizeof(dst_addr))); + xo_emit(" {:local-address/%s} <-> {:remote-address/%s}", + addr_to_string(&sock.sa_local, src_addr, sizeof(src_addr)), + addr_to_string(&sock.sa_peer, dst_addr, sizeof(dst_addr))); break; case AF_UNIX: /* print address of pcb and connected pcb */ if (sock.so_pcb != 0) { - printf(" %lx", (u_long)sock.so_pcb); + xo_emit(" {:pcb_address/%lx}", (u_long)sock.so_pcb); if (sock.unp_conn) { char shoconn[4], *cp; @@ -450,8 +481,8 @@ if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) *cp++ = '>'; *cp = '\0'; - printf(" %s %lx", shoconn, - (u_long)sock.unp_conn); + xo_emit(" {:connection_status/%s} {:connected_pcb_address/%lx}", + shoconn, (u_long)sock.unp_conn); } } if (!sflg) @@ -470,12 +501,14 @@ else addr_to_string(&sock.sa_peer, src_addr, sizeof(src_addr)); - printf(" %s", src_addr); + xo_emit(" {:unix_socket_path/ %s}", src_addr); break; default: /* print protocol number and socket address */ - printf(" %d %lx", sock.proto, (u_long)sock.so_addr); + xo_emit(" {:protocol_number/%d} {:socket_address/%lx}", sock.proto, + (u_long)sock.so_addr); } + xo_close_container("socket"); } static void @@ -487,12 +520,15 @@ error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); if (error != 0) { - printf("* error"); + xo_emit("* {:type/error}"); return; } - printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer); - printf(" %6zd", ps.buffer_cnt); + xo_open_container("pipe"); + xo_emit("* {:type/pipe} {:pipe-addr/%8lx/%lx} <-> {:peer-addr/%8lx/%lx}", + (u_long)ps.addr, (u_long)ps.peer); + xo_emit(" {:buffer-count/%6zd/%zd}", ps.buffer_cnt); print_access_flags(fst->fs_fflags); + xo_close_container("pipe"); } static void @@ -504,16 +540,18 @@ error = procstat_get_pts_info(procstat, fst, &pts, errbuf); if (error != 0) { - printf("* error"); + xo_emit("* {:type/error}"); return; } - printf("* pseudo-terminal master "); + xo_open_container("pts"); + xo_emit("* {:type/pseudo-terminal master} "); if (nflg || !*pts.devname) { - printf("%#10jx", (uintmax_t)pts.dev); + xo_emit("{:device/%#10jx/%#jx}", (uintmax_t)pts.dev); } else { - printf("%10s", pts.devname); + xo_emit("{:device/%10s/%s}", pts.devname); } print_access_flags(fst->fs_fflags); + xo_close_container("pts"); } static void @@ -526,18 +564,20 @@ error = procstat_get_sem_info(procstat, fst, &sem, errbuf); if (error != 0) { - printf("* error"); + xo_emit("* {:type/error}"); return; } + xo_open_container("sem"); if (nflg) { - printf(" "); + xo_emit("{:mount/%13s/%s}", ""); (void)snprintf(mode, sizeof(mode), "%o", sem.mode); } else { - printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-"); + xo_emit(" {:mount/%-15s/%s}", fst->fs_path != NULL ? fst->fs_path : "-"); strmode(sem.mode, mode); } - printf(" %10s %6u", mode, sem.value); + xo_emit(" {:mode/%10s/%s} {:value/%6u/%u}", mode, sem.value); print_access_flags(fst->fs_fflags); + xo_close_container("sem"); } static void @@ -550,18 +590,20 @@ error = procstat_get_shm_info(procstat, fst, &shm, errbuf); if (error != 0) { - printf("* error"); + xo_emit("* {:type/error}"); return; } + xo_open_container("shm"); if (nflg) { - printf(" "); + xo_emit("{:mount/%13s/%s}", ""); (void)snprintf(mode, sizeof(mode), "%o", shm.mode); } else { - printf(" %-15s", fst->fs_path != NULL ? fst->fs_path : "-"); + xo_emit(" {:mount/%-15s/%s}", fst->fs_path != NULL ? fst->fs_path : "-"); strmode(shm.mode, mode); } - printf(" %10s %6ju", mode, shm.size); + xo_emit(" {:mode/%10s/%s} {:size/%6ju/%ju}", mode, shm.size); print_access_flags(fst->fs_fflags); + xo_close_container("shm"); } static void @@ -581,15 +623,18 @@ badtype = "bad"; else if (vn.vn_type == PS_FST_VTYPE_VNON) badtype = "none"; + xo_open_container("vnode"); if (badtype != NULL) { - printf(" - - %10s -", badtype); + xo_emit(" {:mount/%-9s/%s} {:inode/%10s/%s} {:size/%-6s/%s}", + "-", badtype, "-"); + xo_close_container("vnode"); return; } - + if (nflg) - printf(" %#5jx", (uintmax_t)vn.vn_fsid); + xo_emit(" {:mount/%#5jx/%#jx}", (uintmax_t)vn.vn_fsid); else if (vn.vn_mntdir != NULL) - (void)printf(" %-8s", vn.vn_mntdir); + xo_emit(" {:mount/%-8s/%s}", vn.vn_mntdir); /* * Print access mode. @@ -599,30 +644,35 @@ else { strmode(vn.vn_mode, mode); } - (void)printf(" %6jd %10s", (intmax_t)vn.vn_fileid, mode); - + xo_emit(" {:inode/%6jd/%jd} {:mode/%10s/%s}", (intmax_t)vn.vn_fileid, mode); + if (vn.vn_type == PS_FST_VTYPE_VBLK || vn.vn_type == PS_FST_VTYPE_VCHR) { if (nflg || !*vn.vn_devname) - printf(" %#6jx", (uintmax_t)vn.vn_dev); + xo_emit(" {:size/%#6jx/%#jx}", (uintmax_t)vn.vn_dev); else { - printf(" %6s", vn.vn_devname); + xo_emit(" {:size/%6s/%s}", vn.vn_devname); } } else - printf(" %6ju", (uintmax_t)vn.vn_size); + xo_emit(" {:size/%6ju/%ju}", (uintmax_t)vn.vn_size); + print_access_flags(fst->fs_fflags); + xo_close_container("vnode"); } + static void print_access_flags(int flags) { char rw[3]; + xo_open_container("access"); rw[0] = '\0'; if (flags & PS_FST_FFLAG_READ) strcat(rw, "r"); if (flags & PS_FST_FFLAG_WRITE) strcat(rw, "w"); - printf(" %2s", rw); + xo_emit(" {:access_flags/%2s}", rw); + xo_close_container("access"); } int @@ -632,14 +682,14 @@ DEVS *cur; if (stat(filename, &statbuf)) { - warn("%s", filename); + xo_warn("%s", filename); return (0); } if ((cur = malloc(sizeof(DEVS))) == NULL) - err(1, NULL); + xo_err(1, NULL); + cur->next = devs; devs = cur; - cur->ino = statbuf.st_ino; cur->fsid = statbuf.st_dev; cur->name = filename; @@ -649,7 +699,8 @@ static void usage(void) { - (void)fprintf(stderr, - "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n"); + xo_error( + "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] " + "[file ...]\n"); exit(1); }