blob: 05b52abb1c30edcce351694b1fe6a53c21283983 [file] [log] [blame]
Bernhard Reutner-Fischere15d7572006-06-02 20:56:16 +00001/* vi: set sw=4 ts=4: */
Rob Landleyaa872762005-10-28 13:05:12 +00002/*
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003 * tiny fuser implementation
4 *
Rob Landleyaa872762005-10-28 13:05:12 +00005 * Copyright 2004 Tony J. White
6 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02007 * Licensed under GPLv2, see file LICENSE in this source tree.
Rob Landleyaa872762005-10-28 13:05:12 +00008 */
9
Pere Orga5bc8c002011-04-11 03:29:49 +020010//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"
Pere Orga5bc8c002011-04-11 03:29:49 +020014//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
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000020#include "libbb.h"
Rob Landleyaa872762005-10-28 13:05:12 +000021
Denis Vlasenkoafc41132008-03-17 08:44:58 +000022#define MAX_LINE 255
Rob Landleyaa872762005-10-28 13:05:12 +000023
Denis Vlasenkoafc41132008-03-17 08:44:58 +000024#define OPTION_STRING "mks64"
25enum {
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};
Rob Landleyaa872762005-10-28 13:05:12 +000032
33typedef struct inode_list {
Denis Vlasenkoafc41132008-03-17 08:44:58 +000034 struct inode_list *next;
Rob Landleyaa872762005-10-28 13:05:12 +000035 ino_t inode;
36 dev_t dev;
Rob Landleyaa872762005-10-28 13:05:12 +000037} inode_list;
38
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020039struct globals {
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020040 int recursion_depth;
41 pid_t mypid;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020042 inode_list *inode_list_head;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020043 smallint kill_failed;
44 int killsig;
Denys Vlasenkoe8d0a142011-01-16 11:21:15 +010045} FIX_ALIASING;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020046#define G (*(struct globals*)&bb_common_bufsiz1)
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020047#define INIT_G() do { \
48 G.mypid = getpid(); \
49 G.killsig = SIGKILL; \
50} while (0)
Rob Landleyaa872762005-10-28 13:05:12 +000051
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020052static void add_inode(const struct stat *st)
Rob Landleyaa872762005-10-28 13:05:12 +000053{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020054 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;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000063 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020064
65 *curr = xzalloc(sizeof(inode_list));
66 (*curr)->dev = st->st_dev;
67 (*curr)->inode = st->st_ino;
Rob Landleyaa872762005-10-28 13:05:12 +000068}
69
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020070static smallint search_dev_inode(const struct stat *st)
Rob Landleyaa872762005-10-28 13:05:12 +000071{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020072 inode_list *ilist = G.inode_list_head;
73
Denis Vlasenkoafc41132008-03-17 08:44:58 +000074 while (ilist) {
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020075 if (ilist->dev == st->st_dev) {
Denis Vlasenkoafc41132008-03-17 08:44:58 +000076 if (option_mask32 & OPT_MOUNT)
77 return 1;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020078 if (ilist->inode == st->st_ino)
Denis Vlasenkoafc41132008-03-17 08:44:58 +000079 return 1;
80 }
81 ilist = ilist->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000082 }
83 return 0;
Rob Landleyaa872762005-10-28 13:05:12 +000084}
85
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020086enum {
87 PROC_NET = 0,
88 PROC_DIR,
89 PROC_DIR_LINKS,
90 PROC_SUBDIR_LINKS,
91};
92
93static smallint scan_proc_net_or_maps(const char *path, unsigned port)
Rob Landleyaa872762005-10-28 13:05:12 +000094{
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020095 FILE *f;
96 char line[MAX_LINE + 1], addr[68];
97 int major, minor, r;
Eric Andersena68ea1c2006-01-30 22:48:39 +000098 long long uint64_inode;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020099 unsigned tmp_port;
100 smallint retval;
101 struct stat statbuf;
102 const char *fmt;
103 void *fag, *sag;
Rob Landleyaa872762005-10-28 13:05:12 +0000104
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200105 f = fopen_for_read(path);
106 if (!f)
107 return 0;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200108
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200109 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;
Rob Landleyaa872762005-10-28 13:05:12 +0000128 }
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200129
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;
Rob Landleyaa872762005-10-28 13:05:12 +0000157}
158
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200159static smallint scan_recursive(const char *path)
Rob Landleyaa872762005-10-28 13:05:12 +0000160{
161 DIR *d;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200162 struct dirent *d_ent;
163 smallint stop_scan;
164 smallint retval;
Rob Landleyaa872762005-10-28 13:05:12 +0000165
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200166 d = opendir(path);
167 if (d == NULL)
168 return 0;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200169
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200170 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);
Rob Landleyaa872762005-10-28 13:05:12 +0000252 }
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000253 closedir(d);
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200254 G.recursion_depth--;
255 return retval;
Rob Landleyaa872762005-10-28 13:05:12 +0000256}
257
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000258int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000259int fuser_main(int argc UNUSED_PARAM, char **argv)
Rob Landley31642d72006-03-14 21:45:38 +0000260{
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000261 char **pp;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200262
263 INIT_G();
264
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000265 /* Handle -SIGNAL. Oh my... */
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000266 pp = argv;
267 while (*++pp) {
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200268 int sig;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000269 char *arg = *pp;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200270
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000271 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" */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200277 sig = get_signum(&arg[1]);
278 if (sig < 0)
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000279 continue;
280 /* "-SIGNAL" option found. Remove it and bail out */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200281 G.killsig = sig;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000282 do {
283 pp[0] = arg = pp[1];
284 pp++;
285 } while (arg);
286 break;
287 }
Rob Landleyaa872762005-10-28 13:05:12 +0000288
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200289 opt_complementary = "-1"; /* at least one param */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200290 getopt32(argv, OPTION_STRING);
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000291 argv += optind;
Mike Frysinger70cbb6e2006-04-21 22:04:05 +0000292
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000293 pp = argv;
294 while (*pp) {
Maksym Kryzhanovskyye3657dc2010-06-06 22:56:12 +0200295 /* parse net arg */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200296 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
Denys Vlasenko217a7f42011-05-29 02:03:38 +0200301 && access(path, R_OK) == 0
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200302 ) {
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);
Rob Landleyaa872762005-10-28 13:05:12 +0000310 }
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000311 pp++;
Rob Landleyaa872762005-10-28 13:05:12 +0000312 }
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000313
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200314 if (scan_recursive("/proc")) {
315 if (!(option_mask32 & OPT_SILENT))
316 bb_putchar('\n');
317 return G.kill_failed;
Rob Landleyaa872762005-10-28 13:05:12 +0000318 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200319
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200320 return EXIT_FAILURE;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000321}