/*
 * LTlib.c -- the lsof test library
 *
 * V. Abell
 * Purdue University
 */

/*
 * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
 * 47907.  All rights reserved.
 *
 * Written by V. 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.
 */

#include "LsofTest.h"

/*
 * Pre-defintions that may be changed by a specific dialect
 */

#define X2DEV_T unsigned int /* cast for result of x2dev() */
#define XDINDEV                                                                \
    8 /* number of hex digits in an lsof                                       \
       * device field -- should be                                             \
       * 2 X sizeof(X2DEV_T) */

#if defined(LT_DIAL_aix)
/*
 * AIX-specific items
 */

#    include <sys/sysmacros.h>

#    if defined(LT_AIXA) && LT_AIXA >= 1

/*
 * Note: the DEVNO64 and ISDEVNO54 #define's come from <sys/sysmacros.h>, but
 * only when _KERNEL is #define'd.
 */

#        undef DEVNO64
#        define DEVNO64 0x8000000000000000LL
#        undef ISDEVNO64
#        define ISDEVNO64(d) (((ulong)(d)&DEVNO64) ? 1 : 0)

/*
 * Define major and minor extraction macros that work on 64 bit AIX
 * architectures.
 */

#        define major_S(d) (ISDEVNO64(d) ? major64(d) : minor(d & ~SDEV_REMOTE))
#        define minor_S(d)                                                     \
            (ISDEVNO64(d) ? (minor64(d) & ~SDEV_REMOTE) : minor(d))
#        undef X2DEV_T
#        define X2DEV_T unsigned long long
#        undef XDINDEV
#        define XDINDEV 16
#        define major_X(dp, em) major_S(x2dev(dp, em))
#        define minor_X(dp, em) minor_S(x2dev(dp, em))
#    endif /* defined(LT_AIXA) && LT_AIXA>=1 */

#endif /* defined(LT_DIAL_aix) */

#if defined(LT_DIAL_bsdi)
/*
 * BSDI-specific items
 */

#    define minor_S(dev) dv_subunit(dev)
#    define unit_S(dev) dv_unit(dev)
#    define minor_X(dp, em) dv_subunit(x2dev(dp, em))
#    define unit_X(dp, em) dv_unit(x2dev(dp, em))
#endif /* defined(LT_DIAL_bsdi) */

#if defined(LT_DIAL_freebsd)
/*
 *FreeBSD-specific items
 */

#    undef XDINDEV
#    define XDINDEV 16
#    if defined(LT_DEV64)
#        undef X2DEV_T
#        define X2DEV_T unsigned long long
#    endif /* defined(LT_DEV64) */
#endif     /* defined(LT_DIAL_freebsd) */

#if defined(LT_DIAL_osr)
/*
 * OpenUNIX-specific items
 */

#    include <sys/sysmacros.h>
#endif /* defined(LT_DIAL_osr) */

#if defined(LT_DIAL_ou)
/*
 * OpenUNIX-specific items
 */

#    include <sys/mkdev.h>
#endif /* defined(LT_DIAL_ou) */

#if defined(LT_DIAL_solaris)
/*
 * Solaris-specific items
 */

#    include <sys/sysmacros.h>

/*
 * Define maximum major device number in a stat(2) dev_t
 */

#    if LT_VERS >= 20501
#        define LT_MJX                                                         \
            L_MAXMAJ /* Get maximum major device number from                   \
                      * <sys/sysmacros.h>. */
#    else            /* LT_VERS<20501 */
#        define LT_MJX                                                         \
            0x3fff /* Avoid <sys/sysmacros.h> when                             \
                    * Solaris < 2.5.1. */
#    endif         /* LT_VERS>=20501 */

#    define major_S(dev) ((int)((dev >> L_BITSMINOR) & LT_MJX))
#    define minor_S(dev) ((int)(dev & L_MAXMIN))

#    if defined(LT_DEV64)

