Imported Upstream version 4.87
[platform/upstream/lsof.git] / tests / LTlib.c
1 /*
2  * LTlib.c -- the lsof test library
3  *
4  * V. Abell
5  * Purdue University
6  */
7
8
9 /*
10  * Copyright 2002 Purdue Research Foundation, West Lafayette, Indiana
11  * 47907.  All rights reserved.
12  *
13  * Written by V. Abell.
14  *
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.
17  *
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:
21  *
22  * 1. Neither the authors nor Purdue University are responsible for any
23  *    consequences of the use of this software.
24  *
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.
28  *
29  * 3. Altered versions must be plainly marked as such, and must not be
30  *    misrepresented as being the original software.
31  *
32  * 4. This notice may not be removed or altered.
33  */
34
35 #ifndef lint
36 static char copyright[] =
37 "@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
38 #endif
39
40 #include "LsofTest.h"
41
42
43 /*
44  * Pre-defintions that may be changed by a specific dialect
45  */
46
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) */
51
52
53 #if     defined(LT_DIAL_aix)
54 /*
55  * AIX-specific items
56  */
57
58 #include <sys/sysmacros.h>
59
60 # if    defined(LT_AIXA) && LT_AIXA>=1
61
62 /*
63  * Note: the DEVNO64 and ISDEVNO54 #define's come from <sys/sysmacros.h>, but
64  * only when _KERNEL is #define'd.
65  */
66
67 #undef  DEVNO64
68 #define DEVNO64         0x8000000000000000LL
69 #undef  ISDEVNO64
70 #define ISDEVNO64(d)    (((ulong)(d) & DEVNO64) ? 1 : 0)
71
72 /*
73  * Define major and minor extraction macros that work on 64 bit AIX
74  * architectures.
75  */
76  
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))
79 #undef  X2DEV_T
80 #define X2DEV_T         unsigned long long
81 #undef  XDINDEV
82 #define XDINDEV         16
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 */
86
87 #endif  /* defined(LT_DIAL_aix) */
88
89
90 #if     defined(LT_DIAL_bsdi)
91 /*
92  * BSDI-specific items
93  */
94
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) */
100
101
102 #if     defined(LT_DIAL_osr)
103 /*
104  * OpenUNIX-specific items
105  */
106
107 #include <sys/sysmacros.h>
108 #endif  /* defined(LT_DIAL_osr) */
109
110
111 #if     defined(LT_DIAL_ou)
112 /*
113  * OpenUNIX-specific items
114  */
115
116 #include <sys/mkdev.h>
117 #endif  /* defined(LT_DIAL_ou) */
118
119
120 #if     defined(LT_DIAL_solaris)
121 /*
122  * Solaris-specific items
123  */
124
125 #include <sys/sysmacros.h>
126
127
128 /*
129  * Define maximum major device number in a stat(2) dev_t
130  */
131
132 # if    LT_VERS>=20501
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 */
139
140 #define major_S(dev)    ((int)((dev >> L_BITSMINOR) & LT_MJX))
141 #define minor_S(dev)    ((int)(dev & L_MAXMIN))
142
143 # if    defined(LT_K64)
144
145 /*
146  * Solaris 64 bit kernel
147  */
148
149 #undef  X2DEV_T
150 #define X2DEV_T         unsigned long long
151 #undef  XDINDEV
152 #define XDINDEV         16
153
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) */
157
158 /*
159  * Solaris 32 bit kernel
160  */
161
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))
164 # endif /* LT_K64 */
165 #endif  /* defined(LT_DIAL_solaris) */
166
167
168 #if     defined(LT_DIAL_uw)
169 /*
170  * UnixWare-specific items
171  */
172
173 #include <sys/mkdev.h>
174 #endif  /* defined(LT_DIAL_uw) */
175
176
177 /*
178  * Global variables
179  */
180
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
188                                          * to be issued */
189
190
191 /*
192  * Local static variables
193  */
194
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 */
201
202
203 /*
204  * Local function prototypes
205  */
206
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,
210                          char *pn));
211 _PROTOTYPE(static X2DEV_T x2dev,(char *x, char **em));
212
213
214 /*
215  * Default major, minor, and unit macros.
216  */
217
218 #if     !defined(major_S)
219 #define major_S         major
220 #endif  /* defined(major_S) */
221
222 #if     !defined(minor_S)
223 #define minor_S         minor
224 #endif  /* defined(minor_S) */
225
226 #if     !defined(unit_S)
227 #define unit_S(x)       0
228 #endif  /* defined(unit_S) */
229
230 #if     !defined(major_X)
231 #define major_X(dp, em) major(x2dev(dp, em))
232 #endif  /* defined(major_X) */
233
234 #if     !defined(minor_X)
235 #define minor_X(dp, em) minor(x2dev(dp, em))
236 #endif  /* defined(minor_X) */
237
238 #if     !defined(unit_X)
239 #define unit_X(dp, em)  0
240 #endif  /* defined(unit_X) */
241
242
243 /*
244  * CanRdKmem() -- can lsof read kernel memory devices?
245  */
246
247 char *
248 CanRdKmem()
249 {
250
251 #if     defined(LT_KMEM)
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 */
258 /*
259  * Get the lsof path.  If it is not the default, check no further.
260  */
261     (void) getlsofpath();
262     if (!strcmp(LsofPath, LT_DEF_LSOF_PATH))
263         return((char *)NULL);
264 /*
265  * Check /dev/kmem access.
266  */
267     dn = "/dev/kmem";
268     if (stat(dn, &sb)) {
269         em = "stat";
270
271 kmem_error:
272
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));
277     }
278     if ((fd = open(dn, O_RDONLY, 0)) < 0) {
279         em = "open";
280         goto kmem_error;
281     }
282     (void) close(fd);
283 /*
284  * Check /dev/mem access.
285  */
286     dn = "/dev/mem";
287     if (stat(dn, &sb)) {
288
289     /*
290      * If /dev/mem can't be found, ignore the error.
291      */
292         return((char *)NULL);
293     }
294     if ((fd = open(dn, O_RDONLY, 0)) < 0) {
295         em = "open";
296         goto kmem_error;
297     }
298     (void) close(fd);
299 #endif  /* defined(LT_KMEM) */
300
301     return((char *)NULL);
302 }
303
304
305 /*
306  * closepipe() -- close pipe from lsof
307  */
308
309 static void
310 closepipe()
311 {
312     if (LsofFd >= 0) {
313
314     /*
315      * A pipe from lsof is open.  Close it and the associated stream.
316      */
317         if (LsofFs) {
318             (void) fclose(LsofFs);
319             LsofFs = (FILE *)NULL;
320         }
321         (void) close(LsofFd);
322         LsofFd = -1;
323     }
324 }
325
326
327 /*
328  * ConvLsofDev() -- convert lsof device string
329  *
330  * Note: this function is dialect-specific.
331  */
332
333 char *
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 */
339 {
340     char *dp;                   /* device pointer */
341     char *em;                   /* error message pointer */
342     int tlen;                   /* temporary length */
343 /*
344  * Check function arguments.
345  *
346  * Establish values for decoding the device string.
347  */
348     if (!dev)
349         return("ERROR!!!  no ConvLsofDev() device");
350     if (!ldev)
351         return("ERROR!!!  no ConvLsofDev() result pointer");
352     if (strncmp(dev, "0x", 2))
353         return("ERROR!!!  no leading 0x in ConvLsofDev() device");
354     dp = dev + 2;
355     if (((tlen = (int)strlen(dp)) < 1) || (tlen > XDINDEV))
356         return("ERROR!!!  bad ConvLsofDev() device length");
357 /*
358  * Use the pre-defined *_X() macros to do the decomposition.
359  */
360     ldev->maj = (unsigned int)major_X(dp, &em);
361     if (em)
362         return(em);
363     ldev->min = (unsigned int)minor_X(dp, &em);
364     if (em)
365         return(em);
366     ldev->unit = (unsigned int)unit_X(dp, &em);
367     return(em);
368 }
369
370
371 /*
372  * ConvStatDev() -- convert stat(2) device number
373  *
374  * Note: this function is dialect-specific.
375  */
376
377 char *
378 ConvStatDev(dev, ldev)
379     dev_t *dev;                 /* device number to be converted */
380     LTdev_t *ldev;              /* results are returned to this structure */
381 {
382
383 /*
384  * Check function arguments.
385  */
386     if (!dev)
387         return("ERROR!!!  no ConvStatDev() device");
388     if (!ldev)
389         return("ERROR!!!  no ConvStatDev() result pointer");
390 /*
391  * Use the pre-defined *_S() macros to do the decomposition.
392  */
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);
397 }
398
399
400 /*
401  * ExecLsof() -- execute lsof with full field output and a NUL field terminator
402  *               in a child process
403  */
404
405 char *
406 ExecLsof(opt)
407     char **opt;                         /* lsof options -- a pointer to an
408                                          * array of character pointers,
409                                          * terminated by a NULL pointer */
410 {
411     static char **av = (char **)NULL;   /* lsof argument vector, dynamically
412                                          * allocated */
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
421                                          * pointer */
422     int ti;                             /* temporary integer */
423     int tlen;                           /* temporary length */
424     pid_t tpid;                         /* temporary PID holder */
425 /*
426  * It's an error if lsof is already in execution or if no lsof options
427  * were supplied.
428  */
429     (void) getlsofpath();
430     if (LsofPid)
431         return("ERROR!!!  ExecLsof() says lsof is already in execution");
432     if (!opt)
433         return("ERROR!!!  no ExecLsof() option list");
434     for (optc = 0, tcpp = opt; *tcpp; optc++, tcpp++)
435         ;
436 /*
437  * Make sure lsof is executable.
438  */
439     if ((em = IsLsofExec()))
440         return(em);
441 /*
442  * Open a pipe through which lsof can return output.
443  */
444     if (pipe(p)) {
445         (void) snprintf(buf, sizeof(buf) - 1,
446             "ERROR!!!  can't open pipe: %s", strerror(errno));
447         return(MkStrCpy(buf, &ti));
448     }
449 /*
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.
453  */
454     if ((optc + 4) > ava) {
455         tlen = (int)(sizeof(char *) * (optc + 4));
456         if (!av)
457             av = (char **)malloc(tlen);
458         else
459             av = (char **)realloc((void *)av, tlen);
460         if (!av) {
461             (void) snprintf(buf, sizeof(buf) - 1,
462                 "LTlib: ExecLsof() can't allocat pointers for %d arguments",
463                 optc + 4);
464             return(MkStrCpy(buf, &ti));
465         }
466         ava = optc + 4;
467     }
468     for (ti = 0, tcpp = opt; ti < (optc + 3); ti++) {
469         switch(ti) {
470         case 0:
471             av[ti] = "lsof";
472             break;
473         case 1:
474             av[ti] = "-wFr";
475             break;
476         case 2:
477             av[ti] = "-F0";
478             break;
479         default:
480             av[ti] = *tcpp;
481             tcpp++;
482         }
483     }
484     av[ti] = (char *)NULL;
485 /*
486  * Fork a child process to run lsof.
487  */
488     switch((tpid = fork())) {
489     case (pid_t)0:
490
491     /*
492      * This is the child process.
493      *
494      * First close all file descriptors except the output side of the pipe.
495      *
496      * Make the output side of the pipe STDOUT and STDERR.
497      */
498         for (fd = 0, nf = getdtablesize(); fd < nf; fd++) {
499             if (fd == p[1])
500                 continue;
501             (void) close(fd);
502         }
503         if (p[1] != 1)
504             (void) dup2(p[1], 1);
505         if (p[1] != 2)
506             (void) dup2(p[1], 2);
507         if ((p[1] != 1) && (p[1] != 2))
508             (void) close(p[1]);
509     /*
510      * Execute lsof.
511      */
512         (void) execv(LsofPath, av);
513         _exit(0);                               /* (Shouldn't get here.) */
514     case (pid_t)-1:
515
516     /*
517      * A fork error occurred.  Form and return a message.
518      */
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));
523     default:
524
525     /*
526      * This is the parent.
527      *
528      * Save the lsof child PID.
529      *
530      * Close the output side of the pipe.
531      *
532      * Save the input side of the pipe as LsofFd; open a stream for it.
533      */
534         LsofPid = tpid;
535         (void) close(p[1]);
536         LsofFd = p[0];
537         if (!(LsofFs = fdopen(LsofFd, "r")))
538             return("ERROR!!!  ExecLsof() can't open stream to lsof output FD");
539     }
540 /*
541  * Wait a bit for lsof to start and put something in its pipe, then return
542  * an "All is well." response.
543  */
544     sleep(1);
545     return((char *)NULL);
546 }
547
548
549 /*
550  * getlsofpath() -- get lsof path, either from LT_LSOF_PATH in the environment
551  *                  or from LT_DEF_LSOF_PATH
552  */
553
554 static void
555 getlsofpath()
556 {
557     char *tcp;                          /* temporary character pointer */
558     int ti;                             /* temporary integer */
559
560     if (LsofPath)
561         return;
562     if ((tcp = getenv("LT_LSOF_PATH")))
563         LsofPath = MkStrCpy(tcp, &ti);
564     else
565         LsofPath = LT_DEF_LSOF_PATH;
566 }
567
568
569 /*
570  * GetOpt() -- Local get option
571  *
572  * Borrowed from lsof's main.c source file.
573  *
574  * Liberally adapted from the public domain AT&T getopt() source,
575  * distributed at the 1985 UNIFORM conference in Dallas
576  *
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.
580  */
581
582 static int
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 */
588     char *pn;
589 {
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
594                                          * MkStrCpy() */
595
596     *em = (char *)NULL;
597     if (GOx2 == 0) {
598
599     /*
600      * Move to a new entry of the option array.
601      *
602      * EOF if:
603      *
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 ``++''.
608      */
609         if (GOx1 >= ct
610         ||  (opt[GOx1][0] != '-' && opt[GOx1][0] != '+')
611         ||  !opt[GOx1][1])
612             return(EOF);
613         if (strcmp(opt[GOx1], "--") == 0 || strcmp(opt[GOx1], "++") == 0) {
614             GOx1++;
615             return(EOF);
616         }
617         GOx2 = 1;
618     }
619 /*
620  * Flag `:' option character as an error.
621  *
622  * Check for a rule on this option character.
623  */
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);
634     }
635     if (*em) {
636
637     /*
638      * An error was detected.
639      *
640      * Advance to the next option character.
641      *
642      * Return the character causing the error.
643      */
644         if (opt[GOx1][++GOx2] == '\0') {
645             GOx1++;
646             GOx2 = 0;
647         }
648         return(c);
649     }
650     if (*(cp + 1) == ':') {
651
652     /*
653      * The option may have a following value.  The caller decides if it does.
654      *
655      * Don't indicate that an option of ``--'' is a possible value.
656      *
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.
660      */
661         if(opt[GOx1][GOx2 + 1] != '\0') {
662             GOv = &opt[GOx1++][GOx2];
663         } else if (++GOx1 >= ct)
664             GOv = (char *)NULL;
665         else {
666             GOv = opt[GOx1];
667             if (strcmp(GOv, "--") == 0)
668                 GOv = (char *)NULL;
669             else
670                 GOx1++;
671         }
672         GOx2 = 0;
673      } else {
674
675     /*
676      * The option character stands alone with no following value.
677      *
678      * Advance to the next option character.
679      */
680         if (opt[GOx1][++GOx2] == '\0') {
681             GOx2 = 0;
682             GOx1++;
683         }
684         GOv = (char *)NULL;
685     }
686 /*
687  * Return the option character.
688  */
689     return(c);
690 }
691
692
693 /*
694  * IsLsofExec() -- see if lsof is executable
695  */
696
697 char *
698 IsLsofExec()
699 {
700     char buf[2048];                     /* temporary buffer */
701     int len;                            /* temporary length */
702
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));
708     }
709     return((char *)NULL);
710 }
711
712
713 /*
714  * LTlibClean() -- clean up LTlib resource accesses
715  */
716
717 void
718 LTlibClean()
719 {
720     (void) StopLsof();
721 }
722
723
724 /*
725  * MkStrCpy() -- make string copy
726  */
727
728 char *
729 MkStrCpy(src, len)
730     char *src;                  /* string source to copy */
731     int *len;                   /* returned length allocation */
732 {
733     char *rp;                   /* return pointer */
734     int srclen;                 /* source string length */
735
736     if (!src) {
737         (void) fprintf(stderr, "ERROR!!!  no string supplied to MkStrCpy()\n");
738         exit(1);
739     }
740     srclen = (int)strlen(src);
741     *len = srclen++;
742     if (!(rp = (char *)malloc(srclen))) {
743         (void) fprintf(stderr, "ERROR!!!  MkStrCpy() -- no malloc() space");
744         exit(1);
745     }
746     (void) strcpy(rp, src);
747     return(rp);
748 }
749
750
751 /*
752  * PrtMsg() -- print message
753  */
754
755 void
756 PrtMsg(mp, pn)
757     char *mp;                           /* message pointer -- may be NULL to
758                                          * trigger space prefix initialization
759                                          */
760     char *pn;                           /* program name */
761 {
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 */
767
768     if (pfxlen == -1) {
769
770     /*
771      * This is the first call.  Compute the prefix length and build the
772      * prefix.
773      */
774         if (!pn)
775             pfxlen = 0;
776         else
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",
781                 pfxlen);
782             exit(1);
783         }
784         for (ti = 0; ti < pfxlen; ti++) {
785             pfx[ti] = ' ';
786         }
787         pfx[pfxlen] = '\0';
788         MsgStat = 0;
789     }
790 /*
791  * Process the message.
792  */
793     if (MsgStat)
794         (void) printf("%s", pfx);
795     if (mp && *mp) {
796         (void) printf("%s\n", mp);
797         MsgStat = 1;
798     }
799 }
800
801
802 /*
803  * PrtMsgX() -- print message and exit
804  */
805
806 void
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 */
812 {
813     if (mp)
814         PrtMsg(mp, pn);
815     if (f)
816         (void) (*f)();
817     (void) LTlibClean();
818     exit(xv);
819 }
820
821
822 /*
823  * RdFrLsof() -- read from lsof
824  */
825
826 LTfldo_t *
827 RdFrLsof(nf, em)
828     int *nf;                            /* number of fields receiver */
829     char **em;                          /* error message pointer receiver */
830 {
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
834                                          * address) */
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 */
841 /*
842  * Check for errors.
843  */
844     if (!em)
845         return((LTfldo_t *)NULL);
846     if (!nf) {
847         *em = "ERROR!!!  RdFrLsof() not given a count return pointer";
848         return((LTfldo_t *)NULL);
849     }
850     *em = (char *)NULL;
851     *nf = 0;
852 /*
853  * If fields are in use, release their resources.
854  */
855     for (ti = 0, tfop = Fo; (ti < Ufo); ti++, tfop++) {
856         if (tfop->v)
857             (void) free((void *)tfop->v);
858     }
859     Ufo = 0;
860 /*
861  * Read a line from lsof.
862  */
863     if (!fgets(buf, bufl - 2, LsofFs)) {
864
865     /*
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).
869      */
870         return((LTfldo_t *)NULL);
871     }
872 /*
873  * Parse the lsof line, allocating field output structures as appropriate.
874  *
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.
877  */
878     for (tcp = buf, Ufo = 0; (*tcp != '\n') && (tcp < blim); tcp++) {
879
880     /*
881      * Start a new field.  The first character is the LSOF_FID_*.
882      *
883      * First allocate an LTfldo_t structure.
884      */
885         if (Ufo >= Afo) {
886
887         /*
888          * More LTfldo_t space is required.
889          */
890              Afo += LT_FLDO_ALLOC;
891              tlen = (int)(Afo * sizeof(LTfldo_t));
892              if (Fo)
893                 Fo = (LTfldo_t *)realloc(Fo, tlen);
894              else
895                 Fo = (LTfldo_t *)malloc(tlen);
896             if (!Fo) {
897
898             /*
899              * A serious error has occurred; no LTfldo_t space is available.
900              */
901                 (void) snprintf(buf, bufl,
902                     "ERROR!!!  RdFrLsof() can't allocate %d pointer bytes",
903                     tlen);
904                 *em = MkStrCpy(buf, &ti);
905                 *nf = -1;
906                 return((LTfldo_t *)NULL);
907             }
908         }
909         tfop = Fo + Ufo;
910         tfop->v = (char *)NULL;
911         Ufo++;
912     /*
913      * Save the LSOF_FID_* character.  Then compute the field value length,
914      * and make a copy of it.
915      */
916         tfop->ft = *tcp++;
917         fsp = tcp;
918         tlen = 0;
919         while (*tcp && (*tcp != '\n') && (tcp < blim)) {
920              tcp++;
921              tlen++;
922         }
923         if (!(vp = (char *)malloc(tlen + 1))) {
924
925         /*
926          * A serious error has occurred; there's no space for the field value.
927          */
928             (void) snprintf(buf, bufl,
929                 "ERROR!!!  RdFrLsof() can't allocate %d field bytes", tlen + 1);
930             *em = MkStrCpy(buf, &ti);
931             *nf = -1;
932             return((LTfldo_t *)NULL);
933         }
934         (void) memcpy((void *)vp, (void *)fsp, tlen);
935         vp[tlen] = '\0';
936         tfop->v = vp;
937         if (*tcp == '\n')
938             break;
939         if (tcp >= blim) {
940
941         /*
942          * The lsof line has no NL terminator; that's an error.
943          */
944             *em = "ERROR!!! RdFrLsof() didn't find a NL";
945             *nf = -1;
946             return((LTfldo_t *)NULL);
947         }
948     }
949 /*
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.
952  */
953     if (!Ufo) {
954         *em = "ERROR!!! RdFrLsof() read an empty lsof line";
955         *nf = -1;
956         return((LTfldo_t *)NULL);
957     }
958     *nf = Ufo;
959     *em = (char *)NULL;
960     return(Fo);
961 }
962
963
964 /*
965  * ScanArg() -- scan arguments
966  */
967
968 int
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 */
974 {
975     char *em;                           /* pointer to error message returned by
976                                          * GetOpt() */
977     char embf[2048];                    /* error message buffer */
978     int rv = 0;                         /* return value */
979     int tc;                             /* temporary character value */
980 /*
981  * Preset possible argument values.
982  */
983     LTopt_h = 0;
984     if (LTopt_p) {
985         (void) free((void *)LTopt_p);
986         LTopt_p = (char *)NULL;
987     }
988 /*
989  * Process the options according to the supplied option string.
990  */
991     while ((tc = GetOpt(ac, av, opt, &em, pn)) != EOF) {
992         if (em) {
993             rv = 1;
994             PrtMsg(em, pn);
995             continue;
996         }
997         switch (tc) {
998         case 'h':
999             LTopt_h = 1;
1000             break;
1001         case 'p':
1002             if (!GOv || *GOv == '-' || *GOv == '+') {
1003                 rv = 1;
1004                 (void) PrtMsg("ERROR!!!  -p not followed by a path", pn);
1005             } else
1006                 LTopt_p = GOv;
1007             break;
1008         default:
1009             rv = 1;
1010             (void) snprintf(embf, sizeof(embf) - 1,
1011                 "ERROR!!!  unknown option: %c", tc);
1012             PrtMsg(embf, pn);
1013         }
1014     }
1015     for (; GOx1 < ac; GOx1++) {
1016
1017     /*
1018      * Report extraneous arguments.
1019      */
1020         rv = 1;
1021         (void) snprintf(embf, sizeof(embf) - 1,
1022             "ERROR!!!  extraneous option: \"%s\"", av[GOx1]);
1023         PrtMsg(embf, pn);
1024     }
1025     return(rv);
1026 }
1027
1028
1029 /*
1030  * StopLsof() -- stop a running lsof process and close the pipe from it
1031  */
1032
1033 void
1034 StopLsof()
1035 {
1036     pid_t pid;
1037
1038     if (LsofPid) {
1039
1040     /*
1041      * An lsof child process may be active.  Wait for (or kill) it.
1042      */
1043         pid = wait3(NULL, WNOHANG, NULL);
1044         if (pid != LsofPid) {
1045             (void) kill(LsofPid, SIGKILL);
1046             sleep(2);
1047             pid = wait3(NULL, WNOHANG, NULL);
1048         }
1049         LsofPid = (pid_t)0;
1050     }
1051     (void) closepipe();
1052 }
1053
1054
1055 /*
1056  * x2dev() -- convert hex string to device number
1057  */
1058
1059 static X2DEV_T
1060 x2dev(x, em)
1061     char *x;                            /* hex string */
1062     char **em;                          /* error message receiver */
1063 {
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 */
1069
1070     if (!x || !*x) {
1071         *em = "ERROR!!!  no hex string supplied to x2dev()";
1072         return(0);
1073     }
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);
1080         return(0);
1081     }
1082 /*
1083  * Assemble the device number result from the hex string.
1084  */
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);
1088             continue;
1089         }
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);
1093             continue;
1094         }
1095         (void) snprintf(buf, sizeof(buf) - 1,
1096             "ERROR!!!  x2dev(\"%s\") non-hex character: %c", x, c);
1097         *em = MkStrCpy(buf, &c);
1098     }
1099 /*
1100  * Return result and no error indication.
1101  */
1102     *em = (char *)NULL;
1103     return(dev);
1104 }