1/* 2 * rdev.c -- readdev() function for lsof library 3 */ 4 5 6/* 7 * Copyright 1997 Purdue Research Foundation, West Lafayette, Indiana 8 * 47907. All rights reserved. 9 * 10 * Written by Victor A. Abell 11 * 12 * This software is not subject to any license of the American Telephone 13 * and Telegraph Company or the Regents of the University of California. 14 * 15 * Permission is granted to anyone to use this software for any purpose on 16 * any computer system, and to alter it and redistribute it freely, subject 17 * to the following restrictions: 18 * 19 * 1. Neither the authors nor Purdue University are responsible for any 20 * consequences of the use of this software. 21 * 22 * 2. The origin of this software must not be misrepresented, either by 23 * explicit claim or by omission. Credit to the authors and Purdue 24 * University must appear in documentation and sources. 25 * 26 * 3. Altered versions must be plainly marked as such, and must not be 27 * misrepresented as being the original software. 28 * 29 * 4. This notice may not be removed or altered. 30 */ 31 32 33#include "../machine.h" 34 35#if defined(USE_LIB_READDEV) 36 37# if !defined(lint) 38static char copyright[] = 39"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n"; 40static char *rcsid = "$Id: rdev.c,v 1.12 2008/10/21 16:13:23 abe Exp $"; 41# endif /* !defined(lint) */ 42 43#include "../lsof.h" 44 45 46_PROTOTYPE(static int rmdupdev,(struct l_dev ***dp, int n, char *nm)); 47 48 49/* 50 * To use this source file: 51 * 52 * 1. Define DIRTYPE as: 53 * 54 * #define DIRTYPE direct 55 * or #define DIRTYPE dirent 56 * 57 * 2. Define HASDNAMLEN if struct DIRTYPE has a d_namlen element, giving 58 * the length of d_name. 59 * 60 * 3. Define the RDEV_EXPDEV macro to apply special handling to device 61 * numbers, as required. For example, for EP/IX 2.1.1: 62 * 63 * #define RDEV_EXPDEV(n) expdev(n) 64 * 65 * to use the expdev() function to expand device numbers. If 66 * no RDEV_EXPDEV macro is defined, it defaults to: 67 * 68 * #define RDEV_EXPDEV(n) (n) 69 * 70 * 4. Define HASBLKDEV to request that information on S_IFBLK devices be 71 * recorded in BDevtp[]. 72 * 73 * Define NOWARNBLKDEV to suppress the issuance of a warning when no 74 * block devices are found. 75 * 76 * 5. Define RDEV_STATFN to be a stat function other than stat() or lstat() 77 * -- e.g., 78 * 79 * #define RDEV_STATFN private_stat 80 * 81 * 6. Define HAS_STD_CLONE to request that clone device information be stored 82 * in standard clone structures (defined in lsof.h and addressed via 83 * Clone). If HAS_STD_CLONE is defined, these must also be defined: 84 * 85 * a. Define CLONEMAJ to be the name of the constant or 86 * variable that defines the clone major device -- e.g., 87 * 88 * #define CLONEMAJ CloneMaj 89 * 90 * b. Define HAVECLONEMAJ to be the name of the variable that 91 * contains the status of the clone major device -- e.g., 92 * 93 * #define HAVECLONEMAJ HaveCloneMaj 94 * 95 * Define HAS_STD_CLONE to be 1 if readdev() is expected to build the 96 * clone table, the clone table is cached (if HASDCACHE is defined), and 97 * there is a function to clear the cache table when the device table must 98 * be reloaded. (See dvch.c for naming the clone cache build and clear 99 * functions.) 100 */ 101 102 103# if !defined(RDEV_EXPDEV) 104#define RDEV_EXPDEV(n) (n) 105# endif /* !defined(RDEV_EXPDEV) */ 106 107# if !defined(RDEV_STATFN) 108# if defined(USE_STAT) 109#define RDEV_STATFN stat 110# else /* !defined(USE_STAT) */ 111#define RDEV_STATFN lstat 112# endif /* defined(USE_STAT) */ 113# endif /* !defined(RDEV_STATFN) */ 114 115 116/* 117 * readdev() - read device names, modes and types 118 */ 119 120void 121readdev(skip) 122 int skip; /* skip device cache read if 1 */ 123{ 124 125# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 126 struct clone *c; 127# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */ 128 129# if defined(HASDCACHE) 130 int dcrd; 131# endif /* defined(HASDCACHE) */ 132 133 DIR *dfp; 134 int dnamlen; 135 struct DIRTYPE *dp; 136 char *fp = (char *)NULL; 137 int i = 0; 138 139# if defined(HASBLKDEV) 140 int j = 0; 141# endif /* defined(HASBLKDEV) */ 142 143 char *path = (char *)NULL; 144 MALLOC_S pl; 145 struct stat sb; 146 147 if (Sdev) 148 return; 149 150# if defined(HASDCACHE) 151/* 152 * Read device cache, as directed. 153 */ 154 if (!skip) { 155 if (DCstate == 2 || DCstate == 3) { 156 if ((dcrd = read_dcache()) == 0) 157 return; 158 } 159 } else 160 dcrd = 1; 161# endif /* defined(HASDCACHE) */ 162 163 Dstkn = Dstkx = 0; 164 Dstk = (char **)NULL; 165 (void) stkdir("/dev"); 166/* 167 * Unstack the next /dev or /dev/<subdirectory> directory. 168 */ 169 while (--Dstkx >= 0) { 170 if (!(dfp = OpenDir(Dstk[Dstkx]))) { 171 172# if defined(WARNDEVACCESS) 173 if (!Fwarn) { 174 (void) fprintf(stderr, "%s: WARNING: can't open: ", Pn); 175 safestrprt(Dstk[Dstkx], stderr, 1); 176 } 177# endif /* defined(WARNDEVACCESS) */ 178 179 (void) free((FREE_P *)Dstk[Dstkx]); 180 Dstk[Dstkx] = (char *)NULL; 181 continue; 182 } 183 if (path) { 184 (void) free((FREE_P *)path); 185 path = (char *)NULL; 186 } 187 if (!(path = mkstrcat(Dstk[Dstkx], -1, "/", 1, (char *)NULL, -1, 188 &pl))) 189 { 190 (void) fprintf(stderr, "%s: no space for: ", Pn); 191 safestrprt(Dstk[Dstkx], stderr, 1); 192 Exit(1); 193 } 194 (void) free((FREE_P *)Dstk[Dstkx]); 195 Dstk[Dstkx] = (char *)NULL; 196 /* 197 * Scan the directory. 198 */ 199 for (dp = ReadDir(dfp); dp; dp = ReadDir(dfp)) { 200 if (dp->d_ino == 0 || dp->d_name[0] == '.') 201 continue; 202 /* 203 * Form the full path name and get its status. 204 */ 205 206# if defined(HASDNAMLEN) 207 dnamlen = (int)dp->d_namlen; 208# else /* !defined(HASDNAMLEN) */ 209 dnamlen = (int)strlen(dp->d_name); 210# endif /* defined(HASDNAMLEN) */ 211 212 if (fp) { 213 (void) free((FREE_P *)fp); 214 fp = (char *)NULL; 215 } 216 if (!(fp = mkstrcat(path, pl, dp->d_name, dnamlen, 217 (char *)NULL, -1, (MALLOC_S *)NULL))) 218 { 219 (void) fprintf(stderr, "%s: no space for: ", Pn); 220 safestrprt(path, stderr, 0); 221 safestrprtn(dp->d_name, dnamlen, stderr, 1); 222 Exit(1); 223 } 224 if (RDEV_STATFN(fp, &sb) != 0) { 225 if (errno == ENOENT) /* a sym link to nowhere? */ 226 continue; 227 228# if defined(WARNDEVACCESS) 229 if (!Fwarn) { 230 int errno_save = errno; 231 232 (void) fprintf(stderr, "%s: can't stat ", Pn); 233 safestrprt(fp, stderr, 0); 234 (void) fprintf(stderr, ": %s\n", strerror(errno_save)); 235 } 236# endif /* defined(WARNDEVACCESS) */ 237 238 continue; 239 } 240 /* 241 * If it's a subdirectory, stack its name for later 242 * processing. 243 */ 244 if ((sb.st_mode & S_IFMT) == S_IFDIR) { 245 (void) stkdir(fp); 246 continue; 247 } 248 if ((sb.st_mode & S_IFMT) == S_IFCHR) { 249 250 /* 251 * Save character device information in Devtp[]. 252 */ 253 if (i >= Ndev) { 254 Ndev += DEVINCR; 255 if (!Devtp) 256 Devtp = (struct l_dev *)malloc( 257 (MALLOC_S)(sizeof(struct l_dev)*Ndev)); 258 else 259 Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp, 260 (MALLOC_S)(sizeof(struct l_dev)*Ndev)); 261 if (!Devtp) { 262 (void) fprintf(stderr, 263 "%s: no space for character device\n", Pn); 264 Exit(1); 265 } 266 } 267 Devtp[i].rdev = RDEV_EXPDEV(sb.st_rdev); 268 Devtp[i].inode = (INODETYPE)sb.st_ino; 269 if (!(Devtp[i].name = mkstrcpy(fp, (MALLOC_S *)NULL))) { 270 (void) fprintf(stderr, 271 "%s: no space for device name: ", Pn); 272 safestrprt(fp, stderr, 1); 273 Exit(1); 274 } 275 Devtp[i].v = 0; 276 277# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 278 if (HAVECLONEMAJ && GET_MAJ_DEV(Devtp[i].rdev) == CLONEMAJ) 279 { 280 281 /* 282 * Record clone device information. 283 */ 284 if (!(c = (struct clone *)malloc(sizeof(struct clone)))) 285 { 286 (void) fprintf(stderr, 287 "%s: no space for clone device: ", Pn); 288 safestrprt(fp, stderr, 1); 289 Exit(1); 290 } 291 c->dx = i; 292 c->next = Clone; 293 Clone = c; 294 } 295# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */ 296 297 i++; 298 } 299 300# if defined(HASBLKDEV) 301 if ((sb.st_mode & S_IFMT) == S_IFBLK) { 302 303 /* 304 * Save block device information in BDevtp[]. 305 */ 306 if (j >= BNdev) { 307 BNdev += DEVINCR; 308 if (!BDevtp) 309 BDevtp = (struct l_dev *)malloc( 310 (MALLOC_S)(sizeof(struct l_dev)*BNdev)); 311 else 312 BDevtp = (struct l_dev *)realloc((MALLOC_P *)BDevtp, 313 (MALLOC_S)(sizeof(struct l_dev)*BNdev)); 314 if (!BDevtp) { 315 (void) fprintf(stderr, 316 "%s: no space for block device\n", Pn); 317 Exit(1); 318 } 319 } 320 BDevtp[j].name = fp; 321 fp = (char *)NULL; 322 BDevtp[j].inode = (INODETYPE)sb.st_ino; 323 BDevtp[j].rdev = RDEV_EXPDEV(sb.st_rdev); 324 BDevtp[j].v = 0; 325 j++; 326 } 327# endif /* defined(HASBLKDEV) */ 328 329 } 330 (void) CloseDir(dfp); 331 } 332/* 333 * Free any allocated space. 334 */ 335 if (!Dstk) { 336 (void) free((FREE_P *)Dstk); 337 Dstk = (char **)NULL; 338 } 339 if (fp) 340 (void) free((FREE_P *)fp); 341 if (path) 342 (void) free((FREE_P *)path); 343 344# if defined(HASBLKDEV) 345/* 346 * Reduce the BDevtp[] (optional) and Devtp[] tables to their minimum 347 * sizes; allocate and build sort pointer lists; and sort the tables by 348 * device number. 349 */ 350 if (BNdev) { 351 if (BNdev > j) { 352 BNdev = j; 353 BDevtp = (struct l_dev *)realloc((MALLOC_P *)BDevtp, 354 (MALLOC_S)(sizeof(struct l_dev) * BNdev)); 355 } 356 if (!(BSdev = (struct l_dev **)malloc( 357 (MALLOC_S)(sizeof(struct l_dev *) * BNdev)))) 358 { 359 (void) fprintf(stderr, 360 "%s: no space for block device sort pointers\n", Pn); 361 Exit(1); 362 } 363 for (j = 0; j < BNdev; j++) { 364 BSdev[j] = &BDevtp[j]; 365 } 366 (void) qsort((QSORT_P *)BSdev, (size_t)BNdev, 367 (size_t)sizeof(struct l_dev *), compdev); 368 BNdev = rmdupdev(&BSdev, BNdev, "block"); 369 } 370 371# if !defined(NOWARNBLKDEV) 372 else { 373 if (!Fwarn) 374 (void) fprintf(stderr, 375 "%s: WARNING: no block devices found\n", Pn); 376 } 377# endif /* !defined(NOWARNBLKDEV) */ 378# endif /* defined(HASBLKDEV) */ 379 380 if (Ndev) { 381 if (Ndev > i) { 382 Ndev = i; 383 Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp, 384 (MALLOC_S)(sizeof(struct l_dev) * Ndev)); 385 } 386 if (!(Sdev = (struct l_dev **)malloc( 387 (MALLOC_S)(sizeof(struct l_dev *) * Ndev)))) 388 { 389 (void) fprintf(stderr, 390 "%s: no space for character device sort pointers\n", Pn); 391 Exit(1); 392 } 393 for (i = 0; i < Ndev; i++) { 394 Sdev[i] = &Devtp[i]; 395 } 396 (void) qsort((QSORT_P *)Sdev, (size_t)Ndev, 397 (size_t)sizeof(struct l_dev *), compdev); 398 Ndev = rmdupdev(&Sdev, Ndev, "char"); 399 } else { 400 (void) fprintf(stderr, "%s: no character devices found\n", Pn); 401 Exit(1); 402 } 403 404# if defined(HASDCACHE) 405/* 406 * Write device cache file, as required. 407 */ 408 if (DCstate == 1 || (DCstate == 3 && dcrd)) 409 write_dcache(); 410# endif /* defined(HASDCACHE) */ 411 412} 413 414 415# if defined(HASDCACHE) 416/* 417 * rereaddev() - reread device names, modes and types 418 */ 419 420void 421rereaddev() 422{ 423 (void) clr_devtab(); 424 425# if defined(DCACHE_CLR) 426 (void) DCACHE_CLR(); 427# endif /* defined(DCACHE_CLR) */ 428 429 readdev(1); 430 DCunsafe = 0; 431} 432#endif /* defined(HASDCACHE) */ 433 434 435/* 436 * rmdupdev() - remove duplicate (major/minor/inode) devices 437 */ 438 439static int 440rmdupdev(dp, n, nm) 441 struct l_dev ***dp; /* device table pointers address */ 442 int n; /* number of pointers */ 443 char *nm; /* device table name for error message */ 444{ 445 446# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 447 struct clone *c, *cp; 448# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */ 449 450 int i, j, k; 451 struct l_dev **p; 452 453 for (i = j = 0, p = *dp; i < n ;) { 454 for (k = i + 1; k < n; k++) { 455 if (p[i]->rdev != p[k]->rdev || p[i]->inode != p[k]->inode) 456 break; 457 458# if defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 459 /* 460 * See if we're deleting a duplicate clone device. If so, 461 * delete its clone table entry. 462 */ 463 for (c = Clone, cp = (struct clone *)NULL; 464 c; 465 cp = c, c = c->next) 466 { 467 if (&Devtp[c->dx] != p[k]) 468 continue; 469 if (!cp) 470 Clone = c->next; 471 else 472 cp->next = c->next; 473 (void) free((FREE_P *)c); 474 break; 475 } 476# endif /* defined(HAS_STD_CLONE) && HAS_STD_CLONE==1 */ 477 478 } 479 if (i != j) 480 p[j] = p[i]; 481 j++; 482 i = k; 483 } 484 if (n == j) 485 return(n); 486 if (!(*dp = (struct l_dev **)realloc((MALLOC_P *)*dp, 487 (MALLOC_S)(j * sizeof(struct l_dev *))))) 488 { 489 (void) fprintf(stderr, "%s: can't realloc %s device pointers\n", 490 Pn, nm); 491 Exit(1); 492 } 493 return(j); 494} 495 496 497# if defined(HASDCACHE) 498/* 499 * vfy_dev() - verify a device table entry (usually when DCunsafe == 1) 500 * 501 * Note: rereads entire device table when an entry can't be verified. 502 */ 503 504int 505vfy_dev(dp) 506 struct l_dev *dp; /* device table pointer */ 507{ 508 struct stat sb; 509 510 if (!DCunsafe || dp->v) 511 return(1); 512 if (RDEV_STATFN(dp->name, &sb) != 0 513 || dp->rdev != RDEV_EXPDEV(sb.st_rdev) 514 || dp->inode != sb.st_ino) { 515 (void) rereaddev(); 516 return(0); 517 } 518 dp->v = 1; 519 return(1); 520} 521# endif /* defined(HASDCACHE) */ 522#else /* !defined(USE_LIB_READDEV) */ 523char rdev_d1[] = "d"; char *rdev_d2 = rdev_d1; 524#endif /* defined(USE_LIB_READDEV) */ 525