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