#        undef X2DEV_T
#        define X2DEV_T unsigned long long
#        undef XDINDEV
#        define XDINDEV 16
#    endif /* !defined(LT_DEV64) */

#    define major_X(dp, em) (major_S(x2dev(dp, em)))
#    define minor_X(dp, em) (minor_S(x2dev(dp, em)))

#endif /* defined(LT_DIAL_solaris) */

#if defined(LT_DIAL_uw)
/*
 * UnixWare-specific items
 */

#    include <sys/mkdev.h>
#endif /* defined(LT_DIAL_uw) */

#if defined(LT_DIAL_linux)
/*
 * Linux-specific items
 */

#    include <sys/sysmacros.h>
#endif /* defined(LT_DIAL_linux) */

/*
 * Global variables
 */

int LsofFd = -1;               /* lsof pipe FD */
FILE *LsofFs = (FILE *)NULL;   /* stream for lsof pipe FD */
char *LsofPath = (char *)NULL; /* path to lsof executable */
pid_t LsofPid = (pid_t)0;      /* PID of lsof child process */
int LTopt_h = 0;               /* "-h" option's switch value */
char *LTopt_p = (char *)NULL;  /* "-p path" option's path value */
int MsgStat = 0;               /* message status: 1 means prefix needs
                                * to be issued */

/*
 * Local static variables
 */

static int Afo = 0;                     /* Fo[] structures allocated */
static char *GOv = (char *)NULL;        /* option `:' value pointer */
static int GOx1 = 1;                    /* first opt[][] index */
static int GOx2 = 0;                    /* second opt[][] index */
static LTfldo_t *Fo = (LTfldo_t *)NULL; /* allocated LTfldo_t structures */
static int Ufo = 0;                     /* Fo[] structures used */

/*
 * Local function prototypes
 */

static void closepipe(void);
static void getlsofpath(void);
static int GetOpt(int ct, char *opt[], char *rules, char **em, char *pn);
static X2DEV_T x2dev(char *x, char **em);

/*
 * Default major, minor, and unit macros.
 */

#if !defined(major_S)
#    define major_S major
#endif /* defined(major_S) */

#if !defined(minor_S)
#    define minor_S minor
#endif /* defined(minor_S) */

#if !defined(unit_S)
#    define unit_S(x) 0
#endif /* defined(unit_S) */

#if !defined(major_X)
#    define major_X(dp, em) major(x2dev(dp, em))
#endif /* defined(major_X) */

#if !defined(minor_X)
#    define minor_X(dp, em) minor(x2dev(dp, em))
#endif /* defined(minor_X) */

#if !defined(unit_X)
#    define unit_X(dp, em) 0
#endif /* defined(unit_X) */

/*
 * CanRdKmem() -- can lsof read kernel memory devices?
 */

char *CanRdKmem() {

#if defined(LT_KMEM)
    char buf[2048]; /* temporary buffer */
    char *dn;       /* memory device name */
    char *em;       /* error message pointer */
    int fd;         /* temporary file descriptor */
    struct stat sb; /* memory device stat(2) buffer */
    int ti;         /* temporary integer */
                    /*
                     * Get the lsof path.  If it is not the default, check no further.
                     */
    (void)getlsofpath();
    if (!strcmp(LsofPath, LT_DEF_LSOF_PATH))
        return ((char *)NULL);
    /*
     * Check /dev/kmem access.
     */
    dn = "/dev/kmem";
    if (stat(dn, &sb)) {
        em = "stat";

    kmem_error:

        (void)snprintf(buf, sizeof(buf) - 1, "ERROR!!!  can't %s(%s): %s\n", em,
                       dn, strerror(errno));
        buf[sizeof(buf) - 1] = '\0';
        return (MkStrCpy(buf, &ti));
    }
    if ((fd = open(dn, O_RDONLY, 0)) < 0) {
        em = "open";
        goto kmem_error;
    }
    (void)close(fd);
    /*
     * Check /dev/mem access.
     */
    dn = "/dev/mem";
    if (stat(dn, &sb)) {

        /*
         * If /dev/mem can't be found, ignore the error.
         */
        return ((char *)NULL);
    }
    if ((fd = open(dn, O_RDONLY, 0)) < 0) {
        em = "open";
        goto kmem_error;
    }
    (void)close(fd);
#endif /* defined(LT_KMEM) */

    return ((char *)NULL);
}

