Browse Source

Merge branch 'tty' into dev

greatbridf 2 years ago
parent
commit
2aa7013175

+ 2 - 0
CMakeLists.txt

@@ -49,6 +49,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         src/kernel/hw/serial.cpp
                         src/kernel/hw/timer.c
                         src/kernel/event/event.cpp
+                        src/kernel/signal.cpp
                         src/types/bitmap.c
                         src/types/elf.cpp
                         src/types/libstdcpp.cpp
@@ -66,6 +67,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/kernel/mm.hpp
                         include/kernel/vfs.hpp
                         include/kernel/vga.hpp
+                        include/kernel/signal.hpp
                         include/kernel/hw/ata.hpp
                         include/kernel/hw/keyboard.h
                         include/kernel/hw/port.hpp

+ 3 - 0
gblibc/include/unistd.h

@@ -26,6 +26,9 @@ unsigned int sleep(unsigned int seconds);
 int chdir(const char* path);
 char* getcwd(char* buf, size_t bufsize);
 
+pid_t setsid(void);
+pid_t getsid(pid_t pid);
+
 #ifdef __cplusplus
 }
 #endif

+ 2 - 0
gblibc/private-include/syscall.h

@@ -14,6 +14,8 @@
 #define SYS_getdents (0x08)
 #define SYS_open (0x09)
 #define SYS_getcwd (0x0a)
+#define SYS_setsid (0x0b)
+#define SYS_getsid (0x0c)
 
 #ifdef __cplusplus
 extern "C" {

+ 10 - 0
gblibc/src/unistd.c

@@ -42,3 +42,13 @@ char* getcwd(char* buf, size_t bufsize)
 {
     return (char*)syscall2(SYS_getcwd, (uint32_t)buf, bufsize);
 }
+
+pid_t setsid(void)
+{
+    return syscall0(SYS_setsid);
+}
+
+pid_t getsid(pid_t pid)
+{
+    return syscall1(SYS_getsid, pid);
+}

+ 49 - 3
include/kernel/process.hpp

@@ -5,7 +5,9 @@
 #include <kernel/event/evtqueue.hpp>
 #include <kernel/interrupt.h>
 #include <kernel/mm.hpp>
+#include <kernel/signal.hpp>
 #include <kernel/task.h>
+#include <kernel/tty.hpp>
 #include <kernel/vfs.hpp>
 #include <stdint.h>
 #include <sys/types.h>
@@ -263,16 +265,23 @@ public:
     thdlist thds;
     kernel::evtqueue wait_lst;
     process_attr attr;
-    pid_t pid;
-    pid_t ppid;
     filearr files;
     types::string<> pwd;
+    kernel::signal_list signals;
+
+    pid_t pid;
+    pid_t ppid;
+    pid_t pgid;
+    pid_t sid;
 
 public:
     process(process&& val);
     process(const process&);
 
-    explicit process(pid_t ppid, bool system = true, types::string<>&& path = "/");
+    explicit process(pid_t ppid,
+        bool system = true,
+        types::string<>&& path = "/",
+        kernel::signal_list&& sigs = {});
 
     constexpr bool is_system(void) const
     {
@@ -296,12 +305,14 @@ class proclist final {
 public:
     using list_type = types::map<pid_t, process>;
     using child_index_type = types::hash_map<pid_t, types::list<pid_t>, types::linux_hasher<pid_t>>;
+    using tty_index_type = types::map<pid_t, tty*>;
     using iterator_type = list_type::iterator_type;
     using const_iterator_type = list_type::const_iterator_type;
 
 private:
     list_type m_procs;
     child_index_type m_child_idx;
+    tty_index_type m_tty_idx;
 
 public:
     template <typename... Args>
@@ -323,6 +334,25 @@ public:
         return iter;
     }
 
+    constexpr void set_ctrl_tty(pid_t pid, tty* _tty)
+    {
+        auto iter = m_tty_idx.find(pid);
+        _tty->set_pgrp(pid);
+        if (iter) {
+            iter->value = _tty;
+        } else {
+            m_tty_idx.insert(types::make_pair(pid, _tty));
+        }
+    }
+
+    constexpr tty* get_ctrl_tty(pid_t pid)
+    {
+        auto iter = m_tty_idx.find(pid);
+        if (!iter)
+            return nullptr;
+        return iter->value;
+    }
+
     constexpr void remove(pid_t pid)
     {
         make_children_orphans(pid);
@@ -362,6 +392,20 @@ public:
         }
     }
 
+    void send_signal(pid_t pid, kernel::sig_t signal)
+    {
+        auto iter = this->find(pid);
+        if (!iter)
+            return iter->signals.set(signal);
+    }
+    void send_signal_grp(pid_t pgid, kernel::sig_t signal)
+    {
+        for (auto& proc : m_procs) {
+            if (proc.value.pgid == pgid)
+                proc.value.signals.set(signal);
+        }
+    }
+
     void kill(pid_t pid, int exit_code);
 };
 
