home *** CD-ROM | disk | FTP | other *** search
- Subject: v12i049: Who's using that file? (For Unix-PC)
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rs@uunet.UU.NET
-
- Submitted-by: crash!kenobi!ford
- Posting-number: Volume 12, Issue 49
- Archive-name: fuser
-
- [ This program takes a filename, searches the kernel's open file
- table, and determines who has that file open. You can to kill the
- offending process(es). This is useful for backups, e.g. A port of
- this to BSD and other Unices would be nice. --r$ ]
-
- Here is the latest source to my version of the fuser(1M) command, written
- and running on the Unix PC. A man page and makefile are included.
-
- Verify that the macros in "Makefile" are correct for you (LBIN, for example
- should be where you want the program to be installed), and run make AS ROOT.
- It is important to do the make as root so that the resulting program will
- have the set-uid bit set, allowing it to read the kernel's memory.
-
-
- Some notes to anyone who wants to port this program to something other
- than the Unix PC:
-
- The Unix PC has "tunable kernel parameters", which mean that things that
- are constants on most older Unix systems are variable. This version of
- fuser reads the values of these variables from the kernel's memory. I have
- tried to make most of this transparent, for ease of porting to a more plain-
- vanilla Unix. For example, I have 'int' varialbes called 'NOFILE', 'NPROC',
- etc., which emulate the #defines present in 'standard' Unix. To make it
- run on such systems, it should be possible to remove these variables and
- the code that sets them, and the program will use the #defines from
- <sys/*.h>. The only other significant change is that because NOFILE is
- variable, the u_ofile field in the user structure is a pointer rather than
- an array. This means that you can delete the code that copies the u_ofile
- list since it is already right in the user structure.
-
-
- -=] Ford [=-
-
- "GNU does not eliminate (In Real Life: Michael Ditto)
- all the world's problems, kenobi!ford@crash.CTS.COM
- only some of them." -rms ...!crash!kenobi!ford
-
-
- Here's the shar file:
-
- #! /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 the files:
- # fuser.1
- # Makefile
- # fuser.c
- export PATH; PATH=/bin:$PATH
- echo shar: extracting "'fuser.1'" '(1947 characters)'
- if test -f 'fuser.1'
- then
- echo shar: will not over-write existing file "'fuser.1'"
- else
- cat << \SHAR_EOF > 'fuser.1'
- .TH FUSER 1M
- .SH NAME
- fuser \- find processes using a file or filesystem
- .SH SYNOPSIS
- .B fuser
- [-ku] files [...]
- .SH DESCRIPTION
- .I Fuser
- searches the kernel's internal tables to find all processes that are
- accessing the listed
- .I files.
- If one of the
- .I files
- is a block special file (such as a disk) then fuser finds processes that
- are accessing any file on that device.
- .P
- .I Fuser
- lists on its standard output the process ID of each process found
- to be using any of the files
- specified. The process IDs will be followed by the letters
- .B c,
- .B r,
- or
- .B p,
- if the file is open as the current directory, root directory, or
- parent directory of the process, respectively. (The kernel has a process'
- parent directory "open" for internal use under certain conditions.)
- .SH OPTIONS
- The -k option causes
- .I fuser
- to attempt to kill each process found with the SIGKILL signal (normal
- permission controls apply; see kill(2)).
- .P
- The -u option causes
- .I fuser
- to print, in parentheses, the user ID of the owner of each process listed.
- .P
- Options may be re-specified between filenames; a '-' argument by itself
- turns off the -k and -u options.
- .P
- The process IDs are written to standard output, one line per file searched
- for. Other output is written to standard error.
- .SH EXAMPLES
- fuser /tmp/foo
- .br
- .RS
- This prints the process IDs of all processes that have the file /tmp/foo open.
- .RE
- .P
- fuser -u /dev/fp021
- .br
- .RS
- This finds all processes that are accessing any file on the disk /dev/fp021.
- The process IDs and user names of the process owners are listed.
- This is useful to find out why a disk can not be unmounted.
- .RE
- .P
- fuser -k /dev/fp021
- .br
- umount /dev/fp021
- .br
- .RS
- If run by super-user, this kills all processes that are preventing
- the disk /dev/fp021 from being unmounted, and then unmounts the disk.
- .RE
- .SH BUGS
- If a process's user area is swapped (happens on some versions of UNIX)
- fuser will print a warning and ignore that process.
- SHAR_EOF
- if test 1947 -ne "`wc -c < 'fuser.1'`"
- then
- echo shar: error transmitting "'fuser.1'" '(should have been 1947 characters)'
- fi
- fi # end of overwriting check
- echo shar: extracting "'Makefile'" '(164 characters)'
- if test -f 'Makefile'
- then
- echo shar: will not over-write existing file "'Makefile'"
- else
- cat << \SHAR_EOF > 'Makefile'
- SHELL=/bin/sh
- INSTALL=ln
- LBIN=/usr/lbin
- CFLAGS=-O
-
- fuser : fuser.o
- $(CC) $(CFLAGS) -o fuser fuser.o
- chmod 4755 fuser
- chown root fuser
- $(INSTALL) fuser $(LBIN)
-
- SHAR_EOF
- if test 164 -ne "`wc -c < 'Makefile'`"
- then
- echo shar: error transmitting "'Makefile'" '(should have been 164 characters)'
- fi
- fi # end of overwriting check
- echo shar: extracting "'fuser.c'" '(9081 characters)'
- if test -f 'fuser.c'
- then
- echo shar: will not over-write existing file "'fuser.c'"
- else
- cat << \SHAR_EOF > 'fuser.c'
- /************************************************************
- *
- * This program was written by me, Mike "Ford" Ditto, and
- * I hereby release it into the public domain in the interest
- * of promoting the development of free, quality software
- * for the hackers and users of the world.
- *
- * Feel free to use, copy, modify, improve, and redistribute
- * this program, but keep in mind the spirit of this
- * contribution; always provide source, and always allow
- * free redistribution (shareware is fine with me). If
- * you use a significant part of this code in a program of
- * yours, I would appreciate being given the appropriate
- * amount of credit.
- * -=] Ford [=-
- *
- ************************************************************/
-
- #include <stdio.h>
- #include <fcntl.h>
- #include <ctype.h>
- #include <pwd.h>
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/sysmacros.h>
- #include <sys/stat.h>
- #include <sys/tune.h>
- #include <sys/inode.h>
- #include <sys/file.h>
- #include <sys/user.h>
- #include <sys/proc.h>
- #include <sys/signal.h>
- #include <a.out.h>
-
- /* get rid of meaningless NOFILE from param.h */
- #ifdef NOFILE
- #undef NOFILE
- #endif
-
- extern char *sbrk();
- extern long lseek();
- extern void perror(), exit();
- extern struct passwd *getpwuid();
-
-
- char *progname;
-
- #define tuhiaddr (mysyms[0].n_value)
- #define inodeaddr (mysyms[1].n_value)
- #define fileaddr (mysyms[2].n_value)
- #define procaddr (mysyms[3].n_value)
- #define nofileaddr (mysyms[4].n_value)
-
- struct nlist mysyms[] =
- {
- { "tuhi", },
- { "inode", },
- { "file", },
- { "proc", },
- { "nofile", },
- { (char *)0, },
- };
-
- char buf[BUFSIZ];
-
- int kmem, mem, kflag, uflag;
- int NINODE, NFILE, NPROC, NOFILE;
-
- struct inode *inode;
- struct file *file;
- struct proc *proc;
-
-
- /* main program for fuser(1M), a program which lists */
- /* processes that are using the given file(s) */
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int status=0;
-
- progname = *argv;
-
- setup();
-
- while (++argv,--argc)
- if ((*argv)[0]=='-')
- {
- register char c, *i;
-
- kflag=uflag=0;
-
- i = *argv+1;
- while (c= *i++) switch(c)
- {
- case 'k':
- ++kflag;
- break;
- case 'u':
- ++uflag;
- break;
- default:
- fprintf(stderr, "%s: bad flag `-%c'\n", progname, c);
- fprintf(stderr,
- "Usage: %s [-ku] files [[-] [-ku] files]\n",
- progname);
- return -1;
- }
- }
- else
- status += fuser(*argv);
-
- return status;
- }
-
-
- /* a fast, zeroizing, memory allocator for things */
- /* that will never need to be freed */
- char *myalloc(nbytes)
- long nbytes;
- {
- register char *ptr = sbrk((int)nbytes);
-
- if ((long)ptr < 0L)
- {
- sprintf(buf, "%s: no memory!", progname);
- perror(buf);
- exit(1);
- }
-
- return ptr;
- }
-
-
- /* one-time setup of main data structures from the kernel */
- setup()
- {
- struct tunable tune;
-
- if ( (kmem=open("/dev/kmem", O_RDONLY)) < 0 )
- {
- sprintf(buf, "%s: can't open /dev/kmem", progname);
- perror(buf);
- exit(1);
- }
-
- if ( (mem=open("/dev/mem", O_RDONLY)) < 0 )
- {
- sprintf(buf, "%s: can't open /dev/mem", progname);
- perror(buf);
- exit(1);
- }
-
- if (nlist("/unix", mysyms))
- {
- sprintf(buf, "%s: can't nlist /unix", progname);
- perror(buf);
- exit(1);
- }
-
- setuid(getuid());
-
- kcopy((char *)&NOFILE, nofileaddr, (long) sizeof NOFILE);
-
- #ifdef DEBUG
- fprintf(stderr, "tuhi: 0x%08lx\n", tuhiaddr);
- #endif DEBUG
- kcopy((char *)&tune, tuhiaddr, (long) sizeof tune);
-
- /* do indirection on these addresses, since they */
- /* are just pointers in the kernel */
- kcopy((char *)&inodeaddr, inodeaddr, (long) sizeof inodeaddr);
- kcopy((char *)&fileaddr, fileaddr, (long) sizeof fileaddr);
- kcopy((char *)&procaddr, procaddr, (long) sizeof procaddr);
-
- #ifdef DEBUG
- fprintf(stderr, "inode: 0x%08lx\n", inodeaddr);
- fprintf(stderr, "file: 0x%08lx\n", fileaddr);
- fprintf(stderr, "proc: 0x%08lx\n", procaddr);
- #endif DEBUG
-
- NINODE = tune.ninode;
- NFILE = tune.nfile;
- NPROC = tune.nproc;
-
- #ifdef DEBUG
- fprintf(stderr, "NOFILE: %d\n", NOFILE);
- fprintf(stderr, "NINODE: %d\n", NINODE);
- fprintf(stderr, "NFILE: %d\n", NFILE);
- fprintf(stderr, "NPROC: %d\n", NPROC);
- #endif DEBUG
-
- inode = (struct inode *)myalloc((long) sizeof (struct inode) * NINODE);
- file = (struct file *)myalloc((long) sizeof (struct file) * NFILE);
- proc = (struct proc *)myalloc((long) sizeof (struct proc) * NPROC);
-
- kcopy((char *)inode, inodeaddr, (long) sizeof (struct inode) * NINODE);
- kcopy((char *)file, fileaddr, (long) sizeof (struct file) * NFILE);
- kcopy((char *)proc, procaddr, (long) sizeof (struct proc) * NPROC);
- }
-
-
- /* copy bytes from physical address space to this process */
- pcopy(caddr, paddr, nbytes)
- char *caddr;
- long paddr;
- long nbytes;
- {
- if ( lseek(mem, paddr, 0)<0L ||
- read(mem, caddr, (unsigned)nbytes) != nbytes )
- {
- sprintf(buf, "%s: can't read /dev/mem", progname);
- perror(buf);
- exit(1);
- }
- }
-
-
- /* copy bytes from kernel address space to this process */
- kcopy(caddr, kaddr, nbytes)
- char *caddr;
- long kaddr;
- long nbytes;
- {
- if ( lseek(kmem, kaddr, 0)<0L ||
- read(kmem, caddr, (unsigned)nbytes) != nbytes )
- {
- sprintf(buf, "%s: can't read /dev/kmem", progname);
- perror(buf);
- exit(1);
- }
- }
-
-
- /* Return a pointer to a local copy of the user structure */
- /* for process number `procidx'. Returns NULL if procidx */
- /* refers to an invalid (not-in-use or otherwise) slot. */
- struct user *getuser(procidx)
- int procidx;
- {
- static struct user **users;
- struct file **ofile;
- long upage;
-
- if (!proc[procidx].p_stat ||
- proc[procidx].p_stat == SIDL ||
- proc[procidx].p_stat == SZOMB)
- return 0;
-
- if (!(proc[procidx].p_flag & SLOAD))
- {
- /* can't handle swapped process yet */
- fprintf(stderr, "%s: can't handle swapped process %d (flag=%05x)\n",
- progname, proc[procidx].p_pid, proc[procidx].p_flag);
- return 0;
- }
-
- if (!users)
- users = (struct user **)myalloc((long) sizeof (struct user *) * NPROC);
-
- if (!users[procidx])
- {
- upage = (long)ctob(proc[procidx].p_addr[0]);
-
- /* allocate and copy in the user structure */
- users[procidx] = (struct user *)myalloc((long) sizeof (struct user));
- pcopy((char *)(users[procidx]),
- upage + U_OFFSET,
- (long) sizeof (struct user));
-
- /* allocate and copy in the list of file pointers */
- ofile = (struct file **)myalloc((long) sizeof (struct file *) * NOFILE);
- pcopy((char *)ofile,
- upage+(long)(users[procidx]->u_ofile)-VPG_BASE,
- (long) sizeof (struct file *) * NOFILE);
- users[procidx]->u_ofile = ofile;
- }
-
- return users[procidx];
- }
-
-
- /* find all users of the file `name' */
- fuser(name)
- char *name;
- {
- register i;
- int filesys;
- struct stat Stat;
-
- if (stat(name, &Stat))
- {
- sprintf(buf, "%s: can't stat %s", progname, name);
- perror(buf);
- return 1;
- }
-
- /* see if we are looking for a whole filesystem */
- filesys = ((Stat.st_mode&S_IFMT) == S_IFBLK);
-
- #ifdef DEBUG
- if (filesys)
- fprintf(stderr, "looking for files on dev=%d,%d\n",
- bmajor(Stat.st_rdev), minor(Stat.st_rdev));
- else
- fprintf(stderr, "looking for dev=%d,%d, ino=%d\n",
- bmajor(Stat.st_dev), minor(Stat.st_dev), Stat.st_ino);
- #endif DEBUG
-
- for ( i=0 ; i<NINODE ; ++i )
- {
- if ( inode[i].i_count &&
- (filesys
- ? (brdev(inode[i].i_dev) == Stat.st_rdev)
- : (brdev(inode[i].i_dev) == Stat.st_dev &&
- inode[i].i_number == Stat.st_ino)) )
- {
- #ifdef DEBUG
- fprintf(stderr, "Found it! inode[%d], i_size is %ld\n",
- i, inode[i].i_size);
- #endif DEBUG
-
- iuser((struct inode *)inodeaddr + i);
- }
- }
-
- putchar('\n');
-
- return 0;
- }
-
-
- #define CHECK(kaddr, type) if (kaddr==kinode) { if (++flag==1) printf(" %d", proc[i].p_pid); if (type) putchar(type); }
-
- /* find all users of the inode at kernel address `kinode' */
- iuser(kinode)
- struct inode *kinode;
- {
- register int i, j;
- int flag;
- struct user *user;
- struct passwd *pwd;
-
- #ifdef DEBUG
- fprintf(stderr, "Looking for users of inode at kernel address 0x%08lx\n",
- kinode);
- #endif DEBUG
-
- for ( i=0 ; i<NPROC ; ++i )
- if (user = getuser(i))
- {
- #ifdef DEBUG
- fprintf(stderr, "%03d: pid=%5d addr[0]=%05x addr[1]=%05x swaddr=%05x\n",
- i, proc[i].p_pid, proc[i].p_addr[0], proc[i].p_addr[1],
- proc[i].p_swaddr);
- #endif DEBUG
-
- #ifdef DEBUG
- fprintf(stderr, " user = 0x%08lx\n", user);
- fprintf(stderr, " user->u_ofile = 0x%08lx\n", user->u_ofile);
- #endif DEBUG
-
- fflush(stderr);
- flag=0;
- CHECK(user->u_cdir, 'c');
- CHECK(user->u_rdir, 'r');
- CHECK(user->u_pdir, 'p');
- for ( j=0 ; !flag && j<NOFILE ; ++j )
- if (user->u_ofile[j])
- CHECK(file[user->u_ofile[j]-(struct file *)fileaddr].f_inode, 0);
- fflush(stdout);
-
- if (flag)
- {
- if (uflag)
- {
- if ( (pwd=getpwuid((int)proc[i].p_uid)) )
- fprintf(stderr, "(%s)", pwd->pw_name);
- else
- fprintf(stderr, "(%d)", proc[i].p_uid);
- }
- if (kflag && proc[i].p_pid)
- if (kill(proc[i].p_pid, SIGKILL))
- {
- sprintf(buf, "%s: can't kill process %d",
- progname, proc[i].p_pid);
- perror(buf);
- }
- }
- }
- }
- SHAR_EOF
- if test 9081 -ne "`wc -c < 'fuser.c'`"
- then
- echo shar: error transmitting "'fuser.c'" '(should have been 9081 characters)'
- fi
- fi # end of overwriting check
- # End of shell archive
- exit 0
-