From db7182ddc3b99c7066a424bc095daca49911a169 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Tue, 15 Nov 2016 21:08:41 +0100 Subject: [PATCH] Add support for sessions. This change refactors the process group implementation and adds support for sessions. The setsid(2) and getsid(2) system calls were added. psctl(2) now has PSCTL_TTYNAME, which lets you get the name of a process's terminal, and ps(1) now uses it. The initial terminal is now called /dev/tty1. /dev/tty is now a factory for the current terminal. A global lock now protects the process hierarchy which makes it safe to access other processes. This refactor removes potential vulnerabilities and increases system robustness. A number of terminal ioctls have been added. This is a compatible ABI change. --- Makefile | 2 +- kernel/descriptor.cpp | 8 +- kernel/fs/user.cpp | 37 +-- kernel/include/sortix/ioctl.h | 6 + kernel/include/sortix/kernel/descriptor.h | 2 +- kernel/include/sortix/kernel/inode.h | 4 +- kernel/include/sortix/kernel/process.h | 40 ++- kernel/include/sortix/kernel/syscall.h | 2 + kernel/include/sortix/kernel/vnode.h | 2 +- kernel/include/sortix/limits.h | 1 + kernel/include/sortix/psctl.h | 9 +- kernel/include/sortix/syscall.h | 6 +- kernel/inode.cpp | 4 +- kernel/io.cpp | 19 +- kernel/kernel.cpp | 48 +++- kernel/logterminal.cpp | 57 +++- kernel/logterminal.h | 8 +- kernel/process.cpp | 312 +++++++++++++--------- kernel/psctl.cpp | 74 +++-- kernel/resource.cpp | 27 +- kernel/signal.cpp | 22 +- kernel/syscall.cpp | 2 + kernel/tty.cpp | 271 ++++++++++++++----- kernel/tty.h | 37 ++- kernel/vnode.cpp | 4 +- libc/Makefile | 2 + libc/include/unistd.h | 4 +- libc/unistd/getsid.c | 29 ++ libc/unistd/setsid.c | 29 ++ libc/unistd/ttyname.c | 34 +-- libc/unistd/ttyname_r.c | 20 +- utils/ps.c | 41 ++- 32 files changed, 817 insertions(+), 346 deletions(-) create mode 100644 libc/unistd/getsid.c create mode 100644 libc/unistd/setsid.c diff --git a/Makefile b/Makefile index e3bb4402..176b4d46 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,7 @@ sysroot-system: sysroot-fsh sysroot-base-headers echo 'ID=sortix' && \ echo 'VERSION_ID="$(VERSION)"' && \ echo 'PRETTY_NAME="Sortix $(VERSION)"' && \ - echo 'SORTIX_ABI=0.1' && \ + echo 'SORTIX_ABI=0.2' && \ true) > "$(SYSROOT)/etc/sortix-release" echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system" ln -sf sortix-release "$(SYSROOT)/etc/os-release" diff --git a/kernel/descriptor.cpp b/kernel/descriptor.cpp index ca1d3fb4..f1d91d07 100644 --- a/kernel/descriptor.cpp +++ b/kernel/descriptor.cpp @@ -694,9 +694,13 @@ 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); + int old_ctx_dflags = ctx->dflags; + ctx->dflags = ContextFlags(old_ctx_dflags, dflags); + int result = vnode->ioctl(ctx, cmd, arg); + ctx->dflags = old_ctx_dflags; + return result; } int Descriptor::tcsetpgrp(ioctx_t* ctx, pid_t pgid) diff --git a/kernel/fs/user.cpp b/kernel/fs/user.cpp index 0fa28fdd..b7b9254c 100644 --- a/kernel/fs/user.cpp +++ b/kernel/fs/user.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -227,7 +228,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); @@ -1227,23 +1228,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..0a7243cc 100644 --- a/kernel/include/sortix/ioctl.h +++ b/kernel/include/sortix/ioctl.h @@ -32,5 +32,11 @@ #define __IOCTL_TYPE(value) ((value) & __IOCTL_TYPE_MASK) #define TIOCGWINSZ __IOCTL(1, __IOCTL_TYPE_PTR) +#define TIOCSWINSZ __IOCTL(2, __IOCTL_TYPE_PTR) +#define TIOCSCTTY __IOCTL(3, __IOCTL_TYPE_INT) +#define TIOCSPTLCK __IOCTL(4, __IOCTL_TYPE_PTR) +#define TIOCGPTLCK __IOCTL(5, __IOCTL_TYPE_PTR) +#define TIOCGNAME __IOCTL(6, __IOCTL_TYPE_PTR) +#define TIOCGPTN __IOCTL(7, __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 40354012..8133a8df 100644 --- a/kernel/include/sortix/kernel/inode.h +++ b/kernel/include/sortix/kernel/inode.h @@ -86,7 +86,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; @@ -181,7 +181,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/process.h b/kernel/include/sortix/kernel/process.h index cee25a6a..2c9135ee 100644 --- a/kernel/include/sortix/kernel/process.h +++ b/kernel/include/sortix/kernel/process.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2013, 2014, 205, 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 @@ -77,6 +77,7 @@ public: private: kthread_mutex_t ptrlock; + Ref tty; Ref root; Ref cwd; Ref mtable; @@ -101,38 +102,36 @@ public: Ref GetDTable(); Ref GetMTable(); Ref GetPTable(); + Ref GetTTY(); Ref GetRoot(); Ref GetCWD(); Ref GetDescriptor(int fd); + void SetTTY(Ref tty); void SetRoot(Ref newroot); void SetCWD(Ref newcwd); public: -// A process may only access its parent if parentlock is locked. A process -// may only use its list of children if childlock is locked. A process may -// not access its sibling processes. Process* parent; Process* prevsibling; Process* nextsibling; Process* firstchild; Process* zombiechild; + Process* group; + Process* groupprev; + Process* groupnext; + Process* groupfirst; + Process* session; + Process* sessionprev; + Process* sessionnext; + Process* sessionfirst; kthread_mutex_t childlock; kthread_mutex_t parentlock; kthread_cond_t zombiecond; bool iszombie; bool nozombify; + bool limbo; int exit_code; -public: - Process* group; - Process* groupprev; - Process* groupnext; - Process* groupfirst; - kthread_mutex_t groupchildlock; - kthread_mutex_t groupparentlock; - kthread_cond_t groupchildleft; - bool grouplimbo; - public: Thread* firstthread; kthread_mutex_t threadlock; @@ -165,13 +164,14 @@ public: pid_t Wait(pid_t pid, int* status, int options); bool DeliverSignal(int signum); bool DeliverGroupSignal(int signum); + bool DeliverSessionSignal(int signum); void OnThreadDestruction(Thread* thread); - pid_t GetParentProcessId(); - void AddChildProcess(Process* child); void ScheduleDeath(); void AbortConstruction(); bool MapSegment(struct segment* result, void* hint, size_t size, int flags, int prot); + void GroupRemoveMember(Process* child); + void SessionRemoveMember(Process* child); public: Process* Fork(); @@ -180,19 +180,17 @@ private: void OnLastThreadExit(); void LastPrayer(); void WaitedFor(); - void NotifyMemberExit(Process* child); void NotifyChildExit(Process* child, bool zombify); - void NotifyNewZombies(); void DeleteTimers(); - -public: - void NotifyLeftProcessGroup(); + bool IsLimboDone(); public: void ResetForExecute(); }; +extern kthread_mutex_t process_family_lock; + Process* CurrentProcess(); } // namespace Sortix diff --git a/kernel/include/sortix/kernel/syscall.h b/kernel/include/sortix/kernel/syscall.h index ebe2696e..1d2dd4e7 100644 --- a/kernel/include/sortix/kernel/syscall.h +++ b/kernel/include/sortix/kernel/syscall.h @@ -100,6 +100,7 @@ pid_t sys_getpgid(pid_t); pid_t sys_getpid(void); pid_t sys_getppid(void); int sys_getpriority(int, id_t); +pid_t sys_getsid(pid_t); int sys_getsockname(int, struct sockaddr*, socklen_t*); int sys_getsockopt(int, int, int, void*, size_t*); int sys_gettermmode(int, unsigned*); @@ -146,6 +147,7 @@ int sys_setgid(gid_t); int sys_sethostname(const char*, size_t); int sys_setpgid(pid_t, pid_t); int sys_setpriority(int, id_t, int); +pid_t sys_setsid(void); int sys_setsockopt(int, int, int, const void*, size_t); int sys_settermmode(int, unsigned); int sys_setuid(uid_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/limits.h b/kernel/include/sortix/limits.h index 429923d3..556214e4 100644 --- a/kernel/include/sortix/limits.h +++ b/kernel/include/sortix/limits.h @@ -24,6 +24,7 @@ #if __USE_SORTIX || __USE_POSIX #define HOST_NAME_MAX 255 +#define TTY_NAME_MAX 32 #endif #endif diff --git a/kernel/include/sortix/psctl.h b/kernel/include/sortix/psctl.h index e0a17ad5..b900b9fb 100644 --- a/kernel/include/sortix/psctl.h +++ b/kernel/include/sortix/psctl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Jonas 'Sortie' Termansen. + * 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 @@ -97,4 +97,11 @@ struct psctl_program_path size_t size; }; +#define PSCTL_TTYNAME __PSCTL(psctl_program_path, 5) +struct psctl_ttyname +{ + char* buffer; + size_t size; +}; + #endif diff --git a/kernel/include/sortix/syscall.h b/kernel/include/sortix/syscall.h index 260151fe..d9cd98e9 100644 --- a/kernel/include/sortix/syscall.h +++ b/kernel/include/sortix/syscall.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2013, 2014, 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 @@ -183,6 +183,8 @@ #define SYSCALL_TCSENDBREAK 160 #define SYSCALL_TCSETATTR 161 #define SYSCALL_SCRAM 162 -#define SYSCALL_MAX_NUM 163 /* index of highest constant + 1 */ +#define SYSCALL_GETSID 163 +#define SYSCALL_SETSID 164 +#define SYSCALL_MAX_NUM 165 /* index of highest constant + 1 */ #endif diff --git a/kernel/inode.cpp b/kernel/inode.cpp index 414e7c8c..a1c9d2ec 100644 --- a/kernel/inode.cpp +++ b/kernel/inode.cpp @@ -298,10 +298,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 b0e300fe..da4f3930 100644 --- a/kernel/io.cpp +++ b/kernel/io.cpp @@ -371,13 +371,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) @@ -648,14 +646,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/kernel.cpp b/kernel/kernel.cpp index 72f5e839..6cb0d7c3 100644 --- a/kernel/kernel.cpp +++ b/kernel/kernel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2013, 2014, 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 @@ -18,6 +18,7 @@ * initial process from the init ramdisk, allowing a full operating system. */ +#include #include #include @@ -342,6 +343,10 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p) system->groupprev = NULL; system->groupnext = NULL; system->groupfirst = system; + system->session = system; + system->sessionprev = NULL; + system->sessionnext = NULL; + system->sessionfirst = system; if ( !(system->program_image_path = String::Clone("")) ) Panic("Unable to clone string for system process name"); @@ -480,12 +485,20 @@ static void BootThread(void* /*user*/) // Initialize the PS/2 controller. PS2::Init(keyboard, mouse); - // Register the kernel terminal as /dev/tty. - Ref tty(new LogTerminal(slashdev->dev, 0666, 0, 0, keyboard, kblayout)); + // Register /dev/tty as the current-terminal factory. + Ref tty(new DevTTY(slashdev->dev, 0666, 0, 0)); if ( !tty ) - Panic("Could not allocate a kernel terminal"); + Panic("Could not allocate a kernel terminal factory"); if ( LinkInodeInDir(&ctx, slashdev, "tty", tty) != 0 ) - Panic("Unable to link /dev/tty to kernel terminal."); + Panic("Unable to link /dev/tty to kernel terminal factory."); + + // Register the kernel terminal as /dev/tty1. + Ref tty1(new LogTerminal(slashdev->dev, 0666, 0, 0, + keyboard, kblayout, "tty1")); + if ( !tty1 ) + Panic("Could not allocate a kernel terminal"); + if ( LinkInodeInDir(&ctx, slashdev, "tty1", tty1) != 0 ) + Panic("Unable to link /dev/tty1 to kernel terminal."); // Register the mouse as /dev/mouse. Ref mousedev(new PS2MouseDevice(slashdev->dev, 0666, 0, 0, mouse)); @@ -562,12 +575,24 @@ static void BootThread(void* /*user*/) Panic("Could not allocate init process"); if ( (init->pid = (init->ptable = CurrentProcess()->ptable)->Allocate(init)) < 0 ) Panic("Could not allocate init a pid"); + + kthread_mutex_lock(&process_family_lock); + Process* kernel_process = CurrentProcess(); + init->parent = kernel_process; + init->nextsibling = kernel_process->firstchild; + init->prevsibling = NULL; + if ( kernel_process->firstchild ) + kernel_process->firstchild->prevsibling = init; + kernel_process->firstchild = init; init->group = init; init->groupprev = NULL; init->groupnext = NULL; init->groupfirst = init; - - CurrentProcess()->AddChildProcess(init); + init->session = init; + init->sessionprev = NULL; + init->sessionnext = NULL; + init->sessionfirst = init; + kthread_mutex_unlock(&process_family_lock); // TODO: Why don't we fork from pid=0 and this is done for us? // TODO: Fork dtable and mtable, don't share them! @@ -582,7 +607,7 @@ static void BootThread(void* /*user*/) if ( !initthread ) Panic("Could not create init thread"); - // Wait until init init is done and then shut down the computer. + // Wait until init is done and then shut down the computer. int status; pid_t pid = CurrentProcess()->Wait(init->pid, &status, 0); if ( pid != init->pid ) @@ -618,6 +643,13 @@ static void InitThread(void* /*user*/) Ref dtable = process->GetDTable(); + Ref tty1 = root->open(&ctx, "/dev/tty1", O_READ | O_WRITE); + if ( !tty1 ) + PanicF("/dev/tty1: %m"); + if ( tty1->ioctl(&ctx, TIOCSCTTY, 0) < 0 ) + PanicF("/dev/tty1: ioctl: TIOCSCTTY: %m"); + tty1.Reset(); + Ref tty_stdin = root->open(&ctx, "/dev/tty", O_READ); if ( !tty_stdin || dtable->Allocate(tty_stdin, 0) != 0 ) Panic("Could not prepare stdin for initialization process"); diff --git a/kernel/logterminal.cpp b/kernel/logterminal.cpp index b5b4b24f..227e8e35 100644 --- a/kernel/logterminal.cpp +++ b/kernel/logterminal.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -107,8 +108,9 @@ static inline const struct kbkey_sequence* LookupKeystrokeSequence(int kbkey) } LogTerminal::LogTerminal(dev_t dev, mode_t mode, uid_t owner, gid_t group, - Keyboard* keyboard, KeyboardLayoutExecutor* kblayout) - : TTY(dev, mode, owner, group) + Keyboard* keyboard, KeyboardLayoutExecutor* kblayout, + const char* name) + : TTY(dev, 0, mode, owner, group, name) { this->keyboard = keyboard; this->kblayout = kblayout; @@ -122,9 +124,16 @@ 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); + if ( hungup ) + return errno = EIO, -1; return Log::Sync() ? 0 : -1; } @@ -239,6 +248,9 @@ void LogTerminal::ProcessKeystroke(int kbkey) ssize_t LogTerminal::tcgetblob(ioctx_t* ctx, const char* name, void* buffer, size_t count) { + ScopedLockSignal lock(&termlock); + if ( hungup ) + return errno = EIO, -1; if ( !name ) { static const char index[] = "kblayout\0"; @@ -251,7 +263,6 @@ ssize_t LogTerminal::tcgetblob(ioctx_t* ctx, const char* name, void* buffer, siz } else if ( !strcmp(name, "kblayout") ) { - ScopedLockSignal lock(&termlock); const uint8_t* data; size_t size; if ( !kblayout->Download(&data, &size) ) @@ -268,6 +279,9 @@ ssize_t LogTerminal::tcgetblob(ioctx_t* ctx, const char* name, void* buffer, siz ssize_t LogTerminal::tcsetblob(ioctx_t* ctx, const char* name, const void* buffer, size_t count) { + ScopedLockSignal lock(&termlock); + if ( hungup ) + return errno = EIO, -1; if ( !name ) return errno = EPERM, -1; else if ( !strcmp(name, "kblayout") ) @@ -277,7 +291,6 @@ ssize_t LogTerminal::tcsetblob(ioctx_t* ctx, const char* name, const void* buffe return -1; if ( !ctx->copy_from_src(data, buffer, count) ) return -1; - ScopedLockSignal lock(&termlock); if ( !kblayout->Upload(data, count) ) return -1; delete[] data; @@ -287,4 +300,40 @@ 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); + if ( hungup ) + return errno = EIO, -1; + 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) +{ + ScopedLock lock(&termlock); + if ( hungup ) + return errno = EIO, -1; + if ( cmd == TIOCGWINSZ ) + { + struct winsize* ws = (struct winsize*) arg; + 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; + } + lock.Reset(); + return TTY::ioctl(ctx, cmd, arg); +} + } // namespace Sortix diff --git a/kernel/logterminal.h b/kernel/logterminal.h index 4327de68..3343afbd 100644 --- a/kernel/logterminal.h +++ b/kernel/logterminal.h @@ -28,17 +28,23 @@ class LogTerminal : public TTY, public KeyboardOwner { public: LogTerminal(dev_t dev, mode_t mode, uid_t owner, gid_t group, - Keyboard* keyboard, KeyboardLayoutExecutor* kblayout); + Keyboard* keyboard, KeyboardLayoutExecutor* kblayout, + const char* name); virtual ~LogTerminal(); 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/process.cpp b/kernel/process.cpp index dc7f074d..ced2f755 100644 --- a/kernel/process.cpp +++ b/kernel/process.cpp @@ -69,6 +69,8 @@ namespace Sortix { +kthread_mutex_t process_family_lock = KTHREAD_MUTEX_INITIALIZER; + Process::Process() { program_image_path = NULL; @@ -84,6 +86,7 @@ Process::Process() umask = 0022; ptrlock = KTHREAD_MUTEX_INITIALIZER; + // tty set to null reference in the member constructor. // root set to null reference in the member constructor. // cwd set to null reference in the member constructor. // mtable set to null reference in the member constructor. @@ -115,22 +118,19 @@ Process::Process() nextsibling = NULL; firstchild = NULL; zombiechild = NULL; - childlock = KTHREAD_MUTEX_INITIALIZER; - parentlock = KTHREAD_MUTEX_INITIALIZER; - zombiecond = KTHREAD_COND_INITIALIZER; - iszombie = false; - nozombify = false; - exit_code = -1; - group = NULL; groupprev = NULL; groupnext = NULL; groupfirst = NULL; - - groupparentlock = KTHREAD_MUTEX_INITIALIZER; - groupchildlock = KTHREAD_MUTEX_INITIALIZER; - groupchildleft = KTHREAD_COND_INITIALIZER; - grouplimbo = false; + session = NULL; + sessionprev = NULL; + sessionnext = NULL; + sessionfirst = NULL; + zombiecond = KTHREAD_COND_INITIALIZER; + iszombie = false; + nozombify = false; + limbo = false; + exit_code = -1; firstthread = NULL; threadlock = KTHREAD_MUTEX_INITIALIZER; @@ -153,12 +153,17 @@ Process::Process() alarm_timer.Attach(Time::GetClock(CLOCK_MONOTONIC)); } -Process::~Process() +Process::~Process() // process_family_lock taken { if ( alarm_timer.IsAttached() ) alarm_timer.Detach(); delete[] program_image_path; assert(!zombiechild); + assert(!session); + assert(!group); + assert(!parent); + assert(!sessionfirst); + assert(!groupfirst); assert(!firstchild); assert(!addrspace); assert(!segments); @@ -170,6 +175,7 @@ Process::~Process() assert(ptable); ptable->Free(pid); ptable.Reset(); + tty.Reset(); } void Process::BootstrapTables(Ref dtable, Ref mtable) @@ -280,6 +286,7 @@ void Process::LastPrayer() ResetAddressSpace(); + // tty is kept alive in session leader until no longer in limbo. if ( dtable ) dtable.Reset(); if ( cwd ) cwd.Reset(); if ( root ) root.Reset(); @@ -290,14 +297,15 @@ void Process::LastPrayer() Memory::DestroyAddressSpace(prevaddrspace); addrspace = 0; + ScopedLock family_lock(&process_family_lock); + + iszombie = true; + // Init is nice and will gladly raise our orphaned children and zombies. Process* init = Scheduler::GetInitProcess(); assert(init); - kthread_mutex_lock(&childlock); while ( firstchild ) { - ScopedLock firstchildlock(&firstchild->parentlock); - ScopedLock initlock(&init->childlock); Process* process = firstchild; firstchild = process->nextsibling; process->parent = init; @@ -322,39 +330,32 @@ void Process::LastPrayer() zombie->nozombify = true; zombie->WaitedFor(); } - kthread_mutex_unlock(&childlock); - - iszombie = true; + // Remove ourself from our process group. + if ( group ) + group->GroupRemoveMember(this); + // Remove ourself from our session. + if ( session ) + session->SessionRemoveMember(this); bool zombify = !nozombify; - // Remove ourself from our process group. - kthread_mutex_lock(&groupchildlock); - if ( group ) - group->NotifyMemberExit(this); - kthread_mutex_unlock(&groupchildlock); - // This class instance will be destroyed by our parent process when it // has received and acknowledged our death. - kthread_mutex_lock(&parentlock); if ( parent ) parent->NotifyChildExit(this, zombify); - kthread_mutex_unlock(&parentlock); // If nobody is waiting for us, then simply commit suicide. if ( !zombify ) WaitedFor(); } -void Process::WaitedFor() +void Process::WaitedFor() // process_family_lock taken { - kthread_mutex_lock(&parentlock); parent = NULL; - kthread_mutex_unlock(&parentlock); - kthread_mutex_lock(&groupparentlock); - bool in_limbo = groupfirst && (grouplimbo = true); - kthread_mutex_unlock(&groupparentlock); - if ( !in_limbo ) + limbo = false; + if ( groupfirst || sessionfirst ) + limbo = true; + if ( !limbo ) delete this; } @@ -375,37 +376,48 @@ void Process::ResetAddressSpace() segments = NULL; } -void Process::NotifyMemberExit(Process* child) +void Process::GroupRemoveMember(Process* child) // process_family_lock taken { assert(child->group == this); - kthread_mutex_lock(&groupparentlock); if ( child->groupprev ) child->groupprev->groupnext = child->groupnext; else groupfirst = child->groupnext; if ( child->groupnext ) child->groupnext->groupprev = child->groupprev; - kthread_cond_signal(&groupchildleft); - kthread_mutex_unlock(&groupparentlock); - child->group = NULL; - - NotifyLeftProcessGroup(); + if ( IsLimboDone() ) + delete this; } -void Process::NotifyLeftProcessGroup() +void Process::SessionRemoveMember(Process* child) // process_family_lock taken { - ScopedLock parentlock(&groupparentlock); - if ( !grouplimbo || groupfirst ) - return; - grouplimbo = false; - delete this; + assert(child->session == this); + if ( child->sessionprev ) + child->sessionprev->sessionnext = child->sessionnext; + else + sessionfirst = child->sessionnext; + if ( child->sessionnext ) + child->sessionnext->sessionprev = child->sessionprev; + child->session = NULL; + if ( !sessionfirst ) + { + // Remove reference to tty when session is empty. + ScopedLock lock(&ptrlock); + tty.Reset(); + } + if ( IsLimboDone() ) + delete this; } +bool Process::IsLimboDone() // process_family_lock taken +{ + return limbo && !groupfirst && !sessionfirst; +} + +// process_family_lock taken void Process::NotifyChildExit(Process* child, bool zombify) { - kthread_mutex_lock(&childlock); - if ( child->prevsibling ) child->prevsibling->nextsibling = child->nextsibling; if ( child->nextsibling ) @@ -424,17 +436,11 @@ void Process::NotifyChildExit(Process* child, bool zombify) zombiechild = child; } - kthread_mutex_unlock(&childlock); - if ( zombify ) - NotifyNewZombies(); -} - -void Process::NotifyNewZombies() -{ - ScopedLock lock(&childlock); - DeliverSignal(SIGCHLD); - kthread_cond_broadcast(&zombiecond); + { + DeliverSignal(SIGCHLD); + kthread_cond_broadcast(&zombiecond); + } } pid_t Process::Wait(pid_t thepid, int* status_ptr, int options) @@ -443,7 +449,7 @@ pid_t Process::Wait(pid_t thepid, int* status_ptr, int options) if ( thepid < -1 || thepid == 0 ) return errno = ENOSYS, -1; - ScopedLock lock(&childlock); + ScopedLock lock(&process_family_lock); // A process can only wait if it has children. if ( !firstchild && !zombiechild ) @@ -475,7 +481,7 @@ pid_t Process::Wait(pid_t thepid, int* status_ptr, int options) break; if ( options & WNOHANG ) return 0; - if ( !kthread_cond_wait_signal(&zombiecond, &childlock) ) + if ( !kthread_cond_wait_signal(&zombiecond, &process_family_lock) ) return errno = EINTR, -1; } @@ -536,21 +542,6 @@ void Process::ExitWithCode(int requested_exit_code) t->DeliverSignal(SIGKILL); } -void Process::AddChildProcess(Process* child) -{ - ScopedLock mylock(&childlock); - ScopedLock itslock(&child->parentlock); - assert(!child->parent); - assert(!child->nextsibling); - assert(!child->prevsibling); - child->parent = this; - child->nextsibling = firstchild; - child->prevsibling = NULL; - if ( firstchild ) - firstchild->prevsibling = child; - firstchild = child; -} - Ref Process::GetMTable() { ScopedLock lock(&ptrlock); @@ -572,6 +563,12 @@ Ref Process::GetPTable() return ptable; } +Ref Process::GetTTY() +{ + ScopedLock lock(&ptrlock); + return tty; +} + Ref Process::GetRoot() { ScopedLock lock(&ptrlock); @@ -586,6 +583,12 @@ Ref Process::GetCWD() return cwd; } +void Process::SetTTY(Ref newtty) +{ + ScopedLock lock(&ptrlock); + tty = newtty; +} + void Process::SetRoot(Ref newroot) { ScopedLock lock(&ptrlock); @@ -611,19 +614,10 @@ Process* Process::Fork() { assert(CurrentProcess() == this); - // TODO: This adds the new process to the process table, but it's not ready - // and functions that access this new process will be surprised that - // it's not fully constructed and really bad things will happen. Process* clone = new Process; if ( !clone ) return NULL; - if ( (clone->pid = (clone->ptable = ptable)->Allocate(clone)) < 0 ) - { - delete clone; - return NULL; - } - struct segment* clone_segments = NULL; // Fork the segment list. @@ -653,19 +647,38 @@ Process* Process::Fork() clone->segments_used = segments_used; clone->segments_length = segments_used; + kthread_mutex_lock(&process_family_lock); + + if ( (clone->pid = (clone->ptable = ptable)->Allocate(clone)) < 0 ) + { + kthread_mutex_unlock(&process_family_lock); + clone->AbortConstruction(); + return NULL; + } + // Remember the relation to the child process. - AddChildProcess(clone); + clone->parent = this; + clone->nextsibling = firstchild; + clone->prevsibling = NULL; + if ( firstchild ) + firstchild->prevsibling = clone; + firstchild = clone; // Add the new process to the current process group. - kthread_mutex_lock(&groupchildlock); - kthread_mutex_lock(&group->groupparentlock); clone->group = group; clone->groupprev = NULL; if ( (clone->groupnext = group->groupfirst) ) group->groupfirst->groupprev = clone; group->groupfirst = clone; - kthread_mutex_unlock(&group->groupparentlock); - kthread_mutex_unlock(&groupchildlock); + + // Add the new process to the current session. + clone->session = session; + clone->sessionprev = NULL; + if ( (clone->sessionnext = session->sessionfirst) ) + session->sessionfirst->sessionprev = clone; + session->sessionfirst = clone; + + kthread_mutex_unlock(&process_family_lock); // Initialize everything that is safe and can't fail. kthread_mutex_lock(&resource_limits_lock); @@ -1495,36 +1508,42 @@ pid_t sys_getpid(void) return CurrentProcess()->pid; } -pid_t Process::GetParentProcessId() -{ - ScopedLock lock(&parentlock); - if( !parent ) - return 0; - return parent->pid; -} - pid_t sys_getppid(void) { - return CurrentProcess()->GetParentProcessId(); + Process* process = CurrentProcess(); + ScopedLock lock(&process_family_lock); + if ( !process->parent ) + return 0; + return process->parent->pid; } pid_t sys_getpgid(pid_t pid) { + ScopedLock lock(&process_family_lock); Process* process = !pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid); if ( !process ) return errno = ESRCH, -1; - - // Prevent the process group from changing while we read it. - ScopedLock childlock(&process->groupchildlock); - assert(process->group); - + if ( !process->group ) + return errno = ESRCH, -1; return process->group->pid; } +pid_t sys_getsid(pid_t pid) +{ + ScopedLock lock(&process_family_lock); + Process* process = !pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid); + if ( !process ) + return errno = ESRCH, -1; + if ( !process->session ) + return errno = ESRCH, -1; + return process->session->pid; +} + int sys_setpgid(pid_t pid, pid_t pgid) { - // TODO: Prevent changing the process group of zombies and other volatile - // things that are about to implode. + if ( pid < 0 || pgid < 0 ) + return errno = EINVAL, -1; + // TODO: Either prevent changing the process group after an exec or provide // a version of this system call with a flags parameter that lets you // decide if you want this behavior. This will fix a race condition @@ -1534,6 +1553,10 @@ int sys_setpgid(pid_t pid, pid_t pgid) // process group, and then the shell gets around to change the process // group. This probably unlikely, but correctness over all! + Process* current_process = CurrentProcess(); + + ScopedLock lock(&process_family_lock); + // Find the processes in question. Process* process = !pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid); if ( !process ) @@ -1542,47 +1565,78 @@ int sys_setpgid(pid_t pid, pid_t pgid) if ( !group ) return errno = ESRCH, -1; - // Prevent the current group from being changed while we also change it - ScopedLock childlock(&process->groupchildlock); - assert(process->group); + // The process must be this one or a direct child. + if ( process != current_process && process->parent != current_process ) + return errno = EPERM, -1; + // The process must be in this session. + if ( process->session != current_process->session ) + return errno = EPERM, -1; + // The new group must be in the same session as the process. + if ( group->session != process->session ) + return errno = EPERM, -1; + // The process must not be a process group leader. + // TODO: Maybe POSIX actually allows this. + if ( process->groupfirst ) + return errno = EPERM, -1; + // The process must not be a session leader. + if ( process->sessionfirst ) + return errno = EPERM, -1; + // The group must either exist or be the process itself. + if ( !group->groupfirst && group != process ) + return errno = EPERM, -1; // Exit early if this is a noop. if ( process->group == group ) return 0; - // Prevent changing the process group of a process group leader. - if ( process->group == process ) - return errno = EPERM, -1; - // Remove the process from its current process group. - kthread_mutex_lock(&process->group->groupparentlock); - if ( process->groupprev ) - process->groupprev->groupnext = process->groupnext; - else - process->group->groupfirst = process->groupnext; - if ( process->groupnext ) - process->groupnext->groupprev = process->groupprev; - kthread_cond_signal(&process->group->groupchildleft); - kthread_mutex_unlock(&process->group->groupparentlock); - process->group->NotifyLeftProcessGroup(); - process->group = NULL; - - // TODO: Somehow prevent joining a zombie group, or worse yet, one that is - // currently being deleted by its parent! + if ( process->group ) + process->group->GroupRemoveMember(process); // Insert the process into its new process group. - kthread_mutex_lock(&group->groupparentlock); process->groupprev = NULL; process->groupnext = group->groupfirst; if ( group->groupfirst ) group->groupfirst->groupprev = process; group->groupfirst = process; process->group = group; - kthread_mutex_unlock(&group->groupparentlock); return 0; } +pid_t sys_setsid(void) +{ + Process* process = CurrentProcess(); + + ScopedLock lock(&process_family_lock); + + // Test if already a process group leader. + if ( process->group == process ) + return errno = EPERM, -1; + + // Remove the process from its current process group. + if ( process->group ) + process->group->GroupRemoveMember(process); + + // Remove the process from its current session. + if ( process->session ) + process->session->SessionRemoveMember(process); + + // Insert the process into its new session. + process->sessionprev = NULL; + process->sessionnext = NULL; + process->sessionfirst = process; + process->session = process; + + // Insert the process into its new process group. + process->groupprev = NULL; + process->groupnext = NULL; + process->groupfirst = process; + process->group = process; + + return process->pid; +} + size_t sys_getpagesize(void) { return Page::Size(); diff --git a/kernel/psctl.cpp b/kernel/psctl.cpp index ce51d7a7..f6ff0006 100644 --- a/kernel/psctl.cpp +++ b/kernel/psctl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Jonas 'Sortie' Termansen. + * 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 @@ -17,16 +17,23 @@ * Process control interface. */ +#include #include #include +#include #include +#if !defined(TTY_NAME_MAX) +#include +#endif #include #include +#include #include #include +#include #include #include #include @@ -37,6 +44,7 @@ namespace Sortix { int sys_psctl(pid_t pid, int request, void* ptr) { + ScopedLock lock(&process_family_lock); Ref ptable = CurrentProcess()->GetPTable(); if ( request == PSCTL_PREV_PID ) { @@ -52,7 +60,6 @@ int sys_psctl(pid_t pid, int request, void* ptr) resp.next_pid = ptable->Next(pid); return CopyToUser(ptr, &resp, sizeof(resp)) ? 0 : -1; } - // TODO: Scoped lock that prevents zombies from terminating. Process* process = ptable->Get(pid); if ( !process ) return errno = ESRCH, -1; @@ -61,55 +68,48 @@ int sys_psctl(pid_t pid, int request, void* ptr) struct psctl_stat psst; memset(&psst, 0, sizeof(psst)); psst.pid = pid; - kthread_mutex_lock(&process->parentlock); if ( process->parent ) { Process* parent = process->parent; psst.ppid = parent->pid; - kthread_mutex_unlock(&process->parentlock); - // TODO: Is there a risk of getting a new parent here? - kthread_mutex_lock(&parent->childlock); psst.ppid_prev = process->prevsibling ? process->prevsibling->pid : -1; psst.ppid_next = process->nextsibling ? process->nextsibling->pid : -1; - kthread_mutex_unlock(&parent->childlock); } else { - kthread_mutex_unlock(&process->parentlock); psst.ppid = -1; psst.ppid_prev = -1; psst.ppid_next = -1; } - kthread_mutex_lock(&process->childlock); psst.ppid_first = process->firstchild ? process->firstchild->pid : -1; - kthread_mutex_unlock(&process->childlock); - kthread_mutex_lock(&process->groupparentlock); if ( process->group ) { Process* group = process->group; psst.pgid = group->pid; - kthread_mutex_unlock(&process->groupparentlock); - // TODO: Is there a risk of getting a new group here? - kthread_mutex_lock(&group->groupchildlock); psst.pgid_prev = process->groupprev ? process->groupprev->pid : -1; psst.pgid_next = process->groupnext ? process->groupnext->pid : -1; - kthread_mutex_unlock(&group->groupchildlock); } else { - kthread_mutex_unlock(&process->groupparentlock); psst.pgid = -1; psst.pgid_prev = -1; psst.pgid_next = -1; } - kthread_mutex_lock(&process->groupchildlock); psst.pgid_first = process->groupfirst ? process->groupfirst->pid : -1; - kthread_mutex_unlock(&process->groupchildlock); - // TODO: Implement sessions. - psst.sid = 1; - psst.sid_prev = ptable->Prev(pid); - psst.sid_next = ptable->Next(pid); - psst.sid_first = pid == 1 ? 1 : -1; + if ( process->session ) + { + Process* session = process->session; + psst.sid = session->pid; + psst.sid_prev = process->sessionprev ? process->sessionprev->pid : -1; + psst.sid_next = process->sessionnext ? process->sessionnext->pid : -1; + } + else + { + psst.sid = -1; + psst.sid_prev = -1; + psst.sid_next = -1; + } + psst.sid_first = process->sessionfirst ? process->sessionfirst->pid : -1; // TODO: Implement init groupings. psst.init = 1; psst.init_prev = ptable->Prev(pid); @@ -155,6 +155,34 @@ int sys_psctl(pid_t pid, int request, void* ptr) } return 0; } + else if ( request == PSCTL_TTYNAME ) + { + struct psctl_ttyname ctl; + if ( !CopyFromUser(&ctl, ptr, sizeof(ctl)) ) + return -1; + ioctx_t kctx; SetupKernelIOCtx(&kctx); + if ( !process->session ) + return errno = ENOTTY, -1; + Ref tty = process->session->GetTTY(); + if ( !tty ) + return errno = ENOTTY, -1; + char ttyname[TTY_NAME_MAX-5+1]; + if ( tty->ioctl(&kctx, TIOCGNAME, (uintptr_t) ttyname) < 0 ) + return -1; + size_t size = strlen(ttyname) + 1; + struct psctl_ttyname resp = ctl; + resp.size = size; + if ( !CopyToUser(ptr, &resp, sizeof(resp)) ) + return -1; + if ( ctl.buffer ) + { + if ( ctl.size < size ) + return errno = ERANGE, -1; + if ( !CopyToUser(ctl.buffer, ttyname, size) ) + return -1; + } + return 0; + } return errno = EINVAL, -1; } diff --git a/kernel/resource.cpp b/kernel/resource.cpp index 8828bf3f..2e104366 100644 --- a/kernel/resource.cpp +++ b/kernel/resource.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2014, 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 @@ -36,8 +36,6 @@ static int GetProcessPriority(pid_t who) { if ( who < 0 ) return errno = EINVAL, -1; - // TODO: If who isn't the current process, then it could self-destruct at - // any time while we use it; there is no safe way to do this yet. Process* process = who ? CurrentProcess()->GetPTable()->Get(who) : CurrentProcess(); if ( !process ) return errno = ESRCH, -1; @@ -49,8 +47,6 @@ static int SetProcessPriority(pid_t who, int prio) { if ( who < 0 ) return errno = EINVAL, -1; - // TODO: If who isn't the current process, then it could self-destruct at - // any time while we use it; there is no safe way to do this yet. Process* process = who ? CurrentProcess()->GetPTable()->Get(who) : CurrentProcess(); if ( !process ) return errno = ESRCH, -1; @@ -61,24 +57,17 @@ static int SetProcessPriority(pid_t who, int prio) static Process* CurrentProcessGroup() { - Process* process = CurrentProcess(); - ScopedLock lock(&process->groupchildlock); - // TODO: The process group can change when this call returns, additionally - // the current process leader could self-destruct. - return process->group; + return CurrentProcess()->group; } static int GetProcessGroupPriority(pid_t who) { if ( who < 0 ) return errno = EINVAL, -1; - // TODO: If who isn't the current process, then it could self-destruct at - // any time while we use it; there is no safe way to do this yet. Process* group = who ? CurrentProcess()->GetPTable()->Get(who) : CurrentProcessGroup(); if ( !group ) return errno = ESRCH, -1; int lowest = INT_MAX; - ScopedLock group_parent_lock(&group->groupparentlock); for ( Process* process = group->groupfirst; process; process = process->groupnext ) { ScopedLock lock(&process->nicelock); @@ -92,12 +81,9 @@ static int SetProcessGroupPriority(pid_t who, int prio) { if ( who < 0 ) return errno = EINVAL, -1; - // TODO: If who isn't the current process, then it could self-destruct at - // any time while we use it; there is no safe way to do this yet. Process* group = who ? CurrentProcess()->GetPTable()->Get(who) : CurrentProcessGroup(); if ( !group ) return errno = ESRCH, -1; - ScopedLock group_parent_lock(&group->groupparentlock); for ( Process* process = group->groupfirst; process; process = process->groupnext ) { ScopedLock lock(&process->nicelock); @@ -108,20 +94,17 @@ static int SetProcessGroupPriority(pid_t who, int prio) static int GetUserPriority(uid_t /*who*/) { - // TODO: There is currently no easy way to iterate all processes without - // dire race conditions being possible. return errno = ENOSYS, -1; } static int SetUserPriority(uid_t /*who*/, int /*prio*/) { - // TODO: There is currently no easy way to iterate all processes without - // dire race conditions being possible. return errno = ENOSYS, -1; } int sys_getpriority(int which, id_t who) { + ScopedLock lock(&process_family_lock); switch ( which ) { case PRIO_PROCESS: return GetProcessPriority(who); @@ -133,6 +116,7 @@ int sys_getpriority(int which, id_t who) int sys_setpriority(int which, id_t who, int prio) { + ScopedLock lock(&process_family_lock); switch ( which ) { case PRIO_PROCESS: return SetProcessPriority(who, prio); @@ -151,8 +135,7 @@ int sys_prlimit(pid_t pid, return errno = EINVAL, -1; if ( resource < 0 || RLIMIT_NUM_DECLARED <= resource ) return errno = EINVAL, -1; - // TODO: If pid isn't the current process, then it could self-destruct at - // any time while we use it; there is no safe way to do this yet. + ScopedLock family_lock(&process_family_lock); Process* process = pid ? CurrentProcess()->GetPTable()->Get(pid) : CurrentProcess(); if ( !process ) return errno = ESRCH, -1; diff --git a/kernel/signal.cpp b/kernel/signal.cpp index 43f8d0e9..cbc0341c 100644 --- a/kernel/signal.cpp +++ b/kernel/signal.cpp @@ -279,7 +279,8 @@ int sys_kill(pid_t pid, int signum) // TODO: Implement that pid == -1 means all processes! bool process_group = pid < 0 ? (pid = -pid, true) : false; - // TODO: Race condition: The process could be deleted while we use it. + ScopedLock lock(&process_family_lock); + Process* process = CurrentProcess()->GetPTable()->Get(pid); if ( !process ) return errno = ESRCH, -1; @@ -300,9 +301,8 @@ int sys_kill(pid_t pid, int signum) return errno = 0, 0; } -bool Process::DeliverGroupSignal(int signum) +bool Process::DeliverGroupSignal(int signum) // process_family_lock held { - ScopedLock lock(&groupparentlock); if ( !groupfirst ) return errno = ESRCH, false; for ( Process* iter = groupfirst; iter; iter = iter->groupnext ) @@ -317,6 +317,22 @@ bool Process::DeliverGroupSignal(int signum) return true; } +bool Process::DeliverSessionSignal(int signum) // process_family_lock held +{ + if ( !sessionfirst ) + return errno = ESRCH, false; + for ( Process* iter = sessionfirst; iter; iter = iter->sessionnext ) + { + int saved_errno = errno; + if ( !iter->DeliverSignal(signum) && errno != ESIGPENDING ) + { + // This is not currently an error condition. + } + errno = saved_errno; + } + return true; +} + bool Process::DeliverSignal(int signum) { ScopedLock lock(&threadlock); diff --git a/kernel/syscall.cpp b/kernel/syscall.cpp index 4d5abdb7..58a0fa76 100644 --- a/kernel/syscall.cpp +++ b/kernel/syscall.cpp @@ -197,6 +197,8 @@ void* syscall_list[SYSCALL_MAX_NUM + 1] = [SYSCALL_TCSENDBREAK] = (void*) sys_tcsendbreak, [SYSCALL_TCSETATTR] = (void*) sys_tcsetattr, [SYSCALL_SCRAM] = (void*) sys_scram, + [SYSCALL_GETSID] = (void*) sys_getsid, + [SYSCALL_SETSID] = (void*) sys_setsid, [SYSCALL_MAX_NUM] = (void*) sys_bad_syscall, }; } /* extern "C" */ diff --git a/kernel/tty.cpp b/kernel/tty.cpp index acb78cdc..d77b0cb6 100644 --- a/kernel/tty.cpp +++ b/kernel/tty.cpp @@ -17,9 +17,11 @@ * Terminal line discipline. */ +#include #include #include +#include #include #include #include @@ -28,6 +30,9 @@ #include #include +#if !defined(TTY_NAME_MAX) +#include +#endif #include #include #include @@ -35,6 +40,7 @@ #include #include +#include #include #include #include @@ -47,6 +53,7 @@ #include #include #include +#include #include "tty.h" @@ -79,13 +86,50 @@ static inline bool IsUTF8Continuation(unsigned char byte) return (byte & 0b11000000) == 0b10000000; } -TTY::TTY(dev_t dev, mode_t mode, uid_t owner, gid_t group) +DevTTY::DevTTY(dev_t dev, mode_t mode, uid_t owner, gid_t group) { if ( !dev ) dev = (dev_t) this; inode_type = INODE_TYPE_TTY; this->dev = dev; this->ino = (ino_t) this; + this->type = S_IFFACTORY; + this->stat_mode = (mode & S_SETABLE) | this->type; + this->stat_uid = owner; + this->stat_gid = group; +} + +DevTTY::~DevTTY() +{ +} + +Ref DevTTY::factory(ioctx_t* ctx, const char* filename, int flags, + mode_t mode) +{ + (void) ctx; + (void) filename; + (void) flags; + (void) mode; + ScopedLock lock(&process_family_lock); + Process* process = CurrentProcess(); + if ( !process->session ) + return errno = ENOTTY, Ref(NULL); + Ref tty_desc = process->session->GetTTY(); + if ( !tty_desc ) + return errno = ENOTTY, Ref(NULL); + return tty_desc->vnode->inode; +} + +TTY::TTY(dev_t dev, ino_t ino, mode_t mode, uid_t owner, gid_t group, + const char* name) +{ + if ( !dev ) + dev = (dev_t) this; + if ( !ino ) + ino = (ino_t) this; + inode_type = INODE_TYPE_TTY; + this->dev = dev; + this->ino = ino; this->type = S_IFCHR; this->stat_mode = (mode & S_SETABLE) | this->type; this->stat_uid = owner; @@ -109,10 +153,13 @@ TTY::TTY(dev_t dev, mode_t mode, uid_t owner, gid_t group) tio.c_cc[VWERASE] = CONTROL('W'); tio.c_ispeed = B38400; tio.c_ospeed = B38400; - this->termlock = KTHREAD_MUTEX_INITIALIZER; - this->datacond = KTHREAD_COND_INITIALIZER; - this->numeofs = 0; - this->foreground_pgid = 0; + termlock = KTHREAD_MUTEX_INITIALIZER; + datacond = KTHREAD_COND_INITIALIZER; + numeofs = 0; + foreground_pgid = -1; + sid = -1; + hungup = false; + snprintf(ttyname, sizeof(ttyname), "%s", name); } TTY::~TTY() @@ -122,8 +169,10 @@ TTY::~TTY() int TTY::settermmode(ioctx_t* /*ctx*/, unsigned int termmode) { ScopedLock lock(&termlock); + if ( hungup ) + return errno = EIO, -1; if ( !RequireForeground(SIGTTOU) ) - return errno = EINTR, -1; + return -1; if ( termmode & ~SUPPORTED_TERMMODES ) return errno = EINVAL, -1; tcflag_t old_cflag = tio.c_cflag; @@ -195,6 +244,8 @@ int TTY::settermmode(ioctx_t* /*ctx*/, unsigned int termmode) int TTY::gettermmode(ioctx_t* ctx, unsigned int* mode) { ScopedLock lock(&termlock); + if ( hungup ) + return errno = EIO, -1; unsigned int termmode = 0; if ( tio.c_lflag & ISORTIX_KBKEY ) termmode |= TERMMODE_KBKEY; @@ -225,46 +276,25 @@ 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) { ScopedLock lock(&termlock); - if ( !RequireForeground(SIGTTOU) ) - return errno = EINTR, -1; + if ( hungup ) + return errno = EIO, -1; + ScopedLock family_lock(&process_family_lock); + if ( !RequireForegroundUnlocked(SIGTTOU) ) + return -1; if ( pgid <= 0 ) return errno = ESRCH, -1; Process* process = CurrentProcess()->GetPTable()->Get(pgid); if ( !process ) return errno = ESRCH, -1; - kthread_mutex_lock(&process->groupparentlock); - bool is_process_group = process->group == process; - kthread_mutex_unlock(&process->groupparentlock); - if ( !is_process_group ) + if ( !process->groupfirst ) return errno = EINVAL, -1; foreground_pgid = pgid; return 0; @@ -273,9 +303,26 @@ int TTY::tcsetpgrp(ioctx_t* /*ctx*/, pid_t pgid) pid_t TTY::tcgetpgrp(ioctx_t* /*ctx*/) { ScopedLock lock(&termlock); + if ( hungup ) + return errno = EIO, -1; return foreground_pgid; } +void TTY::hup() +{ + ScopedLock lock(&termlock); + ScopedLock family_lock(&process_family_lock); + hungup = true; + if ( 0 < sid ) + { + Process* process = CurrentProcess()->GetPTable()->Get(sid); + if ( process ) + process->DeliverSessionSignal(SIGHUP); + } + kthread_cond_broadcast(&datacond); + poll_channel.Signal(POLLHUP); +} + void TTY::ProcessUnicode(uint32_t unicode) { mbstate_t ps; @@ -311,6 +358,7 @@ void TTY::ProcessByte(unsigned char byte, uint32_t control_unicode) { while ( linebuffer.CanBackspace() ) linebuffer.Backspace(); + ScopedLock lock(&process_family_lock); if ( Process* process = CurrentProcess()->GetPTable()->Get(foreground_pgid) ) process->DeliverGroupSignal(SIGQUIT); return; @@ -320,6 +368,7 @@ void TTY::ProcessByte(unsigned char byte, uint32_t control_unicode) { while ( linebuffer.CanBackspace() ) linebuffer.Backspace(); + ScopedLock lock(&process_family_lock); if ( Process* process = CurrentProcess()->GetPTable()->Get(foreground_pgid) ) process->DeliverGroupSignal(SIGINT); return; @@ -340,6 +389,7 @@ void TTY::ProcessByte(unsigned char byte, uint32_t control_unicode) { while ( linebuffer.CanBackspace() ) linebuffer.Backspace(); + ScopedLock lock(&process_family_lock); if ( Process* process = CurrentProcess()->GetPTable()->Get(foreground_pgid) ) process->DeliverGroupSignal(SIGQUIT); return; @@ -359,9 +409,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; } @@ -388,9 +438,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; @@ -407,9 +457,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; @@ -422,7 +472,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; } @@ -445,16 +495,19 @@ void TTY::ProcessByte(unsigned char byte, uint32_t control_unicode) if ( byte == '\n' ) { if ( tio.c_oflag & OPOST && tio.c_oflag & ONLCR ) - Log::PrintData("\r\n", 2); + tty_output("\r\n"); else if ( tio.c_oflag & OPOST && tio.c_oflag & OCRNL ) - Log::PrintData("\r", 1); + tty_output("\r"); else - Log::PrintData("\n", 1); + tty_output("\n"); } else 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' ) @@ -476,8 +529,10 @@ ssize_t TTY::read(ioctx_t* ctx, uint8_t* userbuf, size_t count) ScopedLockSignal lock(&termlock); if ( !lock.IsAcquired() ) return errno = EINTR, -1; + if ( hungup ) + return errno = EIO, -1; if ( !RequireForeground(SIGTTIN) ) - return errno = EINTR, -1; + return -1; size_t sofar = 0; size_t left = count; bool nonblocking = tio.c_lflag & ISORTIX_NONBLOCK || @@ -494,6 +549,8 @@ ssize_t TTY::read(ioctx_t* ctx, uint8_t* userbuf, size_t count) return errno = EWOULDBLOCK, -1; if ( !kthread_cond_wait_signal(&datacond, &termlock) ) return sofar ? sofar : (errno = EINTR, -1); + if ( hungup ) + return sofar ? sofar : (errno = EIO, -1); } else { @@ -511,6 +568,8 @@ ssize_t TTY::read(ioctx_t* ctx, uint8_t* userbuf, size_t count) } if ( !kthread_cond_wait_signal(&datacond, &termlock) ) return sofar ? sofar : (errno = EINTR, -1); + if ( hungup ) + return sofar ? sofar : (errno = EIO, -1); } else if ( tio.c_cc[VMIN] == 0 && 0 < tio.c_cc[VTIME] ) { @@ -522,6 +581,8 @@ ssize_t TTY::read(ioctx_t* ctx, uint8_t* userbuf, size_t count) // TODO: Only wait up until tio.c_cc[VTIME] * 0.1 seconds. if ( !kthread_cond_wait_signal(&datacond, &termlock) ) return sofar ? sofar : (errno = EINTR, -1); + if ( hungup ) + return sofar ? sofar : (errno = EIO, -1); } else return sofar; @@ -586,8 +647,10 @@ ssize_t TTY::read(ioctx_t* ctx, uint8_t* userbuf, size_t count) ssize_t TTY::write(ioctx_t* ctx, const uint8_t* io_buffer, size_t count) { ScopedLockSignal lock(&termlock); + if ( hungup ) + return errno = EIO, -1; if ( tio.c_lflag & TOSTOP && !RequireForeground(SIGTTOU) ) - return errno = EINTR, -1; + return -1; // TODO: Add support for ioctx to the kernel log. unsigned char buffer[256]; size_t max_incoming = sizeof(buffer); @@ -632,13 +695,15 @@ ssize_t TTY::write(ioctx_t* ctx, const uint8_t* io_buffer, size_t count) buffer[i] = '\n'; } } - Log::PrintData(buffer + offset, outgoing); + tty_output(buffer + offset, outgoing); sofar += incoming; if ( ++rounds % 16 == 0 ) { kthread_mutex_unlock(&termlock); kthread_yield(); kthread_mutex_lock(&termlock); + if ( hungup ) + return sofar; } if ( Signal::IsPending() ) return sofar; @@ -649,6 +714,8 @@ ssize_t TTY::write(ioctx_t* ctx, const uint8_t* io_buffer, size_t count) short TTY::PollEventStatus() { short status = 0; + if ( hungup ) + status |= POLLHUP; if ( linebuffer.CanPop() || numeofs ) status |= POLLIN | POLLRDNORM; if ( true /* can always write */ ) @@ -672,8 +739,10 @@ int TTY::poll(ioctx_t* /*ctx*/, PollNode* node) int TTY::tcdrain(ioctx_t* /*ctx*/) { ScopedLockSignal lock(&termlock); + if ( hungup ) + return errno = EIO, -1; if ( !RequireForeground(SIGTTOU) ) - return errno = EINTR, -1; + return -1; return 0; } @@ -681,7 +750,7 @@ int TTY::tcflow(ioctx_t* /*ctx*/, int action) { ScopedLockSignal lock(&termlock); if ( !RequireForeground(SIGTTOU) ) - return errno = EINTR, -1; + return -1; switch ( action ) { case TCOOFF: break; // TODO: Suspend output. @@ -698,8 +767,10 @@ int TTY::tcflush(ioctx_t* /*ctx*/, int queue_selector) if ( queue_selector & ~TCIOFLUSH ) return errno = EINVAL, -1; ScopedLockSignal lock(&termlock); + if ( hungup ) + return errno = EIO, -1; if ( !RequireForeground(SIGTTOU) ) - return errno = EINTR, -1; + return -1; if ( queue_selector & TCIFLUSH ) linebuffer.Flush(); return 0; @@ -708,6 +779,8 @@ int TTY::tcflush(ioctx_t* /*ctx*/, int queue_selector) int TTY::tcgetattr(ioctx_t* ctx, struct termios* io_tio) { ScopedLockSignal lock(&termlock); + if ( hungup ) + return errno = EIO, -1; if ( !ctx->copy_to_dest(io_tio, &tio, sizeof(tio)) ) return -1; return 0; @@ -715,23 +788,29 @@ int TTY::tcgetattr(ioctx_t* ctx, struct termios* io_tio) pid_t TTY::tcgetsid(ioctx_t* /*ctx*/) { - // TODO: Implement sessions. - return 1; + ScopedLockSignal lock(&termlock); + if ( hungup ) + return errno = EIO, -1; + return sid; } int TTY::tcsendbreak(ioctx_t* /*ctx*/, int /*duration*/) { ScopedLockSignal lock(&termlock); + if ( hungup ) + return errno = EIO, -1; if ( !RequireForeground(SIGTTOU) ) - return errno = EINTR, -1; + return -1; return 0; } int TTY::tcsetattr(ioctx_t* ctx, int actions, const struct termios* io_tio) { ScopedLockSignal lock(&termlock); + if ( hungup ) + return errno = EIO, -1; if ( !RequireForeground(SIGTTOU) ) - return errno = EINTR, -1; + return -1; switch ( actions ) { case TCSANOW: break; @@ -739,18 +818,88 @@ int TTY::tcsetattr(ioctx_t* ctx, int actions, const struct termios* io_tio) case TCSAFLUSH: linebuffer.Flush(); break; default: return errno = EINVAL, -1; } + tcflag_t old_lflag = tio.c_lflag; if ( !ctx->copy_from_src(&tio, io_tio, sizeof(tio)) ) return -1; - // TODO: Potentially take action here if something changed. + tcflag_t new_lflag = tio.c_lflag; + bool oldnoutf8 = old_lflag & ISORTIX_32BIT; + bool newnoutf8 = new_lflag & ISORTIX_32BIT; + if ( oldnoutf8 != newnoutf8 ) + memset(&read_ps, 0, sizeof(read_ps)); + if ( !(tio.c_lflag & ICANON) ) + CommitLineBuffer(); return 0; } +int TTY::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg) +{ + ScopedLockSignal lock(&termlock); + if ( hungup ) + return errno = EIO, -1; + if ( cmd == TIOCSCTTY ) + { + ScopedLock family_lock(&process_family_lock); + if ( 0 <= sid ) + return errno = EPERM, -1; + Process* process = CurrentProcess(); + if ( !process->sessionfirst ) + return errno = EPERM, -1; + if ( (ctx->dflags & (O_READ | O_WRITE)) != (O_READ | O_WRITE) ) + return errno = EPERM, -1; + Ref vnode(new Vnode(Ref(this), Ref(NULL), 0, 0)); + if ( !vnode ) + return -1; + Ref desc(new Descriptor(vnode, O_READ | O_WRITE)); + if ( !desc ) + return -1; + sid = process->pid; + foreground_pgid = process->pid; + process->SetTTY(desc); + return 0; + } + else if ( cmd == TIOCSPTLCK ) + { + // TODO: Figure out what locked ptys are and implement it if sensible. + const int* arg_ptr = (const int*) arg; + int new_locked; + if ( !ctx->copy_from_src(&new_locked, arg_ptr, sizeof(new_locked)) ) + return -1; + return 0; + } + else if ( cmd == TIOCGPTLCK ) + { + // TODO: Figure out what locked ptys are and implement it if sensible. + int* arg_ptr = (int*) arg; + int locked = 0; + if ( !ctx->copy_to_dest(arg_ptr, &locked, sizeof(locked)) ) + return -1; + return 0; + } + else if ( cmd == TIOCGNAME ) + { + char* arg_ptr = (char*) arg; + size_t ttynamesize = strlen(ttyname) + 1; + if ( !ctx->copy_to_dest(arg_ptr, ttyname, ttynamesize) ) + return -1; + return 0; + } + lock.Reset(); + return AbstractInode::ioctl(ctx, cmd, arg); +} + bool TTY::RequireForeground(int sig) +{ + ScopedLock family_lock(&process_family_lock); + return RequireForegroundUnlocked(sig); +} + +bool TTY::RequireForegroundUnlocked(int sig) // process_family_lock held { Thread* thread = CurrentThread(); Process* process = thread->process; - ScopedLock group_lock(&process->groupparentlock); - if ( process->group->pid == foreground_pgid ) + if ( !process->session || process->session->pid != sid || !process->group ) + return true; + if ( foreground_pgid < 1 || process->group->pid == foreground_pgid ) return true; if ( sigismember(&thread->signal_mask, sig) ) return true; @@ -758,8 +907,8 @@ bool TTY::RequireForeground(int sig) if ( process->signal_actions[sig].sa_handler == SIG_IGN ) return true; signal_lock.Reset(); - group_lock.Reset(); process->group->DeliverGroupSignal(sig); + errno = EINTR; return false; } diff --git a/kernel/tty.h b/kernel/tty.h index 5dcb41ca..930a8566 100644 --- a/kernel/tty.h +++ b/kernel/tty.h @@ -20,8 +20,12 @@ #ifndef SORTIX_TTY_H #define SORTIX_TTY_H +#include #include +#if !defined(TTY_NAME_MAX) +#include +#endif #include #include @@ -35,17 +39,29 @@ namespace Sortix { +class DevTTY : public AbstractInode +{ +public: + DevTTY(dev_t dev, mode_t mode, uid_t owner, gid_t group); + virtual ~DevTTY(); + +public: + virtual Ref factory(ioctx_t* ctx, const char* filename, int flags, + mode_t mode); + +}; + class TTY : public AbstractInode { public: - TTY(dev_t dev, mode_t mode, uid_t owner, gid_t group); + TTY(dev_t dev, ino_t ino, mode_t mode, uid_t owner, gid_t group, + const char* name); virtual ~TTY(); 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); @@ -58,6 +74,17 @@ public: virtual pid_t tcgetsid(ioctx_t* ctx); virtual int tcsendbreak(ioctx_t* ctx, int duration); virtual int tcsetattr(ioctx_t* ctx, int actions, const struct termios* tio); + virtual int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg); + +public: + void hup(); + +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); @@ -66,17 +93,21 @@ protected: short PollEventStatus(); bool CheckForeground(); bool RequireForeground(int sig); + bool RequireForegroundUnlocked(int sig); bool CheckHandledByte(tcflag_t lflags, unsigned char key, unsigned char byte); protected: PollChannel poll_channel; - mutable kthread_mutex_t termlock; + kthread_mutex_t termlock; kthread_cond_t datacond; mbstate_t read_ps; size_t numeofs; LineBuffer linebuffer; struct termios tio; pid_t foreground_pgid; + pid_t sid; + bool hungup; + char ttyname[TTY_NAME_MAX-5+1]; }; diff --git a/kernel/vnode.cpp b/kernel/vnode.cpp index 17b27ce4..25de8937 100644 --- a/kernel/vnode.cpp +++ b/kernel/vnode.cpp @@ -339,9 +339,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 0edd038c..4cf90406 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -688,6 +688,7 @@ unistd/getpagesize.o \ unistd/getpgid.o \ unistd/getpid.o \ unistd/getppid.o \ +unistd/getsid.o \ unistd/getuid.o \ unistd/isatty.o \ unistd/lchown.o \ @@ -710,6 +711,7 @@ unistd/seteuid.o \ unistd/setgid.o \ unistd/sethostname.o \ unistd/setpgid.o \ +unistd/setsid.o \ unistd/setuid.o \ unistd/sfork.o \ unistd/sleep.o \ diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 44352326..b73742b4 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -440,7 +440,7 @@ ssize_t read(int, void*, size_t); int rmdir(const char*); int setgid(gid_t); int setpgid(pid_t, pid_t); -/* TODO: pid_t setsid(void); */ +pid_t setsid(void); int setuid(uid_t); unsigned sleep(unsigned); long sysconf(int); @@ -494,7 +494,7 @@ int symlink(const char*, const char*); #if __USE_SORTIX || 200809L <= __USE_POSIX || 420 <= __USE_XOPEN int fchdir(int); int fchown(int, uid_t, gid_t); -/* TODO: pid_t getsid(void); */ +pid_t getsid(pid_t); int lchown(const char*, uid_t, gid_t); int truncate(const char*, off_t); #endif diff --git a/libc/unistd/getsid.c b/libc/unistd/getsid.c new file mode 100644 index 00000000..2e473195 --- /dev/null +++ b/libc/unistd/getsid.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 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. + * + * unistd/getsid.c + * Get the current session. + */ + +#include + +#include + +DEFN_SYSCALL1(pid_t, sys_getsid, SYSCALL_GETSID, pid_t); + +pid_t getsid(pid_t pid) +{ + return sys_getsid(pid); +} diff --git a/libc/unistd/setsid.c b/libc/unistd/setsid.c new file mode 100644 index 00000000..21bfc83f --- /dev/null +++ b/libc/unistd/setsid.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 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. + * + * unistd/setsid.c + * Enter a new session. + */ + +#include + +#include + +DEFN_SYSCALL0(pid_t, sys_setsid, SYSCALL_SETSID); + +pid_t setsid(void) +{ + return sys_setsid(); +} diff --git a/libc/unistd/ttyname.c b/libc/unistd/ttyname.c index d94640f4..e5006e06 100644 --- a/libc/unistd/ttyname.c +++ b/libc/unistd/ttyname.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2014, 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 @@ -17,24 +17,24 @@ * Returns the pathname of a terminal. */ -#include -#include +#include + +#include #include +#if !defined(TTY_NAME_MAX) +#include +#endif + char* ttyname(int fd) { - static char* result = NULL; - static size_t result_size = 0; - while ( ttyname_r(fd, result, result_size) < 0 ) - { - if ( errno != ERANGE ) - return NULL; - size_t new_result_size = result_size ? 2 * result_size : 16; - char* new_result = (char*) realloc(result, new_result_size); - if ( !new_result ) - return NULL; - result = new_result; - result_size = new_result_size; - } - return result; + static char name[TTY_NAME_MAX+1]; + name[0] = '/'; + name[1] = 'd'; + name[2] = 'e'; + name[3] = 'v'; + name[4] = '/'; + if ( ioctl(fd, TIOCGNAME, name + 5) < 0 ) + return NULL; + return name; } diff --git a/libc/unistd/ttyname_r.c b/libc/unistd/ttyname_r.c index 82184df2..8ae46737 100644 --- a/libc/unistd/ttyname_r.c +++ b/libc/unistd/ttyname_r.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2013, 2014, 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 @@ -17,16 +17,28 @@ * Returns the pathname of a terminal. */ +#include + #include +#include #include #include +#if !defined(TTY_NAME_MAX) +#include +#endif + int ttyname_r(int fd, char* path, size_t path_size) { - if ( isatty(fd) < 1 ) + char name[TTY_NAME_MAX+1]; + name[0] = '/'; + name[1] = 'd'; + name[2] = 'e'; + name[3] = 'v'; + name[4] = '/'; + if ( ioctl(fd, TIOCGNAME, name + 5) < 0 ) return -1; - const char* result = "/dev/tty"; - if ( path_size <= strlcpy(path, result, path_size) ) + if ( path_size <= strlcpy(path, name, path_size) ) return errno = ERANGE, -1; return 0; } diff --git a/utils/ps.c b/utils/ps.c index b1c10b3b..c45dc15e 100644 --- a/utils/ps.c +++ b/utils/ps.c @@ -51,6 +51,27 @@ static char* get_program_path_of_pid(pid_t pid) } } +static char* get_ttyname_of_pid(pid_t pid) +{ + struct psctl_ttyname ctl; + memset(&ctl, 0, sizeof(ctl)); + ctl.buffer = NULL; + ctl.size = 0; + if ( psctl(pid, PSCTL_TTYNAME, &ctl) < 0 ) + return NULL; + while ( true ) + { + char* new_buffer = (char*) realloc(ctl.buffer, ctl.size); + if ( !new_buffer ) + return free(ctl.buffer), (char*) NULL; + ctl.buffer = new_buffer; + if ( psctl(pid, PSCTL_TTYNAME, &ctl) == 0 ) + return ctl.buffer; + if ( errno != ERANGE ) + return free(ctl.buffer), (char*) NULL; + } +} + static void compact_arguments(int* argc, char*** argv) { for ( int i = 0; i < *argc; i++ ) @@ -140,8 +161,12 @@ int main(int argc, char* argv[]) if ( show_full || show_long ) printf("PPID\t"); if ( show_long ) - printf("NI "); - printf("TTY "); + printf("PGID\t"); + if ( show_long ) + printf("SID\t"); + if ( show_long ) + printf("NI\t"); + printf("TTY\t"); printf("TIME\t "); printf("CMD\n"); pid_t pid = 0; @@ -175,8 +200,16 @@ int main(int argc, char* argv[]) if ( show_full || show_long ) printf("%" PRIiPID "\t", psst.ppid); if ( show_long ) - printf("%-4i", psst.nice); - printf("tty "); + printf("%" PRIiPID "\t", psst.pgid); + if ( show_long ) + printf("%" PRIiPID "\t", psst.sid); + if ( show_long ) + printf("%-4i\t", psst.nice); + char* ttyname = get_ttyname_of_pid(pid); + // TODO: Strip special characters from the ttyname lest an attacker + // do things to the user's terminal. + printf("%s\t", ttyname ? ttyname : "?"); + free(ttyname); time_t time = psst.tmns.tmns_utime.tv_sec; int hours = (time / (60 * 60)) % 24; int minutes = (time / 60) % 60;