ladybird/Userland/Libraries/LibCore/Process.cpp
Liav A 1c0aa51684 Kernel+Userland: Remove the {get,set}_thread_name syscalls
These syscalls are not necessary on their own, and they give the false
impression that a caller could set or get the thread name of any process
in the system, which is not true.

Therefore, move the functionality of these syscalls to be options in the
prctl syscall, which makes it abundantly clear that these operations
could only occur from a running thread in a process that sees other
threads in that process only.
2023-08-25 11:51:52 +02:00

211 lines
7 KiB
C++

/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/DeprecatedString.h>
#include <AK/ScopeGuard.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibCore/File.h>
#include <LibCore/Process.h>
#include <LibCore/System.h>
#include <errno.h>
#include <spawn.h>
#include <unistd.h>
#if defined(AK_OS_SERENITY)
# include <serenity.h>
# include <sys/prctl.h>
# include <syscall.h>
#elif defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_SOLARIS)
# include <sys/sysctl.h>
#endif
namespace Core {
struct ArgvList {
DeprecatedString m_path;
DeprecatedString m_working_directory;
Vector<char const*, 10> m_argv;
ArgvList(DeprecatedString path, size_t size)
: m_path { path }
{
m_argv.ensure_capacity(size + 2);
m_argv.append(m_path.characters());
}
void append(char const* arg)
{
m_argv.append(arg);
}
Span<char const*> get()
{
if (m_argv.is_empty() || m_argv.last() != nullptr)
m_argv.append(nullptr);
return m_argv;
}
void set_working_directory(DeprecatedString const& working_directory)
{
m_working_directory = working_directory;
}
ErrorOr<pid_t> spawn(Process::KeepAsChild keep_as_child)
{
#ifdef AK_OS_SERENITY
posix_spawn_file_actions_t spawn_actions;
posix_spawn_file_actions_init(&spawn_actions);
ScopeGuard cleanup_spawn_actions = [&] {
posix_spawn_file_actions_destroy(&spawn_actions);
};
if (!m_working_directory.is_empty())
posix_spawn_file_actions_addchdir(&spawn_actions, m_working_directory.characters());
auto pid = TRY(System::posix_spawn(m_path.view(), &spawn_actions, nullptr, const_cast<char**>(get().data()), System::environment()));
if (keep_as_child == Process::KeepAsChild::No)
TRY(System::disown(pid));
#else
auto pid = TRY(System::posix_spawn(m_path.view(), nullptr, nullptr, const_cast<char**>(get().data()), System::environment()));
// FIXME: Support keep_as_child outside Serenity.
(void)keep_as_child;
#endif
return pid;
}
};
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<DeprecatedString> arguments, DeprecatedString working_directory, KeepAsChild keep_as_child)
{
ArgvList argv { path, arguments.size() };
for (auto const& arg : arguments)
argv.append(arg.characters());
argv.set_working_directory(working_directory);
return argv.spawn(keep_as_child);
}
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, DeprecatedString working_directory, KeepAsChild keep_as_child)
{
Vector<DeprecatedString> backing_strings;
backing_strings.ensure_capacity(arguments.size());
ArgvList argv { path, arguments.size() };
for (auto const& arg : arguments) {
backing_strings.append(arg);
argv.append(backing_strings.last().characters());
}
argv.set_working_directory(working_directory);
return argv.spawn(keep_as_child);
}
ErrorOr<pid_t> Process::spawn(StringView path, ReadonlySpan<char const*> arguments, DeprecatedString working_directory, KeepAsChild keep_as_child)
{
ArgvList argv { path, arguments.size() };
for (auto arg : arguments)
argv.append(arg);
argv.set_working_directory(working_directory);
return argv.spawn(keep_as_child);
}
ErrorOr<String> Process::get_name()
{
#if defined(AK_OS_SERENITY)
char buffer[BUFSIZ];
int rc = get_process_name(buffer, BUFSIZ);
if (rc != 0)
return Error::from_syscall("get_process_name"sv, -rc);
return String::from_utf8(StringView { buffer, strlen(buffer) });
#elif defined(AK_OS_LINUX)
return String::from_utf8(StringView { program_invocation_name, strlen(program_invocation_name) });
#elif defined(AK_OS_BSD_GENERIC)
auto const* progname = getprogname();
return String::from_utf8(StringView { progname, strlen(progname) });
#else
// FIXME: Implement Process::get_name() for other platforms.
return "???"_string;
#endif
}
ErrorOr<void> Process::set_name([[maybe_unused]] StringView name, [[maybe_unused]] SetThreadName set_thread_name)
{
#if defined(AK_OS_SERENITY)
int rc = set_process_name(name.characters_without_null_termination(), name.length());
if (rc != 0)
return Error::from_syscall("set_process_name"sv, -rc);
if (set_thread_name == SetThreadName::No)
return {};
rc = prctl(PR_SET_THREAD_NAME, gettid(), name.characters_without_null_termination(), name.length());
if (rc != 0)
return Error::from_syscall("set_thread_name"sv, -rc);
return {};
#else
// FIXME: Implement Process::set_name() for other platforms.
return {};
#endif
}
ErrorOr<bool> Process::is_being_debugged()
{
#if defined(AK_OS_LINUX)
auto unbuffered_status_file = TRY(Core::File::open("/proc/self/status"sv, Core::File::OpenMode::Read));
auto status_file = TRY(Core::InputBufferedFile::create(move(unbuffered_status_file)));
auto buffer = TRY(ByteBuffer::create_uninitialized(4096));
while (TRY(status_file->can_read_line())) {
auto line = TRY(status_file->read_line(buffer));
auto const parts = line.split_view(':');
if (parts.size() < 2 || parts[0] != "TracerPid"sv)
continue;
auto tracer_pid = parts[1].to_uint<u32>();
return (tracer_pid != 0UL);
}
return false;
#elif defined(AK_OS_MACOS)
// https://developer.apple.com/library/archive/qa/qa1361/_index.html
int mib[4] = {};
struct kinfo_proc info = {};
size_t size = sizeof(info);
// Initialize mib, which tells sysctl the info we want, in this case
// we're looking for information about a specific process ID.
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
if (sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) < 0)
return Error::from_syscall("sysctl"sv, -errno);
// We're being debugged if the P_TRACED flag is set.
return ((info.kp_proc.p_flag & P_TRACED) != 0);
#endif
// FIXME: Implement this for more platforms.
return Error::from_string_view("Platform does not support checking for debugger"sv);
}
// Forces the process to sleep until a debugger is attached, then breaks.
void Process::wait_for_debugger_and_break()
{
bool should_print_process_info { true };
for (;;) {
auto check = Process::is_being_debugged();
if (check.is_error()) {
dbgln("Cannot wait for debugger: {}. Continuing.", check.release_error());
return;
}
if (check.value()) {
kill(getpid(), SIGTRAP);
return;
}
if (should_print_process_info) {
dbgln("Process {} with pid {} is sleeping, waiting for debugger.", Process::get_name(), getpid());
should_print_process_info = false;
}
::usleep(100 * 1000);
}
}
}