/* $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_prot.c 1.42 90/01/17 $"

/*
 * kern_prot.c
 *	System calls related to processes and protection
 */

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

#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/signal.h"
#include "sys/systm.h"
#include "sys/mutex.h"
#include "sys/user.h"
#include "sys/errno.h"
#include "sys/timer.h"
#include "sys/session.h"
#include "sys/proc.h"
#include "sys/conf.h"
#include "sys/buf.h"
#include "sys/acct.h"

#include "sys/intctl.h"
#include "sys/gate.h"

void	crfree();

#ifdef DEBUG
int	jc_debug=0;
#endif


getpid()
{
	u.u_r.r_val1 = u.u_procp->p_pid;
	u.u_r.r_val2 = u.u_procp->p_ppid;
}

getuid()
{
	u.u_r.r_val1 = u.u_ruid;
	u.u_r.r_val2 = u.u_uid;
}

getgid()
{
	u.u_r.r_val1 = u.u_rgid;
	u.u_r.r_val2 = u.u_gid;
}

/*
 * getgroups()
 *	return supplementary group list
 *
 * As a special case, if the size argument is 0, return the number
 * of groups this process is a member of. Assumes there are no
 * "holes" in u.u_groups.
 */

getgroups()
{
	register struct	a {
		int	gidsetsize;
		gid_t	*grouplist;
	} *uap = (struct a *)u.u_ap;
	register gid_t *gp;
	register int gidsize;

	for (gp = u.u_groups; gp < &u.u_groups[NGROUPS]; gp++)
		if (*gp == INVAL_GID)
			break;

	gidsize = gp - u.u_groups;
	u.u_r.r_val1 = gidsize;
	if (uap->gidsetsize == 0)
		return;

	if (uap->gidsetsize < gidsize) {
		u.u_error = EINVAL;
		return;
	}
	u.u_error = copyout((caddr_t)u.u_groups, (caddr_t)uap->grouplist,
	    gidsize * sizeof (u.u_groups[0]));

	/* u.u_r.r_val1 is set above */
}

/*
 * setpgrp() and getpgrp() system calls
 *
 * DYNIX V setpgrp() can set only the current process's pgrp, and
 * only to that process's pid.  flag is set for setpgrp, clear
 * for getpgrp().
 * If this is used with POSIX job controll then we must create a new
 * session to get the intended semantics.
 */
setpgrp()
{
	register struct proc *p = u.u_procp;
	register struct a {
		int	flag;
	} *uap = (struct a *)u.u_ap;

	if ((uap->flag) && (p->p_pgrp != p->p_pid)) {
		/*
		 * Create a new session but ignore the errors!
		 */
		setsid();
		u.u_error = 0;
		/*
		 * There is one case where the setsid() call may not have
		 * discarded our controlling tty.  This case occurs only
		 * when:
		 *
		 *   POSIX IEEE std 1003.1-1988 4.3.2.
		 *   The process group ID of a process other than the calling
		 *   process matches the process ID of the calling process.
		 *
		 * To discard the controlling tty in this case would make
		 * the code in gentty() very tricky, as we would have to
		 * detect--upon reacquiring the controlling tty--that the
		 * controlling tty had been discarded, and a new one acquired--
		 * whilst we were stopped.
		 *
		 * Since this case can only occur under an unusual set of
		 * circumstances, and also requires the use of setpgid(),
		 * we don't worry about it.
		 *
		 * AT&T V.4 code doesn't worry about this case either....
		 */
	}
	u.u_r.r_val1 = p->p_pgrp;
}


/*
 * POSIX IEEE std 1003.1-1988 4.3.3.
 * Setpgid
 * The setpgid function is used to either join an existing process group
 * or create a new process group within the session of the calling process.
 * The process group ID of a session leader can not be changed. Upon 
 * successful completion, the process group ID of the process with a process
 * ID that matches pid shall be set to pgid. As a special case, if pid is
 * zero, the process ID of the calling process will be used. If pgid is
 * zero the process ID of the indicated process shall be used.
 */
