Serenity Operating System
at master 139 lines 4.2 kB view raw
1/* 2 * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#define AK_DONT_REPLACE_STD 8 9#include "../HelperProcess.h" 10#include "../Utilities.h" 11#include <AK/Platform.h> 12#include <LibCore/ArgsParser.h> 13#include <LibCore/Directory.h> 14#include <LibCore/EventLoop.h> 15#include <LibCore/StandardPaths.h> 16#include <LibCore/System.h> 17#include <LibCore/TCPServer.h> 18#include <LibMain/Main.h> 19#include <QCoreApplication> 20#include <WebDriver/Client.h> 21 22#if defined(AK_OS_MACOS) 23# include <crt_externs.h> 24#endif 25 26extern DeprecatedString s_serenity_resource_root; 27 28static char** environment() 29{ 30#if defined(AK_OS_MACOS) 31 return *_NSGetEnviron(); 32#else 33 extern char** environ; 34 return environ; 35#endif 36} 37 38static ErrorOr<pid_t> launch_process(StringView application, char const* argv[]) 39{ 40 auto paths = TRY(get_paths_for_helper_process(application)); 41 42 ErrorOr<pid_t> result = -1; 43 for (auto const& path : paths) { 44 auto path_view = path.bytes_as_string_view(); 45 argv[0] = path_view.characters_without_null_termination(); 46 result = Core::System::posix_spawn(path_view, nullptr, nullptr, const_cast<char**>(argv), environment()); 47 if (!result.is_error()) 48 break; 49 } 50 return result; 51} 52 53static ErrorOr<pid_t> launch_browser(DeprecatedString const& socket_path) 54{ 55 char const* argv[] = { 56 "ladybird", 57 "--webdriver-content-path", 58 socket_path.characters(), 59 nullptr, 60 }; 61 62 return launch_process("ladybird"sv, argv); 63} 64 65static ErrorOr<pid_t> launch_headless_browser(DeprecatedString const& socket_path) 66{ 67 auto resources = DeprecatedString::formatted("{}/res", s_serenity_resource_root); 68 69 char const* argv[] = { 70 "headless-browser", 71 "--resources", 72 resources.characters(), 73 "--webdriver-ipc-path", 74 socket_path.characters(), 75 "about:blank", 76 nullptr, 77 }; 78 79 return launch_process("headless-browser"sv, argv); 80} 81 82ErrorOr<int> serenity_main(Main::Arguments arguments) 83{ 84 // Note: only creating this to get access to its static methods in HelperProcess 85 QCoreApplication application(arguments.argc, arguments.argv); 86 87 auto listen_address = "0.0.0.0"sv; 88 int port = 8000; 89 90 Core::ArgsParser args_parser; 91 args_parser.add_option(listen_address, "IP address to listen on", "listen-address", 'l', "listen_address"); 92 args_parser.add_option(port, "Port to listen on", "port", 'p', "port"); 93 args_parser.parse(arguments); 94 95 auto ipv4_address = IPv4Address::from_string(listen_address); 96 if (!ipv4_address.has_value()) { 97 warnln("Invalid listen address: {}", listen_address); 98 return 1; 99 } 100 101 if ((u16)port != port) { 102 warnln("Invalid port number: {}", port); 103 return 1; 104 } 105 106 platform_init(); 107 108 auto webdriver_socket_path = DeprecatedString::formatted("{}/webdriver", TRY(Core::StandardPaths::runtime_directory())); 109 TRY(Core::Directory::create(webdriver_socket_path, Core::Directory::CreateDirectories::Yes)); 110 111 Core::EventLoop loop; 112 auto server = TRY(Core::TCPServer::try_create()); 113 114 // FIXME: Propagate errors 115 server->on_ready_to_accept = [&] { 116 auto maybe_client_socket = server->accept(); 117 if (maybe_client_socket.is_error()) { 118 warnln("Failed to accept the client: {}", maybe_client_socket.error()); 119 return; 120 } 121 122 auto maybe_buffered_socket = Core::BufferedTCPSocket::create(maybe_client_socket.release_value()); 123 if (maybe_buffered_socket.is_error()) { 124 warnln("Could not obtain a buffered socket for the client: {}", maybe_buffered_socket.error()); 125 return; 126 } 127 128 auto maybe_client = WebDriver::Client::try_create(maybe_buffered_socket.release_value(), { launch_browser, launch_headless_browser }, server); 129 if (maybe_client.is_error()) { 130 warnln("Could not create a WebDriver client: {}", maybe_client.error()); 131 return; 132 } 133 }; 134 135 TRY(server->listen(ipv4_address.value(), port, Core::TCPServer::AllowAddressReuse::Yes)); 136 outln("Listening on {}:{}", ipv4_address.value(), port); 137 138 return loop.exec(); 139}