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 dev_t fsid; 96 ino_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 } 371