blob: 2585a4203d44697568aad9d9943fbccb76ee3c4a [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 */
Denys Vlasenkof8f81ed2016-11-23 06:23:44 +01009//config:config FUSER
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020010//config: bool "fuser (7 kb)"
Denys Vlasenkof8f81ed2016-11-23 06:23:44 +010011//config: default y
12//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020013//config: fuser lists all PIDs (Process IDs) that currently have a given
14//config: file open. fuser can also list all PIDs that have a given network
15//config: (TCP or UDP) port open.
Denys Vlasenkof8f81ed2016-11-23 06:23:44 +010016
17//applet:IF_FUSER(APPLET(fuser, BB_DIR_USR_BIN, BB_SUID_DROP))
18
19//kbuild:lib-$(CONFIG_FUSER) += fuser.o
Rob Landleyaa872762005-10-28 13:05:12 +000020
Pere Orga5bc8c002011-04-11 03:29:49 +020021//usage:#define fuser_trivial_usage
22//usage: "[OPTIONS] FILE or PORT/PROTO"
23//usage:#define fuser_full_usage "\n\n"
24//usage: "Find processes which use FILEs or PORTs\n"
Pere Orga5bc8c002011-04-11 03:29:49 +020025//usage: "\n -m Find processes which use same fs as FILEs"
26//usage: "\n -4,-6 Search only IPv4/IPv6 space"
27//usage: "\n -s Don't display PIDs"
28//usage: "\n -k Kill found processes"
29//usage: "\n -SIGNAL Signal to send (default: KILL)"
30
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000031#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +020032#include "common_bufsiz.h"
Rob Landleyaa872762005-10-28 13:05:12 +000033
Denis Vlasenkoafc41132008-03-17 08:44:58 +000034#define MAX_LINE 255
Rob Landleyaa872762005-10-28 13:05:12 +000035
Denis Vlasenkoafc41132008-03-17 08:44:58 +000036#define OPTION_STRING "mks64"
37enum {
38 OPT_MOUNT = (1 << 0),
39 OPT_KILL = (1 << 1),
40 OPT_SILENT = (1 << 2),
41 OPT_IP6 = (1 << 3),
42 OPT_IP4 = (1 << 4),
43};
Rob Landleyaa872762005-10-28 13:05:12 +000044
45typedef struct inode_list {
Denis Vlasenkoafc41132008-03-17 08:44:58 +000046 struct inode_list *next;
Rob Landleyaa872762005-10-28 13:05:12 +000047 ino_t inode;
48 dev_t dev;
Rob Landleyaa872762005-10-28 13:05:12 +000049} inode_list;
50
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020051struct globals {
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020052 int recursion_depth;
53 pid_t mypid;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020054 inode_list *inode_list_head;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020055 smallint kill_failed;
56 int killsig;
Denys Vlasenkoe8d0a142011-01-16 11:21:15 +010057} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +020058#define G (*(struct globals*)bb_common_bufsiz1)
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020059#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +020060 setup_common_bufsiz(); \
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020061 G.mypid = getpid(); \
62 G.killsig = SIGKILL; \
63} while (0)
Rob Landleyaa872762005-10-28 13:05:12 +000064
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020065static void add_inode(const struct stat *st)
Rob Landleyaa872762005-10-28 13:05:12 +000066{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020067 inode_list **curr = &G.inode_list_head;
68
69 while (*curr) {
70 if ((*curr)->dev == st->st_dev
71 && (*curr)->inode == st->st_ino
72 ) {
73 return;
74 }
75 curr = &(*curr)->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000076 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020077
78 *curr = xzalloc(sizeof(inode_list));
79 (*curr)->dev = st->st_dev;
80 (*curr)->inode = st->st_ino;
Rob Landleyaa872762005-10-28 13:05:12 +000081}
82
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020083static smallint search_dev_inode(const struct stat *st)
Rob Landleyaa872762005-10-28 13:05:12 +000084{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020085 inode_list *ilist = G.inode_list_head;
86
Denis Vlasenkoafc41132008-03-17 08:44:58 +000087 while (ilist) {
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020088 if (ilist->dev == st->st_dev) {
Denis Vlasenkoafc41132008-03-17 08:44:58 +000089 if (option_mask32 & OPT_MOUNT)
90 return 1;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020091 if (ilist->inode == st->st_ino)
Denis Vlasenkoafc41132008-03-17 08:44:58 +000092 return 1;
93 }
94 ilist = ilist->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000095 }
96 return 0;
Rob Landleyaa872762005-10-28 13:05:12 +000097}
98
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +020099enum {
100 PROC_NET = 0,
101 PROC_DIR,
102 PROC_DIR_LINKS,
103 PROC_SUBDIR_LINKS,
104};
105
106static smallint scan_proc_net_or_maps(const char *path, unsigned port)
Rob Landleyaa872762005-10-28 13:05:12 +0000107{
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200108 FILE *f;
109 char line[MAX_LINE + 1], addr[68];
110 int major, minor, r;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000111 long long uint64_inode;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200112 unsigned tmp_port;
113 smallint retval;
114 struct stat statbuf;
115 const char *fmt;
116 void *fag, *sag;
Rob Landleyaa872762005-10-28 13:05:12 +0000117
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200118 f = fopen_for_read(path);
119 if (!f)
120 return 0;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200121
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200122 if (G.recursion_depth == PROC_NET) {
123 int fd;
124
125 /* find socket dev */
126 statbuf.st_dev = 0;
127 fd = socket(AF_INET, SOCK_DGRAM, 0);
128 if (fd >= 0) {
129 fstat(fd, &statbuf);
130 close(fd);
131 }
132
133 fmt = "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x "
134 "%*x:%*x %*x:%*x %*x %*d %*d %llu";
135 fag = addr;
136 sag = &tmp_port;
137 } else {
138 fmt = "%*s %*s %*s %x:%x %llu";
139 fag = &major;
140 sag = &minor;
Rob Landleyaa872762005-10-28 13:05:12 +0000141 }
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200142
143 retval = 0;
144 while (fgets(line, MAX_LINE, f)) {
145 r = sscanf(line, fmt, fag, sag, &uint64_inode);
146 if (r != 3)
147 continue;
148
149 statbuf.st_ino = uint64_inode;
150 if (G.recursion_depth == PROC_NET) {
151 r = strlen(addr);
152 if (r == 8 && (option_mask32 & OPT_IP6))
153 continue;
154 if (r > 8 && (option_mask32 & OPT_IP4))
155 continue;
156 if (tmp_port == port)
157 add_inode(&statbuf);
158 } else {
159 if (major != 0 && minor != 0 && statbuf.st_ino != 0) {
160 statbuf.st_dev = makedev(major, minor);
161 retval = search_dev_inode(&statbuf);
162 if (retval)
163 break;
164 }
165 }
166 }
167 fclose(f);
168
169 return retval;
Rob Landleyaa872762005-10-28 13:05:12 +0000170}
171
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200172static smallint scan_recursive(const char *path)
Rob Landleyaa872762005-10-28 13:05:12 +0000173{
174 DIR *d;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200175 struct dirent *d_ent;
176 smallint stop_scan;
177 smallint retval;
Rob Landleyaa872762005-10-28 13:05:12 +0000178
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200179 d = opendir(path);
180 if (d == NULL)
181 return 0;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200182
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200183 G.recursion_depth++;
184 retval = 0;
185 stop_scan = 0;
186 while (!stop_scan && (d_ent = readdir(d)) != NULL) {
187 struct stat statbuf;
188 pid_t pid;
189 char *subpath;
190
191 subpath = concat_subpath_file(path, d_ent->d_name);
192 if (subpath == NULL)
193 continue; /* . or .. */
194
195 switch (G.recursion_depth) {
196 case PROC_DIR:
197 pid = (pid_t)bb_strtou(d_ent->d_name, NULL, 10);
198 if (errno != 0
199 || pid == G.mypid
200 /* "this PID doesn't use specified FILEs or PORT/PROTO": */
201 || scan_recursive(subpath) == 0
202 ) {
203 break;
204 }
205 if (option_mask32 & OPT_KILL) {
206 if (kill(pid, G.killsig) != 0) {
207 bb_perror_msg("kill pid %s", d_ent->d_name);
208 G.kill_failed = 1;
209 }
210 }
211 if (!(option_mask32 & OPT_SILENT))
212 printf("%s ", d_ent->d_name);
213 retval = 1;
214 break;
215
216 case PROC_DIR_LINKS:
217 switch (
218 index_in_substrings(
219 "cwd" "\0" "exe" "\0"
220 "root" "\0" "fd" "\0"
221 "lib" "\0" "mmap" "\0"
222 "maps" "\0",
223 d_ent->d_name
224 )
225 ) {
226 enum {
227 CWD_LINK,
228 EXE_LINK,
229 ROOT_LINK,
230 FD_DIR_LINKS,
231 LIB_DIR_LINKS,
232 MMAP_DIR_LINKS,
233 MAPS,
234 };
235 case CWD_LINK:
236 case EXE_LINK:
237 case ROOT_LINK:
238 goto scan_link;
239 case FD_DIR_LINKS:
240 case LIB_DIR_LINKS:
241 case MMAP_DIR_LINKS:
242 stop_scan = scan_recursive(subpath);
243 if (stop_scan)
244 retval = stop_scan;
245 break;
246 case MAPS:
247 stop_scan = scan_proc_net_or_maps(subpath, 0);
248 if (stop_scan)
249 retval = stop_scan;
250 default:
251 break;
252 }
253 break;
254 case PROC_SUBDIR_LINKS:
255 scan_link:
256 if (stat(subpath, &statbuf) < 0)
257 break;
258 stop_scan = search_dev_inode(&statbuf);
259 if (stop_scan)
260 retval = stop_scan;
261 default:
262 break;
263 }
264 free(subpath);
Rob Landleyaa872762005-10-28 13:05:12 +0000265 }
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000266 closedir(d);
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200267 G.recursion_depth--;
268 return retval;
Rob Landleyaa872762005-10-28 13:05:12 +0000269}
270
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000271int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000272int fuser_main(int argc UNUSED_PARAM, char **argv)
Rob Landley31642d72006-03-14 21:45:38 +0000273{
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000274 char **pp;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200275
276 INIT_G();
277
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000278 /* Handle -SIGNAL. Oh my... */
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000279 pp = argv;
280 while (*++pp) {
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200281 int sig;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000282 char *arg = *pp;
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200283
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000284 if (arg[0] != '-')
285 continue;
286 if (arg[1] == '-' && arg[2] == '\0') /* "--" */
287 break;
288 if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
289 continue; /* it's "-4" or "-6" */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200290 sig = get_signum(&arg[1]);
291 if (sig < 0)
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000292 continue;
293 /* "-SIGNAL" option found. Remove it and bail out */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200294 G.killsig = sig;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000295 do {
296 pp[0] = arg = pp[1];
297 pp++;
298 } while (arg);
299 break;
300 }
Rob Landleyaa872762005-10-28 13:05:12 +0000301
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200302 opt_complementary = "-1"; /* at least one param */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200303 getopt32(argv, OPTION_STRING);
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000304 argv += optind;
Mike Frysinger70cbb6e2006-04-21 22:04:05 +0000305
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000306 pp = argv;
307 while (*pp) {
Maksym Kryzhanovskyye3657dc2010-06-06 22:56:12 +0200308 /* parse net arg */
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200309 unsigned port;
310 char path[sizeof("/proc/net/TCP6")];
311
312 strcpy(path, "/proc/net/");
313 if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2
Denys Vlasenko217a7f42011-05-29 02:03:38 +0200314 && access(path, R_OK) == 0
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200315 ) {
316 /* PORT/PROTO */
317 scan_proc_net_or_maps(path, port);
318 } else {
319 /* FILE */
320 struct stat statbuf;
321 xstat(*pp, &statbuf);
322 add_inode(&statbuf);
Rob Landleyaa872762005-10-28 13:05:12 +0000323 }
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000324 pp++;
Rob Landleyaa872762005-10-28 13:05:12 +0000325 }
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000326
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200327 if (scan_recursive("/proc")) {
328 if (!(option_mask32 & OPT_SILENT))
329 bb_putchar('\n');
330 return G.kill_failed;
Rob Landleyaa872762005-10-28 13:05:12 +0000331 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200332
Maksym Kryzhanovskyyeeed2302011-05-23 03:39:48 +0200333 return EXIT_FAILURE;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000334}