fuser.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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. char addr[128];
  92. ino_t tmp_inode;
  93. dev_t tmp_dev;
  94. long long uint64_inode;
  95. unsigned tmp_port;
  96. FILE *f;
  97. tmp_dev = find_socket_dev();
  98. sprintf(path, "/proc/net/%s", proto);
  99. f = fopen(path, "r");
  100. if (!f)
  101. return ilist;
  102. while (fgets(line, MAX_LINE, f)) {
  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. if (strlen(addr) == 8 && (option_mask32 & OPT_IP6))
  108. continue;
  109. if (strlen(addr) > 8 && (option_mask32 & OPT_IP4))
  110. continue;
  111. if (tmp_port == port) {
  112. tmp_inode = uint64_inode;
  113. ilist = add_inode(ilist, tmp_dev, tmp_inode);
  114. }
  115. }
  116. }
  117. fclose(f);
  118. return ilist;
  119. }
  120. static int search_dev_inode(inode_list *ilist, dev_t dev, ino_t inode)
  121. {
  122. while (ilist) {
  123. if (ilist->dev == dev) {
  124. if (option_mask32 & OPT_MOUNT)
  125. return 1;
  126. if (ilist->inode == inode)
  127. return 1;
  128. }
  129. ilist = ilist->next;
  130. }
  131. return 0;
  132. }
  133. static pid_list *scan_pid_maps(const char *fname, pid_t pid,
  134. inode_list *ilist, pid_list *plist)
  135. {
  136. FILE *file;
  137. char line[MAX_LINE + 1];
  138. int major, minor;
  139. ino_t inode;
  140. long long uint64_inode;
  141. dev_t dev;
  142. file = fopen(fname, "r");
  143. if (!file)
  144. return plist;
  145. while (fgets(line, MAX_LINE, file)) {
  146. if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3)
  147. continue;
  148. inode = uint64_inode;
  149. if (major == 0 && minor == 0 && inode == 0)
  150. continue;
  151. dev = makedev(major, minor);
  152. if (search_dev_inode(ilist, dev, inode))
  153. plist = add_pid(plist, pid);
  154. }
  155. fclose(file);
  156. return plist;
  157. }
  158. static pid_list *scan_link(const char *lname, pid_t pid,
  159. inode_list *ilist, pid_list *plist)
  160. {
  161. ino_t inode;
  162. dev_t dev;
  163. if (!file_to_dev_inode(lname, &dev, &inode))
  164. return plist;
  165. if (search_dev_inode(ilist, dev, inode))
  166. plist = add_pid(plist, pid);
  167. return plist;
  168. }
  169. static pid_list *scan_dir_links(const char *dname, pid_t pid,
  170. inode_list *ilist, pid_list *plist)
  171. {
  172. DIR *d;
  173. struct dirent *de;
  174. char *lname;
  175. d = opendir(dname);
  176. if (!d)
  177. return plist;
  178. while ((de = readdir(d)) != NULL) {
  179. lname = concat_subpath_file(dname, de->d_name);
  180. if (lname == NULL)
  181. continue;
  182. plist = scan_link(lname, pid, ilist, plist);
  183. free(lname);
  184. }
  185. closedir(d);
  186. return plist;
  187. }
  188. /* NB: does chdir internally */
  189. static pid_list *scan_proc_pids(inode_list *ilist)
  190. {
  191. DIR *d;
  192. struct dirent *de;
  193. pid_t pid;
  194. pid_list *plist;
  195. xchdir("/proc");
  196. d = opendir("/proc");
  197. if (!d)
  198. return NULL;
  199. plist = NULL;
  200. while ((de = readdir(d)) != NULL) {
  201. pid = (pid_t)bb_strtou(de->d_name, NULL, 10);
  202. if (errno)
  203. continue;
  204. if (chdir(de->d_name) < 0)
  205. continue;
  206. plist = scan_link("cwd", pid, ilist, plist);
  207. plist = scan_link("exe", pid, ilist, plist);
  208. plist = scan_link("root", pid, ilist, plist);
  209. plist = scan_dir_links("fd", pid, ilist, plist);
  210. plist = scan_dir_links("lib", pid, ilist, plist);
  211. plist = scan_dir_links("mmap", pid, ilist, plist);
  212. plist = scan_pid_maps("maps", pid, ilist, plist);
  213. xchdir("/proc");
  214. }
  215. closedir(d);
  216. return plist;
  217. }
  218. static int print_pid_list(pid_list *plist)
  219. {
  220. while (plist != NULL) {
  221. printf("%u ", (unsigned)plist->pid);
  222. plist = plist->next;
  223. }
  224. bb_putchar('\n');
  225. return 1;
  226. }
  227. static int kill_pid_list(pid_list *plist, int sig)
  228. {
  229. pid_t mypid = getpid();
  230. int success = 1;
  231. while (plist != NULL) {
  232. if (plist->pid != mypid) {
  233. if (kill(plist->pid, sig) != 0) {
  234. bb_perror_msg("kill pid %u", (unsigned)plist->pid);
  235. success = 0;
  236. }
  237. }
  238. plist = plist->next;
  239. }
  240. return success;
  241. }
  242. int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  243. int fuser_main(int argc ATTRIBUTE_UNUSED, char **argv)
  244. {
  245. pid_list *plist;
  246. inode_list *ilist;
  247. char **pp;
  248. dev_t dev;
  249. ino_t inode;
  250. unsigned port;
  251. int opt;
  252. int success;
  253. int killsig;
  254. /*
  255. fuser [options] FILEs or PORT/PROTOs
  256. Find processes which use FILEs or PORTs
  257. -m Find processes which use same fs as FILEs
  258. -4 Search only IPv4 space
  259. -6 Search only IPv6 space
  260. -s Silent: just exit with 0 if any processes are found
  261. -k Kill found processes (otherwise display PIDs)
  262. -SIGNAL Signal to send (default: TERM)
  263. */
  264. /* Handle -SIGNAL. Oh my... */
  265. killsig = SIGTERM;
  266. pp = argv;
  267. while (*++pp) {
  268. char *arg = *pp;
  269. if (arg[0] != '-')
  270. continue;
  271. if (arg[1] == '-' && arg[2] == '\0') /* "--" */
  272. break;
  273. if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
  274. continue; /* it's "-4" or "-6" */
  275. opt = get_signum(&arg[1]);
  276. if (opt < 0)
  277. continue;
  278. /* "-SIGNAL" option found. Remove it and bail out */
  279. killsig = opt;
  280. do {
  281. pp[0] = arg = pp[1];
  282. pp++;
  283. } while (arg);
  284. break;
  285. }
  286. opt = getopt32(argv, OPTION_STRING);
  287. argv += optind;
  288. ilist = NULL;
  289. pp = argv;
  290. while (*pp) {
  291. char *proto = parse_net_arg(*pp, &port);
  292. if (proto) { /* PORT/PROTO */
  293. ilist = scan_proc_net(proto, port, ilist);
  294. free(proto);
  295. } else { /* FILE */
  296. if (!file_to_dev_inode(*pp, &dev, &inode))
  297. bb_perror_msg_and_die("can't open %s", *pp);
  298. ilist = add_inode(ilist, dev, inode);
  299. }
  300. pp++;
  301. }
  302. plist = scan_proc_pids(ilist); /* changes dir to "/proc" */
  303. if (!plist)
  304. return EXIT_FAILURE;
  305. success = 1;
  306. if (opt & OPT_KILL) {
  307. success = kill_pid_list(plist, killsig);
  308. } else if (!(opt & OPT_SILENT)) {
  309. success = print_pid_list(plist);
  310. }
  311. return (success != 1); /* 0 == success */
  312. }