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