blob: 7837ff88355eb17154404c13c76576f43bfa9035 [file] [log] [blame]
Bernhard Reutner-Fischere15d7572006-06-02 20:56:16 +00001/* vi: set sw=4 ts=4: */
Rob Landleyaa872762005-10-28 13:05:12 +00002/*
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003 * tiny fuser implementation
4 *
Rob Landleyaa872762005-10-28 13:05:12 +00005 * Copyright 2004 Tony J. White
6 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02007 * Licensed under GPLv2, see file LICENSE in this source tree.
Rob Landleyaa872762005-10-28 13:05:12 +00008 */
9
Pere Orga5bc8c002011-04-11 03:29:49 +020010//usage:#define fuser_trivial_usage
11//usage: "[OPTIONS] FILE or PORT/PROTO"
12//usage:#define fuser_full_usage "\n\n"
13//usage: "Find processes which use FILEs or PORTs\n"
14//usage: "\nOptions:"
15//usage: "\n -m Find processes which use same fs as FILEs"
16//usage: "\n -4,-6 Search only IPv4/IPv6 space"
17//usage: "\n -s Don't display PIDs"
18//usage: "\n -k Kill found processes"
19//usage: "\n -SIGNAL Signal to send (default: KILL)"
20
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000021#include "libbb.h"
Rob Landleyaa872762005-10-28 13:05:12 +000022
Denis Vlasenkoafc41132008-03-17 08:44:58 +000023#define MAX_LINE 255
Rob Landleyaa872762005-10-28 13:05:12 +000024
Denis Vlasenkoafc41132008-03-17 08:44:58 +000025#define OPTION_STRING "mks64"
26enum {
27 OPT_MOUNT = (1 << 0),
28 OPT_KILL = (1 << 1),
29 OPT_SILENT = (1 << 2),
30 OPT_IP6 = (1 << 3),
31 OPT_IP4 = (1 << 4),
32};
Rob Landleyaa872762005-10-28 13:05:12 +000033
34typedef struct inode_list {
Denis Vlasenkoafc41132008-03-17 08:44:58 +000035 struct inode_list *next;
Rob Landleyaa872762005-10-28 13:05:12 +000036 ino_t inode;
37 dev_t dev;
Rob Landleyaa872762005-10-28 13:05:12 +000038} inode_list;
39
40typedef struct pid_list {
Rob Landleyaa872762005-10-28 13:05:12 +000041 struct pid_list *next;
Denis Vlasenkoafc41132008-03-17 08:44:58 +000042 pid_t pid;
Rob Landleyaa872762005-10-28 13:05:12 +000043} pid_list;
44
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020045
46struct globals {
47 pid_list *pid_list_head;
48 inode_list *inode_list_head;
Denys Vlasenkoe8d0a142011-01-16 11:21:15 +010049} FIX_ALIASING;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020050#define G (*(struct globals*)&bb_common_bufsiz1)
51#define INIT_G() do { } while (0)
52
53
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020054static void add_pid(const pid_t pid)
Rob Landleyaa872762005-10-28 13:05:12 +000055{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020056 pid_list **curr = &G.pid_list_head;
57
58 while (*curr) {
59 if ((*curr)->pid == pid)
60 return;
61 curr = &(*curr)->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000062 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020063
64 *curr = xzalloc(sizeof(pid_list));
65 (*curr)->pid = pid;
Rob Landleyaa872762005-10-28 13:05:12 +000066}
67
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020068static void add_inode(const struct stat *st)
Rob Landleyaa872762005-10-28 13:05:12 +000069{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020070 inode_list **curr = &G.inode_list_head;
71
72 while (*curr) {
73 if ((*curr)->dev == st->st_dev
74 && (*curr)->inode == st->st_ino
75 ) {
76 return;
77 }
78 curr = &(*curr)->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000079 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020080
81 *curr = xzalloc(sizeof(inode_list));
82 (*curr)->dev = st->st_dev;
83 (*curr)->inode = st->st_ino;
Rob Landleyaa872762005-10-28 13:05:12 +000084}
85
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020086static void scan_proc_net(const char *path, unsigned port)
Rob Landleyaa872762005-10-28 13:05:12 +000087{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020088 char line[MAX_LINE + 1];
Denis Vlasenko50f7f442007-04-11 23:20:53 +000089 long long uint64_inode;
Denis Vlasenkoafc41132008-03-17 08:44:58 +000090 unsigned tmp_port;
Rob Landleyaa872762005-10-28 13:05:12 +000091 FILE *f;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020092 struct stat st;
Maksym Kryzhanovskyye3657dc2010-06-06 22:56:12 +020093 int fd;
Rob Landleyaa872762005-10-28 13:05:12 +000094
Maksym Kryzhanovskyye3657dc2010-06-06 22:56:12 +020095 /* find socket dev */
96 st.st_dev = 0;
97 fd = socket(AF_INET, SOCK_DGRAM, 0);
98 if (fd >= 0) {
99 fstat(fd, &st);
100 close(fd);
101 }
Rob Landleyaa872762005-10-28 13:05:12 +0000102
Denis Vlasenko5415c852008-07-21 23:05:26 +0000103 f = fopen_for_read(path);
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000104 if (!f)
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200105 return;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000106
107 while (fgets(line, MAX_LINE, f)) {
Denis Vlasenko6b797182008-07-12 09:32:38 +0000108 char addr[68];
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000109 if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
110 "%*x:%*x %*x %*d %*d %llu",
111 addr, &tmp_port, &uint64_inode) == 3
112 ) {
Denis Vlasenkoa46dd892008-07-12 09:20:44 +0000113 int len = strlen(addr);
114 if (len == 8 && (option_mask32 & OPT_IP6))
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000115 continue;
Denis Vlasenkoa46dd892008-07-12 09:20:44 +0000116 if (len > 8 && (option_mask32 & OPT_IP4))
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000117 continue;
118 if (tmp_port == port) {
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200119 st.st_ino = uint64_inode;
120 add_inode(&st);
Rob Landleyaa872762005-10-28 13:05:12 +0000121 }
122 }
Rob Landleyaa872762005-10-28 13:05:12 +0000123 }
124 fclose(f);
Rob Landleyaa872762005-10-28 13:05:12 +0000125}
126
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200127static int search_dev_inode(const struct stat *st)
Rob Landleyaa872762005-10-28 13:05:12 +0000128{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200129 inode_list *ilist = G.inode_list_head;
130
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000131 while (ilist) {
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200132 if (ilist->dev == st->st_dev) {
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000133 if (option_mask32 & OPT_MOUNT)
134 return 1;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200135 if (ilist->inode == st->st_ino)
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000136 return 1;
137 }
138 ilist = ilist->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000139 }
140 return 0;
Rob Landleyaa872762005-10-28 13:05:12 +0000141}
142
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200143static void scan_pid_maps(const char *fname, pid_t pid)
Rob Landleyaa872762005-10-28 13:05:12 +0000144{
145 FILE *file;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000146 char line[MAX_LINE + 1];
Rob Landleyaa872762005-10-28 13:05:12 +0000147 int major, minor;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000148 long long uint64_inode;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200149 struct stat st;
Rob Landleyaa872762005-10-28 13:05:12 +0000150
Denis Vlasenko5415c852008-07-21 23:05:26 +0000151 file = fopen_for_read(fname);
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000152 if (!file)
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200153 return;
154
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000155 while (fgets(line, MAX_LINE, file)) {
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000156 if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3)
157 continue;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200158 st.st_ino = uint64_inode;
159 if (major == 0 && minor == 0 && st.st_ino == 0)
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000160 continue;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200161 st.st_dev = makedev(major, minor);
162 if (search_dev_inode(&st))
163 add_pid(pid);
Rob Landleyaa872762005-10-28 13:05:12 +0000164 }
165 fclose(file);
Rob Landleyaa872762005-10-28 13:05:12 +0000166}
167
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200168static void scan_link(const char *lname, pid_t pid)
Rob Landleyaa872762005-10-28 13:05:12 +0000169{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200170 struct stat st;
Rob Landleyaa872762005-10-28 13:05:12 +0000171
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200172 if (stat(lname, &st) >= 0) {
173 if (search_dev_inode(&st))
174 add_pid(pid);
175 }
Rob Landleyaa872762005-10-28 13:05:12 +0000176}
177
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200178static void scan_dir_links(const char *dname, pid_t pid)
Rob Landleyaa872762005-10-28 13:05:12 +0000179{
180 DIR *d;
181 struct dirent *de;
182 char *lname;
183
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000184 d = opendir(dname);
185 if (!d)
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200186 return;
187
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000188 while ((de = readdir(d)) != NULL) {
189 lname = concat_subpath_file(dname, de->d_name);
190 if (lname == NULL)
191 continue;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200192 scan_link(lname, pid);
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000193 free(lname);
Rob Landleyaa872762005-10-28 13:05:12 +0000194 }
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000195 closedir(d);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000196}
Rob Landleyaa872762005-10-28 13:05:12 +0000197
Denis Vlasenko5de8a132008-05-28 12:44:22 +0000198/* NB: does chdir internally */
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200199static void scan_proc_pids(void)
Rob Landleyaa872762005-10-28 13:05:12 +0000200{
201 DIR *d;
202 struct dirent *de;
203 pid_t pid;
Rob Landleyaa872762005-10-28 13:05:12 +0000204
Denis Vlasenkocd9d4c82008-05-28 14:57:58 +0000205 xchdir("/proc");
Denis Vlasenko5de8a132008-05-28 12:44:22 +0000206 d = opendir("/proc");
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000207 if (!d)
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200208 return;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000209
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000210 while ((de = readdir(d)) != NULL) {
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000211 pid = (pid_t)bb_strtou(de->d_name, NULL, 10);
212 if (errno)
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000213 continue;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000214 if (chdir(de->d_name) < 0)
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000215 continue;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200216 scan_link("cwd", pid);
217 scan_link("exe", pid);
218 scan_link("root", pid);
219
220 scan_dir_links("fd", pid);
221 scan_dir_links("lib", pid);
222 scan_dir_links("mmap", pid);
223
224 scan_pid_maps("maps", pid);
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000225 xchdir("/proc");
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000226 }
227 closedir(d);
Rob Landleyaa872762005-10-28 13:05:12 +0000228}
229
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000230int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000231int fuser_main(int argc UNUSED_PARAM, char **argv)
Rob Landley31642d72006-03-14 21:45:38 +0000232{
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000233 pid_list *plist;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200234 pid_t mypid;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000235 char **pp;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200236 struct stat st;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000237 unsigned port;
238 int opt;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200239 int exitcode;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000240 int killsig;
241/*
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200242fuser [OPTIONS] FILE or PORT/PROTO
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000243Find processes which use FILEs or PORTs
244 -m Find processes which use same fs as FILEs
245 -4 Search only IPv4 space
246 -6 Search only IPv6 space
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200247 -s Don't display PIDs
248 -k Kill found processes
249 -SIGNAL Signal to send (default: KILL)
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000250*/
251 /* Handle -SIGNAL. Oh my... */
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200252 killsig = SIGKILL; /* yes, the default is not SIGTERM */
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000253 pp = argv;
254 while (*++pp) {
255 char *arg = *pp;
256 if (arg[0] != '-')
257 continue;
258 if (arg[1] == '-' && arg[2] == '\0') /* "--" */
259 break;
260 if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
261 continue; /* it's "-4" or "-6" */
262 opt = get_signum(&arg[1]);
263 if (opt < 0)
264 continue;
265 /* "-SIGNAL" option found. Remove it and bail out */
266 killsig = opt;
267 do {
268 pp[0] = arg = pp[1];
269 pp++;
270 } while (arg);
271 break;
272 }
Rob Landleyaa872762005-10-28 13:05:12 +0000273
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200274 opt_complementary = "-1"; /* at least one param */
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000275 opt = getopt32(argv, OPTION_STRING);
276 argv += optind;
Mike Frysinger70cbb6e2006-04-21 22:04:05 +0000277
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000278 pp = argv;
279 while (*pp) {
Maksym Kryzhanovskyye3657dc2010-06-06 22:56:12 +0200280 /* parse net arg */
281 char path[20], tproto[5];
282 if (sscanf(*pp, "%u/%4s", &port, tproto) != 2)
283 goto file;
284 sprintf(path, "/proc/net/%s", tproto);
285 if (access(path, R_OK) != 0) { /* PORT/PROTO */
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200286 scan_proc_net(path, port);
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000287 } else { /* FILE */
Maksym Kryzhanovskyye3657dc2010-06-06 22:56:12 +0200288 file:
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200289 xstat(*pp, &st);
290 add_inode(&st);
Rob Landleyaa872762005-10-28 13:05:12 +0000291 }
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000292 pp++;
Rob Landleyaa872762005-10-28 13:05:12 +0000293 }
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000294
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200295 scan_proc_pids(); /* changes dir to "/proc" */
Rob Landleyaa872762005-10-28 13:05:12 +0000296
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200297 mypid = getpid();
298 plist = G.pid_list_head;
299 while (1) {
300 if (!plist)
301 return EXIT_FAILURE;
302 if (plist->pid != mypid)
303 break;
304 plist = plist->next;
Rob Landleyaa872762005-10-28 13:05:12 +0000305 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200306
307 exitcode = EXIT_SUCCESS;
308 do {
309 if (plist->pid != mypid) {
310 if (opt & OPT_KILL) {
311 if (kill(plist->pid, killsig) != 0) {
312 bb_perror_msg("kill pid %u", (unsigned)plist->pid);
313 exitcode = EXIT_FAILURE;
314 }
315 }
316 if (!(opt & OPT_SILENT)) {
317 printf("%u ", (unsigned)plist->pid);
318 }
319 }
320 plist = plist->next;
321 } while (plist);
322
323 if (!(opt & (OPT_SILENT))) {
324 bb_putchar('\n');
325 }
326
327 return exitcode;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000328}