home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume12 / fuser < prev    next >
Text File  |  1987-10-25  |  15KB  |  567 lines

  1. Subject:  v12i049:  Who's using that file? (For Unix-PC)
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rs@uunet.UU.NET
  5.  
  6. Submitted-by: crash!kenobi!ford
  7. Posting-number: Volume 12, Issue 49
  8. Archive-name: fuser
  9.  
  10. [  This program takes a filename, searches the kernel's open file
  11.    table, and determines who has that file open.  You can to kill the
  12.    offending process(es).  This is useful for backups, e.g.  A port of
  13.    this to BSD and other Unices would be nice.  --r$  ]
  14.  
  15. Here is the latest source to my version of the fuser(1M) command, written
  16. and running on the Unix PC.  A man page and makefile are included.
  17.  
  18. Verify that the macros in "Makefile" are correct for you (LBIN, for example
  19. should be where you want the program to be installed), and run make AS ROOT.
  20. It is important to do the make as root so that the resulting program will
  21. have the set-uid bit set, allowing it to read the kernel's memory.
  22.  
  23.  
  24. Some notes to anyone who wants to port this program to something other
  25. than the Unix PC:
  26.  
  27. The Unix PC has "tunable kernel parameters", which mean that things that
  28. are constants on most older Unix systems are variable.  This version of
  29. fuser reads the values of these variables from the kernel's memory.  I have
  30. tried to make most of this transparent, for ease of porting to a more plain-
  31. vanilla Unix.  For example, I have 'int' varialbes called 'NOFILE', 'NPROC',
  32. etc., which emulate the #defines present in 'standard' Unix.  To make it
  33. run on such systems, it should be possible to remove these variables and
  34. the code that sets them, and the program will use the #defines from
  35. <sys/*.h>.  The only other significant change is that because NOFILE is
  36. variable, the u_ofile field in the user structure is a pointer rather than
  37. an array.  This means that you can delete the code that copies the u_ofile
  38. list since it is already right in the user structure.
  39.  
  40.  
  41.                     -=] Ford [=-
  42.  
  43. "GNU does not eliminate            (In Real Life:  Michael Ditto)
  44. all the world's problems,        kenobi!ford@crash.CTS.COM
  45. only some of them." -rms        ...!crash!kenobi!ford
  46.  
  47.  
  48. Here's the shar file:
  49.  
  50. #! /bin/sh
  51. # This is a shell archive, meaning:
  52. # 1. Remove everything above the #! /bin/sh line.
  53. # 2. Save the resulting text in a file.
  54. # 3. Execute the file with /bin/sh (not csh) to create the files:
  55. #    fuser.1
  56. #    Makefile
  57. #    fuser.c
  58. export PATH; PATH=/bin:$PATH
  59. echo shar: extracting "'fuser.1'" '(1947 characters)'
  60. if test -f 'fuser.1'
  61. then
  62.     echo shar: will not over-write existing file "'fuser.1'"
  63. else
  64. cat << \SHAR_EOF > 'fuser.1'
  65. .TH FUSER 1M
  66. .SH NAME
  67. fuser \- find processes using a file or filesystem
  68. .SH SYNOPSIS
  69. .B fuser
  70. [-ku] files [...]
  71. .SH DESCRIPTION
  72. .I Fuser
  73. searches the kernel's internal tables to find all processes that are
  74. accessing the listed
  75. .I files.
  76. If one of the
  77. .I files
  78. is a block special file (such as a disk) then fuser finds processes that
  79. are accessing any file on that device.
  80. .P
  81. .I Fuser
  82. lists on its standard output the process ID of each process found
  83. to be using any of the files
  84. specified.  The process IDs will be followed by the letters
  85. .B c,
  86. .B r,
  87. or
  88. .B p,
  89. if the file is open as the current directory, root directory, or
  90. parent directory of the process, respectively.  (The kernel has a process'
  91. parent directory "open" for internal use under certain conditions.)
  92. .SH OPTIONS
  93. The -k option causes
  94. .I fuser
  95. to attempt to kill each process found with the SIGKILL signal (normal
  96. permission controls apply; see kill(2)).
  97. .P
  98. The -u option causes
  99. .I fuser
  100. to print, in parentheses, the user ID of the owner of each process listed.
  101. .P
  102. Options may be re-specified between filenames; a '-' argument by itself
  103. turns off the -k and -u options.
  104. .P
  105. The process IDs are written to standard output, one line per file searched
  106. for.  Other output is written to standard error.
  107. .SH EXAMPLES
  108. fuser /tmp/foo
  109. .br
  110. .RS
  111. This prints the process IDs of all processes that have the file /tmp/foo open.
  112. .RE
  113. .P
  114. fuser -u /dev/fp021
  115. .br
  116. .RS
  117. This finds all processes that are accessing any file on the disk /dev/fp021.
  118. The process IDs and user names of the process owners are listed.
  119. This is useful to find out why a disk can not be unmounted.
  120. .RE
  121. .P
  122. fuser -k /dev/fp021
  123. .br
  124. umount /dev/fp021
  125. .br
  126. .RS
  127. If run by super-user, this kills all processes that are preventing
  128. the disk /dev/fp021 from being unmounted, and then unmounts the disk.
  129. .RE
  130. .SH BUGS
  131. If a process's user area is swapped (happens on some versions of UNIX)
  132. fuser will print a warning and ignore that process.
  133. SHAR_EOF
  134. if test 1947 -ne "`wc -c < 'fuser.1'`"
  135. then
  136.     echo shar: error transmitting "'fuser.1'" '(should have been 1947 characters)'
  137. fi
  138. fi # end of overwriting check
  139. echo shar: extracting "'Makefile'" '(164 characters)'
  140. if test -f 'Makefile'
  141. then
  142.     echo shar: will not over-write existing file "'Makefile'"
  143. else
  144. cat << \SHAR_EOF > 'Makefile'
  145. SHELL=/bin/sh
  146. INSTALL=ln
  147. LBIN=/usr/lbin
  148. CFLAGS=-O
  149.  
  150. fuser : fuser.o
  151.     $(CC) $(CFLAGS) -o fuser fuser.o
  152.     chmod 4755 fuser
  153.     chown root fuser
  154.     $(INSTALL) fuser $(LBIN)
  155.  
  156. SHAR_EOF
  157. if test 164 -ne "`wc -c < 'Makefile'`"
  158. then
  159.     echo shar: error transmitting "'Makefile'" '(should have been 164 characters)'
  160. fi
  161. fi # end of overwriting check
  162. echo shar: extracting "'fuser.c'" '(9081 characters)'
  163. if test -f 'fuser.c'
  164. then
  165.     echo shar: will not over-write existing file "'fuser.c'"
  166. else
  167. cat << \SHAR_EOF > 'fuser.c'
  168. /************************************************************
  169.  *
  170.  * This program was written by me, Mike "Ford" Ditto, and
  171.  * I hereby release it into the public domain in the interest
  172.  * of promoting the development of free, quality software
  173.  * for the hackers and users of the world.
  174.  *
  175.  * Feel free to use, copy, modify, improve, and redistribute
  176.  * this program, but keep in mind the spirit of this
  177.  * contribution; always provide source, and always allow
  178.  * free redistribution (shareware is fine with me).  If
  179.  * you use a significant part of this code in a program of
  180.  * yours, I would appreciate being given the appropriate
  181.  * amount of credit.
  182.  *                -=] Ford [=-
  183.  *
  184.  ************************************************************/
  185.  
  186. #include <stdio.h>
  187. #include <fcntl.h>
  188. #include <ctype.h>
  189. #include <pwd.h>
  190. #include <sys/types.h>
  191. #include <sys/param.h>
  192. #include <sys/sysmacros.h>
  193. #include <sys/stat.h>
  194. #include <sys/tune.h>
  195. #include <sys/inode.h>
  196. #include <sys/file.h>
  197. #include <sys/user.h>
  198. #include <sys/proc.h>
  199. #include <sys/signal.h>
  200. #include <a.out.h>
  201.  
  202. /* get rid of meaningless NOFILE from param.h */
  203. #ifdef NOFILE
  204. #undef NOFILE
  205. #endif
  206.  
  207. extern char *sbrk();
  208. extern long lseek();
  209. extern void perror(), exit();
  210. extern struct passwd *getpwuid();
  211.  
  212.  
  213. char *progname;
  214.  
  215. #define tuhiaddr (mysyms[0].n_value)
  216. #define inodeaddr (mysyms[1].n_value)
  217. #define fileaddr (mysyms[2].n_value)
  218. #define procaddr (mysyms[3].n_value)
  219. #define nofileaddr (mysyms[4].n_value)
  220.  
  221. struct nlist mysyms[] =
  222. {
  223.     { "tuhi", },
  224.     { "inode", },
  225.     { "file", },
  226.     { "proc", },
  227.     { "nofile", },
  228.     { (char *)0, },
  229. };
  230.  
  231. char buf[BUFSIZ];
  232.  
  233. int kmem, mem, kflag, uflag;
  234. int NINODE, NFILE, NPROC, NOFILE;
  235.  
  236. struct inode *inode;
  237. struct file *file;
  238. struct proc *proc;
  239.  
  240.  
  241. /* main program for fuser(1M), a program which lists */
  242. /* processes that are using the given file(s) */
  243. main(argc, argv)
  244. int argc;
  245. char *argv[];
  246. {
  247.     int status=0;
  248.  
  249.     progname = *argv;
  250.  
  251.     setup();
  252.  
  253.     while (++argv,--argc)
  254.     if ((*argv)[0]=='-')
  255.     {
  256.         register char c, *i;
  257.  
  258.         kflag=uflag=0;
  259.  
  260.         i = *argv+1;
  261.         while (c= *i++) switch(c)
  262.         {
  263.         case 'k':
  264.         ++kflag;
  265.         break;
  266.         case 'u':
  267.         ++uflag;
  268.         break;
  269.         default:
  270.         fprintf(stderr, "%s: bad flag `-%c'\n", progname, c);
  271.         fprintf(stderr,
  272.             "Usage: %s [-ku] files [[-] [-ku] files]\n",
  273.             progname);
  274.         return -1;
  275.         }
  276.     }
  277.     else
  278.         status += fuser(*argv);
  279.  
  280.     return status;
  281. }
  282.  
  283.  
  284. /* a fast, zeroizing, memory allocator for things */
  285. /* that will never need to be freed */
  286. char *myalloc(nbytes)
  287. long nbytes;
  288. {
  289.     register char *ptr = sbrk((int)nbytes);
  290.  
  291.     if ((long)ptr < 0L)
  292.     {
  293.     sprintf(buf, "%s: no memory!", progname);
  294.     perror(buf);
  295.     exit(1);
  296.     }
  297.  
  298.     return ptr;
  299. }
  300.  
  301.  
  302. /* one-time setup of main data structures from the kernel */
  303. setup()
  304. {
  305.     struct tunable tune;
  306.  
  307.     if ( (kmem=open("/dev/kmem", O_RDONLY)) < 0 )
  308.     {
  309.     sprintf(buf, "%s: can't open /dev/kmem", progname);
  310.     perror(buf);
  311.     exit(1);
  312.     }
  313.  
  314.     if ( (mem=open("/dev/mem", O_RDONLY)) < 0 )
  315.     {
  316.     sprintf(buf, "%s: can't open /dev/mem", progname);
  317.     perror(buf);
  318.     exit(1);
  319.     }
  320.  
  321.     if (nlist("/unix", mysyms))
  322.     {
  323.     sprintf(buf, "%s: can't nlist /unix", progname);
  324.     perror(buf);
  325.     exit(1);
  326.     }
  327.  
  328.     setuid(getuid());
  329.  
  330.     kcopy((char *)&NOFILE, nofileaddr, (long) sizeof NOFILE);
  331.  
  332. #ifdef DEBUG
  333.     fprintf(stderr, "tuhi:    0x%08lx\n", tuhiaddr);
  334. #endif DEBUG
  335.     kcopy((char *)&tune, tuhiaddr, (long) sizeof tune);
  336.  
  337.     /* do indirection on these addresses, since they */
  338.     /* are just pointers in the kernel */
  339.     kcopy((char *)&inodeaddr, inodeaddr, (long) sizeof inodeaddr);
  340.     kcopy((char *)&fileaddr, fileaddr, (long) sizeof fileaddr);
  341.     kcopy((char *)&procaddr, procaddr, (long) sizeof procaddr);
  342.  
  343. #ifdef DEBUG
  344.     fprintf(stderr, "inode:    0x%08lx\n", inodeaddr);
  345.     fprintf(stderr, "file:    0x%08lx\n", fileaddr);
  346.     fprintf(stderr, "proc:    0x%08lx\n", procaddr);
  347. #endif DEBUG
  348.  
  349.     NINODE = tune.ninode;
  350.     NFILE = tune.nfile;
  351.     NPROC = tune.nproc;
  352.  
  353. #ifdef DEBUG
  354.     fprintf(stderr, "NOFILE:    %d\n", NOFILE);
  355.     fprintf(stderr, "NINODE:    %d\n", NINODE);
  356.     fprintf(stderr, "NFILE:    %d\n", NFILE);
  357.     fprintf(stderr, "NPROC:    %d\n", NPROC);
  358. #endif DEBUG
  359.  
  360.     inode = (struct inode *)myalloc((long) sizeof (struct inode) * NINODE);
  361.     file = (struct file *)myalloc((long) sizeof (struct file) * NFILE);
  362.     proc = (struct proc *)myalloc((long) sizeof (struct proc) * NPROC);
  363.  
  364.     kcopy((char *)inode, inodeaddr, (long) sizeof (struct inode) * NINODE);
  365.     kcopy((char *)file, fileaddr, (long) sizeof (struct file) * NFILE);
  366.     kcopy((char *)proc, procaddr, (long) sizeof (struct proc) * NPROC);
  367. }
  368.  
  369.  
  370. /* copy bytes from physical address space to this process */
  371. pcopy(caddr, paddr, nbytes)
  372. char *caddr;
  373. long paddr;
  374. long nbytes;
  375. {
  376.     if ( lseek(mem, paddr, 0)<0L ||
  377.     read(mem, caddr, (unsigned)nbytes) != nbytes )
  378.     {
  379.     sprintf(buf, "%s: can't read /dev/mem", progname);
  380.     perror(buf);
  381.     exit(1);
  382.     }
  383. }
  384.  
  385.  
  386. /* copy bytes from kernel address space to this process */
  387. kcopy(caddr, kaddr, nbytes)
  388. char *caddr;
  389. long kaddr;
  390. long nbytes;
  391. {
  392.     if ( lseek(kmem, kaddr, 0)<0L ||
  393.     read(kmem, caddr, (unsigned)nbytes) != nbytes )
  394.     {
  395.     sprintf(buf, "%s: can't read /dev/kmem", progname);
  396.     perror(buf);
  397.     exit(1);
  398.     }
  399. }
  400.  
  401.  
  402. /* Return a pointer to a local copy of the user structure */
  403. /* for process number `procidx'.  Returns NULL if procidx */
  404. /* refers to an invalid (not-in-use or otherwise) slot. */
  405. struct user *getuser(procidx)
  406. int procidx;
  407. {
  408.     static struct user **users;
  409.     struct file **ofile;
  410.     long upage;
  411.  
  412.     if (!proc[procidx].p_stat ||
  413.     proc[procidx].p_stat == SIDL ||
  414.     proc[procidx].p_stat == SZOMB)
  415.     return 0;
  416.  
  417.     if (!(proc[procidx].p_flag & SLOAD))
  418.     {
  419.     /* can't handle swapped process yet */
  420.     fprintf(stderr, "%s: can't handle swapped process %d (flag=%05x)\n",
  421.         progname, proc[procidx].p_pid, proc[procidx].p_flag);
  422.     return 0;
  423.     }
  424.  
  425.     if (!users)
  426.     users = (struct user **)myalloc((long) sizeof (struct user *) * NPROC);
  427.  
  428.     if (!users[procidx])
  429.     {
  430.     upage = (long)ctob(proc[procidx].p_addr[0]);
  431.  
  432.     /* allocate and copy in the user structure */
  433.     users[procidx] = (struct user *)myalloc((long) sizeof (struct user));
  434.     pcopy((char *)(users[procidx]),
  435.           upage + U_OFFSET,
  436.           (long) sizeof (struct user));
  437.  
  438.     /* allocate and copy in the list of file pointers */
  439.     ofile = (struct file **)myalloc((long) sizeof (struct file *) * NOFILE);
  440.     pcopy((char *)ofile,
  441.           upage+(long)(users[procidx]->u_ofile)-VPG_BASE,
  442.           (long) sizeof (struct file *) * NOFILE);
  443.     users[procidx]->u_ofile = ofile;
  444.     }
  445.  
  446.     return users[procidx];
  447. }
  448.  
  449.  
  450. /* find all users of the file `name' */
  451. fuser(name)
  452. char *name;
  453. {
  454.     register i;
  455.     int filesys;
  456.     struct stat Stat;
  457.  
  458.     if (stat(name, &Stat))
  459.     {
  460.     sprintf(buf, "%s: can't stat %s", progname, name);
  461.     perror(buf);
  462.     return 1;
  463.     }
  464.  
  465.     /* see if we are looking for a whole filesystem */
  466.     filesys = ((Stat.st_mode&S_IFMT) == S_IFBLK);
  467.  
  468. #ifdef DEBUG
  469.     if (filesys)
  470.     fprintf(stderr, "looking for files on dev=%d,%d\n",
  471.         bmajor(Stat.st_rdev), minor(Stat.st_rdev));
  472.     else
  473.     fprintf(stderr, "looking for dev=%d,%d, ino=%d\n",
  474.         bmajor(Stat.st_dev), minor(Stat.st_dev), Stat.st_ino);
  475. #endif DEBUG
  476.  
  477.     for ( i=0 ; i<NINODE ; ++i )
  478.     {
  479.     if ( inode[i].i_count &&
  480.          (filesys
  481.          ? (brdev(inode[i].i_dev) == Stat.st_rdev)
  482.          : (brdev(inode[i].i_dev) == Stat.st_dev &&
  483.             inode[i].i_number == Stat.st_ino)) )
  484.     {
  485. #ifdef DEBUG
  486.         fprintf(stderr, "Found it!  inode[%d], i_size is %ld\n",
  487.            i, inode[i].i_size);
  488. #endif DEBUG
  489.  
  490.         iuser((struct inode *)inodeaddr + i);
  491.     }
  492.     }
  493.  
  494.     putchar('\n');
  495.  
  496.     return 0;
  497. }
  498.  
  499.  
  500. #define CHECK(kaddr, type) if (kaddr==kinode) { if (++flag==1) printf(" %d", proc[i].p_pid); if (type) putchar(type); }
  501.  
  502. /* find all users of the inode at kernel address `kinode' */
  503. iuser(kinode)
  504. struct inode *kinode;
  505. {
  506.     register int i, j;
  507.     int flag;
  508.     struct user *user;
  509.     struct passwd *pwd;
  510.  
  511. #ifdef DEBUG
  512.     fprintf(stderr, "Looking for users of inode at kernel address 0x%08lx\n",
  513.         kinode);
  514. #endif DEBUG
  515.  
  516.     for ( i=0 ; i<NPROC ; ++i )
  517.     if (user = getuser(i))
  518.     {
  519. #ifdef DEBUG
  520.         fprintf(stderr, "%03d: pid=%5d addr[0]=%05x addr[1]=%05x swaddr=%05x\n",
  521.             i, proc[i].p_pid, proc[i].p_addr[0], proc[i].p_addr[1],
  522.             proc[i].p_swaddr);
  523. #endif DEBUG
  524.  
  525. #ifdef DEBUG
  526.         fprintf(stderr, "    user = 0x%08lx\n", user);
  527.         fprintf(stderr, "    user->u_ofile = 0x%08lx\n", user->u_ofile);
  528. #endif DEBUG
  529.  
  530.         fflush(stderr);
  531.         flag=0;
  532.         CHECK(user->u_cdir, 'c');
  533.         CHECK(user->u_rdir, 'r');
  534.         CHECK(user->u_pdir, 'p');
  535.         for ( j=0 ; !flag && j<NOFILE ; ++j )
  536.         if (user->u_ofile[j])
  537.             CHECK(file[user->u_ofile[j]-(struct file *)fileaddr].f_inode, 0);
  538.         fflush(stdout);
  539.  
  540.         if (flag)
  541.         {
  542.         if (uflag)
  543.         {
  544.             if ( (pwd=getpwuid((int)proc[i].p_uid)) )
  545.             fprintf(stderr, "(%s)", pwd->pw_name);
  546.             else
  547.             fprintf(stderr, "(%d)", proc[i].p_uid);
  548.         }
  549.         if (kflag && proc[i].p_pid)
  550.             if (kill(proc[i].p_pid, SIGKILL))
  551.             {
  552.             sprintf(buf, "%s: can't kill process %d",
  553.                 progname, proc[i].p_pid);
  554.             perror(buf);
  555.             }
  556.         }
  557.     }
  558. }
  559. SHAR_EOF
  560. if test 9081 -ne "`wc -c < 'fuser.c'`"
  561. then
  562.     echo shar: error transmitting "'fuser.c'" '(should have been 9081 characters)'
  563. fi
  564. fi # end of overwriting check
  565. #    End of shell archive
  566. exit 0
  567.