| /* |
| * 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. |
| */ |
| |
| #ifndef lint |
| static char copyright[] = |
| "@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n"; |
| #endif |
| |
| #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_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_K64) |
| |
| /* |
| * Solaris 64 bit kernel |
| */ |
| |
| #undef X2DEV_T |
| #define X2DEV_T unsigned long long |
| #undef XDINDEV |
| #define XDINDEV 16 |
| |
| #define major_X(dp, em) ((int)((x2dev(dp, em) >> 32) & 0xffffffff)) |
| #define minor_X(dp, em) ((int)(x2dev(dp, em) & 0xffffffff)) |
| # else /* !defined(LT_K64) */ |
| |
| /* |
| * Solaris 32 bit kernel |
| */ |
| |
| #define major_X(dp, em) ((int)((x2dev(dp, em) >> L_BITSMINOR) & LT_MJX)) |
| #define minor_X(dp, em) ((int)(x2dev(dp, em) & L_MAXMIN)) |
| # endif /* LT_K64 */ |
| #endif /* defined(LT_DIAL_solaris) */ |
| |
| |
| #if defined(LT_DIAL_uw) |
| /* |
| * UnixWare-specific items |
| */ |
| |
| #include <sys/mkdev.h> |
| #endif /* defined(LT_DIAL_uw) */ |
| |
| |
| /* |
| * 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 |
| */ |
| |
| _PROTOTYPE(static void closepipe,(void)); |
| _PROTOTYPE(static void getlsofpath,(void)); |
| _PROTOTYPE(static int GetOpt,(int ct, char *opt[], char *rules, char **em, |
| char *pn)); |
| _PROTOTYPE(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(dev, ldev) |
| 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, ldev) |
| 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(opt) |
| 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(ct, opt, rules, em, pn) |
| 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(src, len) |
| 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(nf, em) |
| 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(ac, av, opt, pn) |
| 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(x, em) |
| 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); |
| } |