/*
 * dproc.c - AIX 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

#include "common.h"

static void get_kernel_access(struct lsof_context *ctx);

#if AIXA < 2
static struct le *getle(struct lsof_context *ctx, KA_T a, KA_T sid, char **err);
#endif /* AIXA<2 */

#if AIXV >= 4110
static void getlenm(struct lsof_context *ctx, struct le *le, KA_T sid);
#endif /* AIXV>=4110 */

static int kreadx(KA_T addr, char *buf, int len, KA_T sid);

#if AIXA < 2
static void process_text(struct lsof_context *ctx, KA_T sid);
#else  /* AIXA>=2 */
static void getsoinfo(void);
static void process_text(struct lsof_context *ctx, pid_t pid);
#endif /* AIXA<2 */

#if defined(SIGDANGER)
#    if defined(HASINTSIGNAL)
static int lowpgsp(struct lsof_context *ctx, int sig);
#    else  /* !defined(HASINTSIGNAL) */
static void lowpgsp(struct lsof_context *ctx, int sig);
#    endif /* defined(HASINTSIGNAL) */
#endif     /* defined(SIGDANGER) */

/*
 * Local definitions
 */

#if AIXV < 4300
#    define PROCINFO procinfo
#else /* AIXV>=4300 */
#    define PROCINFO_INCR 256
#    if AIXA < 1
#        define FDSINFO fdsinfo
#        define GETPROCS getprocs
#        define PROCINFO procsinfo
#    else /* AIXA>=1 */
#        define FDSINFO fdsinfo64
#        define GETPROCS getprocs64
#        define PROCINFO procentry64

#        if AIXA > 1
/*
 * AIX 5 and greater ia64 loader definitions
 */

#            include <sys/ldr.h>

#            define SOHASHBUCKS                                                \
                128 /* SoHash[] bucket count                                   \
                     * MUST BE A POWER OF 2!!! */
#            define SOHASH(d, n)                                               \
                ((((int)(((GET_MIN_DEV(d) & 0x7fffffff) * SOHASHBUCKS) + n) *  \
                   31415) >>                                                   \
                  7) &                                                         \
                 (SOHASHBUCKS - 1))

typedef struct so_hash {
    dev_t dev;            /* device (st_dev) */
    int nlink;            /* link count (st_nlink) */
    char *nm;             /* name (mi_name) */
    INODETYPE node;       /* node number (st_ino) */
    struct so_hash *next; /* next entry in hash bucket */
    SZOFFTYPE sz;         /* size (st_size) */
} so_hash_t;

so_hash_t **SoHash = (so_hash_t **)NULL;
#        endif /* AIXA>1 */
#    endif     /* AIXA<1 */
#endif         /* AIXV<4300 */

#define PROCSIZE sizeof(struct PROCINFO)

/*
 * Create the FDSINFOSIZE definition for allocating FDSINFO space.  (This
 * isn't as straightforward as it might seem, because someone made a bad
 * decision to change the struct fdsinfo* family at AIX 5.2.)
 */

#define FDSINFOSIZE sizeof(struct FDSINFO) /* (If we're lucky.) */

#if defined(OPEN_SHRT_MAX)
#    if OPEN_SHRT_MAX < OPEN_MAX
#        undef FDSINFOSIZE /* (We weren't lucky.) */
#        define FDSELEMSIZE (sizeof(struct FDSINFO) / OPEN_SHRT_MAX)
#        define FDSINFOSIZE (OPEN_MAX * FDSELEMSIZE)
#    endif /* OPEN_SHRT_MAX<OPEN_MAX */
#endif     /* defined(OPEN_SHRT_MAX) */

#if AIXV >= 4110
/*
 * Loader access definitions for AIX 4.1.1 and above.
 */

#    define LIBNMLN                                                            \
        40 /* maximum library table name                                       \
            * length */

#    define LIBMASK 0xf0000000 /* library table mask */
#    define LIBNMCOMP                                                          \
        0xd0000000 /* library table name has                                   \
                    * multiple components */
#    if AIXA < 1
#        define RDXMASK 0x0fffffff          /* kreadx() address mask */
#    else                                   /* AIXA>=1 */
#        define RDXMASK 0x0fffffffffffffff  /* kreadx() address mask */
#        define URDXMASK 0x0fffffff00000000 /* upper part of RDXMASK */
#    endif                                  /* AIXA<1 */
#endif                                      /* AIXV>=4110 */

/*
 * Loader structure definitions.  (AIX doesn't supply ld_data.h.)
 */

struct le { /* loader entry */

    struct le *next; /* next entry pointer */

#if AIXV < 4300
    ushort dummy1;
    ushort dummy2;
    uint dummy3;
    struct file *fp; /* file table entry pointer */

#    if AIXV >= 4110
    int ft; /* file type indicator */
    unsigned dummy4;
    char *dummy5;
    unsigned dummy6;
    char *dummy7[3];
    char *nm; /* name */
#    endif    /* AIXV>=4110 */
#else         /* AIXV>=4300 */
#    if AIXA < 2
    uint flags;
    struct file *fp; /* file table entry pointer */
    char *nm;        /* name */
#    else  /* AIXA>=2 */
    KA_T d1[2];
    KA_T nm; /* name */
    KA_T d2[10];
    struct file *fp; /* file table entry pointer */
#    endif /* AIXA<2 */
#endif     /* AIXV<4300 */
};

