top: display sort indicator in memory display
[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:     "\nOptions:"
15 //usage:     "\n        -m      Find processes which use same fs as FILEs"
16 //usage:     "\n        -4,-6   Search only IPv4/IPv6 space"
17 //usage:     "\n        -s      Don't display PIDs"
18 //usage:     "\n        -k      Kill found processes"
19 //usage:     "\n        -SIGNAL Signal to send (default: KILL)"
20
21 #include "libbb.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 typedef struct pid_list {
41         struct pid_list *next;
42         pid_t pid;
43 } pid_list;
44
45
46 struct globals {
47         pid_list *pid_list_head;
48         inode_list *inode_list_head;
49 } FIX_ALIASING;
50 #define G (*(struct globals*)&bb_common_bufsiz1)
51 #define INIT_G() do { } while (0)
52
53
54 static void add_pid(const pid_t pid)
55 {
56         pid_list **curr = &G.pid_list_head;
57
58         while (*curr) {
59                 if ((*curr)->pid == pid)
60                         return;
61                 curr = &(*curr)->next;
62         }
63
64         *curr = xzalloc(sizeof(pid_list));
65         (*curr)->pid = pid;
66 }
67
68 static void add_inode(const struct stat *st)
69 {
70         inode_list **curr = &G.inode_list_head;
71
72         while (*curr) {
73                 if ((*curr)->dev == st->st_dev
74                  && (*curr)->inode == st->st_ino
75                 ) {
76                         return;
77                 }
78                 curr = &(*curr)->next;
79         }
80
81         *curr = xzalloc(sizeof(inode_list));
82         (*curr)->dev = st->st_dev;
83         (*curr)->inode = st->st_ino;
84 }
85
86 static void scan_proc_net(const char *path, unsigned port)
87 {
88         char line[MAX_LINE + 1];
89         long long uint64_inode;
90         unsigned tmp_port;
91         FILE *f;
92         struct stat st;
93         int fd;
94
95         /* find socket dev */
96         st.st_dev = 0;
97         fd = socket(AF_INET, SOCK_DGRAM, 0);
98         if (fd >= 0) {
99                 fstat(fd, &st);
100                 close(fd);
101         }
102
103         f = fopen_for_read(path);
104         if (!f)
105                 return;
106
107         while (fgets(line, MAX_LINE, f)) {
108                 char addr[68];
109                 if (sscanf(line, "%*d: %64[0-9A-Fa-f]:%x %*x:%*x %*x %*x:%*x "
110                                 "%*x:%*x %*x %*d %*d %llu",
111                                 addr, &tmp_port, &uint64_inode) == 3
112                 ) {
113                         int len = strlen(addr);
114                         if (len == 8 && (option_mask32 & OPT_IP6))
115                                 continue;
116                         if (len > 8 && (option_mask32 & OPT_IP4))
117                                 continue;
118                         if (tmp_port == port) {
119                                 st.st_ino = uint64_inode;
120                                 add_inode(&st);
121                         }
122                 }
123         }
124         fclose(f);
125 }
126
127 static int search_dev_inode(const struct stat *st)
128 {
129         inode_list *ilist = G.inode_list_head;
130
131         while (ilist) {
132                 if (ilist->dev == st->st_dev) {
133                         if (option_mask32 & OPT_MOUNT)
134                                 return 1;
135                         if (ilist->inode == st->st_ino)
136                                 return 1;
137                 }
138                 ilist = ilist->next;
139         }
140         return 0;
141 }
142
143 static void scan_pid_maps(const char *fname, pid_t pid)
144 {
145         FILE *file;
146         char line[MAX_LINE + 1];
147         int major, minor;
148         long long uint64_inode;
149         struct stat st;
150
151         file = fopen_for_read(fname);
152         if (!file)
153                 return;
154
155         while (fgets(line, MAX_LINE, file)) {
156                 if (sscanf(line, "%*s %*s %*s %x:%x %llu", &major, &minor, &uint64_inode) != 3)
157                         continue;
158                 st.st_ino = uint64_inode;
159                 if (major == 0 && minor == 0 && st.st_ino == 0)
160                         continue;
161                 st.st_dev = makedev(major, minor);
162                 if (search_dev_inode(&st))
163                         add_pid(pid);
164         }
165         fclose(file);
166 }
167
168 static void scan_link(const char *lname, pid_t pid)
169 {
170         struct stat st;
171
172         if (stat(lname, &st) >= 0) {
173                 if (search_dev_inode(&st))
174                         add_pid(pid);
175         }
176 }
177
178 static void scan_dir_links(const char *dname, pid_t pid)
179 {
180         DIR *d;
181         struct dirent *de;
182         char *lname;
183
184         d = opendir(dname);
185         if (!d)
186                 return;
187
188         while ((de = readdir(d)) != NULL) {
189                 lname = concat_subpath_file(dname, de->d_name);
190                 if (lname == NULL)
191                         continue;
192                 scan_link(lname, pid);
193                 free(lname);
194         }
195         closedir(d);
196 }
197
198 /* NB: does chdir internally */
199 static void scan_proc_pids(void)
200 {
201         DIR *d;
202         struct dirent *de;
203         pid_t pid;
204
205         xchdir("/proc");
206         d = opendir("/proc");
207         if (!d)
208                 return;
209
210         while ((de = readdir(d)) != NULL) {
211                 pid = (pid_t)bb_strtou(de->d_name, NULL, 10);
212                 if (errno)
213                         continue;
214                 if (chdir(de->d_name) < 0)
215                         continue;
216                 scan_link("cwd", pid);
217                 scan_link("exe", pid);
218                 scan_link("root", pid);
219
220                 scan_dir_links("fd", pid);
221                 scan_dir_links("lib", pid);
222                 scan_dir_links("mmap", pid);
223
224                 scan_pid_maps("maps", pid);
225                 xchdir("/proc");
226         }
227         closedir(d);
228 }
229
230 int fuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
231 int fuser_main(int argc UNUSED_PARAM, char **argv)
232 {
233         pid_list *plist;
234         pid_t mypid;
235         char **pp;
236         struct stat st;
237         unsigned port;
238         int opt;
239         int exitcode;
240         int killsig;
241 /*
242 fuser [OPTIONS] FILE or PORT/PROTO
243 Find processes which use FILEs or PORTs
244         -m      Find processes which use same fs as FILEs
245         -4      Search only IPv4 space
246         -6      Search only IPv6 space
247         -s      Don't display PIDs
248         -k      Kill found processes
249         -SIGNAL Signal to send (default: KILL)
250 */
251         /* Handle -SIGNAL. Oh my... */
252         killsig = SIGKILL; /* yes, the default is not SIGTERM */
253         pp = argv;
254         while (*++pp) {
255                 char *arg = *pp;
256                 if (arg[0] != '-')
257                         continue;
258                 if (arg[1] == '-' && arg[2] == '\0') /* "--" */
259                         break;
260                 if ((arg[1] == '4' || arg[1] == '6') && arg[2] == '\0')
261                         continue; /* it's "-4" or "-6" */
262                 opt = get_signum(&arg[1]);
263                 if (opt < 0)
264                         continue;
265                 /* "-SIGNAL" option found. Remove it and bail out */
266                 killsig = opt;
267                 do {
268                         pp[0] = arg = pp[1];
269                         pp++;
270                 } while (arg);
271                 break;
272         }
273
274         opt_complementary = "-1"; /* at least one param */
275         opt = getopt32(argv, OPTION_STRING);
276         argv += optind;
277
278         pp = argv;
279         while (*pp) {
280                 /* parse net arg */
281                 char path[20], tproto[5];
282                 if (sscanf(*pp, "%u/%4s", &port, tproto) != 2)
283                         goto file;
284                 sprintf(path, "/proc/net/%s", tproto);
285                 if (access(path, R_OK) != 0) { /* PORT/PROTO */
286                         scan_proc_net(path, port);
287                 } else { /* FILE */
288  file:
289                         xstat(*pp, &st);
290                         add_inode(&st);
291                 }
292                 pp++;
293         }
294
295         scan_proc_pids(); /* changes dir to "/proc" */
296
297         mypid = getpid();
298         plist = G.pid_list_head;
299         while (1) {
300                 if (!plist)
301                         return EXIT_FAILURE;
302                 if (plist->pid != mypid)
303                         break;
304                 plist = plist->next;
305         }
306
307         exitcode = EXIT_SUCCESS;
308         do {
309                 if (plist->pid != mypid) {
310                         if (opt & OPT_KILL) {
311                                 if (kill(plist->pid, killsig) != 0) {
312                                         bb_perror_msg("kill pid %u", (unsigned)plist->pid);
313                                         exitcode = EXIT_FAILURE;
314                                 }
315                         }
316                         if (!(opt & OPT_SILENT)) {
317                                 printf("%u ", (unsigned)plist->pid);
318                         }
319                 }
320                 plist = plist->next;
321         } while (plist);
322
323         if (!(opt & (OPT_SILENT))) {
324                 bb_putchar('\n');
325         }
326
327         return exitcode;
328 }