blob: eecdf1f6386358052f85f51350e3418c46553347 [file] [log] [blame]
/*
* dproc.c - Solaris lsof functions for accessing process information
*/
/*
* 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: dproc.c,v 1.36 2010/01/18 19:03:54 abe Exp $";
#endif
#include "lsof.h"
#if solaris<20500
#include "kernelbase.h"
#endif /* solaris<20500 */
#if defined(HAS_CRED_IMPL_H)
# if solaris>=110000
#define _KERNEL
# endif /* solaris>=110000 */
#include <sys/cred_impl.h>
# if solaris>=110000
#undef _KERNEL
# endif /* solaris>=110000 */
#endif /* defined(HAS_CRED_IMPL_H) */
/*
* Local definitions
*/
#if defined(__sparc) || defined(__sparcv9)
#define ARCH64B "sparcv9"
#else /* !defined(__sparc) && !defined(__sparcv9) */
# if defined(__i386) || defined(__amd64)
#define ARCH64B "amd64"
# endif /* defined(__i386) || defined(__amd64) */
#endif /* defined(__sparc) || defined(__sparcv9) */
#if solaris>=20501
#define KVMHASHBN 8192 /* KVM hash bucket count -- MUST BE
* A POWER OF 2!!! */
#define HASHKVM(va) ((int)((va * 31415) >> 3) & (KVMHASHBN-1))
/* virtual address hash function */
# if solaris<70000
#define KAERR (u_longlong_t)-1 /* kvm_physaddr() error return */
#define KBUFT char /* kernel read buffer type */
#define KPHYS u_longlong_t /* kernel physical address type */
#define KVIRT u_int /* kernel virtual address type */
# else /* solaris>=70000 */
#define KAERR (uint64_t)-1 /* kvm_physaddr() error return */
#define KBUFT void /* kernel read buffer type */
#define KPHYS uint64_t /* kernel physical address type */
#define KVIRT uintptr_t /* kernel virtual address type */
# endif /* solaris<70000 */
#endif /* solaris>=20501 */
/*
* Local structures
*/
#if solaris>=20501
typedef struct kvmhash {
KVIRT vpa; /* virtual page address */
KPHYS pa; /* physical address */
struct kvmhash *nxt; /* next virtual address */
} kvmhash_t;
#endif /* solaris>=20501 */
/*
* Local variables
*/
#if solaris>=20501
static struct as *Kas = (struct as *)NULL;
/* pointer to kernel's address space
* map in kernel virtual memory */
static kvmhash_t **KVMhb = (kvmhash_t **)NULL;
/* KVM hash buckets */
static int PageSz = 0; /* page size */
static int PSMask = 0; /* page size mask */
static int PSShft = 0; /* page size shift */
# if solaris<70000
static struct as Kam; /* kernel's address space map */
static int Kmd = -1; /* memory device file descriptor */
# endif /* solaris<70000 */
#endif /* solaris>=20501 */
#if solaris>=20500
static KA_T Kb = (KA_T)NULL; /* KERNELBASE for Solaris 2.5 */
#endif /* solaris>=20500 */
static int Np; /* number of P[], Pgid[] and Pid[]
* entries */
static int Npa = 0; /* number of P[], Pgid[] and Pid[]
* entries for which space has been
* allocated */
static struct proc *P = NULL; /* local proc structure table */
static int *Pgid = NULL; /* process group IDs for P[] entries */
static int *Pid = NULL; /* PIDs for P[] entries */
static KA_T PrAct = (KA_T)NULL; /* kernel's *practive address */
static gid_t Savedgid; /* saved (effective) GID */
static KA_T Sgvops; /* [_]segvn_ops address */
static int Switchgid = 0; /* must switch GIDs for kvm_open() */
#if defined(HASZONES)
static znhash_t **ZoneNm = (znhash_t **)NULL;
/* zone names hash buckets */
#endif /* defined(HASZONES) */
/*
* Local function prototypes
*/
_PROTOTYPE(static void get_kernel_access,(void));
_PROTOTYPE(static void process_text,(KA_T pa));
_PROTOTYPE(static void read_proc,(void));
_PROTOTYPE(static void readfsinfo,(void));
#if solaris>=20501
_PROTOTYPE(static void readkam,(KA_T addr));
#endif /* solaris>=20501 */
#if solaris>=20501 && solaris<70000
_PROTOTYPE(extern u_longlong_t kvm_physaddr,(kvm_t *, struct as *, u_int));
#endif /* solaris>=20501 && solaris<70000 */
#if defined(HASZONES)
_PROTOTYPE(static int hash_zn,(char *zn));
#endif /* defined(HASZONES) */
/*
* close_kvm() - close kernel virtual memory access
*/
void
close_kvm()
{
if (!Kd)
return;
if (Kd) {
if (kvm_close(Kd) != 0) {
(void) fprintf(stderr, "%s: kvm_close failed\n", Pn);
Exit(1);
}
Kd = (kvm_t *)NULL;
}
#if solaris>=20501 && solaris<70000
if (Kmd >= 0) {
(void) close(Kmd);
Kmd = -1;
}
#endif /* solaris>=20501 && solaris<70000 */
}
/*
* gather_proc_info() - gather process information
*/
void
gather_proc_info()
{
short cckreg; /* conditional status of regular file
* checking:
* 0 = unconditionally check
* 1 = conditionally check */
short ckscko; /* socket file only checking status:
* 0 = none
* 1 = check only socket files,
* including TCP and UDP
* streams with eXPORT data,
* where supported */
static int ft = 1;
int i, j;
struct proc *p;
int pgid, pid, px;
long pofv;
short pss, sf;
struct user *u;
uid_t uid;
#if solaris>=20400
int k;
# if !defined(NFPCHUNK)
#define uf_ofile uf_file
#define uf_pofile uf_flag
#define u_flist u_finfo.fi_list
#define u_nofiles u_finfo.fi_nfiles
#define NFPREAD 64
# else /* defined(NFPCHUNK) */
#define NFPREAD NFPCHUNK
# endif /* !defined(NFPCHUNK) */
uf_entry_t uf[NFPREAD];
#endif /* solaris>=20400 */
#if solaris>=20500
struct cred pc;
#endif /* solaris>=20500 */
#if defined(HASZONES)
struct zone z;
int zh;
char zn[ZONENAME_MAX + 1];
znhash_t *zp, *zpn;
#endif /* defined(HASZONES) */
if (ft) {
/*
* Do first-time only operations.
*/
/*
* Get the segment vnodeops address.
*/
if (get_Nl_value("sgvops", Drive_Nl, &Sgvops) < 0)
Sgvops = (KA_T)NULL;
ft = 0;
} else if (!HasALLKMEM) {
/*
* If not the first time and the ALLKMEM device isn't available, it is
* necessary to close and reopen the KVM device, so that kvm_open()
* will acquire a fresh address for the head of the linked list process
* table.
*/
close_kvm();
open_kvm();
#if solaris>=20501
/*
* If not the first time and the ALLKMEM device isn't available,
* re-read the kernel's address space map.
*/
readkam((KA_T)NULL);
#endif /* solaris>=20501 */
}
/*
* Define socket and regular file conditional processing flags.
*
* If only socket files have been selected, or socket files have been
* selected, ANDed with other selection options, enable the skipping of
* regular files.
*
* If socket files and some process options have been selected, enable
* conditional skipping of regular file; i.e., regular files will be skipped
* unless they belong to a process selected by one of the specified options.
*/
if (Selflags & SELNW) {
/*
* Some network files selection options have been specified.
*/
if (Fand || !(Selflags & ~SELNW)) {
/*
* Selection ANDing or only network file options have been
* specified, so set unconditional skipping of regular files
* and socket file only checking.
*/
cckreg = 0;
ckscko = 1;
} else {
/*
* If ORed file selection options have been specified, or no
* ORed process selection options have been specified, enable
* unconditional file checking and clear socket file only
* checking.
*
* If only ORed process selection options have been specified,
* enable conditional file skipping and socket file only checking.
*/
if ((Selflags & SELFILE) || !(Selflags & SELPROC))
cckreg = ckscko = 0;
else
cckreg = ckscko = 1;
}
} else {
/*
* No network file selection options were specified. Enable
* unconditional file checking and clear socket file only checking.
*/
cckreg = ckscko = 0;
}
/*
* Read the process table.
*/
read_proc();
/*
* Loop through processes.
*/
for (p = P, px = 0; px < Np; p++, px++) {
/*
* Get the process ID.
*/
if (Fpgid)
pgid = Pgid[px];
else
pgid = 0;
pid = Pid[px];
#if solaris<20500
uid = p->p_uid;
#else /* solaris >=20500 */
/*
* Read credentials for Solaris 2.5 and above process.
*/
if (kread((KA_T)p->p_cred, (char *)&pc, sizeof(pc)))
continue;
uid = pc.cr_uid;
#endif /* solaris<20500 */
/*
* See if the process is excluded.
*/
if (is_proc_excl(pid, pgid, (UID_ARG)uid, &pss, &sf))
continue;
#if defined(HASZONES)
/*
* If the -z (zone) option was specified, get the zone name.
*/
if (Fzone) {
zn[0] = zn[sizeof(zn) - 1] = '\0';
if (p->p_zone
&& !kread((KA_T)p->p_zone, (char *)&z, sizeof(z)))
{
if (!z.zone_name
|| kread((KA_T)z.zone_name, (char *)&zn, sizeof(zn) - 1))
zn[0] = '\0';
}
}
#endif /* defined(HASZONES) */
/*
* Get the user area associated with the process.
*/
u = &p->p_user;
/*
* Allocate a local process structure and start filling it.
*/
if (is_cmd_excl(u->u_comm, &pss, &sf))
continue;
if (cckreg) {
/*
* If conditional checking of regular files is enabled, enable
* socket file only checking, based on the process' selection
* status.
*/
ckscko = (sf & SELPROC) ? 0 : 1;
}
alloc_lproc(pid, pgid, (int)p->p_ppid, (UID_ARG)uid, u->u_comm,
(int)pss, (int)sf);
Plf = (struct lfile *)NULL;
#if defined(HASZONES)
/*
* If zone processing is enabled and requested, and if there is a zone
* name:
*
* o Skip processes excluded by zone name.
* o Save zone name.
*/
if (Fzone && zn[0]) {
zh = hash_zn(zn);
if (ZoneArg) {
/*
* See if zone name excludes the process.
*/
for (zp = ZoneArg[zh]; zp; zp = zp->next) {
if (!strcmp(zn, zp->zn))
break;
}
if (!zp)
continue;
zp->f = 1;
Lp->pss |= PS_PRI;
Lp->sf |= SELZONE;
}
/*
* Make sure the zone name is cached, then save a pointer to it in
* the local proc structure.
*/
if (!ZoneNm) {
if (!(ZoneNm = (znhash_t **)calloc(HASHZONE,
sizeof(znhash_t *))))
{
(void) fprintf(stderr,
"%s: no space for zone name hash\n", Pn);
Exit(1);
}
}
for (zp = ZoneNm[zh]; zp; zp = zp->next) {
if (!strcmp(zn, zp->zn))
break;
}
if (!zp) {
/*
* The zone name isn't cached, so cache it.
*/
if (!(zp = (znhash_t *)malloc((MALLOC_S)sizeof(znhash_t))))
{
(void) fprintf(stderr,
"%s: no zone name cache space: %s\n", Pn, zn);
Exit(1);
}
if (!(zp->zn = mkstrcpy(zn, (MALLOC_S *)NULL))) {
(void) fprintf(stderr,
"%s: no zone name space at PID %d: %s\n",
Pn, (int)Lp->pid, zn);
Exit(1);
}
zp->next = ZoneNm[zh];
ZoneNm[zh] = zp;
}
Lp->zn = zp->zn;
}
#endif /* defined(HASZONES) */
/*
* Save file count.
*/
Unof = u->u_nofiles;
/*
* Save current working directory information.
*/
if (!ckscko && u->u_cdir) {
alloc_lfile(CWD, -1);
#if defined(FILEPTR)
FILEPTR = (struct file *)NULL;
#endif /* defined(FILEPTR) */
process_node((KA_T)u->u_cdir);
if (Lf->sf)
link_lfile();
}
/*
* Save root directory information.
*/
if (!ckscko && u->u_rdir) {
alloc_lfile(RTD, -1);
#if defined(FILEPTR)
FILEPTR = (struct file *)NULL;
#endif /* defined(FILEPTR) */
process_node((KA_T)u->u_rdir);
if (Lf->sf)
link_lfile();
}
/*
* Save information on text files.
*/
if (!ckscko && p->p_as && Sgvops) {
#if defined(FILEPTR)
FILEPTR = (struct file *)NULL;
#endif /* defined(FILEPTR) */
process_text((KA_T)p->p_as);
}
/*
* Save information on file descriptors.
*
* Under Solaris the file pointers are stored in dynamically-linked
* ufchunk structures, each containing NFPREAD file pointers. The
* first ufchunk structure is in the user area.
*
* Under Solaris 2.4 the file pointers are in a dynamically allocated,
* contiguous memory block.
*/
#if solaris<20400
for (i = 0, j = 0; i < u->u_nofiles; i++) {
if (++j > NFPCHUNK) {
if (!u->u_flist.uf_next)
break;
if (kread((KA_T)u->u_flist.uf_next,
(char *)&u->u_flist, sizeof(struct ufchunk)))
break;
j = 1;
}
if (!u->u_flist.uf_ofile[j-1])
#else /* solaris>=20400 */
for (i = 0, j = NFPREAD; i < u->u_nofiles; i++) {
if (++j > NFPREAD) {
k = u->u_nofiles - i;
if (k > NFPREAD)
k = NFPREAD;
if (kread((KA_T)((unsigned long)u->u_flist +
i * sizeof(uf_entry_t)),
(char*)&uf, k * sizeof(uf_entry_t)))
{
break;
}
j = 1;
}
if (!uf[j-1].uf_ofile)
#endif /* solaris<20400 */
continue;
alloc_lfile((char *)NULL, i);
#if solaris<20400
pofv = (long)u->u_flist.uf_pofile[j-1];
process_file((KA_T)u->u_flist.uf_ofile[j-1]);
#else /* solaris>=20400 */
pofv = uf[j-1].uf_pofile;
process_file((KA_T)uf[j-1].uf_ofile);
#endif /* solaris <20400 */
if (Lf->sf) {
#if defined(HASFSTRUCT)
if (Fsv & FSV_FG)
Lf->pof = pofv;
#endif /* defined(HASFSTRUCT) */
link_lfile();
}
}
/*
* Examine results.
*/
if (examine_lproc())
return;
}
}
/*
* get_kernel_access() - access the required information in the kernel
*/
static void
get_kernel_access()
{
int i;
struct stat sb;
KA_T v;
#if defined(HAS_AFS)
struct nlist *nl = (struct nlist *)NULL;
#endif /* defined(HAS_AFS) */
/*
* Check the Solaris or SunOS version number; check the SunOS architecture.
*/
(void) ckkv("Solaris", LSOF_VSTR, (char *)NULL, (char *)NULL);
#if solaris>=70000
/*
* Compare the Solaris 7 and above lsof compilation bit size with the kernel
* bit size.
*
* Quit on a mismatch.
*/
{
char *cp, isa[1024];
short kbits = 32;
# if defined(_LP64)
short xkbits = 64;
# else /* !defined(_LP64) */
short xkbits = 32;
# endif /* defined(_LP64) */
if (sysinfo(SI_ISALIST, isa, (long)sizeof(isa)) < 0) {
(void) fprintf(stderr, "%s: can't get ISA list: %s\n",
Pn, strerror(errno));
Exit(1);
}
for (cp = isa; *cp;) {
if (strncmp(cp, ARCH64B, strlen(ARCH64B)) == 0) {
kbits = 64;
break;
}
if (!(cp = strchr(cp, ' ')))
break;
cp++;
}
if (kbits != xkbits) {
(void) fprintf(stderr,
"%s: FATAL: lsof was compiled for a %d bit kernel,\n",
Pn, (int)xkbits);
(void) fprintf(stderr,
" but this machine has booted a %d bit kernel.\n",
(int)kbits);
Exit(1);
}
}
#endif /* solaris>=70000 */
/*
* Get kernel symbols.
*/
if (Nmlst && !is_readable(Nmlst, 1))
Exit(1);
(void) build_Nl(Drive_Nl);
#if defined(HAS_AFS)
if (!Nmlst) {
/*
* If AFS is defined and we're getting kernel symbol values from
* from N_UNIX, make a copy of Nl[] for possible use with the AFS
* modload file.
*/
if (!(nl = (struct nlist *)malloc(Nll))) {
(void) fprintf(stderr, "%s: no space (%d) for Nl[] copy\n",
Pn, Nll);
Exit(1);
}
(void) memcpy((void *)nl, (void *)Nl, (size_t)Nll);
}
#endif /* defined(HAS_AFS) */
if (nlist(Nmlst ? Nmlst : N_UNIX, Nl) < 0) {
(void) fprintf(stderr, "%s: can't read namelist from %s\n",
Pn, Nmlst ? Nmlst : N_UNIX);
Exit(1);
}
#if defined(HAS_AFS)
if (nl) {
/*
* If AFS is defined and we're getting kernel symbol values from
* N_UNIX, and if any X_AFS_* symbols isn't there, see if it is in the
* the AFS modload file. Make sure that other symbols that appear in
* both name list files have the same values.
*/
if ((get_Nl_value("arFID", Drive_Nl, &v) >= 0 && !v)
|| (get_Nl_value("avops", Drive_Nl, &v) >= 0 && !v)
|| (get_Nl_value("avol", Drive_Nl, &v) >= 0 && !v))
(void) ckAFSsym(nl);
(void) free((MALLOC_P *)nl);
}
#endif /* defined(HAS_AFS) */
/*
* Determine the availability of the ALLKMEM device. If it is available, the
* active processes will be gathered directly from the active process chain.
*
* If ALLKMEM isn't available, the active processes will be gathered via the
* kvm_*proc() functions.
*/
if (statsafely(ALLKMEM, &sb) == 0)
HasALLKMEM = 1;
#if defined(HASVXFSUTIL)
/*
* If the VXFS utility library is being used, attempt to get the VXFS inode
* offsets before setgid permission is surrendered.
*/
if (access_vxfs_ioffsets() && !Fwarn) {
/*
* Warn that the VxFS offsets are unavailable.
*/
(void) fprintf(stderr,
"%s: WARNING: vxfsu_get_ioffsets() returned an error.\n", Pn);
(void) fprintf(stderr,
"%s: WARNING: Thus, no vx_inode information is available\n",
Pn);
(void) fprintf(stderr,
"%s: WARNING: for display or selection of VxFS files.\n", Pn);
}
#endif /* defined(HASVXFSUTIL) */
#if defined(WILLDROPGID)
/*
* If Solaris kernel memory is coming from KMEM, the process is willing to
* surrender GID permission, and the ALLKMEM device is not available, set up
* for GID switching after the first call to open_kvm().
*/
if (!Memory && !HasALLKMEM) {
Savedgid = getegid();
if (Setgid)
Switchgid = 1;
}
/*
* If kernel memory isn't coming from KMEM, drop setgid permission
* before attempting to open the (Memory) file.
*/
if (Memory)
(void) dropgid();
#else /* !defined(WILLDROPGID) */
/*
* See if the non-KMEM memory file is readable.
*/
if (Memory && !is_readable(Memory, 1))
Exit(1);
#endif /* defined(WILLDROPGID) */
/*
* Open access to kernel memory.
*/
open_kvm();
#if solaris>=20500
/*
* Get the kernel's KERNELBASE value for Solaris 2.5 and above.
*/
v = (KA_T)0;
if (get_Nl_value("kbase", Drive_Nl, &v) < 0 || !v
|| kread((KA_T)v, (char *)&Kb, sizeof(Kb))) {
(void) fprintf(stderr,
"%s: can't read kernel base address from %s\n",
Pn, print_kptr(v, (char *)NULL, 0));
Exit(1);
}
#endif /* solaris>=20500 */
/*
* Get the Solaris clone major device number, if possible.
*/
v = (KA_T)0;
if ((get_Nl_value("clmaj", Drive_Nl, &v) < 0) || !v) {
if (get_Nl_value("clmaj_alt", Drive_Nl, &v) < 0)
v = (KA_T)0;
}
if (v && kread((KA_T)v, (char *)&CloneMaj, sizeof(CloneMaj)) == 0)
HaveCloneMaj = 1;
/*
* If the ALLKMEM device is available, check for the address of the kernel's
* active process chain. If it's not available, clear the ALLKMEM status.
*/
if (HasALLKMEM) {
if ((get_Nl_value("pract", Drive_Nl, &PrAct) < 0) || !PrAct)
HasALLKMEM = 0;
}
#if solaris>=20501
/*
* If the ALLKMEM device isn't available, get the kernel's virtual to physical
* map structure for Solaris 2.5.1 and above.
*/
if (!HasALLKMEM) {
if (get_Nl_value("kasp", Drive_Nl, &v) >= 0 && v) {
PageSz = getpagesize();
PSMask = PageSz - 1;
for (i = 1, PSShft = 0; i < PageSz; i <<= 1, PSShft++)
;
(void) readkam(v);
}
}
#endif /* solaris>=20501 */
#if defined(WILLDROPGID)
/*
* If the ALLKMEM device is available -- i.e., we're not using the kvm_*proc()
* functions to read proc structures -- and if we're willing to drop setgid
* permission, do so.
*/
if (HasALLKMEM)
(void) dropgid();
#endif /* defined(WILLDROPGID) */
}
#if defined(HASZONES)
/*
* enter_zone_arg() - enter zone name argument
*/
int
enter_zone_arg(zn)
char *zn; /* zone name */
{
int zh;
znhash_t *zp, *zpn;
/*
* Allocate zone argument hash space, as required.
*/
if (!ZoneArg) {
if (!(ZoneArg = (znhash_t **)calloc(HASHZONE, sizeof(znhash_t *))))
{
(void) fprintf(stderr, "%s: no space for zone arg hash\n", Pn);
Exit(1);
}
}
/*
* Hash the zone name and search the argument hash.
*/
zh = hash_zn(zn);
for (zp = ZoneArg[zh]; zp; zp = zp->next) {
if (!strcmp(zp->zn, zn))
break;
}
if (zp) {
/*
* Process a duplicate.
*/
if (!Fwarn)
(void) fprintf(stderr, "%s: duplicate zone name: %s\n", Pn, zn);
return(1);
}
/*
* Create a new hash entry and link it to its bucket.
*/
if (!(zpn = (znhash_t *)malloc((MALLOC_S)sizeof(znhash_t)))) {
(void) fprintf(stderr, "%s no hash space for zone: %s\n", Pn, zn);
Exit(1);
}
zpn->f = 0;
zpn->zn = zn;
zpn->next = ZoneArg[zh];
ZoneArg[zh] = zpn;
return(0);
}
/*
* hash_zn() - hash zone name
*/
static int
hash_zn(zn)
char *zn; /* zone name */
{
register int i, h;
size_t l;
if (!(l = strlen(zn)))
return(0);
if (l == 1)
return((int)*zn & (HASHZONE - 1));
for (i = h = 0; i < (int)(l - 1); i++) {
h ^= ((int)zn[i] * (int)zn[i+1]) << ((i*3)%13);
}
return(h & (HASHZONE - 1));
}
#endif /* defined(HASZONES) */
/*
* initialize() - perform all initialization
*/
void
initialize()
{
get_kernel_access();
/*
* Read Solaris file system information and construct the clone table.
*
* The clone table is needed to identify sockets.
*/
readfsinfo();
#if defined(HASDCACHE)
readdev(0);
#else /* !defined(HASDCACHE) */
read_clone();
#endif /*defined(HASDCACHE) */
}
/*
* kread() - read from kernel memory
*/
int
kread(addr, buf, len)
KA_T addr; /* kernel memory address */
char *buf; /* buffer to receive data */
READLEN_T len; /* length to read */
{
register int br;
/*
* Because lsof reads kernel data and follows pointers found there at a
* rate considerably slower than the kernel, lsof sometimes acquires
* invalid pointers. If the invalid pointers are fed to kvm_[k]read(),
* a segmentation violation may result, so legal kernel addresses are
* limited by the value of the KERNELBASE symbol (Kb value from the
* kernel's _kernelbase variable for Solaris 2.5 and above).
*/
#if solaris>=20500
#define KVMREAD kvm_kread
if (addr < Kb)
#else /* solaris<20500 */
#define KVMREAD kvm_read
if (addr < (KA_T)KERNELBASE)
#endif /* solaris>=20500 */
return(1);
#if solaris>=20501
/*
* Do extra address checking for Solaris above 2.5 when the ALLKMEM device
* isn't available.
*
* Make sure the virtual address represents real physical memory by testing
* it with kvm_physaddr().
*
* For Solaris below 7 read the kernel data with llseek() and read(). For
* Solaris 7 and above use kvm_pread().
*/
if (Kas && !HasALLKMEM) {
# if solaris>20501
register int b2r;
register char *bp;
# endif /* solaris>20501 */
register int h, ip, tb;
register kvmhash_t *kp;
KPHYS pa;
register KVIRT va, vpa;
# if solaris<20600
for (tb = 0, va = (KVIRT)addr;
tb < len;
tb += br, va += (KVIRT)br)
# else /* solaris>=20600 */
for (bp = buf, tb = 0, va = (KVIRT)addr;
tb < len;
bp += br, tb += br, va += (KVIRT)br)
# endif /* solaris<20600 */
{
vpa = (va & (KVIRT)~PSMask) >> PSShft;
ip = (int)(va & (KVIRT)PSMask);
h = HASHKVM(vpa);
for (kp = KVMhb[h]; kp; kp = kp->nxt) {
if (kp->vpa == vpa) {
pa = kp->pa;
break;
}
}
if (!kp) {
if ((pa = kvm_physaddr(Kd, Kas, va)) == KAERR)
return(1);
if (!(kp = (kvmhash_t *)malloc(sizeof(kvmhash_t)))) {
(void) fprintf(stderr, "%s: no kvmhash_t space\n", Pn);
Exit(1);
}
kp->nxt = KVMhb[h];
pa = kp->pa = (pa & ~(KPHYS)PSMask);
kp->vpa = vpa;
KVMhb[h] = kp;
}
# if solaris<20600
br = (int)(len - tb);
if ((ip + br) > PageSz)
br = PageSz - ip;
# else /* solaris>=20600 */
b2r = (int)(len - tb);
if ((ip + b2r) > PageSz)
b2r = PageSz - ip;
pa |= (KPHYS)ip;
# if solaris<70000
if (llseek(Kmd, (offset_t)pa, SEEK_SET) == (offset_t)-1)
return(1);
if ((br = (int)read(Kmd, (void *)bp, (size_t)b2r)) <= 0)
return(1);
# else /* solaris>=70000 */
if ((br = kvm_pread(Kd, pa, (void *)bp, (size_t)b2r)) <= 0)
return(1);
# endif /* solaris<70000 */
# endif /* solaris<20600 */
}
# if solaris>=20600
return(0);
# endif /* solaris>=20600 */
}
#endif /* solaris>=20501 */
/*
* Use kvm_read for Solaris < 2.5; use kvm_kread() Solaris >= 2.5.
*/
br = KVMREAD(Kd, (u_long)addr, buf, len);
return(((READLEN_T)br == len) ? 0 : 1);
}
/*
* open_kvm() - open kernel virtual memory access
*/
void
open_kvm()
{
if (Kd)
return;
#if defined(WILLDROPGID)
/*
* If this Solaris process began with setgid permission and its been
* surrendered, regain it.
*/
(void) restoregid();
#endif /* defined(WILLDROPGID) */
if (!(Kd = kvm_open(Nmlst, Memory, NULL, O_RDONLY, Pn))) {
(void) fprintf(stderr,
"%s: kvm_open(namelist=%s, corefile=%s): %s\n",
Pn,
Nmlst ? Nmlst : "default",
Memory ? Memory : "default",
strerror(errno));
Exit(1);
}
#if solaris>=20501 && solaris<70000
if ((Kmd = open((Memory ? Memory : KMEM), O_RDONLY)) < 0) {
(void) fprintf(stderr, "%s: open(\"/dev/mem\"): %s\n", Pn,
strerror(errno));
Exit(1);
}
#endif /* solaris>=20501 && solaris<70000 */
#if defined(WILLDROPGID)
/*
* If this process has setgid permission, and is willing to surrender it,
* do so.
*/
(void) dropgid();
/*
* If this Solaris process must switch GIDs, enable switching after the
* first call to this function.
*/
if (Switchgid == 1)
Switchgid = 2;
#endif /* define(WILLDROPGID) */
}
/*
* process_text() - process text access information
*/
#if solaris>=90000
#include <sys/avl.h>
/*
* Avl trees are implemented as follows: types in AVL trees contain an
* avl_node_t. These avl_nodes connect to other avl nodes embedded in
* objects of the same type. The avl_tree contains knowledge about the
* size of the structure and the offset of the AVL node in the object
* so we can convert between AVL nodes and (in this case) struct seg.
*
* This code was provided by Casper Dik <Casper.Dik@holland.sun.com>.
*/
#define READ_AVL_NODE(n,o,s) \
if (kread((KA_T)AVL_NODE2DATA(n, o), (char*) s, sizeof(*s))) \
return -1
static int
get_first_seg(avl_tree_t *av, struct seg *s)
{
avl_node_t *node = av->avl_root;
size_t off = av->avl_offset;
int count = 0;
while (node != NULL && ++count < MAXSEGS * 2) {
READ_AVL_NODE(node, off, s);
node = s->s_tree.avl_child[0];
if (node == NULL)
return 0;
}
return -1;
}
static int
get_next_seg(avl_tree_t *av, struct seg *s)
{
avl_node_t *node = &s->s_tree;
size_t off = av->avl_offset;
int count = 0;
if (node->avl_child[1]) {
/*
* Has right child, go all the way to the leftmost child of
* the right child.
*/
READ_AVL_NODE(node->avl_child[1], off, s);
while (node->avl_child[0] != NULL && ++count < 2 * MAXSEGS)
READ_AVL_NODE(node->avl_child[0],off,s);
if (count < 2 * MAXSEGS)
return 0;
} else {
/*
* No right child, go up until we find a node we're not a right
* child of.
*/
for (;count < 2 * MAXSEGS; count++) {
int index = AVL_XCHILD(node);
avl_node_t *parent = AVL_XPARENT(node);
if (parent == NULL)
return -1;
READ_AVL_NODE(parent, off, s);
if (index == 0)
return 0;
}
}
return -1;
}
static void
process_text(pa)
KA_T pa; /* address space description pointer */
{
struct as as;
int i, j, k;
struct seg s;
struct segvn_data vn;
avl_tree_t *avtp;
KA_T v[MAXSEGS];
/*
* Get address space description.
*/
if (kread((KA_T)pa, (char *)&as, sizeof(as))) {
alloc_lfile(" txt", -1);
(void) snpf(Namech, Namechl, "can't read text segment list (%s)",
print_kptr(pa, (char *)NULL, 0));
enter_nm(Namech);
if (Lf->sf)
link_lfile();
return;
}
/*
* Loop through the segments. The loop should stop when the segment
* pointer returns to its starting point, but just in case, it's stopped
* when MAXSEGS unique segments have been recorded or 2*MAXSEGS segments
* have been examined.
*/
for (avtp = &as.a_segtree, i = j = 0;
(i < MAXSEGS) && (j < 2*MAXSEGS);
j++)
{
if (j ? get_next_seg(avtp, &s) : get_first_seg(avtp, &s))
break;
if ((KA_T)s.s_ops == Sgvops && s.s_data) {
if (kread((KA_T)s.s_data, (char *)&vn, sizeof(vn)))
break;
if (vn.vp) {
/*
* This is a virtual node segment.
*
* If its vnode pointer has not been seen already, record the
* vnode pointer and process the vnode.
*/
for (k = 0; k < i; k++) {
if (v[k] == (KA_T)vn.vp)
break;
}
if (k >= i) {
v[i++] = (KA_T)vn.vp;
alloc_lfile(" txt", -1);
# if defined(FILEPTR)
FILEPTR = (struct file *)NULL;
# endif /* defined(FILEPTR) */
process_node((KA_T)vn.vp);
if (Lf->sf)
link_lfile();
}
}
}
}
}
#else /* solaris<90000 */
# if solaris>=20400
#define S_NEXT s_next.list
# else /* solaris<20400 */
#define S_NEXT s_next
# endif /* solaris>=20400 */
static void
process_text(pa)
KA_T pa; /* address space description pointer */
{
struct as as;
int i, j, k;
struct seg s;
struct segvn_data vn;
KA_T v[MAXSEGS];
/*
* Get address space description.
*/
if (kread((KA_T)pa, (char *)&as, sizeof(as))) {
alloc_lfile(" txt", -1);
(void) snpf(Namech, Namechl, "can't read text segment list (%s)",
print_kptr(pa, (char *)NULL, 0));
enter_nm(Namech);
if (Lf->sf)
link_lfile();
return;
}
/*
* Loop through the segments. The loop should stop when the segment
* pointer returns to its starting point, but just in case, it's stopped
* when MAXSEGS unique segments have been recorded or 2*MAXSEGS segments
* have been examined.
*/
for (s.s_next = as.a_segs, i = j = 0;
i < MAXSEGS && j < 2*MAXSEGS;
j++)
{
if (!s.S_NEXT
|| kread((KA_T)s.S_NEXT, (char *)&s, sizeof(s)))
break;
if ((KA_T)s.s_ops == Sgvops && s.s_data) {
if (kread((KA_T)s.s_data, (char *)&vn, sizeof(vn)))
break;
if (vn.vp) {
/*
* This is a virtual node segment.
*
* If its vnode pointer has not been seen already, record the
* vnode pointer and process the vnode.
*/
for (k = 0; k < i; k++) {
if (v[k] == (KA_T)vn.vp)
break;
}
if (k >= i) {
v[i++] = (KA_T)vn.vp;
alloc_lfile(" txt", -1);
# if defined(FILEPTR)
FILEPTR = (struct file *)NULL;
# endif /* defined(FILEPTR) */
process_node((KA_T)vn.vp);
if (Lf->sf)
link_lfile();
}
}
}
/*
* Follow the segment link to the starting point in the address
* space description. (The i and j counters place an absolute
* limit on the loop.)
*/
# if solaris<20400
if (s.s_next == as.a_segs)
# else /* solaris>=20400 */
if (s.s_next.list == as.a_segs.list)
# endif /* solaris<20400 */
break;
}
}
#endif /* solaris>=90000 */
/*
* readfsinfo() - read file system information
*/
static void
readfsinfo()
{
char buf[FSTYPSZ+1];
int i, len;
if ((Fsinfomax = sysfs(GETNFSTYP)) == -1) {
(void) fprintf(stderr, "%s: sysfs(GETNFSTYP) error: %s\n",
Pn, strerror(errno));
Exit(1);
}
if (Fsinfomax == 0)
return;
if (!(Fsinfo = (char **)malloc((MALLOC_S)(Fsinfomax * sizeof(char *)))))
{
(void) fprintf(stderr, "%s: no space for sysfs info\n", Pn);
Exit(1);
}
for (i = 1; i <= Fsinfomax; i++) {
if (sysfs(GETFSTYP, i, buf) == -1) {
(void) fprintf(stderr, "%s: sysfs(GETFSTYP) error: %s\n",
Pn, strerror(errno));
Exit(1);
}
if (buf[0] == '\0') {
Fsinfo[i-1] = "";
continue;
}
buf[FSTYPSZ] = '\0';
len = strlen(buf) + 1;
if (!(Fsinfo[i-1] = (char *)malloc((MALLOC_S)len))) {
(void) fprintf(stderr,
"%s: no space for file system entry %s\n", Pn, buf);
Exit(1);
}
(void) snpf(Fsinfo[i-1], len, "%s", buf);
# if defined(HAS_AFS)
if (strcasecmp(buf, "afs") == 0)
AFSfstype = i;
# endif /* defined(HAS_AFS) */
}
}
#if solaris>=20501
/*
* readkam() - read kernel's address map structure
*/
static void
readkam(addr)
KA_T addr; /* kernel virtual address */
{
register int i;
register kvmhash_t *kp, *kpp;
static KA_T kas = (KA_T)NULL;
if (addr)
kas = addr;
Kas = (struct as *)NULL;
#if solaris<70000
if (kas && !kread(kas, (char *)&Kam, sizeof(Kam)))
Kas = (KA_T)&Kam;
#else /* solaris>=70000 */
Kas = (struct as *)kas;
#endif /* solaris<70000 */
if (Kas) {
if (!KVMhb) {
if (!(KVMhb = (kvmhash_t **)calloc(KVMHASHBN,
sizeof(kvmhash_t *))))
{
(void) fprintf(stderr,
"%s: no space (%d) for KVM hash buckets\n",
Pn, (int)(KVMHASHBN * sizeof(kvmhash_t *)));
Exit(1);
}
} else if (!addr) {
for (i = 0; i < KVMHASHBN; i++) {
if ((kp = KVMhb[i])) {
while (kp) {
kpp = kp->nxt;
(void) free((void *)kp);
kp = kpp;
}
KVMhb[i] = (kvmhash_t *)NULL;
}
}
}
}
}
#endif /* solaris>=20501 */
/*
* read_proc() - read proc structures
*
* As a side-effect, Kd is set by a call to kvm_open().
*/
static void
read_proc()
{
int ct, ctl, knp, n, try;
MALLOC_S len;
struct proc *p;
KA_T pa, paf, pan;
struct pid pg, pids;
/*
* Try PROCTRYLM times to read a valid proc table.
*/
for (try = 0; try < PROCTRYLM; try++) {
/*
* Get a proc structure count estimate.
*/
if (get_Nl_value("nproc", Drive_Nl, &pa) < 0 || !pa
|| kread(pa, (char *)&knp, sizeof(knp))
|| knp < 1)
knp = PROCDFLT;
/*
* Pre-allocate space, as required.
*/
n = knp + PROCDFLT/4;
if (n > Npa) {
/*
* Allocate proc structure space.
*/
len = (n * sizeof(struct proc));
if (P)
P = (struct proc *)realloc((MALLOC_P *)P, len);
else
P = (struct proc *)malloc(len);
if (!P) {
(void) fprintf(stderr, "%s: no proc table space\n", Pn);
Exit(1);
}
/*
* Pre-allocate PGID and PID number space.
*/
len = (MALLOC_S)(n * sizeof(int));
if (Fpgid) {
if (Pgid)
Pgid = (int *)realloc((MALLOC_P *)Pgid, len);
else
Pgid = (int *)malloc(len);
if (!Pgid) {
(void) fprintf(stderr, "%s: no PGID table space\n", Pn);
Exit(1);
}
}
if (Pid)
Pid = (int *)realloc((MALLOC_P *)Pid, len);
else
Pid = (int *)malloc(len);
if (!Pid) {
(void) fprintf(stderr, "%s: no PID table space\n", Pn);
Exit(1);
}
Npa = n;
}
if (HasALLKMEM) {
/*
* Prepare for a proc table scan via direct reading of the active
* chain.
*/
if (!PrAct || kread(PrAct, (char *)&paf, sizeof(pa))) {
(void) fprintf(stderr, "%s: can't read practive from %s\n",
Pn, print_kptr(PrAct, (char *)NULL, 0));
Exit(1);
}
ct = 1;
ctl = knp << 3;
pan = paf;
pa = (KA_T)NULL;
} else {
/*
* Prepare for a proc table scan via the kvm_*proc() functions.
*/
if (kvm_setproc(Kd) != 0) {
(void) fprintf(stderr, "%s: kvm_setproc: %s\n", Pn,
strerror(errno));
Exit(1);
}
}
/*
* Accumulate proc structures.
*/
Np = 0;
for (;;) {
if (Np >= Npa) {
/*
* Expand the local proc table.
*/
Npa += PROCDFLT/2;
len = (MALLOC_S)(Npa * sizeof(struct proc));
if (!(P = (struct proc *)realloc((MALLOC_P *)P, len))) {
(void) fprintf(stderr,
"%s: no more (%d) proc space\n", Pn, Npa);
Exit(1);
}
/*
* Expand the PGID and PID tables.
*/
len = (MALLOC_S)(Npa * sizeof(int));
if (Fpgid) {
if (!(Pgid = (int *)realloc((MALLOC_P *)Pgid, len))) {
(void) fprintf(stderr,
"%s: no more (%d) PGID space\n", Pn, Npa);
Exit(1);
}
}
if (!(Pid = (int *)realloc((MALLOC_P *)Pid, len))) {
(void) fprintf(stderr,
"%s: no more (%d) PID space\n", Pn, Npa);
Exit(1);
}
}
/*
* Read the next proc structure.
*/
if (HasALLKMEM) {
/*
* If the ALLKMEM device exists, read proc structures directly
* from the active chain.
*/
if (!pa)
pa = paf;
else {
pa = pan;
if ((pan == paf) || (++ct > ctl))
break;
}
if (!pa)
break;
p = (struct proc *)&P[Np];
if (kread(pa, (char *)p, sizeof(struct proc)))
break;
pan = (KA_T)p->p_next;
} else {
/*
* If the ALLKMEM device doesn't exist, read proc structures
* via kbm_getproc().
*/
if (!(p = kvm_nextproc(Kd)))
break;
}
/*
* Check process status.
*/
if (p->p_stat == 0 || p->p_stat == SZOMB)
continue;
#if solaris >=20500
/*
* Check Solaris 2.5 and above p_cred pointer.
*/
if (!p->p_cred)
continue;
#endif /* solaris >=20500 */
/*
* Read Solaris PGID and PID numbers.
*/
if (Fpgid) {
if (!p->p_pgidp
|| kread((KA_T)p->p_pgidp, (char *)&pg, sizeof(pg)))
continue;
}
if (!p->p_pidp
|| kread((KA_T)p->p_pidp, (char *)&pids, sizeof(pids)))
continue;
/*
* Save the PGID and PID numbers in local tables.
*/
if (Fpgid)
Pgid[Np] = (int)pg.pid_id;
Pid[Np] = (int)pids.pid_id;
/*
* If the proc structure came from kvm_getproc(), save it in the
* local table.
*/
if (!HasALLKMEM)
P[Np] = *p;
Np++;
}
/*
* If not enough processes were saved in the local table, try again.
*
* If the ALLKMEM device isn't available, it is necessary to close and
* reopen the KVM device, so that kvm_open() will acquire a fresh
* address for the head of the linked list process table.
*/
if (Np >= PROCMIN)
break;
if (!HasALLKMEM) {
close_kvm();
open_kvm();
}
}
/*
* Quit if no proc structures were stored in the local table.
*/
if (try >= PROCTRYLM) {
(void) fprintf(stderr, "%s: can't read proc table\n", Pn);
Exit(1);
}
if (Np < Npa && !RptTm) {
/*
* Reduce the local proc structure table size to its minimum if
* not in repeat mode.
*/
len = (MALLOC_S)(Np * sizeof(struct proc));
if (!(P = (struct proc *)realloc((MALLOC_P *)P, len))) {
(void) fprintf(stderr, "%s: can't reduce proc table to %d\n",
Pn, Np);
Exit(1);
}
/*
* Reduce the Solaris PGID and PID tables to their minimum if
* not in repeat mode.
*/
len = (MALLOC_S)(Np * sizeof(int));
if (Fpgid) {
if (!(Pgid = (int *)realloc((MALLOC_P *)Pgid, len))) {
(void) fprintf(stderr,
"%s: can't reduce PGID table to %d\n", Pn, Np);
Exit(1);
}
}
if (!(Pid = (int *)realloc((MALLOC_P *)Pid, len))) {
(void) fprintf(stderr,
"%s: can't reduce PID table to %d\n", Pn, Np);
Exit(1);
}
Npa = Np;
}
}
#if defined(WILLDROPGID)
/*
* restoregid() -- restore setgid permission, as required
*/
void
restoregid()
{
if (Switchgid == 2 && !Setgid) {
if (setgid(Savedgid) != 0) {
(void) fprintf(stderr,
"%s: can't set effective GID to %d: %s\n",
Pn, (int)Savedgid, strerror(errno));
Exit(1);
}
Setgid = 1;
}
}
#endif /* defined(WILLDROPGID) */
#if defined(HASNCACHE) && solaris>=90000
/*
* Local static values
*/
static int Mhl; /* local name cache hash mask */
static int Nhl = 0; /* size of local name cache hash
* pointer table */
struct l_nch {
KA_T vp; /* vnode address */
KA_T dp; /* parent vnode address */
struct l_nch *pa; /* parent Ncache address */
char *nm; /* name */
int nl; /* name length */
};
static struct l_nch *Ncache = (struct l_nch *)NULL;
/* the local name cache */
static struct l_nch **Nchash = (struct l_nch **)NULL;
/* Ncache hash pointers */
static int Ncfirst = 1; /* first-call status */
static KA_T NegVN = (KA_T)NULL; /* negative vnode address */
static int Nla = 0; /* entries allocated to Ncache[] */
static int Nlu = 0; /* entries used in Ncache[] */
_PROTOTYPE(static struct l_nch *ncache_addr,(KA_T v));
#define ncachehash(v) Nchash+((((int)(v)>>2)*31415)&Mhl)
_PROTOTYPE(static int ncache_isroot,(KA_T va, char *cp));
#define LNCHINCRSZ 64 /* local size increment */
#define XNC 15 /* extra name characters to read beyond those
* in name[] of the ncache_t structure -- this
* is an efficiency hint and MUST BE AT LEAST
* ONE. */
/*
* ncache_addr() - look up a node's local ncache address
*/
static struct l_nch *
ncache_addr(v)
KA_T v; /* vnode's address */
{
struct l_nch **hp;
for (hp = ncachehash(v); *hp; hp++) {
if ((*hp)->vp == v)
return(*hp);
}
return((struct l_nch *)NULL);
}
/*
* ncache_isroot() - is head of name cache path a file system root?
*/
static int
ncache_isroot(va, cp)
KA_T va; /* kernel vnode address */
char *cp; /* partial path */
{
char buf[MAXPATHLEN];
int i;
MALLOC_S len;
struct mounts *mtp;
struct stat sb;
struct vnode v;
static int vca = 0;
static int vcn = 0;
static KA_T *vc = (KA_T *)NULL;
if (!va)
return(0);
/*
* Search the root vnode cache.
*/
for (i = 0; i < vcn; i++) {
if (va == vc[i])
return(1);
}
/*
* Read the vnode and see if it's a VDIR node with the VROOT flag set. If
* it is, then the path is complete.
*
* If it isn't, and if the file has an inode number, search the mount table
* and see if the file system's inode number is known. If it is, form the
* possible full path, safely stat() it, and see if it's inode number matches
* the one we have for this file. If it does, then the path is complete.
*/
if (kread((KA_T)va, (char *)&v, sizeof(v))
|| v.v_type != VDIR || !(v.v_flag & VROOT)) {
/*
* The vnode tests failed. Try the inode tests.
*/
if (Lf->inp_ty != 1 || !Lf->inode
|| !Lf->fsdir || (len = strlen(Lf->fsdir)) < 1)
return(0);
if ((len + 1 + strlen(cp) + 1) > sizeof(buf))
return(0);
for (mtp = readmnt(); mtp; mtp = mtp->next) {
if (!mtp->dir || !mtp->inode)
continue;
if (strcmp(Lf->fsdir, mtp->dir) == 0)
break;
}
if (!mtp)
return(0);
(void) strcpy(buf, Lf->fsdir);
if (buf[len - 1] != '/')
buf[len++] = '/';
(void) strcpy(&buf[len], cp);
if (statsafely(buf, &sb) != 0
|| (INODETYPE)sb.st_ino != Lf->inode)
return(0);
}
/*
* Add the vnode address to the root vnode cache.
*/
if (vcn >= vca) {
vca += 10;
len = (MALLOC_S)(vca * sizeof(KA_T));
if (!vc)
vc = (KA_T *)malloc(len);
else
vc = (KA_T *)realloc(vc, len);
if (!vc) {
(void) fprintf(stderr, "%s: no space for root vnode table\n",
Pn);
Exit(1);
}
}
vc[vcn++] = va;
return(1);
}
/*
* ncache_load() - load the kernel's name cache
*/
void
ncache_load()
{
char *cp;
struct l_nch **hp, *lc;
int h, i, len, n, xl;
static int iNch = 0;
nc_hash_t *kh;
static KA_T kha = (KA_T)NULL;
static nc_hash_t *khl = (nc_hash_t *)NULL;
KA_T kn;
static ncache_t *nc = (ncache_t *)NULL;
static int Nch = 0;
static int nmo = 0;
KA_T v;
static int xn = 0;
if (!Fncache)
return;
if (Ncfirst) {
/*
* Do startup (first-time) functions.
*/
Ncfirst = 0;
/*
* Establish DNLC hash size.
*/
v = (KA_T)0;
if (get_Nl_value(X_NCSIZE, (struct drive_Nl *)NULL, &v) < 0
|| !v
|| kread((KA_T)v, (char *)&Nch, sizeof(Nch)))
{
if (!Fwarn)
(void) fprintf(stderr,
"%s: WARNING: can't read DNLC hash size: %s\n",
Pn, print_kptr(v, (char *)NULL, 0));
iNch = Nch = 0;
return;
}
if ((iNch = Nch) < 1) {
if (!Fwarn)
(void) fprintf(stderr,
"%s: WARNING: DNLC hash size: %d\n", Pn, Nch);
iNch = Nch = 0;
return;
}
/*
* Get negative vnode address.
*/
if (get_Nl_value(NCACHE_NEGVN, (struct drive_Nl *)NULL, &NegVN)
< 0)
NegVN = (KA_T)NULL;
/*
* Establish DNLC hash address.
*/
v = (KA_T)0;
if (get_Nl_value(X_NCACHE,(struct drive_Nl *)NULL,(KA_T *)&v) < 0
|| !v
|| kread(v, (char *)&kha, sizeof(kha))
|| !kha
) {
if (!Fwarn)
(void) fprintf(stderr,
"%s: WARNING: no DNLC hash address\n", Pn);
iNch = Nch = 0;
return;
}
/*
* Allocate space for a local copy of the kernel's hash table.
*/
len = Nch * sizeof(nc_hash_t);
if (!(khl = (nc_hash_t *)malloc((MALLOC_S)len))) {
(void) fprintf(stderr,
"%s: can't allocate DNLC hash space: %d\n", Pn, len);
Exit(1);
}
/*
* Allocate space for a kernel DNLC entry, plus additional name space
* for efficiency.
*/
xn = XNC;
if (!(nc = (ncache_t *)malloc((MALLOC_S)(sizeof(ncache_t) + XNC))))
{
(void) fprintf(stderr,
"%s: can't allocate DNLC ncache_t space\n", Pn);
Exit(1);
}
nmo = offsetof(struct ncache, name);
/*
* Allocate estimated space for the local cache, based on the
* hash table count and the current average hash length.
*/
v = (KA_T)0;
if ((get_Nl_value("hshav", (struct drive_Nl *)NULL, (KA_T *)&v) < 0)
|| !v
|| kread(v, (char *)&i, sizeof(i))
|| (i < 1)
) {
i = 16;
if (!Fwarn) {
(void) fprintf(stderr,
"%s: can't read DNLC average hash bucket size,", Pn);
(void) fprintf(stderr, " using %d\n", i);
}
}
Nla = Nch * i;
if (!(Ncache = (struct l_nch *)calloc(Nla, sizeof(struct l_nch)))) {
no_local_space:
(void) fprintf(stderr,
"%s: no space for %d byte local name cache\n", Pn, len);
Exit(1);
}
} else {
/*
* Do setup for repeat calls.
*/
if (!iNch || !Nla || !Ncache)
return;
if (Nchash) {
(void) free((FREE_P *)Nchash);
Nchash = (struct l_nch **)NULL;
}
if (Ncache && Nlu) {
/*
* Free space malloc'd to names in local name cache.
*/
for (i = 0, lc = Ncache; i < Nlu; i++, lc++) {
if (lc->nm) {
(void) free((FREE_P *)lc->nm);
lc->nm = (char *)NULL;
}
}
}
Nch = iNch;
Nlu = 0;
}
/*
* Read the kernel's DNLC hash.
*/
if (kread(kha, (char *)khl, (Nch * sizeof(nc_hash_t)))) {
if (!Fwarn)
(void) fprintf(stderr,
"%s: WARNING: can't read DNLC hash: %s\n",
Pn, print_kptr(kha, (char *)NULL, 0));
iNch = Nch = 0;
return;
}
/*
* Build a local copy of the kernel name cache.
*/
for (i = n = 0, kh = khl, lc = Ncache; i < Nch; i++, kh++) {
/*
* Skip empty hash buckets.
*/
if (!kh->hash_next || ((KA_T)kh->hash_next == kha))
continue;
/*
* Process a hash bucket.
*/
for (kn = (KA_T)kh->hash_next, h = 0;
kn && (h < Nch) && (!h || (h && kn != (KA_T)kh->hash_next));
kn = (KA_T)nc->hash_next, h++)
{
if (kread(kn, (char *)nc, sizeof(ncache_t) + XNC))
break;
if (!nc->vp || (len = (int)nc->namlen) < 1)
continue;
if (NegVN && ((KA_T)nc->vp == NegVN))
continue;
if ((len < 3) && (nc->name[0] == '.')) {
if ((len < 2) || (nc->name[1] == '.'))
continue;
}
/*
* If not all the name has been read, read the rest of it,
* allocating more space at the end of the ncache structure as
* required.
*/
if (len > (XNC + 1)) {
if (len > (xn + 1)) {
while (len > (xn + 1))
xn = xn + xn;
xn = ((xn + 7) & ~7) - 1;
if (!(nc = (ncache_t *)realloc((MALLOC_P *)nc,
(sizeof(ncache_t) + xn)))
) {
(void) fprintf(stderr,
"%s: can't extend DNLC ncache_t buffer\n", Pn);
Exit(1);
}
}
cp = &nc->name[XNC + 1];
v = (KA_T)((char *)kn + nmo + XNC + 1);
xl = len - XNC - 1;
if (kread(v, cp, xl))
continue;
}
/*
* Allocate space for the name in the local name cache entry.
*/
if (!(cp = (char *)malloc(len + 1))) {
(void) fprintf(stderr,
"%s: can't allocate %d bytes for name cache name\n",
Pn, len + 1);
Exit(1);
}
(void) strncpy(cp, nc->name, len);
cp[len] = '\0';
/*
* Make sure there is space for another local name cache entry.
* If not, allocate twice as many entries.
*/
if (n >= Nla) {
Nla = Nla + Nla;
if (!(Ncache = (struct l_nch *)realloc(Ncache,
(MALLOC_S)(Nla * sizeof(struct l_nch))))
) {
(void) fprintf(stderr,
"%s: can't enlarge local name cache\n", Pn);
Exit(1);
}
lc = &Ncache[n];
}
/*
* Complete the local cache entry.
*/
lc->vp = (KA_T)nc->vp;
lc->dp = (KA_T)nc->dp;
lc->pa = (struct l_nch *)NULL;
lc->nm = cp;
lc->nl = len;
lc++;
n++;
}
}
/*
* Reduce memory usage, as required.
*/
if ((Nlu = n) < 1) {
/*
* No DNLC entries were located, an unexpected result.
*/
if (!RptTm && Ncache) {
/*
* If not in repeat mode, free the space that has been malloc'd
* to the local name cache.
*/
(void) free((FREE_P *)Ncache);
Ncache = (struct l_nch *)NULL;
Nla = Nlu = 0;
}
/*
* Issue a warning and disable furthe DNLC processing.
*/
if (!Fwarn)
(void) fprintf(stderr,
"%s: WARNING: unusable local name cache size: %d\n", Pn, n);
iNch = Nch = 0;
return;
}
if ((Nlu < Nla) && !RptTm) {
len = Nlu * sizeof(struct l_nch);
if (!(Ncache = (struct l_nch *)realloc(Ncache, len)))
goto no_local_space;
Nla = Nlu;
}
/*
* Build a hash table to locate Ncache entries.
*/
for (Nhl = 1; Nhl < Nlu; Nhl <<= 1)
;
Nhl <<= 1;
Mhl = Nhl - 1;
if (!(Nchash = (struct l_nch **)calloc(Nhl + Nlu,
sizeof(struct l_nch *))))
{
(void) fprintf(stderr,
"%s: no space for %d name cache hash pointers\n",
Pn, Nhl + Nlu);
Exit(1);
}
for (i = 0, lc = Ncache; i < Nlu; i++, lc++) {
for (hp = ncachehash(lc->vp), h = 1; *hp; hp++) {
if ((*hp)->vp == lc->vp && strcmp((*hp)->nm, lc->nm) == 0
&& (*hp)->dp == lc->dp
) {
h = 0;
break;
}
}
if (h)
*hp = lc;
}
/*
* Make a final pass through the local cache and convert parent vnode
* addresses to local name cache pointers.
*/
for (i = 0, lc = Ncache; i < Nlu; i++, lc++) {
if (!lc->dp)
continue;
if (NegVN && (lc->dp == NegVN)) {
lc->pa = (struct l_nch *)NULL;
continue;
}
lc->pa = ncache_addr(lc->dp);
}
}
/*
* 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;
struct l_nch *lc;
struct mounts *mtp;
int nl, rlen;
*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 to print the file system mount point name only.
*/
if (Lf->inp_ty == 1 && Lf->fs_ino && Lf->inode == Lf->fs_ino)
return(cp);
# endif /* defined(HASFSINO) */
/*
* Look up the name cache entry for the node address.
*/
if (!Nlu || !(lc = ncache_addr(Lf->na))) {
/*
* If the node has no cache entry, see if it's the mount
* point of a known file system.
*/
if (!Lf->fsdir || !Lf->dev_def || Lf->inp_ty != 1)
return((char *)NULL);
for (mtp = readmnt(); mtp; mtp = mtp->next) {
if (!mtp->dir || !mtp->inode)
continue;
if (Lf->dev == mtp->dev
&& mtp->inode == Lf->inode
&& strcmp(mtp->dir, Lf->fsdir) == 0)
return(cp);
}
return((char *)NULL);
}
/*
* Begin the path assembly.
*/
if ((nl = lc->nl) > (blen - 1))
return((char *)NULL);
cp = buf + blen - nl - 1;
rlen = blen - nl - 1;
(void) strcpy(cp, lc->nm);
/*
* Look up the name cache entries that are parents of the node address.
* Quit when:
*
* there's no parent;
* the name is too large to fit in the receiving buffer.
*/
for (;;) {
if (!lc->pa) {
if (ncache_isroot(lc->dp, cp))
*fp = 1;
break;
}
lc = lc->pa;
if (((nl = lc->nl) + 1) > rlen)
break;
*(cp - 1) = '/';
cp--;
rlen--;
(void) strncpy((cp - nl), lc->nm, nl);
cp -= nl;
rlen -= nl;
}
return(cp);
}
#endif /* defined(HASNCACHE) && solaris>=90000 */