1 /* $OpenBSD: fuser.c,v 1.8 2019/01/25 00:19:26 millert Exp $ */
2
3 /*
4 * Copyright (c) 2009 Todd C. Miller <millert@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * Copyright (c) 2002 Peter Werner <peterw@ifost.org.au>
21 * All rights reserved.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 *
27 * 1. Redistributions of source code must retain the above copyright
28 * notice, this list of conditions and the following disclaimer.
29 * 2. The name of the author may not be used to endorse or promote products
30 * derived from this software without specific prior written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
33 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
34 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
35 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
37 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
38 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
39 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
40 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 */
43
44 #include <sys/queue.h>
45 #include <sys/stat.h>
46 #include <sys/sysctl.h>
47 #include <sys/ucred.h>
48 #define _KERNEL /* for DTYPE_VNODE */
49 #include <sys/file.h>
50 #undef _KERNEL
51
52 #include <err.h>
53 #include <fcntl.h>
54 #include <pwd.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #include "fstat.h"
62
63 /*
64 * Returns 1 if the file watched (fa) is equivalent
65 * to a file held by a process (kf), else 0.
66 */
67 static int
match(struct filearg * fa,struct kinfo_file * kf)68 match(struct filearg *fa, struct kinfo_file *kf)
69 {
70 if (fa->dev == kf->va_fsid) {
71 if (cflg)
72 return (1);
73 if (fa->ino == kf->va_fileid)
74 return (1);
75 }
76 return (0);
77 }
78
79 /*
80 * Examine kinfo_file struct and record the details if they
81 * match a watched file.
82 */
83 void
fuser_check(struct kinfo_file * kf)84 fuser_check(struct kinfo_file *kf)
85 {
86 struct filearg *fa;
87 struct fuser *fu;
88
89 if (kf->f_type != DTYPE_VNODE)
90 return;
91
92 SLIST_FOREACH(fa, &fileargs, next) {
93 if (!match(fa, kf))
94 continue;
95
96 /*
97 * This assumes that kinfo_files2 returns all files
98 * associated with a process in a contiguous block.
99 */
100 if (TAILQ_EMPTY(&fa->fusers) || kf->p_pid !=
101 (fu = TAILQ_LAST(&fa->fusers, fuserhead))->pid) {
102 fu = malloc(sizeof(*fu));
103 if (fu == NULL)
104 err(1, NULL);
105 fu->pid = kf->p_pid;
106 fu->uid = kf->p_uid;
107 fu->flags = 0;
108 TAILQ_INSERT_TAIL(&fa->fusers, fu, tq);
109 }
110 switch (kf->fd_fd) {
111 case KERN_FILE_CDIR:
112 fu->flags |= F_CWD;
113 break;
114 case KERN_FILE_RDIR:
115 fu->flags |= F_ROOT;
116 break;
117 case KERN_FILE_TEXT:
118 fu->flags |= F_TEXT;
119 break;
120 case KERN_FILE_TRACE:
121 /* ignore */
122 break;
123 default:
124 fu->flags |= F_OPEN;
125 break;
126 }
127 }
128 }
129
130 /*
131 * Print out the specfics for a given file/filesystem
132 */
133 static void
printfu(struct fuser * fu)134 printfu(struct fuser *fu)
135 {
136 const char *name;
137
138 printf("%d", fu->pid);
139 fflush(stdout);
140
141 if (fu->flags & F_CWD)
142 fprintf(stderr, "c");
143
144 if (fu->flags & F_ROOT)
145 fprintf(stderr, "r");
146
147 if (fu->flags & F_TEXT)
148 fprintf(stderr, "t");
149
150 if (uflg) {
151 name = user_from_uid(fu->uid, 1);
152 if (name != NULL)
153 fprintf(stderr, "(%s)", name);
154 else
155 fprintf(stderr, "(%u)", fu->uid);
156 }
157
158 putchar(' ');
159 }
160
161 /*
162 * For each file, print matching process info and optionally send a signal.
163 */
164 void
fuser_run(void)165 fuser_run(void)
166 {
167 struct filearg *fa;
168 struct fuser *fu;
169 pid_t mypid = getpid();
170
171 SLIST_FOREACH(fa, &fileargs, next) {
172 fprintf(stderr, "%s: ", fa->name);
173 TAILQ_FOREACH(fu, &fa->fusers, tq) {
174 printfu(fu);
175 if (sflg && fu->pid != mypid) {
176 kill(fu->pid, signo);
177 }
178 }
179 fflush(stdout);
180 fprintf(stderr, "\n");
181 }
182 }
183