blob: 35d77ed1c9a67c076135e8114fd3272c0de060cf [file] [log] [blame]
Rob Landleyaa872762005-10-28 13:05:12 +00001/*
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002 * tiny fuser implementation
3 *
Rob Landleyaa872762005-10-28 13:05:12 +00004 * Copyright 2004 Tony J. White
5 *
6 * May be distributed under the conditions of the
7 * GNU Library General Public License
8 */
9
Rob Landleyaa872762005-10-28 13:05:12 +000010#include <stdio.h>
11#include <stdlib.h>
12#include <unistd.h>
13#include <string.h>
14#include <limits.h>
15#include <dirent.h>
16#include <signal.h>
17#include <sys/types.h>
18#include <sys/ioctl.h>
19#include <sys/stat.h>
20#include <sys/socket.h>
Mike Frysingerda2a7d62006-02-19 22:47:51 +000021#include <sys/sysmacros.h>
Rob Landleyaa872762005-10-28 13:05:12 +000022#include "busybox.h"
23
24#define FUSER_PROC_DIR "/proc"
25#define FUSER_MAX_LINE 255
26
27#define FUSER_OPT_MOUNT 1
28#define FUSER_OPT_KILL 2
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000029#define FUSER_OPT_SILENT 4
30#define FUSER_OPT_IP6 8
31#define FUSER_OPT_IP4 16
Rob Landleyaa872762005-10-28 13:05:12 +000032
33typedef struct inode_list {
34 ino_t inode;
35 dev_t dev;
36 struct inode_list *next;
37} inode_list;
38
39typedef struct pid_list {
40 pid_t pid;
41 struct pid_list *next;
42} pid_list;
43
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000044static int fuser_option(char *option)
Rob Landleyaa872762005-10-28 13:05:12 +000045{
46 int opt = 0;
47
48 if(!(strlen(option))) return 0;
49 if(option[0] != '-') return 0;
Paul Foxf9d40d62006-01-20 21:48:06 +000050 ++option;
Rob Landleyaa872762005-10-28 13:05:12 +000051 while(*option != '\0') {
52 if(*option == 'm') opt |= FUSER_OPT_MOUNT;
53 else if(*option == 'k') opt |= FUSER_OPT_KILL;
54 else if(*option == 's') opt |= FUSER_OPT_SILENT;
55 else if(*option == '6') opt |= FUSER_OPT_IP6;
56 else if(*option == '4') opt |= FUSER_OPT_IP4;
57 else {
58 bb_error_msg_and_die(
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000059 "Unsupported option '%c'", *option);
Rob Landleyaa872762005-10-28 13:05:12 +000060 }
Paul Foxf9d40d62006-01-20 21:48:06 +000061 ++option;
Rob Landleyaa872762005-10-28 13:05:12 +000062 }
63 return opt;
64}
65
66static int fuser_file_to_dev_inode(const char *filename,
67 dev_t *dev, ino_t *inode)
68{
69 struct stat f_stat;
70 if((stat(filename, &f_stat)) < 0) return 0;
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +000071 *inode = f_stat.st_ino;
72 *dev = f_stat.st_dev;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000073 return 1;
Rob Landleyaa872762005-10-28 13:05:12 +000074}
75
Rob Landley31642d72006-03-14 21:45:38 +000076static int fuser_find_socket_dev(dev_t *dev)
77{
Rob Landleyaa872762005-10-28 13:05:12 +000078 int fd = socket(PF_INET, SOCK_DGRAM,0);
79 struct stat buf;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000080
Rob Landleyaa872762005-10-28 13:05:12 +000081 if (fd >= 0 && (fstat(fd, &buf)) == 0) {
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +000082 *dev = buf.st_dev;
Rob Landleyaa872762005-10-28 13:05:12 +000083 close(fd);
84 return 1;
85 }
86 return 0;
87}
88
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000089static int fuser_parse_net_arg(const char *filename,
90 const char **proto, int *port)
Rob Landleyaa872762005-10-28 13:05:12 +000091{
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +000092 char path[sizeof(FUSER_PROC_DIR)+12], tproto[5];
Rob Landleyaa872762005-10-28 13:05:12 +000093
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +000094 if((sscanf(filename, "%d/%4s", port, tproto)) != 2) return 0;
95 sprintf(path, "%s/net/%s", FUSER_PROC_DIR, tproto);
Rob Landleyaa872762005-10-28 13:05:12 +000096 if((access(path, R_OK)) != 0) return 0;
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +000097 *proto = bb_xstrdup(tproto);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000098 return 1;
Rob Landleyaa872762005-10-28 13:05:12 +000099}
100
101static int fuser_add_pid(pid_list *plist, pid_t pid)
102{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000103 pid_list *curr = NULL, *last = NULL;
104
105 if(plist->pid == 0) plist->pid = pid;
106 curr = plist;
107 while(curr != NULL) {
108 if(curr->pid == pid) return 1;
109 last = curr;
110 curr = curr->next;
111 }
112 curr = xmalloc(sizeof(pid_list));
113 last->next = curr;
114 curr->pid = pid;
Rob Landleyaa872762005-10-28 13:05:12 +0000115 curr->next = NULL;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000116 return 1;
Rob Landleyaa872762005-10-28 13:05:12 +0000117}
118
119static int fuser_add_inode(inode_list *ilist, dev_t dev, ino_t inode)
120{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000121 inode_list *curr = NULL, *last = NULL;
122
123 if(!ilist->inode && !ilist->dev) {
Rob Landleyaa872762005-10-28 13:05:12 +0000124 ilist->dev = dev;
125 ilist->inode = inode;
126 }
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000127 curr = ilist;
128 while(curr != NULL) {
129 if(curr->inode == inode && curr->dev == dev) return 1;
130 last = curr;
131 curr = curr->next;
132 }
133 curr = xmalloc(sizeof(inode_list));
134 last->next = curr;
135 curr->dev = dev;
136 curr->inode = inode;
Rob Landleyaa872762005-10-28 13:05:12 +0000137 curr->next = NULL;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000138 return 1;
Rob Landleyaa872762005-10-28 13:05:12 +0000139}
140
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000141static int fuser_scan_proc_net(int opts, const char *proto,
142 int port, inode_list *ilist)
Rob Landleyaa872762005-10-28 13:05:12 +0000143{
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +0000144 char path[sizeof(FUSER_PROC_DIR)+12], line[FUSER_MAX_LINE+1];
Rob Landleyaa872762005-10-28 13:05:12 +0000145 char addr[128];
146 ino_t tmp_inode;
147 dev_t tmp_dev;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000148 long long uint64_inode;
Rob Landleyaa872762005-10-28 13:05:12 +0000149 int tmp_port;
150 FILE *f;
151
152 if(!fuser_find_socket_dev(&tmp_dev)) tmp_dev = 0;
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +0000153 sprintf(path, "%s/net/%s", FUSER_PROC_DIR, proto);
Rob Landleyaa872762005-10-28 13:05:12 +0000154
155 if (!(f = fopen(path, "r"))) return 0;
156 while(fgets(line, FUSER_MAX_LINE, f)) {
157 if(sscanf(line,
158 "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +0000159 "%*x:%*x %*x %*d %*d %llu",
160 addr, &tmp_port, &uint64_inode) == 3) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000161 if((strlen(addr) == 8) &&
Rob Landleyaa872762005-10-28 13:05:12 +0000162 (opts & FUSER_OPT_IP6)) continue;
163 else if((strlen(addr) > 8) &&
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000164 (opts & FUSER_OPT_IP4)) continue;
Rob Landleyaa872762005-10-28 13:05:12 +0000165 if(tmp_port == port) {
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +0000166 tmp_inode = uint64_inode;
Rob Landleyaa872762005-10-28 13:05:12 +0000167 fuser_add_inode(ilist, tmp_dev, tmp_inode);
168 }
169 }
170
171 }
172 fclose(f);
173 return 1;
174}
175
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000176static int fuser_search_dev_inode(int opts, inode_list *ilist,
177 dev_t dev, ino_t inode)
Rob Landleyaa872762005-10-28 13:05:12 +0000178{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000179 inode_list *curr;
180 curr = ilist;
Rob Landleyaa872762005-10-28 13:05:12 +0000181
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000182 while(curr) {
Rob Landleyaa872762005-10-28 13:05:12 +0000183 if((opts & FUSER_OPT_MOUNT) && curr->dev == dev)
184 return 1;
185 if(curr->inode == inode && curr->dev == dev)
186 return 1;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000187 curr = curr->next;
188 }
189 return 0;
Rob Landleyaa872762005-10-28 13:05:12 +0000190}
191
192static int fuser_scan_pid_maps(int opts, const char *fname, pid_t pid,
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000193 inode_list *ilist, pid_list *plist)
Rob Landleyaa872762005-10-28 13:05:12 +0000194{
195 FILE *file;
196 char line[FUSER_MAX_LINE + 1];
197 int major, minor;
198 ino_t inode;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000199 long long uint64_inode;
Rob Landleyaa872762005-10-28 13:05:12 +0000200 dev_t dev;
201
202 if (!(file = fopen(fname, "r"))) return 0;
203 while (fgets(line, FUSER_MAX_LINE, file)) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000204 if(sscanf(line, "%*s %*s %*s %x:%x %llu",
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +0000205 &major, &minor, &uint64_inode) != 3) continue;
206 inode = uint64_inode;
Rob Landleyaa872762005-10-28 13:05:12 +0000207 if(major == 0 && minor == 0 && inode == 0) continue;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000208 dev = makedev(major, minor);
Rob Landleyaa872762005-10-28 13:05:12 +0000209 if(fuser_search_dev_inode(opts, ilist, dev, inode)) {
210 fuser_add_pid(plist, pid);
211 }
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000212
Rob Landleyaa872762005-10-28 13:05:12 +0000213 }
214 fclose(file);
215 return 1;
216}
217
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000218static int fuser_scan_link(int opts, const char *lname, pid_t pid,
219 inode_list *ilist, pid_list *plist)
Rob Landleyaa872762005-10-28 13:05:12 +0000220{
221 ino_t inode;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000222 dev_t dev;
Rob Landleyaa872762005-10-28 13:05:12 +0000223
224 if(!fuser_file_to_dev_inode(lname, &dev, &inode)) return 0;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000225 if(fuser_search_dev_inode(opts, ilist, dev, inode))
Rob Landleyaa872762005-10-28 13:05:12 +0000226 fuser_add_pid(plist, pid);
227 return 1;
228}
229
230static int fuser_scan_dir_links(int opts, const char *dname, pid_t pid,
231 inode_list *ilist, pid_list *plist)
232{
233 DIR *d;
234 struct dirent *de;
235 char *lname;
236
237 if((d = opendir(dname))) {
238 while((de = readdir(d)) != NULL) {
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +0000239 lname = concat_subpath_file(dname, de->d_name);
240 if(lname == NULL)
Rob Landleyaa872762005-10-28 13:05:12 +0000241 continue;
Rob Landleyaa872762005-10-28 13:05:12 +0000242 fuser_scan_link(opts, lname, pid, ilist, plist);
243 free(lname);
244 }
245 closedir(d);
246 }
247 else return 0;
248 return 1;
249
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000250}
Rob Landleyaa872762005-10-28 13:05:12 +0000251
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000252static int fuser_scan_proc_pids(int opts, inode_list *ilist, pid_list *plist)
Rob Landleyaa872762005-10-28 13:05:12 +0000253{
254 DIR *d;
255 struct dirent *de;
256 pid_t pid;
257 char *dname;
258
259 if(!(d = opendir(FUSER_PROC_DIR))) return 0;
260 while((de = readdir(d)) != NULL) {
261 pid = (pid_t)atoi(de->d_name);
262 if(!pid) continue;
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +0000263 dname = concat_subpath_file(FUSER_PROC_DIR, de->d_name);
Rob Landleyaa872762005-10-28 13:05:12 +0000264 if(chdir(dname) < 0) {
265 free(dname);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000266 continue;
Rob Landleyaa872762005-10-28 13:05:12 +0000267 }
268 free(dname);
269 fuser_scan_link(opts, "cwd", pid, ilist, plist);
270 fuser_scan_link(opts, "exe", pid, ilist, plist);
271 fuser_scan_link(opts, "root", pid, ilist, plist);
272 fuser_scan_dir_links(opts, "fd", pid, ilist, plist);
273 fuser_scan_dir_links(opts, "lib", pid, ilist, plist);
274 fuser_scan_dir_links(opts, "mmap", pid, ilist, plist);
275 fuser_scan_pid_maps(opts, "maps", pid, ilist, plist);
"Vladimir N. Oleynik"7eb8e452005-10-28 16:08:47 +0000276 chdir("..");
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000277 }
278 closedir(d);
Rob Landleyaa872762005-10-28 13:05:12 +0000279 return 1;
280}
281
Rob Landley31642d72006-03-14 21:45:38 +0000282static int fuser_print_pid_list(pid_list *plist)
283{
Paul Foxf9d40d62006-01-20 21:48:06 +0000284 pid_list *curr = plist;
Rob Landleyaa872762005-10-28 13:05:12 +0000285
286 if(plist == NULL) return 0;
287 while(curr != NULL) {
288 if(curr->pid > 0) printf("%d ", curr->pid);
289 curr = curr->next;
290 }
291 printf("\n");
292 return 1;
293}
294
Rob Landley31642d72006-03-14 21:45:38 +0000295static int fuser_kill_pid_list(pid_list *plist, int sig)
296{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000297 pid_list *curr = plist;
Rob Landleyaa872762005-10-28 13:05:12 +0000298 pid_t mypid = getpid();
299 int success = 1;
300
301 if(plist == NULL) return 0;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000302 while(curr != NULL) {
Rob Landleyaa872762005-10-28 13:05:12 +0000303 if(curr->pid > 0 && curr->pid != mypid) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000304 if (kill(curr->pid, sig) != 0) {
305 bb_perror_msg(
Rob Landleyaa872762005-10-28 13:05:12 +0000306 "Could not kill pid '%d'", curr->pid);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000307 success = 0;
Rob Landleyaa872762005-10-28 13:05:12 +0000308 }
309 }
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000310 curr = curr->next;
311 }
312 return success;
Rob Landleyaa872762005-10-28 13:05:12 +0000313}
314
Rob Landley31642d72006-03-14 21:45:38 +0000315int fuser_main(int argc, char **argv)
316{
Rob Landleyaa872762005-10-28 13:05:12 +0000317 int port, i, optn;
318 int* fni; /* file name indexes of argv */
319 int fnic = 0; /* file name index count */
320 const char *proto;
321 static int opt = 0; /* FUSER_OPT_ */
322 dev_t dev;
323 ino_t inode;
324 pid_list *pids;
325 inode_list *inodes;
326 int killsig = SIGTERM;
327 int success = 1;
328
Mike Frysinger70cbb6e2006-04-21 22:04:05 +0000329 if (argc < 2)
330 bb_show_usage();
331
Rob Landleyaa872762005-10-28 13:05:12 +0000332 fni = xmalloc(sizeof(int));
333 for(i=1;i<argc;i++) {
334 optn = fuser_option(argv[i]);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000335 if(optn) opt |= optn;
Rob Landleyaa872762005-10-28 13:05:12 +0000336 else if(argv[i][0] == '-') {
337 if(!(u_signal_names(argv[i]+1, &killsig, 0)))
338 killsig = SIGTERM;
339 }
340 else {
341 fni = xrealloc(fni, sizeof(int) * (fnic+2));
342 fni[fnic++] = i;
343 }
344 }
345 if(!fnic) return 1;
346
347 pids = xmalloc(sizeof(pid_list));
348 inodes = xmalloc(sizeof(inode_list));
349 for(i=0;i<fnic;i++) {
350 if(fuser_parse_net_arg(argv[fni[i]], &proto, &port)) {
351 fuser_scan_proc_net(opt, proto, port, inodes);
352 }
353 else {
354 if(!fuser_file_to_dev_inode(
355 argv[fni[i]], &dev, &inode)) {
356 free(pids);
357 free(inodes);
358 bb_perror_msg_and_die(
359 "Could not open '%s'", argv[fni[i]]);
360 }
361 fuser_add_inode(inodes, dev, inode);
362 }
363 }
364 success = fuser_scan_proc_pids(opt, inodes, pids);
365 /* if the first pid in the list is 0, none have been found */
366 if(pids->pid == 0) success = 0;
367 if(success) {
368 if(opt & FUSER_OPT_KILL) {
369 success = fuser_kill_pid_list(pids, killsig);
370 }
371 else if(!(opt & FUSER_OPT_SILENT)) {
372 success = fuser_print_pid_list(pids);
373 }
374 }
375 free(pids);
376 free(inodes);
377 /* return 0 on (success == 1) 1 otherwise */
378 return (success != 1);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000379}