xref: /illumos-gate/usr/src/cmd/fuser/fuser.c (revision ef150c2b133e57069ae0f58d5d80cef8980ac9c3)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
287c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <errno.h>
327c478bd9Sstevel@tonic-gate #include <fcntl.h>
337c478bd9Sstevel@tonic-gate #include <kstat.h>
347c478bd9Sstevel@tonic-gate #include <libdevinfo.h>
357c478bd9Sstevel@tonic-gate #include <locale.h>
367c478bd9Sstevel@tonic-gate #include <pwd.h>
377c478bd9Sstevel@tonic-gate #include <signal.h>
387c478bd9Sstevel@tonic-gate #include <stdio.h>
397c478bd9Sstevel@tonic-gate #include <stdlib.h>
407c478bd9Sstevel@tonic-gate #include <string.h>
417c478bd9Sstevel@tonic-gate #include <unistd.h>
427c478bd9Sstevel@tonic-gate #include <sys/mnttab.h>
437c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
447c478bd9Sstevel@tonic-gate #include <sys/stat.h>
457c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
467c478bd9Sstevel@tonic-gate #include <sys/types.h>
477c478bd9Sstevel@tonic-gate #include <sys/utssys.h>
487c478bd9Sstevel@tonic-gate #include <sys/var.h>
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate /*
517c478bd9Sstevel@tonic-gate  * Command line options for fuser command. Mutually exclusive.
527c478bd9Sstevel@tonic-gate  */
537c478bd9Sstevel@tonic-gate #define	OPT_FILE_ONLY		0x0001		/* -f */
547c478bd9Sstevel@tonic-gate #define	OPT_CONTAINED		0x0002		/* -c */
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /*
577c478bd9Sstevel@tonic-gate  * Command line option modifiers for fuser command.
587c478bd9Sstevel@tonic-gate  */
597c478bd9Sstevel@tonic-gate #define	OPT_SIGNAL		0x0100		/* -k, -s */
607c478bd9Sstevel@tonic-gate #define	OPT_USERID		0x0200		/* -u */
617c478bd9Sstevel@tonic-gate #define	OPT_NBMANDLIST		0x0400		/* -n */
627c478bd9Sstevel@tonic-gate #define	OPT_DEVINFO		0x0800		/* -d */
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate #define	NELEM(a)		(sizeof (a) / sizeof ((a)[0]))
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate /*
677c478bd9Sstevel@tonic-gate  * System call prototype
687c478bd9Sstevel@tonic-gate  */
697c478bd9Sstevel@tonic-gate extern int utssys(void *buf, int arg, int type, void *outbp);
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate /*
727c478bd9Sstevel@tonic-gate  * Option flavors or types of options fuser command takes. Exclusive
737c478bd9Sstevel@tonic-gate  * options (EXCL_OPT) are mutually exclusive key options, while
747c478bd9Sstevel@tonic-gate  * modifier options (MOD_OPT) add to the key option. Examples are -f
757c478bd9Sstevel@tonic-gate  * for EXCL_OPT and -u for MOD_OPT.
767c478bd9Sstevel@tonic-gate  */
777c478bd9Sstevel@tonic-gate typedef enum {EXCL_OPT, MOD_OPT} opt_flavor_t;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate struct co_tab {
807c478bd9Sstevel@tonic-gate 	int	c_flag;
817c478bd9Sstevel@tonic-gate 	char	c_char;
827c478bd9Sstevel@tonic-gate };
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate static struct co_tab code_tab[] = {
857c478bd9Sstevel@tonic-gate 	{F_CDIR,	'c'},	/* current directory */
867c478bd9Sstevel@tonic-gate 	{F_RDIR,	'r'},	/* root directory (via chroot) */
877c478bd9Sstevel@tonic-gate 	{F_TEXT,	't'},	/* textfile */
887c478bd9Sstevel@tonic-gate 	{F_OPEN,	'o'},	/* open (creat, etc.) file */
897c478bd9Sstevel@tonic-gate 	{F_MAP,		'm'},	/* mapped file */
907c478bd9Sstevel@tonic-gate 	{F_TTY,		'y'},	/* controlling tty */
917c478bd9Sstevel@tonic-gate 	{F_TRACE,	'a'},	/* trace file */
927c478bd9Sstevel@tonic-gate 	{F_NBM,		'n'}	/* nbmand lock/share reservation on file */
937c478bd9Sstevel@tonic-gate };
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate /*
967c478bd9Sstevel@tonic-gate  * Return a pointer to the mount point matching the given special name, if
977c478bd9Sstevel@tonic-gate  * possible, otherwise, exit with 1 if mnttab corruption is detected, else
987c478bd9Sstevel@tonic-gate  * return NULL.
997c478bd9Sstevel@tonic-gate  *
1007c478bd9Sstevel@tonic-gate  * NOTE:  the underlying storage for mget and mref is defined static by
1017c478bd9Sstevel@tonic-gate  * libos.  Repeated calls to getmntany() overwrite it; to save mnttab
1027c478bd9Sstevel@tonic-gate  * structures would require copying the member strings elsewhere.
1037c478bd9Sstevel@tonic-gate  */
1047c478bd9Sstevel@tonic-gate static char *
spec_to_mount(char * specname)1057c478bd9Sstevel@tonic-gate spec_to_mount(char *specname)
1067c478bd9Sstevel@tonic-gate {
1077c478bd9Sstevel@tonic-gate 	struct mnttab 	mref, mget;
1087c478bd9Sstevel@tonic-gate 	struct stat 	st;
1097c478bd9Sstevel@tonic-gate 	FILE		*frp;
1107c478bd9Sstevel@tonic-gate 	int 		ret;
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	/* get mount-point */
1137c478bd9Sstevel@tonic-gate 	if ((frp = fopen(MNTTAB, "r")) == NULL)
1147c478bd9Sstevel@tonic-gate 		return (NULL);
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 	mntnull(&mref);
1177c478bd9Sstevel@tonic-gate 	mref.mnt_special = specname;
1187c478bd9Sstevel@tonic-gate 	ret = getmntany(frp, &mget, &mref);
1197c478bd9Sstevel@tonic-gate 	(void) fclose(frp);
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 	if (ret == 0) {
1227c478bd9Sstevel@tonic-gate 		if ((stat(specname, &st) == 0) && S_ISBLK(st.st_mode))
1237c478bd9Sstevel@tonic-gate 			return (mget.mnt_mountp);
1247c478bd9Sstevel@tonic-gate 	} else if (ret > 0) {
1257c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("mnttab is corrupted\n"));
1267c478bd9Sstevel@tonic-gate 		exit(1);
1277c478bd9Sstevel@tonic-gate 	}
1287c478bd9Sstevel@tonic-gate 	return (NULL);
1297c478bd9Sstevel@tonic-gate }
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate /*
1327c478bd9Sstevel@tonic-gate  * The main objective of this routine is to allocate an array of f_user_t's.
1337c478bd9Sstevel@tonic-gate  * In order for it to know how large an array to allocate, it must know
1347c478bd9Sstevel@tonic-gate  * the value of v.v_proc in the kernel.  To get this, we do a kstat
1357c478bd9Sstevel@tonic-gate  * lookup to get the var structure from the kernel.
1367c478bd9Sstevel@tonic-gate  */
1377c478bd9Sstevel@tonic-gate static fu_data_t *
get_f_user_buf()1387c478bd9Sstevel@tonic-gate get_f_user_buf()
1397c478bd9Sstevel@tonic-gate {
1407c478bd9Sstevel@tonic-gate 	fu_data_t	fu_header, *fu_data;
1417c478bd9Sstevel@tonic-gate 	kstat_ctl_t	*kc;
1427c478bd9Sstevel@tonic-gate 	struct var	v;
1437c478bd9Sstevel@tonic-gate 	kstat_t		*ksp;
1447c478bd9Sstevel@tonic-gate 	int		count;
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	if ((kc = kstat_open()) == NULL ||
1477c478bd9Sstevel@tonic-gate 	    (ksp = kstat_lookup(kc, "unix", 0, "var")) == NULL ||
1487c478bd9Sstevel@tonic-gate 	    kstat_read(kc, ksp, &v) == -1) {
1497c478bd9Sstevel@tonic-gate 		perror(gettext("kstat_read() of struct var failed"));
1507c478bd9Sstevel@tonic-gate 		exit(1);
1517c478bd9Sstevel@tonic-gate 	}
1527c478bd9Sstevel@tonic-gate 	(void) kstat_close(kc);
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 	/*
1557c478bd9Sstevel@tonic-gate 	 * get a count of the current number of kernel file consumers
1567c478bd9Sstevel@tonic-gate 	 *
1577c478bd9Sstevel@tonic-gate 	 * the number of kernel file consumers can change between
1587c478bd9Sstevel@tonic-gate 	 * the time when we get this count of all kernel file
1597c478bd9Sstevel@tonic-gate 	 * consumers and when we get the actual file usage
1607c478bd9Sstevel@tonic-gate 	 * information back from the kernel.
1617c478bd9Sstevel@tonic-gate 	 *
1627c478bd9Sstevel@tonic-gate 	 * we use the current count as a maximum because we assume
1637c478bd9Sstevel@tonic-gate 	 * that not all kernel file consumers are accessing the
1647c478bd9Sstevel@tonic-gate 	 * file we're interested in.  this assumption should make
1657c478bd9Sstevel@tonic-gate 	 * the current number of kernel file consumers a valid
1667c478bd9Sstevel@tonic-gate 	 * upper limit of possible file consumers.
1677c478bd9Sstevel@tonic-gate 	 *
1687c478bd9Sstevel@tonic-gate 	 * this call should never fail
1697c478bd9Sstevel@tonic-gate 	 */
1707c478bd9Sstevel@tonic-gate 	fu_header.fud_user_max = 0;
1717c478bd9Sstevel@tonic-gate 	fu_header.fud_user_count = 0;
1727c478bd9Sstevel@tonic-gate 	(void) utssys(NULL, F_KINFO_COUNT, UTS_FUSERS, &fu_header);
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	count = v.v_proc + fu_header.fud_user_count;
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	fu_data = (fu_data_t *)malloc(fu_data_size(count));
1777c478bd9Sstevel@tonic-gate 	if (fu_data == NULL) {
1787c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
1797c478bd9Sstevel@tonic-gate 		    gettext("fuser: could not allocate buffer\n"));
1807c478bd9Sstevel@tonic-gate 		exit(1);
1817c478bd9Sstevel@tonic-gate 	}
1827c478bd9Sstevel@tonic-gate 	fu_data->fud_user_max = count;
1837c478bd9Sstevel@tonic-gate 	fu_data->fud_user_count = 0;
1847c478bd9Sstevel@tonic-gate 	return (fu_data);
1857c478bd9Sstevel@tonic-gate }
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate /*
1887c478bd9Sstevel@tonic-gate  * display the fuser usage message and exit
1897c478bd9Sstevel@tonic-gate  */
1907c478bd9Sstevel@tonic-gate static void
usage()1917c478bd9Sstevel@tonic-gate usage()
1927c478bd9Sstevel@tonic-gate {
1937c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr,
1947c478bd9Sstevel@tonic-gate 	    gettext("Usage:  fuser [-[k|s sig]un[c|f|d]] files"
1957c478bd9Sstevel@tonic-gate 	    " [-[[k|s sig]un[c|f|d]] files]..\n"));
1967c478bd9Sstevel@tonic-gate 	exit(1);
1977c478bd9Sstevel@tonic-gate }
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate static int
report_process(f_user_t * f_user,int options,int sig)2007c478bd9Sstevel@tonic-gate report_process(f_user_t *f_user, int options, int sig)
2017c478bd9Sstevel@tonic-gate {
2027c478bd9Sstevel@tonic-gate 	struct passwd	*pwdp;
2037c478bd9Sstevel@tonic-gate 	int		i;
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	(void) fprintf(stdout, " %7d", (int)f_user->fu_pid);
2067c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	/* print out any character codes for the process */
2097c478bd9Sstevel@tonic-gate 	for (i = 0; i < NELEM(code_tab); i++) {
2107c478bd9Sstevel@tonic-gate 		if (f_user->fu_flags & code_tab[i].c_flag)
2117c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "%c", code_tab[i].c_char);
2127c478bd9Sstevel@tonic-gate 	}
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	/* optionally print the login name for the process */
2157c478bd9Sstevel@tonic-gate 	if ((options & OPT_USERID) &&
2167c478bd9Sstevel@tonic-gate 	    ((pwdp = getpwuid(f_user->fu_uid)) != NULL))
2177c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "(%s)", pwdp->pw_name);
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	/* optionally send a signal to the process */
2207c478bd9Sstevel@tonic-gate 	if (options & OPT_SIGNAL)
2217c478bd9Sstevel@tonic-gate 		(void) kill(f_user->fu_pid, sig);
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	return (0);
2247c478bd9Sstevel@tonic-gate }
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate static char *
i_get_dev_path(f_user_t * f_user,char * drv_name,int major,di_node_t * di_root)2277c478bd9Sstevel@tonic-gate i_get_dev_path(f_user_t *f_user, char *drv_name, int major, di_node_t *di_root)
2287c478bd9Sstevel@tonic-gate {
2297c478bd9Sstevel@tonic-gate 	di_minor_t	di_minor;
2307c478bd9Sstevel@tonic-gate 	di_node_t	di_node;
2317c478bd9Sstevel@tonic-gate 	dev_t		dev;
2327c478bd9Sstevel@tonic-gate 	char		*path;
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 	/*
2357c478bd9Sstevel@tonic-gate 	 * if we don't have a snapshot of the device tree yet, then
2367c478bd9Sstevel@tonic-gate 	 * take one so we can try to look up the device node and
2377c478bd9Sstevel@tonic-gate 	 * some kind of path to it.
2387c478bd9Sstevel@tonic-gate 	 */
2397c478bd9Sstevel@tonic-gate 	if (*di_root == DI_NODE_NIL) {
2407c478bd9Sstevel@tonic-gate 		*di_root = di_init("/", DINFOSUBTREE | DINFOMINOR);
2417c478bd9Sstevel@tonic-gate 		if (*di_root == DI_NODE_NIL) {
2427c478bd9Sstevel@tonic-gate 			perror(gettext("devinfo snapshot failed"));
2437c478bd9Sstevel@tonic-gate 			return ((char *)-1);
2447c478bd9Sstevel@tonic-gate 		}
2457c478bd9Sstevel@tonic-gate 	}
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	/* find device nodes that are bound to this driver */
2487c478bd9Sstevel@tonic-gate 	di_node = di_drv_first_node(drv_name, *di_root);
2497c478bd9Sstevel@tonic-gate 	if (di_node == DI_NODE_NIL)
2507c478bd9Sstevel@tonic-gate 		return (NULL);
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	/* try to get a dev_t for the device node we want to look up */
2537c478bd9Sstevel@tonic-gate 	if (f_user->fu_minor == -1)
2547c478bd9Sstevel@tonic-gate 		dev = DDI_DEV_T_NONE;
2557c478bd9Sstevel@tonic-gate 	else
2567c478bd9Sstevel@tonic-gate 		dev = makedev(major, f_user->fu_minor);
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	/* walk all the device nodes bound to this driver */
2597c478bd9Sstevel@tonic-gate 	do {
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 		/* see if we can get a path to the minor node */
2627c478bd9Sstevel@tonic-gate 		if (dev != DDI_DEV_T_NONE) {
2637c478bd9Sstevel@tonic-gate 			di_minor = DI_MINOR_NIL;
2647c478bd9Sstevel@tonic-gate 			while (di_minor = di_minor_next(di_node, di_minor)) {
2657c478bd9Sstevel@tonic-gate 				if (dev != di_minor_devt(di_minor))
2667c478bd9Sstevel@tonic-gate 					continue;
2677c478bd9Sstevel@tonic-gate 				path = di_devfs_minor_path(di_minor);
2687c478bd9Sstevel@tonic-gate 				if (path == NULL) {
2697c478bd9Sstevel@tonic-gate 					perror(gettext(
2707c478bd9Sstevel@tonic-gate 						"unable to get device path"));
2717c478bd9Sstevel@tonic-gate 					return ((char *)-1);
2727c478bd9Sstevel@tonic-gate 				}
2737c478bd9Sstevel@tonic-gate 				return (path);
2747c478bd9Sstevel@tonic-gate 			}
2757c478bd9Sstevel@tonic-gate 		}
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate 		/* see if we can get a path to the device instance */
2787c478bd9Sstevel@tonic-gate 		if ((f_user->fu_instance != -1) &&
2797c478bd9Sstevel@tonic-gate 		    (f_user->fu_instance == di_instance(di_node))) {
2807c478bd9Sstevel@tonic-gate 			path = di_devfs_path(di_node);
2817c478bd9Sstevel@tonic-gate 			if (path == NULL) {
2827c478bd9Sstevel@tonic-gate 				perror(gettext("unable to get device path"));
2837c478bd9Sstevel@tonic-gate 				return ((char *)-1);
2847c478bd9Sstevel@tonic-gate 			}
2857c478bd9Sstevel@tonic-gate 			return (path);
2867c478bd9Sstevel@tonic-gate 		}
2877c478bd9Sstevel@tonic-gate 	} while (di_node = di_drv_next_node(di_node));
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	return (NULL);
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate static int
report_kernel(f_user_t * f_user,di_node_t * di_root)2937c478bd9Sstevel@tonic-gate report_kernel(f_user_t *f_user, di_node_t *di_root)
2947c478bd9Sstevel@tonic-gate {
2957c478bd9Sstevel@tonic-gate 	struct modinfo	modinfo;
2967c478bd9Sstevel@tonic-gate 	char		*path;
2977c478bd9Sstevel@tonic-gate 	int		major = -1;
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	/* get the module name */
3007c478bd9Sstevel@tonic-gate 	modinfo.mi_info = MI_INFO_ONE | MI_INFO_CNT | MI_INFO_NOBASE;
3017c478bd9Sstevel@tonic-gate 	modinfo.mi_id = modinfo.mi_nextid = f_user->fu_modid;
3027c478bd9Sstevel@tonic-gate 	if (modctl(MODINFO, f_user->fu_modid, &modinfo) < 0) {
3037c478bd9Sstevel@tonic-gate 		perror(gettext("unable to get kernel module information"));
3047c478bd9Sstevel@tonic-gate 		return (-1);
3057c478bd9Sstevel@tonic-gate 	}
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	/*
3087c478bd9Sstevel@tonic-gate 	 * if we don't have any device info then just
3097c478bd9Sstevel@tonic-gate 	 * print the module name
3107c478bd9Sstevel@tonic-gate 	 */
3117c478bd9Sstevel@tonic-gate 	if ((f_user->fu_instance == -1) && (f_user->fu_minor == -1)) {
3127c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, " [%s]", modinfo.mi_name);
3137c478bd9Sstevel@tonic-gate 		return (0);
3147c478bd9Sstevel@tonic-gate 	}
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	/* get the driver major number */
3177c478bd9Sstevel@tonic-gate 	if (modctl(MODGETMAJBIND,
3187c478bd9Sstevel@tonic-gate 	    modinfo.mi_name, strlen(modinfo.mi_name) + 1, &major) < 0) {
3197c478bd9Sstevel@tonic-gate 		perror(gettext("unable to get driver major number"));
3207c478bd9Sstevel@tonic-gate 		return (-1);
3217c478bd9Sstevel@tonic-gate 	}
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	path = i_get_dev_path(f_user, modinfo.mi_name, major, di_root);
3247c478bd9Sstevel@tonic-gate 	if (path == (char *)-1)
3257c478bd9Sstevel@tonic-gate 		return (-1);
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	/* check if we couldn't get any device pathing info */
3287c478bd9Sstevel@tonic-gate 	if (path == NULL) {
3297c478bd9Sstevel@tonic-gate 		if (f_user->fu_minor == -1) {
3307c478bd9Sstevel@tonic-gate 			/*
3317c478bd9Sstevel@tonic-gate 			 * we don't really have any more info on the device
3327c478bd9Sstevel@tonic-gate 			 * so display the driver name in the same format
3337c478bd9Sstevel@tonic-gate 			 * that we would for a plain module
3347c478bd9Sstevel@tonic-gate 			 */
3357c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, " [%s]", modinfo.mi_name);
3367c478bd9Sstevel@tonic-gate 			return (0);
3377c478bd9Sstevel@tonic-gate 		} else {
3387c478bd9Sstevel@tonic-gate 			/*
3397c478bd9Sstevel@tonic-gate 			 * if we only have dev_t information, then display
3407c478bd9Sstevel@tonic-gate 			 * the driver name and the dev_t info
3417c478bd9Sstevel@tonic-gate 			 */
3427c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, " [%s,dev=(%d,%d)]",
3437c478bd9Sstevel@tonic-gate 			    modinfo.mi_name, major, f_user->fu_minor);
3447c478bd9Sstevel@tonic-gate 			return (0);
3457c478bd9Sstevel@tonic-gate 		}
3467c478bd9Sstevel@tonic-gate 	}
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate 	/* display device pathing information */
3497c478bd9Sstevel@tonic-gate 	if (f_user->fu_minor == -1) {
3507c478bd9Sstevel@tonic-gate 		/*
3517c478bd9Sstevel@tonic-gate 		 * display the driver name and a path to the device
3527c478bd9Sstevel@tonic-gate 		 * instance.
3537c478bd9Sstevel@tonic-gate 		 */
3547c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, " [%s,dev_path=%s]",
3557c478bd9Sstevel@tonic-gate 		    modinfo.mi_name, path);
3567c478bd9Sstevel@tonic-gate 	} else {
3577c478bd9Sstevel@tonic-gate 		/*
3587c478bd9Sstevel@tonic-gate 		 * here we have lot's of info.  the driver name, the minor
3597c478bd9Sstevel@tonic-gate 		 * node dev_t, and a path to the device.  display it all.
3607c478bd9Sstevel@tonic-gate 		 */
3617c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, " [%s,dev=(%d,%d),dev_path=%s]",
3627c478bd9Sstevel@tonic-gate 		    modinfo.mi_name, major, f_user->fu_minor, path);
3637c478bd9Sstevel@tonic-gate 	}
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	di_devfs_path_free(path);
3667c478bd9Sstevel@tonic-gate 	return (0);
3677c478bd9Sstevel@tonic-gate }
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate /*
3707c478bd9Sstevel@tonic-gate  * Show pids and usage indicators for the nusers processes in the users list.
3717c478bd9Sstevel@tonic-gate  * When OPT_USERID is set, give associated login names.  When OPT_SIGNAL is
3727c478bd9Sstevel@tonic-gate  * set, issue the specified signal to those processes.
3737c478bd9Sstevel@tonic-gate  */
3747c478bd9Sstevel@tonic-gate static void
report(fu_data_t * fu_data,int options,int sig)3757c478bd9Sstevel@tonic-gate report(fu_data_t *fu_data, int options, int sig)
3767c478bd9Sstevel@tonic-gate {
3777c478bd9Sstevel@tonic-gate 	di_node_t	di_root = DI_NODE_NIL;
3787c478bd9Sstevel@tonic-gate 	f_user_t 	*f_user;
3797c478bd9Sstevel@tonic-gate 	int		err, i;
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	for (err = i = 0; (err == 0) && (i <  fu_data->fud_user_count); i++) {
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 		f_user = &(fu_data->fud_user[i]);
3847c478bd9Sstevel@tonic-gate 		if (f_user->fu_flags & F_KERNEL) {
3857c478bd9Sstevel@tonic-gate 			/* a kernel module is using the file */
3867c478bd9Sstevel@tonic-gate 			err = report_kernel(f_user, &di_root);
3877c478bd9Sstevel@tonic-gate 		} else {
3887c478bd9Sstevel@tonic-gate 			/* a userland process using the file */
3897c478bd9Sstevel@tonic-gate 			err = report_process(f_user, options, sig);
3907c478bd9Sstevel@tonic-gate 		}
3917c478bd9Sstevel@tonic-gate 	}
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	if (di_root != DI_NODE_NIL)
3947c478bd9Sstevel@tonic-gate 		di_fini(di_root);
3957c478bd9Sstevel@tonic-gate }
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate /*
3987c478bd9Sstevel@tonic-gate  * Sanity check the option "nextopt" and OR it into *options.
3997c478bd9Sstevel@tonic-gate  */
4007c478bd9Sstevel@tonic-gate static void
set_option(int * options,int nextopt,opt_flavor_t type)4017c478bd9Sstevel@tonic-gate set_option(int *options, int nextopt, opt_flavor_t type)
4027c478bd9Sstevel@tonic-gate {
4037c478bd9Sstevel@tonic-gate 	static const char	*excl_opts[] = {"-c", "-f", "-d"};
4047c478bd9Sstevel@tonic-gate 	int			i;
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	/*
4077c478bd9Sstevel@tonic-gate 	 * Disallow repeating options
4087c478bd9Sstevel@tonic-gate 	 */
4097c478bd9Sstevel@tonic-gate 	if (*options & nextopt)
4107c478bd9Sstevel@tonic-gate 		usage();
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	/*
4137c478bd9Sstevel@tonic-gate 	 * If EXCL_OPT, allow only one option to be set
4147c478bd9Sstevel@tonic-gate 	 */
4157c478bd9Sstevel@tonic-gate 	if ((type == EXCL_OPT) && (*options)) {
4167c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr,
4177c478bd9Sstevel@tonic-gate 		    gettext("Use only one of the following options :"));
4187c478bd9Sstevel@tonic-gate 		for (i = 0; i < NELEM(excl_opts); i++) {
4197c478bd9Sstevel@tonic-gate 			if (i == 0) {
4207c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(" %s"),
4217c478bd9Sstevel@tonic-gate 				    excl_opts[i]);
4227c478bd9Sstevel@tonic-gate 			} else {
4237c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(", %s"),
4247c478bd9Sstevel@tonic-gate 				    excl_opts[i]);
4257c478bd9Sstevel@tonic-gate 			}
4267c478bd9Sstevel@tonic-gate 		}
4277c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n"),
4287c478bd9Sstevel@tonic-gate 		usage();
4297c478bd9Sstevel@tonic-gate 	}
4307c478bd9Sstevel@tonic-gate 	*options |= nextopt;
4317c478bd9Sstevel@tonic-gate }
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate /*
4347c478bd9Sstevel@tonic-gate  * Determine which processes are using a named file or file system.
4357c478bd9Sstevel@tonic-gate  * On stdout, show the pid of each process using each command line file
4367c478bd9Sstevel@tonic-gate  * with indication(s) of its use(s).  Optionally display the login
4377c478bd9Sstevel@tonic-gate  * name with each process.  Also optionally, issue the specified signal to
4387c478bd9Sstevel@tonic-gate  * each process.
4397c478bd9Sstevel@tonic-gate  *
4407c478bd9Sstevel@tonic-gate  * X/Open Commands and Utilites, Issue 5 requires fuser to process
4417c478bd9Sstevel@tonic-gate  * the complete list of names it is given, so if an error is encountered
4427c478bd9Sstevel@tonic-gate  * it will continue through the list, and then exit with a non-zero
4437c478bd9Sstevel@tonic-gate  * value. This is a change from earlier behavior where the command
4447c478bd9Sstevel@tonic-gate  * would exit immediately upon an error.
4457c478bd9Sstevel@tonic-gate  *
4467c478bd9Sstevel@tonic-gate  * The preferred use of the command is with a single file or file system.
4477c478bd9Sstevel@tonic-gate  */
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate int
main(int argc,char ** argv)4507c478bd9Sstevel@tonic-gate main(int argc, char **argv)
4517c478bd9Sstevel@tonic-gate {
4527c478bd9Sstevel@tonic-gate 	fu_data_t	*fu_data;
453*ef150c2bSRichard Lowe 	char		*mntname;
454*ef150c2bSRichard Lowe 	int		c, newfile = 0, errors = 0, opts = 0, flags = 0;
4557c478bd9Sstevel@tonic-gate 	int		uts_flags, sig, okay, err;
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
4587c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 	if (argc < 2)
4617c478bd9Sstevel@tonic-gate 		usage();
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	do {
4647c478bd9Sstevel@tonic-gate 		while ((c = getopt(argc, argv, "cdfkns:u")) != EOF) {
4657c478bd9Sstevel@tonic-gate 			if (newfile) {
4667c478bd9Sstevel@tonic-gate 				/*
4677c478bd9Sstevel@tonic-gate 				 * Starting a new group of files.
4687c478bd9Sstevel@tonic-gate 				 * Clear out options currently in
4697c478bd9Sstevel@tonic-gate 				 * force.
4707c478bd9Sstevel@tonic-gate 				 */
4717c478bd9Sstevel@tonic-gate 				flags = opts = newfile = 0;
4727c478bd9Sstevel@tonic-gate 			}
4737c478bd9Sstevel@tonic-gate 			switch (c) {
4747c478bd9Sstevel@tonic-gate 			case 'd':
4757c478bd9Sstevel@tonic-gate 				set_option(&opts, OPT_DEVINFO, EXCL_OPT);
4767c478bd9Sstevel@tonic-gate 				break;
4777c478bd9Sstevel@tonic-gate 			case 'k':
4787c478bd9Sstevel@tonic-gate 				set_option(&flags, OPT_SIGNAL, MOD_OPT);
4797c478bd9Sstevel@tonic-gate 				sig = SIGKILL;
4807c478bd9Sstevel@tonic-gate 				break;
4817c478bd9Sstevel@tonic-gate 			case 's':
4827c478bd9Sstevel@tonic-gate 				set_option(&flags, OPT_SIGNAL, MOD_OPT);
4837c478bd9Sstevel@tonic-gate 				if (str2sig(optarg, &sig) != 0) {
4847c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr,
4857c478bd9Sstevel@tonic-gate 					    gettext("Invalid signal %s\n"),
4867c478bd9Sstevel@tonic-gate 					    optarg);
4877c478bd9Sstevel@tonic-gate 					usage();
4887c478bd9Sstevel@tonic-gate 				}
4897c478bd9Sstevel@tonic-gate 				break;
4907c478bd9Sstevel@tonic-gate 			case 'u':
4917c478bd9Sstevel@tonic-gate 				set_option(&flags, OPT_USERID, MOD_OPT);
4927c478bd9Sstevel@tonic-gate 				break;
4937c478bd9Sstevel@tonic-gate 			case 'n':
4947c478bd9Sstevel@tonic-gate 				/*
4957c478bd9Sstevel@tonic-gate 				 * Report only users with NBMAND locks
4967c478bd9Sstevel@tonic-gate 				 */
4977c478bd9Sstevel@tonic-gate 				set_option(&flags, OPT_NBMANDLIST, MOD_OPT);
4987c478bd9Sstevel@tonic-gate 				break;
4997c478bd9Sstevel@tonic-gate 			case 'c':
5007c478bd9Sstevel@tonic-gate 				set_option(&opts, OPT_CONTAINED, EXCL_OPT);
5017c478bd9Sstevel@tonic-gate 				break;
5027c478bd9Sstevel@tonic-gate 			case 'f':
5037c478bd9Sstevel@tonic-gate 				set_option(&opts, OPT_FILE_ONLY, EXCL_OPT);
5047c478bd9Sstevel@tonic-gate 				break;
5057c478bd9Sstevel@tonic-gate 			default:
5067c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
5077c478bd9Sstevel@tonic-gate 				    gettext("Illegal option %c.\n"), c);
5087c478bd9Sstevel@tonic-gate 				usage();
5097c478bd9Sstevel@tonic-gate 			}
5107c478bd9Sstevel@tonic-gate 		}
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 		if ((optind < argc) && (newfile)) {
5137c478bd9Sstevel@tonic-gate 			/*
5147c478bd9Sstevel@tonic-gate 			 * Cancel the options currently in
5157c478bd9Sstevel@tonic-gate 			 * force if a lone dash is specified.
5167c478bd9Sstevel@tonic-gate 			 */
5177c478bd9Sstevel@tonic-gate 			if (strcmp(argv[optind], "-") == 0) {
5187c478bd9Sstevel@tonic-gate 				flags = opts = newfile = 0;
5197c478bd9Sstevel@tonic-gate 				optind++;
5207c478bd9Sstevel@tonic-gate 			}
5217c478bd9Sstevel@tonic-gate 		}
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 		/*
5247c478bd9Sstevel@tonic-gate 		 * newfile is set when a new group of files is found.  If all
5257c478bd9Sstevel@tonic-gate 		 * arguments are processed and newfile isn't set here, then
5267c478bd9Sstevel@tonic-gate 		 * the user did not use the correct syntax
5277c478bd9Sstevel@tonic-gate 		 */
5287c478bd9Sstevel@tonic-gate 		if (optind > argc - 1) {
5297c478bd9Sstevel@tonic-gate 			if (!newfile) {
5307c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
5317c478bd9Sstevel@tonic-gate 				    gettext("fuser: missing file name\n"));
5327c478bd9Sstevel@tonic-gate 				usage();
5337c478bd9Sstevel@tonic-gate 			}
5347c478bd9Sstevel@tonic-gate 		} else {
5357c478bd9Sstevel@tonic-gate 			if (argv[optind][0] == '-') {
5367c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
5377c478bd9Sstevel@tonic-gate 				    gettext("fuser: incorrect use of -\n"));
5387c478bd9Sstevel@tonic-gate 				usage();
5397c478bd9Sstevel@tonic-gate 			} else {
5407c478bd9Sstevel@tonic-gate 				newfile = 1;
5417c478bd9Sstevel@tonic-gate 			}
5427c478bd9Sstevel@tonic-gate 		}
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 		/* allocate a buffer to hold usage data */
5457c478bd9Sstevel@tonic-gate 		fu_data = get_f_user_buf();
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 		/*
5487c478bd9Sstevel@tonic-gate 		 * First print file name on stderr
5497c478bd9Sstevel@tonic-gate 		 * (so stdout (pids) can be piped to kill)
5507c478bd9Sstevel@tonic-gate 		 */
5517c478bd9Sstevel@tonic-gate 		(void) fflush(stdout);
5527c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", argv[optind]);
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 		/*
5557c478bd9Sstevel@tonic-gate 		 * if not OPT_FILE_ONLY, OPT_DEVINFO, or OPT_CONTAINED,
5567c478bd9Sstevel@tonic-gate 		 * attempt to translate the target file name to a mount
5577c478bd9Sstevel@tonic-gate 		 * point via /etc/mnttab.
5587c478bd9Sstevel@tonic-gate 		 */
5597c478bd9Sstevel@tonic-gate 		okay = 0;
5607c478bd9Sstevel@tonic-gate 		if (!opts &&
5617c478bd9Sstevel@tonic-gate 		    (mntname = spec_to_mount(argv[optind])) != NULL) {
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate 			uts_flags = F_CONTAINED |
5647c478bd9Sstevel@tonic-gate 			    ((flags & OPT_NBMANDLIST) ? F_NBMANDLIST : 0);
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 			err = utssys(mntname, uts_flags, UTS_FUSERS, fu_data);
5677c478bd9Sstevel@tonic-gate 			if (err == 0) {
5687c478bd9Sstevel@tonic-gate 				report(fu_data, flags, sig);
5697c478bd9Sstevel@tonic-gate 				okay = 1;
5707c478bd9Sstevel@tonic-gate 			}
5717c478bd9Sstevel@tonic-gate 		}
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 		uts_flags = \
5747c478bd9Sstevel@tonic-gate 		    ((opts & OPT_CONTAINED) ? F_CONTAINED : 0) |
5757c478bd9Sstevel@tonic-gate 		    ((opts & OPT_DEVINFO) ? F_DEVINFO : 0) |
5767c478bd9Sstevel@tonic-gate 		    ((flags & OPT_NBMANDLIST) ? F_NBMANDLIST : 0);
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 		err = utssys(argv[optind], uts_flags, UTS_FUSERS, fu_data);
5797c478bd9Sstevel@tonic-gate 		if (err == 0) {
5807c478bd9Sstevel@tonic-gate 			report(fu_data, flags, sig);
5817c478bd9Sstevel@tonic-gate 		} else if (!okay) {
5827c478bd9Sstevel@tonic-gate 			perror("fuser");
5837c478bd9Sstevel@tonic-gate 			errors = 1;
5847c478bd9Sstevel@tonic-gate 			free(fu_data);
5857c478bd9Sstevel@tonic-gate 			continue;
5867c478bd9Sstevel@tonic-gate 		}
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
5897c478bd9Sstevel@tonic-gate 		free(fu_data);
5907c478bd9Sstevel@tonic-gate 	} while (++optind < argc);
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	return (errors);
5937c478bd9Sstevel@tonic-gate }
594