blob: bb0930601ade27e54bf18db1a3a4285b8e428e17 [file] [log] [blame]
/*
* dfile.c - Solaris file processing functions for lsof
*/
/*
* Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana
* 47907. All rights reserved.
*
* Written by Victor A. Abell
*
* This software is not subject to any license of the American Telephone
* and Telegraph Company or the Regents of the University of California.
*
* Permission is granted to anyone to use this software for any purpose on
* any computer system, and to alter it and redistribute it freely, subject
* to the following restrictions:
*
* 1. Neither the authors nor Purdue University are responsible for any
* consequences of the use of this software.
*
* 2. The origin of this software must not be misrepresented, either by
* explicit claim or by omission. Credit to the authors and Purdue
* University must appear in documentation and sources.
*
* 3. Altered versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 4. This notice may not be removed or altered.
*/
#ifndef lint
static char copyright[] =
"@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: dfile.c,v 1.21 2009/03/25 19:22:16 abe Exp $";
#endif
#include "lsof.h"
/*
* Local structures
*/
struct hsfile {
struct sfile *s; /* the Sfile table address */
struct hsfile *next; /* the next hash bucket entry */
};
/*
* Local static variables
*/
static struct hsfile *HbyCd = /* hash by clone buckets */
(struct hsfile *)NULL;
static int HbyCdCt = 0; /* HbyCd entry count */
static struct hsfile *HbyFdi = /* hash by file buckets */
(struct hsfile *)NULL;
static int HbyFdiCt = 0; /* HbyFdi entry count */
static struct hsfile *HbyFrd = /* hash by file raw device buckets */
(struct hsfile *)NULL;
static int HbyFrdCt = 0; /* HbyFrd entry count */
static struct hsfile *HbyFsd = /* hash by file system buckets */
(struct hsfile *)NULL;
static int HbyFsdCt = 0; /* HbyFsd entry count */
static struct hsfile *HbyNm = /* hash by name buckets */
(struct hsfile *)NULL;
static int HbyNmCt = 0; /* HbyNm entry count */
/*
* Local definitions
*/
#define SFCDHASH 1024 /* Sfile hash by clone device */
#define SFDIHASH 4094 /* Sfile hash by (device,inode) number
* pair bucket count (power of 2!) */
#define SFFSHASH 128 /* Sfile hash by file system device
* number bucket count (power of 2!) */
#define SFHASHDEVINO(maj, min, ino, mod) ((int)(((int)((((int)(maj+1))*((int)((min+1))))+ino)*31415)&(mod-1)))
/* hash for Sfile by major device,
* minor device, and inode, modulo m
* (m must be a power of 2) */
#define SFNMHASH 4096 /* Sfile hash by name bucket count
(power of 2!) */
#define SFRDHASH 1024 /* Sfile hash by raw device number
* bucket count (power of 2!) */
#define SFHASHRDEVI(maj, min, rmaj, rmin, ino, mod) ((int)(((int)((((int)(maj+1))*((int)((min+1))))+((int)(rmaj+1)*(int)(rmin+1))+ino)*31415)&(mod-1)))
/* hash for Sfile by major device,
* minor device, major raw device,
* minor raw device, and inode, modulo
* mod (mod must be a power of 2) */
#if solaris<20500
/*
* get_max_fd() - get maximum file descriptor plus one
*/
int
get_max_fd()
{
struct rlimit r;
if (getrlimit(RLIMIT_NOFILE, &r))
return(-1);
return(r.rlim_cur);
}
#endif /* solaris<20500 */
/*
* hashSfile() - hash Sfile entries for use in is_file_named() searches
*/
void
hashSfile()
{
int cmaj, hvc, i;
static int hs = 0;
struct sfile *s;
struct hsfile *sh, *sn;
/*
* Do nothing if there are no file search arguments cached or if the
* hashes have already been constructed.
*/
if (!Sfile || hs)
return;
/*
* Preset the clone major device for Solaris.
*/
if (HaveCloneMaj) {
cmaj = CloneMaj;
hvc = 1;
} else
hvc = 0;
/*
* Allocate hash buckets by clone device, (device,inode), file system device,
* and file name.
*/
if (hvc) {
if (!(HbyCd = (struct hsfile *)calloc((MALLOC_S)SFCDHASH,
sizeof(struct hsfile))))
{
(void) fprintf(stderr,
"%s: can't allocate space for %d clone hash buckets\n",
Pn, SFCDHASH);
Exit(1);
}
}
if (!(HbyFdi = (struct hsfile *)calloc((MALLOC_S)SFDIHASH,
sizeof(struct hsfile))))
{
(void) fprintf(stderr,
"%s: can't allocate space for %d (dev,ino) hash buckets\n",
Pn, SFDIHASH);
Exit(1);
}
if (!(HbyFrd = (struct hsfile *)calloc((MALLOC_S)SFRDHASH,
sizeof(struct hsfile))))
{
(void) fprintf(stderr,
"%s: can't allocate space for %d rdev hash buckets\n",
Pn, SFRDHASH);
Exit(1);
}
if (!(HbyFsd = (struct hsfile *)calloc((MALLOC_S)SFFSHASH,
sizeof(struct hsfile))))
{
(void) fprintf(stderr,
"%s: can't allocate space for %d file sys hash buckets\n",
Pn, SFFSHASH);
Exit(1);
}
if (!(HbyNm = (struct hsfile *)calloc((MALLOC_S)SFNMHASH,
sizeof(struct hsfile))))
{
(void) fprintf(stderr,
"%s: can't allocate space for %d name hash buckets\n",
Pn, SFNMHASH);
Exit(1);
}
hs++;
/*
* Scan the Sfile chain, building file, file system, and file name hash
* bucket chains.
*/
for (s = Sfile; s; s = s->next) {
for (i = 0; i < 4; i++) {
if (i == 0) {
if (!s->aname)
continue;
sh = &HbyNm[hashbyname(s->aname, SFNMHASH)];
HbyNmCt++;
} else if (i == 1) {
if (s->type) {
sh = &HbyFdi[SFHASHDEVINO(GET_MAJ_DEV(s->dev),
GET_MIN_DEV(s->dev),
s->i,
SFDIHASH)];
HbyFdiCt++;
} else {
sh = &HbyFsd[SFHASHDEVINO(GET_MAJ_DEV(s->dev),
GET_MIN_DEV(s->dev),
0,
SFFSHASH)];
HbyFsdCt++;
}
} else if (i == 2) {
if (s->type
&& ((s->mode == S_IFCHR) || (s->mode == S_IFBLK)))
{
sh = &HbyFrd[SFHASHRDEVI(GET_MAJ_DEV(s->dev),
GET_MIN_DEV(s->dev),
GET_MAJ_DEV(s->rdev),
GET_MIN_DEV(s->rdev),
s->i,
SFRDHASH)];
HbyFrdCt++;
} else
continue;
} else {
if (!hvc || (GET_MAJ_DEV(s->rdev) != cmaj))
continue;
sh = &HbyCd[SFHASHDEVINO(0, GET_MIN_DEV(s->rdev), 0,
SFCDHASH)];
HbyCdCt++;
}
if (!sh->s) {
sh->s = s;
sh->next = (struct hsfile *)NULL;
continue;
} else {
if (!(sn = (struct hsfile *)malloc(
(MALLOC_S)sizeof(struct hsfile))))
{
(void) fprintf(stderr,
"%s: can't allocate hsfile bucket for: %s\n",
Pn, s->aname);
Exit(1);
}
sn->s = s;
sn->next = sh->next;
sh->next = sn;
}
}
}
}
/*
* is_file_named() - is this file named?
*/
int
is_file_named(p, nt, vt, ps)
char *p; /* path name; NULL = search by device
* and inode (from *Lf) */
int nt; /* node type -- e.g., N_* */
enum vtype vt; /* vnode type */
int ps; /* print status: 0 = don't copy name
* to Namech */
{
char *ep;
int f = 0;
struct sfile *s;
struct hsfile *sh;
size_t sz;
/*
* Check for a path name match, as requested.
*/
if (p && HbyNmCt) {
for (sh = &HbyNm[hashbyname(p, SFNMHASH)]; sh; sh = sh->next) {
if ((s = sh->s) && strcmp(p, s->aname) == 0) {
f = 2;
break;
}
}
}
/*
* Check for a Solaris clone file.
*/
if (!f && HbyCdCt && nt == N_STREAM && Lf->dev_def && Lf->rdev_def
&& (Lf->dev == DevDev))
{
for (sh = &HbyCd[SFHASHDEVINO(0, GET_MAJ_DEV(Lf->rdev), 0,
SFCDHASH)];
sh;
sh = sh->next)
{
if ((s = sh->s) && (GET_MAJ_DEV(Lf->rdev)
== GET_MIN_DEV(s->rdev)))
{
f = 1;
break;
}
}
}
/*
* Check for a regular file.
*/
if (!f && HbyFdiCt && Lf->dev_def
&& (Lf->inp_ty == 1 || Lf->inp_ty == 3))
{
for (sh = &HbyFdi[SFHASHDEVINO(GET_MAJ_DEV(Lf->dev),
GET_MIN_DEV(Lf->dev),
Lf->inode,
SFDIHASH)];
sh;
sh = sh->next)
{
if ((s = sh->s) && (Lf->dev == s->dev)
&& (Lf->inode == s->i)) {
f = 1;
break;
}
}
}
/*
* Check for a file system match.
*/
if (!f && HbyFsdCt && Lf->dev_def) {
for (sh = &HbyFsd[SFHASHDEVINO(GET_MAJ_DEV(Lf->dev),
GET_MIN_DEV(Lf->dev), 0, SFFSHASH)];
sh;
sh = sh->next)
{
if ((s = sh->s) && Lf->dev == s->dev) {
f = 1;
break;
}
}
}
/*
* Check for a character or block device match.
*/
if (!f && HbyFrdCt
&& ((vt = VCHR) || (vt = VBLK))
&& Lf->dev_def && (Lf->dev == DevDev)
&& Lf->rdev_def
&& (Lf->inp_ty == 1 || Lf->inp_ty == 3))
{
for (sh = &HbyFrd[SFHASHRDEVI(GET_MAJ_DEV(Lf->dev),
GET_MIN_DEV(Lf->dev),
GET_MAJ_DEV(Lf->rdev),
GET_MIN_DEV(Lf->rdev),
Lf->inode, SFRDHASH)];
sh;
sh = sh->next)
{
if ((s = sh->s) && (s->dev == Lf->dev)
&& (s->rdev == Lf->rdev) && (s->i == Lf->inode))
{
f = 1;
break;
}
}
}
/*
* Convert the name if a match occurred.
*/
if (f) {
if (f == 2) {
if (ps)
(void) snpf(Namech, Namechl, "%s", p);
} else {
if (ps && s->type) {
/*
* If the search argument isn't a file system, propagate it
* to Namech[]; otherwise, let printname() compose the name.
*/
(void) snpf(Namech, Namechl, "%s", s->name);
if (s->devnm) {
ep = endnm(&sz);
(void) snpf(ep, sz, " (%s)", s->devnm);
}
}
}
s->f = 1;
return(1);
}
return(0);
}
#if defined(HASPRINTDEV)
/*
* print_dev() - print device
*/
char *
print_dev(lf, dev)
struct lfile *lf; /* file whose device is to be printed */
dev_t *dev; /* device to be printed */
{
static char buf[128];
/*
* Avoid the Solaris major() and minor() functions from makedev(3C) to get
* printable major/minor numbers.
*
* We would like to use the L_MAXMAJ definition from <sys/sysmacros.h> all
* the time, but it's not always correct in all versions of Solaris.
*/
(void) snpf(buf, sizeof(buf), "%d,%d", (int)((*dev >> L_BITSMINOR) &
#if solaris>=20501
L_MAXMAJ
#else /* solaris<20501 */
0x3fff
#endif /* solaris>=20501 */
), (int)(*dev & L_MAXMIN));
return(buf);
}
#endif /* defined(HASPRINTDEV) */
#if defined(HAS_V_PATH)
/*
* Local definitions
*/
#define VPRDLEN ((MAXPATHLEN + 7)/8) /* v_path read length increment */
/*
* print_v_path() - print path name from vnode's v_path pointer
*/
extern int
print_v_path(lf)
struct lfile *lf; /* local file structure */
{
char buf[MAXPATHLEN+1];
unsigned char del = 0;
unsigned char aperr = 0;
# if defined(HASMNTSTAT)
struct stat sb;
# endif /* defined(HASMNTSTAT) */
# if defined(HASVXFS) && defined(HASVXFSRNL)
if (lf->is_vxfs && (lf->inp_ty == 1) && lf->fsdir) {
if (print_vxfs_rnl_path(lf))
return(1);
}
# endif /* defined(HASVXFS) && defined(HASVXFSRNL) */
(void) read_v_path((KA_T)lf->V_path, buf, (size_t)sizeof(buf));
if (buf[0]) {
# if defined(HASMNTSTAT)
if (!lf->mnt_stat && lf->dev_def && (lf->inp_ty == 1)) {
/*
* No problem was detected in applying stat(2) to this mount point.
* If the device and inode for the file are known, it is probably
* safe and worthwhile to apply stat(2) to the v_path.
*/
if (!statsafely(buf, &sb)) {
/*
* The stat(2) succeeded. See if the device and inode match.
* If they both don't match, ignore the v_path.
*/
if ((lf->dev != sb.st_dev)
|| (lf->inode != (INODETYPE)sb.st_ino)
) {
return(0);
}
} else {
/*
* The stat(2) failed.
*
* If the error reply is ENOENT and the -X option hasn't been
* specified, ignore the v_path.
*
* If the error reply is ENOENT, the -X option has been
* specified and the file's link count is zero, report the
* v_path with the "(deleted)" notation.
*
* If the error reply is EACCES or EPERM, report the v_path,
* followed by "(?)", because lsof probably lacks permission
* to apply stat(2) to v_path.
*/
switch (errno) {
case EACCES:
case EPERM:
aperr = 1;
break;
case ENOENT:
# if defined(HASXOPT)
if (Fxopt && lf->nlink_def && !lf->nlink) {
del = 1;
break;
}
# endif /* defined(HASXOPT) */
return(0);
default:
return(0);
}
}
}
# endif /* defined(HASMNTSTAT) */
/*
* Print the v_path.
*/
safestrprt(buf, stdout, 0);
if (del)
safestrprt(" (deleted)", stdout, 0);
else if (aperr)
safestrprt(" (?)", stdout, 0);
return(1);
}
return(0);
}
/*
* read_v_path() - read path name from vnode's v_path pointer
*/
extern void
read_v_path(ka, rb, rbl)
KA_T ka; /* kernel path address */
char *rb; /* receiving buffer */
size_t rbl; /* receiving buffer length */
{
char *ba;
size_t rl, tl;
*rb = '\0';
if (!ka)
return;
for (ba = rb, tl = 0;
tl < (rbl - 1);
ba += rl, ka += (KA_T)((char *)ka + rl), tl += rl
) {
/*
* Read v_path VPRDLEN bytes at a time until the local buffer is full
* or a NUL byte is reached.
*/
if ((rl = rbl - 1 - tl) > VPRDLEN)
rl = VPRDLEN;
else if (rl < 1) {
*(rb + rbl - 1) = '\0';
break;
}
if (!kread(ka, ba, rl)) {
*(ba + rl) = '\0';
if (strchr(ba, '\0') < (ba + rl))
break;
} else {
/*
* Can't read a full buffer load; try reducing the length one
* byte at a time until it reaches zero. Stop here, since it
* has been established that no more bytes can be read.
*/
for (rl--; rl > 0; rl--) {
if (!kread(ka, ba, rl)) {
*(ba + rl) = '\0';
break;
}
}
if (rl <= 0)
*ba = '\0';
break;
}
}
}
#endif /* defined(HAS_V_PATH) */
/*
* process_file() - process file
*/
void
process_file(fp)
KA_T fp; /* kernel file structure address */
{
struct file f;
int flag;
#if defined(FILEPTR)
FILEPTR = &f;
#endif /* defined(FILEPTR) */
if (kread(fp, (char *)&f, sizeof(f))) {
(void) snpf(Namech, Namechl, "can't read file struct from %s",
print_kptr(fp, (char *)NULL, 0));
enter_nm(Namech);
return;
}
Lf->off = (SZOFFTYPE)f.f_offset;
if (f.f_count) {
/*
* Construct access code.
*/
if ((flag = (f.f_flag & (FREAD | FWRITE))) == FREAD)
Lf->access = 'r';
else if (flag == FWRITE)
Lf->access = 'w';
else if (flag == (FREAD | FWRITE))
Lf->access = 'u';
#if defined(HASFSTRUCT)
/*
* Save file structure values.
*/
if (Fsv & FSV_CT) {
Lf->fct = (long)f.f_count;
Lf->fsv |= FSV_CT;
}
if (Fsv & FSV_FA) {
Lf->fsa = fp;
Lf->fsv |= FSV_FA;
}
if (Fsv & FSV_FG) {
Lf->ffg = (long)f.f_flag;
Lf->fsv |= FSV_FG;
}
if (Fsv & FSV_NI) {
Lf->fna = (KA_T)f.f_vnode;
Lf->fsv |= FSV_NI;
}
#endif /* defined(HASFSTRUCT) */
/*
* Solaris file structures contain a vnode pointer. Process it.
*/
process_node((KA_T)f.f_vnode);
return;
}
enter_nm("no more information"); }
#if defined(HASIPv6)
/*
* gethostbyname2() -- an RFC2133-compatible get-host-by-name-two function
* to get AF_INET and AF_INET6 addresses from host names,
* using the RFC2553-compatible getipnodebyname() function
*/
extern struct hostent *
gethostbyname2(nm, prot)
const char *nm; /* host name */
int prot; /* protocol -- AF_INET or AF_INET6 */
{
int err;
static struct hostent *hep = (struct hostent *)NULL;
if (hep)
(void) freehostent(hep);
return((hep = getipnodebyname(nm, prot, 0, &err)));
}
#endif /* defined(HASIPv6) */