setpgid()
{
	extern proc_t	*pgrp_find(); 
	extern proc_t 	*pid_find();
	extern proc_t 	*pgfind();
	register proc_t *p; /* our process */
	register struct a {
		int	pid;
		int	pgid;
	} *uap = (struct a *)u.u_ap;
	pid_t		pid;		/* the pid of the proc to change */
	pid_t		pgid;		/* the new pgrp */
	proc_t		*q;		/* the target proc to change 	*/
	proc_t		*newg;		/* a member of the new pgrp */
	int		jc_shell;	/* set if the target has a jc sh */
	struct	session *s;		/* pointer the processes session */
	proc_t		*zomb;		/* pointer to potential zombie */

	p = u.u_procp;
	s = p->p_session;
	pgid = uap->pgid;
	pid = uap->pid;

	(void) p_lock(&s->s_lock, SPLSESS);

	if ((pid == 0) || (pid == p->p_pid)) {
		/*
		 * POSIX IEEE std 1003.1-1988 4.3.3.4
		 * If pid is zero the process ID of the calling process
		 * is used.
		 */
		pid = p->p_pid;
		q = p;
#ifdef DEBUG
		if (jc_debug)
			printf("setpgid(%d,)  using my pid %d:0x%x\n",
						uap->pid, pid, q);
#endif
	} else {
		if ((q = pid_find(pid)) == NULL) {
			if (q = pfind(pid)) {
				v_lock(&q->p_state, SPLSESS);
			} else {
				v_lock(&s->s_lock, SPL0);
				u.u_error = ESRCH;
				return;
			}
		}
		if (q && (q->p_pptr != p)) {
			v_lock(&s->s_lock, SPL0);
			/*
			 * POSIX IEEE std 1003.1-1988 4.3.3.4
			 * The value of the pid argument does not match
			 * the process ID of the calling process or of a
			 * child process of the calling process.
			 */
			u.u_error = ESRCH;
			return;
		} else if ((q == NULL) || (q->p_sid != p->p_sid)) {
			v_lock(&s->s_lock, SPL0);
			/*
			 * POSIX IEEE std 1003.1-1988 4.3.3.4
			 * The value of pgid .. does not match .....
			 *  . in the same session as the calling process.
			 */
			u.u_error = EPERM;
			return;
		}
#ifdef DEBUG
		if (jc_debug)
			printf("setpgid(%d,)  using child pid %d:0x%x\n",
						uap->pid, pid, q);
#endif
		if (q->p_flag & SEXEC) {
			v_lock(&s->s_lock, SPL0);
			/*
			 * POSIX IEEE std 1003.1-1988 4.3.3.4
			 * The value of the pid argument matches the process ID
			 * of a child process of the calling process and the 
			 * child process has successfully executed one of
			 * exec functions.
			 */
#ifdef DEBUG
			if (jc_debug)
				printf("setpgid(%d,) child has execed\n"); 
#endif
			u.u_error = EACCES;
			return;
		}
	}

	if (pgid == 0) {
		/*
		 * POSIX IEEE std 1003.1-1988 4.3.3.4
		 * If pgid is zero the process ID of the process
		 * specified by pid is used.
		 */
		pgid = q->p_pid;
#ifdef DEBUG
		if (jc_debug)
			printf("setpgid(%d,0) pgid is %d:0x%x\n",
						pid, pgid, q);
#endif
	} else if ((pgid < 0)|| (pgid > MAXPID)) {
		v_lock(&s->s_lock, SPL0);
		/*
		 * POSIX IEEE std 1003.1-1988 4.3.3.4
		 * The value of the pgid argument is less than zero
		 * or is not a value supported by the implementation.
		 */
		u.u_error = EINVAL;
		return;
	}

	if (pid == q->p_sid) {
		v_lock(&s->s_lock, SPL0);
		/*
		 * POSIX IEEE std 1003.1-1988 4.3.3.4
		 * The process indicated by the pid argument 
		 * is a session leader.
		 */
#ifdef DEBUG
		if (jc_debug)
			printf("setpgid(%d,%d) is a session leader\n", pid, pgid);
#endif
		u.u_error = EPERM;
		return;
	}

	/*
	 * newg is set to a member of the new group if any.
	 */
	if (!(newg = pgrp_find(pgid))) {
		/*
		 * The process may be zombied. if so find it and allow
		 * the use of the process group as if it were a new group.
		 */
		if (zomb=pgfind(pgid)) {
			if (!(zomb->p_stat & SZOMB) || (zomb->p_sid != p->p_sid)) {
				v_lock(&zomb->p_state, SPLSESS);
				zomb = NULL;
			} else
				v_lock(&zomb->p_state, SPLSESS);
		}
	}
#ifdef DEBUG
	if (newg && jc_debug)
		printf("setpgid(%d,%d) - pgid already exits @0x%x\n",
							pid, pgid, newg);
#endif
	if ((pgid != q->p_pid) && !newg && !zomb) {
		/*
		 * POSIX IEEE std 1003.1-1988 4.3.3.4
		 * The value of the pgid argument does not match the process
		 * ID of the process indicated by the pid argument and there
		 * is no process with a process ID that matches the vaule of 
		 * pgid argument in the same session as the calling process.
		 */
		v_lock(&s->s_lock, SPL0);
#ifdef DEBUG
		if (jc_debug)
			printf("setpgid(%d,%d) pgid %d is not %d and is not a process group of this session\n",
				pid, pgid, pgid, q->p_pid);
#endif
		u.u_error = EPERM;
		return;
	}

	/*
	 * Error checking done. If the process was already in the group we
	 * are done else set the p_pgrp value.
	 */
	if (q->p_pgrp == pgid) {
#ifdef DEBUG
		if (jc_debug)
			printf("setpgid(%d,%d) - already in group\n", pid, pgid);
#endif
		v_lock(&s->s_lock, SPL0);
		return;
	}
	/*
	 * Set our orphaness accoring to our own linerage with
	 * respect to our new process group.
	 * This may change according to the relationship we have
	 * to the new group that we join.
	 */
	jc_shell = (q->p_pptr->p_sid == q->p_sid) && 
		       (q->p_pptr->p_pgrp != pgid);
	/*
	 * leave old process group and set our new p_pgrp.
	 * If old session is empty put ourselves back in as the only
	 * member rather than freeing the session.
	 */
#ifdef DEBUG
	if (jc_debug)
		printf("setpgid() proc_leave(%d)\n", q->p_pid);
#endif /* DEBUG */
	if (proc_leave(q))
		p->p_sessb = p;		/* put ourselves back */
	(void) p_lock(&q->p_state, SPLHI);
	q->p_pgrp = pgid;
	v_lock(&q->p_state, SPLSESS);

	if (newg) {		/* joining a group */
		PGRP_INS(q, newg);
		if (!jc_shell) {
			if (!(q->p_pgrpf->p_flag & SORPH)) {
				/*
				 * We are now orphaned but the group
				 * was not orphaned. We should therefore
				 * save ourselves except that if the 
				 * group was un-orphaned due to us we 
				 * have now doommed the group. so check 
				 * group if any are now real unorphans.
				 */
				un_orphan(q);
			}
			else {
				(void) p_lock(&q->p_state, SPLHI);
				q->p_flag |= SORPH;
				v_lock(&q->p_state, SPLSESS);
			}
		} else {
			/*
			 * the new group may now be un_orphaned.
			 */
			if (q->p_pgrpf->p_flag & SORPH)
				save_orph(q);
			else {
				(void) p_lock(&q->p_state, SPLHI);
				q->p_flag &= ~SORPH;
				v_lock(&q->p_state, SPLSESS);
			}
		}
	} else {
		/* 
		 * This is a new group. We will end up as the only member.
		 */
#ifdef DEBUG
		if (jc_debug) {
			printf("setpgid(%d,%d) - brand new group\n", pid, pgid);
			list_pgrp();
		}
#endif
		SESS_INS(q, s);
		(void) p_lock(&q->p_state, SPLHI);
		if (jc_shell)
			q->p_flag &= ~SORPH;
		else
			q->p_flag |= SORPH;
		v_lock(&q->p_state, SPLSESS);
	}
#ifdef DEBUG
	if (jc_debug) {
		printf("setpgid(%d,%d) (done by pid %d) is complete\n",
				pid, pgid, p->p_pid);
		list_pgrp();
	}
#endif
	v_lock(&s->s_lock, SPL0);
}


