1/* 2 * ddev.c - Darwin device support functions for /dev/kmem-based lsof 3 */ 4 5 6/* 7 * Copyright 1994 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#ifndef lint 33static char copyright[] = 34"@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n"; 35static char *rcsid = "$Id: ddev.c,v 1.5 2006/03/27 23:24:50 abe Exp $"; 36#endif 37 38 39#include "lsof.h" 40 41 42/* 43 * Local definitions 44 */ 45 46#if defined(DVCH_DEVPATH) 47#define DDEV_DEVPATH DVCH_DEVPATH 48#else /* !defined(DVCH_DEVPATH) */ 49#define DDEV_DEVPATH "/dev" 50#endif /* defined(DVCH_DEVPATH) */ 51 52#if defined(USE_STAT) 53#define STATFN stat 54#else /* !defined(USE_STAT) */ 55#define STATFN lstat 56#endif /* defined(USE_STAT) */ 57 58 59/* 60 * Local static variables. 61 */ 62 63static dev_t *ADev = (dev_t *) NULL; /* device numbers besides DevDev found 64 * inside DDEV_DEVPATH */ 65static int ADevA = 0; /* entries allocated to ADev[] */ 66static int ADevU = 0; /* entries used in ADev[] */ 67 68 69/* 70 * Local function prototypes 71 */ 72 73_PROTOTYPE(static int rmdupdev,(struct l_dev ***dp, int n, char *nm)); 74_PROTOTYPE(static void saveADev,(struct stat *s)); 75 76 77#if defined(HASSPECDEVD) 78/* 79 * HASSPECDEVD() -- process stat(2) result to see if the device number is 80 * inside DDEV_DEVPATH "/" 81 * 82 * exit: s->st_dev changed to DevDev, as required 83 */ 84 85void 86HASSPECDEVD(p, s) 87 char *p; /* file path */ 88 struct stat *s; /* stat(2) result for file */ 89{ 90 int i; 91 92 switch (s->st_mode & S_IFMT) { 93 case S_IFCHR: 94 case S_IFBLK: 95 if (s->st_dev == DevDev) 96 return; 97 (void) readdev(0); 98 if (!ADev) 99 return; 100 for (i = 0; i < ADevU; i++) { 101 if (s->st_dev == ADev[i]) { 102 s->st_dev = DevDev; 103 return; 104 } 105 } 106 } 107} 108#endif /* defined(HASSPECDEVD) */ 109 110 111/* 112 * readdev() - read device names, modes and types 113 */ 114 115void 116readdev(skip) 117 int skip; /* skip device cache read if 1 -- 118 * ignored since device cache not 119 * used */ 120{ 121 DIR *dfp; 122 int dnamlen; 123 struct dirent *dp; 124 char *fp = (char *)NULL; 125 char *path = (char *)NULL; 126 int i = 0; 127 int j = 0; 128 MALLOC_S pl, sz; 129 struct stat sb; 130/* 131 * Read device names but once. 132 */ 133 if (Sdev) 134 return; 135/* 136 * Prepare to scan DDEV_DEVPATH. 137 */ 138 Dstkn = Dstkx = 0; 139 Dstk = (char **)NULL; 140 (void) stkdir(DDEV_DEVPATH); 141/* 142 * Unstack the next directory. 143 */ 144 while (--Dstkx >= 0) { 145 if (!(dfp = OpenDir(Dstk[Dstkx]))) { 146 147# if defined(WARNDEVACCESS) 148 if (!Fwarn) { 149 (void) fprintf(stderr, "%s: WARNING: can't open: ", Pn); 150 safestrprt(Dstk[Dstkx], stderr, 1); 151 } 152# endif /* defined(WARNDEVACCESS) */ 153 154 (void) free((FREE_P *)Dstk[Dstkx]); 155 Dstk[Dstkx] = (char *)NULL; 156 continue; 157 } 158 if (path) { 159 (void) free((FREE_P *)path); 160 path = (char *)NULL; 161 } 162 if (!(path = mkstrcat(Dstk[Dstkx], -1, "/", 1, (char *)NULL, -1, 163 &pl))) 164 { 165 (void) fprintf(stderr, "%s: no space for: ", Pn); 166 safestrprt(Dstk[Dstkx], stderr, 1); 167 Exit(1); 168 } 169 (void) free((FREE_P *)Dstk[Dstkx]); 170 Dstk[Dstkx] = (char *)NULL; 171 /* 172 * Scan the directory. 173 */ 174 for (dp = ReadDir(dfp); dp; dp = ReadDir(dfp)) { 175 if (dp->d_ino == 0 || dp->d_name[0] == '.') 176 continue; 177 /* 178 * Form the full path name and get its status. 179 */ 180 dnamlen = (int)dp->d_namlen; 181 if (fp) { 182 (void) free((FREE_P *)fp); 183 fp = (char *)NULL; 184 } 185 if (!(fp = mkstrcat(path, pl, dp->d_name, dnamlen, 186 (char *)NULL, -1, (MALLOC_S *)NULL))) 187 { 188 (void) fprintf(stderr, "%s: no space for: ", Pn); 189 safestrprt(path, stderr, 0); 190 safestrprtn(dp->d_name, dnamlen, stderr, 1); 191 Exit(1); 192 } 193 if (STATFN(fp, &sb) != 0) { 194 if (errno == ENOENT) /* a sym link to nowhere? */ 195 continue; 196 197# if defined(WARNDEVACCESS) 198 if (!Fwarn) { 199 int errno_save = errno; 200 201 (void) fprintf(stderr, "%s: can't stat ", Pn); 202 safestrprt(fp, stderr, 0); 203 (void) fprintf(stderr, ": %s\n", strerror(errno_save)); 204 } 205# endif /* defined(WARNDEVACCESS) */ 206 207 continue; 208 } 209 /* 210 * If it's a subdirectory, stack its name for later 211 * processing. 212 */ 213 if ((sb.st_mode & S_IFMT) == S_IFDIR) { 214 215 /* 216 * Skip /dev/fd. 217 */ 218 if (strcmp(fp, "/dev/fd")) 219 (void) stkdir(fp); 220 continue; 221 } 222 if ((sb.st_mode & S_IFMT) == S_IFLNK) { 223 224 /* 225 * Ignore symbolic links. 226 */ 227 continue; 228 } 229 if ((sb.st_mode & S_IFMT) == S_IFCHR) { 230 231 /* 232 * Save character device information in Devtp[]. 233 */ 234 if (i >= Ndev) { 235 Ndev += DEVINCR; 236 if (!Devtp) 237 Devtp = (struct l_dev *)malloc( 238 (MALLOC_S)(sizeof(struct l_dev)*Ndev)); 239 else 240 Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp, 241 (MALLOC_S)(sizeof(struct l_dev)*Ndev)); 242 if (!Devtp) { 243 (void) fprintf(stderr, 244 "%s: no space for character device\n", Pn); 245 Exit(1); 246 } 247 } 248 Devtp[i].rdev = sb.st_rdev; 249 Devtp[i].inode = (INODETYPE)sb.st_ino; 250 if (!(Devtp[i].name = mkstrcpy(fp, (MALLOC_S *)NULL))) { 251 (void) fprintf(stderr, 252 "%s: no space for device name: ", Pn); 253 safestrprt(fp, stderr, 1); 254 Exit(1); 255 } 256 Devtp[i].v = 0; 257 i++; 258 } 259 260# if defined(HASBLKDEV) 261 if ((sb.st_mode & S_IFMT) == S_IFBLK) { 262 263 /* 264 * Save block device information in BDevtp[]. 265 */ 266 if (j >= BNdev) { 267 BNdev += DEVINCR; 268 if (!BDevtp) 269 BDevtp = (struct l_dev *)malloc( 270 (MALLOC_S)(sizeof(struct l_dev)*BNdev)); 271 else 272 BDevtp = (struct l_dev *)realloc((MALLOC_P *)BDevtp, 273 (MALLOC_S)(sizeof(struct l_dev)*BNdev)); 274 if (!BDevtp) { 275 (void) fprintf(stderr, 276 "%s: no space for block device\n", Pn); 277 Exit(1); 278 } 279 } 280 BDevtp[j].name = fp; 281 fp = (char *)NULL; 282 BDevtp[j].inode = (INODETYPE)sb.st_ino; 283 BDevtp[j].rdev = sb.st_rdev; 284 BDevtp[j].v = 0; 285 j++; 286 } 287# endif /* defined(HASBLKDEV) */ 288 289 /* 290 * Save a possible new st_dev number within DDEV_DEVPATH. 291 */ 292 if (sb.st_dev != DevDev) 293 (void) saveADev(&sb); 294 } 295 (void) CloseDir(dfp); 296 } 297/* 298 * Free any unneeded space that was allocated. 299 */ 300 if (ADev && (ADevU < ADevA)) { 301 302 /* 303 * Reduce space allocated to additional DDEV_DEVPATH device numbers. 304 */ 305 if (!ADevU) { 306 307 /* 308 * If no space was used, free the entire allocation. 309 */ 310 (void) free((FREE_P *)ADev); 311 ADev = (dev_t *)NULL; 312 ADevA = 0; 313 } else { 314 315 /* 316 * Reduce the allocation to what was used. 317 */ 318 sz = (MALLOC_S)(ADevU * sizeof(dev_t)); 319 if (!(ADev = (dev_t *)realloc((MALLOC_P *)ADev, sz))) { 320 (void) fprintf(stderr, "%s: can't reduce ADev[]\n", Pn); 321 Exit(1); 322 } 323 } 324 } 325 if (!Dstk) { 326 (void) free((FREE_P *)Dstk); 327 Dstk = (char **)NULL; 328 } 329 if (fp) 330 (void) free((FREE_P *)fp); 331 if (path) 332 (void) free((FREE_P *)path); 333 334# if defined(HASBLKDEV) 335/* 336 * Reduce the BDevtp[] (optional) and Devtp[] tables to their minimum 337 * sizes; allocate and build sort pointer lists; and sort the tables by 338 * device number. 339 */ 340 if (BNdev) { 341 if (BNdev > j) { 342 BNdev = j; 343 BDevtp = (struct l_dev *)realloc((MALLOC_P *)BDevtp, 344 (MALLOC_S)(sizeof(struct l_dev) * BNdev)); 345 } 346 if (!(BSdev = (struct l_dev **)malloc( 347 (MALLOC_S)(sizeof(struct l_dev *) * BNdev)))) 348 { 349 (void) fprintf(stderr, 350 "%s: no space for block device sort pointers\n", Pn); 351 Exit(1); 352 } 353 for (j = 0; j < BNdev; j++) { 354 BSdev[j] = &BDevtp[j]; 355 } 356 (void) qsort((QSORT_P *)BSdev, (size_t)BNdev, 357 (size_t)sizeof(struct l_dev *), compdev); 358 BNdev = rmdupdev(&BSdev, BNdev, "block"); 359 } 360 361# if !defined(NOWARNBLKDEV) 362 else { 363 if (!Fwarn) 364 (void) fprintf(stderr, 365 "%s: WARNING: no block devices found\n", Pn); 366 } 367# endif /* !defined(NOWARNBLKDEV) */ 368# endif /* defined(HASBLKDEV) */ 369 370 if (Ndev) { 371 if (Ndev > i) { 372 Ndev = i; 373 Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp, 374 (MALLOC_S)(sizeof(struct l_dev) * Ndev)); 375 } 376 if (!(Sdev = (struct l_dev **)malloc( 377 (MALLOC_S)(sizeof(struct l_dev *) * Ndev)))) 378 { 379 (void) fprintf(stderr, 380 "%s: no space for character device sort pointers\n", Pn); 381 Exit(1); 382 } 383 for (i = 0; i < Ndev; i++) { 384 Sdev[i] = &Devtp[i]; 385 } 386 (void) qsort((QSORT_P *)Sdev, (size_t)Ndev, 387 (size_t)sizeof(struct l_dev *), compdev); 388 Ndev = rmdupdev(&Sdev, Ndev, "char"); 389 } else { 390 (void) fprintf(stderr, "%s: no character devices found\n", Pn); 391 Exit(1); 392 } 393} 394 395 396/* 397 * rmdupdev() - remove duplicate (major/minor/inode) devices 398 */ 399 400static int 401rmdupdev(dp, n, nm) 402 struct l_dev ***dp; /* device table pointers address */ 403 int n; /* number of pointers */ 404 char *nm; /* device table name for error message */ 405{ 406 int i, j, k; 407 struct l_dev **p; 408 409 for (i = j = 0, p = *dp; i < n ;) { 410 for (k = i + 1; k < n; k++) { 411 if (p[i]->rdev != p[k]->rdev || p[i]->inode != p[k]->inode) 412 break; 413 } 414 if (i != j) 415 p[j] = p[i]; 416 j++; 417 i = k; 418 } 419 if (n == j) 420 return(n); 421 if (!(*dp = (struct l_dev **)realloc((MALLOC_P *)*dp, 422 (MALLOC_S)(j * sizeof(struct l_dev *))))) 423 { 424 (void) fprintf(stderr, "%s: can't realloc %s device pointers\n", 425 Pn, nm); 426 Exit(1); 427 } 428 return(j); 429} 430 431 432/* 433 * saveADev() - save additional device number appearing inside DDEV_DEVPATH 434 */ 435 436static void 437saveADev(s) 438 struct stat *s; /* stat(2) buffer for file */ 439{ 440 int i; 441 MALLOC_S sz; 442/* 443 * Process VCHR files. 444 * 445 * Optionally process VBLK files. 446 */ 447 448#if defined(HASBLKDEV) 449 if (((s->st_mode & S_IFMT) != S_IFBLK) 450 && ((s->st_mode & S_IFMT) != S_IFCHR)) 451#else /* !defined(HASBLKDEV) */ 452 if ((s->st_mode & S_IFCHR) != S_IFCHR) 453#endif /* defined(HASBLKDEV) */ 454 455 return; 456/* 457 * See if this is a new VBLK or VCHR st_dev value for ADev[]. 458 */ 459 for (i = 0; i < ADevU; i++) { 460 if (s->st_dev == ADev[i]) 461 return; 462 } 463/* 464 * This is a new device number to add to ADev[]. 465 */ 466 if (ADevU >= ADevA) { 467 ADevA += 16; 468 sz = (MALLOC_S)(ADevA * sizeof(dev_t)); 469 if (ADev) 470 ADev = (dev_t *)realloc((MALLOC_P *)ADev, sz); 471 else 472 ADev = (dev_t *)malloc(sz); 473 if (!ADev) { 474 (void) fprintf(stderr, "%s: no space for ADev[]\n", Pn); 475 Exit(1); 476 } 477 } 478 ADev[ADevU++] = s->st_dev; 479} 480