@@ -439,3 +483,5 @@ void k_new_thread(void (*func)(void*), void* data);
 
 void NORETURN freeze(void);
 void NORETURN kill_current(int exit_code);
+
+void check_signal(void);

+ 69 - 0
include/kernel/signal.hpp

@@ -0,0 +1,69 @@
+#pragma once
+
+#include <stdint.h>
+#include <types/cplusplus.hpp>
+#include <types/list.hpp>
+
+namespace kernel {
+
+using sig_t = uint32_t;
+
+constexpr sig_t SIGINT = 1 << 0;
+constexpr sig_t SIGQUIT = 1 << 1;
+constexpr sig_t SIGSTOP = 1 << 2;
+
+class signal_list {
+public:
+    using list_type = types::list<sig_t>;
+
+private:
+    list_type m_list;
+    sig_t m_mask;
+
+public:
+    constexpr signal_list(void)
+        : m_mask(0)
+    {
+    }
+    constexpr signal_list(const signal_list& val)
+        : m_list(val.m_list)
+        , m_mask(val.m_mask)
+    {
+    }
+
+    constexpr signal_list(signal_list&& val)
+        : m_list(types::move(val.m_list))
+        , m_mask(val.m_mask)
+    {
+    }
+
+    constexpr bool empty(void) const
+    {
+        return this->m_list.empty();
+    }
+
+    constexpr void set(sig_t signal)
+    {
+        if (this->m_mask && signal)
+            return;
+
+        this->m_list.push_back(signal);
+        this->m_mask |= signal;
+    }
+
+    constexpr sig_t pop(void)
+    {
+        if (this->empty())
+            return 0;
+
+        auto iter = this->m_list.begin();
+        sig_t signal = *iter;
+        this->m_list.erase(iter);
+
+        this->m_mask &= ~signal;
+
+        return signal;
+    }
+};
+
+} // namespace kernel

+ 1 - 1
include/kernel/syscall.hpp

@@ -4,6 +4,6 @@
 #include <types/types.h>
 
 // return value is stored in %eax and %edx
-typedef void (*syscall_handler)(interrupt_stack* data);
+typedef int (*syscall_handler)(interrupt_stack* data);
 
 void init_syscall(void);

+ 13 - 0
include/kernel/tty.hpp

@@ -1,6 +1,7 @@
 #pragma once
 #include <kernel/event/evtqueue.hpp>
 #include <stdint.h>
+#include <sys/types.h>
 #include <types/allocator.hpp>
 #include <types/buffer.hpp>
 #include <types/cplusplus.hpp>
@@ -17,12 +18,24 @@ public:
     void print(const char* str);
     size_t read(char* buf, size_t buf_size, size_t n);
 
+    constexpr void set_pgrp(pid_t pgid)
+    {
+        fg_pgroup = pgid;
+    }
+
+    constexpr pid_t get_pgrp(void) const
+    {
+        return fg_pgroup;
+    }
+
     char name[NAME_SIZE];
     bool echo = true;
 
 protected:
     types::buffer<types::kernel_ident_allocator> buf;
     kernel::evtqueue blocklist;