/*
 * unlink a process from his session linkage.
 * If the session is empty return true.
 * The session must be locked.
 */

int
proc_leave(p)
	proc_t	*p;
{
	proc_t		*group;
	struct	session	*s;

	s = p->p_session;
#ifdef DEBUG
	if (jc_debug) {
		printf("proc_leave(%d) from group %d\n", p->p_pid, p->p_pgrp);
		list_pgrp();
	}
#endif
	/*
	 * If there are other members in the same process group then
	 * delete our selves from the list. If we are at the top of the
	 * list then give the list to another process.
	 * p_pgrb is a circular list.
	 */
	if ((group = p->p_pgrpb) != p) {	/* other group members ? */
#ifdef DEBUG
		if (jc_debug) {
			printf("proc_leave(%d) - old group had more than just us in it\n", p->p_pid);
		}
#endif
		PGRP_DEL(p);	
		if (p->p_sessb) {	/* top of process list ? */
			/*
			 * make the next process in our chain
			 * the holder of our session links 
			 */
			ASSERT_DEBUG((group->p_sessb == NULL), "leave_proc() non-null top 1");
			ASSERT_DEBUG((group->p_sessf == NULL), "leave_proc() non-null top 2");
			group->p_sessf = p->p_sessf;
			if (p->p_sessb == p)
				group->p_sessb = group;
			else
				group->p_sessb = p->p_sessb;
			/*
			 * if we were at the start of the session list
			 * change the sessions pointer as well.
			 */
			if (s->s_pgrp == p) {
				s->s_pgrp = group;
#ifdef DEBUG
				if (jc_debug)
					printf("proc_leave(%d) - promoting pid %d to be at top and at the start\n", p->p_pid, group->p_pid);
#endif
			} else {
				p->p_sessb->p_sessf = group;

#ifdef DEBUG
				if (jc_debug)
				printf("proc_leave(%d) - promoting pid %d to be at top\n", p->p_pid, group->p_pid);
#endif
			}
			/*
			 * fix up the predcessor to point to the group.
			 */
			if (p->p_sessf)
				p->p_sessf->p_sessb = group;
			else
				s->s_pgrp->p_sessb = group;
			p->p_sessf = NULL;
			p->p_sessb = NULL;
		} 
#ifdef DEBUG
		if (jc_debug) {
			printf("proc_leave(%d)- %d is in old pgrp\n",
					p->p_pid, group->p_pid);
		}
#endif
		/*
		 * if our parent is in the same session as us but in a different
		 * procces group then our leaving may cause this process
		 * group to become orphaned.
		 */
		if (!(group->p_flag & SORPH) &&
				(p->p_pptr->p_sid == p->p_sid) &&
				(p->p_pptr->p_pgrp != p->p_pgrp) ) {
#ifdef DEBUG
			if (jc_debug)
				printf("proc_leave() checking for orphans\n");
#endif
			orphan(group, p);
		}
		return (0);
	}
	/*
	 * Since the process group is now dead check the foreground process
	 * group. If it was us null it out so that no signals can be sent
	 * if the pgrp is reallocated.
	 * (c_pgrp can only be changed by us).
	 */
	if (s->s_ctty && ((s->s_ctty->c_pgrp == p->p_pgrp) && (s->s_ctty->c_sid == p->p_sid))) {
#ifdef DEBUG
		if (jc_debug) 
			printf("proc_leave(%d) - foreground group %d dead\n",
					p->p_pgrp);
#endif
		s->s_ctty->c_pgrp = 0;
	}
	/*
	 * We are at the top of the process list so we were the
	 * only memeber of the process group in which case
	 * remove this process group from the session list.
	 */
	ASSERT_DEBUG(p->p_sessb, "proc_leave empty group not in session");
	if (p->p_sessb->p_sessf)
		p->p_sessb->p_sessf = p->p_sessf;
	/* 
	 * and fix up the next guys back pointer, or if we are at the end
	 * fix  up the start of session list back pointer to us.
	 */
	if (p->p_sessf)
		p->p_sessf->p_sessb = p->p_sessb;
	else
		s->s_pgrp->p_sessb = p->p_sessb;
	p->p_sessb = NULL;
	/*
	 * now check if we were at the start of the session list.
	 * if so then link the session to the next group.
	 * if there is no next group the the session is empty so delete it.
	 */
	if (s->s_pgrp == p){
#ifdef DEBUG
		if (jc_debug) 
			printf("proc_leave(%d) - start of session list\n");
#endif
		if (p->p_sessf) {  	/* more groups ? */
			s->s_pgrp = p->p_sessf;
			p->p_sessf = NULL;
		} else
			return (1);
	} else
		p->p_sessf = NULL;
	return (0);
}


