xref: /openbsd-src/usr.bin/fstat/fuser.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: fuser.c,v 1.4 2014/07/10 14:25:29 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
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/param.h>
45 #include <sys/queue.h>
46 #include <sys/stat.h>
47 #include <sys/sysctl.h>
48 #define _KERNEL /* for DTYPE_VNODE */
49 #include <sys/ucred.h>
50 #include <sys/file.h>
51 #undef _KERNEL
52 
53 #include <err.h>
54 #include <fcntl.h>
55 #include <pwd.h>
56 #include <signal.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61 
62 #include "fstat.h"
63 
64 /*
65  * Returns 1 if the file watched (fa) is equivalent
66  * to a file held by a process (kf), else 0.
67  */
68 static int
69 match(struct filearg *fa, struct kinfo_file *kf)
70 {
71 	if (fa->dev == kf->va_fsid) {
72 		if (cflg)
73 			return (1);
74 		if (fa->ino == kf->va_fileid)
75 			return (1);
76 	}
77 	return (0);
78 }
79 
80 /*
81  * Examine kinfo_file struct and record the details if they
82  * match a watched file.
83  */
84 void
85 fuser_check(struct kinfo_file *kf)
86 {
87 	struct filearg *fa;
88 	struct fuser *fu;
89 
90 	if (kf->f_type != DTYPE_VNODE)
91 		return;
92 
93 	SLIST_FOREACH(fa, &fileargs, next) {
94 		if (!match(fa, kf))
95 			continue;
96 
97 		/*
98 		 * This assumes that kinfo_files2 returns all files
99 		 * associated with a process in a contiguous block.
100 		 */
101 		if (TAILQ_EMPTY(&fa->fusers) || kf->p_pid !=
102 		    (fu = TAILQ_LAST(&fa->fusers, fuserhead))->pid) {
103 			fu = malloc(sizeof(*fu));
104 			if (fu == NULL)
105 				err(1, NULL);
106 			fu->pid = kf->p_pid;
107 			fu->uid = kf->p_uid;
108 			fu->flags = 0;
109 			TAILQ_INSERT_TAIL(&fa->fusers, fu, tq);
110 		}
111 		switch (kf->fd_fd) {
112 		case KERN_FILE_CDIR:
113 			fu->flags |= F_CWD;
114 			break;
115 		case KERN_FILE_RDIR:
116 			fu->flags |= F_ROOT;
117 			break;
118 		case KERN_FILE_TEXT:
119 			fu->flags |= F_TEXT;
120 			break;
121 		case KERN_FILE_TRACE:
122 			/* ignore */
123 			break;
124 		default:
125 			fu->flags |= F_OPEN;
126 			break;
127 		}
128 	}
129 }
130 
131 /*
132  * Print out the specfics for a given file/filesystem
133  */
134 static void
135 printfu(struct fuser *fu)
136 {
137 	struct passwd *pwd;
138 
139 	printf("%d", fu->pid);
140 	fflush(stdout);
141 
142 	if (fu->flags & F_CWD)
143 		fprintf(stderr, "c");
144 
145 	if (fu->flags & F_ROOT)
146 		fprintf(stderr, "r");
147 
148 	if (fu->flags & F_TEXT)
149 		fprintf(stderr, "t");
150 
151 	if (uflg) {
152 		pwd = getpwuid(fu->uid);
153 		if (pwd != NULL)
154 			fprintf(stderr, "(%s)", pwd->pw_name);
155 		else
156 			fprintf(stderr, "(%d)", fu->uid);
157 	}
158 
159 	putchar(' ');
160 }
161 
162 /*
163  * For each file, print matching process info and optionally send a signal.
164  */
165 void
166 fuser_run(void)
167 {
168 	struct filearg *fa;
169 	struct fuser *fu;
170 	pid_t mypid = getpid();
171 
172 	SLIST_FOREACH(fa, &fileargs, next) {
173 		fprintf(stderr, "%s: ", fa->name);
174 		TAILQ_FOREACH(fu, &fa->fusers, tq) {
175 			printfu(fu);
176 			if (sflg && fu->pid != mypid) {
177 				kill(fu->pid, signo);
178 			}
179 		}
180 		fflush(stdout);
181 		fprintf(stderr, "\n");
182 	}
183 }
184