Add a PTY multiplexer (/dev/ptmx) device.

When you open /dev/ptmx, you get a file descriptor pointing to one of the
available MasterPTY's. If none are available, you get an EBUSY.

This makes it possible to open multiple (up to 4) Terminals. :^)

To support this, I also added a CharacterDevice::open() that gets control
when VFS is opening a CharacterDevice. This is useful when we want to return
a custom FileDescriptor like we do here.
This commit is contained in:
Andreas Kling 2019-01-16 13:36:10 +01:00
parent b46ae2bf09
commit 9dd29f9aa9
Notes: sideshowbarker 2024-07-19 16:01:34 +09:00
13 changed files with 75 additions and 37 deletions

View file

@ -19,6 +19,7 @@ KERNEL_OBJS = \
ProcFileSystem.o \
RTC.o \
TTY.o \
PTYMultiplexer.o \
MasterPTY.o \
SlavePTY.o \
VirtualConsole.o \

View file

@ -6,7 +6,6 @@
class SlavePTY;
class MasterPTY final : public CharacterDevice {
AK_MAKE_ETERNAL
public:
explicit MasterPTY(unsigned index);
virtual ~MasterPTY() override;
@ -17,6 +16,7 @@ public:
virtual bool can_write(Process&) const override;
virtual bool is_master_pty() const override { return true; }
unsigned index() const { return m_index; }
String pts_name() const;
void on_slave_write(const byte*, size_t);

27
Kernel/PTYMultiplexer.cpp Normal file
View file

@ -0,0 +1,27 @@
#include "PTYMultiplexer.h"
#include "MasterPTY.h"
#include <LibC/errno_numbers.h>
PTYMultiplexer::PTYMultiplexer()
: CharacterDevice(5, 2)
{
m_freelist.ensureCapacity(4);
for (int i = 4; i > 0; --i)
m_freelist.unchecked_append(adopt(*new MasterPTY(i - 1)));
}
PTYMultiplexer::~PTYMultiplexer()
{
}
RetainPtr<FileDescriptor> PTYMultiplexer::open(int& error, int options)
{
LOCKER(m_lock);
if (m_freelist.is_empty()) {
error = -EBUSY;
return nullptr;
}
auto master = m_freelist.takeLast();
dbgprintf("PTYMultiplexer::open: Vending master %u\n", master->index());
return VFS::the().open(move(master), error, options);
}

24
Kernel/PTYMultiplexer.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include <VirtualFileSystem/CharacterDevice.h>
#include <AK/Lock.h>
class MasterPTY;
class PTYMultiplexer final : public CharacterDevice {
AK_MAKE_ETERNAL
public:
PTYMultiplexer();
virtual ~PTYMultiplexer() override;
// ^CharacterDevice
virtual RetainPtr<FileDescriptor> open(int& error, int options) override;
virtual ssize_t read(Process&, byte*, size_t) override { return 0; }
virtual ssize_t write(Process&, const byte*, size_t) override { return 0; }
virtual bool can_read(Process&) const override { return true; }
virtual bool can_write(Process&) const override { return true; }
private:
SpinLock m_lock;
Vector<RetainPtr<MasterPTY>> m_freelist;
};

View file

@ -617,9 +617,10 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring
} else {
m_fds.resize(m_max_open_file_descriptors);
if (tty) {
m_fds[0].set(tty->open(O_RDONLY));
m_fds[1].set(tty->open(O_WRONLY));
m_fds[2].set(tty->open(O_WRONLY));
int error;
m_fds[0].set(tty->open(error, O_RDONLY));
m_fds[1].set(tty->open(error, O_WRONLY));
m_fds[2].set(tty->open(error, O_WRONLY));
}
}

View file