/*
 * POSIX IEEE std 1003.1-1988 4.3.2.
 * setsid()
 * If the calling process is not a process group leader, the setsid() function
 * will creat a new session. The calling process will be the session leader of
 * of this new session, and will be the process group leader of the new
 * process group, and will have no controlling terminal. The process group ID
 * of the calling process will be set equal to the process ID of the calling
 * process. The calling process will be the only process in the new process
 * group and the only process in the new session.
 */
setsid()
{
	extern		sessalloc();
	register	proc_t	*p;
	struct		session	*s;

	p = u.u_procp;
#ifdef DEBUG
	if (jc_debug)
		printf("setsid() pid %d pgrp %d sid %d\n", p->p_pid,
				p->p_pgrp, p->p_sid);
#endif
	if (p->p_pgrp == p->p_pid) {
		/*
		 * POSIX IEEE std 1003.1-1988 4.3.2.
		 * The calling process is already a process group leader.
		 */
		u.u_error = EPERM;
		return;
	}
	/*
	 * pgrp_finds() finds if a process group is already part of the
	 * session.
	 */
	s = p->p_session;
	(void)p_lock(&s->s_lock, SPLSESS);
	if (pgrp_find(p->p_pid)) {
		/*
		 * POSIX IEEE std 1003.1-1988 4.3.2.
		 * The process group ID of a process other than the calling
		 * process matches the process ID of the calling process.
		 */
		v_lock(&s->s_lock, SPL0);
		u.u_error = EPERM;
		return;
	}

	/*
	 * Remove our pgrp from the previous session
	 * then take this process out of the the process group
	 */
#ifdef DEBUG
	if (jc_debug)
		printf("setsid() proc_leave(%d)\n", p->p_pid);
#endif /* DEBUG */
	if (proc_leave(p)) {
		/*
		 * reuse the old session structure.
		 */
		p->p_sessb = p;
		s->s_sid = p->p_pid;
		s->s_vp = NULL;
		s->s_ctty = NULL;
	} else {
		sessalloc(p);
	}
	/*
	 * Set IDs
	 */
	(void) p_lock(&p->p_state, SPLHI);
	p->p_sid = p->p_pid;
	p->p_pgrp = p->p_pid;
	p->p_flag |= SORPH;	/* new group is always orphaned */
	v_lock(&p->p_state, SPLSESS);
#ifdef DEBUG
	if (jc_debug)
		printf("setsid(): done pid %d pgrp %d sid %d\n", p->p_pid, 
						p->p_pgrp, p->p_sid);
#endif /* DEBUG */
	v_lock(&s->s_lock, SPL0);
	/*
	 * Return the process group.
	 */
	u.u_r.r_val1 = p->p_pgrp;
}


