TYPE3
[iec.git] / src / type3_AndroidCloud / anbox-master / src / anbox / cmds / container_manager.cpp
1 /*
2  * Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
3  *
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.
7  *
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.
12  *
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/>.
15  *
16  */
17
18 #include "anbox/cmds/container_manager.h"
19 #include "anbox/container/service.h"
20 #include "anbox/common/loop_device_allocator.h"
21 #include "anbox/logger.h"
22 #include "anbox/runtime.h"
23 #include "anbox/system_configuration.h"
24
25 #include "core/posix/signal.h"
26 #include "core/posix/exec.h"
27
28 #include <sys/mount.h>
29 #include <linux/loop.h>
30 #include <fcntl.h>
31
32 namespace fs = boost::filesystem;
33
34 namespace {
35 constexpr unsigned int unprivileged_user_id{100000};
36 }
37
38 anbox::cmds::ContainerManager::ContainerManager()
39     : CommandWithFlagsAndAction{
40           cli::Name{"container-manager"}, cli::Usage{"container-manager"},
41           cli::Description{"Start the container manager service"}, true} {
42
43   flag(cli::make_flag(cli::Name{"android-image"},
44                       cli::Description{"Path to the Android rootfs image file if not stored in the data path"},
45                       android_img_path_));
46   flag(cli::make_flag(cli::Name{"data-path"},
47                       cli::Description{"Path where the container and its data is stored"},
48                       data_path_));
49   flag(cli::make_flag(cli::Name{"privileged"},
50                       cli::Description{"Run Android container in privileged mode"},
51                       privileged_));
52   flag(cli::make_flag(cli::Name{"daemon"},
53                       cli::Description{"Mark service as being started as systemd daemon"},
54                       daemon_));
55   flag(cli::make_flag(cli::Name{"use-rootfs-overlay"},
56                       cli::Description{"Use an overlay for the Android rootfs"},
57                       enable_rootfs_overlay_));
58   flag(cli::make_flag(cli::Name{"force-squashfuse"},
59                       cli::Description{"Force using squashfuse for mounting the Android rootfs"},
60                       enable_squashfuse_));
61   flag(cli::make_flag(cli::Name{"container-network-address"},
62                       cli::Description{"Assign the specified network address to the Android container"},
63                       container_network_address_));
64   flag(cli::make_flag(cli::Name{"container-network-gateway"},
65                       cli::Description{"Assign the specified network gateway to the Android container"},
66                       container_network_gateway_));
67   flag(cli::make_flag(cli::Name{"container-network-dns-servers"},
68                       cli::Description{"Assign the specified DNS servers to the Android container"},
69                       container_network_dns_servers_));
70
71   action([&](const cli::Command::Context&) {
72     try {
73       if (!daemon_) {
74         WARNING("You are running the container manager manually which is most likely not");
75         WARNING("what you want. The container manager is normally started by systemd or");
76         WARNING("another init system. If you still want to run the container-manager");
77         WARNING("you can get rid of this warning by starting with the --daemon option.");
78         WARNING("");
79       }
80
81       if (geteuid() != 0) {
82         ERROR("You are not running the container-manager as root. Generally you don't");
83         ERROR("want to run the container-manager manually unless you're a developer");
84         ERROR("as it is started by the init system of your operating system.");
85         return EXIT_FAILURE;
86       }
87
88       auto trap = core::posix::trap_signals_for_process(
89           {core::posix::Signal::sig_term, core::posix::Signal::sig_int});
90       trap->signal_raised().connect([trap](const core::posix::Signal& signal) {
91         INFO("Signal %i received. Good night.", static_cast<int>(signal));
92         trap->stop();
93       });
94
95       if (!data_path_.empty())
96         SystemConfiguration::instance().set_data_path(data_path_);
97
98       if (!fs::exists(data_path_))
99         fs::create_directories(data_path_);
100
101       if (!setup_mounts())
102         return EXIT_FAILURE;
103
104       auto rt = Runtime::create();
105       container::Service::Configuration config;
106       config.privileged = privileged_;
107       config.rootfs_overlay = enable_rootfs_overlay_;
108       config.container_network_address = container_network_address_;
109       config.container_network_gateway = container_network_gateway_;
110
111       if (container_network_dns_servers_.length() > 0)
112         config.container_network_dns_servers = utils::string_split(container_network_dns_servers_, ',');
113
114       auto service = container::Service::create(rt, config);
115
116       rt->start();
117       trap->run();
118       rt->stop();
119
120       return EXIT_SUCCESS;
121     } catch (std::exception &err) {
122       ERROR("%s", err.what());
123       return EXIT_FAILURE;
124     }
125   });
126 }
127
128 anbox::cmds::ContainerManager::~ContainerManager() {}
129
130 bool anbox::cmds::ContainerManager::setup_mounts() {
131   fs::path android_img_path = android_img_path_;
132   if (android_img_path.empty())
133     android_img_path = SystemConfiguration::instance().data_dir() / "android.img";
134
135   if (!fs::exists(android_img_path)) {
136     ERROR("Android image does not exist at path %s", android_img_path);
137     return false;
138   }
139
140   const auto android_rootfs_dir = SystemConfiguration::instance().rootfs_dir();
141   if (utils::is_mounted(android_rootfs_dir)) {
142     ERROR("Androd rootfs is already mounted!?");
143     return false;
144   }
145
146   if (!fs::exists(android_rootfs_dir))
147     fs::create_directory(android_rootfs_dir);
148
149   // We prefer using the kernel for mounting the squashfs image but
150   // for some cases (unprivileged containers) where no loop support
151   // is available we do the mount instead via squashfuse which will
152   // work entirely in userspace.
153   if (!fs::exists("/dev/loop-control") && !enable_squashfuse_) {
154       WARNING("/dev/loop-control not found. Falling back to squashfuse.");
155       enable_squashfuse_ = true;
156   }
157   if (!enable_squashfuse_) {
158     std::shared_ptr<common::LoopDevice> loop_device;
159
160     try {
161       loop_device = common::LoopDeviceAllocator::new_device();
162     } catch (const std::exception& e) {
163       ERROR("Could not create loopback device: %s", e.what());
164       return false;
165     } catch (...) {
166       ERROR("Could not create loopback device");
167       return false;
168     }
169
170     if (!loop_device->attach_file(android_img_path)) {
171       ERROR("Failed to attach Android rootfs image to loopback device");
172       return false;
173     }
174
175     auto m = common::MountEntry::create(loop_device, android_rootfs_dir, "squashfs", MS_MGC_VAL | MS_RDONLY | MS_PRIVATE);
176     if (!m) {
177       ERROR("Failed to mount Android rootfs");
178       return false;
179     }
180     mounts_.push_back(m);
181   } else if (fs::exists("/dev/fuse") && !utils::find_program_on_path("squashfuse").empty()) {
182     std::vector<std::string> args = {
183       "-t", "fuse.squashfuse",
184       // Allow other users than root to access the rootfs
185       "-o", "allow_other",
186       android_img_path.string(),
187       android_rootfs_dir,
188     };
189
190     // Easiest is here to go with the standard mount program as that
191     // will handle everything for us which is relevant to get the
192     // squashfs via squashfuse properly mount without having to
193     // reimplement all the details. Once the mount call comes back
194     // without an error we can expect the image to be mounted.
195     auto child = core::posix::exec("/bin/mount", args, {}, core::posix::StandardStream::empty, []() {});
196     const auto result = child.wait_for(core::posix::wait::Flags::untraced);
197     if (result.status != core::posix::wait::Result::Status::exited ||
198         result.detail.if_exited.status != core::posix::exit::Status::success) {
199       ERROR("Failed to mount squashfs Android image");
200       return false;
201     }
202
203     auto m = common::MountEntry::create(android_rootfs_dir);
204     if (!m) {
205       ERROR("Failed to create mount entry for Android rootfs");
206       return false;
207     }
208     mounts_.push_back(m);
209   } else {
210     ERROR("No loop device or FUSE support found. Can't setup Android rootfs!");
211     return false;
212   }
213
214   auto final_android_rootfs_dir = android_rootfs_dir;
215   if (enable_rootfs_overlay_) {
216     if (!setup_rootfs_overlay())
217       return false;
218
219     final_android_rootfs_dir = SystemConfiguration::instance().combined_rootfs_dir();
220   }
221
222
223   for (const auto &dir_name : std::vector<std::string>{"cache", "data"}) {
224     auto target_dir_path = fs::path(final_android_rootfs_dir) / dir_name;
225     auto src_dir_path = SystemConfiguration::instance().data_dir() / dir_name;
226
227     if (!fs::exists(src_dir_path)) {
228       if (!fs::create_directory(src_dir_path)) {
229         ERROR("Failed to create Android %s directory", dir_name);
230         mounts_.clear();
231         return false;
232       }
233       if (::chown(src_dir_path.c_str(), unprivileged_user_id, unprivileged_user_id) != 0) {
234         ERROR("Failed to allow access for unprivileged user on %s directory of the rootfs", dir_name);
235         mounts_.clear();
236         return false;
237       }
238     }
239
240     auto m = common::MountEntry::create(src_dir_path, target_dir_path, "", MS_MGC_VAL | MS_BIND | MS_PRIVATE);
241     if (!m) {
242       ERROR("Failed to mount Android %s directory", dir_name);
243       mounts_.clear();
244       return false;
245     }
246     mounts_.push_back(m);
247   }
248
249   // Unmounting needs to happen in reverse order
250   std::reverse(mounts_.begin(), mounts_.end());
251
252   return true;
253 }
254
255 bool anbox::cmds::ContainerManager::setup_rootfs_overlay() {
256   const auto combined_rootfs_path = SystemConfiguration::instance().combined_rootfs_dir();
257   if (!fs::exists(combined_rootfs_path))
258     fs::create_directories(combined_rootfs_path);
259
260   const auto overlay_path = SystemConfiguration::instance().overlay_dir();
261   if (!fs::exists(overlay_path))
262     fs::create_directories(overlay_path);
263
264   const auto rootfs_path = SystemConfiguration::instance().rootfs_dir();
265   const auto overlay_config = utils::string_format("lowerdir=%s:%s", overlay_path, rootfs_path);
266   auto m = common::MountEntry::create("overlay", combined_rootfs_path, "overlay", MS_RDONLY, overlay_config.c_str());
267   if (!m) {
268     ERROR("Failed to setup rootfs overlay");
269     mounts_.clear();
270     return false;
271   }
272   mounts_.push_back(m);
273
274   DEBUG("Successfully setup rootfs overlay");
275   return true;
276 }