HelperProcess.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /*
  2. * Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "HelperProcess.h"
  7. #include "Utilities.h"
  8. #include <LibCore/Environment.h>
  9. #include <LibWebView/ProcessManager.h>
  10. ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
  11. WebView::ViewImplementation& view,
  12. ReadonlySpan<ByteString> candidate_web_content_paths,
  13. Ladybird::WebContentOptions const& web_content_options,
  14. Optional<WebView::SocketPair> request_server_sockets)
  15. {
  16. int socket_fds[2] {};
  17. TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
  18. int ui_fd = socket_fds[0];
  19. int wc_fd = socket_fds[1];
  20. int fd_passing_socket_fds[2] {};
  21. TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, fd_passing_socket_fds));
  22. int ui_fd_passing_fd = fd_passing_socket_fds[0];
  23. int wc_fd_passing_fd = fd_passing_socket_fds[1];
  24. auto child_pid = TRY(Core::System::fork());
  25. if (child_pid == 0) {
  26. TRY(Core::System::close(ui_fd_passing_fd));
  27. TRY(Core::System::close(ui_fd));
  28. auto takeover_string = TRY(String::formatted("WebContent:{}", wc_fd));
  29. TRY(Core::Environment::set("SOCKET_TAKEOVER"sv, takeover_string, Core::Environment::Overwrite::Yes));
  30. auto webcontent_fd_passing_socket_string = TRY(String::number(wc_fd_passing_fd));
  31. ErrorOr<void> result;
  32. for (auto const& path : candidate_web_content_paths) {
  33. constexpr auto callgrind_prefix_length = 3;
  34. if (Core::System::access(path, X_OK).is_error())
  35. continue;
  36. auto arguments = Vector {
  37. "valgrind"sv,
  38. "--tool=callgrind"sv,
  39. "--instr-atstart=no"sv,
  40. path.view(),
  41. "--command-line"sv,
  42. web_content_options.command_line,
  43. "--executable-path"sv,
  44. web_content_options.executable_path,
  45. "--webcontent-fd-passing-socket"sv,
  46. webcontent_fd_passing_socket_string
  47. };
  48. if (web_content_options.enable_callgrind_profiling == Ladybird::EnableCallgrindProfiling::No)
  49. arguments.remove(0, callgrind_prefix_length);
  50. if (web_content_options.is_layout_test_mode == Ladybird::IsLayoutTestMode::Yes)
  51. arguments.append("--layout-test-mode"sv);
  52. if (web_content_options.use_lagom_networking == Ladybird::UseLagomNetworking::Yes)
  53. arguments.append("--use-lagom-networking"sv);
  54. if (web_content_options.enable_gpu_painting == Ladybird::EnableGPUPainting::Yes)
  55. arguments.append("--use-gpu-painting"sv);
  56. if (web_content_options.wait_for_debugger == Ladybird::WaitForDebugger::Yes)
  57. arguments.append("--wait-for-debugger"sv);
  58. if (web_content_options.log_all_js_exceptions == Ladybird::LogAllJSExceptions::Yes)
  59. arguments.append("--log-all-js-exceptions"sv);
  60. if (web_content_options.enable_idl_tracing == Ladybird::EnableIDLTracing::Yes)
  61. arguments.append("--enable-idl-tracing"sv);
  62. if (auto server = mach_server_name(); server.has_value()) {
  63. arguments.append("--mach-server-name"sv);
  64. arguments.append(server.value());
  65. }
  66. Vector<String> fd_strings;
  67. if (request_server_sockets.has_value()) {
  68. arguments.append("--request-server-socket"sv);
  69. fd_strings.append(MUST(String::number(request_server_sockets->socket.fd())));
  70. arguments.append(fd_strings.last());
  71. arguments.append("--request-server-fd-passing-socket"sv);
  72. fd_strings.append(MUST(String::number(request_server_sockets->fd_passing_socket.fd())));
  73. arguments.append(fd_strings.last());
  74. }
  75. result = Core::System::exec(arguments[0], arguments.span(), Core::System::SearchInPath::Yes);
  76. if (!result.is_error())
  77. break;
  78. }
  79. if (result.is_error())
  80. warnln("Could not launch any of {}: {}", candidate_web_content_paths, result.error());
  81. VERIFY_NOT_REACHED();
  82. }
  83. TRY(Core::System::close(wc_fd_passing_fd));
  84. TRY(Core::System::close(wc_fd));
  85. auto socket = TRY(Core::LocalSocket::adopt_fd(ui_fd));
  86. TRY(socket->set_blocking(true));
  87. auto new_client = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) WebView::WebContentClient(move(socket), view)));
  88. new_client->set_fd_passing_socket(TRY(Core::LocalSocket::adopt_fd(ui_fd_passing_fd)));
  89. if (web_content_options.enable_callgrind_profiling == Ladybird::EnableCallgrindProfiling::Yes) {
  90. dbgln();
  91. dbgln("\033[1;45mLaunched WebContent process under callgrind!\033[0m");
  92. dbgln("\033[100mRun `\033[4mcallgrind_control -i on\033[24m` to start instrumentation and `\033[4mcallgrind_control -i off\033[24m` stop it again.\033[0m");
  93. dbgln();
  94. }
  95. return new_client;
  96. }
  97. template<typename Client>
  98. ErrorOr<NonnullRefPtr<Client>> launch_generic_server_process(ReadonlySpan<ByteString> candidate_server_paths, StringView server_name, Vector<StringView> extra_arguments = {})
  99. {
  100. int socket_fds[2] {};
  101. TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
  102. int ui_fd = socket_fds[0];
  103. int server_fd = socket_fds[1];
  104. int fd_passing_socket_fds[2] {};
  105. TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, fd_passing_socket_fds));
  106. int ui_fd_passing_fd = fd_passing_socket_fds[0];
  107. int server_fd_passing_fd = fd_passing_socket_fds[1];
  108. auto child_pid = TRY(Core::System::fork());
  109. if (child_pid == 0) {
  110. TRY(Core::System::close(ui_fd));
  111. TRY(Core::System::close(ui_fd_passing_fd));
  112. auto takeover_string = TRY(String::formatted("{}:{}", server_name, server_fd));
  113. TRY(Core::Environment::set("SOCKET_TAKEOVER"sv, takeover_string, Core::Environment::Overwrite::Yes));
  114. auto fd_passing_socket_string = TRY(String::number(server_fd_passing_fd));
  115. ErrorOr<void> result;
  116. for (auto const& path : candidate_server_paths) {
  117. if (Core::System::access(path, X_OK).is_error())
  118. continue;
  119. auto arguments = Vector<StringView> {
  120. path.view(),
  121. "--fd-passing-socket"sv,
  122. fd_passing_socket_string,
  123. };
  124. if (!extra_arguments.is_empty())
  125. arguments.extend(extra_arguments);
  126. result = Core::System::exec(arguments[0], arguments.span(), Core::System::SearchInPath::Yes);
  127. if (!result.is_error())
  128. break;
  129. }
  130. if (result.is_error())
  131. warnln("Could not launch any of {}: {}", candidate_server_paths, result.error());
  132. VERIFY_NOT_REACHED();
  133. }
  134. TRY(Core::System::close(server_fd));
  135. TRY(Core::System::close(server_fd_passing_fd));
  136. auto socket = TRY(Core::LocalSocket::adopt_fd(ui_fd));
  137. TRY(socket->set_blocking(true));
  138. auto new_client = TRY(try_make_ref_counted<Client>(move(socket)));
  139. new_client->set_fd_passing_socket(TRY(Core::LocalSocket::adopt_fd(ui_fd_passing_fd)));
  140. WebView::ProcessManager::the().add_process(WebView::process_type_from_name(server_name), child_pid);
  141. return new_client;
  142. }
  143. ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process(ReadonlySpan<ByteString> candidate_image_decoder_paths)
  144. {
  145. return launch_generic_server_process<ImageDecoderClient::Client>(candidate_image_decoder_paths, "ImageDecoder"sv);
  146. }
  147. ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(ReadonlySpan<ByteString> candidate_web_worker_paths, NonnullRefPtr<Protocol::RequestClient> request_client)
  148. {
  149. auto request_server_sockets = TRY(connect_new_request_server_client(move(request_client)));
  150. Vector<StringView> arguments;
  151. Vector<String> fd_strings;
  152. arguments.append("--request-server-socket"sv);
  153. fd_strings.append(MUST(String::number(request_server_sockets.socket.fd())));
  154. arguments.append(fd_strings.last());
  155. arguments.append("--request-server-fd-passing-socket"sv);
  156. fd_strings.append(MUST(String::number(request_server_sockets.fd_passing_socket.fd())));
  157. arguments.append(fd_strings.last());
  158. return launch_generic_server_process<Web::HTML::WebWorkerClient>(candidate_web_worker_paths, "WebWorker"sv, move(arguments));
  159. }
  160. ErrorOr<NonnullRefPtr<Protocol::RequestClient>> launch_request_server_process(ReadonlySpan<ByteString> candidate_request_server_paths, StringView serenity_resource_root, Vector<ByteString> const& certificates)
  161. {
  162. Vector<StringView> arguments;
  163. if (!serenity_resource_root.is_empty()) {
  164. arguments.append("--serenity-resource-root"sv);
  165. arguments.append(serenity_resource_root);
  166. }
  167. Vector<ByteString> certificate_args;
  168. for (auto const& certificate : certificates) {
  169. certificate_args.append(ByteString::formatted("--certificate={}", certificate));
  170. arguments.append(certificate_args.last().view());
  171. }
  172. return launch_generic_server_process<Protocol::RequestClient>(candidate_request_server_paths, "RequestServer"sv, move(arguments));
  173. }
  174. ErrorOr<WebView::SocketPair> connect_new_request_server_client(Protocol::RequestClient& client)
  175. {
  176. auto new_sockets = client.send_sync_but_allow_failure<Messages::RequestServer::ConnectNewClient>();
  177. if (!new_sockets)
  178. return Error::from_string_literal("Failed to connect to RequestServer");
  179. auto socket = new_sockets->take_client_socket();
  180. auto fd_passing_socket = new_sockets->take_client_fd_passing_socket();
  181. // FIXME: IPC::Files transferred over the wire are always set O_CLOEXEC during decoding.
  182. // Perhaps we should add an option to IPC::File to allow the receiver to decide whether to
  183. // make it O_CLOEXEC or not. Or an attribute in the .ipc file?
  184. for (auto fd : { socket.fd(), fd_passing_socket.fd() }) {
  185. auto fd_flags = MUST(Core::System::fcntl(fd, F_GETFD));
  186. fd_flags &= ~FD_CLOEXEC;
  187. MUST(Core::System::fcntl(fd, F_SETFD, fd_flags));
  188. }
  189. return WebView::SocketPair { move(socket), move(fd_passing_socket) };
  190. }