/*
 * closepipe() -- close pipe from lsof
 */

static void closepipe() {
    if (LsofFd >= 0) {

        /*
         * A pipe from lsof is open.  Close it and the associated stream.
         */
        if (LsofFs) {
            (void)fclose(LsofFs);
            LsofFs = (FILE *)NULL;
        }
        (void)close(LsofFd);
        LsofFd = -1;
    }
}

/*
 * ConvLsofDev() -- convert lsof device string
 *
 * Note: this function is dialect-specific.
 */

char *ConvLsofDev(char *dev,     /* lsof device string -- the value to the
                                  * LSOF_FID_DEVN field of a LSOF_FID_FD block
                                  * (see lsof_fields.h) */
                  LTdev_t *ldev) /* results are returned to this structure */
{
    char *dp; /* device pointer */
    char *em; /* error message pointer */
    int tlen; /* temporary length */
              /*
               * Check function arguments.
               *
               * Establish values for decoding the device string.
               */
    if (!dev)
        return ("ERROR!!!  no ConvLsofDev() device");
    if (!ldev)
        return ("ERROR!!!  no ConvLsofDev() result pointer");
    if (strncmp(dev, "0x", 2))
        return ("ERROR!!!  no leading 0x in ConvLsofDev() device");
    dp = dev + 2;
    if (((tlen = (int)strlen(dp)) < 1) || (tlen > XDINDEV))
        return ("ERROR!!!  bad ConvLsofDev() device length");
    /*
     * Use the pre-defined *_X() macros to do the decomposition.
     */
    ldev->maj = (unsigned int)major_X(dp, &em);
    if (em)
        return (em);
    ldev->min = (unsigned int)minor_X(dp, &em);
    if (em)
        return (em);
    ldev->unit = (unsigned int)unit_X(dp, &em);
    return (em);
}

/*
 * ConvStatDev() -- convert stat(2) device number
 *
 * Note: this function is dialect-specific.
 */

char *ConvStatDev(dev_t *dev,    /* device number to be converted */
                  LTdev_t *ldev) /* results are returned to this structure */
{

    /*
     * Check function arguments.
     */
    if (!dev)
        return ("ERROR!!!  no ConvStatDev() device");
    if (!ldev)
        return ("ERROR!!!  no ConvStatDev() result pointer");
    /*
     * Use the pre-defined *_S() macros to do the decomposition.
     */
    ldev->maj = (unsigned int)major_S(*dev);
    ldev->min = (unsigned int)minor_S(*dev);
    ldev->unit = (unsigned int)unit_S(*dev);
    return ((char *)NULL);
}

/*
 * ExecLsof() -- execute lsof with full field output and a NUL field terminator
 *		 in a child process
 */

