1/*
2** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3** Distributed under the terms of the Haiku License.
4*/
5
6
7#include <stdlib.h>
8#include <string.h>
9
10#include <util/AutoLock.h>
11
12#include <team.h>
13
14#include "tty_private.h"
15
16
17//#define SLAVE_TRACE
18#ifdef SLAVE_TRACE
19#	define TRACE(x) dprintf x
20#else
21#	define TRACE(x)
22#endif
23
24
25struct slave_cookie : tty_cookie {
26};
27
28
29struct tty gSlaveTTYs[kNumTTYs];
30
31
32static status_t
33slave_open(const char *name, uint32 flags, void **_cookie)
34{
35	// Get the tty index: Opening "/dev/tty" means opening the process'
36	// controlling tty.
37	int32 index = get_tty_index(name);
38	if (strcmp(name, "tty") == 0) {
39		index = team_get_controlling_tty();
40		if (index < 0)
41			return B_NOT_ALLOWED;
42	} else {
43		index = get_tty_index(name);
44		if (index >= (int32)kNumTTYs)
45			return B_ERROR;
46	}
47
48	TRACE(("slave_open: TTY index = %ld (name = %s)\n", index, name));
49
50	MutexLocker globalLocker(gGlobalTTYLock);
51
52	// we may only be used if our master has already been opened
53	if (gMasterTTYs[index].open_count == 0)
54		return B_IO_ERROR;
55
56	bool makeControllingTTY = (flags & O_NOCTTY) == 0;
57	pid_t processID = getpid();
58	pid_t sessionID = getsid(processID);
59
60	if (gSlaveTTYs[index].open_count == 0) {
61		// We only allow session leaders to open the tty initially.
62		if (makeControllingTTY && processID != sessionID)
63			return B_NOT_ALLOWED;
64
65		status_t status = tty_open(&gSlaveTTYs[index], NULL);
66		if (status < B_OK) {
67			// initializing TTY failed
68			return status;
69		}
70	} else if (makeControllingTTY) {
71		// If already open, we allow only processes from the same session
72		// to open the tty again.
73		pid_t ttySession = gSlaveTTYs[index].settings->session_id;
74		if (ttySession >= 0) {
75			if (ttySession != sessionID)
76				return B_NOT_ALLOWED;
77			makeControllingTTY = false;
78		} else {
79			// The tty is not associated with a session yet. The process needs
80			// to be a session leader.
81			if (makeControllingTTY && processID != sessionID)
82				return B_NOT_ALLOWED;
83		}
84	}
85
86 	slave_cookie *cookie = (slave_cookie *)malloc(sizeof(struct slave_cookie));
87	if (cookie == NULL) {
88		if (gSlaveTTYs[index].open_count == 0)
89			tty_close(&gSlaveTTYs[index]);
90
91		return B_NO_MEMORY;
92	}
93
94	status_t status = init_tty_cookie(cookie, &gSlaveTTYs[index],
95		&gMasterTTYs[index], flags);
96	if (status != B_OK) {
97		free(cookie);
98
99		if (gSlaveTTYs[index].open_count == 0)
100			tty_close(&gSlaveTTYs[index]);
101
102		return status;
103	}
104
105	if (gSlaveTTYs[index].open_count == 0) {
106		gSlaveTTYs[index].lock = gMasterTTYs[index].lock;
107		gSlaveTTYs[index].settings->session_id = -1;
108		gSlaveTTYs[index].settings->pgrp_id = -1;
109	}
110
111	if (makeControllingTTY) {
112		gSlaveTTYs[index].settings->session_id = sessionID;
113		gSlaveTTYs[index].settings->pgrp_id = sessionID;
114		team_set_controlling_tty(gSlaveTTYs[index].index);
115	}
116
117	add_tty_cookie(cookie);
118
119	*_cookie = cookie;
120
121	return B_OK;
122}
123
124
125static status_t
126slave_close(void *_cookie)
127{
128	slave_cookie *cookie = (slave_cookie *)_cookie;
129
130	TRACE(("slave_close: cookie %p\n", _cookie));
131
132	MutexLocker globalLocker(gGlobalTTYLock);
133
134	// unblock and wait for all blocking operations
135	tty_close_cookie(cookie);
136
137	return B_OK;
138}
139
140
141static status_t
142slave_free_cookie(void *_cookie)
143{
144	// The TTY is already closed. We only have to free the cookie.
145	slave_cookie *cookie = (slave_cookie *)_cookie;
146
147	TRACE(("slave_free_cookie: cookie %p\n", _cookie));
148
149	MutexLocker globalLocker(gGlobalTTYLock);
150	uninit_tty_cookie(cookie);
151	globalLocker.Unlock();
152
153	free(cookie);
154
155	return B_OK;
156}
157
158
159static status_t
160slave_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
161{
162	slave_cookie *cookie = (slave_cookie *)_cookie;
163
164	TRACE(("slave_ioctl: cookie %p, op %lu, buffer %p, length %lu\n", _cookie, op, buffer, length));
165
166	return tty_ioctl(cookie, op, buffer, length);
167}
168
169
170static status_t
171slave_read(void *_cookie, off_t offset, void *buffer, size_t *_length)
172{
173	slave_cookie *cookie = (slave_cookie *)_cookie;
174
175	TRACE(("slave_read: cookie %p, offset %Ld, buffer %p, length %lu\n",
176		_cookie, offset, buffer, *_length));
177
178	status_t result = tty_input_read(cookie, buffer, _length);
179
180	TRACE(("slave_read done: cookie %p, result %lx, length %lu\n",
181		_cookie, result, *_length));
182
183	return result;
184}
185
186
187static status_t
188slave_write(void *_cookie, off_t offset, const void *buffer, size_t *_length)
189{
190	slave_cookie *cookie = (slave_cookie *)_cookie;
191
192	TRACE(("slave_write: cookie %p, offset %Ld, buffer %p, length %lu\n",
193		_cookie, offset, buffer, *_length));
194
195	status_t result = tty_write_to_tty_slave(cookie, buffer, _length);
196
197	TRACE(("slave_write done: cookie %p, result %lx, length %lu\n",
198		_cookie, result, *_length));
199
200	return result;
201}
202
203
204static status_t
205slave_select(void *_cookie, uint8 event, uint32 ref, selectsync *sync)
206{
207	slave_cookie *cookie = (slave_cookie *)_cookie;
208
209	return tty_select(cookie, event, ref, sync);
210}
211
212
213static status_t
214slave_deselect(void *_cookie, uint8 event, selectsync *sync)
215{
216	slave_cookie *cookie = (slave_cookie *)_cookie;
217
218	return tty_deselect(cookie, event, sync);
219}
220
221
222device_hooks gSlaveTTYHooks = {
223	&slave_open,
224	&slave_close,
225	&slave_free_cookie,
226	&slave_ioctl,
227	&slave_read,
228	&slave_write,
229	&slave_select,
230	&slave_deselect,
231	NULL,	// read_pages()
232	NULL	// write_pages()
233};
234