blob: 6dac852ed1fafc3c74a1c1791f96148903502594 [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"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +020021#include "common_bufsiz.h"
Rob Landleyaa872762005-10-28 13:05:12 +000022
Denis Vlasenkoafc41132008-03-17 08:44:58 +000023#define MAX_LINE 255
Rob Landleyaa872762005-10-28 13:05:12 +000024
Denis Vlasenkoafc41132008-03-17 08:44:58 +000025#define OPTION_STRING "mks64"
26enum {
27 OPT_MOUNT = (1 << 0),
28 OPT_KILL = (1 << 1),
29 OPT_SILENT = (1 << 2),
30 OPT_IP6 = (1 << 3),
31 OPT_IP4 = (1 << 4),
32};
Rob Landleyaa872762005-10-28 13:05:12 +000033
34typedef struct inode_list {
Denis Vlasenkoafc41132008-03-17 08:44:58 +000035 struct inode_list *next;
Rob Landleyaa872762005-10-28 13:05:12 +000036 ino_t inode;
37 dev_t dev;
Rob Landleyaa872762005-10-28 13:05:12 +000038} inode_list;
39
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020040struct globals {
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020041 int recursion_depth;
42 pid_t mypid;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020043 inode_list *inode_list_head;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020044 smallint kill_failed;
45 int killsig;
Denys Vlasenkoe8d0a142011-01-16 11:21:15 +010046} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +020047#define G (*(struct globals*)bb_common_bufsiz1)
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020048#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +020049 setup_common_bufsiz(); \
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020050 G.mypid = getpid(); \
51 G.killsig = SIGKILL; \
52} while (0)
Rob Landleyaa872762005-10-28 13:05:12 +000053
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020054static void add_inode(const struct stat *st)
Rob Landleyaa872762005-10-28 13:05:12 +000055{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020056 inode_list **curr = &G.inode_list_head;
57
58 while (*curr) {
59 if ((*curr)->dev == st->st_dev
60 && (*curr)->inode == st->st_ino
61 ) {
62 return;
63 }
64 curr = &(*curr)->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000065 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020066
67 *curr = xzalloc(sizeof(inode_list));
68 (*curr)->dev = st->st_dev;
69 (*curr)->inode = st->st_ino;
Rob Landleyaa872762005-10-28 13:05:12 +000070}
71
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020072static smallint search_dev_inode(const struct stat *st)
Rob Landleyaa872762005-10-28 13:05:12 +000073{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020074 inode_list *ilist = G.inode_list_head;
75
Denis Vlasenkoafc41132008-03-17 08:44:58 +000076 while (ilist) {
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020077 if (ilist->dev == st->st_dev) {
Denis Vlasenkoafc41132008-03-17 08:44:58 +000078 if (option_mask32 & OPT_MOUNT)
79 return 1;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020080 if (ilist->inode == st->st_ino)
Denis Vlasenkoafc41132008-03-17 08:44:58 +000081 return 1;
82 }
83 ilist = ilist->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000084 }
85 return 0;
Rob Landleyaa872762005-10-28 13:05:12 +000086}
87
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020088enum {
89 PROC_NET = 0,
90 PROC_DIR,
91 PROC_DIR_LINKS,
92 PROC_SUBDIR_LINKS,
93};
94
95static smallint scan_proc_net_or_maps(const char *path, unsigned port)
Rob Landleyaa872762005-10-28 13:05:12 +000096{
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020097 FILE *f;
98 char line[MAX_LINE + 1], addr[68];
99 int major, minor, r;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000100 long long uint64_inode;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200101 unsigned tmp_port;
102 smallint retval;
103 struct stat statbuf;
104 const char *fmt;
105 void *fag, *sag;
Rob Landleyaa872762005-10-28 13:05:12 +0000106
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200107 f = fopen_for_read(path);
108 if (!f)
109 return 0;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200110
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200111 if (G.recursion_depth == PROC_NET) {
112 int fd;
113
114 /* find socket dev */
115 statbuf.st_dev = 0;
116 fd = socket(AF_INET, SOCK_DGRAM, 0);
117 if (fd >= 0) {
118 fstat(fd, &statbuf);
119 close(fd);
120 }
121
122 fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x "
123 "%*x:%*x %*x:%*x %*x %*d %*d %llu";
124 fag = addr;
125 sag = &tmp_port;
126 } else {
127 fmt = "%*s %*s %*s %x:%x %llu";
128 fag = &major;
129 sag = &minor;
Rob Landleyaa872762005-10-28 13:05:12 +0000130 }
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200131
132 retval = 0;
133 while (fgets(line, MAX_LINE, f)) {
134 r = sscanf(line, fmt, fag, sag, &uint64_inode);
135 if (r != 3)
136 continue;
137
138 statbuf.st_ino = uint64_inode;
139 if (G.recursion_depth == PROC_NET) {
140 r = strlen(addr);
141 if (r == 8 && (option_mask32 & OPT_IP6))
142 continue;
143 if (r > 8 && (option_mask32 & OPT_IP4))
144 continue;
145 if (tmp_port == port)
146 add_inode(&statbuf);
147 } else {
148 if (major != 0 && minor != 0 && statbuf.st_ino != 0) {
149 statbuf.st_dev = makedev(major, minor);
150 retval = search_dev_inode(&statbuf);
151 if (retval)
152 break;
153 }
154 }
155 }
156 fclose(f);
157
158 return retval;
Rob Landleyaa872762005-10-28 13:05:12 +0000159}
160
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200161static smallint scan_recursive(const char *path)
Rob Landleyaa872762005-10-28 13:05:12 +0000162{
163 DIR *d;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200164 struct dirent *d_ent;
165 smallint stop_scan;
166 smallint retval;
Rob Landleyaa872762005-10-28 13:05:12 +0000167
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200168 d = opendir(path);
169 if (d == NULL)
170 return 0;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200171
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200172 G.recursion_depth++;
173 retval = 0;
174 stop_scan = 0;
175 while (!stop_scan && (d_ent = readdir(d)) != NULL) {
176 struct stat statbuf;
177 pid_t pid;
178 char *subpath;
179
180 subpath = concat_subpath_file(path, d_ent->d_name);
181 if (subpath == NULL)
182 continue; /* . or .. */
183
184 switch (G.recursion_depth) {
185 case PROC_DIR:
186 pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10);
187 if (errno != 0
188 || pid == G.mypid
189 /* "this PID doesn't use specified FILEs or PORT/PROTO": */
190 || scan_recursive(subpath) == 0
191 ) {
192 break;
193 }
194 if (option_mask32 & OPT_KILL) {
195 if (kill(pid, G.killsig) != 0) {
196 bb_perror_msg("kill pid %s", d_ent->d_name);
197 G.kill_failed = 1;
198 }
199 }
200 if (!(option_mask32 & OPT_SILENT))
201 printf("%s ", d_ent->d_name);
202 retval = 1;
203 break;
204
205 case PROC_DIR_LINKS:
206 switch (
207 index_in_substrings(
208 "cwd" "\0" "exe" "\0"
209 "root" "\0" "fd" "\0"
210 "lib" "\0" "mmap" "\0"
211 "maps" "\0",
212 d_ent->d_name
213 )
214 ) {
215 enum {
216 CWD_LINK,
217 EXE_LINK,
218 ROOT_LINK,
219 FD_DIR_LINKS,
220 LIB_DIR_LINKS,
221 MMAP_DIR_LINKS,
222 MAPS,
223 };
224 case CWD_LINK:
225 case EXE_LINK:
226 case ROOT_LINK:
227 goto scan_link;
228 case FD_DIR_LINKS:
229 case LIB_DIR_LINKS:
230 case MMAP_DIR_LINKS:
231 stop_scan = scan_recursive(subpath);
232 if (stop_scan)
233 retval = stop_scan;
234 break;
235 case MAPS:
236 stop_scan = scan_proc_net_or_maps(subpath, 0);
237 if (stop_scan)
238 retval = stop_scan;
239 default:
240 break;
241 }
242 break;
243 case PROC_SUBDIR_LINKS:
244 scan_link:
245 if (stat(subpath, &statbuf) < 0)
246 break;
247 stop_scan = search_dev_inode(&statbuf);
248 if (stop_scan)
249 retval = stop_scan;
250 default:
251 break;
252 }
253 free(subpath);
Rob Landleyaa872762005-10-28 13:05:12 +0000254 }
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000255 closedir(d);
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200256 G.recursion_depth--;
257 return retval;
Rob Landleyaa872762005-10-28 13:05:12 +0000258}
259
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000260int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000261int fuser_main(int argc UNUSED_PARAM, char **argv)
Rob Landley31642d72006-03-14 21:45:38 +0000262{
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000263 char **pp;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200264
265 INIT_G();
266
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000267 /* Handle -SIGNAL. Oh my... */
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000268 pp = argv;
269 while (*++pp) {
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200270 int sig;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000271 char *arg = *pp;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200272
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000273 if (arg[0] != '-')
274 continue;
275 if (arg[1] == '-' && arg[2] == '\0') /* "--" */
276 break;
277 if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
278 continue; /* it's "-4" or "-6" */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200279 sig = get_signum(&arg[1]);
280 if (sig < 0)
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000281 continue;
282 /* "-SIGNAL" option found. Remove it and bail out */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200283 G.killsig = sig;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000284 do {
285 pp[0] = arg = pp[1];
286 pp++;
287 } while (arg);
288 break;
289 }
Rob Landleyaa872762005-10-28 13:05:12 +0000290
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200291 opt_complementary = "-1"; /* at least one param */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200292 getopt32(argv, OPTION_STRING);
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000293 argv += optind;
Mike Frysinger70cbb6e2006-04-21 22:04:05 +0000294
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000295 pp = argv;
296 while (*pp) {
Maksym Kryzhanovskyye3657dc2010-06-06 22:56:12 +0200297 /* parse net arg */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200298 unsigned port;
299 char path[sizeof("/proc/net/TCP6")];
300
301 strcpy(path, "/proc/net/");
302 if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2
Denys Vlasenko217a7f42011-05-29 02:03:38 +0200303 && access(path, R_OK) == 0
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200304 ) {
305 /* PORT/PROTO */
306 scan_proc_net_or_maps(path, port);
307 } else {
308 /* FILE */
309 struct stat statbuf;
310 xstat(*pp, &statbuf);
311 add_inode(&statbuf);
Rob Landleyaa872762005-10-28 13:05:12 +0000312 }
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000313 pp++;
Rob Landleyaa872762005-10-28 13:05:12 +0000314 }
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000315
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200316 if (scan_recursive("/proc")) {
317 if (!(option_mask32 & OPT_SILENT))
318 bb_putchar('\n');
319 return G.kill_failed;
Rob Landleyaa872762005-10-28 13:05:12 +0000320 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200321
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200322 return EXIT_FAILURE;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000323}