setuid()
{
	register struct a {
		int	uid;
	} *uap = (struct a *)u.u_ap;
	register uid_t uid;
	register struct proc *p = u.u_procp;

	uid = uap->uid;
	if (uid >= MAXUID) {
		u.u_error = EINVAL;
		return;
	}
	if (u.u_uid && (uid == u.u_ruid || uid == p->p_suid)) {
		u.u_cred = crcopy(u.u_cred);
		u.u_uid = uid;
	} else if (suser()) {
		u.u_cred = crcopy(u.u_cred);
		u.u_uid = uid;
		p->p_uid = uid;
		p->p_suid = uid;
		u.u_ruid = uid;
	}
}

setgid()
{
	register struct a {
		int	gid;
	} *uap = (struct a *)u.u_ap;
	register gid_t gid;
	register struct proc *p = u.u_procp;

	gid = uap->gid;
	if (gid >= MAXUID) {
		u.u_error = EINVAL;
		return;
	}
	if (u.u_uid && (gid == u.u_rgid || gid == p->p_sgid)) {
		u.u_cred = crcopy(u.u_cred);
		u.u_gid = gid;
	} else if (suser()) {
		u.u_cred = crcopy(u.u_cred);
		u.u_gid = gid;
		p->p_sgid = gid;
		u.u_rgid = gid;
	}
}

