LibWebView+LibCore: Move IPCProcess into WebView::Process

Ladybird's HelperProcess.cpp was the only user of the IPCProcess class.
Moving the helper class from LibCore allows for some more interesting
LibIPC changes in the upcoming commits.
This commit is contained in:
Andrew Kaster 2024-10-22 13:27:15 -06:00 committed by Andrew Kaster
parent e537adad77
commit 9a6eccc590
Notes: github-actions[bot] 2024-10-23 19:14:21 +00:00
6 changed files with 144 additions and 151 deletions

View file

@ -42,15 +42,15 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
options.executable = path;
}
auto result = Core::IPCProcess::spawn<ClientType>(move(options), forward<ClientArguments>(client_arguments)...);
auto result = WebView::Process::spawn<ClientType>(process_type, move(options), forward<ClientArguments>(client_arguments)...);
if (!result.is_error()) {
auto process = result.release_value();
auto&& [process, client] = result.release_value();
if constexpr (requires { process.client->set_pid(pid_t {}); })
process.client->set_pid(process.process.pid());
if constexpr (requires { client->set_pid(pid_t {}); })
client->set_pid(process.pid());
WebView::Application::the().add_child_process(WebView::Process { process_type, process.client, move(process.process) });
WebView::Application::the().add_child_process(move(process));
if (chrome_options.profile_helper_process == process_type) {
dbgln();
@ -59,7 +59,7 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
dbgln();
}
return move(process.client);
return move(client);
}
if (i == candidate_server_paths.size() - 1) {

View file

@ -14,9 +14,6 @@
#include <LibCore/Environment.h>
#include <LibCore/File.h>
#include <LibCore/Process.h>
#include <LibCore/Socket.h>
#include <LibCore/SocketAddress.h>
#include <LibCore/StandardPaths.h>
#include <LibCore/System.h>
#include <errno.h>
#include <signal.h>
@ -363,101 +360,4 @@ ErrorOr<bool> Process::wait_for_termination()
return exited_with_code_0;
}
ErrorOr<IPCProcess::ProcessAndIPCSocket> IPCProcess::spawn_and_connect_to_process(ProcessSpawnOptions const& options)
{
int socket_fds[2] {};
TRY(System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
ArmedScopeGuard guard_fd_0 { [&] { MUST(System::close(socket_fds[0])); } };
ArmedScopeGuard guard_fd_1 { [&] { MUST(System::close(socket_fds[1])); } };
auto& file_actions = const_cast<ProcessSpawnOptions&>(options).file_actions;
file_actions.append(FileAction::CloseFile { socket_fds[0] });
auto takeover_string = MUST(String::formatted("{}:{}", options.name, socket_fds[1]));
TRY(Environment::set("SOCKET_TAKEOVER"sv, takeover_string, Environment::Overwrite::Yes));
auto process = TRY(Process::spawn(options));
auto ipc_socket = TRY(LocalSocket::adopt_fd(socket_fds[0]));
guard_fd_0.disarm();
TRY(ipc_socket->set_blocking(true));
return ProcessAndIPCSocket { move(process), move(ipc_socket) };
}
ErrorOr<Optional<pid_t>> IPCProcess::get_process_pid(StringView process_name, StringView pid_path)
{
if (System::stat(pid_path).is_error())
return OptionalNone {};
Optional<pid_t> pid;
{
auto pid_file = File::open(pid_path, File::OpenMode::Read);
if (pid_file.is_error()) {
warnln("Could not open {} PID file '{}': {}", process_name, pid_path, pid_file.error());
return pid_file.release_error();
}
auto contents = pid_file.value()->read_until_eof();
if (contents.is_error()) {
warnln("Could not read {} PID file '{}': {}", process_name, pid_path, contents.error());
return contents.release_error();
}
pid = StringView { contents.value() }.to_number<pid_t>();
}
if (!pid.has_value()) {
warnln("{} PID file '{}' exists, but with an invalid PID", process_name, pid_path);
TRY(System::unlink(pid_path));
return OptionalNone {};
}
if (kill(*pid, 0) < 0) {
warnln("{} PID file '{}' exists with PID {}, but process cannot be found", process_name, pid_path, *pid);
TRY(System::unlink(pid_path));
return OptionalNone {};
}
return pid;
}
// This is heavily based on how SystemServer's Service creates its socket.
ErrorOr<int> IPCProcess::create_ipc_socket(ByteString const& socket_path)
{
if (!System::stat(socket_path).is_error())
TRY(System::unlink(socket_path));
#ifdef SOCK_NONBLOCK
auto socket_fd = TRY(System::socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0));
#else
auto socket_fd = TRY(System::socket(AF_LOCAL, SOCK_STREAM, 0));
int option = 1;
TRY(System::ioctl(socket_fd, FIONBIO, &option));
TRY(System::fcntl(socket_fd, F_SETFD, FD_CLOEXEC));
#endif
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_GNU_HURD)
TRY(System::fchmod(socket_fd, 0600));
#endif
auto socket_address = SocketAddress::local(socket_path);
auto socket_address_un = socket_address.to_sockaddr_un().release_value();
TRY(System::bind(socket_fd, reinterpret_cast<sockaddr*>(&socket_address_un), sizeof(socket_address_un)));
TRY(System::listen(socket_fd, 16));
return socket_fd;
}
ErrorOr<IPCProcess::ProcessPaths> IPCProcess::paths_for_process(StringView process_name)
{
auto runtime_directory = TRY(StandardPaths::runtime_directory());
auto socket_path = ByteString::formatted("{}/{}.socket", runtime_directory, process_name);
auto pid_path = ByteString::formatted("{}/{}.pid", runtime_directory, process_name);
return ProcessPaths { move(socket_path), move(pid_path) };
}
}

