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