xref: /openbsd/usr.bin/fstat/fuser.c (revision 91f110e0)
1 /*	$OpenBSD: fuser.c,v 1.3 2013/10/22 16:40:28 guenther 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/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
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
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
134 printfu(struct fuser *fu)
135 {
136 	struct passwd *pwd;
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 		pwd = getpwuid(fu->uid);
152 		if (pwd != NULL)
153 			fprintf(stderr, "(%s)", pwd->pw_name);
154 		else
155 			fprintf(stderr, "(%d)", 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
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