From 688e54eac739405f9fe848e986a1194f29483f14 Mon Sep 17 00:00:00 2001 From: AnotherTest Date: Sat, 15 Aug 2020 23:43:19 +0430 Subject: [PATCH] Kernel: Distinguish between new and old process groups with equal pgids This does not add any behaviour change to the processes, but it ties a TTY to an active process group via TIOCSPGRP, and returns the TTY to the kernel when all processes in the process group die. Also makes the TTY keep a link to the original controlling process' parent (for SIGCHLD) instead of the process itself. --- Kernel/CMakeLists.txt | 1 + Kernel/Process.cpp | 1 + Kernel/Process.h | 7 ++-- Kernel/ProcessGroup.cpp | 70 ++++++++++++++++++++++++++++++++++++ Kernel/ProcessGroup.h | 72 +++++++++++++++++++++++++++++++++++++ Kernel/Syscalls/fork.cpp | 2 +- Kernel/Syscalls/setpgid.cpp | 10 +++--- Kernel/TTY/TTY.cpp | 30 +++++++++------- Kernel/TTY/TTY.h | 6 ++-- 9 files changed, 176 insertions(+), 23 deletions(-) create mode 100644 Kernel/ProcessGroup.cpp create mode 100644 Kernel/ProcessGroup.h diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 991030cd4d9..ff9379d8233 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -82,6 +82,7 @@ set(KERNEL_SOURCES PCI/MMIOAccess.cpp PerformanceEventBuffer.cpp Process.cpp + ProcessGroup.cpp Profiling.cpp Ptrace.cpp RTC.cpp diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index d722a4335ae..14ef8e033f4 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -111,6 +111,7 @@ void Process::initialize() next_pid.store(0, AK::MemoryOrder::memory_order_release); g_processes = new InlineLinkedList; + g_process_groups = new InlineLinkedList; g_hostname = new String("courage"); g_hostname_lock = new Lock; diff --git a/Kernel/Process.h b/Kernel/Process.h index 763b09e9c24..7d25648c3fd 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -159,8 +160,8 @@ public: ProcessID pid() const { return m_pid; } SessionID sid() const { return m_sid; } bool is_session_leader() const { return m_sid.value() == m_pid.value(); } - ProcessGroupID pgid() const { return m_pgid; } - bool is_group_leader() const { return m_pgid.value() == m_pid.value(); } + ProcessGroupID pgid() const { return m_pg ? m_pg->pgid() : 0; } + bool is_group_leader() const { return pgid().value() == m_pid.value(); } const FixedArray& extra_gids() const { return m_extra_gids; } uid_t euid() const { return m_euid; } gid_t egid() const { return m_egid; } @@ -620,7 +621,7 @@ private: ProcessID m_pid { 0 }; SessionID m_sid { 0 }; - ProcessGroupID m_pgid { 0 }; + RefPtr m_pg; uid_t m_euid { 0 }; gid_t m_egid { 0 }; diff --git a/Kernel/ProcessGroup.cpp b/Kernel/ProcessGroup.cpp new file mode 100644 index 00000000000..1fd9a7e53cc --- /dev/null +++ b/Kernel/ProcessGroup.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ProcessGroup.h" + +namespace Kernel { + +RecursiveSpinLock g_process_groups_lock; +InlineLinkedList* g_process_groups; + +ProcessGroup::~ProcessGroup() +{ + ScopedSpinLock lock(g_process_groups_lock); + g_process_groups->remove(this); +} + +NonnullRefPtr ProcessGroup::create(ProcessGroupID pgid) +{ + auto process_group = adopt(*new ProcessGroup(pgid)); + { + ScopedSpinLock lock(g_process_groups_lock); + g_process_groups->prepend(process_group); + } + + return process_group; +} + +NonnullRefPtr ProcessGroup::find_or_create(ProcessGroupID pgid) +{ + if (auto existing = from_pgid(pgid)) + return *existing; + + return create(pgid); +} + +ProcessGroup* ProcessGroup::from_pgid(ProcessGroupID pgid) +{ + ScopedSpinLock lock(g_process_groups_lock); + + for (auto& group : *g_process_groups) { + if (group.pgid() == pgid) + return &group; + } + return nullptr; +} + +} diff --git a/Kernel/ProcessGroup.h b/Kernel/ProcessGroup.h new file mode 100644 index 00000000000..17b93739a24 --- /dev/null +++ b/Kernel/ProcessGroup.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class ProcessGroup + : public RefCounted + , public Weakable + , public InlineLinkedListNode { + + AK_MAKE_NONMOVABLE(ProcessGroup); + AK_MAKE_NONCOPYABLE(ProcessGroup); + + friend InlineLinkedListNode; + +public: + ~ProcessGroup(); + + static NonnullRefPtr create(ProcessGroupID); + static NonnullRefPtr find_or_create(ProcessGroupID); + static ProcessGroup* from_pgid(ProcessGroupID); + + const ProcessGroupID& pgid() const { return m_pgid; } + +private: + ProcessGroup(ProcessGroupID pgid) + : m_pgid(pgid) + { + } + + ProcessGroup* m_prev { nullptr }; + ProcessGroup* m_next { nullptr }; + + ProcessGroupID m_pgid; +}; + +extern InlineLinkedList* g_process_groups; +extern RecursiveSpinLock g_process_groups_lock; + +} diff --git a/Kernel/Syscalls/fork.cpp b/Kernel/Syscalls/fork.cpp index a66d7722870..f011ebd735e 100644 --- a/Kernel/Syscalls/fork.cpp +++ b/Kernel/Syscalls/fork.cpp @@ -46,7 +46,7 @@ pid_t Process::sys$fork(RegisterState& regs) child->m_unveiled_paths = m_unveiled_paths; child->m_fds = m_fds; child->m_sid = m_sid; - child->m_pgid = m_pgid; + child->m_pg = m_pg; child->m_umask = m_umask; #ifdef FORK_DEBUG diff --git a/Kernel/Syscalls/setpgid.cpp b/Kernel/Syscalls/setpgid.cpp index 3e8a09cd43f..fb650ecf23d 100644 --- a/Kernel/Syscalls/setpgid.cpp +++ b/Kernel/Syscalls/setpgid.cpp @@ -56,7 +56,7 @@ pid_t Process::sys$setsid() return -EPERM; // Create a new Session and a new ProcessGroup. m_sid = m_pid.value(); - m_pgid = m_pid.value(); + m_pg = ProcessGroup::create(ProcessGroupID(m_pid.value())); m_tty = nullptr; return m_sid.value(); } @@ -65,18 +65,18 @@ pid_t Process::sys$getpgid(pid_t pid) { REQUIRE_PROMISE(proc); if (pid == 0) - return m_pgid.value(); + return pgid().value(); ScopedSpinLock lock(g_processes_lock); // FIXME: Use a ProcessHandle auto process = Process::from_pid(pid); if (!process) return -ESRCH; - return process->m_pgid.value(); + return process->pgid().value(); } pid_t Process::sys$getpgrp() { REQUIRE_PROMISE(stdio); - return m_pgid.value(); + return pgid().value(); } SessionID Process::get_sid_from_pgid(ProcessGroupID pgid) @@ -134,7 +134,7 @@ int Process::sys$setpgid(pid_t specified_pid, pid_t specified_pgid) return -EPERM; } // FIXME: There are more EPERM conditions to check for here.. - process->m_pgid = new_pgid; + process->m_pg = ProcessGroup::find_or_create(new_pgid); return 0; } diff --git a/Kernel/TTY/TTY.cpp b/Kernel/TTY/TTY.cpp index 28e781927e5..6ffce4dbfff 100644 --- a/Kernel/TTY/TTY.cpp +++ b/Kernel/TTY/TTY.cpp @@ -155,10 +155,8 @@ void TTY::emit(u8 ch) if (ch == m_termios.c_cc[VSUSP]) { dbg() << tty_name() << ": VSUSP pressed!"; generate_signal(SIGTSTP); - if (m_process) { - if (auto parent = Process::from_pid(m_process->ppid())) - (void)parent->send_signal(SIGCHLD, m_process); - } + if (m_original_process_parent) + (void)m_original_process_parent->send_signal(SIGCHLD, nullptr); // TODO: Else send it to the session leader maybe? return; } @@ -310,13 +308,27 @@ int TTY::ioctl(FileDescription&, unsigned request, FlatPtr arg) if (pgid <= 0) return -EINVAL; InterruptDisabler disabler; - auto process = Process::from_pid(pgid.value()); + auto process_group = ProcessGroup::from_pgid(pgid); + // Disallow setting a nonexistent PGID. + if (!process_group) + return -EINVAL; + + auto process = Process::from_pid(ProcessID(pgid.value())); SessionID new_sid = process ? process->sid() : Process::get_sid_from_pgid(pgid); if (!new_sid || new_sid != current_process.sid()) return -EPERM; if (process && pgid != process->pgid()) return -EPERM; - m_process = process ? process->make_weak_ptr() : WeakPtr(); + m_pg = process_group->make_weak_ptr(); + + if (process) { + if (auto parent = Process::from_pid(process->ppid())) { + m_original_process_parent = parent->make_weak_ptr(); + return 0; + } + } + + m_original_process_parent = nullptr; return 0; } case TCGETS: { @@ -392,10 +404,4 @@ void TTY::hang_up() { generate_signal(SIGHUP); } - -ProcessGroupID TTY::pgid() const -{ - return m_process ? m_process->pgid() : 0; -} - } diff --git a/Kernel/TTY/TTY.h b/Kernel/TTY/TTY.h index e8febbd0790..d858aa68a6a 100644 --- a/Kernel/TTY/TTY.h +++ b/Kernel/TTY/TTY.h @@ -30,6 +30,7 @@ #include #include #include +#include #include namespace Kernel { @@ -50,7 +51,7 @@ public: unsigned short rows() const { return m_rows; } unsigned short columns() const { return m_columns; } - ProcessGroupID pgid() const; + ProcessGroupID pgid() const { return m_pg ? m_pg->pgid() : 0; } void set_termios(const termios&); bool should_generate_signals() const { return m_termios.c_lflag & ISIG; } @@ -91,7 +92,8 @@ private: virtual bool is_tty() const final override { return true; } CircularDeque m_input_buffer; - WeakPtr m_process; + WeakPtr m_original_process_parent; + WeakPtr m_pg; termios m_termios; unsigned short m_rows { 0 }; unsigned short m_columns { 0 };