xref: /onnv-gate/usr/src/uts/common/os/session.c (revision 2712:f74a135872bc)
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
5*2712Snn35248  * Common Development and Distribution License (the "License").
6*2712Snn35248  * 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*2712Snn35248  * Copyright 2006 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>
49*2712Snn35248 #include <sys/fs/snode.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate sess_t session0 = {
52*2712Snn35248 	&pid0,		/* s_sidp */
53*2712Snn35248 	{0},		/* s_lock */
54*2712Snn35248 	1,		/* s_ref */
55*2712Snn35248 	B_FALSE,	/* s_sighuped */
56*2712Snn35248 	B_FALSE,	/* s_exit */
57*2712Snn35248 	0,		/* s_exit_cv */
58*2712Snn35248 	0,		/* s_cnt */
59*2712Snn35248 	0,		/* s_cnt_cv */
60*2712Snn35248 	NODEV,		/* s_dev */
61*2712Snn35248 	NULL,		/* s_vp */
62*2712Snn35248 	NULL		/* s_cred */
630Sstevel@tonic-gate };
640Sstevel@tonic-gate 
650Sstevel@tonic-gate void
66*2712Snn35248 sess_hold(proc_t *p)
670Sstevel@tonic-gate {
68*2712Snn35248 	ASSERT(MUTEX_HELD(&pidlock) || MUTEX_HELD(&p->p_splock));
69*2712Snn35248 	mutex_enter(&p->p_sessp->s_lock);
70*2712Snn35248 	p->p_sessp->s_ref++;
71*2712Snn35248 	mutex_exit(&p->p_sessp->s_lock);
72*2712Snn35248 }
73*2712Snn35248 
74*2712Snn35248 void
75*2712Snn35248 sess_rele(sess_t *sp, boolean_t pidlock_held)
76*2712Snn35248 {
77*2712Snn35248 	ASSERT(MUTEX_HELD(&pidlock) || !pidlock_held);
78*2712Snn35248 
79*2712Snn35248 	mutex_enter(&sp->s_lock);
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	ASSERT(sp->s_ref != 0);
82*2712Snn35248 	if (--sp->s_ref > 0) {
83*2712Snn35248 		mutex_exit(&sp->s_lock);
84*2712Snn35248 		return;
850Sstevel@tonic-gate 	}
86*2712Snn35248 	ASSERT(sp->s_ref == 0);
87*2712Snn35248 
88*2712Snn35248 	/*
89*2712Snn35248 	 * It's ok to free this session structure now because we know
90*2712Snn35248 	 * that no one else can have a pointer to it.  We know this
91*2712Snn35248 	 * to be true because the only time that s_ref can possibly
92*2712Snn35248 	 * be incremented is when pidlock or p_splock is held AND there
93*2712Snn35248 	 * is a proc_t that points to that session structure.  In that
94*2712Snn35248 	 * case we are guaranteed that the s_ref is at least 1 since there
95*2712Snn35248 	 * is a proc_t that points to it.  So when s_ref finally drops to
96*2712Snn35248 	 * zero then no one else has a reference (and hence pointer) to
97*2712Snn35248 	 * this session structure and there is no valid proc_t pointing
98*2712Snn35248 	 * to this session structure anymore so, no one can acquire a
99*2712Snn35248 	 * reference (and pointer) to this session structure so it's
100*2712Snn35248 	 * ok to free it here.
101*2712Snn35248 	 */
102*2712Snn35248 
103*2712Snn35248 	if (sp == &session0)
104*2712Snn35248 		panic("sp == &session0");
105*2712Snn35248 
106*2712Snn35248 	/* make sure there are no outstanding holds */
107*2712Snn35248 	ASSERT(sp->s_cnt == 0);
108*2712Snn35248 
109*2712Snn35248 	/* make sure there is no exit in progress */
110*2712Snn35248 	ASSERT(!sp->s_exit);
111*2712Snn35248 
112*2712Snn35248 	/* make sure someone already freed any ctty */
113*2712Snn35248 	ASSERT(sp->s_vp == NULL);
114*2712Snn35248 	ASSERT(sp->s_dev == NODEV);
115*2712Snn35248 
116*2712Snn35248 	if (!pidlock_held)
117*2712Snn35248 		mutex_enter(&pidlock);
118*2712Snn35248 	PID_RELE(sp->s_sidp);
119*2712Snn35248 	if (!pidlock_held)
120*2712Snn35248 		mutex_exit(&pidlock);
121*2712Snn35248 
122*2712Snn35248 	mutex_destroy(&sp->s_lock);
123*2712Snn35248 	cv_destroy(&sp->s_cnt_cv);
124*2712Snn35248 	kmem_free(sp, sizeof (sess_t));
125*2712Snn35248 }
126*2712Snn35248 
127*2712Snn35248 sess_t *
128*2712Snn35248 tty_hold(void)
129*2712Snn35248 {
130*2712Snn35248 	proc_t		*p = curproc;
131*2712Snn35248 	sess_t		*sp;
132*2712Snn35248 	boolean_t	got_sig = B_FALSE;
133*2712Snn35248 
134*2712Snn35248 	/* make sure the caller isn't holding locks they shouldn't */
135*2712Snn35248 	ASSERT(MUTEX_NOT_HELD(&pidlock));
136*2712Snn35248 
137*2712Snn35248 	for (;;) {
138*2712Snn35248 		mutex_enter(&p->p_splock);	/* protect p->p_sessp */
139*2712Snn35248 		sp = p->p_sessp;
140*2712Snn35248 		mutex_enter(&sp->s_lock);	/* protect sp->* */
141*2712Snn35248 
142*2712Snn35248 		/* make sure the caller isn't holding locks they shouldn't */
143*2712Snn35248 		ASSERT((sp->s_vp == NULL) ||
144*2712Snn35248 		    MUTEX_NOT_HELD(&sp->s_vp->v_stream->sd_lock));
145*2712Snn35248 
146*2712Snn35248 		/*
147*2712Snn35248 		 * If the session leader process is not exiting (and hence
148*2712Snn35248 		 * not trying to release the session's ctty) then we can
149*2712Snn35248 		 * safely grab a hold on the current session structure
150*2712Snn35248 		 * and return it.  If on the other hand the session leader
151*2712Snn35248 		 * process is exiting and clearing the ctty then we'll
152*2712Snn35248 		 * wait till it's done before we loop around and grab a
153*2712Snn35248 		 * hold on the session structure.
154*2712Snn35248 		 */
155*2712Snn35248 		if (!sp->s_exit)
156*2712Snn35248 			break;
157*2712Snn35248 
158*2712Snn35248 		/* need to hold the session so it can't be freed */
159*2712Snn35248 		sp->s_ref++;
160*2712Snn35248 		mutex_exit(&p->p_splock);
161*2712Snn35248 
162*2712Snn35248 		/* Wait till the session leader is done */
163*2712Snn35248 		if (!cv_wait_sig(&sp->s_exit_cv, &sp->s_lock))
164*2712Snn35248 			got_sig = B_TRUE;
165*2712Snn35248 
166*2712Snn35248 		/*
167*2712Snn35248 		 * Now we need to drop our hold on the session structure,
168*2712Snn35248 		 * but we can't hold any locks when we do this because
169*2712Snn35248 		 * sess_rele() may need to aquire pidlock.
170*2712Snn35248 		 */
171*2712Snn35248 		mutex_exit(&sp->s_lock);
172*2712Snn35248 		sess_rele(sp, B_FALSE);
173*2712Snn35248 
174*2712Snn35248 		if (got_sig)
175*2712Snn35248 			return (NULL);
176*2712Snn35248 	}
177*2712Snn35248 
178*2712Snn35248 	/* whew, we finally got a hold */
179*2712Snn35248 	sp->s_cnt++;
180*2712Snn35248 	sp->s_ref++;
181*2712Snn35248 	mutex_exit(&sp->s_lock);
182*2712Snn35248 	mutex_exit(&p->p_splock);
183*2712Snn35248 	return (sp);
184*2712Snn35248 }
185*2712Snn35248 
186*2712Snn35248 void
187*2712Snn35248 tty_rele(sess_t *sp)
188*2712Snn35248 {
189*2712Snn35248 	/* make sure the caller isn't holding locks they shouldn't */
190*2712Snn35248 	ASSERT(MUTEX_NOT_HELD(&pidlock));
191*2712Snn35248 
192*2712Snn35248 	mutex_enter(&sp->s_lock);
193*2712Snn35248 	if ((--sp->s_cnt) == 0)
194*2712Snn35248 		cv_broadcast(&sp->s_cnt_cv);
195*2712Snn35248 	mutex_exit(&sp->s_lock);
196*2712Snn35248 
197*2712Snn35248 	sess_rele(sp, B_FALSE);
1980Sstevel@tonic-gate }
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate void
2010Sstevel@tonic-gate sess_create(void)
2020Sstevel@tonic-gate {
203*2712Snn35248 	proc_t *p = curproc;
204*2712Snn35248 	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);
209*2712Snn35248 	cv_init(&sp->s_cnt_cv, NULL, CV_DEFAULT, NULL);
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	/*
212*2712Snn35248 	 * we need to grap p_lock to protect p_pgidp because
213*2712Snn35248 	 * /proc looks at p_pgidp while holding only p_lock.
214*2712Snn35248 	 *
215*2712Snn35248 	 * we don't need to hold p->p_sessp->s_lock or get a hold on the
216*2712Snn35248 	 * session structure since we're not actually updating any of
217*2712Snn35248 	 * the contents of the old session structure.
2180Sstevel@tonic-gate 	 */
219*2712Snn35248 	mutex_enter(&pidlock);
220*2712Snn35248 	mutex_enter(&p->p_lock);
221*2712Snn35248 	mutex_enter(&p->p_splock);
2220Sstevel@tonic-gate 
223*2712Snn35248 	pgexit(p);
224*2712Snn35248 
225*2712Snn35248 	sp->s_sidp = p->p_pidp;
2260Sstevel@tonic-gate 	sp->s_ref = 1;
2270Sstevel@tonic-gate 	sp->s_dev = NODEV;
2280Sstevel@tonic-gate 
229*2712Snn35248 	old_sp = p->p_sessp;
230*2712Snn35248 	p->p_sessp = sp;
231*2712Snn35248 
232*2712Snn35248 	pgjoin(p, p->p_pidp);
233*2712Snn35248 	PID_HOLD(p->p_pidp);
2340Sstevel@tonic-gate 
235*2712Snn35248 	mutex_exit(&p->p_splock);
236*2712Snn35248 	mutex_exit(&p->p_lock);
237*2712Snn35248 	mutex_exit(&pidlock);
2380Sstevel@tonic-gate 
239*2712Snn35248 	sess_rele(old_sp, B_FALSE);
2400Sstevel@tonic-gate }
2410Sstevel@tonic-gate 
242*2712Snn35248 /*
243*2712Snn35248  * Note that sess_ctty_clear() resets all the fields in the session
244*2712Snn35248  * structure but doesn't release any holds or free any objects
245*2712Snn35248  * that the session structure might currently point to.  it is the
246*2712Snn35248  * callers responsibility to do this.
247*2712Snn35248  */
248*2712Snn35248 static void
249*2712Snn35248 sess_ctty_clear(sess_t *sp, stdata_t *stp)
2500Sstevel@tonic-gate {
251*2712Snn35248 	/*
252*2712Snn35248 	 * Assert that we hold all the necessary locks.  We also need
253*2712Snn35248 	 * to be holding proc_t->p_splock for the process associated
254*2712Snn35248 	 * with this session, but since we don't have a proc pointer
255*2712Snn35248 	 * passed in we can't assert this here.
256*2712Snn35248 	 */
257*2712Snn35248 	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
258*2712Snn35248 	    MUTEX_HELD(&sp->s_lock));
2590Sstevel@tonic-gate 
260*2712Snn35248 	/* reset the session structure members to defaults */
261*2712Snn35248 	sp->s_sighuped = B_FALSE;
262*2712Snn35248 	sp->s_dev = NODEV;
2630Sstevel@tonic-gate 	sp->s_vp = NULL;
264560Smeem 	sp->s_cred = NULL;
2650Sstevel@tonic-gate 
266*2712Snn35248 	/* reset the stream session and group pointers */
267*2712Snn35248 	stp->sd_pgidp = NULL;
268*2712Snn35248 	stp->sd_sidp = NULL;
269*2712Snn35248 }
270*2712Snn35248 
271*2712Snn35248 static void
272*2712Snn35248 sess_ctty_set(proc_t *p, sess_t *sp, stdata_t *stp)
273*2712Snn35248 {
274*2712Snn35248 	cred_t	*crp;
275*2712Snn35248 
276*2712Snn35248 	/* Assert that we hold all the necessary locks. */
277*2712Snn35248 	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
278*2712Snn35248 	    MUTEX_HELD(&p->p_splock) && MUTEX_HELD(&sp->s_lock));
279*2712Snn35248 
280*2712Snn35248 	/* get holds on structures */
281*2712Snn35248 	mutex_enter(&p->p_crlock);
282*2712Snn35248 	crhold(crp = p->p_cred);
283*2712Snn35248 	mutex_exit(&p->p_crlock);
284*2712Snn35248 	PID_HOLD(sp->s_sidp);	/* requires pidlock */
285*2712Snn35248 	PID_HOLD(sp->s_sidp);	/* requires pidlock */
286*2712Snn35248 
287*2712Snn35248 	/* update the session structure members */
288*2712Snn35248 	sp->s_vp = makectty(stp->sd_vnode);
289*2712Snn35248 	sp->s_dev = sp->s_vp->v_rdev;
290*2712Snn35248 	sp->s_cred = crp;
291*2712Snn35248 
292*2712Snn35248 	/* update the stream emebers */
293*2712Snn35248 	stp->sd_flag |= STRISTTY;	/* just to be sure */
294*2712Snn35248 	stp->sd_sidp = sp->s_sidp;
295*2712Snn35248 	stp->sd_pgidp = sp->s_sidp;
296*2712Snn35248 }
297*2712Snn35248 
298*2712Snn35248 int
299*2712Snn35248 strctty(stdata_t *stp)
300*2712Snn35248 {
301*2712Snn35248 	sess_t		*sp;
302*2712Snn35248 	proc_t		*p = curproc;
303*2712Snn35248 	boolean_t	got_sig = B_FALSE;
304*2712Snn35248 
3050Sstevel@tonic-gate 	/*
306*2712Snn35248 	 * We are going to try to make stp the default ctty for the session
307*2712Snn35248 	 * associated with curproc.  Not only does this require holding a
308*2712Snn35248 	 * bunch of locks but it also requires waiting for any outstanding
309*2712Snn35248 	 * holds on the session structure (aquired via tty_hold()) to be
310*2712Snn35248 	 * released.  Hence, we have the following for(;;) loop that will
311*2712Snn35248 	 * aquire our locks, do some sanity checks, and wait for the hold
312*2712Snn35248 	 * count on the session structure to hit zero.  If we get a signal
313*2712Snn35248 	 * while waiting for outstanding holds to be released then we abort
314*2712Snn35248 	 * the operation and return.
3150Sstevel@tonic-gate 	 */
316*2712Snn35248 	for (;;) {
317*2712Snn35248 		mutex_enter(&stp->sd_lock);	/* protects sd_pgidp/sd_sidp */
318*2712Snn35248 		mutex_enter(&pidlock);		/* protects p_pidp */
319*2712Snn35248 		mutex_enter(&p->p_splock);	/* protects p_sessp */
320*2712Snn35248 		sp = p->p_sessp;
321*2712Snn35248 		mutex_enter(&sp->s_lock);	/* protects sp->* */
3220Sstevel@tonic-gate 
323*2712Snn35248 		if (((stp->sd_flag & (STRHUP|STRDERR|STWRERR|STPLEX)) != 0) ||
324*2712Snn35248 		    (stp->sd_sidp != NULL) ||		/* stp already ctty? */
325*2712Snn35248 		    (p->p_pidp != sp->s_sidp) ||	/* we're not leader? */
326*2712Snn35248 		    (sp->s_vp != NULL)) {		/* session has ctty? */
327*2712Snn35248 			mutex_exit(&sp->s_lock);
328*2712Snn35248 			mutex_exit(&p->p_splock);
329*2712Snn35248 			mutex_exit(&pidlock);
330*2712Snn35248 			mutex_exit(&stp->sd_lock);
331*2712Snn35248 			return (ENOTTY);
332*2712Snn35248 		}
333*2712Snn35248 
334*2712Snn35248 		/* sanity check.  we can't be exiting right now */
335*2712Snn35248 		ASSERT(!sp->s_exit);
336*2712Snn35248 
337*2712Snn35248 		/*
338*2712Snn35248 		 * If no one else has a hold on this session structure
339*2712Snn35248 		 * then we now have exclusive access to it, so break out
340*2712Snn35248 		 * of this loop and update the session structure.
341*2712Snn35248 		 */
342*2712Snn35248 		if (sp->s_cnt == 0)
343*2712Snn35248 			break;
344*2712Snn35248 
345*2712Snn35248 		/* need to hold the session so it can't be freed */
346*2712Snn35248 		sp->s_ref++;
347*2712Snn35248 
348*2712Snn35248 		/* ain't locking order fun? */
349*2712Snn35248 		mutex_exit(&p->p_splock);
350*2712Snn35248 		mutex_exit(&pidlock);
351*2712Snn35248 		mutex_exit(&stp->sd_lock);
352*2712Snn35248 
353*2712Snn35248 		if (!cv_wait_sig(&sp->s_cnt_cv, &sp->s_lock))
354*2712Snn35248 			got_sig = B_TRUE;
355*2712Snn35248 		mutex_exit(&sp->s_lock);
356*2712Snn35248 		sess_rele(sp, B_FALSE);
357*2712Snn35248 
358*2712Snn35248 		if (got_sig)
359*2712Snn35248 			return (EINTR);
360*2712Snn35248 	}
361*2712Snn35248 
362*2712Snn35248 	/* set the session ctty bindings */
363*2712Snn35248 	sess_ctty_set(p, sp, stp);
364*2712Snn35248 
3650Sstevel@tonic-gate 	mutex_exit(&sp->s_lock);
366*2712Snn35248 	mutex_exit(&p->p_splock);
367*2712Snn35248 	mutex_exit(&pidlock);
368*2712Snn35248 	mutex_exit(&stp->sd_lock);
369*2712Snn35248 	return (0);
370*2712Snn35248 }
371*2712Snn35248 
372*2712Snn35248 /*
373*2712Snn35248  * freectty_lock() attempts to aquire the army of locks required to free
374*2712Snn35248  * the ctty associated with a given session leader process.  If it returns
375*2712Snn35248  * successfully the following locks will be held:
376*2712Snn35248  *	sd_lock, pidlock, p_splock, s_lock
377*2712Snn35248  *
378*2712Snn35248  * as a secondary bit of convience, freectty_lock() will also return
379*2712Snn35248  * pointers to the session, ctty, and ctty stream associated with the
380*2712Snn35248  * specified session leader process.
381*2712Snn35248  */
382*2712Snn35248 static boolean_t
383*2712Snn35248 freectty_lock(proc_t *p, sess_t **spp, vnode_t **vpp, stdata_t **stpp,
384*2712Snn35248     boolean_t at_exit)
385*2712Snn35248 {
386*2712Snn35248 	sess_t		*sp;
387*2712Snn35248 	vnode_t		*vp;
388*2712Snn35248 	stdata_t	*stp;
389*2712Snn35248 
390*2712Snn35248 	mutex_enter(&pidlock);			/* protect p_pidp */
391*2712Snn35248 	mutex_enter(&p->p_splock);		/* protect p->p_sessp */
392*2712Snn35248 	sp = p->p_sessp;
393*2712Snn35248 	mutex_enter(&sp->s_lock);		/* protect sp->* */
394*2712Snn35248 
395*2712Snn35248 	if ((sp->s_sidp != p->p_pidp) ||	/* we're not leader? */
396*2712Snn35248 	    (sp->s_vp == NULL)) {		/* no ctty? */
397*2712Snn35248 		mutex_exit(&sp->s_lock);
398*2712Snn35248 		mutex_exit(&p->p_splock);
399*2712Snn35248 		mutex_exit(&pidlock);
400*2712Snn35248 		return (B_FALSE);
401*2712Snn35248 	}
402*2712Snn35248 
403*2712Snn35248 	vp = sp->s_vp;
404*2712Snn35248 	stp = sp->s_vp->v_stream;
405*2712Snn35248 
406*2712Snn35248 	if (at_exit) {
407*2712Snn35248 		/* stop anyone else calling tty_hold() */
408*2712Snn35248 		sp->s_exit = B_TRUE;
409*2712Snn35248 	} else {
410*2712Snn35248 		/*
411*2712Snn35248 		 * due to locking order we have to grab stp->sd_lock before
412*2712Snn35248 		 * grabbing all the other proc/session locks.  but after we
413*2712Snn35248 		 * drop all our current locks it's possible that someone
414*2712Snn35248 		 * could come in and change our current session or close
415*2712Snn35248 		 * the current ctty (vp) there by making sp or stp invalid.
416*2712Snn35248 		 * (a VN_HOLD on vp won't protect stp because that only
417*2712Snn35248 		 * prevents the vnode from being freed not closed.)  so
418*2712Snn35248 		 * to prevent this we bump s_ref and s_cnt here.
419*2712Snn35248 		 *
420*2712Snn35248 		 * course this doesn't matter if we're the last thread in
421*2712Snn35248 		 * an exiting process that is the session leader, since no
422*2712Snn35248 		 * one else can change our session or free our ctty.
423*2712Snn35248 		 */
424*2712Snn35248 		sp->s_ref++;	/* hold the session structure */
425*2712Snn35248 		sp->s_cnt++;	/* protect vp and stp */
426*2712Snn35248 	}
427*2712Snn35248 
428*2712Snn35248 	/* drop our session locks */
429*2712Snn35248 	mutex_exit(&sp->s_lock);
430*2712Snn35248 	mutex_exit(&p->p_splock);
431*2712Snn35248 	mutex_exit(&pidlock);
432*2712Snn35248 
433*2712Snn35248 	/* grab locks in the right order */
434*2712Snn35248 	mutex_enter(&stp->sd_lock);		/* protects sd_pgidp/sd_sidp */
435*2712Snn35248 	mutex_enter(&pidlock);			/* protect p_pidp */
436*2712Snn35248 	mutex_enter(&p->p_splock);		/* protects p->p_sessp */
437*2712Snn35248 	mutex_enter(&sp->s_lock);		/* protects sp->* */
438*2712Snn35248 
439*2712Snn35248 	/* if the session has changed, abort mission */
440*2712Snn35248 	if (sp != p->p_sessp) {
441*2712Snn35248 		/*
442*2712Snn35248 		 * this can't happen during process exit since we're the
443*2712Snn35248 		 * only thread in the process and we sure didn't change
444*2712Snn35248 		 * our own session at this point.
445*2712Snn35248 		 */
446*2712Snn35248 		ASSERT(!at_exit);
447*2712Snn35248 
448*2712Snn35248 		/* release our locks and holds */
449*2712Snn35248 		mutex_exit(&sp->s_lock);
450*2712Snn35248 		mutex_exit(&p->p_splock);
451*2712Snn35248 		mutex_exit(&pidlock);
452*2712Snn35248 		mutex_exit(&stp->sd_lock);
453*2712Snn35248 		tty_rele(sp);
454*2712Snn35248 		return (B_FALSE);
455*2712Snn35248 	}
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 	/*
458*2712Snn35248 	 * sanity checks.  none of this should have changed since we had
459*2712Snn35248 	 * holds on the current ctty.
4600Sstevel@tonic-gate 	 */
461*2712Snn35248 	ASSERT(sp->s_sidp == p->p_pidp);	/* we're the leader */
462*2712Snn35248 	ASSERT(sp->s_vp != NULL);		/* a ctty exists */
463*2712Snn35248 	ASSERT(vp == sp->s_vp);
464*2712Snn35248 	ASSERT(stp == sp->s_vp->v_stream);
4650Sstevel@tonic-gate 
466*2712Snn35248 	/* release our holds */
467*2712Snn35248 	if (!at_exit) {
468*2712Snn35248 		if ((--(sp)->s_cnt) == 0)
469*2712Snn35248 			cv_broadcast(&sp->s_cnt_cv);
470*2712Snn35248 		sp->s_ref--;
471*2712Snn35248 		ASSERT(sp->s_ref > 0);
472*2712Snn35248 	}
473*2712Snn35248 
474*2712Snn35248 	/* return our pointers */
475*2712Snn35248 	*spp = sp;
476*2712Snn35248 	*vpp = vp;
477*2712Snn35248 	*stpp = stp;
478*2712Snn35248 
479*2712Snn35248 	return (B_TRUE);
480*2712Snn35248 }
481*2712Snn35248 
482*2712Snn35248 /*
483*2712Snn35248  * Returns B_FALSE if no signal is sent to the process group associated with
484*2712Snn35248  * this ctty.  Returns B_TRUE if a signal is sent to the process group.
485*2712Snn35248  * If it return B_TRUE it also means that all the locks we were holding
486*2712Snn35248  * were dropped so that we could send the signal.
487*2712Snn35248  */
488*2712Snn35248 static boolean_t
489*2712Snn35248 freectty_signal(proc_t *p, sess_t *sp, stdata_t *stp, boolean_t at_exit)
490*2712Snn35248 {
491*2712Snn35248 	/* Assert that we hold all the necessary locks. */
492*2712Snn35248 	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
493*2712Snn35248 	    MUTEX_HELD(&p->p_splock) && MUTEX_HELD(&sp->s_lock));
494*2712Snn35248 
495*2712Snn35248 	/* check if we already signaled this group */
496*2712Snn35248 	if (sp->s_sighuped)
497*2712Snn35248 		return (B_FALSE);
498*2712Snn35248 
499*2712Snn35248 	sp->s_sighuped = B_TRUE;
500*2712Snn35248 
501*2712Snn35248 	if (!at_exit) {
502*2712Snn35248 		/*
503*2712Snn35248 		 * once again, we're about to drop our army of locks and we
504*2712Snn35248 		 * don't want sp or stp to be freed.  (see the comment in
505*2712Snn35248 		 * freectty_lock())
506*2712Snn35248 		 */
507*2712Snn35248 		sp->s_ref++;	/* hold the session structure */
508*2712Snn35248 		sp->s_cnt++;	/* protect vp and stp */
509*2712Snn35248 	}
510*2712Snn35248 
511*2712Snn35248 	/* can't hold these locks while calling pgsignal() */
512*2712Snn35248 	mutex_exit(&sp->s_lock);
513*2712Snn35248 	mutex_exit(&p->p_splock);
514*2712Snn35248 	mutex_exit(&pidlock);
515*2712Snn35248 
516*2712Snn35248 	/* signal anyone in the foreground process group */
517*2712Snn35248 	pgsignal(stp->sd_pgidp, SIGHUP);
518*2712Snn35248 
519*2712Snn35248 	/* signal anyone blocked in poll on this stream */
520*2712Snn35248 	if (!(stp->sd_flag & STRHUP))
521*2712Snn35248 		strhup(stp);
522*2712Snn35248 
523*2712Snn35248 	mutex_exit(&stp->sd_lock);
524*2712Snn35248 
525*2712Snn35248 	/* release our holds */
526*2712Snn35248 	if (!at_exit)
527*2712Snn35248 		tty_rele(sp);
528*2712Snn35248 
529*2712Snn35248 	return (B_TRUE);
530*2712Snn35248 }
531*2712Snn35248 
532*2712Snn35248 int
533*2712Snn35248 freectty(boolean_t at_exit)
534*2712Snn35248 {
535*2712Snn35248 	proc_t		*p = curproc;
536*2712Snn35248 	stdata_t	*stp;
537*2712Snn35248 	vnode_t		*vp;
538*2712Snn35248 	cred_t		*cred;
539*2712Snn35248 	sess_t		*sp;
540*2712Snn35248 	struct pid	*pgidp, *sidp;
541*2712Snn35248 	boolean_t	got_sig = B_FALSE;
542*2712Snn35248 
543*2712Snn35248 	/*
544*2712Snn35248 	 * If the current process is a session leader we are going to
545*2712Snn35248 	 * try to release the ctty associated our current session.  To
546*2712Snn35248 	 * do this we need to aquire a bunch of locks, signal any
547*2712Snn35248 	 * processes in the forground that are associated with the ctty,
548*2712Snn35248 	 * and make sure no one has any outstanding holds on the current
549*2712Snn35248 	 * session * structure (aquired via tty_hold()).  Hence, we have
550*2712Snn35248 	 * the following for(;;) loop that will do all this work for
551*2712Snn35248 	 * us and break out when the hold count on the session structure
552*2712Snn35248 	 * hits zero.
553*2712Snn35248 	 */
554*2712Snn35248 	for (;;) {
555*2712Snn35248 		if (!freectty_lock(p, &sp, &vp, &stp, at_exit))
556*2712Snn35248 			return (EIO);
557*2712Snn35248 
558*2712Snn35248 		if (freectty_signal(p, sp, stp, at_exit)) {
559*2712Snn35248 			/* loop around to re-aquire locks */
560*2712Snn35248 			continue;
561*2712Snn35248 		}
562*2712Snn35248 
563*2712Snn35248 		/*
564*2712Snn35248 		 * Only a session leader process can free a ctty.  So if
565*2712Snn35248 		 * we've made it here we know we're a session leader and
566*2712Snn35248 		 * if we're not actively exiting it impossible for another
567*2712Snn35248 		 * thread in this process to be exiting.  (Because that
568*2712Snn35248 		 * thread would have already stopped all other threads
569*2712Snn35248 		 * in the current process.)
570*2712Snn35248 		 */
571*2712Snn35248 		ASSERT(at_exit || !sp->s_exit);
572*2712Snn35248 
573*2712Snn35248 		/*
574*2712Snn35248 		 * If no one else has a hold on this session structure
575*2712Snn35248 		 * then we now have exclusive access to it, so break out
576*2712Snn35248 		 * of this loop and update the session structure.
577*2712Snn35248 		 */
578*2712Snn35248 		if (sp->s_cnt == 0)
579*2712Snn35248 			break;
580*2712Snn35248 
581*2712Snn35248 		if (!at_exit) {
582*2712Snn35248 			/* need to hold the session so it can't be freed */
583*2712Snn35248 			sp->s_ref++;
584*2712Snn35248 		}
585*2712Snn35248 
586*2712Snn35248 		/* ain't locking order fun? */
587*2712Snn35248 		mutex_exit(&p->p_splock);
588*2712Snn35248 		mutex_exit(&pidlock);
589*2712Snn35248 		mutex_exit(&stp->sd_lock);
590*2712Snn35248 
591*2712Snn35248 		if (at_exit) {
592*2712Snn35248 			/*
593*2712Snn35248 			 * if we're exiting then we can't allow this operation
594*2712Snn35248 			 * to fail so we do a cw_wait() instead of a
595*2712Snn35248 			 * cv_wait_sig().  if there are threads with active
596*2712Snn35248 			 * holds on this ctty that are blocked, then
597*2712Snn35248 			 * they should only be blocked in a cv_wait_sig()
598*2712Snn35248 			 * and hopefully they were in the foreground process
599*2712Snn35248 			 * group and recieved the SIGHUP we sent above.  of
600*2712Snn35248 			 * course it's possible that they weren't in the
601*2712Snn35248 			 * foreground process group and didn't get our
602*2712Snn35248 			 * signal (or they could be stopped by job control
603*2712Snn35248 			 * in which case our signal wouldn't matter until
604*2712Snn35248 			 * they are restarted).  in this case we won't
605*2712Snn35248 			 * exit until someone else sends them a signal.
606*2712Snn35248 			 */
607*2712Snn35248 			cv_wait(&sp->s_cnt_cv, &sp->s_lock);
608*2712Snn35248 			mutex_exit(&sp->s_lock);
609*2712Snn35248 			continue;
610*2712Snn35248 		}
611*2712Snn35248 
612*2712Snn35248 		if (!cv_wait_sig(&sp->s_cnt_cv, &sp->s_lock)) {
613*2712Snn35248 			got_sig = B_TRUE;
614*2712Snn35248 		}
615*2712Snn35248 
616*2712Snn35248 		mutex_exit(&sp->s_lock);
617*2712Snn35248 		sess_rele(sp, B_FALSE);
618*2712Snn35248 
619*2712Snn35248 		if (got_sig)
620*2712Snn35248 			return (EINTR);
621*2712Snn35248 	}
622*2712Snn35248 	ASSERT(sp->s_cnt == 0);
623*2712Snn35248 
624*2712Snn35248 	/* save some pointers for later */
625*2712Snn35248 	cred = sp->s_cred;
626*2712Snn35248 	pgidp = stp->sd_pgidp;
627*2712Snn35248 	sidp = stp->sd_sidp;
628*2712Snn35248 
629*2712Snn35248 	/* clear the session ctty bindings */
630*2712Snn35248 	sess_ctty_clear(sp, stp);
631*2712Snn35248 
632*2712Snn35248 	/* wake up anyone blocked in tty_hold() */
633*2712Snn35248 	if (at_exit) {
634*2712Snn35248 		ASSERT(sp->s_exit);
635*2712Snn35248 		sp->s_exit = B_FALSE;
636*2712Snn35248 		cv_broadcast(&sp->s_exit_cv);
637*2712Snn35248 	}
638*2712Snn35248 
639*2712Snn35248 	/* we can drop these locks now */
640*2712Snn35248 	mutex_exit(&sp->s_lock);
641*2712Snn35248 	mutex_exit(&p->p_splock);
642*2712Snn35248 	mutex_exit(&pidlock);
643*2712Snn35248 	mutex_exit(&stp->sd_lock);
644*2712Snn35248 
645*2712Snn35248 	/* This is the only remaining thread with access to this vnode */
6460Sstevel@tonic-gate 	(void) VOP_CLOSE(vp, 0, 1, (offset_t)0, cred);
6470Sstevel@tonic-gate 	VN_RELE(vp);
648*2712Snn35248 	crfree(cred);
6490Sstevel@tonic-gate 
650*2712Snn35248 	/* release our holds on assorted structures and return */
651*2712Snn35248 	mutex_enter(&pidlock);
652*2712Snn35248 	PID_RELE(pgidp);
653*2712Snn35248 	PID_RELE(sidp);
654*2712Snn35248 	mutex_exit(&pidlock);
655*2712Snn35248 
656*2712Snn35248 	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
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
6810Sstevel@tonic-gate cttydev(proc_t *pp)
6820Sstevel@tonic-gate {
683*2712Snn35248 	sess_t	*sp;
684*2712Snn35248 	dev_t	dev;
685*2712Snn35248 
686*2712Snn35248 	mutex_enter(&pp->p_splock);	/* protects p->p_sessp */
687*2712Snn35248 	sp = pp->p_sessp;
688*2712Snn35248 
689*2712Snn35248 #ifdef DEBUG
690*2712Snn35248 	mutex_enter(&sp->s_lock);	/* protects sp->* */
6910Sstevel@tonic-gate 	if (sp->s_vp == NULL)
692*2712Snn35248 		ASSERT(sp->s_dev == NODEV);
693*2712Snn35248 	else
694*2712Snn35248 		ASSERT(sp->s_dev != NODEV);
695*2712Snn35248 	mutex_exit(&sp->s_lock);
696*2712Snn35248 #endif /* DEBUG */
697*2712Snn35248 
698*2712Snn35248 	dev = sp->s_dev;
699*2712Snn35248 	mutex_exit(&pp->p_splock);
700*2712Snn35248 	return (dev);
7010Sstevel@tonic-gate }
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate void
704*2712Snn35248 ctty_clear_sighuped(void)
7050Sstevel@tonic-gate {
706*2712Snn35248 	ASSERT(MUTEX_HELD(&pidlock) || MUTEX_HELD(&curproc->p_splock));
707*2712Snn35248 	curproc->p_sessp->s_sighuped = B_FALSE;
7080Sstevel@tonic-gate }
709