/*
 * dmnt.c - SunOS (Solaris 1.x and 2.x) mount support functions for lsof
 */


/*
 * Copyright 1994 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 1994 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: dmnt.c,v 1.14 96/02/21 08:09:25 abe Exp $";
#endif


#include "lsof.h"


_PROTOTYPE(static char *getmntdev,(char *o, int l, struct stat *s, char *f));


/*
 * getmntdev() - get mount entry's device number
 */

static char *
getmntdev(o, l, s, f)
	char *o;			/* start of device option */
	int l;				/* length of device keyword (not
					 * including `=') */
	struct stat *s;			/* pointer to stat buffer to create */
	char *f;			/* file system type */
{
	char *opte;

	memset((char *)s, 0, sizeof(struct stat));
	if ((opte = x2dev(o + l + 1, &s->st_dev)) == NULL)
		return(NULL);
	s->st_mode = S_IFDIR | 0777;

#if	defined(HASFSTYPE)
	if (f) {
		(void) strncpy(s->st_fstype, f, sizeof(s->st_fstype));
		s->st_fstype[sizeof(s->st_fstype) - 1] = '\0';
	}
#endif	/* defined(HASFSTYPE) */

	return(opte);
}


/*
 * readmnt() - read mount table
 */

int
readmnt()
{
	int devl, ignore;
	char *dn = NULL;
	int err = 0;
	char *ln;
	FILE *mfp;
	struct mounts *mtp;
	char *opt, *opte;
	struct stat sb;

#if	defined(solaris)
	struct mnttab me;
	struct mnttab *mp;
#else
	struct mntent *mp;
#endif

#if	defined(HASPROCFS)
	int procfs = 0;
#endif

/*
 * Open access to the mount table and read mount table entries.
 */

#if	defined(solaris)
	if ((mfp = fopen(MNTTAB, "r")) == NULL) {
                (void) fprintf(stderr, "%s: can't access %s\n", Pn, MNTTAB);
                return(0);
        }
	for (mp = &me; getmntent(mfp, mp) == 0;)
#else
        if ((mfp = setmntent(MOUNTED, "r")) == NULL) {
                (void) fprintf(stderr, "%s: can't access %s\n", Pn, MOUNTED);
                return(0);
        }
        while ((mp = getmntent(mfp)) != NULL)
#endif	/* solaris */

	{

	/*
	 * Skip loop-back mounts, since they are aliases for legitimate file
	 * systems and there is no way to determine that a vnode refers to a
	 * loop-back alias.
	 */

#if	defined(solaris)
		if (strcmp(mp->mnt_fstype, MNTTYPE_LO) == 0)
#else	/* !defined(solaris) */
		if (strcmp(mp->mnt_type, MNTTYPE_LO) == 0)
#endif	/* defined(solaris) */

			continue;
/*
 * Check for a "ignore" type (SunOS) or "ignore" option (Solaris).
 */

#if	defined(solaris)
		if (hasmntopt(mp, MNTOPT_IGNORE))
#else	/* !defined(solaris) */
		if (strcmp(mp->mnt_type, MNTTYPE_IGNORE) == 0)
#endif	/* defined(solaris) */

			ignore = 1;
		else
			ignore = 0;

	/*
	 * Interpolate a possible symbolic directory link, unless this
	 * is an "ignore" entry.
	 */
		if (dn)
			(void) free((FREE_P *)dn);

#if	defined(solaris)
		if ((dn=(char *)malloc((MALLOC_S)(strlen(mp->mnt_mountp) + 1)))
#else
		if ((dn = (char *)malloc((MALLOC_S)(strlen(mp->mnt_dir) + 1)))
#endif
		== NULL) {
			err = 1;
			break;
		}

#if	defined(solaris)
		(void) strcpy(dn, mp->mnt_mountp);
#else
		(void) strcpy(dn, mp->mnt_dir);
#endif

		if (!ignore) {
		    if ((ln = Readlink(dn)) == NULL) {
			if (!Fwarn) {
			    (void) fprintf(stderr,
			      "      Output information may be incomplete.\n");
			}
			err = 2;
			continue;
		    }
		} else
		    ln = dn;
		if (ln != dn) {
			(void) free((FREE_P *)dn);
			dn = ln;
		}
	/*
	 * Stat() the directory.
	 *
	 * Avoid the stat() if the mount entry has an "ignore" type (SunOS)
	 * or "ignore" option (Solaris) and try to use the mount entry's
	 * device number instead.
	 */

#if	defined(solaris)
		opt = hasmntopt(mp, MNTOPT_DEV);
		devl = strlen(MNTOPT_DEV);
#else	/* !defined(solaris) */
		opt = hasmntopt(mp, MNTINFO_DEV);
		devl = strlen(MNTINFO_DEV);
#endif	/* defined(solaris) */

		if (ignore) {
		    if (opt == NULL
		    ||  (opte = getmntdev(opt, devl, &sb,

#if	defined(HASFSTYPE)
# if	defined(solaris)
					  mp->mnt_fstype
# else	/* !defined(solaris) */
					  NULL
# endif	/* defined(solaris) */
#else	/* !defined(HASFSTYPE) */
					  NULL
#endif	/* defined(hasfstype) */

		    ))
		    == NULL)
			continue;
		} else if (statsafely(dn, &sb)) {
		    if (!Fwarn) {
			(void) fprintf(stderr,
			    "%s: WARNING: can't stat() %s file system %s\n",

#if	defined(solaris)
			    Pn, mp->mnt_fstype, mp->mnt_mountp);
#else	/* !defined(solaris) */
			    Pn, mp->mnt_type, mp->mnt_dir);
#endif	/* defined(solaris) */

			(void) fprintf(stderr,
			    "      Output information may be incomplete.\n");
		    }
		    err = 2;
		    if (opt != NULL) {
			if ((opte = getmntdev(opt, devl, &sb,

#if	defined(HASFSTYPE)
# if	defined(solaris)
					      mp->mnt_fstype
# else	/* !defined(solaris) */
					      NULL
# endif	/* defined(solaris) */
#else	/* !defined(HASFSTYPE) */
					      NULL
#endif	/* defined(hasfstype) */

			))
			!= NULL) {
			    if (!Fwarn)
				(void) fprintf(stderr,
				    "      assuming \"%.*s\" from %s\n",
				    (opte - opt), opt,

#if	defined(solaris)
				    MNTTAB
#else	/* !defined(solaris) */
				    MOUNTED
#endif	/* defined(solaris) */

				);
			} else
			    opt = NULL;
		    }
		    if (opt == NULL)
			continue;
		}
	/*
	 * Allocate and fill a local mount structure.
	 */
		if ((mtp = (struct mounts *)malloc(sizeof(struct mounts)))
		== NULL) {
			err = 1;
			break;
		}
		if ((mtp->fsname =

#if	defined(solaris)
			(char *)malloc((MALLOC_S)(strlen(mp->mnt_special)+1)))
#else
			(char *)malloc((MALLOC_S)(strlen(mp->mnt_fsname)+1)))
#endif

		== NULL) {
			err = 1;
			break;
		}

#if	defined(solaris)
		(void) strcpy(mtp->fsname, mp->mnt_special);
#else
		(void) strcpy(mtp->fsname, mp->mnt_fsname);
#endif

#if	defined(HASFSTYPE)
		if ((mtp->fstype =
			(char *)malloc((MALLOC_S)(strlen(sb.st_fstype) + 1)))
		== NULL) {
			err = 1;
			break;
		}
		(void) strcpy(mtp->fstype, sb.st_fstype);
#endif	/* HASFSTYPE */

		mtp->dir = dn;
		dn = NULL;
		mtp->next = Mtab;
		mtp->dev = sb.st_dev;
		mtp->rdev = sb.st_rdev;
		mtp->inode = sb.st_ino;
		mtp->mode = sb.st_mode;

#if	defined(HASPROCFS)

		if (strcmp(sb.st_fstype, HASPROCFS) == 0) {

		/*
		 * Save information on exactly on proc file system.
		 */
			if (procfs)
				Mtprocfs = NULL;
			else {
				procfs = 1;
				Mtprocfs = mtp;
			}
		}
#endif

		Mtab = mtp;
        }

