fuser.c 6.2 KB

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