char *ExecLsof(char **opt) /* lsof options -- a pointer to an
                            * array of character pointers,
                            * terminated by a NULL pointer */
{
    static char **av = (char **)NULL; /* lsof argument vector, dynamically
                                       * allocated */
    static int ava = 0;               /* **av entries allocated */
    char buf[2048];                   /* temporary buffer */
    char *em;                         /* error message pointer */
    int fd;                           /* temporary file descriptor */
    int optc;                         /* option count */
    int nf;                           /* number of files */
    int p[2];                         /* pipe FDs */
    char **tcpp;                      /* temporary character pointer
                                       * pointer */
    int ti;                           /* temporary integer */
    int tlen;                         /* temporary length */
    pid_t tpid;                       /* temporary PID holder */
                                      /*
                                       * It's an error if lsof is already in execution or if no lsof options
                                       * were supplied.
                                       */
    (void)getlsofpath();
    if (LsofPid)
        return ("ERROR!!!  ExecLsof() says lsof is already in execution");
    if (!opt)
        return ("ERROR!!!  no ExecLsof() option list");
    for (optc = 0, tcpp = opt; *tcpp; optc++, tcpp++)
        ;
    /*
     * Make sure lsof is executable.
     */
    if ((em = IsLsofExec()))
        return (em);
    /*
     * Open a pipe through which lsof can return output.
     */
    if (pipe(p)) {
        (void)snprintf(buf, sizeof(buf) - 1, "ERROR!!!  can't open pipe: %s",
                       strerror(errno));
        return (MkStrCpy(buf, &ti));
    }
    /*
     * Allocate and build an argument vector.  The first entry will be set
     * to "lsof", the second to "-wFr", and the third to "-F0".  Additional
     * entries will be set as supplied by the caller.
     */
    if ((optc + 4) > ava) {
        tlen = (int)(sizeof(char *) * (optc + 4));
        if (!av)
            av = (char **)malloc(tlen);
        else
            av = (char **)realloc((void *)av, tlen);
        if (!av) {
            (void)snprintf(
                buf, sizeof(buf) - 1,
                "LTlib: ExecLsof() can't allocat pointers for %d arguments",
                optc + 4);
            return (MkStrCpy(buf, &ti));
        }
        ava = optc + 4;
    }
    for (ti = 0, tcpp = opt; ti < (optc + 3); ti++) {
        switch (ti) {
        case 0:
            av[ti] = "lsof";
            break;
        case 1:
            av[ti] = "-wFr";
            break;
        case 2:
            av[ti] = "-F0";
            break;
        default:
            av[ti] = *tcpp;
            tcpp++;
        }
    }
    av[ti] = (char *)NULL;
    /*
     * Fork a child process to run lsof.
     */
    switch ((tpid = fork())) {
    case (pid_t)0:

        /*
         * This is the child process.
         *
         * First close all file descriptors except the output side of the pipe.
         *
         * Make the output side of the pipe STDOUT and STDERR.
         */
        for (fd = 0, nf = getdtablesize(); fd < nf; fd++) {
            if (fd == p[1])
                continue;
            (void)close(fd);
        }
        if (p[1] != 1)
            (void)dup2(p[1], 1);
        if (p[1] != 2)
            (void)dup2(p[1], 2);
        if ((p[1] != 1) && (p[1] != 2))
            (void)close(p[1]);
        /*
         * Execute lsof.
         */
        (void)execv(LsofPath, av);
        _exit(0); /* (Shouldn't get here.) */
    case (pid_t)-1:

        /*
         * A fork error occurred.  Form and return a message.
         */
        (void)snprintf(buf, sizeof(buf) - 1,
                       "ERROR!!!  ExecLsof() can't fork: %s", strerror(errno));
        buf[sizeof(buf) - 1] = '\0';
        return (MkStrCpy(buf, &ti));
    default:

        /*
         * This is the parent.
         *
         * Save the lsof child PID.
         *
         * Close the output side of the pipe.
         *
         * Save the input side of the pipe as LsofFd; open a stream for it.
         */
        LsofPid = tpid;
        (void)close(p[1]);
        LsofFd = p[0];
        if (!(LsofFs = fdopen(LsofFd, "r")))
            return ("ERROR!!!  ExecLsof() can't open stream to lsof output FD");
    }
    /*
     * Wait a bit for lsof to start and put something in its pipe, then return
     * an "All is well." response.
     */
    sleep(1);
    return ((char *)NULL);
}

/*
 * getlsofpath() -- get lsof path, either from LT_LSOF_PATH in the environment
 *		    or from LT_DEF_LSOF_PATH
 */

