1 /* 2 * Copyright 2007-2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2004-2009, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "tty_private.h" 9 10 #include <ctype.h> 11 #include <errno.h> 12 #include <signal.h> 13 #include <stdio.h> 14 #include <string.h> 15 #include <sys/ioctl.h> 16 #include <sys/stat.h> 17 #include <unistd.h> 18 19 #include <util/AutoLock.h> 20 #include <util/kernel_cpp.h> 21 22 #include <team.h> 23 24 #include <tty.h> 25 26 27 //#define TTY_TRACE 28 #ifdef TTY_TRACE 29 # define TRACE(x) dprintf x 30 #else 31 # define TRACE(x) ; 32 #endif 33 34 35 /* 36 Locking 37 ------- 38 39 There are three locks involved. If more than one needs to be held at a 40 time, they must be acquired in the order they are listed here. 41 42 gTTYCookieLock: Guards the access to the fields 43 tty_cookie::{thread_count,closed}, or more precisely makes access to them 44 atomic. thread_count is the number of threads currently using the cookie 45 (i.e. read(), write(), ioctl() operations in progress). Together with 46 blocking_semaphore this serves the purpose to make sure that all pending 47 operations are done at a certain point when closing a cookie 48 (cf. tty_close_cookie() and TTYReference). 49 50 tty::lock: Guards the access to tty::{input_buffer,settings::{termios, 51 window_size,pgrp_id}}. Moreover when held guarantees that tty::open_count 52 won't drop to zero. A tty and the tty connected to it (master and slave) 53 share the same lock. 54 55 gTTYRequestLock: Guards access to tty::{reader,writer}_queue (most 56 RequestQueue methods do the locking themselves (the lock is a 57 recursive_lock)), queued Requests and associated RequestOwners. 58 59 60 Reading/Writing 61 --------------- 62 63 Most of the dirty work when dealing with reading/writing is done by the 64 {Reader,Writer}Locker classes. Upon construction they lock the tty, 65 (tty::lock) create a RequestOwner and queue Requests in the respective 66 reader/writer queues (tty::{reader,writer}_queue). The 67 Acquire{Reader,Writer}() methods need to be called before being allowed to 68 read/write. They ensure that there is actually something to read/space for 69 writing -- in blocking mode they wait, if necessary. When destroyed the 70 {Reader,Writer}Locker() remove the formerly enqueued Requests and notify 71 waiting reader/writer and/or send out select events, whatever is appropiate. 72 73 Acquire{Reader,Writer}() never return without an actual event being 74 occurred. Either an error has occurred (return value) -- in this case the 75 caller should terminate -- or bytes are available for reading/space for 76 writing (cf. AvailableBytes()). 77 */ 78 79 80 static void tty_notify_select_event(struct tty* tty, uint8 event); 81 static void tty_notify_if_available(struct tty* tty, struct tty* otherTTY, 82 bool notifySelect); 83 84 85 class AbstractLocker { 86 public: 87 AbstractLocker(tty_cookie* cookie) 88 : 89 fCookie(cookie), 90 fBytes(0) 91 { 92 } 93 94 size_t AvailableBytes() const 95 { return fBytes; } 96 97 protected: 98 void Lock() 99 { recursive_lock_lock(fCookie->tty->lock); } 100 void Unlock() 101 { recursive_lock_unlock(fCookie->tty->lock); } 102 103 tty_cookie* fCookie; 104 size_t fBytes; 105 }; 106 107 108 class WriterLocker : public AbstractLocker { 109 public: 110 WriterLocker(tty_cookie* sourceCookie); 111 ~WriterLocker(); 112 113 status_t AcquireWriter(bool dontBlock, 114 size_t bytesNeeded); 115 116 private: 117 size_t _CheckAvailableBytes() const; 118 status_t _CheckBackgroundWrite() const; 119 120 struct tty* fSource; 121 struct tty* fTarget; 122 RequestOwner fRequestOwner; 123 bool fEcho; 124 }; 125 126 127 class ReaderLocker : public AbstractLocker { 128 public: 129 ReaderLocker(tty_cookie* cookie); 130 ~ReaderLocker(); 131 132 status_t AcquireReader(bigtime_t timeout, 133 size_t bytesNeeded); 134 135 private: 136 size_t _CheckAvailableBytes() const; 137 status_t _CheckBackgroundRead() const; 138 139 struct tty* fTTY; 140 RequestOwner fRequestOwner; 141 }; 142 143 144 class TTYReferenceLocking { 145 public: 146 inline bool Lock(tty_cookie* cookie) 147 { 148 MutexLocker _(gTTYCookieLock); 149 150 if (cookie->closed) 151 return false; 152 153 cookie->thread_count++; 154 155 return true; 156 } 157 158 inline void Unlock(tty_cookie* cookie) 159 { 160 MutexLocker locker(gTTYCookieLock); 161 162 sem_id semaphore = -1; 163 if (--cookie->thread_count == 0 && cookie->closed) 164 semaphore = cookie->blocking_semaphore; 165 166 locker.Unlock(); 167 168 if (semaphore >= 0) { 169 TRACE(("TTYReference: cookie %p closed, last operation done, " 170 "releasing blocking sem %" B_PRId32 "\n", cookie, semaphore)); 171 172 release_sem(semaphore); 173 } 174 } 175 }; 176 177 typedef AutoLocker<tty_cookie, TTYReferenceLocking> TTYReference; 178 179 180 // #pragma mark - 181 182 183 Request::Request() 184 : 185 fOwner(NULL), 186 fCookie(NULL), 187 fBytesNeeded(0), 188 fNotified(false), 189 fError(false) 190 { 191 } 192 193 194 void 195 Request::Init(RequestOwner* owner, tty_cookie* cookie, size_t bytesNeeded) 196 { 197 fOwner = owner; 198 fCookie = cookie; 199 fBytesNeeded = bytesNeeded; 200 fNotified = false; 201 fError = false; 202 } 203 204 205 void 206 Request::Notify(size_t bytesAvailable) 207 { 208 if (!fNotified && bytesAvailable >= fBytesNeeded && fOwner) { 209 fOwner->Notify(this); 210 fNotified = true; 211 } 212 } 213 214 215 void 216 Request::NotifyError(status_t error) 217 { 218 if (!fError && fOwner) { 219 fOwner->NotifyError(this, error); 220 fError = true; 221 fNotified = true; 222 } 223 } 224 225 226 void 227 Request::Dump(const char* prefix) 228 { 229 kprintf("%srequest: %p\n", prefix, this); 230 kprintf("%s owner: %p\n", prefix, fOwner); 231 kprintf("%s cookie: %p\n", prefix, fCookie); 232 kprintf("%s bytes needed: %lu\n", prefix, fBytesNeeded); 233 kprintf("%s notified: %s\n", prefix, fNotified ? "true" : "false"); 234 kprintf("%s error: %s\n", prefix, fError ? "true" : "false"); 235 } 236 237 238 // #pragma mark - 239 240 241 RequestQueue::RequestQueue() 242 : 243 fRequests() 244 { 245 } 246 247 248 void 249 RequestQueue::Add(Request* request) 250 { 251 if (request) { 252 RecursiveLocker _(gTTYRequestLock); 253 254 fRequests.Add(request, true); 255 } 256 } 257 258 259 void 260 RequestQueue::Remove(Request* request) 261 { 262 if (request) { 263 RecursiveLocker _(gTTYRequestLock); 264 265 fRequests.Remove(request); 266 } 267 } 268 269 270 void 271 RequestQueue::NotifyFirst(size_t bytesAvailable) 272 { 273 RecursiveLocker _(gTTYRequestLock); 274 275 if (Request* first = First()) 276 first->Notify(bytesAvailable); 277 } 278 279 280 void 281 RequestQueue::NotifyError(status_t error) 282 { 283 RecursiveLocker _(gTTYRequestLock); 284 285 for (RequestList::Iterator it = fRequests.GetIterator(); it.HasNext();) { 286 Request* request = it.Next(); 287 request->NotifyError(error); 288 } 289 } 290 291 292 void 293 RequestQueue::NotifyError(tty_cookie* cookie, status_t error) 294 { 295 RecursiveLocker _(gTTYRequestLock); 296 297 for (RequestList::Iterator it = fRequests.GetIterator(); it.HasNext();) { 298 Request* request = it.Next(); 299 if (request->TTYCookie() == cookie) 300 request->NotifyError(error); 301 } 302 } 303 304 305 void 306 RequestQueue::Dump(const char* prefix) 307 { 308 RequestList::Iterator it = fRequests.GetIterator(); 309 while (Request* request = it.Next()) 310 request->Dump(prefix); 311 } 312 313 314 // #pragma mark - 315 316 317 RequestOwner::RequestOwner() 318 : 319 fConditionVariable(NULL), 320 fCookie(NULL), 321 fError(B_OK), 322 fBytesNeeded(1) 323 { 324 fRequestQueues[0] = NULL; 325 fRequestQueues[1] = NULL; 326 } 327 328 329 /*! The caller must already hold the request lock. 330 */ 331 void 332 RequestOwner::Enqueue(tty_cookie* cookie, RequestQueue* queue1, 333 RequestQueue* queue2) 334 { 335 TRACE(("%p->RequestOwner::Enqueue(%p, %p, %p)\n", this, cookie, queue1, 336 queue2)); 337 338 fCookie = cookie; 339 340 fRequestQueues[0] = queue1; 341 fRequestQueues[1] = queue2; 342 343 fRequests[0].Init(this, cookie, fBytesNeeded); 344 if (queue1) 345 queue1->Add(&fRequests[0]); 346 else 347 fRequests[0].Notify(fBytesNeeded); 348 349 fRequests[1].Init(this, cookie, fBytesNeeded); 350 if (queue2) 351 queue2->Add(&fRequests[1]); 352 else 353 fRequests[1].Notify(fBytesNeeded); 354 } 355 356 357 /*! The caller must already hold the request lock. 358 */ 359 void 360 RequestOwner::Dequeue() 361 { 362 TRACE(("%p->RequestOwner::Dequeue()\n", this)); 363 364 if (fRequestQueues[0]) 365 fRequestQueues[0]->Remove(&fRequests[0]); 366 if (fRequestQueues[1]) 367 fRequestQueues[1]->Remove(&fRequests[1]); 368 369 fRequestQueues[0] = NULL; 370 fRequestQueues[1] = NULL; 371 } 372 373 374 void 375 RequestOwner::SetBytesNeeded(size_t bytesNeeded) 376 { 377 if (fRequestQueues[0]) 378 fRequests[0].Init(this, fCookie, bytesNeeded); 379 380 if (fRequestQueues[1]) 381 fRequests[1].Init(this, fCookie, bytesNeeded); 382 } 383 384 385 /*! The request lock MUST NOT be held! 386 */ 387 status_t 388 RequestOwner::Wait(bool interruptable, bigtime_t timeout) 389 { 390 TRACE(("%p->RequestOwner::Wait(%d)\n", this, interruptable)); 391 392 status_t error = B_OK; 393 394 RecursiveLocker locker(gTTYRequestLock); 395 396 // check, if already done 397 if (fError == B_OK 398 && (!fRequests[0].WasNotified() || !fRequests[1].WasNotified())) { 399 // not yet done 400 401 // publish the condition variable 402 ConditionVariable conditionVariable; 403 conditionVariable.Init(this, "tty request"); 404 fConditionVariable = &conditionVariable; 405 406 // add an entry to wait on 407 ConditionVariableEntry entry; 408 conditionVariable.Add(&entry); 409 410 locker.Unlock(); 411 412 // wait 413 TRACE(("%p->RequestOwner::Wait(): waiting for condition...\n", this)); 414 415 error = entry.Wait( 416 (interruptable ? B_CAN_INTERRUPT : 0) | B_RELATIVE_TIMEOUT, 417 timeout); 418 419 TRACE(("%p->RequestOwner::Wait(): condition occurred: %" B_PRIx32 "\n", 420 this, error)); 421 422 // remove the condition variable 423 locker.Lock(); 424 fConditionVariable = NULL; 425 } 426 427 // get the result 428 if (error == B_OK) 429 error = fError; 430 431 return error; 432 } 433 434 435 bool 436 RequestOwner::IsFirstInQueues() 437 { 438 RecursiveLocker locker(gTTYRequestLock); 439 440 for (int i = 0; i < 2; i++) { 441 if (fRequestQueues[i] && fRequestQueues[i]->First() != &fRequests[i]) 442 return false; 443 } 444 445 return true; 446 } 447 448 449 void 450 RequestOwner::Notify(Request* request) 451 { 452 TRACE(("%p->RequestOwner::Notify(%p)\n", this, request)); 453 454 if (fError == B_OK && !request->WasNotified()) { 455 bool notify = false; 456 457 if (&fRequests[0] == request) { 458 notify = fRequests[1].WasNotified(); 459 } else if (&fRequests[1] == request) { 460 notify = fRequests[0].WasNotified(); 461 } else { 462 // spurious call 463 } 464 465 if (notify && fConditionVariable) 466 fConditionVariable->NotifyOne(); 467 } 468 } 469 470 471 void 472 RequestOwner::NotifyError(Request* request, status_t error) 473 { 474 TRACE(("%p->RequestOwner::NotifyError(%p, %" B_PRIx32 ")\n", this, request, 475 error)); 476 477 if (fError == B_OK) { 478 fError = error; 479 480 if (!fRequests[0].WasNotified() || !fRequests[1].WasNotified()) { 481 if (fConditionVariable) 482 fConditionVariable->NotifyOne(); 483 } 484 } 485 } 486 487 488 // #pragma mark - 489 490 491 WriterLocker::WriterLocker(tty_cookie* sourceCookie) 492 : 493 AbstractLocker(sourceCookie), 494 fSource(fCookie->tty), 495 fTarget(fCookie->other_tty), 496 fRequestOwner(), 497 fEcho(false) 498 { 499 Lock(); 500 501 // Now that the tty pair is locked, we can check, whether the target is 502 // open at all. 503 if (fTarget->open_count > 0) { 504 // The target tty is open. As soon as we have appended a request to 505 // the writer queue of the target, it is guaranteed to remain valid 506 // until we have removed the request (and notified the 507 // tty_close_cookie() pseudo request). 508 509 // get the echo mode 510 fEcho = (fSource->is_master 511 && fSource->settings->termios.c_lflag & ECHO) != 0; 512 513 // enqueue ourselves in the respective request queues 514 RecursiveLocker locker(gTTYRequestLock); 515 fRequestOwner.Enqueue(fCookie, &fTarget->writer_queue, 516 (fEcho ? &fSource->writer_queue : NULL)); 517 } else { 518 // target is not open: we set it to NULL; all further operations on 519 // this locker will fail 520 fTarget = NULL; 521 } 522 } 523 524 525 WriterLocker::~WriterLocker() 526 { 527 // dequeue from request queues 528 RecursiveLocker locker(gTTYRequestLock); 529 fRequestOwner.Dequeue(); 530 531 // check the tty queues and notify the next in line, and send out select 532 // events 533 if (fTarget) 534 tty_notify_if_available(fTarget, fSource, true); 535 if (fEcho) 536 tty_notify_if_available(fSource, fTarget, true); 537 538 locker.Unlock(); 539 540 Unlock(); 541 } 542 543 544 size_t 545 WriterLocker::_CheckAvailableBytes() const 546 { 547 size_t writable = line_buffer_writable(fTarget->input_buffer); 548 if (fEcho) { 549 // we can only write as much as is available on both ends 550 size_t locallyWritable = line_buffer_writable(fSource->input_buffer); 551 if (locallyWritable < writable) 552 writable = locallyWritable; 553 } 554 return writable; 555 } 556 557 558 status_t 559 WriterLocker::AcquireWriter(bool dontBlock, size_t bytesNeeded) 560 { 561 if (!fTarget) 562 return B_FILE_ERROR; 563 if (fEcho && fCookie->closed) 564 return B_FILE_ERROR; 565 566 RecursiveLocker requestLocker(gTTYRequestLock); 567 568 // check, if we're first in queue, and if there is space to write 569 if (fRequestOwner.IsFirstInQueues()) { 570 fBytes = _CheckAvailableBytes(); 571 if (fBytes >= bytesNeeded) 572 return B_OK; 573 } 574 575 // We are not the first in queue or currently there's no space to write: 576 // bail out, if we shall not block. 577 if (dontBlock) 578 return B_WOULD_BLOCK; 579 580 // set the number of bytes we need and notify, just in case we're first in 581 // one of the queues (RequestOwner::SetBytesNeeded() resets the notification 582 // state) 583 fRequestOwner.SetBytesNeeded(bytesNeeded); 584 if (fTarget) 585 tty_notify_if_available(fTarget, fSource, false); 586 if (fEcho) 587 tty_notify_if_available(fSource, fTarget, false); 588 589 requestLocker.Unlock(); 590 591 // block until something happens 592 Unlock(); 593 status_t status = fRequestOwner.Wait(true); 594 Lock(); 595 596 // RequestOwner::Wait() returns the error, but to avoid a race condition 597 // when closing a tty, we re-get the error with the tty lock being held. 598 if (status == B_OK) { 599 RecursiveLocker _(gTTYRequestLock); 600 status = fRequestOwner.Error(); 601 } 602 603 if (status == B_OK) 604 status = _CheckBackgroundWrite(); 605 606 if (status == B_OK) 607 fBytes = _CheckAvailableBytes(); 608 609 return status; 610 } 611 612 613 status_t 614 WriterLocker::_CheckBackgroundWrite() const 615 { 616 // only relevant for the slave end and only when TOSTOP is set 617 if (fSource->is_master 618 || (fSource->settings->termios.c_lflag & TOSTOP) == 0) { 619 return B_OK; 620 } 621 622 pid_t processGroup = getpgid(0); 623 if (fSource->settings->pgrp_id != 0 624 && processGroup != fSource->settings->pgrp_id) { 625 if (team_get_controlling_tty() == fSource) 626 send_signal(-processGroup, SIGTTOU); 627 } 628 629 return B_OK; 630 } 631 632 633 // #pragma mark - 634 635 636 ReaderLocker::ReaderLocker(tty_cookie* cookie) 637 : 638 AbstractLocker(cookie), 639 fTTY(cookie->tty), 640 fRequestOwner() 641 { 642 Lock(); 643 644 // enqueue ourselves in the reader request queue 645 RecursiveLocker locker(gTTYRequestLock); 646 fRequestOwner.Enqueue(fCookie, &fTTY->reader_queue); 647 } 648 649 650 ReaderLocker::~ReaderLocker() 651 { 652 // dequeue from reader request queue 653 RecursiveLocker locker(gTTYRequestLock); 654 fRequestOwner.Dequeue(); 655 656 // check the tty queues and notify the next in line, and send out select 657 // events 658 struct tty* otherTTY = fCookie->other_tty; 659 tty_notify_if_available(fTTY, (otherTTY->open_count > 0 ? otherTTY : NULL), 660 true); 661 662 locker.Unlock(); 663 664 Unlock(); 665 } 666 667 668 status_t 669 ReaderLocker::AcquireReader(bigtime_t timeout, size_t bytesNeeded) 670 { 671 if (fCookie->closed) 672 return B_FILE_ERROR; 673 674 status_t status = _CheckBackgroundRead(); 675 if (status != B_OK) 676 return status; 677 678 // check, if we're first in queue, and if there is something to read 679 if (fRequestOwner.IsFirstInQueues()) { 680 fBytes = _CheckAvailableBytes(); 681 if (fBytes >= bytesNeeded) 682 return B_OK; 683 } 684 685 if (fCookie->other_tty->open_count == 0 686 && fCookie->other_tty->opened_count > 0) { 687 TRACE(("ReaderLocker::AcquireReader() opened_count %" B_PRId32 "\n", 688 fCookie->other_tty->opened_count)); 689 return B_FILE_ERROR; 690 } 691 692 // We are not the first in queue or currently there's nothing to read: 693 // bail out, if we shall not block. 694 if (timeout <= 0) 695 return B_WOULD_BLOCK; 696 697 // reset the number of bytes we need 698 fRequestOwner.SetBytesNeeded(bytesNeeded); 699 700 // block until something happens 701 Unlock(); 702 status = fRequestOwner.Wait(true, timeout); 703 Lock(); 704 705 if (status == B_OK) 706 status = _CheckBackgroundRead(); 707 708 fBytes = _CheckAvailableBytes(); 709 710 TRACE(("ReaderLocker::AcquireReader() ended status 0x%" B_PRIx32 "\n", 711 status)); 712 713 return status; 714 } 715 716 717 size_t 718 ReaderLocker::_CheckAvailableBytes() const 719 { 720 // Reading from the slave with canonical input processing enabled means 721 // that we read at max until hitting a line end or EOF. 722 if (!fTTY->is_master && (fTTY->settings->termios.c_lflag & ICANON) != 0) { 723 return line_buffer_readable_line(fTTY->input_buffer, 724 fTTY->settings->termios.c_cc[VEOL], 725 fTTY->settings->termios.c_cc[VEOF]); 726 } 727 728 return line_buffer_readable(fTTY->input_buffer); 729 } 730 731 732 status_t 733 ReaderLocker::_CheckBackgroundRead() const 734 { 735 // only relevant for the slave end 736 if (fTTY->is_master) 737 return B_OK; 738 739 pid_t processGroup = getpgid(0); 740 if (fTTY->settings->pgrp_id != 0 741 && processGroup != fTTY->settings->pgrp_id) { 742 if (team_get_controlling_tty() == fTTY) 743 send_signal(-processGroup, SIGTTIN); 744 } 745 746 return B_OK; 747 } 748 749 750 // #pragma mark - 751 752 753 static void 754 reset_termios(struct termios& termios) 755 { 756 memset(&termios, 0, sizeof(struct termios)); 757 758 termios.c_iflag = ICRNL; 759 termios.c_oflag = OPOST | ONLCR; 760 termios.c_cflag = B19200 | CS8 | CREAD | HUPCL; 761 // enable receiver, hang up on last close 762 termios.c_lflag = ECHO | ISIG | ICANON; 763 termios.c_ispeed = B19200; 764 termios.c_ospeed = B19200; 765 766 // control characters 767 termios.c_cc[VINTR] = CTRL('C'); 768 termios.c_cc[VQUIT] = CTRL('\\'); 769 termios.c_cc[VERASE] = 0x7f; 770 termios.c_cc[VKILL] = CTRL('U'); 771 termios.c_cc[VEOF] = CTRL('D'); 772 termios.c_cc[VEOL] = '\0'; 773 termios.c_cc[VEOL2] = '\0'; 774 termios.c_cc[VSTART] = CTRL('S'); 775 termios.c_cc[VSTOP] = CTRL('Q'); 776 termios.c_cc[VSUSP] = CTRL('Z'); 777 } 778 779 780 static void 781 reset_tty_settings(tty_settings& settings) 782 { 783 reset_termios(settings.termios); 784 785 settings.pgrp_id = 0; 786 // this value prevents any signal of being sent 787 settings.session_id = -1; 788 789 // some initial window size - the TTY in question should set these values 790 settings.window_size.ws_col = 80; 791 settings.window_size.ws_row = 25; 792 settings.window_size.ws_xpixel = settings.window_size.ws_col * 8; 793 settings.window_size.ws_ypixel = settings.window_size.ws_row * 8; 794 } 795 796 797 /*! Processes the input character and puts it into the TTY's input buffer. 798 Depending on the termios flags set, signals may be sent, the input 799 character changed or removed, etc. 800 */ 801 static void 802 tty_input_putc_locked(struct tty* tty, int c) 803 { 804 const termios& termios = tty->settings->termios; 805 806 // process signals if needed 807 if ((termios.c_lflag & ISIG) != 0) { 808 // enable signals, process INTR, QUIT, and SUSP 809 int signal = -1; 810 811 if (c == termios.c_cc[VINTR]) 812 signal = SIGINT; 813 else if (c == termios.c_cc[VQUIT]) 814 signal = SIGQUIT; 815 else if (c == termios.c_cc[VSUSP]) 816 signal = SIGTSTP; 817 818 // do we need to deliver a signal? 819 if (signal != -1) { 820 // we may have to flush the input buffer 821 if ((termios.c_lflag & NOFLSH) == 0) 822 clear_line_buffer(tty->input_buffer); 823 824 if (tty->settings->pgrp_id != 0) 825 send_signal(-tty->settings->pgrp_id, signal); 826 return; 827 } 828 } 829 830 // process special canonical input characters 831 if ((termios.c_lflag & ICANON) != 0) { 832 // canonical mode, process ERASE and KILL 833 const cc_t* controlChars = termios.c_cc; 834 835 if (c == controlChars[VERASE]) { 836 // erase one character 837 char lastChar; 838 if (line_buffer_tail_getc(tty->input_buffer, &lastChar)) { 839 if (lastChar == controlChars[VEOF] 840 || lastChar == controlChars[VEOL] 841 || lastChar == '\n' || lastChar == '\r') { 842 // EOF or end of line -- put it back 843 line_buffer_putc(tty->input_buffer, lastChar); 844 } 845 } 846 return; 847 } else if (c == controlChars[VKILL]) { 848 // erase line 849 char lastChar; 850 while (line_buffer_tail_getc(tty->input_buffer, &lastChar)) { 851 if (lastChar == controlChars[VEOF] 852 || lastChar == controlChars[VEOL] 853 || lastChar == '\n' || lastChar == '\r') { 854 // EOF or end of line -- put it back 855 line_buffer_putc(tty->input_buffer, lastChar); 856 break; 857 } 858 } 859 return; 860 } else if (c == controlChars[VEOF]) { 861 // we still write the EOF to the stream -- tty_input_read() needs 862 // to recognize it 863 tty->pending_eof++; 864 } 865 } 866 867 // Input character conversions have already been done. What reaches this 868 // point can directly be written to the line buffer. 869 870 line_buffer_putc(tty->input_buffer, c); 871 } 872 873 874 static int32 875 tty_readable(struct tty* tty) 876 { 877 if (!tty->is_master && (tty->settings->termios.c_lflag & ICANON) != 0) { 878 return line_buffer_readable_line(tty->input_buffer, 879 tty->settings->termios.c_cc[VEOL], 880 tty->settings->termios.c_cc[VEOF]); 881 } 882 883 return line_buffer_readable(tty->input_buffer); 884 } 885 886 887 /** \brief Notify anyone waiting on events for this TTY. 888 * 889 * The TTY lock must be held. 890 * 891 * Otherwise, the select_pool may be modified or deleted (which happens automatically when the last 892 * item is removed from it). 893 */ 894 static void 895 tty_notify_select_event(struct tty* tty, uint8 event) 896 { 897 TRACE(("tty_notify_select_event(%p, %u)\n", tty, event)); 898 899 if (tty->select_pool) 900 notify_select_event_pool(tty->select_pool, event); 901 } 902 903 904 /*! \brief Checks whether bytes can be read from/written to the line buffer of 905 the given TTY and notifies the respective queues. 906 907 Also sends out \c B_SELECT_READ and \c B_SELECT_WRITE events as needed. 908 909 The TTY and the request lock must be held. 910 911 \param tty The TTY. 912 \param otherTTY The connected TTY. 913 */ 914 static void 915 tty_notify_if_available(struct tty* tty, struct tty* otherTTY, 916 bool notifySelect) 917 { 918 if (!tty) 919 return; 920 921 // Check, if something is readable (depending on whether canonical input 922 // processing is enabled). 923 int32 readable = tty_readable(tty); 924 if (readable > 0) { 925 // if nobody is waiting send select events, otherwise notify the waiter 926 if (!tty->reader_queue.IsEmpty()) 927 tty->reader_queue.NotifyFirst(readable); 928 else if (notifySelect) 929 tty_notify_select_event(tty, B_SELECT_READ); 930 } 931 932 int32 writable = line_buffer_writable(tty->input_buffer); 933 if (writable > 0) { 934 // if nobody is waiting send select events, otherwise notify the waiter 935 if (!tty->writer_queue.IsEmpty()) { 936 tty->writer_queue.NotifyFirst(writable); 937 } else if (notifySelect) { 938 if (otherTTY && otherTTY->open_count > 0) 939 tty_notify_select_event(otherTTY, B_SELECT_WRITE); 940 } 941 } 942 } 943 944 945 /*! \brief Performs input character conversion and writes the result to 946 \a buffer. 947 \param tty The master tty. 948 \param c The input character. 949 \param buffer The buffer to which to write the converted character. 950 \param _bytesNeeded The number of bytes needed in the target tty's 951 line buffer. 952 \return \c true, if the character shall be processed further, \c false, if 953 it shall be skipped. 954 */ 955 static bool 956 process_input_char(struct tty* tty, char c, char* buffer, 957 size_t* _bytesNeeded) 958 { 959 const termios& termios = tty->settings->termios; 960 tcflag_t flags = termios.c_iflag; 961 962 // signals 963 if (termios.c_lflag & ISIG) { 964 if (c == termios.c_cc[VINTR] 965 || c == termios.c_cc[VQUIT] 966 || c == termios.c_cc[VSUSP]) { 967 *buffer = c; 968 *_bytesNeeded = 0; 969 return true; 970 } 971 } 972 973 // canonical input characters 974 if (termios.c_lflag & ICANON) { 975 if (c == termios.c_cc[VERASE] 976 || c == termios.c_cc[VKILL]) { 977 *buffer = c; 978 *_bytesNeeded = 0; 979 return true; 980 } 981 } 982 983 // convert chars 984 if (c == '\r') { 985 if (flags & IGNCR) // ignore CR 986 return false; 987 if (flags & ICRNL) // CR -> NL 988 c = '\n'; 989 } else if (c == '\n') { 990 if (flags & INLCR) // NL -> CR 991 c = '\r'; 992 } else if ((flags & ISTRIP) != 0) // strip off eighth bit 993 c &= 0x7f; 994 995 *buffer = c; 996 *_bytesNeeded = 1; 997 return true; 998 } 999 1000 1001 /*! \brief Performs output character conversion and writes the result to 1002 \a buffer. 1003 \param tty The master tty. 1004 \param c The output character. 1005 \param buffer The buffer to which to write the converted character(s). 1006 \param _bytesWritten The number of bytes written to the output buffer 1007 (max 3). 1008 \param echoed \c true if the output char to be processed has been echoed 1009 from the input. 1010 */ 1011 static void 1012 process_output_char(struct tty* tty, char c, char* buffer, 1013 size_t* _bytesWritten, bool echoed) 1014 { 1015 const termios& termios = tty->settings->termios; 1016 tcflag_t flags = termios.c_oflag; 1017 1018 if (flags & OPOST) { 1019 if (echoed && c == termios.c_cc[VERASE]) { 1020 if (termios.c_lflag & ECHOE) { 1021 // ERASE -> BS SPACE BS 1022 buffer[0] = CTRL('H'); 1023 buffer[1] = ' '; 1024 buffer[2] = CTRL('H');; 1025 *_bytesWritten = 3; 1026 return; 1027 } 1028 } else if (echoed && c == termios.c_cc[VKILL]) { 1029 if (!(termios.c_lflag & ECHOK)) { 1030 // don't echo KILL 1031 *_bytesWritten = 0; 1032 return; 1033 } 1034 } else if (echoed && c == termios.c_cc[VEOF]) { 1035 // don't echo EOF 1036 *_bytesWritten = 0; 1037 return; 1038 } else if (c == '\n') { 1039 if (echoed && !(termios.c_lflag & ECHONL)) { 1040 // don't echo NL 1041 *_bytesWritten = 0; 1042 return; 1043 } 1044 if (flags & ONLCR) { // NL -> CR-NL 1045 buffer[0] = '\r'; 1046 buffer[1] = '\n'; 1047 *_bytesWritten = 2; 1048 return; 1049 } 1050 } else if (c == '\r') { 1051 if (flags & OCRNL) { // CR -> NL 1052 c = '\n'; 1053 } else if (flags & ONLRET) { // NL also does RET, ignore CR 1054 *_bytesWritten = 0; 1055 return; 1056 } else if (flags & ONOCR) { // don't output CR at column 0 1057 // TODO: We can't decide that here. 1058 } 1059 } else { 1060 if (flags & OLCUC) // lower case -> upper case 1061 c = toupper(c); 1062 } 1063 } 1064 1065 *buffer = c; 1066 *_bytesWritten = 1; 1067 } 1068 1069 1070 static status_t 1071 tty_write_to_tty_master_unsafe(tty_cookie* sourceCookie, const char* data, 1072 size_t* _length) 1073 { 1074 struct tty* source = sourceCookie->tty; 1075 struct tty* target = sourceCookie->other_tty; 1076 size_t length = *_length; 1077 size_t bytesWritten = 0; 1078 uint32 mode = sourceCookie->open_mode; 1079 bool dontBlock = (mode & O_NONBLOCK) != 0; 1080 1081 // bail out, if source is already closed 1082 TTYReference sourceTTYReference(sourceCookie); 1083 if (!sourceTTYReference.IsLocked()) 1084 return B_FILE_ERROR; 1085 1086 if (length == 0) 1087 return B_OK; 1088 1089 WriterLocker locker(sourceCookie); 1090 1091 // if the target is not open, fail now 1092 if (target->open_count <= 0) 1093 return B_FILE_ERROR; 1094 1095 bool echo = (source->settings->termios.c_lflag & ECHO) != 0; 1096 1097 TRACE(("tty_write_to_tty_master(source = %p, target = %p, " 1098 "length = %lu%s)\n", source, target, length, 1099 (echo ? ", echo mode" : ""))); 1100 1101 // Make sure we are first in the writer queue(s) and AvailableBytes() is 1102 // initialized. 1103 status_t status = locker.AcquireWriter(dontBlock, 0); 1104 if (status != B_OK) { 1105 *_length = 0; 1106 return status; 1107 } 1108 size_t writable = locker.AvailableBytes(); 1109 size_t writtenSinceLastNotify = 0; 1110 1111 while (bytesWritten < length) { 1112 // fetch next char and do input processing 1113 char c; 1114 size_t bytesNeeded; 1115 if (!process_input_char(source, *data, &c, &bytesNeeded)) { 1116 // input char shall be skipped 1117 data++; 1118 bytesWritten++; 1119 continue; 1120 } 1121 1122 // If in echo mode, we do the output conversion and need to update 1123 // the needed bytes count. 1124 char echoBuffer[3]; 1125 size_t echoBytes = 0; 1126 if (echo) { 1127 process_output_char(source, c, echoBuffer, &echoBytes, true); 1128 if (echoBytes > bytesNeeded) 1129 bytesNeeded = echoBytes; 1130 } 1131 1132 // If there's not enough space to write what we have, we need to wait 1133 // until it is available. 1134 if (writable < bytesNeeded) { 1135 if (writtenSinceLastNotify > 0) { 1136 tty_notify_if_available(target, source, true); 1137 if (echo) 1138 tty_notify_if_available(source, target, true); 1139 writtenSinceLastNotify = 0; 1140 } 1141 1142 status = locker.AcquireWriter(dontBlock, bytesNeeded); 1143 if (status != B_OK) { 1144 *_length = bytesWritten; 1145 return status; 1146 } 1147 1148 writable = locker.AvailableBytes(); 1149 1150 // XXX: do we need to support VMIN & VTIME for write() ? 1151 1152 // We need to restart the loop, since the termios flags might have 1153 // changed in the meantime (while we've unlocked the tty). Note, 1154 // that we don't re-get "echo" -- maybe we should. 1155 continue; 1156 } 1157 1158 // write the bytes 1159 tty_input_putc_locked(target, c); 1160 1161 if (echo) { 1162 for (size_t i = 0; i < echoBytes; i++) 1163 line_buffer_putc(source->input_buffer, echoBuffer[i]); 1164 } 1165 1166 writable -= bytesNeeded; 1167 data++; 1168 bytesWritten++; 1169 writtenSinceLastNotify++; 1170 } 1171 1172 return B_OK; 1173 } 1174 1175 1176 static status_t 1177 tty_write_to_tty_slave_unsafe(tty_cookie* sourceCookie, const char* data, 1178 size_t* _length) 1179 { 1180 struct tty* target = sourceCookie->other_tty; 1181 size_t length = *_length; 1182 size_t bytesWritten = 0; 1183 uint32 mode = sourceCookie->open_mode; 1184 bool dontBlock = (mode & O_NONBLOCK) != 0; 1185 1186 // bail out, if source is already closed 1187 TTYReference sourceTTYReference(sourceCookie); 1188 if (!sourceTTYReference.IsLocked()) 1189 return B_FILE_ERROR; 1190 1191 if (length == 0) 1192 return B_OK; 1193 1194 WriterLocker locker(sourceCookie); 1195 1196 // if the target is not open, fail now 1197 if (target->open_count <= 0) 1198 return B_FILE_ERROR; 1199 1200 TRACE(("tty_write_to_tty_slave(source = %p, target = %p, length = %lu)\n", 1201 sourceCookie->tty, target, length)); 1202 1203 // Make sure we are first in the writer queue(s) and AvailableBytes() is 1204 // initialized. 1205 status_t status = locker.AcquireWriter(dontBlock, 0); 1206 if (status != B_OK) { 1207 *_length = 0; 1208 return status; 1209 } 1210 size_t writable = locker.AvailableBytes(); 1211 size_t writtenSinceLastNotify = 0; 1212 1213 while (bytesWritten < length) { 1214 // fetch next char and do output processing 1215 char buffer[3]; 1216 size_t bytesNeeded; 1217 process_output_char(target, *data, buffer, &bytesNeeded, false); 1218 1219 // If there's not enough space to write what we have, we need to wait 1220 // until it is available. 1221 if (writable < bytesNeeded) { 1222 if (writtenSinceLastNotify > 0) { 1223 tty_notify_if_available(target, sourceCookie->tty, true); 1224 writtenSinceLastNotify = 0; 1225 } 1226 1227 status = locker.AcquireWriter(dontBlock, bytesNeeded); 1228 if (status != B_OK) { 1229 *_length = bytesWritten; 1230 return status; 1231 } 1232 1233 writable = locker.AvailableBytes(); 1234 1235 // We need to restart the loop, since the termios flags might have 1236 // changed in the meantime (while we've unlocked the tty). 1237 continue; 1238 } 1239 1240 // write the bytes 1241 for (size_t i = 0; i < bytesNeeded; i++) 1242 line_buffer_putc(target->input_buffer, buffer[i]); 1243 1244 writable -= bytesNeeded; 1245 data++; 1246 bytesWritten++; 1247 writtenSinceLastNotify++; 1248 } 1249 1250 return B_OK; 1251 } 1252 1253 1254 static status_t 1255 tty_write_to_tty_master(tty_cookie* sourceCookie, const void* _buffer, 1256 size_t* _length) 1257 { 1258 const char* buffer = (const char*)_buffer; 1259 size_t bytesRemaining = *_length; 1260 *_length = 0; 1261 1262 while (bytesRemaining > 0) { 1263 // copy data to stack 1264 char safeBuffer[256]; 1265 size_t toWrite = min_c(sizeof(safeBuffer), bytesRemaining); 1266 status_t error = user_memcpy(safeBuffer, buffer, toWrite); 1267 if (error != B_OK) 1268 return error; 1269 1270 // write them 1271 size_t written = toWrite; 1272 error = tty_write_to_tty_master_unsafe(sourceCookie, safeBuffer, 1273 &written); 1274 if (error != B_OK) 1275 return error; 1276 1277 buffer += written; 1278 bytesRemaining -= written; 1279 *_length += written; 1280 1281 if (written < toWrite) 1282 return B_OK; 1283 } 1284 1285 return B_OK; 1286 } 1287 1288 1289 static status_t 1290 tty_write_to_tty_slave(tty_cookie* sourceCookie, const void* _buffer, 1291 size_t* _length) 1292 { 1293 const char* buffer = (const char*)_buffer; 1294 size_t bytesRemaining = *_length; 1295 *_length = 0; 1296 1297 while (bytesRemaining > 0) { 1298 // copy data to stack 1299 char safeBuffer[256]; 1300 size_t toWrite = min_c(sizeof(safeBuffer), bytesRemaining); 1301 status_t error = user_memcpy(safeBuffer, buffer, toWrite); 1302 if (error != B_OK) 1303 return error; 1304 1305 // write them 1306 size_t written = toWrite; 1307 error = tty_write_to_tty_slave_unsafe(sourceCookie, safeBuffer, 1308 &written); 1309 if (error != B_OK) 1310 return error; 1311 1312 buffer += written; 1313 bytesRemaining -= written; 1314 *_length += written; 1315 1316 if (written < toWrite) 1317 return B_OK; 1318 } 1319 1320 return B_OK; 1321 } 1322 1323 1324 // #pragma mark - public API 1325 1326 1327 status_t 1328 tty_create(tty_service_func func, struct tty* master, struct tty** _tty) 1329 { 1330 struct tty* tty = new(std::nothrow) (struct tty); 1331 if (tty == NULL) 1332 return B_NO_MEMORY; 1333 1334 if (master == NULL) { 1335 tty->is_master = true; 1336 tty->lock = new(std::nothrow) recursive_lock; 1337 tty->settings = new(std::nothrow) tty_settings; 1338 if (tty->lock == NULL || tty->settings == NULL) { 1339 delete tty->lock; 1340 delete tty->settings; 1341 delete tty; 1342 return B_NO_MEMORY; 1343 } 1344 1345 recursive_lock_init(tty->lock, "tty lock"); 1346 reset_tty_settings(*tty->settings); 1347 } else { 1348 tty->is_master = false; 1349 tty->lock = master->lock; 1350 tty->settings = master->settings; 1351 } 1352 1353 tty->ref_count = 0; 1354 tty->open_count = 0; 1355 tty->opened_count = 0; 1356 tty->select_pool = NULL; 1357 tty->pending_eof = 0; 1358 tty->hardware_bits = 0; 1359 tty->is_exclusive = false; 1360 1361 status_t status = init_line_buffer(tty->input_buffer, TTY_BUFFER_SIZE); 1362 1363 if (status < B_OK) { 1364 if (tty->is_master) { 1365 recursive_lock_destroy(tty->lock); 1366 delete tty->lock; 1367 } 1368 delete tty; 1369 return status; 1370 } 1371 1372 tty->service_func = func; 1373 1374 *_tty = tty; 1375 1376 return B_OK; 1377 } 1378 1379 1380 void 1381 tty_destroy(struct tty* tty) 1382 { 1383 TRACE(("tty_destroy(%p)\n", tty)); 1384 uninit_line_buffer(tty->input_buffer); 1385 delete_select_sync_pool(tty->select_pool); 1386 if (tty->is_master) { 1387 recursive_lock_destroy(tty->lock); 1388 delete tty->lock; 1389 delete tty->settings; 1390 } 1391 1392 delete tty; 1393 } 1394 1395 1396 status_t 1397 tty_create_cookie(struct tty* tty, struct tty* otherTTY, uint32 openMode, tty_cookie** _cookie) 1398 { 1399 tty_cookie* cookie = new(std::nothrow) tty_cookie; 1400 if (cookie == NULL) 1401 return B_NO_MEMORY; 1402 1403 cookie->blocking_semaphore = create_sem(0, "wait for tty close"); 1404 if (cookie->blocking_semaphore < 0) { 1405 status_t status = cookie->blocking_semaphore; 1406 delete cookie; 1407 return status; 1408 } 1409 1410 cookie->tty = tty; 1411 cookie->other_tty = otherTTY; 1412 cookie->open_mode = openMode; 1413 cookie->thread_count = 0; 1414 cookie->closed = false; 1415 1416 1417 RecursiveLocker locker(cookie->tty->lock); 1418 1419 if (tty->is_exclusive && geteuid() != 0) { 1420 delete_sem(cookie->blocking_semaphore); 1421 delete cookie; 1422 return B_BUSY; 1423 } 1424 1425 // add to the TTY's cookie list 1426 tty->cookies.Add(cookie); 1427 tty->open_count++; 1428 tty->ref_count++; 1429 tty->opened_count++; 1430 1431 *_cookie = cookie; 1432 1433 return B_OK; 1434 } 1435 1436 1437 void 1438 tty_close_cookie(tty_cookie* cookie) 1439 { 1440 MutexLocker locker(gTTYCookieLock); 1441 1442 // Already closed? This can happen for slaves that have been closed when 1443 // the master was closed. 1444 if (cookie->closed) 1445 return; 1446 1447 // set the cookie's `closed' flag 1448 cookie->closed = true; 1449 bool unblock = (cookie->thread_count > 0); 1450 1451 // unblock blocking threads 1452 if (unblock) { 1453 cookie->tty->reader_queue.NotifyError(cookie, B_FILE_ERROR); 1454 cookie->tty->writer_queue.NotifyError(cookie, B_FILE_ERROR); 1455 1456 if (cookie->other_tty->open_count > 0) { 1457 cookie->other_tty->reader_queue.NotifyError(cookie, B_FILE_ERROR); 1458 cookie->other_tty->writer_queue.NotifyError(cookie, B_FILE_ERROR); 1459 } 1460 } 1461 1462 locker.Unlock(); 1463 1464 // wait till all blocking (and now unblocked) threads have left the 1465 // critical code 1466 if (unblock) { 1467 TRACE(("tty_close_cookie(): cookie %p, there're still pending " 1468 "operations, acquire blocking sem %" B_PRId32 "\n", cookie, 1469 cookie->blocking_semaphore)); 1470 1471 acquire_sem(cookie->blocking_semaphore); 1472 } 1473 1474 // For the removal of the cookie acquire the TTY's lock. This ensures, that 1475 // cookies will not be removed from a TTY (or added -- cf. add_tty_cookie()) 1476 // as long as the TTY's lock is being held. 1477 RecursiveLocker ttyLocker(cookie->tty->lock); 1478 1479 // remove the cookie from the TTY's cookie list 1480 cookie->tty->cookies.Remove(cookie); 1481 1482 // close the tty, if no longer used 1483 if (--cookie->tty->open_count == 0) { 1484 // The last cookie of this tty has been closed. We're going to close 1485 // the TTY and need to unblock all write requests before. There should 1486 // be no read requests, since only a cookie of this TTY issues those. 1487 // We do this by first notifying all queued requests of the error 1488 // condition. We then clear the line buffer for the TTY and queue 1489 // an own request. 1490 1491 // Notify the other TTY first; it doesn't accept any read/writes 1492 // while there is only one end. 1493 cookie->other_tty->reader_queue.NotifyError(B_FILE_ERROR); 1494 cookie->other_tty->writer_queue.NotifyError(B_FILE_ERROR); 1495 1496 RecursiveLocker requestLocker(gTTYRequestLock); 1497 1498 // we only need to do all this, if the writer queue is not empty 1499 if (!cookie->tty->writer_queue.IsEmpty()) { 1500 // notify the blocking writers 1501 cookie->tty->writer_queue.NotifyError(B_FILE_ERROR); 1502 1503 // enqueue our request 1504 RequestOwner requestOwner; 1505 requestOwner.Enqueue(cookie, &cookie->tty->writer_queue); 1506 1507 requestLocker.Unlock(); 1508 1509 // clear the line buffer 1510 clear_line_buffer(cookie->tty->input_buffer); 1511 1512 ttyLocker.Unlock(); 1513 1514 // wait for our turn 1515 requestOwner.Wait(false); 1516 1517 // re-lock 1518 ttyLocker.Lock(); 1519 requestLocker.Lock(); 1520 1521 // dequeue our request 1522 requestOwner.Dequeue(); 1523 } 1524 1525 requestLocker.Unlock(); 1526 1527 // notify a select write event on the other tty, if we've closed this tty 1528 if (cookie->other_tty->open_count > 0) 1529 tty_notify_select_event(cookie->other_tty, B_SELECT_WRITE); 1530 1531 cookie->tty->is_exclusive = false; 1532 } 1533 } 1534 1535 1536 void 1537 tty_destroy_cookie(tty_cookie* cookie) 1538 { 1539 RecursiveLocker locker(cookie->tty->lock); 1540 cookie->tty->ref_count--; 1541 locker.Unlock(); 1542 1543 if (cookie->blocking_semaphore >= 0) 1544 delete_sem(cookie->blocking_semaphore); 1545 1546 delete cookie; 1547 } 1548 1549 1550 status_t 1551 tty_read(tty_cookie* cookie, void* _buffer, size_t* _length) 1552 { 1553 char* buffer = (char*)_buffer; 1554 struct tty* tty = cookie->tty; 1555 uint32 mode = cookie->open_mode; 1556 bool dontBlock = (mode & O_NONBLOCK) != 0; 1557 size_t length = *_length; 1558 bool canon = true; 1559 bigtime_t timeout = dontBlock ? 0 : B_INFINITE_TIMEOUT; 1560 bigtime_t interCharTimeout = 0; 1561 size_t bytesNeeded = 1; 1562 1563 TRACE(("tty_input_read(tty = %p, length = %lu, mode = %" B_PRIu32 ")\n", 1564 tty, length, mode)); 1565 1566 if (length == 0) 1567 return B_OK; 1568 1569 // bail out, if the TTY is already closed 1570 TTYReference ttyReference(cookie); 1571 if (!ttyReference.IsLocked()) 1572 return B_FILE_ERROR; 1573 1574 ReaderLocker locker(cookie); 1575 1576 // handle raw mode 1577 if ((!tty->is_master) && ((tty->settings->termios.c_lflag & ICANON) == 0)) { 1578 canon = false; 1579 if (!dontBlock) { 1580 // Non-blocking mode. Handle VMIN and VTIME. 1581 bytesNeeded = tty->settings->termios.c_cc[VMIN]; 1582 bigtime_t vtime = tty->settings->termios.c_cc[VTIME] * 100000; 1583 TRACE(("tty_input_read: icanon vmin %lu, vtime %" B_PRIdBIGTIME 1584 "us\n", bytesNeeded, vtime)); 1585 1586 if (bytesNeeded == 0) { 1587 // In this case VTIME specifies a relative total timeout. We 1588 // have no inter-char timeout, though. 1589 timeout = vtime; 1590 } else { 1591 // VTIME specifies the inter-char timeout. 0 is indefinitely. 1592 if (vtime == 0) 1593 interCharTimeout = B_INFINITE_TIMEOUT; 1594 else 1595 interCharTimeout = vtime; 1596 1597 if (bytesNeeded > length) 1598 bytesNeeded = length; 1599 } 1600 } 1601 } 1602 1603 status_t status; 1604 *_length = 0; 1605 1606 do { 1607 TRACE(("tty_input_read: AcquireReader(%" B_PRIdBIGTIME "us, %ld)\n", 1608 timeout, bytesNeeded)); 1609 status = locker.AcquireReader(timeout, bytesNeeded); 1610 size_t toRead = locker.AvailableBytes(); 1611 if (status != B_OK && toRead == 0) { 1612 TRACE(("tty_input_read() AcquireReader failed\n")); 1613 break; 1614 } 1615 1616 if (toRead > length) 1617 toRead = length; 1618 1619 bool _hitEOF = false; 1620 bool* hitEOF = canon && tty->pending_eof > 0 ? &_hitEOF : NULL; 1621 1622 ssize_t bytesRead = line_buffer_user_read(tty->input_buffer, buffer, 1623 toRead, tty->settings->termios.c_cc[VEOF], hitEOF); 1624 if (bytesRead < 0) { 1625 status = bytesRead; 1626 break; 1627 } 1628 1629 buffer += bytesRead; 1630 length -= bytesRead; 1631 *_length += bytesRead; 1632 bytesNeeded = (size_t)bytesRead > bytesNeeded 1633 ? 0 : bytesNeeded - bytesRead; 1634 1635 // we hit an EOF char -- bail out, whatever amount of data we have 1636 if (hitEOF && *hitEOF) { 1637 tty->pending_eof--; 1638 break; 1639 } 1640 1641 // Once we have read something reset the timeout to the inter-char 1642 // timeout, if applicable. 1643 if (!dontBlock && !canon && *_length > 0) 1644 timeout = interCharTimeout; 1645 } while (bytesNeeded > 0); 1646 1647 if (status == B_WOULD_BLOCK || status == B_TIMED_OUT) { 1648 // In non-blocking non-canonical-input-processing mode never return 1649 // timeout errors. Just return 0, if nothing has been read. 1650 if (!dontBlock && !canon) 1651 status = B_OK; 1652 } 1653 1654 TRACE(("tty_input_read() status 0x%" B_PRIx32 "\n", status)); 1655 1656 return *_length == 0 ? status : B_OK; 1657 } 1658 1659 1660 status_t 1661 tty_write(tty_cookie* sourceCookie, const void* _buffer, size_t* _length) 1662 { 1663 if (sourceCookie->tty->is_master) 1664 return tty_write_to_tty_master(sourceCookie, _buffer, _length); 1665 1666 return tty_write_to_tty_slave(sourceCookie, _buffer, _length); 1667 } 1668 1669 1670 status_t 1671 tty_control(tty_cookie* cookie, uint32 op, void* buffer, size_t length) 1672 { 1673 struct tty* tty = cookie->tty; 1674 1675 // bail out, if already closed 1676 TTYReference ttyReference(cookie); 1677 if (!ttyReference.IsLocked()) 1678 return B_FILE_ERROR; 1679 1680 TRACE(("tty_ioctl: tty %p, op %" B_PRIu32 ", buffer %p, length %" 1681 B_PRIuSIZE "\n", tty, op, buffer, length)); 1682 RecursiveLocker locker(tty->lock); 1683 1684 switch (op) { 1685 // blocking/non-blocking mode 1686 1687 case B_SET_BLOCKING_IO: 1688 cookie->open_mode &= ~O_NONBLOCK; 1689 return B_OK; 1690 case B_SET_NONBLOCKING_IO: 1691 cookie->open_mode |= O_NONBLOCK; 1692 return B_OK; 1693 1694 // get and set TTY attributes 1695 1696 case TCGETA: 1697 TRACE(("tty: get attributes\n")); 1698 return user_memcpy(buffer, &tty->settings->termios, 1699 sizeof(struct termios)); 1700 1701 case TCSETA: 1702 case TCSETAW: 1703 case TCSETAF: 1704 { 1705 TRACE(("tty: set attributes (iflag = %" B_PRIx32 ", oflag = %" 1706 B_PRIx32 ", cflag = %" B_PRIx32 ", lflag = %" B_PRIx32 ")\n", 1707 tty->settings->termios.c_iflag, tty->settings->termios.c_oflag, 1708 tty->settings->termios.c_cflag, 1709 tty->settings->termios.c_lflag)); 1710 1711 status_t status = user_memcpy(&tty->settings->termios, buffer, 1712 sizeof(struct termios)); 1713 if (status != B_OK) 1714 return status; 1715 1716 tty->service_func(tty, TTYSETMODES, &tty->settings->termios, 1717 sizeof(struct termios)); 1718 return status; 1719 } 1720 1721 // get and set process group ID 1722 1723 case TIOCGPGRP: 1724 TRACE(("tty: get pgrp_id\n")); 1725 return user_memcpy(buffer, &tty->settings->pgrp_id, sizeof(pid_t)); 1726 1727 case TIOCSPGRP: 1728 { 1729 TRACE(("tty: set pgrp_id\n")); 1730 pid_t groupID; 1731 1732 if (user_memcpy(&groupID, buffer, sizeof(pid_t)) != B_OK) 1733 return B_BAD_ADDRESS; 1734 1735 status_t error = team_set_foreground_process_group(tty, 1736 groupID); 1737 if (error == B_OK) 1738 tty->settings->pgrp_id = groupID; 1739 return error; 1740 } 1741 1742 // become controlling TTY 1743 case TIOCSCTTY: 1744 { 1745 TRACE(("tty: become controlling tty\n")); 1746 pid_t processID = getpid(); 1747 pid_t sessionID = getsid(processID); 1748 // Only session leaders can become controlling tty 1749 if (processID != sessionID) 1750 return B_NOT_ALLOWED; 1751 // Check if already controlling tty 1752 if (team_get_controlling_tty() == tty) 1753 return B_OK; 1754 tty->settings->session_id = sessionID; 1755 tty->settings->pgrp_id = sessionID; 1756 team_set_controlling_tty(tty); 1757 return B_OK; 1758 } 1759 1760 // get session leader process group ID 1761 case TIOCGSID: 1762 { 1763 TRACE(("tty: get session_id\n")); 1764 return user_memcpy(buffer, &tty->settings->session_id, 1765 sizeof(pid_t)); 1766 } 1767 1768 // get and set window size 1769 1770 case TIOCGWINSZ: 1771 TRACE(("tty: get window size\n")); 1772 return user_memcpy(buffer, &tty->settings->window_size, 1773 sizeof(struct winsize)); 1774 1775 case TIOCSWINSZ: 1776 { 1777 uint16 oldColumns = tty->settings->window_size.ws_col; 1778 uint16 oldRows = tty->settings->window_size.ws_row; 1779 1780 TRACE(("tty: set window size\n")); 1781 if (user_memcpy(&tty->settings->window_size, buffer, 1782 sizeof(struct winsize)) < B_OK) { 1783 return B_BAD_ADDRESS; 1784 } 1785 1786 // send a signal only if the window size has changed 1787 if ((oldColumns != tty->settings->window_size.ws_col 1788 || oldRows != tty->settings->window_size.ws_row) 1789 && tty->settings->pgrp_id != 0) { 1790 send_signal(-tty->settings->pgrp_id, SIGWINCH); 1791 } 1792 1793 return B_OK; 1794 } 1795 1796 case 'ichr': // BeOS (int*) (pre- select() support) 1797 { 1798 int wanted; 1799 int toRead; 1800 1801 // help identify apps using it 1802 //dprintf("tty: warning: legacy BeOS opcode 'ichr'\n"); 1803 1804 if (user_memcpy(&wanted, buffer, sizeof(int)) != B_OK) 1805 return B_BAD_ADDRESS; 1806 1807 // release the mutex and grab a read lock 1808 locker.Unlock(); 1809 ReaderLocker readLocker(cookie); 1810 1811 bigtime_t timeout = wanted == 0 ? 0 : B_INFINITE_TIMEOUT; 1812 1813 // TODO: If wanted is > the TTY buffer size, this loop cannot work 1814 // correctly. Refactor the read code! 1815 do { 1816 status_t status = readLocker.AcquireReader(timeout, wanted); 1817 if (status != B_OK) 1818 return status; 1819 1820 toRead = readLocker.AvailableBytes(); 1821 } while (toRead < wanted); 1822 1823 if (user_memcpy(buffer, &toRead, sizeof(int)) != B_OK) 1824 return B_BAD_ADDRESS; 1825 1826 return B_OK; 1827 } 1828 1829 case FIONREAD: 1830 { 1831 int toRead = 0; 1832 1833 // release the mutex and grab a read lock 1834 locker.Unlock(); 1835 ReaderLocker readLocker(cookie); 1836 1837 status_t status = readLocker.AcquireReader(0, 1); 1838 if (status == B_OK) 1839 toRead = readLocker.AvailableBytes(); 1840 else if (status != B_WOULD_BLOCK) 1841 return status; 1842 1843 if (user_memcpy(buffer, &toRead, sizeof(int)) != B_OK) 1844 return B_BAD_ADDRESS; 1845 1846 return B_OK; 1847 } 1848 1849 case TIOCOUTQ: 1850 { 1851 int toWrite = 0; 1852 1853 // release the mutex and grab a write lock 1854 locker.Unlock(); 1855 WriterLocker writeLocker(cookie); 1856 1857 status_t status = writeLocker.AcquireWriter(0, 1); 1858 if (status == B_OK) 1859 toWrite = line_buffer_readable(tty->input_buffer); 1860 else if (status != B_WOULD_BLOCK) 1861 return status; 1862 1863 if (user_memcpy(buffer, &toWrite, sizeof(int)) != B_OK) 1864 return B_BAD_ADDRESS; 1865 1866 return B_OK; 1867 } 1868 1869 case TCXONC: // Unix, but even Linux doesn't handle it 1870 //dprintf("tty: unsupported TCXONC\n"); 1871 break; 1872 1873 case TCSETDTR: 1874 case TCSETRTS: 1875 case TIOCMSET: 1876 { 1877 // control line state setting, we only support DTR and RTS 1878 int value; 1879 if (user_memcpy(&value, buffer, sizeof(value)) != B_OK) 1880 return B_BAD_ADDRESS; 1881 1882 bool result = true; 1883 bool dtr = (op == TCSETDTR && value != 0) 1884 || (op == TIOCMSET && (value & TIOCM_DTR) != 0); 1885 if (op == TCSETDTR || op == TIOCMSET) 1886 result &= tty->service_func(tty, TTYSETDTR, &dtr, sizeof(dtr)); 1887 1888 bool rts = (op == TCSETRTS && value != 0) 1889 || (op == TIOCMSET && (value & TIOCM_RTS) != 0); 1890 if (op == TCSETRTS || op == TIOCMSET) 1891 result &= tty->service_func(tty, TTYSETRTS, &rts, sizeof(rts)); 1892 1893 return result ? B_OK : B_ERROR; 1894 } 1895 1896 case TCGETBITS: 1897 case TIOCMGET: 1898 { 1899 tty->service_func(tty, TTYGETSIGNALS, NULL, 0); 1900 int bits = tty->hardware_bits; 1901 return user_memcpy(buffer, &bits, sizeof(bits)); 1902 } 1903 1904 case TIOCMBIS: 1905 case TIOCMBIC: 1906 { 1907 // control line state setting, we only support DTR and RTS 1908 int value; 1909 if (user_memcpy(&value, buffer, sizeof(value)) != B_OK) 1910 return B_BAD_ADDRESS; 1911 1912 bool result = true; 1913 bool dtr = (op == TIOCMBIS); 1914 if (value & TIOCM_DTR) 1915 result &= tty->service_func(tty, TTYSETDTR, &dtr, sizeof(dtr)); 1916 1917 bool rts = (op == TIOCMBIS); 1918 if (value & TIOCM_RTS) 1919 result &= tty->service_func(tty, TTYSETRTS, &rts, sizeof(rts)); 1920 1921 return result ? B_OK : B_ERROR; 1922 } 1923 1924 case TIOCSBRK: 1925 case TIOCCBRK: 1926 case TCSBRK: 1927 { 1928 bool set; 1929 if (op == TIOCSBRK) 1930 set = true; 1931 else if (op == TIOCCBRK) 1932 set = false; 1933 else { 1934 int value = (int)(uintptr_t)buffer; 1935 set = value != 0; 1936 } 1937 1938 if (tty->service_func(tty, TTYSETBREAK, &set, sizeof(set))) 1939 return B_OK; 1940 1941 return B_ERROR; 1942 } 1943 1944 case TCFLSH: 1945 { 1946 int value = (int)(uintptr_t)buffer; 1947 if (value & TCOFLUSH) { 1948 struct tty* otherTTY = cookie->other_tty; 1949 if (otherTTY->open_count <= 0) 1950 return B_ERROR; 1951 1952 clear_line_buffer(otherTTY->input_buffer); 1953 } 1954 1955 if (value & TCIFLUSH) 1956 clear_line_buffer(tty->input_buffer); 1957 1958 if (tty->service_func(tty, TTYFLUSH, &value, sizeof(value))) 1959 return B_OK; 1960 1961 return B_ERROR; 1962 } 1963 1964 case TIOCEXCL: 1965 { 1966 tty->is_exclusive = true; 1967 return B_OK; 1968 } 1969 1970 case TIOCNXCL: 1971 { 1972 tty->is_exclusive = false; 1973 return B_OK; 1974 } 1975 } 1976 1977 TRACE(("tty: unsupported opcode %" B_PRIu32 "\n", op)); 1978 return B_BAD_VALUE; 1979 } 1980 1981 1982 status_t 1983 tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync) 1984 { 1985 struct tty* tty = cookie->tty; 1986 1987 TRACE(("tty_select(cookie = %p, event = %u, ref = %" B_PRIu32 ", sync = " 1988 "%p)\n", cookie, event, ref, sync)); 1989 1990 // we don't support all kinds of events 1991 if (event < B_SELECT_READ || event > B_SELECT_ERROR) 1992 return B_BAD_VALUE; 1993 1994 // if the TTY is already closed, we notify immediately 1995 TTYReference ttyReference(cookie); 1996 if (!ttyReference.IsLocked()) { 1997 TRACE(("tty_select() done: cookie %p already closed\n", cookie)); 1998 1999 notify_select_event(sync, event); 2000 return B_OK; 2001 } 2002 2003 // lock the TTY (allows us to freely access the cookie lists of this and 2004 // the other TTY) 2005 RecursiveLocker ttyLocker(tty->lock); 2006 2007 // get the other TTY -- needed for `write' events 2008 struct tty* otherTTY = cookie->other_tty; 2009 if (otherTTY->open_count <= 0) 2010 otherTTY = NULL; 2011 2012 // add the event to the TTY's pool 2013 status_t error = add_select_sync_pool_entry(&tty->select_pool, sync, event); 2014 if (error != B_OK) { 2015 TRACE(("tty_select() done: add_select_sync_pool_entry() failed: %" 2016 B_PRIx32 "\n", error)); 2017 2018 return error; 2019 } 2020 2021 // finally also acquire the request mutex, for access to the reader/writer 2022 // queues 2023 RecursiveLocker requestLocker(gTTYRequestLock); 2024 2025 // check, if the event is already present 2026 switch (event) { 2027 case B_SELECT_READ: 2028 if (tty->reader_queue.IsEmpty() && tty_readable(tty) > 0) 2029 notify_select_event(sync, event); 2030 break; 2031 2032 case B_SELECT_WRITE: 2033 { 2034 // writes go to the other TTY 2035 if (!otherTTY) { 2036 notify_select_event(sync, event); 2037 break; 2038 } 2039 2040 // In case input is echoed, we have to check, whether we can 2041 // currently can write to our TTY as well. 2042 bool echo = (tty->is_master 2043 && tty->settings->termios.c_lflag & ECHO); 2044 2045 if (otherTTY->writer_queue.IsEmpty() 2046 && line_buffer_writable(otherTTY->input_buffer) > 0) { 2047 if (!echo 2048 || (tty->writer_queue.IsEmpty() 2049 && line_buffer_writable(tty->input_buffer) > 0)) { 2050 notify_select_event(sync, event); 2051 } 2052 } 2053 break; 2054 } 2055 2056 case B_SELECT_ERROR: 2057 default: 2058 break; 2059 } 2060 2061 return B_OK; 2062 } 2063 2064 2065 status_t 2066 tty_deselect(tty_cookie* cookie, uint8 event, selectsync* sync) 2067 { 2068 struct tty* tty = cookie->tty; 2069 2070 TRACE(("tty_deselect(cookie = %p, event = %u, sync = %p)\n", cookie, event, 2071 sync)); 2072 2073 // we don't support all kinds of events 2074 if (event < B_SELECT_READ || event > B_SELECT_ERROR) 2075 return B_BAD_VALUE; 2076 2077 // lock the TTY (guards the select sync pool, among other things) 2078 RecursiveLocker ttyLocker(tty->lock); 2079 2080 return remove_select_sync_pool_entry(&tty->select_pool, sync, event); 2081 } 2082 2083 2084 status_t 2085 tty_hardware_signal(tty_cookie* cookie, int signal, bool set) 2086 { 2087 int bit = 0; 2088 2089 switch (signal) { 2090 case TTYHWDCD: 2091 bit = TCGB_DCD; 2092 break; 2093 case TTYHWCTS: 2094 bit = TCGB_CTS; 2095 break; 2096 case TTYHWDSR: 2097 bit = TCGB_DSR; 2098 break; 2099 case TTYHWRI: 2100 bit = TCGB_RI; 2101 break; 2102 2103 default: 2104 return B_BAD_VALUE; 2105 } 2106 2107 if (set) 2108 cookie->tty->hardware_bits |= bit; 2109 else 2110 cookie->tty->hardware_bits &= ~bit; 2111 2112 return B_ERROR; 2113 } 2114