[comp.sources.misc] v03i090: fuser for 386 xenix

jfh@rpp386.UUCP (The Beach Bum) (07/13/88)

Posting-number: Volume 3, Issue 90
Submitted-by: "The Beach Bum" <jfh@rpp386.UUCP>
Archive-name: xenix-fuser

brando -

ha!  rich $alz last hacked on this before me so far as i can tell.  this
probably belongs to him (for posting) but working on 386 xenix only seems
as .misc as it gets.

the next major submission will be a reimplementation of crash which runs
on, you guessed it, 386 xenix only!  i'm not into writing portable code
this week.

- john.
--
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	README
#	fuser.1
#	Makefile
#	fuser.c.sco
#	fuser.c.u-pc
# This archive created: Sun Jul  3 11:21:31 1988
# By:	The Beach Bum (Big "D" Home for Wayward Hackers)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
X[  This has been ported to SCO Xenix for the Intel 80386.  There are
X   two versions of the program.  The differences were large than the
X   entire sharchive, so I saw no point in including context diffs.
X   The Makefile should explain everything.  Ideally, the machine
X   dependencies would be separated into different files. - john. ]
X
X[  This program takes a filename, searches the kernel's open file
X   table, and determines who has that file open.  You can to kill the
X   offending process(es).  This is useful for backups, e.g.  A port of
X   this to BSD and other Unices would be nice.  --r$  ]
X
XHere is the latest source to my version of the fuser(1M) command, written
Xand running on the Unix PC.  A man page and makefile are included.
X
XVerify that the macros in "Makefile" are correct for you (LBIN, for example
Xshould be where you want the program to be installed), and run make AS ROOT.
XIt is important to do the make as root so that the resulting program will
Xhave the set-uid bit set, allowing it to read the kernel's memory.
X
X
XSome notes to anyone who wants to port this program to something other
Xthan the Unix PC:
X
XThe Unix PC has "tunable kernel parameters", which mean that things that
Xare constants on most older Unix systems are variable.  This version of
Xfuser reads the values of these variables from the kernel's memory.  I have
Xtried to make most of this transparent, for ease of porting to a more plain-
Xvanilla Unix.  For example, I have 'int' varialbes called 'NOFILE', 'NPROC',
Xetc., which emulate the #defines present in 'standard' Unix.  To make it
Xrun on such systems, it should be possible to remove these variables and
Xthe code that sets them, and the program will use the #defines from
X<sys/*.h>.  The only other significant change is that because NOFILE is
Xvariable, the u_ofile field in the user structure is a pointer rather than
Xan array.  This means that you can delete the code that copies the u_ofile
Xlist since it is already right in the user structure.
X
X
X					-=] Ford [=-
X
X"GNU does not eliminate			(In Real Life:  Michael Ditto)
Xall the world's problems,		kenobi!ford@crash.CTS.COM
Xonly some of them." -rms		...!crash!kenobi!ford
X
X
XHere's the shar file:
SHAR_EOF
fi
if test -f 'fuser.1'
then
	echo shar: "will not over-write existing file 'fuser.1'"
else
sed 's/^X//' << \SHAR_EOF > 'fuser.1'
X.TH FUSER 1M
X.SH NAME
Xfuser \- find processes using a file or filesystem
X.SH SYNOPSIS
X.B fuser
X[-ku] files [...]
X.SH DESCRIPTION
X.I Fuser
Xsearches the kernel's internal tables to find all processes that are
Xaccessing the listed
X.I files.
XIf one of the
X.I files
Xis a block special file (such as a disk) then fuser finds processes that
Xare accessing any file on that device.
X.P
X.I Fuser
Xlists on its standard output the process ID of each process found
Xto be using any of the files
Xspecified.  The process IDs will be followed by the letters
X.B c,
X.B r,
Xor
X.B p,
Xif the file is open as the current directory, root directory, or
Xparent directory of the process, respectively.  (The kernel has a process'
Xparent directory "open" for internal use under certain conditions.)
X.SH OPTIONS
XThe -k option causes
X.I fuser
Xto attempt to kill each process found with the SIGKILL signal (normal
Xpermission controls apply; see kill(2)).
X.P
XThe -u option causes
X.I fuser
Xto print, in parentheses, the user ID of the owner of each process listed.
X.P
XOptions may be re-specified between filenames; a '-' argument by itself
Xturns off the -k and -u options.
X.P
XThe process IDs are written to standard output, one line per file searched
Xfor.  Other output is written to standard error.
X.SH EXAMPLES
Xfuser /tmp/foo
X.br
X.RS
XThis prints the process IDs of all processes that have the file /tmp/foo open.
X.RE
X.P
Xfuser -u /dev/fp021
X.br
X.RS
XThis finds all processes that are accessing any file on the disk /dev/fp021.
XThe process IDs and user names of the process owners are listed.
XThis is useful to find out why a disk can not be unmounted.
X.RE
X.P
Xfuser -k /dev/fp021
X.br
Xumount /dev/fp021
X.br
X.RS
XIf run by super-user, this kills all processes that are preventing
Xthe disk /dev/fp021 from being unmounted, and then unmounts the disk.
X.RE
X.SH BUGS
XIf a process's user area is swapped (happens on some versions of UNIX)
Xfuser will print a warning and ignore that process.
SHAR_EOF
fi
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
XSHELL=/bin/sh
XLBIN=/usr/local/bin
XINSTALL=cp
X
X# Set this to version of .c file you want.  fuser.c.sco is for SCO Xenix
X# for a 386.  fuser.c.u-pc is for Unix System V on an Unix PC.
XFUSER=fuser.c.sco
X# FUSER=fuser.c.u-pc
X
X# Set this to group which owns /dev/swap, /dev/mem and /dev/kmem.  Those
X# files MUST be readable by that group.  Otherwise, the program must be
X# SUID root to be used by anyone other than root, or other special
X# arrangements must be made, all of which could compromise the system.
X# ADMIN=root
XADMIN=sysinfo
X
X# Debugging flags
X# CFLAGS=-DDEBUG -g
X# LDFLAGS=-g
X
X# Production flags
XCFLAGS=-O
XLDFLAGS=-s
X
Xfuser: fuser.o
X	$(CC) $(LDFLAGS) -o fuser fuser.o
X
Xinstall: fuser
X	$(INSTALL) fuser $(LBIN)
X	chown root $(LBIN)/fuser
X	chgrp $(ADMIN) $(LBIN)/fuser
X	chmod 2711 $(LBIN)/fuser
X
Xclean:
X	-rm -f *.o core
X
Xclobber: clean
X	-rm -f fuser.c fuser $(LBIN)/fuser
X
Xfuser.c: $(FUSER)
X	cp $(FUSER) fuser.c
SHAR_EOF
fi
if test -f 'fuser.c.sco'
then
	echo shar: "will not over-write existing file 'fuser.c.sco'"
else
sed 's/^X//' << \SHAR_EOF > 'fuser.c.sco'
X/************************************************************
X *
X * This program was written by me, Mike "Ford" Ditto, and
X * I hereby release it into the public domain in the interest
X * of promoting the development of free, quality software
X * for the hackers and users of the world.
X *
X * Feel free to use, copy, modify, improve, and redistribute
X * this program, but keep in mind the spirit of this
X * contribution; always provide source, and always allow
X * free redistribution (shareware is fine with me).  If
X * you use a significant part of this code in a program of
X * yours, I would appreciate being given the appropriate
X * amount of credit.
X *				-=] Ford [=-
X *
X ************************************************************/
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/sysmacros.h>
X#include <sys/stat.h>
X#include <sys/inode.h>
X#include <sys/file.h>
X#include <sys/seg.h>
X#include <sys/page.h>
X#include <sys/text.h>
X#include <sys/proc.h>
X#include <sys/signal.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#include <sys/var.h>
X
Xextern char *sbrk();
Xextern long lseek();
Xextern void perror(), exit();
Xextern struct passwd *getpwuid();
Xextern char *malloc();
X
X
Xchar *progname;
X
X#define inodeaddr (mysyms[0].xl_value)
X#define fileaddr (mysyms[1].xl_value)
X#define procaddr (mysyms[2].xl_value)
X#define varaddr (mysyms[3].xl_value)
X
Xstruct xlist mysyms[] =
X{
X    { 0,0,0,"_inode" },
X    { 0,0,0,"_file" },
X    { 0,0,0,"_proc" },
X    { 0,0,0,"_v" },
X    { 0,0,0,(char *)0 }
X};
X
Xchar buf[BUFSIZ];
X
Xint kmem, mem, swap, kflag, uflag;
Xint NINODE, NFILE, NPROC;
X
Xstruct inode *inodep;
Xstruct file *filep;
Xstruct proc *procp;
Xstruct var *var;
X
X
X/* main program for fuser(1M), a program which lists */
X/* processes that are using the given file(s) */
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    int status=0;
X
X    progname = *argv;
X
X    setup();
X
X    while (++argv,--argc)
X	if ((*argv)[0]=='-')
X	{
X	    register char c, *i;
X
X	    kflag=uflag=0;
X
X	    i = *argv+1;
X	    while (c= *i++) switch(c)
X	    {
X	    case 'k':
X		++kflag;
X		break;
X	    case 'u':
X		++uflag;
X		break;
X	    default:
X		fprintf(stderr, "%s: bad flag `-%c'\n", progname, c);
X		fprintf(stderr,
X			"Usage: %s [-ku] files [[-] [-ku] files]\n",
X			progname);
X		return -1;
X	    }
X	}
X	else {
X	    printf ("%s: ", *argv);
X	    status += fuser(*argv);
X	    putchar ('\n');
X	}
X    return status;
X}
X
X
X/* a fast, zeroizing, memory allocator for things */
X/* that will never need to be freed */
Xchar *myalloc(nbytes)
Xlong nbytes;
X{
X    register char *ptr;
X
X    ptr = malloc((unsigned) nbytes);
X    if (! ptr)
X    {
X	sprintf(buf, "%s: no memory!", progname);
X	perror(buf);
X	exit(1);
X    }
X    memset (ptr, 0, nbytes);
X
X    return ptr;
X}
X
X
X/* one-time setup of main data structures from the kernel */
Xsetup()
X{
X    if ( (kmem=open("/dev/kmem", O_RDONLY)) < 0 )
X    {
X	sprintf(buf, "%s: can't open /dev/kmem", progname);
X	perror(buf);
X	exit(1);
X    }
X
X    if ( (mem=open("/dev/mem", O_RDONLY)) < 0 )
X    {
X	sprintf(buf, "%s: can't open /dev/mem", progname);
X	perror(buf);
X	exit(1);
X    }
X
X    if ( (swap=open("/dev/swap", O_RDONLY)) < 0 )
X    {
X	sprintf(buf, "%s: can't open /dev/swap", progname);
X	perror(buf);
X	exit(1);
X    }
X
X    if (xlist("/xenix", mysyms))
X    {
X	sprintf(buf, "%s: can't xlist /xenix", progname);
X	perror(buf);
X	exit(1);
X    }
X
X    setuid(getuid());
X
X#ifdef DEBUG
X    fprintf(stderr, "inode:	0x%08lx\n", inodeaddr);
X    fprintf(stderr, "file:	0x%08lx\n", fileaddr);
X    fprintf(stderr, "proc:	0x%08lx\n", procaddr);
X    fprintf(stderr, "var:       0x%08lx\n", varaddr);
X#endif DEBUG
X
X    var = (struct var *)myalloc((long) sizeof (struct var));
X    kcopy((char *)var, varaddr, (long) sizeof (struct var));
X
X    NINODE = var->v_inode;
X    NFILE = var->v_file;
X    NPROC = var->v_proc;
X
X#ifdef DEBUG
X    fprintf(stderr, "NOFILE:	%d\n", NOFILE);
X    fprintf(stderr, "NINODE:	%d\n", NINODE);
X    fprintf(stderr, "NFILE:	%d\n", NFILE);
X    fprintf(stderr, "NPROC:	%d\n", NPROC);
X#endif DEBUG
X
X    inodep = (struct inode *)myalloc((long) sizeof (struct inode) * NINODE);
X    filep = (struct file *)myalloc((long) sizeof (struct file) * NFILE);
X    procp = (struct proc *)myalloc((long) sizeof (struct proc) * NPROC);
X
X    kcopy((char *)inodep, inodeaddr, (long) sizeof (struct inode) * NINODE);
X    kcopy((char *)filep, fileaddr, (long) sizeof (struct file) * NFILE);
X    kcopy((char *)procp, procaddr, (long) sizeof (struct proc) * NPROC);
X}
X
X
X/* copy bytes from physical address space to this process */
Xpcopy(caddr, paddr, nbytes)
Xchar *caddr;
Xlong paddr;
Xlong nbytes;
X{
X    if ( lseek(mem, paddr, 0)<0L ||
X	read(mem, caddr, (unsigned)nbytes) != nbytes )
X    {
X	sprintf(buf, "%s: can't read /dev/mem", progname);
X	perror(buf);
X	exit(1);
X    }
X}
X
X
X/* copy bytes from kernel address space to this process */
Xkcopy(caddr, kaddr, nbytes)
Xchar *caddr;
Xlong kaddr;
Xlong nbytes;
X{
X    if ( lseek(kmem, kaddr, 0)<0L ||
X	read(kmem, caddr, (unsigned)nbytes) != nbytes )
X    {
X	sprintf(buf, "%s: can't read /dev/kmem", progname);
X	perror(buf);
X	exit(1);
X    }
X}
X
Xfindu (proc, slot, user)
Xstruct	proc	*proc;
Xint	slot;
Xstruct	user	*user;
X{
X	long	swapaddr;
X	int	i;
X
X	if ((proc->p_flag & (SSWAP|SSPART)) || ! (proc->p_flag & SLOAD)) {
X		swapaddr = proc->p_addr[0].te_frameno * NBPC;
X		lseek (swap, swapaddr, 0);
X		if (read (swap, user, sizeof *user) < 0)
X			return (0);
X	} else {
X		lseek (mem, proc->p_addr[0].te_frameno * NBPC, 0);
X		if (read (mem, user, sizeof *user) < 0)
X			return (0);
X	}
X	if ((user->u_procp - (struct proc *) procaddr) == slot)
X		return (1);
X	else
X		return (0);
X}
X/* Return a pointer to a local copy of the user structure */
X/* for process number `procidx'.  Returns NULL if procidx */
X/* refers to an invalid (not-in-use or otherwise) slot. */
Xstruct user *getuser(procidx)
Xint procidx;
X{
X    static struct user **users;
X    struct file **ofile;
X    long upage;
X
X#ifdef	DEBUG
X    fprintf (stderr, "Looking for upage for proc %d\n", procidx);
X#endif
X    if (!procp[procidx].p_stat ||
X	procp[procidx].p_stat == SIDL ||
X	procp[procidx].p_stat == SZOMB)
X	return 0;
X
X    if (!users)
X	users = (struct user **)myalloc((long) sizeof (struct user *) * NPROC);
X
X    if (!users[procidx])
X    {
X	/* allocate and copy in the user structure */
X	users[procidx] = (struct user *)myalloc((long) sizeof (struct user));
X	if (! findu (&procp[procidx], procidx, users[procidx]))
X	    return 0;
X    }
X    return users[procidx];
X}
X
X
X/* find all users of the file `name' */
Xfuser(name)
Xchar *name;
X{
X    register i;
X    int filesys;
X    struct stat Stat;
X
X    if (stat(name, &Stat))
X    {
X	sprintf(buf, "%s: can't stat %s", progname, name);
X	perror(buf);
X	return 1;
X    }
X
X    /* see if we are looking for a whole filesystem */
X    filesys = ((Stat.st_mode & S_IFMT) == S_IFBLK);
X
X#ifdef DEBUG
X    if (filesys)
X	fprintf(stderr, "looking for files on dev=%d,%d\n",
X		bmajor(Stat.st_rdev), minor(Stat.st_rdev));
X    else
X	fprintf(stderr, "looking for dev=%d,%d, ino=%d\n",
X		bmajor(Stat.st_dev), minor(Stat.st_dev), Stat.st_ino);
X#endif DEBUG
X
X    for ( i=0 ; i<NINODE ; ++i )
X    {
X	if ( inodep[i].i_count &&
X	     ((filesys && (inodep[i].i_dev == Stat.st_rdev))
X		 || (inodep[i].i_dev == Stat.st_dev &&
X		    inodep[i].i_number == Stat.st_ino)) )
X	{
X#ifdef DEBUG
X	    fprintf(stderr, "Found it!  inodep[%d], i_size is %ld\n",
X		   i, inodep[i].i_size);
X#endif DEBUG
X
X	    iuser((struct inode *)inodeaddr + i);
X	}
X    }
X
X    return 0;
X}
X
X
X#define CHECK(kaddr, type) if (kaddr==kinode) { if (++flag==1) printf(" %d", procp[i].p_pid); if (type) putchar(type); }
X
X/* find all users of the inode at kernel address `kinode' */
Xiuser(kinode)
Xstruct inode *kinode;
X{
X    register int i, j;
X    int flag;
X    struct user *user;
X    struct passwd *pwd;
X    struct text text;
X
X#ifdef DEBUG
X    fprintf(stderr, "Looking for users of inode at kernel address 0x%08lx\n",
X	    kinode);
X#endif DEBUG
X
X    for ( i=0 ; i<NPROC ; ++i )
X	if (user = getuser(i))
X	{
X#ifdef DEBUG
X	    fprintf(stderr, "%03d: pid=%5d addr[0]=%05x addr[1]=%05x\n",
X		    i, procp[i].p_pid, procp[i].p_addr[0], procp[i].p_addr[1]);
X#endif DEBUG
X
X#ifdef DEBUG
X	    fprintf(stderr, "	user = 0x%08lx\n", user);
X#endif DEBUG
X
X	    fflush(stderr);
X	    flag=0;
X	    if (procp[i].p_textp) {
X		kcopy((char *)&text, procp[i].p_textp, (long) sizeof (struct text));
X		CHECK(text.x_iptr, 't');
X	    }
X
X	    CHECK(user->u_cdir, 'c');
X	    CHECK(user->u_rdir, 'r');
X	    CHECK(user->u_pdir, 'p');
X
X	    for ( j=0 ; !flag && j<NOFILE ; ++j )
X		if (user->u_ofile[j])
X		    CHECK(filep[user->u_ofile[j]-(struct file *)fileaddr].f_inode, 0);
X	    fflush(stdout);
X
X	    if (flag)
X	    {
X		if (uflag)
X		{
X		    if ( (pwd=getpwuid((int)procp[i].p_uid)) )
X			fprintf(stderr, "(%s)", pwd->pw_name);
X		    else
X			fprintf(stderr, "(%d)", procp[i].p_uid);
X		}
X		if (kflag && procp[i].p_pid)
X		    if (kill(procp[i].p_pid, SIGKILL))
X		    {
X			sprintf(buf, "%s: can't kill process %d",
X				progname, procp[i].p_pid);
X			perror(buf);
X		    }
X	    }
X	}
X}
SHAR_EOF
fi
if test -f 'fuser.c.u-pc'
then
	echo shar: "will not over-write existing file 'fuser.c.u-pc'"