/*
 * setgroups()
 *	Initialize supplementary group list
 *
 * Getgroups() and groupmember() assume that the group list is
 * terminated by an entry containing INVALID_GID (or if array
 * size is exhausted), so caller must be careful to ensure no
 * entry in the list holds the value INVALID_GID.
 */

setgroups()
{
	register struct	a {
		uint	gidsetsize;
		gid_t	*grouplist;
	} *uap = (struct a *)u.u_ap;
	register gid_t *gp;
	struct ucred *newcr, *tmpcr;

	if (!suser())
		return;
	if (uap->gidsetsize > sizeof (u.u_groups) / sizeof (u.u_groups[0])) {
		u.u_error = EINVAL;
		return;
	}
	newcr = crdup(u.u_cred);
	u.u_error = copyin((caddr_t)uap->grouplist, (caddr_t)newcr->cr_groups,
	    uap->gidsetsize * sizeof (newcr->cr_groups[0]));
	if (u.u_error) {
		crfree(newcr);
		return;
	}
	tmpcr = u.u_cred;
	u.u_cred = newcr;
	crfree(tmpcr);
	for (gp = &u.u_groups[uap->gidsetsize]; gp < &u.u_groups[NGROUPS]; gp++)
		*gp = INVAL_GID;
}

/*
 * Check if gid is a member of the group set.
 */
groupmember(gid)
	gid_t gid;
{
	register gid_t *gp;

	if (u.u_gid == gid)
		return (1);
	for (gp = u.u_groups; gp < &u.u_groups[NGROUPS] && *gp != INVAL_GID; gp++)
		if (*gp == gid)
			return (1);
	return (0);
}

/*
 * Routines to allocate and free credentials structures
 * These could all be macros.
 *
 * Free-list is kept thru cr_groups[0].  This is kindof a kludge, but
 * we avoid the "union" since the free-list is kept local to this module.
 */

/*
 * ucred_init()
 *	Initialize credentials list, locks, and semaphores.
 *
 * Assumes boot-time code allocated cred data-structures.
 * Assumes caller of crget() init's cr_lock.
 */

int		nucred;				/* # cred's allocated */
struct	ucred	*ucred_free;			/* head of free-list */

lock_t		ucred_mutex;			/* for cred-list manipulation */
sema_t		ucred_wait;			/* wait here for free cred */

#define		CR_NEXT(cr)	(*(struct ucred **)&((cr)->cr_groups[0]))

ucred_init()
{
	register struct	ucred	*cr;
	register int i;

	init_lock(&ucred_mutex);
	init_sema(&ucred_wait, 0);

	for (cr = ucred_free, i = 0; i < nucred; i++, cr++)
		CR_NEXT(cr) = (cr+1);
	CR_NEXT(cr-1) = NULL;
}

/*
 * crhold()
 *	Hold a ucred structure (add a reference).
 */

void
crhold(cr)
	struct ucred *cr;
{
	spl_t	s;

	s = p_lock(&cr->cr_lock, SPLHI);
	cr->cr_ref++;
	v_lock(&cr->cr_lock, s);
}

