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