31 |
pid_t pid; |
pid_t pid; |
32 |
} pid_list; |
} pid_list; |
33 |
|
|
|
static dev_t find_socket_dev(void) |
|
|
{ |
|
|
int fd = socket(AF_INET, SOCK_DGRAM, 0); |
|
|
if (fd >= 0) { |
|
|
struct stat buf; |
|
|
int r = fstat(fd, &buf); |
|
|
close(fd); |
|
|
if (r == 0) |
|
|
return buf.st_dev; |
|
|
} |
|
|
return 0; |
|
|
} |
|
34 |
|
|
35 |
static int file_to_dev_inode(const char *filename, dev_t *dev, ino_t *inode) |
struct globals { |
36 |
{ |
pid_list *pid_list_head; |
37 |
struct stat f_stat; |
inode_list *inode_list_head; |
38 |
if (stat(filename, &f_stat)) |
}; |
39 |
return 0; |
#define G (*(struct globals*)&bb_common_bufsiz1) |
40 |
*inode = f_stat.st_ino; |
#define INIT_G() do { } while (0) |
41 |
*dev = f_stat.st_dev; |
|
|
return 1; |
|
|
} |
|
42 |
|
|
43 |
static char *parse_net_arg(const char *arg, unsigned *port) |
static void add_pid(const pid_t pid) |
44 |
{ |
{ |
45 |
char path[20], tproto[5]; |
pid_list **curr = &G.pid_list_head; |
46 |
|
|
47 |
if (sscanf(arg, "%u/%4s", port, tproto) != 2) |
while (*curr) { |
48 |
return NULL; |
if ((*curr)->pid == pid) |
49 |
sprintf(path, "/proc/net/%s", tproto); |
return; |
50 |
if (access(path, R_OK) != 0) |
curr = &(*curr)->next; |
51 |
return NULL; |
} |
|
return xstrdup(tproto); |
|
|
} |
|
52 |
|
|
53 |
static pid_list *add_pid(pid_list *plist, pid_t pid) |
*curr = xzalloc(sizeof(pid_list)); |
54 |
{ |
(*curr)->pid = pid; |
|
pid_list *curr = plist; |
|
|
while (curr != NULL) { |
|
|
if (curr->pid == pid) |
|
|
return plist; |
|
|
curr = curr->next; |
|
|
} |
|
|
curr = xmalloc(sizeof(pid_list)); |
|
|
curr->pid = pid; |
|
|
curr->next = plist; |
|
|
return curr; |
|
55 |
} |
} |
56 |
|
|
57 |
static inode_list *add_inode(inode_list *ilist, dev_t dev, ino_t inode) |
static void add_inode(const struct stat *st) |
58 |
{ |
{ |
59 |
inode_list *curr = ilist; |
inode_list **curr = &G.inode_list_head; |
60 |
while (curr != NULL) { |
|
61 |
if (curr->inode == inode && curr->dev == dev) |
while (*curr) { |
62 |
return ilist; |
if ((*curr)->dev == st->st_dev |
63 |
curr = curr->next; |
&& (*curr)->inode == st->st_ino |
64 |
} |
) { |
65 |
curr = xmalloc(sizeof(inode_list)); |
return; |
66 |
curr->dev = dev; |
} |
67 |
curr->inode = inode; |
curr = &(*curr)->next; |
68 |
curr->next = ilist; |
} |
69 |
return curr; |
|
70 |
|
*curr = xzalloc(sizeof(inode_list)); |
71 |
|
(*curr)->dev = st->st_dev; |
72 |
|
(*curr)->inode = st->st_ino; |
73 |
} |
} |
74 |
|
|
75 |
static inode_list *scan_proc_net(const char *proto, |
static void scan_proc_net(const char *path, unsigned port) |
|
unsigned port, inode_list *ilist) |
|
76 |
{ |
{ |
77 |
char path[20], line[MAX_LINE + 1]; |
char line[MAX_LINE + 1]; |
|
ino_t tmp_inode; |
|
|
dev_t tmp_dev; |
|
78 |
long long uint64_inode; |
long long uint64_inode; |
79 |
unsigned tmp_port; |
unsigned tmp_port; |
80 |
FILE *f; |
FILE *f; |
81 |
|
struct stat st; |
82 |
|
int fd; |
83 |
|
|
84 |
tmp_dev = find_socket_dev(); |
/* find socket dev */ |
85 |
|
st.st_dev = 0; |
86 |
|
fd = socket(AF_INET, SOCK_DGRAM, 0); |
87 |
|
if (fd >= 0) { |
88 |
|
fstat(fd, &st); |
89 |
|
close(fd); |
90 |
|
} |
91 |
|
|
|
sprintf(path, "/proc/net/%s", proto); |
|
92 |
f = fopen_for_read(path); |
f = fopen_for_read(path); |
93 |
if (!f) |
if (!f) |
94 |
return ilist; |
return; |
95 |
|
|
96 |
while (fgets(line, MAX_LINE, f)) { |
while (fgets(line, MAX_LINE, f)) { |
97 |
char addr[68]; |
char addr[68]; |
105 |
if (len > 8 && (option_mask32 & OPT_IP4)) |
if (len > 8 && (option_mask32 & OPT_IP4)) |
106 |
continue; |
continue; |
107 |
if (tmp_port == port) { |
if (tmp_port == port) { |
108 |
tmp_inode = uint64_inode; |
st.st_ino = uint64_inode; |
109 |
ilist = add_inode(ilist, tmp_dev, tmp_inode); |
add_inode(&st); |
110 |
} |
} |
111 |
} |
} |
112 |
} |
} |
113 |
fclose(f); |
fclose(f); |
|
return ilist; |
|
114 |
} |
} |
115 |
|
|
116 |
static int search_dev_inode(inode_list *ilist, dev_t dev, ino_t inode) |
static int search_dev_inode(const struct stat *st) |
117 |
{ |
{ |
118 |
|
inode_list *ilist = G.inode_list_head; |
119 |
|
|
120 |
while (ilist) { |
while (ilist) { |
121 |
if (ilist->dev == dev) { |
if (ilist->dev == st->st_dev) { |
122 |
if (option_mask32 & OPT_MOUNT) |
if (option_mask32 & OPT_MOUNT) |
123 |
return 1; |
return 1; |
124 |
if (ilist->inode == inode) |
if (ilist->inode == st->st_ino) |
125 |
return 1; |
return 1; |
126 |
} |
} |
127 |
ilist = ilist->next; |
ilist = ilist->next; |
129 |
return 0; |
return 0; |
130 |
} |
} |
131 |
|
|
132 |
static pid_list *scan_pid_maps(const char *fname, pid_t pid, |
static void scan_pid_maps(const char *fname, pid_t pid) |
|
inode_list *ilist, pid_list *plist) |
|
133 |
{ |
{ |
134 |
FILE *file; |
FILE *file; |
135 |
char line[MAX_LINE + 1]; |
char line[MAX_LINE + 1]; |
136 |
int major, minor; |
int major, minor; |
|
ino_t inode; |
|
137 |
long long uint64_inode; |
long long uint64_inode; |
138 |
dev_t dev; |
struct stat st; |
139 |
|
|
140 |
file = fopen_for_read(fname); |
file = fopen_for_read(fname); |
141 |
if (!file) |
if (!file) |
142 |
return plist; |
return; |
143 |
|
|
144 |
while (fgets(line, MAX_LINE, file)) { |
while (fgets(line, MAX_LINE, file)) { |
145 |
if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) |
if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3) |
146 |
continue; |
continue; |
147 |
inode = uint64_inode; |
st.st_ino = uint64_inode; |
148 |
if (major == 0 && minor == 0 && inode == 0) |
if (major == 0 && minor == 0 && st.st_ino == 0) |
149 |
continue; |
continue; |
150 |
dev = makedev(major, minor); |
st.st_dev = makedev(major, minor); |
151 |
if (search_dev_inode(ilist, dev, inode)) |
if (search_dev_inode(&st)) |
152 |
plist = add_pid(plist, pid); |
add_pid(pid); |
153 |
} |
} |
154 |
fclose(file); |
fclose(file); |
|
return plist; |
|
155 |
} |
} |
156 |
|
|
157 |
static pid_list *scan_link(const char *lname, pid_t pid, |
static void scan_link(const char *lname, pid_t pid) |
|
inode_list *ilist, pid_list *plist) |
|
158 |
{ |
{ |
159 |
ino_t inode; |
struct stat st; |
|
dev_t dev; |
|
160 |
|
|
161 |
if (!file_to_dev_inode(lname, &dev, &inode)) |
if (stat(lname, &st) >= 0) { |
162 |
return plist; |
if (search_dev_inode(&st)) |
163 |
if (search_dev_inode(ilist, dev, inode)) |
add_pid(pid); |
164 |
plist = add_pid(plist, pid); |
} |
|
return plist; |
|
165 |
} |
} |
166 |
|
|
167 |
static pid_list *scan_dir_links(const char *dname, pid_t pid, |
static void scan_dir_links(const char *dname, pid_t pid) |
|
inode_list *ilist, pid_list *plist) |
|
168 |
{ |
{ |
169 |
DIR *d; |
DIR *d; |
170 |
struct dirent *de; |
struct dirent *de; |
172 |
|
|
173 |
d = opendir(dname); |
d = opendir(dname); |
174 |
if (!d) |
if (!d) |
175 |
return plist; |
return; |
176 |
|
|
177 |
while ((de = readdir(d)) != NULL) { |
while ((de = readdir(d)) != NULL) { |
178 |
lname = concat_subpath_file(dname, de->d_name); |
lname = concat_subpath_file(dname, de->d_name); |
179 |
if (lname == NULL) |
if (lname == NULL) |
180 |
continue; |
continue; |
181 |
plist = scan_link(lname, pid, ilist, plist); |
scan_link(lname, pid); |
182 |
free(lname); |
free(lname); |
183 |
} |
} |
184 |
closedir(d); |
closedir(d); |
|
return plist; |
|
185 |
} |
} |
186 |
|
|
187 |
/* NB: does chdir internally */ |
/* NB: does chdir internally */ |
188 |
static pid_list *scan_proc_pids(inode_list *ilist) |
static void scan_proc_pids(void) |
189 |
{ |
{ |
190 |
DIR *d; |
DIR *d; |
191 |
struct dirent *de; |
struct dirent *de; |
192 |
pid_t pid; |
pid_t pid; |
|
pid_list *plist; |
|
193 |
|
|
194 |
xchdir("/proc"); |
xchdir("/proc"); |
195 |
d = opendir("/proc"); |
d = opendir("/proc"); |
196 |
if (!d) |
if (!d) |
197 |
return NULL; |
return; |
198 |
|
|
|
plist = NULL; |
|
199 |
while ((de = readdir(d)) != NULL) { |
while ((de = readdir(d)) != NULL) { |
200 |
pid = (pid_t)bb_strtou(de->d_name, NULL, 10); |
pid = (pid_t)bb_strtou(de->d_name, NULL, 10); |
201 |
if (errno) |
if (errno) |
202 |
continue; |
continue; |
203 |
if (chdir(de->d_name) < 0) |
if (chdir(de->d_name) < 0) |
204 |
continue; |
continue; |
205 |
plist = scan_link("cwd", pid, ilist, plist); |
scan_link("cwd", pid); |
206 |
plist = scan_link("exe", pid, ilist, plist); |
scan_link("exe", pid); |
207 |
plist = scan_link("root", pid, ilist, plist); |
scan_link("root", pid); |
208 |
plist = scan_dir_links("fd", pid, ilist, plist); |
|
209 |
plist = scan_dir_links("lib", pid, ilist, plist); |
scan_dir_links("fd", pid); |
210 |
plist = scan_dir_links("mmap", pid, ilist, plist); |
scan_dir_links("lib", pid); |
211 |
plist = scan_pid_maps("maps", pid, ilist, plist); |
scan_dir_links("mmap", pid); |
212 |
|
|
213 |
|
scan_pid_maps("maps", pid); |
214 |
xchdir("/proc"); |
xchdir("/proc"); |
215 |
} |
} |
216 |
closedir(d); |
closedir(d); |
|
return plist; |
|
|
} |
|
|
|
|
|
static int print_pid_list(pid_list *plist) |
|
|
{ |
|
|
while (plist != NULL) { |
|
|
printf("%u ", (unsigned)plist->pid); |
|
|
plist = plist->next; |
|
|
} |
|
|
bb_putchar('\n'); |
|
|
return 1; |
|
|
} |
|
|
|
|
|
static int kill_pid_list(pid_list *plist, int sig) |
|
|
{ |
|
|
pid_t mypid = getpid(); |
|
|
int success = 1; |
|
|
|
|
|
while (plist != NULL) { |
|
|
if (plist->pid != mypid) { |
|
|
if (kill(plist->pid, sig) != 0) { |
|
|
bb_perror_msg("kill pid %u", (unsigned)plist->pid); |
|
|
success = 0; |
|
|
} |
|
|
} |
|
|
plist = plist->next; |
|
|
} |
|
|
return success; |
|
217 |
} |
} |
218 |
|
|
219 |
int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
220 |
int fuser_main(int argc UNUSED_PARAM, char **argv) |
int fuser_main(int argc UNUSED_PARAM, char **argv) |
221 |
{ |
{ |
222 |
pid_list *plist; |
pid_list *plist; |
223 |
inode_list *ilist; |
pid_t mypid; |
224 |
char **pp; |
char **pp; |
225 |
dev_t dev; |
struct stat st; |
|
ino_t inode; |
|
226 |
unsigned port; |
unsigned port; |
227 |
int opt; |
int opt; |
228 |
int success; |
int exitcode; |
229 |
int killsig; |
int killsig; |
230 |
/* |
/* |
231 |
fuser [options] FILEs or PORT/PROTOs |
fuser [OPTIONS] FILE or PORT/PROTO |
232 |
Find processes which use FILEs or PORTs |
Find processes which use FILEs or PORTs |
233 |
-m Find processes which use same fs as FILEs |
-m Find processes which use same fs as FILEs |
234 |
-4 Search only IPv4 space |
-4 Search only IPv4 space |
235 |
-6 Search only IPv6 space |
-6 Search only IPv6 space |
236 |
-s Silent: just exit with 0 if any processes are found |
-s Don't display PIDs |
237 |
-k Kill found processes (otherwise display PIDs) |
-k Kill found processes |
238 |
-SIGNAL Signal to send (default: TERM) |
-SIGNAL Signal to send (default: KILL) |
239 |
*/ |
*/ |
240 |
/* Handle -SIGNAL. Oh my... */ |
/* Handle -SIGNAL. Oh my... */ |
241 |
killsig = SIGTERM; |
killsig = SIGKILL; /* yes, the default is not SIGTERM */ |
242 |
pp = argv; |
pp = argv; |
243 |
while (*++pp) { |
while (*++pp) { |
244 |
char *arg = *pp; |
char *arg = *pp; |
260 |
break; |
break; |
261 |
} |
} |
262 |
|
|
263 |
|
opt_complementary = "-1"; /* at least one param */ |
264 |
opt = getopt32(argv, OPTION_STRING); |
opt = getopt32(argv, OPTION_STRING); |
265 |
argv += optind; |
argv += optind; |
266 |
|
|
|
ilist = NULL; |
|
267 |
pp = argv; |
pp = argv; |
268 |
while (*pp) { |
while (*pp) { |
269 |
char *proto = parse_net_arg(*pp, &port); |
/* parse net arg */ |
270 |
if (proto) { /* PORT/PROTO */ |
char path[20], tproto[5]; |
271 |
ilist = scan_proc_net(proto, port, ilist); |
if (sscanf(*pp, "%u/%4s", &port, tproto) != 2) |
272 |
free(proto); |
goto file; |
273 |
|
sprintf(path, "/proc/net/%s", tproto); |
274 |
|
if (access(path, R_OK) != 0) { /* PORT/PROTO */ |
275 |
|
scan_proc_net(path, port); |
276 |
} else { /* FILE */ |
} else { /* FILE */ |
277 |
if (!file_to_dev_inode(*pp, &dev, &inode)) |
file: |
278 |
bb_perror_msg_and_die("can't open '%s'", *pp); |
xstat(*pp, &st); |
279 |
ilist = add_inode(ilist, dev, inode); |
add_inode(&st); |
280 |
} |
} |
281 |
pp++; |
pp++; |
282 |
} |
} |
283 |
|
|
284 |
plist = scan_proc_pids(ilist); /* changes dir to "/proc" */ |
scan_proc_pids(); /* changes dir to "/proc" */ |
285 |
|
|
286 |
if (!plist) |
mypid = getpid(); |
287 |
return EXIT_FAILURE; |
plist = G.pid_list_head; |
288 |
success = 1; |
while (1) { |
289 |
if (opt & OPT_KILL) { |
if (!plist) |
290 |
success = kill_pid_list(plist, killsig); |
return EXIT_FAILURE; |
291 |
} else if (!(opt & OPT_SILENT)) { |
if (plist->pid != mypid) |
292 |
success = print_pid_list(plist); |
break; |
293 |
|
plist = plist->next; |
294 |
} |
} |
295 |
return (success != 1); /* 0 == success */ |
|
296 |
|
exitcode = EXIT_SUCCESS; |
297 |
|
do { |
298 |
|
if (plist->pid != mypid) { |
299 |
|
if (opt & OPT_KILL) { |
300 |
|
if (kill(plist->pid, killsig) != 0) { |
301 |
|
bb_perror_msg("kill pid %u", (unsigned)plist->pid); |
302 |
|
exitcode = EXIT_FAILURE; |
303 |
|
} |
304 |
|
} |
305 |
|
if (!(opt & OPT_SILENT)) { |
306 |
|
printf("%u ", (unsigned)plist->pid); |
307 |
|
} |
308 |
|
} |
309 |
|
plist = plist->next; |
310 |
|
} while (plist); |
311 |
|
|
312 |
|
if (!(opt & (OPT_SILENT))) { |
313 |
|
bb_putchar('\n'); |
314 |
|
} |
315 |
|
|
316 |
|
return exitcode; |
317 |
} |
} |