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