@ -5,13 +5,13 @@
class MasterPTY;
class SlavePTY final : public TTY {
AK_MAKE_ETERNAL
public:
virtual ~SlavePTY() override;
virtual String tty_name() const override;
void on_master_write(const byte*, size_t);
unsigned index() const { return m_index; }
protected:
virtual void on_tty_write(const byte*, size_t) override;

View file

@ -22,7 +22,7 @@
#include "VirtualConsole.h"
#include "Scheduler.h"
#include "PS2MouseDevice.h"
#include "MasterPTY.h"
#include "PTYMultiplexer.h"
#define SPAWN_GUI_TEST_APP
//#define SPAWN_MULTIPLE_SHELLS
@ -37,10 +37,6 @@ VirtualConsole* tty3;
Keyboard* keyboard;
PS2MouseDevice* ps2mouse;
GUIEventDevice* gui_event_device;
MasterPTY* ptm0;
MasterPTY* ptm1;
MasterPTY* ptm2;
MasterPTY* ptm3;
#ifdef STRESS_TEST_SPAWNING
static void spawn_stress() NORETURN;
@ -80,10 +76,8 @@ static void init_stage2()
auto dev_random = make<RandomDevice>();
vfs->register_character_device(*dev_random);
VFS::the().register_character_device(*new MasterPTY(0));
VFS::the().register_character_device(*new MasterPTY(1));
VFS::the().register_character_device(*new MasterPTY(2));
VFS::the().register_character_device(*new MasterPTY(3));
auto dev_ptmx = make<PTYMultiplexer>();
vfs->register_character_device(*dev_ptmx);
vfs->register_character_device(*keyboard);
vfs->register_character_device(*ps2mouse);

View file

@ -9,6 +9,7 @@ mknod mnt/dev/tty1 c 4 1
mknod mnt/dev/tty2 c 4 2
mknod mnt/dev/tty3 c 4 3
mknod mnt/dev/psaux c 10 1
mknod mnt/dev/ptmx c 5 2
mknod mnt/dev/ptm0 c 10 0
mknod mnt/dev/ptm1 c 10 1
mknod mnt/dev/ptm2 c 10 2

View file

@ -59,23 +59,13 @@ static int max(int a, int b)
return a > b ? a : b;
}
static int open_ptm()
{
char buf[32];
for (unsigned i = 0; i < 4; ++i) {
sprintf(buf, "/dev/ptm%u", i);
int fd = open(buf, O_RDWR);
if (fd)
return fd;
}
dbgprintf("No master PTY available :(\n");
exit(1);
return -1;
}
int main(int, char**)
{
int ptm_fd = open_ptm();
int ptm_fd = open("/dev/ptmx", O_RDWR);
if (ptm_fd < 0) {
perror("open(ptmx)");
return 1;
}
make_shell(ptm_fd);

View file

@ -3,12 +3,11 @@
CharacterDevice::~CharacterDevice()
{
ASSERT_NOT_REACHED();
}
RetainPtr<FileDescriptor> CharacterDevice::open(int options)
RetainPtr<FileDescriptor> CharacterDevice::open(int& error, int options)
{
return VFS::the().open(*this, options);
return VFS::the().open(*this, error, options);
}
int CharacterDevice::ioctl(Process&, unsigned, unsigned)

View file

@ -13,7 +13,7 @@ public:
InodeMetadata metadata() const { return { }; }
RetainPtr<FileDescriptor> open(int options);
virtual RetainPtr<FileDescriptor> open(int& error, int options);
virtual bool can_read(Process&) const = 0;
virtual bool can_write(Process&) const = 0;

View file

@ -128,11 +128,12 @@ void VFS::traverse_directory_inode(Inode& dir_inode, Function<bool(const FS::Dir
});
}
RetainPtr<FileDescriptor> VFS::open(CharacterDevice& device, int options)
RetainPtr<FileDescriptor> VFS::open(RetainPtr<CharacterDevice>&& device, int& error, int options)
{
// FIXME: Respect options.
(void) options;
return FileDescriptor::create(device);
(void) error;
return FileDescriptor::create(move(device));
}
RetainPtr<FileDescriptor> VFS::open(const String& path, int& error, int options, InodeIdentifier base)
@ -148,7 +149,7 @@ RetainPtr<FileDescriptor> VFS::open(const String& path, int& error, int options,
kprintf("VFS::open: no such character device %u,%u\n", metadata.majorDevice, metadata.minorDevice);
return nullptr;
}
return FileDescriptor::create((*it).value);
return (*it).value->open(error, options);
}
return FileDescriptor::create(move(inode));
}

View file

@ -63,7 +63,7 @@ public:
bool mount_root(RetainPtr<FS>&&);
bool mount(RetainPtr<FS>&&, const String& path);
RetainPtr<FileDescriptor> open(CharacterDevice&, int options);
RetainPtr<FileDescriptor> open(RetainPtr<CharacterDevice>&&, int& error, int options);
RetainPtr<FileDescriptor> open(const String& path, int& error, int options = 0, InodeIdentifier base = InodeIdentifier());
RetainPtr<FileDescriptor> create(const String& path, InodeIdentifier base, int& error);
bool mkdir(const String& path, mode_t mode, InodeIdentifier base, int& error);