/*
 * ddev.c - SCO Unix device support functions for lsof
 */


/*
 * Copyright 1995 Purdue Research Foundation, West Lafayette, Indiana
 * 47907.  All rights reserved.
 *
 * Written by Victor A. Abell
 *
 * This software is not subject to any license of the American Telephone
 * and Telegraph Company or the Regents of the University of California.
 *
 * Permission is granted to anyone to use this software for any purpose on
 * any computer system, and to alter it and redistribute it freely, subject
 * to the following restrictions:
 *
 * 1. Neither the authors nor Purdue University are responsible for any
 *    consequences of the use of this software.
 *
 * 2. The origin of this software must not be misrepresented, either by
 *    explicit claim or by omission.  Credit to the authors and Purdue
 *    University must appear in documentation and sources.
 *
 * 3. Altered versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 *
 * 4. This notice may not be removed or altered.
 */

#ifndef lint
static char copyright[] =
"@(#) Copyright 1995 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: ddev.c,v 1.15 95/09/17 08:43:06 abe Exp $";
#endif


#include "lsof.h"

#if     defined(HASDCACHE)
_PROTOTYPE(static void clr_sect,(void));
_PROTOTYPE(static int rw_clone_sect,(int m));
#endif  /* defined(HASDCACHE) */

_PROTOTYPE(static void stkdir,(char ***d, int *n, int *x, char *p));


/*
 * printchdevname() - print character device name
 */

int
printchdevname(rdev, f)
	dev_t *rdev;			/* device */
	int f;				/* 1 = print trailing '\n' */
{
	struct clone *c;
	struct l_dev *dp;

	readdev();
/*
 * Search for clone.
 */
	if (Lf->is_stream && Clone) {
		for (c = Clone; c; c = c->next) {
			if (major(*rdev) == minor(Devtp[c->dx].rdev)) {
				if (f)
				    (void) puts(Devtp[c->dx].name);
				else
				    (void) fputs(Devtp[c->dx].name, stdout);
				return(1);
			}
		}
	}
/*
 * Search device table for match.
 */
	if ((dp = lkupdev(rdev, 1)) != (struct l_dev *)NULL) {
		if (f)
			(void) puts(dp->name);
		else
			(void) fputs(dp->name, stdout);
		return(1);
	}
	return(0);
}


/*
 * readdev() - read names, modes and device types of everything in /dev
 */

