xref: /haiku/src/add-ons/kernel/drivers/pty/driver.cpp (revision 88306be95c0ae957a30cda77c47a1b3c9b9c6957)
10c2a5bb5SAugustin Cavalier /*
20c2a5bb5SAugustin Cavalier  * Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
30c2a5bb5SAugustin Cavalier  * Copyright 2023, Haiku, Inc. All rights reserved.
40c2a5bb5SAugustin Cavalier  * Distributed under the terms of the MIT License.
50c2a5bb5SAugustin Cavalier  */
60c2a5bb5SAugustin Cavalier 
70c2a5bb5SAugustin Cavalier #include <new>
80c2a5bb5SAugustin Cavalier 
90c2a5bb5SAugustin Cavalier #include <stdio.h>
100c2a5bb5SAugustin Cavalier #include <stdlib.h>
110c2a5bb5SAugustin Cavalier #include <string.h>
120c2a5bb5SAugustin Cavalier #include <errno.h>
130c2a5bb5SAugustin Cavalier 
140c2a5bb5SAugustin Cavalier #include <util/AutoLock.h>
150c2a5bb5SAugustin Cavalier #include <Drivers.h>
160c2a5bb5SAugustin Cavalier 
170c2a5bb5SAugustin Cavalier #include <team.h>
180c2a5bb5SAugustin Cavalier 
190c2a5bb5SAugustin Cavalier extern "C" {
200c2a5bb5SAugustin Cavalier #include <drivers/tty.h>
210c2a5bb5SAugustin Cavalier #include <tty_module.h>
220c2a5bb5SAugustin Cavalier }
230c2a5bb5SAugustin Cavalier #include "tty_private.h"
240c2a5bb5SAugustin Cavalier 
250c2a5bb5SAugustin Cavalier 
260c2a5bb5SAugustin Cavalier //#define PTY_TRACE
270c2a5bb5SAugustin Cavalier #ifdef PTY_TRACE
280c2a5bb5SAugustin Cavalier #	define TRACE(x) dprintf x
290c2a5bb5SAugustin Cavalier #else
300c2a5bb5SAugustin Cavalier #	define TRACE(x)
310c2a5bb5SAugustin Cavalier #endif
320c2a5bb5SAugustin Cavalier 
330c2a5bb5SAugustin Cavalier #define DRIVER_NAME "pty"
340c2a5bb5SAugustin Cavalier 
350c2a5bb5SAugustin Cavalier 
360c2a5bb5SAugustin Cavalier int32 api_version = B_CUR_DRIVER_API_VERSION;
370c2a5bb5SAugustin Cavalier tty_module_info *gTTYModule = NULL;
380c2a5bb5SAugustin Cavalier 
390c2a5bb5SAugustin Cavalier struct mutex gGlobalTTYLock;
400c2a5bb5SAugustin Cavalier 
410c2a5bb5SAugustin Cavalier static const uint32 kNumTTYs = 64;
420c2a5bb5SAugustin Cavalier char *gDeviceNames[kNumTTYs * 2 + 3];
430c2a5bb5SAugustin Cavalier 	// reserve space for "pt/" and "tt/" entries, "ptmx", "tty",
440c2a5bb5SAugustin Cavalier 	// and the terminating NULL
450c2a5bb5SAugustin Cavalier 
460c2a5bb5SAugustin Cavalier struct tty* gMasterTTYs[kNumTTYs];
470c2a5bb5SAugustin Cavalier struct tty* gSlaveTTYs[kNumTTYs];
480c2a5bb5SAugustin Cavalier 
490c2a5bb5SAugustin Cavalier extern device_hooks gMasterPTYHooks, gSlavePTYHooks;
500c2a5bb5SAugustin Cavalier 
510c2a5bb5SAugustin Cavalier 
520c2a5bb5SAugustin Cavalier status_t
530c2a5bb5SAugustin Cavalier init_hardware(void)
540c2a5bb5SAugustin Cavalier {
550c2a5bb5SAugustin Cavalier 	TRACE((DRIVER_NAME ": init_hardware()\n"));
560c2a5bb5SAugustin Cavalier 	return B_OK;
570c2a5bb5SAugustin Cavalier }
580c2a5bb5SAugustin Cavalier 
590c2a5bb5SAugustin Cavalier 
600c2a5bb5SAugustin Cavalier status_t
610c2a5bb5SAugustin Cavalier init_driver(void)
620c2a5bb5SAugustin Cavalier {
630c2a5bb5SAugustin Cavalier 	status_t status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
640c2a5bb5SAugustin Cavalier 	if (status < B_OK)
650c2a5bb5SAugustin Cavalier 		return status;
660c2a5bb5SAugustin Cavalier 
670c2a5bb5SAugustin Cavalier 	TRACE((DRIVER_NAME ": init_driver()\n"));
680c2a5bb5SAugustin Cavalier 
690c2a5bb5SAugustin Cavalier 	mutex_init(&gGlobalTTYLock, "tty global");
700c2a5bb5SAugustin Cavalier 	memset(gDeviceNames, 0, sizeof(gDeviceNames));
710c2a5bb5SAugustin Cavalier 	memset(gMasterTTYs, 0, sizeof(gMasterTTYs));
720c2a5bb5SAugustin Cavalier 	memset(gSlaveTTYs, 0, sizeof(gSlaveTTYs));
730c2a5bb5SAugustin Cavalier 
740c2a5bb5SAugustin Cavalier 	// create driver name array
750c2a5bb5SAugustin Cavalier 
760c2a5bb5SAugustin Cavalier 	char letter = 'p';
770c2a5bb5SAugustin Cavalier 	int8 digit = 0;
780c2a5bb5SAugustin Cavalier 
790c2a5bb5SAugustin Cavalier 	for (uint32 i = 0; i < kNumTTYs; i++) {
800c2a5bb5SAugustin Cavalier 		// For compatibility, we have to create the same mess in /dev/pt and
810c2a5bb5SAugustin Cavalier 		// /dev/tt as BeOS does: we publish devices p0, p1, ..., pf, r1, ...,
820c2a5bb5SAugustin Cavalier 		// sf. It would be nice if we could drop compatibility and create
830c2a5bb5SAugustin Cavalier 		// something better. In fact we already don't need the master devices
840c2a5bb5SAugustin Cavalier 		// anymore, since "/dev/ptmx" does the job. The slaves entries could
850c2a5bb5SAugustin Cavalier 		// be published on the fly when a master is opened (e.g via
860c2a5bb5SAugustin Cavalier 		// vfs_create_special_node()).
870c2a5bb5SAugustin Cavalier 		char buffer[64];
880c2a5bb5SAugustin Cavalier 
890c2a5bb5SAugustin Cavalier 		snprintf(buffer, sizeof(buffer), "pt/%c%x", letter, digit);
900c2a5bb5SAugustin Cavalier 		gDeviceNames[i] = strdup(buffer);
910c2a5bb5SAugustin Cavalier 
920c2a5bb5SAugustin Cavalier 		snprintf(buffer, sizeof(buffer), "tt/%c%x", letter, digit);
930c2a5bb5SAugustin Cavalier 		gDeviceNames[i + kNumTTYs] = strdup(buffer);
940c2a5bb5SAugustin Cavalier 
950c2a5bb5SAugustin Cavalier 		if (++digit > 15)
960c2a5bb5SAugustin Cavalier 			digit = 0, letter++;
970c2a5bb5SAugustin Cavalier 
980c2a5bb5SAugustin Cavalier 		if (!gDeviceNames[i] || !gDeviceNames[i + kNumTTYs]) {
990c2a5bb5SAugustin Cavalier 			uninit_driver();
1000c2a5bb5SAugustin Cavalier 			return B_NO_MEMORY;
1010c2a5bb5SAugustin Cavalier 		}
1020c2a5bb5SAugustin Cavalier 	}
1030c2a5bb5SAugustin Cavalier 
1040c2a5bb5SAugustin Cavalier 	gDeviceNames[2 * kNumTTYs] = (char *)"ptmx";
1050c2a5bb5SAugustin Cavalier 	gDeviceNames[2 * kNumTTYs + 1] = (char *)"tty";
1060c2a5bb5SAugustin Cavalier 
1070c2a5bb5SAugustin Cavalier 	return B_OK;
1080c2a5bb5SAugustin Cavalier }
1090c2a5bb5SAugustin Cavalier 
1100c2a5bb5SAugustin Cavalier 
1110c2a5bb5SAugustin Cavalier void
1120c2a5bb5SAugustin Cavalier uninit_driver(void)
1130c2a5bb5SAugustin Cavalier {
1140c2a5bb5SAugustin Cavalier 	TRACE((DRIVER_NAME ": uninit_driver()\n"));
1150c2a5bb5SAugustin Cavalier 
1160c2a5bb5SAugustin Cavalier 	for (int32 i = 0; i < (int32)kNumTTYs * 2; i++)
1170c2a5bb5SAugustin Cavalier 		free(gDeviceNames[i]);
1180c2a5bb5SAugustin Cavalier 
1190c2a5bb5SAugustin Cavalier 	mutex_destroy(&gGlobalTTYLock);
1200c2a5bb5SAugustin Cavalier 
1210c2a5bb5SAugustin Cavalier 	put_module(B_TTY_MODULE_NAME);
1220c2a5bb5SAugustin Cavalier }
1230c2a5bb5SAugustin Cavalier 
1240c2a5bb5SAugustin Cavalier 
1250c2a5bb5SAugustin Cavalier const char **
1260c2a5bb5SAugustin Cavalier publish_devices(void)
1270c2a5bb5SAugustin Cavalier {
1280c2a5bb5SAugustin Cavalier 	TRACE((DRIVER_NAME ": publish_devices()\n"));
1290c2a5bb5SAugustin Cavalier 	return const_cast<const char **>(gDeviceNames);
1300c2a5bb5SAugustin Cavalier }
1310c2a5bb5SAugustin Cavalier 
1320c2a5bb5SAugustin Cavalier 
1330c2a5bb5SAugustin Cavalier device_hooks *
1340c2a5bb5SAugustin Cavalier find_device(const char *name)
1350c2a5bb5SAugustin Cavalier {
1360c2a5bb5SAugustin Cavalier 	TRACE((DRIVER_NAME ": find_device(\"%s\")\n", name));
1370c2a5bb5SAugustin Cavalier 
1380c2a5bb5SAugustin Cavalier 	for (uint32 i = 0; gDeviceNames[i] != NULL; i++) {
1390c2a5bb5SAugustin Cavalier 		if (!strcmp(name, gDeviceNames[i])) {
1400c2a5bb5SAugustin Cavalier 			return i < kNumTTYs || i == (2 * kNumTTYs)
1410c2a5bb5SAugustin Cavalier 				? &gMasterPTYHooks : &gSlavePTYHooks;
1420c2a5bb5SAugustin Cavalier 		}
1430c2a5bb5SAugustin Cavalier 	}
1440c2a5bb5SAugustin Cavalier 
1450c2a5bb5SAugustin Cavalier 	return NULL;
1460c2a5bb5SAugustin Cavalier }
1470c2a5bb5SAugustin Cavalier 
1480c2a5bb5SAugustin Cavalier 
1490c2a5bb5SAugustin Cavalier static int32
1500c2a5bb5SAugustin Cavalier get_tty_index(const char *name)
1510c2a5bb5SAugustin Cavalier {
1520c2a5bb5SAugustin Cavalier 	// device names follow this form: "pt/%c%x"
1530c2a5bb5SAugustin Cavalier 	int8 digit = name[4];
1540c2a5bb5SAugustin Cavalier 	if (digit >= 'a') {
1550c2a5bb5SAugustin Cavalier 		// hexadecimal digits
1560c2a5bb5SAugustin Cavalier 		digit -= 'a' - 10;
1570c2a5bb5SAugustin Cavalier 	} else
1580c2a5bb5SAugustin Cavalier 		digit -= '0';
1590c2a5bb5SAugustin Cavalier 
1600c2a5bb5SAugustin Cavalier 	return (name[3] - 'p') * 16 + digit;
1610c2a5bb5SAugustin Cavalier }
1620c2a5bb5SAugustin Cavalier 
1630c2a5bb5SAugustin Cavalier 
1640c2a5bb5SAugustin Cavalier static int32
1650c2a5bb5SAugustin Cavalier get_tty_index(struct tty *tty)
1660c2a5bb5SAugustin Cavalier {
1670c2a5bb5SAugustin Cavalier 	int32 index = -1;
1680c2a5bb5SAugustin Cavalier 	for (uint32 i = 0; i < kNumTTYs; i++) {
169*88306be9SAugustin Cavalier 		if (tty == gMasterTTYs[i] || tty == gSlaveTTYs[i]) {
1700c2a5bb5SAugustin Cavalier 			index = i;
1710c2a5bb5SAugustin Cavalier 			break;
1720c2a5bb5SAugustin Cavalier 		}
1730c2a5bb5SAugustin Cavalier 	}
1740c2a5bb5SAugustin Cavalier 	return index;
1750c2a5bb5SAugustin Cavalier }
1760c2a5bb5SAugustin Cavalier 
1770c2a5bb5SAugustin Cavalier 
1780c2a5bb5SAugustin Cavalier //	#pragma mark - device hooks
1790c2a5bb5SAugustin Cavalier 
1800c2a5bb5SAugustin Cavalier 
1810c2a5bb5SAugustin Cavalier static bool
1820c2a5bb5SAugustin Cavalier master_service(struct tty *tty, uint32 op, void *buffer, size_t length)
1830c2a5bb5SAugustin Cavalier {
1840c2a5bb5SAugustin Cavalier 	// nothing here yet
1850c2a5bb5SAugustin Cavalier 	return false;
1860c2a5bb5SAugustin Cavalier }
1870c2a5bb5SAugustin Cavalier 
1880c2a5bb5SAugustin Cavalier 
1890c2a5bb5SAugustin Cavalier static bool
1900c2a5bb5SAugustin Cavalier slave_service(struct tty *tty, uint32 op, void *buffer, size_t length)
1910c2a5bb5SAugustin Cavalier {
1920c2a5bb5SAugustin Cavalier 	// nothing here yet
1930c2a5bb5SAugustin Cavalier 	return false;
1940c2a5bb5SAugustin Cavalier }
1950c2a5bb5SAugustin Cavalier 
1960c2a5bb5SAugustin Cavalier 
1970c2a5bb5SAugustin Cavalier static status_t
1980c2a5bb5SAugustin Cavalier master_open(const char *name, uint32 flags, void **_cookie)
1990c2a5bb5SAugustin Cavalier {
2000c2a5bb5SAugustin Cavalier 	bool findUnusedTTY = strcmp(name, "ptmx") == 0;
2010c2a5bb5SAugustin Cavalier 
2020c2a5bb5SAugustin Cavalier 	int32 index = -1;
2030c2a5bb5SAugustin Cavalier 	if (!findUnusedTTY) {
2040c2a5bb5SAugustin Cavalier 		index = get_tty_index(name);
2050c2a5bb5SAugustin Cavalier 		if (index >= (int32)kNumTTYs)
2060c2a5bb5SAugustin Cavalier 			return B_ERROR;
2070c2a5bb5SAugustin Cavalier 	}
2080c2a5bb5SAugustin Cavalier 
2090c2a5bb5SAugustin Cavalier 	TRACE(("pty_open: TTY index = %" B_PRId32 " (name = %s)\n", index, name));
2100c2a5bb5SAugustin Cavalier 
2110c2a5bb5SAugustin Cavalier 	MutexLocker globalLocker(gGlobalTTYLock);
2120c2a5bb5SAugustin Cavalier 
2130c2a5bb5SAugustin Cavalier 	if (findUnusedTTY) {
2140c2a5bb5SAugustin Cavalier 		for (index = 0; index < (int32)kNumTTYs; index++) {
2150c2a5bb5SAugustin Cavalier 			if (gMasterTTYs[index] == NULL || gMasterTTYs[index]->ref_count == 0)
2160c2a5bb5SAugustin Cavalier 				break;
2170c2a5bb5SAugustin Cavalier 		}
2180c2a5bb5SAugustin Cavalier 		if (index >= (int32)kNumTTYs)
2190c2a5bb5SAugustin Cavalier 			return ENOENT;
2200c2a5bb5SAugustin Cavalier 	} else if (gMasterTTYs[index] != NULL && gMasterTTYs[index]->ref_count != 0) {
2210c2a5bb5SAugustin Cavalier 		// we're already open!
2220c2a5bb5SAugustin Cavalier 		return B_BUSY;
2230c2a5bb5SAugustin Cavalier 	}
2240c2a5bb5SAugustin Cavalier 
2250c2a5bb5SAugustin Cavalier 	if (gMasterTTYs[index] == NULL) {
2260c2a5bb5SAugustin Cavalier 		gMasterTTYs[index] = gTTYModule->tty_create(master_service, NULL);
2270c2a5bb5SAugustin Cavalier 		if (gMasterTTYs[index] == NULL)
2280c2a5bb5SAugustin Cavalier 			return B_NO_MEMORY;
2290c2a5bb5SAugustin Cavalier 	}
2300c2a5bb5SAugustin Cavalier 	if (gSlaveTTYs[index] == NULL) {
2310c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index] = gTTYModule->tty_create(slave_service, gMasterTTYs[index]);
2320c2a5bb5SAugustin Cavalier 		if (gSlaveTTYs[index] == NULL)
2330c2a5bb5SAugustin Cavalier 			return B_NO_MEMORY;
2340c2a5bb5SAugustin Cavalier 	}
2350c2a5bb5SAugustin Cavalier 
2360c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = gTTYModule->tty_create_cookie(gMasterTTYs[index],
2370c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index], flags);
2380c2a5bb5SAugustin Cavalier 	if (cookie == NULL)
2390c2a5bb5SAugustin Cavalier 		return B_NO_MEMORY;
2400c2a5bb5SAugustin Cavalier 
2410c2a5bb5SAugustin Cavalier 	*_cookie = cookie;
2420c2a5bb5SAugustin Cavalier 	return B_OK;
2430c2a5bb5SAugustin Cavalier }
2440c2a5bb5SAugustin Cavalier 
2450c2a5bb5SAugustin Cavalier 
2460c2a5bb5SAugustin Cavalier static status_t
2470c2a5bb5SAugustin Cavalier slave_open(const char *name, uint32 flags, void **_cookie)
2480c2a5bb5SAugustin Cavalier {
2490c2a5bb5SAugustin Cavalier 	// Get the tty index: Opening "/dev/tty" means opening the process'
2500c2a5bb5SAugustin Cavalier 	// controlling tty.
2510c2a5bb5SAugustin Cavalier 	int32 index = get_tty_index(name);
2520c2a5bb5SAugustin Cavalier 	if (strcmp(name, "tty") == 0) {
2530c2a5bb5SAugustin Cavalier 		struct tty *controllingTTY = (struct tty *)team_get_controlling_tty();
2540c2a5bb5SAugustin Cavalier 		if (controllingTTY == NULL)
2550c2a5bb5SAugustin Cavalier 			return B_NOT_ALLOWED;
2560c2a5bb5SAugustin Cavalier 
2570c2a5bb5SAugustin Cavalier 		index = get_tty_index(controllingTTY);
2580c2a5bb5SAugustin Cavalier 		if (index < 0)
2590c2a5bb5SAugustin Cavalier 			return B_NOT_ALLOWED;
2600c2a5bb5SAugustin Cavalier 	} else {
2610c2a5bb5SAugustin Cavalier 		index = get_tty_index(name);
2620c2a5bb5SAugustin Cavalier 		if (index >= (int32)kNumTTYs)
2630c2a5bb5SAugustin Cavalier 			return B_ERROR;
2640c2a5bb5SAugustin Cavalier 	}
2650c2a5bb5SAugustin Cavalier 
2660c2a5bb5SAugustin Cavalier 	TRACE(("slave_open: TTY index = %" B_PRId32 " (name = %s)\n", index,
2670c2a5bb5SAugustin Cavalier 		name));
2680c2a5bb5SAugustin Cavalier 
2690c2a5bb5SAugustin Cavalier 	MutexLocker globalLocker(gGlobalTTYLock);
2700c2a5bb5SAugustin Cavalier 
2710c2a5bb5SAugustin Cavalier 	// we may only be used if our master has already been opened
2720c2a5bb5SAugustin Cavalier 	if (gMasterTTYs[index] == NULL || gMasterTTYs[index]->open_count == 0
2730c2a5bb5SAugustin Cavalier 			|| gSlaveTTYs[index] == NULL) {
2740c2a5bb5SAugustin Cavalier 		return B_IO_ERROR;
2750c2a5bb5SAugustin Cavalier 	}
2760c2a5bb5SAugustin Cavalier 
2770c2a5bb5SAugustin Cavalier 	bool makeControllingTTY = (flags & O_NOCTTY) == 0;
2780c2a5bb5SAugustin Cavalier 	pid_t processID = getpid();
2790c2a5bb5SAugustin Cavalier 	pid_t sessionID = getsid(processID);
2800c2a5bb5SAugustin Cavalier 
2810c2a5bb5SAugustin Cavalier 	if (gSlaveTTYs[index]->open_count == 0) {
2820c2a5bb5SAugustin Cavalier 		// We only allow session leaders to open the tty initially.
2830c2a5bb5SAugustin Cavalier 		if (makeControllingTTY && processID != sessionID)
2840c2a5bb5SAugustin Cavalier 			return B_NOT_ALLOWED;
2850c2a5bb5SAugustin Cavalier 	} else if (makeControllingTTY) {
2860c2a5bb5SAugustin Cavalier 		// If already open, we allow only processes from the same session
2870c2a5bb5SAugustin Cavalier 		// to open the tty again while becoming controlling tty
2880c2a5bb5SAugustin Cavalier 		pid_t ttySession = gSlaveTTYs[index]->settings->session_id;
2890c2a5bb5SAugustin Cavalier 		if (ttySession >= 0) {
2900c2a5bb5SAugustin Cavalier 			makeControllingTTY = false;
2910c2a5bb5SAugustin Cavalier 		} else {
2920c2a5bb5SAugustin Cavalier 			// The tty is not associated with a session yet. The process needs
2930c2a5bb5SAugustin Cavalier 			// to be a session leader.
2940c2a5bb5SAugustin Cavalier 			if (makeControllingTTY && processID != sessionID)
2950c2a5bb5SAugustin Cavalier 				return B_NOT_ALLOWED;
2960c2a5bb5SAugustin Cavalier 		}
2970c2a5bb5SAugustin Cavalier 	}
2980c2a5bb5SAugustin Cavalier 
2990c2a5bb5SAugustin Cavalier 	if (gSlaveTTYs[index]->open_count == 0) {
3000c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index]->settings->session_id = -1;
3010c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index]->settings->pgrp_id = -1;
3020c2a5bb5SAugustin Cavalier 	}
3030c2a5bb5SAugustin Cavalier 
3040c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = gTTYModule->tty_create_cookie(gSlaveTTYs[index],
3050c2a5bb5SAugustin Cavalier 		gMasterTTYs[index], flags);
3060c2a5bb5SAugustin Cavalier 	if (cookie == NULL) {
3070c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index] = NULL;
3080c2a5bb5SAugustin Cavalier 		return B_NO_MEMORY;
3090c2a5bb5SAugustin Cavalier 	}
3100c2a5bb5SAugustin Cavalier 
3110c2a5bb5SAugustin Cavalier 	if (makeControllingTTY) {
3120c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index]->settings->session_id = sessionID;
3130c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index]->settings->pgrp_id = sessionID;
3140c2a5bb5SAugustin Cavalier 		team_set_controlling_tty(gSlaveTTYs[index]);
3150c2a5bb5SAugustin Cavalier 	}
3160c2a5bb5SAugustin Cavalier 
3170c2a5bb5SAugustin Cavalier 	*_cookie = cookie;
3180c2a5bb5SAugustin Cavalier 	return B_OK;
3190c2a5bb5SAugustin Cavalier }
3200c2a5bb5SAugustin Cavalier 
3210c2a5bb5SAugustin Cavalier 
3220c2a5bb5SAugustin Cavalier static status_t
3230c2a5bb5SAugustin Cavalier pty_close(void *_cookie)
3240c2a5bb5SAugustin Cavalier {
3250c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
3260c2a5bb5SAugustin Cavalier 
3270c2a5bb5SAugustin Cavalier 	TRACE(("pty_close: cookie %p\n", _cookie));
3280c2a5bb5SAugustin Cavalier 
3290c2a5bb5SAugustin Cavalier 	MutexLocker globalLocker(gGlobalTTYLock);
3300c2a5bb5SAugustin Cavalier 
3310c2a5bb5SAugustin Cavalier 	gTTYModule->tty_close_cookie(cookie);
3320c2a5bb5SAugustin Cavalier 
3330c2a5bb5SAugustin Cavalier 	return B_OK;
3340c2a5bb5SAugustin Cavalier }
3350c2a5bb5SAugustin Cavalier 
3360c2a5bb5SAugustin Cavalier 
3370c2a5bb5SAugustin Cavalier static status_t
3380c2a5bb5SAugustin Cavalier pty_free_cookie(void *_cookie)
3390c2a5bb5SAugustin Cavalier {
3400c2a5bb5SAugustin Cavalier 	// The TTY is already closed. We only have to free the cookie.
3410c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
3420c2a5bb5SAugustin Cavalier 
3430c2a5bb5SAugustin Cavalier 	gTTYModule->tty_destroy_cookie(cookie);
3440c2a5bb5SAugustin Cavalier 
3450c2a5bb5SAugustin Cavalier 	return B_OK;
3460c2a5bb5SAugustin Cavalier }
3470c2a5bb5SAugustin Cavalier 
3480c2a5bb5SAugustin Cavalier 
3490c2a5bb5SAugustin Cavalier static status_t
3500c2a5bb5SAugustin Cavalier pty_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
3510c2a5bb5SAugustin Cavalier {
3520c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
3530c2a5bb5SAugustin Cavalier 
3540c2a5bb5SAugustin Cavalier 	struct tty* tty = cookie->tty;
3550c2a5bb5SAugustin Cavalier 	RecursiveLocker locker(tty->lock);
3560c2a5bb5SAugustin Cavalier 
3570c2a5bb5SAugustin Cavalier 	TRACE(("pty_ioctl: cookie %p, op %" B_PRIu32 ", buffer %p, length %lu"
3580c2a5bb5SAugustin Cavalier 		"\n", _cookie, op, buffer, length));
3590c2a5bb5SAugustin Cavalier 
3600c2a5bb5SAugustin Cavalier 	switch (op) {
3610c2a5bb5SAugustin Cavalier 		case B_IOCTL_GET_TTY_INDEX:
3620c2a5bb5SAugustin Cavalier 		{
3630c2a5bb5SAugustin Cavalier 			int32 ptyIndex = get_tty_index(cookie->tty);
3640c2a5bb5SAugustin Cavalier 			if (ptyIndex < 0)
3650c2a5bb5SAugustin Cavalier 				return B_BAD_VALUE;
3660c2a5bb5SAugustin Cavalier 
3670c2a5bb5SAugustin Cavalier 			if (user_memcpy(buffer, &ptyIndex, sizeof(int32)) < B_OK)
3680c2a5bb5SAugustin Cavalier 				return B_BAD_ADDRESS;
3690c2a5bb5SAugustin Cavalier 
3700c2a5bb5SAugustin Cavalier 			return B_OK;
3710c2a5bb5SAugustin Cavalier 		}
3720c2a5bb5SAugustin Cavalier 
3730c2a5bb5SAugustin Cavalier 		case B_IOCTL_GRANT_TTY:
3740c2a5bb5SAugustin Cavalier 		{
3750c2a5bb5SAugustin Cavalier 			if (!cookie->tty->is_master)
3760c2a5bb5SAugustin Cavalier 				return B_BAD_VALUE;
3770c2a5bb5SAugustin Cavalier 
3780c2a5bb5SAugustin Cavalier 			int32 ptyIndex = get_tty_index(cookie->tty);
3790c2a5bb5SAugustin Cavalier 			if (ptyIndex < 0)
3800c2a5bb5SAugustin Cavalier 				return B_BAD_VALUE;
3810c2a5bb5SAugustin Cavalier 
3820c2a5bb5SAugustin Cavalier 			// get slave path
3830c2a5bb5SAugustin Cavalier 			char path[64];
3840c2a5bb5SAugustin Cavalier 			snprintf(path, sizeof(path), "/dev/%s",
3850c2a5bb5SAugustin Cavalier 				gDeviceNames[kNumTTYs + ptyIndex]);
3860c2a5bb5SAugustin Cavalier 
3870c2a5bb5SAugustin Cavalier 			// set owner and permissions respectively
3880c2a5bb5SAugustin Cavalier 			if (chown(path, getuid(), getgid()) != 0
3890c2a5bb5SAugustin Cavalier 				|| chmod(path, S_IRUSR | S_IWUSR | S_IWGRP) != 0) {
3900c2a5bb5SAugustin Cavalier 				return errno;
3910c2a5bb5SAugustin Cavalier 			}
3920c2a5bb5SAugustin Cavalier 
3930c2a5bb5SAugustin Cavalier 			return B_OK;
3940c2a5bb5SAugustin Cavalier 		}
3950c2a5bb5SAugustin Cavalier 
3960c2a5bb5SAugustin Cavalier 		case 'pgid':				// BeOS
3970c2a5bb5SAugustin Cavalier 			op = TIOCSPGRP;
3980c2a5bb5SAugustin Cavalier 
3990c2a5bb5SAugustin Cavalier 		case 'wsiz':				// BeOS
4000c2a5bb5SAugustin Cavalier 			op = TIOCSWINSZ;
4010c2a5bb5SAugustin Cavalier 			break;
4020c2a5bb5SAugustin Cavalier 
4030c2a5bb5SAugustin Cavalier 		default:
4040c2a5bb5SAugustin Cavalier 			break;
4050c2a5bb5SAugustin Cavalier 	}
4060c2a5bb5SAugustin Cavalier 
4070c2a5bb5SAugustin Cavalier 	return gTTYModule->tty_control(cookie, op, buffer, length);
4080c2a5bb5SAugustin Cavalier }
4090c2a5bb5SAugustin Cavalier 
4100c2a5bb5SAugustin Cavalier 
4110c2a5bb5SAugustin Cavalier static status_t
4120c2a5bb5SAugustin Cavalier pty_read(void *_cookie, off_t offset, void *buffer, size_t *_length)
4130c2a5bb5SAugustin Cavalier {
4140c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
4150c2a5bb5SAugustin Cavalier 
4160c2a5bb5SAugustin Cavalier 	TRACE(("pty_read: cookie %p, offset %" B_PRIdOFF ", buffer %p, length "
4170c2a5bb5SAugustin Cavalier 		"%lu\n", _cookie, offset, buffer, *_length));
4180c2a5bb5SAugustin Cavalier 
4190c2a5bb5SAugustin Cavalier 	status_t result = gTTYModule->tty_read(cookie, buffer, _length);
4200c2a5bb5SAugustin Cavalier 
4210c2a5bb5SAugustin Cavalier 	TRACE(("pty_read done: cookie %p, result: %" B_PRIx32 ", length %lu\n",
4220c2a5bb5SAugustin Cavalier 		_cookie, result, *_length));
4230c2a5bb5SAugustin Cavalier 
4240c2a5bb5SAugustin Cavalier 	return result;
4250c2a5bb5SAugustin Cavalier }
4260c2a5bb5SAugustin Cavalier 
4270c2a5bb5SAugustin Cavalier 
4280c2a5bb5SAugustin Cavalier static status_t
4290c2a5bb5SAugustin Cavalier pty_write(void *_cookie, off_t offset, const void *buffer, size_t *_length)
4300c2a5bb5SAugustin Cavalier {
4310c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
4320c2a5bb5SAugustin Cavalier 
4330c2a5bb5SAugustin Cavalier 	TRACE(("pty_write: cookie %p, offset %" B_PRIdOFF ", buffer %p, length "
4340c2a5bb5SAugustin Cavalier 		"%lu\n", _cookie, offset, buffer, *_length));
4350c2a5bb5SAugustin Cavalier 
4360c2a5bb5SAugustin Cavalier 	status_t result = gTTYModule->tty_write(cookie, buffer, _length);
4370c2a5bb5SAugustin Cavalier 
4380c2a5bb5SAugustin Cavalier 	TRACE(("pty_write done: cookie %p, result: %" B_PRIx32 ", length %lu\n",
4390c2a5bb5SAugustin Cavalier 		_cookie, result, *_length));
4400c2a5bb5SAugustin Cavalier 
4410c2a5bb5SAugustin Cavalier 	return result;
4420c2a5bb5SAugustin Cavalier }
4430c2a5bb5SAugustin Cavalier 
4440c2a5bb5SAugustin Cavalier 
4450c2a5bb5SAugustin Cavalier static status_t
4460c2a5bb5SAugustin Cavalier pty_select(void *_cookie, uint8 event, uint32 ref, selectsync *sync)
4470c2a5bb5SAugustin Cavalier {
4480c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
4490c2a5bb5SAugustin Cavalier 
4500c2a5bb5SAugustin Cavalier 	return gTTYModule->tty_select(cookie, event, ref, sync);
4510c2a5bb5SAugustin Cavalier }
4520c2a5bb5SAugustin Cavalier 
4530c2a5bb5SAugustin Cavalier 
4540c2a5bb5SAugustin Cavalier static status_t
4550c2a5bb5SAugustin Cavalier pty_deselect(void *_cookie, uint8 event, selectsync *sync)
4560c2a5bb5SAugustin Cavalier {
4570c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
4580c2a5bb5SAugustin Cavalier 
4590c2a5bb5SAugustin Cavalier 	return gTTYModule->tty_deselect(cookie, event, sync);
4600c2a5bb5SAugustin Cavalier }
4610c2a5bb5SAugustin Cavalier 
4620c2a5bb5SAugustin Cavalier 
4630c2a5bb5SAugustin Cavalier device_hooks gMasterPTYHooks = {
4640c2a5bb5SAugustin Cavalier 	&master_open,
4650c2a5bb5SAugustin Cavalier 	&pty_close,
4660c2a5bb5SAugustin Cavalier 	&pty_free_cookie,
4670c2a5bb5SAugustin Cavalier 	&pty_ioctl,
4680c2a5bb5SAugustin Cavalier 	&pty_read,
4690c2a5bb5SAugustin Cavalier 	&pty_write,
4700c2a5bb5SAugustin Cavalier 	&pty_select,
4710c2a5bb5SAugustin Cavalier 	&pty_deselect,
4720c2a5bb5SAugustin Cavalier 	NULL,	// read_pages()
4730c2a5bb5SAugustin Cavalier 	NULL	// write_pages()
4740c2a5bb5SAugustin Cavalier };
4750c2a5bb5SAugustin Cavalier 
4760c2a5bb5SAugustin Cavalier device_hooks gSlavePTYHooks = {
4770c2a5bb5SAugustin Cavalier 	&slave_open,
4780c2a5bb5SAugustin Cavalier 	&pty_close,
4790c2a5bb5SAugustin Cavalier 	&pty_free_cookie,
4800c2a5bb5SAugustin Cavalier 	&pty_ioctl,
4810c2a5bb5SAugustin Cavalier 	&pty_read,
4820c2a5bb5SAugustin Cavalier 	&pty_write,
4830c2a5bb5SAugustin Cavalier 	&pty_select,
4840c2a5bb5SAugustin Cavalier 	&pty_deselect,
4850c2a5bb5SAugustin Cavalier 	NULL,	// read_pages()
4860c2a5bb5SAugustin Cavalier 	NULL	// write_pages()
4870c2a5bb5SAugustin Cavalier };
488