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