+
+    pid_t fg_pgroup;
 };
 
 class vga_tty : public virtual tty {

+ 2 - 0
include/types/map.hpp

@@ -323,12 +323,14 @@ public:
         constexpr iterator& operator=(const iterator& iter)
         {
             p = iter.p;
+            return *this;
         }
 
         constexpr iterator& operator=(iterator&& iter)
         {
             p = iter.p;
             iter.p = nullptr;
+            return *this;
         }
 
         constexpr bool operator==(const iterator& iter) const

+ 1 - 4
src/asm/interrupt.s

@@ -189,16 +189,13 @@ syscall_stub:
     jge syscall_stub_end
     pushal
 
-    # syscall function no. is in %eax
-    # store it in %ebx
-    movl %eax, %ebx
     # stack alignment and push *data
     movl %esp, %eax
     subl $0x4, %esp
     andl $0xfffffff0, %esp
     movl %eax, (%esp)
 
-    call *syscall_handlers(,%ebx,4)
+    call syscall_entry
 
     # restore stack
     popl %esp

+ 21 - 0
src/kernel/interrupt.cpp

@@ -244,10 +244,16 @@ extern "C" void int14_handler(int14_data* d)
     }
 }
 
+void after_irq(void)
+{
+    check_signal();
+}
+
 extern "C" void irq0_handler(interrupt_stack*)
 {
     inc_tick();
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
     schedule();
 }
 // keyboard interrupt
@@ -255,70 +261,85 @@ extern "C" void irq1_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
     handle_keyboard_interrupt();
+    after_irq();
 }
 extern "C" void irq2_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq3_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq4_handler(void)
 {
     // TODO: register interrupt handler in serial port driver
     serial_receive_data_interrupt();
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq5_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq6_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq7_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq8_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq9_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq10_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq11_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq12_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq13_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq14_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }
 extern "C" void irq15_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    after_irq();
 }

+ 32 - 10
src/kernel/process.cpp

@@ -93,23 +93,26 @@ process::process(process&& val)
     , thds { types::move(val.thds), this }
     , wait_lst(types::move(val.wait_lst))
     , attr { val.attr }
-    , pid(val.pid)
-    , ppid(val.ppid)
     , files(types::move(val.files))
     , pwd(types::move(val.pwd))
+    , pid(val.pid)
+    , ppid(val.ppid)
+    , pgid(val.pgid)
+    , sid(val.sid)
 {
     if (current_process == &val)
         current_process = this;
-
-    val.pid = 0;
-    val.ppid = 0;
-    val.attr.system = 0;
-    val.attr.zombie = 0;
 }
 
 process::process(const process& parent)
-    : process { parent.pid, parent.is_system(), types::string<>(parent.pwd) }
+    : process { parent.pid,
+        parent.is_system(),
+        types::string<>(parent.pwd),
+        kernel::signal_list(parent.signals) }
 {
+    this->pgid = parent.pgid;
+    this->sid = parent.sid;
+
     for (auto& area : parent.mms) {
         if (area.is_kernel_space() || area.attr.in.system)
             continue;
@@ -120,12 +123,18 @@ process::process(const process& parent)
     this->files.dup(parent.files);
 }
 
-process::process(pid_t _ppid, bool _system, types::string<>&& path)
+process::process(pid_t _ppid,
+    bool _system,
+    types::string<>&& path,
+    kernel::signal_list&& _sigs)
     : mms(*kernel_mms)
     , attr { .system = _system }
+    , pwd { types::move(path) }
+    , signals(types::move(_sigs))
     , pid { process::alloc_pid() }
     , ppid { _ppid }
-    , pwd { path }
+    , pgid { 0 }
+    , sid { 0 }
 {
 }
 
@@ -398,3 +407,16 @@ void NORETURN kill_current(int exit_code)
     procs->kill(current_process->pid, exit_code);
     schedule_noreturn();
 }
+
+void check_signal()
+{
+    switch (current_process->signals.pop()) {
+    case kernel::SIGINT:
+    case kernel::SIGQUIT:
+    case kernel::SIGSTOP:
+        kill_current(-1);
+        break;
+    case 0:
+        break;
+    }
+}

