#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/mnttab.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/utssys.h>
#include <sys/var.h>
#define OPT_FILE_ONLY 0x0001
#define OPT_CONTAINED 0x0002
#define OPT_SIGNAL 0x0100
#define OPT_USERID 0x0200
#define OPT_NBMANDLIST 0x0400
#define OPT_DEVINFO 0x0800
#define NELEM(a) (sizeof (a) / sizeof ((a)[0]))
extern int utssys(void *buf, int arg, int type, void *outbp);
typedef enum {EXCL_OPT, MOD_OPT} opt_flavor_t;
struct co_tab {
int c_flag;
char c_char;
};
static struct co_tab code_tab[] = {
{F_CDIR, 'c'},
{F_RDIR, 'r'},
{F_TEXT, 't'},
{F_OPEN, 'o'},
{F_MAP, 'm'},
{F_TTY, 'y'},
{F_TRACE, 'a'},
{F_NBM, 'n'}
};
static char *
spec_to_mount(char *specname)
{
struct mnttab mref, mget;
struct stat st;
FILE *frp;
int ret;
if ((frp = fopen(MNTTAB, "r")) == NULL)
return (NULL);
mntnull(&mref);
mref.mnt_special = specname;
ret = getmntany(frp, &mget, &mref);
(void) fclose(frp);
if (ret == 0) {
if ((stat(specname, &st) == 0) && S_ISBLK(st.st_mode))
return (mget.mnt_mountp);
} else if (ret > 0) {
(void) fprintf(stderr, gettext("mnttab is corrupted\n"));
exit(1);
}
return (NULL);
}
static fu_data_t *
get_f_user_buf()
{
fu_data_t fu_header, *fu_data;
kstat_ctl_t *kc;
struct var v;
kstat_t *ksp;
int count;
if ((kc = kstat_open()) == NULL ||
(ksp = kstat_lookup(kc, "unix", 0, "var")) == NULL ||
kstat_read(kc, ksp, &v) == -1) {
perror(gettext("kstat_read() of struct var failed"));
exit(1);
}
(void) kstat_close(kc);
fu_header.fud_user_max = 0;
fu_header.fud_user_count = 0;
(void) utssys(NULL, F_KINFO_COUNT, UTS_FUSERS, &fu_header);
count = v.v_proc + fu_header.fud_user_count;
fu_data = (fu_data_t *)malloc(fu_data_size(count));
if (fu_data == NULL) {
(void) fprintf(stderr,
gettext("fuser: could not allocate buffer\n"));
exit(1);
}
fu_data->fud_user_max = count;
fu_data->fud_user_count = 0;
return (fu_data);
}
static void
usage()
{
(void) fprintf(stderr,
gettext("Usage: fuser [-[k|s sig]un[c|f|d]] files"
" [-[[k|s sig]un[c|f|d]] files]..\n"));
exit(1);
}
static int
report_process(f_user_t *f_user, int options, int sig)
{
struct passwd *pwdp;
int i;
(void) fprintf(stdout, " %7d", (int)f_user->fu_pid);
(void) fflush(stdout);
for (i = 0; i < NELEM(code_tab); i++) {
if (f_user->fu_flags & code_tab[i].c_flag)
(void) fprintf(stderr, "%c", code_tab[i].c_char);
}
if ((options & OPT_USERID) &&
((pwdp = getpwuid(f_user->fu_uid)) != NULL))
(void) fprintf(stderr, "(%s)", pwdp->pw_name);
if (options & OPT_SIGNAL)
(void) kill(f_user->fu_pid, sig);
return (0);
}
static char *
i_get_dev_path(f_user_t *f_user, char *drv_name, int major, di_node_t *di_root)
{
di_minor_t di_minor;
di_node_t di_node;
dev_t dev;
char *path;
if (*di_root == DI_NODE_NIL) {
*di_root = di_init("/", DINFOSUBTREE | DINFOMINOR);
if (*di_root == DI_NODE_NIL) {
perror(gettext("devinfo snapshot failed"));
return ((char *)-1);
}
}
di_node = di_drv_first_node(drv_name, *di_root);
if (di_node == DI_NODE_NIL)
return (NULL);
if (f_user->fu_minor == -1)
dev = DDI_DEV_T_NONE;
else
dev = makedev(major, f_user->fu_minor);
do {
if (dev != DDI_DEV_T_NONE) {
di_minor = DI_MINOR_NIL;
while (di_minor = di_minor_next(di_node, di_minor)) {
if (dev != di_minor_devt(di_minor))
continue;
path = di_devfs_minor_path(di_minor);
if (path == NULL) {
perror(gettext(
"unable to get device path"));
return ((char *)-1);
}
return (path);
}
}
if ((f_user->fu_instance != -1) &&
(f_user->fu_instance == di_instance(di_node))) {
path = di_devfs_path(di_node);
if (path == NULL) {
perror(gettext("unable to get device path"));
return ((char *)-1);
}
return (path);
}
} while (di_node = di_drv_next_node(di_node));
return (NULL);
}
static int
report_kernel(f_user_t *f_user, di_node_t *di_root)
{
struct modinfo modinfo;
char *path;
int major = -1;
modinfo.mi_info = MI_INFO_ONE | MI_INFO_CNT | MI_INFO_NOBASE;
modinfo.mi_id = modinfo.mi_nextid = f_user->fu_modid;
if (modctl(MODINFO, f_user->fu_modid, &modinfo) < 0) {
perror(gettext("unable to get kernel module information"));
return (-1);
}
if ((f_user->fu_instance == -1) && (f_user->fu_minor == -1)) {
(void) fprintf(stderr, " [%s]", modinfo.mi_name);
return (0);
}
if (modctl(MODGETMAJBIND,
modinfo.mi_name, strlen(modinfo.mi_name) + 1, &major) < 0) {
perror(gettext("unable to get driver major number"));
return (-1);
}
path = i_get_dev_path(f_user, modinfo.mi_name, major, di_root);
if (path == (char *)-1)
return (-1);
if (path == NULL) {
if (f_user->fu_minor == -1) {
(void) fprintf(stderr, " [%s]", modinfo.mi_name);
return (0);
} else {
(void) fprintf(stderr, " [%s,dev=(%d,%d)]",
modinfo.mi_name, major, f_user->fu_minor);
return (0);
}
}
if (f_user->fu_minor == -1) {
(void) fprintf(stderr, " [%s,dev_path=%s]",
modinfo.mi_name, path);
} else {
(void) fprintf(stderr, " [%s,dev=(%d,%d),dev_path=%s]",
modinfo.mi_name, major, f_user->fu_minor, path);
}
di_devfs_path_free(path);
return (0);
}
static void
report(fu_data_t *fu_data, int options, int sig)
{
di_node_t di_root = DI_NODE_NIL;
f_user_t *f_user;
int err, i;
for (err = i = 0; (err == 0) && (i < fu_data->fud_user_count); i++) {
f_user = &(fu_data->fud_user[i]);
if (f_user->fu_flags & F_KERNEL) {
err = report_kernel(f_user, &di_root);
} else {
err = report_process(f_user, options, sig);
}
}
if (di_root != DI_NODE_NIL)
di_fini(di_root);
}
static void
set_option(int *options, int nextopt, opt_flavor_t type)
{
static const char *excl_opts[] = {"-c", "-f", "-d"};
int i;
if (*options & nextopt)
usage();
if ((type == EXCL_OPT) && (*options)) {
(void) fprintf(stderr,
gettext("Use only one of the following options :"));
for (i = 0; i < NELEM(excl_opts); i++) {
if (i == 0) {
(void) fprintf(stderr, gettext(" %s"),
excl_opts[i]);
} else {
(void) fprintf(stderr, gettext(", %s"),
excl_opts[i]);
}
}
(void) fprintf(stderr, "\n"),
usage();
}
*options |= nextopt;
}
int
main(int argc, char **argv)
{
fu_data_t *fu_data;
char *mntname;
int c, newfile = 0, errors = 0, opts = 0, flags = 0;
int uts_flags, sig, okay, err;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
if (argc < 2)
usage();
do {
while ((c = getopt(argc, argv, "cdfkns:u")) != EOF) {
if (newfile) {
flags = opts = newfile = 0;
}
switch (c) {
case 'd':
set_option(&opts, OPT_DEVINFO, EXCL_OPT);
break;
case 'k':
set_option(&flags, OPT_SIGNAL, MOD_OPT);
sig = SIGKILL;
break;
case 's':
set_option(&flags, OPT_SIGNAL, MOD_OPT);
if (str2sig(optarg, &sig) != 0) {
(void) fprintf(stderr,
gettext("Invalid signal %s\n"),
optarg);
usage();
}
break;
case 'u':
set_option(&flags, OPT_USERID, MOD_OPT);
break;
case 'n':
set_option(&flags, OPT_NBMANDLIST, MOD_OPT);
break;
case 'c':
set_option(&opts, OPT_CONTAINED, EXCL_OPT);
break;
case 'f':
set_option(&opts, OPT_FILE_ONLY, EXCL_OPT);
break;
default:
(void) fprintf(stderr,
gettext("Illegal option %c.\n"), c);
usage();
}
}
if ((optind < argc) && (newfile)) {
if (strcmp(argv[optind], "-") == 0) {
flags = opts = newfile = 0;
optind++;
}
}
if (optind > argc - 1) {
if (!newfile) {
(void) fprintf(stderr,
gettext("fuser: missing file name\n"));
usage();
}
} else {
if (argv[optind][0] == '-') {
(void) fprintf(stderr,
gettext("fuser: incorrect use of -\n"));
usage();
} else {
newfile = 1;
}
}
fu_data = get_f_user_buf();
(void) fflush(stdout);
(void) fprintf(stderr, "%s: ", argv[optind]);
okay = 0;
if (!opts &&
(mntname = spec_to_mount(argv[optind])) != NULL) {
uts_flags = F_CONTAINED |
((flags & OPT_NBMANDLIST) ? F_NBMANDLIST : 0);
err = utssys(mntname, uts_flags, UTS_FUSERS, fu_data);
if (err == 0) {
report(fu_data, flags, sig);
okay = 1;
}
}
uts_flags = \
((opts & OPT_CONTAINED) ? F_CONTAINED : 0) |
((opts & OPT_DEVINFO) ? F_DEVINFO : 0) |
((flags & OPT_NBMANDLIST) ? F_NBMANDLIST : 0);
err = utssys(argv[optind], uts_flags, UTS_FUSERS, fu_data);
if (err == 0) {
report(fu_data, flags, sig);
} else if (!okay) {
perror("fuser");
errors = 1;
free(fu_data);
continue;
}
(void) fprintf(stderr, "\n");
free(fu_data);
} while (++optind < argc);
return (errors);
}