xref: /onnv-gate/usr/src/uts/common/os/session.c (revision 5331:3047ad28a67b)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52712Snn35248  * Common Development and Distribution License (the "License").
62712Snn35248  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*5331Samw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved  	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/sysmacros.h>
340Sstevel@tonic-gate #include <sys/param.h>
350Sstevel@tonic-gate #include <sys/systm.h>
360Sstevel@tonic-gate #include <sys/file.h>
370Sstevel@tonic-gate #include <sys/vnode.h>
380Sstevel@tonic-gate #include <sys/errno.h>
390Sstevel@tonic-gate #include <sys/signal.h>
400Sstevel@tonic-gate #include <sys/cred.h>
410Sstevel@tonic-gate #include <sys/policy.h>
420Sstevel@tonic-gate #include <sys/conf.h>
430Sstevel@tonic-gate #include <sys/debug.h>
440Sstevel@tonic-gate #include <sys/proc.h>
450Sstevel@tonic-gate #include <sys/session.h>
460Sstevel@tonic-gate #include <sys/kmem.h>
470Sstevel@tonic-gate #include <sys/cmn_err.h>
480Sstevel@tonic-gate #include <sys/strsubr.h>
492712Snn35248 #include <sys/fs/snode.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate sess_t session0 = {
522712Snn35248 	&pid0,		/* s_sidp */
532712Snn35248 	{0},		/* s_lock */
542712Snn35248 	1,		/* s_ref */
552712Snn35248 	B_FALSE,	/* s_sighuped */
562712Snn35248 	B_FALSE,	/* s_exit */
572712Snn35248 	0,		/* s_exit_cv */
582712Snn35248 	0,		/* s_cnt */
592712Snn35248 	0,		/* s_cnt_cv */
602712Snn35248 	NODEV,		/* s_dev */
612712Snn35248 	NULL,		/* s_vp */
622712Snn35248 	NULL		/* s_cred */
630Sstevel@tonic-gate };
640Sstevel@tonic-gate 
650Sstevel@tonic-gate void
sess_hold(proc_t * p)662712Snn35248 sess_hold(proc_t *p)
670Sstevel@tonic-gate {
682712Snn35248 	ASSERT(MUTEX_HELD(&pidlock) || MUTEX_HELD(&p->p_splock));
692712Snn35248 	mutex_enter(&p->p_sessp->s_lock);
702712Snn35248 	p->p_sessp->s_ref++;
712712Snn35248 	mutex_exit(&p->p_sessp->s_lock);
722712Snn35248 }
732712Snn35248 
742712Snn35248 void
sess_rele(sess_t * sp,boolean_t pidlock_held)752712Snn35248 sess_rele(sess_t *sp, boolean_t pidlock_held)
762712Snn35248 {
772712Snn35248 	ASSERT(MUTEX_HELD(&pidlock) || !pidlock_held);
782712Snn35248 
792712Snn35248 	mutex_enter(&sp->s_lock);
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	ASSERT(sp->s_ref != 0);
822712Snn35248 	if (--sp->s_ref > 0) {
832712Snn35248 		mutex_exit(&sp->s_lock);
842712Snn35248 		return;
850Sstevel@tonic-gate 	}
862712Snn35248 	ASSERT(sp->s_ref == 0);
872712Snn35248 
882712Snn35248 	/*
892712Snn35248 	 * It's ok to free this session structure now because we know
902712Snn35248 	 * that no one else can have a pointer to it.  We know this
912712Snn35248 	 * to be true because the only time that s_ref can possibly
922712Snn35248 	 * be incremented is when pidlock or p_splock is held AND there
932712Snn35248 	 * is a proc_t that points to that session structure.  In that
942712Snn35248 	 * case we are guaranteed that the s_ref is at least 1 since there
952712Snn35248 	 * is a proc_t that points to it.  So when s_ref finally drops to
962712Snn35248 	 * zero then no one else has a reference (and hence pointer) to
972712Snn35248 	 * this session structure and there is no valid proc_t pointing
982712Snn35248 	 * to this session structure anymore so, no one can acquire a
992712Snn35248 	 * reference (and pointer) to this session structure so it's
1002712Snn35248 	 * ok to free it here.
1012712Snn35248 	 */
1022712Snn35248 
1032712Snn35248 	if (sp == &session0)
1042712Snn35248 		panic("sp == &session0");
1052712Snn35248 
1062712Snn35248 	/* make sure there are no outstanding holds */
1072712Snn35248 	ASSERT(sp->s_cnt == 0);
1082712Snn35248 
1092712Snn35248 	/* make sure there is no exit in progress */
1102712Snn35248 	ASSERT(!sp->s_exit);
1112712Snn35248 
1122712Snn35248 	/* make sure someone already freed any ctty */
1132712Snn35248 	ASSERT(sp->s_vp == NULL);
1142712Snn35248 	ASSERT(sp->s_dev == NODEV);
1152712Snn35248 
1162712Snn35248 	if (!pidlock_held)
1172712Snn35248 		mutex_enter(&pidlock);
1182712Snn35248 	PID_RELE(sp->s_sidp);
1192712Snn35248 	if (!pidlock_held)
1202712Snn35248 		mutex_exit(&pidlock);
1212712Snn35248 
1222712Snn35248 	mutex_destroy(&sp->s_lock);
1232712Snn35248 	cv_destroy(&sp->s_cnt_cv);
1242712Snn35248 	kmem_free(sp, sizeof (sess_t));
1252712Snn35248 }
1262712Snn35248 
1272712Snn35248 sess_t *
tty_hold(void)1282712Snn35248 tty_hold(void)
1292712Snn35248 {
1302712Snn35248 	proc_t		*p = curproc;
1312712Snn35248 	sess_t		*sp;
1322712Snn35248 	boolean_t	got_sig = B_FALSE;
1332712Snn35248 
1342712Snn35248 	/* make sure the caller isn't holding locks they shouldn't */
1352712Snn35248 	ASSERT(MUTEX_NOT_HELD(&pidlock));
1362712Snn35248 
1372712Snn35248 	for (;;) {
1382712Snn35248 		mutex_enter(&p->p_splock);	/* protect p->p_sessp */
1392712Snn35248 		sp = p->p_sessp;
1402712Snn35248 		mutex_enter(&sp->s_lock);	/* protect sp->* */
1412712Snn35248 
1422712Snn35248 		/* make sure the caller isn't holding locks they shouldn't */
1432712Snn35248 		ASSERT((sp->s_vp == NULL) ||
1442712Snn35248 		    MUTEX_NOT_HELD(&sp->s_vp->v_stream->sd_lock));
1452712Snn35248 
1462712Snn35248 		/*
1472712Snn35248 		 * If the session leader process is not exiting (and hence
1482712Snn35248 		 * not trying to release the session's ctty) then we can
1492712Snn35248 		 * safely grab a hold on the current session structure
1502712Snn35248 		 * and return it.  If on the other hand the session leader
1512712Snn35248 		 * process is exiting and clearing the ctty then we'll
1522712Snn35248 		 * wait till it's done before we loop around and grab a
1532712Snn35248 		 * hold on the session structure.
1542712Snn35248 		 */
1552712Snn35248 		if (!sp->s_exit)
1562712Snn35248 			break;
1572712Snn35248 
1582712Snn35248 		/* need to hold the session so it can't be freed */
1592712Snn35248 		sp->s_ref++;
1602712Snn35248 		mutex_exit(&p->p_splock);
1612712Snn35248 
1622712Snn35248 		/* Wait till the session leader is done */
1632712Snn35248 		if (!cv_wait_sig(&sp->s_exit_cv, &sp->s_lock))
1642712Snn35248 			got_sig = B_TRUE;
1652712Snn35248 
1662712Snn35248 		/*
1672712Snn35248 		 * Now we need to drop our hold on the session structure,
1682712Snn35248 		 * but we can't hold any locks when we do this because
169*5331Samw 		 * sess_rele() may need to acquire pidlock.
1702712Snn35248 		 */
1712712Snn35248 		mutex_exit(&sp->s_lock);
1722712Snn35248 		sess_rele(sp, B_FALSE);
1732712Snn35248 
1742712Snn35248 		if (got_sig)
1752712Snn35248 			return (NULL);
1762712Snn35248 	}
1772712Snn35248 
1782712Snn35248 	/* whew, we finally got a hold */
1792712Snn35248 	sp->s_cnt++;
1802712Snn35248 	sp->s_ref++;
1812712Snn35248 	mutex_exit(&sp->s_lock);
1822712Snn35248 	mutex_exit(&p->p_splock);
1832712Snn35248 	return (sp);
1842712Snn35248 }
1852712Snn35248 
1862712Snn35248 void
tty_rele(sess_t * sp)1872712Snn35248 tty_rele(sess_t *sp)
1882712Snn35248 {
1892712Snn35248 	/* make sure the caller isn't holding locks they shouldn't */
1902712Snn35248 	ASSERT(MUTEX_NOT_HELD(&pidlock));
1912712Snn35248 
1922712Snn35248 	mutex_enter(&sp->s_lock);
1932712Snn35248 	if ((--sp->s_cnt) == 0)
1942712Snn35248 		cv_broadcast(&sp->s_cnt_cv);
1952712Snn35248 	mutex_exit(&sp->s_lock);
1962712Snn35248 
1972712Snn35248 	sess_rele(sp, B_FALSE);
1980Sstevel@tonic-gate }
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate void
sess_create(void)2010Sstevel@tonic-gate sess_create(void)
2020Sstevel@tonic-gate {
2032712Snn35248 	proc_t *p = curproc;
2042712Snn35248 	sess_t *sp, *old_sp;
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	sp = kmem_zalloc(sizeof (sess_t), KM_SLEEP);
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	mutex_init(&sp->s_lock, NULL, MUTEX_DEFAULT, NULL);
2092712Snn35248 	cv_init(&sp->s_cnt_cv, NULL, CV_DEFAULT, NULL);
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	/*
2122712Snn35248 	 * we need to grap p_lock to protect p_pgidp because
2132712Snn35248 	 * /proc looks at p_pgidp while holding only p_lock.
2142712Snn35248 	 *
2152712Snn35248 	 * we don't need to hold p->p_sessp->s_lock or get a hold on the
2162712Snn35248 	 * session structure since we're not actually updating any of
2172712Snn35248 	 * the contents of the old session structure.
2180Sstevel@tonic-gate 	 */
2192712Snn35248 	mutex_enter(&pidlock);
2202712Snn35248 	mutex_enter(&p->p_lock);
2212712Snn35248 	mutex_enter(&p->p_splock);
2220Sstevel@tonic-gate 
2232712Snn35248 	pgexit(p);
2242712Snn35248 
2252712Snn35248 	sp->s_sidp = p->p_pidp;
2260Sstevel@tonic-gate 	sp->s_ref = 1;
2270Sstevel@tonic-gate 	sp->s_dev = NODEV;
2280Sstevel@tonic-gate 
2292712Snn35248 	old_sp = p->p_sessp;
2302712Snn35248 	p->p_sessp = sp;
2312712Snn35248 
2322712Snn35248 	pgjoin(p, p->p_pidp);
2332712Snn35248 	PID_HOLD(p->p_pidp);
2340Sstevel@tonic-gate 
2352712Snn35248 	mutex_exit(&p->p_splock);
2362712Snn35248 	mutex_exit(&p->p_lock);
2372712Snn35248 	mutex_exit(&pidlock);
2380Sstevel@tonic-gate 
2392712Snn35248 	sess_rele(old_sp, B_FALSE);
2400Sstevel@tonic-gate }
2410Sstevel@tonic-gate 
2422712Snn35248 /*
2432712Snn35248  * Note that sess_ctty_clear() resets all the fields in the session
2442712Snn35248  * structure but doesn't release any holds or free any objects
2452712Snn35248  * that the session structure might currently point to.  it is the
2462712Snn35248  * callers responsibility to do this.
2472712Snn35248  */
2482712Snn35248 static void
sess_ctty_clear(sess_t * sp,stdata_t * stp)2492712Snn35248 sess_ctty_clear(sess_t *sp, stdata_t *stp)
2500Sstevel@tonic-gate {
2512712Snn35248 	/*
2522712Snn35248 	 * Assert that we hold all the necessary locks.  We also need
2532712Snn35248 	 * to be holding proc_t->p_splock for the process associated
2542712Snn35248 	 * with this session, but since we don't have a proc pointer
2552712Snn35248 	 * passed in we can't assert this here.
2562712Snn35248 	 */
2572712Snn35248 	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
2582712Snn35248 	    MUTEX_HELD(&sp->s_lock));
2590Sstevel@tonic-gate 
2602712Snn35248 	/* reset the session structure members to defaults */
2612712Snn35248 	sp->s_sighuped = B_FALSE;
2622712Snn35248 	sp->s_dev = NODEV;
2630Sstevel@tonic-gate 	sp->s_vp = NULL;
264560Smeem 	sp->s_cred = NULL;
2650Sstevel@tonic-gate 
2662712Snn35248 	/* reset the stream session and group pointers */
2672712Snn35248 	stp->sd_pgidp = NULL;
2682712Snn35248 	stp->sd_sidp = NULL;
2692712Snn35248 }
2702712Snn35248 
2712712Snn35248 static void
sess_ctty_set(proc_t * p,sess_t * sp,stdata_t * stp)2722712Snn35248 sess_ctty_set(proc_t *p, sess_t *sp, stdata_t *stp)
2732712Snn35248 {
2742712Snn35248 	cred_t	*crp;
2752712Snn35248 
2762712Snn35248 	/* Assert that we hold all the necessary locks. */
2772712Snn35248 	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
2782712Snn35248 	    MUTEX_HELD(&p->p_splock) && MUTEX_HELD(&sp->s_lock));
2792712Snn35248 
2802712Snn35248 	/* get holds on structures */
2812712Snn35248 	mutex_enter(&p->p_crlock);
2822712Snn35248 	crhold(crp = p->p_cred);
2832712Snn35248 	mutex_exit(&p->p_crlock);
2842712Snn35248 	PID_HOLD(sp->s_sidp);	/* requires pidlock */
2852712Snn35248 	PID_HOLD(sp->s_sidp);	/* requires pidlock */
2862712Snn35248 
2872712Snn35248 	/* update the session structure members */
2882712Snn35248 	sp->s_vp = makectty(stp->sd_vnode);
2892712Snn35248 	sp->s_dev = sp->s_vp->v_rdev;
2902712Snn35248 	sp->s_cred = crp;
2912712Snn35248 
2922712Snn35248 	/* update the stream emebers */
2932712Snn35248 	stp->sd_flag |= STRISTTY;	/* just to be sure */
2942712Snn35248 	stp->sd_sidp = sp->s_sidp;
2952712Snn35248 	stp->sd_pgidp = sp->s_sidp;
2962712Snn35248 }
2972712Snn35248 
2982712Snn35248 int
strctty(stdata_t * stp)2992712Snn35248 strctty(stdata_t *stp)
3002712Snn35248 {
3012712Snn35248 	sess_t		*sp;
3022712Snn35248 	proc_t		*p = curproc;
3032712Snn35248 	boolean_t	got_sig = B_FALSE;
3042712Snn35248 
3050Sstevel@tonic-gate 	/*
3062712Snn35248 	 * We are going to try to make stp the default ctty for the session
3072712Snn35248 	 * associated with curproc.  Not only does this require holding a
3082712Snn35248 	 * bunch of locks but it also requires waiting for any outstanding
309*5331Samw 	 * holds on the session structure (acquired via tty_hold()) to be
3102712Snn35248 	 * released.  Hence, we have the following for(;;) loop that will
311*5331Samw 	 * acquire our locks, do some sanity checks, and wait for the hold
3122712Snn35248 	 * count on the session structure to hit zero.  If we get a signal
3132712Snn35248 	 * while waiting for outstanding holds to be released then we abort
3142712Snn35248 	 * the operation and return.
3150Sstevel@tonic-gate 	 */
3162712Snn35248 	for (;;) {
3172712Snn35248 		mutex_enter(&stp->sd_lock);	/* protects sd_pgidp/sd_sidp */
3182712Snn35248 		mutex_enter(&pidlock);		/* protects p_pidp */
3192712Snn35248 		mutex_enter(&p->p_splock);	/* protects p_sessp */
3202712Snn35248 		sp = p->p_sessp;
3212712Snn35248 		mutex_enter(&sp->s_lock);	/* protects sp->* */
3220Sstevel@tonic-gate 
3232712Snn35248 		if (((stp->sd_flag & (STRHUP|STRDERR|STWRERR|STPLEX)) != 0) ||
3242712Snn35248 		    (stp->sd_sidp != NULL) ||		/* stp already ctty? */
3252712Snn35248 		    (p->p_pidp != sp->s_sidp) ||	/* we're not leader? */
3262712Snn35248 		    (sp->s_vp != NULL)) {		/* session has ctty? */
3272712Snn35248 			mutex_exit(&sp->s_lock);
3282712Snn35248 			mutex_exit(&p->p_splock);
3292712Snn35248 			mutex_exit(&pidlock);
3302712Snn35248 			mutex_exit(&stp->sd_lock);
3312712Snn35248 			return (ENOTTY);
3322712Snn35248 		}
3332712Snn35248 
3342712Snn35248 		/* sanity check.  we can't be exiting right now */
3352712Snn35248 		ASSERT(!sp->s_exit);
3362712Snn35248 
3372712Snn35248 		/*
3382712Snn35248 		 * If no one else has a hold on this session structure
3392712Snn35248 		 * then we now have exclusive access to it, so break out
3402712Snn35248 		 * of this loop and update the session structure.
3412712Snn35248 		 */
3422712Snn35248 		if (sp->s_cnt == 0)
3432712Snn35248 			break;
3442712Snn35248 
3452712Snn35248 		/* need to hold the session so it can't be freed */
3462712Snn35248 		sp->s_ref++;
3472712Snn35248 
3482712Snn35248 		/* ain't locking order fun? */
3492712Snn35248 		mutex_exit(&p->p_splock);
3502712Snn35248 		mutex_exit(&pidlock);
3512712Snn35248 		mutex_exit(&stp->sd_lock);
3522712Snn35248 
3532712Snn35248 		if (!cv_wait_sig(&sp->s_cnt_cv, &sp->s_lock))
3542712Snn35248 			got_sig = B_TRUE;
3552712Snn35248 		mutex_exit(&sp->s_lock);
3562712Snn35248 		sess_rele(sp, B_FALSE);
3572712Snn35248 
3582712Snn35248 		if (got_sig)
3592712Snn35248 			return (EINTR);
3602712Snn35248 	}
3612712Snn35248 
3622712Snn35248 	/* set the session ctty bindings */
3632712Snn35248 	sess_ctty_set(p, sp, stp);
3642712Snn35248 
3650Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
3662712Snn35248 	mutex_exit(&p->p_splock);
3672712Snn35248 	mutex_exit(&pidlock);
3682712Snn35248 	mutex_exit(&stp->sd_lock);
3692712Snn35248 	return (0);
3702712Snn35248 }
3712712Snn35248 
3722712Snn35248 /*
373*5331Samw  * freectty_lock() attempts to acquire the army of locks required to free
3742712Snn35248  * the ctty associated with a given session leader process.  If it returns
3752712Snn35248  * successfully the following locks will be held:
3762712Snn35248  *	sd_lock, pidlock, p_splock, s_lock
3772712Snn35248  *
378*5331Samw  * as a secondary bit of convenience, freectty_lock() will also return
3792712Snn35248  * pointers to the session, ctty, and ctty stream associated with the
3802712Snn35248  * specified session leader process.
3812712Snn35248  */
3822712Snn35248 static boolean_t
freectty_lock(proc_t * p,sess_t ** spp,vnode_t ** vpp,stdata_t ** stpp,boolean_t at_exit)3832712Snn35248 freectty_lock(proc_t *p, sess_t **spp, vnode_t **vpp, stdata_t **stpp,
3842712Snn35248     boolean_t at_exit)
3852712Snn35248 {
3862712Snn35248 	sess_t		*sp;
3872712Snn35248 	vnode_t		*vp;
3882712Snn35248 	stdata_t	*stp;
3892712Snn35248 
3902712Snn35248 	mutex_enter(&pidlock);			/* protect p_pidp */
3912712Snn35248 	mutex_enter(&p->p_splock);		/* protect p->p_sessp */
3922712Snn35248 	sp = p->p_sessp;
3932712Snn35248 	mutex_enter(&sp->s_lock);		/* protect sp->* */
3942712Snn35248 
3952712Snn35248 	if ((sp->s_sidp != p->p_pidp) ||	/* we're not leader? */
3962712Snn35248 	    (sp->s_vp == NULL)) {		/* no ctty? */
3972712Snn35248 		mutex_exit(&sp->s_lock);
3982712Snn35248 		mutex_exit(&p->p_splock);
3992712Snn35248 		mutex_exit(&pidlock);
4002712Snn35248 		return (B_FALSE);
4012712Snn35248 	}
4022712Snn35248 
4032712Snn35248 	vp = sp->s_vp;
4042712Snn35248 	stp = sp->s_vp->v_stream;
4052712Snn35248 
4062712Snn35248 	if (at_exit) {
4072712Snn35248 		/* stop anyone else calling tty_hold() */
4082712Snn35248 		sp->s_exit = B_TRUE;
4092712Snn35248 	} else {
4102712Snn35248 		/*
4112712Snn35248 		 * due to locking order we have to grab stp->sd_lock before
4122712Snn35248 		 * grabbing all the other proc/session locks.  but after we
4132712Snn35248 		 * drop all our current locks it's possible that someone
4142712Snn35248 		 * could come in and change our current session or close
4152712Snn35248 		 * the current ctty (vp) there by making sp or stp invalid.
4162712Snn35248 		 * (a VN_HOLD on vp won't protect stp because that only
4172712Snn35248 		 * prevents the vnode from being freed not closed.)  so
4182712Snn35248 		 * to prevent this we bump s_ref and s_cnt here.
4192712Snn35248 		 *
4202712Snn35248 		 * course this doesn't matter if we're the last thread in
4212712Snn35248 		 * an exiting process that is the session leader, since no
4222712Snn35248 		 * one else can change our session or free our ctty.
4232712Snn35248 		 */
4242712Snn35248 		sp->s_ref++;	/* hold the session structure */
4252712Snn35248 		sp->s_cnt++;	/* protect vp and stp */
4262712Snn35248 	}
4272712Snn35248 
4282712Snn35248 	/* drop our session locks */
4292712Snn35248 	mutex_exit(&sp->s_lock);
4302712Snn35248 	mutex_exit(&p->p_splock);
4312712Snn35248 	mutex_exit(&pidlock);
4322712Snn35248 
4332712Snn35248 	/* grab locks in the right order */
4342712Snn35248 	mutex_enter(&stp->sd_lock);		/* protects sd_pgidp/sd_sidp */
4352712Snn35248 	mutex_enter(&pidlock);			/* protect p_pidp */
4362712Snn35248 	mutex_enter(&p->p_splock);		/* protects p->p_sessp */
4372712Snn35248 	mutex_enter(&sp->s_lock);		/* protects sp->* */
4382712Snn35248 
4392712Snn35248 	/* if the session has changed, abort mission */
4402712Snn35248 	if (sp != p->p_sessp) {
4412712Snn35248 		/*
4422712Snn35248 		 * this can't happen during process exit since we're the
4432712Snn35248 		 * only thread in the process and we sure didn't change
4442712Snn35248 		 * our own session at this point.
4452712Snn35248 		 */
4462712Snn35248 		ASSERT(!at_exit);
4472712Snn35248 
4482712Snn35248 		/* release our locks and holds */
4492712Snn35248 		mutex_exit(&sp->s_lock);
4502712Snn35248 		mutex_exit(&p->p_splock);
4512712Snn35248 		mutex_exit(&pidlock);
4522712Snn35248 		mutex_exit(&stp->sd_lock);
4532712Snn35248 		tty_rele(sp);
4542712Snn35248 		return (B_FALSE);
4552712Snn35248 	}
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 	/*
4582712Snn35248 	 * sanity checks.  none of this should have changed since we had
4592712Snn35248 	 * holds on the current ctty.
4600Sstevel@tonic-gate 	 */
4612712Snn35248 	ASSERT(sp->s_sidp == p->p_pidp);	/* we're the leader */
4622712Snn35248 	ASSERT(sp->s_vp != NULL);		/* a ctty exists */
4632712Snn35248 	ASSERT(vp == sp->s_vp);
4642712Snn35248 	ASSERT(stp == sp->s_vp->v_stream);
4650Sstevel@tonic-gate 
4662712Snn35248 	/* release our holds */
4672712Snn35248 	if (!at_exit) {
4682712Snn35248 		if ((--(sp)->s_cnt) == 0)
4692712Snn35248 			cv_broadcast(&sp->s_cnt_cv);
4702712Snn35248 		sp->s_ref--;
4712712Snn35248 		ASSERT(sp->s_ref > 0);
4722712Snn35248 	}
4732712Snn35248 
4742712Snn35248 	/* return our pointers */
4752712Snn35248 	*spp = sp;
4762712Snn35248 	*vpp = vp;
4772712Snn35248 	*stpp = stp;
4782712Snn35248 
4792712Snn35248 	return (B_TRUE);
4802712Snn35248 }
4812712Snn35248 
4822712Snn35248 /*
4832712Snn35248  * Returns B_FALSE if no signal is sent to the process group associated with
4842712Snn35248  * this ctty.  Returns B_TRUE if a signal is sent to the process group.
4852712Snn35248  * If it return B_TRUE it also means that all the locks we were holding
4862712Snn35248  * were dropped so that we could send the signal.
4872712Snn35248  */
4882712Snn35248 static boolean_t
freectty_signal(proc_t * p,sess_t * sp,stdata_t * stp,boolean_t at_exit)4892712Snn35248 freectty_signal(proc_t *p, sess_t *sp, stdata_t *stp, boolean_t at_exit)
4902712Snn35248 {
4912712Snn35248 	/* Assert that we hold all the necessary locks. */
4922712Snn35248 	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
4932712Snn35248 	    MUTEX_HELD(&p->p_splock) && MUTEX_HELD(&sp->s_lock));
4942712Snn35248 
4952712Snn35248 	/* check if we already signaled this group */
4962712Snn35248 	if (sp->s_sighuped)
4972712Snn35248 		return (B_FALSE);
4982712Snn35248 
4992712Snn35248 	sp->s_sighuped = B_TRUE;
5002712Snn35248 
5012712Snn35248 	if (!at_exit) {
5022712Snn35248 		/*
5032712Snn35248 		 * once again, we're about to drop our army of locks and we
5042712Snn35248 		 * don't want sp or stp to be freed.  (see the comment in
5052712Snn35248 		 * freectty_lock())
5062712Snn35248 		 */
5072712Snn35248 		sp->s_ref++;	/* hold the session structure */
5082712Snn35248 		sp->s_cnt++;	/* protect vp and stp */
5092712Snn35248 	}
5102712Snn35248 
5112712Snn35248 	/* can't hold these locks while calling pgsignal() */
5122712Snn35248 	mutex_exit(&sp->s_lock);
5132712Snn35248 	mutex_exit(&p->p_splock);
5142712Snn35248 	mutex_exit(&pidlock);
5152712Snn35248 
5162712Snn35248 	/* signal anyone in the foreground process group */
5172712Snn35248 	pgsignal(stp->sd_pgidp, SIGHUP);
5182712Snn35248 
5192712Snn35248 	/* signal anyone blocked in poll on this stream */
5202712Snn35248 	if (!(stp->sd_flag & STRHUP))
5212712Snn35248 		strhup(stp);
5222712Snn35248 
5232712Snn35248 	mutex_exit(&stp->sd_lock);
5242712Snn35248 
5252712Snn35248 	/* release our holds */
5262712Snn35248 	if (!at_exit)
5272712Snn35248 		tty_rele(sp);
5282712Snn35248 
5292712Snn35248 	return (B_TRUE);
5302712Snn35248 }
5312712Snn35248 
5322712Snn35248 int
freectty(boolean_t at_exit)5332712Snn35248 freectty(boolean_t at_exit)
5342712Snn35248 {
5352712Snn35248 	proc_t		*p = curproc;
5362712Snn35248 	stdata_t	*stp;
5372712Snn35248 	vnode_t		*vp;
5382712Snn35248 	cred_t		*cred;
5392712Snn35248 	sess_t		*sp;
5402712Snn35248 	struct pid	*pgidp, *sidp;
5412712Snn35248 	boolean_t	got_sig = B_FALSE;
5422712Snn35248 
5432712Snn35248 	/*
5442712Snn35248 	 * If the current process is a session leader we are going to
5452712Snn35248 	 * try to release the ctty associated our current session.  To
546*5331Samw 	 * do this we need to acquire a bunch of locks, signal any
5472712Snn35248 	 * processes in the forground that are associated with the ctty,
5482712Snn35248 	 * and make sure no one has any outstanding holds on the current
549*5331Samw 	 * session * structure (acquired via tty_hold()).  Hence, we have
5502712Snn35248 	 * the following for(;;) loop that will do all this work for
5512712Snn35248 	 * us and break out when the hold count on the session structure
5522712Snn35248 	 * hits zero.
5532712Snn35248 	 */
5542712Snn35248 	for (;;) {
5552712Snn35248 		if (!freectty_lock(p, &sp, &vp, &stp, at_exit))
5562712Snn35248 			return (EIO);
5572712Snn35248 
5582712Snn35248 		if (freectty_signal(p, sp, stp, at_exit)) {
559*5331Samw 			/* loop around to re-acquire locks */
5602712Snn35248 			continue;
5612712Snn35248 		}
5622712Snn35248 
5632712Snn35248 		/*
5642712Snn35248 		 * Only a session leader process can free a ctty.  So if
5652712Snn35248 		 * we've made it here we know we're a session leader and
5662712Snn35248 		 * if we're not actively exiting it impossible for another
5672712Snn35248 		 * thread in this process to be exiting.  (Because that
5682712Snn35248 		 * thread would have already stopped all other threads
5692712Snn35248 		 * in the current process.)
5702712Snn35248 		 */
5712712Snn35248 		ASSERT(at_exit || !sp->s_exit);
5722712Snn35248 
5732712Snn35248 		/*
5742712Snn35248 		 * If no one else has a hold on this session structure
5752712Snn35248 		 * then we now have exclusive access to it, so break out
5762712Snn35248 		 * of this loop and update the session structure.
5772712Snn35248 		 */
5782712Snn35248 		if (sp->s_cnt == 0)
5792712Snn35248 			break;
5802712Snn35248 
5812712Snn35248 		if (!at_exit) {
5822712Snn35248 			/* need to hold the session so it can't be freed */
5832712Snn35248 			sp->s_ref++;
5842712Snn35248 		}
5852712Snn35248 
5862712Snn35248 		/* ain't locking order fun? */
5872712Snn35248 		mutex_exit(&p->p_splock);
5882712Snn35248 		mutex_exit(&pidlock);
5892712Snn35248 		mutex_exit(&stp->sd_lock);
5902712Snn35248 
5912712Snn35248 		if (at_exit) {
5922712Snn35248 			/*
5932712Snn35248 			 * if we're exiting then we can't allow this operation
5942712Snn35248 			 * to fail so we do a cw_wait() instead of a
5952712Snn35248 			 * cv_wait_sig().  if there are threads with active
5962712Snn35248 			 * holds on this ctty that are blocked, then
5972712Snn35248 			 * they should only be blocked in a cv_wait_sig()
5982712Snn35248 			 * and hopefully they were in the foreground process
5992712Snn35248 			 * group and recieved the SIGHUP we sent above.  of
6002712Snn35248 			 * course it's possible that they weren't in the
6012712Snn35248 			 * foreground process group and didn't get our
6022712Snn35248 			 * signal (or they could be stopped by job control
6032712Snn35248 			 * in which case our signal wouldn't matter until
6042712Snn35248 			 * they are restarted).  in this case we won't
6052712Snn35248 			 * exit until someone else sends them a signal.
6062712Snn35248 			 */
6072712Snn35248 			cv_wait(&sp->s_cnt_cv, &sp->s_lock);
6082712Snn35248 			mutex_exit(&sp->s_lock);
6092712Snn35248 			continue;
6102712Snn35248 		}
6112712Snn35248 
6122712Snn35248 		if (!cv_wait_sig(&sp->s_cnt_cv, &sp->s_lock)) {
6132712Snn35248 			got_sig = B_TRUE;
6142712Snn35248 		}
6152712Snn35248 
6162712Snn35248 		mutex_exit(&sp->s_lock);
6172712Snn35248 		sess_rele(sp, B_FALSE);
6182712Snn35248 
6192712Snn35248 		if (got_sig)
6202712Snn35248 			return (EINTR);
6212712Snn35248 	}
6222712Snn35248 	ASSERT(sp->s_cnt == 0);
6232712Snn35248 
6242712Snn35248 	/* save some pointers for later */
6252712Snn35248 	cred = sp->s_cred;
6262712Snn35248 	pgidp = stp->sd_pgidp;
6272712Snn35248 	sidp = stp->sd_sidp;
6282712Snn35248 
6292712Snn35248 	/* clear the session ctty bindings */
6302712Snn35248 	sess_ctty_clear(sp, stp);
6312712Snn35248 
6322712Snn35248 	/* wake up anyone blocked in tty_hold() */
6332712Snn35248 	if (at_exit) {
6342712Snn35248 		ASSERT(sp->s_exit);
6352712Snn35248 		sp->s_exit = B_FALSE;
6362712Snn35248 		cv_broadcast(&sp->s_exit_cv);
6372712Snn35248 	}
6382712Snn35248 
6392712Snn35248 	/* we can drop these locks now */
6402712Snn35248 	mutex_exit(&sp->s_lock);
6412712Snn35248 	mutex_exit(&p->p_splock);
6422712Snn35248 	mutex_exit(&pidlock);
6432712Snn35248 	mutex_exit(&stp->sd_lock);
6442712Snn35248 
6452712Snn35248 	/* This is the only remaining thread with access to this vnode */
646*5331Samw 	(void) VOP_CLOSE(vp, 0, 1, (offset_t)0, cred, NULL);
6470Sstevel@tonic-gate 	VN_RELE(vp);
6482712Snn35248 	crfree(cred);
6490Sstevel@tonic-gate 
6502712Snn35248 	/* release our holds on assorted structures and return */
6512712Snn35248 	mutex_enter(&pidlock);
6522712Snn35248 	PID_RELE(pgidp);
6532712Snn35248 	PID_RELE(sidp);
6542712Snn35248 	mutex_exit(&pidlock);
6552712Snn35248 
6562712Snn35248 	return (1);
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate /*
6600Sstevel@tonic-gate  *	++++++++++++++++++++++++
6610Sstevel@tonic-gate  *	++  SunOS4.1 Buyback  ++
6620Sstevel@tonic-gate  *	++++++++++++++++++++++++
6630Sstevel@tonic-gate  *
6640Sstevel@tonic-gate  * vhangup: Revoke access of the current tty by all processes
6650Sstevel@tonic-gate  * Used by privileged users to give a "clean" terminal at login
6660Sstevel@tonic-gate  */
6670Sstevel@tonic-gate int
vhangup(void)668560Smeem vhangup(void)
6690Sstevel@tonic-gate {
6700Sstevel@tonic-gate 	if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
6710Sstevel@tonic-gate 		return (set_errno(EPERM));
6720Sstevel@tonic-gate 	/*
6730Sstevel@tonic-gate 	 * This routine used to call freectty() under a condition that
6740Sstevel@tonic-gate 	 * could never happen.  So this code has never actually done
675560Smeem 	 * anything, and evidently nobody has ever noticed.
6760Sstevel@tonic-gate 	 */
6770Sstevel@tonic-gate 	return (0);
6780Sstevel@tonic-gate }
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate dev_t
cttydev(proc_t * pp)6810Sstevel@tonic-gate cttydev(proc_t *pp)
6820Sstevel@tonic-gate {
6832712Snn35248 	sess_t	*sp;
6842712Snn35248 	dev_t	dev;
6852712Snn35248 
6862712Snn35248 	mutex_enter(&pp->p_splock);	/* protects p->p_sessp */
6872712Snn35248 	sp = pp->p_sessp;
6882712Snn35248 
6892712Snn35248 #ifdef DEBUG
6902712Snn35248 	mutex_enter(&sp->s_lock);	/* protects sp->* */
6910Sstevel@tonic-gate 	if (sp->s_vp == NULL)
6922712Snn35248 		ASSERT(sp->s_dev == NODEV);
6932712Snn35248 	else
6942712Snn35248 		ASSERT(sp->s_dev != NODEV);
6952712Snn35248 	mutex_exit(&sp->s_lock);
6962712Snn35248 #endif /* DEBUG */
6972712Snn35248 
6982712Snn35248 	dev = sp->s_dev;
6992712Snn35248 	mutex_exit(&pp->p_splock);
7002712Snn35248 	return (dev);
7010Sstevel@tonic-gate }
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate void
ctty_clear_sighuped(void)7042712Snn35248 ctty_clear_sighuped(void)
7050Sstevel@tonic-gate {
7062712Snn35248 	ASSERT(MUTEX_HELD(&pidlock) || MUTEX_HELD(&curproc->p_splock));
7072712Snn35248 	curproc->p_sessp->s_sighuped = B_FALSE;
7080Sstevel@tonic-gate }
709