/*
 * dnode.c - SCO Unix node 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: dnode.c,v 1.17 96/01/11 12:55:21 abe Exp $";
#endif


#include "lsof.h"


_PROTOTYPE(static struct l_dev * finddev,(dev_t *dev, int stream));


/*
 * finddev() - look up device by device number
 */

static struct l_dev *
finddev(dev, stream)
	dev_t *dev;			/* device */
	int stream;			/* stream if 1 */
{
	struct clone *c;
	struct l_dev *dp;
/*
 * Search device table for match.
 */
	if ((dp = lkupdev(dev, 0)) != (struct l_dev *)NULL)
		return(dp);
/*
 * Search for clone.
 */
	if (stream && Clone) {
		for (c = Clone; c; c = c->next) {
			if (major(*dev) == minor(Devtp[c->dx].rdev))
				return(&Devtp[c->dx]);
		}
	}
	return(NULL);
}


/*
 * process_node() - process node
 */

void
process_node(na)
	caddr_t na;			/* inode kernel space address */
{
	char *cp, *cp1, dev_ch[32];
	short dl;
	struct l_dev *dp;
	struct filock fl;
	struct inode i;
	struct in_addr *ia;
	short ity, udpsf, udpsl, udptm;
	int j, k, l;
	KA_T ka, qp;
	struct mounts *lm;
	struct module_info mi;
	unsigned short *n;
	struct inpcb *p, pcb;
	int port;
	int pt = -1;
	struct queue q;
	struct qinit qi;
	struct stdata sd;
	char *tn;
	int type;
	struct udpdev udp;

#if	defined(HAS_NFS)
	struct rnode r;
#endif	/* defined(HAS_NFS) */

#if	_SCOV>=500
	short hpps = 0;
	unsigned short *n1;
	struct pipeinode pi;
#endif	/* _SCOV>=500 */

/*
 * Read the inode.
 */
	if ( ! na) {
		enter_nm("no inode address");
		return;
	}
	if (readinode((struct inode *)na, &i)) {
		enter_nm(Namech);
		return;
	}

# if    defined(HASNCACHE)
	Lf->na = (unsigned long)na;
# endif /* defined(HASNCACHE) */

/*
 * Identify the node type.
 */
	if (HaveSockdev && (i.i_ftype & IFMT) == IFCHR
	       &&  major(i.i_rdev) == Sockdev)
	{

	/*
	 * Process a socket.
	 */
		process_socket(&i);
		return;
	}
	ity = i.i_fstyp;
	type = i.i_ftype & IFMT;
	if (ity < 1 || ity > Fsinfomax || Fsinfo[ity-1] == NULL) {

#if	_SCOV>=500
	    if (ity) {
#endif	/* _SCOV>=500 */

		(void) sprintf(Namech, "unknown fstyp (%d) in inode", ity);
		enter_nm(Namech);
		return;

#if	_SCOV>=500
	    }
#endif	/* _SCOV>=500 */

	}
	if (ity && strcasecmp(Fsinfo[ity-1], "HS") == 0)
		Ntype = N_HSFS;

#if	defined(HAS_NFS)
	 else if (ity && strcasecmp(Fsinfo[ity-1], "NFS") == 0) {

	/*
	 * Get information on NFS file.
	 */
		Ntype = N_NFS;
		Lf->is_nfs = 1;
		if (Fnfs)
			Lf->sf |= SELNFS;
		if (i.i_fsptr == NULL || readrnode((caddr_t)i.i_fsptr, &r)) {
			(void) sprintf(Namech, "can't read rnode (%#x)",
			    i.i_fsptr);
			enter_nm(Namech);
			return;
		}

# if    defined(HASNCACHE)
		Lf->na = (unsigned long)i.i_fsptr;
# endif /* defined(HASNCACHE) */

	}
#endif	/* defined(HAS_NFS) */

	else {

	/*
	 * Determine the node type from the inode file type.
	 */
		switch (type) {

		case IFBLK:
			Ntype = N_BLK;
			break;
		case IFCHR:
			Ntype = N_CHR;
			break;
		case IFIFO:
			Ntype = N_FIFO;
			break;
		case IFMPB:
		case IFMPC:
			Ntype = N_MPC;
			break;
		}
	}
/*
 * Obtain lock information.
 */
	if (i.i_filocks) {
		if ((kread((KA_T)i.i_filocks, (char *)&fl, sizeof(fl)))) {
			(void) sprintf(Namech, "can't read filock (%#x)",
				i.i_filocks);
			enter_nm(Namech);
			return;
		} else {
			if (fl.set.l_type == F_RDLCK)
				Lf->lock = 'R';
			else if (fl.set.l_type == F_WRLCK)
				Lf->lock = 'W';
			else if (fl.set.l_type == (F_RDLCK | F_WRLCK))
				Lf->lock = 'u';
		}
	}

#if	_SCOV>=500
/*
 * See if a FIFO node is an HPPS node -- 3.2v5.0.0 and higher.
 */
	if (Ntype == N_FIFO && ity && strcasecmp(Fsinfo[ity-1], "HPPS") == 0)
	{
		hpps = 1;
		if (i.i_fsptr) {
		    (void) sprintf(dev_ch, "0x%08x", i.i_fsptr);
		    enter_dev_ch(dev_ch);
		    if (kread((KA_T)i.i_fsptr, (char *)&pi, sizeof(pi)) == 0)
			hpps = 2;
		}
	}
#endif	/* _SCOV>=500 */

/*
 * Determine the device.
 */
	switch (Ntype) {
	case N_BLK:
	case N_FIFO:
	case N_HSFS:
	case N_REGLR:

#if	_SCOV>=500
		if (hpps)
			break;
#endif	/* _SCOV>=500 */

		Lf->dev = i.i_dev;
		Lf->dev_def = 1;
		break;
	case N_CHR:
		Lf->dev = i.i_rdev;
		Lf->dev_def = 1;
		if (i.i_sptr) {

		/*
		 * Namech may be:
		 *    /dev/* name if it exists for i.i_rdev;
		 *    cdevsw[].d_name if it exists for major(i.i_rdev);
		 *    "STR:" otherwise.
		 */
		    (void) strcpy(Namech, "STR:");
		    Lf->is_stream = 1;
		    k = strlen(Namech);
		    cp = NULL;
		    if ((dp = finddev(&i.i_rdev, 1)) != NULL) {
			(void) strcpy(&Namech[k], dp->name);
			k += strlen(dp->name);
			if ((cp = strrchr(dp->name, '/')) != NULL)
			    cp++;
		    } else if ((j = major(i.i_rdev)) < Cdevcnt
			   &&  (cp = Cdevsw[j]) != NULL)
		    {
			(void) strcpy(Namech, cp);
			k += strlen(cp);
		    }
		/*
		 * Get the module names of all queue elements of the stream's
		 * sd_wrq queue.  Skip module names that end in "head", 
		 * match the last component of the /dev name, or match the
		 * cdevsw[].d_name.
		 */
		    p = NULL;
		    if (kread((KA_T)i.i_sptr, (char *)&sd, sizeof(sd)) == 0) {
			dl = sizeof(dev_ch) - 1;
			dev_ch[dl] = '\0';
			qp = (KA_T)sd.sd_wrq;
			for (j = 0; qp && j < 20; j++, qp = (KA_T)q.q_next) {
			    if (kread(qp, (char *)&q, sizeof(q)))
				break;
			    if ((ka = (KA_T)q.q_qinfo) == (KA_T)NULL
			    ||  kread(ka, (char *)&qi, sizeof(qi)))
				continue;
			    if ((ka = (KA_T)qi.qi_minfo) == (KA_T)NULL
			    ||  kread(ka, (char *)&mi, sizeof(mi)))
				continue;
			    if ((ka = (KA_T)mi.mi_idname) == (KA_T)NULL
			    ||  kread(ka, dev_ch, dl))
				continue;
			    if ((l = strlen(dev_ch)) < 1)
				continue;
			    if (l >= 4 && strcmp(&dev_ch[l - 4], "head") == 0)
				continue;
			    if (cp && strcmp(cp, dev_ch) == 0) {
				if (q.q_ptr && pt < 0) {

				/*
				 * If this is a TCP or UDP module and the
				 * queue structure has a private pointer in
				 * q_ptr, save it as a PCB address.
				 */
				    if (strcasecmp(cp, "tcp") == 0) {
					pt = 0;
					(void) strcpy(Lf->iproto, "TCP");
				    } else if (strcasecmp(cp, "udp") == 0) {
					pt = 1;
					(void) strcpy(Lf->iproto, "UDP");
				    }
				    if (pt >= 0)
					p = (struct inpcb *)q.q_ptr;
				    else
					pt = -1;
				}
				continue;
			    }
			    if (k) {
				if ((k + 2) > (MAXPATHLEN - 1))
				    break;
				(void) strcpy(&Namech[k], "->");
				k += 2;
			    }
			    if ((k + l) > (MAXPATHLEN - 1))
				break;
			    (void) strcpy(&Namech[k], dev_ch);
			    k += l;
			}
		    }
		    if (p && pt >= 0) {

		    /*
		     * If the stream has a TCP or UDP module with a PCB
		     * pointer, print any associated local and foreign
		     * Internet addresses.
		     */
			if (kread((KA_T)p, (char *)&pcb, sizeof(pcb)))
			    break;
			if (Fnet)
			    Lf->sf |= SELNET;
			if ((k + 1) > (MAXPATHLEN - 1))
			    break;
			Namech[k++] = ' ';
			Namech[k] = '\0';
			if (pt == 1 && pcb.inp_ppcb) {

			/*
			 * If this is a UDP stream, get the udpdev structure
			 * at the PCB's per-protocol address.  It may contain
			 * addresses.
			 */
			    if (kread((KA_T)pcb.inp_ppcb, (char *)&udp,
				       sizeof(udp))
			    == 0)
			    {

#if	_SCOV>=500
				if (udp.ud_lsin.sin_addr.s_addr != INADDR_ANY ||
				    udp.ud_lsin.sin_port != 0
				)
				    udpsl = 1;
				else
				    udpsl = 0;
#endif	/* _SCOV>=500 */

				if (udp.ud_fsin.sin_addr.s_addr != INADDR_ANY ||
				    udp.ud_fsin.sin_port != 0
				)
				    udpsf = 1;
				else
				    udpsf = 0;
			    }
			} else
			    udpsf = udpsl = 0;
		    /*
		     * Print the local address from the PCB.  If there is none,
		     * and if this is a 5.0.0 or greater UDP stream, and if it
		     * has a local address set, use it.
		     */
			ia = &pcb.inp_laddr;
			port = (int)ntohs(pcb.inp_lport);

#if	_SCOV>=500
			if (ia->s_addr == INADDR_ANY && port == 0 && udpsl) {
			    ia = &udp.ud_lsin.sin_addr;
			    port = (int)ntohs(udp.ud_lsin.sin_port);
			}

#endif	/* _SCOV>=500 */

			printinaddr(ia, port);
		    /*
		     * Print the foreign address from the PCB.  If there is
		     * none, and if this is a 5.0.0 or greater UDP stream, and
		     * if it has a local address set, use it.
		     */
			if (pcb.inp_faddr.s_addr != INADDR_ANY
			||  pcb.inp_fport != 0) {
			    ia = &pcb.inp_faddr;
			    port = (int)ntohs(pcb.inp_fport);
			    udptm = 0;
			} else if (udpsf) {
			    ia = &udp.ud_fsin.sin_addr;
			    port = (int)ntohs(udp.ud_fsin.sin_port);
			    udptm = 1;
			} else
			    ia = NULL;
			if (ia) {
			    cp = endnm();
			    if ((MAXPATHLEN - 1 - (cp - Namech)) >= 2)
				(void) strcat(cp, "->");
			    printinaddr(ia, port);
			    if (udptm) {
				if ((cp1 = udp_tm(udp.ud_ftime, &j)) != NULL) {
				    cp = endnm();
				    k = MAXPATHLEN - 1 - (cp - Namech);
				    if (k < j)
					j = k;
				    (void) strncpy(cp, cp1, j);
				    *(cp + j) = '\0';
				}
			    }
			}
			if (i.i_number == 0)
				Lf->inp_ty = 2;
		    }
		} else {
			if (strcasecmp(Fsinfo[ity-1], "COM") == 0)
				Ntype = N_COM;
			else
				Lf->is_chr_dev  = 1;
		}
		break;

#if	defined(HAS_NFS)
	case N_NFS:

#if	_SCOV<500
		Lf->dev = (dev_t)_makedev(~major(i.i_dev), minor(i.i_dev));
#else	/* _SCOV>=500 */
		Lf->dev = i.i_dev;
#endif	/* _SCOV<500 */

		Lf->dev_def = 1;
		break;
#endif	/* defined(HAS_NFS) */

	}
/*
 * Determine the inode number.
 */
	switch (Ntype) {
	case N_HSFS:

#if	_SCOV<500
	/*
	 * High Sierra inode numbers for versions below 5.0.0, as reported
	 * by "ls -i" and stat(2), is the lower 16 bits of i_number.
	 */
		if ((Lf->inode = (unsigned long)(i.i_number & 0xffff)))
#else	/* _SCOV>=500 */
		if ((Lf->inode = (unsigned long)i.i_number))
#endif	/* _SCOV<500 */

		    Lf->inp_ty = 1;
		break;

#if	defined(HAS_NFS)
	case N_NFS:

#if	_SCOV<500
		n = (unsigned short *)&r.r_fh.fh_pad[14];
		if ((Lf->inode = (unsigned long)ntohs(*n)))
		    Lf->inp_ty = 1;
		else if ((Lf->inode = (unsigned long)r.r_fh.fh_u.fh_fgen_u))
#else	/* _SCOV>=500 */
		n = (unsigned short *)&r.r_fh.fh_u.fh_fid_u[4];
		n1 = (unsigned short *)&r.r_fh.fh_u.fh_fid_u[2];
		if ((Lf->inode = (unsigned long)*n))
		    Lf->inp_ty = 1;
		else if ((Lf->inode = (unsigned long)*n1))
#endif	/* _SCOV<500 */

		    Lf->inp_ty = 1;
		break;
#endif	/* defined(HAS_NFS) */

	case N_BLK:
	case N_CHR:
	case N_COM:
	case N_FIFO:
	case N_REGLR:
		if ((Lf->inode = (unsigned long)i.i_number))
		    Lf->inp_ty = 1;
		break;
	}
/*
 * Determine the file size.
 */
	if (Foffset)
		Lf->off_def = 1;
	else {
		switch (Ntype) {
		case N_BLK:
			Lf->sz = (unsigned long)i.i_size;
			Lf->sz_def = 1;
			break;
		case N_CHR:
		case N_COM:
			if (!Fsize)
				Lf->off_def = 1;
			break;
		case N_FIFO:

#if	_SCOV>=500
			if (hpps == 2) {
				Lf->sz = (unsigned long)pi.count;
				Lf->sz_def = 1;
			}
#endif	/* _SCOV>=500 */

			if (!Fsize)
				Lf->off_def = 1;
			break;
		case N_HSFS:

#if	defined(HAS_NFS)
		case N_NFS:
			Lf->sz = (unsigned long)i.i_size;
			Lf->sz_def = 1;
			break;
#endif	/* defined(HAS_NFS) */

		case N_REGLR:
			if (type == IFREG || type == IFDIR)
				Lf->sz = (unsigned long)i.i_size;
				Lf->sz_def = 1;
			break;
		}
	}
/*
 * Format the type name.
 */
	switch (type) {
	case IFDIR:
		tn = "DIR";
		break;
	case IFBLK:
		tn = "BLK";
		break;
	case IFCHR:
		tn = "CHR";
		break;
	case IFREG:
		tn = "REG";
		break;
	case IFMPC:
		tn = "MPC";
		break;
	case IFMPB:
		tn = "MPB";
		break;
	case IFIFO:
		tn = "FIFO";
		break;

#if	defined(IFLNK)
	case IFLNK:
		tn = "LINK";
		break;
#endif	/* defined(IFLNK) */

	default:
		if (type > 9999)
			(void) sprintf(Lf->type, "*%03d", type % 1000);
		else
			(void) sprintf(Lf->type, "%4d", type);
		(void) strcpy(Namech, "unknown inode type");
		tn = NULL;
	}
	if (tn)
		(void) strcpy(Lf->type, tn);
/*
 * Save the file system names.
 */
	switch (Ntype) {
	case N_BLK:
	case N_FIFO:
	case N_HSFS:

#if	defined(HAS_NFS)
	case N_NFS:
#endif	/* defined(HAS_NFS) */

	case N_REGLR:
		if (Lf->dev_def) {
			for (lm = Mtab; lm; lm = lm->next) {
				if (Lf->dev == lm->dev) {
					Lf->fsdir = lm->dir;
					Lf->fsdev = lm->fsname;
					Lf->fs_ino = lm->inode;
					break;
				}
			}
		}
		break;
	}
/*
 * If this is a IFCHR file and it's missing an inode number, try to
 * supply one.
 */
	if (Lf->inp_ty == 0 && type == IFCHR && Lf->dev_def)
		find_ch_ino();
/*
 * Test for specified file.
 */
	if (Sfile && is_file_named(NULL, type))
		Lf->sf |= SELNM;

#if	_SCOV>=500
/*
 * If this is an HPPS node and no other name characters have been
 * entered, enter HPPS as the name.
 */
	if (hpps && Namech[0] == '\0')
		(void) strcpy(Namech, "HPPS");
#endif	/* _SCOV>=500 */

/*
 * Enter name characters.
 */
	if (Namech[0])
		enter_nm(Namech);
}
