xref: /haiku/src/add-ons/kernel/drivers/pty/driver.cpp (revision ed3d3b1023c4ce541cdf82b1304a99c8010a06d9)
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
init_hardware(void)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
init_driver(void)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
uninit_driver(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 **
publish_devices(void)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 *
find_device(const char * name)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
get_tty_index(const char * name)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
get_tty_index(struct tty * tty)1650c2a5bb5SAugustin Cavalier get_tty_index(struct tty *tty)
1660c2a5bb5SAugustin Cavalier {
1670c2a5bb5SAugustin Cavalier 	int32 index = -1;
1680c2a5bb5SAugustin Cavalier 	for (uint32 i = 0; i < kNumTTYs; i++) {
16988306be9SAugustin 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
master_service(struct tty * tty,uint32 op,void * buffer,size_t length)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
slave_service(struct tty * tty,uint32 op,void * buffer,size_t length)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
master_open(const char * name,uint32 flags,void ** _cookie)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++) {
215*ed3d3b10SAugustin Cavalier 			if (gMasterTTYs[index] == NULL)
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 
225b197dcbaSTrung Nguyen 	status_t status = B_OK;
226b197dcbaSTrung Nguyen 
2270c2a5bb5SAugustin Cavalier 	if (gMasterTTYs[index] == NULL) {
228b197dcbaSTrung Nguyen 		status = gTTYModule->tty_create(master_service, NULL, &gMasterTTYs[index]);
229b197dcbaSTrung Nguyen 		if (status != B_OK)
230b197dcbaSTrung Nguyen 			return status;
2310c2a5bb5SAugustin Cavalier 	}
2320c2a5bb5SAugustin Cavalier 	if (gSlaveTTYs[index] == NULL) {
233b197dcbaSTrung Nguyen 		status = gTTYModule->tty_create(slave_service, gMasterTTYs[index], &gSlaveTTYs[index]);
234b197dcbaSTrung Nguyen 		if (status != B_OK)
235b197dcbaSTrung Nguyen 			return status;
2360c2a5bb5SAugustin Cavalier 	}
2370c2a5bb5SAugustin Cavalier 
238b197dcbaSTrung Nguyen 	tty_cookie *cookie;
239b197dcbaSTrung Nguyen 	status = gTTYModule->tty_create_cookie(gMasterTTYs[index], gSlaveTTYs[index], flags, &cookie);
240b197dcbaSTrung Nguyen 	if (status != B_OK)
241b197dcbaSTrung Nguyen 		return status;
2420c2a5bb5SAugustin Cavalier 
2430c2a5bb5SAugustin Cavalier 	*_cookie = cookie;
2440c2a5bb5SAugustin Cavalier 	return B_OK;
2450c2a5bb5SAugustin Cavalier }
2460c2a5bb5SAugustin Cavalier 
2470c2a5bb5SAugustin Cavalier 
2480c2a5bb5SAugustin Cavalier static status_t
slave_open(const char * name,uint32 flags,void ** _cookie)2490c2a5bb5SAugustin Cavalier slave_open(const char *name, uint32 flags, void **_cookie)
2500c2a5bb5SAugustin Cavalier {
2510c2a5bb5SAugustin Cavalier 	// Get the tty index: Opening "/dev/tty" means opening the process'
2520c2a5bb5SAugustin Cavalier 	// controlling tty.
2530c2a5bb5SAugustin Cavalier 	int32 index = get_tty_index(name);
2540c2a5bb5SAugustin Cavalier 	if (strcmp(name, "tty") == 0) {
2550c2a5bb5SAugustin Cavalier 		struct tty *controllingTTY = (struct tty *)team_get_controlling_tty();
2560c2a5bb5SAugustin Cavalier 		if (controllingTTY == NULL)
2570c2a5bb5SAugustin Cavalier 			return B_NOT_ALLOWED;
2580c2a5bb5SAugustin Cavalier 
2590c2a5bb5SAugustin Cavalier 		index = get_tty_index(controllingTTY);
2600c2a5bb5SAugustin Cavalier 		if (index < 0)
2610c2a5bb5SAugustin Cavalier 			return B_NOT_ALLOWED;
2620c2a5bb5SAugustin Cavalier 	} else {
2630c2a5bb5SAugustin Cavalier 		index = get_tty_index(name);
2640c2a5bb5SAugustin Cavalier 		if (index >= (int32)kNumTTYs)
2650c2a5bb5SAugustin Cavalier 			return B_ERROR;
2660c2a5bb5SAugustin Cavalier 	}
2670c2a5bb5SAugustin Cavalier 
2680c2a5bb5SAugustin Cavalier 	TRACE(("slave_open: TTY index = %" B_PRId32 " (name = %s)\n", index,
2690c2a5bb5SAugustin Cavalier 		name));
2700c2a5bb5SAugustin Cavalier 
2710c2a5bb5SAugustin Cavalier 	MutexLocker globalLocker(gGlobalTTYLock);
2720c2a5bb5SAugustin Cavalier 
2730c2a5bb5SAugustin Cavalier 	// we may only be used if our master has already been opened
2740c2a5bb5SAugustin Cavalier 	if (gMasterTTYs[index] == NULL || gMasterTTYs[index]->open_count == 0
2750c2a5bb5SAugustin Cavalier 			|| gSlaveTTYs[index] == NULL) {
2760c2a5bb5SAugustin Cavalier 		return B_IO_ERROR;
2770c2a5bb5SAugustin Cavalier 	}
2780c2a5bb5SAugustin Cavalier 
2790c2a5bb5SAugustin Cavalier 	bool makeControllingTTY = (flags & O_NOCTTY) == 0;
2800c2a5bb5SAugustin Cavalier 	pid_t processID = getpid();
2810c2a5bb5SAugustin Cavalier 	pid_t sessionID = getsid(processID);
2820c2a5bb5SAugustin Cavalier 
2830c2a5bb5SAugustin Cavalier 	if (gSlaveTTYs[index]->open_count == 0) {
2840c2a5bb5SAugustin Cavalier 		// We only allow session leaders to open the tty initially.
2850c2a5bb5SAugustin Cavalier 		if (makeControllingTTY && processID != sessionID)
2860c2a5bb5SAugustin Cavalier 			return B_NOT_ALLOWED;
2870c2a5bb5SAugustin Cavalier 	} else if (makeControllingTTY) {
2880c2a5bb5SAugustin Cavalier 		// If already open, we allow only processes from the same session
2890c2a5bb5SAugustin Cavalier 		// to open the tty again while becoming controlling tty
2900c2a5bb5SAugustin Cavalier 		pid_t ttySession = gSlaveTTYs[index]->settings->session_id;
2910c2a5bb5SAugustin Cavalier 		if (ttySession >= 0) {
2920c2a5bb5SAugustin Cavalier 			makeControllingTTY = false;
2930c2a5bb5SAugustin Cavalier 		} else {
2940c2a5bb5SAugustin Cavalier 			// The tty is not associated with a session yet. The process needs
2950c2a5bb5SAugustin Cavalier 			// to be a session leader.
2960c2a5bb5SAugustin Cavalier 			if (makeControllingTTY && processID != sessionID)
2970c2a5bb5SAugustin Cavalier 				return B_NOT_ALLOWED;
2980c2a5bb5SAugustin Cavalier 		}
2990c2a5bb5SAugustin Cavalier 	}
3000c2a5bb5SAugustin Cavalier 
3010c2a5bb5SAugustin Cavalier 	if (gSlaveTTYs[index]->open_count == 0) {
3020c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index]->settings->session_id = -1;
3030c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index]->settings->pgrp_id = -1;
3040c2a5bb5SAugustin Cavalier 	}
3050c2a5bb5SAugustin Cavalier 
306b197dcbaSTrung Nguyen 	tty_cookie *cookie;
307b197dcbaSTrung Nguyen 	status_t status = gTTYModule->tty_create_cookie(gSlaveTTYs[index], gMasterTTYs[index], flags,
308b197dcbaSTrung Nguyen 		&cookie);
309b197dcbaSTrung Nguyen 	if (status != B_OK)
310b197dcbaSTrung Nguyen 		return status;
3110c2a5bb5SAugustin Cavalier 
3120c2a5bb5SAugustin Cavalier 	if (makeControllingTTY) {
3130c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index]->settings->session_id = sessionID;
3140c2a5bb5SAugustin Cavalier 		gSlaveTTYs[index]->settings->pgrp_id = sessionID;
3150c2a5bb5SAugustin Cavalier 		team_set_controlling_tty(gSlaveTTYs[index]);
3160c2a5bb5SAugustin Cavalier 	}
3170c2a5bb5SAugustin Cavalier 
3180c2a5bb5SAugustin Cavalier 	*_cookie = cookie;
3190c2a5bb5SAugustin Cavalier 	return B_OK;
3200c2a5bb5SAugustin Cavalier }
3210c2a5bb5SAugustin Cavalier 
3220c2a5bb5SAugustin Cavalier 
3230c2a5bb5SAugustin Cavalier static status_t
pty_close(void * _cookie)3240c2a5bb5SAugustin Cavalier pty_close(void *_cookie)
3250c2a5bb5SAugustin Cavalier {
3260c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
3270c2a5bb5SAugustin Cavalier 
3280c2a5bb5SAugustin Cavalier 	TRACE(("pty_close: cookie %p\n", _cookie));
3290c2a5bb5SAugustin Cavalier 
3300c2a5bb5SAugustin Cavalier 	MutexLocker globalLocker(gGlobalTTYLock);
3310c2a5bb5SAugustin Cavalier 
3325193c8cdSAugustin Cavalier 	if (cookie->tty->is_master) {
3335193c8cdSAugustin Cavalier 		// close all connected slave cookies first
3345193c8cdSAugustin Cavalier 		while (tty_cookie *slave = cookie->other_tty->cookies.Head())
3355193c8cdSAugustin Cavalier 			gTTYModule->tty_close_cookie(slave);
3365193c8cdSAugustin Cavalier 	}
3375193c8cdSAugustin Cavalier 
3380c2a5bb5SAugustin Cavalier 	gTTYModule->tty_close_cookie(cookie);
3390c2a5bb5SAugustin Cavalier 
3400c2a5bb5SAugustin Cavalier 	return B_OK;
3410c2a5bb5SAugustin Cavalier }
3420c2a5bb5SAugustin Cavalier 
3430c2a5bb5SAugustin Cavalier 
3440c2a5bb5SAugustin Cavalier static status_t
pty_free_cookie(void * _cookie)3450c2a5bb5SAugustin Cavalier pty_free_cookie(void *_cookie)
3460c2a5bb5SAugustin Cavalier {
3470c2a5bb5SAugustin Cavalier 	// The TTY is already closed. We only have to free the cookie.
3480c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
349*ed3d3b10SAugustin Cavalier 	struct tty *tty = cookie->tty;
350*ed3d3b10SAugustin Cavalier 
351*ed3d3b10SAugustin Cavalier 	MutexLocker globalLocker(gGlobalTTYLock);
3520c2a5bb5SAugustin Cavalier 
3530c2a5bb5SAugustin Cavalier 	gTTYModule->tty_destroy_cookie(cookie);
3540c2a5bb5SAugustin Cavalier 
355*ed3d3b10SAugustin Cavalier 	if (tty->ref_count == 0) {
356*ed3d3b10SAugustin Cavalier 		// We need to destroy both master and slave TTYs at the same time,
357*ed3d3b10SAugustin Cavalier 		// and in the proper order.
358*ed3d3b10SAugustin Cavalier 		int32 index = get_tty_index(tty);
359*ed3d3b10SAugustin Cavalier 		if (index < 0)
360*ed3d3b10SAugustin Cavalier 			return B_OK;
361*ed3d3b10SAugustin Cavalier 
362*ed3d3b10SAugustin Cavalier 		if (gMasterTTYs[index]->ref_count == 0 && gSlaveTTYs[index]->ref_count == 0) {
363*ed3d3b10SAugustin Cavalier 			gTTYModule->tty_destroy(gSlaveTTYs[index]);
364*ed3d3b10SAugustin Cavalier 			gTTYModule->tty_destroy(gMasterTTYs[index]);
365*ed3d3b10SAugustin Cavalier 			gMasterTTYs[index] = gSlaveTTYs[index] = NULL;
366*ed3d3b10SAugustin Cavalier 		}
367*ed3d3b10SAugustin Cavalier 	}
368*ed3d3b10SAugustin Cavalier 
3690c2a5bb5SAugustin Cavalier 	return B_OK;
3700c2a5bb5SAugustin Cavalier }
3710c2a5bb5SAugustin Cavalier 
3720c2a5bb5SAugustin Cavalier 
3730c2a5bb5SAugustin Cavalier static status_t
pty_ioctl(void * _cookie,uint32 op,void * buffer,size_t length)3740c2a5bb5SAugustin Cavalier pty_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
3750c2a5bb5SAugustin Cavalier {
3760c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
3770c2a5bb5SAugustin Cavalier 
3780c2a5bb5SAugustin Cavalier 	struct tty* tty = cookie->tty;
3790c2a5bb5SAugustin Cavalier 	RecursiveLocker locker(tty->lock);
3800c2a5bb5SAugustin Cavalier 
3810c2a5bb5SAugustin Cavalier 	TRACE(("pty_ioctl: cookie %p, op %" B_PRIu32 ", buffer %p, length %lu"
3820c2a5bb5SAugustin Cavalier 		"\n", _cookie, op, buffer, length));
3830c2a5bb5SAugustin Cavalier 
3840c2a5bb5SAugustin Cavalier 	switch (op) {
3850c2a5bb5SAugustin Cavalier 		case B_IOCTL_GET_TTY_INDEX:
3860c2a5bb5SAugustin Cavalier 		{
3870c2a5bb5SAugustin Cavalier 			int32 ptyIndex = get_tty_index(cookie->tty);
3880c2a5bb5SAugustin Cavalier 			if (ptyIndex < 0)
3890c2a5bb5SAugustin Cavalier 				return B_BAD_VALUE;
3900c2a5bb5SAugustin Cavalier 
3910c2a5bb5SAugustin Cavalier 			if (user_memcpy(buffer, &ptyIndex, sizeof(int32)) < B_OK)
3920c2a5bb5SAugustin Cavalier 				return B_BAD_ADDRESS;
3930c2a5bb5SAugustin Cavalier 
3940c2a5bb5SAugustin Cavalier 			return B_OK;
3950c2a5bb5SAugustin Cavalier 		}
3960c2a5bb5SAugustin Cavalier 
3970c2a5bb5SAugustin Cavalier 		case B_IOCTL_GRANT_TTY:
3980c2a5bb5SAugustin Cavalier 		{
3990c2a5bb5SAugustin Cavalier 			if (!cookie->tty->is_master)
4000c2a5bb5SAugustin Cavalier 				return B_BAD_VALUE;
4010c2a5bb5SAugustin Cavalier 
4020c2a5bb5SAugustin Cavalier 			int32 ptyIndex = get_tty_index(cookie->tty);
4030c2a5bb5SAugustin Cavalier 			if (ptyIndex < 0)
4040c2a5bb5SAugustin Cavalier 				return B_BAD_VALUE;
4050c2a5bb5SAugustin Cavalier 
4060c2a5bb5SAugustin Cavalier 			// get slave path
4070c2a5bb5SAugustin Cavalier 			char path[64];
4080c2a5bb5SAugustin Cavalier 			snprintf(path, sizeof(path), "/dev/%s",
4090c2a5bb5SAugustin Cavalier 				gDeviceNames[kNumTTYs + ptyIndex]);
4100c2a5bb5SAugustin Cavalier 
4110c2a5bb5SAugustin Cavalier 			// set owner and permissions respectively
4120c2a5bb5SAugustin Cavalier 			if (chown(path, getuid(), getgid()) != 0
4130c2a5bb5SAugustin Cavalier 				|| chmod(path, S_IRUSR | S_IWUSR | S_IWGRP) != 0) {
4140c2a5bb5SAugustin Cavalier 				return errno;
4150c2a5bb5SAugustin Cavalier 			}
4160c2a5bb5SAugustin Cavalier 
4170c2a5bb5SAugustin Cavalier 			return B_OK;
4180c2a5bb5SAugustin Cavalier 		}
4190c2a5bb5SAugustin Cavalier 
4200c2a5bb5SAugustin Cavalier 		case 'pgid':				// BeOS
4210c2a5bb5SAugustin Cavalier 			op = TIOCSPGRP;
4220c2a5bb5SAugustin Cavalier 
4230c2a5bb5SAugustin Cavalier 		case 'wsiz':				// BeOS
4240c2a5bb5SAugustin Cavalier 			op = TIOCSWINSZ;
4250c2a5bb5SAugustin Cavalier 			break;
4260c2a5bb5SAugustin Cavalier 
4270c2a5bb5SAugustin Cavalier 		default:
4280c2a5bb5SAugustin Cavalier 			break;
4290c2a5bb5SAugustin Cavalier 	}
4300c2a5bb5SAugustin Cavalier 
4310c2a5bb5SAugustin Cavalier 	return gTTYModule->tty_control(cookie, op, buffer, length);
4320c2a5bb5SAugustin Cavalier }
4330c2a5bb5SAugustin Cavalier 
4340c2a5bb5SAugustin Cavalier 
4350c2a5bb5SAugustin Cavalier static status_t
pty_read(void * _cookie,off_t offset,void * buffer,size_t * _length)4360c2a5bb5SAugustin Cavalier pty_read(void *_cookie, off_t offset, void *buffer, size_t *_length)
4370c2a5bb5SAugustin Cavalier {
4380c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
4390c2a5bb5SAugustin Cavalier 
4400c2a5bb5SAugustin Cavalier 	TRACE(("pty_read: cookie %p, offset %" B_PRIdOFF ", buffer %p, length "
4410c2a5bb5SAugustin Cavalier 		"%lu\n", _cookie, offset, buffer, *_length));
4420c2a5bb5SAugustin Cavalier 
4430c2a5bb5SAugustin Cavalier 	status_t result = gTTYModule->tty_read(cookie, buffer, _length);
4440c2a5bb5SAugustin Cavalier 
4450c2a5bb5SAugustin Cavalier 	TRACE(("pty_read done: cookie %p, result: %" B_PRIx32 ", length %lu\n",
4460c2a5bb5SAugustin Cavalier 		_cookie, result, *_length));
4470c2a5bb5SAugustin Cavalier 
4480c2a5bb5SAugustin Cavalier 	return result;
4490c2a5bb5SAugustin Cavalier }
4500c2a5bb5SAugustin Cavalier 
4510c2a5bb5SAugustin Cavalier 
4520c2a5bb5SAugustin Cavalier static status_t
pty_write(void * _cookie,off_t offset,const void * buffer,size_t * _length)4530c2a5bb5SAugustin Cavalier pty_write(void *_cookie, off_t offset, const void *buffer, size_t *_length)
4540c2a5bb5SAugustin Cavalier {
4550c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
4560c2a5bb5SAugustin Cavalier 
4570c2a5bb5SAugustin Cavalier 	TRACE(("pty_write: cookie %p, offset %" B_PRIdOFF ", buffer %p, length "
4580c2a5bb5SAugustin Cavalier 		"%lu\n", _cookie, offset, buffer, *_length));
4590c2a5bb5SAugustin Cavalier 
4600c2a5bb5SAugustin Cavalier 	status_t result = gTTYModule->tty_write(cookie, buffer, _length);
4610c2a5bb5SAugustin Cavalier 
4620c2a5bb5SAugustin Cavalier 	TRACE(("pty_write done: cookie %p, result: %" B_PRIx32 ", length %lu\n",
4630c2a5bb5SAugustin Cavalier 		_cookie, result, *_length));
4640c2a5bb5SAugustin Cavalier 
4650c2a5bb5SAugustin Cavalier 	return result;
4660c2a5bb5SAugustin Cavalier }
4670c2a5bb5SAugustin Cavalier 
4680c2a5bb5SAugustin Cavalier 
4690c2a5bb5SAugustin Cavalier static status_t
pty_select(void * _cookie,uint8 event,uint32 ref,selectsync * sync)4700c2a5bb5SAugustin Cavalier pty_select(void *_cookie, uint8 event, uint32 ref, selectsync *sync)
4710c2a5bb5SAugustin Cavalier {
4720c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
4730c2a5bb5SAugustin Cavalier 
4740c2a5bb5SAugustin Cavalier 	return gTTYModule->tty_select(cookie, event, ref, sync);
4750c2a5bb5SAugustin Cavalier }
4760c2a5bb5SAugustin Cavalier 
4770c2a5bb5SAugustin Cavalier 
4780c2a5bb5SAugustin Cavalier static status_t
pty_deselect(void * _cookie,uint8 event,selectsync * sync)4790c2a5bb5SAugustin Cavalier pty_deselect(void *_cookie, uint8 event, selectsync *sync)
4800c2a5bb5SAugustin Cavalier {
4810c2a5bb5SAugustin Cavalier 	tty_cookie *cookie = (tty_cookie *)_cookie;
4820c2a5bb5SAugustin Cavalier 
4830c2a5bb5SAugustin Cavalier 	return gTTYModule->tty_deselect(cookie, event, sync);
4840c2a5bb5SAugustin Cavalier }
4850c2a5bb5SAugustin Cavalier 
4860c2a5bb5SAugustin Cavalier 
4870c2a5bb5SAugustin Cavalier device_hooks gMasterPTYHooks = {
4880c2a5bb5SAugustin Cavalier 	&master_open,
4890c2a5bb5SAugustin Cavalier 	&pty_close,
4900c2a5bb5SAugustin Cavalier 	&pty_free_cookie,
4910c2a5bb5SAugustin Cavalier 	&pty_ioctl,
4920c2a5bb5SAugustin Cavalier 	&pty_read,
4930c2a5bb5SAugustin Cavalier 	&pty_write,
4940c2a5bb5SAugustin Cavalier 	&pty_select,
4950c2a5bb5SAugustin Cavalier 	&pty_deselect,
4960c2a5bb5SAugustin Cavalier 	NULL,	// read_pages()
4970c2a5bb5SAugustin Cavalier 	NULL	// write_pages()
4980c2a5bb5SAugustin Cavalier };
4990c2a5bb5SAugustin Cavalier 
5000c2a5bb5SAugustin Cavalier device_hooks gSlavePTYHooks = {
5010c2a5bb5SAugustin Cavalier 	&slave_open,
5020c2a5bb5SAugustin Cavalier 	&pty_close,
5030c2a5bb5SAugustin Cavalier 	&pty_free_cookie,
5040c2a5bb5SAugustin Cavalier 	&pty_ioctl,
5050c2a5bb5SAugustin Cavalier 	&pty_read,
5060c2a5bb5SAugustin Cavalier 	&pty_write,
5070c2a5bb5SAugustin Cavalier 	&pty_select,
5080c2a5bb5SAugustin Cavalier 	&pty_deselect,
5090c2a5bb5SAugustin Cavalier 	NULL,	// read_pages()
5100c2a5bb5SAugustin Cavalier 	NULL	// write_pages()
5110c2a5bb5SAugustin Cavalier };
512