diff --git a/Base/etc/SystemServer.ini b/Base/etc/SystemServer.ini index 6359cfe330f..cbb52efdfc8 100644 --- a/Base/etc/SystemServer.ini +++ b/Base/etc/SystemServer.ini @@ -33,6 +33,13 @@ Priority=low KeepAlive=1 User=notify +[LaunchServer] +Socket=/tmp/portal/launch +SocketPermissions=660 +Lazy=1 +KeepAlive=1 +User=anon + [WindowServer] Socket=/tmp/portal/window SocketPermissions=660 diff --git a/Base/home/anon/LaunchServer.ini b/Base/home/anon/LaunchServer.ini new file mode 100644 index 00000000000..100f0d0bd3c --- /dev/null +++ b/Base/home/anon/LaunchServer.ini @@ -0,0 +1,13 @@ +[FileType] +png=/bin/QuickShow +gif=/bin/QuickShow +html=/bin/Browser +wav=/bin/SoundPlayer +txt=/bin/TextEditor +frm=/bin/VisualBuilder +*=/bin/TextEditor + +[Protocol] +irc=/bin/IRCClient +http=/bin/Browser +*=/bin/Browser diff --git a/Kernel/build-root-filesystem.sh b/Kernel/build-root-filesystem.sh index b94de4b4c4b..50d54be7da9 100755 --- a/Kernel/build-root-filesystem.sh +++ b/Kernel/build-root-filesystem.sh @@ -169,6 +169,7 @@ cp ../Services/ProtocolServer/ProtocolServer mnt/bin/ProtocolServer cp ../Services/SystemMenu/SystemMenu mnt/bin/SystemMenu cp ../Services/NotificationServer/NotificationServer mnt/bin/NotificationServer cp ../Services/WebServer/WebServer mnt/bin/WebServer +cp ../Services/LaunchServer/LaunchServer mnt/bin/LaunchServer cp ../Shell/Shell mnt/bin/Shell cp ../MenuApplets/Audio/Audio.MenuApplet mnt/bin/ cp ../MenuApplets/ResourceGraph/ResourceGraph.MenuApplet mnt/bin/ diff --git a/Services/LaunchServer/ClientConnection.cpp b/Services/LaunchServer/ClientConnection.cpp new file mode 100644 index 00000000000..7a693006846 --- /dev/null +++ b/Services/LaunchServer/ClientConnection.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020, Nicholas Hollett + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ClientConnection.h" +#include "LaunchClientEndpoint.h" +#include "Launcher.h" +#include +#include + +namespace LaunchServer { + +static HashMap> s_connections; +ClientConnection::ClientConnection(Core::LocalSocket& client_socket, int client_id) + : IPC::ClientConnection(*this, client_socket, client_id) +{ + s_connections.set(client_id, *this); +} + +ClientConnection::~ClientConnection() +{ +} + +void ClientConnection::die() +{ + s_connections.remove(client_id()); +} + +OwnPtr ClientConnection::handle(const Messages::LaunchServer::Greet&) +{ + return make(client_id()); +} + +OwnPtr ClientConnection::handle(const Messages::LaunchServer::OpenUrl& request) +{ + URL url(request.url()); + auto result = Launcher::the().open_url(url); + return make(result); +} +} diff --git a/Services/LaunchServer/ClientConnection.h b/Services/LaunchServer/ClientConnection.h new file mode 100644 index 00000000000..bab60335737 --- /dev/null +++ b/Services/LaunchServer/ClientConnection.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Nicholas Hollett + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +namespace LaunchServer { + +class ClientConnection final : public IPC::ClientConnection + , public LaunchServerEndpoint { + C_OBJECT(ClientConnection) +public: + ~ClientConnection() override; + + virtual void die() override; + +private: + explicit ClientConnection(Core::LocalSocket&, int client_id); + + virtual OwnPtr handle(const Messages::LaunchServer::Greet&) override; + virtual OwnPtr handle(const Messages::LaunchServer::OpenUrl&) override; +}; +} diff --git a/Services/LaunchServer/LaunchClient.ipc b/Services/LaunchServer/LaunchClient.ipc new file mode 100644 index 00000000000..abd4e9ba3d4 --- /dev/null +++ b/Services/LaunchServer/LaunchClient.ipc @@ -0,0 +1,4 @@ +endpoint LaunchClient = 102 +{ + Dummy() =| +} diff --git a/Services/LaunchServer/LaunchServer.ipc b/Services/LaunchServer/LaunchServer.ipc new file mode 100644 index 00000000000..06e57b7b1a8 --- /dev/null +++ b/Services/LaunchServer/LaunchServer.ipc @@ -0,0 +1,6 @@ +endpoint LaunchServer = 101 +{ + Greet() => (i32 client_id) + + OpenUrl(String url) => (bool response) +} diff --git a/Services/LaunchServer/Launcher.cpp b/Services/LaunchServer/Launcher.cpp new file mode 100644 index 00000000000..14afddbf662 --- /dev/null +++ b/Services/LaunchServer/Launcher.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2020, Nicholas Hollett , Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Launcher.h" +#include +#include +#include + +namespace LaunchServer { + +static Launcher* s_the; +static bool spawn(String executable, String argument); + +Launcher::Launcher() +{ + ASSERT(s_the == nullptr); + s_the = this; +} + +Launcher& Launcher::the() +{ + ASSERT(s_the); + return *s_the; +} + +void Launcher::load_config(const Core::ConfigFile& cfg) +{ + for (auto key : cfg.keys("FileType")) { + m_file_handlers.set(key.to_lowercase(), cfg.read_entry("FileType", key)); + } + + for (auto key : cfg.keys("Protocol")) { + m_protocol_handlers.set(key.to_lowercase(), cfg.read_entry("Protocol", key)); + } +} + +bool Launcher::open_url(const URL& url) +{ + if (url.protocol() == "file") + return open_file_url(url); + + return open_with_handlers(m_protocol_handlers, url.protocol(), url.to_string(), "/bin/Browser"); +} + +bool spawn(String executable, String argument) +{ + pid_t child_pid = fork(); + if (child_pid < 0) { + perror("fork"); + return false; + } + if (child_pid == 0) { + if (execl(executable.characters(), executable.characters(), argument.characters(), nullptr) < 0) { + perror("execl"); + return false; + } + ASSERT_NOT_REACHED(); + } + return true; +} + +bool Launcher::open_with_handlers(const HashMap& handlers, const String key, const String argument, const String default_program) +{ + auto program_path = handlers.get(key); + if (program_path.has_value()) + return spawn(program_path.value(), argument); + + // There wasn't a handler for this, so try the fallback instead + program_path = handlers.get("*"); + if (program_path.has_value()) + return spawn(program_path.value(), argument); + + // Absolute worst case, try the provided default + + return spawn(default_program, argument); +} + +bool Launcher::open_file_url(const URL& url) +{ + struct stat st; + if (stat(url.path().characters(), &st) < 0) { + perror("stat"); + return false; + } + + // TODO: Make directory opening configurable + if (S_ISDIR(st.st_mode)) + return spawn("/bin/FileManager", url.path()); + + if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + return spawn(url.path(), {}); + + auto extension_parts = url.path().to_lowercase().split('.'); + String extension = {}; + if (extension_parts.size() > 1) + extension = extension_parts.last(); + return open_with_handlers(m_file_handlers, extension, url.path(), "/bin/TextEdit"); +} + +} diff --git a/Services/LaunchServer/Launcher.h b/Services/LaunchServer/Launcher.h new file mode 100644 index 00000000000..26ae697773f --- /dev/null +++ b/Services/LaunchServer/Launcher.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020, Nicholas Hollett + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include + +namespace LaunchServer { + +class Launcher { +public: + Launcher(); + static Launcher& the(); + + void load_config(const Core::ConfigFile&); + bool open_url(const URL&); + +private: + HashMap m_protocol_handlers; + HashMap m_file_handlers; + + bool open_file_url(const URL&); + bool open_with_handlers(const HashMap& handlers, const String key, const String argument, const String default_program); +}; +} diff --git a/Services/LaunchServer/Makefile b/Services/LaunchServer/Makefile new file mode 100644 index 00000000000..ef1eddce7db --- /dev/null +++ b/Services/LaunchServer/Makefile @@ -0,0 +1,24 @@ +OBJS = \ + main.o \ + ClientConnection.o \ + Launcher.o + +PROGRAM = LaunchServer + +LIB_DEPS = Core IPC + +EXTRA_CLEAN = LaunchServerEndpoint.h LaunchClientEndpoint.h + +*.cpp: LaunchServerEndpoint.h LaunchClientEndpoint.h + +LaunchServerEndpoint.h: LaunchServer.ipc | IPCCOMPILER + @echo "IPC $<"; $(IPCCOMPILER) $< > $@ + +LaunchClientEndpoint.h: LaunchClient.ipc | IPCCOMPILER + @echo "IPC $<"; $(IPCCOMPILER) $< > $@ + +install: + mkdir -p $(SERENITY_BASE_DIR)/Root/usr/include/LaunchServer/ + cp *.h $(SERENITY_BASE_DIR)/Root/usr/include/LaunchServer/ + +include ../../Makefile.common diff --git a/Services/LaunchServer/main.cpp b/Services/LaunchServer/main.cpp new file mode 100644 index 00000000000..af9234b3a16 --- /dev/null +++ b/Services/LaunchServer/main.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020, Nicholas Hollett + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ClientConnection.h" +#include "Launcher.h" +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + (void)argc; + (void)argv; + + Core::EventLoop event_loop; + auto server = Core::LocalServer::construct(); + + auto launcher = LaunchServer::Launcher(); + + launcher.load_config(Core::ConfigFile::get_for_app("LaunchServer")); + + if (pledge("stdio accept rpath proc exec", nullptr) < 0) { + perror("pledge"); + return 1; + } + + bool ok = server->take_over_from_system_server(); + ASSERT(ok); + server->on_ready_to_accept = [&] { + auto client_socket = server->accept(); + if (!client_socket) { + dbg() << "LaunchServer: accept failed."; + return; + } + static int s_next_client_id = 0; + int client_id = ++s_next_client_id; + dbg() << "Received connection"; + IPC::new_client_connection(*client_socket, client_id); + }; + + return event_loop.exec(); +}