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