/*
 * proc.c - common process and file structure 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: proc.c,v 1.17 96/02/21 09:13:03 abe Exp $";
#endif


#include "lsof.h"


/*
 * comppid() - compare PIDs
 */

int
comppid(a1, a2)
	COMP_P *a1, *a2;
{
	struct lproc **p1 = (struct lproc **)a1;
	struct lproc **p2 = (struct lproc **)a2;

	if ((*p1)->pid < (*p2)->pid)
		return(-1);
	if ((*p1)->pid > (*p2)->pid)
		return(1);
	return(0);
}


/*
 * alloc_lfile() - allocate local file structure space
 */

void
alloc_lfile(nm, num)
	char *nm;
	int num;
{
	char buf[32], *cp;
	struct str_lst *sp;

	if (Lf) {
/*
 * If reusing a previously allocated structure, release any allocated
 * space it was using.
 */
		if (Lf->dev_ch)
			(void) free((FREE_P *)Lf->dev_ch);
		if (Lf->nm)
			(void) free((FREE_P *)Lf->nm);
/*
 * Othwerise, allocate a new structure.
 */
	} else if ((Lf = (struct lfile *)malloc(sizeof(struct lfile)))
	== NULL) {
		(void) fprintf(stderr, "%s: no local file space at PID %d\n",
			Pn, Lp->pid);
		exit(1);
	}
/*
 * Initialize the structure.
 */
	Lf->access = Lf->lock = ' ';
	Lf->dev_def = Lf->inp_ty = Lf->is_chr_dev = Lf->is_com = Lf->is_nfs
		    = Lf->is_stream = Lf->off_def = Lf->sz_def = 0;

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

	Lf->inode = Lf->off = 0;
	if (Lp->pss & PS_PRI)
		Lf->sf = Lp->sf;
	else
		Lf->sf = 0;
	Lf->iproto[0] = Lf->type[0] = '\0';
	if (nm) {
		(void) strncpy(Lf->fd, nm, FDLEN - 1);
		Lf->fd[FDLEN - 1] = '\0';
	} else if (num >= 0) {
		if (num < 10000)
			(void) sprintf(Lf->fd, "%4d", num);
		else
			(void) sprintf(Lf->fd, "*%03d", num % 1000);
	} else
		Lf->fd[0] = '\0';
	Lf->dev_ch = Lf->fsdir = Lf->fsdev = Lf->nm = Lf->nma = NULL;
	Lf->ch = -1;

#if	defined(HASNCACHE)
	Lf->na = (KA_T)NULL;
#endif	/* defined(HASNCACHE) */

	Lf->next = NULL;
	Ntype = N_REGLR;
	Namech[0] = '\0';

#if	defined(HASLFILEADD) && defined(SETLFILEADD)
/*
 * Do local initializations.
 */
	SETLFILEADD
#endif	/* defined(HASLFILEADD) && defined(SETLFILEADD) */

/*
 * See if the file descriptor has been selected.
 */
	if (Fdl == NULL || (nm == NULL && num < 0))
		return;
	if ((cp = nm) != NULL) {
		while (*cp && *cp == ' ')
			cp++;
	} else {
		(void) sprintf(buf, "%d", num);
		cp = buf;
	}
	for (sp = Fdl; sp; sp = sp->next) {
		if (strcmp(sp->str, cp) == 0) {
			Lf->sf |= SELFD;
			return;
		}
	}
}


/*
 * alloc_lproc() - allocate local proc structure space
 */

void
alloc_lproc(pid, pgrp, uid, cmd, pss, sf)
	int pid;			/* Process ID */
	int pgrp;			/* process group ID */
	UID_ARG uid;			/* User ID */
	char *cmd;			/* command */
	int pss;			/* process select state */
	int sf;				/* process select flags */
{
	static int sz;

	if (Lproc == NULL) {
		if ((Lproc = (struct lproc *)malloc(
		    (MALLOC_S) (LPROCINCR * sizeof(struct lproc))))
		== NULL) {
		    (void) fprintf(stderr,
			"%s: no malloc space for %d local proc structures\n",
			Pn, LPROCINCR);
		    exit(1);
		}
		sz = LPROCINCR;
	} else if ((Nlproc + 1) > sz) {
		sz += LPROCINCR;
		if ((Lproc = (struct lproc *)realloc((MALLOC_P *)Lproc,
		    (MALLOC_S) (sz * sizeof(struct lproc))))
		== NULL) {
		    (void) fprintf(stderr,
			"%s: no realloc space for %d local proc structures\n",
			Pn, sz);
		    exit(1);
		}
	}
	Lp = &Lproc[Nlproc++];
	(void) strncpy(Lp->cmd, cmd, CMDL-1);
	Lp->cmd[CMDL-1] = '\0';
	Lp->pid = pid;
	Lp->pgrp = pgrp;
	Lp->file = NULL;
	Lp->sf = (short)sf;
	Lp->pss = (short)pss;
	Lp->uid = (uid_t)uid;
}


