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