xref: /illumos-gate/usr/src/uts/common/os/session.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 #include <sys/types.h>
30 #include <sys/sysmacros.h>
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/file.h>
34 #include <sys/vnode.h>
35 #include <sys/errno.h>
36 #include <sys/signal.h>
37 #include <sys/cred.h>
38 #include <sys/policy.h>
39 #include <sys/conf.h>
40 #include <sys/debug.h>
41 #include <sys/proc.h>
42 #include <sys/session.h>
43 #include <sys/kmem.h>
44 #include <sys/cmn_err.h>
45 #include <sys/strsubr.h>
46 #include <sys/fs/snode.h>
47 
48 sess_t session0 = {
49 	&pid0,		/* s_sidp */
50 	{0},		/* s_lock */
51 	1,		/* s_ref */
52 	B_FALSE,	/* s_sighuped */
53 	B_FALSE,	/* s_exit */
54 	0,		/* s_exit_cv */
55 	0,		/* s_cnt */
56 	0,		/* s_cnt_cv */
57 	NODEV,		/* s_dev */
58 	NULL,		/* s_vp */
59 	NULL		/* s_cred */
60 };
61 
62 void
63 sess_hold(proc_t *p)
64 {
65 	ASSERT(MUTEX_HELD(&pidlock) || MUTEX_HELD(&p->p_splock));
66 	mutex_enter(&p->p_sessp->s_lock);
67 	p->p_sessp->s_ref++;
68 	mutex_exit(&p->p_sessp->s_lock);
69 }
70 
71 void
72 sess_rele(sess_t *sp, boolean_t pidlock_held)
73 {
74 	ASSERT(MUTEX_HELD(&pidlock) || !pidlock_held);
75 
76 	mutex_enter(&sp->s_lock);
77 
78 	ASSERT(sp->s_ref != 0);
79 	if (--sp->s_ref > 0) {
80 		mutex_exit(&sp->s_lock);
81 		return;
82 	}
83 	ASSERT(sp->s_ref == 0);
84 
85 	/*
86 	 * It's ok to free this session structure now because we know
87 	 * that no one else can have a pointer to it.  We know this
88 	 * to be true because the only time that s_ref can possibly
89 	 * be incremented is when pidlock or p_splock is held AND there
90 	 * is a proc_t that points to that session structure.  In that
91 	 * case we are guaranteed that the s_ref is at least 1 since there
92 	 * is a proc_t that points to it.  So when s_ref finally drops to
93 	 * zero then no one else has a reference (and hence pointer) to
94 	 * this session structure and there is no valid proc_t pointing
95 	 * to this session structure anymore so, no one can acquire a
96 	 * reference (and pointer) to this session structure so it's
97 	 * ok to free it here.
98 	 */
99 
100 	if (sp == &session0)
101 		panic("sp == &session0");
102 
103 	/* make sure there are no outstanding holds */
104 	ASSERT(sp->s_cnt == 0);
105 
106 	/* make sure there is no exit in progress */
107 	ASSERT(!sp->s_exit);
108 
109 	/* make sure someone already freed any ctty */
110 	ASSERT(sp->s_vp == NULL);
111 	ASSERT(sp->s_dev == NODEV);
112 
113 	if (!pidlock_held)
114 		mutex_enter(&pidlock);
115 	PID_RELE(sp->s_sidp);
116 	if (!pidlock_held)
117 		mutex_exit(&pidlock);
118 
119 	mutex_destroy(&sp->s_lock);
120 	cv_destroy(&sp->s_cnt_cv);
121 	kmem_free(sp, sizeof (sess_t));
122 }
123 
124 sess_t *
125 tty_hold(void)
126 {
127 	proc_t		*p = curproc;
128 	sess_t		*sp;
129 	boolean_t	got_sig = B_FALSE;
130 
131 	/* make sure the caller isn't holding locks they shouldn't */
132 	ASSERT(MUTEX_NOT_HELD(&pidlock));
133 
134 	for (;;) {
135 		mutex_enter(&p->p_splock);	/* protect p->p_sessp */
136 		sp = p->p_sessp;
137 		mutex_enter(&sp->s_lock);	/* protect sp->* */
138 
139 		/* make sure the caller isn't holding locks they shouldn't */
140 		ASSERT((sp->s_vp == NULL) ||
141 		    MUTEX_NOT_HELD(&sp->s_vp->v_stream->sd_lock));
142 
143 		/*
144 		 * If the session leader process is not exiting (and hence
145 		 * not trying to release the session's ctty) then we can
146 		 * safely grab a hold on the current session structure
147 		 * and return it.  If on the other hand the session leader
148 		 * process is exiting and clearing the ctty then we'll
149 		 * wait till it's done before we loop around and grab a
150 		 * hold on the session structure.
151 		 */
152 		if (!sp->s_exit)
153 			break;
154 
155 		/* need to hold the session so it can't be freed */
156 		sp->s_ref++;
157 		mutex_exit(&p->p_splock);
158 
159 		/* Wait till the session leader is done */
160 		if (!cv_wait_sig(&sp->s_exit_cv, &sp->s_lock))
161 			got_sig = B_TRUE;
162 
163 		/*
164 		 * Now we need to drop our hold on the session structure,
165 		 * but we can't hold any locks when we do this because
166 		 * sess_rele() may need to acquire pidlock.
167 		 */
168 		mutex_exit(&sp->s_lock);
169 		sess_rele(sp, B_FALSE);
170 
171 		if (got_sig)
172 			return (NULL);
173 	}
174 
175 	/* whew, we finally got a hold */
176 	sp->s_cnt++;
177 	sp->s_ref++;
178 	mutex_exit(&sp->s_lock);
179 	mutex_exit(&p->p_splock);
180 	return (sp);
181 }
182 
183 void
184 tty_rele(sess_t *sp)
185 {
186 	/* make sure the caller isn't holding locks they shouldn't */
187 	ASSERT(MUTEX_NOT_HELD(&pidlock));
188 
189 	mutex_enter(&sp->s_lock);
190 	if ((--sp->s_cnt) == 0)
191 		cv_broadcast(&sp->s_cnt_cv);
192 	mutex_exit(&sp->s_lock);
193 
194 	sess_rele(sp, B_FALSE);
195 }
196 
197 void
198 sess_create(void)
199 {
200 	proc_t *p = curproc;
201 	sess_t *sp, *old_sp;
202 
203 	sp = kmem_zalloc(sizeof (sess_t), KM_SLEEP);
204 
205 	mutex_init(&sp->s_lock, NULL, MUTEX_DEFAULT, NULL);
206 	cv_init(&sp->s_cnt_cv, NULL, CV_DEFAULT, NULL);
207 
208 	/*
209 	 * we need to grap p_lock to protect p_pgidp because
210 	 * /proc looks at p_pgidp while holding only p_lock.
211 	 *
212 	 * we don't need to hold p->p_sessp->s_lock or get a hold on the
213 	 * session structure since we're not actually updating any of
214 	 * the contents of the old session structure.
215 	 */
216 	mutex_enter(&pidlock);
217 	mutex_enter(&p->p_lock);
218 	mutex_enter(&p->p_splock);
219 
220 	pgexit(p);
221 
222 	sp->s_sidp = p->p_pidp;
223 	sp->s_ref = 1;
224 	sp->s_dev = NODEV;
225 
226 	old_sp = p->p_sessp;
227 	p->p_sessp = sp;
228 
229 	pgjoin(p, p->p_pidp);
230 	PID_HOLD(p->p_pidp);
231 
232 	mutex_exit(&p->p_splock);
233 	mutex_exit(&p->p_lock);
234 	mutex_exit(&pidlock);
235 
236 	sess_rele(old_sp, B_FALSE);
237 }
238 
239 /*
240  * Note that sess_ctty_clear() resets all the fields in the session
241  * structure but doesn't release any holds or free any objects
242  * that the session structure might currently point to.  it is the
243  * callers responsibility to do this.
244  */
245 static void
246 sess_ctty_clear(sess_t *sp, stdata_t *stp)
247 {
248 	/*
249 	 * Assert that we hold all the necessary locks.  We also need
250 	 * to be holding proc_t->p_splock for the process associated
251 	 * with this session, but since we don't have a proc pointer
252 	 * passed in we can't assert this here.
253 	 */
254 	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
255 	    MUTEX_HELD(&sp->s_lock));
256 
257 	/* reset the session structure members to defaults */
258 	sp->s_sighuped = B_FALSE;
259 	sp->s_dev = NODEV;
260 	sp->s_vp = NULL;
261 	sp->s_cred = NULL;
262 
263 	/* reset the stream session and group pointers */
264 	stp->sd_pgidp = NULL;
265 	stp->sd_sidp = NULL;
266 }
267 
268 static void
269 sess_ctty_set(proc_t *p, sess_t *sp, stdata_t *stp)
270 {
271 	cred_t	*crp;
272 
273 	/* Assert that we hold all the necessary locks. */
274 	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
275 	    MUTEX_HELD(&p->p_splock) && MUTEX_HELD(&sp->s_lock));
276 
277 	/* get holds on structures */
278 	mutex_enter(&p->p_crlock);
279 	crhold(crp = p->p_cred);
280 	mutex_exit(&p->p_crlock);
281 	PID_HOLD(sp->s_sidp);	/* requires pidlock */
282 	PID_HOLD(sp->s_sidp);	/* requires pidlock */
283 
284 	/* update the session structure members */
285 	sp->s_vp = makectty(stp->sd_vnode);
286 	sp->s_dev = sp->s_vp->v_rdev;
287 	sp->s_cred = crp;
288 
289 	/* update the stream emebers */
290 	stp->sd_flag |= STRISTTY;	/* just to be sure */
291 	stp->sd_sidp = sp->s_sidp;
292 	stp->sd_pgidp = sp->s_sidp;
293 }
294 
295 int
296 strctty(stdata_t *stp)
297 {
298 	sess_t		*sp;
299 	proc_t		*p = curproc;
300 	boolean_t	got_sig = B_FALSE;
301 
302 	/*
303 	 * We are going to try to make stp the default ctty for the session
304 	 * associated with curproc.  Not only does this require holding a
305 	 * bunch of locks but it also requires waiting for any outstanding
306 	 * holds on the session structure (acquired via tty_hold()) to be
307 	 * released.  Hence, we have the following for(;;) loop that will
308 	 * acquire our locks, do some sanity checks, and wait for the hold
309 	 * count on the session structure to hit zero.  If we get a signal
310 	 * while waiting for outstanding holds to be released then we abort
311 	 * the operation and return.
312 	 */
313 	for (;;) {
314 		mutex_enter(&stp->sd_lock);	/* protects sd_pgidp/sd_sidp */
315 		mutex_enter(&pidlock);		/* protects p_pidp */
316 		mutex_enter(&p->p_splock);	/* protects p_sessp */
317 		sp = p->p_sessp;
318 		mutex_enter(&sp->s_lock);	/* protects sp->* */
319 
320 		if (((stp->sd_flag & (STRHUP|STRDERR|STWRERR|STPLEX)) != 0) ||
321 		    (stp->sd_sidp != NULL) ||		/* stp already ctty? */
322 		    (p->p_pidp != sp->s_sidp) ||	/* we're not leader? */
323 		    (sp->s_vp != NULL)) {		/* session has ctty? */
324 			mutex_exit(&sp->s_lock);
325 			mutex_exit(&p->p_splock);
326 			mutex_exit(&pidlock);
327 			mutex_exit(&stp->sd_lock);
328 			return (ENOTTY);
329 		}
330 
331 		/* sanity check.  we can't be exiting right now */
332 		ASSERT(!sp->s_exit);
333 
334 		/*
335 		 * If no one else has a hold on this session structure
336 		 * then we now have exclusive access to it, so break out
337 		 * of this loop and update the session structure.
338 		 */
339 		if (sp->s_cnt == 0)
340 			break;
341 
342 		/* need to hold the session so it can't be freed */
343 		sp->s_ref++;
344 
345 		/* ain't locking order fun? */
346 		mutex_exit(&p->p_splock);
347 		mutex_exit(&pidlock);
348 		mutex_exit(&stp->sd_lock);
349 
350 		if (!cv_wait_sig(&sp->s_cnt_cv, &sp->s_lock))
351 			got_sig = B_TRUE;
352 		mutex_exit(&sp->s_lock);
353 		sess_rele(sp, B_FALSE);
354 
355 		if (got_sig)
356 			return (EINTR);
357 	}
358 
359 	/* set the session ctty bindings */
360 	sess_ctty_set(p, sp, stp);
361 
362 	mutex_exit(&sp->s_lock);
363 	mutex_exit(&p->p_splock);
364 	mutex_exit(&pidlock);
365 	mutex_exit(&stp->sd_lock);
366 	return (0);
367 }
368 
369 /*
370  * freectty_lock() attempts to acquire the army of locks required to free
371  * the ctty associated with a given session leader process.  If it returns
372  * successfully the following locks will be held:
373  *	sd_lock, pidlock, p_splock, s_lock
374  *
375  * as a secondary bit of convenience, freectty_lock() will also return
376  * pointers to the session, ctty, and ctty stream associated with the
377  * specified session leader process.
378  */
379 static boolean_t
380 freectty_lock(proc_t *p, sess_t **spp, vnode_t **vpp, stdata_t **stpp,
381     boolean_t at_exit)
382 {
383 	sess_t		*sp;
384 	vnode_t		*vp;
385 	stdata_t	*stp;
386 
387 	mutex_enter(&pidlock);			/* protect p_pidp */
388 	mutex_enter(&p->p_splock);		/* protect p->p_sessp */
389 	sp = p->p_sessp;
390 	mutex_enter(&sp->s_lock);		/* protect sp->* */
391 
392 	if ((sp->s_sidp != p->p_pidp) ||	/* we're not leader? */
393 	    (sp->s_vp == NULL)) {		/* no ctty? */
394 		mutex_exit(&sp->s_lock);
395 		mutex_exit(&p->p_splock);
396 		mutex_exit(&pidlock);
397 		return (B_FALSE);
398 	}
399 
400 	vp = sp->s_vp;
401 	stp = sp->s_vp->v_stream;
402 
403 	if (at_exit) {
404 		/* stop anyone else calling tty_hold() */
405 		sp->s_exit = B_TRUE;
406 	} else {
407 		/*
408 		 * due to locking order we have to grab stp->sd_lock before
409 		 * grabbing all the other proc/session locks.  but after we
410 		 * drop all our current locks it's possible that someone
411 		 * could come in and change our current session or close
412 		 * the current ctty (vp) there by making sp or stp invalid.
413 		 * (a VN_HOLD on vp won't protect stp because that only
414 		 * prevents the vnode from being freed not closed.)  so
415 		 * to prevent this we bump s_ref and s_cnt here.
416 		 *
417 		 * course this doesn't matter if we're the last thread in
418 		 * an exiting process that is the session leader, since no
419 		 * one else can change our session or free our ctty.
420 		 */
421 		sp->s_ref++;	/* hold the session structure */
422 		sp->s_cnt++;	/* protect vp and stp */
423 	}
424 
425 	/* drop our session locks */
426 	mutex_exit(&sp->s_lock);
427 	mutex_exit(&p->p_splock);
428 	mutex_exit(&pidlock);
429 
430 	/* grab locks in the right order */
431 	mutex_enter(&stp->sd_lock);		/* protects sd_pgidp/sd_sidp */
432 	mutex_enter(&pidlock);			/* protect p_pidp */
433 	mutex_enter(&p->p_splock);		/* protects p->p_sessp */
434 	mutex_enter(&sp->s_lock);		/* protects sp->* */
435 
436 	/* if the session has changed, abort mission */
437 	if (sp != p->p_sessp) {
438 		/*
439 		 * this can't happen during process exit since we're the
440 		 * only thread in the process and we sure didn't change
441 		 * our own session at this point.
442 		 */
443 		ASSERT(!at_exit);
444 
445 		/* release our locks and holds */
446 		mutex_exit(&sp->s_lock);
447 		mutex_exit(&p->p_splock);
448 		mutex_exit(&pidlock);
449 		mutex_exit(&stp->sd_lock);
450 		tty_rele(sp);
451 		return (B_FALSE);
452 	}
453 
454 	/*
455 	 * sanity checks.  none of this should have changed since we had
456 	 * holds on the current ctty.
457 	 */
458 	ASSERT(sp->s_sidp == p->p_pidp);	/* we're the leader */
459 	ASSERT(sp->s_vp != NULL);		/* a ctty exists */
460 	ASSERT(vp == sp->s_vp);
461 	ASSERT(stp == sp->s_vp->v_stream);
462 
463 	/* release our holds */
464 	if (!at_exit) {
465 		if ((--(sp)->s_cnt) == 0)
466 			cv_broadcast(&sp->s_cnt_cv);
467 		sp->s_ref--;
468 		ASSERT(sp->s_ref > 0);
469 	}
470 
471 	/* return our pointers */
472 	*spp = sp;
473 	*vpp = vp;
474 	*stpp = stp;
475 
476 	return (B_TRUE);
477 }
478 
479 /*
480  * Returns B_FALSE if no signal is sent to the process group associated with
481  * this ctty.  Returns B_TRUE if a signal is sent to the process group.
482  * If it return B_TRUE it also means that all the locks we were holding
483  * were dropped so that we could send the signal.
484  */
485 static boolean_t
486 freectty_signal(proc_t *p, sess_t *sp, stdata_t *stp, boolean_t at_exit)
487 {
488 	/* Assert that we hold all the necessary locks. */
489 	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
490 	    MUTEX_HELD(&p->p_splock) && MUTEX_HELD(&sp->s_lock));
491 
492 	/* check if we already signaled this group */
493 	if (sp->s_sighuped)
494 		return (B_FALSE);
495 
496 	sp->s_sighuped = B_TRUE;
497 
498 	if (!at_exit) {
499 		/*
500 		 * once again, we're about to drop our army of locks and we
501 		 * don't want sp or stp to be freed.  (see the comment in
502 		 * freectty_lock())
503 		 */
504 		sp->s_ref++;	/* hold the session structure */
505 		sp->s_cnt++;	/* protect vp and stp */
506 	}
507 
508 	/* can't hold these locks while calling pgsignal() */
509 	mutex_exit(&sp->s_lock);
510 	mutex_exit(&p->p_splock);
511 	mutex_exit(&pidlock);
512 
513 	/* signal anyone in the foreground process group */
514 	pgsignal(stp->sd_pgidp, SIGHUP);
515 
516 	/* signal anyone blocked in poll on this stream */
517 	if (!(stp->sd_flag & STRHUP))
518 		strhup(stp);
519 
520 	mutex_exit(&stp->sd_lock);
521 
522 	/* release our holds */
523 	if (!at_exit)
524 		tty_rele(sp);
525 
526 	return (B_TRUE);
527 }
528 
529 int
530 freectty(boolean_t at_exit)
531 {
532 	proc_t		*p = curproc;
533 	stdata_t	*stp;
534 	vnode_t		*vp;
535 	cred_t		*cred;
536 	sess_t		*sp;
537 	struct pid	*pgidp, *sidp;
538 	boolean_t	got_sig = B_FALSE;
539 
540 	/*
541 	 * If the current process is a session leader we are going to
542 	 * try to release the ctty associated our current session.  To
543 	 * do this we need to acquire a bunch of locks, signal any
544 	 * processes in the forground that are associated with the ctty,
545 	 * and make sure no one has any outstanding holds on the current
546 	 * session * structure (acquired via tty_hold()).  Hence, we have
547 	 * the following for(;;) loop that will do all this work for
548 	 * us and break out when the hold count on the session structure
549 	 * hits zero.
550 	 */
551 	for (;;) {
552 		if (!freectty_lock(p, &sp, &vp, &stp, at_exit))
553 			return (EIO);
554 
555 		if (freectty_signal(p, sp, stp, at_exit)) {
556 			/* loop around to re-acquire locks */
557 			continue;
558 		}
559 
560 		/*
561 		 * Only a session leader process can free a ctty.  So if
562 		 * we've made it here we know we're a session leader and
563 		 * if we're not actively exiting it impossible for another
564 		 * thread in this process to be exiting.  (Because that
565 		 * thread would have already stopped all other threads
566 		 * in the current process.)
567 		 */
568 		ASSERT(at_exit || !sp->s_exit);
569 
570 		/*
571 		 * If no one else has a hold on this session structure
572 		 * then we now have exclusive access to it, so break out
573 		 * of this loop and update the session structure.
574 		 */
575 		if (sp->s_cnt == 0)
576 			break;
577 
578 		if (!at_exit) {
579 			/* need to hold the session so it can't be freed */
580 			sp->s_ref++;
581 		}
582 
583 		/* ain't locking order fun? */
584 		mutex_exit(&p->p_splock);
585 		mutex_exit(&pidlock);
586 		mutex_exit(&stp->sd_lock);
587 
588 		if (at_exit) {
589 			/*
590 			 * if we're exiting then we can't allow this operation
591 			 * to fail so we do a cw_wait() instead of a
592 			 * cv_wait_sig().  if there are threads with active
593 			 * holds on this ctty that are blocked, then
594 			 * they should only be blocked in a cv_wait_sig()
595 			 * and hopefully they were in the foreground process
596 			 * group and recieved the SIGHUP we sent above.  of
597 			 * course it's possible that they weren't in the
598 			 * foreground process group and didn't get our
599 			 * signal (or they could be stopped by job control
600 			 * in which case our signal wouldn't matter until
601 			 * they are restarted).  in this case we won't
602 			 * exit until someone else sends them a signal.
603 			 */
604 			cv_wait(&sp->s_cnt_cv, &sp->s_lock);
605 			mutex_exit(&sp->s_lock);
606 			continue;
607 		}
608 
609 		if (!cv_wait_sig(&sp->s_cnt_cv, &sp->s_lock)) {
610 			got_sig = B_TRUE;
611 		}
612 
613 		mutex_exit(&sp->s_lock);
614 		sess_rele(sp, B_FALSE);
615 
616 		if (got_sig)
617 			return (EINTR);
618 	}
619 	ASSERT(sp->s_cnt == 0);
620 
621 	/* save some pointers for later */
622 	cred = sp->s_cred;
623 	pgidp = stp->sd_pgidp;
624 	sidp = stp->sd_sidp;
625 
626 	/* clear the session ctty bindings */
627 	sess_ctty_clear(sp, stp);
628 
629 	/* wake up anyone blocked in tty_hold() */
630 	if (at_exit) {
631 		ASSERT(sp->s_exit);
632 		sp->s_exit = B_FALSE;
633 		cv_broadcast(&sp->s_exit_cv);
634 	}
635 
636 	/* we can drop these locks now */
637 	mutex_exit(&sp->s_lock);
638 	mutex_exit(&p->p_splock);
639 	mutex_exit(&pidlock);
640 	mutex_exit(&stp->sd_lock);
641 
642 	/* This is the only remaining thread with access to this vnode */
643 	(void) VOP_CLOSE(vp, 0, 1, (offset_t)0, cred, NULL);
644 	VN_RELE(vp);
645 	crfree(cred);
646 
647 	/* release our holds on assorted structures and return */
648 	mutex_enter(&pidlock);
649 	PID_RELE(pgidp);
650 	PID_RELE(sidp);
651 	mutex_exit(&pidlock);
652 
653 	return (1);
654 }
655 
656 /*
657  *	++++++++++++++++++++++++
658  *	++  SunOS4.1 Buyback  ++
659  *	++++++++++++++++++++++++
660  *
661  * vhangup: Revoke access of the current tty by all processes
662  * Used by privileged users to give a "clean" terminal at login
663  */
664 int
665 vhangup(void)
666 {
667 	if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
668 		return (set_errno(EPERM));
669 	/*
670 	 * This routine used to call freectty() under a condition that
671 	 * could never happen.  So this code has never actually done
672 	 * anything, and evidently nobody has ever noticed.
673 	 */
674 	return (0);
675 }
676 
677 dev_t
678 cttydev(proc_t *pp)
679 {
680 	sess_t	*sp;
681 	dev_t	dev;
682 
683 	mutex_enter(&pp->p_splock);	/* protects p->p_sessp */
684 	sp = pp->p_sessp;
685 
686 #ifdef DEBUG
687 	mutex_enter(&sp->s_lock);	/* protects sp->* */
688 	if (sp->s_vp == NULL)
689 		ASSERT(sp->s_dev == NODEV);
690 	else
691 		ASSERT(sp->s_dev != NODEV);
692 	mutex_exit(&sp->s_lock);
693 #endif /* DEBUG */
694 
695 	dev = sp->s_dev;
696 	mutex_exit(&pp->p_splock);
697 	return (dev);
698 }
699 
700 void
701 ctty_clear_sighuped(void)
702 {
703 	ASSERT(MUTEX_HELD(&pidlock) || MUTEX_HELD(&curproc->p_splock));
704 	curproc->p_sessp->s_sighuped = B_FALSE;
705 }
706