/*
 * examine_lproc() - examine local process
 *
 * return: 1 = last process
 *
 * Note: the structures of an unselected process are not deallocated,
 *	 since they contain pointers to allocated and non-allocated
 *	 strings alike.
 */

int
examine_lproc()
{
	int sbp = 0;

	if (RptTm)
		return(0);
/*
 * List the process if the process is selected and:
 *
 *	o  listing is limited to a single PID selection -- this one;
 *
 *	o  listing is selected by an ANDed option set (not all options)
 *	   that includes a single PID selection -- this one.
 */
	if ((Lp->sf & SELPID) && !Selall) {
		if ((Selflags == SELPID)
		||  (Fand && (Selflags & SELPID))) {
			sbp = 1;
			Npuns--;
		}
	}
	if (Lp->pss && Npid == 1 && sbp) {
		print_proc();
		Lp->pss = 0;
	}
/*
 * Deprecate an unselected (or listed) process.
 */
	if ( ! Lp->pss)
		Nlproc--;
/*
 * Indicate last-process if listing is limited to PID selections,
 * and all selected processes have been listed.
 */
	return((sbp && Npuns == 0) ? 1 : 0);
}


/*
 * is_cmd_excl() - is command excluded?
 */

int
is_cmd_excl(cmd, pss, sf)
	char *cmd;			/* command name */
	short *pss;			/* process state */
	short *sf;			/* process select flags */
{
	struct str_lst *sp;
/*
 * The command is not excluded if no command selection was requested,
 * or if its name matches any -c <command> specification.
 * 
 */
	if ((Selflags & SELCMD) == 0)
		return(0);
	for (sp = Cmdl; sp; sp = sp->next) {
		if (strncmp(sp->str, cmd, sp->len) == 0) {
			*pss |= PS_PRI;
			*sf |= SELCMD;
			return(0);
		}
	}
/*
 * The command name doesn't match any -c <command> specification.
 *
 * It's excluded if the only selection condition is command name,
 * or if command name selection is part of an ANDed set.
 */
	if (Selflags == SELCMD)
		return(1);
	return (Fand ? 1 : 0);
}


/*
 * is_file_sel() - is file selected?
 */

int
is_file_sel(lf)
	struct lfile *lf;		/* lfile structure pointer */
{
	if ( !lf || !lf->sf)
		return(0);
	if (Selall)
		return(1);
	if (Fand && ((lf->sf & Selflags) != Selflags))
		return(0);
	return(1);
}


/*
 * is_proc_excl() - is process excluded?
 */

