1 /* vi: set sw=4 ts=4: */
3 * tiny fuser implementation
5 * Copyright 2004 Tony J. White
7 * Licensed under GPLv2, see file LICENSE in this tarball for details.
14 #define OPTION_STRING "mks64"
18 OPT_SILENT
= (1 << 2),
23 typedef struct inode_list
{
24 struct inode_list
*next
;
29 typedef struct pid_list
{
30 struct pid_list
*next
;
34 static dev_t
find_socket_dev(void)
36 int fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
39 int r
= fstat(fd
, &buf
);
47 static int file_to_dev_inode(const char *filename
, dev_t
*dev
, ino_t
*inode
)
50 if (stat(filename
, &f_stat
))
52 *inode
= f_stat
.st_ino
;
57 static char *parse_net_arg(const char *arg
, unsigned *port
)
59 char path
[20], tproto
[5];
61 if (sscanf(arg
, "%u/%4s", port
, tproto
) != 2)
63 sprintf(path
, "/proc/net/%s", tproto
);
64 if (access(path
, R_OK
) != 0)
66 return xstrdup(tproto
);
69 static pid_list
*add_pid(pid_list
*plist
, pid_t pid
)
71 pid_list
*curr
= plist
;
72 while (curr
!= NULL
) {
77 curr
= xmalloc(sizeof(pid_list
));
83 static inode_list
*add_inode(inode_list
*ilist
, dev_t dev
, ino_t inode
)
85 inode_list
*curr
= ilist
;
86 while (curr
!= NULL
) {
87 if (curr
->inode
== inode
&& curr
->dev
== dev
)
91 curr
= xmalloc(sizeof(inode_list
));
98 static inode_list
*scan_proc_net(const char *proto
,
99 unsigned port
, inode_list
*ilist
)
101 char path
[20], line
[MAX_LINE
+ 1];
104 long long uint64_inode
;
108 tmp_dev
= find_socket_dev();
110 sprintf(path
, "/proc/net/%s", proto
);
111 f
= fopen_for_read(path
);
115 while (fgets(line
, MAX_LINE
, f
)) {
117 if (sscanf(line
, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
118 "%*x:%*x %*x %*d %*d %llu",
119 addr
, &tmp_port
, &uint64_inode
) == 3
121 int len
= strlen(addr
);
122 if (len
== 8 && (option_mask32
& OPT_IP6
))
124 if (len
> 8 && (option_mask32
& OPT_IP4
))
126 if (tmp_port
== port
) {
127 tmp_inode
= uint64_inode
;
128 ilist
= add_inode(ilist
, tmp_dev
, tmp_inode
);
136 static int search_dev_inode(inode_list
*ilist
, dev_t dev
, ino_t inode
)
139 if (ilist
->dev
== dev
) {
140 if (option_mask32
& OPT_MOUNT
)
142 if (ilist
->inode
== inode
)
150 static pid_list
*scan_pid_maps(const char *fname
, pid_t pid
,
151 inode_list
*ilist
, pid_list
*plist
)
154 char line
[MAX_LINE
+ 1];
157 long long uint64_inode
;
160 file
= fopen_for_read(fname
);
163 while (fgets(line
, MAX_LINE
, file
)) {
164 if (sscanf(line
, "%*s %*s %*s %x:%x %llu", &major
, &minor
, &uint64_inode
) != 3)
166 inode
= uint64_inode
;
167 if (major
== 0 && minor
== 0 && inode
== 0)
169 dev
= makedev(major
, minor
);
170 if (search_dev_inode(ilist
, dev
, inode
))
171 plist
= add_pid(plist
, pid
);
177 static pid_list
*scan_link(const char *lname
, pid_t pid
,
178 inode_list
*ilist
, pid_list
*plist
)
183 if (!file_to_dev_inode(lname
, &dev
, &inode
))
185 if (search_dev_inode(ilist
, dev
, inode
))
186 plist
= add_pid(plist
, pid
);
190 static pid_list
*scan_dir_links(const char *dname
, pid_t pid
,
191 inode_list
*ilist
, pid_list
*plist
)
200 while ((de
= readdir(d
)) != NULL
) {
201 lname
= concat_subpath_file(dname
, de
->d_name
);
204 plist
= scan_link(lname
, pid
, ilist
, plist
);
211 /* NB: does chdir internally */
212 static pid_list
*scan_proc_pids(inode_list
*ilist
)
220 d
= opendir("/proc");
225 while ((de
= readdir(d
)) != NULL
) {
226 pid
= (pid_t
)bb_strtou(de
->d_name
, NULL
, 10);
229 if (chdir(de
->d_name
) < 0)
231 plist
= scan_link("cwd", pid
, ilist
, plist
);
232 plist
= scan_link("exe", pid
, ilist
, plist
);
233 plist
= scan_link("root", pid
, ilist
, plist
);
234 plist
= scan_dir_links("fd", pid
, ilist
, plist
);
235 plist
= scan_dir_links("lib", pid
, ilist
, plist
);
236 plist
= scan_dir_links("mmap", pid
, ilist
, plist
);
237 plist
= scan_pid_maps("maps", pid
, ilist
, plist
);
244 static int print_pid_list(pid_list
*plist
)
246 while (plist
!= NULL
) {
247 printf("%u ", (unsigned)plist
->pid
);
254 static int kill_pid_list(pid_list
*plist
, int sig
)
256 pid_t mypid
= getpid();
259 while (plist
!= NULL
) {
260 if (plist
->pid
!= mypid
) {
261 if (kill(plist
->pid
, sig
) != 0) {
262 bb_perror_msg("kill pid %u", (unsigned)plist
->pid
);
271 int fuser_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
272 int fuser_main(int argc UNUSED_PARAM
, char **argv
)
284 fuser [options] FILEs or PORT/PROTOs
285 Find processes which use FILEs or PORTs
286 -m Find processes which use same fs as FILEs
287 -4 Search only IPv4 space
288 -6 Search only IPv6 space
289 -s Silent: just exit with 0 if any processes are found
290 -k Kill found processes (otherwise display PIDs)
291 -SIGNAL Signal to send (default: TERM)
293 /* Handle -SIGNAL. Oh my... */
300 if (arg
[1] == '-' && arg
[2] == '\0') /* "--" */
302 if ((arg
[1] == '4' || arg
[1] == '6') && arg
[2] == '\0')
303 continue; /* it's "-4" or "-6" */
304 opt
= get_signum(&arg
[1]);
307 /* "-SIGNAL" option found. Remove it and bail out */
316 opt
= getopt32(argv
, OPTION_STRING
);
322 char *proto
= parse_net_arg(*pp
, &port
);
323 if (proto
) { /* PORT/PROTO */
324 ilist
= scan_proc_net(proto
, port
, ilist
);
327 if (!file_to_dev_inode(*pp
, &dev
, &inode
))
328 bb_perror_msg_and_die("can't open '%s'", *pp
);
329 ilist
= add_inode(ilist
, dev
, inode
);
334 plist
= scan_proc_pids(ilist
); /* changes dir to "/proc" */
339 if (opt
& OPT_KILL
) {
340 success
= kill_pid_list(plist
, killsig
);
341 } else if (!(opt
& OPT_SILENT
)) {
342 success
= print_pid_list(plist
);
344 return (success
!= 1); /* 0 == success */