1 /* vi: set sw=4 ts=4: */
3 * tiny fuser implementation
5 * Copyright 2004 Tony J. White
7 * May be distributed under the conditions of the
8 * GNU Library General Public License
15 #define OPTION_STRING "mks64"
19 OPT_SILENT
= (1 << 2),
24 typedef struct inode_list
{
25 struct inode_list
*next
;
30 typedef struct pid_list
{
31 struct pid_list
*next
;
35 static dev_t
find_socket_dev(void)
37 int fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
40 int r
= fstat(fd
, &buf
);
48 static int file_to_dev_inode(const char *filename
, dev_t
*dev
, ino_t
*inode
)
51 if (stat(filename
, &f_stat
))
53 *inode
= f_stat
.st_ino
;
58 static char *parse_net_arg(const char *arg
, unsigned *port
)
60 char path
[20], tproto
[5];
62 if (sscanf(arg
, "%u/%4s", port
, tproto
) != 2)
64 sprintf(path
, "/proc/net/%s", tproto
);
65 if (access(path
, R_OK
) != 0)
67 return xstrdup(tproto
);
70 static pid_list
*add_pid(pid_list
*plist
, pid_t pid
)
72 pid_list
*curr
= plist
;
73 while (curr
!= NULL
) {
78 curr
= xmalloc(sizeof(pid_list
));
84 static inode_list
*add_inode(inode_list
*ilist
, dev_t dev
, ino_t inode
)
86 inode_list
*curr
= ilist
;
87 while (curr
!= NULL
) {
88 if (curr
->inode
== inode
&& curr
->dev
== dev
)
92 curr
= xmalloc(sizeof(inode_list
));
99 static inode_list
*scan_proc_net(const char *proto
,
100 unsigned port
, inode_list
*ilist
)
102 char path
[20], line
[MAX_LINE
+ 1];
105 long long uint64_inode
;
109 tmp_dev
= find_socket_dev();
111 sprintf(path
, "/proc/net/%s", proto
);
112 f
= fopen_for_read(path
);
116 while (fgets(line
, MAX_LINE
, f
)) {
118 if (sscanf(line
, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
119 "%*x:%*x %*x %*d %*d %llu",
120 addr
, &tmp_port
, &uint64_inode
) == 3
122 int len
= strlen(addr
);
123 if (len
== 8 && (option_mask32
& OPT_IP6
))
125 if (len
> 8 && (option_mask32
& OPT_IP4
))
127 if (tmp_port
== port
) {
128 tmp_inode
= uint64_inode
;
129 ilist
= add_inode(ilist
, tmp_dev
, tmp_inode
);
137 static int search_dev_inode(inode_list
*ilist
, dev_t dev
, ino_t inode
)
140 if (ilist
->dev
== dev
) {
141 if (option_mask32
& OPT_MOUNT
)
143 if (ilist
->inode
== inode
)
151 static pid_list
*scan_pid_maps(const char *fname
, pid_t pid
,
152 inode_list
*ilist
, pid_list
*plist
)
155 char line
[MAX_LINE
+ 1];
158 long long uint64_inode
;
161 file
= fopen_for_read(fname
);
164 while (fgets(line
, MAX_LINE
, file
)) {
165 if (sscanf(line
, "%*s %*s %*s %x:%x %llu", &major
, &minor
, &uint64_inode
) != 3)
167 inode
= uint64_inode
;
168 if (major
== 0 && minor
== 0 && inode
== 0)
170 dev
= makedev(major
, minor
);
171 if (search_dev_inode(ilist
, dev
, inode
))
172 plist
= add_pid(plist
, pid
);
178 static pid_list
*scan_link(const char *lname
, pid_t pid
,
179 inode_list
*ilist
, pid_list
*plist
)
184 if (!file_to_dev_inode(lname
, &dev
, &inode
))
186 if (search_dev_inode(ilist
, dev
, inode
))
187 plist
= add_pid(plist
, pid
);
191 static pid_list
*scan_dir_links(const char *dname
, pid_t pid
,
192 inode_list
*ilist
, pid_list
*plist
)
201 while ((de
= readdir(d
)) != NULL
) {
202 lname
= concat_subpath_file(dname
, de
->d_name
);
205 plist
= scan_link(lname
, pid
, ilist
, plist
);
212 /* NB: does chdir internally */
213 static pid_list
*scan_proc_pids(inode_list
*ilist
)
221 d
= opendir("/proc");
226 while ((de
= readdir(d
)) != NULL
) {
227 pid
= (pid_t
)bb_strtou(de
->d_name
, NULL
, 10);
230 if (chdir(de
->d_name
) < 0)
232 plist
= scan_link("cwd", pid
, ilist
, plist
);
233 plist
= scan_link("exe", pid
, ilist
, plist
);
234 plist
= scan_link("root", pid
, ilist
, plist
);
235 plist
= scan_dir_links("fd", pid
, ilist
, plist
);
236 plist
= scan_dir_links("lib", pid
, ilist
, plist
);
237 plist
= scan_dir_links("mmap", pid
, ilist
, plist
);
238 plist
= scan_pid_maps("maps", pid
, ilist
, plist
);
245 static int print_pid_list(pid_list
*plist
)
247 while (plist
!= NULL
) {
248 printf("%u ", (unsigned)plist
->pid
);
255 static int kill_pid_list(pid_list
*plist
, int sig
)
257 pid_t mypid
= getpid();
260 while (plist
!= NULL
) {
261 if (plist
->pid
!= mypid
) {
262 if (kill(plist
->pid
, sig
) != 0) {
263 bb_perror_msg("kill pid %u", (unsigned)plist
->pid
);
272 int fuser_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
273 int fuser_main(int argc UNUSED_PARAM
, char **argv
)
285 fuser [options] FILEs or PORT/PROTOs
286 Find processes which use FILEs or PORTs
287 -m Find processes which use same fs as FILEs
288 -4 Search only IPv4 space
289 -6 Search only IPv6 space
290 -s Silent: just exit with 0 if any processes are found
291 -k Kill found processes (otherwise display PIDs)
292 -SIGNAL Signal to send (default: TERM)
294 /* Handle -SIGNAL. Oh my... */
301 if (arg
[1] == '-' && arg
[2] == '\0') /* "--" */
303 if ((arg
[1] == '4' || arg
[1] == '6') && arg
[2] == '\0')
304 continue; /* it's "-4" or "-6" */
305 opt
= get_signum(&arg
[1]);
308 /* "-SIGNAL" option found. Remove it and bail out */
317 opt
= getopt32(argv
, OPTION_STRING
);
323 char *proto
= parse_net_arg(*pp
, &port
);
324 if (proto
) { /* PORT/PROTO */
325 ilist
= scan_proc_net(proto
, port
, ilist
);
328 if (!file_to_dev_inode(*pp
, &dev
, &inode
))
329 bb_perror_msg_and_die("can't open %s", *pp
);
330 ilist
= add_inode(ilist
, dev
, inode
);
335 plist
= scan_proc_pids(ilist
); /* changes dir to "/proc" */
340 if (opt
& OPT_KILL
) {
341 success
= kill_pid_list(plist
, killsig
);
342 } else if (!(opt
& OPT_SILENT
)) {
343 success
= print_pid_list(plist
);
345 return (success
!= 1); /* 0 == success */