int
is_proc_excl(pid, pgrp, uid, pss, sf)
	int pid;			/* Process ID */
	int pgrp;			/* process group ID */
	UID_ARG uid;			/* User ID */
	short *pss;			/* process select state for lproc */
	short *sf;			/* select flags for lproc */
{
	int i;

	*pss = *sf = 0;

#if	defined(HASSECURITY)
/*
 * The process is excluded by virtue of the security option if it
 * isn't owned by the owner of this lsof process.
 */
	if (Myuid && Myuid != (uid_t)uid)
		return(1);
#endif

/*
 * If the listing of all processes is selected, then this one
 * is not excluded.
 */
	if (Selall) {
		*pss = PS_PRI;
		*sf = SELALL;
		return(0);
	}
/*
 * If the listing of processes has been specified by process group ID, see
 * if this one is specified.
 */
	if (Npgrp && (Selflags & SELPGRP)) {
		for (i = 0; i < Npgrp; i++) {
			if (Spgrp[i] == pgrp) {
				*pss = PS_PRI;
				*sf = SELPGRP;
				if (Selflags == SELPGRP)
					return(0);
				break;
			}
		}
		if (Selflags == SELPGRP && ! *sf)
			return(1);
	}
/*
 * If the listing of processes has been specified by PID, see
 * if this one is specified.
 */
	if (Npid && (Selflags & SELPID)) {
		for (i = 0; i < Npid; i++) {
			if (Spid[i] == pid) {
				*pss = PS_PRI;
				*sf |= SELPID;
				if (Selflags == SELPID)
					return(0);
				break;
			}
		}
		if (Selflags == SELPID && ! *sf)
			return(1);
	}
/*
 * If the listing of processes has been specified by UID, see
 * if the owner of this process has been specified.
 */
	if (Nuid && (Selflags & SELUID)) {
		for (i = 0; i < Nuid; i++) {
			if (Suid[i] == (uid_t)uid) {
				*pss = PS_PRI;
				*sf |= SELUID;
				if (Selflags == SELUID)
					return(0);
				break;
			}
		}
		if (Selflags == SELUID && (*sf & SELUID) == 0)
			return(1);
	}
/*
 * When neither the process group ID, nor the PID, nor the UID is selected:
 *
 *	If list option ANDing of process group IDs, PIDs or UIDs is specified,
 *	the process is excluded;
 *
 *	Otherwise, it's not excluded by the tests of this function.
 */
	if ( ! *sf)
		return((Fand && (Selflags & (SELPGRP|SELPID|SELUID))) ? 1 : 0);
/*
 * When the process group ID, PID, or UID is selected and the process group
 * ID, PID, or UID list option has been specified:
 *
 *	If list option ANDing has been specified, and the correct
 *	combination of process group ID, PID, and UID is selected, reply that
 *	the process is not excluded;
 * or
 *	If list option ANDing has not been specified, reply that the
 *	process is not excluded by the tests of this function.
 */
	if (Selflags & (SELPGRP|SELPID|SELUID)) {
		if (Fand)
			return(((Selflags & (SELPGRP|SELPID|SELUID)) != *sf) ?
				1 : 0);
		return(0);
	}
/*
 * Finally, when neither the process group ID, nor the PID, nor the UID is
 * selected, and no process group ID, PIDm or UID list option has been
 * specified:
 *
 *	If list option ANDing has been specified, this process is
 *	excluded;
 *
 *	Otherwise, it isn't excluded by the tests of this function.
 */
	return(Fand ? 1 : 0);
}


/*
 * link_lfile() - link local file structures
 */

void
link_lfile()
{
	Lp->pss |= PS_SEC;
	if (Plf)
		Plf->next = Lf;
	else
		Lp->file = Lf;
	Plf = Lf;
	Lf = NULL;
}


/*
 * print_proc() - print process
 */