View file

@ -13,7 +13,6 @@
#include <AK/Forward.h>
#include <AK/Span.h>
#include <LibCore/File.h>
#include <LibCore/Socket.h>
namespace Core {
@ -45,8 +44,6 @@ struct ProcessSpawnOptions {
Vector<FileActionType> file_actions {};
};
class IPCProcess;
class Process {
AK_MAKE_NONCOPYABLE(Process);
@ -102,8 +99,6 @@ public:
ErrorOr<bool> wait_for_termination();
private:
friend IPCProcess;
Process(pid_t pid)
: m_pid(pid)
, m_should_disown(true)
@ -114,41 +109,4 @@ private:
bool m_should_disown;
};
class IPCProcess {
public:
template<typename ClientType>
struct ProcessAndIPCClient {
Process process;
NonnullRefPtr<ClientType> client;
};
template<typename ClientType, typename... ClientArguments>
static ErrorOr<ProcessAndIPCClient<ClientType>> spawn(ProcessSpawnOptions const& options, ClientArguments&&... client_arguments)
{
auto [process, socket] = TRY(spawn_and_connect_to_process(options));
auto client = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ClientType { move(socket), forward<ClientArguments>(client_arguments)... }));
return ProcessAndIPCClient<ClientType> { move(process), move(client) };
}
struct ProcessPaths {
ByteString socket_path;
ByteString pid_path;
};
static ErrorOr<ProcessPaths> paths_for_process(StringView process_name);
static ErrorOr<Optional<pid_t>> get_process_pid(StringView process_name, StringView pid_path);
static ErrorOr<int> create_ipc_socket(ByteString const& socket_path);
pid_t pid() const { return m_process.pid(); }
private:
struct ProcessAndIPCSocket {
Process process;
NonnullOwnPtr<Core::LocalSocket> m_ipc_socket;
};
static ErrorOr<ProcessAndIPCSocket> spawn_and_connect_to_process(ProcessSpawnOptions const& options);
Process m_process;
};
}

View file

