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