/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#include <errno.h>
#include <fcntl.h>
#include <kstat.h>
#include <libdevinfo.h>
#include <locale.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/sysmacros.h>
/*
* Command line options for fuser command. Mutually exclusive.
*/
/*
* Command line option modifiers for fuser command.
*/
#define NELEM(a) (sizeof (a) / sizeof ((a)[0]))
/*
* System call prototype
*/
/*
* Option flavors or types of options fuser command takes. Exclusive
* options (EXCL_OPT) are mutually exclusive key options, while
* modifier options (MOD_OPT) add to the key option. Examples are -f
* for EXCL_OPT and -u for MOD_OPT.
*/
struct co_tab {
int c_flag;
char c_char;
};
};
/*
* Return a pointer to the mount point matching the given special name, if
* possible, otherwise, exit with 1 if mnttab corruption is detected, else
* return NULL.
*
* NOTE: the underlying storage for mget and mref is defined static by
* libos. Repeated calls to getmntany() overwrite it; to save mnttab
* structures would require copying the member strings elsewhere.
*/
static char *
{
int ret;
/* get mount-point */
return (NULL);
if (ret == 0) {
return (mget.mnt_mountp);
} else if (ret > 0) {
exit(1);
}
return (NULL);
}
/*
* The main objective of this routine is to allocate an array of f_user_t's.
* In order for it to know how large an array to allocate, it must know
* the value of v.v_proc in the kernel. To get this, we do a kstat
* lookup to get the var structure from the kernel.
*/
static fu_data_t *
{
struct var v;
int count;
exit(1);
}
(void) kstat_close(kc);
/*
* get a count of the current number of kernel file consumers
*
* the number of kernel file consumers can change between
* the time when we get this count of all kernel file
* consumers and when we get the actual file usage
* information back from the kernel.
*
* we use the current count as a maximum because we assume
* that not all kernel file consumers are accessing the
* file we're interested in. this assumption should make
* the current number of kernel file consumers a valid
* upper limit of possible file consumers.
*
* this call should never fail
*/
fu_header.fud_user_max = 0;
fu_header.fud_user_count = 0;
gettext("fuser: could not allocate buffer\n"));
exit(1);
}
fu_data->fud_user_count = 0;
return (fu_data);
}
/*
* display the fuser usage message and exit
*/
static void
usage()
{
gettext("Usage: fuser [-[k|s sig]un[c|f|d]] files"
" [-[[k|s sig]un[c|f|d]] files]..\n"));
exit(1);
}
static int
{
int i;
/* print out any character codes for the process */
}
/* optionally print the login name for the process */
if ((options & OPT_USERID) &&
/* optionally send a signal to the process */
if (options & OPT_SIGNAL)
return (0);
}
static char *
{
char *path;
/*
* if we don't have a snapshot of the device tree yet, then
* take one so we can try to look up the device node and
* some kind of path to it.
*/
if (*di_root == DI_NODE_NIL) {
if (*di_root == DI_NODE_NIL) {
return ((char *)-1);
}
}
/* find device nodes that are bound to this driver */
if (di_node == DI_NODE_NIL)
return (NULL);
/* try to get a dev_t for the device node we want to look up */
else
/* walk all the device nodes bound to this driver */
do {
/* see if we can get a path to the minor node */
if (dev != DDI_DEV_T_NONE) {
continue;
"unable to get device path"));
return ((char *)-1);
}
return (path);
}
}
/* see if we can get a path to the device instance */
return ((char *)-1);
}
return (path);
}
return (NULL);
}
static int
{
char *path;
/* get the module name */
return (-1);
}
/*
* if we don't have any device info then just
* print the module name
*/
return (0);
}
/* get the driver major number */
if (modctl(MODGETMAJBIND,
return (-1);
}
if (path == (char *)-1)
return (-1);
/* check if we couldn't get any device pathing info */
/*
* we don't really have any more info on the device
* so display the driver name in the same format
* that we would for a plain module
*/
return (0);
} else {
/*
* if we only have dev_t information, then display
* the driver name and the dev_t info
*/
return (0);
}
}
/* display device pathing information */
/*
* display the driver name and a path to the device
* instance.
*/
} else {
/*
* here we have lot's of info. the driver name, the minor
* node dev_t, and a path to the device. display it all.
*/
}
return (0);
}
/*
* Show pids and usage indicators for the nusers processes in the users list.
* When OPT_USERID is set, give associated login names. When OPT_SIGNAL is
* set, issue the specified signal to those processes.
*/
static void
{
int err, i;
/* a kernel module is using the file */
} else {
/* a userland process using the file */
}
}
if (di_root != DI_NODE_NIL)
}
/*
* Sanity check the option "nextopt" and OR it into *options.
*/
static void
{
int i;
/*
* Disallow repeating options
*/
usage();
/*
* If EXCL_OPT, allow only one option to be set
*/
gettext("Use only one of the following options :"));
if (i == 0) {
excl_opts[i]);
} else {
excl_opts[i]);
}
}
usage();
}
}
/*
* Determine which processes are using a named file or file system.
* On stdout, show the pid of each process using each command line file
* with indication(s) of its use(s). Optionally display the login
* name with each process. Also optionally, issue the specified signal to
* each process.
*
* the complete list of names it is given, so if an error is encountered
* it will continue through the list, and then exit with a non-zero
* value. This is a change from earlier behavior where the command
* would exit immediately upon an error.
*
* The preferred use of the command is with a single file or file system.
*/
int
{
char *mntname, c;
(void) textdomain(TEXT_DOMAIN);
if (argc < 2)
usage();
do {
if (newfile) {
/*
* Starting a new group of files.
* Clear out options currently in
* force.
*/
}
switch (c) {
case 'd':
break;
case 'k':
break;
case 's':
gettext("Invalid signal %s\n"),
optarg);
usage();
}
break;
case 'u':
break;
case 'n':
/*
* Report only users with NBMAND locks
*/
break;
case 'c':
break;
case 'f':
break;
default:
gettext("Illegal option %c.\n"), c);
usage();
}
}
/*
* Cancel the options currently in
* force if a lone dash is specified.
*/
optind++;
}
}
/*
* newfile is set when a new group of files is found. If all
* arguments are processed and newfile isn't set here, then
* the user did not use the correct syntax
*/
if (!newfile) {
gettext("fuser: missing file name\n"));
usage();
}
} else {
gettext("fuser: incorrect use of -\n"));
usage();
} else {
newfile = 1;
}
}
/* allocate a buffer to hold usage data */
fu_data = get_f_user_buf();
/*
* First print file name on stderr
* (so stdout (pids) can be piped to kill)
*/
/*
* if not OPT_FILE_ONLY, OPT_DEVINFO, or OPT_CONTAINED,
* attempt to translate the target file name to a mount
*/
okay = 0;
if (!opts &&
uts_flags = F_CONTAINED |
if (err == 0) {
okay = 1;
}
}
uts_flags = \
if (err == 0) {
} else if (!okay) {
perror("fuser");
errors = 1;
continue;
}
return (errors);
}