@ -29,9 +29,9 @@ ErrorOr<ChromeProcess::ProcessDisposition> ChromeProcess::connect(Vector<ByteStr
{
static constexpr auto process_name = "Ladybird"sv;
auto [socket_path, pid_path] = TRY(Core::IPCProcess::paths_for_process(process_name));
auto [socket_path, pid_path] = TRY(Process::paths_for_process(process_name));
if (auto pid = TRY(Core::IPCProcess::get_process_pid(process_name, pid_path)); pid.has_value()) {
if (auto pid = TRY(Process::get_process_pid(process_name, pid_path)); pid.has_value()) {
TRY(connect_as_client(socket_path, raw_urls, new_window));
return ProcessDisposition::ExitProcess;
}
@ -63,7 +63,7 @@ ErrorOr<void> ChromeProcess::connect_as_client(ByteString const& socket_path, Ve
ErrorOr<void> ChromeProcess::connect_as_server(ByteString const& socket_path)
{
auto socket_fd = TRY(Core::IPCProcess::create_ipc_socket(socket_path));
auto socket_fd = TRY(Process::create_ipc_socket(socket_path));
m_socket_path = socket_path;
auto local_server = TRY(Core::LocalServer::try_create());
TRY(local_server->take_over_fd(socket_fd));

View file

@ -4,7 +4,9 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCore/Environment.h>
#include <LibCore/Process.h>
#include <LibCore/StandardPaths.h>
#include <LibWebView/Process.h>
namespace WebView {
@ -22,4 +24,101 @@ Process::~Process()
m_connection->shutdown();
}
ErrorOr<Process::ProcessAndIPCSocket> Process::spawn_and_connect_to_process(Core::ProcessSpawnOptions const& options)
{
int socket_fds[2] {};
TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
ArmedScopeGuard guard_fd_0 { [&] { MUST(Core::System::close(socket_fds[0])); } };
ArmedScopeGuard guard_fd_1 { [&] { MUST(Core::System::close(socket_fds[1])); } };
auto& file_actions = const_cast<Core::ProcessSpawnOptions&>(options).file_actions;
file_actions.append(Core::FileAction::CloseFile { socket_fds[0] });
auto takeover_string = MUST(String::formatted("{}:{}", options.name, socket_fds[1]));
TRY(Core::Environment::set("SOCKET_TAKEOVER"sv, takeover_string, Core::Environment::Overwrite::Yes));
auto process = TRY(Core::Process::spawn(options));
auto ipc_socket = TRY(Core::LocalSocket::adopt_fd(socket_fds[0]));
guard_fd_0.disarm();
TRY(ipc_socket->set_blocking(true));
return ProcessAndIPCSocket { move(process), move(ipc_socket) };
}
ErrorOr<Optional<pid_t>> Process::get_process_pid(StringView process_name, StringView pid_path)
{
if (Core::System::stat(pid_path).is_error())
return OptionalNone {};
Optional<pid_t> pid;
{
auto pid_file = Core::File::open(pid_path, Core::File::OpenMode::Read);
if (pid_file.is_error()) {
warnln("Could not open {} PID file '{}': {}", process_name, pid_path, pid_file.error());
return pid_file.release_error();
}
auto contents = pid_file.value()->read_until_eof();
if (contents.is_error()) {
warnln("Could not read {} PID file '{}': {}", process_name, pid_path, contents.error());
return contents.release_error();
}
pid = StringView { contents.value() }.to_number<pid_t>();
}
if (!pid.has_value()) {
warnln("{} PID file '{}' exists, but with an invalid PID", process_name, pid_path);
TRY(Core::System::unlink(pid_path));
return OptionalNone {};
}
if (kill(*pid, 0) < 0) {
warnln("{} PID file '{}' exists with PID {}, but process cannot be found", process_name, pid_path, *pid);
TRY(Core::System::unlink(pid_path));
return OptionalNone {};
}
return pid;
}
// This is heavily based on how SystemServer's Service creates its socket.
ErrorOr<int> Process::create_ipc_socket(ByteString const& socket_path)
{
if (!Core::System::stat(socket_path).is_error())
TRY(Core::System::unlink(socket_path));
#ifdef SOCK_NONBLOCK
auto socket_fd = TRY(Core::System::socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0));
#else
auto socket_fd = TRY(Core::System::socket(AF_LOCAL, SOCK_STREAM, 0));
int option = 1;
TRY(Core::System::ioctl(socket_fd, FIONBIO, &option));
TRY(Core::System::fcntl(socket_fd, F_SETFD, FD_CLOEXEC));
#endif
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_GNU_HURD)
TRY(Core::System::fchmod(socket_fd, 0600));
#endif
auto socket_address = Core::SocketAddress::local(socket_path);
auto socket_address_un = socket_address.to_sockaddr_un().release_value();
TRY(Core::System::bind(socket_fd, reinterpret_cast<sockaddr*>(&socket_address_un), sizeof(socket_address_un)));
TRY(Core::System::listen(socket_fd, 16));
return socket_fd;
}
ErrorOr<Process::ProcessPaths> Process::paths_for_process(StringView process_name)
{
auto runtime_directory = TRY(Core::StandardPaths::runtime_directory());
auto socket_path = ByteString::formatted("{}/{}.socket", runtime_directory, process_name);
auto pid_path = ByteString::formatted("{}/{}.pid", runtime_directory, process_name);
return ProcessPaths { move(socket_path), move(pid_path) };
}
}

View file

@ -9,6 +9,7 @@
#include <AK/String.h>
#include <AK/WeakPtr.h>
#include <LibCore/Process.h>
#include <LibCore/Socket.h>
#include <LibIPC/Connection.h>
#include <LibWebView/ProcessType.h>
@ -22,6 +23,12 @@ public:
Process(ProcessType type, RefPtr<IPC::ConnectionBase> connection, Core::Process process);
~Process();
template<typename ClientType>
struct ProcessAndClient;
template<typename ClientType, typename... ClientArguments>
static ErrorOr<ProcessAndClient<ClientType>> spawn(ProcessType type, Core::ProcessSpawnOptions const& options, ClientArguments&&... client_arguments);
ProcessType type() const { return m_type; }
Optional<String> const& title() const { return m_title; }
void set_title(Optional<String> title) { m_title = move(title); }
@ -36,11 +43,40 @@ public:
pid_t pid() const { return m_process.pid(); }
struct ProcessPaths {
ByteString socket_path;
ByteString pid_path;
};
static ErrorOr<ProcessPaths> paths_for_process(StringView process_name);
static ErrorOr<Optional<pid_t>> get_process_pid(StringView process_name, StringView pid_path);
static ErrorOr<int> create_ipc_socket(ByteString const& socket_path);
private:
struct ProcessAndIPCSocket {
Core::Process process;
NonnullOwnPtr<Core::LocalSocket> socket;
};
static ErrorOr<ProcessAndIPCSocket> spawn_and_connect_to_process(Core::ProcessSpawnOptions const& options);
Core::Process m_process;
ProcessType m_type;
Optional<String> m_title;
WeakPtr<IPC::ConnectionBase> m_connection;
};
template<typename ClientType>
struct Process::ProcessAndClient {
Process process;
NonnullRefPtr<ClientType> client;
};
template<typename ClientType, typename... ClientArguments>
ErrorOr<Process::ProcessAndClient<ClientType>> Process::spawn(ProcessType type, Core::ProcessSpawnOptions const& options, ClientArguments&&... client_arguments)
{
auto [core_process, socket] = TRY(spawn_and_connect_to_process(options));
auto client = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ClientType { move(socket), forward<ClientArguments>(client_arguments)... }));
return ProcessAndClient { Process { type, client, move(core_process) }, client };
}
}