#if AIXV >= 4300
/*
 * The elements of interest from the AIX >= 4.3 loader anchor structure.
 */
struct la { /* loader anchor */

#    if AIXA < 2
    struct le *list;
    struct le *exec;
#    else  /* AIXA>=2 */
    KA_T exec;
    KA_T list;
#    endif /* AIXA<2 */
};
#endif /* AIXV>=4300 */

/*
 * Local static values
 */

static int Np = 0; /* number of processes */
static struct PROCINFO *P = (struct PROCINFO *)NULL;
/* the process table */
static struct user *Up; /* user structure */

#if AIXV >= 4110
#    if AIXA < 2
static KA_T Soff;  /* shared library VM offset */
int Soff_stat = 0; /* Soff-available status */
#    endif         /* AIXA<2 */
static KA_T Uo;    /* user area VM offset */
#endif             /* AIXV>=4110 */

/*
 * ckkv() - check kernel version
 */

void ckkv(struct lsof_context *ctx, /* context */
          char *d,                  /* dialect */
          char *er,                 /* expected release */
          char *ev,                 /* expected version */
          char *ea)                 /* expected architecture */
{

#if defined(HASKERNIDCK)
#    if AIXV < 5000

    /*
     * Use oslevel below AIX 5.
     */
    int br, p[2], pid;
    char buf[128], *cp;
    struct stat sb;

    if (Fwarn)
        return;
        /*
         * Make sure we can execute OSLEVEL.  If OSLEVEL doesn't exist and the
         * AIX version is below 4.1, return quietly.
         */

#        define OSLEVEL "oslevel"
#        define OSLEVELPATH "/usr/bin/oslevel"

    if (stat(OSLEVELPATH, &sb)) {

#        if AIXV < 4100
        if (errno == ENOENT)
            return;
#        endif /* AIXV<4100 */

        (void)fprintf(stderr, "%s: can't execute %s: %s\n", Pn, OSLEVELPATH,
                      strerror(errno));
        Error(ctx);
    }
    if ((sb.st_mode & (S_IROTH | S_IXOTH)) != (S_IROTH | S_IXOTH)) {
        (void)fprintf(stderr, "%s: can't execute %s, modes: %o\n", Pn,
                      OSLEVELPATH, sb.st_mode);
        Error(ctx);
    }
    /*
     * Open a pipe for receiving the version number from OSLEVEL.  Fork a
     * child to run OSLEVEL.  Retrieve the OSLEVEL output.
     */
    if (pipe(p)) {
        (void)fprintf(stderr, "%s: can't create pipe to: %s\n", Pn,
                      OSLEVELPATH);
        Error(ctx);
    }
    if ((pid = fork()) == 0) {
        (void)close(1);
        (void)close(2);
        (void)close(p[0]);
        dup2(p[1], 1);
        dup2(p[1], 2);
        (void)close(p[1]);
        execl(OSLEVELPATH, OSLEVEL, NULL);
        _exit(0);
    }
    if (pid < 0) {
        (void)fprintf(stderr, "%s: can't fork a child for %s: %s\n", Pn,
                      OSLEVELPATH, strerror(errno));
        Error(ctx);
    }
    (void)close(p[1]);
    br = read(p[0], buf, sizeof(buf) - 1);
    (void)close(p[0]);
    (void)wait(NULL);
    /*
     * Warn if the actual and expected versions don't match.
     */
    if (br > 0) {
        buf[br] = '\0';
        if ((cp = strrchr(buf, '\n')))
            *cp = '\0';
    } else
        (void)snpf(buf, sizeof(buf), "UNKNOWN");
#    else  /* AIXV>=5000 */

    /*
     * Use uname() for AIX 5 and above.
     */
    char buf[64];
    struct utsname u;

    if (Fwarn)
        return;

    (void)memset((void *)&u, 0, sizeof(u));
    (void)uname(&u);
    (void)snpf(buf, sizeof(buf) - 1, "%s.%s.0.0", u.version, u.release);
    buf[sizeof(buf) - 1] = '\0';
#    endif /* AIXV<5000 */
    if (!ev || strcmp(buf, ev))
        (void)fprintf(stderr,
                      "%s: WARNING: compiled for %s version %s; this is %s.\n",
                      Pn, d, ev ? ev : "UNKNOWN", buf);
#endif /* defined(HASKERNIDCK) */
}

/*
 * gather_proc_info() - gather process information
 */

