1/*
2 * LTdnlc.c -- Lsof Test Dynamic Name Lookup Cache test
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
36static char copyright[] =
37"@(#) Copyright 2002 Purdue Research Foundation.\nAll rights reserved.\n";
38#endif
39
40#include "LsofTest.h"
41#include "lsof_fields.h"
42
43
44/*
45 * Pre-definitions that may be revoked by specific dialects
46 */
47
48#define	DO_TEST				/* do the test */
49
50
51/*
52 * Dialect-specific items
53 */
54
55
56#if	defined(LT_DIAL_aix)
57/*
58 * AIX-specific items
59 */
60
61#undef	DO_TEST
62#endif	/* defined(LT_DIAL_aix) */
63
64
65#if   defined(LT_DIAL_darwin)
66/*
67 * Darwin-specific items
68 */
69
70# if	LT_VERS<800
71#undef        DO_TEST
72# endif	/* LT_VERS<800 */
73#endif        /* defined(LT_DIAL_darwin) */
74
75
76/*
77 * Local definitions
78 */
79
80#define	ATTEMPT_CT	5		/* number of lsof CWD lookup attempts */
81#define	LSPATH		"/bin/ls"	/* path to ls(1) */
82#define	SUCCESS_THRESH	50.0		/* success threshold */
83
84
85/*
86 * Globals
87 */
88
89pid_t MyPid = (pid_t)0;		/* PID of this process */
90char *Pn = (char *)NULL;	/* program name */
91
92
93/*
94 * Local function prototypes
95 */
96
97_PROTOTYPE(static void cleanup,(void));
98_PROTOTYPE(static char *FindLsofCwd,(int *ff, LTdev_t *cwddc, char *ibuf));
99
100
101/*
102 * Main program
103 */
104
105int
106main(argc, argv)
107    int argc;				/* argument count */
108    char *argv[];			/* arguments */
109{
110    char buf[2048];			/* temporary buffer */
111    char cwd[MAXPATHLEN + 1];		/* CWD */
112    LTdev_t cwddc;			/* CWD device components */
113    char *em;				/* error message pointer */
114    int ff;				/* FindFile() file-found flag */
115    int fpathct;			/* full path found count */
116    char ibuf[32];			/* inode buffer */
117    char lsbuf[2048 + MAXPATHLEN + 1];	/* ls(1) system() command */
118    double pct;				/* performance percentage */
119    struct stat sb;			/* CWD stat(2) results */
120    int ti;				/* temporary index */
121    int xv = 0;				/* exit value */
122/*
123 * Get program name and PID, issue start message, and build space prefix.
124 */
125    if ((Pn = strrchr(argv[0], '/')))
126	Pn++;
127    else
128	Pn = argv[0];
129    MyPid = getpid();
130    (void) printf("%s ... ", Pn);
131    (void) fflush(stdout);
132    PrtMsg((char *)NULL, Pn);
133/*
134 * Process arguments.
135 */
136    if (ScanArg(argc, argv, "h", Pn))
137	xv = 1;
138    if (xv || LTopt_h) {
139	(void) PrtMsg("usage: [-h] [-p path]", Pn);
140	PrtMsgX("       -h       print help (this panel)", Pn, cleanup, xv);
141    }
142
143#if	!defined(DO_TEST)
144/*
145 * If the dialect has disabled the test, echo that result and exit with
146 * a successful return code.
147 */
148    (void) PrtMsgX(LT_DONT_DO_TEST, Pn, cleanup, 0);
149#endif	/* !defined(DO_TEST) */
150
151/*
152 * See if lsof can be executed and can access kernel memory.
153 */
154    if ((em = IsLsofExec()))
155	(void) PrtMsgX(em, Pn, cleanup, 1);
156    if ((em = CanRdKmem()))
157	(void) PrtMsgX(em, Pn, cleanup, 1);
158/*
159 * Get the CWD and form the ls(1) system() command.
160 */
161
162#if	defined(USE_GETCWD)
163    em = "getcwd";
164    if (!getcwd(cwd, sizeof(cwd)))
165#else	/* ! defined(USE_GETCWD) */
166    em = "getwd";
167    if (!getwd(cwd))
168#endif	/* defined(USE_GETCWD) */
169
170    {
171	(void) snprintf(buf, sizeof(buf) - 1,
172	    "ERROR!!!  %s() error: %s", em, strerror(errno));
173	buf[sizeof(buf) - 1] = '\0';
174	(void) PrtMsgX(buf, Pn, cleanup, 1);
175    }
176    (void) snprintf(lsbuf, sizeof(lsbuf) - 1, "%s %s > /dev/null 2>&1",
177	LSPATH, cwd);
178/*
179 * Get the CWD stat(2) results.
180 */
181    if (stat(cwd, &sb)) {
182	(void) snprintf(buf, sizeof(buf) - 1,
183	    "ERROR!!!  stat(%s) error: %s", cwd, strerror(errno));
184	buf[sizeof(buf) - 1] = '\0';
185	(void) PrtMsgX(buf, Pn, cleanup, 1);
186    }
187    if ((em = ConvStatDev(&sb.st_dev, &cwddc)))
188	PrtMsgX(em, Pn, cleanup, 1);
189    (void) snprintf(ibuf, sizeof(ibuf) - 1, "%u", (unsigned int)sb.st_ino);
190    ibuf[sizeof(ibuf) - 1] = '\0';
191/*
192 * Loop ATTEMPT_CT times.
193 */
194    for (fpathct = ti = 0; ti < ATTEMPT_CT; ti++) {
195
196    /*
197     * Call ls(1) to list the CWD to /dev/null.
198     */
199	(void) system(lsbuf);
200    /*
201     * Call lsof to look up its own CWD -- i.e., this one.
202     */
203	if ((em = FindLsofCwd(&ff, &cwddc, ibuf))) {
204
205	/*
206	 * FindLsofCwd() returned a message.  Decode it via ff.
207	 */
208	    if (ff == -1)
209		PrtMsgX(em, Pn, cleanup, 1);
210	    else if (ff == 1) {
211
212	    /*
213	     * This shouldn't happen.  If FindLsof() found lsof's CWD, it
214	     * should set ff to one and return NULL.
215	     */
216		PrtMsgX("ERROR!!!  inconsistent FindLsofCwd() return", Pn,
217		    cleanup, 1);
218	    }
219	} else if (ff == 1) {
220	    fpathct++;
221	}
222    }
223/*
224 * Compute, display, and measure the success percentage.
225 */
226    pct = ((double)fpathct * (double)100.0) / (double)ATTEMPT_CT;
227    PrtMsg((char *)NULL, Pn);
228    (void) printf("%s found: %.2f%%\n", cwd, pct);	/* NeXT snpf.c has no
229							 * %f support */
230    MsgStat = 1;
231    if (pct < (double)SUCCESS_THRESH) {
232	PrtMsg("ERROR!!!  the find rate was too low.", Pn);
233	if (!fpathct) {
234	    (void) PrtMsg(
235		"Hint: since the find rate is zero, it may be that this file",
236		Pn);
237	    (void) PrtMsg(
238		"system does not fully participate in kernel DNLC processing",
239		Pn);
240	    (void) PrtMsg(
241		"-- e.g., NFS file systems often do not, /tmp file systems",
242		Pn);
243	    (void) PrtMsg(
244		"sometimes do not, Solaris loopback file systems do not.\n",
245		Pn);
246	    (void) PrtMsg(
247		"As a work-around rebuild and test lsof on a file system that",
248		Pn);
249	    (void) PrtMsg(
250		"fully participates in kernel DNLC processing.\n",
251		Pn);
252	    (void) PrtMsg("See 00FAQ and 00TEST for more information.", Pn);
253	}
254	exit(1);
255    }
256/*
257 * Exit successfully.
258 */
259    (void) PrtMsgX("OK", Pn, cleanup, 0);
260    return(0);
261}
262
263
264/*
265 * cleanup() -- release resources
266 */
267
268static void
269cleanup()
270{
271}
272
273
274/*
275 * FindLsofCwd() -- find the lsof CWD
276 */
277
278static char *
279FindLsofCwd(ff, cwddc, ibuf)
280    int *ff;				/* file-found response receptor */
281    LTdev_t *cwddc;			/* CWD device components */
282    char *ibuf;				/* CWD inode number in ASCII */
283{
284    char *cp;				/* temporary character pointer */
285    char *cem;				/* current error message pointer */
286    LTfldo_t *cmdp;			/* command pointer */
287    LTdev_t devdc;			/* devp->v device components */
288    LTfldo_t *devp;			/* device pointer */
289    LTfldo_t *fop;			/* field output pointer */
290    LTfldo_t *inop;			/* inode number pointer */
291    int nf;				/* number of fields */
292    LTfldo_t *nmp;			/* name pointer */
293    char *opv[3];			/* option vector for ExecLsof() */
294    char *pem = (char *)NULL;		/* previous error message pointer */
295    pid_t pid;				/* PID */
296    int pids = 0;			/* PID found status */
297    int ti;				/* temporary integer */
298    LTfldo_t *typ;			/* file type pointer */
299/*
300 * Check the argument pointers.
301 *
302 * Set the file-found response false.
303 */
304    if (!ff || !cwddc || !ibuf)
305	(void) PrtMsgX("ERROR!!!  missing argument to FindFile()",
306		       Pn, cleanup, 1);
307    *ff = 0;
308/*
309 * Complete the option vector and start lsof execution.
310 */
311    opv[0] = "-clsof";
312    opv[1] = "-adcwd";
313    opv[2] = (char *)NULL;
314    if ((cem = ExecLsof(opv))) {
315	*ff = -1;
316	return(cem);
317    }
318/*
319 * Read lsof output.
320 */
321    while (!*ff && (fop = RdFrLsof(&nf, &cem))) {
322	if (cem) {
323	    if (pem)
324		(void) PrtMsg(pem, Pn);
325	    *ff = -1;
326	    return(cem);
327	}
328	switch (fop->ft) {
329	case LSOF_FID_PID:
330
331	/*
332	 * This is a process information line.
333	 */
334	    pid = (pid_t)atoi(fop->v);
335	    pids = 1;
336	    cmdp = (LTfldo_t *)NULL;
337	    for (fop++, ti = 1; ti < nf; fop++, ti++) {
338		switch (fop->ft) {
339		case LSOF_FID_CMD:
340		    cmdp = fop;
341		    break;
342		}
343	    }
344	    if (!cmdp || (pid != LsofPid))
345		pids = 0;
346	    break;
347	case LSOF_FID_FD:
348
349	/*
350	 * This is a file descriptor line.  Make sure it's for the expected
351	 * PID and its type is "cwd".
352	 */
353	    if (!pids)
354		break;
355	    if (strcasecmp(fop->v, "cwd"))
356		break;
357	/*
358	 * Scan for device, inode, name, and type fields.
359	 */
360	    devp = inop = nmp = typ = (LTfldo_t *)NULL;
361	    for (fop++, ti = 1; ti < nf; fop++, ti++) {
362		switch (fop->ft) {
363		case LSOF_FID_DEVN:
364		    devp = fop;
365		    break;
366		case LSOF_FID_INODE:
367		    inop = fop;
368		    break;
369		case LSOF_FID_NAME:
370		    nmp = fop;
371		    break;
372		case LSOF_FID_TYPE:
373		    typ = fop;
374		    break;
375		}
376	    }
377	/*
378	 * Check the device, inode, and type of the file.
379	 */
380	    if (!devp || !inop || !nmp || !typ)
381		break;
382	    if (strcasecmp(typ->v, "dir") && strcasecmp(typ->v, "vdir"))
383		break;
384	    if ((cem = ConvLsofDev(devp->v, &devdc))) {
385		if (pem)
386		    (void) PrtMsg(pem, Pn);
387		pem = cem;
388		break;
389	    }
390	    if ((cwddc->maj != devdc.maj)
391	    ||  (cwddc->min != devdc.min)
392	    ||  (cwddc->unit != devdc.unit)
393	    ||  strcmp(inop->v, ibuf)
394	    ) {
395		break;
396	    }
397	/*
398	 * Check the name for spaces.  If it has none, set a file-found
399	 * response.
400	 */
401	    if (!(cp = strchr(nmp->v, ' ')))
402		*ff = 1;
403	    else {
404
405	    /*
406	     * If a parenthesized file system name follows the space in the
407	     * file's name, it probably is an NFS file system name and can
408	     * be ignored.  Accordingly set a file-found response.
409	     */
410		if ((*(cp + 1) == '(') && *(cp + 2) && !strchr(cp + 2, ' ')) {
411		    if ((cp = strchr(cp + 2, ')')) && !*(cp + 1))
412			*ff = 1;
413		}
414	    }
415	}
416    }
417/*
418 * Clean up and return.
419 */
420    (void) StopLsof();
421    if (pem) {
422	*ff = -1;
423	return(pem);
424    }
425    return((char *)NULL);
426}
427