ifupdown: do not fail if interface disappears during ifdown
[oweals/busybox.git] / procps / fuser.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * tiny fuser implementation
4  *
5  * Copyright 2004 Tony J. White
6  *
7  * Licensed under GPLv2, see file LICENSE in this source tree.
8  */
9 //config:config FUSER
10 //config:       bool "fuser (7 kb)"
11 //config:       default y
12 //config:       help
13 //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.
16
17 //applet:IF_FUSER(APPLET(fuser, BB_DIR_USR_BIN, BB_SUID_DROP))
18
19 //kbuild:lib-$(CONFIG_FUSER) += fuser.o
20
21 //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"
25 //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
31 #include "libbb.h"
32 #include "common_bufsiz.h"
33
34 #define MAX_LINE 255
35
36 #define OPTION_STRING "mks64"
37 enum {
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 };
44
45 typedef struct inode_list {
46         struct inode_list *next;
47         ino_t inode;
48         dev_t dev;
49 } inode_list;
50
51 struct globals {
52         int recursion_depth;
53         pid_t mypid;
54         inode_list *inode_list_head;
55         smallint kill_failed;
56         int killsig;
57 } FIX_ALIASING;
58 #define G (*(struct globals*)bb_common_bufsiz1)
59 #define INIT_G() do { \
60         setup_common_bufsiz(); \
61         G.mypid = getpid(); \
62         G.killsig = SIGKILL; \
63 } while (0)
64
65 static void add_inode(const struct stat *st)
66 {
67         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;
76         }
77
78         *curr = xzalloc(sizeof(inode_list));
79         (*curr)->dev = st->st_dev;
80         (*curr)->inode = st->st_ino;
81 }
82
83 static smallint search_dev_inode(const struct stat *st)
84 {
85         inode_list *ilist = G.inode_list_head;
86
87         while (ilist) {
88                 if (ilist->dev == st->st_dev) {
89                         if (option_mask32 & OPT_MOUNT)
90                                 return 1;
91                         if (ilist->inode == st->st_ino)
92                                 return 1;
93                 }
94                 ilist = ilist->next;
95         }
96         return 0;
97 }
98
99 enum {
100         PROC_NET = 0,
101         PROC_DIR,
102         PROC_DIR_LINKS,
103         PROC_SUBDIR_LINKS,
104 };
105
106 static smallint scan_proc_net_or_maps(const char *path, unsigned port)
107 {
108         FILE *f;
109         char line[MAX_LINE + 1], addr[68];
110         int major, minor, r;
111         long long uint64_inode;
112         unsigned tmp_port;
113         smallint retval;
114         struct stat statbuf;
115         const char *fmt;
116         void *fag, *sag;
117
118         f = fopen_for_read(path);
119         if (!f)
120                 return 0;
121
122         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;
141         }
142
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;
170 }
171
172 static smallint scan_recursive(const char *path)
173 {
174         DIR *d;
175         struct dirent *d_ent;
176         smallint stop_scan;
177         smallint retval;
178
179         d = opendir(path);
180         if (d == NULL)
181                 return 0;
182
183         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);
265         }
266         closedir(d);
267         G.recursion_depth--;
268         return retval;
269 }
270
271 int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
272 int fuser_main(int argc UNUSED_PARAM, char **argv)
273 {
274         char **pp;
275
276         INIT_G();
277
278         /* Handle -SIGNAL. Oh my... */
279         pp = argv;
280         while (*++pp) {
281                 int sig;
282                 char *arg = *pp;
283
284                 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" */
290                 sig = get_signum(&arg[1]);
291                 if (sig < 0)
292                         continue;
293                 /* "-SIGNAL" option found. Remove it and bail out */
294                 G.killsig = sig;
295                 do {
296                         pp[0] = arg = pp[1];
297                         pp++;
298                 } while (arg);
299                 break;
300         }
301
302         getopt32(argv, "^" OPTION_STRING "\0" "-1"/*at least one arg*/);
303         argv += optind;
304
305         pp = argv;
306         while (*pp) {
307                 /* parse net arg */
308                 unsigned port;
309                 char path[sizeof("/proc/net/TCP6")];
310
311                 strcpy(path, "/proc/net/");
312                 if (sscanf(*pp, "%u/%4s", &port, path + sizeof("/proc/net/")-1) == 2
313                  && access(path, R_OK) == 0
314                 ) {
315                         /* PORT/PROTO */
316                         scan_proc_net_or_maps(path, port);
317                 } else {
318                         /* FILE */
319                         struct stat statbuf;
320                         xstat(*pp, &statbuf);
321                         add_inode(&statbuf);
322                 }
323                 pp++;
324         }
325
326         if (scan_recursive("/proc")) {
327                 if (!(option_mask32 & OPT_SILENT))
328                         bb_putchar('\n');
329                 return G.kill_failed;
330         }
331
332         return EXIT_FAILURE;
333 }