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 ASSERT(tty->ref_count == 0); 1385 1386 uninit_line_buffer(tty->input_buffer); 1387 delete_select_sync_pool(tty->select_pool); 1388 if (tty->is_master) { 1389 recursive_lock_destroy(tty->lock); 1390 delete tty->lock; 1391 delete tty->settings; 1392 } 1393 1394 delete tty; 1395 } 1396 1397 1398 status_t 1399 tty_create_cookie(struct tty* tty, struct tty* otherTTY, uint32 openMode, tty_cookie** _cookie) 1400 { 1401 tty_cookie* cookie = new(std::nothrow) tty_cookie; 1402 if (cookie == NULL) 1403 return B_NO_MEMORY; 1404 1405 cookie->blocking_semaphore = create_sem(0, "wait for tty close"); 1406 if (cookie->blocking_semaphore < 0) { 1407 status_t status = cookie->blocking_semaphore; 1408 delete cookie; 1409 return status; 1410 } 1411 1412 cookie->tty = tty; 1413 cookie->other_tty = otherTTY; 1414 cookie->open_mode = openMode; 1415 cookie->thread_count = 0; 1416 cookie->closed = false; 1417 1418 1419 RecursiveLocker locker(cookie->tty->lock); 1420 1421 if (tty->is_exclusive && geteuid() != 0) { 1422 delete_sem(cookie->blocking_semaphore); 1423 delete cookie; 1424 return B_BUSY; 1425 } 1426 1427 // add to the TTY's cookie list 1428 tty->cookies.Add(cookie); 1429 tty->open_count++; 1430 tty->ref_count++; 1431 tty->opened_count++; 1432 1433 *_cookie = cookie; 1434 1435 return B_OK; 1436 } 1437 1438 1439 void 1440 tty_close_cookie(tty_cookie* cookie) 1441 { 1442 MutexLocker locker(gTTYCookieLock); 1443 1444 // Already closed? This can happen for slaves that have been closed when 1445 // the master was closed. 1446 if (cookie->closed) 1447 return; 1448 1449 // set the cookie's `closed' flag 1450 cookie->closed = true; 1451 bool unblock = (cookie->thread_count > 0); 1452 1453 // unblock blocking threads 1454 if (unblock) { 1455 cookie->tty->reader_queue.NotifyError(cookie, B_FILE_ERROR); 1456 cookie->tty->writer_queue.NotifyError(cookie, B_FILE_ERROR); 1457 1458 if (cookie->other_tty->open_count > 0) { 1459 cookie->other_tty->reader_queue.NotifyError(cookie, B_FILE_ERROR); 1460 cookie->other_tty->writer_queue.NotifyError(cookie, B_FILE_ERROR); 1461 } 1462 } 1463 1464 locker.Unlock(); 1465 1466 // wait till all blocking (and now unblocked) threads have left the 1467 // critical code 1468 if (unblock) { 1469 TRACE(("tty_close_cookie(): cookie %p, there're still pending " 1470 "operations, acquire blocking sem %" B_PRId32 "\n", cookie, 1471 cookie->blocking_semaphore)); 1472 1473 acquire_sem(cookie->blocking_semaphore); 1474 } 1475 1476 // For the removal of the cookie acquire the TTY's lock. This ensures, that 1477 // cookies will not be removed from a TTY (or added -- cf. add_tty_cookie()) 1478 // as long as the TTY's lock is being held. 1479 RecursiveLocker ttyLocker(cookie->tty->lock); 1480 1481 // remove the cookie from the TTY's cookie list 1482 cookie->tty->cookies.Remove(cookie); 1483 1484 // close the tty, if no longer used 1485 if (--cookie->tty->open_count == 0) { 1486 // The last cookie of this tty has been closed. We're going to close 1487 // the TTY and need to unblock all write requests before. There should 1488 // be no read requests, since only a cookie of this TTY issues those. 1489 // We do this by first notifying all queued requests of the error 1490 // condition. We then clear the line buffer for the TTY and queue 1491 // an own request. 1492 1493 // Notify the other TTY first; it doesn't accept any read/writes 1494 // while there is only one end. 1495 cookie->other_tty->reader_queue.NotifyError(B_FILE_ERROR); 1496 cookie->other_tty->writer_queue.NotifyError(B_FILE_ERROR); 1497 1498 RecursiveLocker requestLocker(gTTYRequestLock); 1499 1500 // we only need to do all this, if the writer queue is not empty 1501 if (!cookie->tty->writer_queue.IsEmpty()) { 1502 // notify the blocking writers 1503 cookie->tty->writer_queue.NotifyError(B_FILE_ERROR); 1504 1505 // enqueue our request 1506 RequestOwner requestOwner; 1507 requestOwner.Enqueue(cookie, &cookie->tty->writer_queue); 1508 1509 requestLocker.Unlock(); 1510 1511 // clear the line buffer 1512 clear_line_buffer(cookie->tty->input_buffer); 1513 1514 ttyLocker.Unlock(); 1515 1516 // wait for our turn 1517 requestOwner.Wait(false); 1518 1519 // re-lock 1520 ttyLocker.Lock(); 1521 requestLocker.Lock(); 1522 1523 // dequeue our request 1524 requestOwner.Dequeue(); 1525 } 1526 1527 requestLocker.Unlock(); 1528 1529 // notify a select read and write event on the other tty, if we've closed this tty 1530 if (cookie->other_tty->open_count > 0) { 1531 tty_notify_select_event(cookie->other_tty, B_SELECT_WRITE); 1532 tty_notify_select_event(cookie->other_tty, B_SELECT_READ); 1533 } 1534 1535 cookie->tty->is_exclusive = false; 1536 } 1537 } 1538 1539 1540 void 1541 tty_destroy_cookie(tty_cookie* cookie) 1542 { 1543 RecursiveLocker locker(cookie->tty->lock); 1544 cookie->tty->ref_count--; 1545 locker.Unlock(); 1546 1547 if (cookie->blocking_semaphore >= 0) 1548 delete_sem(cookie->blocking_semaphore); 1549 1550 delete cookie; 1551 } 1552 1553 1554 status_t 1555 tty_read(tty_cookie* cookie, void* _buffer, size_t* _length) 1556 { 1557 char* buffer = (char*)_buffer; 1558 struct tty* tty = cookie->tty; 1559 uint32 mode = cookie->open_mode; 1560 bool dontBlock = (mode & O_NONBLOCK) != 0; 1561 size_t length = *_length; 1562 bool canon = true; 1563 bigtime_t timeout = dontBlock ? 0 : B_INFINITE_TIMEOUT; 1564 bigtime_t interCharTimeout = 0; 1565 size_t bytesNeeded = 1; 1566 1567 TRACE(("tty_input_read(tty = %p, length = %lu, mode = %" B_PRIu32 ")\n", 1568 tty, length, mode)); 1569 1570 if (length == 0) 1571 return B_OK; 1572 1573 // bail out, if the TTY is already closed 1574 TTYReference ttyReference(cookie); 1575 if (!ttyReference.IsLocked()) 1576 return B_FILE_ERROR; 1577 1578 ReaderLocker locker(cookie); 1579 1580 // handle raw mode 1581 if ((!tty->is_master) && ((tty->settings->termios.c_lflag & ICANON) == 0)) { 1582 canon = false; 1583 if (!dontBlock) { 1584 // Non-blocking mode. Handle VMIN and VTIME. 1585 bytesNeeded = tty->settings->termios.c_cc[VMIN]; 1586 bigtime_t vtime = tty->settings->termios.c_cc[VTIME] * 100000; 1587 TRACE(("tty_input_read: icanon vmin %lu, vtime %" B_PRIdBIGTIME 1588 "us\n", bytesNeeded, vtime)); 1589 1590 if (bytesNeeded == 0) { 1591 // In this case VTIME specifies a relative total timeout. We 1592 // have no inter-char timeout, though. 1593 timeout = vtime; 1594 } else { 1595 // VTIME specifies the inter-char timeout. 0 is indefinitely. 1596 if (vtime == 0) 1597 interCharTimeout = B_INFINITE_TIMEOUT; 1598 else 1599 interCharTimeout = vtime; 1600 1601 if (bytesNeeded > length) 1602 bytesNeeded = length; 1603 } 1604 } 1605 } 1606 1607 status_t status; 1608 *_length = 0; 1609 1610 do { 1611 TRACE(("tty_input_read: AcquireReader(%" B_PRIdBIGTIME "us, %ld)\n", 1612 timeout, bytesNeeded)); 1613 status = locker.AcquireReader(timeout, bytesNeeded); 1614 size_t toRead = locker.AvailableBytes(); 1615 if (status != B_OK && toRead == 0) { 1616 TRACE(("tty_input_read() AcquireReader failed\n")); 1617 break; 1618 } 1619 1620 if (toRead > length) 1621 toRead = length; 1622 1623 bool _hitEOF = false; 1624 bool* hitEOF = canon && tty->pending_eof > 0 ? &_hitEOF : NULL; 1625 1626 ssize_t bytesRead = line_buffer_user_read(tty->input_buffer, buffer, 1627 toRead, tty->settings->termios.c_cc[VEOF], hitEOF); 1628 if (bytesRead < 0) { 1629 status = bytesRead; 1630 break; 1631 } 1632 1633 buffer += bytesRead; 1634 length -= bytesRead; 1635 *_length += bytesRead; 1636 bytesNeeded = (size_t)bytesRead > bytesNeeded 1637 ? 0 : bytesNeeded - bytesRead; 1638 1639 // we hit an EOF char -- bail out, whatever amount of data we have 1640 if (hitEOF && *hitEOF) { 1641 tty->pending_eof--; 1642 break; 1643 } 1644 1645 // Once we have read something reset the timeout to the inter-char 1646 // timeout, if applicable. 1647 if (!dontBlock && !canon && *_length > 0) 1648 timeout = interCharTimeout; 1649 } while (bytesNeeded > 0); 1650 1651 if (status == B_WOULD_BLOCK || status == B_TIMED_OUT) { 1652 // In non-blocking non-canonical-input-processing mode never return 1653 // timeout errors. Just return 0, if nothing has been read. 1654 if (!dontBlock && !canon) 1655 status = B_OK; 1656 } 1657 1658 TRACE(("tty_input_read() status 0x%" B_PRIx32 "\n", status)); 1659 1660 return *_length == 0 ? status : B_OK; 1661 } 1662 1663 1664 status_t 1665 tty_write(tty_cookie* sourceCookie, const void* _buffer, size_t* _length) 1666 { 1667 if (sourceCookie->tty->is_master) 1668 return tty_write_to_tty_master(sourceCookie, _buffer, _length); 1669 1670 return tty_write_to_tty_slave(sourceCookie, _buffer, _length); 1671 } 1672 1673 1674 status_t 1675 tty_control(tty_cookie* cookie, uint32 op, void* buffer, size_t length) 1676 { 1677 struct tty* tty = cookie->tty; 1678 1679 // bail out, if already closed 1680 TTYReference ttyReference(cookie); 1681 if (!ttyReference.IsLocked()) 1682 return B_FILE_ERROR; 1683 1684 TRACE(("tty_ioctl: tty %p, op %" B_PRIu32 ", buffer %p, length %" 1685 B_PRIuSIZE "\n", tty, op, buffer, length)); 1686 RecursiveLocker locker(tty->lock); 1687 1688 switch (op) { 1689 // blocking/non-blocking mode 1690 1691 case B_SET_BLOCKING_IO: 1692 cookie->open_mode &= ~O_NONBLOCK; 1693 return B_OK; 1694 case B_SET_NONBLOCKING_IO: 1695 cookie->open_mode |= O_NONBLOCK; 1696 return B_OK; 1697 1698 // get and set TTY attributes 1699 1700 case TCGETA: 1701 TRACE(("tty: get attributes\n")); 1702 return user_memcpy(buffer, &tty->settings->termios, 1703 sizeof(struct termios)); 1704 1705 case TCSETA: 1706 case TCSETAW: 1707 case TCSETAF: 1708 { 1709 TRACE(("tty: set attributes (iflag = %" B_PRIx32 ", oflag = %" 1710 B_PRIx32 ", cflag = %" B_PRIx32 ", lflag = %" B_PRIx32 ")\n", 1711 tty->settings->termios.c_iflag, tty->settings->termios.c_oflag, 1712 tty->settings->termios.c_cflag, 1713 tty->settings->termios.c_lflag)); 1714 1715 status_t status = user_memcpy(&tty->settings->termios, buffer, 1716 sizeof(struct termios)); 1717 if (status != B_OK) 1718 return status; 1719 1720 tty->service_func(tty, TTYSETMODES, &tty->settings->termios, 1721 sizeof(struct termios)); 1722 return status; 1723 } 1724 1725 // get and set process group ID 1726 1727 case TIOCGPGRP: 1728 TRACE(("tty: get pgrp_id\n")); 1729 return user_memcpy(buffer, &tty->settings->pgrp_id, sizeof(pid_t)); 1730 1731 case TIOCSPGRP: 1732 { 1733 TRACE(("tty: set pgrp_id\n")); 1734 pid_t groupID; 1735 1736 if (user_memcpy(&groupID, buffer, sizeof(pid_t)) != B_OK) 1737 return B_BAD_ADDRESS; 1738 1739 status_t error = team_set_foreground_process_group(tty, 1740 groupID); 1741 if (error == B_OK) 1742 tty->settings->pgrp_id = groupID; 1743 return error; 1744 } 1745 1746 // become controlling TTY 1747 case TIOCSCTTY: 1748 { 1749 TRACE(("tty: become controlling tty\n")); 1750 pid_t processID = getpid(); 1751 pid_t sessionID = getsid(processID); 1752 // Only session leaders can become controlling tty 1753 if (processID != sessionID) 1754 return B_NOT_ALLOWED; 1755 // Check if already controlling tty 1756 if (team_get_controlling_tty() == tty) 1757 return B_OK; 1758 tty->settings->session_id = sessionID; 1759 tty->settings->pgrp_id = sessionID; 1760 team_set_controlling_tty(tty); 1761 1762 // NOTE: We do not acquire a reference to the TTY for the team, so 1763 // consumers of team_get_controlling_tty() must check the TTY is still 1764 // valid before actually using it! 1765 return B_OK; 1766 } 1767 1768 // get session leader process group ID 1769 case TIOCGSID: 1770 { 1771 TRACE(("tty: get session_id\n")); 1772 return user_memcpy(buffer, &tty->settings->session_id, 1773 sizeof(pid_t)); 1774 } 1775 1776 // get and set window size 1777 1778 case TIOCGWINSZ: 1779 TRACE(("tty: get window size\n")); 1780 return user_memcpy(buffer, &tty->settings->window_size, 1781 sizeof(struct winsize)); 1782 1783 case TIOCSWINSZ: 1784 { 1785 uint16 oldColumns = tty->settings->window_size.ws_col; 1786 uint16 oldRows = tty->settings->window_size.ws_row; 1787 1788 TRACE(("tty: set window size\n")); 1789 if (user_memcpy(&tty->settings->window_size, buffer, 1790 sizeof(struct winsize)) < B_OK) { 1791 return B_BAD_ADDRESS; 1792 } 1793 1794 // send a signal only if the window size has changed 1795 if ((oldColumns != tty->settings->window_size.ws_col 1796 || oldRows != tty->settings->window_size.ws_row) 1797 && tty->settings->pgrp_id != 0) { 1798 send_signal(-tty->settings->pgrp_id, SIGWINCH); 1799 } 1800 1801 return B_OK; 1802 } 1803 1804 case 'ichr': // BeOS (int*) (pre- select() support) 1805 { 1806 int wanted; 1807 int toRead; 1808 1809 // help identify apps using it 1810 //dprintf("tty: warning: legacy BeOS opcode 'ichr'\n"); 1811 1812 if (user_memcpy(&wanted, buffer, sizeof(int)) != B_OK) 1813 return B_BAD_ADDRESS; 1814 1815 // release the mutex and grab a read lock 1816 locker.Unlock(); 1817 ReaderLocker readLocker(cookie); 1818 1819 bigtime_t timeout = wanted == 0 ? 0 : B_INFINITE_TIMEOUT; 1820 1821 // TODO: If wanted is > the TTY buffer size, this loop cannot work 1822 // correctly. Refactor the read code! 1823 do { 1824 status_t status = readLocker.AcquireReader(timeout, wanted); 1825 if (status != B_OK) 1826 return status; 1827 1828 toRead = readLocker.AvailableBytes(); 1829 } while (toRead < wanted); 1830 1831 if (user_memcpy(buffer, &toRead, sizeof(int)) != B_OK) 1832 return B_BAD_ADDRESS; 1833 1834 return B_OK; 1835 } 1836 1837 case FIONREAD: 1838 { 1839 int toRead = 0; 1840 1841 // release the mutex and grab a read lock 1842 locker.Unlock(); 1843 ReaderLocker readLocker(cookie); 1844 1845 status_t status = readLocker.AcquireReader(0, 1); 1846 if (status == B_OK) 1847 toRead = readLocker.AvailableBytes(); 1848 else if (status != B_WOULD_BLOCK) 1849 return status; 1850 1851 if (user_memcpy(buffer, &toRead, sizeof(int)) != B_OK) 1852 return B_BAD_ADDRESS; 1853 1854 return B_OK; 1855 } 1856 1857 case TIOCOUTQ: 1858 { 1859 int toWrite = 0; 1860 1861 // release the mutex and grab a write lock 1862 locker.Unlock(); 1863 WriterLocker writeLocker(cookie); 1864 1865 status_t status = writeLocker.AcquireWriter(0, 1); 1866 if (status == B_OK) 1867 toWrite = line_buffer_readable(tty->input_buffer); 1868 else if (status != B_WOULD_BLOCK) 1869 return status; 1870 1871 if (user_memcpy(buffer, &toWrite, sizeof(int)) != B_OK) 1872 return B_BAD_ADDRESS; 1873 1874 return B_OK; 1875 } 1876 1877 case TCXONC: // Unix, but even Linux doesn't handle it 1878 //dprintf("tty: unsupported TCXONC\n"); 1879 break; 1880 1881 case TCSETDTR: 1882 case TCSETRTS: 1883 case TIOCMSET: 1884 { 1885 // control line state setting, we only support DTR and RTS 1886 int value; 1887 if (user_memcpy(&value, buffer, sizeof(value)) != B_OK) 1888 return B_BAD_ADDRESS; 1889 1890 bool result = true; 1891 bool dtr = (op == TCSETDTR && value != 0) 1892 || (op == TIOCMSET && (value & TIOCM_DTR) != 0); 1893 if (op == TCSETDTR || op == TIOCMSET) 1894 result &= tty->service_func(tty, TTYSETDTR, &dtr, sizeof(dtr)); 1895 1896 bool rts = (op == TCSETRTS && value != 0) 1897 || (op == TIOCMSET && (value & TIOCM_RTS) != 0); 1898 if (op == TCSETRTS || op == TIOCMSET) 1899 result &= tty->service_func(tty, TTYSETRTS, &rts, sizeof(rts)); 1900 1901 return result ? B_OK : B_ERROR; 1902 } 1903 1904 case TCGETBITS: 1905 case TIOCMGET: 1906 { 1907 tty->service_func(tty, TTYGETSIGNALS, NULL, 0); 1908 int bits = tty->hardware_bits; 1909 return user_memcpy(buffer, &bits, sizeof(bits)); 1910 } 1911 1912 case TIOCMBIS: 1913 case TIOCMBIC: 1914 { 1915 // control line state setting, we only support DTR and RTS 1916 int value; 1917 if (user_memcpy(&value, buffer, sizeof(value)) != B_OK) 1918 return B_BAD_ADDRESS; 1919 1920 bool result = true; 1921 bool dtr = (op == TIOCMBIS); 1922 if (value & TIOCM_DTR) 1923 result &= tty->service_func(tty, TTYSETDTR, &dtr, sizeof(dtr)); 1924 1925 bool rts = (op == TIOCMBIS); 1926 if (value & TIOCM_RTS) 1927 result &= tty->service_func(tty, TTYSETRTS, &rts, sizeof(rts)); 1928 1929 return result ? B_OK : B_ERROR; 1930 } 1931 1932 case TIOCSBRK: 1933 case TIOCCBRK: 1934 case TCSBRK: 1935 { 1936 bool set; 1937 if (op == TIOCSBRK) 1938 set = true; 1939 else if (op == TIOCCBRK) 1940 set = false; 1941 else { 1942 int value = (int)(uintptr_t)buffer; 1943 set = value != 0; 1944 } 1945 1946 if (tty->service_func(tty, TTYSETBREAK, &set, sizeof(set))) 1947 return B_OK; 1948 1949 return B_ERROR; 1950 } 1951 1952 case TCFLSH: 1953 { 1954 int value = (int)(uintptr_t)buffer; 1955 if (value & TCOFLUSH) { 1956 struct tty* otherTTY = cookie->other_tty; 1957 if (otherTTY->open_count <= 0) 1958 return B_ERROR; 1959 1960 clear_line_buffer(otherTTY->input_buffer); 1961 } 1962 1963 if (value & TCIFLUSH) 1964 clear_line_buffer(tty->input_buffer); 1965 1966 if (tty->service_func(tty, TTYFLUSH, &value, sizeof(value))) 1967 return B_OK; 1968 1969 return B_ERROR; 1970 } 1971 1972 case TIOCEXCL: 1973 { 1974 tty->is_exclusive = true; 1975 return B_OK; 1976 } 1977 1978 case TIOCNXCL: 1979 { 1980 tty->is_exclusive = false; 1981 return B_OK; 1982 } 1983 } 1984 1985 TRACE(("tty: unsupported opcode %" B_PRIu32 "\n", op)); 1986 return B_BAD_VALUE; 1987 } 1988 1989 1990 status_t 1991 tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync) 1992 { 1993 struct tty* tty = cookie->tty; 1994 1995 TRACE(("tty_select(cookie = %p, event = %u, ref = %" B_PRIu32 ", sync = " 1996 "%p)\n", cookie, event, ref, sync)); 1997 1998 // we don't support all kinds of events 1999 if (event < B_SELECT_READ || event > B_SELECT_ERROR) 2000 return B_BAD_VALUE; 2001 2002 // if the TTY is already closed, we notify immediately 2003 TTYReference ttyReference(cookie); 2004 if (!ttyReference.IsLocked()) { 2005 TRACE(("tty_select() done: cookie %p already closed\n", cookie)); 2006 2007 notify_select_event(sync, event); 2008 return B_OK; 2009 } 2010 2011 // lock the TTY (allows us to freely access the cookie lists of this and 2012 // the other TTY) 2013 RecursiveLocker ttyLocker(tty->lock); 2014 2015 // get the other TTY -- needed for `write' events 2016 struct tty* otherTTY = cookie->other_tty; 2017 if (otherTTY->open_count <= 0) 2018 otherTTY = NULL; 2019 2020 // add the event to the TTY's pool 2021 status_t error = add_select_sync_pool_entry(&tty->select_pool, sync, event); 2022 if (error != B_OK) { 2023 TRACE(("tty_select() done: add_select_sync_pool_entry() failed: %" 2024 B_PRIx32 "\n", error)); 2025 2026 return error; 2027 } 2028 2029 // finally also acquire the request mutex, for access to the reader/writer 2030 // queues 2031 RecursiveLocker requestLocker(gTTYRequestLock); 2032 2033 // check, if the event is already present 2034 switch (event) { 2035 case B_SELECT_READ: 2036 if (tty->reader_queue.IsEmpty() && tty_readable(tty) > 0) 2037 notify_select_event(sync, event); 2038 break; 2039 2040 case B_SELECT_WRITE: 2041 { 2042 // writes go to the other TTY 2043 if (!otherTTY) { 2044 notify_select_event(sync, event); 2045 break; 2046 } 2047 2048 // In case input is echoed, we have to check, whether we can 2049 // currently can write to our TTY as well. 2050 bool echo = (tty->is_master 2051 && tty->settings->termios.c_lflag & ECHO); 2052 2053 if (otherTTY->writer_queue.IsEmpty() 2054 && line_buffer_writable(otherTTY->input_buffer) > 0) { 2055 if (!echo 2056 || (tty->writer_queue.IsEmpty() 2057 && line_buffer_writable(tty->input_buffer) > 0)) { 2058 notify_select_event(sync, event); 2059 } 2060 } 2061 break; 2062 } 2063 2064 case B_SELECT_ERROR: 2065 default: 2066 break; 2067 } 2068 2069 return B_OK; 2070 } 2071 2072 2073 status_t 2074 tty_deselect(tty_cookie* cookie, uint8 event, selectsync* sync) 2075 { 2076 struct tty* tty = cookie->tty; 2077 2078 TRACE(("tty_deselect(cookie = %p, event = %u, sync = %p)\n", cookie, event, 2079 sync)); 2080 2081 // we don't support all kinds of events 2082 if (event < B_SELECT_READ || event > B_SELECT_ERROR) 2083 return B_BAD_VALUE; 2084 2085 // lock the TTY (guards the select sync pool, among other things) 2086 RecursiveLocker ttyLocker(tty->lock); 2087 2088 return remove_select_sync_pool_entry(&tty->select_pool, sync, event); 2089 } 2090 2091 2092 status_t 2093 tty_hardware_signal(tty_cookie* cookie, int signal, bool set) 2094 { 2095 int bit = 0; 2096 2097 switch (signal) { 2098 case TTYHWDCD: 2099 bit = TCGB_DCD; 2100 break; 2101 case TTYHWCTS: 2102 bit = TCGB_CTS; 2103 break; 2104 case TTYHWDSR: 2105 bit = TCGB_DSR; 2106 break; 2107 case TTYHWRI: 2108 bit = TCGB_RI; 2109 break; 2110 2111 default: 2112 return B_BAD_VALUE; 2113 } 2114 2115 if (set) 2116 cookie->tty->hardware_bits |= bit; 2117 else 2118 cookie->tty->hardware_bits &= ~bit; 2119 2120 return B_ERROR; 2121 } 2122