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
80static void tty_notify_select_event(struct tty* tty, uint8 event);
81static void tty_notify_if_available(struct tty* tty, struct tty* otherTTY,
82	bool notifySelect);
83
84
85class AbstractLocker {
86public:
87	AbstractLocker(tty_cookie* cookie)
88		:
89		fCookie(cookie),
90		fBytes(0)
91	{
92	}
93
94	size_t AvailableBytes() const
95		{ return fBytes; }
96
97protected:
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
108class WriterLocker : public AbstractLocker {
109public:
110								WriterLocker(tty_cookie* sourceCookie);
111								~WriterLocker();
112
113			status_t			AcquireWriter(bool dontBlock,
114									size_t bytesNeeded);
115
116private:
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
127class ReaderLocker : public AbstractLocker {
128public:
129								ReaderLocker(tty_cookie* cookie);
130								~ReaderLocker();
131
132			status_t			AcquireReader(bigtime_t timeout,
133									size_t bytesNeeded);
134
135private:
136			size_t				_CheckAvailableBytes() const;
137			status_t			_CheckBackgroundRead() const;
138
139			struct tty*			fTTY;
140			RequestOwner		fRequestOwner;
141};
142
143
144class TTYReferenceLocking {
145public:
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
177typedef AutoLocker<tty_cookie, TTYReferenceLocking> TTYReference;
178
179
180// #pragma mark -
181
182
183Request::Request()
184	:
185	fOwner(NULL),
186	fCookie(NULL),
187	fBytesNeeded(0),
188	fNotified(false),
189	fError(false)
190{
191}
192
193
194void
195Request::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
205void
206Request::Notify(size_t bytesAvailable)
207{
208	if (!fNotified && bytesAvailable >= fBytesNeeded && fOwner) {
209		fOwner->Notify(this);
210		fNotified = true;
211	}
212}
213
214
215void
216Request::NotifyError(status_t error)
217{
218	if (!fError && fOwner) {
219		fOwner->NotifyError(this, error);
220		fError = true;
221		fNotified = true;
222	}
223}
224
225
226void
227Request::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
241RequestQueue::RequestQueue()
242	:
243	fRequests()
244{
245}
246
247
248void
249RequestQueue::Add(Request* request)
250{
251	if (request) {
252		RecursiveLocker _(gTTYRequestLock);
253
254		fRequests.Add(request, true);
255	}
256}
257
258
259void
260RequestQueue::Remove(Request* request)
261{
262	if (request) {
263		RecursiveLocker _(gTTYRequestLock);
264
265		fRequests.Remove(request);
266	}
267}
268
269
270void
271RequestQueue::NotifyFirst(size_t bytesAvailable)
272{
273	RecursiveLocker _(gTTYRequestLock);
274
275	if (Request* first = First())
276		first->Notify(bytesAvailable);
277}
278
279
280void
281RequestQueue::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
292void
293RequestQueue::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
305void
306RequestQueue::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
317RequestOwner::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*/
331void
332RequestOwner::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*/
359void
360RequestOwner::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
374void
375RequestOwner::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*/
387status_t
388RequestOwner::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
435bool
436RequestOwner::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
449void
450RequestOwner::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
471void
472RequestOwner::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
491WriterLocker::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
525WriterLocker::~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
544size_t
545WriterLocker::_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
558status_t
559WriterLocker::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
613status_t
614WriterLocker::_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
636ReaderLocker::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
650ReaderLocker::~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
668status_t
669ReaderLocker::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
717size_t
718ReaderLocker::_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
732status_t
733ReaderLocker::_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
753static void
754reset_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
780static void
781reset_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*/
801static void
802tty_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
874static int32
875tty_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 */
894static void
895tty_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*/
914static void
915tty_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*/
955static bool
956process_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*/
1011static void
1012process_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
1070static status_t
1071tty_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 *_length == 0 ? status : B_OK;
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
1176static status_t
1177tty_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 *_length == 0 ? status : B_OK;
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
1254static status_t
1255tty_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
1289static status_t
1290tty_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 status = user_memcpy(safeBuffer, buffer, toWrite);
1302		if (status != B_OK)
1303			return status;
1304
1305		// write them
1306		size_t written = toWrite;
1307		status = tty_write_to_tty_slave_unsafe(sourceCookie, safeBuffer,
1308			&written);
1309		if (status != B_OK)
1310			return *_length == 0 ? status : B_OK;
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
1327status_t
1328tty_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
1380void
1381tty_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
1398status_t
1399tty_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
1439void
1440tty_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
1540void
1541tty_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
1554status_t
1555tty_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
1664status_t
1665tty_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
1674status_t
1675tty_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
1990status_t
1991tty_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
2073status_t
2074tty_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
2092status_t
2093tty_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