2 * Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "anbox/cmds/launch.h"
19 #include "anbox/dbus/stub/application_manager.h"
20 #include "anbox/dbus/interface.h"
21 #include "anbox/ui/splash_screen.h"
22 #include "anbox/system_configuration.h"
23 #include "anbox/logger.h"
25 #include "core/posix/exec.h"
26 #include "core/posix/fork.h"
27 #include "core/posix/signal.h"
29 #include <boost/filesystem.hpp>
34 namespace fs = boost::filesystem;
37 constexpr unsigned int max_session_mgr_wait_attempts{10};
38 const std::chrono::seconds session_mgr_wait_interval{5};
39 constexpr unsigned int max_dbus_service_wait_attempts{10};
40 const std::chrono::seconds dbus_service_wait_interval{5};
42 static int redirect_to_null(int flags, int fd) {
44 if ((fd2 = open("/dev/null", flags)) < 0)
50 if (dup2(fd2, fd) < 0)
58 bool anbox::cmds::Launch::launch_session_manager() {
59 std::vector<std::string> args = {"session-manager"};
60 const auto should_force_software_rendering = utils::get_env_value("ANBOX_FORCE_SOFTWARE_RENDERING", "false");
61 if (should_force_software_rendering == "true")
62 args.push_back("--software-rendering");
64 std::map<std::string,std::string> env;
65 core::posix::this_process::env::for_each([&](const std::string &name, const std::string &value) {
66 env.insert({name, value});
69 const auto exe_path = utils::process_get_exe_path(::getpid());
70 if (!fs::exists(exe_path)) {
71 ERROR("Can't find correct anbox executable to run. Found %s but does not exist", exe_path);
76 auto flags = core::posix::StandardStream::empty;
77 auto child = core::posix::fork([&]() {
79 // We redirect all in/out/err to /dev/null as they can't be seen
80 // anywhere. All logging output will directly go to syslog as we
81 // will become a session leader below which will get us rid of a
82 // controlling terminal.
83 if (redirect_to_null(O_RDONLY, 0) < 0 ||
84 redirect_to_null(O_WRONLY, 1) < 0 ||
85 redirect_to_null(O_WRONLY, 2) < 0) {
86 ERROR("Failed to redirect stdout/stderr/stdin: %s", strerror(errno));
87 return core::posix::exit::Status::failure;
90 // As we forked one time already we're sure that our process is
91 // not the session leader anymore so we can safely become the
92 // new one and lead the process group.
94 ERROR("Failed to become new session leader: %s", strerror(errno));
95 return core::posix::exit::Status::failure;
100 if (chdir("/") < 0) {
101 ERROR("Failed to change current directory: %s", strerror(errno));
102 return core::posix::exit::Status::failure;
105 auto grandchild = core::posix::exec(exe_path, args, env, flags);
106 grandchild.dont_kill_on_cleanup();
107 return core::posix::exit::Status::success;
110 // We don't wait for the grandchild but the child as we use double forking
111 // here to break through the process hierarchy and make the grandchild a
112 // direct child of the init process so it keeps running on its own and
113 // indepent of our short living process here.
114 child.wait_for(core::posix::wait::Flags::untraced);
116 DEBUG("Started session manager, will now try to connect ..");
119 ERROR("Failed to start session manager instance");
125 bool anbox::cmds::Launch::try_launch_activity(const std::shared_ptr<dbus::stub::ApplicationManager> &stub) {
127 DEBUG("Sending launch intent %s to Android ..", intent_);
128 stub->launch(intent_, graphics::Rect::Invalid, stack_);
129 } catch (const std::exception &err) {
130 ERROR("Failed to launch activity: %s", err.what());
133 ERROR("Failed to launch activity");
140 anbox::cmds::Launch::Launch()
141 : CommandWithFlagsAndAction{
142 cli::Name{"launch"}, cli::Usage{"launch"},
143 cli::Description{"Launch an Activity by sending an intent"}} {
144 flag(cli::make_flag(cli::Name{"action"},
145 cli::Description{"Action of the intent"},
147 flag(cli::make_flag(cli::Name{"type"},
148 cli::Description{"MIME type for the intent"},
150 flag(cli::make_flag(cli::Name{"uri"},
151 cli::Description{"URI used as data within the intent"},
153 flag(cli::make_flag(cli::Name{"package"},
154 cli::Description{"Package the intent should go to"},
156 flag(cli::make_flag(cli::Name{"component"},
157 cli::Description{"Component of a package the intent should go"},
159 flag(cli::make_flag(cli::Name{"stack"},
160 cli::Description{"Which window stack the activity should be started on. Possible: default, fullscreen, freeform"},
162 flag(cli::make_flag(cli::Name{"use-system-dbus"},
163 cli::Description{"Use system instead of session DBus"},
167 action([this](const cli::Command::Context&) {
168 if (!intent_.valid()) {
169 ERROR("The intent you provided is invalid. Please provide a correct launch intent.");
170 ERROR("For example to launch the application manager, run:");
171 ERROR("$ anbox launch --package=org.anbox.appmgr --component=org.anbox.appmgr.AppViewActivity");
175 auto bus_type = anbox::dbus::Bus::Type::Session;
176 if (use_system_dbus_)
177 bus_type = anbox::dbus::Bus::Type::System;
178 auto bus = std::make_shared<anbox::dbus::Bus>(bus_type);
180 std::shared_ptr<ui::SplashScreen> ss;
181 if (!bus->has_service_with_name(dbus::interface::Service::name())) {
182 DEBUG("Session manager is not yet running, trying to start it");
184 if (!launch_session_manager())
187 // Give us a splash screen as long as we're trying to connect
188 // with the session manager so the user knows something is
189 // happening after he started Anbox.
190 ss = std::make_shared<ui::SplashScreen>();
194 while (n < max_dbus_service_wait_attempts) {
195 if (bus->has_service_with_name(dbus::interface::Service::name()))
198 std::this_thread::sleep_for(dbus_service_wait_interval);
202 auto app_mgr = dbus::stub::ApplicationManager::create_for_bus(bus);
204 while (n < max_session_mgr_wait_attempts) {
205 app_mgr->update_properties();
206 if (app_mgr->ready().get())
209 std::this_thread::sleep_for(session_mgr_wait_interval);
213 if (!app_mgr->ready()) {
214 ERROR("Session manager failed to become ready");
218 // If we have a splash screen now is the time to drop it as we're
219 // going to launch the real application now.
222 const auto success = try_launch_activity(app_mgr);
223 return success ? EXIT_SUCCESS : EXIT_FAILURE;