blob: 62ad04882af0a1082a49d85683bdd40ec9a271cb [file] [log] [blame]
/*
* dproc.c - /dev/kmem-based HP-UX process access 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: dproc.c,v 1.18 2008/10/08 13:24:36 abe Exp $";
#endif
#if defined(HPUXKERNBITS)
# if HPUXKERNBITS>=64
#define _INO_T
typedef int ino_t;
#define _TIME_T
typedef int time_t;
# else /* HPUXKERNBITS<64 */
#define _RLIM_T
# if !defined(__STDC_32_MODE__)
typedef unsigned long long rlim_t;
# else /* defined(__STDC_32_MODE__) */
typedef unsigned long rlim_t;
# endif /* !defined(__STDC_32_MODE__) */
# endif /* HPUXKERNBITS>=64 */
#endif /* defined(HPUXKERNBITS) */
#include "lsof.h"
#if defined(HASNCACHE)
#include <sys/dnlc.h>
#endif /* defined(HASNCACHE) */
#if HPUXV>=1010
/*
* HP doesn't include a definition for the proc structure in HP-UX 10.10
* or above in an attempt to force use of pstat(2). Unfortunately, pstat(2)
* doesn't return the information lsof needs. Hence, this private proc
* structure definition.
*/
#include <sys/vas.h>
#define SZOMB 3
# if HPUXV<1020
struct proc {
caddr_t d1[2]; /* dummy to occupy space */
caddr_t p_firstthreadp; /* thread pointer */
caddr_t d2[4]; /* dummy to occupy space */
int p_stat; /* process status */
caddr_t d3[9]; /* dummy to occupy space */
uid_t p_uid; /* UID */
caddr_t d4[2]; /* dummy to occupy space */
gid_t p_pgid; /* process group ID */
pid_t p_pid; /* PID */
pid_t p_ppid; /* parent PID */
caddr_t d5[9]; /* dummy to occupy space */
vas_t *p_vas; /* virtual address space */
caddr_t d6[16]; /* dummy to occupy space */
int p_maxof; /* max open files allowed */
struct vnode *p_cdir; /* current directory */
struct vnode *p_rdir; /* root directory */
struct ofile_t **p_ofilep; /* file descriptor chunks */
caddr_t d7[43]; /* dummy to occupy space */
};
# endif /* HPUXV<1020 */
# if HPUXV>=1020 && HPUXV<1030
struct proc {
caddr_t d1[2]; /* dummy to occupy space */
caddr_t p_firstthreadp; /* thread pointer */
caddr_t d2[6]; /* dummy to occupy space */
int p_stat; /* process status */
caddr_t d3[14]; /* dummy to occupy space */
uid_t p_uid; /* real UID */
uid_t p_suid; /* effective UID */
caddr_t d4; /* dummy to occupy space */
gid_t p_pgid; /* process group ID */
pid_t p_pid; /* PID */
pid_t p_ppid; /* parent PID */
caddr_t d5[9]; /* dummy to occupy space */
vas_t *p_vas; /* virtual address space */
caddr_t d6[16]; /* dummy to occupy space */
int p_maxof; /* max open files allowed */
struct vnode *p_cdir; /* current directory */
struct vnode *p_rdir; /* root directory */
struct ofile_t **p_ofilep; /* file descriptor chunks */
caddr_t d7[84]; /* dummy to occupy space */
};
# endif /* HPUXV>=1020 && HPUXV<1030 */
#endif /* HPUXV<1010 */
/*
* Local static values
*/
static KA_T Kp; /* kernel's process table address */
static int Np; /* number of kernel processes */
#if HPUXV>=800
static MALLOC_S Nva = 0; /* number of entries allocated to
* vnode address cache */
static KA_T *Vp = (KA_T *)NULL; /* vnode address cache */
#endif /* HPUXV>=800 */
_PROTOTYPE(static void get_kernel_access,(void));
#if HPUXV>=800
_PROTOTYPE(static void process_text,(KA_T vasp));
#endif /* HPUXV>=800 */
/*
* gather_proc_info() -- gather process information
*/
void
gather_proc_info()
{
KA_T fp;
int err, i, j;
#if HPUXV>=1020 && HPUXV<1100
struct ofile_t {
struct ofa {
KA_T ofile;
int d1;
int pofile;
} ofa[SFDCHUNK];
};
struct ofa *ofap;
int ofasz = (int)sizeof(struct ofa);
struct ofile_t oft;
char *oftp = (char *)&oft;
int oftsz = (int)sizeof(struct ofile_t);
#else /* HPUXV<1020 || HPUXV>=1100 */
# if HPUXV>=1100
struct ofa {
KA_T ofile;
int d1;
short d2;
char d3;
char pofile;
};
struct ofa *ofap;
int ofasz = (int)sizeof(struct ofa);
char *oftp = (char *)NULL;
int oftsz = (int)(sizeof(struct ofa) * SFDCHUNK);
KA_T v;
# endif /* HPUXV>=1100 */
#endif /* HPUXV>=1020 && HPUXV<1100 */
#if HPUXV>=800
char *c, *s;
KA_T pfp, ofp;
#if HPUXV<1020
struct ofile_t oft;
char *oftp = (char *)&oft;
int oftsz = (int)sizeof(struct ofile_t);
#endif /* HPUXV<1020 */
struct pst_status ps;
# if HPUXV<1010
struct user us;
# else /* HPUXV>=1010 */
struct user {
char u_comm[PST_CLEN];
} us;
# endif /* HPUXV<1010 */
#else /* HPUXV<800 */
int k;
long sw;
char us[U_SIZE]; /* must read HP-UX SWAP in DEV_BSIZE chunks */
# if defined(hp9000s300)
struct pte pte1, pte2;
KA_T pte_off, pte_addr;
# endif /* defined(hp9000s300) */
#endif /* HPUXV>=800 */
struct proc *p;
struct proc pbuf;
short pss, sf;
int px;
struct user *u;
#if defined(HASFSTRUCT)
# if HPUXV>=1020 || (HPUXV>=900 && HPUXV<1000)
#define USESPOFILE 1
long pof;
# endif /* HPUXV>=1020 || (HPUXV>=900 && HPUXV<1000) */
#endif /* defined(HASFSTRUCT) */
#if HPUXV>=1100
/*
* Define FD chunk size and pointer for HP-UX >= 11.
*/
if (!oftp) {
if ((get_Nl_value("chunksz", Drive_Nl, &v) >= 0) && v) {
if (kread(v, (char *)&oftsz, sizeof(oftsz))) {
(void) fprintf(stderr, "%s: can't get FD chunk size\n",
Pn);
Exit(1);
}
if (!oftsz) {
(void) fprintf(stderr, "%s: bad FD chunk size: %d\n",
Pn, oftsz);
Exit(1);
}
}
ofasz = (int)(oftsz / SFDCHUNK);
if (oftsz != (ofasz * SFDCHUNK)) {
(void) fprintf(stderr,
"%s: FD chunk size (%d) not exact multiple of %d\n",
Pn, oftsz, SFDCHUNK);
Exit(1);
}
if (!(oftp = (char *)malloc((MALLOC_S)oftsz))) {
(void) fprintf(stderr, "%s: no space for %d FD bytes\n",
Pn, oftsz);
Exit(1);
}
}
#endif /* HPUXV>=1100 */
/*
* Examine proc structures and their associated information.
*/
#if HPUXV>=800
u = &us;
(void) zeromem((char *)u, U_SIZE);
for (p = &pbuf, px = 0; px < Np; px++)
#else /* HPUXV<800 */
for (p = &pbuf, px = 0, u = (struct user *)us; px < Np; px++)
#endif /* HPUXV>=800 */
{
Kpa = Kp + (KA_T)(px * sizeof(struct proc));
if (kread(Kpa, (char *)&pbuf, sizeof(pbuf)))
continue;
if (p->p_stat == 0 || p->p_stat == SZOMB)
continue;
/*
* See if process is excluded.
*/
if (is_proc_excl(p->p_pid, (int)p->p_pgid, (UID_ARG)p->p_uid,
&pss, &sf))
continue;
#if HPUXV>=1010
/*
* Save the kernel thread pointer.
*/
Ktp = (KA_T)p->p_firstthreadp;
#endif /* HPUXV>=1010 */
/*
* Read the user area.
*/
#if HPUXV>=800
/*
* Use the pstat() syscall to read process status.
*/
if (pstat(PSTAT_PROC, &ps, sizeof(ps), 0, p->p_pid) != 1) {
if (!Fwarn)
(void) fprintf(stderr, "%s: can't pstat process %d: %s\n",
Pn, p->p_pid, strerror(errno));
continue;
}
/*
* Use the pst_cmd command buffer.
*/
c = ps.pst_cmd;
ps.pst_cmd[PST_CLEN - 1] = '\0'; /* paranoia */
/*
* Skip to the last component of the first path name. Also skip any
* leading `-', signifying a login shell. Copy the result to u_comm[].
*/
if (*c == '-')
c++;
for (s = c; *c && (*c != ' '); c++) {
if (*c == '/')
s = c + 1;
}
for (i = 0; i < MAXCOMLEN; i++) {
if (*s == '\0' || *s == ' ' || *s == '/')
break;
u->u_comm[i] = *s++;
}
u->u_comm[i] = '\0';
#else /* HPUXV<800 */
/*
* Read the user area from the swap file or memory.
*/
if ((p->p_flag & SLOAD) == 0) {
/*
* If the process is not loaded, read the user area from the swap
* file.
*/
if (Swap < 0)
continue;
sw = (long)p->p_swaddr;
# if defined(hp9000s800)
sw += (long)ctod(btoc(STACKSIZE * NBPG));
# endif /* defined(hp9000s800) */
if (lseek(Swap, (off_t)dtob(sw), L_SET) == (off_t)-1
|| read(Swap, u, U_SIZE) != U_SIZE)
continue;
} else {
/*
* Read the user area via the page table.
*/
# if defined(hp9000s300)
pte_off = (KA_T) &Usrptmap[btokmx(p->p_p0br) + p->p_szpt - 1];
if (kread(pte_off, (char *)&pte1, sizeof(pte1)))
continue;
pte_addr = (KA_T)(ctob(pte1.pg_pfnum + 1)
- ((UPAGES + FLOAT) * sizeof(pte2)));
if (mread(pte_addr, (char *)&pte2, sizeof(pte2)))
continue;
if (mread((KA_T)ctob(pte2.pg_pfnum), (char *)u,
sizeof(struct user)))
continue;
# endif /* defined(hp9000s300) */
# if defined(hp9000s800)
if (kread((KA_T)uvadd((struct proc *)Kpa), (char *)u,
sizeof(struct user)))
continue;
}
# endif /* defined(hp9000s800) */
#endif /* HPUXV>=800 */
/*
* Allocate a local process structure.
*/
if (is_cmd_excl(u->u_comm, &pss, &sf))
continue;
alloc_lproc(p->p_pid, (int)p->p_pgid, (int)p->p_ppid,
(UID_ARG)p->p_uid, u->u_comm, (int)pss, (int)sf);
Plf = (struct lfile *)NULL;
/*
* Save current working directory information.
*/
if (CURDIR) {
alloc_lfile(CWD, -1);
process_node((KA_T)CURDIR);
if (Lf->sf)
link_lfile();
}
/*
* Save root directory information.
*/
if (ROOTDIR) {
alloc_lfile(RTD, -1);
process_node((KA_T)ROOTDIR);
if (Lf->sf)
link_lfile();
}
#if HPUXV>=800
/*
* Print information on the text file.
*/
if (p->p_vas)
process_text((KA_T)p->p_vas);
#endif /* HPUXV>=800 */
/*
* Loop through user's files.
*/
#if HPUXV>=800
for (i = 0, j = SFDCHUNK, pfp = (KA_T)p->p_ofilep;
i < p->p_maxof;
i++)
#else /* HPUXV<800 */
for (i = j = k = 0;; i++)
#endif /* HPUXV>=800 */
{
#if HPUXV>=800
if (j >= SFDCHUNK) {
if (!pfp || kread((KA_T)pfp, (char *)&ofp, sizeof(ofp))
|| !ofp || kread((KA_T)ofp, oftp, oftsz))
break;
j = 0;
pfp += sizeof(KA_T);
# if HPUXV>=1020
ofap = (struct ofa *)oftp;
# endif /* HPUXV>=1020 */
}
j++;
# if HPUXV>=1020
# if defined(USESPOFILE)
pof = (long)ofap->pofile;
# endif /* defined(USESPOFILE) */
fp = (KA_T)ofap->ofile;
ofap = (struct ofa *)((char *)ofap + ofasz);
if (fp)
# else /* HPUXV<1020 */
# if defined(USESPOFILE)
pof = (long)oft.pofile[j - 1];
# endif /* defined(USESPOFILE) */
if ((fp = (KA_T)oft.ofile[j - 1]))
# endif /* HPUXV>=1020 */
#else /* HPUXV<800 */
if (j >= SFDCHUNK) {
/*
* Get next file pointer "chunk".
*/
while (++k < NFDCHUNKS && !u->u_ofilep[k])
;
if (k >= NFDCHUNKS)
break;
if (kread((KA_T)u->u_ofilep[k], (char *)&u->u_ofile,
sizeof(struct ofile_t)))
{
break;
}
j = 0;
}
j++;
if ((fp = (KA_T)u->u_ofile.ofile[j - 1]))
#endif /* HPUXV>=800 */
/*
* Process the file pointer.
*/
{
alloc_lfile(NULL, i);
process_file(fp);
if (Lf->sf) {
#if defined(USESPOFILE)
if (Fsv & FSV_FG)
Lf->pof = pof;
#endif /* defined(USESPOFILE) */
link_lfile();
}
}
}
/*
* Examine results.
*/
if (examine_lproc())
return;
}
}
/*
* get_kernel_access() - access the required information in the kernel
*/
static void
get_kernel_access()
{
KA_T v;
/*
* Check the kernel version.
*/
(void) ckkv("HP-UX", LSOF_VSTR, (char *)NULL, (char *)NULL);
#if HPUXV>=1030
/*
* See if build and run bit sizes match. Exit if they don't.
*/
{
long rv;
if ((rv = sysconf(_SC_KERNEL_BITS)) < 0) {
(void) fprintf(stderr,
"%s: sysconf(_SC_KERNEL_BITS) returns: %s\n",
Pn, strerror(errno));
Exit(1);
}
if (rv != (long)HPUXKERNBITS) {
(void) fprintf(stderr,
"%s: FATAL: %s was built for a %d bit kernel, but this\n",
Pn, Pn, HPUXKERNBITS);
(void) fprintf(stderr, " is a %ld bit kernel.\n", rv);
Exit(1);
}
}
#endif /* HPUXV>=1030 */
#if defined(HAS_AFS)
struct NLIST_TYPE *nl = (struct NLIST_TYPE *)NULL;
#endif /* defined(HAS_AFS) */
#if HPUXV<800
/*
* Open access to /dev/mem and SWAP.
*/
if ((Mem = open("/dev/mem", O_RDONLY, 0)) < 0) {
(void) fprintf(stderr, "%s: can't open /dev/mem: %s\n",
Pn, strerror(errno));
err = 1;
}
if (!Memory || strcmp(Memory, KMEM) == 0) {
if ((Swap = open(SWAP, O_RDONLY, 0)) < 0) {
(void) fprintf(stderr, "%s: %s: %s\n",
Pn, SWAP, strerror(errno));
err = 1;
}
}
#endif /* HPUXV<800 */
#if defined(WILLDROPGID)
/*
* 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 kernel memory access.
*/
if ((Kd = open(Memory ? Memory : KMEM, O_RDONLY, 0)) < 0) {
int errno_save = errno;
(void) fprintf(stderr, "%s: can't open ", Pn);
safestrprt(Memory ? Memory : KMEM, stderr, 0);
(void) fprintf(stderr, ": %s\n", strerror(errno_save));
Exit(1);
}
#if defined(WILLDROPGID)
/*
* Drop setgid permission, if necessary.
*/
if (!Memory)
(void) dropgid();
#else /* !defined(WILLDROPGID) */
/*
* See if the name list file is readable.
*/
if (Nmlst && !is_readable(Nmlst, 1))
Exit(1);
#endif /* defined(WILLDROPGID) */
(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
* module name list file.
*/
if (!(nl = (struct NLIST_TYPE *)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) */
/*
* Access kernel symbols.
*/
if (NLIST_TYPE(Nmlst ? Nmlst : N_UNIX, Nl) < 0) {
(void) fprintf(stderr, "%s: can't read namelist from: ", Pn);
safestrprt(Nmlst ? Nmlst : N_UNIX, stderr, 1);
Exit(1);
}
if (get_Nl_value("proc", Drive_Nl, &v) < 0 || !v
|| kread((KA_T)v, (char *)&Kp, sizeof(Kp))
|| get_Nl_value("nproc", Drive_Nl, &v) < 0 || !v
|| kread((KA_T)v, (char *)&Np, sizeof(Np))
|| !Kp || Np < 1) {
(void) fprintf(stderr, "%s: can't read proc table info\n", Pn);
Exit(1);
}
if (get_Nl_value("vfops", Drive_Nl, (KA_T *)&Vnfops) < 0)
Vnfops = (KA_T)NULL;
#if HPUXV<800 && defined(hp9000s300)
if (get_Nl_value("upmap", Drive_Nl, (unsigned long *)&Usrptmap) < 0) {
(void) fprintf(stderr, "%s: can't get kernel's Usrptmap\n", Pn);
Exit(1);
}
if (get_Nl_value("upt", Drive_Nl, (unsigned long *)&usrpt) < 0) {
(void) fprintf(stderr, "%s: can't get kernel's usrpt\n", Pn);
Exit(1);
}
#endif /* HPUXV<800 && defined(hp9000s300) */
#if HPUXV<800 && defined(hp9000s800)
proc = (struct proc *)Kp;
if (get_Nl_value("ubase", Drive_Nl, (unsigned long *)&ubase) < 0) {
(void) fprintf(stderr, "%s: can't get kernel's ubase\n", Pn);
Exit(1);
}
if (get_Nl_value("npids", Drive_Nl, &v) < 0 || !v
|| kread((KA_T)v, (char *)&npids, sizeof(npids))) {
(void) fprintf(stderr, "%s: can't get kernel's npids\n", Pn);
Exit(1);
}
#endif /* HPUXV<800 && defined(hp9000s800) */
#if HPUXV>=1030
if (get_Nl_value("clmaj", Drive_Nl, &v) < 0 || !v
|| kread((KA_T)v, (char *)&CloneMaj, sizeof(CloneMaj)))
HaveCloneMaj = 0;
else
HaveCloneMaj = 1;
#endif /* HPUXV>=1030 */
#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 module name list 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((FREE_P *)nl);
}
#endif /* defined(HAS_AFS) */
}
/*
* initialize() - perform all initialization
*/
void
initialize()
{
get_kernel_access();
}
/*
* 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 */
{
int br;
if (lseek(Kd, (off_t)addr, L_SET) == (off_t)-1L)
return(-1);
br = read(Kd, buf, len);
return((br == len) ? 0 : 1);
}
#if HPUXV<800
/*
* mread() -- read from /dev/mem
*/
static int
mread(addr, buf, len)
KA_T addr; /* /dev/mem address */
char *buf; /* buffer to receive data */
READLEN_T len; /* length to read */
{
int br;
if (lseek(Mem, addr, L_SET) == (off_t)-1L)
return(1);
br = read(Mem, buf, len);
return((br == len) ? 0 : 1);
}
#endif /* HPUXV<800 */
#if HPUXV>=800
/*
* process_text() - process text access information
*/
static void
process_text(vasp)
KA_T vasp; /* kernel's virtual address space
* pointer */
{
char fd[FDLEN];
int i, j, lm;
MALLOC_S len;
struct pregion p;
KA_T prp;
struct region r;
struct vas v;
KA_T va;
/*
* Read virtual address space pointer.
*/
if (kread(vasp, (char *)&v, sizeof(v)))
return;
/*
* Follow the virtual address space pregion structure chain.
*/
for (i = lm = 0, prp = (KA_T)v.va_next;
prp != vasp;
prp = (KA_T)p.p_next, lm++)
{
/*
* Avoid infinite loop.
*/
if (lm > 1000) {
if (!Fwarn)
(void) fprintf(stderr,
"%s: too many virtual address regions for PID %d\n",
Pn, Lp->pid);
return;
}
/*
* Read the pregion and region.
*/
if (kread(prp, (char *)&p, sizeof(p)))
return;
if (kread((KA_T)p.p_reg, (char *)&r, sizeof(r)))
return;
/*
* Skip file entries with no file pointers.
*/
if (!(va = (KA_T)r.r_fstore))
continue;
/*
* Skip entries whose vnodes have already been displayed.
*
* Record new, unique vnode pointers.
*/
for (j = 0; j < i; j++) {
if (Vp[j] == va)
break;
}
if (j < i)
continue;
if (i >= Nva) {
Nva += 10;
len = (MALLOC_S)(Nva * sizeof(KA_T));
if (!Vp)
Vp = (KA_T *)malloc(len);
else
Vp = (KA_T *)realloc((MALLOC_P *)Vp, len);
if (!Vp) {
(void) fprintf(stderr,
"%s: no more space for text vnode pointers\n", Pn);
Exit(1);
}
}
Vp[i++] = va;
/*
* Allocate local file structure.
*/
switch (p.p_type) {
case PT_DATA:
case PT_TEXT:
alloc_lfile(" txt", -1);
break;
case PT_MMAP:
alloc_lfile(" mem", -1);
break;
default:
(void) snpf(fd, sizeof(fd), "R%02d", p.p_type);
alloc_lfile(fd, -1);
}
/*
* Save vnode information.
*/
process_node(va);
if (Lf->sf)
link_lfile();
}
}
#endif /* HPUXV>=800 */