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