From b197dcbafab14231fac58cdb85c2311d9de9be68 Mon Sep 17 00:00:00 2001 From: Trung Nguyen Date: Tue, 9 May 2023 22:16:49 +1000 Subject: [PATCH] tty: Implement exclusive mode Implemented exclusive mode on Haiku and added the related `ioctl` operations (`TIOCEXCL` and `TIOCNXCL`). Change-Id: Iaa201ea20eec0e45d02dd5db9ba6aa35fd27dfb2 Reviewed-on: https://review.haiku-os.org/c/haiku/+/6387 Tested-by: Commit checker robot Reviewed-by: Jessica Hamilton --- headers/os/drivers/tty/tty_module.h | 8 +-- headers/posix/termios.h | 2 + .../drivers/ports/pc_serial/SerialDevice.cpp | 32 ++++++------ .../drivers/ports/usb_serial/SerialDevice.cpp | 33 ++++++------ src/add-ons/kernel/drivers/pty/driver.cpp | 33 ++++++------ src/add-ons/kernel/generic/tty/tty.cpp | 52 ++++++++++++++----- src/add-ons/kernel/generic/tty/tty_private.h | 8 +-- src/bin/debug/strace/ioctl.cpp | 2 + 8 files changed, 101 insertions(+), 69 deletions(-) diff --git a/headers/os/drivers/tty/tty_module.h b/headers/os/drivers/tty/tty_module.h index 4f1232ab75..ee88540e12 100644 --- a/headers/os/drivers/tty/tty_module.h +++ b/headers/os/drivers/tty/tty_module.h @@ -46,12 +46,12 @@ typedef struct tty_module_info tty_module_info; struct tty_module_info { module_info mi; - struct tty *(*tty_create)(tty_service_func serviceFunction, struct tty* master); + status_t (*tty_create)(tty_service_func serviceFunction, struct tty *master, + struct tty **tty); void (*tty_destroy)(struct tty *tty); - struct tty_cookie * - (*tty_create_cookie)(struct tty *masterTTY, struct tty *slaveTTY, - uint32 openMode); + status_t (*tty_create_cookie)(struct tty *masterTTY, struct tty *slaveTTY, + uint32 openMode, struct tty_cookie **cookie); void (*tty_close_cookie)(struct tty_cookie *cookie); void (*tty_destroy_cookie)(struct tty_cookie *cookie); diff --git a/headers/posix/termios.h b/headers/posix/termios.h index bcf29847cd..c0653bf7aa 100644 --- a/headers/posix/termios.h +++ b/headers/posix/termios.h @@ -189,6 +189,8 @@ struct termios { #define TIOCMBIC (TCGETA + 23) /* clear bits in line state */ #define TIOCGSID (TCGETA + 24) /* get session leader process group ID */ #define TIOCOUTQ (TCGETA + 25) /* get output queue size */ +#define TIOCEXCL (TCGETA + 26) /* set exclusive use of tty */ +#define TIOCNXCL (TCGETA + 27) /* clear exclusive use of tty */ /* Event codes. Returned from TCWAITEVENT */ #define EV_RING 0x0001 diff --git a/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.cpp b/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.cpp index 0436562dd4..a174a651f2 100644 --- a/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.cpp +++ b/src/add-ons/kernel/drivers/ports/pc_serial/SerialDevice.cpp @@ -161,7 +161,7 @@ SerialDevice::SetModes(struct termios *tios) // disable MaskReg8(MCR, MCR_DTR); } else { - // set FCR now, + // set FCR now, // 16650 and later chips have another reg at 2 when DLAB=1 uint8 fcr = FCR_ENABLE | FCR_RX_RST | FCR_TX_RST | FCR_F_8 | FCR_F64EN; // enable fifo @@ -283,7 +283,7 @@ SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length) SignalControlLineState(TTYHWCTS, msr & MSR_CTS); if (enable) { - // + // WriteReg8(MCR, MCR_DTR | MCR_RTS | MCR_IRQ_EN /*| MCR_LOOP*//*XXXXXXX*/); // enable irqs fCachedIER = IER_RLS | IER_MS | IER_RDA; @@ -424,7 +424,7 @@ SerialDevice::InterruptHandler() size_t readable = 0; size_t fifoavail = 1; size_t i; - + //DEBUG // for (int count = 0; ReadReg8(LSR) & LSR_DR; count++) // gTTYModule->ttyin(&fTTY, &fRover, ReadReg8(RBR)); @@ -545,36 +545,34 @@ SerialDevice::Open(uint32 flags) if (fDeviceRemoved) return B_DEV_NOT_READY; - fMasterTTY = gTTYModule->tty_create(pc_serial_service, NULL); - if (fMasterTTY == NULL) { + status = gTTYModule->tty_create(pc_serial_service, NULL, &fMasterTTY); + if (status != B_OK) { TRACE_ALWAYS("open: failed to init master tty\n"); - return B_NO_MEMORY; + return status; } - fSlaveTTY = gTTYModule->tty_create(pc_serial_service, fMasterTTY); - if (fSlaveTTY == NULL) { + status = gTTYModule->tty_create(pc_serial_service, fMasterTTY, &fSlaveTTY); + if (status != B_OK) { TRACE_ALWAYS("open: failed to init slave tty\n"); gTTYModule->tty_destroy(fMasterTTY); - return B_NO_MEMORY; + return status; } - fSystemTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY, - O_RDWR); - if (fSystemTTYCookie == NULL) { + status = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY, O_RDWR, &fSystemTTYCookie); + if (status != B_OK) { TRACE_ALWAYS("open: failed to init system tty cookie\n"); gTTYModule->tty_destroy(fMasterTTY); gTTYModule->tty_destroy(fSlaveTTY); - return B_NO_MEMORY; + return status; } - fDeviceTTYCookie = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY, - O_RDWR); - if (fDeviceTTYCookie == NULL) { + status = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY, O_RDWR, &fDeviceTTYCookie); + if (status != B_OK) { TRACE_ALWAYS("open: failed to init device tty cookie\n"); gTTYModule->tty_destroy_cookie(fSystemTTYCookie); gTTYModule->tty_destroy(fMasterTTY); gTTYModule->tty_destroy(fSlaveTTY); - return B_NO_MEMORY; + return status; } ResetDevice(); diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp b/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp index e408669950..8056983060 100644 --- a/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp +++ b/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp @@ -287,42 +287,42 @@ SerialDevice::Service(struct tty *tty, uint32 op, void *buffer, size_t length) status_t SerialDevice::Open(uint32 flags) { + status_t status = B_OK; + if (fDeviceOpen) return B_BUSY; if (fDeviceRemoved) return B_DEV_NOT_READY; - fMasterTTY = gTTYModule->tty_create(usb_serial_service, NULL); - if (fMasterTTY == NULL) { + status = gTTYModule->tty_create(usb_serial_service, NULL, &fMasterTTY); + if (status != B_OK) { TRACE_ALWAYS("open: failed to init master tty\n"); - return B_NO_MEMORY; + return status; } - fSlaveTTY = gTTYModule->tty_create(usb_serial_service, fMasterTTY); - if (fSlaveTTY == NULL) { + status = gTTYModule->tty_create(usb_serial_service, fMasterTTY, &fSlaveTTY); + if (status != B_OK) { TRACE_ALWAYS("open: failed to init slave tty\n"); gTTYModule->tty_destroy(fMasterTTY); - return B_NO_MEMORY; + return status; } - fSystemTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY, - O_RDWR); - if (fSystemTTYCookie == NULL) { + status = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY, O_RDWR, &fSystemTTYCookie); + if (status != B_OK) { TRACE_ALWAYS("open: failed to init system tty cookie\n"); gTTYModule->tty_destroy(fMasterTTY); gTTYModule->tty_destroy(fSlaveTTY); - return B_NO_MEMORY; + return status; } - fDeviceTTYCookie = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY, - O_RDWR); - if (fDeviceTTYCookie == NULL) { + status = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY, O_RDWR, &fDeviceTTYCookie); + if (status != B_OK) { TRACE_ALWAYS("open: failed to init device tty cookie\n"); gTTYModule->tty_destroy_cookie(fSystemTTYCookie); gTTYModule->tty_destroy(fMasterTTY); gTTYModule->tty_destroy(fSlaveTTY); - return B_NO_MEMORY; + return status; } ResetDevice(); @@ -342,9 +342,8 @@ SerialDevice::Open(uint32 flags) | USB_CDC_CONTROL_SIGNAL_STATE_RTS; SetControlLineState(fControlOut); - status_t status = gUSBModule->queue_interrupt(fControlPipe, - fInterruptBuffer, fInterruptBufferSize, _InterruptCallbackFunction, - this); + status = gUSBModule->queue_interrupt(fControlPipe, fInterruptBuffer, fInterruptBufferSize, + _InterruptCallbackFunction, this); if (status < B_OK) TRACE_ALWAYS("failed to queue initial interrupt\n"); diff --git a/src/add-ons/kernel/drivers/pty/driver.cpp b/src/add-ons/kernel/drivers/pty/driver.cpp index bec8df418e..7d98a8b109 100644 --- a/src/add-ons/kernel/drivers/pty/driver.cpp +++ b/src/add-ons/kernel/drivers/pty/driver.cpp @@ -222,21 +222,23 @@ master_open(const char *name, uint32 flags, void **_cookie) return B_BUSY; } + status_t status = B_OK; + if (gMasterTTYs[index] == NULL) { - gMasterTTYs[index] = gTTYModule->tty_create(master_service, NULL); - if (gMasterTTYs[index] == NULL) - return B_NO_MEMORY; + status = gTTYModule->tty_create(master_service, NULL, &gMasterTTYs[index]); + if (status != B_OK) + return status; } if (gSlaveTTYs[index] == NULL) { - gSlaveTTYs[index] = gTTYModule->tty_create(slave_service, gMasterTTYs[index]); - if (gSlaveTTYs[index] == NULL) - return B_NO_MEMORY; + status = gTTYModule->tty_create(slave_service, gMasterTTYs[index], &gSlaveTTYs[index]); + if (status != B_OK) + return status; } - tty_cookie *cookie = gTTYModule->tty_create_cookie(gMasterTTYs[index], - gSlaveTTYs[index], flags); - if (cookie == NULL) - return B_NO_MEMORY; + tty_cookie *cookie; + status = gTTYModule->tty_create_cookie(gMasterTTYs[index], gSlaveTTYs[index], flags, &cookie); + if (status != B_OK) + return status; *_cookie = cookie; return B_OK; @@ -301,12 +303,11 @@ slave_open(const char *name, uint32 flags, void **_cookie) gSlaveTTYs[index]->settings->pgrp_id = -1; } - tty_cookie *cookie = gTTYModule->tty_create_cookie(gSlaveTTYs[index], - gMasterTTYs[index], flags); - if (cookie == NULL) { - gSlaveTTYs[index] = NULL; - return B_NO_MEMORY; - } + tty_cookie *cookie; + status_t status = gTTYModule->tty_create_cookie(gSlaveTTYs[index], gMasterTTYs[index], flags, + &cookie); + if (status != B_OK) + return status; if (makeControllingTTY) { gSlaveTTYs[index]->settings->session_id = sessionID; diff --git a/src/add-ons/kernel/generic/tty/tty.cpp b/src/add-ons/kernel/generic/tty/tty.cpp index 9318157656..6933fd97bb 100644 --- a/src/add-ons/kernel/generic/tty/tty.cpp +++ b/src/add-ons/kernel/generic/tty/tty.cpp @@ -1324,12 +1324,12 @@ tty_write_to_tty_slave(tty_cookie* sourceCookie, const void* _buffer, // #pragma mark - public API -struct tty* -tty_create(tty_service_func func, struct tty* master) +status_t +tty_create(tty_service_func func, struct tty* master, struct tty** _tty) { struct tty* tty = new(std::nothrow) (struct tty); if (tty == NULL) - return NULL; + return B_NO_MEMORY; if (master == NULL) { tty->is_master = true; @@ -1339,7 +1339,7 @@ tty_create(tty_service_func func, struct tty* master) delete tty->lock; delete tty->settings; delete tty; - return NULL; + return B_NO_MEMORY; } recursive_lock_init(tty->lock, "tty lock"); @@ -1356,19 +1356,24 @@ tty_create(tty_service_func func, struct tty* master) tty->select_pool = NULL; tty->pending_eof = 0; tty->hardware_bits = 0; + tty->is_exclusive = false; - if (init_line_buffer(tty->input_buffer, TTY_BUFFER_SIZE) < B_OK) { + status_t status = init_line_buffer(tty->input_buffer, TTY_BUFFER_SIZE); + + if (status < B_OK) { if (tty->is_master) { recursive_lock_destroy(tty->lock); delete tty->lock; } delete tty; - return NULL; + return status; } tty->service_func = func; - return tty; + *_tty = tty; + + return B_OK; } @@ -1388,17 +1393,18 @@ tty_destroy(struct tty* tty) } -tty_cookie* -tty_create_cookie(struct tty* tty, struct tty* otherTTY, uint32 openMode) +status_t +tty_create_cookie(struct tty* tty, struct tty* otherTTY, uint32 openMode, tty_cookie** _cookie) { tty_cookie* cookie = new(std::nothrow) tty_cookie; if (cookie == NULL) - return NULL; + return B_NO_MEMORY; cookie->blocking_semaphore = create_sem(0, "wait for tty close"); if (cookie->blocking_semaphore < 0) { + status_t status = cookie->blocking_semaphore; delete cookie; - return NULL; + return status; } cookie->tty = tty; @@ -1410,13 +1416,21 @@ tty_create_cookie(struct tty* tty, struct tty* otherTTY, uint32 openMode) RecursiveLocker locker(cookie->tty->lock); + if (tty->is_exclusive && geteuid() != 0) { + delete_sem(cookie->blocking_semaphore); + delete cookie; + return B_BUSY; + } + // add to the TTY's cookie list tty->cookies.Add(cookie); tty->open_count++; tty->ref_count++; tty->opened_count++; - return cookie; + *_cookie = cookie; + + return B_OK; } @@ -1513,6 +1527,8 @@ tty_close_cookie(tty_cookie* cookie) // notify a select write event on the other tty, if we've closed this tty if (cookie->other_tty->open_count > 0) tty_notify_select_event(cookie->other_tty, B_SELECT_WRITE); + + cookie->tty->is_exclusive = false; } } @@ -1944,6 +1960,18 @@ tty_control(tty_cookie* cookie, uint32 op, void* buffer, size_t length) return B_ERROR; } + + case TIOCEXCL: + { + tty->is_exclusive = true; + return B_OK; + } + + case TIOCNXCL: + { + tty->is_exclusive = false; + return B_OK; + } } TRACE(("tty: unsupported opcode %" B_PRIu32 "\n", op)); diff --git a/src/add-ons/kernel/generic/tty/tty_private.h b/src/add-ons/kernel/generic/tty/tty_private.h index 47f000c225..8268e260d6 100644 --- a/src/add-ons/kernel/generic/tty/tty_private.h +++ b/src/add-ons/kernel/generic/tty/tty_private.h @@ -140,17 +140,19 @@ struct tty { recursive_lock* lock; tty_settings* settings; uint8 hardware_bits; + bool is_exclusive; }; extern struct mutex gTTYCookieLock; extern struct recursive_lock gTTYRequestLock; -extern struct tty *tty_create(tty_service_func func, struct tty* masterTTY); +extern status_t tty_create(tty_service_func func, struct tty *masterTTY, + struct tty **tty); extern void tty_destroy(struct tty *tty); -extern tty_cookie *tty_create_cookie(struct tty *tty, struct tty *otherTTY, - uint32 openMode); +extern status_t tty_create_cookie(struct tty *tty, struct tty *otherTTY, + uint32 openMode, struct tty_cookie **cookie); extern void tty_destroy_cookie(tty_cookie *cookie); extern void tty_close_cookie(tty_cookie *cookie); diff --git a/src/bin/debug/strace/ioctl.cpp b/src/bin/debug/strace/ioctl.cpp index 6bc4856b2b..71d62441c9 100644 --- a/src/bin/debug/strace/ioctl.cpp +++ b/src/bin/debug/strace/ioctl.cpp @@ -137,6 +137,8 @@ static const ioctl_info kIOCtls[] = { IOCTL_INFO_ENTRY(TIOCMBIS), IOCTL_INFO_ENTRY(TIOCMBIC), IOCTL_INFO_ENTRY(TIOCOUTQ), + IOCTL_INFO_ENTRY(TIOCEXCL), + IOCTL_INFO_ENTRY(TIOCNXCL), // private termios IOCTL_INFO_ENTRY(B_IOCTL_GET_TTY_INDEX), IOCTL_INFO_ENTRY(B_IOCTL_GRANT_TTY),