blob: 7465d455499641f05a0e064d27e6091d80c5558a [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 *
Denis Vlasenkodb12d1d2008-12-07 00:52:58 +00007 * Licensed under GPLv2, see file LICENSE in this tarball for details.
Rob Landleyaa872762005-10-28 13:05:12 +00008 */
9
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000010#include "libbb.h"
Rob Landleyaa872762005-10-28 13:05:12 +000011
Denis Vlasenkoafc41132008-03-17 08:44:58 +000012#define MAX_LINE 255
Rob Landleyaa872762005-10-28 13:05:12 +000013
Denis Vlasenkoafc41132008-03-17 08:44:58 +000014#define OPTION_STRING "mks64"
15enum {
16 OPT_MOUNT = (1 << 0),
17 OPT_KILL = (1 << 1),
18 OPT_SILENT = (1 << 2),
19 OPT_IP6 = (1 << 3),
20 OPT_IP4 = (1 << 4),
21};
Rob Landleyaa872762005-10-28 13:05:12 +000022
23typedef struct inode_list {
Denis Vlasenkoafc41132008-03-17 08:44:58 +000024 struct inode_list *next;
Rob Landleyaa872762005-10-28 13:05:12 +000025 ino_t inode;
26 dev_t dev;
Rob Landleyaa872762005-10-28 13:05:12 +000027} inode_list;
28
29typedef struct pid_list {
Rob Landleyaa872762005-10-28 13:05:12 +000030 struct pid_list *next;
Denis Vlasenkoafc41132008-03-17 08:44:58 +000031 pid_t pid;
Rob Landleyaa872762005-10-28 13:05:12 +000032} pid_list;
33
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020034
35struct globals {
36 pid_list *pid_list_head;
37 inode_list *inode_list_head;
38};
39#define G (*(struct globals*)&bb_common_bufsiz1)
40#define INIT_G() do { } while (0)
41
42
Denis Vlasenkoafc41132008-03-17 08:44:58 +000043static dev_t find_socket_dev(void)
Rob Landleyaa872762005-10-28 13:05:12 +000044{
Denis Vlasenkoafc41132008-03-17 08:44:58 +000045 int fd = socket(AF_INET, SOCK_DGRAM, 0);
46 if (fd >= 0) {
47 struct stat buf;
48 int r = fstat(fd, &buf);
49 close(fd);
50 if (r == 0)
51 return buf.st_dev;
Rob Landleyaa872762005-10-28 13:05:12 +000052 }
Denis Vlasenkoafc41132008-03-17 08:44:58 +000053 return 0;
Rob Landleyaa872762005-10-28 13:05:12 +000054}
55
Denis Vlasenkoafc41132008-03-17 08:44:58 +000056static char *parse_net_arg(const char *arg, unsigned *port)
Rob Landley31642d72006-03-14 21:45:38 +000057{
Denis Vlasenko1b2058f2008-03-29 17:59:27 +000058 char path[20], tproto[5];
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000059
Denis Vlasenkoafc41132008-03-17 08:44:58 +000060 if (sscanf(arg, "%u/%4s", port, tproto) != 2)
61 return NULL;
Denis Vlasenko1b2058f2008-03-29 17:59:27 +000062 sprintf(path, "/proc/net/%s", tproto);
Denis Vlasenkoafc41132008-03-17 08:44:58 +000063 if (access(path, R_OK) != 0)
64 return NULL;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020065 return xstrdup(path);
Rob Landleyaa872762005-10-28 13:05:12 +000066}
67
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020068static void add_pid(const pid_t pid)
Rob Landleyaa872762005-10-28 13:05:12 +000069{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020070 pid_list **curr = &G.pid_list_head;
71
72 while (*curr) {
73 if ((*curr)->pid == pid)
74 return;
75 curr = &(*curr)->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000076 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020077
78 *curr = xzalloc(sizeof(pid_list));
79 (*curr)->pid = pid;
Rob Landleyaa872762005-10-28 13:05:12 +000080}
81
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020082static void add_inode(const struct stat *st)
Rob Landleyaa872762005-10-28 13:05:12 +000083{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020084 inode_list **curr = &G.inode_list_head;
85
86 while (*curr) {
87 if ((*curr)->dev == st->st_dev
88 && (*curr)->inode == st->st_ino
89 ) {
90 return;
91 }
92 curr = &(*curr)->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000093 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +020094
95 *curr = xzalloc(sizeof(inode_list));
96 (*curr)->dev = st->st_dev;
97 (*curr)->inode = st->st_ino;
Rob Landleyaa872762005-10-28 13:05:12 +000098}
99
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200100static void scan_proc_net(const char *path, unsigned port)
Rob Landleyaa872762005-10-28 13:05:12 +0000101{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200102 char line[MAX_LINE + 1];
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000103 long long uint64_inode;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000104 unsigned tmp_port;
Rob Landleyaa872762005-10-28 13:05:12 +0000105 FILE *f;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200106 struct stat st;
Rob Landleyaa872762005-10-28 13:05:12 +0000107
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200108 st.st_dev = find_socket_dev();
Rob Landleyaa872762005-10-28 13:05:12 +0000109
Denis Vlasenko5415c852008-07-21 23:05:26 +0000110 f = fopen_for_read(path);
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000111 if (!f)
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200112 return;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000113
114 while (fgets(line, MAX_LINE, f)) {
Denis Vlasenko6b797182008-07-12 09:32:38 +0000115 char addr[68];
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000116 if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
117 "%*x:%*x %*x %*d %*d %llu",
118 addr, &tmp_port, &uint64_inode) == 3
119 ) {
Denis Vlasenkoa46dd892008-07-12 09:20:44 +0000120 int len = strlen(addr);
121 if (len == 8 && (option_mask32 & OPT_IP6))
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000122 continue;
Denis Vlasenkoa46dd892008-07-12 09:20:44 +0000123 if (len > 8 && (option_mask32 & OPT_IP4))
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000124 continue;
125 if (tmp_port == port) {
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200126 st.st_ino = uint64_inode;
127 add_inode(&st);
Rob Landleyaa872762005-10-28 13:05:12 +0000128 }
129 }
Rob Landleyaa872762005-10-28 13:05:12 +0000130 }
131 fclose(f);
Rob Landleyaa872762005-10-28 13:05:12 +0000132}
133
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200134static int search_dev_inode(const struct stat *st)
Rob Landleyaa872762005-10-28 13:05:12 +0000135{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200136 inode_list *ilist = G.inode_list_head;
137
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000138 while (ilist) {
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200139 if (ilist->dev == st->st_dev) {
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000140 if (option_mask32 & OPT_MOUNT)
141 return 1;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200142 if (ilist->inode == st->st_ino)
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000143 return 1;
144 }
145 ilist = ilist->next;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000146 }
147 return 0;
Rob Landleyaa872762005-10-28 13:05:12 +0000148}
149
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200150static void scan_pid_maps(const char *fname, pid_t pid)
Rob Landleyaa872762005-10-28 13:05:12 +0000151{
152 FILE *file;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000153 char line[MAX_LINE + 1];
Rob Landleyaa872762005-10-28 13:05:12 +0000154 int major, minor;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000155 long long uint64_inode;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200156 struct stat st;
Rob Landleyaa872762005-10-28 13:05:12 +0000157
Denis Vlasenko5415c852008-07-21 23:05:26 +0000158 file = fopen_for_read(fname);
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000159 if (!file)
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200160 return;
161
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000162 while (fgets(line, MAX_LINE, file)) {
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000163 if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3)
164 continue;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200165 st.st_ino = uint64_inode;
166 if (major == 0 && minor == 0 && st.st_ino == 0)
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000167 continue;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200168 st.st_dev = makedev(major, minor);
169 if (search_dev_inode(&st))
170 add_pid(pid);
Rob Landleyaa872762005-10-28 13:05:12 +0000171 }
172 fclose(file);
Rob Landleyaa872762005-10-28 13:05:12 +0000173}
174
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200175static void scan_link(const char *lname, pid_t pid)
Rob Landleyaa872762005-10-28 13:05:12 +0000176{
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200177 struct stat st;
Rob Landleyaa872762005-10-28 13:05:12 +0000178
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200179 if (stat(lname, &st) >= 0) {
180 if (search_dev_inode(&st))
181 add_pid(pid);
182 }
Rob Landleyaa872762005-10-28 13:05:12 +0000183}
184
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200185static void scan_dir_links(const char *dname, pid_t pid)
Rob Landleyaa872762005-10-28 13:05:12 +0000186{
187 DIR *d;
188 struct dirent *de;
189 char *lname;
190
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000191 d = opendir(dname);
192 if (!d)
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200193 return;
194
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000195 while ((de = readdir(d)) != NULL) {
196 lname = concat_subpath_file(dname, de->d_name);
197 if (lname == NULL)
198 continue;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200199 scan_link(lname, pid);
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000200 free(lname);
Rob Landleyaa872762005-10-28 13:05:12 +0000201 }
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000202 closedir(d);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000203}
Rob Landleyaa872762005-10-28 13:05:12 +0000204
Denis Vlasenko5de8a132008-05-28 12:44:22 +0000205/* NB: does chdir internally */
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200206static void scan_proc_pids(void)
Rob Landleyaa872762005-10-28 13:05:12 +0000207{
208 DIR *d;
209 struct dirent *de;
210 pid_t pid;
Rob Landleyaa872762005-10-28 13:05:12 +0000211
Denis Vlasenkocd9d4c82008-05-28 14:57:58 +0000212 xchdir("/proc");
Denis Vlasenko5de8a132008-05-28 12:44:22 +0000213 d = opendir("/proc");
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000214 if (!d)
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200215 return;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000216
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000217 while ((de = readdir(d)) != NULL) {
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000218 pid = (pid_t)bb_strtou(de->d_name, NULL, 10);
219 if (errno)
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000220 continue;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000221 if (chdir(de->d_name) < 0)
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000222 continue;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200223 scan_link("cwd", pid);
224 scan_link("exe", pid);
225 scan_link("root", pid);
226
227 scan_dir_links("fd", pid);
228 scan_dir_links("lib", pid);
229 scan_dir_links("mmap", pid);
230
231 scan_pid_maps("maps", pid);
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000232 xchdir("/proc");
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000233 }
234 closedir(d);
Rob Landleyaa872762005-10-28 13:05:12 +0000235}
236
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000237int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000238int fuser_main(int argc UNUSED_PARAM, char **argv)
Rob Landley31642d72006-03-14 21:45:38 +0000239{
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000240 pid_list *plist;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200241 pid_t mypid;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000242 char **pp;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200243 struct stat st;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000244 unsigned port;
245 int opt;
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200246 int exitcode;
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000247 int killsig;
248/*
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200249fuser [OPTIONS] FILE or PORT/PROTO
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000250Find processes which use FILEs or PORTs
251 -m Find processes which use same fs as FILEs
252 -4 Search only IPv4 space
253 -6 Search only IPv6 space
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200254 -s Don't display PIDs
255 -k Kill found processes
256 -SIGNAL Signal to send (default: KILL)
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000257*/
258 /* Handle -SIGNAL. Oh my... */
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200259 killsig = SIGKILL; /* yes, the default is not SIGTERM */
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000260 pp = argv;
261 while (*++pp) {
262 char *arg = *pp;
263 if (arg[0] != '-')
264 continue;
265 if (arg[1] == '-' && arg[2] == '\0') /* "--" */
266 break;
267 if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
268 continue; /* it's "-4" or "-6" */
269 opt = get_signum(&arg[1]);
270 if (opt < 0)
271 continue;
272 /* "-SIGNAL" option found. Remove it and bail out */
273 killsig = opt;
274 do {
275 pp[0] = arg = pp[1];
276 pp++;
277 } while (arg);
278 break;
279 }
Rob Landleyaa872762005-10-28 13:05:12 +0000280
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200281 opt_complementary = "-1"; /* at least one param */
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000282 opt = getopt32(argv, OPTION_STRING);
283 argv += optind;
Mike Frysinger70cbb6e2006-04-21 22:04:05 +0000284
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000285 pp = argv;
286 while (*pp) {
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200287 char *path = parse_net_arg(*pp, &port);
288 if (path) { /* PORT/PROTO */
289 scan_proc_net(path, port);
290 free(path);
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000291 } else { /* FILE */
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200292 xstat(*pp, &st);
293 add_inode(&st);
Rob Landleyaa872762005-10-28 13:05:12 +0000294 }
Denis Vlasenkoafc41132008-03-17 08:44:58 +0000295 pp++;
Rob Landleyaa872762005-10-28 13:05:12 +0000296 }
Denis Vlasenko50f7f442007-04-11 23:20:53 +0000297
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200298 scan_proc_pids(); /* changes dir to "/proc" */
Rob Landleyaa872762005-10-28 13:05:12 +0000299
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200300 mypid = getpid();
301 plist = G.pid_list_head;
302 while (1) {
303 if (!plist)
304 return EXIT_FAILURE;
305 if (plist->pid != mypid)
306 break;
307 plist = plist->next;
Rob Landleyaa872762005-10-28 13:05:12 +0000308 }
Maksym Kryzhanovskyyfef9ee72010-05-22 20:41:08 +0200309
310 exitcode = EXIT_SUCCESS;
311 do {
312 if (plist->pid != mypid) {
313 if (opt & OPT_KILL) {
314 if (kill(plist->pid, killsig) != 0) {
315 bb_perror_msg("kill pid %u", (unsigned)plist->pid);
316 exitcode = EXIT_FAILURE;
317 }
318 }
319 if (!(opt & OPT_SILENT)) {
320 printf("%u ", (unsigned)plist->pid);
321 }
322 }
323 plist = plist->next;
324 } while (plist);
325
326 if (!(opt & (OPT_SILENT))) {
327 bb_putchar('\n');
328 }
329
330 return exitcode;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000331}