aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2005-10-28 13:05:12 +0000
committerRob Landley <rob@landley.net>2005-10-28 13:05:12 +0000
commitaa87276bed8df4cdcd443f102b58d95c26be9c6d (patch)
tree4592b8c6be22d6bac7182f8b3304b3df56ca85f0
parent30c7de068b50de999b0b73a22e859dd2e9c23204 (diff)
downloadbusybox-w32-aa87276bed8df4cdcd443f102b58d95c26be9c6d.tar.gz
busybox-w32-aa87276bed8df4cdcd443f102b58d95c26be9c6d.tar.bz2
busybox-w32-aa87276bed8df4cdcd443f102b58d95c26be9c6d.zip
New applet, fuser, from Tony J. White. (Needs some cleanup.)
-rw-r--r--include/applets.h3
-rw-r--r--include/usage.h10
-rw-r--r--procps/Config.in9
-rw-r--r--procps/Makefile.in1
-rw-r--r--procps/fuser.c386
5 files changed, 409 insertions, 0 deletions
diff --git a/include/applets.h b/include/applets.h
index 883e55990..2b2c45d67 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -258,6 +258,9 @@
258#ifdef CONFIG_FTPPUT 258#ifdef CONFIG_FTPPUT
259 APPLET(ftpput, ftpgetput_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) 259 APPLET(ftpput, ftpgetput_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
260#endif 260#endif
261#ifdef CONFIG_FUSER
262 APPLET(fuser, fuser_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
263#endif
261#ifdef CONFIG_GETOPT 264#ifdef CONFIG_GETOPT
262 APPLET(getopt, getopt_main, _BB_DIR_BIN, _BB_SUID_NEVER) 265 APPLET(getopt, getopt_main, _BB_DIR_BIN, _BB_SUID_NEVER)
263#endif 266#endif
diff --git a/include/usage.h b/include/usage.h
index 69c50b97b..79a61d20a 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -841,6 +841,16 @@
841 "\t-p, --password Password to be used\n" \ 841 "\t-p, --password Password to be used\n" \
842 "\t-P, --port Port number to be used" 842 "\t-P, --port Port number to be used"
843 843
844#define fuser_trivial_usage \
845 "[options] file OR port/proto"
846#define fuser_full_usage \
847 "Options:\n" \
848 "\t-m Show all processes on the same mounted fs\n" \
849 "\t-k Kill all processes that match.\n" \
850 "\t-s Don't print or kill anything.\n" \
851 "\t-4 When using port/proto only search IPv4 space\n" \
852 "\t-6 When using port/proto only search IPv6 space\n" \
853 "\t-SIGNAL When used with -k, this signal will be used to kill\n"
844#define getopt_trivial_usage \ 854#define getopt_trivial_usage \
845 "[OPTIONS]..." 855 "[OPTIONS]..."
846#define getopt_full_usage \ 856#define getopt_full_usage \
diff --git a/procps/Config.in b/procps/Config.in
index b4832db6b..8a22a5dcf 100644
--- a/procps/Config.in
+++ b/procps/Config.in
@@ -13,6 +13,14 @@ config CONFIG_FREE
13 memory in the system, as well as the buffers used by the kernel. 13 memory in the system, as well as the buffers used by the kernel.
14 The shared memory column should be ignored; it is obsolete. 14 The shared memory column should be ignored; it is obsolete.
15 15
16config CONFIG_FUSER
17 bool "fuser"
18 default n
19 help
20 fuser lists all PIDs (Process IDs) that currently have a given
21 file open. fuser can also list all PIDs that have a given network
22 (TCP or UDP) port open.
23
16config CONFIG_KILL 24config CONFIG_KILL
17 bool "kill" 25 bool "kill"
18 default n 26 default n
@@ -103,5 +111,6 @@ config CONFIG_UPTIME
103 the system has been running, how many users are currently logged 111 the system has been running, how many users are currently logged
104 on, and the system load averages for the past 1, 5, and 15 minutes. 112 on, and the system load averages for the past 1, 5, and 15 minutes.
105 113
114
106endmenu 115endmenu
107 116
diff --git a/procps/Makefile.in b/procps/Makefile.in
index 15633a001..782944e8f 100644
--- a/procps/Makefile.in
+++ b/procps/Makefile.in
@@ -19,6 +19,7 @@ PROCPS-$(CONFIG_RENICE) += renice.o
19PROCPS-$(CONFIG_BB_SYSCTL) += sysctl.o 19PROCPS-$(CONFIG_BB_SYSCTL) += sysctl.o
20PROCPS-$(CONFIG_TOP) += top.o 20PROCPS-$(CONFIG_TOP) += top.o
21PROCPS-$(CONFIG_UPTIME) += uptime.o 21PROCPS-$(CONFIG_UPTIME) += uptime.o
22PROCPS-$(CONFIG_FUSER) += fuser.o
22 23
23libraries-y+=$(PROCPS_DIR)$(PROCPS_AR) 24libraries-y+=$(PROCPS_DIR)$(PROCPS_AR)
24 25
diff --git a/procps/fuser.c b/procps/fuser.c
new file mode 100644
index 000000000..2f53685a2
--- /dev/null
+++ b/procps/fuser.c
@@ -0,0 +1,386 @@
1/*
2 * tiny fuser implementation
3 *
4 * Copyright 2004 Tony J. White
5 *
6 * May be distributed under the conditions of the
7 * GNU Library General Public License
8 */
9
10/* this define is needed for asprintf() */
11#ifndef _GNU_SOURCE
12#define _GNU_SOURCE
13#endif
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <unistd.h>
18#include <string.h>
19#include <limits.h>
20#include <dirent.h>
21#include <signal.h>
22#include <sys/types.h>
23#include <sys/ioctl.h>
24#include <sys/stat.h>
25#include <sys/socket.h>
26#include "busybox.h"
27
28#define FUSER_PROC_DIR "/proc"
29#define FUSER_MAX_LINE 255
30
31#define FUSER_OPT_MOUNT 1
32#define FUSER_OPT_KILL 2
33#define FUSER_OPT_SILENT 4
34#define FUSER_OPT_IP6 8
35#define FUSER_OPT_IP4 16
36
37typedef struct inode_list {
38 ino_t inode;
39 dev_t dev;
40 struct inode_list *next;
41} inode_list;
42
43typedef struct pid_list {
44 pid_t pid;
45 struct pid_list *next;
46} pid_list;
47
48static int fuser_option(char *option)
49{
50 int opt = 0;
51
52 if(!(strlen(option))) return 0;
53 if(option[0] != '-') return 0;
54 *++option;
55 while(*option != '\0') {
56 if(*option == 'm') opt |= FUSER_OPT_MOUNT;
57 else if(*option == 'k') opt |= FUSER_OPT_KILL;
58 else if(*option == 's') opt |= FUSER_OPT_SILENT;
59 else if(*option == '6') opt |= FUSER_OPT_IP6;
60 else if(*option == '4') opt |= FUSER_OPT_IP4;
61 else {
62 bb_error_msg_and_die(
63 "Unsupported option '%c'", *option);
64 }
65 *++option;
66 }
67 return opt;
68}
69
70static int fuser_file_to_dev_inode(const char *filename,
71 dev_t *dev, ino_t *inode)
72{
73 struct stat f_stat;
74 if((stat(filename, &f_stat)) < 0) return 0;
75 memcpy(inode, &f_stat.st_ino, sizeof(ino_t));
76 memcpy(dev, &f_stat.st_dev, sizeof(dev_t));
77 return 1;
78}
79
80static int fuser_find_socket_dev(dev_t *dev) {
81 int fd = socket(PF_INET, SOCK_DGRAM,0);
82 struct stat buf;
83
84 if (fd >= 0 && (fstat(fd, &buf)) == 0) {
85 memcpy(dev, &buf.st_dev, sizeof(dev_t));
86 close(fd);
87 return 1;
88 }
89 return 0;
90}
91
92static int fuser_parse_net_arg(const char *filename,
93 const char **proto, int *port)
94{
95 int tport;
96 char path[PATH_MAX+1], tproto[5];
97
98 if((sscanf(filename, "%d/%4s", &tport, &tproto[0])) != 2) return 0;
99 strncpy(path, FUSER_PROC_DIR, sizeof(FUSER_PROC_DIR));
100 strncat(path, "/net/", 5);
101 strncat(path, tproto, strlen(tproto));
102 if((access(path, R_OK)) != 0) return 0;
103 *proto = bb_xstrndup(tproto, strlen(tproto));
104 memcpy(port, &tport, sizeof(int));
105 return 1;
106}
107
108static int fuser_add_pid(pid_list *plist, pid_t pid)
109{
110 pid_list *curr = NULL, *last = NULL;
111
112 if(plist->pid == 0) plist->pid = pid;
113 curr = plist;
114 while(curr != NULL) {
115 if(curr->pid == pid) return 1;
116 last = curr;
117 curr = curr->next;
118 }
119 curr = xmalloc(sizeof(pid_list));
120 last->next = curr;
121 curr->pid = pid;
122 curr->next = NULL;
123 return 1;
124}
125
126static int fuser_add_inode(inode_list *ilist, dev_t dev, ino_t inode)
127{
128 inode_list *curr = NULL, *last = NULL;
129
130 if(!ilist->inode && !ilist->dev) {
131 ilist->dev = dev;
132 ilist->inode = inode;
133 }
134 curr = ilist;
135 while(curr != NULL) {
136 if(curr->inode == inode && curr->dev == dev) return 1;
137 last = curr;
138 curr = curr->next;
139 }
140 curr = xmalloc(sizeof(inode_list));
141 last->next = curr;
142 curr->dev = dev;
143 curr->inode = inode;
144 curr->next = NULL;
145 return 1;
146}
147
148static int fuser_scan_proc_net(int opts, const char *proto,
149 int port, inode_list *ilist)
150{
151 char path[PATH_MAX+1], line[FUSER_MAX_LINE+1];
152 char addr[128];
153 ino_t tmp_inode;
154 dev_t tmp_dev;
155 int tmp_port;
156 FILE *f;
157
158 if(!fuser_find_socket_dev(&tmp_dev)) tmp_dev = 0;
159
160 strncpy(path, FUSER_PROC_DIR, sizeof(FUSER_PROC_DIR));
161 strncat(path, "/net/", 5);
162 strncat(path, proto, strlen(proto));
163
164 if (!(f = fopen(path, "r"))) return 0;
165 while(fgets(line, FUSER_MAX_LINE, f)) {
166 if(sscanf(line,
167 "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
168 "%*x:%*x %*x %*d %*d %lu",
169 &addr[0], &tmp_port, &tmp_inode) == 3) {
170 if((strlen(addr) == 8) &&
171 (opts & FUSER_OPT_IP6)) continue;
172 else if((strlen(addr) > 8) &&
173 (opts & FUSER_OPT_IP4)) continue;
174 if(tmp_port == port) {
175 fuser_add_inode(ilist, tmp_dev, tmp_inode);
176 }
177 }
178
179 }
180 fclose(f);
181 return 1;
182}
183
184static int fuser_search_dev_inode(int opts, inode_list *ilist,
185 dev_t dev, ino_t inode)
186{
187 inode_list *curr;
188 curr = ilist;
189
190 while(curr) {
191 if((opts & FUSER_OPT_MOUNT) && curr->dev == dev)
192 return 1;
193 if(curr->inode == inode && curr->dev == dev)
194 return 1;
195 curr = curr->next;
196 }
197 return 0;
198}
199
200static int fuser_scan_pid_maps(int opts, const char *fname, pid_t pid,
201 inode_list *ilist, pid_list *plist)
202{
203 FILE *file;
204 char line[FUSER_MAX_LINE + 1];
205 int major, minor;
206 ino_t inode;
207 dev_t dev;
208
209 if (!(file = fopen(fname, "r"))) return 0;
210 while (fgets(line, FUSER_MAX_LINE, file)) {
211 if(sscanf(line, "%*s %*s %*s %x:%x %ld",
212 &major, &minor, &inode) != 3) continue;
213 if(major == 0 && minor == 0 && inode == 0) continue;
214 dev = makedev(major, minor);
215 if(fuser_search_dev_inode(opts, ilist, dev, inode)) {
216 fuser_add_pid(plist, pid);
217 }
218
219 }
220 fclose(file);
221 return 1;
222}
223
224static int fuser_scan_link(int opts, const char *lname, pid_t pid,
225 inode_list *ilist, pid_list *plist)
226{
227 ino_t inode;
228 dev_t dev;
229
230 if(!fuser_file_to_dev_inode(lname, &dev, &inode)) return 0;
231 if(fuser_search_dev_inode(opts, ilist, dev, inode))
232 fuser_add_pid(plist, pid);
233 return 1;
234}
235
236static int fuser_scan_dir_links(int opts, const char *dname, pid_t pid,
237 inode_list *ilist, pid_list *plist)
238{
239 DIR *d;
240 struct dirent *de;
241 char *lname;
242
243 if((d = opendir(dname))) {
244 while((de = readdir(d)) != NULL) {
245 if(!(strcmp(de->d_name, "."))) continue;
246 if(!(strcmp(de->d_name, ".."))) continue;
247 if(asprintf(&lname, "%s/%s", dname, de->d_name) < 0) {
248 free(lname);
249 continue;
250 }
251 fuser_scan_link(opts, lname, pid, ilist, plist);
252 free(lname);
253 }
254 closedir(d);
255 }
256 else return 0;
257 return 1;
258
259}
260
261static int fuser_scan_proc_pids(int opts, inode_list *ilist, pid_list *plist)
262{
263 DIR *d;
264 struct dirent *de;
265 pid_t pid;
266 char *dname;
267
268 if(!(d = opendir(FUSER_PROC_DIR))) return 0;
269 while((de = readdir(d)) != NULL) {
270 pid = (pid_t)atoi(de->d_name);
271 if(!pid) continue;
272 if(asprintf(&dname, "%s/%d/", FUSER_PROC_DIR, pid) < 0) {
273 free(dname);
274 continue;
275 }
276 if(chdir(dname) < 0) {
277 free(dname);
278 continue;
279 }
280 free(dname);
281 fuser_scan_link(opts, "cwd", pid, ilist, plist);
282 fuser_scan_link(opts, "exe", pid, ilist, plist);
283 fuser_scan_link(opts, "root", pid, ilist, plist);
284 fuser_scan_dir_links(opts, "fd", pid, ilist, plist);
285 fuser_scan_dir_links(opts, "lib", pid, ilist, plist);
286 fuser_scan_dir_links(opts, "mmap", pid, ilist, plist);
287 fuser_scan_pid_maps(opts, "maps", pid, ilist, plist);
288 }
289 closedir(d);
290 return 1;
291}
292
293static int fuser_print_pid_list(pid_list *plist) {
294 pid_list *curr;
295 curr = plist;
296
297 if(plist == NULL) return 0;
298 while(curr != NULL) {
299 if(curr->pid > 0) printf("%d ", curr->pid);
300 curr = curr->next;
301 }
302 printf("\n");
303 return 1;
304}
305
306static int fuser_kill_pid_list(pid_list *plist, int sig) {
307 pid_list *curr;
308 curr = plist;
309 pid_t mypid = getpid();
310 int success = 1;
311
312 if(plist == NULL) return 0;
313 while(curr != NULL) {
314 if(curr->pid > 0 && curr->pid != mypid) {
315 if (kill(curr->pid, sig) != 0) {
316 bb_perror_msg(
317 "Could not kill pid '%d'", curr->pid);
318 success = 0;
319 }
320 }
321 curr = curr->next;
322 }
323 return success;
324}
325
326extern int fuser_main(int argc, char **argv) {
327 int port, i, optn;
328 int* fni; /* file name indexes of argv */
329 int fnic = 0; /* file name index count */
330 const char *proto;
331 static int opt = 0; /* FUSER_OPT_ */
332 dev_t dev;
333 ino_t inode;
334 pid_list *pids;
335 inode_list *inodes;
336 int killsig = SIGTERM;
337 int success = 1;
338
339 fni = xmalloc(sizeof(int));
340 for(i=1;i<argc;i++) {
341 optn = fuser_option(argv[i]);
342 if(optn) opt |= optn;
343 else if(argv[i][0] == '-') {
344 if(!(u_signal_names(argv[i]+1, &killsig, 0)))
345 killsig = SIGTERM;
346 }
347 else {
348 fni = xrealloc(fni, sizeof(int) * (fnic+2));
349 fni[fnic++] = i;
350 }
351 }
352 if(!fnic) return 1;
353
354 pids = xmalloc(sizeof(pid_list));
355 inodes = xmalloc(sizeof(inode_list));
356 for(i=0;i<fnic;i++) {
357 if(fuser_parse_net_arg(argv[fni[i]], &proto, &port)) {
358 fuser_scan_proc_net(opt, proto, port, inodes);
359 }
360 else {
361 if(!fuser_file_to_dev_inode(
362 argv[fni[i]], &dev, &inode)) {
363 free(pids);
364 free(inodes);
365 bb_perror_msg_and_die(
366 "Could not open '%s'", argv[fni[i]]);
367 }
368 fuser_add_inode(inodes, dev, inode);
369 }
370 }
371 success = fuser_scan_proc_pids(opt, inodes, pids);
372 /* if the first pid in the list is 0, none have been found */
373 if(pids->pid == 0) success = 0;
374 if(success) {
375 if(opt & FUSER_OPT_KILL) {
376 success = fuser_kill_pid_list(pids, killsig);
377 }
378 else if(!(opt & FUSER_OPT_SILENT)) {
379 success = fuser_print_pid_list(pids);
380 }
381 }
382 free(pids);
383 free(inodes);
384 /* return 0 on (success == 1) 1 otherwise */
385 return (success != 1);
386}