fuser.c 6.3 KB

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