static void getlsofpath() {
    char *tcp; /* temporary character pointer */
    int ti;    /* temporary integer */

    if (LsofPath)
        return;
    if ((tcp = getenv("LT_LSOF_PATH")))
        LsofPath = MkStrCpy(tcp, &ti);
    else
        LsofPath = LT_DEF_LSOF_PATH;
}

/*
 * GetOpt() -- Local get option
 *
 * Borrowed from lsof's main.c source file.
 *
 * Liberally adapted from the public domain AT&T getopt() source,
 * distributed at the 1985 UNIFORM conference in Dallas
 *
 * The modifications allow `?' to be an option character and allow
 * the caller to decide that an option that may be followed by a
 * value doesn't have one -- e.g., has a default instead.
 */

static int GetOpt(int ct,      /* option count */
                  char *opt[], /* options */
                  char *rules, /* option rules */
                  char **em,   /* error message return */
                  char *pn) {
    register int c;                   /* character value */
    register char *cp = (char *)NULL; /* character pointer */
    char embf[2048];                  /* error message buffer */
    int tlen;                         /* temporary message length from
                                       * MkStrCpy() */

    *em = (char *)NULL;
    if (GOx2 == 0) {

        /*
         * Move to a new entry of the option array.
         *
         * EOF if:
         *
         *	Option list has been exhausted;
         *	Next option doesn't start with `-' or `+';
         *	Next option has nothing but `-' or `+';
         *	Next option is ``--'' or ``++''.
         */
        if (GOx1 >= ct || (opt[GOx1][0] != '-' && opt[GOx1][0] != '+') ||
            !opt[GOx1][1])
            return (EOF);
        if (strcmp(opt[GOx1], "--") == 0 || strcmp(opt[GOx1], "++") == 0) {
            GOx1++;
            return (EOF);
        }
        GOx2 = 1;
    }
    /*
     * Flag `:' option character as an error.
     *
     * Check for a rule on this option character.
     */
    if ((c = opt[GOx1][GOx2]) == ':') {
        (void)snprintf(embf, sizeof(embf) - 1,
                       "ERROR!!!  colon is an illegal option character.");
        embf[sizeof(embf) - 1] = '\0';
        *em = MkStrCpy(embf, &tlen);
    } else if (!(cp = strchr(rules, c))) {
        (void)snprintf(embf, sizeof(embf) - 1,
                       "ERROR!!!  illegal option character: %c", c);
        embf[sizeof(embf) - 1] = '\0';
        *em = MkStrCpy(embf, &tlen);
    }
    if (*em) {

        /*
         * An error was detected.
         *
         * Advance to the next option character.
         *
         * Return the character causing the error.
         */
        if (opt[GOx1][++GOx2] == '\0') {
            GOx1++;
            GOx2 = 0;
        }
        return (c);
    }
    if (*(cp + 1) == ':') {

        /*
         * The option may have a following value.  The caller decides if it
         * does.
         *
         * Don't indicate that an option of ``--'' is a possible value.
         *
         * Finally, on the assumption that the caller will decide that the
         * possible value belongs to the option, position to the option
         * following the possible value, so that the next call to GetOpt() will
         * find it.
         */
        if (opt[GOx1][GOx2 + 1] != '\0') {
            GOv = &opt[GOx1++][GOx2];
        } else if (++GOx1 >= ct)
            GOv = (char *)NULL;
        else {
            GOv = opt[GOx1];
            if (strcmp(GOv, "--") == 0)
                GOv = (char *)NULL;
            else
                GOx1++;
        }
        GOx2 = 0;
    } else {

        /*
         * The option character stands alone with no following value.
         *
         * Advance to the next option character.
         */
        if (opt[GOx1][++GOx2] == '\0') {
            GOx2 = 0;
            GOx1++;
        }
        GOv = (char *)NULL;
    }
    /*
     * Return the option character.
     */
    return (c);
}

