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