void
readdev()
{
#if	defined(HASDCACHE)
	int dcrd;
#endif	/* defined(HASDCACHE) */

	struct clone *c;
	DIR *dfp;
	int dn = 0;
	struct dirent *dp;
	STRNCPY_L dnamel;
	char **dstk = NULL;
	int dx = 0;
	int err = 0;
	int i = 0;
	MALLOC_S nl;
	char path[MAXNAMLEN+1];
	int pl;
	struct stat sb;

	if (Sdev)
		return;

#if	defined(HASDCACHE)
/*
 * Read device cache, as directed.
 */
	if (DCstate == 2 || DCstate == 3) {
		if ((dcrd = read_dcache()) == 0)
			return;
	}
#endif	/* defined(HASDCACHE) */

	(void) stkdir(&dstk, &dn, &dx, "/dev");
/*
 * Unstack the next /dev or /dev/<subdirectory> directory.
 */
	while (--dx >= 0) {
		(void) strcpy(path, dstk[dx]);
		if ((dfp = opendir(path)) == NULL) {

#if	defined(WARNDEVACCESS)
			if (!Fwarn)
				(void) fprintf(stderr,
					"%s: WARNING: can't open %s\n",
					Pn, path);
#endif	/* defined(WARNDEVACCESS) */

			continue;
		}
		(void) strcat(path, "/");
		pl = strlen(path);
		(void) free((FREE_P *)dstk[dx]);
		dstk[dx] = NULL;
	/*
	 * Scan the directory.
	 */
		for (dp = readdir(dfp); dp; dp = readdir(dfp)) {
			if (dp->d_ino == 0 || dp->d_name[0] == '.')
				continue;
		/*
		 * Form the full path name and get its status.
		 */
			dnamel = strlen(dp->d_name);
			if ((nl = pl + dnamel) >= sizeof(path)) {
				(void) fprintf(stderr,
					"%s: /dev entry name too long: %s\n",
					Pn, dp->d_name);
				exit(1);
			}
			(void) strncpy(&path[pl], dp->d_name, dnamel);
			path[nl++] = '\0';
			if (lstat(path, &sb) != 0) {
				(void) fprintf(stderr,
					"%s: can't stat %s: %s\n",
					Pn, path, strerror(errno));
				err = 1;
				continue;
			}
		/*
		 * If it's a subdirectory, stack its name for later
		 * processing.
		 */
			if ((sb.st_mode & S_IFMT) == S_IFDIR) {
				(void) stkdir(&dstk, &dn, &dx, path);
				continue;
			}
		/*
		 * Skip all but character devices.
		 */
			if ((sb.st_mode & S_IFMT) != S_IFCHR)
				continue;
		/*
		 * Make room for another Devtp[] entry.
		 */
			if (i >= Ndev) {
				Ndev += DEVINCR;
				if (Devtp == NULL)
				    Devtp = (struct l_dev *)malloc(
					(MALLOC_S)(sizeof(struct l_dev)*Ndev));
				else
				    Devtp = (struct l_dev *)realloc(
					(MALLOC_P *)Devtp,
					(MALLOC_S)(sizeof(struct l_dev)*Ndev));
				if (Devtp == NULL) {
					(void) fprintf(stderr,
						"%s: no space for devices\n",
						Pn);
					exit(1);
				}
			}
		/*
		 * Store the device number, inode number, and name in the
		 * Devtp[] entry.
		 */
			Devtp[i].rdev = sb.st_rdev;
			Devtp[i].inode = sb.st_ino;
			if ((Devtp[i].name = (char *)malloc(nl)) == NULL) {
				(void) fprintf(stderr,
					"%s: no space for /dev/%s\n",
					Pn, dp->d_name);
				exit(1);
			}
			(void) strcpy(Devtp[i].name, path);
		/*
		 * Save clone device information.
		 */
			if (HaveCloneMajor
			&&  major(Devtp[i].rdev) == CloneMajor)
			{
				if ((c = (struct clone *)malloc(
					sizeof(struct clone)))
				== NULL) {
				    (void) fprintf(stderr,
					"%s: no space for clone device: %s\n",
				    Pn, Devtp[i].name);
				    exit(1);
				}
				c->dx = i;
				c->next = Clone;
				Clone = c;
			}
			i++;
		}
		(void) closedir(dfp);
	}
/*
 * Free any directory stack space.
 */
	if (dstk != NULL)
		(void) free((FREE_P *)dstk);
/*
 * Reduce the Devtp[] table to its minimum size.
 */
	if (Ndev > i) {
		Ndev = i;
		Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp,
		      (MALLOC_S)(sizeof(struct l_dev) * Ndev));
	}
	if (err)
		exit(1);
/*
 * Allocate sorting pointers and sort them by Devtp[] device number.
 */
	if ((Sdev = (struct l_dev **)malloc((MALLOC_S)(sizeof(struct l_dev *)
	    * Ndev)))
	== NULL) {
		(void) fprintf(stderr, "%s: no space for device pointers\n",
			Pn);
		exit(1);
	}
	for (i = 0; i < Ndev; i++) {
		Sdev[i] = &Devtp[i];
	}
	(void) qsort((QSORT_P *)Sdev, (size_t)Ndev,
		(size_t)sizeof(struct l_dev *), compdev);

#if	defined(HASDCACHE)
/*
 * Write device cache file, as required.
 */
	if (DCstate == 1 || (DCstate == 3 && dcrd))
		write_dcache();
#endif	/* defined(HASDCACHE) */

}


#if	defined(HASDCACHE)
/*
 * clr_sect() - clear cached clone and pseudo sections
 */

static void
clr_sect()
{
	struct clone *c, *c1;

	if (Clone) {
		for (c = Clone; c; c = c1) {
			c1 = c->next;
			(void) free((FREE_P *)c);
		}
		Clone = NULL;
	}
}


/*
 * rw_clone_sect() - read/write the device cache file clone section
 */

