blob: b68ffdf7dacbec0050ad2fe15e9037fac7622174 [file] [log] [blame]
/*
* dfile.c -- pstat-based HP-UX file functions for lsof
*/
/*
* Copyright 1999 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 1999 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id";
#endif
#include "lsof.h"
#if defined(HASNCACHE)
/*
* Local definitions
*/
#define DNLCINCR 2048 /* DNLC read increment */
#define NFSIDH 256 /* file system ID hash count
* -- MUST BE A POWER OF TWO!!! */
#define NFSID sizeof(struct psfsid)
/* size of psfsid structure */
#define NL_NC sizeof(struct l_nc)
/* size of l_nc structure */
#define NPSTM sizeof(struct pst_mpathnode)
/* size of pst_mpathnode */
/*
* Local structure definitions
*/
struct l_nc { /* local name cache */
struct psfileid id; /* node's PSTAT ID */
struct psfileid par; /* parent's PSTAT ID */
struct l_nc *pl; /* local parent name cache entry --
* NULL if not found or not yet
* accessed (see pls) */
int pls; /* status of pl: 0 = not accessed
* 1 = accessed */
int nl; /* name length */
char *nm; /* name */
struct l_nc *next; /* next hash bucket link */
};
struct l_fic { /* file system ID cache */
struct psfsid fsid; /* file system ID */
int nc; /* files cached for file system */
struct l_fic *next; /* next hash bucket link */
};
/*
* Local static variables
*/
static int Nceh; /* number of Nchash entries allocated */
static struct l_nc **Nchash = (struct l_nc **)NULL;
/* the name cache hash buckets */
static int Ncmask; /* power of two mask for the name
* cache -- sized from Nc */
static int Ndnlc; /* number of DNLC entries via
* pst_dynamic.psd_dnlc_size */
static struct l_fic **Ncfsid = (struct l_fic **)NULL;
/* the file system hash buckets */
static struct pst_fid Nzpf; /* zeroed pst_fid (for memcmp()) */
static struct psfileid Nzps; /* zeroed psfilid (for memcmp()) */
static int Nzpfs = 0; /* Nzpf status: 1 = zeroed */
static int Nzpss = 0; /* Nzps status: 1 = zeroed */
/*
* Local macros
*/
#define HASHFSID(i) (Ncfsid + \
(((int)(((((struct psfsid *)i)->psfs_id * 31415) << 3)&0xfffffff) \
+ (int)((((((struct psfsid *)i)->psfs_type * 31415) << 5)&0xfffffff))) \
& (NFSIDH - 1)))
#define HASHPSFILEID(p) (Nchash + \
(((int)(((int)((((struct psfileid *)p)->psf_fsid.psfs_id * 31415) << 3)\
& 0xfffffff) \
+ (int)(((((struct psfileid *)p)->psf_fsid.psfs_type * 31415) << 5) \
& 0xfffffff) \
+ (int)(((((struct psfileid *)p)->psf_fileid * 31415) << 7) \
& 0xfffffff))) \
& Ncmask))
/*
* Local function prototypes
*/
_PROTOTYPE(static struct l_nc *ncache_addr,(struct psfileid *ps));
_PROTOTYPE(static void ncache_free,(void));
_PROTOTYPE(static int ncache_isroot,(struct psfileid *ps));
_PROTOTYPE(static void ncache_size,(void));
#endif /* defined(HASNCACHE) */
#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 gethostbyname() and RFC2553-compatible
* getipnodebyname() functions
*/
extern struct hostent *
gethostbyname2(nm, prot)
char *nm; /* host name */
int prot; /* protocol -- AF_INET or AF_INET6 */
{
int err;
if (prot == AF_INET) {
/*
* This shouldn't be necessary if /etc/nsswitch.conf is correct, but
* it's a good fail-safe in case /etc/nsswitch.conf is missing or
* incorrect.
*/
return(gethostbyname(nm));
}
return(getipnodebyname(nm, prot, 0, &err));
}
#endif /* defined(HASIPv6) */
/*
* 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);
}
#if defined(HASNCACHE)
/*
* ncache_addr() -- get ncache entry address
*/
static struct l_nc *
ncache_addr(ps)
struct psfileid *ps; /* parent's psfileid */
{
struct l_nc **hp, *lc;
for (hp = HASHPSFILEID(ps), lc = *hp; lc; lc = lc->next) {
if (!memcmp((void *)ps, (void *)&lc->id, sizeof(struct psfileid)))
return(lc);
}
return((struct l_nc *)NULL);
}
/*
* ncache_alloc() -- allocate name cache space
*/
static void
ncache_alloc()
{
if (Nchash || Ncfsid)
ncache_free();
(void) ncache_size();
if (!(Nchash = (struct l_nc **)calloc(Nceh, sizeof(struct l_nc *))))
{
(void) fprintf(stderr,
"%s: can't allocate %d local name cache entries\n", Pn, Nceh);
Exit(1);
}
if (Ncfsid)
return;
if (!(Ncfsid = (struct l_fic **)calloc(NFSIDH, sizeof(struct l_fic *))))
{
(void) fprintf(stderr,
"%s: can't allocate %d local file system cache entries\n",
Pn, NFSIDH);
Exit(1);
}
}
/*
* ncache_free() -- free previous ncache allocations
*/
static void
ncache_free()
{
int i;
struct l_fic **fh, *fp, *fx;
struct l_nc **nh, *np, *nx;
if (Ncfsid) {
/*
* Free file system ID hash bucket contents.
*/
for (fh = Ncfsid, i = 0; i < NFSIDH; fh++, i++) {
for (fp = *fh; fp; fp = fx) {
fx = fp->next;
(void) free((MALLOC_P *)fp);
}
Ncfsid[i] = (struct l_fic *)NULL;
}
}
if (Nchash) {
/*
* Free name cache.
*/
for (i = 0, nh = Nchash; i < Nceh; i++, nh++) {
for (np = *nh; np; np = nx) {
nx = np->next;
if (np->nm)
(void) free((MALLOC_P *)np->nm);
(void) free((MALLOC_P *)np);
}
}
(void) free((MALLOC_P *)Nchash);
Nchash = (struct l_nc **)NULL;
}
}
/*
* ncache_isroot() -- does psfileid represent the root of a file system?
*/
static int
ncache_isroot(ps)
struct psfileid *ps; /* psfileid */
{
if (!ps->psf_fsid.psfs_id && !ps->psf_fsid.psfs_type
&& ps->psf_fileid == -1)
return(1);
# if defined(HASFSINO)
if (!Lf->fs_ino || (Lf->inp_ty != 1) || !Lf->dev_def)
return(0);
if ((Lf->dev == (dev_t)ps->psf_fsid.psfs_id)
&& (Lf->fs_ino == (unsigned long)ps->psf_fileid))
return(1);
# endif /* defined(HASFSINO) */
return(0);
}
/*
* ncache_load() -- load name cache
*/
void
ncache_load()
{
if (!Fncache)
return;
(void) ncache_alloc();
if (!Nzpfs) {
(void)memset((void *)&Nzpf, 0, sizeof(Nzpf));
Nzpfs = 1;
}
if (!Nzpss) {
(void)memset((void *)&Nzps, 0, sizeof(Nzps));
Nzpss = 1;
}
}
/*
* ncache_loadfs() -- load the name cache for a file system
*/
struct l_fic *
ncache_loadfs(fsid, fh)
struct psfsid *fsid; /* ID of file system to add */
struct l_fic **fh; /* Ncfsid hash bucket */
{
char *cp;
struct l_fic *f;
int i, nl, nr;
struct pst_mpathnode mp[DNLCINCR];
struct l_nc **nh, *nn, *nt, *ntp;
int x = 0;
/*
* Allocate a new file system pointer structure and link it to its bucket.
*/
if (!(f = (struct l_fic *)malloc(sizeof(struct l_fic)))) {
(void) fprintf(stderr, "%s: no fsid structure space\n", Pn);
Exit(1);
}
f->fsid = *fsid;
f->nc = 0;
f->next = *fh;
*fh = f;
while ((nr = pstat_getmpathname(&mp[0], NPSTM, DNLCINCR, x, fsid)) > 0)
{
x = mp[nr - 1].psr_idx + 1;
for (i = 0; i < nr; i++) {
/*
* Ignore NUL names, ".", and "..".
*/
if (!(nl = (int)strlen(mp[i].psr_name)))
continue;
if ((nl < 3) && (mp[i].psr_name[0] == '.')) {
if ((nl == 1) || (mp[i].psr_name[1] == '.'))
continue;
}
/*
* Allocate name and name cache structure space.
*/
if (!(cp = (char *)malloc((MALLOC_S)(nl + 1)))) {
(void) fprintf(stderr,
"%s: no name entry space (%d) for:%s\n",
Pn, nl + 1, mp[i].psr_name);
Exit(1);
}
if (!(nn = (struct l_nc *)malloc(sizeof(struct l_nc)))) {
(void) fprintf(stderr,
"%s: no name cache entry space (%d) for: %s\n",
Pn, (int)sizeof(struct l_nc), mp[i].psr_name);
Exit(1);
}
/*
* Fill in name cache entry, complete with name and name length.
*/
(void) snpf(cp, nl + 1, "%s", mp[i].psr_name);
nn->id = mp[i].psr_file;
nn->par = mp[i].psr_parent;
nn->nm = cp;
nn->nl = nl;
nn->pl = nn->next = (struct l_nc *)NULL;
nn->pls = 0;
nh = HASHPSFILEID(&mp[i].psr_file);
/*
* Skip to the end of the hash bucket chain, looking for
* duplicates along the way.
*/
for (nt = *nh, ntp = (struct l_nc *)NULL;
nt;
ntp = nt, nt = nt->next)
{
if (memcmp((void *)&nt->id, (void *)&nn->id, NL_NC) == 0)
break;
}
if (nt) {
/*
* Remove a duplicate.
*/
if (ntp)
ntp = nt->next;
else
*nh = nt->next;
(void) free((MALLOC_P *)nt->nm);
(void) free((MALLOC_P *)nt);
(void) free((MALLOC_P *)nn->nm);
(void) free((MALLOC_P *)nn);
} else {
/*
* Link a new entry.
*/
if (ntp)
ntp->next = nn;
else
*nh = nn;
f->nc++;
}
}
if (nr < DNLCINCR)
break;
}
return(f);
}
/*
* ncache_lookup() -- look up a node's name in the kernel's name cache
*/
char *
ncache_lookup(buf, blen, fp)
char *buf; /* receiving name buffer */
int blen; /* receiving buffer length */
int *fp; /* full path reply */
{
char *cp = buf;
int ef;
struct l_fic **fh, *fs;
struct l_nc *lc;
int nl, rlen;
char *pc;
*cp = '\0';
*fp = 0;
# if defined(HASFSINO)
/*
* If the entry has an inode number that matches the inode number of the
* file system mount point, return an empty path reply. That tells the
* caller that the already-printed system mount point name is sufficient.
*/
if (Lf->inp_ty == 1 && Lf->fs_ino && Lf->inode == Lf->fs_ino)
return(cp);
# endif /* defined(HASFSINO) */
/*
* See if cache has been loaded for this pfsid. Don't try to load if cache
* loading has been inhibited with -C, or unless the real or effective UID of
* this process is root.
*/
if ((!Myuid || Setuidroot) && Fncache) {
for (fh = HASHFSID(&Lf->psfid.psf_fsid), fs = *fh;
fs;
fs = fs->next)
{
if (memcmp((void *)&fs->fsid, (void *)&Lf->psfid.psf_fsid,
NFSID)
== 0)
break;
}
if (!fs)
fs = ncache_loadfs(&Lf->psfid.psf_fsid, fh);
} else
fs = (struct l_fic *)NULL;
/*
* Search the cache for an entry whose psfileid matches.
*/
if (!fs || !fs->nc || !(lc = ncache_addr(&Lf->psfid))) {
/*
* If the node has no cache entry, see if it's the root of the file
* system.
*/
# if defined(HASFSINO)
if (Lf->fs_ino && (Lf->inp_ty == 1) && (Lf->fs_ino == Lf->inode))
return(cp);
# endif /* defined(HASFSINO) */
/*
* If the file system's cache couldn't be loaded -- e.g., this lsof
* process lacks permission to load it or cache lookup is inhibited
* with -C -- but the UID of the file's process matches the UID of the
* lsof process, see if it's possible to read the single path name for
* this particular file. (The file must have a non-zero opaque ID.)
*/
if (!fs) {
if (Fncache
&& (Myuid == Lp->uid)
&& memcmp((void *)&Lf->opfid, (void *)&Nzpf, sizeof(Nzpf))
&& (nl = pstat_getpathname(buf, (blen - 1), &Lf->opfid)) > 0)
{
buf[nl] = '\0';
if (*buf == '/')
*fp = 1;
return(buf);
}
}
return((char *)NULL);
}
if (ncache_isroot(&lc->id)) {
/*
* If the node is the root of the file system, return a response
* that will cause the root directory to be displayed.
*/
return(cp);
}
/*
* Start the path assembly.
*/
if ((nl = lc->nl) > (blen - 1))
return((char *)NULL);
cp = buf + blen - nl - 1;
rlen = blen - nl - 1;
(void) snpf(cp, nl + 1, "%s", lc->nm);
/*
* Look up the name cache entries that are parents of the node address.
* Quit when:
*
* there's no parent;
* the file system root is reached;
* the name length is too large to fit in the receiving buffer.
*/
for (ef = 0; !ef;) {
if (!lc->pl) {
if (!lc->pls) {
/*
* If there is a parent, look up its Ncache address;
* otherwise quit on an incomplete path assembly.
*/
if (memcmp((void *)&lc->par, (void *)&Nzps, sizeof(Nzps))) {
lc->pl = ncache_addr(&lc->par);
lc->pls = 1;
} else
break;
}
}
if (ncache_isroot(&lc->par)) {
/*
* If the parent entry is the file system root, enter the file
* system root directory, and indicate that the assembly should
* stop after this entry.
*/
if (!(pc = Lf->fsdir))
break;
nl = (int)strlen(pc);
ef = 1;
} else {
/*
* Use the parent link if it exists; otherwise exit on an
* incomplete path assembly.
*/
if (!(lc = lc->pl))
break;
pc = lc->nm;
nl = lc->nl;
}
/*
* Prefix the next path component. Intersperse a '/' if the
* component doesn't end in one.
*/
if (!nl)
break;
if (pc[nl - 1] != '/') {
if (1 > rlen)
break;
*(cp - 1) = '/';
cp--;
rlen--;
}
if (nl > rlen)
break;
(void) strncpy((cp - nl), pc, nl);
cp -= nl;
rlen -= nl;
if (ef) {
/*
* If the file system root directory was just prefixed, return
* a full-path indication.
*/
*fp = 1;
break;
}
}
return(cp);
}
/*
* ncache_size() -- get DNLC size
*/
static void
ncache_size()
{
struct pst_dynamic pd;
if (pstat_getdynamic(&pd, sizeof(pd), 1, 0) != 1) {
(void) fprintf(stderr, "%s: can't get dynamic status\n", Pn);
Exit(1);
}
Ndnlc = (int)pd.psd_dnlc_size;
for (Nceh = 1; Nceh < (Ndnlc + Ndnlc); Nceh <<= 1)
;
Ncmask = Nceh - 1;
}
#endif /* defined(HASNCACHE) */
/*
* 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];
(void) snpf(buf, sizeof(buf), "%d,%#x", GET_MAJ_DEV(*dev),
GET_MIN_DEV(*dev));
return(buf);
}
/*
* process_finfo() -- process file information
*/
void
process_finfo(pd, opfid, psfid, na)
struct pst_filedetails *pd; /* file details */
struct pst_fid *opfid; /* opaque file ID for this file */
struct psfileid *psfid; /* PSTAT file ID for this file */
KA_T na; /* node address */
{
char *cp, buf[32];
dev_t dev;
int devs = 0;
int32_t lk;
struct mounts *mp;
/*
* Save file IDs for later use in name lookup.
*/
Lf->opfid = *opfid;
Lf->psfid = *psfid;
#if defined(HASFSTRUCT)
/*
* Save node ID.
*/
if (na && (Fsv & FSV_NI)) {
Lf->fna = na;
Lf->fsv |= FSV_NI;
}
#endif /* defined(HASFSTRUCT) */
/*
* Construct lock code.
*/
if ((lk = pd->psfd_lckflag) & PS_FPARTRDLCK)
Lf->lock = 'r';
else if (lk & PS_FPARTWRLCK)
Lf->lock = 'w';
else if (lk & PS_FFULLRDLCK)
Lf->lock = 'R';
else if (lk & PS_FFULLWRLCK)
Lf->lock = 'W';
else
Lf->lock = ' ';
/*
* Derive type from modes.
*/
switch ((int)(pd->psfd_mode & PS_IFMT)) {
case PS_IFREG:
cp = "REG";
Ntype = N_REGLR;
break;
case PS_IFBLK:
cp = "BLK";
Ntype = N_BLK;
break;
case PS_IFDIR:
cp = "DIR";
Ntype = N_REGLR;
break;
case PS_IFCHR:
cp = "CHR";
Ntype = N_CHR;
break;
case PS_IFIFO:
cp = "FIFO";
Ntype = N_FIFO;
break;
default:
(void) snpf(buf, sizeof(buf), "%04o",
(unsigned int)(((pd->psfd_mode & PS_IFMT) >> 12) & 0xfff));
cp = buf;
Ntype = N_REGLR;
}
if (!Lf->type[0])
(void) snpf(Lf->type, sizeof(Lf->type), "%s", cp);
Lf->ntype = Ntype;
/*
* Save device number.
*/
switch (Ntype) {
case N_FIFO:
(void) enter_dev_ch(print_kptr(na, (char *)NULL, 0));
break;
default:
dev = Lf->dev = (dev_t)pd->psfd_dev;
devs = Lf->dev_def = 1;
if ((Ntype == N_CHR) || (Ntype == N_BLK)) {
Lf->rdev = (dev_t)pd->psfd_rdev;
Lf->rdev_def = 1;
}
}
/*
* Save node number.
*/
Lf->inode = (INODETYPE)pd->psfd_ino;
Lf->inp_ty = 1;
/*
* Save link count.
*/
if (Fnlink) {
/*
* Ignore a zero link count only if the file is a FIFO.
*/
if ((Lf->nlink = (long)pd->psfd_nlink) || (Ntype != N_FIFO))
Lf->nlink_def = 1;
if (Lf->nlink_def && Nlink && (Lf->nlink < Nlink))
Lf->sf |= SELNLINK;
}
/*
* Save file system identity.
*/
if (devs) {
for (mp = readmnt(); mp; mp = mp->next) {
if (dev == mp->dev) {
Lf->fsdir = mp->dir;
Lf->fsdev = mp->fsname;
#if defined(HASFSINO)
Lf->fs_ino = (unsigned long)mp->inode;
#endif /* defined(HASFSINO) */
break;
}
}
} else
mp = (struct mounts *)NULL;
/*
* If no offset has been activated and no size saved, activate the offset or
* save the size.
*/
if (!Lf->off_def && !Lf->sz_def) {
if (Foffset)
Lf->off_def = 1;
else {
switch (Ntype) {
case N_CHR:
case N_FIFO:
Lf->off_def = 1;
break;
default:
Lf->sz = (SZOFFTYPE)pd->psfd_size;
Lf->sz_def = 1;
}
}
}
/*
* See if this is an NFS file.
*/
if (Fnfs) {
if (HasNFS < 0)
(void) scanmnttab();
if (HasNFS && mp && mp->is_nfs)
Lf->sf |= SELNFS;
}
/*
* Test for specified file.
*/
if (Sfile && is_file_named(NULL,
((Ntype == N_CHR) || (Ntype == N_BLK) ? 1
: 0)))
Lf->sf |= SELNM;
/*
* Enter name characters.
*/
if (!Lf->nm && Namech[0])
enter_nm(Namech);
}