Bump to version 1.22.1
[platform/upstream/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
10 //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"
14 //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
20 #include "libbb.h"
21
22 #define MAX_LINE 255
23
24 #define OPTION_STRING "mks64"
25 enum {
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 };
32
33 typedef struct inode_list {
34         struct inode_list *next;
35         ino_t inode;
36         dev_t dev;
37 } inode_list;
38
39 struct globals {
40         int recursion_depth;
41         pid_t mypid;
42         inode_list *inode_list_head;
43         smallint kill_failed;
44         int killsig;
45 } FIX_ALIASING;
46 #define G (*(struct globals*)&bb_common_bufsiz1)
47 #define INIT_G() do { \
48         G.mypid = getpid(); \
49         G.killsig = SIGKILL; \
50 } while (0)
51
52 static void add_inode(const struct stat *st)
53 {
54         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;
63         }
64
65         *curr = xzalloc(sizeof(inode_list));
66         (*curr)->dev = st->st_dev;
67         (*curr)->inode = st->st_ino;
68 }
69
70 static smallint search_dev_inode(const struct stat *st)
71 {
72         inode_list *ilist = G.inode_list_head;
73
74         while (ilist) {
75                 if (ilist->dev == st->st_dev) {
76                         if (option_mask32 & OPT_MOUNT)
77                                 return 1;
78                         if (ilist->inode == st->st_ino)
79                                 return 1;
80                 }
81                 ilist = ilist->next;
82         }
83         return 0;
84 }
85
86 enum {
87         PROC_NET = 0,
88         PROC_DIR,
89         PROC_DIR_LINKS,
90         PROC_SUBDIR_LINKS,
91 };
92
93 static smallint scan_proc_net_or_maps(const char *path, unsigned port)
94 {
95         FILE *f;
96         char line[MAX_LINE + 1], addr[68];
97         int major, minor, r;
98         long long uint64_inode;
99         unsigned tmp_port;
100         smallint retval;
101         struct stat statbuf;
102         const char *fmt;
103         void *fag, *sag;
104
105         f = fopen_for_read(path);
106         if (!f)
107                 return 0;
108
109         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;
128         }
129
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;
157 }
158
159 static smallint scan_recursive(const char *path)
160 {
161         DIR *d;
162         struct dirent *d_ent;
163         smallint stop_scan;
164         smallint retval;
165
166         d = opendir(path);
167         if (d == NULL)
168                 return 0;
169
170         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);
252         }
253         closedir(d);
254         G.recursion_depth--;
255         return retval;
256 }
257
258 int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
259 int fuser_main(int argc UNUSED_PARAM, char **argv)
260 {
261         char **pp;
262
263         INIT_G();
264
265         /* Handle -SIGNAL. Oh my... */
266         pp = argv;
267         while (*++pp) {
268                 int sig;
269                 char *arg = *pp;
270
271                 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" */
277                 sig = get_signum(&arg[1]);
278                 if (sig < 0)
279                         continue;
280                 /* "-SIGNAL" option found. Remove it and bail out */
281                 G.killsig = sig;
282                 do {
283                         pp[0] = arg = pp[1];
284                         pp++;
285                 } while (arg);
286                 break;
287         }
288
289         opt_complementary = "-1"; /* at least one param */
290         getopt32(argv, OPTION_STRING);
291         argv += optind;
292
293         pp = argv;
294         while (*pp) {
295                 /* parse net arg */
296                 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
301                  && access(path, R_OK) == 0
302                 ) {
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);
310                 }
311                 pp++;
312         }
313
314         if (scan_recursive("/proc")) {
315                 if (!(option_mask32 & OPT_SILENT))
316                         bb_putchar('\n');
317                 return G.kill_failed;
318         }
319
320         return EXIT_FAILURE;
321 }