/*
 * IsLsofExec() -- see if lsof is executable
 */

char *IsLsofExec() {
    char buf[2048]; /* temporary buffer */
    int len;        /* temporary length */

    (void)getlsofpath();
    if (access(LsofPath, X_OK) < 0) {
        (void)snprintf(buf, sizeof(buf) - 1, "ERROR!!!  can't execute %s: %s",
                       LsofPath, strerror(errno));
        return (MkStrCpy(buf, &len));
    }
    return ((char *)NULL);
}

/*
 * LTlibClean() -- clean up LTlib resource accesses
 */

void LTlibClean() { (void)StopLsof(); }

/*
 * MkStrCpy() -- make string copy
 */

char *MkStrCpy(char *src, /* string source to copy */
               int *len)  /* returned length allocation */
{
    char *rp;   /* return pointer */
    int srclen; /* source string length */

    if (!src) {
        (void)fprintf(stderr, "ERROR!!!  no string supplied to MkStrCpy()\n");
        exit(1);
    }
    srclen = (int)strlen(src);
    *len = srclen++;
    if (!(rp = (char *)malloc(srclen))) {
        (void)fprintf(stderr, "ERROR!!!  MkStrCpy() -- no malloc() space");
        exit(1);
    }
    (void)strcpy(rp, src);
    return (rp);
}

/*
 * PrtMsg() -- print message
 */

void PrtMsg(mp, pn) char *mp; /* message pointer -- may be NULL to
                               * trigger space prefix initialization
                               */
char *pn;                     /* program name */
{
    static int pfxlen = -1;          /* prefix length, based on program */
                                     /* name -- computed on first call
                                      * when pfxlen == -1 */
    static char *pfx = (char *)NULL; /* prefix (spaces) */
    int ti;                          /* temporary index */

    if (pfxlen == -1) {

        /*
         * This is the first call.  Compute the prefix length and build the
         * prefix.
         */
        if (!pn)
            pfxlen = 0;
        else
            pfxlen = (int)(strlen(pn));
        pfxlen += (int)strlen(" ... ");
        if (!(pfx = (char *)malloc(pfxlen + 1))) {
            (void)printf("ERROR!!!  not enough space for %d space prefix\n",
                         pfxlen);
            exit(1);
        }
        for (ti = 0; ti < pfxlen; ti++) {
            pfx[ti] = ' ';
        }
        pfx[pfxlen] = '\0';
        MsgStat = 0;
    }
    /*
     * Process the message.
     */
    if (MsgStat)
        (void)printf("%s", pfx);
    if (mp && *mp) {
        (void)printf("%s\n", mp);
        MsgStat = 1;
    }
}

/*
 * PrtMsgX() -- print message and exit
 */

void PrtMsgX(mp, pn, f, xv) char *mp; /* message pointer */
char *pn;                             /* program name */
void (*f)();                          /* clean-up function pointer */
int xv;                               /* exit value */
{
    if (mp)
        PrtMsg(mp, pn);
    if (f)
        (void)(*f)();
    (void)LTlibClean();
    exit(xv);
}

/*
 * RdFrLsof() -- read from lsof
 */

