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