/*
 * crget()
 *	Allocate a ucred structure.
 *
 * The structure is not filled out or zeroed; caller must do this.
 *
 * The Caller must init the lock and ref count,
 * because these may be incorrect (due to concurrent activity)
 * if the caller copies another ucred struct into the new struct.
 */

struct ucred *
crget()
{
	struct ucred *cr;
	spl_t	s;

	s = p_lock(&ucred_mutex, SPLIMP);

	while (ucred_free == NULL) {
		(void)p_sema_v_lock(&ucred_wait, PRSWAIT, &ucred_mutex);
		s = p_lock(&ucred_mutex, SPLIMP);
	}

	cr = ucred_free;
	ucred_free = CR_NEXT(cr);

	v_lock(&ucred_mutex, s);

	return(cr);
}

/*
 * crfree()
 *	Free a ucred structure.
 *
 * Releases ucred structure to free-list when ref count gets to 0.
 */

void
crfree(cr)
	register struct ucred *cr;
{
	spl_t	s;

	s = p_lock(&cr->cr_lock, SPLHI);
	if (--cr->cr_ref)
		v_lock(&cr->cr_lock, s);
	else {
		/*
		 * Ref-cnt == 0 so we are last holder of ucred and can free it.
		 */
		(void) p_lock(&ucred_mutex, SPLHI);
		CR_NEXT(cr) = ucred_free;
		ucred_free = cr;
		if (blocked_sema(&ucred_wait))
			v_sema(&ucred_wait);
		v_lock(&ucred_mutex, s);
	}
}

/*
 * crcopy()
 *	Copy ucred structure to a new one and free the old one.
 */

struct ucred *
crcopy(cr)
	struct ucred *cr;
{
	struct ucred *newcr;

	/*
	 * Mutex: cr data is safe since caller has crhold() on it.
	 * Lock and count may be changing, and must be re-initialized.
	 */

	newcr = crget();
	*newcr = *cr;					/* Struct copy */
	newcr->cr_ref = 1;
	init_lock(&newcr->cr_lock);
	crfree(cr);

	return(newcr);
}

/*
 * crdup()
 *	Dup ucred struct to a new held one.
 */

struct ucred *
crdup(cr)
	struct ucred *cr;
{
	struct ucred *newcr;

	/*
	 * Mutex: cr data is safe since caller has crhold() on it.
	 * Lock and count may be changing, and must be re-initialized.
	 */

	newcr = crget();
	*newcr = *cr;					/* Struct Copy */
	newcr->cr_ref = 1;
	init_lock(&newcr->cr_lock);

	return(newcr);
}

/*
 * suser()
 *	Test if the current user is the super user.
 */

suser()
{
	if (u.u_uid == 0) {
		u.u_acflag |= ASU;
		return (1);
	}
	u.u_error = EPERM;
	return (0);
}


/*
 * Save a group from becoming orphaned by clearing SORPH in thier p_flags.
 * The p_pgrpf p_pgrpb list is circular.
 * Caller must have session locked.
 */
save_orph(pgrp)
	proc_t	*pgrp;
{
	proc_t	*p;
	spl_t	spl;

	p = pgrp; 
	do {
		if (p->p_flag & SORPH){
			spl = p_lock(&p->p_state, SPLHI);
			p->p_flag &= ~SORPH;
			v_lock(&p->p_state, spl);
		}
		p = p->p_pgrpf;
	} while (p != pgrp);
}


/*
 * Doom a group to becoming orphaned by setting SORPH in thier p_flags.
 * The p_pgrpf list is circular.
 * Caller must have session locked.
 */
set_orph(pgrp)
	proc_t	*pgrp;
{
	proc_t	*p;
	spl_t	spl;

	p = pgrp; 
	do {
		if (!(p->p_flag & SORPH)){
			spl = p_lock(&p->p_state, SPLHI);
			p->p_flag |= SORPH;
			v_lock(&p->p_state, spl);
		}
		p = p->p_pgrpf;
	} while (p != pgrp);
}