+ 0 - 0
src/kernel/signal.cpp


+ 75 - 65
src/kernel/syscall.cpp

@@ -17,26 +17,11 @@
 #include <types/elf.hpp>
 #include <types/status.h>
 
-#define SYSCALL_SET_RETURN_VAL_EAX(_eax) \
-    data->s_regs.eax = ((decltype(data->s_regs.eax))(_eax))
-
-#define SYSCALL_SET_RETURN_VAL_EDX(_edx) \
-    data->s_regs.edx = ((decltype(data->s_regs.edx))(_edx))
-
-#define SYSCALL_SET_RETURN_VAL(_eax, _edx) \
-    SYSCALL_SET_RETURN_VAL_EAX(_eax);      \
-    SYSCALL_SET_RETURN_VAL_EDX(_edx)
-
 #define SYSCALL_HANDLERS_SIZE (16)
 syscall_handler syscall_handlers[SYSCALL_HANDLERS_SIZE];
 
-void _syscall_not_impl(interrupt_stack* data)
-{
-    SYSCALL_SET_RETURN_VAL(0xffffffff, 0xffffffff);
-}
-
 extern "C" void _syscall_stub_fork_return(void);
-void _syscall_fork(interrupt_stack* data)
+int _syscall_fork(interrupt_stack* data)
 {
     auto* newproc = &procs->emplace(*current_process)->value;
     auto* newthd = &newproc->thds.Emplace(*current_thread, newproc);
@@ -74,80 +59,74 @@ void _syscall_fork(interrupt_stack* data)
     // eflags
     push_stack(&newthd->esp, 0);
 
-    SYSCALL_SET_RETURN_VAL(newproc->pid, 0);
+    return newproc->pid;
 }
 
-void _syscall_write(interrupt_stack* data)
+int _syscall_write(interrupt_stack* data)
 {
     int fd = data->s_regs.edi;
     const char* buf = reinterpret_cast<const char*>(data->s_regs.esi);
     size_t n = data->s_regs.edx;
 
     auto* file = current_process->files[fd];
-    if (file->type == fs::file::types::directory) {
-        SYSCALL_SET_RETURN_VAL(GB_FAILED, EINVAL);
-        return;
-    }
+    if (file->type == fs::file::types::directory)
+        return GB_FAILED;
 
     int n_wrote = fs::vfs_write(file->ind, buf, file->cursor, n);
     file->cursor += n_wrote;
-    SYSCALL_SET_RETURN_VAL(n_wrote, 0);
+    return n_wrote;
 }
 
-void _syscall_read(interrupt_stack* data)
+int _syscall_read(interrupt_stack* data)
 {
     int fd = data->s_regs.edi;
     char* buf = reinterpret_cast<char*>(data->s_regs.esi);
     size_t n = data->s_regs.edx;
 
     auto* file = current_process->files[fd];
-    if (file->type == fs::file::types::directory) {
-        SYSCALL_SET_RETURN_VAL(GB_FAILED, EINVAL);
-        return;
-    }
+    if (file->type == fs::file::types::directory)
+        return GB_FAILED;
 
     // TODO: copy to user function !IMPORTANT
     int n_wrote = fs::vfs_read(file->ind, buf, n, file->cursor, n);
-    file->cursor += n_wrote;
-    SYSCALL_SET_RETURN_VAL(n_wrote, 0);
+    if (n_wrote >= 0)
+        file->cursor += n_wrote;
+    return n_wrote;
 }
 
-void _syscall_sleep(interrupt_stack* data)
+int _syscall_sleep(interrupt_stack* data)
 {
     current_thread->attr.ready = 0;
     current_thread->attr.wait = 1;
 
-    SYSCALL_SET_RETURN_VAL(0, 0);
-
     schedule();
+    return 0;
 }
 
