]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/fstat/fuser.c
Remove spurious newline
[FreeBSD/FreeBSD.git] / usr.bin / fstat / fuser.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2005-2009 Stanislav Sedov <stas@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/queue.h>
33 #include <sys/stat.h>
34 #include <sys/sysctl.h>
35 #include <sys/user.h>
36
37 #include <assert.h>
38 #include <ctype.h>
39 #include <err.h>
40 #include <fcntl.h>
41 #include <libprocstat.h>
42 #include <limits.h>
43 #include <paths.h>
44 #include <pwd.h>
45 #include <signal.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <sysexits.h>
50 #include <unistd.h>
51
52 #include "functions.h"
53
54 /*
55  * File access mode flags table.
56  */
57 static const struct {
58         int     flag;
59         char    ch;
60 } fflags[] = {
61         {PS_FST_FFLAG_WRITE,    'w'},
62         {PS_FST_FFLAG_APPEND,   'a'},
63         {PS_FST_FFLAG_DIRECT,   'd'},
64         {PS_FST_FFLAG_SHLOCK,   's'},
65         {PS_FST_FFLAG_EXLOCK,   'e'}
66 };
67 #define NFFLAGS (sizeof(fflags) / sizeof(*fflags))
68
69 /*
70  * Usage flags translation table.
71  */
72 static const struct {
73         int     flag;
74         char    ch;
75 } uflags[] = {
76         {PS_FST_UFLAG_RDIR,     'r'},
77         {PS_FST_UFLAG_CDIR,     'c'},
78         {PS_FST_UFLAG_JAIL,     'j'},
79         {PS_FST_UFLAG_TRACE,    't'},
80         {PS_FST_UFLAG_TEXT,     'x'},
81         {PS_FST_UFLAG_MMAP,     'm'},
82         {PS_FST_UFLAG_CTTY,     'y'}
83 };
84 #define NUFLAGS (sizeof(uflags) / sizeof(*uflags))
85
86 struct consumer {
87         pid_t   pid;
88         uid_t   uid;
89         int     fd;
90         int     flags;
91         int     uflags;
92         STAILQ_ENTRY(consumer)  next;
93 };
94 struct reqfile {
95         uint32_t        fsid;
96         uint64_t        fileid;
97         const char      *name;
98         STAILQ_HEAD(, consumer) consumers;
99 };
100
101 /*
102  * Option flags.
103  */
104 #define UFLAG   0x01    /* -u flag: show users                          */
105 #define FFLAG   0x02    /* -f flag: specified files only                */
106 #define CFLAG   0x04    /* -c flag: treat as mpoints                    */
107 #define MFLAG   0x10    /* -m flag: mmapped files too                   */
108 #define KFLAG   0x20    /* -k flag: send signal (SIGKILL by default)    */
109
110 static int flags = 0;   /* Option flags. */
111
112 static void     printflags(struct consumer *consumer);
113 static int      str2sig(const char *str);
114 static void     usage(void) __dead2;
115 static int      addfile(const char *path, struct reqfile *reqfile);
116 static void     dofiles(struct procstat *procstat, struct kinfo_proc *kp,
117     struct reqfile *reqfiles, size_t nfiles);
118
119 static void
120 usage(void)
121 {
122
123         fprintf(stderr,
124 "usage: fuser [-cfhkmu] [-M core] [-N system] [-s signal] file ...\n");
125         exit(EX_USAGE);
126 }
127
128 static void
129 printflags(struct consumer *cons)
130 {
131         unsigned int i;
132
133         assert(cons);
134         for (i = 0; i < NUFLAGS; i++)
135                 if ((cons->uflags & uflags[i].flag) != 0)
136                         fputc(uflags[i].ch, stderr);
137         for (i = 0; i < NFFLAGS; i++)
138                 if ((cons->flags & fflags[i].flag) != 0)
139                         fputc(fflags[i].ch, stderr);
140 }
141
142 /*
143  * Add file to the list.
144  */
145 static int
146 addfile(const char *path, struct reqfile *reqfile)
147 {
148         struct stat sb;
149
150         assert(path);
151         if (stat(path, &sb) != 0) {
152                 warn("%s", path);
153                 return (1);
154         }
155         reqfile->fileid = sb.st_ino;
156         reqfile->fsid = sb.st_dev;
157         reqfile->name = path;
158         STAILQ_INIT(&reqfile->consumers);
159         return (0);
160 }
161
162 int
163 do_fuser(int argc, char *argv[])
164 {
165         struct consumer *consumer;
166         struct kinfo_proc *p, *procs;
167         struct procstat *procstat;
168         struct reqfile *reqfiles;
169         char *ep, *nlistf, *memf;
170         int ch, cnt, sig;
171         unsigned int i, nfiles;
172
173         sig = SIGKILL;  /* Default to kill. */
174         nlistf = NULL;
175         memf = NULL;
176         while ((ch = getopt(argc, argv, "M:N:cfhkms:u")) != -1)
177                 switch(ch) {
178                 case 'f':
179                         if ((flags & CFLAG) != 0)
180                                 usage();
181                         flags |= FFLAG;
182                         break;
183                 case 'c':
184                         if ((flags & FFLAG) != 0)
185                                 usage();
186                         flags |= CFLAG;
187                         break;
188                 case 'N':
189                         nlistf = optarg;
190                         break;
191                 case 'M':
192                         memf = optarg;
193                         break;
194                 case 'u':
195                         flags |= UFLAG;
196                         break;
197                 case 'm':
198                         flags |= MFLAG;
199                         break;
200                 case 'k':
201                         flags |= KFLAG;
202                         break;
203                 case 's':
204                         if (isdigit(*optarg)) {
205                                 sig = strtol(optarg, &ep, 10);
206                                 if (*ep != '\0' || sig < 0 || sig >= sys_nsig)
207                                         errx(EX_USAGE, "illegal signal number" ": %s",
208                                             optarg);
209                         } else {
210                                 sig = str2sig(optarg);
211                                 if (sig < 0)
212                                         errx(EX_USAGE, "illegal signal name: "
213                                             "%s", optarg);
214                         }
215                         break;
216                 case 'h':
217                         /* PASSTHROUGH */
218                 default:
219                         usage();
220                         /* NORETURN */
221                 }
222         argv += optind;
223         argc -= optind;
224
225         assert(argc >= 0);
226         if (argc == 0)
227                 usage();
228                 /* NORETURN */
229
230         /*
231          * Process named files.
232          */
233         reqfiles = malloc(argc * sizeof(struct reqfile));
234         if (reqfiles == NULL)
235                 err(EX_OSERR, "malloc()");
236         nfiles = 0;
237         while (argc--)
238                 if (!addfile(*(argv++), &reqfiles[nfiles]))
239                         nfiles++;
240         if (nfiles == 0)
241                 errx(EX_IOERR, "files not accessible");
242
243         if (memf != NULL)
244                 procstat = procstat_open_kvm(nlistf, memf);
245         else
246                 procstat = procstat_open_sysctl();
247         if (procstat == NULL)
248                 errx(1, "procstat_open()");
249         procs = procstat_getprocs(procstat, KERN_PROC_PROC, 0, &cnt);
250         if (procs == NULL)
251                  errx(1, "procstat_getprocs()");
252
253         /*
254          * Walk through process table and look for matching files.
255          */
256         p = procs;
257         while(cnt--)
258                 if (p->ki_stat != SZOMB)
259                         dofiles(procstat, p++, reqfiles, nfiles);
260
261         for (i = 0; i < nfiles; i++) {
262                 fprintf(stderr, "%s:", reqfiles[i].name);
263                 fflush(stderr);
264                 STAILQ_FOREACH(consumer, &reqfiles[i].consumers, next) {
265                         if (consumer->flags != 0) {
266                                 fprintf(stdout, "%6d", consumer->pid);
267                                 fflush(stdout);
268                                 printflags(consumer);
269                                 if ((flags & UFLAG) != 0)
270                                         fprintf(stderr, "(%s)",
271                                             user_from_uid(consumer->uid, 0));
272                                 if ((flags & KFLAG) != 0)
273                                         kill(consumer->pid, sig);
274                                 fflush(stderr);
275                         }
276                 }
277                 (void)fprintf(stderr, "\n");
278         }
279         procstat_freeprocs(procstat, procs);
280         procstat_close(procstat);
281         free(reqfiles);
282         return (0);
283 }
284
285 static void
286 dofiles(struct procstat *procstat, struct kinfo_proc *kp,
287     struct reqfile *reqfiles, size_t nfiles)
288 {
289         struct vnstat vn;
290         struct consumer *cons;
291         struct filestat *fst;
292         struct filestat_list *head;
293         int error, match;
294         unsigned int i;
295         char errbuf[_POSIX2_LINE_MAX];
296         
297         head = procstat_getfiles(procstat, kp, flags & MFLAG);
298         if (head == NULL)
299                 return;
300         STAILQ_FOREACH(fst, head, next) {
301                 if (fst->fs_type != PS_FST_TYPE_VNODE)
302                         continue;
303                 error = procstat_get_vnode_info(procstat, fst, &vn, errbuf);
304                 if (error != 0)
305                         continue;
306                 for (i = 0; i < nfiles; i++) {
307                         if (flags & CFLAG && reqfiles[i].fsid == vn.vn_fsid) {
308                                 break;
309                         }
310                         else if (reqfiles[i].fsid == vn.vn_fsid &&
311                             reqfiles[i].fileid == vn.vn_fileid) {
312                                 break;
313                         }
314                         else if (!(flags & FFLAG) &&
315                             (vn.vn_type == PS_FST_VTYPE_VCHR ||
316                             vn.vn_type == PS_FST_VTYPE_VBLK) &&
317                             vn.vn_fsid == reqfiles[i].fileid) {
318                                 break;
319                         }
320                 }
321                 if (i == nfiles)
322                         continue;       /* No match. */
323
324                 /*
325                  * Look for existing entries.
326                  */
327                 match = 0;
328                 STAILQ_FOREACH(cons, &reqfiles[i].consumers, next)
329                         if (cons->pid == kp->ki_pid) {
330                                 match = 1;
331                                 break;
332                         }
333                 if (match == 1) {       /* Use old entry. */
334                         cons->flags |= fst->fs_fflags;
335                         cons->uflags |= fst->fs_uflags;
336                 } else {
337                         /*
338                          * Create new entry in the consumer chain.
339                          */
340                         cons = calloc(1, sizeof(struct consumer));
341                         if (cons == NULL) {
342                                 warn("malloc()");
343                                 continue;
344                         }
345                         cons->uid = kp->ki_uid;
346                         cons->pid = kp->ki_pid;
347                         cons->uflags = fst->fs_uflags;
348                         cons->flags = fst->fs_fflags;
349                         STAILQ_INSERT_TAIL(&reqfiles[i].consumers, cons, next);
350                 }
351         }
352         procstat_freefiles(procstat, head);
353 }
354
355 /*
356  * Returns signal number for it's string representation.
357  */
358 static int
359 str2sig(const char *str)
360 {
361         int i;
362
363         if (!strncasecmp(str, "SIG", 3))
364                 str += 3;
365         for (i = 1; i < sys_nsig; i++) {
366                 if (!strcasecmp(sys_signame[i], str))
367                         return (i);
368         }
369         return (-1);
370 }