1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate *
4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate * with the License.
8*7c478bd9Sstevel@tonic-gate *
9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate *
14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate *
20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate */
26*7c478bd9Sstevel@tonic-gate
27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
28*7c478bd9Sstevel@tonic-gate
29*7c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
30*7c478bd9Sstevel@tonic-gate /* All Rights Reserved */
31*7c478bd9Sstevel@tonic-gate
32*7c478bd9Sstevel@tonic-gate
33*7c478bd9Sstevel@tonic-gate #include <errno.h>
34*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
35*7c478bd9Sstevel@tonic-gate #include <kstat.h>
36*7c478bd9Sstevel@tonic-gate #include <libdevinfo.h>
37*7c478bd9Sstevel@tonic-gate #include <locale.h>
38*7c478bd9Sstevel@tonic-gate #include <pwd.h>
39*7c478bd9Sstevel@tonic-gate #include <signal.h>
40*7c478bd9Sstevel@tonic-gate #include <stdio.h>
41*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
42*7c478bd9Sstevel@tonic-gate #include <string.h>
43*7c478bd9Sstevel@tonic-gate #include <unistd.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/mnttab.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/utssys.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/var.h>
51*7c478bd9Sstevel@tonic-gate
52*7c478bd9Sstevel@tonic-gate /*
53*7c478bd9Sstevel@tonic-gate * Command line options for fuser command. Mutually exclusive.
54*7c478bd9Sstevel@tonic-gate */
55*7c478bd9Sstevel@tonic-gate #define OPT_FILE_ONLY 0x0001 /* -f */
56*7c478bd9Sstevel@tonic-gate #define OPT_CONTAINED 0x0002 /* -c */
57*7c478bd9Sstevel@tonic-gate
58*7c478bd9Sstevel@tonic-gate /*
59*7c478bd9Sstevel@tonic-gate * Command line option modifiers for fuser command.
60*7c478bd9Sstevel@tonic-gate */
61*7c478bd9Sstevel@tonic-gate #define OPT_SIGNAL 0x0100 /* -k, -s */
62*7c478bd9Sstevel@tonic-gate #define OPT_USERID 0x0200 /* -u */
63*7c478bd9Sstevel@tonic-gate #define OPT_NBMANDLIST 0x0400 /* -n */
64*7c478bd9Sstevel@tonic-gate #define OPT_DEVINFO 0x0800 /* -d */
65*7c478bd9Sstevel@tonic-gate
66*7c478bd9Sstevel@tonic-gate #define NELEM(a) (sizeof (a) / sizeof ((a)[0]))
67*7c478bd9Sstevel@tonic-gate
68*7c478bd9Sstevel@tonic-gate /*
69*7c478bd9Sstevel@tonic-gate * System call prototype
70*7c478bd9Sstevel@tonic-gate */
71*7c478bd9Sstevel@tonic-gate extern int utssys(void *buf, int arg, int type, void *outbp);
72*7c478bd9Sstevel@tonic-gate
73*7c478bd9Sstevel@tonic-gate /*
74*7c478bd9Sstevel@tonic-gate * Option flavors or types of options fuser command takes. Exclusive
75*7c478bd9Sstevel@tonic-gate * options (EXCL_OPT) are mutually exclusive key options, while
76*7c478bd9Sstevel@tonic-gate * modifier options (MOD_OPT) add to the key option. Examples are -f
77*7c478bd9Sstevel@tonic-gate * for EXCL_OPT and -u for MOD_OPT.
78*7c478bd9Sstevel@tonic-gate */
79*7c478bd9Sstevel@tonic-gate typedef enum {EXCL_OPT, MOD_OPT} opt_flavor_t;
80*7c478bd9Sstevel@tonic-gate
81*7c478bd9Sstevel@tonic-gate struct co_tab {
82*7c478bd9Sstevel@tonic-gate int c_flag;
83*7c478bd9Sstevel@tonic-gate char c_char;
84*7c478bd9Sstevel@tonic-gate };
85*7c478bd9Sstevel@tonic-gate
86*7c478bd9Sstevel@tonic-gate static struct co_tab code_tab[] = {
87*7c478bd9Sstevel@tonic-gate {F_CDIR, 'c'}, /* current directory */
88*7c478bd9Sstevel@tonic-gate {F_RDIR, 'r'}, /* root directory (via chroot) */
89*7c478bd9Sstevel@tonic-gate {F_TEXT, 't'}, /* textfile */
90*7c478bd9Sstevel@tonic-gate {F_OPEN, 'o'}, /* open (creat, etc.) file */
91*7c478bd9Sstevel@tonic-gate {F_MAP, 'm'}, /* mapped file */
92*7c478bd9Sstevel@tonic-gate {F_TTY, 'y'}, /* controlling tty */
93*7c478bd9Sstevel@tonic-gate {F_TRACE, 'a'}, /* trace file */
94*7c478bd9Sstevel@tonic-gate {F_NBM, 'n'} /* nbmand lock/share reservation on file */
95*7c478bd9Sstevel@tonic-gate };
96*7c478bd9Sstevel@tonic-gate
97*7c478bd9Sstevel@tonic-gate /*
98*7c478bd9Sstevel@tonic-gate * Return a pointer to the mount point matching the given special name, if
99*7c478bd9Sstevel@tonic-gate * possible, otherwise, exit with 1 if mnttab corruption is detected, else
100*7c478bd9Sstevel@tonic-gate * return NULL.
101*7c478bd9Sstevel@tonic-gate *
102*7c478bd9Sstevel@tonic-gate * NOTE: the underlying storage for mget and mref is defined static by
103*7c478bd9Sstevel@tonic-gate * libos. Repeated calls to getmntany() overwrite it; to save mnttab
104*7c478bd9Sstevel@tonic-gate * structures would require copying the member strings elsewhere.
105*7c478bd9Sstevel@tonic-gate */
106*7c478bd9Sstevel@tonic-gate static char *
spec_to_mount(char * specname)107*7c478bd9Sstevel@tonic-gate spec_to_mount(char *specname)
108*7c478bd9Sstevel@tonic-gate {
109*7c478bd9Sstevel@tonic-gate struct mnttab mref, mget;
110*7c478bd9Sstevel@tonic-gate struct stat st;
111*7c478bd9Sstevel@tonic-gate FILE *frp;
112*7c478bd9Sstevel@tonic-gate int ret;
113*7c478bd9Sstevel@tonic-gate
114*7c478bd9Sstevel@tonic-gate /* get mount-point */
115*7c478bd9Sstevel@tonic-gate if ((frp = fopen(MNTTAB, "r")) == NULL)
116*7c478bd9Sstevel@tonic-gate return (NULL);
117*7c478bd9Sstevel@tonic-gate
118*7c478bd9Sstevel@tonic-gate mntnull(&mref);
119*7c478bd9Sstevel@tonic-gate mref.mnt_special = specname;
120*7c478bd9Sstevel@tonic-gate ret = getmntany(frp, &mget, &mref);
121*7c478bd9Sstevel@tonic-gate (void) fclose(frp);
122*7c478bd9Sstevel@tonic-gate
123*7c478bd9Sstevel@tonic-gate if (ret == 0) {
124*7c478bd9Sstevel@tonic-gate if ((stat(specname, &st) == 0) && S_ISBLK(st.st_mode))
125*7c478bd9Sstevel@tonic-gate return (mget.mnt_mountp);
126*7c478bd9Sstevel@tonic-gate } else if (ret > 0) {
127*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext("mnttab is corrupted\n"));
128*7c478bd9Sstevel@tonic-gate exit(1);
129*7c478bd9Sstevel@tonic-gate }
130*7c478bd9Sstevel@tonic-gate return (NULL);
131*7c478bd9Sstevel@tonic-gate }
132*7c478bd9Sstevel@tonic-gate
133*7c478bd9Sstevel@tonic-gate /*
134*7c478bd9Sstevel@tonic-gate * The main objective of this routine is to allocate an array of f_user_t's.
135*7c478bd9Sstevel@tonic-gate * In order for it to know how large an array to allocate, it must know
136*7c478bd9Sstevel@tonic-gate * the value of v.v_proc in the kernel. To get this, we do a kstat
137*7c478bd9Sstevel@tonic-gate * lookup to get the var structure from the kernel.
138*7c478bd9Sstevel@tonic-gate */
139*7c478bd9Sstevel@tonic-gate static fu_data_t *
get_f_user_buf()140*7c478bd9Sstevel@tonic-gate get_f_user_buf()
141*7c478bd9Sstevel@tonic-gate {
142*7c478bd9Sstevel@tonic-gate fu_data_t fu_header, *fu_data;
143*7c478bd9Sstevel@tonic-gate kstat_ctl_t *kc;
144*7c478bd9Sstevel@tonic-gate struct var v;
145*7c478bd9Sstevel@tonic-gate kstat_t *ksp;
146*7c478bd9Sstevel@tonic-gate int count;
147*7c478bd9Sstevel@tonic-gate
148*7c478bd9Sstevel@tonic-gate if ((kc = kstat_open()) == NULL ||
149*7c478bd9Sstevel@tonic-gate (ksp = kstat_lookup(kc, "unix", 0, "var")) == NULL ||
150*7c478bd9Sstevel@tonic-gate kstat_read(kc, ksp, &v) == -1) {
151*7c478bd9Sstevel@tonic-gate perror(gettext("kstat_read() of struct var failed"));
152*7c478bd9Sstevel@tonic-gate exit(1);
153*7c478bd9Sstevel@tonic-gate }
154*7c478bd9Sstevel@tonic-gate (void) kstat_close(kc);
155*7c478bd9Sstevel@tonic-gate
156*7c478bd9Sstevel@tonic-gate /*
157*7c478bd9Sstevel@tonic-gate * get a count of the current number of kernel file consumers
158*7c478bd9Sstevel@tonic-gate *
159*7c478bd9Sstevel@tonic-gate * the number of kernel file consumers can change between
160*7c478bd9Sstevel@tonic-gate * the time when we get this count of all kernel file
161*7c478bd9Sstevel@tonic-gate * consumers and when we get the actual file usage
162*7c478bd9Sstevel@tonic-gate * information back from the kernel.
163*7c478bd9Sstevel@tonic-gate *
164*7c478bd9Sstevel@tonic-gate * we use the current count as a maximum because we assume
165*7c478bd9Sstevel@tonic-gate * that not all kernel file consumers are accessing the
166*7c478bd9Sstevel@tonic-gate * file we're interested in. this assumption should make
167*7c478bd9Sstevel@tonic-gate * the current number of kernel file consumers a valid
168*7c478bd9Sstevel@tonic-gate * upper limit of possible file consumers.
169*7c478bd9Sstevel@tonic-gate *
170*7c478bd9Sstevel@tonic-gate * this call should never fail
171*7c478bd9Sstevel@tonic-gate */
172*7c478bd9Sstevel@tonic-gate fu_header.fud_user_max = 0;
173*7c478bd9Sstevel@tonic-gate fu_header.fud_user_count = 0;
174*7c478bd9Sstevel@tonic-gate (void) utssys(NULL, F_KINFO_COUNT, UTS_FUSERS, &fu_header);
175*7c478bd9Sstevel@tonic-gate
176*7c478bd9Sstevel@tonic-gate count = v.v_proc + fu_header.fud_user_count;
177*7c478bd9Sstevel@tonic-gate
178*7c478bd9Sstevel@tonic-gate fu_data = (fu_data_t *)malloc(fu_data_size(count));
179*7c478bd9Sstevel@tonic-gate if (fu_data == NULL) {
180*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
181*7c478bd9Sstevel@tonic-gate gettext("fuser: could not allocate buffer\n"));
182*7c478bd9Sstevel@tonic-gate exit(1);
183*7c478bd9Sstevel@tonic-gate }
184*7c478bd9Sstevel@tonic-gate fu_data->fud_user_max = count;
185*7c478bd9Sstevel@tonic-gate fu_data->fud_user_count = 0;
186*7c478bd9Sstevel@tonic-gate return (fu_data);
187*7c478bd9Sstevel@tonic-gate }
188*7c478bd9Sstevel@tonic-gate
189*7c478bd9Sstevel@tonic-gate /*
190*7c478bd9Sstevel@tonic-gate * display the fuser usage message and exit
191*7c478bd9Sstevel@tonic-gate */
192*7c478bd9Sstevel@tonic-gate static void
usage()193*7c478bd9Sstevel@tonic-gate usage()
194*7c478bd9Sstevel@tonic-gate {
195*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
196*7c478bd9Sstevel@tonic-gate gettext("Usage: fuser [-[k|s sig]un[c|f|d]] files"
197*7c478bd9Sstevel@tonic-gate " [-[[k|s sig]un[c|f|d]] files]..\n"));
198*7c478bd9Sstevel@tonic-gate exit(1);
199*7c478bd9Sstevel@tonic-gate }
200*7c478bd9Sstevel@tonic-gate
201*7c478bd9Sstevel@tonic-gate static int
report_process(f_user_t * f_user,int options,int sig)202*7c478bd9Sstevel@tonic-gate report_process(f_user_t *f_user, int options, int sig)
203*7c478bd9Sstevel@tonic-gate {
204*7c478bd9Sstevel@tonic-gate struct passwd *pwdp;
205*7c478bd9Sstevel@tonic-gate int i;
206*7c478bd9Sstevel@tonic-gate
207*7c478bd9Sstevel@tonic-gate (void) fprintf(stdout, " %7d", (int)f_user->fu_pid);
208*7c478bd9Sstevel@tonic-gate (void) fflush(stdout);
209*7c478bd9Sstevel@tonic-gate
210*7c478bd9Sstevel@tonic-gate /* print out any character codes for the process */
211*7c478bd9Sstevel@tonic-gate for (i = 0; i < NELEM(code_tab); i++) {
212*7c478bd9Sstevel@tonic-gate if (f_user->fu_flags & code_tab[i].c_flag)
213*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%c", code_tab[i].c_char);
214*7c478bd9Sstevel@tonic-gate }
215*7c478bd9Sstevel@tonic-gate
216*7c478bd9Sstevel@tonic-gate /* optionally print the login name for the process */
217*7c478bd9Sstevel@tonic-gate if ((options & OPT_USERID) &&
218*7c478bd9Sstevel@tonic-gate ((pwdp = getpwuid(f_user->fu_uid)) != NULL))
219*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "(%s)", pwdp->pw_name);
220*7c478bd9Sstevel@tonic-gate
221*7c478bd9Sstevel@tonic-gate /* optionally send a signal to the process */
222*7c478bd9Sstevel@tonic-gate if (options & OPT_SIGNAL)
223*7c478bd9Sstevel@tonic-gate (void) kill(f_user->fu_pid, sig);
224*7c478bd9Sstevel@tonic-gate
225*7c478bd9Sstevel@tonic-gate return (0);
226*7c478bd9Sstevel@tonic-gate }
227*7c478bd9Sstevel@tonic-gate
228*7c478bd9Sstevel@tonic-gate static char *
i_get_dev_path(f_user_t * f_user,char * drv_name,int major,di_node_t * di_root)229*7c478bd9Sstevel@tonic-gate i_get_dev_path(f_user_t *f_user, char *drv_name, int major, di_node_t *di_root)
230*7c478bd9Sstevel@tonic-gate {
231*7c478bd9Sstevel@tonic-gate di_minor_t di_minor;
232*7c478bd9Sstevel@tonic-gate di_node_t di_node;
233*7c478bd9Sstevel@tonic-gate dev_t dev;
234*7c478bd9Sstevel@tonic-gate char *path;
235*7c478bd9Sstevel@tonic-gate
236*7c478bd9Sstevel@tonic-gate /*
237*7c478bd9Sstevel@tonic-gate * if we don't have a snapshot of the device tree yet, then
238*7c478bd9Sstevel@tonic-gate * take one so we can try to look up the device node and
239*7c478bd9Sstevel@tonic-gate * some kind of path to it.
240*7c478bd9Sstevel@tonic-gate */
241*7c478bd9Sstevel@tonic-gate if (*di_root == DI_NODE_NIL) {
242*7c478bd9Sstevel@tonic-gate *di_root = di_init("/", DINFOSUBTREE | DINFOMINOR);
243*7c478bd9Sstevel@tonic-gate if (*di_root == DI_NODE_NIL) {
244*7c478bd9Sstevel@tonic-gate perror(gettext("devinfo snapshot failed"));
245*7c478bd9Sstevel@tonic-gate return ((char *)-1);
246*7c478bd9Sstevel@tonic-gate }
247*7c478bd9Sstevel@tonic-gate }
248*7c478bd9Sstevel@tonic-gate
249*7c478bd9Sstevel@tonic-gate /* find device nodes that are bound to this driver */
250*7c478bd9Sstevel@tonic-gate di_node = di_drv_first_node(drv_name, *di_root);
251*7c478bd9Sstevel@tonic-gate if (di_node == DI_NODE_NIL)
252*7c478bd9Sstevel@tonic-gate return (NULL);
253*7c478bd9Sstevel@tonic-gate
254*7c478bd9Sstevel@tonic-gate /* try to get a dev_t for the device node we want to look up */
255*7c478bd9Sstevel@tonic-gate if (f_user->fu_minor == -1)
256*7c478bd9Sstevel@tonic-gate dev = DDI_DEV_T_NONE;
257*7c478bd9Sstevel@tonic-gate else
258*7c478bd9Sstevel@tonic-gate dev = makedev(major, f_user->fu_minor);
259*7c478bd9Sstevel@tonic-gate
260*7c478bd9Sstevel@tonic-gate /* walk all the device nodes bound to this driver */
261*7c478bd9Sstevel@tonic-gate do {
262*7c478bd9Sstevel@tonic-gate
263*7c478bd9Sstevel@tonic-gate /* see if we can get a path to the minor node */
264*7c478bd9Sstevel@tonic-gate if (dev != DDI_DEV_T_NONE) {
265*7c478bd9Sstevel@tonic-gate di_minor = DI_MINOR_NIL;
266*7c478bd9Sstevel@tonic-gate while (di_minor = di_minor_next(di_node, di_minor)) {
267*7c478bd9Sstevel@tonic-gate if (dev != di_minor_devt(di_minor))
268*7c478bd9Sstevel@tonic-gate continue;
269*7c478bd9Sstevel@tonic-gate path = di_devfs_minor_path(di_minor);
270*7c478bd9Sstevel@tonic-gate if (path == NULL) {
271*7c478bd9Sstevel@tonic-gate perror(gettext(
272*7c478bd9Sstevel@tonic-gate "unable to get device path"));
273*7c478bd9Sstevel@tonic-gate return ((char *)-1);
274*7c478bd9Sstevel@tonic-gate }
275*7c478bd9Sstevel@tonic-gate return (path);
276*7c478bd9Sstevel@tonic-gate }
277*7c478bd9Sstevel@tonic-gate }
278*7c478bd9Sstevel@tonic-gate
279*7c478bd9Sstevel@tonic-gate /* see if we can get a path to the device instance */
280*7c478bd9Sstevel@tonic-gate if ((f_user->fu_instance != -1) &&
281*7c478bd9Sstevel@tonic-gate (f_user->fu_instance == di_instance(di_node))) {
282*7c478bd9Sstevel@tonic-gate path = di_devfs_path(di_node);
283*7c478bd9Sstevel@tonic-gate if (path == NULL) {
284*7c478bd9Sstevel@tonic-gate perror(gettext("unable to get device path"));
285*7c478bd9Sstevel@tonic-gate return ((char *)-1);
286*7c478bd9Sstevel@tonic-gate }
287*7c478bd9Sstevel@tonic-gate return (path);
288*7c478bd9Sstevel@tonic-gate }
289*7c478bd9Sstevel@tonic-gate } while (di_node = di_drv_next_node(di_node));
290*7c478bd9Sstevel@tonic-gate
291*7c478bd9Sstevel@tonic-gate return (NULL);
292*7c478bd9Sstevel@tonic-gate }
293*7c478bd9Sstevel@tonic-gate
294*7c478bd9Sstevel@tonic-gate static int
report_kernel(f_user_t * f_user,di_node_t * di_root)295*7c478bd9Sstevel@tonic-gate report_kernel(f_user_t *f_user, di_node_t *di_root)
296*7c478bd9Sstevel@tonic-gate {
297*7c478bd9Sstevel@tonic-gate struct modinfo modinfo;
298*7c478bd9Sstevel@tonic-gate char *path;
299*7c478bd9Sstevel@tonic-gate int major = -1;
300*7c478bd9Sstevel@tonic-gate
301*7c478bd9Sstevel@tonic-gate /* get the module name */
302*7c478bd9Sstevel@tonic-gate modinfo.mi_info = MI_INFO_ONE | MI_INFO_CNT | MI_INFO_NOBASE;
303*7c478bd9Sstevel@tonic-gate modinfo.mi_id = modinfo.mi_nextid = f_user->fu_modid;
304*7c478bd9Sstevel@tonic-gate if (modctl(MODINFO, f_user->fu_modid, &modinfo) < 0) {
305*7c478bd9Sstevel@tonic-gate perror(gettext("unable to get kernel module information"));
306*7c478bd9Sstevel@tonic-gate return (-1);
307*7c478bd9Sstevel@tonic-gate }
308*7c478bd9Sstevel@tonic-gate
309*7c478bd9Sstevel@tonic-gate /*
310*7c478bd9Sstevel@tonic-gate * if we don't have any device info then just
311*7c478bd9Sstevel@tonic-gate * print the module name
312*7c478bd9Sstevel@tonic-gate */
313*7c478bd9Sstevel@tonic-gate if ((f_user->fu_instance == -1) && (f_user->fu_minor == -1)) {
314*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, " [%s]", modinfo.mi_name);
315*7c478bd9Sstevel@tonic-gate return (0);
316*7c478bd9Sstevel@tonic-gate }
317*7c478bd9Sstevel@tonic-gate
318*7c478bd9Sstevel@tonic-gate /* get the driver major number */
319*7c478bd9Sstevel@tonic-gate if (modctl(MODGETMAJBIND,
320*7c478bd9Sstevel@tonic-gate modinfo.mi_name, strlen(modinfo.mi_name) + 1, &major) < 0) {
321*7c478bd9Sstevel@tonic-gate perror(gettext("unable to get driver major number"));
322*7c478bd9Sstevel@tonic-gate return (-1);
323*7c478bd9Sstevel@tonic-gate }
324*7c478bd9Sstevel@tonic-gate
325*7c478bd9Sstevel@tonic-gate path = i_get_dev_path(f_user, modinfo.mi_name, major, di_root);
326*7c478bd9Sstevel@tonic-gate if (path == (char *)-1)
327*7c478bd9Sstevel@tonic-gate return (-1);
328*7c478bd9Sstevel@tonic-gate
329*7c478bd9Sstevel@tonic-gate /* check if we couldn't get any device pathing info */
330*7c478bd9Sstevel@tonic-gate if (path == NULL) {
331*7c478bd9Sstevel@tonic-gate if (f_user->fu_minor == -1) {
332*7c478bd9Sstevel@tonic-gate /*
333*7c478bd9Sstevel@tonic-gate * we don't really have any more info on the device
334*7c478bd9Sstevel@tonic-gate * so display the driver name in the same format
335*7c478bd9Sstevel@tonic-gate * that we would for a plain module
336*7c478bd9Sstevel@tonic-gate */
337*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, " [%s]", modinfo.mi_name);
338*7c478bd9Sstevel@tonic-gate return (0);
339*7c478bd9Sstevel@tonic-gate } else {
340*7c478bd9Sstevel@tonic-gate /*
341*7c478bd9Sstevel@tonic-gate * if we only have dev_t information, then display
342*7c478bd9Sstevel@tonic-gate * the driver name and the dev_t info
343*7c478bd9Sstevel@tonic-gate */
344*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, " [%s,dev=(%d,%d)]",
345*7c478bd9Sstevel@tonic-gate modinfo.mi_name, major, f_user->fu_minor);
346*7c478bd9Sstevel@tonic-gate return (0);
347*7c478bd9Sstevel@tonic-gate }
348*7c478bd9Sstevel@tonic-gate }
349*7c478bd9Sstevel@tonic-gate
350*7c478bd9Sstevel@tonic-gate /* display device pathing information */
351*7c478bd9Sstevel@tonic-gate if (f_user->fu_minor == -1) {
352*7c478bd9Sstevel@tonic-gate /*
353*7c478bd9Sstevel@tonic-gate * display the driver name and a path to the device
354*7c478bd9Sstevel@tonic-gate * instance.
355*7c478bd9Sstevel@tonic-gate */
356*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, " [%s,dev_path=%s]",
357*7c478bd9Sstevel@tonic-gate modinfo.mi_name, path);
358*7c478bd9Sstevel@tonic-gate } else {
359*7c478bd9Sstevel@tonic-gate /*
360*7c478bd9Sstevel@tonic-gate * here we have lot's of info. the driver name, the minor
361*7c478bd9Sstevel@tonic-gate * node dev_t, and a path to the device. display it all.
362*7c478bd9Sstevel@tonic-gate */
363*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, " [%s,dev=(%d,%d),dev_path=%s]",
364*7c478bd9Sstevel@tonic-gate modinfo.mi_name, major, f_user->fu_minor, path);
365*7c478bd9Sstevel@tonic-gate }
366*7c478bd9Sstevel@tonic-gate
367*7c478bd9Sstevel@tonic-gate di_devfs_path_free(path);
368*7c478bd9Sstevel@tonic-gate return (0);
369*7c478bd9Sstevel@tonic-gate }
370*7c478bd9Sstevel@tonic-gate
371*7c478bd9Sstevel@tonic-gate /*
372*7c478bd9Sstevel@tonic-gate * Show pids and usage indicators for the nusers processes in the users list.
373*7c478bd9Sstevel@tonic-gate * When OPT_USERID is set, give associated login names. When OPT_SIGNAL is
374*7c478bd9Sstevel@tonic-gate * set, issue the specified signal to those processes.
375*7c478bd9Sstevel@tonic-gate */
376*7c478bd9Sstevel@tonic-gate static void
report(fu_data_t * fu_data,int options,int sig)377*7c478bd9Sstevel@tonic-gate report(fu_data_t *fu_data, int options, int sig)
378*7c478bd9Sstevel@tonic-gate {
379*7c478bd9Sstevel@tonic-gate di_node_t di_root = DI_NODE_NIL;
380*7c478bd9Sstevel@tonic-gate f_user_t *f_user;
381*7c478bd9Sstevel@tonic-gate int err, i;
382*7c478bd9Sstevel@tonic-gate
383*7c478bd9Sstevel@tonic-gate for (err = i = 0; (err == 0) && (i < fu_data->fud_user_count); i++) {
384*7c478bd9Sstevel@tonic-gate
385*7c478bd9Sstevel@tonic-gate f_user = &(fu_data->fud_user[i]);
386*7c478bd9Sstevel@tonic-gate if (f_user->fu_flags & F_KERNEL) {
387*7c478bd9Sstevel@tonic-gate /* a kernel module is using the file */
388*7c478bd9Sstevel@tonic-gate err = report_kernel(f_user, &di_root);
389*7c478bd9Sstevel@tonic-gate } else {
390*7c478bd9Sstevel@tonic-gate /* a userland process using the file */
391*7c478bd9Sstevel@tonic-gate err = report_process(f_user, options, sig);
392*7c478bd9Sstevel@tonic-gate }
393*7c478bd9Sstevel@tonic-gate }
394*7c478bd9Sstevel@tonic-gate
395*7c478bd9Sstevel@tonic-gate if (di_root != DI_NODE_NIL)
396*7c478bd9Sstevel@tonic-gate di_fini(di_root);
397*7c478bd9Sstevel@tonic-gate }
398*7c478bd9Sstevel@tonic-gate
399*7c478bd9Sstevel@tonic-gate /*
400*7c478bd9Sstevel@tonic-gate * Sanity check the option "nextopt" and OR it into *options.
401*7c478bd9Sstevel@tonic-gate */
402*7c478bd9Sstevel@tonic-gate static void
set_option(int * options,int nextopt,opt_flavor_t type)403*7c478bd9Sstevel@tonic-gate set_option(int *options, int nextopt, opt_flavor_t type)
404*7c478bd9Sstevel@tonic-gate {
405*7c478bd9Sstevel@tonic-gate static const char *excl_opts[] = {"-c", "-f", "-d"};
406*7c478bd9Sstevel@tonic-gate int i;
407*7c478bd9Sstevel@tonic-gate
408*7c478bd9Sstevel@tonic-gate /*
409*7c478bd9Sstevel@tonic-gate * Disallow repeating options
410*7c478bd9Sstevel@tonic-gate */
411*7c478bd9Sstevel@tonic-gate if (*options & nextopt)
412*7c478bd9Sstevel@tonic-gate usage();
413*7c478bd9Sstevel@tonic-gate
414*7c478bd9Sstevel@tonic-gate /*
415*7c478bd9Sstevel@tonic-gate * If EXCL_OPT, allow only one option to be set
416*7c478bd9Sstevel@tonic-gate */
417*7c478bd9Sstevel@tonic-gate if ((type == EXCL_OPT) && (*options)) {
418*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
419*7c478bd9Sstevel@tonic-gate gettext("Use only one of the following options :"));
420*7c478bd9Sstevel@tonic-gate for (i = 0; i < NELEM(excl_opts); i++) {
421*7c478bd9Sstevel@tonic-gate if (i == 0) {
422*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(" %s"),
423*7c478bd9Sstevel@tonic-gate excl_opts[i]);
424*7c478bd9Sstevel@tonic-gate } else {
425*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, gettext(", %s"),
426*7c478bd9Sstevel@tonic-gate excl_opts[i]);
427*7c478bd9Sstevel@tonic-gate }
428*7c478bd9Sstevel@tonic-gate }
429*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n"),
430*7c478bd9Sstevel@tonic-gate usage();
431*7c478bd9Sstevel@tonic-gate }
432*7c478bd9Sstevel@tonic-gate *options |= nextopt;
433*7c478bd9Sstevel@tonic-gate }
434*7c478bd9Sstevel@tonic-gate
435*7c478bd9Sstevel@tonic-gate /*
436*7c478bd9Sstevel@tonic-gate * Determine which processes are using a named file or file system.
437*7c478bd9Sstevel@tonic-gate * On stdout, show the pid of each process using each command line file
438*7c478bd9Sstevel@tonic-gate * with indication(s) of its use(s). Optionally display the login
439*7c478bd9Sstevel@tonic-gate * name with each process. Also optionally, issue the specified signal to
440*7c478bd9Sstevel@tonic-gate * each process.
441*7c478bd9Sstevel@tonic-gate *
442*7c478bd9Sstevel@tonic-gate * X/Open Commands and Utilites, Issue 5 requires fuser to process
443*7c478bd9Sstevel@tonic-gate * the complete list of names it is given, so if an error is encountered
444*7c478bd9Sstevel@tonic-gate * it will continue through the list, and then exit with a non-zero
445*7c478bd9Sstevel@tonic-gate * value. This is a change from earlier behavior where the command
446*7c478bd9Sstevel@tonic-gate * would exit immediately upon an error.
447*7c478bd9Sstevel@tonic-gate *
448*7c478bd9Sstevel@tonic-gate * The preferred use of the command is with a single file or file system.
449*7c478bd9Sstevel@tonic-gate */
450*7c478bd9Sstevel@tonic-gate
451*7c478bd9Sstevel@tonic-gate int
main(int argc,char ** argv)452*7c478bd9Sstevel@tonic-gate main(int argc, char **argv)
453*7c478bd9Sstevel@tonic-gate {
454*7c478bd9Sstevel@tonic-gate fu_data_t *fu_data;
455*7c478bd9Sstevel@tonic-gate char *mntname, c;
456*7c478bd9Sstevel@tonic-gate int newfile = 0, errors = 0, opts = 0, flags = 0;
457*7c478bd9Sstevel@tonic-gate int uts_flags, sig, okay, err;
458*7c478bd9Sstevel@tonic-gate
459*7c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
460*7c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
461*7c478bd9Sstevel@tonic-gate
462*7c478bd9Sstevel@tonic-gate if (argc < 2)
463*7c478bd9Sstevel@tonic-gate usage();
464*7c478bd9Sstevel@tonic-gate
465*7c478bd9Sstevel@tonic-gate do {
466*7c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, "cdfkns:u")) != EOF) {
467*7c478bd9Sstevel@tonic-gate if (newfile) {
468*7c478bd9Sstevel@tonic-gate /*
469*7c478bd9Sstevel@tonic-gate * Starting a new group of files.
470*7c478bd9Sstevel@tonic-gate * Clear out options currently in
471*7c478bd9Sstevel@tonic-gate * force.
472*7c478bd9Sstevel@tonic-gate */
473*7c478bd9Sstevel@tonic-gate flags = opts = newfile = 0;
474*7c478bd9Sstevel@tonic-gate }
475*7c478bd9Sstevel@tonic-gate switch (c) {
476*7c478bd9Sstevel@tonic-gate case 'd':
477*7c478bd9Sstevel@tonic-gate set_option(&opts, OPT_DEVINFO, EXCL_OPT);
478*7c478bd9Sstevel@tonic-gate break;
479*7c478bd9Sstevel@tonic-gate case 'k':
480*7c478bd9Sstevel@tonic-gate set_option(&flags, OPT_SIGNAL, MOD_OPT);
481*7c478bd9Sstevel@tonic-gate sig = SIGKILL;
482*7c478bd9Sstevel@tonic-gate break;
483*7c478bd9Sstevel@tonic-gate case 's':
484*7c478bd9Sstevel@tonic-gate set_option(&flags, OPT_SIGNAL, MOD_OPT);
485*7c478bd9Sstevel@tonic-gate if (str2sig(optarg, &sig) != 0) {
486*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
487*7c478bd9Sstevel@tonic-gate gettext("Invalid signal %s\n"),
488*7c478bd9Sstevel@tonic-gate optarg);
489*7c478bd9Sstevel@tonic-gate usage();
490*7c478bd9Sstevel@tonic-gate }
491*7c478bd9Sstevel@tonic-gate break;
492*7c478bd9Sstevel@tonic-gate case 'u':
493*7c478bd9Sstevel@tonic-gate set_option(&flags, OPT_USERID, MOD_OPT);
494*7c478bd9Sstevel@tonic-gate break;
495*7c478bd9Sstevel@tonic-gate case 'n':
496*7c478bd9Sstevel@tonic-gate /*
497*7c478bd9Sstevel@tonic-gate * Report only users with NBMAND locks
498*7c478bd9Sstevel@tonic-gate */
499*7c478bd9Sstevel@tonic-gate set_option(&flags, OPT_NBMANDLIST, MOD_OPT);
500*7c478bd9Sstevel@tonic-gate break;
501*7c478bd9Sstevel@tonic-gate case 'c':
502*7c478bd9Sstevel@tonic-gate set_option(&opts, OPT_CONTAINED, EXCL_OPT);
503*7c478bd9Sstevel@tonic-gate break;
504*7c478bd9Sstevel@tonic-gate case 'f':
505*7c478bd9Sstevel@tonic-gate set_option(&opts, OPT_FILE_ONLY, EXCL_OPT);
506*7c478bd9Sstevel@tonic-gate break;
507*7c478bd9Sstevel@tonic-gate default:
508*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
509*7c478bd9Sstevel@tonic-gate gettext("Illegal option %c.\n"), c);
510*7c478bd9Sstevel@tonic-gate usage();
511*7c478bd9Sstevel@tonic-gate }
512*7c478bd9Sstevel@tonic-gate }
513*7c478bd9Sstevel@tonic-gate
514*7c478bd9Sstevel@tonic-gate if ((optind < argc) && (newfile)) {
515*7c478bd9Sstevel@tonic-gate /*
516*7c478bd9Sstevel@tonic-gate * Cancel the options currently in
517*7c478bd9Sstevel@tonic-gate * force if a lone dash is specified.
518*7c478bd9Sstevel@tonic-gate */
519*7c478bd9Sstevel@tonic-gate if (strcmp(argv[optind], "-") == 0) {
520*7c478bd9Sstevel@tonic-gate flags = opts = newfile = 0;
521*7c478bd9Sstevel@tonic-gate optind++;
522*7c478bd9Sstevel@tonic-gate }
523*7c478bd9Sstevel@tonic-gate }
524*7c478bd9Sstevel@tonic-gate
525*7c478bd9Sstevel@tonic-gate /*
526*7c478bd9Sstevel@tonic-gate * newfile is set when a new group of files is found. If all
527*7c478bd9Sstevel@tonic-gate * arguments are processed and newfile isn't set here, then
528*7c478bd9Sstevel@tonic-gate * the user did not use the correct syntax
529*7c478bd9Sstevel@tonic-gate */
530*7c478bd9Sstevel@tonic-gate if (optind > argc - 1) {
531*7c478bd9Sstevel@tonic-gate if (!newfile) {
532*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
533*7c478bd9Sstevel@tonic-gate gettext("fuser: missing file name\n"));
534*7c478bd9Sstevel@tonic-gate usage();
535*7c478bd9Sstevel@tonic-gate }
536*7c478bd9Sstevel@tonic-gate } else {
537*7c478bd9Sstevel@tonic-gate if (argv[optind][0] == '-') {
538*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
539*7c478bd9Sstevel@tonic-gate gettext("fuser: incorrect use of -\n"));
540*7c478bd9Sstevel@tonic-gate usage();
541*7c478bd9Sstevel@tonic-gate } else {
542*7c478bd9Sstevel@tonic-gate newfile = 1;
543*7c478bd9Sstevel@tonic-gate }
544*7c478bd9Sstevel@tonic-gate }
545*7c478bd9Sstevel@tonic-gate
546*7c478bd9Sstevel@tonic-gate /* allocate a buffer to hold usage data */
547*7c478bd9Sstevel@tonic-gate fu_data = get_f_user_buf();
548*7c478bd9Sstevel@tonic-gate
549*7c478bd9Sstevel@tonic-gate /*
550*7c478bd9Sstevel@tonic-gate * First print file name on stderr
551*7c478bd9Sstevel@tonic-gate * (so stdout (pids) can be piped to kill)
552*7c478bd9Sstevel@tonic-gate */
553*7c478bd9Sstevel@tonic-gate (void) fflush(stdout);
554*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", argv[optind]);
555*7c478bd9Sstevel@tonic-gate
556*7c478bd9Sstevel@tonic-gate /*
557*7c478bd9Sstevel@tonic-gate * if not OPT_FILE_ONLY, OPT_DEVINFO, or OPT_CONTAINED,
558*7c478bd9Sstevel@tonic-gate * attempt to translate the target file name to a mount
559*7c478bd9Sstevel@tonic-gate * point via /etc/mnttab.
560*7c478bd9Sstevel@tonic-gate */
561*7c478bd9Sstevel@tonic-gate okay = 0;
562*7c478bd9Sstevel@tonic-gate if (!opts &&
563*7c478bd9Sstevel@tonic-gate (mntname = spec_to_mount(argv[optind])) != NULL) {
564*7c478bd9Sstevel@tonic-gate
565*7c478bd9Sstevel@tonic-gate uts_flags = F_CONTAINED |
566*7c478bd9Sstevel@tonic-gate ((flags & OPT_NBMANDLIST) ? F_NBMANDLIST : 0);
567*7c478bd9Sstevel@tonic-gate
568*7c478bd9Sstevel@tonic-gate err = utssys(mntname, uts_flags, UTS_FUSERS, fu_data);
569*7c478bd9Sstevel@tonic-gate if (err == 0) {
570*7c478bd9Sstevel@tonic-gate report(fu_data, flags, sig);
571*7c478bd9Sstevel@tonic-gate okay = 1;
572*7c478bd9Sstevel@tonic-gate }
573*7c478bd9Sstevel@tonic-gate }
574*7c478bd9Sstevel@tonic-gate
575*7c478bd9Sstevel@tonic-gate uts_flags = \
576*7c478bd9Sstevel@tonic-gate ((opts & OPT_CONTAINED) ? F_CONTAINED : 0) |
577*7c478bd9Sstevel@tonic-gate ((opts & OPT_DEVINFO) ? F_DEVINFO : 0) |
578*7c478bd9Sstevel@tonic-gate ((flags & OPT_NBMANDLIST) ? F_NBMANDLIST : 0);
579*7c478bd9Sstevel@tonic-gate
580*7c478bd9Sstevel@tonic-gate err = utssys(argv[optind], uts_flags, UTS_FUSERS, fu_data);
581*7c478bd9Sstevel@tonic-gate if (err == 0) {
582*7c478bd9Sstevel@tonic-gate report(fu_data, flags, sig);
583*7c478bd9Sstevel@tonic-gate } else if (!okay) {
584*7c478bd9Sstevel@tonic-gate perror("fuser");
585*7c478bd9Sstevel@tonic-gate errors = 1;
586*7c478bd9Sstevel@tonic-gate free(fu_data);
587*7c478bd9Sstevel@tonic-gate continue;
588*7c478bd9Sstevel@tonic-gate }
589*7c478bd9Sstevel@tonic-gate
590*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n");
591*7c478bd9Sstevel@tonic-gate free(fu_data);
592*7c478bd9Sstevel@tonic-gate } while (++optind < argc);
593*7c478bd9Sstevel@tonic-gate
594*7c478bd9Sstevel@tonic-gate return (errors);
595*7c478bd9Sstevel@tonic-gate }
596