fuser.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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 source tree.
  8. */
  9. //config:config FUSER
  10. //config: bool "fuser"
  11. //config: default y
  12. //config: help
  13. //config: fuser lists all PIDs (Process IDs) that currently have a given
  14. //config: file open. fuser can also list all PIDs that have a given network
  15. //config: (TCP or UDP) port open.
  16. //applet:IF_FUSER(APPLET(fuser, BB_DIR_USR_BIN, BB_SUID_DROP))
  17. //kbuild:lib-$(CONFIG_FUSER) += fuser.o
  18. //usage:#define fuser_trivial_usage
  19. //usage: "[OPTIONS] FILE or PORT/PROTO"
  20. //usage:#define fuser_full_usage "\n\n"
  21. //usage: "Find processes which use FILEs or PORTs\n"
  22. //usage: "\n -m Find processes which use same fs as FILEs"
  23. //usage: "\n -4,-6 Search only IPv4/IPv6 space"
  24. //usage: "\n -s Don't display PIDs"
  25. //usage: "\n -k Kill found processes"
  26. //usage: "\n -SIGNAL Signal to send (default: KILL)"
  27. #include "libbb.h"
  28. #include "common_bufsiz.h"
  29. #define MAX_LINE 255
  30. #define OPTION_STRING "mks64"
  31. enum {
  32. OPT_MOUNT = (1 << 0),
  33. OPT_KILL = (1 << 1),
  34. OPT_SILENT = (1 << 2),
  35. OPT_IP6 = (1 << 3),
  36. OPT_IP4 = (1 << 4),
  37. };
  38. typedef struct inode_list {
  39. struct inode_list *next;
  40. ino_t inode;
  41. dev_t dev;
  42. } inode_list;
  43. struct globals {
  44. int recursion_depth;
  45. pid_t mypid;
  46. inode_list *inode_list_head;
  47. smallint kill_failed;
  48. int killsig;
  49. } FIX_ALIASING;
  50. #define G (*(struct globals*)bb_common_bufsiz1)
  51. #define INIT_G() do { \
  52. setup_common_bufsiz(); \
  53. G.mypid = getpid(); \
  54. G.killsig = SIGKILL; \
  55. } while (0)
  56. static void add_inode(const struct stat *st)
  57. {
  58. inode_list **curr = &G.inode_list_head;
  59. while (*curr) {
  60. if ((*curr)->dev == st->st_dev
  61. && (*curr)->inode == st->st_ino
  62. ) {
  63. return;
  64. }
  65. curr = &(*curr)->next;
  66. }
  67. *curr = xzalloc(sizeof(inode_list));
  68. (*curr)->dev = st->st_dev;
  69. (*curr)->inode = st->st_ino;
  70. }
  71. static smallint search_dev_inode(const struct stat *st)
  72. {
  73. inode_list *ilist = G.inode_list_head;
  74. while (ilist) {
  75. if (ilist->dev == st->st_dev) {
  76. if (option_mask32 & OPT_MOUNT)
  77. return 1;
  78. if (ilist->inode == st->st_ino)
  79. return 1;
  80. }
  81. ilist = ilist->next;
  82. }
  83. return 0;
  84. }
  85. enum {
  86. PROC_NET = 0,
  87. PROC_DIR,
  88. PROC_DIR_LINKS,
  89. PROC_SUBDIR_LINKS,
  90. };
  91. static smallint scan_proc_net_or_maps(const char *path, unsigned port)
  92. {
  93. FILE *f;
  94. char line[MAX_LINE + 1], addr[68];
  95. int major, minor, r;
  96. long long uint64_inode;
  97. unsigned tmp_port;
  98. smallint retval;
  99. struct stat statbuf;
  100. const char *fmt;
  101. void *fag, *sag;
  102. f = fopen_for_read(path);
  103. if (!f)
  104. return 0;
  105. if (G.recursion_depth == PROC_NET) {
  106. int fd;
  107. /* find socket dev */
  108. statbuf.st_dev = 0;
  109. fd = socket(AF_INET, SOCK_DGRAM, 0);
  110. if (fd >= 0) {
  111. fstat(fd, &statbuf);
  112. close(fd);
  113. }
  114. fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x "
  115. "%*x:%*x %*x:%*x %*x %*d %*d %llu";
  116. fag = addr;
  117. sag = &tmp_port;
  118. } else {
  119. fmt = "%*s %*s %*s %x:%x %llu";
  120. fag = &major;
  121. sag = &minor;
  122. }
  123. retval = 0;
  124. while (fgets(line, MAX_LINE, f)) {
  125. r = sscanf(line, fmt, fag, sag, &uint64_inode);
  126. if (r != 3)
  127. continue;
  128. statbuf.st_ino = uint64_inode;
  129. if (G.recursion_depth == PROC_NET) {
  130. r = strlen(addr);
  131. if (r == 8 && (option_mask32 & OPT_IP6))
  132. continue;
  133. if (r > 8 && (option_mask32 & OPT_IP4))
  134. continue;
  135. if (tmp_port == port)
  136. add_inode(&statbuf);
  137. } else {
  138. if (major != 0 && minor != 0 && statbuf.st_ino != 0) {
  139. statbuf.st_dev = makedev(major, minor);
  140. retval = search_dev_inode(&statbuf);
  141. if (retval)
  142. break;
  143. }
  144. }
  145. }
  146. fclose(f);
  147. return retval;
  148. }
  149. static smallint scan_recursive(const char *path)
  150. {
  151. DIR *d;
  152. struct dirent *d_ent;
  153. smallint stop_scan;
  154. smallint retval;
  155. d = opendir(path);
  156. if (d == NULL)
  157. return 0;
  158. G.recursion_depth++;
  159. retval = 0;
  160. stop_scan = 0;
  161. while (!stop_scan && (d_ent = readdir(d)) != NULL) {
  162. struct stat statbuf;
  163. pid_t pid;
  164. char *subpath;
  165. subpath = concat_subpath_file(path, d_ent->d_name);
  166. if (subpath == NULL)
  167. continue; /* . or .. */
  168. switch (G.recursion_depth) {
  169. case PROC_DIR:
  170. pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10);
  171. if (errno != 0
  172. || pid == G.mypid
  173. /* "this PID doesn't use specified FILEs or PORT/PROTO": */
  174. || scan_recursive(subpath) == 0
  175. ) {
  176. break;
  177. }
  178. if (option_mask32 & OPT_KILL) {
  179. if (kill(pid, G.killsig) != 0) {
  180. bb_perror_msg("kill pid %s", d_ent->d_name);
  181. G.kill_failed = 1;
  182. }
  183. }
  184. if (!(option_mask32 & OPT_SILENT))
  185. printf("%s ", d_ent->d_name);
  186. retval = 1;
  187. break;
  188. case PROC_DIR_LINKS:
  189. switch (
  190. index_in_substrings(
  191. "cwd" "\0" "exe" "\0"
  192. "root" "\0" "fd" "\0"
  193. "lib" "\0" "mmap" "\0"
  194. "maps" "\0",
  195. d_ent->d_name
  196. )
  197. ) {
  198. enum {
  199. CWD_LINK,
  200. EXE_LINK,
  201. ROOT_LINK,
  202. FD_DIR_LINKS,
  203. LIB_DIR_LINKS,
  204. MMAP_DIR_LINKS,
  205. MAPS,
  206. };
  207. case CWD_LINK:
  208. case EXE_LINK:
  209. case ROOT_LINK:
  210. goto scan_link;
  211. case FD_DIR_LINKS:
  212. case LIB_DIR_LINKS:
  213. case MMAP_DIR_LINKS:
  214. stop_scan = scan_recursive(subpath);
  215. if (stop_scan)
  216. retval = stop_scan;
  217. break;
  218. case MAPS:
  219. stop_scan = scan_proc_net_or_maps(subpath, 0);
  220. if (stop_scan)
  221. retval = stop_scan;
  222. default:
  223. break;
  224. }
  225. break;
  226. case PROC_SUBDIR_LINKS:
  227. scan_link:
  228. if (stat(subpath, &statbuf) < 0)
  229. break;
  230. stop_scan = search_dev_inode(&statbuf);
  231. if (stop_scan)
  232. retval = stop_scan;
  233. default:
  234. break;
  235. }
  236. free(subpath);
  237. }
  238. closedir(d);
  239. G.recursion_depth--;
  240. return retval;
  241. }
  242. int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  243. int fuser_main(int argc UNUSED_PARAM, char **argv)
  244. {
  245. char **pp;
  246. INIT_G();
  247. /* Handle -SIGNAL. Oh my... */
  248. pp = argv;
  249. while (*++pp) {
  250. int sig;
  251. char *arg = *pp;
  252. if (arg[0] != '-')
  253. continue;
  254. if (arg[1] == '-' && arg[2] == '\0') /* "--" */
  255. break;
  256. if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
  257. continue; /* it's "-4" or "-6" */
  258. sig = get_signum(&arg[1]);
  259. if (sig < 0)
  260. continue;
  261. /* "-SIGNAL" option found. Remove it and bail out */
  262. G.killsig = sig;
  263. do {
  264. pp[0] = arg = pp[1];
  265. pp++;
  266. } while (arg);
  267. break;
  268. }
  269. opt_complementary = "-1"; /* at least one param */
  270. getopt32(argv, OPTION_STRING);
  271. argv += optind;
  272. pp = argv;
  273. while (*pp) {
  274. /* parse net arg */
  275. unsigned port;
  276. char path[sizeof("/proc/net/TCP6")];
  277. strcpy(path, "/proc/net/");
  278. if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2
  279. && access(path, R_OK) == 0
  280. ) {
  281. /* PORT/PROTO */
  282. scan_proc_net_or_maps(path, port);
  283. } else {
  284. /* FILE */
  285. struct stat statbuf;
  286. xstat(*pp, &statbuf);
  287. add_inode(&statbuf);
  288. }
  289. pp++;
  290. }
  291. if (scan_recursive("/proc")) {
  292. if (!(option_mask32 & OPT_SILENT))
  293. bb_putchar('\n');
  294. return G.kill_failed;
  295. }
  296. return EXIT_FAILURE;
  297. }