/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: kern_proc.c 1.22 89/08/07 $"

/* $Log:	kern_proc.c,v $
 */

#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/signal.h"
#include "sys/mutex.h"
#include "sys/systm.h"
#include "sys/map.h"
#include "sys/user.h"
#include "sys/timer.h"
#include "sys/session.h"
#include "sys/proc.h"
#include "sys/buf.h"
#include "sys/acct.h"
#include "sys/vm.h"
#include "sys/file.h"
#include "sys/uio.h"
#include "sys/var.h"

#include "sys/reg.h"
#include "sys/pte.h"
#include "sys/psl.h"
#include "sys/intctl.h"

#ifdef DEBUG
extern	int	jc_debug;
#endif

/*
 * pfind()
 *	Find process via hash list.
 *
 * If process found return with process state (p_state) locked.
 * Otherwise return 0.
 *
 * Assumes caller knows the appropriate ipl to v_lock with.
 */

struct proc *
pfind(pid)
	int pid;
{
	register struct proc *p;
	spl_t s_ipl;

	/*
	 * Must lock proc_list to keep pidhash list consistent.
	 */
	s_ipl = p_lock(&proc_list, SPL_PROC_LIST);
	for (p = &proc[pidhash[PIDHASH(pid)]]; p != &proc[0]; p = &proc[p->p_idhash])
		if (p->p_pid == pid) {
			(void) p_lock(&p->p_state, SPLHI);
			v_lock(&proc_list, SPLHI);
			return (p);
		}
	v_lock(&proc_list, s_ipl);
	return((struct proc *)0);
}


/*
 * pgfind()
 *	Find process by process group
 *
 * If process found return with process state (p_state) locked.
 * Otherwise return 0.
 *
 * Assumes caller knows the appropriate ipl to v_lock with.
 */

proc_t *
pgfind(pgrp)
	int pgrp;
{
	register proc_t *p;
	spl_t 		s_ipl;

	/*
	 * Must lock proc_list to prevent process from leaving.
	 */
	s_ipl = p_lock(&proc_list, SPL_PROC_LIST);
	for (p = &proc[1]; p < v.ve_proc; p++) {
		if (p->p_stat == NULL )
			continue;
		if (p->p_pgrp == pgrp) {
			(void) p_lock(&p->p_state, SPLHI);
			/*
			 * recheck under lock 
			 */
			if (p->p_pgrp == pgrp) {
				v_lock(&proc_list, s_ipl);
				return (p);
			}
			v_lock(&p->p_state, s_ipl);
		}
	}
	v_lock(&proc_list, s_ipl);
	return((struct proc *)0);
}


/*
 * pgrp_find()
 *	Find process group within a session.
 *	Session must be locked by caller.
 *	Session linkage is dismantled on process termination.
 */
proc_t *
pgrp_find(pgrp)
	pid_t pgrp;
{
	register proc_t *p;

	for (p = u.u_procp->p_session->s_pgrp; p; p=p->p_sessf) {
		if (p->p_pgrp == pgrp) {
			return (p);
		}
	}
	return((proc_t *)0);
}


/*
 * pid_find()
 *	Find a process in the same session.
 *
 * No locks held
 * session must be locked by caller.
 */
proc_t *
pid_find(pid)
	pid_t pid;
{
	register proc_t *p;
	register proc_t *pg;

	for (p = u.u_procp->p_session->s_pgrp; p; p=p->p_sessf) {
		pg = p;
		do {
			if (pg->p_pid == pid) {
				return (pg);
			}
			pg = pg->p_pgrpf;
		} while (pg != p);
	}
	return((proc_t *)0);
}


/*
 * check to see if the process group "pgrp" has become orphaned
 * by us leaving the group.
 * Since we only call this if our parent was a job control cognizant
 * shell there are still some short cuts.
 * we have already removed ourselves from the process group list.
 * caller must lock the session.
 */

orphan(pgrp, p)
	proc_t	*pgrp;
	proc_t	*p;
{
	proc_t	*q;
	pid_t	sid;
	pid_t	pgid;

	sid = pgrp->p_sid;
	pgid = pgrp->p_pgrp;
#ifdef DEBUG
	if (jc_debug)
		printf("orphan(pgrp %d:0x%x,pid %d:0x%x)\n", pgrp->p_pid, 
				pgrp, p->p_pid, p);
#endif
	q = pgrp;
	do {
		/*
		 * check to see if the procces is a sibling
		 * if so then our leaving is not going to change anything.
		 */
#ifdef DEBUG
		if (jc_debug)
			printf("orphan relative %d \n", q->p_pid);
#endif
		if (q->p_pptr == p->p_pptr) {
#ifdef DEBUG
			if (jc_debug)
				printf("orphan sibling %d saves\n",q->p_pid);
#endif
			return;
		}
		if ((q->p_pptr->p_sid == sid) && (q->p_pptr->p_pgrp != pgid)) {
#ifdef DEBUG
			if (jc_debug)
				printf("orphan cousin %d parent %d (aunty) saves\n", q->p_pid, q->p_pptr->p_pid);
#endif
			return;
		}
		q = q->p_pgrpf;
	} while(q != pgrp);
	/*
	 * yup we're dead.
	 */
#ifdef DEBUG
	if (jc_debug)
		printf("orphan death for pgrp %d\n",pgrp->p_pgrp);
#endif
	set_orph(pgrp);
	return;
}