-void _syscall_chdir(interrupt_stack* data)
+int _syscall_chdir(interrupt_stack* data)
 {
     const char* path = reinterpret_cast<const char*>(data->s_regs.edi);
     auto* dir = fs::vfs_open(path);
     if (!dir) {
         // set errno ENOTFOUND
-        SYSCALL_SET_RETURN_VAL_EAX(-1);
-        return;
+        return -1;
     }
 
     if (!dir->ind->flags.in.directory) {
         // set errno ENOTDIR
-        SYSCALL_SET_RETURN_VAL_EAX(-1);
-        return;
+        return -1;
     }
 
     current_process->pwd = path;
 
-    SYSCALL_SET_RETURN_VAL_EAX(0);
+    return 0;
 }
 
 // syscall_exec(const char* exec, const char** argv)
 // @param exec: the path of program to execute
 // @param argv: arguments end with nullptr
 // @param envp: environment variables end with nullptr
-void _syscall_exec(interrupt_stack* data)
+int _syscall_exec(interrupt_stack* data)
 {
     const char* exec = reinterpret_cast<const char*>(data->s_regs.edi);
     char* const* argv = reinterpret_cast<char* const*>(data->s_regs.esi);
@@ -160,17 +139,17 @@ void _syscall_exec(interrupt_stack* data)
     d.system = false;
 
     int ret = types::elf::elf32_load(&d);
-    if (ret != GB_OK) {
-        data->s_regs.eax = d.errcode;
-        return;
-    }
+    if (ret != GB_OK)
+        return d.errcode;
 
     data->v_eip = d.eip;
     data->esp = (uint32_t)d.sp;
+
+    return 0;
 }
 
 // @param exit_code
-void _syscall_exit(interrupt_stack* data)
+int _syscall_exit(interrupt_stack* data)
 {
     uint32_t exit_code = data->s_regs.edi;
 
@@ -187,25 +166,22 @@ void _syscall_exit(interrupt_stack* data)
 
     // we should not return to here
     assert(false);
+    return -1;
 }
 
 // @param address of exit code: int*
 // @return pid of the exited process