LTfldo_t *RdFrLsof(int *nf,   /* number of fields receiver */
                   char **em) /* error message pointer receiver */
{
    char buf[2048];              /* temporary buffer */
    int bufl = (int)sizeof(buf); /* size of buf[] */
    char *blim = &buf[bufl - 1]; /* buf[] limit (last character
                                  * address) */
    char *fsp;                   /* field start pointer */
    char *tcp;                   /* temporary character pointer */
    LTfldo_t *tfop;              /* temporary field output pointer */
    int ti;                      /* temporary index */
    int tlen;                    /* remporary length */
    char *vp;                    /* value character pointer */
                                 /*
                                  * Check for errors.
                                  */
    if (!em)
        return ((LTfldo_t *)NULL);
    if (!nf) {
        *em = "ERROR!!!  RdFrLsof() not given a count return pointer";
        return ((LTfldo_t *)NULL);
    }
    *em = (char *)NULL;
    *nf = 0;
    /*
     * If fields are in use, release their resources.
     */
    for (ti = 0, tfop = Fo; (ti < Ufo); ti++, tfop++) {
        if (tfop->v)
            (void)free((void *)tfop->v);
    }
    Ufo = 0;
    /*
     * Read a line from lsof.
     */
    if (!fgets(buf, bufl - 2, LsofFs)) {

        /*
         * An lsof pipe EOF has been reached.  Indicate that with a NULL
         * pointer return, coupled with a NULL error message return pointer
         * (set above), and a field count of zero (set above).
         */
        return ((LTfldo_t *)NULL);
    }
    /*
     * Parse the lsof line, allocating field output structures as appropriate.
     *
     * It is expected that fields will end in a NUL ('\0') or a NL ('\0') and
     * that a NL ends all fields in the lsof line.
     */
    for (tcp = buf, Ufo = 0; (*tcp != '\n') && (tcp < blim); tcp++) {

        /*
         * Start a new field.  The first character is the LSOF_FID_*.
         *
         * First allocate an LTfldo_t structure.
         */
        if (Ufo >= Afo) {

            /*
             * More LTfldo_t space is required.
             */
            Afo += LT_FLDO_ALLOC;
            tlen = (int)(Afo * sizeof(LTfldo_t));
            if (Fo)
                Fo = (LTfldo_t *)realloc(Fo, tlen);
            else
                Fo = (LTfldo_t *)malloc(tlen);
            if (!Fo) {

                /*
                 * A serious error has occurred; no LTfldo_t space is available.
                 */
                (void)snprintf(
                    buf, bufl,
                    "ERROR!!!  RdFrLsof() can't allocate %d pointer bytes",
                    tlen);
                *em = MkStrCpy(buf, &ti);
                *nf = -1;
                return ((LTfldo_t *)NULL);
            }
        }
        tfop = Fo + Ufo;
        tfop->v = (char *)NULL;
        Ufo++;
        /*
         * Save the LSOF_FID_* character.  Then compute the field value length,
         * and make a copy of it.
         */
        tfop->ft = *tcp++;
        fsp = tcp;
        tlen = 0;
        while (*tcp && (*tcp != '\n') && (tcp < blim)) {
            tcp++;
            tlen++;
        }
        if (!(vp = (char *)malloc(tlen + 1))) {

            /*
             * A serious error has occurred; there's no space for the field
             * value.
             */
            (void)snprintf(buf, bufl,
                           "ERROR!!!  RdFrLsof() can't allocate %d field bytes",
                           tlen + 1);
            *em = MkStrCpy(buf, &ti);
            *nf = -1;
            return ((LTfldo_t *)NULL);
        }
        (void)memcpy((void *)vp, (void *)fsp, tlen);
        vp[tlen] = '\0';
        tfop->v = vp;
        if (*tcp == '\n')
            break;
        if (tcp >= blim) {

            /*
             * The lsof line has no NL terminator; that's an error.
             */
            *em = "ERROR!!! RdFrLsof() didn't find a NL";
            *nf = -1;
            return ((LTfldo_t *)NULL);
        }
    }
    /*
     * The end of the lsof line has been reached.  If no fields were assembled,
     * return an error indicate.  Otherwise return the fields and their count.
     */
    if (!Ufo) {
        *em = "ERROR!!! RdFrLsof() read an empty lsof line";
        *nf = -1;
        return ((LTfldo_t *)NULL);
    }
    *nf = Ufo;
    *em = (char *)NULL;
    return (Fo);
}

/*
 * ScanArg() -- scan arguments
 */