/*
 * check to see if the process group "pgrp" has become un orphaned
 * by us joining the list.
 * Since we may have been the job control cognizant shell for the group
 * before we joined, we would then be dooming the group unless another
 * process is looking after it. Beacause of this ther are no short cuts.
 * caller must lock the session.
 */
un_orphan(pgrp)
	proc_t	*pgrp;
{
	proc_t	*q;
	pid_t	sid;
	pid_t	pgid;

	q = pgrp;
	sid = pgrp->p_sid;
	pgid = pgrp->p_pgrp;
#ifdef DEBUG
	if (jc_debug)
		printf("un_orphan pgid%d in session %d\n",pgid,sid);
#endif
	do {
		if ((q->p_pptr->p_sid == sid) && (q->p_pptr->p_pgrp != pgid)) {
			/*
			 * group is saved we must set everyone in the group.
			 */
#ifdef DEBUG
			if (jc_debug)
				printf("un_orphan saved pgrp %d\n", pgrp->p_pgrp);
#endif
			save_orph(pgrp);
			return;
		}
		q = q->p_pgrpf;
	} while(q != pgrp);
	/*
	 * yup we have orphaned the group by joining!.
	 */
#ifdef DEBUG
	if (jc_debug)
		printf("un_orphan doomed pgrp %d\n", pgrp->p_pgrp);
#endif
	set_orph(pgrp);
	return;
}

/*
 * any_stopped()
 * POSIX 3.2.2.2
 *
 * If the implementation supports job control, and if
 * the exit of the process causes a process group to
 * become orphaned, and if any member of the 
 * newly-orphaned process group is stopped, then a 
 * SIGHUP signal followed by a SIGCONT signal shall be
 * sent to each process in the newly-orphaned process 
 * group.
 *
 *	caller must have session locked.
 *	pgrp->p_state must be locked.
 *	other members must be locked as thier state is looked at.
 */
any_stopped(pgrp)
	proc_t 	*pgrp;
{
	register proc_t *q;
	register spl_t	spl;
	int	 	stopped;

#ifdef DEBUG
	if (jc_debug)
		printf("any_stopped() looking at pgrp %d\n",pgrp->p_pgrp);
#endif
	stopped = 0;
	if ((pgrp->p_stat == SSTOP) && !(pgrp->p_flag&SMPSTOP)) {
#ifdef DEBUG
		if (jc_debug)
			printf("any_stopped() we are! pid %d\n", pgrp->p_pid);
#endif
		stopped = 1;
	} else {
		for (q = pgrp->p_pgrpf; q != pgrp; q = q->p_pgrpf) {
			spl = p_lock(&q->p_state, SPLHI);	
			if ((q->p_stat == SSTOP) && !(q->p_flag&SMPSTOP)) {
#ifdef DEBUG
				if (jc_debug)
					printf("any_stopped() found pid %d\n", 
								q->p_pid);
#endif
				v_lock(&q->p_state, spl);
				stopped = 1;
				break;
			}
			v_lock(&q->p_state, spl);
		}
	}
	if (!stopped ) {
#ifdef DEBUG
		if (jc_debug)
			printf("any_stopped() none-stopped\n");
#endif
		return;
	}
	/*
	 * send the signals to us.
	 */
	lpsignal(pgrp, SIGHUP);
	lpsignal(pgrp, SIGCONT);
	/*
	 * the the signals to all others in the same process group.
	 */
	for (q = pgrp->p_pgrpf; q != pgrp; q = q->p_pgrpf) {
		spl = p_lock(&q->p_state, SPLHI);
		lpsignal(q, SIGHUP);
		lpsignal(q, SIGCONT);
		v_lock(&q->p_state, spl);
	}
}


#ifdef DEBUG
/*
 * list_pgrp()
 *	List process group within a session.
 */
list_pgrp()
{
	register proc_t *p;
	register proc_t *q;

	if (!jc_debug)
		return;

	printf("list_pgrp() listing session %d\n", u.u_procp->p_sid);

	for (p = u.u_procp->p_session->s_pgrp; p; p=p->p_sessf) {
		printf(" found pgrp %d (0x%x) p_sessf:0x%x p_sessb:0x%x\n", 
				p->p_pgrp, p, p->p_sessf, p->p_sessb);
		q = p;
		do {
			printf("        pid %d (0x%x) p_pgrf:0x%x p_pgrb:0x%x\n",
				q->p_pid, q, q->p_pgrpf, q->p_pgrpb);
			q = q->p_pgrpf;
		} while (q != p);
		if (p == p->p_sessf) {
			printf("cycle in session groups?\n");
			return;
		}
	}
}
#endif
