HelperProcess.cpp 8.6 KB

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