fuser.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * tiny fuser implementation
  4. *
  5. * Copyright 2004 Tony J. White
  6. *
  7. * Licensed under GPLv2, see file LICENSE in this tarball for details.
  8. */
  9. #include "libbb.h"
  10. #define MAX_LINE 255
  11. #define OPTION_STRING "mks64"
  12. enum {
  13. OPT_MOUNT = (1 << 0),
  14. OPT_KILL = (1 << 1),
  15. OPT_SILENT = (1 << 2),
  16. OPT_IP6 = (1 << 3),
  17. OPT_IP4 = (1 << 4),
  18. };
  19. typedef struct inode_list {
  20. struct inode_list *next;
  21. ino_t inode;
  22. dev_t dev;
  23. } inode_list;
  24. typedef struct pid_list {
  25. struct pid_list *next;
  26. pid_t pid;
  27. } pid_list;
  28. struct globals {
  29. pid_list *pid_list_head;
  30. inode_list *inode_list_head;
  31. };
  32. #define G (*(struct globals*)&bb_common_bufsiz1)
  33. #define INIT_G() do { } while (0)
  34. static void add_pid(const pid_t pid)
  35. {
  36. pid_list **curr = &G.pid_list_head;
  37. while (*curr) {
  38. if ((*curr)->pid == pid)
  39. return;
  40. curr = &(*curr)->next;
  41. }
  42. *curr = xzalloc(sizeof(pid_list));
  43. (*curr)->pid = pid;
  44. }
  45. static void add_inode(const struct stat *st)
  46. {
  47. inode_list **curr = &G.inode_list_head;
  48. while (*curr) {
  49. if ((*curr)->dev == st->st_dev
  50. && (*curr)->inode == st->st_ino
  51. ) {
  52. return;
  53. }
  54. curr = &(*curr)->next;
  55. }
  56. *curr = xzalloc(sizeof(inode_list));
  57. (*curr)->dev = st->st_dev;
  58. (*curr)->inode = st->st_ino;
  59. }
  60. static void scan_proc_net(const char *path, unsigned port)
  61. {
  62. char line[MAX_LINE + 1];
  63. long long uint64_inode;
  64. unsigned tmp_port;
  65. FILE *f;
  66. struct stat st;
  67. int fd;
  68. /* find socket dev */
  69. st.st_dev = 0;
  70. fd = socket(AF_INET, SOCK_DGRAM, 0);
  71. if (fd >= 0) {
  72. fstat(fd, &st);
  73. close(fd);
  74. }
  75. f = fopen_for_read(path);
  76. if (!f)
  77. return;
  78. while (fgets(line, MAX_LINE, f)) {
  79. char addr[68];
  80. if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
  81. "%*x:%*x %*x %*d %*d %llu",
  82. addr, &tmp_port, &uint64_inode) == 3
  83. ) {
  84. int len = strlen(addr);
  85. if (len == 8 && (option_mask32 & OPT_IP6))
  86. continue;
  87. if (len > 8 && (option_mask32 & OPT_IP4))
  88. continue;
  89. if (tmp_port == port) {
  90. st.st_ino = uint64_inode;
  91. add_inode(&st);
  92. }
  93. }
  94. }
  95. fclose(f);
  96. }
  97. static int search_dev_inode(const struct stat *st)
  98. {
  99. inode_list *ilist = G.inode_list_head;
  100. while (ilist) {
  101. if (ilist->dev == st->st_dev) {
  102. if (option_mask32 & OPT_MOUNT)
  103. return 1;
  104. if (ilist->inode == st->st_ino)
  105. return 1;
  106. }
  107. ilist = ilist->next;
  108. }
  109. return 0;
  110. }
  111. static void scan_pid_maps(const char *fname, pid_t pid)
  112. {
  113. FILE *file;
  114. char line[MAX_LINE + 1];
  115. int major, minor;
  116. long long uint64_inode;
  117. struct stat st;
  118. file = fopen_for_read(fname);
  119. if (!file)
  120. return;
  121. while (fgets(line, MAX_LINE, file)) {
  122. if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3)
  123. continue;
  124. st.st_ino = uint64_inode;
  125. if (major == 0 && minor == 0 && st.st_ino == 0)
  126. continue;
  127. st.st_dev = makedev(major, minor);
  128. if (search_dev_inode(&st))
  129. add_pid(pid);
  130. }
  131. fclose(file);
  132. }
  133. static void scan_link(const char *lname, pid_t pid)
  134. {
  135. struct stat st;
  136. if (stat(lname, &st) >= 0) {
  137. if (search_dev_inode(&st))
  138. add_pid(pid);
  139. }
  140. }
  141. static void scan_dir_links(const char *dname, pid_t pid)
  142. {
  143. DIR *d;
  144. struct dirent *de;
  145. char *lname;
  146. d = opendir(dname);
  147. if (!d)
  148. return;
  149. while ((de = readdir(d)) != NULL) {
  150. lname = concat_subpath_file(dname, de->d_name);
  151. if (lname == NULL)
  152. continue;
  153. scan_link(lname, pid);
  154. free(lname);
  155. }
  156. closedir(d);
  157. }
  158. /* NB: does chdir internally */
  159. static void scan_proc_pids(void)
  160. {
  161. DIR *d;
  162. struct dirent *de;
  163. pid_t pid;
  164. xchdir("/proc");
  165. d = opendir("/proc");
  166. if (!d)
  167. return;
  168. while ((de = readdir(d)) != NULL) {
  169. pid = (pid_t)bb_strtou(de->d_name, NULL, 10);
  170. if (errno)
  171. continue;
  172. if (chdir(de->d_name) < 0)
  173. continue;
  174. scan_link("cwd", pid);
  175. scan_link("exe", pid);
  176. scan_link("root", pid);
  177. scan_dir_links("fd", pid);
  178. scan_dir_links("lib", pid);
  179. scan_dir_links("mmap", pid);
  180. scan_pid_maps("maps", pid);
  181. xchdir("/proc");
  182. }
  183. closedir(d);
  184. }
  185. int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  186. int fuser_main(int argc UNUSED_PARAM, char **argv)
  187. {
  188. pid_list *plist;
  189. pid_t mypid;
  190. char **pp;
  191. struct stat st;
  192. unsigned port;
  193. int opt;
  194. int exitcode;
  195. int killsig;
  196. /*
  197. fuser [OPTIONS] FILE or PORT/PROTO
  198. Find processes which use FILEs or PORTs
  199. -m Find processes which use same fs as FILEs
  200. -4 Search only IPv4 space
  201. -6 Search only IPv6 space
  202. -s Don't display PIDs
  203. -k Kill found processes
  204. -SIGNAL Signal to send (default: KILL)
  205. */
  206. /* Handle -SIGNAL. Oh my... */
  207. killsig = SIGKILL; /* yes, the default is not SIGTERM */
  208. pp = argv;
  209. while (*++pp) {
  210. char *arg = *pp;
  211. if (arg[0] != '-')
  212. continue;
  213. if (arg[1] == '-' && arg[2] == '\0') /* "--" */
  214. break;
  215. if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
  216. continue; /* it's "-4" or "-6" */
  217. opt = get_signum(&arg[1]);
  218. if (opt < 0)
  219. continue;
  220. /* "-SIGNAL" option found. Remove it and bail out */
  221. killsig = opt;
  222. do {
  223. pp[0] = arg = pp[1];
  224. pp++;
  225. } while (arg);
  226. break;
  227. }
  228. opt_complementary = "-1"; /* at least one param */
  229. opt = getopt32(argv, OPTION_STRING);
  230. argv += optind;
  231. pp = argv;
  232. while (*pp) {
  233. /* parse net arg */
  234. char path[20], tproto[5];
  235. if (sscanf(*pp, "%u/%4s", &port, tproto) != 2)
  236. goto file;
  237. sprintf(path, "/proc/net/%s", tproto);
  238. if (access(path, R_OK) != 0) { /* PORT/PROTO */
  239. scan_proc_net(path, port);
  240. } else { /* FILE */
  241. file:
  242. xstat(*pp, &st);
  243. add_inode(&st);
  244. }
  245. pp++;
  246. }
  247. scan_proc_pids(); /* changes dir to "/proc" */
  248. mypid = getpid();
  249. plist = G.pid_list_head;
  250. while (1) {
  251. if (!plist)
  252. return EXIT_FAILURE;
  253. if (plist->pid != mypid)
  254. break;
  255. plist = plist->next;
  256. }
  257. exitcode = EXIT_SUCCESS;
  258. do {
  259. if (plist->pid != mypid) {
  260. if (opt & OPT_KILL) {
  261. if (kill(plist->pid, killsig) != 0) {
  262. bb_perror_msg("kill pid %u", (unsigned)plist->pid);
  263. exitcode = EXIT_FAILURE;
  264. }
  265. }
  266. if (!(opt & OPT_SILENT)) {
  267. printf("%u ", (unsigned)plist->pid);
  268. }
  269. }
  270. plist = plist->next;
  271. } while (plist);
  272. if (!(opt & (OPT_SILENT))) {
  273. bb_putchar('\n');
  274. }
  275. return exitcode;
  276. }