else
sed 's/^X//' << \SHAR_EOF > 'fuser.c.u-pc'
X/************************************************************
X *
X * This program was written by me, Mike "Ford" Ditto, and
X * I hereby release it into the public domain in the interest
X * of promoting the development of free, quality software
X * for the hackers and users of the world.
X *
X * Feel free to use, copy, modify, improve, and redistribute
X * this program, but keep in mind the spirit of this
X * contribution; always provide source, and always allow
X * free redistribution (shareware is fine with me).  If
X * you use a significant part of this code in a program of
X * yours, I would appreciate being given the appropriate
X * amount of credit.
X *				-=] Ford [=-
X *
X ************************************************************/
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/sysmacros.h>
X#include <sys/stat.h>
X#include <sys/tune.h>
X#include <sys/inode.h>
X#include <sys/file.h>
X#include <sys/user.h>
X#include <sys/proc.h>
X#include <sys/signal.h>
X#include <a.out.h>
X
X/* get rid of meaningless NOFILE from param.h */
X#ifdef NOFILE
X#undef NOFILE
X#endif
X
Xextern char *sbrk();
Xextern long lseek();
Xextern void perror(), exit();
Xextern struct passwd *getpwuid();
X
X
Xchar *progname;
X
X#define tuhiaddr (mysyms[0].n_value)
X#define inodeaddr (mysyms[1].n_value)
X#define fileaddr (mysyms[2].n_value)
X#define procaddr (mysyms[3].n_value)
X#define nofileaddr (mysyms[4].n_value)
X
Xstruct nlist mysyms[] =
X{
X    { "tuhi", },
X    { "inode", },
X    { "file", },
X    { "proc", },
X    { "nofile", },
X    { (char *)0, },
X};
X
Xchar buf[BUFSIZ];
X
Xint kmem, mem, kflag, uflag;
Xint NINODE, NFILE, NPROC, NOFILE;
X
Xstruct inode *inode;
Xstruct file *file;
Xstruct proc *proc;
X
X
X/* main program for fuser(1M), a program which lists */
X/* processes that are using the given file(s) */
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    int status=0;
X
X    progname = *argv;
X
X    setup();
X
X    while (++argv,--argc)
X	if ((*argv)[0]=='-')
X	{
X	    register char c, *i;
X
X	    kflag=uflag=0;
X
X	    i = *argv+1;
X	    while (c= *i++) switch(c)
X	    {
X	    case 'k':
X		++kflag;
X		break;
X	    case 'u':
X		++uflag;
X		break;
X	    default:
X		fprintf(stderr, "%s: bad flag `-%c'\n", progname, c);
X		fprintf(stderr,
X			"Usage: %s [-ku] files [[-] [-ku] files]\n",
X			progname);
X		return -1;
X	    }
X	}
X	else
X	    status += fuser(*argv);
X
X    return status;
X}
X
X
X/* a fast, zeroizing, memory allocator for things */
X/* that will never need to be freed */
Xchar *myalloc(nbytes)
Xlong nbytes;
X{
X    register char *ptr = sbrk((int)nbytes);
X
X    if ((long)ptr < 0L)
X    {
X	sprintf(buf, "%s: no memory!", progname);
X	perror(buf);
X	exit(1);
X    }
X
X    return ptr;
X}
X
X
X/* one-time setup of main data structures from the kernel */
Xsetup()
X{
X    struct tunable tune;
X
X    if ( (kmem=open("/dev/kmem", O_RDONLY)) < 0 )
X    {
X	sprintf(buf, "%s: can't open /dev/kmem", progname);
X	perror(buf);
X	exit(1);
X    }
X
X    if ( (mem=open("/dev/mem", O_RDONLY)) < 0 )
X    {
X	sprintf(buf, "%s: can't open /dev/mem", progname);
X	perror(buf);
X	exit(1);
X    }
X
X    if (nlist("/unix", mysyms))
X    {
X	sprintf(buf, "%s: can't nlist /unix", progname);
X	perror(buf);
X	exit(1);
X    }
X
X    setuid(getuid());
X
X    kcopy((char *)&NOFILE, nofileaddr, (long) sizeof NOFILE);
X
X#ifdef DEBUG
X    fprintf(stderr, "tuhi:	0x%08lx\n", tuhiaddr);
X#endif DEBUG
X    kcopy((char *)&tune, tuhiaddr, (long) sizeof tune);
X
X    /* do indirection on these addresses, since they */
X    /* are just pointers in the kernel */
X    kcopy((char *)&inodeaddr, inodeaddr, (long) sizeof inodeaddr);
X    kcopy((char *)&fileaddr, fileaddr, (long) sizeof fileaddr);
X    kcopy((char *)&procaddr, procaddr, (long) sizeof procaddr);
X
X#ifdef DEBUG
X    fprintf(stderr, "inode:	0x%08lx\n", inodeaddr);
X    fprintf(stderr, "file:	0x%08lx\n", fileaddr);
X    fprintf(stderr, "proc:	0x%08lx\n", procaddr);
X#endif DEBUG
X
X    NINODE = tune.ninode;
X    NFILE = tune.nfile;
X    NPROC = tune.nproc;
X
X#ifdef DEBUG
X    fprintf(stderr, "NOFILE:	%d\n", NOFILE);
X    fprintf(stderr, "NINODE:	%d\n", NINODE);
X    fprintf(stderr, "NFILE:	%d\n", NFILE);
X    fprintf(stderr, "NPROC:	%d\n", NPROC);
X#endif DEBUG
X
X    inode = (struct inode *)myalloc((long) sizeof (struct inode) * NINODE);
X    file = (struct file *)myalloc((long) sizeof (struct file) * NFILE);
X    proc = (struct proc *)myalloc((long) sizeof (struct proc) * NPROC);
X
X    kcopy((char *)inode, inodeaddr, (long) sizeof (struct inode) * NINODE);
X    kcopy((char *)file, fileaddr, (long) sizeof (struct file) * NFILE);
X    kcopy((char *)proc, procaddr, (long) sizeof (struct proc) * NPROC);
X}
X
X
X/* copy bytes from physical address space to this process */
Xpcopy(caddr, paddr, nbytes)
Xchar *caddr;
Xlong paddr;
Xlong nbytes;
X{
X    if ( lseek(mem, paddr, 0)<0L ||
X	read(mem, caddr, (unsigned)nbytes) != nbytes )
X    {
X	sprintf(buf, "%s: can't read /dev/mem", progname);
X	perror(buf);
X	exit(1);
X    }
X}
X
X
X/* copy bytes from kernel address space to this process */
Xkcopy(caddr, kaddr, nbytes)
Xchar *caddr;
Xlong kaddr;
Xlong nbytes;
X{
X    if ( lseek(kmem, kaddr, 0)<0L ||
X	read(kmem, caddr, (unsigned)nbytes) != nbytes )
X    {
X	sprintf(buf, "%s: can't read /dev/kmem", progname);
X	perror(buf);
X	exit(1);
X    }
X}
X
X
X/* Return a pointer to a local copy of the user structure */
X/* for process number `procidx'.  Returns NULL if procidx */
X/* refers to an invalid (not-in-use or otherwise) slot. */
Xstruct user *getuser(procidx)
Xint procidx;
X{
X    static struct user **users;
X    struct file **ofile;
X    long upage;
X
X    if (!proc[procidx].p_stat ||
X	proc[procidx].p_stat == SIDL ||
X	proc[procidx].p_stat == SZOMB)
X	return 0;
X
X    if (!(proc[procidx].p_flag & SLOAD))
X    {
X	/* can't handle swapped process yet */
X	fprintf(stderr, "%s: can't handle swapped process %d (flag=%05x)\n",
X		progname, proc[procidx].p_pid, proc[procidx].p_flag);
X	return 0;
X    }
X
X    if (!users)
X	users = (struct user **)myalloc((long) sizeof (struct user *) * NPROC);
X
X    if (!users[procidx])
X    {
X	upage = (long)ctob(proc[procidx].p_addr[0]);
X
X	/* allocate and copy in the user structure */
X	users[procidx] = (struct user *)myalloc((long) sizeof (struct user));
X	pcopy((char *)(users[procidx]),
X	      upage + U_OFFSET,
X	      (long) sizeof (struct user));
X
X	/* allocate and copy in the list of file pointers */
X	ofile = (struct file **)myalloc((long) sizeof (struct file *) * NOFILE);
X	pcopy((char *)ofile,
X	      upage+(long)(users[procidx]->u_ofile)-VPG_BASE,
X	      (long) sizeof (struct file *) * NOFILE);
X	users[procidx]->u_ofile = ofile;
X    }
X
X    return users[procidx];
X}
X
X
X/* find all users of the file `name' */
Xfuser(name)
Xchar *name;
X{
X    register i;
X    int filesys;
X    struct stat Stat;
X
X    if (stat(name, &Stat))
X    {
X	sprintf(buf, "%s: can't stat %s", progname, name);
X	perror(buf);
X	return 1;
X    }
X
X    /* see if we are looking for a whole filesystem */
X    filesys = ((Stat.st_mode&S_IFMT) == S_IFBLK);
X
X#ifdef DEBUG
X    if (filesys)
X	fprintf(stderr, "looking for files on dev=%d,%d\n",
X		bmajor(Stat.st_rdev), minor(Stat.st_rdev));
X    else
X	fprintf(stderr, "looking for dev=%d,%d, ino=%d\n",
X		bmajor(Stat.st_dev), minor(Stat.st_dev), Stat.st_ino);
X#endif DEBUG
X
X    for ( i=0 ; i<NINODE ; ++i )
X    {
X	if ( inode[i].i_count &&
X	     (filesys
X		 ? (brdev(inode[i].i_dev) == Stat.st_rdev)
X		 : (brdev(inode[i].i_dev) == Stat.st_dev &&
X		    inode[i].i_number == Stat.st_ino)) )
X	{
X#ifdef DEBUG
X	    fprintf(stderr, "Found it!  inode[%d], i_size is %ld\n",
X		   i, inode[i].i_size);
X#endif DEBUG
X
X	    iuser((struct inode *)inodeaddr + i);
X	}
X    }
X
X    putchar('\n');
X
X    return 0;
X}
X
X
X#define CHECK(kaddr, type) if (kaddr==kinode) { if (++flag==1) printf(" %d", proc[i].p_pid); if (type) putchar(type); }
X
X/* find all users of the inode at kernel address `kinode' */
Xiuser(kinode)
Xstruct inode *kinode;
X{
X    register int i, j;
X    int flag;
X    struct user *user;
X    struct passwd *pwd;
X
X#ifdef DEBUG
X    fprintf(stderr, "Looking for users of inode at kernel address 0x%08lx\n",
X	    kinode);
X#endif DEBUG
X
X    for ( i=0 ; i<NPROC ; ++i )
X	if (user = getuser(i))
X	{
X#ifdef DEBUG
X	    fprintf(stderr, "%03d: pid=%5d addr[0]=%05x addr[1]=%05x swaddr=%05x\n",
X		    i, proc[i].p_pid, proc[i].p_addr[0], proc[i].p_addr[1],
X		    proc[i].p_swaddr);
X#endif DEBUG
X
X#ifdef DEBUG
X	    fprintf(stderr, "	user = 0x%08lx\n", user);
X	    fprintf(stderr, "	user->u_ofile = 0x%08lx\n", user->u_ofile);
X#endif DEBUG
X
X	    fflush(stderr);
X	    flag=0;
X	    CHECK(user->u_cdir, 'c');
X	    CHECK(user->u_rdir, 'r');
X	    CHECK(user->u_pdir, 'p');
X	    for ( j=0 ; !flag && j<NOFILE ; ++j )
X		if (user->u_ofile[j])
X		    CHECK(file[user->u_ofile[j]-(struct file *)fileaddr].f_inode, 0);
X	    fflush(stdout);
X
X	    if (flag)
X	    {
X		if (uflag)
X		{
X		    if ( (pwd=getpwuid((int)proc[i].p_uid)) )
X			fprintf(stderr, "(%s)", pwd->pw_name);
X		    else
X			fprintf(stderr, "(%d)", proc[i].p_uid);
X		}
X		if (kflag && proc[i].p_pid)
X		    if (kill(proc[i].p_pid, SIGKILL))
X		    {
X			sprintf(buf, "%s: can't kill process %d",
X				progname, proc[i].p_pid);
X			perror(buf);
X		    }
X	    }
X	}
X}
SHAR_EOF
fi
exit 0
#	End of shell archive