int ScanArg(int ac,     /* argument count */
            char *av[], /* argument pointers */
            char *opt,  /* option string */
            char *pn)   /* program name */
{
    char *em;        /* pointer to error message returned by
                      * GetOpt() */
    char embf[2048]; /* error message buffer */
    int rv = 0;      /* return value */
    int tc;          /* temporary character value */
                     /*
                      * Preset possible argument values.
                      */
    LTopt_h = 0;
    if (LTopt_p) {
        (void)free((void *)LTopt_p);
        LTopt_p = (char *)NULL;
    }
    /*
     * Process the options according to the supplied option string.
     */
    while ((tc = GetOpt(ac, av, opt, &em, pn)) != EOF) {
        if (em) {
            rv = 1;
            PrtMsg(em, pn);
            continue;
        }
        switch (tc) {
        case 'h':
            LTopt_h = 1;
            break;
        case 'p':
            if (!GOv || *GOv == '-' || *GOv == '+') {
                rv = 1;
                (void)PrtMsg("ERROR!!!  -p not followed by a path", pn);
            } else
                LTopt_p = GOv;
            break;
        default:
            rv = 1;
            (void)snprintf(embf, sizeof(embf) - 1,
                           "ERROR!!!  unknown option: %c", tc);
            PrtMsg(embf, pn);
        }
    }
    for (; GOx1 < ac; GOx1++) {

        /*
         * Report extraneous arguments.
         */
        rv = 1;
        (void)snprintf(embf, sizeof(embf) - 1,
                       "ERROR!!!  extraneous option: \"%s\"", av[GOx1]);
        PrtMsg(embf, pn);
    }
    return (rv);
}

/*
 * StopLsof() -- stop a running lsof process and close the pipe from it
 */

void StopLsof() {
    pid_t pid;

    if (LsofPid) {

        /*
         * An lsof child process may be active.  Wait for (or kill) it.
         */
        pid = wait3(NULL, WNOHANG, NULL);
        if (pid != LsofPid) {
            (void)kill(LsofPid, SIGKILL);
            sleep(2);
            pid = wait3(NULL, WNOHANG, NULL);
        }
        LsofPid = (pid_t)0;
    }
    (void)closepipe();
}

/*
 * x2dev() -- convert hex string to device number
 */

static X2DEV_T x2dev(char *x,   /* hex string */
                     char **em) /* error message receiver */
{
    char buf[2048]; /* temporary message buffer */
    int c;          /* character holder */
    X2DEV_T dev;    /* device number result */
    char *wx;       /* working hex string pointer */
    int xl;         /* hex string length */

    if (!x || !*x) {
        *em = "ERROR!!!  no hex string supplied to x2dev()";
        return (0);
    }
    wx = strncasecmp(x, "0x", 2) ? x : (x + 2);
    if (((xl = (int)strlen(wx)) < 1) || (xl > XDINDEV)) {
        (void)snprintf(buf, sizeof(buf) - 1,
                       "ERROR!!!  x2dev(\"%s\") bad length: %d", x, xl + 2);
        buf[sizeof(buf) - 1] = '\0';
        *em = MkStrCpy(buf, &c);
        return (0);
    }
    /*
     * Assemble the device number result from the hex string.
     */
    for (dev = (X2DEV_T)0; *wx; wx++) {
        if (isdigit((unsigned char)*wx)) {
            dev = (dev << 4) | (unsigned int)(((int)*wx - (int)'0') & 0xf);
            continue;
        }
        c = (int)tolower((unsigned char)*wx);
        if ((c >= (int)'a') && (c <= (int)'f')) {
            dev = (dev << 4) | (unsigned int)((c - 'a' + 10) & 0xf);
            continue;
        }
        (void)snprintf(buf, sizeof(buf) - 1,
                       "ERROR!!!  x2dev(\"%s\") non-hex character: %c", x, c);
        *em = MkStrCpy(buf, &c);
    }
    /*
     * Return result and no error indication.
     */
    *em = (char *)NULL;
    return (dev);
}