-void _syscall_wait(interrupt_stack* data)
+int _syscall_wait(interrupt_stack* data)
 {
     auto* arg1 = reinterpret_cast<int*>(data->s_regs.edi);
 
     // TODO: check valid address
-    if (arg1 < (int*)0x40000000) {
-        SYSCALL_SET_RETURN_VAL(-1, EINVAL);
-        return;
-    }
+    if (arg1 < (int*)0x40000000)
+        return -1;
 
     auto& waitlst = current_process->wait_lst;
-    if (waitlst.empty() && !procs->has_child(current_process->pid)) {
-        SYSCALL_SET_RETURN_VAL(-1, ECHILD);
-        return;
-    }
+    if (waitlst.empty() && !procs->has_child(current_process->pid))
+        return -1;
 
     while (waitlst.empty()) {
         current_thread->attr.ready = 0;
@@ -227,20 +203,18 @@ void _syscall_wait(interrupt_stack* data)
     *arg1 = (int)evt.data2;
 
     procs->remove(pid);
-    SYSCALL_SET_RETURN_VAL(pid, 0);
+    return pid;
 }
 
-void _syscall_getdents(interrupt_stack* data)
+int _syscall_getdents(interrupt_stack* data)
 {
     int fd = data->s_regs.edi;
     auto* buf = (char*)(data->s_regs.esi);
     size_t cnt = data->s_regs.edx;
 
     auto* dir = current_process->files[fd];
-    if (dir->type != fs::file::types::directory) {
-        data->s_regs.eax = -1;
-        return;
-    }
+    if (dir->type != fs::file::types::directory)
+        return -1;
 
     size_t orig_cnt = cnt;
     int nread = dir->ind->fs->inode_readdir(dir->ind, dir->cursor,
@@ -270,17 +244,17 @@ void _syscall_getdents(interrupt_stack* data)
     if (nread > 0)
         dir->cursor += nread;
 
-    data->s_regs.eax = orig_cnt - cnt;
+    return orig_cnt - cnt;
 }
 
-void _syscall_open(interrupt_stack* data)
+int _syscall_open(interrupt_stack* data)
 {
     auto* path = (const char*)data->s_regs.edi;
     uint32_t flags = data->s_regs.esi;
-    data->s_regs.eax = current_process->files.open(path, flags);
+    return current_process->files.open(path, flags);
 }
 
-void _syscall_getcwd(interrupt_stack* data)
+int _syscall_getcwd(interrupt_stack* data)
 {
     char* buf = reinterpret_cast<char*>(data->s_regs.edi);
     size_t bufsize = reinterpret_cast<size_t>(data->s_regs.esi);
@@ -289,7 +263,41 @@ void _syscall_getcwd(interrupt_stack* data)
     strncpy(buf, current_process->pwd.c_str(), bufsize);
     buf[bufsize - 1] = 0;
 
-    SYSCALL_SET_RETURN_VAL_EAX(buf);
+    return (uint32_t)buf;
+}
+
+int _syscall_setsid(interrupt_stack* data)
+{
+    if (current_process->pid == current_process->pgid)
+        return -1;
+
+    current_process->sid = current_process->pid;
+    current_process->pgid = current_process->pid;
+
+    // TODO: get tty* from fd or block device id
+    procs->set_ctrl_tty(current_process->pid, console);
+
+    return current_process->pid;
+}
+
+int _syscall_getsid(interrupt_stack* data)
+{
+    pid_t pid = data->s_regs.edi;
+
+    auto* proc = procs->find(pid);
+    if (!proc || proc->sid != current_process->sid)
+        return -1;
+
+    return proc->sid;
+}
+
+extern "C" void syscall_entry(interrupt_stack* data)
+{
+    int ret = syscall_handlers[data->s_regs.eax](data);
+
+    data->s_regs.eax = ret;
+
+    check_signal();
 }
 
 SECTION(".text.kinit")
@@ -306,4 +314,6 @@ void init_syscall(void)
     syscall_handlers[8] = _syscall_getdents;
     syscall_handlers[9] = _syscall_open;
     syscall_handlers[10] = _syscall_getcwd;
+    syscall_handlers[11] = _syscall_setsid;
+    syscall_handlers[12] = _syscall_getsid;
 }

+ 74 - 19
src/kernel/tty.cpp

@@ -1,3 +1,4 @@
+#include <kernel/event/evtqueue.hpp>
 #include <kernel/hw/serial.h>
 #include <kernel/process.hpp>
 #include <kernel/tty.hpp>
@@ -5,6 +6,10 @@
 #include <stdint.h>
 #include <stdio.h>
 
+#define TTY_DATA (1 << 0)
+#define TTY_EOF (1 << 1)
+#define TTY_INT (1 << 2)
+
 tty::tty()
     : buf(BUFFER_SIZE)
 {
@@ -21,12 +26,31 @@ size_t tty::read(char* buf, size_t buf_size, size_t n)
     size_t orig_n = n;
 
     while (buf_size && n) {
-        while (this->buf.empty()) {
-            current_thread->attr.ready = 0;
-            current_thread->attr.wait = 1;
-            this->blocklist.subscribe(current_thread);
-            schedule();
-            this->blocklist.unsubscribe(current_thread);
+        if (this->buf.empty()) {
+            while (this->blocklist.empty()) {
+                current_thread->attr.ready = 0;
+                current_thread->attr.wait = 1;
+                this->blocklist.subscribe(current_thread);
+                schedule();
+
+                if (!this->blocklist.empty()) {
+                    this->blocklist.unsubscribe(current_thread);
+                    break;
+                }
+            }
+
+            auto evt = this->blocklist.front();
+            switch ((int)evt.data1) {
+            // INTERRUPT
+            case TTY_INT:
+                return -1;
+            // DATA
+            case TTY_DATA:
+                break;
+            // EOF
+            case TTY_EOF:
+                return orig_n - n;
+            }
         }
 
         *buf = this->buf.get();
@@ -78,33 +102,64 @@ void serial_tty::recvchar(char c)
             serial_send_data(PORT_SERIAL0, '\r');
             serial_send_data(PORT_SERIAL0, '\n');
         }
+        this->blocklist.push(kernel::evt { nullptr, (void*)TTY_DATA, nullptr, nullptr });
         this->blocklist.notify();
         break;
     // ^?: backspace
     case 0x7f:
-        if (!buf.empty() && buf.back() != '\n')
+        if (!buf.empty() && buf.back() != '\n') {
             buf.pop();
 
-        if (echo) {
-            serial_send_data(PORT_SERIAL0, 0x08);
-            serial_send_data(PORT_SERIAL0, '\x1b');
-            serial_send_data(PORT_SERIAL0, '[');
-            serial_send_data(PORT_SERIAL0, 'K');
+            if (echo) {
+                serial_send_data(PORT_SERIAL0, 0x08);
+                serial_send_data(PORT_SERIAL0, '\x1b');
+                serial_send_data(PORT_SERIAL0, '[');
+                serial_send_data(PORT_SERIAL0, 'K');
+            }
         }
         break;
     // ^U: clear the line
     case 0x15:
-        while (!buf.empty() && buf.back() != '\n')
+        while (!buf.empty() && buf.back() != '\n') {
             buf.pop();
 
-        if (echo) {
-            serial_send_data(PORT_SERIAL0, '\r');
-            serial_send_data(PORT_SERIAL0, '\x1b');
-            serial_send_data(PORT_SERIAL0, '[');
-            serial_send_data(PORT_SERIAL0, '2');
-            serial_send_data(PORT_SERIAL0, 'K');
+            if (echo) {
+                // clear the line
+                // serial_send_data(PORT_SERIAL0, '\r');
+                // serial_send_data(PORT_SERIAL0, '\x1b');
+                // serial_send_data(PORT_SERIAL0, '[');
+                // serial_send_data(PORT_SERIAL0, '2');
+                // serial_send_data(PORT_SERIAL0, 'K');
+                serial_send_data(PORT_SERIAL0, 0x08);
+                serial_send_data(PORT_SERIAL0, '\x1b');
+                serial_send_data(PORT_SERIAL0, '[');
+                serial_send_data(PORT_SERIAL0, 'K');
+            }
         }
         break;
+    // ^C: SIGINT
+    case 0x03:
+        this->blocklist.push(kernel::evt { nullptr, (void*)TTY_INT, nullptr, nullptr });
+        this->blocklist.notify();
+        procs->send_signal_grp(fg_pgroup, kernel::SIGINT);
+        break;
+    // ^D: EOF
+    case 0x04:
+        this->blocklist.push(kernel::evt { nullptr, (void*)TTY_EOF, nullptr, nullptr });
+        this->blocklist.notify();
+        break;
+    // ^Z: SIGSTOP
+    case 0x1a:
+        this->blocklist.push(kernel::evt { nullptr, (void*)TTY_INT, nullptr, nullptr });
+        this->blocklist.notify();
+        procs->send_signal_grp(fg_pgroup, kernel::SIGSTOP);
+        break;
+    // ^\: SIGQUIT
+    case 0x1c:
+        this->blocklist.push(kernel::evt { nullptr, (void*)TTY_INT, nullptr, nullptr });
+        this->blocklist.notify();
+        procs->send_signal_grp(fg_pgroup, kernel::SIGQUIT);
+        break;
     default:
         buf.put(c);
         if (echo)

+ 10 - 0
user-space-program/init.c

@@ -9,6 +9,7 @@ int main(int argc, char** argv)
 {
     print("***** GBOS INIT SYSTEM *****\n");
 
+_run_sh:;
     pid_t sh_pid = fork();
     if (sh_pid < 0) {
         print("[init] unable to fork(), exiting...\n");
@@ -17,6 +18,12 @@ int main(int argc, char** argv)
 
     // child
     if (sh_pid == 0) {
+        pid_t sid = setsid();
+        if (sid < 0) {
+            print("[init] unable to setsid, exiting...\n");
+            return -1;
+        }
+
         char* shell_argv[128] = {};
         char* envp[1] = { NULL };
 
@@ -40,6 +47,9 @@ int main(int argc, char** argv)
         pid = wait(&ret);
         snprintf(buf, sizeof(buf), "[init] pid%d has exited with code %d\n", pid, ret);
         print(buf);
+        // sh
+        if (pid == sh_pid)
+            goto _run_sh;
     }
 
     return 0;