2 * LTlib.c -- the lsof test library
10 * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
11 * 47907. All rights reserved.
13 * Written by V. Abell.
15 * This software is not subject to any license of the American Telephone
16 * and Telegraph Company or the Regents of the University of California.
18 * Permission is granted to anyone to use this software for any purpose on
19 * any computer system, and to alter it and redistribute it freely, subject
20 * to the following restrictions:
22 * 1. Neither the authors nor Purdue University are responsible for any
23 * consequences of the use of this software.
25 * 2. The origin of this software must not be misrepresented, either by
26 * explicit claim or by omission. Credit to the authors and Purdue
27 * University must appear in documentation and sources.
29 * 3. Altered versions must be plainly marked as such, and must not be
30 * misrepresented as being the original software.
32 * 4. This notice may not be removed or altered.
36 static char copyright[] =
37 "@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
44 * Pre-defintions that may be changed by a specific dialect
47 #define X2DEV_T unsigned int /* cast for result of x2dev() */
48 #define XDINDEV 8 /* number of hex digits in an lsof
49 * device field -- should be
50 * 2 X sizeof(X2DEV_T) */
53 #if defined(LT_DIAL_aix)
58 #include <sys/sysmacros.h>
60 # if defined(LT_AIXA) && LT_AIXA>=1
63 * Note: the DEVNO64 and ISDEVNO54 #define's come from <sys/sysmacros.h>, but
64 * only when _KERNEL is #define'd.
68 #define DEVNO64 0x8000000000000000LL
70 #define ISDEVNO64(d) (((ulong)(d) & DEVNO64) ? 1 : 0)
73 * Define major and minor extraction macros that work on 64 bit AIX
77 #define major_S(d) (ISDEVNO64(d) ? major64(d) : minor(d & ~SDEV_REMOTE))
78 #define minor_S(d) (ISDEVNO64(d) ? (minor64(d) & ~SDEV_REMOTE) : minor(d))
80 #define X2DEV_T unsigned long long
83 #define major_X(dp, em) major_S(x2dev(dp, em))
84 #define minor_X(dp, em) minor_S(x2dev(dp, em))
85 # endif /* defined(LT_AIXA) && LT_AIXA>=1 */
87 #endif /* defined(LT_DIAL_aix) */
90 #if defined(LT_DIAL_bsdi)
95 #define minor_S(dev) dv_subunit(dev)
96 #define unit_S(dev) dv_unit(dev)
97 #define minor_X(dp, em) dv_subunit(x2dev(dp, em))
98 #define unit_X(dp, em) dv_unit(x2dev(dp, em))
99 #endif /* defined(LT_DIAL_bsdi) */
102 #if defined(LT_DIAL_osr)
104 * OpenUNIX-specific items
107 #include <sys/sysmacros.h>
108 #endif /* defined(LT_DIAL_osr) */
111 #if defined(LT_DIAL_ou)
113 * OpenUNIX-specific items
116 #include <sys/mkdev.h>
117 #endif /* defined(LT_DIAL_ou) */
120 #if defined(LT_DIAL_solaris)
122 * Solaris-specific items
125 #include <sys/sysmacros.h>
129 * Define maximum major device number in a stat(2) dev_t
133 #define LT_MJX L_MAXMAJ /* Get maximum major device number from
134 * <sys/sysmacros.h>. */
135 # else /* LT_VERS<20501 */
136 #define LT_MJX 0x3fff /* Avoid <sys/sysmacros.h> when
137 * Solaris < 2.5.1. */
138 # endif /* LT_VERS>=20501 */
140 #define major_S(dev) ((int)((dev >> L_BITSMINOR) & LT_MJX))
141 #define minor_S(dev) ((int)(dev & L_MAXMIN))
146 * Solaris 64 bit kernel
150 #define X2DEV_T unsigned long long
154 #define major_X(dp, em) ((int)((x2dev(dp, em) >> 32) & 0xffffffff))
155 #define minor_X(dp, em) ((int)(x2dev(dp, em) & 0xffffffff))
156 # else /* !defined(LT_K64) */
159 * Solaris 32 bit kernel
162 #define major_X(dp, em) ((int)((x2dev(dp, em) >> L_BITSMINOR) & LT_MJX))
163 #define minor_X(dp, em) ((int)(x2dev(dp, em) & L_MAXMIN))
165 #endif /* defined(LT_DIAL_solaris) */
168 #if defined(LT_DIAL_uw)
170 * UnixWare-specific items
173 #include <sys/mkdev.h>
174 #endif /* defined(LT_DIAL_uw) */
181 int LsofFd = -1; /* lsof pipe FD */
182 FILE *LsofFs = (FILE *)NULL; /* stream for lsof pipe FD */
183 char *LsofPath = (char *)NULL; /* path to lsof executable */
184 pid_t LsofPid = (pid_t)0; /* PID of lsof child process */
185 int LTopt_h = 0; /* "-h" option's switch value */
186 char *LTopt_p = (char *)NULL; /* "-p path" option's path value */
187 int MsgStat = 0; /* message status: 1 means prefix needs
192 * Local static variables
195 static int Afo = 0; /* Fo[] structures allocated */
196 static char *GOv = (char *)NULL; /* option `:' value pointer */
197 static int GOx1 = 1; /* first opt[][] index */
198 static int GOx2 = 0; /* second opt[][] index */
199 static LTfldo_t *Fo = (LTfldo_t *)NULL; /* allocated LTfldo_t structures */
200 static int Ufo = 0; /* Fo[] structures used */
204 * Local function prototypes
207 _PROTOTYPE(static void closepipe,(void));
208 _PROTOTYPE(static void getlsofpath,(void));
209 _PROTOTYPE(static int GetOpt,(int ct, char *opt[], char *rules, char **em,
211 _PROTOTYPE(static X2DEV_T x2dev,(char *x, char **em));
215 * Default major, minor, and unit macros.
218 #if !defined(major_S)
219 #define major_S major
220 #endif /* defined(major_S) */
222 #if !defined(minor_S)
223 #define minor_S minor
224 #endif /* defined(minor_S) */
228 #endif /* defined(unit_S) */
230 #if !defined(major_X)
231 #define major_X(dp, em) major(x2dev(dp, em))
232 #endif /* defined(major_X) */
234 #if !defined(minor_X)
235 #define minor_X(dp, em) minor(x2dev(dp, em))
236 #endif /* defined(minor_X) */
239 #define unit_X(dp, em) 0
240 #endif /* defined(unit_X) */
244 * CanRdKmem() -- can lsof read kernel memory devices?
252 char buf[2048]; /* temporary buffer */
253 char *dn; /* memory device name */
254 char *em; /* error message pointer */
255 int fd; /* temporary file descriptor */
256 struct stat sb; /* memory device stat(2) buffer */
257 int ti; /* temporary integer */
259 * Get the lsof path. If it is not the default, check no further.
261 (void) getlsofpath();
262 if (!strcmp(LsofPath, LT_DEF_LSOF_PATH))
263 return((char *)NULL);
265 * Check /dev/kmem access.
273 (void) snprintf(buf, sizeof(buf) - 1,
274 "ERROR!!! can't %s(%s): %s\n", em, dn, strerror(errno));
275 buf[sizeof(buf) - 1] = '\0';
276 return(MkStrCpy(buf, &ti));
278 if ((fd = open(dn, O_RDONLY, 0)) < 0) {
284 * Check /dev/mem access.
290 * If /dev/mem can't be found, ignore the error.
292 return((char *)NULL);
294 if ((fd = open(dn, O_RDONLY, 0)) < 0) {
299 #endif /* defined(LT_KMEM) */
301 return((char *)NULL);
306 * closepipe() -- close pipe from lsof
315 * A pipe from lsof is open. Close it and the associated stream.
318 (void) fclose(LsofFs);
319 LsofFs = (FILE *)NULL;
321 (void) close(LsofFd);
328 * ConvLsofDev() -- convert lsof device string
330 * Note: this function is dialect-specific.
334 ConvLsofDev(dev, ldev)
335 char *dev; /* lsof device string -- the value to the
336 * LSOF_FID_DEVN field of a LSOF_FID_FD block
337 * (see lsof_fields.h) */
338 LTdev_t *ldev; /* results are returned to this structure */
340 char *dp; /* device pointer */
341 char *em; /* error message pointer */
342 int tlen; /* temporary length */
344 * Check function arguments.
346 * Establish values for decoding the device string.
349 return("ERROR!!! no ConvLsofDev() device");
351 return("ERROR!!! no ConvLsofDev() result pointer");
352 if (strncmp(dev, "0x", 2))
353 return("ERROR!!! no leading 0x in ConvLsofDev() device");
355 if (((tlen = (int)strlen(dp)) < 1) || (tlen > XDINDEV))
356 return("ERROR!!! bad ConvLsofDev() device length");
358 * Use the pre-defined *_X() macros to do the decomposition.
360 ldev->maj = (unsigned int)major_X(dp, &em);
363 ldev->min = (unsigned int)minor_X(dp, &em);
366 ldev->unit = (unsigned int)unit_X(dp, &em);
372 * ConvStatDev() -- convert stat(2) device number
374 * Note: this function is dialect-specific.
378 ConvStatDev(dev, ldev)
379 dev_t *dev; /* device number to be converted */
380 LTdev_t *ldev; /* results are returned to this structure */
384 * Check function arguments.
387 return("ERROR!!! no ConvStatDev() device");
389 return("ERROR!!! no ConvStatDev() result pointer");
391 * Use the pre-defined *_S() macros to do the decomposition.
393 ldev->maj = (unsigned int)major_S(*dev);
394 ldev->min = (unsigned int)minor_S(*dev);
395 ldev->unit = (unsigned int)unit_S(*dev);
396 return((char *)NULL);
401 * ExecLsof() -- execute lsof with full field output and a NUL field terminator
407 char **opt; /* lsof options -- a pointer to an
408 * array of character pointers,
409 * terminated by a NULL pointer */
411 static char **av = (char **)NULL; /* lsof argument vector, dynamically
413 static int ava = 0; /* **av entries allocated */
414 char buf[2048]; /* temporary buffer */
415 char *em; /* error message pointer */
416 int fd; /* temporary file descriptor */
417 int optc; /* option count */
418 int nf; /* number of files */
419 int p[2]; /* pipe FDs */
420 char **tcpp; /* temporary character pointer
422 int ti; /* temporary integer */
423 int tlen; /* temporary length */
424 pid_t tpid; /* temporary PID holder */
426 * It's an error if lsof is already in execution or if no lsof options
429 (void) getlsofpath();
431 return("ERROR!!! ExecLsof() says lsof is already in execution");
433 return("ERROR!!! no ExecLsof() option list");
434 for (optc = 0, tcpp = opt; *tcpp; optc++, tcpp++)
437 * Make sure lsof is executable.
439 if ((em = IsLsofExec()))
442 * Open a pipe through which lsof can return output.
445 (void) snprintf(buf, sizeof(buf) - 1,
446 "ERROR!!! can't open pipe: %s", strerror(errno));
447 return(MkStrCpy(buf, &ti));
450 * Allocate and build an argument vector. The first entry will be set
451 * to "lsof", the second to "-wFr", and the third to "-F0". Additional
452 * entries will be set as supplied by the caller.
454 if ((optc + 4) > ava) {
455 tlen = (int)(sizeof(char *) * (optc + 4));
457 av = (char **)malloc(tlen);
459 av = (char **)realloc((void *)av, tlen);
461 (void) snprintf(buf, sizeof(buf) - 1,
462 "LTlib: ExecLsof() can't allocat pointers for %d arguments",
464 return(MkStrCpy(buf, &ti));
468 for (ti = 0, tcpp = opt; ti < (optc + 3); ti++) {
484 av[ti] = (char *)NULL;
486 * Fork a child process to run lsof.
488 switch((tpid = fork())) {
492 * This is the child process.
494 * First close all file descriptors except the output side of the pipe.
496 * Make the output side of the pipe STDOUT and STDERR.
498 for (fd = 0, nf = getdtablesize(); fd < nf; fd++) {
504 (void) dup2(p[1], 1);
506 (void) dup2(p[1], 2);
507 if ((p[1] != 1) && (p[1] != 2))
512 (void) execv(LsofPath, av);
513 _exit(0); /* (Shouldn't get here.) */
517 * A fork error occurred. Form and return a message.
519 (void) snprintf(buf, sizeof(buf) - 1,
520 "ERROR!!! ExecLsof() can't fork: %s", strerror(errno));
521 buf[sizeof(buf) - 1] = '\0';
522 return(MkStrCpy(buf, &ti));
526 * This is the parent.
528 * Save the lsof child PID.
530 * Close the output side of the pipe.
532 * Save the input side of the pipe as LsofFd; open a stream for it.
537 if (!(LsofFs = fdopen(LsofFd, "r")))
538 return("ERROR!!! ExecLsof() can't open stream to lsof output FD");
541 * Wait a bit for lsof to start and put something in its pipe, then return
542 * an "All is well." response.
545 return((char *)NULL);
550 * getlsofpath() -- get lsof path, either from LT_LSOF_PATH in the environment
551 * or from LT_DEF_LSOF_PATH
557 char *tcp; /* temporary character pointer */
558 int ti; /* temporary integer */
562 if ((tcp = getenv("LT_LSOF_PATH")))
563 LsofPath = MkStrCpy(tcp, &ti);
565 LsofPath = LT_DEF_LSOF_PATH;
570 * GetOpt() -- Local get option
572 * Borrowed from lsof's main.c source file.
574 * Liberally adapted from the public domain AT&T getopt() source,
575 * distributed at the 1985 UNIFORM conference in Dallas
577 * The modifications allow `?' to be an option character and allow
578 * the caller to decide that an option that may be followed by a
579 * value doesn't have one -- e.g., has a default instead.
583 GetOpt(ct, opt, rules, em, pn)
584 int ct; /* option count */
585 char *opt[]; /* options */
586 char *rules; /* option rules */
587 char **em; /* error message return */
590 register int c; /* character value */
591 register char *cp = (char *)NULL; /* character pointer */
592 char embf[2048]; /* error message buffer */
593 int tlen; /* temporary message length from
600 * Move to a new entry of the option array.
604 * Option list has been exhausted;
605 * Next option doesn't start with `-' or `+';
606 * Next option has nothing but `-' or `+';
607 * Next option is ``--'' or ``++''.
610 || (opt[GOx1][0] != '-' && opt[GOx1][0] != '+')
613 if (strcmp(opt[GOx1], "--") == 0 || strcmp(opt[GOx1], "++") == 0) {
620 * Flag `:' option character as an error.
622 * Check for a rule on this option character.
624 if ((c = opt[GOx1][GOx2]) == ':') {
625 (void) snprintf(embf, sizeof(embf) - 1,
626 "ERROR!!! colon is an illegal option character.");
627 embf[sizeof(embf) - 1] = '\0';
628 *em = MkStrCpy(embf, &tlen);
629 } else if (!(cp = strchr(rules, c))) {
630 (void) snprintf(embf, sizeof(embf) - 1,
631 "ERROR!!! illegal option character: %c", c);
632 embf[sizeof(embf) - 1] = '\0';
633 *em = MkStrCpy(embf, &tlen);
638 * An error was detected.
640 * Advance to the next option character.
642 * Return the character causing the error.
644 if (opt[GOx1][++GOx2] == '\0') {
650 if (*(cp + 1) == ':') {
653 * The option may have a following value. The caller decides if it does.
655 * Don't indicate that an option of ``--'' is a possible value.
657 * Finally, on the assumption that the caller will decide that the possible
658 * value belongs to the option, position to the option following the
659 * possible value, so that the next call to GetOpt() will find it.
661 if(opt[GOx1][GOx2 + 1] != '\0') {
662 GOv = &opt[GOx1++][GOx2];
663 } else if (++GOx1 >= ct)
667 if (strcmp(GOv, "--") == 0)
676 * The option character stands alone with no following value.
678 * Advance to the next option character.
680 if (opt[GOx1][++GOx2] == '\0') {
687 * Return the option character.
694 * IsLsofExec() -- see if lsof is executable
700 char buf[2048]; /* temporary buffer */
701 int len; /* temporary length */
703 (void) getlsofpath();
704 if (access(LsofPath, X_OK) < 0) {
705 (void) snprintf(buf, sizeof(buf) - 1,
706 "ERROR!!! can't execute %s: %s", LsofPath, strerror(errno));
707 return(MkStrCpy(buf, &len));
709 return((char *)NULL);
714 * LTlibClean() -- clean up LTlib resource accesses
725 * MkStrCpy() -- make string copy
730 char *src; /* string source to copy */
731 int *len; /* returned length allocation */
733 char *rp; /* return pointer */
734 int srclen; /* source string length */
737 (void) fprintf(stderr, "ERROR!!! no string supplied to MkStrCpy()\n");
740 srclen = (int)strlen(src);
742 if (!(rp = (char *)malloc(srclen))) {
743 (void) fprintf(stderr, "ERROR!!! MkStrCpy() -- no malloc() space");
746 (void) strcpy(rp, src);
752 * PrtMsg() -- print message
757 char *mp; /* message pointer -- may be NULL to
758 * trigger space prefix initialization
760 char *pn; /* program name */
762 static int pfxlen = -1; /* prefix length, based on program */
763 /* name -- computed on first call
764 * when pfxlen == -1 */
765 static char *pfx = (char *)NULL; /* prefix (spaces) */
766 int ti; /* temporary index */
771 * This is the first call. Compute the prefix length and build the
777 pfxlen = (int)(strlen(pn));
778 pfxlen += (int)strlen(" ... ");
779 if (!(pfx = (char *)malloc(pfxlen + 1))) {
780 (void) printf( "ERROR!!! not enough space for %d space prefix\n",
784 for (ti = 0; ti < pfxlen; ti++) {
791 * Process the message.
794 (void) printf("%s", pfx);
796 (void) printf("%s\n", mp);
803 * PrtMsgX() -- print message and exit
807 PrtMsgX(mp, pn, f, xv)
808 char *mp; /* message pointer */
809 char *pn; /* program name */
810 void (*f)(); /* clean-up function pointer */
811 int xv; /* exit value */
823 * RdFrLsof() -- read from lsof
828 int *nf; /* number of fields receiver */
829 char **em; /* error message pointer receiver */
831 char buf[2048]; /* temporary buffer */
832 int bufl = (int)sizeof(buf); /* size of buf[] */
833 char *blim = &buf[bufl - 1]; /* buf[] limit (last character
835 char *fsp; /* field start pointer */
836 char *tcp; /* temporary character pointer */
837 LTfldo_t *tfop; /* temporary field output pointer */
838 int ti; /* temporary index */
839 int tlen; /* remporary length */
840 char *vp; /* value character pointer */
845 return((LTfldo_t *)NULL);
847 *em = "ERROR!!! RdFrLsof() not given a count return pointer";
848 return((LTfldo_t *)NULL);
853 * If fields are in use, release their resources.
855 for (ti = 0, tfop = Fo; (ti < Ufo); ti++, tfop++) {
857 (void) free((void *)tfop->v);
861 * Read a line from lsof.
863 if (!fgets(buf, bufl - 2, LsofFs)) {
866 * An lsof pipe EOF has been reached. Indicate that with a NULL
867 * pointer return, coupled with a NULL error message return pointer
868 * (set above), and a field count of zero (set above).
870 return((LTfldo_t *)NULL);
873 * Parse the lsof line, allocating field output structures as appropriate.
875 * It is expected that fields will end in a NUL ('\0') or a NL ('\0') and
876 * that a NL ends all fields in the lsof line.
878 for (tcp = buf, Ufo = 0; (*tcp != '\n') && (tcp < blim); tcp++) {
881 * Start a new field. The first character is the LSOF_FID_*.
883 * First allocate an LTfldo_t structure.
888 * More LTfldo_t space is required.
890 Afo += LT_FLDO_ALLOC;
891 tlen = (int)(Afo * sizeof(LTfldo_t));
893 Fo = (LTfldo_t *)realloc(Fo, tlen);
895 Fo = (LTfldo_t *)malloc(tlen);
899 * A serious error has occurred; no LTfldo_t space is available.
901 (void) snprintf(buf, bufl,
902 "ERROR!!! RdFrLsof() can't allocate %d pointer bytes",
904 *em = MkStrCpy(buf, &ti);
906 return((LTfldo_t *)NULL);
910 tfop->v = (char *)NULL;
913 * Save the LSOF_FID_* character. Then compute the field value length,
914 * and make a copy of it.
919 while (*tcp && (*tcp != '\n') && (tcp < blim)) {
923 if (!(vp = (char *)malloc(tlen + 1))) {
926 * A serious error has occurred; there's no space for the field value.
928 (void) snprintf(buf, bufl,
929 "ERROR!!! RdFrLsof() can't allocate %d field bytes", tlen + 1);
930 *em = MkStrCpy(buf, &ti);
932 return((LTfldo_t *)NULL);
934 (void) memcpy((void *)vp, (void *)fsp, tlen);
942 * The lsof line has no NL terminator; that's an error.
944 *em = "ERROR!!! RdFrLsof() didn't find a NL";
946 return((LTfldo_t *)NULL);
950 * The end of the lsof line has been reached. If no fields were assembled,
951 * return an error indicate. Otherwise return the fields and their count.
954 *em = "ERROR!!! RdFrLsof() read an empty lsof line";
956 return((LTfldo_t *)NULL);
965 * ScanArg() -- scan arguments
969 ScanArg(ac, av, opt, pn)
970 int ac; /* argument count */
971 char *av[]; /* argument pointers */
972 char *opt; /* option string */
973 char *pn; /* program name */
975 char *em; /* pointer to error message returned by
977 char embf[2048]; /* error message buffer */
978 int rv = 0; /* return value */
979 int tc; /* temporary character value */
981 * Preset possible argument values.
985 (void) free((void *)LTopt_p);
986 LTopt_p = (char *)NULL;
989 * Process the options according to the supplied option string.
991 while ((tc = GetOpt(ac, av, opt, &em, pn)) != EOF) {
1002 if (!GOv || *GOv == '-' || *GOv == '+') {
1004 (void) PrtMsg("ERROR!!! -p not followed by a path", pn);
1010 (void) snprintf(embf, sizeof(embf) - 1,
1011 "ERROR!!! unknown option: %c", tc);
1015 for (; GOx1 < ac; GOx1++) {
1018 * Report extraneous arguments.
1021 (void) snprintf(embf, sizeof(embf) - 1,
1022 "ERROR!!! extraneous option: \"%s\"", av[GOx1]);
1030 * StopLsof() -- stop a running lsof process and close the pipe from it
1041 * An lsof child process may be active. Wait for (or kill) it.
1043 pid = wait3(NULL, WNOHANG, NULL);
1044 if (pid != LsofPid) {
1045 (void) kill(LsofPid, SIGKILL);
1047 pid = wait3(NULL, WNOHANG, NULL);
1056 * x2dev() -- convert hex string to device number
1061 char *x; /* hex string */
1062 char **em; /* error message receiver */
1064 char buf[2048]; /* temporary message buffer */
1065 int c; /* character holder */
1066 X2DEV_T dev; /* device number result */
1067 char *wx; /* working hex string pointer */
1068 int xl; /* hex string length */
1071 *em = "ERROR!!! no hex string supplied to x2dev()";
1074 wx = strncasecmp(x, "0x", 2) ? x : (x + 2);
1075 if (((xl = (int)strlen(wx)) < 1) || (xl > XDINDEV)) {
1076 (void) snprintf(buf, sizeof(buf) - 1,
1077 "ERROR!!! x2dev(\"%s\") bad length: %d", x, xl + 2);
1078 buf[sizeof(buf) - 1] = '\0';
1079 *em = MkStrCpy(buf, &c);
1083 * Assemble the device number result from the hex string.
1085 for (dev = (X2DEV_T)0; *wx; wx++) {
1086 if (isdigit((unsigned char)*wx)) {
1087 dev = (dev << 4) | (unsigned int)(((int)*wx - (int)'0') & 0xf);
1090 c = (int) tolower((unsigned char)*wx);
1091 if ((c >= (int)'a') && (c <= (int)'f')) {
1092 dev = (dev << 4) | (unsigned int)((c - 'a' + 10) & 0xf);
1095 (void) snprintf(buf, sizeof(buf) - 1,
1096 "ERROR!!! x2dev(\"%s\") non-hex character: %c", x, c);
1097 *em = MkStrCpy(buf, &c);
1100 * Return result and no error indication.