BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / drivers / tty / tty.cpp
blob02b713daf398eef6a23fe56d89dd619969a83ee2
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 */
8 // This file could be moved into a generic tty module.
9 // The whole hardware signaling stuff is missing, though - it's currently
10 // tailored for pseudo-TTYs. Have a look at Be's TTY includes (drivers/tty/*)
13 #include "tty_private.h"
15 #include <ctype.h>
16 #include <errno.h>
17 #include <signal.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <sys/ioctl.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
24 #include <util/AutoLock.h>
25 #include <util/kernel_cpp.h>
27 #include <team.h>
29 #include <tty.h>
32 //#define TTY_TRACE
33 #ifdef TTY_TRACE
34 # define TRACE(x) dprintf x
35 #else
36 # define TRACE(x) ;
37 #endif
41 Locking
42 -------
44 There are four locks involved. If more than one needs to be held at a
45 time, they must be acquired in the order they are listed here.
47 gGlobalTTYLock: Guards open/close operations. When held, tty_open(),
48 tty_close(), tty_close_cookie() etc. won't be invoked by other threads,
49 cookies won't be added to/removed from TTYs, and tty::ref_count,
50 tty::open_count, tty_cookie::closed won't change.
52 gTTYCookieLock: Guards the access to the fields
53 tty_cookie::{thread_count,closed}, or more precisely makes access to them
54 atomic. thread_count is the number of threads currently using the cookie
55 (i.e. read(), write(), ioctl() operations in progress). Together with
56 blocking_semaphore this serves the purpose to make sure that all pending
57 operations are done at a certain point when closing a cookie
58 (cf. tty_close_cookie() and TTYReference).
60 tty::lock: Guards the access to tty::{input_buffer,settings::{termios,
61 window_size,pgrp_id}}. Moreover when held guarantees that tty::open_count
62 won't drop to zero (both gGlobalTTYLock and tty::lock must be held to
63 decrement it). A tty and the tty connected to it (master and slave) share
64 the same lock.
66 gTTYRequestLock: Guards access to tty::{reader,writer}_queue (most
67 RequestQueue methods do the locking themselves (the lock is a
68 recursive_lock)), queued Requests and associated RequestOwners.
71 Reading/Writing
72 ---------------
74 Most of the dirty work when dealing with reading/writing is done by the
75 {Reader,Writer}Locker classes. Upon construction they lock the tty,
76 (tty::lock) create a RequestOwner and queue Requests in the respective
77 reader/writer queues (tty::{reader,writer}_queue). The
78 Acquire{Reader,Writer}() methods need to be called before being allowed to
79 read/write. They ensure that there is actually something to read/space for
80 writing -- in blocking mode they wait, if necessary. When destroyed the
81 {Reader,Writer}Locker() remove the formerly enqueued Requests and notify
82 waiting reader/writer and/or send out select events, whatever is appropiate.
84 Acquire{Reader,Writer}() never return without an actual event being
85 occurred. Either an error has occurred (return value) -- in this case the
86 caller should terminate -- or bytes are available for reading/space for
87 writing (cf. AvailableBytes()).
91 tty_settings gTTYSettings[kNumTTYs];
94 static void tty_notify_select_event(struct tty* tty, uint8 event);
95 static void tty_notify_if_available(struct tty* tty, struct tty* otherTTY,
96 bool notifySelect);
99 class AbstractLocker {
100 public:
101 AbstractLocker(tty_cookie* cookie)
103 fCookie(cookie),
104 fBytes(0)
108 size_t AvailableBytes() const
109 { return fBytes; }
111 protected:
112 void Lock()
113 { mutex_lock(fCookie->tty->lock); }
114 void Unlock()
115 { mutex_unlock(fCookie->tty->lock); }
117 tty_cookie* fCookie;
118 size_t fBytes;
122 class WriterLocker : public AbstractLocker {
123 public:
124 WriterLocker(tty_cookie* sourceCookie);
125 ~WriterLocker();
127 status_t AcquireWriter(bool dontBlock,
128 size_t bytesNeeded);
130 private:
131 size_t _CheckAvailableBytes() const;
132 status_t _CheckBackgroundWrite() const;
134 struct tty* fSource;
135 struct tty* fTarget;
136 RequestOwner fRequestOwner;
137 bool fEcho;
141 class ReaderLocker : public AbstractLocker {
142 public:
143 ReaderLocker(tty_cookie* cookie);
144 ~ReaderLocker();
146 status_t AcquireReader(bigtime_t timeout,
147 size_t bytesNeeded);
149 private:
150 size_t _CheckAvailableBytes() const;
151 status_t _CheckBackgroundRead() const;
153 struct tty* fTTY;
154 RequestOwner fRequestOwner;
158 class TTYReferenceLocking {
159 public:
160 inline bool Lock(tty_cookie* cookie)
162 MutexLocker _(gTTYCookieLock);
164 if (cookie->closed)
165 return false;
167 cookie->thread_count++;
169 return true;
172 inline void Unlock(tty_cookie* cookie)
174 MutexLocker locker(gTTYCookieLock);
176 sem_id semaphore = -1;
177 if (--cookie->thread_count == 0 && cookie->closed)
178 semaphore = cookie->blocking_semaphore;
180 locker.Unlock();
182 if (semaphore >= 0) {
183 TRACE(("TTYReference: cookie %p closed, last operation done, "
184 "releasing blocking sem %ld\n", cookie, semaphore));
186 release_sem(semaphore);
191 typedef AutoLocker<tty_cookie, TTYReferenceLocking> TTYReference;
194 // #pragma mark -
197 Request::Request()
199 fOwner(NULL),
200 fCookie(NULL),
201 fBytesNeeded(0),
202 fNotified(false),
203 fError(false)
208 void
209 Request::Init(RequestOwner* owner, tty_cookie* cookie, size_t bytesNeeded)
211 fOwner = owner;
212 fCookie = cookie;
213 fBytesNeeded = bytesNeeded;
214 fNotified = false;
215 fError = false;
219 void
220 Request::Notify(size_t bytesAvailable)
222 if (!fNotified && bytesAvailable >= fBytesNeeded && fOwner) {
223 fOwner->Notify(this);
224 fNotified = true;
229 void
230 Request::NotifyError(status_t error)
232 if (!fError && fOwner) {
233 fOwner->NotifyError(this, error);
234 fError = true;
235 fNotified = true;
240 void
241 Request::Dump(const char* prefix)
243 kprintf("%srequest: %p\n", prefix, this);
244 kprintf("%s owner: %p\n", prefix, fOwner);
245 kprintf("%s cookie: %p\n", prefix, fCookie);
246 kprintf("%s bytes needed: %lu\n", prefix, fBytesNeeded);
247 kprintf("%s notified: %s\n", prefix, fNotified ? "true" : "false");
248 kprintf("%s error: %s\n", prefix, fError ? "true" : "false");
252 // #pragma mark -
255 RequestQueue::RequestQueue()
257 fRequests()
262 void
263 RequestQueue::Add(Request* request)
265 if (request) {
266 RecursiveLocker _(gTTYRequestLock);
268 fRequests.Add(request, true);
273 void
274 RequestQueue::Remove(Request* request)
276 if (request) {
277 RecursiveLocker _(gTTYRequestLock);
279 fRequests.Remove(request);
284 void
285 RequestQueue::NotifyFirst(size_t bytesAvailable)
287 RecursiveLocker _(gTTYRequestLock);
289 if (Request* first = First())
290 first->Notify(bytesAvailable);
294 void
295 RequestQueue::NotifyError(status_t error)
297 RecursiveLocker _(gTTYRequestLock);
299 for (RequestList::Iterator it = fRequests.GetIterator(); it.HasNext();) {
300 Request* request = it.Next();
301 request->NotifyError(error);
306 void
307 RequestQueue::NotifyError(tty_cookie* cookie, status_t error)
309 RecursiveLocker _(gTTYRequestLock);
311 for (RequestList::Iterator it = fRequests.GetIterator(); it.HasNext();) {
312 Request* request = it.Next();
313 if (request->TTYCookie() == cookie)
314 request->NotifyError(error);
319 void
320 RequestQueue::Dump(const char* prefix)
322 RequestList::Iterator it = fRequests.GetIterator();
323 while (Request* request = it.Next())
324 request->Dump(prefix);
328 // #pragma mark -
331 RequestOwner::RequestOwner()
333 fConditionVariable(NULL),
334 fCookie(NULL),
335 fError(B_OK),
336 fBytesNeeded(1)
338 fRequestQueues[0] = NULL;
339 fRequestQueues[1] = NULL;
343 /*! The caller must already hold the request lock.
345 void
346 RequestOwner::Enqueue(tty_cookie* cookie, RequestQueue* queue1,
347 RequestQueue* queue2)
349 TRACE(("%p->RequestOwner::Enqueue(%p, %p, %p)\n", this, cookie, queue1,
350 queue2));
352 fCookie = cookie;
354 fRequestQueues[0] = queue1;
355 fRequestQueues[1] = queue2;
357 fRequests[0].Init(this, cookie, fBytesNeeded);
358 if (queue1)
359 queue1->Add(&fRequests[0]);
360 else
361 fRequests[0].Notify(fBytesNeeded);
363 fRequests[1].Init(this, cookie, fBytesNeeded);
364 if (queue2)
365 queue2->Add(&fRequests[1]);
366 else
367 fRequests[1].Notify(fBytesNeeded);
371 /*! The caller must already hold the request lock.
373 void
374 RequestOwner::Dequeue()
376 TRACE(("%p->RequestOwner::Dequeue()\n", this));
378 if (fRequestQueues[0])
379 fRequestQueues[0]->Remove(&fRequests[0]);
380 if (fRequestQueues[1])
381 fRequestQueues[1]->Remove(&fRequests[1]);
383 fRequestQueues[0] = NULL;
384 fRequestQueues[1] = NULL;
388 void
389 RequestOwner::SetBytesNeeded(size_t bytesNeeded)
391 if (fRequestQueues[0])
392 fRequests[0].Init(this, fCookie, bytesNeeded);
394 if (fRequestQueues[1])
395 fRequests[1].Init(this, fCookie, bytesNeeded);
399 /*! The request lock MUST NOT be held!
401 status_t
402 RequestOwner::Wait(bool interruptable, bigtime_t timeout)
404 TRACE(("%p->RequestOwner::Wait(%d)\n", this, interruptable));
406 status_t error = B_OK;
408 RecursiveLocker locker(gTTYRequestLock);
410 // check, if already done
411 if (fError == B_OK
412 && (!fRequests[0].WasNotified() || !fRequests[1].WasNotified())) {
413 // not yet done
415 // publish the condition variable
416 ConditionVariable conditionVariable;
417 conditionVariable.Init(this, "tty request");
418 fConditionVariable = &conditionVariable;
420 // add an entry to wait on
421 ConditionVariableEntry entry;
422 conditionVariable.Add(&entry);
424 locker.Unlock();
426 // wait
427 TRACE(("%p->RequestOwner::Wait(): waiting for condition...\n", this));
429 error = entry.Wait(
430 (interruptable ? B_CAN_INTERRUPT : 0) | B_RELATIVE_TIMEOUT,
431 timeout);
433 TRACE(("%p->RequestOwner::Wait(): condition occurred: %lx\n", this,
434 error));
436 // remove the condition variable
437 locker.Lock();
438 fConditionVariable = NULL;
441 // get the result
442 if (error == B_OK)
443 error = fError;
445 return error;
449 bool
450 RequestOwner::IsFirstInQueues()
452 RecursiveLocker locker(gTTYRequestLock);
454 for (int i = 0; i < 2; i++) {
455 if (fRequestQueues[i] && fRequestQueues[i]->First() != &fRequests[i])
456 return false;
459 return true;
463 void
464 RequestOwner::Notify(Request* request)
466 TRACE(("%p->RequestOwner::Notify(%p)\n", this, request));
468 if (fError == B_OK && !request->WasNotified()) {
469 bool notify = false;
471 if (&fRequests[0] == request) {
472 notify = fRequests[1].WasNotified();
473 } else if (&fRequests[1] == request) {
474 notify = fRequests[0].WasNotified();
475 } else {
476 // spurious call
479 if (notify && fConditionVariable)
480 fConditionVariable->NotifyOne();
485 void
486 RequestOwner::NotifyError(Request* request, status_t error)
488 TRACE(("%p->RequestOwner::NotifyError(%p, %lx)\n", this, request, error));
490 if (fError == B_OK) {
491 fError = error;
493 if (!fRequests[0].WasNotified() || !fRequests[1].WasNotified()) {
494 if (fConditionVariable)
495 fConditionVariable->NotifyOne();
501 // #pragma mark -
504 WriterLocker::WriterLocker(tty_cookie* sourceCookie)
506 AbstractLocker(sourceCookie),
507 fSource(fCookie->tty),
508 fTarget(fCookie->other_tty),
509 fRequestOwner(),
510 fEcho(false)
512 Lock();
514 // Now that the tty pair is locked, we can check, whether the target is
515 // open at all.
516 if (fTarget->open_count > 0) {
517 // The target tty is open. As soon as we have appended a request to
518 // the writer queue of the target, it is guaranteed to remain valid
519 // until we have removed the request (and notified the
520 // tty_close_cookie() pseudo request).
522 // get the echo mode
523 fEcho = (fSource->is_master
524 && fSource->settings->termios.c_lflag & ECHO) != 0;
526 // enqueue ourselves in the respective request queues
527 RecursiveLocker locker(gTTYRequestLock);
528 fRequestOwner.Enqueue(fCookie, &fTarget->writer_queue,
529 (fEcho ? &fSource->writer_queue : NULL));
530 } else {
531 // target is not open: we set it to NULL; all further operations on
532 // this locker will fail
533 fTarget = NULL;
538 WriterLocker::~WriterLocker()
540 // dequeue from request queues
541 RecursiveLocker locker(gTTYRequestLock);
542 fRequestOwner.Dequeue();
544 // check the tty queues and notify the next in line, and send out select
545 // events
546 if (fTarget)
547 tty_notify_if_available(fTarget, fSource, true);
548 if (fEcho)
549 tty_notify_if_available(fSource, fTarget, true);
551 locker.Unlock();
553 Unlock();
557 size_t
558 WriterLocker::_CheckAvailableBytes() const
560 size_t writable = line_buffer_writable(fTarget->input_buffer);
561 if (fEcho) {
562 // we can only write as much as is available on both ends
563 size_t locallyWritable = line_buffer_writable(fSource->input_buffer);
564 if (locallyWritable < writable)
565 writable = locallyWritable;
567 return writable;
571 status_t
572 WriterLocker::AcquireWriter(bool dontBlock, size_t bytesNeeded)
574 if (!fTarget)
575 return B_FILE_ERROR;
576 if (fEcho && fCookie->closed)
577 return B_FILE_ERROR;
579 RecursiveLocker requestLocker(gTTYRequestLock);
581 // check, if we're first in queue, and if there is space to write
582 if (fRequestOwner.IsFirstInQueues()) {
583 fBytes = _CheckAvailableBytes();
584 if (fBytes >= bytesNeeded)
585 return B_OK;
588 // We are not the first in queue or currently there's no space to write:
589 // bail out, if we shall not block.
590 if (dontBlock)
591 return B_WOULD_BLOCK;
593 // set the number of bytes we need and notify, just in case we're first in
594 // one of the queues (RequestOwner::SetBytesNeeded() resets the notification
595 // state)
596 fRequestOwner.SetBytesNeeded(bytesNeeded);
597 if (fTarget)
598 tty_notify_if_available(fTarget, fSource, false);
599 if (fEcho)
600 tty_notify_if_available(fSource, fTarget, false);
602 requestLocker.Unlock();
604 // block until something happens
605 Unlock();
606 status_t status = fRequestOwner.Wait(true);
607 Lock();
609 // RequestOwner::Wait() returns the error, but to avoid a race condition
610 // when closing a tty, we re-get the error with the tty lock being held.
611 if (status == B_OK) {
612 RecursiveLocker _(gTTYRequestLock);
613 status = fRequestOwner.Error();
616 if (status == B_OK)
617 status = _CheckBackgroundWrite();
619 if (status == B_OK) {
620 if (fTarget->open_count > 0)
621 fBytes = _CheckAvailableBytes();
622 else
623 status = B_FILE_ERROR;
626 return status;
630 status_t
631 WriterLocker::_CheckBackgroundWrite() const
633 // only relevant for the slave end and only when TOSTOP is set
634 if (fSource->is_master
635 || (fSource->settings->termios.c_lflag & TOSTOP) == 0) {
636 return B_OK;
639 pid_t processGroup = getpgid(0);
640 if (fSource->settings->pgrp_id != 0
641 && processGroup != fSource->settings->pgrp_id) {
642 if (team_get_controlling_tty() == fSource->index)
643 send_signal(-processGroup, SIGTTOU);
644 return EIO;
647 return B_OK;
651 // #pragma mark -
654 ReaderLocker::ReaderLocker(tty_cookie* cookie)
656 AbstractLocker(cookie),
657 fTTY(cookie->tty),
658 fRequestOwner()
660 Lock();
662 // enqueue ourselves in the reader request queue
663 RecursiveLocker locker(gTTYRequestLock);
664 fRequestOwner.Enqueue(fCookie, &fTTY->reader_queue);
668 ReaderLocker::~ReaderLocker()
670 // dequeue from reader request queue
671 RecursiveLocker locker(gTTYRequestLock);
672 fRequestOwner.Dequeue();
674 // check the tty queues and notify the next in line, and send out select
675 // events
676 struct tty* otherTTY = fCookie->other_tty;
677 tty_notify_if_available(fTTY, (otherTTY->open_count > 0 ? otherTTY : NULL),
678 true);
680 locker.Unlock();
682 Unlock();
686 status_t
687 ReaderLocker::AcquireReader(bigtime_t timeout, size_t bytesNeeded)
689 if (fCookie->closed)
690 return B_FILE_ERROR;
692 status_t status = _CheckBackgroundRead();
693 if (status != B_OK)
694 return status;
696 // check, if we're first in queue, and if there is something to read
697 if (fRequestOwner.IsFirstInQueues()) {
698 fBytes = _CheckAvailableBytes();
699 if (fBytes >= bytesNeeded)
700 return B_OK;
703 // We are not the first in queue or currently there's nothing to read:
704 // bail out, if we shall not block.
705 if (timeout <= 0)
706 return B_WOULD_BLOCK;
708 // reset the number of bytes we need
709 fRequestOwner.SetBytesNeeded(bytesNeeded);
711 // block until something happens
712 Unlock();
713 status = fRequestOwner.Wait(true, timeout);
714 Lock();
716 if (status == B_OK)
717 status = _CheckBackgroundRead();
719 if (status == B_OK)
720 fBytes = _CheckAvailableBytes();
722 return status;
726 size_t
727 ReaderLocker::_CheckAvailableBytes() const
729 // Reading from the slave with canonical input processing enabled means
730 // that we read at max until hitting a line end or EOF.
731 if (!fTTY->is_master && (fTTY->settings->termios.c_lflag & ICANON) != 0) {
732 return line_buffer_readable_line(fTTY->input_buffer,
733 fTTY->settings->termios.c_cc[VEOL],
734 fTTY->settings->termios.c_cc[VEOF]);
737 return line_buffer_readable(fTTY->input_buffer);
741 status_t
742 ReaderLocker::_CheckBackgroundRead() const
744 // only relevant for the slave end
745 if (fTTY->is_master)
746 return B_OK;
748 pid_t processGroup = getpgid(0);
749 if (fTTY->settings->pgrp_id != 0
750 && processGroup != fTTY->settings->pgrp_id) {
751 if (team_get_controlling_tty() == fTTY->index)
752 send_signal(-processGroup, SIGTTIN);
753 return EIO;
756 return B_OK;
760 // #pragma mark -
763 int32
764 get_tty_index(const char* name)
766 // device names follow this form: "pt/%c%x"
767 int8 digit = name[4];
768 if (digit >= 'a') {
769 // hexadecimal digits
770 digit -= 'a' - 10;
771 } else
772 digit -= '0';
774 return (name[3] - 'p') * 16 + digit;
778 static void
779 reset_termios(struct termios& termios)
781 memset(&termios, 0, sizeof(struct termios));
783 termios.c_iflag = ICRNL;
784 termios.c_oflag = OPOST | ONLCR;
785 termios.c_cflag = B19200 | CS8 | CREAD | HUPCL;
786 // enable receiver, hang up on last close
787 termios.c_lflag = ECHO | ISIG | ICANON;
788 termios.c_ispeed = B19200;
789 termios.c_ospeed = B19200;
791 // control characters
792 termios.c_cc[VINTR] = CTRL('C');
793 termios.c_cc[VQUIT] = CTRL('\\');
794 termios.c_cc[VERASE] = 0x7f;
795 termios.c_cc[VKILL] = CTRL('U');
796 termios.c_cc[VEOF] = CTRL('D');
797 termios.c_cc[VEOL] = '\0';
798 termios.c_cc[VEOL2] = '\0';
799 termios.c_cc[VSTART] = CTRL('S');
800 termios.c_cc[VSTOP] = CTRL('Q');
801 termios.c_cc[VSUSP] = CTRL('Z');
805 void
806 reset_tty_settings(tty_settings* settings, int32 index)
808 reset_termios(settings->termios);
810 settings->pgrp_id = 0;
811 // this value prevents any signal of being sent
812 settings->session_id = -1;
814 // some initial window size - the TTY in question should set these values
815 settings->window_size.ws_col = 80;
816 settings->window_size.ws_row = 25;
817 settings->window_size.ws_xpixel = settings->window_size.ws_col * 8;
818 settings->window_size.ws_ypixel = settings->window_size.ws_row * 8;
822 void
823 reset_tty(struct tty* tty, int32 index, mutex* lock, bool isMaster)
825 tty->ref_count = 0;
826 tty->open_count = 0;
827 tty->index = index;
828 tty->lock = lock;
829 tty->settings = &gTTYSettings[index];
830 tty->select_pool = NULL;
831 tty->is_master = isMaster;
832 tty->pending_eof = 0;
836 status_t
837 tty_output_getc(struct tty* tty, int* _c)
839 return B_ERROR;
843 /*! Processes the input character and puts it into the TTY's input buffer.
844 Depending on the termios flags set, signals may be sent, the input
845 character changed or removed, etc.
847 static void
848 tty_input_putc_locked(struct tty* tty, int c)
850 // process signals if needed
852 if ((tty->settings->termios.c_lflag & ISIG) != 0) {
853 // enable signals, process INTR, QUIT, and SUSP
854 int signal = -1;
856 if (c == tty->settings->termios.c_cc[VINTR])
857 signal = SIGINT;
858 else if (c == tty->settings->termios.c_cc[VQUIT])
859 signal = SIGQUIT;
860 else if (c == tty->settings->termios.c_cc[VSUSP])
861 signal = SIGTSTP;
863 // do we need to deliver a signal?
864 if (signal != -1) {
865 // we may have to flush the input buffer
866 if ((tty->settings->termios.c_lflag & NOFLSH) == 0)
867 clear_line_buffer(tty->input_buffer);
869 if (tty->settings->pgrp_id != 0)
870 send_signal(-tty->settings->pgrp_id, signal);
871 return;
875 // process special canonical input characters
877 if ((tty->settings->termios.c_lflag & ICANON) != 0) {
878 // canonical mode, process ERASE and KILL
879 cc_t* controlChars = tty->settings->termios.c_cc;
881 if (c == controlChars[VERASE]) {
882 // erase one character
883 char lastChar;
884 if (line_buffer_tail_getc(tty->input_buffer, &lastChar)) {
885 if (lastChar == controlChars[VEOF]
886 || lastChar == controlChars[VEOL]
887 || lastChar == '\n' || lastChar == '\r') {
888 // EOF or end of line -- put it back
889 line_buffer_putc(tty->input_buffer, lastChar);
892 return;
893 } else if (c == controlChars[VKILL]) {
894 // erase line
895 char lastChar;
896 while (line_buffer_tail_getc(tty->input_buffer, &lastChar)) {
897 if (lastChar == controlChars[VEOF]
898 || lastChar == controlChars[VEOL]
899 || lastChar == '\n' || lastChar == '\r') {
900 // EOF or end of line -- put it back
901 line_buffer_putc(tty->input_buffer, lastChar);
902 break;
905 return;
906 } else if (c == controlChars[VEOF]) {
907 // we still write the EOF to the stream -- tty_input_read() needs
908 // to recognize it
909 tty->pending_eof++;
913 // Input character conversions have already been done. What reaches this
914 // point can directly be written to the line buffer.
916 line_buffer_putc(tty->input_buffer, c);
920 #if 0
921 status_t
922 tty_input_putc(struct tty* tty, int c)
924 status_t status = acquire_sem_etc(tty->write_sem, 1, B_CAN_INTERRUPT, 0);
925 if (status != B_OK)
926 return status;
928 MutexLocker locker(&tty->lock);
930 bool wasEmpty = line_buffer_readable(tty->input_buffer) == 0;
932 tty_input_putc_locked(tty, c);
934 // If the buffer was empty before, we can now start other readers on it.
935 // We assume that most of the time more than one character will be written
936 // using this function, so we don't want to reschedule after every character
937 if (wasEmpty)
938 release_sem_etc(tty->read_sem, 1, B_DO_NOT_RESCHEDULE);
940 // We only wrote one char - we give others the opportunity
941 // to write if there is still space left in the buffer
942 if (line_buffer_writable(tty->input_buffer))
943 release_sem_etc(tty->write_sem, 1, B_DO_NOT_RESCHEDULE);
945 return B_OK;
947 #endif // 0
950 /*! The global lock must be held.
952 status_t
953 init_tty_cookie(tty_cookie* cookie, struct tty* tty, struct tty* otherTTY,
954 uint32 openMode)
956 cookie->blocking_semaphore = create_sem(0, "wait for tty close");
957 if (cookie->blocking_semaphore < 0)
958 return cookie->blocking_semaphore;
960 cookie->tty = tty;
961 cookie->other_tty = otherTTY;
962 cookie->open_mode = openMode;
963 cookie->thread_count = 0;
964 cookie->closed = false;
966 tty->ref_count++;
968 return B_OK;
972 /*! The global lock must be held.
974 void
975 uninit_tty_cookie(tty_cookie* cookie)
977 cookie->tty->ref_count--;
979 if (cookie->blocking_semaphore >= 0) {
980 delete_sem(cookie->blocking_semaphore);
981 cookie->blocking_semaphore = -1;
984 cookie->tty = NULL;
985 cookie->thread_count = 0;
986 cookie->closed = false;
990 /*! The global lock must be held.
992 void
993 add_tty_cookie(tty_cookie* cookie)
995 MutexLocker locker(cookie->tty->lock);
997 // add to the TTY's cookie list
998 cookie->tty->cookies.Add(cookie);
999 cookie->tty->open_count++;
1003 /*! The global lock must be held.
1005 void
1006 tty_close_cookie(struct tty_cookie* cookie)
1008 MutexLocker locker(gTTYCookieLock);
1010 // Already closed? This can happen for slaves that have been closed when
1011 // the master was closed.
1012 if (cookie->closed)
1013 return;
1015 // set the cookie's `closed' flag
1016 cookie->closed = true;
1017 bool unblock = (cookie->thread_count > 0);
1019 // unblock blocking threads
1020 if (unblock) {
1021 cookie->tty->reader_queue.NotifyError(cookie, B_FILE_ERROR);
1022 cookie->tty->writer_queue.NotifyError(cookie, B_FILE_ERROR);
1024 if (cookie->other_tty->open_count > 0) {
1025 cookie->other_tty->reader_queue.NotifyError(cookie, B_FILE_ERROR);
1026 cookie->other_tty->writer_queue.NotifyError(cookie, B_FILE_ERROR);
1030 locker.Unlock();
1032 // wait till all blocking (and now unblocked) threads have left the
1033 // critical code
1034 if (unblock) {
1035 TRACE(("tty_close_cookie(): cookie %p, there're still pending "
1036 "operations, acquire blocking sem %ld\n", cookie,
1037 cookie->blocking_semaphore));
1039 acquire_sem(cookie->blocking_semaphore);
1042 // For the removal of the cookie acquire the TTY's lock. This ensures, that
1043 // cookies will not be removed from a TTY (or added -- cf. add_tty_cookie())
1044 // as long as the TTY's lock is being held. This is required for the select
1045 // support, since we need to iterate through the cookies of a TTY without
1046 // having to acquire the global lock.
1047 MutexLocker ttyLocker(cookie->tty->lock);
1049 // remove the cookie from the TTY's cookie list
1050 cookie->tty->cookies.Remove(cookie);
1052 // close the tty, if no longer used
1053 if (--cookie->tty->open_count == 0) {
1054 // The last cookie of this tty has been closed. We're going to close
1055 // the TTY and need to unblock all write requests before. There should
1056 // be no read requests, since only a cookie of this TTY issues those.
1057 // We do this by first notifying all queued requests of the error
1058 // condition. We then clear the line buffer for the TTY and queue
1059 // an own request.
1061 // Notify the other TTY first; it doesn't accept any read/writes
1062 // while there is only one end.
1063 cookie->other_tty->reader_queue.NotifyError(B_FILE_ERROR);
1064 cookie->other_tty->writer_queue.NotifyError(B_FILE_ERROR);
1066 RecursiveLocker requestLocker(gTTYRequestLock);
1068 // we only need to do all this, if the writer queue is not empty
1069 if (!cookie->tty->writer_queue.IsEmpty()) {
1070 // notify the blocking writers
1071 cookie->tty->writer_queue.NotifyError(B_FILE_ERROR);
1073 // enqueue our request
1074 RequestOwner requestOwner;
1075 requestOwner.Enqueue(cookie, &cookie->tty->writer_queue);
1077 requestLocker.Unlock();
1079 // clear the line buffer
1080 clear_line_buffer(cookie->tty->input_buffer);
1082 ttyLocker.Unlock();
1084 // wait for our turn
1085 requestOwner.Wait(false);
1087 // re-lock
1088 ttyLocker.Lock();
1089 requestLocker.Lock();
1091 // dequeue our request
1092 requestOwner.Dequeue();
1095 requestLocker.Unlock();
1097 // finally close the tty
1098 tty_close(cookie->tty);
1101 // notify pending select()s and cleanup the select sync pool
1103 // notify a select write event on the other tty, if we've closed this tty
1104 if (cookie->tty->open_count == 0 && cookie->other_tty->open_count > 0)
1105 tty_notify_select_event(cookie->other_tty, B_SELECT_WRITE);
1109 static int32
1110 tty_readable(struct tty* tty)
1112 if (!tty->is_master && (tty->settings->termios.c_lflag & ICANON) != 0) {
1113 return line_buffer_readable_line(tty->input_buffer,
1114 tty->settings->termios.c_cc[VEOL],
1115 tty->settings->termios.c_cc[VEOF]);
1118 return line_buffer_readable(tty->input_buffer);
1122 static void
1123 tty_notify_select_event(struct tty* tty, uint8 event)
1125 TRACE(("tty_notify_select_event(%p, %u)\n", tty, event));
1127 if (tty->select_pool)
1128 notify_select_event_pool(tty->select_pool, event);
1132 /*! \brief Checks whether bytes can be read from/written to the line buffer of
1133 the given TTY and notifies the respective queues.
1135 Also sends out \c B_SELECT_READ and \c B_SELECT_WRITE events as needed.
1137 The TTY and the request lock must be held.
1139 \param tty The TTY.
1140 \param otherTTY The connected TTY.
1142 static void
1143 tty_notify_if_available(struct tty* tty, struct tty* otherTTY,
1144 bool notifySelect)
1146 if (!tty)
1147 return;
1149 // Check, if something is readable (depending on whether canonical input
1150 // processing is enabled).
1151 int32 readable = tty_readable(tty);
1152 if (readable > 0) {
1153 // if nobody is waiting send select events, otherwise notify the waiter
1154 if (!tty->reader_queue.IsEmpty())
1155 tty->reader_queue.NotifyFirst(readable);
1156 else if (notifySelect)
1157 tty_notify_select_event(tty, B_SELECT_READ);
1160 int32 writable = line_buffer_writable(tty->input_buffer);
1161 if (writable > 0) {
1162 // if nobody is waiting send select events, otherwise notify the waiter
1163 if (!tty->writer_queue.IsEmpty()) {
1164 tty->writer_queue.NotifyFirst(writable);
1165 } else if (notifySelect) {
1166 if (otherTTY && otherTTY->open_count > 0)
1167 tty_notify_select_event(otherTTY, B_SELECT_WRITE);
1173 /*! \brief Performs input character conversion and writes the result to
1174 \a buffer.
1175 \param tty The master tty.
1176 \param c The input character.
1177 \param buffer The buffer to which to write the converted character.
1178 \param _bytesNeeded The number of bytes needed in the target tty's
1179 line buffer.
1180 \return \c true, if the character shall be processed further, \c false, if
1181 it shall be skipped.
1183 static bool
1184 process_input_char(struct tty* tty, char c, char* buffer,
1185 size_t* _bytesNeeded)
1187 tcflag_t flags = tty->settings->termios.c_iflag;
1189 // signals
1190 if (tty->settings->termios.c_lflag & ISIG) {
1191 if (c == tty->settings->termios.c_cc[VINTR]
1192 || c == tty->settings->termios.c_cc[VQUIT]
1193 || c == tty->settings->termios.c_cc[VSUSP]) {
1194 *buffer = c;
1195 *_bytesNeeded = 0;
1196 return true;
1200 // canonical input characters
1201 if (tty->settings->termios.c_lflag & ICANON) {
1202 if (c == tty->settings->termios.c_cc[VERASE]
1203 || c == tty->settings->termios.c_cc[VKILL]) {
1204 *buffer = c;
1205 *_bytesNeeded = 0;
1206 return true;
1210 // convert chars
1211 if (c == '\r') {
1212 if (flags & IGNCR) // ignore CR
1213 return false;
1214 if (flags & ICRNL) // CR -> NL
1215 c = '\n';
1216 } else if (c == '\n') {
1217 if (flags & INLCR) // NL -> CR
1218 c = '\r';
1219 } else if ((flags & ISTRIP) != 0) // strip off eighth bit
1220 c &= 0x7f;
1222 *buffer = c;
1223 *_bytesNeeded = 1;
1224 return true;
1228 /*! \brief Performs output character conversion and writes the result to
1229 \a buffer.
1230 \param tty The master tty.
1231 \param c The output character.
1232 \param buffer The buffer to which to write the converted character(s).
1233 \param _bytesWritten The number of bytes written to the output buffer
1234 (max 3).
1235 \param echoed \c true if the output char to be processed has been echoed
1236 from the input.
1238 static void
1239 process_output_char(struct tty* tty, char c, char* buffer,
1240 size_t* _bytesWritten, bool echoed)
1242 tcflag_t flags = tty->settings->termios.c_oflag;
1244 if (flags & OPOST) {
1245 if (echoed && c == tty->settings->termios.c_cc[VERASE]) {
1246 if (tty->settings->termios.c_lflag & ECHOE) {
1247 // ERASE -> BS SPACE BS
1248 buffer[0] = CTRL('H');
1249 buffer[1] = ' ';
1250 buffer[2] = CTRL('H');;
1251 *_bytesWritten = 3;
1252 return;
1254 } else if (echoed && c == tty->settings->termios.c_cc[VKILL]) {
1255 if (!(tty->settings->termios.c_lflag & ECHOK)) {
1256 // don't echo KILL
1257 *_bytesWritten = 0;
1258 return;
1260 } else if (echoed && c == tty->settings->termios.c_cc[VEOF]) {
1261 // don't echo EOF
1262 *_bytesWritten = 0;
1263 return;
1264 } else if (c == '\n') {
1265 if (echoed && !(tty->settings->termios.c_lflag & ECHONL)) {
1266 // don't echo NL
1267 *_bytesWritten = 0;
1268 return;
1270 if (flags & ONLCR) { // NL -> CR-NL
1271 buffer[0] = '\r';
1272 buffer[1] = '\n';
1273 *_bytesWritten = 2;
1274 return;
1276 } else if (c == '\r') {
1277 if (flags & OCRNL) { // CR -> NL
1278 c = '\n';
1279 } else if (flags & ONLRET) { // NL also does RET, ignore CR
1280 *_bytesWritten = 0;
1281 return;
1282 } else if (flags & ONOCR) { // don't output CR at column 0
1283 // TODO: We can't decide that here.
1285 } else {
1286 if (flags & OLCUC) // lower case -> upper case
1287 c = toupper(c);
1291 *buffer = c;
1292 *_bytesWritten = 1;
1296 static status_t
1297 tty_write_to_tty_master_unsafe(tty_cookie* sourceCookie, const char* data,
1298 size_t* _length)
1300 struct tty* source = sourceCookie->tty;
1301 struct tty* target = sourceCookie->other_tty;
1302 size_t length = *_length;
1303 size_t bytesWritten = 0;
1304 uint32 mode = sourceCookie->open_mode;
1305 bool dontBlock = (mode & O_NONBLOCK) != 0;
1307 // bail out, if source is already closed
1308 TTYReference sourceTTYReference(sourceCookie);
1309 if (!sourceTTYReference.IsLocked())
1310 return B_FILE_ERROR;
1312 if (length == 0)
1313 return B_OK;
1315 WriterLocker locker(sourceCookie);
1317 // if the target is not open, fail now
1318 if (target->open_count <= 0)
1319 return B_FILE_ERROR;
1321 bool echo = (source->settings->termios.c_lflag & ECHO) != 0;
1323 TRACE(("tty_write_to_tty_master(source = %p, target = %p, "
1324 "length = %lu%s)\n", source, target, length,
1325 (echo ? ", echo mode" : "")));
1327 // Make sure we are first in the writer queue(s) and AvailableBytes() is
1328 // initialized.
1329 status_t status = locker.AcquireWriter(dontBlock, 0);
1330 if (status != B_OK) {
1331 *_length = 0;
1332 return status;
1334 size_t writable = locker.AvailableBytes();
1335 size_t writtenSinceLastNotify = 0;
1337 while (bytesWritten < length) {
1338 // fetch next char and do input processing
1339 char c;
1340 size_t bytesNeeded;
1341 if (!process_input_char(source, *data, &c, &bytesNeeded)) {
1342 // input char shall be skipped
1343 data++;
1344 bytesWritten++;
1345 continue;
1348 // If in echo mode, we do the output conversion and need to update
1349 // the needed bytes count.
1350 char echoBuffer[3];
1351 size_t echoBytes = 0;
1352 if (echo) {
1353 process_output_char(source, c, echoBuffer, &echoBytes, true);
1354 if (echoBytes > bytesNeeded)
1355 bytesNeeded = echoBytes;
1358 // If there's not enough space to write what we have, we need to wait
1359 // until it is available.
1360 if (writable < bytesNeeded) {
1361 if (writtenSinceLastNotify > 0) {
1362 tty_notify_if_available(target, source, true);
1363 if (echo)
1364 tty_notify_if_available(source, target, true);
1365 writtenSinceLastNotify = 0;
1368 status = locker.AcquireWriter(dontBlock, bytesNeeded);
1369 if (status != B_OK) {
1370 *_length = bytesWritten;
1371 return status;
1374 writable = locker.AvailableBytes();
1376 // XXX: do we need to support VMIN & VTIME for write() ?
1378 // We need to restart the loop, since the termios flags might have
1379 // changed in the meantime (while we've unlocked the tty). Note,
1380 // that we don't re-get "echo" -- maybe we should.
1381 continue;
1384 // write the bytes
1385 tty_input_putc_locked(target, c);
1387 if (echo) {
1388 for (size_t i = 0; i < echoBytes; i++)
1389 line_buffer_putc(source->input_buffer, echoBuffer[i]);
1392 writable -= bytesNeeded;
1393 data++;
1394 bytesWritten++;
1395 writtenSinceLastNotify++;
1398 return B_OK;
1402 static status_t
1403 tty_write_to_tty_slave_unsafe(tty_cookie* sourceCookie, const char* data,
1404 size_t* _length)
1406 struct tty* target = sourceCookie->other_tty;
1407 size_t length = *_length;
1408 size_t bytesWritten = 0;
1409 uint32 mode = sourceCookie->open_mode;
1410 bool dontBlock = (mode & O_NONBLOCK) != 0;
1412 // bail out, if source is already closed
1413 TTYReference sourceTTYReference(sourceCookie);
1414 if (!sourceTTYReference.IsLocked())
1415 return B_FILE_ERROR;
1417 if (length == 0)
1418 return B_OK;
1420 WriterLocker locker(sourceCookie);
1422 // if the target is not open, fail now
1423 if (target->open_count <= 0)
1424 return B_FILE_ERROR;
1426 TRACE(("tty_write_to_tty_slave(source = %p, target = %p, length = %lu)\n",
1427 sourceCookie->tty, target, length));
1429 // Make sure we are first in the writer queue(s) and AvailableBytes() is
1430 // initialized.
1431 status_t status = locker.AcquireWriter(dontBlock, 0);
1432 if (status != B_OK) {
1433 *_length = 0;
1434 return status;
1436 size_t writable = locker.AvailableBytes();
1437 size_t writtenSinceLastNotify = 0;
1439 while (bytesWritten < length) {
1440 // fetch next char and do output processing
1441 char buffer[3];
1442 size_t bytesNeeded;
1443 process_output_char(target, *data, buffer, &bytesNeeded, false);
1445 // If there's not enough space to write what we have, we need to wait
1446 // until it is available.
1447 if (writable < bytesNeeded) {
1448 if (writtenSinceLastNotify > 0) {
1449 tty_notify_if_available(target, sourceCookie->tty, true);
1450 writtenSinceLastNotify = 0;
1453 status = locker.AcquireWriter(dontBlock, bytesNeeded);
1454 if (status != B_OK) {
1455 *_length = bytesWritten;
1456 return status;
1459 writable = locker.AvailableBytes();
1461 // We need to restart the loop, since the termios flags might have
1462 // changed in the meantime (while we've unlocked the tty).
1463 continue;
1466 // write the bytes
1467 for (size_t i = 0; i < bytesNeeded; i++)
1468 line_buffer_putc(target->input_buffer, buffer[i]);
1470 writable -= bytesNeeded;
1471 data++;
1472 bytesWritten++;
1473 writtenSinceLastNotify++;
1476 return B_OK;
1480 static void
1481 dump_tty_settings(struct tty_settings& settings)
1483 kprintf(" pgrp_id: %" B_PRId32 "\n", settings.pgrp_id);
1484 kprintf(" session_id: %" B_PRId32 "\n", settings.session_id);
1486 kprintf(" termios:\n");
1487 kprintf(" c_iflag: 0x%08" B_PRIx32 "\n", settings.termios.c_iflag);
1488 kprintf(" c_oflag: 0x%08" B_PRIx32 "\n", settings.termios.c_oflag);
1489 kprintf(" c_cflag: 0x%08" B_PRIx32 "\n", settings.termios.c_cflag);
1490 kprintf(" c_lflag: 0x%08" B_PRIx32 "\n", settings.termios.c_lflag);
1491 kprintf(" c_line: %d\n", settings.termios.c_line);
1492 kprintf(" c_ispeed: %u\n", settings.termios.c_ispeed);
1493 kprintf(" c_ospeed: %u\n", settings.termios.c_ospeed);
1494 for (int i = 0; i < NCCS; i++)
1495 kprintf(" c_cc[%02d]: %d\n", i, settings.termios.c_cc[i]);
1497 kprintf(" wsize: %u x %u c, %u x %u pxl\n",
1498 settings.window_size.ws_row, settings.window_size.ws_col,
1499 settings.window_size.ws_xpixel, settings.window_size.ws_ypixel);
1503 static void
1504 dump_tty_struct(struct tty& tty)
1506 kprintf(" tty @: %p\n", &tty);
1507 kprintf(" index: %" B_PRId32 "\n", tty.index);
1508 kprintf(" is_master: %s\n", tty.is_master ? "true" : "false");
1509 kprintf(" open_count: %" B_PRId32 "\n", tty.open_count);
1510 kprintf(" select_pool: %p\n", tty.select_pool);
1511 kprintf(" pending_eof: %" B_PRIu32 "\n", tty.pending_eof);
1512 kprintf(" lock: %p\n", tty.lock);
1514 kprintf(" input_buffer:\n");
1515 kprintf(" first: %" B_PRId32 "\n", tty.input_buffer.first);
1516 kprintf(" in: %lu\n", tty.input_buffer.in);
1517 kprintf(" size: %lu\n", tty.input_buffer.size);
1518 kprintf(" buffer: %p\n", tty.input_buffer.buffer);
1520 kprintf(" reader queue:\n");
1521 tty.reader_queue.Dump(" ");
1522 kprintf(" writer queue:\n");
1523 tty.writer_queue.Dump(" ");
1525 kprintf(" cookies: ");
1526 TTYCookieList::Iterator it = tty.cookies.GetIterator();
1527 while (tty_cookie* cookie = it.Next())
1528 kprintf(" %p", cookie);
1529 kprintf("\n");
1533 static int
1534 dump_tty(int argc, char** argv)
1536 if (argc < 2) {
1537 kprintf("Usage: %s <tty index>\n", argv[0]);
1538 return 0;
1541 int32 index = atol(argv[1]);
1542 if (index < 0 || index >= (int32)kNumTTYs) {
1543 kprintf("Invalid tty index.\n");
1544 return 0;
1547 kprintf("master:\n");
1548 dump_tty_struct(gMasterTTYs[index]);
1549 kprintf("slave:\n");
1550 dump_tty_struct(gSlaveTTYs[index]);
1551 kprintf("settings:\n");
1552 dump_tty_settings(gTTYSettings[index]);
1554 return 0;
1558 // #pragma mark - device functions
1561 status_t
1562 tty_close(struct tty* tty)
1564 // destroy the queues
1565 tty->reader_queue.~RequestQueue();
1566 tty->writer_queue.~RequestQueue();
1567 tty->cookies.~TTYCookieList();
1569 uninit_line_buffer(tty->input_buffer);
1571 return B_OK;
1575 status_t
1576 tty_open(struct tty* tty, tty_service_func func)
1578 if (init_line_buffer(tty->input_buffer, TTY_BUFFER_SIZE) < B_OK)
1579 return B_NO_MEMORY;
1581 tty->service_func = func;
1583 // construct the queues
1584 new(&tty->reader_queue) RequestQueue;
1585 new(&tty->writer_queue) RequestQueue;
1586 new(&tty->cookies) TTYCookieList;
1588 return B_OK;
1592 status_t
1593 tty_ioctl(tty_cookie* cookie, uint32 op, void* buffer, size_t length)
1595 struct tty* tty = cookie->tty;
1597 // bail out, if already closed
1598 TTYReference ttyReference(cookie);
1599 if (!ttyReference.IsLocked())
1600 return B_FILE_ERROR;
1602 TRACE(("tty_ioctl: tty %p, op %lu, buffer %p, length %lu\n", tty, op, buffer, length));
1603 MutexLocker locker(tty->lock);
1605 // values marked BeOS are non-standard codes we support for legacy apps
1606 switch (op) {
1607 // blocking/non-blocking mode
1609 case B_SET_BLOCKING_IO:
1610 cookie->open_mode &= ~O_NONBLOCK;
1611 return B_OK;
1612 case B_SET_NONBLOCKING_IO:
1613 cookie->open_mode |= O_NONBLOCK;
1614 return B_OK;
1616 // get and set TTY attributes
1618 case TCGETA:
1619 TRACE(("tty: get attributes\n"));
1620 return user_memcpy(buffer, &tty->settings->termios,
1621 sizeof(struct termios));
1623 case TCSETA:
1624 case TCSETAW:
1625 case TCSETAF:
1626 TRACE(("tty: set attributes (iflag = %lx, oflag = %lx, "
1627 "cflag = %lx, lflag = %lx)\n", tty->settings->termios.c_iflag,
1628 tty->settings->termios.c_oflag, tty->settings->termios.c_cflag,
1629 tty->settings->termios.c_lflag));
1631 return user_memcpy(&tty->settings->termios, buffer,
1632 sizeof(struct termios));
1634 // get and set process group ID
1636 case TIOCGPGRP:
1637 TRACE(("tty: get pgrp_id\n"));
1638 return user_memcpy(buffer, &tty->settings->pgrp_id, sizeof(pid_t));
1640 case TIOCSPGRP:
1641 case 'pgid': // BeOS
1643 TRACE(("tty: set pgrp_id\n"));
1644 pid_t groupID;
1646 if (user_memcpy(&groupID, buffer, sizeof(pid_t)) != B_OK)
1647 return B_BAD_ADDRESS;
1649 status_t error = team_set_foreground_process_group(tty->index,
1650 groupID);
1651 if (error == B_OK)
1652 tty->settings->pgrp_id = groupID;
1653 return error;
1656 // become controlling TTY
1657 case TIOCSCTTY:
1659 TRACE(("tty: become controlling tty\n"));
1660 pid_t processID = getpid();
1661 pid_t sessionID = getsid(processID);
1662 // Only session leaders can become controlling tty
1663 if (processID != sessionID)
1664 return B_NOT_ALLOWED;
1665 // Check if already controlling tty
1666 if (team_get_controlling_tty() == tty->index)
1667 return B_OK;
1668 tty->settings->session_id = sessionID;
1669 tty->settings->pgrp_id = sessionID;
1670 team_set_controlling_tty(tty->index);
1671 return B_OK;
1674 // get and set window size
1676 case TIOCGWINSZ:
1677 TRACE(("tty: get window size\n"));
1678 return user_memcpy(buffer, &tty->settings->window_size,
1679 sizeof(struct winsize));
1681 case TIOCSWINSZ:
1682 case 'wsiz': // BeOS
1684 uint16 oldColumns = tty->settings->window_size.ws_col;
1685 uint16 oldRows = tty->settings->window_size.ws_row;
1687 TRACE(("tty: set window size\n"));
1688 if (user_memcpy(&tty->settings->window_size, buffer,
1689 sizeof(struct winsize)) < B_OK) {
1690 return B_BAD_ADDRESS;
1693 // send a signal only if the window size has changed
1694 if ((oldColumns != tty->settings->window_size.ws_col
1695 || oldRows != tty->settings->window_size.ws_row)
1696 && tty->settings->pgrp_id != 0) {
1697 send_signal(-tty->settings->pgrp_id, SIGWINCH);
1700 return B_OK;
1703 case B_IOCTL_GET_TTY_INDEX:
1704 if (user_memcpy(buffer, &tty->index, sizeof(int32)) < B_OK)
1705 return B_BAD_ADDRESS;
1707 return B_OK;
1709 case B_IOCTL_GRANT_TTY:
1711 if (!tty->is_master)
1712 return B_BAD_VALUE;
1714 // get slave path
1715 char path[64];
1716 snprintf(path, sizeof(path), "/dev/%s",
1717 gDeviceNames[kNumTTYs + tty->index]);
1719 // set owner and permissions respectively
1720 if (chown(path, getuid(), getgid()) != 0
1721 || chmod(path, S_IRUSR | S_IWUSR | S_IWGRP) != 0) {
1722 return errno;
1725 return B_OK;
1728 case 'ichr': // BeOS (int*) (pre- select() support)
1730 int wanted;
1731 int toRead;
1733 // help identify apps using it
1734 //dprintf("tty: warning: legacy BeOS opcode 'ichr'\n");
1736 if (user_memcpy(&wanted, buffer, sizeof(int)) != B_OK)
1737 return B_BAD_ADDRESS;
1739 // release the mutex and grab a read lock
1740 locker.Unlock();
1741 ReaderLocker readLocker(cookie);
1743 bigtime_t timeout = wanted == 0 ? 0 : B_INFINITE_TIMEOUT;
1745 // TODO: If wanted is > the TTY buffer size, this loop cannot work
1746 // correctly. Refactor the read code!
1747 do {
1748 status_t status = readLocker.AcquireReader(timeout, wanted);
1749 if (status != B_OK)
1750 return status;
1752 toRead = readLocker.AvailableBytes();
1753 } while (toRead < wanted);
1755 if (user_memcpy(buffer, &toRead, sizeof(int)) != B_OK)
1756 return B_BAD_ADDRESS;
1758 return B_OK;
1761 case FIONREAD:
1763 int toRead = 0;
1765 // release the mutex and grab a read lock
1766 locker.Unlock();
1767 ReaderLocker readLocker(cookie);
1769 status_t status = readLocker.AcquireReader(0, 1);
1770 if (status == B_OK)
1771 toRead = readLocker.AvailableBytes();
1772 else if (status != B_WOULD_BLOCK)
1773 return status;
1775 if (user_memcpy(buffer, &toRead, sizeof(int)) != B_OK)
1776 return B_BAD_ADDRESS;
1778 return B_OK;
1781 case TCWAITEVENT: // BeOS (uint*)
1782 // wait for event (timeout if !NULL)
1783 case TCVTIME: // BeOS (bigtime_t*) set highrez VTIME
1784 case 'ochr': // BeOS (int*) same as ichr for write
1785 dprintf("tty: unsupported legacy opcode %" B_PRIu32 "\n", op);
1786 // TODO ?
1787 break;
1789 case TCXONC: // Unix, but even Linux doesn't handle it
1790 //dprintf("tty: unsupported TCXONC\n");
1791 break;
1793 case TCQUERYCONNECTED: // BeOS
1794 dprintf("tty: unsupported legacy opcode %" B_PRIu32 "\n", op);
1795 // BeOS didn't implement them anyway
1796 break;
1798 case TCSETDTR:
1799 case TCSETRTS:
1800 case TCGETBITS:
1801 // TODO: should call the driver service func here,
1802 // for non-virtual tty.
1803 // return tty->driver->service();
1804 break;
1807 TRACE(("tty: unsupported opcode %lu\n", op));
1808 return B_BAD_VALUE;
1812 status_t
1813 tty_input_read(tty_cookie* cookie, void* _buffer, size_t* _length)
1815 char* buffer = (char*)_buffer;
1816 struct tty* tty = cookie->tty;
1817 uint32 mode = cookie->open_mode;
1818 bool dontBlock = (mode & O_NONBLOCK) != 0;
1819 size_t length = *_length;
1820 bool canon = true;
1821 bigtime_t timeout = dontBlock ? 0 : B_INFINITE_TIMEOUT;
1822 bigtime_t interCharTimeout = 0;
1823 size_t bytesNeeded = 1;
1825 TRACE(("tty_input_read(tty = %p, length = %lu, mode = %lu)\n", tty, length, mode));
1827 if (length == 0)
1828 return B_OK;
1830 // bail out, if the TTY is already closed
1831 TTYReference ttyReference(cookie);
1832 if (!ttyReference.IsLocked())
1833 return B_FILE_ERROR;
1835 ReaderLocker locker(cookie);
1837 // handle raw mode
1838 if ((!tty->is_master) && ((tty->settings->termios.c_lflag & ICANON) == 0)) {
1839 canon = false;
1840 if (!dontBlock) {
1841 // Non-blocking mode. Handle VMIN and VTIME.
1842 bytesNeeded = tty->settings->termios.c_cc[VMIN];
1843 bigtime_t vtime = tty->settings->termios.c_cc[VTIME] * 100000;
1844 TRACE(("tty_input_read: icanon vmin %lu, vtime %Ldus\n", bytesNeeded,
1845 vtime));
1847 if (bytesNeeded == 0) {
1848 // In this case VTIME specifies a relative total timeout. We
1849 // have no inter-char timeout, though.
1850 timeout = vtime;
1851 } else {
1852 // VTIME specifies the inter-char timeout. 0 is indefinitely.
1853 if (vtime == 0)
1854 interCharTimeout = B_INFINITE_TIMEOUT;
1855 else
1856 interCharTimeout = vtime;
1858 if (bytesNeeded > length)
1859 bytesNeeded = length;
1864 status_t status;
1865 *_length = 0;
1867 do {
1868 TRACE(("tty_input_read: AcquireReader(%Ldus, %ld)\n", timeout,
1869 bytesNeeded));
1870 status = locker.AcquireReader(timeout, bytesNeeded);
1871 if (status != B_OK)
1872 break;
1874 size_t toRead = locker.AvailableBytes();
1875 if (toRead > length)
1876 toRead = length;
1878 bool _hitEOF = false;
1879 bool* hitEOF = canon && tty->pending_eof > 0 ? &_hitEOF : NULL;
1881 ssize_t bytesRead = line_buffer_user_read(tty->input_buffer, buffer,
1882 toRead, tty->settings->termios.c_cc[VEOF], hitEOF);
1883 if (bytesRead < 0) {
1884 status = bytesRead;
1885 break;
1888 buffer += bytesRead;
1889 length -= bytesRead;
1890 *_length += bytesRead;
1891 bytesNeeded = (size_t)bytesRead > bytesNeeded
1892 ? 0 : bytesNeeded - bytesRead;
1894 // we hit an EOF char -- bail out, whatever amount of data we have
1895 if (hitEOF && *hitEOF) {
1896 tty->pending_eof--;
1897 break;
1900 // Once we have read something reset the timeout to the inter-char
1901 // timeout, if applicable.
1902 if (!dontBlock && !canon && *_length > 0)
1903 timeout = interCharTimeout;
1904 } while (bytesNeeded > 0);
1906 if (status == B_WOULD_BLOCK || status == B_TIMED_OUT) {
1907 // In non-blocking non-canonical-input-processing mode never return
1908 // timeout errors. Just return 0, if nothing has been read.
1909 if (!dontBlock && !canon)
1910 status = B_OK;
1913 return *_length == 0 ? status : B_OK;
1917 status_t
1918 tty_write_to_tty_master(tty_cookie* sourceCookie, const void* _buffer,
1919 size_t* _length)
1921 const char* buffer = (const char*)_buffer;
1922 size_t bytesRemaining = *_length;
1923 *_length = 0;
1925 while (bytesRemaining > 0) {
1926 // copy data to stack
1927 char safeBuffer[256];
1928 size_t toWrite = min_c(sizeof(safeBuffer), bytesRemaining);
1929 status_t error = user_memcpy(safeBuffer, buffer, toWrite);
1930 if (error != B_OK)
1931 return error;
1933 // write them
1934 size_t written = toWrite;
1935 error = tty_write_to_tty_master_unsafe(sourceCookie, safeBuffer,
1936 &written);
1937 if (error != B_OK)
1938 return error;
1940 buffer += written;
1941 bytesRemaining -= written;
1942 *_length += written;
1944 if (written < toWrite)
1945 return B_OK;
1948 return B_OK;
1952 status_t
1953 tty_write_to_tty_slave(tty_cookie* sourceCookie, const void* _buffer,
1954 size_t* _length)
1956 const char* buffer = (const char*)_buffer;
1957 size_t bytesRemaining = *_length;
1958 *_length = 0;
1960 while (bytesRemaining > 0) {
1961 // copy data to stack
1962 char safeBuffer[256];
1963 size_t toWrite = min_c(sizeof(safeBuffer), bytesRemaining);
1964 status_t error = user_memcpy(safeBuffer, buffer, toWrite);
1965 if (error != B_OK)
1966 return error;
1968 // write them
1969 size_t written = toWrite;
1970 error = tty_write_to_tty_slave_unsafe(sourceCookie, safeBuffer,
1971 &written);
1972 if (error != B_OK)
1973 return error;
1975 buffer += written;
1976 bytesRemaining -= written;
1977 *_length += written;
1979 if (written < toWrite)
1980 return B_OK;
1983 return B_OK;
1987 status_t
1988 tty_select(tty_cookie* cookie, uint8 event, uint32 ref, selectsync* sync)
1990 struct tty* tty = cookie->tty;
1992 TRACE(("tty_select(cookie = %p, event = %u, ref = %lu, sync = %p)\n",
1993 cookie, event, ref, sync));
1995 // we don't support all kinds of events
1996 if (event < B_SELECT_READ || event > B_SELECT_ERROR)
1997 return B_BAD_VALUE;
1999 // if the TTY is already closed, we notify immediately
2000 TTYReference ttyReference(cookie);
2001 if (!ttyReference.IsLocked()) {
2002 TRACE(("tty_select() done: cookie %p already closed\n", cookie));
2004 notify_select_event(sync, event);
2005 return B_OK;
2008 // lock the TTY (allows us to freely access the cookie lists of this and
2009 // the other TTY)
2010 MutexLocker ttyLocker(tty->lock);
2012 // get the other TTY -- needed for `write' events
2013 struct tty* otherTTY = cookie->other_tty;
2014 if (otherTTY->open_count <= 0)
2015 otherTTY = NULL;
2017 // add the event to the TTY's pool
2018 status_t error = add_select_sync_pool_entry(&tty->select_pool, sync, event);
2019 if (error != B_OK) {
2020 TRACE(("tty_select() done: add_select_sync_pool_entry() failed: %lx\n",
2021 error));
2023 return error;
2026 // finally also acquire the request mutex, for access to the reader/writer
2027 // queues
2028 RecursiveLocker requestLocker(gTTYRequestLock);
2030 // check, if the event is already present
2031 switch (event) {
2032 case B_SELECT_READ:
2033 if (tty->reader_queue.IsEmpty() && tty_readable(tty) > 0)
2034 notify_select_event(sync, event);
2035 break;
2037 case B_SELECT_WRITE:
2039 // writes go to the other TTY
2040 if (!otherTTY) {
2041 notify_select_event(sync, event);
2042 break;
2045 // In case input is echoed, we have to check, whether we can
2046 // currently can write to our TTY as well.
2047 bool echo = (tty->is_master
2048 && tty->settings->termios.c_lflag & ECHO);
2050 if (otherTTY->writer_queue.IsEmpty()
2051 && line_buffer_writable(otherTTY->input_buffer) > 0) {
2052 if (!echo
2053 || (tty->writer_queue.IsEmpty()
2054 && line_buffer_writable(tty->input_buffer) > 0)) {
2055 notify_select_event(sync, event);
2058 break;
2061 case B_SELECT_ERROR:
2062 default:
2063 break;
2066 return B_OK;
2070 status_t
2071 tty_deselect(tty_cookie* cookie, uint8 event, selectsync* sync)
2073 struct tty* tty = cookie->tty;
2075 TRACE(("tty_deselect(cookie = %p, event = %u, sync = %p)\n", cookie, event,
2076 sync));
2078 // we don't support all kinds of events
2079 if (event < B_SELECT_READ || event > B_SELECT_ERROR)
2080 return B_BAD_VALUE;
2082 // lock the TTY (guards the select sync pool, among other things)
2083 MutexLocker ttyLocker(tty->lock);
2085 return remove_select_sync_pool_entry(&tty->select_pool, sync, event);
2089 void
2090 tty_add_debugger_commands()
2092 add_debugger_command("tty", &dump_tty, "Dump info on a tty");
2096 void
2097 tty_remove_debugger_commands()
2099 remove_debugger_command("tty", &dump_tty);