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