2023-02-02 10:00:30 +00:00
/*
* Copyright ( c ) 2023 , Andrew Kaster < akaster @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include "HelperProcess.h"
2024-04-04 20:13:14 +00:00
# include "Utilities.h"
2024-01-30 16:16:17 +00:00
# include <LibCore/Environment.h>
2024-04-22 16:31:29 +00:00
# include <LibCore/SingletonProcess.h>
2024-03-26 00:29:14 +00:00
# include <LibWebView/ProcessManager.h>
2023-02-02 10:00:30 +00:00
2023-12-01 17:18:40 +00:00
ErrorOr < NonnullRefPtr < WebView : : WebContentClient > > launch_web_content_process (
WebView : : ViewImplementation & view ,
2024-02-22 01:27:05 +00:00
ReadonlySpan < ByteString > candidate_web_content_paths ,
2024-04-15 23:39:48 +00:00
Ladybird : : WebContentOptions const & web_content_options ,
2024-04-18 00:44:39 +00:00
Optional < IPC : : File > request_server_socket )
2023-02-02 10:00:30 +00:00
{
2023-08-01 17:56:10 +00:00
int socket_fds [ 2 ] { } ;
TRY ( Core : : System : : socketpair ( AF_LOCAL , SOCK_STREAM , 0 , socket_fds ) ) ;
int ui_fd = socket_fds [ 0 ] ;
int wc_fd = socket_fds [ 1 ] ;
2024-03-26 00:29:14 +00:00
auto child_pid = TRY ( Core : : System : : fork ( ) ) ;
if ( child_pid = = 0 ) {
2023-08-01 17:56:10 +00:00
TRY ( Core : : System : : close ( ui_fd ) ) ;
auto takeover_string = TRY ( String : : formatted ( " WebContent:{} " , wc_fd ) ) ;
2024-01-30 16:16:17 +00:00
TRY ( Core : : Environment : : set ( " SOCKET_TAKEOVER " sv , takeover_string , Core : : Environment : : Overwrite : : Yes ) ) ;
2023-08-01 17:56:10 +00:00
ErrorOr < void > result ;
for ( auto const & path : candidate_web_content_paths ) {
constexpr auto callgrind_prefix_length = 3 ;
if ( Core : : System : : access ( path , X_OK ) . is_error ( ) )
continue ;
auto arguments = Vector {
" valgrind " sv ,
" --tool=callgrind " sv ,
" --instr-atstart=no " sv ,
2024-02-22 01:27:05 +00:00
path . view ( ) ,
2024-01-16 17:55:40 +00:00
" --command-line " sv ,
web_content_options . command_line ,
" --executable-path " sv ,
web_content_options . executable_path ,
2023-08-01 17:56:10 +00:00
} ;
2023-12-01 17:18:40 +00:00
if ( web_content_options . enable_callgrind_profiling = = Ladybird : : EnableCallgrindProfiling : : No )
2023-08-01 17:56:10 +00:00
arguments . remove ( 0 , callgrind_prefix_length ) ;
2023-12-01 17:18:40 +00:00
if ( web_content_options . is_layout_test_mode = = Ladybird : : IsLayoutTestMode : : Yes )
2023-08-01 17:56:10 +00:00
arguments . append ( " --layout-test-mode " sv ) ;
2023-12-01 17:18:40 +00:00
if ( web_content_options . use_lagom_networking = = Ladybird : : UseLagomNetworking : : Yes )
2023-08-01 20:39:19 +00:00
arguments . append ( " --use-lagom-networking " sv ) ;
2023-12-01 17:18:40 +00:00
if ( web_content_options . enable_gpu_painting = = Ladybird : : EnableGPUPainting : : Yes )
2023-10-27 15:28:18 +00:00
arguments . append ( " --use-gpu-painting " sv ) ;
2023-12-20 16:52:17 +00:00
if ( web_content_options . wait_for_debugger = = Ladybird : : WaitForDebugger : : Yes )
arguments . append ( " --wait-for-debugger " sv ) ;
2024-04-16 06:02:41 +00:00
if ( web_content_options . log_all_js_exceptions = = Ladybird : : LogAllJSExceptions : : Yes )
arguments . append ( " --log-all-js-exceptions " sv ) ;
2024-04-16 12:39:57 +00:00
if ( web_content_options . enable_idl_tracing = = Ladybird : : EnableIDLTracing : : Yes )
arguments . append ( " --enable-idl-tracing " sv ) ;
2024-04-20 20:46:38 +00:00
if ( web_content_options . expose_internals_object = = Ladybird : : ExposeInternalsObject : : Yes )
arguments . append ( " --expose-internals-object " sv ) ;
2024-04-04 20:13:14 +00:00
if ( auto server = mach_server_name ( ) ; server . has_value ( ) ) {
arguments . append ( " --mach-server-name " sv ) ;
arguments . append ( server . value ( ) ) ;
}
2024-04-18 00:44:39 +00:00
String fd_string ;
if ( request_server_socket . has_value ( ) ) {
2024-04-15 23:39:48 +00:00
arguments . append ( " --request-server-socket " sv ) ;
2024-04-18 00:44:39 +00:00
fd_string = MUST ( String : : number ( request_server_socket - > fd ( ) ) ) ;
arguments . append ( fd_string . bytes_as_string_view ( ) ) ;
2024-02-06 15:25:22 +00:00
}
2023-08-01 17:56:10 +00:00
result = Core : : System : : exec ( arguments [ 0 ] , arguments . span ( ) , Core : : System : : SearchInPath : : Yes ) ;
if ( ! result . is_error ( ) )
break ;
}
if ( result . is_error ( ) )
warnln ( " Could not launch any of {}: {} " , candidate_web_content_paths , result . error ( ) ) ;
VERIFY_NOT_REACHED ( ) ;
}
TRY ( Core : : System : : close ( wc_fd ) ) ;
auto socket = TRY ( Core : : LocalSocket : : adopt_fd ( ui_fd ) ) ;
TRY ( socket - > set_blocking ( true ) ) ;
auto new_client = TRY ( adopt_nonnull_ref_or_enomem ( new ( nothrow ) WebView : : WebContentClient ( move ( socket ) , view ) ) ) ;
2023-12-01 17:18:40 +00:00
if ( web_content_options . enable_callgrind_profiling = = Ladybird : : EnableCallgrindProfiling : : Yes ) {
2023-08-01 17:56:10 +00:00
dbgln ( ) ;
dbgln ( " \033 [1;45mLaunched WebContent process under callgrind! \033 [0m " ) ;
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 " ) ;
dbgln ( ) ;
}
return new_client ;
2023-02-02 10:00:30 +00:00
}
2023-08-01 20:39:19 +00:00
2023-08-03 00:13:23 +00:00
template < typename Client >
2024-04-15 23:39:48 +00:00
ErrorOr < NonnullRefPtr < Client > > launch_generic_server_process ( ReadonlySpan < ByteString > candidate_server_paths , StringView server_name , Vector < StringView > extra_arguments = { } )
2023-08-01 20:39:19 +00:00
{
int socket_fds [ 2 ] { } ;
TRY ( Core : : System : : socketpair ( AF_LOCAL , SOCK_STREAM , 0 , socket_fds ) ) ;
int ui_fd = socket_fds [ 0 ] ;
2023-08-03 00:13:23 +00:00
int server_fd = socket_fds [ 1 ] ;
2023-08-01 20:39:19 +00:00
2024-03-26 00:29:14 +00:00
auto child_pid = TRY ( Core : : System : : fork ( ) ) ;
if ( child_pid = = 0 ) {
2023-08-01 20:39:19 +00:00
TRY ( Core : : System : : close ( ui_fd ) ) ;
2023-08-03 00:13:23 +00:00
auto takeover_string = TRY ( String : : formatted ( " {}:{} " , server_name , server_fd ) ) ;
2024-01-30 16:16:17 +00:00
TRY ( Core : : Environment : : set ( " SOCKET_TAKEOVER " sv , takeover_string , Core : : Environment : : Overwrite : : Yes ) ) ;
2023-08-01 20:39:19 +00:00
ErrorOr < void > result ;
2023-08-03 00:13:23 +00:00
for ( auto const & path : candidate_server_paths ) {
2023-08-01 20:39:19 +00:00
if ( Core : : System : : access ( path , X_OK ) . is_error ( ) )
continue ;
2024-04-15 23:39:48 +00:00
auto arguments = Vector < StringView > {
2024-02-22 01:27:05 +00:00
path . view ( ) ,
2023-08-01 20:39:19 +00:00
} ;
2024-04-15 23:39:48 +00:00
if ( ! extra_arguments . is_empty ( ) )
arguments . extend ( extra_arguments ) ;
2023-08-01 20:39:19 +00:00
result = Core : : System : : exec ( arguments [ 0 ] , arguments . span ( ) , Core : : System : : SearchInPath : : Yes ) ;
if ( ! result . is_error ( ) )
break ;
}
if ( result . is_error ( ) )
2023-08-03 00:13:23 +00:00
warnln ( " Could not launch any of {}: {} " , candidate_server_paths , result . error ( ) ) ;
2023-08-01 20:39:19 +00:00
VERIFY_NOT_REACHED ( ) ;
}
2023-08-03 00:13:23 +00:00
TRY ( Core : : System : : close ( server_fd ) ) ;
2023-08-01 20:39:19 +00:00
auto socket = TRY ( Core : : LocalSocket : : adopt_fd ( ui_fd ) ) ;
TRY ( socket - > set_blocking ( true ) ) ;
2023-08-03 00:13:23 +00:00
auto new_client = TRY ( try_make_ref_counted < Client > ( move ( socket ) ) ) ;
2023-08-01 20:39:19 +00:00
2024-03-26 00:29:14 +00:00
WebView : : ProcessManager : : the ( ) . add_process ( WebView : : process_type_from_name ( server_name ) , child_pid ) ;
2023-08-01 20:39:19 +00:00
return new_client ;
}
2023-08-03 00:13:23 +00:00
2024-02-22 01:27:05 +00:00
ErrorOr < NonnullRefPtr < ImageDecoderClient : : Client > > launch_image_decoder_process ( ReadonlySpan < ByteString > candidate_image_decoder_paths )
2023-09-08 10:30:50 +00:00
{
2024-04-15 23:39:48 +00:00
return launch_generic_server_process < ImageDecoderClient : : Client > ( candidate_image_decoder_paths , " ImageDecoder " sv ) ;
2023-09-08 10:30:50 +00:00
}
2024-04-15 23:39:48 +00:00
ErrorOr < NonnullRefPtr < Web : : HTML : : WebWorkerClient > > launch_web_worker_process ( ReadonlySpan < ByteString > candidate_web_worker_paths , NonnullRefPtr < Protocol : : RequestClient > request_client )
2024-01-06 20:13:59 +00:00
{
2024-04-18 00:44:39 +00:00
auto socket = TRY ( connect_new_request_server_client ( move ( request_client ) ) ) ;
2024-04-15 23:39:48 +00:00
Vector < StringView > arguments ;
2024-04-18 00:44:39 +00:00
String fd_string = MUST ( String : : number ( socket . fd ( ) ) ) ;
2024-04-15 23:39:48 +00:00
arguments . append ( " --request-server-socket " sv ) ;
2024-04-18 00:44:39 +00:00
arguments . append ( fd_string . bytes_as_string_view ( ) ) ;
2024-04-15 23:39:48 +00:00
return launch_generic_server_process < Web : : HTML : : WebWorkerClient > ( candidate_web_worker_paths , " WebWorker " sv , move ( arguments ) ) ;
2024-01-06 20:13:59 +00:00
}
2024-02-22 01:27:05 +00:00
ErrorOr < NonnullRefPtr < Protocol : : RequestClient > > launch_request_server_process ( ReadonlySpan < ByteString > candidate_request_server_paths , StringView serenity_resource_root , Vector < ByteString > const & certificates )
2023-08-03 00:13:23 +00:00
{
2024-04-15 23:39:48 +00:00
Vector < StringView > arguments ;
if ( ! serenity_resource_root . is_empty ( ) ) {
arguments . append ( " --serenity-resource-root " sv ) ;
arguments . append ( serenity_resource_root ) ;
}
Vector < ByteString > certificate_args ;
for ( auto const & certificate : certificates ) {
certificate_args . append ( ByteString : : formatted ( " --certificate={} " , certificate ) ) ;
arguments . append ( certificate_args . last ( ) . view ( ) ) ;
}
return launch_generic_server_process < Protocol : : RequestClient > ( candidate_request_server_paths , " RequestServer " sv , move ( arguments ) ) ;
}
2024-04-22 16:31:29 +00:00
ErrorOr < NonnullRefPtr < SQL : : SQLClient > > launch_sql_server_process ( ReadonlySpan < ByteString > candidate_sql_server_paths )
{
auto [ client , _ ] = TRY ( Core : : launch_singleton_process < SQL : : SQLClient > ( " SQLServer " sv , candidate_sql_server_paths ) ) ;
return client ;
}
2024-04-18 00:44:39 +00:00
ErrorOr < IPC : : File > connect_new_request_server_client ( Protocol : : RequestClient & client )
2024-04-15 23:39:48 +00:00
{
2024-04-18 00:44:39 +00:00
auto new_socket = client . send_sync_but_allow_failure < Messages : : RequestServer : : ConnectNewClient > ( ) ;
if ( ! new_socket )
2024-04-15 23:39:48 +00:00
return Error : : from_string_literal ( " Failed to connect to RequestServer " ) ;
2024-04-18 00:44:39 +00:00
auto socket = new_socket - > take_client_socket ( ) ;
2024-04-15 23:39:48 +00:00
// FIXME: IPC::Files transferred over the wire are always set O_CLOEXEC during decoding.
// Perhaps we should add an option to IPC::File to allow the receiver to decide whether to
// make it O_CLOEXEC or not. Or an attribute in the .ipc file?
2024-04-18 00:44:39 +00:00
auto fd = socket . fd ( ) ;
auto fd_flags = MUST ( Core : : System : : fcntl ( fd , F_GETFD ) ) ;
fd_flags & = ~ FD_CLOEXEC ;
MUST ( Core : : System : : fcntl ( fd , F_SETFD , fd_flags ) ) ;
2024-04-15 23:39:48 +00:00
2024-04-18 00:44:39 +00:00
return socket ;
2023-08-03 00:13:23 +00:00
}