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.
This commit is contained in:
Jonas 'Sortie' Termansen 2016-11-15 21:08:41 +01:00
parent d529a1e332
commit db7182ddc3
32 changed files with 817 additions and 346 deletions

View File

@ -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"

View File

@ -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)

View File

@ -30,6 +30,7 @@
#include <sortix/dirent.h>
#include <sortix/fcntl.h>
#include <sortix/ioctl.h>
#include <sortix/stat.h>
#include <sortix/timespec.h>
#include <sortix/winsize.h>
@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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<Descriptor> tty;
Ref<Descriptor> root;
Ref<Descriptor> cwd;
Ref<MountTable> mtable;
@ -101,38 +102,36 @@ public:
Ref<DescriptorTable> GetDTable();
Ref<MountTable> GetMTable();
Ref<ProcessTable> GetPTable();
Ref<Descriptor> GetTTY();
Ref<Descriptor> GetRoot();
Ref<Descriptor> GetCWD();
Ref<Descriptor> GetDescriptor(int fd);
void SetTTY(Ref<Descriptor> tty);
void SetRoot(Ref<Descriptor> newroot);
void SetCWD(Ref<Descriptor> 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

View File

@ -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);

View File

@ -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);

View File

@ -24,6 +24,7 @@
#if __USE_SORTIX || __USE_POSIX
#define HOST_NAME_MAX 255
#define TTY_NAME_MAX 32
#endif
#endif

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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<Descriptor> desc = CurrentProcess()->GetDescriptor(fd);
if ( !desc )
return -1;
ioctx_t ctx; SetupUserIOCtx(&ctx);
return desc->ioctl(&ctx, cmd, arg);
}
ssize_t sys_readdirents(int fd, struct dirent* dirent, size_t size)
@ -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<Descriptor> desc = CurrentProcess()->GetDescriptor(fd);
if ( !desc )
return -1;
ioctx_t ctx; SetupUserIOCtx(&ctx);
return desc->tcgetwinsize(&ctx, ws);
return sys_ioctl(fd, TIOCGWINSZ, (uintptr_t) ws);
}
int sys_tcsetpgrp(int fd, pid_t pgid)

View File

@ -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 <sys/ioctl.h>
#include <sys/types.h>
#include <assert.h>
@ -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("<kernel process>")) )
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<Inode> tty(new LogTerminal(slashdev->dev, 0666, 0, 0, keyboard, kblayout));
// Register /dev/tty as the current-terminal factory.
Ref<Inode> 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<Inode> 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<Inode> 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<DescriptorTable> dtable = process->GetDTable();
Ref<Descriptor> 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<Descriptor> 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");

View File

@ -27,6 +27,7 @@
#include <wchar.h>
#include <sortix/fcntl.h>
#include <sortix/ioctl.h>
#include <sortix/keycodes.h>
#include <sortix/poll.h>
#include <sortix/signal.h>
@ -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

View File

@ -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);

View File

@ -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<DescriptorTable> dtable, Ref<MountTable> 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<MountTable> Process::GetMTable()
{
ScopedLock lock(&ptrlock);
@ -572,6 +563,12 @@ Ref<ProcessTable> Process::GetPTable()
return ptable;
}
Ref<Descriptor> Process::GetTTY()
{
ScopedLock lock(&ptrlock);
return tty;
}
Ref<Descriptor> Process::GetRoot()
{
ScopedLock lock(&ptrlock);
@ -586,6 +583,12 @@ Ref<Descriptor> Process::GetCWD()
return cwd;
}
void Process::SetTTY(Ref<Descriptor> newtty)
{
ScopedLock lock(&ptrlock);
tty = newtty;
}
void Process::SetRoot(Ref<Descriptor> 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();

View File

@ -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 <sys/ioctl.h>
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#if !defined(TTY_NAME_MAX)
#include <sortix/limits.h>
#endif
#include <sortix/psctl.h>
#include <sortix/kernel/clock.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/ptable.h>
@ -37,6 +44,7 @@ namespace Sortix {
int sys_psctl(pid_t pid, int request, void* ptr)
{
ScopedLock lock(&process_family_lock);
Ref<ProcessTable> 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<Descriptor> 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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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" */

View File

@ -17,9 +17,11 @@
* Terminal line discipline.
*/
#include <sys/ioctl.h>
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
@ -28,6 +30,9 @@
#include <sortix/fcntl.h>
#include <sortix/keycodes.h>
#if !defined(TTY_NAME_MAX)
#include <sortix/limits.h>
#endif
#include <sortix/poll.h>
#include <sortix/signal.h>
#include <sortix/stat.h>
@ -35,6 +40,7 @@
#include <sortix/termmode.h>
#include <sortix/winsize.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/interlock.h>
#include <sortix/kernel/ioctx.h>
@ -47,6 +53,7 @@
#include <sortix/kernel/refcount.h>
#include <sortix/kernel/scheduler.h>
#include <sortix/kernel/thread.h>
#include <sortix/kernel/vnode.h>
#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<Inode> 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<Inode>(NULL);
Ref<Descriptor> tty_desc = process->session->GetTTY();
if ( !tty_desc )
return errno = ENOTTY, Ref<Inode>(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> vnode(new Vnode(Ref<Inode>(this), Ref<Vnode>(NULL), 0, 0));
if ( !vnode )
return -1;
Ref<Descriptor> 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;
}

View File

@ -20,8 +20,12 @@
#ifndef SORTIX_TTY_H
#define SORTIX_TTY_H
#include <string.h>
#include <wchar.h>
#if !defined(TTY_NAME_MAX)
#include <sortix/limits.h>
#endif
#include <sortix/termios.h>
#include <sortix/kernel/kthread.h>
@ -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<Inode> 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];
};

View File

@ -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)

View File

@ -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 \

View File

@ -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

29
libc/unistd/getsid.c Normal file
View File

@ -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 <sys/syscall.h>
#include <unistd.h>
DEFN_SYSCALL1(pid_t, sys_getsid, SYSCALL_GETSID, pid_t);
pid_t getsid(pid_t pid)
{
return sys_getsid(pid);
}

29
libc/unistd/setsid.c Normal file
View File

@ -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 <sys/syscall.h>
#include <unistd.h>
DEFN_SYSCALL0(pid_t, sys_setsid, SYSCALL_SETSID);
pid_t setsid(void)
{
return sys_setsid();
}

View File

@ -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 <errno.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <limits.h>
#include <unistd.h>
#if !defined(TTY_NAME_MAX)
#include <sortix/limits.h>
#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;
}

View File

@ -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 <sys/ioctl.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#if !defined(TTY_NAME_MAX)
#include <sortix/limits.h>
#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;
}

View File

@ -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;