void gather_proc_info(struct lsof_context *ctx) {
    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 */
    KA_T cdir, fp, pdir, rdir;
    char *cmd;
    int hl, i, nf, np;
    struct PROCINFO *p;
    short pss, sf;
    struct user us;

#if AIXV >= 4300
    static struct FDSINFO *fds = (struct FDSINFO *)NULL;
    MALLOC_S msz;
#    if AIXA == 1
    pid32_t pid; /* Since we're operating with types defined
                  * under _KERNEL (see machine.), but
                  * getprocs64() expects application types
                  * (where pid_t is 32 bits), the pid variable
                  * must be cast in an application-compatible
                  * manner.
                  */
#    else        /* AIXA!=1 */
    pid_t pid;
#    endif       /* AIXA==1 */
#    if AIXV == 4330
    static int trx = 0;
    unsigned int mxof;
    static int uo = 0;
#    endif /* AIXV==4330 */
#endif     /* AIXV>=4300 */

    /*
     * 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.
     */

#if AIXV < 4300
    if (!P) {
        if (!(P = (struct PROCINFO *)malloc((MALLOC_S)PROCSIZE))) {
            (void)fprintf(stderr, "%s: can't allocate space for 1 proc\n", Pn);
            Error(ctx);
        }
        Np = 1;
    }
    while (((np = getproc(P, Np, PROCSIZE)) == -1) && errno == ENOSPC) {
        Np = P->p_pid + 10;
        if (!(P = (struct PROCINFO *)realloc((MALLOC_P *)P,
                                             (size_t)(Np * PROCSIZE)))) {
            (void)fprintf(stderr, "%s: no space for %d procinfo's\n", Pn, Np);
            Error(ctx);
        }
    }
#else  /* AIXV>=4300 */
    if (!P) {
        msz = (MALLOC_S)(PROCSIZE * PROCINFO_INCR);
        if (!(P = (struct PROCINFO *)malloc(msz))) {
            (void)fprintf(stderr, "%s: can't allocate space for %d procs\n", Pn,
                          PROCINFO_INCR);
            Error(ctx);
        }
        Np = PROCINFO_INCR;
    }
    np = pid = 0;
    p = P;
    while ((i = GETPROCS(p, PROCSIZE, (struct FDSINFO *)NULL, 0, &pid,
                         PROCINFO_INCR)) == PROCINFO_INCR) {
        np += PROCINFO_INCR;
        if (np >= Np) {
            msz = (MALLOC_S)(PROCSIZE * (Np + PROCINFO_INCR));
            if (!(P = (struct PROCINFO *)realloc((MALLOC_P *)P, msz))) {
                (void)fprintf(stderr, "%s: no more space for proc storage\n",
                              Pn);
                Error(ctx);
            }
            Np += PROCINFO_INCR;
        }
        p = (struct PROCINFO *)((char *)P + (np * PROCSIZE));
    }
    if (i > 0)
        np += i;
#endif /* AIXV<4300 */

    /*
     * Loop through processes.
     */
    for (p = P, Up = &us; np > 0; np--, p++) {
        if (p->p_stat == 0 || p->p_stat == SZOMB)
            continue;
        if (is_proc_excl(ctx, p->p_pid, (int)p->p_pgid, (UID_ARG)p->p_uid, &pss,
                         &sf))
            continue;

#if AIXV < 4300
            /*
             * Get user structure for AIX < 4.3.
             *
             * If AIX version is below 4.1.1, use getuser().
             *
             * If AIX version is 4.1.1 or above: if readx() is disabled (no -X
             * option, use  getuser(); if readx() is enabled (-X), use readx().
             */

#    if AIXV >= 4110
        if (Fxopt && kreadx(Uo, (char *)Up, U_SIZE, (KA_T)p->pi_adspace) == 0)
            i = 1;
        else
            i = 0;
        if (i == 0) {
            if (getuser(p, PROCSIZE, Up, U_SIZE) != 0)
                continue;
        }
        hl = i;
#    else  /* AIXV<4110 */
        if (getuser(p, PROCSIZE, Up, U_SIZE) != 0)
            continue;
        hl = 1;
#    endif /* AIXV>=4110 */
           /*
            * Save directory vnode addresses, command name address, and open file
            * count from user structure.
            *
            * Skip processes excluded by the user structure command name.
            */
        cdir = (KA_T)Up->u_cdir;

#    if AIXV < 4100
        pdir = (KA_T)Up->u_pdir;
#    endif /* AIXV<4100 */

        rdir = (KA_T)Up->u_rdir;
        cmd = Up->u_comm;
        nf = Up->u_maxofile;
        if (is_cmd_excl(ctx, cmd, &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;
        }

#else /* AIXV>=4300 */
        /*
         * For AIX 4.3 and above, skip processes excluded by the procsinfo
         * command name.  Use getprocs() to get the file descriptors for
         * included processes.
         *
         * If readx is enabled (-X), use it to get the loader_anchor structure.
         */
        if (is_cmd_excl(ctx, p->pi_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;
        }
        if (!fds) {
            if (!(fds = (struct FDSINFO *)malloc((MALLOC_S)FDSINFOSIZE))) {
                (void)fprintf(stderr,
                              "%s: can't allocate fdsinfo struct for PID %d\n",
                              Pn, pid);
                Error(ctx);
            }
        }
        pid = p->p_pid;
        if (GETPROCS((struct PROCINFO *)NULL, PROCSIZE, fds, FDSINFOSIZE, &pid,
                     1) != 1)
            continue;
        hl = 0;

#    if AIXV == 4330
        /*
         * Handle readx() for AIX 4.3.3 specially, because 4.3.3 was released
         * with two different user struct definitions in <sys/user.h> and
         * their form affects using readx() to get the loader table pointers
         * from U_loader of the user structure (when -X is specified).
         */
        if (Fxopt) {
            for (;;) {

                /*
                 * Read the AIX 4.3.3 U_loader pointers.
                 */
                if (kreadx((KA_T)((char *)Uo + offsetof(struct user, U_loader) +
                                  uo),
                           (char *)&Up->U_loader, sizeof(struct la),
                           (KA_T)p->pi_adspace))
                    break;
                if (trx) {
                    hl = 1;
                    break;
                }
                /*
                 * Until the correct size of the U_loader offset in lo has been
                 * established, read U_maxofile and match it to pi_maxofile
                 * from the PROCINFO structure.  Try the offsets 0, 48, and
                 * -48.  Note: these offsets are heuristic attempts to adjust
                 * to differences in the user struct as observed on two systems
                 * whose <sys/user.h> header files differed.  U_maxofile
                 * follows U_loader by the same number of elements in both
                 * user structs, so the U_loader offset should be the same as
                 * the U_maxofile offset.
                 */
                if (!kreadx((KA_T)((char *)Uo +
                                   offsetof(struct user, U_maxofile) + uo),
                            (char *)&mxof, sizeof(mxof), (KA_T)p->pi_adspace) &&
                    (mxof == p->pi_maxofile)) {
                    hl = trx = 1;
                    break;
                }
                if (uo == 0)
                    uo = 48;
                else if (uo == 48)
                    uo = -48;
                else {
                    Fxopt = hl = 0;
                    trx = 1;
                    if (!Fwarn) {
                        (void)fprintf(stderr,
                                      "%s: WARNING: user struct mismatch;", Pn);
                        (void)fprintf(stderr, " -X option disabled.\n");
                    }
                    break;
                }
            }
        }
#    else  /* AIXV!=4330 */
        if (Fxopt &&
            kreadx((KA_T)((char *)Uo + offsetof(struct user, U_loader)),
                   (char *)&Up->U_loader, sizeof(struct la),
                   (KA_T)p->pi_adspace) == 0)
            hl = 1;
#    endif /* AIXV==4330 */

        /*
         * Save directory vnode addresses, command name, and open file count
         * from procinfo structure.
         */
        cdir = (KA_T)p->pi_cdir;
        pdir = (KA_T)NULL;
        rdir = (KA_T)p->pi_rdir;
        cmd = p->pi_comm;
        nf = p->pi_maxofile;
#endif     /* AIXV<4300 */

        /*
         * Allocate a local process structure and start filling it.
         */
        alloc_lproc(ctx, p->p_pid, (int)p->p_pgid, (int)p->p_ppid,
                    (UID_ARG)p->p_uid, cmd, (int)pss, (int)sf);
        Plf = (struct lfile *)NULL;
        /*
         * Save current working directory information.
         */
        if (!ckscko && cdir) {
            alloc_lfile(ctx, LSOF_FD_CWD, -1);
            process_node(ctx, cdir);
            if (Lf->sf)
                link_lfile(ctx);
        }
        /*
         * Save root directory information.
         */
        if (!ckscko && rdir) {
            alloc_lfile(ctx, LSOF_FD_ROOT_DIR, -1);
            process_node(ctx, rdir);
            if (Lf->sf)
                link_lfile(ctx);
        }

#if AIXV < 4100
        /*
         * Save parent directory information.
         */
        if (!ckscko && pdir) {
            alloc_lfile(ctx, LSOF_FD_PARENT_DIR, -1);
            process_node(ctx, pdir);
            if (Lf->sf)
                link_lfile(ctx);
        }
#endif /* AIXV<4100 */

        /*
         * Save information on text files.
         */
        if (!ckscko && hl) {

#if AIXA < 2
#    if AIXA < 1
            process_text((KA_T)p->pi_adspace);
#    else  /* AIXA==1 */
            {
                int ck = 1;
                KA_T sid = (KA_T)p->pi_adspace;

                if ((Up->U_loader[0] & URDXMASK) ||
                    (Up->U_loader[1] & URDXMASK)) {

                    /*
                     * If the upper part of either loader map address is
                     * non-zero and this is not the lsof process, skip the
                     * processing of text files.  If this is the lsof process,
                     * set the segment address to zero, forcing text file
                     * information to come from kmem rather than mem.
                     */
                    if (Mypid == p->p_pid)
                        sid = (KA_T)0;
                    else
                        ck = 0;
                }
                if (ck)
                    process_text(ctx, sid);
            }
#    endif /* AIXA<1 */
#else      /* AIXA>=2 */
            process_text(ctx, p->p_pid);
#endif     /* AIXA<2 */
        }
        /*
         * Save information on file descriptors.
         */
        for (i = 0; i < nf; i++) {

#if AIXV < 4300
            fp = (KA_T)Up->u_ufd[i].fp;
#else  /* AIXV>=4300 */
            fp = (KA_T)fds->pi_ufd[i].fp;
#endif /* AIXV<4300 */

            if (fp) {
                alloc_lfile(ctx, LSOF_FD_NUMERIC, i);
                process_file(ctx, fp);
                if (Lf->sf) {

#if defined(HASFSTRUCT)

#    if AIXV < 4300
                    Lf->pof = (long)(Up->u_ufd[i].flags & 0x7f);
#    else  /* AIXV>=4300 */
                    Lf->pof = (long)(fds->pi_ufd[i].flags & 0x7f);
#    endif /* AIXV<4300 */
#endif     /* defined(HASFSTRUCT) */

                    link_lfile(ctx);
                }
            }
        }
        /*
         * Examine results.
         */
        if (examine_lproc(ctx))
            return;
    }
}

/*
 * get_kernel_access() - get access to kernel memory
 */

static void get_kernel_access(struct lsof_context *ctx) {
    int oe = 0;

#if defined(AIX_KERNBITS)
    int kb;
    char *kbb, *kbr;
    /*
     * Check the kernel bit size against the size for which this executable was
     * configured.
     */
    if (__KERNEL_32()) {
        kb = 32;
        kbr = "32";
    } else if (__KERNEL_64()) {
        kb = 64;
        kbr = "64";
    } else {
        kb = 0;
        kbr = "unknown";
    }
    if ((AIX_KERNBITS == 0) || !kb || (kb != AIX_KERNBITS)) {
        if (AIX_KERNBITS == 32)
            kbb = "32";
        else if (AIX_KERNBITS == 64)
            kbb = "64";
        else
            kbb = "unknown";
        (void)fprintf(stderr,
                      "%s: FATAL: compiled for a kernel of %s bit size.\n", Pn,
                      kbb);
        (void)fprintf(stderr, "      The bit size of this kernel is %s.\n",
                      kbr);
        Error(ctx);
    }
#endif /* defined(AIX_KERNBITS) */

    /*
     * Access /dev/mem.
     */
    if ((Km = open("/dev/mem", O_RDONLY, 0)) < 0) {
        (void)fprintf(stderr, "%s: can't open /dev/mem: %s\n", Pn,
                      strerror(errno));
        oe++;
    }

#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(ctx);
#else  /* !defined(WILLDROPGID) */
    /*
     * See if the non-KMEM memory file is readable.
     */
    if (Memory && !is_readable(ctx, Memory, 1))
        Error(ctx);
#endif /* defined(WILLDROPGID) */

    /*
     * Access kernel memory file.
     */
    if ((Kd = open(Memory ? Memory : KMEM, O_RDONLY, 0)) < 0) {
        (void)fprintf(stderr, "%s: can't open %s: %s\n", Pn,
                      Memory ? Memory : KMEM, strerror(errno));
        oe++;
    }
    if (oe)
        Error(ctx);

#if defined(WILLDROPGID)
    /*
     * Drop setgid permission, if necessary.
     */
    if (!Memory)
        (void)dropgid(ctx);
#endif /* defined(WILLDROPGID) */

    /*
     * Get kernel symbols.
     */
    if (knlist(Nl, X_NL_NUM, sizeof(struct nlist)) || !Nl[X_UADDR].n_value) {
        (void)fprintf(stderr, "%s: can't get kernel's %s address\n", Pn,
                      Nl[X_UADDR].n_name);
        Error(ctx);
    }

#if defined(HAS_AFS)
    (void)knlist(AFSnl, X_AFSNL_NUM, sizeof(struct nlist));
#endif /* defined(HAS_AFS) */

#if AIXV >= 4110
    /*
     * Get user area and shared library VM offsets for AIX 4.1.1 and above.
     */
    if (Fxopt) {
        struct ublock *ub;

#    if AIXA < 2
        struct nlist ll[] = {
            {"library_anchor"},

#        if AIXV >= 4330
            {"library_le_handle"},
#        else  /* AIXV<4330 */
            {"library_data_handle"},
#        endif /* AIXV>=4330 */

            {(char *)NULL}
        };

        if (nlist(N_UNIX, ll) == 0 && ll[0].n_value != (long)0 &&
            ll[1].n_value != (long)0 &&
            kreadx((KA_T)(ll[1].n_value & RDXMASK), (char *)&Soff, sizeof(Soff),
                   (KA_T)0) == 0)
            Soff_stat++;
#    endif /* AIXA<2 */

        ub = (struct ublock *)Nl[X_UADDR].n_value;
        Uo = (KA_T)((KA_T)&ub->ub_user & RDXMASK);
    }
#endif /* AIXV>=4110 */

    /*
     * Check the kernel version number.
     */
    (void)ckkv(ctx, "AIX", (char *)NULL, LSOF_VSTR, (char *)NULL);

#if defined(SIGDANGER)
    /*
     * If SIGDANGER is defined, enable its handler.
     */
    (void)signal(SIGDANGER, lowpgsp);
#endif /* defined(SIGDANGER) */
}

#if AIXA < 2
/*
 * getle() - get loader entry structure
 */

static struct le *getle(struct lsof_context *ctx, /* context */
                        KA_T a,     /* loader entry kernel address */
                        KA_T sid,   /* user structure segment ID */
                        char **err) /* error message (if return is NULL) */
{
    static struct le le;

#    if AIXV < 4110
    if (a < Nl[X_UADDR].n_value) {
        *err = "address too small";
        return ((struct le *)NULL);
    }
    if (((char *)a + sizeof(le)) <= ((char *)Nl[X_UADDR].n_value + U_SIZE))
        return ((struct le *)((char *)Up + a - Nl[X_UADDR].n_value));
#    endif /* AIXV<4110 */

    if (!Fxopt) {
        *err = "readx() disabled for Stale Segment ID bug (see -X)";
        return ((struct le *)NULL);
    }

#    if AIXV >= 4110
    if (!sid) {
        if (!kread(ctx, a, (char *)&le, sizeof(le)))
            return (&le);
    } else {
        if (!kreadx((KA_T)(a & RDXMASK), (char *)&le, sizeof(le), (KA_T)sid))
            return (&le);
    }
#    else  /* AIXV<4110 */
    if (!kreadx((KA_T)a, (char *)&le, sizeof(le), (KA_T)sid))
        return (&le);
#    endif /* AIXV>=4110 */

getle_err:

    *err = "can't readx()";
    return ((struct le *)NULL);
}
#endif /* AIXA<2 */

#if AIXV >= 4110
/*
 * getlenm() - get loader entry file name for AIX >= 4.1.1
 */

static void getlenm(struct lsof_context *ctx, /* context */
                    struct le *le,            /* loader entry structure */
                    KA_T sid)                 /* segment ID */
{
    char buf[LIBNMLN];
    int i;

#    if AIXV < 4300
    if ((le->ft & LIBMASK) != LIBNMCOMP)
        return;
#    else /* AIXV>=4300 */
#        if AIXA < 2
    if (!sid) {
        if (kread(ctx, (KA_T)le->nm, buf, LIBNMLN))
            return;
    } else {
        if (!Soff_stat || !le->nm ||
            kreadx((KA_T)le->nm & (KA_T)RDXMASK, buf, LIBNMLN, (KA_T)Soff))
            return;
    }
    buf[LIBNMLN - 1] = '\0';
    i = strlen(buf);
    if (i < (LIBNMLN - 3) && buf[i + 1])
        enter_nm(ctx, &buf[i + 1]);
    else if (buf[0])
        enter_nm(ctx, buf);
#        else  /* AIXA>=2 */
    if (!le->nm || kread(ctx, le->nm, buf, sizeof(buf)))
        return;
    buf[LIBNMLN - 1] = '\0';
    if (!strlen(buf))
        return;
    enter_nm(ctx, buf);
#        endif /* AIXA<2 */
#    endif     /* AIXV<4300 */
}
#endif /* AIXV>=4110 */

#if AIXA > 1
/*
 * getsoinfo() - get *.so information for ia64 AIX >= 5
 */

static void getsoinfo() {
    char buf[65536];
    uint bufsz = (uint)sizeof(buf);
    int ct, h;
    char *ln = (char *)NULL;
    char *rn = (char *)NULL;
    LDR_Mod_info_t *lp;
    struct stat sb;
    so_hash_t *sp;
    /*
     * See if loader information is needed.  Warn if this process has
     * insufficient permission to acquire it from all processes.
     */
    if (!Fxopt)
        return;
    if ((Myuid != 0) && !Setuidroot && !Fwarn) {
        (void)fprintf(stderr,
                      "%s: WARNING: insufficient permission to access all", Pn);
        (void)fprintf(stderr, " /%s/object sub-\n", HASPROCFS);
        (void)fprintf(stderr, "      directories; some loader information may",
                      Pn);
        (void)fprintf(stderr, " be unavailable.\n");
    }
    /*
     * Get the loader module table.  Allocate hash space for it.
     */
    if ((ct = ldr_get_modules(SOL_GLOBAL, (void *)buf, &bufsz)) < 1)
        return;
    if (!(SoHash = (so_hash_t **)calloc((MALLOC_S)SOHASHBUCKS,
                                        sizeof(so_hash_t *)))) {
        (void)fprintf(stderr, "%s: no space for *.so hash buckets\n", Pn);
        Error(ctx);
    }
    /*
     * Cache the loader module information, complete with stat(2) results.
     */
    for (lp = (LDR_Mod_info_t *)buf; ct; ct--, lp++) {

        /*
         * Release previous name space allocations.
         */
        if (ln) {
            (void)free((MALLOC_P *)ln);
            ln = (char *)NULL;
        }
        if (rn) {
            (void)free((MALLOC_P *)rn);
            rn = (char *)NULL;
        }
        /*
         * Make a copy of the loader module name.
         */
        if (!(rn = mkstrcpy(lp->mi_name, (MALLOC_S *)NULL))) {
            (void)fprintf(stderr, "%s: no space for name: %s\n", Pn,
                          lp->mi_name);
            Error(ctx);
        }
        /*
         * Resolve symbolic links.
         */
        ln = Readlink(rn);
        if (ln == rn)
            rn = (char *)NULL;
        /*
         * Get stat(2) information.
         */
        if (statsafely(ln, &sb)) {
            if (!Fwarn)
                (void)fprintf(stderr, "%s: WARNING: can't stat: %s\n", Pn, ln);
            continue;
        }
        /*
         * Allocate and fill a loader information hash structure.
         */
        if (!(sp = (so_hash_t *)malloc((MALLOC_S)sizeof(so_hash_t)))) {
            (void)fprintf(stderr, "%s: no space for *.so hash entry: %s\n", Pn,
                          ln);
            Error(ctx);
        }
        sp->dev = sb.st_dev;
        sp->nlink = (int)sb.st_nlink;
        sp->nm = ln;
        ln = (char *)NULL;
        sp->node = (INODETYPE)sb.st_ino;
        sp->sz = (SZOFFTYPE)sb.st_size;
        /*
         * Link the structure to the appropriate hash bucket.
         */
        h = SOHASH(sb.st_dev, (INODETYPE)sb.st_ino);
        if (SoHash[h])
            sp->next = SoHash[h];
        else
            sp->next = (so_hash_t *)NULL;
        SoHash[h] = sp;
    }
    /*
     * Free any unused name space that was allocated.
     */
    if (ln)
        (void)free((MALLOC_P *)ln);
    if (rn)
        (void)free((MALLOC_P *)rn);
}
#endif /* AIXA>1 */

/*
 * initialize() - perform all initialization
 */

void initialize(struct lsof_context *ctx) {
    get_kernel_access(ctx);

#if AIXA > 1
    (void)getsoinfo();
#endif /* AIXA>1 */
}

/*
 * 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 AIXV < 4200
    if (lseek(Kd, (off_t)addr, L_SET) == (off_t)-1)
#else  /* AIXV>=4200 */
    if (lseek64(Kd, (off64_t)addr, L_SET) == (off64_t)-1)
#endif /* AIXV<4200 */

        return (1);
    br = read(Kd, buf, len);
    return ((br == len) ? 0 : 1);
}

/*
 * kreadx() - read kernel segmented memory
 */

int kreadx(KA_T addr, /* kernel address */
           char *buf, /* destination buffer */
           int len,   /* length */
           KA_T sid)  /* segment ID */
{
    int br;

#if AIXV < 4200
    if (lseek(Km, addr, L_SET) == (off_t)-1)
#else  /* AIXV>=4200 */
    if (lseek64(Km, (off64_t)addr, L_SET) == (off64_t)-1)
#endif /* AIXV<4200 */

        return (1);
    br = readx(Km, buf, len, sid);
    return (br == len ? 0 : 1);
}

#if defined(SIGDANGER)
/*
 * lowpgsp() - hangle a SIGDANGER signal about low paging space
 */

#    if defined(HASINTSIGNAL)
static int
#    else  /* !defined(HASINTSIGNAL) */
static void
#    endif /* defined(HASINTSIGNAL) */

lowpgsp(struct lsof_context *ctx, int sig) {
    (void)fprintf(stderr, "%s: FATAL: system paging space is low.\n", Pn);
    Error(ctx);
}
#endif /* defined(SIGDANGER) */

#if AIXA < 2
/*
 * process_text() - process text file information for non-ia64 AIX
 */

static void process_text(struct lsof_context *ctx, /* context */
                         KA_T sid)                 /* user area segment ID */
{
    char *err, fd[8];
    static struct file **f = (struct file **)NULL;
    int i, j, n;
    struct le *le;
    KA_T ll;
    MALLOC_S msz;
    static MALLOC_S nf = 0;
    struct file *xf = (struct file *)NULL;

#    if AIXV >= 4300
    struct la *la = (struct la *)&Up->U_loader;
#    endif /* AIXV>=4300 */

    /*
     * Display information on the exec'd entry.
     */

#    if AIXV < 4300
    if ((ll = (KA_T)Up->u_loader[1]))
#    else  /* AIXV>=4300 */
    if ((ll = (KA_T)la->exec))
#    endif /* AIXV<4300 */

    {
        alloc_lfile(ctx, LSOF_FD_PROGRAM_TEXT, -1);
        if ((le = getle(ctx, ll, sid, &err))) {
            if ((xf = le->fp)) {
                process_file(ctx, (KA_T)xf);
                if (Lf->sf) {

#    if AIXV >= 4110 && AIXV < 4300
                    if (!Lf->nm || !Lf->nm[0])
                        getlenm(le, sid);
#    endif /* AIXV>=4110 && AIXV<4300 */

                    link_lfile(ctx);
                }
            }
        } else {
            (void)snpf(Namech, Namechl, "text entry at %s: %s",
                       print_kptr((KA_T)ll, (char *)NULL, 0), err);
            enter_nm(ctx, Namech);
            if (Lf->sf)
                link_lfile(ctx);
        }
    }
    /*
     * Display the loader list.
     */
    for (i = n = 0,

#    if AIXV < 4300
        ll = (KA_T)Up->u_loader[0];
#    else  /* AIXV>=4300 */
        ll = (KA_T)la->list;
#    endif /* AIXV<4300 */

         ll; ll = (KA_T)le->next) {
        alloc_lfile(ctx, LSOF_FD_LIBRARY_REF, i);
        if (!(le = getle(ctx, ll, sid, &err))) {
            (void)snpf(Namech, Namechl, "loader entry at %s: %s",
                       print_kptr((KA_T)ll, (char *)NULL, 0), err);
            enter_nm(ctx, Namech);
            if (Lf->sf)
                link_lfile(ctx);
            return;
        }
        /*
         * Skip entries with no file pointers, the exec'd file, and entries
         * that have already been processed.
         */
        if (!le->fp || (le->fp == xf))
            continue;
        for (j = 0; j < n; j++) {
            if (f[j] == le->fp)
                break;
        }
        if (j < n)
            continue;
        if (n >= nf) {

            /*
             * Allocate file structure address cache space.
             */
            nf += 10;
            msz = (MALLOC_S)(nf * sizeof(struct file *));
            if (f)
                f = (struct file **)realloc((MALLOC_P *)f, msz);
            else
                f = (struct file **)malloc(msz);
            if (!f) {
                (void)fprintf(stderr, "%s: no space for text file pointers\n",
                              Pn);
                Error(ctx);
            }
        }
        f[n++] = le->fp;
        /*
         * Save the loader entry.
         */
        process_file(ctx, (KA_T)le->fp);
        if (Lf->sf) {

#    if AIXV >= 4110
            if (!Lf->nm || !Lf->nm[0])
                getlenm(ctx, le, sid);
#    endif /* AIXV>=4110 */

            link_lfile(ctx);
            i++;
        }
    }
}
#else /* AIXA>=2 */
/*
 * process_text() - process text file information for ia64 AIX >= 5
 */

static void process_text(pid_t pid) /* process PID */
{
    char buf[MAXPATHLEN + 1], fd[8], *nm, *pp;
    size_t bufl = sizeof(buf);
    DIR *dfp;
    struct dirent *dp;
    int i;
    struct la *la = (struct la *)&Up->U_loader;
    struct le le;
    struct lfile *lf;
    struct stat sb;
    so_hash_t *sp;
    size_t sz;
    dev_t xdev;
    INODETYPE xnode;
    int xs = 0;
    /*
     * Display information on the exec'd entry.
     */
    if (la->exec && !kread(ctx, (KA_T)la->exec, (char *)&le, sizeof(le)) &&
        le.fp) {
        alloc_lfile(ctx, LSOF_FD_PROGRAM_TEXT, -1);
        process_file(ctx, (KA_T)le.fp);
        if (Lf->dev_def && (Lf->inp_ty == 1)) {
            xdev = Lf->dev;
            xnode = Lf->inode;
            xs = 1;
        }
        if (Lf->sf) {
            if (!Lf->nm || !Lf->nm[0])
                getlenm(&le, (KA_T)0);
            link_lfile(ctx);
        }
    }
    /*
     * Collect devices and names for the entries in /HASPROCFS/PID/object -- the
     * AIX 5 loader list equivalent.  When things fail in this processing --
     * most likely for insufficient permissions -- be silent; a warning was
     * issued by getsoinfo().
     */
    (void)snpf(buf, bufl, "/%s/%ld/object", HASPROCFS, (long)pid);
    if (!(dfp = opendir(buf)))
        return;
    if ((sz = strlen(buf)) >= bufl)
        return;
    buf[sz++] = '/';
    pp = &buf[sz];
    sz = bufl - sz;
    /*
     * Read the entries in the /HASPROCFS/PID/object subdirectory.
     */
    for (dp = readdir(dfp), i = 0; dp; dp = readdir(dfp)) {

        /*
         * Skip '.', "..", entries with no node number, and entries whose
         * names are too long.
         */
        if (!dp->d_ino || (dp->d_name[0] == '.'))
            continue;
        if ((dp->d_namlen + 1) >= sz)
            continue;
        (void)strncpy(pp, dp->d_name, dp->d_namlen);
        pp[dp->d_namlen] = '\0';
        /*
         * Get stat(2) information.
         */
        if (statsafely(buf, &sb))
            continue;
        /*
         * Ignore the exec'd and non-regular files.
         */
        if (xs && (xdev == sb.st_dev) && (xnode == (INODETYPE)sb.st_ino))
            continue;
        if (!S_ISREG(sb.st_mode))
            continue;
        /*
         * Allocate space for a file entry.  Set its basic characteristics.
         */
        alloc_lfile(ctx, LSOF_FD_LIBRARY_REF, i++);
        Lf->dev_def = Lf->inp_ty = Lf->nlink_def = 1;
        Lf->dev = sb.st_dev;
        Lf->inode = (INODETYPE)sb.st_ino;
        Lf->type = LSOF_FILE_VNODE_VREG;
        /*
         * Look for a match on device and node numbers in the *.so cache.
         */
        for (sp = SoHash[SOHASH(sb.st_dev, (INODETYPE)sb.st_ino)]; sp;
             sp = sp->next) {
            if ((sp->dev == sb.st_dev) && (sp->node == (INODETYPE)sb.st_ino)) {

                /*
                 * A match was found; use its name, link count, and size.
                 */
                nm = sp->nm;
                Lf->nlink = sp->nlink;
                Lf->sz = sp->sz;
                Lf->sz_def = 1;
                break;
            }
        }
        if (!sp) {

            /*
             * No match was found; use the /HASPROCFS/object name, its link
             * count, and its size.
             */
            nm = pp;
            Lf->nlink_def = sb.st_nlink;
            Lf->sz = sb.st_size;
            Lf->sz_def = 1;
        }
        /*
         * Do selection tests: NFS; link count; file name; and file system.
         */

#    if defined(HAS_NFS)
        if (Fnfs && (GET_MIN_DEV(Lf->dev_def) & SDEV_REMOTE))
            Lf->sf |= SELNFS;
#    endif /* defined(HAS_NFS) */

        if (Nlink && (Lf->nlink < Nlink))
            Lf->sf |= SELNLINK;
        if (Sfile && is_file_named(NULL, VREG, 0, 0))
            Lf->sf |= SELNM;
        if (Lf->sf) {

            /*
             * If the file was selected, enter its name and link it to the
             * other files of the process.
             */
            enter_nm(ctx, nm);
            link_lfile(ctx);
        }
    }
    (void)closedir(dfp);
}
#endif     /* AIXA<2 */
