/*
 * 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";
#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 "common.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 */

static void get_kernel_access(void);

#if HPUXV >= 800
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(ctx, v, (char *)&oftsz, sizeof(oftsz))) {
                (void)fprintf(stderr, "%s: can't get FD chunk size\n", Pn);
                Error(ctx);
            }
            if (!oftsz) {
                (void)fprintf(stderr, "%s: bad FD chunk size: %d\n", Pn, oftsz);
                Error(ctx);
            }
        }
        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);
            Error(ctx);
        }
        if (!(oftp = (char *)malloc((MALLOC_S)oftsz))) {
            (void)fprintf(stderr, "%s: no space for %d FD bytes\n", Pn, oftsz);
            Error(ctx);
        }
    }
#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(ctx, 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(ctx, 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(ctx, (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(ctx, LSOF_FD_CWD, -1);
            process_node((KA_T)CURDIR);
            if (Lf->sf)
                link_lfile();
        }
        /*
         * Save root directory information.
         */
        if (ROOTDIR) {
            alloc_lfile(ctx, LSOF_FD_ROOT_DIR, -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(ctx, (KA_T)pfp, (char *)&ofp, sizeof(ofp)) ||
                    !ofp || kread(ctx, (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(ctx, (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(ctx, LSOF_FD_NUMERIC, i);
                process_file(fp);
                if (Lf->sf) {

#if defined(USESPOFILE)
                    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));
            Error(ctx);
        }
        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);
            Error(ctx);
        }
    }
#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))
        Error(ctx);
#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));
        Error(ctx);
    }

#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))
        Error(ctx);
#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);
            Error(ctx);
        }
        (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);
        Error(ctx);
    }
    if (get_Nl_value("proc", Drive_Nl, &v) < 0 || !v ||
        kread(ctx, (KA_T)v, (char *)&Kp, sizeof(Kp)) ||
        get_Nl_value("nproc", Drive_Nl, &v) < 0 || !v ||
        kread(ctx, (KA_T)v, (char *)&Np, sizeof(Np)) || !Kp || Np < 1) {
        (void)fprintf(stderr, "%s: can't read proc table info\n", Pn);
        Error(ctx);
    }
    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);
        Error(ctx);
    }
    if (get_Nl_value("upt", Drive_Nl, (unsigned long *)&usrpt) < 0) {
        (void)fprintf(stderr, "%s: can't get kernel's usrpt\n", Pn);
        Error(ctx);
    }
#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);
        Error(ctx);
    }
    if (get_Nl_value("npids", Drive_Nl, &v) < 0 || !v ||
        kread(ctx, (KA_T)v, (char *)&npids, sizeof(npids))) {
        (void)fprintf(stderr, "%s: can't get kernel's npids\n", Pn);
        Error(ctx);
    }
#endif /* HPUXV<800 && defined(hp9000s800) */

#if HPUXV >= 1030
    if (get_Nl_value("clmaj", Drive_Nl, &v) < 0 || !v ||
        kread(ctx, (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(struct lsof_context *ctx, /* context */
          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(ctx, 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(ctx, prp, (char *)&p, sizeof(p)))
            return;
        if (kread(ctx, (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);
                Error(ctx);
            }
        }
        Vp[i++] = va;
        /*
         * Allocate local file structure.
         */
        switch (p.p_type) {
        case PT_DATA:
        case PT_TEXT:
            alloc_lfile(ctx, LSOF_FD_PROGRAM_TEXT, -1);
            break;
        case PT_MMAP:
            alloc_lfile(ctx, LSOF_FD_MEMORY, -1);
            break;
        default:
            alloc_lfile(ctx, LSOF_FD_PREGION_UNKNOWN, p.p_type);
        }
        /*
         * Save vnode information.
         */
        process_node(va);
        if (Lf->sf)
            link_lfile();
    }
}
#endif /* HPUXV>=800 */
