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 = %" B_PRId32 " (name = %s)\n", index,
49		name));
50
51	MutexLocker globalLocker(gGlobalTTYLock);
52
53	// we may only be used if our master has already been opened
54	if (gMasterTTYs[index].open_count == 0)
55		return B_IO_ERROR;
56
57	bool makeControllingTTY = (flags & O_NOCTTY) == 0;
58	pid_t processID = getpid();
59	pid_t sessionID = getsid(processID);
60
61	if (gSlaveTTYs[index].open_count == 0) {
62		// We only allow session leaders to open the tty initially.
63		if (makeControllingTTY && processID != sessionID)
64			return B_NOT_ALLOWED;
65
66		status_t status = tty_open(&gSlaveTTYs[index], NULL);
67		if (status < B_OK) {
68			// initializing TTY failed
69			return status;
70		}
71	} else if (makeControllingTTY) {
72		// If already open, we allow only processes from the same session
73		// to open the tty again.
74		pid_t ttySession = gSlaveTTYs[index].settings->session_id;
75		if (ttySession >= 0) {
76			if (ttySession != sessionID)
77				return B_NOT_ALLOWED;
78			makeControllingTTY = false;
79		} else {
80			// The tty is not associated with a session yet. The process needs
81			// to be a session leader.
82			if (makeControllingTTY && processID != sessionID)
83				return B_NOT_ALLOWED;
84		}
85	}
86
87 	slave_cookie *cookie = (slave_cookie *)malloc(sizeof(struct slave_cookie));
88	if (cookie == NULL) {
89		if (gSlaveTTYs[index].open_count == 0)
90			tty_close(&gSlaveTTYs[index]);
91
92		return B_NO_MEMORY;
93	}
94
95	status_t status = init_tty_cookie(cookie, &gSlaveTTYs[index],
96		&gMasterTTYs[index], flags);
97	if (status != B_OK) {
98		free(cookie);
99
100		if (gSlaveTTYs[index].open_count == 0)
101			tty_close(&gSlaveTTYs[index]);
102
103		return status;
104	}
105
106	if (gSlaveTTYs[index].open_count == 0) {
107		gSlaveTTYs[index].lock = gMasterTTYs[index].lock;
108		gSlaveTTYs[index].settings->session_id = -1;
109		gSlaveTTYs[index].settings->pgrp_id = -1;
110	}
111
112	if (makeControllingTTY) {
113		gSlaveTTYs[index].settings->session_id = sessionID;
114		gSlaveTTYs[index].settings->pgrp_id = sessionID;
115		team_set_controlling_tty(gSlaveTTYs[index].index);
116	}
117
118	add_tty_cookie(cookie);
119
120	*_cookie = cookie;
121
122	return B_OK;
123}
124
125
126static status_t
127slave_close(void *_cookie)
128{
129	slave_cookie *cookie = (slave_cookie *)_cookie;
130
131	TRACE(("slave_close: cookie %p\n", _cookie));
132
133	MutexLocker globalLocker(gGlobalTTYLock);
134
135	// unblock and wait for all blocking operations
136	tty_close_cookie(cookie);
137
138	return B_OK;
139}
140
141
142static status_t
143slave_free_cookie(void *_cookie)
144{
145	// The TTY is already closed. We only have to free the cookie.
146	slave_cookie *cookie = (slave_cookie *)_cookie;
147
148	TRACE(("slave_free_cookie: cookie %p\n", _cookie));
149
150	MutexLocker globalLocker(gGlobalTTYLock);
151	uninit_tty_cookie(cookie);
152	globalLocker.Unlock();
153
154	free(cookie);
155
156	return B_OK;
157}
158
159
160static status_t
161slave_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
162{
163	slave_cookie *cookie = (slave_cookie *)_cookie;
164
165	TRACE(("slave_ioctl: cookie %p, op %" B_PRIu32 ", buffer %p, length %lu\n",
166		_cookie, op, buffer, length));
167
168	return tty_ioctl(cookie, op, buffer, length);
169}
170
171
172static status_t
173slave_read(void *_cookie, off_t offset, void *buffer, size_t *_length)
174{
175	slave_cookie *cookie = (slave_cookie *)_cookie;
176
177	TRACE(("slave_read: cookie %p, offset %" B_PRIdOFF ", buffer %p, length "
178		"%lu\n", _cookie, offset, buffer, *_length));
179
180	status_t result = tty_input_read(cookie, buffer, _length);
181
182	TRACE(("slave_read done: cookie %p, result %" B_PRIx32 ", length %lu\n",
183		_cookie, result, *_length));
184
185	return result;
186}
187
188
189static status_t
190slave_write(void *_cookie, off_t offset, const void *buffer, size_t *_length)
191{
192	slave_cookie *cookie = (slave_cookie *)_cookie;
193
194	TRACE(("slave_write: cookie %p, offset %" B_PRIdOFF", buffer %p, length "
195		"%lu\n", _cookie, offset, buffer, *_length));
196
197	status_t result = tty_write_to_tty_slave(cookie, buffer, _length);
198
199	TRACE(("slave_write done: cookie %p, result %" B_PRIx32 ", length %lu\n",
200		_cookie, result, *_length));
201
202	return result;
203}
204
205
206static status_t
207slave_select(void *_cookie, uint8 event, uint32 ref, selectsync *sync)
208{
209	slave_cookie *cookie = (slave_cookie *)_cookie;
210
211	return tty_select(cookie, event, ref, sync);
212}
213
214
215static status_t
216slave_deselect(void *_cookie, uint8 event, selectsync *sync)
217{
218	slave_cookie *cookie = (slave_cookie *)_cookie;
219
220	return tty_deselect(cookie, event, sync);
221}
222
223
224device_hooks gSlaveTTYHooks = {
225	&slave_open,
226	&slave_close,
227	&slave_free_cookie,
228	&slave_ioctl,
229	&slave_read,
230	&slave_write,
231	&slave_select,
232	&slave_deselect,
233	NULL,	// read_pages()
234	NULL	// write_pages()
235};
236