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