1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 16:18:12 +00:00

Add basic PTY support.

For now, there are four hard-coded PTYs: /dev/pt{m,s}[0123]
Use this in the Terminal to open a pty pair and spawn a shell.
This commit is contained in:
Andreas Kling 2019-01-15 06:30:19 +01:00
parent ecb4ab0943
commit 2f74c2f430
21 changed files with 287 additions and 10 deletions

View file

@ -19,6 +19,8 @@ KERNEL_OBJS = \
ProcFileSystem.o \ ProcFileSystem.o \
RTC.o \ RTC.o \
TTY.o \ TTY.o \
MasterPTY.o \
SlavePTY.o \
VirtualConsole.o \ VirtualConsole.o \
FIFO.o \ FIFO.o \
Scheduler.o \ Scheduler.o \

42
Kernel/MasterPTY.cpp Normal file
View file

@ -0,0 +1,42 @@
#include "MasterPTY.h"
#include "SlavePTY.h"
MasterPTY::MasterPTY(unsigned index)
: CharacterDevice(10, index)
, m_index(index)
{
}
MasterPTY::~MasterPTY()
{
}
String MasterPTY::pts_name() const
{
dbgprintf("MasterPTY::pts_name requested for index %u!\n", m_index);
char buffer[32];
ksprintf(buffer, "/dev/pts%u", m_index);
return buffer;
}
ssize_t MasterPTY::read(byte* buffer, size_t size)
{
return m_buffer.read(buffer, size);
}
ssize_t MasterPTY::write(const byte* buffer, size_t size)
{
m_slave->on_master_write(buffer, size);
return size;
}
bool MasterPTY::has_data_available_for_reading(Process&) const
{
return !m_buffer.is_empty();
}
void MasterPTY::on_slave_write(const byte* data, size_t size)
{
m_buffer.write(data, size);
}

26
Kernel/MasterPTY.h Normal file
View file

@ -0,0 +1,26 @@
#pragma once
#include <VirtualFileSystem/CharacterDevice.h>
#include "DoubleBuffer.h"
class SlavePTY;
class MasterPTY final : public CharacterDevice {
public:
explicit MasterPTY(unsigned index);
virtual ~MasterPTY() override;
void set_slave(SlavePTY& slave) { m_slave = &slave; }
virtual ssize_t read(byte*, size_t) override;
virtual ssize_t write(const byte*, size_t) override;
virtual bool has_data_available_for_reading(Process&) const override;
virtual bool is_master_pty() const override { return true; }
String pts_name() const;
void on_slave_write(const byte*, size_t);
private:
unsigned m_index;
SlavePTY* m_slave { nullptr };
DoubleBuffer m_buffer;
};

View file