#if	defined(solaris)
	(void) fclose(mfp);
#else
        (void) endmntent(mfp);
#endif

	if (dn)
		(void) free((FREE_P *)dn);
/*
 * Handle errors.
 */
	switch (err) {
	case 1:
		(void) fprintf(stderr, "%s: no space for mount at %s (%s)\n",

#if	defined(solaris)
			Pn, mp->mnt_special, mp->mnt_mountp);
#else
			Pn, mp->mnt_fsname, mp->mnt_dir);
#endif

		return(0);
	case 2:
		return(1);
	}
	return(1);
}


/*
 * readvfs() - read vfs structure
 */

struct l_vfs *
readvfs(ka, la, lv)
	struct vfs *ka;			/* vfs structure kernel address, if
					 * must be read from kernel */
	struct vfs *la;			/* local vfs structure address, non-
					 * NULL if already read from kernel */
	struct vnode *lv;		/* local vnode */
{
	struct vfs *v, tv;
	struct l_vfs *vp;

#if	!defined(solaris)
	struct mount m;
	dev_t td;
# endif

	if (!ka && !la)
		return(NULL);
	for (vp = Lvfs; vp; vp = vp->next) {
		if (ka == vp->addr)
			return(vp);
	}
	if ((vp = (struct l_vfs *)malloc(sizeof(struct l_vfs))) == NULL) {
		(void) fprintf(stderr, "%s: PID %d, no space for vfs\n",
			Pn, Lp->pid);
		exit(1);
	}
	vp->dir = NULL;
	vp->fsname = NULL;

#if	defined(HASFSINO)
	vp->fs_ino = 0;
#endif	/* defined(HASFSINO) */

/*
 * Read vfs structure from kernel, if necessary.
 */
	if (la)
		v = la;
	else {
		v = &tv;
		if (kread((KA_T)ka, (char *)v, sizeof(tv))) {
			(void) free((FREE_P *)vp);
			return(NULL);
		}
	}

#if	defined(HAS_AFS)
/*
 * Fake the device number for an AFS device.
 */
# if	defined(solaris)
	if (v->vfs_fstype == AFSfstype)
		v->vfs_dev = AFSDEV;
# endif	/* defined(solaris) */
#endif	/* defined(HAS_AFS) */

/*
 * Complete mount information.
 */

#if	defined(solaris)
	(void) completevfs(vp, (dev_t *)&v->vfs_dev);
#else
	if (Ntype == N_HSFS || Ntype == N_PCFS) {
		td = (dev_t)(v->vfs_fsid.val[0] & 0xffff);
		(void) completevfs(vp, &td);
	} else if (v->vfs_data) {
	       if (kread((KA_T)v->vfs_data, (char *)&m, sizeof(m)) == 0)
			(void) completevfs(vp, &m.m_dev);
	}
# if	defined(HAS_AFS)
	else if (!lv->v_data && AFSVfsp && lv->v_vfsp == AFSVfsp) {
		td = (dev_t)AFSDEV;
		(void) completevfs(vp, &td);
	}
# endif	/* defined(HAS_AFS) */
#endif	/* solaris */

	vp->next = Lvfs;
	vp->addr = ka;
	Lvfs = vp;
	return(vp);
}


/*
 * The completevfs() function is obtained from ../common/cvfs.frag.
 */

/* CVFS_DEVSAVE is not defined. */
#define	USECVFS		1
