fuser.c 7.2 KB

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