@ -19,6 +19,7 @@
#include "FIFO.h" #include "FIFO.h"
#include "KSyms.h" #include "KSyms.h"
#include <Widgets/Window.h> #include <Widgets/Window.h>
#include "MasterPTY.h"
//#define DEBUG_IO //#define DEBUG_IO
//#define TASK_DEBUG //#define TASK_DEBUG
@ -989,6 +990,23 @@ int Process::sys$ttyname_r(int fd, char* buffer, size_t size)
return 0; return 0;
} }
int Process::sys$ptsname_r(int fd, char* buffer, size_t size)
{
if (!validate_write(buffer, size))
return -EFAULT;
auto* descriptor = file_descriptor(fd);
if (!descriptor)
return -EBADF;
auto* master_pty = descriptor->master_pty();
if (!master_pty)
return -ENOTTY;
auto pts_name = master_pty->pts_name();
if (size < pts_name.length() + 1)
return -ERANGE;
strcpy(buffer, pts_name.characters());
return 0;
}
ssize_t Process::sys$write(int fd, const void* data, size_t size) ssize_t Process::sys$write(int fd, const void* data, size_t size)
{ {
if (!validate_read(data, size)) if (!validate_read(data, size))
@ -1270,7 +1288,7 @@ int Process::sys$open(const char* path, int options)
return -EFAULT; return -EFAULT;
if (number_of_open_file_descriptors() >= m_max_open_file_descriptors) if (number_of_open_file_descriptors() >= m_max_open_file_descriptors)
return -EMFILE; return -EMFILE;
int error; int error = -EWHYTHO;
auto descriptor = VFS::the().open(path, error, options, cwd_inode()->identifier()); auto descriptor = VFS::the().open(path, error, options, cwd_inode()->identifier());
if (!descriptor) if (!descriptor)
return error; return error;

View file

@ -167,6 +167,7 @@ public:
int sys$uname(utsname*); int sys$uname(utsname*);
int sys$readlink(const char*, char*, size_t); int sys$readlink(const char*, char*, size_t);
int sys$ttyname_r(int fd, char*, size_t); int sys$ttyname_r(int fd, char*, size_t);
int sys$ptsname_r(int fd, char*, size_t);
pid_t sys$fork(RegisterDump&); pid_t sys$fork(RegisterDump&);
int sys$execve(const char* filename, const char** argv, const char** envp); int sys$execve(const char* filename, const char** argv, const char** envp);
int sys$isatty(int fd); int sys$isatty(int fd);

30
Kernel/SlavePTY.cpp Normal file
View file

@ -0,0 +1,30 @@
#include "SlavePTY.h"
#include "MasterPTY.h"
SlavePTY::SlavePTY(unsigned index)
: TTY(11, index)
, m_index(index)
{
}
SlavePTY::~SlavePTY()
{
}
String SlavePTY::tty_name() const
{
char buffer[32];
ksprintf(buffer, "/dev/pts%u", m_index);
return buffer;
}
void SlavePTY::on_master_write(const byte* buffer, size_t size)
{
for (size_t i = 0; i < size; ++i)
emit(buffer[i]);
}
void SlavePTY::on_tty_write(const byte* data, size_t size)
{
m_master->on_slave_write(data, size);
}

24
Kernel/SlavePTY.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include "TTY.h"
class MasterPTY;
class SlavePTY final : public TTY {
public:
explicit SlavePTY(unsigned index);
virtual ~SlavePTY() override;
void set_master(MasterPTY& master) { m_master = &master; }
virtual String tty_name() const override;
void on_master_write(const byte*, size_t);
protected:
virtual void on_tty_write(const byte*, size_t) override;
private:
unsigned m_index;
MasterPTY* m_master { nullptr };
};

View file

@ -121,6 +121,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2,
return current->sys$readlink((const char*)arg1, (char*)arg2, (size_t)arg3); return current->sys$readlink((const char*)arg1, (char*)arg2, (size_t)arg3);
case Syscall::SC_ttyname_r: case Syscall::SC_ttyname_r:
return current->sys$ttyname_r((int)arg1, (char*)arg2, (size_t)arg3); return current->sys$ttyname_r((int)arg1, (char*)arg2, (size_t)arg3);
case Syscall::SC_ptsname_r:
return current->sys$ptsname_r((int)arg1, (char*)arg2, (size_t)arg3);
case Syscall::SC_setsid: case Syscall::SC_setsid:
return current->sys$setsid(); return current->sys$setsid();
case Syscall::SC_getsid: case Syscall::SC_getsid:

View file

@ -66,6 +66,7 @@
__ENUMERATE_SYSCALL(times) \ __ENUMERATE_SYSCALL(times) \
__ENUMERATE_SYSCALL(utime) \ __ENUMERATE_SYSCALL(utime) \
__ENUMERATE_SYSCALL(sync) \ __ENUMERATE_SYSCALL(sync) \
__ENUMERATE_SYSCALL(ptsname_r) \
__ENUMERATE_SYSCALL(gui_create_window) \ __ENUMERATE_SYSCALL(gui_create_window) \
__ENUMERATE_SYSCALL(gui_destroy_window) \ __ENUMERATE_SYSCALL(gui_destroy_window) \
__ENUMERATE_SYSCALL(gui_get_window_backing_store) \ __ENUMERATE_SYSCALL(gui_get_window_backing_store) \

View file

@ -7,7 +7,7 @@
#include "IRQHandler.h" #include "IRQHandler.h"
#include "PIC.h" #include "PIC.h"
#define PAGE_FAULT_DEBUG //#define PAGE_FAULT_DEBUG
struct DescriptorTablePointer { struct DescriptorTablePointer {
word size; word size;

View file

@ -22,6 +22,8 @@
#include "VirtualConsole.h" #include "VirtualConsole.h"
#include "Scheduler.h" #include "Scheduler.h"
#include "PS2MouseDevice.h" #include "PS2MouseDevice.h"
#include "MasterPTY.h"
#include "SlavePTY.h"
#define SPAWN_GUI_TEST_APP #define SPAWN_GUI_TEST_APP
//#define SPAWN_MULTIPLE_SHELLS //#define SPAWN_MULTIPLE_SHELLS
@ -36,6 +38,14 @@ VirtualConsole* tty3;
Keyboard* keyboard; Keyboard* keyboard;
PS2MouseDevice* ps2mouse; PS2MouseDevice* ps2mouse;
GUIEventDevice* gui_event_device; GUIEventDevice* gui_event_device;
MasterPTY* ptm0;
MasterPTY* ptm1;
MasterPTY* ptm2;
MasterPTY* ptm3;
SlavePTY* pts0;
SlavePTY* pts1;
SlavePTY* pts2;
SlavePTY* pts3;
#ifdef STRESS_TEST_SPAWNING #ifdef STRESS_TEST_SPAWNING
static void spawn_stress() NORETURN; static void spawn_stress() NORETURN;
@ -56,6 +66,16 @@ static void spawn_stress()
} }
#endif #endif
static void make_pty_pair(unsigned index)
{
auto* master = new MasterPTY(index);
auto* slave = new SlavePTY(index);
master->set_slave(*slave);
slave->set_master(*master);
VFS::the().register_character_device(*master);
VFS::the().register_character_device(*slave);
}
static void init_stage2() NORETURN; static void init_stage2() NORETURN;
static void init_stage2() static void init_stage2()
{ {
@ -75,6 +95,11 @@ static void init_stage2()
auto dev_random = make<RandomDevice>(); auto dev_random = make<RandomDevice>();
vfs->register_character_device(*dev_random); vfs->register_character_device(*dev_random);
make_pty_pair(0);
make_pty_pair(1);
make_pty_pair(2);
make_pty_pair(3);
vfs->register_character_device(*keyboard); vfs->register_character_device(*keyboard);
vfs->register_character_device(*ps2mouse); vfs->register_character_device(*ps2mouse);
vfs->register_character_device(*gui_event_device); vfs->register_character_device(*gui_event_device);

View file

@ -6,6 +6,8 @@ make -C ../LibC clean && \
make -C ../LibC && \ make -C ../LibC && \
make -C ../Userland clean && \ make -C ../Userland clean && \
make -C ../Userland && \ make -C ../Userland && \
make -C ../Terminal clean && \
make -C ../Terminal && \
make clean &&\ make clean &&\
make && \ make && \
sudo ./sync.sh sudo ./sync.sh

View file

@ -9,6 +9,14 @@ mknod mnt/dev/tty1 c 4 1
mknod mnt/dev/tty2 c 4 2 mknod mnt/dev/tty2 c 4 2
mknod mnt/dev/tty3 c 4 3 mknod mnt/dev/tty3 c 4 3
mknod mnt/dev/psaux c 10 1 mknod mnt/dev/psaux c 10 1
mknod mnt/dev/ptm0 c 10 0
mknod mnt/dev/ptm1 c 10 1
mknod mnt/dev/ptm2 c 10 2
mknod mnt/dev/ptm3 c 10 3
mknod mnt/dev/pts0 c 11 0
mknod mnt/dev/pts1 c 11 1
mknod mnt/dev/pts2 c 11 2
mknod mnt/dev/pts3 c 11 3
mknod mnt/dev/gui_events c 66 1 mknod mnt/dev/gui_events c 66 1
cp -R ../Base/* mnt/ cp -R ../Base/* mnt/
cp -v ../Userland/sh mnt/bin/sh cp -v ../Userland/sh mnt/bin/sh

View file

@ -5,8 +5,10 @@
#include <string.h> #include <string.h>
#include <alloca.h> #include <alloca.h>
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <AK/Assertions.h> #include <AK/Assertions.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <Kernel/Syscall.h>
extern "C" { extern "C" {
@ -215,6 +217,19 @@ long atol(const char* str)
return atoi(str); return atoi(str);
} }
static char ptsname_buf[32];
char* ptsname(int fd)
{
if (ptsname_r(fd, ptsname_buf, sizeof(ptsname_buf)) < 0)
return nullptr;
return ptsname_buf;
}
int ptsname_r(int fd, char* buffer, size_t size)
{
int rc = syscall(SC_ptsname_r, fd, buffer, size);
__RETURN_WITH_ERRNO(rc, rc, -1);
}
static unsigned long s_next_rand = 1; static unsigned long s_next_rand = 1;

View file

@ -18,6 +18,8 @@ long atol(const char*);
void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
void exit(int status) __NORETURN; void exit(int status) __NORETURN;
void abort() __NORETURN; void abort() __NORETURN;
char* ptsname(int fd);
int ptsname_r(int fd, char* buffer, size_t);
#define RAND_MAX 32767 #define RAND_MAX 32767
int rand(); int rand();

View file

@ -49,8 +49,6 @@ Terminal::Terminal()
word* line_mem = reinterpret_cast<word*>(m_buffer); word* line_mem = reinterpret_cast<word*>(m_buffer);
for (word i = 0; i < rows() * columns(); ++i) for (word i = 0; i < rows() * columns(); ++i)
line_mem[i] = 0x0720; line_mem[i] = 0x0720;
inject_string_at(2, 2, "I am text inside the Terminal buffer.");
} }
void Terminal::inject_string_at(word row, word column, const String& string) void Terminal::inject_string_at(word row, word column, const String& string)

View file

@ -11,12 +11,50 @@
#include <gui.h> #include <gui.h>
#include "Terminal.h" #include "Terminal.h"
static void paint(GraphicsBitmap& bitmap, int width, int height); static void make_shell(int ptm_fd)
int main(int argc, char** argv)
{ {
int fd = open("/dev/gui_events", O_RDONLY); pid_t pid = fork();
if (fd < 0) { if (pid == 0) {
const char* tty_name = ptsname(ptm_fd);
if (!tty_name) {
perror("ptsname");
exit(1);
}
close(ptm_fd);
int pts_fd = open(tty_name, O_RDWR);
dbgprintf("*** In child (%d), opening slave pty %s, pts_fd=%d\n", getpid(), tty_name, pts_fd);
close(0);
close(1);
close(2);
dup2(pts_fd, 0);
dup2(pts_fd, 1);
dup2(pts_fd, 2);
close(pts_fd);
int rc = execve("/bin/sh", nullptr, nullptr);
if (rc < 0) {
perror("execve");
exit(1);
}
ASSERT_NOT_REACHED();
} else {
dbgprintf("*** In parent, child is %d\n", pid);
}
}
int main(int, char**)
{
int ptm_fd = open("/dev/ptm0", O_RDWR);
if (ptm_fd < 0) {
perror("open");
return 1;
}
dbgprintf("ptm_fd = %d\n", ptm_fd);
make_shell(ptm_fd);
int event_fd = open("/dev/gui_events", O_RDONLY);
if (event_fd < 0) {
perror("open"); perror("open");
return 1; return 1;
} }
@ -26,8 +64,17 @@ int main(int argc, char** argv)
terminal.paint(); terminal.paint();
for (;;) { for (;;) {
byte buffer[1024];
ssize_t ptm_nread = read(ptm_fd, buffer, sizeof(buffer));
if (ptm_nread > 0) {
for (ssize_t i = 0; i < ptm_nread; ++i) {
terminal.on_char(buffer[i]);
}
terminal.paint();
}
#if 0
GUI_Event event; GUI_Event event;
ssize_t nread = read(fd, &event, sizeof(event)); ssize_t nread = read(event_fd, &event, sizeof(event));
if (nread < 0) { if (nread < 0) {
perror("read"); perror("read");
return 1; return 1;
@ -39,10 +86,13 @@ int main(int argc, char** argv)
case GUI_Event::Type::MouseDown: dbgprintf("WID=%x MouseDown %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break; case GUI_Event::Type::MouseDown: dbgprintf("WID=%x MouseDown %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
case GUI_Event::Type::MouseUp: dbgprintf("WID=%x MouseUp %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break; case GUI_Event::Type::MouseUp: dbgprintf("WID=%x MouseUp %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
case GUI_Event::Type::MouseMove: dbgprintf("WID=%x MouseMove %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break; case GUI_Event::Type::MouseMove: dbgprintf("WID=%x MouseMove %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
default:
ASSERT_NOT_REACHED();
} }
if (event.type == GUI_Event::Type::MouseDown) if (event.type == GUI_Event::Type::MouseDown)
terminal.paint(); terminal.paint();
#endif
} }
return 0; return 0;
} }

View file

@ -21,6 +21,7 @@ public:
unsigned minor() const { return m_minor; } unsigned minor() const { return m_minor; }
virtual bool is_tty() const { return false; } virtual bool is_tty() const { return false; }
virtual bool is_master_pty() const { return false; }
virtual int ioctl(Process&, unsigned request, unsigned arg); virtual int ioctl(Process&, unsigned request, unsigned arg);

View file

@ -8,6 +8,7 @@
#ifdef SERENITY #ifdef SERENITY
#include "TTY.h" #include "TTY.h"
#include "MasterPTY.h"
#endif #endif
RetainPtr<FileDescriptor> FileDescriptor::create(RetainPtr<Vnode>&& vnode) RetainPtr<FileDescriptor> FileDescriptor::create(RetainPtr<Vnode>&& vnode)
@ -258,6 +259,29 @@ TTY* FileDescriptor::tty()
return static_cast<TTY*>(device); return static_cast<TTY*>(device);
return nullptr; return nullptr;
} }
bool FileDescriptor::is_master_pty() const
{
if (is_fifo())
return false;
if (auto* device = m_vnode->characterDevice())
return device->is_master_pty();
return false;
}
const MasterPTY* FileDescriptor::master_pty() const
{
if (!is_master_pty())
return nullptr;
return static_cast<const MasterPTY*>(m_vnode->characterDevice());
}
MasterPTY* FileDescriptor::master_pty()
{
if (!is_master_pty())
return nullptr;
return static_cast<MasterPTY*>(m_vnode->characterDevice());
}
#endif #endif
int FileDescriptor::close() int FileDescriptor::close()

View file

@ -9,6 +9,7 @@
#ifdef SERENITY #ifdef SERENITY
class TTY; class TTY;
class MasterPTY;
class Process; class Process;
#endif #endif
@ -46,6 +47,10 @@ public:
bool is_tty() const; bool is_tty() const;
const TTY* tty() const; const TTY* tty() const;
TTY* tty(); TTY* tty();
bool is_master_pty() const;
const MasterPTY* master_pty() const;
MasterPTY* master_pty();
#endif #endif
InodeMetadata metadata() const { return m_vnode->metadata(); } InodeMetadata metadata() const { return m_vnode->metadata(); }

View file

@ -265,6 +265,7 @@ RetainPtr<FileDescriptor> VFS::open(const String& path, int& error, int options,
auto inode = resolve_path(path, base, error, options); auto inode = resolve_path(path, base, error, options);
if (!inode.is_valid()) if (!inode.is_valid())
return nullptr; return nullptr;
// FIXME: Propagate any error from get_or_create_node().
auto vnode = get_or_create_node(inode); auto vnode = get_or_create_node(inode);
if (!vnode) if (!vnode)
return nullptr; return nullptr;