void
print_proc()
{
	char *cp;
	int lc, st, ty;
	int sel = 0;
/*
 * If nothing in the process has been selected, skip it.
 */
	if (!Lp->pss)
		return;
	if (Fterse) {

	/*
	 * The mode is terse and something in the process has been
	 * selected.  If options are being OR'd, print the PID;
	 * if AND'd, see if anything has been selected.
	 */
		if (Fand) {
			for (Lf = Lp->file; Lf; Lf = Lf->next) {
				if (is_file_sel(Lf)) {
					sel++;
					break;
				}
			}
		} else
			sel = 1;
		if (sel)
			(void) printf("%d\n", Lp->pid);
		return;
	}
/*
 * If fields have been selected, output the process-only ones, provided
 * that some file has also been selected.
 */
	if (Ffield) {
		for (Lf = Lp->file; Lf; Lf = Lf->next) {
			if (is_file_sel(Lf))
				break;
		}
		if (Lf == (struct lfile *)NULL)
			return;
		(void) printf("%c%d%c",
			LSOF_FID_PID, Lp->pid, Terminator);
		if (FieldSel[LSOF_FIX_PGRP].n)
			(void) printf("%c%d%c",
				LSOF_FID_PGRP, Lp->pgrp, Terminator);
		if (FieldSel[LSOF_FIX_CMD].n)
			(void) printf("%c%s%c",
				LSOF_FID_CMD, Lp->cmd, Terminator);
		if (FieldSel[LSOF_FIX_UID].n)
			(void) printf("%c%d%c",
				LSOF_FID_UID, Lp->uid, Terminator);
		if (FieldSel[LSOF_FIX_LOGIN].n) {
			cp = printuid((UID_ARG)Lp->uid, &ty);
			if (ty == 0)
				(void) printf("%c%s%c",
					LSOF_FID_LOGIN, cp, Terminator);
		}
		if (Terminator == '\0')
			putchar('\n');
	}
/*
 * Print files.
 */
	for (Lf = Lp->file; Lf; Lf = Lf->next) {
		if (!is_file_sel(Lf))
			continue;
	/*
	 * If no field output selected, print dialects-specific formatted
	 * output.
	 */
		if (!Ffield) {
			print_file();
			continue;
		}
	/*
	 * Print selected fields.
	 */
		lc = st = 0;
		if (FieldSel[LSOF_FIX_FD].n) {
			for (cp = Lf->fd; *cp == ' '; cp++)
				;
			if (*cp) {
				(void) printf("%c%s%c",
					LSOF_FID_FD, cp, Terminator);
				lc++;
			}
		}
		if (FieldSel[LSOF_FIX_ACCESS].n) {
			(void) printf("%c%c%c",
				LSOF_FID_ACCESS, Lf->access, Terminator);
			lc++;
		}
		if (FieldSel[LSOF_FIX_LOCK].n) {
			(void) printf("%c%c%c",
				LSOF_FID_LOCK, Lf->lock, Terminator);
			lc++;
		}
		if (FieldSel[LSOF_FIX_TYPE].n) {
			for (cp = Lf->type; *cp == ' '; cp++)
				;
			if (*cp) {
				(void) printf("%c%s%c",
					LSOF_FID_TYPE, cp, Terminator);
				lc++;
			}
		}
		if (FieldSel[LSOF_FIX_DEVCH].n && Lf->dev_ch && Lf->dev_ch[0]) {
			for (cp = Lf->dev_ch; *cp == ' '; cp++)
				;
			if (*cp) {
				(void) printf("%c%s%c",
					LSOF_FID_DEVCH, cp, Terminator);
				lc++;
			}
		}
		if (FieldSel[LSOF_FIX_DEVN].n && Lf->dev_def) {
			(void) printf("%c0x%lx%c",
				LSOF_FID_DEVN, (unsigned long)Lf->dev,
				Terminator);
			lc++;
		}
		if (FieldSel[LSOF_FIX_SIZE].n && Lf->sz_def) {
			(void) printf("%c%d%c",
				LSOF_FID_SIZE, Lf->sz, Terminator);
			lc++;
		}
		if (FieldSel[LSOF_FIX_OFFSET].n && Lf->off_def) {
			if ((unsigned int)Lf->off < 100000000)
				(void) printf("%c0t%d%c",
					LSOF_FID_OFFSET, Lf->off, Terminator);
			else
				(void) printf("%c0x%x%c",
					LSOF_FID_OFFSET, Lf->off, Terminator);
			lc++;
		}
		if (FieldSel[LSOF_FIX_INODE].n && Lf->inp_ty == 1) {
			(void) printf("%c%lu%c",
				LSOF_FID_INODE, Lf->inode, Terminator);
			lc++;
		}
		if (FieldSel[LSOF_FIX_PROTO].n && Lf->inp_ty == 2) {
			for (cp = Lf->iproto; *cp == ' '; cp++)
				;
			if (*cp) {
				(void) printf("%c%s%c",
					LSOF_FID_PROTO, cp, Terminator);
				lc++;
			}
		}
		if (FieldSel[LSOF_FIX_STREAM].n && Lf->nm && Lf->is_stream) {
			if (strncmp(Lf->nm, "STR:", 4) == 0
			||  strcmp(Lf->iproto, "STR") == 0) {
				putchar(FieldSel[LSOF_FIX_STREAM].id);
				printname(0);
				putchar(Terminator);
				lc++;
				st++;
			}
		}
		if (st == 0 && FieldSel[LSOF_FIX_NAME].n) {
			putchar(FieldSel[LSOF_FIX_NAME].id);
			printname(0);
			putchar(Terminator);
			lc++;
		}

#if	defined(HASFIELDAP1)
		LISTLFILEAP1
#endif	/* defined(HASFIELDAP1) */

#if	defined(HASFIELDAP2)
		LISTLFILEAP2
#endif	/* defined(HASFIELDAP2) */

#if	defined(HASFIELDAP3)
		LISTLFILEAP3
#endif	/* defined(HASFIELDAP3) */

#if	defined(HASFIELDAP4)
		LISTLFILEAP4
#endif	/* defined(HASFIELDAP4) */

#if	defined(HASFIELDAP5)
		LISTLFILEAP5
#endif	/* defined(HASFIELDAP5) */

#if	defined(HASFIELDAP6)
		LISTLFILEAP6
#endif	/* defined(HASFIELDAP6) */

#if	defined(HASFIELDAP7)
		LISTLFILEAP7
#endif	/* defined(HASFIELDAP7) */

#if	defined(HASFIELDAP8)
		LISTLFILEAP8
#endif	/* defined(HASFIELDAP8) */

#if	defined(HASFIELDAP9)
		LISTLFILEAP9
#endif	/* defined(HASFIELDAP9) */

		if (Terminator == '\0' && lc)
			putchar('\n');
	}
}