static int
rw_clone_sect(m)
	int m;				/* mode: 1 = read; 2 = write */
{
	char buf[MAXPATHLEN*2], *cp, *cp1;
	struct clone *c;
	struct l_dev *dp;
	int i, j, len, n;

	if (m == 1) {

	/*
	 * Read the clone section header and validate it.
	 */
	    if (fgets(buf, sizeof(buf), DCfs) == NULL) {

bad_clone_sect:
		if (!Fwarn)
		    (void) fprintf(stderr,
			"%s: bad clone section header in %s: %s",
			Pn, DCpath[DCpathX], buf);
		return(1);
	    }
	    (void) crc(buf, strlen(buf), &DCcksum);
	    len = strlen("clone section: ");
	    if (strncmp(buf, "clone section: ", len) != 0)
		goto bad_clone_sect;
	    if ((n = atoi(&buf[len])) < 0)
		goto bad_clone_sect;
	/*
	 * Read the clone section lines and create the Clone list.
	 */
	    for (i = 0; i < n; i++) {
		if (fgets(buf, sizeof(buf), DCfs) == NULL) {
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "%s: bad clone line in %s: %s",
			    Pn, DCpath[DCpathX], buf);
		    return(1);
		}
		(void) crc(buf, strlen(buf), &DCcksum);
	    /*
	     * Assemble Devtp[] index and make sure it's correct.
	     */
		for (cp = buf, j = 0; *cp != ' '; cp++) {
		    if (*cp < '0' || *cp > '9') {

bad_clone_index:
			if (!Fwarn)
			    (void) fprintf(stderr,
				"%s: bad cached clone device index: %s",
				Pn, buf);
			return(1);
		    }
		    j = (j * 10) + (int)(*cp - '0');
		}
		if (j < 0 || j >= Ndev || (cp1 = strchr(++cp, '\n')) == NULL)
		    goto bad_clone_index;
		if (strncmp(cp, Devtp[j].name, (cp1 - cp)) != 0)
		    goto bad_clone_index;
	    /*
	     * Allocate and complete a clone structure.
	     */
		if ((c = (struct clone *)malloc(sizeof(struct clone)))
		== NULL) {
		    (void) fprintf(stderr,
			"%s: no space for cached clone: %s", Pn, buf);
		    exit(1);
		}
		c->dx = j;
		c->next = Clone;
		Clone = c;
	    }
	    return(0);
	} else if (m == 2) {

	/*
	 * Write the clone section header.
	 */
	    for (c = Clone, n = 0; c; c = c->next, n++)
		;
	    (void) sprintf(buf, "clone section: %d\n", n);
	    if (wr2DCfd(buf, &DCcksum))
		return(1);
	/*
	 * Write the clone section lines.
	 */
	    for (c = Clone; c; c = c->next) {
		for (dp = &Devtp[c->dx], j = 0; j < Ndev; j++) {
		    if (dp == Sdev[j])
			break;
		}
		if (j >= Ndev) {
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "%s: can't make index for clone: %s\n",
			    Pn, dp->name);
		    (void) unlink(DCpath[DCpathX]);
		    (void) close(DCfd);
		    DCfd = -1;
		    return(1);
		}
		(void) sprintf(buf, "%d %s\n", j, dp->name);
		if (wr2DCfd(buf, &DCcksum))
		    return(1);
	    }
	    return(0);
	}
/*
 * A shouldn't-happen case: mode neither 1 nor 2.
 */
	(void) fprintf(stderr, "%s: internal rw_clone_sect error: %d\n",
	    Pn, m);
	exit(1);
}
#endif	/* defined(HASDCACHE) */


/*
 * stkdir() - stack directory name
 */

static void
stkdir(d, n, x, p)
	char ***d;		/* array of directory pointers */
	int *n;			/* number of pointers */
	int *x;			/* current index */
	char *p;		/* directory path */
{
	if (*d == NULL) {

	/*
	 * Allocate first entry.
	 */
		if ((*d = (char **)malloc(sizeof(char *))) == NULL) {

stkdir_nospace:

			(void) fprintf(stderr,
				"%s: no space for directory stack at %s\n",
				Pn, p);
			exit(1);
		}
		*n = 1;
		*x = 0;
	} else if (*x >= *n) {

	/*
	 * Allocate additional space as required.
	 */
		*n += 1;
		if ((*d = (char **)realloc((MALLOC_P *)*d,
			  (MALLOC_S)(*n * sizeof(char *))))
		== NULL)
			goto stkdir_nospace;
	}
/*
 * Allocate space for the name, copy it there and put its pointer on the stack.
 */
	if (((*d)[*x] = (char *)malloc((MALLOC_S)(strlen(p) + 1))) == NULL) {
		(void) fprintf(stderr, "%s: no space for %s\n", Pn, p);
		exit(1);
	}
	(void) strcpy((*d)[*x], p);
	*x += 1;
}


/*
 * find_ch_ino() comes from ../common/fchi.frag.
 * lkupdev() comes from ../common/fchi.frag.
 * read_dcache() and write_dcache() come from ../common/dvch.frag.
 */

#if	defined(HASDCACHE)
#define	DCACHE_CLONE	rw_clone_sect
#define	DCACHE_CLR	clr_sect	/* function to clear clone cache
					 * when reading the device cache
					 * file fails */
# if	_SCOV<500
#define	DVCH_CHOWN	1		/* no fchown() below Release 5.0 */
# endif	/* _SCOV<500 */
#endif	/* defined(HASDCACHE) */
