Contents of /tags/mkinitrd-6_2_3/busybox/procps/fuser.c
Parent Directory | Revision Log
Revision 1054 -
(show annotations)
(download)
Mon May 31 20:54:44 2010 UTC (14 years, 4 months ago) by niro
File MIME type: text/plain
File size: 7321 byte(s)
Mon May 31 20:54:44 2010 UTC (14 years, 4 months ago) by niro
File MIME type: text/plain
File size: 7321 byte(s)
tagged 'mkinitrd-6_2_3'
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 tarball for details. |
8 | */ |
9 | |
10 | #include "libbb.h" |
11 | |
12 | #define MAX_LINE 255 |
13 | |
14 | #define OPTION_STRING "mks64" |
15 | enum { |
16 | OPT_MOUNT = (1 << 0), |
17 | OPT_KILL = (1 << 1), |
18 | OPT_SILENT = (1 << 2), |
19 | OPT_IP6 = (1 << 3), |
20 | OPT_IP4 = (1 << 4), |
21 | }; |
22 | |
23 | typedef struct inode_list { |
24 | struct inode_list *next; |
25 | ino_t inode; |
26 | dev_t dev; |
27 | } inode_list; |
28 | |
29 | typedef struct pid_list { |
30 | struct pid_list *next; |
31 | pid_t pid; |
32 | } pid_list; |
33 | |
34 | static dev_t find_socket_dev(void) |
35 | { |
36 | int fd = socket(AF_INET, SOCK_DGRAM, 0); |
37 | if (fd >= 0) { |
38 | struct stat buf; |
39 | int r = fstat(fd, &buf); |
40 | close(fd); |
41 | if (r == 0) |
42 | return buf.st_dev; |
43 | } |
44 | return 0; |
45 | } |
46 | |
47 | static int file_to_dev_inode(const char *filename, dev_t *dev, ino_t *inode) |
48 | { |
49 | struct stat f_stat; |
50 | if (stat(filename, &f_stat)) |
51 | return 0; |
52 | *inode = f_stat.st_ino; |
53 | *dev = f_stat.st_dev; |
54 | return 1; |
55 | } |
56 | |
57 | static char *parse_net_arg(const char *arg, unsigned *port) |
58 | { |
59 | char path[20], tproto[5]; |
60 | |
61 | if (sscanf(arg, "%u/%4s", port, tproto) != 2) |
62 | return NULL; |
63 | sprintf(path, "/proc/net/%s", tproto); |
64 | if (access(path, R_OK) != 0) |
65 | return NULL; |
66 | return xstrdup(tproto); |
67 | } |
68 | |
69 | static pid_list *add_pid(pid_list *plist, pid_t pid) |
70 | { |
71 | pid_list *curr = plist; |
72 | while (curr != NULL) { |
73 | if (curr->pid == pid) |
74 | return plist; |
75 | curr = curr->next; |
76 | } |
77 | curr = xmalloc(sizeof(pid_list)); |
78 | curr->pid = pid; |
79 | curr->next = plist; |
80 | return curr; |
81 | } |
82 | |
83 | static inode_list *add_inode(inode_list *ilist, dev_t dev, ino_t inode) |
84 | { |
85 | inode_list *curr = ilist; |
86 | while (curr != NULL) { |
87 | if (curr->inode == inode && curr->dev == dev) |
88 | return ilist; |
89 | curr = curr->next; |
90 | } |
91 | curr = xmalloc(sizeof(inode_list)); |
92 | curr->dev = dev; |
93 | curr->inode = inode; |
94 | curr->next = ilist; |
95 | return curr; |
96 | } |
97 | |
98 | static inode_list *scan_proc_net(const char *proto, |
99 | unsigned port, inode_list *ilist) |
100 | { |
101 | char path[20], line[MAX_LINE + 1]; |
102 | ino_t tmp_inode; |
103 | dev_t tmp_dev; |
104 | long long uint64_inode; |
105 | unsigned tmp_port; |
106 | FILE *f; |
107 | |
108 | tmp_dev = find_socket_dev(); |
109 | |
110 | sprintf(path, "/proc/net/%s", proto); |
111 | f = fopen_for_read(path); |
112 | if (!f) |
113 | return ilist; |
114 | |
115 | while (fgets(line, MAX_LINE, f)) { |
116 | char addr[68]; |
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 |
120 | ) { |
121 | int len = strlen(addr); |
122 | if (len == 8 && (option_mask32 & OPT_IP6)) |
123 | continue; |
124 | if (len > 8 && (option_mask32 & OPT_IP4)) |
125 | continue; |
126 | if (tmp_port == port) { |
127 | tmp_inode = uint64_inode; |
128 | ilist = add_inode(ilist, tmp_dev, tmp_inode); |
129 | } |
130 | } |
131 | } |
132 | fclose(f); |
133 | return ilist; |
134 | } |
135 | |
136 | static int search_dev_inode(inode_list *ilist, dev_t dev, ino_t inode) |
137 | { |
138 | while (ilist) { |
139 | if (ilist->dev == dev) { |
140 | if (option_mask32 & OPT_MOUNT) |
141 | return 1; |
142 | if (ilist->inode == inode) |
143 | return 1; |
144 | } |
145 | ilist = ilist->next; |
146 | } |
147 | return 0; |
148 | } |
149 | |
150 | static pid_list *scan_pid_maps(const char *fname, pid_t pid, |
151 | inode_list *ilist, pid_list *plist) |
152 | { |
153 | FILE *file; |
154 | char line[MAX_LINE + 1]; |
155 | int major, minor; |
156 | ino_t inode; |
157 | long long uint64_inode; |
158 | dev_t dev; |
159 | |
160 | file = fopen_for_read(fname); |
161 | if (!file) |
162 | return plist; |
163 | while (fgets(line, MAX_LINE, file)) { |
164 | if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) |
165 | continue; |
166 | inode = uint64_inode; |
167 | if (major == 0 && minor == 0 && inode == 0) |
168 | continue; |
169 | dev = makedev(major, minor); |
170 | if (search_dev_inode(ilist, dev, inode)) |
171 | plist = add_pid(plist, pid); |
172 | } |
173 | fclose(file); |
174 | return plist; |
175 | } |
176 | |
177 | static pid_list *scan_link(const char *lname, pid_t pid, |
178 | inode_list *ilist, pid_list *plist) |
179 | { |
180 | ino_t inode; |
181 | dev_t dev; |
182 | |
183 | if (!file_to_dev_inode(lname, &dev, &inode)) |
184 | return plist; |
185 | if (search_dev_inode(ilist, dev, inode)) |
186 | plist = add_pid(plist, pid); |
187 | return plist; |
188 | } |
189 | |
190 | static pid_list *scan_dir_links(const char *dname, pid_t pid, |
191 | inode_list *ilist, pid_list *plist) |
192 | { |
193 | DIR *d; |
194 | struct dirent *de; |
195 | char *lname; |
196 | |
197 | d = opendir(dname); |
198 | if (!d) |
199 | return plist; |
200 | while ((de = readdir(d)) != NULL) { |
201 | lname = concat_subpath_file(dname, de->d_name); |
202 | if (lname == NULL) |
203 | continue; |
204 | plist = scan_link(lname, pid, ilist, plist); |
205 | free(lname); |
206 | } |
207 | closedir(d); |
208 | return plist; |
209 | } |
210 | |
211 | /* NB: does chdir internally */ |
212 | static pid_list *scan_proc_pids(inode_list *ilist) |
213 | { |
214 | DIR *d; |
215 | struct dirent *de; |
216 | pid_t pid; |
217 | pid_list *plist; |
218 | |
219 | xchdir("/proc"); |
220 | d = opendir("/proc"); |
221 | if (!d) |
222 | return NULL; |
223 | |
224 | plist = NULL; |
225 | while ((de = readdir(d)) != NULL) { |
226 | pid = (pid_t)bb_strtou(de->d_name, NULL, 10); |
227 | if (errno) |
228 | continue; |
229 | if (chdir(de->d_name) < 0) |
230 | continue; |
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); |
238 | xchdir("/proc"); |
239 | } |
240 | closedir(d); |
241 | return plist; |
242 | } |
243 | |
244 | static int print_pid_list(pid_list *plist) |
245 | { |
246 | while (plist != NULL) { |
247 | printf("%u ", (unsigned)plist->pid); |
248 | plist = plist->next; |
249 | } |
250 | bb_putchar('\n'); |
251 | return 1; |
252 | } |
253 | |
254 | static int kill_pid_list(pid_list *plist, int sig) |
255 | { |
256 | pid_t mypid = getpid(); |
257 | int success = 1; |
258 | |
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); |
263 | success = 0; |
264 | } |
265 | } |
266 | plist = plist->next; |
267 | } |
268 | return success; |
269 | } |
270 | |
271 | int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
272 | int fuser_main(int argc UNUSED_PARAM, char **argv) |
273 | { |
274 | pid_list *plist; |
275 | inode_list *ilist; |
276 | char **pp; |
277 | dev_t dev; |
278 | ino_t inode; |
279 | unsigned port; |
280 | int opt; |
281 | int success; |
282 | int killsig; |
283 | /* |
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) |
292 | */ |
293 | /* Handle -SIGNAL. Oh my... */ |
294 | killsig = SIGTERM; |
295 | pp = argv; |
296 | while (*++pp) { |
297 | char *arg = *pp; |
298 | if (arg[0] != '-') |
299 | continue; |
300 | if (arg[1] == '-' && arg[2] == '\0') /* "--" */ |
301 | break; |
302 | if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0') |
303 | continue; /* it's "-4" or "-6" */ |
304 | opt = get_signum(&arg[1]); |
305 | if (opt < 0) |
306 | continue; |
307 | /* "-SIGNAL" option found. Remove it and bail out */ |
308 | killsig = opt; |
309 | do { |
310 | pp[0] = arg = pp[1]; |
311 | pp++; |
312 | } while (arg); |
313 | break; |
314 | } |
315 | |
316 | opt = getopt32(argv, OPTION_STRING); |
317 | argv += optind; |
318 | |
319 | ilist = NULL; |
320 | pp = argv; |
321 | while (*pp) { |
322 | char *proto = parse_net_arg(*pp, &port); |
323 | if (proto) { /* PORT/PROTO */ |
324 | ilist = scan_proc_net(proto, port, ilist); |
325 | free(proto); |
326 | } else { /* FILE */ |
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); |
330 | } |
331 | pp++; |
332 | } |
333 | |
334 | plist = scan_proc_pids(ilist); /* changes dir to "/proc" */ |
335 | |
336 | if (!plist) |
337 | return EXIT_FAILURE; |
338 | success = 1; |
339 | if (opt & OPT_KILL) { |
340 | success = kill_pid_list(plist, killsig); |
341 | } else if (!(opt & OPT_SILENT)) { |
342 | success = print_pid_list(plist); |
343 | } |
344 | return (success != 1); /* 0 == success */ |
345 | } |