mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Add mkpty(2).
This commit is contained in:
parent
d5236cd8ac
commit
1a3b3f6deb
21 changed files with 471 additions and 77 deletions
|
@ -134,6 +134,7 @@ poll.o \
|
|||
process.o \
|
||||
psctl.o \
|
||||
ptable.o \
|
||||
pty.o \
|
||||
random.o \
|
||||
refcount.o \
|
||||
registers.o \
|
||||
|
|
|
@ -694,9 +694,9 @@ int Descriptor::tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp)
|
|||
return vnode->tcgetwincurpos(ctx, wcp);
|
||||
}
|
||||
|
||||
int Descriptor::tcgetwinsize(ioctx_t* ctx, struct winsize* ws)
|
||||
int Descriptor::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg)
|
||||
{
|
||||
return vnode->tcgetwinsize(ctx, ws);
|
||||
return vnode->ioctl(ctx, cmd, arg);
|
||||
}
|
||||
|
||||
int Descriptor::tcsetpgrp(ioctx_t* ctx, pid_t pgid)
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include <sortix/dirent.h>
|
||||
#include <sortix/fcntl.h>
|
||||
#include <sortix/ioctl.h>
|
||||
#include <sortix/stat.h>
|
||||
#include <sortix/timespec.h>
|
||||
#include <sortix/winsize.h>
|
||||
|
@ -225,7 +226,7 @@ public:
|
|||
const char* filename);
|
||||
virtual ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz);
|
||||
virtual int tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp);
|
||||
virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws);
|
||||
virtual int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg);
|
||||
virtual int tcsetpgrp(ioctx_t* ctx, pid_t pgid);
|
||||
virtual pid_t tcgetpgrp(ioctx_t* ctx);
|
||||
virtual int settermmode(ioctx_t* ctx, unsigned mode);
|
||||
|
@ -1219,23 +1220,27 @@ int Unode::tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int Unode::tcgetwinsize(ioctx_t* ctx, struct winsize* ws)
|
||||
int Unode::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg)
|
||||
{
|
||||
Channel* channel = server->Connect(ctx);
|
||||
if ( !channel )
|
||||
return -1;
|
||||
int ret = -1;
|
||||
struct fsm_req_tcgetwinsize msg;
|
||||
struct fsm_resp_tcgetwinsize resp;
|
||||
msg.ino = ino;
|
||||
if ( SendMessage(channel, FSM_REQ_TCGETWINSIZE, &msg, sizeof(msg)) &&
|
||||
RecvMessage(channel, FSM_RESP_TCGETWINSIZE, &resp, sizeof(resp)) &&
|
||||
ctx->copy_to_dest(ws, &resp.size, sizeof(*ws)) )
|
||||
ret = 0;
|
||||
channel->KernelClose();
|
||||
return ret;
|
||||
if ( cmd == TIOCGWINSZ )
|
||||
{
|
||||
struct winsize* ws = (struct winsize*) arg;
|
||||
Channel* channel = server->Connect(ctx);
|
||||
if ( !channel )
|
||||
return -1;
|
||||
int ret = -1;
|
||||
struct fsm_req_tcgetwinsize msg;
|
||||
struct fsm_resp_tcgetwinsize resp;
|
||||
msg.ino = ino;
|
||||
if ( SendMessage(channel, FSM_REQ_TCGETWINSIZE, &msg, sizeof(msg)) &&
|
||||
RecvMessage(channel, FSM_RESP_TCGETWINSIZE, &resp, sizeof(resp)) &&
|
||||
ctx->copy_to_dest(ws, &resp.size, sizeof(*ws)) )
|
||||
ret = 0;
|
||||
channel->KernelClose();
|
||||
return ret;
|
||||
}
|
||||
return errno = ENOTTY, -1;
|
||||
}
|
||||
|
||||
int Unode::tcsetpgrp(ioctx_t* ctx, pid_t pgid)
|
||||
{
|
||||
Channel* channel = server->Connect(ctx);
|
||||
|
|
|
@ -32,5 +32,6 @@
|
|||
#define __IOCTL_TYPE(value) ((value) & __IOCTL_TYPE_MASK)
|
||||
|
||||
#define TIOCGWINSZ __IOCTL(1, __IOCTL_TYPE_PTR)
|
||||
#define TIOCSWINSZ __IOCTL(2, __IOCTL_TYPE_PTR)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -78,7 +78,7 @@ public:
|
|||
int symlink(ioctx_t* ctx, const char* oldname, const char* filename);
|
||||
ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz);
|
||||
int tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp);
|
||||
int tcgetwinsize(ioctx_t* ctx, struct winsize* ws);
|
||||
int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg);
|
||||
int tcsetpgrp(ioctx_t* ctx, pid_t pgid);
|
||||
pid_t tcgetpgrp(ioctx_t* ctx);
|
||||
int settermmode(ioctx_t* ctx, unsigned mode);
|
||||
|
|
|
@ -84,7 +84,7 @@ public:
|
|||
const char* filename) = 0;
|
||||
virtual ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz) = 0;
|
||||
virtual int tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp) = 0;
|
||||
virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws) = 0;
|
||||
virtual int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg) = 0;
|
||||
virtual int tcsetpgrp(ioctx_t* ctx, pid_t pgid) = 0;
|
||||
virtual pid_t tcgetpgrp(ioctx_t* ctx) = 0;
|
||||
virtual int settermmode(ioctx_t* ctx, unsigned mode) = 0;
|
||||
|
@ -177,7 +177,7 @@ public:
|
|||
const char* filename);
|
||||
virtual ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz);
|
||||
virtual int tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp);
|
||||
virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws);
|
||||
virtual int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg);
|
||||
virtual int tcsetpgrp(ioctx_t* ctx, pid_t pgid);
|
||||
virtual pid_t tcgetpgrp(ioctx_t* ctx);
|
||||
virtual int settermmode(ioctx_t* ctx, unsigned mode);
|
||||
|
|
|
@ -115,6 +115,7 @@ off_t sys_lseek(int, off_t, int);
|
|||
int sys_memstat(size_t*, size_t*);
|
||||
int sys_mkdirat(int, const char*, mode_t);
|
||||
int sys_mkpartition(int, off_t, off_t, int);
|
||||
int sys_mkptyline(int*, int*, int);
|
||||
void* sys_mmap_wrapper(struct mmap_request*);
|
||||
int sys_mprotect(const void*, size_t, int);
|
||||
int sys_munmap(void*, size_t);
|
||||
|
|
|
@ -77,7 +77,7 @@ public:
|
|||
ssize_t readlink(ioctx_t* ctx, char* buf, size_t bufsiz);
|
||||
int fsbind(ioctx_t* ctx, Vnode* node, int flags);
|
||||
int tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp);
|
||||
int tcgetwinsize(ioctx_t* ctx, struct winsize* ws);
|
||||
int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg);
|
||||
int tcsetpgrp(ioctx_t* ctx, pid_t pgid);
|
||||
pid_t tcgetpgrp(ioctx_t* ctx);
|
||||
int settermmode(ioctx_t* ctx, unsigned mode);
|
||||
|
|
|
@ -173,7 +173,7 @@
|
|||
#define SYSCALL_UNMOUNTAT 150
|
||||
#define SYSCALL_FSM_MOUNTAT 151
|
||||
#define SYSCALL_CLOSEFROM 152
|
||||
#define SYSCALL_RESERVED1 153
|
||||
#define SYSCALL_MKPTYLINE 153
|
||||
#define SYSCALL_PSCTL 154
|
||||
#define SYSCALL_TCDRAIN 155
|
||||
#define SYSCALL_TCFLOW 156
|
||||
|
|
|
@ -290,10 +290,8 @@ int AbstractInode::tcgetwincurpos(ioctx_t* /*ctx*/, struct wincurpos* /*wcp*/)
|
|||
return errno = ENOTTY, -1;
|
||||
}
|
||||
|
||||
int AbstractInode::tcgetwinsize(ioctx_t* /*ctx*/, struct winsize* /*ws*/)
|
||||
int AbstractInode::ioctl(ioctx_t* /*ctx*/, int /*cmd*/, uintptr_t /*arg*/)
|
||||
{
|
||||
if ( inode_type == INODE_TYPE_TTY )
|
||||
return errno = EBADF, -1;
|
||||
return errno = ENOTTY, -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -368,13 +368,11 @@ int sys_fcntl(int fd, int cmd, uintptr_t arg)
|
|||
|
||||
int sys_ioctl(int fd, int cmd, uintptr_t arg)
|
||||
{
|
||||
switch ( cmd )
|
||||
{
|
||||
case TIOCGWINSZ:
|
||||
return sys_tcgetwinsize(fd, (struct winsize*) arg);
|
||||
default:
|
||||
return errno = EINVAL, -1;
|
||||
}
|
||||
Ref<Descriptor> desc = CurrentProcess()->GetDescriptor(fd);
|
||||
if ( !desc )
|
||||
return -1;
|
||||
ioctx_t ctx; SetupUserIOCtx(&ctx);
|
||||
return desc->ioctl(&ctx, cmd, arg);
|
||||
}
|
||||
|
||||
ssize_t sys_readdirents(int fd, struct dirent* dirent, size_t size)
|
||||
|
@ -645,14 +643,9 @@ int sys_tcgetwincurpos(int fd, struct wincurpos* wcp)
|
|||
return desc->tcgetwincurpos(&ctx, wcp);
|
||||
}
|
||||
|
||||
|
||||
int sys_tcgetwinsize(int fd, struct winsize* ws)
|
||||
{
|
||||
Ref<Descriptor> desc = CurrentProcess()->GetDescriptor(fd);
|
||||
if ( !desc )
|
||||
return -1;
|
||||
ioctx_t ctx; SetupUserIOCtx(&ctx);
|
||||
return desc->tcgetwinsize(&ctx, ws);
|
||||
return sys_ioctl(fd, TIOCGWINSZ, (uintptr_t) ws);
|
||||
}
|
||||
|
||||
int sys_tcsetpgrp(int fd, pid_t pgid)
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <wchar.h>
|
||||
|
||||
#include <sortix/fcntl.h>
|
||||
#include <sortix/ioctl.h>
|
||||
#include <sortix/keycodes.h>
|
||||
#include <sortix/poll.h>
|
||||
#include <sortix/signal.h>
|
||||
|
@ -122,6 +123,11 @@ LogTerminal::~LogTerminal()
|
|||
delete kblayout;
|
||||
}
|
||||
|
||||
void LogTerminal::tty_output(const unsigned char* buffer, size_t length)
|
||||
{
|
||||
Log::PrintData(buffer, length);
|
||||
}
|
||||
|
||||
int LogTerminal::sync(ioctx_t* /*ctx*/)
|
||||
{
|
||||
ScopedLock lock(&termlock);
|
||||
|
@ -281,4 +287,35 @@ ssize_t LogTerminal::tcsetblob(ioctx_t* ctx, const char* name, const void* buffe
|
|||
return errno = ENOENT, -1;
|
||||
}
|
||||
|
||||
int LogTerminal::tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp)
|
||||
{
|
||||
ScopedLock lock(&termlock);
|
||||
struct wincurpos retwcp;
|
||||
memset(&retwcp, 0, sizeof(retwcp));
|
||||
size_t cursor_column, cursor_row;
|
||||
Log::GetCursor(&cursor_column, &cursor_row);
|
||||
retwcp.wcp_col = cursor_column;
|
||||
retwcp.wcp_row = cursor_row;
|
||||
if ( !ctx->copy_to_dest(wcp, &retwcp, sizeof(retwcp)) )
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LogTerminal::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg)
|
||||
{
|
||||
if ( cmd == TIOCGWINSZ )
|
||||
{
|
||||
struct winsize* ws = (struct winsize*) arg;
|
||||
ScopedLock lock(&termlock);
|
||||
struct winsize retws;
|
||||
memset(&retws, 0, sizeof(retws));
|
||||
retws.ws_col = Log::Width();
|
||||
retws.ws_row = Log::Height();
|
||||
if ( !ctx->copy_to_dest(ws, &retws, sizeof(retws)) )
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
return TTY::ioctl(ctx, cmd, arg);
|
||||
}
|
||||
|
||||
} // namespace Sortix
|
||||
|
|
|
@ -35,10 +35,15 @@ public:
|
|||
virtual int sync(ioctx_t* ctx);
|
||||
virtual ssize_t tcgetblob(ioctx_t* ctx, const char* name, void* buffer, size_t count);
|
||||
virtual ssize_t tcsetblob(ioctx_t* ctx, const char* name, const void* buffer, size_t count);
|
||||
virtual int tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp);
|
||||
virtual int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg);
|
||||
|
||||
public:
|
||||
virtual void OnKeystroke(Keyboard* keyboard, void* user);
|
||||
|
||||
protected:
|
||||
virtual void tty_output(const unsigned char* buffer, size_t length);
|
||||
|
||||
private:
|
||||
void ProcessKeystroke(int kbkey);
|
||||
|
||||
|
|
333
kernel/pty.cpp
Normal file
333
kernel/pty.cpp
Normal file
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2016 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* pty.cpp
|
||||
* Pseudoterminals.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <sortix/fcntl.h>
|
||||
#include <sortix/ioctl.h>
|
||||
#include <sortix/poll.h>
|
||||
#include <sortix/stat.h>
|
||||
#include <sortix/termmode.h>
|
||||
#include <sortix/winsize.h>
|
||||
|
||||
#include <sortix/kernel/copy.h>
|
||||
#include <sortix/kernel/descriptor.h>
|
||||
#include <sortix/kernel/dtable.h>
|
||||
#include <sortix/kernel/inode.h>
|
||||
#include <sortix/kernel/ioctx.h>
|
||||
#include <sortix/kernel/kernel.h>
|
||||
#include <sortix/kernel/kthread.h>
|
||||
#include <sortix/kernel/poll.h>
|
||||
#include <sortix/kernel/process.h>
|
||||
#include <sortix/kernel/refcount.h>
|
||||
#include <sortix/kernel/signal.h>
|
||||
#include <sortix/kernel/syscall.h>
|
||||
#include <sortix/kernel/vnode.h>
|
||||
|
||||
#include "tty.h"
|
||||
|
||||
namespace Sortix {
|
||||
|
||||
class PTY : public TTY
|
||||
{
|
||||
public:
|
||||
PTY(mode_t mode, uid_t owner, gid_t group);
|
||||
virtual ~PTY();
|
||||
|
||||
public:
|
||||
virtual int sync(ioctx_t* ctx);
|
||||
virtual int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg);
|
||||
|
||||
public:
|
||||
ssize_t master_read(ioctx_t* ctx, uint8_t* buf, size_t count);
|
||||
ssize_t master_write(ioctx_t* ctx, const uint8_t* buf, size_t count);
|
||||
int master_poll(ioctx_t* ctx, PollNode* node);
|
||||
int master_ioctl(ioctx_t* ctx, int cmd, uintptr_t arg);
|
||||
|
||||
protected:
|
||||
virtual void tty_output(const unsigned char* buffer, size_t length);
|
||||
|
||||
private:
|
||||
struct winsize ws;
|
||||
kthread_mutex_t input_lock;
|
||||
kthread_mutex_t output_lock;
|
||||
kthread_cond_t output_ready_cond;
|
||||
kthread_cond_t output_possible_cond;
|
||||
size_t output_offset;
|
||||
size_t output_used;
|
||||
static const size_t output_size = 4096;
|
||||
uint8_t output[output_size];
|
||||
|
||||
};
|
||||
|
||||
PTY::PTY(mode_t mode, uid_t owner, gid_t group)
|
||||
: TTY(0, mode, owner, group)
|
||||
{
|
||||
tio.c_cflag |= CREAD;
|
||||
input_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||
output_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||
output_ready_cond = KTHREAD_COND_INITIALIZER;
|
||||
output_possible_cond = KTHREAD_COND_INITIALIZER;
|
||||
output_offset = 0;
|
||||
output_used = 0;
|
||||
memset(&ws, 0, sizeof(ws));
|
||||
}
|
||||
|
||||
PTY::~PTY()
|
||||
{
|
||||
}
|
||||
|
||||
ssize_t PTY::master_read(ioctx_t* ctx, uint8_t* buf, size_t count)
|
||||
{
|
||||
ScopedLockSignal lock(&output_lock);
|
||||
if ( !lock.IsAcquired() )
|
||||
return errno = EINTR, -1;
|
||||
while ( !output_used )
|
||||
{
|
||||
if ( ctx->dflags & O_NONBLOCK )
|
||||
return errno = EWOULDBLOCK, -1;
|
||||
if ( !kthread_cond_wait_signal(&output_ready_cond, &output_lock) )
|
||||
return errno = EINTR, -1;
|
||||
}
|
||||
size_t sofar = 0;
|
||||
while ( output_used && sofar < count )
|
||||
{
|
||||
size_t limit = output_size - output_offset;
|
||||
size_t possible = limit < output_used ? limit : output_used;
|
||||
size_t amount = count < possible ? count : possible;
|
||||
if ( !ctx->copy_to_dest(buf + sofar, output + output_offset, amount) )
|
||||
return sofar ? (ssize_t) sofar : -1;
|
||||
output_used -= amount;
|
||||
output_offset += amount;
|
||||
if ( output_offset == output_size )
|
||||
output_offset = 0;
|
||||
sofar += amount;
|
||||
kthread_cond_signal(&output_possible_cond);
|
||||
}
|
||||
return (ssize_t) sofar;
|
||||
}
|
||||
|
||||
ssize_t PTY::master_write(ioctx_t* ctx, const uint8_t* buf, size_t count)
|
||||
{
|
||||
ScopedLockSignal lock(&input_lock);
|
||||
if ( !lock.IsAcquired() )
|
||||
return errno = EINTR, -1;
|
||||
size_t sofar = 0;
|
||||
while ( sofar < count )
|
||||
{
|
||||
uint8_t input[1024];
|
||||
size_t amount = count < sizeof(input) ? count : sizeof(input);
|
||||
if ( !ctx->copy_from_src(input, buf + sofar, amount) )
|
||||
return sofar ? (ssize_t) sofar : -1;
|
||||
for ( size_t i = 0; i < amount; i++ )
|
||||
{
|
||||
if ( Signal::IsPending() )
|
||||
return sofar ? (ssize_t) sofar : (errno = EINTR, -1);
|
||||
ProcessByte(input[i]);
|
||||
}
|
||||
sofar += amount;
|
||||
}
|
||||
return (ssize_t) sofar;
|
||||
}
|
||||
|
||||
// TODO: This function can deadlock if data is written using master_write, with
|
||||
// tty ECHO on, then it echos the input through this function, but the
|
||||
// output buffer is full, so it blocks. But there only was a single thread
|
||||
// using the pty, which did a write, and now is waiting for itself to
|
||||
// read. Either this is a broken usage of pty's and you must have a
|
||||
// dedicated read thread, or need a dedicated kernel thread for each pty
|
||||
// that buffers up a large amount of input, then processes it at its own
|
||||
// pace.
|
||||
void PTY::tty_output(const unsigned char* buffer, size_t length)
|
||||
{
|
||||
ScopedLock lock(&output_lock);
|
||||
while ( length )
|
||||
{
|
||||
while ( output_used == output_size )
|
||||
if ( !kthread_cond_wait_signal(&output_possible_cond, &output_lock) )
|
||||
return; // TODO: Data loss?
|
||||
size_t offset = output_offset + output_used;
|
||||
if ( output_size <= offset )
|
||||
offset -= output_size;
|
||||
size_t left = output_size - output_used;
|
||||
size_t end = offset + left;
|
||||
if ( output_size < end )
|
||||
end = output_size;
|
||||
size_t possible = end - offset;
|
||||
size_t amount = length < possible ? length : possible;
|
||||
memcpy(output + offset, buffer, amount);
|
||||
buffer += amount;
|
||||
length -= amount;
|
||||
output_used += amount;
|
||||
kthread_cond_signal(&output_ready_cond);
|
||||
}
|
||||
}
|
||||
|
||||
int PTY::master_poll(ioctx_t* ctx, PollNode* node)
|
||||
{
|
||||
(void) ctx;
|
||||
(void) node;
|
||||
return errno = ENOTSUP, -1; // TODO: Implement this.
|
||||
}
|
||||
|
||||
int PTY::sync(ioctx_t* /*ctx*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PTY::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg)
|
||||
{
|
||||
if ( cmd == TIOCGWINSZ )
|
||||
{
|
||||
ScopedLock lock(&termlock);
|
||||
struct winsize* user_ws = (struct winsize*) arg;
|
||||
if ( !ctx->copy_to_dest(user_ws, &ws, sizeof(ws)) )
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
return TTY::ioctl(ctx, cmd, arg);
|
||||
}
|
||||
|
||||
int PTY::master_ioctl(ioctx_t* ctx, int cmd, uintptr_t arg)
|
||||
{
|
||||
if ( cmd == TIOCSWINSZ )
|
||||
{
|
||||
ScopedLock lock(&termlock);
|
||||
const struct winsize* user_ws = (const struct winsize*) arg;
|
||||
if ( !ctx->copy_from_src(&ws, user_ws, sizeof(ws)) )
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
return ioctl(ctx, cmd, arg);
|
||||
}
|
||||
|
||||
class MasterNode : public AbstractInode
|
||||
{
|
||||
public:
|
||||
MasterNode(uid_t owner, gid_t group, mode_t mode, Ref<PTY> pty);
|
||||
virtual ~MasterNode();
|
||||
virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count);
|
||||
virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count);
|
||||
virtual int poll(ioctx_t* ctx, PollNode* node);
|
||||
virtual int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg);
|
||||
|
||||
public:
|
||||
Ref<PTY> pty;
|
||||
|
||||
};
|
||||
|
||||
MasterNode::MasterNode(uid_t owner, gid_t group, mode_t mode, Ref<PTY> pty)
|
||||
: pty(pty)
|
||||
{
|
||||
inode_type = INODE_TYPE_TTY;
|
||||
this->dev = (dev_t) this;
|
||||
this->ino = (ino_t) this;
|
||||
this->stat_uid = owner;
|
||||
this->stat_gid = group;
|
||||
this->type = S_IFCHR;
|
||||
this->stat_mode = (mode & S_SETABLE) | this->type;
|
||||
}
|
||||
|
||||
MasterNode::~MasterNode()
|
||||
{
|
||||
// TODO: Destrution of master should probably SIGHUP everything that has the
|
||||
// pty as its controlling terminal.
|
||||
}
|
||||
|
||||
ssize_t MasterNode::read(ioctx_t* ctx, uint8_t* buf, size_t count)
|
||||
{
|
||||
return pty->master_read(ctx, buf, count);
|
||||
}
|
||||
|
||||
ssize_t MasterNode::write(ioctx_t* ctx, const uint8_t* buf, size_t count)
|
||||
{
|
||||
return pty->master_write(ctx, buf, count);
|
||||
}
|
||||
|
||||
int MasterNode::poll(ioctx_t* ctx, PollNode* node)
|
||||
{
|
||||
return pty->master_poll(ctx, node);
|
||||
}
|
||||
|
||||
int MasterNode::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg)
|
||||
{
|
||||
return pty->master_ioctl(ctx, cmd, arg);
|
||||
}
|
||||
|
||||
int sys_mkptyline(int* master_fd_user, int* slave_fd_user, int flags)
|
||||
{
|
||||
int fdflags = 0;
|
||||
if ( flags & O_CLOEXEC ) fdflags |= FD_CLOEXEC;
|
||||
if ( flags & O_CLOFORK ) fdflags |= FD_CLOFORK;
|
||||
flags &= ~(O_CLOEXEC | O_CLOFORK);
|
||||
|
||||
if ( flags & ~(O_NONBLOCK) )
|
||||
return errno = EINVAL, -1;
|
||||
|
||||
Process* process = CurrentProcess();
|
||||
uid_t uid = process->uid;
|
||||
uid_t gid = process->gid;
|
||||
mode_t mode = 0600;
|
||||
|
||||
Ref<PTY> slave_inode(new PTY(uid, gid, mode));
|
||||
if ( !slave_inode )
|
||||
return -1;
|
||||
Ref<MasterNode> master_inode(new MasterNode(uid, gid, mode, slave_inode));
|
||||
if ( !master_inode )
|
||||
return -1;
|
||||
|
||||
Ref<Vnode> master_vnode(new Vnode(master_inode, Ref<Vnode>(NULL), 0, 0));
|
||||
Ref<Vnode> slave_vnode(new Vnode(slave_inode, Ref<Vnode>(NULL), 0, 0));
|
||||
master_inode.Reset();
|
||||
slave_inode.Reset();
|
||||
if ( !master_vnode || !slave_vnode )
|
||||
return -1;
|
||||
|
||||
Ref<Descriptor> master_desc(new Descriptor(master_vnode, O_READ | O_WRITE | flags));
|
||||
Ref<Descriptor> slave_desc(new Descriptor(slave_vnode, O_READ | O_WRITE | flags));
|
||||
master_vnode.Reset();
|
||||
slave_vnode.Reset();
|
||||
if ( !master_desc || !slave_desc )
|
||||
return -1;
|
||||
|
||||
Ref<DescriptorTable> dtable = process->GetDTable();
|
||||
int master_fd = dtable->Allocate(master_desc, fdflags);
|
||||
int slave_fd = dtable->Allocate(slave_desc, fdflags);
|
||||
master_desc.Reset();
|
||||
slave_desc.Reset();
|
||||
if ( master_fd < 0 || slave_fd < 0 )
|
||||
{
|
||||
if ( 0 < master_fd )
|
||||
dtable->Free(master_fd);
|
||||
if ( 0 < master_fd )
|
||||
dtable->Free(slave_fd);
|
||||
return -1;
|
||||
}
|
||||
dtable.Reset();
|
||||
|
||||
if ( !CopyToUser(master_fd_user, &master_fd, sizeof(int)) ||
|
||||
!CopyToUser(slave_fd_user, &slave_fd, sizeof(int)) )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Sortix
|
|
@ -187,7 +187,7 @@ void* syscall_list[SYSCALL_MAX_NUM + 1] =
|
|||
[SYSCALL_UNMOUNTAT] = (void*) sys_unmountat,
|
||||
[SYSCALL_FSM_MOUNTAT] = (void*) sys_fsm_mountat,
|
||||
[SYSCALL_CLOSEFROM] = (void*) sys_closefrom,
|
||||
[SYSCALL_RESERVED1] = (void*) sys_bad_syscall,
|
||||
[SYSCALL_MKPTYLINE] = (void*) sys_mkptyline,
|
||||
[SYSCALL_PSCTL] = (void*) sys_psctl,
|
||||
[SYSCALL_TCDRAIN] = (void*) sys_tcdrain,
|
||||
[SYSCALL_TCFLOW] = (void*) sys_tcflow,
|
||||
|
|
|
@ -201,30 +201,9 @@ int TTY::gettermmode(ioctx_t* ctx, unsigned int* mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int TTY::tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp)
|
||||
int TTY::tcgetwincurpos(ioctx_t* /*ctx*/, struct wincurpos* /*wcp*/)
|
||||
{
|
||||
ScopedLock lock(&termlock);
|
||||
struct wincurpos retwcp;
|
||||
memset(&retwcp, 0, sizeof(retwcp));
|
||||
size_t cursor_column, cursor_row;
|
||||
Log::GetCursor(&cursor_column, &cursor_row);
|
||||
retwcp.wcp_col = cursor_column;
|
||||
retwcp.wcp_row = cursor_row;
|
||||
if ( !ctx->copy_to_dest(wcp, &retwcp, sizeof(retwcp)) )
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TTY::tcgetwinsize(ioctx_t* ctx, struct winsize* ws)
|
||||
{
|
||||
ScopedLock lock(&termlock);
|
||||
struct winsize retws;
|
||||
memset(&retws, 0, sizeof(retws));
|
||||
retws.ws_col = Log::Width();
|
||||
retws.ws_row = Log::Height();
|
||||
if ( !ctx->copy_to_dest(ws, &retws, sizeof(retws)) )
|
||||
return -1;
|
||||
return 0;
|
||||
return errno = ENOTSUP, -1;
|
||||
}
|
||||
|
||||
int TTY::tcsetpgrp(ioctx_t* /*ctx*/, pid_t pgid)
|
||||
|
@ -335,9 +314,9 @@ void TTY::ProcessByte(unsigned char byte, uint32_t control_unicode)
|
|||
{
|
||||
// TODO: Handle tab specially. (Is that even possible without
|
||||
// knowing cursor position?).
|
||||
Log::Print("\b \b");
|
||||
tty_output("\b \b");
|
||||
if ( !IsByteUnescaped(delchar) )
|
||||
Log::Print("\b \b");
|
||||
tty_output("\b \b");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -364,9 +343,9 @@ void TTY::ProcessByte(unsigned char byte, uint32_t control_unicode)
|
|||
linebuffer.Backspace();
|
||||
if ( tio.c_lflag & ECHOE )
|
||||
{
|
||||
Log::Print("\b \b");
|
||||
tty_output("\b \b");
|
||||
if ( !IsByteUnescaped(delchar) )
|
||||
Log::Print("\b \b");
|
||||
tty_output("\b \b");
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -383,9 +362,9 @@ void TTY::ProcessByte(unsigned char byte, uint32_t control_unicode)
|
|||
continue;
|
||||
if ( tio.c_lflag & ECHOE )
|
||||
{
|
||||
Log::Print("\b \b");
|
||||
tty_output("\b \b");
|
||||
if ( !IsByteUnescaped(delchar) )
|
||||
Log::Print("\b \b");
|
||||
tty_output("\b \b");
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -398,7 +377,7 @@ void TTY::ProcessByte(unsigned char byte, uint32_t control_unicode)
|
|||
ProcessUnicode(KBKEY_ENCODE(KBKEY_ENTER));
|
||||
ProcessByte('\n');
|
||||
ProcessUnicode(KBKEY_ENCODE(-KBKEY_ENTER));
|
||||
Log::PrintF("\e[H\e[2J");
|
||||
tty_output("\e[H\e[2J");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -419,9 +398,12 @@ void TTY::ProcessByte(unsigned char byte, uint32_t control_unicode)
|
|||
if ( tio.c_lflag & ECHO )
|
||||
{
|
||||
if ( IsByteUnescaped(byte) )
|
||||
Log::PrintData(&byte, 1);
|
||||
tty_output(&byte, 1);
|
||||
else
|
||||
Log::PrintF("^%c", CONTROL(byte));
|
||||
{
|
||||
unsigned char cs[2] = { '^', (unsigned char) CONTROL(byte) };
|
||||
tty_output(cs, sizeof(cs));
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(tio.c_lflag & ICANON) || byte == '\n' )
|
||||
|
@ -523,7 +505,7 @@ ssize_t TTY::write(ioctx_t* ctx, const uint8_t* io_buffer, size_t count)
|
|||
return errno = EINTR, -1;
|
||||
// TODO: Add support for ioctx to the kernel log.
|
||||
const size_t BUFFER_SIZE = 64UL;
|
||||
char buffer[BUFFER_SIZE];
|
||||
unsigned char buffer[BUFFER_SIZE];
|
||||
size_t sofar = 0;
|
||||
while ( sofar < count )
|
||||
{
|
||||
|
@ -532,7 +514,7 @@ ssize_t TTY::write(ioctx_t* ctx, const uint8_t* io_buffer, size_t count)
|
|||
amount = BUFFER_SIZE;
|
||||
if ( !ctx->copy_from_src(buffer, io_buffer + sofar, amount) )
|
||||
return -1;
|
||||
Log::PrintData(buffer, amount);
|
||||
tty_output(buffer, amount);
|
||||
sofar += amount;
|
||||
if ( sofar < count )
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#ifndef SORTIX_TTY_H
|
||||
#define SORTIX_TTY_H
|
||||
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include <sortix/termios.h>
|
||||
|
@ -45,7 +46,6 @@ public:
|
|||
virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count);
|
||||
virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count);
|
||||
virtual int tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp);
|
||||
virtual int tcgetwinsize(ioctx_t* ctx, struct winsize* ws);
|
||||
virtual int tcsetpgrp(ioctx_t* ctx, pid_t pgid);
|
||||
virtual pid_t tcgetpgrp(ioctx_t* ctx);
|
||||
virtual int settermmode(ioctx_t* ctx, unsigned termmode);
|
||||
|
@ -59,6 +59,13 @@ public:
|
|||
virtual int tcsendbreak(ioctx_t* ctx, int duration);
|
||||
virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio);
|
||||
|
||||
protected:
|
||||
void tty_output(const char* str)
|
||||
{
|
||||
tty_output((const unsigned char*) str, strlen(str));
|
||||
}
|
||||
virtual void tty_output(const unsigned char* buffer, size_t length) = 0;
|
||||
|
||||
protected:
|
||||
void ProcessUnicode(uint32_t unicode);
|
||||
void ProcessByte(unsigned char byte, uint32_t control_unicode = 0);
|
||||
|
|
|
@ -332,9 +332,9 @@ int Vnode::tcgetwincurpos(ioctx_t* ctx, struct wincurpos* wcp)
|
|||
return inode->tcgetwincurpos(ctx, wcp);
|
||||
}
|
||||
|
||||
int Vnode::tcgetwinsize(ioctx_t* ctx, struct winsize* ws)
|
||||
int Vnode::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg)
|
||||
{
|
||||
return inode->tcgetwinsize(ctx, ws);
|
||||
return inode->ioctl(ctx, cmd, arg);
|
||||
}
|
||||
|
||||
int Vnode::tcsetpgrp(ioctx_t* ctx, pid_t pgid)
|
||||
|
|
|
@ -602,6 +602,7 @@ sys/uio/writev.o \
|
|||
sys/utsname/uname.o \
|
||||
sys/wait/wait.o \
|
||||
sys/wait/waitpid.o \
|
||||
termios/mkptyline.o \
|
||||
termios/tcdrain.o \
|
||||
termios/tcflow.o \
|
||||
termios/tcflush.o \
|
||||
|
|
|
@ -69,6 +69,7 @@ int tcsetattr(int, int, const struct termios*);
|
|||
|
||||
/* Functions that are Sortix extensions. */
|
||||
#if __USE_SORTIX
|
||||
int mkptyline(int*, int*, int);
|
||||
ssize_t tcgetblob(int, const char*, void*, size_t);
|
||||
ssize_t tcsetblob(int, const char*, const void*, size_t);
|
||||
int tcgetwincurpos(int, struct wincurpos*);
|
||||
|
|
29
libc/termios/mkptyline.cpp
Normal file
29
libc/termios/mkptyline.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* termios/mkptyline.cpp
|
||||
* Creates an unnamed pseudoterminal.
|
||||
*/
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <termios.h>
|
||||
|
||||
DEFN_SYSCALL3(int, sys_mkptyline, SYSCALL_MKPTYLINE, int*, int*, int);
|
||||
|
||||
extern "C" int mkptyline(int* master_fd, int* slave_fd, int flags)
|
||||
{
|
||||
return sys_mkptyline(master_fd, slave_fd, flags);
|
||||
}
|
Loading…
Add table
Reference in a new issue