blob: 05b52abb1c30edcce351694b1fe6a53c21283983
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * tiny fuser implementation |
4 | * |
5 | * Copyright 2004 Tony J. White |
6 | * |
7 | * Licensed under GPLv2, see file LICENSE in this source tree. |
8 | */ |
9 | |
10 | //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: "\n -m Find processes which use same fs as FILEs" |
15 | //usage: "\n -4,-6 Search only IPv4/IPv6 space" |
16 | //usage: "\n -s Don't display PIDs" |
17 | //usage: "\n -k Kill found processes" |
18 | //usage: "\n -SIGNAL Signal to send (default: KILL)" |
19 | |
20 | #include "libbb.h" |
21 | |
22 | #define MAX_LINE 255 |
23 | |
24 | #define OPTION_STRING "mks64" |
25 | enum { |
26 | OPT_MOUNT = (1 << 0), |
27 | OPT_KILL = (1 << 1), |
28 | OPT_SILENT = (1 << 2), |
29 | OPT_IP6 = (1 << 3), |
30 | OPT_IP4 = (1 << 4), |
31 | }; |
32 | |
33 | typedef struct inode_list { |
34 | struct inode_list *next; |
35 | ino_t inode; |
36 | dev_t dev; |
37 | } inode_list; |
38 | |
39 | struct globals { |
40 | int recursion_depth; |
41 | pid_t mypid; |
42 | inode_list *inode_list_head; |
43 | smallint kill_failed; |
44 | int killsig; |
45 | } FIX_ALIASING; |
46 | #define G (*(struct globals*)&bb_common_bufsiz1) |
47 | #define INIT_G() do { \ |
48 | G.mypid = getpid(); \ |
49 | G.killsig = SIGKILL; \ |
50 | } while (0) |
51 | |
52 | static void add_inode(const struct stat *st) |
53 | { |
54 | inode_list **curr = &G.inode_list_head; |
55 | |
56 | while (*curr) { |
57 | if ((*curr)->dev == st->st_dev |
58 | && (*curr)->inode == st->st_ino |
59 | ) { |
60 | return; |
61 | } |
62 | curr = &(*curr)->next; |
63 | } |
64 | |
65 | *curr = xzalloc(sizeof(inode_list)); |
66 | (*curr)->dev = st->st_dev; |
67 | (*curr)->inode = st->st_ino; |
68 | } |
69 | |
70 | static smallint search_dev_inode(const struct stat *st) |
71 | { |
72 | inode_list *ilist = G.inode_list_head; |
73 | |
74 | while (ilist) { |
75 | if (ilist->dev == st->st_dev) { |
76 | if (option_mask32 & OPT_MOUNT) |
77 | return 1; |
78 | if (ilist->inode == st->st_ino) |
79 | return 1; |
80 | } |
81 | ilist = ilist->next; |
82 | } |
83 | return 0; |
84 | } |
85 | |
86 | enum { |
87 | PROC_NET = 0, |
88 | PROC_DIR, |
89 | PROC_DIR_LINKS, |
90 | PROC_SUBDIR_LINKS, |
91 | }; |
92 | |
93 | static smallint scan_proc_net_or_maps(const char *path, unsigned port) |
94 | { |
95 | FILE *f; |
96 | char line[MAX_LINE + 1], addr[68]; |
97 | int major, minor, r; |
98 | long long uint64_inode; |
99 | unsigned tmp_port; |
100 | smallint retval; |
101 | struct stat statbuf; |
102 | const char *fmt; |
103 | void *fag, *sag; |
104 | |
105 | f = fopen_for_read(path); |
106 | if (!f) |
107 | return 0; |
108 | |
109 | if (G.recursion_depth == PROC_NET) { |
110 | int fd; |
111 | |
112 | /* find socket dev */ |
113 | statbuf.st_dev = 0; |
114 | fd = socket(AF_INET, SOCK_DGRAM, 0); |
115 | if (fd >= 0) { |
116 | fstat(fd, &statbuf); |
117 | close(fd); |
118 | } |
119 | |
120 | fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x " |
121 | "%*x:%*x %*x:%*x %*x %*d %*d %llu"; |
122 | fag = addr; |
123 | sag = &tmp_port; |
124 | } else { |
125 | fmt = "%*s %*s %*s %x:%x %llu"; |
126 | fag = &major; |
127 | sag = &minor; |
128 | } |
129 | |
130 | retval = 0; |
131 | while (fgets(line, MAX_LINE, f)) { |
132 | r = sscanf(line, fmt, fag, sag, &uint64_inode); |
133 | if (r != 3) |
134 | continue; |
135 | |
136 | statbuf.st_ino = uint64_inode; |
137 | if (G.recursion_depth == PROC_NET) { |
138 | r = strlen(addr); |
139 | if (r == 8 && (option_mask32 & OPT_IP6)) |
140 | continue; |
141 | if (r > 8 && (option_mask32 & OPT_IP4)) |
142 | continue; |
143 | if (tmp_port == port) |
144 | add_inode(&statbuf); |
145 | } else { |
146 | if (major != 0 && minor != 0 && statbuf.st_ino != 0) { |
147 | statbuf.st_dev = makedev(major, minor); |
148 | retval = search_dev_inode(&statbuf); |
149 | if (retval) |
150 | break; |
151 | } |
152 | } |
153 | } |
154 | fclose(f); |
155 | |
156 | return retval; |
157 | } |
158 | |
159 | static smallint scan_recursive(const char *path) |
160 | { |
161 | DIR *d; |
162 | struct dirent *d_ent; |
163 | smallint stop_scan; |
164 | smallint retval; |
165 | |
166 | d = opendir(path); |
167 | if (d == NULL) |
168 | return 0; |
169 | |
170 | G.recursion_depth++; |
171 | retval = 0; |
172 | stop_scan = 0; |
173 | while (!stop_scan && (d_ent = readdir(d)) != NULL) { |
174 | struct stat statbuf; |
175 | pid_t pid; |
176 | char *subpath; |
177 | |
178 | subpath = concat_subpath_file(path, d_ent->d_name); |
179 | if (subpath == NULL) |
180 | continue; /* . or .. */ |
181 | |
182 | switch (G.recursion_depth) { |
183 | case PROC_DIR: |
184 | pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10); |
185 | if (errno != 0 |
186 | || pid == G.mypid |
187 | /* "this PID doesn't use specified FILEs or PORT/PROTO": */ |
188 | || scan_recursive(subpath) == 0 |
189 | ) { |
190 | break; |
191 | } |
192 | if (option_mask32 & OPT_KILL) { |
193 | if (kill(pid, G.killsig) != 0) { |
194 | bb_perror_msg("kill pid %s", d_ent->d_name); |
195 | G.kill_failed = 1; |
196 | } |
197 | } |
198 | if (!(option_mask32 & OPT_SILENT)) |
199 | printf("%s ", d_ent->d_name); |
200 | retval = 1; |
201 | break; |
202 | |
203 | case PROC_DIR_LINKS: |
204 | switch ( |
205 | index_in_substrings( |
206 | "cwd" "\0" "exe" "\0" |
207 | "root" "\0" "fd" "\0" |
208 | "lib" "\0" "mmap" "\0" |
209 | "maps" "\0", |
210 | d_ent->d_name |
211 | ) |
212 | ) { |
213 | enum { |
214 | CWD_LINK, |
215 | EXE_LINK, |
216 | ROOT_LINK, |
217 | FD_DIR_LINKS, |
218 | LIB_DIR_LINKS, |
219 | MMAP_DIR_LINKS, |
220 | MAPS, |
221 | }; |
222 | case CWD_LINK: |
223 | case EXE_LINK: |
224 | case ROOT_LINK: |
225 | goto scan_link; |
226 | case FD_DIR_LINKS: |
227 | case LIB_DIR_LINKS: |
228 | case MMAP_DIR_LINKS: |
229 | stop_scan = scan_recursive(subpath); |
230 | if (stop_scan) |
231 | retval = stop_scan; |
232 | break; |
233 | case MAPS: |
234 | stop_scan = scan_proc_net_or_maps(subpath, 0); |
235 | if (stop_scan) |
236 | retval = stop_scan; |
237 | default: |
238 | break; |
239 | } |
240 | break; |
241 | case PROC_SUBDIR_LINKS: |
242 | scan_link: |
243 | if (stat(subpath, &statbuf) < 0) |
244 | break; |
245 | stop_scan = search_dev_inode(&statbuf); |
246 | if (stop_scan) |
247 | retval = stop_scan; |
248 | default: |
249 | break; |
250 | } |
251 | free(subpath); |
252 | } |
253 | closedir(d); |
254 | G.recursion_depth--; |
255 | return retval; |
256 | } |
257 | |
258 | int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
259 | int fuser_main(int argc UNUSED_PARAM, char **argv) |
260 | { |
261 | char **pp; |
262 | |
263 | INIT_G(); |
264 | |
265 | /* Handle -SIGNAL. Oh my... */ |
266 | pp = argv; |
267 | while (*++pp) { |
268 | int sig; |
269 | char *arg = *pp; |
270 | |
271 | if (arg[0] != '-') |
272 | continue; |
273 | if (arg[1] == '-' && arg[2] == '\0') /* "--" */ |
274 | break; |
275 | if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') |
276 | continue; /* it's "-4" or "-6" */ |
277 | sig = get_signum(&arg[1]); |
278 | if (sig < 0) |
279 | continue; |
280 | /* "-SIGNAL" option found. Remove it and bail out */ |
281 | G.killsig = sig; |
282 | do { |
283 | pp[0] = arg = pp[1]; |
284 | pp++; |
285 | } while (arg); |
286 | break; |
287 | } |
288 | |
289 | opt_complementary = "-1"; /* at least one param */ |
290 | getopt32(argv, OPTION_STRING); |
291 | argv += optind; |
292 | |
293 | pp = argv; |
294 | while (*pp) { |
295 | /* parse net arg */ |
296 | unsigned port; |
297 | char path[sizeof("/proc/net/TCP6")]; |
298 | |
299 | strcpy(path, "/proc/net/"); |
300 | if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2 |
301 | && access(path, R_OK) == 0 |
302 | ) { |
303 | /* PORT/PROTO */ |
304 | scan_proc_net_or_maps(path, port); |
305 | } else { |
306 | /* FILE */ |
307 | struct stat statbuf; |
308 | xstat(*pp, &statbuf); |
309 | add_inode(&statbuf); |
310 | } |
311 | pp++; |
312 | } |
313 | |
314 | if (scan_recursive("/proc")) { |
315 | if (!(option_mask32 & OPT_SILENT)) |
316 | bb_putchar('\n'); |
317 | return G.kill_failed; |
318 | } |
319 | |
320 | return EXIT_FAILURE; |
321 | } |
322 |