rsk@j.cc.purdue.edu.UUCP (05/14/87)
I have taken ofiles and rewritten it to work correctly for remote filesystems, character special devices, and block special devices (as devices). I have tested it on Sun 3.2 and 4.3+NFS (Wisconsin), but I'm sure I haven't got it quite right for the sequent (we don't have one). I certainly don't have the #includes right. Perhaps someone with access to a sequent could get it working and mail me their working copy, so I can include it in the further enhancements I am making. I used getopt() - if you don't have a copy you can get it from any mod.sources archive or send me mail and I'll send you a copy. This posting doesn't include a man page - the only added flag is "-f" which prevents looking up arguments in /etc/mtab - useful if you want to see if anyone has your filesystems' block special devices open as devices. Because of the the way /dev/tty is implemented, you can't say "ofiles /dev/tty" to find processes with their controlling tty open. But "ofiles /dev/ttya" or "ofiles /dev/null" works fine. @alex --- arpanet: dupuy@columbia.edu uucp: ...!seismo!columbia!dupuy : This is a shar archive. Extract with sh, not csh. : The rest of this file will extract: : : ofiles.c : echo x - ofiles.c sed 's/^X//' > ofiles.c << '//go.sysin dd *' X#include <sys/param.h> X#include <sys/dir.h> X#include <sys/user.h> X#ifdef sequent X# define KERNEL X# include <sys/file.h> X# include <sys/vnode.h> X# include <sys/vfs.h> X# include <sys/inode.h> X# include <rpc/types.h> X# include <nfs/nfs.h> X# include <netinet/in.h> X# include <nfs/nfs_clnt.h> X# include <nfs/rnode.h> X# undef KERNEL X#else X# define KERNEL X# include <sys/file.h> X# undef KERNEL X# include <sys/vnode.h> X# include <sys/vfs.h> X# include <ufs/inode.h> X# include <rpc/types.h> X# include <nfs/nfs.h> X# include <netinet/in.h> X# include <nfs/nfs_clnt.h> X# include <nfs/rnode.h> X#endif X#include <machine/pte.h> X#include <sys/vmmac.h> X#include <sys/proc.h> X#include <nlist.h> X#include <sys/stat.h> X#include <strings.h> X#include <stdio.h> X#include <mntent.h> X#include <pwd.h> X Xunion fileid X{ X struct X { X long fsid; X long nodeid; X } file; X struct X { X dev_t dev; X enum vtype type; X } special; X}; X X#define CDIR 01 X#define OFILE 02 X#define RDIR 04 X Xlong eseek (); Xint eread (); Xchar *vtype (); Xchar *itype (); Xchar *rtype (); Xstruct mntent *getmntname (); Xstruct mntent *getmntfile (); Xunion fileid *getfileid (); Xstruct user *getuser (); X X#define error(s) (perror ((s) ? (s) : ""), exit (1)) X X#define istype(mode,type) (((mode) & S_IFMT) == (type)) X#define ispecial(mode) (istype ((mode), S_IFBLK) || istype ((mode), S_IFCHR)) X X#define vspecial(vtype) ((vtype) == VCHR || (vtype) == VBLK) X X#define nfsdev(rdev) (major (rdev) == 0xff) X X/* X * File descriptors. X */ X Xint mem; /* fd for /dev/mem */ Xint kmem; /* fd for /dev/kmem */ Xint swap; /* fd for /dev/drum */ X XFILE *mtab; /* stdio file for /etc/mtab */ X X/* X * Kernel tables. X */ X X#ifdef sequent X# define UNIX "/dynix" X#else X# define UNIX "/vmunix" X#endif Xstruct nlist nl[] = X{ X#define X_PROC 0 X {"_proc"}, X#define X_NPROC 1 X {"_nproc"}, X#define X_USRPTMA 2 X {"_Usrptmap"}, X#define X_USRPT 3 X {"_usrpt"}, X {0} X}; Xlong procbase; /* address of process table */ Xint nproc; /* number of entries in proc table */ Xstruct pte *Usrptma, /* user page table map */ X *usrpt; /* user page table */ X Xint inodes[NOFILE]; /* inodes of open files */ X X/* X * Program options. X */ X Xchar *progname; Xint terse = 0; /* if non-zero, only output pids */ Xint debug = 10; /* turn on debugging output */ Xint filesonly = 0; /* match args as files, not mntents */ Xint inode = 0; /* if non-zero, inode to match */ X Xmain (argc, argv) Xint argc; Xchar *argv[]; X{ X X char filename[MNTMAXSTR], X fsname[MNTMAXSTR]; X struct stat stats; X struct mntent *mnt; X X extern char *optarg; X extern int optind; X int option; X int usage = 0; X X progname = argv[0]; X X while ((option = getopt (argc, argv, "pfd:")) != EOF) X { X switch (option) X { X case 'p': X terse = 1; X break; X X case 'f': X filesonly = 1; X break; X X case 'd': X debug = atoi (optarg); X break; X X case '?': X usage = 1; X } X } X if (!(argc -= optind) || usage) X { X fprintf (stderr, "usage: %s [-p] files\n", progname); X exit (1); X } X X if ((mem = open ("/dev/mem", 0)) < 0) X error ("can't open /dev/mem. "); X if ((kmem = open ("/dev/kmem", 0)) < 0) X error ("can't open /dev/kmem. "); X if ((swap = open ("/dev/drum", 0)) < 0) X error ("can't open /dev/drum. "); X X if ((mtab = setmntent (MOUNTED, "r")) == 0) X error ("can't open /etc/mtab. "); X X getsyms (); X X for (argv += optind; argc--; argv++) X { X /* X * Check for filesystem/mountpoint vs. file. X */ X X if (!filesonly && (mnt = getmntname (*argv)) != NULL) X { X inode = 0; /* match any inode on this fs */ X strcpy (filename, mnt->mnt_dir); X strcpy (fsname, mnt->mnt_fsname); X } X else X { X inode = 1; /* match only one inode */ X strcpy (filename, *argv); X } X X if (stat (filename, &stats)) /* find out what it is we want */ X { X perror (filename); X continue; X } X X if (inode) /* looking for file */ X { X inode = stats.st_ino; X if ((mnt = getmntfile (&stats)) != NULL) X strcpy (fsname, mnt->mnt_fsname); X else X strcpy (fsname, "unknown"); X } X X if (!terse) X { X printf ("%s:\t%s\n", fsname, filename); X X if (debug > 5) X if (ispecial (stats.st_mode)) X printf (" %s device %d/%d\n", itype (stats.st_mode), X major (stats.st_rdev), minor (stats.st_rdev)); X else if (nfsdev (stats.st_dev)) X printf (" inode %d on remote fs %d (%s)\n", X stats.st_ino, minor (stats.st_dev), X itype (stats.st_mode)); X else X printf (" inode %d on block device %x (%s)\n", X stats.st_ino, stats.st_dev, itype (stats.st_mode)); X X printf ("user\t process command\ttype\tinode(s)\n"); X } X X lookfor (&stats); X } X} X Xstruct mntent * Xgetmntname (name) Xchar *name; X{ X register struct mntent *mnt; X X rewind (mtab); X while ((mnt = getmntent (mtab)) != 0) X { X if (strcmp (name, mnt->mnt_fsname) == 0) X return (mnt); X if (strcmp (name, mnt->mnt_dir) == 0) X return (mnt); X } X return (NULL); X} X Xstruct mntent * Xgetmntfile (filestats) Xstruct stat *filestats; X{ X register struct mntent *mnt; X struct stat dirstats; X X rewind (mtab); X while ((mnt = getmntent (mtab)) != 0) X if ((stat (mnt->mnt_dir, &dirstats) >= 0) && X (filestats->st_dev == dirstats.st_dev)) X return (mnt); X X return (NULL); X} X Xlookfor (filestats) Xstruct stat *filestats; X{ X struct proc p; X struct user *u; X register int procn, X flags, X filen; X X for (procn = 0; procn < nproc; procn++) X { X procslot (procn, &p); X flags = 0; X if (p.p_stat == 0 || p.p_stat == SZOMB) X continue; X u = getuser (&p); X if (u == (struct user *) NULL) X continue; X X if (debug > 10) X printf ("uid %d pid %d command %s\n", p.p_uid, p.p_pid, u->u_comm); X X if (u->u_rdir != NULL) X if (check (filestats, getfileid (u->u_rdir, "rdir"))) X flags |= RDIR; X X if (u->u_cdir != NULL) X if (check (filestats, getfileid (u->u_cdir, "cdir"))) X flags |= CDIR; X X for (filen = 0; filen < NOFILE; filen++) X { X struct file f; X union fileid *fid; X X inodes[filen] = 0; X X if (u->u_ofile[filen] == NULL) X continue; X X eseek (kmem, (long) u->u_ofile[filen], 0, "file"); X eread (kmem, (char *) &f, sizeof (f), "file"); X X if (f.f_count > 0) X { X if (f.f_type != DTYPE_VNODE) X continue; X X fid = getfileid ((struct vnode *) f.f_data, "ofile"); X if (check (filestats, fid)) X { X flags |= OFILE; X if (!ispecial (filestats->st_mode)) X inodes[filen] = fid->file.nodeid; X } X } X } X if (flags) X gotone (u, &p, flags); X } X} X X/* X * Print the name of the user owning process "p" and the pid of that process X */ X Xgotone (u, p, f) Xstruct user *u; Xstruct proc *p; Xint f; X{ X struct passwd *getpwuid (); X register struct passwd *pw; X register int filen; X X if (terse) /* only print pids and return */ X { X printf ("%d ", p->p_pid); X fflush (stdout); X return; X } X X pw = getpwuid (p->p_uid); X if (pw) X printf ("%-8.8s", pw->pw_name); X else X printf ("(%d)\t", p->p_uid); X X printf (" %d\t", p->p_pid); X X printf (" %-14.14s\t", u->u_comm); X X if (f & CDIR) X putchar ('c'); /* proc's current dir is on device */ X if (f & RDIR) X putchar ('r'); /* proc's root dir is on device */ X if (f & OFILE) X putchar ('f'); /* proc has file(s) open on device */ X printf ("\t"); X X if (f & OFILE) /* list inodes of open file(s) */ X for (filen = 0; filen < NOFILE; filen++) X if (inodes[filen] > 0) X printf ("%d ", inodes[filen]); X X puts (""); /* silly way to print a newline */ X} X X X/* X * Check if file id matches stats we are searching for. X */ X Xcheck (filestats, fid) Xstruct stat *filestats; Xunion fileid *fid; X{ X if (fid == NULL) X return (0); X X if (ispecial (filestats->st_mode)) X { X if (filestats->st_rdev == fid->special.dev X && !strcmp (itype (filestats->st_mode), vtype (fid->special.type))) X return (1); /* for special devices */ X else X return (0); X } X X if (filestats->st_dev == fid->file.fsid) X { X if (!inode) /* if we'll match any file */ X return (1); X X if (inode == fid->file.nodeid) X return (1); X#ifdef sun /* not sure I understand this */ X else if (fid->file.nodeid == 0) X return (1); X#endif X } X X return (0); X} X X/* X * Read a vnode from /dev/kmem, return fileid (dev,ino). X */ X Xunion fileid * Xgetfileid (node, s) Xstruct vnode *node; Xchar *s; X{ X struct vnode v; X static union xnode X { X struct inode i; X struct rnode r; X } x; X X union fileid fid; X X if (node == NULL) X return (NULL); X X eseek (kmem, (long) node, 0, "vnode"); X eread (kmem, (char *) &v, sizeof (v), "vnode"); X X if (vspecial (v.v_type)) X { X fid.special.dev = v.v_rdev; X fid.special.type = v.v_type; X X if (debug > 20) X printf (" %s %s device %d/%d\n", X s, vtype (v.v_type), major (v.v_rdev), minor (v.v_rdev)); X return (&fid); X } X X if (debug > 20) X printf (" %s %s", s, vtype (v.v_type)); X X eseek (kmem, (long) v.v_data, 0, "inode/rnode"); X eread (kmem, (char *) &x, sizeof (x), "inode/rnode"); X X if (x.r.r_nfsattr.na_type == v.v_type) X { /* best test I could find for nfs */ X struct vfs vf; X struct mntinfo mi; X X eseek (kmem, (long) v.v_vfsp, 0, "vfs"); X eread (kmem, (char *) &vf, sizeof (vf), "vfs"); X X eseek (kmem, (long) vf.vfs_data, 0, "mntinfo"); X eread (kmem, (char *) &mi, sizeof (mi), "mntinfo"); X X fid.file.fsid = makedev (0xff, mi.mi_mntno); X fid.file.nodeid = x.r.r_nfsattr.na_nodeid; X X if (debug > 20) X printf (" inode %d on nfs fs %x (%s)\n", X x.r.r_nfsattr.na_nodeid, x.r.r_nfsattr.na_fsid, X rtype (x.r.r_nfsattr.na_type)); X } X else X { X fid.file.fsid = x.i.i_dev; X fid.file.nodeid = x.i.i_number; X X if (debug > 20) X printf (" inode %d on device %x (%s)\n", X x.i.i_number, x.i.i_dev, itype (x.i.i_mode)); X } X X return (&fid); X} X X Xchar * Xvtype (type) Xenum vtype type; X{ X switch (type) X { X case VNON: X return ("none"); X case VREG: X return ("file"); X case VDIR: X return ("directory"); X case VBLK: X return ("block special"); X case VCHR: X return ("character special"); X case VLNK: X return ("symbolic link"); X case VSOCK: X return ("socket"); X case VBAD: X return ("bad type"); X#ifdef S_IFIFO X case VFIFO: X return ("fifo"); X#endif X default: X return ("unknown vnode type"); X } X} X Xchar * Xrtype (type) Xenum nfsftype type; X{ X switch (type) X { X case NFNON: X return ("none"); X case NFREG: X return ("file"); X case NFDIR: X return ("directory"); X case NFBLK: X return ("block special"); X case NFCHR: X return ("character special"); X case NFLNK: X return ("symbolic link"); X default: X return ("unknown"); X } X} X Xchar * Xitype (mode) Xunsigned mode; X{ X switch (mode & S_IFMT) X { X case S_IFREG: X return ("file"); X case S_IFDIR: X return ("directory"); X case S_IFBLK: X return ("block special"); X case S_IFCHR: X return ("character special"); X case S_IFLNK: X return ("symbolic link"); X case S_IFSOCK: X return ("socket"); X#ifdef S_IFIFO X case S_IFIFO: X return ("fifo"); X#endif X default: X return ("unknown inode type"); X } X} X X/* X * get user page for proc "p" from core or swap return pointer to user struct X */ Xstruct user * Xgetuser (p) Xstruct proc *p; X{ X struct pte *ptep; X struct pte mypgtbl[UPAGES]; X int n, X nbytes; X char *up; X static struct user user; X X /* easy way */ X if ((p->p_flag & SLOAD) == 0) X { X eseek (swap, (long) dtob (p->p_swaddr), 0, "user"); X if (read (swap, (char *) &user, sizeof (struct user)) <= 0) X { X if (debug) X fprintf (stderr, "error: can't get swapped user page pid %d\n", X p->p_pid); X return (struct user *) NULL; X } X if (debug > 50) X printf ("read swap\n"); X } X else X { /* boo */ X /* now get user page tbl */ X#ifdef sequent X eseek (mem, p->p_upte, 0L, "user page table"); X if (read (mem, mypgtbl, sizeof (mypgtbl)) != sizeof (mypgtbl)) X { X#else X eseek (kmem, p->p_addr, 0L, "user page table"); X if (read (kmem, mypgtbl, sizeof (mypgtbl)) != sizeof (mypgtbl)) X { X#endif X if (debug) X fprintf (stderr, "error: can't get mypgtbl pid %d\n", X p->p_pid); X return (struct user *) NULL; X } X /* now collect pages of u area */ X up = (char *) &user; X ptep = mypgtbl; X for (nbytes = sizeof (struct user); nbytes > 0; nbytes -= NBPG) X { X eseek (mem, ptep++->pg_pfnum * NBPG, 0L, "page table entry"); X n = MIN (nbytes, NBPG); X if (read (mem, up, n) != n) X { X if (debug) X fprintf (stderr, "cant get user tbl pid %d\n", p->p_pid); X return (struct user *) NULL; X } X up += n; X } X } X return &user; X} X X X/* X * Get symbols from the kernel. X */ X Xgetsyms () X{ X register i; X X if (nlist (UNIX, nl)) X perror (UNIX), exit (1); X X for (i = 0; i < (sizeof (nl) / sizeof (nl[0])) - 1; i++) X if (nl[i].n_value == 0) X { X fprintf (stderr, "%s: can't nlist for %s.\n", X progname, nl[i].n_name); X exit (1); X } X X eseek (kmem, (long) nl[X_PROC].n_value, 0, "procbase"); X eread (kmem, &procbase, (unsigned) sizeof (procbase), "procbase"); X X eseek (kmem, (long) nl[X_NPROC].n_value, 0, "nproc"); X eread (kmem, &nproc, sizeof (nproc), "nproc"); X X /* variables used by <vmmac.h> */ X X Usrptma = (struct pte *) nl[X_USRPTMA].n_value; X usrpt = (struct pte *) nl[X_USRPT].n_value; X} X X X/* X * Read proc table entry "n" into buffer "p" . X */ Xprocslot (n, p) Xint n; Xstruct proc *p; X{ X eseek (kmem, procbase + (long) (n * sizeof (struct proc)), 0, "proc"); X eread (kmem, (char *) p, sizeof (struct proc), "proc"); X} X X/* X * Read with error checking X */ Xeread (fd, p, size, s) Xint fd; Xchar *p; Xint size; Xchar *s; X{ X int n; X char buf[100]; X if ((n = read (fd, p, size)) != size) X { X sprintf (buf, "read error for %s. ", s); X error (buf); X } X return n; X} X X/* X * Seek with error checking X */ X Xlong Xeseek (fd, off, whence, s) Xint fd; Xlong off; Xint whence; Xchar *s; X{ X long lseek (); X long ret; X char buf[100]; X X if ((ret = lseek (fd, off, whence)) != off) X { X sprintf (buf, "seek for %s failed, wanted %o, got %o. ", X s, off, ret); X error (buf); X } X return ret; X} //go.sysin dd * exit