diff --git a/kernel/Makefile b/kernel/Makefile index e13bc732..043555ad 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -134,6 +134,7 @@ poll.o \ process.o \ psctl.o \ ptable.o \ +pty.o \ random.o \ refcount.o \ registers.o \ diff --git a/kernel/descriptor.cpp b/kernel/descriptor.cpp index ca1d3fb4..645f8c7c 100644 --- a/kernel/descriptor.cpp +++ b/kernel/descriptor.cpp @@ -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) diff --git a/kernel/fs/user.cpp b/kernel/fs/user.cpp index 44b0ed05..0af14f9e 100644 --- a/kernel/fs/user.cpp +++ b/kernel/fs/user.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -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); diff --git a/kernel/include/sortix/ioctl.h b/kernel/include/sortix/ioctl.h index e573989c..ea92dde3 100644 --- a/kernel/include/sortix/ioctl.h +++ b/kernel/include/sortix/ioctl.h @@ -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 diff --git a/kernel/include/sortix/kernel/descriptor.h b/kernel/include/sortix/kernel/descriptor.h index 13e9a485..f995d219 100644 --- a/kernel/include/sortix/kernel/descriptor.h +++ b/kernel/include/sortix/kernel/descriptor.h @@ -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); diff --git a/kernel/include/sortix/kernel/inode.h b/kernel/include/sortix/kernel/inode.h index b2017dc5..9e8efe34 100644 --- a/kernel/include/sortix/kernel/inode.h +++ b/kernel/include/sortix/kernel/inode.h @@ -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); diff --git a/kernel/include/sortix/kernel/syscall.h b/kernel/include/sortix/kernel/syscall.h index ebe2696e..063ca6ee 100644 --- a/kernel/include/sortix/kernel/syscall.h +++ b/kernel/include/sortix/kernel/syscall.h @@ -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); diff --git a/kernel/include/sortix/kernel/vnode.h b/kernel/include/sortix/kernel/vnode.h index 85b51230..476aea7e 100644 --- a/kernel/include/sortix/kernel/vnode.h +++ b/kernel/include/sortix/kernel/vnode.h @@ -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); diff --git a/kernel/include/sortix/syscall.h b/kernel/include/sortix/syscall.h index 260151fe..b80d032f 100644 --- a/kernel/include/sortix/syscall.h +++ b/kernel/include/sortix/syscall.h @@ -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 diff --git a/kernel/inode.cpp b/kernel/inode.cpp index c44b964a..82776f7c 100644 --- a/kernel/inode.cpp +++ b/kernel/inode.cpp @@ -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; } diff --git a/kernel/io.cpp b/kernel/io.cpp index b8fe2d9a..32a4b8d6 100644 --- a/kernel/io.cpp +++ b/kernel/io.cpp @@ -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 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 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) diff --git a/kernel/logterminal.cpp b/kernel/logterminal.cpp index c5b9a4d3..188df322 100644 --- a/kernel/logterminal.cpp +++ b/kernel/logterminal.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -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 diff --git a/kernel/logterminal.h b/kernel/logterminal.h index 4327de68..cc41ad2b 100644 --- a/kernel/logterminal.h +++ b/kernel/logterminal.h @@ -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); diff --git a/kernel/pty.cpp b/kernel/pty.cpp new file mode 100644 index 00000000..d0254dde --- /dev/null +++ b/kernel/pty.cpp @@ -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 + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + 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; + +}; + +MasterNode::MasterNode(uid_t owner, gid_t group, mode_t mode, Ref 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 slave_inode(new PTY(uid, gid, mode)); + if ( !slave_inode ) + return -1; + Ref master_inode(new MasterNode(uid, gid, mode, slave_inode)); + if ( !master_inode ) + return -1; + + Ref master_vnode(new Vnode(master_inode, Ref(NULL), 0, 0)); + Ref slave_vnode(new Vnode(slave_inode, Ref(NULL), 0, 0)); + master_inode.Reset(); + slave_inode.Reset(); + if ( !master_vnode || !slave_vnode ) + return -1; + + Ref master_desc(new Descriptor(master_vnode, O_READ | O_WRITE | flags)); + Ref 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 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 diff --git a/kernel/syscall.cpp b/kernel/syscall.cpp index 4d5abdb7..4b8f4cff 100644 --- a/kernel/syscall.cpp +++ b/kernel/syscall.cpp @@ -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, diff --git a/kernel/tty.cpp b/kernel/tty.cpp index 99170192..83b7ca65 100644 --- a/kernel/tty.cpp +++ b/kernel/tty.cpp @@ -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 ) { diff --git a/kernel/tty.h b/kernel/tty.h index 5dcb41ca..290859b2 100644 --- a/kernel/tty.h +++ b/kernel/tty.h @@ -20,6 +20,7 @@ #ifndef SORTIX_TTY_H #define SORTIX_TTY_H +#include #include #include @@ -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); diff --git a/kernel/vnode.cpp b/kernel/vnode.cpp index 94dd28a0..24042f2d 100644 --- a/kernel/vnode.cpp +++ b/kernel/vnode.cpp @@ -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) diff --git a/libc/Makefile b/libc/Makefile index db67020d..a645a8ab 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -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 \ diff --git a/libc/include/termios.h b/libc/include/termios.h index 07cece4c..b0f3c23e 100644 --- a/libc/include/termios.h +++ b/libc/include/termios.h @@ -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*); diff --git a/libc/termios/mkptyline.cpp b/libc/termios/mkptyline.cpp new file mode 100644 index 00000000..891bdbfe --- /dev/null +++ b/libc/termios/mkptyline.cpp @@ -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 + +#include + +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); +}