Index: projects/libprocstat/usr.bin/fstat/fstat.c =================================================================== --- projects/libprocstat/usr.bin/fstat/fstat.c (revision 196016) +++ projects/libprocstat/usr.bin/fstat/fstat.c (revision 196017) @@ -1,509 +1,510 @@ /*- * Copyright (c) 2009 Stanislav Sedov * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "functions.h" #include "libprocstat.h" int fsflg, /* show files on same filesystem as file(s) argument */ pflg, /* show files open by a particular pid */ uflg; /* show files open by a particular (effective) user */ int checkfile; /* true if restricting to particular files or filesystems */ int nflg; /* (numerical) display f.s. and rdev as dev_t */ int mflg; /* include memory-mapped files */ int vflg; /* be verbose */ typedef struct devs { struct devs *next; uint32_t fsid; uint64_t ino; const char *name; } DEVS; DEVS *devs; char *memf, *nlistf; static int getfname(const char *filename); static void dofiles(struct procstat *procstat, struct kinfo_proc *p); static void print_access_flags(int flags); static void print_file_info(struct procstat *procstat, struct filestat *fst, const char *uname, const char *cmd, int pid); static void print_pipe_info(struct procstat *procstat, struct filestat *fst); static void print_pts_info(struct procstat *procstat, struct filestat *fst); static void print_socket_info(struct procstat *procstat, struct filestat *fst); static void print_vnode_info(struct procstat *procstat, struct filestat *fst); static void usage(void) __dead2; int do_fstat(int argc, char **argv) { struct kinfo_proc *p; struct passwd *passwd; struct procstat *procstat; int arg, ch, what; int cnt, i; arg = 0; what = KERN_PROC_PROC; nlistf = memf = NULL; while ((ch = getopt(argc, argv, "fmnp:u:vN:M:")) != -1) switch((char)ch) { case 'f': fsflg = 1; break; case 'M': memf = optarg; break; case 'N': nlistf = optarg; break; case 'm': mflg = 1; break; case 'n': nflg = 1; break; case 'p': if (pflg++) usage(); if (!isdigit(*optarg)) { warnx("-p requires a process id"); usage(); } what = KERN_PROC_PID; arg = atoi(optarg); break; case 'u': if (uflg++) usage(); if (!(passwd = getpwnam(optarg))) errx(1, "%s: unknown uid", optarg); what = KERN_PROC_UID; arg = passwd->pw_uid; break; case 'v': vflg = 1; break; case '?': default: usage(); } if (*(argv += optind)) { for (; *argv; ++argv) { if (getfname(*argv)) checkfile = 1; } if (!checkfile) /* file(s) specified, but none accessable */ exit(1); } if (fsflg && !checkfile) { /* -f with no files means use wd */ if (getfname(".") == 0) exit(1); checkfile = 1; } procstat = procstat_open(nlistf, memf); if (procstat == NULL) errx(1, "procstat_open()"); p = procstat_getprocs(procstat, what, arg, &cnt); if (p == NULL) errx(1, "procstat_getprocs()"); /* * 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"); if (checkfile && fsflg == 0) printf(" NAME\n"); else putchar('\n'); /* * Go through the process list. */ for (i = 0; i < cnt; i++) { if (p[i].ki_stat == SZOMB) continue; dofiles(procstat, &p[i]); } procstat_freeprocs(procstat, p); procstat_close(procstat); return (0); } static void dofiles(struct procstat *procstat, struct kinfo_proc *kp) { const char *cmd; const char *uname; struct filestat *fst; struct filestat_list *head; int pid; uname = user_from_uid(kp->ki_uid, 0); pid = kp->ki_pid; cmd = kp->ki_comm; head = procstat_getfiles(procstat, kp, mflg); if (head == NULL) return; STAILQ_FOREACH(fst, head, next) print_file_info(procstat, fst, uname, cmd, pid); procstat_freefiles(procstat, head); } static void print_file_info(struct procstat *procstat, struct filestat *fst, const char *uname, const char *cmd, int pid) { struct vnstat vn; DEVS *d; const char *filename; int error, fsmatch = 0; + char errbuf[_POSIX2_LINE_MAX]; filename = NULL; if (checkfile != 0) { if (fst->fs_type != PS_FST_TYPE_VNODE && fst->fs_type != PS_FST_TYPE_FIFO) return; - error = procstat_get_vnode_info(procstat, fst, &vn, NULL); + error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); if (error != 0) return; for (d = devs; d != NULL; d = d->next) if (d->fsid == vn.vn_fsid) { fsmatch = 1; if ((unsigned)d->ino == vn.vn_fileid) { filename = d->name; break; } } if (fsmatch == 0 || (filename == NULL && fsflg == 0)) return; } /* * 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); /* * Print type-specific data. */ switch (fst->fs_type) { case PS_FST_TYPE_FIFO: case PS_FST_TYPE_VNODE: print_vnode_info(procstat, fst); break; case PS_FST_TYPE_SOCKET: print_socket_info(procstat, fst); break; case PS_FST_TYPE_PIPE: print_pipe_info(procstat, fst); break; case PS_FST_TYPE_PTS: print_pts_info(procstat, fst); break; default: if (vflg) fprintf(stderr, "unknown file type %d for file %d of pid %d\n", fst->fs_type, fst->fs_fd, pid); } if (filename && !fsflg) printf(" %s", filename); putchar('\n'); } static void print_socket_info(struct procstat *procstat, struct filestat *fst) { static const char *stypename[] = { "unused", /* 0 */ "stream", /* 1 */ "dgram", /* 2 */ "raw", /* 3 */ "rdm", /* 4 */ "seqpak" /* 5 */ }; #define STYPEMAX 5 struct sockstat sock; struct protoent *pe; char errbuf[_POSIX2_LINE_MAX]; int error; static int isopen; error = procstat_get_socket_info(procstat, fst, &sock, errbuf); if (error != 0) { printf("* error"); return; } if (sock.type > STYPEMAX) printf("* %s ?%d", sock.dname, sock.type); else printf("* %s %s", sock.dname, stypename[sock.type]); /* * protocol specific formatting * * Try to find interesting things to print. For tcp, the interesting * thing is the address of the tcpcb, for udp and others, just the * inpcb (socket pcb). For unix domain, its the address of the socket * pcb and the address of the connected pcb (if connected). Otherwise * just print the protocol number and address of the socket itself. * The idea is not to duplicate netstat, but to make available enough * information for further analysis. */ switch (sock.dom_family) { case AF_INET: case AF_INET6: if (!isopen) setprotoent(++isopen); if ((pe = getprotobynumber(sock.proto)) != NULL) printf(" %s", pe->p_name); else printf(" %d", sock.proto); if (sock.proto == IPPROTO_TCP ) { if (sock.inp_ppcb != 0) printf(" %lx", (u_long)sock.inp_ppcb); } else if (sock.so_pcb != 0) printf(" %lx", (u_long)sock.so_pcb); break; case AF_UNIX: /* print address of pcb and connected pcb */ if (sock.so_pcb != 0) { printf(" %lx", (u_long)sock.so_pcb); if (sock.unp_conn) { char shoconn[4], *cp; cp = shoconn; if (!(sock.so_rcv_sb_state & SBS_CANTRCVMORE)) *cp++ = '<'; *cp++ = '-'; if (!(sock.so_snd_sb_state & SBS_CANTSENDMORE)) *cp++ = '>'; *cp = '\0'; printf(" %s %lx", shoconn, (u_long)sock.unp_conn); } } break; default: /* print protocol number and socket address */ printf(" %d %lx", sock.proto, (u_long)sock.so_addr); } } static void print_pipe_info(struct procstat *procstat, struct filestat *fst) { struct pipestat ps; char errbuf[_POSIX2_LINE_MAX]; int error; error = procstat_get_pipe_info(procstat, fst, &ps, errbuf); if (error != 0) { printf("* error"); return; } printf("* pipe %8lx <-> %8lx", (u_long)ps.addr, (u_long)ps.peer); printf(" %6zd", ps.buffer_cnt); print_access_flags(fst->fs_fflags); } static void print_pts_info(struct procstat *procstat, struct filestat *fst) { struct ptsstat pts; char errbuf[_POSIX2_LINE_MAX]; int error; error = procstat_get_pts_info(procstat, fst, &pts, errbuf); if (error != 0) { printf("* error"); return; } printf("* pseudo-terminal master "); if (nflg || !*pts.devname) { printf("%10d,%-2d", major(pts.dev), minor(pts.dev)); } else { printf("%10s", pts.devname); } print_access_flags(fst->fs_fflags); } static void print_vnode_info(struct procstat *procstat, struct filestat *fst) { struct vnstat vn; char errbuf[_POSIX2_LINE_MAX]; char mode[15]; const char *badtype; int error; badtype = NULL; error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); if (error != 0) badtype = errbuf; else if (vn.vn_type == PS_FST_VTYPE_VBAD) badtype = "bad"; else if (vn.vn_type == PS_FST_VTYPE_VNON) badtype = "none"; if (badtype != NULL) { printf(" - - %10s -", badtype); return; } if (nflg) printf(" %2d,%-2d", major(vn.vn_fsid), minor(vn.vn_fsid)); else if (vn.vn_mntdir != NULL) (void)printf(" %-8s", vn.vn_mntdir); /* * Print access mode. */ if (nflg) (void)snprintf(mode, sizeof(mode), "%o", vn.vn_mode); else { strmode(vn.vn_mode, mode); } (void)printf(" %6lld %10s", 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(" %2d,%-2d", major(vn.vn_dev), minor(vn.vn_dev)); else { printf(" %6s", vn.vn_devname); } } else printf(" %6llu", vn.vn_size); print_access_flags(fst->fs_fflags); } static void print_access_flags(int flags) { char rw[3]; rw[0] = '\0'; if (flags & PS_FST_FFLAG_READ) strcat(rw, "r"); if (flags & PS_FST_FFLAG_WRITE) strcat(rw, "w"); printf(" %2s", rw); } int getfname(const char *filename) { struct stat statbuf; DEVS *cur; if (stat(filename, &statbuf)) { warn("%s", filename); return (0); } if ((cur = malloc(sizeof(DEVS))) == NULL) err(1, NULL); cur->next = devs; devs = cur; cur->ino = statbuf.st_ino; cur->fsid = statbuf.st_dev; cur->name = filename; return (1); } static void usage(void) { (void)fprintf(stderr, "usage: fstat [-fmnv] [-M core] [-N system] [-p pid] [-u user] [file ...]\n"); exit(1); } Index: projects/libprocstat/usr.bin/fstat/fuser.c =================================================================== --- projects/libprocstat/usr.bin/fstat/fuser.c (revision 196016) +++ projects/libprocstat/usr.bin/fstat/fuser.c (revision 196017) @@ -1,366 +1,367 @@ /*- * Copyright (c) 2005-2009 Stanislav Sedov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "functions.h" #include "libprocstat.h" /* * File access mode flags table. */ struct { int flag; char ch; } fflags[] = { {PS_FST_FFLAG_WRITE, 'w'}, {PS_FST_FFLAG_APPEND, 'a'}, {PS_FST_FFLAG_DIRECT, 'd'}, {PS_FST_FFLAG_SHLOCK, 's'}, {PS_FST_FFLAG_EXLOCK, 'e'} }; #define NFFLAGS (sizeof(fflags) / sizeof(*fflags)) /* * Usage flags translation table. */ struct { int flag; char ch; } uflags[] = { {PS_FST_UFLAG_RDIR, 'r'}, {PS_FST_UFLAG_CDIR, 'c'}, {PS_FST_UFLAG_JAIL, 'j'}, {PS_FST_UFLAG_TRACE, 't'}, {PS_FST_UFLAG_TEXT, 'x'}, {PS_FST_UFLAG_MMAP, 'm'}, {PS_FST_UFLAG_CTTY, 'y'} }; #define NUFLAGS (sizeof(uflags) / sizeof(*uflags)) struct consumer { pid_t pid; uid_t uid; int fd; int flags; int uflags; STAILQ_ENTRY(consumer) next; }; struct reqfile { uint32_t fsid; uint64_t fileid; const char *name; STAILQ_HEAD(, consumer) consumers; }; /* * Option flags. */ #define UFLAG 0x01 /* -u flag: show users */ #define FFLAG 0x02 /* -f flag: specified files only */ #define CFLAG 0x04 /* -c flag: treat as mpoints */ #define MFLAG 0x10 /* -m flag: mmapped files too */ #define KFLAG 0x20 /* -k flag: send signal (SIGKILL by default) */ static int flags = 0; /* Option flags. */ static void printflags(struct consumer *consumer); static int str2sig(const char *str); static void usage(void) __dead2; static int addfile(const char *path, struct reqfile *reqfile); static void dofiles(struct procstat *procstat, struct kinfo_proc *kp, struct reqfile *reqfiles, size_t nfiles); static void usage(void) { fprintf(stderr, "usage: fuser [-cfhkmu] [-M core] [-N system] [-s signal] file ...\n"); exit(EX_USAGE); } static void printflags(struct consumer *cons) { unsigned int i; assert(cons); for (i = 0; i < NUFLAGS; i++) if ((cons->uflags & uflags[i].flag) != 0) fputc(uflags[i].ch, stderr); for (i = 0; i < NFFLAGS; i++) if ((cons->flags & fflags[i].flag) != 0) fputc(fflags[i].ch, stderr); } /* * Add file to the list. */ static int addfile(const char *path, struct reqfile *reqfile) { struct stat sb; assert(path); if (stat(path, &sb) != 0) { warn("%s", path); return (1); } reqfile->fileid = sb.st_ino; reqfile->fsid = sb.st_dev; reqfile->name = path; STAILQ_INIT(&reqfile->consumers); return (0); } int do_fuser(int argc, char *argv[]) { struct consumer *consumer; struct kinfo_proc *p, *procs; struct procstat *procstat; struct reqfile *reqfiles; char *ep, *nlistf, *memf; int ch, cnt, sig; unsigned int i, nfiles; sig = SIGKILL; /* Default to kill. */ nlistf = NULL; memf = NULL; while ((ch = getopt(argc, argv, "M:N:cfhkms:u")) != -1) switch(ch) { case 'f': if ((flags & CFLAG) != 0) usage(); flags |= FFLAG; break; case 'c': if ((flags & FFLAG) != 0) usage(); flags |= CFLAG; break; case 'N': nlistf = optarg; break; case 'M': memf = optarg; break; case 'u': flags |= UFLAG; break; case 'm': flags |= MFLAG; break; case 'k': flags |= KFLAG; break; case 's': if (isdigit(*optarg)) { sig = strtol(optarg, &ep, 10); if (*ep != '\0' || sig < 0 || sig >= sys_nsig) errx(EX_USAGE, "illegal signal number" ": %s", optarg); } else { sig = str2sig(optarg); if (sig < 0) errx(EX_USAGE, "illegal signal name: " "%s", optarg); } break; case 'h': /* PASSTHROUGH */ default: usage(); /* NORETURN */ } argv += optind; argc -= optind; assert(argc >= 0); if (argc == 0) usage(); /* NORETURN */ /* * Process named files. */ reqfiles = malloc(argc * sizeof(struct reqfile)); if (reqfiles == NULL) err(EX_OSERR, "malloc()"); nfiles = 0; while (argc--) if (!addfile(*(argv++), &reqfiles[nfiles])) nfiles++; if (nfiles == 0) errx(EX_IOERR, "files not accessible"); procstat = procstat_open(nlistf, memf); if (procstat == NULL) errx(1, "procstat_open()"); procs = procstat_getprocs(procstat, KERN_PROC_PROC, 0, &cnt); if (procs == NULL) errx(1, "procstat_getprocs()"); /* * Walk through process table and look for matching files. */ p = procs; while(cnt--) if (p->ki_stat != SZOMB) dofiles(procstat, p++, reqfiles, nfiles); for (i = 0; i < nfiles; i++) { fprintf(stderr, "%s:", reqfiles[i].name); fflush(stderr); STAILQ_FOREACH(consumer, &reqfiles[i].consumers, next) { if (consumer->flags != 0) { fprintf(stdout, "%6d", consumer->pid); fflush(stdout); printflags(consumer); if ((flags & UFLAG) != 0) fprintf(stderr, "(%s)", user_from_uid(consumer->uid, 0)); if ((flags & KFLAG) != 0) kill(consumer->pid, sig); fflush(stderr); } } (void)fprintf(stderr, "\n"); } procstat_freeprocs(procstat, procs); procstat_close(procstat); free(reqfiles); return (0); } static void dofiles(struct procstat *procstat, struct kinfo_proc *kp, struct reqfile *reqfiles, size_t nfiles) { struct vnstat vn; struct consumer *cons; struct filestat *fst; struct filestat_list *head; int error, match; unsigned int i; + char errbuf[_POSIX2_LINE_MAX]; head = procstat_getfiles(procstat, kp, flags & MFLAG); if (head == NULL) return; STAILQ_FOREACH(fst, head, next) { if (fst->fs_type != PS_FST_TYPE_VNODE) continue; - error = procstat_get_vnode_info(procstat, fst, &vn, NULL); + error = procstat_get_vnode_info(procstat, fst, &vn, errbuf); if (error != 0) continue; for (i = 0; i < nfiles; i++) { if (flags & CFLAG && reqfiles[i].fsid == vn.vn_fsid) { break; } else if (reqfiles[i].fsid == vn.vn_fsid && reqfiles[i].fileid == vn.vn_fileid) { break; } else if (!(flags & FFLAG) && (vn.vn_type == PS_FST_VTYPE_VCHR || vn.vn_type == PS_FST_VTYPE_VBLK) && vn.vn_fsid == reqfiles[i].fileid) { break; } } if (i == nfiles) continue; /* No match. */ /* * Look for existing entries. */ match = 0; STAILQ_FOREACH(cons, &reqfiles[i].consumers, next) if (cons->pid == kp->ki_pid) { match = 1; break; } if (match == 1) { /* Use old entry. */ cons->flags |= fst->fs_fflags; cons->uflags |= fst->fs_uflags; } else { /* * Create new entry in the consumer chain. */ cons = calloc(1, sizeof(struct consumer)); if (cons == NULL) { warn("malloc()"); continue; } cons->uid = kp->ki_uid; cons->pid = kp->ki_pid; cons->uflags = fst->fs_uflags; cons->flags = fst->fs_fflags; STAILQ_INSERT_TAIL(&reqfiles[i].consumers, cons, next); } } procstat_freefiles(procstat, head); } /* * Returns signal number for it's string representation. */ static int str2sig(const char *str) { int i; #define SIGPREFIX "sig" if (!strncasecmp(str, SIGPREFIX, sizeof(SIGPREFIX))) str += sizeof(SIGPREFIX); for (i = 1; i < sys_nsig; i++) { if (!strcasecmp(sys_signame[i], str)) return (i); } return (-1); }